@fonderie/react-admin-screens 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +433 -233
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -4
- package/dist/index.d.ts +11 -4
- package/dist/index.js +427 -223
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -307,10 +307,74 @@ function RoutesScreen({ client }) {
|
|
|
307
307
|
] });
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
// src/screens/MigrationsScreen.tsx
|
|
311
|
+
import { useAdminMigrations } from "@fonderie/react-admin";
|
|
312
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
313
|
+
function why(m, everApplied) {
|
|
314
|
+
if (m.pending.length === 0) return null;
|
|
315
|
+
if (m.blockedBy) return `Apply "${m.blockedBy}" first \u2014 it runs before this one and is behind.`;
|
|
316
|
+
if (everApplied && m.pending.some((p) => p.impact === "destructive"))
|
|
317
|
+
return "Contains a migration that deletes data. No down-migration brings it back \u2014 apply this one through CI or `npm run migrate`.";
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
function MigrationsScreen({ client }) {
|
|
321
|
+
const { report, isLoading, error, refresh, apply } = useAdminMigrations(client);
|
|
322
|
+
const behind = report?.modules.filter((m) => m.pending.length > 0) ?? [];
|
|
323
|
+
return /* @__PURE__ */ jsxs7("div", { style: styles.container, children: [
|
|
324
|
+
/* @__PURE__ */ jsx7("h1", { style: styles.title, children: "Migrations" }),
|
|
325
|
+
error ? /* @__PURE__ */ jsx7("p", { style: styles.error, role: "alert", children: error.status === 403 ? "Applying migrations needs a token with the write scope." : error.explanation }) : null,
|
|
326
|
+
/* @__PURE__ */ jsxs7("p", { children: [
|
|
327
|
+
/* @__PURE__ */ jsx7("button", { type: "button", style: styles.button, onClick: () => void refresh(), disabled: isLoading, children: isLoading ? "Checking\u2026" : "Refresh" }),
|
|
328
|
+
" ",
|
|
329
|
+
report && !report.everApplied ? /* @__PURE__ */ jsx7("span", { style: styles.advice, children: "This database has never been migrated \u2014 treating it as a first install, so nothing is held back." }) : null
|
|
330
|
+
] }),
|
|
331
|
+
report && behind.length === 0 ? /* @__PURE__ */ jsx7("p", { style: styles.ok, children: "Every module is up to date." }) : null,
|
|
332
|
+
behind.map((m) => {
|
|
333
|
+
const blocked = why(m, report?.everApplied ?? false);
|
|
334
|
+
const files = m.pending.map((p) => p.file);
|
|
335
|
+
return /* @__PURE__ */ jsxs7("section", { style: { marginBottom: 24 }, children: [
|
|
336
|
+
/* @__PURE__ */ jsxs7("h2", { style: styles.subtitle, children: [
|
|
337
|
+
m.name,
|
|
338
|
+
" \u2014 ",
|
|
339
|
+
m.pending.length,
|
|
340
|
+
" pending"
|
|
341
|
+
] }),
|
|
342
|
+
/* @__PURE__ */ jsx7("ul", { children: m.pending.map((p) => /* @__PURE__ */ jsxs7("li", { children: [
|
|
343
|
+
/* @__PURE__ */ jsx7("code", { children: p.file }),
|
|
344
|
+
" ",
|
|
345
|
+
p.impact === "destructive" ? /* @__PURE__ */ jsx7("span", { style: styles.bad, children: "destructive" }) : /* @__PURE__ */ jsx7("span", { style: styles.advice, children: "additive" }),
|
|
346
|
+
p.destructive.length > 0 ? /* @__PURE__ */ jsx7("ul", { children: p.destructive.map((s) => /* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsx7("code", { children: s.slice(0, 120) }) }, s)) }) : null
|
|
347
|
+
] }, p.file)) }),
|
|
348
|
+
blocked ? /* @__PURE__ */ jsx7("p", { style: styles.advice, children: blocked }) : /* @__PURE__ */ jsxs7(
|
|
349
|
+
"button",
|
|
350
|
+
{
|
|
351
|
+
type: "button",
|
|
352
|
+
style: styles.button,
|
|
353
|
+
disabled: isLoading || !m.appliable,
|
|
354
|
+
onClick: () => {
|
|
355
|
+
if (window.confirm(
|
|
356
|
+
`Apply ${m.pending.length} migration(s) to "${m.name}"? This changes the database schema.`
|
|
357
|
+
))
|
|
358
|
+
void apply(m.name, files).catch(() => {
|
|
359
|
+
});
|
|
360
|
+
},
|
|
361
|
+
children: [
|
|
362
|
+
"Apply ",
|
|
363
|
+
m.pending.length,
|
|
364
|
+
" migration",
|
|
365
|
+
m.pending.length === 1 ? "" : "s"
|
|
366
|
+
]
|
|
367
|
+
}
|
|
368
|
+
)
|
|
369
|
+
] }, m.name);
|
|
370
|
+
})
|
|
371
|
+
] });
|
|
372
|
+
}
|
|
373
|
+
|
|
310
374
|
// src/screens/TokensScreen.tsx
|
|
311
375
|
import { useAdminTokens } from "@fonderie/react-admin";
|
|
312
376
|
import { useState } from "react";
|
|
313
|
-
import { Fragment as Fragment4, jsx as
|
|
377
|
+
import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
314
378
|
var ALL = ["read", "write", "secrets"];
|
|
315
379
|
function TokensScreen({ client }) {
|
|
316
380
|
const { report, isLoading, error, issue, revoke } = useAdminTokens(client);
|
|
@@ -319,25 +383,25 @@ function TokensScreen({ client }) {
|
|
|
319
383
|
const [days, setDays] = useState("");
|
|
320
384
|
const [minted, setMinted] = useState(null);
|
|
321
385
|
const toggle = (s) => setScopes((prev) => prev.includes(s) ? prev.filter((x) => x !== s) : [...prev, s]);
|
|
322
|
-
return /* @__PURE__ */
|
|
323
|
-
/* @__PURE__ */
|
|
324
|
-
error ? /* @__PURE__ */
|
|
325
|
-
/* @__PURE__ */
|
|
326
|
-
report ? /* @__PURE__ */
|
|
327
|
-
report.admin.ok ? /* @__PURE__ */
|
|
328
|
-
report.admin.problems.map((p) => /* @__PURE__ */
|
|
386
|
+
return /* @__PURE__ */ jsxs8("div", { style: styles.container, children: [
|
|
387
|
+
/* @__PURE__ */ jsx8("h1", { style: styles.title, children: "Access" }),
|
|
388
|
+
error ? /* @__PURE__ */ jsx8("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,
|
|
389
|
+
/* @__PURE__ */ jsx8("h2", { style: styles.subtitle, children: "Root token" }),
|
|
390
|
+
report ? /* @__PURE__ */ jsxs8("p", { children: [
|
|
391
|
+
report.admin.ok ? /* @__PURE__ */ jsx8("span", { style: styles.ok, children: "strong" }) : /* @__PURE__ */ jsx8("span", { style: styles.bad, children: "weak" }),
|
|
392
|
+
report.admin.problems.map((p) => /* @__PURE__ */ jsx8("div", { style: styles.bad, children: p.message }, p.message))
|
|
329
393
|
] }) : null,
|
|
330
|
-
/* @__PURE__ */
|
|
331
|
-
report?.issued === null ? /* @__PURE__ */
|
|
332
|
-
minted ? /* @__PURE__ */
|
|
394
|
+
/* @__PURE__ */ jsx8("h2", { style: styles.subtitle, children: "Issued tokens" }),
|
|
395
|
+
report?.issued === null ? /* @__PURE__ */ jsx8("p", { style: styles.muted, children: "Issuing is off \u2014 give AdminModule a store and run its migrations." }) : /* @__PURE__ */ jsxs8(Fragment4, { children: [
|
|
396
|
+
minted ? /* @__PURE__ */ jsxs8("p", { style: styles.ok, children: [
|
|
333
397
|
"Copy this now \u2014 it is never shown again.",
|
|
334
398
|
" ",
|
|
335
|
-
/* @__PURE__ */
|
|
399
|
+
/* @__PURE__ */ jsx8("span", { style: styles.mono, children: minted.token }),
|
|
336
400
|
" (",
|
|
337
401
|
minted.name,
|
|
338
402
|
")"
|
|
339
403
|
] }) : null,
|
|
340
|
-
/* @__PURE__ */
|
|
404
|
+
/* @__PURE__ */ jsxs8(
|
|
341
405
|
"form",
|
|
342
406
|
{
|
|
343
407
|
style: styles.toolbar,
|
|
@@ -356,7 +420,7 @@ function TokensScreen({ client }) {
|
|
|
356
420
|
});
|
|
357
421
|
},
|
|
358
422
|
children: [
|
|
359
|
-
/* @__PURE__ */
|
|
423
|
+
/* @__PURE__ */ jsx8(
|
|
360
424
|
"input",
|
|
361
425
|
{
|
|
362
426
|
value: name,
|
|
@@ -366,12 +430,12 @@ function TokensScreen({ client }) {
|
|
|
366
430
|
"aria-label": "Name"
|
|
367
431
|
}
|
|
368
432
|
),
|
|
369
|
-
ALL.map((s) => /* @__PURE__ */
|
|
370
|
-
/* @__PURE__ */
|
|
433
|
+
ALL.map((s) => /* @__PURE__ */ jsxs8("label", { style: { ...styles.badge, cursor: "pointer" }, children: [
|
|
434
|
+
/* @__PURE__ */ jsx8("input", { type: "checkbox", checked: scopes.includes(s), onChange: () => toggle(s) }),
|
|
371
435
|
" ",
|
|
372
436
|
s
|
|
373
437
|
] }, s)),
|
|
374
|
-
/* @__PURE__ */
|
|
438
|
+
/* @__PURE__ */ jsx8(
|
|
375
439
|
"input",
|
|
376
440
|
{
|
|
377
441
|
value: days,
|
|
@@ -381,7 +445,7 @@ function TokensScreen({ client }) {
|
|
|
381
445
|
"aria-label": "Days"
|
|
382
446
|
}
|
|
383
447
|
),
|
|
384
|
-
/* @__PURE__ */
|
|
448
|
+
/* @__PURE__ */ jsx8(
|
|
385
449
|
"button",
|
|
386
450
|
{
|
|
387
451
|
type: "submit",
|
|
@@ -393,29 +457,29 @@ function TokensScreen({ client }) {
|
|
|
393
457
|
]
|
|
394
458
|
}
|
|
395
459
|
),
|
|
396
|
-
report?.issued && report.issued.length > 0 ? /* @__PURE__ */
|
|
397
|
-
/* @__PURE__ */
|
|
398
|
-
/* @__PURE__ */
|
|
399
|
-
/* @__PURE__ */
|
|
400
|
-
/* @__PURE__ */
|
|
401
|
-
/* @__PURE__ */
|
|
402
|
-
/* @__PURE__ */
|
|
403
|
-
/* @__PURE__ */
|
|
460
|
+
report?.issued && report.issued.length > 0 ? /* @__PURE__ */ jsxs8("table", { style: styles.table, children: [
|
|
461
|
+
/* @__PURE__ */ jsx8("thead", { children: /* @__PURE__ */ jsxs8("tr", { children: [
|
|
462
|
+
/* @__PURE__ */ jsx8("th", { style: styles.th, children: "Name" }),
|
|
463
|
+
/* @__PURE__ */ jsx8("th", { style: styles.th, children: "Scopes" }),
|
|
464
|
+
/* @__PURE__ */ jsx8("th", { style: styles.th, children: "Created" }),
|
|
465
|
+
/* @__PURE__ */ jsx8("th", { style: styles.th, children: "Expires" }),
|
|
466
|
+
/* @__PURE__ */ jsx8("th", { style: styles.th, children: "Last used" }),
|
|
467
|
+
/* @__PURE__ */ jsx8("th", { style: styles.th })
|
|
404
468
|
] }) }),
|
|
405
|
-
/* @__PURE__ */
|
|
406
|
-
/* @__PURE__ */
|
|
469
|
+
/* @__PURE__ */ jsx8("tbody", { children: report.issued.map((t) => /* @__PURE__ */ jsxs8("tr", { children: [
|
|
470
|
+
/* @__PURE__ */ jsxs8("td", { style: styles.td, children: [
|
|
407
471
|
t.name,
|
|
408
|
-
t.revokedAt ? /* @__PURE__ */
|
|
472
|
+
t.revokedAt ? /* @__PURE__ */ jsx8("span", { style: styles.badge, children: " revoked" }) : null
|
|
409
473
|
] }),
|
|
410
|
-
/* @__PURE__ */
|
|
411
|
-
/* @__PURE__ */
|
|
474
|
+
/* @__PURE__ */ jsx8("td", { style: { ...styles.td, ...styles.mono }, children: t.scopes.join(", ") }),
|
|
475
|
+
/* @__PURE__ */ jsxs8("td", { style: { ...styles.td, ...styles.muted }, children: [
|
|
412
476
|
new Date(t.createdAt).toLocaleDateString(),
|
|
413
477
|
" \xB7 ",
|
|
414
478
|
t.createdBy
|
|
415
479
|
] }),
|
|
416
|
-
/* @__PURE__ */
|
|
417
|
-
/* @__PURE__ */
|
|
418
|
-
/* @__PURE__ */
|
|
480
|
+
/* @__PURE__ */ jsx8("td", { style: { ...styles.td, ...styles.muted }, children: t.expiresAt ? new Date(t.expiresAt).toLocaleDateString() : "never" }),
|
|
481
|
+
/* @__PURE__ */ jsx8("td", { style: { ...styles.td, ...styles.muted }, children: t.lastUsedAt ? new Date(t.lastUsedAt).toLocaleString() : "never" }),
|
|
482
|
+
/* @__PURE__ */ jsx8("td", { style: styles.td, children: t.revokedAt ? null : /* @__PURE__ */ jsx8(
|
|
419
483
|
"button",
|
|
420
484
|
{
|
|
421
485
|
type: "button",
|
|
@@ -431,44 +495,55 @@ function TokensScreen({ client }) {
|
|
|
431
495
|
}
|
|
432
496
|
) })
|
|
433
497
|
] }, t.id)) })
|
|
434
|
-
] }) : /* @__PURE__ */
|
|
498
|
+
] }) : /* @__PURE__ */ jsx8("p", { style: styles.muted, children: "None issued." })
|
|
435
499
|
] }),
|
|
436
|
-
/* @__PURE__ */
|
|
437
|
-
report && report.legacy.length === 0 ? /* @__PURE__ */
|
|
438
|
-
/* @__PURE__ */
|
|
500
|
+
/* @__PURE__ */ jsx8("h2", { style: styles.subtitle, children: "Legacy per-brick tokens" }),
|
|
501
|
+
report && report.legacy.length === 0 ? /* @__PURE__ */ jsx8("p", { style: styles.muted, children: "None \u2014 every brick's admin surface goes through this token." }) : /* @__PURE__ */ jsx8("ul", { style: styles.list, children: report?.legacy.map((l) => /* @__PURE__ */ jsxs8("li", { style: styles.row, children: [
|
|
502
|
+
/* @__PURE__ */ jsx8("span", { style: styles.mono, children: l.module }),
|
|
439
503
|
" ",
|
|
440
|
-
/* @__PURE__ */
|
|
504
|
+
/* @__PURE__ */ jsx8("span", { style: styles.muted, children: "still registers its standalone routes with its own token (deprecated)" })
|
|
441
505
|
] }, l.module)) })
|
|
442
506
|
] });
|
|
443
507
|
}
|
|
444
508
|
|
|
445
509
|
// src/screens/UsersScreen.tsx
|
|
446
|
-
import {
|
|
510
|
+
import {
|
|
511
|
+
useAdminLoginHistory,
|
|
512
|
+
useAdminUser,
|
|
513
|
+
useAdminUserSessions,
|
|
514
|
+
useAdminUsers
|
|
515
|
+
} from "@fonderie/react-admin";
|
|
447
516
|
import { useState as useState2 } from "react";
|
|
448
|
-
import { Fragment as Fragment5, jsx as
|
|
449
|
-
function UsersScreen({ client }) {
|
|
517
|
+
import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
518
|
+
function UsersScreen({ client, pageSize = 50 }) {
|
|
450
519
|
const [input, setInput] = useState2("");
|
|
451
|
-
const [
|
|
520
|
+
const [selected, setSelected] = useState2(null);
|
|
521
|
+
const list = useAdminUsers(client, { limit: pageSize });
|
|
452
522
|
const { user, isLoading, error, suspend, unsuspend, revokeSessions } = useAdminUser(
|
|
453
523
|
client,
|
|
454
|
-
|
|
524
|
+
selected ?? {}
|
|
455
525
|
);
|
|
456
526
|
const userId = user?.id ?? null;
|
|
457
527
|
const sessions = useAdminUserSessions(client, userId);
|
|
458
528
|
const history = useAdminLoginHistory(client, userId, { limit: 20 });
|
|
459
|
-
const yesNo = (v) => v ? /* @__PURE__ */
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
529
|
+
const yesNo = (v) => v ? /* @__PURE__ */ jsx9("span", { style: styles.ok, children: "yes" }) : /* @__PURE__ */ jsx9("span", { style: styles.muted, children: "no" });
|
|
530
|
+
const clear = () => {
|
|
531
|
+
setSelected(null);
|
|
532
|
+
setInput("");
|
|
533
|
+
};
|
|
534
|
+
return /* @__PURE__ */ jsxs9("div", { style: styles.container, children: [
|
|
535
|
+
/* @__PURE__ */ jsx9("h1", { style: styles.title, children: "Users" }),
|
|
536
|
+
/* @__PURE__ */ jsxs9(
|
|
463
537
|
"form",
|
|
464
538
|
{
|
|
465
539
|
style: styles.toolbar,
|
|
466
540
|
onSubmit: (e) => {
|
|
467
541
|
e.preventDefault();
|
|
468
|
-
|
|
542
|
+
const email = input.trim();
|
|
543
|
+
setSelected(email ? { email } : null);
|
|
469
544
|
},
|
|
470
545
|
children: [
|
|
471
|
-
/* @__PURE__ */
|
|
546
|
+
/* @__PURE__ */ jsx9(
|
|
472
547
|
"input",
|
|
473
548
|
{
|
|
474
549
|
type: "email",
|
|
@@ -479,53 +554,102 @@ function UsersScreen({ client }) {
|
|
|
479
554
|
"aria-label": "Email"
|
|
480
555
|
}
|
|
481
556
|
),
|
|
482
|
-
/* @__PURE__ */
|
|
557
|
+
/* @__PURE__ */ jsx9("button", { type: "submit", style: styles.button, disabled: isLoading, children: "Look up" }),
|
|
558
|
+
selected ? /* @__PURE__ */ jsx9("button", { type: "button", style: styles.button, onClick: clear, children: "\u2190 All users" }) : /* @__PURE__ */ jsx9(
|
|
559
|
+
"button",
|
|
560
|
+
{
|
|
561
|
+
type: "button",
|
|
562
|
+
style: styles.button,
|
|
563
|
+
onClick: () => void list.refresh(),
|
|
564
|
+
disabled: list.isLoading,
|
|
565
|
+
children: "Refresh"
|
|
566
|
+
}
|
|
567
|
+
)
|
|
483
568
|
]
|
|
484
569
|
}
|
|
485
570
|
),
|
|
486
|
-
error ? /* @__PURE__ */
|
|
487
|
-
|
|
488
|
-
/* @__PURE__ */
|
|
571
|
+
error ? /* @__PURE__ */ jsx9("p", { style: styles.error, role: "alert", children: error.status === 404 ? "No user with that email." : error.explanation }) : null,
|
|
572
|
+
!selected ? /* @__PURE__ */ jsxs9(Fragment5, { children: [
|
|
573
|
+
list.error ? /* @__PURE__ */ jsx9("p", { style: styles.error, role: "alert", children: list.error.explanation }) : null,
|
|
574
|
+
list.users.length === 0 && !list.isLoading ? /* @__PURE__ */ jsx9("p", { style: styles.muted, children: "No users yet." }) : /* @__PURE__ */ jsxs9("table", { style: styles.table, children: [
|
|
575
|
+
/* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsxs9("tr", { children: [
|
|
576
|
+
/* @__PURE__ */ jsx9("th", { style: styles.th, children: "Email" }),
|
|
577
|
+
/* @__PURE__ */ jsx9("th", { style: styles.th, children: "Name" }),
|
|
578
|
+
/* @__PURE__ */ jsx9("th", { style: styles.th, children: "Created" }),
|
|
579
|
+
/* @__PURE__ */ jsx9("th", { style: styles.th, children: "Status" })
|
|
580
|
+
] }) }),
|
|
581
|
+
/* @__PURE__ */ jsx9("tbody", { children: list.users.map((u) => /* @__PURE__ */ jsxs9("tr", { children: [
|
|
582
|
+
/* @__PURE__ */ jsx9("td", { style: styles.td, children: /* @__PURE__ */ jsx9(
|
|
583
|
+
"button",
|
|
584
|
+
{
|
|
585
|
+
type: "button",
|
|
586
|
+
style: { ...styles.navItem, padding: 0 },
|
|
587
|
+
onClick: () => setSelected({ id: u.id }),
|
|
588
|
+
children: u.email
|
|
589
|
+
}
|
|
590
|
+
) }),
|
|
591
|
+
/* @__PURE__ */ jsx9("td", { style: styles.td, children: `${u.firstName ?? ""} ${u.lastName ?? ""}`.trim() || /* @__PURE__ */ jsx9("span", { style: styles.muted, children: "\u2014" }) }),
|
|
592
|
+
/* @__PURE__ */ jsx9("td", { style: styles.td, children: new Date(u.createdAt).toLocaleDateString() }),
|
|
593
|
+
/* @__PURE__ */ jsxs9("td", { style: styles.td, children: [
|
|
594
|
+
u.suspended ? /* @__PURE__ */ jsx9("span", { style: styles.badge, children: "suspended" }) : null,
|
|
595
|
+
u.deletedAt ? /* @__PURE__ */ jsx9("span", { style: styles.badge, children: "deleted" }) : null,
|
|
596
|
+
!u.suspended && !u.deletedAt ? /* @__PURE__ */ jsx9("span", { style: styles.muted, children: "active" }) : null
|
|
597
|
+
] })
|
|
598
|
+
] }, u.id)) })
|
|
599
|
+
] }),
|
|
600
|
+
list.isLoading ? /* @__PURE__ */ jsx9("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
601
|
+
list.hasMore && !list.isLoading ? /* @__PURE__ */ jsx9(
|
|
602
|
+
"button",
|
|
603
|
+
{
|
|
604
|
+
type: "button",
|
|
605
|
+
style: { ...styles.button, marginTop: 8 },
|
|
606
|
+
onClick: () => void list.loadMore(),
|
|
607
|
+
children: "Load more"
|
|
608
|
+
}
|
|
609
|
+
) : null
|
|
610
|
+
] }) : null,
|
|
611
|
+
user ? /* @__PURE__ */ jsxs9(Fragment5, { children: [
|
|
612
|
+
/* @__PURE__ */ jsxs9("h2", { style: styles.subtitle, children: [
|
|
489
613
|
user.firstName || user.lastName ? `${user.firstName} ${user.lastName}`.trim() : user.email,
|
|
490
614
|
" ",
|
|
491
|
-
user.suspended ? /* @__PURE__ */
|
|
492
|
-
user.deletedAt ? /* @__PURE__ */
|
|
615
|
+
user.suspended ? /* @__PURE__ */ jsx9("span", { style: styles.badge, children: "suspended" }) : null,
|
|
616
|
+
user.deletedAt ? /* @__PURE__ */ jsx9("span", { style: styles.badge, children: "deleted" }) : null
|
|
493
617
|
] }),
|
|
494
|
-
/* @__PURE__ */
|
|
495
|
-
/* @__PURE__ */
|
|
496
|
-
/* @__PURE__ */
|
|
497
|
-
/* @__PURE__ */
|
|
618
|
+
/* @__PURE__ */ jsx9("table", { style: styles.table, children: /* @__PURE__ */ jsxs9("tbody", { children: [
|
|
619
|
+
/* @__PURE__ */ jsxs9("tr", { children: [
|
|
620
|
+
/* @__PURE__ */ jsx9("td", { style: styles.td, children: "id" }),
|
|
621
|
+
/* @__PURE__ */ jsx9("td", { style: { ...styles.td, ...styles.mono }, children: user.id })
|
|
498
622
|
] }),
|
|
499
|
-
/* @__PURE__ */
|
|
500
|
-
/* @__PURE__ */
|
|
501
|
-
/* @__PURE__ */
|
|
623
|
+
/* @__PURE__ */ jsxs9("tr", { children: [
|
|
624
|
+
/* @__PURE__ */ jsx9("td", { style: styles.td, children: "email" }),
|
|
625
|
+
/* @__PURE__ */ jsxs9("td", { style: styles.td, children: [
|
|
502
626
|
user.email,
|
|
503
627
|
" \xB7 verified ",
|
|
504
628
|
yesNo(user.isEmailVerified)
|
|
505
629
|
] })
|
|
506
630
|
] }),
|
|
507
|
-
/* @__PURE__ */
|
|
508
|
-
/* @__PURE__ */
|
|
509
|
-
/* @__PURE__ */
|
|
631
|
+
/* @__PURE__ */ jsxs9("tr", { children: [
|
|
632
|
+
/* @__PURE__ */ jsx9("td", { style: styles.td, children: "MFA" }),
|
|
633
|
+
/* @__PURE__ */ jsx9("td", { style: styles.td, children: yesNo(user.mfaEnabled) })
|
|
510
634
|
] }),
|
|
511
|
-
/* @__PURE__ */
|
|
512
|
-
/* @__PURE__ */
|
|
513
|
-
/* @__PURE__ */
|
|
635
|
+
/* @__PURE__ */ jsxs9("tr", { children: [
|
|
636
|
+
/* @__PURE__ */ jsx9("td", { style: styles.td, children: "provider" }),
|
|
637
|
+
/* @__PURE__ */ jsxs9("td", { style: styles.td, children: [
|
|
514
638
|
user.provider || "password",
|
|
515
639
|
user.hasPassword ? "" : " \xB7 no password set"
|
|
516
640
|
] })
|
|
517
641
|
] }),
|
|
518
|
-
/* @__PURE__ */
|
|
519
|
-
/* @__PURE__ */
|
|
520
|
-
/* @__PURE__ */
|
|
642
|
+
/* @__PURE__ */ jsxs9("tr", { children: [
|
|
643
|
+
/* @__PURE__ */ jsx9("td", { style: styles.td, children: "last login" }),
|
|
644
|
+
/* @__PURE__ */ jsx9("td", { style: styles.td, children: user.lastLogin ? new Date(user.lastLogin).toLocaleString() : /* @__PURE__ */ jsx9("span", { style: styles.muted, children: "never" }) })
|
|
521
645
|
] }),
|
|
522
|
-
/* @__PURE__ */
|
|
523
|
-
/* @__PURE__ */
|
|
524
|
-
/* @__PURE__ */
|
|
646
|
+
/* @__PURE__ */ jsxs9("tr", { children: [
|
|
647
|
+
/* @__PURE__ */ jsx9("td", { style: styles.td, children: "created" }),
|
|
648
|
+
/* @__PURE__ */ jsx9("td", { style: styles.td, children: new Date(user.createdAt).toLocaleString() })
|
|
525
649
|
] })
|
|
526
650
|
] }) }),
|
|
527
|
-
/* @__PURE__ */
|
|
528
|
-
user.suspended ? /* @__PURE__ */
|
|
651
|
+
/* @__PURE__ */ jsxs9("div", { style: { ...styles.toolbar, marginTop: 12 }, children: [
|
|
652
|
+
user.suspended ? /* @__PURE__ */ jsx9(
|
|
529
653
|
"button",
|
|
530
654
|
{
|
|
531
655
|
type: "button",
|
|
@@ -534,7 +658,7 @@ function UsersScreen({ client }) {
|
|
|
534
658
|
disabled: isLoading,
|
|
535
659
|
children: "Unsuspend"
|
|
536
660
|
}
|
|
537
|
-
) : /* @__PURE__ */
|
|
661
|
+
) : /* @__PURE__ */ jsx9(
|
|
538
662
|
"button",
|
|
539
663
|
{
|
|
540
664
|
type: "button",
|
|
@@ -544,7 +668,7 @@ function UsersScreen({ client }) {
|
|
|
544
668
|
children: "Suspend"
|
|
545
669
|
}
|
|
546
670
|
),
|
|
547
|
-
/* @__PURE__ */
|
|
671
|
+
/* @__PURE__ */ jsx9(
|
|
548
672
|
"button",
|
|
549
673
|
{
|
|
550
674
|
type: "button",
|
|
@@ -555,26 +679,26 @@ function UsersScreen({ client }) {
|
|
|
555
679
|
}
|
|
556
680
|
)
|
|
557
681
|
] }),
|
|
558
|
-
/* @__PURE__ */
|
|
559
|
-
sessions.sessions.length === 0 ? /* @__PURE__ */
|
|
560
|
-
/* @__PURE__ */
|
|
682
|
+
/* @__PURE__ */ jsx9("h2", { style: styles.subtitle, children: "Live sessions" }),
|
|
683
|
+
sessions.sessions.length === 0 ? /* @__PURE__ */ jsx9("p", { style: styles.muted, children: "None." }) : /* @__PURE__ */ jsx9("ul", { style: styles.list, children: sessions.sessions.map((s) => /* @__PURE__ */ jsxs9("li", { style: styles.row, children: [
|
|
684
|
+
/* @__PURE__ */ jsx9("span", { style: styles.mono, children: s.ipAddress ?? "\u2014" }),
|
|
561
685
|
" ",
|
|
562
|
-
/* @__PURE__ */
|
|
686
|
+
/* @__PURE__ */ jsx9("span", { style: styles.muted, children: s.userAgent ?? "" }),
|
|
563
687
|
" ",
|
|
564
|
-
/* @__PURE__ */
|
|
688
|
+
/* @__PURE__ */ jsxs9("span", { style: styles.muted, children: [
|
|
565
689
|
"since ",
|
|
566
690
|
new Date(s.createdAt).toLocaleString()
|
|
567
691
|
] })
|
|
568
692
|
] }, s.id)) }),
|
|
569
|
-
/* @__PURE__ */
|
|
570
|
-
history.events.length === 0 && !history.isLoading ? /* @__PURE__ */
|
|
571
|
-
/* @__PURE__ */
|
|
693
|
+
/* @__PURE__ */ jsx9("h2", { style: styles.subtitle, children: "Recent sign-ins" }),
|
|
694
|
+
history.events.length === 0 && !history.isLoading ? /* @__PURE__ */ jsx9("p", { style: styles.muted, children: "None recorded." }) : /* @__PURE__ */ jsx9("ul", { style: styles.list, children: history.events.map((e) => /* @__PURE__ */ jsxs9("li", { style: styles.row, children: [
|
|
695
|
+
/* @__PURE__ */ jsx9("span", { style: e.outcome === "success" ? styles.ok : styles.bad, children: e.outcome }),
|
|
572
696
|
" ",
|
|
573
|
-
/* @__PURE__ */
|
|
697
|
+
/* @__PURE__ */ jsx9("span", { style: styles.muted, children: e.method }),
|
|
574
698
|
" ",
|
|
575
|
-
/* @__PURE__ */
|
|
699
|
+
/* @__PURE__ */ jsx9("span", { style: styles.muted, children: new Date(e.createdAt).toLocaleString() })
|
|
576
700
|
] }, e.id)) }),
|
|
577
|
-
history.hasMore && !history.isLoading ? /* @__PURE__ */
|
|
701
|
+
history.hasMore && !history.isLoading ? /* @__PURE__ */ jsx9(
|
|
578
702
|
"button",
|
|
579
703
|
{
|
|
580
704
|
type: "button",
|
|
@@ -589,14 +713,14 @@ function UsersScreen({ client }) {
|
|
|
589
713
|
|
|
590
714
|
// src/screens/CatalogScreen.tsx
|
|
591
715
|
import { useAdminCatalog } from "@fonderie/react-admin";
|
|
592
|
-
import { Fragment as Fragment6, jsx as
|
|
716
|
+
import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
593
717
|
function CatalogScreen({ client }) {
|
|
594
718
|
const { catalog, isLoading, error, refresh, deletePlan } = useAdminCatalog(client);
|
|
595
719
|
const money = (v) => v == null ? "\u2014" : (v / 100).toFixed(2);
|
|
596
|
-
return /* @__PURE__ */
|
|
597
|
-
/* @__PURE__ */
|
|
598
|
-
/* @__PURE__ */
|
|
599
|
-
/* @__PURE__ */
|
|
720
|
+
return /* @__PURE__ */ jsxs10("div", { style: styles.container, children: [
|
|
721
|
+
/* @__PURE__ */ jsxs10("div", { style: styles.toolbar, children: [
|
|
722
|
+
/* @__PURE__ */ jsx10("h1", { style: { ...styles.title, marginBottom: 0 }, children: "Catalog" }),
|
|
723
|
+
/* @__PURE__ */ jsx10(
|
|
600
724
|
"button",
|
|
601
725
|
{
|
|
602
726
|
type: "button",
|
|
@@ -607,37 +731,37 @@ function CatalogScreen({ client }) {
|
|
|
607
731
|
}
|
|
608
732
|
)
|
|
609
733
|
] }),
|
|
610
|
-
error ? /* @__PURE__ */
|
|
611
|
-
isLoading && !catalog ? /* @__PURE__ */
|
|
612
|
-
catalog ? /* @__PURE__ */
|
|
613
|
-
/* @__PURE__ */
|
|
614
|
-
/* @__PURE__ */
|
|
615
|
-
/* @__PURE__ */
|
|
616
|
-
catalog.stored.length === 0 ? /* @__PURE__ */
|
|
617
|
-
/* @__PURE__ */
|
|
618
|
-
/* @__PURE__ */
|
|
619
|
-
/* @__PURE__ */
|
|
620
|
-
/* @__PURE__ */
|
|
621
|
-
/* @__PURE__ */
|
|
622
|
-
/* @__PURE__ */
|
|
623
|
-
/* @__PURE__ */
|
|
624
|
-
/* @__PURE__ */
|
|
734
|
+
error ? /* @__PURE__ */ jsx10("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
|
|
735
|
+
isLoading && !catalog ? /* @__PURE__ */ jsx10("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
736
|
+
catalog ? /* @__PURE__ */ jsxs10(Fragment6, { children: [
|
|
737
|
+
/* @__PURE__ */ jsx10("h2", { style: styles.subtitle, children: "Configured (code)" }),
|
|
738
|
+
/* @__PURE__ */ jsx10("pre", { style: { ...styles.mono, background: "#f9fafb", padding: 12, overflowX: "auto" }, children: JSON.stringify(catalog.configured, null, 2) }),
|
|
739
|
+
/* @__PURE__ */ jsx10("h2", { style: styles.subtitle, children: "Stored (database)" }),
|
|
740
|
+
catalog.stored.length === 0 ? /* @__PURE__ */ jsx10("p", { style: styles.muted, children: "No stored plans." }) : /* @__PURE__ */ jsxs10("table", { style: styles.table, children: [
|
|
741
|
+
/* @__PURE__ */ jsx10("thead", { children: /* @__PURE__ */ jsxs10("tr", { children: [
|
|
742
|
+
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Plan" }),
|
|
743
|
+
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Tier" }),
|
|
744
|
+
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Seats" }),
|
|
745
|
+
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Monthly" }),
|
|
746
|
+
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Yearly" }),
|
|
747
|
+
/* @__PURE__ */ jsx10("th", { style: styles.th, children: "Trial" }),
|
|
748
|
+
/* @__PURE__ */ jsx10("th", { style: styles.th })
|
|
625
749
|
] }) }),
|
|
626
|
-
/* @__PURE__ */
|
|
627
|
-
/* @__PURE__ */
|
|
628
|
-
/* @__PURE__ */
|
|
629
|
-
p.description ? /* @__PURE__ */
|
|
750
|
+
/* @__PURE__ */ jsx10("tbody", { children: catalog.stored.map((p) => /* @__PURE__ */ jsxs10("tr", { children: [
|
|
751
|
+
/* @__PURE__ */ jsxs10("td", { style: styles.td, children: [
|
|
752
|
+
/* @__PURE__ */ jsx10("strong", { children: p.name }),
|
|
753
|
+
p.description ? /* @__PURE__ */ jsx10("div", { style: styles.muted, children: p.description }) : null
|
|
630
754
|
] }),
|
|
631
|
-
/* @__PURE__ */
|
|
632
|
-
/* @__PURE__ */
|
|
633
|
-
/* @__PURE__ */
|
|
755
|
+
/* @__PURE__ */ jsx10("td", { style: styles.td, children: p.tier }),
|
|
756
|
+
/* @__PURE__ */ jsx10("td", { style: styles.td, children: p.seats ?? "\u221E" }),
|
|
757
|
+
/* @__PURE__ */ jsxs10("td", { style: styles.td, children: [
|
|
634
758
|
money(p.pricing?.monthly),
|
|
635
759
|
" ",
|
|
636
760
|
p.pricing?.currency ?? ""
|
|
637
761
|
] }),
|
|
638
|
-
/* @__PURE__ */
|
|
639
|
-
/* @__PURE__ */
|
|
640
|
-
/* @__PURE__ */
|
|
762
|
+
/* @__PURE__ */ jsx10("td", { style: styles.td, children: money(p.pricing?.yearly) }),
|
|
763
|
+
/* @__PURE__ */ jsx10("td", { style: styles.td, children: p.trialDays ? `${p.trialDays} d` : "\u2014" }),
|
|
764
|
+
/* @__PURE__ */ jsx10("td", { style: styles.td, children: /* @__PURE__ */ jsx10(
|
|
641
765
|
"button",
|
|
642
766
|
{
|
|
643
767
|
type: "button",
|
|
@@ -657,20 +781,27 @@ function CatalogScreen({ client }) {
|
|
|
657
781
|
}
|
|
658
782
|
|
|
659
783
|
// src/screens/SubscriberScreen.tsx
|
|
660
|
-
import { useAdminSubscriber } from "@fonderie/react-admin";
|
|
784
|
+
import { useAdminSubscriber, useAdminSubscribers } from "@fonderie/react-admin";
|
|
661
785
|
import { useState as useState3 } from "react";
|
|
662
|
-
import { Fragment as Fragment7, jsx as
|
|
663
|
-
function SubscriberScreen({ client }) {
|
|
786
|
+
import { Fragment as Fragment7, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
787
|
+
function SubscriberScreen({ client, pageSize = 50 }) {
|
|
664
788
|
const [type, setType] = useState3("user");
|
|
665
789
|
const [idInput, setIdInput] = useState3("");
|
|
666
790
|
const [subscriber, setSubscriber] = useState3(null);
|
|
791
|
+
const list = useAdminSubscribers(client, { limit: pageSize });
|
|
667
792
|
const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = useAdminSubscriber(client, subscriber, { limit: 20 });
|
|
668
793
|
const [amount, setAmount] = useState3("");
|
|
669
794
|
const [note, setNote] = useState3("");
|
|
670
795
|
const [granted, setGranted] = useState3(null);
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
796
|
+
const open = (t, id) => {
|
|
797
|
+
setGranted(null);
|
|
798
|
+
setType(t);
|
|
799
|
+
setIdInput(id);
|
|
800
|
+
setSubscriber({ type: t, id });
|
|
801
|
+
};
|
|
802
|
+
return /* @__PURE__ */ jsxs11("div", { style: styles.container, children: [
|
|
803
|
+
/* @__PURE__ */ jsx11("h1", { style: styles.title, children: "Subscriber" }),
|
|
804
|
+
/* @__PURE__ */ jsxs11(
|
|
674
805
|
"form",
|
|
675
806
|
{
|
|
676
807
|
style: styles.toolbar,
|
|
@@ -680,7 +811,7 @@ function SubscriberScreen({ client }) {
|
|
|
680
811
|
setSubscriber(idInput.trim() ? { type, id: idInput.trim() } : null);
|
|
681
812
|
},
|
|
682
813
|
children: [
|
|
683
|
-
/* @__PURE__ */
|
|
814
|
+
/* @__PURE__ */ jsxs11(
|
|
684
815
|
"select",
|
|
685
816
|
{
|
|
686
817
|
value: type,
|
|
@@ -688,12 +819,12 @@ function SubscriberScreen({ client }) {
|
|
|
688
819
|
style: styles.button,
|
|
689
820
|
"aria-label": "Type",
|
|
690
821
|
children: [
|
|
691
|
-
/* @__PURE__ */
|
|
692
|
-
/* @__PURE__ */
|
|
822
|
+
/* @__PURE__ */ jsx11("option", { value: "user", children: "user" }),
|
|
823
|
+
/* @__PURE__ */ jsx11("option", { value: "workspace", children: "workspace" })
|
|
693
824
|
]
|
|
694
825
|
}
|
|
695
826
|
),
|
|
696
|
-
/* @__PURE__ */
|
|
827
|
+
/* @__PURE__ */ jsx11(
|
|
697
828
|
"input",
|
|
698
829
|
{
|
|
699
830
|
value: idInput,
|
|
@@ -703,32 +834,100 @@ function SubscriberScreen({ client }) {
|
|
|
703
834
|
"aria-label": "Subscriber id"
|
|
704
835
|
}
|
|
705
836
|
),
|
|
706
|
-
/* @__PURE__ */
|
|
837
|
+
/* @__PURE__ */ jsx11("button", { type: "submit", style: styles.button, disabled: isLoading, children: "Look up" }),
|
|
838
|
+
subscriber ? /* @__PURE__ */ jsx11(
|
|
839
|
+
"button",
|
|
840
|
+
{
|
|
841
|
+
type: "button",
|
|
842
|
+
style: styles.button,
|
|
843
|
+
onClick: () => {
|
|
844
|
+
setSubscriber(null);
|
|
845
|
+
setIdInput("");
|
|
846
|
+
setGranted(null);
|
|
847
|
+
},
|
|
848
|
+
children: "\u2190 All subscribers"
|
|
849
|
+
}
|
|
850
|
+
) : /* @__PURE__ */ jsx11(
|
|
851
|
+
"button",
|
|
852
|
+
{
|
|
853
|
+
type: "button",
|
|
854
|
+
style: styles.button,
|
|
855
|
+
onClick: () => void list.refresh(),
|
|
856
|
+
disabled: list.isLoading,
|
|
857
|
+
children: "Refresh"
|
|
858
|
+
}
|
|
859
|
+
)
|
|
707
860
|
]
|
|
708
861
|
}
|
|
709
862
|
),
|
|
710
|
-
error ? /* @__PURE__ */
|
|
711
|
-
subscriber
|
|
712
|
-
/* @__PURE__ */
|
|
713
|
-
|
|
714
|
-
/* @__PURE__ */
|
|
715
|
-
/* @__PURE__ */
|
|
716
|
-
/* @__PURE__ */
|
|
717
|
-
|
|
863
|
+
error ? /* @__PURE__ */ jsx11("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
|
|
864
|
+
!subscriber ? /* @__PURE__ */ jsxs11(Fragment7, { children: [
|
|
865
|
+
list.error ? /* @__PURE__ */ jsx11("p", { style: styles.error, role: "alert", children: list.error.explanation }) : null,
|
|
866
|
+
list.subscriptions.length === 0 && !list.isLoading ? /* @__PURE__ */ jsx11("p", { style: styles.muted, children: "No subscribers yet." }) : /* @__PURE__ */ jsxs11("table", { style: styles.table, children: [
|
|
867
|
+
/* @__PURE__ */ jsx11("thead", { children: /* @__PURE__ */ jsxs11("tr", { children: [
|
|
868
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Subscriber" }),
|
|
869
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Plan" }),
|
|
870
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Status" }),
|
|
871
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Renews" })
|
|
872
|
+
] }) }),
|
|
873
|
+
/* @__PURE__ */ jsx11("tbody", { children: list.subscriptions.map((s) => /* @__PURE__ */ jsxs11("tr", { children: [
|
|
874
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: /* @__PURE__ */ jsxs11(
|
|
875
|
+
"button",
|
|
876
|
+
{
|
|
877
|
+
type: "button",
|
|
878
|
+
style: { ...styles.navItem, padding: 0, ...styles.mono },
|
|
879
|
+
onClick: () => open(s.subscriberType, s.subscriberId),
|
|
880
|
+
children: [
|
|
881
|
+
s.subscriberType,
|
|
882
|
+
"/",
|
|
883
|
+
s.subscriberId
|
|
884
|
+
]
|
|
885
|
+
}
|
|
886
|
+
) }),
|
|
887
|
+
/* @__PURE__ */ jsxs11("td", { style: styles.td, children: [
|
|
888
|
+
/* @__PURE__ */ jsx11("strong", { children: s.plan }),
|
|
889
|
+
" \xB7 ",
|
|
890
|
+
s.interval
|
|
891
|
+
] }),
|
|
892
|
+
/* @__PURE__ */ jsxs11("td", { style: styles.td, children: [
|
|
893
|
+
s.status,
|
|
894
|
+
s.cancelAtPeriodEnd ? /* @__PURE__ */ jsx11("span", { style: styles.badge, children: "cancels" }) : null
|
|
895
|
+
] }),
|
|
896
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: s.currentPeriodEnd ? new Date(s.currentPeriodEnd).toLocaleDateString() : /* @__PURE__ */ jsx11("span", { style: styles.muted, children: "\u2014" }) })
|
|
897
|
+
] }, s.id)) })
|
|
898
|
+
] }),
|
|
899
|
+
list.isLoading ? /* @__PURE__ */ jsx11("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
900
|
+
list.hasMore && !list.isLoading ? /* @__PURE__ */ jsx11(
|
|
901
|
+
"button",
|
|
902
|
+
{
|
|
903
|
+
type: "button",
|
|
904
|
+
style: { ...styles.button, marginTop: 8 },
|
|
905
|
+
onClick: () => void list.loadMore(),
|
|
906
|
+
children: "Load more"
|
|
907
|
+
}
|
|
908
|
+
) : null
|
|
909
|
+
] }) : null,
|
|
910
|
+
subscriber && !isLoading ? /* @__PURE__ */ jsxs11(Fragment7, { children: [
|
|
911
|
+
/* @__PURE__ */ jsx11("h2", { style: styles.subtitle, children: "Subscription" }),
|
|
912
|
+
subscription ? /* @__PURE__ */ jsx11("table", { style: styles.table, children: /* @__PURE__ */ jsxs11("tbody", { children: [
|
|
913
|
+
/* @__PURE__ */ jsxs11("tr", { children: [
|
|
914
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: "plan" }),
|
|
915
|
+
/* @__PURE__ */ jsxs11("td", { style: styles.td, children: [
|
|
916
|
+
/* @__PURE__ */ jsx11("strong", { children: subscription.plan }),
|
|
718
917
|
" \xB7 ",
|
|
719
918
|
subscription.interval
|
|
720
919
|
] })
|
|
721
920
|
] }),
|
|
722
|
-
/* @__PURE__ */
|
|
723
|
-
/* @__PURE__ */
|
|
724
|
-
/* @__PURE__ */
|
|
921
|
+
/* @__PURE__ */ jsxs11("tr", { children: [
|
|
922
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: "status" }),
|
|
923
|
+
/* @__PURE__ */ jsxs11("td", { style: styles.td, children: [
|
|
725
924
|
subscription.status,
|
|
726
925
|
subscription.cancelAtPeriodEnd ? " \xB7 cancels at period end" : ""
|
|
727
926
|
] })
|
|
728
927
|
] }),
|
|
729
|
-
/* @__PURE__ */
|
|
730
|
-
/* @__PURE__ */
|
|
731
|
-
/* @__PURE__ */
|
|
928
|
+
/* @__PURE__ */ jsxs11("tr", { children: [
|
|
929
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: "period" }),
|
|
930
|
+
/* @__PURE__ */ jsxs11("td", { style: styles.td, children: [
|
|
732
931
|
subscription.currentPeriodStart ? new Date(subscription.currentPeriodStart).toLocaleDateString() : "\u2014",
|
|
733
932
|
" ",
|
|
734
933
|
"\u2192",
|
|
@@ -736,19 +935,19 @@ function SubscriberScreen({ client }) {
|
|
|
736
935
|
subscription.currentPeriodEnd ? new Date(subscription.currentPeriodEnd).toLocaleDateString() : "\u2014"
|
|
737
936
|
] })
|
|
738
937
|
] }),
|
|
739
|
-
/* @__PURE__ */
|
|
740
|
-
/* @__PURE__ */
|
|
741
|
-
/* @__PURE__ */
|
|
938
|
+
/* @__PURE__ */ jsxs11("tr", { children: [
|
|
939
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: "provider" }),
|
|
940
|
+
/* @__PURE__ */ jsx11("td", { style: { ...styles.td, ...styles.mono }, children: subscription.providerSubscriptionId ?? "\u2014" })
|
|
742
941
|
] })
|
|
743
|
-
] }) }) : /* @__PURE__ */
|
|
744
|
-
/* @__PURE__ */
|
|
745
|
-
wallet ? /* @__PURE__ */
|
|
746
|
-
/* @__PURE__ */
|
|
747
|
-
/* @__PURE__ */
|
|
942
|
+
] }) }) : /* @__PURE__ */ jsx11("p", { style: styles.muted, children: "No subscription." }),
|
|
943
|
+
/* @__PURE__ */ jsx11("h2", { style: styles.subtitle, children: "Wallet" }),
|
|
944
|
+
wallet ? /* @__PURE__ */ jsxs11(Fragment7, { children: [
|
|
945
|
+
/* @__PURE__ */ jsxs11("p", { children: [
|
|
946
|
+
/* @__PURE__ */ jsx11("strong", { children: wallet.balance }),
|
|
748
947
|
" ",
|
|
749
948
|
wallet.currency,
|
|
750
949
|
" ",
|
|
751
|
-
/* @__PURE__ */
|
|
950
|
+
/* @__PURE__ */ jsxs11("span", { style: styles.muted, children: [
|
|
752
951
|
"(granted ",
|
|
753
952
|
wallet.granted ?? "0",
|
|
754
953
|
" \xB7 purchased ",
|
|
@@ -757,7 +956,7 @@ function SubscriberScreen({ client }) {
|
|
|
757
956
|
wallet.precision
|
|
758
957
|
] })
|
|
759
958
|
] }),
|
|
760
|
-
/* @__PURE__ */
|
|
959
|
+
/* @__PURE__ */ jsxs11(
|
|
761
960
|
"form",
|
|
762
961
|
{
|
|
763
962
|
style: styles.toolbar,
|
|
@@ -776,7 +975,7 @@ function SubscriberScreen({ client }) {
|
|
|
776
975
|
});
|
|
777
976
|
},
|
|
778
977
|
children: [
|
|
779
|
-
/* @__PURE__ */
|
|
978
|
+
/* @__PURE__ */ jsx11(
|
|
780
979
|
"input",
|
|
781
980
|
{
|
|
782
981
|
value: amount,
|
|
@@ -786,7 +985,7 @@ function SubscriberScreen({ client }) {
|
|
|
786
985
|
"aria-label": "Amount"
|
|
787
986
|
}
|
|
788
987
|
),
|
|
789
|
-
/* @__PURE__ */
|
|
988
|
+
/* @__PURE__ */ jsx11(
|
|
790
989
|
"input",
|
|
791
990
|
{
|
|
792
991
|
value: note,
|
|
@@ -796,27 +995,27 @@ function SubscriberScreen({ client }) {
|
|
|
796
995
|
"aria-label": "Reason"
|
|
797
996
|
}
|
|
798
997
|
),
|
|
799
|
-
/* @__PURE__ */
|
|
800
|
-
granted ? /* @__PURE__ */
|
|
998
|
+
/* @__PURE__ */ jsx11("button", { type: "submit", style: styles.button, disabled: !/^\d+$/.test(amount.trim()), children: "Grant" }),
|
|
999
|
+
granted ? /* @__PURE__ */ jsx11("span", { style: styles.ok, children: granted }) : null
|
|
801
1000
|
]
|
|
802
1001
|
}
|
|
803
1002
|
),
|
|
804
|
-
/* @__PURE__ */
|
|
805
|
-
ledger.length === 0 ? /* @__PURE__ */
|
|
806
|
-
/* @__PURE__ */
|
|
807
|
-
/* @__PURE__ */
|
|
808
|
-
/* @__PURE__ */
|
|
809
|
-
/* @__PURE__ */
|
|
810
|
-
/* @__PURE__ */
|
|
1003
|
+
/* @__PURE__ */ jsx11("h2", { style: styles.subtitle, children: "Ledger" }),
|
|
1004
|
+
ledger.length === 0 ? /* @__PURE__ */ jsx11("p", { style: styles.muted, children: "Nothing moved yet." }) : /* @__PURE__ */ jsxs11("table", { style: styles.table, children: [
|
|
1005
|
+
/* @__PURE__ */ jsx11("thead", { children: /* @__PURE__ */ jsxs11("tr", { children: [
|
|
1006
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "When" }),
|
|
1007
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Kind" }),
|
|
1008
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Amount" }),
|
|
1009
|
+
/* @__PURE__ */ jsx11("th", { style: styles.th, children: "Note" })
|
|
811
1010
|
] }) }),
|
|
812
|
-
/* @__PURE__ */
|
|
813
|
-
/* @__PURE__ */
|
|
814
|
-
/* @__PURE__ */
|
|
815
|
-
/* @__PURE__ */
|
|
816
|
-
/* @__PURE__ */
|
|
1011
|
+
/* @__PURE__ */ jsx11("tbody", { children: ledger.map((t) => /* @__PURE__ */ jsxs11("tr", { children: [
|
|
1012
|
+
/* @__PURE__ */ jsx11("td", { style: { ...styles.td, ...styles.muted }, children: new Date(t.createdAt).toLocaleString() }),
|
|
1013
|
+
/* @__PURE__ */ jsx11("td", { style: styles.td, children: t.type }),
|
|
1014
|
+
/* @__PURE__ */ jsx11("td", { style: { ...styles.td, ...styles.mono }, children: t.amount }),
|
|
1015
|
+
/* @__PURE__ */ jsx11("td", { style: { ...styles.td, ...styles.muted }, children: t.description ?? "" })
|
|
817
1016
|
] }, t.id)) })
|
|
818
1017
|
] }),
|
|
819
|
-
hasMoreLedger ? /* @__PURE__ */
|
|
1018
|
+
hasMoreLedger ? /* @__PURE__ */ jsx11(
|
|
820
1019
|
"button",
|
|
821
1020
|
{
|
|
822
1021
|
type: "button",
|
|
@@ -825,7 +1024,7 @@ function SubscriberScreen({ client }) {
|
|
|
825
1024
|
children: "Load more"
|
|
826
1025
|
}
|
|
827
1026
|
) : null
|
|
828
|
-
] }) : /* @__PURE__ */
|
|
1027
|
+
] }) : /* @__PURE__ */ jsx11("p", { style: styles.muted, children: "No wallet \u2014 billing has no wallet configuration." })
|
|
829
1028
|
] }) : null
|
|
830
1029
|
] });
|
|
831
1030
|
}
|
|
@@ -833,7 +1032,7 @@ function SubscriberScreen({ client }) {
|
|
|
833
1032
|
// src/screens/AuditScreen.tsx
|
|
834
1033
|
import { useAdminAudit } from "@fonderie/react-admin";
|
|
835
1034
|
import { useState as useState4 } from "react";
|
|
836
|
-
import { jsx as
|
|
1035
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
837
1036
|
function AuditScreen({ client, pageSize = 50 }) {
|
|
838
1037
|
const [draft, setDraft] = useState4({ workspaceId: "", type: "", actorId: "" });
|
|
839
1038
|
const [filter, setFilter] = useState4(
|
|
@@ -843,7 +1042,7 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
843
1042
|
...filter,
|
|
844
1043
|
limit: pageSize
|
|
845
1044
|
});
|
|
846
|
-
const field = (key, placeholder) => /* @__PURE__ */
|
|
1045
|
+
const field = (key, placeholder) => /* @__PURE__ */ jsx12(
|
|
847
1046
|
"input",
|
|
848
1047
|
{
|
|
849
1048
|
value: draft[key],
|
|
@@ -853,9 +1052,9 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
853
1052
|
"aria-label": placeholder
|
|
854
1053
|
}
|
|
855
1054
|
);
|
|
856
|
-
return /* @__PURE__ */
|
|
857
|
-
/* @__PURE__ */
|
|
858
|
-
/* @__PURE__ */
|
|
1055
|
+
return /* @__PURE__ */ jsxs12("div", { style: styles.container, children: [
|
|
1056
|
+
/* @__PURE__ */ jsx12("h1", { style: styles.title, children: "Audit" }),
|
|
1057
|
+
/* @__PURE__ */ jsxs12(
|
|
859
1058
|
"form",
|
|
860
1059
|
{
|
|
861
1060
|
style: styles.toolbar,
|
|
@@ -871,8 +1070,8 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
871
1070
|
field("workspaceId", "workspace id (all if empty)"),
|
|
872
1071
|
field("type", "event type"),
|
|
873
1072
|
field("actorId", "actor id"),
|
|
874
|
-
/* @__PURE__ */
|
|
875
|
-
/* @__PURE__ */
|
|
1073
|
+
/* @__PURE__ */ jsx12("button", { type: "submit", style: styles.button, disabled: isLoading, children: "Filter" }),
|
|
1074
|
+
/* @__PURE__ */ jsx12(
|
|
876
1075
|
"button",
|
|
877
1076
|
{
|
|
878
1077
|
type: "button",
|
|
@@ -885,25 +1084,25 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
885
1084
|
]
|
|
886
1085
|
}
|
|
887
1086
|
),
|
|
888
|
-
error ? /* @__PURE__ */
|
|
889
|
-
events.length === 0 && !isLoading ? /* @__PURE__ */
|
|
890
|
-
/* @__PURE__ */
|
|
891
|
-
/* @__PURE__ */
|
|
892
|
-
/* @__PURE__ */
|
|
893
|
-
/* @__PURE__ */
|
|
894
|
-
/* @__PURE__ */
|
|
895
|
-
/* @__PURE__ */
|
|
1087
|
+
error ? /* @__PURE__ */ jsx12("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
|
|
1088
|
+
events.length === 0 && !isLoading ? /* @__PURE__ */ jsx12("p", { style: styles.muted, children: "Nothing recorded for this filter." }) : /* @__PURE__ */ jsxs12("table", { style: styles.table, children: [
|
|
1089
|
+
/* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsxs12("tr", { children: [
|
|
1090
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "When" }),
|
|
1091
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Type" }),
|
|
1092
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Workspace" }),
|
|
1093
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Actor" }),
|
|
1094
|
+
/* @__PURE__ */ jsx12("th", { style: styles.th, children: "Request" })
|
|
896
1095
|
] }) }),
|
|
897
|
-
/* @__PURE__ */
|
|
898
|
-
/* @__PURE__ */
|
|
899
|
-
/* @__PURE__ */
|
|
900
|
-
/* @__PURE__ */
|
|
901
|
-
/* @__PURE__ */
|
|
902
|
-
/* @__PURE__ */
|
|
1096
|
+
/* @__PURE__ */ jsx12("tbody", { children: events.map((e) => /* @__PURE__ */ jsxs12("tr", { children: [
|
|
1097
|
+
/* @__PURE__ */ jsx12("td", { style: { ...styles.td, ...styles.muted }, children: new Date(e.createdAt).toLocaleString() }),
|
|
1098
|
+
/* @__PURE__ */ jsx12("td", { style: { ...styles.td, ...styles.mono }, children: e.type }),
|
|
1099
|
+
/* @__PURE__ */ jsx12("td", { style: { ...styles.td, ...styles.mono }, children: String(e.payload["workspaceId"] ?? "\u2014") }),
|
|
1100
|
+
/* @__PURE__ */ jsx12("td", { style: { ...styles.td, ...styles.mono }, children: e.actorId ?? "\u2014" }),
|
|
1101
|
+
/* @__PURE__ */ jsx12("td", { style: { ...styles.td, ...styles.muted }, children: e.requestId ?? "" })
|
|
903
1102
|
] }, e.id)) })
|
|
904
1103
|
] }),
|
|
905
|
-
isLoading ? /* @__PURE__ */
|
|
906
|
-
hasMore && !isLoading ? /* @__PURE__ */
|
|
1104
|
+
isLoading ? /* @__PURE__ */ jsx12("p", { style: styles.status, children: "Loading\u2026" }) : null,
|
|
1105
|
+
hasMore && !isLoading ? /* @__PURE__ */ jsx12(
|
|
907
1106
|
"button",
|
|
908
1107
|
{
|
|
909
1108
|
type: "button",
|
|
@@ -916,7 +1115,7 @@ function AuditScreen({ client, pageSize = 50 }) {
|
|
|
916
1115
|
}
|
|
917
1116
|
|
|
918
1117
|
// src/screens/AdminShell.tsx
|
|
919
|
-
import { Fragment as Fragment8, jsx as
|
|
1118
|
+
import { Fragment as Fragment8, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
920
1119
|
var NAV = [
|
|
921
1120
|
{ group: "Today", items: [{ page: "attention", label: "Attention" }] },
|
|
922
1121
|
{
|
|
@@ -943,7 +1142,8 @@ var NAV = [
|
|
|
943
1142
|
items: [
|
|
944
1143
|
{ page: "audit", label: "Audit", needs: "audit" },
|
|
945
1144
|
{ page: "log", label: "Admin log" },
|
|
946
|
-
{ page: "tokens", label: "Access" }
|
|
1145
|
+
{ page: "tokens", label: "Access" },
|
|
1146
|
+
{ page: "migrations", label: "Migrations" }
|
|
947
1147
|
]
|
|
948
1148
|
}
|
|
949
1149
|
];
|
|
@@ -975,42 +1175,45 @@ function AdminShell({
|
|
|
975
1175
|
let body;
|
|
976
1176
|
switch (current) {
|
|
977
1177
|
case "attention":
|
|
978
|
-
body = /* @__PURE__ */
|
|
1178
|
+
body = /* @__PURE__ */ jsx13(AttentionScreen, { client });
|
|
979
1179
|
break;
|
|
980
1180
|
case "modules":
|
|
981
|
-
body = /* @__PURE__ */
|
|
1181
|
+
body = /* @__PURE__ */ jsx13(ModulesScreen, { client });
|
|
982
1182
|
break;
|
|
983
1183
|
case "doctor":
|
|
984
|
-
body = /* @__PURE__ */
|
|
1184
|
+
body = /* @__PURE__ */ jsx13(DoctorScreen, { client });
|
|
985
1185
|
break;
|
|
986
1186
|
case "config":
|
|
987
|
-
body = /* @__PURE__ */
|
|
1187
|
+
body = /* @__PURE__ */ jsx13(ConfigScreen, { client });
|
|
988
1188
|
break;
|
|
989
1189
|
case "routes":
|
|
990
|
-
body = /* @__PURE__ */
|
|
1190
|
+
body = /* @__PURE__ */ jsx13(RoutesScreen, { client });
|
|
991
1191
|
break;
|
|
992
1192
|
case "tokens":
|
|
993
|
-
body = /* @__PURE__ */
|
|
1193
|
+
body = /* @__PURE__ */ jsx13(TokensScreen, { client });
|
|
1194
|
+
break;
|
|
1195
|
+
case "migrations":
|
|
1196
|
+
body = /* @__PURE__ */ jsx13(MigrationsScreen, { client });
|
|
994
1197
|
break;
|
|
995
1198
|
case "log":
|
|
996
|
-
body = /* @__PURE__ */
|
|
1199
|
+
body = /* @__PURE__ */ jsx13(AdminLogScreen, { client });
|
|
997
1200
|
break;
|
|
998
1201
|
case "catalog":
|
|
999
|
-
body = billingClient ? /* @__PURE__ */
|
|
1202
|
+
body = billingClient ? /* @__PURE__ */ jsx13(CatalogScreen, { client: billingClient }) : /* @__PURE__ */ jsx13("p", { style: styles.status, children: "Pass a BillingAdminClient to see the catalog here." });
|
|
1000
1203
|
break;
|
|
1001
1204
|
case "subscriber":
|
|
1002
|
-
body = billingClient ? /* @__PURE__ */
|
|
1205
|
+
body = billingClient ? /* @__PURE__ */ jsx13(SubscriberScreen, { client: billingClient }) : /* @__PURE__ */ jsx13("p", { style: styles.status, children: "Pass a BillingAdminClient to look up subscribers here." });
|
|
1003
1206
|
break;
|
|
1004
1207
|
case "audit":
|
|
1005
|
-
body = auditClient ? /* @__PURE__ */
|
|
1208
|
+
body = auditClient ? /* @__PURE__ */ jsx13(AuditScreen, { client: auditClient }) : /* @__PURE__ */ jsx13("p", { style: styles.status, children: "Pass an AuditAdminClient to see the audit trail here." });
|
|
1006
1209
|
break;
|
|
1007
1210
|
case "users":
|
|
1008
|
-
body = authClient ? /* @__PURE__ */
|
|
1211
|
+
body = authClient ? /* @__PURE__ */ jsx13(UsersScreen, { client: authClient }) : /* @__PURE__ */ jsx13("p", { style: styles.status, children: "Pass an AuthAdminClient to look up users here." });
|
|
1009
1212
|
break;
|
|
1010
1213
|
case "settings":
|
|
1011
|
-
body = !configClient ? /* @__PURE__ */
|
|
1012
|
-
/* @__PURE__ */
|
|
1013
|
-
/* @__PURE__ */
|
|
1214
|
+
body = !configClient ? /* @__PURE__ */ jsx13("p", { style: styles.status, children: "Pass a ConfigAdminClient to manage config and secrets here." }) : editing && editing.kind !== "template" ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1215
|
+
/* @__PURE__ */ jsx13("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
|
|
1216
|
+
/* @__PURE__ */ jsx13(
|
|
1014
1217
|
ConfigEditorScreen,
|
|
1015
1218
|
{
|
|
1016
1219
|
client: configClient,
|
|
@@ -1020,7 +1223,7 @@ function AdminShell({
|
|
|
1020
1223
|
onSaved: () => setEditing(null)
|
|
1021
1224
|
}
|
|
1022
1225
|
)
|
|
1023
|
-
] }) : /* @__PURE__ */
|
|
1226
|
+
] }) : /* @__PURE__ */ jsx13(
|
|
1024
1227
|
ConfigListScreen,
|
|
1025
1228
|
{
|
|
1026
1229
|
client: configClient,
|
|
@@ -1031,9 +1234,9 @@ function AdminShell({
|
|
|
1031
1234
|
);
|
|
1032
1235
|
break;
|
|
1033
1236
|
case "templates":
|
|
1034
|
-
body = !courierClient ? /* @__PURE__ */
|
|
1035
|
-
/* @__PURE__ */
|
|
1036
|
-
/* @__PURE__ */
|
|
1237
|
+
body = !courierClient ? /* @__PURE__ */ jsx13("p", { style: styles.status, children: "Pass a CourierAdminClient to manage templates here." }) : editing?.kind === "template" ? /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1238
|
+
/* @__PURE__ */ jsx13("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
|
|
1239
|
+
/* @__PURE__ */ jsx13(
|
|
1037
1240
|
TemplateEditorScreen,
|
|
1038
1241
|
{
|
|
1039
1242
|
client: courierClient,
|
|
@@ -1041,7 +1244,7 @@ function AdminShell({
|
|
|
1041
1244
|
onSaved: () => setEditing(null)
|
|
1042
1245
|
}
|
|
1043
1246
|
)
|
|
1044
|
-
] }) : /* @__PURE__ */
|
|
1247
|
+
] }) : /* @__PURE__ */ jsx13(
|
|
1045
1248
|
TemplateListScreen,
|
|
1046
1249
|
{
|
|
1047
1250
|
client: courierClient,
|
|
@@ -1050,13 +1253,13 @@ function AdminShell({
|
|
|
1050
1253
|
);
|
|
1051
1254
|
break;
|
|
1052
1255
|
}
|
|
1053
|
-
return /* @__PURE__ */
|
|
1054
|
-
/* @__PURE__ */
|
|
1256
|
+
return /* @__PURE__ */ jsxs13("div", { style: styles.shell, children: [
|
|
1257
|
+
/* @__PURE__ */ jsx13("nav", { style: styles.nav, "aria-label": "Admin", children: NAV.map((g) => {
|
|
1055
1258
|
const items = g.items.filter((i) => !i.needs || has[i.needs]);
|
|
1056
1259
|
if (items.length === 0) return null;
|
|
1057
|
-
return /* @__PURE__ */
|
|
1058
|
-
/* @__PURE__ */
|
|
1059
|
-
items.map((i) => /* @__PURE__ */
|
|
1260
|
+
return /* @__PURE__ */ jsxs13("div", { children: [
|
|
1261
|
+
/* @__PURE__ */ jsx13("div", { style: styles.navGroup, children: g.group }),
|
|
1262
|
+
items.map((i) => /* @__PURE__ */ jsx13(
|
|
1060
1263
|
"button",
|
|
1061
1264
|
{
|
|
1062
1265
|
type: "button",
|
|
@@ -1072,7 +1275,7 @@ function AdminShell({
|
|
|
1072
1275
|
))
|
|
1073
1276
|
] }, g.group);
|
|
1074
1277
|
}) }),
|
|
1075
|
-
/* @__PURE__ */
|
|
1278
|
+
/* @__PURE__ */ jsx13("main", { style: styles.main, children: body })
|
|
1076
1279
|
] });
|
|
1077
1280
|
}
|
|
1078
1281
|
export {
|
|
@@ -1083,6 +1286,7 @@ export {
|
|
|
1083
1286
|
CatalogScreen,
|
|
1084
1287
|
ConfigScreen,
|
|
1085
1288
|
DoctorScreen,
|
|
1289
|
+
MigrationsScreen,
|
|
1086
1290
|
ModulesScreen,
|
|
1087
1291
|
RoutesScreen,
|
|
1088
1292
|
SubscriberScreen,
|