chore: perauset customer project
This commit is contained in:
205
apps/app/src/components/create-template-form.tsx
Normal file
205
apps/app/src/components/create-template-form.tsx
Normal file
@@ -0,0 +1,205 @@
|
||||
import { useState } from 'react'
|
||||
import {
|
||||
Card,
|
||||
Stack,
|
||||
TextInput,
|
||||
Textarea,
|
||||
Select,
|
||||
Group,
|
||||
Button,
|
||||
Switch,
|
||||
NumberInput,
|
||||
Collapse,
|
||||
Title,
|
||||
} from '@pikku/mantine/core'
|
||||
import { useDisclosure } from '@mantine/hooks'
|
||||
import { notifications } from '@mantine/notifications'
|
||||
import { m } from '@/i18n/messages'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { Plus } from 'lucide-react'
|
||||
import { usePikkuMutation } from '@perauset/functions-sdk/pikku/api.gen'
|
||||
|
||||
type Category =
|
||||
| 'housekeeping'
|
||||
| 'kitchen'
|
||||
| 'maintenance'
|
||||
| 'operations'
|
||||
| 'retreat'
|
||||
| 'transport'
|
||||
|
||||
const CATEGORIES: { value: Category; label: string }[] = [
|
||||
{ value: 'housekeeping', label: 'Housekeeping' },
|
||||
{ value: 'kitchen', label: 'Kitchen' },
|
||||
{ value: 'maintenance', label: 'Maintenance' },
|
||||
{ value: 'operations', label: 'Operations' },
|
||||
{ value: 'retreat', label: 'Retreat' },
|
||||
{ value: 'transport', label: 'Transport' },
|
||||
]
|
||||
|
||||
export function CreateTemplateForm() {
|
||||
const queryClient = useQueryClient()
|
||||
const [opened, { toggle }] = useDisclosure(false)
|
||||
const [title, setTitle] = useState('')
|
||||
const [description, setDescription] = useState('')
|
||||
const [category, setCategory] = useState<Category | null>(null)
|
||||
const [isClaimable, setIsClaimable] = useState(false)
|
||||
const [defaultLocation, setDefaultLocation] = useState('')
|
||||
const [estimatedMinutes, setEstimatedMinutes] = useState<number | string>('')
|
||||
const [recurrenceCron, setRecurrenceCron] = useState('')
|
||||
|
||||
const resetForm = () => {
|
||||
setTitle('')
|
||||
setDescription('')
|
||||
setCategory(null)
|
||||
setIsClaimable(false)
|
||||
setDefaultLocation('')
|
||||
setEstimatedMinutes('')
|
||||
setRecurrenceCron('')
|
||||
}
|
||||
|
||||
const mutation = usePikkuMutation('createTaskTemplate', {
|
||||
onSuccess: () => {
|
||||
notifications.show({
|
||||
title: m.templates_created(),
|
||||
message: `"${title}" template has been created.`,
|
||||
color: 'teal',
|
||||
})
|
||||
resetForm()
|
||||
toggle()
|
||||
queryClient.invalidateQueries({
|
||||
predicate: (q) =>
|
||||
typeof q.queryKey[0] === 'string' &&
|
||||
(q.queryKey[0] as string).toLowerCase().includes('template'),
|
||||
})
|
||||
},
|
||||
onError: (err: Error) => {
|
||||
notifications.show({
|
||||
title: m.common_error(),
|
||||
message: err.message ?? 'Something went wrong',
|
||||
color: 'red',
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
if (!title.trim() || !category) {
|
||||
notifications.show({
|
||||
title: m.common_validation(),
|
||||
message: m.templates_title_category_required(),
|
||||
color: 'orange',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const body = {
|
||||
title: title.trim(),
|
||||
category,
|
||||
...(description.trim() ? { description: description.trim() } : {}),
|
||||
...(isClaimable ? { isClaimable: true } : {}),
|
||||
...(defaultLocation.trim() ? { defaultLocation: defaultLocation.trim() } : {}),
|
||||
...(typeof estimatedMinutes === 'number' && estimatedMinutes > 0
|
||||
? { estimatedMinutes }
|
||||
: {}),
|
||||
...(recurrenceCron.trim() ? { recurrenceCron: recurrenceCron.trim() } : {}),
|
||||
}
|
||||
|
||||
mutation.mutate(body)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button variant="light" leftSection={<Plus size={16} />} onClick={toggle}>
|
||||
{opened ? m.templates_hide_form() : m.templates_create()}
|
||||
</Button>
|
||||
|
||||
<Collapse expanded={opened}>
|
||||
<Card
|
||||
padding="lg"
|
||||
radius="md"
|
||||
style={{
|
||||
backgroundColor: '#ffffff',
|
||||
boxShadow:
|
||||
'0 2px 12px rgba(61, 43, 31, 0.08), 0 1px 4px rgba(61, 43, 31, 0.04)',
|
||||
maxWidth: 640,
|
||||
}}
|
||||
>
|
||||
<Title
|
||||
order={4}
|
||||
mb="md"
|
||||
style={{ fontFamily: '"Cormorant", serif', color: '#3D2B1F' }}
|
||||
>
|
||||
{m.templates_new()}
|
||||
</Title>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
label={m.templates_field_title()}
|
||||
placeholder={m.templates_field_title_placeholder()}
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<Textarea
|
||||
label={m.templates_field_description()}
|
||||
placeholder={m.templates_field_description_placeholder()}
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.currentTarget.value)}
|
||||
minRows={2}
|
||||
/>
|
||||
|
||||
<Select
|
||||
label={m.templates_field_category()}
|
||||
placeholder={m.templates_field_category_placeholder()}
|
||||
data={CATEGORIES}
|
||||
value={category}
|
||||
onChange={(v) => setCategory(v as Category | null)}
|
||||
required
|
||||
/>
|
||||
|
||||
<Switch
|
||||
label={m.templates_field_claimable()}
|
||||
checked={isClaimable}
|
||||
onChange={(e) => setIsClaimable(e.currentTarget.checked)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label={m.templates_field_default_location()}
|
||||
placeholder={m.templates_field_default_location_placeholder()}
|
||||
value={defaultLocation}
|
||||
onChange={(e) => setDefaultLocation(e.currentTarget.value)}
|
||||
/>
|
||||
|
||||
<NumberInput
|
||||
label={m.templates_field_estimated_minutes()}
|
||||
placeholder={m.templates_field_estimated_minutes_placeholder()}
|
||||
min={1}
|
||||
value={estimatedMinutes}
|
||||
onChange={setEstimatedMinutes}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label={m.templates_field_recurrence()}
|
||||
placeholder={m.templates_field_recurrence_placeholder()}
|
||||
description={m.templates_recurrence_hint()}
|
||||
value={recurrenceCron}
|
||||
onChange={(e) => setRecurrenceCron(e.currentTarget.value)}
|
||||
/>
|
||||
|
||||
<Group justify="flex-end" mt="sm">
|
||||
<Button variant="default" onClick={toggle}>
|
||||
{m.common_cancel()}
|
||||
</Button>
|
||||
<Button type="submit" loading={mutation.isPending}>
|
||||
{m.templates_create()}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
</Card>
|
||||
</Collapse>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user