@twentyfirstmedia/ui-core 0.2.1 → 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.
- package/dist/components/admin.d.ts.map +1 -1
- package/dist/components/admin.js +95 -71
- package/dist/components/admin.js.map +1 -1
- package/dist/components/agents.d.ts.map +1 -1
- package/dist/components/agents.js +49 -35
- package/dist/components/agents.js.map +1 -1
- package/dist/components/app-chrome.d.ts.map +1 -1
- package/dist/components/app-chrome.js +6 -2
- package/dist/components/app-chrome.js.map +1 -1
- package/dist/components/approvals.d.ts.map +1 -1
- package/dist/components/approvals.js +22 -18
- package/dist/components/approvals.js.map +1 -1
- package/dist/components/building-blocks.d.ts.map +1 -1
- package/dist/components/building-blocks.js +6 -2
- package/dist/components/building-blocks.js.map +1 -1
- package/dist/components/charts.d.ts.map +1 -1
- package/dist/components/charts.js +20 -10
- package/dist/components/charts.js.map +1 -1
- package/dist/components/chat.d.ts.map +1 -1
- package/dist/components/chat.js +31 -19
- package/dist/components/chat.js.map +1 -1
- package/dist/components/commerce.d.ts +2 -2
- package/dist/components/commerce.d.ts.map +1 -1
- package/dist/components/commerce.js +45 -31
- package/dist/components/commerce.js.map +1 -1
- package/dist/components/data-display.d.ts +5 -9
- package/dist/components/data-display.d.ts.map +1 -1
- package/dist/components/data-display.js +34 -35
- package/dist/components/data-display.js.map +1 -1
- package/dist/components/data-table.d.ts.map +1 -1
- package/dist/components/data-table.js +15 -10
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/developer.d.ts.map +1 -1
- package/dist/components/developer.js +20 -11
- package/dist/components/developer.js.map +1 -1
- package/dist/components/feedback.d.ts.map +1 -1
- package/dist/components/feedback.js +12 -6
- package/dist/components/feedback.js.map +1 -1
- package/dist/components/form.d.ts +2 -1
- package/dist/components/form.d.ts.map +1 -1
- package/dist/components/form.js +12 -9
- package/dist/components/form.js.map +1 -1
- package/dist/components/forms.d.ts +2 -1
- package/dist/components/forms.d.ts.map +1 -1
- package/dist/components/forms.js +92 -42
- package/dist/components/forms.js.map +1 -1
- package/dist/components/foundation.d.ts +5 -2
- package/dist/components/foundation.d.ts.map +1 -1
- package/dist/components/foundation.js +15 -12
- package/dist/components/foundation.js.map +1 -1
- package/dist/components/inputs.d.ts.map +1 -1
- package/dist/components/inputs.js +3 -1
- package/dist/components/inputs.js.map +1 -1
- package/dist/components/mail.d.ts.map +1 -1
- package/dist/components/mail.js +15 -10
- package/dist/components/mail.js.map +1 -1
- package/dist/components/media.d.ts.map +1 -1
- package/dist/components/media.js +39 -18
- package/dist/components/media.js.map +1 -1
- package/dist/components/navigation.d.ts.map +1 -1
- package/dist/components/navigation.js +21 -9
- package/dist/components/navigation.js.map +1 -1
- package/dist/components/operations.d.ts.map +1 -1
- package/dist/components/operations.js +37 -27
- package/dist/components/operations.js.map +1 -1
- package/dist/components/previews.d.ts.map +1 -1
- package/dist/components/previews.js +14 -10
- package/dist/components/previews.js.map +1 -1
- package/dist/components/primitives.d.ts.map +1 -1
- package/dist/components/primitives.js +4 -1
- package/dist/components/primitives.js.map +1 -1
- package/dist/components/real-estate.d.ts.map +1 -1
- package/dist/components/real-estate.js +43 -28
- package/dist/components/real-estate.js.map +1 -1
- package/dist/components/rich-text-editor.d.ts.map +1 -1
- package/dist/components/rich-text-editor.js +18 -15
- package/dist/components/rich-text-editor.js.map +1 -1
- package/dist/components/select-menu.d.ts.map +1 -1
- package/dist/components/select-menu.js +4 -1
- package/dist/components/select-menu.js.map +1 -1
- package/dist/components/shell.d.ts.map +1 -1
- package/dist/components/shell.js +8 -3
- package/dist/components/shell.js.map +1 -1
- package/dist/components/site.d.ts.map +1 -1
- package/dist/components/site.js +17 -13
- package/dist/components/site.js.map +1 -1
- package/dist/components/types.d.ts +102 -3
- package/dist/components/types.d.ts.map +1 -1
- package/dist/components/workspace.d.ts.map +1 -1
- package/dist/components/workspace.js +50 -34
- package/dist/components/workspace.js.map +1 -1
- package/dist/context.d.ts +39 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +76 -0
- package/dist/context.js.map +1 -0
- package/dist/format.d.ts +33 -6
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +75 -10
- package/dist/format.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +18 -11
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +11 -18
- package/dist/provider.js.map +1 -1
- package/dist/strings.d.ts +503 -0
- package/dist/strings.d.ts.map +1 -0
- package/dist/strings.js +537 -0
- package/dist/strings.js.map +1 -0
- package/package.json +1 -1
- package/src/components/admin.tsx +117 -93
- package/src/components/agents.tsx +55 -41
- package/src/components/app-chrome.tsx +6 -2
- package/src/components/approvals.tsx +26 -22
- package/src/components/building-blocks.tsx +6 -2
- package/src/components/charts.tsx +21 -11
- package/src/components/chat.tsx +45 -35
- package/src/components/commerce.tsx +61 -53
- package/src/components/data-display.tsx +38 -45
- package/src/components/data-table.tsx +20 -15
- package/src/components/developer.tsx +26 -15
- package/src/components/feedback.tsx +19 -7
- package/src/components/form.tsx +17 -9
- package/src/components/forms.tsx +131 -53
- package/src/components/foundation.tsx +23 -36
- package/src/components/inputs.tsx +4 -2
- package/src/components/mail.tsx +16 -11
- package/src/components/media.tsx +64 -21
- package/src/components/navigation.tsx +25 -13
- package/src/components/operations.tsx +40 -32
- package/src/components/previews.tsx +17 -15
- package/src/components/primitives.tsx +8 -5
- package/src/components/real-estate.tsx +55 -35
- package/src/components/rich-text-editor.tsx +20 -17
- package/src/components/select-menu.tsx +5 -2
- package/src/components/shell.tsx +8 -3
- package/src/components/site.tsx +18 -14
- package/src/components/types.ts +111 -3
- package/src/components/workspace.tsx +79 -60
- package/src/context.ts +112 -0
- package/src/format.ts +108 -15
- package/src/index.ts +43 -0
- package/src/provider.tsx +36 -40
- 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;
|
|
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"}
|
package/dist/components/admin.js
CHANGED
|
@@ -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 {
|
|
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":
|
|
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":
|
|
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:
|
|
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:
|
|
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] ?
|
|
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:
|
|
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:
|
|
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 ||
|
|
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 ||
|
|
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" ?
|
|
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 &&
|
|
189
|
-
days != null && p.balance > 0 &&
|
|
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:
|
|
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 ||
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 ||
|
|
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 ||
|
|
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 ?
|
|
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 ||
|
|
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",
|
|
261
|
-
published: ["ok",
|
|
262
|
-
hidden: ["idle",
|
|
263
|
-
flagged: ["warn",
|
|
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: [" · ",
|
|
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:
|
|
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:
|
|
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:
|
|
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 ?
|
|
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:
|
|
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:
|
|
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
|
-
?
|
|
310
|
-
:
|
|
311
|
-
p.until &&
|
|
312
|
-
p.min != null &&
|
|
313
|
-
p.revenue != null &&
|
|
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:
|
|
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":
|
|
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:
|
|
333
|
-
}), _jsx("span", { className: "tf-locfield-done", children:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 ||
|
|
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 ||
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
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":
|
|
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:
|
|
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:
|
|
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:
|
|
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](
|
|
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:
|
|
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:
|
|
476
|
-
}, children:
|
|
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:
|
|
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":
|
|
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:
|
|
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 ?
|
|
528
|
+
(ratio >= 4.5 ? t.contrastAA : ratio >= 3 ? t.contrastAALarge : t.contrastFails) }))] }) }));
|
|
505
529
|
}
|
|
506
530
|
//# sourceMappingURL=admin.js.map
|