feat(portal): replace mock data with real session-driven API calls (#152)

Closes GRO-205. Reviewed and approved by CTO (The Dogfather) and QA (Lint Roller). cc @cpfarhood
This commit was merged in pull request #152.
This commit is contained in:
groombook-engineer[bot]
2026-03-29 07:08:35 +00:00
committed by GitHub
parent 3834e45b66
commit 4746a63292
24 changed files with 4230 additions and 1048 deletions
+20 -3
View File
@@ -19,9 +19,10 @@ import { impersonationRouter } from "./routes/impersonation.js";
import { settingsRouter } from "./routes/settings.js";
import { searchRouter } from "./routes/search.js";
import { calendarRouter } from "./routes/calendar.js";
import { getDb, businessSettings } from "@groombook/db";
import { setupRouter } from "./routes/setup.js";
import { getDb, businessSettings, eq, staff } from "@groombook/db";
import { authMiddleware } from "./middleware/auth.js";
import { resolveStaffMiddleware, requireRole } from "./middleware/rbac.js";
import { resolveStaffMiddleware, requireRole, requireRoleOrSuperUser, requireSuperUser } from "./middleware/rbac.js";
import { devRouter } from "./routes/dev.js";
import { adminSeedRouter } from "./routes/admin/seed.js";
import { startReminderScheduler } from "./services/reminders.js";
@@ -67,6 +68,17 @@ app.get("/api/branding", async (c) => {
// Public iCal calendar feed — token auth in URL, no auth middleware required
app.route("/api/calendar", calendarRouter);
// Public setup status — no auth required, must be registered before auth middleware
app.get("/api/setup/status", async (c) => {
const db = getDb();
const [superUser] = await db
.select({ id: staff.id })
.from(staff)
.where(eq(staff.isSuperUser, true))
.limit(1);
return c.json({ needsSetup: !superUser });
});
// Protected API routes
const api = app.basePath("/api");
api.use("*", authMiddleware);
@@ -82,8 +94,10 @@ api.route("/auth", authRouter);
// Manager-only: admin settings, reports, invoices, impersonation
// Staff CRUD: all roles may READ; manager-only for CREATE/UPDATE/DELETE
api.on(["GET"], "/staff/*", requireRole("manager", "receptionist", "groomer"));
api.use("/staff/*", requireRole("manager"));
// Staff write routes: manager OR super-user (combined guard — avoids AND stacking)
api.on(["POST", "PATCH", "DELETE"], "/staff/*", requireRoleOrSuperUser("manager"));
api.use("/admin/*", requireRole("manager"));
api.use("/admin/settings/*", requireSuperUser());
api.use("/reports/*", requireRole("manager"));
api.use("/invoices/*", requireRole("manager"));
api.use("/impersonation/*", requireRole("manager"));
@@ -123,6 +137,9 @@ api.on(
);
// ──────────────────────────────────────────────────────────────────────────────
// Setup: POST /api/setup (authenticated) — requires staff context from auth middleware
api.route("/setup", setupRouter);
api.route("/clients", clientsRouter);
api.route("/pets", petsRouter);
api.route("/services", servicesRouter);