fix(audit): comprehensive 2026-05-15 audit fix wave + Documenso v2 polish
Bundles the prior session's 50-task fix sweep (Documenso v2 + EOI/signing-
progress redesign + env-to-admin migration + dev-mode banner) with the
2026-05-18 audit fix wave (3 CRITICAL, 14 HIGH, 28 MEDIUM, 6 LOW).
CRITICAL (3):
- C-01 interest-berths INNER JOIN -> LEFT JOIN so hard-deleted berths
no longer silently drop interest links
- C-02 /setup added to PUBLIC_PATHS; fresh-deploy bootstrap loop fixed
- C-03 generic PATCH /interests/[id] no longer accepts pipelineStage —
callers must go through /stage with the override-guard chain
HIGH (14/15):
- H-01 explicit ON DELETE on previously-implicit NO ACTION FKs across
interests/documents/reservations/reminders/invoices (migration 0070)
- H-02 login page reads ?redirect= param with same-origin guard
- H-03 CRM invite token moves to URL fragment so it never lands in
nginx access logs / Referer headers
- H-04 Retry-After header on sign-in-by-identifier 429 (RFC 6585 §4)
- H-05 toggleAccount writes an audit row
- H-06 upsertSetting masks any value whose key ends with _encrypted
- H-07 archiveClient cascade fires per-interest audit rows
- H-08 createSalesTransporter applies SMTP_TIMEOUTS
- H-09 AppShell stable children — viewport flip across breakpoint no
longer destroys in-progress form drafts
- H-10 portal documents page swaps Unicode glyph status icons for
Lucide CheckCircle2/XCircle/Circle + aria-labels
- H-12 list components swap alert(...) for toast.warning(...)
- H-13 5 icon-only buttons gain aria-label
- H-14 parseBody treats empty bodies as {}
- H-15 admin layout renders a 403 panel instead of silent bounce
- H-11 not applicable — mobile-search-overlay IS a mobile bottom-sheet
MEDIUM (28+):
- M-MT01-05 defense-in-depth port_id/parent-id filters on UPDATE/DELETE
WHEREs across custom-fields, notes (all 6 entity types x update +
delete), client-contacts, yacht ownerClient lookup, webhook reads
- M-D01 documents-hub realtime event-name typo (file:created -> uploaded)
- M-EM01 portal-auth emails thread through portId
- M-EM02 sendEmail accepts cc/bcc params
- M-EM04 notification_digest catalog key
- M-IN01 portal presigned download URLs use 4h TTL
- M-IN02 OpenAI client lazy-instantiated
- M-IN04 stale pdfme refs updated to pdf-lib AcroForm
- M-IN05 umami.testConnection returns tagged union
- M-L01 reservations tenure_type unified with berths
- M-L02 report-generators canonicalize stage values
- M-AU01 audit log placeholder copy fixed
- M-AU04 outcome_set / outcome_cleared distinct audit verbs
- M-NEW-2 activity feed entity name+type separator
- M-R01 portal allowlist narrowed + portal_session backstop in proxy
- M-SC02 companies archived partial index
- M-SC04 audit_logs.searchText documented as DB-managed
- M-S01 storage_s3_access_key_encrypted admin field
- M-U01 audit log empty state uses <EmptyState>
- M-U09 invoice delete dialog -> <AlertDialog>
- M-U10 toast.success on ClientForm + InterestForm create/edit
- M-U11 settings-form-card logo preview alt text
- M-U14 mobile topbar title on clients/yachts/interests/berths
- M-U15 Invoices in mobile More-sheet
LOW (6/8):
- L-AU01 severity defaults for security-relevant verbs
- L-AU02 +13 missing actions in admin audit filter
- L-AU03 +7 missing entity types in admin audit filter
- L-AU04 dead listAuditLogs stubbed
- L-D02 CLAUDE.md Owner-wins chain tightened
Bonus — Document detail polish (#67 partial, 3/6 deliverables):
- state-aware action button per signer
- watcher Add UI with display-name resolution
- cleanSignerName cleanup
Prior session work bundled in:
- Documenso v2 webhook + envelope-ID normalization + sequential signing
- SigningProgress UI redesign (avatars, per-signer state, timestamps)
- env->admin settings registry + RegistryDrivenForm + encrypted creds
- Embedded-signing card + Test connection + setup help
- Dev-mode EMAIL_REDIRECT_TO banner
- Pipeline rules admin page
- Sales email config card
- Audit log details Sheet
- EOI tab: Finalising badge, absolute timestamps, sequential indicator
- Notes pipeline_stage_at_creation (migration 0069)
- Documenso numeric ID dual-key webhook (migration 0068)
- Dimensions criterion copy (migration 0067)
Tests: 1374/1374 vitest pass. tsc clean. lint clean.
See docs/AUDIT-FIX-WAVE-2026-05-18.md for the full progress report and
the user-input items still pending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 13:28:50 +02:00
'use client' ;
import { useState } from 'react' ;
import { CheckCircle2 , HelpCircle , Loader2 , XCircle } from 'lucide-react' ;
import { toast } from 'sonner' ;
import { Button } from '@/components/ui/button' ;
import { Card , CardContent , CardDescription , CardHeader , CardTitle } from '@/components/ui/card' ;
import {
Sheet ,
SheetContent ,
SheetDescription ,
SheetHeader ,
SheetTitle ,
} from '@/components/ui/sheet' ;
import {
SettingsFormCard ,
type SettingFieldDef ,
} from '@/components/admin/shared/settings-form-card' ;
import { apiFetch } from '@/lib/api/client' ;
import { toastError } from '@/lib/api/toast-error' ;
interface TestResult {
ok : boolean ;
host? : string ;
checks? : Array < { path : string ; status? : number ; ok : boolean ; error? : string } > ;
error? : string ;
at : Date ;
}
const EMBED_FIELDS : SettingFieldDef [ ] = [
{
key : 'embedded_signing_host' ,
label : 'Embedded signing host' ,
description :
"Origin of the public site that hosts the embedded Documenso signing pages. Outbound emails wrap raw Documenso signing URLs into {host}/sign/<type>/<token> so clients sign on your branded page rather than Documenso's domain. Leave blank to fall back to the app URL. Marketing-website pattern: https://portnimara.com" ,
type : 'string' ,
placeholder : 'https://portnimara.com' ,
defaultValue : '' ,
} ,
] ;
/ * *
* Admin card for the embedded - signing host setting . Provides :
* - The setting field itself ( via SettingsFormCard )
* - A Test connection button that probes the host ' s ` / ` and
* ` /sign/success ` paths to verify the marketing - site cutover is
* ready BEFORE signers get sent there from outbound emails .
* - A Help button that opens a Sheet with the setup instructions —
* what routes the marketing site needs , what URL parameters to
* handle , and the Documenso webhook config that pairs with it .
* /
export function EmbeddedSigningCard() {
const [ testing , setTesting ] = useState ( false ) ;
const [ result , setResult ] = useState < TestResult | null > ( null ) ;
const [ helpOpen , setHelpOpen ] = useState ( false ) ;
const handleTest = async ( ) = > {
setTesting ( true ) ;
setResult ( null ) ;
try {
const res = ( await apiFetch ( '/api/v1/admin/embedded-signing/test' , {
method : 'POST' ,
body : { } ,
} ) ) as {
data : {
ok : boolean ;
host? : string ;
error? : string ;
checks? : Array < { path : string ; status? : number ; ok : boolean ; error? : string } > ;
} ;
} ;
setResult ( { . . . res . data , at : new Date ( ) } ) ;
if ( res . data . ok ) toast . success ( 'Embedded signing host reachable.' ) ;
2026-05-21 20:02:58 +02:00
else toast . error ( 'Embedded signing host probe failed - see card.' ) ;
fix(audit): comprehensive 2026-05-15 audit fix wave + Documenso v2 polish
Bundles the prior session's 50-task fix sweep (Documenso v2 + EOI/signing-
progress redesign + env-to-admin migration + dev-mode banner) with the
2026-05-18 audit fix wave (3 CRITICAL, 14 HIGH, 28 MEDIUM, 6 LOW).
CRITICAL (3):
- C-01 interest-berths INNER JOIN -> LEFT JOIN so hard-deleted berths
no longer silently drop interest links
- C-02 /setup added to PUBLIC_PATHS; fresh-deploy bootstrap loop fixed
- C-03 generic PATCH /interests/[id] no longer accepts pipelineStage —
callers must go through /stage with the override-guard chain
HIGH (14/15):
- H-01 explicit ON DELETE on previously-implicit NO ACTION FKs across
interests/documents/reservations/reminders/invoices (migration 0070)
- H-02 login page reads ?redirect= param with same-origin guard
- H-03 CRM invite token moves to URL fragment so it never lands in
nginx access logs / Referer headers
- H-04 Retry-After header on sign-in-by-identifier 429 (RFC 6585 §4)
- H-05 toggleAccount writes an audit row
- H-06 upsertSetting masks any value whose key ends with _encrypted
- H-07 archiveClient cascade fires per-interest audit rows
- H-08 createSalesTransporter applies SMTP_TIMEOUTS
- H-09 AppShell stable children — viewport flip across breakpoint no
longer destroys in-progress form drafts
- H-10 portal documents page swaps Unicode glyph status icons for
Lucide CheckCircle2/XCircle/Circle + aria-labels
- H-12 list components swap alert(...) for toast.warning(...)
- H-13 5 icon-only buttons gain aria-label
- H-14 parseBody treats empty bodies as {}
- H-15 admin layout renders a 403 panel instead of silent bounce
- H-11 not applicable — mobile-search-overlay IS a mobile bottom-sheet
MEDIUM (28+):
- M-MT01-05 defense-in-depth port_id/parent-id filters on UPDATE/DELETE
WHEREs across custom-fields, notes (all 6 entity types x update +
delete), client-contacts, yacht ownerClient lookup, webhook reads
- M-D01 documents-hub realtime event-name typo (file:created -> uploaded)
- M-EM01 portal-auth emails thread through portId
- M-EM02 sendEmail accepts cc/bcc params
- M-EM04 notification_digest catalog key
- M-IN01 portal presigned download URLs use 4h TTL
- M-IN02 OpenAI client lazy-instantiated
- M-IN04 stale pdfme refs updated to pdf-lib AcroForm
- M-IN05 umami.testConnection returns tagged union
- M-L01 reservations tenure_type unified with berths
- M-L02 report-generators canonicalize stage values
- M-AU01 audit log placeholder copy fixed
- M-AU04 outcome_set / outcome_cleared distinct audit verbs
- M-NEW-2 activity feed entity name+type separator
- M-R01 portal allowlist narrowed + portal_session backstop in proxy
- M-SC02 companies archived partial index
- M-SC04 audit_logs.searchText documented as DB-managed
- M-S01 storage_s3_access_key_encrypted admin field
- M-U01 audit log empty state uses <EmptyState>
- M-U09 invoice delete dialog -> <AlertDialog>
- M-U10 toast.success on ClientForm + InterestForm create/edit
- M-U11 settings-form-card logo preview alt text
- M-U14 mobile topbar title on clients/yachts/interests/berths
- M-U15 Invoices in mobile More-sheet
LOW (6/8):
- L-AU01 severity defaults for security-relevant verbs
- L-AU02 +13 missing actions in admin audit filter
- L-AU03 +7 missing entity types in admin audit filter
- L-AU04 dead listAuditLogs stubbed
- L-D02 CLAUDE.md Owner-wins chain tightened
Bonus — Document detail polish (#67 partial, 3/6 deliverables):
- state-aware action button per signer
- watcher Add UI with display-name resolution
- cleanSignerName cleanup
Prior session work bundled in:
- Documenso v2 webhook + envelope-ID normalization + sequential signing
- SigningProgress UI redesign (avatars, per-signer state, timestamps)
- env->admin settings registry + RegistryDrivenForm + encrypted creds
- Embedded-signing card + Test connection + setup help
- Dev-mode EMAIL_REDIRECT_TO banner
- Pipeline rules admin page
- Sales email config card
- Audit log details Sheet
- EOI tab: Finalising badge, absolute timestamps, sequential indicator
- Notes pipeline_stage_at_creation (migration 0069)
- Documenso numeric ID dual-key webhook (migration 0068)
- Dimensions criterion copy (migration 0067)
Tests: 1374/1374 vitest pass. tsc clean. lint clean.
See docs/AUDIT-FIX-WAVE-2026-05-18.md for the full progress report and
the user-input items still pending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 13:28:50 +02:00
} catch ( err ) {
toastError ( err ) ;
setResult ( {
ok : false ,
error : err instanceof Error ? err.message : String ( err ) ,
at : new Date ( ) ,
} ) ;
} finally {
setTesting ( false ) ;
}
} ;
return (
< >
< Card >
< CardHeader >
< div className = "flex items-start justify-between gap-2" >
< div >
< CardTitle > Embedded signing < / CardTitle >
< CardDescription >
Where the public - facing branded signing pages live . The CRM rewrites Documenso
signing URLs to point here when sending invitation and reminder emails .
< / CardDescription >
< / div >
< Button
type = "button"
variant = "ghost"
size = "sm"
onClick = { ( ) = > setHelpOpen ( true ) }
className = "gap-1.5 [&_svg]:size-3.5"
>
< HelpCircle / >
Setup help
< / Button >
< / div >
< / CardHeader >
< CardContent className = "space-y-4" >
{ / * R e n d e r s i n s i d e o u r o u t e r C a r d w i t h i t s o w n m i c r o - h e a d e r .
Title kept terse ( empty string would look broken ) so the
user still has a visual anchor for the field . * / }
< SettingsFormCard title = "Host URL" description = "" fields = { EMBED_FIELDS } / >
< div className = "flex flex-wrap items-center gap-2" >
< Button
variant = "outline"
onClick = { handleTest }
disabled = { testing }
className = "gap-1.5 [&_svg]:size-3.5"
>
{ testing ? < Loader2 className = "animate-spin" aria - hidden / > : null }
Test connection
< / Button >
< p className = "text-xs text-muted-foreground" >
Probes < code > / < / code > and < code > / sign / success < / code > on the configured host .
< / p >
< / div >
{ result ? (
< div
className = { ` rounded-md border p-3 text-sm ${
result . ok
? 'border-emerald-200 bg-emerald-50 text-emerald-900'
: 'border-rose-200 bg-rose-50 text-rose-900'
} ` }
>
< div className = "flex items-start gap-2" >
{ result . ok ? (
< CheckCircle2 className = "mt-0.5 h-4 w-4 shrink-0" aria - hidden / >
) : (
< XCircle className = "mt-0.5 h-4 w-4 shrink-0" aria - hidden / >
) }
< div className = "flex-1" >
< p className = "font-medium" > { result . ok ? 'Connection ok' : 'Connection failed' } < / p >
{ result . host ? (
< p className = "text-xs" >
Host : < code > { result . host } < / code >
< / p >
) : null }
{ result . error ? < p className = "text-xs" > { result . error } < / p > : null }
{ result . checks ? (
< ul className = "mt-1 space-y-0.5 text-xs" >
{ result . checks . map ( ( c ) = > (
< li key = { c . path } >
< code > { c . path } < / code > → { ' ' }
{ c . ok ? (
< span className = "text-emerald-800" > { c . status ? ? 'ok' } < / span >
) : (
< span className = "text-rose-800" >
{ c . status ? ` ${ c . status } fail ` : ( c . error ? ? 'fail' ) }
< / span >
) }
< / li >
) ) }
< / ul >
) : null }
< p className = "mt-1 text-[11px] opacity-70" > { result . at . toLocaleTimeString ( ) } < / p >
< / div >
< / div >
< / div >
) : null }
< / CardContent >
< / Card >
< Sheet open = { helpOpen } onOpenChange = { setHelpOpen } >
< SheetContent side = "right" className = "overflow-y-auto sm:max-w-xl" >
< SheetHeader >
< SheetTitle > Set up embedded signing < / SheetTitle >
< SheetDescription >
How the marketing site has to be wired up so the branded signing flow works .
< / SheetDescription >
< / SheetHeader >
< div className = "space-y-4 pt-4 text-sm leading-6" >
< section >
< h3 className = "mb-1 font-semibold" > 1 . Choose the host < / h3 >
< p className = "text-muted-foreground" >
Pick a public host ( e . g . < code > https : //portnimara.com</code>) and enter it in the
Embedded signing host field above . The CRM rewrites raw Documenso signing URLs into { ' ' }
< code > { '{host}/sign/<role>/<token>' } < / code > for every outbound invitation + reminder
email .
< / p >
< / section >
< section >
< h3 className = "mb-1 font-semibold" > 2 . Implement the signing route < / h3 >
< p className = "text-muted-foreground" >
The marketing site needs to handle < code > / sign / [ role ] / [ token ] < / code > by forwarding
to the underlying Documenso signing URL ( or embedding it in an iframe ) . Role is one
2026-05-21 20:02:58 +02:00
of < code > client < / code > / < code > developer < / code > / < code > approver < / code > - useful for
fix(audit): comprehensive 2026-05-15 audit fix wave + Documenso v2 polish
Bundles the prior session's 50-task fix sweep (Documenso v2 + EOI/signing-
progress redesign + env-to-admin migration + dev-mode banner) with the
2026-05-18 audit fix wave (3 CRITICAL, 14 HIGH, 28 MEDIUM, 6 LOW).
CRITICAL (3):
- C-01 interest-berths INNER JOIN -> LEFT JOIN so hard-deleted berths
no longer silently drop interest links
- C-02 /setup added to PUBLIC_PATHS; fresh-deploy bootstrap loop fixed
- C-03 generic PATCH /interests/[id] no longer accepts pipelineStage —
callers must go through /stage with the override-guard chain
HIGH (14/15):
- H-01 explicit ON DELETE on previously-implicit NO ACTION FKs across
interests/documents/reservations/reminders/invoices (migration 0070)
- H-02 login page reads ?redirect= param with same-origin guard
- H-03 CRM invite token moves to URL fragment so it never lands in
nginx access logs / Referer headers
- H-04 Retry-After header on sign-in-by-identifier 429 (RFC 6585 §4)
- H-05 toggleAccount writes an audit row
- H-06 upsertSetting masks any value whose key ends with _encrypted
- H-07 archiveClient cascade fires per-interest audit rows
- H-08 createSalesTransporter applies SMTP_TIMEOUTS
- H-09 AppShell stable children — viewport flip across breakpoint no
longer destroys in-progress form drafts
- H-10 portal documents page swaps Unicode glyph status icons for
Lucide CheckCircle2/XCircle/Circle + aria-labels
- H-12 list components swap alert(...) for toast.warning(...)
- H-13 5 icon-only buttons gain aria-label
- H-14 parseBody treats empty bodies as {}
- H-15 admin layout renders a 403 panel instead of silent bounce
- H-11 not applicable — mobile-search-overlay IS a mobile bottom-sheet
MEDIUM (28+):
- M-MT01-05 defense-in-depth port_id/parent-id filters on UPDATE/DELETE
WHEREs across custom-fields, notes (all 6 entity types x update +
delete), client-contacts, yacht ownerClient lookup, webhook reads
- M-D01 documents-hub realtime event-name typo (file:created -> uploaded)
- M-EM01 portal-auth emails thread through portId
- M-EM02 sendEmail accepts cc/bcc params
- M-EM04 notification_digest catalog key
- M-IN01 portal presigned download URLs use 4h TTL
- M-IN02 OpenAI client lazy-instantiated
- M-IN04 stale pdfme refs updated to pdf-lib AcroForm
- M-IN05 umami.testConnection returns tagged union
- M-L01 reservations tenure_type unified with berths
- M-L02 report-generators canonicalize stage values
- M-AU01 audit log placeholder copy fixed
- M-AU04 outcome_set / outcome_cleared distinct audit verbs
- M-NEW-2 activity feed entity name+type separator
- M-R01 portal allowlist narrowed + portal_session backstop in proxy
- M-SC02 companies archived partial index
- M-SC04 audit_logs.searchText documented as DB-managed
- M-S01 storage_s3_access_key_encrypted admin field
- M-U01 audit log empty state uses <EmptyState>
- M-U09 invoice delete dialog -> <AlertDialog>
- M-U10 toast.success on ClientForm + InterestForm create/edit
- M-U11 settings-form-card logo preview alt text
- M-U14 mobile topbar title on clients/yachts/interests/berths
- M-U15 Invoices in mobile More-sheet
LOW (6/8):
- L-AU01 severity defaults for security-relevant verbs
- L-AU02 +13 missing actions in admin audit filter
- L-AU03 +7 missing entity types in admin audit filter
- L-AU04 dead listAuditLogs stubbed
- L-D02 CLAUDE.md Owner-wins chain tightened
Bonus — Document detail polish (#67 partial, 3/6 deliverables):
- state-aware action button per signer
- watcher Add UI with display-name resolution
- cleanSignerName cleanup
Prior session work bundled in:
- Documenso v2 webhook + envelope-ID normalization + sequential signing
- SigningProgress UI redesign (avatars, per-signer state, timestamps)
- env->admin settings registry + RegistryDrivenForm + encrypted creds
- Embedded-signing card + Test connection + setup help
- Dev-mode EMAIL_REDIRECT_TO banner
- Pipeline rules admin page
- Sales email config card
- Audit log details Sheet
- EOI tab: Finalising badge, absolute timestamps, sequential indicator
- Notes pipeline_stage_at_creation (migration 0069)
- Documenso numeric ID dual-key webhook (migration 0068)
- Dimensions criterion copy (migration 0067)
Tests: 1374/1374 vitest pass. tsc clean. lint clean.
See docs/AUDIT-FIX-WAVE-2026-05-18.md for the full progress report and
the user-input items still pending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 13:28:50 +02:00
tracking which slot the signer is in .
< / p >
< p className = "mt-1 text-muted-foreground" > Minimum Next . js example : < / p >
< pre className = "mt-1 overflow-x-auto rounded bg-muted p-2 font-mono text-[11px]" >
{ ` // app/sign/[role]/[token]/page.tsx
export default function SignPage ( { params } ) {
const documenseUrl = \ ` \ ${ env . DOCUMENSO_URL } /sign/ \ ${ params . token } \` ;
return < iframe src = { documenseUrl } className = "w-full h-screen" / > ;
} ` }
< / pre >
< / section >
< section >
< h3 className = "mb-1 font-semibold" > 3 . Implement the success route < / h3 >
< p className = "text-muted-foreground" >
After signing , Documenso redirects to the URL configured in { ' ' }
< strong > Post - sign redirect URL < / strong > . Default points at { ' ' }
< code > { '{host}/sign/success' } < / code > . Render a confirmation page there ( the
signer & apos ; s already done ; this is just the friendly & ldquo ; Thanks ! & rdquo ; UX ) .
< / p >
< / section >
< section >
< h3 className = "mb-1 font-semibold" > 4 . Test the connection < / h3 >
< p className = "text-muted-foreground" >
Use the Test connection button to verify < code > / < / code > and { ' ' }
< code > / sign / success < / code > return 2 xx . If either fails , the marketing site
2026-05-21 20:02:58 +02:00
isn & apos ; t ready - fix the route before flipping live or signers will land on a 404
fix(audit): comprehensive 2026-05-15 audit fix wave + Documenso v2 polish
Bundles the prior session's 50-task fix sweep (Documenso v2 + EOI/signing-
progress redesign + env-to-admin migration + dev-mode banner) with the
2026-05-18 audit fix wave (3 CRITICAL, 14 HIGH, 28 MEDIUM, 6 LOW).
CRITICAL (3):
- C-01 interest-berths INNER JOIN -> LEFT JOIN so hard-deleted berths
no longer silently drop interest links
- C-02 /setup added to PUBLIC_PATHS; fresh-deploy bootstrap loop fixed
- C-03 generic PATCH /interests/[id] no longer accepts pipelineStage —
callers must go through /stage with the override-guard chain
HIGH (14/15):
- H-01 explicit ON DELETE on previously-implicit NO ACTION FKs across
interests/documents/reservations/reminders/invoices (migration 0070)
- H-02 login page reads ?redirect= param with same-origin guard
- H-03 CRM invite token moves to URL fragment so it never lands in
nginx access logs / Referer headers
- H-04 Retry-After header on sign-in-by-identifier 429 (RFC 6585 §4)
- H-05 toggleAccount writes an audit row
- H-06 upsertSetting masks any value whose key ends with _encrypted
- H-07 archiveClient cascade fires per-interest audit rows
- H-08 createSalesTransporter applies SMTP_TIMEOUTS
- H-09 AppShell stable children — viewport flip across breakpoint no
longer destroys in-progress form drafts
- H-10 portal documents page swaps Unicode glyph status icons for
Lucide CheckCircle2/XCircle/Circle + aria-labels
- H-12 list components swap alert(...) for toast.warning(...)
- H-13 5 icon-only buttons gain aria-label
- H-14 parseBody treats empty bodies as {}
- H-15 admin layout renders a 403 panel instead of silent bounce
- H-11 not applicable — mobile-search-overlay IS a mobile bottom-sheet
MEDIUM (28+):
- M-MT01-05 defense-in-depth port_id/parent-id filters on UPDATE/DELETE
WHEREs across custom-fields, notes (all 6 entity types x update +
delete), client-contacts, yacht ownerClient lookup, webhook reads
- M-D01 documents-hub realtime event-name typo (file:created -> uploaded)
- M-EM01 portal-auth emails thread through portId
- M-EM02 sendEmail accepts cc/bcc params
- M-EM04 notification_digest catalog key
- M-IN01 portal presigned download URLs use 4h TTL
- M-IN02 OpenAI client lazy-instantiated
- M-IN04 stale pdfme refs updated to pdf-lib AcroForm
- M-IN05 umami.testConnection returns tagged union
- M-L01 reservations tenure_type unified with berths
- M-L02 report-generators canonicalize stage values
- M-AU01 audit log placeholder copy fixed
- M-AU04 outcome_set / outcome_cleared distinct audit verbs
- M-NEW-2 activity feed entity name+type separator
- M-R01 portal allowlist narrowed + portal_session backstop in proxy
- M-SC02 companies archived partial index
- M-SC04 audit_logs.searchText documented as DB-managed
- M-S01 storage_s3_access_key_encrypted admin field
- M-U01 audit log empty state uses <EmptyState>
- M-U09 invoice delete dialog -> <AlertDialog>
- M-U10 toast.success on ClientForm + InterestForm create/edit
- M-U11 settings-form-card logo preview alt text
- M-U14 mobile topbar title on clients/yachts/interests/berths
- M-U15 Invoices in mobile More-sheet
LOW (6/8):
- L-AU01 severity defaults for security-relevant verbs
- L-AU02 +13 missing actions in admin audit filter
- L-AU03 +7 missing entity types in admin audit filter
- L-AU04 dead listAuditLogs stubbed
- L-D02 CLAUDE.md Owner-wins chain tightened
Bonus — Document detail polish (#67 partial, 3/6 deliverables):
- state-aware action button per signer
- watcher Add UI with display-name resolution
- cleanSignerName cleanup
Prior session work bundled in:
- Documenso v2 webhook + envelope-ID normalization + sequential signing
- SigningProgress UI redesign (avatars, per-signer state, timestamps)
- env->admin settings registry + RegistryDrivenForm + encrypted creds
- Embedded-signing card + Test connection + setup help
- Dev-mode EMAIL_REDIRECT_TO banner
- Pipeline rules admin page
- Sales email config card
- Audit log details Sheet
- EOI tab: Finalising badge, absolute timestamps, sequential indicator
- Notes pipeline_stage_at_creation (migration 0069)
- Documenso numeric ID dual-key webhook (migration 0068)
- Dimensions criterion copy (migration 0067)
Tests: 1374/1374 vitest pass. tsc clean. lint clean.
See docs/AUDIT-FIX-WAVE-2026-05-18.md for the full progress report and
the user-input items still pending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 13:28:50 +02:00
page from outbound emails .
< / p >
< / section >
< section >
< h3 className = "mb-1 font-semibold" > 5 . Pair the Documenso webhook < / h3 >
< p className = "text-muted-foreground" >
Make sure the Documenso webhook points at { ' ' }
< code > { '{appUrl}/api/webhooks/documenso' } < / code > with the matching webhook secret
stored under Documenso → API → Webhook secret . Without this the EOI status never
updates after signing .
< / p >
< / section >
< section >
< h3 className = "mb-1 font-semibold" > 6 . Cutover < / h3 >
< p className = "text-muted-foreground" >
Flip the Embedded signing host field to your live URL and save . Existing in - flight
EOIs keep their pre - cutover signing URLs ( the rewrite happens at email - dispatch
time , not at envelope creation ) , so old signers can still complete on the old host
until they sign or the EOI is cancelled .
< / p >
< / section >
< / div >
< / SheetContent >
< / Sheet >
< / >
) ;
}