@hot-updater/console 0.28.0 → 0.29.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 (93) hide show
  1. package/.output/nitro.json +17 -0
  2. package/.output/public/apple-touch-icon.png +0 -0
  3. package/.output/public/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  4. package/.output/public/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  5. package/.output/public/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  6. package/.output/public/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  7. package/.output/public/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  8. package/.output/public/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  9. package/.output/public/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  10. package/.output/public/assets/main-Dlx8-qN-.js +61 -0
  11. package/.output/public/assets/routes-DB0tWmiJ.js +10 -0
  12. package/.output/public/assets/styles-Bfxg4M1x.css +2 -0
  13. package/.output/public/favicon-16x16.png +0 -0
  14. package/.output/public/favicon-32x32.png +0 -0
  15. package/.output/public/favicon.ico +0 -0
  16. package/.output/public/logo.svg +1 -0
  17. package/.output/public/manifest.json +30 -0
  18. package/.output/public/robots.txt +3 -0
  19. package/.output/server/_chunks/ssr-renderer.mjs +15 -0
  20. package/.output/server/_libs/@floating-ui/core+[...].mjs +698 -0
  21. package/.output/server/_libs/@floating-ui/dom+[...].mjs +644 -0
  22. package/.output/server/_libs/@floating-ui/react-dom+[...].mjs +839 -0
  23. package/.output/server/_libs/@radix-ui/react-alert-dialog+[...].mjs +2093 -0
  24. package/.output/server/_libs/@radix-ui/react-popper+[...].mjs +287 -0
  25. package/.output/server/_libs/@radix-ui/react-select+[...].mjs +1003 -0
  26. package/.output/server/_libs/@tanstack/devtools-event-client+[...].mjs +196 -0
  27. package/.output/server/_libs/@tanstack/form-core+[...].mjs +2396 -0
  28. package/.output/server/_libs/@tanstack/react-form+[...].mjs +298 -0
  29. package/.output/server/_libs/@tanstack/react-router+[...].mjs +13068 -0
  30. package/.output/server/_libs/@tanstack/react-table+[...].mjs +2372 -0
  31. package/.output/server/_libs/chownr.mjs +60 -0
  32. package/.output/server/_libs/class-variance-authority+clsx.mjs +69 -0
  33. package/.output/server/_libs/core-util-is.mjs +67 -0
  34. package/.output/server/_libs/dayjs.mjs +408 -0
  35. package/.output/server/_libs/h3+rou3+srvx.mjs +1158 -0
  36. package/.output/server/_libs/hookable.mjs +41 -0
  37. package/.output/server/_libs/immediate.mjs +57 -0
  38. package/.output/server/_libs/inherits.mjs +39 -0
  39. package/.output/server/_libs/isaacs__fs-minipass+minipass.mjs +1120 -0
  40. package/.output/server/_libs/isarray.mjs +10 -0
  41. package/.output/server/_libs/jszip+[...].mjs +8311 -0
  42. package/.output/server/_libs/lucide-react.mjs +371 -0
  43. package/.output/server/_libs/minizlib.mjs +345 -0
  44. package/.output/server/_libs/next-themes.mjs +49 -0
  45. package/.output/server/_libs/radix-ui__number.mjs +6 -0
  46. package/.output/server/_libs/radix-ui__primitive.mjs +9 -0
  47. package/.output/server/_libs/radix-ui__react-arrow.mjs +23 -0
  48. package/.output/server/_libs/radix-ui__react-collection.mjs +78 -0
  49. package/.output/server/_libs/radix-ui__react-direction.mjs +11 -0
  50. package/.output/server/_libs/radix-ui__react-label.mjs +22 -0
  51. package/.output/server/_libs/radix-ui__react-separator.mjs +31 -0
  52. package/.output/server/_libs/radix-ui__react-slider.mjs +451 -0
  53. package/.output/server/_libs/radix-ui__react-switch.mjs +118 -0
  54. package/.output/server/_libs/radix-ui__react-tooltip.mjs +491 -0
  55. package/.output/server/_libs/semver.mjs +1339 -0
  56. package/.output/server/_libs/sonner.mjs +908 -0
  57. package/.output/server/_libs/tailwind-merge.mjs +1962 -0
  58. package/.output/server/_libs/tanstack__history.mjs +322 -0
  59. package/.output/server/_libs/tanstack__query-core.mjs +2073 -0
  60. package/.output/server/_libs/tanstack__react-query.mjs +146 -0
  61. package/.output/server/_libs/tanstack__router-core.mjs +6 -0
  62. package/.output/server/_libs/tar.mjs +1996 -0
  63. package/.output/server/_libs/ufo.mjs +64 -0
  64. package/.output/server/_runtime.mjs +26 -0
  65. package/.output/server/_ssr/api-rpc-D3ZehMIN.mjs +217 -0
  66. package/.output/server/_ssr/config.server-JUYQ7UbI.mjs +26 -0
  67. package/.output/server/_ssr/deleteBundle-DWUxu9-K.mjs +22 -0
  68. package/.output/server/_ssr/extract-timestamp-from-uuidv7-B90UBADU.mjs +24 -0
  69. package/.output/server/_ssr/promoteBundle-DtMHuubR.mjs +1571 -0
  70. package/.output/server/_ssr/router-pgc7NX76.mjs +250 -0
  71. package/.output/server/_ssr/routes-PqTTQSoI.mjs +1833 -0
  72. package/.output/server/_ssr/sidebar-DXng0IOP.mjs +439 -0
  73. package/.output/server/_ssr/ssr.mjs +5050 -0
  74. package/.output/server/_ssr/start-DQK0r85G.mjs +4 -0
  75. package/.output/server/_tanstack-start-manifest_v-DTbQVOpU.mjs +17 -0
  76. package/.output/server/index.mjs +417 -0
  77. package/.output/server/node_modules/tslib/modules/index.js +70 -0
  78. package/.output/server/node_modules/tslib/modules/package.json +3 -0
  79. package/.output/server/node_modules/tslib/package.json +47 -0
  80. package/.output/server/node_modules/tslib/tslib.js +484 -0
  81. package/.output/server/package.json +9 -0
  82. package/README.md +191 -2
  83. package/package.json +77 -50
  84. package/dist/.gitkeep +0 -0
  85. package/dist/assets/favicon-BkwcEHsj.ico +0 -0
  86. package/dist/assets/index-DUlKsori.css +0 -1
  87. package/dist/assets/index-ijmIcyn1.js +0 -27
  88. package/dist/assets/logo-BYNFyja1.png +0 -0
  89. package/dist/index.cjs +0 -2129
  90. package/dist/index.d.cts +0 -218
  91. package/dist/index.d.ts +0 -218
  92. package/dist/index.html +0 -14
  93. package/dist/index.js +0 -2125
@@ -0,0 +1,1833 @@
1
+ import { r as __toESM } from "../_runtime.mjs";
2
+ 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";
3
+ import { d as useNavigate, f as useSearch } from "../_libs/@tanstack/react-router+[...].mjs";
4
+ import { u as require_react } from "../_libs/@floating-ui/react-dom+[...].mjs";
5
+ import { n as useStore, t as useForm } from "../_libs/@tanstack/react-form+[...].mjs";
6
+ import { n as createServerFn, r as TSS_SERVER_FUNCTION, t as getServerFnById } from "./ssr.mjs";
7
+ import { n as extractTimestampFromUUIDv7, t as createUUIDv7 } from "./extract-timestamp-from-uuidv7-B90UBADU.mjs";
8
+ import { 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";
9
+ import { t as cva } from "../_libs/class-variance-authority+clsx.mjs";
10
+ 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";
11
+ import { t as Root$1 } from "../_libs/radix-ui__react-label.mjs";
12
+ import { i as Track, n as Root$2, r as Thumb, t as Range } from "../_libs/radix-ui__react-slider.mjs";
13
+ import { n as Thumb$1, t as Root$3 } from "../_libs/radix-ui__react-switch.mjs";
14
+ import { C as Tooltip$1, D as cn, E as TooltipTrigger, S as Skeleton, T as TooltipProvider, a as SheetContent, c as SheetTitle, i as Sheet, n as Input, o as SheetDescription, r as Separator$1, s as SheetHeader, t as Button, w as TooltipContent, x as SidebarTrigger } from "./sidebar-DXng0IOP.mjs";
15
+ import { i as useQueryClient, n as useQuery, t as useMutation } from "../_libs/tanstack__react-query.mjs";
16
+ import { n as toast } from "../_libs/sonner.mjs";
17
+ import { t as require_semver } from "../_libs/semver.mjs";
18
+ import { i as getCoreRowModel, n as useReactTable, r as createColumnHelper, t as flexRender } from "../_libs/@tanstack/react-table+[...].mjs";
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-PqTTQSoI.js
21
+ var import_jsx_runtime = require_jsx_runtime();
22
+ var import_react = /* @__PURE__ */ __toESM(require_react());
23
+ var import_semver = /* @__PURE__ */ __toESM(require_semver());
24
+ var import_dayjs_min = /* @__PURE__ */ __toESM(require_dayjs_min());
25
+ var import_relativeTime = /* @__PURE__ */ __toESM(require_relativeTime());
26
+ function AppleIcon({ className }) {
27
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
28
+ role: "img",
29
+ "aria-label": "iOS",
30
+ viewBox: "0 0 24 24",
31
+ xmlns: "http://www.w3.org/2000/svg",
32
+ className,
33
+ fill: "currentColor",
34
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701" })
35
+ });
36
+ }
37
+ function AndroidIcon({ className }) {
38
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
39
+ role: "img",
40
+ "aria-label": "Android",
41
+ viewBox: "0 0 24 24",
42
+ xmlns: "http://www.w3.org/2000/svg",
43
+ className,
44
+ fill: "currentColor",
45
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M18.4395 5.5586c-.675 1.1664-1.352 2.3318-2.0274 3.498-.0366-.0155-.0742-.0286-.1113-.043-1.8249-.6957-3.484-.8-4.42-.787-1.8551.0185-3.3544.4643-4.2597.8203-.084-.1494-1.7526-3.021-2.0215-3.4864a1.1451 1.1451 0 0 0-.1406-.1914c-.3312-.364-.9054-.4859-1.379-.203-.475.282-.7136.9361-.3886 1.5019 1.9466 3.3696-.0966-.2158 1.9473 3.3593.0172.031-.4946.2642-1.3926 1.0177C2.8987 12.176.452 14.772 0 18.9902h24c-.119-1.1108-.3686-2.099-.7461-3.0683-.7438-1.9118-1.8435-3.2928-2.7402-4.1836a12.1048 12.1048 0 0 0-2.1309-1.6875c.6594-1.122 1.312-2.2559 1.9649-3.3848.2077-.3615.1886-.7956-.0079-1.1191a1.1001 1.1001 0 0 0-.8515-.5332c-.5225-.0536-.9392.3128-1.0488.5449zm-.0391 8.461c.3944.5926.324 1.3306-.1563 1.6503-.4799.3197-1.188.0985-1.582-.4941-.3944-.5927-.324-1.3307.1563-1.6504.4727-.315 1.1812-.1086 1.582.4941zM7.207 13.5273c.4803.3197.5506 1.0577.1563 1.6504-.394.5926-1.1038.8138-1.584.4941-.48-.3197-.5503-1.0577-.1563-1.6504.4008-.6021 1.1087-.8106 1.584-.4941z" })
46
+ });
47
+ }
48
+ function PlatformIcon({ platform, className }) {
49
+ if (platform === "ios") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppleIcon, { className });
50
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AndroidIcon, { className });
51
+ }
52
+ function BundleBasicInfo({ bundle }) {
53
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
54
+ className: "flex flex-col gap-3 text-sm mt-1",
55
+ children: [
56
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
57
+ className: "flex items-center gap-2",
58
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
59
+ platform: bundle.platform,
60
+ className: "h-4 w-4"
61
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
62
+ className: "font-medium",
63
+ children: bundle.platform === "ios" ? "iOS" : "Android"
64
+ })]
65
+ }),
66
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
67
+ className: "flex items-center gap-2",
68
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
69
+ className: "font-medium text-muted-foreground",
70
+ children: "Bundle ID"
71
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
72
+ className: "text-xs text-foreground",
73
+ children: bundle.id
74
+ })]
75
+ }),
76
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
77
+ className: "flex items-center gap-2",
78
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
79
+ className: "font-medium text-muted-foreground",
80
+ children: "Channel"
81
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
82
+ className: "text-xs text-foreground",
83
+ children: bundle.channel
84
+ })]
85
+ })
86
+ ]
87
+ });
88
+ }
89
+ var NUMERIC_COHORT_SIZE = 1e3;
90
+ var DEFAULT_ROLLOUT_COHORT_COUNT = NUMERIC_COHORT_SIZE;
91
+ var INVALID_COHORT_ERROR_MESSAGE = `Invalid cohort. Use 1-1000 or a lowercase slug without spaces, up to 64 characters.`;
92
+ var CUSTOM_COHORT_PATTERN = /^[a-z0-9-]+$/;
93
+ function parseNumericCohortValue(cohort) {
94
+ if (!/^\d+$/.test(cohort)) return null;
95
+ const parsed = Number.parseInt(cohort, 10);
96
+ if (Number.isNaN(parsed) || parsed < 1 || parsed > 1e3) return null;
97
+ return parsed;
98
+ }
99
+ function positiveMod(value, modulus) {
100
+ return (value % modulus + modulus) % modulus;
101
+ }
102
+ function hashString(value) {
103
+ let hash = 0;
104
+ for (let i = 0; i < value.length; i++) {
105
+ const char = value.charCodeAt(i);
106
+ hash = (hash << 5) - hash + char;
107
+ hash |= 0;
108
+ }
109
+ return hash;
110
+ }
111
+ function gcd(a, b) {
112
+ let x = Math.abs(a);
113
+ let y = Math.abs(b);
114
+ while (y !== 0) {
115
+ const next = x % y;
116
+ x = y;
117
+ y = next;
118
+ }
119
+ return x;
120
+ }
121
+ function modularInverse(value, modulus) {
122
+ let t = 0;
123
+ let newT = 1;
124
+ let r = modulus;
125
+ let newR = positiveMod(value, modulus);
126
+ while (newR !== 0) {
127
+ const quotient = Math.floor(r / newR);
128
+ [t, newT] = [newT, t - quotient * newT];
129
+ [r, newR] = [newR, r - quotient * newR];
130
+ }
131
+ if (r > 1) throw new Error(`No modular inverse for ${value} mod ${modulus}`);
132
+ return positiveMod(t, modulus);
133
+ }
134
+ function getRolloutShuffleParameters(bundleId) {
135
+ let multiplier = positiveMod(hashString(`${bundleId}:multiplier`), 997);
136
+ if (multiplier === 0) multiplier = 1;
137
+ while (gcd(multiplier, NUMERIC_COHORT_SIZE) !== 1) {
138
+ multiplier = positiveMod(multiplier + 1, NUMERIC_COHORT_SIZE);
139
+ if (multiplier === 0) multiplier = 1;
140
+ }
141
+ const offset = positiveMod(hashString(`${bundleId}:offset`), NUMERIC_COHORT_SIZE);
142
+ return {
143
+ multiplier,
144
+ offset,
145
+ inverseMultiplier: modularInverse(multiplier, NUMERIC_COHORT_SIZE)
146
+ };
147
+ }
148
+ function normalizeRolloutCohortCount(rolloutCohortCount) {
149
+ if (rolloutCohortCount === null || rolloutCohortCount === void 0) return DEFAULT_ROLLOUT_COHORT_COUNT;
150
+ if (rolloutCohortCount <= 0) return 0;
151
+ if (rolloutCohortCount >= 1e3) return NUMERIC_COHORT_SIZE;
152
+ return Math.floor(rolloutCohortCount);
153
+ }
154
+ function normalizeCohortValue(cohort) {
155
+ const normalized = cohort.trim().toLowerCase();
156
+ const numericCohort = parseNumericCohortValue(normalized);
157
+ if (numericCohort !== null) return String(numericCohort);
158
+ return normalized;
159
+ }
160
+ function getNumericCohortValue(cohort) {
161
+ return parseNumericCohortValue(normalizeCohortValue(cohort));
162
+ }
163
+ function isNumericCohort(cohort) {
164
+ return getNumericCohortValue(cohort) !== null;
165
+ }
166
+ function isCustomCohort(cohort) {
167
+ const normalized = normalizeCohortValue(cohort);
168
+ return normalized.length > 0 && normalized.length <= 64 && !/^\d+$/.test(normalized) && CUSTOM_COHORT_PATTERN.test(normalized);
169
+ }
170
+ function isValidCohort(cohort) {
171
+ const normalized = normalizeCohortValue(cohort);
172
+ return isNumericCohort(normalized) || isCustomCohort(normalized);
173
+ }
174
+ function getNumericCohortRolloutPosition(bundleId, cohortValue) {
175
+ if (cohortValue < 1 || cohortValue > 1e3) throw new Error(`Invalid numeric cohort: ${cohortValue}`);
176
+ const { offset, inverseMultiplier } = getRolloutShuffleParameters(bundleId);
177
+ return positiveMod(inverseMultiplier * (cohortValue - 1 - offset), NUMERIC_COHORT_SIZE);
178
+ }
179
+ var badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", {
180
+ variants: { variant: {
181
+ default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
182
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
183
+ destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
184
+ outline: "text-foreground"
185
+ } },
186
+ defaultVariants: { variant: "default" }
187
+ });
188
+ function Badge({ className, variant = "default", asChild = false, ...props }) {
189
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(asChild ? Slot : "span", {
190
+ "data-slot": "badge",
191
+ "data-variant": variant,
192
+ className: cn(badgeVariants({ variant }), className),
193
+ ...props
194
+ });
195
+ }
196
+ function Label$1({ className, ...props }) {
197
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Root$1, {
198
+ "data-slot": "label",
199
+ className: cn("gap-2 text-xs/relaxed leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed", className),
200
+ ...props
201
+ });
202
+ }
203
+ function Slider$1({ className, defaultValue, value, min = 0, max = 100, ...props }) {
204
+ const _values = import_react.useMemo(() => Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min, max], [
205
+ value,
206
+ defaultValue,
207
+ min,
208
+ max
209
+ ]);
210
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Root$2, {
211
+ "data-slot": "slider",
212
+ defaultValue,
213
+ value,
214
+ min,
215
+ max,
216
+ className: cn("relative flex w-full touch-none select-none items-center", className),
217
+ ...props,
218
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Track, {
219
+ "data-slot": "slider-track",
220
+ className: "relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20",
221
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Range, {
222
+ "data-slot": "slider-range",
223
+ className: "absolute h-full bg-primary"
224
+ })
225
+ }), Array.from({ length: _values.length }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Thumb, {
226
+ "data-slot": "slider-thumb",
227
+ 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"
228
+ }, index))]
229
+ });
230
+ }
231
+ function Switch$1({ className, ...props }) {
232
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Root$3, {
233
+ "data-slot": "switch",
234
+ 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),
235
+ ...props,
236
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Thumb$1, {
237
+ "data-slot": "switch-thumb",
238
+ 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"
239
+ })
240
+ });
241
+ }
242
+ function Textarea({ className, ...props }) {
243
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", {
244
+ "data-slot": "textarea",
245
+ className: cn("flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", className),
246
+ ...props
247
+ });
248
+ }
249
+ var createSsrRpc = (functionId, importer) => {
250
+ const url = "/_serverFn/" + functionId;
251
+ const serverFnMeta = { id: functionId };
252
+ const fn = async (...args) => {
253
+ return (importer ? await importer() : await getServerFnById(functionId))(...args);
254
+ };
255
+ return Object.assign(fn, {
256
+ url,
257
+ serverFnMeta,
258
+ [TSS_SERVER_FUNCTION]: true
259
+ });
260
+ };
261
+ var getConfig = createServerFn().handler(createSsrRpc("51a34c05479a893c7d320bd4cd1604427289d667698c48b9f30a01aabf8a5e68"));
262
+ var getChannels = createServerFn().handler(createSsrRpc("79ada05964de8f2123bfcd62b10097d59bf8fd689ef5c7161031e5871d8396c5"));
263
+ createServerFn().handler(createSsrRpc("f8bdca3d0579adb812d6404f55d9261a89cdb025e920b1fcad82883646a5fe9e"));
264
+ var getBundles = createServerFn({ method: "GET" }).inputValidator((input) => input).handler(createSsrRpc("00ccacb4a0212c83ec29f4d11719046ad91ea8291cfc557d514dbf00d3bd7f5f"));
265
+ var getBundle = createServerFn({ method: "GET" }).inputValidator((input) => input).handler(createSsrRpc("1bd85c2a50e24785cb6abb023a247a9f048f37ddfa85cbb1c57e579563bad013"));
266
+ var getBundleDownloadUrl = createServerFn({ method: "GET" }).inputValidator((input) => input).handler(createSsrRpc("e000081a14772a0496dfdf615232fbbb1a23b89191d0e32f89a05dd67a1916f7"));
267
+ var updateBundle = createServerFn({ method: "POST" }).inputValidator((input) => input).handler(createSsrRpc("67f90ce4cd10fd0226cd9d77cdcd8d0f25a59a6ac406360b655e44296bc4208b"));
268
+ var promoteBundle = createServerFn({ method: "POST" }).inputValidator((input) => input).handler(createSsrRpc("c4ef3bbb77ea8a4410623fd18a4741e2ff0668b1a97510bcdd032a8428eaa5da"));
269
+ createServerFn({ method: "POST" }).inputValidator((input) => input).handler(createSsrRpc("16cc7c2f080ea5b73e0c6bba815b110dbd7727796036d248ba0d8ae819ddef08"));
270
+ var deleteBundle = createServerFn({ method: "POST" }).inputValidator((input) => input).handler(createSsrRpc("3a27ff5679228b86a346b3fe42142e7a5bf0264f73173c0dbe5f07fb37c43cf7"));
271
+ var bundleListQueryKey = ["bundles"];
272
+ var queryKeys = {
273
+ config: ["config"],
274
+ channels: ["channels"],
275
+ configLoaded: ["config-loaded"],
276
+ bundles: {
277
+ all: bundleListQueryKey,
278
+ list: (filters) => [...bundleListQueryKey, filters ?? {}]
279
+ },
280
+ bundle: (bundleId) => ["bundle", bundleId]
281
+ };
282
+ function replaceBundleInQueryData(data, updatedBundle) {
283
+ if (!data) return data;
284
+ return {
285
+ ...data,
286
+ data: data.data.map((bundle) => bundle.id === updatedBundle.id ? updatedBundle : bundle)
287
+ };
288
+ }
289
+ function useConfigQuery() {
290
+ return useQuery({
291
+ queryKey: queryKeys.config,
292
+ queryFn: () => getConfig(),
293
+ staleTime: Infinity
294
+ });
295
+ }
296
+ function useChannelsQuery() {
297
+ return useQuery({
298
+ queryKey: queryKeys.channels,
299
+ queryFn: () => getChannels(),
300
+ staleTime: Infinity
301
+ });
302
+ }
303
+ function useBundlesQuery(filters) {
304
+ return useQuery({
305
+ queryKey: queryKeys.bundles.list(filters),
306
+ queryFn: () => getBundles({ data: filters }),
307
+ staleTime: Infinity,
308
+ placeholderData: (previousData) => previousData
309
+ });
310
+ }
311
+ function useBundleQuery(bundleId) {
312
+ return useQuery({
313
+ queryKey: queryKeys.bundle(bundleId),
314
+ queryFn: () => getBundle({ data: { bundleId } }),
315
+ staleTime: Infinity,
316
+ enabled: !!bundleId
317
+ });
318
+ }
319
+ function useBundleDownloadUrlMutation() {
320
+ return useMutation({ mutationFn: (params) => getBundleDownloadUrl({ data: params }) });
321
+ }
322
+ function useUpdateBundleMutation() {
323
+ const queryClient = useQueryClient();
324
+ return useMutation({
325
+ mutationFn: (params) => updateBundle({ data: params }),
326
+ onSuccess: async ({ bundle: updatedBundle }, vars) => {
327
+ queryClient.setQueryData(queryKeys.bundle(vars.bundleId), updatedBundle);
328
+ queryClient.setQueriesData({ queryKey: queryKeys.bundles.all }, (data) => replaceBundleInQueryData(data, updatedBundle));
329
+ await Promise.all([
330
+ queryClient.invalidateQueries({ queryKey: queryKeys.bundles.all }),
331
+ queryClient.invalidateQueries({ queryKey: queryKeys.bundle(vars.bundleId) }),
332
+ queryClient.invalidateQueries({ queryKey: queryKeys.channels })
333
+ ]);
334
+ }
335
+ });
336
+ }
337
+ function usePromoteBundleMutation() {
338
+ const queryClient = useQueryClient();
339
+ return useMutation({
340
+ mutationFn: (params) => promoteBundle({ data: params }),
341
+ onSuccess: async ({ bundle }) => {
342
+ queryClient.setQueryData(queryKeys.bundle(bundle.id), bundle);
343
+ await Promise.all([
344
+ queryClient.invalidateQueries({ queryKey: queryKeys.bundles.all }),
345
+ queryClient.invalidateQueries({ queryKey: queryKeys.channels }),
346
+ queryClient.invalidateQueries({ queryKey: queryKeys.bundle(bundle.id) })
347
+ ]);
348
+ }
349
+ });
350
+ }
351
+ function useDeleteBundleMutation() {
352
+ const queryClient = useQueryClient();
353
+ return useMutation({
354
+ mutationFn: (params) => deleteBundle({ data: params }),
355
+ onSuccess: async (_, vars) => {
356
+ queryClient.removeQueries({ queryKey: queryKeys.bundle(vars.bundleId) });
357
+ await Promise.all([queryClient.invalidateQueries({ queryKey: queryKeys.bundles.all }), queryClient.invalidateQueries({ queryKey: queryKeys.channels })]);
358
+ }
359
+ });
360
+ }
361
+ function AlertDialog$1({ ...props }) {
362
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Root2, {
363
+ "data-slot": "alert-dialog",
364
+ ...props
365
+ });
366
+ }
367
+ function AlertDialogPortal({ ...props }) {
368
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Portal2, {
369
+ "data-slot": "alert-dialog-portal",
370
+ ...props
371
+ });
372
+ }
373
+ function AlertDialogOverlay({ className, ...props }) {
374
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Overlay2, {
375
+ "data-slot": "alert-dialog-overlay",
376
+ className: cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/80 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 z-50", className),
377
+ ...props
378
+ });
379
+ }
380
+ function AlertDialogContent({ className, size = "default", ...props }) {
381
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDialogPortal, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogOverlay, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Content2, {
382
+ "data-slot": "alert-dialog-content",
383
+ "data-size": size,
384
+ className: cn("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 bg-background ring-foreground/10 gap-3 rounded-xl p-4 ring-1 duration-100 data-[size=default]:max-w-xs data-[size=sm]:max-w-64 data-[size=default]:sm:max-w-sm group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 outline-none", className),
385
+ ...props
386
+ })] });
387
+ }
388
+ function AlertDialogHeader({ className, ...props }) {
389
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
390
+ "data-slot": "alert-dialog-header",
391
+ className: cn("grid grid-rows-[auto_1fr] place-items-center gap-1 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]", className),
392
+ ...props
393
+ });
394
+ }
395
+ function AlertDialogFooter({ className, ...props }) {
396
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
397
+ "data-slot": "alert-dialog-footer",
398
+ className: cn("flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end", className),
399
+ ...props
400
+ });
401
+ }
402
+ function AlertDialogTitle({ className, ...props }) {
403
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Title2, {
404
+ "data-slot": "alert-dialog-title",
405
+ className: cn("text-sm font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2", className),
406
+ ...props
407
+ });
408
+ }
409
+ function AlertDialogDescription({ className, ...props }) {
410
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Description2, {
411
+ "data-slot": "alert-dialog-description",
412
+ className: cn("text-muted-foreground *:[a]:hover:text-foreground text-xs/relaxed text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3", className),
413
+ ...props
414
+ });
415
+ }
416
+ function AlertDialogAction({ className, variant = "default", size = "default", ...props }) {
417
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
418
+ variant,
419
+ size,
420
+ asChild: true,
421
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Action, {
422
+ "data-slot": "alert-dialog-action",
423
+ className: cn(className),
424
+ ...props
425
+ })
426
+ });
427
+ }
428
+ function AlertDialogCancel({ className, variant = "outline", size = "default", ...props }) {
429
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
430
+ variant,
431
+ size,
432
+ asChild: true,
433
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cancel, {
434
+ "data-slot": "alert-dialog-cancel",
435
+ className: cn(className),
436
+ ...props
437
+ })
438
+ });
439
+ }
440
+ function DeleteBundleDialog({ bundle, open, onOpenChange, onSuccess }) {
441
+ const deleteBundleMutation = useDeleteBundleMutation();
442
+ const handleDelete = async () => {
443
+ try {
444
+ await deleteBundleMutation.mutateAsync({ bundleId: bundle.id });
445
+ toast.success("Bundle deleted successfully");
446
+ onOpenChange(false);
447
+ onSuccess();
448
+ } catch (error) {
449
+ toast.error("Failed to delete bundle");
450
+ console.error(error);
451
+ }
452
+ };
453
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialog$1, {
454
+ 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
+ ] })
486
+ });
487
+ }
488
+ function Dialog$1({ ...props }) {
489
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Root, {
490
+ "data-slot": "dialog",
491
+ ...props
492
+ });
493
+ }
494
+ function DialogTrigger({ ...props }) {
495
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Trigger, {
496
+ "data-slot": "dialog-trigger",
497
+ ...props
498
+ });
499
+ }
500
+ function DialogPortal({ ...props }) {
501
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Portal, {
502
+ "data-slot": "dialog-portal",
503
+ ...props
504
+ });
505
+ }
506
+ function DialogOverlay({ className, ...props }) {
507
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Overlay, {
508
+ "data-slot": "dialog-overlay",
509
+ className: cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/80 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50", className),
510
+ ...props
511
+ });
512
+ }
513
+ function DialogContent({ className, children, showCloseButton = true, ...props }) {
514
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogPortal, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogOverlay, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Content, {
515
+ "data-slot": "dialog-content",
516
+ className: cn("bg-background 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 ring-foreground/10 grid max-w-[calc(100%-2rem)] gap-4 rounded-xl p-4 text-xs/relaxed ring-1 duration-100 sm:max-w-sm fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2", className),
517
+ ...props,
518
+ children: [children, showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Close, {
519
+ "data-slot": "dialog-close",
520
+ asChild: true,
521
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
522
+ variant: "ghost",
523
+ className: "absolute top-2 right-2",
524
+ size: "icon-sm",
525
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
526
+ className: "sr-only",
527
+ children: "Close"
528
+ })]
529
+ })
530
+ })]
531
+ })] });
532
+ }
533
+ function DialogHeader({ className, ...props }) {
534
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
535
+ "data-slot": "dialog-header",
536
+ className: cn("gap-1 flex flex-col", className),
537
+ ...props
538
+ });
539
+ }
540
+ function DialogFooter({ className, showCloseButton = false, children, ...props }) {
541
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
542
+ "data-slot": "dialog-footer",
543
+ className: cn("gap-2 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className),
544
+ ...props,
545
+ children: [children, showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Close, {
546
+ asChild: true,
547
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
548
+ variant: "outline",
549
+ children: "Close"
550
+ })
551
+ })]
552
+ });
553
+ }
554
+ function DialogTitle({ className, ...props }) {
555
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Title, {
556
+ "data-slot": "dialog-title",
557
+ className: cn("text-sm font-medium", className),
558
+ ...props
559
+ });
560
+ }
561
+ function DialogDescription({ className, ...props }) {
562
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Description, {
563
+ "data-slot": "dialog-description",
564
+ className: cn("text-muted-foreground *:[a]:hover:text-foreground text-xs/relaxed *:[a]:underline *:[a]:underline-offset-3", className),
565
+ ...props
566
+ });
567
+ }
568
+ function Select$1({ ...props }) {
569
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Root2$1, {
570
+ "data-slot": "select",
571
+ ...props
572
+ });
573
+ }
574
+ function SelectValue({ ...props }) {
575
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Value, {
576
+ "data-slot": "select-value",
577
+ ...props
578
+ });
579
+ }
580
+ function SelectTrigger({ className, children, ...props }) {
581
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Trigger$1, {
582
+ "data-slot": "select-trigger",
583
+ 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),
584
+ ...props,
585
+ children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, {
586
+ asChild: true,
587
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: "h-4 w-4 opacity-50" })
588
+ })]
589
+ });
590
+ }
591
+ function SelectContent({ className, children, position = "item-aligned", align = "center", ...props }) {
592
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Portal$1, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Content2$1, {
593
+ "data-slot": "select-content",
594
+ 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),
595
+ position,
596
+ align,
597
+ ...props,
598
+ children: [
599
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollUpButton, {}),
600
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Viewport, {
601
+ "data-position": position,
602
+ 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" && ""),
603
+ children
604
+ }),
605
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectScrollDownButton, {})
606
+ ]
607
+ }) });
608
+ }
609
+ function SelectItem({ className, children, ...props }) {
610
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Item, {
611
+ "data-slot": "select-item",
612
+ 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),
613
+ ...props,
614
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
615
+ className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center",
616
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "h-4 w-4" }) })
617
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ItemText, { children })]
618
+ });
619
+ }
620
+ function SelectScrollUpButton({ className, ...props }) {
621
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollUpButton, {
622
+ "data-slot": "select-scroll-up-button",
623
+ className: cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-3.5", className),
624
+ ...props,
625
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronUp, {})
626
+ });
627
+ }
628
+ function SelectScrollDownButton({ className, ...props }) {
629
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollDownButton, {
630
+ "data-slot": "select-scroll-down-button",
631
+ className: cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-3.5", className),
632
+ ...props,
633
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, {})
634
+ });
635
+ }
636
+ function useFilterParams() {
637
+ const search = useSearch({ from: "/" });
638
+ const navigate = useNavigate();
639
+ const filters = {
640
+ channel: search.channel,
641
+ platform: search.platform,
642
+ offset: search.offset
643
+ };
644
+ const bundleId = search.bundleId;
645
+ const navigateWithSearch = (nextSearch) => {
646
+ navigate({
647
+ to: "/",
648
+ search: nextSearch
649
+ });
650
+ };
651
+ const getNextFilters = (newFilters) => {
652
+ const hasChannel = Object.hasOwn(newFilters, "channel");
653
+ const hasPlatform = Object.hasOwn(newFilters, "platform");
654
+ const hasOffset = Object.hasOwn(newFilters, "offset");
655
+ return {
656
+ channel: hasChannel ? newFilters.channel : filters.channel,
657
+ platform: hasPlatform ? newFilters.platform : filters.platform,
658
+ offset: hasChannel || hasPlatform ? "0" : hasOffset ? newFilters.offset : filters.offset
659
+ };
660
+ };
661
+ const setFilters = (newFilters) => {
662
+ navigateWithSearch({
663
+ ...getNextFilters(newFilters),
664
+ bundleId: void 0
665
+ });
666
+ };
667
+ const setBundleId = (nextBundleId, newFilters = {}) => {
668
+ navigateWithSearch({
669
+ ...getNextFilters(newFilters),
670
+ bundleId: nextBundleId
671
+ });
672
+ };
673
+ const resetFilters = () => {
674
+ navigateWithSearch({
675
+ channel: void 0,
676
+ platform: void 0,
677
+ offset: void 0,
678
+ bundleId: void 0
679
+ });
680
+ };
681
+ return {
682
+ filters,
683
+ bundleId,
684
+ setFilters,
685
+ setBundleId,
686
+ resetFilters
687
+ };
688
+ }
689
+ function PromoteChannelDialog({ bundle, open, onOpenChange, onSuccess }) {
690
+ const [targetChannel, setTargetChannel] = (0, import_react.useState)("");
691
+ const [action, setAction] = (0, import_react.useState)("move");
692
+ const [copyBundleId, setCopyBundleId] = (0, import_react.useState)("");
693
+ const { setBundleId } = useFilterParams();
694
+ const { data: channels = [] } = useChannelsQuery();
695
+ const promoteBundleMutation = usePromoteBundleMutation();
696
+ const availableChannels = channels.filter((c) => c !== bundle.channel);
697
+ const isCopy = action === "copy";
698
+ const normalizedTargetChannel = targetChannel.trim();
699
+ const isSameChannel = normalizedTargetChannel === bundle.channel;
700
+ const displayedCopyBundleId = copyBundleId || "Generating bundle ID...";
701
+ const handleOpenChange = (nextOpen) => {
702
+ onOpenChange(nextOpen);
703
+ if (!nextOpen) {
704
+ setTargetChannel("");
705
+ setAction("move");
706
+ setCopyBundleId("");
707
+ }
708
+ };
709
+ const handleActionChange = (value) => {
710
+ const nextAction = value;
711
+ setAction(nextAction);
712
+ if (nextAction === "copy") setCopyBundleId((current) => current || createUUIDv7());
713
+ };
714
+ const openBundleDetail = (nextBundleId, nextChannel) => {
715
+ setBundleId(nextBundleId, {
716
+ channel: nextChannel,
717
+ offset: "0"
718
+ });
719
+ };
720
+ const handlePromote = async () => {
721
+ if (!normalizedTargetChannel) {
722
+ toast.error("Please select a target channel");
723
+ return;
724
+ }
725
+ if (isSameChannel) {
726
+ toast.error("Target channel must be different from the current channel");
727
+ return;
728
+ }
729
+ try {
730
+ const nextBundleId = isCopy ? copyBundleId || createUUIDv7() : void 0;
731
+ const { bundle: promotedBundle } = await promoteBundleMutation.mutateAsync({
732
+ action,
733
+ bundleId: bundle.id,
734
+ nextBundleId,
735
+ targetChannel: normalizedTargetChannel
736
+ });
737
+ const promotedBundleId = promotedBundle.id;
738
+ handleOpenChange(false);
739
+ onSuccess?.();
740
+ toast.success(isCopy ? `Bundle copied to ${normalizedTargetChannel}` : `Bundle moved to ${normalizedTargetChannel}`, {
741
+ description: `bundleId: ${promotedBundleId}`,
742
+ action: {
743
+ label: "Show Detail",
744
+ onClick: () => openBundleDetail(promotedBundleId, normalizedTargetChannel)
745
+ }
746
+ });
747
+ } catch (error) {
748
+ toast.error(error instanceof Error ? error.message : "Failed to promote bundle");
749
+ console.error(error);
750
+ }
751
+ };
752
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog$1, {
753
+ open,
754
+ onOpenChange: handleOpenChange,
755
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent, { children: [
756
+ /* @__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." })] }),
757
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
758
+ className: "space-y-4 py-4",
759
+ children: [
760
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
761
+ className: "space-y-2",
762
+ children: [
763
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, {
764
+ htmlFor: "promote-action",
765
+ children: "Action"
766
+ }),
767
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
768
+ value: action,
769
+ onValueChange: handleActionChange,
770
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
771
+ id: "promote-action",
772
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { placeholder: "Select an action" })
773
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
774
+ value: "move",
775
+ children: "Move bundle"
776
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
777
+ value: "copy",
778
+ children: "Copy bundle"
779
+ })] })]
780
+ }),
781
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
782
+ className: "text-xs text-muted-foreground",
783
+ 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."
784
+ })
785
+ ]
786
+ }),
787
+ isCopy && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
788
+ className: "space-y-2",
789
+ children: [
790
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, {
791
+ htmlFor: "copy-bundle-id",
792
+ children: "New Bundle ID"
793
+ }),
794
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
795
+ id: "copy-bundle-id",
796
+ value: displayedCopyBundleId,
797
+ readOnly: true
798
+ }),
799
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
800
+ className: "font-mono text-xs text-muted-foreground",
801
+ children: displayedCopyBundleId
802
+ })
803
+ ]
804
+ }),
805
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
806
+ className: "space-y-2",
807
+ children: [
808
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, {
809
+ htmlFor: "target-channel",
810
+ children: "Target Channel"
811
+ }),
812
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
813
+ id: "target-channel",
814
+ value: targetChannel,
815
+ onChange: (event) => setTargetChannel(event.target.value),
816
+ placeholder: "Enter a channel name",
817
+ list: "available-channels",
818
+ "aria-invalid": isSameChannel
819
+ }),
820
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("datalist", {
821
+ id: "available-channels",
822
+ children: availableChannels.map((channel) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: channel }, channel))
823
+ }),
824
+ availableChannels.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
825
+ className: "flex flex-wrap gap-2",
826
+ children: availableChannels.map((channel) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
827
+ type: "button",
828
+ variant: "outline",
829
+ size: "xs",
830
+ onClick: () => setTargetChannel(channel),
831
+ children: channel
832
+ }, channel))
833
+ }),
834
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
835
+ className: "text-xs text-muted-foreground",
836
+ children: "Choose an existing channel or enter a new one."
837
+ }),
838
+ isSameChannel && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
839
+ className: "text-xs text-destructive",
840
+ role: "alert",
841
+ children: "Target channel must be different from the current channel."
842
+ })
843
+ ]
844
+ })
845
+ ]
846
+ }),
847
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogFooter, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
848
+ variant: "outline",
849
+ onClick: () => handleOpenChange(false),
850
+ children: "Cancel"
851
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
852
+ onClick: handlePromote,
853
+ disabled: !normalizedTargetChannel || isCopy && !copyBundleId || isSameChannel || promoteBundleMutation.isPending,
854
+ children: isCopy ? "Copy" : "Move"
855
+ })] })
856
+ ] })
857
+ });
858
+ }
859
+ function Card({ className, ...props }) {
860
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
861
+ "data-slot": "card",
862
+ className: cn("rounded-xl border bg-card text-card-foreground shadow", className),
863
+ ...props
864
+ });
865
+ }
866
+ function CardHeader({ className, ...props }) {
867
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
868
+ "data-slot": "card-header",
869
+ className: cn("flex flex-col space-y-1.5 p-6", className),
870
+ ...props
871
+ });
872
+ }
873
+ function CardTitle({ className, ...props }) {
874
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
875
+ "data-slot": "card-title",
876
+ className: cn("font-semibold leading-none tracking-tight", className),
877
+ ...props
878
+ });
879
+ }
880
+ function CardDescription({ className, ...props }) {
881
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
882
+ "data-slot": "card-description",
883
+ className: cn("text-sm text-muted-foreground", className),
884
+ ...props
885
+ });
886
+ }
887
+ function CardContent({ className, ...props }) {
888
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
889
+ "data-slot": "card-content",
890
+ className: cn("p-6 pt-0", className),
891
+ ...props
892
+ });
893
+ }
894
+ function RolloutCohortsDialog({ bundleId, rolloutCohortCount, targetCohorts, triggerLabel = "View Cohorts", triggerVariant = "outline", triggerSize = "sm", triggerClassName }) {
895
+ const normalizedRolloutCount = normalizeRolloutCohortCount(rolloutCohortCount);
896
+ const hasTargetCohortOverride = (targetCohorts?.length ?? 0) > 0;
897
+ if (!(normalizedRolloutCount > 0 && normalizedRolloutCount < 1e3 && !hasTargetCohortOverride)) return null;
898
+ const rolloutCohorts = Array.from({ length: NUMERIC_COHORT_SIZE }, (_, index) => index + 1).filter((cohortValue) => getNumericCohortRolloutPosition(bundleId, cohortValue) < normalizedRolloutCount);
899
+ const rolloutPercentage = (normalizedRolloutCount / 10).toFixed(1);
900
+ const excludedCount = NUMERIC_COHORT_SIZE - rolloutCohorts.length;
901
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Dialog$1, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTrigger, {
902
+ asChild: true,
903
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
904
+ type: "button",
905
+ variant: triggerVariant,
906
+ size: triggerSize,
907
+ className: triggerClassName,
908
+ onClick: (event) => event.stopPropagation(),
909
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(List, { className: "h-3.5 w-3.5" }), triggerLabel]
910
+ })
911
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent, {
912
+ className: "sm:max-w-3xl",
913
+ children: [
914
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTitle, { children: "Rolled Out Cohorts" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogDescription, { children: [
915
+ rolloutPercentage,
916
+ "% rollout currently targets",
917
+ " ",
918
+ rolloutCohorts.length,
919
+ " of ",
920
+ NUMERIC_COHORT_SIZE,
921
+ " numeric cohorts. The selected set stays stable for this bundle as you expand or shrink rollout."
922
+ ] })] }),
923
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
924
+ className: "grid gap-3 sm:grid-cols-3",
925
+ children: [
926
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
927
+ className: "p-4",
928
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardDescription, { children: "Selected Cohorts" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, {
929
+ className: "font-mono text-xl",
930
+ children: rolloutCohorts.length
931
+ })]
932
+ }) }),
933
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
934
+ className: "p-4",
935
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardDescription, { children: "Excluded Cohorts" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, {
936
+ className: "font-mono text-xl",
937
+ children: excludedCount
938
+ })]
939
+ }) }),
940
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
941
+ className: "p-4",
942
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardDescription, { children: "Bundle ID" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, {
943
+ className: "font-mono text-xs break-all leading-relaxed",
944
+ children: bundleId
945
+ })]
946
+ }) })
947
+ ]
948
+ }),
949
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
950
+ className: "p-4 pb-3",
951
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, {
952
+ className: "text-sm",
953
+ children: "Numeric Cohorts"
954
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardDescription, { children: "Listed in ascending order for readability." })]
955
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardContent, {
956
+ className: "p-4 pt-0",
957
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
958
+ className: "max-h-[50vh] overflow-y-auto rounded-lg border bg-muted/20 p-3",
959
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
960
+ className: "grid grid-cols-4 gap-2 sm:grid-cols-6 lg:grid-cols-8",
961
+ children: rolloutCohorts.map((cohortValue) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
962
+ variant: "outline",
963
+ className: "justify-center font-mono",
964
+ children: cohortValue
965
+ }, cohortValue))
966
+ })
967
+ })
968
+ })] }),
969
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogFooter, { showCloseButton: true })
970
+ ]
971
+ })] });
972
+ }
973
+ function getTargetAppVersionValidation(value) {
974
+ const normalizedValue = value.trim();
975
+ if (normalizedValue.length === 0) return {
976
+ error: "Invalid target app version",
977
+ normalizedRange: null
978
+ };
979
+ const normalizedRange = import_semver.default.validRange(normalizedValue);
980
+ if (!normalizedRange) return {
981
+ error: "Invalid target app version",
982
+ normalizedRange: null
983
+ };
984
+ return {
985
+ error: void 0,
986
+ normalizedRange
987
+ };
988
+ }
989
+ function getDefaultValues(bundle) {
990
+ return {
991
+ message: bundle.message || "",
992
+ targetAppVersion: bundle.targetAppVersion || "",
993
+ enabled: bundle.enabled,
994
+ shouldForceUpdate: bundle.shouldForceUpdate,
995
+ rolloutCohortCount: bundle.rolloutCohortCount ?? 1e3,
996
+ targetCohorts: bundle.targetCohorts ?? []
997
+ };
998
+ }
999
+ var formatRolloutPercentage = (rolloutCohortCount) => (rolloutCohortCount / 10).toFixed(1);
1000
+ function BundleEditorForm({ bundle, onClose }) {
1001
+ const bundleDownloadUrlMutation = useBundleDownloadUrlMutation();
1002
+ const updateBundleMutation = useUpdateBundleMutation();
1003
+ const [showPromoteDialog, setShowPromoteDialog] = (0, import_react.useState)(false);
1004
+ const [showDeleteDialog, setShowDeleteDialog] = (0, import_react.useState)(false);
1005
+ const [newCohort, setNewCohort] = (0, import_react.useState)("");
1006
+ const shouldEditTargetAppVersion = Boolean(bundle.targetAppVersion);
1007
+ const form = useForm({
1008
+ defaultValues: getDefaultValues(bundle),
1009
+ onSubmit: async ({ value }) => {
1010
+ const targetAppVersion = value.targetAppVersion.trim();
1011
+ const { error } = shouldEditTargetAppVersion ? getTargetAppVersionValidation(targetAppVersion) : { error: void 0 };
1012
+ if (error) {
1013
+ toast.error(error);
1014
+ return;
1015
+ }
1016
+ const normalizedTargetCohorts = Array.from(new Set(value.targetCohorts.map((cohort) => normalizeCohortValue(cohort))));
1017
+ if (normalizedTargetCohorts.find((cohort) => !isValidCohort(cohort))) {
1018
+ toast.error(INVALID_COHORT_ERROR_MESSAGE);
1019
+ return;
1020
+ }
1021
+ try {
1022
+ await updateBundleMutation.mutateAsync({
1023
+ bundleId: bundle.id,
1024
+ bundle: {
1025
+ message: value.message,
1026
+ targetAppVersion: targetAppVersion || void 0,
1027
+ enabled: value.enabled,
1028
+ shouldForceUpdate: value.shouldForceUpdate,
1029
+ rolloutCohortCount: value.rolloutCohortCount,
1030
+ targetCohorts: normalizedTargetCohorts.length > 0 ? normalizedTargetCohorts : null
1031
+ }
1032
+ });
1033
+ toast.success("Bundle updated successfully");
1034
+ onClose();
1035
+ } catch (error) {
1036
+ toast.error("Failed to update bundle");
1037
+ console.error(error);
1038
+ }
1039
+ }
1040
+ });
1041
+ const hasChanges = useStore(form.store, (state) => !state.isDefaultValue);
1042
+ const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
1043
+ const targetAppVersion = useStore(form.store, (state) => state.values.targetAppVersion);
1044
+ const targetCohorts = useStore(form.store, (state) => state.values.targetCohorts);
1045
+ const isSaving = isSubmitting || updateBundleMutation.isPending;
1046
+ const targetAppVersionValidation = shouldEditTargetAppVersion ? getTargetAppVersionValidation(targetAppVersion) : {
1047
+ error: void 0,
1048
+ normalizedRange: null
1049
+ };
1050
+ const hasTargetAppVersionError = Boolean(targetAppVersionValidation.error);
1051
+ const isDownloading = bundleDownloadUrlMutation.isPending;
1052
+ const handleAddCohort = () => {
1053
+ const normalizedCohort = normalizeCohortValue(newCohort);
1054
+ if (!normalizedCohort) return;
1055
+ if (!isValidCohort(normalizedCohort)) {
1056
+ toast.error(INVALID_COHORT_ERROR_MESSAGE);
1057
+ return;
1058
+ }
1059
+ const currentCohorts = form.getFieldValue("targetCohorts");
1060
+ if (currentCohorts.includes(normalizedCohort)) {
1061
+ toast.error("Cohort already exists");
1062
+ return;
1063
+ }
1064
+ form.setFieldValue("targetCohorts", [...currentCohorts, normalizedCohort]);
1065
+ setNewCohort("");
1066
+ };
1067
+ const handleRemoveCohort = (cohortToRemove) => {
1068
+ const currentCohorts = form.getFieldValue("targetCohorts");
1069
+ form.setFieldValue("targetCohorts", currentCohorts.filter((cohort) => cohort !== cohortToRemove));
1070
+ };
1071
+ const handleKeyDown = (e) => {
1072
+ if (e.key === "Enter") {
1073
+ e.preventDefault();
1074
+ handleAddCohort();
1075
+ }
1076
+ };
1077
+ const handleDownloadBundle = async () => {
1078
+ const downloadWindow = window.open("", "_blank");
1079
+ try {
1080
+ const { fileUrl } = await bundleDownloadUrlMutation.mutateAsync({ bundleId: bundle.id });
1081
+ if (!fileUrl) throw new Error("Bundle download URL is empty");
1082
+ if (downloadWindow) {
1083
+ downloadWindow.opener = null;
1084
+ downloadWindow.location.href = fileUrl;
1085
+ } else window.open(fileUrl, "_blank", "noopener,noreferrer");
1086
+ toast.success("Bundle download started");
1087
+ } catch (error) {
1088
+ downloadWindow?.close();
1089
+ const message = error instanceof Error ? error.message : "Failed to download bundle";
1090
+ toast.error(message);
1091
+ console.error(error);
1092
+ }
1093
+ };
1094
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1095
+ className: "space-y-6",
1096
+ children: [
1097
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", {
1098
+ onSubmit: (e) => {
1099
+ e.preventDefault();
1100
+ e.stopPropagation();
1101
+ if (form.state.isDefaultValue || isSaving || hasTargetAppVersionError) return;
1102
+ form.handleSubmit();
1103
+ },
1104
+ className: "space-y-6",
1105
+ children: [
1106
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(form.Field, {
1107
+ name: "message",
1108
+ children: (field) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1109
+ className: "space-y-2",
1110
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, {
1111
+ htmlFor: "message",
1112
+ children: "Message"
1113
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Textarea, {
1114
+ id: "message",
1115
+ value: field.state.value,
1116
+ onChange: (e) => field.handleChange(e.target.value),
1117
+ placeholder: "Update message...",
1118
+ rows: 3
1119
+ })]
1120
+ })
1121
+ }),
1122
+ shouldEditTargetAppVersion && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(form.Field, {
1123
+ name: "targetAppVersion",
1124
+ children: (field) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1125
+ className: "space-y-2",
1126
+ children: [
1127
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, {
1128
+ htmlFor: "targetAppVersion",
1129
+ children: "Target App Version"
1130
+ }),
1131
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
1132
+ id: "targetAppVersion",
1133
+ value: field.state.value,
1134
+ onChange: (e) => field.handleChange(e.target.value),
1135
+ placeholder: "1.0.0",
1136
+ "aria-invalid": hasTargetAppVersionError
1137
+ }),
1138
+ hasTargetAppVersionError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1139
+ className: "text-xs text-destructive",
1140
+ role: "alert",
1141
+ children: targetAppVersionValidation.error
1142
+ }) : targetAppVersionValidation.normalizedRange && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1143
+ className: "text-xs text-muted-foreground",
1144
+ children: targetAppVersionValidation.normalizedRange
1145
+ })
1146
+ ]
1147
+ })
1148
+ }),
1149
+ bundle.fingerprintHash && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1150
+ className: "space-y-2",
1151
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, { children: "Fingerprint Hash" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
1152
+ value: bundle.fingerprintHash,
1153
+ disabled: true,
1154
+ className: "font-mono text-xs"
1155
+ })]
1156
+ }),
1157
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(form.Field, {
1158
+ name: "enabled",
1159
+ children: (field) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1160
+ className: "flex items-center justify-between",
1161
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, {
1162
+ htmlFor: "enabled",
1163
+ children: "Enabled"
1164
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Switch$1, {
1165
+ id: "enabled",
1166
+ checked: field.state.value,
1167
+ onCheckedChange: field.handleChange
1168
+ })]
1169
+ })
1170
+ }),
1171
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(form.Field, {
1172
+ name: "shouldForceUpdate",
1173
+ children: (field) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1174
+ className: "flex items-center justify-between",
1175
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, {
1176
+ htmlFor: "shouldForceUpdate",
1177
+ children: "Force Update"
1178
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Switch$1, {
1179
+ id: "shouldForceUpdate",
1180
+ checked: field.state.value,
1181
+ onCheckedChange: field.handleChange
1182
+ })]
1183
+ })
1184
+ }),
1185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(form.Field, {
1186
+ name: "rolloutCohortCount",
1187
+ children: (field) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1188
+ className: "space-y-2",
1189
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1190
+ className: "flex items-center justify-between",
1191
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, {
1192
+ htmlFor: "rolloutCohortCount",
1193
+ children: "Rollout Percentage"
1194
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1195
+ className: "flex items-center gap-2",
1196
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1197
+ className: "text-sm font-medium",
1198
+ children: [formatRolloutPercentage(field.state.value), "%"]
1199
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RolloutCohortsDialog, {
1200
+ bundleId: bundle.id,
1201
+ rolloutCohortCount: field.state.value,
1202
+ targetCohorts,
1203
+ triggerLabel: "Preview Cohorts",
1204
+ triggerVariant: "outline",
1205
+ triggerSize: "sm"
1206
+ })]
1207
+ })]
1208
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Slider$1, {
1209
+ id: "rolloutCohortCount",
1210
+ value: [field.state.value],
1211
+ onValueChange: ([value]) => field.handleChange(value),
1212
+ min: 0,
1213
+ max: 1e3,
1214
+ step: 1,
1215
+ className: "mt-2"
1216
+ })]
1217
+ })
1218
+ }),
1219
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(form.Field, {
1220
+ name: "targetCohorts",
1221
+ children: (field) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1222
+ className: "space-y-2",
1223
+ children: [
1224
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label$1, { children: "Target Cohorts (optional)" }),
1225
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1226
+ className: "flex gap-2",
1227
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
1228
+ value: newCohort,
1229
+ onChange: (e) => setNewCohort(e.target.value),
1230
+ onKeyDown: handleKeyDown,
1231
+ placeholder: "Enter cohort...",
1232
+ className: "font-mono text-xs"
1233
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
1234
+ type: "button",
1235
+ variant: "outline",
1236
+ size: "icon",
1237
+ onClick: handleAddCohort,
1238
+ disabled: !newCohort.trim(),
1239
+ "aria-label": "Add cohort",
1240
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Plus, { className: "h-4 w-4" })
1241
+ })]
1242
+ }),
1243
+ field.state.value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
1244
+ className: "flex flex-wrap gap-2 mt-2",
1245
+ children: field.state.value.map((cohort) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Badge, {
1246
+ variant: "secondary",
1247
+ className: "font-mono text-xs gap-1 pr-1",
1248
+ children: [cohort.length > 20 ? `${cohort.slice(0, 20)}...` : cohort, /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
1249
+ type: "button",
1250
+ onClick: () => handleRemoveCohort(cohort),
1251
+ "aria-label": `Remove cohort ${cohort}`,
1252
+ className: "ml-1 rounded-full hover:bg-muted-foreground/20 p-0.5",
1253
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, { className: "h-3 w-3" })
1254
+ })]
1255
+ }, cohort))
1256
+ }),
1257
+ field.state.value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
1258
+ className: "text-xs text-muted-foreground mt-2",
1259
+ children: [
1260
+ field.state.value.length,
1261
+ " cohort",
1262
+ field.state.value.length !== 1 ? "s" : "",
1263
+ " targeted"
1264
+ ]
1265
+ })
1266
+ ]
1267
+ })
1268
+ }),
1269
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
1270
+ className: "pt-2",
1271
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
1272
+ type: "submit",
1273
+ size: "lg",
1274
+ className: "w-full",
1275
+ disabled: !hasChanges || isSaving || hasTargetAppVersionError,
1276
+ children: isSaving ? "Saving..." : "Save Changes"
1277
+ })
1278
+ })
1279
+ ]
1280
+ }),
1281
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Separator$1, { className: "my-8" }),
1282
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1283
+ className: "space-y-4",
1284
+ children: [
1285
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", {
1286
+ className: "text-sm font-medium",
1287
+ children: "Actions"
1288
+ }),
1289
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
1290
+ variant: "outline",
1291
+ size: "sm",
1292
+ className: "w-full",
1293
+ onClick: () => setShowPromoteDialog(true),
1294
+ children: "Promote to Channel"
1295
+ }),
1296
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
1297
+ variant: "outline",
1298
+ size: "sm",
1299
+ className: "w-full",
1300
+ onClick: handleDownloadBundle,
1301
+ disabled: isDownloading,
1302
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Download, { className: "h-4 w-4" }), isDownloading ? "Preparing Download..." : "Download Bundle"]
1303
+ }),
1304
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
1305
+ variant: "destructive",
1306
+ size: "sm",
1307
+ className: "w-full",
1308
+ onClick: () => setShowDeleteDialog(true),
1309
+ children: "Delete Bundle"
1310
+ })
1311
+ ]
1312
+ }),
1313
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PromoteChannelDialog, {
1314
+ bundle,
1315
+ open: showPromoteDialog,
1316
+ onOpenChange: setShowPromoteDialog,
1317
+ onSuccess: onClose
1318
+ }),
1319
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeleteBundleDialog, {
1320
+ bundle,
1321
+ open: showDeleteDialog,
1322
+ onOpenChange: setShowDeleteDialog,
1323
+ onSuccess: onClose
1324
+ })
1325
+ ]
1326
+ });
1327
+ }
1328
+ var normalizeGitUrl = (gitUrl) => {
1329
+ const trimmedGitUrl = gitUrl.trim();
1330
+ if (trimmedGitUrl.startsWith("git@")) {
1331
+ const sshMatch = trimmedGitUrl.match(/^git@([^:]+):(.+?)(?:\.git)?$/);
1332
+ if (sshMatch) return `https://${sshMatch[1]}/${sshMatch[2]}`;
1333
+ }
1334
+ if (trimmedGitUrl.startsWith("ssh://")) try {
1335
+ const url = new URL(trimmedGitUrl);
1336
+ return `https://${url.hostname}${url.pathname}`.replace(/\/+$/, "").replace(/\.git$/, "");
1337
+ } catch {
1338
+ return trimmedGitUrl.replace(/\/+$/, "").replace(/\.git$/, "");
1339
+ }
1340
+ return trimmedGitUrl.replace(/\/+$/, "").replace(/\.git$/, "");
1341
+ };
1342
+ var getCommitUrl = (gitUrl, commitHash) => {
1343
+ if (!gitUrl?.trim()) return null;
1344
+ const normalizedGitUrl = normalizeGitUrl(gitUrl);
1345
+ try {
1346
+ const { hostname } = new URL(normalizedGitUrl);
1347
+ if (hostname.includes("gitlab")) return `${normalizedGitUrl}/-/commit/${commitHash}`;
1348
+ if (hostname.includes("bitbucket")) return `${normalizedGitUrl}/commits/${commitHash}`;
1349
+ } catch {}
1350
+ return `${normalizedGitUrl}/commit/${commitHash}`;
1351
+ };
1352
+ function BundleMetadata({ bundle }) {
1353
+ const { data: configData, isFetched } = useConfigQuery();
1354
+ const hasMetadata = bundle.targetAppVersion || bundle.fingerprintHash || bundle.gitCommitHash || bundle.fileHash;
1355
+ const gitCommitUrl = bundle.gitCommitHash && isFetched ? getCommitUrl(configData?.console.gitUrl, bundle.gitCommitHash) : null;
1356
+ if (!hasMetadata) return null;
1357
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, {
1358
+ className: "text-sm font-medium",
1359
+ children: "Metadata"
1360
+ }) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardContent, {
1361
+ className: "space-y-3 text-sm",
1362
+ children: [
1363
+ bundle.targetAppVersion && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1364
+ className: "flex items-center justify-between",
1365
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1366
+ className: "text-muted-foreground",
1367
+ children: "App Version"
1368
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1369
+ className: "font-mono",
1370
+ children: bundle.targetAppVersion
1371
+ })]
1372
+ }),
1373
+ bundle.fingerprintHash && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1374
+ className: "flex items-center justify-between",
1375
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1376
+ className: "text-muted-foreground",
1377
+ children: "Fingerprint"
1378
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1379
+ className: "font-mono text-xs",
1380
+ children: [bundle.fingerprintHash.slice(0, 16), "..."]
1381
+ })]
1382
+ }),
1383
+ bundle.gitCommitHash && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1384
+ className: "flex items-center justify-between",
1385
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1386
+ className: "text-muted-foreground",
1387
+ children: "Git Commit"
1388
+ }), gitCommitUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("a", {
1389
+ href: gitCommitUrl,
1390
+ target: "_blank",
1391
+ rel: "noopener noreferrer",
1392
+ className: "flex items-center gap-1 text-primary hover:underline font-mono text-xs",
1393
+ children: [bundle.gitCommitHash.slice(0, 8), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ExternalLink, { className: "h-3 w-3" })]
1394
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1395
+ className: "font-mono text-xs",
1396
+ children: bundle.gitCommitHash.slice(0, 8)
1397
+ })]
1398
+ }),
1399
+ bundle.fileHash && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1400
+ className: "flex items-center justify-between",
1401
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1402
+ className: "text-muted-foreground",
1403
+ children: "File Hash"
1404
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1405
+ className: "font-mono text-xs",
1406
+ children: [bundle.fileHash.slice(0, 16), "..."]
1407
+ })]
1408
+ })
1409
+ ]
1410
+ })] });
1411
+ }
1412
+ function BundleEditorSheet({ bundleId, bundle, loading = false, open, onOpenChange }) {
1413
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Sheet, {
1414
+ open,
1415
+ onOpenChange,
1416
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SheetContent, {
1417
+ className: "w-[600px] sm:max-w-[600px] overflow-y-auto",
1418
+ 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", {
1419
+ className: "font-mono text-xs",
1420
+ children: ["Loading ", bundleId]
1421
+ }) : "Loading bundle details" : bundleId ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1422
+ className: "font-mono text-xs",
1423
+ children: ["Bundle not found: ", bundleId]
1424
+ }) : "Bundle details unavailable" })] }), bundle ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1425
+ className: "px-6 pb-6 space-y-6",
1426
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleEditorForm, {
1427
+ bundle,
1428
+ onClose: () => onOpenChange(false)
1429
+ }, bundle.id), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleMetadata, { bundle })]
1430
+ }) : loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1431
+ className: "px-6 pb-6 space-y-4",
1432
+ children: [
1433
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-10 w-full" }),
1434
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-28 w-full" }),
1435
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-10 w-full" }),
1436
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-10 w-full" })
1437
+ ]
1438
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
1439
+ className: "px-6 pb-6 text-sm text-muted-foreground",
1440
+ children: "The requested bundle could not be loaded."
1441
+ })]
1442
+ })
1443
+ });
1444
+ }
1445
+ function Table({ className, ...props }) {
1446
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
1447
+ "data-slot": "table-container",
1448
+ className: "relative w-full overflow-x-auto",
1449
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("table", {
1450
+ "data-slot": "table",
1451
+ className: cn("w-full caption-bottom text-xs", className),
1452
+ ...props
1453
+ })
1454
+ });
1455
+ }
1456
+ function TableHeader({ className, ...props }) {
1457
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", {
1458
+ "data-slot": "table-header",
1459
+ className: cn("[&_tr]:border-b", className),
1460
+ ...props
1461
+ });
1462
+ }
1463
+ function TableBody({ className, ...props }) {
1464
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", {
1465
+ "data-slot": "table-body",
1466
+ className: cn("[&_tr:last-child]:border-0", className),
1467
+ ...props
1468
+ });
1469
+ }
1470
+ function TableRow({ className, ...props }) {
1471
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", {
1472
+ "data-slot": "table-row",
1473
+ className: cn("hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors", className),
1474
+ ...props
1475
+ });
1476
+ }
1477
+ function TableHead({ className, ...props }) {
1478
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
1479
+ "data-slot": "table-head",
1480
+ className: cn("text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0", className),
1481
+ ...props
1482
+ });
1483
+ }
1484
+ function TableCell({ className, ...props }) {
1485
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
1486
+ "data-slot": "table-cell",
1487
+ className: cn("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0", className),
1488
+ ...props
1489
+ });
1490
+ }
1491
+ function BundleIdDisplay({ bundleId, maxLength = 12 }) {
1492
+ const truncated = bundleId.length > maxLength ? bundleId.slice(-maxLength) : bundleId;
1493
+ if (bundleId.length <= maxLength) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1494
+ className: "font-mono text-xs",
1495
+ children: bundleId
1496
+ });
1497
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tooltip$1, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipTrigger, {
1498
+ asChild: true,
1499
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1500
+ className: "font-mono text-xs cursor-help",
1501
+ children: truncated
1502
+ })
1503
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1504
+ className: "font-mono text-xs",
1505
+ children: bundleId
1506
+ }) })] }) });
1507
+ }
1508
+ var channelColors = {
1509
+ production: "bg-green-500/10 text-green-700 dark:text-green-400 border-green-500/20",
1510
+ dev: "bg-blue-500/10 text-blue-700 dark:text-blue-400 border-blue-500/20",
1511
+ staging: "bg-yellow-500/10 text-yellow-700 dark:text-yellow-400 border-yellow-500/20"
1512
+ };
1513
+ function ChannelBadge({ channel, className }) {
1514
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
1515
+ variant: "outline",
1516
+ className: cn(channelColors[channel.toLowerCase()] || "bg-gray-500/10 text-gray-700 dark:text-gray-400 border-gray-500/20", className),
1517
+ children: channel
1518
+ });
1519
+ }
1520
+ function EnabledStatusIcon({ enabled, className, falseIcon = "x" }) {
1521
+ if (enabled) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: cn("h-4 w-4 text-green-600 dark:text-green-400", className) });
1522
+ if (falseIcon === "minus") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Minus, { className: cn("h-4 w-4 text-muted-foreground", className) });
1523
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, { className: cn("h-4 w-4 text-red-600 dark:text-red-400", className) });
1524
+ }
1525
+ function RolloutPercentageBadge({ percentage, className }) {
1526
+ const isPartialRollout = percentage < 100;
1527
+ const formattedPercentage = percentage.toFixed(1);
1528
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Badge, {
1529
+ variant: isPartialRollout ? "secondary" : "default",
1530
+ className: cn("gap-1", className),
1531
+ children: [
1532
+ isPartialRollout && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TriangleAlert, { className: "h-3 w-3" }),
1533
+ formattedPercentage,
1534
+ "%"
1535
+ ]
1536
+ });
1537
+ }
1538
+ import_dayjs_min.default.extend(import_relativeTime.default);
1539
+ function TimestampDisplay({ uuid, format = "relative" }) {
1540
+ const date = (0, import_dayjs_min.default)(extractTimestampFromUUIDv7(uuid));
1541
+ if (format === "relative") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1542
+ className: "text-sm text-muted-foreground",
1543
+ children: date.fromNow()
1544
+ });
1545
+ if (format === "absolute") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1546
+ className: "text-sm text-muted-foreground",
1547
+ children: date.format("YYYY-MM-DD HH:mm:ss")
1548
+ });
1549
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1550
+ className: "flex flex-col",
1551
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1552
+ className: "text-sm",
1553
+ children: date.fromNow()
1554
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1555
+ className: "text-xs text-muted-foreground",
1556
+ children: date.format("YYYY-MM-DD HH:mm:ss")
1557
+ })]
1558
+ });
1559
+ }
1560
+ var columnHelper = createColumnHelper();
1561
+ var bundleColumns = [
1562
+ columnHelper.accessor("id", {
1563
+ header: "Bundle ID",
1564
+ cell: (info) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleIdDisplay, { bundleId: info.getValue() })
1565
+ }),
1566
+ columnHelper.accessor("channel", {
1567
+ header: "Channel",
1568
+ cell: (info) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChannelBadge, { channel: info.getValue() })
1569
+ }),
1570
+ columnHelper.accessor("platform", {
1571
+ header: "Platform",
1572
+ cell: (info) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1573
+ className: "flex items-center gap-2",
1574
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformIcon, {
1575
+ platform: info.getValue(),
1576
+ className: "h-4 w-4"
1577
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: info.getValue() === "ios" ? "iOS" : "Android" })]
1578
+ })
1579
+ }),
1580
+ columnHelper.display({
1581
+ id: "target",
1582
+ header: "Target",
1583
+ cell: (info) => {
1584
+ const row = info.row.original;
1585
+ if (row.fingerprintHash) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tooltip$1, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipTrigger, {
1586
+ asChild: true,
1587
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1588
+ className: "flex items-center gap-2 cursor-help",
1589
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FingerprintPattern, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1590
+ className: "font-mono text-xs",
1591
+ children: row.fingerprintHash.slice(0, 8)
1592
+ })]
1593
+ })
1594
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1595
+ className: "font-mono text-xs",
1596
+ children: row.fingerprintHash
1597
+ }) })] });
1598
+ if (row.targetAppVersion) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1599
+ className: "flex items-center gap-2",
1600
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Package, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1601
+ className: "text-sm",
1602
+ children: row.targetAppVersion
1603
+ })]
1604
+ });
1605
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1606
+ className: "text-sm text-muted-foreground",
1607
+ children: "-"
1608
+ });
1609
+ }
1610
+ }),
1611
+ columnHelper.accessor("enabled", {
1612
+ header: "Enabled",
1613
+ cell: (info) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EnabledStatusIcon, { enabled: info.getValue() })
1614
+ }),
1615
+ columnHelper.accessor("shouldForceUpdate", {
1616
+ header: "Force Update",
1617
+ cell: (info) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EnabledStatusIcon, {
1618
+ enabled: info.getValue(),
1619
+ falseIcon: "minus"
1620
+ })
1621
+ }),
1622
+ columnHelper.accessor("rolloutCohortCount", {
1623
+ header: "Rollout",
1624
+ cell: (info) => {
1625
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RolloutPercentageBadge, { percentage: (info.getValue() ?? 1e3) / 10 });
1626
+ }
1627
+ }),
1628
+ columnHelper.accessor("message", {
1629
+ header: "Message",
1630
+ cell: (info) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1631
+ className: "text-sm text-muted-foreground",
1632
+ children: info.getValue() || "-"
1633
+ })
1634
+ }),
1635
+ columnHelper.accessor("id", {
1636
+ id: "created",
1637
+ header: "Created",
1638
+ cell: (info) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TimestampDisplay, { uuid: info.getValue() })
1639
+ })
1640
+ ];
1641
+ function BundlesTable({ bundles, selectedBundleId, onRowClick }) {
1642
+ const { filters, setFilters } = useFilterParams();
1643
+ const currentOffset = Number(filters.offset || 0);
1644
+ const table = useReactTable({
1645
+ data: bundles,
1646
+ columns: bundleColumns,
1647
+ getCoreRowModel: getCoreRowModel()
1648
+ });
1649
+ const handlePreviousPage = () => {
1650
+ setFilters({ offset: Math.max(0, currentOffset - 20).toString() });
1651
+ };
1652
+ const handleNextPage = () => {
1653
+ setFilters({ offset: (currentOffset + 20).toString() });
1654
+ };
1655
+ const hasNextPage = bundles.length === 20;
1656
+ const hasPreviousPage = currentOffset > 0;
1657
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1658
+ className: "space-y-4",
1659
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
1660
+ className: "rounded-lg border bg-card text-card-foreground shadow-sm overflow-hidden",
1661
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Table, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHeader, {
1662
+ className: "bg-muted/40",
1663
+ children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableRow, {
1664
+ className: "hover:bg-transparent border-b border-border/60",
1665
+ children: headerGroup.headers.map((header) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
1666
+ className: "h-10 text-xs font-semibold uppercase text-muted-foreground/70",
1667
+ children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())
1668
+ }, header.id))
1669
+ }, headerGroup.id))
1670
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableBody, { children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableRow, {
1671
+ "data-state": row.original.id === selectedBundleId ? "selected" : void 0,
1672
+ onClick: () => onRowClick(row.original),
1673
+ className: "cursor-pointer hover:bg-muted/30 transition-colors data-[state=selected]:bg-muted",
1674
+ children: row.getVisibleCells().map((cell) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
1675
+ className: "py-3",
1676
+ children: flexRender(cell.column.columnDef.cell, cell.getContext())
1677
+ }, cell.id))
1678
+ }, row.id)) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
1679
+ colSpan: bundleColumns.length,
1680
+ className: "h-32 text-center text-muted-foreground",
1681
+ children: "No bundles found matching your filters."
1682
+ }) }) })] })
1683
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1684
+ className: "flex items-center justify-between px-2",
1685
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1686
+ className: "text-xs text-muted-foreground font-medium",
1687
+ children: [
1688
+ "Showing ",
1689
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1690
+ className: "text-foreground",
1691
+ children: currentOffset + 1
1692
+ }),
1693
+ " ",
1694
+ "to",
1695
+ " ",
1696
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1697
+ className: "text-foreground",
1698
+ children: currentOffset + bundles.length
1699
+ }),
1700
+ " ",
1701
+ "entries"
1702
+ ]
1703
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1704
+ className: "flex items-center gap-2",
1705
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
1706
+ variant: "outline",
1707
+ size: "sm",
1708
+ onClick: handlePreviousPage,
1709
+ disabled: !hasPreviousPage,
1710
+ className: "h-8 px-3 text-xs",
1711
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronLeft, { className: "h-3.5 w-3.5 mr-1" }), "Previous"]
1712
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
1713
+ variant: "outline",
1714
+ size: "sm",
1715
+ onClick: handleNextPage,
1716
+ disabled: !hasNextPage,
1717
+ className: "h-8 px-3 text-xs",
1718
+ children: ["Next", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronRight, { className: "h-3.5 w-3.5 ml-1" })]
1719
+ })]
1720
+ })]
1721
+ })]
1722
+ });
1723
+ }
1724
+ function FilterToolbar() {
1725
+ const { filters, setFilters, resetFilters } = useFilterParams();
1726
+ const { data: channels = [] } = useChannelsQuery();
1727
+ const hasActiveFilters = filters.channel || filters.platform;
1728
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
1729
+ className: "flex h-12 shrink-0 items-center gap-2 border-b px-4 bg-card/50 backdrop-blur-sm sticky top-0 z-10",
1730
+ children: [
1731
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarTrigger, { className: "-ml-1" }),
1732
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1733
+ className: "flex items-center gap-1.5 text-muted-foreground ml-2",
1734
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Funnel, { className: "h-3.5 w-3.5" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1735
+ className: "text-xs font-medium",
1736
+ children: "Filters"
1737
+ })]
1738
+ }),
1739
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
1740
+ value: filters.platform || "all",
1741
+ onValueChange: (value) => setFilters({ platform: value === "all" ? void 0 : value }),
1742
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
1743
+ className: "w-[140px] h-8 text-xs",
1744
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { placeholder: "All Platforms" })
1745
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [
1746
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
1747
+ value: "all",
1748
+ children: "All Platforms"
1749
+ }),
1750
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
1751
+ value: "ios",
1752
+ children: "iOS"
1753
+ }),
1754
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
1755
+ value: "android",
1756
+ children: "Android"
1757
+ })
1758
+ ] })]
1759
+ }),
1760
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
1761
+ value: filters.channel || "all",
1762
+ onValueChange: (value) => setFilters({ channel: value === "all" ? void 0 : value }),
1763
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
1764
+ className: "w-[140px] h-8 text-xs",
1765
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { placeholder: "All Channels" })
1766
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SelectContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
1767
+ value: "all",
1768
+ children: "All Channels"
1769
+ }), channels.map((channel) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
1770
+ value: channel,
1771
+ children: channel
1772
+ }, channel))] })]
1773
+ }),
1774
+ hasActiveFilters && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
1775
+ variant: "ghost",
1776
+ size: "sm",
1777
+ onClick: resetFilters,
1778
+ className: "h-8 px-2 text-xs text-muted-foreground hover:text-foreground",
1779
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, { className: "h-3.5 w-3.5 mr-1" }), "Clear"]
1780
+ })
1781
+ ]
1782
+ });
1783
+ }
1784
+ function BundlesPage() {
1785
+ const { filters, bundleId, setBundleId } = useFilterParams();
1786
+ const activeBundleId = bundleId ?? "";
1787
+ const { data: bundlesData, isLoading } = useBundlesQuery({
1788
+ channel: filters.channel,
1789
+ platform: filters.platform,
1790
+ offset: filters.offset,
1791
+ limit: "20"
1792
+ });
1793
+ const bundles = bundlesData?.data ?? [];
1794
+ const selectedBundleFromList = activeBundleId ? bundles.find((bundle) => bundle.id === activeBundleId) ?? null : null;
1795
+ const { data: selectedBundleFromQuery, isPending: isSelectedBundlePending } = useBundleQuery(activeBundleId);
1796
+ const selectedBundle = selectedBundleFromQuery ?? selectedBundleFromList;
1797
+ const isSelectedBundleLoading = Boolean(activeBundleId) && !selectedBundle && isSelectedBundlePending;
1798
+ if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1799
+ className: "flex flex-col h-full",
1800
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilterToolbar, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1801
+ className: "flex-1 p-6 space-y-4 bg-muted/5",
1802
+ children: [
1803
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-12 w-full" }),
1804
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-12 w-full" }),
1805
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-12 w-full" }),
1806
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-12 w-full" })
1807
+ ]
1808
+ })]
1809
+ });
1810
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1811
+ className: "flex flex-col h-full",
1812
+ children: [
1813
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilterToolbar, {}),
1814
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
1815
+ className: "flex-1 p-6 space-y-6 bg-muted/5",
1816
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundlesTable, {
1817
+ bundles,
1818
+ selectedBundleId: bundleId,
1819
+ onRowClick: (bundle) => setBundleId(bundle.id)
1820
+ })
1821
+ }),
1822
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BundleEditorSheet, {
1823
+ bundleId,
1824
+ bundle: selectedBundle,
1825
+ loading: isSelectedBundleLoading,
1826
+ open: Boolean(bundleId),
1827
+ onOpenChange: (open) => !open && setBundleId(void 0)
1828
+ })
1829
+ ]
1830
+ });
1831
+ }
1832
+ //#endregion
1833
+ export { BundlesPage as component };