Files
pn-new-crm/src/components/layout/topbar.tsx

182 lines
8.6 KiB
TypeScript
Raw Normal View History

'use client';
feat(launch-readiness-batch): UAT drains, navigation refactor, launch infra, trackers Bundles the rest of the in-flight work from this UAT round into one checkpoint. Each sub-area is independent; see the headings below. UAT polish (drained 11 findings from active-uat.md): - Dialog primitive default bumped sm:max-w-xl/lg:max-w-3xl → sm:max-w-2xl/lg:max-w-4xl so multi-field forms + PDF previews aren't cramped at 1440-1920px. - Notes tab badge aggregation: new countFor{Client,Yacht,Company} Aggregated helpers in notes.service mirror the listFor*Aggregated symmetric-reach joins. yacht-tabs + company-tabs render the badge; client-tabs already had badge support. - Supplemental-info form polish bundle: BrandedAuthShell gains a `width: 'sm' | 'md'` prop (md uses min-h-dvh scroll instead of fixed inset-0 pin so long forms scroll naturally). Form picks up port branding (logoUrl + backgroundUrl + appName) via loadByToken. Address fields completed (street + city + region + postal + country). Port name eyebrow + success-state copy added. - new-document-menu Upload-file landing toast: per-file completion emits toast.success with action link to the destination entity or folder. - interest-tabs OverviewTab "from client" pill on Email + Phone rows via new EditableRow `inheritedFrom` prop. - create-document-wizard subject picker → segmented button strip (5 types visible at once). Launch infra: - UTM column wiring (Init 1b step 4): migration 0089_website_submissions_utm.sql adds utm_source/medium/campaign/ term/content + composite index (port_id, utm_source, received_at) for per-campaign rollups. website-inquiries intake accepts the five fields. Residential intake intentionally untouched per audit scope. - Invoicing module gate (Init 1c spike): new invoices-module.service + invoices layout guard + registry entry invoices_module_enabled (default false). Audit conclusion in launch-readiness.md: payments table is canonical money path; /invoices flow is parallel infrastructure now hidden by default. Smart-back navigation refactor: - Replaced breadcrumb component with history-aware Back button. New route-labels.ts + use-smart-back hook + navigation-history-tracker so back falls through to the parent route when there's no prior page in history. - Sidebar / topbar / mobile-topbar adopt the new pattern; old breadcrumb-store kept for back-compat consumers but the breadcrumbs component is gone. - 6 detail pages (admin/errors per-id + codes, invoices/ upload-receipts, reports kind, tenancies detail, analytics metric, client detail) migrated. Trackers + docs: - docs/launch-readiness.md — master pre-launch tracker. Includes the reports gap audit (cross-cutting filter set, Marketing + Financial blockers, custom builder remaining entities, scheduled CSV/XLSX, template scope picker). - docs/superpowers/audits/active-uat.md — 15 findings flipped OPEN → SHIPPED locally with fix-applied notes; 4 OPEN remaining (each blocked on user input or cross-repo). - CLAUDE.md — minor session notes carried forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 22:42:37 +02:00
import { Plus } from 'lucide-react';
import { useRouter } from 'next/navigation';
feat: round 2 — stage prompts, berth header, EOI inline edit, measurement units Berth surfaces - New compact mooring-chip header (colored plate + status pill, dock-label in tooltip) replaces the redundant "Berth B1 / Sold / B DOCK" stack - Berth list gains a "Latest deal stage" column showing the most-advanced pipeline stage of any active linked interest (server-aggregated, ranks by PIPELINE_STAGES index) - "Linked prospect" Select on the status-change dialog rebuilt as a Command combobox: search, recent-first sort, stage-coloured pills Pipeline UX - Reverting an interest to Open with linked berths now prompts: keep the links, unlink and reset, or cancel. Silent when no berths are linked - Activity feed + entity-activity feed normalise enum field values via STAGE_LABELS / formatSource: "deposit_10pct → contract_sent" reads as "10% Deposit → Contract Sent" EOI generate dialog - Inline-editable rows for client name, nationality (country combobox), and yacht name — pencil affordance saves directly via clients/yachts PATCH - Replaces the single "Edit on client's page" link with two contextual links framed by short copy explaining what's inline vs what needs the canonical page - Backend EoiContext now includes client.id + yacht.id so the dialog can PATCH without an extra round-trip Company form - New "Connections" section lets the rep attach members (clients) and yachts during create. Yacht attach uses the existing transfer endpoint so audit log + ownership history capture the change - Inline "+ New client" / "+ New yacht" buttons open the canonical forms stacked over the company sheet - After save, the form chains to a yacht pull-in prompt (if any attached client owns yachts not yet linked) and an optional "Create interest" step pre-filled with the first attached client Admin - /admin landing gains a searchable index — typed query flattens groups into a result list matching label + description + group title - "Documenso & EOI" card relabelled to "EOI signing service" (consistent with the user-facing language rename from round 1) Measurement units (migration 0053) - interests gains desired_*_m columns + desired_*_unit discriminators so the rep's literal entry (ft OR m) is preserved verbatim instead of being reconstructed from a single canonical column on every render - yachts + berths gain matching *_unit columns alongside their existing ft + m pairs; defaults to 'ft' so legacy rows still render normally - Interest form POST/PATCH now sends both ft + m + unit; computed m is derived from the ft canonical to keep the recommender SQL unchanged Misc - Active-deals tile + topbar type their Link href as `Route` instead of `any` - Unused REPORT_TYPE_LABELS const dropped from generate-report-form - Test fixtures (fill-eoi-form, documenso-payload, public-berths) updated to include the new id + unit fields on the EoiContext / Berth shapes Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 15:28:22 +02:00
import type { Route } from 'next';
feat(layout): add tablet viewport tier (mobile/tablet/desktop) Previously the app used a binary matchMedia split at 1023.98px, so iPad portrait + half-screen-on-13"-Mac both fell into the mobile shell — neither is really mobile. The tablet tier fills that gap. - `use-is-mobile.ts` gains `useViewportTier()` returning 'mobile' | 'tablet' | 'desktop' (mobile < 768, tablet 768-1023, desktop ≥ 1024). Backed by useSyncExternalStore so render reads stay pure. `useIsMobile()` retained as a back-compat alias = `tier !== 'desktop'` so existing call sites don't have to change in lockstep. - `app-shell.tsx` now renders three branches. Mobile + desktop unchanged. Tablet renders the desktop shell, but the Sidebar lives inside a left-side `<Sheet>` opened by a new leading logo button in the Topbar. SheetContent width matches `--width-sidebar` so the open state reads consistent. Children subtree position stays invariant across tier flips so inline-edit drafts survive a resize. - `topbar.tsx` accepts an optional `leadingSlot` rendered before the back button + breadcrumbs in the LEFT column. AppShell mounts a port-logo button in that slot on tablet (or a three-bar menu icon when the port has no logo yet) that triggers the sheet. - `page-header.tsx` was the dashboard "title card looks bad on tablet" surface — the actions row was forced no-wrap at sm (640px) which crushed the title on iPad-portrait. Stack point moved from sm to lg, so tablet stacks vertically (title above, actions below); desktop returns to side-by-side. tsc clean, 1454/1454 vitest pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:37:23 +02:00
import type { ReactNode } from 'react';
import { useUIStore } from '@/stores/ui-store';
import { Button } from '@/components/ui/button';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Separator } from '@/components/ui/separator';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
feat(launch-readiness-batch): UAT drains, navigation refactor, launch infra, trackers Bundles the rest of the in-flight work from this UAT round into one checkpoint. Each sub-area is independent; see the headings below. UAT polish (drained 11 findings from active-uat.md): - Dialog primitive default bumped sm:max-w-xl/lg:max-w-3xl → sm:max-w-2xl/lg:max-w-4xl so multi-field forms + PDF previews aren't cramped at 1440-1920px. - Notes tab badge aggregation: new countFor{Client,Yacht,Company} Aggregated helpers in notes.service mirror the listFor*Aggregated symmetric-reach joins. yacht-tabs + company-tabs render the badge; client-tabs already had badge support. - Supplemental-info form polish bundle: BrandedAuthShell gains a `width: 'sm' | 'md'` prop (md uses min-h-dvh scroll instead of fixed inset-0 pin so long forms scroll naturally). Form picks up port branding (logoUrl + backgroundUrl + appName) via loadByToken. Address fields completed (street + city + region + postal + country). Port name eyebrow + success-state copy added. - new-document-menu Upload-file landing toast: per-file completion emits toast.success with action link to the destination entity or folder. - interest-tabs OverviewTab "from client" pill on Email + Phone rows via new EditableRow `inheritedFrom` prop. - create-document-wizard subject picker → segmented button strip (5 types visible at once). Launch infra: - UTM column wiring (Init 1b step 4): migration 0089_website_submissions_utm.sql adds utm_source/medium/campaign/ term/content + composite index (port_id, utm_source, received_at) for per-campaign rollups. website-inquiries intake accepts the five fields. Residential intake intentionally untouched per audit scope. - Invoicing module gate (Init 1c spike): new invoices-module.service + invoices layout guard + registry entry invoices_module_enabled (default false). Audit conclusion in launch-readiness.md: payments table is canonical money path; /invoices flow is parallel infrastructure now hidden by default. Smart-back navigation refactor: - Replaced breadcrumb component with history-aware Back button. New route-labels.ts + use-smart-back hook + navigation-history-tracker so back falls through to the parent route when there's no prior page in history. - Sidebar / topbar / mobile-topbar adopt the new pattern; old breadcrumb-store kept for back-compat consumers but the breadcrumbs component is gone. - 6 detail pages (admin/errors per-id + codes, invoices/ upload-receipts, reports kind, tenancies detail, analytics metric, client detail) migrated. Trackers + docs: - docs/launch-readiness.md — master pre-launch tracker. Includes the reports gap audit (cross-cutting filter set, Marketing + Financial blockers, custom builder remaining entities, scheduled CSV/XLSX, template scope picker). - docs/superpowers/audits/active-uat.md — 15 findings flipped OPEN → SHIPPED locally with fix-applied notes; 4 OPEN remaining (each blocked on user input or cross-repo). - CLAUDE.md — minor session notes carried forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 22:42:37 +02:00
import { BackButton } from '@/components/layout/back-button';
import { CommandSearch } from '@/components/search/command-search';
import { Inbox } from '@/components/layout/inbox';
import { UserMenu } from '@/components/layout/user-menu';
import type { Port } from '@/lib/db/schema/ports';
interface TopbarProps {
ports: Port[];
user?: { name: string; email: string };
feat(layout): add tablet viewport tier (mobile/tablet/desktop) Previously the app used a binary matchMedia split at 1023.98px, so iPad portrait + half-screen-on-13"-Mac both fell into the mobile shell — neither is really mobile. The tablet tier fills that gap. - `use-is-mobile.ts` gains `useViewportTier()` returning 'mobile' | 'tablet' | 'desktop' (mobile < 768, tablet 768-1023, desktop ≥ 1024). Backed by useSyncExternalStore so render reads stay pure. `useIsMobile()` retained as a back-compat alias = `tier !== 'desktop'` so existing call sites don't have to change in lockstep. - `app-shell.tsx` now renders three branches. Mobile + desktop unchanged. Tablet renders the desktop shell, but the Sidebar lives inside a left-side `<Sheet>` opened by a new leading logo button in the Topbar. SheetContent width matches `--width-sidebar` so the open state reads consistent. Children subtree position stays invariant across tier flips so inline-edit drafts survive a resize. - `topbar.tsx` accepts an optional `leadingSlot` rendered before the back button + breadcrumbs in the LEFT column. AppShell mounts a port-logo button in that slot on tablet (or a three-bar menu icon when the port has no logo yet) that triggers the sheet. - `page-header.tsx` was the dashboard "title card looks bad on tablet" surface — the actions row was forced no-wrap at sm (640px) which crushed the title on iPad-portrait. Stack point moved from sm to lg, so tablet stacks vertically (title above, actions below); desktop returns to side-by-side. tsc clean, 1454/1454 vitest pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:37:23 +02:00
/** Optional leading slot rendered before the breadcrumbs on tablet
* viewports - used by AppShell to mount a sidebar trigger button
feat(layout): add tablet viewport tier (mobile/tablet/desktop) Previously the app used a binary matchMedia split at 1023.98px, so iPad portrait + half-screen-on-13"-Mac both fell into the mobile shell — neither is really mobile. The tablet tier fills that gap. - `use-is-mobile.ts` gains `useViewportTier()` returning 'mobile' | 'tablet' | 'desktop' (mobile < 768, tablet 768-1023, desktop ≥ 1024). Backed by useSyncExternalStore so render reads stay pure. `useIsMobile()` retained as a back-compat alias = `tier !== 'desktop'` so existing call sites don't have to change in lockstep. - `app-shell.tsx` now renders three branches. Mobile + desktop unchanged. Tablet renders the desktop shell, but the Sidebar lives inside a left-side `<Sheet>` opened by a new leading logo button in the Topbar. SheetContent width matches `--width-sidebar` so the open state reads consistent. Children subtree position stays invariant across tier flips so inline-edit drafts survive a resize. - `topbar.tsx` accepts an optional `leadingSlot` rendered before the back button + breadcrumbs in the LEFT column. AppShell mounts a port-logo button in that slot on tablet (or a three-bar menu icon when the port has no logo yet) that triggers the sheet. - `page-header.tsx` was the dashboard "title card looks bad on tablet" surface — the actions row was forced no-wrap at sm (640px) which crushed the title on iPad-portrait. Stack point moved from sm to lg, so tablet stacks vertically (title above, actions below); desktop returns to side-by-side. tsc clean, 1454/1454 vitest pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:37:23 +02:00
* (logo) when the sidebar is hidden behind a slide-over Sheet. */
leadingSlot?: ReactNode;
}
feat(layout): add tablet viewport tier (mobile/tablet/desktop) Previously the app used a binary matchMedia split at 1023.98px, so iPad portrait + half-screen-on-13"-Mac both fell into the mobile shell — neither is really mobile. The tablet tier fills that gap. - `use-is-mobile.ts` gains `useViewportTier()` returning 'mobile' | 'tablet' | 'desktop' (mobile < 768, tablet 768-1023, desktop ≥ 1024). Backed by useSyncExternalStore so render reads stay pure. `useIsMobile()` retained as a back-compat alias = `tier !== 'desktop'` so existing call sites don't have to change in lockstep. - `app-shell.tsx` now renders three branches. Mobile + desktop unchanged. Tablet renders the desktop shell, but the Sidebar lives inside a left-side `<Sheet>` opened by a new leading logo button in the Topbar. SheetContent width matches `--width-sidebar` so the open state reads consistent. Children subtree position stays invariant across tier flips so inline-edit drafts survive a resize. - `topbar.tsx` accepts an optional `leadingSlot` rendered before the back button + breadcrumbs in the LEFT column. AppShell mounts a port-logo button in that slot on tablet (or a three-bar menu icon when the port has no logo yet) that triggers the sheet. - `page-header.tsx` was the dashboard "title card looks bad on tablet" surface — the actions row was forced no-wrap at sm (640px) which crushed the title on iPad-portrait. Stack point moved from sm to lg, so tablet stacks vertically (title above, actions below); desktop returns to side-by-side. tsc clean, 1454/1454 vitest pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:37:23 +02:00
export function Topbar({ ports, user, leadingSlot }: TopbarProps) {
const router = useRouter();
const currentPortSlug = useUIStore((s) => s.currentPortSlug);
const base = currentPortSlug ? `/${currentPortSlug}` : '';
return (
feat(launch-readiness-batch): UAT drains, navigation refactor, launch infra, trackers Bundles the rest of the in-flight work from this UAT round into one checkpoint. Each sub-area is independent; see the headings below. UAT polish (drained 11 findings from active-uat.md): - Dialog primitive default bumped sm:max-w-xl/lg:max-w-3xl → sm:max-w-2xl/lg:max-w-4xl so multi-field forms + PDF previews aren't cramped at 1440-1920px. - Notes tab badge aggregation: new countFor{Client,Yacht,Company} Aggregated helpers in notes.service mirror the listFor*Aggregated symmetric-reach joins. yacht-tabs + company-tabs render the badge; client-tabs already had badge support. - Supplemental-info form polish bundle: BrandedAuthShell gains a `width: 'sm' | 'md'` prop (md uses min-h-dvh scroll instead of fixed inset-0 pin so long forms scroll naturally). Form picks up port branding (logoUrl + backgroundUrl + appName) via loadByToken. Address fields completed (street + city + region + postal + country). Port name eyebrow + success-state copy added. - new-document-menu Upload-file landing toast: per-file completion emits toast.success with action link to the destination entity or folder. - interest-tabs OverviewTab "from client" pill on Email + Phone rows via new EditableRow `inheritedFrom` prop. - create-document-wizard subject picker → segmented button strip (5 types visible at once). Launch infra: - UTM column wiring (Init 1b step 4): migration 0089_website_submissions_utm.sql adds utm_source/medium/campaign/ term/content + composite index (port_id, utm_source, received_at) for per-campaign rollups. website-inquiries intake accepts the five fields. Residential intake intentionally untouched per audit scope. - Invoicing module gate (Init 1c spike): new invoices-module.service + invoices layout guard + registry entry invoices_module_enabled (default false). Audit conclusion in launch-readiness.md: payments table is canonical money path; /invoices flow is parallel infrastructure now hidden by default. Smart-back navigation refactor: - Replaced breadcrumb component with history-aware Back button. New route-labels.ts + use-smart-back hook + navigation-history-tracker so back falls through to the parent route when there's no prior page in history. - Sidebar / topbar / mobile-topbar adopt the new pattern; old breadcrumb-store kept for back-compat consumers but the breadcrumbs component is gone. - 6 detail pages (admin/errors per-id + codes, invoices/ upload-receipts, reports kind, tenancies detail, analytics metric, client detail) migrated. Trackers + docs: - docs/launch-readiness.md — master pre-launch tracker. Includes the reports gap audit (cross-cutting filter set, Marketing + Financial blockers, custom builder remaining entities, scheduled CSV/XLSX, template scope picker). - docs/superpowers/audits/active-uat.md — 15 findings flipped OPEN → SHIPPED locally with fix-applied notes; 4 OPEN remaining (each blocked on user input or cross-repo). - CLAUDE.md — minor session notes carried forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 22:42:37 +02:00
// Three-column grid: smart back button left, search center, actions right.
// The brand logo lives in the sidebar header so the topbar center is
// dedicated to the global search bar.
fix(layout): unblock tablet topbar trigger + un-crush 1024 dashboard title Two Bucket 1 quick-fixes from the 2026-05-22 visual audit, both 1-2-line CSS changes with outsized visual impact. PageHeader stack point: lg → xl The earlier sm → lg revision (commit 6d665d0) fixed the 768 tablet crush but introduced a SECOND crush at exactly 1024: that's where the desktop shell mounts (sidebar = 256px) AND lg:flex-row kicks in, leaving the title cell to compete with a 4-button action row in only ~720px of content. Title degraded to "(" and "Last 30 days" wrapped three-deep ("Last / 30 / days"). Moving to xl (1280) keeps the strip stacked through tablet AND the narrowest desktop width. Verified via Playwright at 1024 — title now reads cleanly with the action row stacked below. Topbar tablet logo trigger: AppShell mounts a logo button in Topbar's leadingSlot prop on tablet (the design intent: click logo → sidebar Sheet slides in). Live screenshot at 768 showed zero affordance — search bar started at the very left edge of the visible viewport. Two root causes, both fixed: - center grid column was minmax(420px, 800px) which starved the left column to ~100px at 768 width (no sidebar present). Changed to minmax(280px, 800px) at base, minmax(420px, 800px) only at lg+. - search container had unconditional sm:-translate-x-... shifting it 128px LEFT to compensate for a sidebar that isn't present at tablet, pulling the search input over the leading- slot. Gated the translate to lg: so it only kicks in when the sidebar is actually inline. Verified via Playwright at 768 — hamburger icon now appears in the top-left corner; search bar sits to its right without overlap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:02:57 +02:00
//
feat(launch-readiness-batch): UAT drains, navigation refactor, launch infra, trackers Bundles the rest of the in-flight work from this UAT round into one checkpoint. Each sub-area is independent; see the headings below. UAT polish (drained 11 findings from active-uat.md): - Dialog primitive default bumped sm:max-w-xl/lg:max-w-3xl → sm:max-w-2xl/lg:max-w-4xl so multi-field forms + PDF previews aren't cramped at 1440-1920px. - Notes tab badge aggregation: new countFor{Client,Yacht,Company} Aggregated helpers in notes.service mirror the listFor*Aggregated symmetric-reach joins. yacht-tabs + company-tabs render the badge; client-tabs already had badge support. - Supplemental-info form polish bundle: BrandedAuthShell gains a `width: 'sm' | 'md'` prop (md uses min-h-dvh scroll instead of fixed inset-0 pin so long forms scroll naturally). Form picks up port branding (logoUrl + backgroundUrl + appName) via loadByToken. Address fields completed (street + city + region + postal + country). Port name eyebrow + success-state copy added. - new-document-menu Upload-file landing toast: per-file completion emits toast.success with action link to the destination entity or folder. - interest-tabs OverviewTab "from client" pill on Email + Phone rows via new EditableRow `inheritedFrom` prop. - create-document-wizard subject picker → segmented button strip (5 types visible at once). Launch infra: - UTM column wiring (Init 1b step 4): migration 0089_website_submissions_utm.sql adds utm_source/medium/campaign/ term/content + composite index (port_id, utm_source, received_at) for per-campaign rollups. website-inquiries intake accepts the five fields. Residential intake intentionally untouched per audit scope. - Invoicing module gate (Init 1c spike): new invoices-module.service + invoices layout guard + registry entry invoices_module_enabled (default false). Audit conclusion in launch-readiness.md: payments table is canonical money path; /invoices flow is parallel infrastructure now hidden by default. Smart-back navigation refactor: - Replaced breadcrumb component with history-aware Back button. New route-labels.ts + use-smart-back hook + navigation-history-tracker so back falls through to the parent route when there's no prior page in history. - Sidebar / topbar / mobile-topbar adopt the new pattern; old breadcrumb-store kept for back-compat consumers but the breadcrumbs component is gone. - 6 detail pages (admin/errors per-id + codes, invoices/ upload-receipts, reports kind, tenancies detail, analytics metric, client detail) migrated. Trackers + docs: - docs/launch-readiness.md — master pre-launch tracker. Includes the reports gap audit (cross-cutting filter set, Marketing + Financial blockers, custom builder remaining entities, scheduled CSV/XLSX, template scope picker). - docs/superpowers/audits/active-uat.md — 15 findings flipped OPEN → SHIPPED locally with fix-applied notes; 4 OPEN remaining (each blocked on user input or cross-repo). - CLAUDE.md — minor session notes carried forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 22:42:37 +02:00
// Grid is `auto auto 1fr` so the left + right columns size to their
// actual content (back-button label on the left; New / Inbox / Avatar
// on the right) and the search column soaks up the rest.
//
// Wayfinding model: the legacy breadcrumb chain was removed in favor
// of a single contextual back button ("Back to Clients", "Back to
// Sarah Doe"). Detail pages register their parent via
// `useBreadcrumbHint` so the label is entity-aware; everything else
// is URL-derived. See src/hooks/use-smart-back.ts.
<header className="relative grid h-14 grid-cols-[auto_1fr_auto] items-center border-b border-border bg-background gap-3 px-4 shrink-0">
feat(launch-readiness-batch): UAT drains, navigation refactor, launch infra, trackers Bundles the rest of the in-flight work from this UAT round into one checkpoint. Each sub-area is independent; see the headings below. UAT polish (drained 11 findings from active-uat.md): - Dialog primitive default bumped sm:max-w-xl/lg:max-w-3xl → sm:max-w-2xl/lg:max-w-4xl so multi-field forms + PDF previews aren't cramped at 1440-1920px. - Notes tab badge aggregation: new countFor{Client,Yacht,Company} Aggregated helpers in notes.service mirror the listFor*Aggregated symmetric-reach joins. yacht-tabs + company-tabs render the badge; client-tabs already had badge support. - Supplemental-info form polish bundle: BrandedAuthShell gains a `width: 'sm' | 'md'` prop (md uses min-h-dvh scroll instead of fixed inset-0 pin so long forms scroll naturally). Form picks up port branding (logoUrl + backgroundUrl + appName) via loadByToken. Address fields completed (street + city + region + postal + country). Port name eyebrow + success-state copy added. - new-document-menu Upload-file landing toast: per-file completion emits toast.success with action link to the destination entity or folder. - interest-tabs OverviewTab "from client" pill on Email + Phone rows via new EditableRow `inheritedFrom` prop. - create-document-wizard subject picker → segmented button strip (5 types visible at once). Launch infra: - UTM column wiring (Init 1b step 4): migration 0089_website_submissions_utm.sql adds utm_source/medium/campaign/ term/content + composite index (port_id, utm_source, received_at) for per-campaign rollups. website-inquiries intake accepts the five fields. Residential intake intentionally untouched per audit scope. - Invoicing module gate (Init 1c spike): new invoices-module.service + invoices layout guard + registry entry invoices_module_enabled (default false). Audit conclusion in launch-readiness.md: payments table is canonical money path; /invoices flow is parallel infrastructure now hidden by default. Smart-back navigation refactor: - Replaced breadcrumb component with history-aware Back button. New route-labels.ts + use-smart-back hook + navigation-history-tracker so back falls through to the parent route when there's no prior page in history. - Sidebar / topbar / mobile-topbar adopt the new pattern; old breadcrumb-store kept for back-compat consumers but the breadcrumbs component is gone. - 6 detail pages (admin/errors per-id + codes, invoices/ upload-receipts, reports kind, tenancies detail, analytics metric, client detail) migrated. Trackers + docs: - docs/launch-readiness.md — master pre-launch tracker. Includes the reports gap audit (cross-cutting filter set, Marketing + Financial blockers, custom builder remaining entities, scheduled CSV/XLSX, template scope picker). - docs/superpowers/audits/active-uat.md — 15 findings flipped OPEN → SHIPPED locally with fix-applied notes; 4 OPEN remaining (each blocked on user input or cross-repo). - CLAUDE.md — minor session notes carried forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 22:42:37 +02:00
{/* LEFT: optional sidebar trigger (tablet) + smart back button.
Hard-capped width so the column never extends into the
absolutely-positioned search bar's footprint. The cap is
conservative on smaller widths to leave the search bar
breathing room, more generous at xl. */}
<div className="min-w-0 flex items-center gap-1.5 max-w-[180px] lg:max-w-[220px] xl:max-w-[260px]">
feat(layout): add tablet viewport tier (mobile/tablet/desktop) Previously the app used a binary matchMedia split at 1023.98px, so iPad portrait + half-screen-on-13"-Mac both fell into the mobile shell — neither is really mobile. The tablet tier fills that gap. - `use-is-mobile.ts` gains `useViewportTier()` returning 'mobile' | 'tablet' | 'desktop' (mobile < 768, tablet 768-1023, desktop ≥ 1024). Backed by useSyncExternalStore so render reads stay pure. `useIsMobile()` retained as a back-compat alias = `tier !== 'desktop'` so existing call sites don't have to change in lockstep. - `app-shell.tsx` now renders three branches. Mobile + desktop unchanged. Tablet renders the desktop shell, but the Sidebar lives inside a left-side `<Sheet>` opened by a new leading logo button in the Topbar. SheetContent width matches `--width-sidebar` so the open state reads consistent. Children subtree position stays invariant across tier flips so inline-edit drafts survive a resize. - `topbar.tsx` accepts an optional `leadingSlot` rendered before the back button + breadcrumbs in the LEFT column. AppShell mounts a port-logo button in that slot on tablet (or a three-bar menu icon when the port has no logo yet) that triggers the sheet. - `page-header.tsx` was the dashboard "title card looks bad on tablet" surface — the actions row was forced no-wrap at sm (640px) which crushed the title on iPad-portrait. Stack point moved from sm to lg, so tablet stacks vertically (title above, actions below); desktop returns to side-by-side. tsc clean, 1454/1454 vitest pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:37:23 +02:00
{leadingSlot}
feat(launch-readiness-batch): UAT drains, navigation refactor, launch infra, trackers Bundles the rest of the in-flight work from this UAT round into one checkpoint. Each sub-area is independent; see the headings below. UAT polish (drained 11 findings from active-uat.md): - Dialog primitive default bumped sm:max-w-xl/lg:max-w-3xl → sm:max-w-2xl/lg:max-w-4xl so multi-field forms + PDF previews aren't cramped at 1440-1920px. - Notes tab badge aggregation: new countFor{Client,Yacht,Company} Aggregated helpers in notes.service mirror the listFor*Aggregated symmetric-reach joins. yacht-tabs + company-tabs render the badge; client-tabs already had badge support. - Supplemental-info form polish bundle: BrandedAuthShell gains a `width: 'sm' | 'md'` prop (md uses min-h-dvh scroll instead of fixed inset-0 pin so long forms scroll naturally). Form picks up port branding (logoUrl + backgroundUrl + appName) via loadByToken. Address fields completed (street + city + region + postal + country). Port name eyebrow + success-state copy added. - new-document-menu Upload-file landing toast: per-file completion emits toast.success with action link to the destination entity or folder. - interest-tabs OverviewTab "from client" pill on Email + Phone rows via new EditableRow `inheritedFrom` prop. - create-document-wizard subject picker → segmented button strip (5 types visible at once). Launch infra: - UTM column wiring (Init 1b step 4): migration 0089_website_submissions_utm.sql adds utm_source/medium/campaign/ term/content + composite index (port_id, utm_source, received_at) for per-campaign rollups. website-inquiries intake accepts the five fields. Residential intake intentionally untouched per audit scope. - Invoicing module gate (Init 1c spike): new invoices-module.service + invoices layout guard + registry entry invoices_module_enabled (default false). Audit conclusion in launch-readiness.md: payments table is canonical money path; /invoices flow is parallel infrastructure now hidden by default. Smart-back navigation refactor: - Replaced breadcrumb component with history-aware Back button. New route-labels.ts + use-smart-back hook + navigation-history-tracker so back falls through to the parent route when there's no prior page in history. - Sidebar / topbar / mobile-topbar adopt the new pattern; old breadcrumb-store kept for back-compat consumers but the breadcrumbs component is gone. - 6 detail pages (admin/errors per-id + codes, invoices/ upload-receipts, reports kind, tenancies detail, analytics metric, client detail) migrated. Trackers + docs: - docs/launch-readiness.md — master pre-launch tracker. Includes the reports gap audit (cross-cutting filter set, Marketing + Financial blockers, custom builder remaining entities, scheduled CSV/XLSX, template scope picker). - docs/superpowers/audits/active-uat.md — 15 findings flipped OPEN → SHIPPED locally with fix-applied notes; 4 OPEN remaining (each blocked on user input or cross-repo). - CLAUDE.md — minor session notes carried forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 22:42:37 +02:00
<BackButton variant="desktop" />
</div>
{/* CENTER (spacer): the search bar is absolutely positioned below
so it anchors to true viewport center regardless of left/right
column widths. This empty grid track keeps `auto 1fr auto` so
the right column behaves the same as before. */}
<div aria-hidden />
{/* CENTER: global search, anchored to true viewport center.
The topbar element starts AFTER the 256px sidebar at lg+, so
`left: 50%` of the topbar lands sidebar/2 (=128px) right of the
viewport center. We subtract that offset at lg+ so the search
bar sits under the browser address bar; below lg the sidebar
is hidden behind a Sheet and the topbar spans the full
viewport, so plain `left: 50%` is already correct.
Caps scale by viewport tier so the bar doesn't crowd the side
feat(launch-readiness-batch): UAT drains, navigation refactor, launch infra, trackers Bundles the rest of the in-flight work from this UAT round into one checkpoint. Each sub-area is independent; see the headings below. UAT polish (drained 11 findings from active-uat.md): - Dialog primitive default bumped sm:max-w-xl/lg:max-w-3xl → sm:max-w-2xl/lg:max-w-4xl so multi-field forms + PDF previews aren't cramped at 1440-1920px. - Notes tab badge aggregation: new countFor{Client,Yacht,Company} Aggregated helpers in notes.service mirror the listFor*Aggregated symmetric-reach joins. yacht-tabs + company-tabs render the badge; client-tabs already had badge support. - Supplemental-info form polish bundle: BrandedAuthShell gains a `width: 'sm' | 'md'` prop (md uses min-h-dvh scroll instead of fixed inset-0 pin so long forms scroll naturally). Form picks up port branding (logoUrl + backgroundUrl + appName) via loadByToken. Address fields completed (street + city + region + postal + country). Port name eyebrow + success-state copy added. - new-document-menu Upload-file landing toast: per-file completion emits toast.success with action link to the destination entity or folder. - interest-tabs OverviewTab "from client" pill on Email + Phone rows via new EditableRow `inheritedFrom` prop. - create-document-wizard subject picker → segmented button strip (5 types visible at once). Launch infra: - UTM column wiring (Init 1b step 4): migration 0089_website_submissions_utm.sql adds utm_source/medium/campaign/ term/content + composite index (port_id, utm_source, received_at) for per-campaign rollups. website-inquiries intake accepts the five fields. Residential intake intentionally untouched per audit scope. - Invoicing module gate (Init 1c spike): new invoices-module.service + invoices layout guard + registry entry invoices_module_enabled (default false). Audit conclusion in launch-readiness.md: payments table is canonical money path; /invoices flow is parallel infrastructure now hidden by default. Smart-back navigation refactor: - Replaced breadcrumb component with history-aware Back button. New route-labels.ts + use-smart-back hook + navigation-history-tracker so back falls through to the parent route when there's no prior page in history. - Sidebar / topbar / mobile-topbar adopt the new pattern; old breadcrumb-store kept for back-compat consumers but the breadcrumbs component is gone. - 6 detail pages (admin/errors per-id + codes, invoices/ upload-receipts, reports kind, tenancies detail, analytics metric, client detail) migrated. Trackers + docs: - docs/launch-readiness.md — master pre-launch tracker. Includes the reports gap audit (cross-cutting filter set, Marketing + Financial blockers, custom builder remaining entities, scheduled CSV/XLSX, template scope picker). - docs/superpowers/audits/active-uat.md — 15 findings flipped OPEN → SHIPPED locally with fix-applied notes; 4 OPEN remaining (each blocked on user input or cross-repo). - CLAUDE.md — minor session notes carried forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 22:42:37 +02:00
columns. The previous max-w-2xl (672px) at xl ate so much of
the topbar that the back-button column on the left got
visually clipped by the search bar; tightened to max-w-xl so
a "Back to Administration"-class label can render in full:
base: max-w-md (28rem)
feat(launch-readiness-batch): UAT drains, navigation refactor, launch infra, trackers Bundles the rest of the in-flight work from this UAT round into one checkpoint. Each sub-area is independent; see the headings below. UAT polish (drained 11 findings from active-uat.md): - Dialog primitive default bumped sm:max-w-xl/lg:max-w-3xl → sm:max-w-2xl/lg:max-w-4xl so multi-field forms + PDF previews aren't cramped at 1440-1920px. - Notes tab badge aggregation: new countFor{Client,Yacht,Company} Aggregated helpers in notes.service mirror the listFor*Aggregated symmetric-reach joins. yacht-tabs + company-tabs render the badge; client-tabs already had badge support. - Supplemental-info form polish bundle: BrandedAuthShell gains a `width: 'sm' | 'md'` prop (md uses min-h-dvh scroll instead of fixed inset-0 pin so long forms scroll naturally). Form picks up port branding (logoUrl + backgroundUrl + appName) via loadByToken. Address fields completed (street + city + region + postal + country). Port name eyebrow + success-state copy added. - new-document-menu Upload-file landing toast: per-file completion emits toast.success with action link to the destination entity or folder. - interest-tabs OverviewTab "from client" pill on Email + Phone rows via new EditableRow `inheritedFrom` prop. - create-document-wizard subject picker → segmented button strip (5 types visible at once). Launch infra: - UTM column wiring (Init 1b step 4): migration 0089_website_submissions_utm.sql adds utm_source/medium/campaign/ term/content + composite index (port_id, utm_source, received_at) for per-campaign rollups. website-inquiries intake accepts the five fields. Residential intake intentionally untouched per audit scope. - Invoicing module gate (Init 1c spike): new invoices-module.service + invoices layout guard + registry entry invoices_module_enabled (default false). Audit conclusion in launch-readiness.md: payments table is canonical money path; /invoices flow is parallel infrastructure now hidden by default. Smart-back navigation refactor: - Replaced breadcrumb component with history-aware Back button. New route-labels.ts + use-smart-back hook + navigation-history-tracker so back falls through to the parent route when there's no prior page in history. - Sidebar / topbar / mobile-topbar adopt the new pattern; old breadcrumb-store kept for back-compat consumers but the breadcrumbs component is gone. - 6 detail pages (admin/errors per-id + codes, invoices/ upload-receipts, reports kind, tenancies detail, analytics metric, client detail) migrated. Trackers + docs: - docs/launch-readiness.md — master pre-launch tracker. Includes the reports gap audit (cross-cutting filter set, Marketing + Financial blockers, custom builder remaining entities, scheduled CSV/XLSX, template scope picker). - docs/superpowers/audits/active-uat.md — 15 findings flipped OPEN → SHIPPED locally with fix-applied notes; 4 OPEN remaining (each blocked on user input or cross-repo). - CLAUDE.md — minor session notes carried forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 22:42:37 +02:00
lg: max-w-lg (32rem)
xl: max-w-xl (36rem)
The wrapper is pointer-events-none so it doesn't capture
clicks meant for the left/right columns underneath; only the
input itself receives pointer events. */}
feat(launch-readiness-batch): UAT drains, navigation refactor, launch infra, trackers Bundles the rest of the in-flight work from this UAT round into one checkpoint. Each sub-area is independent; see the headings below. UAT polish (drained 11 findings from active-uat.md): - Dialog primitive default bumped sm:max-w-xl/lg:max-w-3xl → sm:max-w-2xl/lg:max-w-4xl so multi-field forms + PDF previews aren't cramped at 1440-1920px. - Notes tab badge aggregation: new countFor{Client,Yacht,Company} Aggregated helpers in notes.service mirror the listFor*Aggregated symmetric-reach joins. yacht-tabs + company-tabs render the badge; client-tabs already had badge support. - Supplemental-info form polish bundle: BrandedAuthShell gains a `width: 'sm' | 'md'` prop (md uses min-h-dvh scroll instead of fixed inset-0 pin so long forms scroll naturally). Form picks up port branding (logoUrl + backgroundUrl + appName) via loadByToken. Address fields completed (street + city + region + postal + country). Port name eyebrow + success-state copy added. - new-document-menu Upload-file landing toast: per-file completion emits toast.success with action link to the destination entity or folder. - interest-tabs OverviewTab "from client" pill on Email + Phone rows via new EditableRow `inheritedFrom` prop. - create-document-wizard subject picker → segmented button strip (5 types visible at once). Launch infra: - UTM column wiring (Init 1b step 4): migration 0089_website_submissions_utm.sql adds utm_source/medium/campaign/ term/content + composite index (port_id, utm_source, received_at) for per-campaign rollups. website-inquiries intake accepts the five fields. Residential intake intentionally untouched per audit scope. - Invoicing module gate (Init 1c spike): new invoices-module.service + invoices layout guard + registry entry invoices_module_enabled (default false). Audit conclusion in launch-readiness.md: payments table is canonical money path; /invoices flow is parallel infrastructure now hidden by default. Smart-back navigation refactor: - Replaced breadcrumb component with history-aware Back button. New route-labels.ts + use-smart-back hook + navigation-history-tracker so back falls through to the parent route when there's no prior page in history. - Sidebar / topbar / mobile-topbar adopt the new pattern; old breadcrumb-store kept for back-compat consumers but the breadcrumbs component is gone. - 6 detail pages (admin/errors per-id + codes, invoices/ upload-receipts, reports kind, tenancies detail, analytics metric, client detail) migrated. Trackers + docs: - docs/launch-readiness.md — master pre-launch tracker. Includes the reports gap audit (cross-cutting filter set, Marketing + Financial blockers, custom builder remaining entities, scheduled CSV/XLSX, template scope picker). - docs/superpowers/audits/active-uat.md — 15 findings flipped OPEN → SHIPPED locally with fix-applied notes; 4 OPEN remaining (each blocked on user input or cross-repo). - CLAUDE.md — minor session notes carried forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 22:42:37 +02:00
<div className="pointer-events-none absolute inset-y-0 left-1/2 lg:left-[calc(50%-var(--width-sidebar)/2)] flex w-full max-w-md -translate-x-1/2 items-center px-4 lg:max-w-lg xl:max-w-xl">
<div className="pointer-events-auto w-full min-w-0">
<CommandSearch />
</div>
</div>
{/* RIGHT: action row */}
<div className="flex items-center gap-2 shrink-0 justify-end">
{/* + New dropdown */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="sm"
className="bg-gradient-brand hover:opacity-90 text-white gap-1.5 shadow-sm transition-all duration-base ease-smooth hover:scale-[1.02]"
>
<Plus className="w-4 h-4" aria-hidden />
<span className="hidden sm:inline">New</span>
</Button>
</DropdownMenuTrigger>
feat(ui): broad consistency sweep — sources, dates, comboboxes, milestones Mobile + responsive - berth-form full-width on phones (was 480px fixed → overflowed iPhone) - currency-input switched to inputMode=decimal with live thousands separator - client-form Country/Timezone/Source/Preferred-Contact full-width <sm - contacts row restructured so Primary toggle + Remove get their own strip - customize-dashboard footer stacks vertically on mobile; Done full-width - interest-form client/berth pickers no longer cmdk-filter on UUID (typing "Carlos" now returns Carlos Vega instead of "No clients found") Data + consistency - SOURCES + SOURCE_LABELS + formatSource() in lib/constants; 9 surfaces now resolve interest/client source from one place - INTEREST_OUTCOMES adds lost_other (picker, badge, timeline) - Berth options natural-sort A1 → A2 → … → A10 via lib/utils/mooring-sort - archiver downgraded ^8 → ^7.0.1 so the GDPR export route compiles - TableBody last-row uses border-b-0 (not border-0); colored left-accent on the bottom berth row now renders - Hide Invite-to-Portal until port setting === true (was !== false default-show) - OwnerPicker primer query resolves entity name on first paint (no more UUID flash before the popover opens) Terminology - Replaced user-facing "Documenso" with "signing service" / "Generated EOI" / "Manual EOI" in 8 components (admin/internal references kept) - Plainer status-change copy on berth-detail-header Forms + editing - InlineEditableField gained a `date` variant (native picker); applied to company incorporation date and ready for other YYYY-MM-DD plaintext fields - Inline source picker on interest-tabs detail (was free text) - TagPicker self-hides when port has no tags AND nothing is selected - New ReminderDaysInput with preset chips (1d / 3d / 1wk / 2wk / 1mo / custom) - Compose dialog follow-up is now a toggle that reveals datetime picker Pipeline milestones - changeStageSchema accepts optional milestoneDate; service stamps it on the matching date column instead of always using now - MilestoneAdvanceButton popover collects a back-date before stage advance - Applied to every "Mark X manually" surface on the interest overview EOI / linked-berths polish - Add-bypass row aligned inline with toggle descriptions - Tooltips on "Specifically pitching" / "Mark in EOI bundle" explain their legal vs. public-map consequences Surfaces - Companies list now has the column picker + persisted hidden-column prefs - NotesList aggregate flag enabled on clients, companies, residential_clients (yachts already aggregated) ft/m unit toggle (interim, before drift fix) - "Berth size desired" gets a section-level ft/m toggle; per-field hint shows the converted value. Storage stays canonical-ft for now; the drift-safe persistence migration is the next step. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 14:50:58 +02:00
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuLabel className="text-xs text-muted-foreground">Create</DropdownMenuLabel>
<DropdownMenuSeparator />
feat(ui): broad consistency sweep — sources, dates, comboboxes, milestones Mobile + responsive - berth-form full-width on phones (was 480px fixed → overflowed iPhone) - currency-input switched to inputMode=decimal with live thousands separator - client-form Country/Timezone/Source/Preferred-Contact full-width <sm - contacts row restructured so Primary toggle + Remove get their own strip - customize-dashboard footer stacks vertically on mobile; Done full-width - interest-form client/berth pickers no longer cmdk-filter on UUID (typing "Carlos" now returns Carlos Vega instead of "No clients found") Data + consistency - SOURCES + SOURCE_LABELS + formatSource() in lib/constants; 9 surfaces now resolve interest/client source from one place - INTEREST_OUTCOMES adds lost_other (picker, badge, timeline) - Berth options natural-sort A1 → A2 → … → A10 via lib/utils/mooring-sort - archiver downgraded ^8 → ^7.0.1 so the GDPR export route compiles - TableBody last-row uses border-b-0 (not border-0); colored left-accent on the bottom berth row now renders - Hide Invite-to-Portal until port setting === true (was !== false default-show) - OwnerPicker primer query resolves entity name on first paint (no more UUID flash before the popover opens) Terminology - Replaced user-facing "Documenso" with "signing service" / "Generated EOI" / "Manual EOI" in 8 components (admin/internal references kept) - Plainer status-change copy on berth-detail-header Forms + editing - InlineEditableField gained a `date` variant (native picker); applied to company incorporation date and ready for other YYYY-MM-DD plaintext fields - Inline source picker on interest-tabs detail (was free text) - TagPicker self-hides when port has no tags AND nothing is selected - New ReminderDaysInput with preset chips (1d / 3d / 1wk / 2wk / 1mo / custom) - Compose dialog follow-up is now a toggle that reveals datetime picker Pipeline milestones - changeStageSchema accepts optional milestoneDate; service stamps it on the matching date column instead of always using now - MilestoneAdvanceButton popover collects a back-date before stage advance - Applied to every "Mark X manually" surface on the interest overview EOI / linked-berths polish - Add-bypass row aligned inline with toggle descriptions - Tooltips on "Specifically pitching" / "Mark in EOI bundle" explain their legal vs. public-map consequences Surfaces - Companies list now has the column picker + persisted hidden-column prefs - NotesList aggregate flag enabled on clients, companies, residential_clients (yachts already aggregated) ft/m unit toggle (interim, before drift fix) - "Berth size desired" gets a section-level ft/m toggle; per-field hint shows the converted value. Storage stays canonical-ft for now; the drift-safe persistence migration is the next step. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 14:50:58 +02:00
{/* Each item routes to the list page with ?create=1 so the
relevant create sheet pops automatically (see
useCreateFromUrl). The legacy `/clients/new`-style routes
this menu used to push to landed on the dynamic detail
page with id="new" and silently 404'd. */}
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
feat(ui): broad consistency sweep — sources, dates, comboboxes, milestones Mobile + responsive - berth-form full-width on phones (was 480px fixed → overflowed iPhone) - currency-input switched to inputMode=decimal with live thousands separator - client-form Country/Timezone/Source/Preferred-Contact full-width <sm - contacts row restructured so Primary toggle + Remove get their own strip - customize-dashboard footer stacks vertically on mobile; Done full-width - interest-form client/berth pickers no longer cmdk-filter on UUID (typing "Carlos" now returns Carlos Vega instead of "No clients found") Data + consistency - SOURCES + SOURCE_LABELS + formatSource() in lib/constants; 9 surfaces now resolve interest/client source from one place - INTEREST_OUTCOMES adds lost_other (picker, badge, timeline) - Berth options natural-sort A1 → A2 → … → A10 via lib/utils/mooring-sort - archiver downgraded ^8 → ^7.0.1 so the GDPR export route compiles - TableBody last-row uses border-b-0 (not border-0); colored left-accent on the bottom berth row now renders - Hide Invite-to-Portal until port setting === true (was !== false default-show) - OwnerPicker primer query resolves entity name on first paint (no more UUID flash before the popover opens) Terminology - Replaced user-facing "Documenso" with "signing service" / "Generated EOI" / "Manual EOI" in 8 components (admin/internal references kept) - Plainer status-change copy on berth-detail-header Forms + editing - InlineEditableField gained a `date` variant (native picker); applied to company incorporation date and ready for other YYYY-MM-DD plaintext fields - Inline source picker on interest-tabs detail (was free text) - TagPicker self-hides when port has no tags AND nothing is selected - New ReminderDaysInput with preset chips (1d / 3d / 1wk / 2wk / 1mo / custom) - Compose dialog follow-up is now a toggle that reveals datetime picker Pipeline milestones - changeStageSchema accepts optional milestoneDate; service stamps it on the matching date column instead of always using now - MilestoneAdvanceButton popover collects a back-date before stage advance - Applied to every "Mark X manually" surface on the interest overview EOI / linked-berths polish - Add-bypass row aligned inline with toggle descriptions - Tooltips on "Specifically pitching" / "Mark in EOI bundle" explain their legal vs. public-map consequences Surfaces - Companies list now has the column picker + persisted hidden-column prefs - NotesList aggregate flag enabled on clients, companies, residential_clients (yachts already aggregated) ft/m unit toggle (interim, before drift fix) - "Berth size desired" gets a section-level ft/m toggle; per-field hint shows the converted value. Storage stays canonical-ft for now; the drift-safe persistence migration is the next step. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 14:50:58 +02:00
<DropdownMenuItem onClick={() => router.push(`${base}/clients?create=1` as any)}>
New Client
</DropdownMenuItem>
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
feat(ui): broad consistency sweep — sources, dates, comboboxes, milestones Mobile + responsive - berth-form full-width on phones (was 480px fixed → overflowed iPhone) - currency-input switched to inputMode=decimal with live thousands separator - client-form Country/Timezone/Source/Preferred-Contact full-width <sm - contacts row restructured so Primary toggle + Remove get their own strip - customize-dashboard footer stacks vertically on mobile; Done full-width - interest-form client/berth pickers no longer cmdk-filter on UUID (typing "Carlos" now returns Carlos Vega instead of "No clients found") Data + consistency - SOURCES + SOURCE_LABELS + formatSource() in lib/constants; 9 surfaces now resolve interest/client source from one place - INTEREST_OUTCOMES adds lost_other (picker, badge, timeline) - Berth options natural-sort A1 → A2 → … → A10 via lib/utils/mooring-sort - archiver downgraded ^8 → ^7.0.1 so the GDPR export route compiles - TableBody last-row uses border-b-0 (not border-0); colored left-accent on the bottom berth row now renders - Hide Invite-to-Portal until port setting === true (was !== false default-show) - OwnerPicker primer query resolves entity name on first paint (no more UUID flash before the popover opens) Terminology - Replaced user-facing "Documenso" with "signing service" / "Generated EOI" / "Manual EOI" in 8 components (admin/internal references kept) - Plainer status-change copy on berth-detail-header Forms + editing - InlineEditableField gained a `date` variant (native picker); applied to company incorporation date and ready for other YYYY-MM-DD plaintext fields - Inline source picker on interest-tabs detail (was free text) - TagPicker self-hides when port has no tags AND nothing is selected - New ReminderDaysInput with preset chips (1d / 3d / 1wk / 2wk / 1mo / custom) - Compose dialog follow-up is now a toggle that reveals datetime picker Pipeline milestones - changeStageSchema accepts optional milestoneDate; service stamps it on the matching date column instead of always using now - MilestoneAdvanceButton popover collects a back-date before stage advance - Applied to every "Mark X manually" surface on the interest overview EOI / linked-berths polish - Add-bypass row aligned inline with toggle descriptions - Tooltips on "Specifically pitching" / "Mark in EOI bundle" explain their legal vs. public-map consequences Surfaces - Companies list now has the column picker + persisted hidden-column prefs - NotesList aggregate flag enabled on clients, companies, residential_clients (yachts already aggregated) ft/m unit toggle (interim, before drift fix) - "Berth size desired" gets a section-level ft/m toggle; per-field hint shows the converted value. Storage stays canonical-ft for now; the drift-safe persistence migration is the next step. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 14:50:58 +02:00
<DropdownMenuItem onClick={() => router.push(`${base}/yachts?create=1` as any)}>
New Yacht
</DropdownMenuItem>
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
<DropdownMenuItem onClick={() => router.push(`${base}/companies?create=1` as any)}>
New Company
</DropdownMenuItem>
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
<DropdownMenuItem onClick={() => router.push(`${base}/interests?create=1` as any)}>
New Interest
</DropdownMenuItem>
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
feat(ui): broad consistency sweep — sources, dates, comboboxes, milestones Mobile + responsive - berth-form full-width on phones (was 480px fixed → overflowed iPhone) - currency-input switched to inputMode=decimal with live thousands separator - client-form Country/Timezone/Source/Preferred-Contact full-width <sm - contacts row restructured so Primary toggle + Remove get their own strip - customize-dashboard footer stacks vertically on mobile; Done full-width - interest-form client/berth pickers no longer cmdk-filter on UUID (typing "Carlos" now returns Carlos Vega instead of "No clients found") Data + consistency - SOURCES + SOURCE_LABELS + formatSource() in lib/constants; 9 surfaces now resolve interest/client source from one place - INTEREST_OUTCOMES adds lost_other (picker, badge, timeline) - Berth options natural-sort A1 → A2 → … → A10 via lib/utils/mooring-sort - archiver downgraded ^8 → ^7.0.1 so the GDPR export route compiles - TableBody last-row uses border-b-0 (not border-0); colored left-accent on the bottom berth row now renders - Hide Invite-to-Portal until port setting === true (was !== false default-show) - OwnerPicker primer query resolves entity name on first paint (no more UUID flash before the popover opens) Terminology - Replaced user-facing "Documenso" with "signing service" / "Generated EOI" / "Manual EOI" in 8 components (admin/internal references kept) - Plainer status-change copy on berth-detail-header Forms + editing - InlineEditableField gained a `date` variant (native picker); applied to company incorporation date and ready for other YYYY-MM-DD plaintext fields - Inline source picker on interest-tabs detail (was free text) - TagPicker self-hides when port has no tags AND nothing is selected - New ReminderDaysInput with preset chips (1d / 3d / 1wk / 2wk / 1mo / custom) - Compose dialog follow-up is now a toggle that reveals datetime picker Pipeline milestones - changeStageSchema accepts optional milestoneDate; service stamps it on the matching date column instead of always using now - MilestoneAdvanceButton popover collects a back-date before stage advance - Applied to every "Mark X manually" surface on the interest overview EOI / linked-berths polish - Add-bypass row aligned inline with toggle descriptions - Tooltips on "Specifically pitching" / "Mark in EOI bundle" explain their legal vs. public-map consequences Surfaces - Companies list now has the column picker + persisted hidden-column prefs - NotesList aggregate flag enabled on clients, companies, residential_clients (yachts already aggregated) ft/m unit toggle (interim, before drift fix) - "Berth size desired" gets a section-level ft/m toggle; per-field hint shows the converted value. Storage stays canonical-ft for now; the drift-safe persistence migration is the next step. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 14:50:58 +02:00
<DropdownMenuItem onClick={() => router.push(`${base}/expenses?create=1` as any)}>
New Expense
</DropdownMenuItem>
feat(ui): broad consistency sweep — sources, dates, comboboxes, milestones Mobile + responsive - berth-form full-width on phones (was 480px fixed → overflowed iPhone) - currency-input switched to inputMode=decimal with live thousands separator - client-form Country/Timezone/Source/Preferred-Contact full-width <sm - contacts row restructured so Primary toggle + Remove get their own strip - customize-dashboard footer stacks vertically on mobile; Done full-width - interest-form client/berth pickers no longer cmdk-filter on UUID (typing "Carlos" now returns Carlos Vega instead of "No clients found") Data + consistency - SOURCES + SOURCE_LABELS + formatSource() in lib/constants; 9 surfaces now resolve interest/client source from one place - INTEREST_OUTCOMES adds lost_other (picker, badge, timeline) - Berth options natural-sort A1 → A2 → … → A10 via lib/utils/mooring-sort - archiver downgraded ^8 → ^7.0.1 so the GDPR export route compiles - TableBody last-row uses border-b-0 (not border-0); colored left-accent on the bottom berth row now renders - Hide Invite-to-Portal until port setting === true (was !== false default-show) - OwnerPicker primer query resolves entity name on first paint (no more UUID flash before the popover opens) Terminology - Replaced user-facing "Documenso" with "signing service" / "Generated EOI" / "Manual EOI" in 8 components (admin/internal references kept) - Plainer status-change copy on berth-detail-header Forms + editing - InlineEditableField gained a `date` variant (native picker); applied to company incorporation date and ready for other YYYY-MM-DD plaintext fields - Inline source picker on interest-tabs detail (was free text) - TagPicker self-hides when port has no tags AND nothing is selected - New ReminderDaysInput with preset chips (1d / 3d / 1wk / 2wk / 1mo / custom) - Compose dialog follow-up is now a toggle that reveals datetime picker Pipeline milestones - changeStageSchema accepts optional milestoneDate; service stamps it on the matching date column instead of always using now - MilestoneAdvanceButton popover collects a back-date before stage advance - Applied to every "Mark X manually" surface on the interest overview EOI / linked-berths polish - Add-bypass row aligned inline with toggle descriptions - Tooltips on "Specifically pitching" / "Mark in EOI bundle" explain their legal vs. public-map consequences Surfaces - Companies list now has the column picker + persisted hidden-column prefs - NotesList aggregate flag enabled on clients, companies, residential_clients (yachts already aggregated) ft/m unit toggle (interim, before drift fix) - "Berth size desired" gets a section-level ft/m toggle; per-field hint shows the converted value. Storage stays canonical-ft for now; the drift-safe persistence migration is the next step. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 14:50:58 +02:00
{/* /reminders 301s to /inbox#reminders (the merged page) and
the server redirect strips the query string, so point
straight at the new path. The Reminders section's
useCreateFromUrl handler still picks up ?create=1. */}
feat: round 2 — stage prompts, berth header, EOI inline edit, measurement units Berth surfaces - New compact mooring-chip header (colored plate + status pill, dock-label in tooltip) replaces the redundant "Berth B1 / Sold / B DOCK" stack - Berth list gains a "Latest deal stage" column showing the most-advanced pipeline stage of any active linked interest (server-aggregated, ranks by PIPELINE_STAGES index) - "Linked prospect" Select on the status-change dialog rebuilt as a Command combobox: search, recent-first sort, stage-coloured pills Pipeline UX - Reverting an interest to Open with linked berths now prompts: keep the links, unlink and reset, or cancel. Silent when no berths are linked - Activity feed + entity-activity feed normalise enum field values via STAGE_LABELS / formatSource: "deposit_10pct → contract_sent" reads as "10% Deposit → Contract Sent" EOI generate dialog - Inline-editable rows for client name, nationality (country combobox), and yacht name — pencil affordance saves directly via clients/yachts PATCH - Replaces the single "Edit on client's page" link with two contextual links framed by short copy explaining what's inline vs what needs the canonical page - Backend EoiContext now includes client.id + yacht.id so the dialog can PATCH without an extra round-trip Company form - New "Connections" section lets the rep attach members (clients) and yachts during create. Yacht attach uses the existing transfer endpoint so audit log + ownership history capture the change - Inline "+ New client" / "+ New yacht" buttons open the canonical forms stacked over the company sheet - After save, the form chains to a yacht pull-in prompt (if any attached client owns yachts not yet linked) and an optional "Create interest" step pre-filled with the first attached client Admin - /admin landing gains a searchable index — typed query flattens groups into a result list matching label + description + group title - "Documenso & EOI" card relabelled to "EOI signing service" (consistent with the user-facing language rename from round 1) Measurement units (migration 0053) - interests gains desired_*_m columns + desired_*_unit discriminators so the rep's literal entry (ft OR m) is preserved verbatim instead of being reconstructed from a single canonical column on every render - yachts + berths gain matching *_unit columns alongside their existing ft + m pairs; defaults to 'ft' so legacy rows still render normally - Interest form POST/PATCH now sends both ft + m + unit; computed m is derived from the ft canonical to keep the recommender SQL unchanged Misc - Active-deals tile + topbar type their Link href as `Route` instead of `any` - Unused REPORT_TYPE_LABELS const dropped from generate-report-form - Test fixtures (fill-eoi-form, documenso-payload, public-berths) updated to include the new id + unit fields on the EoiContext / Berth shapes Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 15:28:22 +02:00
<DropdownMenuItem
onClick={() => router.push(`${base}/inbox?create=1#reminders` as unknown as Route)}
feat: round 2 — stage prompts, berth header, EOI inline edit, measurement units Berth surfaces - New compact mooring-chip header (colored plate + status pill, dock-label in tooltip) replaces the redundant "Berth B1 / Sold / B DOCK" stack - Berth list gains a "Latest deal stage" column showing the most-advanced pipeline stage of any active linked interest (server-aggregated, ranks by PIPELINE_STAGES index) - "Linked prospect" Select on the status-change dialog rebuilt as a Command combobox: search, recent-first sort, stage-coloured pills Pipeline UX - Reverting an interest to Open with linked berths now prompts: keep the links, unlink and reset, or cancel. Silent when no berths are linked - Activity feed + entity-activity feed normalise enum field values via STAGE_LABELS / formatSource: "deposit_10pct → contract_sent" reads as "10% Deposit → Contract Sent" EOI generate dialog - Inline-editable rows for client name, nationality (country combobox), and yacht name — pencil affordance saves directly via clients/yachts PATCH - Replaces the single "Edit on client's page" link with two contextual links framed by short copy explaining what's inline vs what needs the canonical page - Backend EoiContext now includes client.id + yacht.id so the dialog can PATCH without an extra round-trip Company form - New "Connections" section lets the rep attach members (clients) and yachts during create. Yacht attach uses the existing transfer endpoint so audit log + ownership history capture the change - Inline "+ New client" / "+ New yacht" buttons open the canonical forms stacked over the company sheet - After save, the form chains to a yacht pull-in prompt (if any attached client owns yachts not yet linked) and an optional "Create interest" step pre-filled with the first attached client Admin - /admin landing gains a searchable index — typed query flattens groups into a result list matching label + description + group title - "Documenso & EOI" card relabelled to "EOI signing service" (consistent with the user-facing language rename from round 1) Measurement units (migration 0053) - interests gains desired_*_m columns + desired_*_unit discriminators so the rep's literal entry (ft OR m) is preserved verbatim instead of being reconstructed from a single canonical column on every render - yachts + berths gain matching *_unit columns alongside their existing ft + m pairs; defaults to 'ft' so legacy rows still render normally - Interest form POST/PATCH now sends both ft + m + unit; computed m is derived from the ft canonical to keep the recommender SQL unchanged Misc - Active-deals tile + topbar type their Link href as `Route` instead of `any` - Unused REPORT_TYPE_LABELS const dropped from generate-report-form - Test fixtures (fill-eoi-form, documenso-payload, public-berths) updated to include the new id + unit fields on the EoiContext / Berth shapes Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 15:28:22 +02:00
>
New Reminder
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
{/* Unified inbox - combines system alerts (operational) and personal
notifications (user-targeted) into a single bell with two tabs.
Replaces the previous AlertBell + NotificationBell pair. */}
<Inbox />
<Separator orientation="vertical" className="h-6" />
{/* User menu - single source of truth for the profile dropdown.
Same component is mounted in the sidebar footer so the menu
items (incl. port switcher) stay consistent across triggers. */}
<UserMenu
align="end"
user={user}
ports={ports}
trigger={
feat(interests): EOI/contract/reservation tabs + contact log + berth interest milestone + interest list overhaul Major interest workflow expansion driven by the rapid-fire UX session. EOI / Contract / Reservation tabs replace the generic Documents tab when the deal is at the relevant stage — workspace pattern with active-doc hero, signing progress, paper-signed upload, and history strip. Stage- conditional visibility wired through interest-tabs.tsx so the tab set shrinks/expands as the deal moves through the pipeline. Contact log: per-interaction structured log (channel/direction/summary/ optional follow-up reminder). New `interest_contact_log` table + service + tab UI (timeline with channel-coded icons + compose dialog). auto-creates a reminder when followUpAt is set. Berth Interest milestone: first milestone in the OverviewTab's pipeline strip, completes the moment any berth is linked via the junction. Drives the "have we captured what they want?" sanity check for general_interest leads before they move to EOI. Stage-conditional milestones: past phases collapse into a one-liner strip, current phase expands, future phases hide behind a "Show upcoming" toggle. Inline stage picker now defers reason capture to an override-confirm view (only required for illegal transitions, not the default flow). Notes blob → threaded: dropped `interests.notes` column entirely; the threaded `interest_notes` table is the single source of truth. Latest- note teaser on Overview links into the dedicated Notes tab. Polymorphic notes service gains aggregated client view (unions client + interest + yacht notes with source chips and group-by-source toggle). Berth interest list overhaul: - Configurable columns via ColumnPicker (18 toggleable, 5 default-on) - Natural-sort SQL ORDER BY on mooring number (A1, A2, A10 not A10, A2) - Per-letter row tinting via colored left-border accent + dot in cell - Documents tab merged Files (single attachments section) Topbar improvements: - Always-visible back arrow on detail pages (path depth > 2) - Breadcrumb-hint store + useBreadcrumbHint hook so detail pages can push their entity hierarchy (Clients › Mary Smith › Interest › B17) - Tighter spacing, softer separators, 160px crumb truncation DataTable upgrades: - Page-size selector with All option (validator cap raised to 1000) - getRowClassName slot for per-row styling (used by berth tinting) - Fixed Radix SelectItem crash on empty-string values via __any__ sentinel (was crashing every list page that opened a select filter) Interest list: - Configurable columns picker - Stage cell clickable into detail - TagPicker + SavedViewsDropdown sized h-8 to match adjacent buttons - Save view moved into ColumnPicker menu; Views button hidden when no views are saved - Pipeline kanban board endpoint at /api/v1/interests/board with minimal projection, 5000-row cap + truncated banner, filter pass-through Mobile chrome + sidebar collapse removed (always-expanded design choice). User management lists super-admins (was inner-joined on user_port_roles which excluded global super-admins). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:59:28 +02:00
// Button shrunk to match the Avatar's visible footprint so
// the hover halo lands as a tight circle behind the avatar
// (was h-11 w-11 default - the rounded-full halo extended
feat(interests): EOI/contract/reservation tabs + contact log + berth interest milestone + interest list overhaul Major interest workflow expansion driven by the rapid-fire UX session. EOI / Contract / Reservation tabs replace the generic Documents tab when the deal is at the relevant stage — workspace pattern with active-doc hero, signing progress, paper-signed upload, and history strip. Stage- conditional visibility wired through interest-tabs.tsx so the tab set shrinks/expands as the deal moves through the pipeline. Contact log: per-interaction structured log (channel/direction/summary/ optional follow-up reminder). New `interest_contact_log` table + service + tab UI (timeline with channel-coded icons + compose dialog). auto-creates a reminder when followUpAt is set. Berth Interest milestone: first milestone in the OverviewTab's pipeline strip, completes the moment any berth is linked via the junction. Drives the "have we captured what they want?" sanity check for general_interest leads before they move to EOI. Stage-conditional milestones: past phases collapse into a one-liner strip, current phase expands, future phases hide behind a "Show upcoming" toggle. Inline stage picker now defers reason capture to an override-confirm view (only required for illegal transitions, not the default flow). Notes blob → threaded: dropped `interests.notes` column entirely; the threaded `interest_notes` table is the single source of truth. Latest- note teaser on Overview links into the dedicated Notes tab. Polymorphic notes service gains aggregated client view (unions client + interest + yacht notes with source chips and group-by-source toggle). Berth interest list overhaul: - Configurable columns via ColumnPicker (18 toggleable, 5 default-on) - Natural-sort SQL ORDER BY on mooring number (A1, A2, A10 not A10, A2) - Per-letter row tinting via colored left-border accent + dot in cell - Documents tab merged Files (single attachments section) Topbar improvements: - Always-visible back arrow on detail pages (path depth > 2) - Breadcrumb-hint store + useBreadcrumbHint hook so detail pages can push their entity hierarchy (Clients › Mary Smith › Interest › B17) - Tighter spacing, softer separators, 160px crumb truncation DataTable upgrades: - Page-size selector with All option (validator cap raised to 1000) - getRowClassName slot for per-row styling (used by berth tinting) - Fixed Radix SelectItem crash on empty-string values via __any__ sentinel (was crashing every list page that opened a select filter) Interest list: - Configurable columns picker - Stage cell clickable into detail - TagPicker + SavedViewsDropdown sized h-8 to match adjacent buttons - Save view moved into ColumnPicker menu; Views button hidden when no views are saved - Pipeline kanban board endpoint at /api/v1/interests/board with minimal projection, 5000-row cap + truncated banner, filter pass-through Mobile chrome + sidebar collapse removed (always-expanded design choice). User management lists super-admins (was inner-joined on user_port_roles which excluded global super-admins). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:59:28 +02:00
// well past the visible avatar and read as a square glow).
<Button variant="ghost" className="rounded-full h-9 w-9 p-0">
<Avatar className="w-7 h-7">
<AvatarImage src={undefined} />
<AvatarFallback className="bg-brand text-white text-xs font-semibold">
{(user?.name ?? 'U').slice(0, 1).toUpperCase()}
</AvatarFallback>
</Avatar>
</Button>
}
/>
</div>
</header>
);
}