chore(copy): em-dash sweep across user-facing JSX text + bump lint to error

Replaced 174 em-dashes (—) with " - " (space-hyphen-space) across 49
files in src/components + src/app. The em-dash reads as a tell-tale
"AI-generated" marker per the user's design feedback; hyphens with
spaces preserve the connector semantics without the AI tint.

Touched only lines outside pure-comment context (// /* * */). Code
comments, JSDoc, audit-log strings, structured logging strings, and
templates outside the lint scope retain their em-dashes for now —
they're not user-visible.

Also captured two remaining cases that used the `—` HTML entity
instead of the literal character (system-monitoring-dashboard,
interest-stage-picker) — replaced with a plain hyphen.

Bumped the existing `no-restricted-syntax` rule from `warn` → `error`
in eslint.config.mjs scoped to src/components/**/*.tsx +
src/app/**/*.tsx. New code reintroducing em-dashes in JSX text now
fails the lint gate.

Verified: tsc clean, vitest 1448/1448, eslint 0 em-dash warnings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-21 20:02:58 +02:00
parent 292a8b5e4a
commit f0dbefcac2
59 changed files with 213 additions and 205 deletions

View File

@@ -329,7 +329,7 @@ function MilestoneAdvanceButton({
placeholder="Pick a date"
/>
<p className="text-[11px] text-muted-foreground">
Defaults to today back-date if the event happened earlier.
Defaults to today - back-date if the event happened earlier.
</p>
</div>
<div className="flex justify-end gap-2">
@@ -962,11 +962,11 @@ function OverviewTab({
return (
<div className="space-y-6">
{/* Skip-ahead nudge informational only; fires when the deal jumped
{/* Skip-ahead nudge - informational only; fires when the deal jumped
past a milestone without stamping the matching date. */}
<SkipAheadBanner interest={interest} />
{/* Conflict callout fires when a linked berth is sold or already
{/* Conflict callout - fires when a linked berth is sold or already
under offer to another active deal. Doesn't block the rep; just
surfaces the situation so they treat the deal as a backup. */}
<InterestBerthStatusBanner
@@ -976,22 +976,22 @@ function OverviewTab({
archivedAt={null}
/>
{/* Qualification checklist surfaces the port's per-port criteria so
{/* Qualification checklist - surfaces the port's per-port criteria so
the rep can mark each one confirmed before the deal advances out
of 'enquiry'. Hidden when the port has no enabled criteria. */}
<QualificationChecklist interestId={interestId} currentStage={interest.pipelineStage} />
{/* Payments bank-issued invoices live elsewhere; this is the
{/* Payments - bank-issued invoices live elsewhere; this is the
internal audit record of money received against the deal. The
running deposit total here drives the auto-advance into the
deposit_paid stage server-side. Hidden before the reservation
stage: no deposit is expected yet, so the empty card is just
noise the next-milestone card carries the actionable copy
noise - the next-milestone card carries the actionable copy
instead. Render order: deprioritized below the milestone strip
so the rep's eye lands on the active step first. */}
{/* Pre-reservation: the dedicated "Next step" guidance card was
removed in favour of a brighter NEXT STEP pill on the active
MilestoneSection below (it already owns the workflow actions
MilestoneSection below (it already owns the workflow actions -
two surfaces was redundant). Nurturing keeps a slim helper
since no milestone is naturally "current" while a deal is
paused. */}
@@ -1005,7 +1005,7 @@ function OverviewTab({
</div>
) : null}
{/* Sales-process milestones phase-aware so the user only sees
{/* Sales-process milestones - phase-aware so the user only sees
what's actionable now. Past milestones collapse into a tight
history strip; the current milestone gets the full card; future
milestones are hidden behind a toggle so reps can still
@@ -1097,7 +1097,7 @@ function OverviewTab({
</dl>
</div>
{/* Contact client's primary email + phone (from the linked client
{/* Contact - client's primary email + phone (from the linked client
record) AND the first/last-contact activity dates from the
contact log. Phone is rendered via libphonenumber-js's
international formatter so `+33633219796` reads as
@@ -1125,7 +1125,7 @@ function OverviewTab({
}}
/>
) : (
<span className="text-muted-foreground"></span>
<span className="text-muted-foreground"> - </span>
)}
</EditableRow>
<EditableRow label="Phone">
@@ -1150,7 +1150,7 @@ function OverviewTab({
}}
/>
) : (
<span className="text-muted-foreground"></span>
<span className="text-muted-foreground"> - </span>
)}
</EditableRow>
{interest.dateFirstContact || interest.dateLastContact ? (
@@ -1160,7 +1160,7 @@ function OverviewTab({
</>
) : (
<p className="mt-1 text-xs text-muted-foreground italic">
No contact activity logged yet log a call, email, or meeting from the Contact log
No contact activity logged yet - log a call, email, or meeting from the Contact log
tab to start tracking.
</p>
)}
@@ -1170,7 +1170,7 @@ function OverviewTab({
</dl>
</div>
{/* Berth requirements desired length / width / draft. Editable
{/* Berth requirements - desired length / width / draft. Editable
inline so reps can capture or correct a buyer's needs without
leaving the Overview tab. These values drive the auto-tick on
the "Dimensions confirmed" qualification row + the
@@ -1183,7 +1183,7 @@ function OverviewTab({
value={interest.desiredLengthFt ?? null}
onSave={save('desiredLengthFt')}
placeholder="e.g. 60"
emptyText=""
emptyText=" - "
/>
</EditableRow>
<EditableRow label="Desired width (ft)">
@@ -1191,7 +1191,7 @@ function OverviewTab({
value={interest.desiredWidthFt ?? null}
onSave={save('desiredWidthFt')}
placeholder="e.g. 25"
emptyText=""
emptyText=" - "
/>
</EditableRow>
<EditableRow label="Desired draft (ft)">
@@ -1199,7 +1199,7 @@ function OverviewTab({
value={interest.desiredDraftFt ?? null}
onSave={save('desiredDraftFt')}
placeholder="e.g. 6"
emptyText=""
emptyText=" - "
/>
</EditableRow>
</dl>
@@ -1215,7 +1215,7 @@ function OverviewTab({
{/* Most-recent threaded note teaser. Saves a click into the Notes
tab when the rep just wants to peek at "what was discussed last."
Always rendered now that the redundant `interests.notes` blob is
gone falls back to an empty-state prompt so reps still have an
gone - falls back to an empty-state prompt so reps still have an
obvious entry point to the Notes tab from Overview. */}
<div className="space-y-1 md:col-span-2">
<div className="mb-2 flex items-center justify-between">
@@ -1271,7 +1271,7 @@ function OverviewTab({
what's already linked before browsing more options. Each row exposes
per-berth role-flag toggles and the EOI bypass control (only visible
once the parent interest's primary EOI is signed). */}
{/* Won-status wrap-up checklist only renders when this interest's
{/* Won-status wrap-up checklist - only renders when this interest's
outcome is `won`. Surfaces upload slots for the manual paperwork
that didn't flow through the EOI->Contract chain automatically. */}
<WonStatusPanel interestId={interestId} outcome={interest.outcome ?? null} />
@@ -1298,7 +1298,7 @@ function OverviewTab({
{confirmDialog}
{/* Mounted at the Overview level so the EOI milestone's "Generate EOI"
footer button can launch the dialog without leaving the tab. Same
dialog component the dedicated EOI tab uses single source of
dialog component the dedicated EOI tab uses - single source of
truth for the editing/confirmation flow. */}
<EoiGenerateDialog
interestId={interestId}