@openrails/billing-ui 0.242.0 → 0.244.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.
Files changed (267) hide show
  1. package/CHANGELOG.md +27 -11
  2. package/README.md +41 -8
  3. package/dist/admin/billing-admin.d.ts +3 -0
  4. package/dist/admin/components/app-sidebar.d.ts +1 -0
  5. package/dist/admin/components/confirm.d.ts +4 -0
  6. package/dist/admin/components/cursor-pager.d.ts +8 -0
  7. package/dist/admin/components/data-table.d.ts +14 -0
  8. package/dist/admin/components/fact-card.d.ts +5 -0
  9. package/dist/admin/components/form-field-errors.d.ts +3 -0
  10. package/dist/admin/components/linked-table-row.d.ts +5 -0
  11. package/dist/admin/components/logo.d.ts +3 -0
  12. package/dist/admin/components/no-access.d.ts +3 -0
  13. package/dist/admin/components/no-merchants.d.ts +1 -0
  14. package/dist/admin/components/notification-bell.d.ts +2 -0
  15. package/dist/admin/components/route-error.d.ts +3 -0
  16. package/dist/admin/components/site-header.d.ts +9 -0
  17. package/dist/admin/components/status-badge.d.ts +3 -0
  18. package/dist/admin/components/typed-confirm-dialog.d.ts +11 -0
  19. package/dist/admin/components/ui/alert-dialog.d.ts +18 -0
  20. package/dist/admin/components/ui/avatar.d.ts +11 -0
  21. package/dist/admin/components/ui/badge.d.ts +7 -0
  22. package/dist/admin/components/ui/breadcrumb.d.ts +10 -0
  23. package/dist/admin/components/ui/button.d.ts +8 -0
  24. package/dist/admin/components/ui/card.d.ts +11 -0
  25. package/dist/admin/components/ui/chart.d.ts +44 -0
  26. package/dist/admin/components/ui/dialog.d.ts +17 -0
  27. package/dist/admin/components/ui/dropdown-menu.d.ts +29 -0
  28. package/dist/admin/components/ui/input.d.ts +3 -0
  29. package/dist/admin/components/ui/label.d.ts +3 -0
  30. package/dist/admin/components/ui/select.d.ts +15 -0
  31. package/dist/admin/components/ui/separator.d.ts +3 -0
  32. package/dist/admin/components/ui/sheet.d.ts +14 -0
  33. package/dist/admin/components/ui/sidebar.d.ts +63 -0
  34. package/dist/admin/components/ui/skeleton.d.ts +2 -0
  35. package/dist/admin/components/ui/sonner.d.ts +3 -0
  36. package/dist/admin/components/ui/switch.d.ts +5 -0
  37. package/dist/admin/components/ui/table.d.ts +10 -0
  38. package/dist/admin/components/ui/tabs.d.ts +10 -0
  39. package/dist/admin/components/ui/textarea.d.ts +3 -0
  40. package/dist/admin/components/ui/tooltip.d.ts +6 -0
  41. package/dist/admin/context.d.ts +1193 -0
  42. package/dist/admin/hooks/use-mobile.d.ts +1 -0
  43. package/dist/admin/index.d.ts +5 -0
  44. package/dist/admin/layouts/app-layout.d.ts +4 -0
  45. package/dist/admin/lib/api/client.d.ts +64 -0
  46. package/dist/admin/lib/api/copilot.d.ts +6 -0
  47. package/dist/admin/lib/api/credit-endpoints.d.ts +8 -0
  48. package/dist/admin/lib/api/credit-types.d.ts +9 -0
  49. package/dist/admin/lib/api/endpoints.d.ts +133 -0
  50. package/dist/admin/lib/api/index.d.ts +89 -0
  51. package/dist/admin/lib/api/invoice-profile-endpoints.d.ts +6 -0
  52. package/dist/admin/lib/api/metrics.d.ts +68 -0
  53. package/dist/admin/lib/api/order-endpoints.d.ts +21 -0
  54. package/dist/admin/lib/api/types.d.ts +122 -0
  55. package/dist/admin/lib/auth.d.ts +6 -0
  56. package/dist/admin/lib/capabilities.d.ts +13 -0
  57. package/dist/admin/lib/chunk-error.d.ts +1 -0
  58. package/dist/admin/lib/confirm.d.ts +11 -0
  59. package/dist/admin/lib/credit-queries.d.ts +23 -0
  60. package/dist/admin/lib/csv.d.ts +3 -0
  61. package/dist/admin/lib/cursor-pages.d.ts +13 -0
  62. package/dist/admin/lib/dialog-width.d.ts +6 -0
  63. package/dist/admin/lib/dom.d.ts +1 -0
  64. package/dist/admin/lib/duration.d.ts +3 -0
  65. package/dist/admin/lib/format.d.ts +23 -0
  66. package/dist/admin/lib/mount.d.ts +6 -0
  67. package/dist/admin/lib/mutations.d.ts +125 -0
  68. package/dist/admin/lib/order-queries.d.ts +29 -0
  69. package/dist/admin/lib/queries.d.ts +369 -0
  70. package/dist/admin/lib/query-client.d.ts +3 -0
  71. package/dist/admin/lib/toast.d.ts +10 -0
  72. package/dist/admin/lib/utils.d.ts +2 -0
  73. package/dist/admin/messages.d.ts +670 -0
  74. package/dist/admin/navigation.d.ts +14 -0
  75. package/dist/admin/pages/catalog/application-dialog.d.ts +2 -0
  76. package/dist/admin/pages/catalog/copilot-panel.d.ts +4 -0
  77. package/dist/admin/pages/catalog/index.d.ts +4 -0
  78. package/dist/admin/pages/catalog/meter-form.d.ts +6 -0
  79. package/dist/admin/pages/catalog/metering-model.d.ts +67 -0
  80. package/dist/admin/pages/catalog/metering.d.ts +3 -0
  81. package/dist/admin/pages/catalog/price-detail.d.ts +2 -0
  82. package/dist/admin/pages/catalog/price-format.d.ts +3 -0
  83. package/dist/admin/pages/catalog/price-wizard-logic.d.ts +20 -0
  84. package/dist/admin/pages/catalog/price-wizard.d.ts +10 -0
  85. package/dist/admin/pages/catalog/psp-links.d.ts +10 -0
  86. package/dist/admin/pages/catalog/rate-card-editor.d.ts +10 -0
  87. package/dist/admin/pages/customers/credit-form.d.ts +14 -0
  88. package/dist/admin/pages/customers/credits.d.ts +5 -0
  89. package/dist/admin/pages/customers/default-card-badges.d.ts +3 -0
  90. package/dist/admin/pages/customers/detail.d.ts +5 -0
  91. package/dist/admin/pages/customers/grant-duration.d.ts +32 -0
  92. package/dist/admin/pages/customers/index.d.ts +2 -0
  93. package/dist/admin/pages/customers/invoice-profile-model.d.ts +18 -0
  94. package/dist/admin/pages/customers/invoice-profile.d.ts +9 -0
  95. package/dist/admin/pages/dashboard/index.d.ts +2 -0
  96. package/dist/admin/pages/dashboard/lib.d.ts +48 -0
  97. package/dist/admin/pages/dashboard/range-picker.d.ts +1 -0
  98. package/dist/admin/pages/dashboard/range.d.ts +34 -0
  99. package/dist/admin/pages/dashboard/reports-model.d.ts +29 -0
  100. package/dist/admin/pages/dashboard/reports.d.ts +2 -0
  101. package/dist/admin/pages/dashboard/use-range.d.ts +5 -0
  102. package/dist/admin/pages/dashboard/widget-editor.d.ts +8 -0
  103. package/dist/admin/pages/dashboard/widget-tile.d.ts +9 -0
  104. package/dist/admin/pages/dashboard/widget-viz.d.ts +12 -0
  105. package/dist/admin/pages/ops/index.d.ts +2 -0
  106. package/dist/admin/pages/orders/detail.d.ts +5 -0
  107. package/dist/admin/pages/orders/index.d.ts +1 -0
  108. package/dist/admin/pages/orders/model.d.ts +7 -0
  109. package/dist/admin/pages/payments/attempts.d.ts +7 -0
  110. package/dist/admin/pages/payments/detail.d.ts +2 -0
  111. package/dist/admin/pages/payments/health-model.d.ts +149 -0
  112. package/dist/admin/pages/payments/health.d.ts +2 -0
  113. package/dist/admin/pages/payments/index.d.ts +2 -0
  114. package/dist/admin/pages/settings/configuration-edit.d.ts +9 -0
  115. package/dist/admin/pages/settings/index.d.ts +9 -0
  116. package/dist/admin/pages/settings/notifications.d.ts +2 -0
  117. package/dist/admin/pages/settings/setting-fields.d.ts +20 -0
  118. package/dist/admin/pages/subscriptions/change-subscription-dialog.d.ts +16 -0
  119. package/dist/admin/pages/subscriptions/detail.d.ts +2 -0
  120. package/dist/admin/pages/subscriptions/dunning.d.ts +2 -0
  121. package/dist/admin/pages/subscriptions/index.d.ts +2 -0
  122. package/dist/admin/pages/subscriptions/subscription-change-options.d.ts +23 -0
  123. package/dist/admin/pages/subscriptions/views.d.ts +15 -0
  124. package/dist/admin/router.d.ts +32 -0
  125. package/dist/admin/routes.d.ts +5 -0
  126. package/dist/admin/summary.d.ts +27 -0
  127. package/dist/admin/theme.d.ts +2 -0
  128. package/dist/admin/types.d.ts +56 -0
  129. package/dist/admin/ui-context.d.ts +14 -0
  130. package/dist/admin-attempts-lAoW-Urd.js +419 -0
  131. package/dist/admin-attempts-lAoW-Urd.js.map +1 -0
  132. package/dist/admin-auth-DQGHtt0j.js +14 -0
  133. package/dist/admin-auth-DQGHtt0j.js.map +1 -0
  134. package/dist/admin-badge-DR5-1qd8.js +31 -0
  135. package/dist/admin-badge-DR5-1qd8.js.map +1 -0
  136. package/dist/admin-button-CTL3DgOt.js +46 -0
  137. package/dist/admin-button-CTL3DgOt.js.map +1 -0
  138. package/dist/admin-capabilities-BuigAgtm.js +59 -0
  139. package/dist/admin-capabilities-BuigAgtm.js.map +1 -0
  140. package/dist/admin-card-bzAks3-3.js +51 -0
  141. package/dist/admin-card-bzAks3-3.js.map +1 -0
  142. package/dist/admin-catalog-CK6CM9I2.js +1199 -0
  143. package/dist/admin-catalog-CK6CM9I2.js.map +1 -0
  144. package/dist/admin-confirm-katGq0Qo.js +12 -0
  145. package/dist/admin-confirm-katGq0Qo.js.map +1 -0
  146. package/dist/admin-csv-CPoNZj2D.js +16 -0
  147. package/dist/admin-csv-CPoNZj2D.js.map +1 -0
  148. package/dist/admin-customers-D8u5gq3M.js +160 -0
  149. package/dist/admin-customers-D8u5gq3M.js.map +1 -0
  150. package/dist/admin-dashboard-DqqFjOzq.js +420 -0
  151. package/dist/admin-dashboard-DqqFjOzq.js.map +1 -0
  152. package/dist/admin-data-table-qfXLOFmN.js +96 -0
  153. package/dist/admin-data-table-qfXLOFmN.js.map +1 -0
  154. package/dist/admin-detail-5aNNvrc0.js +324 -0
  155. package/dist/admin-detail-5aNNvrc0.js.map +1 -0
  156. package/dist/admin-detail-7p684Uyc.js +342 -0
  157. package/dist/admin-detail-7p684Uyc.js.map +1 -0
  158. package/dist/admin-detail-CGOyqs7s.js +1312 -0
  159. package/dist/admin-detail-CGOyqs7s.js.map +1 -0
  160. package/dist/admin-detail-DUAJQg8G.js +710 -0
  161. package/dist/admin-detail-DUAJQg8G.js.map +1 -0
  162. package/dist/admin-dialog-BjuKI5Hp.js +88 -0
  163. package/dist/admin-dialog-BjuKI5Hp.js.map +1 -0
  164. package/dist/admin-dialog-width-BPrgdYlj.js +6 -0
  165. package/dist/admin-dialog-width-BPrgdYlj.js.map +1 -0
  166. package/dist/admin-dom-uMmZzRvU.js +149 -0
  167. package/dist/admin-dom-uMmZzRvU.js.map +1 -0
  168. package/dist/admin-dropdown-menu-BW1D_h6s.js +69 -0
  169. package/dist/admin-dropdown-menu-BW1D_h6s.js.map +1 -0
  170. package/dist/admin-dunning-B2zgtrOo.js +10 -0
  171. package/dist/admin-dunning-B2zgtrOo.js.map +1 -0
  172. package/dist/admin-fact-card-DO_fHosg.js +20 -0
  173. package/dist/admin-fact-card-DO_fHosg.js.map +1 -0
  174. package/dist/admin-form-field-errors-hbUMIriS.js +14 -0
  175. package/dist/admin-form-field-errors-hbUMIriS.js.map +1 -0
  176. package/dist/admin-format-DoohJ7Fg.js +136 -0
  177. package/dist/admin-format-DoohJ7Fg.js.map +1 -0
  178. package/dist/admin-health-B4jN2LwR.js +983 -0
  179. package/dist/admin-health-B4jN2LwR.js.map +1 -0
  180. package/dist/admin-input-CcfQfJFN.js +17 -0
  181. package/dist/admin-input-CcfQfJFN.js.map +1 -0
  182. package/dist/admin-invoice-profile-model-Cgjy3zV8.js +53 -0
  183. package/dist/admin-invoice-profile-model-Cgjy3zV8.js.map +1 -0
  184. package/dist/admin-label-BiEN5cjb.js +15 -0
  185. package/dist/admin-label-BiEN5cjb.js.map +1 -0
  186. package/dist/admin-linked-table-row-BsaUruCN.js +26 -0
  187. package/dist/admin-linked-table-row-BsaUruCN.js.map +1 -0
  188. package/dist/admin-metering-COFcSPQy.js +1721 -0
  189. package/dist/admin-metering-COFcSPQy.js.map +1 -0
  190. package/dist/admin-ops-B3mcr76u.js +217 -0
  191. package/dist/admin-ops-B3mcr76u.js.map +1 -0
  192. package/dist/admin-orders-dxC99Idl.js +192 -0
  193. package/dist/admin-orders-dxC99Idl.js.map +1 -0
  194. package/dist/admin-payments-Bs7HSfP0.js +246 -0
  195. package/dist/admin-payments-Bs7HSfP0.js.map +1 -0
  196. package/dist/admin-price-detail-DRquxpA9.js +482 -0
  197. package/dist/admin-price-detail-DRquxpA9.js.map +1 -0
  198. package/dist/admin-price-format-DM1qORWI.js +44 -0
  199. package/dist/admin-price-format-DM1qORWI.js.map +1 -0
  200. package/dist/admin-price-wizard-CI7K-2Nz.js +357 -0
  201. package/dist/admin-price-wizard-CI7K-2Nz.js.map +1 -0
  202. package/dist/admin-range-picker-Dr2M1EZr.js +192 -0
  203. package/dist/admin-range-picker-Dr2M1EZr.js.map +1 -0
  204. package/dist/admin-reports-CywKLiSA.js +320 -0
  205. package/dist/admin-reports-CywKLiSA.js.map +1 -0
  206. package/dist/admin-select-DBgprVW6.js +81 -0
  207. package/dist/admin-select-DBgprVW6.js.map +1 -0
  208. package/dist/admin-settings-Bmoy95uO.js +1434 -0
  209. package/dist/admin-settings-Bmoy95uO.js.map +1 -0
  210. package/dist/admin-skeleton-DsM7crYi.js +14 -0
  211. package/dist/admin-skeleton-DsM7crYi.js.map +1 -0
  212. package/dist/admin-status-badge-BFPgo71d.js +38 -0
  213. package/dist/admin-status-badge-BFPgo71d.js.map +1 -0
  214. package/dist/admin-subscriptions-CLYc_HEV.js +301 -0
  215. package/dist/admin-subscriptions-CLYc_HEV.js.map +1 -0
  216. package/dist/admin-switch-BJUxLCsa.js +20 -0
  217. package/dist/admin-switch-BJUxLCsa.js.map +1 -0
  218. package/dist/admin-table-CH9TrXJf.js +61 -0
  219. package/dist/admin-table-CH9TrXJf.js.map +1 -0
  220. package/dist/admin-tabs-528FJMjt.js +46 -0
  221. package/dist/admin-tabs-528FJMjt.js.map +1 -0
  222. package/dist/admin-textarea-DEz38RaI.js +15 -0
  223. package/dist/admin-textarea-DEz38RaI.js.map +1 -0
  224. package/dist/admin-typed-confirm-dialog-DU7m9-O7.js +66 -0
  225. package/dist/admin-typed-confirm-dialog-DU7m9-O7.js.map +1 -0
  226. package/dist/admin-utils-BK4Vh1o_.js +1809 -0
  227. package/dist/admin-utils-BK4Vh1o_.js.map +1 -0
  228. package/dist/admin-widget-viz-Da48c-Zf.js +581 -0
  229. package/dist/admin-widget-viz-Da48c-Zf.js.map +1 -0
  230. package/dist/admin.css +3 -0
  231. package/dist/admin.js +2164 -0
  232. package/dist/admin.js.map +1 -0
  233. package/dist/appearance.d.ts +1 -1
  234. package/dist/client/client.d.ts +13 -6
  235. package/dist/client/generated/error-codes.d.ts +1 -1
  236. package/dist/client/generated/routes.d.ts +2173 -178
  237. package/dist/client/generated/wire.d.ts +1287 -1
  238. package/dist/client/index.d.ts +1 -1
  239. package/dist/client/types.d.ts +14 -2
  240. package/dist/{client-PjivPHmE.js → client-D5Af726I.js} +8 -7
  241. package/dist/client-D5Af726I.js.map +1 -0
  242. package/dist/client.js +1 -1
  243. package/dist/{hooks-Uiw-zW3H.js → hooks-CYjlHS28.js} +8 -8
  244. package/dist/hooks-CYjlHS28.js.map +1 -0
  245. package/dist/index.d.ts +1 -1
  246. package/dist/index.js +12 -12
  247. package/dist/index.js.map +1 -1
  248. package/dist/lib/{offer-label.d.ts → price-label.d.ts} +1 -1
  249. package/dist/locales/de.js +1 -1
  250. package/dist/locales/de.js.map +1 -1
  251. package/dist/locales/en.d.ts +1 -1
  252. package/dist/locales/en.js +1 -1
  253. package/dist/locales/en.js.map +1 -1
  254. package/dist/locales/es.js +1 -1
  255. package/dist/locales/es.js.map +1 -1
  256. package/dist/locales/ja.js +1 -1
  257. package/dist/locales/ja.js.map +1 -1
  258. package/dist/locales/ko.js +1 -1
  259. package/dist/locales/ko.js.map +1 -1
  260. package/dist/locales/zh.js +1 -1
  261. package/dist/locales/zh.js.map +1 -1
  262. package/dist/{offers.d.ts → product-list.d.ts} +5 -5
  263. package/dist/react.js +1 -1
  264. package/dist/styles.css +1 -1
  265. package/package.json +28 -6
  266. package/dist/client-PjivPHmE.js.map +0 -1
  267. package/dist/hooks-Uiw-zW3H.js.map +0 -1
@@ -0,0 +1,420 @@
1
+ import { E as e, S as t, _ as n, i as r, p as i, r as a, t as o, w as s } from "./admin-utils-BK4Vh1o_.js";
2
+ import { i as c, n as l, s as u, t as d } from "./admin-dropdown-menu-BW1D_h6s.js";
3
+ import { t as f } from "./admin-button-CTL3DgOt.js";
4
+ import { t as p } from "./admin-input-CcfQfJFN.js";
5
+ import { t as m } from "./admin-skeleton-DsM7crYi.js";
6
+ import { c as h, r as g } from "./admin-capabilities-BuigAgtm.js";
7
+ import { n as _ } from "./admin-confirm-katGq0Qo.js";
8
+ import { a as v, i as y, n as b, r as x, t as S } from "./admin-select-DBgprVW6.js";
9
+ import { a as C, i as w, n as T, o as E, r as D, t as O } from "./admin-dialog-BjuKI5Hp.js";
10
+ import { t as k } from "./admin-label-BiEN5cjb.js";
11
+ import { t as A } from "./admin-switch-BJUxLCsa.js";
12
+ import { r as j } from "./admin-dialog-width-BPrgdYlj.js";
13
+ import { r as M, t as N } from "./admin-card-bzAks3-3.js";
14
+ import { a as P, n as F, t as I } from "./admin-range-picker-Dr2M1EZr.js";
15
+ import { n as L, o as ee, t as R } from "./admin-widget-viz-Da48c-Zf.js";
16
+ import * as z from "react";
17
+ import { useMutation as B, useQuery as V, useQueryClient as H } from "@tanstack/react-query";
18
+ import { Fragment as te, jsx as U, jsxs as W } from "react/jsx-runtime";
19
+ import { HugeiconsIcon as G } from "@hugeicons/react";
20
+ import { ArrowUpRight01Icon as ne, Delete02Icon as K, DragDropVerticalIcon as q, Loading02Icon as J, MoreVerticalIcon as Y, PencilIcon as X, Refresh01Icon as re } from "@hugeicons/core-free-icons";
21
+ import { GridLayout as ie } from "react-grid-layout";
22
+ //#region src/admin/pages/dashboard/widget-editor.tsx
23
+ var Z = {
24
+ stat: "Single number",
25
+ line: "Line chart",
26
+ area: "Area chart",
27
+ bar: "Bar chart",
28
+ donut: "Donut chart",
29
+ table: "Table"
30
+ };
31
+ function ae({ onOpenChange: r, initial: a, onSave: c }) {
32
+ let { id: l, text: u } = e(), { adminQueries: d } = i(), [m, h] = z.useState(a.title), [g, _] = z.useState(a.fixed_range ?? !1), [M, N] = z.useState(a.viz), { query: P } = a, { data: F, error: I, isFetching: L, refetch: B } = V(d.widgetMetrics(P)), H = I ? I instanceof t ? I.message : "query failed" : null;
33
+ return /* @__PURE__ */ U(O, {
34
+ open: !0,
35
+ onOpenChange: r,
36
+ children: /* @__PURE__ */ W(T, {
37
+ className: o("flex max-h-[90vh] flex-col overflow-hidden", j),
38
+ children: [
39
+ /* @__PURE__ */ W(C, { children: [/* @__PURE__ */ U(E, { children: /* @__PURE__ */ U(s, { id: "Edit widget" }) }), /* @__PURE__ */ U(D, { children: /* @__PURE__ */ U(s, { id: "Change the title, chart type or date range behavior. The query stays the same." }) })] }),
40
+ /* @__PURE__ */ W("div", {
41
+ className: "flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto",
42
+ children: [
43
+ /* @__PURE__ */ W("div", {
44
+ className: "grid gap-2 sm:grid-cols-[1fr_12rem]",
45
+ children: [/* @__PURE__ */ W("div", {
46
+ className: "flex flex-col gap-1.5",
47
+ children: [/* @__PURE__ */ U(k, {
48
+ htmlFor: l("widget-title"),
49
+ children: /* @__PURE__ */ U(s, { id: "Title" })
50
+ }), /* @__PURE__ */ U(p, {
51
+ id: l("widget-title"),
52
+ value: m,
53
+ onChange: (e) => h(e.target.value),
54
+ placeholder: u("Widget title")
55
+ })]
56
+ }), /* @__PURE__ */ W("div", {
57
+ className: "flex flex-col gap-1.5",
58
+ children: [/* @__PURE__ */ U(k, {
59
+ htmlFor: l("widget-viz"),
60
+ children: /* @__PURE__ */ U(s, { id: "Chart type" })
61
+ }), /* @__PURE__ */ W(S, {
62
+ items: n.map((e) => ({
63
+ value: e,
64
+ label: Z[e]
65
+ })),
66
+ value: M,
67
+ onValueChange: (e) => N(e),
68
+ children: [/* @__PURE__ */ U(y, {
69
+ id: l("widget-viz"),
70
+ className: "w-full",
71
+ children: /* @__PURE__ */ U(v, {})
72
+ }), /* @__PURE__ */ U(b, { children: n.map((e) => /* @__PURE__ */ U(x, {
73
+ value: e,
74
+ children: Z[e]
75
+ }, e)) })]
76
+ })]
77
+ })]
78
+ }),
79
+ /* @__PURE__ */ W("div", {
80
+ className: "flex items-start gap-3",
81
+ children: [/* @__PURE__ */ U(A, {
82
+ id: l("widget-fixed-range"),
83
+ checked: g,
84
+ onCheckedChange: _
85
+ }), /* @__PURE__ */ W("div", {
86
+ className: "grid gap-0.5",
87
+ children: [/* @__PURE__ */ U(k, {
88
+ htmlFor: l("widget-fixed-range"),
89
+ children: /* @__PURE__ */ U(s, { id: "Keep its own range" })
90
+ }), /* @__PURE__ */ U("p", {
91
+ className: "text-xs text-muted-foreground",
92
+ children: g ? `Always shows ${ee(P.range)}, whatever range the dashboard shows.` : "Follows the range picked on the dashboard."
93
+ })]
94
+ })]
95
+ }),
96
+ /* @__PURE__ */ W("div", {
97
+ className: "flex min-h-56 flex-1 flex-col rounded-lg border bg-muted/30 p-3",
98
+ children: [/* @__PURE__ */ W("div", {
99
+ className: "mb-2 flex items-center justify-between",
100
+ children: [/* @__PURE__ */ U("span", {
101
+ className: "text-xs font-medium text-muted-foreground uppercase",
102
+ children: /* @__PURE__ */ U(s, { id: "Preview" })
103
+ }), /* @__PURE__ */ W(f, {
104
+ size: "sm",
105
+ variant: "outline",
106
+ onClick: () => void B(),
107
+ disabled: L,
108
+ children: [
109
+ L ? /* @__PURE__ */ U(G, {
110
+ icon: J,
111
+ className: "size-3.5 animate-spin"
112
+ }) : null,
113
+ " ",
114
+ /* @__PURE__ */ U(s, { id: "Refresh" })
115
+ ]
116
+ })]
117
+ }), /* @__PURE__ */ U("div", {
118
+ className: "min-h-0 flex-1",
119
+ children: H ? /* @__PURE__ */ U("p", {
120
+ className: "text-xs whitespace-pre-wrap text-destructive",
121
+ role: "alert",
122
+ children: H
123
+ }) : F ? /* @__PURE__ */ U(R, {
124
+ viz: M,
125
+ result: F,
126
+ query: P
127
+ }) : /* @__PURE__ */ U("p", {
128
+ className: "flex h-full items-center justify-center text-xs text-muted-foreground",
129
+ children: L ? "Running…" : "No preview yet"
130
+ })
131
+ })]
132
+ })
133
+ ]
134
+ }),
135
+ /* @__PURE__ */ W(w, { children: [/* @__PURE__ */ U(f, {
136
+ variant: "outline",
137
+ onClick: () => r(!1),
138
+ children: /* @__PURE__ */ U(s, { id: "Cancel" })
139
+ }), /* @__PURE__ */ U(f, {
140
+ onClick: () => {
141
+ c({
142
+ title: m.trim(),
143
+ viz: M,
144
+ fixed_range: g
145
+ }), r(!1);
146
+ },
147
+ disabled: !m.trim(),
148
+ children: /* @__PURE__ */ U(s, { id: "Save changes" })
149
+ })] })
150
+ ]
151
+ })
152
+ });
153
+ }
154
+ //#endregion
155
+ //#region src/admin/pages/dashboard/widget-tile.tsx
156
+ function Q({ widget: t, range: n, editable: r = !0, onEdit: p, onDelete: m }) {
157
+ let { text: h } = e(), { adminQueries: g } = i(), _ = P(t, n), { data: v, isPending: y, isFetching: b, error: x, refetch: S } = V(g.widgetMetrics(_)), C = L(t.query, t.viz), w = t.viz === "stat";
158
+ return /* @__PURE__ */ W(N, {
159
+ className: "group/tile relative h-full gap-2",
160
+ children: [/* @__PURE__ */ W("div", {
161
+ className: "absolute top-2 right-2 z-10 flex shrink-0 items-center gap-0.5 rounded-md border border-border bg-card opacity-0 shadow-sm transition-opacity group-hover/tile:opacity-100 focus-within:opacity-100",
162
+ children: [
163
+ r && /* @__PURE__ */ U(G, {
164
+ icon: q,
165
+ "aria-hidden": !0,
166
+ className: "widget-drag-handle size-6 shrink-0 cursor-grab p-1.5 text-muted-foreground active:cursor-grabbing"
167
+ }),
168
+ C ? /* @__PURE__ */ U(a, {
169
+ to: C,
170
+ className: "flex size-6 items-center justify-center text-muted-foreground hover:text-foreground",
171
+ title: h("Open matching list"),
172
+ "aria-label": h("Open matching list"),
173
+ children: /* @__PURE__ */ U(G, {
174
+ icon: ne,
175
+ className: "size-3.5"
176
+ })
177
+ }) : null,
178
+ /* @__PURE__ */ U(f, {
179
+ variant: "ghost",
180
+ size: "icon",
181
+ className: "size-6",
182
+ onClick: () => void S(),
183
+ title: h("Refresh"),
184
+ "aria-label": h("Refresh widget"),
185
+ children: /* @__PURE__ */ U(G, {
186
+ icon: re,
187
+ className: o("size-3.5", b && "animate-spin")
188
+ })
189
+ }),
190
+ r && /* @__PURE__ */ W(d, { children: [/* @__PURE__ */ U(u, { render: /* @__PURE__ */ U(f, {
191
+ variant: "ghost",
192
+ size: "icon",
193
+ className: "size-6",
194
+ "aria-label": h("Widget menu"),
195
+ children: /* @__PURE__ */ U(G, {
196
+ icon: Y,
197
+ className: "size-3.5"
198
+ })
199
+ }) }), /* @__PURE__ */ W(l, {
200
+ align: "end",
201
+ children: [/* @__PURE__ */ W(c, {
202
+ onClick: p,
203
+ children: [/* @__PURE__ */ U(G, {
204
+ icon: X,
205
+ className: "size-3.5"
206
+ }), /* @__PURE__ */ U(s, { id: " Edit widget" })]
207
+ }), /* @__PURE__ */ W(c, {
208
+ onClick: m,
209
+ variant: "destructive",
210
+ children: [
211
+ /* @__PURE__ */ U(G, {
212
+ icon: K,
213
+ className: "size-3.5"
214
+ }),
215
+ " ",
216
+ /* @__PURE__ */ U(s, { id: "Remove" })
217
+ ]
218
+ })]
219
+ })] })
220
+ ]
221
+ }), /* @__PURE__ */ W(M, {
222
+ className: "flex min-h-0 flex-1 flex-col gap-2",
223
+ children: [/* @__PURE__ */ U("span", {
224
+ className: o("truncate", w ? "text-xs font-medium tracking-wider text-muted-foreground uppercase" : "text-sm font-medium"),
225
+ title: t.title,
226
+ children: t.title
227
+ }), /* @__PURE__ */ U("div", {
228
+ className: "min-h-0 flex-1",
229
+ children: /* @__PURE__ */ U(oe, {
230
+ loading: y && !v,
231
+ error: x,
232
+ widget: t,
233
+ data: v ?? null
234
+ })
235
+ })]
236
+ })]
237
+ });
238
+ }
239
+ function oe({ loading: e, error: n, widget: r, data: i }) {
240
+ if (e) return /* @__PURE__ */ W("div", {
241
+ className: "flex h-full flex-col justify-center gap-2",
242
+ children: [/* @__PURE__ */ U(m, { className: "h-6 w-2/3" }), /* @__PURE__ */ U(m, { className: "h-3 w-1/3" })]
243
+ });
244
+ if (n) {
245
+ let e = n instanceof t ? n.isPermissionDenied ? "your role lacks metrics access" : n.message : "query failed";
246
+ return /* @__PURE__ */ U("div", {
247
+ className: "flex h-full items-center justify-center text-center text-xs text-destructive",
248
+ role: "alert",
249
+ children: e
250
+ });
251
+ }
252
+ return i ? /* @__PURE__ */ U(R, {
253
+ viz: r.viz,
254
+ result: i,
255
+ query: r.query
256
+ }) : null;
257
+ }
258
+ //#endregion
259
+ //#region src/admin/pages/dashboard/index.tsx
260
+ var se = 12, $ = 56;
261
+ function ce() {
262
+ let [e, t] = z.useState(null), [n, r] = z.useState(0);
263
+ return z.useEffect(() => {
264
+ if (!e) return;
265
+ let t = new ResizeObserver((e) => {
266
+ r(Math.round(e[0].contentRect.width));
267
+ });
268
+ return t.observe(e), () => t.disconnect();
269
+ }, [e]), {
270
+ containerRef: t,
271
+ width: n,
272
+ mounted: n > 0
273
+ };
274
+ }
275
+ function le(e, t) {
276
+ let n = new Map(t.map((e) => [e.i, e.y * 1e3 + e.x]));
277
+ return [...e].sort((e, t) => (n.get(e.id) ?? 0) - (n.get(t.id) ?? 0));
278
+ }
279
+ function ue() {
280
+ let e = g();
281
+ return e ? e.metrics ? /* @__PURE__ */ U(fe, {}) : /* @__PURE__ */ U(r, {
282
+ to: de(e),
283
+ replace: !0
284
+ }) : null;
285
+ }
286
+ function de(e) {
287
+ return e.admin === "read" || e.admin === "update" ? "/customers" : e.catalog === "read" || e.catalog === "update" ? "/catalog" : "/settings";
288
+ }
289
+ function fe() {
290
+ let { toastApiError: e, adminMutations: t, adminQueries: n } = i(), r = H(), a = h(), { mutate: o } = B(t.saveDashboard(r)), c = n.dashboard(), { data: l, isPending: u } = V(c), [d, f] = z.useState(null), p = d ?? l?.widgets ?? null, [g, v] = z.useState(!1), [y, b] = z.useState(null), { width: x, containerRef: S, mounted: C } = ce(), [w] = F(), T = _(), E = z.useRef(null);
291
+ z.useEffect(() => () => {
292
+ E.current && clearTimeout(E.current);
293
+ }, []);
294
+ let D = z.useCallback((t) => {
295
+ E.current && clearTimeout(E.current), E.current = setTimeout(() => {
296
+ o(t, { onError: (t) => e(t, "Save dashboard") });
297
+ }, 800);
298
+ }, [o, e]), O = z.useCallback((e) => {
299
+ f(e), D(e);
300
+ }, [D]), k = (e) => {
301
+ if (!p || !a) return;
302
+ let t = new Map(e.map((e) => [e.i, e])), n = !1, r = p.map((e) => {
303
+ let r = t.get(e.id);
304
+ return r && (r.x !== e.grid.x || r.y !== e.grid.y || r.w !== e.grid.w || r.h !== e.grid.h) ? (n = !0, {
305
+ ...e,
306
+ grid: {
307
+ x: r.x,
308
+ y: r.y,
309
+ w: r.w,
310
+ h: r.h
311
+ }
312
+ }) : e;
313
+ });
314
+ n && O(r);
315
+ }, A = (e, t) => {
316
+ p && O(p.map((n) => n.id === e ? {
317
+ ...n,
318
+ ...t
319
+ } : n));
320
+ }, j = async (e) => {
321
+ p && await T({
322
+ title: `Remove "${e.title}"?`,
323
+ description: "This removes it from the merchant dashboard for everyone.",
324
+ confirmLabel: "Remove widget",
325
+ tone: "destructive"
326
+ }) && O(p.filter((t) => t.id !== e.id));
327
+ }, M = /* @__PURE__ */ W("div", {
328
+ className: "flex flex-wrap items-center justify-between gap-3",
329
+ children: [/* @__PURE__ */ U("h1", {
330
+ className: "text-2xl font-semibold tracking-tight",
331
+ children: /* @__PURE__ */ U(s, { id: "Dashboard" })
332
+ }), /* @__PURE__ */ U("div", {
333
+ className: "flex flex-wrap items-center gap-3",
334
+ children: /* @__PURE__ */ U(I, {})
335
+ })]
336
+ });
337
+ if (u && !p) return /* @__PURE__ */ W("div", {
338
+ className: "flex flex-col gap-4",
339
+ children: [M, /* @__PURE__ */ W("div", {
340
+ className: "grid grid-cols-1 gap-3 md:grid-cols-4",
341
+ children: [
342
+ Array.from({ length: 4 }).map((e, t) => /* @__PURE__ */ U(m, { className: "h-[124px]" }, t)),
343
+ /* @__PURE__ */ U(m, { className: "h-64 md:col-span-2" }),
344
+ /* @__PURE__ */ U(m, { className: "h-64 md:col-span-2" })
345
+ ]
346
+ })]
347
+ });
348
+ if (!p) return null;
349
+ let N = p.map((e) => ({
350
+ i: e.id,
351
+ x: e.grid.x,
352
+ y: e.grid.y,
353
+ w: e.grid.w,
354
+ h: e.grid.h,
355
+ minW: 2,
356
+ minH: 2
357
+ }));
358
+ return /* @__PURE__ */ W("div", {
359
+ className: "flex flex-col gap-4",
360
+ children: [
361
+ M,
362
+ /* @__PURE__ */ U("div", {
363
+ ref: S,
364
+ className: "min-h-0 w-full",
365
+ children: p.length === 0 ? /* @__PURE__ */ U("div", {
366
+ className: "flex h-48 flex-col items-center justify-center gap-3 rounded-xl border border-dashed",
367
+ children: /* @__PURE__ */ U("p", {
368
+ className: "text-sm text-muted-foreground",
369
+ children: /* @__PURE__ */ U(s, { id: "No widgets yet" })
370
+ })
371
+ }) : /* @__PURE__ */ W(te, { children: [/* @__PURE__ */ U("div", {
372
+ className: "grid gap-3 md:hidden",
373
+ children: le(p, N).map((e) => /* @__PURE__ */ U("div", {
374
+ className: e.viz === "stat" ? "h-[124px]" : "h-64",
375
+ children: /* @__PURE__ */ U(Q, {
376
+ widget: e,
377
+ range: w,
378
+ editable: a,
379
+ onEdit: () => {
380
+ b(e), v(!0);
381
+ },
382
+ onDelete: () => void j(e)
383
+ })
384
+ }, e.id))
385
+ }), /* @__PURE__ */ U("div", {
386
+ className: "hidden md:block",
387
+ children: C ? /* @__PURE__ */ U(ie, {
388
+ layout: N,
389
+ width: x,
390
+ gridConfig: {
391
+ cols: se,
392
+ rowHeight: $,
393
+ margin: [12, 12]
394
+ },
395
+ dragConfig: { handle: ".widget-drag-handle" },
396
+ onLayoutChange: k,
397
+ children: p.map((e) => /* @__PURE__ */ U("div", { children: /* @__PURE__ */ U(Q, {
398
+ widget: e,
399
+ range: w,
400
+ editable: a,
401
+ onEdit: () => {
402
+ b(e), v(!0);
403
+ },
404
+ onDelete: () => void j(e)
405
+ }) }, e.id))
406
+ }) : null
407
+ })] })
408
+ }),
409
+ g && y ? /* @__PURE__ */ U(ae, {
410
+ onOpenChange: v,
411
+ initial: y,
412
+ onSave: (e) => A(y.id, e)
413
+ }) : null
414
+ ]
415
+ });
416
+ }
417
+ //#endregion
418
+ export { ue as DashboardPage };
419
+
420
+ //# sourceMappingURL=admin-dashboard-DqqFjOzq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-dashboard-DqqFjOzq.js","names":[],"sources":["../src/admin/pages/dashboard/widget-editor.tsx","../src/admin/pages/dashboard/widget-tile.tsx","../src/admin/pages/dashboard/index.tsx"],"sourcesContent":["import { AdminMessage, useAdminUI } from \"#orck/admin/ui-context\"\nimport { useAdminServices } from \"#orck/admin/context\"\n// Edit an existing widget's presentation. Its metrics query stays unchanged.\nimport { HugeiconsIcon } from \"@hugeicons/react\"\nimport { Loading02Icon } from \"@hugeicons/core-free-icons\"\nimport * as React from \"react\"\nimport { useQuery } from \"@tanstack/react-query\"\nimport { Button } from \"#orck/admin/components/ui/button\"\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"#orck/admin/components/ui/dialog\"\nimport { Input } from \"#orck/admin/components/ui/input\"\nimport { Label } from \"#orck/admin/components/ui/label\"\nimport { Switch } from \"#orck/admin/components/ui/switch\"\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"#orck/admin/components/ui/select\"\nimport { ApiError } from \"#orck/admin/lib/api/client\"\nimport { DIALOG_WIDE } from \"#orck/admin/lib/dialog-width\"\nimport {\n WIDGET_VIZ,\n type Widget,\n type WidgetViz,\n} from \"#orck/admin/lib/api/metrics\"\n\nimport { cn } from \"#orck/admin/lib/utils\"\nimport { ownRange } from \"./lib\"\nimport { WidgetVizView } from \"./widget-viz\"\n\nconst VIZ_LABELS: Record<WidgetViz, string> = {\n stat: \"Single number\",\n line: \"Line chart\",\n area: \"Area chart\",\n bar: \"Bar chart\",\n donut: \"Donut chart\",\n table: \"Table\",\n}\n\nexport type WidgetDraft = Pick<Widget, \"title\" | \"viz\" | \"fixed_range\">\n\nexport function WidgetEditor({\n onOpenChange,\n initial,\n onSave,\n}: {\n onOpenChange: (open: boolean) => void\n initial: Widget\n onSave: (data: WidgetDraft) => void\n}) {\n const { id: adminId, text: adminText } = useAdminUI()\n\n const { adminQueries } = useAdminServices()\n\n const [title, setTitle] = React.useState(initial.title)\n const [fixedRange, setFixedRange] = React.useState(\n initial.fixed_range ?? false\n )\n const [viz, setViz] = React.useState<WidgetViz>(initial.viz)\n const { query } = initial\n const {\n data: preview,\n error: previewQueryError,\n isFetching: previewing,\n refetch: refreshPreview,\n } = useQuery(adminQueries.widgetMetrics(query))\n const previewError = previewQueryError\n ? previewQueryError instanceof ApiError\n ? previewQueryError.message\n : \"query failed\"\n : null\n\n return (\n <Dialog open onOpenChange={onOpenChange}>\n <DialogContent\n className={cn(\n \"flex max-h-[90vh] flex-col overflow-hidden\",\n DIALOG_WIDE\n )}\n >\n <DialogHeader>\n <DialogTitle>\n <AdminMessage id={\"Edit widget\"} />\n </DialogTitle>\n <DialogDescription>\n <AdminMessage\n id={\n \"Change the title, chart type or date range behavior. The query stays the same.\"\n }\n />\n </DialogDescription>\n </DialogHeader>\n <div className=\"flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto\">\n <div className=\"grid gap-2 sm:grid-cols-[1fr_12rem]\">\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor={adminId(\"widget-title\")}>\n <AdminMessage id={\"Title\"} />\n </Label>\n <Input\n id={adminId(\"widget-title\")}\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder={adminText(\"Widget title\")}\n />\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor={adminId(\"widget-viz\")}>\n <AdminMessage id={\"Chart type\"} />\n </Label>\n <Select\n items={WIDGET_VIZ.map((value) => ({\n value,\n label: VIZ_LABELS[value],\n }))}\n value={viz}\n onValueChange={(v) => setViz(v as WidgetViz)}\n >\n <SelectTrigger id={adminId(\"widget-viz\")} className=\"w-full\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {WIDGET_VIZ.map((v) => (\n <SelectItem key={v} value={v}>\n {VIZ_LABELS[v]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n <div className=\"flex items-start gap-3\">\n <Switch\n id={adminId(\"widget-fixed-range\")}\n checked={fixedRange}\n onCheckedChange={setFixedRange}\n />\n <div className=\"grid gap-0.5\">\n <Label htmlFor={adminId(\"widget-fixed-range\")}>\n <AdminMessage id={\"Keep its own range\"} />\n </Label>\n <p className=\"text-xs text-muted-foreground\">\n {fixedRange\n ? `Always shows ${ownRange(query.range)}, whatever range the dashboard shows.`\n : \"Follows the range picked on the dashboard.\"}\n </p>\n </div>\n </div>\n <div className=\"flex min-h-56 flex-1 flex-col rounded-lg border bg-muted/30 p-3\">\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-xs font-medium text-muted-foreground uppercase\">\n <AdminMessage id={\"Preview\"} />\n </span>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => void refreshPreview()}\n disabled={previewing}\n >\n {previewing ? (\n <HugeiconsIcon\n icon={Loading02Icon}\n className=\"size-3.5 animate-spin\"\n />\n ) : null}{\" \"}\n <AdminMessage id={\"Refresh\"} />\n </Button>\n </div>\n <div className=\"min-h-0 flex-1\">\n {previewError ? (\n <p\n className=\"text-xs whitespace-pre-wrap text-destructive\"\n role=\"alert\"\n >\n {previewError}\n </p>\n ) : preview ? (\n <WidgetVizView viz={viz} result={preview} query={query} />\n ) : (\n <p className=\"flex h-full items-center justify-center text-xs text-muted-foreground\">\n {previewing ? \"Running…\" : \"No preview yet\"}\n </p>\n )}\n </div>\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n <AdminMessage id={\"Cancel\"} />\n </Button>\n <Button\n onClick={() => {\n onSave({ title: title.trim(), viz, fixed_range: fixedRange })\n onOpenChange(false)\n }}\n disabled={!title.trim()}\n >\n <AdminMessage id={\"Save changes\"} />\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n","import { AdminMessage, useAdminUI } from \"#orck/admin/ui-context\"\nimport { useAdminServices } from \"#orck/admin/context\"\n// One dashboard tile: runs its saved query (POST /v1/metrics/query),\n// renders the viz, and offers refresh / edit / delete. Count widgets deep-link\n// to the matching admin list.\nimport { HugeiconsIcon } from \"@hugeicons/react\"\nimport {\n ArrowUpRight01Icon,\n Delete02Icon,\n DragDropVerticalIcon,\n MoreVerticalIcon,\n PencilIcon,\n Refresh01Icon,\n} from \"@hugeicons/core-free-icons\"\nimport { Link } from \"#orck/admin/router\"\nimport { useQuery } from \"@tanstack/react-query\"\nimport { Button } from \"#orck/admin/components/ui/button\"\nimport { Card, CardContent } from \"#orck/admin/components/ui/card\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"#orck/admin/components/ui/dropdown-menu\"\nimport { Skeleton } from \"#orck/admin/components/ui/skeleton\"\nimport { ApiError } from \"#orck/admin/lib/api/client\"\nimport { type MetricsResult, type Widget } from \"#orck/admin/lib/api/metrics\"\n\nimport { cn } from \"#orck/admin/lib/utils\"\n\nimport { deepLinkFor } from \"./lib\"\nimport { widgetQuery, type DashboardRange } from \"./range\"\nimport { WidgetVizView } from \"./widget-viz\"\n\nexport function WidgetTile({\n widget,\n range,\n editable = true,\n onEdit,\n onDelete,\n}: {\n widget: Widget\n range: DashboardRange\n // editable shows the drag handle and the edit and remove menu.\n editable?: boolean\n onEdit: () => void\n onDelete: () => void\n}) {\n const { text: adminText } = useAdminUI()\n\n const { adminQueries } = useAdminServices()\n\n const query = widgetQuery(widget, range)\n const {\n data,\n isPending: loading,\n isFetching,\n error,\n refetch,\n } = useQuery(adminQueries.widgetMetrics(query))\n const link = deepLinkFor(widget.query, widget.viz)\n\n const isStat = widget.viz === \"stat\"\n\n return (\n <Card className=\"group/tile relative h-full gap-2\">\n <div className=\"absolute top-2 right-2 z-10 flex shrink-0 items-center gap-0.5 rounded-md border border-border bg-card opacity-0 shadow-sm transition-opacity group-hover/tile:opacity-100 focus-within:opacity-100\">\n {editable && (\n <HugeiconsIcon\n icon={DragDropVerticalIcon}\n aria-hidden\n className=\"widget-drag-handle size-6 shrink-0 cursor-grab p-1.5 text-muted-foreground active:cursor-grabbing\"\n />\n )}\n {link ? (\n <Link\n to={link}\n className=\"flex size-6 items-center justify-center text-muted-foreground hover:text-foreground\"\n title={adminText(\"Open matching list\")}\n aria-label={adminText(\"Open matching list\")}\n >\n <HugeiconsIcon icon={ArrowUpRight01Icon} className=\"size-3.5\" />\n </Link>\n ) : null}\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-6\"\n onClick={() => void refetch()}\n title={adminText(\"Refresh\")}\n aria-label={adminText(\"Refresh widget\")}\n >\n <HugeiconsIcon\n icon={Refresh01Icon}\n className={cn(\"size-3.5\", isFetching && \"animate-spin\")}\n />\n </Button>\n {editable && (\n <DropdownMenu>\n <DropdownMenuTrigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-6\"\n aria-label={adminText(\"Widget menu\")}\n >\n <HugeiconsIcon icon={MoreVerticalIcon} className=\"size-3.5\" />\n </Button>\n }\n />\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onClick={onEdit}>\n <HugeiconsIcon icon={PencilIcon} className=\"size-3.5\" />\n <AdminMessage id={\" Edit widget\"} />\n </DropdownMenuItem>\n <DropdownMenuItem onClick={onDelete} variant=\"destructive\">\n <HugeiconsIcon icon={Delete02Icon} className=\"size-3.5\" />{\" \"}\n <AdminMessage id={\"Remove\"} />\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </div>\n <CardContent className=\"flex min-h-0 flex-1 flex-col gap-2\">\n <span\n className={cn(\n \"truncate\",\n isStat\n ? \"text-xs font-medium tracking-wider text-muted-foreground uppercase\"\n : \"text-sm font-medium\"\n )}\n title={widget.title}\n >\n {widget.title}\n </span>\n <div className=\"min-h-0 flex-1\">\n <TileBody\n loading={loading && !data}\n error={error}\n widget={widget}\n data={data ?? null}\n />\n </div>\n </CardContent>\n </Card>\n )\n}\n\nfunction TileBody({\n loading,\n error,\n widget,\n data,\n}: {\n loading: boolean\n error: unknown\n widget: Widget\n data: MetricsResult | null\n}) {\n if (loading) {\n return (\n <div className=\"flex h-full flex-col justify-center gap-2\">\n <Skeleton className=\"h-6 w-2/3\" />\n <Skeleton className=\"h-3 w-1/3\" />\n </div>\n )\n }\n if (error) {\n const msg =\n error instanceof ApiError\n ? error.isPermissionDenied\n ? \"your role lacks metrics access\"\n : error.message\n : \"query failed\"\n return (\n <div\n className=\"flex h-full items-center justify-center text-center text-xs text-destructive\"\n role=\"alert\"\n >\n {msg}\n </div>\n )\n }\n if (!data) return null\n return <WidgetVizView viz={widget.viz} result={data} query={widget.query} />\n}\n","import { AdminMessage } from \"#orck/admin/ui-context\"\nimport { useAdminServices } from \"#orck/admin/context\"\n// #741 configurable dashboard: a per-merchant widget grid over the metrics\n// API. Tile registry + serialized Layout[] on react-grid-layout v2 — drag,\n// resize, edit, remove; the layout persists via PUT (debounced). First\n// load with no saved row shows the seeded default template.\nimport type { AdminAccess } from \"#orck/client/generated/wire\"\nimport { useAccess, useDashboardLayout } from \"#orck/admin/lib/capabilities\"\nimport * as React from \"react\"\nimport { Navigate } from \"#orck/admin/router\"\nimport { GridLayout, type Layout } from \"react-grid-layout\"\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\"\n\nimport \"react-grid-layout/css/styles.css\"\n\nimport { useConfirm } from \"#orck/admin/lib/confirm\"\nimport { Skeleton } from \"#orck/admin/components/ui/skeleton\"\nimport type { Widget } from \"#orck/admin/lib/api/metrics\"\n\nimport { RangePicker } from \"./range-picker\"\nimport { useDashboardRange } from \"./use-range\"\nimport { WidgetEditor, type WidgetDraft } from \"./widget-editor\"\nimport { WidgetTile } from \"./widget-tile\"\n\nconst COLS = 12\nconst ROW_HEIGHT = 56\n\n// useMeasuredWidth tracks the live content width of the grid container; the\n// ResizeObserver fires on observe and on every resize, so the grid rescales\n// with the container instead of trusting a one-shot measurement.\nfunction useMeasuredWidth() {\n // Callback ref: the grid container mounts after loading returns early, so\n // a one-shot ref effect would observe null and never re-attach.\n const [el, setEl] = React.useState<HTMLDivElement | null>(null)\n const [width, setWidth] = React.useState(0)\n React.useEffect(() => {\n if (!el) return\n const observer = new ResizeObserver((entries) => {\n setWidth(Math.round(entries[0].contentRect.width))\n })\n observer.observe(el)\n return () => observer.disconnect()\n }, [el])\n return { containerRef: setEl, width, mounted: width > 0 }\n}\n\n// stackedWidgets orders widgets by their grid position (top-left first) for\n// the single-column mobile flow.\nfunction stackedWidgets<T extends { id: string }>(\n widgets: T[],\n layout: Layout\n): T[] {\n const pos = new Map(layout.map((l) => [l.i, l.y * 1000 + l.x]))\n return [...widgets].sort(\n (a, b) => (pos.get(a.id) ?? 0) - (pos.get(b.id) ?? 0)\n )\n}\n\n// DashboardPage is the console's home: the metrics dashboard for a caller\n// holding Metrics, else their first area.\nexport function DashboardPage() {\n const access = useAccess()\n if (!access) return null\n if (!access.metrics) return <Navigate to={firstArea(access)} replace />\n return <MetricsDashboard />\n}\n\n// firstArea is where a caller without metrics starts.\nfunction firstArea(access: AdminAccess): string {\n if (access.admin === \"read\" || access.admin === \"update\") return \"/customers\"\n if (access.catalog === \"read\" || access.catalog === \"update\")\n return \"/catalog\"\n return \"/settings\"\n}\n\nfunction MetricsDashboard() {\n const { toastApiError, adminMutations, adminQueries } = useAdminServices()\n\n const queryClient = useQueryClient()\n // The layout is the merchant's configuration, over its metrics: editable\n // only to a caller holding both.\n const editable = useDashboardLayout()\n const { mutate: saveDashboard } = useMutation(\n adminMutations.saveDashboard(queryClient)\n )\n const dashboardOptions = adminQueries.dashboard()\n const { data, isPending: loading } = useQuery(dashboardOptions)\n const [draftWidgets, setDraftWidgets] = React.useState<Widget[] | null>(null)\n const widgets = draftWidgets ?? data?.widgets ?? null\n const [editorOpen, setEditorOpen] = React.useState(false)\n const [editing, setEditing] = React.useState<Widget | null>(null)\n const { width, containerRef, mounted } = useMeasuredWidth()\n const [range] = useDashboardRange()\n const confirm = useConfirm()\n const saveTimer = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n\n React.useEffect(\n () => () => {\n if (saveTimer.current) clearTimeout(saveTimer.current)\n },\n []\n )\n\n // Debounced persistence: drags/resizes coalesce into one PUT.\n const persist = React.useCallback(\n (next: Widget[]) => {\n if (saveTimer.current) clearTimeout(saveTimer.current)\n saveTimer.current = setTimeout(() => {\n saveDashboard(next, {\n onError: (err) => toastApiError(err, \"Save dashboard\"),\n })\n }, 800)\n },\n [saveDashboard, toastApiError]\n )\n\n const update = React.useCallback(\n (next: Widget[]) => {\n setDraftWidgets(next)\n persist(next)\n },\n [persist]\n )\n\n const onLayoutChange = (layout: Layout) => {\n if (!widgets || !editable) return\n const byId = new Map(layout.map((l) => [l.i, l]))\n let changed = false\n const next = widgets.map((w) => {\n const l = byId.get(w.id)\n if (!l) return w\n if (\n l.x !== w.grid.x ||\n l.y !== w.grid.y ||\n l.w !== w.grid.w ||\n l.h !== w.grid.h\n ) {\n changed = true\n return { ...w, grid: { x: l.x, y: l.y, w: l.w, h: l.h } }\n }\n return w\n })\n if (changed) update(next)\n }\n\n const editWidget = (id: string, draft: WidgetDraft) => {\n if (!widgets) return\n update(widgets.map((w) => (w.id === id ? { ...w, ...draft } : w)))\n }\n\n const removeWidget = async (widget: Widget) => {\n if (!widgets) return\n const removing = await confirm({\n title: `Remove \"${widget.title}\"?`,\n description: \"This removes it from the merchant dashboard for everyone.\",\n confirmLabel: \"Remove widget\",\n tone: \"destructive\",\n })\n if (removing) update(widgets.filter((w) => w.id !== widget.id))\n }\n\n const header = (\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <h1 className=\"text-2xl font-semibold tracking-tight\">\n <AdminMessage id={\"Dashboard\"} />\n </h1>\n <div className=\"flex flex-wrap items-center gap-3\">\n <RangePicker />\n </div>\n </div>\n )\n\n if (loading && !widgets) {\n return (\n <div className=\"flex flex-col gap-4\">\n {header}\n <div className=\"grid grid-cols-1 gap-3 md:grid-cols-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <Skeleton key={i} className=\"h-[124px]\" />\n ))}\n <Skeleton className=\"h-64 md:col-span-2\" />\n <Skeleton className=\"h-64 md:col-span-2\" />\n </div>\n </div>\n )\n }\n if (!widgets) return null\n\n const layout: Layout = widgets.map((w) => ({\n i: w.id,\n x: w.grid.x,\n y: w.grid.y,\n w: w.grid.w,\n h: w.grid.h,\n minW: 2,\n minH: 2,\n }))\n\n return (\n <div className=\"flex flex-col gap-4\">\n {header}\n\n <div ref={containerRef} className=\"min-h-0 w-full\">\n {widgets.length === 0 ? (\n <div className=\"flex h-48 flex-col items-center justify-center gap-3 rounded-xl border border-dashed\">\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"No widgets yet\"} />\n </p>\n </div>\n ) : (\n <>\n <div className=\"grid gap-3 md:hidden\">\n {stackedWidgets(widgets, layout).map((w) => (\n <div\n key={w.id}\n className={w.viz === \"stat\" ? \"h-[124px]\" : \"h-64\"}\n >\n <WidgetTile\n widget={w}\n range={range}\n editable={editable}\n onEdit={() => {\n setEditing(w)\n setEditorOpen(true)\n }}\n onDelete={() => void removeWidget(w)}\n />\n </div>\n ))}\n </div>\n <div className=\"hidden md:block\">\n {mounted ? (\n <GridLayout\n layout={layout}\n width={width}\n gridConfig={{\n cols: COLS,\n rowHeight: ROW_HEIGHT,\n margin: [12, 12],\n }}\n dragConfig={{ handle: \".widget-drag-handle\" }}\n onLayoutChange={onLayoutChange}\n >\n {widgets.map((w) => (\n <div key={w.id}>\n <WidgetTile\n widget={w}\n range={range}\n editable={editable}\n onEdit={() => {\n setEditing(w)\n setEditorOpen(true)\n }}\n onDelete={() => void removeWidget(w)}\n />\n </div>\n ))}\n </GridLayout>\n ) : null}\n </div>\n </>\n )}\n </div>\n\n {editorOpen && editing ? (\n <WidgetEditor\n onOpenChange={setEditorOpen}\n initial={editing}\n onSave={(draft) => editWidget(editing.id, draft)}\n />\n ) : null}\n </div>\n )\n}\n"],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ import { E as e, w as t } from "./admin-utils-BK4Vh1o_.js";
2
+ import { t as n } from "./admin-button-CTL3DgOt.js";
3
+ import { t as r } from "./admin-skeleton-DsM7crYi.js";
4
+ import { t as i } from "./admin-dom-uMmZzRvU.js";
5
+ import { a, n as o, o as s, r as c, s as l, t as u } from "./admin-table-CH9TrXJf.js";
6
+ import { jsx as d, jsxs as f } from "react/jsx-runtime";
7
+ import { HugeiconsIcon as p } from "@hugeicons/react";
8
+ import { ArrowLeft01Icon as m, ArrowRight01Icon as h } from "@hugeicons/core-free-icons";
9
+ import { flexRender as g, getCoreRowModel as _, useReactTable as v } from "@tanstack/react-table";
10
+ //#region src/admin/components/data-table.tsx
11
+ function y({ columns: y, data: b, loading: x, total: S, limit: C, offset: w, onPageChange: T, onRowClick: E, emptyMessage: D = "No results." }) {
12
+ let { text: O } = e(), k = v({
13
+ data: b,
14
+ columns: y,
15
+ getCoreRowModel: _(),
16
+ manualPagination: !0
17
+ }), A = T && C !== void 0 && w !== void 0 && S !== void 0;
18
+ return /* @__PURE__ */ f("div", {
19
+ className: "flex flex-col gap-3",
20
+ children: [/* @__PURE__ */ d("div", {
21
+ className: "overflow-x-auto",
22
+ children: /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d(s, { children: k.getHeaderGroups().map((e) => /* @__PURE__ */ d(l, {
23
+ className: "hover:bg-transparent",
24
+ children: e.headers.map((e) => /* @__PURE__ */ d(a, {
25
+ className: "text-muted-foreground",
26
+ children: e.isPlaceholder ? null : typeof e.column.columnDef.header == "string" ? O(e.column.columnDef.header) : g(e.column.columnDef.header, e.getContext())
27
+ }, e.id))
28
+ }, e.id)) }), /* @__PURE__ */ d(o, { children: x ? Array.from({ length: 5 }).map((e, t) => /* @__PURE__ */ d(l, {
29
+ className: "hover:bg-transparent",
30
+ children: y.map((e, t) => /* @__PURE__ */ d(c, {
31
+ className: "py-3.5",
32
+ children: /* @__PURE__ */ d(r, { className: "h-4 w-full max-w-28" })
33
+ }, t))
34
+ }, t)) : k.getRowModel().rows.length ? k.getRowModel().rows.map((e) => /* @__PURE__ */ d(l, {
35
+ tabIndex: E ? 0 : void 0,
36
+ role: E ? "link" : void 0,
37
+ onClick: E ? (t) => {
38
+ i(t.target) || E(e.original);
39
+ } : void 0,
40
+ onKeyDown: E ? (t) => {
41
+ t.key === "Enter" && (i(t.target) || E(e.original));
42
+ } : void 0,
43
+ className: E ? "cursor-pointer focus-visible:bg-muted/50 focus-visible:outline-none" : void 0,
44
+ children: e.getVisibleCells().map((e) => /* @__PURE__ */ d(c, {
45
+ className: "py-3.5",
46
+ children: g(e.column.columnDef.cell, e.getContext())
47
+ }, e.id))
48
+ }, e.id)) : /* @__PURE__ */ d(l, {
49
+ className: "hover:bg-transparent",
50
+ children: /* @__PURE__ */ d(c, {
51
+ colSpan: y.length,
52
+ className: "h-40 text-center text-muted-foreground",
53
+ children: D
54
+ })
55
+ }) })] })
56
+ }), A && /* @__PURE__ */ f("div", {
57
+ className: "flex items-center justify-between text-sm text-muted-foreground",
58
+ children: [/* @__PURE__ */ f("span", {
59
+ className: "tabular-nums",
60
+ children: [
61
+ S === 0 ? "0" : `${w + 1}–${Math.min(w + C, S)}`,
62
+ " ",
63
+ /* @__PURE__ */ d(t, { id: "of " }),
64
+ S
65
+ ]
66
+ }), /* @__PURE__ */ f("div", {
67
+ className: "flex gap-1",
68
+ children: [/* @__PURE__ */ d(n, {
69
+ variant: "ghost",
70
+ size: "icon",
71
+ "aria-label": O("Previous page"),
72
+ disabled: w <= 0 || x,
73
+ onClick: () => T(Math.max(0, w - C)),
74
+ children: /* @__PURE__ */ d(p, {
75
+ icon: m,
76
+ className: "size-4"
77
+ })
78
+ }), /* @__PURE__ */ d(n, {
79
+ variant: "ghost",
80
+ size: "icon",
81
+ "aria-label": O("Next page"),
82
+ disabled: w + C >= S || x,
83
+ onClick: () => T(w + C),
84
+ children: /* @__PURE__ */ d(p, {
85
+ icon: h,
86
+ className: "size-4"
87
+ })
88
+ })]
89
+ })]
90
+ })]
91
+ });
92
+ }
93
+ //#endregion
94
+ export { y as t };
95
+
96
+ //# sourceMappingURL=admin-data-table-qfXLOFmN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-data-table-qfXLOFmN.js","names":[],"sources":["../src/admin/components/data-table.tsx"],"sourcesContent":["import { AdminMessage, useAdminUI } from \"#orck/admin/ui-context\"\n// Server-paginated data table on @tanstack/react-table v8 + the shadcn Table\n// primitives (shadcn Data Table pattern).\nimport { HugeiconsIcon } from \"@hugeicons/react\"\nimport { ArrowLeft01Icon, ArrowRight01Icon } from \"@hugeicons/core-free-icons\"\nimport {\n flexRender,\n getCoreRowModel,\n useReactTable,\n type ColumnDef,\n} from \"@tanstack/react-table\"\nimport { Button } from \"#orck/admin/components/ui/button\"\nimport { Skeleton } from \"#orck/admin/components/ui/skeleton\"\nimport { isInteractiveTarget } from \"#orck/admin/lib/dom\"\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"#orck/admin/components/ui/table\"\n\ninterface DataTableProps<TData> {\n columns: ColumnDef<TData, unknown>[]\n data: TData[]\n loading?: boolean\n total?: number\n limit?: number\n offset?: number\n onPageChange?: (offset: number) => void\n onRowClick?: (row: TData) => void\n emptyMessage?: string\n}\n\nexport function DataTable<TData>({\n columns,\n data,\n loading,\n total,\n limit,\n offset,\n onPageChange,\n onRowClick,\n emptyMessage = \"No results.\",\n}: DataTableProps<TData>) {\n const { text: adminText } = useAdminUI()\n\n const table = useReactTable({\n data,\n columns,\n getCoreRowModel: getCoreRowModel(),\n manualPagination: true,\n })\n\n const paged =\n onPageChange &&\n limit !== undefined &&\n offset !== undefined &&\n total !== undefined\n\n return (\n <div className=\"flex flex-col gap-3\">\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n {table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={headerGroup.id} className=\"hover:bg-transparent\">\n {headerGroup.headers.map((header) => (\n <TableHead key={header.id} className=\"text-muted-foreground\">\n {header.isPlaceholder\n ? null\n : typeof header.column.columnDef.header === \"string\"\n ? adminText(header.column.columnDef.header)\n : flexRender(\n header.column.columnDef.header,\n header.getContext()\n )}\n </TableHead>\n ))}\n </TableRow>\n ))}\n </TableHeader>\n <TableBody>\n {loading ? (\n Array.from({ length: 5 }).map((_, ri) => (\n <TableRow key={ri} className=\"hover:bg-transparent\">\n {columns.map((_, ci) => (\n <TableCell key={ci} className=\"py-3.5\">\n <Skeleton className=\"h-4 w-full max-w-28\" />\n </TableCell>\n ))}\n </TableRow>\n ))\n ) : table.getRowModel().rows.length ? (\n table.getRowModel().rows.map((row) => (\n <TableRow\n key={row.id}\n // A clickable row takes Enter as well as a click, and ignores\n // both on interactive content inside its cells.\n tabIndex={onRowClick ? 0 : undefined}\n role={onRowClick ? \"link\" : undefined}\n onClick={\n onRowClick\n ? (event) => {\n if (isInteractiveTarget(event.target)) return\n onRowClick(row.original)\n }\n : undefined\n }\n onKeyDown={\n onRowClick\n ? (event) => {\n if (event.key !== \"Enter\") return\n if (isInteractiveTarget(event.target)) return\n onRowClick(row.original)\n }\n : undefined\n }\n className={\n onRowClick\n ? \"cursor-pointer focus-visible:bg-muted/50 focus-visible:outline-none\"\n : undefined\n }\n >\n {row.getVisibleCells().map((cell) => (\n <TableCell key={cell.id} className=\"py-3.5\">\n {flexRender(\n cell.column.columnDef.cell,\n cell.getContext()\n )}\n </TableCell>\n ))}\n </TableRow>\n ))\n ) : (\n <TableRow className=\"hover:bg-transparent\">\n <TableCell\n colSpan={columns.length}\n className=\"h-40 text-center text-muted-foreground\"\n >\n {emptyMessage}\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </div>\n {paged && (\n <div className=\"flex items-center justify-between text-sm text-muted-foreground\">\n <span className=\"tabular-nums\">\n {total === 0\n ? \"0\"\n : `${offset + 1}–${Math.min(offset + limit, total)}`}{\" \"}\n <AdminMessage id={\"of \"} />\n {total}\n </span>\n <div className=\"flex gap-1\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={adminText(\"Previous page\")}\n disabled={offset <= 0 || loading}\n onClick={() => onPageChange(Math.max(0, offset - limit))}\n >\n <HugeiconsIcon icon={ArrowLeft01Icon} className=\"size-4\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={adminText(\"Next page\")}\n disabled={offset + limit >= total || loading}\n onClick={() => onPageChange(offset + limit)}\n >\n <HugeiconsIcon icon={ArrowRight01Icon} className=\"size-4\" />\n </Button>\n </div>\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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"}