feat(notifications): include berth-range suffix in stage-change titles
Stage-change notification titles previously read "Acme Corp moved to
Reservation" with no context on which berths the deal covers. For
multi-berth deals the rep had to drill into the interest to see what
moved. With multiple deals in flight per client the bell tray became
ambiguous.
Switch the title-build path from `getPrimaryBerth` (single-row) to
`listBerthsForInterest` (full set) and append a compact suffix via
`formatBerthRange()`:
Acme Corp moved to Reservation [A1-A3, B5]
Falls back to plain "<subject> moved to <stage>" when the interest
has no linked berths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,10 +24,12 @@ import { logger } from '@/lib/logger';
|
|||||||
import {
|
import {
|
||||||
getPrimaryBerth,
|
getPrimaryBerth,
|
||||||
getPrimaryBerthsForInterests,
|
getPrimaryBerthsForInterests,
|
||||||
|
listBerthsForInterest,
|
||||||
removeInterestBerth,
|
removeInterestBerth,
|
||||||
upsertInterestBerth,
|
upsertInterestBerth,
|
||||||
upsertInterestBerthTx,
|
upsertInterestBerthTx,
|
||||||
} from '@/lib/services/interest-berths.service';
|
} from '@/lib/services/interest-berths.service';
|
||||||
|
import { formatBerthRange } from '@/lib/templates/berth-range';
|
||||||
import { buildListQuery } from '@/lib/db/query-builder';
|
import { buildListQuery } from '@/lib/db/query-builder';
|
||||||
import { diffEntity } from '@/lib/entity-diff';
|
import { diffEntity } from '@/lib/entity-diff';
|
||||||
import { softDelete, restore, withTransaction } from '@/lib/db/utils';
|
import { softDelete, restore, withTransaction } from '@/lib/db/utils';
|
||||||
@@ -1041,17 +1043,22 @@ export async function changeInterestStage(
|
|||||||
// canonical STAGE_LABELS dictionary so "deposit_10pct" reads as
|
// canonical STAGE_LABELS dictionary so "deposit_10pct" reads as
|
||||||
// "10% Deposit" everywhere.
|
// "10% Deposit" everywhere.
|
||||||
void (async () => {
|
void (async () => {
|
||||||
const [{ createNotification }, clientRow, primaryBerth] = await Promise.all([
|
const [{ createNotification }, clientRow, allBerths] = await Promise.all([
|
||||||
import('@/lib/services/notifications.service'),
|
import('@/lib/services/notifications.service'),
|
||||||
db.query.clients.findFirst({
|
db.query.clients.findFirst({
|
||||||
where: eq(clients.id, existing.clientId),
|
where: eq(clients.id, existing.clientId),
|
||||||
columns: { fullName: true },
|
columns: { fullName: true },
|
||||||
}),
|
}),
|
||||||
getPrimaryBerth(id).catch(() => null),
|
listBerthsForInterest(id).catch(
|
||||||
|
() => [] as Awaited<ReturnType<typeof listBerthsForInterest>>,
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
|
const primaryBerth = allBerths[0] ?? null;
|
||||||
|
const moorings = allBerths.map((b) => b.mooringNumber).filter((m): m is string => Boolean(m));
|
||||||
|
const berthSuffix = moorings.length > 0 ? ` [${formatBerthRange(moorings)}]` : '';
|
||||||
const subject =
|
const subject =
|
||||||
clientRow?.fullName ??
|
clientRow?.fullName ??
|
||||||
(primaryBerth ? `Berth ${primaryBerth.mooringNumber}` : 'this interest');
|
(primaryBerth?.mooringNumber ? `Berth ${primaryBerth.mooringNumber}` : 'this interest');
|
||||||
const fromLabel = oldStage
|
const fromLabel = oldStage
|
||||||
? (STAGE_LABELS[oldStage as PipelineStage] ?? oldStage.replace(/_/g, ' '))
|
? (STAGE_LABELS[oldStage as PipelineStage] ?? oldStage.replace(/_/g, ' '))
|
||||||
: 'unknown';
|
: 'unknown';
|
||||||
@@ -1061,7 +1068,7 @@ export async function changeInterestStage(
|
|||||||
portId,
|
portId,
|
||||||
userId: meta.userId,
|
userId: meta.userId,
|
||||||
type: 'interest_stage_changed',
|
type: 'interest_stage_changed',
|
||||||
title: `${subject} moved to ${toLabel}`,
|
title: `${subject} moved to ${toLabel}${berthSuffix}`,
|
||||||
description: `Stage changed from ${fromLabel} to ${toLabel}.`,
|
description: `Stage changed from ${fromLabel} to ${toLabel}.`,
|
||||||
link: `/interests/${id}`,
|
link: `/interests/${id}`,
|
||||||
entityType: 'interest',
|
entityType: 'interest',
|
||||||
|
|||||||
Reference in New Issue
Block a user