@learncard/email-templates 1.0.3 → 1.0.5
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/dist/components/Layout.d.ts +2 -0
- package/dist/components/Layout.d.ts.map +1 -1
- package/dist/components/Layout.js +6 -1
- package/dist/components/Layout.js.map +1 -1
- package/dist/components/LinkFallback.d.ts +2 -0
- package/dist/components/LinkFallback.d.ts.map +1 -1
- package/dist/components/LinkFallback.js +5 -1
- package/dist/components/LinkFallback.js.map +1 -1
- package/dist/i18n.d.ts +52 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +77 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/render.d.ts +3 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +33 -30
- package/dist/render.js.map +1 -1
- package/dist/sms.d.ts +6 -3
- package/dist/sms.d.ts.map +1 -1
- package/dist/sms.js +43 -7
- package/dist/sms.js.map +1 -1
- package/dist/templates/account-approved.d.ts +3 -1
- package/dist/templates/account-approved.d.ts.map +1 -1
- package/dist/templates/account-approved.js +55 -5
- package/dist/templates/account-approved.js.map +1 -1
- package/dist/templates/credential-awaiting-guardian.d.ts +3 -1
- package/dist/templates/credential-awaiting-guardian.d.ts.map +1 -1
- package/dist/templates/credential-awaiting-guardian.js +54 -4
- package/dist/templates/credential-awaiting-guardian.js.map +1 -1
- package/dist/templates/email-verification.d.ts +3 -1
- package/dist/templates/email-verification.d.ts.map +1 -1
- package/dist/templates/email-verification.js +57 -4
- package/dist/templates/email-verification.js.map +1 -1
- package/dist/templates/endorsement-request.d.ts +3 -1
- package/dist/templates/endorsement-request.d.ts.map +1 -1
- package/dist/templates/endorsement-request.js +53 -5
- package/dist/templates/endorsement-request.js.map +1 -1
- package/dist/templates/guardian-approval.d.ts +3 -1
- package/dist/templates/guardian-approval.d.ts.map +1 -1
- package/dist/templates/guardian-approval.js +55 -5
- package/dist/templates/guardian-approval.js.map +1 -1
- package/dist/templates/guardian-approved-claim.d.ts +3 -1
- package/dist/templates/guardian-approved-claim.d.ts.map +1 -1
- package/dist/templates/guardian-approved-claim.js +50 -4
- package/dist/templates/guardian-approved-claim.js.map +1 -1
- package/dist/templates/guardian-credential-approval.d.ts +3 -1
- package/dist/templates/guardian-credential-approval.d.ts.map +1 -1
- package/dist/templates/guardian-credential-approval.js +50 -4
- package/dist/templates/guardian-credential-approval.js.map +1 -1
- package/dist/templates/guardian-email-otp.d.ts +3 -1
- package/dist/templates/guardian-email-otp.d.ts.map +1 -1
- package/dist/templates/guardian-email-otp.js +55 -5
- package/dist/templates/guardian-email-otp.js.map +1 -1
- package/dist/templates/guardian-rejected-credential.d.ts +3 -1
- package/dist/templates/guardian-rejected-credential.d.ts.map +1 -1
- package/dist/templates/guardian-rejected-credential.js +45 -3
- package/dist/templates/guardian-rejected-credential.js.map +1 -1
- package/dist/templates/inbox-claim.d.ts +3 -1
- package/dist/templates/inbox-claim.d.ts.map +1 -1
- package/dist/templates/inbox-claim.js +63 -11
- package/dist/templates/inbox-claim.js.map +1 -1
- package/dist/templates/recovery-key.d.ts +3 -1
- package/dist/templates/recovery-key.d.ts.map +1 -1
- package/dist/templates/recovery-key.js +59 -3
- package/dist/templates/recovery-key.js.map +1 -1
- package/dist/templates/verification-code.d.ts +3 -1
- package/dist/templates/verification-code.d.ts.map +1 -1
- package/dist/templates/verification-code.js +153 -17
- package/dist/templates/verification-code.js.map +1 -1
- package/dist/tenant-registry.d.ts.map +1 -1
- package/dist/tenant-registry.js +17 -0
- package/dist/tenant-registry.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/i18n.test.ts +329 -0
- package/src/__tests__/tenant-registry.test.ts +24 -0
- package/src/components/Layout.tsx +69 -60
- package/src/components/LinkFallback.tsx +19 -11
- package/src/i18n.ts +110 -0
- package/src/index.ts +15 -2
- package/src/render.ts +47 -28
- package/src/sms.ts +61 -6
- package/src/templates/account-approved.tsx +82 -29
- package/src/templates/credential-awaiting-guardian.tsx +113 -19
- package/src/templates/email-verification.tsx +87 -16
- package/src/templates/endorsement-request.tsx +142 -17
- package/src/templates/guardian-approval.tsx +106 -16
- package/src/templates/guardian-approved-claim.tsx +137 -17
- package/src/templates/guardian-credential-approval.tsx +112 -17
- package/src/templates/guardian-email-otp.tsx +103 -25
- package/src/templates/guardian-rejected-credential.tsx +137 -15
- package/src/templates/inbox-claim.tsx +180 -29
- package/src/templates/recovery-key.tsx +126 -30
- package/src/templates/verification-code.tsx +204 -38
- package/src/tenant-registry.ts +24 -2
|
@@ -17,6 +17,8 @@ import type { TenantBranding } from '../branding';
|
|
|
17
17
|
import { DEFAULT_BRANDING } from '../branding';
|
|
18
18
|
import { Layout } from '../components/Layout';
|
|
19
19
|
import { CodeBlock } from '../components/CodeBlock';
|
|
20
|
+
import type { NotificationLocale } from '../i18n';
|
|
21
|
+
import { resolveCatalogLocale, interpolate, SHARED } from '../i18n';
|
|
20
22
|
|
|
21
23
|
export type VerificationCodeVariant =
|
|
22
24
|
| 'login'
|
|
@@ -29,29 +31,33 @@ export interface VerificationCodeProps {
|
|
|
29
31
|
verificationCode: string;
|
|
30
32
|
verificationEmail?: string;
|
|
31
33
|
variant: VerificationCodeVariant;
|
|
34
|
+
/** Recipient locale (BCP-47). Defaults to English. */
|
|
35
|
+
locale?: string;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Per-variant, per-locale strings. `description` is a function because the
|
|
40
|
+
* login/recovery-email/embed-verification variants historically branched on
|
|
41
|
+
* whether a verification email address was present (two distinct EN strings).
|
|
42
|
+
* EN values byte-match the original templates.
|
|
43
|
+
*/
|
|
44
|
+
type VariantStrings = {
|
|
45
|
+
subject: string;
|
|
46
|
+
heading: string;
|
|
47
|
+
description: (email: string | undefined, brandName: string) => string;
|
|
48
|
+
expiry: string;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const EN: Record<VerificationCodeVariant, VariantStrings> = {
|
|
43
52
|
login: {
|
|
44
|
-
subject:
|
|
45
|
-
heading:
|
|
46
|
-
description: (
|
|
47
|
-
email
|
|
48
|
-
? `Here's your secure 6-digit code to log into ${b}`
|
|
49
|
-
: `Here's your secure 6-digit code to log into ${b}`,
|
|
53
|
+
subject: 'Your {brandName} login code',
|
|
54
|
+
heading: 'Your {brandName} Login Code',
|
|
55
|
+
description: (_email, b) => `Here's your secure 6-digit code to log into ${b}`,
|
|
50
56
|
expiry: 'This code will expire in 5 minutes. If you didn\u2019t request this, you can safely ignore this email.',
|
|
51
57
|
},
|
|
52
58
|
'recovery-email': {
|
|
53
|
-
subject:
|
|
54
|
-
heading:
|
|
59
|
+
subject: 'Verify your recovery email',
|
|
60
|
+
heading: 'Your {brandName} Verification Code',
|
|
55
61
|
description: (email, b) =>
|
|
56
62
|
email
|
|
57
63
|
? `Here's your secure 6-digit code to add ${email} as a recovery method for ${b}`
|
|
@@ -59,8 +65,8 @@ const VARIANT_CONFIG: Record<
|
|
|
59
65
|
expiry: 'This code will expire in 5 minutes. If you didn\u2019t request this, you can safely ignore this email.',
|
|
60
66
|
},
|
|
61
67
|
'embed-verification': {
|
|
62
|
-
subject:
|
|
63
|
-
heading:
|
|
68
|
+
subject: 'Your {brandName} verification code',
|
|
69
|
+
heading: 'Your {brandName} Verification Code',
|
|
64
70
|
description: email =>
|
|
65
71
|
email
|
|
66
72
|
? `Enter this code to verify ${email} and claim your credential:`
|
|
@@ -68,44 +74,200 @@ const VARIANT_CONFIG: Record<
|
|
|
68
74
|
expiry: 'This code will expire in 10 minutes. If you didn\u2019t request this, you can safely ignore this email.',
|
|
69
75
|
},
|
|
70
76
|
'contact-method': {
|
|
71
|
-
subject:
|
|
72
|
-
heading:
|
|
77
|
+
subject: 'Your {brandName} verification code',
|
|
78
|
+
heading: 'Your {brandName} Verification Code',
|
|
73
79
|
description: () => 'Enter this code in the app to verify your contact information.',
|
|
74
80
|
expiry: 'This code will expire in 24 hours. If you didn\u2019t request this, you can safely ignore this email.',
|
|
75
81
|
},
|
|
76
82
|
};
|
|
77
83
|
|
|
84
|
+
const ES: Record<VerificationCodeVariant, VariantStrings> = {
|
|
85
|
+
login: {
|
|
86
|
+
subject: 'Tu código de acceso a {brandName}',
|
|
87
|
+
heading: 'Tu código de acceso a {brandName}',
|
|
88
|
+
description: (_email, b) =>
|
|
89
|
+
`Aquí tienes tu código seguro de 6 dígitos para iniciar sesión en ${b}`,
|
|
90
|
+
expiry: 'Este código caducará en 5 minutos. Si no solicitaste esto, puedes ignorar este correo con seguridad.',
|
|
91
|
+
},
|
|
92
|
+
'recovery-email': {
|
|
93
|
+
subject: 'Verifica tu correo de recuperación',
|
|
94
|
+
heading: 'Tu código de verificación de {brandName}',
|
|
95
|
+
description: (email, b) =>
|
|
96
|
+
email
|
|
97
|
+
? `Aquí tienes tu código seguro de 6 dígitos para añadir ${email} como método de recuperación de ${b}`
|
|
98
|
+
: `Aquí tienes tu código seguro de 6 dígitos para añadir un método de recuperación de ${b}`,
|
|
99
|
+
expiry: 'Este código caducará en 5 minutos. Si no solicitaste esto, puedes ignorar este correo con seguridad.',
|
|
100
|
+
},
|
|
101
|
+
'embed-verification': {
|
|
102
|
+
subject: 'Tu código de verificación de {brandName}',
|
|
103
|
+
heading: 'Tu código de verificación de {brandName}',
|
|
104
|
+
description: email =>
|
|
105
|
+
email
|
|
106
|
+
? `Introduce este código para verificar ${email} y reclamar tu credencial:`
|
|
107
|
+
: 'Introduce este código para verificar tu dirección de correo y reclamar tu credencial:',
|
|
108
|
+
expiry: 'Este código caducará en 10 minutos. Si no solicitaste esto, puedes ignorar este correo con seguridad.',
|
|
109
|
+
},
|
|
110
|
+
'contact-method': {
|
|
111
|
+
subject: 'Tu código de verificación de {brandName}',
|
|
112
|
+
heading: 'Tu código de verificación de {brandName}',
|
|
113
|
+
description: () =>
|
|
114
|
+
'Introduce este código en la app para verificar tu información de contacto.',
|
|
115
|
+
expiry: 'Este código caducará en 24 horas. Si no solicitaste esto, puedes ignorar este correo con seguridad.',
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const FR: Record<VerificationCodeVariant, VariantStrings> = {
|
|
120
|
+
login: {
|
|
121
|
+
subject: 'Votre code de connexion {brandName}',
|
|
122
|
+
heading: 'Votre code de connexion {brandName}',
|
|
123
|
+
description: (_email, b) =>
|
|
124
|
+
`Voici votre code sécurisé à 6 chiffres pour vous connecter à ${b}`,
|
|
125
|
+
expiry: 'Ce code expirera dans 5 minutes. Si vous n\u2019êtes pas à l\u2019origine de cette demande, vous pouvez ignorer cet e-mail en toute sécurité.',
|
|
126
|
+
},
|
|
127
|
+
'recovery-email': {
|
|
128
|
+
subject: 'Vérifiez votre e-mail de récupération',
|
|
129
|
+
heading: 'Votre code de vérification {brandName}',
|
|
130
|
+
description: (email, b) =>
|
|
131
|
+
email
|
|
132
|
+
? `Voici votre code sécurisé à 6 chiffres pour ajouter ${email} comme méthode de récupération à ${b}`
|
|
133
|
+
: `Voici votre code sécurisé à 6 chiffres pour ajouter une méthode de récupération à ${b}`,
|
|
134
|
+
expiry: 'Ce code expirera dans 5 minutes. Si vous n\u2019êtes pas à l\u2019origine de cette demande, vous pouvez ignorer cet e-mail en toute sécurité.',
|
|
135
|
+
},
|
|
136
|
+
'embed-verification': {
|
|
137
|
+
subject: 'Votre code de vérification {brandName}',
|
|
138
|
+
heading: 'Votre code de vérification {brandName}',
|
|
139
|
+
description: email =>
|
|
140
|
+
email
|
|
141
|
+
? `Saisissez ce code pour vérifier ${email} et réclamer votre titre :`
|
|
142
|
+
: 'Saisissez ce code pour vérifier votre adresse e-mail et réclamer votre titre :',
|
|
143
|
+
expiry: 'Ce code expirera dans 10 minutes. Si vous n\u2019êtes pas à l\u2019origine de cette demande, vous pouvez ignorer cet e-mail en toute sécurité.',
|
|
144
|
+
},
|
|
145
|
+
'contact-method': {
|
|
146
|
+
subject: 'Votre code de vérification {brandName}',
|
|
147
|
+
heading: 'Votre code de vérification {brandName}',
|
|
148
|
+
description: () =>
|
|
149
|
+
'Saisissez ce code dans l\u2019application pour vérifier vos coordonnées.',
|
|
150
|
+
expiry: 'Ce code expirera dans 24 heures. Si vous n\u2019êtes pas à l\u2019origine de cette demande, vous pouvez ignorer cet e-mail en toute sécurité.',
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const AR: Record<VerificationCodeVariant, VariantStrings> = {
|
|
155
|
+
login: {
|
|
156
|
+
subject: 'رمز تسجيل الدخول إلى {brandName}',
|
|
157
|
+
heading: 'رمز تسجيل الدخول إلى {brandName}',
|
|
158
|
+
description: (_email, b) => `إليك رمزك الآمن المكوّن من 6 أرقام لتسجيل الدخول إلى ${b}`,
|
|
159
|
+
expiry: 'تنتهي صلاحية هذا الرمز خلال 5 دقائق. إذا لم تطلب ذلك، يمكنك تجاهل هذه الرسالة بأمان.',
|
|
160
|
+
},
|
|
161
|
+
'recovery-email': {
|
|
162
|
+
subject: 'تأكيد بريد الاستعادة الخاص بك',
|
|
163
|
+
heading: 'رمز التحقق الخاص بك في {brandName}',
|
|
164
|
+
description: (email, b) =>
|
|
165
|
+
email
|
|
166
|
+
? `إليك رمزك الآمن المكوّن من 6 أرقام لإضافة ${email} كطريقة استعادة في ${b}`
|
|
167
|
+
: `إليك رمزك الآمن المكوّن من 6 أرقام لإضافة طريقة استعادة في ${b}`,
|
|
168
|
+
expiry: 'تنتهي صلاحية هذا الرمز خلال 5 دقائق. إذا لم تطلب ذلك، يمكنك تجاهل هذه الرسالة بأمان.',
|
|
169
|
+
},
|
|
170
|
+
'embed-verification': {
|
|
171
|
+
subject: 'رمز التحقق الخاص بك في {brandName}',
|
|
172
|
+
heading: 'رمز التحقق الخاص بك في {brandName}',
|
|
173
|
+
description: email =>
|
|
174
|
+
email
|
|
175
|
+
? `أدخل هذا الرمز لتأكيد ${email} والمطالبة بشهادتك:`
|
|
176
|
+
: 'أدخل هذا الرمز لتأكيد عنوان بريدك الإلكتروني والمطالبة بشهادتك:',
|
|
177
|
+
expiry: 'تنتهي صلاحية هذا الرمز خلال 10 دقائق. إذا لم تطلب ذلك، يمكنك تجاهل هذه الرسالة بأمان.',
|
|
178
|
+
},
|
|
179
|
+
'contact-method': {
|
|
180
|
+
subject: 'رمز التحقق الخاص بك في {brandName}',
|
|
181
|
+
heading: 'رمز التحقق الخاص بك في {brandName}',
|
|
182
|
+
description: () => 'أدخل هذا الرمز في التطبيق لتأكيد معلومات الاتصال الخاصة بك.',
|
|
183
|
+
expiry: 'تنتهي صلاحية هذا الرمز خلال 24 ساعة. إذا لم تطلب ذلك، يمكنك تجاهل هذه الرسالة بأمان.',
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const CATALOGS: Record<NotificationLocale, Record<VerificationCodeVariant, VariantStrings>> = {
|
|
188
|
+
en: EN,
|
|
189
|
+
es: ES,
|
|
190
|
+
fr: FR,
|
|
191
|
+
ar: AR,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/** Shared body strings (interpolates {brandName}). */
|
|
195
|
+
const BODY: Record<
|
|
196
|
+
NotificationLocale,
|
|
197
|
+
{ preview: string; greeting: string; passport: string; sincerely: string; codeLabel: string }
|
|
198
|
+
> = {
|
|
199
|
+
en: {
|
|
200
|
+
preview: 'Your code: {code}',
|
|
201
|
+
greeting: 'Hello,',
|
|
202
|
+
passport:
|
|
203
|
+
'{brandName} is your private, digital passport for learning and work. It lets you securely collect and share your verified skills and achievements online.',
|
|
204
|
+
sincerely: 'Sincerely,',
|
|
205
|
+
codeLabel: 'Verification code',
|
|
206
|
+
},
|
|
207
|
+
es: {
|
|
208
|
+
preview: 'Tu código: {code}',
|
|
209
|
+
greeting: 'Hola,',
|
|
210
|
+
passport:
|
|
211
|
+
'{brandName} es tu pasaporte digital y privado para el aprendizaje y el trabajo. Te permite recopilar y compartir de forma segura tus habilidades y logros verificados en línea.',
|
|
212
|
+
sincerely: 'Atentamente,',
|
|
213
|
+
codeLabel: 'Código de verificación',
|
|
214
|
+
},
|
|
215
|
+
fr: {
|
|
216
|
+
preview: 'Votre code : {code}',
|
|
217
|
+
greeting: 'Bonjour,',
|
|
218
|
+
passport:
|
|
219
|
+
'{brandName} est votre passeport numérique privé pour l\u2019apprentissage et le travail. Il vous permet de collecter et de partager en toute sécurité vos compétences et réalisations vérifiées en ligne.',
|
|
220
|
+
sincerely: 'Cordialement,',
|
|
221
|
+
codeLabel: 'Code de vérification',
|
|
222
|
+
},
|
|
223
|
+
ar: {
|
|
224
|
+
preview: 'رمزك: {code}',
|
|
225
|
+
greeting: 'مرحبًا،',
|
|
226
|
+
passport:
|
|
227
|
+
'{brandName} هو جواز سفرك الرقمي والخاص للتعلّم والعمل. يتيح لك جمع ومشاركة مهاراتك وإنجازاتك الموثّقة بأمان عبر الإنترنت.',
|
|
228
|
+
sincerely: 'مع التحيات،',
|
|
229
|
+
codeLabel: 'رمز التحقق',
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
|
|
78
233
|
export const VerificationCode: React.FC<VerificationCodeProps> = ({
|
|
79
234
|
branding,
|
|
80
235
|
verificationCode,
|
|
81
236
|
verificationEmail,
|
|
82
237
|
variant,
|
|
238
|
+
locale,
|
|
83
239
|
}) => {
|
|
84
|
-
const
|
|
240
|
+
const loc = resolveCatalogLocale(locale);
|
|
241
|
+
const config = CATALOGS[loc][variant];
|
|
242
|
+
const body = BODY[loc];
|
|
243
|
+
const heading = interpolate(config.heading, { brandName: branding.brandName });
|
|
244
|
+
const description = config.description(verificationEmail, branding.brandName);
|
|
245
|
+
const passport = interpolate(body.passport, { brandName: branding.brandName });
|
|
85
246
|
|
|
86
247
|
return (
|
|
87
|
-
<Layout
|
|
88
|
-
|
|
248
|
+
<Layout
|
|
249
|
+
branding={branding}
|
|
250
|
+
locale={locale}
|
|
251
|
+
preview={interpolate(body.preview, { code: verificationCode })}
|
|
252
|
+
>
|
|
253
|
+
<Text style={headingStyle}>{heading}</Text>
|
|
89
254
|
|
|
90
|
-
<Text style={paragraph}>
|
|
255
|
+
<Text style={paragraph}>{body.greeting}</Text>
|
|
91
256
|
|
|
92
|
-
<Text style={paragraph}>
|
|
93
|
-
{config.description(verificationEmail, branding.brandName)}
|
|
94
|
-
</Text>
|
|
257
|
+
<Text style={paragraph}>{description}</Text>
|
|
95
258
|
|
|
96
|
-
<CodeBlock code={verificationCode} label=
|
|
259
|
+
<CodeBlock code={verificationCode} label={body.codeLabel} />
|
|
97
260
|
|
|
98
261
|
<Text style={muted}>{config.expiry}</Text>
|
|
99
262
|
|
|
100
|
-
<Text style={paragraph}>
|
|
101
|
-
{branding.brandName} is your private, digital passport for learning and work. It
|
|
102
|
-
lets you securely collect and share your verified skills and achievements online.
|
|
103
|
-
</Text>
|
|
263
|
+
<Text style={paragraph}>{passport}</Text>
|
|
104
264
|
|
|
105
265
|
<Text style={signOff}>
|
|
106
|
-
|
|
266
|
+
{body.sincerely}
|
|
107
267
|
<br />
|
|
108
|
-
|
|
268
|
+
{interpolate(SHARED[resolveCatalogLocale(locale)].teamSignature, {
|
|
269
|
+
brandName: branding.brandName,
|
|
270
|
+
})}
|
|
109
271
|
</Text>
|
|
110
272
|
</Layout>
|
|
111
273
|
);
|
|
@@ -113,8 +275,12 @@ export const VerificationCode: React.FC<VerificationCodeProps> = ({
|
|
|
113
275
|
|
|
114
276
|
export const getVerificationCodeSubject = (
|
|
115
277
|
branding: TenantBranding,
|
|
116
|
-
variant: VerificationCodeVariant
|
|
117
|
-
|
|
278
|
+
variant: VerificationCodeVariant,
|
|
279
|
+
locale?: string
|
|
280
|
+
): string => {
|
|
281
|
+
const config = CATALOGS[resolveCatalogLocale(locale)][variant];
|
|
282
|
+
return interpolate(config.subject, { brandName: branding.brandName });
|
|
283
|
+
};
|
|
118
284
|
|
|
119
285
|
// ---------------------------------------------------------------------------
|
|
120
286
|
// Styles
|
|
@@ -149,7 +315,7 @@ const signOff: React.CSSProperties = {
|
|
|
149
315
|
};
|
|
150
316
|
|
|
151
317
|
// ---------------------------------------------------------------------------
|
|
152
|
-
// Preview (used by `
|
|
318
|
+
// Preview (used by `pnpm dev` / react-email dev server)
|
|
153
319
|
// ---------------------------------------------------------------------------
|
|
154
320
|
|
|
155
321
|
export default function Preview() {
|
package/src/tenant-registry.ts
CHANGED
|
@@ -54,6 +54,12 @@ const ORIGIN_MAP: Record<string, string> = {
|
|
|
54
54
|
'alpha.vetpass.app': 'vetpass',
|
|
55
55
|
'staging.vetpass.app': 'vetpass',
|
|
56
56
|
|
|
57
|
+
// ScoutPass
|
|
58
|
+
'scout.org': 'scoutpass',
|
|
59
|
+
'www.scout.org': 'scoutpass',
|
|
60
|
+
'pass.scout.org': 'scoutpass',
|
|
61
|
+
'staging.pass.scout.org': 'scoutpass',
|
|
62
|
+
|
|
57
63
|
// LearnCard
|
|
58
64
|
'learncard.app': 'learncard',
|
|
59
65
|
'staging.learncard.app': 'learncard',
|
|
@@ -85,6 +91,19 @@ const TENANT_EMAIL_BRANDING: Record<string, Partial<TenantBranding>> = {
|
|
|
85
91
|
fromDomain: 'vetpass.app',
|
|
86
92
|
copyrightHolder: 'VetPass',
|
|
87
93
|
},
|
|
94
|
+
|
|
95
|
+
scoutpass: {
|
|
96
|
+
brandName: 'ScoutPass',
|
|
97
|
+
logoUrl: 'https://pass.scout.org/assets/icon/icon.png',
|
|
98
|
+
logoAlt: 'ScoutPass',
|
|
99
|
+
primaryColor: '#00BA88',
|
|
100
|
+
primaryTextColor: '#ffffff',
|
|
101
|
+
supportEmail: 'support@scout.org',
|
|
102
|
+
websiteUrl: 'https://scout.org/scoutpass',
|
|
103
|
+
appUrl: 'https://pass.scout.org',
|
|
104
|
+
fromDomain: 'scoutnetwork.org',
|
|
105
|
+
copyrightHolder: 'ScoutPass',
|
|
106
|
+
},
|
|
88
107
|
};
|
|
89
108
|
|
|
90
109
|
// ---------------------------------------------------------------------------
|
|
@@ -129,7 +148,7 @@ const tenantIdFromHostname = (hostname: string): string | undefined => {
|
|
|
129
148
|
*/
|
|
130
149
|
export const resolveTenantFromRequest = (
|
|
131
150
|
headers: RequestHeaders,
|
|
132
|
-
envTenantId?: string
|
|
151
|
+
envTenantId?: string
|
|
133
152
|
): ResolvedTenant => {
|
|
134
153
|
// 1. Explicit X-Tenant-Id header (native apps, any informed client)
|
|
135
154
|
const explicit = normalizeHeader(headers['x-tenant-id']);
|
|
@@ -196,7 +215,10 @@ export const registerOriginMapping = (hostname: string, tenantId: string): void
|
|
|
196
215
|
* Register or update email branding for a tenant at startup.
|
|
197
216
|
* Merges with any existing branding (does not replace).
|
|
198
217
|
*/
|
|
199
|
-
export const registerTenantBranding = (
|
|
218
|
+
export const registerTenantBranding = (
|
|
219
|
+
tenantId: string,
|
|
220
|
+
branding: Partial<TenantBranding>
|
|
221
|
+
): void => {
|
|
200
222
|
TENANT_EMAIL_BRANDING[tenantId] = {
|
|
201
223
|
...TENANT_EMAIL_BRANDING[tenantId],
|
|
202
224
|
...branding,
|