@fonderie/vue-courier-admin-screens 0.10.0 → 0.11.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 CHANGED
@@ -292,6 +292,8 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
292
292
  type: { type: String, default: void 0 },
293
293
  /** Locales already in use, offered as suggestions. */
294
294
  locales: { type: Array, default: () => [] },
295
+ /** The system locale: the default version already is it, so adding it is refused. */
296
+ defaultLocale: { type: String, default: void 0 },
295
297
  /** The console language; defaults to English. */
296
298
  locale: { type: String, default: void 0 }
297
299
  },
@@ -319,7 +321,7 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
319
321
  const tr = (key, params) => (0, import_client.createAdminT)(props.locale)(key, params);
320
322
  (0, import_vue.onMounted)(() => {
321
323
  if (!props.type) return;
322
- props.client.getTemplate(props.type, null).then(({ result }) => {
324
+ props.client.getTemplate(props.type, null).catch(() => props.client.getBuiltInTemplate(props.type, null)).then(({ result }) => {
323
325
  subject.value = result.subject ?? "";
324
326
  html.value = result.html ?? "";
325
327
  text.value = result.text;
@@ -384,6 +386,10 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
384
386
  error.value = tr("templates.create.errorLocale");
385
387
  return;
386
388
  }
389
+ if (l && props.defaultLocale && l.toLowerCase() === props.defaultLocale.toLowerCase()) {
390
+ error.value = tr("templates.create.errorDefaultLocale", { locale: props.defaultLocale });
391
+ return;
392
+ }
387
393
  if (!text.value.trim()) {
388
394
  error.value = tr("templates.create.errorText");
389
395
  return;
@@ -527,7 +533,8 @@ var tabSelected = {
527
533
  borderBottomColor: "var(--fonderie-text,#171717)",
528
534
  cursor: "default"
529
535
  };
530
- var tabInactive = { ...tab, opacity: 0.55, textDecoration: "line-through" };
536
+ var tabInactive = { opacity: 0.55, textDecoration: "line-through" };
537
+ var tabBuiltIn = { fontStyle: "italic" };
531
538
  var tabBar = {
532
539
  display: "flex",
533
540
  alignItems: "flex-end",
@@ -577,10 +584,27 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
577
584
  props.type,
578
585
  props.locale
579
586
  );
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 ?? []
587
+ const { saveTemplate, removeTemplate } = (0, import_vue_courier_admin2.useTemplates)(props.client);
588
+ const notSaved = (0, import_vue2.computed)(() => error.value?.status === 404);
589
+ const { builtIn: builtIn2, isLoading: builtInLoading } = (0, import_vue_courier_admin2.useBuiltInTemplate)(
590
+ props.client,
591
+ props.type,
592
+ props.locale
593
+ );
594
+ const { catalog, refresh: refreshCatalog } = (0, import_vue_courier_admin2.useTemplateCatalog)(props.client);
595
+ const defaultLocale = (0, import_vue2.computed)(() => catalog.value?.defaultLocale ?? "en-US");
596
+ const email = (0, import_vue2.computed)(() => catalog.value?.emails.find((e) => e.type === props.type));
597
+ const languages = (0, import_vue2.computed)(
598
+ () => email.value ? (0, import_client2.templateLanguages)(email.value, defaultLocale.value) : []
583
599
  );
600
+ const source = (0, import_vue2.computed)(() => template.value ?? (notSaved.value ? builtIn2.value : null));
601
+ const protectedRow = (0, import_vue2.computed)(() => !props.locale && (email.value?.system ?? props.system));
602
+ const shipsHere = (0, import_vue2.computed)(
603
+ () => props.locale ? languages.value.some((l) => l.locale === props.locale && l.builtIn) : !!email.value?.builtIn.default
604
+ );
605
+ const { resolution, resolve } = (0, import_vue_courier_admin2.useTemplateResolution)(props.client);
606
+ const probe = (0, import_vue2.ref)("");
607
+ (0, import_vue2.watch)(defaultLocale, (dl) => void resolve(props.type, props.locale ?? dl), { immediate: true });
584
608
  const isDeleting = (0, import_vue2.ref)(false);
585
609
  const { revisions, rollback } = (0, import_vue_courier_admin2.useTemplateRevisions)(props.client, props.type, props.locale);
586
610
  const isSaving = (0, import_vue2.ref)(false);
@@ -589,12 +613,12 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
589
613
  const html = (0, import_vue2.ref)("");
590
614
  const text = (0, import_vue2.ref)("");
591
615
  const active = (0, import_vue2.ref)(true);
592
- (0, import_vue2.watch)(template, (tpl) => {
593
- if (!tpl) return;
594
- subject.value = tpl.subject ?? "";
595
- html.value = tpl.html ?? "";
596
- text.value = tpl.text;
597
- active.value = tpl.active;
616
+ (0, import_vue2.watch)(source, (src) => {
617
+ if (!src) return;
618
+ subject.value = src.subject ?? "";
619
+ html.value = src.html ?? "";
620
+ text.value = src.text;
621
+ active.value = template.value ? template.value.active : true;
598
622
  });
599
623
  const {
600
624
  preview,
@@ -639,13 +663,13 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
639
663
  }
640
664
  let previewTimer;
641
665
  (0, import_vue2.watch)([template, subject, html, text, sampleJson], () => {
642
- if (!template.value) return;
666
+ if (!source.value) return;
643
667
  clearTimeout(previewTimer);
644
668
  previewTimer = setTimeout(() => void run(), PREVIEW_DEBOUNCE_MS2);
645
669
  });
646
670
  const dirty = (0, import_vue2.computed)(() => {
647
671
  const tpl = template.value;
648
- return !!tpl && (subject.value !== (tpl.subject ?? "") || html.value !== (tpl.html ?? "") || text.value !== tpl.text || active.value !== tpl.active);
672
+ return notSaved.value && !!builtIn2.value || !!tpl && (subject.value !== (tpl.subject ?? "") || html.value !== (tpl.html ?? "") || text.value !== tpl.text || active.value !== tpl.active);
649
673
  });
650
674
  async function handleSubmit(event) {
651
675
  event.preventDefault();
@@ -657,7 +681,7 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
657
681
  if (html.value) input.html = html.value;
658
682
  if (template.value?.version !== void 0) input.ifVersion = template.value.version;
659
683
  await saveTemplate(props.type, input, props.locale);
660
- await refresh();
684
+ await Promise.all([refresh(), refreshCatalog()]);
661
685
  emit("saved");
662
686
  } catch (err) {
663
687
  saveError.value = err;
@@ -665,7 +689,32 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
665
689
  isSaving.value = false;
666
690
  }
667
691
  }
668
- const leave = () => !dirty.value || window.confirm(t("templates.editor.discardChanges"));
692
+ const leave = () => !dirty.value || notSaved.value && !!builtIn2.value || window.confirm(t("templates.editor.discardChanges"));
693
+ async function handleReset() {
694
+ const copy = builtIn2.value;
695
+ if (!copy || !window.confirm(
696
+ t("templates.editor.confirmReset", { locale: props.locale ?? defaultLocale.value })
697
+ ))
698
+ return;
699
+ isDeleting.value = true;
700
+ saveError.value = null;
701
+ try {
702
+ if (props.locale) {
703
+ await removeTemplate(props.type, props.locale);
704
+ } else {
705
+ const input = { text: copy.text, active: active.value };
706
+ if (copy.subject) input.subject = copy.subject;
707
+ if (copy.html) input.html = copy.html;
708
+ if (template.value?.version !== void 0) input.ifVersion = template.value.version;
709
+ await saveTemplate(props.type, input, null);
710
+ }
711
+ await Promise.all([refresh(), refreshCatalog()]);
712
+ } catch (err) {
713
+ saveError.value = err;
714
+ } finally {
715
+ isDeleting.value = false;
716
+ }
717
+ }
669
718
  function renderTabBar() {
670
719
  if (!props.localeTabs && !props.allowAddLocale) return null;
671
720
  return (0, import_vue2.h)("div", { style: tabBar }, [
@@ -675,21 +724,30 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
675
724
  (0, import_vue2.h)(
676
725
  "nav",
677
726
  { "aria-label": t("templates.editor.locales"), style: tabs },
678
- siblings.value.map((entry) => {
679
- const selected = entry.locale === (props.locale ?? null);
727
+ languages.value.map((l) => {
728
+ const selected = l.locale === (props.locale ?? null);
680
729
  return (0, import_vue2.h)(
681
730
  "button",
682
731
  {
683
- key: entry.locale ?? "",
732
+ key: l.label,
684
733
  type: "button",
685
734
  "aria-current": selected ? "true" : void 0,
686
- style: selected ? tabSelected : entry.active ? tab : tabInactive,
687
- title: entry.active ? void 0 : t("common.status.inactive"),
735
+ style: {
736
+ ...selected ? tabSelected : tab,
737
+ ...l.saved ? {} : tabBuiltIn,
738
+ ...l.active ? {} : tabInactive
739
+ },
740
+ title: l.active ? void 0 : t("common.status.inactive"),
688
741
  onClick: () => {
689
- if (!selected && leave()) emit("select-locale", entry);
742
+ if (!selected && leave())
743
+ emit("select-locale", {
744
+ type: props.type,
745
+ locale: l.locale,
746
+ system: !!email.value?.system
747
+ });
690
748
  }
691
749
  },
692
- entry.locale ?? t("templates.defaultChip")
750
+ l.label
693
751
  );
694
752
  })
695
753
  )
@@ -702,7 +760,8 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
702
760
  onClick: () => {
703
761
  if (leave())
704
762
  emit("add-locale", props.type, {
705
- locales: (0, import_client2.missingTemplateLocales)(templates.value, props.type)
763
+ locales: catalog.value ? (0, import_client2.suggestTemplateLocales)(catalog.value, props.type) : [],
764
+ defaultLocale: defaultLocale.value
706
765
  });
707
766
  }
708
767
  },
@@ -738,16 +797,19 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
738
797
  ]);
739
798
  }
740
799
  return () => {
741
- if (isLoading.value) return (0, import_vue2.h)("p", { style: styles.status }, t("templates.editor.loading"));
742
- if (error.value)
800
+ if (isLoading.value || notSaved.value && builtInLoading.value)
801
+ return (0, import_vue2.h)("p", { style: styles.status }, t("templates.editor.loading"));
802
+ if (error.value && !(notSaved.value && builtIn2.value))
743
803
  return (0, import_vue2.h)("p", { style: styles.error, role: "alert" }, error.value.explanation);
744
804
  return (0, import_vue2.h)("div", { style: styles.container }, [
745
805
  (0, import_vue2.h)("h1", { style: styles.title }, props.type),
746
806
  renderTabBar(),
747
807
  (0, import_vue2.h)("p", { style: styles.meta }, [
748
- (0, import_vue2.h)("strong", props.locale ?? t("templates.defaultLocale")),
749
- ` \xB7 v${template.value?.version ?? 1}`,
750
- props.system ? ` \xB7 ${t("templates.editor.builtInNote")}` : ""
808
+ (0, import_vue2.h)("strong", props.locale ?? defaultLocale.value),
809
+ template.value ? ` \xB7 v${template.value.version}` : "",
810
+ template.value && protectedRow.value ? ` \xB7 ${t("templates.editor.builtInNote")}` : "",
811
+ !template.value && builtIn2.value ? ` \xB7 ${t("templates.editor.builtInCopy")}` : "",
812
+ resolution.value && resolution.value.chain.length > 1 && resolution.value.requested === (props.locale ?? defaultLocale.value) ? ` \xB7 ${t("templates.editor.chain", { chain: [...resolution.value.chain, defaultLocale.value].join(" \u2192 ") })}` : ""
751
813
  ]),
752
814
  (0, import_vue2.h)("div", { style: styles.split }, [
753
815
  (0, import_vue2.h)("div", { style: styles.column }, [
@@ -818,7 +880,16 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
818
880
  isSaving.value ? t("common.saving") : t("common.save")
819
881
  ),
820
882
  !dirty.value && !isSaving.value ? (0, import_vue2.h)("span", { style: styles.meta }, t("templates.editor.noChanges")) : null,
821
- !props.system ? (0, import_vue2.h)(
883
+ template.value && shipsHere.value && builtIn2.value ? (0, import_vue2.h)(
884
+ "button",
885
+ {
886
+ type: "button",
887
+ disabled: isDeleting.value,
888
+ onClick: () => void handleReset(),
889
+ style: { ...styles.rollbackButton, height: "36px", marginLeft: "auto" }
890
+ },
891
+ t("templates.editor.resetToBuiltIn")
892
+ ) : template.value && !protectedRow.value ? (0, import_vue2.h)(
822
893
  "button",
823
894
  {
824
895
  type: "button",
@@ -877,6 +948,46 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
877
948
  ) : preview.value ? (0, import_vue2.h)("pre", { style: styles.previewText }, preview.value.text) : (0, import_vue2.h)("p", { style: styles.meta }, t("templates.editor.nothingRendered"))
878
949
  ])
879
950
  ]),
951
+ // Who receives what: any locale, answered by the same decision a send makes.
952
+ (0, import_vue2.h)("div", { style: styles.revisions }, [
953
+ (0, import_vue2.h)("h2", { style: styles.subtitle }, t("templates.editor.whoReceives")),
954
+ (0, import_vue2.h)(
955
+ "form",
956
+ {
957
+ style: { display: "flex", gap: "8px", alignItems: "center", flexWrap: "wrap" },
958
+ onSubmit: (event) => {
959
+ event.preventDefault();
960
+ if (probe.value.trim()) void resolve(props.type, probe.value.trim());
961
+ }
962
+ },
963
+ [
964
+ (0, import_vue2.h)("input", {
965
+ "aria-label": t("templates.editor.whoReceives"),
966
+ style: { ...styles.input, width: "200px" },
967
+ value: probe.value,
968
+ placeholder: t("templates.editor.whoPlaceholder"),
969
+ spellcheck: false,
970
+ onInput: (e) => {
971
+ probe.value = e.target.value;
972
+ }
973
+ }),
974
+ (0, import_vue2.h)(
975
+ "button",
976
+ { type: "submit", style: { ...styles.rollbackButton, height: "36px" } },
977
+ t("templates.editor.check")
978
+ ),
979
+ resolution.value && probe.value.trim() ? (0, import_vue2.h)("span", { style: { ...styles.meta, marginBottom: "0" }, role: "status" }, [
980
+ t("templates.editor.receives", {
981
+ requested: resolution.value.requested,
982
+ sent: resolution.value.sent
983
+ }),
984
+ " ",
985
+ resolution.value.source === "saved" ? t("templates.editor.sourceSaved") : t("templates.editor.sourceBuiltIn"),
986
+ resolution.value.chain.length > 0 ? ` (${[...resolution.value.chain, resolution.value.defaultLocale].join(" \u2192 ")})` : ""
987
+ ]) : null
988
+ ]
989
+ )
990
+ ]),
880
991
  revisions.value.length > 0 ? (0, import_vue2.h)("div", { style: styles.revisions }, [
881
992
  (0, import_vue2.h)("h2", { style: styles.subtitle }, t("templates.editor.history")),
882
993
  (0, import_vue2.h)("ul", { style: styles.list }, revisions.value.map(renderRevision))
@@ -890,13 +1001,15 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
890
1001
  var import_client3 = require("@fonderie/client");
891
1002
  var import_vue_courier_admin3 = require("@fonderie/vue-courier-admin");
892
1003
  var import_vue3 = require("vue");
1004
+ var MONO = 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)';
893
1005
  var builtIn = {
894
1006
  fontSize: "11px",
895
1007
  fontWeight: 500,
896
1008
  padding: "1px 7px",
897
1009
  borderRadius: "999px",
898
1010
  border: "1px solid var(--fonderie-border,#e0e0e0)",
899
- color: "var(--fonderie-text-muted,#5c5c5c)"
1011
+ color: "var(--fonderie-text-muted,#5c5c5c)",
1012
+ flexShrink: 0
900
1013
  };
901
1014
  var chip = {
902
1015
  height: "24px",
@@ -907,23 +1020,42 @@ var chip = {
907
1020
  background: "var(--fonderie-surface,#fff)",
908
1021
  color: "var(--fonderie-text,#171717)",
909
1022
  fontSize: "12px",
910
- fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)',
1023
+ fontFamily: MONO,
911
1024
  cursor: "pointer"
912
1025
  };
913
- var chipInactive = { ...chip, opacity: 0.5, textDecoration: "line-through" };
1026
+ var chipBuiltIn = {
1027
+ borderStyle: "dashed",
1028
+ background: "transparent",
1029
+ color: "var(--fonderie-text-muted,#5c5c5c)"
1030
+ };
1031
+ var chipOff = { opacity: 0.5, textDecoration: "line-through" };
914
1032
  var row = {
915
1033
  ...styles.row,
916
- display: "flex",
1034
+ display: "grid",
1035
+ gridTemplateColumns: "minmax(0, 1fr) minmax(180px, 280px) 88px",
917
1036
  alignItems: "center",
918
- gap: "8px",
1037
+ gap: "16px",
919
1038
  paddingRight: "16px"
920
1039
  };
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"
1040
+ var rowButton = {
1041
+ ...styles.rowButton,
1042
+ width: "auto",
1043
+ minWidth: "0",
1044
+ justifyContent: "flex-start",
1045
+ gap: "12px"
1046
+ };
1047
+ var typeName = {
1048
+ ...styles.type,
1049
+ flex: "none",
1050
+ overflow: "hidden",
1051
+ textOverflow: "ellipsis",
1052
+ whiteSpace: "nowrap"
1053
+ };
1054
+ var chips = { display: "flex", flexWrap: "wrap", gap: "6px" };
1055
+ var legend = {
1056
+ fontSize: "12.5px",
1057
+ color: "var(--fonderie-text-muted,#5c5c5c)",
1058
+ margin: "10px 2px 0"
927
1059
  };
928
1060
  var newButton = {
929
1061
  display: "inline-flex",
@@ -951,90 +1083,97 @@ var TemplateListScreen = (0, import_vue3.defineComponent)({
951
1083
  },
952
1084
  emits: {
953
1085
  /**
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.
1086
+ * One row per email — built-in ones included, even when nobody saved a
1087
+ * copy. The row opens the default version; a language chip that language.
956
1088
  */
957
1089
  "select-template": (_template) => true,
958
1090
  /** Receives the locales in use, to suggest. */
959
1091
  "create-template": (_context) => true
960
1092
  },
961
1093
  setup(props, { emit }) {
962
- const { templates, isLoading, error } = (0, import_vue_courier_admin3.useTemplates)(props.client);
1094
+ const { catalog, isLoading, error } = (0, import_vue_courier_admin3.useTemplateCatalog)(props.client);
963
1095
  const t = (key, params) => (0, import_client3.createAdminT)(props.locale)(key, params);
964
- function renderRow(group) {
965
- const primary = group.primary;
966
- return (0, import_vue3.h)("li", { key: group.type, style: row }, [
967
- (0, import_vue3.h)(
968
- "button",
969
- {
970
- type: "button",
971
- style: rowButton,
972
- onClick: () => emit("select-template", primary)
973
- },
974
- [
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 }, [
978
- (0, import_vue3.h)("span", { style: styles.dot }),
979
- primary.active ? t("common.status.active") : t("common.status.inactive")
980
- ])
981
- ]
982
- ),
983
- // Siblings of the row button, not inside it: a button cannot hold buttons.
1096
+ const defaultLocale = () => catalog.value?.defaultLocale ?? "en-US";
1097
+ function renderRow(email) {
1098
+ const languages = (0, import_client3.templateLanguages)(email, defaultLocale());
1099
+ const primary = languages.find((l) => l.locale === null) ?? languages[0];
1100
+ const open = (l) => emit("select-template", {
1101
+ type: email.type,
1102
+ locale: l?.locale ?? null,
1103
+ system: email.system
1104
+ });
1105
+ return (0, import_vue3.h)("li", { key: email.type, style: row }, [
1106
+ (0, import_vue3.h)("button", { type: "button", style: rowButton, onClick: () => open(primary) }, [
1107
+ (0, import_vue3.h)("span", { style: typeName }, email.type),
1108
+ email.system ? (0, import_vue3.h)("span", { style: builtIn }, t("templates.builtInBadge")) : null
1109
+ ]),
1110
+ // Siblings of the row button: a button cannot hold buttons. Each chip
1111
+ // names its email and language for screen readers.
984
1112
  (0, import_vue3.h)(
985
1113
  "span",
986
1114
  { style: chips },
987
- group.entries.map((entry) => {
988
- const name = entry.locale ?? t("templates.defaultChip");
989
- return (0, import_vue3.h)(
1115
+ languages.map(
1116
+ (l) => (0, import_vue3.h)(
990
1117
  "button",
991
1118
  {
992
- key: entry.locale ?? "",
1119
+ key: l.label,
993
1120
  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)
1121
+ style: { ...chip, ...l.saved ? {} : chipBuiltIn, ...l.active ? {} : chipOff },
1122
+ "aria-label": t("templates.list.openLocale", { type: email.type, locale: l.label }),
1123
+ title: l.active ? void 0 : t("common.status.inactive"),
1124
+ onClick: () => open(l)
998
1125
  },
999
- name
1000
- );
1001
- })
1002
- )
1126
+ l.label
1127
+ )
1128
+ )
1129
+ ),
1130
+ (0, import_vue3.h)("span", { style: primary?.active === false ? styles.inactive : styles.active }, [
1131
+ (0, import_vue3.h)("span", { style: styles.dot }),
1132
+ primary?.active === false ? t("common.status.inactive") : t("common.status.active")
1133
+ ])
1003
1134
  ]);
1004
1135
  }
1005
- return () => (0, import_vue3.h)("div", { style: styles.listContainer }, [
1006
- (0, import_vue3.h)(
1007
- "div",
1008
- {
1009
- style: {
1010
- display: "flex",
1011
- alignItems: "center",
1012
- justifyContent: "space-between",
1013
- gap: "16px"
1014
- }
1015
- },
1016
- [
1017
- (0, import_vue3.h)("h1", { style: styles.listTitle }, t("templates.list.title")),
1018
- props.allowCreate ? (0, import_vue3.h)(
1019
- "button",
1020
- {
1021
- type: "button",
1022
- style: newButton,
1023
- onClick: () => emit("create-template", {
1024
- locales: [
1025
- ...new Set(
1026
- templates.value.map((tpl) => tpl.locale).filter((l) => !!l)
1027
- )
1028
- ]
1029
- })
1030
- },
1031
- t("templates.list.newTemplate")
1032
- ) : null
1136
+ return () => {
1137
+ const emails = catalog.value?.emails ?? [];
1138
+ return (0, import_vue3.h)("div", { style: styles.listContainer }, [
1139
+ (0, import_vue3.h)(
1140
+ "div",
1141
+ {
1142
+ style: {
1143
+ display: "flex",
1144
+ alignItems: "center",
1145
+ justifyContent: "space-between",
1146
+ gap: "16px"
1147
+ }
1148
+ },
1149
+ [
1150
+ (0, import_vue3.h)("h1", { style: styles.listTitle }, t("templates.list.title")),
1151
+ props.allowCreate ? (0, import_vue3.h)(
1152
+ "button",
1153
+ {
1154
+ type: "button",
1155
+ style: newButton,
1156
+ onClick: () => emit("create-template", {
1157
+ locales: [
1158
+ ...new Set(
1159
+ emails.flatMap(
1160
+ (e) => (0, import_client3.templateLanguages)(e, defaultLocale()).filter((l) => l.locale !== null).map((l) => l.label)
1161
+ )
1162
+ )
1163
+ ].sort()
1164
+ })
1165
+ },
1166
+ t("templates.list.newTemplate")
1167
+ ) : null
1168
+ ]
1169
+ ),
1170
+ (0, import_vue3.h)("p", { style: styles.hint }, t("templates.list.hint")),
1171
+ 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) : [
1172
+ (0, import_vue3.h)("ul", { style: styles.list }, emails.map(renderRow)),
1173
+ (0, import_vue3.h)("p", { style: legend }, t("templates.list.legend"))
1033
1174
  ]
1034
- ),
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))
1037
- ]);
1175
+ ]);
1176
+ };
1038
1177
  }
1039
1178
  });
1040
1179
  // Annotate the CommonJS export names for ESM import in node: