@hot-updater/console 1.0.0-rc.9 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/ConsoleFeatureUnavailable-jB4Loglh.js +1 -0
  3. package/.output/public/assets/EventDetails-B6yUsyNA.js +1 -0
  4. package/.output/public/assets/HashValueDisplay-BFX_kY3-.js +1 -0
  5. package/.output/public/assets/InsightsInfo-BNO0WznZ.js +1 -0
  6. package/.output/public/assets/InsightsPageHeader-B892oVml.js +1 -0
  7. package/.output/public/assets/NotFoundPage-DIqCpWa3.js +1 -0
  8. package/.output/public/assets/alert-dialog-Bbk_4-WJ.js +1 -0
  9. package/.output/public/assets/api-DCM0wr6O.js +1 -0
  10. package/.output/public/assets/api-keys-CIO9L_wu.js +1 -0
  11. package/.output/public/assets/api-keys-DVTL_--f.js +1 -0
  12. package/.output/public/assets/arrow-left-CnMYpqB7.js +1 -0
  13. package/.output/public/assets/arrow-up-right-BtffZA6e.js +1 -0
  14. package/.output/public/assets/chevron-right-BqD5wtFb.js +1 -0
  15. package/.output/public/assets/clipboard-DS9ucUQL.js +1 -0
  16. package/.output/public/assets/console-features-api-BH5m_PtQ.js +1 -0
  17. package/.output/public/assets/dialog-Bqbb7B0N.js +1 -0
  18. package/.output/public/assets/dist-LxKJB-Vu.js +1 -0
  19. package/.output/public/assets/empty-CuT2UhFp.js +1 -0
  20. package/.output/public/assets/field-BBDpjIqu.js +1 -0
  21. package/.output/public/assets/index-6o51GkFe.js +11 -0
  22. package/.output/public/assets/input-group-ja_30qaF.js +1 -0
  23. package/.output/public/assets/insights-CmraDzQh.js +1 -0
  24. package/.output/public/assets/insights-CxZ2GCYV.js +2 -0
  25. package/.output/public/assets/insights-api-BigAU9sZ.js +1 -0
  26. package/.output/public/assets/insights-usage-rpc-DYEhsCKT.js +1 -0
  27. package/.output/public/assets/insights-vNRCrfnS.js +42 -0
  28. package/.output/public/assets/insights_.distribution-ChOerfJt.js +1 -0
  29. package/.output/public/assets/insights_.distribution-D-e4iga8.js +1 -0
  30. package/.output/public/assets/insights_.distribution-DZtkibX1.js +2 -0
  31. package/.output/public/assets/installations-BazSJXE0.js +2 -0
  32. package/.output/public/assets/installations-ClHTqdAO.js +1 -0
  33. package/.output/public/assets/installations-DpKsBuHi.js +1 -0
  34. package/.output/public/assets/key-round-wS_SIZ6b.js +1 -0
  35. package/.output/public/assets/list-CgrYiadM.js +1 -0
  36. package/.output/public/assets/refresh-cw-BYgoNCco.js +1 -0
  37. package/.output/public/assets/remote-config-BWwHOkCK.js +1 -0
  38. package/.output/public/assets/remote-config-Br56BODb.js +6 -0
  39. package/.output/public/assets/remote-config-CXOi0LRu.js +1 -0
  40. package/.output/public/assets/routes-B88Erkpk.js +2 -0
  41. package/.output/public/assets/routes-CfzsAleC.js +1 -0
  42. package/.output/public/assets/search-DaTDPW25.js +1 -0
  43. package/.output/public/assets/select-BZZBXMkr.js +1 -0
  44. package/.output/public/assets/sidebar-DNVyJPfX.js +3 -0
  45. package/.output/public/assets/smartphone-DpTFgtKf.js +1 -0
  46. package/.output/public/assets/styles-DTGEAJFv.css +2 -0
  47. package/.output/public/assets/switch-DZ1TPwx-.js +1 -0
  48. package/.output/public/assets/table-Gm4qRWwE.js +1 -0
  49. package/.output/public/assets/tabs-C7FOEjFC.js +1 -0
  50. package/.output/public/assets/undo-2-CmrHjwFc.js +1 -0
  51. package/.output/public/assets/useCompositeListItem-9E4pj_CH.js +1 -0
  52. package/.output/public/assets/valueToPercent-CH8GCHaL.js +1 -0
  53. package/.output/server/__23tanstack-start-server-fn-resolver-BpRlos5Q.mjs +170 -0
  54. package/.output/server/_libs/@base-ui/react+[...].mjs +4389 -4207
  55. package/.output/server/_libs/@tanstack/react-router+[...].mjs +3802 -4022
  56. package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +140 -97
  57. package/.output/server/_libs/lucide-react.mjs +221 -9
  58. package/.output/server/_libs/next-themes.mjs +1 -1
  59. package/.output/server/_libs/react-redux+[...].mjs +1 -1
  60. package/.output/server/_libs/recharts+[...].mjs +483 -901
  61. package/.output/server/_libs/sonner.mjs +1 -1
  62. package/.output/server/_libs/tanstack__history.mjs +68 -41
  63. package/.output/server/_libs/tanstack__query-core.mjs +2010 -309
  64. package/.output/server/_libs/tanstack__react-query.mjs +448 -21
  65. package/.output/server/_libs/verkit.mjs +102 -1
  66. package/.output/server/_ssr/BundleIdDisplay-x94o9rlU.mjs +13 -0
  67. package/.output/server/_ssr/ConsoleFeatureUnavailable-ldW8v8-q.mjs +58 -0
  68. package/.output/server/_ssr/EventDetails-Q8kfj9Nr.mjs +344 -0
  69. package/.output/server/_ssr/{HashValueDisplay-CyMmr0O9.mjs → HashValueDisplay-n1GholGS.mjs} +3 -3
  70. package/.output/server/_ssr/InsightsInfo-DuJFatQO.mjs +75 -0
  71. package/.output/server/_ssr/InsightsPageHeader-BSUX3Ye9.mjs +145 -0
  72. package/.output/server/_ssr/NotFoundPage-DZzOOzS3.mjs +37 -0
  73. package/.output/server/_ssr/{dialog-CKvQ4VIE.mjs → alert-dialog-C43Doa0I.mjs} +6 -83
  74. package/.output/server/_ssr/{api-keys-CzZ7Ont4.mjs → api-keys-B4sYm8dM.mjs} +48 -9
  75. package/.output/server/_ssr/api-keys-CEnM9fqG.mjs +5 -0
  76. package/.output/server/_ssr/api-keys-rpc-BIFuUgMt.mjs +52 -0
  77. package/.output/server/_ssr/api-oZjRn9tP.mjs +186 -0
  78. package/.output/server/_ssr/api-rpc-BNiFRRK8.mjs +240 -0
  79. package/.output/server/_ssr/{auth-BF8mbAgF.mjs → auth-UP0wXs4i.mjs} +1 -1
  80. package/.output/server/_ssr/{auth-rpc-DH6Bhwcz.mjs → auth-rpc-DnZqYxyq.mjs} +6 -6
  81. package/.output/server/_ssr/auth.server-DFpyxRDd.mjs +5374 -0
  82. package/.output/server/_ssr/bundleActivity-M5gzBFNg.mjs +18 -0
  83. package/.output/server/_ssr/config-WVX4IxSR.mjs +15 -0
  84. package/.output/server/_ssr/config.server-DskXCAyI.mjs +35 -0
  85. package/.output/server/_ssr/console-access-BJfAXknd.mjs +17 -0
  86. package/.output/server/_ssr/console-features-Oz0q20jJ.mjs +61 -0
  87. package/.output/server/_ssr/console-features-api-C9kiKvaV.mjs +43 -0
  88. package/.output/server/_ssr/console-features-rpc-P0GL7Z-R.mjs +19 -0
  89. package/.output/server/_ssr/{createServerFn-B2hcXoPM.mjs → createMiddleware-Cm6Tn0_Y.mjs} +67 -56
  90. package/.output/server/_ssr/{createServerRpc-DB29C_5p.mjs → createServerRpc-RLR4H-Fn.mjs} +2 -2
  91. package/.output/server/_ssr/databaseConfig-C5SkAyUy.mjs +5 -0
  92. package/.output/server/_ssr/{databaseJsonValue-d6I3nAS4.mjs → databaseJsonValue-Csbuzc9Q.mjs} +28 -5
  93. package/.output/server/_ssr/databaseRows-CPphvbag.mjs +73 -0
  94. package/.output/server/_ssr/dialog-BbYDMjdC.mjs +84 -0
  95. package/.output/server/_ssr/{dist-CyzPaI5v.mjs → dist-CbQbBIw-.mjs} +140 -7
  96. package/.output/server/_ssr/{downloadBundle-CI7JUKvC.mjs → downloadBundle-CZysjmNW.mjs} +10 -10
  97. package/.output/server/_ssr/empty-CVLGfxuu.mjs +60 -0
  98. package/.output/server/_ssr/{empty-plugin-adapters-B5QvGpEl.mjs → empty-plugin-adapters-CPtwX8a7.mjs} +1 -1
  99. package/.output/server/_ssr/failureReports-DKDhVsXA.mjs +33 -0
  100. package/.output/server/_ssr/field-asw83X0v.mjs +189 -0
  101. package/.output/server/_ssr/getBundleChildren-Cv9iz1As.mjs +22 -0
  102. package/.output/server/_ssr/{input-group-BrGSjz4S.mjs → input-group-CuYJHosb.mjs} +2 -2
  103. package/.output/server/_ssr/insights-B55FG5fm.mjs +14 -0
  104. package/.output/server/_ssr/insights-CWnHIFUV.mjs +5 -0
  105. package/.output/server/_ssr/insights-CpLpedFZ.mjs +2315 -0
  106. package/.output/server/_ssr/insights-api-sjws23WF.mjs +122 -0
  107. package/.output/server/_ssr/insights-errors-BjnFUyZj.mjs +34 -0
  108. package/.output/server/_ssr/insights-failures-rpc-BvbWvqNf.mjs +36 -0
  109. package/.output/server/_ssr/insights-failures-xaa41Buf.mjs +46 -0
  110. package/.output/server/_ssr/insights-recovery-BWQhiZaF.mjs +28 -0
  111. package/.output/server/_ssr/insights-recovery-rpc-GiYXdkaY.mjs +32 -0
  112. package/.output/server/_ssr/{insights-rpc-DDKmIrpV.mjs → insights-rpc-DP_8Rmz4.mjs} +22 -14
  113. package/.output/server/_ssr/insights-usage-BpBM0U0c.mjs +29 -0
  114. package/.output/server/_ssr/insights-usage-rpc-BiolgB8e.mjs +29 -0
  115. package/.output/server/_ssr/insights-usage-rpc-KStL0zOx.mjs +41 -0
  116. package/.output/server/_ssr/insightsUsage-CzhVjsc8.mjs +56 -0
  117. package/.output/server/_ssr/insights_.distribution-CVrnbP3R.mjs +7 -0
  118. package/.output/server/_ssr/insights_.distribution-D2HaNTAA.mjs +16 -0
  119. package/.output/server/_ssr/{insights_.distribution-cIsU-kHG.mjs → insights_.distribution-DN9pR422.mjs} +50 -22
  120. package/.output/server/_ssr/installations-BHmbbnH8.mjs +5 -0
  121. package/.output/server/_ssr/{installations-C5anhyP0.mjs → installations-BSCmFq8Q.mjs} +279 -367
  122. package/.output/server/_ssr/installations-DD1g__Nc.mjs +69 -0
  123. package/.output/server/_ssr/listReleases-BfUBlCHo.mjs +79 -0
  124. package/.output/server/_ssr/plugins-BaD1slHx.mjs +2086 -0
  125. package/.output/server/_ssr/release-adoption-005tQRpo.mjs +55 -0
  126. package/.output/server/_ssr/release-adoption-rpc-BsstqxUp.mjs +41 -0
  127. package/.output/server/_ssr/releaseAdoption-CtOFkwli.mjs +114 -0
  128. package/.output/server/_ssr/releaseManagement-DFMUnDF5.mjs +11 -0
  129. package/.output/server/_ssr/remote-config-C2Ee8lXO.mjs +5 -0
  130. package/.output/server/_ssr/remote-config-Dv1ccMo-.mjs +17 -0
  131. package/.output/server/_ssr/remote-config-of0lGQH8.mjs +2648 -0
  132. package/.output/server/_ssr/remote-config-rpc-BzX31mUS.mjs +115 -0
  133. package/.output/server/_ssr/rolldown-runtime-rSIU-vHC.mjs +13 -0
  134. package/.output/server/_ssr/{router-BuanSsRI.mjs → router-D8NuoJ_I.mjs} +107 -134
  135. package/.output/server/_ssr/{routes-DY-vqU6R.mjs → routes-DgvnFqDy.mjs} +195 -429
  136. package/.output/server/_ssr/routes-V8sDybGf.mjs +409 -0
  137. package/.output/server/_ssr/runtime.server-De0QVWc9.mjs +104 -0
  138. package/.output/server/_ssr/schema-BiAVyEj2.mjs +361 -0
  139. package/.output/server/_ssr/{select-DD2szs3Y.mjs → select-t3iXRwc9.mjs} +3 -3
  140. package/.output/server/_ssr/server-h4VxnExB.mjs +8325 -0
  141. package/.output/server/_ssr/{createSsrRpc-CdlfejmI.mjs → sidebar-DJcErBMo.mjs} +107 -47
  142. package/.output/server/_ssr/ssr.mjs +1 -1
  143. package/.output/server/_ssr/start-BfFbHKMK.mjs +4 -0
  144. package/.output/server/_ssr/switch-E-btwMGt.mjs +43 -0
  145. package/.output/server/_ssr/{table-CTj6n-4F.mjs → table-37Dd23TN.mjs} +2 -2
  146. package/.output/server/_ssr/tabs-DEFnTm95.mjs +45 -0
  147. package/.output/server/_ssr/updateFailures-H6pnnYK4.mjs +40 -0
  148. package/.output/server/_ssr/{uuidv7-D0QFkPL_.mjs → uuidv7-C88s9jWF.mjs} +2 -2
  149. package/.output/server/_tanstack-start-manifest_v-55AGsehh.mjs +139 -0
  150. package/.output/server/index.mjs +309 -211
  151. package/LICENSE +13 -14
  152. package/README.md +80 -71
  153. package/lib/index.d.mts +23 -11
  154. package/lib/vite-internal.mjs +13 -3
  155. package/lib/vite.d.mts +2 -0
  156. package/package.json +14 -14
  157. package/src/components/AppSidebar.tsx +69 -74
  158. package/src/components/ConsoleFeatureUnavailable.tsx +80 -0
  159. package/src/components/NotFoundPage.tsx +7 -7
  160. package/src/components/features/FeatureSlots.tsx +103 -0
  161. package/src/components/features/api-keys/apiKeysFeatureUi.tsx +15 -0
  162. package/src/components/features/bundles/BundleMetadata.tsx +8 -5
  163. package/src/components/features/bundles/RolloutCohortsDialog.tsx +1 -1
  164. package/src/components/features/channels/ChannelManagementDialog.tsx +8 -6
  165. package/src/components/features/insights/AppUsage.tsx +41 -14
  166. package/src/components/features/insights/BuiltinBundle.tsx +36 -0
  167. package/src/components/features/insights/ComparedBundlesChart.tsx +310 -0
  168. package/src/components/features/insights/EstimatedCount.tsx +18 -0
  169. package/src/components/features/insights/EventDetails.tsx +390 -26
  170. package/src/components/features/insights/EventHistoryCard.tsx +111 -25
  171. package/src/components/features/insights/EventHistoryList.tsx +1 -1
  172. package/src/components/features/insights/FailureReports.tsx +407 -0
  173. package/src/components/features/insights/InsightsControls.tsx +240 -142
  174. package/src/components/features/insights/InsightsErrorAlert.tsx +14 -1
  175. package/src/components/features/insights/InsightsOverview.tsx +601 -403
  176. package/src/components/features/insights/InsightsPageHeader.tsx +19 -14
  177. package/src/components/features/insights/InsightsPeriodSelector.tsx +67 -12
  178. package/src/components/features/insights/InstallationHistoryCard.tsx +65 -10
  179. package/src/components/features/insights/InstallationMatchesCard.tsx +1 -1
  180. package/src/components/features/insights/ReleaseActivity.tsx +308 -0
  181. package/src/components/features/insights/ReportingDevicesSummary.tsx +13 -13
  182. package/src/components/features/insights/RollbackButton.tsx +118 -0
  183. package/src/components/features/insights/UpdateFailures.tsx +382 -0
  184. package/src/components/features/insights/insightsFeatureUi.tsx +35 -0
  185. package/src/components/features/releases/ReleaseEditorSheet.tsx +27 -13
  186. package/src/components/features/remote-config/ConditionDialog.tsx +592 -0
  187. package/src/components/features/remote-config/ConditionsCard.tsx +247 -0
  188. package/src/components/features/remote-config/ParameterDialog.tsx +426 -0
  189. package/src/components/features/remote-config/ParametersCard.tsx +254 -0
  190. package/src/components/features/remote-config/PreviewCard.tsx +284 -0
  191. package/src/components/features/remote-config/PublishDialog.tsx +325 -0
  192. package/src/components/features/remote-config/RemoteConfigPage.tsx +353 -0
  193. package/src/components/features/remote-config/RuleSummary.tsx +21 -0
  194. package/src/components/features/remote-config/ValueInput.tsx +151 -0
  195. package/src/components/features/remote-config/VersionsCard.tsx +384 -0
  196. package/src/components/features/remote-config/remoteConfigFeatureUi.tsx +15 -0
  197. package/src/components/features/remote-config/useDateTimeText.ts +16 -0
  198. package/src/components/ui/sidebar.tsx +22 -29
  199. package/src/index.ts +26 -19
  200. package/src/lib/api-keys-api.ts +0 -28
  201. package/src/lib/api-keys-rpc.ts +27 -38
  202. package/src/lib/api-rpc.ts +125 -288
  203. package/src/lib/api.ts +15 -134
  204. package/src/lib/bundle-activity.ts +24 -4
  205. package/src/lib/console-access.ts +20 -0
  206. package/src/lib/console-features-api.ts +68 -0
  207. package/src/lib/console-features-rpc.ts +13 -0
  208. package/src/lib/console-features.ts +98 -0
  209. package/src/lib/insights-api.ts +56 -2
  210. package/src/lib/insights-errors.ts +64 -0
  211. package/src/lib/insights-failures-rpc.ts +36 -0
  212. package/src/lib/insights-failures.ts +130 -0
  213. package/src/lib/insights-recovery-rpc.ts +13 -17
  214. package/src/lib/insights-recovery.ts +12 -36
  215. package/src/lib/insights-retention.ts +21 -0
  216. package/src/lib/insights-rpc.ts +30 -12
  217. package/src/lib/insights-search.ts +50 -4
  218. package/src/lib/insights-usage-rpc.ts +15 -9
  219. package/src/lib/insights-usage.ts +41 -3
  220. package/src/lib/insights-view.ts +34 -5
  221. package/src/lib/release-adoption-api.ts +214 -0
  222. package/src/lib/release-adoption-rpc.ts +49 -0
  223. package/src/lib/release-adoption.ts +164 -0
  224. package/src/lib/remote-config-api.ts +119 -0
  225. package/src/lib/remote-config-draft.ts +666 -0
  226. package/src/lib/remote-config-rpc.ts +196 -0
  227. package/src/lib/scroll-restoration.ts +21 -0
  228. package/src/lib/server/auth.server.ts +13 -5
  229. package/src/lib/server/bundleActivity.ts +27 -28
  230. package/src/lib/server/config.server.ts +21 -76
  231. package/src/lib/server/console-feature-apis.ts +70 -0
  232. package/src/lib/server/console-runtime.server.ts +82 -38
  233. package/src/lib/server/downloadBundle.ts +14 -15
  234. package/src/lib/server/failureReports.ts +50 -0
  235. package/src/lib/server/getBundleChildren.ts +31 -122
  236. package/src/lib/server/insightsUsage.ts +52 -113
  237. package/src/lib/server/listReleases.ts +109 -44
  238. package/src/lib/server/releaseAdoption.ts +151 -0
  239. package/src/lib/server/releaseReachability.ts +8 -8
  240. package/src/lib/server/remoteConfig.ts +75 -0
  241. package/src/lib/server/runtime.server.ts +120 -31
  242. package/src/lib/server/updateFailures.ts +66 -0
  243. package/src/lib/use-dialog-target.ts +27 -0
  244. package/src/routeTree.gen.ts +44 -44
  245. package/src/router.tsx +3 -2
  246. package/src/routes/-installations-search.ts +32 -8
  247. package/src/routes/-releases-search.ts +76 -29
  248. package/src/routes/__root.tsx +7 -1
  249. package/src/routes/api/bundles/$bundleId/download.ts +2 -5
  250. package/src/routes/api-keys.tsx +5 -2
  251. package/src/routes/index.tsx +210 -152
  252. package/src/routes/insights.tsx +91 -23
  253. package/src/routes/insights_.distribution.tsx +50 -9
  254. package/src/routes/installations.tsx +97 -40
  255. package/src/routes/remote-config.tsx +42 -0
  256. package/src/styles.css +9 -0
  257. package/src/vite-internal.ts +15 -3
  258. package/src/vite.ts +2 -0
  259. package/.output/public/assets/HashValueDisplay-BpTc3Oyg.js +0 -1
  260. package/.output/public/assets/InsightsPageHeader-DC2eEst8.js +0 -1
  261. package/.output/public/assets/api-CdUyUqzR.js +0 -42
  262. package/.output/public/assets/api-keys-64PM9PwW.js +0 -1
  263. package/.output/public/assets/api-keys-api-BeaJnvY5.js +0 -1
  264. package/.output/public/assets/arrow-left-04Rh8Zgp.js +0 -1
  265. package/.output/public/assets/card-CYHs5FZv.js +0 -1
  266. package/.output/public/assets/chart-no-axes-combined-DwyMsVFW.js +0 -1
  267. package/.output/public/assets/createServerFn-DU71xucx.js +0 -3
  268. package/.output/public/assets/dialog-IVMkBRCq.js +0 -1
  269. package/.output/public/assets/dist-BR4GzeEu.js +0 -1
  270. package/.output/public/assets/field-B3CYDpjg.js +0 -1
  271. package/.output/public/assets/index-Cb_D1D6N.js +0 -10
  272. package/.output/public/assets/info-C3y9HNoU.js +0 -1
  273. package/.output/public/assets/input-group-nLZylhWF.js +0 -1
  274. package/.output/public/assets/insights-Bm9v8uO3.js +0 -2
  275. package/.output/public/assets/insights-DS3iO7Up.js +0 -1
  276. package/.output/public/assets/insights-search-Byj3g3Mp.js +0 -1
  277. package/.output/public/assets/insights-usage-rpc-BaGR3lgq.js +0 -1
  278. package/.output/public/assets/insights_.distribution-B1i6cjlQ.js +0 -1
  279. package/.output/public/assets/insights_.distribution-C1Wf27eB.js +0 -2
  280. package/.output/public/assets/installations-CkFW4fV9.js +0 -2
  281. package/.output/public/assets/installations-DxTaIcHD.js +0 -1
  282. package/.output/public/assets/lazyRouteComponent-BapzjYnb.js +0 -1
  283. package/.output/public/assets/list-Dqt53fWZ.js +0 -1
  284. package/.output/public/assets/loader-circle-CUKfUqow.js +0 -1
  285. package/.output/public/assets/preload-helper-ClBMtM1g.js +0 -1
  286. package/.output/public/assets/refresh-cw-CNE_2PnD.js +0 -1
  287. package/.output/public/assets/routes-Cgtvspjg.js +0 -2
  288. package/.output/public/assets/routes-O2r2UUN8.js +0 -1
  289. package/.output/public/assets/shield-check-BFcJ46kz.js +0 -1
  290. package/.output/public/assets/signing-BgspC-xP.js +0 -1
  291. package/.output/public/assets/styles-DnrWuBwd.css +0 -2
  292. package/.output/public/assets/table-BuWQgxJJ.js +0 -1
  293. package/.output/public/assets/useMutation-CSV9NCUl.js +0 -1
  294. package/.output/public/assets/useOpenInteractionType-D5kduCTD.js +0 -1
  295. package/.output/public/assets/valueToPercent-BAtcbSLk.js +0 -1
  296. package/.output/server/__23tanstack-start-server-fn-resolver-DBRojHaG.mjs +0 -150
  297. package/.output/server/_ssr/InsightsPageHeader-mtxVuZKK.mjs +0 -64
  298. package/.output/server/_ssr/api-BfCgYlj5.mjs +0 -332
  299. package/.output/server/_ssr/api-keys-api-C3oeq01R.mjs +0 -60
  300. package/.output/server/_ssr/api-keys-rpc-CdPFdGOx.mjs +0 -136
  301. package/.output/server/_ssr/api-rpc-B2E1ZIG5.mjs +0 -1399
  302. package/.output/server/_ssr/auth.server-CmicHh0J.mjs +0 -47
  303. package/.output/server/_ssr/bundle-signing-rpc-Di7-I6fE.mjs +0 -16
  304. package/.output/server/_ssr/bundle-signing.server-Uqt1OF49.mjs +0 -12
  305. package/.output/server/_ssr/bundleActivity-pBrPSG01.mjs +0 -28
  306. package/.output/server/_ssr/card-Bemj0dpY.mjs +0 -108
  307. package/.output/server/_ssr/config-Dv8qLDZ0.mjs +0 -13
  308. package/.output/server/_ssr/config.server-B8WsT3s0.mjs +0 -368
  309. package/.output/server/_ssr/databaseRows-5bCRTxm-.mjs +0 -126
  310. package/.output/server/_ssr/deleteBundle-8X-jq7Z7.mjs +0 -66
  311. package/.output/server/_ssr/field-BrQ2u0fT.mjs +0 -74
  312. package/.output/server/_ssr/getBundleChildren-DoL8l_AS.mjs +0 -50
  313. package/.output/server/_ssr/insights-BwQ-wF2a.mjs +0 -10
  314. package/.output/server/_ssr/insights-gr1I_dJU.mjs +0 -938
  315. package/.output/server/_ssr/insights-recovery-Cv6RSyeR.mjs +0 -21
  316. package/.output/server/_ssr/insights-recovery-rpc-DOzxfYfv.mjs +0 -37
  317. package/.output/server/_ssr/insights-search-1czJMZgk.mjs +0 -21
  318. package/.output/server/_ssr/insights-usage-rpc-4ImOq_0p.mjs +0 -184
  319. package/.output/server/_ssr/insights-usage-rpc-BUruODjE.mjs +0 -24
  320. package/.output/server/_ssr/insightsContract-B09K_Px1.mjs +0 -303
  321. package/.output/server/_ssr/insightsHistory-Ci4rYAgl.mjs +0 -41
  322. package/.output/server/_ssr/insightsRecovery-CaghWL2t.mjs +0 -113
  323. package/.output/server/_ssr/insightsUsage-DNIj6jxF.mjs +0 -71
  324. package/.output/server/_ssr/insights_.distribution-VmHuikRE.mjs +0 -10
  325. package/.output/server/_ssr/installations-HXMF_kKf.mjs +0 -29
  326. package/.output/server/_ssr/routes-Dru3IRj0.mjs +0 -37
  327. package/.output/server/_ssr/runtime.server-CsiKFp_Z.mjs +0 -436
  328. package/.output/server/_ssr/server-DJm72Kj-.mjs +0 -7468
  329. package/.output/server/_ssr/signing-H8Lg4lPe.mjs +0 -133
  330. package/.output/server/_ssr/start-DpViQGnt.mjs +0 -4
  331. package/.output/server/_tanstack-start-manifest_v-DsFcTLw_.mjs +0 -125
  332. package/src/components/features/bundles/BundleActivityChart.tsx +0 -97
  333. package/src/components/features/bundles/BundleInsightsSummary.tsx +0 -126
  334. package/src/components/features/signing/BundleSigningPage.tsx +0 -139
  335. package/src/lib/bundle-signing-api.ts +0 -14
  336. package/src/lib/bundle-signing-rpc.ts +0 -16
  337. package/src/lib/server/bundle-signing.server.ts +0 -23
  338. package/src/lib/server/deleteBundle.ts +0 -145
  339. package/src/lib/server/insightsHistory.ts +0 -52
  340. package/src/lib/server/insightsRecovery.ts +0 -187
  341. package/src/routes/signing.tsx +0 -37
@@ -0,0 +1,151 @@
1
+ import { Braces } from "lucide-react";
2
+ import type { ReactNode } from "react";
3
+
4
+ import { Button } from "@/components/ui/button";
5
+ import { Field, FieldError, FieldLabel } from "@/components/ui/field";
6
+ import { Input } from "@/components/ui/input";
7
+ import { Switch } from "@/components/ui/switch";
8
+ import { Textarea } from "@/components/ui/textarea";
9
+ import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
10
+ import {
11
+ type RemoteConfigParameterValue,
12
+ type RemoteConfigValueType,
13
+ valueTextError,
14
+ } from "@/lib/remote-config-draft";
15
+
16
+ /** A value's starting text for a type: what a new value of that type holds. */
17
+ export const initialValueText = (valueType: RemoteConfigValueType): string =>
18
+ valueType === "BOOLEAN"
19
+ ? "false"
20
+ : valueType === "NUMBER"
21
+ ? "0"
22
+ : valueType === "JSON"
23
+ ? "{}"
24
+ : "";
25
+
26
+ /** The problem with a value, or null; an in-app default has none. */
27
+ export const parameterValueError = (
28
+ valueType: RemoteConfigValueType,
29
+ value: RemoteConfigParameterValue,
30
+ ): string | null =>
31
+ "value" in value ? valueTextError(valueType, value.value) : null;
32
+
33
+ /**
34
+ * Edits one value of a parameter, by its type, with the option to leave the
35
+ * app's in-app default in place.
36
+ */
37
+ export function ValueInput({
38
+ id,
39
+ label,
40
+ valueType,
41
+ value,
42
+ onChange,
43
+ showErrors,
44
+ }: {
45
+ readonly id: string;
46
+ /** The visible label; it names the toggle group too when that is text. */
47
+ readonly label: ReactNode;
48
+ readonly valueType: RemoteConfigValueType;
49
+ readonly value: RemoteConfigParameterValue;
50
+ readonly onChange: (value: RemoteConfigParameterValue) => void;
51
+ /** Show a problem once the user tried to save. */
52
+ readonly showErrors: boolean;
53
+ }) {
54
+ const inAppDefault = !("value" in value);
55
+ const text = "value" in value ? value.value : "";
56
+ const error = showErrors ? parameterValueError(valueType, value) : null;
57
+ const setText = (next: string) => onChange({ value: next });
58
+
59
+ return (
60
+ <Field data-invalid={error !== null || undefined}>
61
+ <div className="flex flex-wrap items-center justify-between gap-2">
62
+ <FieldLabel htmlFor={id} id={`${id}-label`}>
63
+ {label}
64
+ </FieldLabel>
65
+ <div className="flex min-h-11 items-center gap-2 text-xs text-muted-foreground sm:min-h-0">
66
+ <Switch
67
+ checked={inAppDefault}
68
+ id={`${id}-in-app-default`}
69
+ onCheckedChange={(checked) =>
70
+ onChange(
71
+ checked
72
+ ? { useInAppDefault: true }
73
+ : { value: initialValueText(valueType) },
74
+ )
75
+ }
76
+ />
77
+ <label htmlFor={`${id}-in-app-default`}>Use in-app default</label>
78
+ </div>
79
+ </div>
80
+ {inAppDefault ? (
81
+ <p
82
+ className="rounded-md border border-dashed px-3 py-2 text-xs text-muted-foreground"
83
+ id={id}
84
+ >
85
+ Devices keep the default set in the app's{" "}
86
+ <code className="font-mono">remoteConfig({"{ defaults }"})</code>.
87
+ </p>
88
+ ) : valueType === "BOOLEAN" ? (
89
+ <ToggleGroup
90
+ aria-labelledby={`${id}-label`}
91
+ id={id}
92
+ onValueChange={(values) => {
93
+ const [next] = values as string[];
94
+ if (next === "true" || next === "false") setText(next);
95
+ }}
96
+ value={[text]}
97
+ variant="outline"
98
+ >
99
+ <ToggleGroupItem className="min-h-11 px-4 sm:min-h-8" value="true">
100
+ true
101
+ </ToggleGroupItem>
102
+ <ToggleGroupItem className="min-h-11 px-4 sm:min-h-8" value="false">
103
+ false
104
+ </ToggleGroupItem>
105
+ </ToggleGroup>
106
+ ) : valueType === "NUMBER" ? (
107
+ <Input
108
+ aria-invalid={error !== null || undefined}
109
+ autoComplete="off"
110
+ className="min-h-11 font-mono sm:min-h-9"
111
+ id={id}
112
+ inputMode="decimal"
113
+ onChange={(event) => setText(event.target.value)}
114
+ value={text}
115
+ />
116
+ ) : valueType === "JSON" ? (
117
+ <div className="flex flex-col gap-2">
118
+ <Textarea
119
+ aria-invalid={error !== null || undefined}
120
+ className="min-h-24 font-mono text-xs"
121
+ id={id}
122
+ onChange={(event) => setText(event.target.value)}
123
+ spellCheck={false}
124
+ value={text}
125
+ />
126
+ <Button
127
+ className="self-start"
128
+ disabled={valueTextError("JSON", text) !== null}
129
+ onClick={() => setText(JSON.stringify(JSON.parse(text), null, 2))}
130
+ size="sm"
131
+ type="button"
132
+ variant="outline"
133
+ >
134
+ <Braces data-icon="inline-start" />
135
+ Format JSON
136
+ </Button>
137
+ </div>
138
+ ) : (
139
+ <Textarea
140
+ aria-invalid={error !== null || undefined}
141
+ className="min-h-11 text-sm sm:min-h-9"
142
+ id={id}
143
+ onChange={(event) => setText(event.target.value)}
144
+ rows={2}
145
+ value={text}
146
+ />
147
+ )}
148
+ <FieldError>{error}</FieldError>
149
+ </Field>
150
+ );
151
+ }
@@ -0,0 +1,384 @@
1
+ import dayjs from "dayjs";
2
+ import {
3
+ AlertTriangle,
4
+ Clipboard,
5
+ Eye,
6
+ History,
7
+ Loader2,
8
+ RefreshCw,
9
+ RotateCcw,
10
+ } from "lucide-react";
11
+ import { type MouseEvent, useState } from "react";
12
+ import { toast } from "sonner";
13
+
14
+ import {
15
+ Alert,
16
+ AlertAction,
17
+ AlertDescription,
18
+ AlertTitle,
19
+ } from "@/components/ui/alert";
20
+ import {
21
+ AlertDialog,
22
+ AlertDialogAction,
23
+ AlertDialogCancel,
24
+ AlertDialogContent,
25
+ AlertDialogDescription,
26
+ AlertDialogFooter,
27
+ AlertDialogHeader,
28
+ AlertDialogTitle,
29
+ } from "@/components/ui/alert-dialog";
30
+ import { Badge } from "@/components/ui/badge";
31
+ import { Button } from "@/components/ui/button";
32
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
33
+ import {
34
+ Dialog,
35
+ DialogContent,
36
+ DialogDescription,
37
+ DialogHeader,
38
+ DialogTitle,
39
+ } from "@/components/ui/dialog";
40
+ import {
41
+ Empty,
42
+ EmptyContent,
43
+ EmptyDescription,
44
+ EmptyHeader,
45
+ EmptyMedia,
46
+ EmptyTitle,
47
+ } from "@/components/ui/empty";
48
+ import { Skeleton } from "@/components/ui/skeleton";
49
+ import {
50
+ useRemoteConfigVersionQuery,
51
+ useRemoteConfigVersionsQuery,
52
+ useRollbackRemoteConfigMutation,
53
+ } from "@/lib/remote-config-api";
54
+ import { useDialogTarget } from "@/lib/use-dialog-target";
55
+
56
+ /** A publish time: the date and time, with the exact UTC time on hover. */
57
+ function PublishedAt({ ms }: { readonly ms: number }) {
58
+ const date = new Date(ms);
59
+ return (
60
+ <time
61
+ className="tabular-nums"
62
+ dateTime={date.toISOString()}
63
+ title={`${date.toISOString().replace("T", " ").slice(0, 19)} UTC`}
64
+ >
65
+ {dayjs(ms).format("YYYY/MM/DD HH:mm")}
66
+ </time>
67
+ );
68
+ }
69
+
70
+ function VersionDialog({
71
+ open,
72
+ version,
73
+ onOpenChange,
74
+ }: {
75
+ readonly open: boolean;
76
+ /** Kept while the dialog animates closed. */
77
+ readonly version: number | null;
78
+ readonly onOpenChange: (open: boolean) => void;
79
+ }) {
80
+ const detail = useRemoteConfigVersionQuery(version);
81
+ const json =
82
+ detail.data === null || detail.data === undefined
83
+ ? null
84
+ : JSON.stringify(detail.data.template, null, 2);
85
+ return (
86
+ <Dialog onOpenChange={onOpenChange} open={open}>
87
+ <DialogContent className="max-h-[calc(100svh-2rem)] grid-rows-[auto_minmax(0,1fr)] sm:max-w-2xl">
88
+ <DialogHeader>
89
+ <DialogTitle>Version {version}</DialogTitle>
90
+ <DialogDescription>
91
+ The template as it was published, in the JSON the admin API takes.
92
+ </DialogDescription>
93
+ </DialogHeader>
94
+ {detail.isLoading ? (
95
+ <Skeleton className="h-48 w-full" />
96
+ ) : detail.isError ? (
97
+ <Alert variant="destructive">
98
+ <AlertTriangle />
99
+ <AlertTitle>This version couldn't be loaded</AlertTitle>
100
+ <AlertDescription>
101
+ Check the connection and try again.
102
+ </AlertDescription>
103
+ <AlertAction>
104
+ <Button
105
+ onClick={() => void detail.refetch()}
106
+ size="xs"
107
+ variant="outline"
108
+ >
109
+ <RefreshCw data-icon="inline-start" />
110
+ Retry
111
+ </Button>
112
+ </AlertAction>
113
+ </Alert>
114
+ ) : json === null ? (
115
+ <p className="text-sm text-muted-foreground">
116
+ Version {version} no longer exists.
117
+ </p>
118
+ ) : (
119
+ <div className="flex min-h-0 flex-col gap-2">
120
+ <pre className="min-h-0 overflow-auto rounded-md bg-muted p-3 font-mono text-xs">
121
+ {json}
122
+ </pre>
123
+ <Button
124
+ className="self-end"
125
+ onClick={() => {
126
+ navigator.clipboard.writeText(json).then(
127
+ () => toast.success("Template JSON copied"),
128
+ () => toast.error("Unable to copy the template"),
129
+ );
130
+ }}
131
+ size="sm"
132
+ variant="outline"
133
+ >
134
+ <Clipboard data-icon="inline-start" />
135
+ Copy JSON
136
+ </Button>
137
+ </div>
138
+ )}
139
+ </DialogContent>
140
+ </Dialog>
141
+ );
142
+ }
143
+
144
+ /** Published versions, newest first, each viewable and restorable. */
145
+ export function VersionsCard({
146
+ activeVersion,
147
+ hasDraft,
148
+ onRolledBack,
149
+ onStartEditing,
150
+ }: {
151
+ readonly activeVersion: number;
152
+ /** Unpublished edits exist; a rollback discards them. */
153
+ readonly hasDraft: boolean;
154
+ readonly onRolledBack: () => void;
155
+ readonly onStartEditing: () => void;
156
+ }) {
157
+ const versions = useRemoteConfigVersionsQuery({ enabled: true });
158
+ const rollback = useRollbackRemoteConfigMutation();
159
+ const [viewing, setViewing] = useState<number | null>(null);
160
+ // The version to restore and the one the rollback publishes, taken when
161
+ // the dialog opens.
162
+ const [restoring, setRestoring] = useState<{
163
+ readonly source: number;
164
+ readonly next: number;
165
+ } | null>(null);
166
+ // Kept while each dialog animates closed.
167
+ const shownViewing = useDialogTarget(viewing);
168
+ const shownRestoring = useDialogTarget(restoring);
169
+ const rows = versions.data?.pages.flatMap((page) => page.versions) ?? [];
170
+
171
+ const restore = async (event: MouseEvent<HTMLButtonElement>) => {
172
+ event.preventDefault();
173
+ if (restoring === null) return;
174
+ try {
175
+ const result = await rollback.mutateAsync({
176
+ version: restoring.source,
177
+ baseVersion: restoring.next - 1,
178
+ });
179
+ if (result.status === "published") {
180
+ toast.success(
181
+ `Version ${result.version.version} restores version ${restoring.source}`,
182
+ );
183
+ onRolledBack();
184
+ } else if (result.status === "conflict") {
185
+ toast.error(
186
+ `Version ${result.currentVersion} was published meanwhile. Review it, then roll back again.`,
187
+ );
188
+ } else {
189
+ toast.error("That version no longer exists.");
190
+ }
191
+ setRestoring(null);
192
+ } catch (error) {
193
+ toast.error(
194
+ error instanceof Error ? error.message : "Unable to roll back",
195
+ );
196
+ }
197
+ };
198
+
199
+ return (
200
+ <Card className="overflow-hidden shadow-sm">
201
+ <CardHeader className="border-b px-4 py-3 sm:px-6">
202
+ <CardTitle className="text-sm">
203
+ <h2>Versions</h2>
204
+ </CardTitle>
205
+ </CardHeader>
206
+ <CardContent className="p-0">
207
+ {versions.isError ? (
208
+ <div className="p-4">
209
+ <Alert variant="destructive">
210
+ <AlertTriangle />
211
+ <AlertTitle>Versions couldn't be loaded</AlertTitle>
212
+ <AlertDescription>
213
+ Check the connection and try again.
214
+ </AlertDescription>
215
+ <AlertAction>
216
+ <Button
217
+ onClick={() => void versions.refetch()}
218
+ size="xs"
219
+ variant="outline"
220
+ >
221
+ <RefreshCw data-icon="inline-start" />
222
+ Retry
223
+ </Button>
224
+ </AlertAction>
225
+ </Alert>
226
+ </div>
227
+ ) : versions.isLoading ? (
228
+ <div className="flex flex-col gap-3 p-4 sm:p-6">
229
+ <span className="sr-only">Loading versions</span>
230
+ {Array.from({ length: 3 }, (_, index) => (
231
+ <Skeleton className="h-10 w-full" key={index} />
232
+ ))}
233
+ </div>
234
+ ) : rows.length === 0 ? (
235
+ <Empty className="py-10">
236
+ <EmptyHeader>
237
+ <EmptyMedia variant="icon">
238
+ <History />
239
+ </EmptyMedia>
240
+ <EmptyTitle>No versions yet</EmptyTitle>
241
+ <EmptyDescription>
242
+ Your first publish becomes version 1. Every publish after it is
243
+ kept here, ready to roll back to.
244
+ </EmptyDescription>
245
+ </EmptyHeader>
246
+ <EmptyContent>
247
+ <Button className="min-h-11 sm:min-h-7" onClick={onStartEditing}>
248
+ Add a parameter
249
+ </Button>
250
+ </EmptyContent>
251
+ </Empty>
252
+ ) : (
253
+ <>
254
+ <ol aria-label="Versions" className="divide-y">
255
+ {rows.map((row) => (
256
+ <li
257
+ className="grid gap-2 px-4 py-3 sm:px-6 md:grid-cols-[minmax(0,1fr)_auto] md:items-center"
258
+ key={row.version}
259
+ >
260
+ <div className="min-w-0">
261
+ <div className="flex flex-wrap items-center gap-2">
262
+ <span className="text-sm font-medium tabular-nums">
263
+ Version {row.version}
264
+ </span>
265
+ {row.version === activeVersion ? (
266
+ <Badge>Active</Badge>
267
+ ) : null}
268
+ {row.updateType === "ROLLBACK" ? (
269
+ <Badge variant="outline">
270
+ Rollback to v{row.rollbackSource}
271
+ </Badge>
272
+ ) : null}
273
+ </div>
274
+ <p className="mt-0.5 text-xs text-muted-foreground">
275
+ <PublishedAt ms={row.createdAtMs} />
276
+ {row.description === null ||
277
+ // The default a rollback gets, which the badge says.
278
+ (row.updateType === "ROLLBACK" &&
279
+ row.description ===
280
+ `Rollback to version ${row.rollbackSource}`) ? null : (
281
+ <> · {row.description}</>
282
+ )}
283
+ </p>
284
+ </div>
285
+ <div className="flex gap-1 md:justify-end">
286
+ <Button
287
+ aria-label={`View version ${row.version}`}
288
+ className="min-h-11 md:min-h-7"
289
+ onClick={() => setViewing(row.version)}
290
+ size="sm"
291
+ variant="ghost"
292
+ >
293
+ <Eye data-icon="inline-start" />
294
+ View
295
+ </Button>
296
+ <Button
297
+ aria-label={`Roll back to version ${row.version}`}
298
+ className="min-h-11 md:min-h-7"
299
+ disabled={row.version === activeVersion}
300
+ onClick={() =>
301
+ setRestoring({
302
+ source: row.version,
303
+ next: activeVersion + 1,
304
+ })
305
+ }
306
+ size="sm"
307
+ variant="outline"
308
+ >
309
+ <RotateCcw data-icon="inline-start" />
310
+ Roll back
311
+ </Button>
312
+ </div>
313
+ </li>
314
+ ))}
315
+ </ol>
316
+ {versions.hasNextPage ? (
317
+ <div className="border-t px-4 py-3 sm:px-6">
318
+ <Button
319
+ className="min-h-11 w-full sm:min-h-7 sm:w-auto"
320
+ disabled={versions.isFetchingNextPage}
321
+ onClick={() => void versions.fetchNextPage()}
322
+ variant="outline"
323
+ >
324
+ {versions.isFetchingNextPage ? (
325
+ <Loader2
326
+ className="animate-spin"
327
+ data-icon="inline-start"
328
+ />
329
+ ) : null}
330
+ Load older versions
331
+ </Button>
332
+ </div>
333
+ ) : null}
334
+ </>
335
+ )}
336
+ </CardContent>
337
+ <VersionDialog
338
+ onOpenChange={(open) => {
339
+ if (!open) setViewing(null);
340
+ }}
341
+ open={viewing !== null}
342
+ version={shownViewing}
343
+ />
344
+ <AlertDialog
345
+ onOpenChange={(open) => {
346
+ if (!rollback.isPending && !open) setRestoring(null);
347
+ }}
348
+ open={restoring !== null}
349
+ >
350
+ <AlertDialogContent>
351
+ <AlertDialogHeader>
352
+ <AlertDialogTitle>
353
+ Roll back to version {shownRestoring?.source}?
354
+ </AlertDialogTitle>
355
+ <AlertDialogDescription>
356
+ Version {shownRestoring?.next} will publish a copy of version{" "}
357
+ {shownRestoring?.source}, and devices get its values on their next
358
+ fetch.
359
+ {hasDraft ? " Your unpublished changes are discarded." : ""}
360
+ </AlertDialogDescription>
361
+ </AlertDialogHeader>
362
+ <AlertDialogFooter>
363
+ <AlertDialogCancel disabled={rollback.isPending}>
364
+ Cancel
365
+ </AlertDialogCancel>
366
+ <AlertDialogAction
367
+ disabled={rollback.isPending}
368
+ onClick={(event: MouseEvent<HTMLButtonElement>) =>
369
+ void restore(event)
370
+ }
371
+ >
372
+ {rollback.isPending ? (
373
+ <Loader2 className="animate-spin" data-icon="inline-start" />
374
+ ) : (
375
+ <RotateCcw data-icon="inline-start" />
376
+ )}
377
+ Roll back
378
+ </AlertDialogAction>
379
+ </AlertDialogFooter>
380
+ </AlertDialogContent>
381
+ </AlertDialog>
382
+ </Card>
383
+ );
384
+ }
@@ -0,0 +1,15 @@
1
+ import { Link } from "@tanstack/react-router";
2
+ import { SlidersHorizontal } from "lucide-react";
3
+
4
+ import type { ConsoleFeatureUiRegistry } from "../FeatureSlots";
5
+
6
+ /** What Remote Config adds: its navigation item. */
7
+ export const remoteConfigFeatureUi = {
8
+ remoteConfig: {
9
+ navigation: {
10
+ icon: <SlidersHorizontal />,
11
+ paths: ["/remote-config"],
12
+ link: ({ onClick }) => <Link to="/remote-config" onClick={onClick} />,
13
+ },
14
+ },
15
+ } satisfies ConsoleFeatureUiRegistry;
@@ -0,0 +1,16 @@
1
+ import { useEffect, useMemo, useState } from "react";
2
+
3
+ import { createDateTimeText } from "@/lib/remote-config-draft";
4
+
5
+ /**
6
+ * Dates in the viewer's time zone, with its name. The server renders in UTC,
7
+ * and the page switches once mounted, so both render the same first.
8
+ */
9
+ export function useDateTimeText() {
10
+ const [timeZone, setTimeZone] = useState("UTC");
11
+ useEffect(() => {
12
+ setTimeZone(Intl.DateTimeFormat().resolvedOptions().timeZone);
13
+ }, []);
14
+ const dateTimeText = useMemo(() => createDateTimeText(timeZone), [timeZone]);
15
+ return { timeZone, dateTimeText };
16
+ }
@@ -9,6 +9,13 @@ import * as React from "react";
9
9
  import { Button } from "@/components/ui/button";
10
10
  import { Input } from "@/components/ui/input";
11
11
  import { Separator } from "@/components/ui/separator";
12
+ import {
13
+ Sheet,
14
+ SheetContent,
15
+ SheetDescription,
16
+ SheetHeader,
17
+ SheetTitle,
18
+ } from "@/components/ui/sheet";
12
19
  import { Skeleton } from "@/components/ui/skeleton";
13
20
  import {
14
21
  Tooltip,
@@ -220,44 +227,30 @@ function Sidebar({
220
227
  }
221
228
 
222
229
  if (isMobile) {
223
- if (!openMobile) {
224
- return null;
225
- }
226
-
227
230
  return (
228
- <div
229
- data-sidebar="sidebar"
230
- data-slot="sidebar"
231
- data-mobile="true"
232
- className="fixed inset-0 z-50 md:hidden"
233
- {...props}
234
- >
235
- <button
236
- type="button"
237
- aria-label="Close sidebar"
238
- className="absolute inset-0 bg-sidebar/55 supports-backdrop-filter:backdrop-blur-sm"
239
- onClick={() => setOpenMobile(false)}
240
- />
241
- <dialog
242
- open
243
- aria-modal="true"
244
- aria-label="Sidebar"
245
- className={cn(
246
- "bg-sidebar text-sidebar-foreground absolute inset-y-0 z-10 flex w-(--sidebar-width) flex-col shadow-lg",
247
- side === "left" ? "left-0" : "right-0",
248
- )}
231
+ <Sheet open={openMobile} onOpenChange={setOpenMobile}>
232
+ <SheetContent
233
+ data-sidebar="sidebar"
234
+ data-slot="sidebar"
235
+ data-mobile="true"
236
+ className="bg-sidebar text-sidebar-foreground w-(--sidebar-width) max-w-[calc(100vw-2rem)] p-0"
249
237
  style={
250
238
  {
251
239
  "--sidebar-width": SIDEBAR_WIDTH_MOBILE,
252
240
  } as React.CSSProperties
253
241
  }
242
+ side={side}
243
+ {...props}
254
244
  >
255
- <div className="pointer-events-none absolute inset-x-0 top-full h-32 bg-sidebar" />
256
- <div className="flex h-full w-full flex-col overflow-y-auto bg-sidebar">
245
+ <SheetHeader className="sr-only">
246
+ <SheetTitle>Sidebar</SheetTitle>
247
+ <SheetDescription>Console navigation.</SheetDescription>
248
+ </SheetHeader>
249
+ <div className="flex size-full min-h-0 flex-col pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)]">
257
250
  {children}
258
251
  </div>
259
- </dialog>
260
- </div>
252
+ </SheetContent>
253
+ </Sheet>
261
254
  );
262
255
  }
263
256