Replaces every em-dash and en-dash with regular ASCII hyphens across comments, JSX strings, and dev-facing logs. Mostly cosmetic but stops the inconsistent mix that crept in over the last few months (some files used em-dashes in comments, others didn't, some used both). Bundles two small dashboard-layout tweaks that touch a couple of already-modified files: - (dashboard)/layout.tsx main padding goes from p-6 to pt-3 px-6 pb-6 so page content sits closer to the topbar. - Sidebar now receives the ports list it needs for the footer port switcher. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
86 lines
3.0 KiB
TypeScript
86 lines
3.0 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
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';
|
|
import { apiFetch } from '@/lib/api/client';
|
|
|
|
interface InterestDocumentsTabProps {
|
|
interestId: string;
|
|
}
|
|
|
|
interface InterestData {
|
|
id: string;
|
|
yachtId?: string | null;
|
|
berthId?: string | null;
|
|
clientName?: string | null;
|
|
/** Surfaced by getInterestById for the EOI prerequisites checklist. */
|
|
clientPrimaryEmail?: string | null;
|
|
clientHasAddress?: boolean;
|
|
}
|
|
|
|
export function InterestDocumentsTab({ interestId }: InterestDocumentsTabProps) {
|
|
const [eoiDialogOpen, setEoiDialogOpen] = useState(false);
|
|
|
|
// Same query key + queryFn shape as InterestDetail's parent query, so the
|
|
// cache is consistent. (Mismatched shapes on the same key clobber each other
|
|
// and the parent header degenerates to "Unknown Client".)
|
|
const { data: interest } = useQuery<InterestData>({
|
|
queryKey: ['interests', interestId],
|
|
queryFn: () =>
|
|
apiFetch<{ data: InterestData }>(`/api/v1/interests/${interestId}`).then((r) => r.data),
|
|
});
|
|
|
|
const prerequisites = {
|
|
// Required (EOI Section 2 - top paragraph): name, address, email.
|
|
hasName: Boolean(interest?.clientName),
|
|
hasEmail: Boolean(interest?.clientPrimaryEmail),
|
|
hasAddress: Boolean(interest?.clientHasAddress),
|
|
// Optional (EOI Section 3): yacht + berth. Render blank when absent.
|
|
hasYacht: Boolean(interest?.yachtId),
|
|
hasBerth: Boolean(interest?.berthId),
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<h3 className="text-sm font-medium text-muted-foreground">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>
|
|
</div>
|
|
}
|
|
/>
|
|
|
|
<EoiGenerateDialog
|
|
interestId={interestId}
|
|
open={eoiDialogOpen}
|
|
onOpenChange={setEoiDialogOpen}
|
|
prerequisites={prerequisites}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|