@fonderie/react-courier-admin-screens 0.5.0 → 0.7.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
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ TemplateCreateScreen: () => TemplateCreateScreen,
23
24
  TemplateEditorScreen: () => TemplateEditorScreen,
24
25
  TemplateListScreen: () => TemplateListScreen
25
26
  });
@@ -35,10 +36,14 @@ function TemplateEditorScreen({
35
36
  client,
36
37
  type,
37
38
  locale,
38
- onSaved
39
+ onSaved,
40
+ system = false,
41
+ onDeleted,
42
+ onAddLocale
39
43
  }) {
40
44
  const { template, isLoading, error, refresh } = (0, import_react_courier_admin.useTemplate)(client, type, locale);
41
- const { saveTemplate } = (0, import_react_courier_admin.useTemplates)(client);
45
+ const { saveTemplate, removeTemplate } = (0, import_react_courier_admin.useTemplates)(client);
46
+ const [isDeleting, setIsDeleting] = (0, import_react.useState)(false);
42
47
  const { revisions, rollback } = (0, import_react_courier_admin.useTemplateRevisions)(client, type, locale);
43
48
  const [isSaving, setIsSaving] = (0, import_react.useState)(false);
44
49
  const [saveError, setSaveError] = (0, import_react.useState)(null);
@@ -108,15 +113,42 @@ function TemplateEditorScreen({
108
113
  setIsSaving(false);
109
114
  }
110
115
  };
116
+ const handleDelete = async () => {
117
+ const which = locale ? `the ${locale} version of "${type}"` : `"${type}"`;
118
+ const fallback = locale ? " People in that locale will receive the default version." : "";
119
+ if (!window.confirm(`Delete ${which}?${fallback}`)) return;
120
+ setIsDeleting(true);
121
+ setSaveError(null);
122
+ try {
123
+ await removeTemplate(type, locale);
124
+ onDeleted?.();
125
+ } catch (err) {
126
+ setSaveError(err);
127
+ } finally {
128
+ setIsDeleting(false);
129
+ }
130
+ };
111
131
  if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.status, children: "Loading template\u2026" });
112
132
  if (error)
113
133
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, role: "alert", children: error.explanation });
114
134
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.container, children: [
115
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { style: styles.title, children: type }),
135
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }, children: [
136
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { style: styles.title, children: type }),
137
+ onAddLocale && !locale ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
138
+ "button",
139
+ {
140
+ type: "button",
141
+ style: { ...styles.rollbackButton, marginLeft: "auto" },
142
+ onClick: () => onAddLocale(type),
143
+ children: "+ Add locale"
144
+ }
145
+ ) : null
146
+ ] }),
116
147
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.meta, children: [
117
- locale ?? "base",
148
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: locale ?? "default locale" }),
118
149
  " \xB7 v",
119
- template?.version ?? 1
150
+ template?.version ?? 1,
151
+ system ? " \xB7 built-in email: edit or roll back, it cannot be deleted" : ""
120
152
  ] }),
121
153
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.split, children: [
122
154
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.column, children: [
@@ -165,10 +197,49 @@ function TemplateEditorScreen({
165
197
  ),
166
198
  "Active"
167
199
  ] }),
168
- saveError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, role: "alert", children: saveError.explanation }),
200
+ saveError?.reason === "VERSION_CONFLICT" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.error, role: "alert", children: [
201
+ "Someone changed this template since you opened it. Reload to see their change, then edit again.",
202
+ " ",
203
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
204
+ "button",
205
+ {
206
+ type: "button",
207
+ style: styles.rollbackButton,
208
+ onClick: () => {
209
+ setSaveError(null);
210
+ void refresh();
211
+ },
212
+ children: "Reload"
213
+ }
214
+ )
215
+ ] }) : saveError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, role: "alert", children: saveError.explanation }) : null,
169
216
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.saveRow, children: [
170
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "submit", disabled: isSaving || !dirty, style: dirty ? styles.button : styles.buttonDisabled, children: isSaving ? "Saving\u2026" : "Save" }),
171
- !dirty && !isSaving && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.meta, children: "No changes to save" })
217
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
218
+ "button",
219
+ {
220
+ type: "submit",
221
+ disabled: isSaving || !dirty,
222
+ style: dirty ? styles.button : styles.buttonDisabled,
223
+ children: isSaving ? "Saving\u2026" : "Save"
224
+ }
225
+ ),
226
+ !dirty && !isSaving && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.meta, children: "No changes to save" }),
227
+ !system ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
228
+ "button",
229
+ {
230
+ type: "button",
231
+ disabled: isDeleting,
232
+ onClick: () => void handleDelete(),
233
+ style: {
234
+ ...styles.rollbackButton,
235
+ height: 36,
236
+ marginLeft: "auto",
237
+ color: "var(--fonderie-danger,#e00)",
238
+ borderColor: "color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)"
239
+ },
240
+ children: isDeleting ? "Deleting\u2026" : "Delete"
241
+ }
242
+ ) : null
172
243
  ] })
173
244
  ] }),
174
245
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-sample", children: "Sample data" }),
@@ -234,13 +305,24 @@ function TemplateEditorScreen({
234
305
  ] });
235
306
  }
236
307
  var styles = {
237
- container: { padding: 24 },
308
+ container: { padding: "8px 40px 64px", maxWidth: 1400, boxSizing: "border-box" },
238
309
  // Two columns, always: the editor on the left, the rendered result on the
239
310
  // right. Stacking the preview under a long form hid it below the fold.
240
- split: { display: "grid", gridTemplateColumns: "minmax(0, 1fr) minmax(0, 1fr)", gap: 24, alignItems: "start" },
311
+ split: {
312
+ display: "grid",
313
+ gridTemplateColumns: "minmax(0, 1fr) minmax(0, 1fr)",
314
+ gap: 24,
315
+ alignItems: "start"
316
+ },
241
317
  column: { minWidth: 0, display: "flex", flexDirection: "column" },
242
318
  // Pinned while the form scrolls, so the preview stays in view as you edit.
243
- previewColumn: { minWidth: 0, display: "flex", flexDirection: "column", position: "sticky", top: 16 },
319
+ previewColumn: {
320
+ minWidth: 0,
321
+ display: "flex",
322
+ flexDirection: "column",
323
+ position: "sticky",
324
+ top: 16
325
+ },
244
326
  saveRow: { display: "flex", alignItems: "center", gap: 12, marginTop: 16 },
245
327
  previewHeader: {
246
328
  display: "flex",
@@ -250,6 +332,7 @@ var styles = {
250
332
  },
251
333
  previewSubject: { fontSize: 14, fontWeight: 600, margin: "8px 0" },
252
334
  previewFrame: {
335
+ boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))",
253
336
  width: "100%",
254
337
  height: 520,
255
338
  border: "1px solid var(--fonderie-border,#e0e0e0)",
@@ -264,60 +347,127 @@ var styles = {
264
347
  fontSize: 13,
265
348
  fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)'
266
349
  },
267
- title: { fontSize: 24, fontWeight: 700 },
268
- meta: { fontSize: 13, color: "var(--fonderie-text-muted,#5c5c5c)", marginBottom: 16 },
350
+ title: {
351
+ fontSize: 22,
352
+ fontWeight: 600,
353
+ margin: "8px 0 4px",
354
+ letterSpacing: "var(--fonderie-tracking-display,-0.05em)",
355
+ lineHeight: 1.25
356
+ },
357
+ meta: { fontSize: 13, color: "var(--fonderie-text-muted,#5c5c5c)", marginBottom: 20 },
269
358
  status: { padding: 24, textAlign: "center", color: "var(--fonderie-text-muted,#5c5c5c)" },
270
- error: { color: "var(--fonderie-danger,#e00)", marginBottom: 12, fontSize: 14 },
271
- form: { display: "flex", flexDirection: "column", gap: 4 },
272
- label: { fontSize: 13, fontWeight: 600, marginTop: 12 },
273
- input: { border: "1px solid var(--fonderie-border,#e0e0e0)", borderRadius: 8, padding: 10, fontSize: 14 },
359
+ error: {
360
+ color: "var(--fonderie-danger,#e00)",
361
+ background: "color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)",
362
+ borderRadius: 6,
363
+ padding: "8px 12px",
364
+ margin: "8px 0",
365
+ fontSize: 13.5
366
+ },
367
+ form: {
368
+ display: "flex",
369
+ flexDirection: "column",
370
+ gap: 4,
371
+ background: "var(--fonderie-surface,#fff)",
372
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
373
+ borderRadius: "var(--fonderie-radius-lg,8px)",
374
+ padding: "6px 20px 20px",
375
+ boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))"
376
+ },
377
+ label: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },
378
+ input: {
379
+ height: 36,
380
+ boxSizing: "border-box",
381
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
382
+ borderRadius: 6,
383
+ padding: "0 10px",
384
+ fontSize: 13.5,
385
+ fontFamily: "inherit",
386
+ background: "var(--fonderie-surface,#fff)",
387
+ color: "var(--fonderie-text,#171717)"
388
+ },
274
389
  textarea: {
275
390
  border: "1px solid var(--fonderie-border,#e0e0e0)",
276
- borderRadius: 8,
391
+ borderRadius: 6,
277
392
  padding: 10,
278
- fontSize: 14,
393
+ fontSize: 13,
394
+ lineHeight: 1.55,
395
+ background: "var(--fonderie-surface,#fff)",
396
+ color: "var(--fonderie-text,#171717)",
279
397
  fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)'
280
398
  },
281
399
  checkboxLabel: { display: "flex", alignItems: "center", gap: 8, marginTop: 12, fontSize: 14 },
282
400
  button: {
401
+ display: "inline-flex",
402
+ alignItems: "center",
403
+ height: 36,
404
+ boxSizing: "border-box",
283
405
  backgroundColor: "var(--fonderie-text,#171717)",
284
406
  color: "var(--fonderie-surface,#fff)",
285
- padding: "10px 20px",
286
- borderRadius: 8,
287
- border: "none",
288
- fontSize: 14,
407
+ padding: "0 16px",
408
+ borderRadius: 6,
409
+ border: "1px solid var(--fonderie-text,#171717)",
410
+ fontSize: 13.5,
289
411
  fontWeight: 600,
412
+ fontFamily: "inherit",
290
413
  cursor: "pointer",
291
414
  alignSelf: "flex-start"
292
415
  },
293
416
  buttonDisabled: {
294
- backgroundColor: "var(--fonderie-border,#e0e0e0)",
295
- color: "var(--fonderie-text-muted,#5c5c5c)",
296
- padding: "10px 20px",
297
- borderRadius: 8,
298
- border: "none",
299
- fontSize: 14,
417
+ display: "inline-flex",
418
+ alignItems: "center",
419
+ height: 36,
420
+ boxSizing: "border-box",
421
+ backgroundColor: "var(--fonderie-text,#171717)",
422
+ color: "var(--fonderie-surface,#fff)",
423
+ padding: "0 16px",
424
+ borderRadius: 6,
425
+ border: "1px solid var(--fonderie-text,#171717)",
426
+ fontSize: 13.5,
300
427
  fontWeight: 600,
301
- cursor: "not-allowed",
302
- alignSelf: "flex-start"
428
+ fontFamily: "inherit",
429
+ alignSelf: "flex-start",
430
+ opacity: 0.45,
431
+ cursor: "not-allowed"
303
432
  },
304
433
  revisions: { marginTop: 32 },
305
- subtitle: { fontSize: 16, fontWeight: 600, marginBottom: 8 },
306
- list: { listStyle: "none", padding: 0, margin: 0 },
434
+ subtitle: {
435
+ fontSize: 15,
436
+ fontWeight: 600,
437
+ margin: "0 0 10px",
438
+ letterSpacing: "var(--fonderie-tracking-display,-0.05em)"
439
+ },
440
+ list: {
441
+ listStyle: "none",
442
+ padding: 0,
443
+ margin: 0,
444
+ background: "var(--fonderie-surface,#fff)",
445
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
446
+ borderRadius: "var(--fonderie-radius-lg,8px)",
447
+ overflow: "hidden"
448
+ },
307
449
  row: {
308
450
  display: "flex",
309
451
  justifyContent: "space-between",
310
452
  alignItems: "center",
311
- padding: "8px 0",
453
+ gap: 12,
454
+ padding: "9px 16px",
312
455
  borderBottom: "1px solid var(--fonderie-border-light,#f5f5f5)",
313
456
  fontSize: 13
314
457
  },
315
458
  rollbackButton: {
316
- background: "none",
459
+ display: "inline-flex",
460
+ alignItems: "center",
461
+ height: 28,
462
+ boxSizing: "border-box",
463
+ background: "var(--fonderie-surface,#fff)",
464
+ color: "var(--fonderie-text,#171717)",
317
465
  border: "1px solid var(--fonderie-border,#e0e0e0)",
318
- borderRadius: 8,
319
- padding: "4px 10px",
320
- fontSize: 12,
466
+ borderRadius: 6,
467
+ padding: "0 10px",
468
+ fontSize: 12.5,
469
+ fontWeight: 500,
470
+ fontFamily: "inherit",
321
471
  cursor: "pointer"
322
472
  }
323
473
  };
@@ -325,14 +475,37 @@ var styles = {
325
475
  // src/screens/TemplateListScreen.tsx
326
476
  var import_react_courier_admin2 = require("@fonderie/react-courier-admin");
327
477
  var import_jsx_runtime2 = require("react/jsx-runtime");
328
- function TemplateListScreen({ client, onSelectTemplate }) {
478
+ function TemplateListScreen({
479
+ client,
480
+ onSelectTemplate,
481
+ onCreateTemplate
482
+ }) {
329
483
  const { templates, isLoading, error } = (0, import_react_courier_admin2.useTemplates)(client);
330
- if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.status, children: "Loading templates\u2026" });
331
- if (error)
332
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.error, role: "alert", children: error.explanation });
333
484
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles2.container, children: [
334
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { style: styles2.title, children: "Templates" }),
335
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { style: styles2.list, children: templates.map((template) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { style: styles2.row, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
485
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
486
+ "div",
487
+ {
488
+ style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16 },
489
+ children: [
490
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { style: styles2.title, children: "Templates" }),
491
+ onCreateTemplate ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
492
+ "button",
493
+ {
494
+ type: "button",
495
+ onClick: () => onCreateTemplate({
496
+ locales: [
497
+ ...new Set(templates.map((t) => t.locale).filter((l) => !!l))
498
+ ]
499
+ }),
500
+ style: styles2.newButton,
501
+ children: "New template"
502
+ }
503
+ ) : null
504
+ ]
505
+ }
506
+ ),
507
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.hint, children: "Every email the app sends. Open one to edit its copy and preview it live." }),
508
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.status, children: "Loading templates\u2026" }) : error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.error, role: "alert", children: error.explanation }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { style: styles2.list, children: templates.map((template) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { style: styles2.row, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
336
509
  "button",
337
510
  {
338
511
  type: "button",
@@ -340,38 +513,316 @@ function TemplateListScreen({ client, onSelectTemplate }) {
340
513
  style: styles2.rowButton,
341
514
  children: [
342
515
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.type, children: template.type }),
343
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.locale, children: template.locale ?? "base" }),
344
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: template.active ? styles2.active : styles2.inactive, children: template.active ? "Active" : "Inactive" })
516
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.locale, children: template.locale ?? "default locale" }),
517
+ template.system ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.builtIn, children: "built-in" }) : null,
518
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: template.active ? styles2.active : styles2.inactive, children: [
519
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.dot }),
520
+ template.active ? "active" : "inactive"
521
+ ] })
345
522
  ]
346
523
  }
347
524
  ) }, `${template.type}:${template.locale ?? "base"}`)) })
348
525
  ] });
349
526
  }
527
+ var pill = {
528
+ display: "inline-flex",
529
+ alignItems: "center",
530
+ gap: 6,
531
+ borderRadius: 999,
532
+ padding: "2px 9px 2px 8px",
533
+ fontSize: 12,
534
+ fontWeight: 600,
535
+ lineHeight: 1.5,
536
+ minWidth: 64
537
+ };
350
538
  var styles2 = {
351
- container: { padding: 24, maxWidth: 640 },
352
- title: { fontSize: 24, fontWeight: 700, marginBottom: 16 },
353
- status: { padding: 24, textAlign: "center", color: "var(--fonderie-text-muted,#5c5c5c)" },
539
+ container: { padding: "32px 40px 64px", maxWidth: 1160, boxSizing: "border-box" },
540
+ title: {
541
+ fontSize: 22,
542
+ fontWeight: 600,
543
+ margin: 0,
544
+ letterSpacing: "var(--fonderie-tracking-display,-0.05em)",
545
+ lineHeight: 1.25
546
+ },
547
+ hint: { fontSize: 13.5, color: "var(--fonderie-text-muted,#5c5c5c)", margin: "4px 0 24px" },
548
+ status: { padding: "24px 0", color: "var(--fonderie-text-muted,#5c5c5c)", fontSize: 13.5 },
354
549
  error: { color: "var(--fonderie-danger,#e00)", marginBottom: 12, fontSize: 14 },
355
- list: { listStyle: "none", padding: 0, margin: 0 },
550
+ list: {
551
+ listStyle: "none",
552
+ padding: 0,
553
+ margin: 0,
554
+ background: "var(--fonderie-surface,#fff)",
555
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
556
+ borderRadius: "var(--fonderie-radius-lg,8px)",
557
+ overflow: "hidden",
558
+ boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))"
559
+ },
356
560
  row: { borderBottom: "1px solid var(--fonderie-border-light,#f5f5f5)" },
357
561
  rowButton: {
358
562
  width: "100%",
359
563
  display: "flex",
360
564
  justifyContent: "space-between",
361
565
  alignItems: "center",
362
- padding: "12px 0",
566
+ gap: 16,
567
+ padding: "13px 16px",
363
568
  background: "none",
364
569
  border: "none",
365
570
  cursor: "pointer",
366
- textAlign: "left"
571
+ textAlign: "left",
572
+ color: "var(--fonderie-text,#171717)",
573
+ fontFamily: "inherit"
574
+ },
575
+ type: {
576
+ fontSize: 13,
577
+ fontWeight: 600,
578
+ flex: 1,
579
+ fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)'
580
+ },
581
+ locale: { fontSize: 12.5, color: "var(--fonderie-text-muted,#5c5c5c)" },
582
+ dot: { width: 6, height: 6, borderRadius: 999, background: "currentColor" },
583
+ builtIn: {
584
+ fontSize: 11,
585
+ fontWeight: 500,
586
+ padding: "1px 7px",
587
+ borderRadius: 999,
588
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
589
+ color: "var(--fonderie-text-muted,#5c5c5c)"
590
+ },
591
+ newButton: {
592
+ display: "inline-flex",
593
+ alignItems: "center",
594
+ height: 32,
595
+ boxSizing: "border-box",
596
+ borderRadius: 6,
597
+ padding: "0 12px",
598
+ fontSize: 13,
599
+ fontWeight: 500,
600
+ fontFamily: "inherit",
601
+ cursor: "pointer",
602
+ backgroundColor: "var(--fonderie-text,#171717)",
603
+ color: "var(--fonderie-surface,#fff)",
604
+ border: "1px solid var(--fonderie-text,#171717)"
367
605
  },
368
- type: { fontSize: 14, fontWeight: 600, flex: 1 },
369
- locale: { fontSize: 13, color: "var(--fonderie-text-muted,#5c5c5c)", marginRight: 16 },
370
- active: { fontSize: 12, color: "var(--fonderie-accent-strong,#009767)" },
371
- inactive: { fontSize: 12, color: "var(--fonderie-text-muted,#5c5c5c)" }
606
+ active: {
607
+ ...pill,
608
+ color: "var(--fonderie-accent-strong,#009767)",
609
+ background: "color-mix(in srgb, var(--fonderie-accent-strong,#009767) 13%, transparent)"
610
+ },
611
+ inactive: {
612
+ ...pill,
613
+ color: "var(--fonderie-text-muted,#5c5c5c)",
614
+ background: "color-mix(in srgb, var(--fonderie-text-muted,#5c5c5c) 10%, transparent)"
615
+ }
616
+ };
617
+
618
+ // src/screens/TemplateCreateScreen.tsx
619
+ var import_client = require("@fonderie/client");
620
+ var import_react2 = require("react");
621
+ var import_jsx_runtime3 = require("react/jsx-runtime");
622
+ var TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;
623
+ var LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
624
+ function TemplateCreateScreen({
625
+ client,
626
+ type,
627
+ locales = [],
628
+ onCreated
629
+ }) {
630
+ const addingLocale = type !== void 0;
631
+ const [newType, setNewType] = (0, import_react2.useState)("");
632
+ const [locale, setLocale] = (0, import_react2.useState)("");
633
+ const [subject, setSubject] = (0, import_react2.useState)("");
634
+ const [html, setHtml] = (0, import_react2.useState)("");
635
+ const [text, setText] = (0, import_react2.useState)("");
636
+ const [error, setError] = (0, import_react2.useState)(null);
637
+ const [busy, setBusy] = (0, import_react2.useState)(false);
638
+ (0, import_react2.useEffect)(() => {
639
+ if (!type) return;
640
+ client.getTemplate(type, null).then(({ result }) => {
641
+ setSubject(result.subject ?? "");
642
+ setHtml(result.html ?? "");
643
+ setText(result.text);
644
+ }).catch(() => void 0);
645
+ }, [client, type]);
646
+ const submit = async (e) => {
647
+ e.preventDefault();
648
+ setError(null);
649
+ const t = addingLocale ? type : newType.trim();
650
+ const l = locale.trim() || null;
651
+ if (!TYPE_RE.test(t))
652
+ return setError("Type: lowercase letters, digits, - and _ (e.g. weekly-digest).");
653
+ if (addingLocale && !l) return setError("Choose the locale to add, e.g. fr or fr-CA.");
654
+ if (l && !LOCALE_RE.test(l)) return setError("Locale: a language tag such as fr, es or fr-CA.");
655
+ if (!text.trim())
656
+ return setError("The plain-text body is required \u2014 it is what every email client can show.");
657
+ setBusy(true);
658
+ try {
659
+ const exists = await client.getTemplate(t, l).then(
660
+ () => true,
661
+ (err) => !(err instanceof import_client.FonderieApiError && err.status === 404)
662
+ );
663
+ if (exists) {
664
+ setError(`"${t}"${l ? ` (${l})` : ""} already exists \u2014 open it from the list to edit.`);
665
+ return;
666
+ }
667
+ const input = { text, active: true };
668
+ if (subject) input.subject = subject;
669
+ if (html) input.html = html;
670
+ await client.setTemplate(t, input, l);
671
+ onCreated?.({ type: t, locale: l });
672
+ } catch (err) {
673
+ setError(err instanceof import_client.FonderieApiError ? err.explanation : String(err));
674
+ } finally {
675
+ setBusy(false);
676
+ }
677
+ };
678
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.container, children: [
679
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { style: styles3.title, children: addingLocale ? `Add a locale to ${type}` : "New template" }),
680
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.meta, children: 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." }),
681
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { style: styles3.form, onSubmit: (e) => void submit(e), children: [
682
+ !addingLocale ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
683
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-type", children: "Type" }),
684
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
685
+ "input",
686
+ {
687
+ id: "new-template-type",
688
+ style: styles3.input,
689
+ value: newType,
690
+ placeholder: "weekly-digest",
691
+ autoComplete: "off",
692
+ spellCheck: false,
693
+ onChange: (e) => setNewType(e.target.value)
694
+ }
695
+ )
696
+ ] }) : null,
697
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { style: styles3.label, htmlFor: "new-template-locale", children: [
698
+ "Locale ",
699
+ addingLocale ? "" : "(optional \u2014 empty is the default)"
700
+ ] }),
701
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
702
+ "input",
703
+ {
704
+ id: "new-template-locale",
705
+ style: styles3.input,
706
+ value: locale,
707
+ placeholder: "fr",
708
+ list: "new-template-locales",
709
+ autoComplete: "off",
710
+ spellCheck: false,
711
+ onChange: (e) => setLocale(e.target.value)
712
+ }
713
+ ),
714
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("datalist", { id: "new-template-locales", children: [...new Set(locales)].map((l) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: l }, l)) }),
715
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-subject", children: "Subject" }),
716
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
717
+ "input",
718
+ {
719
+ id: "new-template-subject",
720
+ style: styles3.input,
721
+ value: subject,
722
+ onChange: (e) => setSubject(e.target.value)
723
+ }
724
+ ),
725
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-html", children: "HTML body" }),
726
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
727
+ "textarea",
728
+ {
729
+ id: "new-template-html",
730
+ style: { ...styles3.textarea, minHeight: 160 },
731
+ value: html,
732
+ onChange: (e) => setHtml(e.target.value)
733
+ }
734
+ ),
735
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-text", children: "Plain-text body" }),
736
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
737
+ "textarea",
738
+ {
739
+ id: "new-template-text",
740
+ style: { ...styles3.textarea, minHeight: 100 },
741
+ value: text,
742
+ onChange: (e) => setText(e.target.value)
743
+ }
744
+ ),
745
+ error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.error, role: "alert", children: error }) : null,
746
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", disabled: busy, style: styles3.button, children: busy ? "Creating\u2026" : addingLocale ? "Add locale" : "Create template" })
747
+ ] })
748
+ ] });
749
+ }
750
+ var MONO = 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)';
751
+ var styles3 = {
752
+ container: { padding: "8px 40px 64px", maxWidth: 760, boxSizing: "border-box" },
753
+ title: {
754
+ fontSize: 22,
755
+ fontWeight: 600,
756
+ margin: "8px 0 4px",
757
+ letterSpacing: "var(--fonderie-tracking-display,-0.05em)",
758
+ lineHeight: 1.25
759
+ },
760
+ meta: {
761
+ fontSize: 13,
762
+ color: "var(--fonderie-text-muted,#5c5c5c)",
763
+ marginBottom: 20,
764
+ lineHeight: 1.5
765
+ },
766
+ form: {
767
+ display: "flex",
768
+ flexDirection: "column",
769
+ gap: 4,
770
+ background: "var(--fonderie-surface,#fff)",
771
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
772
+ borderRadius: "var(--fonderie-radius-lg,8px)",
773
+ padding: "6px 20px 20px"
774
+ },
775
+ label: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },
776
+ input: {
777
+ height: 36,
778
+ boxSizing: "border-box",
779
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
780
+ borderRadius: 6,
781
+ padding: "0 10px",
782
+ fontSize: 13.5,
783
+ fontFamily: "inherit",
784
+ background: "var(--fonderie-surface,#fff)",
785
+ color: "var(--fonderie-text,#171717)"
786
+ },
787
+ textarea: {
788
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
789
+ borderRadius: 6,
790
+ padding: 10,
791
+ fontSize: 13,
792
+ lineHeight: 1.55,
793
+ background: "var(--fonderie-surface,#fff)",
794
+ color: "var(--fonderie-text,#171717)",
795
+ fontFamily: MONO
796
+ },
797
+ error: {
798
+ color: "var(--fonderie-danger,#e00)",
799
+ background: "color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)",
800
+ borderRadius: 6,
801
+ padding: "8px 12px",
802
+ margin: "8px 0",
803
+ fontSize: 13.5
804
+ },
805
+ button: {
806
+ display: "inline-flex",
807
+ alignItems: "center",
808
+ height: 36,
809
+ boxSizing: "border-box",
810
+ backgroundColor: "var(--fonderie-text,#171717)",
811
+ color: "var(--fonderie-surface,#fff)",
812
+ padding: "0 16px",
813
+ borderRadius: 6,
814
+ border: "1px solid var(--fonderie-text,#171717)",
815
+ fontSize: 13.5,
816
+ fontWeight: 600,
817
+ fontFamily: "inherit",
818
+ cursor: "pointer",
819
+ alignSelf: "flex-start",
820
+ marginTop: 16
821
+ }
372
822
  };
373
823
  // Annotate the CommonJS export names for ESM import in node:
374
824
  0 && (module.exports = {
825
+ TemplateCreateScreen,
375
826
  TemplateEditorScreen,
376
827
  TemplateListScreen
377
828
  });