diff --git a/src/components/forms/clinic-form.tsx b/src/components/forms/clinic-form.tsx new file mode 100644 index 0000000..b928481 --- /dev/null +++ b/src/components/forms/clinic-form.tsx @@ -0,0 +1,263 @@ +import { Input } from '@/components/base/input/input'; +import { Select } from '@/components/base/select/select'; +import { TextArea } from '@/components/base/textarea/textarea'; +import { Toggle } from '@/components/base/toggle/toggle'; + +// Reusable clinic form used by both the /settings/clinics slideout and the +// /setup wizard step. The parent owns the form state so it can also own the +// submit button and the loading/error UI. +// +// Field shapes mirror the platform's ClinicCreateInput (which uses +// `addressCustom`, not `address`, and `onlineBooking`, not +// `onlineBookingEnabled` as the SDK field). See +// FortyTwoApps/helix-engage/src/objects/clinic.object.ts for the SDK source of +// truth; GraphQL-level differences are normalised in clinicFormToGraphQLInput. + +export type ClinicStatus = 'ACTIVE' | 'TEMPORARILY_CLOSED' | 'PERMANENTLY_CLOSED'; + +export type ClinicFormValues = { + clinicName: string; + addressStreet1: string; + addressStreet2: string; + addressCity: string; + addressState: string; + addressPostcode: string; + phone: string; + email: string; + weekdayHours: string; + saturdayHours: string; + sundayHours: string; + status: ClinicStatus; + walkInAllowed: boolean; + onlineBooking: boolean; + cancellationWindowHours: string; + arriveEarlyMin: string; + requiredDocuments: string; +}; + +export const emptyClinicFormValues = (): ClinicFormValues => ({ + clinicName: '', + addressStreet1: '', + addressStreet2: '', + addressCity: '', + addressState: '', + addressPostcode: '', + phone: '', + email: '', + weekdayHours: '9:00 AM - 6:00 PM', + saturdayHours: '9:00 AM - 2:00 PM', + sundayHours: 'Closed', + status: 'ACTIVE', + walkInAllowed: true, + onlineBooking: true, + cancellationWindowHours: '24', + arriveEarlyMin: '15', + requiredDocuments: '', +}); + +const STATUS_ITEMS = [ + { id: 'ACTIVE', label: 'Active' }, + { id: 'TEMPORARILY_CLOSED', label: 'Temporarily closed' }, + { id: 'PERMANENTLY_CLOSED', label: 'Permanently closed' }, +]; + +// Convert form state into the shape the platform's createClinic / updateClinic +// mutations expect. Only non-empty fields are included so the platform can +// apply its own defaults for the rest. +export const clinicFormToGraphQLInput = (v: ClinicFormValues): Record => { + const input: Record = { + clinicName: v.clinicName.trim(), + status: v.status, + walkInAllowed: v.walkInAllowed, + onlineBooking: v.onlineBooking, + }; + + const hasAddress = v.addressStreet1 || v.addressCity || v.addressState || v.addressPostcode; + if (hasAddress) { + input.addressCustom = { + addressStreet1: v.addressStreet1 || null, + addressStreet2: v.addressStreet2 || null, + addressCity: v.addressCity || null, + addressState: v.addressState || null, + addressPostcode: v.addressPostcode || null, + addressCountry: 'India', + }; + } + + if (v.phone.trim()) { + input.phone = { + primaryPhoneNumber: v.phone.trim(), + primaryPhoneCountryCode: 'IN', + primaryPhoneCallingCode: '+91', + additionalPhones: null, + }; + } + + if (v.email.trim()) { + input.email = { + primaryEmail: v.email.trim(), + additionalEmails: null, + }; + } + + if (v.weekdayHours.trim()) input.weekdayHours = v.weekdayHours.trim(); + if (v.saturdayHours.trim()) input.saturdayHours = v.saturdayHours.trim(); + if (v.sundayHours.trim()) input.sundayHours = v.sundayHours.trim(); + if (v.cancellationWindowHours.trim()) { + const n = Number(v.cancellationWindowHours); + if (!Number.isNaN(n)) input.cancellationWindowHours = n; + } + if (v.arriveEarlyMin.trim()) { + const n = Number(v.arriveEarlyMin); + if (!Number.isNaN(n)) input.arriveEarlyMin = n; + } + if (v.requiredDocuments.trim()) input.requiredDocuments = v.requiredDocuments.trim(); + + return input; +}; + +type ClinicFormProps = { + value: ClinicFormValues; + onChange: (value: ClinicFormValues) => void; +}; + +export const ClinicForm = ({ value, onChange }: ClinicFormProps) => { + const patch = (updates: Partial) => onChange({ ...value, ...updates }); + + return ( +
+ patch({ clinicName: v })} + /> + + + +
+

Address

+ patch({ addressStreet1: v })} + /> + patch({ addressStreet2: v })} + /> +
+ patch({ addressCity: v })} + /> + patch({ addressState: v })} + /> +
+ patch({ addressPostcode: v })} + /> +
+ +
+

Contact

+ patch({ phone: v })} + /> + patch({ email: v })} + /> +
+ +
+

Visiting hours

+ patch({ weekdayHours: v })} + /> +
+ patch({ saturdayHours: v })} + /> + patch({ sundayHours: v })} + /> +
+
+ +
+

Booking policy

+
+ patch({ walkInAllowed: checked })} + /> + patch({ onlineBooking: checked })} + /> +
+
+ patch({ cancellationWindowHours: v })} + /> + patch({ arriveEarlyMin: v })} + /> +
+