What's new
- The inbox bio pane now shows campaign-specific Travio context for each recipient: resort name, target travel dates, the most recent gross price quoted, family composition (adults + children count and ages), and recent reservations. Each pratica is a one-click jump into Travio for the operator.
- Translated for
en,it,fr,de,es,pt,zhvia a dedicatedinbox.campaignContextnext-intl namespace. Dates render in Europe/Rome regardless of browser timezone so the same trip looks the same to everyone.
Privacy & security
- Operator-only PII surface, product-approved for authorized operators.
- Campaign PII is kept out of the inbox IndexedDB cache; the bio card is hydrated only on direct conversation fetch and mark-read.
- Server-side snapshot deletions clear the bio card immediately — the reducer distinguishes list-refresh nulls from authoritative direct-hydration nulls so stale PII never outlives its source row.
- Travio links are validated against the allow-listed origin and a strict
/pratiche/detail/\d+path; embedded credentials, ports, query, and hash are all rejected.
Data hardening
- New
campaign_recipient_client_contextstable with workspace-scoped RLS (SELECT-only forauthenticated), strict check constraints on currency / pratica id / lengths / dates ordering, and unique indexes on(workspace_id, campaign_recipient_id)and(workspace_id, campaign_id, contact_id). - Sanitizer clamps adults / children counts to 50, child ages to 0–17 with a ≤ 20 array cap, and drops any non-numeric pratica id.
Verification
pnpm --filter @repo/database typecheckpnpm --filter @repo/database testpnpm --filter app exec tsc --noEmitpnpm --filter app testpnpm exec biome check