@replicated/portal-components 0.0.25 → 0.0.26
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/components/metadata/registry.json +2 -2
- package/components/metadata/registry.md +2 -2
- package/dist/actions/branding-actions.js +6 -4
- package/dist/actions/branding-actions.js.map +1 -1
- package/dist/actions/change-team.js +1 -0
- package/dist/actions/change-team.js.map +1 -1
- package/dist/actions/index.d.mts +1 -1
- package/dist/actions/index.d.ts +1 -1
- package/dist/actions/index.js +1 -50
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/install-actions.d.mts +1 -1
- package/dist/actions/install-actions.d.ts +1 -1
- package/dist/actions/install-actions.js +1 -0
- package/dist/actions/install-actions.js.map +1 -1
- package/dist/actions/invite-actions.js +6 -5
- package/dist/actions/invite-actions.js.map +1 -1
- package/dist/actions/magic-link-actions.js +6 -5
- package/dist/actions/magic-link-actions.js.map +1 -1
- package/dist/actions/service-account.d.mts +1 -1
- package/dist/actions/service-account.d.ts +1 -1
- package/dist/actions/service-account.js +1 -0
- package/dist/actions/service-account.js.map +1 -1
- package/dist/actions/support-bundles.d.mts +1 -1
- package/dist/actions/support-bundles.d.ts +1 -1
- package/dist/actions/support-bundles.js +1 -50
- package/dist/actions/support-bundles.js.map +1 -1
- package/dist/actions/team-settings.d.mts +1 -1
- package/dist/actions/team-settings.d.ts +1 -1
- package/dist/actions/team-settings.js +1 -0
- package/dist/actions/team-settings.js.map +1 -1
- package/dist/actions/trial-signup-actions.js +6 -5
- package/dist/actions/trial-signup-actions.js.map +1 -1
- package/dist/actions/trial-signup.js +6 -5
- package/dist/actions/trial-signup.js.map +1 -1
- package/dist/actions/user-settings.d.mts +1 -1
- package/dist/actions/user-settings.d.ts +1 -1
- package/dist/actions/user-settings.js +1 -0
- package/dist/actions/user-settings.js.map +1 -1
- package/dist/airgap-instances.d.mts +1 -1
- package/dist/airgap-instances.d.ts +1 -1
- package/dist/airgap-instances.js +25 -25
- package/dist/airgap-instances.js.map +1 -1
- package/dist/{saml-handlers.d.mts → api/saml.d.mts} +2 -2
- package/dist/{saml-handlers.d.ts → api/saml.d.ts} +2 -2
- package/dist/{saml-handlers.js → api/saml.js} +4 -3
- package/dist/api/saml.js.map +1 -0
- package/dist/api/support-bundles.d.mts +22 -0
- package/dist/api/support-bundles.d.ts +22 -0
- package/dist/api/support-bundles.js +245 -0
- package/dist/api/support-bundles.js.map +1 -0
- package/dist/{branding-BsMSywts.d.mts → branding-DmsrDTNE.d.mts} +2 -1
- package/dist/{branding-BsMSywts.d.ts → branding-DmsrDTNE.d.ts} +2 -1
- package/dist/esm/actions/branding-actions.js +6 -4
- package/dist/esm/actions/branding-actions.js.map +1 -1
- package/dist/esm/actions/change-team.js +1 -0
- package/dist/esm/actions/change-team.js.map +1 -1
- package/dist/esm/actions/index.js +2 -50
- package/dist/esm/actions/index.js.map +1 -1
- package/dist/esm/actions/install-actions.js +1 -0
- package/dist/esm/actions/install-actions.js.map +1 -1
- package/dist/esm/actions/invite-actions.js +6 -5
- package/dist/esm/actions/invite-actions.js.map +1 -1
- package/dist/esm/actions/magic-link-actions.js +6 -5
- package/dist/esm/actions/magic-link-actions.js.map +1 -1
- package/dist/esm/actions/service-account.js +1 -0
- package/dist/esm/actions/service-account.js.map +1 -1
- package/dist/esm/actions/support-bundles.js +2 -50
- package/dist/esm/actions/support-bundles.js.map +1 -1
- package/dist/esm/actions/team-settings.js +1 -0
- package/dist/esm/actions/team-settings.js.map +1 -1
- package/dist/esm/actions/trial-signup-actions.js +6 -5
- package/dist/esm/actions/trial-signup-actions.js.map +1 -1
- package/dist/esm/actions/trial-signup.js +6 -5
- package/dist/esm/actions/trial-signup.js.map +1 -1
- package/dist/esm/actions/user-settings.js +1 -0
- package/dist/esm/actions/user-settings.js.map +1 -1
- package/dist/esm/airgap-instances.js +25 -25
- package/dist/esm/airgap-instances.js.map +1 -1
- package/dist/esm/{saml-handlers.js → api/saml.js} +4 -3
- package/dist/esm/api/saml.js.map +1 -0
- package/dist/esm/api/support-bundles.js +243 -0
- package/dist/esm/api/support-bundles.js.map +1 -0
- package/dist/esm/helm-install-wizard.js +1 -0
- package/dist/esm/helm-install-wizard.js.map +1 -1
- package/dist/esm/index.js +262 -215
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/install-actions.js +1 -0
- package/dist/esm/install-actions.js.map +1 -1
- package/dist/esm/instance-card.js +25 -25
- package/dist/esm/instance-card.js.map +1 -1
- package/dist/esm/license-details.js +1 -0
- package/dist/esm/license-details.js.map +1 -1
- package/dist/esm/linux-install-wizard.js +1 -0
- package/dist/esm/linux-install-wizard.js.map +1 -1
- package/dist/esm/online-instance-list.js +25 -25
- package/dist/esm/online-instance-list.js.map +1 -1
- package/dist/esm/saml-callback-client.js.map +1 -1
- package/dist/esm/support-card.js +1 -0
- package/dist/esm/support-card.js.map +1 -1
- package/dist/esm/top-nav.js +82 -79
- package/dist/esm/top-nav.js.map +1 -1
- package/dist/esm/update-layout.js +82 -79
- package/dist/esm/update-layout.js.map +1 -1
- package/dist/esm/upload-support-bundle-modal.js +46 -16
- package/dist/esm/upload-support-bundle-modal.js.map +1 -1
- package/dist/esm/utils/index.js +48 -1
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/helm-install-wizard.d.mts +2 -2
- package/dist/helm-install-wizard.d.ts +2 -2
- package/dist/helm-install-wizard.js +1 -0
- package/dist/helm-install-wizard.js.map +1 -1
- package/dist/{index-DXy7RxOX.d.ts → index-Bcp17Mf3.d.ts} +1 -16
- package/dist/{index-JX9-CIMA.d.mts → index-DaH1bSuO.d.mts} +1 -16
- package/dist/index.d.mts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +262 -214
- package/dist/index.js.map +1 -1
- package/dist/install-actions.d.mts +2 -2
- package/dist/install-actions.d.ts +2 -2
- package/dist/install-actions.js +1 -0
- package/dist/install-actions.js.map +1 -1
- package/dist/instance-card.d.mts +1 -1
- package/dist/instance-card.d.ts +1 -1
- package/dist/instance-card.js +25 -25
- package/dist/instance-card.js.map +1 -1
- package/dist/license-details.js +1 -0
- package/dist/license-details.js.map +1 -1
- package/dist/linux-install-wizard.d.mts +2 -2
- package/dist/linux-install-wizard.d.ts +2 -2
- package/dist/linux-install-wizard.js +1 -0
- package/dist/linux-install-wizard.js.map +1 -1
- package/dist/online-instance-list.d.mts +1 -1
- package/dist/online-instance-list.d.ts +1 -1
- package/dist/online-instance-list.js +25 -25
- package/dist/online-instance-list.js.map +1 -1
- package/dist/pending-installations.d.mts +1 -1
- package/dist/pending-installations.d.ts +1 -1
- package/dist/saml-callback-client.d.mts +1 -1
- package/dist/saml-callback-client.d.ts +1 -1
- package/dist/saml-callback-client.js.map +1 -1
- package/dist/security-card.d.mts +1 -1
- package/dist/security-card.d.ts +1 -1
- package/dist/styles.css +4 -0
- package/dist/support-bundles-card.d.mts +1 -1
- package/dist/support-bundles-card.d.ts +1 -1
- package/dist/support-card.js +1 -0
- package/dist/support-card.js.map +1 -1
- package/dist/{top-nav-CEqw0KpO.d.ts → top-nav-8f2U69MF.d.ts} +1 -1
- package/dist/{top-nav-BUQAGoG1.d.mts → top-nav-B2yA3PC7.d.mts} +1 -1
- package/dist/top-nav.d.mts +2 -2
- package/dist/top-nav.d.ts +2 -2
- package/dist/top-nav.js +82 -79
- package/dist/top-nav.js.map +1 -1
- package/dist/update-layout.js +82 -79
- package/dist/update-layout.js.map +1 -1
- package/dist/upload-support-bundle-modal.d.mts +9 -9
- package/dist/upload-support-bundle-modal.d.ts +9 -9
- package/dist/upload-support-bundle-modal.js +46 -16
- package/dist/upload-support-bundle-modal.js.map +1 -1
- package/dist/upload-with-progress-D760HthX.d.mts +35 -0
- package/dist/upload-with-progress-D760HthX.d.ts +35 -0
- package/dist/utils/index.d.mts +3 -2
- package/dist/utils/index.d.ts +3 -2
- package/dist/utils/index.js +48 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +10 -5
- package/dist/esm/saml-handlers.js.map +0 -1
- package/dist/saml-handlers.js.map +0 -1
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { U as UploadSupportBundleResult } from './upload-with-progress-D760HthX.mjs';
|
|
2
3
|
|
|
3
4
|
interface UploadSupportBundleModalProps {
|
|
4
5
|
isOpen: boolean;
|
|
5
6
|
onClose: () => void;
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}>;
|
|
7
|
+
/**
|
|
8
|
+
* Upload function with progress tracking
|
|
9
|
+
* onProgress receives percentage (0-100)
|
|
10
|
+
* signal is an AbortSignal for cancellation
|
|
11
|
+
*/
|
|
12
|
+
uploadWithProgress: (file: File, appId: string, onProgress: (percent: number) => void, signal: AbortSignal) => Promise<UploadSupportBundleResult>;
|
|
13
13
|
/** Callback after successful upload - performs async polling to refresh bundle list */
|
|
14
14
|
onUploadSuccess: () => Promise<void>;
|
|
15
15
|
appId: string;
|
|
16
16
|
primaryColor?: string;
|
|
17
17
|
}
|
|
18
18
|
declare const UploadSupportBundleModal: {
|
|
19
|
-
({ isOpen, onClose,
|
|
19
|
+
({ isOpen, onClose, uploadWithProgress, onUploadSuccess, appId, primaryColor }: UploadSupportBundleModalProps): react_jsx_runtime.JSX.Element | null;
|
|
20
20
|
displayName: string;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
export { UploadSupportBundleModal };
|
|
23
|
+
export { UploadSupportBundleModal, UploadSupportBundleResult };
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { U as UploadSupportBundleResult } from './upload-with-progress-D760HthX.js';
|
|
2
3
|
|
|
3
4
|
interface UploadSupportBundleModalProps {
|
|
4
5
|
isOpen: boolean;
|
|
5
6
|
onClose: () => void;
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}>;
|
|
7
|
+
/**
|
|
8
|
+
* Upload function with progress tracking
|
|
9
|
+
* onProgress receives percentage (0-100)
|
|
10
|
+
* signal is an AbortSignal for cancellation
|
|
11
|
+
*/
|
|
12
|
+
uploadWithProgress: (file: File, appId: string, onProgress: (percent: number) => void, signal: AbortSignal) => Promise<UploadSupportBundleResult>;
|
|
13
13
|
/** Callback after successful upload - performs async polling to refresh bundle list */
|
|
14
14
|
onUploadSuccess: () => Promise<void>;
|
|
15
15
|
appId: string;
|
|
16
16
|
primaryColor?: string;
|
|
17
17
|
}
|
|
18
18
|
declare const UploadSupportBundleModal: {
|
|
19
|
-
({ isOpen, onClose,
|
|
19
|
+
({ isOpen, onClose, uploadWithProgress, onUploadSuccess, appId, primaryColor }: UploadSupportBundleModalProps): react_jsx_runtime.JSX.Element | null;
|
|
20
20
|
displayName: string;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
export { UploadSupportBundleModal };
|
|
23
|
+
export { UploadSupportBundleModal, UploadSupportBundleResult };
|
|
@@ -24,20 +24,27 @@ var DEFAULT_PRIMARY_COLOR = "#4f46e5";
|
|
|
24
24
|
var UploadSupportBundleModal = ({
|
|
25
25
|
isOpen,
|
|
26
26
|
onClose,
|
|
27
|
-
|
|
27
|
+
uploadWithProgress,
|
|
28
28
|
onUploadSuccess,
|
|
29
29
|
appId,
|
|
30
30
|
primaryColor = DEFAULT_PRIMARY_COLOR
|
|
31
31
|
}) => {
|
|
32
32
|
const [selectedFile, setSelectedFile] = react.useState(null);
|
|
33
33
|
const [isUploading, setIsUploading] = react.useState(false);
|
|
34
|
+
const [uploadProgress, setUploadProgress] = react.useState(0);
|
|
34
35
|
const [error, setError] = react.useState(null);
|
|
36
|
+
const [abortController, setAbortController] = react.useState(null);
|
|
35
37
|
const fileInputRef = react.useRef(null);
|
|
36
38
|
react.useEffect(() => {
|
|
37
39
|
if (!isOpen) {
|
|
38
40
|
setSelectedFile(null);
|
|
39
41
|
setIsUploading(false);
|
|
42
|
+
setUploadProgress(0);
|
|
40
43
|
setError(null);
|
|
44
|
+
setAbortController((prev) => {
|
|
45
|
+
prev?.abort();
|
|
46
|
+
return null;
|
|
47
|
+
});
|
|
41
48
|
}
|
|
42
49
|
}, [isOpen]);
|
|
43
50
|
const handleFileChange = react.useCallback((event) => {
|
|
@@ -54,30 +61,38 @@ var UploadSupportBundleModal = ({
|
|
|
54
61
|
if (!selectedFile || !appId) return;
|
|
55
62
|
setError(null);
|
|
56
63
|
setIsUploading(true);
|
|
64
|
+
setUploadProgress(0);
|
|
57
65
|
try {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
const controller = new AbortController();
|
|
67
|
+
setAbortController(controller);
|
|
68
|
+
const result = await uploadWithProgress(
|
|
69
|
+
selectedFile,
|
|
70
|
+
appId,
|
|
71
|
+
setUploadProgress,
|
|
72
|
+
controller.signal
|
|
73
|
+
);
|
|
62
74
|
if (!result.success) {
|
|
63
75
|
throw new Error(result.error ?? "Failed to upload support bundle");
|
|
64
76
|
}
|
|
65
77
|
await onUploadSuccess();
|
|
66
78
|
onClose();
|
|
67
79
|
} catch (err) {
|
|
68
|
-
|
|
69
|
-
|
|
80
|
+
if (err instanceof Error && err.message !== "Upload cancelled") {
|
|
81
|
+
console.error("Upload error:", err);
|
|
82
|
+
setError(err.message);
|
|
83
|
+
}
|
|
70
84
|
} finally {
|
|
71
85
|
setIsUploading(false);
|
|
86
|
+
setUploadProgress(0);
|
|
87
|
+
setAbortController(null);
|
|
72
88
|
}
|
|
73
|
-
}, [selectedFile, appId,
|
|
89
|
+
}, [selectedFile, appId, uploadWithProgress, onUploadSuccess, onClose]);
|
|
74
90
|
const handleCancel = react.useCallback(() => {
|
|
75
|
-
if (isUploading) {
|
|
76
|
-
|
|
77
|
-
return;
|
|
91
|
+
if (isUploading && abortController) {
|
|
92
|
+
abortController.abort();
|
|
78
93
|
}
|
|
79
94
|
onClose();
|
|
80
|
-
}, [isUploading, onClose]);
|
|
95
|
+
}, [isUploading, abortController, onClose]);
|
|
81
96
|
const handleBackdropClick = react.useCallback((e) => {
|
|
82
97
|
if (e.target === e.currentTarget && !isUploading) {
|
|
83
98
|
onClose();
|
|
@@ -113,9 +128,24 @@ var UploadSupportBundleModal = ({
|
|
|
113
128
|
] })
|
|
114
129
|
] }),
|
|
115
130
|
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 rounded-lg bg-red-50 px-3 py-2 text-sm text-red-700", children: error }),
|
|
116
|
-
isUploading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 mb-6
|
|
117
|
-
/* @__PURE__ */ jsxRuntime.
|
|
118
|
-
|
|
131
|
+
isUploading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 mb-6", children: [
|
|
132
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
133
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-700", children: "Uploading support bundle..." }),
|
|
134
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-medium text-gray-900", children: [
|
|
135
|
+
uploadProgress,
|
|
136
|
+
"%"
|
|
137
|
+
] })
|
|
138
|
+
] }),
|
|
139
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full bg-gray-200 rounded-full h-2", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
140
|
+
"div",
|
|
141
|
+
{
|
|
142
|
+
className: "h-2 rounded-full transition-all duration-300",
|
|
143
|
+
style: {
|
|
144
|
+
width: `${uploadProgress}%`,
|
|
145
|
+
backgroundColor: primaryColor || "#3B82F6"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
) })
|
|
119
149
|
] }),
|
|
120
150
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 flex justify-end gap-3", children: [
|
|
121
151
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -124,7 +154,7 @@ var UploadSupportBundleModal = ({
|
|
|
124
154
|
type: "button",
|
|
125
155
|
onClick: handleCancel,
|
|
126
156
|
className: "rounded-lg px-4 py-2 text-sm font-medium text-gray-600 transition hover:bg-gray-100",
|
|
127
|
-
children: isUploading ? "Cancel Upload" : "Cancel"
|
|
157
|
+
children: isUploading && abortController ? "Cancel Upload" : "Cancel"
|
|
128
158
|
}
|
|
129
159
|
),
|
|
130
160
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/format.ts","../src/utils/constants.ts","../src/components/upload-support-bundle-modal.tsx"],"names":["useState","useRef","useEffect","useCallback","jsx","jsxs"],"mappings":"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","file":"upload-support-bundle-modal.js","sourcesContent":["/**\n * Format bytes to human-readable string\n * @param bytes - Number of bytes\n * @param decimals - Number of decimal places (default: 1)\n * @returns Formatted string (e.g., \"1.5 MB\")\n */\nexport function formatBytes(bytes: number, decimals = 1): string {\n if (bytes === 0) return \"0 Bytes\";\n\n const k = 1024;\n const dm = decimals < 0 ? 0 : decimals;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\"];\n\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;\n}\n\n/**\n * Format date string to short numeric date (no time)\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"01/27/2026\") or \"Never\" if no date\n */\nexport function formatDateShort(dateString?: string | null): string {\n if (!dateString) return \"Never\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\"\n });\n}\n\n/**\n * Format date string to human-readable local timestamp\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"Jan 27, 2026, 10:32 PM\") or \"Never\" if no date\n */\nexport function formatDate(dateString?: string | null): string {\n if (!dateString) return \"Never\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true\n });\n}\n\n/**\n * Format date string to human-readable UTC timestamp\n * @param dateString - ISO date string\n * @returns Formatted string (e.g., \"01/27/2026, 22:32:39 UTC\")\n */\nexport function formatDateTime(dateString: string): string {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleString(\"en-US\", {\n timeZone: \"UTC\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false\n }) + \" UTC\";\n}\n\n/**\n * Format date string to user-friendly local timestamp with 12-hour time\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"1/27/2026, 10:32 PM\") or \"N/A\" if not provided\n */\nexport function formatDateTimeLocal(dateString?: string | null): string {\n if (!dateString) return \"N/A\";\n\n try {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n month: \"numeric\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true\n });\n } catch {\n return \"N/A\";\n }\n}\n","/**\n * Default globe favicon matching the CiGlobe icon (Circum Icons)\n * Used as fallback when custom branding doesn't provide a favicon\n */\nexport const DEFAULT_FAVICON = \"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='1.5'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cellipse cx='12' cy='12' rx='4' ry='10'/%3E%3Cpath d='M2 12h20'/%3E%3C/svg%3E\";\n\n/**\n * Default primary brand color\n */\nexport const DEFAULT_PRIMARY_COLOR = \"#4f46e5\";\n\n/**\n * Default secondary brand color\n */\nexport const DEFAULT_SECONDARY_COLOR = \"#6366f1\";\n\n/**\n * Check if the API origin is HTTP (used for repldev environments)\n * This determines cookie security settings for cross-origin iframes\n * @returns true if the API origin starts with http:// (not https://)\n */\nexport const isHttpApiOrigin = (): boolean => {\n return process.env.REPLICATED_APP_ORIGIN?.startsWith('http://') || false;\n};\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { formatBytes } from \"../utils/format\";\nimport { DEFAULT_PRIMARY_COLOR } from \"../utils/constants\";\n\ninterface UploadSupportBundleModalProps {\n isOpen: boolean;\n onClose: () => void;\n /** Server action to upload the support bundle */\n uploadSupportBundle: (formData: FormData) => Promise<{ \n success: boolean; \n error?: string; \n bundleId?: string;\n bundleSlug?: string;\n }>;\n /** Callback after successful upload - performs async polling to refresh bundle list */\n onUploadSuccess: () => Promise<void>;\n appId: string;\n primaryColor?: string;\n}\n\nexport const UploadSupportBundleModal = ({\n isOpen,\n onClose,\n uploadSupportBundle,\n onUploadSuccess,\n appId,\n primaryColor = DEFAULT_PRIMARY_COLOR\n}: UploadSupportBundleModalProps) => {\n const [selectedFile, setSelectedFile] = useState<File | null>(null);\n const [isUploading, setIsUploading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // Reset state when modal opens/closes\n useEffect(() => {\n if (!isOpen) {\n setSelectedFile(null);\n setIsUploading(false);\n setError(null);\n }\n }, [isOpen]);\n\n const handleFileChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n if (!file) return;\n\n if (!file.name.endsWith(\".tgz\") && !file.name.endsWith(\".tar.gz\")) {\n setError(\"Invalid file type. Please select a .tgz or .tar.gz file.\");\n return;\n }\n\n setError(null);\n setSelectedFile(file);\n }, []);\n\n const handleUpload = useCallback(async () => {\n if (!selectedFile || !appId) return;\n\n setError(null);\n setIsUploading(true);\n\n try {\n // Create FormData to send to server action\n const formData = new FormData();\n formData.append(\"file\", selectedFile);\n formData.append(\"appId\", appId);\n\n // Upload via server action (streams to Enterprise Portal API)\n const result = await uploadSupportBundle(formData);\n\n if (!result.success) {\n throw new Error(result.error ?? \"Failed to upload support bundle\");\n }\n\n // Success - notify parent and wait for polling to complete\n await onUploadSuccess();\n onClose();\n } catch (err) {\n console.error(\"Upload error:\", err);\n setError(err instanceof Error ? err.message : \"Upload failed\");\n } finally {\n setIsUploading(false);\n }\n }, [selectedFile, appId, uploadSupportBundle, onUploadSuccess, onClose]);\n\n const handleCancel = useCallback(() => {\n // Note: Cannot abort server action once started\n if (isUploading) {\n setError(\"Upload cannot be cancelled once started\");\n return;\n }\n onClose();\n }, [isUploading, onClose]);\n\n const handleBackdropClick = useCallback((e: React.MouseEvent) => {\n if (e.target === e.currentTarget && !isUploading) {\n onClose();\n }\n }, [isUploading, onClose]);\n\n if (!isOpen) return null;\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50\"\n onClick={handleBackdropClick}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"upload-modal-title\"\n >\n <div className=\"w-full max-w-md rounded-2xl bg-white p-6 shadow-xl\">\n <h2 id=\"upload-modal-title\" className=\"text-lg font-semibold text-gray-900\">\n Upload Support Bundle\n </h2>\n\n <div className=\"mt-4\">\n <label className=\"block text-sm font-medium text-gray-700\">\n Select Support Bundle\n </label>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".tgz,.tar.gz,application/gzip,application/x-gzip\"\n onChange={handleFileChange}\n disabled={isUploading}\n className=\"mt-2 block w-full text-sm text-gray-500 file:mr-4 file:rounded-lg file:border-0 file:bg-gray-100 file:px-4 file:py-2 file:text-sm file:font-medium file:text-gray-700 hover:file:bg-gray-200 disabled:opacity-50\"\n />\n {selectedFile && (\n <div className=\"mt-3 flex items-center justify-between rounded-lg bg-gray-50 px-3 py-2 text-sm\">\n <span className=\"truncate text-gray-700\">{selectedFile.name}</span>\n <span className=\"ml-2 shrink-0 text-gray-500\">{formatBytes(selectedFile.size)}</span>\n </div>\n )}\n </div>\n\n {error && (\n <div className=\"mt-4 rounded-lg bg-red-50 px-3 py-2 text-sm text-red-700\">\n {error}\n </div>\n )}\n\n {isUploading && (\n <div className=\"mt-6 mb-6 flex items-center justify-center gap-3\">\n <div className=\"h-5 w-5 animate-spin rounded-full border-2 border-gray-300 border-t-transparent\" style={{ borderTopColor: primaryColor }} />\n <span className=\"text-sm font-medium text-gray-700\">\n Uploading support bundle...\n </span>\n </div>\n )}\n\n <div className=\"mt-6 flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"rounded-lg px-4 py-2 text-sm font-medium text-gray-600 transition hover:bg-gray-100\"\n >\n {isUploading ? \"Cancel Upload\" : \"Cancel\"}\n </button>\n <button\n type=\"button\"\n onClick={handleUpload}\n disabled={!selectedFile || isUploading}\n className=\"rounded-lg px-4 py-2 text-sm font-medium text-white transition-opacity duration-200 hover:opacity-90 disabled:opacity-50\"\n style={{ backgroundColor: primaryColor }}\n >\n {isUploading ? \"Uploading...\" : \"Upload\"}\n </button>\n </div>\n </div>\n </div>\n );\n};\n\nUploadSupportBundleModal.displayName = \"UploadSupportBundleModal\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/format.ts","../src/utils/constants.ts","../src/components/upload-support-bundle-modal.tsx"],"names":["useState","useRef","useEffect","useCallback","jsx","jsxs"],"mappings":"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","file":"upload-support-bundle-modal.js","sourcesContent":["/**\n * Format bytes to human-readable string\n * @param bytes - Number of bytes\n * @param decimals - Number of decimal places (default: 1)\n * @returns Formatted string (e.g., \"1.5 MB\")\n */\nexport function formatBytes(bytes: number, decimals = 1): string {\n if (bytes === 0) return \"0 Bytes\";\n\n const k = 1024;\n const dm = decimals < 0 ? 0 : decimals;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\"];\n\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;\n}\n\n/**\n * Format date string to short numeric date (no time)\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"01/27/2026\") or \"Never\" if no date\n */\nexport function formatDateShort(dateString?: string | null): string {\n if (!dateString) return \"Never\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\"\n });\n}\n\n/**\n * Format date string to human-readable local timestamp\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"Jan 27, 2026, 10:32 PM\") or \"Never\" if no date\n */\nexport function formatDate(dateString?: string | null): string {\n if (!dateString) return \"Never\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true\n });\n}\n\n/**\n * Format date string to human-readable UTC timestamp\n * @param dateString - ISO date string\n * @returns Formatted string (e.g., \"01/27/2026, 22:32:39 UTC\")\n */\nexport function formatDateTime(dateString: string): string {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleString(\"en-US\", {\n timeZone: \"UTC\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false\n }) + \" UTC\";\n}\n\n/**\n * Format date string to user-friendly local timestamp with 12-hour time\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"1/27/2026, 10:32 PM\") or \"N/A\" if not provided\n */\nexport function formatDateTimeLocal(dateString?: string | null): string {\n if (!dateString) return \"N/A\";\n\n try {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n month: \"numeric\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true\n });\n } catch {\n return \"N/A\";\n }\n}\n","/**\n * Default globe favicon matching the CiGlobe icon (Circum Icons)\n * Used as fallback when custom branding doesn't provide a favicon\n */\nexport const DEFAULT_FAVICON = \"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='1.5'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cellipse cx='12' cy='12' rx='4' ry='10'/%3E%3Cpath d='M2 12h20'/%3E%3C/svg%3E\";\n\n/**\n * Default primary brand color\n */\nexport const DEFAULT_PRIMARY_COLOR = \"#4f46e5\";\n\n/**\n * Default secondary brand color\n */\nexport const DEFAULT_SECONDARY_COLOR = \"#6366f1\";\n\n/**\n * Check if the API origin is HTTP (used for repldev environments)\n * This determines cookie security settings for cross-origin iframes\n * @returns true if the API origin starts with http:// (not https://)\n */\nexport const isHttpApiOrigin = (): boolean => {\n return process.env.REPLICATED_APP_ORIGIN?.startsWith('http://') || false;\n};\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { formatBytes } from \"../utils/format\";\nimport { DEFAULT_PRIMARY_COLOR } from \"../utils/constants\";\nimport type { UploadSupportBundleResult } from \"../utils/upload-with-progress\";\n\ninterface UploadSupportBundleModalProps {\n isOpen: boolean;\n onClose: () => void;\n /**\n * Upload function with progress tracking\n * onProgress receives percentage (0-100)\n * signal is an AbortSignal for cancellation\n */\n uploadWithProgress: (\n file: File,\n appId: string,\n onProgress: (percent: number) => void,\n signal: AbortSignal\n ) => Promise<UploadSupportBundleResult>;\n /** Callback after successful upload - performs async polling to refresh bundle list */\n onUploadSuccess: () => Promise<void>;\n appId: string;\n primaryColor?: string;\n}\n\nexport const UploadSupportBundleModal = ({\n isOpen,\n onClose,\n uploadWithProgress,\n onUploadSuccess,\n appId,\n primaryColor = DEFAULT_PRIMARY_COLOR\n}: UploadSupportBundleModalProps) => {\n const [selectedFile, setSelectedFile] = useState<File | null>(null);\n const [isUploading, setIsUploading] = useState(false);\n const [uploadProgress, setUploadProgress] = useState<number>(0);\n const [error, setError] = useState<string | null>(null);\n const [abortController, setAbortController] = useState<AbortController | null>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // Reset state when modal opens/closes\n useEffect(() => {\n if (!isOpen) {\n setSelectedFile(null);\n setIsUploading(false);\n setUploadProgress(0);\n setError(null);\n // Abort any in-flight upload before clearing controller\n setAbortController(prev => {\n prev?.abort();\n return null;\n });\n }\n }, [isOpen]);\n\n const handleFileChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n if (!file) return;\n\n if (!file.name.endsWith(\".tgz\") && !file.name.endsWith(\".tar.gz\")) {\n setError(\"Invalid file type. Please select a .tgz or .tar.gz file.\");\n return;\n }\n\n setError(null);\n setSelectedFile(file);\n }, []);\n\n const handleUpload = useCallback(async () => {\n if (!selectedFile || !appId) return;\n\n setError(null);\n setIsUploading(true);\n setUploadProgress(0);\n\n try {\n // Upload with progress tracking\n const controller = new AbortController();\n setAbortController(controller);\n\n const result = await uploadWithProgress(\n selectedFile,\n appId,\n setUploadProgress,\n controller.signal\n );\n\n if (!result.success) {\n throw new Error(result.error ?? \"Failed to upload support bundle\");\n }\n\n // Success - notify parent and wait for polling to complete\n await onUploadSuccess();\n onClose();\n } catch (err) {\n if (err instanceof Error && err.message !== \"Upload cancelled\") {\n console.error(\"Upload error:\", err);\n setError(err.message);\n }\n } finally {\n setIsUploading(false);\n setUploadProgress(0);\n setAbortController(null);\n }\n }, [selectedFile, appId, uploadWithProgress, onUploadSuccess, onClose]);\n\n const handleCancel = useCallback(() => {\n if (isUploading && abortController) {\n abortController.abort();\n }\n onClose();\n }, [isUploading, abortController, onClose]);\n\n const handleBackdropClick = useCallback((e: React.MouseEvent) => {\n if (e.target === e.currentTarget && !isUploading) {\n onClose();\n }\n }, [isUploading, onClose]);\n\n if (!isOpen) return null;\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50\"\n onClick={handleBackdropClick}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"upload-modal-title\"\n >\n <div className=\"w-full max-w-md rounded-2xl bg-white p-6 shadow-xl\">\n <h2 id=\"upload-modal-title\" className=\"text-lg font-semibold text-gray-900\">\n Upload Support Bundle\n </h2>\n\n <div className=\"mt-4\">\n <label className=\"block text-sm font-medium text-gray-700\">\n Select Support Bundle\n </label>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".tgz,.tar.gz,application/gzip,application/x-gzip\"\n onChange={handleFileChange}\n disabled={isUploading}\n className=\"mt-2 block w-full text-sm text-gray-500 file:mr-4 file:rounded-lg file:border-0 file:bg-gray-100 file:px-4 file:py-2 file:text-sm file:font-medium file:text-gray-700 hover:file:bg-gray-200 disabled:opacity-50\"\n />\n {selectedFile && (\n <div className=\"mt-3 flex items-center justify-between rounded-lg bg-gray-50 px-3 py-2 text-sm\">\n <span className=\"truncate text-gray-700\">{selectedFile.name}</span>\n <span className=\"ml-2 shrink-0 text-gray-500\">{formatBytes(selectedFile.size)}</span>\n </div>\n )}\n </div>\n\n {error && (\n <div className=\"mt-4 rounded-lg bg-red-50 px-3 py-2 text-sm text-red-700\">\n {error}\n </div>\n )}\n\n {isUploading && (\n <div className=\"mt-6 mb-6\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm font-medium text-gray-700\">\n Uploading support bundle...\n </span>\n <span className=\"text-sm font-medium text-gray-900\">\n {uploadProgress}%\n </span>\n </div>\n <div className=\"w-full bg-gray-200 rounded-full h-2\">\n <div\n className=\"h-2 rounded-full transition-all duration-300\"\n style={{\n width: `${uploadProgress}%`,\n backgroundColor: primaryColor || \"#3B82F6\",\n }}\n />\n </div>\n </div>\n )}\n\n <div className=\"mt-6 flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"rounded-lg px-4 py-2 text-sm font-medium text-gray-600 transition hover:bg-gray-100\"\n >\n {isUploading && abortController ? \"Cancel Upload\" : \"Cancel\"}\n </button>\n <button\n type=\"button\"\n onClick={handleUpload}\n disabled={!selectedFile || isUploading}\n className=\"rounded-lg px-4 py-2 text-sm font-medium text-white transition-opacity duration-200 hover:opacity-90 disabled:opacity-50\"\n style={{ backgroundColor: primaryColor }}\n >\n {isUploading ? \"Uploading...\" : \"Upload\"}\n </button>\n </div>\n </div>\n </div>\n );\n};\n\nUploadSupportBundleModal.displayName = \"UploadSupportBundleModal\";\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side upload function with progress tracking and streaming using XMLHttpRequest
|
|
3
|
+
*
|
|
4
|
+
* This sends the file directly (not as FormData) to avoid proxy issues and enable true streaming.
|
|
5
|
+
* The file is sent with Content-Type: application/gzip and appId as a query parameter.
|
|
6
|
+
*
|
|
7
|
+
* @param file - The file to upload
|
|
8
|
+
* @param appId - The application ID
|
|
9
|
+
* @param onProgress - Callback that receives progress percentage (0-100)
|
|
10
|
+
* @param signal - AbortSignal for cancellation
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* import { uploadSupportBundleWithProgress } from "@replicated/portal-components/utils";
|
|
15
|
+
*
|
|
16
|
+
* <UploadSupportBundleModal
|
|
17
|
+
* uploadWithProgress={uploadSupportBundleWithProgress}
|
|
18
|
+
* // ... other props
|
|
19
|
+
* />
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Note: This requires a corresponding API route in your Next.js app at:
|
|
23
|
+
* /api/support-bundles/upload
|
|
24
|
+
*
|
|
25
|
+
* The API route should stream the request body directly to the backend without buffering.
|
|
26
|
+
*/
|
|
27
|
+
interface UploadSupportBundleResult {
|
|
28
|
+
success: boolean;
|
|
29
|
+
error?: string;
|
|
30
|
+
bundleId?: string;
|
|
31
|
+
bundleSlug?: string;
|
|
32
|
+
}
|
|
33
|
+
declare function uploadSupportBundleWithProgress(file: File, appId: string, onProgress: (percent: number) => void, signal: AbortSignal): Promise<UploadSupportBundleResult>;
|
|
34
|
+
|
|
35
|
+
export { type UploadSupportBundleResult as U, uploadSupportBundleWithProgress as u };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side upload function with progress tracking and streaming using XMLHttpRequest
|
|
3
|
+
*
|
|
4
|
+
* This sends the file directly (not as FormData) to avoid proxy issues and enable true streaming.
|
|
5
|
+
* The file is sent with Content-Type: application/gzip and appId as a query parameter.
|
|
6
|
+
*
|
|
7
|
+
* @param file - The file to upload
|
|
8
|
+
* @param appId - The application ID
|
|
9
|
+
* @param onProgress - Callback that receives progress percentage (0-100)
|
|
10
|
+
* @param signal - AbortSignal for cancellation
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* import { uploadSupportBundleWithProgress } from "@replicated/portal-components/utils";
|
|
15
|
+
*
|
|
16
|
+
* <UploadSupportBundleModal
|
|
17
|
+
* uploadWithProgress={uploadSupportBundleWithProgress}
|
|
18
|
+
* // ... other props
|
|
19
|
+
* />
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Note: This requires a corresponding API route in your Next.js app at:
|
|
23
|
+
* /api/support-bundles/upload
|
|
24
|
+
*
|
|
25
|
+
* The API route should stream the request body directly to the backend without buffering.
|
|
26
|
+
*/
|
|
27
|
+
interface UploadSupportBundleResult {
|
|
28
|
+
success: boolean;
|
|
29
|
+
error?: string;
|
|
30
|
+
bundleId?: string;
|
|
31
|
+
bundleSlug?: string;
|
|
32
|
+
}
|
|
33
|
+
declare function uploadSupportBundleWithProgress(file: File, appId: string, onProgress: (percent: number) => void, signal: AbortSignal): Promise<UploadSupportBundleResult>;
|
|
34
|
+
|
|
35
|
+
export { type UploadSupportBundleResult as U, uploadSupportBundleWithProgress as u };
|
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export { d as decodeBranding, n as normalizeColor, s as sanitizeUrlForCss } from '../branding-
|
|
2
|
-
import { a1 as DownloadPortalRelease } from '../index-
|
|
1
|
+
export { a as BackgroundType, B as BrandingResult, d as decodeBranding, n as normalizeColor, s as sanitizeUrlForCss } from '../branding-DmsrDTNE.mjs';
|
|
2
|
+
import { a1 as DownloadPortalRelease } from '../index-DaH1bSuO.mjs';
|
|
3
3
|
import { ReleaseEntry } from '../release-history-panel.mjs';
|
|
4
|
+
export { U as UploadSupportBundleResult, u as uploadSupportBundleWithProgress } from '../upload-with-progress-D760HthX.mjs';
|
|
4
5
|
import '../fetch-license-iTyF7_GY.mjs';
|
|
5
6
|
import '../actions/change-team.mjs';
|
|
6
7
|
import '../actions/trial-signup.mjs';
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export { d as decodeBranding, n as normalizeColor, s as sanitizeUrlForCss } from '../branding-
|
|
2
|
-
import { a1 as DownloadPortalRelease } from '../index-
|
|
1
|
+
export { a as BackgroundType, B as BrandingResult, d as decodeBranding, n as normalizeColor, s as sanitizeUrlForCss } from '../branding-DmsrDTNE.js';
|
|
2
|
+
import { a1 as DownloadPortalRelease } from '../index-Bcp17Mf3.js';
|
|
3
3
|
import { ReleaseEntry } from '../release-history-panel.js';
|
|
4
|
+
export { U as UploadSupportBundleResult, u as uploadSupportBundleWithProgress } from '../upload-with-progress-D760HthX.js';
|
|
4
5
|
import '../fetch-license-iTyF7_GY.js';
|
|
5
6
|
import '../actions/change-team.js';
|
|
6
7
|
import '../actions/trial-signup.js';
|
package/dist/utils/index.js
CHANGED
|
@@ -150,6 +150,7 @@ var decodeBranding = ({ brandingData }) => {
|
|
|
150
150
|
const secondaryColorRaw = parsed.secondaryColor ?? parsed.secondary_color;
|
|
151
151
|
const primaryColor = normalizeColor(primaryColorRaw);
|
|
152
152
|
const secondaryColor = normalizeColor(secondaryColorRaw);
|
|
153
|
+
const contact = typeof parsed.contact === "string" ? parsed.contact : void 0;
|
|
153
154
|
const supportPortalLink = typeof parsed.supportPortalLink === "string" ? parsed.supportPortalLink : void 0;
|
|
154
155
|
const backgroundRaw = parsed.background;
|
|
155
156
|
const background = backgroundRaw === "minimal" || backgroundRaw === "custom" || backgroundRaw === "image" ? backgroundRaw : void 0;
|
|
@@ -162,6 +163,7 @@ var decodeBranding = ({ brandingData }) => {
|
|
|
162
163
|
favicon,
|
|
163
164
|
primaryColor: primaryColor || DEFAULT_PRIMARY_COLOR,
|
|
164
165
|
secondaryColor: secondaryColor || DEFAULT_SECONDARY_COLOR,
|
|
166
|
+
contact,
|
|
165
167
|
supportPortalLink,
|
|
166
168
|
background,
|
|
167
169
|
backgroundImage,
|
|
@@ -211,6 +213,7 @@ var fetchCustomBrandingImpl = async () => {
|
|
|
211
213
|
title: payload.title,
|
|
212
214
|
primaryColor: payload.primaryColor,
|
|
213
215
|
secondaryColor: payload.secondaryColor,
|
|
216
|
+
contact: payload.contact,
|
|
214
217
|
favicon: payload.faviconUrl,
|
|
215
218
|
supportPortalLink: payload.supportPortalLink || "",
|
|
216
219
|
background: payload.background,
|
|
@@ -395,6 +398,50 @@ function convertToReleaseEntry(release, channelName, options) {
|
|
|
395
398
|
};
|
|
396
399
|
}
|
|
397
400
|
|
|
401
|
+
// src/utils/upload-with-progress.ts
|
|
402
|
+
async function uploadSupportBundleWithProgress(file, appId, onProgress, signal) {
|
|
403
|
+
return new Promise((resolve, reject) => {
|
|
404
|
+
const xhr = new XMLHttpRequest();
|
|
405
|
+
xhr.upload.addEventListener("progress", (e) => {
|
|
406
|
+
if (e.lengthComputable) {
|
|
407
|
+
const percent = Math.round(e.loaded / e.total * 100);
|
|
408
|
+
onProgress(percent);
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
xhr.addEventListener("load", () => {
|
|
412
|
+
if (xhr.status === 200) {
|
|
413
|
+
try {
|
|
414
|
+
const response = JSON.parse(xhr.responseText);
|
|
415
|
+
resolve(response);
|
|
416
|
+
} catch (err) {
|
|
417
|
+
reject(new Error("Failed to parse server response"));
|
|
418
|
+
}
|
|
419
|
+
} else {
|
|
420
|
+
try {
|
|
421
|
+
const response = JSON.parse(xhr.responseText);
|
|
422
|
+
reject(new Error(response.error || `Upload failed: ${xhr.status}`));
|
|
423
|
+
} catch {
|
|
424
|
+
reject(new Error(`Upload failed: ${xhr.status} ${xhr.statusText}`));
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
xhr.addEventListener("error", () => {
|
|
429
|
+
reject(new Error("Network error. Please check your connection."));
|
|
430
|
+
});
|
|
431
|
+
xhr.addEventListener("abort", () => {
|
|
432
|
+
reject(new Error("Upload cancelled"));
|
|
433
|
+
});
|
|
434
|
+
signal.addEventListener("abort", () => {
|
|
435
|
+
xhr.abort();
|
|
436
|
+
});
|
|
437
|
+
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
|
438
|
+
const uploadUrl = `${basePath}/api/support-bundles/upload?appId=${encodeURIComponent(appId)}`;
|
|
439
|
+
xhr.open("POST", uploadUrl, true);
|
|
440
|
+
xhr.setRequestHeader("Content-Type", "application/gzip");
|
|
441
|
+
xhr.send(file);
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
|
|
398
445
|
exports.DEFAULT_FAVICON = DEFAULT_FAVICON;
|
|
399
446
|
exports.DEFAULT_PRIMARY_COLOR = DEFAULT_PRIMARY_COLOR;
|
|
400
447
|
exports.DEFAULT_SECONDARY_COLOR = DEFAULT_SECONDARY_COLOR;
|
|
@@ -411,6 +458,7 @@ exports.isHttpApiOrigin = isHttpApiOrigin;
|
|
|
411
458
|
exports.isRedirectError = isRedirectError;
|
|
412
459
|
exports.normalizeColor = normalizeColor;
|
|
413
460
|
exports.sanitizeUrlForCss = sanitizeUrlForCss;
|
|
461
|
+
exports.uploadSupportBundleWithProgress = uploadSupportBundleWithProgress;
|
|
414
462
|
exports.validateSession = validateSession;
|
|
415
463
|
//# sourceMappingURL=index.js.map
|
|
416
464
|
//# sourceMappingURL=index.js.map
|