@hartl-services/medusa-base 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/src/admin/index.js +117 -1
- package/.medusa/server/src/admin/index.mjs +118 -2
- package/.medusa/server/src/documents/plugin-features.js +15 -1
- package/.medusa/server/src/documents/templates/financial-document.js +69 -51
- package/.medusa/server/src/documents/templates/format.js +44 -2
- package/.medusa/server/src/documents/templates/html-factory.js +7 -5
- package/.medusa/server/src/documents/templates/html-utils.js +12 -2
- package/.medusa/server/src/documents/templates/layout.js +115 -70
- package/.medusa/server/src/documents/templates/packing-list.js +10 -8
- package/.medusa/server/src/documents/utils/document-render-settings.js +59 -0
- package/.medusa/server/src/email/application/handle-invite-event.js +8 -2
- package/.medusa/server/src/email/application/handle-password-reset-event.js +16 -4
- package/.medusa/server/src/email/domain/order-summary.js +103 -0
- package/.medusa/server/src/email/domain/refund-summary.js +43 -0
- package/.medusa/server/src/email/domain/return-summary.js +118 -0
- package/.medusa/server/src/email/domain/shipment-summary.js +74 -0
- package/.medusa/server/src/email/plugin-features.js +9 -1
- package/.medusa/server/src/email/rendering/default-templates.js +693 -65
- package/.medusa/server/src/email/rendering/load-email-shop-details.js +36 -0
- package/.medusa/server/src/email/rendering/render-email.js +16 -3
- package/.medusa/server/src/email/rendering/resolve-shop-configuration.js +20 -1
- package/.medusa/server/src/email/types/template-data.js +155 -2
- package/.medusa/server/src/modules/plugin-features/domain/plugin-settings.js +4 -3
- package/.medusa/server/src/public/descriptor.js +3 -1
- package/.medusa/server/src/public/shop-master-data.js +2 -2
- package/.medusa/server/src/shop-config/build-food-shop-config.js +5 -1
- package/.medusa/server/src/shop-config/options.js +5 -1
- package/.medusa/server/src/workflows/documents/issue-document.js +4 -2
- package/.medusa/server/src/workflows/documents/issue-packing-list.js +8 -2
- package/.medusa/server/src/workflows/email/dispatch-email-message.js +4 -2
- package/.medusa/server/src/workflows/email/prepare-order-canceled-email.js +42 -4
- package/.medusa/server/src/workflows/email/prepare-order-received-email.js +31 -5
- package/.medusa/server/src/workflows/email/prepare-refund-email.js +25 -1
- package/.medusa/server/src/workflows/email/prepare-return-email.js +64 -3
- package/.medusa/server/src/workflows/email/prepare-shipment-email.js +36 -3
- package/README.md +166 -9
- package/dist/admin-components/features/plugin-features/components/ImageSettingInput.d.ts +15 -0
- package/dist/admin-components/features/plugin-features/components/ImageSettingInput.js +42 -0
- package/dist/admin-components/features/plugin-features/components/PluginSettingsForm.js +4 -2
- package/dist/admin-components/features/plugin-features/hooks/usePluginFeatureEnabled.d.ts +1 -1
- package/dist/admin-components/features/plugin-features/hooks/usePluginFeatures.d.ts +4 -4
- package/dist/admin-components/features/plugin-features/hooks/usePluginSettingsForm.d.ts +1 -1
- package/dist/admin-components/features/plugin-features/lib/image-upload.d.ts +6 -0
- package/dist/admin-components/features/plugin-features/lib/image-upload.js +17 -0
- package/dist/admin-components/features/plugin-features/lib/plugin-features-admin-client.d.ts +4 -4
- package/dist/admin-components/i18n/texts.d.ts +6 -0
- package/dist/admin-components/i18n/texts.js +6 -0
- package/dist/documents/templates/financial-document.d.ts +3 -3
- package/dist/documents/templates/financial-document.js +68 -50
- package/dist/documents/templates/format.d.ts +14 -0
- package/dist/documents/templates/format.js +43 -1
- package/dist/documents/templates/html-factory.d.ts +6 -2
- package/dist/documents/templates/html-factory.js +6 -4
- package/dist/documents/templates/html-utils.d.ts +6 -0
- package/dist/documents/templates/html-utils.js +11 -1
- package/dist/documents/templates/layout.d.ts +21 -3
- package/dist/documents/templates/layout.js +114 -69
- package/dist/documents/templates/packing-list.js +9 -7
- package/dist/documents/types/documents.d.ts +17 -0
- package/dist/documents/types/html-factory.d.ts +1 -0
- package/dist/email/types/shop-configuration.d.ts +25 -0
- package/dist/email/types/template-data.d.ts +509 -0
- package/dist/email/types/template-data.js +154 -1
- package/dist/public/descriptor.js +2 -0
- package/dist/public/shop-master-data.d.ts +1 -1
- package/dist/public/shop-master-data.js +1 -1
- package/dist/public/types.d.ts +2 -1
- package/dist/shop-config/build-food-shop-config.js +4 -0
- package/dist/shop-config/options.d.ts +10 -3
- package/dist/shop-config/options.js +4 -0
- package/package.json +2 -2
|
@@ -1,96 +1,141 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createDocumentLayout = void 0;
|
|
3
|
+
exports.createDocumentLayout = exports.formatCountryName = void 0;
|
|
4
4
|
const html_utils_1 = require("./html-utils");
|
|
5
5
|
const formatCountryName = (countryCode) => new Intl.DisplayNames("de", { type: "region" }).of(countryCode.toUpperCase()) ?? countryCode.toUpperCase();
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
[merchant.postal_code, merchant.city].join(" "),
|
|
10
|
-
formatCountryName(merchant.country_code),
|
|
11
|
-
]
|
|
12
|
-
.filter((part) => Boolean(part))
|
|
6
|
+
exports.formatCountryName = formatCountryName;
|
|
7
|
+
const joinLines = (lines) => lines
|
|
8
|
+
.filter((line) => Boolean(line))
|
|
13
9
|
.map(html_utils_1.escapeHtml)
|
|
14
10
|
.join("<br />");
|
|
15
|
-
|
|
11
|
+
const renderFooterColumn = (heading, lines) => {
|
|
12
|
+
const content = joinLines(lines);
|
|
13
|
+
if (!content) {
|
|
14
|
+
return "";
|
|
15
|
+
}
|
|
16
|
+
const title = heading
|
|
17
|
+
? `<div class="footer-heading">${(0, html_utils_1.escapeHtml)(heading)}</div>`
|
|
18
|
+
: "";
|
|
19
|
+
return `<div class="footer-column">${title}${content}</div>`;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Legal footer in three columns: sender with UID and company register,
|
|
23
|
+
* contact, bank. Packing lists (`includeLegalDetails: false`) show only the
|
|
24
|
+
* sender's name and address.
|
|
25
|
+
*/
|
|
16
26
|
const renderMerchantFooter = (merchant, includeLegalDetails) => {
|
|
27
|
+
const sender = `<div class="footer-column"><strong>${(0, html_utils_1.escapeHtml)(merchant.legal_name)}</strong><br />${joinLines([
|
|
28
|
+
merchant.address_line1,
|
|
29
|
+
merchant.address_line2,
|
|
30
|
+
`${merchant.postal_code} ${merchant.city}`,
|
|
31
|
+
(0, exports.formatCountryName)(merchant.country_code),
|
|
32
|
+
...(includeLegalDetails
|
|
33
|
+
? [
|
|
34
|
+
merchant.vat_number ? `UID: ${merchant.vat_number}` : null,
|
|
35
|
+
merchant.company_register_number
|
|
36
|
+
? `Firmenbuchnummer: ${merchant.company_register_number}`
|
|
37
|
+
: null,
|
|
38
|
+
merchant.company_register_court
|
|
39
|
+
? `Firmenbuchgericht: ${merchant.company_register_court}`
|
|
40
|
+
: null,
|
|
41
|
+
]
|
|
42
|
+
: []),
|
|
43
|
+
])}</div>`;
|
|
17
44
|
if (!includeLegalDetails) {
|
|
18
|
-
return
|
|
45
|
+
return sender;
|
|
19
46
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
merchant.legal_name,
|
|
31
|
-
merchant.vat_number ? `UID: ${merchant.vat_number}` : null,
|
|
32
|
-
...register,
|
|
33
|
-
],
|
|
34
|
-
[merchant.email, merchant.phone, merchant.website],
|
|
35
|
-
[
|
|
36
|
-
merchant.iban ? `IBAN: ${merchant.iban}` : null,
|
|
47
|
+
return [
|
|
48
|
+
sender,
|
|
49
|
+
renderFooterColumn("Kontakt", [
|
|
50
|
+
merchant.phone,
|
|
51
|
+
merchant.email,
|
|
52
|
+
merchant.website,
|
|
53
|
+
]),
|
|
54
|
+
// The heading names the IBAN; only the BIC keeps its label.
|
|
55
|
+
renderFooterColumn("Bankverbindung", [
|
|
56
|
+
merchant.iban,
|
|
37
57
|
merchant.bic ? `BIC: ${merchant.bic}` : null,
|
|
38
|
-
],
|
|
39
|
-
];
|
|
40
|
-
return lines
|
|
41
|
-
.map((parts) => parts
|
|
42
|
-
.filter((part) => Boolean(part))
|
|
43
|
-
.map(html_utils_1.escapeHtml)
|
|
44
|
-
.join(" · "))
|
|
45
|
-
.filter(Boolean)
|
|
46
|
-
.join("<br />");
|
|
58
|
+
]),
|
|
59
|
+
].join("");
|
|
47
60
|
};
|
|
61
|
+
const renderLetterhead = (merchant, settings) => settings.logo_src
|
|
62
|
+
? `<img class="logo" src="${(0, html_utils_1.escapeHtml)(settings.logo_src)}" alt="${(0, html_utils_1.escapeHtml)(merchant.legal_name)}" />`
|
|
63
|
+
: `<div class="logo-text">${(0, html_utils_1.escapeHtml)(merchant.legal_name)}</div>`;
|
|
48
64
|
const renderMetadata = (metadata) => metadata
|
|
49
|
-
.map(({ label, value }) => `<
|
|
65
|
+
.map(({ label, value }) => `<tr><th scope="row">${(0, html_utils_1.escapeHtml)(label)}</th><td>${(0, html_utils_1.escapeHtml)(value)}</td></tr>`)
|
|
50
66
|
.join("");
|
|
51
67
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
68
|
+
* A4 with only a bottom page margin, which holds the page number
|
|
69
|
+
* (`@bottom-right`; `counter(pages)` works only in page margin boxes). `.page`
|
|
70
|
+
* carries the other margins and repeats them on every printed page
|
|
71
|
+
* (`box-decoration-break: clone`); the fixed footer is positioned in the page
|
|
72
|
+
* area, `--footer-height` reserves its space at the bottom of each page.
|
|
54
73
|
*/
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
.
|
|
66
|
-
.
|
|
67
|
-
.document-
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
const DEFAULT_STYLESHEET = `
|
|
75
|
+
@page { size: A4; margin: 0 0 8mm 0; @bottom-right { color: #6b7280; content: "Seite " counter(page) " von " counter(pages); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 7.5pt; padding-right: 20mm; vertical-align: top; } }
|
|
76
|
+
:root { --accent: #111827; --text: #1f2937; --muted: #6b7280; --line: #d1d5db; --table-head: #f3f4f6; --page-x: 20mm; --page-top: 18mm; --footer-height: 30mm; }
|
|
77
|
+
* { box-sizing: border-box; }
|
|
78
|
+
html, body { margin: 0; padding: 0; }
|
|
79
|
+
body { color: var(--text); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 10pt; line-height: 1.4; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
80
|
+
.page { -webkit-box-decoration-break: clone; box-decoration-break: clone; padding: var(--page-top) var(--page-x) calc(var(--footer-height) + 8mm); }
|
|
81
|
+
.letterhead { margin-bottom: 14mm; min-height: 16mm; }
|
|
82
|
+
.logo { display: block; height: 22mm; max-width: 90mm; object-fit: contain; object-position: left center; width: auto; }
|
|
83
|
+
.logo-text { color: var(--accent); font-size: 20pt; font-weight: 700; letter-spacing: 0.02em; }
|
|
84
|
+
.document-head { break-inside: avoid; display: flex; gap: 12mm; justify-content: space-between; margin-bottom: 14mm; page-break-inside: avoid; }
|
|
85
|
+
.recipient { flex: 1 1 0; font-style: normal; padding-top: 9mm; }
|
|
86
|
+
.document-info { flex: 0 0 78mm; }
|
|
87
|
+
h1 { color: var(--accent); font-size: 16pt; font-weight: 400; letter-spacing: 0.08em; margin: 0 0 3mm; text-transform: uppercase; }
|
|
88
|
+
h2 { break-after: avoid; font-size: 10pt; margin: 8mm 0 2mm; page-break-after: avoid; }
|
|
89
|
+
.document-metadata { border-collapse: collapse; width: 100%; }
|
|
90
|
+
.document-metadata th, .document-metadata td { border: 0; font-weight: 400; padding: 0; text-align: left; vertical-align: top; }
|
|
91
|
+
.document-metadata td { text-align: right; }
|
|
92
|
+
.muted, .sku, .batch { color: var(--muted); }
|
|
93
|
+
.batch, .packing-batch { font-size: 8pt; margin-top: 1mm; }
|
|
70
94
|
.packing-batch { display: flex; gap: 12px; justify-content: space-between; }
|
|
71
95
|
table { border-collapse: collapse; page-break-inside: auto; width: 100%; }
|
|
72
96
|
thead { display: table-header-group; }
|
|
73
97
|
tfoot { display: table-footer-group; }
|
|
74
98
|
tbody { page-break-inside: auto; }
|
|
75
99
|
tr { break-inside: avoid; page-break-inside: avoid; }
|
|
76
|
-
th { background:
|
|
77
|
-
th
|
|
100
|
+
.items th { background: var(--table-head); font-weight: 700; text-align: left; }
|
|
101
|
+
.items th.number { text-align: right; }
|
|
102
|
+
.items th, .items td { padding: 2.5mm 2mm; vertical-align: top; }
|
|
103
|
+
.items tbody.lines td { border-bottom: 1px solid var(--line); }
|
|
78
104
|
.number { text-align: right; white-space: nowrap; }
|
|
79
|
-
.
|
|
80
|
-
.totals {
|
|
81
|
-
.totals td {
|
|
82
|
-
.totals tr
|
|
83
|
-
|
|
84
|
-
|
|
105
|
+
.money { white-space: nowrap; }
|
|
106
|
+
.items tbody.totals td { padding-bottom: 1.5mm; padding-top: 1.5mm; }
|
|
107
|
+
.items tbody.totals tr:first-child td { padding-top: 3mm; }
|
|
108
|
+
.items tbody.totals tr.grand-total td.label, .items tbody.totals tr.grand-total td.number { border-bottom: 1px solid var(--line); border-top: 1px solid var(--line); font-weight: 700; padding-bottom: 2mm; padding-top: 2mm; }
|
|
109
|
+
.payment-terms { margin-top: 10mm; white-space: pre-line; }
|
|
110
|
+
.recipient, .tax-compliance, .payment-terms, .items tbody.totals { break-inside: avoid; page-break-inside: avoid; }
|
|
111
|
+
.page-footer { align-items: flex-start; bottom: 4mm; color: var(--text); display: flex; font-size: 8pt; gap: 8mm; left: var(--page-x); line-height: 1.35; position: fixed; right: var(--page-x); }
|
|
112
|
+
.footer-column { flex: 1 1 0; }
|
|
113
|
+
.footer-heading { color: var(--muted); }
|
|
114
|
+
`;
|
|
115
|
+
/**
|
|
116
|
+
* `includeLegalDetails` adds UID, company register, contact and bank details
|
|
117
|
+
* to the footer (financial documents); packing lists show only the sender.
|
|
118
|
+
*/
|
|
119
|
+
const createDocumentLayout = (merchant, settings, includeLegalDetails, theme = {}) => ({ body, documentTitle, metadata, number, pageTitle, recipient }) => `<!doctype html>
|
|
120
|
+
<html lang="de">
|
|
121
|
+
<head>
|
|
122
|
+
<meta charset="utf-8" />
|
|
123
|
+
<title>${(0, html_utils_1.escapeHtml)(pageTitle)} ${(0, html_utils_1.escapeHtml)(number)}</title>
|
|
124
|
+
<style>${DEFAULT_STYLESHEET}${theme.stylesheet ?? ""}</style>
|
|
85
125
|
</head>
|
|
86
126
|
<body>
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
127
|
+
<footer class="page-footer">${renderMerchantFooter(merchant, includeLegalDetails)}</footer>
|
|
128
|
+
<main class="page">
|
|
129
|
+
<header class="letterhead">${renderLetterhead(merchant, settings)}</header>
|
|
130
|
+
<section class="document-head">
|
|
131
|
+
<address class="recipient">${recipient ?? ""}</address>
|
|
132
|
+
<div class="document-info">
|
|
133
|
+
<h1>${(0, html_utils_1.escapeHtml)(documentTitle)}</h1>
|
|
134
|
+
<table class="document-metadata"><tbody>${renderMetadata(metadata)}</tbody></table>
|
|
135
|
+
</div>
|
|
136
|
+
</section>
|
|
137
|
+
${body}
|
|
138
|
+
</main>
|
|
94
139
|
</body>
|
|
95
140
|
</html>`;
|
|
96
141
|
exports.createDocumentLayout = createDocumentLayout;
|
|
@@ -10,15 +10,15 @@ const renderBatchAllocations = (line, timeZone) => {
|
|
|
10
10
|
return line.batch_allocations
|
|
11
11
|
.map((allocation) => `<div class="packing-batch">
|
|
12
12
|
<strong>${(0, html_utils_1.escapeHtml)(allocation.lot_number)}</strong>
|
|
13
|
-
<span>MHD: ${(0, format_1.
|
|
13
|
+
<span>MHD: ${(0, html_utils_1.escapeHtml)((0, format_1.formatDocumentDate)(allocation.expiry_date, timeZone))}</span>
|
|
14
14
|
<span>Menge: ${(0, html_utils_1.escapeHtml)(allocation.quantity)}</span>
|
|
15
15
|
</div>`)
|
|
16
16
|
.join("");
|
|
17
17
|
};
|
|
18
18
|
const renderLines = (input, timeZone) => input.lines
|
|
19
19
|
.map((line) => `<tr>
|
|
20
|
-
<td>
|
|
21
|
-
<
|
|
20
|
+
<td class="item">
|
|
21
|
+
<div class="item-title">${(0, html_utils_1.escapeHtml)(line.title)}</div>
|
|
22
22
|
${line.sku ? `<div class="sku">SKU: ${(0, html_utils_1.escapeHtml)(line.sku)}</div>` : ""}
|
|
23
23
|
</td>
|
|
24
24
|
<td>${renderBatchAllocations(line, timeZone)}</td>
|
|
@@ -26,12 +26,13 @@ const renderLines = (input, timeZone) => input.lines
|
|
|
26
26
|
</tr>`)
|
|
27
27
|
.join("");
|
|
28
28
|
const renderPackingList = (input, layout, timeZone) => layout({
|
|
29
|
-
body: `<table>
|
|
30
|
-
<thead><tr><th>Bezeichnung</th><th>Charge</th><th class="number">Benötigte Menge</th></tr></thead>
|
|
31
|
-
<tbody>${renderLines(input, timeZone)}</tbody>
|
|
29
|
+
body: `<table class="items packing-items">
|
|
30
|
+
<thead><tr><th class="item">Bezeichnung</th><th>Charge</th><th class="number">Benötigte Menge</th></tr></thead>
|
|
31
|
+
<tbody class="lines">${renderLines(input, timeZone)}</tbody>
|
|
32
32
|
</table>`,
|
|
33
33
|
documentTitle: "Packliste",
|
|
34
34
|
metadata: [
|
|
35
|
+
{ label: "Packlistennummer", value: input.document.number },
|
|
35
36
|
{
|
|
36
37
|
label: "Bestellung",
|
|
37
38
|
value: (0, html_utils_1.formatOrderReference)(input.order.display_id),
|
|
@@ -39,11 +40,12 @@ const renderPackingList = (input, layout, timeZone) => layout({
|
|
|
39
40
|
{ label: "Fulfillment", value: input.fulfillment.id },
|
|
40
41
|
{
|
|
41
42
|
label: "Erstellungsdatum",
|
|
42
|
-
value: (0, format_1.
|
|
43
|
+
value: (0, format_1.formatDocumentDate)(input.document.generated_at, timeZone),
|
|
43
44
|
},
|
|
44
45
|
{ label: "Lagerplatz", value: input.fulfillment.location_name },
|
|
45
46
|
],
|
|
46
47
|
number: input.document.number,
|
|
47
48
|
pageTitle: "Packliste",
|
|
49
|
+
recipient: null,
|
|
48
50
|
});
|
|
49
51
|
exports.renderPackingList = renderPackingList;
|
|
@@ -161,11 +161,28 @@ export type DocumentMerchantOption = {
|
|
|
161
161
|
};
|
|
162
162
|
vatNumber?: string;
|
|
163
163
|
};
|
|
164
|
+
/**
|
|
165
|
+
* The Documents settings (Admin -> "Hartl Services Plugins" -> Dokumente) a
|
|
166
|
+
* render needs, read when a document is rendered. Not part of the stored
|
|
167
|
+
* snapshot: a regenerated document shows the current values.
|
|
168
|
+
*/
|
|
169
|
+
export type DocumentRenderSettings = {
|
|
170
|
+
/**
|
|
171
|
+
* The uploaded logo as a `data:` URL, ready for `<img src>`; `null` when no
|
|
172
|
+
* logo is set or it could not be loaded (the document then shows the
|
|
173
|
+
* merchant's legal name instead).
|
|
174
|
+
*/
|
|
175
|
+
logo_src: string | null;
|
|
176
|
+
/** Payment terms printed below the totals of an invoice; `null` = none. */
|
|
177
|
+
payment_terms: string | null;
|
|
178
|
+
};
|
|
164
179
|
export type FinancialDocumentRenderInput = DocumentTemplateInput & {
|
|
165
180
|
merchant: DocumentMerchant;
|
|
181
|
+
settings: DocumentRenderSettings;
|
|
166
182
|
};
|
|
167
183
|
export type PackingListRenderInput = PackingListTemplateInput & {
|
|
168
184
|
merchant: DocumentMerchant;
|
|
185
|
+
settings: DocumentRenderSettings;
|
|
169
186
|
};
|
|
170
187
|
export type DocumentRenderInput = FinancialDocumentRenderInput | PackingListRenderInput;
|
|
171
188
|
export type DocumentPdfOptions = {
|
|
@@ -5,3 +5,4 @@ import type { DocumentRenderInput } from "./documents";
|
|
|
5
5
|
*/
|
|
6
6
|
export type DocumentHtmlFactory = (input: DocumentRenderInput) => string | Promise<string>;
|
|
7
7
|
export declare const defineDocumentHtmlFactory: (factory: DocumentHtmlFactory) => DocumentHtmlFactory;
|
|
8
|
+
export type { DocumentTheme } from "../templates/layout";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ShopMasterData } from "../../public";
|
|
1
2
|
import type { EmailKind } from "./email-kinds";
|
|
2
3
|
import type { EmailRenderDataMap } from "./template-data";
|
|
3
4
|
export type EmailTemplateContent = Readonly<{
|
|
@@ -5,10 +6,22 @@ export type EmailTemplateContent = Readonly<{
|
|
|
5
6
|
subject: string;
|
|
6
7
|
text: string;
|
|
7
8
|
}>;
|
|
9
|
+
/**
|
|
10
|
+
* The shop's master data ("Shop-Stammdaten") plus the `logo` URL of the
|
|
11
|
+
* E-Mail settings, read when the mail is sent. Every field may be missing; a
|
|
12
|
+
* layout shows what is there instead of failing.
|
|
13
|
+
*/
|
|
14
|
+
export type EmailShopDetails = Readonly<ShopMasterData & {
|
|
15
|
+
logo?: string;
|
|
16
|
+
}>;
|
|
17
|
+
/** Who reads a mail: a customer or the shop's own team. */
|
|
18
|
+
export type EmailAudience = "customer" | "shop";
|
|
8
19
|
export type EmailLayout = (input: Readonly<{
|
|
20
|
+
audience: EmailAudience;
|
|
9
21
|
bodyHtml: string;
|
|
10
22
|
preheader: string;
|
|
11
23
|
subject: string;
|
|
24
|
+
shop: EmailShopDetails;
|
|
12
25
|
}>) => string;
|
|
13
26
|
export type EmailRendererMap = {
|
|
14
27
|
readonly [Kind in EmailKind]: (data: EmailRenderDataMap[Kind]) => EmailTemplateContent;
|
|
@@ -19,6 +32,13 @@ export type EmailRendererMap = {
|
|
|
19
32
|
* partial `renderers` map replaces only the listed kinds.
|
|
20
33
|
*/
|
|
21
34
|
export type ShopEmailConfiguration = Readonly<{
|
|
35
|
+
/**
|
|
36
|
+
* Link to an order in the Medusa Admin; the shop's order notification shows
|
|
37
|
+
* an "Im Admin öffnen" button only when it is set.
|
|
38
|
+
*/
|
|
39
|
+
adminOrderUrl?: (input: Readonly<{
|
|
40
|
+
orderId: string;
|
|
41
|
+
}>) => string;
|
|
22
42
|
affiliatePortalUrl: string;
|
|
23
43
|
displayName: string;
|
|
24
44
|
inviteUrl: (input: Readonly<{
|
|
@@ -30,6 +50,11 @@ export type ShopEmailConfiguration = Readonly<{
|
|
|
30
50
|
locale: string | null;
|
|
31
51
|
}>) => string;
|
|
32
52
|
renderers?: Partial<EmailRendererMap>;
|
|
53
|
+
/**
|
|
54
|
+
* IANA time zone of dates in the default templates (e.g. the order time in
|
|
55
|
+
* the shop's order notification); without it they are left out.
|
|
56
|
+
*/
|
|
57
|
+
timeZone?: string;
|
|
33
58
|
}>;
|
|
34
59
|
/** A `ShopEmailConfiguration` with the defaults merged in; used internally. */
|
|
35
60
|
export type ResolvedShopEmailConfiguration = Readonly<Omit<ShopEmailConfiguration, "layout" | "renderers"> & {
|