@fonderie/react-admin-screens 1.0.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1762 -620
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -3
- package/dist/index.d.ts +15 -3
- package/dist/index.js +1710 -569
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/screens/AdminShell.tsx
|
|
2
2
|
import { ConfigEditorScreen, ConfigListScreen } from "@fonderie/react-config-admin-screens";
|
|
3
3
|
import { TemplateEditorScreen, TemplateListScreen } from "@fonderie/react-courier-admin-screens";
|
|
4
|
-
import { useState as
|
|
4
|
+
import { useEffect, useState as useState7 } from "react";
|
|
5
5
|
|
|
6
6
|
// src/styles.ts
|
|
7
7
|
var t = {
|
|
@@ -23,6 +23,31 @@ var t = {
|
|
|
23
23
|
trackingDisplay: "var(--fonderie-tracking-display,-0.05em)",
|
|
24
24
|
shadowCard: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))"
|
|
25
25
|
};
|
|
26
|
+
var tint = (token, pct) => `color-mix(in srgb, ${token} ${pct}%, transparent)`;
|
|
27
|
+
var toneColor = {
|
|
28
|
+
ok: t.accentStrong,
|
|
29
|
+
bad: t.danger,
|
|
30
|
+
// Amber on white fails contrast; mixing in the text colour darkens it in
|
|
31
|
+
// light mode and lightens it in dark, from the same two tokens.
|
|
32
|
+
warn: `color-mix(in srgb, ${t.warning} 70%, ${t.text})`,
|
|
33
|
+
neutral: t.muted,
|
|
34
|
+
info: t.text
|
|
35
|
+
};
|
|
36
|
+
var control = {
|
|
37
|
+
display: "inline-flex",
|
|
38
|
+
alignItems: "center",
|
|
39
|
+
justifyContent: "center",
|
|
40
|
+
gap: 6,
|
|
41
|
+
height: 32,
|
|
42
|
+
boxSizing: "border-box",
|
|
43
|
+
borderRadius: 6,
|
|
44
|
+
padding: "0 12px",
|
|
45
|
+
fontSize: 13,
|
|
46
|
+
fontWeight: 500,
|
|
47
|
+
fontFamily: "inherit",
|
|
48
|
+
lineHeight: 1,
|
|
49
|
+
whiteSpace: "nowrap"
|
|
50
|
+
};
|
|
26
51
|
var styles = {
|
|
27
52
|
shell: {
|
|
28
53
|
display: "flex",
|
|
@@ -31,155 +56,578 @@ var styles = {
|
|
|
31
56
|
color: t.text,
|
|
32
57
|
background: t.bg
|
|
33
58
|
},
|
|
34
|
-
//
|
|
35
|
-
//
|
|
59
|
+
// Sticky, full height, scrolls on its own: the nav never scrolls away with
|
|
60
|
+
// a long table, and the footer (session controls) stays in reach.
|
|
36
61
|
nav: {
|
|
37
|
-
width:
|
|
38
|
-
borderRight: `1px solid ${t.border}`,
|
|
39
|
-
padding: 16,
|
|
62
|
+
width: 248,
|
|
40
63
|
flexShrink: 0,
|
|
41
|
-
|
|
64
|
+
boxSizing: "border-box",
|
|
65
|
+
borderRight: `1px solid ${t.border}`,
|
|
66
|
+
background: t.surface,
|
|
67
|
+
position: "sticky",
|
|
68
|
+
top: 0,
|
|
69
|
+
height: "100vh",
|
|
70
|
+
display: "flex",
|
|
71
|
+
flexDirection: "column"
|
|
42
72
|
},
|
|
73
|
+
navBrand: {
|
|
74
|
+
display: "flex",
|
|
75
|
+
alignItems: "center",
|
|
76
|
+
gap: 10,
|
|
77
|
+
padding: "18px 16px 14px",
|
|
78
|
+
borderBottom: `1px solid ${t.borderLight}`
|
|
79
|
+
},
|
|
80
|
+
navMark: {
|
|
81
|
+
width: 28,
|
|
82
|
+
height: 28,
|
|
83
|
+
borderRadius: 7,
|
|
84
|
+
background: t.text,
|
|
85
|
+
color: t.surface,
|
|
86
|
+
display: "grid",
|
|
87
|
+
placeItems: "center",
|
|
88
|
+
flexShrink: 0
|
|
89
|
+
},
|
|
90
|
+
navBrandTitle: {
|
|
91
|
+
fontSize: 14,
|
|
92
|
+
fontWeight: 600,
|
|
93
|
+
letterSpacing: t.trackingDisplay,
|
|
94
|
+
lineHeight: 1.2,
|
|
95
|
+
overflow: "hidden",
|
|
96
|
+
textOverflow: "ellipsis",
|
|
97
|
+
whiteSpace: "nowrap"
|
|
98
|
+
},
|
|
99
|
+
navScroll: { flex: 1, overflowY: "auto", padding: "8px 10px 16px" },
|
|
43
100
|
navGroup: {
|
|
44
101
|
fontSize: 11,
|
|
45
102
|
textTransform: "uppercase",
|
|
46
103
|
letterSpacing: 0.6,
|
|
47
104
|
color: t.muted,
|
|
48
|
-
margin: "16px
|
|
105
|
+
margin: "16px 8px 4px",
|
|
49
106
|
fontWeight: 600
|
|
50
107
|
},
|
|
51
108
|
navItem: {
|
|
52
|
-
display: "
|
|
109
|
+
display: "flex",
|
|
110
|
+
alignItems: "center",
|
|
111
|
+
gap: 10,
|
|
53
112
|
width: "100%",
|
|
54
113
|
textAlign: "left",
|
|
55
114
|
background: "none",
|
|
56
115
|
border: "none",
|
|
57
|
-
borderRadius:
|
|
58
|
-
padding: "
|
|
116
|
+
borderRadius: 6,
|
|
117
|
+
padding: "7px 8px",
|
|
59
118
|
cursor: "pointer",
|
|
60
|
-
fontSize:
|
|
61
|
-
|
|
62
|
-
|
|
119
|
+
fontSize: 13.5,
|
|
120
|
+
fontWeight: 500,
|
|
121
|
+
color: t.muted,
|
|
122
|
+
fontFamily: "inherit",
|
|
123
|
+
position: "relative"
|
|
124
|
+
},
|
|
125
|
+
navItemActive: { background: tint(t.accent, 12), color: t.text, fontWeight: 600 },
|
|
126
|
+
navIconActive: { color: t.accentStrong },
|
|
127
|
+
navFooter: {
|
|
128
|
+
borderTop: `1px solid ${t.borderLight}`,
|
|
129
|
+
padding: 12,
|
|
130
|
+
display: "flex",
|
|
131
|
+
flexDirection: "column",
|
|
132
|
+
gap: 10
|
|
133
|
+
},
|
|
134
|
+
// Phone layout: a top bar with the menu button replaces the sidebar, which
|
|
135
|
+
// opens as a drawer over the content.
|
|
136
|
+
topbar: {
|
|
137
|
+
position: "sticky",
|
|
138
|
+
top: 0,
|
|
139
|
+
zIndex: 20,
|
|
140
|
+
display: "flex",
|
|
141
|
+
alignItems: "center",
|
|
142
|
+
gap: 10,
|
|
143
|
+
padding: "10px 16px",
|
|
144
|
+
background: t.surface,
|
|
145
|
+
borderBottom: `1px solid ${t.border}`
|
|
146
|
+
},
|
|
147
|
+
drawer: {
|
|
148
|
+
position: "fixed",
|
|
149
|
+
inset: 0,
|
|
150
|
+
zIndex: 30,
|
|
151
|
+
display: "flex"
|
|
152
|
+
},
|
|
153
|
+
scrim: { flex: 1, background: "rgba(0,0,0,.35)" },
|
|
154
|
+
main: { flex: 1, minWidth: 0 },
|
|
155
|
+
container: { padding: "32px 40px 64px", maxWidth: 1160, boxSizing: "border-box" },
|
|
156
|
+
// ── page header ──
|
|
157
|
+
header: {
|
|
158
|
+
display: "flex",
|
|
159
|
+
alignItems: "flex-start",
|
|
160
|
+
justifyContent: "space-between",
|
|
161
|
+
gap: 16,
|
|
162
|
+
marginBottom: 24,
|
|
163
|
+
flexWrap: "wrap"
|
|
63
164
|
},
|
|
64
|
-
navItemActive: { background: t.surfaceAlt, fontWeight: 600, color: t.accentStrong },
|
|
65
|
-
main: { flex: 1, padding: 24, maxWidth: 960 },
|
|
66
|
-
container: { padding: 24, maxWidth: 960 },
|
|
67
165
|
title: {
|
|
68
|
-
fontSize:
|
|
166
|
+
fontSize: 22,
|
|
69
167
|
fontWeight: 600,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
168
|
+
margin: 0,
|
|
169
|
+
letterSpacing: t.trackingDisplay,
|
|
170
|
+
lineHeight: 1.25
|
|
73
171
|
},
|
|
172
|
+
lead: { color: t.muted, fontSize: 13.5, margin: "4px 0 0", lineHeight: 1.5 },
|
|
173
|
+
actions: { display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" },
|
|
74
174
|
subtitle: {
|
|
75
175
|
fontSize: 15,
|
|
76
176
|
fontWeight: 600,
|
|
77
|
-
marginTop:
|
|
78
|
-
marginBottom:
|
|
79
|
-
letterSpacing: t.trackingDisplay
|
|
177
|
+
marginTop: 32,
|
|
178
|
+
marginBottom: 10,
|
|
179
|
+
letterSpacing: t.trackingDisplay,
|
|
180
|
+
display: "flex",
|
|
181
|
+
alignItems: "center",
|
|
182
|
+
gap: 8
|
|
183
|
+
},
|
|
184
|
+
// ── feedback ──
|
|
185
|
+
status: { padding: "24px 0", color: t.muted, fontSize: 13.5 },
|
|
186
|
+
error: {
|
|
187
|
+
color: t.danger,
|
|
188
|
+
background: tint(t.danger, 8),
|
|
189
|
+
border: `1px solid ${tint(t.danger, 25)}`,
|
|
190
|
+
borderRadius: t.radiusLg,
|
|
191
|
+
padding: "10px 14px",
|
|
192
|
+
marginBottom: 16,
|
|
193
|
+
fontSize: 13.5
|
|
194
|
+
},
|
|
195
|
+
notice: {
|
|
196
|
+
color: t.text,
|
|
197
|
+
background: tint(t.warning, 10),
|
|
198
|
+
border: `1px solid ${tint(t.warning, 35)}`,
|
|
199
|
+
borderRadius: t.radiusLg,
|
|
200
|
+
padding: "10px 14px",
|
|
201
|
+
marginBottom: 16,
|
|
202
|
+
fontSize: 13.5
|
|
80
203
|
},
|
|
81
|
-
status: { padding: 12, color: t.muted },
|
|
82
|
-
error: { color: t.danger, marginBottom: 12, fontSize: 14 },
|
|
83
204
|
ok: { color: t.accentStrong, fontWeight: 600 },
|
|
84
205
|
bad: { color: t.danger, fontWeight: 600 },
|
|
85
206
|
advice: { color: t.warning },
|
|
86
207
|
muted: { color: t.muted, fontSize: 13 },
|
|
87
|
-
mono: { fontFamily: t.mono, fontSize:
|
|
88
|
-
|
|
208
|
+
mono: { fontFamily: t.mono, fontSize: 12.5 },
|
|
209
|
+
code: {
|
|
210
|
+
fontFamily: t.mono,
|
|
211
|
+
fontSize: 12,
|
|
212
|
+
background: t.surfaceAlt,
|
|
213
|
+
border: `1px solid ${t.borderLight}`,
|
|
214
|
+
borderRadius: 4,
|
|
215
|
+
padding: "1px 5px"
|
|
216
|
+
},
|
|
217
|
+
// ── surfaces ──
|
|
218
|
+
// Tables and lists are cards themselves, so every screen gets the framed,
|
|
219
|
+
// rounded look without wrapping each one. The last row's rule is removed in
|
|
220
|
+
// the shell stylesheet (a pseudo-selector); embedded without the shell it
|
|
221
|
+
// simply doubles against the frame.
|
|
222
|
+
table: {
|
|
223
|
+
width: "100%",
|
|
224
|
+
borderCollapse: "separate",
|
|
225
|
+
borderSpacing: 0,
|
|
226
|
+
fontSize: 13.5,
|
|
227
|
+
background: t.surface,
|
|
228
|
+
border: `1px solid ${t.border}`,
|
|
229
|
+
borderRadius: t.radiusLg,
|
|
230
|
+
overflow: "hidden",
|
|
231
|
+
boxShadow: t.shadowCard
|
|
232
|
+
},
|
|
89
233
|
th: {
|
|
90
234
|
textAlign: "left",
|
|
91
235
|
borderBottom: `1px solid ${t.border}`,
|
|
92
|
-
padding: "
|
|
236
|
+
padding: "9px 14px",
|
|
93
237
|
fontWeight: 600,
|
|
94
|
-
|
|
238
|
+
fontSize: 11.5,
|
|
239
|
+
textTransform: "uppercase",
|
|
240
|
+
letterSpacing: 0.4,
|
|
241
|
+
color: t.muted,
|
|
242
|
+
background: t.surfaceAlt,
|
|
243
|
+
whiteSpace: "nowrap"
|
|
244
|
+
},
|
|
245
|
+
td: {
|
|
246
|
+
borderBottom: `1px solid ${t.borderLight}`,
|
|
247
|
+
padding: "11px 14px",
|
|
248
|
+
verticalAlign: "top"
|
|
249
|
+
},
|
|
250
|
+
list: {
|
|
251
|
+
listStyle: "none",
|
|
252
|
+
padding: 0,
|
|
253
|
+
margin: 0,
|
|
254
|
+
background: t.surface,
|
|
255
|
+
border: `1px solid ${t.border}`,
|
|
256
|
+
borderRadius: t.radiusLg,
|
|
257
|
+
overflow: "hidden",
|
|
258
|
+
boxShadow: t.shadowCard
|
|
259
|
+
},
|
|
260
|
+
row: { borderBottom: `1px solid ${t.borderLight}`, padding: "12px 16px" },
|
|
261
|
+
card: {
|
|
262
|
+
background: t.surface,
|
|
263
|
+
border: `1px solid ${t.border}`,
|
|
264
|
+
borderRadius: t.radiusLg,
|
|
265
|
+
padding: 16,
|
|
266
|
+
boxShadow: t.shadowCard
|
|
267
|
+
},
|
|
268
|
+
grid: {
|
|
269
|
+
display: "grid",
|
|
270
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))",
|
|
271
|
+
gap: 12,
|
|
272
|
+
marginBottom: 24
|
|
273
|
+
},
|
|
274
|
+
statLabel: { fontSize: 12, color: t.muted, fontWeight: 500 },
|
|
275
|
+
statValue: {
|
|
276
|
+
fontSize: 26,
|
|
277
|
+
fontWeight: 600,
|
|
278
|
+
letterSpacing: t.trackingDisplay,
|
|
279
|
+
lineHeight: 1.2,
|
|
280
|
+
marginTop: 6
|
|
281
|
+
},
|
|
282
|
+
statHint: { fontSize: 12, color: t.muted, marginTop: 2 },
|
|
283
|
+
empty: {
|
|
284
|
+
background: t.surface,
|
|
285
|
+
border: `1px dashed ${t.border}`,
|
|
286
|
+
borderRadius: t.radiusLg,
|
|
287
|
+
padding: "40px 24px",
|
|
288
|
+
textAlign: "center",
|
|
289
|
+
color: t.muted,
|
|
290
|
+
fontSize: 13.5
|
|
291
|
+
},
|
|
292
|
+
emptyTitle: { color: t.text, fontWeight: 600, fontSize: 14.5, margin: "10px 0 4px" },
|
|
293
|
+
dl: { display: "grid", gridTemplateColumns: "160px 1fr", margin: 0 },
|
|
294
|
+
dt: {
|
|
295
|
+
color: t.muted,
|
|
296
|
+
fontSize: 13,
|
|
297
|
+
padding: "10px 16px",
|
|
298
|
+
borderBottom: `1px solid ${t.borderLight}`
|
|
299
|
+
},
|
|
300
|
+
dd: {
|
|
301
|
+
margin: 0,
|
|
302
|
+
padding: "10px 16px",
|
|
303
|
+
borderBottom: `1px solid ${t.borderLight}`,
|
|
304
|
+
fontSize: 13.5,
|
|
305
|
+
minWidth: 0
|
|
95
306
|
},
|
|
96
|
-
td: { borderBottom: `1px solid ${t.borderLight}`, padding: "6px 8px", verticalAlign: "top" },
|
|
97
|
-
list: { listStyle: "none", padding: 0, margin: 0 },
|
|
98
|
-
row: { borderBottom: `1px solid ${t.borderLight}`, padding: "8px 0" },
|
|
99
307
|
badge: {
|
|
100
|
-
display: "inline-
|
|
308
|
+
display: "inline-flex",
|
|
309
|
+
alignItems: "center",
|
|
310
|
+
gap: 5,
|
|
101
311
|
borderRadius: 999,
|
|
102
|
-
padding: "
|
|
103
|
-
fontSize:
|
|
312
|
+
padding: "2px 8px",
|
|
313
|
+
fontSize: 11.5,
|
|
314
|
+
fontWeight: 500,
|
|
315
|
+
lineHeight: 1.5,
|
|
104
316
|
background: t.surfaceAlt,
|
|
105
317
|
border: `1px solid ${t.border}`,
|
|
106
|
-
color: t.muted
|
|
318
|
+
color: t.muted,
|
|
319
|
+
whiteSpace: "nowrap"
|
|
320
|
+
},
|
|
321
|
+
pill: {
|
|
322
|
+
display: "inline-flex",
|
|
323
|
+
alignItems: "center",
|
|
324
|
+
gap: 6,
|
|
325
|
+
borderRadius: 999,
|
|
326
|
+
padding: "2px 9px 2px 8px",
|
|
327
|
+
fontSize: 12,
|
|
328
|
+
fontWeight: 600,
|
|
329
|
+
lineHeight: 1.5,
|
|
330
|
+
whiteSpace: "nowrap",
|
|
331
|
+
verticalAlign: "baseline"
|
|
107
332
|
},
|
|
333
|
+
dot: { width: 6, height: 6, borderRadius: 999, flexShrink: 0, background: "currentColor" },
|
|
334
|
+
// ── controls ──
|
|
108
335
|
button: {
|
|
336
|
+
...control,
|
|
109
337
|
background: t.surface,
|
|
110
338
|
border: `1px solid ${t.border}`,
|
|
111
|
-
borderRadius: t.radius,
|
|
112
|
-
padding: "4px 10px",
|
|
113
|
-
cursor: "pointer",
|
|
114
|
-
fontSize: 13,
|
|
115
339
|
color: t.text,
|
|
116
|
-
|
|
340
|
+
cursor: "pointer",
|
|
117
341
|
boxShadow: t.shadowCard
|
|
118
342
|
},
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
343
|
+
buttonPrimary: {
|
|
344
|
+
...control,
|
|
345
|
+
background: t.text,
|
|
346
|
+
border: `1px solid ${t.text}`,
|
|
347
|
+
color: t.surface,
|
|
348
|
+
cursor: "pointer"
|
|
349
|
+
},
|
|
350
|
+
buttonDanger: {
|
|
351
|
+
...control,
|
|
123
352
|
background: t.surface,
|
|
353
|
+
border: `1px solid ${tint(t.danger, 40)}`,
|
|
354
|
+
color: t.danger,
|
|
355
|
+
cursor: "pointer"
|
|
356
|
+
},
|
|
357
|
+
buttonGhost: {
|
|
358
|
+
...control,
|
|
359
|
+
background: "none",
|
|
360
|
+
border: "1px solid transparent",
|
|
361
|
+
color: t.muted,
|
|
362
|
+
cursor: "pointer",
|
|
363
|
+
padding: "0 8px"
|
|
364
|
+
},
|
|
365
|
+
link: {
|
|
366
|
+
background: "none",
|
|
367
|
+
border: "none",
|
|
368
|
+
padding: 0,
|
|
369
|
+
color: t.text,
|
|
370
|
+
fontFamily: "inherit",
|
|
371
|
+
fontSize: "inherit",
|
|
372
|
+
fontWeight: 500,
|
|
373
|
+
cursor: "pointer",
|
|
374
|
+
textAlign: "left"
|
|
375
|
+
},
|
|
376
|
+
input: {
|
|
377
|
+
height: 32,
|
|
378
|
+
boxSizing: "border-box",
|
|
124
379
|
border: `1px solid ${t.border}`,
|
|
125
|
-
borderRadius:
|
|
126
|
-
padding:
|
|
127
|
-
|
|
380
|
+
borderRadius: 6,
|
|
381
|
+
padding: "0 10px",
|
|
382
|
+
fontSize: 13.5,
|
|
383
|
+
fontFamily: "inherit",
|
|
384
|
+
background: t.surface,
|
|
385
|
+
color: t.text,
|
|
386
|
+
minWidth: 0
|
|
128
387
|
},
|
|
388
|
+
toolbar: { display: "flex", gap: 8, alignItems: "center", marginBottom: 16, flexWrap: "wrap" },
|
|
129
389
|
accent: { color: t.accent }
|
|
130
390
|
};
|
|
131
391
|
|
|
392
|
+
// src/icons.ts
|
|
393
|
+
var ICONS = {
|
|
394
|
+
attention: ["M22 12h-4l-3 9L9 3l-3 9H2"],
|
|
395
|
+
modules: [
|
|
396
|
+
"M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z",
|
|
397
|
+
"M3.3 7 12 12l8.7-5",
|
|
398
|
+
"M12 22V12"
|
|
399
|
+
],
|
|
400
|
+
environment: [
|
|
401
|
+
"M4 21v-7",
|
|
402
|
+
"M4 10V3",
|
|
403
|
+
"M12 21v-9",
|
|
404
|
+
"M12 8V3",
|
|
405
|
+
"M20 21v-5",
|
|
406
|
+
"M20 12V3",
|
|
407
|
+
"M1 14h6",
|
|
408
|
+
"M9 8h6",
|
|
409
|
+
"M17 16h6"
|
|
410
|
+
],
|
|
411
|
+
doctor: [
|
|
412
|
+
"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2"
|
|
413
|
+
],
|
|
414
|
+
routes: [
|
|
415
|
+
"M6 3v12",
|
|
416
|
+
"M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6z",
|
|
417
|
+
"M6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6z",
|
|
418
|
+
"M18 9a9 9 0 0 1-9 9"
|
|
419
|
+
],
|
|
420
|
+
users: [
|
|
421
|
+
"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",
|
|
422
|
+
"M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z",
|
|
423
|
+
"M22 21v-2a4 4 0 0 0-3-3.87",
|
|
424
|
+
"M16 3.13a4 4 0 0 1 0 7.75"
|
|
425
|
+
],
|
|
426
|
+
catalog: [
|
|
427
|
+
"M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z",
|
|
428
|
+
"M7.5 8a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1z"
|
|
429
|
+
],
|
|
430
|
+
subscriber: ["M2 7h20v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2z", "M2 11h20", "M6 15h4"],
|
|
431
|
+
settings: [
|
|
432
|
+
"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z",
|
|
433
|
+
"M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"
|
|
434
|
+
],
|
|
435
|
+
templates: [
|
|
436
|
+
"M22 7l-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7",
|
|
437
|
+
"M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"
|
|
438
|
+
],
|
|
439
|
+
audit: ["M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z", "M9 12l2 2 4-4"],
|
|
440
|
+
log: ["M12 8v4l3 3", "M3.05 11a9 9 0 1 1 .5 4m-.5 5v-5h5"],
|
|
441
|
+
tokens: [
|
|
442
|
+
"M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z",
|
|
443
|
+
"M16.5 8a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1z"
|
|
444
|
+
],
|
|
445
|
+
migrations: [
|
|
446
|
+
"M12 22c4.97 0 9-1.79 9-4V6",
|
|
447
|
+
"M3 6v12c0 2.21 4.03 4 9 4",
|
|
448
|
+
"M12 10c4.97 0 9-1.79 9-4s-4.03-4-9-4-9 1.79-9 4 4.03 4 9 4z",
|
|
449
|
+
"M3 12c0 2.21 4.03 4 9 4s9-1.79 9-4"
|
|
450
|
+
],
|
|
451
|
+
menu: ["M4 6h16", "M4 12h16", "M4 18h16"],
|
|
452
|
+
close: ["M18 6 6 18", "m6 6 12 12"],
|
|
453
|
+
refresh: [
|
|
454
|
+
"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",
|
|
455
|
+
"M21 3v5h-5",
|
|
456
|
+
"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",
|
|
457
|
+
"M8 16H3v5"
|
|
458
|
+
],
|
|
459
|
+
check: ["M20 6 9 17l-5-5"],
|
|
460
|
+
alert: [
|
|
461
|
+
"M12 9v4",
|
|
462
|
+
"M12 17h.01",
|
|
463
|
+
"M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"
|
|
464
|
+
],
|
|
465
|
+
search: ["M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16z", "m21 21-4.3-4.3"],
|
|
466
|
+
back: ["m12 19-7-7 7-7", "M19 12H5"],
|
|
467
|
+
logout: ["M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4", "m16 17 5-5-5-5", "M21 12H9"],
|
|
468
|
+
lock: [
|
|
469
|
+
"M5 11h14a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2z",
|
|
470
|
+
"M7 11V7a5 5 0 0 1 10 0v4"
|
|
471
|
+
],
|
|
472
|
+
plus: ["M12 5v14", "M5 12h14"],
|
|
473
|
+
mark: [
|
|
474
|
+
"M4 3h5a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z",
|
|
475
|
+
"M15 3h5a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z",
|
|
476
|
+
"M15 14h5a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1v-5a1 1 0 0 1 1-1z",
|
|
477
|
+
"M4 14h5a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-5a1 1 0 0 1 1-1z"
|
|
478
|
+
]
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// src/ui.tsx
|
|
482
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
483
|
+
function Icon({
|
|
484
|
+
name,
|
|
485
|
+
size = 16,
|
|
486
|
+
style
|
|
487
|
+
}) {
|
|
488
|
+
return /* @__PURE__ */ jsx(
|
|
489
|
+
"svg",
|
|
490
|
+
{
|
|
491
|
+
width: size,
|
|
492
|
+
height: size,
|
|
493
|
+
viewBox: "0 0 24 24",
|
|
494
|
+
fill: "none",
|
|
495
|
+
stroke: "currentColor",
|
|
496
|
+
strokeWidth: 2,
|
|
497
|
+
strokeLinecap: "round",
|
|
498
|
+
strokeLinejoin: "round",
|
|
499
|
+
"aria-hidden": "true",
|
|
500
|
+
style: { flexShrink: 0, ...style },
|
|
501
|
+
children: ICONS[name].map((d) => /* @__PURE__ */ jsx("path", { d }, d))
|
|
502
|
+
}
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
function PageHeader({
|
|
506
|
+
title,
|
|
507
|
+
lead,
|
|
508
|
+
actions
|
|
509
|
+
}) {
|
|
510
|
+
return /* @__PURE__ */ jsxs("div", { style: styles.header, children: [
|
|
511
|
+
/* @__PURE__ */ jsxs("div", { style: { minWidth: 0 }, children: [
|
|
512
|
+
/* @__PURE__ */ jsx("h1", { style: styles.title, children: title }),
|
|
513
|
+
lead ? /* @__PURE__ */ jsx("p", { style: styles.lead, children: lead }) : null
|
|
514
|
+
] }),
|
|
515
|
+
actions ? /* @__PURE__ */ jsx("div", { style: styles.actions, children: actions }) : null
|
|
516
|
+
] });
|
|
517
|
+
}
|
|
518
|
+
function Pill({
|
|
519
|
+
tone,
|
|
520
|
+
children,
|
|
521
|
+
dot = true
|
|
522
|
+
}) {
|
|
523
|
+
const color = toneColor[tone];
|
|
524
|
+
return /* @__PURE__ */ jsxs("span", { style: { ...styles.pill, color, background: tint(color, tone === "neutral" ? 10 : 13) }, children: [
|
|
525
|
+
dot ? /* @__PURE__ */ jsx("span", { style: styles.dot }) : null,
|
|
526
|
+
children
|
|
527
|
+
] });
|
|
528
|
+
}
|
|
529
|
+
function Stat({
|
|
530
|
+
label,
|
|
531
|
+
value,
|
|
532
|
+
hint,
|
|
533
|
+
tone
|
|
534
|
+
}) {
|
|
535
|
+
return /* @__PURE__ */ jsxs("div", { style: styles.card, children: [
|
|
536
|
+
/* @__PURE__ */ jsx("div", { style: styles.statLabel, children: label }),
|
|
537
|
+
/* @__PURE__ */ jsx("div", { style: { ...styles.statValue, ...tone ? { color: toneColor[tone] } : {} }, children: value }),
|
|
538
|
+
hint ? /* @__PURE__ */ jsx("div", { style: styles.statHint, children: hint }) : null
|
|
539
|
+
] });
|
|
540
|
+
}
|
|
541
|
+
function Empty({
|
|
542
|
+
icon = "check",
|
|
543
|
+
title,
|
|
544
|
+
children
|
|
545
|
+
}) {
|
|
546
|
+
return /* @__PURE__ */ jsxs("div", { style: styles.empty, children: [
|
|
547
|
+
/* @__PURE__ */ jsx(Icon, { name: icon, size: 22 }),
|
|
548
|
+
/* @__PURE__ */ jsx("div", { style: styles.emptyTitle, children: title }),
|
|
549
|
+
children ? /* @__PURE__ */ jsx("div", { children }) : null
|
|
550
|
+
] });
|
|
551
|
+
}
|
|
552
|
+
function RefreshButton({
|
|
553
|
+
onClick,
|
|
554
|
+
busy,
|
|
555
|
+
label = "Refresh"
|
|
556
|
+
}) {
|
|
557
|
+
return /* @__PURE__ */ jsxs("button", { type: "button", style: styles.button, onClick, disabled: busy, children: [
|
|
558
|
+
/* @__PURE__ */ jsx(Icon, { name: "refresh", size: 14 }),
|
|
559
|
+
busy ? "Working\u2026" : label
|
|
560
|
+
] });
|
|
561
|
+
}
|
|
562
|
+
function MethodChip({ method }) {
|
|
563
|
+
const tone = method === "GET" ? "ok" : method === "DELETE" ? "bad" : method === "POST" ? "info" : "warn";
|
|
564
|
+
const color = toneColor[tone];
|
|
565
|
+
return /* @__PURE__ */ jsx(
|
|
566
|
+
"span",
|
|
567
|
+
{
|
|
568
|
+
style: {
|
|
569
|
+
...styles.mono,
|
|
570
|
+
fontSize: 11,
|
|
571
|
+
fontWeight: 700,
|
|
572
|
+
color,
|
|
573
|
+
background: tint(color, 12),
|
|
574
|
+
borderRadius: 4,
|
|
575
|
+
padding: "2px 6px",
|
|
576
|
+
display: "inline-block",
|
|
577
|
+
minWidth: 44,
|
|
578
|
+
textAlign: "center"
|
|
579
|
+
},
|
|
580
|
+
children: method
|
|
581
|
+
}
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
|
|
132
585
|
// src/screens/AdminLogScreen.tsx
|
|
133
586
|
import { useAdminLog } from "@fonderie/react-admin";
|
|
134
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
587
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
135
588
|
function AdminLogScreen({ client, pageSize = 50 }) {
|
|
136
589
|
const { entries, hasMore, isLoading, error, refresh, loadMore } = useAdminLog(client, {
|
|
137
590
|
limit: pageSize
|
|
138
591
|
});
|
|
139
|
-
return /* @__PURE__ */
|
|
140
|
-
/* @__PURE__ */
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
"
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
/* @__PURE__ */ jsx("th", { style: styles.th, children: "When" }),
|
|
157
|
-
/* @__PURE__ */ jsx("th", { style: styles.th, children: "Actor" }),
|
|
158
|
-
/* @__PURE__ */ jsx("th", { style: styles.th, children: "Request" }),
|
|
159
|
-
/* @__PURE__ */ jsx("th", { style: styles.th, children: "Status" }),
|
|
160
|
-
/* @__PURE__ */ jsx("th", { style: styles.th, children: "Module" })
|
|
592
|
+
return /* @__PURE__ */ jsxs2("div", { style: styles.container, children: [
|
|
593
|
+
/* @__PURE__ */ jsx2(
|
|
594
|
+
PageHeader,
|
|
595
|
+
{
|
|
596
|
+
title: "Admin log",
|
|
597
|
+
lead: "Every request made through this surface, newest first \u2014 refused ones included.",
|
|
598
|
+
actions: /* @__PURE__ */ jsx2(RefreshButton, { onClick: () => void refresh(), busy: isLoading })
|
|
599
|
+
}
|
|
600
|
+
),
|
|
601
|
+
error ? /* @__PURE__ */ jsx2("p", { style: styles.error, role: "alert", children: error.status === 404 ? "The admin log is off \u2014 give AdminModule a store." : error.explanation }) : entries.length === 0 && !isLoading ? /* @__PURE__ */ jsx2(Empty, { icon: "log", title: "No requests yet" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
602
|
+
/* @__PURE__ */ jsxs2("table", { style: styles.table, children: [
|
|
603
|
+
/* @__PURE__ */ jsx2("thead", { children: /* @__PURE__ */ jsxs2("tr", { children: [
|
|
604
|
+
/* @__PURE__ */ jsx2("th", { style: styles.th, children: "When" }),
|
|
605
|
+
/* @__PURE__ */ jsx2("th", { style: styles.th, children: "Actor" }),
|
|
606
|
+
/* @__PURE__ */ jsx2("th", { style: styles.th, children: "Request" }),
|
|
607
|
+
/* @__PURE__ */ jsx2("th", { style: styles.th, children: "Status" }),
|
|
608
|
+
/* @__PURE__ */ jsx2("th", { style: styles.th, children: "Module" })
|
|
161
609
|
] }) }),
|
|
162
|
-
/* @__PURE__ */
|
|
163
|
-
/* @__PURE__ */
|
|
164
|
-
/* @__PURE__ */
|
|
165
|
-
/* @__PURE__ */
|
|
166
|
-
e.method,
|
|
610
|
+
/* @__PURE__ */ jsx2("tbody", { children: entries.map((e) => /* @__PURE__ */ jsxs2("tr", { children: [
|
|
611
|
+
/* @__PURE__ */ jsx2("td", { style: { ...styles.td, ...styles.muted, whiteSpace: "nowrap" }, children: new Date(e.at).toLocaleString() }),
|
|
612
|
+
/* @__PURE__ */ jsx2("td", { style: styles.td, children: e.actor }),
|
|
613
|
+
/* @__PURE__ */ jsxs2("td", { style: styles.td, children: [
|
|
614
|
+
/* @__PURE__ */ jsx2(MethodChip, { method: e.method }),
|
|
167
615
|
" ",
|
|
168
|
-
e.path
|
|
616
|
+
/* @__PURE__ */ jsx2("span", { style: { ...styles.mono, wordBreak: "break-all" }, children: e.path })
|
|
169
617
|
] }),
|
|
170
|
-
/* @__PURE__ */
|
|
171
|
-
/* @__PURE__ */
|
|
618
|
+
/* @__PURE__ */ jsxs2("td", { style: { ...styles.td, whiteSpace: "nowrap" }, children: [
|
|
619
|
+
/* @__PURE__ */ jsx2(Pill, { tone: e.status >= 500 ? "bad" : e.status >= 400 ? "warn" : "ok", children: e.status }),
|
|
172
620
|
" ",
|
|
173
|
-
/* @__PURE__ */
|
|
621
|
+
/* @__PURE__ */ jsxs2("span", { style: styles.muted, children: [
|
|
174
622
|
e.durationMs,
|
|
175
623
|
" ms"
|
|
176
624
|
] })
|
|
177
625
|
] }),
|
|
178
|
-
/* @__PURE__ */
|
|
626
|
+
/* @__PURE__ */ jsx2("td", { style: { ...styles.td, ...styles.muted }, children: e.module })
|
|
179
627
|
] }, e.id)) })
|
|
180
628
|
] }),
|
|
181
|
-
isLoading ? /* @__PURE__ */
|
|
182
|
-
hasMore && !isLoading ? /* @__PURE__ */
|
|
629
|
+
isLoading ? /* @__PURE__ */ jsx2("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
630
|
+
hasMore && !isLoading ? /* @__PURE__ */ jsx2(
|
|
183
631
|
"button",
|
|
184
632
|
{
|
|
185
633
|
type: "button",
|
|
@@ -193,180 +641,373 @@ function AdminLogScreen({ client, pageSize = 50 }) {
|
|
|
193
641
|
}
|
|
194
642
|
|
|
195
643
|
// src/screens/AttentionScreen.tsx
|
|
196
|
-
import { useAttention } from "@fonderie/react-admin";
|
|
197
|
-
import { jsx as
|
|
644
|
+
import { useAttention, useManifest } from "@fonderie/react-admin";
|
|
645
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
198
646
|
function AttentionScreen({ client }) {
|
|
199
647
|
const { attention, isLoading, error, refresh } = useAttention(client);
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
648
|
+
const { manifest } = useManifest(client);
|
|
649
|
+
const errors = attention?.items.filter((i) => i.severity === "error") ?? [];
|
|
650
|
+
const advice = attention?.items.filter((i) => i.severity !== "error") ?? [];
|
|
651
|
+
const ready = manifest?.modules.filter((m) => m.readiness.ok).length ?? 0;
|
|
652
|
+
const total = manifest?.modules.length ?? 0;
|
|
653
|
+
return /* @__PURE__ */ jsxs3("div", { style: styles.container, children: [
|
|
654
|
+
/* @__PURE__ */ jsx3(
|
|
655
|
+
PageHeader,
|
|
656
|
+
{
|
|
657
|
+
title: "Attention",
|
|
658
|
+
lead: attention ? `What needs you on this deployment \xB7 checked ${new Date(attention.generatedAt).toLocaleTimeString()}` : "What needs you on this deployment",
|
|
659
|
+
actions: /* @__PURE__ */ jsx3(RefreshButton, { onClick: () => void refresh(), busy: isLoading })
|
|
660
|
+
}
|
|
661
|
+
),
|
|
662
|
+
error ? /* @__PURE__ */ jsx3("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
|
|
663
|
+
/* @__PURE__ */ jsxs3("div", { style: styles.grid, children: [
|
|
664
|
+
/* @__PURE__ */ jsx3(
|
|
665
|
+
Stat,
|
|
205
666
|
{
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
667
|
+
label: "Needs action",
|
|
668
|
+
value: attention ? errors.length : "\u2014",
|
|
669
|
+
tone: errors.length ? "bad" : attention ? "ok" : void 0,
|
|
670
|
+
hint: "errors to fix"
|
|
671
|
+
}
|
|
672
|
+
),
|
|
673
|
+
/* @__PURE__ */ jsx3(
|
|
674
|
+
Stat,
|
|
675
|
+
{
|
|
676
|
+
label: "Advice",
|
|
677
|
+
value: attention ? advice.length : "\u2014",
|
|
678
|
+
tone: advice.length ? "warn" : void 0,
|
|
679
|
+
hint: "worth a look"
|
|
680
|
+
}
|
|
681
|
+
),
|
|
682
|
+
/* @__PURE__ */ jsx3(
|
|
683
|
+
Stat,
|
|
684
|
+
{
|
|
685
|
+
label: "Modules ready",
|
|
686
|
+
value: manifest ? `${ready}/${total}` : "\u2014",
|
|
687
|
+
tone: manifest && ready < total ? "warn" : manifest ? "ok" : void 0,
|
|
688
|
+
hint: manifest ? `admin ${manifest.admin.version}` : void 0
|
|
689
|
+
}
|
|
690
|
+
),
|
|
691
|
+
/* @__PURE__ */ jsx3(
|
|
692
|
+
Stat,
|
|
693
|
+
{
|
|
694
|
+
label: "Routes",
|
|
695
|
+
value: manifest ? manifest.routes.length : "\u2014",
|
|
696
|
+
hint: manifest ? manifest.env : void 0
|
|
211
697
|
}
|
|
212
698
|
)
|
|
213
699
|
] }),
|
|
214
|
-
isLoading && !attention ? /* @__PURE__ */
|
|
215
|
-
"
|
|
700
|
+
isLoading && !attention ? /* @__PURE__ */ jsx3("p", { style: styles.status, children: "Running the checks\u2026" }) : attention && attention.items.length === 0 ? /* @__PURE__ */ jsxs3(Empty, { title: "Nothing needs you", children: [
|
|
701
|
+
"Every check passes. Checked ",
|
|
216
702
|
new Date(attention.generatedAt).toLocaleString(),
|
|
217
703
|
"."
|
|
218
|
-
] }) : /* @__PURE__ */
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
704
|
+
] }) : /* @__PURE__ */ jsx3("ul", { style: styles.list, children: [...errors, ...advice].map((item) => /* @__PURE__ */ jsxs3(
|
|
705
|
+
"li",
|
|
706
|
+
{
|
|
707
|
+
style: { ...styles.row, display: "flex", gap: 12, alignItems: "flex-start" },
|
|
708
|
+
children: [
|
|
709
|
+
/* @__PURE__ */ jsx3(
|
|
710
|
+
Icon,
|
|
711
|
+
{
|
|
712
|
+
name: "alert",
|
|
713
|
+
size: 16,
|
|
714
|
+
style: {
|
|
715
|
+
marginTop: 3,
|
|
716
|
+
color: item.severity === "error" ? "var(--fonderie-danger,#e00)" : "var(--fonderie-warning,#f5a623)"
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
),
|
|
720
|
+
/* @__PURE__ */ jsxs3("div", { style: { minWidth: 0, flex: 1 }, children: [
|
|
721
|
+
/* @__PURE__ */ jsxs3(
|
|
722
|
+
"div",
|
|
723
|
+
{
|
|
724
|
+
style: {
|
|
725
|
+
display: "flex",
|
|
726
|
+
gap: 8,
|
|
727
|
+
alignItems: "center",
|
|
728
|
+
flexWrap: "wrap",
|
|
729
|
+
marginBottom: 2
|
|
730
|
+
},
|
|
731
|
+
children: [
|
|
732
|
+
/* @__PURE__ */ jsx3(Pill, { tone: item.severity === "error" ? "bad" : "warn", children: item.severity === "error" ? "error" : "advice" }),
|
|
733
|
+
/* @__PURE__ */ jsx3("span", { style: styles.mono, children: item.source })
|
|
734
|
+
]
|
|
735
|
+
}
|
|
736
|
+
),
|
|
737
|
+
/* @__PURE__ */ jsx3("div", { style: { lineHeight: 1.55 }, children: item.message })
|
|
738
|
+
] })
|
|
739
|
+
]
|
|
740
|
+
},
|
|
741
|
+
`${item.source}:${item.message}`
|
|
742
|
+
)) })
|
|
225
743
|
] });
|
|
226
744
|
}
|
|
227
745
|
|
|
228
746
|
// src/screens/EnvironmentScreen.tsx
|
|
229
747
|
import { useAdminEnvironment } from "@fonderie/react-admin";
|
|
230
|
-
import { Fragment as Fragment2, jsx as
|
|
748
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
231
749
|
function EnvironmentScreen({ client }) {
|
|
232
750
|
const { report, isLoading, error } = useAdminEnvironment(client);
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
" ",
|
|
240
|
-
|
|
751
|
+
const missing = report?.env.filter((e) => !e.set).length ?? 0;
|
|
752
|
+
return /* @__PURE__ */ jsxs4("div", { style: styles.container, children: [
|
|
753
|
+
/* @__PURE__ */ jsx4(
|
|
754
|
+
PageHeader,
|
|
755
|
+
{
|
|
756
|
+
title: "Environment",
|
|
757
|
+
lead: "Whether each module is configured and each variable the app reads is set. Values are never shown.",
|
|
758
|
+
actions: report && report.env.length > 0 ? /* @__PURE__ */ jsx4(Pill, { tone: missing ? "bad" : "ok", children: missing ? `${missing} missing` : "all set" }) : null
|
|
759
|
+
}
|
|
760
|
+
),
|
|
761
|
+
isLoading ? /* @__PURE__ */ jsx4("p", { style: styles.status, children: "Loading\u2026" }) : error ? /* @__PURE__ */ jsx4("p", { style: styles.error, role: "alert", children: error.explanation }) : report ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
762
|
+
/* @__PURE__ */ jsx4("h2", { style: { ...styles.subtitle, marginTop: 0 }, children: "Variables" }),
|
|
763
|
+
report.env.length === 0 ? /* @__PURE__ */ jsx4("p", { style: styles.muted, children: "No variables declared \u2014 pass `env` to AdminModule." }) : /* @__PURE__ */ jsx4(
|
|
764
|
+
"div",
|
|
765
|
+
{
|
|
766
|
+
style: {
|
|
767
|
+
...styles.grid,
|
|
768
|
+
gridTemplateColumns: "repeat(auto-fill, minmax(260px, 1fr))",
|
|
769
|
+
gap: 8
|
|
770
|
+
},
|
|
771
|
+
children: report.env.map((e) => /* @__PURE__ */ jsxs4(
|
|
772
|
+
"div",
|
|
773
|
+
{
|
|
774
|
+
style: {
|
|
775
|
+
...styles.card,
|
|
776
|
+
padding: "10px 12px",
|
|
777
|
+
display: "flex",
|
|
778
|
+
alignItems: "center",
|
|
779
|
+
justifyContent: "space-between",
|
|
780
|
+
gap: 8
|
|
781
|
+
},
|
|
782
|
+
children: [
|
|
783
|
+
/* @__PURE__ */ jsx4("span", { style: { ...styles.mono, wordBreak: "break-all" }, children: e.name }),
|
|
784
|
+
/* @__PURE__ */ jsx4(Pill, { tone: e.set ? "ok" : "bad", children: e.set ? "set" : "missing" })
|
|
785
|
+
]
|
|
786
|
+
},
|
|
787
|
+
e.name
|
|
788
|
+
))
|
|
789
|
+
}
|
|
790
|
+
),
|
|
791
|
+
/* @__PURE__ */ jsx4("h2", { style: styles.subtitle, children: "Module readiness" }),
|
|
792
|
+
/* @__PURE__ */ jsx4("ul", { style: styles.list, children: report.modules.map((m) => /* @__PURE__ */ jsxs4("li", { style: styles.row, children: [
|
|
793
|
+
/* @__PURE__ */ jsxs4(
|
|
241
794
|
"div",
|
|
242
795
|
{
|
|
243
|
-
style:
|
|
796
|
+
style: {
|
|
797
|
+
display: "flex",
|
|
798
|
+
justifyContent: "space-between",
|
|
799
|
+
gap: 12,
|
|
800
|
+
alignItems: "center"
|
|
801
|
+
},
|
|
802
|
+
children: [
|
|
803
|
+
/* @__PURE__ */ jsx4("span", { style: { ...styles.mono, fontWeight: 600 }, children: m.name }),
|
|
804
|
+
/* @__PURE__ */ jsx4(
|
|
805
|
+
Pill,
|
|
806
|
+
{
|
|
807
|
+
tone: m.problems.length === 0 ? "ok" : m.problems.some((p) => p.severity === "error") ? "bad" : "warn",
|
|
808
|
+
children: m.problems.length === 0 ? "ready" : `${m.problems.length} problem${m.problems.length === 1 ? "" : "s"}`
|
|
809
|
+
}
|
|
810
|
+
)
|
|
811
|
+
]
|
|
812
|
+
}
|
|
813
|
+
),
|
|
814
|
+
m.problems.map((p) => /* @__PURE__ */ jsx4(
|
|
815
|
+
"div",
|
|
816
|
+
{
|
|
817
|
+
style: {
|
|
818
|
+
marginTop: 6,
|
|
819
|
+
fontSize: 13,
|
|
820
|
+
color: p.severity === "error" ? "var(--fonderie-danger,#e00)" : "var(--fonderie-text-muted,#5c5c5c)"
|
|
821
|
+
},
|
|
244
822
|
children: p.message
|
|
245
823
|
},
|
|
246
824
|
p.message
|
|
247
825
|
))
|
|
248
|
-
] }, m.name)) })
|
|
249
|
-
/* @__PURE__ */ jsx3("h2", { style: styles.subtitle, children: "Environment" }),
|
|
250
|
-
report.env.length === 0 ? /* @__PURE__ */ jsx3("p", { style: styles.muted, children: "No variables declared \u2014 pass `env` to AdminModule." }) : /* @__PURE__ */ jsx3("ul", { style: styles.list, children: report.env.map((e) => /* @__PURE__ */ jsxs3("li", { style: styles.row, children: [
|
|
251
|
-
/* @__PURE__ */ jsx3("span", { style: styles.mono, children: e.name }),
|
|
252
|
-
" ",
|
|
253
|
-
e.set ? /* @__PURE__ */ jsx3("span", { style: styles.ok, children: "set" }) : /* @__PURE__ */ jsx3("span", { style: styles.bad, children: "missing" })
|
|
254
|
-
] }, e.name)) })
|
|
826
|
+
] }, m.name)) })
|
|
255
827
|
] }) : null
|
|
256
828
|
] });
|
|
257
829
|
}
|
|
258
830
|
|
|
259
831
|
// src/screens/DoctorScreen.tsx
|
|
260
832
|
import { useDoctor } from "@fonderie/react-admin";
|
|
261
|
-
import { jsx as
|
|
833
|
+
import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
262
834
|
function DoctorScreen({ client }) {
|
|
263
835
|
const { report, isLoading, error, refresh } = useDoctor(client);
|
|
264
|
-
return /* @__PURE__ */
|
|
265
|
-
/* @__PURE__ */
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
"
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
onClick: () => void refresh(),
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
/* @__PURE__ */
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
836
|
+
return /* @__PURE__ */ jsxs5("div", { style: styles.container, children: [
|
|
837
|
+
/* @__PURE__ */ jsx5(
|
|
838
|
+
PageHeader,
|
|
839
|
+
{
|
|
840
|
+
title: "Doctor",
|
|
841
|
+
lead: "Every reconciliation check across the installed modules, run on demand.",
|
|
842
|
+
actions: /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
843
|
+
report && !isLoading ? /* @__PURE__ */ jsx5(Pill, { tone: report.ok ? "ok" : "bad", children: report.ok ? "all checks pass" : "a check failed" }) : null,
|
|
844
|
+
/* @__PURE__ */ jsx5(RefreshButton, { onClick: () => void refresh(), busy: isLoading, label: "Run again" })
|
|
845
|
+
] })
|
|
846
|
+
}
|
|
847
|
+
),
|
|
848
|
+
isLoading && !report ? /* @__PURE__ */ jsx5("p", { style: styles.status, children: "Running the checks\u2026" }) : error ? /* @__PURE__ */ jsx5("p", { style: styles.error, role: "alert", children: error.explanation }) : /* @__PURE__ */ jsxs5("table", { style: styles.table, children: [
|
|
849
|
+
/* @__PURE__ */ jsx5("thead", { children: /* @__PURE__ */ jsxs5("tr", { children: [
|
|
850
|
+
/* @__PURE__ */ jsx5("th", { style: { ...styles.th, width: 110 }, children: "Result" }),
|
|
851
|
+
/* @__PURE__ */ jsx5("th", { style: styles.th, children: "Check" }),
|
|
852
|
+
/* @__PURE__ */ jsx5("th", { style: { ...styles.th, textAlign: "right", width: 90 }, children: "Time" })
|
|
853
|
+
] }) }),
|
|
854
|
+
/* @__PURE__ */ jsx5("tbody", { children: report?.checks.map((c) => /* @__PURE__ */ jsxs5("tr", { children: [
|
|
855
|
+
/* @__PURE__ */ jsx5("td", { style: styles.td, children: /* @__PURE__ */ jsx5(
|
|
856
|
+
Pill,
|
|
857
|
+
{
|
|
858
|
+
tone: c.skipped ? "neutral" : c.ok ? c.findings.length ? "warn" : "ok" : "bad",
|
|
859
|
+
children: c.skipped ? "skipped" : c.ok ? c.findings.length ? "advice" : "ok" : "failed"
|
|
860
|
+
}
|
|
861
|
+
) }),
|
|
862
|
+
/* @__PURE__ */ jsxs5("td", { style: styles.td, children: [
|
|
863
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
864
|
+
/* @__PURE__ */ jsx5("span", { style: { ...styles.mono, fontWeight: 600 }, children: c.name }),
|
|
865
|
+
" ",
|
|
866
|
+
/* @__PURE__ */ jsx5("span", { style: styles.muted, children: c.module })
|
|
867
|
+
] }),
|
|
868
|
+
c.skipped ? /* @__PURE__ */ jsx5("div", { style: { ...styles.muted, marginTop: 4 }, children: c.skipped }) : null,
|
|
869
|
+
c.findings.map((f) => /* @__PURE__ */ jsx5(
|
|
870
|
+
"div",
|
|
871
|
+
{
|
|
872
|
+
style: {
|
|
873
|
+
marginTop: 4,
|
|
874
|
+
fontSize: 13,
|
|
875
|
+
lineHeight: 1.55,
|
|
876
|
+
color: c.ok ? "var(--fonderie-text,#171717)" : "var(--fonderie-danger,#e00)"
|
|
877
|
+
},
|
|
878
|
+
children: f
|
|
879
|
+
},
|
|
880
|
+
f
|
|
881
|
+
))
|
|
882
|
+
] }),
|
|
883
|
+
/* @__PURE__ */ jsxs5(
|
|
884
|
+
"td",
|
|
885
|
+
{
|
|
886
|
+
style: {
|
|
887
|
+
...styles.td,
|
|
888
|
+
...styles.muted,
|
|
889
|
+
textAlign: "right",
|
|
890
|
+
whiteSpace: "nowrap"
|
|
891
|
+
},
|
|
892
|
+
children: [
|
|
893
|
+
c.durationMs,
|
|
894
|
+
" ms"
|
|
895
|
+
]
|
|
896
|
+
}
|
|
897
|
+
)
|
|
898
|
+
] }, c.name)) })
|
|
899
|
+
] })
|
|
293
900
|
] });
|
|
294
901
|
}
|
|
295
902
|
|
|
296
903
|
// src/screens/ModulesScreen.tsx
|
|
297
|
-
import { useManifest } from "@fonderie/react-admin";
|
|
298
|
-
import {
|
|
904
|
+
import { useManifest as useManifest2 } from "@fonderie/react-admin";
|
|
905
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
299
906
|
function ModulesScreen({ client }) {
|
|
300
|
-
const { manifest, isLoading, error } =
|
|
301
|
-
return /* @__PURE__ */
|
|
302
|
-
/* @__PURE__ */
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
"
|
|
312
|
-
|
|
313
|
-
"
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
/* @__PURE__ */
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
"div",
|
|
329
|
-
{
|
|
330
|
-
style: p.severity === "error" ? styles.bad : styles.advice,
|
|
331
|
-
children: p.message
|
|
907
|
+
const { manifest, isLoading, error } = useManifest2(client);
|
|
908
|
+
return /* @__PURE__ */ jsxs6("div", { style: styles.container, children: [
|
|
909
|
+
/* @__PURE__ */ jsx6(
|
|
910
|
+
PageHeader,
|
|
911
|
+
{
|
|
912
|
+
title: "Modules",
|
|
913
|
+
lead: manifest ? `${manifest.env} \xB7 admin ${manifest.admin.version} \xB7 admin log ${manifest.admin.log ? "on" : "off"} \xB7 ${manifest.routes.length} routes` : "Every installed module, its version and whether it is ready."
|
|
914
|
+
}
|
|
915
|
+
),
|
|
916
|
+
isLoading ? /* @__PURE__ */ jsx6("p", { style: styles.status, children: "Loading\u2026" }) : error ? /* @__PURE__ */ jsx6("p", { style: styles.error, role: "alert", children: error.explanation }) : manifest ? /* @__PURE__ */ jsxs6("table", { style: styles.table, children: [
|
|
917
|
+
/* @__PURE__ */ jsx6("thead", { children: /* @__PURE__ */ jsxs6("tr", { children: [
|
|
918
|
+
/* @__PURE__ */ jsx6("th", { style: styles.th, children: "Module" }),
|
|
919
|
+
/* @__PURE__ */ jsx6("th", { style: styles.th, children: "Version" }),
|
|
920
|
+
/* @__PURE__ */ jsx6("th", { style: styles.th, children: "Readiness" }),
|
|
921
|
+
/* @__PURE__ */ jsx6("th", { style: styles.th, children: "Admin" })
|
|
922
|
+
] }) }),
|
|
923
|
+
/* @__PURE__ */ jsx6("tbody", { children: manifest.modules.map((m) => /* @__PURE__ */ jsxs6("tr", { children: [
|
|
924
|
+
/* @__PURE__ */ jsx6("td", { style: { ...styles.td, ...styles.mono, fontWeight: 600 }, children: m.name }),
|
|
925
|
+
/* @__PURE__ */ jsx6("td", { style: { ...styles.td, ...styles.mono }, children: m.version ?? /* @__PURE__ */ jsx6("span", { style: styles.muted, children: "not reported" }) }),
|
|
926
|
+
/* @__PURE__ */ jsxs6("td", { style: styles.td, children: [
|
|
927
|
+
/* @__PURE__ */ jsx6(Pill, { tone: m.readiness.ok ? "ok" : "bad", children: m.readiness.ok ? "ready" : "error" }),
|
|
928
|
+
m.readiness.problems.map((p) => /* @__PURE__ */ jsx6(
|
|
929
|
+
"div",
|
|
930
|
+
{
|
|
931
|
+
style: {
|
|
932
|
+
marginTop: 6,
|
|
933
|
+
fontSize: 13,
|
|
934
|
+
color: p.severity === "error" ? "var(--fonderie-danger,#e00)" : "var(--fonderie-text-muted,#5c5c5c)"
|
|
332
935
|
},
|
|
333
|
-
p.message
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
] },
|
|
338
|
-
|
|
936
|
+
children: p.message
|
|
937
|
+
},
|
|
938
|
+
p.message
|
|
939
|
+
))
|
|
940
|
+
] }),
|
|
941
|
+
/* @__PURE__ */ jsx6("td", { style: styles.td, children: m.describesAdmin ? /* @__PURE__ */ jsx6("span", { style: styles.badge, children: "describes admin" }) : /* @__PURE__ */ jsx6("span", { style: styles.muted, children: "\u2014" }) })
|
|
942
|
+
] }, m.name)) })
|
|
339
943
|
] }) : null
|
|
340
944
|
] });
|
|
341
945
|
}
|
|
342
946
|
|
|
343
947
|
// src/screens/RoutesScreen.tsx
|
|
344
948
|
import { useAdminRoutes } from "@fonderie/react-admin";
|
|
345
|
-
import {
|
|
949
|
+
import { useState } from "react";
|
|
950
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
346
951
|
function RoutesScreen({ client }) {
|
|
347
952
|
const { report, isLoading, error } = useAdminRoutes(client);
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
953
|
+
const [q, setQ] = useState("");
|
|
954
|
+
const needle = q.trim().toLowerCase();
|
|
955
|
+
const routes = (report?.routes ?? []).filter(
|
|
956
|
+
(r) => !needle || `${r.method} ${r.path} ${r.module ?? "application"} ${r.guard}`.toLowerCase().includes(needle)
|
|
957
|
+
);
|
|
958
|
+
return /* @__PURE__ */ jsxs7("div", { style: styles.container, children: [
|
|
959
|
+
/* @__PURE__ */ jsx7(
|
|
960
|
+
PageHeader,
|
|
961
|
+
{
|
|
962
|
+
title: "Routes",
|
|
963
|
+
lead: report ? `${report.routes.length} routes exposed by this deployment, with the guard in front of each.` : "Every exposed route, with its guard.",
|
|
964
|
+
actions: /* @__PURE__ */ jsx7(
|
|
965
|
+
"input",
|
|
966
|
+
{
|
|
967
|
+
type: "search",
|
|
968
|
+
value: q,
|
|
969
|
+
onChange: (e) => setQ(e.target.value),
|
|
970
|
+
placeholder: "Filter by path, method, module\u2026",
|
|
971
|
+
style: { ...styles.input, width: 280 },
|
|
972
|
+
"aria-label": "Filter routes"
|
|
973
|
+
}
|
|
974
|
+
)
|
|
975
|
+
}
|
|
976
|
+
),
|
|
977
|
+
isLoading ? /* @__PURE__ */ jsx7("p", { style: styles.status, children: "Loading\u2026" }) : error ? /* @__PURE__ */ jsx7("p", { style: styles.error, role: "alert", children: error.explanation }) : /* @__PURE__ */ jsxs7("table", { style: styles.table, children: [
|
|
978
|
+
/* @__PURE__ */ jsx7("thead", { children: /* @__PURE__ */ jsxs7("tr", { children: [
|
|
979
|
+
/* @__PURE__ */ jsx7("th", { style: { ...styles.th, width: 80 }, children: "Method" }),
|
|
980
|
+
/* @__PURE__ */ jsx7("th", { style: styles.th, children: "Path" }),
|
|
981
|
+
/* @__PURE__ */ jsx7("th", { style: styles.th, children: "Guard" }),
|
|
982
|
+
/* @__PURE__ */ jsx7("th", { style: styles.th, children: "Module" })
|
|
356
983
|
] }) }),
|
|
357
|
-
/* @__PURE__ */
|
|
358
|
-
/* @__PURE__ */
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
984
|
+
/* @__PURE__ */ jsxs7("tbody", { children: [
|
|
985
|
+
routes.map((r) => /* @__PURE__ */ jsxs7("tr", { children: [
|
|
986
|
+
/* @__PURE__ */ jsx7("td", { style: styles.td, children: /* @__PURE__ */ jsx7(MethodChip, { method: r.method }) }),
|
|
987
|
+
/* @__PURE__ */ jsx7("td", { style: { ...styles.td, ...styles.mono, wordBreak: "break-all" }, children: r.path }),
|
|
988
|
+
/* @__PURE__ */ jsx7("td", { style: styles.td, children: /* @__PURE__ */ jsx7(
|
|
989
|
+
Pill,
|
|
990
|
+
{
|
|
991
|
+
tone: r.guard === "admin" ? "ok" : r.guard === "probe" ? "neutral" : "info",
|
|
992
|
+
dot: false,
|
|
993
|
+
children: r.guard
|
|
994
|
+
}
|
|
995
|
+
) }),
|
|
996
|
+
/* @__PURE__ */ jsx7("td", { style: { ...styles.td, ...styles.muted }, children: r.module ?? "application" })
|
|
997
|
+
] }, `${r.method} ${r.path}`)),
|
|
998
|
+
routes.length === 0 ? /* @__PURE__ */ jsx7("tr", { children: /* @__PURE__ */ jsxs7("td", { style: { ...styles.td, ...styles.muted }, colSpan: 4, children: [
|
|
999
|
+
"No route matches \u201C",
|
|
1000
|
+
q,
|
|
1001
|
+
"\u201D."
|
|
1002
|
+
] }) }) : null
|
|
1003
|
+
] })
|
|
363
1004
|
] })
|
|
364
1005
|
] });
|
|
365
1006
|
}
|
|
366
1007
|
|
|
367
1008
|
// src/screens/MigrationsScreen.tsx
|
|
368
1009
|
import { useAdminMigrations } from "@fonderie/react-admin";
|
|
369
|
-
import { jsx as
|
|
1010
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
370
1011
|
function why(m, everApplied) {
|
|
371
1012
|
if (m.pending.length === 0) return null;
|
|
372
1013
|
if (m.blockedBy) return `Apply "${m.blockedBy}" first \u2014 it runs before this one and is behind.`;
|
|
@@ -377,36 +1018,40 @@ function why(m, everApplied) {
|
|
|
377
1018
|
function MigrationsScreen({ client }) {
|
|
378
1019
|
const { report, isLoading, error, refresh, apply } = useAdminMigrations(client);
|
|
379
1020
|
const behind = report?.modules.filter((m) => m.pending.length > 0) ?? [];
|
|
380
|
-
return /* @__PURE__ */
|
|
381
|
-
/* @__PURE__ */
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
1021
|
+
return /* @__PURE__ */ jsxs8("div", { style: styles.container, children: [
|
|
1022
|
+
/* @__PURE__ */ jsx8(
|
|
1023
|
+
PageHeader,
|
|
1024
|
+
{
|
|
1025
|
+
title: "Migrations",
|
|
1026
|
+
lead: "Schema changes each module ships, and whether this database has them.",
|
|
1027
|
+
actions: /* @__PURE__ */ jsx8(RefreshButton, { onClick: () => void refresh(), busy: isLoading })
|
|
1028
|
+
}
|
|
1029
|
+
),
|
|
1030
|
+
error ? /* @__PURE__ */ jsx8("p", { style: styles.error, role: "alert", children: error.status === 403 ? "Applying migrations needs a token with the write scope." : error.explanation }) : null,
|
|
1031
|
+
report && !report.everApplied ? /* @__PURE__ */ jsx8("p", { style: styles.notice, children: "This database has never been migrated \u2014 treating it as a first install, so nothing is held back." }) : null,
|
|
1032
|
+
report && behind.length === 0 ? /* @__PURE__ */ jsx8(Empty, { icon: "migrations", title: "Every module is up to date", children: "No pending migrations." }) : null,
|
|
389
1033
|
behind.map((m) => {
|
|
390
1034
|
const blocked = why(m, report?.everApplied ?? false);
|
|
391
1035
|
const files = m.pending.map((p) => p.file);
|
|
392
|
-
return /* @__PURE__ */
|
|
393
|
-
/* @__PURE__ */
|
|
394
|
-
m.name,
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
1036
|
+
return /* @__PURE__ */ jsxs8("section", { style: { ...styles.card, marginBottom: 16 }, children: [
|
|
1037
|
+
/* @__PURE__ */ jsxs8("h2", { style: { ...styles.subtitle, marginTop: 0 }, children: [
|
|
1038
|
+
/* @__PURE__ */ jsx8("span", { style: styles.mono, children: m.name }),
|
|
1039
|
+
/* @__PURE__ */ jsxs8(Pill, { tone: "warn", children: [
|
|
1040
|
+
m.pending.length,
|
|
1041
|
+
" pending"
|
|
1042
|
+
] })
|
|
398
1043
|
] }),
|
|
399
|
-
/* @__PURE__ */
|
|
400
|
-
/* @__PURE__ */
|
|
1044
|
+
/* @__PURE__ */ jsx8("ul", { style: { margin: "0 0 12px", paddingLeft: 18, lineHeight: 1.9 }, children: m.pending.map((p) => /* @__PURE__ */ jsxs8("li", { children: [
|
|
1045
|
+
/* @__PURE__ */ jsx8("code", { style: styles.code, children: p.file }),
|
|
401
1046
|
" ",
|
|
402
|
-
p.impact === "destructive" ? /* @__PURE__ */
|
|
403
|
-
p.destructive.length > 0 ? /* @__PURE__ */
|
|
1047
|
+
p.impact === "destructive" ? /* @__PURE__ */ jsx8(Pill, { tone: "bad", children: "destructive" }) : /* @__PURE__ */ jsx8(Pill, { tone: "neutral", children: "additive" }),
|
|
1048
|
+
p.destructive.length > 0 ? /* @__PURE__ */ jsx8("ul", { children: p.destructive.map((s) => /* @__PURE__ */ jsx8("li", { children: /* @__PURE__ */ jsx8("code", { children: s.slice(0, 120) }) }, s)) }) : null
|
|
404
1049
|
] }, p.file)) }),
|
|
405
|
-
blocked ? /* @__PURE__ */
|
|
1050
|
+
blocked ? /* @__PURE__ */ jsx8("p", { style: { ...styles.notice, marginBottom: 0 }, children: blocked }) : /* @__PURE__ */ jsxs8(
|
|
406
1051
|
"button",
|
|
407
1052
|
{
|
|
408
1053
|
type: "button",
|
|
409
|
-
style: styles.
|
|
1054
|
+
style: styles.buttonPrimary,
|
|
410
1055
|
disabled: isLoading || !m.appliable,
|
|
411
1056
|
onClick: () => {
|
|
412
1057
|
if (window.confirm(
|
|
@@ -428,37 +1073,306 @@ function MigrationsScreen({ client }) {
|
|
|
428
1073
|
] });
|
|
429
1074
|
}
|
|
430
1075
|
|
|
1076
|
+
// src/screens/OperatorsScreen.tsx
|
|
1077
|
+
import { useAdminOperators } from "@fonderie/react-admin";
|
|
1078
|
+
import { useState as useState2 } from "react";
|
|
1079
|
+
import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1080
|
+
var LEVELS = [
|
|
1081
|
+
{ label: "Read only", scopes: ["read"], hint: "look around, change nothing" },
|
|
1082
|
+
{ label: "Editor", scopes: ["read", "write"], hint: "change config, templates, users" },
|
|
1083
|
+
{
|
|
1084
|
+
label: "Owner",
|
|
1085
|
+
scopes: ["read", "write", "secrets"],
|
|
1086
|
+
hint: "secrets, tokens and operators too"
|
|
1087
|
+
}
|
|
1088
|
+
];
|
|
1089
|
+
var levelOf = (scopes) => scopes.includes("secrets") ? "Owner" : scopes.includes("write") ? "Editor" : "Read only";
|
|
1090
|
+
function Minted({ link, onClose }) {
|
|
1091
|
+
const [copied, setCopied] = useState2(false);
|
|
1092
|
+
const url = `${window.location.origin}${link.url}`;
|
|
1093
|
+
return /* @__PURE__ */ jsxs9(
|
|
1094
|
+
"div",
|
|
1095
|
+
{
|
|
1096
|
+
style: {
|
|
1097
|
+
...styles.notice,
|
|
1098
|
+
background: "color-mix(in srgb, var(--fonderie-accent,#00d294) 10%, transparent)",
|
|
1099
|
+
borderColor: "color-mix(in srgb, var(--fonderie-accent,#00d294) 35%, transparent)"
|
|
1100
|
+
},
|
|
1101
|
+
children: [
|
|
1102
|
+
/* @__PURE__ */ jsxs9("strong", { children: [
|
|
1103
|
+
"Send this link to ",
|
|
1104
|
+
link.email,
|
|
1105
|
+
". It is shown once."
|
|
1106
|
+
] }),
|
|
1107
|
+
" It works once and expires",
|
|
1108
|
+
" ",
|
|
1109
|
+
new Date(link.expiresAt).toLocaleString(),
|
|
1110
|
+
".",
|
|
1111
|
+
/* @__PURE__ */ jsxs9("div", { style: { display: "flex", gap: 8, marginTop: 8, alignItems: "center" }, children: [
|
|
1112
|
+
/* @__PURE__ */ jsx9("code", { style: { ...styles.code, flex: 1, padding: "7px 10px", wordBreak: "break-all" }, children: url }),
|
|
1113
|
+
/* @__PURE__ */ jsx9(
|
|
1114
|
+
"button",
|
|
1115
|
+
{
|
|
1116
|
+
type: "button",
|
|
1117
|
+
style: styles.button,
|
|
1118
|
+
onClick: () => {
|
|
1119
|
+
void navigator.clipboard?.writeText(url);
|
|
1120
|
+
setCopied(true);
|
|
1121
|
+
},
|
|
1122
|
+
children: copied ? "Copied" : "Copy"
|
|
1123
|
+
}
|
|
1124
|
+
),
|
|
1125
|
+
/* @__PURE__ */ jsx9("button", { type: "button", style: styles.buttonGhost, onClick: onClose, "aria-label": "Dismiss", children: /* @__PURE__ */ jsx9(Icon, { name: "close", size: 14 }) })
|
|
1126
|
+
] })
|
|
1127
|
+
]
|
|
1128
|
+
}
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
function OperatorsScreen({ client, me }) {
|
|
1132
|
+
const { report, isLoading, error, invite, recover, update, revokeLink } = useAdminOperators(client);
|
|
1133
|
+
const [email, setEmail] = useState2("");
|
|
1134
|
+
const [level, setLevel] = useState2("Editor");
|
|
1135
|
+
const [minted, setMinted] = useState2(null);
|
|
1136
|
+
const scopesFor = (label) => LEVELS.find((l) => l.label === label)?.scopes ?? ["read"];
|
|
1137
|
+
const status = (o) => o.disabledAt ? /* @__PURE__ */ jsx9(Pill, { tone: "neutral", children: "disabled" }) : o.locked ? /* @__PURE__ */ jsx9(Pill, { tone: "warn", children: "locked" }) : !o.enrolled ? /* @__PURE__ */ jsx9(Pill, { tone: "warn", children: "setting up" }) : /* @__PURE__ */ jsx9(Pill, { tone: "ok", children: "active" });
|
|
1138
|
+
return /* @__PURE__ */ jsxs9("div", { style: styles.container, children: [
|
|
1139
|
+
/* @__PURE__ */ jsx9(
|
|
1140
|
+
PageHeader,
|
|
1141
|
+
{
|
|
1142
|
+
title: "Operators",
|
|
1143
|
+
lead: "The people who can sign in to this console. Each one uses a password and an authenticator app; there is no sign-up."
|
|
1144
|
+
}
|
|
1145
|
+
),
|
|
1146
|
+
error ? /* @__PURE__ */ jsx9("p", { style: styles.error, role: "alert", children: error.status === 403 && error.reason === "FORBIDDEN" ? "Managing operators needs the Owner level." : error.explanation }) : null,
|
|
1147
|
+
minted ? /* @__PURE__ */ jsx9(Minted, { link: minted, onClose: () => setMinted(null) }) : null,
|
|
1148
|
+
/* @__PURE__ */ jsxs9(
|
|
1149
|
+
"form",
|
|
1150
|
+
{
|
|
1151
|
+
style: {
|
|
1152
|
+
...styles.card,
|
|
1153
|
+
display: "flex",
|
|
1154
|
+
gap: 8,
|
|
1155
|
+
alignItems: "center",
|
|
1156
|
+
flexWrap: "wrap",
|
|
1157
|
+
marginBottom: 20
|
|
1158
|
+
},
|
|
1159
|
+
onSubmit: (e) => {
|
|
1160
|
+
e.preventDefault();
|
|
1161
|
+
void invite({ email: email.trim(), scopes: scopesFor(level) }).then((l) => {
|
|
1162
|
+
setMinted(l);
|
|
1163
|
+
setEmail("");
|
|
1164
|
+
}).catch(() => {
|
|
1165
|
+
});
|
|
1166
|
+
},
|
|
1167
|
+
children: [
|
|
1168
|
+
/* @__PURE__ */ jsx9(Icon, { name: "users", size: 16 }),
|
|
1169
|
+
/* @__PURE__ */ jsx9("strong", { style: { fontSize: 13.5, marginRight: 4 }, children: "Invite" }),
|
|
1170
|
+
/* @__PURE__ */ jsx9(
|
|
1171
|
+
"input",
|
|
1172
|
+
{
|
|
1173
|
+
type: "email",
|
|
1174
|
+
required: true,
|
|
1175
|
+
value: email,
|
|
1176
|
+
onChange: (e) => setEmail(e.target.value),
|
|
1177
|
+
placeholder: "teammate@company.com",
|
|
1178
|
+
style: { ...styles.input, flex: 1, minWidth: 220 },
|
|
1179
|
+
"aria-label": "Email to invite"
|
|
1180
|
+
}
|
|
1181
|
+
),
|
|
1182
|
+
/* @__PURE__ */ jsx9(
|
|
1183
|
+
"select",
|
|
1184
|
+
{
|
|
1185
|
+
value: level,
|
|
1186
|
+
onChange: (e) => setLevel(e.target.value),
|
|
1187
|
+
style: styles.input,
|
|
1188
|
+
"aria-label": "Access level",
|
|
1189
|
+
children: LEVELS.map((l) => /* @__PURE__ */ jsxs9("option", { value: l.label, children: [
|
|
1190
|
+
l.label,
|
|
1191
|
+
" \u2014 ",
|
|
1192
|
+
l.hint
|
|
1193
|
+
] }, l.label))
|
|
1194
|
+
}
|
|
1195
|
+
),
|
|
1196
|
+
/* @__PURE__ */ jsxs9("button", { type: "submit", style: styles.buttonPrimary, disabled: !email.trim(), children: [
|
|
1197
|
+
/* @__PURE__ */ jsx9(Icon, { name: "plus", size: 14 }),
|
|
1198
|
+
"Create invite link"
|
|
1199
|
+
] })
|
|
1200
|
+
]
|
|
1201
|
+
}
|
|
1202
|
+
),
|
|
1203
|
+
isLoading && !report ? /* @__PURE__ */ jsx9("p", { style: styles.status, children: "Loading\u2026" }) : report && report.operators.length === 0 ? /* @__PURE__ */ jsx9(Empty, { icon: "users", title: "No operators yet" }) : report ? /* @__PURE__ */ jsxs9("table", { style: styles.table, children: [
|
|
1204
|
+
/* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsxs9("tr", { children: [
|
|
1205
|
+
/* @__PURE__ */ jsx9("th", { style: styles.th, children: "Operator" }),
|
|
1206
|
+
/* @__PURE__ */ jsx9("th", { style: styles.th, children: "Access" }),
|
|
1207
|
+
/* @__PURE__ */ jsx9("th", { style: styles.th, children: "Status" }),
|
|
1208
|
+
/* @__PURE__ */ jsx9("th", { style: styles.th, children: "Last sign-in" }),
|
|
1209
|
+
/* @__PURE__ */ jsx9("th", { style: styles.th })
|
|
1210
|
+
] }) }),
|
|
1211
|
+
/* @__PURE__ */ jsx9("tbody", { children: report.operators.map((o) => /* @__PURE__ */ jsxs9("tr", { children: [
|
|
1212
|
+
/* @__PURE__ */ jsxs9("td", { style: styles.td, children: [
|
|
1213
|
+
/* @__PURE__ */ jsx9("div", { style: { fontWeight: 600 }, children: o.name || o.email }),
|
|
1214
|
+
o.name ? /* @__PURE__ */ jsx9("div", { style: styles.muted, children: o.email }) : null
|
|
1215
|
+
] }),
|
|
1216
|
+
/* @__PURE__ */ jsx9("td", { style: styles.td, children: /* @__PURE__ */ jsx9(
|
|
1217
|
+
"select",
|
|
1218
|
+
{
|
|
1219
|
+
value: levelOf(o.scopes),
|
|
1220
|
+
onChange: (e) => void update(o.id, { scopes: scopesFor(e.target.value) }).catch(() => {
|
|
1221
|
+
}),
|
|
1222
|
+
style: { ...styles.input, height: 28, fontSize: 12.5 },
|
|
1223
|
+
disabled: o.email === me,
|
|
1224
|
+
"aria-label": `Access level for ${o.email}`,
|
|
1225
|
+
children: LEVELS.map((l) => /* @__PURE__ */ jsx9("option", { value: l.label, children: l.label }, l.label))
|
|
1226
|
+
}
|
|
1227
|
+
) }),
|
|
1228
|
+
/* @__PURE__ */ jsxs9("td", { style: styles.td, children: [
|
|
1229
|
+
status(o),
|
|
1230
|
+
o.enrolled && o.backupCodesLeft <= 2 && !o.disabledAt ? /* @__PURE__ */ jsxs9("div", { style: { ...styles.muted, marginTop: 4 }, children: [
|
|
1231
|
+
o.backupCodesLeft,
|
|
1232
|
+
" backup code(s) left"
|
|
1233
|
+
] }) : null
|
|
1234
|
+
] }),
|
|
1235
|
+
/* @__PURE__ */ jsx9("td", { style: { ...styles.td, ...styles.muted, whiteSpace: "nowrap" }, children: o.lastLoginAt ? new Date(o.lastLoginAt).toLocaleString() : "never" }),
|
|
1236
|
+
/* @__PURE__ */ jsx9("td", { style: { ...styles.td, textAlign: "right", whiteSpace: "nowrap" }, children: o.email === me ? /* @__PURE__ */ jsx9("span", { style: styles.muted, children: "you" }) : /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
1237
|
+
/* @__PURE__ */ jsx9(
|
|
1238
|
+
"button",
|
|
1239
|
+
{
|
|
1240
|
+
type: "button",
|
|
1241
|
+
style: { ...styles.button, height: 28, marginRight: 6 },
|
|
1242
|
+
onClick: () => {
|
|
1243
|
+
if (window.confirm(
|
|
1244
|
+
`Create a recovery link for ${o.email}? It signs them out everywhere; the link sets a new password and a new authenticator.`
|
|
1245
|
+
))
|
|
1246
|
+
void recover(o.id).then(setMinted).catch(() => {
|
|
1247
|
+
});
|
|
1248
|
+
},
|
|
1249
|
+
children: "Recovery link"
|
|
1250
|
+
}
|
|
1251
|
+
),
|
|
1252
|
+
/* @__PURE__ */ jsx9(
|
|
1253
|
+
"button",
|
|
1254
|
+
{
|
|
1255
|
+
type: "button",
|
|
1256
|
+
style: {
|
|
1257
|
+
...o.disabledAt ? styles.button : styles.buttonDanger,
|
|
1258
|
+
height: 28
|
|
1259
|
+
},
|
|
1260
|
+
onClick: () => {
|
|
1261
|
+
if (o.disabledAt || window.confirm(`Disable ${o.email}? They are signed out immediately.`))
|
|
1262
|
+
void update(o.id, { disabled: !o.disabledAt }).catch(() => {
|
|
1263
|
+
});
|
|
1264
|
+
},
|
|
1265
|
+
children: o.disabledAt ? "Enable" : "Disable"
|
|
1266
|
+
}
|
|
1267
|
+
)
|
|
1268
|
+
] }) })
|
|
1269
|
+
] }, o.id)) })
|
|
1270
|
+
] }) : null,
|
|
1271
|
+
report && report.links.length > 0 ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
1272
|
+
/* @__PURE__ */ jsx9("h2", { style: styles.subtitle, children: "Pending links" }),
|
|
1273
|
+
/* @__PURE__ */ jsx9("ul", { style: styles.list, children: report.links.map((l) => /* @__PURE__ */ jsxs9(
|
|
1274
|
+
"li",
|
|
1275
|
+
{
|
|
1276
|
+
style: { ...styles.row, display: "flex", gap: 12, alignItems: "center" },
|
|
1277
|
+
children: [
|
|
1278
|
+
/* @__PURE__ */ jsx9(Pill, { tone: l.kind === "invite" ? "info" : "warn", dot: false, children: l.kind }),
|
|
1279
|
+
/* @__PURE__ */ jsxs9("span", { style: { flex: 1 }, children: [
|
|
1280
|
+
l.email,
|
|
1281
|
+
l.kind === "invite" ? /* @__PURE__ */ jsxs9("span", { style: styles.muted, children: [
|
|
1282
|
+
" \xB7 ",
|
|
1283
|
+
levelOf(l.scopes)
|
|
1284
|
+
] }) : null
|
|
1285
|
+
] }),
|
|
1286
|
+
/* @__PURE__ */ jsxs9("span", { style: styles.muted, children: [
|
|
1287
|
+
"expires ",
|
|
1288
|
+
new Date(l.expiresAt).toLocaleString()
|
|
1289
|
+
] }),
|
|
1290
|
+
/* @__PURE__ */ jsx9(
|
|
1291
|
+
"button",
|
|
1292
|
+
{
|
|
1293
|
+
type: "button",
|
|
1294
|
+
style: { ...styles.buttonDanger, height: 28 },
|
|
1295
|
+
onClick: () => void revokeLink(l.id).catch(() => {
|
|
1296
|
+
}),
|
|
1297
|
+
children: "Revoke"
|
|
1298
|
+
}
|
|
1299
|
+
)
|
|
1300
|
+
]
|
|
1301
|
+
},
|
|
1302
|
+
l.id
|
|
1303
|
+
)) })
|
|
1304
|
+
] }) : null
|
|
1305
|
+
] });
|
|
1306
|
+
}
|
|
1307
|
+
|
|
431
1308
|
// src/screens/TokensScreen.tsx
|
|
432
1309
|
import { useAdminTokens } from "@fonderie/react-admin";
|
|
433
|
-
import { useState } from "react";
|
|
434
|
-
import { Fragment as
|
|
1310
|
+
import { useState as useState3 } from "react";
|
|
1311
|
+
import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
435
1312
|
var ALL = ["read", "write", "secrets"];
|
|
436
1313
|
function TokensScreen({ client }) {
|
|
437
1314
|
const { report, isLoading, error, issue, revoke } = useAdminTokens(client);
|
|
438
|
-
const [name, setName] =
|
|
439
|
-
const [scopes, setScopes] =
|
|
440
|
-
const [days, setDays] =
|
|
441
|
-
const [minted, setMinted] =
|
|
1315
|
+
const [name, setName] = useState3("");
|
|
1316
|
+
const [scopes, setScopes] = useState3(["read"]);
|
|
1317
|
+
const [days, setDays] = useState3("");
|
|
1318
|
+
const [minted, setMinted] = useState3(null);
|
|
442
1319
|
const toggle = (s) => setScopes((prev) => prev.includes(s) ? prev.filter((x) => x !== s) : [...prev, s]);
|
|
443
|
-
return /* @__PURE__ */
|
|
444
|
-
/* @__PURE__ */
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
1320
|
+
return /* @__PURE__ */ jsxs10("div", { style: styles.container, children: [
|
|
1321
|
+
/* @__PURE__ */ jsx10(
|
|
1322
|
+
PageHeader,
|
|
1323
|
+
{
|
|
1324
|
+
title: "Tokens",
|
|
1325
|
+
lead: "Credentials for machines: the root token's strength, scoped tokens for the CLI and CI, and any legacy per-module tokens. People sign in as operators."
|
|
1326
|
+
}
|
|
1327
|
+
),
|
|
1328
|
+
error ? /* @__PURE__ */ jsx10("p", { style: styles.error, role: "alert", children: error.status === 401 ? "Issuing and revoking need the root token (the one in your deployment config)." : error.explanation }) : null,
|
|
1329
|
+
/* @__PURE__ */ jsx10("h2", { style: { ...styles.subtitle, marginTop: 0 }, children: "Root token" }),
|
|
1330
|
+
report ? /* @__PURE__ */ jsxs10("div", { style: { ...styles.card, display: "flex", gap: 12, alignItems: "flex-start" }, children: [
|
|
1331
|
+
/* @__PURE__ */ jsx10(Icon, { name: "lock", size: 18, style: { marginTop: 2 } }),
|
|
1332
|
+
/* @__PURE__ */ jsxs10("div", { children: [
|
|
1333
|
+
/* @__PURE__ */ jsx10(Pill, { tone: report.admin.ok ? "ok" : "bad", children: report.admin.ok ? "strong" : "weak" }),
|
|
1334
|
+
report.admin.problems.map((p) => /* @__PURE__ */ jsx10(
|
|
1335
|
+
"div",
|
|
1336
|
+
{
|
|
1337
|
+
style: { marginTop: 6, color: "var(--fonderie-danger,#e00)", fontSize: 13 },
|
|
1338
|
+
children: p.message
|
|
1339
|
+
},
|
|
1340
|
+
p.message
|
|
1341
|
+
))
|
|
1342
|
+
] })
|
|
450
1343
|
] }) : null,
|
|
451
|
-
/* @__PURE__ */
|
|
452
|
-
report?.issued === null ? /* @__PURE__ */
|
|
453
|
-
minted ? /* @__PURE__ */
|
|
454
|
-
"
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
1344
|
+
/* @__PURE__ */ jsx10("h2", { style: styles.subtitle, children: "Issued tokens" }),
|
|
1345
|
+
report?.issued === null ? /* @__PURE__ */ jsx10("p", { style: styles.muted, children: "Issuing is off \u2014 give AdminModule a store and run its migrations." }) : /* @__PURE__ */ jsxs10(Fragment5, { children: [
|
|
1346
|
+
minted ? /* @__PURE__ */ jsxs10(
|
|
1347
|
+
"div",
|
|
1348
|
+
{
|
|
1349
|
+
style: {
|
|
1350
|
+
...styles.notice,
|
|
1351
|
+
background: "color-mix(in srgb, var(--fonderie-accent,#00d294) 10%, transparent)",
|
|
1352
|
+
borderColor: "color-mix(in srgb, var(--fonderie-accent,#00d294) 35%, transparent)"
|
|
1353
|
+
},
|
|
1354
|
+
children: [
|
|
1355
|
+
/* @__PURE__ */ jsx10("strong", { children: "Copy this now \u2014 it is never shown again." }),
|
|
1356
|
+
" (",
|
|
1357
|
+
minted.name,
|
|
1358
|
+
")",
|
|
1359
|
+
/* @__PURE__ */ jsx10(
|
|
1360
|
+
"div",
|
|
1361
|
+
{
|
|
1362
|
+
style: {
|
|
1363
|
+
...styles.code,
|
|
1364
|
+
display: "block",
|
|
1365
|
+
marginTop: 8,
|
|
1366
|
+
padding: "8px 10px",
|
|
1367
|
+
wordBreak: "break-all"
|
|
1368
|
+
},
|
|
1369
|
+
children: minted.token
|
|
1370
|
+
}
|
|
1371
|
+
)
|
|
1372
|
+
]
|
|
1373
|
+
}
|
|
1374
|
+
) : null,
|
|
1375
|
+
/* @__PURE__ */ jsxs10(
|
|
462
1376
|
"form",
|
|
463
1377
|
{
|
|
464
1378
|
style: styles.toolbar,
|
|
@@ -477,70 +1391,90 @@ function TokensScreen({ client }) {
|
|
|
477
1391
|
});
|
|
478
1392
|
},
|
|
479
1393
|
children: [
|
|
480
|
-
/* @__PURE__ */
|
|
1394
|
+
/* @__PURE__ */ jsx10(
|
|
481
1395
|
"input",
|
|
482
1396
|
{
|
|
483
1397
|
value: name,
|
|
484
1398
|
onChange: (e) => setName(e.target.value),
|
|
485
1399
|
placeholder: "name",
|
|
486
|
-
style:
|
|
1400
|
+
style: styles.input,
|
|
487
1401
|
"aria-label": "Name"
|
|
488
1402
|
}
|
|
489
1403
|
),
|
|
490
|
-
ALL.map((s) => /* @__PURE__ */
|
|
491
|
-
|
|
492
|
-
|
|
1404
|
+
ALL.map((s) => /* @__PURE__ */ jsxs10(
|
|
1405
|
+
"label",
|
|
1406
|
+
{
|
|
1407
|
+
style: {
|
|
1408
|
+
...styles.badge,
|
|
1409
|
+
cursor: "pointer",
|
|
1410
|
+
height: 32,
|
|
1411
|
+
boxSizing: "border-box",
|
|
1412
|
+
padding: "0 10px",
|
|
1413
|
+
fontSize: 13
|
|
1414
|
+
},
|
|
1415
|
+
children: [
|
|
1416
|
+
/* @__PURE__ */ jsx10("input", { type: "checkbox", checked: scopes.includes(s), onChange: () => toggle(s) }),
|
|
1417
|
+
" ",
|
|
1418
|
+
s
|
|
1419
|
+
]
|
|
1420
|
+
},
|
|
493
1421
|
s
|
|
494
|
-
|
|
495
|
-
/* @__PURE__ */
|
|
1422
|
+
)),
|
|
1423
|
+
/* @__PURE__ */ jsx10(
|
|
496
1424
|
"input",
|
|
497
1425
|
{
|
|
498
1426
|
value: days,
|
|
499
1427
|
onChange: (e) => setDays(e.target.value),
|
|
500
1428
|
placeholder: "days (optional)",
|
|
501
|
-
style: { ...styles.
|
|
1429
|
+
style: { ...styles.input, width: 140 },
|
|
502
1430
|
"aria-label": "Days"
|
|
503
1431
|
}
|
|
504
1432
|
),
|
|
505
|
-
/* @__PURE__ */
|
|
1433
|
+
/* @__PURE__ */ jsxs10(
|
|
506
1434
|
"button",
|
|
507
1435
|
{
|
|
508
1436
|
type: "submit",
|
|
509
|
-
style: styles.
|
|
1437
|
+
style: styles.buttonPrimary,
|
|
510
1438
|
disabled: !name.trim() || scopes.length === 0 || isLoading,
|
|
511
|
-
children:
|
|
1439
|
+
children: [
|
|
1440
|
+
/* @__PURE__ */ jsx10(Icon, { name: "plus", size: 14 }),
|
|
1441
|
+
"Issue token"
|
|
1442
|
+
]
|
|
512
1443
|
}
|
|
513
1444
|
)
|
|
514
1445
|
]
|
|
515
1446
|
}
|
|
516
1447
|
),
|
|
517
|
-
report?.issued && report.issued.length > 0 ? /* @__PURE__ */
|
|
518
|
-
/* @__PURE__ */
|
|
519
|
-
/* @__PURE__ */
|
|
520
|
-
/* @__PURE__ */
|
|
521
|
-
/* @__PURE__ */
|
|
522
|
-
/* @__PURE__ */
|
|
523
|
-
/* @__PURE__ */
|
|
524
|
-
/* @__PURE__ */
|
|
1448
|
+
report?.issued && report.issued.length > 0 ? /* @__PURE__ */ jsxs10("table", { style: styles.table, children: [
|
|
1449
|
+
/* @__PURE__ */ jsx10("thead", { children: /* @__PURE__ */ jsxs10("tr", { children: [
|
|
1450
|
+
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Name" }),
|
|
1451
|
+
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Scopes" }),
|
|
1452
|
+
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Created" }),
|
|
1453
|
+
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Expires" }),
|
|
1454
|
+
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Last used" }),
|
|
1455
|
+
/* @__PURE__ */ jsx10("th", { style: styles.th })
|
|
525
1456
|
] }) }),
|
|
526
|
-
/* @__PURE__ */
|
|
527
|
-
/* @__PURE__ */
|
|
1457
|
+
/* @__PURE__ */ jsx10("tbody", { children: report.issued.map((t2) => /* @__PURE__ */ jsxs10("tr", { children: [
|
|
1458
|
+
/* @__PURE__ */ jsxs10("td", { style: styles.td, children: [
|
|
528
1459
|
t2.name,
|
|
529
|
-
t2.revokedAt ? /* @__PURE__ */
|
|
1460
|
+
t2.revokedAt ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
|
|
1461
|
+
" ",
|
|
1462
|
+
/* @__PURE__ */ jsx10(Pill, { tone: "neutral", children: "revoked" })
|
|
1463
|
+
] }) : null
|
|
530
1464
|
] }),
|
|
531
|
-
/* @__PURE__ */
|
|
532
|
-
/* @__PURE__ */
|
|
1465
|
+
/* @__PURE__ */ jsx10("td", { style: { ...styles.td, ...styles.mono }, children: t2.scopes.join(", ") }),
|
|
1466
|
+
/* @__PURE__ */ jsxs10("td", { style: { ...styles.td, ...styles.muted }, children: [
|
|
533
1467
|
new Date(t2.createdAt).toLocaleDateString(),
|
|
534
1468
|
" \xB7 ",
|
|
535
1469
|
t2.createdBy
|
|
536
1470
|
] }),
|
|
537
|
-
/* @__PURE__ */
|
|
538
|
-
/* @__PURE__ */
|
|
539
|
-
/* @__PURE__ */
|
|
1471
|
+
/* @__PURE__ */ jsx10("td", { style: { ...styles.td, ...styles.muted }, children: t2.expiresAt ? new Date(t2.expiresAt).toLocaleDateString() : "never" }),
|
|
1472
|
+
/* @__PURE__ */ jsx10("td", { style: { ...styles.td, ...styles.muted }, children: t2.lastUsedAt ? new Date(t2.lastUsedAt).toLocaleString() : "never" }),
|
|
1473
|
+
/* @__PURE__ */ jsx10("td", { style: styles.td, children: t2.revokedAt ? null : /* @__PURE__ */ jsx10(
|
|
540
1474
|
"button",
|
|
541
1475
|
{
|
|
542
1476
|
type: "button",
|
|
543
|
-
style: styles.
|
|
1477
|
+
style: styles.buttonDanger,
|
|
544
1478
|
onClick: () => {
|
|
545
1479
|
if (window.confirm(
|
|
546
1480
|
`Revoke "${t2.name}"? Anything using it stops working immediately.`
|
|
@@ -552,13 +1486,13 @@ function TokensScreen({ client }) {
|
|
|
552
1486
|
}
|
|
553
1487
|
) })
|
|
554
1488
|
] }, t2.id)) })
|
|
555
|
-
] }) : /* @__PURE__ */
|
|
1489
|
+
] }) : /* @__PURE__ */ jsx10(Empty, { icon: "tokens", title: "No scoped tokens yet", children: "Issue a read-only token for a dashboard or a teammate instead of sharing the root." })
|
|
556
1490
|
] }),
|
|
557
|
-
/* @__PURE__ */
|
|
558
|
-
report && report.legacy.length === 0 ? /* @__PURE__ */
|
|
559
|
-
/* @__PURE__ */
|
|
1491
|
+
/* @__PURE__ */ jsx10("h2", { style: styles.subtitle, children: "Legacy per-brick tokens" }),
|
|
1492
|
+
report && report.legacy.length === 0 ? /* @__PURE__ */ jsx10("p", { style: styles.muted, children: "None \u2014 every brick's admin surface goes through this token." }) : /* @__PURE__ */ jsx10("ul", { style: styles.list, children: report?.legacy.map((l) => /* @__PURE__ */ jsxs10("li", { style: styles.row, children: [
|
|
1493
|
+
/* @__PURE__ */ jsx10("span", { style: styles.mono, children: l.module }),
|
|
560
1494
|
" ",
|
|
561
|
-
/* @__PURE__ */
|
|
1495
|
+
/* @__PURE__ */ jsx10("span", { style: styles.muted, children: "still registers its standalone routes with its own token (deprecated)" })
|
|
562
1496
|
] }, l.module)) })
|
|
563
1497
|
] });
|
|
564
1498
|
}
|
|
@@ -571,11 +1505,11 @@ import {
|
|
|
571
1505
|
useAdminUserSessions,
|
|
572
1506
|
useAdminUsers
|
|
573
1507
|
} from "@fonderie/react-admin";
|
|
574
|
-
import { useState as
|
|
575
|
-
import { Fragment as
|
|
1508
|
+
import { useState as useState4 } from "react";
|
|
1509
|
+
import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
576
1510
|
function UsersScreen({ client, pageSize = 50 }) {
|
|
577
|
-
const [input, setInput] =
|
|
578
|
-
const [selected, setSelected] =
|
|
1511
|
+
const [input, setInput] = useState4("");
|
|
1512
|
+
const [selected, setSelected] = useState4(null);
|
|
579
1513
|
const list = useAdminUsers(client, { limit: pageSize });
|
|
580
1514
|
const { user, isLoading, error, suspend, unsuspend, revokeSessions } = useAdminUser(
|
|
581
1515
|
client,
|
|
@@ -584,14 +1518,20 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
584
1518
|
const userId = user?.id ?? null;
|
|
585
1519
|
const sessions = useAdminUserSessions(client, userId);
|
|
586
1520
|
const history = useAdminLoginHistory(client, userId, { limit: 20 });
|
|
587
|
-
const yesNo = (v) => v ? /* @__PURE__ */
|
|
1521
|
+
const yesNo = (v) => v ? /* @__PURE__ */ jsx11(Pill, { tone: "ok", children: "yes" }) : /* @__PURE__ */ jsx11(Pill, { tone: "neutral", children: "no" });
|
|
588
1522
|
const clear = () => {
|
|
589
1523
|
setSelected(null);
|
|
590
1524
|
setInput("");
|
|
591
1525
|
};
|
|
592
|
-
return /* @__PURE__ */
|
|
593
|
-
/* @__PURE__ */
|
|
594
|
-
|
|
1526
|
+
return /* @__PURE__ */ jsxs11("div", { style: styles.container, children: [
|
|
1527
|
+
/* @__PURE__ */ jsx11(
|
|
1528
|
+
PageHeader,
|
|
1529
|
+
{
|
|
1530
|
+
title: "Users",
|
|
1531
|
+
lead: "Everyone signed up. Open an account for its sessions, sign-ins and the suspend and sign-out controls."
|
|
1532
|
+
}
|
|
1533
|
+
),
|
|
1534
|
+
/* @__PURE__ */ jsxs11(
|
|
595
1535
|
"form",
|
|
596
1536
|
{
|
|
597
1537
|
style: styles.toolbar,
|
|
@@ -601,19 +1541,25 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
601
1541
|
setSelected(email ? { email } : null);
|
|
602
1542
|
},
|
|
603
1543
|
children: [
|
|
604
|
-
/* @__PURE__ */
|
|
1544
|
+
/* @__PURE__ */ jsx11(
|
|
605
1545
|
"input",
|
|
606
1546
|
{
|
|
607
1547
|
type: "email",
|
|
608
1548
|
value: input,
|
|
609
1549
|
onChange: (e) => setInput(e.target.value),
|
|
610
1550
|
placeholder: "email address",
|
|
611
|
-
style: { ...styles.
|
|
1551
|
+
style: { ...styles.input, minWidth: 280 },
|
|
612
1552
|
"aria-label": "Email"
|
|
613
1553
|
}
|
|
614
1554
|
),
|
|
615
|
-
/* @__PURE__ */
|
|
616
|
-
|
|
1555
|
+
/* @__PURE__ */ jsxs11("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
|
|
1556
|
+
/* @__PURE__ */ jsx11(Icon, { name: "search", size: 14 }),
|
|
1557
|
+
"Look up"
|
|
1558
|
+
] }),
|
|
1559
|
+
selected ? /* @__PURE__ */ jsxs11("button", { type: "button", style: styles.buttonGhost, onClick: clear, children: [
|
|
1560
|
+
/* @__PURE__ */ jsx11(Icon, { name: "back", size: 14 }),
|
|
1561
|
+
"All users"
|
|
1562
|
+
] }) : /* @__PURE__ */ jsx11(
|
|
617
1563
|
"button",
|
|
618
1564
|
{
|
|
619
1565
|
type: "button",
|
|
@@ -626,37 +1572,37 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
626
1572
|
]
|
|
627
1573
|
}
|
|
628
1574
|
),
|
|
629
|
-
error ? /* @__PURE__ */
|
|
630
|
-
!selected ? /* @__PURE__ */
|
|
631
|
-
list.error ? /* @__PURE__ */
|
|
632
|
-
list.users.length === 0 && !list.isLoading ? /* @__PURE__ */
|
|
633
|
-
/* @__PURE__ */
|
|
634
|
-
/* @__PURE__ */
|
|
635
|
-
/* @__PURE__ */
|
|
636
|
-
/* @__PURE__ */
|
|
637
|
-
/* @__PURE__ */
|
|
1575
|
+
error ? /* @__PURE__ */ jsx11("p", { style: styles.error, role: "alert", children: error.status === 404 ? "No user with that email." : error.explanation }) : null,
|
|
1576
|
+
!selected ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1577
|
+
list.error ? /* @__PURE__ */ jsx11("p", { style: styles.error, role: "alert", children: list.error.explanation }) : null,
|
|
1578
|
+
list.users.length === 0 && !list.isLoading ? /* @__PURE__ */ jsx11(Empty, { icon: "users", title: "No users yet", children: "Sign-ups appear here as they happen." }) : /* @__PURE__ */ jsxs11("table", { style: styles.table, children: [
|
|
1579
|
+
/* @__PURE__ */ jsx11("thead", { children: /* @__PURE__ */ jsxs11("tr", { children: [
|
|
1580
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Email" }),
|
|
1581
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Name" }),
|
|
1582
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Created" }),
|
|
1583
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Status" })
|
|
638
1584
|
] }) }),
|
|
639
|
-
/* @__PURE__ */
|
|
640
|
-
/* @__PURE__ */
|
|
1585
|
+
/* @__PURE__ */ jsx11("tbody", { children: list.users.map((u) => /* @__PURE__ */ jsxs11("tr", { children: [
|
|
1586
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: /* @__PURE__ */ jsx11(
|
|
641
1587
|
"button",
|
|
642
1588
|
{
|
|
643
1589
|
type: "button",
|
|
644
|
-
style:
|
|
1590
|
+
style: styles.link,
|
|
645
1591
|
onClick: () => setSelected({ id: u.id }),
|
|
646
1592
|
children: u.email
|
|
647
1593
|
}
|
|
648
1594
|
) }),
|
|
649
|
-
/* @__PURE__ */
|
|
650
|
-
/* @__PURE__ */
|
|
651
|
-
/* @__PURE__ */
|
|
652
|
-
u.suspended ? /* @__PURE__ */
|
|
653
|
-
u.deletedAt ? /* @__PURE__ */
|
|
654
|
-
!u.suspended && !u.deletedAt ? /* @__PURE__ */
|
|
1595
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: `${u.firstName ?? ""} ${u.lastName ?? ""}`.trim() || /* @__PURE__ */ jsx11("span", { style: styles.muted, children: "\u2014" }) }),
|
|
1596
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: new Date(u.createdAt).toLocaleDateString() }),
|
|
1597
|
+
/* @__PURE__ */ jsxs11("td", { style: styles.td, children: [
|
|
1598
|
+
u.suspended ? /* @__PURE__ */ jsx11(Pill, { tone: "warn", children: "suspended" }) : null,
|
|
1599
|
+
u.deletedAt ? /* @__PURE__ */ jsx11(Pill, { tone: "neutral", children: "deleted" }) : null,
|
|
1600
|
+
!u.suspended && !u.deletedAt ? /* @__PURE__ */ jsx11(Pill, { tone: "ok", children: "active" }) : null
|
|
655
1601
|
] })
|
|
656
1602
|
] }, u.id)) })
|
|
657
1603
|
] }),
|
|
658
|
-
list.isLoading ? /* @__PURE__ */
|
|
659
|
-
list.hasMore && !list.isLoading ? /* @__PURE__ */
|
|
1604
|
+
list.isLoading ? /* @__PURE__ */ jsx11("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
1605
|
+
list.hasMore && !list.isLoading ? /* @__PURE__ */ jsx11(
|
|
660
1606
|
"button",
|
|
661
1607
|
{
|
|
662
1608
|
type: "button",
|
|
@@ -666,48 +1612,48 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
666
1612
|
}
|
|
667
1613
|
) : null
|
|
668
1614
|
] }) : null,
|
|
669
|
-
user ? /* @__PURE__ */
|
|
670
|
-
/* @__PURE__ */
|
|
1615
|
+
user ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1616
|
+
/* @__PURE__ */ jsxs11("h2", { style: styles.subtitle, children: [
|
|
671
1617
|
user.firstName || user.lastName ? `${user.firstName} ${user.lastName}`.trim() : user.email,
|
|
672
1618
|
" ",
|
|
673
|
-
user.suspended ? /* @__PURE__ */
|
|
674
|
-
user.deletedAt ? /* @__PURE__ */
|
|
1619
|
+
user.suspended ? /* @__PURE__ */ jsx11(Pill, { tone: "warn", children: "suspended" }) : null,
|
|
1620
|
+
user.deletedAt ? /* @__PURE__ */ jsx11(Pill, { tone: "neutral", children: "deleted" }) : null
|
|
675
1621
|
] }),
|
|
676
|
-
/* @__PURE__ */
|
|
677
|
-
/* @__PURE__ */
|
|
678
|
-
/* @__PURE__ */
|
|
679
|
-
/* @__PURE__ */
|
|
1622
|
+
/* @__PURE__ */ jsx11("table", { style: styles.table, children: /* @__PURE__ */ jsxs11("tbody", { children: [
|
|
1623
|
+
/* @__PURE__ */ jsxs11("tr", { children: [
|
|
1624
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: "id" }),
|
|
1625
|
+
/* @__PURE__ */ jsx11("td", { style: { ...styles.td, ...styles.mono }, children: user.id })
|
|
680
1626
|
] }),
|
|
681
|
-
/* @__PURE__ */
|
|
682
|
-
/* @__PURE__ */
|
|
683
|
-
/* @__PURE__ */
|
|
1627
|
+
/* @__PURE__ */ jsxs11("tr", { children: [
|
|
1628
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: "email" }),
|
|
1629
|
+
/* @__PURE__ */ jsxs11("td", { style: styles.td, children: [
|
|
684
1630
|
user.email,
|
|
685
1631
|
" \xB7 verified ",
|
|
686
1632
|
yesNo(user.isEmailVerified)
|
|
687
1633
|
] })
|
|
688
1634
|
] }),
|
|
689
|
-
/* @__PURE__ */
|
|
690
|
-
/* @__PURE__ */
|
|
691
|
-
/* @__PURE__ */
|
|
1635
|
+
/* @__PURE__ */ jsxs11("tr", { children: [
|
|
1636
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: "MFA" }),
|
|
1637
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: yesNo(user.mfaEnabled) })
|
|
692
1638
|
] }),
|
|
693
|
-
/* @__PURE__ */
|
|
694
|
-
/* @__PURE__ */
|
|
695
|
-
/* @__PURE__ */
|
|
1639
|
+
/* @__PURE__ */ jsxs11("tr", { children: [
|
|
1640
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: "provider" }),
|
|
1641
|
+
/* @__PURE__ */ jsxs11("td", { style: styles.td, children: [
|
|
696
1642
|
user.provider || "password",
|
|
697
1643
|
user.hasPassword ? "" : " \xB7 no password set"
|
|
698
1644
|
] })
|
|
699
1645
|
] }),
|
|
700
|
-
/* @__PURE__ */
|
|
701
|
-
/* @__PURE__ */
|
|
702
|
-
/* @__PURE__ */
|
|
1646
|
+
/* @__PURE__ */ jsxs11("tr", { children: [
|
|
1647
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: "last login" }),
|
|
1648
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: user.lastLogin ? new Date(user.lastLogin).toLocaleString() : /* @__PURE__ */ jsx11("span", { style: styles.muted, children: "never" }) })
|
|
703
1649
|
] }),
|
|
704
|
-
/* @__PURE__ */
|
|
705
|
-
/* @__PURE__ */
|
|
706
|
-
/* @__PURE__ */
|
|
1650
|
+
/* @__PURE__ */ jsxs11("tr", { children: [
|
|
1651
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: "created" }),
|
|
1652
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: new Date(user.createdAt).toLocaleString() })
|
|
707
1653
|
] })
|
|
708
1654
|
] }) }),
|
|
709
|
-
/* @__PURE__ */
|
|
710
|
-
user.suspended ? /* @__PURE__ */
|
|
1655
|
+
/* @__PURE__ */ jsxs11("div", { style: { ...styles.toolbar, marginTop: 12 }, children: [
|
|
1656
|
+
user.suspended ? /* @__PURE__ */ jsx11(
|
|
711
1657
|
"button",
|
|
712
1658
|
{
|
|
713
1659
|
type: "button",
|
|
@@ -716,17 +1662,17 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
716
1662
|
disabled: isLoading,
|
|
717
1663
|
children: "Unsuspend"
|
|
718
1664
|
}
|
|
719
|
-
) : /* @__PURE__ */
|
|
1665
|
+
) : /* @__PURE__ */ jsx11(
|
|
720
1666
|
"button",
|
|
721
1667
|
{
|
|
722
1668
|
type: "button",
|
|
723
|
-
style: styles.
|
|
1669
|
+
style: styles.buttonDanger,
|
|
724
1670
|
onClick: () => void suspend(),
|
|
725
1671
|
disabled: isLoading,
|
|
726
1672
|
children: "Suspend"
|
|
727
1673
|
}
|
|
728
1674
|
),
|
|
729
|
-
/* @__PURE__ */
|
|
1675
|
+
/* @__PURE__ */ jsx11(
|
|
730
1676
|
"button",
|
|
731
1677
|
{
|
|
732
1678
|
type: "button",
|
|
@@ -737,38 +1683,38 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
737
1683
|
}
|
|
738
1684
|
)
|
|
739
1685
|
] }),
|
|
740
|
-
/* @__PURE__ */
|
|
741
|
-
sessions.sessions.length === 0 ? /* @__PURE__ */
|
|
742
|
-
/* @__PURE__ */
|
|
1686
|
+
/* @__PURE__ */ jsx11("h2", { style: styles.subtitle, children: "Live sessions" }),
|
|
1687
|
+
sessions.sessions.length === 0 ? /* @__PURE__ */ jsx11("p", { style: styles.muted, children: "None." }) : /* @__PURE__ */ jsx11("ul", { style: styles.list, children: sessions.sessions.map((s) => /* @__PURE__ */ jsxs11("li", { style: styles.row, children: [
|
|
1688
|
+
/* @__PURE__ */ jsx11("span", { style: styles.mono, children: s.ipAddress ?? "\u2014" }),
|
|
743
1689
|
" ",
|
|
744
|
-
describeLocation(s.location) ? /* @__PURE__ */
|
|
1690
|
+
describeLocation(s.location) ? /* @__PURE__ */ jsxs11("span", { style: styles.muted, children: [
|
|
745
1691
|
describeLocation(s.location),
|
|
746
1692
|
" "
|
|
747
1693
|
] }) : null,
|
|
748
|
-
/* @__PURE__ */
|
|
1694
|
+
/* @__PURE__ */ jsx11("span", { style: styles.muted, children: s.userAgent ?? "" }),
|
|
749
1695
|
" ",
|
|
750
|
-
/* @__PURE__ */
|
|
1696
|
+
/* @__PURE__ */ jsxs11("span", { style: styles.muted, children: [
|
|
751
1697
|
"since ",
|
|
752
1698
|
new Date(s.createdAt).toLocaleString()
|
|
753
1699
|
] })
|
|
754
1700
|
] }, s.id)) }),
|
|
755
|
-
/* @__PURE__ */
|
|
756
|
-
history.events.length === 0 && !history.isLoading ? /* @__PURE__ */
|
|
757
|
-
/* @__PURE__ */
|
|
1701
|
+
/* @__PURE__ */ jsx11("h2", { style: styles.subtitle, children: "Recent sign-ins" }),
|
|
1702
|
+
history.events.length === 0 && !history.isLoading ? /* @__PURE__ */ jsx11("p", { style: styles.muted, children: "None recorded." }) : /* @__PURE__ */ jsx11("ul", { style: styles.list, children: history.events.map((e) => /* @__PURE__ */ jsxs11("li", { style: styles.row, children: [
|
|
1703
|
+
/* @__PURE__ */ jsx11(Pill, { tone: e.outcome === "success" ? "ok" : "bad", children: e.outcome }),
|
|
758
1704
|
" ",
|
|
759
|
-
/* @__PURE__ */
|
|
1705
|
+
/* @__PURE__ */ jsx11("span", { style: styles.muted, children: e.method }),
|
|
760
1706
|
" ",
|
|
761
|
-
/* @__PURE__ */
|
|
1707
|
+
/* @__PURE__ */ jsx11("span", { style: styles.mono, children: e.ipAddress ?? "\u2014" }),
|
|
762
1708
|
" ",
|
|
763
|
-
describeLocation(e.location) ? /* @__PURE__ */
|
|
764
|
-
e.location?.proxy || e.location?.hosting ? /* @__PURE__ */
|
|
1709
|
+
describeLocation(e.location) ? /* @__PURE__ */ jsx11("span", { style: styles.muted, children: describeLocation(e.location) }) : null,
|
|
1710
|
+
e.location?.proxy || e.location?.hosting ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
765
1711
|
" ",
|
|
766
|
-
e.location.proxy ? "proxy/VPN" : "hosting"
|
|
1712
|
+
/* @__PURE__ */ jsx11(Pill, { tone: "warn", children: e.location.proxy ? "proxy/VPN" : "hosting" })
|
|
767
1713
|
] }) : null,
|
|
768
1714
|
" ",
|
|
769
|
-
/* @__PURE__ */
|
|
1715
|
+
/* @__PURE__ */ jsx11("span", { style: styles.muted, children: new Date(e.createdAt).toLocaleString() })
|
|
770
1716
|
] }, e.id)) }),
|
|
771
|
-
history.hasMore && !history.isLoading ? /* @__PURE__ */
|
|
1717
|
+
history.hasMore && !history.isLoading ? /* @__PURE__ */ jsx11(
|
|
772
1718
|
"button",
|
|
773
1719
|
{
|
|
774
1720
|
type: "button",
|
|
@@ -783,59 +1729,67 @@ function UsersScreen({ client, pageSize = 50 }) {
|
|
|
783
1729
|
|
|
784
1730
|
// src/screens/CatalogScreen.tsx
|
|
785
1731
|
import { useAdminCatalog } from "@fonderie/react-admin";
|
|
786
|
-
import { Fragment as
|
|
1732
|
+
import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
787
1733
|
function CatalogScreen({ client }) {
|
|
788
1734
|
const { catalog, isLoading, error, refresh, deletePlan } = useAdminCatalog(client);
|
|
789
1735
|
const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
|
|
790
|
-
return /* @__PURE__ */
|
|
791
|
-
/* @__PURE__ */
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
"
|
|
1736
|
+
return /* @__PURE__ */ jsxs12("div", { style: styles.container, children: [
|
|
1737
|
+
/* @__PURE__ */ jsx12(
|
|
1738
|
+
PageHeader,
|
|
1739
|
+
{
|
|
1740
|
+
title: "Catalog",
|
|
1741
|
+
lead: "What you sell: plans as configured in code, and as stored in the database, side by side.",
|
|
1742
|
+
actions: /* @__PURE__ */ jsx12(RefreshButton, { onClick: () => void refresh(), busy: isLoading })
|
|
1743
|
+
}
|
|
1744
|
+
),
|
|
1745
|
+
error ? /* @__PURE__ */ jsx12("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
|
|
1746
|
+
isLoading && !catalog ? /* @__PURE__ */ jsx12("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
1747
|
+
catalog ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
1748
|
+
/* @__PURE__ */ jsx12("h2", { style: { ...styles.subtitle, marginTop: 0 }, children: "Configured (code)" }),
|
|
1749
|
+
/* @__PURE__ */ jsx12(
|
|
1750
|
+
"pre",
|
|
795
1751
|
{
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
1752
|
+
style: {
|
|
1753
|
+
...styles.card,
|
|
1754
|
+
...styles.mono,
|
|
1755
|
+
margin: 0,
|
|
1756
|
+
overflowX: "auto",
|
|
1757
|
+
maxHeight: 360,
|
|
1758
|
+
lineHeight: 1.6
|
|
1759
|
+
},
|
|
1760
|
+
children: JSON.stringify(catalog.configured, null, 2)
|
|
801
1761
|
}
|
|
802
|
-
)
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
/* @__PURE__ */
|
|
813
|
-
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Tier" }),
|
|
814
|
-
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Seats" }),
|
|
815
|
-
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Monthly" }),
|
|
816
|
-
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Yearly" }),
|
|
817
|
-
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Trial" }),
|
|
818
|
-
/* @__PURE__ */ jsx10("th", { style: styles.th })
|
|
1762
|
+
),
|
|
1763
|
+
/* @__PURE__ */ jsx12("h2", { style: styles.subtitle, children: "Stored (database)" }),
|
|
1764
|
+
catalog.stored.length === 0 ? /* @__PURE__ */ jsx12(Empty, { icon: "catalog", title: "No stored plans", children: "Plans are configured in code; nothing has been written to the database." }) : /* @__PURE__ */ jsxs12("table", { style: styles.table, children: [
|
|
1765
|
+
/* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsxs12("tr", { children: [
|
|
1766
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Plan" }),
|
|
1767
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Tier" }),
|
|
1768
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Seats" }),
|
|
1769
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Monthly" }),
|
|
1770
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Yearly" }),
|
|
1771
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Trial" }),
|
|
1772
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th })
|
|
819
1773
|
] }) }),
|
|
820
|
-
/* @__PURE__ */
|
|
821
|
-
/* @__PURE__ */
|
|
822
|
-
/* @__PURE__ */
|
|
823
|
-
p.description ? /* @__PURE__ */
|
|
1774
|
+
/* @__PURE__ */ jsx12("tbody", { children: catalog.stored.map((p) => /* @__PURE__ */ jsxs12("tr", { children: [
|
|
1775
|
+
/* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
|
|
1776
|
+
/* @__PURE__ */ jsx12("strong", { children: p.name }),
|
|
1777
|
+
p.description ? /* @__PURE__ */ jsx12("div", { style: styles.muted, children: p.description }) : null
|
|
824
1778
|
] }),
|
|
825
|
-
/* @__PURE__ */
|
|
826
|
-
/* @__PURE__ */
|
|
827
|
-
/* @__PURE__ */
|
|
1779
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: p.tier }),
|
|
1780
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: p.seats ?? "\u221E" }),
|
|
1781
|
+
/* @__PURE__ */ jsxs12("td", { style: styles.td, children: [
|
|
828
1782
|
money(p.pricing?.monthly),
|
|
829
1783
|
" ",
|
|
830
1784
|
p.pricing?.currency ?? ""
|
|
831
1785
|
] }),
|
|
832
|
-
/* @__PURE__ */
|
|
833
|
-
/* @__PURE__ */
|
|
834
|
-
/* @__PURE__ */
|
|
1786
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: money(p.pricing?.yearly) }),
|
|
1787
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: p.trialDays ? `${p.trialDays} d` : "\u2014" }),
|
|
1788
|
+
/* @__PURE__ */ jsx12("td", { style: styles.td, children: /* @__PURE__ */ jsx12(
|
|
835
1789
|
"button",
|
|
836
1790
|
{
|
|
837
1791
|
type: "button",
|
|
838
|
-
style: styles.
|
|
1792
|
+
style: styles.buttonDanger,
|
|
839
1793
|
onClick: () => {
|
|
840
1794
|
if (window.confirm(`Delete stored plan "${p.name}"?`))
|
|
841
1795
|
void deletePlan(p.id).catch(() => {
|
|
@@ -852,26 +1806,32 @@ function CatalogScreen({ client }) {
|
|
|
852
1806
|
|
|
853
1807
|
// src/screens/SubscriberScreen.tsx
|
|
854
1808
|
import { useAdminSubscriber, useAdminSubscribers } from "@fonderie/react-admin";
|
|
855
|
-
import { useState as
|
|
856
|
-
import { Fragment as
|
|
1809
|
+
import { useState as useState5 } from "react";
|
|
1810
|
+
import { Fragment as Fragment8, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
857
1811
|
function SubscriberScreen({ client, pageSize = 50 }) {
|
|
858
|
-
const [type, setType] =
|
|
859
|
-
const [idInput, setIdInput] =
|
|
860
|
-
const [subscriber, setSubscriber] =
|
|
1812
|
+
const [type, setType] = useState5("user");
|
|
1813
|
+
const [idInput, setIdInput] = useState5("");
|
|
1814
|
+
const [subscriber, setSubscriber] = useState5(null);
|
|
861
1815
|
const list = useAdminSubscribers(client, { limit: pageSize });
|
|
862
1816
|
const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = useAdminSubscriber(client, subscriber, { limit: 20 });
|
|
863
|
-
const [amount, setAmount] =
|
|
864
|
-
const [note, setNote] =
|
|
865
|
-
const [granted, setGranted] =
|
|
1817
|
+
const [amount, setAmount] = useState5("");
|
|
1818
|
+
const [note, setNote] = useState5("");
|
|
1819
|
+
const [granted, setGranted] = useState5(null);
|
|
866
1820
|
const open = (t2, id) => {
|
|
867
1821
|
setGranted(null);
|
|
868
1822
|
setType(t2);
|
|
869
1823
|
setIdInput(id);
|
|
870
1824
|
setSubscriber({ type: t2, id });
|
|
871
1825
|
};
|
|
872
|
-
return /* @__PURE__ */
|
|
873
|
-
/* @__PURE__ */
|
|
874
|
-
|
|
1826
|
+
return /* @__PURE__ */ jsxs13("div", { style: styles.container, children: [
|
|
1827
|
+
/* @__PURE__ */ jsx13(
|
|
1828
|
+
PageHeader,
|
|
1829
|
+
{
|
|
1830
|
+
title: "Subscribers",
|
|
1831
|
+
lead: "Who is subscribed. Open one for their plan, wallet and ledger, and to grant credit."
|
|
1832
|
+
}
|
|
1833
|
+
),
|
|
1834
|
+
/* @__PURE__ */ jsxs13(
|
|
875
1835
|
"form",
|
|
876
1836
|
{
|
|
877
1837
|
style: styles.toolbar,
|
|
@@ -881,31 +1841,34 @@ function SubscriberScreen({ client, pageSize = 50 }) {
|
|
|
881
1841
|
setSubscriber(idInput.trim() ? { type, id: idInput.trim() } : null);
|
|
882
1842
|
},
|
|
883
1843
|
children: [
|
|
884
|
-
/* @__PURE__ */
|
|
1844
|
+
/* @__PURE__ */ jsxs13(
|
|
885
1845
|
"select",
|
|
886
1846
|
{
|
|
887
1847
|
value: type,
|
|
888
1848
|
onChange: (e) => setType(e.target.value),
|
|
889
|
-
style: styles.
|
|
1849
|
+
style: styles.input,
|
|
890
1850
|
"aria-label": "Type",
|
|
891
1851
|
children: [
|
|
892
|
-
/* @__PURE__ */
|
|
893
|
-
/* @__PURE__ */
|
|
1852
|
+
/* @__PURE__ */ jsx13("option", { value: "user", children: "user" }),
|
|
1853
|
+
/* @__PURE__ */ jsx13("option", { value: "workspace", children: "workspace" })
|
|
894
1854
|
]
|
|
895
1855
|
}
|
|
896
1856
|
),
|
|
897
|
-
/* @__PURE__ */
|
|
1857
|
+
/* @__PURE__ */ jsx13(
|
|
898
1858
|
"input",
|
|
899
1859
|
{
|
|
900
1860
|
value: idInput,
|
|
901
1861
|
onChange: (e) => setIdInput(e.target.value),
|
|
902
1862
|
placeholder: "subscriber id",
|
|
903
|
-
style: { ...styles.
|
|
1863
|
+
style: { ...styles.input, minWidth: 280 },
|
|
904
1864
|
"aria-label": "Subscriber id"
|
|
905
1865
|
}
|
|
906
1866
|
),
|
|
907
|
-
/* @__PURE__ */
|
|
908
|
-
|
|
1867
|
+
/* @__PURE__ */ jsxs13("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
|
|
1868
|
+
/* @__PURE__ */ jsx13(Icon, { name: "search", size: 14 }),
|
|
1869
|
+
"Look up"
|
|
1870
|
+
] }),
|
|
1871
|
+
subscriber ? /* @__PURE__ */ jsxs13(
|
|
909
1872
|
"button",
|
|
910
1873
|
{
|
|
911
1874
|
type: "button",
|
|
@@ -915,9 +1878,12 @@ function SubscriberScreen({ client, pageSize = 50 }) {
|
|
|
915
1878
|
setIdInput("");
|
|
916
1879
|
setGranted(null);
|
|
917
1880
|
},
|
|
918
|
-
children:
|
|
1881
|
+
children: [
|
|
1882
|
+
/* @__PURE__ */ jsx13(Icon, { name: "back", size: 14 }),
|
|
1883
|
+
"All subscribers"
|
|
1884
|
+
]
|
|
919
1885
|
}
|
|
920
|
-
) : /* @__PURE__ */
|
|
1886
|
+
) : /* @__PURE__ */ jsx13(
|
|
921
1887
|
"button",
|
|
922
1888
|
{
|
|
923
1889
|
type: "button",
|
|
@@ -930,22 +1896,22 @@ function SubscriberScreen({ client, pageSize = 50 }) {
|
|
|
930
1896
|
]
|
|
931
1897
|
}
|
|
932
1898
|
),
|
|
933
|
-
error ? /* @__PURE__ */
|
|
934
|
-
!subscriber ? /* @__PURE__ */
|
|
935
|
-
list.error ? /* @__PURE__ */
|
|
936
|
-
list.subscriptions.length === 0 && !list.isLoading ? /* @__PURE__ */
|
|
937
|
-
/* @__PURE__ */
|
|
938
|
-
/* @__PURE__ */
|
|
939
|
-
/* @__PURE__ */
|
|
940
|
-
/* @__PURE__ */
|
|
941
|
-
/* @__PURE__ */
|
|
1899
|
+
error ? /* @__PURE__ */ jsx13("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
|
|
1900
|
+
!subscriber ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1901
|
+
list.error ? /* @__PURE__ */ jsx13("p", { style: styles.error, role: "alert", children: list.error.explanation }) : null,
|
|
1902
|
+
list.subscriptions.length === 0 && !list.isLoading ? /* @__PURE__ */ jsx13(Empty, { icon: "subscriber", title: "No subscribers yet", children: "Subscriptions appear here once someone checks out." }) : /* @__PURE__ */ jsxs13("table", { style: styles.table, children: [
|
|
1903
|
+
/* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsxs13("tr", { children: [
|
|
1904
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Subscriber" }),
|
|
1905
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Plan" }),
|
|
1906
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Status" }),
|
|
1907
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Renews" })
|
|
942
1908
|
] }) }),
|
|
943
|
-
/* @__PURE__ */
|
|
944
|
-
/* @__PURE__ */
|
|
1909
|
+
/* @__PURE__ */ jsx13("tbody", { children: list.subscriptions.map((s) => /* @__PURE__ */ jsxs13("tr", { children: [
|
|
1910
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: /* @__PURE__ */ jsxs13(
|
|
945
1911
|
"button",
|
|
946
1912
|
{
|
|
947
1913
|
type: "button",
|
|
948
|
-
style: { ...styles.
|
|
1914
|
+
style: { ...styles.link, ...styles.mono },
|
|
949
1915
|
onClick: () => open(s.subscriberType, s.subscriberId),
|
|
950
1916
|
children: [
|
|
951
1917
|
s.subscriberType,
|
|
@@ -954,20 +1920,29 @@ function SubscriberScreen({ client, pageSize = 50 }) {
|
|
|
954
1920
|
]
|
|
955
1921
|
}
|
|
956
1922
|
) }),
|
|
957
|
-
/* @__PURE__ */
|
|
958
|
-
/* @__PURE__ */
|
|
1923
|
+
/* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
|
|
1924
|
+
/* @__PURE__ */ jsx13("strong", { children: s.plan }),
|
|
959
1925
|
" \xB7 ",
|
|
960
1926
|
s.interval
|
|
961
1927
|
] }),
|
|
962
|
-
/* @__PURE__ */
|
|
963
|
-
|
|
964
|
-
|
|
1928
|
+
/* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
|
|
1929
|
+
/* @__PURE__ */ jsx13(
|
|
1930
|
+
Pill,
|
|
1931
|
+
{
|
|
1932
|
+
tone: s.status === "active" ? "ok" : s.status === "trialing" ? "info" : s.status === "past_due" ? "warn" : "neutral",
|
|
1933
|
+
children: s.status
|
|
1934
|
+
}
|
|
1935
|
+
),
|
|
1936
|
+
s.cancelAtPeriodEnd ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1937
|
+
" ",
|
|
1938
|
+
/* @__PURE__ */ jsx13(Pill, { tone: "warn", dot: false, children: "cancels" })
|
|
1939
|
+
] }) : null
|
|
965
1940
|
] }),
|
|
966
|
-
/* @__PURE__ */
|
|
1941
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: s.currentPeriodEnd ? new Date(s.currentPeriodEnd).toLocaleDateString() : /* @__PURE__ */ jsx13("span", { style: styles.muted, children: "\u2014" }) })
|
|
967
1942
|
] }, s.id)) })
|
|
968
1943
|
] }),
|
|
969
|
-
list.isLoading ? /* @__PURE__ */
|
|
970
|
-
list.hasMore && !list.isLoading ? /* @__PURE__ */
|
|
1944
|
+
list.isLoading ? /* @__PURE__ */ jsx13("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
1945
|
+
list.hasMore && !list.isLoading ? /* @__PURE__ */ jsx13(
|
|
971
1946
|
"button",
|
|
972
1947
|
{
|
|
973
1948
|
type: "button",
|
|
@@ -977,27 +1952,27 @@ function SubscriberScreen({ client, pageSize = 50 }) {
|
|
|
977
1952
|
}
|
|
978
1953
|
) : null
|
|
979
1954
|
] }) : null,
|
|
980
|
-
subscriber && !isLoading ? /* @__PURE__ */
|
|
981
|
-
/* @__PURE__ */
|
|
982
|
-
subscription ? /* @__PURE__ */
|
|
983
|
-
/* @__PURE__ */
|
|
984
|
-
/* @__PURE__ */
|
|
985
|
-
/* @__PURE__ */
|
|
986
|
-
/* @__PURE__ */
|
|
1955
|
+
subscriber && !isLoading ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1956
|
+
/* @__PURE__ */ jsx13("h2", { style: styles.subtitle, children: "Subscription" }),
|
|
1957
|
+
subscription ? /* @__PURE__ */ jsx13("table", { style: styles.table, children: /* @__PURE__ */ jsxs13("tbody", { children: [
|
|
1958
|
+
/* @__PURE__ */ jsxs13("tr", { children: [
|
|
1959
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: "plan" }),
|
|
1960
|
+
/* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
|
|
1961
|
+
/* @__PURE__ */ jsx13("strong", { children: subscription.plan }),
|
|
987
1962
|
" \xB7 ",
|
|
988
1963
|
subscription.interval
|
|
989
1964
|
] })
|
|
990
1965
|
] }),
|
|
991
|
-
/* @__PURE__ */
|
|
992
|
-
/* @__PURE__ */
|
|
993
|
-
/* @__PURE__ */
|
|
1966
|
+
/* @__PURE__ */ jsxs13("tr", { children: [
|
|
1967
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: "status" }),
|
|
1968
|
+
/* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
|
|
994
1969
|
subscription.status,
|
|
995
1970
|
subscription.cancelAtPeriodEnd ? " \xB7 cancels at period end" : ""
|
|
996
1971
|
] })
|
|
997
1972
|
] }),
|
|
998
|
-
/* @__PURE__ */
|
|
999
|
-
/* @__PURE__ */
|
|
1000
|
-
/* @__PURE__ */
|
|
1973
|
+
/* @__PURE__ */ jsxs13("tr", { children: [
|
|
1974
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: "period" }),
|
|
1975
|
+
/* @__PURE__ */ jsxs13("td", { style: styles.td, children: [
|
|
1001
1976
|
subscription.currentPeriodStart ? new Date(subscription.currentPeriodStart).toLocaleDateString() : "\u2014",
|
|
1002
1977
|
" ",
|
|
1003
1978
|
"\u2192",
|
|
@@ -1005,28 +1980,49 @@ function SubscriberScreen({ client, pageSize = 50 }) {
|
|
|
1005
1980
|
subscription.currentPeriodEnd ? new Date(subscription.currentPeriodEnd).toLocaleDateString() : "\u2014"
|
|
1006
1981
|
] })
|
|
1007
1982
|
] }),
|
|
1008
|
-
/* @__PURE__ */
|
|
1009
|
-
/* @__PURE__ */
|
|
1010
|
-
/* @__PURE__ */
|
|
1983
|
+
/* @__PURE__ */ jsxs13("tr", { children: [
|
|
1984
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: "provider" }),
|
|
1985
|
+
/* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.mono }, children: subscription.providerSubscriptionId ?? "\u2014" })
|
|
1011
1986
|
] })
|
|
1012
|
-
] }) }) : /* @__PURE__ */
|
|
1013
|
-
/* @__PURE__ */
|
|
1014
|
-
wallet ? /* @__PURE__ */
|
|
1015
|
-
/* @__PURE__ */
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1987
|
+
] }) }) : /* @__PURE__ */ jsx13("p", { style: styles.muted, children: "No subscription." }),
|
|
1988
|
+
/* @__PURE__ */ jsx13("h2", { style: styles.subtitle, children: "Wallet" }),
|
|
1989
|
+
wallet ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1990
|
+
/* @__PURE__ */ jsxs13(
|
|
1991
|
+
"p",
|
|
1992
|
+
{
|
|
1993
|
+
style: {
|
|
1994
|
+
...styles.card,
|
|
1995
|
+
display: "flex",
|
|
1996
|
+
alignItems: "baseline",
|
|
1997
|
+
gap: 8,
|
|
1998
|
+
flexWrap: "wrap"
|
|
1999
|
+
},
|
|
2000
|
+
children: [
|
|
2001
|
+
/* @__PURE__ */ jsx13(
|
|
2002
|
+
"strong",
|
|
2003
|
+
{
|
|
2004
|
+
style: {
|
|
2005
|
+
fontSize: 22,
|
|
2006
|
+
letterSpacing: "var(--fonderie-tracking-display,-0.05em)"
|
|
2007
|
+
},
|
|
2008
|
+
children: wallet.balance
|
|
2009
|
+
}
|
|
2010
|
+
),
|
|
2011
|
+
" ",
|
|
2012
|
+
wallet.currency,
|
|
2013
|
+
" ",
|
|
2014
|
+
/* @__PURE__ */ jsxs13("span", { style: styles.muted, children: [
|
|
2015
|
+
"(granted ",
|
|
2016
|
+
wallet.granted ?? "0",
|
|
2017
|
+
" \xB7 purchased ",
|
|
2018
|
+
wallet.purchased ?? "0",
|
|
2019
|
+
") \xB7 minor units, precision ",
|
|
2020
|
+
wallet.precision
|
|
2021
|
+
] })
|
|
2022
|
+
]
|
|
2023
|
+
}
|
|
2024
|
+
),
|
|
2025
|
+
/* @__PURE__ */ jsxs13(
|
|
1030
2026
|
"form",
|
|
1031
2027
|
{
|
|
1032
2028
|
style: styles.toolbar,
|
|
@@ -1045,47 +2041,58 @@ function SubscriberScreen({ client, pageSize = 50 }) {
|
|
|
1045
2041
|
});
|
|
1046
2042
|
},
|
|
1047
2043
|
children: [
|
|
1048
|
-
/* @__PURE__ */
|
|
2044
|
+
/* @__PURE__ */ jsx13(
|
|
1049
2045
|
"input",
|
|
1050
2046
|
{
|
|
1051
2047
|
value: amount,
|
|
1052
2048
|
onChange: (e) => setAmount(e.target.value),
|
|
1053
2049
|
placeholder: "amount (minor units)",
|
|
1054
|
-
style:
|
|
2050
|
+
style: styles.input,
|
|
1055
2051
|
"aria-label": "Amount"
|
|
1056
2052
|
}
|
|
1057
2053
|
),
|
|
1058
|
-
/* @__PURE__ */
|
|
2054
|
+
/* @__PURE__ */ jsx13(
|
|
1059
2055
|
"input",
|
|
1060
2056
|
{
|
|
1061
2057
|
value: note,
|
|
1062
2058
|
onChange: (e) => setNote(e.target.value),
|
|
1063
2059
|
placeholder: "reason (optional)",
|
|
1064
|
-
style: { ...styles.
|
|
2060
|
+
style: { ...styles.input, minWidth: 220 },
|
|
1065
2061
|
"aria-label": "Reason"
|
|
1066
2062
|
}
|
|
1067
2063
|
),
|
|
1068
|
-
/* @__PURE__ */
|
|
1069
|
-
|
|
2064
|
+
/* @__PURE__ */ jsxs13(
|
|
2065
|
+
"button",
|
|
2066
|
+
{
|
|
2067
|
+
type: "submit",
|
|
2068
|
+
style: styles.buttonPrimary,
|
|
2069
|
+
disabled: !/^\d+$/.test(amount.trim()),
|
|
2070
|
+
children: [
|
|
2071
|
+
/* @__PURE__ */ jsx13(Icon, { name: "plus", size: 14 }),
|
|
2072
|
+
"Grant"
|
|
2073
|
+
]
|
|
2074
|
+
}
|
|
2075
|
+
),
|
|
2076
|
+
granted ? /* @__PURE__ */ jsx13("span", { style: styles.ok, children: granted }) : null
|
|
1070
2077
|
]
|
|
1071
2078
|
}
|
|
1072
2079
|
),
|
|
1073
|
-
/* @__PURE__ */
|
|
1074
|
-
ledger.length === 0 ? /* @__PURE__ */
|
|
1075
|
-
/* @__PURE__ */
|
|
1076
|
-
/* @__PURE__ */
|
|
1077
|
-
/* @__PURE__ */
|
|
1078
|
-
/* @__PURE__ */
|
|
1079
|
-
/* @__PURE__ */
|
|
2080
|
+
/* @__PURE__ */ jsx13("h2", { style: styles.subtitle, children: "Ledger" }),
|
|
2081
|
+
ledger.length === 0 ? /* @__PURE__ */ jsx13(Empty, { icon: "subscriber", title: "Nothing moved yet" }) : /* @__PURE__ */ jsxs13("table", { style: styles.table, children: [
|
|
2082
|
+
/* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsxs13("tr", { children: [
|
|
2083
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "When" }),
|
|
2084
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Kind" }),
|
|
2085
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Amount" }),
|
|
2086
|
+
/* @__PURE__ */ jsx13("th", { style: styles.th, children: "Note" })
|
|
1080
2087
|
] }) }),
|
|
1081
|
-
/* @__PURE__ */
|
|
1082
|
-
/* @__PURE__ */
|
|
1083
|
-
/* @__PURE__ */
|
|
1084
|
-
/* @__PURE__ */
|
|
1085
|
-
/* @__PURE__ */
|
|
2088
|
+
/* @__PURE__ */ jsx13("tbody", { children: ledger.map((t2) => /* @__PURE__ */ jsxs13("tr", { children: [
|
|
2089
|
+
/* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.muted }, children: new Date(t2.createdAt).toLocaleString() }),
|
|
2090
|
+
/* @__PURE__ */ jsx13("td", { style: styles.td, children: t2.type }),
|
|
2091
|
+
/* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.mono }, children: t2.amount }),
|
|
2092
|
+
/* @__PURE__ */ jsx13("td", { style: { ...styles.td, ...styles.muted }, children: t2.description ?? "" })
|
|
1086
2093
|
] }, t2.id)) })
|
|
1087
2094
|
] }),
|
|
1088
|
-
hasMoreLedger ? /* @__PURE__ */
|
|
2095
|
+
hasMoreLedger ? /* @__PURE__ */ jsx13(
|
|
1089
2096
|
"button",
|
|
1090
2097
|
{
|
|
1091
2098
|
type: "button",
|
|
@@ -1094,37 +2101,43 @@ function SubscriberScreen({ client, pageSize = 50 }) {
|
|
|
1094
2101
|
children: "Load more"
|
|
1095
2102
|
}
|
|
1096
2103
|
) : null
|
|
1097
|
-
] }) : /* @__PURE__ */
|
|
2104
|
+
] }) : /* @__PURE__ */ jsx13("p", { style: styles.muted, children: "No wallet \u2014 billing has no wallet configuration." })
|
|
1098
2105
|
] }) : null
|
|
1099
2106
|
] });
|
|
1100
2107
|
}
|
|
1101
2108
|
|
|
1102
2109
|
// src/screens/AuditScreen.tsx
|
|
1103
2110
|
import { useAdminAudit } from "@fonderie/react-admin";
|
|
1104
|
-
import { useState as
|
|
1105
|
-
import { jsx as
|
|
2111
|
+
import { useState as useState6 } from "react";
|
|
2112
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1106
2113
|
function AuditScreen({ client, pageSize = 50 }) {
|
|
1107
|
-
const [draft, setDraft] =
|
|
1108
|
-
const [filter, setFilter] =
|
|
2114
|
+
const [draft, setDraft] = useState6({ workspaceId: "", type: "", actorId: "" });
|
|
2115
|
+
const [filter, setFilter] = useState6(
|
|
1109
2116
|
{}
|
|
1110
2117
|
);
|
|
1111
2118
|
const { events, hasMore, isLoading, error, refresh, loadMore } = useAdminAudit(client, {
|
|
1112
2119
|
...filter,
|
|
1113
2120
|
limit: pageSize
|
|
1114
2121
|
});
|
|
1115
|
-
const field = (key, placeholder) => /* @__PURE__ */
|
|
2122
|
+
const field = (key, placeholder) => /* @__PURE__ */ jsx14(
|
|
1116
2123
|
"input",
|
|
1117
2124
|
{
|
|
1118
2125
|
value: draft[key],
|
|
1119
2126
|
onChange: (e) => setDraft({ ...draft, [key]: e.target.value }),
|
|
1120
2127
|
placeholder,
|
|
1121
|
-
style:
|
|
2128
|
+
style: styles.input,
|
|
1122
2129
|
"aria-label": placeholder
|
|
1123
2130
|
}
|
|
1124
2131
|
);
|
|
1125
|
-
return /* @__PURE__ */
|
|
1126
|
-
/* @__PURE__ */
|
|
1127
|
-
|
|
2132
|
+
return /* @__PURE__ */ jsxs14("div", { style: styles.container, children: [
|
|
2133
|
+
/* @__PURE__ */ jsx14(
|
|
2134
|
+
PageHeader,
|
|
2135
|
+
{
|
|
2136
|
+
title: "Audit",
|
|
2137
|
+
lead: "What happened, across every workspace unless you name one. The chain's integrity verdict is on the Doctor page."
|
|
2138
|
+
}
|
|
2139
|
+
),
|
|
2140
|
+
/* @__PURE__ */ jsxs14(
|
|
1128
2141
|
"form",
|
|
1129
2142
|
{
|
|
1130
2143
|
style: styles.toolbar,
|
|
@@ -1140,8 +2153,11 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
1140
2153
|
field("workspaceId", "workspace id (all if empty)"),
|
|
1141
2154
|
field("type", "event type"),
|
|
1142
2155
|
field("actorId", "actor id"),
|
|
1143
|
-
/* @__PURE__ */
|
|
1144
|
-
|
|
2156
|
+
/* @__PURE__ */ jsxs14("button", { type: "submit", style: styles.buttonPrimary, disabled: isLoading, children: [
|
|
2157
|
+
/* @__PURE__ */ jsx14(Icon, { name: "search", size: 14 }),
|
|
2158
|
+
"Filter"
|
|
2159
|
+
] }),
|
|
2160
|
+
/* @__PURE__ */ jsx14(
|
|
1145
2161
|
"button",
|
|
1146
2162
|
{
|
|
1147
2163
|
type: "button",
|
|
@@ -1154,25 +2170,25 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
1154
2170
|
]
|
|
1155
2171
|
}
|
|
1156
2172
|
),
|
|
1157
|
-
error ? /* @__PURE__ */
|
|
1158
|
-
events.length === 0 && !isLoading ? /* @__PURE__ */
|
|
1159
|
-
/* @__PURE__ */
|
|
1160
|
-
/* @__PURE__ */
|
|
1161
|
-
/* @__PURE__ */
|
|
1162
|
-
/* @__PURE__ */
|
|
1163
|
-
/* @__PURE__ */
|
|
1164
|
-
/* @__PURE__ */
|
|
2173
|
+
error ? /* @__PURE__ */ jsx14("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
|
|
2174
|
+
events.length === 0 && !isLoading ? /* @__PURE__ */ jsx14(Empty, { icon: "audit", title: "Nothing recorded for this filter" }) : /* @__PURE__ */ jsxs14("table", { style: styles.table, children: [
|
|
2175
|
+
/* @__PURE__ */ jsx14("thead", { children: /* @__PURE__ */ jsxs14("tr", { children: [
|
|
2176
|
+
/* @__PURE__ */ jsx14("th", { style: styles.th, children: "When" }),
|
|
2177
|
+
/* @__PURE__ */ jsx14("th", { style: styles.th, children: "Type" }),
|
|
2178
|
+
/* @__PURE__ */ jsx14("th", { style: styles.th, children: "Workspace" }),
|
|
2179
|
+
/* @__PURE__ */ jsx14("th", { style: styles.th, children: "Actor" }),
|
|
2180
|
+
/* @__PURE__ */ jsx14("th", { style: styles.th, children: "Request" })
|
|
1165
2181
|
] }) }),
|
|
1166
|
-
/* @__PURE__ */
|
|
1167
|
-
/* @__PURE__ */
|
|
1168
|
-
/* @__PURE__ */
|
|
1169
|
-
/* @__PURE__ */
|
|
1170
|
-
/* @__PURE__ */
|
|
1171
|
-
/* @__PURE__ */
|
|
2182
|
+
/* @__PURE__ */ jsx14("tbody", { children: events.map((e) => /* @__PURE__ */ jsxs14("tr", { children: [
|
|
2183
|
+
/* @__PURE__ */ jsx14("td", { style: { ...styles.td, ...styles.muted }, children: new Date(e.createdAt).toLocaleString() }),
|
|
2184
|
+
/* @__PURE__ */ jsx14("td", { style: { ...styles.td, ...styles.mono }, children: e.type }),
|
|
2185
|
+
/* @__PURE__ */ jsx14("td", { style: { ...styles.td, ...styles.mono }, children: String(e.payload["workspaceId"] ?? "\u2014") }),
|
|
2186
|
+
/* @__PURE__ */ jsx14("td", { style: { ...styles.td, ...styles.mono }, children: e.actorId ?? "\u2014" }),
|
|
2187
|
+
/* @__PURE__ */ jsx14("td", { style: { ...styles.td, ...styles.muted }, children: e.requestId ?? "" })
|
|
1172
2188
|
] }, e.id)) })
|
|
1173
2189
|
] }),
|
|
1174
|
-
isLoading ? /* @__PURE__ */
|
|
1175
|
-
hasMore && !isLoading ? /* @__PURE__ */
|
|
2190
|
+
isLoading ? /* @__PURE__ */ jsx14("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
2191
|
+
hasMore && !isLoading ? /* @__PURE__ */ jsx14(
|
|
1176
2192
|
"button",
|
|
1177
2193
|
{
|
|
1178
2194
|
type: "button",
|
|
@@ -1185,7 +2201,36 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
1185
2201
|
}
|
|
1186
2202
|
|
|
1187
2203
|
// src/screens/AdminShell.tsx
|
|
1188
|
-
import { Fragment as
|
|
2204
|
+
import { Fragment as Fragment9, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2205
|
+
var ICON = {
|
|
2206
|
+
attention: "attention",
|
|
2207
|
+
modules: "modules",
|
|
2208
|
+
environment: "environment",
|
|
2209
|
+
doctor: "doctor",
|
|
2210
|
+
routes: "routes",
|
|
2211
|
+
users: "users",
|
|
2212
|
+
catalog: "catalog",
|
|
2213
|
+
subscriber: "subscriber",
|
|
2214
|
+
settings: "settings",
|
|
2215
|
+
templates: "templates",
|
|
2216
|
+
audit: "audit",
|
|
2217
|
+
log: "log",
|
|
2218
|
+
tokens: "tokens",
|
|
2219
|
+
operators: "users",
|
|
2220
|
+
migrations: "migrations"
|
|
2221
|
+
};
|
|
2222
|
+
var NARROW = "(max-width: 820px)";
|
|
2223
|
+
function useNarrow() {
|
|
2224
|
+
const query = typeof window !== "undefined" && window.matchMedia ? window.matchMedia(NARROW) : null;
|
|
2225
|
+
const [narrow, setNarrow] = useState7(() => query?.matches ?? false);
|
|
2226
|
+
useEffect(() => {
|
|
2227
|
+
if (!query) return;
|
|
2228
|
+
const on = (e) => setNarrow(e.matches);
|
|
2229
|
+
query.addEventListener("change", on);
|
|
2230
|
+
return () => query.removeEventListener("change", on);
|
|
2231
|
+
}, [query]);
|
|
2232
|
+
return narrow;
|
|
2233
|
+
}
|
|
1189
2234
|
var NAV = [
|
|
1190
2235
|
{ group: "Today", items: [{ page: "attention", label: "Attention" }] },
|
|
1191
2236
|
{
|
|
@@ -1212,7 +2257,8 @@ var NAV = [
|
|
|
1212
2257
|
items: [
|
|
1213
2258
|
{ page: "audit", label: "Audit", needs: "audit" },
|
|
1214
2259
|
{ page: "log", label: "Admin log" },
|
|
1215
|
-
{ page: "
|
|
2260
|
+
{ page: "operators", label: "Operators", needs: "operators" },
|
|
2261
|
+
{ page: "tokens", label: "Tokens" },
|
|
1216
2262
|
{ page: "migrations", label: "Migrations" }
|
|
1217
2263
|
]
|
|
1218
2264
|
}
|
|
@@ -1226,64 +2272,79 @@ function AdminShell({
|
|
|
1226
2272
|
auditClient,
|
|
1227
2273
|
environment,
|
|
1228
2274
|
page,
|
|
1229
|
-
onNavigate
|
|
2275
|
+
onNavigate,
|
|
2276
|
+
appName = "Admin",
|
|
2277
|
+
envLabel,
|
|
2278
|
+
footer,
|
|
2279
|
+
operators = false,
|
|
2280
|
+
currentOperator
|
|
1230
2281
|
}) {
|
|
1231
|
-
const
|
|
2282
|
+
const narrow = useNarrow();
|
|
2283
|
+
const [drawer, setDrawer] = useState7(false);
|
|
2284
|
+
const [own, setOwn] = useState7("attention");
|
|
1232
2285
|
const current = page ?? own;
|
|
1233
2286
|
const go = (p) => {
|
|
1234
2287
|
if (onNavigate) onNavigate(p);
|
|
1235
2288
|
if (page === void 0) setOwn(p);
|
|
2289
|
+
setDrawer(false);
|
|
1236
2290
|
};
|
|
1237
|
-
const [editing, setEditing] =
|
|
2291
|
+
const [editing, setEditing] = useState7(null);
|
|
1238
2292
|
const has = {
|
|
1239
2293
|
config: Boolean(configClient),
|
|
1240
2294
|
courier: Boolean(courierClient),
|
|
1241
2295
|
auth: Boolean(authClient),
|
|
1242
2296
|
billing: Boolean(billingClient),
|
|
1243
|
-
audit: Boolean(auditClient)
|
|
2297
|
+
audit: Boolean(auditClient),
|
|
2298
|
+
operators
|
|
1244
2299
|
};
|
|
1245
2300
|
let body;
|
|
1246
2301
|
switch (current) {
|
|
1247
2302
|
case "attention":
|
|
1248
|
-
body = /* @__PURE__ */
|
|
2303
|
+
body = /* @__PURE__ */ jsx15(AttentionScreen, { client });
|
|
1249
2304
|
break;
|
|
1250
2305
|
case "modules":
|
|
1251
|
-
body = /* @__PURE__ */
|
|
2306
|
+
body = /* @__PURE__ */ jsx15(ModulesScreen, { client });
|
|
1252
2307
|
break;
|
|
1253
2308
|
case "doctor":
|
|
1254
|
-
body = /* @__PURE__ */
|
|
2309
|
+
body = /* @__PURE__ */ jsx15(DoctorScreen, { client });
|
|
1255
2310
|
break;
|
|
1256
2311
|
case "environment":
|
|
1257
|
-
body = /* @__PURE__ */
|
|
2312
|
+
body = /* @__PURE__ */ jsx15(EnvironmentScreen, { client });
|
|
1258
2313
|
break;
|
|
1259
2314
|
case "routes":
|
|
1260
|
-
body = /* @__PURE__ */
|
|
2315
|
+
body = /* @__PURE__ */ jsx15(RoutesScreen, { client });
|
|
1261
2316
|
break;
|
|
1262
2317
|
case "tokens":
|
|
1263
|
-
body = /* @__PURE__ */
|
|
2318
|
+
body = /* @__PURE__ */ jsx15(TokensScreen, { client });
|
|
2319
|
+
break;
|
|
2320
|
+
case "operators":
|
|
2321
|
+
body = /* @__PURE__ */ jsx15(OperatorsScreen, { client, me: currentOperator });
|
|
1264
2322
|
break;
|
|
1265
2323
|
case "migrations":
|
|
1266
|
-
body = /* @__PURE__ */
|
|
2324
|
+
body = /* @__PURE__ */ jsx15(MigrationsScreen, { client });
|
|
1267
2325
|
break;
|
|
1268
2326
|
case "log":
|
|
1269
|
-
body = /* @__PURE__ */
|
|
2327
|
+
body = /* @__PURE__ */ jsx15(AdminLogScreen, { client });
|
|
1270
2328
|
break;
|
|
1271
2329
|
case "catalog":
|
|
1272
|
-
body = billingClient ? /* @__PURE__ */
|
|
2330
|
+
body = billingClient ? /* @__PURE__ */ jsx15(CatalogScreen, { client: billingClient }) : /* @__PURE__ */ jsx15("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a BillingAdminClient to see the catalog here." });
|
|
1273
2331
|
break;
|
|
1274
2332
|
case "subscriber":
|
|
1275
|
-
body = billingClient ? /* @__PURE__ */
|
|
2333
|
+
body = billingClient ? /* @__PURE__ */ jsx15(SubscriberScreen, { client: billingClient }) : /* @__PURE__ */ jsx15("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a BillingAdminClient to look up subscribers here." });
|
|
1276
2334
|
break;
|
|
1277
2335
|
case "audit":
|
|
1278
|
-
body = auditClient ? /* @__PURE__ */
|
|
2336
|
+
body = auditClient ? /* @__PURE__ */ jsx15(AuditScreen, { client: auditClient }) : /* @__PURE__ */ jsx15("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass an AuditAdminClient to see the audit trail here." });
|
|
1279
2337
|
break;
|
|
1280
2338
|
case "users":
|
|
1281
|
-
body = authClient ? /* @__PURE__ */
|
|
2339
|
+
body = authClient ? /* @__PURE__ */ jsx15(UsersScreen, { client: authClient }) : /* @__PURE__ */ jsx15("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass an AuthAdminClient to look up users here." });
|
|
1282
2340
|
break;
|
|
1283
2341
|
case "settings":
|
|
1284
|
-
body = !configClient ? /* @__PURE__ */
|
|
1285
|
-
/* @__PURE__ */
|
|
1286
|
-
|
|
2342
|
+
body = !configClient ? /* @__PURE__ */ jsx15("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a ConfigAdminClient to manage config and secrets here." }) : editing && editing.kind !== "template" ? /* @__PURE__ */ jsxs15(Fragment9, { children: [
|
|
2343
|
+
/* @__PURE__ */ jsx15("div", { style: { padding: "24px 40px 0" }, children: /* @__PURE__ */ jsxs15("button", { type: "button", style: styles.buttonGhost, onClick: () => setEditing(null), children: [
|
|
2344
|
+
/* @__PURE__ */ jsx15(Icon, { name: "back", size: 14 }),
|
|
2345
|
+
"Config & secrets"
|
|
2346
|
+
] }) }),
|
|
2347
|
+
/* @__PURE__ */ jsx15(
|
|
1287
2348
|
ConfigEditorScreen,
|
|
1288
2349
|
{
|
|
1289
2350
|
client: configClient,
|
|
@@ -1293,7 +2354,7 @@ function AdminShell({
|
|
|
1293
2354
|
onSaved: () => setEditing(null)
|
|
1294
2355
|
}
|
|
1295
2356
|
)
|
|
1296
|
-
] }) : /* @__PURE__ */
|
|
2357
|
+
] }) : /* @__PURE__ */ jsx15(
|
|
1297
2358
|
ConfigListScreen,
|
|
1298
2359
|
{
|
|
1299
2360
|
client: configClient,
|
|
@@ -1306,9 +2367,12 @@ function AdminShell({
|
|
|
1306
2367
|
);
|
|
1307
2368
|
break;
|
|
1308
2369
|
case "templates":
|
|
1309
|
-
body = !courierClient ? /* @__PURE__ */
|
|
1310
|
-
/* @__PURE__ */
|
|
1311
|
-
|
|
2370
|
+
body = !courierClient ? /* @__PURE__ */ jsx15("p", { style: { ...styles.status, padding: "32px 40px" }, children: "Pass a CourierAdminClient to manage templates here." }) : editing?.kind === "template" ? /* @__PURE__ */ jsxs15(Fragment9, { children: [
|
|
2371
|
+
/* @__PURE__ */ jsx15("div", { style: { padding: "24px 40px 0" }, children: /* @__PURE__ */ jsxs15("button", { type: "button", style: styles.buttonGhost, onClick: () => setEditing(null), children: [
|
|
2372
|
+
/* @__PURE__ */ jsx15(Icon, { name: "back", size: 14 }),
|
|
2373
|
+
"Templates"
|
|
2374
|
+
] }) }),
|
|
2375
|
+
/* @__PURE__ */ jsx15(
|
|
1312
2376
|
TemplateEditorScreen,
|
|
1313
2377
|
{
|
|
1314
2378
|
client: courierClient,
|
|
@@ -1316,7 +2380,7 @@ function AdminShell({
|
|
|
1316
2380
|
onSaved: () => setEditing(null)
|
|
1317
2381
|
}
|
|
1318
2382
|
)
|
|
1319
|
-
] }) : /* @__PURE__ */
|
|
2383
|
+
] }) : /* @__PURE__ */ jsx15(
|
|
1320
2384
|
TemplateListScreen,
|
|
1321
2385
|
{
|
|
1322
2386
|
client: courierClient,
|
|
@@ -1325,29 +2389,105 @@ function AdminShell({
|
|
|
1325
2389
|
);
|
|
1326
2390
|
break;
|
|
1327
2391
|
}
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
2392
|
+
const production = envLabel ? /^prod/i.test(envLabel) : false;
|
|
2393
|
+
const brand = /* @__PURE__ */ jsxs15("div", { style: styles.navBrand, children: [
|
|
2394
|
+
/* @__PURE__ */ jsx15("div", { style: styles.navMark, children: /* @__PURE__ */ jsx15(Icon, { name: "mark", size: 15 }) }),
|
|
2395
|
+
/* @__PURE__ */ jsxs15("div", { style: { minWidth: 0, flex: 1 }, children: [
|
|
2396
|
+
/* @__PURE__ */ jsx15("div", { style: styles.navBrandTitle, children: appName }),
|
|
2397
|
+
envLabel ? /* @__PURE__ */ jsx15(
|
|
2398
|
+
"span",
|
|
2399
|
+
{
|
|
2400
|
+
style: {
|
|
2401
|
+
...styles.badge,
|
|
2402
|
+
marginTop: 3,
|
|
2403
|
+
padding: "0 7px",
|
|
2404
|
+
fontSize: 11,
|
|
2405
|
+
...production ? {
|
|
2406
|
+
color: "var(--fonderie-danger,#e00)",
|
|
2407
|
+
borderColor: "color-mix(in srgb, var(--fonderie-danger,#e00) 35%, transparent)"
|
|
2408
|
+
} : {}
|
|
2409
|
+
},
|
|
2410
|
+
children: envLabel
|
|
2411
|
+
}
|
|
2412
|
+
) : null
|
|
2413
|
+
] }),
|
|
2414
|
+
narrow ? /* @__PURE__ */ jsx15(
|
|
2415
|
+
"button",
|
|
2416
|
+
{
|
|
2417
|
+
type: "button",
|
|
2418
|
+
style: styles.buttonGhost,
|
|
2419
|
+
"aria-label": "Close menu",
|
|
2420
|
+
onClick: () => setDrawer(false),
|
|
2421
|
+
children: /* @__PURE__ */ jsx15(Icon, { name: "close" })
|
|
2422
|
+
}
|
|
2423
|
+
) : null
|
|
2424
|
+
] });
|
|
2425
|
+
const nav = /* @__PURE__ */ jsxs15(
|
|
2426
|
+
"nav",
|
|
2427
|
+
{
|
|
2428
|
+
style: {
|
|
2429
|
+
...styles.nav,
|
|
2430
|
+
...narrow ? { width: 280, maxWidth: "85vw", boxShadow: "0 10px 40px rgba(0,0,0,.25)" } : {}
|
|
2431
|
+
},
|
|
2432
|
+
"aria-label": "Admin",
|
|
2433
|
+
children: [
|
|
2434
|
+
brand,
|
|
2435
|
+
/* @__PURE__ */ jsx15("div", { style: styles.navScroll, children: NAV.map((g) => {
|
|
2436
|
+
const items = g.items.filter((i) => !i.needs || has[i.needs]);
|
|
2437
|
+
if (items.length === 0) return null;
|
|
2438
|
+
return /* @__PURE__ */ jsxs15("div", { children: [
|
|
2439
|
+
/* @__PURE__ */ jsx15("div", { style: styles.navGroup, children: g.group }),
|
|
2440
|
+
items.map((i) => {
|
|
2441
|
+
const active = current === i.page;
|
|
2442
|
+
return /* @__PURE__ */ jsxs15(
|
|
2443
|
+
"button",
|
|
2444
|
+
{
|
|
2445
|
+
type: "button",
|
|
2446
|
+
className: "fonderie-admin-nav-item",
|
|
2447
|
+
style: { ...styles.navItem, ...active ? styles.navItemActive : {} },
|
|
2448
|
+
"aria-current": active ? "page" : void 0,
|
|
2449
|
+
onClick: () => {
|
|
2450
|
+
setEditing(null);
|
|
2451
|
+
go(i.page);
|
|
2452
|
+
},
|
|
2453
|
+
children: [
|
|
2454
|
+
/* @__PURE__ */ jsx15(Icon, { name: ICON[i.page], style: active ? styles.navIconActive : void 0 }),
|
|
2455
|
+
i.label
|
|
2456
|
+
]
|
|
2457
|
+
},
|
|
2458
|
+
i.page
|
|
2459
|
+
);
|
|
2460
|
+
})
|
|
2461
|
+
] }, g.group);
|
|
2462
|
+
}) }),
|
|
2463
|
+
footer ? /* @__PURE__ */ jsx15("div", { style: styles.navFooter, children: footer }) : null
|
|
2464
|
+
]
|
|
2465
|
+
}
|
|
2466
|
+
);
|
|
2467
|
+
const title = NAV.flatMap((g) => g.items).find((i) => i.page === current)?.label ?? "";
|
|
2468
|
+
return /* @__PURE__ */ jsxs15("div", { style: { ...styles.shell, ...narrow ? { flexDirection: "column" } : {} }, children: [
|
|
2469
|
+
narrow ? /* @__PURE__ */ jsxs15(Fragment9, { children: [
|
|
2470
|
+
/* @__PURE__ */ jsxs15("div", { style: styles.topbar, children: [
|
|
2471
|
+
/* @__PURE__ */ jsx15(
|
|
1335
2472
|
"button",
|
|
1336
2473
|
{
|
|
1337
2474
|
type: "button",
|
|
1338
|
-
style:
|
|
1339
|
-
"aria-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
2475
|
+
style: styles.buttonGhost,
|
|
2476
|
+
"aria-label": "Open menu",
|
|
2477
|
+
"aria-expanded": drawer,
|
|
2478
|
+
onClick: () => setDrawer(true),
|
|
2479
|
+
children: /* @__PURE__ */ jsx15(Icon, { name: "menu" })
|
|
2480
|
+
}
|
|
2481
|
+
),
|
|
2482
|
+
/* @__PURE__ */ jsx15("span", { style: { fontWeight: 600, fontSize: 14 }, children: title }),
|
|
2483
|
+
envLabel ? /* @__PURE__ */ jsx15("span", { style: { ...styles.badge, marginLeft: "auto" }, children: envLabel }) : null
|
|
2484
|
+
] }),
|
|
2485
|
+
drawer ? /* @__PURE__ */ jsxs15("div", { style: styles.drawer, children: [
|
|
2486
|
+
nav,
|
|
2487
|
+
/* @__PURE__ */ jsx15("div", { style: styles.scrim, onClick: () => setDrawer(false), "aria-hidden": "true" })
|
|
2488
|
+
] }) : null
|
|
2489
|
+
] }) : nav,
|
|
2490
|
+
/* @__PURE__ */ jsx15("main", { className: "fonderie-admin-main", style: styles.main, children: body })
|
|
1351
2491
|
] });
|
|
1352
2492
|
}
|
|
1353
2493
|
export {
|
|
@@ -1360,6 +2500,7 @@ export {
|
|
|
1360
2500
|
EnvironmentScreen,
|
|
1361
2501
|
MigrationsScreen,
|
|
1362
2502
|
ModulesScreen,
|
|
2503
|
+
OperatorsScreen,
|
|
1363
2504
|
RoutesScreen,
|
|
1364
2505
|
SubscriberScreen,
|
|
1365
2506
|
TokensScreen,
|