task-d.6.2

This commit is contained in:
phaichayon
2026-07-01 13:57:16 +07:00
parent 0a144f0f9e
commit ed28ff6b50
71 changed files with 4137 additions and 51727 deletions

View File

@@ -25,11 +25,13 @@ import {
SheetHeader,
SheetTitle
} from '@/components/ui/sheet';
import { Switch } from '@/components/ui/switch';
import { useAppForm, useFormFields } from '@/components/ui/tanstack-form';
import { opportunityProjectPartiesOptions } from '../api/queries';
import { createOpportunityMutation, updateOpportunityMutation } from '../api/mutations';
import type { OpportunityMutationPayload, OpportunityRecord, OpportunityReferenceData } from '../api/types';
import { opportunitySchema, type OpportunityFormValues } from '../schemas/opportunity.schema';
import { isHotProjectSuggested } from '@/features/crm/shared/hot-project';
function toDefaultValues(
opportunity: OpportunityRecord | undefined,
@@ -50,14 +52,18 @@ function toDefaultValues(
priority:
opportunity?.priority ?? referenceData.priorities[1]?.id ?? referenceData.priorities[0]?.id ?? '',
leadChannel: opportunity?.leadChannel ?? undefined,
estimatedValue: opportunity?.estimatedValue ?? undefined,
chancePercent: opportunity?.chancePercent ?? undefined,
expectedCloseDate: opportunity?.expectedCloseDate ? opportunity.expectedCloseDate.slice(0, 10) : '',
competitor: opportunity?.competitor ?? '',
source: opportunity?.source ?? '',
isHotProject: opportunity?.isHotProject ?? false,
notes: opportunity?.notes ?? '',
isActive: opportunity?.isActive ?? true
estimatedValue: opportunity?.estimatedValue ?? undefined,
chancePercent: opportunity?.chancePercent ?? undefined,
expectedCloseDate: opportunity?.expectedCloseDate ? opportunity.expectedCloseDate.slice(0, 10) : '',
projectCloseDate: opportunity?.projectCloseDate ? opportunity.projectCloseDate.slice(0, 10) : '',
deliveryDate: opportunity?.deliveryDate ? opportunity.deliveryDate.slice(0, 10) : '',
competitor: opportunity?.competitor ?? '',
source: opportunity?.source ?? '',
isHotProject: opportunity?.isHotProject ?? false,
hotProjectAutoSuggested: opportunity?.hotProjectAutoSuggested ?? false,
hotProjectManuallyOverridden: opportunity?.hotProjectManuallyOverridden ?? false,
notes: opportunity?.notes ?? '',
isActive: opportunity?.isActive ?? true
};
}
@@ -144,11 +150,15 @@ export function OpportunityFormSheet({
typeof value.estimatedValue === 'number' ? value.estimatedValue : null,
chancePercent:
typeof value.chancePercent === 'number' ? value.chancePercent : null,
expectedCloseDate: value.expectedCloseDate || null,
competitor: value.competitor,
source: value.source,
isHotProject: value.isHotProject ?? false,
notes: value.notes,
expectedCloseDate: value.expectedCloseDate || null,
projectCloseDate: value.projectCloseDate || null,
deliveryDate: value.deliveryDate || null,
competitor: value.competitor,
source: value.source,
isHotProject: value.isHotProject ?? false,
hotProjectAutoSuggested: value.hotProjectAutoSuggested ?? false,
hotProjectManuallyOverridden: value.hotProjectManuallyOverridden ?? false,
notes: value.notes,
isActive: value.isActive ?? true,
projectParties: projectParties
.filter((item) => item.customerId && item.role)
@@ -196,7 +206,25 @@ export function OpportunityFormSheet({
referenceData.customers.find((customer) => customer.id === selectedCustomerId)?.ownerUserId ?? '';
form.setFieldValue('assignedToUserId', ownerUserId);
}, [form, isEdit, open, referenceData.customers, selectedCustomerId]);
}, [form, isEdit, open, referenceData.customers, selectedCustomerId]);
useEffect(() => {
if (!open) {
return;
}
const projectCloseDate = form.getFieldValue('projectCloseDate');
const manuallyOverridden = form.getFieldValue('hotProjectManuallyOverridden');
if (manuallyOverridden) {
form.setFieldValue('hotProjectAutoSuggested', isHotProjectSuggested(projectCloseDate));
return;
}
const suggested = isHotProjectSuggested(projectCloseDate);
form.setFieldValue('hotProjectAutoSuggested', suggested);
form.setFieldValue('isHotProject', suggested);
}, [form, open, form.state.values.projectCloseDate, form.state.values.hotProjectManuallyOverridden]);
const isPending = createMutation.isPending || updateMutation.isPending;
@@ -376,6 +404,14 @@ export function OpportunityFormSheet({
name='expectedCloseDate'
label='วันที่คาดว่าจะปิดการขาย'
/>
<FormDatePickerField
name='projectCloseDate'
label='Project Close Date'
/>
<FormDatePickerField
name='deliveryDate'
label='Delivery Date'
/>
<FormTextField name='competitor' label='คู่แข่ง' placeholder='Other vendor name' />
<div className='md:col-span-2'>
<FormTextareaField
@@ -402,10 +438,33 @@ export function OpportunityFormSheet({
/>
</div>
<div className='md:col-span-2 grid gap-4 md:grid-cols-2'>
<FormSwitchField
<form.AppField
name='isHotProject'
label='โครงการด่วน'
description='ใช้สำหรับงานเร่งด่วนหรือโครงการสำคัญเชิงกลยุทธ์'
children={(field) => (
<field.FieldSet>
<field.Field orientation='horizontal'>
<div className='flex flex-1 flex-col gap-1.5 leading-snug'>
<field.FieldLabel className='text-base'></field.FieldLabel>
<field.FieldDescription>
</field.FieldDescription>
</div>
<Switch
checked={Boolean(field.state.value)}
onCheckedChange={(checked) => {
field.handleChange(checked);
field.handleBlur();
form.setFieldValue('hotProjectManuallyOverridden', true);
form.setFieldValue(
'hotProjectAutoSuggested',
isHotProjectSuggested(form.getFieldValue('projectCloseDate'))
);
}}
/>
</field.Field>
<field.FieldError />
</field.FieldSet>
)}
/>
<FormSwitchField
name='isActive'
@@ -462,4 +521,3 @@ export function OpportunityFormSheetTrigger({
</>
);
}