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:
@@ -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’ll be the
|
||||
No administrator account exists yet. Create one to get started - you’ll be the
|
||||
super-administrator for this installation.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -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 & 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's backward-compat window —
|
||||
is intentionally still v1 (relies on Documenso 2.x'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'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" card; Documenso redirects the signer to that URL after they
|
||||
complete signing. Use to land clients on the marketing site's success page or
|
||||
back in the portal instead of Documenso'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 "v2 signing behaviour" 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 />}
|
||||
/>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 0–100 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.
|
||||
|
||||
@@ -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'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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'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 14–30 days. The deal isn'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>
|
||||
|
||||
Reference in New Issue
Block a user