feat(uat-polish): live-UAT round — dialog widths, recommender polish, inline create, tenancy + notes plumbing
Compendium of polish + small-fix work captured during the 2026-05-26
live UAT session. Every change has a corresponding entry in
docs/superpowers/audits/active-uat.md with file:line evidence + root
cause + alternatives considered.
Dialog primitive width
- DialogContent default bumped from sm:max-w-lg (512px) to
sm:max-w-xl + lg:max-w-3xl so every consumer gets a sane desktop
default. Confirm dialogs override DOWN, content-heavy dialogs
override UP.
- FilePreviewDialog full-viewport via w-[min(95vw,1400px)] +
h-[85vh] so PDFs render at usable width on real desktops.
Recommender card
- Heat badge now a Popover with the score (X/100), the formula in
plain English, the four component breakdowns (recency / furthest
stage / interest count / EOI count), and a pointer to the admin
weight tuning page.
- Area letter span dropped from the card header - mooring number
already prefixes it.
- BerthRecommenderPanel + the dedicated "Berth Recommendations" tab
both hidden when interest.desiredLengthFt is null. The empty
guidance card was reading as noise. interest-tabs.tsx computes
hasDesiredDims once and gates the inline mount + tab strip
spread off it.
BerthPicker
- Drop area suffix from row labels. Mooring number already carries
the area letter prefix; group heading conveys the same context.
Same fix flows to every BerthPicker consumer (tenancy
create/renew/transfer, interest form, linked-berths picker).
CreateDocumentWizard
- DOCUMENT_TYPE_LABELS constant added to constants.ts. Wizard reads
from the map instead of naive replace(/_/g, ' '): "EOI",
"Contract", "NDA", "Reservation Agreement", "Other".
- "Other" option surfaces a hint pointing the rep at the Title
field so they describe what the doc actually is.
InterestForm inline client + yacht create
- ClientForm gains an onCreated(clientId) callback. Mutation
returns { id } in create mode so onSuccess can forward.
- InterestForm renders an "Add new" Button next to the Client label
(create mode only - hidden on edit), opens ClientForm, auto-
selects the new client into the draft. Mirrors the existing
inline yacht-create pattern.
- Reset path includes source: 'manual' alongside the other create-
mode defaults; the manual flow was dropping back to a blank
source dropdown on reopen.
Tenancy list
- ClientTenanciesTab activeTenancies query now includes status
IN ('pending', 'active'). Was filtering to active-only; pending
rows from manual create + webhook auto-create were invisible on
the client detail's Tenancies tab.
- TenancyList rows are now keyboard- and click-navigable to the
tenancy detail page (Enter/Space included). Inner links + buttons
stop propagation so per-cell navigation works.
NotesList source badge
- Aggregated-mode source badge ("Yacht / Test Yacht") is now a Link
to the source entity's detail page. New sourceLinkFor helper
centralises the URL mapping across clients/companies/yachts/
interests + residential variants.
Yacht transfer audit log
- transferOwnership emits a distinct 'transfer' AuditAction (added
to AuditAction union in src/lib/audit.ts) with old/new owner
names resolved at write time. EntityActivityFeed renders
"Matt transferred owner to Jane Smith" instead of "Matt updated
this record." formatValueForField unwraps the { name } shape so
the audit_logs Record<string, unknown> typing stays clean.
- yacht-transfer-dialog copy: dropped "atomic" jargon. Reads "The
change is logged in the audit history" instead.
Companies autocomplete
- /api/v1/companies/autocomplete now returns the 10 most-recently-
updated companies when the query string is empty. Was returning
[]. CompanyPicker popover opens with results to scan instead of a
blank dropdown.
DocumentsHub FlatFolderListing
- Uploaded files (the files table) now merge into the documents
table view via a parallel /api/v1/files?folderId=X query +
client-side merge into a unified row list. listFiles service
honours the folderId filter that was already accepted by the
validator. New renderFileRow renders file rows with an "Uploaded
file" type pill + "Stored" status pill, links the filename to
the download URL. Existing FolderDropZone invalidation covers
the new query, so drag-drop and New-document-menu uploads
refresh the list without a page reload.
- FlatFolderListing wrapped in a vertically-spaced container so
subfolders / search row / list have consistent gap.
- Per-row chevron only renders when totalSigners > 0; empty
placeholder column kept so grid alignment doesn't jump.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,11 @@ interface ClientFormProps {
|
||||
* or opening the create-interest dialog pre-filled with that
|
||||
* clientId. Skipped in edit mode. */
|
||||
onUseExistingClient?: (clientId: string) => void;
|
||||
/** Optional callback fired with the newly-created client's id after a
|
||||
* successful create. Lets a parent flow (e.g. the new-interest drawer)
|
||||
* chain a "use the client I just created" follow-up without making the
|
||||
* rep re-select. Not called in edit mode. */
|
||||
onCreated?: (clientId: string) => void;
|
||||
/** Optional initial values for the create flow - used by the
|
||||
* inquiry-inbox "Convert to client" triage step (P-4.5) so the rep
|
||||
* doesn't retype values they just read in the inbox. The
|
||||
@@ -84,6 +89,7 @@ export function ClientForm({
|
||||
onOpenChange,
|
||||
client,
|
||||
onUseExistingClient,
|
||||
onCreated,
|
||||
prefill,
|
||||
}: ClientFormProps) {
|
||||
const queryClient = useQueryClient();
|
||||
@@ -253,6 +259,7 @@ export function ClientForm({
|
||||
body: { tagIds: tIds },
|
||||
});
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
const res = await apiFetch<{ data: { id: string } }>('/api/v1/clients', {
|
||||
method: 'POST',
|
||||
@@ -287,13 +294,19 @@ export function ClientForm({
|
||||
);
|
||||
}
|
||||
}
|
||||
return { id: res.data.id };
|
||||
}
|
||||
},
|
||||
onSuccess: () => {
|
||||
onSuccess: (result) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['clients'] });
|
||||
// M-U10: confirm the write landed. Without this the rep closes
|
||||
// the sheet not sure whether the create/edit actually saved.
|
||||
toast.success(isEdit ? 'Client updated' : 'Client created');
|
||||
// Parent flow gets a chance to chain off the new id (e.g. the
|
||||
// new-interest drawer auto-selects the freshly-created client).
|
||||
if (result && 'id' in result && result.id && onCreated) {
|
||||
onCreated(result.id);
|
||||
}
|
||||
onOpenChange(false);
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user