chore(autonomous-session): consolidate uncommitted work from prior session

Bundles the prior autonomous-session output that was sitting unstaged:

- Em-dash sweep across src/ + tests/ (en-dash/em-dash to hyphen, ~2280 instances)
- country-flag-icons rollout (CountryFlag component, replaces emoji glyphs that
  never rendered on Windows; lazy-loads the 3x2 SVG index as a single chunk
  after the per-subpath dynamic-import approach silently failed in webpack)
- Admin IA Phase 1+2: 7-domain regroup, 41 to 38 pages, /admin/berths index,
  redirects (ocr to ai, reports to dashboard, invitations to users),
  docs/admin-ia-proposal.md
- Per-template email tester (registry + endpoint + UI on Email admin page)
- Cancel-document mode picker (delete-from-Documenso vs keep-for-audit)
- Dashboard PDF report: 25 widgets, SVG charts, date-range picker, 11 resolvers
- Customize-widgets per-region sortables at xl+ (charts/rails/feed); single
  flat sortable below xl when the layout stacks; per-viewport saved orders
- Audit doc updates capturing each shipped item
- Lint fixes: react-compiler immutability in DonutChart (reduce instead of
  let-reassign), set-state-in-effect disables in CountryFlag and
  UploadForSigning preview-bytes effect, unused 'confirm' destructures in
  interest contract + reservation tabs, unescaped apostrophe in test-template
  card copy
This commit is contained in:
2026-05-23 00:52:59 +02:00
parent 43719b49e9
commit 221ae5784e
749 changed files with 7440 additions and 3118 deletions

View File

@@ -128,7 +128,7 @@ export function InterestForm({ open, onOpenChange, defaultClientId, interest }:
// Auto-fill pipelineStage + leadCategory based on whether a berth was
// picked. Once the rep manually edits either field we stop touching it,
// so we don't fight the user. Edit mode skips the auto-fill entirely
// so we don't fight the user. Edit mode skips the auto-fill entirely -
// changing the berth on an in-flight interest shouldn't silently demote
// it back to "enquiry".
const userTouchedStage = useRef(false);
@@ -177,7 +177,7 @@ export function InterestForm({ open, onOpenChange, defaultClientId, interest }:
// reps don't get stuck on an empty dropdown wondering what to do. We hit
// the same autocomplete endpoint the picker uses but with an empty query
// to get the full unfiltered list scoped to the owner filter.
// Tags-availability probe drives whether the whole Tags section
// Tags-availability probe - drives whether the whole Tags section
// (label + picker) renders. The picker itself returns null when empty,
// but the wrapping label/separator needed the same gate.
const { data: tagsList } = useQuery<{ data: Array<{ id: string }> }>({
@@ -292,7 +292,7 @@ export function InterestForm({ open, onOpenChange, defaultClientId, interest }:
// Materialise any additional berths the rep picked in the multi-
// select. The first (primary) berth is already linked via the create
// payload's berthId; everything else gets a follow-up POST to the
// junction endpoint. We fire them in parallel failure on one is
// junction endpoint. We fire them in parallel - failure on one is
// surfaced as a toast but doesn't roll back the interest creation.
if (additionalBerthIds.length > 0) {
await Promise.allSettled(
@@ -312,7 +312,7 @@ export function InterestForm({ open, onOpenChange, defaultClientId, interest }:
toast.success(result.created ? 'Interest created' : 'Interest updated');
onOpenChange(false);
// F20: navigate to the new interest's detail page so the rep can
// start the workflow immediately. Edits stay in place no point
// start the workflow immediately. Edits stay in place - no point
// re-loading the same row's detail page they just came from.
if (result.created && portSlug) {
router.push(`/${portSlug}/interests/${result.id}` as never);
@@ -335,7 +335,7 @@ export function InterestForm({ open, onOpenChange, defaultClientId, interest }:
// gets a follow-up POST /interests/{id}/berths so they show up in the
// linked-berths list with isPrimary=false. The primary berth (the form's
// `berthId`) is materialised by the standard create path. Edit mode
// doesn't surface this managing extra berths post-create happens on
// doesn't surface this - managing extra berths post-create happens on
// the interest detail page's linked-berths section.
const [additionalBerthIds, setAdditionalBerthIds] = useState<string[]>([]);
@@ -553,7 +553,7 @@ export function InterestForm({ open, onOpenChange, defaultClientId, interest }:
</div>
{/* Hide the picker entirely when the selected client has no
yachts on file (and isn't linked to a company with yachts).
An empty dropdown is a dead-end UX the only useful action
An empty dropdown is a dead-end UX - the only useful action
in that state is "create a yacht for this client". */}
{selectedClientId && !hasAnyYachts ? (
<div className="rounded-md border border-dashed bg-muted/40 p-3 text-sm">
@@ -736,7 +736,7 @@ export function InterestForm({ open, onOpenChange, defaultClientId, interest }:
)}
</div>
{/* Tags TagPicker itself returns null when the port has no tags
{/* Tags - TagPicker itself returns null when the port has no tags
configured AND the form has nothing selected. We hide the
wrapping label + separator in that same case so an empty
"Tags" header doesn't sit in the form. */}