@fonderie/react-admin-screens 0.3.0 → 0.4.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/README.md CHANGED
@@ -14,10 +14,10 @@ Status: **experimental** (0.x).
14
14
  | Money | Catalog · Subscriber | what am I selling (configured vs stored) · what is this subscriber on, their wallet, ledger, a manual grant (`billingClient`) |
15
15
  | Settings | Config & secrets | the `@fonderie/config` admin screens, as a sub-page |
16
16
  | Messaging | Templates | the `@fonderie/courier` admin screens, as a sub-page |
17
- | Activity | Admin log · Access | who did what · who can be here |
17
+ | Activity | Audit · Admin log · Access | what happened, every workspace (`auditClient`) · who did what through here · who can be here |
18
18
 
19
19
  ```ts
20
- import { AdminClient, AuthAdminClient, BillingAdminClient, ConfigAdminClient, CourierAdminClient } from '@fonderie/client';
20
+ import { AdminClient, AuditAdminClient, AuthAdminClient, BillingAdminClient, ConfigAdminClient, CourierAdminClient } from '@fonderie/client';
21
21
  import { AdminShell } from '@fonderie/react-admin-screens';
22
22
 
23
23
  const base = { baseUrl: 'https://api.example.com', adminToken, actor: 'louis' };
@@ -27,10 +27,11 @@ const configClient = new ConfigAdminClient({ ...base, prefix: '/_admin' });
27
27
  const courierClient = new CourierAdminClient({ ...base, prefix: '/_admin' });
28
28
  const authClient = new AuthAdminClient(base); // @fonderie/auth ≥ 7.8
29
29
  const billingClient = new BillingAdminClient(base); // @fonderie/billing ≥ 9.10
30
+ const auditClient = new AuditAdminClient(base); // @fonderie/audit ≥ 5.2
30
31
  ```
31
32
 
32
33
  ```tsx
33
- <AdminShell client={client} configClient={configClient} courierClient={courierClient} authClient={authClient} billingClient={billingClient} />
34
+ <AdminShell client={client} configClient={configClient} courierClient={courierClient} authClient={authClient} billingClient={billingClient} auditClient={auditClient} />
34
35
  ```
35
36
 
36
37
  Pass `page` and `onNavigate` to own the URL (one route per page); omit both
package/dist/index.cjs CHANGED
@@ -23,6 +23,7 @@ __export(index_exports, {
23
23
  AdminLogScreen: () => AdminLogScreen,
24
24
  AdminShell: () => AdminShell,
25
25
  AttentionScreen: () => AttentionScreen,
26
+ AuditScreen: () => AuditScreen,
26
27
  CatalogScreen: () => CatalogScreen,
27
28
  ConfigScreen: () => ConfigScreen,
28
29
  DoctorScreen: () => DoctorScreen,
@@ -37,7 +38,7 @@ module.exports = __toCommonJS(index_exports);
37
38
  // src/screens/AdminShell.tsx
38
39
  var import_react_config_admin_screens = require("@fonderie/react-config-admin-screens");
39
40
  var import_react_courier_admin_screens = require("@fonderie/react-courier-admin-screens");
40
- var import_react3 = require("react");
41
+ var import_react4 = require("react");
41
42
 
42
43
  // src/styles.ts
43
44
  var styles = {
@@ -754,8 +755,93 @@ function SubscriberScreen({ client }) {
754
755
  ] });
755
756
  }
756
757
 
757
- // src/screens/AdminShell.tsx
758
+ // src/screens/AuditScreen.tsx
759
+ var import_react_admin11 = require("@fonderie/react-admin");
760
+ var import_react3 = require("react");
758
761
  var import_jsx_runtime11 = require("react/jsx-runtime");
762
+ function AuditScreen({ client, pageSize = 50 }) {
763
+ const [draft, setDraft] = (0, import_react3.useState)({ workspaceId: "", type: "", actorId: "" });
764
+ const [filter, setFilter] = (0, import_react3.useState)(
765
+ {}
766
+ );
767
+ const { events, hasMore, isLoading, error, refresh, loadMore } = (0, import_react_admin11.useAdminAudit)(client, {
768
+ ...filter,
769
+ limit: pageSize
770
+ });
771
+ const field = (key, placeholder) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
772
+ "input",
773
+ {
774
+ value: draft[key],
775
+ onChange: (e) => setDraft({ ...draft, [key]: e.target.value }),
776
+ placeholder,
777
+ style: { ...styles.button, cursor: "text" },
778
+ "aria-label": placeholder
779
+ }
780
+ );
781
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: styles.container, children: [
782
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h1", { style: styles.title, children: "Audit" }),
783
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
784
+ "form",
785
+ {
786
+ style: styles.toolbar,
787
+ onSubmit: (e) => {
788
+ e.preventDefault();
789
+ setFilter({
790
+ ...draft.workspaceId.trim() ? { workspaceId: draft.workspaceId.trim() } : {},
791
+ ...draft.type.trim() ? { type: draft.type.trim() } : {},
792
+ ...draft.actorId.trim() ? { actorId: draft.actorId.trim() } : {}
793
+ });
794
+ },
795
+ children: [
796
+ field("workspaceId", "workspace id (all if empty)"),
797
+ field("type", "event type"),
798
+ field("actorId", "actor id"),
799
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "submit", style: styles.button, disabled: isLoading, children: "Filter" }),
800
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
801
+ "button",
802
+ {
803
+ type: "button",
804
+ style: styles.button,
805
+ onClick: () => void refresh(),
806
+ disabled: isLoading,
807
+ children: "Refresh"
808
+ }
809
+ )
810
+ ]
811
+ }
812
+ ),
813
+ error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.error, role: "alert", children: error.explanation }) : null,
814
+ events.length === 0 && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.muted, children: "Nothing recorded for this filter." }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("table", { style: styles.table, children: [
815
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("tr", { children: [
816
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "When" }),
817
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Type" }),
818
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Workspace" }),
819
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Actor" }),
820
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("th", { style: styles.th, children: "Request" })
821
+ ] }) }),
822
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("tbody", { children: events.map((e) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("tr", { children: [
823
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: new Date(e.createdAt).toLocaleString() }),
824
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: e.type }),
825
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: String(e.payload["workspaceId"] ?? "\u2014") }),
826
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: e.actorId ?? "\u2014" }),
827
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: e.requestId ?? "" })
828
+ ] }, e.id)) })
829
+ ] }),
830
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.status, children: "Loading\u2026" }) : null,
831
+ hasMore && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
832
+ "button",
833
+ {
834
+ type: "button",
835
+ style: { ...styles.button, marginTop: 8 },
836
+ onClick: () => void loadMore(),
837
+ children: "Load more"
838
+ }
839
+ ) : null
840
+ ] });
841
+ }
842
+
843
+ // src/screens/AdminShell.tsx
844
+ var import_jsx_runtime12 = require("react/jsx-runtime");
759
845
  var NAV = [
760
846
  { group: "Today", items: [{ page: "attention", label: "Attention" }] },
761
847
  {
@@ -780,6 +866,7 @@ var NAV = [
780
866
  {
781
867
  group: "Activity",
782
868
  items: [
869
+ { page: "audit", label: "Audit", needs: "audit" },
783
870
  { page: "log", label: "Admin log" },
784
871
  { page: "tokens", label: "Access" }
785
872
  ]
@@ -791,59 +878,64 @@ function AdminShell({
791
878
  courierClient,
792
879
  authClient,
793
880
  billingClient,
881
+ auditClient,
794
882
  environment,
795
883
  page,
796
884
  onNavigate
797
885
  }) {
798
- const [own, setOwn] = (0, import_react3.useState)("attention");
886
+ const [own, setOwn] = (0, import_react4.useState)("attention");
799
887
  const current = page ?? own;
800
888
  const go = (p) => {
801
889
  if (onNavigate) onNavigate(p);
802
890
  if (page === void 0) setOwn(p);
803
891
  };
804
- const [editing, setEditing] = (0, import_react3.useState)(null);
892
+ const [editing, setEditing] = (0, import_react4.useState)(null);
805
893
  const has = {
806
894
  config: Boolean(configClient),
807
895
  courier: Boolean(courierClient),
808
896
  auth: Boolean(authClient),
809
- billing: Boolean(billingClient)
897
+ billing: Boolean(billingClient),
898
+ audit: Boolean(auditClient)
810
899
  };
811
900
  let body;
812
901
  switch (current) {
813
902
  case "attention":
814
- body = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AttentionScreen, { client });
903
+ body = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AttentionScreen, { client });
815
904
  break;
816
905
  case "modules":
817
- body = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ModulesScreen, { client });
906
+ body = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ModulesScreen, { client });
818
907
  break;
819
908
  case "doctor":
820
- body = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DoctorScreen, { client });
909
+ body = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DoctorScreen, { client });
821
910
  break;
822
911
  case "config":
823
- body = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ConfigScreen, { client });
912
+ body = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ConfigScreen, { client });
824
913
  break;
825
914
  case "routes":
826
- body = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(RoutesScreen, { client });
915
+ body = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RoutesScreen, { client });
827
916
  break;
828
917
  case "tokens":
829
- body = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TokensScreen, { client });
918
+ body = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TokensScreen, { client });
830
919
  break;
831
920
  case "log":
832
- body = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AdminLogScreen, { client });
921
+ body = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AdminLogScreen, { client });
833
922
  break;
834
923
  case "catalog":
835
- body = billingClient ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CatalogScreen, { client: billingClient }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.status, children: "Pass a BillingAdminClient to see the catalog here." });
924
+ body = billingClient ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CatalogScreen, { client: billingClient }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.status, children: "Pass a BillingAdminClient to see the catalog here." });
836
925
  break;
837
926
  case "subscriber":
838
- body = billingClient ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SubscriberScreen, { client: billingClient }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.status, children: "Pass a BillingAdminClient to look up subscribers here." });
927
+ body = billingClient ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SubscriberScreen, { client: billingClient }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.status, children: "Pass a BillingAdminClient to look up subscribers here." });
928
+ break;
929
+ case "audit":
930
+ body = auditClient ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AuditScreen, { client: auditClient }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.status, children: "Pass an AuditAdminClient to see the audit trail here." });
839
931
  break;
840
932
  case "users":
841
- body = authClient ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(UsersScreen, { client: authClient }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.status, children: "Pass an AuthAdminClient to look up users here." });
933
+ body = authClient ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(UsersScreen, { client: authClient }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.status, children: "Pass an AuthAdminClient to look up users here." });
842
934
  break;
843
935
  case "settings":
844
- body = !configClient ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.status, children: "Pass a ConfigAdminClient to manage config and secrets here." }) : editing && editing.kind !== "template" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
845
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
846
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
936
+ body = !configClient ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.status, children: "Pass a ConfigAdminClient to manage config and secrets here." }) : editing && editing.kind !== "template" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
937
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
938
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
847
939
  import_react_config_admin_screens.ConfigEditorScreen,
848
940
  {
849
941
  client: configClient,
@@ -853,7 +945,7 @@ function AdminShell({
853
945
  onSaved: () => setEditing(null)
854
946
  }
855
947
  )
856
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
948
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
857
949
  import_react_config_admin_screens.ConfigListScreen,
858
950
  {
859
951
  client: configClient,
@@ -864,9 +956,9 @@ function AdminShell({
864
956
  );
865
957
  break;
866
958
  case "templates":
867
- body = !courierClient ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.status, children: "Pass a CourierAdminClient to manage templates here." }) : editing?.kind === "template" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
868
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
869
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
959
+ body = !courierClient ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.status, children: "Pass a CourierAdminClient to manage templates here." }) : editing?.kind === "template" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
960
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", style: styles.button, onClick: () => setEditing(null), children: "\u2190 Back" }),
961
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
870
962
  import_react_courier_admin_screens.TemplateEditorScreen,
871
963
  {
872
964
  client: courierClient,
@@ -874,7 +966,7 @@ function AdminShell({
874
966
  onSaved: () => setEditing(null)
875
967
  }
876
968
  )
877
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
969
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
878
970
  import_react_courier_admin_screens.TemplateListScreen,
879
971
  {
880
972
  client: courierClient,
@@ -883,13 +975,13 @@ function AdminShell({
883
975
  );
884
976
  break;
885
977
  }
886
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: styles.shell, children: [
887
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("nav", { style: styles.nav, "aria-label": "Admin", children: NAV.map((g) => {
978
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: styles.shell, children: [
979
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("nav", { style: styles.nav, "aria-label": "Admin", children: NAV.map((g) => {
888
980
  const items = g.items.filter((i) => !i.needs || has[i.needs]);
889
981
  if (items.length === 0) return null;
890
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
891
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: styles.navGroup, children: g.group }),
892
- items.map((i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
982
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
983
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: styles.navGroup, children: g.group }),
984
+ items.map((i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
893
985
  "button",
894
986
  {
895
987
  type: "button",
@@ -905,7 +997,7 @@ function AdminShell({
905
997
  ))
906
998
  ] }, g.group);
907
999
  }) }),
908
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("main", { style: styles.main, children: body })
1000
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("main", { style: styles.main, children: body })
909
1001
  ] });
910
1002
  }
911
1003
  // Annotate the CommonJS export names for ESM import in node:
@@ -913,6 +1005,7 @@ function AdminShell({
913
1005
  AdminLogScreen,
914
1006
  AdminShell,
915
1007
  AttentionScreen,
1008
+ AuditScreen,
916
1009
  CatalogScreen,
917
1010
  ConfigScreen,
918
1011
  DoctorScreen,