@cosmicdrift/kumiko-document-designer 0.363.0 → 0.365.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 +208 -50
- package/dist/web/cells.js +4 -3
- package/dist/web/editor-header.d.ts +37 -0
- package/dist/web/editor-header.js +43 -0
- package/dist/web/editor-locale.d.ts +1 -0
- package/dist/web/editor-locale.js +15 -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 +101 -52
- package/dist/web/layout-editor.js +14 -7
- 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 +45 -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 +60 -45
- package/dist/web/mail-editor.js +9 -14
- 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.d.ts +8 -0
- package/dist/web/mail-right-rail.js +65 -14
- package/dist/web/mail-types.d.ts +10 -1
- package/dist/web/mail-upload-modal.js +12 -2
- 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 +2 -1
- 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 +91 -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-debounced-value.d.ts +1 -0
- package/dist/web/use-debounced-value.js +11 -0
- package/dist/web/use-insert-targets.d.ts +2 -2
- package/dist/web/use-insert-targets.js +4 -1
- package/dist/web/use-mail-persistence.d.ts +2 -2
- package/dist/web/use-mail-persistence.js +4 -4
- package/dist/web/use-own-address.d.ts +5 -0
- package/dist/web/use-own-address.js +14 -0
- package/dist/web/used-by-label.d.ts +1 -0
- package/dist/web/used-by-label.js +11 -0
- package/package.json +8 -8
- package/src/changes.json +48 -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
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// @runtime client
|
|
2
|
+
import { useAppFeatures, useQuery } from "@cosmicdrift/kumiko-renderer";
|
|
3
|
+
import { PROFILE_FEATURE_NAME, PROFILE_SCREEN_ID, USER_ME_QUERY_QN } from "../constants.js";
|
|
4
|
+
export function useOwnAddress() {
|
|
5
|
+
const features = useAppFeatures();
|
|
6
|
+
const me = useQuery(USER_ME_QUERY_QN, {});
|
|
7
|
+
const email = me.data?.email;
|
|
8
|
+
const hasProfileScreen = features.some((feature) => feature.featureName === PROFILE_FEATURE_NAME &&
|
|
9
|
+
feature.screens.some((screen) => screen.id === PROFILE_SCREEN_ID));
|
|
10
|
+
return {
|
|
11
|
+
email: typeof email === "string" && email !== "" ? email : null,
|
|
12
|
+
profileScreenId: hasProfileScreen ? PROFILE_SCREEN_ID : null,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { OverviewRowView, OverviewUsageView } from "./overview-row.js";
|
|
2
2
|
type Translate = (key: string, params?: Readonly<Record<string, unknown>>) => string;
|
|
3
|
+
export declare function templateTitle(t: Translate, row: Pick<OverviewRowView, "title" | "slug" | "kind">): string;
|
|
3
4
|
export declare function usageParts(t: Translate, usage: OverviewUsageView): {
|
|
4
5
|
readonly feature: string;
|
|
5
6
|
readonly notification: string;
|
|
@@ -5,6 +5,17 @@ function translateOrRaw(t, key, raw) {
|
|
|
5
5
|
const translated = t(key);
|
|
6
6
|
return translated === key ? raw : translated;
|
|
7
7
|
}
|
|
8
|
+
const NOTIFICATION_SLUG = /^([^:]+):notify:([^:]+)$/;
|
|
9
|
+
// A seeded system mail has no stored title, so its notification label from the
|
|
10
|
+
// owning feature names it; the slug stays the last resort.
|
|
11
|
+
export function templateTitle(t, row) {
|
|
12
|
+
if (row.title !== row.slug)
|
|
13
|
+
return row.title;
|
|
14
|
+
const match = row.kind === "mail" ? NOTIFICATION_SLUG.exec(row.slug) : null;
|
|
15
|
+
if (match === null)
|
|
16
|
+
return row.slug;
|
|
17
|
+
return usageParts(t, { feature: match[1] ?? "", notification: match[2] ?? "" }).notification;
|
|
18
|
+
}
|
|
8
19
|
export function usageParts(t, usage) {
|
|
9
20
|
return {
|
|
10
21
|
feature: translateOrRaw(t, `${usage.feature}:feature.label`, usage.feature),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-document-designer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.365.0",
|
|
4
4
|
"description": "Document designer for Kumiko: template overview, layout editor (mail layout, PDF letterhead) and the PDF template editor.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
23
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
24
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
25
|
-
"@cosmicdrift/kumiko-renderer": "0.
|
|
26
|
-
"@cosmicdrift/kumiko-renderer-puppeteer-client": "0.
|
|
27
|
-
"@cosmicdrift/kumiko-renderer-web": "0.
|
|
28
|
-
"@cosmicdrift/kumiko-types": "0.
|
|
22
|
+
"@cosmicdrift/kumiko-bundled-features": "0.365.0",
|
|
23
|
+
"@cosmicdrift/kumiko-framework": "0.365.0",
|
|
24
|
+
"@cosmicdrift/kumiko-headless": "0.365.0",
|
|
25
|
+
"@cosmicdrift/kumiko-renderer": "0.365.0",
|
|
26
|
+
"@cosmicdrift/kumiko-renderer-puppeteer-client": "0.365.0",
|
|
27
|
+
"@cosmicdrift/kumiko-renderer-web": "0.365.0",
|
|
28
|
+
"@cosmicdrift/kumiko-types": "0.365.0",
|
|
29
29
|
"pdfjs-dist": "6.3.289",
|
|
30
30
|
"react": "^19.2.6"
|
|
31
31
|
},
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,52 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.365.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "Seeded mails show readable titles and the editor opens in the UI language",
|
|
6
|
+
"detail": "A mail template without a stored title is named by its notification label (<feature>:notify.<name>.label) in the overview and the editor header instead of its slug. The mail editor opens on the UI language, or its base language, when the template has it, and falls back to the previous choice otherwise. The layout select offers the built-in standard layout when no default layout is stored, and the preview text field shows a hint while empty."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"version": "0.365.0",
|
|
10
|
+
"type": "fix",
|
|
11
|
+
"title": "Layout editor no longer asks for the platform override counts after a tenant save",
|
|
12
|
+
"detail": "Saving or resetting a layout as tenant admin refetched the tenant-overrides query, which only the platform admin may read, so every save logged a 403. The reload now refetches it only for the platform admin."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"version": "0.365.0",
|
|
16
|
+
"type": "improvement",
|
|
17
|
+
"title": "Mail editor rail edits header chip, tone and footer note",
|
|
18
|
+
"detail": "The properties rail of the mail editor has a section \"Kopf und Fuß\" with the header chip and the footer note as placeholder lines and the tone as a select (accent, info, success, warning, danger, neutral). The values are saved in the mail meta and show in the preview right away. A tone stored as placeholder stays as its own option; the accent tone is stored as no tone. template-resolver/web exports MAIL_TONES and MailTone."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"version": "0.365.0",
|
|
22
|
+
"type": "fix",
|
|
23
|
+
"title": "Debounced layout validation, collision-free resource keys and reload after a half-finished system publish",
|
|
24
|
+
"detail": "The layout editor sent a validate query (with an MJML compile) on every change; it now waits 400 ms after the last edit. A second upload with the same file name before the row refetch finished reused the first key; keys handed out earlier are now remembered. When a SystemAdmin saved a mail version but publishing it failed, the editor kept the old state with the error shown; it now reloads so the stored version is visible."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"version": "0.364.0",
|
|
28
|
+
"type": "improvement",
|
|
29
|
+
"title": "Editors and overview show an open draft beside the active version",
|
|
30
|
+
"detail": "The editor header shows the served version (Active vN) next to the open draft (Draft vM), fed by the active field of find-by-id and layout-source; the template overview shows the draftVersion badge under the status of an active row."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"version": "0.364.0",
|
|
34
|
+
"type": "improvement",
|
|
35
|
+
"title": "Document designer editors follow the approved board",
|
|
36
|
+
"detail": "Mail-layout templates accept appearance meta (width, font, outer background, accent line, button style) that the mail renderer applies, and the layout-source query returns it. The layout editor uses the shared editor shell with one paper and one shared toolbar (`DocumentContentEditor` gains `toolbarHost`). Mail and PDF editors share frame, info bar, rails and footer.\nThe unused `sender` key of mail template meta is gone from the editor; stored rows that still carry it are read without it, so they stay editable.\nInvalid template meta now reports per-field messages (`templateResolver.errors.template.meta.<field>.required|invalid`) instead of the parser text; the editors show them at the field. The `tenant-overrides` query additionally returns one row per customising tenant (name, status, version, changed at/by) and, with `diffTenantId`, the diff to the system version. A language without a version shows an empty state in the mail editor instead of a disabled editor.\nThe `compileDocumentBody` option `resourceImageMaxWidthPx` caps resource images in mails (180 px). The `send-test` command takes `prefixSubject` (default true) to skip the `[TEST]` subject prefix, the template preview returns `mailLayout` (slug, scope, version of the layout used), and `RenderedMail` carries the layout ref."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"version": "0.364.0",
|
|
40
|
+
"type": "fix",
|
|
41
|
+
"title": "PDF editor keeps unset margins unset, frees old PDF previews, and shows reset and diff errors",
|
|
42
|
+
"detail": "Setting one margin no longer stores the other three as 15 mm, so the layout's header and footer margins still apply to them. The PDF preview no longer re-renders pages or stacks text layers on re-render and destroys the previous document. A failed reset to the system version shows an error banner in the PDF and mail editors, and a failing diff query shows an error state with retry instead of loading forever. The canvas reserves the footer margin like the renderer. Page formats, orientations and default margins now come from template-resolver (new exports PDF_ORIENTATIONS, PDF_DEFAULT_MARGIN_MM, PDF_HEADER_MARGIN_MM, PDF_FOOTER_MARGIN_MM, PdfOrientation)."
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"version": "0.364.0",
|
|
46
|
+
"type": "fix",
|
|
47
|
+
"title": "Layout editor shows the real number of mails or PDFs using a layout",
|
|
48
|
+
"detail": "The editor asks the overview for the users of one layout (kind and layoutSlug filter) and takes the count from total, so a tenant with more than 200 templates no longer sees a too low usage number."
|
|
49
|
+
},
|
|
2
50
|
{
|
|
3
51
|
"version": "0.363.0",
|
|
4
52
|
"type": "improvement",
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
import type { TemplateRowView } from "./mail-types.js";
|
|
3
|
-
export type EditorMode = "edit" | "preview";
|
|
4
|
-
export type HeaderActions = {
|
|
5
|
-
readonly busy: boolean;
|
|
6
|
-
readonly canSave: boolean;
|
|
7
|
-
readonly canPublish: boolean;
|
|
8
|
-
readonly onSave: () => void;
|
|
9
|
-
readonly onPublish: () => void;
|
|
10
|
-
};
|
|
11
|
-
export declare function MailHeader({ title, row, locales, locale, mode, testPending, actions, onBack, onLocale, onMode, onTestMail, }: {
|
|
12
|
-
readonly title: string;
|
|
13
|
-
readonly row: TemplateRowView | null;
|
|
14
|
-
readonly locales: readonly string[];
|
|
15
|
-
readonly locale: string;
|
|
16
|
-
readonly mode: EditorMode;
|
|
17
|
-
readonly testPending: boolean;
|
|
18
|
-
readonly actions: HeaderActions;
|
|
19
|
-
readonly onBack: () => void;
|
|
20
|
-
readonly onLocale: (locale: string) => void;
|
|
21
|
-
readonly onMode: (mode: EditorMode) => void;
|
|
22
|
-
readonly onTestMail: () => void;
|
|
23
|
-
}): ReactNode;
|
package/dist/web/mail-header.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
|
-
import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
4
|
-
import { ModeSwitch, StatusBadge } from "@cosmicdrift/kumiko-renderer-web";
|
|
5
|
-
import { asRowStatus, statusLabel, statusTone } from "./status-label.js";
|
|
6
|
-
export function MailHeader({ title, row, locales, locale, mode, testPending, actions, onBack, onLocale, onMode, onTestMail, }) {
|
|
7
|
-
const t = useTranslation();
|
|
8
|
-
const { Button, Heading, Link, Text } = usePrimitives();
|
|
9
|
-
const status = row === null ? null : asRowStatus(row.status);
|
|
10
|
-
return (_jsxs("div", { className: "flex flex-col gap-3", "data-testid": "mail-editor-header", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [_jsx(Link, { href: "#", onPress: onBack, testId: "document-designer-back", children: t("documentDesigner.editor.crumb") }), _jsx(Text, { variant: "muted", children: "\u203A" }), _jsx(Heading, { variant: "page", testId: "document-designer-editor-title", children: title }), row !== null && status !== null && (_jsxs(_Fragment, { children: [_jsx(StatusBadge, { tone: statusTone(status), testId: "document-designer-editor-status", children: statusLabel(t, status, row.version) }), _jsx(StatusBadge, { tone: row.scope === "tenant" ? "accent" : "muted", testId: "document-designer-editor-origin", children: t(row.scope === "tenant"
|
|
11
|
-
? "documentDesigner.origin.tenant"
|
|
12
|
-
: "documentDesigner.origin.system") })] })), _jsx("div", { className: "grow" }), _jsx(ModeSwitch, { value: locale, onChange: onLocale, ariaLabel: t("documentDesigner.mail.header.localeSwitch"), testId: "mail-locale-switch", options: locales.map((code) => ({ value: code, label: code })) }), _jsx(ModeSwitch, { value: mode, onChange: onMode, ariaLabel: t("documentDesigner.mail.header.modeSwitch"), testId: "mail-mode-switch", options: [
|
|
13
|
-
{ value: "edit", label: t("documentDesigner.mail.header.edit") },
|
|
14
|
-
{ value: "preview", label: t("documentDesigner.mail.header.preview") },
|
|
15
|
-
] })] }), _jsxs("div", { className: "flex items-center gap-3", "data-testid": "mail-editor-header-actions", children: [_jsx(Button, { variant: "secondary", disabled: testPending, loading: testPending, onClick: onTestMail, testId: "mail-test-mail-header", children: t("documentDesigner.mail.header.testMail") }), _jsx(Button, { variant: "secondary", disabled: !actions.canSave, loading: actions.busy, onClick: actions.onSave, testId: "mail-header-save", children: t("documentDesigner.editor.save") }), _jsx(Button, { disabled: !actions.canPublish, onClick: actions.onPublish, testId: "mail-header-publish", children: t("documentDesigner.editor.publish") })] })] }));
|
|
16
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
import type { VariableInfo } from "./types.js";
|
|
3
|
-
export type SystemPlaceholderRow = {
|
|
4
|
-
readonly name: string;
|
|
5
|
-
readonly label: string;
|
|
6
|
-
readonly status: "used" | "layout" | "unused";
|
|
7
|
-
};
|
|
8
|
-
export type PlaceholderPanelProps = {
|
|
9
|
-
readonly variables: Readonly<Record<string, VariableInfo>>;
|
|
10
|
-
readonly used: readonly string[];
|
|
11
|
-
readonly missingRequired: readonly string[];
|
|
12
|
-
readonly systemRows: readonly SystemPlaceholderRow[];
|
|
13
|
-
readonly resourceKeys: readonly string[];
|
|
14
|
-
readonly canUpload: boolean;
|
|
15
|
-
readonly layoutName: string;
|
|
16
|
-
readonly onInsert: (name: string) => void;
|
|
17
|
-
readonly onInsertResource: (key: string) => void;
|
|
18
|
-
readonly onUpload: (file: File) => Promise<void>;
|
|
19
|
-
};
|
|
20
|
-
export declare function visibleSystemPlaceholders(names: readonly string[]): readonly string[];
|
|
21
|
-
export declare function PlaceholderPanel(props: PlaceholderPanelProps): ReactNode;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @runtime client
|
|
3
|
-
import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
4
|
-
import { cn, SectionCard, UploadZone } from "@cosmicdrift/kumiko-renderer-web";
|
|
5
|
-
const TYPE_GLYPHS = {
|
|
6
|
-
text: "T",
|
|
7
|
-
money: "€",
|
|
8
|
-
date: "D",
|
|
9
|
-
link: "↗",
|
|
10
|
-
list: "≡",
|
|
11
|
-
};
|
|
12
|
-
// unsubscribeUrl belongs to mail; the PDF designer never offers it.
|
|
13
|
-
const HIDDEN_SYSTEM_PLACEHOLDERS = new Set(["unsubscribeUrl"]);
|
|
14
|
-
export function visibleSystemPlaceholders(names) {
|
|
15
|
-
return names.filter((name) => !HIDDEN_SYSTEM_PLACEHOLDERS.has(name));
|
|
16
|
-
}
|
|
17
|
-
export function PlaceholderPanel(props) {
|
|
18
|
-
const t = useTranslation();
|
|
19
|
-
const { Button } = usePrimitives();
|
|
20
|
-
const names = Object.keys(props.variables);
|
|
21
|
-
return (_jsxs("div", { className: "flex flex-col gap-4", "data-testid": "document-designer-placeholders", children: [_jsx(SectionCard, { title: t("documentDesigner.placeholders.title"), children: _jsx("ul", { className: "m-0 flex list-none flex-col gap-1 p-0", children: names.map((name) => {
|
|
22
|
-
const info = props.variables[name];
|
|
23
|
-
if (info === undefined)
|
|
24
|
-
return null;
|
|
25
|
-
const missing = props.missingRequired.includes(name);
|
|
26
|
-
const isUsed = props.used.includes(name);
|
|
27
|
-
return (_jsx("li", { children: _jsxs("button", { type: "button", "data-testid": `placeholder-${name}`, onClick: () => props.onInsert(name), className: cn("flex w-full items-center gap-2 rounded-md border px-2 py-1 text-left text-sm", missing
|
|
28
|
-
? "border-status-bad bg-status-bad-surface"
|
|
29
|
-
: "border-border bg-background hover:bg-muted"), children: [_jsx("span", { className: "w-4 text-center text-xs text-muted-foreground", children: TYPE_GLYPHS[info.type] }), _jsxs("span", { className: "min-w-0 flex-1 truncate", children: [info.label ?? name, info.required === true ? " *" : ""] }), _jsx("span", { className: cn("text-xs", missing ? "font-medium text-status-bad" : "text-muted-foreground"), children: missing ? t("documentDesigner.placeholders.missing") : isUsed ? "✓" : "–" })] }) }, name));
|
|
30
|
-
}) }) }), _jsx(SectionCard, { title: t("documentDesigner.placeholders.system"), children: _jsx("ul", { className: "m-0 flex list-none flex-col gap-1 p-0", children: props.systemRows.map((row) => (_jsx("li", { children: _jsxs("button", { type: "button", "data-testid": `system-placeholder-${row.name}`, onClick: () => props.onInsert(row.name), className: "flex w-full items-center gap-2 rounded-md border border-primary/30 bg-primary/10 px-2 py-1 text-left text-sm text-primary", children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: row.label }), _jsx("span", { className: "text-xs", children: row.status === "used"
|
|
31
|
-
? "✓"
|
|
32
|
-
: row.status === "layout"
|
|
33
|
-
? t("documentDesigner.placeholders.fromLayout")
|
|
34
|
-
: "–" })] }) }, row.name))) }) }), _jsxs(SectionCard, { title: t("documentDesigner.resources.title"), children: [props.resourceKeys.length === 0 ? (_jsx("p", { className: "text-xs text-muted-foreground", children: t("documentDesigner.resources.empty") })) : (_jsx("ul", { className: "m-0 flex list-none flex-col gap-1 p-0 font-mono text-xs", children: props.resourceKeys.map((key) => (_jsxs("li", { className: "flex items-center justify-between gap-2", children: [_jsx("span", { className: "truncate", children: key }), _jsx(Button, { variant: "link", size: "sm", onClick: () => props.onInsertResource(key), ariaLabel: t("documentDesigner.resources.insertNamed", { key }), children: t("documentDesigner.resources.insert") })] }, key))) })), props.canUpload ? (_jsx(UploadZone, { multiple: false, accept: ["image/png", "image/jpeg", "image/gif", "image/webp"], title: t("documentDesigner.resources.upload"), onUpload: props.onUpload, testId: "document-designer-upload" })) : (_jsx(Button, { variant: "secondary", size: "sm", disabled: true, children: t("documentDesigner.resources.upload") })), !props.canUpload && (_jsx("p", { className: "text-xs text-muted-foreground", children: t("documentDesigner.resources.saveFirst") }))] }), _jsx("p", { className: "text-xs text-muted-foreground", children: t("documentDesigner.placeholders.layoutHint", { layout: props.layoutName }) })] }));
|
|
35
|
-
}
|