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

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 (285) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/ConsoleFeatureUnavailable-D0LPL1Gy.js +1 -0
  3. package/.output/public/assets/HashValueDisplay-BmuB4FW-.js +1 -0
  4. package/.output/public/assets/InsightsInfo--v4SC07c.js +1 -0
  5. package/.output/public/assets/InsightsPageHeader-dU83CAQY.js +1 -0
  6. package/.output/public/assets/NotFoundPage-B5mhGG2a.js +1 -0
  7. package/.output/public/assets/api-D-vOVlQ4.js +1 -0
  8. package/.output/public/assets/api-keys-Csh8ARKU.js +1 -0
  9. package/.output/public/assets/api-keys-OMPUoA_A.js +1 -0
  10. package/.output/public/assets/arrow-left-Bwq9X1eS.js +1 -0
  11. package/.output/public/assets/arrow-up-right-BkOVn4if.js +1 -0
  12. package/.output/public/assets/card-C_7d6O9a.js +1 -0
  13. package/.output/public/assets/console-features-api-C1jRZTGw.js +1 -0
  14. package/.output/public/assets/dialog-DE8wFN_P.js +1 -0
  15. package/.output/public/assets/dist-C7ENGzpS.js +1 -0
  16. package/.output/public/assets/empty-CVd5NlQD.js +1 -0
  17. package/.output/public/assets/field-C06q1iC9.js +1 -0
  18. package/.output/public/assets/index-BeWlQtO6.js +11 -0
  19. package/.output/public/assets/input-group-Bi61biIA.js +1 -0
  20. package/.output/public/assets/insights-BjnAtC0f.js +2 -0
  21. package/.output/public/assets/insights-D86m11yH.js +42 -0
  22. package/.output/public/assets/insights-DpYstMoY.js +1 -0
  23. package/.output/public/assets/insights-recovery-rpc-BIm5_ALc.js +1 -0
  24. package/.output/public/assets/insights-search-KIF7mRtB.js +1 -0
  25. package/.output/public/assets/insights-usage-rpc-VHj8vyBt.js +1 -0
  26. package/.output/public/assets/insights_.distribution-ADcrX48c.js +2 -0
  27. package/.output/public/assets/insights_.distribution-CuuJUN6Y.js +1 -0
  28. package/.output/public/assets/insights_.distribution-DJRjuXsO.js +1 -0
  29. package/.output/public/assets/installations-BSBBzqWs.js +2 -0
  30. package/.output/public/assets/installations-DqIE6WcO.js +1 -0
  31. package/.output/public/assets/installations-UsELbDvw.js +1 -0
  32. package/.output/public/assets/loader-circle-CnCBxzHp.js +1 -0
  33. package/.output/public/assets/mutation-Csp7xUNh.js +1 -0
  34. package/.output/public/assets/preload-helper-B-cOmpLH.js +1 -0
  35. package/.output/public/assets/refresh-cw-FF1NtzlQ.js +1 -0
  36. package/.output/public/assets/routes-CoN9XHP_.js +2 -0
  37. package/.output/public/assets/routes-DT1jB0Zi.js +1 -0
  38. package/.output/public/assets/sidebar-D8mJRF15.js +3 -0
  39. package/.output/public/assets/styles-Bwt7v6U5.css +2 -0
  40. package/.output/public/assets/table-WBgfvgXg.js +1 -0
  41. package/.output/public/assets/useCompositeListItem-CloBmDwn.js +1 -0
  42. package/.output/public/assets/useMutation-DO8cvsTx.js +1 -0
  43. package/.output/public/assets/useOpenInteractionType-ReGr9GzJ.js +1 -0
  44. package/.output/public/assets/valueToPercent-BvPAi4bP.js +1 -0
  45. package/.output/server/{__23tanstack-start-server-fn-resolver-B7ioJhtE.mjs → __23tanstack-start-server-fn-resolver-DMRD4e4f.mjs} +51 -47
  46. package/.output/server/_libs/@base-ui/react+[...].mjs +7044 -3622
  47. package/.output/server/_libs/@reduxjs/toolkit+[...].mjs +1554 -0
  48. package/.output/server/_libs/@tanstack/react-router+[...].mjs +3803 -4023
  49. package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +141 -98
  50. package/.output/server/_libs/d3-array.mjs +231 -0
  51. package/.output/server/_libs/d3-color.mjs +330 -0
  52. package/.output/server/_libs/d3-format.mjs +252 -0
  53. package/.output/server/_libs/d3-interpolate.mjs +207 -0
  54. package/.output/server/_libs/d3-path.mjs +77 -0
  55. package/.output/server/_libs/d3-scale+[...].mjs +1907 -0
  56. package/.output/server/_libs/d3-shape.mjs +818 -0
  57. package/.output/server/_libs/decimal.js-light.mjs +884 -0
  58. package/.output/server/_libs/es-toolkit.mjs +1159 -0
  59. package/.output/server/_libs/eventemitter3.mjs +236 -0
  60. package/.output/server/_libs/immer.mjs +532 -0
  61. package/.output/server/_libs/lucide-react.mjs +46 -32
  62. package/.output/server/_libs/next-themes.mjs +2 -2
  63. package/.output/server/_libs/react-redux+[...].mjs +205 -0
  64. package/.output/server/_libs/recharts+[...].mjs +14350 -0
  65. package/.output/server/_libs/sonner.mjs +2 -2
  66. package/.output/server/_libs/tanstack__history.mjs +68 -41
  67. package/.output/server/_libs/tanstack__query-core.mjs +1618 -309
  68. package/.output/server/_libs/tanstack__react-query.mjs +262 -22
  69. package/.output/server/_libs/verkit.mjs +99 -1
  70. package/.output/server/_runtime.mjs +10 -1
  71. package/.output/server/_ssr/ConsoleFeatureUnavailable-Du7jNPNS.mjs +58 -0
  72. package/.output/server/_ssr/{HashValueDisplay-DwpGLfrl.mjs → HashValueDisplay-GQvFkXoi.mjs} +3 -3
  73. package/.output/server/_ssr/InsightsInfo-Do7QSaFR.mjs +37 -0
  74. package/.output/server/_ssr/InsightsPageHeader-DSUWUWRc.mjs +154 -0
  75. package/.output/server/_ssr/NotFoundPage-DsF4Vrhg.mjs +37 -0
  76. package/.output/server/_ssr/api-BEiVs0H7.mjs +203 -0
  77. package/.output/server/_ssr/api-keys-BrvBWGRf.mjs +5 -0
  78. package/.output/server/_ssr/{api-keys-CZeTPQys.mjs → api-keys-K5v32nf7.mjs} +49 -9
  79. package/.output/server/_ssr/api-keys-rpc-B96bBllm.mjs +52 -0
  80. package/.output/server/_ssr/api-rpc-DcUe280B.mjs +296 -0
  81. package/.output/server/_ssr/{auth-rpc-DUaeOWKK.mjs → auth-rpc-C68W56DP.mjs} +6 -6
  82. package/.output/server/_ssr/auth.server-DM1J6NwR.mjs +5712 -0
  83. package/.output/server/_ssr/bundleActivity-CpuY9CTP.mjs +19 -0
  84. package/.output/server/_ssr/card-CPAEKx0w.mjs +49 -0
  85. package/.output/server/_ssr/config-nsIcsHh8.mjs +15 -0
  86. package/.output/server/_ssr/config.server-DDCZk3Sq.mjs +36 -0
  87. package/.output/server/_ssr/console-access-Da-2Qd7q.mjs +17 -0
  88. package/.output/server/_ssr/console-features-Dzu2iUGD.mjs +55 -0
  89. package/.output/server/_ssr/console-features-api-xAsSgFa_.mjs +43 -0
  90. package/.output/server/_ssr/console-features-rpc-qARGpB24.mjs +19 -0
  91. package/.output/server/_ssr/{createServerFn-B2hcXoPM.mjs → createMiddleware-BdBf_ezN.mjs} +67 -56
  92. package/.output/server/_ssr/{createServerRpc-DB29C_5p.mjs → createServerRpc-B_y2lrwA.mjs} +2 -2
  93. package/.output/server/_ssr/{databaseJsonValue-d6I3nAS4.mjs → databaseJsonValue-Bhxciy7Z.mjs} +28 -5
  94. package/.output/server/_ssr/databaseRows-DwMAjaF8.mjs +74 -0
  95. package/.output/server/_ssr/{dialog-DS-nw-pA.mjs → dialog-q__BJ7mQ.mjs} +2 -2
  96. package/.output/server/_ssr/{dist-CyzPaI5v.mjs → dist-Cxo9ghYv.mjs} +140 -6
  97. package/.output/server/_ssr/{downloadBundle-CNnYxYO2.mjs → downloadBundle-BW9I-pMQ.mjs} +10 -10
  98. package/.output/server/_ssr/empty-DcBTAooP.mjs +49 -0
  99. package/.output/server/_ssr/{empty-plugin-adapters-B5QvGpEl.mjs → empty-plugin-adapters-CYdinE-i.mjs} +1 -1
  100. package/.output/server/_ssr/{table-CnZmon_Z.mjs → field-QCDBVpXA.mjs} +63 -51
  101. package/.output/server/_ssr/getBundleChildren-DIAGCF4a.mjs +22 -0
  102. package/.output/server/_ssr/{input-group-BpsnmvZw.mjs → input-group-hCk2nUoU.mjs} +2 -2
  103. package/.output/server/_ssr/insights-BfBO0RFr.mjs +1238 -0
  104. package/.output/server/_ssr/insights-DsAU3-TB.mjs +14 -0
  105. package/.output/server/_ssr/insights-RIfg447W.mjs +5 -0
  106. package/.output/server/_ssr/insights-api-CDn-QQHU.mjs +105 -0
  107. package/.output/server/_ssr/insights-failures-BBGMhuUr.mjs +44 -0
  108. package/.output/server/_ssr/insights-failures-rpc-rA2dctK2.mjs +21 -0
  109. package/.output/server/_ssr/insights-recovery-Dpk6Mfb9.mjs +28 -0
  110. package/.output/server/_ssr/insights-recovery-rpc-BCaF-xSd.mjs +46 -0
  111. package/.output/server/_ssr/insights-recovery-rpc-CcgYyUHk.mjs +20 -0
  112. package/.output/server/_ssr/{insights-rpc-38DujK0G.mjs → insights-rpc-keycmwzw.mjs} +22 -14
  113. package/.output/server/_ssr/insights-search-BRg5VsNe.mjs +25 -0
  114. package/.output/server/_ssr/insights-usage-cbw9n2Fq.mjs +29 -0
  115. package/.output/server/_ssr/insights-usage-rpc-BKgV1QDf.mjs +29 -0
  116. package/.output/server/_ssr/insights-usage-rpc-D-JYachy.mjs +41 -0
  117. package/.output/server/_ssr/insightsRecovery-B7oMc8d8.mjs +45 -0
  118. package/.output/server/_ssr/insightsUsage-Oa7kk5ek.mjs +56 -0
  119. package/.output/server/_ssr/insights_.distribution-BIqOqRW0.mjs +335 -0
  120. package/.output/server/_ssr/insights_.distribution-CuUrDa7P.mjs +5 -0
  121. package/.output/server/_ssr/insights_.distribution-RT_6x_ud.mjs +14 -0
  122. package/.output/server/_ssr/{installations-DiVKEF5T.mjs → installations-BO5CjX2z.mjs} +517 -54
  123. package/.output/server/_ssr/installations-BjaHiIfh.mjs +5 -0
  124. package/.output/server/_ssr/installations-Dt5luA-N.mjs +69 -0
  125. package/.output/server/_ssr/listReleases-BitHzOYB.mjs +79 -0
  126. package/.output/server/_ssr/releaseManagement-B3e7I8J9.mjs +11 -0
  127. package/.output/server/_ssr/{router-B9wG_9f8.mjs → router-DfVAixxe.mjs} +135 -139
  128. package/.output/server/_ssr/{routes-D7Yh_jfj.mjs → routes-C6LstNp8.mjs} +176 -455
  129. package/.output/server/_ssr/routes-Q3Uhmwwz.mjs +292 -0
  130. package/.output/server/_ssr/runtime.server-fL0o6I2b.mjs +1459 -0
  131. package/.output/server/_ssr/schema-BKY9SqaY.mjs +25 -0
  132. package/.output/server/_ssr/select-CUlInmjk.mjs +107 -0
  133. package/.output/server/_ssr/server-C43wq9ft.mjs +8330 -0
  134. package/.output/server/_ssr/{createSsrRpc-DjWNJ0uc.mjs → sidebar-DKbx2ach.mjs} +117 -63
  135. package/.output/server/_ssr/ssr.mjs +1 -1
  136. package/.output/server/_ssr/start-BXUOv_n8.mjs +4 -0
  137. package/.output/server/_ssr/table-NGWV8B-S.mjs +53 -0
  138. package/.output/server/_ssr/updateFailures-Jwe2Baab.mjs +24 -0
  139. package/.output/server/_ssr/{uuidv7-D0QFkPL_.mjs → uuidv7-C8bwiZr8.mjs} +2 -2
  140. package/.output/server/_tanstack-start-manifest_v-WQ5qGn8n.mjs +124 -0
  141. package/.output/server/index.mjs +267 -162
  142. package/LICENSE +13 -14
  143. package/README.md +82 -72
  144. package/lib/index.d.mts +23 -11
  145. package/lib/vite-internal.mjs +13 -3
  146. package/lib/vite.d.mts +2 -0
  147. package/package.json +14 -14
  148. package/src/components/AppSidebar.tsx +100 -71
  149. package/src/components/ConsoleAccessPage.tsx +5 -1
  150. package/src/components/ConsoleFeatureUnavailable.tsx +80 -0
  151. package/src/components/NotFoundPage.tsx +7 -7
  152. package/src/components/features/FeatureSlots.tsx +99 -0
  153. package/src/components/features/api-keys/apiKeysFeatureUi.tsx +15 -0
  154. package/src/components/features/bundles/BundleMetadata.tsx +8 -5
  155. package/src/components/features/bundles/RolloutCohortsDialog.tsx +1 -1
  156. package/src/components/features/channels/ChannelManagementDialog.tsx +2 -5
  157. package/src/components/features/insights/AppUsage.tsx +370 -0
  158. package/src/components/features/insights/EstimatedCount.tsx +18 -0
  159. package/src/components/features/insights/EventDetails.tsx +117 -37
  160. package/src/components/features/insights/EventHistoryCard.tsx +112 -26
  161. package/src/components/features/insights/EventHistoryList.tsx +2 -2
  162. package/src/components/features/insights/InsightsControls.tsx +265 -102
  163. package/src/components/features/insights/InsightsErrorAlert.tsx +14 -1
  164. package/src/components/features/insights/InsightsInfo.tsx +48 -0
  165. package/src/components/features/insights/InsightsOverview.tsx +176 -173
  166. package/src/components/features/insights/InsightsPageHeader.tsx +35 -20
  167. package/src/components/features/insights/InsightsPeriodSelector.tsx +89 -0
  168. package/src/components/features/insights/InstallationHistoryCard.tsx +52 -10
  169. package/src/components/features/insights/InstallationMatchesCard.tsx +1 -1
  170. package/src/components/features/insights/ReleaseActivity.tsx +188 -0
  171. package/src/components/features/insights/ReportingDevicesSummary.tsx +65 -0
  172. package/src/components/features/insights/UpdateFailures.tsx +319 -0
  173. package/src/components/features/insights/insightsFeatureUi.tsx +35 -0
  174. package/src/components/features/releases/ReleaseEditorSheet.tsx +30 -7
  175. package/src/components/ui/card.tsx +1 -1
  176. package/src/components/ui/empty.tsx +101 -0
  177. package/src/components/ui/scroll-area.tsx +52 -0
  178. package/src/components/ui/sidebar.tsx +22 -29
  179. package/src/components/ui/tabs.tsx +81 -0
  180. package/src/index.ts +26 -19
  181. package/src/lib/api-keys-api.ts +0 -28
  182. package/src/lib/api-keys-rpc.ts +27 -38
  183. package/src/lib/api-rpc.ts +189 -266
  184. package/src/lib/api.ts +19 -102
  185. package/src/lib/bundle-activity.ts +31 -0
  186. package/src/lib/console-access.ts +20 -0
  187. package/src/lib/console-features-api.ts +68 -0
  188. package/src/lib/console-features-rpc.ts +13 -0
  189. package/src/lib/console-features.ts +91 -0
  190. package/src/lib/insights-api.ts +37 -1
  191. package/src/lib/insights-failures-rpc.ts +21 -0
  192. package/src/lib/insights-failures.ts +129 -0
  193. package/src/lib/insights-recovery-rpc.ts +51 -0
  194. package/src/lib/insights-recovery.ts +68 -0
  195. package/src/lib/insights-retention.ts +21 -0
  196. package/src/lib/insights-rpc.ts +30 -12
  197. package/src/lib/insights-search.ts +58 -0
  198. package/src/lib/insights-usage-rpc.ts +29 -0
  199. package/src/lib/insights-usage.ts +74 -0
  200. package/src/lib/insights-view.ts +38 -2
  201. package/src/lib/scroll-restoration.ts +21 -0
  202. package/src/lib/server/adminInsights.ts +124 -0
  203. package/src/lib/server/auth.server.ts +13 -5
  204. package/src/lib/server/bundleActivity.ts +37 -0
  205. package/src/lib/server/config.server.ts +21 -74
  206. package/src/lib/server/console-feature-apis.ts +59 -0
  207. package/src/lib/server/console-runtime.server.ts +82 -38
  208. package/src/lib/server/downloadBundle.ts +16 -15
  209. package/src/lib/server/getBundleChildren.ts +31 -122
  210. package/src/lib/server/insightsRecovery.ts +55 -0
  211. package/src/lib/server/insightsUsage.ts +63 -0
  212. package/src/lib/server/listReleases.ts +109 -44
  213. package/src/lib/server/releaseReachability.ts +8 -7
  214. package/src/lib/server/runtime.server.ts +120 -31
  215. package/src/lib/server/updateFailures.ts +31 -0
  216. package/src/routeTree.gen.ts +44 -44
  217. package/src/router.tsx +3 -2
  218. package/src/routes/-installations-search.ts +32 -8
  219. package/src/routes/-releases-search.ts +76 -29
  220. package/src/routes/__root.tsx +10 -4
  221. package/src/routes/api/bundles/$bundleId/download.ts +2 -5
  222. package/src/routes/api-keys.tsx +5 -2
  223. package/src/routes/index.tsx +167 -104
  224. package/src/routes/insights.tsx +100 -59
  225. package/src/routes/insights_.distribution.tsx +398 -0
  226. package/src/routes/installations.tsx +98 -41
  227. package/src/vite-internal.ts +15 -3
  228. package/src/vite.ts +2 -0
  229. package/.output/public/assets/HashValueDisplay-CB0Eskit.js +0 -1
  230. package/.output/public/assets/InsightsPageHeader-Car5LNiZ.js +0 -1
  231. package/.output/public/assets/api-keys-BzKocqRy.js +0 -1
  232. package/.output/public/assets/api-keys-api-BHcODB7q.js +0 -1
  233. package/.output/public/assets/card-Dhr_eFYj.js +0 -1
  234. package/.output/public/assets/chart-no-axes-combined-Dy4WJv2U.js +0 -1
  235. package/.output/public/assets/composite-Bp98A0Ld.js +0 -1
  236. package/.output/public/assets/createServerFn-CA8fs7QD.js +0 -3
  237. package/.output/public/assets/dialog-CUTrcPlB.js +0 -1
  238. package/.output/public/assets/dist-C_NydL0e.js +0 -1
  239. package/.output/public/assets/index-DdAz-CXv.js +0 -10
  240. package/.output/public/assets/input-group-cSOG4wh_.js +0 -1
  241. package/.output/public/assets/insights-C0PBSZ-n.js +0 -1
  242. package/.output/public/assets/installations-CE33wgbY.js +0 -2
  243. package/.output/public/assets/installations-CStZja8x.js +0 -1
  244. package/.output/public/assets/lazyRouteComponent-Ccp3bwBY.js +0 -1
  245. package/.output/public/assets/link-CI8idAUi.js +0 -1
  246. package/.output/public/assets/loader-circle-BoxLrsOu.js +0 -1
  247. package/.output/public/assets/preload-helper-CQ36nxok.js +0 -1
  248. package/.output/public/assets/refresh-cw-Bqc72UZG.js +0 -1
  249. package/.output/public/assets/routes-DGVXdzpw.js +0 -2
  250. package/.output/public/assets/routes-DTlwOr8I.js +0 -1
  251. package/.output/public/assets/shield-check-uK3MHWJZ.js +0 -1
  252. package/.output/public/assets/signing-CMf4aqDO.js +0 -1
  253. package/.output/public/assets/styles-BCvbtKsm.css +0 -2
  254. package/.output/public/assets/table-2vcV89QB.js +0 -1
  255. package/.output/public/assets/useCompositeListItem-CHLITE_4.js +0 -1
  256. package/.output/public/assets/x-DCd0j8c6.js +0 -1
  257. package/.output/server/_ssr/InsightsPageHeader-Df4Brj8u.mjs +0 -484
  258. package/.output/server/_ssr/api-keys-api-BUPQVSpk.mjs +0 -60
  259. package/.output/server/_ssr/api-keys-rpc-Cs7tS5_j.mjs +0 -136
  260. package/.output/server/_ssr/api-rpc-tAupfGQm.mjs +0 -1399
  261. package/.output/server/_ssr/auth.server-DW71WzsD.mjs +0 -47
  262. package/.output/server/_ssr/bundle-signing-rpc-BhnBw0-d.mjs +0 -16
  263. package/.output/server/_ssr/bundle-signing.server-C4NW5mWx.mjs +0 -12
  264. package/.output/server/_ssr/card-BRiPmi0C.mjs +0 -108
  265. package/.output/server/_ssr/config-Dv8qLDZ0.mjs +0 -13
  266. package/.output/server/_ssr/config.server-afDuAwaG.mjs +0 -368
  267. package/.output/server/_ssr/databaseRows-5bCRTxm-.mjs +0 -126
  268. package/.output/server/_ssr/deleteBundle-BTGwBgzx.mjs +0 -66
  269. package/.output/server/_ssr/getBundleChildren-C0vh4ZvE.mjs +0 -50
  270. package/.output/server/_ssr/insights-BdS364Xu.mjs +0 -460
  271. package/.output/server/_ssr/insightsContract-fjJDBeEE.mjs +0 -303
  272. package/.output/server/_ssr/installations-CtA59MQG.mjs +0 -29
  273. package/.output/server/_ssr/routes-CKrrS2q2.mjs +0 -37
  274. package/.output/server/_ssr/runtime.server-DacU1TBV.mjs +0 -438
  275. package/.output/server/_ssr/server-D_C2rMzf.mjs +0 -7468
  276. package/.output/server/_ssr/signing-Dz1WkZOf.mjs +0 -133
  277. package/.output/server/_ssr/start-DpViQGnt.mjs +0 -4
  278. package/.output/server/_tanstack-start-manifest_v-2MjTyxil.mjs +0 -97
  279. package/src/components/features/insights/InsightsBundleReports.tsx +0 -51
  280. package/src/components/features/signing/BundleSigningPage.tsx +0 -139
  281. package/src/lib/bundle-signing-api.ts +0 -14
  282. package/src/lib/bundle-signing-rpc.ts +0 -16
  283. package/src/lib/server/bundle-signing.server.ts +0 -23
  284. package/src/lib/server/deleteBundle.ts +0 -145
  285. package/src/routes/signing.tsx +0 -37
@@ -0,0 +1,1238 @@
1
+ import { i as __toESM } from "../_runtime.mjs";
2
+ import { C as ScrollAreaRoot, S as ScrollAreaViewport, _ as ComboboxInput$1, b as ScrollAreaThumb, c as ComboboxItemIndicator, d as ComboboxItem$1, f as ComboboxPopup, g as ComboboxClear$1, h as ComboboxList$1, l as ComboboxRoot, m as ComboboxPortal, mt as require_react, p as ComboboxPositioner, pt as require_jsx_runtime, u as ComboboxEmpty$1, v as ComboboxTrigger$1, x as ScrollAreaScrollbar, y as ScrollAreaCorner } from "../_libs/@base-ui/react+[...].mjs";
3
+ import { F as Check, M as ChevronRight, P as ChevronDown, R as ArrowRight, b as List, l as RotateCw, t as X } from "../_libs/lucide-react.mjs";
4
+ import { D as buttonVariants, O as cn, S as Skeleton, n as Input, r as Separator$1, t as Button$1 } from "./sidebar-DKbx2ach.mjs";
5
+ import { h as Link } from "../_libs/@tanstack/react-router+[...].mjs";
6
+ import { r as useQuery } from "../_libs/tanstack__react-query.mjs";
7
+ import { a as EmptyTitle, i as EmptyHeader, r as EmptyDescription, t as Empty } from "./empty-DcBTAooP.mjs";
8
+ import { a as CardHeader, i as CardFooter, n as CardContent, o as CardTitle, r as CardDescription, t as Card } from "./card-CPAEKx0w.mjs";
9
+ import { a as Badge, i as AlertTitle, l as FieldGroup, o as Field, r as AlertDescription, t as Alert, u as FieldLabel } from "./field-QCDBVpXA.mjs";
10
+ import { a as TableHeader, i as TableHead, n as TableBody, o as TableRow, r as TableCell, t as Table } from "./table-NGWV8B-S.mjs";
11
+ import { a as failureReasonLabel, c as patchFallbackRate, i as failureRate, o as failureStageLabel, r as failureDetailParts, s as formatRate, t as checkFailureRate } from "./insights-failures-BBGMhuUr.mjs";
12
+ import { n as usageMetrics } from "./insights-usage-cbw9n2Fq.mjs";
13
+ import { n as getRecoveryReportRpc } from "./insights-recovery-rpc-CcgYyUHk.mjs";
14
+ import { c as useInsightsRetention, l as useUpdateFailuresQuery, n as formatDays, r as keepsTwelveMonths, t as DEFAULT_INSIGHTS_RETENTION } from "./insights-api-CDn-QQHU.mjs";
15
+ import { t as InsightsInfo } from "./InsightsInfo-Do7QSaFR.mjs";
16
+ import { t as Route } from "./insights-DsAU3-TB.mjs";
17
+ import { i as useChannelsQuery } from "./api-BEiVs0H7.mjs";
18
+ import { a as SelectItem, i as SelectGroup, n as Select$1, o as SelectTrigger, r as SelectContent, s as SelectValue, t as PlatformIcon } from "./select-CUlInmjk.mjs";
19
+ import { i as InputGroupInput, n as InputGroupAddon, r as InputGroupButton, t as InputGroup } from "./input-group-hCk2nUoU.mjs";
20
+ import { n as getAppUsageReportRpc, t as Progress$1 } from "./insights-usage-rpc-D-JYachy.mjs";
21
+ import { a as UsagePeriodSelector, n as InsightsPageHeader, r as InsightsPeriodSelector, t as InsightsErrorAlert } from "./InsightsPageHeader-DSUWUWRc.mjs";
22
+ import { a as CartesianGrid, c as ResponsiveContainer, i as Line, n as YAxis, o as Tooltip, r as XAxis, t as LineChart } from "../_libs/recharts+[...].mjs";
23
+ //#region node_modules/.nitro/vite/services/ssr/assets/insights-BfBO0RFr.js
24
+ var import_react = /* @__PURE__ */ __toESM(require_react());
25
+ var import_jsx_runtime = require_jsx_runtime();
26
+ var THEMES = {
27
+ light: "",
28
+ dark: ".dark"
29
+ };
30
+ var INITIAL_DIMENSION = {
31
+ width: 320,
32
+ height: 200
33
+ };
34
+ var ChartContext = import_react.createContext(null);
35
+ function useChart() {
36
+ const context = import_react.useContext(ChartContext);
37
+ if (!context) throw new Error("useChart must be used within a <ChartContainer />");
38
+ return context;
39
+ }
40
+ function ChartContainer({ id, className, children, config, initialDimension = INITIAL_DIMENSION, ...props }) {
41
+ const uniqueId = import_react.useId();
42
+ const chartId = `chart-${id ?? uniqueId.replace(/:/g, "")}`;
43
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartContext.Provider, {
44
+ value: { config },
45
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
46
+ "data-slot": "chart",
47
+ "data-chart": chartId,
48
+ className: cn("flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden", className),
49
+ ...props,
50
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartStyle, {
51
+ id: chartId,
52
+ config
53
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ResponsiveContainer, {
54
+ initialDimension,
55
+ children
56
+ })]
57
+ })
58
+ });
59
+ }
60
+ var ChartStyle = ({ id, config }) => {
61
+ const colorConfig = Object.entries(config).filter(([, config]) => config.theme ?? config.color);
62
+ if (!colorConfig.length) return null;
63
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { dangerouslySetInnerHTML: { __html: Object.entries(THEMES).map(([theme, prefix]) => `
64
+ ${prefix} [data-chart=${id}] {
65
+ ${colorConfig.map(([key, itemConfig]) => {
66
+ const color = itemConfig.theme?.[theme] ?? itemConfig.color;
67
+ return color ? ` --color-${key}: ${color};` : null;
68
+ }).join("\n")}
69
+ }
70
+ `).join("\n") } });
71
+ };
72
+ var ChartTooltip = Tooltip;
73
+ function ChartTooltipContent({ active, payload, className, indicator = "dot", hideLabel = false, hideIndicator = false, label, labelFormatter, labelClassName, formatter, color, nameKey, labelKey }) {
74
+ const { config } = useChart();
75
+ const tooltipLabel = import_react.useMemo(() => {
76
+ if (hideLabel || !payload?.length) return null;
77
+ const [item] = payload;
78
+ const itemConfig = getPayloadConfigFromPayload(config, item, `${labelKey ?? item?.dataKey ?? item?.name ?? "value"}`);
79
+ const value = !labelKey && typeof label === "string" ? config[label]?.label ?? label : itemConfig?.label;
80
+ if (labelFormatter) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
81
+ className: cn("font-medium", labelClassName),
82
+ children: labelFormatter(value, payload)
83
+ });
84
+ if (!value) return null;
85
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
86
+ className: cn("font-medium", labelClassName),
87
+ children: value
88
+ });
89
+ }, [
90
+ label,
91
+ labelFormatter,
92
+ payload,
93
+ hideLabel,
94
+ labelClassName,
95
+ config,
96
+ labelKey
97
+ ]);
98
+ if (!active || !payload?.length) return null;
99
+ const nestLabel = payload.length === 1 && indicator !== "dot";
100
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
101
+ className: cn("grid min-w-32 items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs/relaxed shadow-xl", className),
102
+ children: [!nestLabel ? tooltipLabel : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
103
+ className: "grid gap-1.5",
104
+ children: payload.filter((item) => item.type !== "none").map((item, index) => {
105
+ const itemConfig = getPayloadConfigFromPayload(config, item, `${nameKey ?? item.name ?? item.dataKey ?? "value"}`);
106
+ const indicatorColor = color ?? item.payload?.fill ?? item.color;
107
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
108
+ className: cn("flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground", indicator === "dot" && "items-center"),
109
+ children: formatter && item?.value !== void 0 && item.name ? formatter(item.value, item.name, item, index, item.payload) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [itemConfig?.icon ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(itemConfig.icon, {}) : !hideIndicator && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
110
+ className: cn("shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)", {
111
+ "h-2.5 w-2.5": indicator === "dot",
112
+ "w-1": indicator === "line",
113
+ "w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
114
+ "my-0.5": nestLabel && indicator === "dashed"
115
+ }),
116
+ style: {
117
+ "--color-bg": indicatorColor,
118
+ "--color-border": indicatorColor
119
+ }
120
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
121
+ className: cn("flex flex-1 justify-between gap-2 leading-none", nestLabel ? "items-end" : "items-center"),
122
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
123
+ className: "grid gap-1.5",
124
+ children: [nestLabel ? tooltipLabel : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
125
+ className: "text-muted-foreground",
126
+ children: itemConfig?.label ?? item.name
127
+ })]
128
+ }), item.value != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
129
+ className: "font-mono font-medium text-foreground tabular-nums",
130
+ children: typeof item.value === "number" ? item.value.toLocaleString() : String(item.value)
131
+ })]
132
+ })] })
133
+ }, index);
134
+ })
135
+ })]
136
+ });
137
+ }
138
+ function getPayloadConfigFromPayload(config, payload, key) {
139
+ if (typeof payload !== "object" || payload === null) return;
140
+ const payloadPayload = "payload" in payload && typeof payload.payload === "object" && payload.payload !== null ? payload.payload : void 0;
141
+ let configLabelKey = key;
142
+ if (key in payload && typeof payload[key] === "string") configLabelKey = payload[key];
143
+ else if (payloadPayload && key in payloadPayload && typeof payloadPayload[key] === "string") configLabelKey = payloadPayload[key];
144
+ return configLabelKey in config ? config[configLabelKey] : config[key];
145
+ }
146
+ function ScrollArea$1({ className, children, ...props }) {
147
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ScrollAreaRoot, {
148
+ "data-slot": "scroll-area",
149
+ className: cn("relative", className),
150
+ ...props,
151
+ children: [
152
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollAreaViewport, {
153
+ "data-slot": "scroll-area-viewport",
154
+ className: "size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1",
155
+ children
156
+ }),
157
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollBar, {}),
158
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollAreaCorner, {})
159
+ ]
160
+ });
161
+ }
162
+ function ScrollBar({ className, orientation = "vertical", ...props }) {
163
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollAreaScrollbar, {
164
+ "data-slot": "scroll-area-scrollbar",
165
+ "data-orientation": orientation,
166
+ orientation,
167
+ className: cn("flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent", className),
168
+ ...props,
169
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollAreaThumb, {
170
+ "data-slot": "scroll-area-thumb",
171
+ className: "relative flex-1 rounded-full bg-border"
172
+ })
173
+ });
174
+ }
175
+ /**
176
+ * A unique count the server estimates from a HyperLogLog sketch, typically
177
+ * within about 3% of the true count. It shows as approximate, as analytics
178
+ * consoles mark estimates, and says so to screen readers. Zero is exact: no
179
+ * report reached the sketch.
180
+ */
181
+ function EstimatedCount({ value }) {
182
+ if (value === 0) return "0";
183
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
184
+ title: "Estimated",
185
+ children: [
186
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
187
+ "aria-hidden": "true",
188
+ className: "mr-1 text-muted-foreground",
189
+ children: "≈"
190
+ }),
191
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
192
+ className: "sr-only",
193
+ children: "Estimated "
194
+ }),
195
+ value.toLocaleString()
196
+ ]
197
+ });
198
+ }
199
+ function ReportingDevicesSummary({ window, count, isPending, partial, retention = DEFAULT_INSIGHTS_RETENTION }) {
200
+ const metric = usageMetrics[window];
201
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
202
+ "aria-label": `${metric.label} over ${metric.period}`,
203
+ role: "region",
204
+ className: "flex items-center gap-3",
205
+ children: [isPending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, {
206
+ "aria-label": "Loading active users",
207
+ className: "h-9 w-12"
208
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
209
+ className: "text-4xl font-semibold tracking-tight tabular-nums",
210
+ children: count === void 0 ? "—" : partial ? `≥${count.toLocaleString()}` : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EstimatedCount, { value: count })
211
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
212
+ className: "flex items-center gap-1 text-sm text-muted-foreground",
213
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: metric.label }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(InsightsInfo, {
214
+ label: `How ${metric.label} is counted`,
215
+ children: [
216
+ "Unique app installations that reported activity in the last",
217
+ " ",
218
+ metric.period,
219
+ ", including no-change reports. Filtered by platform, channel, and app version. The chart counts unique reporting installations per ",
220
+ metric.interval,
221
+ ". Unique counts are estimates, typically within about 3%. On DynamoDB and Firestore the server updates totals in batches: when the Console cannot write to the database, totals can leave out up to a minute of the latest reports. Hourly counts are kept for ",
222
+ formatDays(retention.rawDays),
223
+ " and daily counts for ",
224
+ formatDays(retention.dailyDays),
225
+ ", so 12 months counts whole UTC days.",
226
+ partial ? " Only part of the history is available; the total is a lower bound." : ""
227
+ ]
228
+ })]
229
+ })]
230
+ });
231
+ }
232
+ var dates$1 = new Intl.DateTimeFormat("en", {
233
+ month: "short",
234
+ day: "numeric",
235
+ timeZone: "UTC"
236
+ });
237
+ var times = new Intl.DateTimeFormat("en", {
238
+ month: "short",
239
+ day: "numeric",
240
+ hour: "numeric",
241
+ timeZone: "UTC"
242
+ });
243
+ function AppUsage({ query, search, window, onWindowChange, retention = DEFAULT_INSIGHTS_RETENTION }) {
244
+ const report = query.error ? void 0 : query.data;
245
+ const formatTime = (ms) => (window === "30d" || window === "12m" ? dates$1 : times).format(ms);
246
+ const distributionPeriod = report ? `since ${dates$1.format(report.distributionSinceMs)}, 00:00 UTC` : "in the selected period";
247
+ const distributionTotals = report ? {
248
+ versions: report.versions.reduce((sum, row) => sum + row.installations, 0),
249
+ platforms: report.platforms.reduce((sum, row) => sum + row.installations, 0)
250
+ } : void 0;
251
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
252
+ className: "grid min-w-0 gap-6 xl:grid-cols-[minmax(0,2fr)_minmax(16rem,1fr)]",
253
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, {
254
+ "aria-label": "App usage",
255
+ role: "region",
256
+ className: "min-w-0 shadow-sm",
257
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
258
+ className: "flex-row flex-wrap items-center justify-between gap-4",
259
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, { children: "App usage" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UsagePeriodSelector, {
260
+ window,
261
+ onWindowChange,
262
+ twelveMonths: keepsTwelveMonths(retention)
263
+ })]
264
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardContent, {
265
+ className: "flex flex-col gap-4",
266
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReportingDevicesSummary, {
267
+ window,
268
+ count: report?.activeInstallations,
269
+ isPending: query.isPending,
270
+ partial: report?.truncated ?? false,
271
+ retention
272
+ }), query.isPending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, {
273
+ "aria-label": "Loading app usage",
274
+ className: "h-48"
275
+ }) : query.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
276
+ className: "flex flex-col items-start gap-4",
277
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsErrorAlert, {
278
+ error: query.error,
279
+ fallbackTitle: "App usage unavailable"
280
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
281
+ variant: "outline",
282
+ onClick: () => void query.refetch(),
283
+ children: "Retry app usage"
284
+ })]
285
+ }) : report?.activeInstallations === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Empty, {
286
+ className: "h-48",
287
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(EmptyHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyTitle, { children: "No activity reported" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyDescription, { children: "Try another period, platform, channel, or app version." })] })
288
+ }) : report ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
289
+ className: "mb-2 text-right text-xs text-muted-foreground",
290
+ children: "Reporting installations · UTC"
291
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartContainer, {
292
+ "aria-label": "Active users per interval",
293
+ className: "h-48 w-full aspect-auto",
294
+ config: { installations: {
295
+ label: "Active users",
296
+ color: "var(--chart-2)"
297
+ } },
298
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(LineChart, {
299
+ accessibilityLayer: true,
300
+ data: report.points,
301
+ margin: {
302
+ right: 8,
303
+ top: 8
304
+ },
305
+ children: [
306
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CartesianGrid, { vertical: false }),
307
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(XAxis, {
308
+ dataKey: "startMs",
309
+ tick: { fill: "var(--muted-foreground)" },
310
+ tickFormatter: formatTime,
311
+ axisLine: false,
312
+ tickLine: false,
313
+ minTickGap: 45
314
+ }),
315
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(YAxis, {
316
+ tick: { fill: "var(--muted-foreground)" },
317
+ allowDecimals: false,
318
+ axisLine: false,
319
+ tickLine: false,
320
+ width: "auto"
321
+ }),
322
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartTooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartTooltipContent, { labelFormatter: (_, payload) => `${times.format(payload[0]?.payload.startMs)} · UTC` }) }),
323
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, {
324
+ dataKey: "installations",
325
+ type: "linear",
326
+ stroke: "var(--color-installations)",
327
+ strokeWidth: 2,
328
+ dot: { r: 2 },
329
+ activeDot: { r: 4 },
330
+ isAnimationActive: false
331
+ })
332
+ ]
333
+ })
334
+ })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", {
335
+ className: "text-xs text-muted-foreground",
336
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", {
337
+ className: "cursor-pointer py-2",
338
+ children: "View usage data"
339
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Table, {
340
+ "aria-label": "App usage by interval",
341
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableRow, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, { children: "Interval · UTC" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
342
+ className: "text-right",
343
+ children: "Active users"
344
+ })] }) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableBody, { children: report.points.map((point) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableRow, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, { children: times.format(point.startMs) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
345
+ className: "text-right tabular-nums",
346
+ children: point.installations === null ? "Unknown" : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EstimatedCount, { value: point.installations })
347
+ })] }, point.startMs)) })]
348
+ })]
349
+ })] }) : null]
350
+ })]
351
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, {
352
+ "aria-label": "Usage distribution",
353
+ role: "region",
354
+ className: "flex min-w-0 flex-col shadow-sm",
355
+ children: [
356
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
357
+ className: "flex-row items-center justify-between gap-3",
358
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, {
359
+ className: "flex min-h-9 items-center",
360
+ children: "Distribution"
361
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(InsightsInfo, {
362
+ label: "How distribution is counted",
363
+ children: [
364
+ "Each reporting installation is counted once, using the app version and platform of its latest matching report ",
365
+ distributionPeriod,
366
+ ". Latest reports are counted by UTC day.",
367
+ report?.truncated ? " Shares reflect only the available history." : ""
368
+ ]
369
+ })]
370
+ }),
371
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardContent, {
372
+ className: "flex-1",
373
+ children: query.isPending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, {
374
+ "aria-label": "Loading distribution",
375
+ className: "h-48"
376
+ }) : query.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsErrorAlert, {
377
+ error: query.error,
378
+ fallbackTitle: "Distribution unavailable"
379
+ }) : !report?.activeInstallations ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Empty, {
380
+ className: "h-40",
381
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(EmptyHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyTitle, { children: "No reporting installations" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyDescription, { children: "Try a different filter or period." })] })
382
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
383
+ className: "flex flex-col gap-6",
384
+ children: ["versions", "platforms"].map((dimension) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
385
+ "aria-label": dimension === "versions" ? "App version distribution" : "Platform distribution",
386
+ className: "flex flex-col gap-4",
387
+ children: [
388
+ dimension === "platforms" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Separator$1, {}) : null,
389
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", {
390
+ className: "flex items-center gap-2 text-sm font-medium text-muted-foreground",
391
+ children: [dimension === "versions" ? "App versions" : "Platforms", dimension === "versions" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
392
+ variant: "secondary",
393
+ children: report.versions.length
394
+ }) : null]
395
+ }),
396
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollArea$1, {
397
+ className: "min-w-0 [&_[data-slot=scroll-area-viewport]]:max-h-40",
398
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dl", {
399
+ className: "flex flex-col gap-4 pr-4",
400
+ children: report[dimension].map((row) => {
401
+ const name = dimension === "platforms" ? row.name === "ios" ? "iOS" : "Android" : row.name;
402
+ const share = row.installations / (distributionTotals?.[dimension] ?? 1) * 100;
403
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
404
+ className: "group relative flex min-h-11 flex-col gap-2 rounded-sm focus-within:ring-2 focus-within:ring-ring sm:min-h-0",
405
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
406
+ className: "flex min-w-0 items-baseline justify-between gap-3",
407
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dt", {
408
+ className: "flex min-w-0 items-center gap-2 text-sm",
409
+ title: name,
410
+ children: [dimension === "platforms" && (row.name === "ios" || row.name === "android") ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
411
+ platform: row.name,
412
+ className: "size-4 shrink-0"
413
+ }) : null, dimension === "versions" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Link, {
414
+ to: "/insights/distribution",
415
+ search: {
416
+ ...search,
417
+ version: row.name
418
+ },
419
+ "aria-label": `View bundles for app version ${row.name}`,
420
+ className: "flex min-w-0 items-center gap-1 after:absolute after:inset-0 group-hover:underline",
421
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
422
+ className: "truncate",
423
+ children: name
424
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronRight, {
425
+ "aria-hidden": "true",
426
+ className: "size-3 shrink-0 text-muted-foreground"
427
+ })]
428
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
429
+ className: "truncate",
430
+ children: name
431
+ })]
432
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dd", {
433
+ className: "flex shrink-0 gap-3 text-sm tabular-nums",
434
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: row.installations.toLocaleString() }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
435
+ className: "min-w-10 text-right text-muted-foreground",
436
+ children: [share.toFixed(1), "%"]
437
+ })]
438
+ })]
439
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Progress$1, {
440
+ "aria-label": `${name} share`,
441
+ "aria-valuetext": `${row.installations} installations, ${share.toFixed(1)}%`,
442
+ value: share
443
+ })]
444
+ }, row.name);
445
+ })
446
+ })
447
+ })
448
+ ]
449
+ }, dimension))
450
+ })
451
+ }),
452
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardFooter, {
453
+ className: "border-t pt-4",
454
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Link, {
455
+ to: "/insights/distribution",
456
+ search,
457
+ className: buttonVariants({
458
+ variant: "outline",
459
+ className: "h-11 w-full sm:h-9"
460
+ }),
461
+ children: ["View distribution", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowRight, {
462
+ "aria-hidden": "true",
463
+ "data-icon": "inline-end"
464
+ })]
465
+ })
466
+ })
467
+ ]
468
+ })]
469
+ });
470
+ }
471
+ var Combobox$1 = ComboboxRoot;
472
+ function ComboboxTrigger({ className, children, ...props }) {
473
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ComboboxTrigger$1, {
474
+ "data-slot": "combobox-trigger",
475
+ className: cn("[&_svg:not([class*='size-'])]:size-3.5", className),
476
+ ...props,
477
+ children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: "text-muted-foreground size-3.5 pointer-events-none" })]
478
+ });
479
+ }
480
+ function ComboboxClear({ className, ...props }) {
481
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxClear$1, {
482
+ "aria-label": "Clear selection",
483
+ "data-slot": "combobox-clear",
484
+ render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InputGroupButton, {
485
+ variant: "ghost",
486
+ size: "icon-xs"
487
+ }),
488
+ className: cn(className),
489
+ ...props,
490
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, { className: "pointer-events-none" })
491
+ });
492
+ }
493
+ function ComboboxInput({ className, children, disabled = false, showTrigger = true, showClear = false, ...props }) {
494
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(InputGroup, {
495
+ className: cn("w-auto", className),
496
+ children: [
497
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxInput$1, {
498
+ render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InputGroupInput, { disabled }),
499
+ ...props
500
+ }),
501
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(InputGroupAddon, {
502
+ align: "inline-end",
503
+ children: [showTrigger && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxTrigger, { render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InputGroupButton, {
504
+ size: "icon-xs",
505
+ variant: "ghost",
506
+ "data-slot": "input-group-button",
507
+ className: "group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent",
508
+ disabled
509
+ }) }), showClear && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxClear, { disabled })]
510
+ }),
511
+ children
512
+ ]
513
+ });
514
+ }
515
+ function ComboboxContent({ className, side = "bottom", sideOffset = 6, align = "start", alignOffset = 0, anchor, ...props }) {
516
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxPortal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxPositioner, {
517
+ side,
518
+ sideOffset,
519
+ align,
520
+ alignOffset,
521
+ anchor,
522
+ className: "isolate z-50",
523
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxPopup, {
524
+ "data-slot": "combobox-content",
525
+ "data-chips": !!anchor,
526
+ 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),
527
+ ...props
528
+ })
529
+ }) });
530
+ }
531
+ function ComboboxList({ className, ...props }) {
532
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxList$1, {
533
+ "data-slot": "combobox-list",
534
+ 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),
535
+ ...props
536
+ });
537
+ }
538
+ function ComboboxItem({ className, children, ...props }) {
539
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ComboboxItem$1, {
540
+ "data-slot": "combobox-item",
541
+ 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),
542
+ ...props,
543
+ children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxItemIndicator, {
544
+ render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pointer-events-none absolute right-2 flex items-center justify-center" }),
545
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "pointer-events-none" })
546
+ })]
547
+ });
548
+ }
549
+ function ComboboxEmpty({ className, ...props }) {
550
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxEmpty$1, {
551
+ "data-slot": "combobox-empty",
552
+ className: cn("text-muted-foreground hidden w-full justify-center py-2 text-center text-xs/relaxed group-data-empty/combobox-content:flex", className),
553
+ ...props
554
+ });
555
+ }
556
+ function InsightsControls({ onFiltersChange, scope, releaseScope, appVersions }) {
557
+ const [platform, setPlatform] = (0, import_react.useState)(scope.platform);
558
+ const [channel, setChannel] = (0, import_react.useState)(scope.channel);
559
+ const [appVersion, setAppVersion] = (0, import_react.useState)(scope.appVersion ?? "");
560
+ const [releasePlatform, setReleasePlatform] = (0, import_react.useState)(releaseScope.platform);
561
+ const [releaseId, setReleaseId] = (0, import_react.useState)(releaseScope.releaseId ?? "");
562
+ const versions = [...new Set([...appVersions, ...appVersion ? [appVersion] : []])];
563
+ const channelsQuery = useChannelsQuery();
564
+ const channels = channelsQuery.data?.map((item) => item.name) ?? [];
565
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, {
566
+ className: "shadow-sm",
567
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
568
+ className: "gap-1 border-b px-5 py-4",
569
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, { children: "Filters" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardDescription, { children: "Channel applies to all reports. Other filters affect the report named in their label." })]
570
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardContent, {
571
+ className: "px-5 py-4",
572
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("form", {
573
+ "aria-label": "Insights filters",
574
+ onSubmit: (event) => {
575
+ event.preventDefault();
576
+ onFiltersChange({
577
+ scope: {
578
+ platform,
579
+ channel,
580
+ ...appVersion ? { appVersion } : {}
581
+ },
582
+ releaseScope: {
583
+ platform: releasePlatform,
584
+ channel,
585
+ ...releaseId.trim() ? { releaseId: releaseId.trim() } : {}
586
+ }
587
+ });
588
+ },
589
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(FieldGroup, {
590
+ className: "grid grid-cols-1 items-end gap-4 sm:grid-cols-2 xl:grid-cols-[minmax(12rem,1.25fr)_9rem_minmax(10rem,1fr)_9rem_minmax(12rem,1fr)_auto]",
591
+ children: [
592
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, {
593
+ className: "min-w-0",
594
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FieldLabel, {
595
+ htmlFor: "insights-channel",
596
+ children: "Channel"
597
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Combobox$1, {
598
+ items: channels,
599
+ value: channel,
600
+ onValueChange: (value) => {
601
+ if (value !== null) setChannel(value);
602
+ },
603
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxInput, {
604
+ className: "h-11 w-full sm:h-9 [&_input]:h-full",
605
+ id: "insights-channel",
606
+ placeholder: "Search channels…",
607
+ maxLength: 1024
608
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ComboboxContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxEmpty, { children: channelsQuery.isPending ? "Loading channels…" : channelsQuery.isError ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
609
+ className: "flex flex-col items-center gap-2 p-2",
610
+ children: ["Couldn't load channels.", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
611
+ onClick: () => void channelsQuery.refetch(),
612
+ size: "sm",
613
+ variant: "outline",
614
+ children: "Retry channels"
615
+ })]
616
+ }) : "No channels found." }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxList, { children: (item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxItem, {
617
+ className: "min-h-11 pr-8 sm:min-h-8",
618
+ value: item,
619
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
620
+ className: "truncate",
621
+ children: item
622
+ })
623
+ }, item) })] })]
624
+ })]
625
+ }),
626
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FieldLabel, {
627
+ htmlFor: "insights-platform",
628
+ children: "Usage platform"
629
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
630
+ items: {
631
+ all: "All platforms",
632
+ ios: "iOS",
633
+ android: "Android"
634
+ },
635
+ value: platform,
636
+ onValueChange: (value) => {
637
+ if (value === "all" || value === "ios" || value === "android") setPlatform(value);
638
+ },
639
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
640
+ id: "insights-platform",
641
+ className: "min-h-11 w-full sm:min-h-9",
642
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectValue, { children: [platform !== "all" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
643
+ platform,
644
+ className: "size-3.5"
645
+ }) : null, platform === "all" ? "All platforms" : platform === "ios" ? "iOS" : "Android"] })
646
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectGroup, { children: [
647
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
648
+ value: "all",
649
+ children: "All platforms"
650
+ }),
651
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem, {
652
+ value: "ios",
653
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
654
+ platform: "ios",
655
+ className: "size-3.5"
656
+ }), "iOS"]
657
+ }),
658
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem, {
659
+ value: "android",
660
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
661
+ platform: "android",
662
+ className: "size-3.5"
663
+ }), "Android"]
664
+ })
665
+ ] }) })]
666
+ })] }),
667
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, {
668
+ className: "min-w-0",
669
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FieldLabel, {
670
+ htmlFor: "insights-app-version",
671
+ children: "App version"
672
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
673
+ items: Object.fromEntries([["all", "All versions"], ...versions.map((version) => [`version:${version}`, version])]),
674
+ value: appVersion ? `version:${appVersion}` : "all",
675
+ onValueChange: (value) => {
676
+ if (value !== null) setAppVersion(value === "all" ? "" : value.slice(8));
677
+ },
678
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
679
+ id: "insights-app-version",
680
+ className: "min-h-11 w-full sm:min-h-9",
681
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, {})
682
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectGroup, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
683
+ value: "all",
684
+ children: "All versions"
685
+ }), versions.map((version) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
686
+ value: `version:${version}`,
687
+ children: version
688
+ }, version))] }) })]
689
+ })]
690
+ }),
691
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FieldLabel, {
692
+ htmlFor: "insights-release-platform",
693
+ children: "Health platform"
694
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
695
+ items: {
696
+ ios: "iOS",
697
+ android: "Android"
698
+ },
699
+ value: releasePlatform,
700
+ onValueChange: (value) => {
701
+ if (value === "ios" || value === "android") setReleasePlatform(value);
702
+ },
703
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
704
+ id: "insights-release-platform",
705
+ className: "min-h-11 w-full sm:min-h-9",
706
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectValue, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
707
+ platform: releasePlatform,
708
+ className: "size-3.5"
709
+ }), releasePlatform === "ios" ? "iOS" : "Android"] })
710
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectGroup, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem, {
711
+ value: "ios",
712
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
713
+ platform: "ios",
714
+ className: "size-3.5"
715
+ }), "iOS"]
716
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem, {
717
+ value: "android",
718
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
719
+ platform: "android",
720
+ className: "size-3.5"
721
+ }), "Android"]
722
+ })] }) })]
723
+ })] }),
724
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, {
725
+ className: "min-w-0",
726
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(FieldLabel, {
727
+ htmlFor: "insights-release-id",
728
+ children: [
729
+ "Release ID",
730
+ " ",
731
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
732
+ className: "text-muted-foreground",
733
+ children: "(optional)"
734
+ })
735
+ ]
736
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
737
+ autoComplete: "off",
738
+ className: "h-11 sm:h-9",
739
+ id: "insights-release-id",
740
+ maxLength: 1024,
741
+ name: "releaseId",
742
+ placeholder: "All releases…",
743
+ value: releaseId,
744
+ onChange: (event) => setReleaseId(event.target.value)
745
+ })]
746
+ }),
747
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
748
+ className: "h-11 w-full sm:h-9",
749
+ size: "lg",
750
+ type: "submit",
751
+ variant: "outline",
752
+ children: "Apply filters"
753
+ })
754
+ ]
755
+ })
756
+ })
757
+ })]
758
+ });
759
+ }
760
+ var dates = new Intl.DateTimeFormat("en", {
761
+ month: "short",
762
+ day: "numeric",
763
+ timeZone: "UTC"
764
+ });
765
+ var rate = (report) => {
766
+ const attempts = report.activeDays + report.failedLaunches;
767
+ return attempts === 0 ? "—" : `${(report.failedLaunches / attempts * 100).toFixed(2)}%`;
768
+ };
769
+ function ReleaseHealth({ report }) {
770
+ const attempts = report.activeDays + report.failedLaunches;
771
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
772
+ className: "flex flex-col gap-8",
773
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dl", {
774
+ className: "grid grid-cols-2 gap-5 lg:grid-cols-4",
775
+ children: [
776
+ ["Active installations", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EstimatedCount, { value: report.activeInstallations })],
777
+ ["Active days", report.activeDays.toLocaleString()],
778
+ ["Failed launches", report.failedLaunches.toLocaleString()],
779
+ ["Crash rate", attempts === 0 ? "— / No launch reports" : rate(report)]
780
+ ].map(([label, value]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
781
+ className: "flex flex-col gap-1",
782
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dt", {
783
+ className: "flex items-center gap-1 text-sm text-muted-foreground",
784
+ children: [label, label === "Active installations" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsInfo, {
785
+ label: "About active installations",
786
+ children: "Distinct installations that reported in this scope and period, or that launched this release, estimated: typically within about 3%."
787
+ }) : label === "Active days" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsInfo, {
788
+ label: "About active days",
789
+ children: "Each installation counts once for each UTC day it launched, and once more on a day an update applies or recovers. The chart shows each day's daily active installations."
790
+ }) : label === "Crash rate" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsInfo, {
791
+ label: "About crash rate",
792
+ children: "Reported OTA launch failures divided by active days plus failed launches for the same scope and period."
793
+ }) : null]
794
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
795
+ className: "text-3xl font-semibold tracking-tight tabular-nums",
796
+ children: value
797
+ })]
798
+ }, label))
799
+ }), report.points.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
800
+ className: "flex h-56 items-center justify-center text-sm text-muted-foreground",
801
+ children: "No launch reports in this period."
802
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartContainer, {
803
+ "aria-label": "Daily active installations and failed launches",
804
+ className: "h-64 w-full aspect-auto",
805
+ config: {
806
+ dailyActiveInstallations: {
807
+ label: "Daily active installations",
808
+ color: "var(--chart-2)"
809
+ },
810
+ failedLaunches: {
811
+ label: "Failed launches",
812
+ color: "var(--chart-1)"
813
+ }
814
+ },
815
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(LineChart, {
816
+ data: report.points,
817
+ margin: {
818
+ left: -12,
819
+ right: 12
820
+ },
821
+ children: [
822
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CartesianGrid, { vertical: false }),
823
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(XAxis, {
824
+ axisLine: false,
825
+ dataKey: "startMs",
826
+ tickFormatter: (value) => dates.format(value),
827
+ tickLine: false
828
+ }),
829
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(YAxis, {
830
+ allowDecimals: false,
831
+ axisLine: false,
832
+ tickLine: false
833
+ }),
834
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartTooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartTooltipContent, { labelFormatter: (_, payload) => `${dates.format(payload[0]?.payload.startMs)} · UTC` }) }),
835
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, {
836
+ dataKey: "dailyActiveInstallations",
837
+ stroke: "var(--color-dailyActiveInstallations)",
838
+ strokeWidth: 2,
839
+ isAnimationActive: false
840
+ }),
841
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, {
842
+ dataKey: "failedLaunches",
843
+ stroke: "var(--color-failedLaunches)",
844
+ strokeWidth: 2,
845
+ isAnimationActive: false
846
+ })
847
+ ]
848
+ })
849
+ })]
850
+ });
851
+ }
852
+ function InsightsOverview({ input, onWindowChange, query, onRefresh }) {
853
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("section", {
854
+ "aria-label": "Release health",
855
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, {
856
+ className: "min-w-0 overflow-hidden shadow-sm",
857
+ children: [
858
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardHeader, {
859
+ className: "flex flex-col gap-4 p-6",
860
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
861
+ className: "flex flex-wrap items-center justify-between gap-4",
862
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, { children: "Release health" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
863
+ className: "flex items-center gap-2",
864
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsPeriodSelector, {
865
+ window: input.window,
866
+ onWindowChange
867
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
868
+ "aria-label": "Refresh release health",
869
+ variant: "ghost",
870
+ size: "icon-lg",
871
+ disabled: query.isFetching,
872
+ onClick: onRefresh,
873
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RotateCw, { "aria-hidden": "true" })
874
+ })]
875
+ })]
876
+ })
877
+ }),
878
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardContent, {
879
+ className: "px-6 pb-6",
880
+ children: query.isPending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, {
881
+ "aria-label": "Loading release health",
882
+ className: "h-80"
883
+ }) : query.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsErrorAlert, {
884
+ error: query.error,
885
+ fallbackTitle: "Release health unavailable"
886
+ }) : query.data ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReleaseHealth, { report: query.data }) : null
887
+ }),
888
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardFooter, {
889
+ className: "flex items-center justify-between border-t px-6 py-5",
890
+ children: [query.data?.coverage.kind === "partial" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
891
+ className: "text-xs text-muted-foreground",
892
+ children: "Partial"
893
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Link, {
894
+ className: buttonVariants({
895
+ variant: "outline",
896
+ size: "sm"
897
+ }),
898
+ to: "/installations",
899
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(List, { "aria-hidden": "true" }), "Event history"]
900
+ })]
901
+ })
902
+ ]
903
+ })
904
+ });
905
+ }
906
+ var share = (events, total) => total === 0 ? "—" : `${(events / total * 100).toFixed(1)}%`;
907
+ function Metric({ label, info, children }) {
908
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
909
+ className: "flex flex-col gap-1",
910
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dt", {
911
+ className: "flex items-center gap-1 text-sm text-muted-foreground",
912
+ children: [label, info ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsInfo, {
913
+ label: `About ${label.toLowerCase()}`,
914
+ children: info
915
+ }) : null]
916
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
917
+ className: "text-3xl font-semibold tracking-tight tabular-nums",
918
+ children
919
+ })]
920
+ });
921
+ }
922
+ /** One stage and reason, and what else its clients knew, most first. */
923
+ function FailureRow({ failure, total }) {
924
+ const label = `${failureStageLabel(failure.stage)} · ${failureReasonLabel(failure.reason)}`;
925
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableRow, {
926
+ className: "[&>td]:px-4 [&>td]:py-3 [&>td]:align-top sm:[&>td]:px-6",
927
+ children: [
928
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
929
+ className: "whitespace-normal",
930
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", {
931
+ className: "group/failure",
932
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("summary", {
933
+ className: "flex cursor-pointer list-none items-center gap-1 rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring/30 [&::-webkit-details-marker]:hidden",
934
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
935
+ className: "font-medium",
936
+ children: label
937
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, {
938
+ "aria-hidden": "true",
939
+ className: "size-3 text-muted-foreground group-open/failure:rotate-180"
940
+ })]
941
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
942
+ "aria-label": `${label} details`,
943
+ className: "mt-2 flex flex-col gap-1 text-xs text-muted-foreground",
944
+ children: failure.details.map((detail) => {
945
+ const parts = failureDetailParts(detail);
946
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", {
947
+ className: "flex justify-between gap-4",
948
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
949
+ className: "wrap-anywhere",
950
+ children: parts.length === 0 ? "No details reported" : parts.join(" · ")
951
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
952
+ className: "tabular-nums",
953
+ children: detail.events.toLocaleString()
954
+ })]
955
+ }, JSON.stringify(detail));
956
+ })
957
+ })]
958
+ })
959
+ }),
960
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
961
+ className: "text-right tabular-nums",
962
+ children: failure.events.toLocaleString()
963
+ }),
964
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
965
+ className: "text-right tabular-nums text-muted-foreground",
966
+ children: share(failure.events, total)
967
+ })
968
+ ]
969
+ });
970
+ }
971
+ function Recoveries({ recoveries }) {
972
+ const known = recoveries.byExitReason.reduce((sum, { events }) => sum + events, 0);
973
+ const unreported = Math.max(0, recoveries.failedLaunches - known);
974
+ const reasons = [...recoveries.byExitReason.map(({ exitReason, events }) => ({
975
+ name: exitReason,
976
+ code: true,
977
+ events
978
+ })), ...unreported > 0 ? [{
979
+ name: "Not reported",
980
+ code: false,
981
+ events: unreported
982
+ }] : []];
983
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
984
+ "aria-labelledby": "recoveries-by-exit-reason",
985
+ className: "flex flex-col gap-2",
986
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", {
987
+ className: "flex items-center gap-1 text-sm font-medium",
988
+ id: "recoveries-by-exit-reason",
989
+ children: ["Recoveries by exit reason", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsInfo, {
990
+ label: "About exit reasons",
991
+ children: "Why the crashed process exited, as Android 11 and later report it: a crash, an ANR, or the system or the user closing the app. iOS and older Android report none."
992
+ })]
993
+ }), reasons.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
994
+ className: "text-sm text-muted-foreground",
995
+ children: "No recoveries in this period."
996
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
997
+ className: "flex flex-col gap-1 text-sm",
998
+ children: reasons.map(({ name, code, events }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", {
999
+ className: "flex justify-between gap-4",
1000
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1001
+ className: code ? "font-mono text-xs" : void 0,
1002
+ children: name
1003
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1004
+ className: "tabular-nums",
1005
+ children: [events.toLocaleString(), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1006
+ className: "ml-2 text-muted-foreground",
1007
+ children: share(events, recoveries.failedLaunches)
1008
+ })]
1009
+ })]
1010
+ }, name))
1011
+ })]
1012
+ });
1013
+ }
1014
+ function FailuresReport({ report }) {
1015
+ const breakdown = report.breakdown ?? [];
1016
+ const total = breakdown.reduce((sum, { events }) => sum + events, 0);
1017
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1018
+ className: "flex flex-col gap-8",
1019
+ children: [
1020
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", {
1021
+ className: "grid grid-cols-2 gap-5 lg:grid-cols-4",
1022
+ children: [
1023
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
1024
+ label: "Failed updates",
1025
+ children: report.failedUpdates.toLocaleString()
1026
+ }),
1027
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
1028
+ label: "Attempt failure rate",
1029
+ info: "The failure rate of update attempts: failed download and install reports divided by those plus download reports.",
1030
+ children: formatRate(failureRate(report))
1031
+ }),
1032
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
1033
+ label: "Failed installations",
1034
+ info: "Distinct installations with a failed download or install, estimated: typically within about 3%. A client reports each failure at most once a UTC day.",
1035
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EstimatedCount, { value: report.failedInstallations })
1036
+ }),
1037
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
1038
+ label: "Patch fallback rate",
1039
+ info: "Of the downloads that tried a patch, the share that fell back to the changed files or the full archive.",
1040
+ children: formatRate(patchFallbackRate(report))
1041
+ }),
1042
+ report.checks ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
1043
+ label: "Failed checks",
1044
+ children: report.checks.failures.toLocaleString()
1045
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
1046
+ label: "Check failure rate",
1047
+ info: "Installations whose update check failed, divided by the channel's active installations; both estimated. A check the device could not send while offline is not reported.",
1048
+ children: formatRate(checkFailureRate(report.checks))
1049
+ })] }) : null
1050
+ ]
1051
+ }),
1052
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
1053
+ "aria-labelledby": "failures-by-stage",
1054
+ className: "flex flex-col gap-2",
1055
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", {
1056
+ className: "text-sm font-medium",
1057
+ id: "failures-by-stage",
1058
+ children: "Failures by stage and reason"
1059
+ }), breakdown.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1060
+ className: "text-sm text-muted-foreground",
1061
+ children: "No update failures in this period."
1062
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Table, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableRow, {
1063
+ className: "[&>th]:px-4 sm:[&>th]:px-6",
1064
+ children: [
1065
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, { children: "Stage · reason" }),
1066
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
1067
+ className: "w-24 text-right",
1068
+ children: "Reports"
1069
+ }),
1070
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
1071
+ className: "w-24 text-right",
1072
+ children: "Share"
1073
+ })
1074
+ ]
1075
+ }) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableBody, { children: breakdown.map((failure) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FailureRow, {
1076
+ failure,
1077
+ total
1078
+ }, `${failure.stage} ${failure.reason}`)) })] })]
1079
+ }),
1080
+ report.recoveries ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Recoveries, { recoveries: report.recoveries }) : null
1081
+ ]
1082
+ });
1083
+ }
1084
+ /**
1085
+ * Update failures for Release health's scope and period: counts, rates, the
1086
+ * stage and reason breakdown with what else the clients knew, and why
1087
+ * crashed processes exited.
1088
+ */
1089
+ function UpdateFailures({ query, onRefresh }) {
1090
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("section", {
1091
+ "aria-label": "Update failures",
1092
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, {
1093
+ className: "min-w-0 overflow-hidden shadow-sm",
1094
+ children: [
1095
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
1096
+ className: "flex flex-col gap-1.5 p-6",
1097
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1098
+ className: "flex flex-wrap items-center justify-between gap-4",
1099
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, { children: "Update failures" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1100
+ "aria-label": "Refresh update failures",
1101
+ variant: "ghost",
1102
+ size: "icon-lg",
1103
+ disabled: query.isFetching,
1104
+ onClick: onRefresh,
1105
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RotateCw, { "aria-hidden": "true" })
1106
+ })]
1107
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardDescription, { children: "Checks, downloads, and installs that failed, for Release health's scope and period." })]
1108
+ }),
1109
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardContent, {
1110
+ className: "px-6 pb-6",
1111
+ children: query.isPending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, {
1112
+ "aria-label": "Loading update failures",
1113
+ className: "h-64"
1114
+ }) : query.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsErrorAlert, {
1115
+ error: query.error,
1116
+ fallbackTitle: "Update failures unavailable"
1117
+ }) : query.data ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FailuresReport, { report: query.data }) : null
1118
+ }),
1119
+ query.data?.coverage.kind === "partial" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardFooter, {
1120
+ className: "border-t px-6 py-5",
1121
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1122
+ className: "text-xs text-muted-foreground",
1123
+ children: "Partial"
1124
+ })
1125
+ }) : null
1126
+ ]
1127
+ })
1128
+ });
1129
+ }
1130
+ function InsightsPage() {
1131
+ const search = Route.useSearch();
1132
+ const navigate = Route.useNavigate();
1133
+ const retention = useInsightsRetention();
1134
+ const usageWindow = search.window ?? "24h";
1135
+ const bundleWindow = search.bundleWindow ?? "7d";
1136
+ const channel = search.channel ?? search.healthChannel ?? "production";
1137
+ const scope = {
1138
+ platform: search.platform ?? "all",
1139
+ channel,
1140
+ appVersion: search.appVersion
1141
+ };
1142
+ const setUsageWindow = (window) => void navigate({ search: {
1143
+ ...search,
1144
+ window
1145
+ } });
1146
+ const input = {
1147
+ ...scope,
1148
+ window: usageWindow
1149
+ };
1150
+ const bundleInput = {
1151
+ platform: search.healthPlatform ?? "ios",
1152
+ channel,
1153
+ window: bundleWindow,
1154
+ ...search.releaseId === void 0 ? {} : { releaseId: search.releaseId }
1155
+ };
1156
+ const query = useQuery({
1157
+ queryKey: [
1158
+ "insights",
1159
+ "app-usage",
1160
+ input
1161
+ ],
1162
+ queryFn: () => getAppUsageReportRpc({ data: input }),
1163
+ staleTime: 3e4
1164
+ });
1165
+ const bundleQuery = useQuery({
1166
+ queryKey: [
1167
+ "insights",
1168
+ "recovery",
1169
+ bundleInput
1170
+ ],
1171
+ queryFn: () => getRecoveryReportRpc({ data: bundleInput }),
1172
+ staleTime: 3e4
1173
+ });
1174
+ const failuresQuery = useUpdateFailuresQuery(bundleInput);
1175
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1176
+ className: "flex h-svh min-h-0 flex-col",
1177
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsPageHeader, {
1178
+ view: "overview",
1179
+ overviewSearch: search
1180
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
1181
+ className: "min-h-0 min-w-0 flex-1 overflow-y-auto bg-muted/5 px-4 py-6 sm:p-8",
1182
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1183
+ className: "mx-auto flex w-full max-w-6xl flex-col gap-6",
1184
+ children: [
1185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsControls, {
1186
+ scope,
1187
+ releaseScope: bundleInput,
1188
+ appVersions: query.data?.appVersions ?? [],
1189
+ onFiltersChange: ({ scope, releaseScope }) => void navigate({ search: {
1190
+ ...search,
1191
+ ...scope,
1192
+ appVersion: scope.appVersion,
1193
+ healthPlatform: releaseScope.platform,
1194
+ healthChannel: void 0,
1195
+ releaseId: releaseScope.releaseId
1196
+ } })
1197
+ }, JSON.stringify({
1198
+ scope,
1199
+ bundleInput
1200
+ })),
1201
+ query.data?.truncated && !query.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Alert, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertTitle, { children: "Partial history" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDescription, { children: [
1202
+ "Only reports since",
1203
+ " ",
1204
+ new Date(query.data.sinceMs).toLocaleString("en", { timeZone: "UTC" }),
1205
+ " ",
1206
+ "UTC are available. The active-user count is a lower bound. Choose a shorter period for a more complete view."
1207
+ ] })] }) : null,
1208
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppUsage, {
1209
+ query,
1210
+ search: {
1211
+ ...scope,
1212
+ window: usageWindow,
1213
+ bundleWindow
1214
+ },
1215
+ window: usageWindow,
1216
+ onWindowChange: setUsageWindow,
1217
+ retention
1218
+ }),
1219
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsOverview, {
1220
+ input: bundleInput,
1221
+ query: bundleQuery,
1222
+ onWindowChange: (window) => void navigate({ search: {
1223
+ ...search,
1224
+ bundleWindow: window
1225
+ } }),
1226
+ onRefresh: () => void bundleQuery.refetch()
1227
+ }),
1228
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UpdateFailures, {
1229
+ query: failuresQuery,
1230
+ onRefresh: () => void failuresQuery.refetch()
1231
+ })
1232
+ ]
1233
+ })
1234
+ })]
1235
+ });
1236
+ }
1237
+ //#endregion
1238
+ export { InsightsPage as component };