284 lines
11 KiB
TypeScript
284 lines
11 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
import { useEffect, useMemo } from 'react';
|
|
import { useStore } from '@tanstack/react-form';
|
|
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,
|
|
customerSubGroup: customer?.customerSubGroup ?? 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('สร้างลูกค้าสำเร็จ');
|
|
onOpenChange(false);
|
|
},
|
|
onError: (error) =>
|
|
toast.error(error instanceof Error ? error.message : 'ไม่สามารถสร้างลูกค้าได้')
|
|
});
|
|
|
|
const updateMutation = useMutation({
|
|
...updateCustomerMutation,
|
|
onSuccess: () => {
|
|
toast.success('อัปเดตลูกค้าสำเร็จ');
|
|
onOpenChange(false);
|
|
},
|
|
onError: (error) =>
|
|
toast.error(error instanceof Error ? error.message : 'ไม่สามารถอัปเดตลูกค้าได้')
|
|
});
|
|
|
|
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,
|
|
customerSubGroup: value.customerSubGroup || 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 selectedCustomerGroup = useStore(form.store, (state) => state.values.customerGroup);
|
|
const selectedCustomerSubGroup = useStore(form.store, (state) => state.values.customerSubGroup);
|
|
const filteredCustomerSubGroups = useMemo(
|
|
() =>
|
|
referenceData.customerSubGroups.filter((item) => item.parentId === selectedCustomerGroup),
|
|
[referenceData.customerSubGroups, selectedCustomerGroup]
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (!selectedCustomerSubGroup) {
|
|
return;
|
|
}
|
|
|
|
const isValid = filteredCustomerSubGroups.some((item) => item.id === selectedCustomerSubGroup);
|
|
|
|
if (!isValid) {
|
|
form.setFieldValue('customerSubGroup', '');
|
|
}
|
|
}, [filteredCustomerSubGroups, form, selectedCustomerSubGroup]);
|
|
|
|
const isPending = createMutation.isPending || updateMutation.isPending;
|
|
|
|
return (
|
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
<SheetContent className='flex flex-col sm:max-w-3xl'>
|
|
<SheetHeader>
|
|
<SheetTitle>{isEdit ? 'แก้ไขลูกค้า' : 'เพิ่มลูกค้า'}</SheetTitle>
|
|
<SheetDescription>จัดการข้อมูลหลักของลูกค้า สาขาที่ดูแล และการจัดประเภทใน CRM</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='ชื่อลูกค้า'
|
|
required
|
|
placeholder='ALLA Service Co., Ltd.'
|
|
/>
|
|
<FormTextField name='abbr' label='ชื่อย่อ' placeholder='ALLA' />
|
|
<FormTextField name='taxId' label='เลขประจำตัวผู้เสียภาษี' placeholder='0105556...' />
|
|
<FormTextField name='phone' label='โทรศัพท์' placeholder='02-000-0000' />
|
|
<FormTextField name='fax' label='แฟกซ์' placeholder='02-000-0001' />
|
|
<FormTextField
|
|
name='email'
|
|
label='อีเมล'
|
|
type='email'
|
|
placeholder='sales@example.com'
|
|
/>
|
|
<FormTextField name='website' label='เว็บไซต์' placeholder='https://example.com' />
|
|
<FormTextField name='country' label='ประเทศ' placeholder='Thailand' />
|
|
<FormSelectField
|
|
name='customerType'
|
|
label='ประเภทลูกค้า'
|
|
required
|
|
options={referenceData.customerTypes.map((item) => ({
|
|
value: item.id,
|
|
label: item.label
|
|
}))}
|
|
/>
|
|
<FormSelectField
|
|
name='customerStatus'
|
|
label='สถานะลูกค้า'
|
|
required
|
|
options={referenceData.customerStatuses.map((item) => ({
|
|
value: item.id,
|
|
label: item.label
|
|
}))}
|
|
/>
|
|
<FormSelectField
|
|
name='branchId'
|
|
label='สาขา'
|
|
options={referenceData.branches.map((branch) => ({
|
|
value: branch.id,
|
|
label: branch.name
|
|
}))}
|
|
/>
|
|
<FormSelectField
|
|
name='leadChannel'
|
|
label='ที่มาของลีด'
|
|
options={referenceData.leadChannels.map((item) => ({
|
|
value: item.id,
|
|
label: item.label
|
|
}))}
|
|
/>
|
|
<FormSelectField
|
|
name='customerGroup'
|
|
label='กลุ่มลูกค้า'
|
|
options={referenceData.customerGroups.map((item) => ({
|
|
value: item.id,
|
|
label: item.label
|
|
}))}
|
|
/>
|
|
<FormSelectField
|
|
name='customerSubGroup'
|
|
label='กลุ่มย่อยลูกค้า'
|
|
disabled={!selectedCustomerGroup}
|
|
placeholder={
|
|
selectedCustomerGroup ? 'เลือกกลุ่มย่อยลูกค้า' : 'เลือกกลุ่มลูกค้าก่อน'
|
|
}
|
|
options={filteredCustomerSubGroups.map((item) => ({
|
|
value: item.id,
|
|
label: item.label
|
|
}))}
|
|
/>
|
|
<div className='md:col-span-2'>
|
|
<FormTextField name='address' label='ที่อยู่' placeholder='123 Main Road' />
|
|
</div>
|
|
<FormTextField name='province' label='จังหวัด' placeholder='Bangkok' />
|
|
<FormTextField name='district' label='เขต / อำเภอ' placeholder='Bang Kapi' />
|
|
<FormTextField name='subDistrict' label='แขวง / ตำบล' placeholder='Hua Mak' />
|
|
<FormTextField name='postalCode' label='รหัสไปรษณีย์' placeholder='10240' />
|
|
<div className='md:col-span-2'>
|
|
<FormTextareaField
|
|
name='notes'
|
|
size='md'
|
|
label='หมายเหตุ'
|
|
placeholder='หมายเหตุภายในเกี่ยวกับความสัมพันธ์กับลูกค้ารายนี้'
|
|
/>
|
|
</div>
|
|
<div className='md:col-span-2'>
|
|
<FormSwitchField
|
|
name='isActive'
|
|
label='ใช้งานอยู่'
|
|
description='ลูกค้าที่ปิดใช้งานยังคงอยู่ในประวัติ แต่ไม่ควรนำไปใช้กับงานใหม่'
|
|
/>
|
|
</div>
|
|
</form.Form>
|
|
</form.AppForm>
|
|
</div>
|
|
|
|
<SheetFooter>
|
|
<Button type='button' variant='outline' onClick={() => onOpenChange(false)}>
|
|
ยกเลิก
|
|
</Button>
|
|
<Button type='submit' form='customer-form-sheet' isLoading={isPending}>
|
|
<Icons.check className='mr-2 h-4 w-4' />
|
|
{isEdit ? 'อัปเดตลูกค้า' : 'สร้างลูกค้า'}
|
|
</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' /> เพิ่มลูกค้า
|
|
</Button>
|
|
<CustomerFormSheet open={open} onOpenChange={setOpen} referenceData={referenceData} />
|
|
</>
|
|
);
|
|
}
|