@hot-updater/console 1.1.3 → 1.1.5

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 (154) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/EventDetails-gbD_fCqc.js +1 -0
  3. package/.output/public/assets/{InsightsPageHeader-SYEOMClX.js → InsightsPageHeader-BRw8XkJW.js} +1 -1
  4. package/.output/public/assets/api-keys-D81VHHNM.js +1 -0
  5. package/.output/public/assets/api-keys-Dia6i8K1.js +1 -0
  6. package/.output/public/assets/badge-Bqf3UDee.js +1 -0
  7. package/.output/public/assets/circle-off-BsbZQtsF.js +1 -0
  8. package/.output/public/assets/field-GxF9SyUo.js +1 -0
  9. package/.output/public/assets/{index-C5S4DrO2.js → index-DrkdiRIn.js} +3 -3
  10. package/.output/public/assets/insights-C-5t8wh2.js +2 -0
  11. package/.output/public/assets/insights-D_ngQdiz.js +1 -0
  12. package/.output/public/assets/insights-KigRBDgC.js +42 -0
  13. package/.output/public/assets/insights-api-1ZjVu3Sb.js +1 -0
  14. package/.output/public/assets/insights-search-aT8BVCci.js +1 -0
  15. package/.output/public/assets/{insights-usage-rpc-DTL0_Rp8.js → insights-usage-rpc-DqTiXI1_.js} +1 -1
  16. package/.output/public/assets/insights_.distribution-Bzl6RFBR.js +2 -0
  17. package/.output/public/assets/insights_.distribution-Cthgttqb.js +1 -0
  18. package/.output/public/assets/insights_.distribution-D2c2v1bu.js +1 -0
  19. package/.output/public/assets/installations-2Nx8Htf-.js +1 -0
  20. package/.output/public/assets/{installations-we1IV0xp.js → installations-BskJ1xQr.js} +2 -2
  21. package/.output/public/assets/installations-K2iJ_r0h.js +1 -0
  22. package/.output/public/assets/{remote-config-uBgCaI-p.js → remote-config-D9X3NiwV.js} +4 -4
  23. package/.output/public/assets/{remote-config-DyTLPRKh.js → remote-config-DzOOiBIO.js} +1 -1
  24. package/.output/public/assets/remote-config-ikg7kak0.js +1 -0
  25. package/.output/public/assets/routes-9rb9P5Y7.js +2 -0
  26. package/.output/public/assets/routes-P7683vKQ.js +1 -0
  27. package/.output/public/assets/{select-CV8nkfsf.js → select-M3H2gxCc.js} +1 -1
  28. package/.output/public/assets/styles-CpiW_YdW.css +2 -0
  29. package/.output/public/assets/{switch-C8ycp9AH.js → switch-By_vfm8I.js} +1 -1
  30. package/.output/server/{__23tanstack-start-server-fn-resolver-Gb7cVpng.mjs → __23tanstack-start-server-fn-resolver-BTRoM3ZK.mjs} +40 -40
  31. package/.output/server/_libs/@base-ui/react+[...].mjs +77 -1
  32. package/.output/server/_libs/@tanstack/react-router+[...].mjs +1 -1
  33. package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +2 -2
  34. package/.output/server/_libs/lucide-react.mjs +1 -1
  35. package/.output/server/_libs/next-themes.mjs +1 -1
  36. package/.output/server/_libs/react-redux+[...].mjs +1 -1
  37. package/.output/server/_libs/recharts+[...].mjs +1 -1
  38. package/.output/server/_libs/sonner.mjs +1 -1
  39. package/.output/server/_libs/tanstack__react-query.mjs +1 -1
  40. package/.output/server/_ssr/{BundleIdDisplay-DDiNTVT3.mjs → BundleIdDisplay-Cu_uxtLL.mjs} +3 -3
  41. package/.output/server/_ssr/{ConsoleFeatureUnavailable-DE65d293.mjs → ConsoleFeatureUnavailable-De7nGook.mjs} +7 -7
  42. package/.output/server/_ssr/{EventDetails-Bg1V3JsJ.mjs → EventDetails-B6qJU3Ho.mjs} +8 -8
  43. package/.output/server/_ssr/{HashValueDisplay-BDHvSsxT.mjs → HashValueDisplay-Dk7gXwSZ.mjs} +3 -3
  44. package/.output/server/_ssr/{InsightsPageHeader-Db-y_PRS.mjs → InsightsPageHeader-BqNItX24.mjs} +8 -8
  45. package/.output/server/_ssr/{NotFoundPage-DHgL7Uzm.mjs → NotFoundPage-O0Y4NQ5N.mjs} +3 -3
  46. package/.output/server/_ssr/{alert-dialog-MbJW0K9H.mjs → alert-dialog-G7g_XzVI.mjs} +2 -2
  47. package/.output/server/_ssr/{api-XOdKU3tw.mjs → api-BL3kC-5m.mjs} +3 -3
  48. package/.output/server/_ssr/{api-keys-v7wa2_rX.mjs → api-keys-CShII1tz.mjs} +2 -2
  49. package/.output/server/_ssr/{api-keys-BHJdcnez.mjs → api-keys-DdGuNGTn.mjs} +9 -8
  50. package/.output/server/_ssr/{api-keys-rpc-B389z2LY.mjs → api-keys-rpc-Dayd_DRM.mjs} +3 -3
  51. package/.output/server/_ssr/{api-rpc-T6dVPhPr.mjs → api-rpc-Cf625HwX.mjs} +6 -41
  52. package/.output/server/_ssr/{auth-rpc-CzPT9U2N.mjs → auth-rpc-Bg9zjpz5.mjs} +4 -4
  53. package/.output/server/_ssr/{auth.server-LmCV7BTA.mjs → auth.server-BQjMW0uK.mjs} +2 -2
  54. package/.output/server/_ssr/badge-DN8fj73I.mjs +108 -0
  55. package/.output/server/_ssr/{bundleActivity-M5gzBFNg.mjs → bundleActivity-0qFIcvqt.mjs} +1 -1
  56. package/.output/server/_ssr/{config.server-COvZCTA4.mjs → config.server-L7ll75x7.mjs} +4 -4
  57. package/.output/server/_ssr/{console-access-CRAcskRF.mjs → console-access-DeVju1PF.mjs} +2 -2
  58. package/.output/server/_ssr/{console-features-Oz0q20jJ.mjs → console-features-4xAGfZHw.mjs} +1 -1
  59. package/.output/server/_ssr/{console-features-api-qihYDNRo.mjs → console-features-api-BY65aaIY.mjs} +5 -5
  60. package/.output/server/_ssr/{console-features-rpc-r1KcSohP.mjs → console-features-rpc-DsTaZWvl.mjs} +3 -3
  61. package/.output/server/_ssr/{dialog-CPbM7GQc.mjs → dialog-CfpXr88f.mjs} +2 -2
  62. package/.output/server/_ssr/{downloadBundle-CZysjmNW.mjs → downloadBundle-94jPBGSO.mjs} +1 -1
  63. package/.output/server/_ssr/{empty-8scgvztU.mjs → empty-B2DK143D.mjs} +3 -3
  64. package/.output/server/_ssr/{failureReports-DKDhVsXA.mjs → failureReports-Bh22wois.mjs} +4 -4
  65. package/.output/server/_ssr/field-DvXxMlEZ.mjs +89 -0
  66. package/.output/server/_ssr/{getBundleChildren-Cv9iz1As.mjs → getBundleChildren-DfxB2K8D.mjs} +1 -1
  67. package/.output/server/_ssr/{input-group-BTB34g3v.mjs → input-group-DDXGLcxY.mjs} +2 -2
  68. package/.output/server/_ssr/{insights-CGpn8W5V.mjs → insights-BeTm0qQ2.mjs} +590 -360
  69. package/.output/server/_ssr/{insights-Btnkt5-Y.mjs → insights-BqC8tldt.mjs} +2 -2
  70. package/.output/server/_ssr/{insights-BulsRVnt.mjs → insights-CPSCgGN4.mjs} +5 -5
  71. package/.output/server/_ssr/{insights-api-Dg3D7Vkg.mjs → insights-api-BO700P1C.mjs} +5 -5
  72. package/.output/server/_ssr/{insights-errors-BjnFUyZj.mjs → insights-errors-Iy-lBaM7.mjs} +2 -2
  73. package/.output/server/_ssr/{insights-failures-xaa41Buf.mjs → insights-failures-BJN3RfLu.mjs} +3 -3
  74. package/.output/server/_ssr/{insights-failures-rpc-DdK55wRd.mjs → insights-failures-rpc-D6OK70Td.mjs} +12 -12
  75. package/.output/server/_ssr/{insights-recovery-BWQhiZaF.mjs → insights-recovery-BUuQdlA3.mjs} +1 -1
  76. package/.output/server/_ssr/{insights-recovery-rpc-D-dwlIPT.mjs → insights-recovery-rpc-Ir31GbY1.mjs} +6 -6
  77. package/.output/server/_ssr/{insights-rpc-Dn-jdkQK.mjs → insights-rpc-BSJ2KM34.mjs} +3 -3
  78. package/.output/server/_ssr/{insights-search-C8EWA0hu.mjs → insights-search-CkCpWvaA.mjs} +8 -7
  79. package/.output/server/_ssr/{insights-usage-BpBM0U0c.mjs → insights-usage-CHqvtehH.mjs} +1 -1
  80. package/.output/server/_ssr/{insights-usage-rpc-DIa-5vUO.mjs → insights-usage-rpc-CZ8sEK9v.mjs} +6 -6
  81. package/.output/server/_ssr/{insights-usage-rpc-BCulD17c.mjs → insights-usage-rpc-DQBuGtPL.mjs} +5 -5
  82. package/.output/server/_ssr/{insightsUsage-CzhVjsc8.mjs → insightsUsage-COUG-_-U.mjs} +3 -3
  83. package/.output/server/_ssr/{insights_.distribution-CE0ve_Rx.mjs → insights_.distribution-Coh7tQy7.mjs} +14 -13
  84. package/.output/server/_ssr/{insights_.distribution-D7ZzykQb.mjs → insights_.distribution-Dq_-XUJC.mjs} +2 -2
  85. package/.output/server/_ssr/{insights_.distribution-CKe3T4k1.mjs → insights_.distribution-Z6uZcuUP.mjs} +5 -5
  86. package/.output/server/_ssr/{installations-Bu4Sk4W6.mjs → installations-7BMDB-Tp.mjs} +4 -4
  87. package/.output/server/_ssr/{installations-CJHIYH0u.mjs → installations-CyPx28Vm.mjs} +2 -2
  88. package/.output/server/_ssr/{installations-CGMSRurj.mjs → installations-D8F2SIvA.mjs} +12 -11
  89. package/.output/server/_ssr/{release-adoption-005tQRpo.mjs → release-adoption-DuYJTGuX.mjs} +17 -7
  90. package/.output/server/_ssr/{release-adoption-rpc-DPMYNIlM.mjs → release-adoption-rpc-CreGclHX.mjs} +8 -8
  91. package/.output/server/_ssr/{releaseAdoption-CtOFkwli.mjs → releaseAdoption-B39cpvfo.mjs} +42 -26
  92. package/.output/server/_ssr/releaseReachability-bFsuElSS.mjs +39 -0
  93. package/.output/server/_ssr/{remote-config-DrC_3H7K.mjs → remote-config-Btt6_RTm.mjs} +2 -2
  94. package/.output/server/_ssr/{remote-config-Ca-Wv5sF.mjs → remote-config-BxGrsk5L.mjs} +18 -17
  95. package/.output/server/_ssr/{remote-config-BCW6vPOD.mjs → remote-config-CPY9yuqw.mjs} +3 -3
  96. package/.output/server/_ssr/{remote-config-rpc-CcGmTmvS.mjs → remote-config-rpc-Dd2ete-w.mjs} +3 -3
  97. package/.output/server/_ssr/{router-C5eI7aq7.mjs → router-Vd7IPVAC.mjs} +17 -17
  98. package/.output/server/_ssr/{routes-Bd3qlRny.mjs → routes-Cg0UP4L0.mjs} +17 -16
  99. package/.output/server/_ssr/{routes-DZoij0js.mjs → routes-DdZKGKrM.mjs} +17 -17
  100. package/.output/server/_ssr/{runtime.server-De0QVWc9.mjs → runtime.server-DsHqVqTr.mjs} +2 -2
  101. package/.output/server/_ssr/{select-usyW0pcU.mjs → select-DfawnwAT.mjs} +2 -2
  102. package/.output/server/_ssr/{server-Bp9nEe3Z.mjs → server-C36LNk44.mjs} +4 -4
  103. package/.output/server/_ssr/{sidebar-Bqu4emj1.mjs → sidebar-BNeODqno.mjs} +3 -3
  104. package/.output/server/_ssr/ssr.mjs +1 -1
  105. package/.output/server/_ssr/{switch-DJPcwvwD.mjs → switch-Cpbv90X-.mjs} +3 -3
  106. package/.output/server/_ssr/{table-DEzdHi1t.mjs → table-W02Ce9NY.mjs} +2 -2
  107. package/.output/server/_ssr/{tabs-HzTSngsE.mjs → tabs-CTxsEjmq.mjs} +3 -3
  108. package/.output/server/_ssr/updateFailures-z1IQy_4q.mjs +103 -0
  109. package/.output/server/{_tanstack-start-manifest_v-DTOQk1l4.mjs → _tanstack-start-manifest_v-_BY9hYTm.mjs} +51 -50
  110. package/.output/server/index.mjs +193 -186
  111. package/package.json +7 -7
  112. package/src/components/features/insights/AppUsage.tsx +15 -2
  113. package/src/components/features/insights/InsightsControls.tsx +264 -246
  114. package/src/components/features/insights/InsightsOverview.tsx +90 -68
  115. package/src/components/features/insights/InsightsScope.tsx +39 -0
  116. package/src/components/features/insights/ReleaseActivity.tsx +4 -4
  117. package/src/components/features/insights/ReleaseCombobox.tsx +196 -0
  118. package/src/components/features/insights/RollbackButton.tsx +4 -0
  119. package/src/components/features/insights/UpdateFailures.tsx +7 -1
  120. package/src/lib/insights-failures-rpc.ts +3 -1
  121. package/src/lib/insights-failures.ts +5 -2
  122. package/src/lib/insights-recovery.ts +5 -0
  123. package/src/lib/insights-search.ts +10 -14
  124. package/src/lib/release-adoption-api.ts +24 -32
  125. package/src/lib/release-adoption.ts +45 -8
  126. package/src/lib/server/failureReports.ts +1 -1
  127. package/src/lib/server/releaseAdoption.ts +75 -26
  128. package/src/lib/server/updateFailures.ts +131 -10
  129. package/src/routes/insights.tsx +56 -24
  130. package/src/routes/insights_.distribution.tsx +2 -1
  131. package/.output/public/assets/EventDetails-CSX2ayg9.js +0 -1
  132. package/.output/public/assets/api-keys-BEdLKiib.js +0 -1
  133. package/.output/public/assets/api-keys-TdTtKm2b.js +0 -1
  134. package/.output/public/assets/chevron-right-BnvHIJdZ.js +0 -1
  135. package/.output/public/assets/field-DBMkBfkD.js +0 -1
  136. package/.output/public/assets/insights-BMKRCIg1.js +0 -2
  137. package/.output/public/assets/insights-CPyViuyC.js +0 -42
  138. package/.output/public/assets/insights-D0PbX-Rm.js +0 -1
  139. package/.output/public/assets/insights-api-Dmd_LgT3.js +0 -1
  140. package/.output/public/assets/insights-search-DAa0f7Dr.js +0 -1
  141. package/.output/public/assets/insights_.distribution-BaOQlBV9.js +0 -1
  142. package/.output/public/assets/insights_.distribution-CxXMIEb5.js +0 -2
  143. package/.output/public/assets/insights_.distribution-EE5OqxyL.js +0 -1
  144. package/.output/public/assets/installations-BgISglUQ.js +0 -1
  145. package/.output/public/assets/installations-DQbgRkSi.js +0 -1
  146. package/.output/public/assets/remote-config-AQmdmbT8.js +0 -1
  147. package/.output/public/assets/routes-Br26jzt9.js +0 -1
  148. package/.output/public/assets/routes-CyirpG2O.js +0 -2
  149. package/.output/public/assets/styles-DTGEAJFv.css +0 -2
  150. package/.output/server/_ssr/field-DHrweW4l.mjs +0 -189
  151. package/.output/server/_ssr/updateFailures-H6pnnYK4.mjs +0 -40
  152. /package/.output/public/assets/{ConsoleFeatureUnavailable-B-iNWpuE.js → ConsoleFeatureUnavailable-C0KfsKWL.js} +0 -0
  153. /package/.output/public/assets/{table-DgGD67Fx.js → table-ek1DmRHq.js} +0 -0
  154. /package/.output/public/assets/{valueToPercent-C4C1WZVn.js → valueToPercent-B44vDssN.js} +0 -0
@@ -1,34 +1,35 @@
1
1
  import { i as __toESM } from "../_runtime.mjs";
2
2
  import { u as createServerFn } from "./createMiddleware-B1iPez7u.mjs";
3
- import { t as consoleAccess } from "./console-access-CRAcskRF.mjs";
4
- import { C as ScrollAreaScrollbar, S as ScrollAreaThumb, T as ScrollAreaRoot, _ as ComboboxList$1, _t as require_react, b as ComboboxTrigger$1, d as ComboboxRoot, f as ComboboxEmpty$1, g as ComboboxPortal, gt as require_jsx_runtime, h as ComboboxPositioner, m as ComboboxPopup, p as ComboboxItem$1, u as ComboboxItemIndicator, v as ComboboxClear$1, w as ScrollAreaViewport, x as ScrollAreaCorner, y as ComboboxInput$1 } from "../_libs/@base-ui/react+[...].mjs";
5
- import { E as List, G as Check, H as ChevronRight, I as Copy, W as ChevronDown, X as ArrowUpRight, Z as ArrowRight, g as RefreshCw, h as RotateCcw, i as TriangleAlert, m as RotateCw, r as Undo2, t as X } from "../_libs/lucide-react.mjs";
6
- import { A as createSsrRpc, C as Skeleton, O as buttonVariants, a as SheetContent, c as SheetTitle, i as Sheet, k as cn, l as SheetTrigger, n as Input, o as SheetDescription, r as Separator$1, s as SheetHeader, t as Button$1 } from "./sidebar-Bqu4emj1.mjs";
7
- import { a as Badge, c as CardDescription, d as CardTitle, f as Field, g as FieldLabel, h as FieldGroup, i as AlertTitle, l as CardFooter, o as Card, r as AlertDescription, s as CardContent, t as Alert, u as CardHeader } from "./field-DHrweW4l.mjs";
8
- import { a as AlertDialogDescription, i as AlertDialogContent, l as AlertDialogTitle, n as AlertDialogAction, o as AlertDialogFooter, r as AlertDialogCancel, s as AlertDialogHeader, t as AlertDialog$1 } from "./alert-dialog-MbJW0K9H.mjs";
9
- import { a as TableHeader, i as TableHead, n as TableBody, o as TableRow, r as TableCell, t as Table } from "./table-DEzdHi1t.mjs";
3
+ import { C as ComboboxTrigger$1, D as ScrollAreaViewport, E as ScrollAreaScrollbar, O as ScrollAreaRoot, S as ComboboxInput$1, T as ScrollAreaThumb, _ as ComboboxPositioner, b as ComboboxCollection$1, bt as require_react, d as ComboboxRoot, f as ComboboxEmpty$1, g as ComboboxPopup, h as ComboboxGroup$1, m as ComboboxGroupLabel, p as ComboboxItem$1, u as ComboboxItemIndicator, v as ComboboxPortal, w as ScrollAreaCorner, x as ComboboxClear$1, y as ComboboxList$1, yt as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
4
+ import { E as List, G as Check, H as ChevronRight, I as Copy, R as CircleOff, W as ChevronDown, X as ArrowUpRight, Z as ArrowRight, g as RefreshCw, h as RotateCcw, i as TriangleAlert, m as RotateCw, r as Undo2, t as X } from "../_libs/lucide-react.mjs";
5
+ import { A as createSsrRpc, C as Skeleton, O as buttonVariants, a as SheetContent, c as SheetTitle, i as Sheet, k as cn, l as SheetTrigger, o as SheetDescription, r as Separator$1, s as SheetHeader, t as Button$1 } from "./sidebar-BNeODqno.mjs";
6
+ import { t as consoleAccess } from "./console-access-DeVju1PF.mjs";
7
+ import { h as Link } from "../_libs/@tanstack/react-router+[...].mjs";
10
8
  import { a as useQueries, i as useQuery, s as useQueryClient } from "../_libs/tanstack__react-query.mjs";
9
+ import { i as EmptyHeader, n as EmptyContent, o as EmptyTitle, r as EmptyDescription, t as Empty } from "./empty-B2DK143D.mjs";
10
+ import { a as Badge, c as CardDescription, d as CardTitle, i as AlertTitle, l as CardFooter, o as Card, r as AlertDescription, s as CardContent, t as Alert, u as CardHeader } from "./badge-DN8fj73I.mjs";
11
+ import { a as AlertDialogDescription, i as AlertDialogContent, l as AlertDialogTitle, n as AlertDialogAction, o as AlertDialogFooter, r as AlertDialogCancel, s as AlertDialogHeader, t as AlertDialog$1 } from "./alert-dialog-G7g_XzVI.mjs";
12
+ import { a as TableHeader, i as TableHead, n as TableBody, o as TableRow, r as TableCell, t as Table } from "./table-W02Ce9NY.mjs";
11
13
  import { n as toast } from "../_libs/sonner.mjs";
12
- import { h as Link } from "../_libs/@tanstack/react-router+[...].mjs";
13
- import { i as EmptyHeader, n as EmptyContent, o as EmptyTitle, r as EmptyDescription, t as Empty } from "./empty-8scgvztU.mjs";
14
- import { r as recoveryWindows, t as insightsPeriodEnd } from "./insights-recovery-BWQhiZaF.mjs";
15
- import { a as failureReasonLabel, i as failureRate, o as failureStageLabel, r as failureDetailParts, s as formatRate, t as checkFailureRate } from "./insights-failures-xaa41Buf.mjs";
16
- import { t as groupFailureReports } from "./insights-errors-BjnFUyZj.mjs";
17
- import { n as usageMetrics } from "./insights-usage-BpBM0U0c.mjs";
18
- import { a as readBundleEventsInput, i as readAdoptionReleasesInput, n as crashRateOf, o as recommendsRollback, r as readAdoptionReleaseInput, t as CRASH_RATE_THRESHOLD } from "./release-adoption-005tQRpo.mjs";
19
- import { r as comparedBundleIds, t as InfoTooltip } from "./insights-search-C8EWA0hu.mjs";
20
- import { i as useFailureReportsQuery, l as useInsightsRetention, r as keepsTwelveMonths, t as DEFAULT_INSIGHTS_RETENTION, u as useUpdateFailuresQuery } from "./insights-api-Dg3D7Vkg.mjs";
21
- import { t as Route } from "./insights-BulsRVnt.mjs";
22
- import { i as TabsTrigger, n as TabsContent, r as TabsList, t as Tabs$1 } from "./tabs-HzTSngsE.mjs";
23
- 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-usyW0pcU.mjs";
24
- import { i as InputGroupInput, n as InputGroupAddon, r as InputGroupButton, t as InputGroup } from "./input-group-BTB34g3v.mjs";
25
- import { i as useChannelsQuery, l as usePreflightReleaseMutation, m as useUpdateReleaseMutation } from "./api-XOdKU3tw.mjs";
26
- import { t as BundleIdDisplay } from "./BundleIdDisplay-DDiNTVT3.mjs";
27
- import { n as getAppUsageReportRpc, t as Progress$1 } from "./insights-usage-rpc-BCulD17c.mjs";
28
- import { i as InsightsPeriodSelector, n as InsightsErrorAlert, o as UsagePeriodSelector, r as InsightsPageHeader } from "./InsightsPageHeader-Db-y_PRS.mjs";
29
- import { i as HttpResponseBody, n as EventTimestamp, o as useInsightsTimeFormat, t as EventBundleTransition } from "./EventDetails-Bg1V3JsJ.mjs";
14
+ import { C as isUUIDv7 } from "./dist-CbQbBIw-.mjs";
15
+ import { r as recoveryWindows, t as insightsPeriodEnd } from "./insights-recovery-BUuQdlA3.mjs";
16
+ import { a as failureReasonLabel, i as failureRate, o as failureStageLabel, r as failureDetailParts, s as formatRate, t as checkFailureRate } from "./insights-failures-BJN3RfLu.mjs";
17
+ import { t as groupFailureReports } from "./insights-errors-Iy-lBaM7.mjs";
18
+ import { n as usageMetrics } from "./insights-usage-CHqvtehH.mjs";
19
+ import { a as readAdoptionReleasesInput, i as readAdoptionReleaseInput, n as crashRateOf, o as readBundleEventsInput, r as defaultAdoptionReleases, s as recommendsRollback, t as CRASH_RATE_THRESHOLD } from "./release-adoption-DuYJTGuX.mjs";
20
+ import { i as comparedBundleIds, n as InfoTooltip, t as DEFAULT_INSIGHTS_CHANNEL } from "./insights-search-CkCpWvaA.mjs";
21
+ import { i as useFailureReportsQuery, l as useInsightsRetention, r as keepsTwelveMonths, t as DEFAULT_INSIGHTS_RETENTION, u as useUpdateFailuresQuery } from "./insights-api-BO700P1C.mjs";
22
+ import { t as Route } from "./insights-CPSCgGN4.mjs";
23
+ import { i as TabsTrigger, n as TabsContent, r as TabsList, t as Tabs$1 } from "./tabs-CTxsEjmq.mjs";
24
+ 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-DfawnwAT.mjs";
25
+ import { i as InputGroupInput, n as InputGroupAddon, r as InputGroupButton, t as InputGroup } from "./input-group-DDXGLcxY.mjs";
26
+ import { i as useChannelsQuery, l as usePreflightReleaseMutation, m as useUpdateReleaseMutation } from "./api-BL3kC-5m.mjs";
27
+ import { t as BundleIdDisplay } from "./BundleIdDisplay-Cu_uxtLL.mjs";
28
+ import { n as getAppUsageReportRpc, t as Progress$1 } from "./insights-usage-rpc-DQBuGtPL.mjs";
29
+ import { i as InsightsPeriodSelector, n as InsightsErrorAlert, o as UsagePeriodSelector, r as InsightsPageHeader } from "./InsightsPageHeader-BqNItX24.mjs";
30
+ import { i as HttpResponseBody, n as EventTimestamp, o as useInsightsTimeFormat, t as EventBundleTransition } from "./EventDetails-B6qJU3Ho.mjs";
30
31
  import { a as CartesianGrid, i as Line, l as ResponsiveContainer, n as YAxis, o as ReferenceLine, r as XAxis, s as Tooltip, t as LineChart } from "../_libs/recharts+[...].mjs";
31
- //#region node_modules/.nitro/vite/services/ssr/assets/insights-CGpn8W5V.js
32
+ //#region node_modules/.nitro/vite/services/ssr/assets/insights-BeTm0qQ2.js
32
33
  var import_react = /* @__PURE__ */ __toESM(require_react());
33
34
  var import_jsx_runtime = require_jsx_runtime();
34
35
  var THEMES = {
@@ -204,6 +205,37 @@ function EstimatedCount({ value }) {
204
205
  ]
205
206
  });
206
207
  }
208
+ var platformLabel = (platform) => platform === "all" ? "All platforms" : platform === "ios" ? "iOS" : "Android";
209
+ /** What a report reads, as the filters set it: its channel, platform, and any narrower filter. */
210
+ function InsightsScope({ channel, platform, narrower = [] }) {
211
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
212
+ className: "flex min-w-0 flex-wrap items-center gap-x-1.5 gap-y-0.5 text-xs text-muted-foreground",
213
+ children: [
214
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
215
+ className: "max-w-full truncate font-medium text-foreground",
216
+ children: channel
217
+ }),
218
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
219
+ "aria-hidden": "true",
220
+ children: "·"
221
+ }),
222
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
223
+ className: "inline-flex items-center gap-1",
224
+ children: [platform === "all" ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
225
+ platform,
226
+ className: "size-3"
227
+ }), platformLabel(platform)]
228
+ }),
229
+ narrower.map((filter) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
230
+ "aria-hidden": "true",
231
+ children: "·"
232
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
233
+ className: "max-w-full truncate",
234
+ children: filter
235
+ })] }, filter))
236
+ ]
237
+ });
238
+ }
207
239
  function ReportingDevicesSummary({ window, count, isPending, partial }) {
208
240
  const metric = usageMetrics[window];
209
241
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -259,7 +291,14 @@ function AppUsage({ query, search, window, onWindowChange, retention = DEFAULT_I
259
291
  className: "min-w-0 shadow-sm",
260
292
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
261
293
  className: "flex-row flex-wrap items-center justify-between gap-4",
262
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, { children: "App usage" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UsagePeriodSelector, {
294
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
295
+ className: "flex min-w-0 flex-col gap-1",
296
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, { children: "App usage" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsScope, {
297
+ channel: search.channel ?? "production",
298
+ platform: search.platform ?? "all",
299
+ narrower: search.appVersion ? [`App version ${search.appVersion}`] : []
300
+ })]
301
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UsagePeriodSelector, {
263
302
  window,
264
303
  onWindowChange,
265
304
  twelveMonths: keepsTwelveMonths(retention)
@@ -877,6 +916,26 @@ function ComboboxItem({ className, children, ...props }) {
877
916
  })]
878
917
  });
879
918
  }
919
+ function ComboboxGroup({ className, ...props }) {
920
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxGroup$1, {
921
+ "data-slot": "combobox-group",
922
+ className: cn(className),
923
+ ...props
924
+ });
925
+ }
926
+ function ComboboxLabel({ className, ...props }) {
927
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxGroupLabel, {
928
+ "data-slot": "combobox-label",
929
+ className: cn("text-muted-foreground px-2 py-1.5 text-xs", className),
930
+ ...props
931
+ });
932
+ }
933
+ function ComboboxCollection({ ...props }) {
934
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxCollection$1, {
935
+ "data-slot": "combobox-collection",
936
+ ...props
937
+ });
938
+ }
880
939
  function ComboboxEmpty({ className, ...props }) {
881
940
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxEmpty$1, {
882
941
  "data-slot": "combobox-empty",
@@ -884,210 +943,9 @@ function ComboboxEmpty({ className, ...props }) {
884
943
  ...props
885
944
  });
886
945
  }
887
- function InsightsControls({ onFiltersChange, scope, releaseScope, appVersions }) {
888
- const [platform, setPlatform] = (0, import_react.useState)(scope.platform);
889
- const [channel, setChannel] = (0, import_react.useState)(scope.channel);
890
- const [appVersion, setAppVersion] = (0, import_react.useState)(scope.appVersion ?? "");
891
- const [releasePlatform, setReleasePlatform] = (0, import_react.useState)(releaseScope.platform);
892
- const [releaseId, setReleaseId] = (0, import_react.useState)(releaseScope.releaseId ?? "");
893
- const versions = [...new Set([...appVersions, ...appVersion ? [appVersion] : []])];
894
- const channelsQuery = useChannelsQuery();
895
- const channels = channelsQuery.data?.map((item) => item.name) ?? [];
896
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, {
897
- className: "shadow-sm",
898
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
899
- className: "gap-1 border-b px-5 py-4",
900
- 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." })]
901
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardContent, {
902
- className: "px-5 py-4",
903
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("form", {
904
- "aria-label": "Insights filters",
905
- onSubmit: (event) => {
906
- event.preventDefault();
907
- onFiltersChange({
908
- scope: {
909
- platform,
910
- channel,
911
- ...appVersion ? { appVersion } : {}
912
- },
913
- releaseScope: {
914
- platform: releasePlatform,
915
- channel,
916
- ...releaseId.trim() ? { releaseId: releaseId.trim() } : {}
917
- }
918
- });
919
- },
920
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(FieldGroup, {
921
- 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]",
922
- children: [
923
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, {
924
- className: "min-w-0",
925
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FieldLabel, {
926
- htmlFor: "insights-channel",
927
- children: "Channel"
928
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Combobox$1, {
929
- items: channels,
930
- value: channel,
931
- onValueChange: (value) => {
932
- if (value !== null) setChannel(value);
933
- },
934
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxInput, {
935
- className: "h-11 w-full sm:h-9 [&_input]:h-full",
936
- id: "insights-channel",
937
- placeholder: "Search channels…",
938
- maxLength: 1024
939
- }), /* @__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", {
940
- className: "flex flex-col items-center gap-2 p-2",
941
- children: ["Couldn't load channels.", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
942
- onClick: () => void channelsQuery.refetch(),
943
- size: "sm",
944
- variant: "outline",
945
- children: "Retry channels"
946
- })]
947
- }) : "No channels found." }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxList, { children: (item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxItem, {
948
- className: "min-h-11 pr-8 sm:min-h-8",
949
- value: item,
950
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
951
- className: "truncate",
952
- children: item
953
- })
954
- }, item) })] })]
955
- })]
956
- }),
957
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FieldLabel, {
958
- htmlFor: "insights-platform",
959
- children: "Usage platform"
960
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
961
- items: {
962
- all: "All platforms",
963
- ios: "iOS",
964
- android: "Android"
965
- },
966
- value: platform,
967
- onValueChange: (value) => {
968
- if (value === "all" || value === "ios" || value === "android") setPlatform(value);
969
- },
970
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
971
- id: "insights-platform",
972
- className: "min-h-11 w-full sm:min-h-9",
973
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectValue, { children: [platform !== "all" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
974
- platform,
975
- className: "size-3.5"
976
- }) : null, platform === "all" ? "All platforms" : platform === "ios" ? "iOS" : "Android"] })
977
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectGroup, { children: [
978
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
979
- value: "all",
980
- children: "All platforms"
981
- }),
982
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem, {
983
- value: "ios",
984
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
985
- platform: "ios",
986
- className: "size-3.5"
987
- }), "iOS"]
988
- }),
989
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem, {
990
- value: "android",
991
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
992
- platform: "android",
993
- className: "size-3.5"
994
- }), "Android"]
995
- })
996
- ] }) })]
997
- })] }),
998
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, {
999
- className: "min-w-0",
1000
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FieldLabel, {
1001
- htmlFor: "insights-app-version",
1002
- children: "App version"
1003
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
1004
- items: Object.fromEntries([["all", "All versions"], ...versions.map((version) => [`version:${version}`, version])]),
1005
- value: appVersion ? `version:${appVersion}` : "all",
1006
- onValueChange: (value) => {
1007
- if (value !== null) setAppVersion(value === "all" ? "" : value.slice(8));
1008
- },
1009
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
1010
- id: "insights-app-version",
1011
- className: "min-h-11 w-full sm:min-h-9",
1012
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, {})
1013
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectGroup, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
1014
- value: "all",
1015
- children: "All versions"
1016
- }), versions.map((version) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
1017
- value: `version:${version}`,
1018
- children: version
1019
- }, version))] }) })]
1020
- })]
1021
- }),
1022
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FieldLabel, {
1023
- htmlFor: "insights-release-platform",
1024
- children: "Health platform"
1025
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
1026
- items: {
1027
- ios: "iOS",
1028
- android: "Android"
1029
- },
1030
- value: releasePlatform,
1031
- onValueChange: (value) => {
1032
- if (value === "ios" || value === "android") setReleasePlatform(value);
1033
- },
1034
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
1035
- id: "insights-release-platform",
1036
- className: "min-h-11 w-full sm:min-h-9",
1037
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectValue, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
1038
- platform: releasePlatform,
1039
- className: "size-3.5"
1040
- }), releasePlatform === "ios" ? "iOS" : "Android"] })
1041
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectGroup, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem, {
1042
- value: "ios",
1043
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
1044
- platform: "ios",
1045
- className: "size-3.5"
1046
- }), "iOS"]
1047
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem, {
1048
- value: "android",
1049
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
1050
- platform: "android",
1051
- className: "size-3.5"
1052
- }), "Android"]
1053
- })] }) })]
1054
- })] }),
1055
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, {
1056
- className: "min-w-0",
1057
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(FieldLabel, {
1058
- htmlFor: "insights-release-id",
1059
- children: [
1060
- "Release ID",
1061
- " ",
1062
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1063
- className: "text-muted-foreground",
1064
- children: "(optional)"
1065
- })
1066
- ]
1067
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
1068
- autoComplete: "off",
1069
- className: "h-11 sm:h-9",
1070
- id: "insights-release-id",
1071
- maxLength: 1024,
1072
- name: "releaseId",
1073
- placeholder: "All releases…",
1074
- value: releaseId,
1075
- onChange: (event) => setReleaseId(event.target.value)
1076
- })]
1077
- }),
1078
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1079
- className: "h-11 w-full sm:h-9",
1080
- size: "lg",
1081
- type: "submit",
1082
- variant: "outline",
1083
- children: "Apply filters"
1084
- })
1085
- ]
1086
- })
1087
- })
1088
- })]
1089
- });
1090
- }
946
+ var listAdoptionReleasesRpc = createServerFn({ method: "GET" }).middleware([consoleAccess]).validator(readAdoptionReleasesInput).handler(createSsrRpc("d11a48f7251928b7819e2376aed4902fa66277bf6d30312d87ca53468332f256"));
947
+ var getAdoptionReleaseRpc = createServerFn({ method: "GET" }).middleware([consoleAccess]).validator(readAdoptionReleaseInput).handler(createSsrRpc("16e3be74610cddd54e5e894419dca16817a7b63c09a4eed15042cdac9c39e1e0"));
948
+ var getBundleEventsRpc = createServerFn({ method: "GET" }).middleware([consoleAccess]).validator(readBundleEventsInput).handler(createSsrRpc("bfcca2365dfef4d071bdcddf97538a2637482c77bb747969dbdcd6769ca98416"));
1091
949
  var HOUR$1 = 36e5;
1092
950
  var DAY$1 = 24 * HOUR$1;
1093
951
  var times = new Intl.DateTimeFormat("en", {
@@ -1258,13 +1116,345 @@ function ComparedBundlesChart({ period, lines, colorOf, active, label, names })
1258
1116
  })
1259
1117
  });
1260
1118
  }
1119
+ /** Whether a bundle deployment matches what was typed: its IDs or target app version. */
1120
+ var matches = (release, typed) => typed === "" || release.releaseId.includes(typed) || release.bundleId.toLowerCase().includes(typed) || (release.targetAppVersion ?? "").toLowerCase().includes(typed);
1121
+ /** A bundle deployment as a picker names it: ID, platform, deployment, target, and state. */
1122
+ function ReleaseOption({ release }) {
1123
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1124
+ className: "flex min-w-0 flex-col gap-0.5",
1125
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1126
+ className: "flex min-w-0 items-center gap-1.5",
1127
+ children: [
1128
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
1129
+ platform: release.platform,
1130
+ className: "size-3 shrink-0"
1131
+ }),
1132
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1133
+ className: "sr-only",
1134
+ children: platformLabel(release.platform)
1135
+ }),
1136
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleIdDisplay, {
1137
+ bundleId: release.releaseId,
1138
+ className: "min-w-0"
1139
+ })
1140
+ ]
1141
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1142
+ className: "text-xs text-muted-foreground",
1143
+ children: [
1144
+ "Deployed ",
1145
+ formatDeployedAt(release.deployedAtMs),
1146
+ release.targetAppVersion ? ` · ${release.targetAppVersion}` : "",
1147
+ release.enabled ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [" · ", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
1148
+ variant: "outline",
1149
+ children: "Disabled"
1150
+ })] }),
1151
+ release.enabled && !release.live ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [" · ", /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1152
+ className: "inline-flex items-center gap-1 align-middle",
1153
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleOff, {
1154
+ "aria-hidden": "true",
1155
+ className: "size-3"
1156
+ }), "Unreachable"]
1157
+ })] }) : null
1158
+ ]
1159
+ })]
1160
+ });
1161
+ }
1162
+ /**
1163
+ * Picks one of a channel's bundle deployments: live ones first, searchable
1164
+ * by ID or target app version. A full release ID the list leaves out is
1165
+ * looked up in the scope, with the read Release health uses for it.
1166
+ */
1167
+ function ReleaseCombobox({ scope, candidates, excluded = [], onPick, disabled = false, label, placeholder, trigger }) {
1168
+ const [query, setQuery] = (0, import_react.useState)("");
1169
+ const typed = query.trim().toLowerCase();
1170
+ const offered = candidates.filter((release) => !excluded.includes(release.releaseId) && matches(release, typed));
1171
+ const lookupId = offered.length === 0 && isUUIDv7(typed) && !candidates.some((release) => release.releaseId === typed) ? typed : void 0;
1172
+ const lookup = useQuery({
1173
+ queryKey: [
1174
+ "insights",
1175
+ "adoption-release",
1176
+ scope,
1177
+ lookupId,
1178
+ false
1179
+ ],
1180
+ queryFn: () => getAdoptionReleaseRpc({ data: {
1181
+ ...scope,
1182
+ releaseId: lookupId
1183
+ } }),
1184
+ enabled: lookupId !== void 0,
1185
+ staleTime: Number.POSITIVE_INFINITY
1186
+ });
1187
+ const found = lookupId !== void 0 && lookup.data?.release && !excluded.includes(lookup.data.release.releaseId) ? [lookup.data.release] : [];
1188
+ const groups = [
1189
+ {
1190
+ value: "Live",
1191
+ items: offered.filter((release) => release.live)
1192
+ },
1193
+ {
1194
+ value: "Not live",
1195
+ items: offered.filter((release) => !release.live)
1196
+ },
1197
+ {
1198
+ value: "Found",
1199
+ items: found
1200
+ }
1201
+ ].filter((group) => group.items.length > 0);
1202
+ const input = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxInput, {
1203
+ "aria-label": label,
1204
+ className: trigger ? void 0 : "h-11 w-full sm:h-9 sm:w-96 [&_input]:h-full [&_input]:text-base sm:[&_input]:text-xs",
1205
+ disabled,
1206
+ placeholder,
1207
+ showTrigger: !trigger,
1208
+ maxLength: 1024
1209
+ });
1210
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Combobox$1, {
1211
+ items: groups,
1212
+ filter: null,
1213
+ value: null,
1214
+ inputValue: query,
1215
+ onInputValueChange: setQuery,
1216
+ itemToStringLabel: (release) => release.releaseId,
1217
+ onValueChange: (release) => {
1218
+ if (release === null) return;
1219
+ setQuery("");
1220
+ onPick(release);
1221
+ },
1222
+ disabled,
1223
+ children: [trigger ?? input, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ComboboxContent, {
1224
+ className: trigger ? "w-96" : void 0,
1225
+ children: [
1226
+ trigger ? input : null,
1227
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxEmpty, { children: lookupId === void 0 ? "No matching bundles." : lookup.isPending ? "Looking up the release…" : lookup.isError ? "Couldn't look up the release." : `No bundle with this ID in ${scope.channel} · ${platformLabel(scope.platform)}.` }),
1228
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxList, { children: (group) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ComboboxGroup, {
1229
+ items: group.items,
1230
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxLabel, { children: group.value }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxCollection, { children: (release) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxItem, {
1231
+ className: "min-h-11 pr-8 sm:min-h-8",
1232
+ value: release,
1233
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReleaseOption, { release })
1234
+ }, release.releaseId) })]
1235
+ }, group.value) })
1236
+ ]
1237
+ })]
1238
+ });
1239
+ }
1240
+ var DEFAULT_FILTERS = {
1241
+ channel: DEFAULT_INSIGHTS_CHANNEL,
1242
+ platform: "all",
1243
+ appVersion: void 0,
1244
+ releaseId: void 0
1245
+ };
1246
+ var CHIP = "h-11 shrink-0 gap-1.5 px-3 text-sm data-[active=true]:border-primary/60 data-[active=true]:bg-primary/10 sm:h-8 sm:text-xs";
1247
+ var SELECT_CHIP = "shrink-0 gap-1.5 px-3 text-sm data-[active=true]:border-primary/60 data-[active=true]:bg-primary/10 data-[size=default]:h-11 sm:text-xs sm:data-[size=default]:h-8";
1248
+ /** A filter with a button that returns it to its default when it is narrowed. */
1249
+ function Clearable({ name, active, onClear, children }) {
1250
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1251
+ className: "flex shrink-0 items-center",
1252
+ children: [children, active ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1253
+ "aria-label": `Clear ${name}`,
1254
+ className: "size-11 sm:size-8",
1255
+ size: "icon",
1256
+ variant: "ghost",
1257
+ onClick: onClear,
1258
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, { "aria-hidden": "true" })
1259
+ }) : null]
1260
+ });
1261
+ }
1262
+ /**
1263
+ * The Insights filters, above every report. Each applies as it changes, so
1264
+ * what they show is what the reports read; one narrower than its default is
1265
+ * filled and can be cleared on its own, and Reset filters clears them all.
1266
+ */
1267
+ function InsightsControls({ filters, appVersions, releases, onFiltersChange }) {
1268
+ const versions = [...new Set([...appVersions, ...filters.appVersion ? [filters.appVersion] : []])];
1269
+ const channelsQuery = useChannelsQuery();
1270
+ const channels = channelsQuery.data?.map((item) => item.name) ?? [];
1271
+ const narrowed = {
1272
+ channel: filters.channel !== DEFAULT_FILTERS.channel,
1273
+ platform: filters.platform !== DEFAULT_FILTERS.platform,
1274
+ appVersion: filters.appVersion !== void 0,
1275
+ releaseId: filters.releaseId !== void 0
1276
+ };
1277
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1278
+ "aria-label": "Insights filters",
1279
+ className: "flex shrink-0 items-center gap-2 overflow-x-auto border-b bg-background px-3 py-2 sm:flex-wrap sm:overflow-visible sm:px-4",
1280
+ role: "group",
1281
+ children: [
1282
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Combobox$1, {
1283
+ items: channels,
1284
+ value: filters.channel,
1285
+ onValueChange: (value) => {
1286
+ if (value !== null && value !== filters.channel) onFiltersChange({ channel: value });
1287
+ },
1288
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ComboboxTrigger, {
1289
+ "aria-label": `Channel: ${filters.channel}`,
1290
+ "data-active": narrowed.channel,
1291
+ render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1292
+ className: CHIP,
1293
+ variant: "outline"
1294
+ }),
1295
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1296
+ className: "text-muted-foreground",
1297
+ children: "Channel"
1298
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1299
+ className: "max-w-48 truncate",
1300
+ children: filters.channel
1301
+ })]
1302
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ComboboxContent, {
1303
+ className: "w-64",
1304
+ children: [
1305
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxInput, {
1306
+ "aria-label": "Search channels",
1307
+ placeholder: "Search channels…",
1308
+ showTrigger: false,
1309
+ maxLength: 1024
1310
+ }),
1311
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxEmpty, { children: channelsQuery.isPending ? "Loading channels…" : channelsQuery.isError ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1312
+ className: "flex flex-col items-center gap-2 p-2",
1313
+ children: ["Couldn't load channels.", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1314
+ onClick: () => void channelsQuery.refetch(),
1315
+ size: "sm",
1316
+ variant: "outline",
1317
+ children: "Retry channels"
1318
+ })]
1319
+ }) : "No channels found." }),
1320
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxList, { children: (item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComboboxItem, {
1321
+ className: "min-h-11 pr-8 sm:min-h-8",
1322
+ value: item,
1323
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1324
+ className: "truncate",
1325
+ children: item
1326
+ })
1327
+ }, item) })
1328
+ ]
1329
+ })]
1330
+ }),
1331
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Clearable, {
1332
+ name: "platform",
1333
+ active: narrowed.platform,
1334
+ onClear: () => onFiltersChange({ platform: DEFAULT_FILTERS.platform }),
1335
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
1336
+ items: {
1337
+ all: "All platforms",
1338
+ ios: "iOS",
1339
+ android: "Android"
1340
+ },
1341
+ value: filters.platform,
1342
+ onValueChange: (value) => {
1343
+ if ((value === "all" || value === "ios" || value === "android") && value !== filters.platform) onFiltersChange({ platform: value });
1344
+ },
1345
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectTrigger, {
1346
+ "aria-label": "Platform",
1347
+ className: SELECT_CHIP,
1348
+ "data-active": narrowed.platform,
1349
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1350
+ className: "text-muted-foreground",
1351
+ children: "Platform"
1352
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectValue, { children: [filters.platform === "all" ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
1353
+ platform: filters.platform,
1354
+ className: "size-3.5"
1355
+ }), platformLabel(filters.platform)] })]
1356
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectGroup, { children: [
1357
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
1358
+ value: "all",
1359
+ children: "All platforms"
1360
+ }),
1361
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem, {
1362
+ value: "ios",
1363
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
1364
+ platform: "ios",
1365
+ className: "size-3.5"
1366
+ }), "iOS"]
1367
+ }),
1368
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem, {
1369
+ value: "android",
1370
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
1371
+ platform: "android",
1372
+ className: "size-3.5"
1373
+ }), "Android"]
1374
+ })
1375
+ ] }) })]
1376
+ })
1377
+ }),
1378
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Clearable, {
1379
+ name: "app version",
1380
+ active: narrowed.appVersion,
1381
+ onClear: () => onFiltersChange({ appVersion: void 0 }),
1382
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
1383
+ items: Object.fromEntries([["all", "All versions"], ...versions.map((version) => [`version:${version}`, version])]),
1384
+ value: filters.appVersion ? `version:${filters.appVersion}` : "all",
1385
+ onValueChange: (value) => {
1386
+ if (value === null) return;
1387
+ const appVersion = value === "all" ? void 0 : value.slice(8);
1388
+ if (appVersion !== filters.appVersion) onFiltersChange({ appVersion });
1389
+ },
1390
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectTrigger, {
1391
+ "aria-label": "App version",
1392
+ className: SELECT_CHIP,
1393
+ "data-active": narrowed.appVersion,
1394
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1395
+ className: "text-muted-foreground",
1396
+ children: "App version"
1397
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, {})]
1398
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectGroup, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
1399
+ value: "all",
1400
+ children: "All versions"
1401
+ }), versions.map((version) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
1402
+ value: `version:${version}`,
1403
+ children: version
1404
+ }, version))] }) })]
1405
+ })
1406
+ }),
1407
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Clearable, {
1408
+ name: "release",
1409
+ active: narrowed.releaseId,
1410
+ onClear: () => onFiltersChange({ releaseId: void 0 }),
1411
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReleaseCombobox, {
1412
+ scope: {
1413
+ channel: filters.channel,
1414
+ platform: filters.platform
1415
+ },
1416
+ candidates: releases,
1417
+ excluded: filters.releaseId ? [filters.releaseId] : [],
1418
+ label: "Search releases",
1419
+ placeholder: "Search by ID or app version…",
1420
+ onPick: (release) => onFiltersChange({ releaseId: release.releaseId }),
1421
+ trigger: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ComboboxTrigger, {
1422
+ "aria-label": `Release: ${filters.releaseId ?? "All"}`,
1423
+ "data-active": narrowed.releaseId,
1424
+ render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1425
+ className: CHIP,
1426
+ variant: "outline"
1427
+ }),
1428
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1429
+ className: "text-muted-foreground",
1430
+ children: "Release"
1431
+ }), filters.releaseId ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1432
+ className: "max-w-36 truncate font-mono",
1433
+ translate: "no",
1434
+ children: filters.releaseId
1435
+ }) : "All"]
1436
+ })
1437
+ })
1438
+ }),
1439
+ Object.values(narrowed).some(Boolean) ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
1440
+ className: "h-11 shrink-0 sm:h-8",
1441
+ variant: "ghost",
1442
+ onClick: () => onFiltersChange(DEFAULT_FILTERS),
1443
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RotateCcw, {
1444
+ "aria-hidden": "true",
1445
+ "data-icon": "inline-start"
1446
+ }), "Reset filters"]
1447
+ }) : null
1448
+ ]
1449
+ });
1450
+ }
1261
1451
  var percent$1 = (rate) => `${(rate * 100).toFixed(1)}%`;
1262
1452
  /**
1263
1453
  * Rolls a crashing bundle back the way its editor does: disabling its
1264
1454
  * release sends installations running it back to the previous enabled
1265
1455
  * release on their next update check.
1266
1456
  */
1267
- function RollbackButton({ release, rate, crashes, attempts }) {
1457
+ function RollbackButton({ release, rate, crashes, attempts, onRolledBack }) {
1268
1458
  const [open, setOpen] = (0, import_react.useState)(false);
1269
1459
  const [error, setError] = (0, import_react.useState)("");
1270
1460
  const queryClient = useQueryClient();
@@ -1282,6 +1472,7 @@ function RollbackButton({ release, rate, crashes, attempts }) {
1282
1472
  try {
1283
1473
  await preflight.mutateAsync(input);
1284
1474
  await update.mutateAsync(input);
1475
+ onRolledBack?.();
1285
1476
  await queryClient.invalidateQueries({ queryKey: ["insights", "adoption-releases"] });
1286
1477
  toast.success(`${id} rolled back`, { description: "Installations running it return to the previous enabled bundle on their next update check." });
1287
1478
  setOpen(false);
@@ -1365,32 +1556,27 @@ function LineSample({ dashed }) {
1365
1556
  });
1366
1557
  }
1367
1558
  /**
1368
- * Release health: how the newest bundles fare after their deployment, on
1559
+ * Release health: how the live bundles fare after their deployment, on
1369
1560
  * one timeline, per interval. Adoption shows installations downloading each
1370
1561
  * bundle (dashed) and launching it (solid), so the gap is the restarts still
1371
1562
  * to come; Crashes shows them crashing back from it, and offers a rollback
1372
1563
  * when a bundle crashes for too many of them.
1373
1564
  */
1374
- function InsightsOverview({ input, chart, onChartChange, health, onReleasesChange, onWindowChange }) {
1565
+ function InsightsOverview({ input, chart, onChartChange, health, onReleasesChange, onWindowChange, onShowAllPlatforms }) {
1375
1566
  const [active, setActive] = (0, import_react.useState)(null);
1376
1567
  const { period, candidates, releases } = health;
1377
1568
  const ids = (releases ?? []).map(({ release }) => release.releaseId);
1378
1569
  const colorOf = useSeriesColors(ids);
1379
- const addable = (candidates ?? []).filter((candidate) => !ids.includes(candidate.releaseId));
1380
1570
  const full = ids.length >= 4;
1381
1571
  const wider = period.durationMs < 7 * DAY ? "7d" : period.durationMs < 30 * DAY ? "30d" : null;
1382
1572
  const reset = health.isDefault ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
1383
1573
  variant: "ghost",
1384
1574
  size: "sm",
1385
1575
  onClick: () => onReleasesChange(void 0),
1386
- children: [
1387
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RotateCcw, {
1388
- "aria-hidden": "true",
1389
- "data-icon": "inline-start"
1390
- }),
1391
- "Show the newest ",
1392
- 2
1393
- ]
1576
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RotateCcw, {
1577
+ "aria-hidden": "true",
1578
+ "data-icon": "inline-start"
1579
+ }), "Show live bundles"]
1394
1580
  });
1395
1581
  const seriesOf = (bundle) => chart === "adoption" ? bundle.launched : bundle.recovered;
1396
1582
  const loaded = (releases ?? []).filter((bundle) => seriesOf(bundle) !== void 0 && (chart !== "adoption" || bundle.downloaded !== void 0));
@@ -1470,7 +1656,21 @@ function InsightsOverview({ input, chart, onChartChange, health, onReleasesChang
1470
1656
  });
1471
1657
  if (releases.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Empty, {
1472
1658
  className: "min-h-64",
1473
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(EmptyHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyTitle, { children: "No bundles deployed here yet" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyDescription, { children: "Release health follows the bundle deployments of the selected channel and health platform. Deploy one, or choose another in the filters." })] }), reset ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyContent, { children: reset }) : null]
1659
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(EmptyHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(EmptyTitle, { children: [
1660
+ "No bundles deployed to ",
1661
+ input.channel,
1662
+ " ·",
1663
+ " ",
1664
+ platformLabel(input.platform)
1665
+ ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyDescription, { children: "Deploy one, or choose another channel or platform in the filters." })] }), reset || input.platform !== "all" && onShowAllPlatforms ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(EmptyContent, {
1666
+ className: "flex-row flex-wrap justify-center",
1667
+ children: [input.platform !== "all" && onShowAllPlatforms ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
1668
+ variant: "outline",
1669
+ size: "sm",
1670
+ onClick: onShowAllPlatforms,
1671
+ children: "Show all platforms"
1672
+ }) : null, reset]
1673
+ }) : null]
1474
1674
  });
1475
1675
  if (loaded.length === 0) return failed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsErrorAlert, {
1476
1676
  error: failed,
@@ -1521,7 +1721,14 @@ function InsightsOverview({ input, chart, onChartChange, health, onReleasesChang
1521
1721
  className: "flex flex-col gap-4 p-6",
1522
1722
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1523
1723
  className: "flex flex-wrap items-center justify-between gap-4",
1524
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, { children: "Release health" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1724
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1725
+ className: "flex min-w-0 flex-col gap-1",
1726
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, { children: "Release health" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsScope, {
1727
+ channel: input.channel,
1728
+ platform: input.platform,
1729
+ narrower: input.releaseId ? [`Release ${input.releaseId}`] : []
1730
+ })]
1731
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1525
1732
  className: "flex items-center gap-2",
1526
1733
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsPeriodSelector, {
1527
1734
  window: input.window,
@@ -1609,7 +1816,8 @@ function InsightsOverview({ input, chart, onChartChange, health, onReleasesChang
1609
1816
  release: bundle.release,
1610
1817
  rate: bundle.rate,
1611
1818
  crashes: bundle.crashes,
1612
- attempts: bundle.attempts
1819
+ attempts: bundle.attempts,
1820
+ onRolledBack: health.isDefault ? () => onReleasesChange(ids) : void 0
1613
1821
  })
1614
1822
  ]
1615
1823
  })
@@ -1690,10 +1898,26 @@ function InsightsOverview({ input, chart, onChartChange, health, onReleasesChang
1690
1898
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1691
1899
  className: "text-xs break-words text-muted-foreground",
1692
1900
  children: [
1901
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
1902
+ platform: release.platform,
1903
+ className: "mr-1 inline size-3 align-[-1px]"
1904
+ }),
1905
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1906
+ className: "sr-only",
1907
+ children: [platformLabel(release.platform), " "]
1908
+ }),
1693
1909
  "Deployed",
1694
1910
  " ",
1695
1911
  formatDeployedAt(release.deployedAtMs),
1696
- release.targetAppVersion ? ` · ${release.targetAppVersion}` : ""
1912
+ release.targetAppVersion ? ` · ${release.targetAppVersion}` : "",
1913
+ release.enabled && !release.live ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [" · ", /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1914
+ className: "inline-flex items-center gap-1 align-middle",
1915
+ title: "No device gets this bundle with the current delivery settings.",
1916
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleOff, {
1917
+ "aria-hidden": "true",
1918
+ className: "size-3"
1919
+ }), "Unreachable"]
1920
+ })] }) : null
1697
1921
  ]
1698
1922
  })]
1699
1923
  })]
@@ -1743,35 +1967,17 @@ function InsightsOverview({ input, chart, onChartChange, health, onReleasesChang
1743
1967
  }, release.releaseId);
1744
1968
  }) })]
1745
1969
  }),
1746
- addable.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
1747
- items: Object.fromEntries(addable.map((candidate) => [candidate.releaseId, `${candidate.releaseId} · ${formatDeployedAt(candidate.deployedAtMs)}`])),
1748
- value: null,
1749
- disabled: full,
1750
- onValueChange: (value) => {
1751
- if (value !== null) onReleasesChange([...ids, value]);
1970
+ (candidates ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReleaseCombobox, {
1971
+ scope: {
1972
+ channel: input.channel,
1973
+ platform: input.platform
1752
1974
  },
1753
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
1754
- "aria-label": "Add a bundle",
1755
- className: "min-h-11 w-full sm:min-h-9 sm:w-80",
1756
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { placeholder: full ? `Up to 4 bundles` : "Add a bundle" })
1757
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectGroup, { children: addable.map((candidate) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
1758
- value: candidate.releaseId,
1759
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1760
- className: "flex min-w-0 flex-col gap-0.5",
1761
- children: [
1762
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleIdDisplay, { bundleId: candidate.releaseId }),
1763
- " ",
1764
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1765
- className: "text-xs text-muted-foreground",
1766
- children: [
1767
- "Deployed",
1768
- " ",
1769
- formatDeployedAt(candidate.deployedAtMs)
1770
- ]
1771
- })
1772
- ]
1773
- })
1774
- }, candidate.releaseId)) }) })]
1975
+ candidates: candidates ?? [],
1976
+ excluded: ids,
1977
+ disabled: full,
1978
+ label: "Add a bundle",
1979
+ placeholder: full ? `Up to 4 bundles` : "Add a bundle by ID or app version…",
1980
+ onPick: (release) => onReleasesChange([...ids, release.releaseId])
1775
1981
  }) : null
1776
1982
  ] }) : null]
1777
1983
  }),
@@ -2012,7 +2218,7 @@ function FailuresReport({ report, errors }) {
2012
2218
  * Update failures for Release health's scope and period: counts, rates, and
2013
2219
  * the stage and reason breakdown with what else the clients knew.
2014
2220
  */
2015
- function UpdateFailures({ errors, query, onRefresh }) {
2221
+ function UpdateFailures({ errors, scope, query, onRefresh }) {
2016
2222
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("section", {
2017
2223
  "aria-label": "Update failures",
2018
2224
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, {
@@ -2022,7 +2228,10 @@ function UpdateFailures({ errors, query, onRefresh }) {
2022
2228
  className: "flex flex-col gap-1.5 p-4 sm:p-6",
2023
2229
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2024
2230
  className: "flex flex-wrap items-center justify-between gap-4",
2025
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, { children: "Update failures" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
2231
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2232
+ className: "flex min-w-0 flex-col gap-1",
2233
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, { children: "Update failures" }), scope]
2234
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
2026
2235
  "aria-label": "Refresh update failures",
2027
2236
  variant: "ghost",
2028
2237
  size: "icon-lg",
@@ -2056,9 +2265,6 @@ function UpdateFailures({ errors, query, onRefresh }) {
2056
2265
  })
2057
2266
  });
2058
2267
  }
2059
- var listAdoptionReleasesRpc = createServerFn({ method: "GET" }).middleware([consoleAccess]).validator(readAdoptionReleasesInput).handler(createSsrRpc("d11a48f7251928b7819e2376aed4902fa66277bf6d30312d87ca53468332f256"));
2060
- var getAdoptionReleaseRpc = createServerFn({ method: "GET" }).middleware([consoleAccess]).validator(readAdoptionReleaseInput).handler(createSsrRpc("16e3be74610cddd54e5e894419dca16817a7b63c09a4eed15042cdac9c39e1e0"));
2061
- var getBundleEventsRpc = createServerFn({ method: "GET" }).middleware([consoleAccess]).validator(readBundleEventsInput).handler(createSsrRpc("bfcca2365dfef4d071bdcddf97538a2637482c77bb747969dbdcd6769ca98416"));
2062
2268
  /** What each Release health chart reads per bundle. */
2063
2269
  var READS = {
2064
2270
  adoption: ["DOWNLOADED", "LAUNCHED"],
@@ -2087,7 +2293,6 @@ function useReleaseHealth({ platform, channel, window, releaseIds, focusReleaseI
2087
2293
  staleTime: 5 * 6e4
2088
2294
  });
2089
2295
  const candidates = candidatesQuery.data;
2090
- const focusIndex = focusReleaseId === void 0 || candidates === void 0 ? -1 : candidates.findIndex((release) => release.releaseId === focusReleaseId);
2091
2296
  const focusQuery = useQuery({
2092
2297
  queryKey: [
2093
2298
  "insights",
@@ -2101,7 +2306,7 @@ function useReleaseHealth({ platform, channel, window, releaseIds, focusReleaseI
2101
2306
  releaseId: focusReleaseId,
2102
2307
  withPrevious: true
2103
2308
  } }),
2104
- enabled: releaseIds === void 0 && focusReleaseId !== void 0 && candidates !== void 0 && (focusIndex === -1 || focusIndex === candidates.length - 1),
2309
+ enabled: releaseIds === void 0 && focusReleaseId !== void 0,
2105
2310
  staleTime: Number.POSITIVE_INFINITY
2106
2311
  });
2107
2312
  const known = /* @__PURE__ */ new Map();
@@ -2110,8 +2315,8 @@ function useReleaseHealth({ platform, channel, window, releaseIds, focusReleaseI
2110
2315
  ...focusQuery.data?.previous ? [focusQuery.data.previous] : [],
2111
2316
  ...candidates ?? []
2112
2317
  ]) known.set(release.releaseId, release);
2113
- const newest = (list) => list.slice(0, 2).map((release) => release.releaseId);
2114
- const chosen = releaseIds !== void 0 ? releaseIds.slice(0, 4) : candidates === void 0 ? void 0 : focusReleaseId === void 0 ? newest(candidates) : focusIndex !== -1 && focusIndex < candidates.length - 1 ? [focusReleaseId, candidates[focusIndex + 1].releaseId] : focusQuery.data === void 0 ? void 0 : focusQuery.data.release === null ? newest(candidates) : [focusQuery.data.release.releaseId, focusQuery.data.previous?.releaseId].filter((id) => id !== void 0);
2318
+ const live = (list) => defaultAdoptionReleases(list, platform);
2319
+ const chosen = releaseIds !== void 0 ? releaseIds.slice(0, 4) : candidates === void 0 ? void 0 : focusReleaseId === void 0 ? live(candidates) : focusQuery.data === void 0 ? void 0 : focusQuery.data.release === null ? live(candidates) : [focusQuery.data.release.releaseId, focusQuery.data.previous?.releaseId].filter((id) => id !== void 0);
2115
2320
  const labelQueries = useQueries({ queries: (chosen ?? []).filter((releaseId) => !known.has(releaseId) && releaseId !== focusReleaseId).map((releaseId) => ({
2116
2321
  queryKey: [
2117
2322
  "insights",
@@ -2137,7 +2342,8 @@ function useReleaseHealth({ platform, channel, window, releaseIds, focusReleaseI
2137
2342
  })));
2138
2343
  const seriesQueries = useQueries({ queries: reads.map(({ release, type }) => {
2139
2344
  const data = {
2140
- ...scope,
2345
+ platform: release.platform,
2346
+ channel,
2141
2347
  window,
2142
2348
  endMs,
2143
2349
  bundleId: release.bundleId,
@@ -2194,9 +2400,10 @@ function InsightsPage() {
2194
2400
  const retention = useInsightsRetention();
2195
2401
  const usageWindow = search.window ?? "24h";
2196
2402
  const bundleWindow = search.bundleWindow ?? "24h";
2197
- const channel = search.channel ?? search.healthChannel ?? "production";
2403
+ const channel = search.channel ?? "production";
2404
+ const platform = search.platform ?? "all";
2198
2405
  const scope = {
2199
- platform: search.platform ?? "all",
2406
+ platform,
2200
2407
  channel,
2201
2408
  appVersion: search.appVersion
2202
2409
  };
@@ -2209,7 +2416,7 @@ function InsightsPage() {
2209
2416
  window: usageWindow
2210
2417
  };
2211
2418
  const bundleInput = {
2212
- platform: search.healthPlatform ?? "ios",
2419
+ platform,
2213
2420
  channel,
2214
2421
  window: bundleWindow,
2215
2422
  ...search.releaseId === void 0 ? {} : { releaseId: search.releaseId }
@@ -2225,7 +2432,7 @@ function InsightsPage() {
2225
2432
  });
2226
2433
  const chart = search.healthChart ?? "adoption";
2227
2434
  const health = useReleaseHealth({
2228
- platform: bundleInput.platform,
2435
+ platform,
2229
2436
  channel,
2230
2437
  window: bundleWindow,
2231
2438
  releaseIds: comparedBundleIds(search.bundles),
@@ -2235,80 +2442,103 @@ function InsightsPage() {
2235
2442
  const failuresQuery = useUpdateFailuresQuery(bundleInput);
2236
2443
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2237
2444
  className: "flex h-svh min-h-0 flex-col",
2238
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsPageHeader, {
2239
- view: "overview",
2240
- overviewSearch: search
2241
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2242
- className: "min-h-0 min-w-0 flex-1 overflow-y-auto bg-muted/5 px-4 py-6 sm:p-8",
2243
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2244
- className: "mx-auto flex w-full max-w-6xl flex-col gap-6",
2245
- children: [
2246
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsControls, {
2247
- scope,
2248
- releaseScope: bundleInput,
2249
- appVersions: query.data?.appVersions ?? [],
2250
- onFiltersChange: ({ scope, releaseScope }) => void navigate({ search: {
2251
- ...search,
2252
- ...scope,
2253
- appVersion: scope.appVersion,
2254
- healthPlatform: releaseScope.platform,
2255
- healthChannel: void 0,
2256
- releaseId: releaseScope.releaseId,
2445
+ children: [
2446
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsPageHeader, {
2447
+ view: "overview",
2448
+ overviewSearch: search
2449
+ }),
2450
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsControls, {
2451
+ filters: {
2452
+ channel,
2453
+ platform,
2454
+ appVersion: search.appVersion,
2455
+ releaseId: search.releaseId
2456
+ },
2457
+ appVersions: query.data?.appVersions ?? [],
2458
+ releases: health.candidates ?? [],
2459
+ onFiltersChange: (change) => {
2460
+ const next = {
2461
+ ...search,
2462
+ ...change
2463
+ };
2464
+ navigate({ search: {
2465
+ ...next,
2466
+ channel: next.channel === "production" ? void 0 : next.channel,
2467
+ platform: next.platform === "all" ? void 0 : next.platform,
2468
+ ..."channel" in change || "platform" in change ? {
2469
+ releaseId: change.releaseId,
2257
2470
  bundles: void 0
2258
- } })
2259
- }, JSON.stringify({
2260
- scope,
2261
- bundleInput
2262
- })),
2263
- 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: [
2264
- "Only reports since",
2265
- " ",
2266
- new Date(query.data.sinceMs).toLocaleString("en", { timeZone: "UTC" }),
2267
- " ",
2268
- "UTC are available. The active-user count is a lower bound. Choose a shorter period for a more complete view."
2269
- ] })] }) : null,
2270
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppUsage, {
2271
- query,
2272
- search: {
2273
- ...scope,
2471
+ } : {},
2472
+ ..."releaseId" in change ? { bundles: void 0 } : {}
2473
+ } });
2474
+ }
2475
+ }),
2476
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2477
+ className: "min-h-0 min-w-0 flex-1 overflow-y-auto bg-muted/5 px-4 py-6 sm:p-8",
2478
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2479
+ className: "mx-auto flex w-full max-w-6xl flex-col gap-6",
2480
+ children: [
2481
+ 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: [
2482
+ "Only reports since",
2483
+ " ",
2484
+ new Date(query.data.sinceMs).toLocaleString("en", { timeZone: "UTC" }),
2485
+ " ",
2486
+ "UTC are available. The active-user count is a lower bound. Choose a shorter period for a more complete view."
2487
+ ] })] }) : null,
2488
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppUsage, {
2489
+ query,
2490
+ search: {
2491
+ ...scope,
2492
+ window: usageWindow,
2493
+ bundleWindow
2494
+ },
2274
2495
  window: usageWindow,
2275
- bundleWindow
2276
- },
2277
- window: usageWindow,
2278
- onWindowChange: setUsageWindow,
2279
- retention
2280
- }),
2281
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsOverview, {
2282
- input: bundleInput,
2283
- chart,
2284
- onChartChange: (healthChart) => void navigate({ search: {
2285
- ...search,
2286
- healthChart
2287
- } }),
2288
- health,
2289
- onReleasesChange: (ids) => void navigate({ search: {
2290
- ...search,
2291
- bundles: ids?.join(",")
2292
- } }),
2293
- onWindowChange: (window) => void navigate({ search: {
2294
- ...search,
2295
- bundleWindow: window
2296
- } })
2297
- }),
2298
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UpdateFailures, {
2299
- query: failuresQuery,
2300
- errors: failuresQuery.data ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FailureReports, { input: {
2301
- ...bundleInput,
2302
- beforeReceivedAtMs: failuresQuery.data.endMs
2303
- } }, JSON.stringify(bundleInput)) : null,
2304
- onRefresh: () => {
2305
- failuresQuery.refetch();
2306
- queryClient.invalidateQueries({ queryKey: ["insights", "failure-reports"] });
2307
- }
2308
- })
2309
- ]
2496
+ onWindowChange: setUsageWindow,
2497
+ retention
2498
+ }),
2499
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsOverview, {
2500
+ input: bundleInput,
2501
+ chart,
2502
+ onChartChange: (healthChart) => void navigate({ search: {
2503
+ ...search,
2504
+ healthChart
2505
+ } }),
2506
+ health,
2507
+ onReleasesChange: (ids) => void navigate({ search: {
2508
+ ...search,
2509
+ bundles: ids?.join(",")
2510
+ } }),
2511
+ onShowAllPlatforms: () => void navigate({ search: {
2512
+ ...search,
2513
+ platform: void 0,
2514
+ releaseId: void 0,
2515
+ bundles: void 0
2516
+ } }),
2517
+ onWindowChange: (window) => void navigate({ search: {
2518
+ ...search,
2519
+ bundleWindow: window
2520
+ } })
2521
+ }),
2522
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UpdateFailures, {
2523
+ scope: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsScope, {
2524
+ channel,
2525
+ platform,
2526
+ narrower: search.releaseId ? [`Release ${search.releaseId}`] : []
2527
+ }),
2528
+ query: failuresQuery,
2529
+ errors: failuresQuery.data ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FailureReports, { input: {
2530
+ ...bundleInput,
2531
+ beforeReceivedAtMs: failuresQuery.data.endMs
2532
+ } }, JSON.stringify(bundleInput)) : null,
2533
+ onRefresh: () => {
2534
+ failuresQuery.refetch();
2535
+ queryClient.invalidateQueries({ queryKey: ["insights", "failure-reports"] });
2536
+ }
2537
+ })
2538
+ ]
2539
+ })
2310
2540
  })
2311
- })]
2541
+ ]
2312
2542
  });
2313
2543
  }
2314
2544
  //#endregion