feat(uat-batch): Group A quick-fixes — 7 items shipped, 5 verified pre-shipped

Sweeps Group A of the 2026-05-21 remaining-plan. Several items the
plan listed as open turned out to already be shipped (annotation gap
in the master doc) — those are confirmed in the commit notes.

Shipped now:
  A1  Documenso settings: collapsed `V2_FEATURE_FIELDS` +
      `CONTRACT_RESERVATION_FIELDS` (legacy SettingsFormCard) into
      `RegistryDrivenForm` sections (`documenso.behavior` +
      `documenso.templates`). Every Documenso setting now flows
      through the registry path that surfaces the env-fallback /
      port / global source badge per field. EOI generation card
      retitled to "Templates & signing pathway" since it now covers
      EOI + reservation + contract template IDs (registry already
      had all three under `documenso.templates`).
  A2  WatchersCard empty state: bumped `mb-3 → mb-4 pb-1` so the
      "No one is watching yet" line has breathing room above the
      "Add a watcher…" select.
  A4  /invoices/upload-receipts guide copy: terse luxury-CRM tone.
      Drop "Snap a photo", "fancy phone camera", "No typing. No
      spreadsheets." Tighten OCR explainer to one sentence;
      action-oriented step + best-practices headers.
  A5  Pageviews chart X-axis: added `interval="preserveStartEnd"` +
      `minTickGap={52}` so multi-week ranges thin out the middle
      ticks instead of overlapping. The MM-DD formatter was already
      in place from an earlier session.
  A7  Inbox doc comment: was stale ("Alerts first, Reminders
      second") but the JSX already had Reminders before Alerts.
      Fixed the docstring.
  A9  CommandList scroll-cap: `max-h-[300px]` now `max-h-[min(300px,
      var(--radix-popover-content-available-height,300px))]` so the
      cmdk list never extends past the host Popover's available
      area. Non-Popover hosts fall through to the 300px static cap.
  A10 DropdownMenuContent: `max-h-96` now
      `max-h-[min(24rem,var(--radix-dropdown-menu-content-
      available-height,24rem))]` for the same available-space
      behaviour on long menus near the viewport edge.
  A11 Residential InterestsTab (list page): row gets an onClick →
      `router.push`; first-cell Link stops propagation so middle-
      click / Cmd-click "open in new tab" still works.
  A12 StageStepper: gained a stage-name row below the bar showing
      every reached stage's short label inline (muted for future
      stages). `size="xs"` variant keeps the cramped table-cell
      footprint intact (no labels).

Already shipped (just annotation gap in master doc):
  A3  EOI "Mark as signed without file" button — line 599 of
      interest-eoi-tab.tsx, parent passes onMarkSigned. Master doc
      already has `SHIPPED in 52342ee` annotation.
  A6  Pageviews vs Sessions explainer — Info popover at line
      157-181 of website-analytics-shell.tsx.
  A8  BulkAddBerthsWizard CurrencySelect — line 376 (apply-to-all)
      + line 456 (per-row).

Verified: tsc clean, vitest 1454/1454.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-21 21:34:20 +02:00
parent a555798cfe
commit e33313bd64
10 changed files with 148 additions and 160 deletions

View File

@@ -1,9 +1,5 @@
import { CheckCircle2, Info } from 'lucide-react';
import {
SettingsFormCard,
type SettingFieldDef,
} from '@/components/admin/shared/settings-form-card';
import { RegistryDrivenForm } from '@/components/admin/shared/registry-driven-form';
import { DocumensoTestButton } from '@/components/admin/documenso/documenso-test-button';
import { EmbeddedSigningCard } from '@/components/admin/documenso/embedded-signing-card';
@@ -11,57 +7,11 @@ import { TemplateSyncButton } from '@/components/admin/documenso/template-sync-b
import { PageHeader } from '@/components/shared/page-header';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
// API_FIELDS removed — replaced by <RegistryDrivenForm sections={['documenso.api']} />
// which adds the new webhook-secret field + AES encrypts the API key at rest.
const CONTRACT_RESERVATION_FIELDS: SettingFieldDef[] = [
{
key: 'documenso_contract_template_id',
label: 'Contract Documenso template ID (optional)',
description:
'Numeric template ID for sales contract generation. Leave blank to use the per-interest upload-and-place-fields flow instead (the typical path for contracts, since they are usually drafted custom per client).',
type: 'string',
placeholder: '',
defaultValue: '',
},
{
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.',
type: 'string',
placeholder: '',
defaultValue: '',
},
];
// Embedded signing field config + Test + Setup help all live inside
// `<EmbeddedSigningCard />` (imported above). Kept out of the field list
// here so the admin page reads as a flat sequence of cards.
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.',
type: 'select',
options: [
{ value: 'PARALLEL', label: 'PARALLEL - all signers invited at once' },
{ value: 'SEQUENTIAL', label: 'SEQUENTIAL - one at a time in order' },
],
defaultValue: 'PARALLEL',
},
{
key: 'documenso_redirect_url',
label: 'Post-signing redirect URL',
description:
"URL Documenso redirects the signer to after they complete signing. Typically the marketing site's success page so signers land on a branded thank-you rather than Documenso's own page. Leave blank to use Documenso's default. v1 and v2 both honour this. Example: https://portnimara.com/sign/success",
type: 'string',
placeholder: 'https://portnimara.com/sign/success',
defaultValue: '',
},
];
// All field arrays removed — every Documenso setting now flows through
// `RegistryDrivenForm`, which surfaces the env-fallback / port / global
// source badge on each field. The settings themselves live in
// `src/lib/settings/registry.ts` under sections `documenso.api` /
// `.signers` / `.templates` / `.behavior`.
export default function DocumensoSettingsPage() {
return (
@@ -235,10 +185,10 @@ export default function DocumensoSettingsPage() {
extra={<DocumensoTestButton />}
/>
<SettingsFormCard
title="v2 signing behaviour"
<RegistryDrivenForm
sections={['documenso.behavior']}
title="Signing behaviour"
description="Cross-cutting settings that apply to EOIs + uploaded contracts/reservations. Sequential signing is v2-only (v1 instances ignore it). Redirect URL is honoured by both v1 and v2 instances."
fields={V2_FEATURE_FIELDS}
/>
<RegistryDrivenForm
@@ -249,17 +199,11 @@ export default function DocumensoSettingsPage() {
<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."
title="Templates & signing pathway"
description="Default pathway, template IDs, and email behaviour for EOIs, reservations, and contracts. Recipient + field discovery happens via 'Sync from Documenso' below that also populates the EOI template ID for you. Most ports leave the reservation/contract template IDs blank because those are typically drafted per interest and uploaded for signing; set them only if you maintain standardised Documenso templates for them."
extra={<TemplateSyncButton />}
/>
<SettingsFormCard
title="Contract & reservation templates (optional)"
description="Most ports leave these blank because contracts/reservations are drafted per interest and uploaded for signing. Set a template ID only if you have a standardised contract/reservation Documenso template."
fields={CONTRACT_RESERVATION_FIELDS}
/>
<EmbeddedSigningCard />
</div>
);