refactor(clients): rebuild detail tabs + columns for new data model

- ClientData in client-detail.tsx now reflects the stripped shape from
  Task 8.2 (drop companyName/isProxy/proxy*/yacht*/berthSizeDesired) and
  gains yachts / companies / activeReservations arrays.
- client-tabs.tsx: Overview trimmed (personal, contacts, source, tags);
  three new count-badged tabs (Yachts, Companies, Reservations).
- New client-yachts-tab.tsx renders owned yachts + Add yacht CTA (TODO:
  YachtForm preset-owner wiring for v2).
- New client-companies-tab.tsx renders memberships with Primary badge and
  since-date; management still lives on the company detail page.
- New client-reservations-tab.tsx maps activeReservations into ReservationRow
  shape and delegates to <ReservationList showBerth />.
- client-columns.tsx drops companyName column (TODO: add Yachts count +
  Primary company once list endpoint joins those).
- client-filters.tsx drops isProxy filter.
- Wire realtime invalidations for yacht:ownership_transferred,
  company_membership:added/ended, and berth_reservation:*.
This commit is contained in:
Matt Ciaccio
2026-04-24 14:36:34 +02:00
parent 4c171848fc
commit b75834ab7e
7 changed files with 374 additions and 113 deletions

View File

@@ -18,7 +18,7 @@ import { TagBadge } from '@/components/shared/tag-badge';
export interface ClientRow {
id: string;
fullName: string;
companyName: string | null;
nationality: string | null;
source: string | null;
archivedAt: string | null;
createdAt: string;
@@ -39,6 +39,10 @@ interface GetColumnsOptions {
onArchive: (client: ClientRow) => void;
}
// TODO: Add "Yachts" (count) and "Primary company" columns once the
// GET /api/v1/clients list endpoint joins owned-yachts and primary-company
// data into the row shape. Until then, the columns are omitted rather than
// shown as empty placeholders.
export function getClientColumns({
portSlug,
onEdit,
@@ -59,14 +63,6 @@ export function getClientColumns({
</Link>
),
},
{
id: 'companyName',
accessorKey: 'companyName',
header: 'Company',
cell: ({ getValue }) => (
<span className="text-muted-foreground">{(getValue() as string | null) ?? '—'}</span>
),
},
{
id: 'primaryContact',
header: 'Primary Contact',
@@ -82,6 +78,14 @@ export function getClientColumns({
);
},
},
{
id: 'nationality',
accessorKey: 'nationality',
header: 'Nationality',
cell: ({ getValue }) => (
<span className="text-muted-foreground">{(getValue() as string | null) ?? '—'}</span>
),
},
{
id: 'source',
accessorKey: 'source',
@@ -149,10 +153,7 @@ export function getClientColumns({
<Pencil className="mr-2 h-3.5 w-3.5" />
Edit
</DropdownMenuItem>
<DropdownMenuItem
className="text-destructive"
onClick={() => onArchive(row.original)}
>
<DropdownMenuItem className="text-destructive" onClick={() => onArchive(row.original)}>
<Archive className="mr-2 h-3.5 w-3.5" />
Archive
</DropdownMenuItem>