@nxgt/mail-presets 0.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/LICENSE +21 -0
- package/README.md +222 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +221 -0
- package/dist/index.js.map +10 -0
- package/dist/presets.d.ts +37 -0
- package/dist/presets.d.ts.map +1 -0
- package/docs/README.md +21 -0
- package/docs/guide/emails.md +337 -0
- package/docs/guide/presets.md +365 -0
- package/docs/roadmap.md +63 -0
- package/docs/troubleshooting.md +395 -0
- package/emails/email-changed.vue +14 -0
- package/emails/invitation.vue +12 -0
- package/emails/magic-link.vue +13 -0
- package/emails/new-sign-in.vue +21 -0
- package/emails/password-changed.vue +14 -0
- package/emails/reset-password.vue +14 -0
- package/emails/sign-in-code.vue +8 -0
- package/emails/verify-email.vue +14 -0
- package/emails/welcome.vue +13 -0
- package/package.json +68 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# Troubleshooting `@nxgt/mail-presets`
|
|
2
|
+
|
|
3
|
+
Each entry is headed by the message you see. Search this page for the words of
|
|
4
|
+
your message. Near the end, a trap that fails no build, and a section for
|
|
5
|
+
contributors to this package.
|
|
6
|
+
|
|
7
|
+
How the messages are shaped:
|
|
8
|
+
|
|
9
|
+
- **A wiring mistake is a `TypeError` starting `presets:`**, thrown by the
|
|
10
|
+
`presets()` call in `maizzle.config.ts` when the config loads, so
|
|
11
|
+
`maizzle build` and `maizzle serve` stop before any template is built. Fix
|
|
12
|
+
the call.
|
|
13
|
+
- **The other failures come from the packages the presets are built with.**
|
|
14
|
+
`@nxgt/mail-i18n` prints `i18n: …`, and a missing `ui()` shows first as
|
|
15
|
+
Vue's `Failed to resolve component` warnings.
|
|
16
|
+
Most of them mean one of the three lines that wire the presets is missing:
|
|
17
|
+
`ui()`, a catalogue in `catalogues`, or the templates in `templates`. This
|
|
18
|
+
page covers the ones the presets cause. The rest are in
|
|
19
|
+
[`@nxgt/mail-i18n`'s troubleshooting](https://github.com/softistx/nxgt-mail/blob/develop/packages/mail-i18n/docs/troubleshooting.md)
|
|
20
|
+
and
|
|
21
|
+
[`@nxgt/mail-ui`'s](https://github.com/softistx/nxgt-mail/blob/develop/packages/mail-ui/docs/troubleshooting.md).
|
|
22
|
+
|
|
23
|
+
Every fix below ends in the same config, which builds every preset in `en`
|
|
24
|
+
and `fr`:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// maizzle.config.ts
|
|
28
|
+
import { defineMailConfig } from '@nxgt/mail-config';
|
|
29
|
+
import { i18n } from '@nxgt/mail-i18n';
|
|
30
|
+
import { presets } from '@nxgt/mail-presets';
|
|
31
|
+
import { ui, uiCatalogues } from '@nxgt/mail-ui';
|
|
32
|
+
|
|
33
|
+
const mails = presets(); // or presets({ only: ['verify-email', 'reset-password'] })
|
|
34
|
+
|
|
35
|
+
export default defineMailConfig({
|
|
36
|
+
plugins: [
|
|
37
|
+
ui({ brand: { name: 'Acme', url: 'https://acme.example' } }),
|
|
38
|
+
i18n({
|
|
39
|
+
locales: ['en', 'fr'],
|
|
40
|
+
catalogues: [uiCatalogues, mails.catalogues],
|
|
41
|
+
templates: [mails.templates],
|
|
42
|
+
}),
|
|
43
|
+
],
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The samples below use the locales `en` and `fr`, the preset `verify-email`,
|
|
48
|
+
and the brand `Acme`.
|
|
49
|
+
|
|
50
|
+
## Index
|
|
51
|
+
|
|
52
|
+
**Wiring**: when `maizzle.config.ts` loads
|
|
53
|
+
- [`presets: options must be an object, as { only: ['verify-email'] }`](#presets-options-must-be-an-object-as--only-verify-email-)
|
|
54
|
+
- [`presets: only must list at least one preset, as ['verify-email']`](#presets-only-must-list-at-least-one-preset-as-verify-email)
|
|
55
|
+
- [`presets: only holds something that is not a preset — name one of verify-email, reset-password, …`](#presets-only-holds-something-that-is-not-a-preset--name-one-of-verify-email-reset-password-password-changed-email-changed-sign-in-code-magic-link-new-sign-in-welcome-invitation)
|
|
56
|
+
- [`presets: only holds the same preset twice`](#presets-only-holds-the-same-preset-twice)
|
|
57
|
+
- [`i18n: templates must be a list of template folders, as [{ dir: '/abs/path/emails' }] — emails, when given, names at least one, each once`](#i18n-templates-must-be-a-list-of-template-folders-as--dir-abspathemails---emails-when-given-names-at-least-one-each-once)
|
|
58
|
+
- [`i18n: templates[0] and templates[1] both have welcome.vue — keep one with emails: [...], or write the project's own in its folder`](#i18n-templates0-and-templates1-both-have-welcomevue--keep-one-with-emails--or-write-the-projects-own-in-its-folder)
|
|
59
|
+
- [`i18n: de: presets.linkFallback is missing — en, the fallback locale, has it`](#i18n-de-presetslinkfallback-is-missing--en-the-fallback-locale-has-it)
|
|
60
|
+
- [`i18n: fr: verifyEmail.body uses {brand}, which en does not declare`](#i18n-fr-verifyemailbody-uses-brand-which-en-does-not-declare)
|
|
61
|
+
|
|
62
|
+
**Build**: while `maizzle build` renders
|
|
63
|
+
- [`i18n: en: verify-email calls t('common.footer.why'), which is not a key of the catalogues`](#i18n-en-verify-email-calls-tcommonfooterwhy-which-is-not-a-key-of-the-catalogues)
|
|
64
|
+
- [`i18n: en: verify-email calls t('verifyEmail.preheader'), which is not a key of the catalogues`](#i18n-en-verify-email-calls-tverifyemailpreheader-which-is-not-a-key-of-the-catalogues)
|
|
65
|
+
- [`TypeError: Cannot read properties of undefined (reading 'name')`](#typeerror-cannot-read-properties-of-undefined-reading-name)
|
|
66
|
+
- [`i18n: en/new-sign-in.html is empty — a tag of its template resolved to no component; list the plugin that brings it, as ui()`](#i18n-ennew-sign-inhtml-is-empty--a-tag-of-its-template-resolved-to-no-component-list-the-plugin-that-brings-it-as-ui)
|
|
67
|
+
- [`i18n: en: verify-email passes {brand} to verifyEmail.body, which does not use it`](#i18n-en-verify-email-passes-brand-to-verifyemailbody-which-does-not-use-it)
|
|
68
|
+
|
|
69
|
+
**Traps: a build that succeeds and is wrong**
|
|
70
|
+
- [A bug in `@nxgt/mail-presets` itself](#a-bug-in-nxgtmail-presets-itself)
|
|
71
|
+
|
|
72
|
+
**Contributing to this package**
|
|
73
|
+
- [``samples/ is what the build writes — run `bun run samples` after a change``](#samples-is-what-the-build-writes--run-bun-run-samples-after-a-change)
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Wiring
|
|
78
|
+
|
|
79
|
+
### `presets: options must be an object, as { only: ['verify-email'] }`
|
|
80
|
+
|
|
81
|
+
**When:** loading `maizzle.config.ts`, when `presets` is called with the list
|
|
82
|
+
of names directly (`presets(['welcome'])`), a string (`presets('welcome')`),
|
|
83
|
+
`null`, or another value that is not an object.
|
|
84
|
+
**Why:** `presets` takes one optional object. Its one option, `only`, names
|
|
85
|
+
the presets to build. Call it with nothing to build every preset.
|
|
86
|
+
**Fix:**
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { presets } from '@nxgt/mail-presets';
|
|
90
|
+
|
|
91
|
+
const mails = presets({ only: ['welcome'] }); // not presets(['welcome']), not presets('welcome')
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `presets: only must list at least one preset, as ['verify-email']`
|
|
95
|
+
|
|
96
|
+
**When:** loading `maizzle.config.ts`, when `only` is an empty list or a
|
|
97
|
+
single name rather than a list (`only: 'welcome'`).
|
|
98
|
+
**Why:** `only` keeps the presets it names and no other, so an empty list
|
|
99
|
+
would build none of them.
|
|
100
|
+
**Fix:** pass a list, or leave `only` out to build every preset:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
presets({ only: ['welcome'] }); // not only: 'welcome', not only: []
|
|
104
|
+
presets(); // every preset
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### `presets: only holds something that is not a preset — name one of verify-email, reset-password, password-changed, email-changed, sign-in-code, magic-link, new-sign-in, welcome, invitation`
|
|
108
|
+
|
|
109
|
+
**When:** loading `maizzle.config.ts`, when `only` names an e-mail the
|
|
110
|
+
package does not ship. Common causes are a typo, a name in camelCase
|
|
111
|
+
(`'verifyEmail'`), a name with `.vue`, or a name of your own template such as
|
|
112
|
+
`'sign-in'`.
|
|
113
|
+
**Why:** `only` names the package's templates, by the name of their file and
|
|
114
|
+
of their built file (`dist/en/verify-email.html`). The message lists them.
|
|
115
|
+
TypeScript refuses such a name too, where the config is type-checked.
|
|
116
|
+
**Fix:**
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { PRESETS, presets } from '@nxgt/mail-presets';
|
|
120
|
+
|
|
121
|
+
presets({ only: ['verify-email', 'sign-in-code'] }); // not 'verifyEmail', not 'sign-in'
|
|
122
|
+
console.log(PRESETS); // every name
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
An e-mail of your own goes in the project's `emails/`, not in `only`.
|
|
126
|
+
|
|
127
|
+
### `presets: only holds the same preset twice`
|
|
128
|
+
|
|
129
|
+
**When:** loading `maizzle.config.ts`, when `only` names a preset twice. This
|
|
130
|
+
usually happens when a list is joined from two others.
|
|
131
|
+
**Why:** each preset is built once. A name given twice is a mistake in the
|
|
132
|
+
list that builds it.
|
|
133
|
+
**Fix:**
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
presets({ only: [...new Set([...authMails, ...accountMails])] });
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### `i18n: templates must be a list of template folders, as [{ dir: '/abs/path/emails' }] — emails, when given, names at least one, each once`
|
|
140
|
+
|
|
141
|
+
**When:** loading `maizzle.config.ts`, most often when the presets' templates
|
|
142
|
+
are passed alone instead of in a list: `templates: mails.templates`.
|
|
143
|
+
**Why:** `@nxgt/mail-i18n` takes a list of template folders, so that several
|
|
144
|
+
packages can each ship templates. `presets()` answers one folder.
|
|
145
|
+
**Fix:** wrap it in a list, even when there is one:
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
i18n({
|
|
149
|
+
locales: ['en', 'fr'],
|
|
150
|
+
catalogues: [uiCatalogues, mails.catalogues],
|
|
151
|
+
templates: [mails.templates], // not templates: mails.templates
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### `i18n: templates[0] and templates[1] both have welcome.vue — keep one with emails: [...], or write the project's own in its folder`
|
|
156
|
+
|
|
157
|
+
**When:** loading `maizzle.config.ts`, when `templates` lists the presets'
|
|
158
|
+
folder twice with a preset in common. This usually happens when `presets()`
|
|
159
|
+
is called twice, once for each group of e-mails, the two `only` lists share a
|
|
160
|
+
preset or one call has no `only`, and both answers are listed.
|
|
161
|
+
**Why:** each e-mail is built once. Two entries that ship the same e-mail
|
|
162
|
+
leave no obvious one to build, so `@nxgt/mail-i18n` refuses to pick.
|
|
163
|
+
**Fix:** call `presets()` once, with every preset you build:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
const mails = presets({ only: ['verify-email', 'reset-password', 'welcome'] });
|
|
167
|
+
|
|
168
|
+
i18n({
|
|
169
|
+
locales: ['en', 'fr'],
|
|
170
|
+
catalogues: [uiCatalogues, mails.catalogues],
|
|
171
|
+
templates: [mails.templates], // not [authMails.templates, accountMails.templates]
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
To replace a preset with your own, write `emails/welcome.vue` in the project.
|
|
176
|
+
It wins over the preset, and this error does not apply to it.
|
|
177
|
+
|
|
178
|
+
### `i18n: de: presets.linkFallback is missing — en, the fallback locale, has it`
|
|
179
|
+
|
|
180
|
+
The message names your locale. The key starts `presets.` or with a preset's
|
|
181
|
+
key (`verifyEmail.`, `signInCode.`, …), or `common.` when the `common` keys
|
|
182
|
+
are missing as well (`i18n: de: common.footer.ignore is missing — …`).
|
|
183
|
+
|
|
184
|
+
**When:** loading `maizzle.config.ts`, when the project builds a locale other
|
|
185
|
+
than `en` and `fr`, such as `locales: ['en', 'de']`.
|
|
186
|
+
**Why:** the presets' messages ship in `en` and `fr` only, and so do
|
|
187
|
+
`@nxgt/mail-ui`'s `common` messages. Every locale must have every key of the
|
|
188
|
+
fallback locale.
|
|
189
|
+
**Fix:** write the missing messages in the project's own catalogue:
|
|
190
|
+
|
|
191
|
+
```jsonc
|
|
192
|
+
// locales/de.json
|
|
193
|
+
{
|
|
194
|
+
"common": { "greeting": "Hallo {name},", "footer": { "why": "…", "ignore": "…" } },
|
|
195
|
+
"presets": { "linkFallback": "…", "notYou": "…" },
|
|
196
|
+
"verifyEmail": { "subject": "…", "preheader": "…", "title": "…", "body": "…", "action": "…" }
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The keys to write are the ones in `presetCatalogues.en` and
|
|
201
|
+
`uiCatalogues.en`. With `only`, write just the `presets` group and the groups
|
|
202
|
+
of the presets kept, as `presets({ only }).catalogues.en` shows:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import { presets } from '@nxgt/mail-presets';
|
|
206
|
+
|
|
207
|
+
console.log(JSON.stringify(presets({ only: ['verify-email'] }).catalogues.en, null, 2));
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### `i18n: fr: verifyEmail.body uses {brand}, which en does not declare`
|
|
211
|
+
|
|
212
|
+
The message names a locale the project did not override, a preset's key, and
|
|
213
|
+
an argument of the preset's message.
|
|
214
|
+
|
|
215
|
+
**When:** loading `maizzle.config.ts`, after the project overrides a preset's
|
|
216
|
+
message in `locales/en.json` with other arguments than the preset's, as
|
|
217
|
+
`"body": "Confirm your address for {company}."` in place of the preset's
|
|
218
|
+
`{brand}`.
|
|
219
|
+
**Why:** a project's message replaces the preset's in that locale only. The
|
|
220
|
+
other locale keeps the preset's text, and every locale must use only the
|
|
221
|
+
arguments that the fallback locale declares.
|
|
222
|
+
**Fix:** keep the preset's arguments in the override. They are the ones in
|
|
223
|
+
the preset's own message:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
import { presetCatalogues } from '@nxgt/mail-presets';
|
|
227
|
+
|
|
228
|
+
console.log(presetCatalogues.en.verifyEmail); // body: '… your {brand} account.'
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
```jsonc
|
|
232
|
+
// locales/en.json
|
|
233
|
+
{ "verifyEmail": { "body": "Confirm your address to start using {brand}." } }
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
When both locales are overridden with a new argument, as `{company}` in `en`
|
|
237
|
+
and in `fr`, the build stops later, on
|
|
238
|
+
`i18n: en: verify-email calls t('verifyEmail.body') without {company}`: the
|
|
239
|
+
preset's template passes `brand`, not `company`. To add an argument, write
|
|
240
|
+
the project's own template in `emails/verify-email.vue`, which replaces the
|
|
241
|
+
preset's.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Build
|
|
246
|
+
|
|
247
|
+
These fail while `maizzle build` renders a template. The build stops, and the
|
|
248
|
+
line to read starts `Error:` or `TypeError:`, after Vue's warnings and a stack
|
|
249
|
+
trace. Maizzle builds the locales in no set order, so the locale in the
|
|
250
|
+
message is `en` in one run and `fr` in the next.
|
|
251
|
+
|
|
252
|
+
### `i18n: en: verify-email calls t('common.footer.why'), which is not a key of the catalogues`
|
|
253
|
+
|
|
254
|
+
The message names a preset, and the key starts `common.`.
|
|
255
|
+
|
|
256
|
+
**When:** `maizzle build`, on the first preset, when `catalogues` holds the
|
|
257
|
+
presets' messages but not `uiCatalogues`.
|
|
258
|
+
**Why:** a preset's layout and greeting use `@nxgt/mail-ui`'s shared
|
|
259
|
+
`common.*` messages, which ship in `uiCatalogues`. `presets().catalogues`
|
|
260
|
+
holds only the presets' own messages.
|
|
261
|
+
**Fix:** pass both, `uiCatalogues` first:
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
i18n({
|
|
265
|
+
locales: ['en', 'fr'],
|
|
266
|
+
catalogues: [uiCatalogues, mails.catalogues], // not [mails.catalogues]
|
|
267
|
+
templates: [mails.templates],
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### `i18n: en: verify-email calls t('verifyEmail.preheader'), which is not a key of the catalogues`
|
|
272
|
+
|
|
273
|
+
The message names a preset, and the key starts with that preset's key
|
|
274
|
+
(`verifyEmail.`, `signInCode.`, …) or `presets.`.
|
|
275
|
+
|
|
276
|
+
**When:** `maizzle build`, on the first preset, when `templates` holds the
|
|
277
|
+
presets' templates but `catalogues` does not hold their messages.
|
|
278
|
+
**Why:** `presets()` answers the templates and their messages separately,
|
|
279
|
+
because `@nxgt/mail-i18n` takes each in its own option. Passing one without
|
|
280
|
+
the other builds templates that have no text.
|
|
281
|
+
**Fix:** pass `mails.catalogues` with `mails.templates`, from the same
|
|
282
|
+
`presets()` call:
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
const mails = presets({ only: ['verify-email'] });
|
|
286
|
+
|
|
287
|
+
i18n({
|
|
288
|
+
locales: ['en', 'fr'],
|
|
289
|
+
catalogues: [uiCatalogues, mails.catalogues],
|
|
290
|
+
templates: [mails.templates],
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
When `only` differs between two `presets()` calls, the catalogues of one lack
|
|
295
|
+
the messages of the templates of the other. Call it once.
|
|
296
|
+
|
|
297
|
+
### `TypeError: Cannot read properties of undefined (reading 'name')`
|
|
298
|
+
|
|
299
|
+
It follows `[Vue warn]: Failed to resolve component: NxLayout` (and
|
|
300
|
+
`NxTypography`, `NxButton`, …) and
|
|
301
|
+
`[Vue warn]: Property "brand" was accessed during render but is not defined on instance.`
|
|
302
|
+
|
|
303
|
+
**When:** `maizzle build`, on the first preset, when `ui()` is not in the
|
|
304
|
+
plugins. That includes a project that registers `@nxgt/mail-ui`'s components
|
|
305
|
+
itself, with `COMPONENTS_DIR` in `components.source`, instead of calling
|
|
306
|
+
`ui()`.
|
|
307
|
+
**Why:** the presets are built with `@nxgt/mail-ui`'s components and read
|
|
308
|
+
the brand that `ui()` gives every template (`brand.name`). Maizzle resolves
|
|
309
|
+
no component in a template under `node_modules`, where the presets are
|
|
310
|
+
installed; `ui()` does it for them. Without `ui()`, the components do not
|
|
311
|
+
resolve and `brand` is undefined.
|
|
312
|
+
**Fix:** add `ui()`, with your brand, and remove any `components.source`
|
|
313
|
+
entry that points at `COMPONENTS_DIR`:
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
export default defineMailConfig({
|
|
317
|
+
plugins: [
|
|
318
|
+
ui({ brand: { name: 'Acme', url: 'https://acme.example' } }),
|
|
319
|
+
i18n({ /* … */ }),
|
|
320
|
+
],
|
|
321
|
+
});
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### `i18n: en/new-sign-in.html is empty — a tag of its template resolved to no component; list the plugin that brings it, as ui()`
|
|
325
|
+
|
|
326
|
+
It follows `[Vue warn]: Failed to resolve component: NxLayout` (and
|
|
327
|
+
`NxTypography`, `NxButton`, …).
|
|
328
|
+
|
|
329
|
+
**When:** `maizzle build`, after the templates, when `ui()` is not in the
|
|
330
|
+
plugins and the presets built read no brand, as with
|
|
331
|
+
`presets({ only: ['new-sign-in'] })`. The other presets stop earlier, on the
|
|
332
|
+
`TypeError` above.
|
|
333
|
+
**Why:** without `ui()`, the presets' components do not resolve, and Vue
|
|
334
|
+
renders a component it cannot resolve as nothing. The e-mail would go out
|
|
335
|
+
blank.
|
|
336
|
+
**Fix:** add `ui()`, as above.
|
|
337
|
+
|
|
338
|
+
### `i18n: en: verify-email passes {brand} to verifyEmail.body, which does not use it`
|
|
339
|
+
|
|
340
|
+
**When:** `maizzle build`, on that preset, after the project overrides one of
|
|
341
|
+
its messages in every locale with a text that leaves out an argument the
|
|
342
|
+
preset's template passes, as `"body": "Confirm your address."` in
|
|
343
|
+
`locales/en.json` and `"body": "Confirmez votre adresse."` in
|
|
344
|
+
`locales/fr.json`.
|
|
345
|
+
**Why:** a template passes exactly the arguments its message uses, and one
|
|
346
|
+
more is refused: the message and the template disagree. The preset's
|
|
347
|
+
template still passes `brand`.
|
|
348
|
+
**Fix:** keep the argument in the override, or write the project's own
|
|
349
|
+
template in `emails/verify-email.vue`, which replaces the preset's:
|
|
350
|
+
|
|
351
|
+
```jsonc
|
|
352
|
+
// locales/en.json
|
|
353
|
+
{ "verifyEmail": { "body": "Confirm your address to start using {brand}." } }
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
When only `en` leaves the argument out, the build stops earlier, when the
|
|
357
|
+
config loads, on `i18n: fr: verifyEmail.body uses {brand}, which en does not
|
|
358
|
+
declare`.
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Traps: a build that succeeds and is wrong
|
|
363
|
+
|
|
364
|
+
### A bug in `@nxgt/mail-presets` itself
|
|
365
|
+
|
|
366
|
+
A preset that fails to build with the config at the top of this page, or a
|
|
367
|
+
text that differs between `en` and `fr` in meaning, is a bug in this package.
|
|
368
|
+
The same goes for a built e-mail that differs from its sample in `samples/`
|
|
369
|
+
for the same brand and theme. Open an issue on
|
|
370
|
+
[`softistx/nxgt-mail`](https://github.com/softistx/nxgt-mail/issues) with the
|
|
371
|
+
message, the package version, the Maizzle version, and your
|
|
372
|
+
`maizzle.config.ts`. Never include a real address or link.
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## Contributing to this package
|
|
377
|
+
|
|
378
|
+
### ``samples/ is what the build writes — run `bun run samples` after a change``
|
|
379
|
+
|
|
380
|
+
**When:** `bun test` in `packages/mail-presets`, after a change to a template
|
|
381
|
+
in `emails/`, a message in `src/locales/`, or anything that changes the
|
|
382
|
+
built HTML, including a change to `@nxgt/mail-ui`'s components or theme. The
|
|
383
|
+
diff shows the old text as expected and the new text as received.
|
|
384
|
+
**Why:** `samples/` holds each preset as the test fixture builds it, and
|
|
385
|
+
the spec compares a fresh build with it, file by file. The samples must stay
|
|
386
|
+
what the package builds.
|
|
387
|
+
**Fix:** rebuild them, check the diff, and commit it with the change:
|
|
388
|
+
|
|
389
|
+
```sh
|
|
390
|
+
cd packages/mail-presets
|
|
391
|
+
bun run samples # builds test/fixture, copies its HTML to samples/, rewrites samples/README.md
|
|
392
|
+
git diff samples/
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Never edit `samples/` by hand.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<NxLayout :preheader="t('emailChanged.preheader')">
|
|
3
|
+
<NxTypography variant="headline-small">{{ t('emailChanged.title') }}</NxTypography>
|
|
4
|
+
<NxTypography>{{ t('common.greeting', { name: placeholder('name') }) }}</NxTypography>
|
|
5
|
+
<NxTypography>{{ t('emailChanged.body', { brand: brand.name, newEmail: placeholder('newEmail') }) }}</NxTypography>
|
|
6
|
+
<NxAlert variant="warning" :description="t('presets.notYou')" />
|
|
7
|
+
<NxButton :href="placeholder('link')">{{ t('emailChanged.action') }}</NxButton>
|
|
8
|
+
<Spacer class="h-6" />
|
|
9
|
+
<NxTypography variant="body-small" class="text-muted-foreground">
|
|
10
|
+
{{ t('presets.linkFallback') }}
|
|
11
|
+
<NxLink :href="placeholder('link')">{{ placeholder('link') }}</NxLink>
|
|
12
|
+
</NxTypography>
|
|
13
|
+
</NxLayout>
|
|
14
|
+
</template>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<NxLayout :preheader="t('invitation.preheader', { organization: placeholder('organization'), brand: brand.name })">
|
|
3
|
+
<NxTypography variant="headline-small">{{ t('invitation.title', { organization: placeholder('organization') }) }}</NxTypography>
|
|
4
|
+
<NxTypography>{{ t('invitation.body', { inviter: placeholder('inviter'), organization: placeholder('organization'), brand: brand.name }) }}</NxTypography>
|
|
5
|
+
<NxButton :href="placeholder('link')">{{ t('invitation.action') }}</NxButton>
|
|
6
|
+
<Spacer class="h-6" />
|
|
7
|
+
<NxTypography variant="body-small" class="text-muted-foreground">
|
|
8
|
+
{{ t('presets.linkFallback') }}
|
|
9
|
+
<NxLink :href="placeholder('link')">{{ placeholder('link') }}</NxLink>
|
|
10
|
+
</NxTypography>
|
|
11
|
+
</NxLayout>
|
|
12
|
+
</template>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<NxLayout :preheader="t('magicLink.preheader')">
|
|
3
|
+
<NxTypography variant="headline-small">{{ t('magicLink.title', { brand: brand.name }) }}</NxTypography>
|
|
4
|
+
<NxTypography>{{ t('magicLink.body') }}</NxTypography>
|
|
5
|
+
<NxButton :href="placeholder('link')">{{ t('magicLink.action') }}</NxButton>
|
|
6
|
+
<Spacer class="h-6" />
|
|
7
|
+
<NxTypography variant="body-small" class="text-muted-foreground">
|
|
8
|
+
{{ t('presets.linkFallback') }}
|
|
9
|
+
<NxLink :href="placeholder('link')">{{ placeholder('link') }}</NxLink>
|
|
10
|
+
</NxTypography>
|
|
11
|
+
<NxTypography variant="caption">{{ t('common.footer.ignore') }}</NxTypography>
|
|
12
|
+
</NxLayout>
|
|
13
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<NxLayout :preheader="t('newSignIn.preheader')">
|
|
3
|
+
<NxTypography variant="headline-small">{{ t('newSignIn.title') }}</NxTypography>
|
|
4
|
+
<NxTypography>{{ t('common.greeting', { name: placeholder('name') }) }}</NxTypography>
|
|
5
|
+
<NxBanner tone="warning" :title="t('newSignIn.banner')" />
|
|
6
|
+
<NxSummaryData
|
|
7
|
+
:data="[
|
|
8
|
+
{ label: t('newSignIn.device'), value: placeholder('device') },
|
|
9
|
+
{ label: t('newSignIn.location'), value: placeholder('location') },
|
|
10
|
+
{ label: t('newSignIn.time'), value: placeholder('time') },
|
|
11
|
+
]"
|
|
12
|
+
/>
|
|
13
|
+
<NxTypography>{{ t('newSignIn.body') }} {{ t('presets.notYou') }}</NxTypography>
|
|
14
|
+
<NxButton :href="placeholder('link')">{{ t('newSignIn.action') }}</NxButton>
|
|
15
|
+
<Spacer class="h-6" />
|
|
16
|
+
<NxTypography variant="body-small" class="text-muted-foreground">
|
|
17
|
+
{{ t('presets.linkFallback') }}
|
|
18
|
+
<NxLink :href="placeholder('link')">{{ placeholder('link') }}</NxLink>
|
|
19
|
+
</NxTypography>
|
|
20
|
+
</NxLayout>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<NxLayout :preheader="t('passwordChanged.preheader')">
|
|
3
|
+
<NxTypography variant="headline-small">{{ t('passwordChanged.title') }}</NxTypography>
|
|
4
|
+
<NxTypography>{{ t('common.greeting', { name: placeholder('name') }) }}</NxTypography>
|
|
5
|
+
<NxTypography>{{ t('passwordChanged.body', { brand: brand.name }) }}</NxTypography>
|
|
6
|
+
<NxAlert variant="warning" :description="t('presets.notYou')" />
|
|
7
|
+
<NxButton :href="placeholder('link')">{{ t('passwordChanged.action') }}</NxButton>
|
|
8
|
+
<Spacer class="h-6" />
|
|
9
|
+
<NxTypography variant="body-small" class="text-muted-foreground">
|
|
10
|
+
{{ t('presets.linkFallback') }}
|
|
11
|
+
<NxLink :href="placeholder('link')">{{ placeholder('link') }}</NxLink>
|
|
12
|
+
</NxTypography>
|
|
13
|
+
</NxLayout>
|
|
14
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<NxLayout :preheader="t('resetPassword.preheader')">
|
|
3
|
+
<NxTypography variant="headline-small">{{ t('resetPassword.title') }}</NxTypography>
|
|
4
|
+
<NxTypography>{{ t('common.greeting', { name: placeholder('name') }) }}</NxTypography>
|
|
5
|
+
<NxTypography>{{ t('resetPassword.body', { brand: brand.name }) }}</NxTypography>
|
|
6
|
+
<NxButton :href="placeholder('link')">{{ t('resetPassword.action') }}</NxButton>
|
|
7
|
+
<Spacer class="h-6" />
|
|
8
|
+
<NxTypography variant="body-small" class="text-muted-foreground">
|
|
9
|
+
{{ t('presets.linkFallback') }}
|
|
10
|
+
<NxLink :href="placeholder('link')">{{ placeholder('link') }}</NxLink>
|
|
11
|
+
</NxTypography>
|
|
12
|
+
<NxTypography variant="caption">{{ t('common.footer.ignore') }}</NxTypography>
|
|
13
|
+
</NxLayout>
|
|
14
|
+
</template>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<NxLayout :preheader="t('signInCode.preheader')">
|
|
3
|
+
<NxTypography variant="headline-small">{{ t('signInCode.title') }}</NxTypography>
|
|
4
|
+
<NxTypography>{{ t('signInCode.body', { brand: brand.name }) }}</NxTypography>
|
|
5
|
+
<NxCode>{{ placeholder('code') }}</NxCode>
|
|
6
|
+
<NxTypography variant="caption">{{ t('signInCode.ignore') }}</NxTypography>
|
|
7
|
+
</NxLayout>
|
|
8
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<NxLayout :preheader="t('verifyEmail.preheader')">
|
|
3
|
+
<NxTypography variant="headline-small">{{ t('verifyEmail.title') }}</NxTypography>
|
|
4
|
+
<NxTypography>{{ t('common.greeting', { name: placeholder('name') }) }}</NxTypography>
|
|
5
|
+
<NxTypography>{{ t('verifyEmail.body', { brand: brand.name }) }}</NxTypography>
|
|
6
|
+
<NxButton :href="placeholder('link')">{{ t('verifyEmail.action') }}</NxButton>
|
|
7
|
+
<Spacer class="h-6" />
|
|
8
|
+
<NxTypography variant="body-small" class="text-muted-foreground">
|
|
9
|
+
{{ t('presets.linkFallback') }}
|
|
10
|
+
<NxLink :href="placeholder('link')">{{ placeholder('link') }}</NxLink>
|
|
11
|
+
</NxTypography>
|
|
12
|
+
<NxTypography variant="caption">{{ t('common.footer.ignore') }}</NxTypography>
|
|
13
|
+
</NxLayout>
|
|
14
|
+
</template>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<NxLayout :preheader="t('welcome.preheader')">
|
|
3
|
+
<NxTypography variant="headline-small">{{ t('welcome.title', { brand: brand.name }) }}</NxTypography>
|
|
4
|
+
<NxTypography>{{ t('common.greeting', { name: placeholder('name') }) }}</NxTypography>
|
|
5
|
+
<NxTypography>{{ t('welcome.body') }}</NxTypography>
|
|
6
|
+
<NxButton :href="placeholder('link')">{{ t('welcome.action') }}</NxButton>
|
|
7
|
+
<Spacer class="h-6" />
|
|
8
|
+
<NxTypography variant="body-small" class="text-muted-foreground">
|
|
9
|
+
{{ t('presets.linkFallback') }}
|
|
10
|
+
<NxLink :href="placeholder('link')">{{ placeholder('link') }}</NxLink>
|
|
11
|
+
</NxTypography>
|
|
12
|
+
</NxLayout>
|
|
13
|
+
</template>
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nxgt/mail-presets",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Ready transactional e-mails for a Maizzle project built with @nxgt/mail-ui and @nxgt/mail-i18n — verify-email, reset-password, sign-in-code, magic-link, new-sign-in, welcome, invitation… in en and fr — with their built HTML as samples.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"emails",
|
|
12
|
+
"docs",
|
|
13
|
+
"README.md",
|
|
14
|
+
"package.json",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"email",
|
|
27
|
+
"maizzle",
|
|
28
|
+
"templates",
|
|
29
|
+
"transactional",
|
|
30
|
+
"i18n",
|
|
31
|
+
"vue"
|
|
32
|
+
],
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/softistx/nxgt-mail.git",
|
|
36
|
+
"directory": "packages/mail-presets"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"registry": "https://registry.npmjs.org",
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "bun run ../../build.ts",
|
|
44
|
+
"test": "bun test src",
|
|
45
|
+
"typecheck": "tsc --noEmit && bun run typecheck:templates",
|
|
46
|
+
"typecheck:templates": "bun run prepare:templates && cd test/fixture && vue-tsc -p tsconfig.json",
|
|
47
|
+
"prepare:templates": "cd test/fixture && maizzle prepare",
|
|
48
|
+
"samples": "bun run scripts/samples.ts"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@maizzle/framework": "6.1.7",
|
|
52
|
+
"@maizzle/tailwindcss": "1.5.6",
|
|
53
|
+
"@nxgt/mail": "0.1.0",
|
|
54
|
+
"@nxgt/mail-config": "0.1.0",
|
|
55
|
+
"@nxgt/mail-i18n": "0.1.0",
|
|
56
|
+
"@nxgt/mail-ui": "0.1.0",
|
|
57
|
+
"@types/bun": "^1.4.2",
|
|
58
|
+
"vue": "3.5.43",
|
|
59
|
+
"vue-tsc": "^3.3.11"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"@maizzle/framework": "^6.1.7",
|
|
63
|
+
"@nxgt/mail-i18n": "^0.1.0",
|
|
64
|
+
"@nxgt/mail-ui": "^0.1.0",
|
|
65
|
+
"typescript": "^6.0.3",
|
|
66
|
+
"vue": "^3.5.0"
|
|
67
|
+
}
|
|
68
|
+
}
|