import { Router } from "express"; import type { Request, Response } from "express"; import { storage } from "../storage"; import { AppointmentUncheckedCreateInputObjectSchema, PatientUncheckedCreateInputObjectSchema, } from "@repo/db/usedSchemas"; import { z } from "zod"; import { extractDobParts } from "../utils/DobParts"; const router = Router(); //creating types out of schema auto generated. type Appointment = z.infer; const insertAppointmentSchema = ( AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject ).omit({ id: true, createdAt: true, }); type InsertAppointment = z.infer; const updateAppointmentSchema = ( AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject ) .omit({ id: true, createdAt: true, }) .partial(); type UpdateAppointment = z.infer; const PatientSchema = ( PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject ).omit({ appointments: true, }); type Patient = z.infer; const insertPatientSchema = ( PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject ).omit({ id: true, createdAt: true, }); type InsertPatient = z.infer; const updatePatientSchema = ( PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject ) .omit({ id: true, createdAt: true, userId: true, }) .partial(); type UpdatePatient = z.infer; // Patient Routes // Get all patients for the logged-in user router.get("/", async (req, res) => { try { const patients = await storage.getPatientsByUserId(req.user!.id); res.json(patients); } catch (error) { res.status(500).json({ message: "Failed to retrieve patients" }); } }); // Get recent patients (paginated) router.get("/recent", async (req: Request, res: Response) => { try { const limit = parseInt(req.query.limit as string) || 10; const offset = parseInt(req.query.offset as string) || 0; const [patients, totalCount] = await Promise.all([ storage.getRecentPatients(limit, offset), storage.getTotalPatientCount(), ]); res.json({ patients, totalCount }); } catch (error) { console.error("Failed to retrieve recent patients:", error); res.status(500).json({ message: "Failed to retrieve recent patients" }); } }); router.get("/search", async (req: Request, res: Response): Promise => { try { const { name, phone, insuranceId, gender, dob, term, limit = "10", offset = "0", } = req.query as Record; const filters: any = { userId: req.user!.id, }; if (term) { filters.OR = [ { firstName: { contains: term, mode: "insensitive" } }, { lastName: { contains: term, mode: "insensitive" } }, { phone: { contains: term, mode: "insensitive" } }, { insuranceId: { contains: term, mode: "insensitive" } }, ]; } if (name) { filters.OR = [ { firstName: { contains: name, mode: "insensitive" } }, { lastName: { contains: name, mode: "insensitive" } }, ]; } if (phone) { filters.phone = { contains: phone, mode: "insensitive" }; } if (insuranceId) { filters.insuranceId = { contains: insuranceId, mode: "insensitive" }; } if (gender) { filters.gender = gender; } if (dob) { const parsed = new Date(dob); if (isNaN(parsed.getTime())) { return res.status(400).json({ message: "Invalid date format for DOB. Use format: YYYY-MM-DD", }); } // Match exact dateOfBirth (optional: adjust for timezone) filters.dateOfBirth = parsed; } const [patients, totalCount] = await Promise.all([ storage.searchPatients({ filters, limit: parseInt(limit), offset: parseInt(offset), }), storage.countPatients(filters), ]); return res.json({ patients, totalCount }); } catch (error) { console.error("Search error:", error); return res.status(500).json({ message: "Failed to search patients" }); } }); // Get a single patient by ID router.get( "/:id", async (req: Request, res: Response): Promise => { try { const patientIdParam = req.params.id; // Ensure that patientIdParam exists and is a valid number if (!patientIdParam) { return res.status(400).json({ message: "Patient ID is required" }); } const patientId = parseInt(patientIdParam); const patient = await storage.getPatient(patientId); if (!patient) { return res.status(404).json({ message: "Patient not found" }); } // Ensure the patient belongs to the logged-in user if (patient.userId !== req.user!.id) { return res.status(403).json({ message: "Forbidden" }); } res.json(patient); } catch (error) { res.status(500).json({ message: "Failed to retrieve patient" }); } } ); // Create a new patient router.post("/", async (req: Request, res: Response): Promise => { try { // Validate request body const patientData = insertPatientSchema.parse({ ...req.body, userId: req.user!.id, }); const patient = await storage.createPatient(patientData); res.status(201).json(patient); } 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 patient" }); } }); // Update an existing patient router.put( "/:id", async (req: Request, res: Response): Promise => { try { const patientIdParam = req.params.id; // Ensure that patientIdParam exists and is a valid number if (!patientIdParam) { return res.status(400).json({ message: "Patient ID is required" }); } const patientId = parseInt(patientIdParam); // Check if patient exists and belongs to user const existingPatient = await storage.getPatient(patientId); if (!existingPatient) { return res.status(404).json({ message: "Patient not found" }); } if (existingPatient.userId !== req.user!.id) { return res.status(403).json({ message: "Forbidden" }); } // Validate request body const patientData = updatePatientSchema.parse(req.body); // Update patient const updatedPatient = await storage.updatePatient(patientId, patientData); res.json(updatedPatient); } 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 patient" }); } } ); // Delete a patient router.delete( "/:id", async (req: Request, res: Response): Promise => { try { const patientIdParam = req.params.id; // Ensure that patientIdParam exists and is a valid number if (!patientIdParam) { return res.status(400).json({ message: "Patient ID is required" }); } const patientId = parseInt(patientIdParam); // Check if patient exists and belongs to user const existingPatient = await storage.getPatient(patientId); if (!existingPatient) { return res.status(404).json({ message: "Patient not found" }); } if (existingPatient.userId !== req.user!.id) { console.warn( `User ${req.user!.id} tried to delete patient ${patientId} owned by ${existingPatient.userId}` ); return res .status(403) .json({ message: "Forbidden: Patient belongs to a different user" }); } // Delete patient await storage.deletePatient(patientId); res.status(204).send(); } catch (error: any) { console.error("Delete patient error:", error); res.status(500).json({ message: "Failed to delete patient" }); } } ); // Appointment Routes // Get all appointments for the logged-in user router.get("/appointments", async (req, res) => { try { const appointments = await storage.getAppointmentsByUserId(req.user!.id); res.json(appointments); } catch (error) { res.status(500).json({ message: "Failed to retrieve appointments" }); } }); // Get appointments for a specific patient router.get( "/:patientId/appointments", async (req: Request, res: Response): Promise => { try { const patientIdParam = req.params.id; // Ensure that patientIdParam exists and is a valid number if (!patientIdParam) { return res.status(400).json({ message: "Patient ID is required" }); } const patientId = parseInt(patientIdParam); // Check if patient exists and belongs to user const patient = await storage.getPatient(patientId); if (!patient) { return res.status(404).json({ message: "Patient not found" }); } if (patient.userId !== req.user!.id) { return res.status(403).json({ message: "Forbidden" }); } const appointments = await storage.getAppointmentsByPatientId(patientId); res.json(appointments); } catch (error) { res.status(500).json({ message: "Failed to retrieve appointments" }); } } ); export default router;