feat: add Procedure Duration/Time Slot settings and custom appointment type
- Add Settings > Advanced > Procedure Duration/Time Slot page with three sections: 1. Procedure Duration: CDT codes with durations (editable table, save per section) 2. Doctor Time Slot: drag-to-block visual grid (A/B/C columns, 8 AM–9 PM, edit/delete slots) 3. Hygienist Time Slot: procedure descriptions with durations - Backend: ProcedureTimeslot Prisma model, storage, and GET/PUT /api/procedure-timeslot route - DB migration: procedure_timeslot table - Appointment form: when type is "Other", show a free-text input for custom description; saved as other:<description> and decoded for display on the schedule card Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,7 @@ import twilioRoutes from "./twilio";
|
||||
import aiSettingsRoutes from "./ai-settings";
|
||||
import officeHoursRoutes from "./office-hours";
|
||||
import officeContactRoutes from "./office-contact";
|
||||
import procedureTimeslotRoutes from "./procedure-timeslot";
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -60,5 +61,6 @@ router.use("/twilio", twilioRoutes);
|
||||
router.use("/ai", aiSettingsRoutes);
|
||||
router.use("/office-hours", officeHoursRoutes);
|
||||
router.use("/office-contact", officeContactRoutes);
|
||||
router.use("/procedure-timeslot", procedureTimeslotRoutes);
|
||||
|
||||
export default router;
|
||||
|
||||
37
apps/Backend/src/routes/procedure-timeslot.ts
Normal file
37
apps/Backend/src/routes/procedure-timeslot.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import express, { Request, Response } from "express";
|
||||
import { storage } from "../storage";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// GET /api/procedure-timeslot
|
||||
router.get("/", async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user?.id;
|
||||
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||
|
||||
const record = await storage.getProcedureTimeslot(userId);
|
||||
return res.status(200).json(record ?? null);
|
||||
} catch (err) {
|
||||
return res.status(500).json({ error: "Failed to fetch procedure timeslot", details: String(err) });
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /api/procedure-timeslot
|
||||
router.put("/", async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user?.id;
|
||||
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||
|
||||
const { data } = req.body;
|
||||
if (!data || typeof data !== "object") {
|
||||
return res.status(400).json({ error: "Invalid data payload" });
|
||||
}
|
||||
|
||||
const record = await storage.upsertProcedureTimeslot(userId, data);
|
||||
return res.status(200).json(record);
|
||||
} catch (err) {
|
||||
return res.status(500).json({ error: "Failed to save procedure timeslot", details: String(err) });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -21,6 +21,7 @@ import { twilioStorage } from "./twilio-storage";
|
||||
import { aiSettingsStorage } from "./ai-settings-storage";
|
||||
import { officeHoursStorage } from "./office-hours-storage";
|
||||
import { officeContactStorage } from "./office-contact-storage";
|
||||
import { procedureTimeslotStorage } from "./procedure-timeslot-storage";
|
||||
|
||||
|
||||
export const storage = {
|
||||
@@ -45,6 +46,7 @@ export const storage = {
|
||||
...aiSettingsStorage,
|
||||
...officeHoursStorage,
|
||||
...officeContactStorage,
|
||||
...procedureTimeslotStorage,
|
||||
|
||||
};
|
||||
|
||||
|
||||
15
apps/Backend/src/storage/procedure-timeslot-storage.ts
Normal file
15
apps/Backend/src/storage/procedure-timeslot-storage.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { prisma as db } from "@repo/db/client";
|
||||
|
||||
export const procedureTimeslotStorage = {
|
||||
async getProcedureTimeslot(userId: number) {
|
||||
return db.procedureTimeslot.findUnique({ where: { userId } });
|
||||
},
|
||||
|
||||
async upsertProcedureTimeslot(userId: number, data: object) {
|
||||
return db.procedureTimeslot.upsert({
|
||||
where: { userId },
|
||||
update: { data },
|
||||
create: { userId, data },
|
||||
});
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user