Files
pn-new-crm/src/components/clients/client-tabs.tsx
Matt 67d7e6e3d5
Some checks failed
Build & Push Docker Images / build-and-push (push) Has been cancelled
Build & Push Docker Images / deploy (push) Has been cancelled
Build & Push Docker Images / lint (push) Has been cancelled
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00

209 lines
5.9 KiB
TypeScript

'use client';
import type { DetailTab } from '@/components/shared/detail-layout';
import { NotesList } from '@/components/shared/notes-list';
interface ClientTabsOptions {
clientId: string;
currentUserId?: string;
client: {
fullName: string;
companyName?: string | null;
nationality?: string | null;
isProxy?: boolean;
proxyType?: string | null;
actualOwnerName?: string | null;
yachtName?: string | null;
yachtLengthFt?: string | null;
yachtWidthFt?: string | null;
yachtDraftFt?: string | null;
berthSizeDesired?: string | null;
preferredContactMethod?: string | null;
preferredLanguage?: string | null;
timezone?: string | null;
source?: string | null;
sourceDetails?: string | null;
contacts?: Array<{
id: string;
channel: string;
value: string;
label?: string | null;
isPrimary: boolean;
}>;
};
}
function InfoRow({ label, value }: { label: string; value?: string | null }) {
if (!value) return null;
return (
<div className="flex gap-2 py-1.5 border-b last:border-0">
<dt className="w-40 shrink-0 text-sm text-muted-foreground">{label}</dt>
<dd className="text-sm">{value}</dd>
</div>
);
}
function OverviewTab({ client }: { client: ClientTabsOptions['client'] }) {
return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Personal Info */}
<div className="space-y-1">
<h3 className="text-sm font-medium mb-2">Personal Information</h3>
<dl>
<InfoRow label="Full Name" value={client.fullName} />
<InfoRow label="Company" value={client.companyName} />
<InfoRow label="Nationality" value={client.nationality} />
<InfoRow label="Preferred Language" value={client.preferredLanguage} />
<InfoRow label="Timezone" value={client.timezone} />
<InfoRow
label="Preferred Contact"
value={client.preferredContactMethod}
/>
</dl>
</div>
{/* Contacts */}
<div className="space-y-1">
<h3 className="text-sm font-medium mb-2">Contact Details</h3>
{client.contacts && client.contacts.length > 0 ? (
<div className="space-y-2">
{client.contacts.map((c) => (
<div
key={c.id}
className="flex items-center gap-2 p-2 rounded-lg border bg-muted/30 text-sm"
>
<span className="capitalize text-muted-foreground w-20 shrink-0">
{c.channel}
</span>
<span className="flex-1">{c.value}</span>
{c.label && (
<span className="text-xs text-muted-foreground capitalize">
{c.label}
</span>
)}
{c.isPrimary && (
<span className="text-xs font-medium text-primary">Primary</span>
)}
</div>
))}
</div>
) : (
<p className="text-sm text-muted-foreground">No contacts added</p>
)}
</div>
{/* Yacht Details */}
{(client.yachtName ||
client.yachtLengthFt ||
client.berthSizeDesired) && (
<div className="space-y-1">
<h3 className="text-sm font-medium mb-2">Yacht Details</h3>
<dl>
<InfoRow label="Yacht Name" value={client.yachtName} />
<InfoRow
label="Length"
value={
client.yachtLengthFt
? `${client.yachtLengthFt} ft`
: undefined
}
/>
<InfoRow
label="Width"
value={
client.yachtWidthFt ? `${client.yachtWidthFt} ft` : undefined
}
/>
<InfoRow
label="Draft"
value={
client.yachtDraftFt
? `${client.yachtDraftFt} ft`
: undefined
}
/>
<InfoRow label="Berth Size Desired" value={client.berthSizeDesired} />
</dl>
</div>
)}
{/* Source */}
{(client.source || client.sourceDetails) && (
<div className="space-y-1">
<h3 className="text-sm font-medium mb-2">Source</h3>
<dl>
<InfoRow label="Source" value={client.source} />
<InfoRow label="Source Details" value={client.sourceDetails} />
</dl>
</div>
)}
{/* Proxy Info */}
{client.isProxy && (
<div className="space-y-1">
<h3 className="text-sm font-medium mb-2">Proxy Information</h3>
<dl>
<InfoRow
label="Proxy Type"
value={client.proxyType?.replace('_', ' ')}
/>
<InfoRow label="Actual Owner" value={client.actualOwnerName} />
</dl>
</div>
)}
</div>
);
}
export function getClientTabs({
clientId,
currentUserId,
client,
}: ClientTabsOptions): DetailTab[] {
return [
{
id: 'overview',
label: 'Overview',
content: <OverviewTab client={client} />,
},
{
id: 'interests',
label: 'Interests',
content: (
<div className="text-center py-12 text-muted-foreground">
<p>Interests will appear here once created.</p>
</div>
),
},
{
id: 'notes',
label: 'Notes',
content: (
<NotesList
entityType="clients"
entityId={clientId}
currentUserId={currentUserId}
/>
),
},
{
id: 'files',
label: 'Files',
content: (
<div className="text-center py-12 text-muted-foreground">
<p>File attachments coming soon.</p>
</div>
),
},
{
id: 'activity',
label: 'Activity',
content: (
<div className="text-center py-12 text-muted-foreground">
<p>Activity log coming soon.</p>
</div>
),
},
];
}