feat: rebrand AgendaPro -> AgendaMax + calendar fixes + current-week demo seed

- Rebrand all user-facing text to AgendaMax
- Fix sidebar scrolling: card container no longer overflows html (flex h-full min-h-0)
- Fix calendar toolbar hover: active buttons keep readable contrast
- Sticky calendar toolbar (month/week/day always visible on scroll)
- Seed guarantees 2-4 appointments per day for current week (Mon-Sat)
This commit is contained in:
AgendaPro Dev
2026-07-27 10:11:16 -06:00
parent 4227db0c8b
commit d796538fd9
57 changed files with 2477 additions and 322 deletions
+32 -1
View File
@@ -44,7 +44,7 @@ const checks = [
["services", "/services", (j) => j.services?.length === 12 && Array.isArray(j.services[0].employee_ids)],
["clients+stats", "/clients", (j) => j.clients?.length > 0 && j.clients[0].stats?.visits !== undefined],
["appointments joined", "/appointments?limit=5", (j) => j.appointments?.[0]?.service && j.appointments[0].client],
["dashboard overview", "/dashboard/overview", (j) => j.revenue_month > 0],
["dashboard overview", "/dashboard/overview", (j) => j.revenue_range > 0],
["best-employees", "/dashboard/best-employees", (j) => j.employees?.length === 6],
["best-services", "/dashboard/best-services", (j) => j.services?.length > 0],
["top-tickets", "/dashboard/top-tickets", (j) => j.tickets?.length > 0],
@@ -82,5 +82,36 @@ check("no auth 401", noAuth === 401);
const { status: empForb } = await req("GET", "/dashboard/overview", null, empLogin.token);
check("employee blocked dashboard 403", empForb === 403);
// ---- Permission boundary (server-side enforcement) ----
// F1: employees must NOT see commission_pct on any colleague row
const { json: empLeak } = await req("GET", "/employees", null, empLogin.token);
const leaked = (empLeak.employees || []).filter((e) => Object.prototype.hasOwnProperty.call(e, "commission_pct"));
check("F1: employee cannot read commission_pct", leaked.length === 0, `${leaked.length} rows leaked commission_pct`);
// F2: employees cannot mutate a colleague's appointment (PATCH / complete / DELETE)
const mateoId = empLogin.user?.employee_id;
const { json: allAppts } = await req("GET", "/appointments?limit=80", null, t);
const foreign = (allAppts.appointments || []).find((a) => a.employee_id !== mateoId);
check("F2: found a colleague's appointment", !!foreign, "no foreign appt in seed");
if (foreign) {
const { status: sPatch } = await req("PATCH", `/appointments/${foreign.id}`, { notes: "x" }, empLogin.token);
check("F2: employee PATCH foreign appt -> 403", sPatch === 403, `status=${sPatch}`);
const { status: sComplete } = await req("POST", `/appointments/${foreign.id}/complete`, { tip: 0 }, empLogin.token);
check("F2: employee complete foreign appt -> 403", sComplete === 403, `status=${sComplete}`);
const { status: sDel } = await req("DELETE", `/appointments/${foreign.id}`, null, empLogin.token);
check("F2: employee DELETE foreign appt -> 403", sDel === 403, `status=${sDel}`);
}
// positive control: employee can still mutate their OWN appointment
if (empAppt.appointment?.id) {
const { status: sOwn } = await req("PATCH", `/appointments/${empAppt.appointment.id}`, { notes: "nota propia" }, empLogin.token);
check("F2: employee PATCH own appt -> 200", sOwn === 200, `status=${sOwn}`);
}
// F3: employees cannot edit/delete clients (ownerOnly)
const { status: sClientPatch } = await req("PATCH", "/clients/1", { notes: "x" }, empLogin.token);
check("F3: employee PATCH client -> 403", sClientPatch === 403, `status=${sClientPatch}`);
const { status: sClientDel } = await req("DELETE", "/clients/1", null, empLogin.token);
check("F3: employee DELETE client -> 403", sClientDel === 403, `status=${sClientDel}`);
console.log(`\n${pass}/${pass + fail} passed${fail ? `, ${fail} FAILED` : ""}`);
process.exit(fail ? 1 : 0);