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:
@@ -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 you’ve configured and lets you mark manual steps complete.',
|
||||
'Step-by-step setup checklist for fresh ports - auto-detects what you’ve 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 {
|
||||
|
||||
Reference in New Issue
Block a user