Files
pn-new-crm/src/components/interests/interest-documents-tab.tsx

173 lines
6.1 KiB
TypeScript
Raw Normal View History

'use client';
import { useState } from 'react';
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
import { useQuery, useQueryClient } from '@tanstack/react-query';
fix(ux): batch UX audit fixes across spine pages Comprehensive audit findings rolled up into one pass. Bugs: - dialog.tsx — sm-breakpoint centering classes (sm:left-[50%] / sm:top-[50%]) were being silently stripped by tailwind-merge because the base inset-0 + sm:inset-auto pair counted as a conflict. Replaced with explicit per-side utilities (top-0 right-0 bottom-0 left-0 + sm:right-auto sm:bottom-auto). Every Dialog instance now centers correctly on desktop. (Affected 16 dialog consumers.) - interest-documents-tab.tsx — useQuery shared the queryKey ['interests', interestId] with the parent InterestDetail's query but returned a different shape ({ data: ... } envelope vs unwrapped). They clobbered each other's cache on tab mount, degenerating the parent header to "Unknown Client" / "Open" briefly. Unified the queryFn shape so the cache stays consistent. - interest-tabs.tsx — milestone steps now derive done-state from PIPELINE_STAGES.indexOf(currentStage) >= step.advanceStage_idx as well as from the date stamp. Stage truth > date truth. Seeded / imported interests that arrived past `open` without per-step dates now correctly show their milestone steps as checked. - interest-detail.tsx — wires useMobileChrome so the mobile topbar shows the client name instead of the interest UUID. - interest-documents-tab.tsx — empty state restructured to a centered "No documents yet — Generate EOI" CTA card instead of a small primary button floating in the corner. - timeline/route.ts — synthesizes a "Created at <stage>" event when no audit-log rows exist for the interest, so the Activity tab isn't empty for seeded interests. - lead-source-chart.tsx — pie radii switched from fixed 90px/50px to "70%"/"40%" so the pie scales with the container instead of being clipped at narrow widths; reserved 40px for the legend. Visual / clarity: - interest-detail-header.tsx — Won/Lost rendered as branded text buttons on desktop ("Mark won", "Close as lost") and icon-only on mobile via `hidden sm:inline`. Edit/Archive stay icon-only. Reopen promoted to a labeled button when the interest is closed. Added "Last contact Xd ago" to the meta row. - detail-header-strip.tsx — py-4 → py-3 (tighter strip). - interest-tabs.tsx — milestone cards: the next pending milestone gets a brand-blue ring + "NEXT" pill so the user can see at a glance which lifecycle to act on. Its primary action gets the filled button variant. - interest-tabs.tsx — Deposit milestone: invoice flow promoted to primary CTA ("Create deposit invoice"), manual stage advance demoted to a small text link ("Mark received manually"). Reflects the actual recommended path now that recordPayment auto-advances on payment. - inline-editable-field.tsx — pencil affordance shown faintly (opacity-20) at rest so users discover that fields are editable without having to hover-test every label. Lifts to opacity-60 on hover. - constants.ts — STAGE_SHORT_LABELS map for cramped contexts; pipeline-chart.tsx + pipeline-funnel-chart.tsx use them on mobile via useIsMobile, so the rotated 9-stage axis isn't a wall of overlap on a 393px screen. - client-pipeline-summary.tsx — StageStepper rebuilt as a single segmented progress bar instead of 9 micro-dots + connectors that rendered inconsistently at tight widths. Each stage is an equal slice that lights up as the interest reaches it; tooltips on hover give the full stage name. Also dropped a pre-existing dead `br` variable. - dashboard empty states — Lead Source, Revenue Breakdown, Pipeline Funnel, and Recent Activity now have helpful descriptions explaining what populates them, instead of bare "No interests in range". - use-paginated-query.ts — reuses `&` when the endpoint already has `?`, so callers like the documents hub don't generate `…?tab=eoi_queue&signatureOnly=true?page=1&limit=25` (which the API rejected as 400). Caught while testing the now-removed EOI route but applies broadly. tsc clean. vitest 832/832 pass. eslint 0 errors (down from 1 pre-existing) on every file touched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 01:24:15 +02:00
import { FileSignature } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { DocumentList } from '@/components/documents/document-list';
import { EoiGenerateDialog } from '@/components/documents/eoi-generate-dialog';
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
import { FileGrid, type FileRow } from '@/components/files/file-grid';
import { FileUploadZone } from '@/components/files/file-upload-zone';
import { FilePreviewDialog } from '@/components/files/file-preview-dialog';
import { PermissionGate } from '@/components/shared/permission-gate';
import { usePaginatedQuery } from '@/hooks/use-paginated-query';
import { useRealtimeInvalidation } from '@/hooks/use-realtime-invalidation';
import { apiFetch } from '@/lib/api/client';
interface InterestDocumentsTabProps {
interestId: string;
}
interface InterestData {
id: string;
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
clientId?: string | null;
}
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
/**
* Documents tab legal instruments (EOI / contract / reservation) with
* full signing status, plus an Attachments section for any other file the
* rep wants on the deal. Replaces the standalone Files tab at the
* interest level virtually everything is either a legal doc or rare
* one-off, and a separate tab was dead weight 95% of the time.
*/
export function InterestDocumentsTab({ interestId }: InterestDocumentsTabProps) {
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
const queryClient = useQueryClient();
const [eoiDialogOpen, setEoiDialogOpen] = useState(false);
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
const [previewFile, setPreviewFile] = useState<FileRow | null>(null);
fix(ux): batch UX audit fixes across spine pages Comprehensive audit findings rolled up into one pass. Bugs: - dialog.tsx — sm-breakpoint centering classes (sm:left-[50%] / sm:top-[50%]) were being silently stripped by tailwind-merge because the base inset-0 + sm:inset-auto pair counted as a conflict. Replaced with explicit per-side utilities (top-0 right-0 bottom-0 left-0 + sm:right-auto sm:bottom-auto). Every Dialog instance now centers correctly on desktop. (Affected 16 dialog consumers.) - interest-documents-tab.tsx — useQuery shared the queryKey ['interests', interestId] with the parent InterestDetail's query but returned a different shape ({ data: ... } envelope vs unwrapped). They clobbered each other's cache on tab mount, degenerating the parent header to "Unknown Client" / "Open" briefly. Unified the queryFn shape so the cache stays consistent. - interest-tabs.tsx — milestone steps now derive done-state from PIPELINE_STAGES.indexOf(currentStage) >= step.advanceStage_idx as well as from the date stamp. Stage truth > date truth. Seeded / imported interests that arrived past `open` without per-step dates now correctly show their milestone steps as checked. - interest-detail.tsx — wires useMobileChrome so the mobile topbar shows the client name instead of the interest UUID. - interest-documents-tab.tsx — empty state restructured to a centered "No documents yet — Generate EOI" CTA card instead of a small primary button floating in the corner. - timeline/route.ts — synthesizes a "Created at <stage>" event when no audit-log rows exist for the interest, so the Activity tab isn't empty for seeded interests. - lead-source-chart.tsx — pie radii switched from fixed 90px/50px to "70%"/"40%" so the pie scales with the container instead of being clipped at narrow widths; reserved 40px for the legend. Visual / clarity: - interest-detail-header.tsx — Won/Lost rendered as branded text buttons on desktop ("Mark won", "Close as lost") and icon-only on mobile via `hidden sm:inline`. Edit/Archive stay icon-only. Reopen promoted to a labeled button when the interest is closed. Added "Last contact Xd ago" to the meta row. - detail-header-strip.tsx — py-4 → py-3 (tighter strip). - interest-tabs.tsx — milestone cards: the next pending milestone gets a brand-blue ring + "NEXT" pill so the user can see at a glance which lifecycle to act on. Its primary action gets the filled button variant. - interest-tabs.tsx — Deposit milestone: invoice flow promoted to primary CTA ("Create deposit invoice"), manual stage advance demoted to a small text link ("Mark received manually"). Reflects the actual recommended path now that recordPayment auto-advances on payment. - inline-editable-field.tsx — pencil affordance shown faintly (opacity-20) at rest so users discover that fields are editable without having to hover-test every label. Lifts to opacity-60 on hover. - constants.ts — STAGE_SHORT_LABELS map for cramped contexts; pipeline-chart.tsx + pipeline-funnel-chart.tsx use them on mobile via useIsMobile, so the rotated 9-stage axis isn't a wall of overlap on a 393px screen. - client-pipeline-summary.tsx — StageStepper rebuilt as a single segmented progress bar instead of 9 micro-dots + connectors that rendered inconsistently at tight widths. Each stage is an equal slice that lights up as the interest reaches it; tooltips on hover give the full stage name. Also dropped a pre-existing dead `br` variable. - dashboard empty states — Lead Source, Revenue Breakdown, Pipeline Funnel, and Recent Activity now have helpful descriptions explaining what populates them, instead of bare "No interests in range". - use-paginated-query.ts — reuses `&` when the endpoint already has `?`, so callers like the documents hub don't generate `…?tab=eoi_queue&signatureOnly=true?page=1&limit=25` (which the API rejected as 400). Caught while testing the now-removed EOI route but applies broadly. tsc clean. vitest 832/832 pass. eslint 0 errors (down from 1 pre-existing) on every file touched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 01:24:15 +02:00
const { data: interest } = useQuery<InterestData>({
queryKey: ['interests', interestId],
fix(ux): batch UX audit fixes across spine pages Comprehensive audit findings rolled up into one pass. Bugs: - dialog.tsx — sm-breakpoint centering classes (sm:left-[50%] / sm:top-[50%]) were being silently stripped by tailwind-merge because the base inset-0 + sm:inset-auto pair counted as a conflict. Replaced with explicit per-side utilities (top-0 right-0 bottom-0 left-0 + sm:right-auto sm:bottom-auto). Every Dialog instance now centers correctly on desktop. (Affected 16 dialog consumers.) - interest-documents-tab.tsx — useQuery shared the queryKey ['interests', interestId] with the parent InterestDetail's query but returned a different shape ({ data: ... } envelope vs unwrapped). They clobbered each other's cache on tab mount, degenerating the parent header to "Unknown Client" / "Open" briefly. Unified the queryFn shape so the cache stays consistent. - interest-tabs.tsx — milestone steps now derive done-state from PIPELINE_STAGES.indexOf(currentStage) >= step.advanceStage_idx as well as from the date stamp. Stage truth > date truth. Seeded / imported interests that arrived past `open` without per-step dates now correctly show their milestone steps as checked. - interest-detail.tsx — wires useMobileChrome so the mobile topbar shows the client name instead of the interest UUID. - interest-documents-tab.tsx — empty state restructured to a centered "No documents yet — Generate EOI" CTA card instead of a small primary button floating in the corner. - timeline/route.ts — synthesizes a "Created at <stage>" event when no audit-log rows exist for the interest, so the Activity tab isn't empty for seeded interests. - lead-source-chart.tsx — pie radii switched from fixed 90px/50px to "70%"/"40%" so the pie scales with the container instead of being clipped at narrow widths; reserved 40px for the legend. Visual / clarity: - interest-detail-header.tsx — Won/Lost rendered as branded text buttons on desktop ("Mark won", "Close as lost") and icon-only on mobile via `hidden sm:inline`. Edit/Archive stay icon-only. Reopen promoted to a labeled button when the interest is closed. Added "Last contact Xd ago" to the meta row. - detail-header-strip.tsx — py-4 → py-3 (tighter strip). - interest-tabs.tsx — milestone cards: the next pending milestone gets a brand-blue ring + "NEXT" pill so the user can see at a glance which lifecycle to act on. Its primary action gets the filled button variant. - interest-tabs.tsx — Deposit milestone: invoice flow promoted to primary CTA ("Create deposit invoice"), manual stage advance demoted to a small text link ("Mark received manually"). Reflects the actual recommended path now that recordPayment auto-advances on payment. - inline-editable-field.tsx — pencil affordance shown faintly (opacity-20) at rest so users discover that fields are editable without having to hover-test every label. Lifts to opacity-60 on hover. - constants.ts — STAGE_SHORT_LABELS map for cramped contexts; pipeline-chart.tsx + pipeline-funnel-chart.tsx use them on mobile via useIsMobile, so the rotated 9-stage axis isn't a wall of overlap on a 393px screen. - client-pipeline-summary.tsx — StageStepper rebuilt as a single segmented progress bar instead of 9 micro-dots + connectors that rendered inconsistently at tight widths. Each stage is an equal slice that lights up as the interest reaches it; tooltips on hover give the full stage name. Also dropped a pre-existing dead `br` variable. - dashboard empty states — Lead Source, Revenue Breakdown, Pipeline Funnel, and Recent Activity now have helpful descriptions explaining what populates them, instead of bare "No interests in range". - use-paginated-query.ts — reuses `&` when the endpoint already has `?`, so callers like the documents hub don't generate `…?tab=eoi_queue&signatureOnly=true?page=1&limit=25` (which the API rejected as 400). Caught while testing the now-removed EOI route but applies broadly. tsc clean. vitest 832/832 pass. eslint 0 errors (down from 1 pre-existing) on every file touched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 01:24:15 +02:00
queryFn: () =>
apiFetch<{ data: InterestData }>(`/api/v1/interests/${interestId}`).then((r) => r.data),
});
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
// Files attach at the client level (the schema has no interest_id
// FK on `files`). For an interest, surface every file that belongs
// to its parent client — covers the realistic case where a rep
// uploaded a passport / scan / photo while working a deal.
// Until the interest record loads we pass a sentinel clientId so the
// server returns empty rather than the unscoped port-wide file list.
const clientId = interest?.clientId ?? '__pending__';
const filesQueryKey = ['files', { clientId }] as const;
const { data: files, isLoading: filesLoading } = usePaginatedQuery<FileRow>({
queryKey: filesQueryKey,
endpoint: `/api/v1/files?clientId=${encodeURIComponent(clientId)}`,
filterDefinitions: [],
});
useRealtimeInvalidation({
'file:uploaded': [filesQueryKey],
'file:updated': [filesQueryKey],
'file:deleted': [filesQueryKey],
});
const handleDownload = async (file: FileRow) => {
try {
const res = await apiFetch<{ data: { url: string; filename: string } }>(
`/api/v1/files/${file.id}/download`,
);
const a = document.createElement('a');
a.href = res.data.url;
a.download = res.data.filename;
a.click();
} catch {
// silent
}
};
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
const handleDelete = async (file: FileRow) => {
if (!confirm(`Delete "${file.filename}"? This cannot be undone.`)) return;
try {
await apiFetch(`/api/v1/files/${file.id}`, { method: 'DELETE' });
queryClient.invalidateQueries({ queryKey: filesQueryKey });
} catch {
// silent
}
};
const hasAttachments = files.length > 0;
return (
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
<div className="space-y-8">
<section className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-sm font-medium text-muted-foreground">Legal documents</h3>
<Button size="sm" variant="outline" onClick={() => setEoiDialogOpen(true)}>
Generate EOI
</Button>
</div>
<DocumentList
interestId={interestId}
emptyState={
<div className="flex flex-col items-center gap-3 rounded-lg border border-dashed border-border bg-muted/20 px-6 py-10 text-center">
<div className="flex size-10 items-center justify-center rounded-full bg-background text-muted-foreground">
<FileSignature className="size-5" />
</div>
<div className="space-y-1">
<p className="text-sm font-medium text-foreground">No documents yet</p>
<p className="text-xs text-muted-foreground">
Generate the EOI to send it for signing in one click.
</p>
</div>
<Button size="sm" onClick={() => setEoiDialogOpen(true)}>
Generate EOI
</Button>
fix(ux): batch UX audit fixes across spine pages Comprehensive audit findings rolled up into one pass. Bugs: - dialog.tsx — sm-breakpoint centering classes (sm:left-[50%] / sm:top-[50%]) were being silently stripped by tailwind-merge because the base inset-0 + sm:inset-auto pair counted as a conflict. Replaced with explicit per-side utilities (top-0 right-0 bottom-0 left-0 + sm:right-auto sm:bottom-auto). Every Dialog instance now centers correctly on desktop. (Affected 16 dialog consumers.) - interest-documents-tab.tsx — useQuery shared the queryKey ['interests', interestId] with the parent InterestDetail's query but returned a different shape ({ data: ... } envelope vs unwrapped). They clobbered each other's cache on tab mount, degenerating the parent header to "Unknown Client" / "Open" briefly. Unified the queryFn shape so the cache stays consistent. - interest-tabs.tsx — milestone steps now derive done-state from PIPELINE_STAGES.indexOf(currentStage) >= step.advanceStage_idx as well as from the date stamp. Stage truth > date truth. Seeded / imported interests that arrived past `open` without per-step dates now correctly show their milestone steps as checked. - interest-detail.tsx — wires useMobileChrome so the mobile topbar shows the client name instead of the interest UUID. - interest-documents-tab.tsx — empty state restructured to a centered "No documents yet — Generate EOI" CTA card instead of a small primary button floating in the corner. - timeline/route.ts — synthesizes a "Created at <stage>" event when no audit-log rows exist for the interest, so the Activity tab isn't empty for seeded interests. - lead-source-chart.tsx — pie radii switched from fixed 90px/50px to "70%"/"40%" so the pie scales with the container instead of being clipped at narrow widths; reserved 40px for the legend. Visual / clarity: - interest-detail-header.tsx — Won/Lost rendered as branded text buttons on desktop ("Mark won", "Close as lost") and icon-only on mobile via `hidden sm:inline`. Edit/Archive stay icon-only. Reopen promoted to a labeled button when the interest is closed. Added "Last contact Xd ago" to the meta row. - detail-header-strip.tsx — py-4 → py-3 (tighter strip). - interest-tabs.tsx — milestone cards: the next pending milestone gets a brand-blue ring + "NEXT" pill so the user can see at a glance which lifecycle to act on. Its primary action gets the filled button variant. - interest-tabs.tsx — Deposit milestone: invoice flow promoted to primary CTA ("Create deposit invoice"), manual stage advance demoted to a small text link ("Mark received manually"). Reflects the actual recommended path now that recordPayment auto-advances on payment. - inline-editable-field.tsx — pencil affordance shown faintly (opacity-20) at rest so users discover that fields are editable without having to hover-test every label. Lifts to opacity-60 on hover. - constants.ts — STAGE_SHORT_LABELS map for cramped contexts; pipeline-chart.tsx + pipeline-funnel-chart.tsx use them on mobile via useIsMobile, so the rotated 9-stage axis isn't a wall of overlap on a 393px screen. - client-pipeline-summary.tsx — StageStepper rebuilt as a single segmented progress bar instead of 9 micro-dots + connectors that rendered inconsistently at tight widths. Each stage is an equal slice that lights up as the interest reaches it; tooltips on hover give the full stage name. Also dropped a pre-existing dead `br` variable. - dashboard empty states — Lead Source, Revenue Breakdown, Pipeline Funnel, and Recent Activity now have helpful descriptions explaining what populates them, instead of bare "No interests in range". - use-paginated-query.ts — reuses `&` when the endpoint already has `?`, so callers like the documents hub don't generate `…?tab=eoi_queue&signatureOnly=true?page=1&limit=25` (which the API rejected as 400). Caught while testing the now-removed EOI route but applies broadly. tsc clean. vitest 832/832 pass. eslint 0 errors (down from 1 pre-existing) on every file touched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 01:24:15 +02:00
</div>
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
}
/>
</section>
<section className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-sm font-medium text-muted-foreground">Attachments</h3>
{hasAttachments ? (
<span className="text-xs text-muted-foreground">
{files.length} file{files.length === 1 ? '' : 's'}
</span>
) : null}
</div>
<PermissionGate resource="files" action="upload">
{interest?.clientId ? (
<FileUploadZone
entityType="client"
entityId={interest.clientId}
onUploadComplete={() => {
queryClient.invalidateQueries({ queryKey: filesQueryKey });
}}
/>
) : null}
</PermissionGate>
{hasAttachments ? (
<FileGrid
files={files}
onDownload={handleDownload}
onPreview={setPreviewFile}
onRename={() => {}}
onDelete={handleDelete}
isLoading={filesLoading}
/>
) : null}
</section>
<EoiGenerateDialog
interestId={interestId}
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
clientId={interest?.clientId ?? null}
open={eoiDialogOpen}
onOpenChange={setEoiDialogOpen}
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
/>
<FilePreviewDialog
open={!!previewFile}
onOpenChange={(open) => !open && setPreviewFile(null)}
fileId={previewFile?.id}
fileName={previewFile?.filename}
mimeType={previewFile?.mimeType ?? undefined}
/>
</div>
);
}