import { useCallback, useEffect, useState } from 'react'; import { WizardStep } from './wizard-step'; import { ClinicsRightPane, type ClinicSummary } from './wizard-right-panes'; import { ClinicForm, clinicCoreToGraphQLInput, holidayInputsFromForm, requiredDocInputsFromForm, emptyClinicFormValues, type ClinicFormValues, } from '@/components/forms/clinic-form'; import { apiClient } from '@/lib/api-client'; import { notify } from '@/lib/toast'; import type { WizardStepComponentProps } from './wizard-step-types'; // Clinic step — presents a single-clinic form. On save the wizard runs // a three-stage create chain: // 1. createClinic (main record → get id) // 2. createHoliday × N (one per holiday entry) // 3. createClinicRequiredDocument × N (one per required doc type) // // This mirrors what the /settings/clinics list page does, minus the // delete-old-first step (wizard is always creating, never updating). // Failures inside the chain throw up through onComplete so the user // sees the error loud, and the wizard stays on the current step. export const WizardStepClinics = (props: WizardStepComponentProps) => { const [values, setValues] = useState(emptyClinicFormValues); const [saving, setSaving] = useState(false); const [clinics, setClinics] = useState([]); const fetchClinics = useCallback(async () => { try { // Field names match what the platform actually exposes: // - the SDK ADDRESS field is named "address" but the // platform mounts it as `addressCustom` (composite type // with addressCity / addressStreet / etc.) // - the SDK SELECT field labelled "Status" lands as plain // `status: ClinicStatusEnum`, NOT `clinicStatus` // Verified via __type introspection — keep this query // pinned to the actual schema to avoid silent empty fetches. type ClinicNode = { id: string; clinicName: string | null; addressCustom: { addressCity: string | null } | null; status: string | null; }; const data = await apiClient.graphql<{ clinics: { edges: { node: ClinicNode }[] }; }>( `{ clinics(first: 100, orderBy: { createdAt: DescNullsLast }) { edges { node { id clinicName addressCustom { addressCity } status } } } }`, undefined, { silent: true }, ); // Flatten into the shape ClinicsRightPane expects. setClinics( data.clinics.edges.map((e) => ({ id: e.node.id, clinicName: e.node.clinicName, addressCity: e.node.addressCustom?.addressCity ?? null, clinicStatus: e.node.status, })), ); } catch (err) { console.error('[wizard/clinics] fetch failed', err); } }, []); useEffect(() => { fetchClinics(); }, [fetchClinics]); const handleSave = async () => { if (!values.clinicName.trim()) { notify.error('Clinic name is required'); return; } setSaving(true); try { // 1. Core clinic record const res = await apiClient.graphql<{ createClinic: { id: string } }>( `mutation CreateClinic($data: ClinicCreateInput!) { createClinic(data: $data) { id } }`, { data: clinicCoreToGraphQLInput(values) }, ); const clinicId = res.createClinic.id; // 2. Holidays if (values.holidays.length > 0) { const holidayInputs = holidayInputsFromForm(values, clinicId); await Promise.all( holidayInputs.map((data) => apiClient.graphql( `mutation CreateHoliday($data: HolidayCreateInput!) { createHoliday(data: $data) { id } }`, { data }, ), ), ); } // 3. Required documents if (values.requiredDocumentTypes.length > 0) { const docInputs = requiredDocInputsFromForm(values, clinicId); await Promise.all( docInputs.map((data) => apiClient.graphql( `mutation CreateClinicRequiredDocument($data: ClinicRequiredDocumentCreateInput!) { createClinicRequiredDocument(data: $data) { id } }`, { data }, ), ), ); } notify.success('Clinic added', values.clinicName); await fetchClinics(); // Mark complete on first successful create. Don't auto-advance — // admins typically add multiple clinics in one sitting; the // Continue button on the wizard nav handles forward motion. if (!props.isCompleted) { await props.onComplete('clinics'); } setValues(emptyClinicFormValues()); } catch (err) { console.error('[wizard/clinics] save failed', err); } finally { setSaving(false); } }; // Same trick as the Team step: once at least one clinic exists, // flip isCompleted=true so the WizardStep renders the "Continue" // button as the primary action — the form stays open below for // adding more clinics. const pretendCompleted = props.isCompleted || clinics.length > 0; return ( } >
); };