@burdenoff/microfe-devportal 2026.720.7 → 2026.728.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devportal/components/ConfirmDialog.d.ts.map +1 -1
- package/dist/devportal/components/ConfirmDialog.js +12 -14
- package/dist/devportal/components/ConfirmDialog.js.map +1 -1
- package/dist/devportal/hooks/usePackageUpload.d.ts.map +1 -1
- package/dist/devportal/hooks/usePackageUpload.js.map +1 -1
- package/dist/devportal/pages/ApplicationDetailPage.js +2 -2
- package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationVersionsPage.d.ts.map +1 -1
- package/dist/devportal/pages/ApplicationVersionsPage.js +276 -271
- package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
- package/dist/devportal/pages/CreateApplicationPage.d.ts.map +1 -1
- package/dist/devportal/pages/CreateApplicationPage.js +246 -177
- package/dist/devportal/pages/CreateApplicationPage.js.map +1 -1
- package/dist/devportal/pages/EditApplicationPage.d.ts.map +1 -1
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/PublishPage.d.ts.map +1 -1
- package/dist/devportal/pages/PublishPage.js.map +1 -1
- package/dist/devportal/pages/SellerDashboardPage.d.ts.map +1 -1
- package/dist/devportal/pages/SellerDashboardPage.js.map +1 -1
- package/dist/devportal/pages/SellerItemsPage.d.ts.map +1 -1
- package/dist/devportal/pages/SellerItemsPage.js.map +1 -1
- package/dist/devportal/types/index.d.ts.map +1 -1
- package/dist/devportal/types/index.js.map +1 -1
- package/dist/devportal/utils/constants.d.ts.map +1 -1
- package/dist/devportal/utils/constants.js +3 -1
- package/dist/devportal/utils/constants.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfirmDialog.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/ConfirmDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAK1E,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,
|
|
1
|
+
{"version":3,"file":"ConfirmDialog.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/ConfirmDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAK1E,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CAgLhD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,KAAK,EAAE,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAwCA,CAAC"}
|
|
@@ -8,8 +8,10 @@ var d = ({ open: d, onOpenChange: f, onConfirm: p, title: m, description: h, con
|
|
|
8
8
|
let x = n(), S = n(), C = r(null), w = r(null), T = r(null), { t: E } = i(), D = (e, t) => {
|
|
9
9
|
let n = E(e);
|
|
10
10
|
return n === e ? t : n;
|
|
11
|
-
};
|
|
11
|
+
}, O = r(f);
|
|
12
12
|
if (t(() => {
|
|
13
|
+
O.current = f;
|
|
14
|
+
}, [f]), t(() => {
|
|
13
15
|
if (!d) return;
|
|
14
16
|
T.current = document.activeElement;
|
|
15
17
|
let e = document.body.style.overflow;
|
|
@@ -19,17 +21,13 @@ var d = ({ open: d, onOpenChange: f, onConfirm: p, title: m, description: h, con
|
|
|
19
21
|
t?.focus();
|
|
20
22
|
});
|
|
21
23
|
let n = (e) => {
|
|
22
|
-
e.key === "Escape" && !y && (e.preventDefault(),
|
|
24
|
+
e.key === "Escape" && !y && (e.preventDefault(), O.current(!1));
|
|
23
25
|
};
|
|
24
26
|
return document.addEventListener("keydown", n), () => {
|
|
25
27
|
document.body.style.overflow = e, document.removeEventListener("keydown", n), T.current?.focus();
|
|
26
28
|
};
|
|
27
|
-
}, [
|
|
28
|
-
|
|
29
|
-
f,
|
|
30
|
-
d
|
|
31
|
-
]), !d) return null;
|
|
32
|
-
let O = {
|
|
29
|
+
}, [y, d]), !d) return null;
|
|
30
|
+
let k = {
|
|
33
31
|
default: {
|
|
34
32
|
icon: /* @__PURE__ */ l(s, { className: "h-6 w-6 text-accent-blue" }),
|
|
35
33
|
iconBg: "bg-status-info-bg-subtle",
|
|
@@ -50,14 +48,14 @@ var d = ({ open: d, onOpenChange: f, onConfirm: p, title: m, description: h, con
|
|
|
50
48
|
iconBg: "bg-status-success-bg-subtle",
|
|
51
49
|
button: "bg-status-success-bg text-white hover:bg-status-success-bg/90"
|
|
52
50
|
}
|
|
53
|
-
}[v === "destructive" ? "danger" : v],
|
|
51
|
+
}[v === "destructive" ? "danger" : v], A = () => f(!1);
|
|
54
52
|
return /* @__PURE__ */ u("div", {
|
|
55
53
|
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
56
54
|
children: [/* @__PURE__ */ l("button", {
|
|
57
55
|
type: "button",
|
|
58
56
|
"aria-label": "Close dialog backdrop",
|
|
59
57
|
className: "absolute inset-0 bg-bg-overlay",
|
|
60
|
-
onClick:
|
|
58
|
+
onClick: A
|
|
61
59
|
}), /* @__PURE__ */ u("div", {
|
|
62
60
|
ref: C,
|
|
63
61
|
role: "dialog",
|
|
@@ -69,8 +67,8 @@ var d = ({ open: d, onOpenChange: f, onConfirm: p, title: m, description: h, con
|
|
|
69
67
|
children: [/* @__PURE__ */ u("div", {
|
|
70
68
|
className: "flex items-start gap-4",
|
|
71
69
|
children: [/* @__PURE__ */ l("div", {
|
|
72
|
-
className: e("rounded-full p-2",
|
|
73
|
-
children:
|
|
70
|
+
className: e("rounded-full p-2", k.iconBg),
|
|
71
|
+
children: k.icon
|
|
74
72
|
}), /* @__PURE__ */ u("div", {
|
|
75
73
|
className: "flex-1",
|
|
76
74
|
children: [
|
|
@@ -95,7 +93,7 @@ var d = ({ open: d, onOpenChange: f, onConfirm: p, title: m, description: h, con
|
|
|
95
93
|
className: "mt-6 flex justify-end gap-3",
|
|
96
94
|
children: [/* @__PURE__ */ l("button", {
|
|
97
95
|
type: "button",
|
|
98
|
-
onClick:
|
|
96
|
+
onClick: A,
|
|
99
97
|
disabled: y,
|
|
100
98
|
className: "rounded-md border px-4 py-2 text-sm font-medium hover:bg-bg-sunken transition-colors disabled:opacity-50",
|
|
101
99
|
children: _
|
|
@@ -104,7 +102,7 @@ var d = ({ open: d, onOpenChange: f, onConfirm: p, title: m, description: h, con
|
|
|
104
102
|
type: "button",
|
|
105
103
|
onClick: p,
|
|
106
104
|
disabled: y,
|
|
107
|
-
className: e("rounded-md px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50",
|
|
105
|
+
className: e("rounded-md px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50", k.button),
|
|
108
106
|
children: y ? D("devportal.processing", "Processing...") : g
|
|
109
107
|
})]
|
|
110
108
|
})]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfirmDialog.js","names":[],"sources":["../../../src/devportal/components/ConfirmDialog.tsx"],"sourcesContent":["import { useEffect, useId, useRef, type FC, type ReactNode } from \"react\";\nimport { AlertTriangle, Info, CheckCircle, XCircle } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ConfirmDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onConfirm: () => void | Promise<void>;\n title: string;\n description?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n variant?: \"default\" | \"danger\" | \"warning\" | \"success\" | \"destructive\";\n loading?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Confirmation dialog component\n */\nexport const ConfirmDialog: FC<ConfirmDialogProps> = ({\n open,\n onOpenChange,\n onConfirm,\n title,\n description,\n confirmLabel = \"Confirm\",\n cancelLabel = \"Cancel\",\n variant = \"default\",\n loading = false,\n children,\n}) => {\n const titleId = useId();\n const descriptionId = useId();\n const dialogRef = useRef<HTMLDivElement | null>(null);\n const confirmButtonRef = useRef<HTMLButtonElement | null>(null);\n const previousFocusRef = useRef<HTMLElement | null>(null);\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n useEffect(() => {\n if (!open) {\n return undefined;\n }\n\n previousFocusRef.current = document.activeElement as HTMLElement | null;\n const originalOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n // For form dialogs (with children), focus the container so Tab naturally\n // walks through the form fields first. Only auto-focus the confirm button\n // for simple confirmation dialogs that have no form fields.\n const focusTarget = children\n ? dialogRef.current\n : (confirmButtonRef.current ?? dialogRef.current);\n window.requestAnimationFrame(() => {\n focusTarget?.focus();\n });\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\" && !loading) {\n event.preventDefault();\n onOpenChange(false);\n }\n };\n\n document.addEventListener(\"keydown\", onKeyDown);\n\n return () => {\n document.body.style.overflow = originalOverflow;\n document.removeEventListener(\"keydown\", onKeyDown);\n previousFocusRef.current?.focus();\n };\n }, [loading, onOpenChange, open]);\n\n if (!open) return null;\n\n // Map 'destructive' to 'danger' for internal use\n const normalizedVariant = variant === \"destructive\" ? \"danger\" : variant;\n\n const variantStyles = {\n default: {\n icon: <Info className=\"h-6 w-6 text-accent-blue\" />,\n iconBg: \"bg-status-info-bg-subtle\",\n button:\n \"bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\",\n },\n danger: {\n icon: <XCircle className=\"h-6 w-6 text-status-error-text\" />,\n iconBg: \"bg-status-error-bg-subtle\",\n button: \"bg-status-error-bg text-white hover:bg-status-error-bg/90\",\n },\n warning: {\n icon: <AlertTriangle className=\"h-6 w-6 text-status-warning-text\" />,\n iconBg: \"bg-status-warning-bg-subtle\",\n button: \"bg-status-warning-bg text-white hover:bg-status-warning-bg/90\",\n },\n success: {\n icon: <CheckCircle className=\"h-6 w-6 text-status-success-text\" />,\n iconBg: \"bg-status-success-bg-subtle\",\n button: \"bg-status-success-bg text-white hover:bg-status-success-bg/90\",\n },\n };\n\n const styles = variantStyles[normalizedVariant];\n\n const handleClose = () => onOpenChange(false);\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <button\n type=\"button\"\n aria-label=\"Close dialog backdrop\"\n className=\"absolute inset-0 bg-bg-overlay\"\n onClick={handleClose}\n />\n\n {/* Dialog */}\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n aria-describedby={description || children ? descriptionId : undefined}\n tabIndex={-1}\n className=\"relative z-10 w-full max-w-md rounded-lg bg-bg-surface p-6 shadow-xl\"\n >\n <div className=\"flex items-start gap-4\">\n <div className={cn(\"rounded-full p-2\", styles.iconBg)}>\n {styles.icon}\n </div>\n <div className=\"flex-1\">\n <h3 id={titleId} className=\"text-lg font-semibold\">\n {title}\n </h3>\n {description && (\n <p\n id={descriptionId}\n className=\"mt-2 text-sm text-text-secondary\"\n >\n {description}\n </p>\n )}\n {children && (\n <div\n className=\"mt-4\"\n id={description ? undefined : descriptionId}\n >\n {children}\n </div>\n )}\n </div>\n </div>\n\n <div className=\"mt-6 flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={handleClose}\n disabled={loading}\n className=\"rounded-md border px-4 py-2 text-sm font-medium hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n {cancelLabel}\n </button>\n <button\n ref={confirmButtonRef}\n type=\"button\"\n onClick={onConfirm}\n disabled={loading}\n className={cn(\n \"rounded-md px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50\",\n styles.button,\n )}\n >\n {loading\n ? tr(\"devportal.processing\", \"Processing...\")\n : confirmLabel}\n </button>\n </div>\n </div>\n </div>\n );\n};\n\n/**\n * Alert banner component\n */\nexport const Alert: FC<{\n variant?: \"info\" | \"warning\" | \"error\" | \"success\";\n title?: string;\n children: ReactNode;\n onDismiss?: () => void;\n className?: string;\n}> = ({ variant = \"info\", title, children, onDismiss, className }) => {\n const variantStyles = {\n info: \"bg-status-info-bg-subtle border-border-subtle text-status-info-text\",\n warning:\n \"bg-status-warning-bg-subtle border-border-subtle text-status-warning-text\",\n error:\n \"bg-status-error-bg-subtle border-border-subtle text-status-error-text\",\n success:\n \"bg-status-success-bg-subtle border-border-subtle text-status-success-text\",\n };\n\n const icons = {\n info: <Info className=\"h-5 w-5\" />,\n warning: <AlertTriangle className=\"h-5 w-5\" />,\n error: <XCircle className=\"h-5 w-5\" />,\n success: <CheckCircle className=\"h-5 w-5\" />,\n };\n\n return (\n <div\n className={cn(\"rounded-lg border p-4\", variantStyles[variant], className)}\n >\n <div className=\"flex items-start gap-3\">\n {icons[variant]}\n <div className=\"flex-1\">\n {title && <h4 className=\"font-medium\">{title}</h4>}\n <div className=\"text-sm\">{children}</div>\n </div>\n {onDismiss && (\n <button\n type=\"button\"\n onClick={onDismiss}\n className=\"hover:opacity-70\"\n >\n <XCircle className=\"h-5 w-5\" />\n </button>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ConfirmDialog.js","names":[],"sources":["../../../src/devportal/components/ConfirmDialog.tsx"],"sourcesContent":["import { useEffect, useId, useRef, type FC, type ReactNode } from \"react\";\nimport { AlertTriangle, Info, CheckCircle, XCircle } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ConfirmDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onConfirm: () => void | Promise<void>;\n title: string;\n description?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n variant?: \"default\" | \"danger\" | \"warning\" | \"success\" | \"destructive\";\n loading?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Confirmation dialog component\n */\nexport const ConfirmDialog: FC<ConfirmDialogProps> = ({\n open,\n onOpenChange,\n onConfirm,\n title,\n description,\n confirmLabel = \"Confirm\",\n cancelLabel = \"Cancel\",\n variant = \"default\",\n loading = false,\n children,\n}) => {\n const titleId = useId();\n const descriptionId = useId();\n const dialogRef = useRef<HTMLDivElement | null>(null);\n const confirmButtonRef = useRef<HTMLButtonElement | null>(null);\n const previousFocusRef = useRef<HTMLElement | null>(null);\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n // Keep a stable ref to the latest onOpenChange. Parents commonly pass an\n // inline arrow (new reference every render), so depending on it directly\n // would re-run the focus effect on every keystroke and yank focus away from\n // form inputs. Storing it in a ref lets the effect depend only on open/loading.\n const onOpenChangeRef = useRef(onOpenChange);\n useEffect(() => {\n onOpenChangeRef.current = onOpenChange;\n }, [onOpenChange]);\n\n useEffect(() => {\n if (!open) {\n return undefined;\n }\n\n previousFocusRef.current = document.activeElement as HTMLElement | null;\n const originalOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n // For form dialogs (with children), focus the container so Tab naturally\n // walks through the form fields first. Only auto-focus the confirm button\n // for simple confirmation dialogs that have no form fields.\n const focusTarget = children\n ? dialogRef.current\n : (confirmButtonRef.current ?? dialogRef.current);\n window.requestAnimationFrame(() => {\n focusTarget?.focus();\n });\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\" && !loading) {\n event.preventDefault();\n onOpenChangeRef.current(false);\n }\n };\n\n document.addEventListener(\"keydown\", onKeyDown);\n\n return () => {\n document.body.style.overflow = originalOverflow;\n document.removeEventListener(\"keydown\", onKeyDown);\n previousFocusRef.current?.focus();\n };\n // children is intentionally omitted: it re-creates every render and would\n // re-trigger focus-stealing on each keystroke. Only open/loading gate this.\n }, [loading, open]);\n\n if (!open) return null;\n\n // Map 'destructive' to 'danger' for internal use\n const normalizedVariant = variant === \"destructive\" ? \"danger\" : variant;\n\n const variantStyles = {\n default: {\n icon: <Info className=\"h-6 w-6 text-accent-blue\" />,\n iconBg: \"bg-status-info-bg-subtle\",\n button:\n \"bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\",\n },\n danger: {\n icon: <XCircle className=\"h-6 w-6 text-status-error-text\" />,\n iconBg: \"bg-status-error-bg-subtle\",\n button: \"bg-status-error-bg text-white hover:bg-status-error-bg/90\",\n },\n warning: {\n icon: <AlertTriangle className=\"h-6 w-6 text-status-warning-text\" />,\n iconBg: \"bg-status-warning-bg-subtle\",\n button: \"bg-status-warning-bg text-white hover:bg-status-warning-bg/90\",\n },\n success: {\n icon: <CheckCircle className=\"h-6 w-6 text-status-success-text\" />,\n iconBg: \"bg-status-success-bg-subtle\",\n button: \"bg-status-success-bg text-white hover:bg-status-success-bg/90\",\n },\n };\n\n const styles = variantStyles[normalizedVariant];\n\n const handleClose = () => onOpenChange(false);\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <button\n type=\"button\"\n aria-label=\"Close dialog backdrop\"\n className=\"absolute inset-0 bg-bg-overlay\"\n onClick={handleClose}\n />\n\n {/* Dialog */}\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n aria-describedby={description || children ? descriptionId : undefined}\n tabIndex={-1}\n className=\"relative z-10 w-full max-w-md rounded-lg bg-bg-surface p-6 shadow-xl\"\n >\n <div className=\"flex items-start gap-4\">\n <div className={cn(\"rounded-full p-2\", styles.iconBg)}>\n {styles.icon}\n </div>\n <div className=\"flex-1\">\n <h3 id={titleId} className=\"text-lg font-semibold\">\n {title}\n </h3>\n {description && (\n <p\n id={descriptionId}\n className=\"mt-2 text-sm text-text-secondary\"\n >\n {description}\n </p>\n )}\n {children && (\n <div\n className=\"mt-4\"\n id={description ? undefined : descriptionId}\n >\n {children}\n </div>\n )}\n </div>\n </div>\n\n <div className=\"mt-6 flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={handleClose}\n disabled={loading}\n className=\"rounded-md border px-4 py-2 text-sm font-medium hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n {cancelLabel}\n </button>\n <button\n ref={confirmButtonRef}\n type=\"button\"\n onClick={onConfirm}\n disabled={loading}\n className={cn(\n \"rounded-md px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50\",\n styles.button,\n )}\n >\n {loading\n ? tr(\"devportal.processing\", \"Processing...\")\n : confirmLabel}\n </button>\n </div>\n </div>\n </div>\n );\n};\n\n/**\n * Alert banner component\n */\nexport const Alert: FC<{\n variant?: \"info\" | \"warning\" | \"error\" | \"success\";\n title?: string;\n children: ReactNode;\n onDismiss?: () => void;\n className?: string;\n}> = ({ variant = \"info\", title, children, onDismiss, className }) => {\n const variantStyles = {\n info: \"bg-status-info-bg-subtle border-border-subtle text-status-info-text\",\n warning:\n \"bg-status-warning-bg-subtle border-border-subtle text-status-warning-text\",\n error:\n \"bg-status-error-bg-subtle border-border-subtle text-status-error-text\",\n success:\n \"bg-status-success-bg-subtle border-border-subtle text-status-success-text\",\n };\n\n const icons = {\n info: <Info className=\"h-5 w-5\" />,\n warning: <AlertTriangle className=\"h-5 w-5\" />,\n error: <XCircle className=\"h-5 w-5\" />,\n success: <CheckCircle className=\"h-5 w-5\" />,\n };\n\n return (\n <div\n className={cn(\"rounded-lg border p-4\", variantStyles[variant], className)}\n >\n <div className=\"flex items-start gap-3\">\n {icons[variant]}\n <div className=\"flex-1\">\n {title && <h4 className=\"font-medium\">{title}</h4>}\n <div className=\"text-sm\">{children}</div>\n </div>\n {onDismiss && (\n <button\n type=\"button\"\n onClick={onDismiss}\n className=\"hover:opacity-70\"\n >\n <XCircle className=\"h-5 w-5\" />\n </button>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePackageUpload.d.ts","sourceRoot":"","sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"usePackageUpload.d.ts","sourceRoot":"","sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AA2JD,wBAAgB,gBAAgB,CAC9B,oBAAoB,EAAE,MAAM,GAAG,SAAS,EACxC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,iBAAiB,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,SAAS,EAC7D,UAAU,SAAwB;eAnKvB,OAAO;cACR,MAAM;WACT,MAAM,GAAG,IAAI;mBA0KL,IAAI,KAAG,OAAO,CAAC,mBAAmB,CAAC;EAoDnD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePackageUpload.js","names":[],"sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"sourcesContent":["/**\n * usePackageUpload — uploads a zip package to wspace-files-svc and returns a\n * stable downloadUrl to store as ApplicationVersion.packageUrl.\n *\n * Flow (mirrors uploadBlobToFiles in @burdenoff/fe-libs/storage):\n * 1. ensureFolderByPath(\"/DevPortal/Packages\") → folderId\n * 2. initiateUpload → { sessionId, presignedUrl }\n * 3. PUT blob to presignedUrl (progress tracked via XHR)\n * 4. completeUpload → { file.downloadUrl }\n *\n * Uses raw fetch for GraphQL mutations so we can target the workspace gateway\n * without a second ApolloProvider.\n */\nimport { useCallback, useState } from \"react\";\n\nexport interface PackageUploadResult {\n fileId: string;\n downloadUrl: string;\n}\n\nexport interface PackageUploadState {\n uploading: boolean;\n progress: number;\n error: string | null;\n}\n\ntype GqlFetcher = (\n query: string,\n variables: Record<string, unknown>,\n) => Promise<Record<string, unknown>>;\n\nfunction makeFetcher(\n workspacesGatewayUrl: string,\n authToken: string | undefined,\n workspaceId: string | undefined,\n workspaceToken: string | undefined,\n): GqlFetcher {\n return async (query, variables) => {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (authToken) headers[\"Authorization\"] = `Bearer ${authToken}`;\n if (workspaceToken)\n headers[\"X-Workspace-Authorization\"] = `Bearer ${workspaceToken}`;\n if (workspaceId) headers[\"x-workspace-id\"] = workspaceId;\n\n const endpoint = workspacesGatewayUrl.endsWith(\"/graphql\")\n ? workspacesGatewayUrl\n : `${workspacesGatewayUrl}/workspaces/graphql`;\n\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers,\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n if (!res.ok) {\n throw new Error(\n `GraphQL request failed: ${res.status} ${res.statusText}`,\n );\n }\n const json = (await res.json()) as {\n data?: Record<string, unknown>;\n errors?: Array<{ message: string }>;\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data ?? {};\n };\n}\n\nasync function ensureFolder(gql: GqlFetcher, path: string): Promise<string> {\n const data = await gql(\n `mutation DevPortal_EnsureFolder($path: String!) {\n ensureFolderByPath(path: $path) { id }\n }`,\n { path },\n );\n const id = (data.ensureFolderByPath as { id?: string } | undefined)?.id;\n if (!id) throw new Error(\"ensureFolderByPath returned no id\");\n return id;\n}\n\nasync function initiateUpload(\n gql: GqlFetcher,\n file: File,\n folderId: string,\n): Promise<{ sessionId: string; uploadUrl: string }> {\n const ext = file.name.includes(\".\")\n ? file.name.substring(file.name.lastIndexOf(\".\") + 1)\n : \"zip\";\n const data = await gql(\n `mutation DevPortal_InitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }`,\n {\n input: {\n fileName: file.name,\n fileSize: String(file.size),\n mimeType: file.type || \"application/zip\",\n extension: ext,\n folderId,\n visibility: \"PUBLIC\",\n },\n },\n );\n const result = data.initiateUpload as\n | { uploadUrl?: string; session?: { id?: string } }\n | undefined;\n const sessionId = result?.session?.id;\n const uploadUrl = result?.uploadUrl;\n if (!sessionId || !uploadUrl) {\n throw new Error(\"initiateUpload did not return sessionId or uploadUrl\");\n }\n return { sessionId, uploadUrl };\n}\n\nasync function putToPresignedUrl(\n file: File,\n uploadUrl: string,\n onProgress: (pct: number) => void,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.upload.addEventListener(\"progress\", (evt) => {\n if (evt.lengthComputable) {\n onProgress(10 + Math.round((evt.loaded / evt.total) * 80));\n }\n });\n xhr.addEventListener(\"load\", () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`S3 PUT failed: ${xhr.status}`));\n }\n });\n xhr.addEventListener(\"error\", () =>\n reject(new Error(\"S3 PUT network error\")),\n );\n xhr.addEventListener(\"abort\", () => reject(new Error(\"S3 PUT aborted\")));\n xhr.open(\"PUT\", uploadUrl);\n xhr.setRequestHeader(\"Content-Type\", file.type || \"application/zip\");\n xhr.send(file);\n });\n}\n\nasync function completeUpload(\n gql: GqlFetcher,\n sessionId: string,\n folderId: string,\n fileSize: number,\n): Promise<PackageUploadResult> {\n const data = await gql(\n `mutation DevPortal_CompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n file { id downloadUrl }\n }\n }`,\n {\n input: {\n sessionId,\n folderId,\n fileSize: String(fileSize),\n },\n },\n );\n const file = (\n data.completeUpload as\n | { file?: { id?: string; downloadUrl?: string } }\n | undefined\n )?.file;\n if (!file?.id || !file.downloadUrl) {\n throw new Error(\"completeUpload did not return file id or downloadUrl\");\n }\n return { fileId: file.id, downloadUrl: file.downloadUrl };\n}\n\nexport function usePackageUpload(\n workspacesGatewayUrl: string | undefined,\n authToken: string | undefined,\n workspaceId: string | undefined,\n getWorkspaceToken: (() => Promise<string | null>) | undefined,\n folderPath = \"/DevPortal/Packages\",\n) {\n const [state, setState] = useState<PackageUploadState>({\n uploading: false,\n progress: 0,\n error: null,\n });\n\n const upload = useCallback(\n async (file: File): Promise<PackageUploadResult> => {\n if (!workspacesGatewayUrl) {\n throw new Error(\n \"workspacesGatewayUrl is not configured. Pass it as a prop to DevportalRoot.\",\n );\n }\n\n const freshToken = getWorkspaceToken\n ? await getWorkspaceToken()\n : undefined;\n const gql = makeFetcher(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n freshToken ?? undefined,\n );\n\n setState({ uploading: true, progress: 0, error: null });\n try {\n const folderId = await ensureFolder(gql, folderPath);\n setState((s) => ({ ...s, progress: 5 }));\n\n const { sessionId, uploadUrl } = await initiateUpload(\n gql,\n file,\n folderId,\n );\n setState((s) => ({ ...s, progress: 10 }));\n\n await putToPresignedUrl(file, uploadUrl, (pct) =>\n setState((s) => ({ ...s, progress: pct })),\n );\n setState((s) => ({ ...s, progress: 90 }));\n\n const result = await completeUpload(\n gql,\n sessionId,\n folderId,\n file.size,\n );\n setState({ uploading: false, progress: 100, error: null });\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Upload failed\";\n setState({ uploading: false, progress: 0, error: message });\n throw err;\n }\n },\n [workspacesGatewayUrl, authToken, workspaceId, getWorkspaceToken],\n );\n\n return { upload, ...state };\n}\n"],"mappings":";;AA+BA,SAAS,EACP,GACA,GACA,GACA,GACY;AACZ,QAAO,OAAO,GAAO,MAAc;EACjC,IAAM,IAAkC,EACtC,gBAAgB,oBACjB;AAID,EAHI,MAAW,EAAQ,gBAAmB,UAAU,MAChD,MACF,EAAQ,+BAA+B,UAAU,MAC/C,MAAa,EAAQ,oBAAoB;EAE7C,IAAM,IAAW,EAAqB,SAAS,WAAW,GACtD,IACA,GAAG,EAAqB,sBAEtB,IAAM,MAAM,MAAM,GAAU;GAChC,QAAQ;GACR;GACA,aAAa;GACb,MAAM,KAAK,UAAU;IAAE;IAAO;IAAW,CAAC;GAC3C,CAAC;AACF,MAAI,CAAC,EAAI,GACP,OAAU,MACR,2BAA2B,EAAI,OAAO,GAAG,EAAI,aAC9C;EAEH,IAAM,IAAQ,MAAM,EAAI,MAAM;AAI9B,MAAI,EAAK,QAAQ,OACf,OAAU,MAAM,EAAK,OAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK,CAAC;AAE/D,SAAO,EAAK,QAAQ,EAAE;;;AAI1B,eAAe,EAAa,GAAiB,GAA+B;CAO1E,IAAM,KANO,MAAM,EACjB,4GAGA,EAAE,SAAM,CACT,EACgB,oBAAoD;AACrE,KAAI,CAAC,EAAI,OAAU,MAAM,oCAAoC;AAC7D,QAAO;;AAGT,eAAe,EACb,GACA,GACA,GACmD;CACnD,IAAM,IAAM,EAAK,KAAK,SAAS,IAAI,GAC/B,EAAK,KAAK,UAAU,EAAK,KAAK,YAAY,IAAI,GAAG,EAAE,GACnD,OAmBE,KAlBO,MAAM,EACjB,4KAMA,EACE,OAAO;EACL,UAAU,EAAK;EACf,UAAU,OAAO,EAAK,KAAK;EAC3B,UAAU,EAAK,QAAQ;EACvB,WAAW;EACX;EACA,YAAY;EACb,EACF,CACF,EACmB,gBAGd,IAAY,GAAQ,SAAS,IAC7B,IAAY,GAAQ;AAC1B,KAAI,CAAC,KAAa,CAAC,EACjB,OAAU,MAAM,uDAAuD;AAEzE,QAAO;EAAE;EAAW;EAAW;;AAGjC,eAAe,EACb,GACA,GACA,GACe;AACf,OAAM,IAAI,SAAe,GAAS,MAAW;EAC3C,IAAM,IAAM,IAAI,gBAAgB;AAmBhC,EAlBA,EAAI,OAAO,iBAAiB,aAAa,MAAQ;AAC/C,GAAI,EAAI,oBACN,EAAW,KAAK,KAAK,MAAO,EAAI,SAAS,EAAI,QAAS,GAAG,CAAC;IAE5D,EACF,EAAI,iBAAiB,cAAc;AACjC,GAAI,EAAI,UAAU,OAAO,EAAI,SAAS,MACpC,GAAS,GAET,EAAO,gBAAI,MAAM,kBAAkB,EAAI,SAAS,CAAC;IAEnD,EACF,EAAI,iBAAiB,eACnB,EAAO,gBAAI,MAAM,uBAAuB,CAAC,CAC1C,EACD,EAAI,iBAAiB,eAAe,EAAO,gBAAI,MAAM,iBAAiB,CAAC,CAAC,EACxE,EAAI,KAAK,OAAO,EAAU,EAC1B,EAAI,iBAAiB,gBAAgB,EAAK,QAAQ,kBAAkB,EACpE,EAAI,KAAK,EAAK;GACd;;AAGJ,eAAe,EACb,GACA,GACA,GACA,GAC8B;CAe9B,IAAM,KAdO,MAAM,EACjB,iKAKA,EACE,OAAO;EACL;EACA;EACA,UAAU,OAAO,EAAS;EAC3B,EACF,CACF,EAEM,gBAGJ;AACH,KAAI,CAAC,GAAM,MAAM,CAAC,EAAK,YACrB,OAAU,MAAM,uDAAuD;AAEzE,QAAO;EAAE,QAAQ,EAAK;EAAI,aAAa,EAAK;EAAa;;AAG3D,SAAgB,EACd,GACA,GACA,GACA,GACA,IAAa,uBACb;CACA,IAAM,CAAC,GAAO,KAAY,EAA6B;EACrD,WAAW;EACX,UAAU;EACV,OAAO;EACR,CAAC;AAsDF,QAAO;EAAE,QApDM,EACb,OAAO,MAA6C;AAClD,OAAI,CAAC,EACH,OAAU,MACR,8EACD;GAMH,IAAM,IAAM,EACV,GACA,GACA,IANiB,IACf,MAAM,GAAmB,GACzB,KAAA,MAKY,KAAA,EACf;AAED,KAAS;IAAE,WAAW;IAAM,UAAU;IAAG,OAAO;IAAM,CAAC;AACvD,OAAI;IACF,IAAM,IAAW,MAAM,EAAa,GAAK,EAAW;AACpD,OAAU,OAAO;KAAE,GAAG;KAAG,UAAU;KAAG,EAAE;IAExC,IAAM,EAAE,cAAW,iBAAc,MAAM,EACrC,GACA,GACA,EACD;AAMD,IALA,GAAU,OAAO;KAAE,GAAG;KAAG,UAAU;KAAI,EAAE,EAEzC,MAAM,EAAkB,GAAM,IAAY,MACxC,GAAU,OAAO;KAAE,GAAG;KAAG,UAAU;KAAK,EAAE,CAC3C,EACD,GAAU,OAAO;KAAE,GAAG;KAAG,UAAU;KAAI,EAAE;IAEzC,IAAM,IAAS,MAAM,EACnB,GACA,GACA,GACA,EAAK,KACN;AAED,WADA,EAAS;KAAE,WAAW;KAAO,UAAU;KAAK,OAAO;KAAM,CAAC,EACnD;YACA,GAAK;AAGZ,UADA,EAAS;KAAE,WAAW;KAAO,UAAU;KAAG,OAD1B,aAAe,QAAQ,EAAI,UAAU;KACK,CAAC,EACrD;;KAGV;GAAC;GAAsB;GAAW;GAAa;GAAkB,CAClE;EAEgB,GAAG;EAAO"}
|
|
1
|
+
{"version":3,"file":"usePackageUpload.js","names":[],"sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"sourcesContent":["/**\n * usePackageUpload — uploads a zip package to wspace-files-svc and returns a\n * stable downloadUrl to store as ApplicationVersion.packageUrl.\n *\n * Flow (mirrors uploadBlobToFiles in @burdenoff/fe-libs/storage):\n * 1. ensureFolderByPath(\"/DevPortal/Packages\") → folderId\n * 2. initiateUpload → { sessionId, presignedUrl }\n * 3. PUT blob to presignedUrl (progress tracked via XHR)\n * 4. completeUpload → { file.downloadUrl }\n *\n * Uses raw fetch for GraphQL mutations so we can target the workspace gateway\n * without a second ApolloProvider.\n */\nimport { useCallback, useState } from \"react\";\n\nexport interface PackageUploadResult {\n fileId: string;\n downloadUrl: string;\n}\n\nexport interface PackageUploadState {\n uploading: boolean;\n progress: number;\n error: string | null;\n}\n\ntype GqlFetcher = (\n query: string,\n variables: Record<string, unknown>,\n) => Promise<Record<string, unknown>>;\n\nfunction makeFetcher(\n workspacesGatewayUrl: string,\n authToken: string | undefined,\n workspaceId: string | undefined,\n workspaceToken: string | undefined,\n): GqlFetcher {\n return async (query, variables) => {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (authToken) headers[\"Authorization\"] = `Bearer ${authToken}`;\n if (workspaceToken)\n headers[\"X-Workspace-Authorization\"] = `Bearer ${workspaceToken}`;\n if (workspaceId) headers[\"x-workspace-id\"] = workspaceId;\n\n const endpoint = workspacesGatewayUrl.endsWith(\"/graphql\")\n ? workspacesGatewayUrl\n : `${workspacesGatewayUrl}/workspaces/graphql`;\n\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers,\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n if (!res.ok) {\n throw new Error(\n `GraphQL request failed: ${res.status} ${res.statusText}`,\n );\n }\n const json = (await res.json()) as {\n data?: Record<string, unknown>;\n errors?: Array<{ message: string }>;\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data ?? {};\n };\n}\n\nasync function ensureFolder(gql: GqlFetcher, path: string): Promise<string> {\n const data = await gql(\n `mutation DevPortal_EnsureFolder($path: String!) {\n ensureFolderByPath(path: $path) { id }\n }`,\n { path },\n );\n const id = (data.ensureFolderByPath as { id?: string } | undefined)?.id;\n if (!id) throw new Error(\"ensureFolderByPath returned no id\");\n return id;\n}\n\nasync function initiateUpload(\n gql: GqlFetcher,\n file: File,\n folderId: string,\n): Promise<{ sessionId: string; uploadUrl: string }> {\n const ext = file.name.includes(\".\")\n ? file.name.substring(file.name.lastIndexOf(\".\") + 1)\n : \"zip\";\n const data = await gql(\n `mutation DevPortal_InitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }`,\n {\n input: {\n fileName: file.name,\n fileSize: String(file.size),\n mimeType: file.type || \"application/zip\",\n extension: ext,\n folderId,\n visibility: \"PUBLIC\",\n },\n },\n );\n const result = data.initiateUpload as\n { uploadUrl?: string; session?: { id?: string } } | undefined;\n const sessionId = result?.session?.id;\n const uploadUrl = result?.uploadUrl;\n if (!sessionId || !uploadUrl) {\n throw new Error(\"initiateUpload did not return sessionId or uploadUrl\");\n }\n return { sessionId, uploadUrl };\n}\n\nasync function putToPresignedUrl(\n file: File,\n uploadUrl: string,\n onProgress: (pct: number) => void,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.upload.addEventListener(\"progress\", (evt) => {\n if (evt.lengthComputable) {\n onProgress(10 + Math.round((evt.loaded / evt.total) * 80));\n }\n });\n xhr.addEventListener(\"load\", () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`S3 PUT failed: ${xhr.status}`));\n }\n });\n xhr.addEventListener(\"error\", () =>\n reject(new Error(\"S3 PUT network error\")),\n );\n xhr.addEventListener(\"abort\", () => reject(new Error(\"S3 PUT aborted\")));\n xhr.open(\"PUT\", uploadUrl);\n xhr.setRequestHeader(\"Content-Type\", file.type || \"application/zip\");\n xhr.send(file);\n });\n}\n\nasync function completeUpload(\n gql: GqlFetcher,\n sessionId: string,\n folderId: string,\n fileSize: number,\n): Promise<PackageUploadResult> {\n const data = await gql(\n `mutation DevPortal_CompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n file { id downloadUrl }\n }\n }`,\n {\n input: {\n sessionId,\n folderId,\n fileSize: String(fileSize),\n },\n },\n );\n const file = (\n data.completeUpload as\n { file?: { id?: string; downloadUrl?: string } } | undefined\n )?.file;\n if (!file?.id || !file.downloadUrl) {\n throw new Error(\"completeUpload did not return file id or downloadUrl\");\n }\n return { fileId: file.id, downloadUrl: file.downloadUrl };\n}\n\nexport function usePackageUpload(\n workspacesGatewayUrl: string | undefined,\n authToken: string | undefined,\n workspaceId: string | undefined,\n getWorkspaceToken: (() => Promise<string | null>) | undefined,\n folderPath = \"/DevPortal/Packages\",\n) {\n const [state, setState] = useState<PackageUploadState>({\n uploading: false,\n progress: 0,\n error: null,\n });\n\n const upload = useCallback(\n async (file: File): Promise<PackageUploadResult> => {\n if (!workspacesGatewayUrl) {\n throw new Error(\n \"workspacesGatewayUrl is not configured. Pass it as a prop to DevportalRoot.\",\n );\n }\n\n const freshToken = getWorkspaceToken\n ? await getWorkspaceToken()\n : undefined;\n const gql = makeFetcher(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n freshToken ?? undefined,\n );\n\n setState({ uploading: true, progress: 0, error: null });\n try {\n const folderId = await ensureFolder(gql, folderPath);\n setState((s) => ({ ...s, progress: 5 }));\n\n const { sessionId, uploadUrl } = await initiateUpload(\n gql,\n file,\n folderId,\n );\n setState((s) => ({ ...s, progress: 10 }));\n\n await putToPresignedUrl(file, uploadUrl, (pct) =>\n setState((s) => ({ ...s, progress: pct })),\n );\n setState((s) => ({ ...s, progress: 90 }));\n\n const result = await completeUpload(\n gql,\n sessionId,\n folderId,\n file.size,\n );\n setState({ uploading: false, progress: 100, error: null });\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Upload failed\";\n setState({ uploading: false, progress: 0, error: message });\n throw err;\n }\n },\n [workspacesGatewayUrl, authToken, workspaceId, getWorkspaceToken],\n );\n\n return { upload, ...state };\n}\n"],"mappings":"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"}
|
|
@@ -2750,10 +2750,10 @@ var mt = ({ app: t, versionCount: n = 0, isPackageApp: r = !1, pricingDone: i =
|
|
|
2750
2750
|
placeholder: "https://...",
|
|
2751
2751
|
hint: p("devportal.videoUrlHint", "YouTube or Vimeo embed URL")
|
|
2752
2752
|
}),
|
|
2753
|
-
/* @__PURE__ */ U(
|
|
2753
|
+
/* @__PURE__ */ U(se, {
|
|
2754
2754
|
label: p("devportal.shortDescription", "Short Description"),
|
|
2755
2755
|
value: m.shortDescription,
|
|
2756
|
-
onChange: (e) => E("shortDescription", e
|
|
2756
|
+
onChange: (e) => E("shortDescription", e),
|
|
2757
2757
|
maxLength: 200,
|
|
2758
2758
|
rows: 2,
|
|
2759
2759
|
hint: `${m.shortDescription.length}/200 · ${p("devportal.shortDescHint", "Shown in search results and cards")}`
|