@learncard/email-templates 1.1.0 → 1.2.1
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/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/previews/escrow-hold-cancelled-scenarios.d.ts +6 -0
- package/dist/previews/escrow-hold-cancelled-scenarios.d.ts.map +1 -0
- package/dist/previews/escrow-hold-cancelled-scenarios.js +7 -0
- package/dist/previews/escrow-hold-cancelled-scenarios.js.map +1 -0
- package/dist/previews/escrow-hold-released-scenarios.d.ts +6 -0
- package/dist/previews/escrow-hold-released-scenarios.d.ts.map +1 -0
- package/dist/previews/escrow-hold-released-scenarios.js +7 -0
- package/dist/previews/escrow-hold-released-scenarios.js.map +1 -0
- package/dist/previews/escrow-hold-reminder-scenarios.d.ts +6 -0
- package/dist/previews/escrow-hold-reminder-scenarios.d.ts.map +1 -0
- package/dist/previews/escrow-hold-reminder-scenarios.js +7 -0
- package/dist/previews/escrow-hold-reminder-scenarios.js.map +1 -0
- package/dist/previews/escrow-hold-started-scenarios.d.ts +6 -0
- package/dist/previews/escrow-hold-started-scenarios.d.ts.map +1 -0
- package/dist/previews/escrow-hold-started-scenarios.js +7 -0
- package/dist/previews/escrow-hold-started-scenarios.js.map +1 -0
- package/dist/previews/escrow-pin-locked-scenarios.d.ts +6 -0
- package/dist/previews/escrow-pin-locked-scenarios.d.ts.map +1 -0
- package/dist/previews/escrow-pin-locked-scenarios.js +7 -0
- package/dist/previews/escrow-pin-locked-scenarios.js.map +1 -0
- package/dist/previews/recovery-key-scenarios.d.ts.map +1 -1
- package/dist/previews/recovery-key-scenarios.js +1 -1
- package/dist/previews/recovery-key-scenarios.js.map +1 -1
- package/dist/render.d.ts +38 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +48 -1
- package/dist/render.js.map +1 -1
- package/dist/templates/account-sign-in-changed.d.ts +11 -0
- package/dist/templates/account-sign-in-changed.d.ts.map +1 -0
- package/dist/templates/account-sign-in-changed.js +70 -0
- package/dist/templates/account-sign-in-changed.js.map +1 -0
- package/dist/templates/escrow-hold-cancelled.d.ts +23 -0
- package/dist/templates/escrow-hold-cancelled.d.ts.map +1 -0
- package/dist/templates/escrow-hold-cancelled.js +131 -0
- package/dist/templates/escrow-hold-cancelled.js.map +1 -0
- package/dist/templates/escrow-hold-released.d.ts +21 -0
- package/dist/templates/escrow-hold-released.d.ts.map +1 -0
- package/dist/templates/escrow-hold-released.js +92 -0
- package/dist/templates/escrow-hold-released.js.map +1 -0
- package/dist/templates/escrow-hold-reminder.d.ts +21 -0
- package/dist/templates/escrow-hold-reminder.d.ts.map +1 -0
- package/dist/templates/escrow-hold-reminder.js +92 -0
- package/dist/templates/escrow-hold-reminder.js.map +1 -0
- package/dist/templates/escrow-hold-started.d.ts +25 -0
- package/dist/templates/escrow-hold-started.d.ts.map +1 -0
- package/dist/templates/escrow-hold-started.js +96 -0
- package/dist/templates/escrow-hold-started.js.map +1 -0
- package/dist/templates/escrow-pin-locked.d.ts +24 -0
- package/dist/templates/escrow-pin-locked.d.ts.map +1 -0
- package/dist/templates/escrow-pin-locked.js +97 -0
- package/dist/templates/escrow-pin-locked.js.map +1 -0
- package/dist/templates/index.d.ts +12 -0
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +6 -0
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/recovery-key.d.ts +1 -0
- package/dist/templates/recovery-key.d.ts.map +1 -1
- package/dist/templates/recovery-key.js +11 -3
- package/dist/templates/recovery-key.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/i18n.test.ts +39 -2
- package/src/__tests__/render.test.ts +195 -0
- package/src/index.ts +7 -0
- package/src/previews/escrow-hold-cancelled-scenarios.tsx +79 -0
- package/src/previews/escrow-hold-released-scenarios.tsx +58 -0
- package/src/previews/escrow-hold-reminder-scenarios.tsx +52 -0
- package/src/previews/escrow-hold-started-scenarios.tsx +67 -0
- package/src/previews/escrow-pin-locked-scenarios.tsx +60 -0
- package/src/previews/recovery-key-scenarios.tsx +3 -0
- package/src/render.ts +128 -0
- package/src/templates/account-sign-in-changed.tsx +113 -0
- package/src/templates/escrow-hold-cancelled.tsx +212 -0
- package/src/templates/escrow-hold-released.tsx +184 -0
- package/src/templates/escrow-hold-reminder.tsx +184 -0
- package/src/templates/escrow-hold-started.tsx +216 -0
- package/src/templates/escrow-pin-locked.tsx +233 -0
- package/src/templates/index.ts +17 -0
- package/src/templates/recovery-key.tsx +26 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* escrow-hold-cancelled — all preview scenarios stacked (one per reason).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
|
|
7
|
+
import { EscrowHoldCancelled } from '../templates/escrow-hold-cancelled';
|
|
8
|
+
import { LEARNCARD_BRANDING, VETPASS_BRANDING, ScenarioDivider } from './_fixtures';
|
|
9
|
+
|
|
10
|
+
export default function EscrowHoldCancelledScenarios() {
|
|
11
|
+
return (
|
|
12
|
+
<>
|
|
13
|
+
<ScenarioDivider label="Reason: user" />
|
|
14
|
+
<EscrowHoldCancelled
|
|
15
|
+
branding={LEARNCARD_BRANDING}
|
|
16
|
+
cancelledAt="September 26, 2026, 9:15 AM"
|
|
17
|
+
reason="user"
|
|
18
|
+
/>
|
|
19
|
+
|
|
20
|
+
<ScenarioDivider label="Reason: superseded" />
|
|
21
|
+
<EscrowHoldCancelled
|
|
22
|
+
branding={LEARNCARD_BRANDING}
|
|
23
|
+
cancelledAt="September 26, 2026, 9:15 AM"
|
|
24
|
+
reason="superseded"
|
|
25
|
+
/>
|
|
26
|
+
|
|
27
|
+
<ScenarioDivider label="Reason: pin-locked" />
|
|
28
|
+
<EscrowHoldCancelled
|
|
29
|
+
branding={LEARNCARD_BRANDING}
|
|
30
|
+
cancelledAt="September 26, 2026, 9:15 AM"
|
|
31
|
+
reason="pin-locked"
|
|
32
|
+
/>
|
|
33
|
+
|
|
34
|
+
<ScenarioDivider label="Reason: release-failed" />
|
|
35
|
+
<EscrowHoldCancelled
|
|
36
|
+
branding={LEARNCARD_BRANDING}
|
|
37
|
+
cancelledAt="September 26, 2026, 9:15 AM"
|
|
38
|
+
reason="release-failed"
|
|
39
|
+
/>
|
|
40
|
+
|
|
41
|
+
<ScenarioDivider label="No reason (generic message)" />
|
|
42
|
+
<EscrowHoldCancelled
|
|
43
|
+
branding={LEARNCARD_BRANDING}
|
|
44
|
+
cancelledAt="September 26, 2026, 9:15 AM"
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
<ScenarioDivider label="Spanish" />
|
|
48
|
+
<EscrowHoldCancelled
|
|
49
|
+
branding={LEARNCARD_BRANDING}
|
|
50
|
+
cancelledAt="26 de septiembre de 2026, 9:15 a.m."
|
|
51
|
+
reason="user"
|
|
52
|
+
locale="es"
|
|
53
|
+
/>
|
|
54
|
+
|
|
55
|
+
<ScenarioDivider label="French" />
|
|
56
|
+
<EscrowHoldCancelled
|
|
57
|
+
branding={LEARNCARD_BRANDING}
|
|
58
|
+
cancelledAt="26 septembre 2026, 9h15"
|
|
59
|
+
reason="user"
|
|
60
|
+
locale="fr"
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
<ScenarioDivider label="Arabic (RTL)" />
|
|
64
|
+
<EscrowHoldCancelled
|
|
65
|
+
branding={LEARNCARD_BRANDING}
|
|
66
|
+
cancelledAt="26 سبتمبر 2026، 9:15 ص"
|
|
67
|
+
reason="user"
|
|
68
|
+
locale="ar"
|
|
69
|
+
/>
|
|
70
|
+
|
|
71
|
+
<ScenarioDivider label="VetPass tenant branding" />
|
|
72
|
+
<EscrowHoldCancelled
|
|
73
|
+
branding={VETPASS_BRANDING}
|
|
74
|
+
cancelledAt="September 26, 2026, 9:15 AM"
|
|
75
|
+
reason="user"
|
|
76
|
+
/>
|
|
77
|
+
</>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* escrow-hold-released — all preview scenarios stacked.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
|
|
7
|
+
import { EscrowHoldReleased } from '../templates/escrow-hold-released';
|
|
8
|
+
import { LEARNCARD_BRANDING, VETPASS_BRANDING, ScenarioDivider } from './_fixtures';
|
|
9
|
+
|
|
10
|
+
export default function EscrowHoldReleasedScenarios() {
|
|
11
|
+
return (
|
|
12
|
+
<>
|
|
13
|
+
<ScenarioDivider label="With support link" />
|
|
14
|
+
<EscrowHoldReleased
|
|
15
|
+
branding={LEARNCARD_BRANDING}
|
|
16
|
+
completedAt="October 2, 2026, 3:00 PM"
|
|
17
|
+
supportUrl="https://learncard.app/support"
|
|
18
|
+
/>
|
|
19
|
+
|
|
20
|
+
<ScenarioDivider label="No support link (footer contact only)" />
|
|
21
|
+
<EscrowHoldReleased
|
|
22
|
+
branding={LEARNCARD_BRANDING}
|
|
23
|
+
completedAt="October 2, 2026, 3:00 PM"
|
|
24
|
+
/>
|
|
25
|
+
|
|
26
|
+
<ScenarioDivider label="Spanish" />
|
|
27
|
+
<EscrowHoldReleased
|
|
28
|
+
branding={LEARNCARD_BRANDING}
|
|
29
|
+
completedAt="2 de octubre de 2026, 3:00 p.m."
|
|
30
|
+
supportUrl="https://learncard.app/support"
|
|
31
|
+
locale="es"
|
|
32
|
+
/>
|
|
33
|
+
|
|
34
|
+
<ScenarioDivider label="French" />
|
|
35
|
+
<EscrowHoldReleased
|
|
36
|
+
branding={LEARNCARD_BRANDING}
|
|
37
|
+
completedAt="2 octobre 2026, 15h00"
|
|
38
|
+
supportUrl="https://learncard.app/support"
|
|
39
|
+
locale="fr"
|
|
40
|
+
/>
|
|
41
|
+
|
|
42
|
+
<ScenarioDivider label="Arabic (RTL)" />
|
|
43
|
+
<EscrowHoldReleased
|
|
44
|
+
branding={LEARNCARD_BRANDING}
|
|
45
|
+
completedAt="2 أكتوبر 2026، 3:00 م"
|
|
46
|
+
supportUrl="https://learncard.app/support"
|
|
47
|
+
locale="ar"
|
|
48
|
+
/>
|
|
49
|
+
|
|
50
|
+
<ScenarioDivider label="VetPass tenant branding" />
|
|
51
|
+
<EscrowHoldReleased
|
|
52
|
+
branding={VETPASS_BRANDING}
|
|
53
|
+
completedAt="October 2, 2026, 3:00 PM"
|
|
54
|
+
supportUrl="https://vetpass.app/support"
|
|
55
|
+
/>
|
|
56
|
+
</>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* escrow-hold-reminder — all preview scenarios stacked.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
|
|
7
|
+
import { EscrowHoldReminder } from '../templates/escrow-hold-reminder';
|
|
8
|
+
import { LEARNCARD_BRANDING, VETPASS_BRANDING, ScenarioDivider } from './_fixtures';
|
|
9
|
+
|
|
10
|
+
export default function EscrowHoldReminderScenarios() {
|
|
11
|
+
return (
|
|
12
|
+
<>
|
|
13
|
+
<ScenarioDivider label="Default — LearnCard branding" />
|
|
14
|
+
<EscrowHoldReminder
|
|
15
|
+
branding={LEARNCARD_BRANDING}
|
|
16
|
+
releaseAfter="October 2, 2026, 3:00 PM"
|
|
17
|
+
cancelUrl="https://learncard.app/recovery/cancel?token=abc123"
|
|
18
|
+
/>
|
|
19
|
+
|
|
20
|
+
<ScenarioDivider label="Spanish" />
|
|
21
|
+
<EscrowHoldReminder
|
|
22
|
+
branding={LEARNCARD_BRANDING}
|
|
23
|
+
releaseAfter="2 de octubre de 2026, 3:00 p.m."
|
|
24
|
+
cancelUrl="https://learncard.app/recovery/cancel?token=abc123"
|
|
25
|
+
locale="es"
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
<ScenarioDivider label="French" />
|
|
29
|
+
<EscrowHoldReminder
|
|
30
|
+
branding={LEARNCARD_BRANDING}
|
|
31
|
+
releaseAfter="2 octobre 2026, 15h00"
|
|
32
|
+
cancelUrl="https://learncard.app/recovery/cancel?token=abc123"
|
|
33
|
+
locale="fr"
|
|
34
|
+
/>
|
|
35
|
+
|
|
36
|
+
<ScenarioDivider label="Arabic (RTL)" />
|
|
37
|
+
<EscrowHoldReminder
|
|
38
|
+
branding={LEARNCARD_BRANDING}
|
|
39
|
+
releaseAfter="2 أكتوبر 2026، 3:00 م"
|
|
40
|
+
cancelUrl="https://learncard.app/recovery/cancel?token=abc123"
|
|
41
|
+
locale="ar"
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
<ScenarioDivider label="VetPass tenant branding" />
|
|
45
|
+
<EscrowHoldReminder
|
|
46
|
+
branding={VETPASS_BRANDING}
|
|
47
|
+
releaseAfter="October 2, 2026, 3:00 PM"
|
|
48
|
+
cancelUrl="https://vetpass.app/recovery/cancel?token=abc123"
|
|
49
|
+
/>
|
|
50
|
+
</>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* escrow-hold-started — all preview scenarios stacked.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
|
|
7
|
+
import { EscrowHoldStarted } from '../templates/escrow-hold-started';
|
|
8
|
+
import { LEARNCARD_BRANDING, VETPASS_BRANDING, ScenarioDivider } from './_fixtures';
|
|
9
|
+
|
|
10
|
+
export default function EscrowHoldStartedScenarios() {
|
|
11
|
+
return (
|
|
12
|
+
<>
|
|
13
|
+
<ScenarioDivider label="Default — with device hint" />
|
|
14
|
+
<EscrowHoldStarted
|
|
15
|
+
branding={LEARNCARD_BRANDING}
|
|
16
|
+
requestedAt="September 25, 2026, 3:00 PM"
|
|
17
|
+
releaseAfter="October 2, 2026, 3:00 PM"
|
|
18
|
+
cancelUrl="https://learncard.app/recovery/cancel?token=abc123"
|
|
19
|
+
deviceHint="Chrome on Windows"
|
|
20
|
+
/>
|
|
21
|
+
|
|
22
|
+
<ScenarioDivider label="No device hint" />
|
|
23
|
+
<EscrowHoldStarted
|
|
24
|
+
branding={LEARNCARD_BRANDING}
|
|
25
|
+
requestedAt="September 25, 2026, 3:00 PM"
|
|
26
|
+
releaseAfter="October 2, 2026, 3:00 PM"
|
|
27
|
+
cancelUrl="https://learncard.app/recovery/cancel?token=abc123"
|
|
28
|
+
/>
|
|
29
|
+
|
|
30
|
+
<ScenarioDivider label="Spanish" />
|
|
31
|
+
<EscrowHoldStarted
|
|
32
|
+
branding={LEARNCARD_BRANDING}
|
|
33
|
+
requestedAt="25 de septiembre de 2026, 3:00 p.m."
|
|
34
|
+
releaseAfter="2 de octubre de 2026, 3:00 p.m."
|
|
35
|
+
cancelUrl="https://learncard.app/recovery/cancel?token=abc123"
|
|
36
|
+
locale="es"
|
|
37
|
+
/>
|
|
38
|
+
|
|
39
|
+
<ScenarioDivider label="French" />
|
|
40
|
+
<EscrowHoldStarted
|
|
41
|
+
branding={LEARNCARD_BRANDING}
|
|
42
|
+
requestedAt="25 septembre 2026, 15h00"
|
|
43
|
+
releaseAfter="2 octobre 2026, 15h00"
|
|
44
|
+
cancelUrl="https://learncard.app/recovery/cancel?token=abc123"
|
|
45
|
+
locale="fr"
|
|
46
|
+
/>
|
|
47
|
+
|
|
48
|
+
<ScenarioDivider label="Arabic (RTL)" />
|
|
49
|
+
<EscrowHoldStarted
|
|
50
|
+
branding={LEARNCARD_BRANDING}
|
|
51
|
+
requestedAt="25 سبتمبر 2026، 3:00 م"
|
|
52
|
+
releaseAfter="2 أكتوبر 2026، 3:00 م"
|
|
53
|
+
cancelUrl="https://learncard.app/recovery/cancel?token=abc123"
|
|
54
|
+
locale="ar"
|
|
55
|
+
/>
|
|
56
|
+
|
|
57
|
+
<ScenarioDivider label="VetPass tenant branding" />
|
|
58
|
+
<EscrowHoldStarted
|
|
59
|
+
branding={VETPASS_BRANDING}
|
|
60
|
+
requestedAt="September 25, 2026, 3:00 PM"
|
|
61
|
+
releaseAfter="October 2, 2026, 3:00 PM"
|
|
62
|
+
cancelUrl="https://vetpass.app/recovery/cancel?token=abc123"
|
|
63
|
+
deviceHint="Safari on iPhone"
|
|
64
|
+
/>
|
|
65
|
+
</>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* escrow-pin-locked — all preview scenarios stacked.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
|
|
7
|
+
import { EscrowPinLocked } from '../templates/escrow-pin-locked';
|
|
8
|
+
import { LEARNCARD_BRANDING, VETPASS_BRANDING, ScenarioDivider } from './_fixtures';
|
|
9
|
+
|
|
10
|
+
export default function EscrowPinLockedScenarios() {
|
|
11
|
+
return (
|
|
12
|
+
<>
|
|
13
|
+
<ScenarioDivider label="With pending waiting-period recovery (cancel link)" />
|
|
14
|
+
<EscrowPinLocked
|
|
15
|
+
branding={LEARNCARD_BRANDING}
|
|
16
|
+
lockedAt="September 25, 2026, 3:00 PM"
|
|
17
|
+
releaseAfter="October 2, 2026, 3:00 PM"
|
|
18
|
+
cancelUrl="https://learncard.app/recovery/cancel?token=abc123"
|
|
19
|
+
/>
|
|
20
|
+
|
|
21
|
+
<ScenarioDivider label="No pending recovery" />
|
|
22
|
+
<EscrowPinLocked branding={LEARNCARD_BRANDING} lockedAt="September 25, 2026, 3:00 PM" />
|
|
23
|
+
|
|
24
|
+
<ScenarioDivider label="Spanish" />
|
|
25
|
+
<EscrowPinLocked
|
|
26
|
+
branding={LEARNCARD_BRANDING}
|
|
27
|
+
lockedAt="25 de septiembre de 2026, 3:00 p.m."
|
|
28
|
+
releaseAfter="2 de octubre de 2026, 3:00 p.m."
|
|
29
|
+
cancelUrl="https://learncard.app/recovery/cancel?token=abc123"
|
|
30
|
+
locale="es"
|
|
31
|
+
/>
|
|
32
|
+
|
|
33
|
+
<ScenarioDivider label="French" />
|
|
34
|
+
<EscrowPinLocked
|
|
35
|
+
branding={LEARNCARD_BRANDING}
|
|
36
|
+
lockedAt="25 septembre 2026, 15h00"
|
|
37
|
+
releaseAfter="2 octobre 2026, 15h00"
|
|
38
|
+
cancelUrl="https://learncard.app/recovery/cancel?token=abc123"
|
|
39
|
+
locale="fr"
|
|
40
|
+
/>
|
|
41
|
+
|
|
42
|
+
<ScenarioDivider label="Arabic (RTL)" />
|
|
43
|
+
<EscrowPinLocked
|
|
44
|
+
branding={LEARNCARD_BRANDING}
|
|
45
|
+
lockedAt="25 سبتمبر 2026، 3:00 م"
|
|
46
|
+
releaseAfter="2 أكتوبر 2026، 3:00 م"
|
|
47
|
+
cancelUrl="https://learncard.app/recovery/cancel?token=abc123"
|
|
48
|
+
locale="ar"
|
|
49
|
+
/>
|
|
50
|
+
|
|
51
|
+
<ScenarioDivider label="VetPass tenant branding" />
|
|
52
|
+
<EscrowPinLocked
|
|
53
|
+
branding={VETPASS_BRANDING}
|
|
54
|
+
lockedAt="September 25, 2026, 3:00 PM"
|
|
55
|
+
releaseAfter="October 2, 2026, 3:00 PM"
|
|
56
|
+
cancelUrl="https://vetpass.app/recovery/cancel?token=abc123"
|
|
57
|
+
/>
|
|
58
|
+
</>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -14,18 +14,21 @@ export default function RecoveryKeyScenarios() {
|
|
|
14
14
|
<RecoveryKey
|
|
15
15
|
branding={LEARNCARD_BRANDING}
|
|
16
16
|
recoveryKey="mango-delta-fox-echo-bravo-seven-lima-niner"
|
|
17
|
+
confirmationCode="123456"
|
|
17
18
|
/>
|
|
18
19
|
|
|
19
20
|
<ScenarioDivider label="Long recovery key (wrapping test)" />
|
|
20
21
|
<RecoveryKey
|
|
21
22
|
branding={LEARNCARD_BRANDING}
|
|
22
23
|
recoveryKey="alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima"
|
|
24
|
+
confirmationCode="654321"
|
|
23
25
|
/>
|
|
24
26
|
|
|
25
27
|
<ScenarioDivider label="VetPass tenant branding" />
|
|
26
28
|
<RecoveryKey
|
|
27
29
|
branding={VETPASS_BRANDING}
|
|
28
30
|
recoveryKey="sierra-tango-uniform-victor-whiskey-xray"
|
|
31
|
+
confirmationCode="987654"
|
|
29
32
|
/>
|
|
30
33
|
</>
|
|
31
34
|
);
|
package/src/render.ts
CHANGED
|
@@ -41,6 +41,18 @@ import {
|
|
|
41
41
|
getEmailVerificationSubject,
|
|
42
42
|
CredentialUpdated,
|
|
43
43
|
getCredentialUpdatedSubject,
|
|
44
|
+
AccountSignInChanged,
|
|
45
|
+
getAccountSignInChangedSubject,
|
|
46
|
+
EscrowHoldStarted,
|
|
47
|
+
getEscrowHoldStartedSubject,
|
|
48
|
+
EscrowHoldReminder,
|
|
49
|
+
getEscrowHoldReminderSubject,
|
|
50
|
+
EscrowHoldReleased,
|
|
51
|
+
getEscrowHoldReleasedSubject,
|
|
52
|
+
EscrowHoldCancelled,
|
|
53
|
+
getEscrowHoldCancelledSubject,
|
|
54
|
+
EscrowPinLocked,
|
|
55
|
+
getEscrowPinLockedSubject,
|
|
44
56
|
} from './templates';
|
|
45
57
|
|
|
46
58
|
import type {
|
|
@@ -56,6 +68,13 @@ import type {
|
|
|
56
68
|
GuardianRejectedCredentialProps,
|
|
57
69
|
EmailVerificationProps,
|
|
58
70
|
CredentialUpdatedProps,
|
|
71
|
+
AccountSignInChangedProps,
|
|
72
|
+
EscrowHoldStartedProps,
|
|
73
|
+
EscrowHoldReminderProps,
|
|
74
|
+
EscrowHoldReleasedProps,
|
|
75
|
+
EscrowHoldCancelledProps,
|
|
76
|
+
EscrowHoldCancelledReason,
|
|
77
|
+
EscrowPinLockedProps,
|
|
59
78
|
} from './templates';
|
|
60
79
|
|
|
61
80
|
// ---------------------------------------------------------------------------
|
|
@@ -152,6 +171,25 @@ export interface TemplateDataMap {
|
|
|
152
171
|
|
|
153
172
|
/** brain-service: managed credential refresh update notice (LC-2198) */
|
|
154
173
|
'credential-updated': CredentialUpdatedData;
|
|
174
|
+
/** lca-api: lost-login identity rebind security notification */
|
|
175
|
+
'account-sign-in-changed': AccountSignInChangedData;
|
|
176
|
+
|
|
177
|
+
// -- Recovery hold lifecycle (nitro-escrow-enclave) ----------------------
|
|
178
|
+
|
|
179
|
+
/** lca-api: recovery hold started — includes a cancel link */
|
|
180
|
+
'escrow-hold-started': EscrowHoldStartedData;
|
|
181
|
+
|
|
182
|
+
/** lca-api: recovery hold reminder (T-24h) — includes a cancel link */
|
|
183
|
+
'escrow-hold-reminder': EscrowHoldReminderData;
|
|
184
|
+
|
|
185
|
+
/** lca-api: recovery hold completed and access was restored */
|
|
186
|
+
'escrow-hold-released': EscrowHoldReleasedData;
|
|
187
|
+
|
|
188
|
+
/** lca-api: recovery hold cancelled (user, superseded, PIN lock, or failure) */
|
|
189
|
+
'escrow-hold-cancelled': EscrowHoldCancelledData;
|
|
190
|
+
|
|
191
|
+
/** lca-api: recovery PIN locked after too many failed attempts */
|
|
192
|
+
'escrow-pin-locked': EscrowPinLockedData;
|
|
155
193
|
}
|
|
156
194
|
|
|
157
195
|
export type TemplateId = keyof TemplateDataMap;
|
|
@@ -190,6 +228,7 @@ export interface AccountApprovedData {
|
|
|
190
228
|
|
|
191
229
|
export interface RecoveryKeyData {
|
|
192
230
|
recoveryKey: string;
|
|
231
|
+
confirmationCode: string;
|
|
193
232
|
}
|
|
194
233
|
|
|
195
234
|
export interface EndorsementRequestData {
|
|
@@ -235,6 +274,36 @@ export interface CredentialUpdatedData {
|
|
|
235
274
|
credential?: { name?: string };
|
|
236
275
|
}
|
|
237
276
|
|
|
277
|
+
export type AccountSignInChangedData = Record<string, never>;
|
|
278
|
+
|
|
279
|
+
export interface EscrowHoldStartedData {
|
|
280
|
+
requestedAt: string;
|
|
281
|
+
releaseAfter: string;
|
|
282
|
+
cancelUrl: string;
|
|
283
|
+
deviceHint?: string;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export interface EscrowHoldReminderData {
|
|
287
|
+
releaseAfter: string;
|
|
288
|
+
cancelUrl: string;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export interface EscrowHoldReleasedData {
|
|
292
|
+
completedAt: string;
|
|
293
|
+
supportUrl?: string;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export interface EscrowHoldCancelledData {
|
|
297
|
+
cancelledAt: string;
|
|
298
|
+
reason?: EscrowHoldCancelledReason;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export interface EscrowPinLockedData {
|
|
302
|
+
lockedAt: string;
|
|
303
|
+
releaseAfter?: string;
|
|
304
|
+
cancelUrl?: string;
|
|
305
|
+
}
|
|
306
|
+
|
|
238
307
|
// ---------------------------------------------------------------------------
|
|
239
308
|
// renderEmail()
|
|
240
309
|
// ---------------------------------------------------------------------------
|
|
@@ -442,6 +511,65 @@ function buildElement(
|
|
|
442
511
|
};
|
|
443
512
|
}
|
|
444
513
|
|
|
514
|
+
case 'account-sign-in-changed': {
|
|
515
|
+
const props: AccountSignInChangedProps = { branding, locale };
|
|
516
|
+
|
|
517
|
+
return {
|
|
518
|
+
element: React.createElement(AccountSignInChanged, props),
|
|
519
|
+
subject: getAccountSignInChangedSubject(branding, locale),
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
case 'escrow-hold-started': {
|
|
524
|
+
const d = data as EscrowHoldStartedData;
|
|
525
|
+
const props: EscrowHoldStartedProps = { branding, ...d, locale };
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
element: React.createElement(EscrowHoldStarted, props),
|
|
529
|
+
subject: getEscrowHoldStartedSubject(branding, locale),
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
case 'escrow-hold-reminder': {
|
|
534
|
+
const d = data as EscrowHoldReminderData;
|
|
535
|
+
const props: EscrowHoldReminderProps = { branding, ...d, locale };
|
|
536
|
+
|
|
537
|
+
return {
|
|
538
|
+
element: React.createElement(EscrowHoldReminder, props),
|
|
539
|
+
subject: getEscrowHoldReminderSubject(branding, locale),
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
case 'escrow-hold-released': {
|
|
544
|
+
const d = data as EscrowHoldReleasedData;
|
|
545
|
+
const props: EscrowHoldReleasedProps = { branding, ...d, locale };
|
|
546
|
+
|
|
547
|
+
return {
|
|
548
|
+
element: React.createElement(EscrowHoldReleased, props),
|
|
549
|
+
subject: getEscrowHoldReleasedSubject(branding, locale),
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
case 'escrow-hold-cancelled': {
|
|
554
|
+
const d = data as EscrowHoldCancelledData;
|
|
555
|
+
const props: EscrowHoldCancelledProps = { branding, ...d, locale };
|
|
556
|
+
|
|
557
|
+
return {
|
|
558
|
+
element: React.createElement(EscrowHoldCancelled, props),
|
|
559
|
+
subject: getEscrowHoldCancelledSubject(branding, locale),
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
case 'escrow-pin-locked': {
|
|
564
|
+
const d = data as EscrowPinLockedData;
|
|
565
|
+
const props: EscrowPinLockedProps = { branding, ...d, locale };
|
|
566
|
+
|
|
567
|
+
return {
|
|
568
|
+
element: React.createElement(EscrowPinLocked, props),
|
|
569
|
+
subject: getEscrowPinLockedSubject(branding, locale),
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
|
|
445
573
|
default: {
|
|
446
574
|
const _exhaustive: never = templateId;
|
|
447
575
|
throw new Error(`Unknown template: ${_exhaustive}`);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Text } from '@react-email/components';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { TenantBranding } from '../branding';
|
|
5
|
+
import { DEFAULT_BRANDING } from '../branding';
|
|
6
|
+
import { Layout } from '../components/Layout';
|
|
7
|
+
import type { NotificationLocale } from '../i18n';
|
|
8
|
+
import { interpolate, resolveCatalogLocale, SHARED } from '../i18n';
|
|
9
|
+
|
|
10
|
+
export interface AccountSignInChangedProps {
|
|
11
|
+
branding: TenantBranding;
|
|
12
|
+
locale?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const STRINGS: Record<
|
|
16
|
+
NotificationLocale,
|
|
17
|
+
{
|
|
18
|
+
preview: string;
|
|
19
|
+
heading: string;
|
|
20
|
+
body: string;
|
|
21
|
+
warning: string;
|
|
22
|
+
subject: string;
|
|
23
|
+
sincerely: string;
|
|
24
|
+
}
|
|
25
|
+
> = {
|
|
26
|
+
en: {
|
|
27
|
+
preview: 'Your account sign-in was changed',
|
|
28
|
+
heading: 'Your Sign-In Was Changed',
|
|
29
|
+
body: 'Your {brandName} account was restored and connected to a new sign-in.',
|
|
30
|
+
warning: 'If this was not you, contact support immediately.',
|
|
31
|
+
subject: 'Your account sign-in was changed',
|
|
32
|
+
sincerely: 'Sincerely,',
|
|
33
|
+
},
|
|
34
|
+
es: {
|
|
35
|
+
preview: 'Se cambió el inicio de sesión de tu cuenta',
|
|
36
|
+
heading: 'Se cambió tu inicio de sesión',
|
|
37
|
+
body: 'Tu cuenta de {brandName} se restauró y se conectó a un nuevo inicio de sesión.',
|
|
38
|
+
warning: 'Si no fuiste tú, comunícate con soporte de inmediato.',
|
|
39
|
+
subject: 'Se cambió el inicio de sesión de tu cuenta',
|
|
40
|
+
sincerely: 'Atentamente,',
|
|
41
|
+
},
|
|
42
|
+
fr: {
|
|
43
|
+
preview: 'La connexion à votre compte a été modifiée',
|
|
44
|
+
heading: 'Votre connexion a été modifiée',
|
|
45
|
+
body: 'Votre compte {brandName} a été restauré et associé à une nouvelle connexion.',
|
|
46
|
+
warning:
|
|
47
|
+
'Si vous n’êtes pas à l’origine de cette action, contactez immédiatement l’assistance.',
|
|
48
|
+
subject: 'La connexion à votre compte a été modifiée',
|
|
49
|
+
sincerely: 'Cordialement,',
|
|
50
|
+
},
|
|
51
|
+
ar: {
|
|
52
|
+
preview: 'تم تغيير تسجيل الدخول إلى حسابك',
|
|
53
|
+
heading: 'تم تغيير تسجيل الدخول',
|
|
54
|
+
body: 'تمت استعادة حسابك في {brandName} وربطه بتسجيل دخول جديد.',
|
|
55
|
+
warning: 'إذا لم تكن أنت من أجرى هذا التغيير، فتواصل مع الدعم فورًا.',
|
|
56
|
+
subject: 'تم تغيير تسجيل الدخول إلى حسابك',
|
|
57
|
+
sincerely: 'مع التحيات،',
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const AccountSignInChanged: React.FC<AccountSignInChangedProps> = ({ branding, locale }) => {
|
|
62
|
+
const strings = STRINGS[resolveCatalogLocale(locale)];
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<Layout branding={branding} locale={locale} preview={strings.preview}>
|
|
66
|
+
<Text style={heading}>{strings.heading}</Text>
|
|
67
|
+
<Text style={paragraph}>
|
|
68
|
+
{interpolate(strings.body, { brandName: branding.brandName })}
|
|
69
|
+
</Text>
|
|
70
|
+
<Text style={warning}>{strings.warning}</Text>
|
|
71
|
+
<Text style={signOff}>
|
|
72
|
+
{strings.sincerely}
|
|
73
|
+
<br />
|
|
74
|
+
{interpolate(SHARED[resolveCatalogLocale(locale)].teamSignature, {
|
|
75
|
+
brandName: branding.brandName,
|
|
76
|
+
})}
|
|
77
|
+
</Text>
|
|
78
|
+
</Layout>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** Return the localized subject for an account sign-in change notification. */
|
|
83
|
+
export const getAccountSignInChangedSubject = (
|
|
84
|
+
_branding: TenantBranding,
|
|
85
|
+
locale?: string
|
|
86
|
+
): string => STRINGS[resolveCatalogLocale(locale)].subject;
|
|
87
|
+
|
|
88
|
+
const heading: React.CSSProperties = {
|
|
89
|
+
color: '#111827',
|
|
90
|
+
fontSize: '24px',
|
|
91
|
+
fontWeight: 600,
|
|
92
|
+
lineHeight: '32px',
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const paragraph: React.CSSProperties = {
|
|
96
|
+
color: '#374151',
|
|
97
|
+
fontSize: '16px',
|
|
98
|
+
lineHeight: '24px',
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const warning: React.CSSProperties = {
|
|
102
|
+
...paragraph,
|
|
103
|
+
fontWeight: 600,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const signOff: React.CSSProperties = {
|
|
107
|
+
...paragraph,
|
|
108
|
+
marginTop: '24px',
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export default function Preview(): React.ReactElement {
|
|
112
|
+
return <AccountSignInChanged branding={DEFAULT_BRANDING} />;
|
|
113
|
+
}
|