@collabdt/core 0.2.0 → 0.3.0

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 (117) hide show
  1. package/README.md +29 -21
  2. package/dist/core/components/AppSidebarContent.d.ts.map +1 -1
  3. package/dist/core/components/AppSidebarContent.js +21 -27
  4. package/dist/core/components/AppSidebarContent.js.map +1 -1
  5. package/dist/core/components/authentication/AuthPage.d.ts.map +1 -1
  6. package/dist/core/components/authentication/AuthPage.js +32 -7
  7. package/dist/core/components/authentication/AuthPage.js.map +1 -1
  8. package/dist/core/components/authentication/SignInAuthHeader.d.ts +7 -0
  9. package/dist/core/components/authentication/SignInAuthHeader.d.ts.map +1 -0
  10. package/dist/core/components/authentication/SignInAuthHeader.js +32 -0
  11. package/dist/core/components/authentication/SignInAuthHeader.js.map +1 -0
  12. package/dist/core/components/authentication/Signin.d.ts.map +1 -1
  13. package/dist/core/components/authentication/Signin.js +308 -10
  14. package/dist/core/components/authentication/Signin.js.map +1 -1
  15. package/dist/core/components/settings/index.js +3 -3
  16. package/dist/core/components/settings/index.js.map +1 -1
  17. package/dist/core/components/settings/src/OrganizationSettingsPanel.d.ts.map +1 -1
  18. package/dist/core/components/settings/src/OrganizationSettingsPanel.js +5 -4
  19. package/dist/core/components/settings/src/OrganizationSettingsPanel.js.map +1 -1
  20. package/dist/core/components/settings/src/SettingsSidebar.d.ts.map +1 -1
  21. package/dist/core/components/settings/src/SettingsSidebar.js +12 -2
  22. package/dist/core/components/settings/src/SettingsSidebar.js.map +1 -1
  23. package/dist/core/components/support/BugReportDialog.d.ts.map +1 -1
  24. package/dist/core/components/support/BugReportDialog.js +94 -13
  25. package/dist/core/components/support/BugReportDialog.js.map +1 -1
  26. package/dist/core/components/support/FeatureRequestDialog.d.ts.map +1 -1
  27. package/dist/core/components/support/FeatureRequestDialog.js +33 -6
  28. package/dist/core/components/support/FeatureRequestDialog.js.map +1 -1
  29. package/dist/core/components/ui/AppSidebar.d.ts.map +1 -1
  30. package/dist/core/components/ui/AppSidebar.js +52 -9
  31. package/dist/core/components/ui/AppSidebar.js.map +1 -1
  32. package/dist/core/components/ui/DataTable.js +3 -3
  33. package/dist/core/components/ui/DataTable.js.map +1 -1
  34. package/dist/core/components/ui/Dialog.js +1 -1
  35. package/dist/core/components/ui/Dialog.js.map +1 -1
  36. package/dist/core/components/ui/InfoSidebar/Container.js +1 -1
  37. package/dist/core/components/ui/InfoSidebar/Container.js.map +1 -1
  38. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
  39. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +2 -2
  40. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
  41. package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
  42. package/dist/core/components/ui/Sensors/SensorsSection.js +2 -1
  43. package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
  44. package/dist/core/components/ui/Sensors/sensorUtils.d.ts +9 -0
  45. package/dist/core/components/ui/Sensors/sensorUtils.d.ts.map +1 -1
  46. package/dist/core/components/ui/Sensors/sensorUtils.js +6 -0
  47. package/dist/core/components/ui/Sensors/sensorUtils.js.map +1 -1
  48. package/dist/core/components/ui/Sidebar.d.ts +4 -0
  49. package/dist/core/components/ui/Sidebar.d.ts.map +1 -1
  50. package/dist/core/components/ui/Sidebar.js +11 -5
  51. package/dist/core/components/ui/Sidebar.js.map +1 -1
  52. package/dist/core/components/ui/Table.js +2 -2
  53. package/dist/core/components/ui/Table.js.map +1 -1
  54. package/dist/core/components/viewers/Data/DataMenu.d.ts.map +1 -1
  55. package/dist/core/components/viewers/Data/DataMenu.js +8 -8
  56. package/dist/core/components/viewers/Data/DataMenu.js.map +1 -1
  57. package/dist/core/components/viewers/Data/buildingDetails/BuildingDetails.d.ts.map +1 -1
  58. package/dist/core/components/viewers/Data/buildingDetails/BuildingDetails.js +26 -0
  59. package/dist/core/components/viewers/Data/buildingDetails/BuildingDetails.js.map +1 -1
  60. package/dist/core/components/viewers/Data/details/DetailHeader.js +14 -14
  61. package/dist/core/components/viewers/Data/details/DetailHeader.js.map +1 -1
  62. package/dist/core/components/viewers/Data/details/TabSidebar.d.ts.map +1 -1
  63. package/dist/core/components/viewers/Data/details/TabSidebar.js +22 -0
  64. package/dist/core/components/viewers/Data/details/TabSidebar.js.map +1 -1
  65. package/dist/core/components/viewers/Data/infrastructureDetails/InfrastructureDetails.d.ts.map +1 -1
  66. package/dist/core/components/viewers/Data/infrastructureDetails/InfrastructureDetails.js +19 -0
  67. package/dist/core/components/viewers/Data/infrastructureDetails/InfrastructureDetails.js.map +1 -1
  68. package/dist/core/components/viewers/Data/siteDetails/SiteDetails.d.ts.map +1 -1
  69. package/dist/core/components/viewers/Data/siteDetails/SiteDetails.js +19 -0
  70. package/dist/core/components/viewers/Data/siteDetails/SiteDetails.js.map +1 -1
  71. package/dist/core/components/viewers/Data/utils/Columns.js +2 -2
  72. package/dist/core/components/viewers/Data/utils/Columns.js.map +1 -1
  73. package/dist/core/components/viewers/Data/utils/positionParser.d.ts.map +1 -1
  74. package/dist/core/components/viewers/Data/utils/positionParser.js +6 -5
  75. package/dist/core/components/viewers/Data/utils/positionParser.js.map +1 -1
  76. package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.d.ts.map +1 -1
  77. package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js +8 -1
  78. package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js.map +1 -1
  79. package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
  80. package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js +7 -1
  81. package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js.map +1 -1
  82. package/dist/core/components/viewers/map/datasets/RowActions.js +6 -6
  83. package/dist/core/components/viewers/map/datasets/RowActions.js.map +1 -1
  84. package/dist/core/components/viewers/map/datasets/data.js +1 -1
  85. package/dist/core/components/viewers/map/datasets/data.js.map +1 -1
  86. package/dist/core/components/viewers/map/datasets/index.d.ts.map +1 -1
  87. package/dist/core/components/viewers/map/datasets/index.js +28 -9
  88. package/dist/core/components/viewers/map/datasets/index.js.map +1 -1
  89. package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.d.ts.map +1 -1
  90. package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js +54 -31
  91. package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js.map +1 -1
  92. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js +1 -1
  93. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js.map +1 -1
  94. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts +2 -1
  95. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts.map +1 -1
  96. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js +5 -4
  97. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js.map +1 -1
  98. package/dist/core/hooks/useCaptureScreenshot.d.ts.map +1 -1
  99. package/dist/core/hooks/useCaptureScreenshot.js +62 -31
  100. package/dist/core/hooks/useCaptureScreenshot.js.map +1 -1
  101. package/dist/core/hooks/useCurrentViewerPosition.d.ts +18 -0
  102. package/dist/core/hooks/useCurrentViewerPosition.d.ts.map +1 -0
  103. package/dist/core/hooks/useCurrentViewerPosition.js +66 -0
  104. package/dist/core/hooks/useCurrentViewerPosition.js.map +1 -0
  105. package/dist/core/hooks/useShareUrl.d.ts +2 -0
  106. package/dist/core/hooks/useShareUrl.d.ts.map +1 -0
  107. package/dist/core/hooks/useShareUrl.js +74 -0
  108. package/dist/core/hooks/useShareUrl.js.map +1 -0
  109. package/dist/core/i18n/index.d.ts +8 -0
  110. package/dist/core/i18n/index.d.ts.map +1 -0
  111. package/dist/core/i18n/index.js +8 -0
  112. package/dist/core/i18n/index.js.map +1 -0
  113. package/dist/core/i18n/messages/en.json +1873 -0
  114. package/dist/core/i18n/messages/es.json +1870 -0
  115. package/dist/core/i18n/messages/fr.json +1870 -0
  116. package/dist/core/styles/globals.css +46 -33
  117. package/package.json +9 -5
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/core/components/support/FeatureRequestDialog.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport { useTranslations } from \"next-intl\";\r\n\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n} from \"../ui/Dialog\";\r\n\r\nimport { Button } from \"../ui/Button\";\r\nimport { Input } from \"../ui/Input\";\r\nimport { Textarea } from \"../ui/Textarea\";\r\n\r\nimport * as LR from \"lucide-react\";\r\nimport { toast } from \"sonner\";\r\n\r\nfunction getBrowserInfo(): string {\r\n const ua = navigator.userAgent;\r\n if (\"userAgentData\" in navigator) {\r\n const uaData = (navigator as any).userAgentData;\r\n const brands: Array<{ brand: string; version: string }> = uaData.brands ?? [];\r\n const brand =\r\n brands.find((b) => !b.brand.includes(\"Not\") && b.brand !== \"Chromium\") ??\r\n brands[0];\r\n const name = brand?.brand ?? \"Unknown\";\r\n const version = brand?.version ?? \"\";\r\n return version ? `${name} ${version}` : name;\r\n }\r\n const checks: Array<[string, RegExp]> = [\r\n [\"Firefox\", /Firefox\\/(\\d+)/],\r\n [\"Edge\", /Edg\\/(\\d+)/],\r\n [\"Chrome\", /Chrome\\/(\\d+)/],\r\n [\"Safari\", /Version\\/(\\d+).*Safari/],\r\n ];\r\n for (const [name, re] of checks) {\r\n const m = re.exec(ua);\r\n if (m) return `${name} ${m[1]}`;\r\n }\r\n return \"Unknown\";\r\n}\r\n\r\nfunction getDeviceInfo(): string {\r\n if (\"userAgentData\" in navigator) {\r\n return (navigator as any).userAgentData?.mobile ? \"Mobile\" : \"Desktop\";\r\n }\r\n return /Mobile|Android|iPhone|iPad|iPod/.test(navigator.userAgent)\r\n ? \"Mobile\"\r\n : \"Desktop\";\r\n}\r\n\r\ntype Props = {\r\n open: boolean;\r\n onOpenChange: (open: boolean) => void;\r\n userEmail?: string;\r\n viewer?: string;\r\n};\r\n\r\nexport function FeatureRequestDialog({ open, onOpenChange, userEmail, viewer }: Props) {\r\n const t = useTranslations(\"supportDialog\");\r\n\r\n const [title, setTitle] = React.useState(\"\");\r\n const [description, setDescription] = React.useState(\"\");\r\n const [loading, setLoading] = React.useState(false);\r\n\r\n const reset = () => {\r\n setTitle(\"\");\r\n setDescription(\"\");\r\n };\r\n\r\n const submit = async () => {\r\n if (!title.trim() || !description.trim()) return;\r\n\r\n try {\r\n setLoading(true);\r\n\r\n await fetch(\"/api/github-issue\", {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n title,\r\n description,\r\n type: \"feature-request\",\r\n labels: [\"users\"],\r\n meta: {\r\n url: window.location.href,\r\n userAgent: navigator.userAgent,\r\n browser: getBrowserInfo(),\r\n device: getDeviceInfo(),\r\n timestamp: new Date().toISOString(),\r\n viewer,\r\n userEmail,\r\n },\r\n }),\r\n });\r\n\r\n reset();\r\n onOpenChange(false);\r\n toast.success(t(\"featureSubmitSuccess\"));\r\n } catch (err) {\r\n console.error(\"Feature request failed:\", err);\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n return (\r\n <Dialog\r\n open={open}\r\n onOpenChange={(v) => {\r\n onOpenChange(v);\r\n if (!v) reset();\r\n }}\r\n >\r\n <DialogContent className=\"sm:max-w-[520px]\">\r\n <DialogHeader>\r\n <DialogTitle className=\"flex items-center gap-2\">\r\n <LR.SquarePlus className=\"h-5 w-5\" />\r\n {t(\"featureTitle\")}\r\n </DialogTitle>\r\n </DialogHeader>\r\n\r\n <div className=\"space-y-4 mt-2\">\r\n <Input\r\n placeholder={t(\"featureTitle\")}\r\n value={title}\r\n onChange={(e) => setTitle(e.target.value)}\r\n />\r\n\r\n <Textarea\r\n placeholder={t(\"descriptionPlaceholder\")}\r\n value={description}\r\n onChange={(e) => setDescription(e.target.value)}\r\n rows={5}\r\n />\r\n\r\n <div className=\"text-sm text-muted-foreground\"> {t(\"wantToBuild\")}{\" \"} \r\n <a href=\"https://docs.collabdt.org/docs/plugins/overview\" target=\"_blank\" rel=\"noreferrer\" className=\"underline hover:text-foreground\" > \r\n {t(\"pluginDocumentation\")}\r\n </a> \r\n </div>\r\n\r\n <div className=\"flex justify-end gap-2\">\r\n <Button\r\n variant=\"secondary\"\r\n onClick={() => onOpenChange(false)}\r\n disabled={loading}\r\n >\r\n {t(\"cancel\")}\r\n </Button>\r\n\r\n <Button\r\n onClick={submit}\r\n disabled={!title.trim() || !description.trim() || loading}\r\n >\r\n {loading ? \"...\" : t(\"submit\")}\r\n </Button>\r\n </div>\r\n </div>\r\n </DialogContent>\r\n </Dialog>\r\n );\r\n}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/core/components/support/FeatureRequestDialog.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport { useTranslations } from \"next-intl\";\r\n\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n} from \"../ui/Dialog\";\r\n\r\nimport { Button } from \"../ui/Button\";\r\nimport { Input } from \"../ui/Input\";\r\nimport { Textarea } from \"../ui/Textarea\";\r\n\r\nimport * as LR from \"lucide-react\";\r\nimport { toast } from \"sonner\";\r\nimport { useCurrentViewerPosition } from \"../../hooks/useCurrentViewerPosition\";\r\nimport { useShareUrl } from \"../../hooks/useShareUrl\";\r\n\r\nfunction getBrowserInfo(): string {\r\n const ua = navigator.userAgent;\r\n if (\"userAgentData\" in navigator) {\r\n const uaData = (navigator as any).userAgentData;\r\n const brands: Array<{ brand: string; version: string }> = uaData.brands ?? [];\r\n const brand =\r\n brands.find((b) => !b.brand.includes(\"Not\") && b.brand !== \"Chromium\") ??\r\n brands[0];\r\n const name = brand?.brand ?? \"Unknown\";\r\n const version = brand?.version ?? \"\";\r\n return version ? `${name} ${version}` : name;\r\n }\r\n const checks: Array<[string, RegExp]> = [\r\n [\"Firefox\", /Firefox\\/(\\d+)/],\r\n [\"Edge\", /Edg\\/(\\d+)/],\r\n [\"Chrome\", /Chrome\\/(\\d+)/],\r\n [\"Safari\", /Version\\/(\\d+).*Safari/],\r\n ];\r\n for (const [name, re] of checks) {\r\n const m = re.exec(ua);\r\n if (m) return `${name} ${m[1]}`;\r\n }\r\n return \"Unknown\";\r\n}\r\n\r\nfunction getDeviceInfo(): string {\r\n const ua = navigator.userAgent;\r\n\r\n // Mobile\r\n if (/iPhone/i.test(ua)) {\r\n return \"Mobile - iPhone\";\r\n }\r\n\r\n if (/Android/i.test(ua) && /Mobile/i.test(ua)) {\r\n return \"Mobile - Android\";\r\n }\r\n\r\n // Tablets\r\n if (/iPad/i.test(ua)) {\r\n return \"Tablet - iPad\";\r\n }\r\n\r\n if (/Android/i.test(ua) && !/Mobile/i.test(ua)) {\r\n return \"Tablet - Android\";\r\n }\r\n\r\n // Desktop OS\r\n if (/Windows NT/i.test(ua)) {\r\n return \"Desktop - Windows\";\r\n }\r\n\r\n if (/Macintosh|Mac OS X/i.test(ua)) {\r\n return \"Desktop - macOS\";\r\n }\r\n\r\n if (/CrOS/i.test(ua)) {\r\n return \"Desktop - ChromeOS\";\r\n }\r\n\r\n if (/Linux/i.test(ua)) {\r\n return \"Desktop - Linux\";\r\n }\r\n\r\n return \"Unknown\";\r\n}\r\n\r\ntype Props = {\r\n open: boolean;\r\n onOpenChange: (open: boolean) => void;\r\n userEmail?: string;\r\n viewer?: string;\r\n};\r\n\r\nexport function FeatureRequestDialog({ open, onOpenChange, userEmail, viewer }: Props) {\r\n const t = useTranslations(\"supportDialog\");\r\n const getPosition = useCurrentViewerPosition();\r\n const getShareUrl = useShareUrl();\r\n\r\n const [title, setTitle] = React.useState(\"\");\r\n const [description, setDescription] = React.useState(\"\");\r\n const [loading, setLoading] = React.useState(false);\r\n\r\n const reset = () => {\r\n setTitle(\"\");\r\n setDescription(\"\");\r\n };\r\n\r\n const submit = async () => {\r\n if (!title.trim() || !description.trim()) return;\r\n\r\n try {\r\n setLoading(true);\r\n\r\n const shareUrl = await getShareUrl();\r\n await fetch(\"/api/github-issue\", {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n title,\r\n description,\r\n type: \"feature-request\",\r\n labels: [\"users\"],\r\n meta: {\r\n url: shareUrl,\r\n userAgent: navigator.userAgent,\r\n browser: getBrowserInfo(),\r\n device: getDeviceInfo(),\r\n timestamp: new Date().toISOString(),\r\n viewer,\r\n userEmail,\r\n position: getPosition(),\r\n },\r\n }),\r\n });\r\n\r\n reset();\r\n onOpenChange(false);\r\n toast.success(t(\"featureSubmitSuccess\"));\r\n } catch (err) {\r\n console.error(\"Feature request failed:\", err);\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n return (\r\n <Dialog\r\n open={open}\r\n onOpenChange={(v) => {\r\n onOpenChange(v);\r\n if (!v) reset();\r\n }}\r\n >\r\n <DialogContent className=\"sm:max-w-[520px]\">\r\n <DialogHeader>\r\n <DialogTitle className=\"flex items-center gap-2\">\r\n <LR.SquarePlus className=\"h-5 w-5\" />\r\n {t(\"featureTitle\")}\r\n </DialogTitle>\r\n </DialogHeader>\r\n\r\n <div className=\"space-y-4 mt-2\">\r\n <Input\r\n placeholder={t(\"featureTitle\")}\r\n value={title}\r\n onChange={(e) => setTitle(e.target.value)}\r\n />\r\n\r\n <Textarea\r\n placeholder={t(\"descriptionPlaceholder\")}\r\n value={description}\r\n onChange={(e) => setDescription(e.target.value)}\r\n rows={5}\r\n />\r\n\r\n <div className=\"text-sm text-muted-foreground\"> {t(\"wantToBuild\")}{\" \"} \r\n <a href=\"https://docs.collabdt.org/docs/plugins/overview\" target=\"_blank\" rel=\"noreferrer\" className=\"underline hover:text-foreground\" > \r\n {t(\"pluginDocumentation\")}\r\n </a> \r\n </div>\r\n\r\n <div className=\"flex justify-end gap-2\">\r\n <Button\r\n variant=\"secondary\"\r\n onClick={() => onOpenChange(false)}\r\n disabled={loading}\r\n >\r\n {t(\"cancel\")}\r\n </Button>\r\n\r\n <Button\r\n onClick={submit}\r\n disabled={!title.trim() || !description.trim() || loading}\r\n >\r\n {loading ? \"...\" : t(\"submit\")}\r\n </Button>\r\n </div>\r\n </div>\r\n </DialogContent>\r\n </Dialog>\r\n );\r\n}"],"mappings":";AA4JU,SACE,KADF;AA1JV,YAAY,WAAW;AACvB,SAAS,uBAAuB;AAEhC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,cAAc;AACvB,SAAS,aAAa;AACtB,SAAS,gBAAgB;AAEzB,YAAY,QAAQ;AACpB,SAAS,aAAa;AACtB,SAAS,gCAAgC;AACzC,SAAS,mBAAmB;AAE5B,SAAS,iBAAyB;AArBlC;AAsBE,QAAM,KAAK,UAAU;AACrB,MAAI,mBAAmB,WAAW;AAChC,UAAM,SAAU,UAAkB;AAClC,UAAM,UAAoD,YAAO,WAAP,YAAiB,CAAC;AAC5E,UAAM,SACJ,YAAO,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,SAAS,KAAK,KAAK,EAAE,UAAU,UAAU,MAArE,YACA,OAAO,CAAC;AACV,UAAM,QAAO,oCAAO,UAAP,YAAgB;AAC7B,UAAM,WAAU,oCAAO,YAAP,YAAkB;AAClC,WAAO,UAAU,GAAG,IAAI,IAAI,OAAO,KAAK;AAAA,EAC1C;AACA,QAAM,SAAkC;AAAA,IACtC,CAAC,WAAW,gBAAgB;AAAA,IAC5B,CAAC,QAAQ,YAAY;AAAA,IACrB,CAAC,UAAU,eAAe;AAAA,IAC1B,CAAC,UAAU,wBAAwB;AAAA,EACrC;AACA,aAAW,CAAC,MAAM,EAAE,KAAK,QAAQ;AAC/B,UAAM,IAAI,GAAG,KAAK,EAAE;AACpB,QAAI,EAAG,QAAO,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC;AAAA,EAC/B;AACA,SAAO;AACT;AAEA,SAAS,gBAAwB;AAC/B,QAAM,KAAK,UAAU;AAGrB,MAAI,UAAU,KAAK,EAAE,GAAG;AACtB,WAAO;AAAA,EACT;AAEA,MAAI,WAAW,KAAK,EAAE,KAAK,UAAU,KAAK,EAAE,GAAG;AAC7C,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,KAAK,EAAE,GAAG;AACpB,WAAO;AAAA,EACT;AAEA,MAAI,WAAW,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,EAAE,GAAG;AAC9C,WAAO;AAAA,EACT;AAGA,MAAI,cAAc,KAAK,EAAE,GAAG;AAC1B,WAAO;AAAA,EACT;AAEA,MAAI,sBAAsB,KAAK,EAAE,GAAG;AAClC,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,KAAK,EAAE,GAAG;AACpB,WAAO;AAAA,EACT;AAEA,MAAI,SAAS,KAAK,EAAE,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AASO,SAAS,qBAAqB,EAAE,MAAM,cAAc,WAAW,OAAO,GAAU;AACrF,QAAM,IAAI,gBAAgB,eAAe;AACzC,QAAM,cAAc,yBAAyB;AAC7C,QAAM,cAAc,YAAY;AAEhC,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAS,EAAE;AAC3C,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,EAAE;AACvD,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAElD,QAAM,QAAQ,MAAM;AAClB,aAAS,EAAE;AACX,mBAAe,EAAE;AAAA,EACnB;AAEA,QAAM,SAAS,YAAY;AACzB,QAAI,CAAC,MAAM,KAAK,KAAK,CAAC,YAAY,KAAK,EAAG;AAE1C,QAAI;AACF,iBAAW,IAAI;AAEf,YAAM,WAAW,MAAM,YAAY;AACnC,YAAM,MAAM,qBAAqB;AAAA,QAC/B,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU;AAAA,UACnB;AAAA,UACA;AAAA,UACA,MAAM;AAAA,UACN,QAAQ,CAAC,OAAO;AAAA,UAChB,MAAM;AAAA,YACJ,KAAK;AAAA,YACL,WAAW,UAAU;AAAA,YACrB,SAAS,eAAe;AAAA,YACxB,QAAQ,cAAc;AAAA,YACtB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,YAClC;AAAA,YACA;AAAA,YACA,UAAU,YAAY;AAAA,UACxB;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAED,YAAM;AACN,mBAAa,KAAK;AAClB,YAAM,QAAQ,EAAE,sBAAsB,CAAC;AAAA,IACzC,SAAS,KAAK;AACZ,cAAQ,MAAM,2BAA2B,GAAG;AAAA,IAC9C,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,cAAc,CAAC,MAAM;AACnB,qBAAa,CAAC;AACd,YAAI,CAAC,EAAG,OAAM;AAAA,MAChB;AAAA,MAEA,+BAAC,iBAAc,WAAU,oBACvB;AAAA,4BAAC,gBACC,+BAAC,eAAY,WAAU,2BACrB;AAAA,8BAAC,GAAG,YAAH,EAAc,WAAU,WAAU;AAAA,UAClC,EAAE,cAAc;AAAA,WACnB,GACF;AAAA,QAEA,qBAAC,SAAI,WAAU,kBACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,aAAa,EAAE,cAAc;AAAA,cAC7B,OAAO;AAAA,cACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA;AAAA,UAC1C;AAAA,UAEA;AAAA,YAAC;AAAA;AAAA,cACC,aAAa,EAAE,wBAAwB;AAAA,cACvC,OAAO;AAAA,cACP,UAAU,CAAC,MAAM,eAAe,EAAE,OAAO,KAAK;AAAA,cAC9C,MAAM;AAAA;AAAA,UACR;AAAA,UAEA,qBAAC,SAAI,WAAU,iCAAgC;AAAA;AAAA,YAAE,EAAE,aAAa;AAAA,YAAG;AAAA,YACjE,oBAAC,OAAE,MAAK,mDAAkD,QAAO,UAAS,KAAI,cAAa,WAAU,mCACpG,YAAE,qBAAqB,GACvB;AAAA,aACD;AAAA,UAEF,qBAAC,SAAI,WAAU,0BACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,SAAS,MAAM,aAAa,KAAK;AAAA,gBACjC,UAAU;AAAA,gBAET,YAAE,QAAQ;AAAA;AAAA,YACb;AAAA,YAEA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS;AAAA,gBACT,UAAU,CAAC,MAAM,KAAK,KAAK,CAAC,YAAY,KAAK,KAAK;AAAA,gBAEjD,oBAAU,QAAQ,EAAE,QAAQ;AAAA;AAAA,YAC/B;AAAA,aACF;AAAA,WACF;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"AppSidebar.d.ts","sourceRoot":"","sources":["../../../../src/core/components/ui/AppSidebar.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAY9B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAGzC,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,WAAW,EACnB,iBAAiB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAC1D,iBAAiB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAC1D,iBAAiB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAC1D,SAAS,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,GAAG,QAAQ,CAAC,CAAC,EACjE,eAAe,GAAG,SAYnB,CAAA;AAED,UAAU,eAAe;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAClF;AAED,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,eAAe,qBAmFhE"}
1
+ {"version":3,"file":"AppSidebar.d.ts","sourceRoot":"","sources":["../../../../src/core/components/ui/AppSidebar.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAa9B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAKzC,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,WAAW,EACnB,iBAAiB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAC1D,iBAAiB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAC1D,iBAAiB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAC1D,SAAS,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,GAAG,QAAQ,CAAC,CAAC,EACjE,eAAe,GAAG,SAYnB,CAAA;AAED,UAAU,eAAe;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAClF;AAED,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,eAAe,qBA6GhE"}
@@ -3,11 +3,14 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { Button } from "./Button";
4
4
  import * as LR from "lucide-react";
5
5
  import { useTranslations } from "next-intl";
6
+ import { useSession } from "next-auth/react";
6
7
  import { CdtIcon } from "./Icons/CdtIcon";
7
8
  import Link from "next/link";
8
9
  import { Sidebar, useSidebar, NavUser } from "./";
9
10
  import { ViewerNames } from "../../types";
10
11
  import { useMenusContext } from "../../store";
12
+ import { BugReportDialog } from "../support/BugReportDialog";
13
+ import { FeatureRequestDialog } from "../support/FeatureRequestDialog";
11
14
  const handleChangeViewer = (viewer, setSelectedItem, setSelectedSite, setSelectedFile, setView, menusDispatch) => {
12
15
  setSelectedItem(null);
13
16
  setSelectedSite(null);
@@ -19,22 +22,44 @@ const handleChangeViewer = (viewer, setSelectedItem, setSelectedSite, setSelecte
19
22
  });
20
23
  };
21
24
  function AppSidebar({ children, signOut }) {
25
+ var _a, _b, _c, _d;
22
26
  const t = useTranslations("AppSidebar");
23
27
  const { state: menusState } = useMenusContext();
24
- const { sidebarState, isMobile, openMobile, toggleMenuSidebar } = useSidebar();
28
+ const { data: session } = useSession();
29
+ const {
30
+ sidebarState,
31
+ isMobile,
32
+ openMobile,
33
+ toggleMenuSidebar,
34
+ bugReportOpen,
35
+ setBugReportOpen,
36
+ featureRequestOpen,
37
+ setFeatureRequestOpen
38
+ } = useSidebar();
25
39
  const { currentViewer } = menusState.menus;
26
40
  const isCollapsed = isMobile ? !openMobile : sidebarState === "collapsed";
27
41
  const collapsibleType = currentViewer === ViewerNames.auth ? "offcanvas" : "icon";
28
42
  return /* @__PURE__ */ jsxs(Fragment, { children: [
29
- isMobile && isCollapsed && /* @__PURE__ */ jsx("div", { id: "sidebar-toggle-button-mobile", className: "fixed left-0 top-1/2 -translate-y-1/2 z-[100] flex items-center justify-center pointer-events-auto", children: /* @__PURE__ */ jsx(
30
- "button",
43
+ isMobile && isCollapsed && /* @__PURE__ */ jsx(
44
+ "div",
31
45
  {
32
- onClick: toggleMenuSidebar,
33
- className: "text-muted-foreground hover:text-primary p-[2px] rounded-r-md bg-sidebar",
34
- style: { border: "1px solid rgba(128,128,128,0.2)", borderLeft: "none", boxShadow: "4px 0 8px -4px rgba(0,0,0,0.12)" },
35
- children: /* @__PURE__ */ jsx(LR.ChevronRight, { className: "w-5" })
46
+ id: "sidebar-toggle-button-mobile",
47
+ className: "fixed left-0 top-[93dvh] z-[100] flex items-center justify-center pointer-events-auto",
48
+ children: /* @__PURE__ */ jsx(
49
+ "button",
50
+ {
51
+ onClick: toggleMenuSidebar,
52
+ className: "text-muted-foreground hover:text-primary px-2 py-1.5 rounded-r-lg bg-sidebar",
53
+ style: {
54
+ border: "1px solid rgba(128,128,128,0.2)",
55
+ borderLeft: "none",
56
+ boxShadow: "4px 0 8px -4px rgba(0,0,0,0.12)"
57
+ },
58
+ children: /* @__PURE__ */ jsx(LR.ChevronRight, { className: "w-6 h-6" })
59
+ }
60
+ )
36
61
  }
37
- ) }),
62
+ ),
38
63
  /* @__PURE__ */ jsxs(Sidebar, { collapsible: collapsibleType, className: "border-none z-50 shadow-md relative", children: [
39
64
  sidebarState === "expanded" && /* @__PURE__ */ jsx("div", { id: "sidebar-toggle-button", className: `absolute right-2 top-2 flex items-center justify-center pointer-events-auto z-50 ${isMobile ? "" : ""}`, children: /* @__PURE__ */ jsx("button", { onClick: toggleMenuSidebar, className: "text-muted-foreground hover:text-primary p-1", children: /* @__PURE__ */ jsx(LR.X, { className: "w-5" }) }) }),
40
65
  !isMobile && sidebarState !== "expanded" && /* @__PURE__ */ jsx("div", { id: "sidebar-toggle-button", className: "absolute -right-1 max-h-1 top-1/2 -translate-y-1/2 flex items-center justify-center pointer-events-auto z-50", children: /* @__PURE__ */ jsx(
@@ -77,7 +102,25 @@ function AppSidebar({ children, signOut }) {
77
102
  ]
78
103
  }
79
104
  )
80
- ] })
105
+ ] }),
106
+ /* @__PURE__ */ jsx(
107
+ BugReportDialog,
108
+ {
109
+ open: bugReportOpen,
110
+ onOpenChange: setBugReportOpen,
111
+ userEmail: (_b = (_a = session == null ? void 0 : session.user) == null ? void 0 : _a.email) != null ? _b : void 0,
112
+ viewer: currentViewer
113
+ }
114
+ ),
115
+ /* @__PURE__ */ jsx(
116
+ FeatureRequestDialog,
117
+ {
118
+ open: featureRequestOpen,
119
+ onOpenChange: setFeatureRequestOpen,
120
+ userEmail: (_d = (_c = session == null ? void 0 : session.user) == null ? void 0 : _c.email) != null ? _d : void 0,
121
+ viewer: currentViewer
122
+ }
123
+ )
81
124
  ] });
82
125
  }
83
126
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/core/components/ui/AppSidebar.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\n\r\n// Dependencies\r\nimport { Button } from './Button'\r\n\r\n// Icons\r\nimport * as LR from 'lucide-react'\r\n\r\nimport { useTranslations } from 'next-intl'\r\nimport { CdtIcon } from './Icons/CdtIcon'\r\nimport Link from 'next/link'\r\nimport { Sidebar, useSidebar, NavUser } from './'\r\nimport { ViewerNames } from '../../types'\r\nimport { useMenusContext } from '../../store'\r\n\r\nexport const handleChangeViewer = (\r\n viewer: ViewerNames,\r\n setSelectedItem: React.Dispatch<React.SetStateAction<any>>,\r\n setSelectedSite: React.Dispatch<React.SetStateAction<any>>,\r\n setSelectedFile: React.Dispatch<React.SetStateAction<any>>,\r\n setView: React.Dispatch<React.SetStateAction<'table' | 'detail'>>,\r\n menusDispatch: any,\r\n) => {\r\n // Reset selected item, file, & view when changing viewer\r\n setSelectedItem(null)\r\n setSelectedSite(null)\r\n setSelectedFile(null)\r\n setView('table')\r\n\r\n menusDispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: viewer },\r\n })\r\n}\r\n\r\ninterface AppSidebarProps {\r\n children?: React.ReactNode\r\n signOut: (options?: { redirectTo?: string; redirect?: boolean }) => Promise<void>\r\n}\r\n\r\nexport function AppSidebar({ children, signOut }: AppSidebarProps) {\r\n // Translations\r\n const t = useTranslations('AppSidebar')\r\n\r\n const { state: menusState } = useMenusContext()\r\n\r\n const { sidebarState, isMobile, openMobile, toggleMenuSidebar } = useSidebar()\r\n\r\n const { currentViewer } = menusState.menus\r\n\r\n // On mobile, treat the sheet open as expanded so labels render when the drawer is visible\r\n const isCollapsed = isMobile ? !openMobile : sidebarState === 'collapsed'\r\n\r\n // Determine the collapsible type based on the current viewer\r\n const collapsibleType = currentViewer === ViewerNames.auth ? 'offcanvas' : 'icon'\r\n\r\n return (\r\n <>\r\n {/* Always show toggle button on mobile when collapsed */}\r\n {isMobile && isCollapsed && (\r\n <div id='sidebar-toggle-button-mobile' className='fixed left-0 top-1/2 -translate-y-1/2 z-[100] flex items-center justify-center pointer-events-auto'>\r\n <button\r\n onClick={toggleMenuSidebar}\r\n className=\"text-muted-foreground hover:text-primary p-[2px] rounded-r-md bg-sidebar\"\r\n style={{ border: '1px solid rgba(128,128,128,0.2)', borderLeft: 'none', boxShadow: '4px 0 8px -4px rgba(0,0,0,0.12)' }}\r\n >\r\n <LR.ChevronRight className=\"w-5\" />\r\n </button>\r\n </div>\r\n )}\r\n <Sidebar collapsible={collapsibleType} className=\"border-none z-50 shadow-md relative\" >\r\n {/* Sidebar toggle button - right side inside Sidebar */}\r\n {(sidebarState === 'expanded') && (\r\n <div id='sidebar-toggle-button' className={`absolute right-2 top-2 flex items-center justify-center pointer-events-auto z-50 ${isMobile ? '' : ''}`}>\r\n <button onClick={toggleMenuSidebar} className='text-muted-foreground hover:text-primary p-1'>\r\n <LR.X className='w-5' />\r\n </button>\r\n </div>\r\n )}\r\n {/* Show collapsed toggle only on desktop */}\r\n {!isMobile && sidebarState !== 'expanded' && (\r\n <div id='sidebar-toggle-button' className='absolute -right-1 max-h-1 top-1/2 -translate-y-1/2 flex items-center justify-center pointer-events-auto z-50'>\r\n <button\r\n onClick={toggleMenuSidebar}\r\n className=\"text-muted-foreground hover:text-primary p-0 rounded-md bg-sidebar\"\r\n style={{ borderRight: '1px solid rgba(128,128,128,0.2)', boxShadow: '4px 0 8px -4px rgba(0,0,0,0.12)' }}\r\n >\r\n <LR.ChevronRight className=\"w-4\" />\r\n </button>\r\n </div>\r\n )}\r\n {children}\r\n <div\r\n className={`w-full flex flex-col ${\r\n sidebarState === 'expanded'\r\n ? 'items-center justify-center'\r\n : 'justify-center pb-4'\r\n }`}\r\n >\r\n {/* Nav User */}\r\n <NavUser signOut={signOut} />\r\n\r\n {!isCollapsed ? (\r\n <Link\r\n href=\"https://collabdt.org/\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"w-full\"\r\n >\r\n <Button\r\n title={t('home')}\r\n variant=\"ghost\"\r\n className=\"text-xs scale-[85%] py-0 my-0 w-full flex flex-row items-center justify-center gap-2\"\r\n >\r\n <CdtIcon monochromatic />\r\n <span>{t('home')}</span>\r\n </Button>\r\n </Link>\r\n ) : null}\r\n </div>\r\n </Sidebar>\r\n </>\r\n )\r\n}"],"mappings":";AA8DI,mBASQ,KA2CI,YApDZ;AAtDJ,SAAS,cAAc;AAGvB,YAAY,QAAQ;AAEpB,SAAS,uBAAuB;AAChC,SAAS,eAAe;AACxB,OAAO,UAAU;AACjB,SAAS,SAAS,YAAY,eAAe;AAC7C,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAEzB,MAAM,qBAAqB,CAChC,QACA,iBACA,iBACA,iBACA,SACA,kBACG;AAEH,kBAAgB,IAAI;AACpB,kBAAgB,IAAI;AACpB,kBAAgB,IAAI;AACpB,UAAQ,OAAO;AAEf,gBAAc;AAAA,IACZ,MAAM;AAAA,IACN,SAAS,EAAE,eAAe,OAAO;AAAA,EACnC,CAAC;AACH;AAOO,SAAS,WAAW,EAAE,UAAU,QAAQ,GAAoB;AAEjE,QAAM,IAAI,gBAAgB,YAAY;AAEtC,QAAM,EAAE,OAAO,WAAW,IAAI,gBAAgB;AAE9C,QAAM,EAAE,cAAc,UAAU,YAAY,kBAAkB,IAAI,WAAW;AAE7E,QAAM,EAAE,cAAc,IAAI,WAAW;AAGrC,QAAM,cAAc,WAAW,CAAC,aAAa,iBAAiB;AAG9D,QAAM,kBAAkB,kBAAkB,YAAY,OAAO,cAAc;AAE3E,SACE,iCAEG;AAAA,gBAAY,eACX,oBAAC,SAAI,IAAG,gCAA+B,WAAU,sGAC/C;AAAA,MAAC;AAAA;AAAA,QACC,SAAS;AAAA,QACT,WAAU;AAAA,QACV,OAAO,EAAE,QAAQ,mCAAmC,YAAY,QAAQ,WAAW,kCAAkC;AAAA,QAErH,8BAAC,GAAG,cAAH,EAAgB,WAAU,OAAM;AAAA;AAAA,IACnC,GACF;AAAA,IAEF,qBAAC,WAAQ,aAAa,iBAAiB,WAAU,uCAE7C;AAAA,uBAAiB,cACjB,oBAAC,SAAI,IAAG,yBAAwB,WAAW,oFAAoF,WAAW,KAAK,EAAE,IAC/I,8BAAC,YAAO,SAAS,mBAAmB,WAAU,gDAC5C,8BAAC,GAAG,GAAH,EAAK,WAAU,OAAM,GACxB,GACF;AAAA,MAGD,CAAC,YAAY,iBAAiB,cAC7B,oBAAC,SAAI,IAAG,yBAAwB,WAAU,gHACxC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAU;AAAA,UACV,OAAO,EAAE,aAAa,mCAAmC,WAAW,kCAAkC;AAAA,UAEtG,8BAAC,GAAG,cAAH,EAAgB,WAAU,OAAM;AAAA;AAAA,MACnC,GACF;AAAA,MAED;AAAA,MACD;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,wBACT,iBAAiB,aACb,gCACA,qBACN;AAAA,UAGE;AAAA,gCAAC,WAAQ,SAAkB;AAAA,YAE1B,CAAC,cACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,QAAO;AAAA,gBACP,KAAI;AAAA,gBACJ,WAAU;AAAA,gBAEV;AAAA,kBAAC;AAAA;AAAA,oBACC,OAAO,EAAE,MAAM;AAAA,oBACf,SAAQ;AAAA,oBACR,WAAU;AAAA,oBAEV;AAAA,0CAAC,WAAQ,eAAa,MAAC;AAAA,sBACvB,oBAAC,UAAM,YAAE,MAAM,GAAE;AAAA;AAAA;AAAA,gBACnB;AAAA;AAAA,YACF,IACE;AAAA;AAAA;AAAA,MACN;AAAA,OACJ;AAAA,KACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/core/components/ui/AppSidebar.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\n\r\n// Dependencies\r\nimport { Button } from './Button'\r\n\r\n// Icons\r\nimport * as LR from 'lucide-react'\r\n\r\nimport { useTranslations } from 'next-intl'\r\nimport { useSession } from 'next-auth/react'\r\nimport { CdtIcon } from './Icons/CdtIcon'\r\nimport Link from 'next/link'\r\nimport { Sidebar, useSidebar, NavUser } from './'\r\nimport { ViewerNames } from '../../types'\r\nimport { useMenusContext } from '../../store'\r\nimport { BugReportDialog } from '../support/BugReportDialog'\r\nimport { FeatureRequestDialog } from '../support/FeatureRequestDialog'\r\n\r\nexport const handleChangeViewer = (\r\n viewer: ViewerNames,\r\n setSelectedItem: React.Dispatch<React.SetStateAction<any>>,\r\n setSelectedSite: React.Dispatch<React.SetStateAction<any>>,\r\n setSelectedFile: React.Dispatch<React.SetStateAction<any>>,\r\n setView: React.Dispatch<React.SetStateAction<'table' | 'detail'>>,\r\n menusDispatch: any,\r\n) => {\r\n // Reset selected item, file, & view when changing viewer\r\n setSelectedItem(null)\r\n setSelectedSite(null)\r\n setSelectedFile(null)\r\n setView('table')\r\n\r\n menusDispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: viewer },\r\n })\r\n}\r\n\r\ninterface AppSidebarProps {\r\n children?: React.ReactNode\r\n signOut: (options?: { redirectTo?: string; redirect?: boolean }) => Promise<void>\r\n}\r\n\r\nexport function AppSidebar({ children, signOut }: AppSidebarProps) {\r\n // Translations\r\n const t = useTranslations('AppSidebar')\r\n\r\n const { state: menusState } = useMenusContext()\r\n const { data: session } = useSession()\r\n\r\n const {\r\n sidebarState, isMobile, openMobile, toggleMenuSidebar,\r\n bugReportOpen, setBugReportOpen, featureRequestOpen, setFeatureRequestOpen,\r\n } = useSidebar()\r\n\r\n const { currentViewer } = menusState.menus\r\n\r\n // On mobile, treat the sheet open as expanded so labels render when the drawer is visible\r\n const isCollapsed = isMobile ? !openMobile : sidebarState === 'collapsed'\r\n\r\n // Determine the collapsible type based on the current viewer\r\n const collapsibleType = currentViewer === ViewerNames.auth ? 'offcanvas' : 'icon'\r\n\r\n return (\r\n <>\r\n {/* Always show toggle button on mobile when collapsed */}\r\n {isMobile && isCollapsed && (\r\n <div\r\n id=\"sidebar-toggle-button-mobile\"\r\n className=\"fixed left-0 top-[93dvh] z-[100] flex items-center justify-center pointer-events-auto\"\r\n >\r\n <button\r\n onClick={toggleMenuSidebar}\r\n className=\"text-muted-foreground hover:text-primary px-2 py-1.5 rounded-r-lg bg-sidebar\"\r\n style={{\r\n border: '1px solid rgba(128,128,128,0.2)',\r\n borderLeft: 'none',\r\n boxShadow: '4px 0 8px -4px rgba(0,0,0,0.12)',\r\n }}\r\n >\r\n <LR.ChevronRight className=\"w-6 h-6\" />\r\n </button>\r\n </div>\r\n )}\r\n <Sidebar collapsible={collapsibleType} className=\"border-none z-50 shadow-md relative\" >\r\n {/* Sidebar toggle button - right side inside Sidebar */}\r\n {(sidebarState === 'expanded') && (\r\n <div id='sidebar-toggle-button' className={`absolute right-2 top-2 flex items-center justify-center pointer-events-auto z-50 ${isMobile ? '' : ''}`}>\r\n <button onClick={toggleMenuSidebar} className='text-muted-foreground hover:text-primary p-1'>\r\n <LR.X className='w-5' />\r\n </button>\r\n </div>\r\n )}\r\n {/* Show collapsed toggle only on desktop */}\r\n {!isMobile && sidebarState !== 'expanded' && (\r\n <div id='sidebar-toggle-button' className='absolute -right-1 max-h-1 top-1/2 -translate-y-1/2 flex items-center justify-center pointer-events-auto z-50'>\r\n <button\r\n onClick={toggleMenuSidebar}\r\n className=\"text-muted-foreground hover:text-primary p-0 rounded-md bg-sidebar\"\r\n style={{ borderRight: '1px solid rgba(128,128,128,0.2)', boxShadow: '4px 0 8px -4px rgba(0,0,0,0.12)' }}\r\n >\r\n <LR.ChevronRight className=\"w-4\" />\r\n </button>\r\n </div>\r\n )}\r\n {children}\r\n <div\r\n className={`w-full flex flex-col ${\r\n sidebarState === 'expanded'\r\n ? 'items-center justify-center'\r\n : 'justify-center pb-4'\r\n }`}\r\n >\r\n {/* Nav User */}\r\n <NavUser signOut={signOut} />\r\n\r\n {!isCollapsed ? (\r\n <Link\r\n href=\"https://collabdt.org/\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"w-full\"\r\n >\r\n <Button\r\n title={t('home')}\r\n variant=\"ghost\"\r\n className=\"text-xs scale-[85%] py-0 my-0 w-full flex flex-row items-center justify-center gap-2\"\r\n >\r\n <CdtIcon monochromatic />\r\n <span>{t('home')}</span>\r\n </Button>\r\n </Link>\r\n ) : null}\r\n </div>\r\n </Sidebar>\r\n\r\n {/* Rendered outside <Sidebar> (and its mobile Sheet) so they survive the\r\n Sheet closing — see the SidebarContext comment for why. */}\r\n <BugReportDialog\r\n open={bugReportOpen}\r\n onOpenChange={setBugReportOpen}\r\n userEmail={session?.user?.email ?? undefined}\r\n viewer={currentViewer}\r\n />\r\n <FeatureRequestDialog\r\n open={featureRequestOpen}\r\n onOpenChange={setFeatureRequestOpen}\r\n userEmail={session?.user?.email ?? undefined}\r\n viewer={currentViewer}\r\n />\r\n </>\r\n )\r\n}"],"mappings":";AAqEI,mBAgBQ,KA2CI,YA3DZ;AA7DJ,SAAS,cAAc;AAGvB,YAAY,QAAQ;AAEpB,SAAS,uBAAuB;AAChC,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,OAAO,UAAU;AACjB,SAAS,SAAS,YAAY,eAAe;AAC7C,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAChC,SAAS,uBAAuB;AAChC,SAAS,4BAA4B;AAE9B,MAAM,qBAAqB,CAChC,QACA,iBACA,iBACA,iBACA,SACA,kBACG;AAEH,kBAAgB,IAAI;AACpB,kBAAgB,IAAI;AACpB,kBAAgB,IAAI;AACpB,UAAQ,OAAO;AAEf,gBAAc;AAAA,IACZ,MAAM;AAAA,IACN,SAAS,EAAE,eAAe,OAAO;AAAA,EACnC,CAAC;AACH;AAOO,SAAS,WAAW,EAAE,UAAU,QAAQ,GAAoB;AAhDnE;AAkDE,QAAM,IAAI,gBAAgB,YAAY;AAEtC,QAAM,EAAE,OAAO,WAAW,IAAI,gBAAgB;AAC9C,QAAM,EAAE,MAAM,QAAQ,IAAI,WAAW;AAErC,QAAM;AAAA,IACJ;AAAA,IAAc;AAAA,IAAU;AAAA,IAAY;AAAA,IACpC;AAAA,IAAe;AAAA,IAAkB;AAAA,IAAoB;AAAA,EACvD,IAAI,WAAW;AAEf,QAAM,EAAE,cAAc,IAAI,WAAW;AAGrC,QAAM,cAAc,WAAW,CAAC,aAAa,iBAAiB;AAG9D,QAAM,kBAAkB,kBAAkB,YAAY,OAAO,cAAc;AAE3E,SACE,iCAEG;AAAA,gBAAY,eACX;AAAA,MAAC;AAAA;AAAA,QACC,IAAG;AAAA,QACH,WAAU;AAAA,QAEV;AAAA,UAAC;AAAA;AAAA,YACC,SAAS;AAAA,YACT,WAAU;AAAA,YACV,OAAO;AAAA,cACL,QAAQ;AAAA,cACR,YAAY;AAAA,cACZ,WAAW;AAAA,YACb;AAAA,YAEA,8BAAC,GAAG,cAAH,EAAgB,WAAU,WAAU;AAAA;AAAA,QACvC;AAAA;AAAA,IACF;AAAA,IAEF,qBAAC,WAAQ,aAAa,iBAAiB,WAAU,uCAE7C;AAAA,uBAAiB,cACjB,oBAAC,SAAI,IAAG,yBAAwB,WAAW,oFAAoF,WAAW,KAAK,EAAE,IAC/I,8BAAC,YAAO,SAAS,mBAAmB,WAAU,gDAC5C,8BAAC,GAAG,GAAH,EAAK,WAAU,OAAM,GACxB,GACF;AAAA,MAGD,CAAC,YAAY,iBAAiB,cAC7B,oBAAC,SAAI,IAAG,yBAAwB,WAAU,gHACxC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAU;AAAA,UACV,OAAO,EAAE,aAAa,mCAAmC,WAAW,kCAAkC;AAAA,UAEtG,8BAAC,GAAG,cAAH,EAAgB,WAAU,OAAM;AAAA;AAAA,MACnC,GACF;AAAA,MAED;AAAA,MACD;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,wBACT,iBAAiB,aACb,gCACA,qBACN;AAAA,UAGE;AAAA,gCAAC,WAAQ,SAAkB;AAAA,YAE1B,CAAC,cACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,QAAO;AAAA,gBACP,KAAI;AAAA,gBACJ,WAAU;AAAA,gBAEV;AAAA,kBAAC;AAAA;AAAA,oBACC,OAAO,EAAE,MAAM;AAAA,oBACf,SAAQ;AAAA,oBACR,WAAU;AAAA,oBAEV;AAAA,0CAAC,WAAQ,eAAa,MAAC;AAAA,sBACvB,oBAAC,UAAM,YAAE,MAAM,GAAE;AAAA;AAAA;AAAA,gBACnB;AAAA;AAAA,YACF,IACE;AAAA;AAAA;AAAA,MACN;AAAA,OACJ;AAAA,IAIA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,cAAc;AAAA,QACd,YAAW,8CAAS,SAAT,mBAAe,UAAf,YAAwB;AAAA,QACnC,QAAQ;AAAA;AAAA,IACV;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,cAAc;AAAA,QACd,YAAW,8CAAS,SAAT,mBAAe,UAAf,YAAwB;AAAA,QACnC,QAAQ;AAAA;AAAA,IACV;AAAA,KACF;AAEJ;","names":[]}
@@ -149,13 +149,13 @@ function DataTable({
149
149
  }
150
150
  },
151
151
  children: [
152
- LeadingCell && /* @__PURE__ */ jsx(TableCell, { className: "w-10 p-2", children: /* @__PURE__ */ jsx(LeadingCell, { dataset: row.original }) }),
152
+ LeadingCell && /* @__PURE__ */ jsx(TableCell, { className: "w-px p-1 sm:p-2", children: /* @__PURE__ */ jsx(LeadingCell, { dataset: row.original }) }),
153
153
  row.getVisibleCells().map((cell) => {
154
154
  const meta = cell.column.columnDef.meta;
155
155
  const columnClasses = (meta == null ? void 0 : meta.columnClasses) || "";
156
156
  return /* @__PURE__ */ jsx(TableCell, { className: columnClasses, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id);
157
157
  }),
158
- TrailingCell && /* @__PURE__ */ jsx(TableCell, { className: "w-10 p-2", children: /* @__PURE__ */ jsx(TrailingCell, { row }) })
158
+ TrailingCell && /* @__PURE__ */ jsx(TableCell, { className: "w-px p-1 sm:p-2", children: /* @__PURE__ */ jsx(TrailingCell, { row }) })
159
159
  ]
160
160
  },
161
161
  row.id
@@ -170,7 +170,7 @@ function DataTable({
170
170
  dataType === "files" && `${t("noFiles")}`
171
171
  ] }) }) })
172
172
  ] }) }) }),
173
- showPagination && /* @__PURE__ */ jsxs("div", { className: `flex items-center flex-wrap justify-center sm:justify-end gap-8 px-1 py-4 ${paginationClasses}`, children: [
173
+ showPagination && /* @__PURE__ */ jsxs("div", { className: `flex items-center flex-wrap justify-center sm:justify-end gap-3 sm:gap-8 px-1 py-4 ${paginationClasses}`, children: [
174
174
  /* @__PURE__ */ jsxs("div", { className: "flex flex-row gap-2 items-center justify-center", children: [
175
175
  /* @__PURE__ */ jsx("span", { className: "text-sm text-foreground text-nowrap", children: t("paginationTitle") }),
176
176
  /* @__PURE__ */ jsxs(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/core/components/ui/DataTable.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\n// Dependencies\r\nimport * as React from 'react'\r\nimport {\r\n ColumnDef,\r\n flexRender,\r\n getCoreRowModel,\r\n getPaginationRowModel,\r\n SortingState,\r\n getSortedRowModel,\r\n useReactTable,\r\n} from '@tanstack/react-table'\r\nimport { useTranslations } from 'next-intl'\r\n\r\nimport { ColumnMeta } from '../viewers/Data/utils/Columns'\r\n\r\n// Shadcn components\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from './Table'\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from './Select'\r\nimport { Button } from './Button'\r\n\r\n// Icons\r\nimport {\r\n ChevronLeft,\r\n ChevronRight,\r\n ChevronsLeft,\r\n ChevronsRight,\r\n} from 'lucide-react'\r\n\r\nimport { MapContext, MenusContext } from '../../store'\r\nimport { ViewerNames } from '../../types/'\r\nimport { DataTableSkeleton } from '../DataTableSkeleton'\r\n\r\ninterface DataTableProps<TData, TValue> {\r\n columns: ColumnDef<TData, TValue>[]\r\n data: TData[]\r\n onRowClick?: (row: TData) => void\r\n onRowHover?: (row: TData) => void\r\n currentViewer?: string\r\n className?: string\r\n paginationClasses?: string\r\n leadingCell?: React.ComponentType<any>\r\n trailingCell?: React.ComponentType<any>\r\n showPagination?: boolean\r\n tab?: string\r\n isLoading?: boolean\r\n}\r\n\r\nexport function DataTable<TData, TValue>({\r\n columns,\r\n data,\r\n onRowClick,\r\n onRowHover,\r\n currentViewer,\r\n className,\r\n paginationClasses,\r\n trailingCell: TrailingCell,\r\n leadingCell: LeadingCell,\r\n showPagination = true,\r\n tab = '',\r\n isLoading = false,\r\n}: DataTableProps<TData, TValue>) {\r\n // Translation\r\n const t = useTranslations('DataTable')\r\n\r\n const [pageIndex, setPageIndex] = React.useState(0)\r\n\r\n const [sorting, setSorting] = React.useState<SortingState>([])\r\n\r\n const { state: menusState, dispatch: menusDispatch } = React.useContext(MenusContext)\r\n\r\n const { rowsPerPage } = menusState.menus\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n\r\n const { currentLocation, countrySubdivisionsData } = mapState.map\r\n const { countrySubdivision, municipality } = currentLocation || {}\r\n\r\n const [countrySubdivisionName, setCountrySubdivisionName] = React.useState<string>('')\r\n\r\n React.useEffect(() => {\r\n if (countrySubdivisionsData && countrySubdivision) {\r\n const sub = countrySubdivisionsData[countrySubdivision] as any\r\n setCountrySubdivisionName((sub?.name ?? sub) || '')\r\n }\r\n }, [countrySubdivisionsData, countrySubdivision])\r\n\r\n const capitalize = (str: string) => str ? str.charAt(0).toUpperCase() + str.slice(1) : ''\r\n\r\n type DataTypes = 'applied' | 'favourites' | 'national' | 'countrySubdivision' | 'municipal' | ViewerNames | ''\r\n\r\n const dataType: DataTypes = React.useMemo(() => {\r\n return (tab || currentViewer || '') as DataTypes\r\n }, [currentViewer, tab])\r\n\r\n // Keep the user on their current page when the data array changes for benign\r\n // reasons (a row's publish state refreshing, a checkbox/favourite toggle, etc.).\r\n // TanStack's autoResetPageIndex defaults to true, which would snap back to page 1\r\n // on every data change. We clamp manually below instead (see effect).\r\n const table = useReactTable({\r\n data,\r\n columns,\r\n autoResetPageIndex: false,\r\n getCoreRowModel: getCoreRowModel(),\r\n getPaginationRowModel: getPaginationRowModel(),\r\n onSortingChange: setSorting,\r\n getSortedRowModel: getSortedRowModel(),\r\n state: {\r\n sorting,\r\n pagination: {\r\n pageSize: rowsPerPage,\r\n pageIndex: pageIndex,\r\n },\r\n },\r\n onPaginationChange: (updater) => {\r\n const newPagination\r\n = typeof updater === 'function'\r\n ? updater({ pageIndex, pageSize: rowsPerPage })\r\n : updater\r\n\r\n setPageIndex(newPagination.pageIndex)\r\n if (newPagination.pageSize !== rowsPerPage) {\r\n menusDispatch({ type: 'SET_PAGINATION_ROWS_PER_PAGE', payload: { rowsPerPage: newPagination.pageSize } })\r\n }\r\n },\r\n })\r\n\r\n // If the data shrinks (filter/search/un-publish) so the current page no longer\r\n // exists, clamp to the last valid page rather than showing an empty table.\r\n React.useEffect(() => {\r\n const pageCount = Math.max(1, Math.ceil(data.length / rowsPerPage))\r\n if (pageIndex > pageCount - 1) {\r\n setPageIndex(pageCount - 1)\r\n }\r\n }, [data.length, rowsPerPage, pageIndex])\r\n\r\n // Show skeleton when loading\r\n if (isLoading) {\r\n return (\r\n <DataTableSkeleton\r\n columns={columns.length}\r\n rows={rowsPerPage}\r\n showLeadingCell={!!LeadingCell}\r\n showTrailingCell={!!TrailingCell}\r\n className={className}\r\n showPagination={showPagination}\r\n />\r\n )\r\n }\r\n if (!data) {\r\n return (\r\n <div className=\"flex items-center justify-center h-full\">\r\n <p className=\"text-gray-500\">{t('no')} {currentViewer} {t('availableAtTheMoment')}</p>\r\n </div>\r\n )\r\n }\r\n\r\n return (\r\n <div className=\"flex flex-col h-full\">\r\n <div className={`${className} rounded-md border overflow-hidden flex-1 flex flex-col`}>\r\n <div className=\"overflow-auto flex-1\">\r\n <Table>\r\n <TableHeader className=\"sticky top-0 z-10 bg-background\">\r\n {table.getHeaderGroups().map(headerGroup => (\r\n <TableRow key={headerGroup.id}>\r\n {/* Empty header cell for LeadingCell if it exists */}\r\n {LeadingCell && <TableHead></TableHead>}\r\n {headerGroup.headers.map((header) => {\r\n // Get the column classes from meta\r\n const meta = header.column.columnDef.meta as ColumnMeta | undefined\r\n const columnClasses = meta?.columnClasses || ''\r\n\r\n return (\r\n <TableHead key={header.id} className={`${columnClasses}`}>\r\n {header.isPlaceholder\r\n ? null\r\n : flexRender(\r\n header.column.columnDef.header,\r\n header.getContext(),\r\n )}\r\n </TableHead>\r\n )\r\n })}\r\n <TableHead className=\"w-10\"></TableHead>\r\n </TableRow>\r\n ))}\r\n </TableHeader>\r\n <TableBody>\r\n {table.getRowModel().rows?.length ? (\r\n table.getRowModel().rows.map(row => (\r\n <TableRow\r\n key={row.id}\r\n data-state={row.getIsSelected() && 'selected'}\r\n className=\"cursor-pointer hover:bg-muted/50\"\r\n onClick={(e) => {\r\n // Only trigger row click if not clicking on the dropdown\r\n if (\r\n !(e.target as HTMLElement).closest('.dropdown-menu-trigger')\r\n && !(e.target as HTMLElement).closest('Button')\r\n && onRowClick) {\r\n onRowClick(row.original as TData)\r\n }\r\n }}\r\n onMouseEnter={() => {\r\n if (onRowHover) {\r\n onRowHover(row.original as TData)\r\n }\r\n }}\r\n >\r\n {/* leading cell(s) */}\r\n {LeadingCell\r\n && (\r\n <TableCell className=\"w-10 p-2\">\r\n <LeadingCell dataset={row.original} />\r\n </TableCell>\r\n )}\r\n\r\n {/* Table content */}\r\n {row.getVisibleCells().map((cell) => {\r\n // Get the column classes from meta for cells\r\n const meta = cell.column.columnDef.meta as ColumnMeta | any\r\n const columnClasses = meta?.columnClasses || ''\r\n\r\n return (\r\n <TableCell key={cell.id} className={columnClasses}>\r\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\r\n </TableCell>\r\n )\r\n })}\r\n\r\n {/* trailing cell(s) */}\r\n {TrailingCell\r\n && (\r\n <TableCell className=\"w-10 p-2\">\r\n <TrailingCell row={row} />\r\n </TableCell>\r\n )}\r\n\r\n </TableRow>\r\n ))\r\n ) : (\r\n <TableRow>\r\n <TableCell colSpan={columns.length + 1} className=\"h-24 text-center\">\r\n {dataType === 'applied' && t('noApplied')}\r\n {dataType === 'favourites' && t('noFavorite')}\r\n {dataType === 'national' && `${t('noNational')}`}\r\n {dataType === 'countrySubdivision' && `${t('noProvincial')} ${countrySubdivisionName}`}\r\n {dataType === 'municipal' && `${t('noMunicipal')} ${municipality}`}\r\n {dataType === 'buildings' && `${t('noBuildings')}`}\r\n {dataType === 'sites' && `${t('noSites')} `}\r\n {dataType === 'files' && `${t('noFiles')}`}\r\n </TableCell>\r\n </TableRow>\r\n )}\r\n </TableBody>\r\n </Table>\r\n </div>\r\n </div>\r\n {/* Pagination */}\r\n {showPagination && (\r\n <div className={`flex items-center flex-wrap justify-center sm:justify-end gap-8 px-1 py-4 ${paginationClasses}`}>\r\n <div className=\"flex flex-row gap-2 items-center justify-center\">\r\n <span className=\"text-sm text-foreground text-nowrap\">{t('paginationTitle')}</span>\r\n <Select\r\n value={rowsPerPage.toString()}\r\n onValueChange={(value) => {\r\n const rowsPerPage = Number.parseInt(value)\r\n menusDispatch({ type: 'SET_PAGINATION_ROWS_PER_PAGE', payload: { rowsPerPage } })\r\n table.setPageSize(rowsPerPage)\r\n }}\r\n >\r\n <SelectTrigger>\r\n <SelectValue placeholder=\"10\" />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"10\">10</SelectItem>\r\n <SelectItem value=\"20\">20</SelectItem>\r\n <SelectItem value=\"50\">50</SelectItem>\r\n <SelectItem value=\"100\">100</SelectItem>\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n\r\n <div>\r\n <span className=\"text-sm text-foreground\">\r\n {t('page')}\r\n {' '}\r\n {table.getState().pagination.pageIndex + 1}\r\n {' '}\r\n {t('of')}\r\n {' '}\r\n {table.getPageCount()}\r\n </span>\r\n </div>\r\n\r\n <div className=\"flex flex-row gap-2 items-center justify-center\">\r\n <Button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n onClick={() => table.firstPage()}\r\n disabled={!table.getCanPreviousPage()}\r\n >\r\n <ChevronsLeft />\r\n </Button>\r\n <Button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n onClick={() => table.previousPage()}\r\n disabled={!table.getCanPreviousPage()}\r\n >\r\n <ChevronLeft />\r\n </Button>\r\n <Button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n onClick={() => table.nextPage()}\r\n disabled={!table.getCanNextPage()}\r\n >\r\n <ChevronRight />\r\n </Button>\r\n <Button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n onClick={() => table.lastPage()}\r\n disabled={!table.getCanNextPage()}\r\n >\r\n <ChevronsRight />\r\n </Button>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","rowsPerPage"]}
1
+ {"version":3,"sources":["../../../../src/core/components/ui/DataTable.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\n// Dependencies\r\nimport * as React from 'react'\r\nimport {\r\n ColumnDef,\r\n flexRender,\r\n getCoreRowModel,\r\n getPaginationRowModel,\r\n SortingState,\r\n getSortedRowModel,\r\n useReactTable,\r\n} from '@tanstack/react-table'\r\nimport { useTranslations } from 'next-intl'\r\n\r\nimport { ColumnMeta } from '../viewers/Data/utils/Columns'\r\n\r\n// Shadcn components\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from './Table'\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from './Select'\r\nimport { Button } from './Button'\r\n\r\n// Icons\r\nimport {\r\n ChevronLeft,\r\n ChevronRight,\r\n ChevronsLeft,\r\n ChevronsRight,\r\n} from 'lucide-react'\r\n\r\nimport { MapContext, MenusContext } from '../../store'\r\nimport { ViewerNames } from '../../types/'\r\nimport { DataTableSkeleton } from '../DataTableSkeleton'\r\n\r\ninterface DataTableProps<TData, TValue> {\r\n columns: ColumnDef<TData, TValue>[]\r\n data: TData[]\r\n onRowClick?: (row: TData) => void\r\n onRowHover?: (row: TData) => void\r\n currentViewer?: string\r\n className?: string\r\n paginationClasses?: string\r\n leadingCell?: React.ComponentType<any>\r\n trailingCell?: React.ComponentType<any>\r\n showPagination?: boolean\r\n tab?: string\r\n isLoading?: boolean\r\n}\r\n\r\nexport function DataTable<TData, TValue>({\r\n columns,\r\n data,\r\n onRowClick,\r\n onRowHover,\r\n currentViewer,\r\n className,\r\n paginationClasses,\r\n trailingCell: TrailingCell,\r\n leadingCell: LeadingCell,\r\n showPagination = true,\r\n tab = '',\r\n isLoading = false,\r\n}: DataTableProps<TData, TValue>) {\r\n // Translation\r\n const t = useTranslations('DataTable')\r\n\r\n const [pageIndex, setPageIndex] = React.useState(0)\r\n\r\n const [sorting, setSorting] = React.useState<SortingState>([])\r\n\r\n const { state: menusState, dispatch: menusDispatch } = React.useContext(MenusContext)\r\n\r\n const { rowsPerPage } = menusState.menus\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n\r\n const { currentLocation, countrySubdivisionsData } = mapState.map\r\n const { countrySubdivision, municipality } = currentLocation || {}\r\n\r\n const [countrySubdivisionName, setCountrySubdivisionName] = React.useState<string>('')\r\n\r\n React.useEffect(() => {\r\n if (countrySubdivisionsData && countrySubdivision) {\r\n const sub = countrySubdivisionsData[countrySubdivision] as any\r\n setCountrySubdivisionName((sub?.name ?? sub) || '')\r\n }\r\n }, [countrySubdivisionsData, countrySubdivision])\r\n\r\n const capitalize = (str: string) => str ? str.charAt(0).toUpperCase() + str.slice(1) : ''\r\n\r\n type DataTypes = 'applied' | 'favourites' | 'national' | 'countrySubdivision' | 'municipal' | ViewerNames | ''\r\n\r\n const dataType: DataTypes = React.useMemo(() => {\r\n return (tab || currentViewer || '') as DataTypes\r\n }, [currentViewer, tab])\r\n\r\n // Keep the user on their current page when the data array changes for benign\r\n // reasons (a row's publish state refreshing, a checkbox/favourite toggle, etc.).\r\n // TanStack's autoResetPageIndex defaults to true, which would snap back to page 1\r\n // on every data change. We clamp manually below instead (see effect).\r\n const table = useReactTable({\r\n data,\r\n columns,\r\n autoResetPageIndex: false,\r\n getCoreRowModel: getCoreRowModel(),\r\n getPaginationRowModel: getPaginationRowModel(),\r\n onSortingChange: setSorting,\r\n getSortedRowModel: getSortedRowModel(),\r\n state: {\r\n sorting,\r\n pagination: {\r\n pageSize: rowsPerPage,\r\n pageIndex: pageIndex,\r\n },\r\n },\r\n onPaginationChange: (updater) => {\r\n const newPagination\r\n = typeof updater === 'function'\r\n ? updater({ pageIndex, pageSize: rowsPerPage })\r\n : updater\r\n\r\n setPageIndex(newPagination.pageIndex)\r\n if (newPagination.pageSize !== rowsPerPage) {\r\n menusDispatch({ type: 'SET_PAGINATION_ROWS_PER_PAGE', payload: { rowsPerPage: newPagination.pageSize } })\r\n }\r\n },\r\n })\r\n\r\n // If the data shrinks (filter/search/un-publish) so the current page no longer\r\n // exists, clamp to the last valid page rather than showing an empty table.\r\n React.useEffect(() => {\r\n const pageCount = Math.max(1, Math.ceil(data.length / rowsPerPage))\r\n if (pageIndex > pageCount - 1) {\r\n setPageIndex(pageCount - 1)\r\n }\r\n }, [data.length, rowsPerPage, pageIndex])\r\n\r\n // Show skeleton when loading\r\n if (isLoading) {\r\n return (\r\n <DataTableSkeleton\r\n columns={columns.length}\r\n rows={rowsPerPage}\r\n showLeadingCell={!!LeadingCell}\r\n showTrailingCell={!!TrailingCell}\r\n className={className}\r\n showPagination={showPagination}\r\n />\r\n )\r\n }\r\n if (!data) {\r\n return (\r\n <div className=\"flex items-center justify-center h-full\">\r\n <p className=\"text-gray-500\">{t('no')} {currentViewer} {t('availableAtTheMoment')}</p>\r\n </div>\r\n )\r\n }\r\n\r\n return (\r\n <div className=\"flex flex-col h-full\">\r\n <div className={`${className} rounded-md border overflow-hidden flex-1 flex flex-col`}>\r\n <div className=\"overflow-auto flex-1\">\r\n <Table>\r\n <TableHeader className=\"sticky top-0 z-10 bg-background\">\r\n {table.getHeaderGroups().map(headerGroup => (\r\n <TableRow key={headerGroup.id}>\r\n {/* Empty header cell for LeadingCell if it exists */}\r\n {LeadingCell && <TableHead></TableHead>}\r\n {headerGroup.headers.map((header) => {\r\n // Get the column classes from meta\r\n const meta = header.column.columnDef.meta as ColumnMeta | undefined\r\n const columnClasses = meta?.columnClasses || ''\r\n\r\n return (\r\n <TableHead key={header.id} className={`${columnClasses}`}>\r\n {header.isPlaceholder\r\n ? null\r\n : flexRender(\r\n header.column.columnDef.header,\r\n header.getContext(),\r\n )}\r\n </TableHead>\r\n )\r\n })}\r\n <TableHead className=\"w-10\"></TableHead>\r\n </TableRow>\r\n ))}\r\n </TableHeader>\r\n <TableBody>\r\n {table.getRowModel().rows?.length ? (\r\n table.getRowModel().rows.map(row => (\r\n <TableRow\r\n key={row.id}\r\n data-state={row.getIsSelected() && 'selected'}\r\n className=\"cursor-pointer hover:bg-muted/50\"\r\n onClick={(e) => {\r\n // Only trigger row click if not clicking on the dropdown\r\n if (\r\n !(e.target as HTMLElement).closest('.dropdown-menu-trigger')\r\n && !(e.target as HTMLElement).closest('Button')\r\n && onRowClick) {\r\n onRowClick(row.original as TData)\r\n }\r\n }}\r\n onMouseEnter={() => {\r\n if (onRowHover) {\r\n onRowHover(row.original as TData)\r\n }\r\n }}\r\n >\r\n {/* leading cell(s) */}\r\n {LeadingCell\r\n && (\r\n <TableCell className=\"w-px p-1 sm:p-2\">\r\n <LeadingCell dataset={row.original} />\r\n </TableCell>\r\n )}\r\n\r\n {/* Table content */}\r\n {row.getVisibleCells().map((cell) => {\r\n // Get the column classes from meta for cells\r\n const meta = cell.column.columnDef.meta as ColumnMeta | any\r\n const columnClasses = meta?.columnClasses || ''\r\n\r\n return (\r\n <TableCell key={cell.id} className={columnClasses}>\r\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\r\n </TableCell>\r\n )\r\n })}\r\n\r\n {/* trailing cell(s) */}\r\n {TrailingCell\r\n && (\r\n <TableCell className=\"w-px p-1 sm:p-2\">\r\n <TrailingCell row={row} />\r\n </TableCell>\r\n )}\r\n\r\n </TableRow>\r\n ))\r\n ) : (\r\n <TableRow>\r\n <TableCell colSpan={columns.length + 1} className=\"h-24 text-center\">\r\n {dataType === 'applied' && t('noApplied')}\r\n {dataType === 'favourites' && t('noFavorite')}\r\n {dataType === 'national' && `${t('noNational')}`}\r\n {dataType === 'countrySubdivision' && `${t('noProvincial')} ${countrySubdivisionName}`}\r\n {dataType === 'municipal' && `${t('noMunicipal')} ${municipality}`}\r\n {dataType === 'buildings' && `${t('noBuildings')}`}\r\n {dataType === 'sites' && `${t('noSites')} `}\r\n {dataType === 'files' && `${t('noFiles')}`}\r\n </TableCell>\r\n </TableRow>\r\n )}\r\n </TableBody>\r\n </Table>\r\n </div>\r\n </div>\r\n {/* Pagination */}\r\n {showPagination && (\r\n <div className={`flex items-center flex-wrap justify-center sm:justify-end gap-3 sm:gap-8 px-1 py-4 ${paginationClasses}`}>\r\n <div className=\"flex flex-row gap-2 items-center justify-center\">\r\n <span className=\"text-sm text-foreground text-nowrap\">{t('paginationTitle')}</span>\r\n <Select\r\n value={rowsPerPage.toString()}\r\n onValueChange={(value) => {\r\n const rowsPerPage = Number.parseInt(value)\r\n menusDispatch({ type: 'SET_PAGINATION_ROWS_PER_PAGE', payload: { rowsPerPage } })\r\n table.setPageSize(rowsPerPage)\r\n }}\r\n >\r\n <SelectTrigger>\r\n <SelectValue placeholder=\"10\" />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"10\">10</SelectItem>\r\n <SelectItem value=\"20\">20</SelectItem>\r\n <SelectItem value=\"50\">50</SelectItem>\r\n <SelectItem value=\"100\">100</SelectItem>\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n\r\n <div>\r\n <span className=\"text-sm text-foreground\">\r\n {t('page')}\r\n {' '}\r\n {table.getState().pagination.pageIndex + 1}\r\n {' '}\r\n {t('of')}\r\n {' '}\r\n {table.getPageCount()}\r\n </span>\r\n </div>\r\n\r\n <div className=\"flex flex-row gap-2 items-center justify-center\">\r\n <Button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n onClick={() => table.firstPage()}\r\n disabled={!table.getCanPreviousPage()}\r\n >\r\n <ChevronsLeft />\r\n </Button>\r\n <Button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n onClick={() => table.previousPage()}\r\n disabled={!table.getCanPreviousPage()}\r\n >\r\n <ChevronLeft />\r\n </Button>\r\n <Button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n onClick={() => table.nextPage()}\r\n disabled={!table.getCanNextPage()}\r\n >\r\n <ChevronRight />\r\n </Button>\r\n <Button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n onClick={() => table.lastPage()}\r\n disabled={!table.getCanNextPage()}\r\n >\r\n <ChevronsRight />\r\n </Button>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","rowsPerPage"]}
@@ -62,7 +62,7 @@ const DialogContent = React.forwardRef((_c, ref) => {
62
62
  __spreadProps(__spreadValues({
63
63
  ref,
64
64
  className: cn(
65
- "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
65
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg max-h-[90vh] overflow-y-auto translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
66
66
  className
67
67
  )
68
68
  }, props), {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/core/components/ui/Dialog.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 DialogPrimitive from '@radix-ui/react-dialog'\r\nimport { X } from 'lucide-react'\r\n\r\nimport { cn } from '../../utils/utils'\r\n\r\nconst Dialog = DialogPrimitive.Root\r\n\r\nconst DialogTrigger = DialogPrimitive.Trigger\r\n\r\nconst DialogPortal = DialogPrimitive.Portal\r\n\r\nconst DialogClose = DialogPrimitive.Close\r\n\r\nconst DialogOverlay = React.forwardRef<\r\n React.ElementRef<typeof DialogPrimitive.Overlay>,\r\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\r\n>(({ className, ...props }, ref) => (\r\n <DialogPrimitive.Overlay\r\n ref={ref}\r\n className={cn(\r\n 'fixed inset-0 z-50 bg-black/10 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\r\n className,\r\n )}\r\n {...props}\r\n />\r\n))\r\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName\r\n\r\ninterface DialogContentProps extends DialogPrimitive.DialogContentProps {\r\n closeClass?: string\r\n}\r\n\r\nconst DialogContent = React.forwardRef<\r\n React.ElementRef<typeof DialogPrimitive.Content>,\r\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & DialogContentProps\r\n>(({ className, children, closeClass, ...props }, ref) => (\r\n <DialogPortal>\r\n <DialogOverlay />\r\n <DialogPrimitive.Content\r\n ref={ref}\r\n className={cn(\r\n 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',\r\n className,\r\n )}\r\n {...props}\r\n >\r\n {children}\r\n <DialogPrimitive.Close className={`absolute right-2.5 top-2.5 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground ${closeClass}`}>\r\n <X className=\"h-6 w-6\" />\r\n <span className=\"sr-only\">Close</span>\r\n </DialogPrimitive.Close>\r\n </DialogPrimitive.Content>\r\n </DialogPortal>\r\n))\r\nDialogContent.displayName = DialogPrimitive.Content.displayName\r\n\r\nconst DialogHeader = ({\r\n className,\r\n ...props\r\n}: React.HTMLAttributes<HTMLDivElement>) => (\r\n <div\r\n className={cn(\r\n 'flex flex-col space-y-1.5 text-center sm:text-left',\r\n className,\r\n )}\r\n {...props}\r\n />\r\n)\r\nDialogHeader.displayName = 'DialogHeader'\r\n\r\nconst DialogFooter = ({\r\n className,\r\n ...props\r\n}: React.HTMLAttributes<HTMLDivElement>) => (\r\n <div\r\n className={cn(\r\n 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',\r\n className,\r\n )}\r\n {...props}\r\n />\r\n)\r\nDialogFooter.displayName = 'DialogFooter'\r\n\r\nconst DialogTitle = React.forwardRef<\r\n React.ElementRef<typeof DialogPrimitive.Title>,\r\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\r\n>(({ className, ...props }, ref) => (\r\n <DialogPrimitive.Title\r\n ref={ref}\r\n className={cn(\r\n 'text-lg font-semibold leading-none tracking-tight',\r\n className,\r\n )}\r\n {...props}\r\n />\r\n))\r\nDialogTitle.displayName = DialogPrimitive.Title.displayName\r\n\r\nconst DialogDescription = React.forwardRef<\r\n React.ElementRef<typeof DialogPrimitive.Description>,\r\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\r\n>(({ className, ...props }, ref) => (\r\n <DialogPrimitive.Description\r\n ref={ref}\r\n className={cn('text-sm text-muted-foreground', className)}\r\n {...props}\r\n />\r\n))\r\nDialogDescription.displayName = DialogPrimitive.Description.displayName\r\n\r\nexport {\r\n Dialog,\r\n DialogPortal,\r\n DialogOverlay,\r\n DialogTrigger,\r\n DialogClose,\r\n DialogContent,\r\n DialogHeader,\r\n DialogFooter,\r\n DialogTitle,\r\n DialogDescription,\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBE,cA8BI,YA9BJ;AAlBF,YAAY,WAAW;AACvB,YAAY,qBAAqB;AACjC,SAAS,SAAS;AAElB,SAAS,UAAU;AAEnB,MAAM,SAAS,gBAAgB;AAE/B,MAAM,gBAAgB,gBAAgB;AAEtC,MAAM,eAAe,gBAAgB;AAErC,MAAM,cAAc,gBAAgB;AAEpC,MAAM,gBAAgB,MAAM,WAG1B,CAAC,IAAyB,QAAK;AAA9B,eAAE,YAtBL,IAsBG,IAAgB,kBAAhB,IAAgB,CAAd;AACH;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,OACI;AAAA,EACN;AAAA,CACD;AACD,cAAc,cAAc,gBAAgB,QAAQ;AAMpD,MAAM,gBAAgB,MAAM,WAG1B,CAAC,IAA+C,QAAK;AAApD,eAAE,aAAW,UAAU,WAzC1B,IAyCG,IAAsC,kBAAtC,IAAsC,CAApC,aAAW,YAAU;AACxB,8BAAC,gBACC;AAAA,wBAAC,iBAAc;AAAA,IACf;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,SACI,QANL;AAAA,QAQE;AAAA;AAAA,UACD,qBAAC,gBAAgB,OAAhB,EAAsB,WAAW,qRAAqR,UAAU,IAC/T;AAAA,gCAAC,KAAE,WAAU,WAAU;AAAA,YACvB,oBAAC,UAAK,WAAU,WAAU,mBAAK;AAAA,aACjC;AAAA;AAAA;AAAA,IACF;AAAA,KACF;AAAA,CACD;AACD,cAAc,cAAc,gBAAgB,QAAQ;AAEpD,MAAM,eAAe,CAAC,OAGmB;AAHnB,eACpB;AAAA;AAAA,EA/DF,IA8DsB,IAEjB,kBAFiB,IAEjB;AAAA,IADH;AAAA;AAGA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,OACI;AAAA,EACN;AAAA;AAEF,aAAa,cAAc;AAE3B,MAAM,eAAe,CAAC,OAGmB;AAHnB,eACpB;AAAA;AAAA,EA7EF,IA4EsB,IAEjB,kBAFiB,IAEjB;AAAA,IADH;AAAA;AAGA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,OACI;AAAA,EACN;AAAA;AAEF,aAAa,cAAc;AAE3B,MAAM,cAAc,MAAM,WAGxB,CAAC,IAAyB,QAAK;AAA9B,eAAE,YA7FL,IA6FG,IAAgB,kBAAhB,IAAgB,CAAd;AACH;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,OACI;AAAA,EACN;AAAA,CACD;AACD,YAAY,cAAc,gBAAgB,MAAM;AAEhD,MAAM,oBAAoB,MAAM,WAG9B,CAAC,IAAyB,QAAK;AAA9B,eAAE,YA5GL,IA4GG,IAAgB,kBAAhB,IAAgB,CAAd;AACH;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC;AAAA,MACA,WAAW,GAAG,iCAAiC,SAAS;AAAA,OACpD;AAAA,EACN;AAAA,CACD;AACD,kBAAkB,cAAc,gBAAgB,YAAY;","names":[]}
1
+ {"version":3,"sources":["../../../../src/core/components/ui/Dialog.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 DialogPrimitive from '@radix-ui/react-dialog'\r\nimport { X } from 'lucide-react'\r\n\r\nimport { cn } from '../../utils/utils'\r\n\r\nconst Dialog = DialogPrimitive.Root\r\n\r\nconst DialogTrigger = DialogPrimitive.Trigger\r\n\r\nconst DialogPortal = DialogPrimitive.Portal\r\n\r\nconst DialogClose = DialogPrimitive.Close\r\n\r\nconst DialogOverlay = React.forwardRef<\r\n React.ElementRef<typeof DialogPrimitive.Overlay>,\r\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\r\n>(({ className, ...props }, ref) => (\r\n <DialogPrimitive.Overlay\r\n ref={ref}\r\n className={cn(\r\n 'fixed inset-0 z-50 bg-black/10 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\r\n className,\r\n )}\r\n {...props}\r\n />\r\n))\r\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName\r\n\r\ninterface DialogContentProps extends DialogPrimitive.DialogContentProps {\r\n closeClass?: string\r\n}\r\n\r\nconst DialogContent = React.forwardRef<\r\n React.ElementRef<typeof DialogPrimitive.Content>,\r\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & DialogContentProps\r\n>(({ className, children, closeClass, ...props }, ref) => (\r\n <DialogPortal>\r\n <DialogOverlay />\r\n <DialogPrimitive.Content\r\n ref={ref}\r\n className={cn(\r\n 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg max-h-[90vh] overflow-y-auto translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',\r\n className,\r\n )}\r\n {...props}\r\n >\r\n {children}\r\n <DialogPrimitive.Close className={`absolute right-2.5 top-2.5 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground ${closeClass}`}>\r\n <X className=\"h-6 w-6\" />\r\n <span className=\"sr-only\">Close</span>\r\n </DialogPrimitive.Close>\r\n </DialogPrimitive.Content>\r\n </DialogPortal>\r\n))\r\nDialogContent.displayName = DialogPrimitive.Content.displayName\r\n\r\nconst DialogHeader = ({\r\n className,\r\n ...props\r\n}: React.HTMLAttributes<HTMLDivElement>) => (\r\n <div\r\n className={cn(\r\n 'flex flex-col space-y-1.5 text-center sm:text-left',\r\n className,\r\n )}\r\n {...props}\r\n />\r\n)\r\nDialogHeader.displayName = 'DialogHeader'\r\n\r\nconst DialogFooter = ({\r\n className,\r\n ...props\r\n}: React.HTMLAttributes<HTMLDivElement>) => (\r\n <div\r\n className={cn(\r\n 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',\r\n className,\r\n )}\r\n {...props}\r\n />\r\n)\r\nDialogFooter.displayName = 'DialogFooter'\r\n\r\nconst DialogTitle = React.forwardRef<\r\n React.ElementRef<typeof DialogPrimitive.Title>,\r\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\r\n>(({ className, ...props }, ref) => (\r\n <DialogPrimitive.Title\r\n ref={ref}\r\n className={cn(\r\n 'text-lg font-semibold leading-none tracking-tight',\r\n className,\r\n )}\r\n {...props}\r\n />\r\n))\r\nDialogTitle.displayName = DialogPrimitive.Title.displayName\r\n\r\nconst DialogDescription = React.forwardRef<\r\n React.ElementRef<typeof DialogPrimitive.Description>,\r\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\r\n>(({ className, ...props }, ref) => (\r\n <DialogPrimitive.Description\r\n ref={ref}\r\n className={cn('text-sm text-muted-foreground', className)}\r\n {...props}\r\n />\r\n))\r\nDialogDescription.displayName = DialogPrimitive.Description.displayName\r\n\r\nexport {\r\n Dialog,\r\n DialogPortal,\r\n DialogOverlay,\r\n DialogTrigger,\r\n DialogClose,\r\n DialogContent,\r\n DialogHeader,\r\n DialogFooter,\r\n DialogTitle,\r\n DialogDescription,\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBE,cA8BI,YA9BJ;AAlBF,YAAY,WAAW;AACvB,YAAY,qBAAqB;AACjC,SAAS,SAAS;AAElB,SAAS,UAAU;AAEnB,MAAM,SAAS,gBAAgB;AAE/B,MAAM,gBAAgB,gBAAgB;AAEtC,MAAM,eAAe,gBAAgB;AAErC,MAAM,cAAc,gBAAgB;AAEpC,MAAM,gBAAgB,MAAM,WAG1B,CAAC,IAAyB,QAAK;AAA9B,eAAE,YAtBL,IAsBG,IAAgB,kBAAhB,IAAgB,CAAd;AACH;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,OACI;AAAA,EACN;AAAA,CACD;AACD,cAAc,cAAc,gBAAgB,QAAQ;AAMpD,MAAM,gBAAgB,MAAM,WAG1B,CAAC,IAA+C,QAAK;AAApD,eAAE,aAAW,UAAU,WAzC1B,IAyCG,IAAsC,kBAAtC,IAAsC,CAApC,aAAW,YAAU;AACxB,8BAAC,gBACC;AAAA,wBAAC,iBAAc;AAAA,IACf;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,SACI,QANL;AAAA,QAQE;AAAA;AAAA,UACD,qBAAC,gBAAgB,OAAhB,EAAsB,WAAW,qRAAqR,UAAU,IAC/T;AAAA,gCAAC,KAAE,WAAU,WAAU;AAAA,YACvB,oBAAC,UAAK,WAAU,WAAU,mBAAK;AAAA,aACjC;AAAA;AAAA;AAAA,IACF;AAAA,KACF;AAAA,CACD;AACD,cAAc,cAAc,gBAAgB,QAAQ;AAEpD,MAAM,eAAe,CAAC,OAGmB;AAHnB,eACpB;AAAA;AAAA,EA/DF,IA8DsB,IAEjB,kBAFiB,IAEjB;AAAA,IADH;AAAA;AAGA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,OACI;AAAA,EACN;AAAA;AAEF,aAAa,cAAc;AAE3B,MAAM,eAAe,CAAC,OAGmB;AAHnB,eACpB;AAAA;AAAA,EA7EF,IA4EsB,IAEjB,kBAFiB,IAEjB;AAAA,IADH;AAAA;AAGA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,OACI;AAAA,EACN;AAAA;AAEF,aAAa,cAAc;AAE3B,MAAM,cAAc,MAAM,WAGxB,CAAC,IAAyB,QAAK;AAA9B,eAAE,YA7FL,IA6FG,IAAgB,kBAAhB,IAAgB,CAAd;AACH;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,OACI;AAAA,EACN;AAAA,CACD;AACD,YAAY,cAAc,gBAAgB,MAAM;AAEhD,MAAM,oBAAoB,MAAM,WAG9B,CAAC,IAAyB,QAAK;AAA9B,eAAE,YA5GL,IA4GG,IAAgB,kBAAhB,IAAgB,CAAd;AACH;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC;AAAA,MACA,WAAW,GAAG,iCAAiC,SAAS;AAAA,OACpD;AAAA,EACN;AAAA,CACD;AACD,kBAAkB,cAAc,gBAAgB,YAAY;","names":[]}
@@ -11,7 +11,7 @@ function InfoSidebarContainer({
11
11
  const { state: buildingState } = React.useContext(BuildingsContext);
12
12
  const { building: currentBuilding } = buildingState.buildings;
13
13
  const [loadingBuildingInfo] = React.useState(false);
14
- return /* @__PURE__ */ jsxs("div", { className: "w-[410px] flex flex-col h-screen bg-background border-r border-border ", children: [
14
+ return /* @__PURE__ */ jsxs("div", { className: "w-full sm:w-[410px] flex flex-col h-full min-h-0 bg-background border-r border-border ", children: [
15
15
  /* @__PURE__ */ jsx(
16
16
  Header,
17
17
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/core/components/ui/InfoSidebar/Container.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\n\r\n// Icons\r\nimport { BuildingsContext } from '../../../store'\r\n\r\n// New structured components\r\nimport { Header } from './Header'\r\nimport type { Organization } from '../../../types/dbTypes'\r\n\r\ninterface InfoSidebarProps {\r\n children?: React.ReactNode\r\n tabSelector?: React.ReactNode\r\n organization?: Organization\r\n}\r\n\r\nexport function InfoSidebarContainer({\r\n children,\r\n tabSelector,\r\n organization,\r\n}: InfoSidebarProps) {\r\n const { state: buildingState } = React.useContext(BuildingsContext);\r\n const { building: currentBuilding } = buildingState.buildings;\r\n\r\n // State\r\n const [loadingBuildingInfo] = React.useState(false)\r\n\r\n return (\r\n <div className=\"w-[410px] flex flex-col h-screen bg-background border-r border-border \">\r\n <Header\r\n currentBuilding={currentBuilding}\r\n loadingBuildingInfo={loadingBuildingInfo}\r\n organization={organization}\r\n />\r\n\r\n {/* Tab Selector */}\r\n {tabSelector}\r\n\r\n {/* Tab Content */}\r\n {children}\r\n </div>\r\n )\r\n}\r\n"],"mappings":";AAgCI,SACE,KADF;AA3BJ,YAAY,WAAW;AAGvB,SAAS,wBAAwB;AAGjC,SAAS,cAAc;AAShB,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,EAAE,OAAO,cAAc,IAAI,MAAM,WAAW,gBAAgB;AAClE,QAAM,EAAE,UAAU,gBAAgB,IAAI,cAAc;AAGpD,QAAM,CAAC,mBAAmB,IAAI,MAAM,SAAS,KAAK;AAElD,SACE,qBAAC,SAAI,WAAU,0EACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,IAGC;AAAA,IAGE;AAAA,KACL;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../../src/core/components/ui/InfoSidebar/Container.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\n\r\n// Icons\r\nimport { BuildingsContext } from '../../../store'\r\n\r\n// New structured components\r\nimport { Header } from './Header'\r\nimport type { Organization } from '../../../types/dbTypes'\r\n\r\ninterface InfoSidebarProps {\r\n children?: React.ReactNode\r\n tabSelector?: React.ReactNode\r\n organization?: Organization\r\n}\r\n\r\nexport function InfoSidebarContainer({\r\n children,\r\n tabSelector,\r\n organization,\r\n}: InfoSidebarProps) {\r\n const { state: buildingState } = React.useContext(BuildingsContext);\r\n const { building: currentBuilding } = buildingState.buildings;\r\n\r\n // State\r\n const [loadingBuildingInfo] = React.useState(false)\r\n\r\n return (\r\n <div className=\"w-full sm:w-[410px] flex flex-col h-full min-h-0 bg-background border-r border-border \">\r\n <Header\r\n currentBuilding={currentBuilding}\r\n loadingBuildingInfo={loadingBuildingInfo}\r\n organization={organization}\r\n />\r\n\r\n {/* Tab Selector */}\r\n {tabSelector}\r\n\r\n {/* Tab Content */}\r\n {children}\r\n </div>\r\n )\r\n}\r\n"],"mappings":";AAgCI,SACE,KADF;AA3BJ,YAAY,WAAW;AAGvB,SAAS,wBAAwB;AAGjC,SAAS,cAAc;AAShB,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,EAAE,OAAO,cAAc,IAAI,MAAM,WAAW,gBAAgB;AAClE,QAAM,EAAE,UAAU,gBAAgB,IAAI,cAAc;AAGpD,QAAM,CAAC,mBAAmB,IAAI,MAAM,SAAS,KAAK;AAElD,SACE,qBAAC,SAAI,WAAU,0FACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,IAGC;AAAA,IAGE;AAAA,KACL;AAEJ;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"CollapsibleSensorItem.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/Sensors/CollapsibleSensorItem.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAS9B,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAO3D,KAAK,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAA;AAExD,UAAU,0BAA0B;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,UAAU,CAAA;IACtB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACrD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,UAAU,EACV,QAAQ,EACR,KAAS,EACT,SAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,YAAY,EACb,EAAE,0BAA0B,qBAyO5B"}
1
+ {"version":3,"file":"CollapsibleSensorItem.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/Sensors/CollapsibleSensorItem.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAS9B,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAQ3D,KAAK,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAA;AAExD,UAAU,0BAA0B;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,UAAU,CAAA;IACtB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACrD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,UAAU,EACV,QAAQ,EACR,KAAS,EACT,SAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,YAAY,EACb,EAAE,0BAA0B,qBAwO5B"}
@@ -14,6 +14,7 @@ import { useCoreHooks } from "../../../hooks/provider";
14
14
  import { useSession } from "next-auth/react";
15
15
  import { SensorChart } from "./SensorChart";
16
16
  import { SensorTagsSection } from "./SensorTagsSection";
17
+ import { resolveLucideIcon } from "./sensorUtils";
17
18
  function CollapsibleSensorItem({
18
19
  sensor,
19
20
  sensorType,
@@ -31,9 +32,8 @@ function CollapsibleSensorItem({
31
32
  const [isLoadingData, setIsLoadingData] = React.useState(false);
32
33
  const prevVisibleRef = React.useRef(isVisible);
33
34
  const dataPath = minioBaseUrl ? `${minioBaseUrl}/sensors/${sensor.url}` : "";
34
- const typeIcon = (sensorType == null ? void 0 : sensorType.icon) || "Radio";
35
35
  const typeName = (_a = sensorType == null ? void 0 : sensorType.name.replace(/_/g, " ")) != null ? _a : "Unknown";
36
- const SensorIcon = LR[typeIcon] || LR.Radio;
36
+ const SensorIcon = resolveLucideIcon(sensorType == null ? void 0 : sensorType.icon);
37
37
  React.useEffect(() => {
38
38
  const prevVisible = prevVisibleRef.current;
39
39
  if (prevVisible && !isVisible) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/core/components/ui/Sensors/CollapsibleSensorItem.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 { Button } from '../Button'\r\nimport { Badge } from '../Badge'\r\nimport { Avatar, AvatarFallback } from '../Avatar'\r\nimport { UserAvatar } from '../UserAvatar'\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport { format } from 'date-fns'\r\nimport { cn } from '../../../utils/utils'\r\nimport { Sensor, SensorType } from '../../../types/dbTypes'\r\nimport { useUser } from '../../../hooks/users/users'\r\nimport { useCoreHooks } from '../../../hooks/provider'\r\nimport { useSession } from 'next-auth/react'\r\nimport { SensorChart } from './SensorChart'\r\nimport { SensorTagsSection } from './SensorTagsSection'\r\nimport type { ChartConfig } from '../chart'\r\ntype SensorAction = 'view' | 'edit' | 'delete' | 'reply'\r\n\r\ninterface CollapsibleSensorItemProps {\r\n sensor: Sensor\r\n sensorType: SensorType\r\n onAction?: (action: SensorAction, id: number) => void\r\n depth?: number\r\n isVisible?: boolean\r\n onMouseEnter?: () => void\r\n onMouseLeave?: () => void\r\n minioBaseUrl?: string\r\n}\r\n\r\nexport function CollapsibleSensorItem({\r\n sensor,\r\n sensorType,\r\n onAction,\r\n depth = 0,\r\n isVisible = true,\r\n onMouseEnter,\r\n onMouseLeave,\r\n minioBaseUrl\r\n}: CollapsibleSensorItemProps) {\r\n const t = useTranslations('SensorsSection')\r\n const [isExpanded, setIsExpanded] = React.useState(false)\r\n const [sensorData, setSensorData] = React.useState<{ time: string; value: number }[]>([])\r\n const [isLoadingData, setIsLoadingData] = React.useState(false)\r\n const prevVisibleRef = React.useRef(isVisible)\r\n\r\n //const { state: { runtimeConfig: { minioUrl } } } = useAppConfigContext()\r\n\r\n //const dataPath = `${minioUrl ?? ''}/sensors/${sensor.url}`\r\n const dataPath = minioBaseUrl\r\n ? `${minioBaseUrl}/sensors/${sensor.url}`\r\n : ''\r\n const typeIcon = sensorType?.icon || 'Radio'\r\n const typeName = sensorType?.name.replace(/_/g, ' ') ?? 'Unknown' \r\n\r\n const SensorIcon = LR[typeIcon] || LR.Radio\r\n\r\n // Auto-collapse when visibility turns off\r\n React.useEffect(() => {\r\n const prevVisible = prevVisibleRef.current\r\n \r\n if (prevVisible && !isVisible) {\r\n // Just turned invisible - collapse\r\n setIsExpanded(false)\r\n }\r\n \r\n prevVisibleRef.current = isVisible\r\n }, [isVisible])\r\n\r\n // Fetch and parse CSV data\r\n React.useEffect(() => {\r\n const fetchCSVData = async () => {\r\n setIsLoadingData(true)\r\n try {\r\n const response = await fetch(dataPath)\r\n const csvText = await response.text()\r\n \r\n // Parse CSV: format is \"time,value\" (e.g., \"0:00:00,7.4\")\r\n const lines = csvText.trim().split('\\n')\r\n const parsed = lines.map(line => {\r\n const [time, valueStr] = line.split(',')\r\n return {\r\n time: time.trim(),\r\n value: parseFloat(valueStr.trim())\r\n }\r\n }).filter(item => !isNaN(item.value)) // Filter out any invalid entries\r\n \r\n setSensorData(parsed)\r\n } catch (error) {\r\n console.error('Error fetching sensor data:', error)\r\n // Fallback to empty array on error\r\n setSensorData([])\r\n } finally {\r\n setIsLoadingData(false)\r\n }\r\n }\r\n\r\n if (isExpanded) {\r\n fetchCSVData()\r\n }\r\n }, [dataPath, isExpanded])\r\n\r\n const chartConfig = {\r\n value: {\r\n label: typeName,\r\n color: \"hsl(var(--chart-1))\",\r\n },\r\n } satisfies ChartConfig\r\n\r\n const user = useSession().data?.user\r\n\r\n const indentClass = depth > 0 ? `ml-${Math.min(depth * 4, 12)}` : ''\r\n\r\n const { user: author } = useUser(String(sensor.authorId))\r\n let authorName: string | undefined = author?.name ?? 'Unknown User'\r\n\r\n const { sensor: sensorHooks } = useCoreHooks()\r\n const { updateSensor } = sensorHooks.useSensor(sensor.id)\r\n\r\n const handleAddTag = async (tag: string) => {\r\n const currentTags = sensor.tags ?? []\r\n await updateSensor({ tags: [...currentTags, tag] })\r\n }\r\n\r\n const handleDeleteTag = async (tag: string) => {\r\n const currentTags = sensor.tags ?? []\r\n await updateSensor({ tags: currentTags.filter(t => t !== tag) })\r\n }\r\n\r\n return (\r\n <div \r\n className={cn(\r\n \"border rounded-md overflow-hidden transition-opacity\",\r\n indentClass,\r\n !isVisible && \"opacity-70\"\r\n )}\r\n onMouseEnter={onMouseEnter}\r\n onMouseLeave={onMouseLeave}\r\n >\r\n {/* Sensor Header */}\r\n <div className=\"flex items-start justify-between p-3 hover:bg-accent/50 transition-colors\">\r\n <div className=\"flex items-start gap-3 flex-1 min-w-0\">\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n className={cn(\"h-6 w-6 p-0 mt-1\", !isVisible && \"opacity-70\")}\r\n onClick={() => setIsExpanded(!isExpanded)}\r\n >\r\n <LR.ChevronRight\r\n className={cn(\r\n \"h-4 w-4 transition-transform\",\r\n isExpanded && \"rotate-90\"\r\n )}\r\n />\r\n </Button>\r\n\r\n <div className={cn(\r\n \"h-8 w-8 mt-1 rounded-full bg-primary flex items-center justify-center flex-shrink-0\",\r\n !isVisible && \"opacity-70 grayscale\"\r\n )}>\r\n <SensorIcon className={cn(\"h-4 w-4 text-primary-foreground\", !isVisible && \"opacity-70\")} />\r\n </div>\r\n\r\n <div className=\"flex-1 min-w-0\">\r\n <div className=\"flex items-center gap-2\">\r\n <span className={cn(\"text-sm font-medium\", isVisible ? \"text-foreground\" : \"text-muted-foreground\")}>\r\n {sensor.name}\r\n </span>\r\n {sensor.updatedAt !== sensor.createdAt && (\r\n <Badge variant=\"outline\" className=\"text-xs\">\r\n {t('edited')}\r\n </Badge>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* Action Buttons */}\r\n {onAction && (\r\n <div className={cn(\"flex items-center gap-0 flex-shrink-0\", !isVisible && \"opacity-70\")}>\r\n {user?.id === String(sensor.authorId) && (\r\n <>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"h-8 w-8 p-0\"\r\n onClick={() => onAction('edit', sensor.id)}\r\n title={t('editSensor')}\r\n >\r\n <LR.Pencil className=\"h-4 w-4\" />\r\n </Button>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"h-8 w-8 p-0\"\r\n onClick={() => onAction('delete', sensor.id)}\r\n title={t('deleteSensor')}\r\n >\r\n <LR.Trash2 className=\"h-4 w-4\" />\r\n </Button>\r\n </>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Expanded Content */ }\r\n {\r\n isExpanded && (\r\n <div className=\"border-t bg-muted/30\">\r\n <div className=\"p-4 space-y-4\">\r\n {/* Sensor Information */}\r\n <div className=\"space-y-2\">\r\n <div className=\"flex items-center justify-between\">\r\n <span className=\"text-xs text-muted-foreground\">\r\n {format(new Date(sensor.createdAt), 'PPp')}\r\n </span>\r\n <Badge variant=\"secondary\" className=\"text-xs\">\r\n {typeName}\r\n </Badge>\r\n </div>\r\n \r\n <div className=\"grid grid-cols-2 gap-2 text-sm\">\r\n <div>\r\n <span className=\"text-muted-foreground\">Name:</span>\r\n <span className=\"ml-2 font-medium\">{sensor.name}</span>\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">Format:</span>\r\n <span className=\"ml-2 font-medium\">{sensor.dataFormat}</span>\r\n </div>\r\n </div>\r\n\r\n {/* Author Information */}\r\n <div className=\"flex items-center gap-2 pt-2\">\r\n <span className=\"text-muted-foreground text-sm\">Author:</span>\r\n <Avatar className=\"h-5 w-5 rounded-full overflow-hidden\">\r\n <UserAvatar imageFileId={author?.imageFileId} name={authorName} />\r\n </Avatar>\r\n <span className=\"text-sm font-medium\">{authorName}</span>\r\n </div>\r\n </div>\r\n\r\n {/* Sensor Data Chart */}\r\n <SensorChart\r\n sensorData={sensorData}\r\n isLoadingData={isLoadingData}\r\n sensorName={sensor.name}\r\n sensorType={sensorType}\r\n updateFrequency={sensor.updateFrequency}\r\n chartConfig={chartConfig}\r\n />\r\n </div>\r\n <div>\r\n <SensorTagsSection\r\n tags={sensor.tags ?? []}\r\n onAdd={handleAddTag}\r\n onDelete={handleDeleteTag}\r\n translations={{\r\n addTag: t('addTag'),\r\n removeTag: t('removeTag'),\r\n cancel: t('cancel'),\r\n newTagPlaceholder: t('newTagPlaceholder'),\r\n }}\r\n />\r\n </div>\r\n\r\n </div>\r\n )\r\n }\r\n </div >\r\n )\r\n}\r\n"],"mappings":";AAwJY,SAiCA,UAjCA,KAgBA,YAhBA;AAnJZ,YAAY,WAAW;AACvB,SAAS,cAAc;AACvB,SAAS,aAAa;AACtB,SAAS,cAA8B;AACvC,SAAS,kBAAkB;AAC3B,YAAY,QAAQ;AACpB,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB,SAAS,UAAU;AAEnB,SAAS,eAAe;AACxB,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAe3B,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AACF,GAA+B;AA3C/B;AA4CE,QAAM,IAAI,gBAAgB,gBAAgB;AAC1C,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AACxD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAA4C,CAAC,CAAC;AACxF,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAS,KAAK;AAC9D,QAAM,iBAAiB,MAAM,OAAO,SAAS;AAK7C,QAAM,WAAW,eACf,GAAG,YAAY,YAAY,OAAO,GAAG,KACrC;AACF,QAAM,YAAY,yCAAY,SAAQ;AACtC,QAAM,YAAW,8CAAY,KAAK,QAAQ,MAAM,SAA/B,YAAuC;AAExD,QAAM,aAAa,GAAG,QAAQ,KAAK,GAAG;AAGtC,QAAM,UAAU,MAAM;AACpB,UAAM,cAAc,eAAe;AAEnC,QAAI,eAAe,CAAC,WAAW;AAE7B,oBAAc,KAAK;AAAA,IACrB;AAEA,mBAAe,UAAU;AAAA,EAC3B,GAAG,CAAC,SAAS,CAAC;AAGd,QAAM,UAAU,MAAM;AACpB,UAAM,eAAe,YAAY;AAC/B,uBAAiB,IAAI;AACrB,UAAI;AACF,cAAM,WAAW,MAAM,MAAM,QAAQ;AACrC,cAAM,UAAU,MAAM,SAAS,KAAK;AAGpC,cAAM,QAAQ,QAAQ,KAAK,EAAE,MAAM,IAAI;AACvC,cAAM,SAAS,MAAM,IAAI,UAAQ;AAC/B,gBAAM,CAAC,MAAM,QAAQ,IAAI,KAAK,MAAM,GAAG;AACvC,iBAAO;AAAA,YACL,MAAM,KAAK,KAAK;AAAA,YAChB,OAAO,WAAW,SAAS,KAAK,CAAC;AAAA,UACnC;AAAA,QACF,CAAC,EAAE,OAAO,UAAQ,CAAC,MAAM,KAAK,KAAK,CAAC;AAEpC,sBAAc,MAAM;AAAA,MACtB,SAAS,OAAO;AACd,gBAAQ,MAAM,+BAA+B,KAAK;AAElD,sBAAc,CAAC,CAAC;AAAA,MAClB,UAAE;AACA,yBAAiB,KAAK;AAAA,MACxB;AAAA,IACF;AAEA,QAAI,YAAY;AACd,mBAAa;AAAA,IACf;AAAA,EACF,GAAG,CAAC,UAAU,UAAU,CAAC;AAEzB,QAAM,cAAc;AAAA,IAClB,OAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,QAAO,gBAAW,EAAE,SAAb,mBAAmB;AAEhC,QAAM,cAAc,QAAQ,IAAI,MAAM,KAAK,IAAI,QAAQ,GAAG,EAAE,CAAC,KAAK;AAElE,QAAM,EAAE,MAAM,OAAO,IAAI,QAAQ,OAAO,OAAO,QAAQ,CAAC;AACxD,MAAI,cAAiC,sCAAQ,SAAR,YAAgB;AAErD,QAAM,EAAE,QAAQ,YAAY,IAAI,aAAa;AAC7C,QAAM,EAAE,aAAa,IAAI,YAAY,UAAU,OAAO,EAAE;AAExD,QAAM,eAAe,OAAO,QAAgB;AA3H9C,QAAAA;AA4HI,UAAM,eAAcA,MAAA,OAAO,SAAP,OAAAA,MAAe,CAAC;AACpC,UAAM,aAAa,EAAE,MAAM,CAAC,GAAG,aAAa,GAAG,EAAE,CAAC;AAAA,EACpD;AAEA,QAAM,kBAAkB,OAAO,QAAgB;AAhIjD,QAAAA;AAiII,UAAM,eAAcA,MAAA,OAAO,SAAP,OAAAA,MAAe,CAAC;AACpC,UAAM,aAAa,EAAE,MAAM,YAAY,OAAO,CAAAC,OAAKA,OAAM,GAAG,EAAE,CAAC;AAAA,EACjE;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA,CAAC,aAAa;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MAGA;AAAA,6BAAC,SAAI,WAAU,6EACb;AAAA,+BAAC,SAAI,WAAU,yCACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,WAAW,GAAG,oBAAoB,CAAC,aAAa,YAAY;AAAA,gBAC5D,SAAS,MAAM,cAAc,CAAC,UAAU;AAAA,gBAExC;AAAA,kBAAC,GAAG;AAAA,kBAAH;AAAA,oBACC,WAAW;AAAA,sBACT;AAAA,sBACA,cAAc;AAAA,oBAChB;AAAA;AAAA,gBACF;AAAA;AAAA,YACF;AAAA,YAEA,oBAAC,SAAI,WAAW;AAAA,cACd;AAAA,cACA,CAAC,aAAa;AAAA,YAChB,GACE,8BAAC,cAAW,WAAW,GAAG,mCAAmC,CAAC,aAAa,YAAY,GAAG,GAC5F;AAAA,YAEA,oBAAC,SAAI,WAAU,kBACb,+BAAC,SAAI,WAAU,2BACX;AAAA,kCAAC,UAAK,WAAW,GAAG,uBAAuB,YAAY,oBAAoB,uBAAuB,GACjG,iBAAO,MACV;AAAA,cACC,OAAO,cAAc,OAAO,aAC3B,oBAAC,SAAM,SAAQ,WAAU,WAAU,WAChC,YAAE,QAAQ,GACb;AAAA,eAEJ,GACF;AAAA,aACF;AAAA,UAGD,YACC,oBAAC,SAAI,WAAW,GAAG,yCAAyC,CAAC,aAAa,YAAY,GACnF,wCAAM,QAAO,OAAO,OAAO,QAAQ,KAClC,iCACE;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,SAAS,MAAM,SAAS,QAAQ,OAAO,EAAE;AAAA,gBACzC,OAAO,EAAE,YAAY;AAAA,gBAErB,8BAAC,GAAG,QAAH,EAAU,WAAU,WAAU;AAAA;AAAA,YACjC;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,SAAS,MAAM,SAAS,UAAU,OAAO,EAAE;AAAA,gBAC3C,OAAO,EAAE,cAAc;AAAA,gBAEvB,8BAAC,GAAG,QAAH,EAAU,WAAU,WAAU;AAAA;AAAA,YACjC;AAAA,aACF,GAEJ;AAAA,WAEJ;AAAA,QAIA,cACE,qBAAC,SAAI,WAAU,wBACb;AAAA,+BAAC,SAAI,WAAU,iBAEb;AAAA,iCAAC,SAAI,WAAU,aACb;AAAA,mCAAC,SAAI,WAAU,qCACb;AAAA,oCAAC,UAAK,WAAU,iCACb,iBAAO,IAAI,KAAK,OAAO,SAAS,GAAG,KAAK,GAC3C;AAAA,gBACA,oBAAC,SAAM,SAAQ,aAAY,WAAU,WAClC,oBACH;AAAA,iBACF;AAAA,cAEA,qBAAC,SAAI,WAAU,kCACb;AAAA,qCAAC,SACC;AAAA,sCAAC,UAAK,WAAU,yBAAwB,mBAAK;AAAA,kBAC7C,oBAAC,UAAK,WAAU,oBAAoB,iBAAO,MAAK;AAAA,mBAClD;AAAA,gBACA,qBAAC,SACC;AAAA,sCAAC,UAAK,WAAU,yBAAwB,qBAAO;AAAA,kBAC/C,oBAAC,UAAK,WAAU,oBAAoB,iBAAO,YAAW;AAAA,mBACxD;AAAA,iBACF;AAAA,cAGA,qBAAC,SAAI,WAAU,gCACb;AAAA,oCAAC,UAAK,WAAU,iCAAgC,qBAAO;AAAA,gBACvD,oBAAC,UAAO,WAAU,wCAChB,8BAAC,cAAW,aAAa,iCAAQ,aAAa,MAAM,YAAY,GAClE;AAAA,gBACA,oBAAC,UAAK,WAAU,uBAAuB,sBAAW;AAAA,iBACpD;AAAA,eACF;AAAA,YAGA;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA;AAAA,gBACA,YAAY,OAAO;AAAA,gBACnB;AAAA,gBACA,iBAAiB,OAAO;AAAA,gBACxB;AAAA;AAAA,YACF;AAAA,aACF;AAAA,UACA,oBAAC,SACG;AAAA,YAAC;AAAA;AAAA,cACC,OAAM,YAAO,SAAP,YAAe,CAAC;AAAA,cACtB,OAAO;AAAA,cACP,UAAU;AAAA,cACV,cAAc;AAAA,gBACZ,QAAQ,EAAE,QAAQ;AAAA,gBAClB,WAAW,EAAE,WAAW;AAAA,gBACxB,QAAQ,EAAE,QAAQ;AAAA,gBAClB,mBAAmB,EAAE,mBAAmB;AAAA,cAC1C;AAAA;AAAA,UACF,GACJ;AAAA,WAEF;AAAA;AAAA;AAAA,EAGF;AAEJ;","names":["_a","t"]}
1
+ {"version":3,"sources":["../../../../../src/core/components/ui/Sensors/CollapsibleSensorItem.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 { Button } from '../Button'\r\nimport { Badge } from '../Badge'\r\nimport { Avatar, AvatarFallback } from '../Avatar'\r\nimport { UserAvatar } from '../UserAvatar'\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport { format } from 'date-fns'\r\nimport { cn } from '../../../utils/utils'\r\nimport { Sensor, SensorType } from '../../../types/dbTypes'\r\nimport { useUser } from '../../../hooks/users/users'\r\nimport { useCoreHooks } from '../../../hooks/provider'\r\nimport { useSession } from 'next-auth/react'\r\nimport { SensorChart } from './SensorChart'\r\nimport { SensorTagsSection } from './SensorTagsSection'\r\nimport type { ChartConfig } from '../chart'\r\nimport { resolveLucideIcon } from './sensorUtils'\r\ntype SensorAction = 'view' | 'edit' | 'delete' | 'reply'\r\n\r\ninterface CollapsibleSensorItemProps {\r\n sensor: Sensor\r\n sensorType: SensorType\r\n onAction?: (action: SensorAction, id: number) => void\r\n depth?: number\r\n isVisible?: boolean\r\n onMouseEnter?: () => void\r\n onMouseLeave?: () => void\r\n minioBaseUrl?: string\r\n}\r\n\r\nexport function CollapsibleSensorItem({\r\n sensor,\r\n sensorType,\r\n onAction,\r\n depth = 0,\r\n isVisible = true,\r\n onMouseEnter,\r\n onMouseLeave,\r\n minioBaseUrl\r\n}: CollapsibleSensorItemProps) {\r\n const t = useTranslations('SensorsSection')\r\n const [isExpanded, setIsExpanded] = React.useState(false)\r\n const [sensorData, setSensorData] = React.useState<{ time: string; value: number }[]>([])\r\n const [isLoadingData, setIsLoadingData] = React.useState(false)\r\n const prevVisibleRef = React.useRef(isVisible)\r\n\r\n //const { state: { runtimeConfig: { minioUrl } } } = useAppConfigContext()\r\n\r\n //const dataPath = `${minioUrl ?? ''}/sensors/${sensor.url}`\r\n const dataPath = minioBaseUrl\r\n ? `${minioBaseUrl}/sensors/${sensor.url}`\r\n : ''\r\n const typeName = sensorType?.name.replace(/_/g, ' ') ?? 'Unknown'\r\n\r\n const SensorIcon = resolveLucideIcon(sensorType?.icon)\r\n\r\n // Auto-collapse when visibility turns off\r\n React.useEffect(() => {\r\n const prevVisible = prevVisibleRef.current\r\n \r\n if (prevVisible && !isVisible) {\r\n // Just turned invisible - collapse\r\n setIsExpanded(false)\r\n }\r\n \r\n prevVisibleRef.current = isVisible\r\n }, [isVisible])\r\n\r\n // Fetch and parse CSV data\r\n React.useEffect(() => {\r\n const fetchCSVData = async () => {\r\n setIsLoadingData(true)\r\n try {\r\n const response = await fetch(dataPath)\r\n const csvText = await response.text()\r\n \r\n // Parse CSV: format is \"time,value\" (e.g., \"0:00:00,7.4\")\r\n const lines = csvText.trim().split('\\n')\r\n const parsed = lines.map(line => {\r\n const [time, valueStr] = line.split(',')\r\n return {\r\n time: time.trim(),\r\n value: parseFloat(valueStr.trim())\r\n }\r\n }).filter(item => !isNaN(item.value)) // Filter out any invalid entries\r\n \r\n setSensorData(parsed)\r\n } catch (error) {\r\n console.error('Error fetching sensor data:', error)\r\n // Fallback to empty array on error\r\n setSensorData([])\r\n } finally {\r\n setIsLoadingData(false)\r\n }\r\n }\r\n\r\n if (isExpanded) {\r\n fetchCSVData()\r\n }\r\n }, [dataPath, isExpanded])\r\n\r\n const chartConfig = {\r\n value: {\r\n label: typeName,\r\n color: \"hsl(var(--chart-1))\",\r\n },\r\n } satisfies ChartConfig\r\n\r\n const user = useSession().data?.user\r\n\r\n const indentClass = depth > 0 ? `ml-${Math.min(depth * 4, 12)}` : ''\r\n\r\n const { user: author } = useUser(String(sensor.authorId))\r\n let authorName: string | undefined = author?.name ?? 'Unknown User'\r\n\r\n const { sensor: sensorHooks } = useCoreHooks()\r\n const { updateSensor } = sensorHooks.useSensor(sensor.id)\r\n\r\n const handleAddTag = async (tag: string) => {\r\n const currentTags = sensor.tags ?? []\r\n await updateSensor({ tags: [...currentTags, tag] })\r\n }\r\n\r\n const handleDeleteTag = async (tag: string) => {\r\n const currentTags = sensor.tags ?? []\r\n await updateSensor({ tags: currentTags.filter(t => t !== tag) })\r\n }\r\n\r\n return (\r\n <div \r\n className={cn(\r\n \"border rounded-md overflow-hidden transition-opacity\",\r\n indentClass,\r\n !isVisible && \"opacity-70\"\r\n )}\r\n onMouseEnter={onMouseEnter}\r\n onMouseLeave={onMouseLeave}\r\n >\r\n {/* Sensor Header */}\r\n <div className=\"flex items-start justify-between p-3 hover:bg-accent/50 transition-colors\">\r\n <div className=\"flex items-start gap-3 flex-1 min-w-0\">\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n className={cn(\"h-6 w-6 p-0 mt-1\", !isVisible && \"opacity-70\")}\r\n onClick={() => setIsExpanded(!isExpanded)}\r\n >\r\n <LR.ChevronRight\r\n className={cn(\r\n \"h-4 w-4 transition-transform\",\r\n isExpanded && \"rotate-90\"\r\n )}\r\n />\r\n </Button>\r\n\r\n <div className={cn(\r\n \"h-8 w-8 mt-1 rounded-full bg-primary flex items-center justify-center flex-shrink-0\",\r\n !isVisible && \"opacity-70 grayscale\"\r\n )}>\r\n <SensorIcon className={cn(\"h-4 w-4 text-primary-foreground\", !isVisible && \"opacity-70\")} />\r\n </div>\r\n\r\n <div className=\"flex-1 min-w-0\">\r\n <div className=\"flex items-center gap-2\">\r\n <span className={cn(\"text-sm font-medium\", isVisible ? \"text-foreground\" : \"text-muted-foreground\")}>\r\n {sensor.name}\r\n </span>\r\n {sensor.updatedAt !== sensor.createdAt && (\r\n <Badge variant=\"outline\" className=\"text-xs\">\r\n {t('edited')}\r\n </Badge>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* Action Buttons */}\r\n {onAction && (\r\n <div className={cn(\"flex items-center gap-0 flex-shrink-0\", !isVisible && \"opacity-70\")}>\r\n {user?.id === String(sensor.authorId) && (\r\n <>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"h-8 w-8 p-0\"\r\n onClick={() => onAction('edit', sensor.id)}\r\n title={t('editSensor')}\r\n >\r\n <LR.Pencil className=\"h-4 w-4\" />\r\n </Button>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"h-8 w-8 p-0\"\r\n onClick={() => onAction('delete', sensor.id)}\r\n title={t('deleteSensor')}\r\n >\r\n <LR.Trash2 className=\"h-4 w-4\" />\r\n </Button>\r\n </>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Expanded Content */ }\r\n {\r\n isExpanded && (\r\n <div className=\"border-t bg-muted/30\">\r\n <div className=\"p-4 space-y-4\">\r\n {/* Sensor Information */}\r\n <div className=\"space-y-2\">\r\n <div className=\"flex items-center justify-between\">\r\n <span className=\"text-xs text-muted-foreground\">\r\n {format(new Date(sensor.createdAt), 'PPp')}\r\n </span>\r\n <Badge variant=\"secondary\" className=\"text-xs\">\r\n {typeName}\r\n </Badge>\r\n </div>\r\n \r\n <div className=\"grid grid-cols-2 gap-2 text-sm\">\r\n <div>\r\n <span className=\"text-muted-foreground\">Name:</span>\r\n <span className=\"ml-2 font-medium\">{sensor.name}</span>\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">Format:</span>\r\n <span className=\"ml-2 font-medium\">{sensor.dataFormat}</span>\r\n </div>\r\n </div>\r\n\r\n {/* Author Information */}\r\n <div className=\"flex items-center gap-2 pt-2\">\r\n <span className=\"text-muted-foreground text-sm\">Author:</span>\r\n <Avatar className=\"h-5 w-5 rounded-full overflow-hidden\">\r\n <UserAvatar imageFileId={author?.imageFileId} name={authorName} />\r\n </Avatar>\r\n <span className=\"text-sm font-medium\">{authorName}</span>\r\n </div>\r\n </div>\r\n\r\n {/* Sensor Data Chart */}\r\n <SensorChart\r\n sensorData={sensorData}\r\n isLoadingData={isLoadingData}\r\n sensorName={sensor.name}\r\n sensorType={sensorType}\r\n updateFrequency={sensor.updateFrequency}\r\n chartConfig={chartConfig}\r\n />\r\n </div>\r\n <div>\r\n <SensorTagsSection\r\n tags={sensor.tags ?? []}\r\n onAdd={handleAddTag}\r\n onDelete={handleDeleteTag}\r\n translations={{\r\n addTag: t('addTag'),\r\n removeTag: t('removeTag'),\r\n cancel: t('cancel'),\r\n newTagPlaceholder: t('newTagPlaceholder'),\r\n }}\r\n />\r\n </div>\r\n\r\n </div>\r\n )\r\n }\r\n </div >\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","t"]}
@@ -1 +1 @@
1
- {"version":3,"file":"SensorsSection.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/Sensors/SensorsSection.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA0B9B,6EAA6E;AAC7E,eAAO,MAAM,YAAY,iBAAiB,CAAA;AAE1C,wBAAgB,cAAc,CAAC,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,qBAkRzE"}
1
+ {"version":3,"file":"SensorsSection.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/Sensors/SensorsSection.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA2B9B,6EAA6E;AAC7E,eAAO,MAAM,YAAY,iBAAiB,CAAA;AAE1C,wBAAgB,cAAc,CAAC,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,qBAkRzE"}
@@ -22,6 +22,7 @@ import { toast } from "sonner";
22
22
  import { useSensorTypes } from "../../../hooks/sensorTypes/sensorTypes";
23
23
  import * as LR from "lucide-react";
24
24
  import { stringToColour } from "../../viewers/map/utils/stringToColour";
25
+ import { resolveLucideIcon } from "./sensorUtils";
25
26
  const UNTAGGED_TAG = "__untagged__";
26
27
  function SensorsSection({ minioBaseUrl }) {
27
28
  var _a, _b;
@@ -187,7 +188,7 @@ function SensorsSection({ minioBaseUrl }) {
187
188
  const typeId = (_a2 = type == null ? void 0 : type.id) != null ? _a2 : -1;
188
189
  const viewerTypes = visibleSensorTypes == null ? void 0 : visibleSensorTypes[currentViewer];
189
190
  const isTypeVisible = (viewerTypes == null ? void 0 : viewerTypes.length) > 0 && viewerTypes.includes(typeId);
190
- const sensorIcon = LR[type == null ? void 0 : type.icon] || LR.Radio;
191
+ const sensorIcon = resolveLucideIcon(type == null ? void 0 : type.icon);
191
192
  return /* @__PURE__ */ jsx(
192
193
  CollapsibleSection,
193
194
  {