checkpoint, form data fixed, need to modify its route

This commit is contained in:
2025-05-31 19:21:47 +05:30
parent b80acf882c
commit 3faef34917
3 changed files with 195 additions and 99 deletions

View File

@@ -27,6 +27,7 @@ import { z } from "zod";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { apiRequest } from "@/lib/queryClient"; import { apiRequest } from "@/lib/queryClient";
import { MultipleFileUploadZone } from "../file-upload/multiple-file-upload-zone"; import { MultipleFileUploadZone } from "../file-upload/multiple-file-upload-zone";
import { useAuth } from "@/hooks/use-auth";
const PatientSchema = ( const PatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any> PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
@@ -70,18 +71,33 @@ const updateAppointmentSchema = (
type UpdateAppointment = z.infer<typeof updateAppointmentSchema>; type UpdateAppointment = z.infer<typeof updateAppointmentSchema>;
interface ServiceLine { interface ServiceLine {
procedure_code: string; procedureCode: string;
procedure_date: string; procedureDate: string; // YYYY-MM-DD
oralCavityArea: string; oralCavityArea?: string;
toothNumber: string; toothNumber?: string;
toothSurface: string; toothSurface?: string;
billedAmount: string; billedAmount: number;
}
interface ClaimFormData {
patientId: number;
appointmentId: number;
userId: number;
staffId: number;
patientName: string;
memberId: string;
dateOfBirth: string;
remarks: string;
serviceDate: string; // YYYY-MM-DD
insuranceProvider: string;
status: string; // default "pending"
serviceLines: ServiceLine[];
} }
interface ClaimFormProps { interface ClaimFormProps {
patientId?: number; patientId: number;
extractedData?: Partial<Patient>; extractedData?: Partial<Patient>;
onSubmit: (claimData: any) => void; onSubmit: (data: ClaimFormData) => void;
onHandleAppointmentSubmit: ( onHandleAppointmentSubmit: (
appointmentData: InsertAppointment | UpdateAppointment appointmentData: InsertAppointment | UpdateAppointment
) => void; ) => void;
@@ -105,9 +121,14 @@ export function ClaimForm({
onClose, onClose,
}: ClaimFormProps) { }: ClaimFormProps) {
const { toast } = useToast(); const { toast } = useToast();
const { user } = useAuth();
const [insuranceProvider, setInsuranceProvider] = useState(null) // Patient state - initialize from extractedData or null (new patient)
// Query patient if patientId provided const [patient, setPatient] = useState<Patient | null>(
extractedData ? ({ ...extractedData } as Patient) : null
);
// Query patient
const { const {
data: fetchedPatient, data: fetchedPatient,
isLoading, isLoading,
@@ -122,11 +143,6 @@ export function ClaimForm({
enabled: !!patientId, enabled: !!patientId,
}); });
// Patient state - initialize from extractedData or null (new patient)
const [patient, setPatient] = useState<Patient | null>(
extractedData ? ({ ...extractedData } as Patient) : null
);
// Sync fetched patient when available // Sync fetched patient when available
useEffect(() => { useEffect(() => {
if (fetchedPatient) { if (fetchedPatient) {
@@ -175,56 +191,13 @@ export function ClaimForm({
return `${year}-${month?.padStart(2, "0")}-${day?.padStart(2, "0")}`; return `${year}-${month?.padStart(2, "0")}-${day?.padStart(2, "0")}`;
} }
// Remarks state
const [remarks, setRemarks] = useState<string>("");
// Service lines state with one empty default line
const [serviceLines, setServiceLines] = useState<ServiceLine[]>([]);
useEffect(() => {
if (serviceDate) {
setServiceLines([
{
procedure_code: "",
procedure_date: serviceDate,
oralCavityArea: "",
toothNumber: "",
toothSurface: "",
billedAmount: "",
},
]);
}
}, [serviceDate]);
// Update a field in serviceLines at index
const updateServiceLine = (
index: number,
field: keyof ServiceLine,
value: string
) => {
setServiceLines((prev) => {
const updated = [...prev];
if (updated[index]) {
updated[index][field] = value;
}
return updated;
});
};
const updateProcedureDate = (index: number, date: Date | undefined) => {
const formatted = formatServiceDate(date);
updateServiceLine(index, "procedure_date", formatted);
};
// Handle patient field changes (to make inputs controlled and editable)
const updatePatientField = (field: keyof Patient, value: any) => {
setPatient((prev) => (prev ? { ...prev, [field]: value } : null));
};
// Update service date when calendar date changes // Update service date when calendar date changes
const onServiceDateChange = (date: Date | undefined) => { const onServiceDateChange = (date: Date | undefined) => {
if (date) { if (date) {
const formattedDate = format(date, "MM/dd/yyyy");
setServiceDateValue(date); setServiceDateValue(date);
setServiceDate(format(date, "MM/dd/yy")); setServiceDate(formattedDate);
setForm((prev) => ({ ...prev, serviceDate: formattedDate }));
} }
}; };
@@ -241,6 +214,98 @@ export function ClaimForm({
return dob; return dob;
}; };
// MAIN FORM INITIAL STATE
const [form, setForm] = useState<ClaimFormData>({
patientId: patientId || 0,
appointmentId: 0, //need to update
userId: Number(user?.id),
staffId: Number(staff?.id),
patientName: `${patient?.firstName} ${patient?.lastName}`.trim(),
memberId: patient?.insuranceId,
dateOfBirth: formatDOB(patient?.dateOfBirth),
remarks: "",
serviceDate: serviceDate,
insuranceProvider: "",
status: "pending",
serviceLines: [
{
procedureCode: "",
procedureDate: serviceDate,
oralCavityArea: "",
toothNumber: "",
toothSurface: "",
billedAmount: 0,
},
],
});
// Sync patient data to form when patient updates
useEffect(() => {
if (patient) {
const fullName =
`${patient.firstName || ""} ${patient.lastName || ""}`.trim();
setForm((prev) => ({
...prev,
patientName: fullName,
dateOfBirth: formatDOB(patient.dateOfBirth),
memberId: patient.insuranceId || "",
patientId: patient.id,
}));
}
}, [patient]);
useEffect(() => {
setForm((prevForm) => {
const updatedLines = prevForm.serviceLines.map((line) => ({
...line,
procedureDate: serviceDate, // set all to current serviceDate string
}));
return {
...prevForm,
serviceLines: updatedLines,
serviceDate, // keep form.serviceDate in sync as well
};
});
}, [serviceDate]);
// Handle patient field changes (to make inputs controlled and editable)
const updatePatientField = (field: keyof Patient, value: any) => {
setPatient((prev) => (prev ? { ...prev, [field]: value } : null));
};
const updateServiceLine = (
index: number,
field: keyof ServiceLine,
value: any
) => {
const updatedLines = [...form.serviceLines];
if (updatedLines[index]) {
if (field === "billedAmount") {
updatedLines[index][field] = parseFloat(value) || 0;
} else {
updatedLines[index][field] = value;
}
}
setForm({ ...form, serviceLines: updatedLines });
};
const updateProcedureDate = (index: number, date: Date | undefined) => {
if (!date) return;
const formattedDate = format(date, "MM/dd/yyyy");
const updatedLines = [...form.serviceLines];
if (updatedLines[index]) {
updatedLines[index].procedureDate = formattedDate;
}
setForm({ ...form, serviceLines: updatedLines });
};
// FILE UPLOAD ZONE // FILE UPLOAD ZONE
const [uploadedFiles, setUploadedFiles] = useState<File[]>([]); const [uploadedFiles, setUploadedFiles] = useState<File[]>([]);
const [isUploading, setIsUploading] = useState(false); const [isUploading, setIsUploading] = useState(false);
@@ -269,6 +334,7 @@ export function ClaimForm({
setIsUploading(false); setIsUploading(false);
}; };
// Delta MA Button Handler
const handleDeltaMASubmit = () => { const handleDeltaMASubmit = () => {
const appointmentData = { const appointmentData = {
patientId: patientId, patientId: patientId,
@@ -284,9 +350,9 @@ export function ClaimForm({
const { id, createdAt, userId, ...sanitizedFields } = patient; const { id, createdAt, userId, ...sanitizedFields } = patient;
const updatedPatientFields = { const updatedPatientFields = {
id, id,
... sanitizedFields, ...sanitizedFields,
insuranceProvider: "Delta MA", insuranceProvider: "Delta MA",
} };
onHandleUpdatePatient(updatedPatientFields); onHandleUpdatePatient(updatedPatientFields);
} else { } else {
toast({ toast({
@@ -297,6 +363,12 @@ export function ClaimForm({
} }
// 3. Create Claim(if not) // 3. Create Claim(if not)
onSubmit({
...form,
staffId: Number(staff?.id),
patientId: patient?.id,
insuranceProvider: "Delta MA",
});
// 4. Close form // 4. Close form
onClose(); onClose();
@@ -321,21 +393,24 @@ export function ClaimForm({
<Label htmlFor="memberId">Member ID</Label> <Label htmlFor="memberId">Member ID</Label>
<Input <Input
id="memberId" id="memberId"
value={patient?.insuranceId || ""} value={form.memberId}
onChange={(e) => onChange={(e) =>
updatePatientField("insuranceId", e.target.value) setForm({ ...form, memberId: e.target.value })
} }
disabled={isLoading}
/> />
</div> </div>
<div> <div>
<Label htmlFor="dateOfBirth">Date Of Birth</Label> <Label htmlFor="dateOfBirth">Date Of Birth</Label>
<Input <Input
id="dateOfBirth" id="dateOfBirth"
value={formatDOB(patient?.dateOfBirth)} value={form.dateOfBirth}
onChange={(e) => onChange={(e) => {
updatePatientField("dateOfBirth", e.target.value) updatePatientField("dateOfBirth", e.target.value);
} setForm((prev) => ({
...prev,
dateOfBirth: e.target.value,
}));
}}
disabled={isLoading} disabled={isLoading}
/> />
</div> </div>
@@ -344,9 +419,14 @@ export function ClaimForm({
<Input <Input
id="firstName" id="firstName"
value={patient?.firstName || ""} value={patient?.firstName || ""}
onChange={(e) => onChange={(e) => {
updatePatientField("firstName", e.target.value) updatePatientField("firstName", e.target.value);
} setForm((prev) => ({
...prev,
patientName:
`${e.target.value} ${patient?.lastName || ""}`.trim(),
}));
}}
disabled={isLoading} disabled={isLoading}
/> />
</div> </div>
@@ -355,9 +435,14 @@ export function ClaimForm({
<Input <Input
id="lastName" id="lastName"
value={patient?.lastName || ""} value={patient?.lastName || ""}
onChange={(e) => onChange={(e) => {
updatePatientField("lastName", e.target.value) updatePatientField("lastName", e.target.value);
} setForm((prev) => ({
...prev,
patientName:
`${patient?.firstName || ""} ${e.target.value}`.trim(),
}));
}}
disabled={isLoading} disabled={isLoading}
/> />
</div> </div>
@@ -372,8 +457,8 @@ export function ClaimForm({
id="remarks" id="remarks"
className="flex-grow" className="flex-grow"
placeholder="Paste clinical notes here" placeholder="Paste clinical notes here"
value={remarks} value={form.remarks}
onChange={(e) => setRemarks(e.target.value)} onChange={(e) => setForm({ ...form, remarks: e.target.value })}
/> />
</div> </div>
@@ -393,7 +478,7 @@ export function ClaimForm({
className="w-[140px] justify-start text-left font-normal mr-4" className="w-[140px] justify-start text-left font-normal mr-4"
> >
<CalendarIcon className="mr-2 h-4 w-4" /> <CalendarIcon className="mr-2 h-4 w-4" />
{serviceDate} {form.serviceDate}
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start"> <PopoverContent className="w-auto p-0" align="start">
@@ -415,7 +500,13 @@ export function ClaimForm({
const selected = staffMembersRaw.find( const selected = staffMembersRaw.find(
(member) => member.id === id (member) => member.id === id
); );
if (selected) setStaff(selected); if (selected) {
setStaff(selected);
setForm((prev) => ({
...prev,
staffId: Number(selected.id),
}));
}
}} }}
> >
<SelectTrigger className="w-36"> <SelectTrigger className="w-36">
@@ -445,13 +536,13 @@ export function ClaimForm({
</div> </div>
{/* Dynamic Rows */} {/* Dynamic Rows */}
{serviceLines.map((line, i) => ( {form.serviceLines.map((line, i) => (
<div key={i} className="grid grid-cols-6 gap-4 mb-2"> <div key={i} className="grid grid-cols-6 gap-4 mb-2">
<Input <Input
placeholder="eg. D0120" placeholder="eg. D0120"
value={line.procedure_code} value={line.procedureCode}
onChange={(e) => onChange={(e) =>
updateServiceLine(i, "procedure_code", e.target.value) updateServiceLine(i, "procedureCode", e.target.value)
} }
/> />
@@ -463,13 +554,13 @@ export function ClaimForm({
className="w-full text-left font-normal" className="w-full text-left font-normal"
> >
<CalendarIcon className="mr-2 h-4 w-4" /> <CalendarIcon className="mr-2 h-4 w-4" />
{line.procedure_date || "Pick Date"} {line.procedureDate || "Pick Date"}
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-auto p-0"> <PopoverContent className="w-auto p-0">
<Calendar <Calendar
mode="single" mode="single"
selected={new Date(line.procedure_date)} selected={new Date(line.procedureDate)}
onSelect={(date) => updateProcedureDate(i, date)} onSelect={(date) => updateProcedureDate(i, date)}
/> />
</PopoverContent> </PopoverContent>
@@ -498,6 +589,7 @@ export function ClaimForm({
/> />
<Input <Input
placeholder="$0.00" placeholder="$0.00"
type="number"
value={line.billedAmount} value={line.billedAmount}
onChange={(e) => onChange={(e) =>
updateServiceLine(i, "billedAmount", e.target.value) updateServiceLine(i, "billedAmount", e.target.value)
@@ -509,17 +601,20 @@ export function ClaimForm({
<Button <Button
variant="outline" variant="outline"
onClick={() => onClick={() =>
setServiceLines([ setForm((prev) => ({
...serviceLines, ...prev,
{ serviceLines: [
procedure_code: "", ...prev.serviceLines,
procedure_date: serviceDate, {
oralCavityArea: "", procedureCode: "",
toothNumber: "", procedureDate: serviceDate,
toothSurface: "", oralCavityArea: "",
billedAmount: "", toothNumber: "",
}, toothSurface: "",
]) billedAmount: 0,
},
],
}))
} }
> >
+ Add Service Line + Add Service Line

View File

@@ -279,8 +279,6 @@ export default function ClaimsPage() {
title: "Claim submitted successfully", title: "Claim submitted successfully",
variant: "default", variant: "default",
}); });
closeClaim();
// optionally refetch claims or appointments if needed
}, },
onError: (error: any) => { onError: (error: any) => {
toast({ toast({

View File

@@ -90,6 +90,9 @@ model Claim {
appointmentId Int appointmentId Int
userId Int userId Int
staffId Int staffId Int
patientName String
memberId String
dateOfBirth DateTime @db.Date
remarks String remarks String
serviceDate DateTime serviceDate DateTime
insuranceProvider String // e.g., "Delta MA" insuranceProvider String // e.g., "Delta MA"