@cosmicdrift/kumiko-document-designer 0.368.0 → 0.370.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 +1 -0
- package/dist/constants.js +2 -0
- package/dist/i18n.js +24 -16
- package/dist/index.d.ts +2 -2
- package/dist/web/editor-shell.d.ts +1 -1
- package/dist/web/i18n.js +2 -2
- package/dist/web/index.d.ts +6 -6
- package/dist/web/layout-editor-body.d.ts +2 -2
- package/dist/web/layout-rails.d.ts +3 -3
- package/dist/web/layout-types.d.ts +2 -2
- package/dist/web/layout-users.d.ts +2 -2
- package/dist/web/mail-draft.d.ts +2 -2
- package/dist/web/mail-draft.js +6 -4
- package/dist/web/mail-edit-center.d.ts +3 -3
- package/dist/web/mail-editor-body.d.ts +1 -1
- package/dist/web/mail-editor-body.js +3 -3
- package/dist/web/mail-footer.d.ts +1 -1
- package/dist/web/mail-footer.js +4 -3
- package/dist/web/mail-left-rail.d.ts +1 -1
- package/dist/web/mail-left-rail.js +2 -1
- package/dist/web/mail-origin-bar.d.ts +1 -1
- package/dist/web/mail-pdf-link-modal.d.ts +1 -1
- package/dist/web/mail-preview-side.d.ts +2 -2
- package/dist/web/mail-preview-side.js +2 -1
- package/dist/web/mail-preview.d.ts +4 -4
- package/dist/web/mail-right-rail.d.ts +6 -5
- package/dist/web/mail-right-rail.js +29 -9
- package/dist/web/mail-types.d.ts +2 -2
- package/dist/web/origin-bar.d.ts +1 -1
- package/dist/web/overrides-drawer.d.ts +1 -1
- package/dist/web/overview-row.d.ts +1 -1
- package/dist/web/page-canvas.d.ts +2 -2
- package/dist/web/pdf-footer.d.ts +2 -2
- package/dist/web/pdf-footer.js +2 -1
- package/dist/web/pdf-template-editor.js +1 -1
- package/dist/web/rail.d.ts +3 -1
- package/dist/web/rail.js +2 -2
- package/dist/web/settings-panel.d.ts +5 -5
- package/dist/web/status-label.d.ts +1 -1
- package/dist/web/template-rows.d.ts +1 -1
- package/dist/web/types.d.ts +1 -1
- package/dist/web/use-mail-persistence.d.ts +1 -1
- package/dist/web/use-overview-rows.d.ts +1 -1
- package/dist/web/used-by-label.d.ts +2 -1
- package/dist/web/used-by-label.js +4 -0
- package/package.json +8 -8
- package/src/changes.json +6 -0
package/dist/constants.d.ts
CHANGED
package/dist/constants.js
CHANGED
|
@@ -35,3 +35,5 @@ export const DocumentDesignerCells = {
|
|
|
35
35
|
overriddenBy: "document-designer-overridden-by-cell",
|
|
36
36
|
};
|
|
37
37
|
export const DEFAULT_TEMPLATE_LOCALES = ["de", "en"];
|
|
38
|
+
// Generated settings screen where a tenant admin picks its mail provider.
|
|
39
|
+
export const TENANT_MAIL_SENDING_SCREEN_ID = "mail-foundation-tenant";
|
package/dist/i18n.js
CHANGED
|
@@ -143,7 +143,7 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
143
143
|
},
|
|
144
144
|
"documentDesigner.editor.mjmlNotice.tenant": {
|
|
145
145
|
en: "You see the system layout. Saving creates your own layout for this tenant.",
|
|
146
|
-
de: "
|
|
146
|
+
de: "Du siehst das System-Layout. Speichern legt ein eigenes Layout für deinen Mandanten an.",
|
|
147
147
|
},
|
|
148
148
|
"documentDesigner.editor.origin.none": {
|
|
149
149
|
en: "There is no layout yet.",
|
|
@@ -151,7 +151,7 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
151
151
|
},
|
|
152
152
|
"documentDesigner.editor.origin.system": {
|
|
153
153
|
en: "You are looking at the system layout. Saving creates a customisation for your tenant.",
|
|
154
|
-
de: "
|
|
154
|
+
de: "Du siehst das System-Layout. Speichern legt eine Anpassung für deinen Mandanten an.",
|
|
155
155
|
},
|
|
156
156
|
"documentDesigner.editor.origin.tenant.mails": {
|
|
157
157
|
en: "Overrides system layout v{version}. Applies to {count} mail templates of this tenant.",
|
|
@@ -183,7 +183,7 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
183
183
|
},
|
|
184
184
|
"documentDesigner.editor.diff.identical": {
|
|
185
185
|
en: "Your layout matches the system layout.",
|
|
186
|
-
de: "
|
|
186
|
+
de: "Dein Layout entspricht dem System-Layout.",
|
|
187
187
|
},
|
|
188
188
|
"documentDesigner.editor.reset.open": {
|
|
189
189
|
en: "Reset",
|
|
@@ -195,7 +195,7 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
195
195
|
},
|
|
196
196
|
"documentDesigner.editor.reset.description": {
|
|
197
197
|
en: "Your own layout is removed and the system layout applies again.",
|
|
198
|
-
de: "
|
|
198
|
+
de: "Dein eigenes Layout wird entfernt, danach gilt wieder das System-Layout.",
|
|
199
199
|
},
|
|
200
200
|
"documentDesigner.editor.reset.confirm": { en: "Reset", de: "Zurücksetzen" },
|
|
201
201
|
"documentDesigner.editor.region.header": { en: "Header", de: "Kopf" },
|
|
@@ -235,7 +235,7 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
235
235
|
},
|
|
236
236
|
"documentDesigner.editor.dirtySwitch.description": {
|
|
237
237
|
en: "Switching the layout kind discards your unsaved changes.",
|
|
238
|
-
de: "Beim Wechsel der Layout-Art gehen
|
|
238
|
+
de: "Beim Wechsel der Layout-Art gehen deine ungespeicherten Änderungen verloren.",
|
|
239
239
|
},
|
|
240
240
|
"documentDesigner.editor.dirtySwitch.confirm": {
|
|
241
241
|
en: "Discard and switch",
|
|
@@ -283,7 +283,7 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
283
283
|
},
|
|
284
284
|
"documentDesigner.mail.origin.tenantOnly": {
|
|
285
285
|
en: "This template exists only for your tenant.",
|
|
286
|
-
de: "Diese Vorlage gibt es nur für
|
|
286
|
+
de: "Diese Vorlage gibt es nur für deinen Mandanten.",
|
|
287
287
|
},
|
|
288
288
|
"documentDesigner.mail.origin.activeSince": {
|
|
289
289
|
en: "Active since {when}.",
|
|
@@ -303,7 +303,7 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
303
303
|
},
|
|
304
304
|
"documentDesigner.mail.origin.system": {
|
|
305
305
|
en: "You are viewing the system template v{version}. Saving creates a customisation for your tenant.",
|
|
306
|
-
de: "
|
|
306
|
+
de: "Du siehst die System-Vorlage v{version}. Speichern legt eine Anpassung für deinen Mandanten an.",
|
|
307
307
|
},
|
|
308
308
|
"documentDesigner.mail.origin.platform": {
|
|
309
309
|
en: "System template v{version}. Saving changes it for every tenant without a customisation.",
|
|
@@ -339,7 +339,7 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
339
339
|
},
|
|
340
340
|
"documentDesigner.mail.origin.copyNote": {
|
|
341
341
|
en: "Copying a system version does not bring images or attachments along; upload them again afterwards.",
|
|
342
|
-
de: "Beim Kopieren einer System-Fassung kommen Bilder und Anhänge nicht mit;
|
|
342
|
+
de: "Beim Kopieren einer System-Fassung kommen Bilder und Anhänge nicht mit; lade sie danach neu hoch.",
|
|
343
343
|
},
|
|
344
344
|
"documentDesigner.mail.origin.createBlank": {
|
|
345
345
|
en: "Start empty",
|
|
@@ -347,7 +347,7 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
347
347
|
},
|
|
348
348
|
"documentDesigner.mail.origin.creating": {
|
|
349
349
|
en: "New version for {language}. It is stored when you save.",
|
|
350
|
-
de: "Neue Fassung für {language}.
|
|
350
|
+
de: "Neue Fassung für {language}. Die Fassung wird erst mit Speichern angelegt.",
|
|
351
351
|
},
|
|
352
352
|
"documentDesigner.mail.reset.title": {
|
|
353
353
|
en: "Reset to the system template?",
|
|
@@ -355,7 +355,7 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
355
355
|
},
|
|
356
356
|
"documentDesigner.mail.reset.description": {
|
|
357
357
|
en: "Your customisation of this template is removed in all languages. The system template applies again.",
|
|
358
|
-
de: "
|
|
358
|
+
de: "Deine Anpassung dieser Vorlage wird in allen Sprachen entfernt. Danach gilt wieder die System-Vorlage.",
|
|
359
359
|
},
|
|
360
360
|
"documentDesigner.mail.reset.confirm": {
|
|
361
361
|
en: "Reset",
|
|
@@ -509,6 +509,14 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
509
509
|
en: "The address comes from the platform's mail delivery.",
|
|
510
510
|
de: "Die Adresse kommt vom Mail-Versand der Plattform.",
|
|
511
511
|
},
|
|
512
|
+
"documentDesigner.mail.props.senderHintTenant": {
|
|
513
|
+
en: "The address comes from your organization's email sending settings.",
|
|
514
|
+
de: "Die Adresse kommt aus dem E-Mail-Versand deines Mandanten.",
|
|
515
|
+
},
|
|
516
|
+
"documentDesigner.mail.props.senderSettingsLink": {
|
|
517
|
+
en: "Open email sending",
|
|
518
|
+
de: "E-Mail-Versand öffnen",
|
|
519
|
+
},
|
|
512
520
|
"documentDesigner.mail.props.replyTo": {
|
|
513
521
|
en: "Reply to",
|
|
514
522
|
de: "Antwort an",
|
|
@@ -679,7 +687,7 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
679
687
|
},
|
|
680
688
|
"documentDesigner.mail.discard.description": {
|
|
681
689
|
en: "Your unsaved changes are lost.",
|
|
682
|
-
de: "
|
|
690
|
+
de: "Deine ungespeicherten Änderungen gehen verloren.",
|
|
683
691
|
},
|
|
684
692
|
"documentDesigner.mail.discard.confirm": {
|
|
685
693
|
en: "Discard",
|
|
@@ -687,7 +695,7 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
687
695
|
},
|
|
688
696
|
"documentDesigner.origin.draftBesideActive": {
|
|
689
697
|
en: "Version {active} is sent, your draft (version {draft}) is not published yet.",
|
|
690
|
-
de: "Verschickt wird Version {active},
|
|
698
|
+
de: "Verschickt wird Version {active}, dein Entwurf (Version {draft}) ist noch nicht veröffentlicht.",
|
|
691
699
|
},
|
|
692
700
|
"documentDesigner.mail.leave.title": {
|
|
693
701
|
en: "Unsaved changes",
|
|
@@ -695,7 +703,7 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
695
703
|
},
|
|
696
704
|
"documentDesigner.mail.leave.description": {
|
|
697
705
|
en: "Save your changes as a draft, discard them or stay on this page.",
|
|
698
|
-
de: "
|
|
706
|
+
de: "Speichere deine Änderungen als Entwurf, verwirf sie oder bleib auf dieser Seite.",
|
|
699
707
|
},
|
|
700
708
|
"documentDesigner.mail.leave.save": {
|
|
701
709
|
en: "Save as draft",
|
|
@@ -791,7 +799,7 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
791
799
|
},
|
|
792
800
|
"documentDesigner.mail.testMail.ownAddress": {
|
|
793
801
|
en: "Your address",
|
|
794
|
-
de: "
|
|
802
|
+
de: "Deine Adresse",
|
|
795
803
|
},
|
|
796
804
|
"documentDesigner.mail.testMail.changeAddress": {
|
|
797
805
|
en: "Change address",
|
|
@@ -815,11 +823,11 @@ export const DOCUMENT_DESIGNER_I18N = {
|
|
|
815
823
|
},
|
|
816
824
|
"documentDesigner.mail.testMail.hint": {
|
|
817
825
|
en: "Sent through the tenant's normal delivery path to your own address.",
|
|
818
|
-
de: "Versand über den normalen Zustellweg des Mandanten an
|
|
826
|
+
de: "Versand über den normalen Zustellweg des Mandanten an deine eigene Adresse.",
|
|
819
827
|
},
|
|
820
828
|
"documentDesigner.mail.testMail.sent": {
|
|
821
829
|
en: "Test mail sent to your address: {subject}",
|
|
822
|
-
de: "Test-Mail an
|
|
830
|
+
de: "Test-Mail an deine Adresse gesendet: {subject}",
|
|
823
831
|
},
|
|
824
832
|
"documentDesigner.mail.testMail.rateLimited": {
|
|
825
833
|
en: "Too many test mails, please try again later.",
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { DEFAULT_TEMPLATE_LOCALES, DOCUMENT_DESIGNER_FEATURE, LAYOUT_EDITOR_SCREEN_ID, MAIL_EDITOR_SCREEN_ID, PDF_TEMPLATE_EDITOR_SCREEN_ID, PLATFORM_TEMPLATES_NAV_ID, PLATFORM_TEMPLATES_SCREEN_ID, TEMPLATE_EDITOR_SCREENS, TEMPLATES_NAV_ID, TEMPLATES_SCREEN_ID, type TemplateEditorKind, } from "./constants.
|
|
2
|
-
export { createDocumentDesignerFeature } from "./feature.
|
|
1
|
+
export { DEFAULT_TEMPLATE_LOCALES, DOCUMENT_DESIGNER_FEATURE, LAYOUT_EDITOR_SCREEN_ID, MAIL_EDITOR_SCREEN_ID, PDF_TEMPLATE_EDITOR_SCREEN_ID, PLATFORM_TEMPLATES_NAV_ID, PLATFORM_TEMPLATES_SCREEN_ID, TEMPLATE_EDITOR_SCREENS, TEMPLATES_NAV_ID, TEMPLATES_SCREEN_ID, type TemplateEditorKind, } from "./constants.ts";
|
|
2
|
+
export { createDocumentDesignerFeature } from "./feature.ts";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
-
import { type EditorOrigin } from "./editor-header.
|
|
2
|
+
import { type EditorOrigin } from "./editor-header.tsx";
|
|
3
3
|
export type InfoBarTone = "active" | "muted" | "warn";
|
|
4
4
|
export type FooterTone = "bad" | "warn" | "ok";
|
|
5
5
|
export declare function EditorFrame({ testId, children, }: {
|
package/dist/web/i18n.js
CHANGED
|
@@ -76,7 +76,7 @@ export const documentDesignerTranslations = {
|
|
|
76
76
|
"documentDesigner.canvas.caption": 'Seite 1 · {format} · Die Vorschau zeigt Seitengrenzen nur ungefähr, verbindlich ist "PDF erzeugen".',
|
|
77
77
|
"documentDesigner.canvas.fromLayout": "aus Layout „{layout}“",
|
|
78
78
|
"documentDesigner.error.resourceTooLarge": "Das Bild ist zu groß. Bitte eine kleinere Datei wählen.",
|
|
79
|
-
"documentDesigner.error.resourceUnsupportedImage": "Dieses Bildformat wird nicht unterstützt.
|
|
79
|
+
"documentDesigner.error.resourceUnsupportedImage": "Dieses Bildformat wird nicht unterstützt. Nutze PNG, JPEG, GIF oder WebP.",
|
|
80
80
|
"documentDesigner.locale": "Sprache",
|
|
81
81
|
"documentDesigner.localeDialog.cancel": "Abbrechen",
|
|
82
82
|
"documentDesigner.localeDialog.description": "Die Änderungen in dieser Sprache sind noch nicht gespeichert.",
|
|
@@ -111,7 +111,7 @@ export const documentDesignerTranslations = {
|
|
|
111
111
|
"documentDesigner.preview.renderFailed": "Das PDF konnte nicht angezeigt werden.",
|
|
112
112
|
"documentDesigner.preview.stale": "Die Vorschau ist veraltet. Bitte das PDF neu erzeugen.",
|
|
113
113
|
"documentDesigner.resources.empty": "Noch keine Bilder.",
|
|
114
|
-
"documentDesigner.resources.saveFirst": "
|
|
114
|
+
"documentDesigner.resources.saveFirst": "Speichere die Vorlage zuerst, danach können Bilder hochgeladen werden.",
|
|
115
115
|
"documentDesigner.resources.title": "Ressourcen",
|
|
116
116
|
"documentDesigner.resources.upload": "+ Hochladen",
|
|
117
117
|
"documentDesigner.settings.fileName": "Dateiname",
|
package/dist/web/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { type DocumentDesignerClientOptions, documentDesignerClient, PdfTemplateEditorScreen, } from "./client-plugin.
|
|
2
|
-
export { composeLayoutHtml, type LayoutRegionValues } from "./compose-layout.
|
|
3
|
-
export { documentDesignerTranslations } from "./i18n.
|
|
4
|
-
export { PdfTemplateEditor, type PdfTemplateEditorProps } from "./pdf-template-editor.
|
|
5
|
-
export { type PreviewRecordPicker, type PreviewRecordPickerProps, PreviewRecordPickerProvider, } from "./preview-record-picker.
|
|
6
|
-
export { usageLabel, usedByLabel } from "./used-by-label.
|
|
1
|
+
export { type DocumentDesignerClientOptions, documentDesignerClient, PdfTemplateEditorScreen, } from "./client-plugin.tsx";
|
|
2
|
+
export { composeLayoutHtml, type LayoutRegionValues } from "./compose-layout.ts";
|
|
3
|
+
export { documentDesignerTranslations } from "./i18n.ts";
|
|
4
|
+
export { PdfTemplateEditor, type PdfTemplateEditorProps } from "./pdf-template-editor.tsx";
|
|
5
|
+
export { type PreviewRecordPicker, type PreviewRecordPickerProps, PreviewRecordPickerProvider, } from "./preview-record-picker.tsx";
|
|
6
|
+
export { usageLabel, usedByLabel } from "./used-by-label.ts";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { LayoutKind, LayoutSourceView, TenantOverrideCounts } from "./layout-types.
|
|
3
|
-
import type { OverviewRowView } from "./overview-row.
|
|
2
|
+
import type { LayoutKind, LayoutSourceView, TenantOverrideCounts } from "./layout-types.ts";
|
|
3
|
+
import type { OverviewRowView } from "./overview-row.ts";
|
|
4
4
|
export declare function LayoutEditorBody({ kind, slug, isSystemAdmin, source, overviewRows, usedByCount, overrides, onKindChange, onReload, }: {
|
|
5
5
|
readonly kind: LayoutKind;
|
|
6
6
|
readonly slug: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
-
import { type LayoutAppearance } from "./layout-meta.
|
|
3
|
-
import type { BrandingView, LayoutKind, LayoutValidation } from "./layout-types.
|
|
4
|
-
import type { OverviewRowView } from "./overview-row.
|
|
2
|
+
import { type LayoutAppearance } from "./layout-meta.ts";
|
|
3
|
+
import type { BrandingView, LayoutKind, LayoutValidation } from "./layout-types.ts";
|
|
4
|
+
import type { OverviewRowView } from "./overview-row.ts";
|
|
5
5
|
export declare function BrandingRail({ data, unavailable, }: {
|
|
6
6
|
readonly data: BrandingView | null;
|
|
7
7
|
readonly unavailable: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { LayoutMeta } from "./layout-meta.
|
|
2
|
-
import type { ServedVersionView } from "./types.
|
|
1
|
+
import type { LayoutMeta } from "./layout-meta.ts";
|
|
2
|
+
import type { ServedVersionView } from "./types.ts";
|
|
3
3
|
export type LayoutKind = "mail-layout" | "pdf-layout";
|
|
4
4
|
export type ValidationIssue = {
|
|
5
5
|
readonly path: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LayoutKind } from "./layout-types.
|
|
2
|
-
import type { OverviewRowView } from "./overview-row.
|
|
1
|
+
import type { LayoutKind } from "./layout-types.ts";
|
|
2
|
+
import type { OverviewRowView } from "./overview-row.ts";
|
|
3
3
|
export declare function layoutUserKind(kind: LayoutKind): "mail" | "document-pdf";
|
|
4
4
|
export declare function layoutUsers(rows: readonly OverviewRowView[], kind: LayoutKind, layoutSlug: string): readonly OverviewRowView[];
|
package/dist/web/mail-draft.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LinkedResourceView, MailDraft, MailVariable, PlaceholderType, TemplateRowView } from "./mail-types.
|
|
1
|
+
import type { LinkedResourceView, MailDraft, MailVariable, PlaceholderType, TemplateRowView } from "./mail-types.ts";
|
|
2
2
|
export declare const EMPTY_DRAFT: MailDraft;
|
|
3
3
|
export declare const PLACEHOLDER_SYMBOL: Readonly<Record<PlaceholderType, string>>;
|
|
4
4
|
export declare const LAYOUT_OWNED_PLACEHOLDERS: ReadonlySet<string>;
|
|
@@ -14,6 +14,6 @@ export declare function parseLinkedResources(value: unknown): readonly LinkedRes
|
|
|
14
14
|
export declare function uniqueResourceKey(source: string, taken: readonly string[]): string;
|
|
15
15
|
export declare function lastSlugSegment(slug: string): string;
|
|
16
16
|
export declare function formatKilobytes(bytes: number): string;
|
|
17
|
-
export declare function variableLabelMap(variables: Readonly<Record<string, MailVariable>>): Record<string, string>;
|
|
17
|
+
export declare function variableLabelMap(t: Translate, variables: Readonly<Record<string, MailVariable>>): Record<string, string>;
|
|
18
18
|
export declare function displayValue(t: (key: string, params?: Readonly<Record<string, unknown>>) => string, value: unknown): string;
|
|
19
19
|
export {};
|
package/dist/web/mail-draft.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { variableLabelText } from "./used-by-label.js";
|
|
1
2
|
export const EMPTY_DRAFT = {
|
|
2
3
|
subject: "",
|
|
3
4
|
previewText: "",
|
|
@@ -142,14 +143,15 @@ export function formatKilobytes(bytes) {
|
|
|
142
143
|
return `${Math.max(1, Math.round(bytes / 1024))} KB`;
|
|
143
144
|
}
|
|
144
145
|
// Labels for the chip editors; list item fields are addressed as "list.item".
|
|
145
|
-
export function variableLabelMap(variables) {
|
|
146
|
+
export function variableLabelMap(t, variables) {
|
|
146
147
|
const labels = {};
|
|
147
148
|
for (const [name, variable] of Object.entries(variables)) {
|
|
148
149
|
if (variable.label !== undefined)
|
|
149
|
-
labels[name] = variable.label;
|
|
150
|
+
labels[name] = variableLabelText(t, name, variable.label);
|
|
150
151
|
for (const [itemName, item] of Object.entries(variable.item ?? {})) {
|
|
151
|
-
if (item.label !== undefined)
|
|
152
|
-
labels[`${name}.${itemName}`] = item.label;
|
|
152
|
+
if (item.label !== undefined) {
|
|
153
|
+
labels[`${name}.${itemName}`] = variableLabelText(t, itemName, item.label);
|
|
154
|
+
}
|
|
153
155
|
}
|
|
154
156
|
}
|
|
155
157
|
return labels;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type DocumentResourcePreview } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
|
-
import type { MailMetaField } from "./mail-footer.
|
|
4
|
-
import type { MailDraft } from "./mail-types.
|
|
5
|
-
import type { InsertTargets } from "./use-insert-targets.
|
|
3
|
+
import type { MailMetaField } from "./mail-footer.ts";
|
|
4
|
+
import type { MailDraft } from "./mail-types.ts";
|
|
5
|
+
import type { InsertTargets } from "./use-insert-targets.ts";
|
|
6
6
|
export type EditorVocabulary = {
|
|
7
7
|
readonly names: readonly string[];
|
|
8
8
|
readonly labels: Readonly<Record<string, string>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { MailValidation, TemplateRowView } from "./mail-types.
|
|
2
|
+
import type { MailValidation, TemplateRowView } from "./mail-types.ts";
|
|
3
3
|
export type MailEditorBodyProps = {
|
|
4
4
|
readonly slug: string;
|
|
5
5
|
readonly locale: string;
|
|
@@ -10,7 +10,7 @@ import { MailConfirmDialogs } from "./mail-dialogs.js";
|
|
|
10
10
|
import { buildMeta, isDraftDirty, loadedMailOf, parseJsonRecord, parseLinkedResources, systemLabelMap, variableLabelMap, } from "./mail-draft.js";
|
|
11
11
|
import { MailEditCenter } from "./mail-edit-center.js";
|
|
12
12
|
import { footerMessage, metaFieldMessages } from "./mail-footer.js";
|
|
13
|
-
import { PlaceholderSection, ResourceSection, SystemPlaceholderSection } from "./mail-left-rail.js";
|
|
13
|
+
import { PlaceholderSection, ResourceSection, SystemPlaceholderSection, } from "./mail-left-rail.js";
|
|
14
14
|
import { MissingLocaleCanvas } from "./mail-missing-locale.js";
|
|
15
15
|
import { MailOriginBar } from "./mail-origin-bar.js";
|
|
16
16
|
import { PdfLinkModal } from "./mail-pdf-link-modal.js";
|
|
@@ -158,7 +158,7 @@ export function MailEditorBody(props) {
|
|
|
158
158
|
names,
|
|
159
159
|
labels: {
|
|
160
160
|
...systemLabelMap(t, validation?.systemPlaceholders ?? []),
|
|
161
|
-
...variableLabelMap(validation?.variables ?? {}),
|
|
161
|
+
...variableLabelMap(t, validation?.variables ?? {}),
|
|
162
162
|
},
|
|
163
163
|
system: validation?.systemPlaceholders ?? [],
|
|
164
164
|
};
|
|
@@ -173,7 +173,7 @@ export function MailEditorBody(props) {
|
|
|
173
173
|
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: () => {
|
|
174
174
|
setResetError(null);
|
|
175
175
|
void onReload();
|
|
176
|
-
}, onResetError: setResetError })), testMail.state.kind === "sent" && (_jsx(Banner, { variant: "primary", testId: "mail-test-sent", children: t("documentDesigner.mail.testMail.sent", { subject: testMail.state.subject }) })), testMail.state.kind === "error" && (_jsx(Banner, { variant: "error", testId: "mail-test-error", children: testMail.state.text })), resetError !== null && (_jsx(Banner, { variant: "error", testId: "mail-reset-error", children: resetError })), resourceError !== null && (_jsx(Banner, { variant: "error", testId: "mail-resource-error", children: resourceError })), mode === "edit" ? (_jsx(EditorBody, { left: _jsxs(_Fragment, { children: [_jsx(PlaceholderSection, { validation: railValidation, onInsert: insert.insertPlaceholder }), _jsx(SystemPlaceholderSection, { validation: railValidation, onInsert: insert.insertPlaceholder }), _jsx(ResourceSection, { resources: resources, pdfTitles: pdfTitles, canUpload: uploadTemplateId !== null, onUpload: () => setUploadOpen(true), onInsert: insert.insertResource, onRemove: (key) => setDialog({ kind: "remove", key }) })] }), center: locked ? (_jsx(MissingLocaleCanvas, { slug: slug, locale: locale, fallbackLocale: props.fallbackLocale, copyScope: isSystemAdmin ? "system" : "tenant", onCreateBlank: () => setCreating(true), onCopied: () => void onReload() })) : (_jsx(MailEditCenter, { draft: draft, vocabulary: vocabulary, resourcePreviews: resourcePreviews, insert: insert, readOnly: false, fieldErrors: fieldErrors, onChange: update }, editorEpoch)), right: _jsxs(_Fragment, { children: [_jsx(PropertiesSection, { draft: draft, layouts: layouts.rows, onChange: update }), _jsx(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: {
|
|
176
|
+
}, 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: {
|
|
177
177
|
slug,
|
|
178
178
|
kind: "mail",
|
|
179
179
|
locale,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MailValidation } from "./mail-types.
|
|
1
|
+
import type { MailValidation } from "./mail-types.ts";
|
|
2
2
|
type Translate = (key: string, params?: Readonly<Record<string, unknown>>) => string;
|
|
3
3
|
export declare function footerMessage(t: Translate, validation: MailValidation, previewMode: boolean): {
|
|
4
4
|
readonly tone: "bad" | "warn" | "ok";
|
package/dist/web/mail-footer.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { variableLabelText } from "./used-by-label.js";
|
|
1
2
|
function issueText(t, issue) {
|
|
2
3
|
const translated = t(issue.i18nKey, issue.params);
|
|
3
4
|
return translated === issue.i18nKey ? issue.code : translated;
|
|
4
5
|
}
|
|
5
|
-
function missingLabels(validation) {
|
|
6
|
+
function missingLabels(t, validation) {
|
|
6
7
|
return validation.missingRequired
|
|
7
|
-
.map((name) => validation.variables[name]?.label
|
|
8
|
+
.map((name) => variableLabelText(t, name, validation.variables[name]?.label))
|
|
8
9
|
.join(", ");
|
|
9
10
|
}
|
|
10
11
|
// The footer sentence: missing required placeholders first, then other blocking issues,
|
|
@@ -15,7 +16,7 @@ export function footerMessage(t, validation, previewMode) {
|
|
|
15
16
|
tone: "bad",
|
|
16
17
|
text: t(previewMode
|
|
17
18
|
? "documentDesigner.mail.footer.missingPreview"
|
|
18
|
-
: "documentDesigner.mail.footer.missing", { names: missingLabels(validation) }),
|
|
19
|
+
: "documentDesigner.mail.footer.missing", { names: missingLabels(t, validation) }),
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
const blocking = [...validation.issues, ...validation.publishIssues][0];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
-
import type { LinkedResourceView, MailValidation } from "./mail-types.
|
|
2
|
+
import type { LinkedResourceView, MailValidation } from "./mail-types.ts";
|
|
3
3
|
export type PlaceholderSource = Pick<MailValidation, "variables" | "used" | "missingRequired" | "systemPlaceholders">;
|
|
4
4
|
export declare function PlaceholderSection({ validation, onInsert, idPrefix, }: {
|
|
5
5
|
readonly validation: PlaceholderSource | null;
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
4
4
|
import { LAYOUT_OWNED_PLACEHOLDERS, PLACEHOLDER_SYMBOL, SYSTEM_PLACEHOLDER_TYPE, systemPlaceholderLabel, } from "./mail-draft.js";
|
|
5
5
|
import { RailHint, RailRow, RailSection } from "./rail.js";
|
|
6
|
+
import { variableLabelText } from "./used-by-label.js";
|
|
6
7
|
function placeholderStatus(t, validation, name) {
|
|
7
8
|
if (validation.missingRequired.includes(name)) {
|
|
8
9
|
return { text: t("documentDesigner.mail.placeholder.missing"), tone: "bad" };
|
|
@@ -15,7 +16,7 @@ export function PlaceholderSection({ validation, onInsert, idPrefix = "mail", })
|
|
|
15
16
|
return (_jsxs(RailSection, { title: t("documentDesigner.mail.placeholder.title"), testId: `${idPrefix}-editor-placeholders`, children: [validation !== null &&
|
|
16
17
|
entries.map(([name, variable]) => {
|
|
17
18
|
const status = placeholderStatus(t, validation, name);
|
|
18
|
-
return (_jsx(RailRow, { testId: `${idPrefix}-placeholder-${name}`, symbol: PLACEHOLDER_SYMBOL[variable.type], label: variable.label
|
|
19
|
+
return (_jsx(RailRow, { testId: `${idPrefix}-placeholder-${name}`, symbol: PLACEHOLDER_SYMBOL[variable.type], label: variableLabelText(t, name, variable.label), nameHint: name, required: variable.required === true, bad: validation.missingRequired.includes(name), trailing: status.text, trailingTone: status.tone, onPress: () => onInsert(name) }, name));
|
|
19
20
|
}), _jsx(RailHint, { children: t("documentDesigner.mail.placeholder.hint") })] }));
|
|
20
21
|
}
|
|
21
22
|
export function SystemPlaceholderSection({ validation, onInsert, idPrefix = "mail", isLayoutOwned = (name) => LAYOUT_OWNED_PLACEHOLDERS.has(name), }) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { TemplateRowView } from "./mail-types.
|
|
2
|
+
import type { TemplateRowView } from "./mail-types.ts";
|
|
3
3
|
type Translate = (key: string, params?: Readonly<Record<string, unknown>>) => string;
|
|
4
4
|
export declare function languageName(t: Translate, locale: string): string;
|
|
5
5
|
export declare function MailOriginBar({ row, systemVersion, locale, isSystemAdmin, creating, controls, onReload, onResetError, }: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { OverviewRowView } from "./overview-row.
|
|
2
|
+
import type { OverviewRowView } from "./overview-row.ts";
|
|
3
3
|
export declare function PdfLinkModal({ open, onOpenChange, templates, ...form }: {
|
|
4
4
|
readonly open: boolean;
|
|
5
5
|
readonly onOpenChange: (open: boolean) => void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { MailPreview, MailValidation } from "./mail-types.
|
|
3
|
-
import type { OwnAddress } from "./use-own-address.
|
|
2
|
+
import type { MailPreview, MailValidation } from "./mail-types.ts";
|
|
3
|
+
import type { OwnAddress } from "./use-own-address.ts";
|
|
4
4
|
export type TestMailControls = {
|
|
5
5
|
readonly pending: boolean;
|
|
6
6
|
readonly onSend: () => void;
|
|
@@ -5,6 +5,7 @@ import { useState } from "react";
|
|
|
5
5
|
import { displayValue } from "./mail-draft.js";
|
|
6
6
|
import { usePreviewRecordPicker } from "./preview-record-picker.js";
|
|
7
7
|
import { RailField, RailHint, RailRow, RailSection } from "./rail.js";
|
|
8
|
+
import { variableLabelText } from "./used-by-label.js";
|
|
8
9
|
const noop = () => undefined;
|
|
9
10
|
// Check params list names as arrays; the message templates expect one string.
|
|
10
11
|
function checkText(t, check) {
|
|
@@ -21,7 +22,7 @@ function SampleValues({ preview, validation, picked, }) {
|
|
|
21
22
|
const names = Object.keys(validation?.variables ?? values);
|
|
22
23
|
return (_jsx(_Fragment, { children: names.map((name) => {
|
|
23
24
|
const missing = validation?.missingRequired.includes(name) === true;
|
|
24
|
-
return (_jsx(RailRow, { testId: `mail-sample-${name}`, label: validation?.variables[name]?.label
|
|
25
|
+
return (_jsx(RailRow, { testId: `mail-sample-${name}`, label: variableLabelText(t, name, validation?.variables[name]?.label), nameHint: name, bad: missing, trailing: missing
|
|
25
26
|
? t("documentDesigner.mail.preview.missingInContent")
|
|
26
27
|
: displayValue(t, values[name]) }, name));
|
|
27
28
|
}) }));
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { EditorOrigin } from "./editor-header.
|
|
3
|
-
import { type TestMailControls } from "./mail-preview-side.
|
|
4
|
-
import type { MailValidation } from "./mail-types.
|
|
5
|
-
import type { OwnAddress } from "./use-own-address.
|
|
2
|
+
import type { EditorOrigin } from "./editor-header.tsx";
|
|
3
|
+
import { type TestMailControls } from "./mail-preview-side.tsx";
|
|
4
|
+
import type { MailValidation } from "./mail-types.ts";
|
|
5
|
+
import type { OwnAddress } from "./use-own-address.ts";
|
|
6
6
|
export type PreviewPayload = {
|
|
7
7
|
readonly slug: string;
|
|
8
8
|
readonly kind: "mail";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
-
import type { EditorVocabulary } from "./mail-edit-center.
|
|
3
|
-
import type { LinkedResourceView, MailDraft } from "./mail-types.
|
|
4
|
-
import type { OverviewRowView } from "./overview-row.
|
|
5
|
-
import type { InsertTargets } from "./use-insert-targets.
|
|
2
|
+
import type { EditorVocabulary } from "./mail-edit-center.tsx";
|
|
3
|
+
import type { LinkedResourceView, MailDraft } from "./mail-types.ts";
|
|
4
|
+
import type { OverviewRowView } from "./overview-row.ts";
|
|
5
|
+
import type { InsertTargets } from "./use-insert-targets.ts";
|
|
6
6
|
export declare function layoutOptionLabel(t: (key: string, params?: Readonly<Record<string, unknown>>) => string, layout: OverviewRowView): string;
|
|
7
7
|
export declare function AttachmentSection({ attachments, pdfTitles, canEdit, onAddPdf, onRemove, }: {
|
|
8
8
|
readonly attachments: readonly LinkedResourceView[];
|
|
@@ -11,7 +11,8 @@ export declare function AttachmentSection({ attachments, pdfTitles, canEdit, onA
|
|
|
11
11
|
readonly onAddPdf: () => void;
|
|
12
12
|
readonly onRemove: (key: string) => void;
|
|
13
13
|
}): ReactNode;
|
|
14
|
-
export declare function PropertiesSection({ draft, layouts, onChange, }: {
|
|
14
|
+
export declare function PropertiesSection({ slug, draft, layouts, onChange, }: {
|
|
15
|
+
readonly slug: string;
|
|
15
16
|
readonly draft: MailDraft;
|
|
16
17
|
readonly layouts: readonly OverviewRowView[];
|
|
17
18
|
readonly onChange: (patch: Partial<MailDraft>) => void;
|
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// @runtime client
|
|
3
3
|
import { MAIL_TONES, PlaceholderLineEditor, TemplateResolverQueries, } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
4
|
-
import { usePrimitives, useQuery, useTranslation } from "@cosmicdrift/kumiko-renderer";
|
|
4
|
+
import { useAppFeatures, useNav, usePrimitives, useQuery, useTranslation, } from "@cosmicdrift/kumiko-renderer";
|
|
5
5
|
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
6
|
-
import { DEFAULT_LAYOUT_SLUG } from "../constants.js";
|
|
6
|
+
import { DEFAULT_LAYOUT_SLUG, TENANT_MAIL_SENDING_SCREEN_ID } from "../constants.js";
|
|
7
7
|
import { RailField, RailHint, RailRow, RailSection } from "./rail.js";
|
|
8
8
|
import { usageParts } from "./used-by-label.js";
|
|
9
|
+
function useTemplateUsage(slug) {
|
|
10
|
+
const { data } = useQuery(TemplateResolverQueries.usage, {
|
|
11
|
+
slug,
|
|
12
|
+
kind: "mail",
|
|
13
|
+
});
|
|
14
|
+
return data?.usedBy ?? [];
|
|
15
|
+
}
|
|
16
|
+
function useHasScreen(screenId) {
|
|
17
|
+
const features = useAppFeatures();
|
|
18
|
+
return features.some((feature) => feature.screens.some((screen) => screen.id === screenId));
|
|
19
|
+
}
|
|
20
|
+
// A notification routed through the tenant transport sends from the tenant's own provider, so
|
|
21
|
+
// the hint points there instead of to the platform.
|
|
22
|
+
function SenderHint({ slug }) {
|
|
23
|
+
const t = useTranslation();
|
|
24
|
+
const nav = useNav();
|
|
25
|
+
const { Link } = usePrimitives();
|
|
26
|
+
const tenantRouted = useTemplateUsage(slug).some((usage) => usage.tenantTransport === true);
|
|
27
|
+
const hasSettingsScreen = useHasScreen(TENANT_MAIL_SENDING_SCREEN_ID);
|
|
28
|
+
if (!tenantRouted)
|
|
29
|
+
return _jsx(RailHint, { children: t("documentDesigner.mail.props.senderHint") });
|
|
30
|
+
return (_jsxs(RailHint, { children: [t("documentDesigner.mail.props.senderHintTenant"), hasSettingsScreen && (_jsxs(_Fragment, { children: [" ", _jsx(Link, { href: nav.hrefFor({ screenId: TENANT_MAIL_SENDING_SCREEN_ID }), onPress: () => nav.navigate({ screenId: TENANT_MAIL_SENDING_SCREEN_ID }), testId: "mail-sender-settings-link", children: t("documentDesigner.mail.props.senderSettingsLink") })] }))] }));
|
|
31
|
+
}
|
|
9
32
|
export function layoutOptionLabel(t, layout) {
|
|
10
33
|
// A layout has no registry title; the slug would read like an id, so name it by kind.
|
|
11
34
|
const title = layout.title === layout.slug
|
|
@@ -31,11 +54,8 @@ function layoutOptions(t, layouts) {
|
|
|
31
54
|
}
|
|
32
55
|
function UsageRows({ slug }) {
|
|
33
56
|
const t = useTranslation();
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
kind: "mail",
|
|
37
|
-
});
|
|
38
|
-
return (_jsx(_Fragment, { children: (data?.usedBy ?? []).map((usage) => {
|
|
57
|
+
const usedBy = useTemplateUsage(slug);
|
|
58
|
+
return (_jsx(_Fragment, { children: usedBy.map((usage) => {
|
|
39
59
|
const parts = usageParts(t, usage);
|
|
40
60
|
return (_jsx(RailRow, { label: t("documentDesigner.mail.usage.line", parts), testId: "mail-usage-row" }, `${usage.feature}:${usage.notification}`));
|
|
41
61
|
}) }));
|
|
@@ -53,12 +73,12 @@ export function AttachmentSection({ attachments, pdfTitles, canEdit, onAddPdf, o
|
|
|
53
73
|
? attachment.fileName
|
|
54
74
|
: 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") })] }));
|
|
55
75
|
}
|
|
56
|
-
export function PropertiesSection({ draft, layouts, onChange, }) {
|
|
76
|
+
export function PropertiesSection({ slug, draft, layouts, onChange, }) {
|
|
57
77
|
const t = useTranslation();
|
|
58
78
|
const { Field, Input } = usePrimitives();
|
|
59
79
|
const layoutValue = draft.layoutSlug === "" ? DEFAULT_LAYOUT_SLUG : draft.layoutSlug;
|
|
60
80
|
const categoryValue = draft.category === "" ? "transactional" : draft.category;
|
|
61
|
-
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: layoutOptions(t, layouts) }) }) }), _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(
|
|
81
|
+
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: layoutOptions(t, layouts) }) }) }), _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(SenderHint, { slug: slug }), _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: [
|
|
62
82
|
{ value: "transactional", label: t("documentDesigner.mail.props.transactional") },
|
|
63
83
|
{ value: "marketing", label: t("documentDesigner.mail.props.marketing") },
|
|
64
84
|
] }) }) }), _jsx(RailHint, { children: t("documentDesigner.mail.props.categoryHint") })] }));
|
package/dist/web/mail-types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ValidationIssue } from "./layout-types.
|
|
2
|
-
import type { ServedVersionView } from "./types.
|
|
1
|
+
import type { ValidationIssue } from "./layout-types.ts";
|
|
2
|
+
import type { ServedVersionView } from "./types.ts";
|
|
3
3
|
export type PlaceholderType = "text" | "money" | "date" | "percent" | "link" | "list";
|
|
4
4
|
export type MailVariable = {
|
|
5
5
|
readonly type: PlaceholderType;
|
package/dist/web/origin-bar.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { LayoutKind, LayoutSourceView, TemplateDiff, TenantOverrideCounts } from "./layout-types.
|
|
2
|
+
import type { LayoutKind, LayoutSourceView, TemplateDiff, TenantOverrideCounts } from "./layout-types.ts";
|
|
3
3
|
export declare function DiffView({ data, mode, }: {
|
|
4
4
|
readonly data: TemplateDiff;
|
|
5
5
|
readonly mode?: "diff" | "full";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type DocumentEditorHandle, type DocumentResourcePreview } from "@cosmicdrift/kumiko-bundled-features/template-resolver/web";
|
|
2
2
|
import { type ReactNode } from "react";
|
|
3
|
-
import { type PdfMetaDraft } from "./pdf-meta.
|
|
4
|
-
import type { LayoutRegionsHtml } from "./types.
|
|
3
|
+
import { type PdfMetaDraft } from "./pdf-meta.ts";
|
|
4
|
+
import type { LayoutRegionsHtml } from "./types.ts";
|
|
5
5
|
export type PageCanvasProps = {
|
|
6
6
|
readonly meta: PdfMetaDraft;
|
|
7
7
|
readonly content: string;
|
package/dist/web/pdf-footer.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { FooterTone } from "./editor-shell.
|
|
2
|
-
import type { ValidationResult } from "./types.
|
|
1
|
+
import type { FooterTone } from "./editor-shell.tsx";
|
|
2
|
+
import type { ValidationResult } from "./types.ts";
|
|
3
3
|
type Translate = (key: string, params?: Readonly<Record<string, unknown>>) => string;
|
|
4
4
|
export type GeneratedProbe = {
|
|
5
5
|
readonly pages: number;
|
package/dist/web/pdf-footer.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { variableLabelText } from "./used-by-label.js";
|
|
1
2
|
function formatSize(bytes) {
|
|
2
3
|
return bytes < 1024 * 1024
|
|
3
4
|
? `${Math.max(1, Math.round(bytes / 1024))} KB`
|
|
@@ -8,7 +9,7 @@ function formatSize(bytes) {
|
|
|
8
9
|
export function pdfFooterMessage(t, validation, probe) {
|
|
9
10
|
if (validation.missingRequired.length > 0) {
|
|
10
11
|
const names = validation.missingRequired
|
|
11
|
-
.map((name) => validation.variables[name]?.label
|
|
12
|
+
.map((name) => variableLabelText(t, name, validation.variables[name]?.label))
|
|
12
13
|
.join(", ");
|
|
13
14
|
return { tone: "bad", text: t("documentDesigner.validation.missingRequired", { name: names }) };
|
|
14
15
|
}
|
|
@@ -115,7 +115,7 @@ export function PdfTemplateEditor({ slug, locale: requestedLocale, onLocaleChang
|
|
|
115
115
|
const variables = validation?.variables ?? NO_VARIABLES;
|
|
116
116
|
const systemVariables = useMemo(() => visibleSystemPlaceholders(validation?.systemPlaceholders ?? []), [validation?.systemPlaceholders]);
|
|
117
117
|
const systemLabels = useMemo(() => systemLabelMap(t, systemVariables), [t, systemVariables]);
|
|
118
|
-
const variableLabels = useMemo(() => ({ ...systemLabels, ...variableLabelMap(variables) }), [systemLabels, variables]);
|
|
118
|
+
const variableLabels = useMemo(() => ({ ...systemLabels, ...variableLabelMap(t, variables) }), [systemLabels, variables, t]);
|
|
119
119
|
const variableNames = useMemo(() => Object.keys(variables), [variables]);
|
|
120
120
|
const linkedResources = detail?.linkedResources ?? null;
|
|
121
121
|
// The editor shows resource images from the stored copies; no extra request is made here.
|
package/dist/web/rail.d.ts
CHANGED
|
@@ -13,9 +13,11 @@ declare const TRAILING_TONE: {
|
|
|
13
13
|
readonly bad: "text-status-bad";
|
|
14
14
|
readonly muted: "text-muted-foreground";
|
|
15
15
|
};
|
|
16
|
-
export declare function RailRow({ symbol, label, required, trailing, trailingTone, action, bad, testId, onPress, }: {
|
|
16
|
+
export declare function RailRow({ symbol, label, nameHint, required, trailing, trailingTone, action, bad, testId, onPress, }: {
|
|
17
17
|
readonly symbol?: string;
|
|
18
18
|
readonly label: string;
|
|
19
|
+
/** Technical name behind a translated label, shown as tooltip. */
|
|
20
|
+
readonly nameHint?: string;
|
|
19
21
|
readonly required?: boolean;
|
|
20
22
|
readonly trailing?: ReactNode;
|
|
21
23
|
readonly trailingTone?: keyof typeof TRAILING_TONE;
|
package/dist/web/rail.js
CHANGED
|
@@ -12,8 +12,8 @@ const TRAILING_TONE = {
|
|
|
12
12
|
bad: "text-status-bad",
|
|
13
13
|
muted: "text-muted-foreground",
|
|
14
14
|
};
|
|
15
|
-
export function RailRow({ symbol, label, required, trailing, trailingTone = "muted", action, bad, testId, onPress, }) {
|
|
16
|
-
const content = (_jsxs(_Fragment, { children: [symbol !== undefined && (_jsx("span", { className: "inline-flex h-[18px] w-[22px] shrink-0 items-center justify-center rounded-sm bg-muted font-mono text-xs text-muted-foreground", children: symbol })), _jsxs("span", { className: cn("min-w-0 grow break-words text-sm", trailing !== undefined && "max-w-[65%] shrink-0", bad === true && "text-status-bad"), children: [label, required === true && _jsx("span", { className: "font-semibold text-status-bad", children: " *" })] }), trailing !== undefined && (_jsx("span", { className: cn("min-w-0 truncate text-right text-xs", bad === true ? "text-status-bad" : TRAILING_TONE[trailingTone]), title: typeof trailing === "string" ? trailing : undefined, children: trailing }))] }));
|
|
15
|
+
export function RailRow({ symbol, label, nameHint, required, trailing, trailingTone = "muted", action, bad, testId, onPress, }) {
|
|
16
|
+
const content = (_jsxs(_Fragment, { children: [symbol !== undefined && (_jsx("span", { className: "inline-flex h-[18px] w-[22px] shrink-0 items-center justify-center rounded-sm bg-muted font-mono text-xs text-muted-foreground", children: symbol })), _jsxs("span", { className: cn("min-w-0 grow break-words text-sm", trailing !== undefined && "max-w-[65%] shrink-0", bad === true && "text-status-bad"), title: nameHint !== undefined && nameHint !== label ? nameHint : undefined, children: [label, required === true && _jsx("span", { className: "font-semibold text-status-bad", children: " *" })] }), trailing !== undefined && (_jsx("span", { className: cn("min-w-0 truncate text-right text-xs", bad === true ? "text-status-bad" : TRAILING_TONE[trailingTone]), title: typeof trailing === "string" ? trailing : undefined, children: trailing }))] }));
|
|
17
17
|
const rowClass = "flex min-h-10 min-w-0 grow items-center gap-2 px-3.5 text-left";
|
|
18
18
|
return (_jsxs("div", { className: cn("flex items-center border-b", bad === true && "bg-status-bad-surface"), "data-testid": testId, "data-bad": bad === true ? "true" : undefined, children: [onPress === undefined ? (_jsx("div", { className: rowClass, children: content })) : (_jsx("button", { type: "button", onClick: onPress, className: cn(rowClass, "hover:bg-muted"), ...(testId !== undefined && { "data-testid": `${testId}-insert` }), children: content })), action !== undefined && _jsx("div", { className: "shrink-0 pr-2", children: action })] }));
|
|
19
19
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
-
import type { EditorVocabulary } from "./mail-edit-center.
|
|
3
|
-
import type { OverviewRowView } from "./overview-row.
|
|
4
|
-
import { type PdfMetaDraft } from "./pdf-meta.
|
|
5
|
-
import { type TemplateStatus } from "./types.
|
|
6
|
-
import type { InsertTargets } from "./use-insert-targets.
|
|
2
|
+
import type { EditorVocabulary } from "./mail-edit-center.tsx";
|
|
3
|
+
import type { OverviewRowView } from "./overview-row.ts";
|
|
4
|
+
import { type PdfMetaDraft } from "./pdf-meta.ts";
|
|
5
|
+
import { type TemplateStatus } from "./types.ts";
|
|
6
|
+
import type { InsertTargets } from "./use-insert-targets.ts";
|
|
7
7
|
export type VersionInfo = {
|
|
8
8
|
readonly id: string;
|
|
9
9
|
readonly version: number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { StatusBadgeTone } from "@cosmicdrift/kumiko-renderer-web";
|
|
2
|
-
import type { OverviewRowView } from "./overview-row.
|
|
2
|
+
import type { OverviewRowView } from "./overview-row.ts";
|
|
3
3
|
type Translate = (key: string, params?: Readonly<Record<string, unknown>>) => string;
|
|
4
4
|
export declare function statusTone(status: OverviewRowView["status"]): StatusBadgeTone;
|
|
5
5
|
export declare function statusLabel(t: Translate, status: OverviewRowView["status"], version: number): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TemplateListRow, TemplateScope } from "./types.
|
|
1
|
+
import type { TemplateListRow, TemplateScope } from "./types.ts";
|
|
2
2
|
/**
|
|
3
3
|
* The row the editor works on. A tenant sees its own customisation, or the
|
|
4
4
|
* system row as the starting point for one; a SystemAdmin edits the system row.
|
package/dist/web/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FieldIssue } from "@cosmicdrift/kumiko-headless";
|
|
2
|
-
import type { TemplateDiff } from "./layout-types.
|
|
2
|
+
import type { TemplateDiff } from "./layout-types.ts";
|
|
3
3
|
export type TemplateScope = "system" | "tenant";
|
|
4
4
|
export type TemplateStatus = "draft" | "active" | "archived";
|
|
5
5
|
export declare const DOCUMENT_PDF_KIND: "document-pdf";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MailDraft, TemplateRowView } from "./mail-types.
|
|
1
|
+
import type { MailDraft, TemplateRowView } from "./mail-types.ts";
|
|
2
2
|
export type SaveOutcome = "saved" | "saved-not-published" | "failed";
|
|
3
3
|
export type MailPersistence = {
|
|
4
4
|
readonly save: (draft: MailDraft, publish: boolean) => Promise<SaveOutcome>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { OverviewRowView, OverviewUsageView } from "./overview-row.
|
|
1
|
+
import type { OverviewRowView, OverviewUsageView } from "./overview-row.ts";
|
|
2
2
|
type Translate = (key: string, params?: Readonly<Record<string, unknown>>) => string;
|
|
3
|
+
export declare function variableLabelText(t: Translate, name: string, label: string | undefined): string;
|
|
3
4
|
export declare function templateTitle(t: Translate, row: Pick<OverviewRowView, "title" | "slug" | "kind">): string;
|
|
4
5
|
export declare function usageParts(t: Translate, usage: OverviewUsageView): {
|
|
5
6
|
readonly feature: string;
|
|
@@ -5,6 +5,10 @@ function translateOrRaw(t, key, raw) {
|
|
|
5
5
|
const translated = t(key);
|
|
6
6
|
return translated === key ? raw : translated;
|
|
7
7
|
}
|
|
8
|
+
// A declared label is an i18n key or plain text; without one the variable name stands in.
|
|
9
|
+
export function variableLabelText(t, name, label) {
|
|
10
|
+
return label === undefined ? name : translateOrRaw(t, label, label);
|
|
11
|
+
}
|
|
8
12
|
const NOTIFICATION_SLUG = /^([^:]+):notify:([^:]+)$/;
|
|
9
13
|
// A seeded system mail has no stored title, so its notification label from the
|
|
10
14
|
// owning feature names it; the slug stays the last resort.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-document-designer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.370.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.370.0",
|
|
23
|
+
"@cosmicdrift/kumiko-framework": "0.370.0",
|
|
24
|
+
"@cosmicdrift/kumiko-headless": "0.370.0",
|
|
25
|
+
"@cosmicdrift/kumiko-renderer": "0.370.0",
|
|
26
|
+
"@cosmicdrift/kumiko-renderer-puppeteer-client": "0.370.0",
|
|
27
|
+
"@cosmicdrift/kumiko-renderer-web": "0.370.0",
|
|
28
|
+
"@cosmicdrift/kumiko-types": "0.370.0",
|
|
29
29
|
"pdfjs-dist": "6.3.289",
|
|
30
30
|
"react": "^19.2.6"
|
|
31
31
|
},
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.370.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "Designer addresses the user informally in German, points tenant-routed senders to Email sending and renders chips inside buttons",
|
|
6
|
+
"detail": "All German document designer strings use du like the rest of the framework; the formal locale overrides keep Sie. For a notification with tenantTransport the sender hint in the mail editor says the address comes from the tenant's Email sending settings and links to that screen when the app mounts it; TemplateUsageEntry gains tenantTransport. A placeholder chip inside a button block is now drawn in inverted colors so the button text stays readable in the editor."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.365.0",
|
|
4
10
|
"type": "improvement",
|