@cosmicdrift/kumiko-document-designer 0.377.0 → 0.379.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/feature.d.ts +6 -2
- package/dist/feature.js +10 -10
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/list-definition.js +14 -2
- package/dist/web/cells.js +3 -4
- package/dist/web/client-plugin.js +3 -4
- package/dist/{constants.d.ts → web/constants.d.ts} +1 -0
- package/dist/{constants.js → web/constants.js} +2 -1
- package/dist/web/default-translations.d.ts +2 -0
- package/dist/web/default-translations.js +218 -0
- package/dist/web/editor-header.js +1 -2
- package/dist/web/editor-locale.js +1 -2
- package/dist/web/editor-shell.js +8 -4
- package/dist/web/errors.js +0 -1
- package/dist/web/field-issue-text.js +0 -1
- package/dist/web/file-name.js +0 -1
- package/dist/web/i18n.d.ts +6 -2
- package/dist/web/i18n.js +873 -219
- package/dist/web/index.d.ts +1 -1
- package/dist/web/index.js +1 -2
- package/dist/web/layout-editor-body.js +6 -6
- package/dist/web/layout-editor.js +1 -2
- package/dist/web/layout-html.js +0 -1
- package/dist/web/layout-meta.js +0 -1
- package/dist/web/layout-rails.js +3 -3
- package/dist/web/layout-users.js +1 -2
- package/dist/web/mail-dialogs.js +0 -1
- package/dist/web/mail-draft.js +4 -3
- package/dist/web/mail-edit-center.js +0 -1
- package/dist/web/mail-editor-body.js +7 -4
- package/dist/web/mail-editor.js +1 -2
- package/dist/web/mail-footer.d.ts +1 -1
- package/dist/web/mail-footer.js +2 -2
- package/dist/web/mail-left-rail.d.ts +2 -1
- package/dist/web/mail-left-rail.js +2 -3
- package/dist/web/mail-missing-locale.js +0 -1
- package/dist/web/mail-origin-bar.js +0 -1
- package/dist/web/mail-pdf-link-modal.js +5 -2
- package/dist/web/mail-preview-side.d.ts +2 -0
- package/dist/web/mail-preview-side.js +3 -2
- package/dist/web/mail-preview.js +1 -2
- package/dist/web/mail-right-rail.js +1 -2
- package/dist/web/mail-types.d.ts +4 -0
- package/dist/web/mail-upload-modal.js +0 -1
- package/dist/web/mail-versions.js +0 -1
- package/dist/web/origin-bar.js +2 -3
- package/dist/web/overrides-drawer.js +0 -1
- package/dist/web/overview-row.d.ts +4 -1
- package/dist/web/overview-row.js +3 -1
- package/dist/web/page-canvas.d.ts +1 -0
- package/dist/web/page-canvas.js +1 -2
- package/dist/web/pdf-left-rail.d.ts +2 -1
- package/dist/web/pdf-left-rail.js +2 -3
- package/dist/web/pdf-meta.js +0 -1
- package/dist/web/pdf-template-editor.js +40 -7
- package/dist/web/preview-record-picker.js +0 -1
- package/dist/web/rail.js +8 -4
- package/dist/web/settings-panel.js +0 -1
- package/dist/web/template-rows.js +0 -1
- package/dist/web/types.d.ts +1 -1
- package/dist/web/use-files-capability.d.ts +1 -0
- package/dist/web/use-files-capability.js +7 -0
- package/dist/web/use-mail-persistence.js +0 -1
- package/dist/web/use-overview-rows.js +0 -1
- package/dist/web/use-own-address.js +1 -2
- package/dist/web/use-settled-value.js +0 -1
- package/dist/web/use-test-mail.d.ts +6 -0
- package/dist/web/use-test-mail.js +8 -4
- package/dist/web/used-by-label.d.ts +1 -0
- package/dist/web/used-by-label.js +10 -5
- package/package.json +11 -9
- package/src/changes.json +43 -0
- package/src/web/styles.css +12 -0
- package/dist/i18n.d.ts +0 -6
- package/dist/i18n.js +0 -836
- package/dist/web/pdf-preview.d.ts +0 -4
- package/dist/web/pdf-preview.js +0 -112
package/dist/web/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { type DocumentDesignerClientOptions, documentDesignerClient, PdfTemplateEditorScreen, } from "./client-plugin.tsx";
|
|
2
2
|
export { composeLayoutHtml, type LayoutRegionValues } from "./compose-layout.ts";
|
|
3
|
-
export { documentDesignerTranslations } from "./
|
|
3
|
+
export { documentDesignerTranslations } from "./default-translations.ts";
|
|
4
4
|
export { PdfTemplateEditor, type PdfTemplateEditorProps } from "./pdf-template-editor.tsx";
|
|
5
5
|
export { type PreviewRecordPicker, type PreviewRecordPickerProps, PreviewRecordPickerProvider, } from "./preview-record-picker.tsx";
|
|
6
6
|
export { usageLabel, usedByLabel } from "./used-by-label.ts";
|
package/dist/web/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// @runtime client
|
|
2
1
|
export { documentDesignerClient, PdfTemplateEditorScreen, } from "./client-plugin.js";
|
|
3
2
|
export { composeLayoutHtml } from "./compose-layout.js";
|
|
4
|
-
export { documentDesignerTranslations } from "./
|
|
3
|
+
export { documentDesignerTranslations } from "./default-translations.js";
|
|
5
4
|
export { PdfTemplateEditor } from "./pdf-template-editor.js";
|
|
6
5
|
export { PreviewRecordPickerProvider, } from "./preview-record-picker.js";
|
|
7
6
|
export { usageLabel, usedByLabel } from "./used-by-label.js";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { DocumentContentEditor, MAIL_DOCUMENT_BLOCKS, PDF_DOCUMENT_BLOCKS, TemplateResolverHandlers, TemplateResolverQueries, } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
3
|
import { dispatcherErrorText, useMutation, usePrimitives, useQuery, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
4
|
+
import { pluralKey } from "@cosmicdrift/kumiko-renderer/headless";
|
|
5
5
|
import { cn, ModeSwitch, TextareaField } from "@cosmicdrift/kumiko-renderer/web";
|
|
6
6
|
import { useState } from "react";
|
|
7
|
-
import { BRANDING_QUERY_QN } from "../constants.js";
|
|
8
7
|
import { composeLayoutHtml } from "./compose-layout.js";
|
|
8
|
+
import { BRANDING_QUERY_QN } from "./constants.js";
|
|
9
9
|
import { EditorHeader } from "./editor-header.js";
|
|
10
10
|
import { EditorBody, EditorFooter, EditorFrame } from "./editor-shell.js";
|
|
11
11
|
import { fileUrl } from "./file-name.js";
|
|
@@ -137,14 +137,14 @@ export function LayoutEditorBody({ kind, slug, isSystemAdmin, source, overviewRo
|
|
|
137
137
|
? { tone: "bad", text: issueText(t, blockingIssue) }
|
|
138
138
|
: {
|
|
139
139
|
tone: "ok",
|
|
140
|
-
text: t(isMail
|
|
140
|
+
text: t(pluralKey(isMail
|
|
141
141
|
? "documentDesigner.editor.footer.okMail"
|
|
142
|
-
: "documentDesigner.editor.footer.okPdf", { count: userCount }),
|
|
142
|
+
: "documentDesigner.editor.footer.okPdf", userCount), { count: userCount }),
|
|
143
143
|
};
|
|
144
144
|
const accentColor = branding.data?.accentColor ?? "";
|
|
145
145
|
const showAccentLine = isMail && appearanceEditable && appearance.accentLine === "branding" && isHexColor(accentColor);
|
|
146
|
-
// A layout region has no per-mail variables, so a steps block there could never show a stage.
|
|
147
|
-
const blocks = (isMail ? MAIL_DOCUMENT_BLOCKS : PDF_DOCUMENT_BLOCKS).filter((block) => block !== "steps");
|
|
146
|
+
// A layout region has no per-mail variables, so a steps block there could never show a stage and an images block no photos.
|
|
147
|
+
const blocks = (isMail ? MAIL_DOCUMENT_BLOCKS : PDF_DOCUMENT_BLOCKS).filter((block) => block !== "steps" && block !== "images");
|
|
148
148
|
// The layout's "logo" resource is the branding logo, so the editor shows it instead of a chip.
|
|
149
149
|
const logoFile = branding.data?.logoFile ?? "";
|
|
150
150
|
const resourcePreviews = logoFile === ""
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { TemplateResolverQueries } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
3
|
import { useNav, useQuery, useUserRoles } from "@cosmicdrift/kumiko-renderer";
|
|
5
4
|
import { ErrorState, LoadingState } from "@cosmicdrift/kumiko-renderer/web";
|
|
6
5
|
import { useState } from "react";
|
|
7
|
-
import { DEFAULT_LAYOUT_SLUG } from "
|
|
6
|
+
import { DEFAULT_LAYOUT_SLUG } from "./constants.js";
|
|
8
7
|
import { LayoutEditorBody } from "./layout-editor-body.js";
|
|
9
8
|
import { layoutUserKind } from "./layout-users.js";
|
|
10
9
|
import { parseOverviewRow } from "./overview-row.js";
|
package/dist/web/layout-html.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { cn } from "@cosmicdrift/kumiko-renderer/web";
|
|
4
3
|
import { createElement, useMemo } from "react";
|
|
5
4
|
// Layout regions arrive server-sanitised, but the preview still renders them
|
package/dist/web/layout-meta.js
CHANGED
package/dist/web/layout-rails.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { useAppFeatures, useNav, usePrimitives, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
4
|
-
import { BRANDING_SCREEN_QN } from "
|
|
3
|
+
import { BRANDING_SCREEN_QN } from "./constants.js";
|
|
5
4
|
import { isHexColor, LAYOUT_ACCENT_LINES, LAYOUT_BUTTON_STYLES, LAYOUT_FONTS, LAYOUT_WIDTHS, } from "./layout-meta.js";
|
|
6
5
|
import { layoutUsers } from "./layout-users.js";
|
|
7
6
|
import { PLACEHOLDER_SYMBOL, SYSTEM_PLACEHOLDER_TYPE, systemPlaceholderLabel, } from "./mail-draft.js";
|
|
8
7
|
import { RailField, RailHint, RailRow, RailSection } from "./rail.js";
|
|
9
8
|
import { statusLabel } from "./status-label.js";
|
|
9
|
+
import { templateTitleText } from "./used-by-label.js";
|
|
10
10
|
const BRANDING_SCREEN_SHORT_ID = BRANDING_SCREEN_QN.slice(BRANDING_SCREEN_QN.lastIndexOf(":") + 1);
|
|
11
11
|
function logoName(logoUrl) {
|
|
12
12
|
const path = logoUrl.split("?")[0] ?? "";
|
|
@@ -90,7 +90,7 @@ export function AppearanceRail({ appearance, onChange, fromMjml, }) {
|
|
|
90
90
|
export function UsedByRail({ layoutSlug, kind, rows, }) {
|
|
91
91
|
const t = useTranslation();
|
|
92
92
|
const users = layoutUsers(rows, kind, layoutSlug);
|
|
93
|
-
return (_jsx(RailSection, { title: t("documentDesigner.usedBy.title"), testId: "document-designer-used-by", children: users.length === 0 ? (_jsx(RailHint, { children: t("documentDesigner.usedBy.none") })) : (users.map((row) => (_jsx(RailRow, { testId: `used-by-${row.id}`, label: row.title, trailing: row.origin === "system"
|
|
93
|
+
return (_jsx(RailSection, { title: t("documentDesigner.usedBy.title"), testId: "document-designer-used-by", children: users.length === 0 ? (_jsx(RailHint, { children: t("documentDesigner.usedBy.none") })) : (users.map((row) => (_jsx(RailRow, { testId: `used-by-${row.id}`, label: templateTitleText(t, row.title), trailing: row.origin === "system"
|
|
94
94
|
? t("documentDesigner.source.system")
|
|
95
95
|
: statusLabel(t, row.status, row.version) }, row.id)))) }));
|
|
96
96
|
}
|
package/dist/web/layout-users.js
CHANGED
package/dist/web/mail-dialogs.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
4
3
|
export function MailConfirmDialogs({ dialog, onClose, onDiscard, onRemove, onLeaveSave, onLeaveDiscard, }) {
|
|
5
4
|
const t = useTranslation();
|
package/dist/web/mail-draft.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { DOCUMENT_FALLBACK_CURRENCY, formatDocumentDate, formatDocumentMoney, formatDocumentPercent, } from "@cosmicdrift/kumiko-renderer/headless";
|
|
1
|
+
import { DOCUMENT_FALLBACK_CURRENCY, formatDocumentDate, formatDocumentMoney, formatDocumentPercent, pluralKey, } from "@cosmicdrift/kumiko-renderer/headless";
|
|
3
2
|
import { variableLabelText } from "./used-by-label.js";
|
|
4
3
|
export const EMPTY_DRAFT = {
|
|
5
4
|
subject: "",
|
|
@@ -167,7 +166,9 @@ export function variableLabelMap(t, variables) {
|
|
|
167
166
|
}
|
|
168
167
|
export function displayValue(t, value, type, format) {
|
|
169
168
|
if (Array.isArray(value))
|
|
170
|
-
return t("documentDesigner.mail.preview.entries",
|
|
169
|
+
return t(pluralKey("documentDesigner.mail.preview.entries", value.length), {
|
|
170
|
+
count: value.length,
|
|
171
|
+
});
|
|
171
172
|
if (type === "money") {
|
|
172
173
|
if (typeof value === "number" && format.tenantCurrency === undefined)
|
|
173
174
|
return String(value);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { DocumentContentEditor, MAIL_DOCUMENT_BLOCKS, PlaceholderLineEditor, } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
3
|
import { useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
5
4
|
const renderMailPaper = (editable) => (_jsx("div", { className: "mx-auto w-full max-w-[600px] bg-card px-10 py-7 shadow-md", children: editable }));
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { TemplateResolverHandlers, TemplateResolverQueries, } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
3
|
import { dispatcherErrorText, useMutation, usePrimitives, useQuery, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
5
4
|
import { useState } from "react";
|
|
@@ -18,12 +17,14 @@ import { MailPreviewMode } from "./mail-preview.js";
|
|
|
18
17
|
import { AttachmentSection, ChromeSection, PropertiesSection, UsageSection, } from "./mail-right-rail.js";
|
|
19
18
|
import { UploadModal } from "./mail-upload-modal.js";
|
|
20
19
|
import { VersionsSection } from "./mail-versions.js";
|
|
20
|
+
import { useFilesCapability } from "./use-files-capability.js";
|
|
21
21
|
import { useInsertTargets } from "./use-insert-targets.js";
|
|
22
22
|
import { useMailPersistence } from "./use-mail-persistence.js";
|
|
23
23
|
import { useOverviewRows } from "./use-overview-rows.js";
|
|
24
24
|
import { useOwnAddress } from "./use-own-address.js";
|
|
25
25
|
import { useSettledValue } from "./use-settled-value.js";
|
|
26
26
|
import { useTestMail } from "./use-test-mail.js";
|
|
27
|
+
import { templateTitleText } from "./used-by-label.js";
|
|
27
28
|
// The editor replaces this token with the decoded image size once the preview image loaded.
|
|
28
29
|
const SIZE_TOKEN = "{size}";
|
|
29
30
|
function uniqueValues(values) {
|
|
@@ -66,6 +67,7 @@ export function MailEditorBody(props) {
|
|
|
66
67
|
});
|
|
67
68
|
const testMail = useTestMail();
|
|
68
69
|
const own = useOwnAddress();
|
|
70
|
+
const filesAvailable = useFilesCapability();
|
|
69
71
|
const resourceRemove = useMutation(TemplateResolverHandlers.resourceRemove);
|
|
70
72
|
const layouts = useOverviewRows("mail-layout");
|
|
71
73
|
const pdfTemplates = useOverviewRows("document-pdf");
|
|
@@ -114,7 +116,7 @@ export function MailEditorBody(props) {
|
|
|
114
116
|
perform(target);
|
|
115
117
|
};
|
|
116
118
|
const resources = parseLinkedResources(row?.linkedResources);
|
|
117
|
-
const pdfTitles = new Map(pdfTemplates.rows.map((template) => [template.slug, template.title]));
|
|
119
|
+
const pdfTitles = new Map(pdfTemplates.rows.map((template) => [template.slug, templateTitleText(t, template.title)]));
|
|
118
120
|
const resourcePreviews = Object.fromEntries(resources.flatMap((resource) => resource.kind === "image"
|
|
119
121
|
? [
|
|
120
122
|
[
|
|
@@ -167,7 +169,7 @@ export function MailEditorBody(props) {
|
|
|
167
169
|
const attachments = resources.filter((resource) => resource.kind !== "image");
|
|
168
170
|
const footer = locked || validation === undefined || validation === null
|
|
169
171
|
? null
|
|
170
|
-
: footerMessage(t, validation, mode === "preview");
|
|
172
|
+
: footerMessage(t, validation, mode === "preview" && testMail.availability.canSend);
|
|
171
173
|
const fieldErrors = locked ? {} : metaFieldMessages(t, validation ?? null);
|
|
172
174
|
const publish = () => void saveAndReload(true);
|
|
173
175
|
const controls = (_jsx(EditorLocaleSwitch, { testIdPrefix: "mail", locales: props.locales, locale: locale, onLocale: (next) => leave({ kind: "locale", locale: next }) }));
|
|
@@ -175,7 +177,7 @@ export function MailEditorBody(props) {
|
|
|
175
177
|
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: () => {
|
|
176
178
|
setResetError(null);
|
|
177
179
|
void onReload();
|
|
178
|
-
}, 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, { slug: slug, draft: draft, layouts: layouts.rows, onChange: update }), _jsx(ChromeSection, { draft: draft, vocabulary: vocabulary, insert: insert, 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: {
|
|
180
|
+
}, 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, filesAvailable: filesAvailable, 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, { slug: slug, draft: draft, layouts: layouts.rows, onChange: update }), _jsx(ChromeSection, { draft: draft, vocabulary: vocabulary, insert: insert, 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: {
|
|
179
181
|
slug,
|
|
180
182
|
kind: "mail",
|
|
181
183
|
locale,
|
|
@@ -184,6 +186,7 @@ export function MailEditorBody(props) {
|
|
|
184
186
|
variableSchema,
|
|
185
187
|
...(picked !== null && { variables: picked }),
|
|
186
188
|
}, picked: picked, onPick: setPicked, validation: validation ?? null, slug: slug, locale: locale, fromName: settled.senderName, own: own, origin: origin, controls: controls, testMail: {
|
|
189
|
+
availability: testMail.availability,
|
|
187
190
|
pending: testMail.pending,
|
|
188
191
|
onSend: sendTestMail,
|
|
189
192
|
includeAttachments,
|
package/dist/web/mail-editor.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { TemplateResolverQueries } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
3
|
import { useLocale, useNav, useQuery, useTranslation, useUserRoles, } from "@cosmicdrift/kumiko-renderer";
|
|
5
4
|
import { LoadingState } from "@cosmicdrift/kumiko-renderer/web";
|
|
6
5
|
import { useState } from "react";
|
|
7
|
-
import { DEFAULT_TEMPLATE_LOCALES } from "
|
|
6
|
+
import { DEFAULT_TEMPLATE_LOCALES } from "./constants.js";
|
|
8
7
|
import { defaultLocaleOf } from "./editor-locale.js";
|
|
9
8
|
import { MailEditorBody } from "./mail-editor-body.js";
|
|
10
9
|
import { useOverviewRows } from "./use-overview-rows.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MailValidation } from "./mail-types.ts";
|
|
2
2
|
type Translate = (key: string, params?: Readonly<Record<string, unknown>>) => string;
|
|
3
|
-
export declare function footerMessage(t: Translate, validation: MailValidation,
|
|
3
|
+
export declare function footerMessage(t: Translate, validation: MailValidation, offersTestMail: boolean): {
|
|
4
4
|
readonly tone: "bad" | "warn" | "ok";
|
|
5
5
|
readonly text: string;
|
|
6
6
|
};
|
package/dist/web/mail-footer.js
CHANGED
|
@@ -10,11 +10,11 @@ function missingLabels(t, validation) {
|
|
|
10
10
|
}
|
|
11
11
|
// The footer sentence: missing required placeholders first, then other blocking issues,
|
|
12
12
|
// then hints (not-yet-uploaded resources), else "ready".
|
|
13
|
-
export function footerMessage(t, validation,
|
|
13
|
+
export function footerMessage(t, validation, offersTestMail) {
|
|
14
14
|
if (validation.missingRequired.length > 0) {
|
|
15
15
|
return {
|
|
16
16
|
tone: "bad",
|
|
17
|
-
text: t(
|
|
17
|
+
text: t(offersTestMail
|
|
18
18
|
? "documentDesigner.mail.footer.missingPreview"
|
|
19
19
|
: "documentDesigner.mail.footer.missing", { names: missingLabels(t, validation) }),
|
|
20
20
|
};
|
|
@@ -13,10 +13,11 @@ export declare function SystemPlaceholderSection({ validation, onInsert, idPrefi
|
|
|
13
13
|
/** Placeholders the layout fills in, so a template that never uses them is fine. */
|
|
14
14
|
readonly isLayoutOwned?: (name: string) => boolean;
|
|
15
15
|
}): ReactNode;
|
|
16
|
-
export declare function ResourceSection({ resources, pdfTitles, canUpload, onUpload, onInsert, onRemove, }: {
|
|
16
|
+
export declare function ResourceSection({ resources, pdfTitles, canUpload, filesAvailable, onUpload, onInsert, onRemove, }: {
|
|
17
17
|
readonly resources: readonly LinkedResourceView[];
|
|
18
18
|
readonly pdfTitles: ReadonlyMap<string, string>;
|
|
19
19
|
readonly canUpload: boolean;
|
|
20
|
+
readonly filesAvailable: boolean;
|
|
20
21
|
readonly onUpload: () => void;
|
|
21
22
|
readonly onInsert: (key: string) => void;
|
|
22
23
|
readonly onRemove: (key: string) => void;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
4
3
|
import { LAYOUT_OWNED_PLACEHOLDERS, PLACEHOLDER_SYMBOL, SYSTEM_PLACEHOLDER_TYPE, systemPlaceholderLabel, } from "./mail-draft.js";
|
|
5
4
|
import { RailHint, RailRow, RailSection } from "./rail.js";
|
|
@@ -47,10 +46,10 @@ function resourceRow(t, resource, pdfTitles) {
|
|
|
47
46
|
};
|
|
48
47
|
}
|
|
49
48
|
}
|
|
50
|
-
export function ResourceSection({ resources, pdfTitles, canUpload, onUpload, onInsert, onRemove, }) {
|
|
49
|
+
export function ResourceSection({ resources, pdfTitles, canUpload, filesAvailable, onUpload, onInsert, onRemove, }) {
|
|
51
50
|
const t = useTranslation();
|
|
52
51
|
const { Button } = usePrimitives();
|
|
53
|
-
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) => {
|
|
52
|
+
return (_jsxs(RailSection, { title: t("documentDesigner.mail.resource.title"), testId: "mail-editor-resources", action: _jsx(Button, { variant: "link", disabled: !canUpload || !filesAvailable, onClick: onUpload, testId: "mail-resource-upload-open", children: t("documentDesigner.mail.resource.upload") }), children: [!filesAvailable && _jsx(RailHint, { children: t("documentDesigner.mail.resource.noFilesFeature") }), filesAvailable && !canUpload && (_jsx(RailHint, { children: t("documentDesigner.mail.resource.saveFirst") })), resources.map((resource) => {
|
|
54
53
|
const row = resourceRow(t, resource, pdfTitles);
|
|
55
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));
|
|
56
55
|
}), _jsx(RailHint, { children: t("documentDesigner.mail.resource.hint") })] }));
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { TemplateResolverHandlers } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
3
|
import { dispatcherErrorText, useMutation, usePrimitives, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
5
4
|
import { useState } from "react";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { TemplateResolverHandlers } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
3
|
import { dispatcherErrorText, formatWhen, useMutation, usePrimitives, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
5
4
|
import { EMPTY_VALUE_PLACEHOLDER } from "@cosmicdrift/kumiko-renderer/headless";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { PlaceholderLineEditor, TemplateResolverHandlers, TemplateResolverQueries, } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
3
|
import { dispatcherErrorText, useMutation, usePrimitives, useQuery, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
5
4
|
import { EmptyState } from "@cosmicdrift/kumiko-renderer/web";
|
|
6
5
|
import { useState } from "react";
|
|
7
6
|
import { lastSlugSegment, parseJsonRecord, uniqueResourceKey } from "./mail-draft.js";
|
|
7
|
+
import { templateTitleText } from "./used-by-label.js";
|
|
8
8
|
function pdfRowIdOf(summaries, slug) {
|
|
9
9
|
const ofSlug = (summaries ?? []).filter((row) => row.slug === slug);
|
|
10
10
|
return (ofSlug.find((row) => row.scope === "tenant") ?? ofSlug[0])?.id ?? null;
|
|
@@ -42,7 +42,10 @@ function PdfLinkForm({ templates, variableLabels, templateId, takenKeys, onLinke
|
|
|
42
42
|
return (_jsxs("div", { className: "flex flex-col gap-4", children: [error !== null && (_jsx(Banner, { variant: "error", testId: "mail-pdf-link-error", children: error })), _jsx(Field, { id: "mail-pdf-template", label: t("documentDesigner.mail.pdfLink.template"), children: _jsx(Input, { kind: "select", id: "mail-pdf-template", name: "mail-pdf-template", value: slug, onChange: (next) => {
|
|
43
43
|
setSlug(next);
|
|
44
44
|
setEditedName(null);
|
|
45
|
-
}, options: templates.map((template) => ({
|
|
45
|
+
}, options: templates.map((template) => ({
|
|
46
|
+
value: template.slug,
|
|
47
|
+
label: templateTitleText(t, template.title),
|
|
48
|
+
})) }) }), _jsx(Field, { id: "mail-pdf-file-name", label: t("documentDesigner.mail.pdfLink.fileName"), description: t("documentDesigner.mail.pdfLink.fileNameHint"), children: _jsx(PlaceholderLineEditor, { id: "mail-pdf-file-name", value: fileName, onChange: setEditedName, variableLabels: variableLabels, "aria-label": t("documentDesigner.mail.pdfLink.fileName") }) }), _jsx("div", { className: "flex justify-end", children: _jsx(Button, { disabled: slug === "" || fileName === "" || linkPdf.pending, loading: linkPdf.pending, onClick: link, testId: "mail-pdf-link-confirm", children: t("documentDesigner.mail.pdfLink.confirm") }) })] }));
|
|
46
49
|
}
|
|
47
50
|
export function PdfLinkModal({ open, onOpenChange, templates, ...form }) {
|
|
48
51
|
const t = useTranslation();
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import type { MailPreview, MailValidation, PickedValues } from "./mail-types.ts";
|
|
3
3
|
import type { OwnAddress } from "./use-own-address.ts";
|
|
4
|
+
import type { TestMailAvailability } from "./use-test-mail.ts";
|
|
4
5
|
export type TestMailControls = {
|
|
6
|
+
readonly availability: TestMailAvailability;
|
|
5
7
|
readonly pending: boolean;
|
|
6
8
|
readonly onSend: () => void;
|
|
7
9
|
readonly includeAttachments: boolean;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { ConfigQueries } from "@cosmicdrift/kumiko-bundled-features/config/web";
|
|
4
3
|
import { useNav, usePrimitives, useQuery, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
5
4
|
import { DOCUMENT_FALLBACK_CURRENCY } from "@cosmicdrift/kumiko-renderer/headless";
|
|
@@ -77,5 +76,7 @@ export function PreviewSide({ preview, validation, slug, locale, own, testMail,
|
|
|
77
76
|
const t = useTranslation();
|
|
78
77
|
const { Button, Field, Input, Text } = usePrimitives();
|
|
79
78
|
const hasAttachments = preview?.attachments.some((a) => a.kind !== "inline") === true;
|
|
80
|
-
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: onPick }), children: [_jsx(SampleValues, { preview: preview, validation: validation, picked: picked, locale: locale }), _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") })] }),
|
|
79
|
+
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: onPick }), children: [_jsx(SampleValues, { preview: preview, validation: validation, picked: picked, locale: locale }), _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 || !testMail.availability.canSend, loading: testMail.pending, onClick: testMail.onSend, testId: "mail-test-send", children: t("documentDesigner.mail.testMail.send") })] }), testMail.availability.reason !== null && (_jsx(RailHint, { children: t(testMail.availability.reason === "no_channel"
|
|
80
|
+
? "documentDesigner.mail.testMail.unavailable.noChannel"
|
|
81
|
+
: "documentDesigner.mail.testMail.unavailable.noAddress") })), _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, validation), bad: !check.ok }, check.code))), _jsx(RailHint, { children: t("documentDesigner.mail.preview.checksHint") })] })] }));
|
|
81
82
|
}
|
package/dist/web/mail-preview.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { TemplateResolverQueries } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
3
|
import { dispatcherErrorText, usePrimitives, useQuery, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
5
4
|
import { LoadingState, ModeSwitch } from "@cosmicdrift/kumiko-renderer/web";
|
|
@@ -23,7 +22,7 @@ function HeadRow({ label, children, }) {
|
|
|
23
22
|
function MailFrame({ preview, format, width, fromName, recipient, }) {
|
|
24
23
|
const t = useTranslation();
|
|
25
24
|
const attachments = preview.attachments.flatMap((a) => attachmentText(t, a) ?? []);
|
|
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-[
|
|
25
|
+
return (_jsxs("div", { className: "mx-auto flex min-h-0 w-full max-w-[680px] flex-1 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: `min-h-[420px] flex-1 border-0 bg-background ${width === "mobile" ? "w-[375px] self-center" : "w-full"}`, "data-testid": "mail-preview-iframe" })) : (_jsx("pre", { className: "min-h-[420px] flex-1 overflow-auto whitespace-pre-wrap p-4 font-mono text-sm", "data-testid": "mail-preview-text", children: preview.text ?? "" }))] }));
|
|
27
26
|
}
|
|
28
27
|
// The preview mail goes to the example recipient of the sample values, not to the test-mail address.
|
|
29
28
|
function recipientOf(preview, own) {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { MAIL_TONES, PlaceholderLineEditor, TemplateResolverQueries, } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
3
|
import { useAppFeatures, useNav, usePrimitives, useQuery, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
5
4
|
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
6
|
-
import { DEFAULT_LAYOUT_SLUG, TENANT_MAIL_SENDING_SCREEN_ID } from "
|
|
5
|
+
import { DEFAULT_LAYOUT_SLUG, TENANT_MAIL_SENDING_SCREEN_ID } from "./constants.js";
|
|
7
6
|
import { labelPlaceholders } from "./mail-draft.js";
|
|
8
7
|
import { RailField, RailHint, RailRow, RailSection } from "./rail.js";
|
|
9
8
|
import { usageParts } from "./used-by-label.js";
|
package/dist/web/mail-types.d.ts
CHANGED
|
@@ -119,4 +119,8 @@ export type UpsertResponse = {
|
|
|
119
119
|
export type SendTestResponse = {
|
|
120
120
|
readonly subject: string;
|
|
121
121
|
};
|
|
122
|
+
export type SendTestStatus = {
|
|
123
|
+
readonly canSend: boolean;
|
|
124
|
+
readonly reason: "no_channel" | "no_address" | null;
|
|
125
|
+
};
|
|
122
126
|
export type PickedValues = Readonly<Record<string, unknown>>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { TemplateResolverHandlers } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
3
|
import { dispatcherErrorText, useMutation, usePrimitives, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
5
4
|
import { useRef, useState } from "react";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { formatWhen, useAppFeatures, useQuery, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
4
3
|
import { EMPTY_VALUE_PLACEHOLDER } from "@cosmicdrift/kumiko-renderer/headless";
|
|
5
4
|
import { StatusBadge } from "@cosmicdrift/kumiko-renderer/web";
|
package/dist/web/origin-bar.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { TemplateResolverHandlers, TemplateResolverQueries, } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
3
|
import { dispatcherErrorText, useMutation, usePrimitives, useQuery, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
5
|
-
import { EMPTY_VALUE_PLACEHOLDER } from "@cosmicdrift/kumiko-renderer/headless";
|
|
4
|
+
import { EMPTY_VALUE_PLACEHOLDER, pluralKey } from "@cosmicdrift/kumiko-renderer/headless";
|
|
6
5
|
import { EmptyState, ErrorState, LoadingState } from "@cosmicdrift/kumiko-renderer/web";
|
|
7
6
|
import { useState } from "react";
|
|
8
7
|
import { originOf } from "./editor-header.js";
|
|
@@ -73,7 +72,7 @@ function originText(t, args) {
|
|
|
73
72
|
}
|
|
74
73
|
const usesKey = kind === "mail-layout" ? "mails" : "pdfs";
|
|
75
74
|
if (source.tenant !== null) {
|
|
76
|
-
return t(`documentDesigner.editor.origin.tenant.${usesKey}`, {
|
|
75
|
+
return t(pluralKey(`documentDesigner.editor.origin.tenant.${usesKey}`, userCount), {
|
|
77
76
|
version: systemVersion,
|
|
78
77
|
count: userCount,
|
|
79
78
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { TemplateResolverQueries } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
3
|
import { formatWhen, usePrimitives, useQuery, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
5
4
|
import { Drawer, EmptyState, LoadingState } from "@cosmicdrift/kumiko-renderer/web";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TemplateEditorKind } from "
|
|
1
|
+
import type { TemplateEditorKind } from "./constants.ts";
|
|
2
2
|
export type OverviewUsageView = {
|
|
3
3
|
readonly feature: string;
|
|
4
4
|
readonly notification: string;
|
|
@@ -16,6 +16,9 @@ export type OverviewRowView = {
|
|
|
16
16
|
/** An open draft beside the served version, never another tenant's. */
|
|
17
17
|
readonly draftVersion: number | null;
|
|
18
18
|
readonly systemVersion: number | null;
|
|
19
|
+
/** SystemAdmin only: a stored tenant override that is not delivered. */
|
|
20
|
+
readonly lockedOverride: boolean;
|
|
21
|
+
readonly lockedOverrideI18nKey: string | null;
|
|
19
22
|
readonly modifiedAt: string;
|
|
20
23
|
readonly usedBy: readonly OverviewUsageView[];
|
|
21
24
|
readonly usedByCount: number;
|
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, draftVersion, systemVersion, modifiedAt, usedBy, usedByCount, } = row;
|
|
24
|
+
const { id, title, layoutSlug, slug, kind, origin, locales, status, version, draftVersion, systemVersion, lockedOverride, lockedOverrideI18nKey, 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")
|
|
@@ -41,6 +41,8 @@ export function parseOverviewRow(row) {
|
|
|
41
41
|
version: numberOr(version, 0),
|
|
42
42
|
draftVersion: numberOr(draftVersion, null),
|
|
43
43
|
systemVersion: numberOr(systemVersion, null),
|
|
44
|
+
lockedOverride: lockedOverride === true,
|
|
45
|
+
lockedOverrideI18nKey: typeof lockedOverrideI18nKey === "string" ? lockedOverrideI18nKey : null,
|
|
44
46
|
modifiedAt: stringOr(modifiedAt, ""),
|
|
45
47
|
usedBy: usages,
|
|
46
48
|
usedByCount: numberOr(usedByCount, usages.length),
|
|
@@ -12,6 +12,7 @@ export type PageCanvasProps = {
|
|
|
12
12
|
readonly systemVariables: readonly string[];
|
|
13
13
|
readonly systemLabels: Readonly<Record<string, string>>;
|
|
14
14
|
readonly variableNames: readonly string[];
|
|
15
|
+
readonly imageVariables: readonly string[];
|
|
15
16
|
readonly resourcePreviews: Readonly<Record<string, DocumentResourcePreview>>;
|
|
16
17
|
readonly onEditorReady: (handle: DocumentEditorHandle | null) => void;
|
|
17
18
|
readonly resourceSrc?: (key: string) => string | undefined;
|
package/dist/web/page-canvas.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { DocumentContentEditor, PDF_DEFAULT_MARGIN_MM, PDF_DOCUMENT_BLOCKS, PDF_FOOTER_MARGIN_MM, } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
3
|
import { useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
5
4
|
import { cn } from "@cosmicdrift/kumiko-renderer/web";
|
|
@@ -44,5 +43,5 @@ export function PageCanvas(props) {
|
|
|
44
43
|
}
|
|
45
44
|
}, [meta.margins, hasFooter]);
|
|
46
45
|
const renderPaper = (editable) => (_jsxs("div", { className: "flex w-full flex-col items-center gap-2", children: [_jsxs("div", { "data-testid": "document-designer-page", className: cn("flex min-h-[1000px] w-full flex-col bg-card shadow-md", PAGE_WIDTH_CLASSES[`${meta.pageFormat}-${meta.orientation}`]), children: [showLetterhead && props.regions !== null && (_jsx(HeaderBand, { html: props.regions.header, systemLabels: props.systemLabels, resourceSrc: props.resourceSrc, caption: fromLayout })), _jsxs("div", { ref: applyMargins, className: cn("flex min-h-0 flex-1 flex-col pr-(--page-margin-right) pb-(--page-margin-bottom) pl-(--page-margin-left)", !showLetterhead && "pt-(--page-margin-top)"), children: [_jsxs("div", { className: "relative my-2 min-h-0 flex-1 border border-dashed border-primary/40 p-2 pt-5", "data-testid": "document-designer-content-area", children: [_jsx("span", { className: "absolute left-2 top-0.5 text-[10px] uppercase tracking-wide text-primary", children: t("documentDesigner.editor.region.content") }), editable] }), showRegions && props.regions !== null && (_jsxs(_Fragment, { children: [_jsx(RegionBlock, { html: props.regions.signature, systemLabels: props.systemLabels, resourceSrc: props.resourceSrc, caption: regionCaption("signature") }), _jsx(RegionBlock, { html: props.regions.footer, systemLabels: props.systemLabels, resourceSrc: props.resourceSrc, caption: regionCaption("footer") })] }))] })] }), _jsx("p", { className: "text-xs text-muted-foreground", children: t("documentDesigner.canvas.caption", { format: meta.pageFormat }) })] }));
|
|
47
|
-
return (_jsx("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", "data-testid": "pdf-editor-center", children: _jsx(DocumentContentEditor, { id: "document-designer-content", value: props.content, onChange: props.onContentChange, variables: props.variableNames, readOnly: false, blocks: PDF_DOCUMENT_BLOCKS, variableLabels: props.variableLabels, systemVariables: props.systemVariables, resourcePreviews: props.resourcePreviews, chrome: "canvas", renderPaper: renderPaper, onEditorReady: props.onEditorReady }) }));
|
|
46
|
+
return (_jsx("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", "data-testid": "pdf-editor-center", children: _jsx(DocumentContentEditor, { id: "document-designer-content", value: props.content, onChange: props.onContentChange, variables: props.variableNames, imageVariables: props.imageVariables, readOnly: false, blocks: PDF_DOCUMENT_BLOCKS, variableLabels: props.variableLabels, systemVariables: props.systemVariables, resourcePreviews: props.resourcePreviews, chrome: "canvas", renderPaper: renderPaper, onEditorReady: props.onEditorReady }) }));
|
|
48
47
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
export declare function visibleSystemPlaceholders(names: readonly string[]): readonly string[];
|
|
3
|
-
export declare function PdfResourceSection({ resourceKeys, canUpload, layoutName, onInsert, onUpload, }: {
|
|
3
|
+
export declare function PdfResourceSection({ resourceKeys, canUpload, filesAvailable, layoutName, onInsert, onUpload, }: {
|
|
4
4
|
readonly resourceKeys: readonly string[];
|
|
5
5
|
readonly canUpload: boolean;
|
|
6
|
+
readonly filesAvailable: boolean;
|
|
6
7
|
readonly layoutName: string;
|
|
7
8
|
readonly onInsert: (key: string) => void;
|
|
8
9
|
readonly onUpload: (file: File) => Promise<void>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
2
|
import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
4
3
|
import { UploadZone } from "@cosmicdrift/kumiko-renderer/web";
|
|
5
4
|
import { RailField, RailHint, RailRow, RailSection } from "./rail.js";
|
|
@@ -9,8 +8,8 @@ export function visibleSystemPlaceholders(names) {
|
|
|
9
8
|
return names.filter((name) => !HIDDEN_SYSTEM_PLACEHOLDERS.has(name));
|
|
10
9
|
}
|
|
11
10
|
const IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
12
|
-
export function PdfResourceSection({ resourceKeys, canUpload, layoutName, onInsert, onUpload, }) {
|
|
11
|
+
export function PdfResourceSection({ resourceKeys, canUpload, filesAvailable, layoutName, onInsert, onUpload, }) {
|
|
13
12
|
const t = useTranslation();
|
|
14
13
|
const { Button } = usePrimitives();
|
|
15
|
-
return (_jsxs(RailSection, { title: t("documentDesigner.resources.title"), testId: "pdf-editor-resources", children: [resourceKeys.length === 0 && _jsx(RailRow, { label: t("documentDesigner.resources.empty") }), resourceKeys.map((key) => (_jsx(RailRow, { testId: `pdf-resource-${key}`, symbol: "\u25A3", label: key, onPress: () => onInsert(key) }, key))), _jsx(RailField, { children: canUpload ? (_jsx(UploadZone, { multiple: false, accept: IMAGE_TYPES, title: t("documentDesigner.resources.upload"), onUpload: onUpload, testId: "document-designer-upload" })) : (_jsx(Button, { variant: "secondary", size: "sm", disabled: true, children: t("documentDesigner.resources.upload") })) }), !canUpload && _jsx(RailHint, { children: t("documentDesigner.resources.saveFirst") }), _jsx(RailHint, { children: t("documentDesigner.placeholders.layoutHint", { layout: layoutName }) })] }));
|
|
14
|
+
return (_jsxs(RailSection, { title: t("documentDesigner.resources.title"), testId: "pdf-editor-resources", children: [resourceKeys.length === 0 && _jsx(RailRow, { label: t("documentDesigner.resources.empty") }), resourceKeys.map((key) => (_jsx(RailRow, { testId: `pdf-resource-${key}`, symbol: "\u25A3", label: key, onPress: () => onInsert(key) }, key))), _jsx(RailField, { children: canUpload && filesAvailable ? (_jsx(UploadZone, { multiple: false, accept: IMAGE_TYPES, title: t("documentDesigner.resources.upload"), onUpload: onUpload, testId: "document-designer-upload" })) : (_jsx(Button, { variant: "secondary", size: "sm", disabled: true, children: t("documentDesigner.resources.upload") })) }), !filesAvailable && _jsx(RailHint, { children: t("documentDesigner.resources.noFilesFeature") }), filesAvailable && !canUpload && (_jsx(RailHint, { children: t("documentDesigner.resources.saveFirst") })), _jsx(RailHint, { children: t("documentDesigner.placeholders.layoutHint", { layout: layoutName }) })] }));
|
|
16
15
|
}
|
package/dist/web/pdf-meta.js
CHANGED