From 05e727f4621e6de49b5eae5b0f1f61d394d3b3f0 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 21 May 2026 18:35:52 +0200 Subject: [PATCH] feat(uat-batch-18): interest-berths defaults + a11y loading/hint fixes - `addInterestBerth` insert-time defaults now match the locked multi-berth EOI UX (queue B2): is_in_eoi_bundle: true (was false) is_specific_interest: matches `isPrimary` (was always true) This means a newly-linked berth is covered by the EOI signature by default but the public map only shows the primary as "Under Offer" until the rep marks others isSpecificInterest. The two existing integration tests pass explicit values so they're unaffected. - A11y: `set-password` form's password-requirements hint linked via aria-describedby so SR users hear the rules on focus. - A11y: Loading fallbacks on set-password / portal/activate / supplemental-info wrapped in role="status" aria-live="polite" with sr-only "Loading" copy where only a spinner was visible. tsc clean. 1419/1419 vitest pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/app/(auth)/set-password/page.tsx | 9 +++++++-- src/app/(portal)/portal/activate/page.tsx | 6 +++++- .../public/supplemental-info/[token]/page.tsx | 3 ++- src/lib/services/interest-berths.service.ts | 17 ++++++++++++++--- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/app/(auth)/set-password/page.tsx b/src/app/(auth)/set-password/page.tsx index 28f10a22..feff73b9 100644 --- a/src/app/(auth)/set-password/page.tsx +++ b/src/app/(auth)/set-password/page.tsx @@ -104,7 +104,9 @@ function SetPasswordInner() { if (token === null) { return ( -
Loading…
+
+ Loading… +
); } @@ -141,10 +143,13 @@ function SetPasswordInner() { type="password" autoComplete="new-password" disabled={isLoading} + aria-describedby="password-hint" className={cn(errors.password && 'border-destructive focus-visible:ring-destructive')} {...register('password')} /> -

At least {MIN_LENGTH} characters.

+

+ At least {MIN_LENGTH} characters. +

{errors.password &&

{errors.password.message}

} diff --git a/src/app/(portal)/portal/activate/page.tsx b/src/app/(portal)/portal/activate/page.tsx index e42f3234..80f209e6 100644 --- a/src/app/(portal)/portal/activate/page.tsx +++ b/src/app/(portal)/portal/activate/page.tsx @@ -6,7 +6,11 @@ export default function PortalActivatePage() { return ( +
Loading…
} diff --git a/src/app/public/supplemental-info/[token]/page.tsx b/src/app/public/supplemental-info/[token]/page.tsx index 2f477dcb..238e008b 100644 --- a/src/app/public/supplemental-info/[token]/page.tsx +++ b/src/app/public/supplemental-info/[token]/page.tsx @@ -140,8 +140,9 @@ export default function SupplementalInfoPage({ params }: PageProps) { if (loading) { return ( -
+
+ Loading
); diff --git a/src/lib/services/interest-berths.service.ts b/src/lib/services/interest-berths.service.ts index 29d67496..62a4568f 100644 --- a/src/lib/services/interest-berths.service.ts +++ b/src/lib/services/interest-berths.service.ts @@ -272,14 +272,25 @@ export async function upsertInterestBerthTx( } } + // EOI bundle UX (locked 2026-05-18): a deal's EOI typically covers + // every berth linked to the interest, but only the rep's "main" + // berth (the primary) should show "Under Offer" on the public map. + // The defaults below encode that workflow so reps don't have to + // tick boxes for the common case: + // • `is_in_eoi_bundle` defaults to TRUE for every newly-linked + // berth (rep unticks for the rare carve-out). + // • `is_specific_interest` defaults to TRUE only on the primary; + // non-primary rows default to FALSE so the public map doesn't + // light up extra berths. + const isPrimary = opts.isPrimary ?? false; const [row] = await tx .insert(interestBerths) .values({ interestId, berthId, - isPrimary: opts.isPrimary ?? false, - isSpecificInterest: opts.isSpecificInterest ?? true, - isInEoiBundle: opts.isInEoiBundle ?? false, + isPrimary, + isSpecificInterest: opts.isSpecificInterest ?? isPrimary, + isInEoiBundle: opts.isInEoiBundle ?? true, addedBy: opts.addedBy, notes: opts.notes, eoiBypassReason: setForUpdate.eoiBypassReason ?? null,