'use client'; import { useState } from 'react'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { Pencil, Archive, RotateCcw, Trophy, XCircle, RefreshCcw } from 'lucide-react'; import Link from 'next/link'; import { Badge } from '@/components/ui/badge'; import { TagBadge } from '@/components/shared/tag-badge'; import { ArchiveConfirmDialog } from '@/components/shared/archive-confirm-dialog'; import { DetailHeaderStrip } from '@/components/shared/detail-header-strip'; import { PermissionGate } from '@/components/shared/permission-gate'; import { InterestForm } from '@/components/interests/interest-form'; import { InlineStagePicker } from '@/components/interests/inline-stage-picker'; import { InterestOutcomeDialog } from '@/components/interests/interest-outcome-dialog'; import { apiFetch } from '@/lib/api/client'; import { cn } from '@/lib/utils'; const OUTCOME_BADGE: Record = { won: { label: 'Won', className: 'bg-emerald-100 text-emerald-700' }, lost_other_marina: { label: 'Lost — other marina', className: 'bg-rose-100 text-rose-700' }, lost_unqualified: { label: 'Lost — unqualified', className: 'bg-rose-100 text-rose-700' }, lost_no_response: { label: 'Lost — no response', className: 'bg-rose-100 text-rose-700' }, cancelled: { label: 'Cancelled', className: 'bg-slate-200 text-slate-700' }, }; const CATEGORY_LABELS: Record = { general_interest: 'General', specific_qualified: 'Specific Qualified', hot_lead: 'Hot Lead', }; interface InterestDetailHeaderProps { portSlug: string; interest: { id: string; clientId: string; clientName: string | null; berthId: string | null; berthMooringNumber: string | null; pipelineStage: string; leadCategory: string | null; source: string | null; notes: string | null; reminderEnabled: boolean; reminderDays: number | null; archivedAt: string | null; outcome?: string | null; outcomeReason?: string | null; tags?: Array<{ id: string; name: string; color: string }>; }; } export function InterestDetailHeader({ portSlug, interest }: InterestDetailHeaderProps) { const queryClient = useQueryClient(); const [editOpen, setEditOpen] = useState(false); const [archiveOpen, setArchiveOpen] = useState(false); const [outcomeDialog, setOutcomeDialog] = useState(null); const isArchived = !!interest.archivedAt; const outcomeBadge = interest.outcome ? OUTCOME_BADGE[interest.outcome] : null; const isClosed = !!interest.outcome; const reopenMutation = useMutation({ mutationFn: () => apiFetch(`/api/v1/interests/${interest.id}/outcome`, { method: 'DELETE', body: {} }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['interests', interest.id] }); queryClient.invalidateQueries({ queryKey: ['interests'] }); }, }); const archiveMutation = useMutation({ mutationFn: () => apiFetch(`/api/v1/interests/${interest.id}`, { method: 'DELETE' }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['interests', interest.id] }); queryClient.invalidateQueries({ queryKey: ['interests'] }); setArchiveOpen(false); }, }); const restoreMutation = useMutation({ mutationFn: () => apiFetch(`/api/v1/interests/${interest.id}/restore`, { method: 'POST' }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['interests', interest.id] }); queryClient.invalidateQueries({ queryKey: ['interests'] }); setArchiveOpen(false); }, }); const meta: Array<{ key: string; node: React.ReactNode }> = []; if (interest.berthMooringNumber) { meta.push({ key: 'berth', node: ( Berth {interest.berthMooringNumber} ), }); } if (interest.leadCategory) { meta.push({ key: 'cat', node: {CATEGORY_LABELS[interest.leadCategory] ?? interest.leadCategory}, }); } if (interest.source) { meta.push({ key: 'src', node: {interest.source}, }); } return ( <>

{interest.clientName ?? 'Unknown Client'}

{isArchived && ( Archived )} {outcomeBadge ? ( {outcomeBadge.label} ) : ( {interest.pipelineStage} } > )}
{meta.length > 0 ? (

{meta.map((m, i) => ( {i > 0 ? ( · ) : null} {m.node} ))}

) : null} {interest.tags && interest.tags.length > 0 && (
{interest.tags.map((tag) => ( ))}
)}
{/* Top-right icon-only actions — no stacking, no labels eating room. */}
{isClosed ? ( ) : ( <> )}
{outcomeDialog && ( !open && setOutcomeDialog(null)} /> )} [0]['interest']} /> { if (isArchived) { restoreMutation.mutate(); } else { archiveMutation.mutate(); } }} isLoading={archiveMutation.isPending || restoreMutation.isPending} /> ); }