feat(client-groups): CM-1 data layer — groups entity, membership, service, Mailchimp scaffold

- client_groups + client_group_members tables (migration 0094, port_id cascade)
- client_groups permission resource (view/manage) in catalog + role backfill
- service: CRUD + wipe-and-rewrite membership + member email resolution
- mailchimp.service scaffold: config reader + inert one-way sync (mapping
  deferred until the client's MC account is wired, per CM-1 decision)
- 4 integration tests (CRUD, membership, email resolution, port-scope guard)

Backend only — API routes + UI to follow. tsc clean, 1635 vitest pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-18 22:28:20 +02:00
parent 4dc0bdd8c4
commit 661187cc79
11 changed files with 539 additions and 0 deletions

View File

@@ -92,6 +92,10 @@ export const ALL_PERMISSIONS: RolePermissions = {
view: true,
manage: true,
},
client_groups: {
view: true,
manage: true,
},
};
export const DIRECTOR_PERMISSIONS: RolePermissions = {
@@ -175,6 +179,10 @@ export const DIRECTOR_PERMISSIONS: RolePermissions = {
view: true,
manage: true,
},
client_groups: {
view: true,
manage: true,
},
};
export const SALES_MANAGER_PERMISSIONS: RolePermissions = {
@@ -258,6 +266,10 @@ export const SALES_MANAGER_PERMISSIONS: RolePermissions = {
view: true,
manage: true,
},
client_groups: {
view: true,
manage: true,
},
};
export const SALES_AGENT_PERMISSIONS: RolePermissions = {
@@ -341,6 +353,10 @@ export const SALES_AGENT_PERMISSIONS: RolePermissions = {
view: true,
manage: true,
},
client_groups: {
view: true,
manage: true,
},
};
export const VIEWER_PERMISSIONS: RolePermissions = {
@@ -430,6 +446,10 @@ export const VIEWER_PERMISSIONS: RolePermissions = {
view: true,
manage: false,
},
client_groups: {
view: true,
manage: false,
},
};
// Residential Partner - for an outside party who handles residential
@@ -522,4 +542,8 @@ export const RESIDENTIAL_PARTNER_PERMISSIONS: RolePermissions = {
view: false,
manage: false,
},
client_groups: {
view: false,
manage: false,
},
};