feat: add schedule column labels, office hours enforcement, and appointment move fix
- Schedule columns default to labels A–F (localStorage, per-browser, click to rename) - Settings → Advanced → Office Hours: configure Doctors (A-C) and Hygienists (D-F) AM/PM hours per weekday - Gray out schedule slots outside office hours; override dialog for manual exceptions - Override Office Hours toggle: select specific dates where all slots are open - Fix appointment move: send only real DB fields to avoid Zod strict-mode rejection of computed fields (hasProcedures, hasClaimWithNumber) - Fix backend PUT /appointments: safe error logging to prevent Prisma error crashing Node inspect - Add OfficeHours Prisma model and GET/PUT /api/office-hours route Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -381,34 +381,37 @@ router.put(
|
||||
const newYMD = new Date(date).toISOString().slice(0, 10);
|
||||
const isDateChanged = oldYMD !== newYMD;
|
||||
|
||||
const updatePayload = {
|
||||
...appointmentData,
|
||||
...(isDateChanged ? { eligibilityStatus: "UNKNOWN" as const } : {}),
|
||||
};
|
||||
// Only pass the fields that are safe to update; never overwrite patientId/userId via this route
|
||||
const updatePayload: Record<string, unknown> = {};
|
||||
if (appointmentData.staffId !== undefined) updatePayload.staffId = appointmentData.staffId;
|
||||
if (appointmentData.title !== undefined) updatePayload.title = appointmentData.title;
|
||||
if (appointmentData.date !== undefined) updatePayload.date = appointmentData.date;
|
||||
if (appointmentData.startTime !== undefined) updatePayload.startTime = appointmentData.startTime;
|
||||
if (appointmentData.endTime !== undefined) updatePayload.endTime = appointmentData.endTime;
|
||||
if (appointmentData.type !== undefined) updatePayload.type = appointmentData.type;
|
||||
if (appointmentData.status !== undefined) updatePayload.status = appointmentData.status;
|
||||
if (appointmentData.notes !== undefined) updatePayload.notes = appointmentData.notes;
|
||||
if (isDateChanged) updatePayload.eligibilityStatus = "UNKNOWN";
|
||||
|
||||
// Update appointment
|
||||
const updatedAppointment = await storage.updateAppointment(
|
||||
appointmentId,
|
||||
updatePayload
|
||||
updatePayload as any
|
||||
);
|
||||
return res.json(updatedAppointment);
|
||||
} catch (error) {
|
||||
console.error("Error updating appointment:", error);
|
||||
// Prisma error objects crash Node's util.inspect — always log as string
|
||||
const msg = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
||||
console.error("Error updating appointment:", msg);
|
||||
|
||||
if (error instanceof z.ZodError) {
|
||||
console.log(
|
||||
"Validation error details:",
|
||||
JSON.stringify(error.format(), null, 2)
|
||||
);
|
||||
return res.status(400).json({
|
||||
message: "Validation error",
|
||||
errors: error.format(),
|
||||
});
|
||||
const fieldErrors = error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(" | ");
|
||||
console.error("Zod validation in PUT /appointments:", fieldErrors);
|
||||
return res.status(400).json({ message: fieldErrors });
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
message: "Failed to update appointment",
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
return res.status(500).json({
|
||||
message: msg,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user