tsk-C
compleate
This commit is contained in:
250
src/features/crm/customers/components/customer-form-sheet.tsx
Normal file
250
src/features/crm/customers/components/customer-form-sheet.tsx
Normal file
@@ -0,0 +1,250 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { toast } from 'sonner';
|
||||
import { Icons } from '@/components/icons';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetFooter,
|
||||
SheetHeader,
|
||||
SheetTitle
|
||||
} from '@/components/ui/sheet';
|
||||
import { useAppForm, useFormFields } from '@/components/ui/tanstack-form';
|
||||
import { createCustomerMutation, updateCustomerMutation } from '../api/mutations';
|
||||
import type { CustomerMutationPayload, CustomerRecord, CustomerReferenceData } from '../api/types';
|
||||
import { customerSchema, type CustomerFormValues } from '../schemas/customer.schema';
|
||||
|
||||
interface CustomerFormSheetProps {
|
||||
customer?: CustomerRecord;
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
referenceData: CustomerReferenceData;
|
||||
}
|
||||
|
||||
function toDefaultValues(
|
||||
customer: CustomerRecord | undefined,
|
||||
referenceData: CustomerReferenceData
|
||||
): CustomerFormValues {
|
||||
return {
|
||||
name: customer?.name ?? '',
|
||||
abbr: customer?.abbr ?? '',
|
||||
taxId: customer?.taxId ?? '',
|
||||
customerType: customer?.customerType ?? referenceData.customerTypes[0]?.id ?? '',
|
||||
customerStatus: customer?.customerStatus ?? referenceData.customerStatuses[0]?.id ?? '',
|
||||
branchId: customer?.branchId ?? undefined,
|
||||
address: customer?.address ?? '',
|
||||
province: customer?.province ?? '',
|
||||
district: customer?.district ?? '',
|
||||
subDistrict: customer?.subDistrict ?? '',
|
||||
postalCode: customer?.postalCode ?? '',
|
||||
country: customer?.country ?? 'Thailand',
|
||||
phone: customer?.phone ?? '',
|
||||
fax: customer?.fax ?? '',
|
||||
email: customer?.email ?? '',
|
||||
website: customer?.website ?? '',
|
||||
leadChannel: customer?.leadChannel ?? undefined,
|
||||
customerGroup: customer?.customerGroup ?? undefined,
|
||||
notes: customer?.notes ?? '',
|
||||
isActive: customer?.isActive ?? true
|
||||
};
|
||||
}
|
||||
|
||||
export function CustomerFormSheet({
|
||||
customer,
|
||||
open,
|
||||
onOpenChange,
|
||||
referenceData
|
||||
}: CustomerFormSheetProps) {
|
||||
const isEdit = !!customer;
|
||||
const defaultValues = useMemo(
|
||||
() => toDefaultValues(customer, referenceData),
|
||||
[customer, referenceData]
|
||||
);
|
||||
const { FormTextField, FormSelectField, FormTextareaField, FormSwitchField } =
|
||||
useFormFields<CustomerFormValues>();
|
||||
|
||||
const createMutation = useMutation({
|
||||
...createCustomerMutation,
|
||||
onSuccess: () => {
|
||||
toast.success('Customer created successfully');
|
||||
onOpenChange(false);
|
||||
},
|
||||
onError: (error) =>
|
||||
toast.error(error instanceof Error ? error.message : 'Failed to create customer')
|
||||
});
|
||||
|
||||
const updateMutation = useMutation({
|
||||
...updateCustomerMutation,
|
||||
onSuccess: () => {
|
||||
toast.success('Customer updated successfully');
|
||||
onOpenChange(false);
|
||||
},
|
||||
onError: (error) =>
|
||||
toast.error(error instanceof Error ? error.message : 'Failed to update customer')
|
||||
});
|
||||
|
||||
const form = useAppForm({
|
||||
defaultValues,
|
||||
validators: {
|
||||
onSubmit: customerSchema
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
const payload: CustomerMutationPayload = {
|
||||
...value,
|
||||
branchId: value.branchId || null,
|
||||
leadChannel: value.leadChannel || null,
|
||||
customerGroup: value.customerGroup || null
|
||||
};
|
||||
|
||||
if (isEdit && customer) {
|
||||
await updateMutation.mutateAsync({ id: customer.id, values: payload });
|
||||
return;
|
||||
}
|
||||
|
||||
await createMutation.mutateAsync(payload);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
return;
|
||||
}
|
||||
|
||||
form.reset(defaultValues);
|
||||
}, [defaultValues, form, open]);
|
||||
|
||||
const isPending = createMutation.isPending || updateMutation.isPending;
|
||||
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={onOpenChange}>
|
||||
<SheetContent className='flex flex-col sm:max-w-3xl'>
|
||||
<SheetHeader>
|
||||
<SheetTitle>{isEdit ? 'Edit Customer' : 'New Customer'}</SheetTitle>
|
||||
<SheetDescription>
|
||||
Maintain the core account profile, ownership branch, and CRM classification here.
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
|
||||
<div className='flex-1 overflow-auto'>
|
||||
<form.AppForm>
|
||||
<form.Form id='customer-form-sheet' className='grid gap-4 md:grid-cols-2'>
|
||||
<FormTextField
|
||||
name='name'
|
||||
label='Customer Name'
|
||||
required
|
||||
placeholder='ALLA Service Co., Ltd.'
|
||||
/>
|
||||
<FormTextField name='abbr' label='Abbreviation' placeholder='ALLA' />
|
||||
<FormTextField name='taxId' label='Tax ID' placeholder='0105556...' />
|
||||
<FormTextField name='phone' label='Phone' placeholder='02-000-0000' />
|
||||
<FormTextField name='fax' label='Fax' placeholder='02-000-0001' />
|
||||
<FormTextField
|
||||
name='email'
|
||||
label='Email'
|
||||
type='email'
|
||||
placeholder='sales@example.com'
|
||||
/>
|
||||
<FormTextField name='website' label='Website' placeholder='https://example.com' />
|
||||
<FormTextField name='country' label='Country' placeholder='Thailand' />
|
||||
<FormSelectField
|
||||
name='customerType'
|
||||
label='Customer Type'
|
||||
required
|
||||
options={referenceData.customerTypes.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.label
|
||||
}))}
|
||||
/>
|
||||
<FormSelectField
|
||||
name='customerStatus'
|
||||
label='Customer Status'
|
||||
required
|
||||
options={referenceData.customerStatuses.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.label
|
||||
}))}
|
||||
/>
|
||||
<FormSelectField
|
||||
name='branchId'
|
||||
label='Branch'
|
||||
options={referenceData.branches.map((branch) => ({
|
||||
value: branch.id,
|
||||
label: branch.name
|
||||
}))}
|
||||
/>
|
||||
<FormSelectField
|
||||
name='leadChannel'
|
||||
label='Lead Channel'
|
||||
options={referenceData.leadChannels.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.label
|
||||
}))}
|
||||
/>
|
||||
<FormSelectField
|
||||
name='customerGroup'
|
||||
label='Customer Group'
|
||||
options={referenceData.customerGroups.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.label
|
||||
}))}
|
||||
/>
|
||||
<div className='md:col-span-2'>
|
||||
<FormTextField name='address' label='Address' placeholder='123 Main Road' />
|
||||
</div>
|
||||
<FormTextField name='province' label='Province' placeholder='Bangkok' />
|
||||
<FormTextField name='district' label='District' placeholder='Bang Kapi' />
|
||||
<FormTextField name='subDistrict' label='Sub District' placeholder='Hua Mak' />
|
||||
<FormTextField name='postalCode' label='Postal Code' placeholder='10240' />
|
||||
<div className='md:col-span-2'>
|
||||
<FormTextareaField
|
||||
name='notes'
|
||||
label='Notes'
|
||||
placeholder='Internal notes about this customer relationship'
|
||||
/>
|
||||
</div>
|
||||
<div className='md:col-span-2'>
|
||||
<FormSwitchField
|
||||
name='isActive'
|
||||
label='Active Record'
|
||||
description='Inactive customers remain visible in history but should not be used for new work.'
|
||||
/>
|
||||
</div>
|
||||
</form.Form>
|
||||
</form.AppForm>
|
||||
</div>
|
||||
|
||||
<SheetFooter>
|
||||
<Button type='button' variant='outline' onClick={() => onOpenChange(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type='submit' form='customer-form-sheet' isLoading={isPending}>
|
||||
<Icons.check className='mr-2 h-4 w-4' />
|
||||
{isEdit ? 'Update Customer' : 'Create Customer'}
|
||||
</Button>
|
||||
</SheetFooter>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
}
|
||||
|
||||
export function CustomerFormSheetTrigger({
|
||||
referenceData
|
||||
}: {
|
||||
referenceData: CustomerReferenceData;
|
||||
}) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setOpen(true)}>
|
||||
<Icons.add className='mr-2 h-4 w-4' /> Add Customer
|
||||
</Button>
|
||||
<CustomerFormSheet open={open} onOpenChange={setOpen} referenceData={referenceData} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user