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:
+32
-1
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user