feat(email): default template with inline logo + shop contact vars

Mirrors the layout from data/mail_template.png:
- Company name + greeting headline
- Body referencing the invoice number
- Inline logo (cid:invoice-logo) attached automatically
- Footer with mailto + website links

New template vars: {{shopEmail}}, {{shopWebsite}}.
Settings UI prefills empty fields with the defaults so users see and
can tweak them without losing the fallback.
This commit is contained in:
Gerhard Scheikl
2026-05-08 23:12:23 +02:00
parent 04933fcac6
commit 573dfbfd50
4 changed files with 123 additions and 17 deletions
+12 -4
View File
@@ -14,6 +14,12 @@ import {
storeUploadedLogo,
} from "../services/invoice/logoCache.server";
import { RichTextEditor } from "../components/RichTextEditor";
import {
DEFAULT_EMAIL_BODY_DE,
DEFAULT_EMAIL_BODY_EN,
DEFAULT_EMAIL_SUBJECT_DE,
DEFAULT_EMAIL_SUBJECT_EN,
} from "../services/invoice/emailTemplates";
interface SettingsFieldErrors {
vatId?: string;
@@ -376,13 +382,13 @@ export default function SettingsRoute() {
<Field
label="Subject (German)"
name="emailSubjectDe"
defaultValue={settings.emailSubjectDe}
defaultValue={settings.emailSubjectDe || DEFAULT_EMAIL_SUBJECT_DE}
helpText="Variables like {{invoiceNumber}} are substituted at send time."
/>
<RichTextEditor
name="emailBodyHtmlDe"
label="Body (German)"
defaultValue={settings.emailBodyHtmlDe}
defaultValue={settings.emailBodyHtmlDe || DEFAULT_EMAIL_BODY_DE}
variables={EMAIL_VARS}
minHeight={220}
/>
@@ -390,13 +396,13 @@ export default function SettingsRoute() {
<Field
label="Subject (English)"
name="emailSubjectEn"
defaultValue={settings.emailSubjectEn}
defaultValue={settings.emailSubjectEn || DEFAULT_EMAIL_SUBJECT_EN}
helpText="Variables like {{invoiceNumber}} are substituted at send time."
/>
<RichTextEditor
name="emailBodyHtmlEn"
label="Body (English)"
defaultValue={settings.emailBodyHtmlEn}
defaultValue={settings.emailBodyHtmlEn || DEFAULT_EMAIL_BODY_EN}
variables={EMAIL_VARS}
minHeight={220}
/>
@@ -425,6 +431,8 @@ const EMAIL_VARS = [
{ token: "{{dueDate}}" },
{ token: "{{companyName}}" },
{ token: "{{ownerName}}" },
{ token: "{{shopEmail}}" },
{ token: "{{shopWebsite}}" },
];
interface FieldProps {