feat: add Office Contact settings page and reorder Advanced sidebar

- Add OfficeContact Prisma model with receptionist name, dentist name, phone, email, fax fields
- Create GET/PUT /api/office-contact backend route and storage
- Add OfficeContactCard frontend component under Settings > Advanced
- Reorder Advanced sidebar: Office Hours → Office Contact → Twilio Settings → Google AI Settings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gitead
2026-05-05 21:19:30 -04:00
parent 2312ad66ca
commit 800008792a
188 changed files with 3780 additions and 173 deletions

View File

@@ -27,6 +27,7 @@ import jobMonitorRoutes from "./job-monitor";
import twilioRoutes from "./twilio";
import aiSettingsRoutes from "./ai-settings";
import officeHoursRoutes from "./office-hours";
import officeContactRoutes from "./office-contact";
const router = Router();
@@ -58,5 +59,6 @@ router.use("/job-monitor", jobMonitorRoutes);
router.use("/twilio", twilioRoutes);
router.use("/ai", aiSettingsRoutes);
router.use("/office-hours", officeHoursRoutes);
router.use("/office-contact", officeContactRoutes);
export default router;

View File

@@ -0,0 +1,39 @@
import express, { Request, Response } from "express";
import { storage } from "../storage";
const router = express.Router();
// GET /api/office-contact
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.getOfficeContact(userId);
return res.status(200).json(record ?? null);
} catch (err) {
return res.status(500).json({ error: "Failed to fetch office contact", details: String(err) });
}
});
// PUT /api/office-contact
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 { receptionistName, dentistName, phoneNumber, email, fax } = req.body;
const record = await storage.upsertOfficeContact(userId, {
receptionistName: receptionistName ?? undefined,
dentistName: dentistName ?? undefined,
phoneNumber: phoneNumber ?? undefined,
email: email ?? undefined,
fax: fax ?? undefined,
});
return res.status(200).json(record);
} catch (err) {
return res.status(500).json({ error: "Failed to save office contact", details: String(err) });
}
});
export default router;

View File

@@ -20,6 +20,7 @@ import { cronJobLogStorage } from "./cron-job-log-storage";
import { twilioStorage } from "./twilio-storage";
import { aiSettingsStorage } from "./ai-settings-storage";
import { officeHoursStorage } from "./office-hours-storage";
import { officeContactStorage } from "./office-contact-storage";
export const storage = {
@@ -43,6 +44,7 @@ export const storage = {
...twilioStorage,
...aiSettingsStorage,
...officeHoursStorage,
...officeContactStorage,
};

View File

@@ -0,0 +1,21 @@
import { prisma as db } from "@repo/db/client";
export const officeContactStorage = {
async getOfficeContact(userId: number) {
return db.officeContact.findUnique({ where: { userId } });
},
async upsertOfficeContact(userId: number, data: {
receptionistName?: string;
dentistName?: string;
phoneNumber?: string;
email?: string;
fax?: string;
}) {
return db.officeContact.upsert({
where: { userId },
update: data,
create: { userId, ...data },
});
},
};