@cosmicdrift/kumiko-document-designer 0.363.0 → 0.364.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +4 -0
- package/dist/i18n.js +144 -50
- package/dist/web/cells.js +1 -1
- package/dist/web/editor-header.d.ts +37 -0
- package/dist/web/editor-header.js +43 -0
- package/dist/web/editor-shell.d.ts +40 -0
- package/dist/web/editor-shell.js +36 -0
- package/dist/web/i18n.js +35 -59
- package/dist/web/layout-editor-body.d.ts +2 -2
- package/dist/web/layout-editor-body.js +94 -49
- package/dist/web/layout-editor.js +8 -6
- package/dist/web/layout-html.d.ts +2 -0
- package/dist/web/layout-html.js +13 -1
- package/dist/web/layout-meta.d.ts +29 -0
- package/dist/web/layout-meta.js +32 -0
- package/dist/web/layout-rails.d.ts +15 -5
- package/dist/web/layout-rails.js +61 -68
- package/dist/web/layout-types.d.ts +4 -0
- package/dist/web/layout-users.d.ts +1 -0
- package/dist/web/layout-users.js +4 -1
- package/dist/web/mail-dialogs.d.ts +1 -6
- package/dist/web/mail-dialogs.js +2 -2
- package/dist/web/mail-draft.d.ts +8 -6
- package/dist/web/mail-draft.js +33 -11
- package/dist/web/mail-edit-center.d.ts +3 -1
- package/dist/web/mail-edit-center.js +5 -4
- package/dist/web/mail-editor-body.d.ts +0 -1
- package/dist/web/mail-editor-body.js +57 -41
- package/dist/web/mail-editor.js +2 -5
- package/dist/web/mail-footer.d.ts +3 -14
- package/dist/web/mail-footer.js +12 -13
- package/dist/web/mail-left-rail.d.ts +9 -4
- package/dist/web/mail-left-rail.js +18 -15
- package/dist/web/mail-missing-locale.d.ts +10 -0
- package/dist/web/mail-missing-locale.js +45 -0
- package/dist/web/mail-origin-bar.d.ts +3 -5
- package/dist/web/mail-origin-bar.js +26 -50
- package/dist/web/mail-preview-side.d.ts +12 -5
- package/dist/web/mail-preview-side.js +14 -5
- package/dist/web/mail-preview.d.ts +9 -5
- package/dist/web/mail-preview.js +24 -13
- package/dist/web/mail-right-rail.js +23 -13
- package/dist/web/mail-types.d.ts +6 -1
- package/dist/web/mail-versions.js +3 -3
- package/dist/web/origin-bar.d.ts +11 -2
- package/dist/web/origin-bar.js +46 -24
- package/dist/web/overrides-drawer.d.ts +3 -2
- package/dist/web/overrides-drawer.js +87 -11
- package/dist/web/overview-row.d.ts +2 -0
- package/dist/web/overview-row.js +2 -1
- package/dist/web/page-canvas.d.ts +1 -0
- package/dist/web/page-canvas.js +23 -13
- package/dist/web/pdf-footer.d.ts +13 -0
- package/dist/web/pdf-footer.js +34 -0
- package/dist/web/pdf-left-rail.d.ts +9 -0
- package/dist/web/pdf-left-rail.js +16 -0
- package/dist/web/pdf-meta.d.ts +3 -5
- package/dist/web/pdf-meta.js +10 -16
- package/dist/web/pdf-preview.js +21 -8
- package/dist/web/pdf-template-editor.js +90 -76
- package/dist/web/rail.d.ts +13 -1
- package/dist/web/rail.js +15 -7
- package/dist/web/relative-time.d.ts +1 -0
- package/dist/web/relative-time.js +18 -0
- package/dist/web/settings-panel.d.ts +11 -5
- package/dist/web/settings-panel.js +24 -13
- package/dist/web/status-label.d.ts +7 -0
- package/dist/web/status-label.js +8 -0
- package/dist/web/types.d.ts +23 -0
- package/dist/web/use-insert-targets.d.ts +1 -1
- package/dist/web/use-mail-persistence.d.ts +0 -1
- package/dist/web/use-mail-persistence.js +1 -1
- package/dist/web/use-own-address.d.ts +5 -0
- package/dist/web/use-own-address.js +14 -0
- package/package.json +8 -8
- package/src/changes.json +24 -0
- package/dist/web/mail-header.d.ts +0 -23
- package/dist/web/mail-header.js +0 -16
- package/dist/web/placeholder-panel.d.ts +0 -21
- package/dist/web/placeholder-panel.js +0 -35
|
@@ -3,13 +3,15 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
3
3
|
import { TemplateResolverHandlers, TemplateResolverQueries, } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
4
|
import { dispatcherErrorText, useMutation, usePrimitives, useQuery, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
5
5
|
import { useState } from "react";
|
|
6
|
+
import { EditorHeader, EditorLocaleSwitch, EditorModeSwitch, originOf, } from "./editor-header.js";
|
|
7
|
+
import { EditorBody, EditorFooter, EditorFrame } from "./editor-shell.js";
|
|
6
8
|
import { fileUrl } from "./file-name.js";
|
|
7
9
|
import { MailConfirmDialogs } from "./mail-dialogs.js";
|
|
8
|
-
import { buildMeta, isDraftDirty, loadedMailOf, parseJsonRecord, parseLinkedResources, variableLabelMap, } from "./mail-draft.js";
|
|
10
|
+
import { buildMeta, isDraftDirty, loadedMailOf, parseJsonRecord, parseLinkedResources, systemLabelMap, variableLabelMap, } from "./mail-draft.js";
|
|
9
11
|
import { MailEditCenter } from "./mail-edit-center.js";
|
|
10
|
-
import {
|
|
11
|
-
import { MailHeader } from "./mail-header.js";
|
|
12
|
+
import { footerMessage, metaFieldMessages } from "./mail-footer.js";
|
|
12
13
|
import { PlaceholderSection, ResourceSection, SystemPlaceholderSection } from "./mail-left-rail.js";
|
|
14
|
+
import { MissingLocaleCanvas } from "./mail-missing-locale.js";
|
|
13
15
|
import { MailOriginBar } from "./mail-origin-bar.js";
|
|
14
16
|
import { PdfLinkModal } from "./mail-pdf-link-modal.js";
|
|
15
17
|
import { MailPreviewMode } from "./mail-preview.js";
|
|
@@ -19,9 +21,12 @@ import { VersionsSection } from "./mail-versions.js";
|
|
|
19
21
|
import { useInsertTargets } from "./use-insert-targets.js";
|
|
20
22
|
import { useMailPersistence } from "./use-mail-persistence.js";
|
|
21
23
|
import { useOverviewRows } from "./use-overview-rows.js";
|
|
24
|
+
import { useOwnAddress } from "./use-own-address.js";
|
|
22
25
|
import { useSettledValue } from "./use-settled-value.js";
|
|
23
26
|
import { useTestMail } from "./use-test-mail.js";
|
|
24
|
-
|
|
27
|
+
// The editor replaces this token with the decoded image size once the preview image loaded.
|
|
28
|
+
const SIZE_TOKEN = "{size}";
|
|
29
|
+
function uniqueValues(values) {
|
|
25
30
|
return [...new Set(values)];
|
|
26
31
|
}
|
|
27
32
|
export function placeholderNames(validation) {
|
|
@@ -31,22 +36,25 @@ export function placeholderNames(validation) {
|
|
|
31
36
|
name,
|
|
32
37
|
...Object.keys(variable.item ?? {}).map((item) => `${name}.${item}`),
|
|
33
38
|
]);
|
|
34
|
-
return
|
|
39
|
+
return uniqueValues([...own, ...validation.systemPlaceholders]);
|
|
35
40
|
}
|
|
36
41
|
export function MailEditorBody(props) {
|
|
37
42
|
const { slug, locale, title, isSystemAdmin, row, onReload, onRefreshRow } = props;
|
|
38
43
|
const t = useTranslation();
|
|
39
44
|
const { Banner } = usePrimitives();
|
|
40
45
|
const [baseline] = useState(() => loadedMailOf(row));
|
|
41
|
-
const [draft, setDraft] = useState(baseline
|
|
42
|
-
const [settled, settle] = useSettledValue(baseline
|
|
46
|
+
const [draft, setDraft] = useState(baseline);
|
|
47
|
+
const [settled, settle] = useSettledValue(baseline);
|
|
43
48
|
const [mode, setMode] = useState("edit");
|
|
44
49
|
const [creating, setCreating] = useState(false);
|
|
45
50
|
const [dialog, setDialog] = useState({ kind: "none" });
|
|
46
51
|
const [uploadOpen, setUploadOpen] = useState(false);
|
|
47
52
|
const [pdfOpen, setPdfOpen] = useState(false);
|
|
48
53
|
const [resourceError, setResourceError] = useState(null);
|
|
54
|
+
const [resetError, setResetError] = useState(null);
|
|
49
55
|
const [editorEpoch, setEditorEpoch] = useState(0);
|
|
56
|
+
const [includeAttachments, setIncludeAttachments] = useState(true);
|
|
57
|
+
const [prefixSubject, setPrefixSubject] = useState(true);
|
|
50
58
|
const insert = useInsertTargets();
|
|
51
59
|
const persistence = useMailPersistence({
|
|
52
60
|
slug,
|
|
@@ -54,9 +62,9 @@ export function MailEditorBody(props) {
|
|
|
54
62
|
title,
|
|
55
63
|
isSystemAdmin,
|
|
56
64
|
row,
|
|
57
|
-
sender: baseline.sender,
|
|
58
65
|
});
|
|
59
66
|
const testMail = useTestMail();
|
|
67
|
+
const own = useOwnAddress();
|
|
60
68
|
const resourceRemove = useMutation(TemplateResolverHandlers.resourceRemove);
|
|
61
69
|
const layouts = useOverviewRows("mail-layout");
|
|
62
70
|
const pdfTemplates = useOverviewRows("document-pdf");
|
|
@@ -67,7 +75,7 @@ export function MailEditorBody(props) {
|
|
|
67
75
|
kind: "mail",
|
|
68
76
|
contentFormat: "html",
|
|
69
77
|
content: settled.body,
|
|
70
|
-
meta: buildMeta(settled
|
|
78
|
+
meta: buildMeta(settled),
|
|
71
79
|
variableSchema,
|
|
72
80
|
locale,
|
|
73
81
|
scope: isSystemAdmin ? "system" : "tenant",
|
|
@@ -78,13 +86,12 @@ export function MailEditorBody(props) {
|
|
|
78
86
|
settle(next);
|
|
79
87
|
};
|
|
80
88
|
const reset = () => {
|
|
81
|
-
setDraft(baseline
|
|
82
|
-
settle(baseline
|
|
89
|
+
setDraft(baseline);
|
|
90
|
+
settle(baseline);
|
|
83
91
|
setEditorEpoch((epoch) => epoch + 1);
|
|
84
92
|
persistence.clearError();
|
|
85
93
|
};
|
|
86
|
-
const dirty = isDraftDirty(draft, baseline
|
|
87
|
-
const isActiveTenantRow = row?.scope === "tenant" && row.status === "active";
|
|
94
|
+
const dirty = isDraftDirty(draft, baseline);
|
|
88
95
|
const busy = persistence.busy;
|
|
89
96
|
const canSave = !locked && !busy && validation?.canSave === true && (dirty || creating);
|
|
90
97
|
const canPublish = !locked &&
|
|
@@ -96,20 +103,10 @@ export function MailEditorBody(props) {
|
|
|
96
103
|
if (await persist(publish))
|
|
97
104
|
await onReload();
|
|
98
105
|
};
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
setDialog({ kind: "activeSave" });
|
|
102
|
-
else
|
|
103
|
-
void saveAndReload(false);
|
|
104
|
-
};
|
|
105
|
-
const perform = (target) => {
|
|
106
|
-
if (target.kind === "locale")
|
|
107
|
-
props.onLocaleChange(target.locale);
|
|
108
|
-
else
|
|
109
|
-
props.onBack();
|
|
110
|
-
};
|
|
106
|
+
const saveDraft = () => void saveAndReload(false);
|
|
107
|
+
const perform = (target) => props.onLocaleChange(target.locale);
|
|
111
108
|
const leave = (target) => {
|
|
112
|
-
if (target.
|
|
109
|
+
if (target.locale === locale)
|
|
113
110
|
return;
|
|
114
111
|
if (dirty)
|
|
115
112
|
setDialog({ kind: "leave", target });
|
|
@@ -124,7 +121,10 @@ export function MailEditorBody(props) {
|
|
|
124
121
|
resource.key,
|
|
125
122
|
{
|
|
126
123
|
src: fileUrl(resource.fileRefId),
|
|
127
|
-
caption: t("documentDesigner.mail.resource.caption", {
|
|
124
|
+
caption: t("documentDesigner.mail.resource.caption", {
|
|
125
|
+
key: resource.key,
|
|
126
|
+
size: SIZE_TOKEN,
|
|
127
|
+
}),
|
|
128
128
|
},
|
|
129
129
|
],
|
|
130
130
|
]
|
|
@@ -146,31 +146,49 @@ export function MailEditorBody(props) {
|
|
|
146
146
|
slug,
|
|
147
147
|
locale,
|
|
148
148
|
content: draft.body,
|
|
149
|
-
meta: buildMeta(draft
|
|
149
|
+
meta: buildMeta(draft),
|
|
150
150
|
variableSchema,
|
|
151
|
+
includeAttachments,
|
|
152
|
+
prefixSubject,
|
|
151
153
|
});
|
|
152
154
|
};
|
|
155
|
+
// Nothing is written yet in a missing language, so no placeholder may read as missing.
|
|
156
|
+
const railValidation = locked && validation != null ? { ...validation, missingRequired: [] } : (validation ?? null);
|
|
153
157
|
const names = placeholderNames(validation ?? null);
|
|
154
158
|
const vocabulary = {
|
|
155
159
|
names,
|
|
156
|
-
labels:
|
|
160
|
+
labels: {
|
|
161
|
+
...systemLabelMap(t, validation?.systemPlaceholders ?? []),
|
|
162
|
+
...variableLabelMap(validation?.variables ?? {}),
|
|
163
|
+
},
|
|
157
164
|
system: validation?.systemPlaceholders ?? [],
|
|
158
165
|
};
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
166
|
+
const attachments = resources.filter((resource) => resource.kind !== "image");
|
|
167
|
+
const footer = locked || validation === undefined || validation === null
|
|
168
|
+
? null
|
|
169
|
+
: footerMessage(t, validation, mode === "preview");
|
|
170
|
+
const fieldErrors = locked ? {} : metaFieldMessages(t, validation ?? null);
|
|
171
|
+
const publish = () => void saveAndReload(true);
|
|
172
|
+
const controls = (_jsx(EditorLocaleSwitch, { testIdPrefix: "mail", locales: props.locales, locale: locale, onLocale: (next) => leave({ kind: "locale", locale: next }) }));
|
|
173
|
+
const origin = row === null ? null : originOf(row.scope !== "tenant", props.systemVersion !== null);
|
|
174
|
+
return (_jsxs(EditorFrame, { testId: "document-designer-mail-editor", children: [_jsx(EditorHeader, { title: title, stored: row === null ? null : { status: row.status, version: row.version }, actions: { busy, canSave, canPublish, onSave: saveDraft, onPublish: publish }, children: _jsx(EditorModeSwitch, { testIdPrefix: "mail", mode: mode, onMode: setMode }) }), mode === "edit" && (_jsx(MailOriginBar, { row: row, systemVersion: props.systemVersion, locale: locale, isSystemAdmin: isSystemAdmin, creating: creating, controls: controls, onReload: () => {
|
|
175
|
+
setResetError(null);
|
|
176
|
+
void onReload();
|
|
177
|
+
}, onResetError: setResetError })), testMail.state.kind === "sent" && (_jsx(Banner, { variant: "primary", testId: "mail-test-sent", children: t("documentDesigner.mail.testMail.sent", { subject: testMail.state.subject }) })), testMail.state.kind === "error" && (_jsx(Banner, { variant: "error", testId: "mail-test-error", children: testMail.state.text })), resetError !== null && (_jsx(Banner, { variant: "error", testId: "mail-reset-error", children: resetError })), resourceError !== null && (_jsx(Banner, { variant: "error", testId: "mail-resource-error", children: resourceError })), mode === "edit" ? (_jsx(EditorBody, { left: _jsxs(_Fragment, { children: [_jsx(PlaceholderSection, { validation: railValidation, onInsert: insert.insertPlaceholder }), _jsx(SystemPlaceholderSection, { validation: railValidation, onInsert: insert.insertPlaceholder }), _jsx(ResourceSection, { resources: resources, pdfTitles: pdfTitles, canUpload: uploadTemplateId !== null, onUpload: () => setUploadOpen(true), onInsert: insert.insertResource, onRemove: (key) => setDialog({ kind: "remove", key }) })] }), center: locked ? (_jsx(MissingLocaleCanvas, { slug: slug, locale: locale, fallbackLocale: props.fallbackLocale, copyScope: isSystemAdmin ? "system" : "tenant", onCreateBlank: () => setCreating(true), onCopied: () => void onReload() })) : (_jsx(MailEditCenter, { draft: draft, vocabulary: vocabulary, resourcePreviews: resourcePreviews, insert: insert, readOnly: false, fieldErrors: fieldErrors, onChange: update }, editorEpoch)), right: _jsxs(_Fragment, { children: [_jsx(PropertiesSection, { draft: draft, layouts: layouts.rows, onChange: update }), _jsx(AttachmentSection, { attachments: attachments, pdfTitles: pdfTitles, canEdit: uploadTemplateId !== null, onAddPdf: () => setPdfOpen(true), onRemove: (key) => setDialog({ kind: "remove", key }) }), _jsx(UsageSection, { slug: slug }), _jsx(VersionsSection, { rowId: row?.id ?? null, currentVersion: row?.version ?? 0, currentStatus: row?.status ?? "draft" })] }) })) : (_jsx(MailPreviewMode, { payload: {
|
|
167
178
|
slug,
|
|
168
179
|
kind: "mail",
|
|
169
180
|
locale,
|
|
170
181
|
content: settled.body,
|
|
171
|
-
meta: buildMeta(settled
|
|
182
|
+
meta: buildMeta(settled),
|
|
172
183
|
variableSchema,
|
|
173
|
-
}, validation: validation ?? null, slug: slug, locale: locale,
|
|
184
|
+
}, validation: validation ?? null, slug: slug, locale: locale, fromName: settled.senderName, own: own, origin: origin, controls: controls, testMail: {
|
|
185
|
+
pending: testMail.pending,
|
|
186
|
+
onSend: sendTestMail,
|
|
187
|
+
includeAttachments,
|
|
188
|
+
onIncludeAttachments: setIncludeAttachments,
|
|
189
|
+
prefixSubject,
|
|
190
|
+
onPrefixSubject: setPrefixSubject,
|
|
191
|
+
} })), !locked && (_jsx(EditorFooter, { testIdPrefix: "mail-editor", message: footer, saveError: persistence.error, modifiedAt: row?.modifiedAt ?? null, dirty: dirty, busy: busy, canSave: canSave, canPublish: canPublish, onDiscard: () => setDialog({ kind: "discard" }), onSave: saveDraft, onPublish: publish })), uploadTemplateId !== null && (_jsxs(_Fragment, { children: [_jsx(UploadModal, { open: uploadOpen, onOpenChange: setUploadOpen, target: {
|
|
174
192
|
templateId: uploadTemplateId,
|
|
175
193
|
takenKeys,
|
|
176
194
|
onUploaded: (uploaded) => {
|
|
@@ -181,8 +199,6 @@ export function MailEditorBody(props) {
|
|
|
181
199
|
} }), _jsx(PdfLinkModal, { open: pdfOpen, onOpenChange: setPdfOpen, templates: pdfTemplates.rows, variableLabels: vocabulary.labels, templateId: uploadTemplateId, takenKeys: takenKeys, onLinked: () => void onRefreshRow() })] })), _jsx(MailConfirmDialogs, { dialog: dialog, onClose: () => setDialog({ kind: "none" }), onDiscard: () => {
|
|
182
200
|
reset();
|
|
183
201
|
setDialog({ kind: "none" });
|
|
184
|
-
}, onSaveDraft: async () => {
|
|
185
|
-
await saveAndReload(false);
|
|
186
202
|
}, onRemove: removeResource, onLeaveSave: async (target) => {
|
|
187
203
|
if (await persist(false))
|
|
188
204
|
perform(target);
|
package/dist/web/mail-editor.js
CHANGED
|
@@ -4,7 +4,7 @@ import { TemplateResolverQueries } from "@cosmicdrift/kumiko-bundled-features/te
|
|
|
4
4
|
import { useNav, useQuery, useUserRoles } from "@cosmicdrift/kumiko-renderer";
|
|
5
5
|
import { LoadingState } from "@cosmicdrift/kumiko-renderer-web";
|
|
6
6
|
import { useState } from "react";
|
|
7
|
-
import { DEFAULT_TEMPLATE_LOCALES
|
|
7
|
+
import { DEFAULT_TEMPLATE_LOCALES } from "../constants.js";
|
|
8
8
|
import { MailEditorBody } from "./mail-editor-body.js";
|
|
9
9
|
import { useOverviewRows } from "./use-overview-rows.js";
|
|
10
10
|
function defaultLocaleOf(appLocales, rowLocales) {
|
|
@@ -25,7 +25,6 @@ function shownSummaryOf(summaries, slug, isSystemAdmin) {
|
|
|
25
25
|
return (isSystemAdmin ? system : (tenant ?? system)) ?? null;
|
|
26
26
|
}
|
|
27
27
|
function MailEditorData({ slug, locale, locales, overviewRow, isSystemAdmin, onLocaleChange, refreshOverview, }) {
|
|
28
|
-
const nav = useNav();
|
|
29
28
|
const [generation, setGeneration] = useState(0);
|
|
30
29
|
const list = useQuery(TemplateResolverQueries.list, {
|
|
31
30
|
kind: "mail",
|
|
@@ -46,9 +45,7 @@ function MailEditorData({ slug, locale, locales, overviewRow, isSystemAdmin, onL
|
|
|
46
45
|
const rowLocales = overviewRow?.locales ?? [];
|
|
47
46
|
return (_jsx(MailEditorBody, { slug: slug, locale: locale, locales: locales, title: overviewRow?.title ?? slug, isSystemAdmin: isSystemAdmin, row: row, systemVersion: overviewRow?.systemVersion ?? null, fallbackLocale: fallbackLocaleOf(locale, rowLocales), onLocaleChange: onLocaleChange, onReload: reload, onRefreshRow: async () => {
|
|
48
47
|
await stored.refetch();
|
|
49
|
-
},
|
|
50
|
-
screenId: isSystemAdmin ? PLATFORM_TEMPLATES_SCREEN_ID : TEMPLATES_SCREEN_ID,
|
|
51
|
-
}) }, `${locale}|${generation}|${row?.id ?? "none"}`));
|
|
48
|
+
} }, `${locale}|${generation}|${row?.id ?? "none"}`));
|
|
52
49
|
}
|
|
53
50
|
function MailEditorEntry({ appLocales }) {
|
|
54
51
|
const nav = useNav();
|
|
@@ -1,21 +1,10 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
1
|
import type { MailValidation } from "./mail-types.js";
|
|
3
2
|
type Translate = (key: string, params?: Readonly<Record<string, unknown>>) => string;
|
|
4
3
|
export declare function footerMessage(t: Translate, validation: MailValidation, previewMode: boolean): {
|
|
5
4
|
readonly tone: "bad" | "warn" | "ok";
|
|
6
5
|
readonly text: string;
|
|
7
6
|
};
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
readonly saveError: string | null;
|
|
12
|
-
readonly modifiedAt: string | null;
|
|
13
|
-
readonly dirty: boolean;
|
|
14
|
-
readonly busy: boolean;
|
|
15
|
-
readonly canSave: boolean;
|
|
16
|
-
readonly canPublish: boolean;
|
|
17
|
-
readonly onDiscard: () => void;
|
|
18
|
-
readonly onSave: () => void;
|
|
19
|
-
readonly onPublish: () => void;
|
|
20
|
-
}): ReactNode;
|
|
7
|
+
declare const MAIL_META_FIELDS: readonly ["subject", "previewText"];
|
|
8
|
+
export type MailMetaField = (typeof MAIL_META_FIELDS)[number];
|
|
9
|
+
export declare function metaFieldMessages(t: Translate, validation: MailValidation | null): Partial<Record<MailMetaField, string>>;
|
|
21
10
|
export {};
|
package/dist/web/mail-footer.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
|
-
import { formatWhen, usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
4
1
|
function issueText(t, issue) {
|
|
5
2
|
const translated = t(issue.i18nKey, issue.params);
|
|
6
3
|
return translated === issue.i18nKey ? issue.code : translated;
|
|
@@ -31,14 +28,16 @@ export function footerMessage(t, validation, previewMode) {
|
|
|
31
28
|
}
|
|
32
29
|
return { tone: "ok", text: t("documentDesigner.mail.footer.ok") };
|
|
33
30
|
}
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
31
|
+
const MAIL_META_FIELDS = ["subject", "previewText"];
|
|
32
|
+
// The server names the setting and the reason per issue; the field itself shows that sentence.
|
|
33
|
+
export function metaFieldMessages(t, validation) {
|
|
34
|
+
if (validation === null)
|
|
35
|
+
return {};
|
|
36
|
+
const messages = {};
|
|
37
|
+
for (const field of MAIL_META_FIELDS) {
|
|
38
|
+
const issue = validation.issues.find((candidate) => candidate.path === `meta.${field}`);
|
|
39
|
+
if (issue !== undefined)
|
|
40
|
+
messages[field] = issueText(t, issue);
|
|
41
|
+
}
|
|
42
|
+
return messages;
|
|
44
43
|
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { LinkedResourceView, MailValidation } from "./mail-types.js";
|
|
3
|
-
export
|
|
4
|
-
|
|
3
|
+
export type PlaceholderSource = Pick<MailValidation, "variables" | "used" | "missingRequired" | "systemPlaceholders">;
|
|
4
|
+
export declare function PlaceholderSection({ validation, onInsert, idPrefix, }: {
|
|
5
|
+
readonly validation: PlaceholderSource | null;
|
|
5
6
|
readonly onInsert: (name: string) => void;
|
|
7
|
+
readonly idPrefix?: string;
|
|
6
8
|
}): ReactNode;
|
|
7
|
-
export declare function SystemPlaceholderSection({ validation, onInsert, }: {
|
|
8
|
-
readonly validation:
|
|
9
|
+
export declare function SystemPlaceholderSection({ validation, onInsert, idPrefix, isLayoutOwned, }: {
|
|
10
|
+
readonly validation: PlaceholderSource | null;
|
|
9
11
|
readonly onInsert: (name: string) => void;
|
|
12
|
+
readonly idPrefix?: string;
|
|
13
|
+
/** Placeholders the layout fills in, so a template that never uses them is fine. */
|
|
14
|
+
readonly isLayoutOwned?: (name: string) => boolean;
|
|
10
15
|
}): ReactNode;
|
|
11
16
|
export declare function ResourceSection({ resources, pdfTitles, canUpload, onUpload, onInsert, onRemove, }: {
|
|
12
17
|
readonly resources: readonly LinkedResourceView[];
|
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// @runtime client
|
|
3
3
|
import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
4
|
-
import { PLACEHOLDER_SYMBOL } from "./mail-draft.js";
|
|
4
|
+
import { LAYOUT_OWNED_PLACEHOLDERS, PLACEHOLDER_SYMBOL, SYSTEM_PLACEHOLDER_TYPE, systemPlaceholderLabel, } from "./mail-draft.js";
|
|
5
5
|
import { RailHint, RailRow, RailSection } from "./rail.js";
|
|
6
6
|
function placeholderStatus(t, validation, name) {
|
|
7
|
-
if (validation.missingRequired.includes(name))
|
|
8
|
-
return t("documentDesigner.mail.placeholder.missing");
|
|
9
|
-
|
|
7
|
+
if (validation.missingRequired.includes(name)) {
|
|
8
|
+
return { text: t("documentDesigner.mail.placeholder.missing"), tone: "bad" };
|
|
9
|
+
}
|
|
10
|
+
return validation.used.includes(name) ? { text: "✓", tone: "ok" } : { text: "–", tone: "muted" };
|
|
10
11
|
}
|
|
11
|
-
export function PlaceholderSection({ validation, onInsert, }) {
|
|
12
|
+
export function PlaceholderSection({ validation, onInsert, idPrefix = "mail", }) {
|
|
12
13
|
const t = useTranslation();
|
|
13
14
|
const entries = Object.entries(validation?.variables ?? {});
|
|
14
|
-
return (_jsxs(RailSection, { title: t("documentDesigner.mail.placeholder.title"), testId:
|
|
15
|
-
entries.map(([name, variable]) =>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const translated = t(key);
|
|
20
|
-
return translated === key ? name : translated;
|
|
15
|
+
return (_jsxs(RailSection, { title: t("documentDesigner.mail.placeholder.title"), testId: `${idPrefix}-editor-placeholders`, children: [validation !== null &&
|
|
16
|
+
entries.map(([name, variable]) => {
|
|
17
|
+
const status = placeholderStatus(t, validation, name);
|
|
18
|
+
return (_jsx(RailRow, { testId: `${idPrefix}-placeholder-${name}`, symbol: PLACEHOLDER_SYMBOL[variable.type], label: variable.label ?? name, required: variable.required === true, bad: validation.missingRequired.includes(name), trailing: status.text, trailingTone: status.tone, onPress: () => onInsert(name) }, name));
|
|
19
|
+
}), _jsx(RailHint, { children: t("documentDesigner.mail.placeholder.hint") })] }));
|
|
21
20
|
}
|
|
22
|
-
export function SystemPlaceholderSection({ validation, onInsert, }) {
|
|
21
|
+
export function SystemPlaceholderSection({ validation, onInsert, idPrefix = "mail", isLayoutOwned = (name) => LAYOUT_OWNED_PLACEHOLDERS.has(name), }) {
|
|
23
22
|
const t = useTranslation();
|
|
24
|
-
return (
|
|
23
|
+
return (_jsxs(RailSection, { title: t("documentDesigner.mail.system.title"), testId: `${idPrefix}-editor-system-placeholders`, children: [(validation?.systemPlaceholders ?? []).map((name) => {
|
|
24
|
+
const used = validation?.used.includes(name) === true;
|
|
25
|
+
const layoutOwned = !used && isLayoutOwned(name);
|
|
26
|
+
return (_jsx(RailRow, { testId: `${idPrefix}-system-${name}`, symbol: PLACEHOLDER_SYMBOL[SYSTEM_PLACEHOLDER_TYPE[name] ?? "text"], label: systemPlaceholderLabel(t, name), trailing: used ? "✓" : layoutOwned ? t("documentDesigner.mail.system.layout") : "–", trailingTone: used ? "ok" : "muted", onPress: () => onInsert(name) }, name));
|
|
27
|
+
}), _jsx(RailHint, { children: t("documentDesigner.mail.system.hint") })] }));
|
|
25
28
|
}
|
|
26
29
|
function resourceRow(t, resource, pdfTitles) {
|
|
27
30
|
switch (resource.kind) {
|
|
@@ -48,6 +51,6 @@ export function ResourceSection({ resources, pdfTitles, canUpload, onUpload, onI
|
|
|
48
51
|
const { Button } = usePrimitives();
|
|
49
52
|
return (_jsxs(RailSection, { title: t("documentDesigner.mail.resource.title"), testId: "mail-editor-resources", action: _jsx(Button, { variant: "link", disabled: !canUpload, onClick: onUpload, testId: "mail-resource-upload-open", children: t("documentDesigner.mail.resource.upload") }), children: [!canUpload && _jsx(RailHint, { children: t("documentDesigner.mail.resource.saveFirst") }), resources.map((resource) => {
|
|
50
53
|
const row = resourceRow(t, resource, pdfTitles);
|
|
51
|
-
return (
|
|
54
|
+
return (_jsx(RailRow, { testId: "mail-resource-row", symbol: row.symbol, label: row.label, trailing: row.meta, action: _jsx(Button, { variant: "danger-ghost", size: "sm", onClick: () => onRemove(resource.key), testId: `mail-resource-remove-${resource.key}`, children: t("documentDesigner.mail.resource.remove") }), ...(resource.kind === "image" && { onPress: () => onInsert(resource.key) }) }, resource.key));
|
|
52
55
|
}), _jsx(RailHint, { children: t("documentDesigner.mail.resource.hint") })] }));
|
|
53
56
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export declare function languageAdjective(t: (key: string, params?: Readonly<Record<string, unknown>>) => string, locale: string): string;
|
|
3
|
+
export declare function MissingLocaleCanvas({ slug, locale, fallbackLocale, copyScope, onCreateBlank, onCopied, }: {
|
|
4
|
+
readonly slug: string;
|
|
5
|
+
readonly locale: string;
|
|
6
|
+
readonly fallbackLocale: string | null;
|
|
7
|
+
readonly copyScope: "tenant" | "system";
|
|
8
|
+
readonly onCreateBlank: () => void;
|
|
9
|
+
readonly onCopied: () => void;
|
|
10
|
+
}): ReactNode;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// @runtime client
|
|
3
|
+
import { TemplateResolverHandlers } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
|
+
import { dispatcherErrorText, useMutation, usePrimitives, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
import { languageName } from "./mail-origin-bar.js";
|
|
7
|
+
export function languageAdjective(t, locale) {
|
|
8
|
+
const key = `documentDesigner.mail.languageAdjective.${locale}`;
|
|
9
|
+
const translated = t(key);
|
|
10
|
+
return translated === key ? locale : translated;
|
|
11
|
+
}
|
|
12
|
+
// Shown in place of the editor while a language has no version yet: nothing to edit, so no
|
|
13
|
+
// subject line, toolbar or properties; the two ways to get a version sit right here.
|
|
14
|
+
export function MissingLocaleCanvas({ slug, locale, fallbackLocale, copyScope, onCreateBlank, onCopied, }) {
|
|
15
|
+
const t = useTranslation();
|
|
16
|
+
const { Banner, Button } = usePrimitives();
|
|
17
|
+
const copyLocale = useMutation(TemplateResolverHandlers.copyLocale);
|
|
18
|
+
const [error, setError] = useState(null);
|
|
19
|
+
const copy = async (from) => {
|
|
20
|
+
setError(null);
|
|
21
|
+
const result = await copyLocale.mutate({
|
|
22
|
+
slug,
|
|
23
|
+
kind: "mail",
|
|
24
|
+
fromLocale: from,
|
|
25
|
+
toLocale: locale,
|
|
26
|
+
overwrite: false,
|
|
27
|
+
scope: copyScope,
|
|
28
|
+
});
|
|
29
|
+
if (result.isSuccess)
|
|
30
|
+
onCopied();
|
|
31
|
+
else
|
|
32
|
+
setError(dispatcherErrorText(result.error, t));
|
|
33
|
+
};
|
|
34
|
+
return (_jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center p-8", "data-testid": "mail-missing-locale", children: _jsxs("div", { className: "flex max-w-md flex-col items-center gap-4 text-center", children: [_jsx("p", { className: "m-0 text-base", children: fallbackLocale === null
|
|
35
|
+
? t("documentDesigner.mail.missing.textNoFallback", {
|
|
36
|
+
language: languageName(t, locale),
|
|
37
|
+
})
|
|
38
|
+
: t("documentDesigner.mail.missing.text", {
|
|
39
|
+
language: languageName(t, locale),
|
|
40
|
+
locale,
|
|
41
|
+
fallbackAdjective: languageAdjective(t, fallbackLocale),
|
|
42
|
+
}) }), _jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [fallbackLocale !== null && (_jsx(Button, { variant: "primary", disabled: copyLocale.pending, onClick: () => copy(fallbackLocale), testId: "mail-copy-locale", children: t("documentDesigner.mail.missing.copy", {
|
|
43
|
+
language: languageName(t, fallbackLocale),
|
|
44
|
+
}) })), _jsx(Button, { variant: "secondary", onClick: onCreateBlank, testId: "mail-create-blank", children: t("documentDesigner.mail.origin.createBlank") })] }), fallbackLocale !== null && (_jsx("p", { className: "m-0 text-xs text-muted-foreground", children: t("documentDesigner.mail.origin.copyNote") })), error !== null && (_jsx(Banner, { variant: "error", testId: "mail-copy-error", children: error }))] }) }));
|
|
45
|
+
}
|
|
@@ -2,16 +2,14 @@ import { type ReactNode } from "react";
|
|
|
2
2
|
import type { TemplateRowView } from "./mail-types.js";
|
|
3
3
|
type Translate = (key: string, params?: Readonly<Record<string, unknown>>) => string;
|
|
4
4
|
export declare function languageName(t: Translate, locale: string): string;
|
|
5
|
-
export declare function MailOriginBar({ row,
|
|
5
|
+
export declare function MailOriginBar({ row, systemVersion, locale, isSystemAdmin, creating, controls, onReload, onResetError, }: {
|
|
6
6
|
readonly row: TemplateRowView | null;
|
|
7
|
-
readonly title: string;
|
|
8
7
|
readonly systemVersion: number | null;
|
|
9
|
-
readonly slug: string;
|
|
10
8
|
readonly locale: string;
|
|
11
|
-
readonly fallbackLocale: string | null;
|
|
12
9
|
readonly isSystemAdmin: boolean;
|
|
13
10
|
readonly creating: boolean;
|
|
14
|
-
readonly
|
|
11
|
+
readonly controls: ReactNode;
|
|
15
12
|
readonly onReload: () => void;
|
|
13
|
+
readonly onResetError: (message: string) => void;
|
|
16
14
|
}): ReactNode;
|
|
17
15
|
export {};
|
|
@@ -3,13 +3,19 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
3
3
|
import { TemplateResolverHandlers } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
4
|
import { dispatcherErrorText, formatWhen, useMutation, usePrimitives, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
5
5
|
import { useState } from "react";
|
|
6
|
-
import {
|
|
6
|
+
import { originOf } from "./editor-header.js";
|
|
7
|
+
import { EditorInfoBar } from "./editor-shell.js";
|
|
8
|
+
import { DiffDrawer } from "./origin-bar.js";
|
|
9
|
+
import { draftBesideActiveSentence } from "./status-label.js";
|
|
7
10
|
export function languageName(t, locale) {
|
|
8
11
|
const key = `documentDesigner.mail.language.${locale}`;
|
|
9
12
|
const translated = t(key);
|
|
10
13
|
return translated === key ? locale : translated;
|
|
11
14
|
}
|
|
12
15
|
function statusSentence(t, row) {
|
|
16
|
+
const besideActive = draftBesideActiveSentence(t, row);
|
|
17
|
+
if (besideActive !== null)
|
|
18
|
+
return besideActive;
|
|
13
19
|
if (row.status === "active") {
|
|
14
20
|
return t("documentDesigner.mail.origin.activeSince", {
|
|
15
21
|
when: row.modifiedAt === null ? "–" : formatWhen(row.modifiedAt),
|
|
@@ -19,66 +25,36 @@ function statusSentence(t, row) {
|
|
|
19
25
|
? "documentDesigner.mail.origin.archived"
|
|
20
26
|
: "documentDesigner.mail.origin.notPublished");
|
|
21
27
|
}
|
|
22
|
-
|
|
23
|
-
function MissingLocaleBar({ slug, locale, fallbackLocale, copyScope, creating, onCreateBlank, onCopied, }) {
|
|
28
|
+
function MissingLocaleBar({ locale, creating, controls, }) {
|
|
24
29
|
const t = useTranslation();
|
|
25
|
-
|
|
26
|
-
const copyLocale = useMutation(TemplateResolverHandlers.copyLocale);
|
|
27
|
-
const [error, setError] = useState(null);
|
|
28
|
-
if (creating) {
|
|
29
|
-
return (_jsx("div", { className: `${BAR_CLASS} bg-muted`, "data-testid": "mail-origin-bar", children: _jsx(Text, { children: t("documentDesigner.mail.origin.creating", { language: languageName(t, locale) }) }) }));
|
|
30
|
-
}
|
|
31
|
-
const copy = async (from) => {
|
|
32
|
-
setError(null);
|
|
33
|
-
const result = await copyLocale.mutate({
|
|
34
|
-
slug,
|
|
35
|
-
kind: "mail",
|
|
36
|
-
fromLocale: from,
|
|
37
|
-
toLocale: locale,
|
|
38
|
-
overwrite: false,
|
|
39
|
-
scope: copyScope,
|
|
40
|
-
});
|
|
41
|
-
if (result.isSuccess)
|
|
42
|
-
onCopied();
|
|
43
|
-
else
|
|
44
|
-
setError(dispatcherErrorText(result.error, t));
|
|
45
|
-
};
|
|
46
|
-
return (_jsxs("div", { className: `${BAR_CLASS} border-status-warn bg-status-warn-surface`, "data-testid": "mail-origin-bar", "data-variant": "warn", children: [_jsxs(Text, { children: [t(fallbackLocale === null
|
|
47
|
-
? "documentDesigner.mail.origin.missingNoFallback"
|
|
48
|
-
: "documentDesigner.mail.origin.missing", {
|
|
49
|
-
language: languageName(t, locale),
|
|
50
|
-
locale,
|
|
51
|
-
fallback: fallbackLocale ?? "",
|
|
52
|
-
}), fallbackLocale !== null && ` ${t("documentDesigner.mail.origin.copyNote")}`] }), _jsx("div", { className: "grow" }), fallbackLocale !== null && (_jsx(Button, { size: "sm", variant: "secondary", loading: copyLocale.pending, onClick: () => copy(fallbackLocale), testId: "mail-copy-locale", children: t("documentDesigner.mail.origin.copyFrom", { fallback: fallbackLocale }) })), _jsx(Button, { size: "sm", variant: "secondary", onClick: onCreateBlank, testId: "mail-create-blank", children: t("documentDesigner.mail.origin.createBlank") }), error !== null && (_jsx(Banner, { variant: "error", testId: "mail-copy-error", children: error }))] }));
|
|
30
|
+
return creating ? (_jsx(EditorInfoBar, { tone: "muted", testId: "mail-origin-bar", controls: controls, children: t("documentDesigner.mail.origin.creating", { language: languageName(t, locale) }) })) : (_jsx(EditorInfoBar, { tone: "warn", testId: "mail-origin-bar", controls: controls, children: t("documentDesigner.mail.origin.missing", { language: languageName(t, locale) }) }));
|
|
53
31
|
}
|
|
54
|
-
function TenantOriginBar({ row,
|
|
32
|
+
function TenantOriginBar({ row, systemVersion, controls, onReset, onResetError, }) {
|
|
55
33
|
const t = useTranslation();
|
|
56
|
-
const { Button, Dialog
|
|
34
|
+
const { Button, Dialog } = usePrimitives();
|
|
57
35
|
const [diffOpen, setDiffOpen] = useState(false);
|
|
58
36
|
const [resetOpen, setResetOpen] = useState(false);
|
|
59
37
|
const resetToSystem = useMutation(TemplateResolverHandlers.resetToSystem);
|
|
60
38
|
const hasSystem = systemVersion !== null;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
} })] }));
|
|
39
|
+
return (_jsxs(EditorInfoBar, { tone: "active", testId: "mail-origin-bar", origin: originOf(false, hasSystem), controls: controls, overlays: _jsxs(_Fragment, { children: [diffOpen && _jsx(DiffDrawer, { tenantId: row.id, onClose: () => setDiffOpen(false) }), _jsx(Dialog, { open: resetOpen, onOpenChange: setResetOpen, title: t("documentDesigner.mail.reset.title"), description: t("documentDesigner.mail.reset.description"), confirmLabel: t("documentDesigner.mail.reset.confirm"), variant: "danger", onConfirm: async () => {
|
|
40
|
+
const result = await resetToSystem.mutate({ id: row.id });
|
|
41
|
+
if (result.isSuccess)
|
|
42
|
+
onReset();
|
|
43
|
+
else
|
|
44
|
+
onResetError(dispatcherErrorText(result.error, t));
|
|
45
|
+
} })] }), links: hasSystem && (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "link", onClick: () => setDiffOpen(true), testId: "document-designer-diff-open", children: t("documentDesigner.editor.diff.open") }), _jsx(Button, { variant: "link", onClick: () => setResetOpen(true), testId: "document-designer-reset-open", children: t("documentDesigner.mail.origin.reset") })] })), children: [hasSystem
|
|
46
|
+
? t("documentDesigner.mail.origin.overrides", { version: systemVersion })
|
|
47
|
+
: t("documentDesigner.mail.origin.tenantOnly"), " ", statusSentence(t, row)] }));
|
|
71
48
|
}
|
|
72
|
-
export function MailOriginBar({ row,
|
|
49
|
+
export function MailOriginBar({ row, systemVersion, locale, isSystemAdmin, creating, controls, onReload, onResetError, }) {
|
|
73
50
|
const t = useTranslation();
|
|
74
|
-
const { Text } = usePrimitives();
|
|
75
51
|
if (row === null) {
|
|
76
|
-
return
|
|
52
|
+
return _jsx(MissingLocaleBar, { locale: locale, creating: creating, controls: controls });
|
|
77
53
|
}
|
|
78
54
|
if (row.scope === "tenant") {
|
|
79
|
-
return (_jsx(TenantOriginBar, { row: row,
|
|
55
|
+
return (_jsx(TenantOriginBar, { row: row, systemVersion: systemVersion, controls: controls, onReset: onReload, onResetError: onResetError }));
|
|
80
56
|
}
|
|
81
|
-
return (_jsx(
|
|
82
|
-
|
|
83
|
-
|
|
57
|
+
return (_jsx(EditorInfoBar, { tone: "muted", testId: "mail-origin-bar", origin: "system", controls: controls, children: t(isSystemAdmin
|
|
58
|
+
? "documentDesigner.mail.origin.platform"
|
|
59
|
+
: "documentDesigner.mail.origin.system", { version: row.version }) }));
|
|
84
60
|
}
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import type { MailPreview, MailValidation } from "./mail-types.js";
|
|
3
|
-
|
|
3
|
+
import type { OwnAddress } from "./use-own-address.js";
|
|
4
|
+
export type TestMailControls = {
|
|
5
|
+
readonly pending: boolean;
|
|
6
|
+
readonly onSend: () => void;
|
|
7
|
+
readonly includeAttachments: boolean;
|
|
8
|
+
readonly onIncludeAttachments: (next: boolean) => void;
|
|
9
|
+
readonly prefixSubject: boolean;
|
|
10
|
+
readonly onPrefixSubject: (next: boolean) => void;
|
|
11
|
+
};
|
|
12
|
+
export declare function PreviewSide({ preview, validation, slug, locale, own, testMail, }: {
|
|
4
13
|
readonly preview: MailPreview | null;
|
|
5
14
|
readonly validation: MailValidation | null;
|
|
6
15
|
readonly slug: string;
|
|
7
16
|
readonly locale: string;
|
|
8
|
-
readonly
|
|
9
|
-
|
|
10
|
-
readonly onSend: () => void;
|
|
11
|
-
};
|
|
17
|
+
readonly own: OwnAddress;
|
|
18
|
+
readonly testMail: TestMailControls;
|
|
12
19
|
}): ReactNode;
|