@twentyfirstmedia/ui-core 0.2.0 → 0.3.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.
Files changed (146) hide show
  1. package/dist/components/admin.d.ts.map +1 -1
  2. package/dist/components/admin.js +95 -71
  3. package/dist/components/admin.js.map +1 -1
  4. package/dist/components/agents.d.ts.map +1 -1
  5. package/dist/components/agents.js +49 -35
  6. package/dist/components/agents.js.map +1 -1
  7. package/dist/components/app-chrome.d.ts.map +1 -1
  8. package/dist/components/app-chrome.js +6 -2
  9. package/dist/components/app-chrome.js.map +1 -1
  10. package/dist/components/approvals.d.ts.map +1 -1
  11. package/dist/components/approvals.js +22 -18
  12. package/dist/components/approvals.js.map +1 -1
  13. package/dist/components/building-blocks.d.ts.map +1 -1
  14. package/dist/components/building-blocks.js +6 -2
  15. package/dist/components/building-blocks.js.map +1 -1
  16. package/dist/components/charts.d.ts.map +1 -1
  17. package/dist/components/charts.js +20 -10
  18. package/dist/components/charts.js.map +1 -1
  19. package/dist/components/chat.d.ts.map +1 -1
  20. package/dist/components/chat.js +31 -19
  21. package/dist/components/chat.js.map +1 -1
  22. package/dist/components/commerce.d.ts +2 -2
  23. package/dist/components/commerce.d.ts.map +1 -1
  24. package/dist/components/commerce.js +45 -31
  25. package/dist/components/commerce.js.map +1 -1
  26. package/dist/components/data-display.d.ts +5 -9
  27. package/dist/components/data-display.d.ts.map +1 -1
  28. package/dist/components/data-display.js +34 -35
  29. package/dist/components/data-display.js.map +1 -1
  30. package/dist/components/data-table.d.ts.map +1 -1
  31. package/dist/components/data-table.js +15 -10
  32. package/dist/components/data-table.js.map +1 -1
  33. package/dist/components/developer.d.ts.map +1 -1
  34. package/dist/components/developer.js +20 -11
  35. package/dist/components/developer.js.map +1 -1
  36. package/dist/components/feedback.d.ts.map +1 -1
  37. package/dist/components/feedback.js +12 -6
  38. package/dist/components/feedback.js.map +1 -1
  39. package/dist/components/form.d.ts +2 -1
  40. package/dist/components/form.d.ts.map +1 -1
  41. package/dist/components/form.js +12 -9
  42. package/dist/components/form.js.map +1 -1
  43. package/dist/components/forms.d.ts +2 -1
  44. package/dist/components/forms.d.ts.map +1 -1
  45. package/dist/components/forms.js +92 -42
  46. package/dist/components/forms.js.map +1 -1
  47. package/dist/components/foundation.d.ts +5 -2
  48. package/dist/components/foundation.d.ts.map +1 -1
  49. package/dist/components/foundation.js +15 -12
  50. package/dist/components/foundation.js.map +1 -1
  51. package/dist/components/inputs.d.ts.map +1 -1
  52. package/dist/components/inputs.js +3 -1
  53. package/dist/components/inputs.js.map +1 -1
  54. package/dist/components/mail.d.ts.map +1 -1
  55. package/dist/components/mail.js +15 -10
  56. package/dist/components/mail.js.map +1 -1
  57. package/dist/components/media.d.ts.map +1 -1
  58. package/dist/components/media.js +39 -18
  59. package/dist/components/media.js.map +1 -1
  60. package/dist/components/navigation.d.ts.map +1 -1
  61. package/dist/components/navigation.js +21 -9
  62. package/dist/components/navigation.js.map +1 -1
  63. package/dist/components/operations.d.ts.map +1 -1
  64. package/dist/components/operations.js +37 -27
  65. package/dist/components/operations.js.map +1 -1
  66. package/dist/components/previews.d.ts.map +1 -1
  67. package/dist/components/previews.js +14 -10
  68. package/dist/components/previews.js.map +1 -1
  69. package/dist/components/primitives.d.ts.map +1 -1
  70. package/dist/components/primitives.js +4 -1
  71. package/dist/components/primitives.js.map +1 -1
  72. package/dist/components/real-estate.d.ts.map +1 -1
  73. package/dist/components/real-estate.js +43 -28
  74. package/dist/components/real-estate.js.map +1 -1
  75. package/dist/components/rich-text-editor.d.ts.map +1 -1
  76. package/dist/components/rich-text-editor.js +18 -15
  77. package/dist/components/rich-text-editor.js.map +1 -1
  78. package/dist/components/select-menu.d.ts.map +1 -1
  79. package/dist/components/select-menu.js +4 -1
  80. package/dist/components/select-menu.js.map +1 -1
  81. package/dist/components/shell.d.ts.map +1 -1
  82. package/dist/components/shell.js +8 -3
  83. package/dist/components/shell.js.map +1 -1
  84. package/dist/components/site.d.ts.map +1 -1
  85. package/dist/components/site.js +17 -13
  86. package/dist/components/site.js.map +1 -1
  87. package/dist/components/types.d.ts +102 -3
  88. package/dist/components/types.d.ts.map +1 -1
  89. package/dist/components/workspace.d.ts.map +1 -1
  90. package/dist/components/workspace.js +50 -34
  91. package/dist/components/workspace.js.map +1 -1
  92. package/dist/context.d.ts +39 -0
  93. package/dist/context.d.ts.map +1 -0
  94. package/dist/context.js +76 -0
  95. package/dist/context.js.map +1 -0
  96. package/dist/format.d.ts +33 -6
  97. package/dist/format.d.ts.map +1 -1
  98. package/dist/format.js +75 -10
  99. package/dist/format.js.map +1 -1
  100. package/dist/index.d.ts +3 -2
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +3 -2
  103. package/dist/index.js.map +1 -1
  104. package/dist/provider.d.ts +18 -11
  105. package/dist/provider.d.ts.map +1 -1
  106. package/dist/provider.js +11 -18
  107. package/dist/provider.js.map +1 -1
  108. package/dist/strings.d.ts +503 -0
  109. package/dist/strings.d.ts.map +1 -0
  110. package/dist/strings.js +537 -0
  111. package/dist/strings.js.map +1 -0
  112. package/package.json +1 -1
  113. package/src/components/admin.tsx +117 -93
  114. package/src/components/agents.tsx +55 -41
  115. package/src/components/app-chrome.tsx +6 -2
  116. package/src/components/approvals.tsx +26 -22
  117. package/src/components/building-blocks.tsx +6 -2
  118. package/src/components/charts.tsx +21 -11
  119. package/src/components/chat.tsx +45 -35
  120. package/src/components/commerce.tsx +61 -53
  121. package/src/components/data-display.tsx +38 -45
  122. package/src/components/data-table.tsx +20 -15
  123. package/src/components/developer.tsx +26 -15
  124. package/src/components/feedback.tsx +19 -7
  125. package/src/components/form.tsx +17 -9
  126. package/src/components/forms.tsx +131 -53
  127. package/src/components/foundation.tsx +23 -36
  128. package/src/components/inputs.tsx +4 -2
  129. package/src/components/mail.tsx +16 -11
  130. package/src/components/media.tsx +64 -21
  131. package/src/components/navigation.tsx +25 -13
  132. package/src/components/operations.tsx +40 -32
  133. package/src/components/previews.tsx +17 -15
  134. package/src/components/primitives.tsx +8 -5
  135. package/src/components/real-estate.tsx +55 -35
  136. package/src/components/rich-text-editor.tsx +20 -17
  137. package/src/components/select-menu.tsx +5 -2
  138. package/src/components/shell.tsx +8 -3
  139. package/src/components/site.tsx +18 -14
  140. package/src/components/types.ts +111 -3
  141. package/src/components/workspace.tsx +79 -60
  142. package/src/context.ts +112 -0
  143. package/src/format.ts +108 -15
  144. package/src/index.ts +43 -0
  145. package/src/provider.tsx +36 -40
  146. package/src/strings.ts +1500 -0
@@ -1 +1 @@
1
- {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/components/admin.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAkB/B,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,eAAe,EACf,eAAe,EAEf,iBAAiB,EAEjB,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EAEnB,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,mBAAmB,EAEnB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAGpB,wBAAgB,WAAW,CAAC,CAAC,EAAE,gBAAgB,qBA4F9C;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,YAAY,qBA+CtC;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,iBAAiB,qBA0DhD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,eAAe,qBAmE5C;AAGD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,aAAa,qBAgHxC;AAOD,wBAAgB,SAAS,sBAqBxB;AAID,wBAAgB,OAAO,CAAC,CAAC,EAAE,YAAY,qBAqGtC;AAKD,wBAAgB,WAAW,CAAC,CAAC,EAAE,gBAAgB,qBAsE9C;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,mBAAmB,qBAiCpD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,eAAe,qBAwC5C;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,oBAAoB,qBA4EtD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,eAAe,qBA4C5C;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,cAAc,qBAmE1C;AAKD,wBAAgB,UAAU,CAAC,CAAC,EAAE,eAAe,qBAoH5C;AAYD,wBAAgB,UAAU,CAAC,CAAC,EAAE,eAAe,qBA6E5C;AAGD,wBAAgB,WAAW,CAAC,CAAC,EAAE,gBAAgB,qBAgG9C;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,mBAAmB,qBAsGpD;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,sBAAsB,qBAwF1D;AAKD,wBAAgB,YAAY,CAAC,CAAC,EAAE,iBAAiB,4BA6JhD;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,mBAAmB,qBAiKpD;AAID,wBAAgB,UAAU,CAAC,CAAC,EAAE,eAAe,qBA2D5C"}
1
+ {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/components/admin.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAmB/B,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,eAAe,EACf,eAAe,EAEf,iBAAiB,EAEjB,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EAEnB,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,mBAAmB,EAEnB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAGpB,wBAAgB,WAAW,CAAC,CAAC,EAAE,gBAAgB,qBA8F9C;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,YAAY,qBAgDtC;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,iBAAiB,qBA0DhD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,eAAe,qBAmE5C;AAGD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,aAAa,qBAkHxC;AAOD,wBAAgB,SAAS,sBAqBxB;AAID,wBAAgB,OAAO,CAAC,CAAC,EAAE,YAAY,qBAuGtC;AAKD,wBAAgB,WAAW,CAAC,CAAC,EAAE,gBAAgB,qBAwE9C;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,mBAAmB,qBAkCpD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,eAAe,qBAyC5C;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,oBAAoB,qBA8EtD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,eAAe,qBA6C5C;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,cAAc,qBAoE1C;AAKD,wBAAgB,UAAU,CAAC,CAAC,EAAE,eAAe,qBAqH5C;AAYD,wBAAgB,UAAU,CAAC,CAAC,EAAE,eAAe,qBA+E5C;AAGD,wBAAgB,WAAW,CAAC,CAAC,EAAE,gBAAgB,qBAiG9C;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,mBAAmB,qBAuGpD;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,sBAAsB,qBAwF1D;AAKD,wBAAgB,YAAY,CAAC,CAAC,EAAE,iBAAiB,4BA+JhD;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,mBAAmB,qBAgKpD;AAID,wBAAgB,UAAU,CAAC,CAAC,EAAE,eAAe,qBA6D5C"}
@@ -2,7 +2,8 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { useControlled, useId } from "../hooks.js";
5
- import { formatMoney, formatNumber } from "../format.js";
5
+ import { useFormat, useStrings } from "../context.js";
6
+ import { ADMIN_STRINGS } from "../strings.js";
6
7
  import { cx } from "../cx.js";
7
8
  import { Icon } from "../Icon.js";
8
9
  import { toast } from "../toast-store.js";
@@ -20,6 +21,8 @@ import { Meter } from "./developer.js";
20
21
  import { Progress } from "./data-display.js";
21
22
  /* ── Shared: range slider, bar list, status select, tile picker ── */
22
23
  export function RangeSlider(p) {
24
+ const t = useStrings(ADMIN_STRINGS);
25
+ const { formatNumber } = useFormat();
23
26
  const id = useId("rs"), min = p.min || 0, max = p.max != null ? p.max : 100, step = p.step || 1, f = p.format || formatNumber;
24
27
  const v = useControlled(p.value, p.defaultValue || [min, max], p.onChange), lo = v[0][0], hi = v[0][1];
25
28
  function pc(x) {
@@ -36,17 +39,18 @@ export function RangeSlider(p) {
36
39
  return (_jsx(FormField, { id: id, label: p.label, hint: p.hint, children: _jsxs("div", { className: "tf-range", children: [_jsxs("div", { className: "tf-range-track", children: [_jsx("span", { style: {
37
40
  left: pc(lo) + "%",
38
41
  width: pc(hi) - pc(lo) + "%",
39
- } }), _jsx("input", { type: "range", min: min, max: max, step: step, value: lo, "aria-label": "Minimum", onChange: (e) => {
42
+ } }), _jsx("input", { type: "range", min: min, max: max, step: step, value: lo, "aria-label": t.minimum, onChange: (e) => {
40
43
  set(0, e.target.value);
41
- } }), _jsx("input", { type: "range", min: min, max: max, step: step, value: hi, "aria-label": "Maximum", onChange: (e) => {
44
+ } }), _jsx("input", { type: "range", min: min, max: max, step: step, value: hi, "aria-label": t.maximum, onChange: (e) => {
42
45
  set(1, e.target.value);
43
- } })] }), p.inputs !== false ? (_jsxs("div", { className: "tf-range-inputs", children: [_jsxs("label", { className: "tf-range-num", children: [_jsx("small", { children: "Min" }), _jsx("input", { id: id, className: "tf-input", inputMode: "numeric", value: formatNumber(lo), onChange: (e) => {
46
+ } })] }), p.inputs !== false ? (_jsxs("div", { className: "tf-range-inputs", children: [_jsxs("label", { className: "tf-range-num", children: [_jsx("small", { children: t.min }), _jsx("input", { id: id, className: "tf-input", inputMode: "numeric", value: formatNumber(lo), onChange: (e) => {
44
47
  set(0, e.target.value.replace(/\D/g, "") || min);
45
- } }), p.unit && _jsx("span", { children: p.unit })] }), _jsx("span", { className: "tf-range-dash", "aria-hidden": true, children: "\u2013" }), _jsxs("label", { className: "tf-range-num", children: [_jsx("small", { children: "Max" }), _jsx("input", { className: "tf-input", inputMode: "numeric", value: formatNumber(hi), onChange: (e) => {
48
+ } }), p.unit && _jsx("span", { children: p.unit })] }), _jsx("span", { className: "tf-range-dash", "aria-hidden": true, children: "\u2013" }), _jsxs("label", { className: "tf-range-num", children: [_jsx("small", { children: t.max }), _jsx("input", { className: "tf-input", inputMode: "numeric", value: formatNumber(hi), onChange: (e) => {
46
49
  set(1, e.target.value.replace(/\D/g, "") || max);
47
50
  } }), p.unit && _jsx("span", { children: p.unit })] })] })) : (_jsxs("div", { className: "tf-range-vals", children: [_jsx("span", { children: f(lo) }), _jsx("span", { children: hi >= max && p.openEnd ? f(hi) + "+" : f(hi) })] }))] }) }));
48
51
  }
49
52
  export function BarList(p) {
53
+ const { formatNumber } = useFormat();
50
54
  const items = p.items || [], max = Math.max.apply(null, items.map((i) => i.value).concat([1])), f = p.format || formatNumber;
51
55
  return (_jsx("div", { className: "tf-barlist", role: "list", "aria-label": p.label, children: items.map((it, i) => (_jsxs("div", { role: "listitem", className: cx("tf-barlist-row", p.onPick && "is-btn"), onClick: p.onPick &&
52
56
  (() => {
@@ -93,23 +97,25 @@ export function TilePicker(p) {
93
97
  }
94
98
  /* ── Live feed ── */
95
99
  export function LiveFeed(p) {
100
+ const t = useStrings(ADMIN_STRINGS);
101
+ const { formatMoney } = useFormat();
96
102
  const paused = useControlled(p.paused, false, p.onPause), held = React.useRef(null), filter = React.useState(null);
97
103
  if (!paused[0] || !held.current)
98
104
  held.current = p.items || [];
99
105
  const heldItems = held.current;
100
106
  const shown = (paused[0] ? heldItems : p.items || []).filter((it) => !filter[0] || it.type === filter[0]), waiting = paused[0] ? Math.max(0, (p.items || []).length - heldItems.length) : 0;
101
107
  const types = p.types || [];
102
- return (_jsxs("div", { className: "tf-livefeed", children: [_jsxs("div", { className: "tf-livefeed-head", children: [_jsxs("span", { className: cx("tf-live", paused[0] && "off"), children: [_jsx("span", { className: "tf-live-dot", "aria-hidden": true }), paused[0] ? "Paused" : "Live"] }), types.length > 0 && (_jsxs("div", { className: "tf-row", style: {
108
+ return (_jsxs("div", { className: "tf-livefeed", children: [_jsxs("div", { className: "tf-livefeed-head", children: [_jsxs("span", { className: cx("tf-live", paused[0] && "off"), children: [_jsx("span", { className: "tf-live-dot", "aria-hidden": true }), paused[0] ? t.paused : t.live] }), types.length > 0 && (_jsxs("div", { className: "tf-row", style: {
103
109
  gap: 4,
104
110
  }, children: [_jsx(Chip, { selected: !filter[0], onClick: () => {
105
111
  filter[1](null);
106
- }, children: "All" }), types.map((t) => (_jsx(Chip, { selected: filter[0] === t.value, onClick: () => {
112
+ }, children: t.all }), types.map((t) => (_jsx(Chip, { selected: filter[0] === t.value, onClick: () => {
107
113
  filter[1](filter[0] === t.value ? null : t.value);
108
114
  }, children: t.label }, t.value)))] })), _jsx("span", { style: {
109
115
  flex: 1,
110
116
  } }), waiting > 0 && (_jsx("button", { type: "button", className: "tf-livefeed-new", onClick: () => {
111
117
  paused[1](false);
112
- }, children: `${waiting} new` })), _jsx(IconButton, { icon: paused[0] ? "play" : "pause", size: "sm", label: paused[0] ? "Resume" : "Pause", onClick: () => {
118
+ }, children: t.newCount(waiting) })), _jsx(IconButton, { icon: paused[0] ? "play" : "pause", size: "sm", label: paused[0] ? t.resume : t.pause, onClick: () => {
113
119
  paused[1](!paused[0]);
114
120
  } })] }), _jsx("div", { className: "tf-livefeed-list", role: "log", "aria-live": paused[0] ? "off" : "polite", style: p.maxHeight
115
121
  ? {
@@ -117,7 +123,7 @@ export function LiveFeed(p) {
117
123
  }
118
124
  : undefined, children: shown.length === 0 ? (_jsx("p", { className: "tf-card-desc", style: {
119
125
  padding: 16,
120
- }, children: p.emptyText || "Waiting for events…" })) : (shown.map((it) => (_jsxs("div", { className: cx("tf-livefeed-row", it.fresh && "fresh"), children: [_jsx(TintIcon, { icon: it.icon || "zap", tint: it.tint || "sky", size: 32 }), _jsxs("span", { className: "tf-livefeed-text", children: [_jsx("b", { children: it.title }), it.text && _jsx("small", { children: it.text })] }), it.amount != null && (_jsx("span", { className: cx("tf-livefeed-amt", it.amount < 0 && "neg"), children: (it.amount > 0 ? "+" : "") + formatMoney(it.amount, it.currency) })), _jsx("span", { className: "tf-livefeed-time", children: it.time })] }, it.id)))) })] }));
126
+ }, children: p.emptyText || t.waitingForEvents })) : (shown.map((it) => (_jsxs("div", { className: cx("tf-livefeed-row", it.fresh && "fresh"), children: [_jsx(TintIcon, { icon: it.icon || "zap", tint: it.tint || "sky", size: 32 }), _jsxs("span", { className: "tf-livefeed-text", children: [_jsx("b", { children: it.title }), it.text && _jsx("small", { children: it.text })] }), it.amount != null && (_jsx("span", { className: cx("tf-livefeed-amt", it.amount < 0 && "neg"), children: (it.amount > 0 ? "+" : "") + formatMoney(it.amount, it.currency) })), _jsx("span", { className: "tf-livefeed-time", children: it.time })] }, it.id)))) })] }));
121
127
  }
122
128
  const WORLD = "0000000001fe03ff000000000000000000000000000079f7ffff8001c01000070000000000000010439fffff0002c0000000000000000000007cdf007fff00000001800ffc01e000000003c4af003ffe0000000408ffffe0000001f8001e67f83ffc000070001bffffffff0083ffffc67e5c1fe00003ff2ffbffffffffff67fffffff83a1f01c007bcffffffffffffff03ffffffe0080e00001e7fffffffffffffff03fbffffc0700200003e5ffffffffffffc6000403fffe07e000002063fffffffffff018001001ffffc7f0000021cfffffffffffc038000000ffffe7fc0000f3fffffffffffff8200000003ffffff0000027fffffffffffff4000000003fffffce00003ffffffffffffff0000000003ffffff000000fbf5e7fffffffe0000000003fffff000000f8de0e7fffffffcc000000003ffffc000000f12eff7ffffff708000000001ffff8000000f00dff3fffffe300000000000ffffc0000001f043ffffffff1700000000007fff0000000ff803ffffffff0800000000001ffe0000000ffeffffffffff8000000000003f810000001fffffe7ffffff0000000000001f810000003ffffdfbffffff00000000000007800000007ffffcff0ffffe80000000400003884000007ffffefe07e7e000000000000001f83400003ffffe7e07c3e0800000000000005c0000007fffff780701e0800000000000000e0000007fffffc00301f0c0000000000000023900003ffffff003016040000000000000017f00001ffffff000810060000000000000007fc0000f3ffff000008200000000000000007ff000000fffe00001860000000000000000fff000000fffc000008ec000000000000000fffc00000fff800000ce8400000000000000ffff80000fff0000004003c0000000000000ffffc00007ff0000002801e00000000000007fffc00007ff0000000000808000000000007fff800007ff0000000000000000000000007fff000007ff100000002c000000000000001fff000007ff300000007ec00000000000000fff000007fc300000007fe00000000000000fff000003fc60000003fff02000000000000ffc000003fc60000007fff80000000000000ff8000003f800000003fff80000000000001ff0000001f800000003fff80000000000001ff0000001f000000003fff80000000000001fe0000001800000000303f80000000000001f80000000000000000000f00000000000001f00000000000000000000200200000000003c00000000000000000000200400000000001c000000000000000000000008000000000038000000000000000000000010000000000038000000000000000000000000000000000030800000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
123
129
  let worldPath = null;
@@ -139,6 +145,8 @@ export function WorldDots() {
139
145
  /* ── Map with pins (background is yours: tiles, an SVG, an image) ── */
140
146
  /* ── Map with pins (background is yours: tiles, an SVG, an image) ── */
141
147
  export function MapView(p) {
148
+ const t = useStrings(ADMIN_STRINGS);
149
+ const { formatNumber } = useFormat();
142
150
  const b = p.bounds || {
143
151
  n: 84,
144
152
  s: -60,
@@ -155,7 +163,7 @@ export function MapView(p) {
155
163
  const cur = pts.find((x) => x.id === sel[0]);
156
164
  return (_jsxs("div", { className: cx("tf-map", p.className), style: {
157
165
  height: Hh,
158
- }, role: "region", "aria-label": p.label || "Map", children: [p.background || (!p.bounds ? _jsx(WorldDots, {}) : null) || (_jsx("svg", { className: "tf-map-grid", viewBox: "0 0 100 100", preserveAspectRatio: "none", "aria-hidden": true, children: [10, 20, 30, 40, 50, 60, 70, 80, 90].map((g) => (_jsxs("g", { children: [_jsx("line", { x1: g, x2: g, y1: 0, y2: 100 }), _jsx("line", { y1: g, y2: g, x1: 0, x2: 100 })] }, g))) })), pts.map((pt) => {
166
+ }, role: "region", "aria-label": p.label || t.viewMap, children: [p.background || (!p.bounds ? _jsx(WorldDots, {}) : null) || (_jsx("svg", { className: "tf-map-grid", viewBox: "0 0 100 100", preserveAspectRatio: "none", "aria-hidden": true, children: [10, 20, 30, 40, 50, 60, 70, 80, 90].map((g) => (_jsxs("g", { children: [_jsx("line", { x1: g, x2: g, y1: 0, y2: 100 }), _jsx("line", { y1: g, y2: g, x1: 0, x2: 100 })] }, g))) })), pts.map((pt) => {
159
167
  const c = xy(pt), r = p.bubbles ? 10 + Math.sqrt((pt.value || 1) / maxV) * 26 : null;
160
168
  return (_jsx("button", { type: "button", className: cx(p.bubbles ? "tf-map-bubble" : "tf-map-pin", "tf-t-" + (pt.tint || "sky"), sel[0] === pt.id && "on", pt.pulse && "pulse"), style: {
161
169
  left: c.x + "%",
@@ -179,21 +187,24 @@ export function MapView(p) {
179
187
  /* ── Rich text editor (Selection/Range based, no execCommand) ── */
180
188
  /* ── Money and operations for admins ── */
181
189
  export function BalanceCard(p) {
190
+ const t = useStrings(ADMIN_STRINGS);
191
+ const { formatMoney } = useFormat();
182
192
  const days = p.perDay && p.perDay > 0 ? Math.floor(p.balance / p.perDay) : null, low = p.low != null && p.balance <= p.low, st = p.status || (p.balance <= 0 ? "danger" : low ? "warn" : "ok");
183
- return (_jsxs("div", { className: cx("tf-balance", "is-" + st), children: [_jsxs("div", { className: "tf-balance-head", children: [p.logo || _jsx(TintIcon, { icon: p.icon || "wallet", tint: p.tint || "butter", size: 36 }), _jsxs("div", { className: "tf-option-text", children: [_jsx("b", { children: p.name }), p.sub && _jsx("small", { children: p.sub })] }), _jsx(StatusIndicator, { status: st, label: p.statusLabel || (st === "danger" ? "Empty" : low ? "Low" : "OK"), size: "sm" })] }), _jsxs("div", { className: "tf-balance-main", children: [_jsxs("div", { className: "tf-col", style: {
193
+ return (_jsxs("div", { className: cx("tf-balance", "is-" + st), children: [_jsxs("div", { className: "tf-balance-head", children: [p.logo || _jsx(TintIcon, { icon: p.icon || "wallet", tint: p.tint || "butter", size: 36 }), _jsxs("div", { className: "tf-option-text", children: [_jsx("b", { children: p.name }), p.sub && _jsx("small", { children: p.sub })] }), _jsx(StatusIndicator, { status: st, label: p.statusLabel || (st === "danger" ? t.balanceEmpty : low ? t.balanceLow : t.balanceOk), size: "sm" })] }), _jsxs("div", { className: "tf-balance-main", children: [_jsxs("div", { className: "tf-col", style: {
184
194
  gap: 2,
185
195
  }, children: [_jsx("span", { className: "tf-balance-amt", children: formatMoney(p.balance, p.currency, {
186
196
  maximumFractionDigits: 2,
187
197
  }) }), _jsx("span", { className: "tf-balance-meta", children: [
188
- p.perDay != null && `${formatMoney(p.perDay, p.currency)} per day`,
189
- days != null && p.balance > 0 && `lasts about ${days} days`,
198
+ p.perDay != null && t.perDay(formatMoney(p.perDay, p.currency)),
199
+ days != null && p.balance > 0 && t.lastsAbout(days),
190
200
  ]
191
201
  .filter(Boolean)
192
- .join(" · ") }), p.low != null && (_jsx("span", { className: "tf-balance-meta", children: `Warns below ${formatMoney(p.low, p.currency)}` }))] }), p.spark && (_jsx(Sparkline, { values: p.spark, width: 96, height: 32, tint: st === "ok" ? "mint" : st === "warn" ? "butter" : "blush" }))] }), (p.onTopUp || p.action) && (_jsxs("div", { className: "tf-row", style: {
202
+ .join(" · ") }), p.low != null && (_jsx("span", { className: "tf-balance-meta", children: t.warnsBelow(formatMoney(p.low, p.currency)) }))] }), p.spark && (_jsx(Sparkline, { values: p.spark, width: 96, height: 32, tint: st === "ok" ? "mint" : st === "warn" ? "butter" : "blush" }))] }), (p.onTopUp || p.action) && (_jsxs("div", { className: "tf-row", style: {
193
203
  justifyContent: "flex-end",
194
- }, children: [p.action, p.onTopUp && (_jsx(Button, { size: "sm", variant: low ? "primary" : "soft", icon: "plus", onClick: p.onTopUp, children: p.topUpLabel || "Top up" }))] }))] }));
204
+ }, children: [p.action, p.onTopUp && (_jsx(Button, { size: "sm", variant: low ? "primary" : "soft", icon: "plus", onClick: p.onTopUp, children: p.topUpLabel || t.topUp }))] }))] }));
195
205
  }
196
206
  export function ServiceMonitor(p) {
207
+ const { formatNumber } = useFormat();
197
208
  return (_jsx("div", { className: "tf-svcmon", role: "list", "aria-label": p.label, children: (p.items || []).map((s) => (_jsxs("div", { role: "listitem", className: "tf-svcmon-row", children: [_jsx(StatusShape, { status: s.status }), _jsxs("span", { className: "tf-option-text", children: [_jsx("b", { children: s.name }), _jsx("small", { children: s.statusLabel || s.status })] }), s.history && (_jsx("span", { className: "tf-svcmon-hist", "aria-hidden": true, children: s.history.map((x, i) => (_jsx("i", { className: "tf-st-" + x }, i))) })), _jsx("span", { className: "tf-svcmon-num", children: s.latency != null ? formatNumber(s.latency) + " ms" : "—" }), _jsx("span", { className: cx("tf-svcmon-num", (s.errorRate ?? 0) > 1 && "bad"), children: s.errorRate != null
198
209
  ? formatNumber(s.errorRate / 100, {
199
210
  style: "percent",
@@ -202,6 +213,7 @@ export function ServiceMonitor(p) {
202
213
  : "—" }), _jsx("span", { className: "tf-svcmon-time", children: s.checked })] }, s.id || s.name))) }));
203
214
  }
204
215
  export function RunHistory(p) {
216
+ const { formatNumber } = useFormat();
205
217
  const open = React.useState(null);
206
218
  return (_jsx("div", { className: "tf-runs", role: "list", "aria-label": p.label, children: (p.items || []).map((r) => {
207
219
  const on = open[0] === r.id;
@@ -213,6 +225,8 @@ export function RunHistory(p) {
213
225
  }) }));
214
226
  }
215
227
  export function WebhookEndpoint(p) {
228
+ const t = useStrings(ADMIN_STRINGS);
229
+ const { formatNumber } = useFormat();
216
230
  const open = React.useState(!!p.defaultOpen);
217
231
  return (_jsxs("div", { className: "tf-hook", children: [_jsxs("div", { className: "tf-hook-head", children: [_jsx(TintIcon, { icon: "webhook", tint: "lilac", size: 36 }), _jsxs("div", { className: "tf-option-text", style: {
218
232
  minWidth: 0,
@@ -220,64 +234,67 @@ export function WebhookEndpoint(p) {
220
234
  }, children: [_jsx("b", { className: "tf-mono tf-hook-url", children: p.url }), _jsx("small", { children: (p.events || []).join(", ") })] }), _jsx(StatusIndicator, { status: p.status || "ok", label: p.statusLabel, size: "sm" }), p.successRate != null && (_jsx("span", { className: "tf-hook-rate", children: formatNumber(p.successRate / 100, {
221
235
  style: "percent",
222
236
  maximumFractionDigits: 1,
223
- }) })), p.onToggle && (_jsx(Switch, { checked: p.enabled !== false, onChange: p.onToggle, label: "Enabled" })), p.menu && (_jsx(Popover, { width: 200, trigger: (s) => _jsx(IconButton, { icon: "more", size: "sm", label: "More", onClick: s.toggle }), children: () => _jsx(Menu, { items: p.menu || [] }) }))] }), p.secret && p.secret, (p.deliveries || []).length > 0 && (_jsx(Collapsible, { title: "Recent deliveries", meta: formatNumber((p.deliveries || []).length), open: open[0], onOpenChange: open[1], children: _jsx("div", { className: "tf-hook-dl", children: (p.deliveries || []).map((d) => {
237
+ }) })), p.onToggle && (_jsx(Switch, { checked: p.enabled !== false, onChange: p.onToggle, label: t.enabled })), p.menu && (_jsx(Popover, { width: 200, trigger: (s) => _jsx(IconButton, { icon: "more", size: "sm", label: t.more, onClick: s.toggle }), children: () => _jsx(Menu, { items: p.menu || [] }) }))] }), p.secret && p.secret, (p.deliveries || []).length > 0 && (_jsx(Collapsible, { title: t.recentDeliveries, meta: formatNumber((p.deliveries || []).length), open: open[0], onOpenChange: open[1], children: _jsx("div", { className: "tf-hook-dl", children: (p.deliveries || []).map((d) => {
224
238
  const ok = d.code != null && d.code >= 200 && d.code < 300;
225
239
  return (_jsxs("div", { className: "tf-hook-dl-row", children: [_jsx("span", { className: cx("tf-hook-code", ok ? "ok" : "bad"), children: d.code || "—" }), _jsx("span", { className: "tf-mono", children: d.event }), _jsxs("span", { className: "tf-hook-dl-time", children: [d.time, d.ms != null && " · " + formatNumber(d.ms) + " ms"] }), !ok && p.onRetry && (_jsx(Button, { size: "sm", variant: "soft", icon: "refresh", onClick: () => {
226
240
  p.onRetry?.(d);
227
- }, children: "Retry" }))] }, d.id));
241
+ }, children: t.retry }))] }, d.id));
228
242
  }) }) }))] }));
229
243
  }
230
244
  export function BackupList(p) {
245
+ const t = useStrings(ADMIN_STRINGS);
231
246
  return (_jsxs("div", { className: "tf-backups", role: "list", "aria-label": p.label, children: [(p.items || []).map((b) => (_jsxs("div", { role: "listitem", className: "tf-backups-row", children: [_jsx(StatusShape, { status: b.status || "ok" }), _jsxs("span", { className: "tf-option-text", children: [_jsx("b", { children: b.name || b.time }), _jsx("small", { children: [b.kind, b.where, b.retention].filter(Boolean).join(" · ") })] }), _jsx("span", { className: "tf-backups-size", children: b.size }), _jsxs("span", { className: "tf-row", style: {
232
247
  gap: 4,
233
- }, children: [p.onDownload && (_jsx(IconButton, { icon: "download", size: "sm", label: "Download", onClick: () => {
248
+ }, children: [p.onDownload && (_jsx(IconButton, { icon: "download", size: "sm", label: t.download, onClick: () => {
234
249
  p.onDownload?.(b);
235
250
  } })), p.onRestore && (_jsx(Button, { size: "sm", variant: "secondary", onClick: () => {
236
251
  p.onRestore?.(b);
237
- }, children: "Restore" }))] })] }, b.id))), p.footer && _jsx("div", { className: "tf-backups-foot", children: p.footer })] }));
252
+ }, children: t.restore }))] })] }, b.id))), p.footer && _jsx("div", { className: "tf-backups-foot", children: p.footer })] }));
238
253
  }
239
254
  export function BlockList(p) {
255
+ const t = useStrings(ADMIN_STRINGS);
240
256
  const q = React.useState("");
241
257
  const list = (p.items || []).filter((b) => !q[0] || (b.value + " " + (b.reason || "")).toLowerCase().indexOf(q[0].toLowerCase()) > -1);
242
258
  return (_jsxs("div", { className: "tf-col", style: {
243
259
  gap: 12,
244
- }, children: [_jsxs("div", { className: "tf-filterbar", children: [_jsx(SearchField, { placeholder: p.searchPlaceholder || "Search blocked entries", value: q[0], onChange: (e) => {
260
+ }, children: [_jsxs("div", { className: "tf-filterbar", children: [_jsx(SearchField, { placeholder: p.searchPlaceholder || t.searchBlocked, value: q[0], onChange: (e) => {
245
261
  q[1](e.target.value);
246
262
  }, style: {
247
263
  flex: "1 1 220px",
248
264
  minWidth: 0,
249
- } }), p.onAdd && (_jsx(Button, { size: "sm", icon: "plus", onClick: p.onAdd, children: p.addLabel || "Block" }))] }), list.length === 0 ? (_jsx("p", { className: "tf-card-desc", children: p.emptyText || "Nothing blocked." })) : (_jsx("div", { className: "tf-blocks", role: "list", children: list.map((b) => (_jsxs("div", { role: "listitem", className: "tf-blocks-row", children: [_jsx(Icon, { name: "ban", size: 15 }), _jsxs("span", { className: "tf-option-text", style: {
265
+ } }), p.onAdd && (_jsx(Button, { size: "sm", icon: "plus", onClick: p.onAdd, children: p.addLabel || t.block }))] }), list.length === 0 ? (_jsx("p", { className: "tf-card-desc", children: p.emptyText || t.nothingBlocked })) : (_jsx("div", { className: "tf-blocks", role: "list", children: list.map((b) => (_jsxs("div", { role: "listitem", className: "tf-blocks-row", children: [_jsx(Icon, { name: "ban", size: 15 }), _jsxs("span", { className: "tf-option-text", style: {
250
266
  minWidth: 0,
251
- }, children: [_jsx("b", { className: "tf-mono", children: b.value }), _jsx("small", { children: [b.kind, b.reason].filter(Boolean).join(" · ") })] }), _jsx("span", { className: "tf-blocks-meta", children: [b.by, b.until ? `until ${b.until}` : "permanent"].filter(Boolean).join(" · ") }), p.onRemove && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
267
+ }, children: [_jsx("b", { className: "tf-mono", children: b.value }), _jsx("small", { children: [b.kind, b.reason].filter(Boolean).join(" · ") })] }), _jsx("span", { className: "tf-blocks-meta", children: [b.by, b.until ? t.until(b.until) : t.permanent].filter(Boolean).join(" · ") }), p.onRemove && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
252
268
  p.onRemove?.(b);
253
- }, children: p.removeLabel || "Unblock" }))] }, b.id))) }))] }));
269
+ }, children: p.removeLabel || t.unblock }))] }, b.id))) }))] }));
254
270
  }
255
271
  /* ── Reviews and coupons ── */
256
272
  /* ── Reviews and coupons ── */
257
273
  export function ReviewCard(p) {
274
+ const t = useStrings(ADMIN_STRINGS);
258
275
  const reply = React.useState(false), txt = React.useState(p.reply || "");
259
276
  const st = {
260
- pending: ["signal", "Waiting"],
261
- published: ["ok", "Published"],
262
- hidden: ["idle", "Hidden"],
263
- flagged: ["warn", "Flagged"],
277
+ pending: ["signal", t.reviewState.pending],
278
+ published: ["ok", t.reviewState.published],
279
+ hidden: ["idle", t.reviewState.hidden],
280
+ flagged: ["warn", t.reviewState.flagged],
264
281
  }[p.state || "pending"];
265
282
  return (_jsxs("article", { className: cx("tf-review", "is-" + (p.state || "pending")), children: [_jsxs("div", { className: "tf-review-head", children: [_jsx(Avatar, { name: p.author, size: 36 }), _jsxs("div", { className: "tf-option-text", style: {
266
283
  flex: 1,
267
- }, children: [_jsxs("b", { children: [p.author, p.verified && (_jsxs("span", { className: "tf-review-ver", children: [" · ", "Verified purchase"] }))] }), _jsx("small", { children: [p.product, p.date].filter(Boolean).join(" · ") })] }), _jsx(Rating, { value: p.rating }), _jsx(StatusIndicator, { status: st[0], label: p.stateLabel || st[1], size: "sm" })] }), p.title && _jsx("b", { className: "tf-review-title", children: p.title }), _jsx("p", { className: "tf-review-text", children: p.highlight ? markWords(p.text, p.highlight) : p.text }), p.reply && !reply[0] && (_jsxs("div", { className: "tf-review-reply", children: [_jsx("small", { children: p.replyLabel || "Your reply" }), p.reply] })), reply[0] && (_jsxs("div", { className: "tf-col", style: {
284
+ }, children: [_jsxs("b", { children: [p.author, p.verified && (_jsxs("span", { className: "tf-review-ver", children: [" · ", t.verifiedPurchase] }))] }), _jsx("small", { children: [p.product, p.date].filter(Boolean).join(" · ") })] }), _jsx(Rating, { value: p.rating }), _jsx(StatusIndicator, { status: st[0], label: p.stateLabel || st[1], size: "sm" })] }), p.title && _jsx("b", { className: "tf-review-title", children: p.title }), _jsx("p", { className: "tf-review-text", children: p.highlight ? markWords(p.text, p.highlight) : p.text }), p.reply && !reply[0] && (_jsxs("div", { className: "tf-review-reply", children: [_jsx("small", { children: p.replyLabel || t.yourReply }), p.reply] })), reply[0] && (_jsxs("div", { className: "tf-col", style: {
268
285
  gap: 8,
269
286
  }, children: [_jsx("textarea", { className: "tf-input", rows: 3, value: txt[0], onChange: (e) => {
270
287
  txt[1](e.target.value);
271
- }, placeholder: "Write a public reply", "aria-label": "Reply" }), _jsxs("div", { className: "tf-row", style: {
288
+ }, placeholder: t.writeReply, "aria-label": t.reply }), _jsxs("div", { className: "tf-row", style: {
272
289
  justifyContent: "flex-end",
273
290
  }, children: [_jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
274
291
  reply[1](false);
275
- }, children: "Cancel" }), _jsx(Button, { size: "sm", icon: "send", onClick: () => {
292
+ }, children: t.cancel }), _jsx(Button, { size: "sm", icon: "send", onClick: () => {
276
293
  p.onReply && p.onReply(txt[0]);
277
294
  reply[1](false);
278
- }, children: "Post reply" })] })] })), !reply[0] && (_jsxs("div", { className: "tf-review-actions", children: [p.state !== "published" && p.onPublish && (_jsx(Button, { size: "sm", icon: "check", onClick: p.onPublish, children: "Publish" })), p.state !== "hidden" && p.onHide && (_jsx(Button, { size: "sm", variant: "secondary", icon: "eye", onClick: p.onHide, children: "Hide" })), p.onReply && (_jsx(Button, { size: "sm", variant: "ghost", icon: "reply", onClick: () => {
295
+ }, children: t.postReply })] })] })), !reply[0] && (_jsxs("div", { className: "tf-review-actions", children: [p.state !== "published" && p.onPublish && (_jsx(Button, { size: "sm", icon: "check", onClick: p.onPublish, children: t.publish })), p.state !== "hidden" && p.onHide && (_jsx(Button, { size: "sm", variant: "secondary", icon: "eye", onClick: p.onHide, children: t.hide })), p.onReply && (_jsx(Button, { size: "sm", variant: "ghost", icon: "reply", onClick: () => {
279
296
  reply[1](true);
280
- }, children: p.reply ? "Edit reply" : "Reply" }))] }))] }));
297
+ }, children: p.reply ? t.editReply : t.reply }))] }))] }));
281
298
  }
282
299
  function markWords(text, words) {
283
300
  const re = new RegExp("(" + words.map((w) => w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|") + ")", "gi");
@@ -286,6 +303,8 @@ function markWords(text, words) {
286
303
  .map((part, i) => (i % 2 ? _jsx("mark", { children: part }, i) : part));
287
304
  }
288
305
  export function CouponCard(p) {
306
+ const t = useStrings(ADMIN_STRINGS);
307
+ const { formatMoney, formatNumber } = useFormat();
289
308
  const used = p.used || 0, st = p.status || (p.limit && used >= p.limit ? "idle" : "ok");
290
309
  return (_jsxs("div", { className: cx("tf-coupon", st !== "ok" && "off"), children: [_jsxs("div", { className: "tf-coupon-val", children: [_jsx("b", { children: p.kind === "fixed"
291
310
  ? formatMoney(p.value, p.currency)
@@ -293,7 +312,7 @@ export function CouponCard(p) {
293
312
  style: "percent",
294
313
  }) }), _jsx("small", { children: p.kind === "fixed" ? "off" : "off" })] }), _jsxs("div", { className: "tf-coupon-main", children: [_jsxs("div", { className: "tf-row", style: {
295
314
  gap: 8,
296
- }, children: [_jsxs("button", { type: "button", className: "tf-coupon-code", title: "Copy", onClick: () => {
315
+ }, children: [_jsxs("button", { type: "button", className: "tf-coupon-code", title: t.copy, onClick: () => {
297
316
  try {
298
317
  navigator.clipboard.writeText(p.code);
299
318
  }
@@ -302,21 +321,22 @@ export function CouponCard(p) {
302
321
  }
303
322
  toast &&
304
323
  toast({
305
- text: "Copied",
324
+ text: t.copied,
306
325
  });
307
326
  p.onCopy && p.onCopy();
308
327
  }, children: [p.code, _jsx(Icon, { name: "copy", size: 13 })] }), _jsx(StatusIndicator, { status: st, label: p.statusLabel, size: "sm" })] }), p.text && _jsx("small", { className: "tf-coupon-text", children: p.text }), _jsx(Meter, { value: used, max: p.limit || Math.max(used, 1), text: p.limit
309
- ? `${formatNumber(used)} of ${formatNumber(p.limit)} used`
310
- : `${formatNumber(used)} used`, tone: "sky", warn: 101, danger: 101 }), _jsx("span", { className: "tf-coupon-foot", children: [
311
- p.until && `Valid until ${p.until}`,
312
- p.min != null && `from ${formatMoney(p.min, p.currency)}`,
313
- p.revenue != null && `${formatMoney(p.revenue, p.currency)} revenue`,
328
+ ? t.usedOf(formatNumber(used), formatNumber(p.limit))
329
+ : t.used(formatNumber(used)), tone: "sky", warn: 101, danger: 101 }), _jsx("span", { className: "tf-coupon-foot", children: [
330
+ p.until && t.validUntil(p.until),
331
+ p.min != null && t.fromAmount(formatMoney(p.min, p.currency)),
332
+ p.revenue != null && t.revenue(formatMoney(p.revenue, p.currency)),
314
333
  ]
315
334
  .filter(Boolean)
316
- .join(" · ") })] }), p.menu && (_jsx(Popover, { width: 200, trigger: (s) => _jsx(IconButton, { icon: "more", size: "sm", label: "More", onClick: s.toggle }), children: () => _jsx(Menu, { items: p.menu || [] }) }))] }));
335
+ .join(" · ") })] }), p.menu && (_jsx(Popover, { width: 200, trigger: (s) => _jsx(IconButton, { icon: "more", size: "sm", label: t.more, onClick: s.toggle }), children: () => _jsx(Menu, { items: p.menu || [] }) }))] }));
317
336
  }
318
337
  /* ── Locale, workspace and translations ── */
319
338
  export function LocaleField(p) {
339
+ const t = useStrings(ADMIN_STRINGS);
320
340
  const locs = p.locales || [], v = useControlled(p.value, p.defaultValue || {}, p.onChange), cur = React.useState(p.active || (locs[0] && locs[0].code)), id = useId("lf");
321
341
  const base = p.source || (locs[0] && locs[0].code), val = v[0][cur[0]] || "";
322
342
  function set(t) {
@@ -325,12 +345,12 @@ export function LocaleField(p) {
325
345
  v[1](n);
326
346
  }
327
347
  const done = locs.filter((l) => (v[0][l.code] || "").trim()).length;
328
- return (_jsx(FormField, { id: id, label: p.label, hint: p.hint, error: p.error, children: _jsxs("div", { className: "tf-locfield", children: [_jsxs("div", { className: "tf-locfield-tabs", role: "tablist", "aria-label": "Languages", children: [locs.map((l) => {
348
+ return (_jsx(FormField, { id: id, label: p.label, hint: p.hint, error: p.error, children: _jsxs("div", { className: "tf-locfield", children: [_jsxs("div", { className: "tf-locfield-tabs", role: "tablist", "aria-label": t.languages, children: [locs.map((l) => {
329
349
  const has = (v[0][l.code] || "").trim();
330
350
  return (_jsxs("button", { type: "button", role: "tab", "aria-selected": cur[0] === l.code, className: cx("tf-locfield-tab", cur[0] === l.code && "on", !has && "missing"), onClick: () => {
331
351
  cur[1](l.code);
332
- }, children: [l.flag && _jsx("span", { "aria-hidden": true, children: l.flag }), l.label || l.code.toUpperCase(), !has && _jsx("span", { className: "tf-locfield-miss", title: "Missing" })] }, l.code));
333
- }), _jsx("span", { className: "tf-locfield-done", children: `${done} of ${locs.length}` })] }), p.multiline ? (_jsx("textarea", { id: id, className: "tf-input", rows: p.rows || 3, value: val, onChange: (e) => {
352
+ }, children: [l.flag && _jsx("span", { "aria-hidden": true, children: l.flag }), l.label || l.code.toUpperCase(), !has && _jsx("span", { className: "tf-locfield-miss", title: t.missing })] }, l.code));
353
+ }), _jsx("span", { className: "tf-locfield-done", children: t.doneOf(done, locs.length) })] }), p.multiline ? (_jsx("textarea", { id: id, className: "tf-input", rows: p.rows || 3, value: val, onChange: (e) => {
334
354
  set(e.target.value);
335
355
  }, placeholder: cur[0] !== base ? v[0][base] || "" : p.placeholder })) : (_jsx("input", { id: id, className: "tf-input", value: val, onChange: (e) => {
336
356
  set(e.target.value);
@@ -338,23 +358,24 @@ export function LocaleField(p) {
338
358
  gap: 8,
339
359
  }, children: [_jsx(Button, { size: "sm", variant: "ghost", icon: "copy", onClick: () => {
340
360
  set(v[0][base] || "");
341
- }, children: `Copy from ${(locs.find((l) => l.code === base) || {}).label || base}` }), p.onTranslate && (_jsx(Button, { size: "sm", variant: "ghost", icon: "sparkle", onClick: () => {
361
+ }, children: t.copyFrom((locs.find((l) => l.code === base) || {}).label || base) }), p.onTranslate && (_jsx(Button, { size: "sm", variant: "ghost", icon: "sparkle", onClick: () => {
342
362
  Promise.resolve(p.onTranslate?.(v[0][base] || "", cur[0])).then((t) => {
343
363
  if (t)
344
364
  set(t);
345
365
  });
346
- }, children: "Translate" }))] }))] }) }));
366
+ }, children: t.translate }))] }))] }) }));
347
367
  }
348
368
  export function LocaleSwitcher(p) {
369
+ const t = useStrings(ADMIN_STRINGS);
349
370
  const lang = useControlled(p.language, p.languages && p.languages[0] && p.languages[0].code, p.onLanguage), cur = useControlled(p.currency, p.currencies && p.currencies[0], p.onCurrency);
350
371
  const L0 = (p.languages || []).find((l) => l.code === lang[0]) || {};
351
- return (_jsx(Popover, { width: 280, trigger: (s) => (_jsxs("button", { type: "button", className: cx("tf-locsw", p.variant === "frame" && "on-frame"), "aria-haspopup": "dialog", "aria-expanded": s.open, onClick: s.toggle, title: "Language and currency", children: [L0.flag ? _jsx("span", { children: L0.flag }) : _jsx(Icon, { name: "globe", size: 15 }), _jsx("span", { children: (L0.short || lang[0] || "").toUpperCase() }), cur[0] && _jsx("span", { className: "tf-locsw-cur", children: cur[0] }), _jsx(Icon, { name: "chevronDown", size: 13 })] })), children: () => (_jsxs("div", { className: "tf-col", style: {
372
+ return (_jsx(Popover, { width: 280, trigger: (s) => (_jsxs("button", { type: "button", className: cx("tf-locsw", p.variant === "frame" && "on-frame"), "aria-haspopup": "dialog", "aria-expanded": s.open, onClick: s.toggle, title: t.languageAndCurrency, children: [L0.flag ? _jsx("span", { children: L0.flag }) : _jsx(Icon, { name: "globe", size: 15 }), _jsx("span", { children: (L0.short || lang[0] || "").toUpperCase() }), cur[0] && _jsx("span", { className: "tf-locsw-cur", children: cur[0] }), _jsx(Icon, { name: "chevronDown", size: 13 })] })), children: () => (_jsxs("div", { className: "tf-col", style: {
352
373
  gap: 12,
353
374
  }, children: [p.languages && (_jsxs("div", { className: "tf-col", style: {
354
375
  gap: 4,
355
376
  }, children: [_jsx("span", { className: "tf-menu-label", style: {
356
377
  padding: "0 8px",
357
- }, children: "Language" }), _jsx("div", { className: "tf-menu", style: {
378
+ }, children: t.language }), _jsx("div", { className: "tf-menu", style: {
358
379
  padding: 0,
359
380
  boxShadow: "none",
360
381
  }, children: p.languages.map((l) => (_jsxs("button", { type: "button", className: cx("tf-menu-item", l.code === lang[0] && "on"), onClick: () => {
@@ -365,7 +386,7 @@ export function LocaleSwitcher(p) {
365
386
  gap: 6,
366
387
  }, children: [_jsx("span", { className: "tf-menu-label", style: {
367
388
  padding: "0 8px",
368
- }, children: "Currency" }), _jsx(Segmented, { size: "sm", block: true, value: cur[0], onChange: cur[1], options: p.currencies })] }))] })) }));
389
+ }, children: t.currency }), _jsx(Segmented, { size: "sm", block: true, value: cur[0], onChange: cur[1], options: p.currencies })] }))] })) }));
369
390
  }
370
391
  export function WorkspaceSwitcher(p) {
371
392
  const items = p.items || [], v = useControlled(p.value, items[0] && items[0].id, p.onChange), cur = items.find((x) => x.id === v[0]) || {};
@@ -386,6 +407,8 @@ export function WorkspaceSwitcher(p) {
386
407
  /* ── Export and 2FA ── */
387
408
  /* ── Export and 2FA ── */
388
409
  export function ExportDialog(p) {
410
+ const t = useStrings(ADMIN_STRINGS);
411
+ const { formatNumber, formatPercent } = useFormat();
389
412
  const fmt = React.useState(p.format || (p.formats || ["csv"])[0]), cols = React.useState((p.columns || []).filter((c) => c.default !== false).map((c) => c.key)), range = React.useState(p.range || null), st = React.useState("form"), prog = React.useState(0);
390
413
  if (!p.open && !p.inline)
391
414
  return null;
@@ -404,12 +427,12 @@ export function ExportDialog(p) {
404
427
  st[1]("done");
405
428
  });
406
429
  }
407
- const body = (_jsxs("div", { className: "tf-modal tf-export", role: "dialog", "aria-modal": p.inline ? undefined : "true", "aria-label": p.title || "Export", children: [_jsx("span", { className: "tf-modal-close", children: _jsx(IconButton, { icon: "x", label: "Close", size: "sm", onClick: p.onClose }) }), _jsxs("div", { children: [_jsx("h2", { className: "tf-modal-title", children: p.title || "Export" }), _jsx("p", { className: "tf-modal-text", style: {
430
+ const body = (_jsxs("div", { className: "tf-modal tf-export", role: "dialog", "aria-modal": p.inline ? undefined : "true", "aria-label": p.title || t.export, children: [_jsx("span", { className: "tf-modal-close", children: _jsx(IconButton, { icon: "x", label: t.close, size: "sm", onClick: p.onClose }) }), _jsxs("div", { children: [_jsx("h2", { className: "tf-modal-title", children: p.title || t.export }), _jsx("p", { className: "tf-modal-text", style: {
408
431
  marginTop: 6,
409
- }, children: p.text || `${formatNumber(p.count || 0)} rows match your filters.` })] }), st[0] === "form" ? (_jsxs(React.Fragment, { children: [_jsx(FormField, { label: "Format", children: _jsx(Segmented, { block: true, value: fmt[0], onChange: fmt[1], options: (p.formats || ["csv", "xlsx", "json"]).map((f) => ({
432
+ }, children: p.text || t.rowsMatch(formatNumber(p.count || 0), p.count || 0) })] }), st[0] === "form" ? (_jsxs(React.Fragment, { children: [_jsx(FormField, { label: t.format, children: _jsx(Segmented, { block: true, value: fmt[0], onChange: fmt[1], options: (p.formats || ["csv", "xlsx", "json"]).map((f) => ({
410
433
  value: f,
411
434
  label: f.toUpperCase(),
412
- })) }) }), p.ranges !== false && (_jsx(DatePicker, { label: "Period", range: true, value: range[0], onChange: (r) => range[1](r), placeholder: "All time" })), (p.columns || []).length > 0 && (_jsx(FormField, { label: "Columns", hint: `${cols[0].length} selected`, children: _jsx("div", { className: "tf-export-cols", children: (p.columns || []).map((c) => {
435
+ })) }) }), p.ranges !== false && (_jsx(DatePicker, { label: t.period, range: true, value: range[0], onChange: (r) => range[1](r), placeholder: t.allTime })), (p.columns || []).length > 0 && (_jsx(FormField, { label: t.columns, hint: t.selectedCount(cols[0].length), children: _jsx("div", { className: "tf-export-cols", children: (p.columns || []).map((c) => {
413
436
  const on = cols[0].indexOf(c.key) > -1;
414
437
  return (_jsxs("label", { className: "tf-check", children: [cbox({
415
438
  checked: on,
@@ -419,66 +442,67 @@ export function ExportDialog(p) {
419
442
  }), c.label] }, c.key));
420
443
  }) }) })), p.note && (_jsx("p", { className: "tf-card-desc", style: {
421
444
  margin: 0,
422
- }, children: p.note })), _jsxs("div", { className: "tf-modal-foot", children: [_jsx(Button, { variant: "ghost", onClick: p.onClose, children: "Cancel" }), _jsx(Button, { icon: "download", disabled: !cols[0].length && (p.columns || []).length > 0, onClick: start, children: "Export" })] })] })) : st[0] === "running" ? (_jsxs("div", { className: "tf-col", style: {
445
+ }, children: p.note })), _jsxs("div", { className: "tf-modal-foot", children: [_jsx(Button, { variant: "ghost", onClick: p.onClose, children: t.cancel }), _jsx(Button, { icon: "download", disabled: !cols[0].length && (p.columns || []).length > 0, onClick: start, children: t.export })] })] })) : st[0] === "running" ? (_jsxs("div", { className: "tf-col", style: {
423
446
  gap: 8,
424
- }, children: [_jsx(Progress, { value: prog[0], label: "Export progress" }), _jsx("span", { className: "tf-card-desc", children: "Preparing the file…" + " " + Math.round(prog[0]) + "%" })] })) : (_jsxs(React.Fragment, { children: [_jsx(StatusIndicator, { status: "ok", label: p.doneText || "Your file is ready." }), _jsxs("div", { className: "tf-modal-foot", children: [_jsx(Button, { variant: "ghost", onClick: () => {
447
+ }, children: [_jsx(Progress, { value: prog[0], label: t.exportProgress }), _jsx("span", { className: "tf-card-desc", children: t.preparingFile + " " + formatPercent(Math.round(prog[0])) })] })) : (_jsxs(React.Fragment, { children: [_jsx(StatusIndicator, { status: "ok", label: p.doneText || t.fileReady }), _jsxs("div", { className: "tf-modal-foot", children: [_jsx(Button, { variant: "ghost", onClick: () => {
425
448
  st[1]("form");
426
- }, children: "New export" }), p.download || (_jsx(Button, { icon: "download", onClick: p.onDownload, children: "Download" }))] })] }))] }));
449
+ }, children: t.newExport }), p.download || (_jsx(Button, { icon: "download", onClick: p.onDownload, children: t.download }))] })] }))] }));
427
450
  return p.inline ? (body) : (_jsx(Portal, { children: _jsxs("div", { className: "tf-overlay", children: [_jsx("div", { className: "tf-backdrop", onClick: p.onClose }), body] }) }));
428
451
  }
429
452
  export function TwoFactorSetup(p) {
453
+ const t = useStrings(ADMIN_STRINGS);
430
454
  const step = React.useState(p.step || 0), err = React.useState(null), saved = React.useState(false);
431
- const steps = ["Scan", "Confirm", "Save codes"];
432
- return (_jsxs("div", { className: "tf-2fa", children: [_jsx("ol", { className: "tf-2fa-steps", children: steps.map((s, i) => (_jsxs("li", { className: cx(i === step[0] && "on", i < step[0] && "done"), children: [_jsx("span", { children: i < step[0] ? _jsx(Icon, { name: "check", size: 12 }) : i + 1 }), s] }, i))) }), step[0] === 0 && (_jsxs("div", { className: "tf-2fa-scan", children: [_jsx("div", { className: "tf-2fa-qr", role: "img", "aria-label": "QR code", children: p.qr || _jsx(Icon, { name: "grid", size: 48 }) }), _jsxs("div", { className: "tf-col", style: {
455
+ const steps = t.twoFactorSteps;
456
+ return (_jsxs("div", { className: "tf-2fa", children: [_jsx("ol", { className: "tf-2fa-steps", children: steps.map((s, i) => (_jsxs("li", { className: cx(i === step[0] && "on", i < step[0] && "done"), children: [_jsx("span", { children: i < step[0] ? _jsx(Icon, { name: "check", size: 12 }) : i + 1 }), s] }, i))) }), step[0] === 0 && (_jsxs("div", { className: "tf-2fa-scan", children: [_jsx("div", { className: "tf-2fa-qr", role: "img", "aria-label": t.qrCode, children: p.qr || _jsx(Icon, { name: "grid", size: 48 }) }), _jsxs("div", { className: "tf-col", style: {
433
457
  gap: 12,
434
458
  minWidth: 0,
435
459
  }, children: [_jsx("p", { className: "tf-card-desc", style: {
436
460
  margin: 0,
437
- }, children: p.scanText ||
438
- "Scan the code with your authenticator app. If you can't scan it, enter the key by hand." }), _jsxs("button", { type: "button", className: "tf-2fa-key", onClick: () => {
461
+ }, children: p.scanText || t.scanText }), _jsxs("button", { type: "button", className: "tf-2fa-key", onClick: () => {
439
462
  try {
440
463
  navigator.clipboard.writeText(p.secret);
441
464
  }
442
465
  catch {
443
466
  /* clipboard unavailable */
444
467
  }
445
- toast && toast({ text: "Copied" });
468
+ toast && toast({ text: t.copied });
446
469
  }, children: [_jsx("span", { className: "tf-mono", children: (p.secret || "").replace(/(.{4})/g, "$1 ").trim() }), _jsx(Icon, { name: "copy", size: 14 })] }), _jsx("div", { children: _jsx(Button, { onClick: () => {
447
470
  step[1](1);
448
- }, children: "Next" }) })] })] })), step[0] === 1 && (_jsxs("div", { className: "tf-col", style: {
471
+ }, children: t.next }) })] })] })), step[0] === 1 && (_jsxs("div", { className: "tf-col", style: {
449
472
  gap: 12,
450
473
  }, children: [_jsx("p", { className: "tf-card-desc", style: {
451
474
  margin: 0,
452
- }, children: "Enter the 6-digit code from the app." }), _jsx(OtpInput, { error: !!err[0], onComplete: (c) => {
475
+ }, children: t.enterCode }), _jsx(OtpInput, { error: !!err[0], onComplete: (c) => {
453
476
  Promise.resolve(p.onVerify ? p.onVerify(c) : true).then((ok) => {
454
477
  if (ok) {
455
478
  err[1](null);
456
479
  step[1](2);
457
480
  }
458
481
  else
459
- err[1]("That code didn't work. Try the current one.");
482
+ err[1](t.codeFailed);
460
483
  });
461
484
  } }), err[0] && _jsx("span", { className: "tf-field-error", children: err[0] }), _jsx("div", { children: _jsx(Button, { variant: "ghost", onClick: () => {
462
485
  step[1](0);
463
- }, children: "Back" }) })] })), step[0] === 2 && (_jsxs("div", { className: "tf-col", style: {
486
+ }, children: t.back }) })] })), step[0] === 2 && (_jsxs("div", { className: "tf-col", style: {
464
487
  gap: 12,
465
488
  }, children: [_jsx("p", { className: "tf-card-desc", style: {
466
489
  margin: 0,
467
- }, children: p.codesText ||
468
- "Each code works once if you lose your phone. Keep them somewhere safe." }), _jsx("div", { className: "tf-2fa-codes", children: (p.codes || []).map((c) => (_jsx("code", { children: c }, c))) }), _jsxs("div", { className: "tf-row", children: [_jsx(Button, { variant: "secondary", icon: "copy", onClick: () => {
490
+ }, children: p.codesText || t.codesText }), _jsx("div", { className: "tf-2fa-codes", children: (p.codes || []).map((c) => (_jsx("code", { children: c }, c))) }), _jsxs("div", { className: "tf-row", children: [_jsx(Button, { variant: "secondary", icon: "copy", onClick: () => {
469
491
  try {
470
492
  navigator.clipboard.writeText((p.codes || []).join("\n"));
471
493
  }
472
494
  catch {
473
495
  /* clipboard unavailable */
474
496
  }
475
- toast && toast({ text: "Copied" });
476
- }, children: "Copy" }), p.onDownload && (_jsx(Button, { variant: "secondary", icon: "download", onClick: p.onDownload, children: "Download" }))] }), _jsx(Checkbox, { checked: saved[0], onChange: () => {
497
+ toast && toast({ text: t.copied });
498
+ }, children: t.copy }), p.onDownload && (_jsx(Button, { variant: "secondary", icon: "download", onClick: p.onDownload, children: t.download }))] }), _jsx(Checkbox, { checked: saved[0], onChange: () => {
477
499
  saved[1](!saved[0]);
478
- }, children: "I saved the recovery codes" }), _jsx("div", { children: _jsx(Button, { icon: "check", disabled: !saved[0], onClick: p.onDone, children: "Turn on two-factor" }) })] }))] }));
500
+ }, children: t.savedCodes }), _jsx("div", { children: _jsx(Button, { icon: "check", disabled: !saved[0], onClick: p.onDone, children: t.turnOnTwoFactor }) })] }))] }));
479
501
  }
480
502
  /* ── Media ── */
481
503
  export function ColorField(p) {
504
+ const t = useStrings(ADMIN_STRINGS);
505
+ const { formatNumber } = useFormat();
482
506
  const id = useId("cf"), v = useControlled(p.value, p.defaultValue || "#000000", p.onChange);
483
507
  function lum(hx) {
484
508
  const m = /^#?([0-9a-f]{6})$/i.exec(hx || "");
@@ -495,12 +519,12 @@ export function ColorField(p) {
495
519
  background: v[0],
496
520
  }, children: _jsx("input", { type: "color", value: /^#[0-9a-f]{6}$/i.test(v[0]) ? v[0] : "#000000", onChange: (e) => {
497
521
  v[1](e.target.value);
498
- }, "aria-label": "Pick a colour" }) }), _jsx("input", { id: id, "aria-label": p.label ? undefined : "Pick a colour", className: "tf-input tf-mono", value: v[0], onChange: (e) => {
522
+ }, "aria-label": t.pickColour }) }), _jsx("input", { id: id, "aria-label": p.label ? undefined : t.pickColour, className: "tf-input tf-mono", value: v[0], onChange: (e) => {
499
523
  v[1](e.target.value);
500
- }, spellCheck: false }), ratio != null && p.against !== false && (_jsx("span", { className: cx("tf-colorf-ratio", ratio >= 4.5 ? "ok" : ratio >= 3 ? "warn" : "bad"), title: `Contrast against ${p.against || "#ffffff"}`, children: formatNumber(ratio, {
524
+ }, spellCheck: false }), ratio != null && p.against !== false && (_jsx("span", { className: cx("tf-colorf-ratio", ratio >= 4.5 ? "ok" : ratio >= 3 ? "warn" : "bad"), title: t.contrastAgainst(p.against || "#ffffff"), children: formatNumber(ratio, {
501
525
  maximumFractionDigits: 1,
502
526
  }) +
503
527
  ":1 " +
504
- (ratio >= 4.5 ? "AA" : ratio >= 3 ? "AA large" : "Fails") }))] }) }));
528
+ (ratio >= 4.5 ? t.contrastAA : ratio >= 3 ? t.contrastAALarge : t.contrastFails) }))] }) }));
505
529
  }
506
530
  //# sourceMappingURL=admin.js.map