@replicated/portal-components 0.0.24 → 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 -53
- 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 -53
- 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 -220
- 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/logout-action.js +1 -1
- package/dist/esm/logout-action.js.map +1 -1
- package/dist/esm/logout-button.js +5 -7
- package/dist/esm/logout-button.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 -6
- 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 +6 -5
- package/dist/index.d.ts +6 -5
- package/dist/index.js +262 -220
- 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/logout-action.d.mts +1 -1
- package/dist/logout-action.d.ts +1 -1
- package/dist/logout-action.js +1 -1
- package/dist/logout-action.js.map +1 -1
- package/dist/logout-button.d.mts +1 -1
- package/dist/logout-button.d.ts +1 -1
- package/dist/logout-button.js +5 -7
- package/dist/logout-button.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 +4 -18
- package/dist/utils/index.d.ts +4 -18
- package/dist/utils/index.js +48 -6
- package/dist/utils/index.js.map +1 -1
- package/package.json +16 -6
- package/dist/esm/saml-handlers.js.map +0 -1
- package/dist/saml-handlers.js.map +0 -1
|
@@ -22,20 +22,27 @@ var DEFAULT_PRIMARY_COLOR = "#4f46e5";
|
|
|
22
22
|
var UploadSupportBundleModal = ({
|
|
23
23
|
isOpen,
|
|
24
24
|
onClose,
|
|
25
|
-
|
|
25
|
+
uploadWithProgress,
|
|
26
26
|
onUploadSuccess,
|
|
27
27
|
appId,
|
|
28
28
|
primaryColor = DEFAULT_PRIMARY_COLOR
|
|
29
29
|
}) => {
|
|
30
30
|
const [selectedFile, setSelectedFile] = useState(null);
|
|
31
31
|
const [isUploading, setIsUploading] = useState(false);
|
|
32
|
+
const [uploadProgress, setUploadProgress] = useState(0);
|
|
32
33
|
const [error, setError] = useState(null);
|
|
34
|
+
const [abortController, setAbortController] = useState(null);
|
|
33
35
|
const fileInputRef = useRef(null);
|
|
34
36
|
useEffect(() => {
|
|
35
37
|
if (!isOpen) {
|
|
36
38
|
setSelectedFile(null);
|
|
37
39
|
setIsUploading(false);
|
|
40
|
+
setUploadProgress(0);
|
|
38
41
|
setError(null);
|
|
42
|
+
setAbortController((prev) => {
|
|
43
|
+
prev?.abort();
|
|
44
|
+
return null;
|
|
45
|
+
});
|
|
39
46
|
}
|
|
40
47
|
}, [isOpen]);
|
|
41
48
|
const handleFileChange = useCallback((event) => {
|
|
@@ -52,30 +59,38 @@ var UploadSupportBundleModal = ({
|
|
|
52
59
|
if (!selectedFile || !appId) return;
|
|
53
60
|
setError(null);
|
|
54
61
|
setIsUploading(true);
|
|
62
|
+
setUploadProgress(0);
|
|
55
63
|
try {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
const controller = new AbortController();
|
|
65
|
+
setAbortController(controller);
|
|
66
|
+
const result = await uploadWithProgress(
|
|
67
|
+
selectedFile,
|
|
68
|
+
appId,
|
|
69
|
+
setUploadProgress,
|
|
70
|
+
controller.signal
|
|
71
|
+
);
|
|
60
72
|
if (!result.success) {
|
|
61
73
|
throw new Error(result.error ?? "Failed to upload support bundle");
|
|
62
74
|
}
|
|
63
75
|
await onUploadSuccess();
|
|
64
76
|
onClose();
|
|
65
77
|
} catch (err) {
|
|
66
|
-
|
|
67
|
-
|
|
78
|
+
if (err instanceof Error && err.message !== "Upload cancelled") {
|
|
79
|
+
console.error("Upload error:", err);
|
|
80
|
+
setError(err.message);
|
|
81
|
+
}
|
|
68
82
|
} finally {
|
|
69
83
|
setIsUploading(false);
|
|
84
|
+
setUploadProgress(0);
|
|
85
|
+
setAbortController(null);
|
|
70
86
|
}
|
|
71
|
-
}, [selectedFile, appId,
|
|
87
|
+
}, [selectedFile, appId, uploadWithProgress, onUploadSuccess, onClose]);
|
|
72
88
|
const handleCancel = useCallback(() => {
|
|
73
|
-
if (isUploading) {
|
|
74
|
-
|
|
75
|
-
return;
|
|
89
|
+
if (isUploading && abortController) {
|
|
90
|
+
abortController.abort();
|
|
76
91
|
}
|
|
77
92
|
onClose();
|
|
78
|
-
}, [isUploading, onClose]);
|
|
93
|
+
}, [isUploading, abortController, onClose]);
|
|
79
94
|
const handleBackdropClick = useCallback((e) => {
|
|
80
95
|
if (e.target === e.currentTarget && !isUploading) {
|
|
81
96
|
onClose();
|
|
@@ -111,9 +126,24 @@ var UploadSupportBundleModal = ({
|
|
|
111
126
|
] })
|
|
112
127
|
] }),
|
|
113
128
|
error && /* @__PURE__ */ jsx("div", { className: "mt-4 rounded-lg bg-red-50 px-3 py-2 text-sm text-red-700", children: error }),
|
|
114
|
-
isUploading && /* @__PURE__ */ jsxs("div", { className: "mt-6 mb-6
|
|
115
|
-
/* @__PURE__ */
|
|
116
|
-
|
|
129
|
+
isUploading && /* @__PURE__ */ jsxs("div", { className: "mt-6 mb-6", children: [
|
|
130
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
131
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700", children: "Uploading support bundle..." }),
|
|
132
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-gray-900", children: [
|
|
133
|
+
uploadProgress,
|
|
134
|
+
"%"
|
|
135
|
+
] })
|
|
136
|
+
] }),
|
|
137
|
+
/* @__PURE__ */ jsx("div", { className: "w-full bg-gray-200 rounded-full h-2", children: /* @__PURE__ */ jsx(
|
|
138
|
+
"div",
|
|
139
|
+
{
|
|
140
|
+
className: "h-2 rounded-full transition-all duration-300",
|
|
141
|
+
style: {
|
|
142
|
+
width: `${uploadProgress}%`,
|
|
143
|
+
backgroundColor: primaryColor || "#3B82F6"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
) })
|
|
117
147
|
] }),
|
|
118
148
|
/* @__PURE__ */ jsxs("div", { className: "mt-6 flex justify-end gap-3", children: [
|
|
119
149
|
/* @__PURE__ */ jsx(
|
|
@@ -122,7 +152,7 @@ var UploadSupportBundleModal = ({
|
|
|
122
152
|
type: "button",
|
|
123
153
|
onClick: handleCancel,
|
|
124
154
|
className: "rounded-lg px-4 py-2 text-sm font-medium text-gray-600 transition hover:bg-gray-100",
|
|
125
|
-
children: isUploading ? "Cancel Upload" : "Cancel"
|
|
155
|
+
children: isUploading && abortController ? "Cancel Upload" : "Cancel"
|
|
126
156
|
}
|
|
127
157
|
),
|
|
128
158
|
/* @__PURE__ */ jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/format.ts","../../src/utils/constants.ts","../../src/components/upload-support-bundle-modal.tsx"],"names":[],"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":[],"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"]}
|
package/dist/esm/utils/index.js
CHANGED
|
@@ -148,6 +148,7 @@ var decodeBranding = ({ brandingData }) => {
|
|
|
148
148
|
const secondaryColorRaw = parsed.secondaryColor ?? parsed.secondary_color;
|
|
149
149
|
const primaryColor = normalizeColor(primaryColorRaw);
|
|
150
150
|
const secondaryColor = normalizeColor(secondaryColorRaw);
|
|
151
|
+
const contact = typeof parsed.contact === "string" ? parsed.contact : void 0;
|
|
151
152
|
const supportPortalLink = typeof parsed.supportPortalLink === "string" ? parsed.supportPortalLink : void 0;
|
|
152
153
|
const backgroundRaw = parsed.background;
|
|
153
154
|
const background = backgroundRaw === "minimal" || backgroundRaw === "custom" || backgroundRaw === "image" ? backgroundRaw : void 0;
|
|
@@ -160,6 +161,7 @@ var decodeBranding = ({ brandingData }) => {
|
|
|
160
161
|
favicon,
|
|
161
162
|
primaryColor: primaryColor || DEFAULT_PRIMARY_COLOR,
|
|
162
163
|
secondaryColor: secondaryColor || DEFAULT_SECONDARY_COLOR,
|
|
164
|
+
contact,
|
|
163
165
|
supportPortalLink,
|
|
164
166
|
background,
|
|
165
167
|
backgroundImage,
|
|
@@ -209,6 +211,7 @@ var fetchCustomBrandingImpl = async () => {
|
|
|
209
211
|
title: payload.title,
|
|
210
212
|
primaryColor: payload.primaryColor,
|
|
211
213
|
secondaryColor: payload.secondaryColor,
|
|
214
|
+
contact: payload.contact,
|
|
212
215
|
favicon: payload.faviconUrl,
|
|
213
216
|
supportPortalLink: payload.supportPortalLink || "",
|
|
214
217
|
background: payload.background,
|
|
@@ -286,11 +289,6 @@ async function validateSession(token) {
|
|
|
286
289
|
}
|
|
287
290
|
return true;
|
|
288
291
|
}
|
|
289
|
-
async function deleteSessionCookie() {
|
|
290
|
-
const { cookies } = await import('next/headers');
|
|
291
|
-
const cookieStore = await cookies();
|
|
292
|
-
cookieStore.delete("portal_session");
|
|
293
|
-
}
|
|
294
292
|
|
|
295
293
|
// src/utils/format.ts
|
|
296
294
|
function formatBytes(bytes, decimals = 1) {
|
|
@@ -398,6 +396,50 @@ function convertToReleaseEntry(release, channelName, options) {
|
|
|
398
396
|
};
|
|
399
397
|
}
|
|
400
398
|
|
|
401
|
-
|
|
399
|
+
// src/utils/upload-with-progress.ts
|
|
400
|
+
async function uploadSupportBundleWithProgress(file, appId, onProgress, signal) {
|
|
401
|
+
return new Promise((resolve, reject) => {
|
|
402
|
+
const xhr = new XMLHttpRequest();
|
|
403
|
+
xhr.upload.addEventListener("progress", (e) => {
|
|
404
|
+
if (e.lengthComputable) {
|
|
405
|
+
const percent = Math.round(e.loaded / e.total * 100);
|
|
406
|
+
onProgress(percent);
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
xhr.addEventListener("load", () => {
|
|
410
|
+
if (xhr.status === 200) {
|
|
411
|
+
try {
|
|
412
|
+
const response = JSON.parse(xhr.responseText);
|
|
413
|
+
resolve(response);
|
|
414
|
+
} catch (err) {
|
|
415
|
+
reject(new Error("Failed to parse server response"));
|
|
416
|
+
}
|
|
417
|
+
} else {
|
|
418
|
+
try {
|
|
419
|
+
const response = JSON.parse(xhr.responseText);
|
|
420
|
+
reject(new Error(response.error || `Upload failed: ${xhr.status}`));
|
|
421
|
+
} catch {
|
|
422
|
+
reject(new Error(`Upload failed: ${xhr.status} ${xhr.statusText}`));
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
xhr.addEventListener("error", () => {
|
|
427
|
+
reject(new Error("Network error. Please check your connection."));
|
|
428
|
+
});
|
|
429
|
+
xhr.addEventListener("abort", () => {
|
|
430
|
+
reject(new Error("Upload cancelled"));
|
|
431
|
+
});
|
|
432
|
+
signal.addEventListener("abort", () => {
|
|
433
|
+
xhr.abort();
|
|
434
|
+
});
|
|
435
|
+
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
|
436
|
+
const uploadUrl = `${basePath}/api/support-bundles/upload?appId=${encodeURIComponent(appId)}`;
|
|
437
|
+
xhr.open("POST", uploadUrl, true);
|
|
438
|
+
xhr.setRequestHeader("Content-Type", "application/gzip");
|
|
439
|
+
xhr.send(file);
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export { DEFAULT_FAVICON, DEFAULT_PRIMARY_COLOR, DEFAULT_SECONDARY_COLOR, UnauthorizedError, authenticatedFetch, convertToReleaseEntry, decodeBranding, formatBytes, formatDate, formatDateShort, formatDateTime, formatDateTimeLocal, isHttpApiOrigin, isRedirectError, normalizeColor, sanitizeUrlForCss, uploadSupportBundleWithProgress, validateSession };
|
|
402
444
|
//# sourceMappingURL=index.js.map
|
|
403
445
|
//# sourceMappingURL=index.js.map
|