@fonderie/vue-admin-screens 0.4.0 → 0.6.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.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/screens/AdminShell.ts
2
2
  import { ConfigEditorScreen, ConfigListScreen } from "@fonderie/vue-config-admin-screens";
3
3
  import { TemplateEditorScreen, TemplateListScreen } from "@fonderie/vue-courier-admin-screens";
4
- import { computed as computed2, defineComponent as defineComponent12, h as h13, ref as ref4 } from "vue";
4
+ import { computed as computed2, defineComponent as defineComponent12, h as h13, ref as ref5 } from "vue";
5
5
 
6
6
  // src/styles.ts
7
7
  var styles = {
@@ -381,18 +381,29 @@ var RoutesScreen = defineComponent6({
381
381
 
382
382
  // src/screens/TokensScreen.ts
383
383
  import { useAdminTokens } from "@fonderie/vue-admin";
384
- import { defineComponent as defineComponent7, h as h8 } from "vue";
384
+ import { defineComponent as defineComponent7, h as h8, ref } from "vue";
385
+ var ALL = ["read", "write", "secrets"];
385
386
  var TokensScreen = defineComponent7({
386
387
  name: "FonderieTokensScreen",
387
388
  props: { client: { type: Object, required: true } },
388
389
  setup(props) {
389
- const { report, isLoading, error } = useAdminTokens(props.client);
390
- return () => page("Access", { isLoading, error }, () => {
390
+ const { report, isLoading, error, issue, revoke } = useAdminTokens(props.client);
391
+ const name = ref("");
392
+ const scopes = ref(["read"]);
393
+ const days = ref("");
394
+ const minted = ref(null);
395
+ const toggle = (s) => scopes.value = scopes.value.includes(s) ? scopes.value.filter((x) => x !== s) : [...scopes.value, s];
396
+ return () => {
391
397
  const r = report.value;
392
- if (!r) return null;
393
- return [
394
- h8("h2", { style: styles.subtitle }, "Admin token"),
395
- h8("p", [
398
+ return h8("div", { style: styles.container }, [
399
+ h8("h1", { style: styles.title }, "Access"),
400
+ error.value ? h8(
401
+ "p",
402
+ { style: styles.error, role: "alert" },
403
+ error.value.status === 401 ? "Issuing and revoking need the root token (the one in your deployment config)." : error.value.explanation
404
+ ) : null,
405
+ h8("h2", { style: styles.subtitle }, "Root token"),
406
+ r ? h8("p", [
396
407
  h8(
397
408
  "span",
398
409
  { style: r.admin.ok ? styles.ok : styles.bad },
@@ -401,16 +412,124 @@ var TokensScreen = defineComponent7({
401
412
  ...r.admin.problems.map(
402
413
  (p) => h8("div", { key: p.message, style: styles.bad }, p.message)
403
414
  )
404
- ]),
415
+ ]) : null,
416
+ h8("h2", { style: styles.subtitle }, "Issued tokens"),
417
+ r?.issued === null ? h8(
418
+ "p",
419
+ { style: styles.muted },
420
+ "Issuing is off \u2014 give AdminModule a store and run its migrations."
421
+ ) : [
422
+ minted.value ? h8("p", { style: styles.ok }, [
423
+ "Copy this now \u2014 it is never shown again. ",
424
+ h8("span", { style: styles.mono }, minted.value.token),
425
+ ` (${minted.value.name})`
426
+ ]) : null,
427
+ h8(
428
+ "form",
429
+ {
430
+ style: styles.toolbar,
431
+ onSubmit: (e) => {
432
+ e.preventDefault();
433
+ const d = Number(days.value);
434
+ void issue({
435
+ name: name.value.trim(),
436
+ scopes: scopes.value,
437
+ ...days.value && Number.isInteger(d) && d > 0 ? { expiresInDays: d } : {}
438
+ }).then((t) => {
439
+ minted.value = { name: t.name, token: t.token };
440
+ name.value = "";
441
+ days.value = "";
442
+ }).catch(() => {
443
+ });
444
+ }
445
+ },
446
+ [
447
+ h8("input", {
448
+ value: name.value,
449
+ onInput: (e) => name.value = e.target.value,
450
+ placeholder: "name",
451
+ style: { ...styles.button, cursor: "text" },
452
+ "aria-label": "Name"
453
+ }),
454
+ ...ALL.map(
455
+ (s) => h8("label", { key: s, style: { ...styles.badge, cursor: "pointer" } }, [
456
+ h8("input", {
457
+ type: "checkbox",
458
+ checked: scopes.value.includes(s),
459
+ onChange: () => toggle(s)
460
+ }),
461
+ ` ${s}`
462
+ ])
463
+ ),
464
+ h8("input", {
465
+ value: days.value,
466
+ onInput: (e) => days.value = e.target.value,
467
+ placeholder: "days (optional)",
468
+ style: { ...styles.button, cursor: "text", width: "140px" },
469
+ "aria-label": "Days"
470
+ }),
471
+ h8(
472
+ "button",
473
+ {
474
+ type: "submit",
475
+ style: styles.button,
476
+ disabled: !name.value.trim() || scopes.value.length === 0 || isLoading.value
477
+ },
478
+ "Issue"
479
+ )
480
+ ]
481
+ ),
482
+ r?.issued && r.issued.length > 0 ? table(
483
+ ["Name", "Scopes", "Created", "Expires", "Last used", ""],
484
+ r.issued.map(
485
+ (t) => h8("tr", { key: t.id }, [
486
+ td([
487
+ t.name,
488
+ t.revokedAt ? h8("span", { style: styles.badge }, " revoked") : ""
489
+ ]),
490
+ td(t.scopes.join(", "), styles.mono),
491
+ td(
492
+ `${new Date(t.createdAt).toLocaleDateString()} \xB7 ${t.createdBy}`,
493
+ styles.muted
494
+ ),
495
+ td(
496
+ t.expiresAt ? new Date(t.expiresAt).toLocaleDateString() : "never",
497
+ styles.muted
498
+ ),
499
+ td(
500
+ t.lastUsedAt ? new Date(t.lastUsedAt).toLocaleString() : "never",
501
+ styles.muted
502
+ ),
503
+ td(
504
+ t.revokedAt ? "" : h8(
505
+ "button",
506
+ {
507
+ type: "button",
508
+ style: styles.button,
509
+ onClick: () => {
510
+ if (window.confirm(
511
+ `Revoke "${t.name}"? Anything using it stops working immediately.`
512
+ ))
513
+ void revoke(t.id).catch(() => {
514
+ });
515
+ }
516
+ },
517
+ "Revoke"
518
+ )
519
+ )
520
+ ])
521
+ )
522
+ ) : h8("p", { style: styles.muted }, "None issued.")
523
+ ],
405
524
  h8("h2", { style: styles.subtitle }, "Legacy per-brick tokens"),
406
- r.legacy.length === 0 ? h8(
525
+ r && r.legacy.length === 0 ? h8(
407
526
  "p",
408
527
  { style: styles.muted },
409
528
  "None \u2014 every brick's admin surface goes through this token."
410
529
  ) : h8(
411
530
  "ul",
412
531
  { style: styles.list },
413
- r.legacy.map(
532
+ (r?.legacy ?? []).map(
414
533
  (l) => h8("li", { key: l.module, style: styles.row }, [
415
534
  h8("span", { style: styles.mono }, l.module),
416
535
  " ",
@@ -422,29 +541,45 @@ var TokensScreen = defineComponent7({
422
541
  ])
423
542
  )
424
543
  )
425
- ];
426
- });
544
+ ]);
545
+ };
427
546
  }
428
547
  });
429
548
 
430
549
  // src/screens/UsersScreen.ts
431
- import { useAdminLoginHistory, useAdminUser, useAdminUserSessions } from "@fonderie/vue-admin";
432
- import { computed, defineComponent as defineComponent8, h as h9, ref } from "vue";
550
+ import {
551
+ useAdminLoginHistory,
552
+ useAdminUser,
553
+ useAdminUserSessions,
554
+ useAdminUsers
555
+ } from "@fonderie/vue-admin";
556
+ import { computed, defineComponent as defineComponent8, h as h9, ref as ref2 } from "vue";
433
557
  var UsersScreen = defineComponent8({
434
558
  name: "FonderieUsersScreen",
435
- props: { client: { type: Object, required: true } },
559
+ props: {
560
+ client: { type: Object, required: true },
561
+ pageSize: { type: Number, default: 50 }
562
+ },
436
563
  setup(props) {
437
- const input = ref("");
438
- const email = ref("");
564
+ const input = ref2("");
565
+ const email = ref2("");
566
+ const selectedId = ref2("");
567
+ const list = useAdminUsers(props.client, { limit: props.pageSize });
439
568
  const { user, isLoading, error, suspend, unsuspend, revokeSessions } = useAdminUser(
440
569
  props.client,
441
- { email }
570
+ { email, id: selectedId }
442
571
  );
443
572
  const userId = computed(() => user.value?.id ?? null);
444
573
  const sessions = useAdminUserSessions(props.client, userId);
445
574
  const history = useAdminLoginHistory(props.client, userId, { limit: 20 });
575
+ const showList = computed(() => !email.value && !selectedId.value);
446
576
  const yesNo = (v) => h9("span", { style: v ? styles.ok : styles.muted }, v ? "yes" : "no");
447
577
  const row = (k, v) => h9("tr", [td(k), td(v)]);
578
+ const clear = () => {
579
+ email.value = "";
580
+ selectedId.value = "";
581
+ input.value = "";
582
+ };
448
583
  return () => {
449
584
  const u = user.value;
450
585
  return h9("div", { style: styles.container }, [
@@ -455,6 +590,7 @@ var UsersScreen = defineComponent8({
455
590
  style: styles.toolbar,
456
591
  onSubmit: (e) => {
457
592
  e.preventDefault();
593
+ selectedId.value = "";
458
594
  email.value = input.value.trim();
459
595
  }
460
596
  },
@@ -471,9 +607,48 @@ var UsersScreen = defineComponent8({
471
607
  "button",
472
608
  { type: "submit", style: styles.button, disabled: isLoading.value },
473
609
  "Look up"
474
- )
610
+ ),
611
+ showList.value ? refreshButton("Refresh", list.isLoading.value, () => void list.refresh()) : refreshButton("\u2190 All users", false, clear)
475
612
  ]
476
613
  ),
614
+ showList.value ? h9("div", [
615
+ list.error.value ? h9("p", { style: styles.error, role: "alert" }, list.error.value.explanation) : null,
616
+ list.users.value.length === 0 && !list.isLoading.value ? h9("p", { style: styles.muted }, "No users yet.") : table(
617
+ ["Email", "Name", "Created", "Status"],
618
+ list.users.value.map(
619
+ (u2) => h9("tr", { key: u2.id }, [
620
+ td(
621
+ h9(
622
+ "button",
623
+ {
624
+ type: "button",
625
+ style: { ...styles.navItem, padding: 0 },
626
+ onClick: () => selectedId.value = u2.id
627
+ },
628
+ u2.email
629
+ )
630
+ ),
631
+ td(
632
+ `${u2.firstName ?? ""} ${u2.lastName ?? ""}`.trim() || h9("span", { style: styles.muted }, "\u2014")
633
+ ),
634
+ td(new Date(u2.createdAt).toLocaleDateString()),
635
+ td(
636
+ u2.suspended ? h9("span", { style: styles.badge }, "suspended") : u2.deletedAt ? h9("span", { style: styles.badge }, "deleted") : h9("span", { style: styles.muted }, "active")
637
+ )
638
+ ])
639
+ )
640
+ ),
641
+ list.isLoading.value ? h9("p", { style: styles.status }, "Loading\u2026") : null,
642
+ list.hasMore.value && !list.isLoading.value ? h9(
643
+ "button",
644
+ {
645
+ type: "button",
646
+ style: { ...styles.button, marginTop: "8px" },
647
+ onClick: () => void list.loadMore()
648
+ },
649
+ "Load more"
650
+ ) : null
651
+ ]) : null,
477
652
  error.value ? h9(
478
653
  "p",
479
654
  { style: styles.error, role: "alert" },
@@ -632,19 +807,29 @@ var CatalogScreen = defineComponent9({
632
807
  });
633
808
 
634
809
  // src/screens/SubscriberScreen.ts
635
- import { useAdminSubscriber } from "@fonderie/vue-admin";
636
- import { defineComponent as defineComponent10, h as h11, ref as ref2 } from "vue";
810
+ import { useAdminSubscriber, useAdminSubscribers } from "@fonderie/vue-admin";
811
+ import { defineComponent as defineComponent10, h as h11, ref as ref3 } from "vue";
637
812
  var SubscriberScreen = defineComponent10({
638
813
  name: "FonderieSubscriberScreen",
639
- props: { client: { type: Object, required: true } },
814
+ props: {
815
+ client: { type: Object, required: true },
816
+ pageSize: { type: Number, default: 50 }
817
+ },
640
818
  setup(props) {
641
- const type = ref2("user");
642
- const idInput = ref2("");
643
- const subscriber = ref2(null);
819
+ const type = ref3("user");
820
+ const idInput = ref3("");
821
+ const subscriber = ref3(null);
822
+ const list = useAdminSubscribers(props.client, { limit: props.pageSize });
644
823
  const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = useAdminSubscriber(props.client, subscriber, { limit: 20 });
645
- const amount = ref2("");
646
- const note = ref2("");
647
- const granted = ref2(null);
824
+ const open = (t, id) => {
825
+ granted.value = null;
826
+ type.value = t;
827
+ idInput.value = id;
828
+ subscriber.value = { type: t, id };
829
+ };
830
+ const amount = ref3("");
831
+ const note = ref3("");
832
+ const granted = ref3(null);
648
833
  const input = (model, placeholder, extra = {}) => h11("input", {
649
834
  value: model.value,
650
835
  onInput: (e) => model.value = e.target.value,
@@ -686,9 +871,69 @@ var SubscriberScreen = defineComponent10({
686
871
  "button",
687
872
  { type: "submit", style: styles.button, disabled: isLoading.value },
688
873
  "Look up"
874
+ ),
875
+ subscriber.value ? h11(
876
+ "button",
877
+ {
878
+ type: "button",
879
+ style: styles.button,
880
+ onClick: () => {
881
+ subscriber.value = null;
882
+ idInput.value = "";
883
+ granted.value = null;
884
+ }
885
+ },
886
+ "\u2190 All subscribers"
887
+ ) : h11(
888
+ "button",
889
+ {
890
+ type: "button",
891
+ style: styles.button,
892
+ disabled: list.isLoading.value,
893
+ onClick: () => void list.refresh()
894
+ },
895
+ "Refresh"
689
896
  )
690
897
  ]
691
898
  ),
899
+ !subscriber.value ? h11("div", [
900
+ list.error.value ? h11("p", { style: styles.error, role: "alert" }, list.error.value.explanation) : null,
901
+ list.subscriptions.value.length === 0 && !list.isLoading.value ? h11("p", { style: styles.muted }, "No subscribers yet.") : table(
902
+ ["Subscriber", "Plan", "Status", "Renews"],
903
+ list.subscriptions.value.map(
904
+ (sub) => h11("tr", { key: sub.id }, [
905
+ td(
906
+ h11(
907
+ "button",
908
+ {
909
+ type: "button",
910
+ style: { ...styles.navItem, padding: 0, ...styles.mono },
911
+ onClick: () => open(sub.subscriberType, sub.subscriberId)
912
+ },
913
+ `${sub.subscriberType}/${sub.subscriberId}`
914
+ )
915
+ ),
916
+ td(`${sub.plan} \xB7 ${sub.interval}`),
917
+ td(
918
+ sub.cancelAtPeriodEnd ? [sub.status, " ", h11("span", { style: styles.badge }, "cancels")] : sub.status
919
+ ),
920
+ td(
921
+ sub.currentPeriodEnd ? new Date(sub.currentPeriodEnd).toLocaleDateString() : h11("span", { style: styles.muted }, "\u2014")
922
+ )
923
+ ])
924
+ )
925
+ ),
926
+ list.isLoading.value ? h11("p", { style: styles.status }, "Loading\u2026") : null,
927
+ list.hasMore.value && !list.isLoading.value ? h11(
928
+ "button",
929
+ {
930
+ type: "button",
931
+ style: { ...styles.button, marginTop: "8px" },
932
+ onClick: () => void list.loadMore()
933
+ },
934
+ "Load more"
935
+ ) : null
936
+ ]) : null,
692
937
  error.value ? h11("p", { style: styles.error, role: "alert" }, error.value.explanation) : null,
693
938
  subscriber.value && !isLoading.value ? [
694
939
  h11("h2", { style: styles.subtitle }, "Subscription"),
@@ -790,7 +1035,7 @@ var SubscriberScreen = defineComponent10({
790
1035
 
791
1036
  // src/screens/AuditScreen.ts
792
1037
  import { useAdminAudit } from "@fonderie/vue-admin";
793
- import { defineComponent as defineComponent11, h as h12, ref as ref3 } from "vue";
1038
+ import { defineComponent as defineComponent11, h as h12, ref as ref4 } from "vue";
794
1039
  var AuditScreen = defineComponent11({
795
1040
  name: "FonderieAuditScreen",
796
1041
  props: {
@@ -798,8 +1043,8 @@ var AuditScreen = defineComponent11({
798
1043
  pageSize: { type: Number, default: 50 }
799
1044
  },
800
1045
  setup(props) {
801
- const draft = ref3({ workspaceId: "", type: "", actorId: "" });
802
- const query = ref3({ limit: props.pageSize });
1046
+ const draft = ref4({ workspaceId: "", type: "", actorId: "" });
1047
+ const query = ref4({ limit: props.pageSize });
803
1048
  const { events, hasMore, isLoading, error, refresh, loadMore } = useAdminAudit(
804
1049
  props.client,
805
1050
  query
@@ -919,9 +1164,9 @@ var AdminShell = defineComponent12({
919
1164
  },
920
1165
  emits: { navigate: (_page) => true },
921
1166
  setup(props, { emit }) {
922
- const own = ref4("attention");
1167
+ const own = ref5("attention");
923
1168
  const current = computed2(() => props.page ?? own.value);
924
- const editing = ref4(null);
1169
+ const editing = ref5(null);
925
1170
  const go = (p) => {
926
1171
  editing.value = null;
927
1172
  emit("navigate", p);