@hot-updater/console 0.31.4 → 0.33.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 (25) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/dist-BzU1qUss.js +9 -0
  3. package/.output/public/assets/main-CmNeAg9B.js +10 -0
  4. package/.output/public/assets/routes-wJZuGodi.js +54 -0
  5. package/.output/public/assets/styles-DEbMLh52.css +2 -0
  6. package/.output/server/{__tanstack-start-server-fn-resolver-5wPQ8bZ3.mjs → __tanstack-start-server-fn-resolver-ySzPUDlM.mjs} +13 -13
  7. package/.output/server/_chunks/ssr-renderer.mjs +2 -2
  8. package/.output/server/_libs/@tanstack/react-form+[...].mjs +5 -5
  9. package/.output/server/_libs/lucide-react.mjs +46 -1
  10. package/.output/server/_libs/unctx.mjs +1 -1
  11. package/.output/server/_ssr/{api-rpc-BhBKhZqY.mjs → api-rpc-Bwustks9.mjs} +2 -2
  12. package/.output/server/_ssr/{deleteBundle-D4jF5HeY.mjs → deleteBundle-CXxwjwEZ.mjs} +30 -11
  13. package/.output/server/_ssr/{router-SkApCyud.mjs → router-1tw3jh6y.mjs} +8 -5
  14. package/.output/server/_ssr/{routes-BSs-dv4D.mjs → routes-D5CmWxI7.mjs} +544 -269
  15. package/.output/server/_ssr/sidebar-CgbtXkE2.mjs +1 -1
  16. package/.output/server/_ssr/ssr.mjs +4 -4
  17. package/.output/server/_ssr/start-CoRKunFF.mjs +4 -0
  18. package/.output/server/{_tanstack-start-manifest_v-D2MqgD3d.mjs → _tanstack-start-manifest_v-AftZl081.mjs} +5 -5
  19. package/.output/server/index.mjs +66 -66
  20. package/package.json +7 -7
  21. package/.output/public/assets/dist-B5egZOkC.js +0 -9
  22. package/.output/public/assets/main-DrVuFR7r.js +0 -10
  23. package/.output/public/assets/routes-C_bgs7kg.js +0 -54
  24. package/.output/public/assets/styles-DZ0tCVA1.css +0 -2
  25. package/.output/server/_ssr/start-D0X4LIsd.mjs +0 -4
@@ -3,7 +3,7 @@ import { c as createServerFn, i as TSS_SERVER_FUNCTION } from "./createServerFn-
3
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
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";
5
5
  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";
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
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";
9
9
  import { t as Root$1 } from "../_libs/radix-ui__react-label.mjs";
@@ -14,11 +14,11 @@ import { d as useNavigate, f as useSearch } from "../_libs/@tanstack/react-route
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-5wPQ8bZ3.mjs";
17
+ import { t as getServerFnById } from "../__tanstack-start-server-fn-resolver-ySzPUDlM.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-BSs-dv4D.js
21
+ //#region node_modules/.nitro/vite/services/ssr/assets/routes-D5CmWxI7.js
22
22
  var import_jsx_runtime = require_jsx_runtime();
23
23
  var import_semver = /* @__PURE__ */ __toESM(require_semver());
24
24
  var import_react = /* @__PURE__ */ __toESM(require_react());
@@ -441,6 +441,17 @@ function replaceBundleInQueryData(data, updatedBundle) {
441
441
  data: data.data.map((bundle) => bundle.id === updatedBundle.id ? updatedBundle : bundle)
442
442
  };
443
443
  }
444
+ function removeBundleFromQueryData(data, bundleId) {
445
+ if (!data) return data;
446
+ return {
447
+ ...data,
448
+ data: data.data.filter((bundle) => bundle.id !== bundleId)
449
+ };
450
+ }
451
+ var hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
452
+ var invalidateInBackground = (queryClient, queryKey) => {
453
+ queryClient.invalidateQueries({ queryKey }).catch(() => void 0);
454
+ };
444
455
  function useConfigQuery() {
445
456
  return useQuery({
446
457
  queryKey: queryKeys.config,
@@ -495,15 +506,12 @@ function useUpdateBundleMutation() {
495
506
  const queryClient = useQueryClient();
496
507
  return useMutation({
497
508
  mutationFn: (params) => updateBundle({ data: params }),
498
- onSuccess: async ({ bundle: updatedBundle }, vars) => {
509
+ onSuccess: ({ bundle: updatedBundle }, vars) => {
499
510
  queryClient.setQueryData(queryKeys.bundle(vars.bundleId), updatedBundle);
500
511
  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
- ]);
512
+ invalidateInBackground(queryClient, queryKeys.bundles.all);
513
+ if (hasOwn(vars.bundle, "patches") || hasOwn(vars.bundle, "channel") || hasOwn(vars.bundle, "platform")) invalidateInBackground(queryClient, queryKeys.bundleChildren.all);
514
+ if (hasOwn(vars.bundle, "channel")) invalidateInBackground(queryClient, queryKeys.channels);
507
515
  }
508
516
  });
509
517
  }
@@ -528,6 +536,7 @@ function useDeleteBundleMutation() {
528
536
  mutationFn: (params) => deleteBundle({ data: params }),
529
537
  onSuccess: async (_, vars) => {
530
538
  queryClient.removeQueries({ queryKey: queryKeys.bundle(vars.bundleId) });
539
+ queryClient.setQueriesData({ queryKey: queryKeys.bundles.all }, (data) => removeBundleFromQueryData(data, vars.bundleId));
531
540
  await Promise.all([
532
541
  queryClient.invalidateQueries({ queryKey: queryKeys.bundles.all }),
533
542
  queryClient.invalidateQueries({ queryKey: queryKeys.bundleChildren.all }),
@@ -2296,6 +2305,209 @@ var createBundleColumns = ({ expandedBundleId, patchCountsByBundleId, onDetailCl
2296
2305
  cell: (info) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TimestampDisplay, { uuid: info.getValue() })
2297
2306
  })
2298
2307
  ];
2308
+ var statusLabels = {
2309
+ queued: "Queued",
2310
+ deleting: "Deleting",
2311
+ deleted: "Deleted",
2312
+ failed: "Failed"
2313
+ };
2314
+ var getDeleteErrorMessage = (error) => error instanceof Error ? error.message : "Delete request failed";
2315
+ var createDeleteItems = (bundles) => bundles.map((bundle) => ({
2316
+ bundle,
2317
+ status: "queued"
2318
+ }));
2319
+ function getStatusIcon(status) {
2320
+ switch (status) {
2321
+ case "failed": return {
2322
+ className: "size-3.5 text-destructive",
2323
+ Icon: CircleX
2324
+ };
2325
+ case "deleting": return {
2326
+ className: "size-3.5 animate-spin text-primary",
2327
+ Icon: LoaderCircle
2328
+ };
2329
+ case "deleted": return {
2330
+ className: "size-3.5 text-primary",
2331
+ Icon: CircleCheck
2332
+ };
2333
+ }
2334
+ }
2335
+ function DeleteStatusIcon({ status }) {
2336
+ if (status === "queued") return null;
2337
+ const label = statusLabels[status];
2338
+ const { className, Icon } = getStatusIcon(status);
2339
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tooltip$1, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipTrigger, {
2340
+ asChild: true,
2341
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2342
+ "aria-label": label,
2343
+ role: "img",
2344
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { className })
2345
+ })
2346
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipContent, { children: label })] });
2347
+ }
2348
+ function SelectedBundlesDeleteDialog({ bundles, open, onOpenChange, onComplete }) {
2349
+ const deleteBundleMutation = useDeleteBundleMutation();
2350
+ const [phase, setPhase] = (0, import_react.useState)("confirming");
2351
+ const [items, setItems] = (0, import_react.useState)(() => createDeleteItems(bundles));
2352
+ const isDeleting = phase === "deleting";
2353
+ const totalCount = items.length;
2354
+ const activeCount = items.filter((item) => item.status === "deleted").length + items.filter((item) => item.status === "failed").length;
2355
+ const failedBundleIds = items.filter((item) => item.status === "failed").map((item) => item.bundle.id);
2356
+ const hasFailures = failedBundleIds.length > 0;
2357
+ const title = phase === "confirming" ? "Delete selected bundles?" : "Deleting bundles";
2358
+ 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.`;
2359
+ const deleteButtonLabel = (0, import_react.useMemo)(() => {
2360
+ if (!isDeleting) return "Delete";
2361
+ return `Deleting ${Math.min(activeCount + 1, totalCount)}/${totalCount}`;
2362
+ }, [
2363
+ activeCount,
2364
+ isDeleting,
2365
+ totalCount
2366
+ ]);
2367
+ (0, import_react.useEffect)(() => {
2368
+ if (open && phase === "confirming") setItems(createDeleteItems(bundles));
2369
+ }, [
2370
+ bundles,
2371
+ open,
2372
+ phase
2373
+ ]);
2374
+ const handleOpenChange = (nextOpen) => {
2375
+ if (!nextOpen && isDeleting) return;
2376
+ onOpenChange(nextOpen);
2377
+ if (!nextOpen) {
2378
+ setPhase("confirming");
2379
+ setItems(createDeleteItems(bundles));
2380
+ }
2381
+ };
2382
+ const runDeletion = async (bundleIds) => {
2383
+ if (isDeleting || bundleIds.length === 0) return;
2384
+ const bundleIdSet = new Set(bundleIds);
2385
+ const deletedBundleIds = [];
2386
+ const nextFailedBundleIds = [];
2387
+ setPhase("deleting");
2388
+ setItems((currentItems) => currentItems.map((item) => bundleIdSet.has(item.bundle.id) ? {
2389
+ bundle: item.bundle,
2390
+ status: "queued"
2391
+ } : item));
2392
+ for (const bundleId of bundleIds) {
2393
+ setItems((currentItems) => currentItems.map((item) => item.bundle.id === bundleId ? {
2394
+ bundle: item.bundle,
2395
+ status: "deleting"
2396
+ } : item));
2397
+ try {
2398
+ await deleteBundleMutation.mutateAsync({ bundleId });
2399
+ deletedBundleIds.push(bundleId);
2400
+ setItems((currentItems) => currentItems.map((item) => item.bundle.id === bundleId ? {
2401
+ bundle: item.bundle,
2402
+ status: "deleted"
2403
+ } : item));
2404
+ } catch (error) {
2405
+ nextFailedBundleIds.push(bundleId);
2406
+ setItems((currentItems) => currentItems.map((item) => item.bundle.id === bundleId ? {
2407
+ bundle: item.bundle,
2408
+ message: getDeleteErrorMessage(error),
2409
+ status: "failed"
2410
+ } : item));
2411
+ }
2412
+ }
2413
+ setPhase("complete");
2414
+ onComplete({
2415
+ deletedBundleIds,
2416
+ failedBundleIds: nextFailedBundleIds
2417
+ });
2418
+ if (nextFailedBundleIds.length > 0) {
2419
+ toast.error(`${deletedBundleIds.length} deleted, ${nextFailedBundleIds.length} failed`);
2420
+ return;
2421
+ }
2422
+ toast.success(`${deletedBundleIds.length} bundles deleted successfully`);
2423
+ };
2424
+ const handleDelete = () => {
2425
+ runDeletion(items.map((item) => item.bundle.id));
2426
+ };
2427
+ const handleRetryFailed = () => {
2428
+ runDeletion(failedBundleIds);
2429
+ };
2430
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog$1, {
2431
+ open,
2432
+ onOpenChange: handleOpenChange,
2433
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent, {
2434
+ showCloseButton: !isDeleting,
2435
+ className: "sm:max-w-lg",
2436
+ onEscapeKeyDown: (event) => {
2437
+ if (isDeleting) event.preventDefault();
2438
+ },
2439
+ onInteractOutside: (event) => {
2440
+ if (isDeleting) event.preventDefault();
2441
+ },
2442
+ children: [
2443
+ /* @__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 })] }),
2444
+ phase === "confirming" ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardContent, {
2445
+ className: "max-h-[50vh] overflow-y-auto p-0",
2446
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Table, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHeader, {
2447
+ className: "sticky top-0 bg-card",
2448
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableRow, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
2449
+ className: "w-12 text-center",
2450
+ children: "Status"
2451
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, { children: "Bundle" })] })
2452
+ }), /* @__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, {
2453
+ className: "text-center align-middle",
2454
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2455
+ className: "flex justify-center",
2456
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeleteStatusIcon, { status: item.status })
2457
+ })
2458
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
2459
+ className: "whitespace-normal",
2460
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2461
+ className: "min-w-0",
2462
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2463
+ className: "break-all font-mono text-[11px] text-foreground",
2464
+ children: item.bundle.id
2465
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2466
+ className: "mt-0.5 flex flex-wrap gap-x-2 gap-y-1 text-[11px] text-muted-foreground",
2467
+ children: [
2468
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: item.bundle.channel }),
2469
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: item.bundle.platform }),
2470
+ item.message ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2471
+ className: "text-destructive",
2472
+ children: item.message
2473
+ }) : null
2474
+ ]
2475
+ })]
2476
+ })
2477
+ })] }, item.bundle.id)) })] })
2478
+ }) }),
2479
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogFooter, { children: [
2480
+ phase === "confirming" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
2481
+ variant: "outline",
2482
+ onClick: () => handleOpenChange(false),
2483
+ children: "Cancel"
2484
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2485
+ variant: "destructive",
2486
+ onClick: handleDelete,
2487
+ disabled: totalCount === 0,
2488
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Trash2, { "data-icon": "inline-start" }), "Delete"]
2489
+ })] }) : null,
2490
+ phase === "deleting" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2491
+ variant: "destructive",
2492
+ disabled: true,
2493
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoaderCircle, {
2494
+ "data-icon": "inline-start",
2495
+ className: "animate-spin"
2496
+ }), deleteButtonLabel]
2497
+ }) : null,
2498
+ phase === "complete" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
2499
+ variant: "outline",
2500
+ onClick: () => handleOpenChange(false),
2501
+ children: "Close"
2502
+ }), hasFailures ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2503
+ onClick: handleRetryFailed,
2504
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RotateCcw, { "data-icon": "inline-start" }), "Retry failed"]
2505
+ }) : null] }) : null
2506
+ ] })
2507
+ ]
2508
+ })
2509
+ });
2510
+ }
2299
2511
  function MobileStatusBadge({ enabled, trueLabel, falseLabel, falseIcon = "x", trueTone = "success" }) {
2300
2512
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
2301
2513
  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 +2522,13 @@ function MobileStatusBadge({ enabled, trueLabel, falseLabel, falseIcon = "x", tr
2310
2522
  function BundlesTable({ bundles, pagination, expandedBundleId, selectedBundleId, onExpandedBundleChange, onDetailClick }) {
2311
2523
  const { setFilters } = useFilterParams();
2312
2524
  const isMobile = useIsMobile();
2525
+ const [selectedBundleIds, setSelectedBundleIds] = (0, import_react.useState)([]);
2526
+ const [deleteDialogOpen, setDeleteDialogOpen] = (0, import_react.useState)(false);
2313
2527
  const cursorPagination = pagination;
2314
- const { data: patchCountsByBundleId = {} } = useBundleChildCountsQuery(bundles.map((bundle) => bundle.id));
2528
+ const bundleIds = bundles.map((bundle) => bundle.id);
2529
+ const selectedBundles = bundles.filter((bundle) => selectedBundleIds.includes(bundle.id));
2530
+ const allBundlesSelected = bundles.length > 0 && selectedBundles.length === bundles.length;
2531
+ const { data: patchCountsByBundleId = {} } = useBundleChildCountsQuery(bundleIds);
2315
2532
  const { data: childBundles = [], isLoading: isChildBundlesLoading } = useBundleChildrenQuery(expandedBundleId ?? "");
2316
2533
  const bundleColumns = createBundleColumns({
2317
2534
  expandedBundleId,
@@ -2346,280 +2563,338 @@ function BundlesTable({ bundles, pagination, expandedBundleId, selectedBundleId,
2346
2563
  before: void 0
2347
2564
  });
2348
2565
  };
2566
+ const toggleBundleSelection = (bundleId) => {
2567
+ setSelectedBundleIds((current) => current.includes(bundleId) ? current.filter((selectedBundleId) => selectedBundleId !== bundleId) : [...current, bundleId]);
2568
+ };
2569
+ const handleToggleAllBundles = () => {
2570
+ setSelectedBundleIds(allBundlesSelected ? [] : bundleIds);
2571
+ };
2349
2572
  const startEntry = bundles.length === 0 ? 0 : (currentPage - 1) * 20 + 1;
2350
2573
  const endEntry = startEntry === 0 ? 0 : startEntry + bundles.length - 1;
2351
2574
  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",
2575
+ className: "flex min-h-0 min-w-0 flex-1 flex-col gap-4",
2576
+ children: [
2577
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2578
+ 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",
2579
+ children: isMobile ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2580
+ className: "flex h-full flex-col overflow-y-auto",
2581
+ children: bundles.length ? bundles.map((bundle) => {
2582
+ const isExpanded = bundle.id === expandedBundleId;
2583
+ const panelId = `bundle-lineage-panel-${bundle.id}`;
2584
+ const rolloutPercentage = (bundle.rolloutCohortCount ?? 1e3) / 10;
2585
+ const patchCount = patchCountsByBundleId[bundle.id];
2586
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2587
+ "data-state": bundle.id === selectedBundleId ? "selected" : void 0,
2588
+ className: cn("border-b border-border/60 last:border-b-0 data-[state=selected]:bg-muted/20", isExpanded && "bg-primary/5"),
2589
+ children: [
2590
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
2591
+ className: "flex items-center gap-2 border-b border-border/60 px-4 py-3 text-xs font-medium text-muted-foreground",
2592
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
2593
+ type: "checkbox",
2594
+ checked: selectedBundleIds.includes(bundle.id),
2595
+ className: "size-4 accent-primary",
2596
+ onChange: () => toggleBundleSelection(bundle.id)
2597
+ }), "Select bundle"]
2598
+ }),
2599
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
2600
+ type: "button",
2601
+ className: "flex w-full flex-col gap-4 p-4 text-left",
2602
+ onClick: () => onDetailClick(bundle),
2603
+ children: [
2604
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2605
+ className: "flex items-start justify-between gap-3",
2606
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2607
+ className: "min-w-0 space-y-2",
2426
2608
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2427
- className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2428
- children: "Patches"
2609
+ className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-muted-foreground/70",
2610
+ children: "Bundle"
2429
2611
  }), /* @__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"}` : "-"
2612
+ className: "min-w-0 text-sm font-medium",
2613
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleIdDisplay, {
2614
+ bundleId: bundle.id,
2615
+ maxLength: 18,
2616
+ fullOnMobile: true
2617
+ })
2432
2618
  })]
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
2619
  }), /* @__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
2620
+ className: "shrink-0",
2621
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChannelBadge, { channel: bundle.channel })
2622
+ })]
2623
+ }),
2624
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2625
+ className: "grid grid-cols-2 gap-3 text-sm",
2626
+ children: [
2627
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2628
+ className: "rounded-md bg-muted/40 p-3",
2629
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2630
+ className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2631
+ children: "Platform"
2632
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2633
+ className: "flex items-center gap-2",
2634
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
2635
+ platform: bundle.platform,
2636
+ className: "h-4 w-4"
2637
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: bundle.platform === "ios" ? "iOS" : "Android" })]
2638
+ })]
2639
+ }),
2640
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2641
+ className: "rounded-md bg-muted/40 p-3",
2642
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2643
+ className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2644
+ children: "Created"
2645
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2646
+ className: "text-xs text-foreground",
2647
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TimestampDisplay, { uuid: bundle.id })
2648
+ })]
2649
+ }),
2650
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2651
+ className: "rounded-md bg-muted/40 p-3",
2652
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2653
+ className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2654
+ children: "Rollout"
2655
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RolloutPercentageBadge, { percentage: rolloutPercentage })]
2656
+ }),
2657
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2658
+ className: "rounded-md bg-muted/40 p-3",
2659
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2660
+ className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2661
+ children: "Patches"
2662
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2663
+ className: "text-xs text-foreground",
2664
+ children: patchCount === void 0 ? "Checking" : patchCount > 0 ? `${patchCount} ${patchCount === 1 ? "patch" : "patches"}` : "-"
2665
+ })]
2666
+ }),
2667
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2668
+ className: "rounded-md bg-muted/40 p-3",
2669
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2670
+ className: "mb-1 text-[11px] font-medium uppercase text-muted-foreground/70",
2671
+ children: "Status"
2672
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2673
+ className: "flex flex-wrap items-center gap-2",
2674
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MobileStatusBadge, {
2675
+ enabled: bundle.enabled,
2676
+ trueLabel: "Enabled",
2677
+ falseLabel: "Disabled"
2678
+ }), bundle.shouldForceUpdate ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MobileStatusBadge, {
2679
+ enabled: bundle.shouldForceUpdate,
2680
+ trueLabel: "Force update",
2681
+ falseLabel: "Optional",
2682
+ falseIcon: "minus",
2683
+ trueTone: "warning"
2684
+ }) : null]
2471
2685
  })]
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
2686
  })
2687
+ ]
2688
+ }),
2689
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2690
+ className: "grid gap-2 rounded-md border border-border/70 bg-background/80 p-3 text-sm",
2691
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2692
+ className: "flex items-center justify-between gap-3",
2693
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2694
+ className: "text-muted-foreground",
2695
+ children: "Target"
2696
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2697
+ className: "min-w-0 text-right",
2698
+ children: bundle.fingerprintHash ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
2699
+ translate: "no",
2700
+ className: "inline-flex min-w-0 items-start gap-2 font-mono text-xs",
2701
+ 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, {
2702
+ value: bundle.fingerprintHash,
2703
+ maxLength: 12
2704
+ })]
2705
+ }) : bundle.targetAppVersion ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
2706
+ translate: "no",
2707
+ className: "inline-flex items-center gap-2 text-xs",
2708
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Package, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), bundle.targetAppVersion]
2709
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2710
+ className: "text-xs text-muted-foreground",
2711
+ children: "-"
2712
+ })
2713
+ })]
2714
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2715
+ className: "flex items-start justify-between gap-3",
2716
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2717
+ className: "text-muted-foreground",
2718
+ children: "Message"
2719
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2720
+ className: "min-w-0 text-right text-xs text-foreground/80",
2721
+ children: bundle.message || "-"
2722
+ })]
2480
2723
  })]
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
- })]
2724
+ })
2725
+ ]
2726
+ }),
2727
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2728
+ className: "border-t border-border/60 px-4 py-3",
2729
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2730
+ type: "button",
2731
+ variant: "ghost",
2732
+ className: "h-9 w-full justify-between px-3 text-sm",
2733
+ "aria-label": isExpanded ? "Hide Lineage" : "Show Lineage",
2734
+ "aria-controls": panelId,
2735
+ "aria-expanded": isExpanded,
2736
+ onClick: () => onExpandedBundleChange(isExpanded ? void 0 : bundle.id),
2737
+ 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, {
2738
+ className: "h-4 w-4",
2739
+ "aria-hidden": "true"
2740
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, {
2741
+ className: "h-4 w-4",
2742
+ "aria-hidden": "true"
2490
2743
  })]
2491
2744
  })
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, {
2745
+ }),
2746
+ isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleChildrenPanel, {
2747
+ panelId,
2748
+ bundle,
2749
+ bundles: childBundles,
2750
+ loading: isChildBundlesLoading,
2751
+ onDetailClick
2752
+ }) : null
2753
+ ]
2754
+ }, bundle.id);
2755
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2756
+ className: "flex h-32 items-center justify-center px-6 text-center text-sm text-muted-foreground",
2757
+ children: "No bundles found matching your filters."
2758
+ })
2759
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Table, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHeader, {
2760
+ className: "bg-muted/40",
2761
+ children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableRow, {
2762
+ className: "border-b border-border/60 hover:bg-transparent",
2763
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
2764
+ className: "w-10",
2765
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
2766
+ type: "checkbox",
2767
+ checked: allBundlesSelected,
2768
+ "aria-label": "Select all bundles",
2769
+ className: "size-4 accent-primary",
2770
+ onChange: handleToggleAllBundles
2771
+ })
2772
+ }), headerGroup.headers.map((header) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
2773
+ className: "h-10 text-xs font-semibold uppercase text-muted-foreground/70",
2774
+ children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())
2775
+ }, header.id))]
2776
+ }, headerGroup.id))
2777
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableBody, { children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => {
2778
+ const isExpanded = row.original.id === expandedBundleId;
2779
+ const panelId = `bundle-lineage-panel-${row.original.id}`;
2780
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableRow, {
2781
+ "data-state": row.original.id === selectedBundleId ? "selected" : void 0,
2782
+ 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"),
2783
+ onClick: () => onDetailClick(row.original),
2784
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
2785
+ className: "py-3",
2786
+ onClick: (event) => event.stopPropagation(),
2787
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
2788
+ type: "checkbox",
2789
+ checked: selectedBundleIds.includes(row.original.id),
2790
+ "aria-label": `Select bundle ${row.original.id}`,
2791
+ className: "size-4 accent-primary",
2792
+ onChange: () => toggleBundleSelection(row.original.id)
2793
+ })
2794
+ }), row.getVisibleCells().map((cell) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
2795
+ className: "py-3",
2796
+ children: flexRender(cell.column.columnDef.cell, cell.getContext())
2797
+ }, cell.id))]
2798
+ }), isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableRow, {
2799
+ className: "hover:bg-transparent",
2800
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
2801
+ colSpan: bundleColumns.length + 1,
2802
+ className: "border-t-0 p-0",
2803
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleChildrenPanel, {
2514
2804
  panelId,
2515
- bundle,
2805
+ bundle: row.original,
2516
2806
  bundles: childBundles,
2517
2807
  loading: isChildBundlesLoading,
2518
2808
  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
2809
+ })
2557
2810
  })
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 ",
2811
+ }) : null] }, row.original.id);
2812
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
2813
+ colSpan: bundleColumns.length + 1,
2814
+ className: "h-32 text-center text-muted-foreground",
2815
+ children: "No bundles found matching your filters."
2816
+ }) }) })] })
2817
+ }),
2818
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2819
+ className: "flex flex-col gap-3 px-2 sm:flex-row sm:items-center sm:justify-between",
2820
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2821
+ className: "text-xs font-medium text-muted-foreground",
2822
+ children: [
2823
+ "Showing ",
2824
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2825
+ className: "text-foreground",
2826
+ children: startEntry
2827
+ }),
2828
+ " to",
2829
+ " ",
2830
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2831
+ className: "text-foreground",
2832
+ children: endEntry
2833
+ }),
2834
+ " entries"
2835
+ ]
2836
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2837
+ className: "flex flex-wrap items-center gap-3 sm:justify-end",
2838
+ children: [
2839
+ selectedBundles.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2840
+ type: "button",
2841
+ variant: "destructive",
2842
+ size: "sm",
2843
+ className: "h-8 flex-1 px-3 text-xs sm:flex-none",
2844
+ onClick: () => setDeleteDialogOpen(true),
2845
+ children: [
2846
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Trash2, { "data-icon": "inline-start" }),
2847
+ "Delete selected (",
2848
+ selectedBundles.length,
2849
+ ")"
2850
+ ]
2851
+ }) : null,
2852
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2853
+ className: "text-xs font-medium text-muted-foreground",
2854
+ children: [
2855
+ "Page ",
2597
2856
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2598
2857
  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
- })]
2858
+ children: currentPage
2859
+ }),
2860
+ totalPages > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2861
+ " ",
2862
+ "of ",
2863
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2864
+ className: "text-foreground",
2865
+ children: totalPages
2866
+ })
2867
+ ] }) : null
2868
+ ]
2869
+ }),
2870
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2871
+ variant: "outline",
2872
+ size: "sm",
2873
+ onClick: handlePreviousPage,
2874
+ disabled: !hasPreviousPage,
2875
+ className: "h-8 flex-1 px-3 text-xs sm:flex-none",
2876
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronLeft, { "data-icon": "inline-start" }), "Previous"]
2877
+ }),
2878
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
2879
+ variant: "outline",
2880
+ size: "sm",
2881
+ onClick: handleNextPage,
2882
+ disabled: !hasNextPage,
2883
+ className: "h-8 flex-1 px-3 text-xs sm:flex-none",
2884
+ children: ["Next", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronRight, { "data-icon": "inline-end" })]
2885
+ })
2886
+ ]
2887
+ })]
2888
+ }),
2889
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectedBundlesDeleteDialog, {
2890
+ bundles: selectedBundles,
2891
+ open: deleteDialogOpen,
2892
+ onOpenChange: setDeleteDialogOpen,
2893
+ onComplete: ({ failedBundleIds }) => {
2894
+ setSelectedBundleIds([...failedBundleIds]);
2895
+ }
2896
+ })
2897
+ ]
2623
2898
  });
2624
2899
  }
2625
2900
  function FilterToolbar() {
@@ -2704,7 +2979,7 @@ function BundlesPage() {
2704
2979
  if (expandedBundleId && !bundles.some((bundle) => bundle.id === expandedBundleId)) setExpandedBundleId(void 0);
2705
2980
  }, [bundles, expandedBundleId]);
2706
2981
  if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2707
- className: "flex flex-col h-full",
2982
+ className: "flex h-svh flex-col",
2708
2983
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilterToolbar, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2709
2984
  className: "flex flex-1 flex-col gap-4 bg-muted/5 p-3 sm:p-6",
2710
2985
  children: [
@@ -2716,11 +2991,11 @@ function BundlesPage() {
2716
2991
  })]
2717
2992
  });
2718
2993
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
2719
- className: "flex flex-col h-full",
2994
+ className: "flex h-svh min-h-0 flex-col",
2720
2995
  children: [
2721
2996
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilterToolbar, {}),
2722
2997
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
2723
- className: "flex flex-1 flex-col gap-6 bg-muted/5 p-3 sm:p-6",
2998
+ className: "flex min-h-0 min-w-0 flex-1 flex-col gap-6 bg-muted/5 p-3 sm:p-6",
2724
2999
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundlesTable, {
2725
3000
  bundles,
2726
3001
  pagination,