+
+
- {/* Contacts */}
-
-
Contact Details
-
-
+
+ {/* Personal Info */}
+
+
Personal Information
+
+
+
+
+
+ {
+ await mutation.mutateAsync({ nationalityIso: iso });
+ }}
+ data-testid="client-nationality-inline"
+ />
+
+
+ {
+ await mutation.mutateAsync({ timezone: tz });
+ }}
+ data-testid="client-timezone-inline"
+ />
+
+
+
+
+
+
- {/* Source */}
-
-
Source
-
-
-
-
-
-
-
-
-
+ {/* Contacts */}
+
+
Contact Details
+
+
- {/* Tags */}
-
-
Tags
-
+ {/* Source */}
+
+
Source
+
+
+
+
+
+
+
+
+
+
+ {/* Tags */}
+
+
Tags
+
+
);
@@ -219,6 +221,11 @@ export function getClientTabs({ clientId, currentUserId, client }: ClientTabsOpt
label: 'Overview',
content:
,
},
+ {
+ id: 'interests',
+ label: 'Interests',
+ content:
,
+ },
{
id: 'yachts',
label: 'Yachts',
@@ -251,15 +258,6 @@ export function getClientTabs({ clientId, currentUserId, client }: ClientTabsOpt
/>
),
},
- {
- id: 'interests',
- label: 'Interests',
- content: (
-
-
Interests will appear here once created.
-
- ),
- },
{
id: 'notes',
label: 'Notes',
diff --git a/src/lib/services/clients.service.ts b/src/lib/services/clients.service.ts
index 9825983..7bfc5ac 100644
--- a/src/lib/services/clients.service.ts
+++ b/src/lib/services/clients.service.ts
@@ -1,4 +1,4 @@
-import { and, count, eq, ilike, inArray, isNull } from 'drizzle-orm';
+import { and, count, desc, eq, ilike, inArray, isNull } from 'drizzle-orm';
import { db } from '@/lib/db';
import {
@@ -11,6 +11,8 @@ import {
import { companies, companyMemberships } from '@/lib/db/schema/companies';
import { yachts } from '@/lib/db/schema/yachts';
import { berthReservations } from '@/lib/db/schema/reservations';
+import { interests } from '@/lib/db/schema/interests';
+import { berths } from '@/lib/db/schema/berths';
import { tags } from '@/lib/db/schema/system';
import { createAuditLog, type AuditMeta } from '@/lib/audit';
import { NotFoundError, ValidationError } from '@/lib/errors';
@@ -81,7 +83,7 @@ export async function listClients(portId: string, query: ListClientsInput) {
const ids = result.data.map((r) => r.id);
- const [yachtCounts, companyCounts] = await Promise.all([
+ const [yachtCounts, companyCounts, interestRows, interestCounts] = await Promise.all([
db
.select({ ownerId: yachts.currentOwnerId, count: count() })
.from(yachts)
@@ -99,18 +101,67 @@ export async function listClients(portId: string, query: ListClientsInput) {
.from(companyMemberships)
.where(and(inArray(companyMemberships.clientId, ids), isNull(companyMemberships.endDate)))
.groupBy(companyMemberships.clientId),
+ db
+ .select({
+ clientId: interests.clientId,
+ pipelineStage: interests.pipelineStage,
+ updatedAt: interests.updatedAt,
+ mooringNumber: berths.mooringNumber,
+ })
+ .from(interests)
+ .leftJoin(berths, eq(berths.id, interests.berthId))
+ .where(
+ and(
+ eq(interests.portId, portId),
+ inArray(interests.clientId, ids),
+ isNull(interests.archivedAt),
+ ),
+ )
+ .orderBy(desc(interests.updatedAt)),
+ db
+ .select({ clientId: interests.clientId, count: count() })
+ .from(interests)
+ .where(
+ and(
+ eq(interests.portId, portId),
+ inArray(interests.clientId, ids),
+ isNull(interests.archivedAt),
+ ),
+ )
+ .groupBy(interests.clientId),
]);
const yachtCountMap = new Map(yachtCounts.map((r) => [r.ownerId, r.count]));
const companyCountMap = new Map(companyCounts.map((r) => [r.clientId, r.count]));
+ const interestCountMap = new Map(interestCounts.map((r) => [r.clientId, r.count]));
+ // interestRows is sorted desc by updatedAt; first hit per clientId is the latest.
+ const latestInterestMap = new Map
();
+ for (const row of interestRows) {
+ if (!latestInterestMap.has(row.clientId)) {
+ latestInterestMap.set(row.clientId, {
+ stage: row.pipelineStage,
+ mooringNumber: row.mooringNumber,
+ });
+ }
+ }
return {
...result,
- data: result.data.map((row) => ({
- ...row,
- yachtCount: yachtCountMap.get(row.id) ?? 0,
- companyCount: companyCountMap.get(row.id) ?? 0,
- })),
+ data: result.data.map((row) => {
+ const latest = latestInterestMap.get(row.id);
+ return {
+ ...row,
+ yachtCount: yachtCountMap.get(row.id) ?? 0,
+ companyCount: companyCountMap.get(row.id) ?? 0,
+ interestCount: interestCountMap.get(row.id) ?? 0,
+ latestInterest: latest
+ ? {
+ stage: latest.stage,
+ mooringNumber: latest.mooringNumber,
+ }
+ : null,
+ };
+ }),
};
}