chore(autonomous-session): consolidate uncommitted work from prior session

Bundles the prior autonomous-session output that was sitting unstaged:

- Em-dash sweep across src/ + tests/ (en-dash/em-dash to hyphen, ~2280 instances)
- country-flag-icons rollout (CountryFlag component, replaces emoji glyphs that
  never rendered on Windows; lazy-loads the 3x2 SVG index as a single chunk
  after the per-subpath dynamic-import approach silently failed in webpack)
- Admin IA Phase 1+2: 7-domain regroup, 41 to 38 pages, /admin/berths index,
  redirects (ocr to ai, reports to dashboard, invitations to users),
  docs/admin-ia-proposal.md
- Per-template email tester (registry + endpoint + UI on Email admin page)
- Cancel-document mode picker (delete-from-Documenso vs keep-for-audit)
- Dashboard PDF report: 25 widgets, SVG charts, date-range picker, 11 resolvers
- Customize-widgets per-region sortables at xl+ (charts/rails/feed); single
  flat sortable below xl when the layout stacks; per-viewport saved orders
- Audit doc updates capturing each shipped item
- Lint fixes: react-compiler immutability in DonutChart (reduce instead of
  let-reassign), set-state-in-effect disables in CountryFlag and
  UploadForSigning preview-bytes effect, unused 'confirm' destructures in
  interest contract + reservation tabs, unescaped apostrophe in test-template
  card copy
This commit is contained in:
2026-05-23 00:52:59 +02:00
parent 43719b49e9
commit 221ae5784e
749 changed files with 7440 additions and 3118 deletions

View File

@@ -4,8 +4,10 @@ import { useMemo, useState } from 'react';
import Link from 'next/link';
import {
Activity,
BarChart3,
AlertCircle,
Anchor,
BellRing,
BookMarked,
BookOpen,
ClipboardList,
CopyCheck,
@@ -15,6 +17,7 @@ import {
FileText,
FileUp,
GitBranch,
Home,
Inbox,
ListChecks,
Mail,
@@ -64,62 +67,60 @@ interface AdminGroup {
// Catalog lives inside the client component so React Server Components don't
// need to serialize the lucide icon factories (functions / $$typeof refs)
// across the RSC boundary that crash was the error the rep hit when the
// across the RSC boundary - that crash was the error the rep hit when the
// catalog lived in the server-component `page.tsx`. Adding a new admin
// surface? Append it to the matching group below.
// surface? Append it to the matching domain below.
//
// 7-domain IA regrouped 2026-05-22 per docs/admin-ia-proposal.md:
// 1. Brand & Communication - how outbound looks
// 2. Sales workflow - pipeline behaviour + templates
// 3. Catalog - tenant-defined data shapes
// 4. Identity & access - who can use the system
// 5. Inbox & data quality - admin queues + cleanup
// 6. Integrations - external systems + providers
// 7. System & observability - infra + diagnostics + escape hatches
//
// Pages deleted in the regroup (redirect-shimmed): /admin/ocr (dup of /ai),
// /admin/invitations (merged into /users 2026-05-21), /admin/reports
// (duplicated dashboard widgets). See docs/admin-ia-proposal.md §8.
const GROUPS: AdminGroup[] = [
{
title: 'Access',
description: 'Who can sign in and what they can do once they do.',
title: 'Brand & Communication',
description: 'How outbound looks and which channels it ships on.',
sections: [
{
href: 'users',
label: 'Users',
description: 'CRM accounts, role assignments, and per-user residential access toggles.',
icon: Users,
keywords: [
'accounts',
'staff',
'team',
'disable user',
'reset password',
'residential access',
],
href: 'branding',
label: 'Branding',
description: 'App name, logo, primary color, and email header/footer HTML.',
icon: Paintbrush,
keywords: ['logo', 'colors', 'email shell', 'preview', 'test email'],
},
// /admin/invitations merged into /admin/users (Active users +
// Invitations tabs) on 2026-05-21. The standalone tile was
// removed; reps still find the invitation flow via the Users
// tile's "Invitations" tab.
{
href: 'roles',
label: 'Roles & Permissions',
description: 'Default permission sets and per-port role overrides.',
icon: Shield,
href: 'email',
label: 'Email Settings',
description:
'SMTP credentials (noreply + sales), per-template tester, routing rules, and the connectivity probe.',
icon: Mail,
keywords: ['smtp', 'noreply', 'sales mailbox', 'test send', 'routing'],
},
{
href: 'email-templates',
label: 'Email Templates',
description:
'Subject-line + body overrides for transactional emails (portal, inquiry, invite).',
icon: FilePen,
},
],
},
{
title: 'Configuration',
description: 'Branding, integrations, and per-port settings.',
title: 'Sales workflow',
description: 'How the pipeline behaves - triggers, scoring, document + form templates.',
sections: [
{
href: 'email',
label: 'Email Settings',
description: 'From address, signatures, and per-port SMTP overrides.',
icon: Mail,
},
{
href: 'documenso',
label: 'EOI signing service',
description:
'API credentials, EOI template, and default in-app vs external signing pathway.',
icon: FileSignature,
},
{
href: 'pipeline-rules',
label: 'Pipeline auto-advance',
label: 'Pipeline rules',
description:
'Per-trigger control: which lifecycle events (EOI signed, deposit received, contract signed) auto-advance the deal stage.',
'Berth-rules engine triggers + per-event auto-advance (EOI signed, deposit received, contract signed).',
icon: GitBranch,
keywords: ['pipeline', 'auto-advance', 'stage rules', 'aggressive', 'conservative'],
},
@@ -127,7 +128,7 @@ const GROUPS: AdminGroup[] = [
href: 'pulse',
label: 'Deal Pulse',
description:
'Configure the chip on every interest header master toggle, per-signal toggles, and tier-label overrides.',
'Configure the chip on every interest header - master toggle, per-signal toggles, and tier-label overrides.',
icon: Activity,
keywords: ['pulse', 'deal-health', 'health chip', 'hot warm cold'],
},
@@ -138,15 +139,232 @@ const GROUPS: AdminGroup[] = [
icon: BellRing,
},
{
href: 'branding',
label: 'Branding',
description: 'App name, logo, primary color, and email header/footer HTML.',
icon: Paintbrush,
href: 'qualification-criteria',
label: 'Qualification criteria',
description:
'Checklist reps complete to qualify an enquiry. Enable/disable/reorder per port; affects the "ready to qualify" hint on the interest detail.',
icon: ListChecks,
keywords: ['qualification', 'criteria', 'checklist', 'qualify', 'enquiry', 'sales gate'],
},
{
href: 'residential-stages',
label: 'Residential pipeline stages',
description:
'Configure stages residential interests flow through. Removing a stage with active interests prompts for reassignment.',
icon: Home,
keywords: ['stages', 'pipeline', 'residential funnel', 'reassign'],
},
{
href: 'forms',
label: 'Forms',
description: 'Form templates used by client-facing inquiry and intake flows.',
icon: ClipboardList,
},
{
href: 'templates',
label: 'Document Templates',
description:
'PDF + email templates with merge-field placeholders - EOI, reservation, contract.',
icon: FileText,
},
],
},
{
title: 'Catalog',
description: 'Tenant-defined data shapes that attach to records.',
sections: [
{
href: 'vocabularies',
label: 'Vocabularies',
description:
'Per-port pick lists used across the CRM: interest temperatures, status reasons, tenure types, expense categories, document types.',
icon: BookOpen,
},
{
href: 'tags',
label: 'Tags',
description: 'Color-coded tags applied to clients, yachts, companies, and interests.',
icon: Tag,
},
{
href: 'custom-fields',
label: 'Custom Fields',
description: 'Tenant-defined fields for clients, yachts, and reservations.',
icon: SlidersHorizontal,
},
{
href: 'brochures',
label: 'Brochures',
description: 'Per-port versioned brochure PDFs distributed through the public site + reps.',
icon: BookMarked,
},
],
},
{
title: 'Identity & access',
description: 'Who can sign in and what they can do once they do.',
sections: [
{
href: 'users',
label: 'Users',
description:
'Active CRM accounts + pending invitations (merged tabs), role assignments, residential access toggles.',
icon: Users,
keywords: [
'accounts',
'staff',
'team',
'invitations',
'invite',
'disable user',
'reset password',
'residential access',
],
},
{
href: 'roles',
label: 'Roles & Permissions',
description: 'Default permission sets and per-port role overrides.',
icon: Shield,
},
{
href: 'ports',
label: 'Ports',
description: 'Manage the marinas/ports this installation serves (super-admin only).',
icon: Ship,
},
],
},
{
title: 'Inbox & data quality',
description: 'Admin queues for inbound submissions + cleanup tooling.',
sections: [
{
href: 'inquiries',
label: 'Inquiry Inbox',
description:
'Submissions captured from the public marketing site (berth, residence, contact).',
icon: Inbox,
},
{
href: 'sends',
label: 'Send Log',
description: 'Brochure and per-berth PDF sends, with delivery failures surfaced for retry.',
icon: Send,
},
{
href: 'duplicates',
label: 'Duplicates',
description: 'Review queue of suspected duplicate clients flagged by the dedup engine.',
icon: CopyCheck,
},
{
href: 'import',
label: 'Bulk Import',
description: 'CSV-driven imports for clients, yachts, and reservations.',
icon: FileUp,
},
{
href: 'berths',
label: 'Berths admin',
description:
'Bulk-add new berths in one wizard + reconcile berths missing required fields.',
icon: Anchor,
keywords: ['bulk add', 'reconcile', 'berth pdf', 'mooring', 'bulk berths'],
},
],
},
{
title: 'Integrations',
description: 'External systems + provider configuration.',
sections: [
{
href: 'documenso',
label: 'Signing service (Documenso)',
description:
'API credentials, signer identities, templates, and signing-behaviour for every document the CRM puts out for signature.',
icon: FileSignature,
keywords: ['documenso', 'eoi', 'signing', 'envelope', 'signer', 'embedded'],
},
{
href: 'webhooks',
label: 'Webhooks',
description: 'Outgoing webhook subscriptions, secrets, and delivery log.',
icon: Webhook,
},
{
href: 'website-analytics',
label: 'Website analytics (Umami)',
description: 'Per-port Umami URL, API token, and Website ID.',
icon: TrendingUp,
keywords: ['umami', 'analytics', 'traffic', 'visitors', 'marketing', 'pageviews'],
},
{
href: 'ai',
label: 'AI configuration',
description:
'One panel for every AI feature: master switch, provider credentials, OCR settings, interest scoring, email-drafts, recommender controls.',
icon: Sparkles,
keywords: [
'openai',
'anthropic',
'gpt',
'claude',
'llm',
'api key',
'embeddings',
'receipt',
'scan',
'tesseract',
'ocr',
'expense scanner',
'interest scoring',
'email drafts',
],
},
],
},
{
title: 'System & observability',
description: 'Infra, observability, escape hatches.',
sections: [
{
href: 'audit',
label: 'Audit Log',
description: 'Searchable log of every authenticated mutation in the system.',
icon: ScrollText,
},
{
href: 'monitoring',
label: 'Queue Monitoring',
description: 'BullMQ queue health, throughput, and retry diagnostics.',
icon: Activity,
},
{
href: 'errors',
label: 'Errors',
description:
'Recent server-side error events with request-id drill-down, plus the error-code catalog.',
icon: AlertCircle,
keywords: ['exceptions', 'request id', 'stack trace', 'error code'],
},
{
href: 'backup',
label: 'Backup & Restore',
description: 'Backup posture + retention policy (read-only).',
icon: DatabaseBackup,
},
{
href: 'storage',
label: 'Storage Backend',
description:
'Choose between S3-compatible object store or local filesystem; migrate between them.',
icon: Server,
},
{
href: 'settings',
label: 'System Settings',
description: 'Generic key/value configuration store for advanced flags.',
description: 'Generic key/value configuration store for advanced flags (escape hatch).',
icon: Settings,
// Mirrors KNOWN_SETTINGS in settings-manager.tsx so a search for any
// individual flag jumps straight to this card. Keep in sync when
@@ -188,194 +406,15 @@ const GROUPS: AdminGroup[] = [
'default currency',
],
},
{
href: 'webhooks',
label: 'Webhooks',
description: 'Outgoing webhook subscriptions, secrets, and delivery log.',
icon: Webhook,
},
],
},
{
title: 'Content',
description: 'Forms, templates, and labels that users see.',
sections: [
{
href: 'forms',
label: 'Forms',
description: 'Form templates used by client-facing inquiry and intake flows.',
icon: ClipboardList,
},
{
href: 'templates',
label: 'Document Templates',
description: 'PDF + email templates with merge-field placeholders.',
icon: FileText,
},
{
href: 'email-templates',
label: 'Email Templates',
description: 'Customize subject lines for transactional emails (portal, inquiry, invite).',
icon: FilePen,
},
{
href: 'tags',
label: 'Tags',
description: 'Color-coded tags applied to clients, yachts, companies, and interests.',
icon: Tag,
},
{
href: 'vocabularies',
label: 'Vocabularies',
description:
'Per-port pick lists used across the CRM: interest temperatures, status reasons, tenure types, expense categories, document types.',
icon: BookOpen,
},
{
href: 'custom-fields',
label: 'Custom Fields',
description: 'Tenant-defined fields for clients, yachts, and reservations.',
icon: SlidersHorizontal,
},
],
},
{
title: 'Data Quality',
description: 'Cleanup, imports, and the audit trail.',
sections: [
{
href: 'inquiries',
label: 'Inquiry Inbox',
description:
'Submissions captured from the public marketing site (berth, residence, contact).',
icon: Inbox,
},
{
href: 'sends',
label: 'Send Log',
description: 'Brochure and per-berth PDF sends, with delivery failures surfaced for retry.',
icon: Send,
},
{
href: 'duplicates',
label: 'Duplicates',
description: 'Review queue of suspected duplicate clients flagged by the dedup engine.',
icon: CopyCheck,
},
{
href: 'import',
label: 'Bulk Import',
description: 'CSV-driven imports for clients, yachts, and reservations.',
icon: FileUp,
},
{
href: 'audit',
label: 'Audit Log',
description: 'Searchable log of every authenticated mutation in the system.',
icon: ScrollText,
},
],
},
{
title: 'Operations',
description: 'Health checks and disaster recovery.',
sections: [
{
href: 'reports',
label: 'Reports',
description: 'Saved analytics views and ad-hoc query results.',
icon: BarChart3,
},
{
href: 'monitoring',
label: 'Queue Monitoring',
description: 'BullMQ queue health, throughput, and retry diagnostics.',
icon: Activity,
},
{
href: 'backup',
label: 'Backup & Restore',
description: 'Backup posture + retention policy (read-only).',
icon: DatabaseBackup,
},
{
href: 'storage',
label: 'Storage Backend',
description:
'Choose between S3-compatible object store or local filesystem; migrate between them.',
icon: Server,
},
],
},
{
title: 'Tenancy',
description: 'Multi-port and multi-install scaffolding.',
sections: [
{
href: 'ports',
label: 'Ports',
description: 'Manage the marinas/ports this installation serves.',
icon: Ship,
},
{
href: 'onboarding',
label: 'Onboarding checklist',
description:
'Step-by-step setup checklist for fresh ports auto-detects what youve configured and lets you mark manual steps complete.',
'Step-by-step setup checklist for fresh ports - auto-detects what youve configured and lets you mark manual steps complete.',
icon: ListChecks,
},
],
},
{
title: 'Integrations',
description: 'Third-party providers wired into the app.',
sections: [
{
href: 'ai',
label: 'AI configuration',
description:
'Master switch, provider credentials, and the Receipt OCR settings in one place. Per-feature pages (berth-PDF parser, recommender) link out from here.',
icon: Sparkles,
keywords: [
'openai',
'anthropic',
'gpt',
'claude',
'llm',
'api key',
'embeddings',
'receipt',
'scan',
'tesseract',
'ocr',
'expense scanner',
],
},
{
href: 'website-analytics',
label: 'Website analytics (Umami)',
description: 'Per-port Umami URL, API token, and Website ID.',
icon: TrendingUp,
keywords: ['umami', 'analytics', 'traffic', 'visitors', 'marketing', 'pageviews'],
},
{
href: 'residential-stages',
label: 'Residential pipeline stages',
description:
'Configure stages residential interests flow through. Removing a stage with active interests prompts for reassignment.',
icon: ListChecks,
keywords: ['stages', 'pipeline', 'residential funnel', 'reassign'],
},
{
href: 'qualification-criteria',
label: 'Qualification criteria',
description:
'Checklist reps complete to qualify an enquiry. Enable/disable/reorder per port; affects the soft "ready to qualify" hint on the interest detail.',
icon: ListChecks,
keywords: ['qualification', 'criteria', 'checklist', 'qualify', 'enquiry', 'sales gate'],
},
],
},
];
interface AdminSectionsBrowserProps {