@fonderie/vue-admin-screens 1.3.1 → 1.5.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 CHANGED
@@ -1755,16 +1755,22 @@ function useSubscriptionIndex(client) {
1755
1755
  // src/screens/SubscriberBilling.ts
1756
1756
  var import_vue_admin10 = require("@fonderie/vue-admin");
1757
1757
  var import_vue13 = require("vue");
1758
- var SubscriberBilling = (0, import_vue13.defineComponent)({
1759
- name: "FonderieSubscriberBilling",
1758
+ var SubscriberBillingInner = (0, import_vue13.defineComponent)({
1759
+ name: "FonderieSubscriberBillingInner",
1760
1760
  props: {
1761
1761
  client: { type: Object, required: true },
1762
1762
  subscriberType: { type: String, required: true },
1763
- subscriberId: { type: String, required: true }
1763
+ subscriberId: { type: String, required: true },
1764
+ currency: { type: String, default: "" }
1764
1765
  },
1765
- setup(props) {
1766
+ emits: { "change-currency": (_currency) => true },
1767
+ setup(props, { emit }) {
1766
1768
  const subscriber = (0, import_vue13.computed)(() => ({ type: props.subscriberType, id: props.subscriberId }));
1767
- const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = (0, import_vue_admin10.useAdminSubscriber)(props.client, subscriber, { limit: 20 });
1769
+ const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = (0, import_vue_admin10.useAdminSubscriber)(props.client, subscriber, {
1770
+ limit: 20,
1771
+ ...props.currency ? { currency: props.currency } : {}
1772
+ });
1773
+ const currencyDraft = (0, import_vue13.ref)(props.currency);
1768
1774
  const amount = (0, import_vue13.ref)("");
1769
1775
  const note = (0, import_vue13.ref)("");
1770
1776
  const granted = (0, import_vue13.ref)(null);
@@ -1805,7 +1811,43 @@ var SubscriberBilling = (0, import_vue13.defineComponent)({
1805
1811
  ) : null
1806
1812
  ]),
1807
1813
  (0, import_vue13.h)("div", { style: styles.card }, [
1808
- (0, import_vue13.h)("div", { style: styles.statLabel }, "Credits"),
1814
+ (0, import_vue13.h)(
1815
+ "div",
1816
+ {
1817
+ style: {
1818
+ display: "flex",
1819
+ justifyContent: "space-between",
1820
+ alignItems: "center",
1821
+ gap: "8px"
1822
+ }
1823
+ },
1824
+ [
1825
+ (0, import_vue13.h)("div", { style: styles.statLabel }, "Credits"),
1826
+ (0, import_vue13.h)(
1827
+ "form",
1828
+ {
1829
+ style: { display: "flex", gap: "4px" },
1830
+ onSubmit: (e) => {
1831
+ e.preventDefault();
1832
+ emit("change-currency", currencyDraft.value.trim().toUpperCase());
1833
+ }
1834
+ },
1835
+ [
1836
+ (0, import_vue13.h)("input", {
1837
+ value: currencyDraft.value,
1838
+ onInput: (e) => {
1839
+ currencyDraft.value = e.target.value;
1840
+ },
1841
+ placeholder: w?.currency ?? "currency",
1842
+ maxlength: 8,
1843
+ style: { ...styles.input, height: "26px", width: "84px", fontSize: "12px" },
1844
+ "aria-label": "Wallet currency",
1845
+ title: "Show the wallet in another currency"
1846
+ })
1847
+ ]
1848
+ )
1849
+ ]
1850
+ ),
1809
1851
  ...w ? [
1810
1852
  (0, import_vue13.h)("div", { style: styles.statValue }, [
1811
1853
  `${w.balance} `,
@@ -1902,6 +1944,27 @@ var SubscriberBilling = (0, import_vue13.defineComponent)({
1902
1944
  };
1903
1945
  }
1904
1946
  });
1947
+ var SubscriberBilling = (0, import_vue13.defineComponent)({
1948
+ name: "FonderieSubscriberBilling",
1949
+ props: {
1950
+ client: { type: Object, required: true },
1951
+ subscriberType: { type: String, required: true },
1952
+ subscriberId: { type: String, required: true }
1953
+ },
1954
+ setup(props) {
1955
+ const currency = (0, import_vue13.ref)("");
1956
+ return () => (0, import_vue13.h)(SubscriberBillingInner, {
1957
+ key: `${props.subscriberType}:${props.subscriberId}:${currency.value}`,
1958
+ client: props.client,
1959
+ subscriberType: props.subscriberType,
1960
+ subscriberId: props.subscriberId,
1961
+ currency: currency.value,
1962
+ "onChange-currency": (c) => {
1963
+ currency.value = c;
1964
+ }
1965
+ });
1966
+ }
1967
+ });
1905
1968
 
1906
1969
  // src/screens/UsersScreen.ts
1907
1970
  var UsersScreen = (0, import_vue14.defineComponent)({
@@ -1920,7 +1983,9 @@ var UsersScreen = (0, import_vue14.defineComponent)({
1920
1983
  const email = (0, import_vue14.ref)("");
1921
1984
  const selectedId = (0, import_vue14.ref)(props.openUserId ?? "");
1922
1985
  const plans = useSubscriptionIndex(props.billingClient);
1923
- const list = (0, import_vue_admin11.useAdminUsers)(props.client, { limit: props.pageSize });
1986
+ const showDeleted = (0, import_vue14.ref)(false);
1987
+ const activeList = (0, import_vue_admin11.useAdminUsers)(props.client, { limit: props.pageSize });
1988
+ const deletedList = (0, import_vue_admin11.useAdminUsers)(props.client, { limit: props.pageSize, deleted: true });
1924
1989
  const { user, isLoading, error, suspend, unsuspend, revokeSessions } = (0, import_vue_admin11.useAdminUser)(
1925
1990
  props.client,
1926
1991
  { email, id: selectedId }
@@ -1948,6 +2013,7 @@ var UsersScreen = (0, import_vue14.defineComponent)({
1948
2013
  };
1949
2014
  return () => {
1950
2015
  const u = user.value;
2016
+ const list = showDeleted.value ? deletedList : activeList;
1951
2017
  return (0, import_vue14.h)("div", { style: styles.container }, [
1952
2018
  pageHeader(
1953
2019
  "Users",
@@ -1977,7 +2043,37 @@ var UsersScreen = (0, import_vue14.defineComponent)({
1977
2043
  { type: "submit", style: styles.buttonPrimary, disabled: isLoading.value },
1978
2044
  [icon("search", 14), "Look up"]
1979
2045
  ),
1980
- showList.value ? refreshButton2("Refresh", list.isLoading.value, () => void list.refresh()) : actionButton([icon("back", 14), "All users"], false, clear, styles.buttonGhost)
2046
+ showList.value ? refreshButton2("Refresh", list.isLoading.value, () => void list.refresh()) : actionButton([icon("back", 14), "All users"], false, clear, styles.buttonGhost),
2047
+ showList.value ? (0, import_vue14.h)(
2048
+ "fieldset",
2049
+ {
2050
+ style: {
2051
+ display: "flex",
2052
+ gap: "6px",
2053
+ border: "none",
2054
+ margin: "0 0 0 auto",
2055
+ padding: "0",
2056
+ minWidth: "0"
2057
+ },
2058
+ "aria-label": "Which accounts"
2059
+ },
2060
+ [false, true].map(
2061
+ (d) => (0, import_vue14.h)(
2062
+ "button",
2063
+ {
2064
+ key: String(d),
2065
+ type: "button",
2066
+ "aria-pressed": showDeleted.value === d,
2067
+ onClick: () => showDeleted.value = d,
2068
+ style: {
2069
+ ...showDeleted.value === d ? styles.buttonPrimary : styles.button,
2070
+ height: "28px"
2071
+ }
2072
+ },
2073
+ d ? "Deleted" : "Active"
2074
+ )
2075
+ )
2076
+ ) : null
1981
2077
  ]
1982
2078
  ),
1983
2079
  showList.value ? (0, import_vue14.h)("div", [
@@ -2057,7 +2153,10 @@ var UsersScreen = (0, import_vue14.defineComponent)({
2057
2153
  row("created", new Date(u.createdAt).toLocaleString())
2058
2154
  ])
2059
2155
  ]),
2060
- (0, import_vue14.h)("div", { style: { ...styles.toolbar, marginTop: "12px" } }, [
2156
+ u.deletedAt ? (0, import_vue14.h)("div", { style: { ...styles.notice, marginTop: "12px" }, role: "status" }, [
2157
+ (0, import_vue14.h)("strong", `Deleted on ${new Date(u.deletedAt).toLocaleString()}.`),
2158
+ " The account cannot sign in, and it is erased when the retention purge runs. Its billing records stay."
2159
+ ]) : (0, import_vue14.h)("div", { style: { ...styles.toolbar, marginTop: "12px" } }, [
2061
2160
  u.suspended ? actionButton("Unsuspend", isLoading.value, () => void unsuspend()) : actionButton(
2062
2161
  "Suspend",
2063
2162
  isLoading.value,
@@ -2077,46 +2176,60 @@ var UsersScreen = (0, import_vue14.defineComponent)({
2077
2176
  subscriberType: "user",
2078
2177
  subscriberId: u.id
2079
2178
  }) : null,
2080
- (0, import_vue14.h)("h2", { style: styles.subtitle }, "Live sessions"),
2081
- sessions.sessions.value.length === 0 ? (0, import_vue14.h)("p", { style: styles.muted }, "None.") : (0, import_vue14.h)(
2082
- "ul",
2083
- { style: styles.list },
2084
- sessions.sessions.value.map(
2085
- (s) => (0, import_vue14.h)("li", { key: s.id, style: styles.row }, [
2086
- (0, import_vue14.h)("span", { style: styles.mono }, s.ipAddress ?? "\u2014"),
2087
- " ",
2088
- (0, import_vue14.h)("span", { style: styles.muted }, (0, import_client.describeLocation)(s.location) ?? ""),
2089
- " ",
2090
- (0, import_vue14.h)("span", { style: styles.muted }, s.userAgent ?? ""),
2091
- " ",
2092
- (0, import_vue14.h)(
2093
- "span",
2094
- { style: styles.muted },
2095
- `since ${new Date(s.createdAt).toLocaleString()}`
2096
- )
2097
- ])
2098
- )
2099
- ),
2100
- (0, import_vue14.h)("h2", { style: styles.subtitle }, "Recent sign-ins"),
2101
- history.events.value.length === 0 && !history.isLoading.value ? (0, import_vue14.h)("p", { style: styles.muted }, "None recorded.") : (0, import_vue14.h)(
2102
- "ul",
2103
- { style: styles.list },
2104
- history.events.value.map(
2105
- (e) => (0, import_vue14.h)("li", { key: e.id, style: styles.row }, [
2106
- pill(e.outcome === "success" ? "ok" : "bad", e.outcome),
2107
- " ",
2108
- (0, import_vue14.h)("span", { style: styles.muted }, e.method),
2109
- " ",
2110
- (0, import_vue14.h)("span", { style: styles.mono }, e.ipAddress ?? "\u2014"),
2111
- " ",
2112
- (0, import_vue14.h)("span", { style: styles.muted }, (0, import_client.describeLocation)(e.location) ?? ""),
2113
- e.location?.proxy || e.location?.hosting ? [" ", pill("warn", e.location.proxy ? "proxy/VPN" : "hosting")] : "",
2114
- " ",
2115
- (0, import_vue14.h)("span", { style: styles.muted }, new Date(e.createdAt).toLocaleString())
2116
- ])
2117
- )
2118
- ),
2119
- history.hasMore.value && !history.isLoading.value ? loadMoreButton(() => void history.loadMore()) : null
2179
+ u.deletedAt ? null : [
2180
+ (0, import_vue14.h)("h2", { style: styles.subtitle }, "Live sessions"),
2181
+ sessions.sessions.value.length === 0 ? (0, import_vue14.h)("p", { style: styles.muted }, "None.") : (0, import_vue14.h)(
2182
+ "ul",
2183
+ { style: styles.list },
2184
+ sessions.sessions.value.map(
2185
+ (s) => (0, import_vue14.h)("li", { key: s.id, style: styles.row }, [
2186
+ (0, import_vue14.h)("span", { style: styles.mono }, s.ipAddress ?? "\u2014"),
2187
+ " ",
2188
+ (0, import_vue14.h)(
2189
+ "span",
2190
+ { style: styles.muted },
2191
+ (0, import_client.describeLocation)(s.location) ?? ""
2192
+ ),
2193
+ " ",
2194
+ (0, import_vue14.h)("span", { style: styles.muted }, s.userAgent ?? ""),
2195
+ " ",
2196
+ (0, import_vue14.h)(
2197
+ "span",
2198
+ { style: styles.muted },
2199
+ `since ${new Date(s.createdAt).toLocaleString()}`
2200
+ )
2201
+ ])
2202
+ )
2203
+ ),
2204
+ (0, import_vue14.h)("h2", { style: styles.subtitle }, "Recent sign-ins"),
2205
+ history.events.value.length === 0 && !history.isLoading.value ? (0, import_vue14.h)("p", { style: styles.muted }, "None recorded.") : (0, import_vue14.h)(
2206
+ "ul",
2207
+ { style: styles.list },
2208
+ history.events.value.map(
2209
+ (e) => (0, import_vue14.h)("li", { key: e.id, style: styles.row }, [
2210
+ pill(e.outcome === "success" ? "ok" : "bad", e.outcome),
2211
+ " ",
2212
+ (0, import_vue14.h)("span", { style: styles.muted }, e.method),
2213
+ " ",
2214
+ (0, import_vue14.h)("span", { style: styles.mono }, e.ipAddress ?? "\u2014"),
2215
+ " ",
2216
+ (0, import_vue14.h)(
2217
+ "span",
2218
+ { style: styles.muted },
2219
+ (0, import_client.describeLocation)(e.location) ?? ""
2220
+ ),
2221
+ e.location?.proxy || e.location?.hosting ? [" ", pill("warn", e.location.proxy ? "proxy/VPN" : "hosting")] : "",
2222
+ " ",
2223
+ (0, import_vue14.h)(
2224
+ "span",
2225
+ { style: styles.muted },
2226
+ new Date(e.createdAt).toLocaleString()
2227
+ )
2228
+ ])
2229
+ )
2230
+ ),
2231
+ history.hasMore.value && !history.isLoading.value ? loadMoreButton(() => void history.loadMore()) : null
2232
+ ]
2120
2233
  ] : null
2121
2234
  ]);
2122
2235
  };
@@ -2381,7 +2494,7 @@ var AuditScreen = (0, import_vue17.defineComponent)({
2381
2494
  pageSize: { type: Number, default: 50 }
2382
2495
  },
2383
2496
  setup(props) {
2384
- const draft = (0, import_vue17.ref)({ workspaceId: "", type: "", actorId: "" });
2497
+ const draft = (0, import_vue17.ref)({ workspaceId: "", type: "", actorId: "", from: "", to: "" });
2385
2498
  const query = (0, import_vue17.ref)({ limit: props.pageSize });
2386
2499
  const { events, hasMore, isLoading, error, refresh, loadMore } = (0, import_vue_admin14.useAdminAudit)(
2387
2500
  props.client,
@@ -2410,7 +2523,10 @@ var AuditScreen = (0, import_vue17.defineComponent)({
2410
2523
  limit: props.pageSize,
2411
2524
  ...d.workspaceId.trim() ? { workspaceId: d.workspaceId.trim() } : {},
2412
2525
  ...d.type.trim() ? { type: d.type.trim() } : {},
2413
- ...d.actorId.trim() ? { actorId: d.actorId.trim() } : {}
2526
+ ...d.actorId.trim() ? { actorId: d.actorId.trim() } : {},
2527
+ // Dates are whole days in the operator's time zone: "to" includes that day.
2528
+ ...d.from ? { from: /* @__PURE__ */ new Date(`${d.from}T00:00:00`) } : {},
2529
+ ...d.to ? { to: /* @__PURE__ */ new Date(`${d.to}T23:59:59.999`) } : {}
2414
2530
  };
2415
2531
  }
2416
2532
  },
@@ -2418,6 +2534,16 @@ var AuditScreen = (0, import_vue17.defineComponent)({
2418
2534
  field("workspaceId", "workspace id (all if empty)"),
2419
2535
  field("type", "event type"),
2420
2536
  field("actorId", "actor id"),
2537
+ ...["from", "to"].map(
2538
+ (k) => (0, import_vue17.h)("input", {
2539
+ type: "date",
2540
+ value: draft.value[k],
2541
+ onInput: (e) => draft.value = { ...draft.value, [k]: e.target.value },
2542
+ style: styles.input,
2543
+ "aria-label": k === "from" ? "From date" : "To date",
2544
+ title: k === "from" ? "From (inclusive)" : "To (inclusive)"
2545
+ })
2546
+ ),
2421
2547
  (0, import_vue17.h)(
2422
2548
  "button",
2423
2549
  { type: "submit", style: styles.buttonPrimary, disabled: isLoading.value },
@@ -2523,7 +2649,10 @@ var AdminShell = (0, import_vue18.defineComponent)({
2523
2649
  // shows the Operators page.
2524
2650
  operators: { type: Boolean, default: false },
2525
2651
  // The signed-in operator's email, when a person (not a token) is signed in.
2526
- currentOperator: { type: String, default: void 0 }
2652
+ currentOperator: { type: String, default: void 0 },
2653
+ // Where the app serves GET /config/public — marks public keys on the
2654
+ // Config page and previews exactly what frontends receive.
2655
+ publicConfigUrl: { type: String, default: void 0 }
2527
2656
  },
2528
2657
  // `footer` slot: pinned to the bottom of the sidebar — session controls
2529
2658
  // (theme, sign out). The React shell takes the same thing as a `footer` prop.
@@ -2602,43 +2731,83 @@ var AdminShell = (0, import_vue18.defineComponent)({
2602
2731
  const cc = props.configClient;
2603
2732
  if (!cc) return missing("Pass a ConfigAdminClient to manage config and secrets here.");
2604
2733
  const e = editing.value;
2605
- if (e && e.kind !== "template")
2734
+ if (e && (e.kind === "config" || e.kind === "secret")) {
2735
+ const env = e.environment ?? props.environment;
2606
2736
  return [
2607
2737
  back("Config & secrets"),
2608
2738
  (0, import_vue18.h)(import_vue_config_admin_screens.ConfigEditorScreen, {
2739
+ key: `${e.kind}:${e.key}:${e.environment ?? ""}`,
2609
2740
  client: cc,
2610
2741
  kind: e.kind,
2611
2742
  configKey: e.key,
2612
- ...props.environment !== void 0 ? { environment: props.environment } : {},
2613
- onSaved: () => editing.value = null
2743
+ ...env !== void 0 ? { environment: env } : {},
2744
+ ...e.environments ? { environments: e.environments } : {},
2745
+ onSaved: () => editing.value = null,
2746
+ onDeleted: () => editing.value = null
2614
2747
  })
2615
2748
  ];
2749
+ }
2750
+ const create = (kind) => (c2) => editing.value = {
2751
+ kind,
2752
+ key: "",
2753
+ environments: c2.environments,
2754
+ ...c2.environment ? { environment: c2.environment } : {}
2755
+ };
2616
2756
  return (0, import_vue18.h)(import_vue_config_admin_screens.ConfigListScreen, {
2617
2757
  client: cc,
2618
2758
  ...props.environment !== void 0 ? { environment: props.environment } : {},
2619
- "onSelect-config": (key) => editing.value = { kind: "config", key },
2620
- "onSelect-secret": (key) => editing.value = { kind: "secret", key },
2759
+ ...props.publicConfigUrl ? { publicConfigUrl: props.publicConfigUrl } : {},
2760
+ "onSelect-config": (key, environment) => editing.value = { kind: "config", key, environment },
2761
+ "onSelect-secret": (key, environment) => editing.value = { kind: "secret", key, environment },
2621
2762
  allowCreate: true,
2622
- "onCreate-config": () => editing.value = { kind: "config", key: "" },
2623
- "onCreate-secret": () => editing.value = { kind: "secret", key: "" }
2763
+ "onCreate-config": create("config"),
2764
+ "onCreate-secret": create("secret")
2624
2765
  });
2625
2766
  }
2626
2767
  case "templates": {
2627
2768
  const kc = props.courierClient;
2628
2769
  if (!kc) return missing("Pass a CourierAdminClient to manage templates here.");
2629
2770
  const e = editing.value;
2771
+ if (e?.kind === "template-new")
2772
+ return [
2773
+ back("Templates"),
2774
+ (0, import_vue18.h)(import_vue_courier_admin_screens.TemplateCreateScreen, {
2775
+ client: kc,
2776
+ ...e.type ? { type: e.type } : {},
2777
+ ...e.locales ? { locales: e.locales } : {},
2778
+ onCreated: (c2) => editing.value = {
2779
+ kind: "template",
2780
+ type: c2.type,
2781
+ locale: c2.locale,
2782
+ system: false
2783
+ }
2784
+ })
2785
+ ];
2630
2786
  if (e?.kind === "template")
2631
2787
  return [
2632
2788
  back("Templates"),
2633
2789
  (0, import_vue18.h)(import_vue_courier_admin_screens.TemplateEditorScreen, {
2790
+ key: `${e.type}:${e.locale ?? ""}`,
2634
2791
  client: kc,
2635
2792
  type: e.type,
2636
- onSaved: () => editing.value = null
2793
+ locale: e.locale,
2794
+ system: e.system,
2795
+ allowAddLocale: true,
2796
+ onSaved: () => editing.value = null,
2797
+ onDeleted: () => editing.value = null,
2798
+ "onAdd-locale": (type) => editing.value = { kind: "template-new", type }
2637
2799
  })
2638
2800
  ];
2639
2801
  return (0, import_vue18.h)(import_vue_courier_admin_screens.TemplateListScreen, {
2640
2802
  client: kc,
2641
- "onSelect-template": (t2) => editing.value = { kind: "template", type: t2.type }
2803
+ allowCreate: true,
2804
+ "onSelect-template": (t2) => editing.value = {
2805
+ kind: "template",
2806
+ type: t2.type,
2807
+ locale: t2.locale ?? null,
2808
+ system: t2.system === true
2809
+ },
2810
+ "onCreate-template": (c2) => editing.value = { kind: "template-new", locales: c2.locales }
2642
2811
  });
2643
2812
  }
2644
2813
  }