@hot-updater/console 0.32.0 → 0.33.1

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 (63) hide show
  1. package/.output/nitro.json +3 -3
  2. package/.output/public/assets/dist-DJX53faP.js +3 -0
  3. package/.output/public/assets/index-CCUspZZV.js +10 -0
  4. package/.output/public/assets/routes-D0t6y5Fr.js +54 -0
  5. package/.output/public/assets/styles-C0q26dS-.css +2 -0
  6. package/.output/server/{__tanstack-start-server-fn-resolver-ySzPUDlM.mjs → __23tanstack-start-server-fn-resolver-B12pVvhf.mjs} +32 -39
  7. package/.output/server/_chunks/ssr-renderer.mjs +14 -3
  8. package/.output/server/_libs/@floating-ui/react-dom+[...].mjs +7 -7
  9. package/.output/server/_libs/@radix-ui/react-alert-dialog+[...].mjs +142 -93
  10. package/.output/server/_libs/@radix-ui/react-popper+[...].mjs +32 -11
  11. package/.output/server/_libs/@radix-ui/react-select+[...].mjs +130 -95
  12. package/.output/server/_libs/@tanstack/form-core+[...].mjs +1138 -306
  13. package/.output/server/_libs/@tanstack/react-form+[...].mjs +156 -24
  14. package/.output/server/_libs/@tanstack/react-router+[...].mjs +2658 -1395
  15. package/.output/server/_libs/@tanstack/react-table+[...].mjs +1 -1
  16. package/.output/server/_libs/dayjs.mjs +6 -6
  17. package/.output/server/_libs/h3+rou3+srvx.mjs +30 -42
  18. package/.output/server/_libs/lucide-react.mjs +54 -9
  19. package/.output/server/_libs/next-themes.mjs +1 -1
  20. package/.output/server/_libs/radix-ui__number.mjs +1 -1
  21. package/.output/server/_libs/radix-ui__react-arrow.mjs +2 -2
  22. package/.output/server/_libs/radix-ui__react-collection.mjs +2 -2
  23. package/.output/server/_libs/radix-ui__react-direction.mjs +2 -2
  24. package/.output/server/_libs/radix-ui__react-label.mjs +2 -2
  25. package/.output/server/_libs/radix-ui__react-separator.mjs +2 -2
  26. package/.output/server/_libs/radix-ui__react-slider.mjs +103 -44
  27. package/.output/server/_libs/radix-ui__react-switch.mjs +88 -49
  28. package/.output/server/_libs/radix-ui__react-tooltip.mjs +6 -4
  29. package/.output/server/_libs/semver.mjs +97 -52
  30. package/.output/server/_libs/sonner.mjs +1 -1
  31. package/.output/server/_libs/tailwind-merge.mjs +51 -3
  32. package/.output/server/_libs/tanstack__history.mjs +28 -8
  33. package/.output/server/_libs/tanstack__query-core.mjs +200 -174
  34. package/.output/server/_libs/tanstack__react-query.mjs +14 -13
  35. package/.output/server/_ssr/{api-rpc-Bwustks9.mjs → api-rpc-ORImfBFu.mjs} +18 -18
  36. package/.output/server/_ssr/{config.server-xu3W-WAK.mjs → config.server-BSS366KT.mjs} +2 -2
  37. package/.output/server/_ssr/{createServerFn-CdeRXnVy.mjs → createServerFn-DzU0k62V.mjs} +129 -90
  38. package/.output/server/_ssr/{deleteBundle-CXxwjwEZ.mjs → deleteBundle-CuujBAH8.mjs} +2 -2
  39. package/.output/server/_ssr/{dist-CRiLZLfa.mjs → dist-KboydRVk.mjs} +1 -1
  40. package/.output/server/_ssr/empty-plugin-adapters-B4HCzgmM.mjs +5 -0
  41. package/.output/server/_ssr/{getBundleChildren-DFqZ6XMp.mjs → getBundleChildren-CVtSiH5t.mjs} +2 -2
  42. package/.output/server/_ssr/{router-DoC6G-mF.mjs → router-DmdnzGpe.mjs} +12 -10
  43. package/.output/server/_ssr/{routes-BHVpk6zR.mjs → routes-Be7gDfJS.mjs} +577 -305
  44. package/.output/server/_ssr/{sidebar-CgbtXkE2.mjs → sidebar-Cv_H0_Jv.mjs} +6 -6
  45. package/.output/server/_ssr/ssr.mjs +4222 -3153
  46. package/.output/server/_ssr/start-D4SBEASt.mjs +4 -0
  47. package/.output/server/_ssr/{storageProfile-wICk5nZZ.mjs → storageProfile-FVkjPoGb.mjs} +1 -1
  48. package/.output/server/_tanstack-start-manifest_v-BroBMdSR.mjs +20 -0
  49. package/.output/server/index.mjs +111 -155
  50. package/package.json +12 -9
  51. package/.output/public/assets/BaseTanStackRouterDevtoolsPanel-Bmws3ikM-q5p5qKUx.js +0 -486
  52. package/.output/public/assets/FloatingTanStackRouterDevtools-B7vy70jP-Bzs2Gthe.js +0 -1
  53. package/.output/public/assets/clsx-CbprLf2V.js +0 -1
  54. package/.output/public/assets/dist-B5egZOkC.js +0 -9
  55. package/.output/public/assets/main-DrVuFR7r.js +0 -10
  56. package/.output/public/assets/preload-helper-C5ST2IKa.js +0 -1
  57. package/.output/public/assets/routes-C_bgs7kg.js +0 -54
  58. package/.output/public/assets/styles-DZ0tCVA1.css +0 -2
  59. package/.output/server/_libs/hookable.mjs +0 -41
  60. package/.output/server/_libs/tanstack__router-core.mjs +0 -6
  61. package/.output/server/_libs/unctx.mjs +0 -82
  62. package/.output/server/_ssr/start-D0X4LIsd.mjs +0 -4
  63. package/.output/server/_tanstack-start-manifest_v-D2MqgD3d.mjs +0 -22
@@ -1,27 +1,27 @@
1
1
  import { r as __toESM } from "../_runtime.mjs";
2
- import { c as createServerFn, i as TSS_SERVER_FUNCTION } from "./createServerFn-CdeRXnVy.mjs";
3
- import { c as getPatchBaseFileHash, d as isValidCohort, f as normalizeCohortValue, l as getPatchFileHash, n as NUMERIC_COHORT_SIZE, o as getNumericCohortRolloutPosition, p as normalizeRolloutCohortCount, s as getPatchBaseBundleId, t as INVALID_COHORT_ERROR_MESSAGE } from "./dist-CRiLZLfa.mjs";
4
- import { A as Slot, P as require_jsx_runtime, a as Overlay2, c as Title2, d as Description, f as Overlay, g as Trigger, h as Title, i as Description2, l as Close, m as Root, n as Cancel, o as Portal2, p as Portal, r as Content2, s as Root2, t as Action, u as Content } from "../_libs/@radix-ui/react-alert-dialog+[...].mjs";
2
+ import { c as createServerFn, i as TSS_SERVER_FUNCTION } from "./createServerFn-DzU0k62V.mjs";
3
+ import { c as getPatchBaseFileHash, d as isValidCohort, f as normalizeCohortValue, l as getPatchFileHash, n as NUMERIC_COHORT_SIZE, o as getNumericCohortRolloutPosition, p as normalizeRolloutCohortCount, s as getPatchBaseBundleId, t as INVALID_COHORT_ERROR_MESSAGE } from "./dist-KboydRVk.mjs";
5
4
  import { u as require_react } from "../_libs/@floating-ui/react-dom+[...].mjs";
6
- import { C as ArrowRight, S as Check, b as ChevronLeft, d as List, g as Download, h as ExternalLink, i as Plus, l as Minus, m as FingerprintPattern, n as TriangleAlert, o as Package, p as Funnel, t as X, v as ChevronUp, x as ChevronDown, y as ChevronRight } from "../_libs/lucide-react.mjs";
5
+ import { A as Slot, P as require_jsx_runtime, a as Overlay2, c as Title2, d as Description, f as Overlay, g as Trigger, h as Title, i as Description2, l as Close, m as Root, n as Cancel, o as Portal2, p as Portal, r as Content2, s as Root2, t as Action, u as Content } from "../_libs/@radix-ui/react-alert-dialog+[...].mjs";
6
+ import { C as ChevronLeft, E as ArrowRight, S as ChevronRight, T as Check, _ as ExternalLink, a as RotateCcw, b as CircleCheck, c as Package, d as Minus, f as LoaderCircle, g as FingerprintPattern, h as Funnel, n as TriangleAlert, o as Plus, p as List, r as Trash2, t as X, v as Download, w as ChevronDown, x as ChevronUp, y as CircleX } from "../_libs/lucide-react.mjs";
7
7
  import { t as cva } from "../_libs/class-variance-authority+clsx.mjs";
8
- import { a as ItemText, c as ScrollDownButton, d as Value, f as Viewport, i as ItemIndicator, l as ScrollUpButton, n as Icon, o as Portal$1, r as Item, s as Root2$1, t as Content2$1, u as Trigger$1 } from "../_libs/@radix-ui/react-select+[...].mjs";
8
+ import { a as SelectItemIndicator, c as SelectScrollDownButton$1, d as SelectValue$1, f as SelectViewport, i as SelectItem$1, l as SelectScrollUpButton$1, n as SelectContent$1, o as SelectItemText, r as SelectIcon, s as SelectPortal, t as Select, u as SelectTrigger$1 } from "../_libs/@radix-ui/react-select+[...].mjs";
9
9
  import { t as Root$1 } from "../_libs/radix-ui__react-label.mjs";
10
- import { i as Track, n as Root$2, r as Thumb, t as Range } from "../_libs/radix-ui__react-slider.mjs";
11
- import { n as Thumb$1, t as Root$3 } from "../_libs/radix-ui__react-switch.mjs";
12
- import { C as useIsMobile, S as cn, _ as Skeleton, g as SidebarTrigger, n as Input, r as Separator$1, t as Button, v as Tooltip$1, x as TooltipTrigger, y as TooltipContent } from "./sidebar-CgbtXkE2.mjs";
13
- import { d as useNavigate, f as useSearch } from "../_libs/@tanstack/react-router+[...].mjs";
10
+ import { i as SliderTrack, n as SliderRange, r as SliderThumb, t as Slider } from "../_libs/radix-ui__react-slider.mjs";
11
+ import { n as SwitchThumb, t as Switch } from "../_libs/radix-ui__react-switch.mjs";
12
+ import { C as useIsMobile, S as cn, _ as Skeleton, g as SidebarTrigger, n as Input, r as Separator$1, t as Button, v as Tooltip$1, x as TooltipTrigger, y as TooltipContent } from "./sidebar-Cv_H0_Jv.mjs";
13
+ import { m as useSearch, p as useNavigate } from "../_libs/@tanstack/react-router+[...].mjs";
14
14
  import { n as useStore, t as useForm } from "../_libs/@tanstack/react-form+[...].mjs";
15
15
  import { i as useQueryClient, n as useQuery, t as useMutation } from "../_libs/tanstack__react-query.mjs";
16
16
  import { n as toast } from "../_libs/sonner.mjs";
17
- import { t as getServerFnById } from "../__tanstack-start-server-fn-resolver-ySzPUDlM.mjs";
17
+ import { t as getServerFnById } from "../__23tanstack-start-server-fn-resolver-B12pVvhf.mjs";
18
18
  import { t as require_semver } from "../_libs/semver.mjs";
19
19
  import { i as getCoreRowModel, n as useReactTable, r as createColumnHelper, t as flexRender } from "../_libs/@tanstack/react-table+[...].mjs";
20
20
  import { n as require_dayjs_min, t as require_relativeTime } from "../_libs/dayjs.mjs";
21
- //#region node_modules/.nitro/vite/services/ssr/assets/routes-BHVpk6zR.js
21
+ //#region node_modules/.nitro/vite/services/ssr/assets/routes-Be7gDfJS.js
22
+ var import_react = /* @__PURE__ */ __toESM(require_react());
22
23
  var import_jsx_runtime = require_jsx_runtime();
23
24
  var import_semver = /* @__PURE__ */ __toESM(require_semver());
24
- var import_react = /* @__PURE__ */ __toESM(require_react());
25
25
  var import_dayjs_min = /* @__PURE__ */ __toESM(require_dayjs_min());
26
26
  var import_relativeTime = /* @__PURE__ */ __toESM(require_relativeTime());
27
27
  var extractTimestampFromUUIDv7 = (uuid) => {
@@ -195,7 +195,6 @@ function BundleIdDisplay({ bundleId, className }) {
195
195
  }
196
196
  function AppleIcon({ className }) {
197
197
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
198
- role: "img",
199
198
  "aria-label": "iOS",
200
199
  viewBox: "0 0 24 24",
201
200
  xmlns: "http://www.w3.org/2000/svg",
@@ -206,7 +205,6 @@ function AppleIcon({ className }) {
206
205
  }
207
206
  function AndroidIcon({ className }) {
208
207
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
209
- role: "img",
210
208
  "aria-label": "Android",
211
209
  viewBox: "0 0 24 24",
212
210
  xmlns: "http://www.w3.org/2000/svg",
@@ -358,7 +356,7 @@ function Slider$1({ className, defaultValue, value, min = 0, max = 100, ...props
358
356
  min,
359
357
  max
360
358
  ]);
361
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Root$2, {
359
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Slider, {
362
360
  "data-slot": "slider",
363
361
  defaultValue,
364
362
  value,
@@ -366,35 +364,35 @@ function Slider$1({ className, defaultValue, value, min = 0, max = 100, ...props
366
364
  max,
367
365
  className: cn("relative flex w-full touch-none select-none items-center", className),
368
366
  ...props,
369
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Track, {
367
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SliderTrack, {
370
368
  "data-slot": "slider-track",
371
369
  className: "relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20",
372
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Range, {
370
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SliderRange, {
373
371
  "data-slot": "slider-range",
374
372
  className: "absolute h-full bg-primary"
375
373
  })
376
- }), Array.from({ length: _values.length }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Thumb, {
374
+ }), Array.from({ length: _values.length }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SliderThumb, {
377
375
  "data-slot": "slider-thumb",
378
376
  className: "block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"
379
377
  }, index))]
380
378
  });
381
379
  }
382
380
  function Switch$1({ className, ...props }) {
383
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Root$3, {
381
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Switch, {
384
382
  "data-slot": "switch",
385
383
  className: cn("peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input", className),
386
384
  ...props,
387
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Thumb$1, {
385
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SwitchThumb, {
388
386
  "data-slot": "switch-thumb",
389
387
  className: "pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
390
388
  })
391
389
  });
392
390
  }
393
- var createSsrRpc = (functionId, importer) => {
391
+ var createSsrRpc = (functionId) => {
394
392
  const url = "/_serverFn/" + functionId;
395
393
  const serverFnMeta = { id: functionId };
396
394
  const fn = async (...args) => {
397
- return (importer ? await importer() : await getServerFnById(functionId))(...args);
395
+ return (await getServerFnById(functionId, { origin: "server" }))(...args);
398
396
  };
399
397
  return Object.assign(fn, {
400
398
  url,
@@ -441,6 +439,17 @@ function replaceBundleInQueryData(data, updatedBundle) {
441
439
  data: data.data.map((bundle) => bundle.id === updatedBundle.id ? updatedBundle : bundle)
442
440
  };
443
441
  }
442
+ function removeBundleFromQueryData(data, bundleId) {
443
+ if (!data) return data;
444
+ return {
445
+ ...data,
446
+ data: data.data.filter((bundle) => bundle.id !== bundleId)
447
+ };
448
+ }
449
+ var hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
450
+ var invalidateInBackground = (queryClient, queryKey) => {
451
+ queryClient.invalidateQueries({ queryKey }).catch(() => void 0);
452
+ };
444
453
  function useConfigQuery() {
445
454
  return useQuery({
446
455
  queryKey: queryKeys.config,
@@ -495,15 +504,12 @@ function useUpdateBundleMutation() {
495
504
  const queryClient = useQueryClient();
496
505
  return useMutation({
497
506
  mutationFn: (params) => updateBundle({ data: params }),
498
- onSuccess: async ({ bundle: updatedBundle }, vars) => {
507
+ onSuccess: ({ bundle: updatedBundle }, vars) => {
499
508
  queryClient.setQueryData(queryKeys.bundle(vars.bundleId), updatedBundle);
500
509
  queryClient.setQueriesData({ queryKey: queryKeys.bundles.all }, (data) => replaceBundleInQueryData(data, updatedBundle));
501
- await Promise.all([
502
- queryClient.invalidateQueries({ queryKey: queryKeys.bundles.all }),
503
- queryClient.invalidateQueries({ queryKey: queryKeys.bundleChildren.all }),
504
- queryClient.invalidateQueries({ queryKey: queryKeys.bundle(vars.bundleId) }),
505
- queryClient.invalidateQueries({ queryKey: queryKeys.channels })
506
- ]);
510
+ invalidateInBackground(queryClient, queryKeys.bundles.all);
511
+ if (hasOwn(vars.bundle, "patches") || hasOwn(vars.bundle, "channel") || hasOwn(vars.bundle, "platform")) invalidateInBackground(queryClient, queryKeys.bundleChildren.all);
512
+ if (hasOwn(vars.bundle, "channel")) invalidateInBackground(queryClient, queryKeys.channels);
507
513
  }
508
514
  });
509
515
  }
@@ -526,13 +532,12 @@ function useDeleteBundleMutation() {
526
532
  const queryClient = useQueryClient();
527
533
  return useMutation({
528
534
  mutationFn: (params) => deleteBundle({ data: params }),
529
- onSuccess: async (_, vars) => {
535
+ onSuccess: (_, vars) => {
530
536
  queryClient.removeQueries({ queryKey: queryKeys.bundle(vars.bundleId) });
531
- await Promise.all([
532
- queryClient.invalidateQueries({ queryKey: queryKeys.bundles.all }),
533
- queryClient.invalidateQueries({ queryKey: queryKeys.bundleChildren.all }),
534
- queryClient.invalidateQueries({ queryKey: queryKeys.channels })
535
- ]);
537
+ queryClient.setQueriesData({ queryKey: queryKeys.bundles.all }, (data) => removeBundleFromQueryData(data, vars.bundleId));
538
+ invalidateInBackground(queryClient, queryKeys.bundles.all);
539
+ invalidateInBackground(queryClient, queryKeys.bundleChildren.all);
540
+ invalidateInBackground(queryClient, queryKeys.channels);
536
541
  }
537
542
  });
538
543
  }
@@ -681,30 +686,30 @@ function DeleteBundleDialog({ bundle, open, onOpenChange, onSuccess }) {
681
686
  });
682
687
  }
683
688
  function Select$1({ ...props }) {
684
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Root2$1, {
689
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Select, {
685
690
  "data-slot": "select",
686
691
  ...props
687
692
  });
688
693
  }
689
694
  function SelectValue({ ...props }) {
690
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Value, {
695
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue$1, {
691
696
  "data-slot": "select-value",
692
697
  ...props
693
698
  });
694
699
  }
695
700
  function SelectTrigger({ className, children, ...props }) {
696
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Trigger$1, {
701
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectTrigger$1, {
697
702
  "data-slot": "select-trigger",
698
703
  className: cn("flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", className),
699
704
  ...props,
700
- children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, {
705
+ children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectIcon, {
701
706
  asChild: true,
702
707
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: "h-4 w-4 opacity-50" })
703
708
  })]
704
709
  });
705
710
  }
706
711
  function SelectContent({ className, children, position = "item-aligned", align = "center", ...props }) {
707
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Portal$1, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Content2$1, {
712
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectPortal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent$1, {
708
713
  "data-slot": "select-content",
709
714
  className: cn("bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 border border-border min-w-32 rounded-lg shadow-md duration-100 relative z-50 max-h-(--radix-select-content-available-height) origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto", position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className),
710
715
  position,
@@ -712,7 +717,7 @@ function SelectContent({ className, children, position = "item-aligned", align =
712
717
  ...props,
713
718
  children: [
714
719
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollUpButton, {}),
715
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Viewport, {
720
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectViewport, {
716
721
  "data-position": position,
717
722
  className: cn("data-[position=popper]:h-[var(--radix-select-trigger-height)] data-[position=popper]:w-full data-[position=popper]:min-w-[var(--radix-select-trigger-width)]", position === "popper" && ""),
718
723
  children
@@ -722,18 +727,18 @@ function SelectContent({ className, children, position = "item-aligned", align =
722
727
  }) });
723
728
  }
724
729
  function SelectItem({ className, children, ...props }) {
725
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Item, {
730
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectItem$1, {
726
731
  "data-slot": "select-item",
727
732
  className: cn("relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className),
728
733
  ...props,
729
734
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
730
735
  className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center",
731
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "h-4 w-4" }) })
732
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ItemText, { children })]
736
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "h-4 w-4" }) })
737
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItemText, { children })]
733
738
  });
734
739
  }
735
740
  function SelectScrollUpButton({ className, ...props }) {
736
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollUpButton, {
741
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollUpButton$1, {
737
742
  "data-slot": "select-scroll-up-button",
738
743
  className: cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-3.5", className),
739
744
  ...props,
@@ -741,7 +746,7 @@ function SelectScrollUpButton({ className, ...props }) {
741
746
  });
742
747
  }
743
748
  function SelectScrollDownButton({ className, ...props }) {
744
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollDownButton, {
749
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollDownButton$1, {
745
750
  "data-slot": "select-scroll-down-button",
746
751
  className: cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-3.5", className),
747
752
  ...props,
@@ -986,6 +991,7 @@ function PromoteChannelDialog({ bundle, open, onOpenChange, onSuccess }) {
986
991
  type: "button",
987
992
  variant: "outline",
988
993
  size: "xs",
994
+ "aria-label": `Use ${channel} as target channel`,
989
995
  onClick: () => setTargetChannel(channel),
990
996
  disabled: isPromoting,
991
997
  children: channel
@@ -2296,6 +2302,208 @@ var createBundleColumns = ({ expandedBundleId, patchCountsByBundleId, onDetailCl
2296
2302
  cell: (info) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TimestampDisplay, { uuid: info.getValue() })
2297
2303
  })
2298
2304
  ];
2305
+ var statusLabels = {
2306
+ queued: "Queued",
2307
+ deleting: "Deleting",
2308
+ deleted: "Deleted",
2309
+ failed: "Failed"
2310
+ };
2311
+ var getDeleteErrorMessage = (error) => error instanceof Error ? error.message : "Delete request failed";
2312
+ var createDeleteItems = (bundles) => bundles.map((bundle) => ({
2313
+ bundle,
2314
+ status: "queued"
2315
+ }));
2316
+ function getStatusIcon(status) {
2317
+ switch (status) {
2318
+ case "failed": return {
2319
+ className: "size-3.5 text-destructive",
2320
+ Icon: CircleX
2321
+ };
2322
+ case "deleting": return {
2323
+ className: "size-3.5 animate-spin text-primary",
2324
+ Icon: LoaderCircle
2325
+ };
2326
+ case "deleted": return {
2327
+ className: "size-3.5 text-primary",
2328
+ Icon: CircleCheck
2329
+ };
2330
+ }
2331
+ }
2332
+ function DeleteStatusIcon({ status }) {
2333
+ if (status === "queued") return null;
2334
+ const label = statusLabels[status];
2335
+ const { className, Icon } = getStatusIcon(status);
2336
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tooltip$1, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipTrigger, {
2337
+ asChild: true,
2338
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2339
+ "aria-label": label,
2340
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { className })
2341
+ })
2342
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipContent, { children: label })] });
2343
+ }
2344
+ function SelectedBundlesDeleteDialog({ bundles, open, onOpenChange, onComplete }) {
2345
+ const deleteBundleMutation = useDeleteBundleMutation();
2346
+ const [phase, setPhase] = (0, import_react.useState)("confirming");
2347
+ const [items, setItems] = (0, import_react.useState)(() => createDeleteItems(bundles));
2348
+ const isDeleting = phase === "deleting";
2349
+ const totalCount = items.length;
2350
+ const activeCount = items.filter((item) => item.status === "deleted").length + items.filter((item) => item.status === "failed").length;
2351
+ const failedBundleIds = items.filter((item) => item.status === "failed").map((item) => item.bundle.id);
2352
+ const hasFailures = failedBundleIds.length > 0;
2353
+ const title = phase === "confirming" ? "Delete selected bundles?" : "Deleting bundles";
2354
+ const description = phase === "confirming" ? `This action cannot be undone. This will permanently delete ${bundles.length} bundles and remove them from storage.` : `${activeCount} of ${totalCount} delete requests finished.`;
2355
+ const deleteButtonLabel = (0, import_react.useMemo)(() => {
2356
+ if (!isDeleting) return "Delete";
2357
+ return `Deleting ${Math.min(activeCount + 1, totalCount)}/${totalCount}`;
2358
+ }, [
2359
+ activeCount,
2360
+ isDeleting,
2361
+ totalCount
2362
+ ]);
2363
+ (0, import_react.useEffect)(() => {
2364
+ if (open && phase === "confirming") setItems(createDeleteItems(bundles));
2365
+ }, [
2366
+ bundles,
2367
+ open,
2368
+ phase
2369
+ ]);
2370
+ const handleOpenChange = (nextOpen) => {
2371
+ if (!nextOpen && isDeleting) return;
2372
+ onOpenChange(nextOpen);
2373
+ if (!nextOpen) {
2374
+ setPhase("confirming");
2375
+ setItems(createDeleteItems(bundles));
2376
+ }
2377
+ };
2378
+ const runDeletion = async (bundleIds) => {
2379
+ if (isDeleting || bundleIds.length === 0) return;
2380
+ const bundleIdSet = new Set(bundleIds);
2381
+ const deletedBundleIds = [];
2382
+ const nextFailedBundleIds = [];
2383
+ setPhase("deleting");
2384
+ setItems((currentItems) => currentItems.map((item) => bundleIdSet.has(item.bundle.id) ? {
2385
+ bundle: item.bundle,
2386
+ status: "queued"
2387
+ } : item));
2388
+ for (const bundleId of bundleIds) {
2389
+ setItems((currentItems) => currentItems.map((item) => item.bundle.id === bundleId ? {
2390
+ bundle: item.bundle,
2391
+ status: "deleting"
2392
+ } : item));
2393
+ try {
2394
+ await deleteBundleMutation.mutateAsync({ bundleId });
2395
+ deletedBundleIds.push(bundleId);
2396
+ setItems((currentItems) => currentItems.map((item) => item.bundle.id === bundleId ? {
2397
+ bundle: item.bundle,
2398
+ status: "deleted"
2399
+ } : item));
2400
+ } catch (error) {
2401
+ nextFailedBundleIds.push(bundleId);
2402
+ setItems((currentItems) => currentItems.map((item) => item.bundle.id === bundleId ? {
2403
+ bundle: item.bundle,
2404
+ message: getDeleteErrorMessage(error),
2405
+ status: "failed"
2406
+ } : item));
2407
+ }
2408
+ }
2409
+ setPhase("complete");
2410
+ onComplete({
2411
+ deletedBundleIds,
2412
+ failedBundleIds: nextFailedBundleIds
2413
+ });
2414
+ if (nextFailedBundleIds.length > 0) {
2415
+ toast.error(`${deletedBundleIds.length} deleted, ${nextFailedBundleIds.length} failed`);
2416
+ return;
2417
+ }
2418
+ toast.success(`${deletedBundleIds.length} bundles deleted successfully`);
2419
+ };
2420
+ const handleDelete = () => {
2421
+ runDeletion(items.map((item) => item.bundle.id));
2422
+ };
2423
+ const handleRetryFailed = () => {
2424
+ runDeletion(failedBundleIds);
2425
+ };
2426
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog$1, {
2427
+ open,
2428
+ onOpenChange: handleOpenChange,
2429
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent, {
2430
+ showCloseButton: !isDeleting,
2431
+ className: "sm:max-w-lg",
2432
+ onEscapeKeyDown: (event) => {
2433
+ if (isDeleting) event.preventDefault();
2434
+ },
2435
+ onInteractOutside: (event) => {
2436
+ if (isDeleting) event.preventDefault();
2437
+ },
2438
+ children: [
2439
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTitle, { children: title }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogDescription, { children: description })] }),
2440
+ phase === "confirming" ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardContent, {
2441
+ className: "max-h-[50vh] overflow-y-auto p-0",
2442
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Table, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHeader, {
2443
+ className: "sticky top-0 bg-card",
2444
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableRow, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
2445
+ className: "w-12 text-center",
2446
+ children: "Status"
2447
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, { children: "Bundle" })] })
2448
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableBody, { children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableRow, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
2449
+ className: "text-center align-middle",
2450
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2451
+ className: "flex justify-center",
2452
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeleteStatusIcon, { status: item.status })
2453
+ })
2454
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
2455
+ className: "whitespace-normal",
2456
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2457
+ className: "min-w-0",
2458
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2459
+ className: "break-all font-mono text-[11px] text-foreground",
2460
+ children: item.bundle.id
2461
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2462
+ className: "mt-0.5 flex flex-wrap gap-x-2 gap-y-1 text-[11px] text-muted-foreground",
2463
+ children: [
2464
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: item.bundle.channel }),
2465
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: item.bundle.platform }),
2466
+ item.message ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2467
+ className: "text-destructive",
2468
+ children: item.message
2469
+ }) : null
2470
+ ]
2471
+ })]
2472
+ })
2473
+ })] }, item.bundle.id)) })] })
2474
+ }) }),
2475
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogFooter, { children: [
2476
+ phase === "confirming" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
2477
+ variant: "outline",
2478
+ onClick: () => handleOpenChange(false),
2479
+ children: "Cancel"
2480
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2481
+ variant: "destructive",
2482
+ onClick: handleDelete,
2483
+ disabled: totalCount === 0,
2484
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Trash2, { "data-icon": "inline-start" }), "Delete"]
2485
+ })] }) : null,
2486
+ phase === "deleting" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2487
+ variant: "destructive",
2488
+ disabled: true,
2489
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoaderCircle, {
2490
+ "data-icon": "inline-start",
2491
+ className: "animate-spin"
2492
+ }), deleteButtonLabel]
2493
+ }) : null,
2494
+ phase === "complete" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
2495
+ variant: "outline",
2496
+ onClick: () => handleOpenChange(false),
2497
+ children: "Close"
2498
+ }), hasFailures ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2499
+ onClick: handleRetryFailed,
2500
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RotateCcw, { "data-icon": "inline-start" }), "Retry failed"]
2501
+ }) : null] }) : null
2502
+ ] })
2503
+ ]
2504
+ })
2505
+ });
2506
+ }
2299
2507
  function MobileStatusBadge({ enabled, trueLabel, falseLabel, falseIcon = "x", trueTone = "success" }) {
2300
2508
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
2301
2509
  className: cn("inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-medium", enabled ? trueTone === "warning" ? "bg-amber-500/14 text-amber-700 dark:text-amber-300" : "bg-emerald-500/12 text-emerald-700 dark:text-emerald-300" : falseIcon === "minus" ? "bg-muted text-muted-foreground" : "bg-red-500/12 text-red-700 dark:text-red-300"),
@@ -2310,8 +2518,13 @@ function MobileStatusBadge({ enabled, trueLabel, falseLabel, falseIcon = "x", tr
2310
2518
  function BundlesTable({ bundles, pagination, expandedBundleId, selectedBundleId, onExpandedBundleChange, onDetailClick }) {
2311
2519
  const { setFilters } = useFilterParams();
2312
2520
  const isMobile = useIsMobile();
2521
+ const [selectedBundleIds, setSelectedBundleIds] = (0, import_react.useState)([]);
2522
+ const [deleteDialogOpen, setDeleteDialogOpen] = (0, import_react.useState)(false);
2313
2523
  const cursorPagination = pagination;
2314
- const { data: patchCountsByBundleId = {} } = useBundleChildCountsQuery(bundles.map((bundle) => bundle.id));
2524
+ const bundleIds = bundles.map((bundle) => bundle.id);
2525
+ const selectedBundles = bundles.filter((bundle) => selectedBundleIds.includes(bundle.id));
2526
+ const allBundlesSelected = bundles.length > 0 && selectedBundles.length === bundles.length;
2527
+ const { data: patchCountsByBundleId = {} } = useBundleChildCountsQuery(bundleIds);
2315
2528
  const { data: childBundles = [], isLoading: isChildBundlesLoading } = useBundleChildrenQuery(expandedBundleId ?? "");
2316
2529
  const bundleColumns = createBundleColumns({
2317
2530
  expandedBundleId,
@@ -2346,280 +2559,339 @@ function BundlesTable({ bundles, pagination, expandedBundleId, selectedBundleId,
2346
2559
  before: void 0
2347
2560
  });
2348
2561
  };
2562
+ const toggleBundleSelection = (bundleId) => {
2563
+ setSelectedBundleIds((current) => current.includes(bundleId) ? current.filter((selectedBundleId) => selectedBundleId !== bundleId) : [...current, bundleId]);
2564
+ };
2565
+ const handleToggleAllBundles = () => {
2566
+ setSelectedBundleIds(allBundlesSelected ? [] : bundleIds);
2567
+ };
2349
2568
  const startEntry = bundles.length === 0 ? 0 : (currentPage - 1) * 20 + 1;
2350
2569
  const endEntry = startEntry === 0 ? 0 : startEntry + bundles.length - 1;
2351
2570
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2352
- className: "flex flex-col gap-4",
2353
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2354
- className: "overflow-hidden rounded-lg border bg-card text-card-foreground shadow-sm",
2355
- children: isMobile ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2356
- className: "flex flex-col",
2357
- children: bundles.length ? bundles.map((bundle) => {
2358
- const isExpanded = bundle.id === expandedBundleId;
2359
- const panelId = `bundle-lineage-panel-${bundle.id}`;
2360
- const rolloutPercentage = (bundle.rolloutCohortCount ?? 1e3) / 10;
2361
- const patchCount = patchCountsByBundleId[bundle.id];
2362
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2363
- "data-state": bundle.id === selectedBundleId ? "selected" : void 0,
2364
- className: cn("border-b border-border/60 last:border-b-0 data-[state=selected]:bg-muted/20", isExpanded && "bg-primary/5"),
2365
- children: [
2366
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
2367
- type: "button",
2368
- className: "flex w-full flex-col gap-4 p-4 text-left",
2369
- onClick: () => onDetailClick(bundle),
2370
- children: [
2371
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2372
- className: "flex items-start justify-between gap-3",
2373
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2374
- className: "min-w-0 space-y-2",
2375
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2376
- className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-muted-foreground/70",
2377
- children: "Bundle"
2378
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2379
- className: "min-w-0 text-sm font-medium",
2380
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleIdDisplay, {
2381
- bundleId: bundle.id,
2382
- maxLength: 18,
2383
- fullOnMobile: true
2384
- })
2385
- })]
2386
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2387
- className: "shrink-0",
2388
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChannelBadge, { channel: bundle.channel })
2389
- })]
2390
- }),
2391
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2392
- className: "grid grid-cols-2 gap-3 text-sm",
2393
- children: [
2394
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2395
- className: "rounded-md bg-muted/40 p-3",
2396
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2397
- className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2398
- children: "Platform"
2399
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2400
- className: "flex items-center gap-2",
2401
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
2402
- platform: bundle.platform,
2403
- className: "h-4 w-4"
2404
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: bundle.platform === "ios" ? "iOS" : "Android" })]
2405
- })]
2406
- }),
2407
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2408
- className: "rounded-md bg-muted/40 p-3",
2409
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2410
- className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2411
- children: "Created"
2412
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2413
- className: "text-xs text-foreground",
2414
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TimestampDisplay, { uuid: bundle.id })
2415
- })]
2416
- }),
2417
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2418
- className: "rounded-md bg-muted/40 p-3",
2419
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2420
- className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2421
- children: "Rollout"
2422
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RolloutPercentageBadge, { percentage: rolloutPercentage })]
2423
- }),
2424
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2425
- className: "rounded-md bg-muted/40 p-3",
2571
+ className: "flex min-h-0 min-w-0 flex-1 flex-col gap-4",
2572
+ children: [
2573
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2574
+ className: "min-h-0 min-w-0 flex-1 overflow-hidden rounded-lg border bg-card text-card-foreground shadow-sm [&_[data-slot=table-container]]:h-full [&_[data-slot=table-container]]:overflow-auto",
2575
+ children: isMobile ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2576
+ className: "flex h-full flex-col overflow-y-auto",
2577
+ children: bundles.length ? bundles.map((bundle) => {
2578
+ const isExpanded = bundle.id === expandedBundleId;
2579
+ const panelId = `bundle-lineage-panel-${bundle.id}`;
2580
+ const rolloutPercentage = (bundle.rolloutCohortCount ?? 1e3) / 10;
2581
+ const patchCount = patchCountsByBundleId[bundle.id];
2582
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2583
+ "data-state": bundle.id === selectedBundleId ? "selected" : void 0,
2584
+ className: cn("border-b border-border/60 last:border-b-0 data-[state=selected]:bg-muted/20", isExpanded && "bg-primary/5"),
2585
+ children: [
2586
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
2587
+ className: "flex items-center gap-2 border-b border-border/60 px-4 py-3 text-xs font-medium text-muted-foreground",
2588
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
2589
+ type: "checkbox",
2590
+ checked: selectedBundleIds.includes(bundle.id),
2591
+ className: "size-4 accent-primary",
2592
+ onChange: () => toggleBundleSelection(bundle.id)
2593
+ }), "Select bundle"]
2594
+ }),
2595
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
2596
+ type: "button",
2597
+ "aria-label": `Open bundle ${bundle.id}`,
2598
+ className: "flex w-full flex-col gap-4 p-4 text-left",
2599
+ onClick: () => onDetailClick(bundle),
2600
+ children: [
2601
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2602
+ className: "flex items-start justify-between gap-3",
2603
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2604
+ className: "min-w-0 space-y-2",
2426
2605
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2427
- className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2428
- children: "Patches"
2606
+ className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-muted-foreground/70",
2607
+ children: "Bundle"
2429
2608
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2430
- className: "text-xs text-foreground",
2431
- children: patchCount === void 0 ? "Checking" : patchCount > 0 ? `${patchCount} ${patchCount === 1 ? "patch" : "patches"}` : "-"
2609
+ className: "min-w-0 text-sm font-medium",
2610
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleIdDisplay, {
2611
+ bundleId: bundle.id,
2612
+ maxLength: 18,
2613
+ fullOnMobile: true
2614
+ })
2432
2615
  })]
2433
- }),
2434
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2435
- className: "rounded-md bg-muted/40 p-3",
2436
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2437
- className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2438
- children: "Status"
2439
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2440
- className: "flex flex-wrap items-center gap-2",
2441
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MobileStatusBadge, {
2442
- enabled: bundle.enabled,
2443
- trueLabel: "Enabled",
2444
- falseLabel: "Disabled"
2445
- }), bundle.shouldForceUpdate ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MobileStatusBadge, {
2446
- enabled: bundle.shouldForceUpdate,
2447
- trueLabel: "Force update",
2448
- falseLabel: "Optional",
2449
- falseIcon: "minus",
2450
- trueTone: "warning"
2451
- }) : null]
2452
- })]
2453
- })
2454
- ]
2455
- }),
2456
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2457
- className: "grid gap-2 rounded-md border border-border/70 bg-background/80 p-3 text-sm",
2458
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2459
- className: "flex items-center justify-between gap-3",
2460
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2461
- className: "text-muted-foreground",
2462
- children: "Target"
2463
2616
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2464
- className: "min-w-0 text-right",
2465
- children: bundle.fingerprintHash ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
2466
- translate: "no",
2467
- className: "inline-flex min-w-0 items-start gap-2 font-mono text-xs",
2468
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FingerprintPattern, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HashValueDisplay, {
2469
- value: bundle.fingerprintHash,
2470
- maxLength: 12
2617
+ className: "shrink-0",
2618
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChannelBadge, { channel: bundle.channel })
2619
+ })]
2620
+ }),
2621
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2622
+ className: "grid grid-cols-2 gap-3 text-sm",
2623
+ children: [
2624
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2625
+ className: "rounded-md bg-muted/40 p-3",
2626
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2627
+ className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2628
+ children: "Platform"
2629
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2630
+ className: "flex items-center gap-2",
2631
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
2632
+ platform: bundle.platform,
2633
+ className: "h-4 w-4"
2634
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: bundle.platform === "ios" ? "iOS" : "Android" })]
2635
+ })]
2636
+ }),
2637
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2638
+ className: "rounded-md bg-muted/40 p-3",
2639
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2640
+ className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2641
+ children: "Created"
2642
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2643
+ className: "text-xs text-foreground",
2644
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TimestampDisplay, { uuid: bundle.id })
2645
+ })]
2646
+ }),
2647
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2648
+ className: "rounded-md bg-muted/40 p-3",
2649
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2650
+ className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2651
+ children: "Rollout"
2652
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RolloutPercentageBadge, { percentage: rolloutPercentage })]
2653
+ }),
2654
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2655
+ className: "rounded-md bg-muted/40 p-3",
2656
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2657
+ className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2658
+ children: "Patches"
2659
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2660
+ className: "text-xs text-foreground",
2661
+ children: patchCount === void 0 ? "Checking" : patchCount > 0 ? `${patchCount} ${patchCount === 1 ? "patch" : "patches"}` : "-"
2662
+ })]
2663
+ }),
2664
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2665
+ className: "rounded-md bg-muted/40 p-3",
2666
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2667
+ className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2668
+ children: "Status"
2669
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2670
+ className: "flex flex-wrap items-center gap-2",
2671
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MobileStatusBadge, {
2672
+ enabled: bundle.enabled,
2673
+ trueLabel: "Enabled",
2674
+ falseLabel: "Disabled"
2675
+ }), bundle.shouldForceUpdate ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MobileStatusBadge, {
2676
+ enabled: bundle.shouldForceUpdate,
2677
+ trueLabel: "Force update",
2678
+ falseLabel: "Optional",
2679
+ falseIcon: "minus",
2680
+ trueTone: "warning"
2681
+ }) : null]
2471
2682
  })]
2472
- }) : bundle.targetAppVersion ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
2473
- translate: "no",
2474
- className: "inline-flex items-center gap-2 text-xs",
2475
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Package, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), bundle.targetAppVersion]
2476
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2477
- className: "text-xs text-muted-foreground",
2478
- children: "-"
2479
2683
  })
2684
+ ]
2685
+ }),
2686
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2687
+ className: "grid gap-2 rounded-md border border-border/70 bg-background/80 p-3 text-sm",
2688
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2689
+ className: "flex items-center justify-between gap-3",
2690
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2691
+ className: "text-muted-foreground",
2692
+ children: "Target"
2693
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2694
+ className: "min-w-0 text-right",
2695
+ children: bundle.fingerprintHash ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
2696
+ translate: "no",
2697
+ className: "inline-flex min-w-0 items-start gap-2 font-mono text-xs",
2698
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FingerprintPattern, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HashValueDisplay, {
2699
+ value: bundle.fingerprintHash,
2700
+ maxLength: 12
2701
+ })]
2702
+ }) : bundle.targetAppVersion ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
2703
+ translate: "no",
2704
+ className: "inline-flex items-center gap-2 text-xs",
2705
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Package, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), bundle.targetAppVersion]
2706
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2707
+ className: "text-xs text-muted-foreground",
2708
+ children: "-"
2709
+ })
2710
+ })]
2711
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2712
+ className: "flex items-start justify-between gap-3",
2713
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2714
+ className: "text-muted-foreground",
2715
+ children: "Message"
2716
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2717
+ className: "min-w-0 text-right text-xs text-foreground/80",
2718
+ children: bundle.message || "-"
2719
+ })]
2480
2720
  })]
2481
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2482
- className: "flex items-start justify-between gap-3",
2483
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2484
- className: "text-muted-foreground",
2485
- children: "Message"
2486
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2487
- className: "min-w-0 text-right text-xs text-foreground/80",
2488
- children: bundle.message || "-"
2489
- })]
2721
+ })
2722
+ ]
2723
+ }),
2724
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2725
+ className: "border-t border-border/60 px-4 py-3",
2726
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2727
+ type: "button",
2728
+ variant: "ghost",
2729
+ className: "h-9 w-full justify-between px-3 text-sm",
2730
+ "aria-label": isExpanded ? "Hide Lineage" : "Show Lineage",
2731
+ "aria-controls": panelId,
2732
+ "aria-expanded": isExpanded,
2733
+ onClick: () => onExpandedBundleChange(isExpanded ? void 0 : bundle.id),
2734
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: patchCount === void 0 ? "Patch lineage" : `Patch lineage (${patchCount})` }), isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronUp, {
2735
+ className: "h-4 w-4",
2736
+ "aria-hidden": "true"
2737
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, {
2738
+ className: "h-4 w-4",
2739
+ "aria-hidden": "true"
2490
2740
  })]
2491
2741
  })
2492
- ]
2493
- }),
2494
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2495
- className: "border-t border-border/60 px-4 py-3",
2496
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2497
- type: "button",
2498
- variant: "ghost",
2499
- className: "h-9 w-full justify-between px-3 text-sm",
2500
- "aria-label": isExpanded ? "Hide Lineage" : "Show Lineage",
2501
- "aria-controls": panelId,
2502
- "aria-expanded": isExpanded,
2503
- onClick: () => onExpandedBundleChange(isExpanded ? void 0 : bundle.id),
2504
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: patchCount === void 0 ? "Patch lineage" : `Patch lineage (${patchCount})` }), isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronUp, {
2505
- className: "h-4 w-4",
2506
- "aria-hidden": "true"
2507
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, {
2508
- className: "h-4 w-4",
2509
- "aria-hidden": "true"
2510
- })]
2511
- })
2512
- }),
2513
- isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleChildrenPanel, {
2742
+ }),
2743
+ isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleChildrenPanel, {
2744
+ panelId,
2745
+ bundle,
2746
+ bundles: childBundles,
2747
+ loading: isChildBundlesLoading,
2748
+ onDetailClick
2749
+ }) : null
2750
+ ]
2751
+ }, bundle.id);
2752
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2753
+ className: "flex h-32 items-center justify-center px-6 text-center text-sm text-muted-foreground",
2754
+ children: "No bundles found matching your filters."
2755
+ })
2756
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Table, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHeader, {
2757
+ className: "bg-muted/40",
2758
+ children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableRow, {
2759
+ className: "border-b border-border/60 hover:bg-transparent",
2760
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
2761
+ className: "w-10",
2762
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
2763
+ type: "checkbox",
2764
+ checked: allBundlesSelected,
2765
+ "aria-label": "Select all bundles",
2766
+ className: "size-4 accent-primary",
2767
+ onChange: handleToggleAllBundles
2768
+ })
2769
+ }), headerGroup.headers.map((header) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
2770
+ className: "h-10 text-xs font-semibold uppercase text-muted-foreground/70",
2771
+ children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())
2772
+ }, header.id))]
2773
+ }, headerGroup.id))
2774
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableBody, { children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => {
2775
+ const isExpanded = row.original.id === expandedBundleId;
2776
+ const panelId = `bundle-lineage-panel-${row.original.id}`;
2777
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableRow, {
2778
+ "data-state": row.original.id === selectedBundleId ? "selected" : void 0,
2779
+ className: cn("cursor-pointer transition-colors hover:bg-muted/10 focus-within:bg-muted/15 data-[state=selected]:bg-muted/15", isExpanded && "bg-primary/5"),
2780
+ onClick: () => onDetailClick(row.original),
2781
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
2782
+ className: "py-3",
2783
+ onClick: (event) => event.stopPropagation(),
2784
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
2785
+ type: "checkbox",
2786
+ checked: selectedBundleIds.includes(row.original.id),
2787
+ "aria-label": `Select bundle ${row.original.id}`,
2788
+ className: "size-4 accent-primary",
2789
+ onChange: () => toggleBundleSelection(row.original.id)
2790
+ })
2791
+ }), row.getVisibleCells().map((cell) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
2792
+ className: "py-3",
2793
+ children: flexRender(cell.column.columnDef.cell, cell.getContext())
2794
+ }, cell.id))]
2795
+ }), isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableRow, {
2796
+ className: "hover:bg-transparent",
2797
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
2798
+ colSpan: bundleColumns.length + 1,
2799
+ className: "border-t-0 p-0",
2800
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleChildrenPanel, {
2514
2801
  panelId,
2515
- bundle,
2802
+ bundle: row.original,
2516
2803
  bundles: childBundles,
2517
2804
  loading: isChildBundlesLoading,
2518
2805
  onDetailClick
2519
- }) : null
2520
- ]
2521
- }, bundle.id);
2522
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2523
- className: "flex h-32 items-center justify-center px-6 text-center text-sm text-muted-foreground",
2524
- children: "No bundles found matching your filters."
2525
- })
2526
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Table, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHeader, {
2527
- className: "bg-muted/40",
2528
- children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableRow, {
2529
- className: "border-b border-border/60 hover:bg-transparent",
2530
- children: headerGroup.headers.map((header) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
2531
- className: "h-10 text-xs font-semibold uppercase text-muted-foreground/70",
2532
- children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())
2533
- }, header.id))
2534
- }, headerGroup.id))
2535
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableBody, { children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => {
2536
- const isExpanded = row.original.id === expandedBundleId;
2537
- const panelId = `bundle-lineage-panel-${row.original.id}`;
2538
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableRow, {
2539
- "data-state": row.original.id === selectedBundleId ? "selected" : void 0,
2540
- className: cn("cursor-pointer transition-colors hover:bg-muted/10 focus-within:bg-muted/15 data-[state=selected]:bg-muted/15", isExpanded && "bg-primary/5"),
2541
- onClick: () => onDetailClick(row.original),
2542
- children: row.getVisibleCells().map((cell) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
2543
- className: "py-3",
2544
- children: flexRender(cell.column.columnDef.cell, cell.getContext())
2545
- }, cell.id))
2546
- }), isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableRow, {
2547
- className: "hover:bg-transparent",
2548
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
2549
- colSpan: bundleColumns.length,
2550
- className: "border-t-0 p-0",
2551
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleChildrenPanel, {
2552
- panelId,
2553
- bundle: row.original,
2554
- bundles: childBundles,
2555
- loading: isChildBundlesLoading,
2556
- onDetailClick
2806
+ })
2557
2807
  })
2558
- })
2559
- }) : null] }, row.original.id);
2560
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
2561
- colSpan: bundleColumns.length,
2562
- className: "h-32 text-center text-muted-foreground",
2563
- children: "No bundles found matching your filters."
2564
- }) }) })] })
2565
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2566
- className: "flex flex-col gap-3 px-2 sm:flex-row sm:items-center sm:justify-between",
2567
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2568
- className: "text-xs font-medium text-muted-foreground",
2569
- children: [
2570
- "Showing ",
2571
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2572
- className: "text-foreground",
2573
- children: startEntry
2574
- }),
2575
- " to",
2576
- " ",
2577
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2578
- className: "text-foreground",
2579
- children: endEntry
2580
- }),
2581
- " entries"
2582
- ]
2583
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2584
- className: "flex flex-wrap items-center gap-3 sm:justify-end",
2585
- children: [
2586
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2587
- className: "text-xs font-medium text-muted-foreground",
2588
- children: [
2589
- "Page ",
2590
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2591
- className: "text-foreground",
2592
- children: currentPage
2593
- }),
2594
- totalPages > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2595
- " ",
2596
- "of ",
2808
+ }) : null] }, row.original.id);
2809
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
2810
+ colSpan: bundleColumns.length + 1,
2811
+ className: "h-32 text-center text-muted-foreground",
2812
+ children: "No bundles found matching your filters."
2813
+ }) }) })] })
2814
+ }),
2815
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2816
+ className: "flex flex-col gap-3 px-2 sm:flex-row sm:items-center sm:justify-between",
2817
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2818
+ className: "text-xs font-medium text-muted-foreground",
2819
+ children: [
2820
+ "Showing ",
2821
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2822
+ className: "text-foreground",
2823
+ children: startEntry
2824
+ }),
2825
+ " to",
2826
+ " ",
2827
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2828
+ className: "text-foreground",
2829
+ children: endEntry
2830
+ }),
2831
+ " entries"
2832
+ ]
2833
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2834
+ className: "flex flex-wrap items-center gap-3 sm:justify-end",
2835
+ children: [
2836
+ selectedBundles.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2837
+ type: "button",
2838
+ variant: "destructive",
2839
+ size: "sm",
2840
+ className: "h-8 flex-1 px-3 text-xs sm:flex-none",
2841
+ onClick: () => setDeleteDialogOpen(true),
2842
+ children: [
2843
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Trash2, { "data-icon": "inline-start" }),
2844
+ "Delete selected (",
2845
+ selectedBundles.length,
2846
+ ")"
2847
+ ]
2848
+ }) : null,
2849
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2850
+ className: "text-xs font-medium text-muted-foreground",
2851
+ children: [
2852
+ "Page ",
2597
2853
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2598
2854
  className: "text-foreground",
2599
- children: totalPages
2600
- })
2601
- ] }) : null
2602
- ]
2603
- }),
2604
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2605
- variant: "outline",
2606
- size: "sm",
2607
- onClick: handlePreviousPage,
2608
- disabled: !hasPreviousPage,
2609
- className: "h-8 flex-1 px-3 text-xs sm:flex-none",
2610
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronLeft, { "data-icon": "inline-start" }), "Previous"]
2611
- }),
2612
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2613
- variant: "outline",
2614
- size: "sm",
2615
- onClick: handleNextPage,
2616
- disabled: !hasNextPage,
2617
- className: "h-8 flex-1 px-3 text-xs sm:flex-none",
2618
- children: ["Next", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronRight, { "data-icon": "inline-end" })]
2619
- })
2620
- ]
2621
- })]
2622
- })]
2855
+ children: currentPage
2856
+ }),
2857
+ totalPages > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2858
+ " ",
2859
+ "of ",
2860
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2861
+ className: "text-foreground",
2862
+ children: totalPages
2863
+ })
2864
+ ] }) : null
2865
+ ]
2866
+ }),
2867
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2868
+ variant: "outline",
2869
+ size: "sm",
2870
+ onClick: handlePreviousPage,
2871
+ disabled: !hasPreviousPage,
2872
+ className: "h-8 flex-1 px-3 text-xs sm:flex-none",
2873
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronLeft, { "data-icon": "inline-start" }), "Previous"]
2874
+ }),
2875
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2876
+ variant: "outline",
2877
+ size: "sm",
2878
+ onClick: handleNextPage,
2879
+ disabled: !hasNextPage,
2880
+ className: "h-8 flex-1 px-3 text-xs sm:flex-none",
2881
+ children: ["Next", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronRight, { "data-icon": "inline-end" })]
2882
+ })
2883
+ ]
2884
+ })]
2885
+ }),
2886
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectedBundlesDeleteDialog, {
2887
+ bundles: selectedBundles,
2888
+ open: deleteDialogOpen,
2889
+ onOpenChange: setDeleteDialogOpen,
2890
+ onComplete: ({ failedBundleIds }) => {
2891
+ setSelectedBundleIds([...failedBundleIds]);
2892
+ }
2893
+ })
2894
+ ]
2623
2895
  });
2624
2896
  }
2625
2897
  function FilterToolbar() {
@@ -2704,7 +2976,7 @@ function BundlesPage() {
2704
2976
  if (expandedBundleId && !bundles.some((bundle) => bundle.id === expandedBundleId)) setExpandedBundleId(void 0);
2705
2977
  }, [bundles, expandedBundleId]);
2706
2978
  if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2707
- className: "flex flex-col h-full",
2979
+ className: "flex h-svh flex-col",
2708
2980
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilterToolbar, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2709
2981
  className: "flex flex-1 flex-col gap-4 bg-muted/5 p-3 sm:p-6",
2710
2982
  children: [
@@ -2716,11 +2988,11 @@ function BundlesPage() {
2716
2988
  })]
2717
2989
  });
2718
2990
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2719
- className: "flex flex-col h-full",
2991
+ className: "flex h-svh min-h-0 flex-col",
2720
2992
  children: [
2721
2993
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilterToolbar, {}),
2722
2994
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2723
- className: "flex flex-1 flex-col gap-6 bg-muted/5 p-3 sm:p-6",
2995
+ className: "flex min-h-0 min-w-0 flex-1 flex-col gap-6 bg-muted/5 p-3 sm:p-6",
2724
2996
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundlesTable, {
2725
2997
  bundles,
2726
2998
  pagination,