@fonderie/vue-courier-admin-screens 0.7.0 → 0.8.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
@@ -280,7 +280,9 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
280
280
  */
281
281
  type: { type: String, default: void 0 },
282
282
  /** Locales already in use, offered as suggestions. */
283
- locales: { type: Array, default: () => [] }
283
+ locales: { type: Array, default: () => [] },
284
+ /** The console language; defaults to English. */
285
+ locale: { type: String, default: void 0 }
284
286
  },
285
287
  emits: {
286
288
  /** What was created, so the caller can open it in the editor. */
@@ -289,12 +291,13 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
289
291
  setup(props, { emit }) {
290
292
  const addingLocale = props.type !== void 0;
291
293
  const newType = (0, import_vue.ref)("");
292
- const locale = (0, import_vue.ref)("");
294
+ const targetLocale = (0, import_vue.ref)("");
293
295
  const subject = (0, import_vue.ref)("");
294
296
  const html = (0, import_vue.ref)("");
295
297
  const text = (0, import_vue.ref)("");
296
298
  const error = (0, import_vue.ref)(null);
297
299
  const busy = (0, import_vue.ref)(false);
300
+ const tr = (key, params) => (0, import_client.createAdminT)(props.locale)(key, params);
298
301
  (0, import_vue.onMounted)(() => {
299
302
  if (!props.type) return;
300
303
  props.client.getTemplate(props.type, null).then(({ result }) => {
@@ -307,21 +310,21 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
307
310
  e.preventDefault();
308
311
  error.value = null;
309
312
  const t = addingLocale ? props.type : newType.value.trim();
310
- const l = locale.value.trim() || null;
313
+ const l = targetLocale.value.trim() || null;
311
314
  if (!TYPE_RE.test(t)) {
312
- error.value = "Type: lowercase letters, digits, - and _ (e.g. weekly-digest).";
315
+ error.value = tr("templates.create.errorType");
313
316
  return;
314
317
  }
315
318
  if (addingLocale && !l) {
316
- error.value = "Choose the locale to add, e.g. fr or fr-CA.";
319
+ error.value = tr("templates.create.errorLocaleRequired");
317
320
  return;
318
321
  }
319
322
  if (l && !LOCALE_RE.test(l)) {
320
- error.value = "Locale: a language tag such as fr, es or fr-CA.";
323
+ error.value = tr("templates.create.errorLocale");
321
324
  return;
322
325
  }
323
326
  if (!text.value.trim()) {
324
- error.value = "The plain-text body is required \u2014 it is what every email client can show.";
327
+ error.value = tr("templates.create.errorText");
325
328
  return;
326
329
  }
327
330
  busy.value = true;
@@ -331,7 +334,7 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
331
334
  (err) => !(err instanceof import_client.FonderieApiError && err.status === 404)
332
335
  );
333
336
  if (exists) {
334
- error.value = `"${t}"${l ? ` (${l})` : ""} already exists \u2014 open it from the list to edit.`;
337
+ error.value = l ? tr("templates.create.existsLocale", { type: t, locale: l }) : tr("templates.create.exists", { type: t });
335
338
  return;
336
339
  }
337
340
  const input = { text: text.value, active: true };
@@ -364,19 +367,19 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
364
367
  (0, import_vue.h)(
365
368
  "h1",
366
369
  { style: styles.title },
367
- addingLocale ? `Add a locale to ${props.type}` : "New template"
370
+ addingLocale ? tr("templates.create.titleLocale", { type: props.type }) : tr("templates.create.titleNew")
368
371
  ),
369
372
  (0, import_vue.h)(
370
373
  "p",
371
374
  { style: { ...styles.meta, lineHeight: 1.5 } },
372
- addingLocale ? "Starts from the default copy. People whose locale matches receive this version; everyone else keeps the default." : "For an email your app sends that has no stored copy yet. The type must match what the app sends."
375
+ addingLocale ? tr("templates.create.hintLocale") : tr("templates.create.hintNew")
373
376
  ),
374
377
  (0, import_vue.h)("form", { style: styles.form, onSubmit: (ev) => void submit(ev) }, [
375
- ...addingLocale ? [] : field("new-template-type", "Type", newType, { placeholder: "weekly-digest" }),
378
+ ...addingLocale ? [] : field("new-template-type", tr("templates.create.type"), newType, { placeholder: "weekly-digest" }),
376
379
  ...field(
377
380
  "new-template-locale",
378
- addingLocale ? "Locale" : "Locale (optional \u2014 empty is the default)",
379
- locale,
381
+ addingLocale ? tr("templates.create.locale") : tr("templates.create.localeOptional"),
382
+ targetLocale,
380
383
  { placeholder: "fr", list: "new-template-locales" }
381
384
  ),
382
385
  (0, import_vue.h)(
@@ -384,9 +387,9 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
384
387
  { id: "new-template-locales" },
385
388
  [...new Set(props.locales)].map((l) => (0, import_vue.h)("option", { key: l, value: l }))
386
389
  ),
387
- ...field("new-template-subject", "Subject", subject),
388
- ...field("new-template-html", "HTML body", html, { textarea: true, minHeight: "160px" }),
389
- ...field("new-template-text", "Plain-text body", text, {
390
+ ...field("new-template-subject", tr("templates.subject"), subject),
391
+ ...field("new-template-html", tr("templates.htmlBody"), html, { textarea: true, minHeight: "160px" }),
392
+ ...field("new-template-text", tr("templates.textBody"), text, {
390
393
  textarea: true,
391
394
  minHeight: "100px"
392
395
  }),
@@ -398,7 +401,7 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
398
401
  disabled: busy.value,
399
402
  style: { ...styles.button, marginTop: "16px" }
400
403
  },
401
- busy.value ? "Creating\u2026" : addingLocale ? "Add locale" : "Create template"
404
+ busy.value ? tr("templates.create.creating") : addingLocale ? tr("templates.create.submitLocale") : tr("templates.create.submitNew")
402
405
  )
403
406
  ])
404
407
  ]);
@@ -406,6 +409,7 @@ var TemplateCreateScreen = (0, import_vue.defineComponent)({
406
409
  });
407
410
 
408
411
  // src/screens/TemplateEditorScreen.ts
412
+ var import_client2 = require("@fonderie/client");
409
413
  var import_vue_courier_admin = require("@fonderie/vue-courier-admin");
410
414
  var import_vue2 = require("vue");
411
415
  var PREVIEW_DEBOUNCE_MS = 500;
@@ -422,7 +426,9 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
422
426
  */
423
427
  system: { type: Boolean, default: false },
424
428
  /** Shows "+ Add locale" on the default locale (emits add-locale). */
425
- allowAddLocale: { type: Boolean, default: false }
429
+ allowAddLocale: { type: Boolean, default: false },
430
+ /** The console language (`locale` is the template's); defaults to English. */
431
+ uiLocale: { type: String, default: void 0 }
426
432
  },
427
433
  emits: {
428
434
  saved: () => true,
@@ -432,6 +438,7 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
432
438
  "add-locale": (_type) => true
433
439
  },
434
440
  setup(props, { emit }) {
441
+ const t = (key, params) => (0, import_client2.createAdminT)(props.uiLocale)(key, params);
435
442
  const { template, isLoading, error, refresh } = (0, import_vue_courier_admin.useTemplate)(
436
443
  props.client,
437
444
  props.type,
@@ -466,13 +473,13 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
466
473
  try {
467
474
  const parsed = JSON.parse(sampleJson.value || "{}");
468
475
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
469
- sampleError.value = "Sample data must be a JSON object.";
476
+ sampleError.value = "templates.editor.sampleNotObject";
470
477
  return;
471
478
  }
472
479
  data = parsed;
473
480
  sampleError.value = null;
474
481
  } catch {
475
- sampleError.value = "Sample data is not valid JSON.";
482
+ sampleError.value = "templates.editor.sampleInvalid";
476
483
  return;
477
484
  }
478
485
  const result = await renderPreview(
@@ -523,9 +530,8 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
523
530
  }
524
531
  }
525
532
  async function handleDelete() {
526
- const which = props.locale ? `the ${props.locale} version of "${props.type}"` : `"${props.type}"`;
527
- const fallback = props.locale ? " People in that locale will receive the default version." : "";
528
- if (!window.confirm(`Delete ${which}?${fallback}`)) return;
533
+ const message = props.locale ? t("templates.editor.confirmDeleteLocale", { locale: props.locale, type: props.type }) : t("templates.editor.confirmDelete", { type: props.type });
534
+ if (!window.confirm(message)) return;
529
535
  isDeleting.value = true;
530
536
  saveError.value = null;
531
537
  try {
@@ -541,17 +547,17 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
541
547
  return (0, import_vue2.h)("li", { key: rev.version, style: styles.revisionRow }, [
542
548
  (0, import_vue2.h)(
543
549
  "span",
544
- `v${rev.version} \u2014 ${rev.actor ?? "unknown"} \u2014 ${new Date(rev.createdAt).toLocaleString()}`
550
+ `v${rev.version} \u2014 ${rev.actor ?? t("templates.editor.unknownActor")} \u2014 ${(0, import_client2.formatAdminDate)(rev.createdAt, props.uiLocale)}`
545
551
  ),
546
552
  (0, import_vue2.h)(
547
553
  "button",
548
554
  { type: "button", style: styles.rollbackButton, onClick: () => rollback(rev.version) },
549
- "Roll back"
555
+ t("templates.editor.rollBack")
550
556
  )
551
557
  ]);
552
558
  }
553
559
  return () => {
554
- if (isLoading.value) return (0, import_vue2.h)("p", { style: styles.status }, "Loading template\u2026");
560
+ if (isLoading.value) return (0, import_vue2.h)("p", { style: styles.status }, t("templates.editor.loading"));
555
561
  if (error.value)
556
562
  return (0, import_vue2.h)("p", { style: styles.error, role: "alert" }, error.value.explanation);
557
563
  return (0, import_vue2.h)("div", { style: styles.container }, [
@@ -567,19 +573,19 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
567
573
  style: { ...styles.rollbackButton, marginLeft: "auto" },
568
574
  onClick: () => emit("add-locale", props.type)
569
575
  },
570
- "+ Add locale"
576
+ t("templates.editor.addLocale")
571
577
  ) : null
572
578
  ]
573
579
  ),
574
580
  (0, import_vue2.h)("p", { style: styles.meta }, [
575
- (0, import_vue2.h)("strong", props.locale ?? "default locale"),
581
+ (0, import_vue2.h)("strong", props.locale ?? t("templates.defaultLocale")),
576
582
  ` \xB7 v${template.value?.version ?? 1}`,
577
- props.system ? " \xB7 built-in email: edit or roll back, it cannot be deleted" : ""
583
+ props.system ? ` \xB7 ${t("templates.editor.builtInNote")}` : ""
578
584
  ]),
579
585
  (0, import_vue2.h)("div", { style: styles.split }, [
580
586
  (0, import_vue2.h)("div", { style: styles.column }, [
581
587
  (0, import_vue2.h)("form", { style: styles.form, onSubmit: handleSubmit }, [
582
- (0, import_vue2.h)("label", { style: styles.label, for: "template-subject" }, "Subject"),
588
+ (0, import_vue2.h)("label", { style: styles.label, for: "template-subject" }, t("templates.subject")),
583
589
  (0, import_vue2.h)("input", {
584
590
  id: "template-subject",
585
591
  style: styles.input,
@@ -588,7 +594,7 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
588
594
  subject.value = e.target.value;
589
595
  }
590
596
  }),
591
- (0, import_vue2.h)("label", { style: styles.label, for: "template-html" }, "HTML body"),
597
+ (0, import_vue2.h)("label", { style: styles.label, for: "template-html" }, t("templates.htmlBody")),
592
598
  (0, import_vue2.h)("textarea", {
593
599
  id: "template-html",
594
600
  style: styles.textarea,
@@ -598,7 +604,7 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
598
604
  html.value = e.target.value;
599
605
  }
600
606
  }),
601
- (0, import_vue2.h)("label", { style: styles.label, for: "template-text" }, "Plain-text body"),
607
+ (0, import_vue2.h)("label", { style: styles.label, for: "template-text" }, t("templates.textBody")),
602
608
  (0, import_vue2.h)("textarea", {
603
609
  id: "template-text",
604
610
  style: styles.textarea,
@@ -617,10 +623,10 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
617
623
  active.value = e.target.checked;
618
624
  }
619
625
  }),
620
- "Active"
626
+ t("templates.editor.active")
621
627
  ]),
622
628
  saveError.value?.reason === "VERSION_CONFLICT" ? (0, import_vue2.h)("p", { style: styles.error, role: "alert" }, [
623
- "Someone changed this template since you opened it. Reload to see their change, then edit again. ",
629
+ `${t("templates.editor.conflict")} `,
624
630
  (0, import_vue2.h)(
625
631
  "button",
626
632
  {
@@ -631,7 +637,7 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
631
637
  void refresh();
632
638
  }
633
639
  },
634
- "Reload"
640
+ t("common.reload")
635
641
  )
636
642
  ]) : saveError.value ? (0, import_vue2.h)("p", { style: styles.error, role: "alert" }, saveError.value.explanation) : null,
637
643
  (0, import_vue2.h)("div", { style: styles.saveRow }, [
@@ -642,9 +648,9 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
642
648
  disabled: isSaving.value || !dirty.value,
643
649
  style: dirty.value ? styles.button : styles.buttonDisabled
644
650
  },
645
- isSaving.value ? "Saving\u2026" : "Save"
651
+ isSaving.value ? t("common.saving") : t("common.save")
646
652
  ),
647
- !dirty.value && !isSaving.value ? (0, import_vue2.h)("span", { style: styles.meta }, "No changes to save") : null,
653
+ !dirty.value && !isSaving.value ? (0, import_vue2.h)("span", { style: styles.meta }, t("templates.editor.noChanges")) : null,
648
654
  !props.system ? (0, import_vue2.h)(
649
655
  "button",
650
656
  {
@@ -659,11 +665,11 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
659
665
  borderColor: "color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)"
660
666
  }
661
667
  },
662
- isDeleting.value ? "Deleting\u2026" : "Delete"
668
+ isDeleting.value ? t("common.deleting") : t("common.delete")
663
669
  ) : null
664
670
  ])
665
671
  ]),
666
- (0, import_vue2.h)("label", { style: styles.label, for: "template-sample" }, "Sample data"),
672
+ (0, import_vue2.h)("label", { style: styles.label, for: "template-sample" }, t("templates.editor.sampleData")),
667
673
  (0, import_vue2.h)("textarea", {
668
674
  id: "template-sample",
669
675
  style: styles.textarea,
@@ -674,12 +680,12 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
674
680
  sampleJson.value = e.target.value;
675
681
  }
676
682
  }),
677
- sampleError.value ? (0, import_vue2.h)("p", { style: styles.error, role: "alert" }, sampleError.value) : null
683
+ sampleError.value ? (0, import_vue2.h)("p", { style: styles.error, role: "alert" }, t(sampleError.value)) : null
678
684
  ]),
679
685
  (0, import_vue2.h)("div", { style: styles.previewColumn }, [
680
686
  (0, import_vue2.h)("div", { style: styles.previewHeader }, [
681
- (0, import_vue2.h)("span", { style: styles.label }, "Preview"),
682
- (0, import_vue2.h)("span", { style: styles.meta }, isPreviewing.value ? "Rendering\u2026" : "Live")
687
+ (0, import_vue2.h)("span", { style: styles.label }, t("templates.editor.preview")),
688
+ (0, import_vue2.h)("span", { style: styles.meta }, isPreviewing.value ? t("templates.editor.rendering") : t("templates.editor.live"))
683
689
  ]),
684
690
  previewError.value ? (0, import_vue2.h)("p", { style: styles.error, role: "alert" }, previewError.value.explanation) : null,
685
691
  preview.value?.subject ? (0, import_vue2.h)("p", { style: styles.previewSubject }, preview.value.subject) : null,
@@ -688,16 +694,16 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
688
694
  // must never execute in the dashboard's origin, where the admin
689
695
  // token lives.
690
696
  (0, import_vue2.h)("iframe", {
691
- title: "Template preview",
697
+ title: t("templates.editor.previewTitle"),
692
698
  style: styles.previewFrame,
693
699
  sandbox: "",
694
700
  srcdoc: preview.value.html
695
701
  })
696
- ) : preview.value ? (0, import_vue2.h)("pre", { style: styles.previewText }, preview.value.text) : (0, import_vue2.h)("p", { style: styles.meta }, "Nothing rendered yet.")
702
+ ) : 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"))
697
703
  ])
698
704
  ]),
699
705
  revisions.value.length > 0 ? (0, import_vue2.h)("div", { style: styles.revisions }, [
700
- (0, import_vue2.h)("h2", { style: styles.subtitle }, "History"),
706
+ (0, import_vue2.h)("h2", { style: styles.subtitle }, t("templates.editor.history")),
701
707
  (0, import_vue2.h)("ul", { style: styles.list }, revisions.value.map(renderRevision))
702
708
  ]) : null
703
709
  ]);
@@ -706,6 +712,7 @@ var TemplateEditorScreen = (0, import_vue2.defineComponent)({
706
712
  });
707
713
 
708
714
  // src/screens/TemplateListScreen.ts
715
+ var import_client3 = require("@fonderie/client");
709
716
  var import_vue_courier_admin2 = require("@fonderie/vue-courier-admin");
710
717
  var import_vue3 = require("vue");
711
718
  var builtIn = {
@@ -736,7 +743,9 @@ var TemplateListScreen = (0, import_vue3.defineComponent)({
736
743
  props: {
737
744
  client: { type: Object, required: true },
738
745
  /** Shows a "New template" button (emits create-template). */
739
- allowCreate: { type: Boolean, default: false }
746
+ allowCreate: { type: Boolean, default: false },
747
+ /** The console language; defaults to English. */
748
+ locale: { type: String, default: void 0 }
740
749
  },
741
750
  emits: {
742
751
  /** The whole row — type AND locale, and whether it is built-in. */
@@ -746,6 +755,7 @@ var TemplateListScreen = (0, import_vue3.defineComponent)({
746
755
  },
747
756
  setup(props, { emit }) {
748
757
  const { templates, isLoading, error } = (0, import_vue_courier_admin2.useTemplates)(props.client);
758
+ const t = (key, params) => (0, import_client3.createAdminT)(props.locale)(key, params);
749
759
  function renderRow(template) {
750
760
  return (0, import_vue3.h)("li", { key: `${template.type}:${template.locale ?? "base"}`, style: styles.row }, [
751
761
  (0, import_vue3.h)(
@@ -757,11 +767,11 @@ var TemplateListScreen = (0, import_vue3.defineComponent)({
757
767
  },
758
768
  [
759
769
  (0, import_vue3.h)("span", { style: styles.type }, template.type),
760
- (0, import_vue3.h)("span", { style: styles.locale }, template.locale ?? "default locale"),
761
- template.system ? (0, import_vue3.h)("span", { style: builtIn }, "built-in") : null,
770
+ (0, import_vue3.h)("span", { style: styles.locale }, template.locale ?? t("templates.defaultLocale")),
771
+ template.system ? (0, import_vue3.h)("span", { style: builtIn }, t("templates.builtInBadge")) : null,
762
772
  (0, import_vue3.h)("span", { style: template.active ? styles.active : styles.inactive }, [
763
773
  (0, import_vue3.h)("span", { style: styles.dot }),
764
- template.active ? "active" : "inactive"
774
+ template.active ? t("common.status.active") : t("common.status.inactive")
765
775
  ])
766
776
  ]
767
777
  )
@@ -779,7 +789,7 @@ var TemplateListScreen = (0, import_vue3.defineComponent)({
779
789
  }
780
790
  },
781
791
  [
782
- (0, import_vue3.h)("h1", { style: styles.listTitle }, "Templates"),
792
+ (0, import_vue3.h)("h1", { style: styles.listTitle }, t("templates.list.title")),
783
793
  props.allowCreate ? (0, import_vue3.h)(
784
794
  "button",
785
795
  {
@@ -788,21 +798,21 @@ var TemplateListScreen = (0, import_vue3.defineComponent)({
788
798
  onClick: () => emit("create-template", {
789
799
  locales: [
790
800
  ...new Set(
791
- templates.value.map((t) => t.locale).filter((l) => !!l)
801
+ templates.value.map((tpl) => tpl.locale).filter((l) => !!l)
792
802
  )
793
803
  ]
794
804
  })
795
805
  },
796
- "New template"
806
+ t("templates.list.newTemplate")
797
807
  ) : null
798
808
  ]
799
809
  ),
800
810
  (0, import_vue3.h)(
801
811
  "p",
802
812
  { style: styles.hint },
803
- "Every email the app sends. Open one to edit its copy and preview it live."
813
+ t("templates.list.hint")
804
814
  ),
805
- isLoading.value ? (0, import_vue3.h)("p", { style: styles.status }, "Loading templates\u2026") : 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))
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))
806
816
  ]);
807
817
  }
808
818
  });