checkpoint, form data fixed, need to modify its route
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user