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
import {
SettingsFormCard ,
type SettingFieldDef ,
} from '@/components/admin/shared/settings-form-card' ;
feat(mobile): swap admin page headers to PageHeader
Mechanical sweep replacing the plain h1+p header markup with the
mobile-aware PageHeader primitive across 12 admin pages: index,
backup, branding, documenso, email, import, invitations, monitoring,
onboarding, reminders, reports, webhooks. Webhooks "Add Webhook"
button preserved via the actions slot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 12:57:52 +02:00
import { PageHeader } from '@/components/shared/page-header' ;
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
const FIELDS : SettingFieldDef [ ] = [
{
key : 'email_from_name' ,
label : 'From name' ,
description : 'Display name shown in the From: header on outgoing email.' ,
type : 'string' ,
placeholder : 'Port Nimara' ,
defaultValue : '' ,
} ,
{
key : 'email_from_address' ,
label : 'From address' ,
description : 'Sender email address. Falls back to SMTP_FROM env when blank.' ,
type : 'string' ,
placeholder : 'noreply@example.com' ,
defaultValue : '' ,
} ,
{
key : 'email_reply_to' ,
label : 'Reply-to address' ,
description : 'Optional Reply-To: header for replies (e.g. sales@example.com).' ,
type : 'string' ,
placeholder : 'sales@example.com' ,
defaultValue : '' ,
} ,
{
key : 'email_signature_html' ,
label : 'Default signature (HTML)' ,
description : 'Appended to the bottom of system-generated emails.' ,
type : 'html' ,
placeholder : '<p>—<br>The Port Nimara team</p>' ,
defaultValue : '' ,
} ,
{
key : 'email_footer_html' ,
label : 'Email footer (HTML)' ,
description : 'Legal/contact footer rendered at the very bottom of all emails.' ,
type : 'html' ,
placeholder : '<p style="font-size:11px;color:#888;">© Port Nimara · ul. ...</p>' ,
defaultValue : '' ,
} ,
{
key : 'smtp_host_override' ,
label : 'SMTP host override' ,
description : 'Optional. Falls back to SMTP_HOST env when blank.' ,
type : 'string' ,
placeholder : 'mail.example.com' ,
defaultValue : '' ,
} ,
{
key : 'smtp_port_override' ,
label : 'SMTP port override' ,
description : 'Optional. Falls back to SMTP_PORT env when blank.' ,
type : 'number' ,
placeholder : '587' ,
defaultValue : null ,
} ,
{
key : 'smtp_user_override' ,
label : 'SMTP username override' ,
description : 'Optional. Falls back to SMTP_USER env when blank.' ,
type : 'string' ,
defaultValue : '' ,
} ,
{
key : 'smtp_pass_override' ,
label : 'SMTP password override' ,
description : 'Optional. Stored in plain text — only set when overriding env credentials.' ,
type : 'password' ,
defaultValue : '' ,
} ,
] ;
export default function EmailSettingsPage() {
return (
< div className = "space-y-6" >
feat(mobile): swap admin page headers to PageHeader
Mechanical sweep replacing the plain h1+p header markup with the
mobile-aware PageHeader primitive across 12 admin pages: index,
backup, branding, documenso, email, import, invitations, monitoring,
onboarding, reminders, reports, webhooks. Webhooks "Add Webhook"
button preserved via the actions slot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 12:57:52 +02:00
< 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."
/ >
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
< SettingsFormCard
title = "From address & signature"
description = "Identity headers and shared HTML used by system-generated emails."
fields = { FIELDS . slice ( 0 , 5 ) }
/ >
< SettingsFormCard
title = "SMTP transport overrides"
description = "Optional per-port SMTP credentials. Leave blank to use the global env defaults."
fields = { FIELDS . slice ( 5 ) }
/ >
< / div >
) ;
}