patient table fixed
This commit is contained in:
@@ -6,6 +6,8 @@ import {
|
||||
PatientUncheckedCreateInputObjectSchema,
|
||||
} from "@repo/db/usedSchemas";
|
||||
import { z } from "zod";
|
||||
import { extractDobParts } from "../utils/DobParts";
|
||||
import { parseDobParts } from "../utils/DobPartsParsing";
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -86,7 +88,7 @@ router.get("/recent", async (req: Request, res: Response) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.get("/search", async (req: Request, res: Response) => {
|
||||
router.get("/search", async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const {
|
||||
name,
|
||||
@@ -94,14 +96,24 @@ router.get("/search", async (req: Request, res: Response) => {
|
||||
insuranceId,
|
||||
gender,
|
||||
dob,
|
||||
term,
|
||||
limit = "10",
|
||||
offset = "0",
|
||||
} = req.query as Record<string, string>;
|
||||
|
||||
const filters: any = {
|
||||
userId: req.user!.id, // Assumes auth middleware sets this
|
||||
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" } },
|
||||
@@ -122,9 +134,70 @@ router.get("/search", async (req: Request, res: Response) => {
|
||||
}
|
||||
|
||||
if (dob) {
|
||||
const parsedDate = new Date(dob);
|
||||
if (!isNaN(parsedDate.getTime())) {
|
||||
filters.dateOfBirth = parsedDate;
|
||||
const range = parseDobParts(dob);
|
||||
|
||||
if (range) {
|
||||
if (!filters.AND) filters.AND = [];
|
||||
|
||||
// Check what kind of match this was
|
||||
const fromYear = range.from.getUTCFullYear();
|
||||
const toYear = range.to.getUTCFullYear();
|
||||
const fromMonth = range.from.getUTCMonth() + 1; // Prisma months: 1-12
|
||||
const toMonth = range.to.getUTCMonth() + 1;
|
||||
const fromDay = range.from.getUTCDate();
|
||||
const toDay = range.to.getUTCDate();
|
||||
|
||||
const isFullDate =
|
||||
fromYear === toYear && fromMonth === toMonth && fromDay === toDay;
|
||||
|
||||
const isDayMonthOnly =
|
||||
fromYear === 1900 &&
|
||||
toYear === 2100 &&
|
||||
fromMonth === toMonth &&
|
||||
fromDay === toDay;
|
||||
|
||||
const isMonthOnly =
|
||||
fromYear === 1900 &&
|
||||
toYear === 2100 &&
|
||||
fromDay === 1 &&
|
||||
toDay >= 28 &&
|
||||
fromMonth === toMonth &&
|
||||
toMonth === toMonth;
|
||||
|
||||
const isYearOnly = fromMonth === 1 && toMonth === 12;
|
||||
|
||||
if (isFullDate) {
|
||||
filters.AND.push({
|
||||
dob_day: fromDay,
|
||||
dob_month: fromMonth,
|
||||
dob_year: fromYear,
|
||||
});
|
||||
} else if (isDayMonthOnly) {
|
||||
filters.AND.push({
|
||||
dob_day: fromDay,
|
||||
dob_month: fromMonth,
|
||||
});
|
||||
} else if (isMonthOnly) {
|
||||
filters.AND.push({
|
||||
dob_month: fromMonth,
|
||||
});
|
||||
} else if (isYearOnly) {
|
||||
filters.AND.push({
|
||||
dob_year: fromYear,
|
||||
});
|
||||
} else {
|
||||
// Fallback: search via dateOfBirth range
|
||||
filters.AND.push({
|
||||
dateOfBirth: {
|
||||
gte: range.from,
|
||||
lte: range.to,
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res.status(400).json({
|
||||
message: `Invalid date format for DOB. Try formats like "12 March", "March", "1980", "12/03/1980", etc.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,10 +210,10 @@ router.get("/search", async (req: Request, res: Response) => {
|
||||
storage.countPatients(filters),
|
||||
]);
|
||||
|
||||
res.json({ patients, totalCount });
|
||||
return res.json({ patients, totalCount });
|
||||
} catch (error) {
|
||||
console.error("Search error:", error);
|
||||
res.status(500).json({ message: "Failed to search patients" });
|
||||
return res.status(500).json({ message: "Failed to search patients" });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -177,7 +250,6 @@ router.get(
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// Create a new patient
|
||||
router.post("/", async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
@@ -187,8 +259,14 @@ router.post("/", async (req: Request, res: Response): Promise<any> => {
|
||||
userId: req.user!.id,
|
||||
});
|
||||
|
||||
// Create patient
|
||||
const patient = await storage.createPatient(patientData);
|
||||
// Extract dob_* from dateOfBirth
|
||||
const dobParts = extractDobParts(new Date(patientData.dateOfBirth));
|
||||
|
||||
const patient = await storage.createPatient({
|
||||
...patientData,
|
||||
...dobParts, // adds dob_day/month/year
|
||||
});
|
||||
|
||||
res.status(201).json(patient);
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
@@ -229,11 +307,15 @@ router.put(
|
||||
// Validate request body
|
||||
const patientData = updatePatientSchema.parse(req.body);
|
||||
|
||||
let dobParts = {};
|
||||
if (patientData.dateOfBirth) {
|
||||
dobParts = extractDobParts(new Date(patientData.dateOfBirth));
|
||||
}
|
||||
// Update patient
|
||||
const updatedPatient = await storage.updatePatient(
|
||||
patientId,
|
||||
patientData
|
||||
);
|
||||
const updatedPatient = await storage.updatePatient(patientId, {
|
||||
...patientData,
|
||||
...dobParts,
|
||||
});
|
||||
res.json(updatedPatient);
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
@@ -280,7 +362,7 @@ router.delete(
|
||||
// Delete patient
|
||||
await storage.deletePatient(patientId);
|
||||
res.status(204).send();
|
||||
} catch (error:any) {
|
||||
} catch (error: any) {
|
||||
console.error("Delete patient error:", error);
|
||||
res.status(500).json({ message: "Failed to delete patient" });
|
||||
}
|
||||
|
||||
7
apps/Backend/src/utils/DobParts.ts
Normal file
7
apps/Backend/src/utils/DobParts.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export function extractDobParts(date: Date) {
|
||||
return {
|
||||
dob_day: date.getUTCDate(),
|
||||
dob_month: date.getUTCMonth() + 1,
|
||||
dob_year: date.getUTCFullYear(),
|
||||
};
|
||||
}
|
||||
81
apps/Backend/src/utils/DobPartsParsing.ts
Normal file
81
apps/Backend/src/utils/DobPartsParsing.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
export function parseDobParts(input: string): { from: Date; to: Date } | null {
|
||||
if (!input || typeof input !== "string") return null;
|
||||
|
||||
const parts = input.trim().split(/[\s/-]+/).filter(Boolean);
|
||||
|
||||
if (parts.length === 1) {
|
||||
const part = parts[0]?.toLowerCase();
|
||||
|
||||
// Year
|
||||
if (part && /^\d{4}$/.test(part)) {
|
||||
const year = parseInt(part);
|
||||
return {
|
||||
from: new Date(Date.UTC(year, 0, 1)),
|
||||
to: new Date(Date.UTC(year, 11, 31, 23, 59, 59)),
|
||||
};
|
||||
}
|
||||
|
||||
// Month
|
||||
const month = part ? parseMonth(part) : null;
|
||||
if (month !== null) {
|
||||
return {
|
||||
from: new Date(Date.UTC(1900, month, 1)),
|
||||
to: new Date(Date.UTC(2100, month + 1, 0, 23, 59, 59)),
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parts.length === 2) {
|
||||
const [part1, part2] = parts;
|
||||
const day = tryParseInt(part1);
|
||||
const month = part2 ? parseMonth(part2) : null;
|
||||
|
||||
if (day !== null && month !== null) {
|
||||
return {
|
||||
from: new Date(Date.UTC(1900, month, day)),
|
||||
to: new Date(Date.UTC(2100, month, day, 23, 59, 59)),
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parts.length === 3) {
|
||||
const [part1, part2, part3] = parts;
|
||||
const day = tryParseInt(part1);
|
||||
const month = part2 ? parseMonth(part2) : null;
|
||||
const year = tryParseInt(part3);
|
||||
|
||||
if (day !== null && month !== null && year !== null) {
|
||||
return {
|
||||
from: new Date(Date.UTC(year, month, day)),
|
||||
to: new Date(Date.UTC(year, month, day, 23, 59, 59)),
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseMonth(input: string): number | null {
|
||||
const normalized = input.toLowerCase();
|
||||
const months = [
|
||||
"january", "february", "march", "april", "may", "june",
|
||||
"july", "august", "september", "october", "november", "december",
|
||||
];
|
||||
|
||||
const index = months.findIndex(
|
||||
(m) => m === normalized || m.startsWith(normalized)
|
||||
);
|
||||
return index !== -1 ? index : null;
|
||||
}
|
||||
|
||||
function tryParseInt(value?: string): number | null {
|
||||
if (!value) return null;
|
||||
const parsed = parseInt(value);
|
||||
return isNaN(parsed) ? null : parsed;
|
||||
}
|
||||
Reference in New Issue
Block a user