@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
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// @runtime client
|
|
3
|
-
import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
3
|
+
import { useNav, usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
4
4
|
import { useState } from "react";
|
|
5
5
|
import { displayValue } from "./mail-draft.js";
|
|
6
6
|
import { usePreviewRecordPicker } from "./preview-record-picker.js";
|
|
7
|
-
import { RailHint, RailRow, RailSection } from "./rail.js";
|
|
7
|
+
import { RailField, RailHint, RailRow, RailSection } from "./rail.js";
|
|
8
|
+
const noop = () => undefined;
|
|
8
9
|
// Check params list names as arrays; the message templates expect one string.
|
|
9
10
|
function checkText(t, check) {
|
|
10
11
|
const params = Object.fromEntries(Object.entries(check.params ?? {}).map(([name, value]) => [
|
|
@@ -40,9 +41,17 @@ function RecordPickerLink({ slug, locale, onPick, }) {
|
|
|
40
41
|
},
|
|
41
42
|
}) })] }));
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
+
function TestMailRecipient({ own }) {
|
|
44
45
|
const t = useTranslation();
|
|
45
|
-
const
|
|
46
|
+
const nav = useNav();
|
|
47
|
+
const { Field, Input, Link } = usePrimitives();
|
|
48
|
+
const { profileScreenId } = own;
|
|
49
|
+
return (_jsxs(RailField, { children: [_jsx(Field, { id: "mail-test-recipient", label: t("documentDesigner.mail.testMail.recipient"), children: _jsx(Input, { kind: "text", id: "mail-test-recipient", name: "mail-test-recipient", testId: "mail-test-recipient", value: own.email ?? t("documentDesigner.mail.testMail.ownAddress"), onChange: noop, disabled: true }) }), profileScreenId !== null && (_jsx(Link, { href: nav.hrefFor({ screenId: profileScreenId }), onPress: () => nav.navigate({ screenId: profileScreenId }), testId: "mail-test-recipient-edit", children: t("documentDesigner.mail.testMail.changeAddress") }))] }));
|
|
50
|
+
}
|
|
51
|
+
export function PreviewSide({ preview, validation, slug, locale, own, testMail, }) {
|
|
52
|
+
const t = useTranslation();
|
|
53
|
+
const { Button, Field, Input, Text } = usePrimitives();
|
|
54
|
+
const hasAttachments = preview?.attachments.some((a) => a.kind !== "inline") === true;
|
|
46
55
|
const [picked, setPicked] = useState(null);
|
|
47
|
-
return (_jsxs("div", {
|
|
56
|
+
return (_jsxs("div", { "data-testid": "mail-preview-side", children: [_jsxs(RailSection, { title: t("documentDesigner.mail.preview.samples"), action: _jsx(RecordPickerLink, { slug: slug, locale: locale, onPick: setPicked }), children: [_jsx(SampleValues, { preview: preview, validation: validation, picked: picked }), _jsx(RailHint, { children: t("documentDesigner.mail.preview.samplesHint") })] }), _jsxs(RailSection, { title: t("documentDesigner.mail.testMail.title"), children: [_jsx(TestMailRecipient, { own: own }), _jsxs(RailField, { children: [_jsx(Field, { id: "mail-test-prefix", label: t("documentDesigner.mail.testMail.prefixSubject"), layout: "inline", children: _jsx(Input, { kind: "boolean", id: "mail-test-prefix", name: "mail-test-prefix", value: testMail.prefixSubject, onChange: testMail.onPrefixSubject }) }), hasAttachments ? (_jsx(Field, { id: "mail-test-attachments", label: t("documentDesigner.mail.testMail.attachments"), layout: "inline", children: _jsx(Input, { kind: "boolean", id: "mail-test-attachments", name: "mail-test-attachments", value: testMail.includeAttachments, onChange: testMail.onIncludeAttachments }) })) : (_jsx(Text, { variant: "muted", testId: "mail-test-no-attachments", children: t("documentDesigner.mail.testMail.noAttachments") })), _jsx(Button, { variant: "secondary", width: "full", disabled: testMail.pending, loading: testMail.pending, onClick: testMail.onSend, testId: "mail-test-send", children: t("documentDesigner.mail.testMail.send") })] }), _jsx(RailHint, { children: t("documentDesigner.mail.testMail.hint") })] }), _jsxs(RailSection, { title: t("documentDesigner.mail.preview.checks"), testId: "mail-preview-checks", children: [(preview?.checks ?? []).map((check) => (_jsx(RailRow, { testId: `mail-check-${check.code}`, symbol: check.ok ? "✓" : "!", label: checkText(t, check), bad: !check.ok }, check.code))), _jsx(RailHint, { children: t("documentDesigner.mail.preview.checksHint") })] })] }));
|
|
48
57
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
+
import type { EditorOrigin } from "./editor-header.js";
|
|
3
|
+
import { type TestMailControls } from "./mail-preview-side.js";
|
|
2
4
|
import type { MailValidation } from "./mail-types.js";
|
|
5
|
+
import type { OwnAddress } from "./use-own-address.js";
|
|
3
6
|
export type PreviewPayload = {
|
|
4
7
|
readonly slug: string;
|
|
5
8
|
readonly kind: "mail";
|
|
@@ -8,13 +11,14 @@ export type PreviewPayload = {
|
|
|
8
11
|
readonly meta: Readonly<Record<string, unknown>>;
|
|
9
12
|
readonly variableSchema: unknown;
|
|
10
13
|
};
|
|
11
|
-
export declare function MailPreviewMode({ payload, validation, slug, locale, testMail, }: {
|
|
14
|
+
export declare function MailPreviewMode({ payload, validation, slug, locale, fromName, own, controls, origin, testMail, }: {
|
|
12
15
|
readonly payload: PreviewPayload;
|
|
13
16
|
readonly validation: MailValidation | null;
|
|
14
17
|
readonly slug: string;
|
|
15
18
|
readonly locale: string;
|
|
16
|
-
readonly
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
readonly fromName: string;
|
|
20
|
+
readonly own: OwnAddress;
|
|
21
|
+
readonly controls: ReactNode;
|
|
22
|
+
readonly origin: EditorOrigin | null;
|
|
23
|
+
readonly testMail: TestMailControls;
|
|
20
24
|
}): ReactNode;
|
package/dist/web/mail-preview.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// @runtime client
|
|
3
3
|
import { TemplateResolverQueries } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
4
|
import { dispatcherErrorText, usePrimitives, useQuery, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
5
5
|
import { LoadingState, ModeSwitch } from "@cosmicdrift/kumiko-renderer-web";
|
|
6
6
|
import { useState } from "react";
|
|
7
|
+
import { EditorBody, EditorInfoBar } from "./editor-shell.js";
|
|
7
8
|
import { formatKilobytes } from "./mail-draft.js";
|
|
8
9
|
import { PreviewSide } from "./mail-preview-side.js";
|
|
9
10
|
function attachmentText(t, attachment) {
|
|
@@ -16,28 +17,38 @@ function attachmentText(t, attachment) {
|
|
|
16
17
|
return `⎘ ${attachment.name} · ${t("documentDesigner.mail.preview.generatedOnSend")}`;
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
|
-
function
|
|
20
|
+
function HeadRow({ label, children, }) {
|
|
21
|
+
return (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-muted-foreground", children: label }), _jsx("span", { className: "min-w-0", children: children })] }));
|
|
22
|
+
}
|
|
23
|
+
function MailFrame({ preview, format, width, fromName, recipient, }) {
|
|
20
24
|
const t = useTranslation();
|
|
21
25
|
const attachments = preview.attachments.flatMap((a) => attachmentText(t, a) ?? []);
|
|
22
|
-
return (_jsxs("div", { className: "flex flex-col
|
|
26
|
+
return (_jsxs("div", { className: "mx-auto flex w-full max-w-[680px] flex-col border bg-card shadow-md", "data-testid": "mail-preview-frame", children: [_jsxs("div", { className: "grid grid-cols-[80px_minmax(0,1fr)] gap-y-1 border-b px-5 py-3.5 text-sm", children: [_jsx(HeadRow, { label: t("documentDesigner.mail.preview.from"), children: _jsx("span", { "data-testid": "mail-preview-from", children: fromName === "" ? t("documentDesigner.mail.props.senderDefault") : fromName }) }), recipient !== null && (_jsx(HeadRow, { label: t("documentDesigner.mail.preview.to"), children: _jsx("span", { "data-testid": "mail-preview-to", children: recipient }) })), _jsx(HeadRow, { label: t("documentDesigner.mail.meta.subject"), children: _jsx("span", { className: "font-medium", "data-testid": "mail-preview-subject", children: preview.subject ?? "" }) }), attachments.length > 0 && (_jsx(HeadRow, { label: t("documentDesigner.mail.preview.attachments"), children: _jsx("span", { className: "flex flex-col items-start gap-1", "data-testid": "mail-preview-attachments", children: attachments.map((line) => (_jsx("span", { className: "rounded border bg-muted px-2 py-0.5 text-xs", children: line }, line))) }) }))] }), format === "html" ? (_jsx("iframe", { title: t("documentDesigner.mail.preview.frameTitle"), sandbox: "", srcDoc: preview.displayHtml ?? preview.html, className: `h-[560px] border-0 bg-background ${width === "mobile" ? "w-[375px] self-center" : "w-full"}`, "data-testid": "mail-preview-iframe" })) : (_jsx("pre", { className: "h-[560px] overflow-auto whitespace-pre-wrap p-4 font-mono text-sm", "data-testid": "mail-preview-text", children: preview.text ?? "" }))] }));
|
|
27
|
+
}
|
|
28
|
+
// The preview mail goes to the example recipient of the sample values, not to the test-mail address.
|
|
29
|
+
function recipientOf(preview, own) {
|
|
30
|
+
const { recipientName: name, recipientEmail: email } = preview.sampleValues;
|
|
31
|
+
if (typeof name !== "string" || name === "")
|
|
32
|
+
return own.email;
|
|
33
|
+
return typeof email === "string" && email !== "" ? `${name} <${email}>` : name;
|
|
23
34
|
}
|
|
24
35
|
function layoutText(t, preview) {
|
|
25
|
-
return preview?.
|
|
36
|
+
return preview?.mailLayout == null
|
|
26
37
|
? t("documentDesigner.mail.preview.barPlain")
|
|
27
|
-
: t("documentDesigner.mail.preview.bar", {
|
|
38
|
+
: t("documentDesigner.mail.preview.bar", { version: preview.mailLayout.version });
|
|
28
39
|
}
|
|
29
|
-
export function MailPreviewMode({ payload, validation, slug, locale, testMail, }) {
|
|
40
|
+
export function MailPreviewMode({ payload, validation, slug, locale, fromName, own, controls, origin, testMail, }) {
|
|
30
41
|
const t = useTranslation();
|
|
31
42
|
const { Banner } = usePrimitives();
|
|
32
43
|
const [format, setFormat] = useState("html");
|
|
33
44
|
const [width, setWidth] = useState("desktop");
|
|
34
45
|
const query = useQuery(TemplateResolverQueries.preview, payload);
|
|
35
46
|
const preview = query.data;
|
|
36
|
-
return (_jsxs(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
return (_jsxs(_Fragment, { children: [_jsx(EditorInfoBar, { tone: "muted", testId: "mail-preview-bar", origin: origin, controls: controls, links: _jsxs(_Fragment, { children: [_jsx(ModeSwitch, { value: format, onChange: setFormat, ariaLabel: t("documentDesigner.mail.preview.formatSwitch"), testId: "mail-preview-format", options: [
|
|
48
|
+
{ value: "html", label: t("documentDesigner.mail.preview.html") },
|
|
49
|
+
{ value: "text", label: t("documentDesigner.mail.preview.text") },
|
|
50
|
+
] }), _jsx(ModeSwitch, { value: width, onChange: setWidth, ariaLabel: t("documentDesigner.mail.preview.widthSwitch"), testId: "mail-preview-width", options: [
|
|
51
|
+
{ value: "desktop", label: t("documentDesigner.mail.preview.desktop") },
|
|
52
|
+
{ value: "mobile", label: t("documentDesigner.mail.preview.mobile") },
|
|
53
|
+
] })] }), children: layoutText(t, preview) }), _jsx(EditorBody, { testId: "mail-editor-preview", center: _jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-3 overflow-auto px-8 py-6", children: [query.error !== null && (_jsx(Banner, { variant: "error", testId: "mail-preview-error", children: dispatcherErrorText(query.error, t) })), preview === null ? (query.error === null && _jsx(LoadingState, {})) : (_jsx(MailFrame, { preview: preview, format: format, width: width, fromName: fromName, recipient: recipientOf(preview, own) }))] }), right: _jsx(PreviewSide, { preview: preview, validation: validation, slug: slug, locale: locale, own: own, testMail: testMail }) })] }));
|
|
43
54
|
}
|
|
@@ -3,12 +3,16 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
3
3
|
import { TemplateResolverQueries } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
4
|
import { usePrimitives, useQuery, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
5
5
|
import { DEFAULT_LAYOUT_SLUG } from "../constants.js";
|
|
6
|
-
import { RailHint, RailRow, RailSection } from "./rail.js";
|
|
6
|
+
import { RailField, RailHint, RailRow, RailSection } from "./rail.js";
|
|
7
7
|
import { usageParts } from "./used-by-label.js";
|
|
8
8
|
export function layoutOptionLabel(t, layout) {
|
|
9
|
+
// A layout has no registry title; the slug would read like an id, so name it by kind.
|
|
10
|
+
const title = layout.title === layout.slug
|
|
11
|
+
? t(`documentDesigner.editor.title.${layout.kind === "mail-layout" ? "mail" : "pdf"}`)
|
|
12
|
+
: layout.title;
|
|
9
13
|
return t(layout.origin === "tenant"
|
|
10
14
|
? "documentDesigner.mail.props.layoutTenant"
|
|
11
|
-
: "documentDesigner.mail.props.layoutSystem", { title
|
|
15
|
+
: "documentDesigner.mail.props.layoutSystem", { title, version: layout.version });
|
|
12
16
|
}
|
|
13
17
|
function UsageRows({ slug }) {
|
|
14
18
|
const t = useTranslation();
|
|
@@ -24,22 +28,28 @@ function UsageRows({ slug }) {
|
|
|
24
28
|
export function AttachmentSection({ attachments, pdfTitles, canEdit, onAddPdf, onRemove, }) {
|
|
25
29
|
const t = useTranslation();
|
|
26
30
|
const { Button } = usePrimitives();
|
|
27
|
-
return (_jsxs(RailSection, { title: t("documentDesigner.mail.attachments.title"), testId: "mail-editor-attachments", action: _jsx(Button, { variant: "link", disabled: !canEdit, onClick: onAddPdf, testId: "mail-attachment-pdf-open", children: t("documentDesigner.mail.attachments.addPdf") }), children: [attachments.length === 0 &&
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
return (_jsxs(RailSection, { title: t("documentDesigner.mail.attachments.title"), testId: "mail-editor-attachments", action: _jsx(Button, { variant: "link", disabled: !canEdit, onClick: onAddPdf, testId: "mail-attachment-pdf-open", children: t("documentDesigner.mail.attachments.addPdf") }), children: [attachments.length === 0 && _jsx(RailRow, { label: t("documentDesigner.mail.attachments.none") }), attachments.map((attachment) => (_jsx(RailRow, { testId: "mail-attachment-row", symbol: "\u2398", label: attachment.kind === "pdf"
|
|
32
|
+
? t("documentDesigner.mail.attachments.pdfRow", {
|
|
33
|
+
title: pdfTitles.get(attachment.templateSlug) ?? attachment.templateSlug,
|
|
34
|
+
})
|
|
35
|
+
: attachment.kind === "file"
|
|
36
|
+
? attachment.fileName
|
|
37
|
+
: attachment.key, trailing: attachment.kind === "pdf"
|
|
38
|
+
? attachment.fileName
|
|
39
|
+
: t("documentDesigner.mail.attachments.file"), action: _jsx(Button, { variant: "danger-ghost", size: "sm", onClick: () => onRemove(attachment.key), testId: `mail-attachment-remove-${attachment.key}`, children: t("documentDesigner.mail.resource.remove") }) }, attachment.key))), _jsx(RailHint, { children: t("documentDesigner.mail.attachments.hint") })] }));
|
|
30
40
|
}
|
|
31
41
|
export function PropertiesSection({ draft, layouts, onChange, }) {
|
|
32
42
|
const t = useTranslation();
|
|
33
|
-
const { Field, Input
|
|
43
|
+
const { Field, Input } = usePrimitives();
|
|
34
44
|
const layoutValue = draft.layoutSlug === "" ? DEFAULT_LAYOUT_SLUG : draft.layoutSlug;
|
|
35
45
|
const categoryValue = draft.category === "" ? "transactional" : draft.category;
|
|
36
|
-
return (_jsxs(RailSection, { title: t("documentDesigner.mail.props.title"), testId: "mail-editor-properties", children: [
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
return (_jsxs(RailSection, { title: t("documentDesigner.mail.props.title"), testId: "mail-editor-properties", children: [_jsx(RailField, { children: _jsx(Field, { id: "mail-layout", label: t("documentDesigner.mail.props.layout"), children: _jsx(Input, { kind: "select", id: "mail-layout", name: "mail-layout", value: layoutValue, display: "dropdown", onChange: (next) => onChange({ layoutSlug: next }), options: layouts.map((layout) => ({
|
|
47
|
+
value: layout.slug,
|
|
48
|
+
label: layoutOptionLabel(t, layout),
|
|
49
|
+
})) }) }) }), _jsx(RailField, { children: _jsx(Field, { id: "mail-sender", label: t("documentDesigner.mail.props.sender"), children: _jsx(Input, { kind: "text", id: "mail-sender", name: "mail-sender", testId: "mail-sender", value: draft.senderName, onChange: (next) => onChange({ senderName: next }), placeholder: t("documentDesigner.mail.props.senderPlaceholder") }) }) }), _jsx(RailHint, { children: t("documentDesigner.mail.props.senderHint") }), _jsx(RailField, { children: _jsx(Field, { id: "mail-reply-to", label: t("documentDesigner.mail.props.replyTo"), children: _jsx(Input, { kind: "email", id: "mail-reply-to", name: "mail-reply-to", testId: "mail-reply-to", value: draft.replyTo, onChange: (next) => onChange({ replyTo: next }) }) }) }), _jsx(RailField, { children: _jsx(Field, { id: "mail-category", label: t("documentDesigner.mail.props.category"), children: _jsx(Input, { kind: "select", id: "mail-category", name: "mail-category", value: categoryValue, display: "dropdown", onChange: (next) => onChange({ category: next === "marketing" ? "marketing" : "transactional" }), options: [
|
|
50
|
+
{ value: "transactional", label: t("documentDesigner.mail.props.transactional") },
|
|
51
|
+
{ value: "marketing", label: t("documentDesigner.mail.props.marketing") },
|
|
52
|
+
] }) }) }), _jsx(RailHint, { children: t("documentDesigner.mail.props.categoryHint") })] }));
|
|
43
53
|
}
|
|
44
54
|
export function UsageSection({ slug }) {
|
|
45
55
|
const t = useTranslation();
|
package/dist/web/mail-types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ValidationIssue } from "./layout-types.js";
|
|
2
|
+
import type { ServedVersionView } from "./types.js";
|
|
2
3
|
export type PlaceholderType = "text" | "money" | "date" | "link" | "list";
|
|
3
4
|
export type MailVariable = {
|
|
4
5
|
readonly type: PlaceholderType;
|
|
@@ -52,9 +53,10 @@ export type MailPreview = {
|
|
|
52
53
|
readonly displayHtml?: string;
|
|
53
54
|
readonly text: string | null;
|
|
54
55
|
readonly attachments: readonly PreviewAttachment[];
|
|
55
|
-
readonly
|
|
56
|
+
readonly mailLayout?: {
|
|
56
57
|
readonly slug: string;
|
|
57
58
|
readonly scope: string;
|
|
59
|
+
readonly version: number;
|
|
58
60
|
} | null;
|
|
59
61
|
readonly sampleValues: Readonly<Record<string, unknown>>;
|
|
60
62
|
readonly checks: readonly PreviewCheck[];
|
|
@@ -80,6 +82,7 @@ export type TemplateRowView = {
|
|
|
80
82
|
readonly linkedResources: unknown;
|
|
81
83
|
readonly meta: unknown;
|
|
82
84
|
readonly modifiedAt: string | null;
|
|
85
|
+
readonly active?: ServedVersionView | null;
|
|
83
86
|
};
|
|
84
87
|
export type MailCategory = "" | "transactional" | "marketing";
|
|
85
88
|
export type MailDraft = {
|
|
@@ -87,6 +90,8 @@ export type MailDraft = {
|
|
|
87
90
|
readonly previewText: string;
|
|
88
91
|
readonly layoutSlug: string;
|
|
89
92
|
readonly category: MailCategory;
|
|
93
|
+
readonly senderName: string;
|
|
94
|
+
readonly replyTo: string;
|
|
90
95
|
readonly body: string;
|
|
91
96
|
};
|
|
92
97
|
export type LinkedResourceView = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// @runtime client
|
|
3
3
|
import { formatWhen, useAppFeatures, useQuery, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
4
4
|
import { StatusBadge } from "@cosmicdrift/kumiko-renderer-web";
|
|
5
|
-
import { RailRow, RailSection } from "./rail.js";
|
|
5
|
+
import { RailHint, RailRow, RailSection } from "./rail.js";
|
|
6
6
|
import { asRowStatus, statusLabel, statusTone } from "./status-label.js";
|
|
7
7
|
const AUDIT_FEATURE = "audit";
|
|
8
8
|
const AUDIT_LIST_QUERY = "audit:query:list";
|
|
@@ -39,5 +39,5 @@ export function VersionsSection({ rowId, currentVersion, currentStatus, }) {
|
|
|
39
39
|
if (rowId === null || !features.some((feature) => feature.featureName === AUDIT_FEATURE)) {
|
|
40
40
|
return null;
|
|
41
41
|
}
|
|
42
|
-
return (
|
|
42
|
+
return (_jsxs(RailSection, { title: t("documentDesigner.mail.versions.title"), testId: "mail-editor-versions", children: [_jsx(VersionRows, { rowId: rowId, currentVersion: currentVersion, currentStatus: currentStatus }), _jsx(RailHint, { children: t("documentDesigner.mail.versions.hint") })] }));
|
|
43
43
|
}
|
package/dist/web/origin-bar.d.ts
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { LayoutKind, LayoutSourceView, TenantOverrideCounts } from "./layout-types.js";
|
|
2
|
+
import type { LayoutKind, LayoutSourceView, TemplateDiff, TenantOverrideCounts } from "./layout-types.js";
|
|
3
|
+
export declare function DiffView({ data, mode, }: {
|
|
4
|
+
readonly data: TemplateDiff;
|
|
5
|
+
readonly mode?: "diff" | "full";
|
|
6
|
+
}): ReactNode;
|
|
3
7
|
export declare function DiffPanel({ tenantId }: {
|
|
4
8
|
readonly tenantId: string;
|
|
5
9
|
}): ReactNode;
|
|
6
|
-
export declare function
|
|
10
|
+
export declare function DiffDrawer({ tenantId, onClose, }: {
|
|
11
|
+
readonly tenantId: string;
|
|
12
|
+
readonly onClose: () => void;
|
|
13
|
+
}): ReactNode;
|
|
14
|
+
export declare function OriginBar({ isSystemAdmin, source, kind, userCount, overrides, onReset, onResetError, }: {
|
|
7
15
|
readonly isSystemAdmin: boolean;
|
|
8
16
|
readonly source: LayoutSourceView;
|
|
9
17
|
readonly kind: LayoutKind;
|
|
10
18
|
readonly userCount: number;
|
|
11
19
|
readonly overrides: TenantOverrideCounts | null;
|
|
12
20
|
readonly onReset: () => void;
|
|
21
|
+
readonly onResetError: (message: string) => void;
|
|
13
22
|
}): ReactNode;
|
package/dist/web/origin-bar.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// @runtime client
|
|
3
3
|
import { TemplateResolverHandlers, TemplateResolverQueries, } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
|
-
import { useMutation, usePrimitives, useQuery, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
5
|
-
import { EmptyState, LoadingState } from "@cosmicdrift/kumiko-renderer-web";
|
|
4
|
+
import { dispatcherErrorText, useMutation, usePrimitives, useQuery, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
5
|
+
import { EmptyState, ErrorState, LoadingState } from "@cosmicdrift/kumiko-renderer-web";
|
|
6
6
|
import { useState } from "react";
|
|
7
|
+
import { originOf } from "./editor-header.js";
|
|
8
|
+
import { EditorInfoBar } from "./editor-shell.js";
|
|
9
|
+
import { draftBesideActiveSentence } from "./status-label.js";
|
|
7
10
|
const DIFF_MARK = {
|
|
8
11
|
same: " ",
|
|
9
12
|
added: "+",
|
|
@@ -19,18 +22,36 @@ function metaValueText(value) {
|
|
|
19
22
|
return "–";
|
|
20
23
|
return typeof value === "string" ? value : JSON.stringify(value);
|
|
21
24
|
}
|
|
22
|
-
|
|
25
|
+
// "full" shows the tenant's version as it reads (everything but the removed system blocks).
|
|
26
|
+
export function DiffView({ data, mode = "diff", }) {
|
|
23
27
|
const t = useTranslation();
|
|
24
|
-
|
|
25
|
-
id: tenantId,
|
|
26
|
-
});
|
|
27
|
-
if (loading || data === null)
|
|
28
|
-
return _jsx(LoadingState, {});
|
|
29
|
-
if (data.identical)
|
|
28
|
+
if (mode === "diff" && data.identical) {
|
|
30
29
|
return _jsx(EmptyState, { title: t("documentDesigner.editor.diff.identical") });
|
|
30
|
+
}
|
|
31
31
|
// Diff blocks have no identity beyond their position.
|
|
32
|
-
const entries = data.blocks
|
|
33
|
-
|
|
32
|
+
const entries = data.blocks
|
|
33
|
+
.map((block, position) => ({ block, key: `block-${position}` }))
|
|
34
|
+
.filter(({ block }) => mode === "diff" || block.change !== "removed");
|
|
35
|
+
return (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("ul", { className: "flex flex-col gap-1", "data-testid": "document-designer-diff", children: entries.map(({ block, key }) => (_jsxs("li", { "data-change": block.change, className: `whitespace-pre-wrap rounded px-2 py-1 font-mono text-xs ${DIFF_TONE[block.change]}`, children: [mode === "diff" && `${DIFF_MARK[block.change]} `, block.content] }, key))) }), mode === "diff" && data.meta.length > 0 && (_jsx("ul", { className: "flex flex-col gap-1", "data-testid": "document-designer-diff-meta", children: data.meta.map((entry) => (_jsxs("li", { className: "rounded bg-muted px-2 py-1 font-mono text-xs", children: [entry.key, ": ", metaValueText(entry.system), " \u2192 ", metaValueText(entry.tenant)] }, entry.key))) }))] }));
|
|
36
|
+
}
|
|
37
|
+
export function DiffPanel({ tenantId }) {
|
|
38
|
+
const { data, loading, error, refetch } = useQuery(TemplateResolverQueries.diffToSystem, { id: tenantId });
|
|
39
|
+
if (error !== null)
|
|
40
|
+
return _jsx(ErrorState, { error: error, onRetry: () => void refetch() });
|
|
41
|
+
if (loading || data === null)
|
|
42
|
+
return _jsx(LoadingState, {});
|
|
43
|
+
return _jsx(DiffView, { data: data });
|
|
44
|
+
}
|
|
45
|
+
export function DiffDrawer({ tenantId, onClose, }) {
|
|
46
|
+
const t = useTranslation();
|
|
47
|
+
const { Drawer, Modal } = usePrimitives();
|
|
48
|
+
const title = t("documentDesigner.editor.diff.title");
|
|
49
|
+
const body = _jsx(DiffPanel, { tenantId: tenantId });
|
|
50
|
+
const onOpenChange = (open) => {
|
|
51
|
+
if (!open)
|
|
52
|
+
onClose();
|
|
53
|
+
};
|
|
54
|
+
return Drawer === undefined ? (_jsx(Modal, { open: true, onOpenChange: onOpenChange, title: title, children: body })) : (_jsx(Drawer, { open: true, onOpenChange: onOpenChange, title: title, children: body }));
|
|
34
55
|
}
|
|
35
56
|
function originText(t, args) {
|
|
36
57
|
const { isSystemAdmin, source, kind, userCount, overrides } = args;
|
|
@@ -60,23 +81,24 @@ function originText(t, args) {
|
|
|
60
81
|
? t("documentDesigner.editor.origin.none")
|
|
61
82
|
: t("documentDesigner.editor.origin.system");
|
|
62
83
|
}
|
|
63
|
-
export function OriginBar({ isSystemAdmin, source, kind, userCount, overrides, onReset, }) {
|
|
84
|
+
export function OriginBar({ isSystemAdmin, source, kind, userCount, overrides, onReset, onResetError, }) {
|
|
64
85
|
const t = useTranslation();
|
|
65
|
-
const { Button, Dialog
|
|
86
|
+
const { Button, Dialog } = usePrimitives();
|
|
66
87
|
const [diffOpen, setDiffOpen] = useState(false);
|
|
67
88
|
const [resetOpen, setResetOpen] = useState(false);
|
|
68
89
|
const resetToSystem = useMutation(TemplateResolverHandlers.resetToSystem);
|
|
69
90
|
const { tenant, system } = source;
|
|
91
|
+
const besideActive = isSystemAdmin || tenant === null ? null : draftBesideActiveSentence(t, tenant);
|
|
70
92
|
const canDiff = tenant !== null && system?.contentFormat === "html";
|
|
71
93
|
const canReset = tenant !== null && system !== null && !isSystemAdmin;
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
94
|
+
const links = (_jsxs(_Fragment, { children: [canDiff && (_jsx(Button, { variant: "link", onClick: () => setDiffOpen(true), testId: "document-designer-diff-open", children: t("documentDesigner.editor.diff.open") })), canReset && (_jsx(Button, { variant: "link", onClick: () => setResetOpen(true), testId: "document-designer-reset-open", children: t("documentDesigner.editor.reset.open") }))] }));
|
|
95
|
+
return (_jsxs(EditorInfoBar, { tone: tenant !== null ? "active" : "muted", testId: "document-designer-origin-bar", origin: originOf(tenant === null, system !== null), links: links, overlays: _jsxs(_Fragment, { children: [diffOpen && tenant !== null && (_jsx(DiffDrawer, { tenantId: tenant.id, onClose: () => setDiffOpen(false) })), _jsx(Dialog, { open: resetOpen, onOpenChange: setResetOpen, title: t("documentDesigner.editor.reset.title"), description: t("documentDesigner.editor.reset.description"), confirmLabel: t("documentDesigner.editor.reset.confirm"), variant: "danger", onConfirm: async () => {
|
|
96
|
+
if (tenant === null)
|
|
97
|
+
return;
|
|
98
|
+
const result = await resetToSystem.mutate({ id: tenant.id });
|
|
99
|
+
if (result.isSuccess)
|
|
100
|
+
onReset();
|
|
101
|
+
else
|
|
102
|
+
onResetError(dispatcherErrorText(result.error, t));
|
|
103
|
+
} })] }), children: [originText(t, { isSystemAdmin, source, kind, userCount, overrides }), besideActive !== null && ` ${besideActive}`] }));
|
|
82
104
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
2
|
import { type TenantOverrides } from "./types.js";
|
|
3
3
|
export type OverridesDrawerProps = {
|
|
4
4
|
readonly open: boolean;
|
|
5
5
|
readonly onOpenChange: (open: boolean) => void;
|
|
6
6
|
readonly slug: string;
|
|
7
|
+
readonly locale: string;
|
|
7
8
|
readonly overrides: TenantOverrides | null;
|
|
8
9
|
};
|
|
9
|
-
export declare function OverridesDrawer({ open, onOpenChange, slug, overrides, }: OverridesDrawerProps): ReactNode;
|
|
10
|
+
export declare function OverridesDrawer({ open, onOpenChange, slug, locale, overrides, }: OverridesDrawerProps): ReactNode;
|
|
@@ -1,19 +1,95 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// @runtime client
|
|
3
3
|
import { TemplateResolverQueries } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
|
-
import { useQuery, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
5
|
-
import { Drawer } from "@cosmicdrift/kumiko-renderer-web";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
import { formatWhen, usePrimitives, useQuery, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
5
|
+
import { Drawer, EmptyState, LoadingState } from "@cosmicdrift/kumiko-renderer-web";
|
|
6
|
+
import { useMemo, useState } from "react";
|
|
7
|
+
import { DiffView } from "./origin-bar.js";
|
|
8
|
+
import { DOCUMENT_PDF_KIND, } from "./types.js";
|
|
9
|
+
const DRAWER_WIDTH = 960;
|
|
10
|
+
function TenantDetail({ slug, locale, view, onBack, }) {
|
|
11
|
+
const t = useTranslation();
|
|
12
|
+
const { Button } = usePrimitives();
|
|
13
|
+
const detail = useQuery(TemplateResolverQueries.tenantOverrides, {
|
|
14
|
+
slug,
|
|
15
|
+
kind: DOCUMENT_PDF_KIND,
|
|
16
|
+
locale,
|
|
17
|
+
diffTenantId: view.row.tenantId,
|
|
18
|
+
});
|
|
19
|
+
return (_jsxs("div", { className: "flex flex-col gap-3", "data-testid": "document-designer-overrides-detail", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("h3", { className: "m-0 text-sm font-semibold", children: t("documentDesigner.overrides.detail.version", {
|
|
20
|
+
tenant: view.row.tenantName,
|
|
21
|
+
version: view.row.version,
|
|
22
|
+
}) }), _jsx(Button, { variant: "link", onClick: onBack, testId: "document-designer-overrides-back", children: t("documentDesigner.overrides.back") })] }), detail.data?.diff == null ? (_jsx(LoadingState, {})) : (_jsx(DiffView, { data: detail.data.diff, mode: view.mode }))] }));
|
|
9
23
|
}
|
|
10
|
-
export function OverridesDrawer({ open, onOpenChange, slug, overrides, }) {
|
|
24
|
+
export function OverridesDrawer({ open, onOpenChange, slug, locale, overrides, }) {
|
|
11
25
|
const t = useTranslation();
|
|
26
|
+
const { DataTable } = usePrimitives();
|
|
27
|
+
const [view, setView] = useState(null);
|
|
12
28
|
// Only asked while the drawer is open; the answer is not needed for the editor itself.
|
|
13
29
|
const usage = useQuery(TemplateResolverQueries.usage, { slug, kind: DOCUMENT_PDF_KIND }, { enabled: open });
|
|
14
|
-
const active = overrides?.activeOverrides ?? 0;
|
|
15
|
-
const draft = overrides?.draftOverrides ?? 0;
|
|
16
|
-
const total = overrides?.totalTenants ?? null;
|
|
17
30
|
const usedBy = usage.data?.usedBy ?? [];
|
|
18
|
-
|
|
31
|
+
const tenantRows = overrides?.rows ?? [];
|
|
32
|
+
const columns = useMemo(() => [
|
|
33
|
+
{
|
|
34
|
+
field: "tenantName",
|
|
35
|
+
label: t("documentDesigner.overrides.column.tenant"),
|
|
36
|
+
type: "text",
|
|
37
|
+
sortable: false,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
field: "status",
|
|
41
|
+
label: t("documentDesigner.overrides.column.status"),
|
|
42
|
+
type: "select",
|
|
43
|
+
sortable: false,
|
|
44
|
+
optionLabels: {
|
|
45
|
+
active: t("documentDesigner.overrides.status.active"),
|
|
46
|
+
draft: t("documentDesigner.overrides.status.draft"),
|
|
47
|
+
},
|
|
48
|
+
optionTones: { active: "ok", draft: "warn" },
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
field: "versionLabel",
|
|
52
|
+
label: t("documentDesigner.overrides.column.version"),
|
|
53
|
+
type: "text",
|
|
54
|
+
sortable: false,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
field: "changed",
|
|
58
|
+
label: t("documentDesigner.overrides.column.changed"),
|
|
59
|
+
type: "text",
|
|
60
|
+
sortable: false,
|
|
61
|
+
},
|
|
62
|
+
], [t]);
|
|
63
|
+
const rows = useMemo(() => tenantRows.map((row) => ({
|
|
64
|
+
id: row.tenantId,
|
|
65
|
+
values: {
|
|
66
|
+
tenantName: row.tenantName,
|
|
67
|
+
status: row.status,
|
|
68
|
+
versionLabel: `v${row.version}`,
|
|
69
|
+
changed: formatWhen(row.changedAt),
|
|
70
|
+
},
|
|
71
|
+
})), [tenantRows]);
|
|
72
|
+
const openRow = (row, mode) => {
|
|
73
|
+
const found = tenantRows.find((candidate) => candidate.tenantId === row.id);
|
|
74
|
+
if (found !== undefined)
|
|
75
|
+
setView({ row: found, mode });
|
|
76
|
+
};
|
|
77
|
+
return (_jsx(Drawer, { open: open, onOpenChange: (next) => {
|
|
78
|
+
if (!next)
|
|
79
|
+
setView(null);
|
|
80
|
+
onOpenChange(next);
|
|
81
|
+
}, title: t("documentDesigner.overrides.title"), width: DRAWER_WIDTH, testId: "document-designer-overrides-drawer", children: _jsx("div", { className: "flex flex-col gap-5", children: view !== null ? (_jsx(TenantDetail, { slug: slug, locale: locale, view: view, onBack: () => setView(null) })) : (_jsxs(_Fragment, { children: [tenantRows.length > 0 && (_jsx("p", { className: "m-0 text-sm text-muted-foreground", "data-testid": "overrides-summary", children: t("documentDesigner.overrides.summary", { count: tenantRows.length }) })), tenantRows.length === 0 ? (_jsx(EmptyState, { title: t("documentDesigner.overrides.empty") })) : (_jsx(DataTable, { columns: columns, rows: rows, rowActionMode: "inline", rowActions: [
|
|
82
|
+
{
|
|
83
|
+
id: "diff",
|
|
84
|
+
label: t("documentDesigner.overrides.action.diff"),
|
|
85
|
+
style: "secondary",
|
|
86
|
+
onTrigger: (row) => openRow(row, "diff"),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: "open",
|
|
90
|
+
label: t("documentDesigner.overrides.action.open"),
|
|
91
|
+
style: "secondary",
|
|
92
|
+
onTrigger: (row) => openRow(row, "full"),
|
|
93
|
+
},
|
|
94
|
+
], testId: "document-designer-overrides-table" })), _jsxs("section", { className: "flex flex-col gap-2", children: [_jsx("h3", { className: "m-0 text-sm font-semibold", children: t("documentDesigner.overrides.usedBy") }), usedBy.length === 0 ? (_jsx("p", { className: "m-0 text-sm text-muted-foreground", children: t("documentDesigner.overrides.usedByEmpty") })) : (_jsx("ul", { className: "m-0 flex list-none flex-col gap-1 p-0 font-mono text-xs", children: usedBy.map((entry) => (_jsx("li", { children: entry.qualifiedName }, `${entry.qualifiedName}:${entry.role}`))) }))] })] })) }) }));
|
|
19
95
|
}
|
|
@@ -13,6 +13,8 @@ export type OverviewRowView = {
|
|
|
13
13
|
readonly locales: readonly string[];
|
|
14
14
|
readonly status: "draft" | "active" | "archived";
|
|
15
15
|
readonly version: number;
|
|
16
|
+
/** An open draft beside the served version, never another tenant's. */
|
|
17
|
+
readonly draftVersion: number | null;
|
|
16
18
|
readonly systemVersion: number | null;
|
|
17
19
|
readonly modifiedAt: string;
|
|
18
20
|
readonly usedBy: readonly OverviewUsageView[];
|
package/dist/web/overview-row.js
CHANGED
|
@@ -21,7 +21,7 @@ function usageViewOf(value) {
|
|
|
21
21
|
const stringOr = (value, fallback) => typeof value === "string" ? value : fallback;
|
|
22
22
|
const numberOr = (value, fallback) => typeof value === "number" ? value : fallback;
|
|
23
23
|
export function parseOverviewRow(row) {
|
|
24
|
-
const { id, title, layoutSlug, slug, kind, origin, locales, status, version, systemVersion, modifiedAt, usedBy, usedByCount, } = row;
|
|
24
|
+
const { id, title, layoutSlug, slug, kind, origin, locales, status, version, draftVersion, systemVersion, modifiedAt, usedBy, usedByCount, } = row;
|
|
25
25
|
if (typeof slug !== "string" || !isTemplateKind(kind) || !isStatus(status))
|
|
26
26
|
return null;
|
|
27
27
|
if (origin !== "tenant" && origin !== "system")
|
|
@@ -39,6 +39,7 @@ export function parseOverviewRow(row) {
|
|
|
39
39
|
: [],
|
|
40
40
|
status,
|
|
41
41
|
version: numberOr(version, 0),
|
|
42
|
+
draftVersion: numberOr(draftVersion, null),
|
|
42
43
|
systemVersion: numberOr(systemVersion, null),
|
|
43
44
|
modifiedAt: stringOr(modifiedAt, ""),
|
|
44
45
|
usedBy: usages,
|
|
@@ -14,5 +14,6 @@ export type PageCanvasProps = {
|
|
|
14
14
|
readonly variableNames: readonly string[];
|
|
15
15
|
readonly resourcePreviews: Readonly<Record<string, DocumentResourcePreview>>;
|
|
16
16
|
readonly onEditorReady: (handle: DocumentEditorHandle) => void;
|
|
17
|
+
readonly resourceSrc?: (key: string) => string | undefined;
|
|
17
18
|
};
|
|
18
19
|
export declare function PageCanvas(props: PageCanvasProps): ReactNode;
|