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

@@ -3,8 +3,6 @@
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import {
Anchor,
BarChart3,
Bookmark,
Building2,
FileSignature,
@@ -18,8 +16,6 @@ import {
Ship,
} from 'lucide-react';
import { useQuery } from '@tanstack/react-query';
import {
Drawer,
DrawerContent,
@@ -28,7 +24,6 @@ import {
DrawerClose,
} from '@/components/shared/drawer';
import { useUmamiActive } from '@/components/website-analytics/use-website-analytics';
import { apiFetch } from '@/lib/api/client';
type MoreItem = {
label: string;
@@ -42,7 +37,7 @@ type MoreGroup = {
};
// Logical grouping (vs alphabetical or frequency-ranked): keeps a stable
// spatial layout reps' muscle memory survives while making the
// spatial layout - reps' muscle memory survives - while making the
// "kind of thing" each tile is explicit. Three sections:
// - Records: entity lists (people, vessels, properties)
// - Operations: daily-use action surfaces
@@ -51,7 +46,7 @@ type MoreGroup = {
// Interests stays here (not bottom nav) to dodge the Clients-vs-
// Interests UX confusion. Inbox replaces the previously-separate
// Alerts + Reminders entries (merged 2026-05-11). Website analytics
// and Reservations are filtered out below when not applicable.
// is filtered out below when Umami isn't wired up for this port.
const MORE_GROUPS: MoreGroup[] = [
{
label: 'Records',
@@ -67,12 +62,10 @@ const MORE_GROUPS: MoreGroup[] = [
label: 'Operations',
items: [
{ label: 'Alerts & Reminders', icon: Inbox, segment: 'inbox' },
// M-U15: invoices was missing from the mobile nav reps doing
// M-U15: invoices was missing from the mobile nav - reps doing
// mobile follow-ups had to type the URL by hand.
{ label: 'Invoices', icon: FileText, segment: 'invoices' },
{ label: 'Expenses', icon: Receipt, segment: 'expenses' },
{ label: 'Reservations', icon: Anchor, segment: 'berth-reservations' },
{ label: 'Reports', icon: BarChart3, segment: 'reports' },
],
},
{
@@ -95,30 +88,16 @@ export function MoreSheet({
const pathname = usePathname();
const portSlug = pathname.split('/').filter(Boolean)[0] ?? 'port-nimara';
// Hide "Website analytics" if Umami isn't wired up for this port the
// Hide "Website analytics" if Umami isn't wired up for this port - the
// dedicated tile on the dashboard already does the same.
const umami = useUmamiActive('today');
const umamiConfigured = !umami.isLoading && umami.data?.notConfigured !== true;
// Hide "Reservations" until at least one exists for this port — until the
// marina has confirmed bookings, the page is empty and surfaces nothing
// useful. Cheap count via pageSize=1; cached 5 min so opening the sheet
// repeatedly doesn't refetch.
const reservations = useQuery<{ pagination?: { total: number } }>({
queryKey: ['berth-reservations', 'sheet-count'],
queryFn: () => apiFetch('/api/v1/berth-reservations?pageSize=1'),
staleTime: 5 * 60_000,
enabled: open,
});
const hasReservations =
!reservations.isLoading && (reservations.data?.pagination?.total ?? 0) > 0;
// Per-group filter: keep only the items relevant to this port's state.
const groups = MORE_GROUPS.map((g) => ({
...g,
items: g.items.filter((item) => {
if (item.segment === 'website-analytics') return umamiConfigured;
if (item.segment === 'berth-reservations') return hasReservations;
return true;
}),
})).filter((g) => g.items.length > 0);