@hot-updater/console 0.29.7 → 0.30.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.
@@ -8,7 +8,7 @@ import { t as QueryClient } from "../_libs/tanstack__query-core.mjs";
8
8
  import { r as QueryClientProvider } from "../_libs/tanstack__react-query.mjs";
9
9
  import { t as z } from "../_libs/next-themes.mjs";
10
10
  import { t as Toaster } from "../_libs/sonner.mjs";
11
- //#region node_modules/.nitro/vite/services/ssr/assets/router-DB7WR5Zr.js
11
+ //#region node_modules/.nitro/vite/services/ssr/assets/router-DjaryVgW.js
12
12
  var import_jsx_runtime = require_jsx_runtime();
13
13
  var import_react = /* @__PURE__ */ __toESM(require_react());
14
14
  function HotUpdaterLogo({ className }) {
@@ -227,7 +227,7 @@ function RootLayout() {
227
227
  (0, import_react.useEffect)(() => {}, []);
228
228
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SidebarProvider, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppSidebar, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarInset, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Outlet, {}) })] });
229
229
  }
230
- var $$splitComponentImporter = () => import("./routes-FhQwSgxW.mjs");
230
+ var $$splitComponentImporter = () => import("./routes-_o5eQ953.mjs");
231
231
  var rootRouteChildren = { IndexRoute: createFileRoute("/")({
232
232
  component: lazyRouteComponent($$splitComponentImporter, "component"),
233
233
  validateSearch: (search) => {
@@ -17,7 +17,7 @@ import { n as toast } from "../_libs/sonner.mjs";
17
17
  import { t as require_semver } from "../_libs/semver.mjs";
18
18
  import { i as getCoreRowModel, n as useReactTable, r as createColumnHelper, t as flexRender } from "../_libs/@tanstack/react-table+[...].mjs";
19
19
  import { n as require_dayjs_min, t as require_relativeTime } from "../_libs/dayjs.mjs";
20
- //#region node_modules/.nitro/vite/services/ssr/assets/routes-FhQwSgxW.js
20
+ //#region node_modules/.nitro/vite/services/ssr/assets/routes-_o5eQ953.js
21
21
  var import_jsx_runtime = require_jsx_runtime();
22
22
  var import_react = /* @__PURE__ */ __toESM(require_react());
23
23
  var import_semver = /* @__PURE__ */ __toESM(require_semver());
@@ -439,7 +439,13 @@ function AlertDialogCancel({ className, variant = "outline", size = "default", .
439
439
  }
440
440
  function DeleteBundleDialog({ bundle, open, onOpenChange, onSuccess }) {
441
441
  const deleteBundleMutation = useDeleteBundleMutation();
442
+ const isDeleting = deleteBundleMutation.isPending;
443
+ const handleOpenChange = (nextOpen) => {
444
+ if (!nextOpen && isDeleting) return;
445
+ onOpenChange(nextOpen);
446
+ };
442
447
  const handleDelete = async () => {
448
+ if (isDeleting) return;
443
449
  try {
444
450
  await deleteBundleMutation.mutateAsync({ bundleId: bundle.id });
445
451
  toast.success("Bundle deleted successfully");
@@ -452,37 +458,48 @@ function DeleteBundleDialog({ bundle, open, onOpenChange, onSuccess }) {
452
458
  };
453
459
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialog$1, {
454
460
  open,
455
- onOpenChange,
456
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDialogContent, { children: [
457
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDialogHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogTitle, { children: "Are you sure?" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogDescription, { children: "This action cannot be undone. This will permanently delete the bundle and remove it from storage." })] }),
458
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
459
- className: "my-4 p-4 bg-muted rounded-lg",
460
- children: [
461
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
462
- className: "text-sm font-medium mb-1",
463
- children: "Bundle ID:"
464
- }),
465
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
466
- className: "text-xs font-mono text-muted-foreground break-all",
467
- children: bundle.id
468
- }),
469
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
470
- className: "text-sm font-medium mt-3 mb-1",
471
- children: "Channel:"
472
- }),
473
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
474
- className: "text-xs text-muted-foreground",
475
- children: bundle.channel
476
- })
477
- ]
478
- }),
479
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDialogFooter, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogCancel, { children: "Cancel" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogAction, {
480
- onClick: handleDelete,
481
- className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
482
- disabled: deleteBundleMutation.isPending,
483
- children: deleteBundleMutation.isPending ? "Deleting..." : "Delete"
484
- })] })
485
- ] })
461
+ onOpenChange: handleOpenChange,
462
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDialogContent, {
463
+ onEscapeKeyDown: (event) => {
464
+ if (isDeleting) event.preventDefault();
465
+ },
466
+ children: [
467
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDialogHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogTitle, { children: "Are you sure?" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogDescription, { children: "This action cannot be undone. This will permanently delete the bundle and remove it from storage." })] }),
468
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
469
+ className: "my-4 p-4 bg-muted rounded-lg",
470
+ children: [
471
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
472
+ className: "text-sm font-medium mb-1",
473
+ children: "Bundle ID:"
474
+ }),
475
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
476
+ className: "text-xs font-mono text-muted-foreground break-all",
477
+ children: bundle.id
478
+ }),
479
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
480
+ className: "text-sm font-medium mt-3 mb-1",
481
+ children: "Channel:"
482
+ }),
483
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
484
+ className: "text-xs text-muted-foreground",
485
+ children: bundle.channel
486
+ })
487
+ ]
488
+ }),
489
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDialogFooter, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogCancel, {
490
+ disabled: isDeleting,
491
+ children: "Cancel"
492
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogAction, {
493
+ onClick: (event) => {
494
+ event.preventDefault();
495
+ handleDelete();
496
+ },
497
+ className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
498
+ disabled: isDeleting,
499
+ children: isDeleting ? "Deleting..." : "Delete"
500
+ })] })
501
+ ]
502
+ })
486
503
  });
487
504
  }
488
505
  function Dialog$1({ ...props }) {
@@ -703,18 +720,25 @@ function PromoteChannelDialog({ bundle, open, onOpenChange, onSuccess }) {
703
720
  const { setBundleId } = useFilterParams();
704
721
  const { data: channels = [] } = useChannelsQuery();
705
722
  const promoteBundleMutation = usePromoteBundleMutation();
723
+ const isPromoting = promoteBundleMutation.isPending;
706
724
  const availableChannels = channels.filter((c) => c !== bundle.channel);
707
725
  const isCopy = action === "copy";
708
726
  const normalizedTargetChannel = targetChannel.trim();
709
727
  const isSameChannel = normalizedTargetChannel === bundle.channel;
710
728
  const displayedCopyBundleId = copyBundleId || "Generating bundle ID...";
729
+ const resetDialogState = () => {
730
+ setTargetChannel("");
731
+ setAction("move");
732
+ setCopyBundleId("");
733
+ };
734
+ const closeDialog = () => {
735
+ onOpenChange(false);
736
+ resetDialogState();
737
+ };
711
738
  const handleOpenChange = (nextOpen) => {
739
+ if (!nextOpen && isPromoting) return;
712
740
  onOpenChange(nextOpen);
713
- if (!nextOpen) {
714
- setTargetChannel("");
715
- setAction("move");
716
- setCopyBundleId("");
717
- }
741
+ if (!nextOpen) resetDialogState();
718
742
  };
719
743
  const handleActionChange = (value) => {
720
744
  const nextAction = value;
@@ -746,7 +770,7 @@ function PromoteChannelDialog({ bundle, open, onOpenChange, onSuccess }) {
746
770
  targetChannel: normalizedTargetChannel
747
771
  });
748
772
  const promotedBundleId = promotedBundle.id;
749
- handleOpenChange(false);
773
+ closeDialog();
750
774
  onSuccess?.();
751
775
  toast.success(isCopy ? `Bundle copied to ${normalizedTargetChannel}` : `Bundle moved to ${normalizedTargetChannel}`, {
752
776
  description: `bundleId: ${promotedBundleId}`,
@@ -763,108 +787,123 @@ function PromoteChannelDialog({ bundle, open, onOpenChange, onSuccess }) {
763
787
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog$1, {
764
788
  open,
765
789
  onOpenChange: handleOpenChange,
766
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent, { children: [
767
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTitle, { children: "Promote to Channel" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogDescription, { children: "Choose how to promote this bundle, then select the target channel." })] }),
768
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
769
- className: "space-y-4 py-4",
770
- children: [
771
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
772
- className: "space-y-2",
773
- children: [
774
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, {
775
- htmlFor: "promote-action",
776
- children: "Action"
777
- }),
778
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
779
- value: action,
780
- onValueChange: handleActionChange,
781
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
782
- id: "promote-action",
783
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { placeholder: "Select an action" })
784
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
785
- value: "move",
786
- children: "Move bundle"
787
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
788
- value: "copy",
789
- children: "Copy bundle"
790
- })] })]
791
- }),
792
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
793
- className: "text-xs text-muted-foreground",
794
- children: isCopy ? "Create a new bundle in the target channel and keep the original in the current channel." : "Move the current bundle to the target channel without creating a new bundle ID."
795
- })
796
- ]
797
- }),
798
- isCopy && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
799
- className: "space-y-2",
800
- children: [
801
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, {
802
- htmlFor: "copy-bundle-id",
803
- children: "New Bundle ID"
804
- }),
805
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
806
- id: "copy-bundle-id",
807
- value: displayedCopyBundleId,
808
- readOnly: true
809
- }),
810
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
811
- className: "font-mono text-xs text-muted-foreground",
812
- children: displayedCopyBundleId
813
- })
814
- ]
815
- }),
816
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
817
- className: "space-y-2",
818
- children: [
819
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, {
820
- htmlFor: "target-channel",
821
- children: "Target Channel"
822
- }),
823
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
824
- id: "target-channel",
825
- value: targetChannel,
826
- onChange: (event) => setTargetChannel(event.target.value),
827
- placeholder: "Enter a channel name",
828
- list: "available-channels",
829
- "aria-invalid": isSameChannel
830
- }),
831
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("datalist", {
832
- id: "available-channels",
833
- children: availableChannels.map((channel) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: channel }, channel))
834
- }),
835
- availableChannels.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
836
- className: "flex flex-wrap gap-2",
837
- children: availableChannels.map((channel) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
838
- type: "button",
839
- variant: "outline",
840
- size: "xs",
841
- onClick: () => setTargetChannel(channel),
842
- children: channel
843
- }, channel))
844
- }),
845
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
846
- className: "text-xs text-muted-foreground",
847
- children: "Choose an existing channel or enter a new one."
848
- }),
849
- isSameChannel && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
850
- className: "text-xs text-destructive",
851
- role: "alert",
852
- children: "Target channel must be different from the current channel."
853
- })
854
- ]
855
- })
856
- ]
857
- }),
858
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogFooter, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
859
- variant: "outline",
860
- onClick: () => handleOpenChange(false),
861
- children: "Cancel"
862
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
863
- onClick: handlePromote,
864
- disabled: !normalizedTargetChannel || isCopy && !copyBundleId || isSameChannel || promoteBundleMutation.isPending,
865
- children: isCopy ? "Copy" : "Move"
866
- })] })
867
- ] })
790
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent, {
791
+ showCloseButton: !isPromoting,
792
+ onEscapeKeyDown: (event) => {
793
+ if (isPromoting) event.preventDefault();
794
+ },
795
+ onInteractOutside: (event) => {
796
+ if (isPromoting) event.preventDefault();
797
+ },
798
+ children: [
799
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTitle, { children: "Promote to Channel" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogDescription, { children: "Choose how to promote this bundle, then select the target channel." })] }),
800
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
801
+ className: "space-y-4 py-4",
802
+ children: [
803
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
804
+ className: "space-y-2",
805
+ children: [
806
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, {
807
+ htmlFor: "promote-action",
808
+ children: "Action"
809
+ }),
810
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
811
+ value: action,
812
+ onValueChange: handleActionChange,
813
+ disabled: isPromoting,
814
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
815
+ id: "promote-action",
816
+ disabled: isPromoting,
817
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { placeholder: "Select an action" })
818
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
819
+ value: "move",
820
+ children: "Move bundle"
821
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
822
+ value: "copy",
823
+ children: "Copy bundle"
824
+ })] })]
825
+ }),
826
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
827
+ className: "text-xs text-muted-foreground",
828
+ children: isCopy ? "Create a new bundle in the target channel and keep the original in the current channel." : "Move the current bundle to the target channel without creating a new bundle ID."
829
+ })
830
+ ]
831
+ }),
832
+ isCopy && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
833
+ className: "space-y-2",
834
+ children: [
835
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, {
836
+ htmlFor: "copy-bundle-id",
837
+ children: "New Bundle ID"
838
+ }),
839
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
840
+ id: "copy-bundle-id",
841
+ value: displayedCopyBundleId,
842
+ readOnly: true,
843
+ disabled: isPromoting
844
+ }),
845
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
846
+ className: "font-mono text-xs text-muted-foreground",
847
+ children: displayedCopyBundleId
848
+ })
849
+ ]
850
+ }),
851
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
852
+ className: "space-y-2",
853
+ children: [
854
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, {
855
+ htmlFor: "target-channel",
856
+ children: "Target Channel"
857
+ }),
858
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
859
+ id: "target-channel",
860
+ value: targetChannel,
861
+ onChange: (event) => setTargetChannel(event.target.value),
862
+ placeholder: "Enter a channel name",
863
+ list: "available-channels",
864
+ "aria-invalid": isSameChannel,
865
+ disabled: isPromoting
866
+ }),
867
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("datalist", {
868
+ id: "available-channels",
869
+ children: availableChannels.map((channel) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: channel }, channel))
870
+ }),
871
+ availableChannels.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
872
+ className: "flex flex-wrap gap-2",
873
+ children: availableChannels.map((channel) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
874
+ type: "button",
875
+ variant: "outline",
876
+ size: "xs",
877
+ onClick: () => setTargetChannel(channel),
878
+ disabled: isPromoting,
879
+ children: channel
880
+ }, channel))
881
+ }),
882
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
883
+ className: "text-xs text-muted-foreground",
884
+ children: "Choose an existing channel or enter a new one."
885
+ }),
886
+ isSameChannel && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
887
+ className: "text-xs text-destructive",
888
+ role: "alert",
889
+ children: "Target channel must be different from the current channel."
890
+ })
891
+ ]
892
+ })
893
+ ]
894
+ }),
895
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogFooter, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
896
+ variant: "outline",
897
+ onClick: () => handleOpenChange(false),
898
+ disabled: isPromoting,
899
+ children: "Cancel"
900
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
901
+ onClick: handlePromote,
902
+ disabled: !normalizedTargetChannel || isCopy && !copyBundleId || isSameChannel || isPromoting,
903
+ children: isPromoting ? isCopy ? "Copying..." : "Moving..." : isCopy ? "Copy" : "Move"
904
+ })] })
905
+ ]
906
+ })
868
907
  });
869
908
  }
870
909
  function Card({ className, ...props }) {
@@ -904,8 +943,9 @@ function CardContent({ className, ...props }) {
904
943
  }
905
944
  function RolloutCohortsDialog({ bundleId, rolloutCohortCount, targetCohorts, triggerLabel = "View Cohorts", triggerVariant = "outline", triggerSize = "sm", triggerClassName }) {
906
945
  const normalizedRolloutCount = normalizeRolloutCohortCount(rolloutCohortCount);
907
- const hasTargetCohortOverride = (targetCohorts?.length ?? 0) > 0;
908
- if (!(normalizedRolloutCount > 0 && normalizedRolloutCount < 1e3 && !hasTargetCohortOverride)) return null;
946
+ const normalizedTargetCohorts = targetCohorts ?? [];
947
+ const hasTargetCohorts = normalizedTargetCohorts.length > 0;
948
+ if (!(normalizedRolloutCount > 0 && normalizedRolloutCount < 1e3)) return null;
909
949
  const rolloutCohorts = Array.from({ length: NUMERIC_COHORT_SIZE }, (_, index) => index + 1).filter((cohortValue) => getNumericCohortRolloutPosition(bundleId, cohortValue) < normalizedRolloutCount);
910
950
  const rolloutPercentage = (normalizedRolloutCount / 10).toFixed(1);
911
951
  const excludedCount = NUMERIC_COHORT_SIZE - rolloutCohorts.length;
@@ -929,7 +969,8 @@ function RolloutCohortsDialog({ bundleId, rolloutCohortCount, targetCohorts, tri
929
969
  rolloutCohorts.length,
930
970
  " of ",
931
971
  NUMERIC_COHORT_SIZE,
932
- " numeric cohorts. The selected set stays stable for this bundle as you expand or shrink rollout."
972
+ " numeric cohorts. The selected set stays stable for this bundle as you expand or shrink rollout.",
973
+ hasTargetCohorts ? " Target Cohorts are added on top of this numeric rollout." : ""
933
974
  ] })] }),
934
975
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
935
976
  className: "grid gap-3 sm:grid-cols-3",
@@ -977,6 +1018,23 @@ function RolloutCohortsDialog({ bundleId, rolloutCohortCount, targetCohorts, tri
977
1018
  })
978
1019
  })
979
1020
  })] }),
1021
+ hasTargetCohorts ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
1022
+ className: "p-4 pb-3",
1023
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, {
1024
+ className: "text-sm",
1025
+ children: "Target Cohorts"
1026
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardDescription, { children: "These cohorts are also included, even if they are outside the numeric rollout." })]
1027
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardContent, {
1028
+ className: "p-4 pt-0",
1029
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
1030
+ className: "flex flex-wrap gap-2 rounded-lg border bg-muted/20 p-3",
1031
+ children: normalizedTargetCohorts.map((cohort) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
1032
+ variant: "secondary",
1033
+ className: "font-mono",
1034
+ children: cohort
1035
+ }, cohort))
1036
+ })
1037
+ })] }) : null,
980
1038
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogFooter, { showCloseButton: true })
981
1039
  ]
982
1040
  })] });
@@ -1008,7 +1066,7 @@ function getDefaultValues(bundle) {
1008
1066
  };
1009
1067
  }
1010
1068
  var formatRolloutPercentage = (rolloutCohortCount) => (rolloutCohortCount / 10).toFixed(1);
1011
- function BundleEditorForm({ bundle, onClose }) {
1069
+ function BundleEditorForm({ bundle, onClose, onBusyChange }) {
1012
1070
  const bundleDownloadUrlMutation = useBundleDownloadUrlMutation();
1013
1071
  const updateBundleMutation = useUpdateBundleMutation();
1014
1072
  const [showPromoteDialog, setShowPromoteDialog] = (0, import_react.useState)(false);
@@ -1060,6 +1118,14 @@ function BundleEditorForm({ bundle, onClose }) {
1060
1118
  };
1061
1119
  const hasTargetAppVersionError = Boolean(targetAppVersionValidation.error);
1062
1120
  const isDownloading = bundleDownloadUrlMutation.isPending;
1121
+ (0, import_react.useEffect)(() => {
1122
+ onBusyChange?.(isSaving);
1123
+ }, [isSaving, onBusyChange]);
1124
+ (0, import_react.useEffect)(() => {
1125
+ return () => {
1126
+ onBusyChange?.(false);
1127
+ };
1128
+ }, [onBusyChange]);
1063
1129
  const handleAddCohort = () => {
1064
1130
  const normalizedCohort = normalizeCohortValue(newCohort);
1065
1131
  if (!normalizedCohort) return;
@@ -1302,6 +1368,7 @@ function BundleEditorForm({ bundle, onClose }) {
1302
1368
  size: "sm",
1303
1369
  className: "w-full",
1304
1370
  onClick: () => setShowPromoteDialog(true),
1371
+ disabled: isSaving,
1305
1372
  children: "Promote to Channel"
1306
1373
  }),
1307
1374
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
@@ -1309,7 +1376,7 @@ function BundleEditorForm({ bundle, onClose }) {
1309
1376
  size: "sm",
1310
1377
  className: "w-full",
1311
1378
  onClick: handleDownloadBundle,
1312
- disabled: isDownloading,
1379
+ disabled: isSaving || isDownloading,
1313
1380
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Download, { className: "h-4 w-4" }), isDownloading ? "Preparing Download..." : "Download Bundle"]
1314
1381
  }),
1315
1382
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
@@ -1317,6 +1384,7 @@ function BundleEditorForm({ bundle, onClose }) {
1317
1384
  size: "sm",
1318
1385
  className: "w-full",
1319
1386
  onClick: () => setShowDeleteDialog(true),
1387
+ disabled: isSaving,
1320
1388
  children: "Delete Bundle"
1321
1389
  })
1322
1390
  ]
@@ -1421,11 +1489,31 @@ function BundleMetadata({ bundle }) {
1421
1489
  })] });
1422
1490
  }
1423
1491
  function BundleEditorSheet({ bundleId, bundle, loading = false, open, onOpenChange }) {
1492
+ const [isSaving, setIsSaving] = (0, import_react.useState)(false);
1493
+ (0, import_react.useEffect)(() => {
1494
+ if (!open) setIsSaving(false);
1495
+ }, [open]);
1496
+ const handleOpenChange = (nextOpen) => {
1497
+ if (!nextOpen && isSaving) return;
1498
+ if (!nextOpen) setIsSaving(false);
1499
+ onOpenChange(nextOpen);
1500
+ };
1501
+ const closeSheet = () => {
1502
+ setIsSaving(false);
1503
+ onOpenChange(false);
1504
+ };
1424
1505
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Sheet, {
1425
1506
  open,
1426
- onOpenChange,
1507
+ onOpenChange: handleOpenChange,
1427
1508
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SheetContent, {
1428
1509
  className: "w-[600px] sm:max-w-[600px] overflow-y-auto",
1510
+ showCloseButton: !isSaving,
1511
+ onEscapeKeyDown: (event) => {
1512
+ if (isSaving) event.preventDefault();
1513
+ },
1514
+ onInteractOutside: (event) => {
1515
+ if (isSaving) event.preventDefault();
1516
+ },
1429
1517
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SheetHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SheetTitle, { children: bundle ? "Edit Bundle" : "Bundle Details" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SheetDescription, { children: bundle ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleBasicInfo, { bundle }) : loading ? bundleId ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1430
1518
  className: "font-mono text-xs",
1431
1519
  children: ["Loading ", bundleId]
@@ -1436,7 +1524,8 @@ function BundleEditorSheet({ bundleId, bundle, loading = false, open, onOpenChan
1436
1524
  className: "px-6 pb-6 space-y-6",
1437
1525
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleEditorForm, {
1438
1526
  bundle,
1439
- onClose: () => onOpenChange(false)
1527
+ onClose: closeSheet,
1528
+ onBusyChange: setIsSaving
1440
1529
  }, bundle.id), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleMetadata, { bundle })]
1441
1530
  }) : loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1442
1531
  className: "px-6 pb-6 space-y-4",
@@ -4269,7 +4269,7 @@ function getResponse() {
4269
4269
  return getH3Event().res;
4270
4270
  }
4271
4271
  async function getStartManifest(matchedRoutes) {
4272
- const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-bE9hwtzg.mjs");
4272
+ const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-DlKQbuiB.mjs");
4273
4273
  const startManifest = tsrStartManifest();
4274
4274
  const rootRoute = startManifest.routes[rootRouteId] = startManifest.routes["__root__"] || {};
4275
4275
  rootRoute.assets = rootRoute.assets || [];
@@ -4747,7 +4747,7 @@ var entriesPromise;
4747
4747
  var baseManifestPromise;
4748
4748
  var cachedFinalManifestPromise;
4749
4749
  async function loadEntries() {
4750
- const routerEntry = await import("./router-DB7WR5Zr.mjs");
4750
+ const routerEntry = await import("./router-DjaryVgW.mjs");
4751
4751
  return {
4752
4752
  startEntry: await import("./start-DQK0r85G.mjs"),
4753
4753
  routerEntry
@@ -1,17 +1,17 @@
1
- //#region node_modules/.nitro/vite/services/ssr/assets/_tanstack-start-manifest_v-bE9hwtzg.js
1
+ //#region node_modules/.nitro/vite/services/ssr/assets/_tanstack-start-manifest_v-DlKQbuiB.js
2
2
  var tsrStartManifest = () => ({
3
3
  "routes": {
4
4
  "__root__": {
5
5
  "filePath": "/home/runner/work/hot-updater/hot-updater/packages/console/src/routes/__root.tsx",
6
6
  "children": ["/"],
7
- "preloads": ["/assets/main-hOHGHX7C.js"]
7
+ "preloads": ["/assets/main-l1zcdavI.js"]
8
8
  },
9
9
  "/": {
10
10
  "filePath": "/home/runner/work/hot-updater/hot-updater/packages/console/src/routes/index.tsx",
11
- "preloads": ["/assets/routes-B2UPnemG.js"]
11
+ "preloads": ["/assets/routes-BlHtyRHx.js"]
12
12
  }
13
13
  },
14
- "clientEntry": "/assets/main-hOHGHX7C.js"
14
+ "clientEntry": "/assets/main-l1zcdavI.js"
15
15
  });
16
16
  //#endregion
17
17
  export { tsrStartManifest };