feat(permissions): add inquiries resource (view/manage) + idempotent role backfill

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 17:59:32 +02:00
parent 6c4490f653
commit 08adb4aeea
6 changed files with 63 additions and 0 deletions

View File

@@ -103,6 +103,10 @@ const DEFAULT_PERMISSIONS: Record<string, Record<string, boolean>> = {
delete: false,
change_stage: false,
},
inquiries: {
view: false,
manage: false,
},
};
const GROUP_LABELS: Record<string, string> = {
@@ -126,6 +130,7 @@ const GROUP_LABELS: Record<string, string> = {
admin: 'Administration',
residential_clients: 'Residential Clients',
residential_interests: 'Residential Interests',
inquiries: 'Inquiries',
};
function formatAction(action: string): string {

View File

@@ -69,6 +69,7 @@ export const PERMISSION_CATALOG = {
],
residential_clients: ['view', 'create', 'edit', 'delete'],
residential_interests: ['view', 'create', 'edit', 'delete', 'change_stage'],
inquiries: ['view', 'manage'],
} as const satisfies {
[R in PermissionResource]: ReadonlyArray<PermissionAction<R> & string>;
};

View File

@@ -0,0 +1,25 @@
-- 0092_inquiries_permission.sql
-- ----------------------------------------------------------------------------
-- New `inquiries` permission resource (view/manage) backing the top-level
-- Inquiries workbench (previously the inbox lived under /admin and was gated on
-- admin.view_audit_log, which sales roles don't have).
--
-- Existing role rows are backfilled so the resource defaults to whatever the
-- role's `clients` access is: view ⟵ clients.view, manage ⟵ clients.create.
-- This lights up the right roles (anyone who can see/create clients) without a
-- manual per-role edit, and defaults to deny for read-only roles.
--
-- New-key only and idempotent via the `? 'inquiries'` guard, so re-running is a
-- no-op. Per-user / port-role override tables are intentionally left untouched:
-- the deep-merge resolver fills missing leaves from the base role (same
-- reasoning as 0041).
UPDATE roles
SET permissions = permissions || jsonb_build_object(
'inquiries', jsonb_build_object(
'view', COALESCE((permissions->'clients'->>'view')::boolean, false),
'manage', COALESCE((permissions->'clients'->>'create')::boolean, false)
)
)
WHERE permissions IS NOT NULL
AND NOT (permissions ? 'inquiries');

View File

@@ -162,6 +162,10 @@ export type RolePermissions = {
delete: boolean;
change_stage: boolean;
};
inquiries: {
view: boolean;
manage: boolean;
};
};
/**

View File

@@ -88,6 +88,10 @@ export const ALL_PERMISSIONS: RolePermissions = {
delete: true,
change_stage: true,
},
inquiries: {
view: true,
manage: true,
},
};
export const DIRECTOR_PERMISSIONS: RolePermissions = {
@@ -167,6 +171,10 @@ export const DIRECTOR_PERMISSIONS: RolePermissions = {
delete: true,
change_stage: true,
},
inquiries: {
view: true,
manage: true,
},
};
export const SALES_MANAGER_PERMISSIONS: RolePermissions = {
@@ -246,6 +254,10 @@ export const SALES_MANAGER_PERMISSIONS: RolePermissions = {
delete: false,
change_stage: false,
},
inquiries: {
view: true,
manage: true,
},
};
export const SALES_AGENT_PERMISSIONS: RolePermissions = {
@@ -325,6 +337,10 @@ export const SALES_AGENT_PERMISSIONS: RolePermissions = {
delete: false,
change_stage: false,
},
inquiries: {
view: true,
manage: true,
},
};
export const VIEWER_PERMISSIONS: RolePermissions = {
@@ -410,6 +426,10 @@ export const VIEWER_PERMISSIONS: RolePermissions = {
delete: false,
change_stage: false,
},
inquiries: {
view: true,
manage: false,
},
};
// Residential Partner - for an outside party who handles residential
@@ -498,4 +518,8 @@ export const RESIDENTIAL_PARTNER_PERMISSIONS: RolePermissions = {
delete: false,
change_stage: true,
},
inquiries: {
view: false,
manage: false,
},
};

View File

@@ -384,6 +384,7 @@ export function makeFullPermissions(): RolePermissions {
delete: true,
change_stage: true,
},
inquiries: { view: true, manage: true },
};
}
@@ -472,6 +473,7 @@ export function makeViewerPermissions(): RolePermissions {
delete: false,
change_stage: false,
},
inquiries: { view: true, manage: false },
};
}
@@ -560,6 +562,7 @@ export function makeSalesAgentPermissions(): RolePermissions {
delete: false,
change_stage: false,
},
inquiries: { view: true, manage: true },
};
}
@@ -648,6 +651,7 @@ export function makeSalesManagerPermissions(): RolePermissions {
delete: true,
change_stage: true,
},
inquiries: { view: true, manage: true },
};
}