feat(crm): add event_reminder_* templates to dev email tester
The pre-event reminder feature shipped with 5 seeded templates
(event_reminder_default + wedding/birthday/corporate/other) but the
CRM → Development "Send any CRM email to me" picker only listed the
quote/invoice/contract templates. Maintainer can now eyeball each
reminder category's body without staging a real event.
Backend:
- Extend TEMPLATES_KEYS in adminDev.js with all 5 reminder keys.
- Add event_date (today+2d), days_before (2), business_name (from
business_profile.legal_name) to the common payload so the
{{tokens}} in the reminder bodies resolve.
Frontend:
- Extend CrmEmailTemplateKey union.
- Add TEMPLATE_LABEL_KEYS entries.
- EN+DE i18n labels under crmDev.templates.label.event_reminder_*.
No PDF attachment — reminders are body-only emails (matches the
real flow).
This commit is contained in:
@@ -98,6 +98,16 @@ const TEMPLATES_KEYS = [
|
|||||||
'contract_sent',
|
'contract_sent',
|
||||||
'contract_signed_admin_notification',
|
'contract_signed_admin_notification',
|
||||||
'contract_fully_signed',
|
'contract_fully_signed',
|
||||||
|
// Pre-event reminder emails (migration 143). The runtime resolver
|
||||||
|
// picks `event_reminder_<events.event_type>` with fallback to
|
||||||
|
// `event_reminder_default`. The dev tester exposes every seeded
|
||||||
|
// template so the maintainer can eyeball each category's body
|
||||||
|
// without staging a real event for each.
|
||||||
|
'event_reminder_default',
|
||||||
|
'event_reminder_wedding',
|
||||||
|
'event_reminder_birthday',
|
||||||
|
'event_reminder_corporate',
|
||||||
|
'event_reminder_other',
|
||||||
];
|
];
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
@@ -355,11 +365,20 @@ async function buildPayloadFor(key, adminId, frontendUrl) {
|
|||||||
const today = new Date();
|
const today = new Date();
|
||||||
const dueDate = new Date(today.getTime() - 5 * 86400000);
|
const dueDate = new Date(today.getTime() - 5 * 86400000);
|
||||||
const validUntil = new Date(today.getTime() + 14 * 86400000);
|
const validUntil = new Date(today.getTime() + 14 * 86400000);
|
||||||
|
// Pre-event reminder mock: pretend the event is 2 days out (the
|
||||||
|
// global default for `crm_event_reminders_days_before`). Renders
|
||||||
|
// {{event_date}} + {{days_before}} for the event_reminder_*
|
||||||
|
// templates.
|
||||||
|
const eventDate = new Date(today.getTime() + 2 * 86400000);
|
||||||
|
const businessProfile = await db('business_profile').first().catch(() => null);
|
||||||
|
|
||||||
const common = {
|
const common = {
|
||||||
customer_name: 'Sample Customer',
|
customer_name: 'Sample Customer',
|
||||||
customer_email: '[email protected]',
|
customer_email: '[email protected]',
|
||||||
event_name: 'Sample Event',
|
event_name: 'Sample Event',
|
||||||
|
event_date: fakeShortDate(eventDate),
|
||||||
|
days_before: 2,
|
||||||
|
business_name: businessProfile?.legal_name || 'Sample Studio',
|
||||||
invoice_number: 'R-DEV-0001',
|
invoice_number: 'R-DEV-0001',
|
||||||
quote_number: 'Q-DEV-0001',
|
quote_number: 'Q-DEV-0001',
|
||||||
total_amount: fakeMoney(total, 'CHF'),
|
total_amount: fakeMoney(total, 'CHF'),
|
||||||
|
|||||||
@@ -3317,6 +3317,26 @@
|
|||||||
"contract_fully_signed": {
|
"contract_fully_signed": {
|
||||||
"title": "Vertrag beidseitig unterzeichnet",
|
"title": "Vertrag beidseitig unterzeichnet",
|
||||||
"description": "Beidseitiger Versand, sobald beide Unterschriften vorliegen. Gestempeltes Vertrags-PDF im Anhang."
|
"description": "Beidseitiger Versand, sobald beide Unterschriften vorliegen. Gestempeltes Vertrags-PDF im Anhang."
|
||||||
|
},
|
||||||
|
"event_reminder_default": {
|
||||||
|
"title": "Event-Erinnerung — Standard (Fallback)",
|
||||||
|
"description": "Vor-Event-Erinnerung, wenn keine kategoriespezifische Vorlage zutrifft."
|
||||||
|
},
|
||||||
|
"event_reminder_wedding": {
|
||||||
|
"title": "Event-Erinnerung — Hochzeit",
|
||||||
|
"description": "Vor-Event-Erinnerung für Events mit event_type=wedding."
|
||||||
|
},
|
||||||
|
"event_reminder_birthday": {
|
||||||
|
"title": "Event-Erinnerung — Geburtstag",
|
||||||
|
"description": "Vor-Event-Erinnerung für Events mit event_type=birthday."
|
||||||
|
},
|
||||||
|
"event_reminder_corporate": {
|
||||||
|
"title": "Event-Erinnerung — Firma",
|
||||||
|
"description": "Vor-Event-Erinnerung für Events mit event_type=corporate."
|
||||||
|
},
|
||||||
|
"event_reminder_other": {
|
||||||
|
"title": "Event-Erinnerung — Sonstiges",
|
||||||
|
"description": "Vor-Event-Erinnerung für Events mit event_type=other."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3317,6 +3317,26 @@
|
|||||||
"contract_fully_signed": {
|
"contract_fully_signed": {
|
||||||
"title": "Contract fully signed (dual-party)",
|
"title": "Contract fully signed (dual-party)",
|
||||||
"description": "Dual-party send once both signatures are in. Stamped contract PDF attached."
|
"description": "Dual-party send once both signatures are in. Stamped contract PDF attached."
|
||||||
|
},
|
||||||
|
"event_reminder_default": {
|
||||||
|
"title": "Event reminder — default (catch-all)",
|
||||||
|
"description": "Pre-event nudge used when no per-category template matches."
|
||||||
|
},
|
||||||
|
"event_reminder_wedding": {
|
||||||
|
"title": "Event reminder — wedding",
|
||||||
|
"description": "Pre-event nudge for events with event_type=wedding."
|
||||||
|
},
|
||||||
|
"event_reminder_birthday": {
|
||||||
|
"title": "Event reminder — birthday",
|
||||||
|
"description": "Pre-event nudge for events with event_type=birthday."
|
||||||
|
},
|
||||||
|
"event_reminder_corporate": {
|
||||||
|
"title": "Event reminder — corporate",
|
||||||
|
"description": "Pre-event nudge for events with event_type=corporate."
|
||||||
|
},
|
||||||
|
"event_reminder_other": {
|
||||||
|
"title": "Event reminder — other",
|
||||||
|
"description": "Pre-event nudge for events with event_type=other."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,36 @@ const TEMPLATE_LABEL_KEYS: Record<
|
|||||||
descKey: 'crmDev.templates.label.contract_fully_signed.description',
|
descKey: 'crmDev.templates.label.contract_fully_signed.description',
|
||||||
descFallback: 'Dual-party send once both signatures are in. Stamped contract PDF attached.',
|
descFallback: 'Dual-party send once both signatures are in. Stamped contract PDF attached.',
|
||||||
},
|
},
|
||||||
|
event_reminder_default: {
|
||||||
|
titleKey: 'crmDev.templates.label.event_reminder_default.title',
|
||||||
|
titleFallback: 'Event reminder — default (catch-all)',
|
||||||
|
descKey: 'crmDev.templates.label.event_reminder_default.description',
|
||||||
|
descFallback: 'Pre-event nudge used when no per-category template matches.',
|
||||||
|
},
|
||||||
|
event_reminder_wedding: {
|
||||||
|
titleKey: 'crmDev.templates.label.event_reminder_wedding.title',
|
||||||
|
titleFallback: 'Event reminder — wedding',
|
||||||
|
descKey: 'crmDev.templates.label.event_reminder_wedding.description',
|
||||||
|
descFallback: 'Pre-event nudge for events with event_type=wedding.',
|
||||||
|
},
|
||||||
|
event_reminder_birthday: {
|
||||||
|
titleKey: 'crmDev.templates.label.event_reminder_birthday.title',
|
||||||
|
titleFallback: 'Event reminder — birthday',
|
||||||
|
descKey: 'crmDev.templates.label.event_reminder_birthday.description',
|
||||||
|
descFallback: 'Pre-event nudge for events with event_type=birthday.',
|
||||||
|
},
|
||||||
|
event_reminder_corporate: {
|
||||||
|
titleKey: 'crmDev.templates.label.event_reminder_corporate.title',
|
||||||
|
titleFallback: 'Event reminder — corporate',
|
||||||
|
descKey: 'crmDev.templates.label.event_reminder_corporate.description',
|
||||||
|
descFallback: 'Pre-event nudge for events with event_type=corporate.',
|
||||||
|
},
|
||||||
|
event_reminder_other: {
|
||||||
|
titleKey: 'crmDev.templates.label.event_reminder_other.title',
|
||||||
|
titleFallback: 'Event reminder — other',
|
||||||
|
descKey: 'crmDev.templates.label.event_reminder_other.description',
|
||||||
|
descFallback: 'Pre-event nudge for events with event_type=other.',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CrmDevelopmentPage: React.FC = () => {
|
export const CrmDevelopmentPage: React.FC = () => {
|
||||||
|
|||||||
@@ -22,7 +22,16 @@ export type CrmEmailTemplateKey =
|
|||||||
// template_key strings.
|
// template_key strings.
|
||||||
| 'contract_sent'
|
| 'contract_sent'
|
||||||
| 'contract_signed_admin_notification'
|
| 'contract_signed_admin_notification'
|
||||||
| 'contract_fully_signed';
|
| 'contract_fully_signed'
|
||||||
|
// Pre-event reminder emails (migration 143). One template per
|
||||||
|
// event_type with a catch-all default. Backend resolves
|
||||||
|
// `event_reminder_<event_type>` then falls back to
|
||||||
|
// `event_reminder_default`.
|
||||||
|
| 'event_reminder_default'
|
||||||
|
| 'event_reminder_wedding'
|
||||||
|
| 'event_reminder_birthday'
|
||||||
|
| 'event_reminder_corporate'
|
||||||
|
| 'event_reminder_other';
|
||||||
|
|
||||||
export interface DevEmailTemplateStatus {
|
export interface DevEmailTemplateStatus {
|
||||||
key: CrmEmailTemplateKey;
|
key: CrmEmailTemplateKey;
|
||||||
|
|||||||
Reference in New Issue
Block a user