feat(admin+search): user-mgmt polish, role labels, search keyword index
Admin search now matches against per-card keyword lists so typing "client portal", "smtp", "tier ladder" lands on the System Settings card (which hosts those flags). The same keyword list extends the topbar global search (NAV_CATALOG) so any setting key resolves from the cmd-K input — settings results sort to the bottom of the dropdown beneath entity hits. User management: - Third action button (Power/PowerOff) enables/disables sign-in from the desktop list; mobile card dropdown gains the same item. Backed by the existing userProfiles.isActive flag — withAuth already refuses disabled sessions with 403. - UserForm collects first + last name (canonical) alongside displayName, with admin email-change behind a confirmation modal. On confirm we send the OLD address an automated "your admin changed your sign-in email" notice (new template at admin-email-change.ts) and rewrite the Better Auth user row. - Phone field swaps the bare tel input for the shared PhoneInput (country combobox + AsYouType formatting + E.164 storage). - "Manage permissions" link points to /admin/roles?focusUser=… as a stepping stone for the future fine-tuned-permissions UI. Role names normalize through a new ROLE_LABELS + formatRole() helper in constants.ts. Replaces the ad-hoc humanizeRole in sidebar and the prettifyRoleName in role-list; user-list and user-card now render "Sales Agent" instead of "sales_agent". Custom roles pass through unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ import { documents, documentSigners } from '@/lib/db/schema/documents';
|
||||
import { expenses } from '@/lib/db/schema/financial';
|
||||
import { alerts as alertsTable } from '@/lib/db/schema/insights';
|
||||
import { ALERT_RULES, type AlertRuleId } from '@/lib/db/schema/insights';
|
||||
import { STAGE_LABELS, type PipelineStage } from '@/lib/constants';
|
||||
|
||||
import type { AlertCandidate } from './alerts.service';
|
||||
|
||||
@@ -101,7 +102,7 @@ async function interestStale(portId: string): Promise<AlertCandidate[]> {
|
||||
ruleId: 'interest.stale',
|
||||
severity: 'info',
|
||||
title: `Stale interest: ${r.clientName}`,
|
||||
body: `In '${r.stage}' with no contact for 14+ days.`,
|
||||
body: `In '${STAGE_LABELS[r.stage as PipelineStage] ?? r.stage.replace(/_/g, ' ')}' with no contact for 14+ days.`,
|
||||
link: `/[port]/interests/${r.id}`,
|
||||
entityType: 'interest',
|
||||
entityId: r.id,
|
||||
|
||||
Reference in New Issue
Block a user