Files
pn-new-crm/src/app/(dashboard)/[portSlug]/admin/email/page.tsx

62 lines
3.1 KiB
TypeScript
Raw Normal View History

feat(uat-batch): Groups F + G + H — DocsHub/signing + admin consolidation + email F27–F29, G30, G31, H32, H33 from the 2026-05-21 plan. Shipped now: F28 Past-milestones expandable history. The Past strip on the Interest overview becomes an <Accordion> — each row collapses to the same one-line summary as before, expands to render the full <MilestoneSection> (steps list, sub-status, inline doc actions). Reuses the existing MilestoneSection so no new per-milestone rendering needs to be maintained. F29 Watchers configurable at document creation time. The unified create-document wizard gets a Watchers section with a multi-select checkbox list backed by /api/v1/admin/users/picker. Selected user ids are sent in the `watchers` array on the POST (replacing the prior hardcoded `[]`). UI matches the post-creation WatchersCard so reps see the same identity rows regardless of entry point. G30 /admin/invitations merged into /admin/users. The Users page now wraps the existing UserList + InvitationsManager in a Tabs control (Active users / Invitations). The standalone /admin/invitations route returns a redirect to the merged page for bookmark back-compat. Removed nav catalog entry + admin-sections-browser tile; extended the Users catalog keywords with "invitations / pending invites / onboarding" so command-K search still lands on the right surface. G31 /admin/ai picks up the berth-PDF-parser section + a "planned AI surfaces" placeholder. Berth PDF parser remains env-configured today; the page now documents it so admins don't hunt for the controls. Closes the "where do I configure AI?" loop. H32 Email settings explainer panel above the SMTP cards. Spells out why noreply + sales have separate credentials and which workflows ship from each mailbox. Existing field titles gained the "(noreply)" suffix so the model maps cleanly. H33 Supplemental-info-request email rebuilt to use the shared branded shell (logo + blurred overhead background + max- width 600 table layout) instead of the prior plain-HTML page. Per-port branding (logo / primary color / background / header / footer) flows from getPortBrandingConfig. CTA button picks up the port's primary color. Already shipped (verified pre-shipped): F27 DocumentsHub root view already hides the breadcrumb via `selectedFolderId !== undefined` conditional. Verified: tsc clean, vitest 1454/1454. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:40:48 +02:00
import { Info } from 'lucide-react';
import { PageHeader } from '@/components/shared/page-header';
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
import { RegistryDrivenForm } from '@/components/admin/shared/registry-driven-form';
feat(emails): sales send-out flows + brochures + email-from settings Phase 7 of the berth-recommender refactor (plan §3.3, §4.8, §4.9, §5.7, §5.8, §5.9, §11.1, §14.7, §14.9). Adds the rep-driven send-out path for per-berth PDFs and port-wide brochures, the per-port sales SMTP/IMAP config + body templates, and the supporting admin UI. Migration: 0031_brochures_and_document_sends.sql Schema additions: - brochures (port-wide, with isDefault marker + archive) - brochure_versions (versioned uploads, storageKey per §4.7a) - document_sends (audit log of every rep-initiated send; failures captured with failedAt + errorReason). berthPdfVersionId is a plain text column (no FK) — loose-coupled to Phase 6b's berth_pdf_versions so the two phases stay independent. §14.7 critical mitigations: - Body XSS: rep-authored markdown goes through renderEmailBody() (HTML-escape first, then a tight allowlist of bold/italic/code/link rules). https:// + mailto: only — javascript:/data: URLs stripped. Tested against script/img/iframe/svg/onerror polyglots. - Recipient typo: strict email regex + two-step confirm modal that shows the exact recipient before send. - Unresolved merge fields: pre-send dry-run /preview endpoint blocks submission until findUnresolvedTokens() returns empty. - SMTP failure: every transport rejection writes a document_sends row with failedAt + errorReason; UI surfaces the message. - Hourly per-user rate limit: 50 sends/user/hour via existing checkRateLimit(). - Size threshold fallback (§11.1): files above email_attach_threshold_mb (default 15) ship as a 24h signed-URL download link in the body instead of an attachment. Storage stream flows directly to nodemailer to avoid buffering 20MB+. §14.10 critical mitigation: - SMTP/IMAP passwords encrypted at rest via the existing EMAIL_CREDENTIAL_KEY (AES-256-GCM). The /api/v1/admin/email/ sales-config GET endpoint never returns the decrypted value — only a *PassIsSet boolean. PATCH treats empty string as "leave unchanged" and explicit null as "clear", so the masked-placeholder UI round- trips without forcing re-entry on every save. system_settings keys (per-port unless noted): - sales_from_address, sales_smtp_{host,port,secure,user,pass_encrypted} - sales_imap_{host,port,user,pass_encrypted} - sales_auth_method (default app_password) - noreply_from_address - email_template_send_berth_pdf_body, email_template_send_brochure_body - brochure_max_upload_mb (default 50) - email_attach_threshold_mb (default 15) UI surfaces (per §5.7, §5.8, §5.9): - <SendDocumentDialog> shared 2-step compose+confirm flow. - <SendBerthPdfDialog>, <SendDocumentsDialog>, <SendFromInterestButton> wrappers per detail page. - /[portSlug]/admin/brochures: list, upload (direct-to-storage presigned PUT for the 20MB+ files per §11.1), default toggle, archive. - /[portSlug]/admin/email extended with <SalesEmailConfigCard>: SMTP + IMAP creds, body templates, threshold/max settings. Storage: every upload + download goes through getStorageBackend() — no direct minio imports, per Phase 6a contract. Tests: 1145 vitest passing (+ 50 new in markdown-email-sanitization.test.ts, document-sends-validators.test.ts, sales-email-config-validators.test.ts). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 03:38:47 +02:00
import { SalesEmailConfigCard } from '@/components/admin/sales-email-config-card';
import { EmailRoutingCard } from '@/components/admin/email-routing-card';
import { SmtpTestSendCard } from '@/components/admin/email/smtp-test-send-card';
import { TestTemplateCard } from '@/components/admin/email/test-template-card';
feat(admin): per-port email/Documenso/branding/reminder settings + invitations Centralizes everything operators need to configure into the admin panel, each setting per-port with env fallback. New admin pages - /admin landing page linking to every admin section as a card - /admin/email FROM name+address, reply-to, signature/footer HTML, optional SMTP host/port/user/pass override - /admin/documenso API URL+key override, EOI Documenso template ID, default EOI pathway (documenso-template vs inapp), "Test connection" button - /admin/branding logo URL, primary color, app name, email header/footer HTML - /admin/reminders port-level defaults for new interests + port-wide daily-digest delivery window - /admin/invitations send / list / resend / revoke CRM invitations Per-user reminder digest - /notifications/preferences gains a Reminder digest card: immediate / daily / weekly / off, with HH:MM, day-of-week, IANA timezone fields. Stored in user_profiles.preferences.reminders. Plumbing - port-config.ts typed accessors (getPortEmailConfig, getPortDocumensoConfig, getPortBrandingConfig, getPortReminderConfig) — settings → env fallback. - sendEmail accepts optional portId; resolves From/SMTP from settings when supplied. - documensoFetch + downloadSignedPdf accept optional portId; each public function takes it through. checkDocumensoHealth() backs the test button. - crm-invite.service gains listCrmInvites / revokeCrmInvite / resendCrmInvite with audit-log entries (revoke_invite, resend_invite added to AuditAction). - AdminLandingPage card grid + shared SettingsFormCard component to remove per-page form boilerplate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 23:21:54 +02:00
export default function EmailSettingsPage() {
return (
<div className="space-y-6">
<PageHeader
title="Email Settings"
description="Per-port outgoing email configuration. SMTP credentials and the From address default to environment variables when these fields are blank. Header/footer HTML lives under Branding."
/>
feat(uat-batch): Groups F + G + H — DocsHub/signing + admin consolidation + email F27–F29, G30, G31, H32, H33 from the 2026-05-21 plan. Shipped now: F28 Past-milestones expandable history. The Past strip on the Interest overview becomes an <Accordion> — each row collapses to the same one-line summary as before, expands to render the full <MilestoneSection> (steps list, sub-status, inline doc actions). Reuses the existing MilestoneSection so no new per-milestone rendering needs to be maintained. F29 Watchers configurable at document creation time. The unified create-document wizard gets a Watchers section with a multi-select checkbox list backed by /api/v1/admin/users/picker. Selected user ids are sent in the `watchers` array on the POST (replacing the prior hardcoded `[]`). UI matches the post-creation WatchersCard so reps see the same identity rows regardless of entry point. G30 /admin/invitations merged into /admin/users. The Users page now wraps the existing UserList + InvitationsManager in a Tabs control (Active users / Invitations). The standalone /admin/invitations route returns a redirect to the merged page for bookmark back-compat. Removed nav catalog entry + admin-sections-browser tile; extended the Users catalog keywords with "invitations / pending invites / onboarding" so command-K search still lands on the right surface. G31 /admin/ai picks up the berth-PDF-parser section + a "planned AI surfaces" placeholder. Berth PDF parser remains env-configured today; the page now documents it so admins don't hunt for the controls. Closes the "where do I configure AI?" loop. H32 Email settings explainer panel above the SMTP cards. Spells out why noreply + sales have separate credentials and which workflows ship from each mailbox. Existing field titles gained the "(noreply)" suffix so the model maps cleanly. H33 Supplemental-info-request email rebuilt to use the shared branded shell (logo + blurred overhead background + max- width 600 table layout) instead of the prior plain-HTML page. Per-port branding (logo / primary color / background / header / footer) flows from getPortBrandingConfig. CTA button picks up the port's primary color. Already shipped (verified pre-shipped): F27 DocumentsHub root view already hides the breadcrumb via `selectedFolderId !== undefined` conditional. Verified: tsc clean, vitest 1454/1454. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:40:48 +02:00
{/* Explainer for the "two accounts" model - addresses the recurring
feat(uat-batch): Groups F + G + H — DocsHub/signing + admin consolidation + email F27–F29, G30, G31, H32, H33 from the 2026-05-21 plan. Shipped now: F28 Past-milestones expandable history. The Past strip on the Interest overview becomes an <Accordion> — each row collapses to the same one-line summary as before, expands to render the full <MilestoneSection> (steps list, sub-status, inline doc actions). Reuses the existing MilestoneSection so no new per-milestone rendering needs to be maintained. F29 Watchers configurable at document creation time. The unified create-document wizard gets a Watchers section with a multi-select checkbox list backed by /api/v1/admin/users/picker. Selected user ids are sent in the `watchers` array on the POST (replacing the prior hardcoded `[]`). UI matches the post-creation WatchersCard so reps see the same identity rows regardless of entry point. G30 /admin/invitations merged into /admin/users. The Users page now wraps the existing UserList + InvitationsManager in a Tabs control (Active users / Invitations). The standalone /admin/invitations route returns a redirect to the merged page for bookmark back-compat. Removed nav catalog entry + admin-sections-browser tile; extended the Users catalog keywords with "invitations / pending invites / onboarding" so command-K search still lands on the right surface. G31 /admin/ai picks up the berth-PDF-parser section + a "planned AI surfaces" placeholder. Berth PDF parser remains env-configured today; the page now documents it so admins don't hunt for the controls. Closes the "where do I configure AI?" loop. H32 Email settings explainer panel above the SMTP cards. Spells out why noreply + sales have separate credentials and which workflows ship from each mailbox. Existing field titles gained the "(noreply)" suffix so the model maps cleanly. H33 Supplemental-info-request email rebuilt to use the shared branded shell (logo + blurred overhead background + max- width 600 table layout) instead of the prior plain-HTML page. Per-port branding (logo / primary color / background / header / footer) flows from getPortBrandingConfig. CTA button picks up the port's primary color. Already shipped (verified pre-shipped): F27 DocumentsHub root view already hides the breadcrumb via `selectedFolderId !== undefined` conditional. Verified: tsc clean, vitest 1454/1454. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:40:48 +02:00
UAT question "why are there separate SMTP credentials for sales
and noreply?". Keeps the answer in front of the admin before
they reach the per-card form below. */}
<div className="rounded-md border border-border bg-muted/40 px-4 py-3 text-sm">
<div className="flex items-start gap-2">
<Info className="mt-0.5 size-4 shrink-0 text-muted-foreground" aria-hidden />
<div className="space-y-1 text-muted-foreground">
<p>
<strong className="text-foreground">Why two accounts?</strong> Transactional emails
(signing invites, notifications, password resets) ship from your noreply mailbox over
the SMTP credentials below. Rep-authored sales emails (one-off messages, proposal
sends) ship from the sales mailbox with separate credentials so replies land in a
human-monitored inbox.
</p>
<p>
The noreply credentials are also used by the supplemental-info workflow + portal
activation, i.e. anywhere the platform sends on its own initiative. The sales
credentials are only used when a rep clicks Send in the compose UI.
</p>
</div>
</div>
</div>
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
{/* Registry-driven so each field shows the "Using env fallback /
port / global / default" badge inline - admins can tell at a
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
glance which fields are coming from .env vs. UI overrides. */}
<RegistryDrivenForm
sections={['email.from']}
feat(uat-batch): Groups F + G + H — DocsHub/signing + admin consolidation + email F27–F29, G30, G31, H32, H33 from the 2026-05-21 plan. Shipped now: F28 Past-milestones expandable history. The Past strip on the Interest overview becomes an <Accordion> — each row collapses to the same one-line summary as before, expands to render the full <MilestoneSection> (steps list, sub-status, inline doc actions). Reuses the existing MilestoneSection so no new per-milestone rendering needs to be maintained. F29 Watchers configurable at document creation time. The unified create-document wizard gets a Watchers section with a multi-select checkbox list backed by /api/v1/admin/users/picker. Selected user ids are sent in the `watchers` array on the POST (replacing the prior hardcoded `[]`). UI matches the post-creation WatchersCard so reps see the same identity rows regardless of entry point. G30 /admin/invitations merged into /admin/users. The Users page now wraps the existing UserList + InvitationsManager in a Tabs control (Active users / Invitations). The standalone /admin/invitations route returns a redirect to the merged page for bookmark back-compat. Removed nav catalog entry + admin-sections-browser tile; extended the Users catalog keywords with "invitations / pending invites / onboarding" so command-K search still lands on the right surface. G31 /admin/ai picks up the berth-PDF-parser section + a "planned AI surfaces" placeholder. Berth PDF parser remains env-configured today; the page now documents it so admins don't hunt for the controls. Closes the "where do I configure AI?" loop. H32 Email settings explainer panel above the SMTP cards. Spells out why noreply + sales have separate credentials and which workflows ship from each mailbox. Existing field titles gained the "(noreply)" suffix so the model maps cleanly. H33 Supplemental-info-request email rebuilt to use the shared branded shell (logo + blurred overhead background + max- width 600 table layout) instead of the prior plain-HTML page. Per-port branding (logo / primary color / background / header / footer) flows from getPortBrandingConfig. CTA button picks up the port's primary color. Already shipped (verified pre-shipped): F27 DocumentsHub root view already hides the breadcrumb via `selectedFolderId !== undefined` conditional. Verified: tsc clean, vitest 1454/1454. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:40:48 +02:00
title="From address (noreply)"
description="Identity headers used by system-generated emails. Set the From + Reply-To here; the matching SMTP credentials live in the next card."
feat(admin): per-port email/Documenso/branding/reminder settings + invitations Centralizes everything operators need to configure into the admin panel, each setting per-port with env fallback. New admin pages - /admin landing page linking to every admin section as a card - /admin/email FROM name+address, reply-to, signature/footer HTML, optional SMTP host/port/user/pass override - /admin/documenso API URL+key override, EOI Documenso template ID, default EOI pathway (documenso-template vs inapp), "Test connection" button - /admin/branding logo URL, primary color, app name, email header/footer HTML - /admin/reminders port-level defaults for new interests + port-wide daily-digest delivery window - /admin/invitations send / list / resend / revoke CRM invitations Per-user reminder digest - /notifications/preferences gains a Reminder digest card: immediate / daily / weekly / off, with HH:MM, day-of-week, IANA timezone fields. Stored in user_profiles.preferences.reminders. Plumbing - port-config.ts typed accessors (getPortEmailConfig, getPortDocumensoConfig, getPortBrandingConfig, getPortReminderConfig) — settings → env fallback. - sendEmail accepts optional portId; resolves From/SMTP from settings when supplied. - documensoFetch + downloadSignedPdf accept optional portId; each public function takes it through. checkDocumensoHealth() backs the test button. - crm-invite.service gains listCrmInvites / revokeCrmInvite / resendCrmInvite with audit-log entries (revoke_invite, resend_invite added to AuditAction). - AdminLandingPage card grid + shared SettingsFormCard component to remove per-page form boilerplate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 23:21:54 +02:00
/>
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
<RegistryDrivenForm
sections={['email.smtp']}
feat(uat-batch): Groups F + G + H — DocsHub/signing + admin consolidation + email F27–F29, G30, G31, H32, H33 from the 2026-05-21 plan. Shipped now: F28 Past-milestones expandable history. The Past strip on the Interest overview becomes an <Accordion> — each row collapses to the same one-line summary as before, expands to render the full <MilestoneSection> (steps list, sub-status, inline doc actions). Reuses the existing MilestoneSection so no new per-milestone rendering needs to be maintained. F29 Watchers configurable at document creation time. The unified create-document wizard gets a Watchers section with a multi-select checkbox list backed by /api/v1/admin/users/picker. Selected user ids are sent in the `watchers` array on the POST (replacing the prior hardcoded `[]`). UI matches the post-creation WatchersCard so reps see the same identity rows regardless of entry point. G30 /admin/invitations merged into /admin/users. The Users page now wraps the existing UserList + InvitationsManager in a Tabs control (Active users / Invitations). The standalone /admin/invitations route returns a redirect to the merged page for bookmark back-compat. Removed nav catalog entry + admin-sections-browser tile; extended the Users catalog keywords with "invitations / pending invites / onboarding" so command-K search still lands on the right surface. G31 /admin/ai picks up the berth-PDF-parser section + a "planned AI surfaces" placeholder. Berth PDF parser remains env-configured today; the page now documents it so admins don't hunt for the controls. Closes the "where do I configure AI?" loop. H32 Email settings explainer panel above the SMTP cards. Spells out why noreply + sales have separate credentials and which workflows ship from each mailbox. Existing field titles gained the "(noreply)" suffix so the model maps cleanly. H33 Supplemental-info-request email rebuilt to use the shared branded shell (logo + blurred overhead background + max- width 600 table layout) instead of the prior plain-HTML page. Per-port branding (logo / primary color / background / header / footer) flows from getPortBrandingConfig. CTA button picks up the port's primary color. Already shipped (verified pre-shipped): F27 DocumentsHub root view already hides the breadcrumb via `selectedFolderId !== undefined` conditional. Verified: tsc clean, vitest 1454/1454. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:40:48 +02:00
title="SMTP transport overrides (noreply)"
description="Optional per-port SMTP credentials for the noreply mailbox. Leave blank to use the global env defaults. Each field shows its current source (env / port / default) so you can tell what's active without checking the deploy."
feat(admin): per-port email/Documenso/branding/reminder settings + invitations Centralizes everything operators need to configure into the admin panel, each setting per-port with env fallback. New admin pages - /admin landing page linking to every admin section as a card - /admin/email FROM name+address, reply-to, signature/footer HTML, optional SMTP host/port/user/pass override - /admin/documenso API URL+key override, EOI Documenso template ID, default EOI pathway (documenso-template vs inapp), "Test connection" button - /admin/branding logo URL, primary color, app name, email header/footer HTML - /admin/reminders port-level defaults for new interests + port-wide daily-digest delivery window - /admin/invitations send / list / resend / revoke CRM invitations Per-user reminder digest - /notifications/preferences gains a Reminder digest card: immediate / daily / weekly / off, with HH:MM, day-of-week, IANA timezone fields. Stored in user_profiles.preferences.reminders. Plumbing - port-config.ts typed accessors (getPortEmailConfig, getPortDocumensoConfig, getPortBrandingConfig, getPortReminderConfig) — settings → env fallback. - sendEmail accepts optional portId; resolves From/SMTP from settings when supplied. - documensoFetch + downloadSignedPdf accept optional portId; each public function takes it through. checkDocumensoHealth() backs the test button. - crm-invite.service gains listCrmInvites / revokeCrmInvite / resendCrmInvite with audit-log entries (revoke_invite, resend_invite added to AuditAction). - AdminLandingPage card grid + shared SettingsFormCard component to remove per-page form boilerplate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 23:21:54 +02:00
/>
<SmtpTestSendCard />
<TestTemplateCard />
feat(emails): sales send-out flows + brochures + email-from settings Phase 7 of the berth-recommender refactor (plan §3.3, §4.8, §4.9, §5.7, §5.8, §5.9, §11.1, §14.7, §14.9). Adds the rep-driven send-out path for per-berth PDFs and port-wide brochures, the per-port sales SMTP/IMAP config + body templates, and the supporting admin UI. Migration: 0031_brochures_and_document_sends.sql Schema additions: - brochures (port-wide, with isDefault marker + archive) - brochure_versions (versioned uploads, storageKey per §4.7a) - document_sends (audit log of every rep-initiated send; failures captured with failedAt + errorReason). berthPdfVersionId is a plain text column (no FK) — loose-coupled to Phase 6b's berth_pdf_versions so the two phases stay independent. §14.7 critical mitigations: - Body XSS: rep-authored markdown goes through renderEmailBody() (HTML-escape first, then a tight allowlist of bold/italic/code/link rules). https:// + mailto: only — javascript:/data: URLs stripped. Tested against script/img/iframe/svg/onerror polyglots. - Recipient typo: strict email regex + two-step confirm modal that shows the exact recipient before send. - Unresolved merge fields: pre-send dry-run /preview endpoint blocks submission until findUnresolvedTokens() returns empty. - SMTP failure: every transport rejection writes a document_sends row with failedAt + errorReason; UI surfaces the message. - Hourly per-user rate limit: 50 sends/user/hour via existing checkRateLimit(). - Size threshold fallback (§11.1): files above email_attach_threshold_mb (default 15) ship as a 24h signed-URL download link in the body instead of an attachment. Storage stream flows directly to nodemailer to avoid buffering 20MB+. §14.10 critical mitigation: - SMTP/IMAP passwords encrypted at rest via the existing EMAIL_CREDENTIAL_KEY (AES-256-GCM). The /api/v1/admin/email/ sales-config GET endpoint never returns the decrypted value — only a *PassIsSet boolean. PATCH treats empty string as "leave unchanged" and explicit null as "clear", so the masked-placeholder UI round- trips without forcing re-entry on every save. system_settings keys (per-port unless noted): - sales_from_address, sales_smtp_{host,port,secure,user,pass_encrypted} - sales_imap_{host,port,user,pass_encrypted} - sales_auth_method (default app_password) - noreply_from_address - email_template_send_berth_pdf_body, email_template_send_brochure_body - brochure_max_upload_mb (default 50) - email_attach_threshold_mb (default 15) UI surfaces (per §5.7, §5.8, §5.9): - <SendDocumentDialog> shared 2-step compose+confirm flow. - <SendBerthPdfDialog>, <SendDocumentsDialog>, <SendFromInterestButton> wrappers per detail page. - /[portSlug]/admin/brochures: list, upload (direct-to-storage presigned PUT for the 20MB+ files per §11.1), default toggle, archive. - /[portSlug]/admin/email extended with <SalesEmailConfigCard>: SMTP + IMAP creds, body templates, threshold/max settings. Storage: every upload + download goes through getStorageBackend() — no direct minio imports, per Phase 6a contract. Tests: 1145 vitest passing (+ 50 new in markdown-email-sanitization.test.ts, document-sends-validators.test.ts, sales-email-config-validators.test.ts). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 03:38:47 +02:00
<SalesEmailConfigCard />
<EmailRoutingCard />
feat(admin): per-port email/Documenso/branding/reminder settings + invitations Centralizes everything operators need to configure into the admin panel, each setting per-port with env fallback. New admin pages - /admin landing page linking to every admin section as a card - /admin/email FROM name+address, reply-to, signature/footer HTML, optional SMTP host/port/user/pass override - /admin/documenso API URL+key override, EOI Documenso template ID, default EOI pathway (documenso-template vs inapp), "Test connection" button - /admin/branding logo URL, primary color, app name, email header/footer HTML - /admin/reminders port-level defaults for new interests + port-wide daily-digest delivery window - /admin/invitations send / list / resend / revoke CRM invitations Per-user reminder digest - /notifications/preferences gains a Reminder digest card: immediate / daily / weekly / off, with HH:MM, day-of-week, IANA timezone fields. Stored in user_profiles.preferences.reminders. Plumbing - port-config.ts typed accessors (getPortEmailConfig, getPortDocumensoConfig, getPortBrandingConfig, getPortReminderConfig) — settings → env fallback. - sendEmail accepts optional portId; resolves From/SMTP from settings when supplied. - documensoFetch + downloadSignedPdf accept optional portId; each public function takes it through. checkDocumensoHealth() backs the test button. - crm-invite.service gains listCrmInvites / revokeCrmInvite / resendCrmInvite with audit-log entries (revoke_invite, resend_invite added to AuditAction). - AdminLandingPage card grid + shared SettingsFormCard component to remove per-page form boilerplate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 23:21:54 +02:00
</div>
);
}