feat(admin): single Sales role, welcome-email password setup, Director=sales
- Collapse the two sales roles in the create-user dropdown to one "Sales" (sales_manager relabelled). Hide super_admin + sales_agent from selection via NON_ASSIGNABLE_ROLE_NAMES; the form keeps a user's *current* role even if hidden so existing assignments stay editable. - Director becomes a senior-title twin of Sales: DIRECTOR_PERMISSIONS now equals SALES_MANAGER_PERMISSIONS (no admin/settings — Super-Admin only). Migration 0097 updates the existing global director row (idempotent, data-only; 0 users assigned on prod, so no blast radius). - Admin create-user defaults to emailing a set-password link instead of an inline password (manual entry still available via a toggle). createUserSchema: password optional + sendSetupEmail; createUser provisions with a throwaway password then triggers the set-password email. - New users get a dedicated, unique WELCOME email (crmWelcomeEmail), not the self-service "reset your password" email. A pending-welcome flag routes the shared better-auth sendResetPassword callback via account-setup-email.ts. - Phone confirmed already optional for staff accounts (no change needed). Tests: +welcome-routing, +create-user-setup; permission-matrix director block realigned to no-admin. 1662 vitest pass; tsc + eslint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
* - viewer can read but not write
|
||||
* - sales_agent can manage own clients/interests but not admin features
|
||||
* - sales_manager has elevated but non-admin access
|
||||
* - director has near-full access
|
||||
* - director mirrors sales (full sales access, no admin)
|
||||
* - deepMerge correctly applies port-level overrides
|
||||
*/
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
@@ -190,17 +190,25 @@ describe('Permission Matrix - sales_manager', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ─── director ─────────────────────────────────────────────────────────────────
|
||||
// ─── director (senior-title twin of Sales: full sales, no admin) ──────────────
|
||||
|
||||
describe('Permission Matrix - director', () => {
|
||||
const ctx = makeCtx({ permissions: makeDirectorPermissions() });
|
||||
|
||||
it('can manage webhooks', async () => {
|
||||
expect(await checkPermission(ctx, 'admin', 'manage_webhooks')).toBe(200);
|
||||
it('has full sales access (create clients)', async () => {
|
||||
expect(await checkPermission(ctx, 'clients', 'create')).toBe(200);
|
||||
});
|
||||
|
||||
it('can manage users', async () => {
|
||||
expect(await checkPermission(ctx, 'admin', 'manage_users')).toBe(200);
|
||||
it('can manage tags', async () => {
|
||||
expect(await checkPermission(ctx, 'admin', 'manage_tags')).toBe(200);
|
||||
});
|
||||
|
||||
it('cannot manage users', async () => {
|
||||
expect(await checkPermission(ctx, 'admin', 'manage_users')).toBe(403);
|
||||
});
|
||||
|
||||
it('cannot manage settings', async () => {
|
||||
expect(await checkPermission(ctx, 'admin', 'manage_settings')).toBe(403);
|
||||
});
|
||||
|
||||
it('cannot perform system_backup', async () => {
|
||||
|
||||
Reference in New Issue
Block a user