@collabdt/core 0.0.54 → 0.0.56
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/core/components/AppSidebarContent.d.ts.map +1 -1
- package/dist/core/components/AppSidebarContent.js +32 -5
- package/dist/core/components/AppSidebarContent.js.map +1 -1
- package/dist/core/components/authentication/AuthPage.d.ts.map +1 -1
- package/dist/core/components/authentication/AuthPage.js +3 -2
- package/dist/core/components/authentication/AuthPage.js.map +1 -1
- package/dist/core/components/authentication/Signin.d.ts +5 -1
- package/dist/core/components/authentication/Signin.d.ts.map +1 -1
- package/dist/core/components/authentication/Signin.js +17 -6
- package/dist/core/components/authentication/Signin.js.map +1 -1
- package/dist/core/components/instance-not-found/src/OrganizationNotFoundSection.d.ts.map +1 -1
- package/dist/core/components/instance-not-found/src/OrganizationNotFoundSection.js +152 -156
- package/dist/core/components/instance-not-found/src/OrganizationNotFoundSection.js.map +1 -1
- package/dist/core/components/organizationConfig/src/organizationConfigContent.d.ts.map +1 -1
- package/dist/core/components/organizationConfig/src/organizationConfigContent.js +146 -150
- package/dist/core/components/organizationConfig/src/organizationConfigContent.js.map +1 -1
- package/dist/core/components/settings/src/OrganizationSettingsPanel.d.ts.map +1 -1
- package/dist/core/components/settings/src/OrganizationSettingsPanel.js +4 -2
- package/dist/core/components/settings/src/OrganizationSettingsPanel.js.map +1 -1
- package/dist/core/components/support/BugReportDialog.d.ts +7 -0
- package/dist/core/components/support/BugReportDialog.d.ts.map +1 -0
- package/dist/core/components/support/BugReportDialog.js +117 -0
- package/dist/core/components/support/BugReportDialog.js.map +1 -0
- package/dist/core/components/support/FeatureRequestDialog.d.ts +8 -0
- package/dist/core/components/support/FeatureRequestDialog.d.ts.map +1 -0
- package/dist/core/components/support/FeatureRequestDialog.js +116 -0
- package/dist/core/components/support/FeatureRequestDialog.js.map +1 -0
- package/dist/core/components/support/SupportMenu.d.ts +10 -0
- package/dist/core/components/support/SupportMenu.d.ts.map +1 -0
- package/dist/core/components/support/SupportMenu.js +78 -0
- package/dist/core/components/support/SupportMenu.js.map +1 -0
- package/dist/core/components/ui/AnimatedBackground.d.ts.map +1 -1
- package/dist/core/components/ui/AnimatedBackground.js +3 -1
- package/dist/core/components/ui/AnimatedBackground.js.map +1 -1
- package/dist/core/components/ui/AppSidebar.d.ts.map +1 -1
- package/dist/core/components/ui/AppSidebar.js +11 -11
- package/dist/core/components/ui/AppSidebar.js.map +1 -1
- package/dist/core/components/ui/Navbar.d.ts.map +1 -1
- package/dist/core/components/ui/Navbar.js +15 -23
- package/dist/core/components/ui/Navbar.js.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +3 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js +4 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/RowActions.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/RowActions.js +3 -1
- package/dist/core/components/viewers/map/datasets/RowActions.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/index.js +2 -2
- package/dist/core/components/viewers/map/datasets/index.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.d.ts +1 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.js +3 -5
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js +5 -3
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/LayersTab/index.js +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/LayersTab/index.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/PointCloudViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/PointCloudViewer.js +4 -2
- package/dist/core/components/viewers/pointcloud/PointCloudViewer.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js +4 -2
- package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudTable.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudTable.js +7 -5
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudTable.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.js +15 -8
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js +13 -12
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/utils/potreeLoader.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/utils/potreeLoader.js +1 -1
- package/dist/core/components/viewers/pointcloud/utils/potreeLoader.js.map +1 -1
- package/dist/core/store/AppConfig/context.d.ts +10 -1
- package/dist/core/store/AppConfig/context.d.ts.map +1 -1
- package/dist/core/store/AppConfig/context.js +26 -5
- package/dist/core/store/AppConfig/context.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.tsx"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n// app/point-clouds/upload/page.tsx\r\n\"use client\";\r\n\r\nimport * as React from \"react\";\r\n\r\nimport { ProgressBar } from \"../../../map/src/tools/AddTools/AddFile/ProgressBar\";\r\nimport { Button, Input, ScrollArea } from '../../../../ui';\r\n\r\nconst API_BASE =\r\n process.env.NEXT_PUBLIC_POINTCLOUD_API_URL ?? \"http://localhost:5101\";\r\n\r\nconst CONVERSION_STATUS = Object.freeze({\r\n STARTED: \"conversion_started\",\r\n DOWNLOAD: \"download\",\r\n CONVERT: \"convert\",\r\n UPLOAD: \"upload\",\r\n CLEAN_UP: \"clean_up\",\r\n FAILED: \"failed\",\r\n FINISHED: \"finished\",\r\n});\r\n\r\n// All possible status values:\r\n// - local client-only phases: \"idle\", \"creating\", \"uploading\"\r\n// - plus all statuses from the backend (CONVERSION_STATUS values)\r\ntype ConversionStatus =\r\n | \"idle\"\r\n | \"creating\"\r\n | \"uploading\"\r\n | (typeof CONVERSION_STATUS)[keyof typeof CONVERSION_STATUS];\r\n\r\ntype CreatePointCloudResponse = {\r\n pointCloud: {\r\n id: string;\r\n name: string;\r\n };\r\n upload: {\r\n uploadUrl: string;\r\n };\r\n};\r\n\r\ntype StartConversionResponse = {\r\n jobId: string;\r\n};\r\n\r\ntype UploadPointCloudPageProps = {\r\n onGoBackButtonClick?: () => void;\r\n};\r\n\r\nexport default function UploadPointCloudPage({\r\n onGoBackButtonClick,\r\n}: UploadPointCloudPageProps) {\r\n const [name, setName] = React.useState(\"\");\r\n const [file, setFile] = React.useState<File | null>(null);\r\n\r\n const [progress, setProgress] = React.useState(0);\r\n const [status, setStatus] = React.useState<ConversionStatus>(\"idle\");\r\n const [pointCloudId, setPointCloudId] = React.useState<string | null>(null);\r\n const [jobId, setJobId] = React.useState<string | null>(null);\r\n const [logLines, setLogLines] = React.useState<string[]>([]);\r\n\r\n const esRef = React.useRef<EventSource | null>(null);\r\n\r\n const appendLog = React.useCallback((...parts: unknown[]) => {\r\n const line = parts\r\n .map((p) => (typeof p === \"string\" ? p : JSON.stringify(p, null, 2)))\r\n .join(\" \");\r\n setLogLines((prev) => [...prev, line]);\r\n }, []);\r\n\r\n React.useEffect(() => {\r\n return () => {\r\n if (esRef.current) {\r\n esRef.current.close();\r\n }\r\n };\r\n }, []);\r\n\r\n async function createPointCloud(\r\n name: string,\r\n ): Promise<CreatePointCloudResponse> {\r\n const res = await fetch(`${API_BASE}/point-cloud`, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ name }),\r\n });\r\n\r\n if (!res.ok) {\r\n const msg = await res.text();\r\n throw new Error(`Create point-cloud failed: ${msg || res.status}`);\r\n }\r\n\r\n return res.json();\r\n }\r\n\r\n function uploadToPresignedUrl(\r\n url: string,\r\n file: File,\r\n onProgress: (pct: number) => void,\r\n ): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n try {\r\n const xhr = new XMLHttpRequest();\r\n xhr.open(\"PUT\", url, true);\r\n\r\n xhr.upload.onprogress = (evt) => {\r\n if (evt.lengthComputable) {\r\n const pct = Math.round((evt.loaded / evt.total) * 100);\r\n onProgress(pct);\r\n }\r\n };\r\n\r\n xhr.onload = () => {\r\n if (xhr.status >= 200 && xhr.status < 300) resolve();\r\n else reject(new Error(`Upload failed with status ${xhr.status}`));\r\n };\r\n\r\n xhr.onerror = () => reject(new Error(\"Network error during upload\"));\r\n xhr.send(file);\r\n } catch (err) {\r\n reject(err);\r\n }\r\n });\r\n }\r\n\r\n async function startConversion(\r\n pointCloudId: string,\r\n ): Promise<StartConversionResponse> {\r\n const res = await fetch(\r\n `${API_BASE}/point-cloud/${encodeURIComponent(\r\n pointCloudId,\r\n )}/convert-to-potree`,\r\n {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ options: { sampling_method: \"poisson\" } }),\r\n },\r\n );\r\n\r\n if (!res.ok) {\r\n const msg = await res.text();\r\n throw new Error(`convert-to-potree failed: ${msg || res.status}`);\r\n }\r\n\r\n return res.json();\r\n }\r\n\r\n function subscribeToProgress(jobId: string) {\r\n if (!jobId) return;\r\n\r\n if (esRef.current) {\r\n esRef.current.close();\r\n }\r\n\r\n const es = new EventSource(\r\n `${API_BASE}/events/convert-progress/${encodeURIComponent(jobId)}`,\r\n );\r\n esRef.current = es;\r\n\r\n es.onopen = () => appendLog(\"🟢 SSE connection opened\");\r\n\r\n es.onerror = (event) => {\r\n appendLog(\"❌ SSE error:\", event);\r\n setStatus((prev) =>\r\n prev === CONVERSION_STATUS.FINISHED ? prev : CONVERSION_STATUS.FAILED,\r\n );\r\n };\r\n\r\n es.addEventListener(\"progress\", (event) => {\r\n try {\r\n const msgEvent = event as MessageEvent;\r\n const data = JSON.parse(msgEvent.data);\r\n\r\n appendLog(\r\n \"[progress]\",\r\n data.status ?? \"\",\r\n data.message ?? \"\",\r\n data.progress ?? \"\",\r\n );\r\n\r\n // If backend sends a status field matching our constants, use it\r\n if (\r\n data.status &&\r\n Object.values(CONVERSION_STATUS).includes(data.status)\r\n ) {\r\n setStatus(data.status as ConversionStatus);\r\n }\r\n\r\n if (typeof data.progress === \"number\") {\r\n setProgress(data.progress);\r\n }\r\n } catch (err) {\r\n appendLog(\"❌ Failed to parse progress event\", err);\r\n }\r\n });\r\n\r\n es.addEventListener(\"finished\", () => {\r\n appendLog(\"[finished] Conversion completed\");\r\n setStatus(CONVERSION_STATUS.FINISHED);\r\n setProgress(100);\r\n es.close();\r\n esRef.current = null;\r\n });\r\n\r\n es.addEventListener(\"failed\", () => {\r\n appendLog(\"[failed] Conversion failed\");\r\n setStatus(CONVERSION_STATUS.FAILED);\r\n es.close();\r\n esRef.current = null;\r\n });\r\n\r\n es.addEventListener(\"connected\", (event) => {\r\n const msgEvent = event as MessageEvent;\r\n appendLog(\"[connected]\", msgEvent.data);\r\n });\r\n\r\n es.onmessage = (event) => {\r\n appendLog(\"[message]\", event.data);\r\n };\r\n }\r\n\r\n const handleSubmit = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n if (!name.trim() || !file) {\r\n alert(\"Please provide a name and select a LAZ/LAS file.\");\r\n return;\r\n }\r\n\r\n setLogLines([]);\r\n setProgress(0);\r\n setPointCloudId(null);\r\n setJobId(null);\r\n setStatus(\"idle\");\r\n\r\n try {\r\n appendLog(\"🟦 1) Creating point cloud...\");\r\n setStatus(\"creating\");\r\n\r\n const { pointCloud, upload } = await createPointCloud(name.trim());\r\n appendLog(pointCloud);\r\n setPointCloudId(pointCloud.id);\r\n\r\n appendLog(\"🟦 2) Uploading to presigned URL...\");\r\n setStatus(\"uploading\");\r\n await uploadToPresignedUrl(upload.uploadUrl, file, (pct) =>\r\n setProgress(pct),\r\n );\r\n appendLog(\"✔ Upload finished\");\r\n\r\n appendLog(\"🟦 3) Starting conversion...\");\r\n // Mark that the conversion has started (using backend status constant)\r\n setStatus(CONVERSION_STATUS.STARTED);\r\n const conversion = await startConversion(pointCloud.id);\r\n appendLog(conversion);\r\n\r\n const jobId = conversion.jobId;\r\n setJobId(jobId);\r\n\r\n appendLog(\"🟦 4) Subscribing to SSE…\");\r\n subscribeToProgress(jobId);\r\n } catch (err) {\r\n const message =\r\n err instanceof Error ? err.message : \"Unknown upload error\";\r\n appendLog(\"🔥 ERROR:\", message);\r\n setStatus(CONVERSION_STATUS.FAILED);\r\n alert(message);\r\n }\r\n };\r\n\r\n const busy =\r\n status !== \"idle\" &&\r\n status !== CONVERSION_STATUS.FINISHED &&\r\n status !== CONVERSION_STATUS.FAILED;\r\n\r\n const statusLabel: string = (() => {\r\n switch (status) {\r\n case \"idle\":\r\n return \"Idle\";\r\n case \"creating\":\r\n return \"Creating point cloud…\";\r\n case \"uploading\":\r\n return \"Uploading LAZ…\";\r\n\r\n case CONVERSION_STATUS.STARTED:\r\n return \"Conversion started…\";\r\n case CONVERSION_STATUS.DOWNLOAD:\r\n return \"Downloading source file…\";\r\n case CONVERSION_STATUS.CONVERT:\r\n return \"Running Potree converter…\";\r\n case CONVERSION_STATUS.UPLOAD:\r\n return \"Uploading Potree output…\";\r\n case CONVERSION_STATUS.CLEAN_UP:\r\n return \"Cleaning up temporary files…\";\r\n case CONVERSION_STATUS.FINISHED:\r\n return \"Done 🎉\";\r\n case CONVERSION_STATUS.FAILED:\r\n return \"Error – see log\";\r\n default:\r\n return \"Unknown status\";\r\n }\r\n })();\r\n\r\n return (\r\n <div className=\"p-2 mt-16\">\r\n <div className=\"flex items-center justify-between gap-2 mb-4\">\r\n <div>\r\n <h1 className=\"text-2xl font-semibold tracking-tight\">\r\n Upload & Convert Point Cloud\r\n </h1>\r\n </div>\r\n\r\n <Button\r\n variant=\"outline\"\r\n onClick={() => {\r\n if (onGoBackButtonClick) onGoBackButtonClick();\r\n }}\r\n >\r\n ← Back to list\r\n </Button>\r\n </div>\r\n\r\n <div className=\"grid gap-4 md:grid-cols-[2fr,1.2fr]\">\r\n {/* Upload card */}\r\n <div className=\"rounded-md border bg-background p-4 space-y-4\">\r\n <div className=\"flex items-center justify-between gap-2 text-sm\">\r\n <span className=\"font-semibold\">Upload point cloud</span>\r\n <span className=\"text-xs text-muted-foreground\">\r\n {statusLabel}\r\n </span>\r\n </div>\r\n\r\n <form onSubmit={handleSubmit} className=\"space-y-3 text-sm\">\r\n <div className=\"space-y-1\">\r\n <div className=\"text-xs font-medium\">Name</div>\r\n <Input\r\n placeholder=\"e.g. Campus north wing\"\r\n value={name}\r\n onChange={(e) => setName(e.target.value)}\r\n />\r\n </div>\r\n\r\n <div className=\"space-y-1\">\r\n <div className=\"text-xs font-medium\">LAZ / LAS file</div>\r\n <Input\r\n type=\"file\"\r\n accept=\".laz,.las\"\r\n onChange={(e) => {\r\n const f = e.target.files?.[0] ?? null;\r\n setFile(f);\r\n }}\r\n />\r\n {file && (\r\n <div className=\"text-[11px] text-muted-foreground\">\r\n Selected: <span className=\"font-medium\">{file.name}</span>\r\n </div>\r\n )}\r\n </div>\r\n\r\n <div className=\"flex items-center gap-3\">\r\n <Button\r\n type=\"submit\"\r\n disabled={!name.trim() || !file || busy}\r\n size=\"sm\"\r\n >\r\n {busy ? \"Working...\" : \"Upload & Convert\"}\r\n </Button>\r\n\r\n <div className=\"flex flex-col text-[11px] text-muted-foreground\">\r\n <span>\r\n PointCloud ID:{\" \"}\r\n <code className=\"text-xs\">{pointCloudId ?? \"—\"}</code>\r\n </span>\r\n <span>\r\n Job ID: <code className=\"text-xs\">{jobId ?? \"—\"}</code>\r\n </span>\r\n </div>\r\n </div>\r\n\r\n <div className=\"space-y-1\">\r\n <div className=\"flex items-center justify-between text-[11px] text-muted-foreground\">\r\n <span>Upload / conversion progress</span>\r\n <span>{progress}%</span>\r\n </div>\r\n <ProgressBar value={progress} />\r\n </div>\r\n </form>\r\n </div>\r\n\r\n {/* Log card */}\r\n <div className=\"rounded-md border bg-muted/20 p-3 flex flex-col gap-2\">\r\n <div className=\"flex items-center justify-between text-xs\">\r\n <h2 className=\"font-semibold uppercase tracking-wide\">\r\n Conversion log\r\n </h2>\r\n <Button\r\n type=\"button\"\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n className=\"h-6 px-2 text-[11px]\"\r\n onClick={() => setLogLines([])}\r\n >\r\n Clear\r\n </Button>\r\n </div>\r\n\r\n <ScrollArea className=\"h-[200px] rounded border bg-background\">\r\n <pre className=\"p-2 text-[11px] leading-snug whitespace-pre-wrap\">\r\n {logLines.length ? logLines.join(\"\\n\") : \"No activity yet.\"}\r\n </pre>\r\n </ScrollArea>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n"],"mappings":"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","names":["name","file","pointCloudId","jobId","_a"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.tsx"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n// app/point-clouds/upload/page.tsx\r\n\"use client\";\r\n\r\nimport * as React from \"react\";\r\n\r\nimport { useSession } from \"next-auth/react\";\r\nimport { useOrganization } from \"../../../../../hooks/organizations/organizations\";\r\nimport { ProgressBar } from \"../../../map/src/tools/AddTools/AddFile/ProgressBar\";\r\nimport { Button, Input, ScrollArea } from '../../../../ui';\r\nimport { useAppConfigContext } from '../../../../../store/AppConfig/context'\r\n\r\nconst CONVERSION_STATUS = Object.freeze({\r\n STARTED: \"conversion_started\",\r\n DOWNLOAD: \"download\",\r\n CONVERT: \"convert\",\r\n UPLOAD: \"upload\",\r\n CLEAN_UP: \"clean_up\",\r\n FAILED: \"failed\",\r\n FINISHED: \"finished\",\r\n});\r\n\r\n// All possible status values:\r\n// - local client-only phases: \"idle\", \"creating\", \"uploading\"\r\n// - plus all statuses from the backend (CONVERSION_STATUS values)\r\ntype ConversionStatus =\r\n | \"idle\"\r\n | \"creating\"\r\n | \"uploading\"\r\n | (typeof CONVERSION_STATUS)[keyof typeof CONVERSION_STATUS];\r\n\r\ntype CreatePointCloudResponse = {\r\n pointCloud: {\r\n id: string;\r\n name: string;\r\n };\r\n upload: {\r\n uploadUrl: string;\r\n };\r\n};\r\n\r\ntype StartConversionResponse = {\r\n jobId: string;\r\n};\r\n\r\ntype UploadPointCloudPageProps = {\r\n onGoBackButtonClick?: () => void;\r\n};\r\n\r\nexport default function UploadPointCloudPage({\r\n onGoBackButtonClick,\r\n}: UploadPointCloudPageProps) {\r\n const { state: appConfigState } = useAppConfigContext()\r\n const API_BASE = appConfigState.runtimeConfig.pointcloudApiUrl ?? 'http://localhost:5101'\r\n const { data: session } = useSession();\r\n const organizationId = session?.user?.organizationId ?? null;\r\n const { organization } = useOrganization(organizationId ? organizationId.toString() : null);\r\n\r\n const [name, setName] = React.useState(\"\");\r\n const [file, setFile] = React.useState<File | null>(null);\r\n\r\n const [progress, setProgress] = React.useState(0);\r\n const [status, setStatus] = React.useState<ConversionStatus>(\"idle\");\r\n const [pointCloudId, setPointCloudId] = React.useState<string | null>(null);\r\n const [jobId, setJobId] = React.useState<string | null>(null);\r\n const [logLines, setLogLines] = React.useState<string[]>([]);\r\n\r\n const esRef = React.useRef<EventSource | null>(null);\r\n\r\n const appendLog = React.useCallback((...parts: unknown[]) => {\r\n const line = parts\r\n .map((p) => (typeof p === \"string\" ? p : JSON.stringify(p, null, 2)))\r\n .join(\" \");\r\n setLogLines((prev) => [...prev, line]);\r\n }, []);\r\n\r\n React.useEffect(() => {\r\n return () => {\r\n if (esRef.current) {\r\n esRef.current.close();\r\n }\r\n };\r\n }, []);\r\n\r\n async function createPointCloud(\r\n name: string,\r\n ): Promise<CreatePointCloudResponse> {\r\n const res = await fetch(`${API_BASE}/point-cloud`, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ name, organizationId, organizationName: organization?.name }),\r\n });\r\n\r\n if (!res.ok) {\r\n const msg = await res.text();\r\n throw new Error(`Create point-cloud failed: ${msg || res.status}`);\r\n }\r\n\r\n return res.json();\r\n }\r\n\r\n function uploadToPresignedUrl(\r\n url: string,\r\n file: File,\r\n onProgress: (pct: number) => void,\r\n ): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n try {\r\n const xhr = new XMLHttpRequest();\r\n xhr.open(\"PUT\", url, true);\r\n\r\n xhr.upload.onprogress = (evt) => {\r\n if (evt.lengthComputable) {\r\n const pct = Math.round((evt.loaded / evt.total) * 100);\r\n onProgress(pct);\r\n }\r\n };\r\n\r\n xhr.onload = () => {\r\n if (xhr.status >= 200 && xhr.status < 300) resolve();\r\n else reject(new Error(`Upload failed with status ${xhr.status}`));\r\n };\r\n\r\n xhr.onerror = () => reject(new Error(\"Network error during upload\"));\r\n xhr.send(file);\r\n } catch (err) {\r\n reject(err);\r\n }\r\n });\r\n }\r\n\r\n async function startConversion(\r\n pointCloudId: string,\r\n ): Promise<StartConversionResponse> {\r\n const res = await fetch(\r\n `${API_BASE}/point-cloud/${encodeURIComponent(\r\n pointCloudId,\r\n )}/convert-to-potree`,\r\n {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ options: { sampling_method: \"poisson\" } }),\r\n },\r\n );\r\n\r\n if (!res.ok) {\r\n const msg = await res.text();\r\n throw new Error(`convert-to-potree failed: ${msg || res.status}`);\r\n }\r\n\r\n return res.json();\r\n }\r\n\r\n function subscribeToProgress(jobId: string) {\r\n if (!jobId) return;\r\n\r\n if (esRef.current) {\r\n esRef.current.close();\r\n }\r\n\r\n const es = new EventSource(\r\n `${API_BASE}/events/convert-progress/${encodeURIComponent(jobId)}`,\r\n );\r\n esRef.current = es;\r\n\r\n es.onopen = () => appendLog(\"🟢 SSE connection opened\");\r\n\r\n es.onerror = (event) => {\r\n appendLog(\"❌ SSE error:\", event);\r\n setStatus((prev) =>\r\n prev === CONVERSION_STATUS.FINISHED ? prev : CONVERSION_STATUS.FAILED,\r\n );\r\n };\r\n\r\n es.addEventListener(\"progress\", (event) => {\r\n try {\r\n const msgEvent = event as MessageEvent;\r\n const data = JSON.parse(msgEvent.data);\r\n\r\n appendLog(\r\n \"[progress]\",\r\n data.status ?? \"\",\r\n data.message ?? \"\",\r\n data.progress ?? \"\",\r\n );\r\n\r\n // If backend sends a status field matching our constants, use it\r\n if (\r\n data.status &&\r\n Object.values(CONVERSION_STATUS).includes(data.status)\r\n ) {\r\n setStatus(data.status as ConversionStatus);\r\n }\r\n\r\n if (typeof data.progress === \"number\") {\r\n setProgress(data.progress);\r\n }\r\n } catch (err) {\r\n appendLog(\"❌ Failed to parse progress event\", err);\r\n }\r\n });\r\n\r\n es.addEventListener(\"finished\", () => {\r\n appendLog(\"[finished] Conversion completed\");\r\n setStatus(CONVERSION_STATUS.FINISHED);\r\n setProgress(100);\r\n es.close();\r\n esRef.current = null;\r\n });\r\n\r\n es.addEventListener(\"failed\", () => {\r\n appendLog(\"[failed] Conversion failed\");\r\n setStatus(CONVERSION_STATUS.FAILED);\r\n es.close();\r\n esRef.current = null;\r\n });\r\n\r\n es.addEventListener(\"connected\", (event) => {\r\n const msgEvent = event as MessageEvent;\r\n appendLog(\"[connected]\", msgEvent.data);\r\n });\r\n\r\n es.onmessage = (event) => {\r\n appendLog(\"[message]\", event.data);\r\n };\r\n }\r\n\r\n const handleSubmit = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n if (!name.trim() || !file) {\r\n alert(\"Please provide a name and select a LAZ/LAS file.\");\r\n return;\r\n }\r\n\r\n setLogLines([]);\r\n setProgress(0);\r\n setPointCloudId(null);\r\n setJobId(null);\r\n setStatus(\"idle\");\r\n\r\n try {\r\n appendLog(\"🟦 1) Creating point cloud...\");\r\n setStatus(\"creating\");\r\n\r\n const { pointCloud, upload } = await createPointCloud(name.trim());\r\n appendLog(pointCloud);\r\n setPointCloudId(pointCloud.id);\r\n\r\n appendLog(\"🟦 2) Uploading to presigned URL...\");\r\n setStatus(\"uploading\");\r\n await uploadToPresignedUrl(upload.uploadUrl, file, (pct) =>\r\n setProgress(pct),\r\n );\r\n appendLog(\"✔ Upload finished\");\r\n\r\n appendLog(\"🟦 3) Starting conversion...\");\r\n // Mark that the conversion has started (using backend status constant)\r\n setStatus(CONVERSION_STATUS.STARTED);\r\n const conversion = await startConversion(pointCloud.id);\r\n appendLog(conversion);\r\n\r\n const jobId = conversion.jobId;\r\n setJobId(jobId);\r\n\r\n appendLog(\"🟦 4) Subscribing to SSE…\");\r\n subscribeToProgress(jobId);\r\n } catch (err) {\r\n const message =\r\n err instanceof Error ? err.message : \"Unknown upload error\";\r\n appendLog(\"🔥 ERROR:\", message);\r\n setStatus(CONVERSION_STATUS.FAILED);\r\n alert(message);\r\n }\r\n };\r\n\r\n const busy =\r\n status !== \"idle\" &&\r\n status !== CONVERSION_STATUS.FINISHED &&\r\n status !== CONVERSION_STATUS.FAILED;\r\n\r\n const statusLabel: string = (() => {\r\n switch (status) {\r\n case \"idle\":\r\n return \"Idle\";\r\n case \"creating\":\r\n return \"Creating point cloud…\";\r\n case \"uploading\":\r\n return \"Uploading LAZ…\";\r\n\r\n case CONVERSION_STATUS.STARTED:\r\n return \"Conversion started…\";\r\n case CONVERSION_STATUS.DOWNLOAD:\r\n return \"Downloading source file…\";\r\n case CONVERSION_STATUS.CONVERT:\r\n return \"Running Potree converter…\";\r\n case CONVERSION_STATUS.UPLOAD:\r\n return \"Uploading Potree output…\";\r\n case CONVERSION_STATUS.CLEAN_UP:\r\n return \"Cleaning up temporary files…\";\r\n case CONVERSION_STATUS.FINISHED:\r\n return \"Done 🎉\";\r\n case CONVERSION_STATUS.FAILED:\r\n return \"Error – see log\";\r\n default:\r\n return \"Unknown status\";\r\n }\r\n })();\r\n\r\n return (\r\n <div className=\"p-2 mt-16\">\r\n <div className=\"flex items-center justify-between gap-2 mb-4\">\r\n <div>\r\n <h1 className=\"text-2xl font-semibold tracking-tight\">\r\n Upload & Convert Point Cloud\r\n </h1>\r\n </div>\r\n\r\n <Button\r\n variant=\"outline\"\r\n onClick={() => {\r\n if (onGoBackButtonClick) onGoBackButtonClick();\r\n }}\r\n >\r\n ← Back to list\r\n </Button>\r\n </div>\r\n\r\n <div className=\"grid gap-4 md:grid-cols-[2fr,1.2fr]\">\r\n {/* Upload card */}\r\n <div className=\"rounded-md border bg-background p-4 space-y-4\">\r\n <div className=\"flex items-center justify-between gap-2 text-sm\">\r\n <span className=\"font-semibold\">Upload point cloud</span>\r\n <span className=\"text-xs text-muted-foreground\">\r\n {statusLabel}\r\n </span>\r\n </div>\r\n\r\n <form onSubmit={handleSubmit} className=\"space-y-3 text-sm\">\r\n <div className=\"space-y-1\">\r\n <div className=\"text-xs font-medium\">Name</div>\r\n <Input\r\n placeholder=\"e.g. Campus north wing\"\r\n value={name}\r\n onChange={(e) => setName(e.target.value)}\r\n />\r\n </div>\r\n\r\n <div className=\"space-y-1\">\r\n <div className=\"text-xs font-medium\">LAZ / LAS file</div>\r\n <Input\r\n type=\"file\"\r\n accept=\".laz,.las\"\r\n onChange={(e) => {\r\n const f = e.target.files?.[0] ?? null;\r\n setFile(f);\r\n }}\r\n />\r\n {file && (\r\n <div className=\"text-[11px] text-muted-foreground\">\r\n Selected: <span className=\"font-medium\">{file.name}</span>\r\n </div>\r\n )}\r\n </div>\r\n\r\n <div className=\"flex items-center gap-3\">\r\n <Button\r\n type=\"submit\"\r\n disabled={!name.trim() || !file || busy}\r\n size=\"sm\"\r\n >\r\n {busy ? \"Working...\" : \"Upload & Convert\"}\r\n </Button>\r\n\r\n <div className=\"flex flex-col text-[11px] text-muted-foreground\">\r\n <span>\r\n PointCloud ID:{\" \"}\r\n <code className=\"text-xs\">{pointCloudId ?? \"—\"}</code>\r\n </span>\r\n <span>\r\n Job ID: <code className=\"text-xs\">{jobId ?? \"—\"}</code>\r\n </span>\r\n </div>\r\n </div>\r\n\r\n <div className=\"space-y-1\">\r\n <div className=\"flex items-center justify-between text-[11px] text-muted-foreground\">\r\n <span>Upload / conversion progress</span>\r\n <span>{progress}%</span>\r\n </div>\r\n <ProgressBar value={progress} />\r\n </div>\r\n </form>\r\n </div>\r\n\r\n {/* Log card */}\r\n <div className=\"rounded-md border bg-muted/20 p-3 flex flex-col gap-2\">\r\n <div className=\"flex items-center justify-between text-xs\">\r\n <h2 className=\"font-semibold uppercase tracking-wide\">\r\n Conversion log\r\n </h2>\r\n <Button\r\n type=\"button\"\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n className=\"h-6 px-2 text-[11px]\"\r\n onClick={() => setLogLines([])}\r\n >\r\n Clear\r\n </Button>\r\n </div>\r\n\r\n <ScrollArea className=\"h-[200px] rounded border bg-background\">\r\n <pre className=\"p-2 text-[11px] leading-snug whitespace-pre-wrap\">\r\n {logLines.length ? logLines.join(\"\\n\") : \"No activity yet.\"}\r\n </pre>\r\n </ScrollArea>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n"],"mappings":"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","names":["name","file","pointCloudId","jobId","_a","_b","_c"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PointCloudSection.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,OAAO,EAAE,MAAM,EAAE,MAAM,uCAAuC,CAAA;
|
|
1
|
+
{"version":3,"file":"PointCloudSection.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,OAAO,EAAE,MAAM,EAAE,MAAM,uCAAuC,CAAA;AAoB9D,UAAU,uBAAuB;IAC/B,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,EAAE,uBAAuB,qBA4YpE"}
|
|
@@ -19,7 +19,6 @@ var __spreadValues = (a, b) => {
|
|
|
19
19
|
};
|
|
20
20
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
21
21
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
22
|
-
var _a;
|
|
23
22
|
import * as React from "react";
|
|
24
23
|
import * as LR from "lucide-react";
|
|
25
24
|
import { useTranslations } from "next-intl";
|
|
@@ -31,13 +30,15 @@ import ConfirmDialog from "../../../../../../../ConfirmDialog";
|
|
|
31
30
|
import { Button } from "../../../../../../../ui/Button";
|
|
32
31
|
import { useOrganization } from "../../../../../../../../hooks/organizations/organizations";
|
|
33
32
|
import { PointCloudContext } from "../../../../../../../../store";
|
|
34
|
-
|
|
33
|
+
import { useAppConfigContext } from "../../../../../../../../store/AppConfig/context";
|
|
35
34
|
const POINT_CLOUD_OPTIONS = ["view", "delete", "info"];
|
|
36
35
|
function PointCloudsSection({ files }) {
|
|
37
|
-
var
|
|
36
|
+
var _a, _b, _c;
|
|
38
37
|
const t = useTranslations("PointCloudManagement");
|
|
38
|
+
const { state: appConfigState } = useAppConfigContext();
|
|
39
|
+
const API_BASE = (_a = appConfigState.runtimeConfig.pointcloudApiUrl) != null ? _a : "http://localhost:5101";
|
|
39
40
|
const { data: session } = useSession();
|
|
40
|
-
const organizationId = (
|
|
41
|
+
const organizationId = (_c = (_b = session == null ? void 0 : session.user) == null ? void 0 : _b.organizationId) != null ? _c : null;
|
|
41
42
|
const { organization } = useOrganization(organizationId ? organizationId.toString() : null);
|
|
42
43
|
const { dispatch: pointCloudDispatch } = React.useContext(PointCloudContext);
|
|
43
44
|
const [pointcloudsFiles, setPointcloudsFiles] = React.useState(
|
|
@@ -47,17 +48,17 @@ function PointCloudsSection({ files }) {
|
|
|
47
48
|
const autoLoadedRef = React.useRef(false);
|
|
48
49
|
const fileSyncKey = files.map(
|
|
49
50
|
(file) => {
|
|
50
|
-
var
|
|
51
|
-
return `${file.id}:${file.name}:${Boolean(file.pointCloudUploaded)}:${Boolean(file.pointCloudPotreeConverted)}:${file.uploadedAt}:${(
|
|
51
|
+
var _a2;
|
|
52
|
+
return `${file.id}:${file.name}:${Boolean(file.pointCloudUploaded)}:${Boolean(file.pointCloudPotreeConverted)}:${file.uploadedAt}:${(_a2 = file.potreeMetadataFileKey) != null ? _a2 : ""}`;
|
|
52
53
|
}
|
|
53
54
|
).join("|");
|
|
54
55
|
React.useEffect(() => {
|
|
55
56
|
setPointcloudsFiles((prev) => {
|
|
56
57
|
const prevMap = new Map(prev.map((f) => [String(f.id), f]));
|
|
57
58
|
return files.map((f) => {
|
|
58
|
-
var
|
|
59
|
+
var _a2, _b2;
|
|
59
60
|
return __spreadProps(__spreadValues({}, f), {
|
|
60
|
-
isVisible: (_b2 = (
|
|
61
|
+
isVisible: (_b2 = (_a2 = prevMap.get(String(f.id))) == null ? void 0 : _a2.isVisible) != null ? _b2 : false
|
|
61
62
|
});
|
|
62
63
|
});
|
|
63
64
|
});
|
|
@@ -245,7 +246,7 @@ function PointCloudsSection({ files }) {
|
|
|
245
246
|
});
|
|
246
247
|
}
|
|
247
248
|
const handleFileUpload = async (file) => {
|
|
248
|
-
var
|
|
249
|
+
var _a2;
|
|
249
250
|
if (!file) return;
|
|
250
251
|
const originalFileName = file.name;
|
|
251
252
|
const fileExt = originalFileName.toLowerCase();
|
|
@@ -263,7 +264,7 @@ function PointCloudsSection({ files }) {
|
|
|
263
264
|
}
|
|
264
265
|
try {
|
|
265
266
|
console.log("Creating point cloud entry...");
|
|
266
|
-
const userEmail = ((
|
|
267
|
+
const userEmail = ((_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.email) || null;
|
|
267
268
|
const { pointCloud, upload } = await createPointCloud(fileName, userEmail);
|
|
268
269
|
console.log("Point cloud created:", pointCloud.id);
|
|
269
270
|
console.log("Uploading file...");
|
|
@@ -287,8 +288,8 @@ function PointCloudsSection({ files }) {
|
|
|
287
288
|
input.type = "file";
|
|
288
289
|
input.accept = ".laz,.las";
|
|
289
290
|
input.onchange = (e) => {
|
|
290
|
-
var
|
|
291
|
-
const file = (
|
|
291
|
+
var _a2;
|
|
292
|
+
const file = (_a2 = e.target.files) == null ? void 0 : _a2[0];
|
|
292
293
|
if (file) {
|
|
293
294
|
handleFileUpload(file);
|
|
294
295
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as React from 'react'\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport { useSession } from 'next-auth/react'\r\nimport { CollapsibleSection } from '../../../../../../../ui/CollapsibleSection'\r\nimport { FileItemComponent } from '../../../../../../../ui/FilesManager/src/FileItemComponent'\r\nimport { useFileActions } from '../../../../../../../ui/FilesManager/src/useFileActions'\r\nimport ConfirmDialog from '../../../../../../../ConfirmDialog'\r\nimport { Button } from '../../../../../../../ui/Button'\r\nimport { useOrganization } from '../../../../../../../../hooks/organizations/organizations'\r\nimport { DbFile } from '../../../../../../../../types/dbTypes'\r\nimport { PointCloudContext } from '../../../../../../../../store'\r\n\r\nconst API_BASE = process.env.NEXT_PUBLIC_POINTCLOUD_API_URL ?? 'http://localhost:5101'\r\n\r\ntype CreatePointCloudResponse = {\r\n pointCloud: {\r\n id: string\r\n name: string\r\n }\r\n upload: {\r\n uploadUrl: string\r\n }\r\n}\r\n\r\ntype StartConversionResponse = {\r\n jobId: string\r\n}\r\n\r\nconst POINT_CLOUD_OPTIONS: import('../../../../../../../../types/global').FileAction[] = ['view', 'delete', 'info']\r\n\r\ninterface PointCloudsSectionProps {\r\n files: DbFile[]\r\n}\r\n\r\nexport function PointCloudsSection({ files }: PointCloudsSectionProps) {\r\n // Translation\r\n const t = useTranslations('PointCloudManagement')\r\n\r\n // Get current user session\r\n const { data: session } = useSession()\r\n const organizationId = session?.user?.organizationId ?? null;\r\n const { organization } = useOrganization(organizationId ? organizationId.toString() : null);\r\n\r\n // Point cloud context for loading/unloading clouds in the viewer\r\n const { dispatch: pointCloudDispatch } = React.useContext(PointCloudContext)\r\n\r\n // State for point clouds — includes isVisible to track which are loaded in viewer\r\n const [pointcloudsFiles, setPointcloudsFiles] = React.useState<(DbFile & { isVisible?: boolean })[]>(\r\n () => files.map(f => ({ ...f, isVisible: false }))\r\n )\r\n const esRef = React.useRef<EventSource | null>(null)\r\n const autoLoadedRef = React.useRef(false)\r\n\r\n const fileSyncKey = files\r\n .map(\r\n (file) =>\r\n `${file.id}:${file.name}:${Boolean(file.pointCloudUploaded)}:${Boolean(file.pointCloudPotreeConverted)}:${file.uploadedAt}:${file.potreeMetadataFileKey ?? ''}`\r\n )\r\n .join('|')\r\n\r\n // Sync incoming files from props while preserving isVisible state for known files\r\n React.useEffect(() => {\r\n setPointcloudsFiles(prev => {\r\n const prevMap = new Map(prev.map(f => [String(f.id), f]))\r\n return files.map(f => ({\r\n ...f,\r\n isVisible: prevMap.get(String(f.id))?.isVisible ?? false,\r\n }))\r\n })\r\n }, [fileSyncKey])\r\n\r\n // Auto-load the most recently uploaded converted file on first access\r\n React.useEffect(() => {\r\n if (autoLoadedRef.current || files.length === 0) return\r\n\r\n const readyFiles = files.filter(f => Boolean(f.pointCloudPotreeConverted))\r\n if (readyFiles.length === 0) return\r\n\r\n const lastFile = readyFiles.reduce((latest, f) =>\r\n new Date(f.uploadedAt) > new Date(latest.uploadedAt) ? f : latest\r\n )\r\n\r\n autoLoadedRef.current = true\r\n pointCloudDispatch({ type: 'LOAD_POINT_CLOUD', payload: { id: String(lastFile.id) } })\r\n setPointcloudsFiles(prev =>\r\n prev.map(f => ({ ...f, isVisible: String(f.id) === String(lastFile.id) }))\r\n )\r\n }, [fileSyncKey, files, pointCloudDispatch])\r\n\r\n React.useEffect(() => {\r\n // Cleanup SSE on unmount\r\n return () => {\r\n if (esRef.current) {\r\n esRef.current.close()\r\n }\r\n }\r\n }, [])\r\n\r\n // Toggle a point cloud on/off in the viewer via context\r\n const handleView = React.useCallback((file: DbFile, newVisibility: boolean) => {\r\n const id = String(file.id)\r\n if (newVisibility) {\r\n pointCloudDispatch({ type: 'LOAD_POINT_CLOUD', payload: { id } })\r\n } else {\r\n pointCloudDispatch({ type: 'UNLOAD_POINT_CLOUD', payload: { id } })\r\n }\r\n }, [pointCloudDispatch])\r\n\r\n const handleDeleteFile = React.useCallback(async (file: DbFile) => {\r\n const res = await fetch(`${API_BASE}/point-cloud/${encodeURIComponent(String(file.id))}`, {\r\n method: 'DELETE',\r\n })\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(msg || `Delete failed (${res.status})`)\r\n }\r\n }, [])\r\n\r\n const handleConvert = React.useCallback(async (file: DbFile) => {\r\n const res = await fetch(\r\n `${API_BASE}/point-cloud/${encodeURIComponent(String(file.id))}/convert-to-potree`,\r\n {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ options: { sampling_method: 'poisson' } }),\r\n }\r\n )\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(msg || `Conversion failed (${res.status})`)\r\n }\r\n\r\n const { jobId } = await res.json() as StartConversionResponse\r\n subscribeToProgress(jobId, file.id)\r\n }, [])\r\n\r\n const handleDeletePointCloud = React.useCallback((file: DbFile) => {\r\n pointCloudDispatch({ type: 'UNLOAD_POINT_CLOUD', payload: { id: String(file.id) } })\r\n }, [pointCloudDispatch])\r\n\r\n // Use the generic file actions hook\r\n const { handleAction, deleteDialog } = useFileActions({\r\n files: pointcloudsFiles,\r\n setFiles: setPointcloudsFiles as React.Dispatch<React.SetStateAction<(DbFile & { isVisible?: boolean })[]>>,\r\n buildingId: 0,\r\n handleDeleteFile,\r\n onView: handleView,\r\n onDelete: handleDeletePointCloud,\r\n })\r\n\r\n // Sort: visible (loaded) files at the top, preserving relative order within each group\r\n const sortedFiles = React.useMemo(() => {\r\n return [...pointcloudsFiles].sort((a, b) => {\r\n const aLoaded = a.isVisible !== false ? 1 : 0\r\n const bLoaded = b.isVisible !== false ? 1 : 0\r\n return bLoaded - aLoaded\r\n })\r\n }, [pointcloudsFiles])\r\n\r\n // Create point cloud entry\r\n async function createPointCloud(name: string, uploadedBy: string | null): Promise<CreatePointCloudResponse> {\r\n const res = await fetch(`${API_BASE}/point-cloud`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ \r\n name, \r\n uploadedBy,\r\n organizationName: organization?.name,\r\n organizationId: organizationId\r\n }),\r\n })\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(`Create point-cloud failed: ${msg || res.status}`)\r\n }\r\n\r\n return res.json()\r\n }\r\n\r\n // Upload to presigned URL\r\n function uploadToPresignedUrl(\r\n url: string,\r\n file: File,\r\n onProgress?: (pct: number) => void\r\n ): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n try {\r\n const xhr = new XMLHttpRequest()\r\n xhr.open('PUT', url, true)\r\n\r\n if (onProgress) {\r\n xhr.upload.onprogress = (evt) => {\r\n if (evt.lengthComputable) {\r\n const pct = Math.round((evt.loaded / evt.total) * 100)\r\n onProgress(pct)\r\n }\r\n }\r\n }\r\n\r\n xhr.onload = () => {\r\n if (xhr.status >= 200 && xhr.status < 300) resolve()\r\n else reject(new Error(`Upload failed with status ${xhr.status}`))\r\n }\r\n\r\n xhr.onerror = () => reject(new Error('Network error during upload'))\r\n xhr.send(file)\r\n } catch (err) {\r\n reject(err)\r\n }\r\n })\r\n }\r\n\r\n // Start conversion\r\n async function startConversion(pointCloudId: string): Promise<StartConversionResponse> {\r\n const res = await fetch(\r\n `${API_BASE}/point-cloud/${encodeURIComponent(pointCloudId)}/convert-to-potree`,\r\n {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ options: { sampling_method: 'poisson' } }),\r\n }\r\n )\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(`convert-to-potree failed: ${msg || res.status}`)\r\n }\r\n\r\n return res.json()\r\n }\r\n\r\n // Subscribe to conversion progress\r\n function subscribeToProgress(jobId: string, pointCloudId: number) {\r\n if (!jobId) return\r\n\r\n if (esRef.current) {\r\n esRef.current.close()\r\n }\r\n\r\n const es = new EventSource(\r\n `${API_BASE}/events/convert-progress/${encodeURIComponent(jobId)}`\r\n )\r\n esRef.current = es\r\n\r\n es.onopen = () => console.log('SSE connection opened')\r\n\r\n es.onerror = (event) => {\r\n console.error('SSE error:', event)\r\n // Update to failed state\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, status: 'idle', conversionProgress: 0 }\r\n : pc\r\n )\r\n )\r\n }\r\n\r\n es.addEventListener('progress', (event) => {\r\n try {\r\n const msgEvent = event as MessageEvent\r\n const data = JSON.parse(msgEvent.data)\r\n\r\n console.log('[progress]', data.status, data.message, data.progress)\r\n\r\n if (typeof data.progress === 'number') {\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, conversionProgress: data.progress }\r\n : pc\r\n )\r\n )\r\n }\r\n } catch (err) {\r\n console.error('Failed to parse progress event', err)\r\n }\r\n })\r\n\r\n es.addEventListener('finished', () => {\r\n console.log('[finished] Conversion completed')\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, status: 'idle', conversionProgress: 100 }\r\n : pc\r\n )\r\n )\r\n es.close()\r\n esRef.current = null\r\n })\r\n\r\n es.addEventListener('failed', () => {\r\n console.log('[failed] Conversion failed')\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, status: 'idle', conversionProgress: 0 }\r\n : pc\r\n )\r\n )\r\n es.close()\r\n esRef.current = null\r\n })\r\n }\r\n\r\n // Handle file upload\r\n const handleFileUpload = async (file: File) => {\r\n if (!file) return\r\n\r\n const originalFileName = file.name\r\n const fileExt = originalFileName.toLowerCase()\r\n const baseFileName = originalFileName.replace(/\\.(laz|las)$/i, '')\r\n\r\n // Validate file extension\r\n if (!fileExt.endsWith('.laz') && !fileExt.endsWith('.las')) {\r\n alert('Please select a LAZ or LAS file.')\r\n return\r\n }\r\n\r\n // Check for duplicate names and append (1), (2), etc.\r\n let fileName = baseFileName\r\n let counter = 1\r\n const existingNames = pointcloudsFiles.map((pc) => pc.name)\r\n \r\n while (existingNames.includes(fileName)) {\r\n fileName = `${baseFileName} (${counter})`\r\n counter++\r\n }\r\n\r\n try {\r\n console.log('Creating point cloud entry...')\r\n const userEmail = session?.user?.email || null\r\n const { pointCloud, upload } = await createPointCloud(fileName, userEmail)\r\n console.log('Point cloud created:', pointCloud.id)\r\n\r\n console.log('Uploading file...')\r\n await uploadToPresignedUrl(upload.uploadUrl, file, (pct) => {\r\n console.log(`Upload progress: ${pct}%`)\r\n })\r\n console.log('Upload complete!')\r\n\r\n console.log('Starting conversion...')\r\n const conversion = await startConversion(pointCloud.id)\r\n console.log('Conversion started:', conversion.jobId)\r\n\r\n // Subscribe to progress\r\n subscribeToProgress(conversion.jobId, Number(pointCloud.id))\r\n } catch (error) {\r\n console.error('Error uploading point cloud:', error)\r\n \r\n alert(\r\n error instanceof Error\r\n ? `Failed to upload: ${error.message}`\r\n : 'Failed to upload point cloud'\r\n )\r\n }\r\n }\r\n\r\n // Handle upload button click\r\n const handleUpload = () => {\r\n const input = document.createElement('input')\r\n input.type = 'file'\r\n input.accept = '.laz,.las'\r\n input.onchange = (e) => {\r\n const file = (e.target as HTMLInputElement).files?.[0]\r\n if (file) {\r\n handleFileUpload(file)\r\n }\r\n }\r\n input.click()\r\n }\r\n\r\n return (\r\n <>\r\n <CollapsibleSection\r\n title={t('title')}\r\n icon={LR.Grip}\r\n className=\"h-1/2 overflow-y-auto\"\r\n itemCount={pointcloudsFiles.length}\r\n onAddItem={handleUpload}\r\n addItemTitle={t('uploadTitle')}\r\n >\r\n <div className=\"space-y-1\">\r\n {sortedFiles.length === 0 ? (\r\n <div className=\"px-2 py-3 text-sm text-muted-foreground text-center\">\r\n {t('noPointClouds')}\r\n </div>\r\n ) : (\r\n sortedFiles.map((file) => (\r\n <div key={file.id} className=\"flex items-center gap-1\">\r\n <div className=\"flex-1 min-w-0\">\r\n <FileItemComponent\r\n file={file}\r\n onAction={handleAction}\r\n options={POINT_CLOUD_OPTIONS}\r\n />\r\n </div>\r\n {Boolean(file.pointCloudUploaded) && !Boolean(file.pointCloudPotreeConverted) && (\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"h-6 w-6 p-0\"\r\n onClick={() => void handleConvert(file)}\r\n title={t('convertTitle')}\r\n >\r\n <LR.RefreshCw className=\"h-3 w-3\" />\r\n </Button>\r\n )}\r\n </div>\r\n ))\r\n )}\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <ConfirmDialog\r\n isOpen={deleteDialog.isOpen}\r\n isDeleting={deleteDialog.isDeleting}\r\n onOpenChange={deleteDialog.onOpenChange}\r\n handleConfirm={deleteDialog.onConfirm}\r\n itemName={deleteDialog.itemName}\r\n dataType=\"file\"\r\n />\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","_b"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as React from 'react'\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport { useSession } from 'next-auth/react'\r\nimport { CollapsibleSection } from '../../../../../../../ui/CollapsibleSection'\r\nimport { FileItemComponent } from '../../../../../../../ui/FilesManager/src/FileItemComponent'\r\nimport { useFileActions } from '../../../../../../../ui/FilesManager/src/useFileActions'\r\nimport ConfirmDialog from '../../../../../../../ConfirmDialog'\r\nimport { Button } from '../../../../../../../ui/Button'\r\nimport { useOrganization } from '../../../../../../../../hooks/organizations/organizations'\r\nimport { DbFile } from '../../../../../../../../types/dbTypes'\r\nimport { PointCloudContext } from '../../../../../../../../store'\r\nimport { useAppConfigContext } from '../../../../../../../../store/AppConfig/context'\r\n\r\ntype CreatePointCloudResponse = {\r\n pointCloud: {\r\n id: string\r\n name: string\r\n }\r\n upload: {\r\n uploadUrl: string\r\n }\r\n}\r\n\r\ntype StartConversionResponse = {\r\n jobId: string\r\n}\r\n\r\nconst POINT_CLOUD_OPTIONS: import('../../../../../../../../types/global').FileAction[] = ['view', 'delete', 'info']\r\n\r\ninterface PointCloudsSectionProps {\r\n files: DbFile[]\r\n}\r\n\r\nexport function PointCloudsSection({ files }: PointCloudsSectionProps) {\r\n // Translation\r\n const t = useTranslations('PointCloudManagement')\r\n const { state: appConfigState } = useAppConfigContext()\r\n const API_BASE = appConfigState.runtimeConfig.pointcloudApiUrl ?? 'http://localhost:5101'\r\n\r\n // Get current user session\r\n const { data: session } = useSession()\r\n const organizationId = session?.user?.organizationId ?? null;\r\n const { organization } = useOrganization(organizationId ? organizationId.toString() : null);\r\n\r\n // Point cloud context for loading/unloading clouds in the viewer\r\n const { dispatch: pointCloudDispatch } = React.useContext(PointCloudContext)\r\n\r\n // State for point clouds — includes isVisible to track which are loaded in viewer\r\n const [pointcloudsFiles, setPointcloudsFiles] = React.useState<(DbFile & { isVisible?: boolean })[]>(\r\n () => files.map(f => ({ ...f, isVisible: false }))\r\n )\r\n const esRef = React.useRef<EventSource | null>(null)\r\n const autoLoadedRef = React.useRef(false)\r\n\r\n const fileSyncKey = files\r\n .map(\r\n (file) =>\r\n `${file.id}:${file.name}:${Boolean(file.pointCloudUploaded)}:${Boolean(file.pointCloudPotreeConverted)}:${file.uploadedAt}:${file.potreeMetadataFileKey ?? ''}`\r\n )\r\n .join('|')\r\n\r\n // Sync incoming files from props while preserving isVisible state for known files\r\n React.useEffect(() => {\r\n setPointcloudsFiles(prev => {\r\n const prevMap = new Map(prev.map(f => [String(f.id), f]))\r\n return files.map(f => ({\r\n ...f,\r\n isVisible: prevMap.get(String(f.id))?.isVisible ?? false,\r\n }))\r\n })\r\n }, [fileSyncKey])\r\n\r\n // Auto-load the most recently uploaded converted file on first access\r\n React.useEffect(() => {\r\n if (autoLoadedRef.current || files.length === 0) return\r\n\r\n const readyFiles = files.filter(f => Boolean(f.pointCloudPotreeConverted))\r\n if (readyFiles.length === 0) return\r\n\r\n const lastFile = readyFiles.reduce((latest, f) =>\r\n new Date(f.uploadedAt) > new Date(latest.uploadedAt) ? f : latest\r\n )\r\n\r\n autoLoadedRef.current = true\r\n pointCloudDispatch({ type: 'LOAD_POINT_CLOUD', payload: { id: String(lastFile.id) } })\r\n setPointcloudsFiles(prev =>\r\n prev.map(f => ({ ...f, isVisible: String(f.id) === String(lastFile.id) }))\r\n )\r\n }, [fileSyncKey, files, pointCloudDispatch])\r\n\r\n React.useEffect(() => {\r\n // Cleanup SSE on unmount\r\n return () => {\r\n if (esRef.current) {\r\n esRef.current.close()\r\n }\r\n }\r\n }, [])\r\n\r\n // Toggle a point cloud on/off in the viewer via context\r\n const handleView = React.useCallback((file: DbFile, newVisibility: boolean) => {\r\n const id = String(file.id)\r\n if (newVisibility) {\r\n pointCloudDispatch({ type: 'LOAD_POINT_CLOUD', payload: { id } })\r\n } else {\r\n pointCloudDispatch({ type: 'UNLOAD_POINT_CLOUD', payload: { id } })\r\n }\r\n }, [pointCloudDispatch])\r\n\r\n const handleDeleteFile = React.useCallback(async (file: DbFile) => {\r\n const res = await fetch(`${API_BASE}/point-cloud/${encodeURIComponent(String(file.id))}`, {\r\n method: 'DELETE',\r\n })\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(msg || `Delete failed (${res.status})`)\r\n }\r\n }, [])\r\n\r\n const handleConvert = React.useCallback(async (file: DbFile) => {\r\n const res = await fetch(\r\n `${API_BASE}/point-cloud/${encodeURIComponent(String(file.id))}/convert-to-potree`,\r\n {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ options: { sampling_method: 'poisson' } }),\r\n }\r\n )\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(msg || `Conversion failed (${res.status})`)\r\n }\r\n\r\n const { jobId } = await res.json() as StartConversionResponse\r\n subscribeToProgress(jobId, file.id)\r\n }, [])\r\n\r\n const handleDeletePointCloud = React.useCallback((file: DbFile) => {\r\n pointCloudDispatch({ type: 'UNLOAD_POINT_CLOUD', payload: { id: String(file.id) } })\r\n }, [pointCloudDispatch])\r\n\r\n // Use the generic file actions hook\r\n const { handleAction, deleteDialog } = useFileActions({\r\n files: pointcloudsFiles,\r\n setFiles: setPointcloudsFiles as React.Dispatch<React.SetStateAction<(DbFile & { isVisible?: boolean })[]>>,\r\n buildingId: 0,\r\n handleDeleteFile,\r\n onView: handleView,\r\n onDelete: handleDeletePointCloud,\r\n })\r\n\r\n // Sort: visible (loaded) files at the top, preserving relative order within each group\r\n const sortedFiles = React.useMemo(() => {\r\n return [...pointcloudsFiles].sort((a, b) => {\r\n const aLoaded = a.isVisible !== false ? 1 : 0\r\n const bLoaded = b.isVisible !== false ? 1 : 0\r\n return bLoaded - aLoaded\r\n })\r\n }, [pointcloudsFiles])\r\n\r\n // Create point cloud entry\r\n async function createPointCloud(name: string, uploadedBy: string | null): Promise<CreatePointCloudResponse> {\r\n const res = await fetch(`${API_BASE}/point-cloud`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ \r\n name, \r\n uploadedBy,\r\n organizationName: organization?.name,\r\n organizationId: organizationId\r\n }),\r\n })\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(`Create point-cloud failed: ${msg || res.status}`)\r\n }\r\n\r\n return res.json()\r\n }\r\n\r\n // Upload to presigned URL\r\n function uploadToPresignedUrl(\r\n url: string,\r\n file: File,\r\n onProgress?: (pct: number) => void\r\n ): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n try {\r\n const xhr = new XMLHttpRequest()\r\n xhr.open('PUT', url, true)\r\n\r\n if (onProgress) {\r\n xhr.upload.onprogress = (evt) => {\r\n if (evt.lengthComputable) {\r\n const pct = Math.round((evt.loaded / evt.total) * 100)\r\n onProgress(pct)\r\n }\r\n }\r\n }\r\n\r\n xhr.onload = () => {\r\n if (xhr.status >= 200 && xhr.status < 300) resolve()\r\n else reject(new Error(`Upload failed with status ${xhr.status}`))\r\n }\r\n\r\n xhr.onerror = () => reject(new Error('Network error during upload'))\r\n xhr.send(file)\r\n } catch (err) {\r\n reject(err)\r\n }\r\n })\r\n }\r\n\r\n // Start conversion\r\n async function startConversion(pointCloudId: string): Promise<StartConversionResponse> {\r\n const res = await fetch(\r\n `${API_BASE}/point-cloud/${encodeURIComponent(pointCloudId)}/convert-to-potree`,\r\n {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ options: { sampling_method: 'poisson' } }),\r\n }\r\n )\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(`convert-to-potree failed: ${msg || res.status}`)\r\n }\r\n\r\n return res.json()\r\n }\r\n\r\n // Subscribe to conversion progress\r\n function subscribeToProgress(jobId: string, pointCloudId: number) {\r\n if (!jobId) return\r\n\r\n if (esRef.current) {\r\n esRef.current.close()\r\n }\r\n\r\n const es = new EventSource(\r\n `${API_BASE}/events/convert-progress/${encodeURIComponent(jobId)}`\r\n )\r\n esRef.current = es\r\n\r\n es.onopen = () => console.log('SSE connection opened')\r\n\r\n es.onerror = (event) => {\r\n console.error('SSE error:', event)\r\n // Update to failed state\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, status: 'idle', conversionProgress: 0 }\r\n : pc\r\n )\r\n )\r\n }\r\n\r\n es.addEventListener('progress', (event) => {\r\n try {\r\n const msgEvent = event as MessageEvent\r\n const data = JSON.parse(msgEvent.data)\r\n\r\n console.log('[progress]', data.status, data.message, data.progress)\r\n\r\n if (typeof data.progress === 'number') {\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, conversionProgress: data.progress }\r\n : pc\r\n )\r\n )\r\n }\r\n } catch (err) {\r\n console.error('Failed to parse progress event', err)\r\n }\r\n })\r\n\r\n es.addEventListener('finished', () => {\r\n console.log('[finished] Conversion completed')\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, status: 'idle', conversionProgress: 100 }\r\n : pc\r\n )\r\n )\r\n es.close()\r\n esRef.current = null\r\n })\r\n\r\n es.addEventListener('failed', () => {\r\n console.log('[failed] Conversion failed')\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, status: 'idle', conversionProgress: 0 }\r\n : pc\r\n )\r\n )\r\n es.close()\r\n esRef.current = null\r\n })\r\n }\r\n\r\n // Handle file upload\r\n const handleFileUpload = async (file: File) => {\r\n if (!file) return\r\n\r\n const originalFileName = file.name\r\n const fileExt = originalFileName.toLowerCase()\r\n const baseFileName = originalFileName.replace(/\\.(laz|las)$/i, '')\r\n\r\n // Validate file extension\r\n if (!fileExt.endsWith('.laz') && !fileExt.endsWith('.las')) {\r\n alert('Please select a LAZ or LAS file.')\r\n return\r\n }\r\n\r\n // Check for duplicate names and append (1), (2), etc.\r\n let fileName = baseFileName\r\n let counter = 1\r\n const existingNames = pointcloudsFiles.map((pc) => pc.name)\r\n \r\n while (existingNames.includes(fileName)) {\r\n fileName = `${baseFileName} (${counter})`\r\n counter++\r\n }\r\n\r\n try {\r\n console.log('Creating point cloud entry...')\r\n const userEmail = session?.user?.email || null\r\n const { pointCloud, upload } = await createPointCloud(fileName, userEmail)\r\n console.log('Point cloud created:', pointCloud.id)\r\n\r\n console.log('Uploading file...')\r\n await uploadToPresignedUrl(upload.uploadUrl, file, (pct) => {\r\n console.log(`Upload progress: ${pct}%`)\r\n })\r\n console.log('Upload complete!')\r\n\r\n console.log('Starting conversion...')\r\n const conversion = await startConversion(pointCloud.id)\r\n console.log('Conversion started:', conversion.jobId)\r\n\r\n // Subscribe to progress\r\n subscribeToProgress(conversion.jobId, Number(pointCloud.id))\r\n } catch (error) {\r\n console.error('Error uploading point cloud:', error)\r\n \r\n alert(\r\n error instanceof Error\r\n ? `Failed to upload: ${error.message}`\r\n : 'Failed to upload point cloud'\r\n )\r\n }\r\n }\r\n\r\n // Handle upload button click\r\n const handleUpload = () => {\r\n const input = document.createElement('input')\r\n input.type = 'file'\r\n input.accept = '.laz,.las'\r\n input.onchange = (e) => {\r\n const file = (e.target as HTMLInputElement).files?.[0]\r\n if (file) {\r\n handleFileUpload(file)\r\n }\r\n }\r\n input.click()\r\n }\r\n\r\n return (\r\n <>\r\n <CollapsibleSection\r\n title={t('title')}\r\n icon={LR.Grip}\r\n className=\"h-1/2 overflow-y-auto\"\r\n itemCount={pointcloudsFiles.length}\r\n onAddItem={handleUpload}\r\n addItemTitle={t('uploadTitle')}\r\n >\r\n <div className=\"space-y-1\">\r\n {sortedFiles.length === 0 ? (\r\n <div className=\"px-2 py-3 text-sm text-muted-foreground text-center\">\r\n {t('noPointClouds')}\r\n </div>\r\n ) : (\r\n sortedFiles.map((file) => (\r\n <div key={file.id} className=\"flex items-center gap-1\">\r\n <div className=\"flex-1 min-w-0\">\r\n <FileItemComponent\r\n file={file}\r\n onAction={handleAction}\r\n options={POINT_CLOUD_OPTIONS}\r\n />\r\n </div>\r\n {Boolean(file.pointCloudUploaded) && !Boolean(file.pointCloudPotreeConverted) && (\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"h-6 w-6 p-0\"\r\n onClick={() => void handleConvert(file)}\r\n title={t('convertTitle')}\r\n >\r\n <LR.RefreshCw className=\"h-3 w-3\" />\r\n </Button>\r\n )}\r\n </div>\r\n ))\r\n )}\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <ConfirmDialog\r\n isOpen={deleteDialog.isOpen}\r\n isDeleting={deleteDialog.isDeleting}\r\n onOpenChange={deleteDialog.onOpenChange}\r\n handleConfirm={deleteDialog.onConfirm}\r\n itemName={deleteDialog.itemName}\r\n dataType=\"file\"\r\n />\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","_b"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"potreeLoader.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/pointcloud/utils/potreeLoader.ts"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"potreeLoader.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/pointcloud/utils/potreeLoader.ts"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,iBAYrC;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,QAInC;AAGD,wBAAsB,aAAa,kBAyBlC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/pointcloud/utils/potreeLoader.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nexport function loadScript(src: string) {\r\n return new Promise<void>((resolve, reject) => {\r\n const s = document.createElement('script');\r\n s.src = src; s.async = false; s.onload = () => resolve(); s.onerror = reject;\r\n document.head.appendChild(s);\r\n });\r\n}\r\n\r\nexport function loadCss(href: string) {\r\n const l = document.createElement('link');\r\n l.rel = 'stylesheet'; l.href = href;\r\n document.head.appendChild(l);\r\n}\r\n\r\n\r\nexport async function loadAllAssets() {\r\n const LIB_PATH = \"/vendors/potree\"\r\n\r\n //load CSS in parralel\r\n loadCss(`${LIB_PATH}/jquery-ui/jquery-ui.min.css`);\r\n loadCss(`${LIB_PATH}/openlayers3/ol.css`);\r\n loadCss(`${LIB_PATH}/spectrum/spectrum.css`);\r\n loadCss(`${LIB_PATH}/jstree/themes/mixed/style.css`);\r\n\r\n //load scripts in order to avoid error\r\n await loadScript(`${LIB_PATH}/jquery/jquery-3.1.1.min.js`);\r\n await loadScript(`${LIB_PATH}/spectrum/spectrum.js`);\r\n await loadScript(`${LIB_PATH}/jquery-ui/jquery-ui.min.js`);\r\n await loadScript(`${LIB_PATH}/other/BinaryHeap.js`);\r\n await loadScript(`${LIB_PATH}/tween/tween.min.js`);\r\n await loadScript(`${LIB_PATH}/d3/d3.js`);\r\n await loadScript(`${LIB_PATH}/proj4/proj4.js`);\r\n await loadScript(`${LIB_PATH}/openlayers3/ol.js`);\r\n await loadScript(`${LIB_PATH}/i18next/i18next.js`);\r\n await loadScript(`${LIB_PATH}/jstree/jstree.js`);\r\n await loadScript(`${LIB_PATH}/potree/potree.js`);\r\n await loadScript(`${LIB_PATH}/plasio/js/laslaz.js`);\r\n\r\n const Potree = (window as any).Potree;\r\n if (!Potree) throw new Error('Potree failed to initialize');\r\n}"],"mappings":"AAGO,SAAS,WAAW,KAAa;AACtC,SAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,UAAM,IAAI,SAAS,cAAc,QAAQ;AACzC,MAAE,MAAM;
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/pointcloud/utils/potreeLoader.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nexport function loadScript(src: string) {\r\n return new Promise<void>((resolve, reject) => {\r\n const s = document.createElement('script');\r\n s.src = new URL(src, window.location.href).href; // ← ensure absolute\r\n s.async = false;\r\n s.onload = () => resolve();\r\n s.onerror = reject;\r\n document.head.appendChild(s);\r\n\r\n // s.src = src; s.async = false; s.onload = () => resolve(); s.onerror = reject;\r\n // document.head.appendChild(s);\r\n });\r\n}\r\n\r\nexport function loadCss(href: string) {\r\n const l = document.createElement('link');\r\n l.rel = 'stylesheet'; l.href = href;\r\n document.head.appendChild(l);\r\n}\r\n\r\n\r\nexport async function loadAllAssets() {\r\n const LIB_PATH = \"/vendors/potree\"\r\n\r\n //load CSS in parralel\r\n loadCss(`${LIB_PATH}/jquery-ui/jquery-ui.min.css`);\r\n loadCss(`${LIB_PATH}/openlayers3/ol.css`);\r\n loadCss(`${LIB_PATH}/spectrum/spectrum.css`);\r\n loadCss(`${LIB_PATH}/jstree/themes/mixed/style.css`);\r\n\r\n //load scripts in order to avoid error\r\n await loadScript(`${LIB_PATH}/jquery/jquery-3.1.1.min.js`);\r\n await loadScript(`${LIB_PATH}/spectrum/spectrum.js`);\r\n await loadScript(`${LIB_PATH}/jquery-ui/jquery-ui.min.js`);\r\n await loadScript(`${LIB_PATH}/other/BinaryHeap.js`);\r\n await loadScript(`${LIB_PATH}/tween/tween.min.js`);\r\n await loadScript(`${LIB_PATH}/d3/d3.js`);\r\n await loadScript(`${LIB_PATH}/proj4/proj4.js`);\r\n await loadScript(`${LIB_PATH}/openlayers3/ol.js`);\r\n await loadScript(`${LIB_PATH}/i18next/i18next.js`);\r\n await loadScript(`${LIB_PATH}/jstree/jstree.js`);\r\n await loadScript(`${LIB_PATH}/potree/potree.js`);\r\n await loadScript(`${LIB_PATH}/plasio/js/laslaz.js`);\r\n\r\n const Potree = (window as any).Potree;\r\n if (!Potree) throw new Error('Potree failed to initialize');\r\n}"],"mappings":"AAGO,SAAS,WAAW,KAAa;AACtC,SAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,UAAM,IAAI,SAAS,cAAc,QAAQ;AACzC,MAAE,MAAM,IAAI,IAAI,KAAK,OAAO,SAAS,IAAI,EAAE;AAC3C,MAAE,QAAQ;AACV,MAAE,SAAS,MAAM,QAAQ;AACzB,MAAE,UAAU;AACZ,aAAS,KAAK,YAAY,CAAC;AAAA,EAI7B,CAAC;AACH;AAEO,SAAS,QAAQ,MAAc;AACpC,QAAM,IAAI,SAAS,cAAc,MAAM;AACvC,IAAE,MAAM;AAAc,IAAE,OAAO;AAC/B,WAAS,KAAK,YAAY,CAAC;AAC7B;AAGA,eAAsB,gBAAgB;AAClC,QAAM,WAAW;AAGjB,UAAQ,GAAG,QAAQ,8BAA8B;AACjD,UAAQ,GAAG,QAAQ,qBAAqB;AACxC,UAAQ,GAAG,QAAQ,wBAAwB;AAC3C,UAAQ,GAAG,QAAQ,gCAAgC;AAGnD,QAAM,WAAW,GAAG,QAAQ,6BAA6B;AACzD,QAAM,WAAW,GAAG,QAAQ,uBAAuB;AACnD,QAAM,WAAW,GAAG,QAAQ,6BAA6B;AACzD,QAAM,WAAW,GAAG,QAAQ,sBAAsB;AAClD,QAAM,WAAW,GAAG,QAAQ,qBAAqB;AACjD,QAAM,WAAW,GAAG,QAAQ,WAAW;AACvC,QAAM,WAAW,GAAG,QAAQ,iBAAiB;AAC7C,QAAM,WAAW,GAAG,QAAQ,oBAAoB;AAChD,QAAM,WAAW,GAAG,QAAQ,qBAAqB;AACjD,QAAM,WAAW,GAAG,QAAQ,mBAAmB;AAC/C,QAAM,WAAW,GAAG,QAAQ,mBAAmB;AAC/C,QAAM,WAAW,GAAG,QAAQ,sBAAsB;AAElD,QAAM,SAAU,OAAe;AAC/B,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,6BAA6B;AAC9D;","names":[]}
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { AppConfigActions, AppConfigState } from './reducer';
|
|
3
|
+
export type RuntimeConfig = {
|
|
4
|
+
minioUrl?: string;
|
|
5
|
+
martinUrl?: string;
|
|
6
|
+
recaptchaSiteKey?: string;
|
|
7
|
+
pointcloudApiUrl?: string;
|
|
8
|
+
};
|
|
3
9
|
type InitialStateType = {
|
|
4
10
|
appConfig: AppConfigState;
|
|
11
|
+
runtimeConfig: RuntimeConfig;
|
|
5
12
|
};
|
|
6
13
|
export declare const AppConfigContext: React.Context<{
|
|
7
14
|
state: InitialStateType;
|
|
8
15
|
dispatch: React.Dispatch<AppConfigActions>;
|
|
9
16
|
}>;
|
|
10
|
-
export declare const AppConfigProvider: React.FC<React.PropsWithChildren
|
|
17
|
+
export declare const AppConfigProvider: React.FC<React.PropsWithChildren<{
|
|
18
|
+
runtimeConfig?: RuntimeConfig;
|
|
19
|
+
}>>;
|
|
11
20
|
export declare const useAppConfigContext: () => {
|
|
12
21
|
state: InitialStateType;
|
|
13
22
|
dispatch: React.Dispatch<AppConfigActions>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../src/core/store/AppConfig/context.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAoB,gBAAgB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAE9E,KAAK,gBAAgB,GAAG;IACtB,SAAS,EAAE,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../src/core/store/AppConfig/context.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAoB,gBAAgB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAE9E,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED,KAAK,gBAAgB,GAAG;IACtB,SAAS,EAAE,cAAc,CAAA;IACzB,aAAa,EAAE,aAAa,CAAA;CAC7B,CAAA;AAkBD,eAAO,MAAM,gBAAgB;WACpB,gBAAgB;cACb,KAAK,CAAC,QAAQ,CAAC,gBAAgB,CAAC;EAI1C,CAAA;AAEF,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC;IAAE,aAAa,CAAC,EAAE,aAAa,CAAA;CAAE,CAAC,CAQlG,CAAA;AAED,eAAO,MAAM,mBAAmB;WAjBvB,gBAAgB;cACb,KAAK,CAAC,QAAQ,CAAC,gBAAgB,CAAC;CAgB+B,CAAA"}
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
4
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
5
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
+
var __spreadValues = (a, b) => {
|
|
10
|
+
for (var prop in b || (b = {}))
|
|
11
|
+
if (__hasOwnProp.call(b, prop))
|
|
12
|
+
__defNormalProp(a, prop, b[prop]);
|
|
13
|
+
if (__getOwnPropSymbols)
|
|
14
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
15
|
+
if (__propIsEnum.call(b, prop))
|
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
|
17
|
+
}
|
|
18
|
+
return a;
|
|
19
|
+
};
|
|
20
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
2
21
|
import { jsx } from "react/jsx-runtime";
|
|
3
22
|
import * as React from "react";
|
|
4
23
|
import { AppConfigReducer } from "./reducer";
|
|
@@ -6,17 +25,19 @@ const initialState = {
|
|
|
6
25
|
appConfig: {
|
|
7
26
|
organization: null,
|
|
8
27
|
user: null
|
|
9
|
-
}
|
|
28
|
+
},
|
|
29
|
+
runtimeConfig: {}
|
|
10
30
|
};
|
|
11
|
-
const reducer = ({ appConfig }, action) => ({
|
|
12
|
-
appConfig: AppConfigReducer(appConfig, action)
|
|
31
|
+
const reducer = ({ appConfig, runtimeConfig }, action) => ({
|
|
32
|
+
appConfig: AppConfigReducer(appConfig, action),
|
|
33
|
+
runtimeConfig
|
|
13
34
|
});
|
|
14
35
|
const AppConfigContext = React.createContext({
|
|
15
36
|
state: initialState,
|
|
16
37
|
dispatch: () => null
|
|
17
38
|
});
|
|
18
|
-
const AppConfigProvider = ({ children }) => {
|
|
19
|
-
const [state, dispatch] = React.useReducer(reducer, initialState);
|
|
39
|
+
const AppConfigProvider = ({ children, runtimeConfig = {} }) => {
|
|
40
|
+
const [state, dispatch] = React.useReducer(reducer, __spreadProps(__spreadValues({}, initialState), { runtimeConfig }));
|
|
20
41
|
const value = React.useMemo(() => ({ state, dispatch }), [state]);
|
|
21
42
|
return /* @__PURE__ */ jsx(AppConfigContext.Provider, { value, children });
|
|
22
43
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/core/store/AppConfig/context.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as React from \"react\";\r\nimport { AppConfigReducer, AppConfigActions, AppConfigState } from './reducer'\r\n\r\ntype InitialStateType = {\r\n appConfig: AppConfigState\r\n}\r\n\r\nconst initialState: InitialStateType = {\r\n appConfig: {\r\n organization: null,\r\n user: null,\r\n },\r\n}\r\n\r\nconst reducer = ({ appConfig }: InitialStateType
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/store/AppConfig/context.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as React from \"react\";\r\nimport { AppConfigReducer, AppConfigActions, AppConfigState } from './reducer'\r\n\r\nexport type RuntimeConfig = {\r\n minioUrl?: string\r\n martinUrl?: string\r\n recaptchaSiteKey?: string\r\n pointcloudApiUrl?: string\r\n}\r\n\r\ntype InitialStateType = {\r\n appConfig: AppConfigState\r\n runtimeConfig: RuntimeConfig\r\n}\r\n\r\nconst initialState: InitialStateType = {\r\n appConfig: {\r\n organization: null,\r\n user: null,\r\n },\r\n runtimeConfig: {},\r\n}\r\n\r\nconst reducer = (\r\n { appConfig, runtimeConfig }: InitialStateType,\r\n action: AppConfigActions,\r\n): InitialStateType => ({\r\n appConfig: AppConfigReducer(appConfig, action),\r\n runtimeConfig,\r\n})\r\n\r\nexport const AppConfigContext = React.createContext<{\r\n state: InitialStateType\r\n dispatch: React.Dispatch<AppConfigActions>\r\n}>({\r\n state: initialState,\r\n dispatch: () => null,\r\n})\r\n\r\nexport const AppConfigProvider: React.FC<React.PropsWithChildren<{ runtimeConfig?: RuntimeConfig }>> = ({ children, runtimeConfig = {} }) => {\r\n const [state, dispatch] = React.useReducer(reducer, { ...initialState, runtimeConfig })\r\n const value = React.useMemo(() => ({ state, dispatch }), [state])\r\n return (\r\n <AppConfigContext.Provider value={value}>\r\n {children}\r\n </AppConfigContext.Provider>\r\n )\r\n}\r\n\r\nexport const useAppConfigContext = () => React.useContext(AppConfigContext)"],"mappings":";;;;;;;;;;;;;;;;;;;;AAgDI;AA3CJ,YAAY,WAAW;AACvB,SAAS,wBAA0D;AAcnE,MAAM,eAAiC;AAAA,EACrC,WAAW;AAAA,IACT,cAAc;AAAA,IACd,MAAM;AAAA,EACR;AAAA,EACA,eAAe,CAAC;AAClB;AAEA,MAAM,UAAU,CACd,EAAE,WAAW,cAAc,GAC3B,YACsB;AAAA,EACtB,WAAW,iBAAiB,WAAW,MAAM;AAAA,EAC7C;AACF;AAEO,MAAM,mBAAmB,MAAM,cAGnC;AAAA,EACD,OAAO;AAAA,EACP,UAAU,MAAM;AAClB,CAAC;AAEM,MAAM,oBAA0F,CAAC,EAAE,UAAU,gBAAgB,CAAC,EAAE,MAAM;AAC3I,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,WAAW,SAAS,iCAAK,eAAL,EAAmB,cAAc,EAAC;AACtF,QAAM,QAAQ,MAAM,QAAQ,OAAO,EAAE,OAAO,SAAS,IAAI,CAAC,KAAK,CAAC;AAChE,SACE,oBAAC,iBAAiB,UAAjB,EAA0B,OACxB,UACH;AAEJ;AAEO,MAAM,sBAAsB,MAAM,MAAM,WAAW,gBAAgB;","names":[]}
|