From 153f6ac7972511e63462aaecee2fa7f61f79f403 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 13 May 2026 11:57:37 +0200 Subject: [PATCH] fix(audit-wave-9): unified template token picker with custom-field group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build a shared that renders the canonical MERGE_FIELDS catalog grouped by scope, plus a dynamically-fetched "Custom (port-specific)" group surfaced from /api/v1/admin/custom-fields. The custom group is filtered to entity types the resolver actually expands at send time (client/interest/berth - see mergeCustomFieldValues in document-sends.service). Wire it into both consumers: - admin/document-templates/template-form.tsx (replaces TEMPLATE_VARIABLES list which had drifted from the canonical catalog) - admin/sales-email-config-card.tsx (replaces flat alphabetical dump) Closes custom-fields §B "UI surfacing of {{custom.…}} tokens". Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/BACKLOG.md | 2 +- .../document-templates/template-form.tsx | 15 +-- .../admin/sales-email-config-card.tsx | 12 +- .../admin/shared/template-token-picker.tsx | 104 ++++++++++++++++++ 4 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 src/components/admin/shared/template-token-picker.tsx diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index 759fb650..bbb93cd2 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -42,7 +42,7 @@ Remaining phases — explicitly back-burnered by the user on 2026-05-07: - ✅ **Merge tokens** — `{{custom.}}` validators + resolver shipped 2026-05-08. Tokens expand at template-render time for client/interest/berth contexts via `mergeCustomFieldValues` in `document-sends.service.ts`. Banner updated. - **Search index** — DEFERRED as design limitation. Adding GIN coverage requires either joining `custom_field_values` per search (slow at scale) or materializing values into a search_text column on the parent (additive maintenance burden). The amber banner documents this. - **Audit diff** — N/A. Custom-field values live in their own table, not as a JSONB blob on the parent entity. The `setValues()` service-layer call already creates its own audit log entry (custom-fields.service.ts:349-358), so changes ARE audited — just separately from the entity-diff. -- **UI surfacing of `{{custom.…}}` tokens in template-edit pickers** — Open. The token list dialog currently only shows static catalog tokens. Surface per-port custom-field definitions as a dynamic group under "Custom" so reps can browse them. Backend already accepts the tokens; this is a UI follow-up. +- ✅ **UI surfacing of `{{custom.…}}` tokens in template-edit pickers** — landed 2026-05-13. Shared `` (`src/components/admin/shared/template-token-picker.tsx`) renders the canonical `MERGE_FIELDS` catalog grouped by scope plus a dynamically-fetched "Custom (port-specific)" group filtered to entityTypes resolvable at send-time (client/interest/berth). Wired into both `sales-email-config-card.tsx` and `document-templates/template-form.tsx` so both pickers share the same surface. --- diff --git a/src/components/admin/document-templates/template-form.tsx b/src/components/admin/document-templates/template-form.tsx index 917e4e54..a98f448c 100644 --- a/src/components/admin/document-templates/template-form.tsx +++ b/src/components/admin/document-templates/template-form.tsx @@ -14,7 +14,7 @@ import { } from '@/components/ui/select'; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetFooter } from '@/components/ui/sheet'; import { apiFetch } from '@/lib/api/client'; -import { TEMPLATE_VARIABLES } from '@/lib/pdf/tiptap-validation'; +import { TemplateTokenPicker } from '@/components/admin/shared/template-token-picker'; const DOCUMENT_TYPES = [ { value: 'eoi', label: 'Expression of Interest' }, @@ -154,8 +154,8 @@ export function TemplateForm({ open, onOpenChange, template, onSuccess }: Templa

Paste or edit TipTap JSON. Use{' '} - {'{{variable.key}}'} tokens for - dynamic content. + {'{{scope.field}}'} tokens for + dynamic content — see the list below.