claim routes work being done

This commit is contained in:
2025-05-30 20:22:06 +05:30
parent ebd5f514be
commit b80acf882c
6 changed files with 337 additions and 23 deletions

View File

@@ -0,0 +1,159 @@
import { Router } from "express";
import { Request, Response } from "express";
import { storage } from "../storage";
import { z } from "zod";
import {
ClaimUncheckedCreateInputObjectSchema,
} from "@repo/db/usedSchemas";
const router = Router();
// Define Zod schemas
const ClaimSchema = (
ClaimUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
id: true,
createdAt: true,
updatedAt: true,
});
type InsertClaim = z.infer<typeof ClaimSchema>;
const updateClaimSchema = (
ClaimUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
)
.omit({
id: true,
createdAt: true,
updatedAt: true,
})
.partial();
type UpdateClaim = z.infer<typeof updateClaimSchema>;
// Routes
// Get all claims for the logged-in user
router.get("/", async (req: Request, res: Response) => {
try {
const claims = await storage.getClaimsByUserId(req.user!.id);
res.json(claims);
} catch (error) {
res.status(500).json({ message: "Failed to retrieve claims" });
}
});
// Get a single claim by ID
router.get("/:id", async (req: Request, res: Response): Promise<any> => {
try {
const idParam = req.params.id;
if (!idParam) {
return res.status(400).json({ error: "Missing claim ID" });
}
const claimId = parseInt(idParam, 10);
if (isNaN(claimId)) {
return res.status(400).json({ error: "Invalid claim ID" });
}
const claim = await storage.getClaim(claimId);
if (!claim) {
return res.status(404).json({ message: "Claim not found" });
}
if (claim.userId !== req.user!.id) {
return res.status(403).json({ message: "Forbidden" });
}
res.json(claim);
} catch (error) {
res.status(500).json({ message: "Failed to retrieve claim" });
}
});
// Create a new claim
router.post("/", async (req: Request, res: Response): Promise<any> => {
try {
const claimData = ClaimSchema.parse({
...req.body,
userId: req.user!.id,
});
const newClaim = await storage.createClaim(claimData);
res.status(201).json(newClaim);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({
message: "Validation error",
errors: error.format(),
});
}
res.status(500).json({ message: "Failed to create claim" });
}
});
// Update a claim
router.put("/:id", async (req: Request, res: Response): Promise<any> => {
try {
const idParam = req.params.id;
if (!idParam) {
return res.status(400).json({ error: "Missing claim ID" });
}
const claimId = parseInt(idParam, 10);
if (isNaN(claimId)) {
return res.status(400).json({ error: "Invalid claim ID" });
}
const existingClaim = await storage.getClaim(claimId);
if (!existingClaim) {
return res.status(404).json({ message: "Claim not found" });
}
if (existingClaim.userId !== req.user!.id) {
return res.status(403).json({ message: "Forbidden" });
}
const claimData = updateClaimSchema.parse(req.body);
const updatedClaim = await storage.updateClaim(claimId, claimData);
res.json(updatedClaim);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({
message: "Validation error",
errors: error.format(),
});
}
res.status(500).json({ message: "Failed to update claim" });
}
});
// Delete a claim
router.delete("/:id", async (req: Request, res: Response): Promise<any> => {
try {
const idParam = req.params.id;
if (!idParam) {
return res.status(400).json({ error: "Missing claim ID" });
}
const claimId = parseInt(idParam, 10);
if (isNaN(claimId)) {
return res.status(400).json({ error: "Invalid claim ID" });
}
const existingClaim = await storage.getClaim(claimId);
if (!existingClaim) {
return res.status(404).json({ message: "Claim not found" });
}
if (existingClaim.userId !== req.user!.id) {
return res.status(403).json({ message: "Forbidden" });
}
await storage.deleteClaim(claimId);
res.status(204).send();
} catch (error) {
res.status(500).json({ message: "Failed to delete claim" });
}
});
export default router;

View File

@@ -4,6 +4,7 @@ import appointmentRoutes from './appointements'
import userRoutes from './users' import userRoutes from './users'
import staffRoutes from './staffs' import staffRoutes from './staffs'
import pdfExtractionRoutes from './pdfExtraction'; import pdfExtractionRoutes from './pdfExtraction';
import claimsRoutes from './claims';
const router = Router(); const router = Router();
@@ -11,6 +12,7 @@ router.use('/patients', patientRoutes);
router.use('/appointments', appointmentRoutes); router.use('/appointments', appointmentRoutes);
router.use('/users', userRoutes); router.use('/users', userRoutes);
router.use('/staffs', staffRoutes); router.use('/staffs', staffRoutes);
router.use('/pdfExtraction/', pdfExtractionRoutes); router.use('/pdfExtraction', pdfExtractionRoutes);
router.use('/claims', claimsRoutes);
export default router; export default router;

View File

@@ -4,6 +4,7 @@ import {
PatientUncheckedCreateInputObjectSchema, PatientUncheckedCreateInputObjectSchema,
UserUncheckedCreateInputObjectSchema, UserUncheckedCreateInputObjectSchema,
StaffUncheckedCreateInputObjectSchema, StaffUncheckedCreateInputObjectSchema,
ClaimUncheckedCreateInputObjectSchema,
} from "@repo/db/usedSchemas"; } from "@repo/db/usedSchemas";
import { z } from "zod"; import { z } from "zod";
@@ -94,6 +95,29 @@ type RegisterFormValues = z.infer<typeof registerSchema>;
// staff types: // staff types:
type Staff = z.infer<typeof StaffUncheckedCreateInputObjectSchema>; type Staff = z.infer<typeof StaffUncheckedCreateInputObjectSchema>;
// Claim typse:
const insertClaimSchema = (
ClaimUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
id: true,
createdAt: true,
updatedAt: true,
});
type InsertClaim = z.infer<typeof insertClaimSchema>;
const updateClaimSchema = (
ClaimUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
)
.omit({
id: true,
createdAt: true,
updatedAt: true,
})
.partial();
type UpdateClaim = z.infer<typeof updateClaimSchema>;
type Claim = z.infer<typeof ClaimUncheckedCreateInputObjectSchema>;
export interface IStorage { export interface IStorage {
// User methods // User methods
getUser(id: number): Promise<User | undefined>; getUser(id: number): Promise<User | undefined>;
@@ -127,6 +151,15 @@ export interface IStorage {
createStaff(staff: Staff): Promise<Staff>; createStaff(staff: Staff): Promise<Staff>;
updateStaff(id: number, updates: Partial<Staff>): Promise<Staff | undefined>; updateStaff(id: number, updates: Partial<Staff>): Promise<Staff | undefined>;
deleteStaff(id: number): Promise<boolean>; deleteStaff(id: number): Promise<boolean>;
// Claim methods
getClaim(id: number): Promise<Claim | undefined>;
getClaimsByUserId(userId: number): Promise<Claim[]>;
getClaimsByPatientId(patientId: number): Promise<Claim[]>;
getClaimsByAppointmentId(appointmentId: number): Promise<Claim[]>;
createClaim(claim: InsertClaim): Promise<Claim>;
updateClaim(id: number, updates: UpdateClaim): Promise<Claim>;
deleteClaim(id: number): Promise<void>;
} }
export const storage: IStorage = { export const storage: IStorage = {
@@ -281,4 +314,45 @@ export const storage: IStorage = {
return false; return false;
} }
}, },
// Claim methods implementation
async getClaim(id: number): Promise<Claim | undefined> {
const claim = await db.claim.findUnique({ where: { id } });
return claim ?? undefined;
},
async getClaimsByUserId(userId: number): Promise<Claim[]> {
return await db.claim.findMany({ where: { userId } });
},
async getClaimsByPatientId(patientId: number): Promise<Claim[]> {
return await db.claim.findMany({ where: { patientId } });
},
async getClaimsByAppointmentId(appointmentId: number): Promise<Claim[]> {
return await db.claim.findMany({ where: { appointmentId } });
},
async createClaim(claim: InsertClaim): Promise<Claim> {
return await db.claim.create({ data: claim as Claim });
},
async updateClaim(id: number, updates: UpdateClaim): Promise<Claim> {
try {
return await db.claim.update({
where: { id },
data: updates,
});
} catch (err) {
throw new Error(`Claim with ID ${id} not found`);
}
},
async deleteClaim(id: number): Promise<void> {
try {
await db.claim.delete({ where: { id } });
} catch (err) {
throw new Error(`Claim with ID ${id} not found`);
}
},
}; };

View File

@@ -35,6 +35,18 @@ const PatientSchema = (
}); });
type Patient = z.infer<typeof PatientSchema>; type Patient = z.infer<typeof PatientSchema>;
const updatePatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
)
.omit({
id: true,
createdAt: true,
userId: true,
})
.partial();
type UpdatePatient = z.infer<typeof updatePatientSchema>;
//creating types out of schema auto generated. //creating types out of schema auto generated.
type Appointment = z.infer<typeof AppointmentUncheckedCreateInputObjectSchema>; type Appointment = z.infer<typeof AppointmentUncheckedCreateInputObjectSchema>;
@@ -73,6 +85,7 @@ interface ClaimFormProps {
onHandleAppointmentSubmit: ( onHandleAppointmentSubmit: (
appointmentData: InsertAppointment | UpdateAppointment appointmentData: InsertAppointment | UpdateAppointment
) => void; ) => void;
onHandleUpdatePatient: (patient: UpdatePatient & { id: number }) => void;
onClose: () => void; onClose: () => void;
} }
@@ -87,11 +100,13 @@ export function ClaimForm({
patientId, patientId,
extractedData, extractedData,
onHandleAppointmentSubmit, onHandleAppointmentSubmit,
onHandleUpdatePatient,
onSubmit, onSubmit,
onClose, onClose,
}: ClaimFormProps) { }: ClaimFormProps) {
const { toast } = useToast(); const { toast } = useToast();
const [insuranceProvider, setInsuranceProvider] = useState(null)
// Query patient if patientId provided // Query patient if patientId provided
const { const {
data: fetchedPatient, data: fetchedPatient,
@@ -254,6 +269,39 @@ export function ClaimForm({
setIsUploading(false); setIsUploading(false);
}; };
const handleDeltaMASubmit = () => {
const appointmentData = {
patientId: patientId,
date: convertToISODate(serviceDate),
staffId: staff?.id,
};
// 1. Create or update appointment
onHandleAppointmentSubmit(appointmentData);
// 2. Update patient
if (patient && typeof patient.id === "number") {
const { id, createdAt, userId, ...sanitizedFields } = patient;
const updatedPatientFields = {
id,
... sanitizedFields,
insuranceProvider: "Delta MA",
}
onHandleUpdatePatient(updatedPatientFields);
} else {
toast({
title: "Error",
description: "Cannot update patient: Missing or invalid patient data",
variant: "destructive",
});
}
// 3. Create Claim(if not)
// 4. Close form
onClose();
};
return ( return (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4 overflow-y-auto"> <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4 overflow-y-auto">
<Card className="w-full max-w-5xl max-h-[90vh] overflow-y-auto bg-white"> <Card className="w-full max-w-5xl max-h-[90vh] overflow-y-auto bg-white">
@@ -536,19 +584,7 @@ export function ClaimForm({
<Button <Button
className="w-32" className="w-32"
variant="outline" variant="outline"
onClick={() => { onClick={handleDeltaMASubmit}
const appointmentData = {
patientId: patientId,
date: convertToISODate(serviceDate),
staffId: staff?.id,
};
// 1. Create or update appointment
onHandleAppointmentSubmit(appointmentData);
// close form
onClose();
}}
> >
Delta MA Delta MA
</Button> </Button>

View File

@@ -68,9 +68,6 @@ export default function ClaimsPage() {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [isClaimFormOpen, setIsClaimFormOpen] = useState(false); const [isClaimFormOpen, setIsClaimFormOpen] = useState(false);
const [selectedPatient, setSelectedPatient] = useState<number | null>(null); const [selectedPatient, setSelectedPatient] = useState<number | null>(null);
const [selectedAppointment, setSelectedAppointment] = useState<number | null>(
null
);
const { toast } = useToast(); const { toast } = useToast();
const { user } = useAuth(); const { user } = useAuth();
const [claimFormData, setClaimFormData] = useState<any>({ const [claimFormData, setClaimFormData] = useState<any>({
@@ -126,6 +123,35 @@ export default function ClaimsPage() {
}, },
}); });
// Update patient mutation
const updatePatientMutation = useMutation({
mutationFn: async ({
id,
patient,
}: {
id: number;
patient: UpdatePatient;
}) => {
const res = await apiRequest("PUT", `/api/patients/${id}`, patient);
return res.json();
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["/api/patients/"] });
toast({
title: "Success",
description: "Patient updated successfully!",
variant: "default",
});
},
onError: (error) => {
toast({
title: "Error",
description: `Failed to update patient: ${error.message}`,
variant: "destructive",
});
},
});
// Create appointment mutation // Create appointment mutation
const createAppointmentMutation = useMutation({ const createAppointmentMutation = useMutation({
mutationFn: async (appointment: InsertAppointment) => { mutationFn: async (appointment: InsertAppointment) => {
@@ -281,9 +307,8 @@ export default function ClaimsPage() {
setIsMobileMenuOpen(!isMobileMenuOpen); setIsMobileMenuOpen(!isMobileMenuOpen);
}; };
const handleNewClaim = (patientId: number, appointmentId: number) => { const handleNewClaim = (patientId: number) => {
setSelectedPatient(patientId); setSelectedPatient(patientId);
setSelectedAppointment(appointmentId);
setIsClaimFormOpen(true); setIsClaimFormOpen(true);
const patient = patients.find((p) => p.id === patientId); const patient = patients.find((p) => p.id === patientId);
@@ -295,7 +320,6 @@ export default function ClaimsPage() {
const closeClaim = () => { const closeClaim = () => {
setIsClaimFormOpen(false); setIsClaimFormOpen(false);
setSelectedPatient(null); setSelectedPatient(null);
setSelectedAppointment(null);
setClaimFormData({ setClaimFormData({
patientId: null, patientId: null,
serviceDate: "", serviceDate: "",
@@ -398,6 +422,24 @@ export default function ClaimsPage() {
}> }>
); );
// Update Patient ( for insuranceId and Insurance Provider)
const handleUpdatePatient = (patient: UpdatePatient & { id?: number }) => {
if (patient && user) {
const { id, ...sanitizedPatient } = patient;
updatePatientMutation.mutate({
id: Number(patient.id),
patient: sanitizedPatient,
});
} else {
console.error("No current patient or user found for update");
toast({
title: "Error",
description: "Cannot update patient: No patient or user found",
variant: "destructive",
});
}
};
return ( return (
<div className="flex h-screen overflow-hidden bg-gray-100"> <div className="flex h-screen overflow-hidden bg-gray-100">
<Sidebar <Sidebar
@@ -429,7 +471,6 @@ export default function ClaimsPage() {
const firstPatient = patientsWithAppointments[0]; const firstPatient = patientsWithAppointments[0];
handleNewClaim( handleNewClaim(
Number(firstPatient?.patientId), Number(firstPatient?.patientId),
Number(firstPatient?.appointmentId)
); );
} else { } else {
toast({ toast({
@@ -465,7 +506,7 @@ export default function ClaimsPage() {
key={item.patientId} key={item.patientId}
className="py-4 flex items-center justify-between cursor-pointer hover:bg-gray-50" className="py-4 flex items-center justify-between cursor-pointer hover:bg-gray-50"
onClick={() => onClick={() =>
handleNewClaim(item.patientId, item.appointmentId) handleNewClaim(item.patientId)
} }
> >
<div> <div>
@@ -519,6 +560,7 @@ export default function ClaimsPage() {
extractedData={claimFormData} extractedData={claimFormData}
onSubmit={handleClaimSubmit} onSubmit={handleClaimSubmit}
onHandleAppointmentSubmit={handleAppointmentSubmit} onHandleAppointmentSubmit={handleAppointmentSubmit}
onHandleUpdatePatient={handleUpdatePatient}
/> />
)} )}
</div> </div>

View File

@@ -2,4 +2,5 @@
export * from '../shared/schemas/objects/AppointmentUncheckedCreateInput.schema'; export * from '../shared/schemas/objects/AppointmentUncheckedCreateInput.schema';
export * from '../shared/schemas/objects/PatientUncheckedCreateInput.schema'; export * from '../shared/schemas/objects/PatientUncheckedCreateInput.schema';
export * from '../shared/schemas/objects/UserUncheckedCreateInput.schema'; export * from '../shared/schemas/objects/UserUncheckedCreateInput.schema';
export * from '../shared/schemas/objects/StaffUncheckedCreateInput.schema' export * from '../shared/schemas/objects/StaffUncheckedCreateInput.schema'
export * from '../shared/schemas/objects/ClaimUncheckedCreateInput.schema'