@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
@@ -2,7 +2,13 @@ import { RefreshCw } from "lucide-react";
2
2
 
3
3
  import { HashValueDisplay } from "@/components/HashValueDisplay";
4
4
  import { Button } from "@/components/ui/button";
5
- import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
5
+ import {
6
+ Card,
7
+ CardContent,
8
+ CardDescription,
9
+ CardHeader,
10
+ CardTitle,
11
+ } from "@/components/ui/card";
6
12
  import { Separator } from "@/components/ui/separator";
7
13
  import { Skeleton } from "@/components/ui/skeleton";
8
14
  import {
@@ -13,6 +19,11 @@ import {
13
19
  TableHeader,
14
20
  TableRow,
15
21
  } from "@/components/ui/table";
22
+ import {
23
+ DEFAULT_INSIGHTS_RETENTION,
24
+ formatDays,
25
+ type InsightsRetentionDays,
26
+ } from "@/lib/insights-retention";
16
27
  import type {
17
28
  InsightsEventRow,
18
29
  InsightsInstallationViewRow,
@@ -22,24 +33,25 @@ import type {
22
33
  import {
23
34
  EventBundleTransition,
24
35
  EventTimestamp,
25
- EventTypeBadge,
36
+ EventTypeDetails,
26
37
  useInsightsTimeFormat,
27
38
  } from "./EventDetails";
28
39
  import { EventHistoryList } from "./EventHistoryList";
29
40
  import { InsightsErrorAlert } from "./InsightsErrorAlert";
30
41
  import { InsightsPagination } from "./InsightsPagination";
31
42
 
32
- const getUserLabel = (event: {
33
- readonly username: string | null;
34
- readonly userId: string | null;
35
- }) => event.userId ?? event.username ?? "—";
43
+ const getUserLabel = (event: { readonly userId: string | null }) =>
44
+ event.userId ?? "—";
36
45
 
46
+ /** A download or a failed update leaves the installation on the bundle it ran. */
37
47
  const getLastKnownBundleId = (
38
48
  event: InsightsEventRow | InsightsInstallationViewRow,
39
49
  ) =>
40
50
  "lastKnownBundleId" in event
41
51
  ? event.lastKnownBundleId
42
- : (event.toBundleId ?? event.fromBundleId);
52
+ : event.type === "UPDATE_DOWNLOADED" || event.type === "UPDATE_FAILED"
53
+ ? event.fromBundleId
54
+ : event.toBundleId;
43
55
 
44
56
  export function InstallationHistoryCard({
45
57
  error,
@@ -49,6 +61,7 @@ export function InstallationHistoryCard({
49
61
  onPrevious,
50
62
  onRefresh,
51
63
  pageNumber,
64
+ retention = DEFAULT_INSIGHTS_RETENTION,
52
65
  selectedEvent,
53
66
  selectedInstallId,
54
67
  }: {
@@ -59,6 +72,8 @@ export function InstallationHistoryCard({
59
72
  readonly onNext: () => void;
60
73
  readonly onPrevious: () => void;
61
74
  readonly pageNumber: number;
75
+ /** How long the server keeps Insights rows. */
76
+ readonly retention?: InsightsRetentionDays;
62
77
  readonly selectedEvent:
63
78
  | InsightsEventRow
64
79
  | InsightsInstallationViewRow
@@ -70,6 +85,15 @@ export function InstallationHistoryCard({
70
85
  ? getLastKnownBundleId(selectedEvent)
71
86
  : undefined;
72
87
 
88
+ const pendingBundleId =
89
+ selectedEvent && "pendingBundleId" in selectedEvent
90
+ ? selectedEvent.pendingBundleId
91
+ : selectedEvent &&
92
+ "type" in selectedEvent &&
93
+ selectedEvent.type === "UPDATE_DOWNLOADED"
94
+ ? selectedEvent.toBundleId
95
+ : null;
96
+
73
97
  return (
74
98
  <Card className="@container min-h-0 min-w-0 shadow-sm">
75
99
  <CardHeader className="p-4 sm:p-6">
@@ -82,6 +106,14 @@ export function InstallationHistoryCard({
82
106
  : "Select an installation"}
83
107
  </h2>
84
108
  </CardTitle>
109
+ {selectedInstallId !== undefined ? (
110
+ <CardDescription>
111
+ Bundle changes and update failures are kept for{" "}
112
+ {formatDays(retention.rawDays)}; the latest report, for{" "}
113
+ {formatDays(retention.dailyDays)} after the installation last
114
+ reports.
115
+ </CardDescription>
116
+ ) : null}
85
117
  </div>
86
118
  {onRefresh && selectedInstallId !== undefined ? (
87
119
  <Button
@@ -138,6 +170,16 @@ export function InstallationHistoryCard({
138
170
  )}
139
171
  </dd>
140
172
  </div>
173
+ {pendingBundleId ? (
174
+ <div className="col-span-2 min-w-0">
175
+ <dt className="text-xs text-muted-foreground">
176
+ Downloaded · Pending apply
177
+ </dt>
178
+ <dd className="mt-1 text-xs">
179
+ <HashValueDisplay value={pendingBundleId} />
180
+ </dd>
181
+ </div>
182
+ ) : null}
141
183
  </dl>
142
184
  </section>
143
185
  <Separator />
@@ -192,7 +234,7 @@ export function InstallationHistoryCard({
192
234
  />
193
235
  </TableCell>
194
236
  <TableCell>
195
- <EventTypeBadge type={event.type} />
237
+ <EventTypeDetails event={event} />
196
238
  </TableCell>
197
239
  <TableCell className="whitespace-normal text-xs">
198
240
  <div className="flex flex-col gap-2">
@@ -226,8 +268,8 @@ export function InstallationHistoryCard({
226
268
  ) : (
227
269
  <>
228
270
  <div className="p-6 text-sm text-muted-foreground">
229
- {pageNumber > 1 || history?.nextCursor
230
- ? "No bundle changes on this page."
271
+ {pageNumber > 1
272
+ ? "No older bundle changes."
231
273
  : "No bundle changes recorded yet."}
232
274
  </div>
233
275
  {history && (pageNumber > 1 || history.nextCursor) ? (
@@ -17,7 +17,7 @@ const getLastKnownBundleId = (event: InsightsInstallationViewRow) =>
17
17
  event.lastKnownBundleId;
18
18
 
19
19
  const getUserLabel = (event: InsightsInstallationViewRow) =>
20
- event.userId ?? event.username ?? "—";
20
+ event.userId ?? "—";
21
21
 
22
22
  export function InstallationMatchesCard({
23
23
  error,
@@ -0,0 +1,188 @@
1
+ import { Link } from "@tanstack/react-router";
2
+
3
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
4
+ import { Skeleton } from "@/components/ui/skeleton";
5
+ import type {
6
+ BundleActivityInput,
7
+ BundleActivityReport,
8
+ } from "@/lib/bundle-activity";
9
+ import { useBundleActivityQuery } from "@/lib/bundle-activity";
10
+ import { useConsoleFeature } from "@/lib/console-features-api";
11
+ import { useUpdateFailuresQuery } from "@/lib/insights-api";
12
+ import { failureRate, formatRate } from "@/lib/insights-failures";
13
+
14
+ import type { ReleaseColumn } from "../FeatureSlots";
15
+ import { InsightsInfo } from "./InsightsInfo";
16
+
17
+ const crashRate = (report: BundleActivityReport): string => {
18
+ const attempts = report.activeDays + report.failedLaunches;
19
+ return attempts === 0
20
+ ? "—"
21
+ : `${((report.failedLaunches / attempts) * 100).toFixed(2)}%`;
22
+ };
23
+
24
+ export function BundleMovementSummary({
25
+ report,
26
+ loading = false,
27
+ input,
28
+ }: {
29
+ readonly report?: BundleActivityReport;
30
+ readonly loading?: boolean;
31
+ readonly input?: BundleActivityInput;
32
+ }) {
33
+ if (!report) {
34
+ return loading ? (
35
+ <Skeleton aria-label="Loading release insights" className="h-4 w-40" />
36
+ ) : (
37
+ <span aria-label="Release insights unavailable">—</span>
38
+ );
39
+ }
40
+ const metrics = (
41
+ <span className="flex min-w-[220px] flex-wrap items-baseline gap-x-3 gap-y-1 text-sm">
42
+ <span>Downloads {report.downloads.toLocaleString()}</span>
43
+ <span>Active days {report.activeDays.toLocaleString()}</span>
44
+ <span>
45
+ Known crashes {report.failedLaunches.toLocaleString()} (
46
+ {crashRate(report)})
47
+ </span>
48
+ {report.coverage.kind === "partial" ? (
49
+ <span className="text-xs text-muted-foreground">Partial</span>
50
+ ) : null}
51
+ </span>
52
+ );
53
+ return (
54
+ <span className="flex items-center gap-1">
55
+ {input ? (
56
+ <Link
57
+ className="rounded-sm hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
58
+ to="/insights"
59
+ search={{
60
+ healthPlatform: input.platform,
61
+ healthChannel: input.channel,
62
+ releaseId: input.releaseId,
63
+ bundleWindow: "7d",
64
+ }}
65
+ >
66
+ {metrics}
67
+ </Link>
68
+ ) : (
69
+ metrics
70
+ )}
71
+ <InsightsInfo label="About release insight metrics">
72
+ Active days count each installation once for each UTC day it launched
73
+ this release. Known crashes are reported OTA launch failures that
74
+ triggered recovery. The rate is known crashes divided by active days
75
+ plus known crashes.
76
+ </InsightsInfo>
77
+ </span>
78
+ );
79
+ }
80
+
81
+ /** The release's failed downloads and installs since its first report, and their rate. */
82
+ export function BundleDownloadFailures({
83
+ input,
84
+ }: {
85
+ readonly input: BundleActivityInput;
86
+ }) {
87
+ const query = useUpdateFailuresQuery({
88
+ platform: input.platform,
89
+ channel: input.channel,
90
+ releaseId: input.releaseId,
91
+ });
92
+ const report = query.data;
93
+ return (
94
+ <span className="flex items-center gap-1 text-sm">
95
+ {report ? (
96
+ <span>
97
+ Download failures {report.failedUpdates.toLocaleString()} (
98
+ {formatRate(failureRate(report))})
99
+ </span>
100
+ ) : query.isPending ? (
101
+ <Skeleton aria-label="Loading download failures" className="h-4 w-40" />
102
+ ) : (
103
+ <span aria-label="Download failures unavailable">—</span>
104
+ )}
105
+ <InsightsInfo label="About download failures">
106
+ Reported failures to download or install this release, since its first
107
+ report; each client reports one at most once a UTC day. The rate is the
108
+ failure rate of update attempts: failures divided by failures plus
109
+ download reports.
110
+ </InsightsInfo>
111
+ </span>
112
+ );
113
+ }
114
+
115
+ /** The bundle detail's Insights card, a section of the release editor: activity and download failures. */
116
+ export function BundleInsightsSummary({
117
+ input,
118
+ }: {
119
+ readonly input: BundleActivityInput;
120
+ }) {
121
+ const query = useBundleActivityQuery([input]);
122
+ return (
123
+ <Card>
124
+ <CardHeader className="px-4 pt-4 pb-3">
125
+ <CardTitle className="text-sm font-medium">Insights</CardTitle>
126
+ </CardHeader>
127
+ <CardContent className="flex flex-col gap-2 px-4 pb-4">
128
+ <BundleMovementSummary
129
+ input={input}
130
+ loading={query.isFetching}
131
+ report={query.data?.[input.releaseId]}
132
+ />
133
+ <BundleDownloadFailures input={input} />
134
+ </CardContent>
135
+ </Card>
136
+ );
137
+ }
138
+
139
+ /**
140
+ * The release editor's Insights card where the console reads no release
141
+ * activity, as through a self-hosted server's admin API: download failures
142
+ * alone. With release activity, its card shows them.
143
+ */
144
+ export function ReleaseFailuresSection({
145
+ input,
146
+ }: {
147
+ readonly input: BundleActivityInput;
148
+ }) {
149
+ const activity = useConsoleFeature("insightsAnalytics");
150
+ if (activity) return null;
151
+ return (
152
+ <Card>
153
+ <CardHeader className="px-4 pt-4 pb-3">
154
+ <CardTitle className="text-sm font-medium">Insights</CardTitle>
155
+ </CardHeader>
156
+ <CardContent className="px-4 pb-4">
157
+ <BundleDownloadFailures input={input} />
158
+ </CardContent>
159
+ </Card>
160
+ );
161
+ }
162
+
163
+ /**
164
+ * One release's activity in the Bundles page's Insights column. Every cell
165
+ * asks for the page's releases under one query key, so the page makes one
166
+ * request.
167
+ */
168
+ function ReleaseActivityCell({
169
+ releaseId,
170
+ releases,
171
+ }: {
172
+ readonly releaseId: string;
173
+ readonly releases: readonly BundleActivityInput[];
174
+ }) {
175
+ const query = useBundleActivityQuery(releases);
176
+ return (
177
+ <BundleMovementSummary
178
+ input={releases.find((release) => release.releaseId === releaseId)}
179
+ loading={query.isFetching}
180
+ report={query.data?.[releaseId]}
181
+ />
182
+ );
183
+ }
184
+
185
+ /** The Bundles page's Insights column: each release's downloads, active days, and crashes. */
186
+ export const releaseActivityColumn: ReleaseColumn = {
187
+ Cell: ReleaseActivityCell,
188
+ };
@@ -0,0 +1,65 @@
1
+ import { Skeleton } from "@/components/ui/skeleton";
2
+ import {
3
+ DEFAULT_INSIGHTS_RETENTION,
4
+ formatDays,
5
+ type InsightsRetentionDays,
6
+ } from "@/lib/insights-retention";
7
+ import { usageMetrics, type UsageWindow } from "@/lib/insights-usage";
8
+
9
+ import { EstimatedCount } from "./EstimatedCount";
10
+ import { InsightsInfo } from "./InsightsInfo";
11
+
12
+ export function ReportingDevicesSummary({
13
+ window,
14
+ count,
15
+ isPending,
16
+ partial,
17
+ retention = DEFAULT_INSIGHTS_RETENTION,
18
+ }: {
19
+ readonly window: UsageWindow;
20
+ readonly count: number | undefined;
21
+ readonly isPending: boolean;
22
+ readonly partial: boolean;
23
+ readonly retention?: InsightsRetentionDays;
24
+ }) {
25
+ const metric = usageMetrics[window];
26
+ return (
27
+ <div
28
+ aria-label={`${metric.label} over ${metric.period}`}
29
+ role="region"
30
+ className="flex items-center gap-3"
31
+ >
32
+ {isPending ? (
33
+ <Skeleton aria-label="Loading active users" className="h-9 w-12" />
34
+ ) : (
35
+ <p className="text-4xl font-semibold tracking-tight tabular-nums">
36
+ {count === undefined ? (
37
+ "—"
38
+ ) : partial ? (
39
+ `≥${count.toLocaleString()}`
40
+ ) : (
41
+ <EstimatedCount value={count} />
42
+ )}
43
+ </p>
44
+ )}
45
+ <div className="flex items-center gap-1 text-sm text-muted-foreground">
46
+ <span>{metric.label}</span>
47
+ <InsightsInfo label={`How ${metric.label} is counted`}>
48
+ Unique app installations that reported activity in the last{" "}
49
+ {metric.period}, including no-change reports. Filtered by platform,
50
+ channel, and app version. The chart counts unique reporting
51
+ installations per {metric.interval}. Unique counts are estimates,
52
+ typically within about 3%. On DynamoDB and Firestore the server
53
+ updates totals in batches: when the Console cannot write to the
54
+ database, totals can leave out up to a minute of the latest reports.
55
+ Hourly counts are kept for {formatDays(retention.rawDays)} and daily
56
+ counts for {formatDays(retention.dailyDays)}, so 12 months counts
57
+ whole UTC days.
58
+ {partial
59
+ ? " Only part of the history is available; the total is a lower bound."
60
+ : ""}
61
+ </InsightsInfo>
62
+ </div>
63
+ </div>
64
+ );
65
+ }