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

@@ -91,7 +91,7 @@ export default function SetupPage() {
password: data.password,
},
});
toast.success('Administrator account created sign in to continue.');
toast.success('Administrator account created - sign in to continue.');
router.replace('/login');
} catch (err) {
toast.error(err instanceof Error ? err.message : 'Failed to create administrator account');
@@ -114,7 +114,7 @@ export default function SetupPage() {
<div className="text-center space-y-1">
<h1 className="text-xl font-semibold">Welcome to {appName}</h1>
<p className="text-sm text-muted-foreground">
No administrator account exists yet. Create one to get started you&rsquo;ll be the
No administrator account exists yet. Create one to get started - you&rsquo;ll be the
super-administrator for this installation.
</p>
</div>

View File

@@ -28,7 +28,7 @@ const CONTRACT_RESERVATION_FIELDS: SettingFieldDef[] = [
key: 'documenso_reservation_template_id',
label: 'Reservation agreement Documenso template ID (optional)',
description:
'Numeric template ID for reservation agreements. Same logic leave blank to upload per interest.',
'Numeric template ID for reservation agreements. Same logic - leave blank to upload per interest.',
type: 'string',
placeholder: '',
defaultValue: '',
@@ -44,11 +44,11 @@ const V2_FEATURE_FIELDS: SettingFieldDef[] = [
key: 'documenso_signing_order',
label: 'Signing order',
description:
'Whether all signers receive the invitation at once (PARALLEL anyone can sign first) or only the next pending signer gets the email once the previous one finishes (SEQUENTIAL). Applied at envelope-create time on both v1 and v2: v1 honours meta.signingOrder on /templates/{id}/generate-document; v2 honours it via /envelope/update right after /template/use.',
'Whether all signers receive the invitation at once (PARALLEL - anyone can sign first) or only the next pending signer gets the email once the previous one finishes (SEQUENTIAL). Applied at envelope-create time on both v1 and v2: v1 honours meta.signingOrder on /templates/{id}/generate-document; v2 honours it via /envelope/update right after /template/use.',
type: 'select',
options: [
{ value: 'PARALLEL', label: 'PARALLEL all signers invited at once' },
{ value: 'SEQUENTIAL', label: 'SEQUENTIAL one at a time in order' },
{ value: 'PARALLEL', label: 'PARALLEL - all signers invited at once' },
{ value: 'SEQUENTIAL', label: 'SEQUENTIAL - one at a time in order' },
],
defaultValue: 'PARALLEL',
},
@@ -75,14 +75,14 @@ export default function DocumensoSettingsPage() {
<CardHeader>
<CardTitle className="flex items-center gap-2 text-base">
<Info className="h-4 w-4" aria-hidden="true" />
v1 vs v2 what changes when you flip the API version
v1 vs v2 - what changes when you flip the API version
</CardTitle>
</CardHeader>
<CardContent className="space-y-4 text-sm">
<p className="text-muted-foreground">
The CRM supports both Documenso 1.13.x (v1) and 2.x (v2). v1 is the default for
backwards compatibility. v2 is recommended for new ports and unlocks the features below.
Switching versions does <strong>not</strong> require any code changes version-aware
Switching versions does <strong>not</strong> require any code changes - version-aware
client methods pick the right endpoint per port. Switch, save, then run the
test-connection button to confirm the chosen instance is actually on the matching
Documenso version.
@@ -111,7 +111,7 @@ export default function DocumensoSettingsPage() {
/>
<span>
<strong>Percent-based field coordinates.</strong> No page-dimension lookup needed
coordinates are portable across page sizes. v1 requires us to assume A4 for
- coordinates are portable across page sizes. v1 requires us to assume A4 for
auto-placed fields.
</span>
</li>
@@ -122,7 +122,7 @@ export default function DocumensoSettingsPage() {
/>
<span>
<strong>Richer field metadata.</strong> TEXT labels &amp; required flags, NUMBER
min/max + format, CHECKBOX/DROPDOWN/RADIO option lists with defaults all ignored
min/max + format, CHECKBOX/DROPDOWN/RADIO option lists with defaults - all ignored
by v1, surfaced by v2 in the signing UI.
</span>
</li>
@@ -134,7 +134,7 @@ export default function DocumensoSettingsPage() {
<span>
<strong>v2-flavoured webhook events.</strong> <code>RECIPIENT_VIEWED</code>,{' '}
<code>RECIPIENT_SIGNED</code>, <code>DOCUMENT_RECIPIENT_COMPLETED</code>,{' '}
<code>DOCUMENT_DECLINED</code>, <code>DOCUMENT_REMINDER_SENT</code> all routed
<code>DOCUMENT_DECLINED</code>, <code>DOCUMENT_REMINDER_SENT</code> - all routed
through the same dedup + audit pipeline as v1 events.
</span>
</li>
@@ -147,9 +147,9 @@ export default function DocumensoSettingsPage() {
<strong>Envelope CRUD endpoints.</strong> <code>GET</code>, <code>DELETE</code>,
<code>POST /envelope/create</code> (multipart),{' '}
<code>POST /envelope/distribute</code>, <code>POST /envelope/redistribute</code>,{' '}
<code>GET /envelope/{'{id}'}/download</code> all routed through{' '}
<code>GET /envelope/{'{id}'}/download</code> - all routed through{' '}
<code>/api/v2/envelope/...</code> when v2 is selected. The template-generate path
is intentionally still v1 (relies on Documenso 2.x&apos;s backward-compat window
is intentionally still v1 (relies on Documenso 2.x&apos;s backward-compat window -
see the deferred-roadmap below).
</span>
</li>
@@ -160,7 +160,7 @@ export default function DocumensoSettingsPage() {
/>
<span>
<strong>One-call send.</strong> v2&apos;s <code>/envelope/distribute</code>{' '}
returns per-recipient <code>signingUrl</code> in the same response v1 requires a
returns per-recipient <code>signingUrl</code> in the same response - v1 requires a
separate GET to fetch them. Faster send flow on the rep side.
</span>
</li>
@@ -186,7 +186,7 @@ export default function DocumensoSettingsPage() {
behaviour&quot; card; Documenso redirects the signer to that URL after they
complete signing. Use to land clients on the marketing site&apos;s success page or
back in the portal instead of Documenso&apos;s default thank-you page. (v1 honours
this too listed here because the admin setting was added with the v2 work.)
this too - listed here because the admin setting was added with the v2 work.)
</span>
</li>
</ul>
@@ -201,7 +201,7 @@ export default function DocumensoSettingsPage() {
<strong>
Single-shot <code>/template/use</code>
</strong>{' '}
with v2 <code>prefillFields</code> by ID current EOI flow uses{' '}
with v2 <code>prefillFields</code> by ID - current EOI flow uses{' '}
<code>/api/v1/templates/{'{id}'}/generate-document</code> with{' '}
<code>formValues</code> keyed by name. v2 instances accept both during their
backward-compat window; full migration requires per-template field-ID capture in
@@ -211,17 +211,17 @@ export default function DocumensoSettingsPage() {
<strong>
Update envelope metadata after creation (<code>/envelope/update</code>)
</strong>{' '}
change title / subject / redirectUrl on a doc already in DRAFT/PENDING without
- change title / subject / redirectUrl on a doc already in DRAFT/PENDING without
re-generating.
</li>
<li>
<strong>Non-SIGNER recipient roles (CC / VIEWER)</strong> APPROVER role is already
<strong>Non-SIGNER recipient roles (CC / VIEWER)</strong> - APPROVER role is already
used by the EOI template; CC + VIEWER not yet exposed in the recipient builder.
Useful for sales managers who want a copy without a signature slot.
</li>
</ul>
<p className="mt-2 text-xs text-muted-foreground">
Sequential signing and post-signing redirect URL <strong>are now wired</strong> see
Sequential signing and post-signing redirect URL <strong>are now wired</strong> - see
the new &quot;v2 signing behaviour&quot; card below to configure them.
</p>
</div>
@@ -244,13 +244,13 @@ export default function DocumensoSettingsPage() {
<RegistryDrivenForm
sections={['documenso.signers']}
title="Signers (developer + approver)"
description="Identity bound to the developer (signing order 2) and approver (signing order 3) slots in your Documenso templates. Leave name + email blank to fall through to whatever you set on the Documenso template itself; set them here to override the template's stored values at send time. Recipient IDs are populated automatically by 'Sync from Documenso' below. Linking a CRM user is optional when set, the platform fires an in-CRM notification for that user when it's their turn to sign."
description="Identity bound to the developer (signing order 2) and approver (signing order 3) slots in your Documenso templates. Leave name + email blank to fall through to whatever you set on the Documenso template itself; set them here to override the template's stored values at send time. Recipient IDs are populated automatically by 'Sync from Documenso' below. Linking a CRM user is optional - when set, the platform fires an in-CRM notification for that user when it's their turn to sign."
/>
<RegistryDrivenForm
sections={['documenso.templates']}
title="EOI generation"
description="Default pathway, template, and email behaviour when an interest's EOI is generated. Recipient + field discovery happens via 'Sync from Documenso' below that also populates the template ID for you."
description="Default pathway, template, and email behaviour when an interest's EOI is generated. Recipient + field discovery happens via 'Sync from Documenso' below - that also populates the template ID for you."
extra={<TemplateSyncButton />}
/>

View File

@@ -69,7 +69,7 @@ export default function ErrorCodeReferencePage() {
</h1>
<p className="text-muted-foreground text-sm mt-1">
Every error code the platform can return, with its HTTP status and the plain-language
message a user sees. Codes are stable identifiers once shipped, they never get
message a user sees. Codes are stable identifiers - once shipped, they never get
renamed.
</p>
</div>

View File

@@ -30,34 +30,34 @@ const TRIGGERS: Array<{
{
key: 'eoi_sent',
label: 'EOI sent',
description: 'Rep generates an EOI for signing moves the deal to "EOI" stage.',
description: 'Rep generates an EOI for signing - moves the deal to "EOI" stage.',
defaultMode: 'auto',
},
{
key: 'eoi_signed',
label: 'EOI signed (all parties)',
description:
'All signatories complete the EOI moves the deal to "Reservation" stage. Conventional CRM behaviour.',
'All signatories complete the EOI - moves the deal to "Reservation" stage. Conventional CRM behaviour.',
defaultMode: 'auto',
},
{
key: 'reservation_signed',
label: 'Reservation agreement signed',
description:
'Reservation paperwork signed by all parties keeps the deal at "Reservation" with sub-status signed.',
'Reservation paperwork signed by all parties - keeps the deal at "Reservation" with sub-status signed.',
defaultMode: 'auto',
},
{
key: 'deposit_received',
label: 'Deposit received in full',
description:
'Deposit total reaches the expected amount moves the deal to "Deposit Paid" stage.',
'Deposit total reaches the expected amount - moves the deal to "Deposit Paid" stage.',
defaultMode: 'auto',
},
{
key: 'contract_signed',
label: 'Sales contract signed',
description: 'Final contract signed by all parties moves the deal to "Contract" stage.',
description: 'Final contract signed by all parties - moves the deal to "Contract" stage.',
defaultMode: 'auto',
},
];
@@ -166,7 +166,7 @@ export default function PipelineRulesPage() {
>
<p className="text-sm font-semibold">Custom</p>
<p className="text-xs text-muted-foreground">
Mix and match the per-trigger toggles below override the preset.
Mix and match - the per-trigger toggles below override the preset.
</p>
</div>
</div>

View File

@@ -24,7 +24,7 @@ export default function PulseAdminPage() {
<p className="text-muted-foreground">
Every interest row carries a small coloured chip in the detail header. It scores the
deal from 0100 using rule-based signals (no AI). Click the chip on any interest to see
the per-signal breakdown every +N or -N traces back to a dated event on the deal.
the per-signal breakdown - every +N or -N traces back to a dated event on the deal.
</p>
<p className="text-muted-foreground">
Positive signals (recent EOI sent, deposit received, contract signed) push the score up.

View File

@@ -206,14 +206,14 @@ export default function ScanReceiptPage() {
)}
{uploadMutation.isError && (
<span className="text-destructive">
Receipt upload failed save will still create the expense without an image.
Receipt upload failed - save will still create the expense without an image.
</span>
)}
</div>
</div>
) : (
<div className="grid gap-2 sm:grid-cols-2">
{/* Camera button available on mobile devices that surface the
{/* Camera button - available on mobile devices that surface the
built-in capture flow when an `image/*` input has the
`capture` attribute. Hidden on desktop where it's a no-op. */}
<Button
@@ -225,7 +225,7 @@ export default function ScanReceiptPage() {
<Camera className="mr-2 h-5 w-5" />
Take photo
</Button>
{/* File picker works on every platform. Phrased so the copy
{/* File picker - works on every platform. Phrased so the copy
fits both mobile (library/files) and desktop (drag and drop). */}
<Button
type="button"
@@ -243,7 +243,7 @@ export default function ScanReceiptPage() {
</p>
</div>
)}
{/* `image/*` is the broadest accept includes HEIC on iOS,
{/* `image/*` is the broadest accept - includes HEIC on iOS,
JPEG/PNG/WebP everywhere. The capture attribute on the second
input invokes the native camera flow on mobile. */}
<input
@@ -272,7 +272,7 @@ export default function ScanReceiptPage() {
{scanMutation.isError && (
<div className="mt-4 rounded-md border border-amber-300 bg-amber-50 p-3 text-xs text-amber-900 dark:border-amber-900 dark:bg-amber-950/40 dark:text-amber-200">
<span className="font-medium">Couldn&apos;t read this receipt automatically.</span>{' '}
You can still fill in the details manually below the receipt image will save with
You can still fill in the details manually below - the receipt image will save with
the expense.
</div>
)}

View File

@@ -25,7 +25,7 @@ export default async function PortalProfilePage() {
<span className="font-medium">{session.email}</span>
</div>
<p className="text-xs text-gray-400 pt-1">
To update name, phone, or address, please contact your port team they keep the records
To update name, phone, or address, please contact your port team - they keep the records
authoritative.
</p>
</div>

View File

@@ -1,7 +1,7 @@
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Deal Pulse & Heat Port Nimara CRM',
title: 'Deal Pulse & Heat - Port Nimara CRM',
description:
'How the deal pulse chip + heat score work: signals, calibration, and what to do when a deal goes cold.',
};
@@ -35,7 +35,7 @@ export default function DealPulseDocsPage() {
<p>
The colored chip on each interest is a fast read of{' '}
<strong>how hot the deal is right now</strong> based on what&apos;s been happening on it
lately not a prediction, not an AI score, just a mechanical rollup of recent activity.
lately - not a prediction, not an AI score, just a mechanical rollup of recent activity.
</p>
</section>
@@ -53,7 +53,7 @@ export default function DealPulseDocsPage() {
<dt className="font-semibold text-amber-900">Warm</dt>
<dd className="text-amber-900/90">
Activity in the last 1430 days. The deal isn&apos;t neglected but the cadence has
slowed usually means a follow-up reminder is the right next action.
slowed - usually means a follow-up reminder is the right next action.
</dd>
</div>
<div className="rounded-md border bg-slate-100 p-3">
@@ -86,7 +86,7 @@ export default function DealPulseDocsPage() {
</li>
<li>
<strong>Time at current stage.</strong> Stagnation drags the score down even if other
signals look good a deal stuck at Reservation for six weeks should not read hot.
signals look good - a deal stuck at Reservation for six weeks should not read hot.
</li>
</ul>
<p className="text-muted-foreground">
@@ -128,7 +128,7 @@ export default function DealPulseDocsPage() {
Can I override the chip on a specific deal?
</summary>
<p className="mt-2 text-muted-foreground">
Not directly the chip is a read-only summary. To change it, change the inputs: log a
Not directly - the chip is a read-only summary. To change it, change the inputs: log a
contact, advance a stage, or close the deal.
</p>
</details>