#262: remove Requests / Approvals / Tasks tabs from Executive Meetings
Full-stack removal of the three retired sections, including the
canApprove capability flag.
Backend
- routes/executive-meetings.ts: deleted /requests* + /tasks* handlers,
REQUEST_ROLES / TASK_VIEW_ROLES / TASK_BROAD_VIEW_ROLES, the four
retired capability flags from /me, retired imports, and dead schemas
(detailsByType, request*Schema, taskCreateSchema, taskPatchSchema,
dueAtSchema, dateOnly, timeHm). Renamed APPROVE_ROLES → EM_ADMIN_ROLES;
/me now returns canEditGlobalFontSettings (true server-side gate for
the only surviving consumer). Dropped now-unused requireApprove export.
- lib/executive-meeting-notify.ts: types collapsed to ['meeting_created'].
Frontend
- pages/executive-meetings.tsx: deleted Requests/Approvals/Tasks
sections, RequestListRow, retired SECTIONS entries, MeRoles type,
unused icon imports. MeCapabilities.canApprove → canEditGlobalFontSettings.
- hooks/use-notifications-socket.ts: dropped two retired invalidations.
- locales/{ar,en}.json: removed nav + section + 6 retired type keys.
Schema + DB
- lib/db/src/schema/executive-meetings.ts: tables/relations removed.
- scripts/cleanup-em-requests-tasks.sql: idempotent cleanup — orphan
prefs / notifications / audit rows then DROP TABLE … CASCADE.
Applied to dev DB; `db push` re-synced.
Tests
- Sequential `node --test --test-concurrency=1` → 226/226 pass.
- /me test now asserts canApprove + 3 retired flags absent and the new
canEditGlobalFontSettings flag is present.
This commit is contained in:
@@ -102,7 +102,7 @@ const MUTATE_ROLES = [
|
||||
"executive_office_manager",
|
||||
"executive_coord_lead",
|
||||
] as const;
|
||||
const APPROVE_ROLES = ["admin", "executive_office_manager"] as const;
|
||||
const EM_ADMIN_ROLES = ["admin", "executive_office_manager"] as const;
|
||||
const ADMIN_AUDIT_ROLES = [
|
||||
"admin",
|
||||
"executive_office_manager",
|
||||
@@ -150,7 +150,6 @@ function makeRequireRoles(allowed: ReadonlyArray<string>) {
|
||||
}
|
||||
|
||||
const requireMutate = makeRequireRoles(MUTATE_ROLES);
|
||||
const requireApprove = makeRequireRoles(APPROVE_ROLES);
|
||||
const requireAdminAudit = makeRequireRoles(ADMIN_AUDIT_ROLES);
|
||||
|
||||
// ---------- Zod schemas ----------
|
||||
@@ -435,7 +434,7 @@ router.get(
|
||||
roles: Array.from(names),
|
||||
canRead: READ_ROLES.some((r) => names.has(r)),
|
||||
canMutate: MUTATE_ROLES.some((r) => names.has(r)),
|
||||
canApprove: APPROVE_ROLES.some((r) => names.has(r)),
|
||||
canEditGlobalFontSettings: EM_ADMIN_ROLES.some((r) => names.has(r)),
|
||||
canViewAudit: ADMIN_AUDIT_ROLES.some((r) => names.has(r)),
|
||||
});
|
||||
},
|
||||
@@ -519,7 +518,7 @@ router.post(
|
||||
newValue: { ...meeting, attendees },
|
||||
performedBy: userId,
|
||||
});
|
||||
const approverIds = await getUserIdsForRoleNames(APPROVE_ROLES);
|
||||
const approverIds = await getUserIdsForRoleNames(EM_ADMIN_ROLES);
|
||||
const recipients = await recordExecutiveMeetingNotifications(tx, {
|
||||
recipientUserIds: approverIds,
|
||||
meetingId: meeting.id,
|
||||
@@ -1558,7 +1557,7 @@ const upsertFontSettingsHandler = async (
|
||||
if (!data) return;
|
||||
if (data.scope === "global") {
|
||||
const names = await getRoleNamesForUser(userId);
|
||||
const ok = APPROVE_ROLES.some((r) => names.has(r));
|
||||
const ok = EM_ADMIN_ROLES.some((r) => names.has(r));
|
||||
if (!ok) {
|
||||
res.status(403).json({ error: "Forbidden", code: "forbidden" });
|
||||
return;
|
||||
@@ -1632,5 +1631,4 @@ router.patch(
|
||||
upsertFontSettingsHandler,
|
||||
);
|
||||
|
||||
export { requireApprove };
|
||||
export default router;
|
||||
|
||||
@@ -144,21 +144,25 @@ test("GET /me exposes the surviving capability flags", async () => {
|
||||
"roles",
|
||||
"canRead",
|
||||
"canMutate",
|
||||
"canApprove",
|
||||
"canEditGlobalFontSettings",
|
||||
"canViewAudit",
|
||||
]) {
|
||||
assert.ok(key in body, `missing ${key} in /me response`);
|
||||
}
|
||||
// The retired flags must not leak back in.
|
||||
for (const removed of ["canSubmitRequest", "canViewTasks", "canViewAllTasks"]) {
|
||||
for (const removed of [
|
||||
"canApprove",
|
||||
"canSubmitRequest",
|
||||
"canViewTasks",
|
||||
"canViewAllTasks",
|
||||
]) {
|
||||
assert.ok(!(removed in body), `unexpected ${removed} in /me response`);
|
||||
}
|
||||
assert.equal(body.canApprove, true);
|
||||
assert.equal(body.canEditGlobalFontSettings, true);
|
||||
|
||||
const coordRes = await api(coordCookie, "GET", "/api/executive-meetings/me");
|
||||
const coordMe = await coordRes.json();
|
||||
assert.equal(coordMe.canRead, true);
|
||||
assert.equal(coordMe.canApprove, false);
|
||||
assert.equal(coordMe.canEditGlobalFontSettings, false);
|
||||
});
|
||||
|
||||
test("Meetings: bilingual title required (titleEn cannot be empty)", async () => {
|
||||
|
||||
@@ -233,7 +233,7 @@ type MeCapabilities = {
|
||||
roles: string[];
|
||||
canRead: boolean;
|
||||
canMutate: boolean;
|
||||
canApprove: boolean;
|
||||
canEditGlobalFontSettings: boolean;
|
||||
canViewAudit: boolean;
|
||||
};
|
||||
|
||||
@@ -661,7 +661,7 @@ export default function ExecutiveMeetingsPage() {
|
||||
{section === "fontSettings" && me && (
|
||||
<FontSettingsSection
|
||||
font={effectiveFont}
|
||||
canEditGlobal={me.canApprove}
|
||||
canEditGlobal={me.canEditGlobalFontSettings}
|
||||
t={t}
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user