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

@@ -5,7 +5,7 @@ import type { SettingEntry } from './types';
/**
* Central registry of every tenant-configurable setting. One entry per setting,
* consumed by the resolver, the admin form generator, the validator, and the
* encryption helper. Adding a new integration is a registry entry no new
* encryption helper. Adding a new integration is a registry entry - no new
* schema, no new resolver, no new admin page wiring.
*
* Do NOT register boot-time / build-time secrets here (DATABASE_URL,
@@ -28,7 +28,7 @@ export const REGISTRY: SettingEntry[] = [
section: 'documenso.api',
label: 'API URL',
description:
'Bare host only never include /api/v1. The client appends versioned paths based on the API version below.',
'Bare host only - never include /api/v1. The client appends versioned paths based on the API version below.',
type: 'url',
scope: 'port',
envFallback: 'DOCUMENSO_API_URL',
@@ -53,8 +53,8 @@ export const REGISTRY: SettingEntry[] = [
'v1 = Documenso 1.13.x stable. v2 = Documenso 2.x with the envelope model. Test the connection after switching.',
type: 'select',
options: [
{ value: 'v1', label: 'v1 Documenso 1.13.x (default, stable)' },
{ value: 'v2', label: 'v2 Documenso 2.x (envelope, recommended for new ports)' },
{ value: 'v1', label: 'v1 - Documenso 1.13.x (default, stable)' },
{ value: 'v2', label: 'v2 - Documenso 2.x (envelope, recommended for new ports)' },
],
scope: 'port',
envFallback: 'DOCUMENSO_API_VERSION',
@@ -78,7 +78,7 @@ export const REGISTRY: SettingEntry[] = [
{
key: 'documenso_developer_name',
section: 'documenso.signers',
label: 'Developer signer name',
label: 'Developer signer - name',
description:
"Override the name on the developer recipient slot. Leave blank to use whatever's set on the Documenso template.",
type: 'string',
@@ -87,7 +87,7 @@ export const REGISTRY: SettingEntry[] = [
{
key: 'documenso_developer_email',
section: 'documenso.signers',
label: 'Developer signer email',
label: 'Developer signer - email',
description:
"Override the email on the developer recipient slot. Leave blank to use whatever's set on the Documenso template.",
type: 'email',
@@ -96,7 +96,7 @@ export const REGISTRY: SettingEntry[] = [
{
key: 'documenso_developer_label',
section: 'documenso.signers',
label: 'Developer signer label',
label: 'Developer signer - label',
description: 'Display label shown on the signing screen (defaults to "Developer").',
type: 'string',
scope: 'port',
@@ -107,7 +107,7 @@ export const REGISTRY: SettingEntry[] = [
section: 'documenso.signers',
label: 'Developer Documenso recipient ID',
description:
'Numeric Documenso recipient slot ID for the developer signer. Set automatically by "Sync from Documenso" you rarely set this by hand.',
'Numeric Documenso recipient slot ID for the developer signer. Set automatically by "Sync from Documenso" - you rarely set this by hand.',
type: 'number',
scope: 'port',
envFallback: 'DOCUMENSO_DEVELOPER_RECIPIENT_ID',
@@ -115,16 +115,16 @@ export const REGISTRY: SettingEntry[] = [
{
key: 'documenso_developer_user_id',
section: 'documenso.signers',
label: 'Developer signer linked CRM user (optional)',
label: 'Developer signer - linked CRM user (optional)',
description:
"Project Director RBAC binding. When set, the webhook handler fires an in-CRM notification for this user when it's their turn to sign alongside the branded email. Leave blank if the developer slot doesn't map to a CRM user (e.g. external developer).",
"Project Director RBAC binding. When set, the webhook handler fires an in-CRM notification for this user when it's their turn to sign - alongside the branded email. Leave blank if the developer slot doesn't map to a CRM user (e.g. external developer).",
type: 'user-select',
scope: 'port',
},
{
key: 'documenso_approver_name',
section: 'documenso.signers',
label: 'Approver signer name',
label: 'Approver signer - name',
description:
"Override the name on the approver recipient slot. Leave blank to use whatever's set on the Documenso template.",
type: 'string',
@@ -133,7 +133,7 @@ export const REGISTRY: SettingEntry[] = [
{
key: 'documenso_approver_email',
section: 'documenso.signers',
label: 'Approver signer email',
label: 'Approver signer - email',
description:
"Override the email on the approver recipient slot. Leave blank to use whatever's set on the Documenso template.",
type: 'email',
@@ -142,7 +142,7 @@ export const REGISTRY: SettingEntry[] = [
{
key: 'documenso_approver_label',
section: 'documenso.signers',
label: 'Approver signer label',
label: 'Approver signer - label',
description: 'Display label shown on the signing screen (defaults to "Approver").',
type: 'string',
scope: 'port',
@@ -153,7 +153,7 @@ export const REGISTRY: SettingEntry[] = [
section: 'documenso.signers',
label: 'Approver Documenso recipient ID',
description:
'Numeric Documenso recipient slot ID for the approver. Set automatically by "Sync from Documenso" you rarely set this by hand.',
'Numeric Documenso recipient slot ID for the approver. Set automatically by "Sync from Documenso" - you rarely set this by hand.',
type: 'number',
scope: 'port',
envFallback: 'DOCUMENSO_APPROVAL_RECIPIENT_ID',
@@ -161,9 +161,9 @@ export const REGISTRY: SettingEntry[] = [
{
key: 'documenso_approver_user_id',
section: 'documenso.signers',
label: 'Approver linked CRM user (optional)',
label: 'Approver - linked CRM user (optional)',
description:
"Same as developer's linked user when set, fires an in-CRM notification when it's the approver's turn to sign.",
"Same as developer's linked user - when set, fires an in-CRM notification when it's the approver's turn to sign.",
type: 'user-select',
scope: 'port',
},
@@ -241,11 +241,11 @@ export const REGISTRY: SettingEntry[] = [
section: 'documenso.behavior',
label: 'Signing order',
description:
'PARALLEL = all recipients can sign at once. SEQUENTIAL = each waits for the previous (v2 only v1 always parallel).',
'PARALLEL = all recipients can sign at once. SEQUENTIAL = each waits for the previous (v2 only - v1 always parallel).',
type: 'select',
options: [
{ value: 'PARALLEL', label: 'Parallel all recipients sign concurrently' },
{ value: 'SEQUENTIAL', label: 'Sequential order matters (v2 only)' },
{ value: 'PARALLEL', label: 'Parallel - all recipients sign concurrently' },
{ value: 'SEQUENTIAL', label: 'Sequential - order matters (v2 only)' },
],
scope: 'port',
defaultValue: 'PARALLEL',
@@ -263,7 +263,7 @@ export const REGISTRY: SettingEntry[] = [
// JSON map keyed by trigger name; value is one of 'auto' | 'suggest' |
// 'off'. Read by `getStageAdvanceMode` in port-config.ts. The registry
// entry uses the generic `string` type because the form generator's
// schemas don't have a JSON variant the admin UI is a dedicated page
// schemas don't have a JSON variant - the admin UI is a dedicated page
// (/admin/pipeline-rules) that renders 3-way toggles per trigger.
{
key: 'stage_advance_rules',
@@ -316,15 +316,15 @@ export const REGISTRY: SettingEntry[] = [
description: 'Used when a feature does not specify an explicit model.',
type: 'select',
options: [
{ value: 'gpt-4o-mini', label: 'gpt-4o-mini cheap, fast, vision-capable' },
{ value: 'gpt-4o', label: 'gpt-4o full-strength multimodal' },
{ value: 'gpt-4-turbo', label: 'gpt-4-turbo legacy text reasoning' },
{ value: 'gpt-4o-mini', label: 'gpt-4o-mini - cheap, fast, vision-capable' },
{ value: 'gpt-4o', label: 'gpt-4o - full-strength multimodal' },
{ value: 'gpt-4-turbo', label: 'gpt-4-turbo - legacy text reasoning' },
],
scope: 'port',
defaultValue: 'gpt-4o-mini',
},
// ─── Email From / Reply-To ──────────────────────────────────────────────
// ─── Email - From / Reply-To ──────────────────────────────────────────────
{
key: 'email_from_name',
section: 'email.from',
@@ -364,7 +364,7 @@ export const REGISTRY: SettingEntry[] = [
placeholder: 'https://portnimara.com/supplemental',
},
// ─── Email SMTP overrides ───────────────────────────────────────────────
// ─── Email - SMTP overrides ───────────────────────────────────────────────
{
key: 'smtp_host_override',
section: 'email.smtp',
@@ -406,7 +406,7 @@ export const REGISTRY: SettingEntry[] = [
envFallback: 'SMTP_PASS',
},
// ─── Storage S3 / MinIO ─────────────────────────────────────────────────
// ─── Storage - S3 / MinIO ─────────────────────────────────────────────────
{
key: 'storage_s3_endpoint',
section: 'storage.s3',
@@ -445,7 +445,7 @@ export const REGISTRY: SettingEntry[] = [
section: 'storage.s3',
label: 'S3 access key',
description:
'IAM access key id. AES-encrypted at rest (was previously stored plaintext fixed in this migration).',
'IAM access key id. AES-encrypted at rest (was previously stored plaintext - fixed in this migration).',
type: 'password',
scope: 'global',
encrypted: true,
@@ -474,13 +474,13 @@ export const REGISTRY: SettingEntry[] = [
defaultValue: false,
},
// ─── Storage Filesystem (single-node only) ──────────────────────────────
// ─── Storage - Filesystem (single-node only) ──────────────────────────────
{
key: 'storage_filesystem_root',
section: 'storage.filesystem',
label: 'Filesystem root path',
description:
'Absolute directory where files land when the active backend is filesystem. Single-node deployments only multi-node MUST use S3.',
'Absolute directory where files land when the active backend is filesystem. Single-node deployments only - multi-node MUST use S3.',
type: 'string',
scope: 'global',
placeholder: '/var/lib/pn-crm/files',
@@ -552,7 +552,7 @@ export const REGISTRY: SettingEntry[] = [
section: 'pulse',
label: 'Signal: document declined (risk)',
description:
'Default on. Strongest cooling signal client refused to sign an EOI / contract / reservation. Requires the risk-data wiring shipped alongside Phase 2 to populate.',
'Default on. Strongest cooling signal - client refused to sign an EOI / contract / reservation. Requires the risk-data wiring shipped alongside Phase 2 to populate.',
type: 'boolean',
scope: 'port',
},
@@ -600,7 +600,7 @@ export const REGISTRY: SettingEntry[] = [
placeholder: 'Cold',
},
// ─── Residential partner forwarding ──────────────────────────────────────
// ─── Residential - partner forwarding ──────────────────────────────────────
{
key: 'residential_partner_recipients',
section: 'residential.partner',