@collabdt/core 0.0.55 → 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.
Files changed (59) hide show
  1. package/dist/core/components/AppSidebarContent.d.ts.map +1 -1
  2. package/dist/core/components/AppSidebarContent.js +3 -3
  3. package/dist/core/components/AppSidebarContent.js.map +1 -1
  4. package/dist/core/components/authentication/AuthPage.d.ts.map +1 -1
  5. package/dist/core/components/authentication/AuthPage.js +3 -2
  6. package/dist/core/components/authentication/AuthPage.js.map +1 -1
  7. package/dist/core/components/authentication/Signin.d.ts +5 -1
  8. package/dist/core/components/authentication/Signin.d.ts.map +1 -1
  9. package/dist/core/components/authentication/Signin.js +17 -6
  10. package/dist/core/components/authentication/Signin.js.map +1 -1
  11. package/dist/core/components/settings/src/OrganizationSettingsPanel.d.ts.map +1 -1
  12. package/dist/core/components/settings/src/OrganizationSettingsPanel.js +4 -2
  13. package/dist/core/components/settings/src/OrganizationSettingsPanel.js.map +1 -1
  14. package/dist/core/components/ui/AnimatedBackground.d.ts.map +1 -1
  15. package/dist/core/components/ui/AnimatedBackground.js +3 -1
  16. package/dist/core/components/ui/AnimatedBackground.js.map +1 -1
  17. package/dist/core/components/ui/AppSidebar.d.ts.map +1 -1
  18. package/dist/core/components/ui/AppSidebar.js +11 -11
  19. package/dist/core/components/ui/AppSidebar.js.map +1 -1
  20. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
  21. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +3 -1
  22. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
  23. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts.map +1 -1
  24. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js +4 -2
  25. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js.map +1 -1
  26. package/dist/core/components/viewers/map/datasets/RowActions.d.ts.map +1 -1
  27. package/dist/core/components/viewers/map/datasets/RowActions.js +3 -1
  28. package/dist/core/components/viewers/map/datasets/RowActions.js.map +1 -1
  29. package/dist/core/components/viewers/map/datasets/index.js +2 -2
  30. package/dist/core/components/viewers/map/datasets/index.js.map +1 -1
  31. package/dist/core/components/viewers/map/datasets/src/minioDatasets.d.ts +1 -1
  32. package/dist/core/components/viewers/map/datasets/src/minioDatasets.d.ts.map +1 -1
  33. package/dist/core/components/viewers/map/datasets/src/minioDatasets.js +3 -5
  34. package/dist/core/components/viewers/map/datasets/src/minioDatasets.js.map +1 -1
  35. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts.map +1 -1
  36. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js +5 -3
  37. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js.map +1 -1
  38. package/dist/core/components/viewers/map/src/MapSidebar/src/LayersTab/index.js +1 -1
  39. package/dist/core/components/viewers/map/src/MapSidebar/src/LayersTab/index.js.map +1 -1
  40. package/dist/core/components/viewers/pointcloud/PointCloudViewer.d.ts.map +1 -1
  41. package/dist/core/components/viewers/pointcloud/PointCloudViewer.js +4 -2
  42. package/dist/core/components/viewers/pointcloud/PointCloudViewer.js.map +1 -1
  43. package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.d.ts.map +1 -1
  44. package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js +4 -2
  45. package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js.map +1 -1
  46. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudTable.d.ts.map +1 -1
  47. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudTable.js +7 -5
  48. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudTable.js.map +1 -1
  49. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.d.ts.map +1 -1
  50. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.js +15 -8
  51. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.js.map +1 -1
  52. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts.map +1 -1
  53. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js +13 -12
  54. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js.map +1 -1
  55. package/dist/core/store/AppConfig/context.d.ts +10 -1
  56. package/dist/core/store/AppConfig/context.d.ts.map +1 -1
  57. package/dist/core/store/AppConfig/context.js +26 -5
  58. package/dist/core/store/AppConfig/context.js.map +1 -1
  59. package/package.json +1 -1
@@ -1,10 +1,11 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- var _a;
4
3
  import * as React from "react";
4
+ import { useSession } from "next-auth/react";
5
+ import { useOrganization } from "../../../../../hooks/organizations/organizations";
5
6
  import { ProgressBar } from "../../../map/src/tools/AddTools/AddFile/ProgressBar";
6
7
  import { Button, Input, ScrollArea } from "../../../../ui";
7
- const API_BASE = (_a = process.env.NEXT_PUBLIC_POINTCLOUD_API_URL) != null ? _a : "http://localhost:5101";
8
+ import { useAppConfigContext } from "../../../../../store/AppConfig/context";
8
9
  const CONVERSION_STATUS = Object.freeze({
9
10
  STARTED: "conversion_started",
10
11
  DOWNLOAD: "download",
@@ -17,6 +18,12 @@ const CONVERSION_STATUS = Object.freeze({
17
18
  function UploadPointCloudPage({
18
19
  onGoBackButtonClick
19
20
  }) {
21
+ var _a, _b, _c;
22
+ const { state: appConfigState } = useAppConfigContext();
23
+ const API_BASE = (_a = appConfigState.runtimeConfig.pointcloudApiUrl) != null ? _a : "http://localhost:5101";
24
+ const { data: session } = useSession();
25
+ const organizationId = (_c = (_b = session == null ? void 0 : session.user) == null ? void 0 : _b.organizationId) != null ? _c : null;
26
+ const { organization } = useOrganization(organizationId ? organizationId.toString() : null);
20
27
  const [name, setName] = React.useState("");
21
28
  const [file, setFile] = React.useState(null);
22
29
  const [progress, setProgress] = React.useState(0);
@@ -40,7 +47,7 @@ function UploadPointCloudPage({
40
47
  const res = await fetch(`${API_BASE}/point-cloud`, {
41
48
  method: "POST",
42
49
  headers: { "Content-Type": "application/json" },
43
- body: JSON.stringify({ name: name2 })
50
+ body: JSON.stringify({ name: name2, organizationId, organizationName: organization == null ? void 0 : organization.name })
44
51
  });
45
52
  if (!res.ok) {
46
53
  const msg = await res.text();
@@ -104,15 +111,15 @@ function UploadPointCloudPage({
104
111
  );
105
112
  };
106
113
  es.addEventListener("progress", (event) => {
107
- var _a2, _b, _c;
114
+ var _a2, _b2, _c2;
108
115
  try {
109
116
  const msgEvent = event;
110
117
  const data = JSON.parse(msgEvent.data);
111
118
  appendLog(
112
119
  "[progress]",
113
120
  (_a2 = data.status) != null ? _a2 : "",
114
- (_b = data.message) != null ? _b : "",
115
- (_c = data.progress) != null ? _c : ""
121
+ (_b2 = data.message) != null ? _b2 : "",
122
+ (_c2 = data.progress) != null ? _c2 : ""
116
123
  );
117
124
  if (data.status && Object.values(CONVERSION_STATUS).includes(data.status)) {
118
125
  setStatus(data.status);
@@ -252,8 +259,8 @@ function UploadPointCloudPage({
252
259
  type: "file",
253
260
  accept: ".laz,.las",
254
261
  onChange: (e) => {
255
- var _a2, _b;
256
- const f = (_b = (_a2 = e.target.files) == null ? void 0 : _a2[0]) != null ? _b : null;
262
+ var _a2, _b2;
263
+ const f = (_b2 = (_a2 = e.target.files) == null ? void 0 : _a2[0]) != null ? _b2 : null;
257
264
  setFile(f);
258
265
  }
259
266
  }
@@ -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":";AAkTM,SAEI,KAFJ;AAlTN;AAMA,YAAY,WAAW;AAEvB,SAAS,mBAAmB;AAC5B,SAAS,QAAQ,OAAO,kBAAkB;AAE1C,MAAM,YACJ,aAAQ,IAAI,mCAAZ,YAA8C;AAEhD,MAAM,oBAAoB,OAAO,OAAO;AAAA,EACtC,SAAS;AAAA,EACT,UAAU;AAAA,EACV,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,UAAU;AACZ,CAAC;AA6Bc,SAAR,qBAAsC;AAAA,EAC3C;AACF,GAA8B;AAC5B,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,EAAE;AACzC,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAsB,IAAI;AAExD,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,CAAC;AAChD,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAA2B,MAAM;AACnE,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAwB,IAAI;AAC1E,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAC5D,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAmB,CAAC,CAAC;AAE3D,QAAM,QAAQ,MAAM,OAA2B,IAAI;AAEnD,QAAM,YAAY,MAAM,YAAY,IAAI,UAAqB;AAC3D,UAAM,OAAO,MACV,IAAI,CAAC,MAAO,OAAO,MAAM,WAAW,IAAI,KAAK,UAAU,GAAG,MAAM,CAAC,CAAE,EACnE,KAAK,GAAG;AACX,gBAAY,CAAC,SAAS,CAAC,GAAG,MAAM,IAAI,CAAC;AAAA,EACvC,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,MAAM;AACpB,WAAO,MAAM;AACX,UAAI,MAAM,SAAS;AACjB,cAAM,QAAQ,MAAM;AAAA,MACtB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,iBAAe,iBACbA,OACmC;AACnC,UAAM,MAAM,MAAM,MAAM,GAAG,QAAQ,gBAAgB;AAAA,MACjD,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,MAAAA,MAAK,CAAC;AAAA,IAC/B,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,YAAM,IAAI,MAAM,8BAA8B,OAAO,IAAI,MAAM,EAAE;AAAA,IACnE;AAEA,WAAO,IAAI,KAAK;AAAA,EAClB;AAEA,WAAS,qBACP,KACAC,OACA,YACe;AACf,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI;AACF,cAAM,MAAM,IAAI,eAAe;AAC/B,YAAI,KAAK,OAAO,KAAK,IAAI;AAEzB,YAAI,OAAO,aAAa,CAAC,QAAQ;AAC/B,cAAI,IAAI,kBAAkB;AACxB,kBAAM,MAAM,KAAK,MAAO,IAAI,SAAS,IAAI,QAAS,GAAG;AACrD,uBAAW,GAAG;AAAA,UAChB;AAAA,QACF;AAEA,YAAI,SAAS,MAAM;AACjB,cAAI,IAAI,UAAU,OAAO,IAAI,SAAS,IAAK,SAAQ;AAAA,cAC9C,QAAO,IAAI,MAAM,6BAA6B,IAAI,MAAM,EAAE,CAAC;AAAA,QAClE;AAEA,YAAI,UAAU,MAAM,OAAO,IAAI,MAAM,6BAA6B,CAAC;AACnE,YAAI,KAAKA,KAAI;AAAA,MACf,SAAS,KAAK;AACZ,eAAO,GAAG;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH;AAEA,iBAAe,gBACbC,eACkC;AAClC,UAAM,MAAM,MAAM;AAAA,MAChB,GAAG,QAAQ,gBAAgB;AAAA,QACzBA;AAAA,MACF,CAAC;AAAA,MACD;AAAA,QACE,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,SAAS,EAAE,iBAAiB,UAAU,EAAE,CAAC;AAAA,MAClE;AAAA,IACF;AAEA,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,YAAM,IAAI,MAAM,6BAA6B,OAAO,IAAI,MAAM,EAAE;AAAA,IAClE;AAEA,WAAO,IAAI,KAAK;AAAA,EAClB;AAEA,WAAS,oBAAoBC,QAAe;AAC1C,QAAI,CAACA,OAAO;AAEZ,QAAI,MAAM,SAAS;AACjB,YAAM,QAAQ,MAAM;AAAA,IACtB;AAEA,UAAM,KAAK,IAAI;AAAA,MACb,GAAG,QAAQ,4BAA4B,mBAAmBA,MAAK,CAAC;AAAA,IAClE;AACA,UAAM,UAAU;AAEhB,OAAG,SAAS,MAAM,UAAU,iCAA0B;AAEtD,OAAG,UAAU,CAAC,UAAU;AACtB,gBAAU,qBAAgB,KAAK;AAC/B;AAAA,QAAU,CAAC,SACT,SAAS,kBAAkB,WAAW,OAAO,kBAAkB;AAAA,MACjE;AAAA,IACF;AAEA,OAAG,iBAAiB,YAAY,CAAC,UAAU;AA1K/C,UAAAC,KAAA;AA2KM,UAAI;AACF,cAAM,WAAW;AACjB,cAAM,OAAO,KAAK,MAAM,SAAS,IAAI;AAErC;AAAA,UACE;AAAA,WACAA,MAAA,KAAK,WAAL,OAAAA,MAAe;AAAA,WACf,UAAK,YAAL,YAAgB;AAAA,WAChB,UAAK,aAAL,YAAiB;AAAA,QACnB;AAGA,YACE,KAAK,UACL,OAAO,OAAO,iBAAiB,EAAE,SAAS,KAAK,MAAM,GACrD;AACA,oBAAU,KAAK,MAA0B;AAAA,QAC3C;AAEA,YAAI,OAAO,KAAK,aAAa,UAAU;AACrC,sBAAY,KAAK,QAAQ;AAAA,QAC3B;AAAA,MACF,SAAS,KAAK;AACZ,kBAAU,yCAAoC,GAAG;AAAA,MACnD;AAAA,IACF,CAAC;AAED,OAAG,iBAAiB,YAAY,MAAM;AACpC,gBAAU,iCAAiC;AAC3C,gBAAU,kBAAkB,QAAQ;AACpC,kBAAY,GAAG;AACf,SAAG,MAAM;AACT,YAAM,UAAU;AAAA,IAClB,CAAC;AAED,OAAG,iBAAiB,UAAU,MAAM;AAClC,gBAAU,4BAA4B;AACtC,gBAAU,kBAAkB,MAAM;AAClC,SAAG,MAAM;AACT,YAAM,UAAU;AAAA,IAClB,CAAC;AAED,OAAG,iBAAiB,aAAa,CAAC,UAAU;AAC1C,YAAM,WAAW;AACjB,gBAAU,eAAe,SAAS,IAAI;AAAA,IACxC,CAAC;AAED,OAAG,YAAY,CAAC,UAAU;AACxB,gBAAU,aAAa,MAAM,IAAI;AAAA,IACnC;AAAA,EACF;AAEA,QAAM,eAAe,OAAO,MAAuB;AACjD,MAAE,eAAe;AACjB,QAAI,CAAC,KAAK,KAAK,KAAK,CAAC,MAAM;AACzB,YAAM,kDAAkD;AACxD;AAAA,IACF;AAEA,gBAAY,CAAC,CAAC;AACd,gBAAY,CAAC;AACb,oBAAgB,IAAI;AACpB,aAAS,IAAI;AACb,cAAU,MAAM;AAEhB,QAAI;AACF,gBAAU,sCAA+B;AACzC,gBAAU,UAAU;AAEpB,YAAM,EAAE,YAAY,OAAO,IAAI,MAAM,iBAAiB,KAAK,KAAK,CAAC;AACjE,gBAAU,UAAU;AACpB,sBAAgB,WAAW,EAAE;AAE7B,gBAAU,4CAAqC;AAC/C,gBAAU,WAAW;AACrB,YAAM;AAAA,QAAqB,OAAO;AAAA,QAAW;AAAA,QAAM,CAAC,QAClD,YAAY,GAAG;AAAA,MACjB;AACA,gBAAU,wBAAmB;AAE7B,gBAAU,qCAA8B;AAExC,gBAAU,kBAAkB,OAAO;AACnC,YAAM,aAAa,MAAM,gBAAgB,WAAW,EAAE;AACtD,gBAAU,UAAU;AAEpB,YAAMD,SAAQ,WAAW;AACzB,eAASA,MAAK;AAEd,gBAAU,uCAA2B;AACrC,0BAAoBA,MAAK;AAAA,IAC3B,SAAS,KAAK;AACZ,YAAM,UACJ,eAAe,QAAQ,IAAI,UAAU;AACvC,gBAAU,oBAAa,OAAO;AAC9B,gBAAU,kBAAkB,MAAM;AAClC,YAAM,OAAO;AAAA,IACf;AAAA,EACF;AAEA,QAAM,OACJ,WAAW,UACX,WAAW,kBAAkB,YAC7B,WAAW,kBAAkB;AAE/B,QAAM,eAAuB,MAAM;AACjC,YAAQ,QAAQ;AAAA,MACd,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MAET,KAAK,kBAAkB;AACrB,eAAO;AAAA,MACT,KAAK,kBAAkB;AACrB,eAAO;AAAA,MACT,KAAK,kBAAkB;AACrB,eAAO;AAAA,MACT,KAAK,kBAAkB;AACrB,eAAO;AAAA,MACT,KAAK,kBAAkB;AACrB,eAAO;AAAA,MACT,KAAK,kBAAkB;AACrB,eAAO;AAAA,MACT,KAAK,kBAAkB;AACrB,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF,GAAG;AAEH,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,yBAAC,SAAI,WAAU,gDACb;AAAA,0BAAC,SACC,8BAAC,QAAG,WAAU,yCAAwC,0CAEtD,GACF;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS,MAAM;AACb,gBAAI,oBAAqB,qBAAoB;AAAA,UAC/C;AAAA,UACD;AAAA;AAAA,MAED;AAAA,OACF;AAAA,IAEA,qBAAC,SAAI,WAAU,uCAEb;AAAA,2BAAC,SAAI,WAAU,iDACb;AAAA,6BAAC,SAAI,WAAU,mDACb;AAAA,8BAAC,UAAK,WAAU,iBAAgB,gCAAkB;AAAA,UAClD,oBAAC,UAAK,WAAU,iCACb,uBACH;AAAA,WACF;AAAA,QAEA,qBAAC,UAAK,UAAU,cAAc,WAAU,qBACtC;AAAA,+BAAC,SAAI,WAAU,aACb;AAAA,gCAAC,SAAI,WAAU,uBAAsB,kBAAI;AAAA,YACzC;AAAA,cAAC;AAAA;AAAA,gBACC,aAAY;AAAA,gBACZ,OAAO;AAAA,gBACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA;AAAA,YACzC;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,aACb;AAAA,gCAAC,SAAI,WAAU,uBAAsB,4BAAc;AAAA,YACnD;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,QAAO;AAAA,gBACP,UAAU,CAAC,MAAM;AA5VjC,sBAAAC,KAAA;AA6VkB,wBAAM,KAAI,MAAAA,MAAA,EAAE,OAAO,UAAT,gBAAAA,IAAiB,OAAjB,YAAuB;AACjC,0BAAQ,CAAC;AAAA,gBACX;AAAA;AAAA,YACF;AAAA,YACC,QACC,qBAAC,SAAI,WAAU,qCAAoC;AAAA;AAAA,cACvC,oBAAC,UAAK,WAAU,eAAe,eAAK,MAAK;AAAA,eACrD;AAAA,aAEJ;AAAA,UAEA,qBAAC,SAAI,WAAU,2BACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,UAAU,CAAC,KAAK,KAAK,KAAK,CAAC,QAAQ;AAAA,gBACnC,MAAK;AAAA,gBAEJ,iBAAO,eAAe;AAAA;AAAA,YACzB;AAAA,YAEA,qBAAC,SAAI,WAAU,mDACb;AAAA,mCAAC,UAAK;AAAA;AAAA,gBACW;AAAA,gBACf,oBAAC,UAAK,WAAU,WAAW,gDAAgB,UAAI;AAAA,iBACjD;AAAA,cACA,qBAAC,UAAK;AAAA;AAAA,gBACI,oBAAC,UAAK,WAAU,WAAW,kCAAS,UAAI;AAAA,iBAClD;AAAA,eACF;AAAA,aACF;AAAA,UAEA,qBAAC,SAAI,WAAU,aACb;AAAA,iCAAC,SAAI,WAAU,uEACb;AAAA,kCAAC,UAAK,0CAA4B;AAAA,cAClC,qBAAC,UAAM;AAAA;AAAA,gBAAS;AAAA,iBAAC;AAAA,eACnB;AAAA,YACA,oBAAC,eAAY,OAAO,UAAU;AAAA,aAChC;AAAA,WACF;AAAA,SACF;AAAA,MAGA,qBAAC,SAAI,WAAU,yDACb;AAAA,6BAAC,SAAI,WAAU,6CACb;AAAA,8BAAC,QAAG,WAAU,yCAAwC,4BAEtD;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,WAAU;AAAA,cACV,SAAS,MAAM,YAAY,CAAC,CAAC;AAAA,cAC9B;AAAA;AAAA,UAED;AAAA,WACF;AAAA,QAEA,oBAAC,cAAW,WAAU,0CACpB,8BAAC,SAAI,WAAU,oDACZ,mBAAS,SAAS,SAAS,KAAK,IAAI,IAAI,oBAC3C,GACF;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAEJ;","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;AAqB9D,UAAU,uBAAuB;IAC/B,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,EAAE,uBAAuB,qBA0YpE"}
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
- const API_BASE = (_a = process.env.NEXT_PUBLIC_POINTCLOUD_API_URL) != null ? _a : "http://localhost:5101";
33
+ import { useAppConfigContext } from "../../../../../../../../store/AppConfig/context";
35
34
  const POINT_CLOUD_OPTIONS = ["view", "delete", "info"];
36
35
  function PointCloudsSection({ files }) {
37
- var _a2, _b;
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 = (_b = (_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.organizationId) != null ? _b : null;
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 _a3;
51
- return `${file.id}:${file.name}:${Boolean(file.pointCloudUploaded)}:${Boolean(file.pointCloudPotreeConverted)}:${file.uploadedAt}:${(_a3 = file.potreeMetadataFileKey) != null ? _a3 : ""}`;
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 _a3, _b2;
59
+ var _a2, _b2;
59
60
  return __spreadProps(__spreadValues({}, f), {
60
- isVisible: (_b2 = (_a3 = prevMap.get(String(f.id))) == null ? void 0 : _a3.isVisible) != null ? _b2 : false
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 _a3;
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 = ((_a3 = session == null ? void 0 : session.user) == null ? void 0 : _a3.email) || null;
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 _a3;
291
- const file = (_a3 = e.target.files) == null ? void 0 : _a3[0];
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,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;CAC1B,CAAA;AAaD,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,CAQ/D,CAAA;AAED,eAAO,MAAM,mBAAmB;WAjBvB,gBAAgB;cACb,KAAK,CAAC,QAAQ,CAAC,gBAAgB,CAAC;CAgB+B,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, action: AppConfigActions) => ({\r\n appConfig: AppConfigReducer(appConfig, action),\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> = ({ children }) => {\r\n const [state, dispatch] = React.useReducer(reducer, initialState)\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":";AAmCI;AA9BJ,YAAY,WAAW;AACvB,SAAS,wBAA0D;AAMnE,MAAM,eAAiC;AAAA,EACrC,WAAW;AAAA,IACT,cAAc;AAAA,IACd,MAAM;AAAA,EACR;AACF;AAEA,MAAM,UAAU,CAAC,EAAE,UAAU,GAAqB,YAA8B;AAAA,EAC9E,WAAW,iBAAiB,WAAW,MAAM;AAC/C;AAEO,MAAM,mBAAmB,MAAM,cAGnC;AAAA,EACD,OAAO;AAAA,EACP,UAAU,MAAM;AAClB,CAAC;AAEM,MAAM,oBAAuD,CAAC,EAAE,SAAS,MAAM;AACpF,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,WAAW,SAAS,YAAY;AAChE,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":[]}
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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@collabdt/core",
3
- "version": "0.0.55",
3
+ "version": "0.0.56",
4
4
  "description": "CDT Core contains all the core technology for your CDT platform",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",