@fonderie/vue-courier-admin-screens 0.8.0 → 0.10.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/index.cjs +299 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -6
- package/dist/index.d.ts +36 -6
- package/dist/index.js +307 -81
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -28,6 +28,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
28
28
|
|
|
29
29
|
// src/screens/TemplateCreateScreen.ts
|
|
30
30
|
var import_client = require("@fonderie/client");
|
|
31
|
+
var import_vue_courier_admin = require("@fonderie/vue-courier-admin");
|
|
31
32
|
var import_vue = require("vue");
|
|
32
33
|
|
|
33
34
|
// src/styles.ts
|
|
@@ -269,6 +270,16 @@ var styles = {
|
|
|
269
270
|
// src/screens/TemplateCreateScreen.ts
|
|
270
271
|
var TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;
|
|
271
272
|
var LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
|
|
273
|
+
var IMPLICIT = /* @__PURE__ */ new Set(["subject", "preheader", "brandName"]);
|
|
274
|
+
var PREVIEW_DEBOUNCE_MS = 500;
|
|
275
|
+
var notice = {
|
|
276
|
+
fontSize: "13px",
|
|
277
|
+
color: "var(--fonderie-text,#171717)",
|
|
278
|
+
background: "color-mix(in srgb, var(--fonderie-warning,#f5a623) 16%, transparent)",
|
|
279
|
+
borderRadius: "6px",
|
|
280
|
+
padding: "8px 12px",
|
|
281
|
+
margin: "12px 0 0"
|
|
282
|
+
};
|
|
272
283
|
var TemplateCreateScreen = (0, import_vue.defineComponent)({
|
|
273
284
|
name: "FonderieTemplateCreateScreen",
|
|
274
285
|
props: {
|
|
@@ -297,6 +308,14 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
|
|
|
297
308
|
const text = (0, import_vue.ref)("");
|
|
298
309
|
const error = (0, import_vue.ref)(null);
|
|
299
310
|
const busy = (0, import_vue.ref)(false);
|
|
311
|
+
const source = (0, import_vue.ref)(null);
|
|
312
|
+
const {
|
|
313
|
+
preview,
|
|
314
|
+
isPreviewing,
|
|
315
|
+
error: previewError,
|
|
316
|
+
renderPreview
|
|
317
|
+
} = (0, import_vue_courier_admin.useTemplatePreview)(props.client);
|
|
318
|
+
let variables = [];
|
|
300
319
|
const tr = (key, params) => (0, import_client.createAdminT)(props.locale)(key, params);
|
|
301
320
|
(0, import_vue.onMounted)(() => {
|
|
302
321
|
if (!props.type) return;
|
|
@@ -304,8 +323,50 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
|
|
|
304
323
|
subject.value = result.subject ?? "";
|
|
305
324
|
html.value = result.html ?? "";
|
|
306
325
|
text.value = result.text;
|
|
326
|
+
source.value = {
|
|
327
|
+
subject: result.subject ?? "",
|
|
328
|
+
html: result.html ?? "",
|
|
329
|
+
text: result.text
|
|
330
|
+
};
|
|
307
331
|
}).catch(() => void 0);
|
|
308
332
|
});
|
|
333
|
+
let timer;
|
|
334
|
+
(0, import_vue.watch)([newType, targetLocale, subject, html, text], () => {
|
|
335
|
+
clearTimeout(timer);
|
|
336
|
+
const type = addingLocale ? props.type : newType.value.trim();
|
|
337
|
+
const l = targetLocale.value.trim();
|
|
338
|
+
if (!TYPE_RE.test(type) || !text.value.trim()) return;
|
|
339
|
+
timer = setTimeout(async () => {
|
|
340
|
+
const input = {
|
|
341
|
+
text: text.value,
|
|
342
|
+
...subject.value ? { subject: subject.value } : {},
|
|
343
|
+
...html.value ? { html: html.value } : {}
|
|
344
|
+
};
|
|
345
|
+
const render = () => renderPreview(
|
|
346
|
+
type,
|
|
347
|
+
{ ...input, data: Object.fromEntries(variables.map((v) => [v, v])) },
|
|
348
|
+
LOCALE_RE.test(l) ? l : null
|
|
349
|
+
);
|
|
350
|
+
try {
|
|
351
|
+
const result = await render();
|
|
352
|
+
const used = result.variables.filter((v) => !IMPLICIT.has(v));
|
|
353
|
+
if (used.some((v) => !variables.includes(v))) {
|
|
354
|
+
variables = used;
|
|
355
|
+
await render();
|
|
356
|
+
}
|
|
357
|
+
} catch {
|
|
358
|
+
}
|
|
359
|
+
}, PREVIEW_DEBOUNCE_MS);
|
|
360
|
+
});
|
|
361
|
+
const untranslated = (0, import_vue.computed)(() => {
|
|
362
|
+
const src = source.value;
|
|
363
|
+
if (!src) return [];
|
|
364
|
+
return [
|
|
365
|
+
subject.value && subject.value === src.subject ? tr("templates.subject") : null,
|
|
366
|
+
html.value && html.value === src.html ? tr("templates.htmlBody") : null,
|
|
367
|
+
text.value === src.text ? tr("templates.textBody") : null
|
|
368
|
+
].filter((f) => f !== null);
|
|
369
|
+
});
|
|
309
370
|
async function submit(e) {
|
|
310
371
|
e.preventDefault();
|
|
311
372
|
error.value = null;
|
|
@@ -363,7 +424,7 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
|
|
|
363
424
|
}
|
|
364
425
|
})
|
|
365
426
|
];
|
|
366
|
-
return () => (0, import_vue.h)("div", { style:
|
|
427
|
+
return () => (0, import_vue.h)("div", { style: styles.container }, [
|
|
367
428
|
(0, import_vue.h)(
|
|
368
429
|
"h1",
|
|
369
430
|
{ style: styles.title },
|
|
@@ -374,35 +435,69 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
|
|
|
374
435
|
{ style: { ...styles.meta, lineHeight: 1.5 } },
|
|
375
436
|
addingLocale ? tr("templates.create.hintLocale") : tr("templates.create.hintNew")
|
|
376
437
|
),
|
|
377
|
-
(0, import_vue.h)("
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
"
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
438
|
+
(0, import_vue.h)("div", { style: styles.split }, [
|
|
439
|
+
(0, import_vue.h)("form", { style: styles.form, onSubmit: (ev) => void submit(ev) }, [
|
|
440
|
+
...addingLocale ? [] : field("new-template-type", tr("templates.create.type"), newType, {
|
|
441
|
+
placeholder: "weekly-digest"
|
|
442
|
+
}),
|
|
443
|
+
...field(
|
|
444
|
+
"new-template-locale",
|
|
445
|
+
addingLocale ? tr("templates.create.locale") : tr("templates.create.localeOptional"),
|
|
446
|
+
targetLocale,
|
|
447
|
+
{ placeholder: "fr", list: "new-template-locales" }
|
|
448
|
+
),
|
|
449
|
+
(0, import_vue.h)(
|
|
450
|
+
"datalist",
|
|
451
|
+
{ id: "new-template-locales" },
|
|
452
|
+
[...new Set(props.locales)].map((l) => (0, import_vue.h)("option", { key: l, value: l }))
|
|
453
|
+
),
|
|
454
|
+
...field("new-template-subject", tr("templates.subject"), subject),
|
|
455
|
+
...field("new-template-html", tr("templates.htmlBody"), html, {
|
|
456
|
+
textarea: true,
|
|
457
|
+
minHeight: "160px"
|
|
458
|
+
}),
|
|
459
|
+
...field("new-template-text", tr("templates.textBody"), text, {
|
|
460
|
+
textarea: true,
|
|
461
|
+
minHeight: "100px"
|
|
462
|
+
}),
|
|
463
|
+
untranslated.value.length > 0 ? (0, import_vue.h)(
|
|
464
|
+
"p",
|
|
465
|
+
{ style: notice },
|
|
466
|
+
tr("templates.create.untranslated", { fields: untranslated.value.join(", ") })
|
|
467
|
+
) : null,
|
|
468
|
+
error.value ? (0, import_vue.h)("p", { style: styles.error, role: "alert" }, error.value) : null,
|
|
469
|
+
(0, import_vue.h)(
|
|
470
|
+
"button",
|
|
471
|
+
{
|
|
472
|
+
type: "submit",
|
|
473
|
+
disabled: busy.value,
|
|
474
|
+
style: { ...styles.button, marginTop: "16px" }
|
|
475
|
+
},
|
|
476
|
+
busy.value ? tr("templates.create.creating") : addingLocale ? tr("templates.create.submitLocale") : tr("templates.create.submitNew")
|
|
477
|
+
)
|
|
478
|
+
]),
|
|
479
|
+
(0, import_vue.h)("div", { style: styles.previewColumn }, [
|
|
480
|
+
(0, import_vue.h)("div", { style: styles.previewHeader }, [
|
|
481
|
+
(0, import_vue.h)("span", { style: styles.label }, tr("templates.editor.preview")),
|
|
482
|
+
(0, import_vue.h)(
|
|
483
|
+
"span",
|
|
484
|
+
{ style: styles.meta },
|
|
485
|
+
isPreviewing.value ? tr("templates.editor.rendering") : tr("templates.editor.live")
|
|
486
|
+
)
|
|
487
|
+
]),
|
|
488
|
+
previewError.value ? (0, import_vue.h)("p", { style: styles.error, role: "alert" }, previewError.value.explanation) : null,
|
|
489
|
+
preview.value?.subject ? (0, import_vue.h)("p", { style: styles.previewSubject }, preview.value.subject) : null,
|
|
490
|
+
preview.value?.html ? (
|
|
491
|
+
// sandbox= — no scripts, no same-origin: operator-authored HTML must
|
|
492
|
+
// never execute in the console's origin.
|
|
493
|
+
(0, import_vue.h)("iframe", {
|
|
494
|
+
title: tr("templates.editor.previewTitle"),
|
|
495
|
+
style: styles.previewFrame,
|
|
496
|
+
sandbox: "",
|
|
497
|
+
srcdoc: preview.value.html
|
|
498
|
+
})
|
|
499
|
+
) : preview.value ? (0, import_vue.h)("pre", { style: styles.previewText }, preview.value.text) : (0, import_vue.h)("p", { style: styles.meta }, tr("templates.editor.nothingRendered"))
|
|
500
|
+
])
|
|
406
501
|
])
|
|
407
502
|
]);
|
|
408
503
|
}
|
|
@@ -410,10 +505,38 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
|
|
|
410
505
|
|
|
411
506
|
// src/screens/TemplateEditorScreen.ts
|
|
412
507
|
var import_client2 = require("@fonderie/client");
|
|
413
|
-
var
|
|
508
|
+
var import_vue_courier_admin2 = require("@fonderie/vue-courier-admin");
|
|
414
509
|
var import_vue2 = require("vue");
|
|
415
|
-
var
|
|
416
|
-
var
|
|
510
|
+
var PREVIEW_DEBOUNCE_MS2 = 500;
|
|
511
|
+
var IMPLICIT2 = /* @__PURE__ */ new Set(["subject", "preheader", "brandName"]);
|
|
512
|
+
var tab = {
|
|
513
|
+
height: "32px",
|
|
514
|
+
padding: "0 10px",
|
|
515
|
+
background: "none",
|
|
516
|
+
border: "none",
|
|
517
|
+
borderBottom: "2px solid transparent",
|
|
518
|
+
color: "var(--fonderie-text-muted,#5c5c5c)",
|
|
519
|
+
fontSize: "13px",
|
|
520
|
+
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)',
|
|
521
|
+
cursor: "pointer"
|
|
522
|
+
};
|
|
523
|
+
var tabSelected = {
|
|
524
|
+
...tab,
|
|
525
|
+
color: "var(--fonderie-text,#171717)",
|
|
526
|
+
fontWeight: 600,
|
|
527
|
+
borderBottomColor: "var(--fonderie-text,#171717)",
|
|
528
|
+
cursor: "default"
|
|
529
|
+
};
|
|
530
|
+
var tabInactive = { ...tab, opacity: 0.55, textDecoration: "line-through" };
|
|
531
|
+
var tabBar = {
|
|
532
|
+
display: "flex",
|
|
533
|
+
alignItems: "flex-end",
|
|
534
|
+
gap: "12px",
|
|
535
|
+
borderBottom: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
536
|
+
margin: "4px 0 12px",
|
|
537
|
+
paddingBottom: "6px"
|
|
538
|
+
};
|
|
539
|
+
var tabs = { display: "flex", flexWrap: "wrap", gap: "4px", marginBottom: "-7px" };
|
|
417
540
|
var TemplateEditorScreen = (0, import_vue2.defineComponent)({
|
|
418
541
|
name: "FonderieTemplateEditorScreen",
|
|
419
542
|
props: {
|
|
@@ -425,8 +548,13 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
|
|
|
425
548
|
* be edited and rolled back, never deleted — Delete is not offered.
|
|
426
549
|
*/
|
|
427
550
|
system: { type: Boolean, default: false },
|
|
428
|
-
/** Shows "+ Add locale"
|
|
551
|
+
/** Shows "+ Add locale" (emits add-locale). */
|
|
429
552
|
allowAddLocale: { type: Boolean, default: false },
|
|
553
|
+
/**
|
|
554
|
+
* Shows a tab per locale this email exists in (emits select-locale).
|
|
555
|
+
* Unsaved edits are confirmed before switching away.
|
|
556
|
+
*/
|
|
557
|
+
localeTabs: { type: Boolean, default: false },
|
|
430
558
|
/** The console language (`locale` is the template's); defaults to English. */
|
|
431
559
|
uiLocale: { type: String, default: void 0 }
|
|
432
560
|
},
|
|
@@ -434,19 +562,27 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
|
|
|
434
562
|
saved: () => true,
|
|
435
563
|
/** The template was deleted and no longer exists. */
|
|
436
564
|
deleted: () => true,
|
|
437
|
-
/**
|
|
438
|
-
|
|
565
|
+
/**
|
|
566
|
+
* Create a translation of this template. `locales` are the ones the app
|
|
567
|
+
* uses elsewhere that this email lacks — suggest them first.
|
|
568
|
+
*/
|
|
569
|
+
"add-locale": (_type, _context) => true,
|
|
570
|
+
/** Open another locale of this email. */
|
|
571
|
+
"select-locale": (_template) => true
|
|
439
572
|
},
|
|
440
573
|
setup(props, { emit }) {
|
|
441
574
|
const t = (key, params) => (0, import_client2.createAdminT)(props.uiLocale)(key, params);
|
|
442
|
-
const { template, isLoading, error, refresh } = (0,
|
|
575
|
+
const { template, isLoading, error, refresh } = (0, import_vue_courier_admin2.useTemplate)(
|
|
443
576
|
props.client,
|
|
444
577
|
props.type,
|
|
445
578
|
props.locale
|
|
446
579
|
);
|
|
447
|
-
const { saveTemplate, removeTemplate } = (0,
|
|
580
|
+
const { templates, saveTemplate, removeTemplate } = (0, import_vue_courier_admin2.useTemplates)(props.client);
|
|
581
|
+
const siblings = (0, import_vue2.computed)(
|
|
582
|
+
() => (0, import_client2.groupTemplatesByType)(templates.value).find((g) => g.type === props.type)?.entries ?? []
|
|
583
|
+
);
|
|
448
584
|
const isDeleting = (0, import_vue2.ref)(false);
|
|
449
|
-
const { revisions, rollback } = (0,
|
|
585
|
+
const { revisions, rollback } = (0, import_vue_courier_admin2.useTemplateRevisions)(props.client, props.type, props.locale);
|
|
450
586
|
const isSaving = (0, import_vue2.ref)(false);
|
|
451
587
|
const saveError = (0, import_vue2.ref)(null);
|
|
452
588
|
const subject = (0, import_vue2.ref)("");
|
|
@@ -465,7 +601,7 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
|
|
|
465
601
|
isPreviewing,
|
|
466
602
|
error: previewError,
|
|
467
603
|
renderPreview
|
|
468
|
-
} = (0,
|
|
604
|
+
} = (0, import_vue_courier_admin2.useTemplatePreview)(props.client);
|
|
469
605
|
const sampleJson = (0, import_vue2.ref)("{}");
|
|
470
606
|
const sampleError = (0, import_vue2.ref)(null);
|
|
471
607
|
async function run() {
|
|
@@ -492,7 +628,7 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
|
|
|
492
628
|
},
|
|
493
629
|
props.locale
|
|
494
630
|
);
|
|
495
|
-
const missing = result.variables.filter((v) => !
|
|
631
|
+
const missing = result.variables.filter((v) => !IMPLICIT2.has(v) && !(v in data));
|
|
496
632
|
if (missing.length > 0) {
|
|
497
633
|
sampleJson.value = JSON.stringify(
|
|
498
634
|
{ ...data, ...Object.fromEntries(missing.map((v) => [v, v])) },
|
|
@@ -505,7 +641,7 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
|
|
|
505
641
|
(0, import_vue2.watch)([template, subject, html, text, sampleJson], () => {
|
|
506
642
|
if (!template.value) return;
|
|
507
643
|
clearTimeout(previewTimer);
|
|
508
|
-
previewTimer = setTimeout(() => void run(),
|
|
644
|
+
previewTimer = setTimeout(() => void run(), PREVIEW_DEBOUNCE_MS2);
|
|
509
645
|
});
|
|
510
646
|
const dirty = (0, import_vue2.computed)(() => {
|
|
511
647
|
const tpl = template.value;
|
|
@@ -529,6 +665,51 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
|
|
|
529
665
|
isSaving.value = false;
|
|
530
666
|
}
|
|
531
667
|
}
|
|
668
|
+
const leave = () => !dirty.value || window.confirm(t("templates.editor.discardChanges"));
|
|
669
|
+
function renderTabBar() {
|
|
670
|
+
if (!props.localeTabs && !props.allowAddLocale) return null;
|
|
671
|
+
return (0, import_vue2.h)("div", { style: tabBar }, [
|
|
672
|
+
props.localeTabs ? (
|
|
673
|
+
// Each tab opens another stored row, so this is navigation, not an
|
|
674
|
+
// ARIA tablist: plain buttons, the open one marked aria-current.
|
|
675
|
+
(0, import_vue2.h)(
|
|
676
|
+
"nav",
|
|
677
|
+
{ "aria-label": t("templates.editor.locales"), style: tabs },
|
|
678
|
+
siblings.value.map((entry) => {
|
|
679
|
+
const selected = entry.locale === (props.locale ?? null);
|
|
680
|
+
return (0, import_vue2.h)(
|
|
681
|
+
"button",
|
|
682
|
+
{
|
|
683
|
+
key: entry.locale ?? "",
|
|
684
|
+
type: "button",
|
|
685
|
+
"aria-current": selected ? "true" : void 0,
|
|
686
|
+
style: selected ? tabSelected : entry.active ? tab : tabInactive,
|
|
687
|
+
title: entry.active ? void 0 : t("common.status.inactive"),
|
|
688
|
+
onClick: () => {
|
|
689
|
+
if (!selected && leave()) emit("select-locale", entry);
|
|
690
|
+
}
|
|
691
|
+
},
|
|
692
|
+
entry.locale ?? t("templates.defaultChip")
|
|
693
|
+
);
|
|
694
|
+
})
|
|
695
|
+
)
|
|
696
|
+
) : null,
|
|
697
|
+
props.allowAddLocale ? (0, import_vue2.h)(
|
|
698
|
+
"button",
|
|
699
|
+
{
|
|
700
|
+
type: "button",
|
|
701
|
+
style: { ...styles.rollbackButton, marginLeft: "auto" },
|
|
702
|
+
onClick: () => {
|
|
703
|
+
if (leave())
|
|
704
|
+
emit("add-locale", props.type, {
|
|
705
|
+
locales: (0, import_client2.missingTemplateLocales)(templates.value, props.type)
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
},
|
|
709
|
+
t("templates.editor.addLocale")
|
|
710
|
+
) : null
|
|
711
|
+
]);
|
|
712
|
+
}
|
|
532
713
|
async function handleDelete() {
|
|
533
714
|
const message = props.locale ? t("templates.editor.confirmDeleteLocale", { locale: props.locale, type: props.type }) : t("templates.editor.confirmDelete", { type: props.type });
|
|
534
715
|
if (!window.confirm(message)) return;
|
|
@@ -561,22 +742,8 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
|
|
|
561
742
|
if (error.value)
|
|
562
743
|
return (0, import_vue2.h)("p", { style: styles.error, role: "alert" }, error.value.explanation);
|
|
563
744
|
return (0, import_vue2.h)("div", { style: styles.container }, [
|
|
564
|
-
(0, import_vue2.h)(
|
|
565
|
-
|
|
566
|
-
{ style: { display: "flex", alignItems: "center", gap: "12px", flexWrap: "wrap" } },
|
|
567
|
-
[
|
|
568
|
-
(0, import_vue2.h)("h1", { style: styles.title }, props.type),
|
|
569
|
-
props.allowAddLocale && !props.locale ? (0, import_vue2.h)(
|
|
570
|
-
"button",
|
|
571
|
-
{
|
|
572
|
-
type: "button",
|
|
573
|
-
style: { ...styles.rollbackButton, marginLeft: "auto" },
|
|
574
|
-
onClick: () => emit("add-locale", props.type)
|
|
575
|
-
},
|
|
576
|
-
t("templates.editor.addLocale")
|
|
577
|
-
) : null
|
|
578
|
-
]
|
|
579
|
-
),
|
|
745
|
+
(0, import_vue2.h)("h1", { style: styles.title }, props.type),
|
|
746
|
+
renderTabBar(),
|
|
580
747
|
(0, import_vue2.h)("p", { style: styles.meta }, [
|
|
581
748
|
(0, import_vue2.h)("strong", props.locale ?? t("templates.defaultLocale")),
|
|
582
749
|
` \xB7 v${template.value?.version ?? 1}`,
|
|
@@ -669,7 +836,11 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
|
|
|
669
836
|
) : null
|
|
670
837
|
])
|
|
671
838
|
]),
|
|
672
|
-
(0, import_vue2.h)(
|
|
839
|
+
(0, import_vue2.h)(
|
|
840
|
+
"label",
|
|
841
|
+
{ style: styles.label, for: "template-sample" },
|
|
842
|
+
t("templates.editor.sampleData")
|
|
843
|
+
),
|
|
673
844
|
(0, import_vue2.h)("textarea", {
|
|
674
845
|
id: "template-sample",
|
|
675
846
|
style: styles.textarea,
|
|
@@ -685,7 +856,11 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
|
|
|
685
856
|
(0, import_vue2.h)("div", { style: styles.previewColumn }, [
|
|
686
857
|
(0, import_vue2.h)("div", { style: styles.previewHeader }, [
|
|
687
858
|
(0, import_vue2.h)("span", { style: styles.label }, t("templates.editor.preview")),
|
|
688
|
-
(0, import_vue2.h)(
|
|
859
|
+
(0, import_vue2.h)(
|
|
860
|
+
"span",
|
|
861
|
+
{ style: styles.meta },
|
|
862
|
+
isPreviewing.value ? t("templates.editor.rendering") : t("templates.editor.live")
|
|
863
|
+
)
|
|
689
864
|
]),
|
|
690
865
|
previewError.value ? (0, import_vue2.h)("p", { style: styles.error, role: "alert" }, previewError.value.explanation) : null,
|
|
691
866
|
preview.value?.subject ? (0, import_vue2.h)("p", { style: styles.previewSubject }, preview.value.subject) : null,
|
|
@@ -713,7 +888,7 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
|
|
|
713
888
|
|
|
714
889
|
// src/screens/TemplateListScreen.ts
|
|
715
890
|
var import_client3 = require("@fonderie/client");
|
|
716
|
-
var
|
|
891
|
+
var import_vue_courier_admin3 = require("@fonderie/vue-courier-admin");
|
|
717
892
|
var import_vue3 = require("vue");
|
|
718
893
|
var builtIn = {
|
|
719
894
|
fontSize: "11px",
|
|
@@ -723,6 +898,33 @@ var builtIn = {
|
|
|
723
898
|
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
724
899
|
color: "var(--fonderie-text-muted,#5c5c5c)"
|
|
725
900
|
};
|
|
901
|
+
var chip = {
|
|
902
|
+
height: "24px",
|
|
903
|
+
boxSizing: "border-box",
|
|
904
|
+
padding: "0 8px",
|
|
905
|
+
borderRadius: "6px",
|
|
906
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
907
|
+
background: "var(--fonderie-surface,#fff)",
|
|
908
|
+
color: "var(--fonderie-text,#171717)",
|
|
909
|
+
fontSize: "12px",
|
|
910
|
+
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)',
|
|
911
|
+
cursor: "pointer"
|
|
912
|
+
};
|
|
913
|
+
var chipInactive = { ...chip, opacity: 0.5, textDecoration: "line-through" };
|
|
914
|
+
var row = {
|
|
915
|
+
...styles.row,
|
|
916
|
+
display: "flex",
|
|
917
|
+
alignItems: "center",
|
|
918
|
+
gap: "8px",
|
|
919
|
+
paddingRight: "16px"
|
|
920
|
+
};
|
|
921
|
+
var rowButton = { ...styles.rowButton, width: "auto", flex: "1", minWidth: "0" };
|
|
922
|
+
var chips = {
|
|
923
|
+
display: "flex",
|
|
924
|
+
flexWrap: "wrap",
|
|
925
|
+
justifyContent: "flex-end",
|
|
926
|
+
gap: "6px"
|
|
927
|
+
};
|
|
726
928
|
var newButton = {
|
|
727
929
|
display: "inline-flex",
|
|
728
930
|
alignItems: "center",
|
|
@@ -748,32 +950,55 @@ var TemplateListScreen = (0, import_vue3.defineComponent)({
|
|
|
748
950
|
locale: { type: String, default: void 0 }
|
|
749
951
|
},
|
|
750
952
|
emits: {
|
|
751
|
-
/**
|
|
953
|
+
/**
|
|
954
|
+
* The whole row — type AND locale, and whether it is built-in. One row
|
|
955
|
+
* per email: the row opens the default locale, a locale chip that locale.
|
|
956
|
+
*/
|
|
752
957
|
"select-template": (_template) => true,
|
|
753
958
|
/** Receives the locales in use, to suggest. */
|
|
754
959
|
"create-template": (_context) => true
|
|
755
960
|
},
|
|
756
961
|
setup(props, { emit }) {
|
|
757
|
-
const { templates, isLoading, error } = (0,
|
|
962
|
+
const { templates, isLoading, error } = (0, import_vue_courier_admin3.useTemplates)(props.client);
|
|
758
963
|
const t = (key, params) => (0, import_client3.createAdminT)(props.locale)(key, params);
|
|
759
|
-
function renderRow(
|
|
760
|
-
|
|
964
|
+
function renderRow(group) {
|
|
965
|
+
const primary = group.primary;
|
|
966
|
+
return (0, import_vue3.h)("li", { key: group.type, style: row }, [
|
|
761
967
|
(0, import_vue3.h)(
|
|
762
968
|
"button",
|
|
763
969
|
{
|
|
764
970
|
type: "button",
|
|
765
|
-
style:
|
|
766
|
-
onClick: () => emit("select-template",
|
|
971
|
+
style: rowButton,
|
|
972
|
+
onClick: () => emit("select-template", primary)
|
|
767
973
|
},
|
|
768
974
|
[
|
|
769
|
-
(0, import_vue3.h)("span", { style: styles.type },
|
|
770
|
-
(0, import_vue3.h)("span", { style:
|
|
771
|
-
|
|
772
|
-
(0, import_vue3.h)("span", { style: template.active ? styles.active : styles.inactive }, [
|
|
975
|
+
(0, import_vue3.h)("span", { style: styles.type }, group.type),
|
|
976
|
+
group.system ? (0, import_vue3.h)("span", { style: builtIn }, t("templates.builtInBadge")) : null,
|
|
977
|
+
(0, import_vue3.h)("span", { style: primary.active ? styles.active : styles.inactive }, [
|
|
773
978
|
(0, import_vue3.h)("span", { style: styles.dot }),
|
|
774
|
-
|
|
979
|
+
primary.active ? t("common.status.active") : t("common.status.inactive")
|
|
775
980
|
])
|
|
776
981
|
]
|
|
982
|
+
),
|
|
983
|
+
// Siblings of the row button, not inside it: a button cannot hold buttons.
|
|
984
|
+
(0, import_vue3.h)(
|
|
985
|
+
"span",
|
|
986
|
+
{ style: chips },
|
|
987
|
+
group.entries.map((entry) => {
|
|
988
|
+
const name = entry.locale ?? t("templates.defaultChip");
|
|
989
|
+
return (0, import_vue3.h)(
|
|
990
|
+
"button",
|
|
991
|
+
{
|
|
992
|
+
key: entry.locale ?? "",
|
|
993
|
+
type: "button",
|
|
994
|
+
style: entry.active ? chip : chipInactive,
|
|
995
|
+
"aria-label": t("templates.list.openLocale", { type: group.type, locale: name }),
|
|
996
|
+
title: entry.active ? void 0 : t("common.status.inactive"),
|
|
997
|
+
onClick: () => emit("select-template", entry)
|
|
998
|
+
},
|
|
999
|
+
name
|
|
1000
|
+
);
|
|
1001
|
+
})
|
|
777
1002
|
)
|
|
778
1003
|
]);
|
|
779
1004
|
}
|
|
@@ -807,12 +1032,8 @@ var TemplateListScreen = (0, import_vue3.defineComponent)({
|
|
|
807
1032
|
) : null
|
|
808
1033
|
]
|
|
809
1034
|
),
|
|
810
|
-
(0, import_vue3.h)(
|
|
811
|
-
|
|
812
|
-
{ style: styles.hint },
|
|
813
|
-
t("templates.list.hint")
|
|
814
|
-
),
|
|
815
|
-
isLoading.value ? (0, import_vue3.h)("p", { style: styles.status }, t("templates.list.loading")) : error.value ? (0, import_vue3.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : (0, import_vue3.h)("ul", { style: styles.list }, templates.value.map(renderRow))
|
|
1035
|
+
(0, import_vue3.h)("p", { style: styles.hint }, t("templates.list.hint")),
|
|
1036
|
+
isLoading.value ? (0, import_vue3.h)("p", { style: styles.status }, t("templates.list.loading")) : error.value ? (0, import_vue3.h)("p", { style: styles.error, role: "alert" }, error.value.explanation) : (0, import_vue3.h)("ul", { style: styles.list }, (0, import_client3.groupTemplatesByType)(templates.value).map(renderRow))
|
|
816
1037
|
]);
|
|
817
1038
|
}
|
|
818
1039
|
});
|