@endora-commerce/mod-transactional-emails 0.100.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.
Files changed (151) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +63 -0
  3. package/dist/admin/api/transactional-emails-client.d.ts +43 -0
  4. package/dist/admin/api/transactional-emails-client.d.ts.map +1 -0
  5. package/dist/admin/api/transactional-emails-client.js +47 -0
  6. package/dist/admin/api/transactional-emails-client.js.map +1 -0
  7. package/dist/admin/components/BrandingPanel.d.ts +10 -0
  8. package/dist/admin/components/BrandingPanel.d.ts.map +1 -0
  9. package/dist/admin/components/BrandingPanel.js +58 -0
  10. package/dist/admin/components/BrandingPanel.js.map +1 -0
  11. package/dist/admin/components/EmailEditorPane.d.ts +7 -0
  12. package/dist/admin/components/EmailEditorPane.d.ts.map +1 -0
  13. package/dist/admin/components/EmailEditorPane.js +48 -0
  14. package/dist/admin/components/EmailEditorPane.js.map +1 -0
  15. package/dist/admin/index.d.ts +46 -0
  16. package/dist/admin/index.d.ts.map +1 -0
  17. package/dist/admin/index.js +92 -0
  18. package/dist/admin/index.js.map +1 -0
  19. package/dist/admin/pages/EmailBlockEditorPage.d.ts +16 -0
  20. package/dist/admin/pages/EmailBlockEditorPage.d.ts.map +1 -0
  21. package/dist/admin/pages/EmailBlockEditorPage.js +19 -0
  22. package/dist/admin/pages/EmailBlockEditorPage.js.map +1 -0
  23. package/dist/admin/pages/EmailBlocksPage.d.ts +8 -0
  24. package/dist/admin/pages/EmailBlocksPage.d.ts.map +1 -0
  25. package/dist/admin/pages/EmailBlocksPage.js +55 -0
  26. package/dist/admin/pages/EmailBlocksPage.js.map +1 -0
  27. package/dist/admin/pages/EmailEditor.d.ts +8 -0
  28. package/dist/admin/pages/EmailEditor.d.ts.map +1 -0
  29. package/dist/admin/pages/EmailEditor.js +126 -0
  30. package/dist/admin/pages/EmailEditor.js.map +1 -0
  31. package/dist/admin/pages/EmailFragmentEditor.d.ts +9 -0
  32. package/dist/admin/pages/EmailFragmentEditor.d.ts.map +1 -0
  33. package/dist/admin/pages/EmailFragmentEditor.js +93 -0
  34. package/dist/admin/pages/EmailFragmentEditor.js.map +1 -0
  35. package/dist/admin/pages/EmailTemplateEditorPage.d.ts +12 -0
  36. package/dist/admin/pages/EmailTemplateEditorPage.d.ts.map +1 -0
  37. package/dist/admin/pages/EmailTemplateEditorPage.js +15 -0
  38. package/dist/admin/pages/EmailTemplateEditorPage.js.map +1 -0
  39. package/dist/admin/pages/EmailTemplatesPage.d.ts +8 -0
  40. package/dist/admin/pages/EmailTemplatesPage.d.ts.map +1 -0
  41. package/dist/admin/pages/EmailTemplatesPage.js +55 -0
  42. package/dist/admin/pages/EmailTemplatesPage.js.map +1 -0
  43. package/dist/admin/pages/EmailsList.d.ts +8 -0
  44. package/dist/admin/pages/EmailsList.d.ts.map +1 -0
  45. package/dist/admin/pages/EmailsList.js +85 -0
  46. package/dist/admin/pages/EmailsList.js.map +1 -0
  47. package/dist/backend/commands/email-activation.commands.d.ts +42 -0
  48. package/dist/backend/commands/email-activation.commands.d.ts.map +1 -0
  49. package/dist/backend/commands/email-activation.commands.js +51 -0
  50. package/dist/backend/commands/email-activation.commands.js.map +1 -0
  51. package/dist/backend/entities/email-block-sales-channel.entity.d.ts +11 -0
  52. package/dist/backend/entities/email-block-sales-channel.entity.d.ts.map +1 -0
  53. package/dist/backend/entities/email-block-sales-channel.entity.js +39 -0
  54. package/dist/backend/entities/email-block-sales-channel.entity.js.map +1 -0
  55. package/dist/backend/entities/email-block.entity.d.ts +24 -0
  56. package/dist/backend/entities/email-block.entity.d.ts.map +1 -0
  57. package/dist/backend/entities/email-block.entity.js +85 -0
  58. package/dist/backend/entities/email-block.entity.js.map +1 -0
  59. package/dist/backend/entities/email-template-sales-channel.entity.d.ts +10 -0
  60. package/dist/backend/entities/email-template-sales-channel.entity.d.ts.map +1 -0
  61. package/dist/backend/entities/email-template-sales-channel.entity.js +38 -0
  62. package/dist/backend/entities/email-template-sales-channel.entity.js.map +1 -0
  63. package/dist/backend/entities/email-template.entity.d.ts +19 -0
  64. package/dist/backend/entities/email-template.entity.d.ts.map +1 -0
  65. package/dist/backend/entities/email-template.entity.js +76 -0
  66. package/dist/backend/entities/email-template.entity.js.map +1 -0
  67. package/dist/backend/entities/transactional-email-content.entity.d.ts +25 -0
  68. package/dist/backend/entities/transactional-email-content.entity.d.ts.map +1 -0
  69. package/dist/backend/entities/transactional-email-content.entity.js +77 -0
  70. package/dist/backend/entities/transactional-email-content.entity.js.map +1 -0
  71. package/dist/backend/entities/transactional-email.entity.d.ts +32 -0
  72. package/dist/backend/entities/transactional-email.entity.d.ts.map +1 -0
  73. package/dist/backend/entities/transactional-email.entity.js +100 -0
  74. package/dist/backend/entities/transactional-email.entity.js.map +1 -0
  75. package/dist/backend/index.d.ts +105 -0
  76. package/dist/backend/index.d.ts.map +1 -0
  77. package/dist/backend/index.js +126 -0
  78. package/dist/backend/index.js.map +1 -0
  79. package/dist/backend/plugin.d.ts +49 -0
  80. package/dist/backend/plugin.d.ts.map +1 -0
  81. package/dist/backend/plugin.js +54 -0
  82. package/dist/backend/plugin.js.map +1 -0
  83. package/dist/backend/routes.admin.d.ts +23 -0
  84. package/dist/backend/routes.admin.d.ts.map +1 -0
  85. package/dist/backend/routes.admin.js +133 -0
  86. package/dist/backend/routes.admin.js.map +1 -0
  87. package/dist/backend/services/branding.service.d.ts +63 -0
  88. package/dist/backend/services/branding.service.d.ts.map +1 -0
  89. package/dist/backend/services/branding.service.js +119 -0
  90. package/dist/backend/services/branding.service.js.map +1 -0
  91. package/dist/backend/services/content-resolver.d.ts +28 -0
  92. package/dist/backend/services/content-resolver.d.ts.map +1 -0
  93. package/dist/backend/services/content-resolver.js +64 -0
  94. package/dist/backend/services/content-resolver.js.map +1 -0
  95. package/dist/backend/services/email-block.service.d.ts +25 -0
  96. package/dist/backend/services/email-block.service.d.ts.map +1 -0
  97. package/dist/backend/services/email-block.service.js +162 -0
  98. package/dist/backend/services/email-block.service.js.map +1 -0
  99. package/dist/backend/services/email-builder-registry.d.ts +9 -0
  100. package/dist/backend/services/email-builder-registry.d.ts.map +1 -0
  101. package/dist/backend/services/email-builder-registry.js +14 -0
  102. package/dist/backend/services/email-builder-registry.js.map +1 -0
  103. package/dist/backend/services/email-defaults-registry.d.ts +63 -0
  104. package/dist/backend/services/email-defaults-registry.d.ts.map +1 -0
  105. package/dist/backend/services/email-defaults-registry.js +60 -0
  106. package/dist/backend/services/email-defaults-registry.js.map +1 -0
  107. package/dist/backend/services/email-template.service.d.ts +23 -0
  108. package/dist/backend/services/email-template.service.d.ts.map +1 -0
  109. package/dist/backend/services/email-template.service.js +155 -0
  110. package/dist/backend/services/email-template.service.js.map +1 -0
  111. package/dist/backend/services/embed-resolver.d.ts +21 -0
  112. package/dist/backend/services/embed-resolver.d.ts.map +1 -0
  113. package/dist/backend/services/embed-resolver.js +68 -0
  114. package/dist/backend/services/embed-resolver.js.map +1 -0
  115. package/dist/backend/services/manifest-reconciler.d.ts +28 -0
  116. package/dist/backend/services/manifest-reconciler.d.ts.map +1 -0
  117. package/dist/backend/services/manifest-reconciler.js +98 -0
  118. package/dist/backend/services/manifest-reconciler.js.map +1 -0
  119. package/dist/backend/services/template-email.d.ts +26 -0
  120. package/dist/backend/services/template-email.d.ts.map +1 -0
  121. package/dist/backend/services/template-email.js +69 -0
  122. package/dist/backend/services/template-email.js.map +1 -0
  123. package/dist/backend/services/transactional-email.service.d.ts +138 -0
  124. package/dist/backend/services/transactional-email.service.d.ts.map +1 -0
  125. package/dist/backend/services/transactional-email.service.js +410 -0
  126. package/dist/backend/services/transactional-email.service.js.map +1 -0
  127. package/dist/manifest.d.ts +195 -0
  128. package/dist/manifest.d.ts.map +1 -0
  129. package/dist/manifest.js +367 -0
  130. package/dist/manifest.js.map +1 -0
  131. package/dist/migrations/20260629T113442_transactional_emails_init.d.ts +6 -0
  132. package/dist/migrations/20260629T113442_transactional_emails_init.d.ts.map +1 -0
  133. package/dist/migrations/20260629T113442_transactional_emails_init.js +133 -0
  134. package/dist/migrations/20260629T113442_transactional_emails_init.js.map +1 -0
  135. package/dist/migrations/20260801T111001_transactional_emails_email_defaults_reseed.d.ts +6 -0
  136. package/dist/migrations/20260801T111001_transactional_emails_email_defaults_reseed.d.ts.map +1 -0
  137. package/dist/migrations/20260801T111001_transactional_emails_email_defaults_reseed.js +49 -0
  138. package/dist/migrations/20260801T111001_transactional_emails_email_defaults_reseed.js.map +1 -0
  139. package/dist/migrations/20260903T101748_transactional_emails_namespace_block_names.d.ts +6 -0
  140. package/dist/migrations/20260903T101748_transactional_emails_namespace_block_names.d.ts.map +1 -0
  141. package/dist/migrations/20260903T101748_transactional_emails_namespace_block_names.js +60 -0
  142. package/dist/migrations/20260903T101748_transactional_emails_namespace_block_names.js.map +1 -0
  143. package/dist/migrations/index.d.ts +29 -0
  144. package/dist/migrations/index.d.ts.map +1 -0
  145. package/dist/migrations/index.js +33 -0
  146. package/dist/migrations/index.js.map +1 -0
  147. package/docs/transactional-emails.md +176 -0
  148. package/i18n/en.json +92 -0
  149. package/i18n/pl.json +92 -0
  150. package/package.json +113 -0
  151. package/tailwind.css +14 -0
@@ -0,0 +1,8 @@
1
+ export declare function EmailEditor(): React.ReactElement;
2
+ /**
3
+ * The registry loads a route component through a dynamic-import factory and
4
+ * reads its default export (feature 091, FR-013). The named export stays: it is
5
+ * the spelling this module's own code and its tests use.
6
+ */
7
+ export default EmailEditor;
8
+ //# sourceMappingURL=EmailEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailEditor.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/EmailEditor.tsx"],"names":[],"mappings":"AAqCA,wBAAgB,WAAW,IAAI,KAAK,CAAC,YAAY,CA4MhD;AAED;;;;GAIG;AACH,eAAe,WAAW,CAAC"}
@@ -0,0 +1,126 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { useParams, useNavigate } from 'react-router-dom';
4
+ import { apiClient, useAuth } from '@endora-commerce/admin-kit/lib';
5
+ import { Alert, AlertDescription, Button, Card, CardContent, CardHeader, CardTitle, Label, PageHeader, SaveButtonGroup, Select } from '@endora-commerce/admin-kit/ui';
6
+ import { transactionalEmailsClient } from '../api/transactional-emails-client.js';
7
+ import { EmailSubjectWithVariables, EmailVariablesProvider, mergeEmailVariables, saveCanvasAsEmailTemplate, listEmailTemplatesForApply, loadEmailTemplateCanvas, } from '@endora-commerce/page-builder-admin/email';
8
+ import { EmailEditorPane } from '../components/EmailEditorPane.js';
9
+ /**
10
+ * List sales channels (feature 091, P6).
11
+ *
12
+ * The request is built here rather than through `sales_channels`' own admin
13
+ * API client: that client is another module's **code**, which is what the
14
+ * cross-module ledger recorded, while `/api/v1/admin/sales-channels` and
15
+ * `SalesChannelListResponse` are an HTTP path and a
16
+ * `@endora-commerce/contracts` type that both sides already compile. That is
17
+ * the exit P2 established and `admin-kit-surface.md` R6 records — one `GET`
18
+ * out of that client's ten methods, and no dependency on the owner's code.
19
+ */
20
+ function listActiveSalesChannels() {
21
+ return apiClient.get('/api/v1/admin/sales-channels?activeOnly=true');
22
+ }
23
+ const emptyData = { root: { props: {} }, content: [] };
24
+ export function EmailEditor() {
25
+ const { code = '' } = useParams();
26
+ const navigate = useNavigate();
27
+ const { hasPermission } = useAuth();
28
+ const canWrite = hasPermission('transactional_emails:write');
29
+ const [channels, setChannels] = useState([]);
30
+ const [channelId, setChannelId] = useState(null);
31
+ const [language, setLanguage] = useState('');
32
+ const [detail, setDetail] = useState(null);
33
+ const [subject, setSubject] = useState('');
34
+ const [content, setContent] = useState(emptyData);
35
+ const [error, setError] = useState(null);
36
+ const [notice, setNotice] = useState(null);
37
+ const [busy, setBusy] = useState(false);
38
+ useEffect(() => {
39
+ let live = true;
40
+ void listActiveSalesChannels().then((res) => {
41
+ if (live)
42
+ setChannels(res.items.map((c) => ({ id: c.id, code: c.code })));
43
+ });
44
+ return () => {
45
+ live = false;
46
+ };
47
+ }, []);
48
+ const load = useCallback(async (lang) => {
49
+ setError(null);
50
+ try {
51
+ const d = await transactionalEmailsClient.get(code, channelId, lang || undefined);
52
+ setDetail(d);
53
+ setLanguage(d.scope.language);
54
+ setSubject(d.effective.subject);
55
+ setContent(d.effective.content);
56
+ }
57
+ catch (err) {
58
+ setError(err instanceof Error ? err.message : String(err));
59
+ }
60
+ }, [code, channelId]);
61
+ useEffect(() => {
62
+ void load(language || undefined);
63
+ // eslint-disable-next-line react-hooks/exhaustive-deps
64
+ }, [code, channelId]);
65
+ const save = async () => {
66
+ setBusy(true);
67
+ setError(null);
68
+ setNotice(null);
69
+ try {
70
+ await transactionalEmailsClient.saveContent(code, channelId, language, { subject, content });
71
+ setNotice('Saved.');
72
+ await load(language);
73
+ return true;
74
+ }
75
+ catch (err) {
76
+ setError(err instanceof Error ? err.message : String(err));
77
+ return false;
78
+ }
79
+ finally {
80
+ setBusy(false);
81
+ }
82
+ };
83
+ const saveAndExit = async () => {
84
+ if (await save())
85
+ navigate('/transactional-emails');
86
+ };
87
+ const reset = async () => {
88
+ if (!window.confirm('Reset this email to the module default? Your customization for this scope will be removed.'))
89
+ return;
90
+ setBusy(true);
91
+ setError(null);
92
+ try {
93
+ await transactionalEmailsClient.resetContent(code, channelId, language);
94
+ setNotice('Reset to default.');
95
+ await load(language);
96
+ }
97
+ catch (err) {
98
+ setError(err instanceof Error ? err.message : String(err));
99
+ }
100
+ finally {
101
+ setBusy(false);
102
+ }
103
+ };
104
+ if (!hasPermission('transactional_emails:read')) {
105
+ return (_jsx(Alert, { children: _jsx(AlertDescription, { children: "You do not have permission to view transactional emails." }) }));
106
+ }
107
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(PageHeader, { title: detail?.name ?? code, description: `Source: ${detail?.effective.source ?? '—'}`, actions: _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Button, { variant: "outline", onClick: () => void reset(), disabled: !canWrite || busy, children: "Reset to default" }), _jsx(SaveButtonGroup, { onSave: () => void save(), onSaveAndExit: () => void saveAndExit(), saving: busy, disabled: !canWrite, saveLabel: "Save", savingLabel: "Save", saveAndExitLabel: "Save and exit" })] }) }), error ? (_jsx(Alert, { variant: "destructive", children: _jsx(AlertDescription, { children: error }) })) : null, notice ? (_jsx(Alert, { children: _jsx(AlertDescription, { children: notice }) })) : null, _jsx(Card, { children: _jsxs(CardContent, { className: "flex flex-wrap items-end gap-4 pt-6", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "te-scope", children: "Scope" }), _jsxs(Select, { id: "te-scope", value: channelId ?? '', onChange: (e) => setChannelId(e.target.value === '' ? null : e.target.value), children: [_jsx("option", { value: "", children: "All channels (default)" }), channels.map((c) => (_jsx("option", { value: c.id, children: c.code }, c.id)))] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "te-lang", children: "Language" }), _jsx(Select, { id: "te-lang", value: language, onChange: (e) => {
108
+ setLanguage(e.target.value);
109
+ void load(e.target.value);
110
+ }, children: (detail?.languages ?? []).map((l) => (_jsx("option", { value: l, children: l }, l))) })] })] }) }), _jsxs(EmailVariablesProvider, { variables: mergeEmailVariables(detail?.variables), children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: "Subject" }) }), _jsx(CardContent, { children: _jsx(EmailSubjectWithVariables, { value: subject, onChange: setSubject, disabled: !canWrite }) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: "Content" }) }), _jsx(CardContent, { children: _jsx(EmailEditorPane, { editorKey: `${code}:${channelId ?? 'global'}:${language}`, data: content, onChange: setContent, salesChannelId: channelId, previewLanguage: language || null, onSaveAsTemplate: async (meta, canvasData) => {
111
+ await saveCanvasAsEmailTemplate({
112
+ ...meta,
113
+ data: canvasData,
114
+ salesChannelIds: channelId ? [channelId] : [],
115
+ languages: detail?.languages?.length ? detail.languages : [language || 'en-US'],
116
+ activeLanguage: language || null,
117
+ });
118
+ }, onListTemplatesForApply: () => listEmailTemplatesForApply(channelId), onResolveTemplateLayout: (templateId) => loadEmailTemplateCanvas(templateId, language || null) }) })] })] })] }));
119
+ }
120
+ /**
121
+ * The registry loads a route component through a dynamic-import factory and
122
+ * reads its default export (feature 091, FR-013). The named export stays: it is
123
+ * the spelling this module's own code and its tests use.
124
+ */
125
+ export default EmailEditor;
126
+ //# sourceMappingURL=EmailEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailEditor.js","sourceRoot":"","sources":["../../../src/admin/pages/EmailEditor.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface EmailFragmentEditorProps {
2
+ kind: 'block' | 'template';
3
+ }
4
+ /**
5
+ * In-admin content editor for a reusable email block or template (feature 047,
6
+ * US3). Editing a fragment's content updates every email that embeds it.
7
+ */
8
+ export declare function EmailFragmentEditor({ kind }: EmailFragmentEditorProps): React.ReactElement;
9
+ //# sourceMappingURL=EmailFragmentEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailFragmentEditor.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/EmailFragmentEditor.tsx"],"names":[],"mappings":"AAoBA,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,GAAG,UAAU,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,EAAE,wBAAwB,GAAG,KAAK,CAAC,YAAY,CAsJ1F"}
@@ -0,0 +1,93 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { useParams, useNavigate } from 'react-router-dom';
4
+ import { useAuth } from '@endora-commerce/admin-kit/lib';
5
+ import { Alert, AlertDescription, Card, CardContent, CardHeader, CardTitle, Label, PageHeader, SaveButtonGroup, Select } from '@endora-commerce/admin-kit/ui';
6
+ import { transactionalEmailsClient } from '../api/transactional-emails-client.js';
7
+ import { EmailVariablesProvider, mergeEmailVariables, saveCanvasAsEmailTemplate, listEmailTemplatesForApply, loadEmailTemplateCanvas, } from '@endora-commerce/page-builder-admin/email';
8
+ import { EmailEditorPane } from '../components/EmailEditorPane.js';
9
+ const emptyData = { root: { props: {} }, content: [] };
10
+ /**
11
+ * In-admin content editor for a reusable email block or template (feature 047,
12
+ * US3). Editing a fragment's content updates every email that embeds it.
13
+ */
14
+ export function EmailFragmentEditor({ kind }) {
15
+ const { id = '' } = useParams();
16
+ const navigate = useNavigate();
17
+ const { hasPermission } = useAuth();
18
+ const canWrite = hasPermission('transactional_emails:write');
19
+ const [detail, setDetail] = useState(null);
20
+ const [language, setLanguage] = useState('');
21
+ const [content, setContent] = useState(emptyData);
22
+ const [error, setError] = useState(null);
23
+ const [notice, setNotice] = useState(null);
24
+ const [busy, setBusy] = useState(false);
25
+ const load = useCallback(async () => {
26
+ setError(null);
27
+ try {
28
+ const d = kind === 'block'
29
+ ? await transactionalEmailsClient.getBlock(id)
30
+ : await transactionalEmailsClient.getTemplate(id);
31
+ setDetail(d);
32
+ const firstLang = language || d.languages[0] || 'en-US';
33
+ setLanguage(firstLang);
34
+ setContent(d.content[firstLang] ?? emptyData);
35
+ }
36
+ catch (err) {
37
+ setError(err instanceof Error ? err.message : String(err));
38
+ }
39
+ }, [id, kind, language]);
40
+ useEffect(() => {
41
+ void load();
42
+ // eslint-disable-next-line react-hooks/exhaustive-deps
43
+ }, [id, kind]);
44
+ const switchLanguage = (lang) => {
45
+ setLanguage(lang);
46
+ setContent(detail?.content[lang] ?? emptyData);
47
+ };
48
+ const save = async () => {
49
+ if (!detail)
50
+ return false;
51
+ setBusy(true);
52
+ setError(null);
53
+ setNotice(null);
54
+ try {
55
+ const body = { content, expectedVersion: detail.version };
56
+ const updated = kind === 'block'
57
+ ? await transactionalEmailsClient.putBlockContent(id, language, body)
58
+ : await transactionalEmailsClient.putTemplateContent(id, language, body);
59
+ setDetail(updated);
60
+ setNotice('Saved.');
61
+ return true;
62
+ }
63
+ catch (err) {
64
+ setError(err instanceof Error ? err.message : String(err));
65
+ return false;
66
+ }
67
+ finally {
68
+ setBusy(false);
69
+ }
70
+ };
71
+ const saveAndExit = async () => {
72
+ if (await save()) {
73
+ navigate(kind === 'block' ? '/transactional-emails/blocks' : '/transactional-emails/templates');
74
+ }
75
+ };
76
+ if (!hasPermission('transactional_emails:read')) {
77
+ return (_jsx(Alert, { children: _jsx(AlertDescription, { children: "You do not have permission to view this content." }) }));
78
+ }
79
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(PageHeader, { title: detail?.name ?? id, description: kind === 'block' ? 'Email block content' : 'Email template content', actions: _jsx(SaveButtonGroup, { onSave: () => void save(), onSaveAndExit: () => void saveAndExit(), saving: busy, disabled: !canWrite, saveLabel: "Save", savingLabel: "Save", saveAndExitLabel: "Save and exit" }) }), error ? (_jsx(Alert, { variant: "destructive", children: _jsx(AlertDescription, { children: error }) })) : null, notice ? (_jsx(Alert, { children: _jsx(AlertDescription, { children: notice }) })) : null, _jsx(Card, { children: _jsx(CardContent, { className: "flex items-end gap-4 pt-6", children: _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "frag-lang", children: "Language" }), _jsx(Select, { id: "frag-lang", value: language, onChange: (e) => switchLanguage(e.target.value), children: (detail?.languages ?? []).map((l) => (_jsx("option", { value: l, children: l }, l))) })] }) }) }), _jsx(EmailVariablesProvider, { variables: mergeEmailVariables([]), children: _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: "Content" }) }), _jsx(CardContent, { children: _jsx(EmailEditorPane, { editorKey: `${kind}:${id}:${language}`, data: content, onChange: setContent, ...(canWrite
80
+ ? {
81
+ onSaveAsTemplate: async (meta, canvasData) => {
82
+ await saveCanvasAsEmailTemplate({
83
+ ...meta,
84
+ data: canvasData,
85
+ salesChannelIds: [],
86
+ languages: detail?.languages?.length ? detail.languages : [language || 'en-US'],
87
+ activeLanguage: language || null,
88
+ });
89
+ },
90
+ }
91
+ : {}), onListTemplatesForApply: () => listEmailTemplatesForApply(null), onResolveTemplateLayout: (templateId) => loadEmailTemplateCanvas(templateId, language || null) }) })] }) })] }));
92
+ }
93
+ //# sourceMappingURL=EmailFragmentEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailFragmentEditor.js","sourceRoot":"","sources":["../../../src/admin/pages/EmailFragmentEditor.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG1D,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AACzD,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAC9J,OAAO,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,yBAAyB,EACzB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAEnE,MAAM,SAAS,GAAS,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AAQ7D;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAE,IAAI,EAA4B;IACpE,MAAM,EAAE,EAAE,GAAG,EAAE,EAAE,GAAG,SAAS,EAAkB,CAAC;IAChD,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,aAAa,CAAC,4BAA4B,CAAC,CAAC;IAE7D,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAC1D,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC7C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAO,SAAS,CAAC,CAAC;IACxD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAC1D,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAExC,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QAClC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,IAAI,KAAK,OAAO;gBACxB,CAAC,CAAC,MAAM,yBAAyB,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC9C,CAAC,CAAC,MAAM,yBAAyB,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;YACpD,SAAS,CAAC,CAAC,CAAC,CAAC;YACb,MAAM,SAAS,GAAG,QAAQ,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC;YACxD,WAAW,CAAC,SAAS,CAAC,CAAC;YACvB,UAAU,CAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAU,IAAI,SAAS,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEzB,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,IAAI,EAAE,CAAC;QACZ,uDAAuD;IACzD,CAAC,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;IAEf,MAAM,cAAc,GAAG,CAAC,IAAY,EAAQ,EAAE;QAC5C,WAAW,CAAC,IAAI,CAAC,CAAC;QAClB,UAAU,CAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAU,IAAI,SAAS,CAAC,CAAC;IAC3D,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,KAAK,IAAsB,EAAE;QACxC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,OAAO,CAAC,IAAI,CAAC,CAAC;QACd,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;YAC1D,MAAM,OAAO,GAAG,IAAI,KAAK,OAAO;gBAC9B,CAAC,CAAC,MAAM,yBAAyB,CAAC,eAAe,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC;gBACrE,CAAC,CAAC,MAAM,yBAAyB,CAAC,kBAAkB,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC3E,SAAS,CAAC,OAAO,CAAC,CAAC;YACnB,SAAS,CAAC,QAAQ,CAAC,CAAC;YACpB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3D,OAAO,KAAK,CAAC;QACf,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,KAAK,IAAmB,EAAE;QAC5C,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;YACjB,QAAQ,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC;QAClG,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,CAAC,aAAa,CAAC,2BAA2B,CAAC,EAAE,CAAC;QAChD,OAAO,CACL,KAAC,KAAK,cACJ,KAAC,gBAAgB,mEAAoE,GAC/E,CACT,CAAC;IACJ,CAAC;IAED,OAAO,CACL,eAAK,SAAS,EAAC,WAAW,aACxB,KAAC,UAAU,IACT,KAAK,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,EACzB,WAAW,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,wBAAwB,EAChF,OAAO,EACL,KAAC,eAAe,IACd,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE,EACzB,aAAa,EAAE,GAAG,EAAE,CAAC,KAAK,WAAW,EAAE,EACvC,MAAM,EAAE,IAAI,EACZ,QAAQ,EAAE,CAAC,QAAQ,EACnB,SAAS,EAAC,MAAM,EAChB,WAAW,EAAC,MAAM,EAClB,gBAAgB,EAAC,eAAe,GAChC,GAEJ,EACD,KAAK,CAAC,CAAC,CAAC,CACP,KAAC,KAAK,IAAC,OAAO,EAAC,aAAa,YAC1B,KAAC,gBAAgB,cAAE,KAAK,GAAoB,GACtC,CACT,CAAC,CAAC,CAAC,IAAI,EACP,MAAM,CAAC,CAAC,CAAC,CACR,KAAC,KAAK,cACJ,KAAC,gBAAgB,cAAE,MAAM,GAAoB,GACvC,CACT,CAAC,CAAC,CAAC,IAAI,EAER,KAAC,IAAI,cACH,KAAC,WAAW,IAAC,SAAS,EAAC,2BAA2B,YAChD,eAAK,SAAS,EAAC,WAAW,aACxB,KAAC,KAAK,IAAC,OAAO,EAAC,WAAW,yBAAiB,EAC3C,KAAC,MAAM,IAAC,EAAE,EAAC,WAAW,EAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,YACpF,CAAC,MAAM,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACpC,iBAAgB,KAAK,EAAE,CAAC,YACrB,CAAC,IADS,CAAC,CAEL,CACV,CAAC,GACK,IACL,GACM,GACT,EAEP,KAAC,sBAAsB,IAAC,SAAS,EAAE,mBAAmB,CAAC,EAAE,CAAC,YACxD,MAAC,IAAI,eACH,KAAC,UAAU,cACT,KAAC,SAAS,0BAAoB,GACnB,EACb,KAAC,WAAW,cACV,KAAC,eAAe,IACd,SAAS,EAAE,GAAG,IAAI,IAAI,EAAE,IAAI,QAAQ,EAAE,EACtC,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,UAAU,KAChB,CAAC,QAAQ;oCACX,CAAC,CAAC;wCACE,gBAAgB,EAAE,KAAK,EACrB,IAAoC,EACpC,UAAgB,EAChB,EAAE;4CACF,MAAM,yBAAyB,CAAC;gDAC9B,GAAG,IAAI;gDACP,IAAI,EAAE,UAAU;gDAChB,eAAe,EAAE,EAAE;gDACnB,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,OAAO,CAAC;gDAC/E,cAAc,EAAE,QAAQ,IAAI,IAAI;6CACjC,CAAC,CAAC;wCACL,CAAC;qCACF;oCACH,CAAC,CAAC,EAAE,CAAC,EACP,uBAAuB,EAAE,GAAG,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,EAC/D,uBAAuB,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,uBAAuB,CAAC,UAAU,EAAE,QAAQ,IAAI,IAAI,CAAC,GAC9F,GACU,IACT,GACgB,IACrB,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { ReactNode } from 'react';
2
+ /**
3
+ * `/transactional-emails/templates/:id` — the same fragment editor, opened on a
4
+ * **template** (feature 047 US3; feature 091, Phase 4, batch 11).
5
+ *
6
+ * The sibling of `EmailBlockEditorPage`, and separate from it for the same
7
+ * reason: the two routes differ only in the `kind` the host used to pass, and a
8
+ * contribution declaration carries no props.
9
+ */
10
+ export declare function EmailTemplateEditorPage(): ReactNode;
11
+ export default EmailTemplateEditorPage;
12
+ //# sourceMappingURL=EmailTemplateEditorPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailTemplateEditorPage.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/EmailTemplateEditorPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,IAAI,SAAS,CAEnD;AAED,eAAe,uBAAuB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { EmailFragmentEditor } from './EmailFragmentEditor.js';
3
+ /**
4
+ * `/transactional-emails/templates/:id` — the same fragment editor, opened on a
5
+ * **template** (feature 047 US3; feature 091, Phase 4, batch 11).
6
+ *
7
+ * The sibling of `EmailBlockEditorPage`, and separate from it for the same
8
+ * reason: the two routes differ only in the `kind` the host used to pass, and a
9
+ * contribution declaration carries no props.
10
+ */
11
+ export function EmailTemplateEditorPage() {
12
+ return _jsx(EmailFragmentEditor, { kind: "template" });
13
+ }
14
+ export default EmailTemplateEditorPage;
15
+ //# sourceMappingURL=EmailTemplateEditorPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailTemplateEditorPage.js","sourceRoot":"","sources":["../../../src/admin/pages/EmailTemplateEditorPage.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO,KAAC,mBAAmB,IAAC,IAAI,EAAC,UAAU,GAAG,CAAC;AACjD,CAAC;AAED,eAAe,uBAAuB,CAAC"}
@@ -0,0 +1,8 @@
1
+ export declare function EmailTemplatesPage(): React.ReactElement;
2
+ /**
3
+ * The registry loads a route component through a dynamic-import factory and
4
+ * reads its default export (feature 091, FR-013). The named export stays: it is
5
+ * the spelling this module's own code and its tests use.
6
+ */
7
+ export default EmailTemplatesPage;
8
+ //# sourceMappingURL=EmailTemplatesPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailTemplatesPage.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/EmailTemplatesPage.tsx"],"names":[],"mappings":"AAOA,wBAAgB,kBAAkB,IAAI,KAAK,CAAC,YAAY,CAqGvD;AAED;;;;GAIG;AACH,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { Link } from 'react-router-dom';
4
+ import { useAuth } from '@endora-commerce/admin-kit/lib';
5
+ import { Alert, AlertDescription, Button, Card, CardContent, Input, PageHeader } from '@endora-commerce/admin-kit/ui';
6
+ import { transactionalEmailsClient } from '../api/transactional-emails-client.js';
7
+ export function EmailTemplatesPage() {
8
+ const { hasPermission } = useAuth();
9
+ const canWrite = hasPermission('transactional_emails:write');
10
+ const [items, setItems] = useState([]);
11
+ const [error, setError] = useState(null);
12
+ const [code, setCode] = useState('');
13
+ const [name, setName] = useState('');
14
+ const reload = () => {
15
+ transactionalEmailsClient
16
+ .listTemplates()
17
+ .then((res) => setItems(res.items))
18
+ .catch((err) => setError(err instanceof Error ? err.message : String(err)));
19
+ };
20
+ useEffect(reload, []);
21
+ const create = async () => {
22
+ setError(null);
23
+ try {
24
+ await transactionalEmailsClient.createTemplate({ code, name, languages: ['en-US', 'pl-PL'] });
25
+ setCode('');
26
+ setName('');
27
+ reload();
28
+ }
29
+ catch (err) {
30
+ setError(err instanceof Error ? err.message : String(err));
31
+ }
32
+ };
33
+ const remove = async (id) => {
34
+ if (!window.confirm('Delete this template?'))
35
+ return;
36
+ try {
37
+ await transactionalEmailsClient.deleteTemplate(id);
38
+ reload();
39
+ }
40
+ catch (err) {
41
+ setError(err instanceof Error ? err.message : String(err));
42
+ }
43
+ };
44
+ if (!hasPermission('transactional_emails:read')) {
45
+ return (_jsx(Alert, { children: _jsx(AlertDescription, { children: "You do not have permission to view email templates." }) }));
46
+ }
47
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(PageHeader, { title: "Email Templates", description: "Reusable email-safe templates." }), error ? (_jsx(Alert, { variant: "destructive", children: _jsx(AlertDescription, { children: error }) })) : null, canWrite ? (_jsx(Card, { children: _jsxs(CardContent, { className: "flex flex-wrap items-end gap-3 pt-6", children: [_jsx(Input, { placeholder: "code", value: code, onChange: (e) => setCode(e.target.value), className: "max-w-[200px]" }), _jsx(Input, { placeholder: "name", value: name, onChange: (e) => setName(e.target.value), className: "max-w-[240px]" }), _jsx(Button, { onClick: () => void create(), disabled: !code || !name, children: "Create template" })] }) })) : null, _jsx(Card, { children: _jsx(CardContent, { className: "p-0", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "border-b text-left text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "p-3 font-medium", children: "Name" }), _jsx("th", { className: "p-3 font-medium", children: "Code" }), _jsx("th", { className: "p-3 font-medium", children: "Scope" }), _jsx("th", { className: "p-3" })] }) }), _jsx("tbody", { children: items.map((t) => (_jsxs("tr", { className: "border-b last:border-0", children: [_jsx("td", { className: "p-3", children: _jsx(Link, { className: "font-medium text-primary hover:underline", to: `/transactional-emails/templates/${t.id}`, children: t.name }) }), _jsx("td", { className: "p-3 font-mono text-xs", children: t.code }), _jsx("td", { className: "p-3", children: t.scope }), _jsx("td", { className: "p-3 text-right", children: canWrite && !t.isSystem ? (_jsx(Button, { variant: "outline", size: "sm", onClick: () => void remove(t.id), children: "Delete" })) : null })] }, t.id))) })] }) }) })] }));
48
+ }
49
+ /**
50
+ * The registry loads a route component through a dynamic-import factory and
51
+ * reads its default export (feature 091, FR-013). The named export stays: it is
52
+ * the spelling this module's own code and its tests use.
53
+ */
54
+ export default EmailTemplatesPage;
55
+ //# sourceMappingURL=EmailTemplatesPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailTemplatesPage.js","sourceRoot":"","sources":["../../../src/admin/pages/EmailTemplatesPage.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export declare function EmailsList(): React.ReactElement;
2
+ /**
3
+ * The registry loads a route component through a dynamic-import factory and
4
+ * reads its default export (feature 091, FR-013). The named export stays: it is
5
+ * the spelling this module's own code and its tests use.
6
+ */
7
+ export default EmailsList;
8
+ //# sourceMappingURL=EmailsList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailsList.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/EmailsList.tsx"],"names":[],"mappings":"AAqFA,wBAAgB,UAAU,IAAI,KAAK,CAAC,YAAY,CAuG/C;AAED;;;;GAIG;AACH,eAAe,UAAU,CAAC"}
@@ -0,0 +1,85 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useState } from 'react';
3
+ import { Link } from 'react-router-dom';
4
+ import { Lock, Power, PowerOff } from 'lucide-react';
5
+ import { useTranslation } from '@endora-commerce/admin-kit/i18n';
6
+ import { ApiError, normalize, useAuth } from '@endora-commerce/admin-kit/lib';
7
+ import { Alert, AlertDescription, Badge, Button, Card, CardContent, Input, PageHeader } from '@endora-commerce/admin-kit/ui';
8
+ import { transactionalEmailsClient } from '../api/transactional-emails-client.js';
9
+ /**
10
+ * Issue #89 — the per-email operator control.
11
+ *
12
+ * Modelled on `platform/ModuleActivationControl`: a protected email renders the
13
+ * control **locked with its reason** rather than absent or silently failing,
14
+ * because the answer to "why can I not switch this off" belongs on screen. The
15
+ * reason is the owning module's own sentence, carried by the API — this app
16
+ * holds no list of protected codes, exactly as the platform screen holds no
17
+ * list of non-deactivatable modules.
18
+ */
19
+ function ActivationCell({ email, t, canWrite, onChanged, onError, }) {
20
+ const [pending, setPending] = useState(false);
21
+ const toggle = useCallback(async () => {
22
+ setPending(true);
23
+ try {
24
+ onChanged(await transactionalEmailsClient.setActive(email.code, !email.active));
25
+ }
26
+ catch (err) {
27
+ onError(err instanceof ApiError ? err.envelope.error.message : String(err));
28
+ }
29
+ finally {
30
+ setPending(false);
31
+ }
32
+ }, [email.active, email.code, onChanged, onError]);
33
+ if (!email.deactivatable) {
34
+ return (_jsxs("div", { className: "flex flex-col items-start gap-1", children: [_jsxs(Button, { type: "button", variant: "outline", size: "sm", disabled: true, title: email.nonDeactivatableReason ?? undefined, children: [_jsx(Lock, { className: "mr-1 size-3.5", "aria-hidden": "true" }), t('activation.locked')] }), _jsx("p", { className: "max-w-prose text-xs text-muted-foreground", children: email.nonDeactivatableReason })] }));
35
+ }
36
+ if (!canWrite) {
37
+ return (_jsx(Badge, { variant: email.active ? 'success' : 'secondary', children: email.active ? t('activation.on') : t('activation.off') }));
38
+ }
39
+ return (_jsxs(Button, { type: "button", variant: email.active ? 'outline' : 'default', size: "sm", disabled: pending, onClick: () => void toggle(), children: [email.active ? (_jsx(PowerOff, { className: "mr-1 size-3.5", "aria-hidden": "true" })) : (_jsx(Power, { className: "mr-1 size-3.5", "aria-hidden": "true" })), email.active ? t('activation.action.disable') : t('activation.action.enable')] }));
40
+ }
41
+ export function EmailsList() {
42
+ const { hasPermission } = useAuth();
43
+ const t = useTranslation('transactional_emails');
44
+ const [items, setItems] = useState([]);
45
+ const [error, setError] = useState(null);
46
+ const [q, setQ] = useState('');
47
+ useEffect(() => {
48
+ let live = true;
49
+ transactionalEmailsClient
50
+ .list()
51
+ .then((res) => {
52
+ if (live)
53
+ setItems(res.items);
54
+ })
55
+ .catch((err) => {
56
+ if (live)
57
+ setError(err instanceof Error ? err.message : String(err));
58
+ });
59
+ return () => {
60
+ live = false;
61
+ };
62
+ }, []);
63
+ const replace = useCallback((next) => {
64
+ setError(null);
65
+ setItems((prev) => prev.map((i) => (i.code === next.code ? next : i)));
66
+ }, []);
67
+ const filtered = useMemo(() => {
68
+ const needle = normalize(q);
69
+ if (!needle)
70
+ return items;
71
+ return items.filter((i) => normalize(i.name).includes(needle) || normalize(i.code).includes(needle));
72
+ }, [items, q]);
73
+ if (!hasPermission('transactional_emails:read')) {
74
+ return (_jsx(Alert, { children: _jsx(AlertDescription, { children: "You do not have permission to view transactional emails." }) }));
75
+ }
76
+ const canWrite = hasPermission('transactional_emails:write');
77
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(PageHeader, { title: "Transactional Emails", description: "Edit the content and look of transactional emails." }), error ? (_jsx(Alert, { variant: "destructive", children: _jsx(AlertDescription, { children: error }) })) : null, _jsx(Input, { placeholder: "Search by name or code\u2026", value: q, onChange: (e) => setQ(e.target.value), className: "max-w-sm" }), _jsx(Card, { children: _jsx(CardContent, { className: "p-0", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "border-b text-left text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "p-3 font-medium", children: "Name" }), _jsx("th", { className: "p-3 font-medium", children: "Code" }), _jsx("th", { className: "p-3 font-medium", children: "Module" }), _jsx("th", { className: "p-3 font-medium", children: "Customized" }), _jsx("th", { className: "p-3 font-medium", children: t('activation.column') })] }) }), _jsxs("tbody", { children: [filtered.map((e) => (_jsxs("tr", { className: "border-b last:border-0 hover:bg-muted/40", children: [_jsx("td", { className: "p-3", children: _jsx(Link, { className: "font-medium text-primary hover:underline", to: `/transactional-emails/${encodeURIComponent(e.code)}`, children: e.name }) }), _jsx("td", { className: "p-3 font-mono text-xs", children: e.code }), _jsx("td", { className: "p-3", children: e.ownerModule }), _jsx("td", { className: "p-3", children: e.hasGlobalOverride || e.hasChannelOverride ? 'Yes' : '—' }), _jsx("td", { className: "p-3", children: _jsx(ActivationCell, { email: e, t: t, canWrite: canWrite, onChanged: replace, onError: setError }) })] }, e.code))), filtered.length === 0 ? (_jsx("tr", { children: _jsx("td", { className: "p-3 text-muted-foreground", colSpan: 5, children: "No transactional emails found." }) })) : null] })] }) }) })] }));
78
+ }
79
+ /**
80
+ * The registry loads a route component through a dynamic-import factory and
81
+ * reads its default export (feature 091, FR-013). The named export stays: it is
82
+ * the spelling this module's own code and its tests use.
83
+ */
84
+ export default EmailsList;
85
+ //# sourceMappingURL=EmailsList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailsList.js","sourceRoot":"","sources":["../../../src/admin/pages/EmailsList.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { Command } from '@endora-commerce/platform/commands';
2
+ import type { EmailDefaultsRegistry } from '../services/email-defaults-registry.js';
3
+ /**
4
+ * The per-email operator flip — issue #89, Principle XIII.
5
+ *
6
+ * `TransactionalEmail.active` has been honoured on the send path since feature
7
+ * 047 (`transactional-email.service.ts`), but nothing could write it: the only
8
+ * way to silence one email was to edit the row by hand. This is that door, and
9
+ * it is deliberately built like the module-level one in
10
+ * `_lifecycle/commands/activation.commands.ts` — refuse in the factory before a
11
+ * transaction opens, then one Command, one audit row, one transaction.
12
+ *
13
+ * Reading the two together is the point. An operator meets the same 409 and the
14
+ * same carried reason whether they aim at a module or at a single email, and a
15
+ * reviewer comparing the files sees one pattern rather than two conventions.
16
+ */
17
+ export interface SetTransactionalEmailActiveInput {
18
+ code: string;
19
+ active: boolean;
20
+ }
21
+ /**
22
+ * Refuse a flip the owning module forbids.
23
+ *
24
+ * Refused in **both** directions, exactly as the module door refuses a
25
+ * non-deactivatable module in both. A protected email is always on, so
26
+ * `active: true` is not a repair — it is a request to operate a switch that
27
+ * does not exist, and answering 200 to it would teach an operator that the
28
+ * control is real.
29
+ */
30
+ export declare function assertEmailActivationWritable(code: string, defaults: EmailDefaultsRegistry): void;
31
+ /**
32
+ * Flip one email's activation.
33
+ *
34
+ * Reversible in the registry sense: the row keeps its content, its overrides
35
+ * and its per-channel customizations, so the undo is switching it back on —
36
+ * the same property Constitution XVII asserts for a module.
37
+ */
38
+ export declare function makeSetTransactionalEmailActiveCommand(input: SetTransactionalEmailActiveInput, defaults: EmailDefaultsRegistry): Command<{
39
+ code: string;
40
+ active: boolean;
41
+ }>;
42
+ //# sourceMappingURL=email-activation.commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-activation.commands.d.ts","sourceRoot":"","sources":["../../../src/backend/commands/email-activation.commands.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAGlE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAEpF;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,gCAAgC;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,qBAAqB,GAC9B,IAAI,CAON;AAED;;;;;;GAMG;AACH,wBAAgB,sCAAsC,CACpD,KAAK,EAAE,gCAAgC,EACvC,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC,CA2B5C"}
@@ -0,0 +1,51 @@
1
+ import { ERROR_CODES } from '@endora-commerce/contracts';
2
+ import { HttpError } from '@endora-commerce/platform/http';
3
+ import { TransactionalEmail } from '../entities/transactional-email.entity.js';
4
+ /**
5
+ * Refuse a flip the owning module forbids.
6
+ *
7
+ * Refused in **both** directions, exactly as the module door refuses a
8
+ * non-deactivatable module in both. A protected email is always on, so
9
+ * `active: true` is not a repair — it is a request to operate a switch that
10
+ * does not exist, and answering 200 to it would teach an operator that the
11
+ * control is real.
12
+ */
13
+ export function assertEmailActivationWritable(code, defaults) {
14
+ const reason = defaults.nonDeactivatableReasonOf(code);
15
+ if (reason === null)
16
+ return;
17
+ throw new HttpError(409, ERROR_CODES.TRANSACTIONAL_EMAIL_NOT_DEACTIVATABLE, reason, {
18
+ code,
19
+ reason,
20
+ });
21
+ }
22
+ /**
23
+ * Flip one email's activation.
24
+ *
25
+ * Reversible in the registry sense: the row keeps its content, its overrides
26
+ * and its per-channel customizations, so the undo is switching it back on —
27
+ * the same property Constitution XVII asserts for a module.
28
+ */
29
+ export function makeSetTransactionalEmailActiveCommand(input, defaults) {
30
+ assertEmailActivationWritable(input.code, defaults);
31
+ return {
32
+ action: 'transactional_email.activation.set',
33
+ objectType: 'transactional_email',
34
+ objectId: input.code,
35
+ run: async ({ em }) => {
36
+ const email = await em.findOne(TransactionalEmail, { code: input.code });
37
+ if (!email) {
38
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, `Transactional email "${input.code}" not found.`);
39
+ }
40
+ const before = email.active;
41
+ email.active = input.active;
42
+ await em.flush();
43
+ return {
44
+ result: { code: input.code, active: input.active },
45
+ before: { code: input.code, active: before },
46
+ after: { code: input.code, active: input.active },
47
+ };
48
+ },
49
+ };
50
+ }
51
+ //# sourceMappingURL=email-activation.commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-activation.commands.js","sourceRoot":"","sources":["../../../src/backend/commands/email-activation.commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAuB/E;;;;;;;;GAQG;AACH,MAAM,UAAU,6BAA6B,CAC3C,IAAY,EACZ,QAA+B;IAE/B,MAAM,MAAM,GAAG,QAAQ,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC;IACvD,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO;IAC5B,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,qCAAqC,EAAE,MAAM,EAAE;QAClF,IAAI;QACJ,MAAM;KACP,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sCAAsC,CACpD,KAAuC,EACvC,QAA+B;IAE/B,6BAA6B,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAEpD,OAAO;QACL,MAAM,EAAE,oCAAoC;QAC5C,UAAU,EAAE,qBAAqB;QACjC,QAAQ,EAAE,KAAK,CAAC,IAAI;QACpB,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;YACpB,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACzE,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,SAAS,EACrB,wBAAwB,KAAK,CAAC,IAAI,cAAc,CACjD,CAAC;YACJ,CAAC;YACD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;YAC5B,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;YAC5B,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;YAEjB,OAAO;gBACL,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE;gBAClD,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE;gBAC5C,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE;aAClD,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}