@hot-updater/console 1.0.0-rc.0 → 1.0.0-rc.2

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 (203) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/HashValueDisplay-CB0Eskit.js +1 -0
  3. package/.output/public/assets/InsightsPageHeader-Car5LNiZ.js +1 -0
  4. package/.output/public/assets/api-keys-BzKocqRy.js +1 -0
  5. package/.output/public/assets/api-keys-api-BHcODB7q.js +1 -0
  6. package/.output/public/assets/card-Dhr_eFYj.js +1 -0
  7. package/.output/public/assets/{chart-no-axes-combined-FaYMGmQS.js → chart-no-axes-combined-Dy4WJv2U.js} +1 -1
  8. package/.output/public/assets/composite-Bp98A0Ld.js +1 -0
  9. package/.output/public/assets/createServerFn-CA8fs7QD.js +3 -0
  10. package/.output/public/assets/dialog-CUTrcPlB.js +1 -0
  11. package/.output/public/assets/{dist-DspNKfMj.js → dist-C_NydL0e.js} +1 -1
  12. package/.output/public/assets/index-DdAz-CXv.js +10 -0
  13. package/.output/public/assets/input-group-cSOG4wh_.js +1 -0
  14. package/.output/public/assets/insights-C0PBSZ-n.js +1 -0
  15. package/.output/public/assets/installations-CE33wgbY.js +2 -0
  16. package/.output/public/assets/installations-CStZja8x.js +1 -0
  17. package/.output/public/assets/lazyRouteComponent-Ccp3bwBY.js +1 -0
  18. package/.output/public/assets/link-CI8idAUi.js +1 -0
  19. package/.output/public/assets/loader-circle-BoxLrsOu.js +1 -0
  20. package/.output/public/assets/refresh-cw-Bqc72UZG.js +1 -0
  21. package/.output/public/assets/{routes-DcQnVajO.js → routes-DGVXdzpw.js} +2 -2
  22. package/.output/public/assets/routes-DTlwOr8I.js +1 -0
  23. package/.output/public/assets/{shield-check-DE_hVuHu.js → shield-check-uK3MHWJZ.js} +1 -1
  24. package/.output/public/assets/signing-CMf4aqDO.js +1 -0
  25. package/.output/public/assets/styles-BCvbtKsm.css +2 -0
  26. package/.output/public/assets/table-2vcV89QB.js +1 -0
  27. package/.output/public/assets/useCompositeListItem-CHLITE_4.js +1 -0
  28. package/.output/public/assets/x-DCd0j8c6.js +1 -0
  29. package/.output/server/{__23tanstack-start-server-fn-resolver-DBP9TQ7K.mjs → __23tanstack-start-server-fn-resolver-B7ioJhtE.mjs} +44 -52
  30. package/.output/server/_libs/@base-ui/react+[...].mjs +1906 -4040
  31. package/.output/server/_libs/@tanstack/react-router+[...].mjs +52 -4
  32. package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +2 -2
  33. package/.output/server/_libs/lucide-react.mjs +36 -3
  34. package/.output/server/_libs/next-themes.mjs +2 -2
  35. package/.output/server/_libs/sonner.mjs +2 -2
  36. package/.output/server/_libs/tanstack__react-query.mjs +2 -2
  37. package/.output/server/_libs/verkit.mjs +20 -15
  38. package/.output/server/_runtime.mjs +1 -10
  39. package/.output/server/_ssr/HashValueDisplay-DwpGLfrl.mjs +55 -0
  40. package/.output/server/_ssr/InsightsPageHeader-Df4Brj8u.mjs +484 -0
  41. package/.output/server/_ssr/{api-keys-BnM_o2nS.mjs → api-keys-CZeTPQys.mjs} +11 -13
  42. package/.output/server/_ssr/{api-keys-api-CIywsSF5.mjs → api-keys-api-BUPQVSpk.mjs} +2 -2
  43. package/.output/server/_ssr/{api-keys-rpc-BSp1wkIV.mjs → api-keys-rpc-Cs7tS5_j.mjs} +5 -5
  44. package/.output/server/_ssr/{api-rpc-cG5wd_tO.mjs → api-rpc-tAupfGQm.mjs} +73 -142
  45. package/.output/server/_ssr/{auth-rpc-BrJ6obHJ.mjs → auth-rpc-DUaeOWKK.mjs} +4 -4
  46. package/.output/server/_ssr/{auth.server-DwKoZoQM.mjs → auth.server-DW71WzsD.mjs} +2 -5
  47. package/.output/server/_ssr/{bundle-signing-rpc-COoQhW8E.mjs → bundle-signing-rpc-BhnBw0-d.mjs} +2 -2
  48. package/.output/server/_ssr/bundle-signing.server-C4NW5mWx.mjs +12 -0
  49. package/.output/server/_ssr/{alert-Dim3MGY5.mjs → card-BRiPmi0C.mjs} +61 -39
  50. package/.output/server/_ssr/{config.server-DEiLhKmy.mjs → config.server-afDuAwaG.mjs} +15 -8
  51. package/.output/server/_ssr/{createSsrRpc-9P0S0lJ3.mjs → createSsrRpc-DjWNJ0uc.mjs} +6 -6
  52. package/.output/server/_ssr/databaseJsonValue-d6I3nAS4.mjs +45 -0
  53. package/.output/server/_ssr/{databaseRows-CwbNwYSl.mjs → databaseRows-5bCRTxm-.mjs} +4 -44
  54. package/.output/server/_ssr/{deleteBundle-Cu_XOMt0.mjs → deleteBundle-BTGwBgzx.mjs} +2 -2
  55. package/.output/server/_ssr/{dialog-CQuRMNZ4.mjs → dialog-DS-nw-pA.mjs} +2 -2
  56. package/.output/server/_ssr/{dist-BVSLTpoP.mjs → dist-CyzPaI5v.mjs} +2 -2
  57. package/.output/server/_ssr/{getBundleChildren-BcRMFoDF.mjs → getBundleChildren-C0vh4ZvE.mjs} +2 -2
  58. package/.output/server/_ssr/input-group-BpsnmvZw.mjs +70 -0
  59. package/.output/server/_ssr/insights-BdS364Xu.mjs +460 -0
  60. package/.output/server/_ssr/insights-rpc-38DujK0G.mjs +57 -0
  61. package/.output/server/_ssr/insightsContract-fjJDBeEE.mjs +303 -0
  62. package/.output/server/_ssr/installations-CtA59MQG.mjs +29 -0
  63. package/.output/server/_ssr/installations-DiVKEF5T.mjs +627 -0
  64. package/.output/server/_ssr/{router-C-KUBfOi.mjs → router-B9wG_9f8.mjs} +37 -46
  65. package/.output/server/_ssr/{routes-Z7TwC6nC.mjs → routes-CKrrS2q2.mjs} +3 -3
  66. package/.output/server/_ssr/{routes-BM4ht9jb.mjs → routes-D7Yh_jfj.mjs} +402 -390
  67. package/.output/server/_ssr/runtime.server-DacU1TBV.mjs +438 -0
  68. package/.output/server/_ssr/{server-BjzmvXnO.mjs → server-D_C2rMzf.mjs} +5 -5
  69. package/.output/server/_ssr/{signing-k8GxyBDF.mjs → signing-Dz1WkZOf.mjs} +16 -85
  70. package/.output/server/_ssr/ssr.mjs +1 -1
  71. package/.output/server/_ssr/{field-DRIXKjZG.mjs → table-CnZmon_Z.mjs} +51 -5
  72. package/.output/server/_ssr/{uuidv7-BbJ-9D_I.mjs → uuidv7-D0QFkPL_.mjs} +3 -3
  73. package/.output/server/_tanstack-start-manifest_v-2MjTyxil.mjs +97 -0
  74. package/.output/server/index.mjs +168 -203
  75. package/lib/index.d.mts +4 -4
  76. package/lib/vite-internal.mjs +1 -1
  77. package/lib/vite.d.mts +0 -1
  78. package/package.json +8 -8
  79. package/src/components/AppSidebar.tsx +12 -16
  80. package/src/components/ConsoleAccessPage.tsx +1 -1
  81. package/src/components/HashValueDisplay.tsx +11 -2
  82. package/src/components/features/api-keys/ApiKeysPage.tsx +1 -1
  83. package/src/components/features/bundles/BundleChildrenPanel.tsx +7 -6
  84. package/src/components/features/bundles/BundleMetadata.tsx +1 -20
  85. package/src/components/features/channels/ChannelManagementDialog.tsx +1 -1
  86. package/src/components/features/insights/EventDetails.tsx +151 -0
  87. package/src/components/features/insights/EventHistoryCard.tsx +245 -0
  88. package/src/components/features/insights/EventHistoryList.tsx +63 -0
  89. package/src/components/features/insights/InsightsBundleReports.tsx +51 -0
  90. package/src/components/features/insights/InsightsControls.tsx +121 -0
  91. package/src/components/features/{analytics/AnalyticsErrorAlert.tsx → insights/InsightsErrorAlert.tsx} +6 -6
  92. package/src/components/features/insights/InsightsOverview.tsx +203 -0
  93. package/src/components/features/insights/InsightsPageHeader.tsx +48 -0
  94. package/src/components/features/insights/InsightsPagination.tsx +52 -0
  95. package/src/components/features/insights/InstallationHistoryCard.tsx +250 -0
  96. package/src/components/features/insights/InstallationMatchesCard.tsx +178 -0
  97. package/src/components/features/insights/InstallationPageHeader.tsx +90 -0
  98. package/src/components/features/releases/ReleaseEditorSheet.tsx +24 -24
  99. package/src/components/features/signing/BundleSigningPage.tsx +18 -126
  100. package/src/components/ui/badge.tsx +2 -0
  101. package/src/components/ui/combobox.tsx +1 -0
  102. package/src/components/ui/skeleton.tsx +4 -1
  103. package/src/components/ui/tooltip.tsx +1 -1
  104. package/src/index.ts +7 -3
  105. package/src/lib/api-rpc.ts +19 -90
  106. package/src/lib/api.ts +0 -99
  107. package/src/lib/insights-api.ts +100 -0
  108. package/src/lib/insights-rpc.ts +88 -0
  109. package/src/lib/insights-view.ts +15 -0
  110. package/src/lib/public-bundle-error.ts +19 -0
  111. package/src/lib/server/bundle-signing.server.ts +5 -82
  112. package/src/lib/server/console-runtime.server.ts +0 -9
  113. package/src/lib/server/releaseReachability.ts +1 -4
  114. package/src/lib/server/runtime.server.ts +6 -142
  115. package/src/routeTree.gen.ts +21 -21
  116. package/src/router.tsx +3 -0
  117. package/src/routes/-installations-search.ts +25 -12
  118. package/src/routes/__root.tsx +6 -16
  119. package/src/routes/api-keys.tsx +1 -1
  120. package/src/routes/index.tsx +21 -81
  121. package/src/routes/insights.tsx +84 -0
  122. package/src/routes/installations.tsx +294 -142
  123. package/src/styles.css +6 -0
  124. package/.output/public/assets/AnalyticsCapabilityContext-BCke57-7.js +0 -1
  125. package/.output/public/assets/BundleActivityChart-CnsBe_1J.js +0 -42
  126. package/.output/public/assets/BundleIdDisplay-Dky75zLU.js +0 -1
  127. package/.output/public/assets/alert-C-1KvDW8.js +0 -1
  128. package/.output/public/assets/analytics-Bqz0c-BT.js +0 -1
  129. package/.output/public/assets/analytics-api-CJdrTLLO.js +0 -1
  130. package/.output/public/assets/api-B_Rh-uzC.js +0 -1
  131. package/.output/public/assets/api-keys-BurCFhzH.js +0 -1
  132. package/.output/public/assets/api-keys-api-BEDJdQg3.js +0 -1
  133. package/.output/public/assets/badge-Bf0fZ-Gx.js +0 -1
  134. package/.output/public/assets/createServerFn-gM2-LFBy.js +0 -3
  135. package/.output/public/assets/dialog-BS60BRr2.js +0 -1
  136. package/.output/public/assets/download-O-yNBc9H.js +0 -1
  137. package/.output/public/assets/field-Buq148-A.js +0 -1
  138. package/.output/public/assets/index-kFhd2S78.js +0 -10
  139. package/.output/public/assets/installations-BrQ-od41.js +0 -2
  140. package/.output/public/assets/installations-CGlZ3ROz.js +0 -1
  141. package/.output/public/assets/lazyRouteComponent-rPzgA7S-.js +0 -1
  142. package/.output/public/assets/loader-circle-DCYF-wJl.js +0 -1
  143. package/.output/public/assets/refresh-cw-n8XYbPPj.js +0 -1
  144. package/.output/public/assets/routes-CrdokJ80.js +0 -1
  145. package/.output/public/assets/search-DLhbWw3W.js +0 -1
  146. package/.output/public/assets/signing-HRexfjsH.js +0 -1
  147. package/.output/public/assets/styles-eB4EtvFS.css +0 -2
  148. package/.output/public/assets/table-D8CLieHX.js +0 -1
  149. package/.output/public/assets/textarea-YJLTVAoq.js +0 -1
  150. package/.output/public/assets/useMutation-JsGVjQMY.js +0 -1
  151. package/.output/public/assets/useOpenInteractionType-4cRD5xO0.js +0 -1
  152. package/.output/server/_libs/@reduxjs/toolkit+[...].mjs +0 -1554
  153. package/.output/server/_libs/d3-array.mjs +0 -231
  154. package/.output/server/_libs/d3-color.mjs +0 -330
  155. package/.output/server/_libs/d3-format.mjs +0 -252
  156. package/.output/server/_libs/d3-interpolate.mjs +0 -207
  157. package/.output/server/_libs/d3-path.mjs +0 -77
  158. package/.output/server/_libs/d3-scale+[...].mjs +0 -1907
  159. package/.output/server/_libs/d3-shape.mjs +0 -818
  160. package/.output/server/_libs/decimal.js-light.mjs +0 -884
  161. package/.output/server/_libs/es-toolkit.mjs +0 -1134
  162. package/.output/server/_libs/eventemitter3.mjs +0 -236
  163. package/.output/server/_libs/immer.mjs +0 -532
  164. package/.output/server/_libs/react-redux+[...].mjs +0 -205
  165. package/.output/server/_libs/recharts+[...].mjs +0 -14117
  166. package/.output/server/_ssr/AnalyticsCapabilityContext-ui4DX8GO.mjs +0 -15
  167. package/.output/server/_ssr/BundleActivityChart-BiB9ZORH.mjs +0 -288
  168. package/.output/server/_ssr/BundleIdDisplay-Czk6iR6p.mjs +0 -13
  169. package/.output/server/_ssr/analytics-DtMTn8fn.mjs +0 -858
  170. package/.output/server/_ssr/analytics-api-Cg1M5v_H.mjs +0 -71
  171. package/.output/server/_ssr/analytics-input-fRRRn0uA.mjs +0 -96
  172. package/.output/server/_ssr/analytics-rpc-DY5uXvHT.mjs +0 -155
  173. package/.output/server/_ssr/api-BTSv1tZb.mjs +0 -282
  174. package/.output/server/_ssr/badge-DCxbEeof.mjs +0 -26
  175. package/.output/server/_ssr/bundle-signing.server-D5u8KkMp.mjs +0 -51
  176. package/.output/server/_ssr/installations-B-azgAvX.mjs +0 -19
  177. package/.output/server/_ssr/installations-Ctvzp2JH.mjs +0 -535
  178. package/.output/server/_ssr/runtime.server-awTLiiXW.mjs +0 -527
  179. package/.output/server/_ssr/table-v1yCvYo7.mjs +0 -53
  180. package/.output/server/_ssr/textarea-DYHnskhb.mjs +0 -14
  181. package/.output/server/_tanstack-start-manifest_v-CH4StzJu.mjs +0 -108
  182. package/src/components/features/analytics/ActivityChart.tsx +0 -150
  183. package/src/components/features/analytics/AnalyticsCapabilityContext.tsx +0 -24
  184. package/src/components/features/analytics/AnalyticsControls.tsx +0 -115
  185. package/src/components/features/analytics/AnalyticsOverview.tsx +0 -208
  186. package/src/components/features/analytics/BundleDistribution.tsx +0 -133
  187. package/src/components/features/analytics/BundleSelector.tsx +0 -114
  188. package/src/components/features/analytics/InstallationHistoryCard.tsx +0 -248
  189. package/src/components/features/analytics/InstallationMatchesCard.tsx +0 -132
  190. package/src/components/features/analytics/InstallationPageHeader.tsx +0 -138
  191. package/src/components/features/analytics/InstallationPagination.tsx +0 -48
  192. package/src/components/features/analytics/InstallationSearch.tsx +0 -230
  193. package/src/components/features/analytics/UpdateOutcomes.tsx +0 -147
  194. package/src/components/features/bundles/BundleActivityChart.tsx +0 -193
  195. package/src/components/features/bundles/BundleAnalyticsSummary.tsx +0 -123
  196. package/src/lib/analytics-api.ts +0 -145
  197. package/src/lib/analytics-error.ts +0 -19
  198. package/src/lib/analytics-input.ts +0 -187
  199. package/src/lib/analytics-overview.ts +0 -170
  200. package/src/lib/analytics-rpc.ts +0 -218
  201. package/src/lib/server/releaseActivity.ts +0 -27
  202. package/src/routes/analytics.tsx +0 -139
  203. /package/.output/public/assets/{preload-helper-TKOBYGYD.js → preload-helper-CQ36nxok.js} +0 -0
@@ -1,858 +0,0 @@
1
- import { i as __toESM } from "../_runtime.mjs";
2
- import { A as ComboboxList$1, C as ComboboxItemIndicator, D as ComboboxPopup, E as ComboboxItem$1, M as ComboboxInput$1, N as ComboboxTrigger$1, O as ComboboxPositioner, T as ComboboxEmpty$1, U as ToggleGroup, W as Toggle, at as require_react, it as require_jsx_runtime, j as ComboboxClear$1, k as ComboboxPortal, w as ComboboxRoot } from "../_libs/@base-ui/react+[...].mjs";
3
- import { n as useAnalyticsCapability } from "./AnalyticsCapabilityContext-ui4DX8GO.mjs";
4
- import { M as ChevronDown, N as Check, P as ChartNoAxesCombined, l as Search, t as X } from "../_libs/lucide-react.mjs";
5
- import { t as cva } from "../_libs/class-variance-authority+clsx.mjs";
6
- import { C as cn, _ as Skeleton, g as SidebarTrigger, t as Button$1 } from "./createSsrRpc-9P0S0lJ3.mjs";
7
- import { m as useNavigate } from "../_libs/@tanstack/react-router+[...].mjs";
8
- import { i as useActiveInstallationQuery, o as useAnalyticsOverviewQuery } from "./analytics-api-Cg1M5v_H.mjs";
9
- import { a as FieldLabel, i as FieldGroup, t as Field } from "./field-DRIXKjZG.mjs";
10
- import { c as useBundleEventAnalyticsQuery, i as InputGroupInput, n as InputGroupAddon, r as InputGroupButton, t as InputGroup } from "./api-BTSv1tZb.mjs";
11
- import { a as Card, c as CardFooter, l as CardHeader, o as CardContent, s as CardDescription, u as CardTitle } from "./alert-Dim3MGY5.mjs";
12
- import { a as CartesianGrid, i as Area, n as YAxis, r as XAxis, t as AreaChart } from "../_libs/recharts+[...].mjs";
13
- import { a as ChartTooltipContent, i as ChartTooltip, n as BundleActivityChart, r as ChartContainer, t as AnalyticsErrorAlert } from "./BundleActivityChart-BiB9ZORH.mjs";
14
- //#region node_modules/.nitro/vite/services/ssr/assets/analytics-DtMTn8fn.js
15
- var import_react = /* @__PURE__ */ __toESM(require_react());
16
- var import_jsx_runtime = require_jsx_runtime();
17
- var toggleVariants = cva("group/toggle inline-flex items-center justify-center gap-1 rounded-md text-xs font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
18
- variants: {
19
- variant: {
20
- default: "bg-transparent",
21
- outline: "border border-input bg-transparent hover:bg-muted"
22
- },
23
- size: {
24
- default: "h-7 min-w-7 px-2 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",
25
- sm: "h-6 min-w-6 rounded-[min(var(--radius-md),8px)] px-2 text-[0.625rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
26
- lg: "h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2"
27
- }
28
- },
29
- defaultVariants: {
30
- variant: "default",
31
- size: "default"
32
- }
33
- });
34
- var ToggleGroupContext = import_react.createContext({
35
- size: "default",
36
- variant: "default",
37
- spacing: 2,
38
- orientation: "horizontal"
39
- });
40
- function ToggleGroup$1({ className, variant, size, spacing = 2, orientation = "horizontal", children, ...props }) {
41
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToggleGroup, {
42
- "data-slot": "toggle-group",
43
- "data-variant": variant,
44
- "data-size": size,
45
- "data-spacing": spacing,
46
- "data-orientation": orientation,
47
- style: { "--gap": spacing },
48
- className: cn("group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-md data-[size=sm]:rounded-[min(var(--radius-md),8px)] data-vertical:flex-col data-vertical:items-stretch", className),
49
- ...props,
50
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToggleGroupContext.Provider, {
51
- value: {
52
- variant,
53
- size,
54
- spacing,
55
- orientation
56
- },
57
- children
58
- })
59
- });
60
- }
61
- function ToggleGroupItem({ className, children, variant = "default", size = "default", ...props }) {
62
- const context = import_react.useContext(ToggleGroupContext);
63
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Toggle, {
64
- "data-slot": "toggle-group-item",
65
- "data-variant": context.variant || variant,
66
- "data-size": context.size || size,
67
- "data-spacing": context.spacing,
68
- className: cn("shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-md group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-md group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-md group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t", toggleVariants({
69
- variant: context.variant || variant,
70
- size: context.size || size
71
- }), className),
72
- ...props,
73
- children
74
- });
75
- }
76
- var windows = [
77
- {
78
- value: "24h",
79
- label: "24 hours",
80
- shortLabel: "24h"
81
- },
82
- {
83
- value: "7d",
84
- label: "7 days",
85
- shortLabel: "7d"
86
- },
87
- {
88
- value: "30d",
89
- label: "30 days",
90
- shortLabel: "30d"
91
- }
92
- ];
93
- function AnalyticsControls({ onInstallationSearch, onWindowChange, window }) {
94
- const [draft, setDraft] = (0, import_react.useState)("");
95
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("section", {
96
- "aria-label": "Analytics controls",
97
- className: "rounded-xl border bg-background px-4 py-3",
98
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("form", {
99
- "aria-label": "Filter analytics",
100
- className: "min-w-0",
101
- onSubmit: (event) => {
102
- event.preventDefault();
103
- const query = draft.trim();
104
- if (query) onInstallationSearch(query);
105
- },
106
- role: "search",
107
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(FieldGroup, {
108
- className: "gap-3 md:grid md:grid-cols-[auto_minmax(18rem,1fr)] md:items-end",
109
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, {
110
- className: "min-w-0",
111
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FieldLabel, { children: "Reporting period" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToggleGroup$1, {
112
- "aria-label": "Reporting period",
113
- className: "w-full lg:w-fit",
114
- onValueChange: (value) => {
115
- if (value[0]) onWindowChange(value[0]);
116
- },
117
- spacing: 0,
118
- size: "lg",
119
- value: [window],
120
- variant: "outline",
121
- children: windows.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToggleGroupItem, {
122
- "aria-label": item.label,
123
- className: "flex-1 md:flex-none",
124
- value: item.value,
125
- children: item.shortLabel
126
- }, item.value))
127
- })]
128
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, {
129
- className: "min-w-0",
130
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FieldLabel, {
131
- htmlFor: "installation-history-search",
132
- children: "Installation history"
133
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
134
- className: "flex min-w-0 flex-col gap-2 sm:flex-row",
135
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(InputGroup, {
136
- className: "h-8 flex-1",
137
- children: [
138
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InputGroupAddon, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Search, { "aria-hidden": "true" }) }),
139
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InputGroupInput, {
140
- "aria-label": "User or install ID",
141
- id: "installation-history-search",
142
- maxLength: 1024,
143
- onChange: (event) => setDraft(event.target.value),
144
- placeholder: "User ID or install ID",
145
- type: "search",
146
- value: draft
147
- }),
148
- draft && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InputGroupAddon, {
149
- align: "inline-end",
150
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InputGroupButton, {
151
- "aria-label": "Clear installation search",
152
- onClick: () => setDraft(""),
153
- size: "icon-xs",
154
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, { "aria-hidden": "true" })
155
- })
156
- })
157
- ]
158
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
159
- className: "w-full sm:w-auto",
160
- size: "lg",
161
- type: "submit",
162
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Search, {
163
- "aria-hidden": "true",
164
- "data-icon": "inline-start"
165
- }), "Search"]
166
- })]
167
- })]
168
- })]
169
- })
170
- })
171
- });
172
- }
173
- var dayFormatter = new Intl.DateTimeFormat("en", {
174
- day: "numeric",
175
- month: "short",
176
- timeZone: "UTC"
177
- });
178
- var hourFormatter = new Intl.DateTimeFormat("en", {
179
- day: "numeric",
180
- hour: "numeric",
181
- month: "short",
182
- timeZone: "UTC"
183
- });
184
- var formatBucket = (bucketStartMs, window) => (window === "24h" ? hourFormatter : dayFormatter).format(new Date(bucketStartMs));
185
- var chartConfig = { installations: {
186
- label: "Active installations",
187
- color: "var(--chart-2)"
188
- } };
189
- function ActivityChart({ series, window }) {
190
- const chartData = series.map((point) => ({
191
- bucketStartMs: point.bucketStartMs,
192
- installations: point.value
193
- }));
194
- const hasReports = series.some(({ value }) => value > 0);
195
- const bucketName = window === "24h" ? "hour" : "day";
196
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
197
- className: "flex min-w-0 flex-col gap-3",
198
- children: [
199
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
200
- className: "flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1",
201
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", {
202
- className: "text-sm font-medium",
203
- children: "Activity over time"
204
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
205
- className: "text-xs text-muted-foreground",
206
- children: ["Unique active installations per ", bucketName]
207
- })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
208
- className: "text-xs text-muted-foreground",
209
- children: "UTC"
210
- })]
211
- }),
212
- !hasReports && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
213
- className: "text-sm text-muted-foreground",
214
- children: "No installations reported during this period."
215
- }),
216
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartContainer, {
217
- "aria-label": `Active installations per ${bucketName}`,
218
- className: "h-64 w-full aspect-auto",
219
- config: chartConfig,
220
- role: "img",
221
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AreaChart, {
222
- accessibilityLayer: true,
223
- data: chartData,
224
- margin: {
225
- left: -16,
226
- right: 4,
227
- top: 8
228
- },
229
- children: [
230
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CartesianGrid, { vertical: false }),
231
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(XAxis, {
232
- axisLine: false,
233
- dataKey: "bucketStartMs",
234
- minTickGap: 28,
235
- tickFormatter: (value) => formatBucket(value, window),
236
- tickLine: false
237
- }),
238
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(YAxis, {
239
- allowDecimals: false,
240
- axisLine: false,
241
- tickLine: false,
242
- width: 32
243
- }),
244
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartTooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartTooltipContent, { labelFormatter: (_, payload) => {
245
- const bucket = payload[0]?.payload.bucketStartMs;
246
- return typeof bucket === "number" ? formatBucket(bucket, window) : "";
247
- } }) }),
248
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Area, {
249
- dataKey: "installations",
250
- fill: "var(--color-installations)",
251
- fillOpacity: .12,
252
- isAnimationActive: false,
253
- stroke: "var(--color-installations)",
254
- strokeWidth: 2,
255
- type: "monotone"
256
- })
257
- ]
258
- })
259
- }),
260
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
261
- className: "text-xs leading-relaxed text-muted-foreground",
262
- children: [
263
- "Each point counts an installation once in that ",
264
- bucketName,
265
- ". The total above counts it once across the whole period, so the points do not add up to the total."
266
- ]
267
- }),
268
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
269
- className: "sr-only",
270
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", {
271
- "aria-label": `Exact active installations per ${bucketName}`,
272
- children: [
273
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("caption", { children: [
274
- "Unique installations that reported an update status in each UTC",
275
- ` ${bucketName}`,
276
- "."
277
- ] }),
278
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("th", {
279
- scope: "col",
280
- children: ["UTC ", bucketName]
281
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
282
- scope: "col",
283
- children: "Active installations"
284
- })] }) }),
285
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: chartData.map((point) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
286
- scope: "row",
287
- children: formatBucket(point.bucketStartMs, window)
288
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: point.installations })] }, point.bucketStartMs)) })
289
- ]
290
- })
291
- })
292
- ]
293
- });
294
- }
295
- var Combobox$1 = ComboboxRoot;
296
- function ComboboxTrigger({ className, children, ...props }) {
297
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ComboboxTrigger$1, {
298
- "data-slot": "combobox-trigger",
299
- className: cn("[&_svg:not([class*='size-'])]:size-3.5", className),
300
- ...props,
301
- children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: "text-muted-foreground size-3.5 pointer-events-none" })]
302
- });
303
- }
304
- function ComboboxClear({ className, ...props }) {
305
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxClear$1, {
306
- "data-slot": "combobox-clear",
307
- render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InputGroupButton, {
308
- variant: "ghost",
309
- size: "icon-xs"
310
- }),
311
- className: cn(className),
312
- ...props,
313
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, { className: "pointer-events-none" })
314
- });
315
- }
316
- function ComboboxInput({ className, children, disabled = false, showTrigger = true, showClear = false, ...props }) {
317
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(InputGroup, {
318
- className: cn("w-auto", className),
319
- children: [
320
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxInput$1, {
321
- render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InputGroupInput, { disabled }),
322
- ...props
323
- }),
324
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(InputGroupAddon, {
325
- align: "inline-end",
326
- children: [showTrigger && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxTrigger, { render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InputGroupButton, {
327
- size: "icon-xs",
328
- variant: "ghost",
329
- "data-slot": "input-group-button",
330
- className: "group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent",
331
- disabled
332
- }) }), showClear && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxClear, { disabled })]
333
- }),
334
- children
335
- ]
336
- });
337
- }
338
- function ComboboxContent({ className, side = "bottom", sideOffset = 6, align = "start", alignOffset = 0, anchor, ...props }) {
339
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxPortal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxPositioner, {
340
- side,
341
- sideOffset,
342
- align,
343
- alignOffset,
344
- anchor,
345
- className: "isolate z-50",
346
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxPopup, {
347
- "data-slot": "combobox-content",
348
- "data-chips": !!anchor,
349
- className: cn("bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 *:data-[slot=input-group]:bg-input/20 dark:bg-popover max-h-72 min-w-32 overflow-hidden rounded-lg shadow-md ring-1 duration-100 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-7 *:data-[slot=input-group]:border-none *:data-[slot=input-group]:shadow-none dark group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) data-[chips=true]:min-w-(--anchor-width)", className),
350
- ...props
351
- })
352
- }) });
353
- }
354
- function ComboboxList({ className, ...props }) {
355
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxList$1, {
356
- "data-slot": "combobox-list",
357
- className: cn("no-scrollbar max-h-[min(calc(--spacing(72)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto p-1 data-empty:p-0 overflow-y-auto overscroll-contain", className),
358
- ...props
359
- });
360
- }
361
- function ComboboxItem({ className, children, ...props }) {
362
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ComboboxItem$1, {
363
- "data-slot": "combobox-item",
364
- className: cn("data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground min-h-7 gap-2 rounded-md px-2 py-1 text-xs/relaxed [&_svg:not([class*='size-'])]:size-3.5 relative flex w-full cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", className),
365
- ...props,
366
- children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxItemIndicator, {
367
- render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pointer-events-none absolute right-2 flex items-center justify-center" }),
368
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "pointer-events-none" })
369
- })]
370
- });
371
- }
372
- function ComboboxEmpty({ className, ...props }) {
373
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxEmpty$1, {
374
- "data-slot": "combobox-empty",
375
- className: cn("text-muted-foreground hidden w-full justify-center py-2 text-center text-xs/relaxed group-data-empty/combobox-content:flex", className),
376
- ...props
377
- });
378
- }
379
- function BundleSelector({ bundleId, bundles, onBundleChange }) {
380
- const selectedBundle = bundles.find((bundle) => bundle.bundleId === bundleId) ?? null;
381
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, {
382
- className: "w-full min-w-0 sm:max-w-md",
383
- "data-disabled": !bundles.length,
384
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FieldLabel, {
385
- htmlFor: "analytics-bundle-selector",
386
- children: "Bundle to inspect"
387
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Combobox$1, {
388
- autoHighlight: true,
389
- disabled: !bundles.length,
390
- filter: (bundle, query) => {
391
- const normalizedQuery = query.trim().toLowerCase();
392
- return bundle.bundleId.toLowerCase().includes(normalizedQuery) || bundle.description.toLowerCase().includes(normalizedQuery);
393
- },
394
- isItemEqualToValue: (bundle, value) => bundle.bundleId === value.bundleId,
395
- items: bundles,
396
- itemToStringLabel: (bundle) => bundle.bundleId,
397
- onValueChange: (bundle) => {
398
- if (bundle) onBundleChange(bundle.bundleId);
399
- },
400
- value: selectedBundle,
401
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxTrigger, {
402
- "aria-label": "Bundle to inspect",
403
- id: "analytics-bundle-selector",
404
- render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
405
- className: "h-auto min-h-12 w-full min-w-0 justify-between px-3 py-2 text-left",
406
- size: "lg",
407
- variant: "outline"
408
- }),
409
- children: selectedBundle ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
410
- className: "flex min-w-0 flex-1 flex-col items-start gap-1",
411
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", {
412
- className: "max-w-full truncate text-xs/4",
413
- children: selectedBundle.bundleId
414
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
415
- className: "max-w-full truncate text-xs/4 text-muted-foreground",
416
- children: selectedBundle.description
417
- })]
418
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
419
- className: "truncate text-muted-foreground",
420
- children: "No bundles available"
421
- })
422
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ComboboxContent, {
423
- className: "min-w-(--anchor-width)",
424
- children: [
425
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxInput, {
426
- "aria-label": "Search bundles",
427
- placeholder: "Search by bundle ID or description",
428
- showClear: true,
429
- showTrigger: false
430
- }),
431
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxEmpty, { children: "No bundles found." }),
432
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxList, { children: (bundle) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxItem, {
433
- className: "min-h-12 px-3 py-2",
434
- value: bundle,
435
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
436
- className: "flex min-w-0 flex-col gap-1 pr-5",
437
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", {
438
- className: "max-w-72 truncate text-xs/4",
439
- children: bundle.bundleId
440
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
441
- className: "truncate text-xs/4 text-muted-foreground",
442
- children: bundle.description
443
- })]
444
- })
445
- }, bundle.bundleId) })
446
- ]
447
- })]
448
- })]
449
- });
450
- }
451
- function UpdateOutcomes({ bundleSelector, configuredPercentage, latestBundleInstallations, reportingInstallations, state, window }) {
452
- const latestBundleShare = reportingInstallations === 0 ? 0 : latestBundleInstallations / reportingInstallations * 100;
453
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, {
454
- className: "min-w-0 overflow-hidden shadow-sm",
455
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
456
- className: "gap-4 space-y-0 lg:flex-row lg:items-start lg:justify-between",
457
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
458
- className: "flex min-w-0 flex-col gap-1.5",
459
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, {
460
- className: "text-sm font-medium",
461
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
462
- id: "bundle-detail-heading",
463
- children: "Selected bundle activity"
464
- })
465
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardDescription, { children: "Select a bundle to inspect its latest presence, applies, and recoveries during this period." })]
466
- }), bundleSelector]
467
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardContent, {
468
- className: "pt-10",
469
- children: state.status === "idle" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
470
- className: "text-sm text-muted-foreground",
471
- children: "No active bundle is available."
472
- }) : state.status === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
473
- "aria-label": "Loading update outcomes",
474
- className: "flex flex-col gap-4",
475
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
476
- className: "grid grid-cols-2 gap-4 lg:grid-cols-4",
477
- children: [
478
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-14 w-full" }),
479
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-14 w-full" }),
480
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-14 w-full" }),
481
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-14 w-full" })
482
- ]
483
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-32 w-full" })]
484
- }) : state.status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AnalyticsErrorAlert, {
485
- error: state.error,
486
- fallbackTitle: "Bundle movement unavailable"
487
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
488
- className: "flex min-w-0 flex-col gap-5",
489
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", {
490
- className: "grid sm:grid-cols-2 lg:grid-cols-4 lg:divide-x lg:divide-border/70",
491
- children: [
492
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
493
- className: "border-b pb-4 sm:pr-4 lg:border-b-0",
494
- children: [
495
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
496
- className: "text-xs text-muted-foreground",
497
- children: "Latest bundle share"
498
- }),
499
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dd", {
500
- className: "mt-1 text-2xl font-semibold tracking-tight tabular-nums",
501
- children: [Math.round(latestBundleShare), "%"]
502
- }),
503
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dd", {
504
- className: "mt-1 text-xs text-muted-foreground tabular-nums",
505
- children: [
506
- latestBundleInstallations.toLocaleString(),
507
- " of",
508
- " ",
509
- reportingInstallations.toLocaleString(),
510
- " reporting installs"
511
- ]
512
- })
513
- ]
514
- }),
515
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
516
- className: "border-b py-4 sm:pl-4 lg:border-b-0 lg:py-0 lg:pr-4",
517
- children: [
518
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
519
- className: "text-xs text-muted-foreground",
520
- children: "Newly applied"
521
- }),
522
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
523
- className: "mt-1 text-2xl font-semibold tracking-tight tabular-nums",
524
- children: state.data.summary.installed.toLocaleString()
525
- }),
526
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
527
- className: "mt-1 text-xs text-muted-foreground",
528
- children: "Distinct installations"
529
- })
530
- ]
531
- }),
532
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
533
- className: "border-b py-4 sm:pr-4 lg:border-b-0 lg:py-0 lg:pl-4",
534
- children: [
535
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
536
- className: "text-xs text-muted-foreground",
537
- children: "Recovered away"
538
- }),
539
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
540
- className: "mt-1 text-2xl font-semibold tracking-tight tabular-nums",
541
- children: state.data.summary.recovered.toLocaleString()
542
- }),
543
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
544
- className: "mt-1 text-xs text-muted-foreground",
545
- children: "Distinct installations"
546
- })
547
- ]
548
- }),
549
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
550
- className: "pt-4 sm:pl-4 lg:py-0",
551
- children: [
552
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
553
- className: "text-xs text-muted-foreground",
554
- children: "Configured rollout"
555
- }),
556
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
557
- className: "mt-1 text-2xl font-semibold tracking-tight tabular-nums",
558
- children: configuredPercentage === null ? "—" : `${configuredPercentage}%`
559
- }),
560
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
561
- className: "mt-1 text-xs text-muted-foreground",
562
- children: "Eligibility setting"
563
- })
564
- ]
565
- })
566
- ]
567
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleActivityChart, {
568
- installed: state.data.series.installed,
569
- recovered: state.data.series.recovered,
570
- window
571
- })]
572
- })
573
- })]
574
- });
575
- }
576
- var asOfFormatter = new Intl.DateTimeFormat("en", {
577
- dateStyle: "medium",
578
- timeStyle: "short",
579
- timeZone: "UTC"
580
- });
581
- var activityWindowCopy = {
582
- "24h": {
583
- label: "Daily active installations",
584
- period: "last 24 hours"
585
- },
586
- "7d": {
587
- label: "Weekly active installations",
588
- period: "last 7 days"
589
- },
590
- "30d": {
591
- label: "Monthly active installations",
592
- period: "last 30 days"
593
- }
594
- };
595
- function LoadingCard({ children, className, label }) {
596
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, {
597
- "aria-label": label,
598
- className: cn("min-w-0", className),
599
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-4 w-32" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-3 w-4/5" })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardContent, { children })]
600
- });
601
- }
602
- function AnalyticsOverview(props) {
603
- if (props.status === "loading") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
604
- "aria-label": "Loading reporting analytics",
605
- className: "flex min-w-0 flex-col gap-8",
606
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(LoadingCard, {
607
- label: "Loading installation activity",
608
- children: [
609
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-10 w-24" }),
610
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "mt-5 h-64 w-full" }),
611
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
612
- className: "mt-5 grid gap-4 border-t pt-5 sm:grid-cols-3",
613
- children: [
614
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-10 w-full" }),
615
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-10 w-full" }),
616
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-10 w-full" })
617
- ]
618
- })
619
- ]
620
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoadingCard, {
621
- label: "Loading bundle detail",
622
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
623
- className: "flex flex-col gap-4",
624
- children: [
625
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-10 w-full max-w-md" }),
626
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
627
- className: "grid grid-cols-2 gap-4 lg:grid-cols-4",
628
- children: [
629
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-14 w-full" }),
630
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-14 w-full" }),
631
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-14 w-full" }),
632
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-14 w-full" })
633
- ]
634
- }),
635
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-32 w-full" })
636
- ]
637
- })
638
- })]
639
- });
640
- if (props.status === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AnalyticsErrorAlert, {
641
- error: props.error,
642
- fallbackTitle: "Reporting analytics unavailable"
643
- });
644
- const { active, bundleId, bundles, catalog, onBundleChange, outcomes } = props;
645
- const activityCopy = activityWindowCopy[active.window];
646
- const selectedBundleId = outcomes.status === "idle" ? null : outcomes.bundleId;
647
- const latestBundleInstallations = active.bundles.find(({ bundleId }) => bundleId === selectedBundleId)?.installations ?? 0;
648
- const configuredPercentage = catalog.configuredRollouts.find(({ bundleId }) => bundleId === selectedBundleId)?.configuredPercentage ?? null;
649
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
650
- className: "flex min-w-0 flex-col gap-8",
651
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("section", {
652
- "aria-label": "Installation activity",
653
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, {
654
- className: "min-w-0 overflow-hidden shadow-sm",
655
- children: [
656
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
657
- className: "pb-4",
658
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, {
659
- className: "text-sm font-medium",
660
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: activityCopy.label })
661
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardDescription, { children: [
662
- "Unique app installations that reported an update status in the",
663
- " ",
664
- activityCopy.period,
665
- "."
666
- ] })]
667
- }),
668
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardContent, {
669
- className: "flex flex-col gap-5",
670
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
671
- className: "flex flex-col gap-1",
672
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
673
- className: "text-xs font-medium text-muted-foreground",
674
- children: "Active installations"
675
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
676
- className: "flex items-end gap-2",
677
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
678
- className: "text-4xl font-semibold tracking-tight tabular-nums",
679
- children: active.activeInstallations.toLocaleString()
680
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
681
- className: "pb-1 text-xs text-muted-foreground",
682
- children: ["unique in ", activityCopy.period]
683
- })]
684
- })]
685
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
686
- className: "border-t pt-5",
687
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActivityChart, {
688
- series: active.series,
689
- window: active.window
690
- })
691
- })]
692
- }),
693
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardFooter, {
694
- className: "border-t bg-muted/15 p-0",
695
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", {
696
- className: "grid w-full sm:grid-cols-3 sm:divide-x sm:divide-border/70",
697
- children: [
698
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
699
- className: "flex flex-col gap-1 px-5 py-4",
700
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
701
- className: "text-xs text-muted-foreground",
702
- children: "Reported bundles"
703
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
704
- className: "text-lg font-semibold tabular-nums",
705
- children: active.bundles.length.toLocaleString()
706
- })]
707
- }),
708
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
709
- className: "flex flex-col gap-1 border-t px-5 py-4 sm:border-t-0",
710
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
711
- className: "text-xs text-muted-foreground",
712
- children: "Reporting window"
713
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
714
- className: "text-sm font-medium",
715
- children: activityCopy.period
716
- })]
717
- }),
718
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
719
- className: "flex flex-col gap-1 border-t px-5 py-4 sm:border-t-0",
720
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
721
- className: "text-xs text-muted-foreground",
722
- children: "As of"
723
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dd", {
724
- className: "text-xs font-medium tabular-nums",
725
- children: [asOfFormatter.format(new Date(active.asOfMs)), " UTC"]
726
- })]
727
- })
728
- ]
729
- })
730
- })
731
- ]
732
- })
733
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("section", {
734
- "aria-labelledby": "bundle-detail-heading",
735
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UpdateOutcomes, {
736
- bundleSelector: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleSelector, {
737
- bundleId,
738
- bundles,
739
- onBundleChange
740
- }),
741
- configuredPercentage,
742
- latestBundleInstallations,
743
- reportingInstallations: active.activeInstallations,
744
- state: outcomes,
745
- window: active.window
746
- })
747
- })]
748
- });
749
- }
750
- function AnalyticsPage() {
751
- const capability = useAnalyticsCapability();
752
- const navigate = useNavigate();
753
- const [window, setWindow] = (0, import_react.useState)("30d");
754
- const [selectedBundleId, setSelectedBundleId] = (0, import_react.useState)("");
755
- const catalog = useAnalyticsOverviewQuery(capability);
756
- const active = useActiveInstallationQuery(capability, { window });
757
- const configuredById = new Map(catalog.data?.configuredRollouts.map((rollout) => [rollout.bundleId, rollout]) ?? []);
758
- const bundleIds = new Set([...active.data?.bundles.map(({ bundleId }) => bundleId) ?? [], ...catalog.data?.configuredRollouts.map(({ bundleId }) => bundleId) ?? []]);
759
- const bundleOptions = [...bundleIds].map((bundleId) => {
760
- const configured = configuredById.get(bundleId);
761
- const appVersion = configured?.bundle.targetAppVersion ?? "all versions";
762
- return {
763
- bundleId,
764
- description: configured ? `${configured.bundle.platform === "ios" ? "iOS" : "Android"} · ${configured.bundle.channel} · ${appVersion}` : "Metadata unavailable"
765
- };
766
- });
767
- const bundleId = bundleIds.has(selectedBundleId) ? selectedBundleId : bundleOptions[0]?.bundleId ?? "";
768
- const outcomes = useBundleEventAnalyticsQuery({
769
- bundleId,
770
- window,
771
- limit: 1,
772
- offset: 0
773
- }, capability.status === "supported" && bundleId.length > 0);
774
- const outcomeState = !bundleId ? { status: "idle" } : outcomes.isLoading ? {
775
- status: "loading",
776
- bundleId
777
- } : outcomes.error ? {
778
- status: "error",
779
- bundleId,
780
- error: outcomes.error
781
- } : outcomes.data ? {
782
- status: "success",
783
- bundleId,
784
- data: outcomes.data
785
- } : {
786
- status: "loading",
787
- bundleId
788
- };
789
- const analyticsError = active.error ?? catalog.error;
790
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
791
- className: "flex h-svh min-h-0 flex-col",
792
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
793
- className: "sticky top-0 z-10 flex shrink-0 flex-wrap items-center gap-x-3 gap-y-1 border-b bg-background px-3 py-3 sm:min-h-12 sm:flex-nowrap sm:bg-card/70 sm:px-4 sm:backdrop-blur-sm",
794
- children: [
795
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarTrigger, { className: "-ml-1" }),
796
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
797
- className: "flex items-center gap-1.5",
798
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartNoAxesCombined, {
799
- "aria-hidden": "true",
800
- className: "size-3.5 text-muted-foreground"
801
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
802
- className: "text-sm font-medium",
803
- children: "Analytics"
804
- })]
805
- }),
806
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
807
- className: "basis-full pl-9 text-xs text-muted-foreground sm:basis-auto sm:pl-0",
808
- children: "Bundle adoption and movement over time."
809
- })
810
- ]
811
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
812
- className: "min-h-0 min-w-0 flex-1 overflow-y-auto bg-muted/5 p-3 sm:p-6",
813
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
814
- className: "mx-auto flex w-full max-w-6xl flex-col gap-6",
815
- children: [
816
- capability.status === "supported" && capability.mode === "bounded" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
817
- className: "text-xs text-muted-foreground",
818
- children: [
819
- "This database scans up to",
820
- " ",
821
- capability.maxMatchingRows.toLocaleString(),
822
- " matching analytics records per query."
823
- ]
824
- }) : null,
825
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AnalyticsControls, {
826
- onInstallationSearch: (query) => {
827
- navigate({
828
- to: "/installations",
829
- search: {
830
- query,
831
- installId: void 0,
832
- searchOffset: 0,
833
- historyOffset: 0
834
- }
835
- });
836
- },
837
- onWindowChange: setWindow,
838
- window
839
- }),
840
- active.isLoading || catalog.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AnalyticsOverview, { status: "loading" }) : analyticsError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AnalyticsOverview, {
841
- status: "error",
842
- error: analyticsError
843
- }) : active.data && catalog.data ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AnalyticsOverview, {
844
- active: active.data,
845
- bundleId,
846
- bundles: bundleOptions,
847
- catalog: catalog.data,
848
- onBundleChange: setSelectedBundleId,
849
- outcomes: outcomeState,
850
- status: "success"
851
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AnalyticsOverview, { status: "loading" })
852
- ]
853
- })
854
- })]
855
- });
856
- }
857
- //#endregion
858
- export { AnalyticsPage as component };