@hot-updater/console 0.36.6 → 1.0.0-rc.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 (266) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/AnalyticsCapabilityContext-BCke57-7.js +1 -0
  3. package/.output/public/assets/BundleActivityChart-CnsBe_1J.js +42 -0
  4. package/.output/public/assets/BundleIdDisplay-Dky75zLU.js +1 -0
  5. package/.output/public/assets/alert-C-1KvDW8.js +1 -0
  6. package/.output/public/assets/analytics-Bqz0c-BT.js +1 -0
  7. package/.output/public/assets/analytics-api-CJdrTLLO.js +1 -0
  8. package/.output/public/assets/api-B_Rh-uzC.js +1 -0
  9. package/.output/public/assets/api-keys-BurCFhzH.js +1 -0
  10. package/.output/public/assets/api-keys-api-BEDJdQg3.js +1 -0
  11. package/.output/public/assets/badge-Bf0fZ-Gx.js +1 -0
  12. package/.output/public/assets/chart-no-axes-combined-FaYMGmQS.js +1 -0
  13. package/.output/public/assets/createServerFn-gM2-LFBy.js +3 -0
  14. package/.output/public/assets/dialog-BS60BRr2.js +1 -0
  15. package/.output/public/assets/dist-DspNKfMj.js +1 -0
  16. package/.output/public/assets/download-O-yNBc9H.js +1 -0
  17. package/.output/public/assets/field-Buq148-A.js +1 -0
  18. package/.output/public/assets/index-kFhd2S78.js +10 -0
  19. package/.output/public/assets/installations-BrQ-od41.js +2 -0
  20. package/.output/public/assets/installations-CGlZ3ROz.js +1 -0
  21. package/.output/public/assets/lazyRouteComponent-rPzgA7S-.js +1 -0
  22. package/.output/public/assets/loader-circle-DCYF-wJl.js +1 -0
  23. package/.output/public/assets/preload-helper-TKOBYGYD.js +1 -0
  24. package/.output/public/assets/refresh-cw-n8XYbPPj.js +1 -0
  25. package/.output/public/assets/routes-CrdokJ80.js +1 -0
  26. package/.output/public/assets/routes-DcQnVajO.js +2 -0
  27. package/.output/public/assets/search-DLhbWw3W.js +1 -0
  28. package/.output/public/assets/shield-check-DE_hVuHu.js +1 -0
  29. package/.output/public/assets/signing-HRexfjsH.js +1 -0
  30. package/.output/public/assets/styles-eB4EtvFS.css +2 -0
  31. package/.output/public/assets/table-D8CLieHX.js +1 -0
  32. package/.output/public/assets/textarea-YJLTVAoq.js +1 -0
  33. package/.output/public/assets/useMutation-JsGVjQMY.js +1 -0
  34. package/.output/public/assets/useOpenInteractionType-4cRD5xO0.js +1 -0
  35. package/.output/server/__23tanstack-start-server-fn-resolver-DBP9TQ7K.mjs +146 -0
  36. package/.output/server/_libs/@base-ui/react+[...].mjs +16124 -0
  37. package/.output/server/_libs/@reduxjs/toolkit+[...].mjs +1554 -0
  38. package/.output/server/_libs/@tanstack/react-router+[...].mjs +41 -10
  39. package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +132 -0
  40. package/.output/server/_libs/d3-array.mjs +231 -0
  41. package/.output/server/_libs/d3-color.mjs +330 -0
  42. package/.output/server/_libs/d3-format.mjs +252 -0
  43. package/.output/server/_libs/d3-interpolate.mjs +207 -0
  44. package/.output/server/_libs/d3-path.mjs +77 -0
  45. package/.output/server/_libs/d3-scale+[...].mjs +1907 -0
  46. package/.output/server/_libs/d3-shape.mjs +818 -0
  47. package/.output/server/_libs/decimal.js-light.mjs +884 -0
  48. package/.output/server/_libs/es-toolkit.mjs +1134 -0
  49. package/.output/server/_libs/eventemitter3.mjs +236 -0
  50. package/.output/server/_libs/immer.mjs +532 -0
  51. package/.output/server/_libs/lucide-react.mjs +141 -13
  52. package/.output/server/_libs/next-themes.mjs +2 -2
  53. package/.output/server/_libs/react-redux+[...].mjs +205 -0
  54. package/.output/server/_libs/recharts+[...].mjs +14117 -0
  55. package/.output/server/_libs/sonner.mjs +2 -2
  56. package/.output/server/_libs/tanstack__query-core.mjs +114 -1
  57. package/.output/server/_libs/tanstack__react-query.mjs +3 -4
  58. package/.output/server/_runtime.mjs +10 -1
  59. package/.output/server/_ssr/AnalyticsCapabilityContext-ui4DX8GO.mjs +15 -0
  60. package/.output/server/_ssr/BundleActivityChart-BiB9ZORH.mjs +288 -0
  61. package/.output/server/_ssr/BundleIdDisplay-Czk6iR6p.mjs +13 -0
  62. package/.output/server/_ssr/alert-Dim3MGY5.mjs +86 -0
  63. package/.output/server/_ssr/analytics-DtMTn8fn.mjs +858 -0
  64. package/.output/server/_ssr/analytics-api-Cg1M5v_H.mjs +71 -0
  65. package/.output/server/_ssr/analytics-input-fRRRn0uA.mjs +96 -0
  66. package/.output/server/_ssr/analytics-rpc-DY5uXvHT.mjs +155 -0
  67. package/.output/server/_ssr/api-BTSv1tZb.mjs +282 -0
  68. package/.output/server/_ssr/api-keys-BnM_o2nS.mjs +300 -0
  69. package/.output/server/_ssr/api-keys-api-CIywsSF5.mjs +60 -0
  70. package/.output/server/_ssr/api-keys-rpc-BSp1wkIV.mjs +136 -0
  71. package/.output/server/_ssr/api-rpc-cG5wd_tO.mjs +1468 -0
  72. package/.output/server/_ssr/auth-BF8mbAgF.mjs +21 -0
  73. package/.output/server/_ssr/auth-rpc-BrJ6obHJ.mjs +24 -0
  74. package/.output/server/_ssr/auth.server-DwKoZoQM.mjs +50 -0
  75. package/.output/server/_ssr/badge-DCxbEeof.mjs +26 -0
  76. package/.output/server/_ssr/bundle-signing-rpc-COoQhW8E.mjs +16 -0
  77. package/.output/server/_ssr/bundle-signing.server-D5u8KkMp.mjs +51 -0
  78. package/.output/server/_ssr/config-Dv8qLDZ0.mjs +13 -0
  79. package/.output/server/_ssr/config.server-DEiLhKmy.mjs +361 -0
  80. package/.output/server/_ssr/{createServerFn-DzU0k62V.mjs → createServerFn-B2hcXoPM.mjs} +1 -1
  81. package/.output/server/_ssr/createServerRpc-DB29C_5p.mjs +12 -0
  82. package/.output/server/_ssr/{sidebar-Cv_H0_Jv.mjs → createSsrRpc-9P0S0lJ3.mjs} +68 -50
  83. package/.output/server/_ssr/databaseRows-CwbNwYSl.mjs +166 -0
  84. package/.output/server/_ssr/deleteBundle-Cu_XOMt0.mjs +66 -0
  85. package/.output/server/_ssr/dialog-CQuRMNZ4.mjs +170 -0
  86. package/.output/server/_ssr/dist-BVSLTpoP.mjs +250 -0
  87. package/.output/server/_ssr/downloadBundle-CNnYxYO2.mjs +24 -0
  88. package/.output/server/_ssr/{empty-plugin-adapters-B4HCzgmM.mjs → empty-plugin-adapters-B5QvGpEl.mjs} +1 -1
  89. package/.output/server/_ssr/field-DRIXKjZG.mjs +74 -0
  90. package/.output/server/_ssr/{getBundleChildren-CVtSiH5t.mjs → getBundleChildren-BcRMFoDF.mjs} +10 -18
  91. package/.output/server/_ssr/installations-B-azgAvX.mjs +19 -0
  92. package/.output/server/_ssr/installations-Ctvzp2JH.mjs +535 -0
  93. package/.output/server/_ssr/router-C-KUBfOi.mjs +513 -0
  94. package/.output/server/_ssr/routes-BM4ht9jb.mjs +2286 -0
  95. package/.output/server/_ssr/routes-Z7TwC6nC.mjs +37 -0
  96. package/.output/server/_ssr/runtime.server-awTLiiXW.mjs +527 -0
  97. package/.output/server/_ssr/server-BjzmvXnO.mjs +7468 -0
  98. package/.output/server/_ssr/signing-k8GxyBDF.mjs +202 -0
  99. package/.output/server/_ssr/ssr.mjs +4 -5740
  100. package/.output/server/_ssr/start-DpViQGnt.mjs +4 -0
  101. package/.output/server/_ssr/table-v1yCvYo7.mjs +53 -0
  102. package/.output/server/_ssr/textarea-DYHnskhb.mjs +14 -0
  103. package/.output/server/_ssr/uuidv7-BbJ-9D_I.mjs +31 -0
  104. package/.output/server/_tanstack-start-manifest_v-CH4StzJu.mjs +108 -0
  105. package/.output/server/index.mjs +249 -46
  106. package/README.md +29 -8
  107. package/lib/index.d.mts +36 -0
  108. package/lib/index.mjs +4 -0
  109. package/lib/vite-internal.mjs +127 -0
  110. package/lib/vite.d.mts +10 -0
  111. package/lib/vite.mjs +5 -0
  112. package/package.json +51 -29
  113. package/public/apple-touch-icon.png +0 -0
  114. package/public/favicon-16x16.png +0 -0
  115. package/public/favicon-32x32.png +0 -0
  116. package/public/favicon.ico +0 -0
  117. package/public/logo.svg +1 -0
  118. package/public/manifest.json +30 -0
  119. package/public/robots.txt +3 -0
  120. package/src/components/AppSidebar.tsx +159 -0
  121. package/src/components/BundleIdDisplay.tsx +18 -0
  122. package/src/components/ChannelBadge.tsx +31 -0
  123. package/src/components/ConsoleAccessPage.tsx +122 -0
  124. package/src/components/EnabledStatusIcon.tsx +54 -0
  125. package/src/components/HashValueDisplay.tsx +85 -0
  126. package/src/components/HotUpdaterLogo.tsx +30 -0
  127. package/src/components/NotFoundPage.tsx +33 -0
  128. package/src/components/PlatformIcon.tsx +39 -0
  129. package/src/components/RolloutPercentageBadge.tsx +28 -0
  130. package/src/components/ThemeProvider.tsx +116 -0
  131. package/src/components/TimestampDisplay.tsx +41 -0
  132. package/src/components/features/analytics/ActivityChart.tsx +150 -0
  133. package/src/components/features/analytics/AnalyticsCapabilityContext.tsx +24 -0
  134. package/src/components/features/analytics/AnalyticsControls.tsx +115 -0
  135. package/src/components/features/analytics/AnalyticsErrorAlert.tsx +21 -0
  136. package/src/components/features/analytics/AnalyticsOverview.tsx +208 -0
  137. package/src/components/features/analytics/BundleDistribution.tsx +133 -0
  138. package/src/components/features/analytics/BundleSelector.tsx +114 -0
  139. package/src/components/features/analytics/InstallationHistoryCard.tsx +248 -0
  140. package/src/components/features/analytics/InstallationMatchesCard.tsx +132 -0
  141. package/src/components/features/analytics/InstallationPageHeader.tsx +138 -0
  142. package/src/components/features/analytics/InstallationPagination.tsx +48 -0
  143. package/src/components/features/analytics/InstallationSearch.tsx +230 -0
  144. package/src/components/features/analytics/UpdateOutcomes.tsx +147 -0
  145. package/src/components/features/api-keys/ApiKeysPage.tsx +377 -0
  146. package/src/components/features/bundles/BundleActivityChart.tsx +193 -0
  147. package/src/components/features/bundles/BundleAnalyticsSummary.tsx +123 -0
  148. package/src/components/features/bundles/BundleChildrenPanel.tsx +201 -0
  149. package/src/components/features/bundles/BundleMetadata.tsx +136 -0
  150. package/src/components/features/bundles/RolloutCohortsDialog.tsx +201 -0
  151. package/src/components/features/channels/ChannelManagementDialog.tsx +228 -0
  152. package/src/components/features/releases/ReleaseEditorSheet.tsx +879 -0
  153. package/src/components/features/releases/ReleaseStateBadge.tsx +20 -0
  154. package/src/components/features/signing/BundleSigningPage.tsx +247 -0
  155. package/src/components/ui/alert-dialog.tsx +185 -0
  156. package/src/components/ui/alert.tsx +76 -0
  157. package/src/components/ui/badge.tsx +49 -0
  158. package/src/components/ui/button.tsx +58 -0
  159. package/src/components/ui/card.tsx +89 -0
  160. package/src/components/ui/chart.tsx +371 -0
  161. package/src/components/ui/combobox.tsx +303 -0
  162. package/src/components/ui/dialog.tsx +156 -0
  163. package/src/components/ui/dropdown-menu.tsx +270 -0
  164. package/src/components/ui/field.tsx +235 -0
  165. package/src/components/ui/input-group.tsx +147 -0
  166. package/src/components/ui/input.tsx +19 -0
  167. package/src/components/ui/label.tsx +25 -0
  168. package/src/components/ui/progress.tsx +84 -0
  169. package/src/components/ui/select.tsx +205 -0
  170. package/src/components/ui/separator.tsx +23 -0
  171. package/src/components/ui/sheet.tsx +132 -0
  172. package/src/components/ui/sidebar.tsx +779 -0
  173. package/src/components/ui/skeleton.tsx +13 -0
  174. package/src/components/ui/slider.tsx +56 -0
  175. package/src/components/ui/sonner.tsx +45 -0
  176. package/src/components/ui/switch.tsx +25 -0
  177. package/src/components/ui/table.tsx +114 -0
  178. package/src/components/ui/textarea.tsx +18 -0
  179. package/src/components/ui/toggle-group.tsx +89 -0
  180. package/src/components/ui/toggle.tsx +43 -0
  181. package/src/components/ui/tooltip.tsx +66 -0
  182. package/src/hooks/use-mobile.ts +30 -0
  183. package/src/index.ts +48 -0
  184. package/src/lib/analytics-api.ts +145 -0
  185. package/src/lib/analytics-error.ts +19 -0
  186. package/src/lib/analytics-input.ts +187 -0
  187. package/src/lib/analytics-overview.ts +170 -0
  188. package/src/lib/analytics-rpc.ts +218 -0
  189. package/src/lib/api-keys-api.ts +69 -0
  190. package/src/lib/api-keys-rpc.ts +87 -0
  191. package/src/lib/api-rpc.ts +478 -0
  192. package/src/lib/api.ts +444 -0
  193. package/src/lib/auth-rpc.ts +16 -0
  194. package/src/lib/bundle-signing-api.ts +14 -0
  195. package/src/lib/bundle-signing-rpc.ts +16 -0
  196. package/src/lib/constants.ts +1 -0
  197. package/src/lib/git.ts +51 -0
  198. package/src/lib/server/auth.server.ts +43 -0
  199. package/src/lib/server/bundle-signing.server.ts +100 -0
  200. package/src/lib/server/config.server.ts +109 -0
  201. package/src/lib/server/console-runtime.server.ts +70 -0
  202. package/src/lib/server/deleteBundle.ts +145 -0
  203. package/src/lib/server/downloadBundle.ts +46 -0
  204. package/src/lib/server/getBundleChildren.ts +130 -0
  205. package/src/lib/server/listReleases.ts +61 -0
  206. package/src/lib/server/releaseActivity.ts +27 -0
  207. package/src/lib/server/releaseReachability.ts +95 -0
  208. package/src/lib/server/runtime.server.ts +172 -0
  209. package/src/lib/utils.ts +9 -0
  210. package/src/logo.svg +1 -0
  211. package/src/routeTree.gen.ts +199 -0
  212. package/src/router.tsx +21 -0
  213. package/src/routes/-installations-search.ts +19 -0
  214. package/src/routes/-releases-search.ts +74 -0
  215. package/src/routes/__root.tsx +174 -0
  216. package/src/routes/analytics.tsx +139 -0
  217. package/src/routes/api/auth/$.ts +19 -0
  218. package/src/routes/api/bundles/$bundleId/download.ts +17 -0
  219. package/src/routes/api-keys.tsx +37 -0
  220. package/src/routes/index.tsx +982 -0
  221. package/src/routes/installations.tsx +194 -0
  222. package/src/routes/signing.tsx +37 -0
  223. package/src/styles.css +152 -0
  224. package/src/virtual-modules.d.ts +21 -0
  225. package/src/vite-env.d.ts +1 -0
  226. package/src/vite-internal.ts +195 -0
  227. package/src/vite.ts +12 -0
  228. package/.output/public/assets/dist-DJX53faP.js +0 -3
  229. package/.output/public/assets/index-Iylis5kY.js +0 -10
  230. package/.output/public/assets/routes-HT21hs0C.js +0 -54
  231. package/.output/public/assets/styles-C0Re4quB.css +0 -2
  232. package/.output/server/__23tanstack-start-server-fn-resolver-CgwgZ0-i.mjs +0 -66
  233. package/.output/server/_libs/@floating-ui/core+[...].mjs +0 -698
  234. package/.output/server/_libs/@floating-ui/dom+[...].mjs +0 -644
  235. package/.output/server/_libs/@floating-ui/react-dom+[...].mjs +0 -839
  236. package/.output/server/_libs/@radix-ui/react-alert-dialog+[...].mjs +0 -2138
  237. package/.output/server/_libs/@radix-ui/react-popper+[...].mjs +0 -308
  238. package/.output/server/_libs/@radix-ui/react-select+[...].mjs +0 -1038
  239. package/.output/server/_libs/@tanstack/devtools-event-client+[...].mjs +0 -196
  240. package/.output/server/_libs/@tanstack/form-core+[...].mjs +0 -3228
  241. package/.output/server/_libs/@tanstack/react-form+[...].mjs +0 -430
  242. package/.output/server/_libs/@tanstack/react-table+[...].mjs +0 -2382
  243. package/.output/server/_libs/radix-ui__number.mjs +0 -6
  244. package/.output/server/_libs/radix-ui__primitive.mjs +0 -9
  245. package/.output/server/_libs/radix-ui__react-arrow.mjs +0 -23
  246. package/.output/server/_libs/radix-ui__react-collection.mjs +0 -78
  247. package/.output/server/_libs/radix-ui__react-direction.mjs +0 -13
  248. package/.output/server/_libs/radix-ui__react-label.mjs +0 -22
  249. package/.output/server/_libs/radix-ui__react-separator.mjs +0 -30
  250. package/.output/server/_libs/radix-ui__react-slider.mjs +0 -510
  251. package/.output/server/_libs/radix-ui__react-switch.mjs +0 -157
  252. package/.output/server/_libs/radix-ui__react-tooltip.mjs +0 -493
  253. package/.output/server/_ssr/api-rpc-BIbDUKSz.mjs +0 -292
  254. package/.output/server/_ssr/config.server-BSS366KT.mjs +0 -41
  255. package/.output/server/_ssr/deleteBundle-BgbZTtpW.mjs +0 -141
  256. package/.output/server/_ssr/dist-KboydRVk.mjs +0 -120
  257. package/.output/server/_ssr/router-C3hqEy3k.mjs +0 -307
  258. package/.output/server/_ssr/routes-B0lQvvlC.mjs +0 -3048
  259. package/.output/server/_ssr/start-02sLteYG.mjs +0 -4
  260. package/.output/server/_ssr/storageProfile-FVkjPoGb.mjs +0 -9
  261. package/.output/server/_tanstack-start-manifest_v-Bpx52WOU.mjs +0 -20
  262. package/.output/server/node_modules/tslib/modules/index.js +0 -70
  263. package/.output/server/node_modules/tslib/modules/package.json +0 -3
  264. package/.output/server/node_modules/tslib/package.json +0 -47
  265. package/.output/server/node_modules/tslib/tslib.js +0 -484
  266. package/.output/server/package.json +0 -9
@@ -0,0 +1,858 @@
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 };