From 552b96690328cf81f2c417c85c97531425215449 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 21 May 2026 18:18:29 +0200 Subject: [PATCH] feat(uat-batch-14): InterestDocumentsTab rename, custom-field tooltip, yacht Transfer surface - InterestDocumentsTab section "Legal documents" renamed to "Signature documents" so its scope is unambiguous. The section holds Documenso envelopes (EOI / Reservation / Contract); generic legal uploads belong in Attachments below. - Custom-field admin form's "Sort Order" label now uses the FieldLabel primitive with an explainer tooltip ("Lower numbers render first... use to pin frequently-edited fields to the top"). First adoption of the FieldLabel primitive shipped in PR4.2. - Yacht Ownership History tab gains a "Transfer ownership" button: in the populated state as a header CTA (perm-gated by yachts.edit), in the empty state as the EmptyState action. Reuses the existing YachtTransferDialog from the header. Closes the "no way to enter/ change" UX gap without duplicating the transfer logic. - Verified the existing row-owner rendering already uses OwnerLink, so the row-click affordance was already in place. tsc clean. 1419/1419 vitest pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../admin/custom-fields/custom-field-form.tsx | 8 +- .../interests/interest-documents-tab.tsx | 2 +- .../yachts/yacht-ownership-history.tsx | 121 +++++++++++------- 3 files changed, 86 insertions(+), 45 deletions(-) diff --git a/src/components/admin/custom-fields/custom-field-form.tsx b/src/components/admin/custom-fields/custom-field-form.tsx index 7ca55d83..a22993ee 100644 --- a/src/components/admin/custom-fields/custom-field-form.tsx +++ b/src/components/admin/custom-fields/custom-field-form.tsx @@ -7,6 +7,7 @@ import { Plus, X } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; +import { FieldLabel } from '@/components/ui/field-label'; import { Switch } from '@/components/ui/switch'; import { Dialog, @@ -297,7 +298,12 @@ function CustomFieldFormBody({ open, onOpenChange, field, onSuccess }: CustomFie {/* Sort Order */}
- + + Sort Order +
-

Legal documents

+

Signature documents

diff --git a/src/components/yachts/yacht-ownership-history.tsx b/src/components/yachts/yacht-ownership-history.tsx index 076ef50e..3b701a16 100644 --- a/src/components/yachts/yacht-ownership-history.tsx +++ b/src/components/yachts/yacht-ownership-history.tsx @@ -1,9 +1,11 @@ 'use client'; +import { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { useParams } from 'next/navigation'; -import { Loader2 } from 'lucide-react'; +import { Loader2, ArrowLeftRight } from 'lucide-react'; +import { Button } from '@/components/ui/button'; import { Table, TableBody, @@ -15,6 +17,8 @@ import { import { Badge } from '@/components/ui/badge'; import { EmptyState } from '@/components/shared/empty-state'; import { OwnerLink } from '@/components/yachts/yacht-detail-header'; +import { YachtTransferDialog } from '@/components/yachts/yacht-transfer-dialog'; +import { PermissionGate } from '@/components/shared/permission-gate'; import { apiFetch } from '@/lib/api/client'; interface OwnershipHistoryRow { @@ -52,6 +56,7 @@ function formatDate(value: string | null): string { export function YachtOwnershipHistory({ yachtId }: YachtOwnershipHistoryProps) { const params = useParams<{ portSlug: string }>(); const portSlug = params?.portSlug ?? ''; + const [transferOpen, setTransferOpen] = useState(false); const { data, isLoading } = useQuery({ queryKey: ['yachts', yachtId, 'ownership-history'], @@ -61,6 +66,11 @@ export function YachtOwnershipHistory({ yachtId }: YachtOwnershipHistoryProps) { ), }); + const currentOwnerRow = data?.find((r) => r.endDate === null); + const currentOwner = currentOwnerRow + ? { type: currentOwnerRow.ownerType, id: currentOwnerRow.ownerId } + : undefined; + if (isLoading) { return (
@@ -71,53 +81,78 @@ export function YachtOwnershipHistory({ yachtId }: YachtOwnershipHistoryProps) { if (!data || data.length === 0) { return ( - + <> + setTransferOpen(true) }} + /> + + ); } return ( -
- - - - Start Date - End Date - Owner - Reason - Notes - - - - {data.map((row) => ( - - {formatDate(row.startDate)} - - {row.endDate ? ( - formatDate(row.endDate) - ) : ( - - Current - - )} - - - - - - {row.transferReason - ? (REASON_LABELS[row.transferReason] ?? row.transferReason) - : '-'} - - - {row.transferNotes ?? '-'} - +
+
+ + + +
+
+
+ + + Start Date + End Date + Owner + Reason + Notes - ))} - -
+ + + {data.map((row) => ( + + {formatDate(row.startDate)} + + {row.endDate ? ( + formatDate(row.endDate) + ) : ( + + Current + + )} + + + + + + {row.transferReason + ? (REASON_LABELS[row.transferReason] ?? row.transferReason) + : '-'} + + + {row.transferNotes ?? '-'} + + + ))} + + +
+
); }