Files
DentalManagement2025/apps/Backend/src/routes/patients.ts

347 lines
9.3 KiB
TypeScript

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<typeof AppointmentUncheckedCreateInputObjectSchema>;
const insertAppointmentSchema = (
AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
id: true,
createdAt: true,
});
type InsertAppointment = z.infer<typeof insertAppointmentSchema>;
const updateAppointmentSchema = (
AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
)
.omit({
id: true,
createdAt: true,
})
.partial();
type UpdateAppointment = z.infer<typeof updateAppointmentSchema>;
const PatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
appointments: true,
});
type Patient = z.infer<typeof PatientSchema>;
const insertPatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
id: true,
createdAt: true,
});
type InsertPatient = z.infer<typeof insertPatientSchema>;
const updatePatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
)
.omit({
id: true,
createdAt: true,
userId: true,
})
.partial();
type UpdatePatient = z.infer<typeof updatePatientSchema>;
// 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<any> => {
try {
const {
name,
phone,
insuranceId,
gender,
dob,
term,
limit = "10",
offset = "0",
} = req.query as Record<string, string>;
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<any> => {
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<any> => {
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<any> => {
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<any> => {
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<any> => {
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;