2026-04-29 14:15:25 +02:00
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import Link from 'next/link';
|
|
|
|
|
import { usePathname } from 'next/navigation';
|
|
|
|
|
import {
|
|
|
|
|
Bookmark,
|
2026-05-03 16:15:37 +02:00
|
|
|
Building2,
|
2026-05-12 14:50:58 +02:00
|
|
|
FileSignature,
|
fix(audit): comprehensive 2026-05-15 audit fix wave + Documenso v2 polish
Bundles the prior session's 50-task fix sweep (Documenso v2 + EOI/signing-
progress redesign + env-to-admin migration + dev-mode banner) with the
2026-05-18 audit fix wave (3 CRITICAL, 14 HIGH, 28 MEDIUM, 6 LOW).
CRITICAL (3):
- C-01 interest-berths INNER JOIN -> LEFT JOIN so hard-deleted berths
no longer silently drop interest links
- C-02 /setup added to PUBLIC_PATHS; fresh-deploy bootstrap loop fixed
- C-03 generic PATCH /interests/[id] no longer accepts pipelineStage —
callers must go through /stage with the override-guard chain
HIGH (14/15):
- H-01 explicit ON DELETE on previously-implicit NO ACTION FKs across
interests/documents/reservations/reminders/invoices (migration 0070)
- H-02 login page reads ?redirect= param with same-origin guard
- H-03 CRM invite token moves to URL fragment so it never lands in
nginx access logs / Referer headers
- H-04 Retry-After header on sign-in-by-identifier 429 (RFC 6585 §4)
- H-05 toggleAccount writes an audit row
- H-06 upsertSetting masks any value whose key ends with _encrypted
- H-07 archiveClient cascade fires per-interest audit rows
- H-08 createSalesTransporter applies SMTP_TIMEOUTS
- H-09 AppShell stable children — viewport flip across breakpoint no
longer destroys in-progress form drafts
- H-10 portal documents page swaps Unicode glyph status icons for
Lucide CheckCircle2/XCircle/Circle + aria-labels
- H-12 list components swap alert(...) for toast.warning(...)
- H-13 5 icon-only buttons gain aria-label
- H-14 parseBody treats empty bodies as {}
- H-15 admin layout renders a 403 panel instead of silent bounce
- H-11 not applicable — mobile-search-overlay IS a mobile bottom-sheet
MEDIUM (28+):
- M-MT01-05 defense-in-depth port_id/parent-id filters on UPDATE/DELETE
WHEREs across custom-fields, notes (all 6 entity types x update +
delete), client-contacts, yacht ownerClient lookup, webhook reads
- M-D01 documents-hub realtime event-name typo (file:created -> uploaded)
- M-EM01 portal-auth emails thread through portId
- M-EM02 sendEmail accepts cc/bcc params
- M-EM04 notification_digest catalog key
- M-IN01 portal presigned download URLs use 4h TTL
- M-IN02 OpenAI client lazy-instantiated
- M-IN04 stale pdfme refs updated to pdf-lib AcroForm
- M-IN05 umami.testConnection returns tagged union
- M-L01 reservations tenure_type unified with berths
- M-L02 report-generators canonicalize stage values
- M-AU01 audit log placeholder copy fixed
- M-AU04 outcome_set / outcome_cleared distinct audit verbs
- M-NEW-2 activity feed entity name+type separator
- M-R01 portal allowlist narrowed + portal_session backstop in proxy
- M-SC02 companies archived partial index
- M-SC04 audit_logs.searchText documented as DB-managed
- M-S01 storage_s3_access_key_encrypted admin field
- M-U01 audit log empty state uses <EmptyState>
- M-U09 invoice delete dialog -> <AlertDialog>
- M-U10 toast.success on ClientForm + InterestForm create/edit
- M-U11 settings-form-card logo preview alt text
- M-U14 mobile topbar title on clients/yachts/interests/berths
- M-U15 Invoices in mobile More-sheet
LOW (6/8):
- L-AU01 severity defaults for security-relevant verbs
- L-AU02 +13 missing actions in admin audit filter
- L-AU03 +7 missing entity types in admin audit filter
- L-AU04 dead listAuditLogs stubbed
- L-D02 CLAUDE.md Owner-wins chain tightened
Bonus — Document detail polish (#67 partial, 3/6 deliverables):
- state-aware action button per signer
- watcher Add UI with display-name resolution
- cleanSignerName cleanup
Prior session work bundled in:
- Documenso v2 webhook + envelope-ID normalization + sequential signing
- SigningProgress UI redesign (avatars, per-signer state, timestamps)
- env->admin settings registry + RegistryDrivenForm + encrypted creds
- Embedded-signing card + Test connection + setup help
- Dev-mode EMAIL_REDIRECT_TO banner
- Pipeline rules admin page
- Sales email config card
- Audit log details Sheet
- EOI tab: Finalising badge, absolute timestamps, sequential indicator
- Notes pipeline_stage_at_creation (migration 0069)
- Documenso numeric ID dual-key webhook (migration 0068)
- Dimensions criterion copy (migration 0067)
Tests: 1374/1374 vitest pass. tsc clean. lint clean.
See docs/AUDIT-FIX-WAVE-2026-05-18.md for the full progress report and
the user-input items still pending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 13:28:50 +02:00
|
|
|
FileText,
|
fix(audit): MEDIUMs sweep — mobile More-sheet, portal profile, inline override, dialog UX, ext-EOI gate
R2-M11: mobile More-sheet missing 4 destinations. Added Reservations,
Notifications, Residential, Website analytics — anyone using mobile
chrome to triage on the go can now reach those domains.
R2-M12: portal had no profile / change-password surface. New
/portal/profile page with read-only contact details + a
ChangePasswordForm component, backed by a new POST
/api/portal/auth/change-password endpoint and
changePortalPassword() service function. Audits both ok and failure
cases at warning severity. Added Profile to PortalNav.
R2-M1: portal dashboard "My Memberships" tile had no href and no
/portal/memberships route — dead-end on tap. Hidden until a
memberships page ships; the count remains in the underlying data.
R2-M7: InlineStagePicker never sent override:true so users with
interests.override_stage couldn't actually use the perm from the
inline chip — they had to fall back to the modal picker. Now the
picker auto-detects when a transition isn't legal AND the user has
override_stage, sets override:true, and supplies a default reason.
Frontend M2: hard-delete-dialog confirm stage now has a "Send a new
code" link in case the original expired before the user could enter
it. Avoids forcing a full Cancel + reopen.
Frontend M4: audit-log-list date-range validation. From > To now
shows an inline error and skips the request rather than firing an
empty-range query that surfaces "no entries found".
R2-M6: external-EOI route now requires interests.edit AND
documents.upload_signed (defense-in-depth) — uploading a signed EOI
mutates interest state, so the upload-signed perm alone shouldn't
let a custom role flip an interest.
1175/1175 vitest passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 22:38:59 +02:00
|
|
|
Globe,
|
|
|
|
|
Home,
|
2026-05-12 14:50:58 +02:00
|
|
|
Inbox,
|
2026-05-03 16:15:37 +02:00
|
|
|
Receipt,
|
2026-04-29 14:15:25 +02:00
|
|
|
Settings,
|
|
|
|
|
Shield,
|
2026-05-03 16:15:37 +02:00
|
|
|
Ship,
|
2026-04-29 14:15:25 +02:00
|
|
|
} from 'lucide-react';
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
Drawer,
|
|
|
|
|
DrawerContent,
|
|
|
|
|
DrawerHeader,
|
|
|
|
|
DrawerTitle,
|
|
|
|
|
DrawerClose,
|
|
|
|
|
} from '@/components/shared/drawer';
|
2026-05-09 04:11:01 +02:00
|
|
|
import { useUmamiActive } from '@/components/website-analytics/use-website-analytics';
|
2026-04-29 14:15:25 +02:00
|
|
|
|
|
|
|
|
type MoreItem = {
|
|
|
|
|
label: string;
|
|
|
|
|
icon: typeof Building2;
|
|
|
|
|
segment: string;
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-12 14:50:58 +02:00
|
|
|
type MoreGroup = {
|
|
|
|
|
label: string;
|
|
|
|
|
items: MoreItem[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Logical grouping (vs alphabetical or frequency-ranked): keeps a stable
|
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
2026-05-23 00:52:59 +02:00
|
|
|
// spatial layout - reps' muscle memory survives - while making the
|
2026-05-12 14:50:58 +02:00
|
|
|
// "kind of thing" each tile is explicit. Three sections:
|
|
|
|
|
// - Records: entity lists (people, vessels, properties)
|
|
|
|
|
// - Operations: daily-use action surfaces
|
|
|
|
|
// - Configuration: port-level setup, hidden from most reps
|
2026-05-09 04:11:01 +02:00
|
|
|
//
|
2026-05-12 14:50:58 +02:00
|
|
|
// 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
|
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
2026-05-23 00:52:59 +02:00
|
|
|
// is filtered out below when Umami isn't wired up for this port.
|
2026-05-12 14:50:58 +02:00
|
|
|
const MORE_GROUPS: MoreGroup[] = [
|
|
|
|
|
{
|
|
|
|
|
label: 'Records',
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'Documents', icon: FileSignature, segment: 'documents' },
|
|
|
|
|
{ label: 'Interests', icon: Bookmark, segment: 'interests' },
|
|
|
|
|
{ label: 'Yachts', icon: Ship, segment: 'yachts' },
|
|
|
|
|
{ label: 'Companies', icon: Building2, segment: 'companies' },
|
|
|
|
|
{ label: 'Residential', icon: Home, segment: 'residential/clients' },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'Operations',
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'Alerts & Reminders', icon: Inbox, segment: 'inbox' },
|
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
2026-05-23 00:52:59 +02:00
|
|
|
// M-U15: invoices was missing from the mobile nav - reps doing
|
fix(audit): comprehensive 2026-05-15 audit fix wave + Documenso v2 polish
Bundles the prior session's 50-task fix sweep (Documenso v2 + EOI/signing-
progress redesign + env-to-admin migration + dev-mode banner) with the
2026-05-18 audit fix wave (3 CRITICAL, 14 HIGH, 28 MEDIUM, 6 LOW).
CRITICAL (3):
- C-01 interest-berths INNER JOIN -> LEFT JOIN so hard-deleted berths
no longer silently drop interest links
- C-02 /setup added to PUBLIC_PATHS; fresh-deploy bootstrap loop fixed
- C-03 generic PATCH /interests/[id] no longer accepts pipelineStage —
callers must go through /stage with the override-guard chain
HIGH (14/15):
- H-01 explicit ON DELETE on previously-implicit NO ACTION FKs across
interests/documents/reservations/reminders/invoices (migration 0070)
- H-02 login page reads ?redirect= param with same-origin guard
- H-03 CRM invite token moves to URL fragment so it never lands in
nginx access logs / Referer headers
- H-04 Retry-After header on sign-in-by-identifier 429 (RFC 6585 §4)
- H-05 toggleAccount writes an audit row
- H-06 upsertSetting masks any value whose key ends with _encrypted
- H-07 archiveClient cascade fires per-interest audit rows
- H-08 createSalesTransporter applies SMTP_TIMEOUTS
- H-09 AppShell stable children — viewport flip across breakpoint no
longer destroys in-progress form drafts
- H-10 portal documents page swaps Unicode glyph status icons for
Lucide CheckCircle2/XCircle/Circle + aria-labels
- H-12 list components swap alert(...) for toast.warning(...)
- H-13 5 icon-only buttons gain aria-label
- H-14 parseBody treats empty bodies as {}
- H-15 admin layout renders a 403 panel instead of silent bounce
- H-11 not applicable — mobile-search-overlay IS a mobile bottom-sheet
MEDIUM (28+):
- M-MT01-05 defense-in-depth port_id/parent-id filters on UPDATE/DELETE
WHEREs across custom-fields, notes (all 6 entity types x update +
delete), client-contacts, yacht ownerClient lookup, webhook reads
- M-D01 documents-hub realtime event-name typo (file:created -> uploaded)
- M-EM01 portal-auth emails thread through portId
- M-EM02 sendEmail accepts cc/bcc params
- M-EM04 notification_digest catalog key
- M-IN01 portal presigned download URLs use 4h TTL
- M-IN02 OpenAI client lazy-instantiated
- M-IN04 stale pdfme refs updated to pdf-lib AcroForm
- M-IN05 umami.testConnection returns tagged union
- M-L01 reservations tenure_type unified with berths
- M-L02 report-generators canonicalize stage values
- M-AU01 audit log placeholder copy fixed
- M-AU04 outcome_set / outcome_cleared distinct audit verbs
- M-NEW-2 activity feed entity name+type separator
- M-R01 portal allowlist narrowed + portal_session backstop in proxy
- M-SC02 companies archived partial index
- M-SC04 audit_logs.searchText documented as DB-managed
- M-S01 storage_s3_access_key_encrypted admin field
- M-U01 audit log empty state uses <EmptyState>
- M-U09 invoice delete dialog -> <AlertDialog>
- M-U10 toast.success on ClientForm + InterestForm create/edit
- M-U11 settings-form-card logo preview alt text
- M-U14 mobile topbar title on clients/yachts/interests/berths
- M-U15 Invoices in mobile More-sheet
LOW (6/8):
- L-AU01 severity defaults for security-relevant verbs
- L-AU02 +13 missing actions in admin audit filter
- L-AU03 +7 missing entity types in admin audit filter
- L-AU04 dead listAuditLogs stubbed
- L-D02 CLAUDE.md Owner-wins chain tightened
Bonus — Document detail polish (#67 partial, 3/6 deliverables):
- state-aware action button per signer
- watcher Add UI with display-name resolution
- cleanSignerName cleanup
Prior session work bundled in:
- Documenso v2 webhook + envelope-ID normalization + sequential signing
- SigningProgress UI redesign (avatars, per-signer state, timestamps)
- env->admin settings registry + RegistryDrivenForm + encrypted creds
- Embedded-signing card + Test connection + setup help
- Dev-mode EMAIL_REDIRECT_TO banner
- Pipeline rules admin page
- Sales email config card
- Audit log details Sheet
- EOI tab: Finalising badge, absolute timestamps, sequential indicator
- Notes pipeline_stage_at_creation (migration 0069)
- Documenso numeric ID dual-key webhook (migration 0068)
- Dimensions criterion copy (migration 0067)
Tests: 1374/1374 vitest pass. tsc clean. lint clean.
See docs/AUDIT-FIX-WAVE-2026-05-18.md for the full progress report and
the user-input items still pending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 13:28:50 +02:00
|
|
|
// mobile follow-ups had to type the URL by hand.
|
|
|
|
|
{ label: 'Invoices', icon: FileText, segment: 'invoices' },
|
2026-05-12 14:50:58 +02:00
|
|
|
{ label: 'Expenses', icon: Receipt, segment: 'expenses' },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'Configuration',
|
|
|
|
|
items: [
|
|
|
|
|
{ label: 'Website analytics', icon: Globe, segment: 'website-analytics' },
|
|
|
|
|
{ label: 'Settings', icon: Settings, segment: 'settings' },
|
|
|
|
|
{ label: 'Admin', icon: Shield, segment: 'admin' },
|
|
|
|
|
],
|
|
|
|
|
},
|
2026-04-29 14:15:25 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export function MoreSheet({
|
|
|
|
|
open,
|
|
|
|
|
onOpenChange,
|
2026-05-31 18:49:16 +02:00
|
|
|
residentialModuleEnabled = true,
|
2026-04-29 14:15:25 +02:00
|
|
|
}: {
|
|
|
|
|
open: boolean;
|
|
|
|
|
onOpenChange: (next: boolean) => void;
|
2026-05-31 18:49:16 +02:00
|
|
|
/** Per-port residential-module gate, resolved SSR-side in the shell.
|
|
|
|
|
* Hides the Residential tile when the module is off. Defaults to true
|
|
|
|
|
* so a missing value keeps the feature visible (registry default). */
|
|
|
|
|
residentialModuleEnabled?: boolean;
|
2026-04-29 14:15:25 +02:00
|
|
|
}) {
|
|
|
|
|
const pathname = usePathname();
|
|
|
|
|
const portSlug = pathname.split('/').filter(Boolean)[0] ?? 'port-nimara';
|
|
|
|
|
|
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
2026-05-23 00:52:59 +02:00
|
|
|
// Hide "Website analytics" if Umami isn't wired up for this port - the
|
2026-05-09 04:11:01 +02:00
|
|
|
// dedicated tile on the dashboard already does the same.
|
|
|
|
|
const umami = useUmamiActive('today');
|
2026-05-12 14:50:58 +02:00
|
|
|
const umamiConfigured = !umami.isLoading && umami.data?.notConfigured !== true;
|
|
|
|
|
|
|
|
|
|
// 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;
|
2026-05-31 18:49:16 +02:00
|
|
|
if (item.segment === 'residential/clients') return residentialModuleEnabled;
|
2026-05-12 14:50:58 +02:00
|
|
|
return true;
|
|
|
|
|
}),
|
|
|
|
|
})).filter((g) => g.items.length > 0);
|
2026-05-09 04:11:01 +02:00
|
|
|
|
2026-04-29 14:15:25 +02:00
|
|
|
return (
|
|
|
|
|
<Drawer open={open} onOpenChange={onOpenChange}>
|
|
|
|
|
<DrawerContent>
|
|
|
|
|
<DrawerHeader>
|
|
|
|
|
<DrawerTitle>More</DrawerTitle>
|
|
|
|
|
</DrawerHeader>
|
2026-05-12 14:50:58 +02:00
|
|
|
<div className="space-y-4 px-3 pb-4">
|
|
|
|
|
{groups.map((group) => (
|
|
|
|
|
<section key={group.label}>
|
chore(audit-drain): rip out next-intl, RTL lint, sweeps, polish
Drain the long-tail audit queue captured in alpha-uat-master.md.
- next-intl ripped out (zero useTranslations callers ever existed):
package.json, next.config.ts plugin wrap, src/i18n/, messages/, and
the layout NextIntlClientProvider all gone; <html lang="en"> hardcoded.
- RTL lint nudge added: warn-only no-restricted-syntax on physical
Tailwind utilities (ml-/mr-/pl-/pr-/text-left/text-right/border-l/
border-r/rounded-l-/rounded-r-) inside JSX className literals.
Existing ~1,000 sites grandfathered; new code trends toward logical.
- Icon-only button accessibility lint: jsx-a11y/control-has-associated-
label enabled at warn; 4 empty <th>/<td> action placeholders gain
sr-only labels.
- Currency: SUPPORTED_CURRENCIES drops the hardcoded English labels;
new currencyLabel(code, locale?) helper resolves via Intl.DisplayNames.
CurrencySelect + settings-manager migrated.
- Date locale sweep: 7 surfaces flip from toLocaleString('en-GB'|'en-US')
to toLocaleString(undefined, ...) so dates honour runtime locale.
- Dialog/Sheet width: 10 document/EOI/entity-form dialogs gain a
lg:max-w-4xl or lg:max-w-5xl step so wide desktops get breathing room.
- PaymentsSection collapsed-bar: slim one-line bar showing
"Payments - Not received yet" or "Payments - \$X received - N payments
- Expand"; per-interest collapse state persists in localStorage; the
RecordPayment flow auto-expands.
- muted-foreground opacity sweep: 10 text-bearing
text-muted-foreground/{60,70,80} hits dropped to plain
text-muted-foreground for AA contrast on muted bg. Icon-only
(aria-hidden) opacity hits left as-is.
- Micro-type bump: text-[10px] and text-[11px] -> text-xs (12px)
across 87 files in src/components + src/app. Pure mechanical sweep.
- Audit-doc cleanup: alpha-uat-master.md stale 2026-05-25 summary
rewritten with cumulative state through today. Items genuinely still
open are now a short long-tail list.
- New docs/marketing-site-followups.md: Umami Phase 4a/3/5, email
pixel E2E verification, and website-cutover work parked here so
they don't get lost in the CRM audit doc.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 18:48:46 +02:00
|
|
|
<h3 className="mb-1.5 px-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
2026-05-12 14:50:58 +02:00
|
|
|
{group.label}
|
|
|
|
|
</h3>
|
|
|
|
|
<ul className="grid grid-cols-3 gap-2">
|
|
|
|
|
{group.items.map((item) => {
|
|
|
|
|
const Icon = item.icon;
|
|
|
|
|
return (
|
|
|
|
|
<li key={item.segment}>
|
|
|
|
|
<DrawerClose asChild>
|
|
|
|
|
<Link
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
|
href={`/${portSlug}/${item.segment}` as any}
|
|
|
|
|
// min-h-[88px] guarantees a 44pt vertical touch
|
|
|
|
|
// target (Apple HIG); icon + label centered.
|
|
|
|
|
className="flex min-h-[88px] flex-col items-center justify-center gap-1.5 rounded-md py-3 px-2 text-center text-xs text-foreground hover:bg-accent active:bg-accent/80"
|
|
|
|
|
>
|
|
|
|
|
<Icon className="size-7 text-muted-foreground" aria-hidden />
|
|
|
|
|
<span className="font-medium leading-tight">{item.label}</span>
|
|
|
|
|
</Link>
|
|
|
|
|
</DrawerClose>
|
|
|
|
|
</li>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</ul>
|
|
|
|
|
</section>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
2026-04-29 14:15:25 +02:00
|
|
|
</DrawerContent>
|
|
|
|
|
</Drawer>
|
|
|
|
|
);
|
|
|
|
|
}
|