@openrails/billing-ui 0.249.0 → 0.250.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 (148) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +13 -13
  3. package/dist/account/account-billing.d.ts +2 -2
  4. package/dist/account/cancel-dialog.d.ts +2 -2
  5. package/dist/account/change-card-dialog.d.ts +2 -2
  6. package/dist/account/payment-history.d.ts +2 -2
  7. package/dist/account/payment-methods-panel.d.ts +2 -2
  8. package/dist/account/section.d.ts +2 -2
  9. package/dist/account/subscriptions-panel.d.ts +2 -2
  10. package/dist/admin/context.d.ts +2 -2
  11. package/dist/admin/lib/queries.d.ts +1 -1
  12. package/dist/admin/messages.d.ts +3 -3
  13. package/dist/admin/pages/catalog/psp-links.d.ts +1 -1
  14. package/dist/admin/theme.d.ts +2 -2
  15. package/dist/admin/types.d.ts +2 -2
  16. package/dist/{admin-attempts-lAoW-Urd.js → admin-attempts-DxRHTODq.js} +8 -8
  17. package/dist/{admin-attempts-lAoW-Urd.js.map → admin-attempts-DxRHTODq.js.map} +1 -1
  18. package/dist/{admin-auth-DQGHtt0j.js → admin-auth-CiuZ-Hlm.js} +2 -2
  19. package/dist/{admin-auth-DQGHtt0j.js.map → admin-auth-CiuZ-Hlm.js.map} +1 -1
  20. package/dist/{admin-badge-DR5-1qd8.js → admin-badge-VCGbnfiN.js} +2 -2
  21. package/dist/{admin-badge-DR5-1qd8.js.map → admin-badge-VCGbnfiN.js.map} +1 -1
  22. package/dist/{admin-button-CTL3DgOt.js → admin-button-Oip73Plq.js} +2 -2
  23. package/dist/{admin-button-CTL3DgOt.js.map → admin-button-Oip73Plq.js.map} +1 -1
  24. package/dist/{admin-capabilities-BuigAgtm.js → admin-capabilities-BGv7oG8v.js} +2 -2
  25. package/dist/{admin-capabilities-BuigAgtm.js.map → admin-capabilities-BGv7oG8v.js.map} +1 -1
  26. package/dist/{admin-card-bzAks3-3.js → admin-card-DGbi6ZHZ.js} +2 -2
  27. package/dist/{admin-card-bzAks3-3.js.map → admin-card-DGbi6ZHZ.js.map} +1 -1
  28. package/dist/{admin-catalog-CK6CM9I2.js → admin-catalog-3pPEAuDY.js} +19 -19
  29. package/dist/{admin-catalog-CK6CM9I2.js.map → admin-catalog-3pPEAuDY.js.map} +1 -1
  30. package/dist/{admin-customers-D8u5gq3M.js → admin-customers-cnKMEV4q.js} +6 -6
  31. package/dist/{admin-customers-D8u5gq3M.js.map → admin-customers-cnKMEV4q.js.map} +1 -1
  32. package/dist/{admin-dashboard-DqqFjOzq.js → admin-dashboard-DfSsUyo-.js} +14 -14
  33. package/dist/{admin-dashboard-DqqFjOzq.js.map → admin-dashboard-DfSsUyo-.js.map} +1 -1
  34. package/dist/{admin-data-table-qfXLOFmN.js → admin-data-table-CgeWv__T.js} +6 -6
  35. package/dist/{admin-data-table-qfXLOFmN.js.map → admin-data-table-CgeWv__T.js.map} +1 -1
  36. package/dist/{admin-detail-X82kR2f7.js → admin-detail-BATuJjVs.js} +12 -12
  37. package/dist/{admin-detail-X82kR2f7.js.map → admin-detail-BATuJjVs.js.map} +1 -1
  38. package/dist/{admin-detail-DUAJQg8G.js → admin-detail-Cq59e14B.js} +15 -15
  39. package/dist/{admin-detail-DUAJQg8G.js.map → admin-detail-Cq59e14B.js.map} +1 -1
  40. package/dist/{admin-detail-BHe51ma9.js → admin-detail-DG-YaHhS.js} +11 -11
  41. package/dist/{admin-detail-BHe51ma9.js.map → admin-detail-DG-YaHhS.js.map} +1 -1
  42. package/dist/{admin-detail-CGOyqs7s.js → admin-detail-Dc7rU4mM.js} +15 -15
  43. package/dist/{admin-detail-CGOyqs7s.js.map → admin-detail-Dc7rU4mM.js.map} +1 -1
  44. package/dist/{admin-dialog-BjuKI5Hp.js → admin-dialog-Ffx95AIT.js} +3 -3
  45. package/dist/{admin-dialog-BjuKI5Hp.js.map → admin-dialog-Ffx95AIT.js.map} +1 -1
  46. package/dist/{admin-dom-uMmZzRvU.js → admin-dom-lnNSQ7ke.js} +4 -4
  47. package/dist/{admin-dom-uMmZzRvU.js.map → admin-dom-lnNSQ7ke.js.map} +1 -1
  48. package/dist/{admin-dropdown-menu-BW1D_h6s.js → admin-dropdown-menu-CCiW4-oz.js} +2 -2
  49. package/dist/{admin-dropdown-menu-BW1D_h6s.js.map → admin-dropdown-menu-CCiW4-oz.js.map} +1 -1
  50. package/dist/{admin-fact-card-DO_fHosg.js → admin-fact-card-D6mZgxpN.js} +2 -2
  51. package/dist/{admin-fact-card-DO_fHosg.js.map → admin-fact-card-D6mZgxpN.js.map} +1 -1
  52. package/dist/{admin-health-B4jN2LwR.js → admin-health-CvJX9prw.js} +13 -13
  53. package/dist/{admin-health-B4jN2LwR.js.map → admin-health-CvJX9prw.js.map} +1 -1
  54. package/dist/{admin-input-CcfQfJFN.js → admin-input-DO5lvCLL.js} +2 -2
  55. package/dist/{admin-input-CcfQfJFN.js.map → admin-input-DO5lvCLL.js.map} +1 -1
  56. package/dist/{admin-label-BiEN5cjb.js → admin-label-BzJpr3lg.js} +2 -2
  57. package/dist/{admin-label-BiEN5cjb.js.map → admin-label-BzJpr3lg.js.map} +1 -1
  58. package/dist/{admin-linked-table-row-BsaUruCN.js → admin-linked-table-row-5vFb6rXY.js} +4 -4
  59. package/dist/{admin-linked-table-row-BsaUruCN.js.map → admin-linked-table-row-5vFb6rXY.js.map} +1 -1
  60. package/dist/{admin-metering-COFcSPQy.js → admin-metering-BksTFRRP.js} +14 -14
  61. package/dist/{admin-metering-COFcSPQy.js.map → admin-metering-BksTFRRP.js.map} +1 -1
  62. package/dist/{admin-ops-B3mcr76u.js → admin-ops-Ch2-BmUp.js} +11 -11
  63. package/dist/{admin-ops-B3mcr76u.js.map → admin-ops-Ch2-BmUp.js.map} +1 -1
  64. package/dist/{admin-orders-dxC99Idl.js → admin-orders-8XzHf7b-.js} +8 -8
  65. package/dist/{admin-orders-dxC99Idl.js.map → admin-orders-8XzHf7b-.js.map} +1 -1
  66. package/dist/{admin-payments-Bs7HSfP0.js → admin-payments-pNLNIU0X.js} +9 -9
  67. package/dist/{admin-payments-Bs7HSfP0.js.map → admin-payments-pNLNIU0X.js.map} +1 -1
  68. package/dist/{admin-price-detail-DRquxpA9.js → admin-price-detail-BeGDPxKX.js} +11 -11
  69. package/dist/admin-price-detail-BeGDPxKX.js.map +1 -0
  70. package/dist/{admin-price-wizard-CI7K-2Nz.js → admin-price-wizard-ferZSTfO.js} +7 -7
  71. package/dist/{admin-price-wizard-CI7K-2Nz.js.map → admin-price-wizard-ferZSTfO.js.map} +1 -1
  72. package/dist/{admin-range-picker-Dr2M1EZr.js → admin-range-picker-DaIvC1oC.js} +5 -5
  73. package/dist/{admin-range-picker-Dr2M1EZr.js.map → admin-range-picker-DaIvC1oC.js.map} +1 -1
  74. package/dist/{admin-reports-CywKLiSA.js → admin-reports-CA-cnNur.js} +8 -8
  75. package/dist/{admin-reports-CywKLiSA.js.map → admin-reports-CA-cnNur.js.map} +1 -1
  76. package/dist/{admin-select-DBgprVW6.js → admin-select-CUXvh6j1.js} +2 -2
  77. package/dist/{admin-select-DBgprVW6.js.map → admin-select-CUXvh6j1.js.map} +1 -1
  78. package/dist/{admin-settings-Bmoy95uO.js → admin-settings-C6eWFkDn.js} +16 -16
  79. package/dist/{admin-settings-Bmoy95uO.js.map → admin-settings-C6eWFkDn.js.map} +1 -1
  80. package/dist/{admin-skeleton-DsM7crYi.js → admin-skeleton-DEY3Q8Dg.js} +2 -2
  81. package/dist/{admin-skeleton-DsM7crYi.js.map → admin-skeleton-DEY3Q8Dg.js.map} +1 -1
  82. package/dist/{admin-status-badge-BFPgo71d.js → admin-status-badge-CwqIU8tK.js} +2 -2
  83. package/dist/{admin-status-badge-BFPgo71d.js.map → admin-status-badge-CwqIU8tK.js.map} +1 -1
  84. package/dist/{admin-subscriptions-CLYc_HEV.js → admin-subscriptions-lJG_EzMe.js} +9 -9
  85. package/dist/{admin-subscriptions-CLYc_HEV.js.map → admin-subscriptions-lJG_EzMe.js.map} +1 -1
  86. package/dist/{admin-switch-BJUxLCsa.js → admin-switch-S4g_ppaU.js} +2 -2
  87. package/dist/{admin-switch-BJUxLCsa.js.map → admin-switch-S4g_ppaU.js.map} +1 -1
  88. package/dist/{admin-table-CH9TrXJf.js → admin-table-CWhiCb9d.js} +2 -2
  89. package/dist/{admin-table-CH9TrXJf.js.map → admin-table-CWhiCb9d.js.map} +1 -1
  90. package/dist/{admin-tabs-528FJMjt.js → admin-tabs-Cjm5a6UJ.js} +2 -2
  91. package/dist/{admin-tabs-528FJMjt.js.map → admin-tabs-Cjm5a6UJ.js.map} +1 -1
  92. package/dist/{admin-textarea-DEz38RaI.js → admin-textarea-DK1Fq31x.js} +2 -2
  93. package/dist/{admin-textarea-DEz38RaI.js.map → admin-textarea-DK1Fq31x.js.map} +1 -1
  94. package/dist/{admin-typed-confirm-dialog-DU7m9-O7.js → admin-typed-confirm-dialog-BloZvFPA.js} +6 -6
  95. package/dist/{admin-typed-confirm-dialog-DU7m9-O7.js.map → admin-typed-confirm-dialog-BloZvFPA.js.map} +1 -1
  96. package/dist/{admin-utils-BK4Vh1o_.js → admin-utils-DnVY5fYd.js} +3 -3
  97. package/dist/admin-utils-DnVY5fYd.js.map +1 -0
  98. package/dist/{admin-widget-viz-Da48c-Zf.js → admin-widget-viz-RCkxUspL.js} +8 -8
  99. package/dist/{admin-widget-viz-Da48c-Zf.js.map → admin-widget-viz-RCkxUspL.js.map} +1 -1
  100. package/dist/admin.js +31 -31
  101. package/dist/admin.js.map +1 -1
  102. package/dist/appearance.d.ts +7 -7
  103. package/dist/buy-button.d.ts +3 -3
  104. package/dist/cart.d.ts +4 -4
  105. package/dist/client/client.d.ts +4 -4
  106. package/dist/client/generated/error-codes.d.ts +1 -1
  107. package/dist/client/generated/routes.d.ts +4 -4
  108. package/dist/client/generated/wire.d.ts +37 -40
  109. package/dist/client/types.d.ts +12 -17
  110. package/dist/{client-rFrCMrkn.js → client-HWr09qpX.js} +11 -13
  111. package/dist/client-HWr09qpX.js.map +1 -0
  112. package/dist/client.js +1 -1
  113. package/dist/embedded-payment.d.ts +25 -0
  114. package/dist/{hooks-B1TffejB.js → hooks-D9MR20BY.js} +2 -2
  115. package/dist/hooks-D9MR20BY.js.map +1 -0
  116. package/dist/index.d.ts +4 -4
  117. package/dist/index.js +69 -69
  118. package/dist/index.js.map +1 -1
  119. package/dist/locales/de.js +1 -1
  120. package/dist/locales/de.js.map +1 -1
  121. package/dist/locales/en.d.ts +1 -1
  122. package/dist/locales/en.js +1 -1
  123. package/dist/locales/en.js.map +1 -1
  124. package/dist/locales/es.js +1 -1
  125. package/dist/locales/es.js.map +1 -1
  126. package/dist/locales/ja.js +1 -1
  127. package/dist/locales/ja.js.map +1 -1
  128. package/dist/locales/ko.js +1 -1
  129. package/dist/locales/ko.js.map +1 -1
  130. package/dist/locales/zh.js +1 -1
  131. package/dist/locales/zh.js.map +1 -1
  132. package/dist/modal.d.ts +8 -8
  133. package/dist/{order-checkout.d.ts → order-payment.d.ts} +4 -4
  134. package/dist/product-list.d.ts +3 -3
  135. package/dist/psp.d.ts +2 -2
  136. package/dist/react/provider.d.ts +3 -3
  137. package/dist/react.js +1 -1
  138. package/dist/save-payment-method.d.ts +2 -2
  139. package/dist/scope-context.d.ts +4 -4
  140. package/dist/scope.d.ts +2 -2
  141. package/dist/tokenized-card-form.d.ts +2 -2
  142. package/docs/payment-form-contract.md +3 -3
  143. package/package.json +2 -2
  144. package/dist/admin-price-detail-DRquxpA9.js.map +0 -1
  145. package/dist/admin-utils-BK4Vh1o_.js.map +0 -1
  146. package/dist/client-rFrCMrkn.js.map +0 -1
  147. package/dist/embedded-checkout.d.ts +0 -25
  148. package/dist/hooks-B1TffejB.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"admin-customers-D8u5gq3M.js","names":[],"sources":["../src/admin/pages/customers/index.tsx"],"sourcesContent":["import { AdminMessage, useAdminUI } from \"#orck/admin/ui-context\"\nimport { useAdminServices } from \"#orck/admin/context\"\nimport { HugeiconsIcon } from \"@hugeicons/react\"\nimport { Download01Icon, Search01Icon } from \"@hugeicons/core-free-icons\"\nimport * as React from \"react\"\nimport { useNavigate, useSearchParams } from \"#orck/admin/router\"\nimport { useMutation, useQuery } from \"@tanstack/react-query\"\nimport type { ColumnDef } from \"@tanstack/react-table\"\n\nimport { CursorPager } from \"#orck/admin/components/cursor-pager\"\nimport { useCursorPages } from \"#orck/admin/lib/cursor-pages\"\nimport { DataTable } from \"#orck/admin/components/data-table\"\nimport { Button } from \"#orck/admin/components/ui/button\"\nimport { Input } from \"#orck/admin/components/ui/input\"\nimport type { Customer } from \"#orck/client/generated/wire\"\nimport { downloadCsv } from \"#orck/admin/lib/csv\"\nimport { formatDate, formatNativeAmount, shortId } from \"#orck/admin/lib/format\"\n\nconst columns: ColumnDef<Customer, unknown>[] = [\n {\n header: \"Contact\",\n cell: ({ row }) => {\n const contact = row.original.contact\n if (!contact) return <span className=\"text-muted-foreground\">—</span>\n return (\n <div className=\"flex min-w-0 flex-col\">\n <span className=\"truncate font-medium\">\n {contact.name || contact.email || contact.username}\n </span>\n {contact.name && contact.email && (\n <span className=\"truncate text-xs text-muted-foreground\">\n {contact.email}\n </span>\n )}\n </div>\n )\n },\n },\n {\n header: \"Username\",\n cell: ({ row }) =>\n row.original.contact?.username ? (\n <span>{row.original.contact.username}</span>\n ) : (\n <span className=\"text-muted-foreground\">—</span>\n ),\n },\n {\n header: \"Balance\",\n cell: ({ row }) => {\n const balances = row.original.balances\n if (!balances.length)\n return <span className=\"text-muted-foreground\">—</span>\n return (\n <div className=\"flex flex-col gap-0.5 tabular-nums\">\n {balances.map((b) => (\n <span key={b.currency}>\n {formatNativeAmount(b.balance_amount, b.currency)}\n {BigInt(b.owed_amount) > 0n && (\n <span className=\"text-muted-foreground\">\n {\" \"}\n <AdminMessage id={\"· owed \"} />\n {formatNativeAmount(b.owed_amount, b.currency)}\n </span>\n )}\n </span>\n ))}\n </div>\n )\n },\n },\n {\n header: \"Customer\",\n cell: ({ row }) => (\n <span className=\"font-mono text-xs\">{shortId(row.original.id, 13)}</span>\n ),\n },\n {\n header: \"Created\",\n cell: ({ row }) => (\n <span className=\"text-muted-foreground tabular-nums\">\n {formatDate(row.original.created_at)}\n </span>\n ),\n },\n {\n header: \"Last active\",\n cell: ({ row }) => (\n <span className=\"text-muted-foreground tabular-nums\">\n {formatDate(row.original.last_seen_at)}\n </span>\n ),\n },\n]\n\nexport function CustomersPage() {\n const { toastApiError, adminMutations, adminQueries } = useAdminServices()\n\n const { text: adminText } = useAdminUI()\n\n const [params, setParams] = useSearchParams()\n const search = params.get(\"search\") ?? \"\"\n const pages = useCursorPages(search)\n const [input, setInput] = React.useState(search)\n const navigate = useNavigate()\n const exportCustomers = useMutation(adminMutations.exportCustomers())\n\n const {\n data,\n isPending: loading,\n isFetching,\n } = useQuery(adminQueries.customers(search, pages.size, pages.cursor))\n\n // Export walks every page of the current filter, not just the visible one.\n const exportCsv = async () => {\n try {\n const rows = await exportCustomers.mutateAsync(search)\n downloadCsv(\n `customers-${new Date().toISOString().slice(0, 10)}.csv`,\n [\"id\", \"email\", \"name\", \"username\", \"created_at\", \"last_seen_at\"],\n rows.map((r) => [\n r.id,\n r.contact?.email,\n r.contact?.name,\n r.contact?.username,\n r.created_at,\n r.last_seen_at,\n ])\n )\n } catch (err) {\n toastApiError(err, \"Export customers\")\n }\n }\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <h1 className=\"text-2xl font-semibold tracking-tight\">\n <AdminMessage id={\"Customers\"} />\n </h1>\n <div className=\"flex items-center gap-3\">\n <form\n className=\"relative\"\n onSubmit={(e) => {\n e.preventDefault()\n setParams(input ? { search: input } : {})\n }}\n >\n <HugeiconsIcon\n icon={Search01Icon}\n className=\"absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground\"\n />\n <Input\n className=\"w-64 pl-8\"\n placeholder={adminText(\"Search email, username, name or id…\")}\n value={input}\n onChange={(e) => setInput(e.target.value)}\n />\n </form>\n <Button\n size=\"sm\"\n onClick={exportCsv}\n disabled={\n exportCustomers.isPending || loading || !data?.data.length\n }\n >\n <HugeiconsIcon icon={Download01Icon} className=\"size-4\" />\n {exportCustomers.isPending ? \"Exporting…\" : \"Export CSV\"}\n </Button>\n </div>\n </div>\n <DataTable\n columns={columns}\n data={data?.data ?? []}\n loading={loading}\n onRowClick={(row) => navigate(`/customers/${row.id}`)}\n emptyMessage={search ? \"No customers match.\" : \"No customers yet.\"}\n />\n <CursorPager\n pages={pages}\n rows={data?.data?.length ?? 0}\n nextCursor={data?.next_cursor}\n busy={isFetching}\n />\n </div>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"admin-customers-cnKMEV4q.js","names":[],"sources":["../src/admin/pages/customers/index.tsx"],"sourcesContent":["import { AdminMessage, useAdminUI } from \"#orck/admin/ui-context\"\nimport { useAdminServices } from \"#orck/admin/context\"\nimport { HugeiconsIcon } from \"@hugeicons/react\"\nimport { Download01Icon, Search01Icon } from \"@hugeicons/core-free-icons\"\nimport * as React from \"react\"\nimport { useNavigate, useSearchParams } from \"#orck/admin/router\"\nimport { useMutation, useQuery } from \"@tanstack/react-query\"\nimport type { ColumnDef } from \"@tanstack/react-table\"\n\nimport { CursorPager } from \"#orck/admin/components/cursor-pager\"\nimport { useCursorPages } from \"#orck/admin/lib/cursor-pages\"\nimport { DataTable } from \"#orck/admin/components/data-table\"\nimport { Button } from \"#orck/admin/components/ui/button\"\nimport { Input } from \"#orck/admin/components/ui/input\"\nimport type { Customer } from \"#orck/client/generated/wire\"\nimport { downloadCsv } from \"#orck/admin/lib/csv\"\nimport { formatDate, formatNativeAmount, shortId } from \"#orck/admin/lib/format\"\n\nconst columns: ColumnDef<Customer, unknown>[] = [\n {\n header: \"Contact\",\n cell: ({ row }) => {\n const contact = row.original.contact\n if (!contact) return <span className=\"text-muted-foreground\">—</span>\n return (\n <div className=\"flex min-w-0 flex-col\">\n <span className=\"truncate font-medium\">\n {contact.name || contact.email || contact.username}\n </span>\n {contact.name && contact.email && (\n <span className=\"truncate text-xs text-muted-foreground\">\n {contact.email}\n </span>\n )}\n </div>\n )\n },\n },\n {\n header: \"Username\",\n cell: ({ row }) =>\n row.original.contact?.username ? (\n <span>{row.original.contact.username}</span>\n ) : (\n <span className=\"text-muted-foreground\">—</span>\n ),\n },\n {\n header: \"Balance\",\n cell: ({ row }) => {\n const balances = row.original.balances\n if (!balances.length)\n return <span className=\"text-muted-foreground\">—</span>\n return (\n <div className=\"flex flex-col gap-0.5 tabular-nums\">\n {balances.map((b) => (\n <span key={b.currency}>\n {formatNativeAmount(b.balance_amount, b.currency)}\n {BigInt(b.owed_amount) > 0n && (\n <span className=\"text-muted-foreground\">\n {\" \"}\n <AdminMessage id={\"· owed \"} />\n {formatNativeAmount(b.owed_amount, b.currency)}\n </span>\n )}\n </span>\n ))}\n </div>\n )\n },\n },\n {\n header: \"Customer\",\n cell: ({ row }) => (\n <span className=\"font-mono text-xs\">{shortId(row.original.id, 13)}</span>\n ),\n },\n {\n header: \"Created\",\n cell: ({ row }) => (\n <span className=\"text-muted-foreground tabular-nums\">\n {formatDate(row.original.created_at)}\n </span>\n ),\n },\n {\n header: \"Last active\",\n cell: ({ row }) => (\n <span className=\"text-muted-foreground tabular-nums\">\n {formatDate(row.original.last_seen_at)}\n </span>\n ),\n },\n]\n\nexport function CustomersPage() {\n const { toastApiError, adminMutations, adminQueries } = useAdminServices()\n\n const { text: adminText } = useAdminUI()\n\n const [params, setParams] = useSearchParams()\n const search = params.get(\"search\") ?? \"\"\n const pages = useCursorPages(search)\n const [input, setInput] = React.useState(search)\n const navigate = useNavigate()\n const exportCustomers = useMutation(adminMutations.exportCustomers())\n\n const {\n data,\n isPending: loading,\n isFetching,\n } = useQuery(adminQueries.customers(search, pages.size, pages.cursor))\n\n // Export walks every page of the current filter, not just the visible one.\n const exportCsv = async () => {\n try {\n const rows = await exportCustomers.mutateAsync(search)\n downloadCsv(\n `customers-${new Date().toISOString().slice(0, 10)}.csv`,\n [\"id\", \"email\", \"name\", \"username\", \"created_at\", \"last_seen_at\"],\n rows.map((r) => [\n r.id,\n r.contact?.email,\n r.contact?.name,\n r.contact?.username,\n r.created_at,\n r.last_seen_at,\n ])\n )\n } catch (err) {\n toastApiError(err, \"Export customers\")\n }\n }\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <h1 className=\"text-2xl font-semibold tracking-tight\">\n <AdminMessage id={\"Customers\"} />\n </h1>\n <div className=\"flex items-center gap-3\">\n <form\n className=\"relative\"\n onSubmit={(e) => {\n e.preventDefault()\n setParams(input ? { search: input } : {})\n }}\n >\n <HugeiconsIcon\n icon={Search01Icon}\n className=\"absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground\"\n />\n <Input\n className=\"w-64 pl-8\"\n placeholder={adminText(\"Search email, username, name or id…\")}\n value={input}\n onChange={(e) => setInput(e.target.value)}\n />\n </form>\n <Button\n size=\"sm\"\n onClick={exportCsv}\n disabled={\n exportCustomers.isPending || loading || !data?.data.length\n }\n >\n <HugeiconsIcon icon={Download01Icon} className=\"size-4\" />\n {exportCustomers.isPending ? \"Exporting…\" : \"Export CSV\"}\n </Button>\n </div>\n </div>\n <DataTable\n columns={columns}\n data={data?.data ?? []}\n loading={loading}\n onRowClick={(row) => navigate(`/customers/${row.id}`)}\n emptyMessage={search ? \"No customers match.\" : \"No customers yet.\"}\n />\n <CursorPager\n pages={pages}\n rows={data?.data?.length ?? 0}\n nextCursor={data?.next_cursor}\n busy={isFetching}\n />\n </div>\n )\n}\n"],"mappings":"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"}
@@ -1,18 +1,18 @@
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";
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-DnVY5fYd.js";
2
+ import { i as c, n as l, s as u, t as d } from "./admin-dropdown-menu-CCiW4-oz.js";
3
+ import { t as f } from "./admin-button-Oip73Plq.js";
4
+ import { t as p } from "./admin-input-DO5lvCLL.js";
5
+ import { t as m } from "./admin-skeleton-DEY3Q8Dg.js";
6
+ import { c as h, r as g } from "./admin-capabilities-BGv7oG8v.js";
7
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";
8
+ import { a as v, i as y, n as b, r as x, t as S } from "./admin-select-CUXvh6j1.js";
9
+ import { a as C, i as w, n as T, o as E, r as D, t as O } from "./admin-dialog-Ffx95AIT.js";
10
+ import { t as k } from "./admin-label-BzJpr3lg.js";
11
+ import { t as A } from "./admin-switch-S4g_ppaU.js";
12
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";
13
+ import { r as M, t as N } from "./admin-card-DGbi6ZHZ.js";
14
+ import { a as P, n as F, t as I } from "./admin-range-picker-DaIvC1oC.js";
15
+ import { n as L, o as ee, t as R } from "./admin-widget-viz-RCkxUspL.js";
16
16
  import * as z from "react";
17
17
  import { useMutation as B, useQuery as V, useQueryClient as H } from "@tanstack/react-query";
18
18
  import { Fragment as te, jsx as U, jsxs as W } from "react/jsx-runtime";
@@ -417,4 +417,4 @@ function fe() {
417
417
  //#endregion
418
418
  export { ue as DashboardPage };
419
419
 
420
- //# sourceMappingURL=admin-dashboard-DqqFjOzq.js.map
420
+ //# sourceMappingURL=admin-dashboard-DfSsUyo-.js.map
@@ -1 +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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"}
1
+ {"version":3,"file":"admin-dashboard-DfSsUyo-.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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"}
@@ -1,8 +1,8 @@
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";
1
+ import { E as e, w as t } from "./admin-utils-DnVY5fYd.js";
2
+ import { t as n } from "./admin-button-Oip73Plq.js";
3
+ import { t as r } from "./admin-skeleton-DEY3Q8Dg.js";
4
+ import { t as i } from "./admin-dom-lnNSQ7ke.js";
5
+ import { a, n as o, o as s, r as c, s as l, t as u } from "./admin-table-CWhiCb9d.js";
6
6
  import { jsx as d, jsxs as f } from "react/jsx-runtime";
7
7
  import { HugeiconsIcon as p } from "@hugeicons/react";
8
8
  import { ArrowLeft01Icon as m, ArrowRight01Icon as h } from "@hugeicons/core-free-icons";
@@ -93,4 +93,4 @@ function y({ columns: y, data: b, loading: x, total: S, limit: C, offset: w, onP
93
93
  //#endregion
94
94
  export { y as t };
95
95
 
96
- //# sourceMappingURL=admin-data-table-qfXLOFmN.js.map
96
+ //# sourceMappingURL=admin-data-table-CgeWv__T.js.map
@@ -1 +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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"}
1
+ {"version":3,"file":"admin-data-table-CgeWv__T.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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"}
@@ -1,17 +1,17 @@
1
- import { E as e, b as t, c as n, p as r, r as i, s as a, w as o } from "./admin-utils-BK4Vh1o_.js";
2
- import { t as s } from "./admin-button-CTL3DgOt.js";
3
- import { t as c } from "./admin-input-CcfQfJFN.js";
4
- import { a as l } from "./admin-capabilities-BuigAgtm.js";
1
+ import { E as e, b as t, c as n, p as r, r as i, s as a, w as o } from "./admin-utils-DnVY5fYd.js";
2
+ import { t as s } from "./admin-button-Oip73Plq.js";
3
+ import { t as c } from "./admin-input-DO5lvCLL.js";
4
+ import { a as l } from "./admin-capabilities-BGv7oG8v.js";
5
5
  import { c as u, i as d, l as f, o as p, s as m, u as h } from "./admin-format-DoohJ7Fg.js";
6
6
  import { t as g } from "./admin-form-field-errors-hbUMIriS.js";
7
- import { a as _, n as v, o as y, r as b, s as x, t as S } from "./admin-table-CH9TrXJf.js";
8
- import { t as C } from "./admin-status-badge-BFPgo71d.js";
9
- import { a as w, i as T, n as E, o as D, r as O, t as k } from "./admin-dialog-BjuKI5Hp.js";
10
- import { t as A } from "./admin-label-BiEN5cjb.js";
11
- import { t as j } from "./admin-switch-BJUxLCsa.js";
7
+ import { a as _, n as v, o as y, r as b, s as x, t as S } from "./admin-table-CWhiCb9d.js";
8
+ import { t as C } from "./admin-status-badge-CwqIU8tK.js";
9
+ import { a as w, i as T, n as E, o as D, r as O, t as k } from "./admin-dialog-Ffx95AIT.js";
10
+ import { t as A } from "./admin-label-BzJpr3lg.js";
11
+ import { t as j } from "./admin-switch-S4g_ppaU.js";
12
12
  import { n as M } from "./admin-dialog-width-BPrgdYlj.js";
13
- import { a as N, o as P, r as F, t as I } from "./admin-card-bzAks3-3.js";
14
- import { t as L } from "./admin-fact-card-DO_fHosg.js";
13
+ import { a as N, o as P, r as F, t as I } from "./admin-card-DGbi6ZHZ.js";
14
+ import { t as L } from "./admin-fact-card-D6mZgxpN.js";
15
15
  import * as R from "react";
16
16
  import { useMutation as z, useQuery as B, useQueryClient as V } from "@tanstack/react-query";
17
17
  import { Fragment as H, jsx as U, jsxs as W } from "react/jsx-runtime";
@@ -325,4 +325,4 @@ function X({ payment: t, disabled: n, disabledNote: i }) {
325
325
  //#endregion
326
326
  export { Y as PaymentDetailPage };
327
327
 
328
- //# sourceMappingURL=admin-detail-X82kR2f7.js.map
328
+ //# sourceMappingURL=admin-detail-BATuJjVs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"admin-detail-X82kR2f7.js","names":[],"sources":["../src/admin/pages/payments/detail.tsx"],"sourcesContent":["import { AdminMessage, useAdminUI } from \"#orck/admin/ui-context\"\nimport { useAdminServices } from \"#orck/admin/context\"\nimport { HugeiconsIcon } from \"@hugeicons/react\"\nimport { ArrowLeft01Icon, Undo02Icon } from \"@hugeicons/core-free-icons\"\nimport * as React from \"react\"\nimport { Link, useNavigate, useParams } from \"#orck/admin/router\"\n\nimport { useForm } from \"@tanstack/react-form\"\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\"\n\nimport { Fact } from \"#orck/admin/components/fact-card\"\nimport { FormFieldErrors } from \"#orck/admin/components/form-field-errors\"\nimport { StatusBadge } from \"#orck/admin/components/status-badge\"\nimport { Button } from \"#orck/admin/components/ui/button\"\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n} from \"#orck/admin/components/ui/card\"\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 Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"#orck/admin/components/ui/table\"\nimport { REFUNDABLE_RAILS } from \"#orck/admin/lib/api/endpoints\"\nimport type { Payment } from \"#orck/client/generated/wire\"\nimport { DIALOG_FORM } from \"#orck/admin/lib/dialog-width\"\nimport {\n formatCard,\n formatNativeAmount,\n formatDate,\n nativeAmountFromInput,\n nativeAmountToInput,\n shortId,\n} from \"#orck/admin/lib/format\"\n\nimport { useAdminUpdates } from \"#orck/admin/lib/capabilities\"\n\nexport function PaymentDetailPage() {\n const { text: adminText } = useAdminUI()\n\n const { adminQueries } = useAdminServices()\n\n const canUpdate = useAdminUpdates()\n const { id = \"\" } = useParams()\n const navigate = useNavigate()\n const { data: payment, isPending: loading } = useQuery(\n adminQueries.payment(id)\n )\n\n if (loading)\n return (\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"Loading…\"} />\n </p>\n )\n if (!payment)\n return (\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"Payment not found.\"} />\n </p>\n )\n\n const railRefunds = REFUNDABLE_RAILS.includes(payment.rail ?? \"\")\n const refundable =\n payment.kind === \"charge\" &&\n railRefunds &&\n (payment.status === \"succeeded\" || payment.status === \"partially_refunded\")\n const refundNote = !payment.rail\n ? `A ${payment.channel} payment is refunded where it was taken`\n : railRefunds\n ? undefined\n : `Refunds are not supported via the ${payment.rail} rail API`\n const link = \"text-xs underline-offset-2 hover:underline\"\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex items-center gap-3\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => navigate(-1)}\n aria-label={adminText(\"Back\")}\n >\n <HugeiconsIcon icon={ArrowLeft01Icon} className=\"size-4\" />\n </Button>\n <div>\n <h2 className=\"flex items-center gap-2 text-sm\">\n {payment.id} <StatusBadge status={payment.status} />\n </h2>\n <p className=\"text-xs text-muted-foreground\">\n {payment.kind} · {payment.rail ?? payment.channel}\n <AdminMessage id={\" · txn\"} /> {payment.transaction_id || \"—\"}\n </p>\n </div>\n {canUpdate && (\n <div className=\"ml-auto\">\n <RefundDialog\n payment={payment}\n disabled={!refundable}\n disabledNote={refundNote}\n />\n </div>\n )}\n </div>\n\n <div className=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n <Fact label={adminText(\"Amount\")}>\n {formatNativeAmount(payment.amount, payment.currency)}\n </Fact>\n <Fact label={adminText(\"Refunded\")}>\n {payment.amount_refunded !== \"0\"\n ? formatNativeAmount(payment.amount_refunded, payment.currency)\n : \"—\"}\n </Fact>\n <Fact label={adminText(\"Customer\")}>\n <Link className={link} to={`/customers/${payment.customer_id}`}>\n {shortId(payment.customer_id, 16)}\n </Link>\n </Fact>\n <Fact label={adminText(\"Subscription\")}>\n {payment.subscription_id ? (\n <Link\n className={link}\n to={`/subscriptions/${payment.subscription_id}`}\n >\n {shortId(payment.subscription_id, 16)}\n </Link>\n ) : (\n \"—\"\n )}\n </Fact>\n <Fact label={adminText(\"Order\")}>\n {payment.order_id ? (\n <Link className={link} to={`/orders/${payment.order_id}`}>\n {shortId(payment.order_id, 16)}\n </Link>\n ) : \"—\"}\n </Fact>\n <Fact label={adminText(\"Card\")}>{formatCard(payment.card)}</Fact>\n <Fact label={adminText(\"Channel\")}>\n {payment.channel}\n {payment.psp_id ? ` · PSP ${shortId(payment.psp_id, 13)}` : \"\"}\n </Fact>\n <Fact label={adminText(\"Created\")}>\n {formatDate(payment.created_at)}\n </Fact>\n {payment.refunded_payment_id ? (\n <Fact label={adminText(\"Reverses\")}>\n <Link\n className={link}\n to={`/payments/${payment.refunded_payment_id}`}\n >\n {shortId(payment.refunded_payment_id, 16)}\n </Link>\n </Fact>\n ) : null}\n {payment.reason ? (\n <Fact label={adminText(\"Reason\")}>{payment.reason}</Fact>\n ) : null}\n {payment.failure ? (\n <Fact label={adminText(\"Failure\")}>\n {payment.failure.message || payment.failure.reason}\n </Fact>\n ) : null}\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-sm\">\n <AdminMessage id={\"Refunds\"} />\n </CardTitle>\n </CardHeader>\n <CardContent>\n {!payment.refunds?.length ? (\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"No refunds.\"} />\n </p>\n ) : (\n <Table>\n <TableHeader>\n <TableRow className=\"hover:bg-transparent\">\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Refund\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Status\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Amount\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Created\"} />\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {payment.refunds.map((r) => (\n <TableRow key={r.id}>\n <TableCell className=\"text-xs\">\n <Link className={link} to={`/payments/${r.id}`}>\n {shortId(r.id, 16)}\n </Link>\n </TableCell>\n <TableCell>\n <StatusBadge status={r.status} />\n </TableCell>\n <TableCell>\n {formatNativeAmount(r.amount, r.currency)}\n </TableCell>\n <TableCell>{formatDate(r.created_at)}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n )}\n </CardContent>\n </Card>\n </div>\n )\n}\n\nfunction RefundDialog({\n payment,\n disabled,\n disabledNote,\n}: {\n payment: Payment\n disabled: boolean\n disabledNote?: string\n}) {\n const { toast, toastApiError, adminMutations } = useAdminServices()\n\n const { id: adminId } = useAdminUI()\n\n const [open, setOpen] = React.useState(false)\n const remaining = (\n BigInt(payment.amount) - BigInt(payment.amount_refunded)\n ).toString()\n const queryClient = useQueryClient()\n const refund = useMutation(\n adminMutations.refundPayment(\n queryClient,\n payment.id,\n payment.customer_id,\n payment.subscription_id ?? undefined\n )\n )\n const form = useForm({\n defaultValues: {\n amount: nativeAmountToInput(remaining, payment.currency),\n reason: \"\",\n revokeAccess: false,\n },\n onSubmit: async ({ value }) => {\n const amount = nativeAmountFromInput(value.amount, payment.currency)\n if (\n amount === null ||\n BigInt(amount) <= 0n ||\n BigInt(amount) > BigInt(remaining)\n )\n return\n try {\n const result = await refund.mutateAsync({\n amount,\n reason: value.reason,\n revokeAccess: value.revokeAccess,\n })\n toast.success(\n result.status === \"pending\" ? \"Refund submitted\" : \"Refunded\"\n )\n handleOpenChange(false)\n } catch (err) {\n toastApiError(err, \"Refund payment\")\n }\n },\n })\n\n const handleOpenChange = (next: boolean) => {\n setOpen(next)\n if (next) {\n form.reset({\n amount: nativeAmountToInput(remaining, payment.currency),\n reason: \"\",\n revokeAccess: false,\n })\n } else {\n form.reset()\n refund.reset()\n }\n }\n\n return (\n <>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n disabled={disabled}\n title={disabledNote}\n onClick={() => handleOpenChange(true)}\n >\n <HugeiconsIcon icon={Undo02Icon} className=\"size-4\" />\n <AdminMessage id={\" Refund\"} />\n </Button>\n <Dialog open={open} onOpenChange={handleOpenChange}>\n <DialogContent className={DIALOG_FORM}>\n <DialogHeader>\n <DialogTitle>\n <AdminMessage id={\"Refund payment\"} />\n </DialogTitle>\n <DialogDescription>\n <AdminMessage id={\"Executes the refund at the \"} />\n {payment.rail}\n <AdminMessage id={\" rail. Remaining refundable: \"} />\n {formatNativeAmount(remaining, payment.currency)}.\n </DialogDescription>\n </DialogHeader>\n <form\n onSubmit={(event) => {\n event.preventDefault()\n event.stopPropagation()\n void form.handleSubmit()\n }}\n className=\"grid gap-4\"\n >\n <div className=\"grid gap-3\">\n <form.Field\n name=\"amount\"\n validators={{\n onChange: ({ value }) => {\n const amount = nativeAmountFromInput(\n value,\n payment.currency\n )\n if (amount === null || BigInt(amount) <= 0n) {\n return \"Enter an amount greater than zero\"\n }\n return BigInt(amount) > BigInt(remaining)\n ? \"Amount exceeds the remaining refundable balance\"\n : undefined\n },\n }}\n >\n {(field) => (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={adminId(\"refund-amount\")}>\n <AdminMessage id={\"Amount (\"} />\n {payment.currency})\n </Label>\n <Input\n id={adminId(\"refund-amount\")}\n type=\"number\"\n step=\"any\"\n min=\"0\"\n max={nativeAmountToInput(remaining, payment.currency)}\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) =>\n field.handleChange(event.target.value)\n }\n aria-invalid={field.state.meta.errors.length > 0}\n />\n <FormFieldErrors errors={field.state.meta.errors} />\n </div>\n )}\n </form.Field>\n <form.Field name=\"reason\">\n {(field) => (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={adminId(\"refund-reason\")}>\n <AdminMessage id={\"Reason (optional)\"} />\n </Label>\n <Input\n id={adminId(\"refund-reason\")}\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) =>\n field.handleChange(event.target.value)\n }\n />\n </div>\n )}\n </form.Field>\n <form.Field name=\"revokeAccess\">\n {(field) => (\n <div className=\"flex items-center gap-2\">\n <Switch\n id={adminId(\"refund-revoke\")}\n checked={field.state.value}\n onCheckedChange={field.handleChange}\n />\n <Label htmlFor={adminId(\"refund-revoke\")}>\n <AdminMessage id={\"Also revoke granted access\"} />\n </Label>\n </div>\n )}\n </form.Field>\n </div>\n <DialogFooter>\n <form.Subscribe\n selector={(state) =>\n [\n state.values.amount,\n state.canSubmit,\n state.isSubmitting,\n ] as const\n }\n >\n {([amountInput, canSubmit, isSubmitting]) => (\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => handleOpenChange(false)}\n disabled={isSubmitting}\n >\n <AdminMessage id={\"Close\"} />\n </Button>\n <Button\n type=\"submit\"\n variant=\"destructive\"\n disabled={\n !nativeAmountFromInput(amountInput, payment.currency) ||\n !canSubmit ||\n isSubmitting\n }\n >\n {isSubmitting ? \"Refunding…\" : \"Refund\"}\n </Button>\n </>\n )}\n </form.Subscribe>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n </>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"admin-detail-BATuJjVs.js","names":[],"sources":["../src/admin/pages/payments/detail.tsx"],"sourcesContent":["import { AdminMessage, useAdminUI } from \"#orck/admin/ui-context\"\nimport { useAdminServices } from \"#orck/admin/context\"\nimport { HugeiconsIcon } from \"@hugeicons/react\"\nimport { ArrowLeft01Icon, Undo02Icon } from \"@hugeicons/core-free-icons\"\nimport * as React from \"react\"\nimport { Link, useNavigate, useParams } from \"#orck/admin/router\"\n\nimport { useForm } from \"@tanstack/react-form\"\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\"\n\nimport { Fact } from \"#orck/admin/components/fact-card\"\nimport { FormFieldErrors } from \"#orck/admin/components/form-field-errors\"\nimport { StatusBadge } from \"#orck/admin/components/status-badge\"\nimport { Button } from \"#orck/admin/components/ui/button\"\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n} from \"#orck/admin/components/ui/card\"\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 Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"#orck/admin/components/ui/table\"\nimport { REFUNDABLE_RAILS } from \"#orck/admin/lib/api/endpoints\"\nimport type { Payment } from \"#orck/client/generated/wire\"\nimport { DIALOG_FORM } from \"#orck/admin/lib/dialog-width\"\nimport {\n formatCard,\n formatNativeAmount,\n formatDate,\n nativeAmountFromInput,\n nativeAmountToInput,\n shortId,\n} from \"#orck/admin/lib/format\"\n\nimport { useAdminUpdates } from \"#orck/admin/lib/capabilities\"\n\nexport function PaymentDetailPage() {\n const { text: adminText } = useAdminUI()\n\n const { adminQueries } = useAdminServices()\n\n const canUpdate = useAdminUpdates()\n const { id = \"\" } = useParams()\n const navigate = useNavigate()\n const { data: payment, isPending: loading } = useQuery(\n adminQueries.payment(id)\n )\n\n if (loading)\n return (\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"Loading…\"} />\n </p>\n )\n if (!payment)\n return (\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"Payment not found.\"} />\n </p>\n )\n\n const railRefunds = REFUNDABLE_RAILS.includes(payment.rail ?? \"\")\n const refundable =\n payment.kind === \"charge\" &&\n railRefunds &&\n (payment.status === \"succeeded\" || payment.status === \"partially_refunded\")\n const refundNote = !payment.rail\n ? `A ${payment.channel} payment is refunded where it was taken`\n : railRefunds\n ? undefined\n : `Refunds are not supported via the ${payment.rail} rail API`\n const link = \"text-xs underline-offset-2 hover:underline\"\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex items-center gap-3\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => navigate(-1)}\n aria-label={adminText(\"Back\")}\n >\n <HugeiconsIcon icon={ArrowLeft01Icon} className=\"size-4\" />\n </Button>\n <div>\n <h2 className=\"flex items-center gap-2 text-sm\">\n {payment.id} <StatusBadge status={payment.status} />\n </h2>\n <p className=\"text-xs text-muted-foreground\">\n {payment.kind} · {payment.rail ?? payment.channel}\n <AdminMessage id={\" · txn\"} /> {payment.transaction_id || \"—\"}\n </p>\n </div>\n {canUpdate && (\n <div className=\"ml-auto\">\n <RefundDialog\n payment={payment}\n disabled={!refundable}\n disabledNote={refundNote}\n />\n </div>\n )}\n </div>\n\n <div className=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n <Fact label={adminText(\"Amount\")}>\n {formatNativeAmount(payment.amount, payment.currency)}\n </Fact>\n <Fact label={adminText(\"Refunded\")}>\n {payment.amount_refunded !== \"0\"\n ? formatNativeAmount(payment.amount_refunded, payment.currency)\n : \"—\"}\n </Fact>\n <Fact label={adminText(\"Customer\")}>\n <Link className={link} to={`/customers/${payment.customer_id}`}>\n {shortId(payment.customer_id, 16)}\n </Link>\n </Fact>\n <Fact label={adminText(\"Subscription\")}>\n {payment.subscription_id ? (\n <Link\n className={link}\n to={`/subscriptions/${payment.subscription_id}`}\n >\n {shortId(payment.subscription_id, 16)}\n </Link>\n ) : (\n \"—\"\n )}\n </Fact>\n <Fact label={adminText(\"Order\")}>\n {payment.order_id ? (\n <Link className={link} to={`/orders/${payment.order_id}`}>\n {shortId(payment.order_id, 16)}\n </Link>\n ) : \"—\"}\n </Fact>\n <Fact label={adminText(\"Card\")}>{formatCard(payment.card)}</Fact>\n <Fact label={adminText(\"Channel\")}>\n {payment.channel}\n {payment.psp_id ? ` · PSP ${shortId(payment.psp_id, 13)}` : \"\"}\n </Fact>\n <Fact label={adminText(\"Created\")}>\n {formatDate(payment.created_at)}\n </Fact>\n {payment.refunded_payment_id ? (\n <Fact label={adminText(\"Reverses\")}>\n <Link\n className={link}\n to={`/payments/${payment.refunded_payment_id}`}\n >\n {shortId(payment.refunded_payment_id, 16)}\n </Link>\n </Fact>\n ) : null}\n {payment.reason ? (\n <Fact label={adminText(\"Reason\")}>{payment.reason}</Fact>\n ) : null}\n {payment.failure ? (\n <Fact label={adminText(\"Failure\")}>\n {payment.failure.message || payment.failure.reason}\n </Fact>\n ) : null}\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-sm\">\n <AdminMessage id={\"Refunds\"} />\n </CardTitle>\n </CardHeader>\n <CardContent>\n {!payment.refunds?.length ? (\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"No refunds.\"} />\n </p>\n ) : (\n <Table>\n <TableHeader>\n <TableRow className=\"hover:bg-transparent\">\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Refund\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Status\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Amount\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Created\"} />\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {payment.refunds.map((r) => (\n <TableRow key={r.id}>\n <TableCell className=\"text-xs\">\n <Link className={link} to={`/payments/${r.id}`}>\n {shortId(r.id, 16)}\n </Link>\n </TableCell>\n <TableCell>\n <StatusBadge status={r.status} />\n </TableCell>\n <TableCell>\n {formatNativeAmount(r.amount, r.currency)}\n </TableCell>\n <TableCell>{formatDate(r.created_at)}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n )}\n </CardContent>\n </Card>\n </div>\n )\n}\n\nfunction RefundDialog({\n payment,\n disabled,\n disabledNote,\n}: {\n payment: Payment\n disabled: boolean\n disabledNote?: string\n}) {\n const { toast, toastApiError, adminMutations } = useAdminServices()\n\n const { id: adminId } = useAdminUI()\n\n const [open, setOpen] = React.useState(false)\n const remaining = (\n BigInt(payment.amount) - BigInt(payment.amount_refunded)\n ).toString()\n const queryClient = useQueryClient()\n const refund = useMutation(\n adminMutations.refundPayment(\n queryClient,\n payment.id,\n payment.customer_id,\n payment.subscription_id ?? undefined\n )\n )\n const form = useForm({\n defaultValues: {\n amount: nativeAmountToInput(remaining, payment.currency),\n reason: \"\",\n revokeAccess: false,\n },\n onSubmit: async ({ value }) => {\n const amount = nativeAmountFromInput(value.amount, payment.currency)\n if (\n amount === null ||\n BigInt(amount) <= 0n ||\n BigInt(amount) > BigInt(remaining)\n )\n return\n try {\n const result = await refund.mutateAsync({\n amount,\n reason: value.reason,\n revokeAccess: value.revokeAccess,\n })\n toast.success(\n result.status === \"pending\" ? \"Refund submitted\" : \"Refunded\"\n )\n handleOpenChange(false)\n } catch (err) {\n toastApiError(err, \"Refund payment\")\n }\n },\n })\n\n const handleOpenChange = (next: boolean) => {\n setOpen(next)\n if (next) {\n form.reset({\n amount: nativeAmountToInput(remaining, payment.currency),\n reason: \"\",\n revokeAccess: false,\n })\n } else {\n form.reset()\n refund.reset()\n }\n }\n\n return (\n <>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n disabled={disabled}\n title={disabledNote}\n onClick={() => handleOpenChange(true)}\n >\n <HugeiconsIcon icon={Undo02Icon} className=\"size-4\" />\n <AdminMessage id={\" Refund\"} />\n </Button>\n <Dialog open={open} onOpenChange={handleOpenChange}>\n <DialogContent className={DIALOG_FORM}>\n <DialogHeader>\n <DialogTitle>\n <AdminMessage id={\"Refund payment\"} />\n </DialogTitle>\n <DialogDescription>\n <AdminMessage id={\"Executes the refund at the \"} />\n {payment.rail}\n <AdminMessage id={\" rail. Remaining refundable: \"} />\n {formatNativeAmount(remaining, payment.currency)}.\n </DialogDescription>\n </DialogHeader>\n <form\n onSubmit={(event) => {\n event.preventDefault()\n event.stopPropagation()\n void form.handleSubmit()\n }}\n className=\"grid gap-4\"\n >\n <div className=\"grid gap-3\">\n <form.Field\n name=\"amount\"\n validators={{\n onChange: ({ value }) => {\n const amount = nativeAmountFromInput(\n value,\n payment.currency\n )\n if (amount === null || BigInt(amount) <= 0n) {\n return \"Enter an amount greater than zero\"\n }\n return BigInt(amount) > BigInt(remaining)\n ? \"Amount exceeds the remaining refundable balance\"\n : undefined\n },\n }}\n >\n {(field) => (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={adminId(\"refund-amount\")}>\n <AdminMessage id={\"Amount (\"} />\n {payment.currency})\n </Label>\n <Input\n id={adminId(\"refund-amount\")}\n type=\"number\"\n step=\"any\"\n min=\"0\"\n max={nativeAmountToInput(remaining, payment.currency)}\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) =>\n field.handleChange(event.target.value)\n }\n aria-invalid={field.state.meta.errors.length > 0}\n />\n <FormFieldErrors errors={field.state.meta.errors} />\n </div>\n )}\n </form.Field>\n <form.Field name=\"reason\">\n {(field) => (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={adminId(\"refund-reason\")}>\n <AdminMessage id={\"Reason (optional)\"} />\n </Label>\n <Input\n id={adminId(\"refund-reason\")}\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) =>\n field.handleChange(event.target.value)\n }\n />\n </div>\n )}\n </form.Field>\n <form.Field name=\"revokeAccess\">\n {(field) => (\n <div className=\"flex items-center gap-2\">\n <Switch\n id={adminId(\"refund-revoke\")}\n checked={field.state.value}\n onCheckedChange={field.handleChange}\n />\n <Label htmlFor={adminId(\"refund-revoke\")}>\n <AdminMessage id={\"Also revoke granted access\"} />\n </Label>\n </div>\n )}\n </form.Field>\n </div>\n <DialogFooter>\n <form.Subscribe\n selector={(state) =>\n [\n state.values.amount,\n state.canSubmit,\n state.isSubmitting,\n ] as const\n }\n >\n {([amountInput, canSubmit, isSubmitting]) => (\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => handleOpenChange(false)}\n disabled={isSubmitting}\n >\n <AdminMessage id={\"Close\"} />\n </Button>\n <Button\n type=\"submit\"\n variant=\"destructive\"\n disabled={\n !nativeAmountFromInput(amountInput, payment.currency) ||\n !canSubmit ||\n isSubmitting\n }\n >\n {isSubmitting ? \"Refunding…\" : \"Refund\"}\n </Button>\n </>\n )}\n </form.Subscribe>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n </>\n )\n}\n"],"mappings":"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"}
@@ -1,22 +1,22 @@
1
- import { E as e, S as t, c as n, p as r, r as i, s as a, w as o } from "./admin-utils-BK4Vh1o_.js";
2
- import { t as s } from "./admin-button-CTL3DgOt.js";
3
- import { t as c } from "./admin-input-CcfQfJFN.js";
4
- import { a as l } from "./admin-capabilities-BuigAgtm.js";
1
+ import { E as e, S as t, c as n, p as r, r as i, s as a, w as o } from "./admin-utils-DnVY5fYd.js";
2
+ import { t as s } from "./admin-button-Oip73Plq.js";
3
+ import { t as c } from "./admin-input-DO5lvCLL.js";
4
+ import { a as l } from "./admin-capabilities-BGv7oG8v.js";
5
5
  import { i as u, o as d, s as f, u as p } from "./admin-format-DoohJ7Fg.js";
6
6
  import { t as m } from "./admin-form-field-errors-hbUMIriS.js";
7
- import { a as h, i as g, n as ee, r as _, t as v } from "./admin-select-DBgprVW6.js";
8
- import { a as y, n as b, o as te, r as x, s as S, t as C } from "./admin-table-CH9TrXJf.js";
9
- import { t as w } from "./admin-badge-DR5-1qd8.js";
10
- import { t as T } from "./admin-status-badge-BFPgo71d.js";
11
- import { a as E, i as ne, n as re, o as ie, r as ae, s as oe, t as se } from "./admin-dialog-BjuKI5Hp.js";
12
- import { t as D } from "./admin-label-BiEN5cjb.js";
13
- import { t as O } from "./admin-switch-BJUxLCsa.js";
7
+ import { a as h, i as g, n as ee, r as _, t as v } from "./admin-select-CUXvh6j1.js";
8
+ import { a as y, n as b, o as te, r as x, s as S, t as C } from "./admin-table-CWhiCb9d.js";
9
+ import { t as w } from "./admin-badge-VCGbnfiN.js";
10
+ import { t as T } from "./admin-status-badge-CwqIU8tK.js";
11
+ import { a as E, i as ne, n as re, o as ie, r as ae, s as oe, t as se } from "./admin-dialog-Ffx95AIT.js";
12
+ import { t as D } from "./admin-label-BzJpr3lg.js";
13
+ import { t as O } from "./admin-switch-S4g_ppaU.js";
14
14
  import { n as ce, r as le } from "./admin-dialog-width-BPrgdYlj.js";
15
15
  import { n as k } from "./admin-price-format-DM1qORWI.js";
16
- import { a as ue, o as de, r as fe, t as pe } from "./admin-card-bzAks3-3.js";
17
- import { t as A } from "./admin-fact-card-DO_fHosg.js";
16
+ import { a as ue, o as de, r as fe, t as pe } from "./admin-card-DGbi6ZHZ.js";
17
+ import { t as A } from "./admin-fact-card-D6mZgxpN.js";
18
18
  import { t as j } from "./admin-dunning-B2zgtrOo.js";
19
- import { t as M } from "./admin-typed-confirm-dialog-DU7m9-O7.js";
19
+ import { t as M } from "./admin-typed-confirm-dialog-BloZvFPA.js";
20
20
  import * as N from "react";
21
21
  import { useMutation as P, useQuery as F, useQueryClient as I } from "@tanstack/react-query";
22
22
  import { Fragment as me, jsx as L, jsxs as R } from "react/jsx-runtime";
@@ -707,4 +707,4 @@ function xe({ change: e }) {
707
707
  //#endregion
708
708
  export { U as SubscriptionDetailPage };
709
709
 
710
- //# sourceMappingURL=admin-detail-DUAJQg8G.js.map
710
+ //# sourceMappingURL=admin-detail-Cq59e14B.js.map