compleate
This commit is contained in:
phaichayon
2026-06-15 13:20:39 +07:00
parent b8cd39eaa4
commit 8148850fda
33 changed files with 4800 additions and 35 deletions

View 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} />
</>
);
}