@collabdt/core 0.3.2 → 0.3.3

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 (145) hide show
  1. package/dist/core/components/ui/Comments/CollapsibleCommentItem.js.map +1 -1
  2. package/dist/core/components/ui/Comments/Comment.js.map +1 -1
  3. package/dist/core/components/ui/Comments/CommentActionButtons.d.ts +5 -1
  4. package/dist/core/components/ui/Comments/CommentActionButtons.d.ts.map +1 -1
  5. package/dist/core/components/ui/Comments/CommentActionButtons.js +37 -9
  6. package/dist/core/components/ui/Comments/CommentActionButtons.js.map +1 -1
  7. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts +1 -2
  8. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
  9. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +66 -94
  10. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
  11. package/dist/core/components/ui/Sensors/Sensor.d.ts +16 -3
  12. package/dist/core/components/ui/Sensors/Sensor.d.ts.map +1 -1
  13. package/dist/core/components/ui/Sensors/Sensor.js +76 -37
  14. package/dist/core/components/ui/Sensors/Sensor.js.map +1 -1
  15. package/dist/core/components/ui/Sensors/SensorChart.d.ts +12 -2
  16. package/dist/core/components/ui/Sensors/SensorChart.d.ts.map +1 -1
  17. package/dist/core/components/ui/Sensors/SensorChart.js +49 -12
  18. package/dist/core/components/ui/Sensors/SensorChart.js.map +1 -1
  19. package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts +9 -0
  20. package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts.map +1 -0
  21. package/dist/core/components/ui/Sensors/SensorDetailDialog.js +128 -0
  22. package/dist/core/components/ui/Sensors/SensorDetailDialog.js.map +1 -0
  23. package/dist/core/components/ui/Sensors/SensorInput.d.ts +6 -1
  24. package/dist/core/components/ui/Sensors/SensorInput.d.ts.map +1 -1
  25. package/dist/core/components/ui/Sensors/SensorInput.js +94 -8
  26. package/dist/core/components/ui/Sensors/SensorInput.js.map +1 -1
  27. package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts +3 -1
  28. package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts.map +1 -1
  29. package/dist/core/components/ui/Sensors/SensorTagsSection.js +4 -1
  30. package/dist/core/components/ui/Sensors/SensorTagsSection.js.map +1 -1
  31. package/dist/core/components/ui/Sensors/SensorsSection.d.ts +1 -3
  32. package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
  33. package/dist/core/components/ui/Sensors/SensorsSection.js +33 -8
  34. package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
  35. package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts +7 -0
  36. package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts.map +1 -0
  37. package/dist/core/components/ui/Sensors/TimeZoneSelect.js +56 -0
  38. package/dist/core/components/ui/Sensors/TimeZoneSelect.js.map +1 -0
  39. package/dist/core/components/ui/Sensors/sensorData.d.ts +42 -0
  40. package/dist/core/components/ui/Sensors/sensorData.d.ts.map +1 -0
  41. package/dist/core/components/ui/Sensors/sensorData.js +118 -0
  42. package/dist/core/components/ui/Sensors/sensorData.js.map +1 -0
  43. package/dist/core/components/ui/Sensors/sensorRange.d.ts +24 -0
  44. package/dist/core/components/ui/Sensors/sensorRange.d.ts.map +1 -0
  45. package/dist/core/components/ui/Sensors/sensorRange.js +34 -0
  46. package/dist/core/components/ui/Sensors/sensorRange.js.map +1 -0
  47. package/dist/core/components/ui/Sensors/useSensorSeries.d.ts +15 -0
  48. package/dist/core/components/ui/Sensors/useSensorSeries.d.ts.map +1 -0
  49. package/dist/core/components/ui/Sensors/useSensorSeries.js +63 -0
  50. package/dist/core/components/ui/Sensors/useSensorSeries.js.map +1 -0
  51. package/dist/core/components/viewers/Viewer.js +1 -1
  52. package/dist/core/components/viewers/Viewer.js.map +1 -1
  53. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts +1 -3
  54. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts.map +1 -1
  55. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js +2 -2
  56. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js.map +1 -1
  57. package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js +1 -1
  58. package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js.map +1 -1
  59. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts +1 -2
  60. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts.map +1 -1
  61. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js +25 -2
  62. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js.map +1 -1
  63. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js.map +1 -1
  64. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts +1 -22
  65. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts.map +1 -1
  66. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js +2 -105
  67. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js.map +1 -1
  68. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts +8 -1
  69. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts.map +1 -1
  70. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js +15 -3
  71. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js.map +1 -1
  72. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js.map +1 -1
  73. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts +2 -23
  74. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts.map +1 -1
  75. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js +2 -24
  76. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js.map +1 -1
  77. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts +33 -0
  78. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts.map +1 -0
  79. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js +90 -0
  80. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js.map +1 -0
  81. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts +7 -1
  82. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts.map +1 -1
  83. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js +26 -230
  84. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js.map +1 -1
  85. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts +7 -1
  86. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts.map +1 -1
  87. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js +39 -11
  88. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js.map +1 -1
  89. package/dist/core/components/viewers/map/MapViewer.d.ts +1 -2
  90. package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
  91. package/dist/core/components/viewers/map/MapViewer.js +2 -2
  92. package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
  93. package/dist/core/components/viewers/map/src/MapLayers/index.d.ts +1 -2
  94. package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
  95. package/dist/core/components/viewers/map/src/MapLayers/index.js +2 -2
  96. package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
  97. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts +1 -3
  98. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts.map +1 -1
  99. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js +74 -39
  100. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js.map +1 -1
  101. package/dist/core/components/viewers/map/src/MapSidebar/index.js +1 -1
  102. package/dist/core/components/viewers/map/src/MapSidebar/index.js.map +1 -1
  103. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts +1 -3
  104. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts.map +1 -1
  105. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js +2 -2
  106. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js.map +1 -1
  107. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts +19 -0
  108. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts.map +1 -0
  109. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js +108 -0
  110. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js.map +1 -0
  111. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts +24 -0
  112. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts.map +1 -0
  113. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js +27 -0
  114. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js.map +1 -0
  115. package/dist/core/components/viewers/shared/markers/types.d.ts +66 -0
  116. package/dist/core/components/viewers/shared/markers/types.d.ts.map +1 -0
  117. package/dist/core/components/viewers/shared/markers/types.js +1 -0
  118. package/dist/core/components/viewers/shared/markers/types.js.map +1 -0
  119. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts +22 -0
  120. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts.map +1 -0
  121. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js +233 -0
  122. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js.map +1 -0
  123. package/dist/core/hooks/ui/useResizableSidebarWidth.js.map +1 -1
  124. package/dist/core/i18n/messages/en.json +5 -1
  125. package/dist/core/i18n/messages/es.json +5 -1
  126. package/dist/core/i18n/messages/fr.json +5 -1
  127. package/dist/core/store/AppConfig/context.d.ts.map +1 -1
  128. package/dist/core/store/AppConfig/context.js +4 -1
  129. package/dist/core/store/AppConfig/context.js.map +1 -1
  130. package/dist/core/store/AppConfig/reducer.d.ts +10 -0
  131. package/dist/core/store/AppConfig/reducer.d.ts.map +1 -1
  132. package/dist/core/store/AppConfig/reducer.js +10 -0
  133. package/dist/core/store/AppConfig/reducer.js.map +1 -1
  134. package/dist/core/store/Menus/context.d.ts.map +1 -1
  135. package/dist/core/store/Menus/context.js +5 -2
  136. package/dist/core/store/Menus/context.js.map +1 -1
  137. package/dist/core/store/Menus/reducer.d.ts +45 -1
  138. package/dist/core/store/Menus/reducer.d.ts.map +1 -1
  139. package/dist/core/store/Menus/reducer.js +21 -3
  140. package/dist/core/store/Menus/reducer.js.map +1 -1
  141. package/dist/core/utils/timeUtils.d.ts +24 -1
  142. package/dist/core/utils/timeUtils.d.ts.map +1 -1
  143. package/dist/core/utils/timeUtils.js +52 -3
  144. package/dist/core/utils/timeUtils.js.map +1 -1
  145. package/package.json +1 -1
@@ -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 { format } from 'date-fns'\r\nimport * as LR from 'lucide-react'\r\nimport { useSession } from 'next-auth/react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\n\r\nimport { useCoreHooks } from '../../../hooks/provider'\r\nimport { useUser } from '../../../hooks/users/users'\r\nimport { cn } from '../../../utils/utils'\r\nimport { Avatar, AvatarFallback } from '../Avatar'\r\nimport { Badge } from '../Badge'\r\nimport { Button } from '../Button'\r\nimport { UserAvatar } from '../UserAvatar'\r\n\r\nimport { SensorChart } from './SensorChart'\r\nimport { SensorTagsSection } from './SensorTagsSection'\r\nimport { resolveLucideIcon } from './sensorUtils'\r\n\r\nimport type { Sensor, SensorType } from '../../../types/dbTypes'\r\nimport type { ChartConfig } from '../chart'\r\n\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 const 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
+ {"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 LR from 'lucide-react'\r\nimport { useSession } from 'next-auth/react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\n\r\nimport { useCoreHooks } from '../../../hooks/provider'\r\nimport { useUser } from '../../../hooks/users/users'\r\nimport { AppConfigContext } from '../../../store'\r\nimport { formatInZone } from '../../../utils/timeUtils'\r\nimport { cn } from '../../../utils/utils'\r\nimport { Avatar, AvatarFallback } from '../Avatar'\r\nimport { Badge } from '../Badge'\r\nimport { Button } from '../Button'\r\nimport { CommentActionButtons } from '../Comments/CommentActionButtons'\r\nimport { UserAvatar } from '../UserAvatar'\r\n\r\nimport { SensorChart } from './SensorChart'\r\nimport { SensorDetailDialog } from './SensorDetailDialog'\r\nimport { SensorTagsSection } from './SensorTagsSection'\r\nimport { resolveLucideIcon } from './sensorUtils'\r\nimport { useSensorSeries } from './useSensorSeries'\r\n\r\nimport type { Sensor, SensorType } from '../../../types/dbTypes'\r\nimport type { ChartConfig } from '../chart'\r\n\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}\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}: CollapsibleSensorItemProps) {\r\n const t = useTranslations('SensorsSection')\r\n const [isExpanded, setIsExpanded] = React.useState(false)\r\n const { state: appConfigState } = React.useContext(AppConfigContext)\r\n const timeZone = appConfigState.appConfig.displayTimeZone\r\n const [detailOpen, setDetailOpen] = React.useState(false)\r\n const [tagAddNonce, setTagAddNonce] = React.useState(0)\r\n const { points: sensorData, unit, valueLabels, isLoading: isLoadingData } =\r\n useSensorSeries(sensor.url ?? '', sensor.dataFormat, sensor.updateFrequency, { enabled: isExpanded })\r\n const prevVisibleRef = React.useRef(isVisible)\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 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 const 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 </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 {formatInZone(new Date(sensor.createdAt).getTime(), timeZone, { dateStyle: 'medium', timeStyle: 'short' })}\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 unit={unit}\r\n valueLabels={valueLabels}\r\n timeZone={timeZone}\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 openAddSignal={tagAddNonce}\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 {/* Card tools row (bottom), mirroring the comment card */}\r\n <div className={cn(\"flex items-center justify-end gap-0 border-t px-2 py-1\", !isVisible && \"opacity-70\")}>\r\n <CommentActionButtons\r\n onExpand={() => setDetailOpen(true)}\r\n onTag={() => { setIsExpanded(true); setTagAddNonce(n => n + 1) }}\r\n onEdit={onAction && user?.id === String(sensor.authorId) ? () => onAction('edit', sensor.id) : undefined}\r\n onDelete={onAction && user?.id === String(sensor.authorId) ? () => onAction('delete', sensor.id) : undefined}\r\n buttonClassName=\"h-8 w-8\"\r\n labels={{ expand: t('expandSensor'), tag: t('addTag'), edit: t('editSensor'), delete: t('deleteSensor') }}\r\n />\r\n </div>\r\n <SensorDetailDialog open={detailOpen} onOpenChange={setDetailOpen} sensor={sensor} sensorType={sensorType} />\r\n </div >\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","t"]}
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
- import { SensorDataFormat } from '../../../types/dbTypes';
2
+ import { type CommentActionLabels } from '../Comments/CommentActionButtons';
3
3
  import { type SensorTagsTranslations } from './SensorTagsSection';
4
- import type { SensorType } from '../../../types/dbTypes';
4
+ import type { SensorDataFormat, SensorType } from '../../../types/dbTypes';
5
5
  export type SensorProps = {
6
6
  sensorName: string;
7
7
  sensorType: SensorType;
@@ -18,10 +18,23 @@ export type SensorProps = {
18
18
  createdAt: string | Date;
19
19
  onRemove?: () => void;
20
20
  onClose?: () => void;
21
+ /** Edit the sensor (used by the in-viewer BIM card action row). */
22
+ onEdit?: () => void;
23
+ /** Open the expandable detail view (wired into the shared action row). */
24
+ onExpand?: () => void;
25
+ /** Display timezone for the chart + created line. Defaults to the browser zone. */
26
+ timeZone?: string;
21
27
  enableCollapse?: boolean;
22
28
  defaultCollapsed?: boolean;
23
29
  size?: 'sm' | 'md' | 'lg';
24
30
  highlight?: boolean;
31
+ /** Double-clicked/zoomed sensor: reserved for the focus ring, mirrors Comment's `focused`. */
32
+ focused?: boolean;
33
+ /** Render the close/edit/delete action row (used by the in-viewer BIM card). */
34
+ showActions?: boolean;
35
+ canEdit?: boolean;
36
+ canDelete?: boolean;
37
+ actionLabels?: CommentActionLabels;
25
38
  };
26
- export default function Sensor({ sensorName, sensorType, dataUrl, dataFormat, updateFrequency, createdAt, onRemove, onClose, enableCollapse, defaultCollapsed, size, highlight, sensorId, tags, tagsVariant, tagsTranslations, onAddTag, onDeleteTag, }: SensorProps): React.JSX.Element;
39
+ export default function Sensor({ sensorName, sensorType, dataUrl, dataFormat, updateFrequency, createdAt, onRemove, onClose, onEdit, onExpand, enableCollapse, defaultCollapsed, size, highlight, focused, showActions, canEdit, canDelete, actionLabels, sensorId, tags, tagsVariant, tagsTranslations, onAddTag, onDeleteTag, timeZone, }: SensorProps): React.JSX.Element;
27
40
  //# sourceMappingURL=Sensor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Sensor.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/Sensors/Sensor.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAMzD,OAAO,EAAqB,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAEpF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,UAAU,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,gBAAgB,GAAG,GAAG,gBAAgB,EAAE,CAAA;IACpD,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAA;IAClC,gBAAgB,CAAC,EAAE,sBAAsB,CAAA;IACzC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACzC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAED,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAC7B,UAAU,EACV,UAAU,EACV,OAAO,EACP,UAAU,EACV,eAAe,EACf,SAAS,EACT,QAAQ,EACR,OAAO,EACP,cAAsB,EACtB,gBAAwB,EACxB,IAAW,EACX,SAAiB,EACjB,QAAQ,EACR,IAAS,EACT,WAAoB,EACpB,gBAAgB,EAChB,QAAQ,EACR,WAAW,GACZ,EAAE,WAAW,qBA6Lb"}
1
+ {"version":3,"file":"Sensor.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/Sensors/Sensor.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,OAAO,EAAwB,KAAK,mBAAmB,EAAE,MAAM,kCAAkC,CAAA;AAGjG,OAAO,EAAqB,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAGpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAG3E,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,UAAU,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,gBAAgB,GAAG,GAAG,gBAAgB,EAAE,CAAA;IACpD,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAA;IAClC,gBAAgB,CAAC,EAAE,sBAAsB,CAAA;IACzC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACzC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,8FAA8F;IAC9F,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gFAAgF;IAChF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,YAAY,CAAC,EAAE,mBAAmB,CAAA;CACnC,CAAA;AAED,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAC7B,UAAU,EACV,UAAU,EACV,OAAO,EACP,UAAU,EACV,eAAe,EACf,SAAS,EACT,QAAQ,EACR,OAAO,EACP,MAAM,EACN,QAAQ,EACR,cAAsB,EACtB,gBAAwB,EACxB,IAAW,EACX,SAAiB,EACjB,OAAe,EACf,WAAmB,EACnB,OAAc,EACd,SAAgB,EAChB,YAAY,EACZ,QAAQ,EACR,IAAS,EACT,WAAoB,EACpB,gBAAgB,EAChB,QAAQ,EACR,WAAW,EACX,QAA2B,GAC5B,EAAE,WAAW,qBAsMb"}
@@ -1,13 +1,33 @@
1
1
  "use client";
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __spreadValues = (a, b) => {
10
+ for (var prop in b || (b = {}))
11
+ if (__hasOwnProp.call(b, prop))
12
+ __defNormalProp(a, prop, b[prop]);
13
+ if (__getOwnPropSymbols)
14
+ for (var prop of __getOwnPropSymbols(b)) {
15
+ if (__propIsEnum.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ }
18
+ return a;
19
+ };
20
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
2
21
  import { jsx, jsxs } from "react/jsx-runtime";
3
22
  import * as LR from "lucide-react";
4
23
  import * as React from "react";
5
- import { SensorDataFormat } from "../../../types/dbTypes";
6
- import { formatTimestamp } from "../../../utils/timeUtils";
24
+ import { formatTimestamp, detectTimeZone } from "../../../utils/timeUtils";
7
25
  import { Button } from "../Button";
8
26
  import { Card } from "../Card";
27
+ import { CommentActionButtons } from "../Comments/CommentActionButtons";
9
28
  import { SensorChart } from "./SensorChart";
10
29
  import { SensorTagsSection } from "./SensorTagsSection";
30
+ import { useSensorSeries } from "./useSensorSeries";
11
31
  function Sensor({
12
32
  sensorName,
13
33
  sensorType,
@@ -17,51 +37,31 @@ function Sensor({
17
37
  createdAt,
18
38
  onRemove,
19
39
  onClose,
40
+ onEdit,
41
+ onExpand,
20
42
  enableCollapse = false,
21
43
  defaultCollapsed = false,
22
44
  size = "md",
23
45
  highlight = false,
46
+ focused = false,
47
+ showActions = false,
48
+ canEdit = true,
49
+ canDelete = true,
50
+ actionLabels,
24
51
  sensorId,
25
52
  tags = [],
26
53
  tagsVariant = "edit",
27
54
  tagsTranslations,
28
55
  onAddTag,
29
- onDeleteTag
56
+ onDeleteTag,
57
+ timeZone = detectTimeZone()
30
58
  }) {
31
- var _a;
59
+ var _a, _b, _c;
32
60
  const [isCollapsed, setIsCollapsed] = React.useState(defaultCollapsed);
33
- const [sensorData, setSensorData] = React.useState([]);
34
- const [isLoadingData, setIsLoadingData] = React.useState(false);
61
+ const { points: sensorData, unit, valueLabels, isLoading: isLoadingData } = useSensorSeries(dataUrl, dataFormat, updateFrequency, { enabled: !(enableCollapse && isCollapsed) });
35
62
  const lastTap = React.useRef(0);
36
63
  const sensorTypeNameFormatted = ((_a = sensorType == null ? void 0 : sensorType.name) == null ? void 0 : _a.replace(/_/g, " ")) || "Other";
37
64
  const SensorIcon = LR[sensorType == null ? void 0 : sensorType.icon] || LR.Radio;
38
- React.useEffect(() => {
39
- const fetchData = async () => {
40
- if (!dataUrl || dataFormat !== SensorDataFormat.Csv) {
41
- return;
42
- }
43
- setIsLoadingData(true);
44
- try {
45
- const response = await fetch(dataUrl);
46
- const text = await response.text();
47
- const lines = text.trim().split("\n");
48
- const data = lines.map((line) => {
49
- const [time, value] = line.split(",");
50
- return {
51
- time: time.trim(),
52
- value: parseFloat(value)
53
- };
54
- });
55
- setSensorData(data);
56
- } catch (error) {
57
- console.error("Failed to fetch sensor data:", error);
58
- setSensorData([]);
59
- } finally {
60
- setIsLoadingData(false);
61
- }
62
- };
63
- fetchData();
64
- }, [dataUrl, dataFormat]);
65
65
  const chartConfig = {
66
66
  value: {
67
67
  label: "Value",
@@ -120,6 +120,10 @@ function Sensor({
120
120
  if (now - lastTap.current < 300) setIsCollapsed((v) => !v);
121
121
  lastTap.current = now;
122
122
  };
123
+ const handleClose = () => {
124
+ if (enableCollapse) setIsCollapsed(true);
125
+ else onClose == null ? void 0 : onClose();
126
+ };
123
127
  if (enableCollapse && isCollapsed) {
124
128
  return /* @__PURE__ */ jsx(
125
129
  "div",
@@ -152,7 +156,26 @@ function Sensor({
152
156
  /* @__PURE__ */ jsx("span", { className: `font-semibold ${currentSize.titleSize} text-foreground leading-none truncate`, children: sensorName }),
153
157
  /* @__PURE__ */ jsx("span", { className: `${currentSize.subtitleSize} text-muted-foreground leading-none`, children: sensorTypeNameFormatted })
154
158
  ] }),
155
- onRemove && /* @__PURE__ */ jsx(
159
+ showActions && /* @__PURE__ */ jsx(
160
+ Button,
161
+ __spreadProps(__spreadValues({
162
+ type: "button",
163
+ variant: "ghost",
164
+ size: "icon",
165
+ title: (_b = actionLabels == null ? void 0 : actionLabels.close) != null ? _b : "Close",
166
+ "aria-label": (_c = actionLabels == null ? void 0 : actionLabels.close) != null ? _c : "Close",
167
+ className: `z-50 ${currentSize.buttonSize} shrink-0 -mr-1 -mt-1`
168
+ }, enableCollapse ? { onPointerDown: (e) => {
169
+ e.stopPropagation();
170
+ handleClose();
171
+ } } : { onClick: (e) => {
172
+ e.stopPropagation();
173
+ handleClose();
174
+ } }), {
175
+ children: /* @__PURE__ */ jsx(LR.X, { className: currentSize.buttonIconSize })
176
+ })
177
+ ),
178
+ !showActions && onRemove && /* @__PURE__ */ jsx(
156
179
  Button,
157
180
  {
158
181
  type: "button",
@@ -171,7 +194,7 @@ function Sensor({
171
194
  ] }),
172
195
  /* @__PURE__ */ jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxs("span", { className: `${currentSize.metaSize} text-muted-foreground`, children: [
173
196
  "Created ",
174
- formatTimestamp(createdAt)
197
+ formatTimestamp(createdAt, timeZone)
175
198
  ] }) }),
176
199
  /* @__PURE__ */ jsx(
177
200
  SensorChart,
@@ -182,7 +205,10 @@ function Sensor({
182
205
  sensorType,
183
206
  updateFrequency,
184
207
  chartConfig,
185
- size
208
+ size,
209
+ unit,
210
+ valueLabels,
211
+ timeZone
186
212
  }
187
213
  ),
188
214
  /* @__PURE__ */ jsx(
@@ -194,7 +220,20 @@ function Sensor({
194
220
  onDelete: onDeleteTag,
195
221
  translations: tagsTranslations
196
222
  }
197
- )
223
+ ),
224
+ showActions && (onEdit || onRemove || onExpand) && /* @__PURE__ */ jsx("div", { className: "mt-2 flex justify-end border-t pt-1", children: /* @__PURE__ */ jsx(
225
+ CommentActionButtons,
226
+ {
227
+ onExpand,
228
+ onEdit: canEdit ? onEdit : void 0,
229
+ onDelete: canDelete ? onRemove : void 0,
230
+ canEdit,
231
+ canDelete,
232
+ activateOnPointerDown: enableCollapse,
233
+ buttonClassName: "h-7 w-7",
234
+ labels: { edit: actionLabels == null ? void 0 : actionLabels.edit, delete: actionLabels == null ? void 0 : actionLabels.delete, close: actionLabels == null ? void 0 : actionLabels.close, expand: actionLabels == null ? void 0 : actionLabels.expand }
235
+ }
236
+ ) })
198
237
  ] })
199
238
  }
200
239
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/core/components/ui/Sensors/Sensor.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 LR from 'lucide-react'\r\nimport * as React from 'react'\r\n\r\nimport { SensorDataFormat } from '../../../types/dbTypes'\r\nimport { formatTimestamp } from '../../../utils/timeUtils'\r\nimport { Button } from '../Button'\r\nimport { Card } from '../Card'\r\n\r\nimport { SensorChart } from './SensorChart'\r\nimport { SensorTagsSection, type SensorTagsTranslations } from './SensorTagsSection'\r\n\r\nimport type { SensorType } from '../../../types/dbTypes';\r\nimport type { ChartConfig } from '../chart'\r\n\r\nexport type SensorProps = {\r\n sensorName: string\r\n sensorType: SensorType\r\n dataUrl: string\r\n dataFormat: SensorDataFormat | `${SensorDataFormat}`\r\n updateFrequency: number\r\n buildingId?: number\r\n sensorId?: number\r\n tags?: string[]\r\n tagsVariant?: 'edit' | 'read-only'\r\n tagsTranslations?: SensorTagsTranslations\r\n onAddTag?: (tag: string) => Promise<void>\r\n onDeleteTag?: (tag: string) => Promise<void>\r\n createdAt: string | Date\r\n onRemove?: () => void\r\n onClose?: () => void\r\n enableCollapse?: boolean\r\n defaultCollapsed?: boolean\r\n size?: 'sm' | 'md' | 'lg'\r\n highlight?: boolean\r\n}\r\n\r\nexport default function Sensor({\r\n sensorName,\r\n sensorType,\r\n dataUrl,\r\n dataFormat,\r\n updateFrequency,\r\n createdAt,\r\n onRemove,\r\n onClose,\r\n enableCollapse = false,\r\n defaultCollapsed = false,\r\n size = 'md',\r\n highlight = false,\r\n sensorId,\r\n tags = [],\r\n tagsVariant = 'edit',\r\n tagsTranslations,\r\n onAddTag,\r\n onDeleteTag,\r\n}: SensorProps) {\r\n const [isCollapsed, setIsCollapsed] = React.useState(defaultCollapsed)\r\n const [sensorData, setSensorData] = React.useState<{ time: string; value: number }[]>([])\r\n const [isLoadingData, setIsLoadingData] = React.useState(false)\r\n const lastTap = React.useRef(0)\r\n\r\n const sensorTypeNameFormatted = sensorType?.name?.replace(/_/g, ' ') || 'Other'\r\n const SensorIcon = LR[sensorType?.icon] || LR.Radio\r\n\r\n // Fetch sensor data\r\n React.useEffect(() => {\r\n const fetchData = async () => {\r\n if (!dataUrl || dataFormat !== SensorDataFormat.Csv) {\r\n return\r\n }\r\n\r\n setIsLoadingData(true)\r\n try {\r\n const response = await fetch(dataUrl)\r\n const text = await response.text()\r\n\r\n const lines = text.trim().split('\\n')\r\n const data = lines.map((line) => {\r\n const [time, value] = line.split(',')\r\n return {\r\n time: time.trim(),\r\n value: parseFloat(value),\r\n }\r\n })\r\n\r\n setSensorData(data)\r\n } catch (error) {\r\n console.error('Failed to fetch sensor data:', error)\r\n setSensorData([])\r\n } finally {\r\n setIsLoadingData(false)\r\n }\r\n }\r\n\r\n fetchData()\r\n }, [dataUrl, dataFormat])\r\n\r\n const chartConfig: ChartConfig = {\r\n value: {\r\n label: 'Value',\r\n color: 'hsl(var(--chart-1))',\r\n },\r\n }\r\n\r\n const sizeStyles = {\r\n sm: {\r\n cardWidth: 'w-[280px]',\r\n cardPadding: 'px-2 py-2',\r\n iconSize: 'h-7 w-7',\r\n iconInnerSize: 'h-4 w-4',\r\n titleSize: 'text-xs',\r\n subtitleSize: 'text-[10px]',\r\n metaSize: 'text-[10px]',\r\n buttonSize: 'h-6 w-6',\r\n buttonIconSize: 'h-3 w-3',\r\n gap: 'gap-2',\r\n collapsedIconSize: 'h-8 w-8',\r\n collapsedIconInnerSize: 'h-4 w-4',\r\n },\r\n md: {\r\n cardWidth: 'w-[410px]',\r\n cardPadding: 'px-3 py-2.5',\r\n iconSize: 'h-9 w-9',\r\n iconInnerSize: 'h-5 w-5',\r\n titleSize: 'text-sm',\r\n subtitleSize: 'text-xs',\r\n metaSize: 'text-xs',\r\n buttonSize: 'h-7 w-7',\r\n buttonIconSize: 'h-4 w-4',\r\n gap: 'gap-3',\r\n collapsedIconSize: 'h-9 w-9',\r\n collapsedIconInnerSize: 'h-5 w-5',\r\n },\r\n lg: {\r\n cardWidth: 'w-[500px]',\r\n cardPadding: 'px-4 py-3',\r\n iconSize: 'h-11 w-11',\r\n iconInnerSize: 'h-6 w-6',\r\n titleSize: 'text-base',\r\n subtitleSize: 'text-sm',\r\n metaSize: 'text-sm',\r\n buttonSize: 'h-8 w-8',\r\n buttonIconSize: 'h-5 w-5',\r\n gap: 'gap-4',\r\n collapsedIconSize: 'h-11 w-11',\r\n collapsedIconInnerSize: 'h-6 w-6',\r\n },\r\n }\r\n\r\n const currentSize = sizeStyles[size]\r\n\r\n const handlePointerDown = (e: React.PointerEvent) => {\r\n if (!enableCollapse) return\r\n e.stopPropagation()\r\n\r\n const now = performance.now()\r\n if (now - lastTap.current < 300) setIsCollapsed(v => !v)\r\n lastTap.current = now\r\n }\r\n\r\n if (enableCollapse && isCollapsed) {\r\n return (\r\n <div\r\n className={`${currentSize.collapsedIconSize} rounded-full shadow-md flex items-center justify-center flex-shrink-0 bg-primary pointer-events-auto`}\r\n onPointerDown={handlePointerDown}\r\n onDoubleClick={(e) => {\r\n e.stopPropagation()\r\n setIsCollapsed(v => !v)\r\n }}\r\n >\r\n <SensorIcon className={`${currentSize.collapsedIconInnerSize} text-primary-foreground cursor-pointer`} />\r\n </div>\r\n )\r\n }\r\n\r\n return (\r\n <div\r\n className=\"relative inline-block group pointer-events-auto\"\r\n title={enableCollapse ? 'Double click to collapse' : undefined}\r\n onPointerDown={handlePointerDown}\r\n onDoubleClick={(e) => {\r\n e.stopPropagation()\r\n if (enableCollapse) setIsCollapsed(v => !v)\r\n else onClose?.()\r\n }}\r\n >\r\n <Card className={`${currentSize.cardWidth} ${currentSize.cardPadding} shadow-md group-hover:shadow-lg transition-shadow duration-200`}>\r\n <div className={`flex items-start ${currentSize.gap}`}>\r\n <div className={`${currentSize.iconSize} rounded-full shadow-sm flex items-center justify-center select-none flex-shrink-0 bg-primary`}>\r\n <SensorIcon className={`${currentSize.iconInnerSize} text-primary-foreground`} />\r\n </div>\r\n\r\n <div className=\"flex-1 min-w-0 flex flex-col gap-1\">\r\n <span className={`font-semibold ${currentSize.titleSize} text-foreground leading-none truncate`}>\r\n {sensorName}\r\n </span>\r\n <span className={`${currentSize.subtitleSize} text-muted-foreground leading-none`}>\r\n {sensorTypeNameFormatted}\r\n </span>\r\n </div>\r\n\r\n {onRemove && (\r\n <Button\r\n type=\"button\"\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n title=\"Remove sensor\"\r\n className={`z-50 ${currentSize.buttonSize} shrink-0`}\r\n aria-label=\"Remove sensor\"\r\n onClick={(e) => {\r\n e.stopPropagation()\r\n onRemove()\r\n }}\r\n >\r\n <LR.Trash2 className={currentSize.buttonIconSize} />\r\n </Button>\r\n )}\r\n </div>\r\n\r\n <div className=\"mb-2\">\r\n <span className={`${currentSize.metaSize} text-muted-foreground`}>\r\n Created {formatTimestamp(createdAt)}\r\n </span>\r\n </div>\r\n\r\n <SensorChart\r\n sensorData={sensorData}\r\n isLoadingData={isLoadingData}\r\n sensorName={sensorName}\r\n sensorType={sensorType}\r\n updateFrequency={updateFrequency}\r\n chartConfig={chartConfig}\r\n size={size}\r\n />\r\n <SensorTagsSection\r\n tags={tags}\r\n variant={tagsVariant}\r\n onAdd={onAddTag}\r\n onDelete={onDeleteTag}\r\n translations={tagsTranslations}\r\n />\r\n </Card>\r\n </div>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../src/core/components/ui/Sensors/Sensor.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 LR from 'lucide-react'\r\nimport * as React from 'react'\r\n\r\nimport { formatTimestamp, detectTimeZone } from '../../../utils/timeUtils'\r\nimport { Button } from '../Button'\r\nimport { Card } from '../Card'\r\n\r\nimport { CommentActionButtons, type CommentActionLabels } from '../Comments/CommentActionButtons'\r\n\r\nimport { SensorChart } from './SensorChart'\r\nimport { SensorTagsSection, type SensorTagsTranslations } from './SensorTagsSection'\r\nimport { useSensorSeries } from './useSensorSeries'\r\n\r\nimport type { SensorDataFormat, SensorType } from '../../../types/dbTypes';\r\nimport type { ChartConfig } from '../chart'\r\n\r\nexport type SensorProps = {\r\n sensorName: string\r\n sensorType: SensorType\r\n dataUrl: string\r\n dataFormat: SensorDataFormat | `${SensorDataFormat}`\r\n updateFrequency: number\r\n buildingId?: number\r\n sensorId?: number\r\n tags?: string[]\r\n tagsVariant?: 'edit' | 'read-only'\r\n tagsTranslations?: SensorTagsTranslations\r\n onAddTag?: (tag: string) => Promise<void>\r\n onDeleteTag?: (tag: string) => Promise<void>\r\n createdAt: string | Date\r\n onRemove?: () => void\r\n onClose?: () => void\r\n /** Edit the sensor (used by the in-viewer BIM card action row). */\r\n onEdit?: () => void\r\n /** Open the expandable detail view (wired into the shared action row). */\r\n onExpand?: () => void\r\n /** Display timezone for the chart + created line. Defaults to the browser zone. */\r\n timeZone?: string\r\n enableCollapse?: boolean\r\n defaultCollapsed?: boolean\r\n size?: 'sm' | 'md' | 'lg'\r\n highlight?: boolean\r\n /** Double-clicked/zoomed sensor: reserved for the focus ring, mirrors Comment's `focused`. */\r\n focused?: boolean\r\n /** Render the close/edit/delete action row (used by the in-viewer BIM card). */\r\n showActions?: boolean\r\n canEdit?: boolean\r\n canDelete?: boolean\r\n actionLabels?: CommentActionLabels\r\n}\r\n\r\nexport default function Sensor({\r\n sensorName,\r\n sensorType,\r\n dataUrl,\r\n dataFormat,\r\n updateFrequency,\r\n createdAt,\r\n onRemove,\r\n onClose,\r\n onEdit,\r\n onExpand,\r\n enableCollapse = false,\r\n defaultCollapsed = false,\r\n size = 'md',\r\n highlight = false,\r\n focused = false,\r\n showActions = false,\r\n canEdit = true,\r\n canDelete = true,\r\n actionLabels,\r\n sensorId,\r\n tags = [],\r\n tagsVariant = 'edit',\r\n tagsTranslations,\r\n onAddTag,\r\n onDeleteTag,\r\n timeZone = detectTimeZone(),\r\n}: SensorProps) {\r\n const [isCollapsed, setIsCollapsed] = React.useState(defaultCollapsed)\r\n const { points: sensorData, unit, valueLabels, isLoading: isLoadingData } =\r\n useSensorSeries(dataUrl, dataFormat, updateFrequency, { enabled: !(enableCollapse && isCollapsed) })\r\n const lastTap = React.useRef(0)\r\n\r\n const sensorTypeNameFormatted = sensorType?.name?.replace(/_/g, ' ') || 'Other'\r\n const SensorIcon = LR[sensorType?.icon] || LR.Radio\r\n\r\n const chartConfig: ChartConfig = {\r\n value: {\r\n label: 'Value',\r\n color: 'hsl(var(--chart-1))',\r\n },\r\n }\r\n\r\n const sizeStyles = {\r\n sm: {\r\n cardWidth: 'w-[280px]',\r\n cardPadding: 'px-2 py-2',\r\n iconSize: 'h-7 w-7',\r\n iconInnerSize: 'h-4 w-4',\r\n titleSize: 'text-xs',\r\n subtitleSize: 'text-[10px]',\r\n metaSize: 'text-[10px]',\r\n buttonSize: 'h-6 w-6',\r\n buttonIconSize: 'h-3 w-3',\r\n gap: 'gap-2',\r\n collapsedIconSize: 'h-8 w-8',\r\n collapsedIconInnerSize: 'h-4 w-4',\r\n },\r\n md: {\r\n cardWidth: 'w-[410px]',\r\n cardPadding: 'px-3 py-2.5',\r\n iconSize: 'h-9 w-9',\r\n iconInnerSize: 'h-5 w-5',\r\n titleSize: 'text-sm',\r\n subtitleSize: 'text-xs',\r\n metaSize: 'text-xs',\r\n buttonSize: 'h-7 w-7',\r\n buttonIconSize: 'h-4 w-4',\r\n gap: 'gap-3',\r\n collapsedIconSize: 'h-9 w-9',\r\n collapsedIconInnerSize: 'h-5 w-5',\r\n },\r\n lg: {\r\n cardWidth: 'w-[500px]',\r\n cardPadding: 'px-4 py-3',\r\n iconSize: 'h-11 w-11',\r\n iconInnerSize: 'h-6 w-6',\r\n titleSize: 'text-base',\r\n subtitleSize: 'text-sm',\r\n metaSize: 'text-sm',\r\n buttonSize: 'h-8 w-8',\r\n buttonIconSize: 'h-5 w-5',\r\n gap: 'gap-4',\r\n collapsedIconSize: 'h-11 w-11',\r\n collapsedIconInnerSize: 'h-6 w-6',\r\n },\r\n }\r\n\r\n const currentSize = sizeStyles[size]\r\n\r\n const handlePointerDown = (e: React.PointerEvent) => {\r\n if (!enableCollapse) return\r\n e.stopPropagation()\r\n\r\n const now = performance.now()\r\n if (now - lastTap.current < 300) setIsCollapsed(v => !v)\r\n lastTap.current = now\r\n }\r\n\r\n const handleClose = () => {\r\n if (enableCollapse) setIsCollapsed(true)\r\n else onClose?.()\r\n }\r\n\r\n if (enableCollapse && isCollapsed) {\r\n return (\r\n <div\r\n className={`${currentSize.collapsedIconSize} rounded-full shadow-md flex items-center justify-center flex-shrink-0 bg-primary pointer-events-auto`}\r\n onPointerDown={handlePointerDown}\r\n onDoubleClick={(e) => {\r\n e.stopPropagation()\r\n setIsCollapsed(v => !v)\r\n }}\r\n >\r\n <SensorIcon className={`${currentSize.collapsedIconInnerSize} text-primary-foreground cursor-pointer`} />\r\n </div>\r\n )\r\n }\r\n\r\n return (\r\n <div\r\n className=\"relative inline-block group pointer-events-auto\"\r\n title={enableCollapse ? 'Double click to collapse' : undefined}\r\n onPointerDown={handlePointerDown}\r\n onDoubleClick={(e) => {\r\n e.stopPropagation()\r\n if (enableCollapse) setIsCollapsed(v => !v)\r\n else onClose?.()\r\n }}\r\n >\r\n <Card className={`${currentSize.cardWidth} ${currentSize.cardPadding} shadow-md group-hover:shadow-lg transition-shadow duration-200`}>\r\n <div className={`flex items-start ${currentSize.gap}`}>\r\n <div className={`${currentSize.iconSize} rounded-full shadow-sm flex items-center justify-center select-none flex-shrink-0 bg-primary`}>\r\n <SensorIcon className={`${currentSize.iconInnerSize} text-primary-foreground`} />\r\n </div>\r\n\r\n <div className=\"flex-1 min-w-0 flex flex-col gap-1\">\r\n <span className={`font-semibold ${currentSize.titleSize} text-foreground leading-none truncate`}>\r\n {sensorName}\r\n </span>\r\n <span className={`${currentSize.subtitleSize} text-muted-foreground leading-none`}>\r\n {sensorTypeNameFormatted}\r\n </span>\r\n </div>\r\n\r\n {showActions && (\r\n <Button\r\n type=\"button\"\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n title={actionLabels?.close ?? 'Close'}\r\n aria-label={actionLabels?.close ?? 'Close'}\r\n className={`z-50 ${currentSize.buttonSize} shrink-0 -mr-1 -mt-1`}\r\n // In the CSS2D 3D card (enableCollapse) activate on pointerdown for reliability;\r\n // the map popup is normal DOM, so keep click for keyboard accessibility.\r\n {...(enableCollapse\r\n ? { onPointerDown: (e: React.PointerEvent) => { e.stopPropagation(); handleClose() } }\r\n : { onClick: (e: React.MouseEvent) => { e.stopPropagation(); handleClose() } })}\r\n >\r\n <LR.X className={currentSize.buttonIconSize} />\r\n </Button>\r\n )}\r\n\r\n {/* Map popup keeps the inline delete; the BIM card uses the action row below instead. */}\r\n {!showActions && onRemove && (\r\n <Button\r\n type=\"button\"\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n title=\"Remove sensor\"\r\n className={`z-50 ${currentSize.buttonSize} shrink-0`}\r\n aria-label=\"Remove sensor\"\r\n onClick={(e) => {\r\n e.stopPropagation()\r\n onRemove()\r\n }}\r\n >\r\n <LR.Trash2 className={currentSize.buttonIconSize} />\r\n </Button>\r\n )}\r\n </div>\r\n\r\n <div className=\"mb-2\">\r\n <span className={`${currentSize.metaSize} text-muted-foreground`}>\r\n Created {formatTimestamp(createdAt, timeZone)}\r\n </span>\r\n </div>\r\n\r\n <SensorChart\r\n sensorData={sensorData}\r\n isLoadingData={isLoadingData}\r\n sensorName={sensorName}\r\n sensorType={sensorType}\r\n updateFrequency={updateFrequency}\r\n chartConfig={chartConfig}\r\n size={size}\r\n unit={unit}\r\n valueLabels={valueLabels}\r\n timeZone={timeZone}\r\n />\r\n <SensorTagsSection\r\n tags={tags}\r\n variant={tagsVariant}\r\n onAdd={onAddTag}\r\n onDelete={onDeleteTag}\r\n translations={tagsTranslations}\r\n />\r\n\r\n {showActions && (onEdit || onRemove || onExpand) && (\r\n <div className=\"mt-2 flex justify-end border-t pt-1\">\r\n <CommentActionButtons\r\n onExpand={onExpand}\r\n onEdit={canEdit ? onEdit : undefined}\r\n onDelete={canDelete ? onRemove : undefined}\r\n canEdit={canEdit}\r\n canDelete={canDelete}\r\n activateOnPointerDown={enableCollapse}\r\n buttonClassName=\"h-7 w-7\"\r\n labels={{ edit: actionLabels?.edit, delete: actionLabels?.delete, close: actionLabels?.close, expand: actionLabels?.expand }}\r\n />\r\n </div>\r\n )}\r\n </Card>\r\n </div>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
@@ -3,7 +3,7 @@ import { type ChartConfig } from '../chart';
3
3
  import type { SensorType } from '../../../types/dbTypes';
4
4
  interface SensorChartProps {
5
5
  sensorData: {
6
- time: string;
6
+ t: number;
7
7
  value: number;
8
8
  }[];
9
9
  isLoadingData: boolean;
@@ -20,7 +20,17 @@ interface SensorChartProps {
20
20
  minColor?: string;
21
21
  midColor?: string;
22
22
  maxColor?: string;
23
+ unit?: string;
24
+ valueLabels?: Record<number, string>;
25
+ timeZone?: string;
26
+ showBrush?: boolean;
27
+ brushStartIndex?: number;
28
+ brushEndIndex?: number;
29
+ onBrushChange?: (range: {
30
+ startIndex?: number;
31
+ endIndex?: number;
32
+ }) => void;
23
33
  }
24
- export declare function SensorChart({ sensorData, isLoadingData, sensorName, sensorType, updateFrequency, chartConfig, size, sensorTitle, loadingText, noDataText, updateFrequencyText, dataPointsText, minColor, midColor, maxColor, }: SensorChartProps): React.JSX.Element;
34
+ export declare function SensorChart({ sensorData, isLoadingData, sensorName, sensorType, updateFrequency, chartConfig, size, sensorTitle, loadingText, noDataText, updateFrequencyText, dataPointsText, minColor, midColor, maxColor, unit, valueLabels, timeZone, showBrush, brushStartIndex, brushEndIndex, onBrushChange, }: SensorChartProps): React.JSX.Element;
25
35
  export {};
26
36
  //# sourceMappingURL=SensorChart.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SensorChart.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/Sensors/SensorChart.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,UAAU,CAAA;AAIjB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAExD,UAAU,gBAAgB;IACxB,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC7C,aAAa,EAAE,OAAO,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,UAAU,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,WAAW,EAAE,WAAW,CAAA;IACxB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,WAAW,CAAC,EAC1B,UAAU,EACV,aAAa,EACb,UAAU,EACV,UAAU,EACV,eAAe,EACf,WAAW,EACX,IAAW,EACX,WAA2B,EAC3B,WAA+B,EAC/B,UAAgC,EAChC,mBAAwC,EACxC,cAA8B,EAC9B,QAAQ,EACR,QAAQ,EACR,QAAQ,GACT,EAAE,gBAAgB,qBAiGlB"}
1
+ {"version":3,"file":"SensorChart.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/Sensors/SensorChart.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,UAAU,CAAA;AAIjB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAExD,UAAU,gBAAgB;IACxB,UAAU,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC1C,aAAa,EAAE,OAAO,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,UAAU,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,WAAW,EAAE,WAAW,CAAA;IACxB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CAC5E;AAED,wBAAgB,WAAW,CAAC,EAC1B,UAAU,EACV,aAAa,EACb,UAAU,EACV,UAAU,EACV,eAAe,EACf,WAAW,EACX,IAAW,EACX,WAA2B,EAC3B,WAA+B,EAC/B,UAAgC,EAChC,mBAAwC,EACxC,cAA8B,EAC9B,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,WAAW,EACX,QAA2B,EAC3B,SAAiB,EACjB,eAAe,EACf,aAAa,EACb,aAAa,GACd,EAAE,gBAAgB,qBAkIlB"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as LR from "lucide-react";
4
- import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
5
- import { formatDuration } from "../../../utils/timeUtils";
4
+ import { Area, AreaChart, Brush, CartesianGrid, XAxis } from "recharts";
5
+ import { detectTimeZone, formatInZone, formatDuration } from "../../../utils/timeUtils";
6
6
  import {
7
7
  Card,
8
8
  CardContent,
@@ -31,7 +31,14 @@ function SensorChart({
31
31
  dataPointsText = "Data Points",
32
32
  minColor,
33
33
  midColor,
34
- maxColor
34
+ maxColor,
35
+ unit,
36
+ valueLabels,
37
+ timeZone = detectTimeZone(),
38
+ showBrush = false,
39
+ brushStartIndex,
40
+ brushEndIndex,
41
+ onBrushChange
35
42
  }) {
36
43
  const { maxColour, midColour, minColour } = sensorType || { name: "Unknown", icon: "Radio" };
37
44
  const finalMin = minColor || minColour || defaultPalette.min;
@@ -44,6 +51,10 @@ function SensorChart({
44
51
  };
45
52
  const currentSize = sizeClasses[size];
46
53
  const gradientId = `gradient-${sensorName.replace(/\s+/g, "-").toLowerCase()}`;
54
+ const axisOpts = { hour: "2-digit", minute: "2-digit", hourCycle: "h23" };
55
+ const fullOpts = { month: "short", day: "2-digit", hour: "2-digit", minute: "2-digit", hourCycle: "h23" };
56
+ const formatAxis = (value) => formatInZone(Number(value), timeZone, axisOpts);
57
+ const formatFull = (value) => formatInZone(Number(value), timeZone, fullOpts);
47
58
  return /* @__PURE__ */ jsxs(Card, { children: [
48
59
  /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { className: currentSize.titleSize, children: sensorTitle }) }),
49
60
  /* @__PURE__ */ jsx(CardContent, { children: isLoadingData ? /* @__PURE__ */ jsxs("div", { className: `flex items-center justify-center ${currentSize.height} text-muted-foreground`, children: [
@@ -65,24 +76,37 @@ function SensorChart({
65
76
  /* @__PURE__ */ jsx(
66
77
  XAxis,
67
78
  {
68
- dataKey: "time",
79
+ dataKey: "t",
80
+ type: "number",
81
+ scale: "time",
82
+ domain: ["dataMin", "dataMax"],
69
83
  tickLine: false,
70
84
  axisLine: false,
71
85
  tickMargin: 8,
72
86
  interval: "preserveStartEnd",
73
- tickFormatter: (value) => {
74
- if (value.endsWith(":00:00") || value.endsWith(":00")) {
75
- return value.substring(0, value.lastIndexOf(":"));
76
- }
77
- return "";
78
- }
87
+ tickFormatter: formatAxis
79
88
  }
80
89
  ),
81
90
  /* @__PURE__ */ jsx(
82
91
  ChartTooltip,
83
92
  {
84
93
  cursor: false,
85
- content: /* @__PURE__ */ jsx(ChartTooltipContent, { indicator: "line" })
94
+ content: /* @__PURE__ */ jsx(
95
+ ChartTooltipContent,
96
+ {
97
+ indicator: "dot",
98
+ labelFormatter: (_label, payload) => {
99
+ var _a, _b;
100
+ const t = (_b = (_a = payload == null ? void 0 : payload[0]) == null ? void 0 : _a.payload) == null ? void 0 : _b.t;
101
+ return t != null ? formatFull(t) : "";
102
+ },
103
+ formatter: unit || valueLabels ? (value) => {
104
+ const label = valueLabels == null ? void 0 : valueLabels[value];
105
+ if (label) return label;
106
+ return unit ? `${value} ${unit}` : String(value);
107
+ } : void 0
108
+ }
109
+ )
86
110
  }
87
111
  ),
88
112
  /* @__PURE__ */ jsx(
@@ -95,6 +119,18 @@ function SensorChart({
95
119
  strokeWidth: 1,
96
120
  fillOpacity: 1
97
121
  }
122
+ ),
123
+ showBrush && sensorData.length > 1 && /* @__PURE__ */ jsx(
124
+ Brush,
125
+ {
126
+ dataKey: "t",
127
+ height: 26,
128
+ travellerWidth: 8,
129
+ startIndex: brushStartIndex,
130
+ endIndex: brushEndIndex,
131
+ tickFormatter: (value) => formatAxis(Number(value)),
132
+ onChange: (range) => onBrushChange == null ? void 0 : onBrushChange(range)
133
+ }
98
134
  )
99
135
  ]
100
136
  }
@@ -110,7 +146,8 @@ function SensorChart({
110
146
  /* @__PURE__ */ jsx(LR.Database, { className: "h-4 w-4" }),
111
147
  sensorData.length,
112
148
  " ",
113
- dataPointsText
149
+ dataPointsText,
150
+ unit ? ` \xB7 ${unit}` : ""
114
151
  ] })
115
152
  ] }) }) })
116
153
  ] });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/core/components/ui/Sensors/SensorChart.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 LR from 'lucide-react'\r\nimport * as React from 'react'\r\nimport { Area, AreaChart, CartesianGrid, XAxis } from 'recharts'\r\n\r\nimport { formatDuration } from '../../../utils/timeUtils'\r\nimport {\r\n Card,\r\n CardContent,\r\n CardFooter,\r\n CardHeader,\r\n CardTitle,\r\n} from '../Card'\r\nimport {\r\n ChartContainer,\r\n ChartTooltip,\r\n ChartTooltipContent,\r\n type ChartConfig,\r\n} from '../chart'\r\n\r\nimport { defaultPalette } from './sensorUtils'\r\n\r\nimport type { SensorType } from '../../../types/dbTypes'\r\n\r\ninterface SensorChartProps {\r\n sensorData: { time: string; value: number }[]\r\n isLoadingData: boolean\r\n sensorName: string\r\n sensorType: SensorType\r\n updateFrequency: number\r\n chartConfig: ChartConfig\r\n size?: 'sm' | 'md' | 'lg'\r\n sensorTitle?: string\r\n loadingText?: string\r\n noDataText?: string\r\n updateFrequencyText?: string\r\n dataPointsText?: string\r\n minColor?: string\r\n midColor?: string\r\n maxColor?: string\r\n}\r\n\r\nexport function SensorChart({\r\n sensorData,\r\n isLoadingData,\r\n sensorName,\r\n sensorType,\r\n updateFrequency,\r\n chartConfig,\r\n size = 'md',\r\n sensorTitle = 'Sensor Data',\r\n loadingText = 'Loading data...',\r\n noDataText = 'No data available',\r\n updateFrequencyText = 'Update Frequency',\r\n dataPointsText = 'Data Points',\r\n minColor,\r\n midColor,\r\n maxColor,\r\n}: SensorChartProps) {\r\n\r\n const { maxColour, midColour, minColour } = sensorType || { name: 'Unknown', icon: 'Radio' }\r\n\r\n const finalMin = minColor || minColour || defaultPalette.min\r\n const finalMid = midColor || midColour || defaultPalette.mid\r\n const finalMax = maxColor || maxColour || defaultPalette.max\r\n\r\n\r\n\r\n const sizeClasses = {\r\n sm: { height: 'h-[150px]', titleSize: 'text-sm', descSize: 'text-xs', footerSize: 'text-xs' },\r\n md: { height: 'h-[200px]', titleSize: 'text-base', descSize: 'text-sm', footerSize: 'text-sm' },\r\n lg: { height: 'h-[300px]', titleSize: 'text-lg', descSize: 'text-base', footerSize: 'text-sm' }\r\n }\r\n\r\n const currentSize = sizeClasses[size]\r\n const gradientId = `gradient-${sensorName.replace(/\\s+/g, '-').toLowerCase()}`\r\n\r\n return (\r\n <Card>\r\n <CardHeader>\r\n <CardTitle className={currentSize.titleSize}>{sensorTitle}</CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n {isLoadingData ? (\r\n <div className={`flex items-center justify-center ${currentSize.height} text-muted-foreground`}>\r\n <LR.Loader2 className=\"h-6 w-6 animate-spin mr-2\" />\r\n {loadingText}\r\n </div>\r\n ) : sensorData.length === 0 ? (\r\n <div className={`flex items-center justify-center ${currentSize.height} text-muted-foreground`}>\r\n {noDataText}\r\n </div>\r\n ) : (\r\n <ChartContainer config={chartConfig}>\r\n <AreaChart\r\n accessibilityLayer\r\n data={sensorData}\r\n margin={{ left: 12, right: 12, top: 10 }}\r\n >\r\n <defs>\r\n <linearGradient id={gradientId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\r\n <stop offset=\"5%\" stopColor={finalMax} stopOpacity={0.8} />\r\n <stop offset=\"50%\" stopColor={finalMid} stopOpacity={0.5} />\r\n <stop offset=\"95%\" stopColor={finalMin} stopOpacity={0.2} />\r\n </linearGradient>\r\n </defs>\r\n\r\n <CartesianGrid vertical={false} strokeDasharray=\"3 3\" opacity={0.4} />\r\n <XAxis\r\n dataKey=\"time\"\r\n tickLine={false}\r\n axisLine={false}\r\n tickMargin={8}\r\n interval=\"preserveStartEnd\"\r\n tickFormatter={(value) => {\r\n if (value.endsWith(':00:00') || value.endsWith(':00')) {\r\n return value.substring(0, value.lastIndexOf(':'))\r\n }\r\n return ''\r\n }}\r\n />\r\n <ChartTooltip\r\n cursor={false}\r\n content={<ChartTooltipContent indicator=\"line\" />}\r\n />\r\n <Area\r\n dataKey=\"value\"\r\n type=\"natural\"\r\n fill={`url(#${gradientId})`}\r\n stroke={finalMax}\r\n strokeWidth={1}\r\n fillOpacity={1}\r\n />\r\n </AreaChart>\r\n </ChartContainer>\r\n )}\r\n </CardContent>\r\n <CardFooter>\r\n <div className={`flex w-full items-start gap-2 ${currentSize.footerSize}`}>\r\n <div className=\"grid gap-2\">\r\n <div className=\"flex items-center gap-2 leading-none text-muted-foreground\">\r\n <LR.Timer className=\"h-4 w-4\" />\r\n {updateFrequencyText} {formatDuration(updateFrequency)}\r\n </div>\r\n {sensorData.length > 0 && (\r\n <div className=\"flex items-center gap-2 leading-none text-muted-foreground\">\r\n <LR.Database className=\"h-4 w-4\" />\r\n {sensorData.length} {dataPointsText}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n </CardFooter>\r\n </Card>\r\n )\r\n}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../src/core/components/ui/Sensors/SensorChart.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 LR from 'lucide-react'\r\nimport * as React from 'react'\r\nimport { Area, AreaChart, Brush, CartesianGrid, XAxis } from 'recharts'\r\n\r\nimport { detectTimeZone, formatInZone, formatDuration } from '../../../utils/timeUtils'\r\nimport {\r\n Card,\r\n CardContent,\r\n CardFooter,\r\n CardHeader,\r\n CardTitle,\r\n} from '../Card'\r\nimport {\r\n ChartContainer,\r\n ChartTooltip,\r\n ChartTooltipContent,\r\n type ChartConfig,\r\n} from '../chart'\r\n\r\nimport { defaultPalette } from './sensorUtils'\r\n\r\nimport type { SensorType } from '../../../types/dbTypes'\r\n\r\ninterface SensorChartProps {\r\n sensorData: { t: number; value: number }[]\r\n isLoadingData: boolean\r\n sensorName: string\r\n sensorType: SensorType\r\n updateFrequency: number\r\n chartConfig: ChartConfig\r\n size?: 'sm' | 'md' | 'lg'\r\n sensorTitle?: string\r\n loadingText?: string\r\n noDataText?: string\r\n updateFrequencyText?: string\r\n dataPointsText?: string\r\n minColor?: string\r\n midColor?: string\r\n maxColor?: string\r\n unit?: string\r\n valueLabels?: Record<number, string>\r\n timeZone?: string\r\n showBrush?: boolean\r\n brushStartIndex?: number\r\n brushEndIndex?: number\r\n onBrushChange?: (range: { startIndex?: number; endIndex?: number }) => void\r\n}\r\n\r\nexport function SensorChart({\r\n sensorData,\r\n isLoadingData,\r\n sensorName,\r\n sensorType,\r\n updateFrequency,\r\n chartConfig,\r\n size = 'md',\r\n sensorTitle = 'Sensor Data',\r\n loadingText = 'Loading data...',\r\n noDataText = 'No data available',\r\n updateFrequencyText = 'Update Frequency',\r\n dataPointsText = 'Data Points',\r\n minColor,\r\n midColor,\r\n maxColor,\r\n unit,\r\n valueLabels,\r\n timeZone = detectTimeZone(),\r\n showBrush = false,\r\n brushStartIndex,\r\n brushEndIndex,\r\n onBrushChange,\r\n}: SensorChartProps) {\r\n\r\n const { maxColour, midColour, minColour } = sensorType || { name: 'Unknown', icon: 'Radio' }\r\n\r\n const finalMin = minColor || minColour || defaultPalette.min\r\n const finalMid = midColor || midColour || defaultPalette.mid\r\n const finalMax = maxColor || maxColour || defaultPalette.max\r\n\r\n const sizeClasses = {\r\n sm: { height: 'h-[150px]', titleSize: 'text-sm', descSize: 'text-xs', footerSize: 'text-xs' },\r\n md: { height: 'h-[200px]', titleSize: 'text-base', descSize: 'text-sm', footerSize: 'text-sm' },\r\n lg: { height: 'h-[300px]', titleSize: 'text-lg', descSize: 'text-base', footerSize: 'text-sm' }\r\n }\r\n\r\n const currentSize = sizeClasses[size]\r\n const gradientId = `gradient-${sensorName.replace(/\\s+/g, '-').toLowerCase()}`\r\n\r\n // All axis/tooltip time strings are formatted in the caller's chosen zone.\r\n const axisOpts: Intl.DateTimeFormatOptions = { hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }\r\n const fullOpts: Intl.DateTimeFormatOptions = { month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }\r\n const formatAxis = (value: number) => formatInZone(Number(value), timeZone, axisOpts)\r\n const formatFull = (value: number) => formatInZone(Number(value), timeZone, fullOpts)\r\n\r\n return (\r\n <Card>\r\n <CardHeader>\r\n <CardTitle className={currentSize.titleSize}>{sensorTitle}</CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n {isLoadingData ? (\r\n <div className={`flex items-center justify-center ${currentSize.height} text-muted-foreground`}>\r\n <LR.Loader2 className=\"h-6 w-6 animate-spin mr-2\" />\r\n {loadingText}\r\n </div>\r\n ) : sensorData.length === 0 ? (\r\n <div className={`flex items-center justify-center ${currentSize.height} text-muted-foreground`}>\r\n {noDataText}\r\n </div>\r\n ) : (\r\n <ChartContainer config={chartConfig}>\r\n <AreaChart\r\n accessibilityLayer\r\n data={sensorData}\r\n margin={{ left: 12, right: 12, top: 10 }}\r\n >\r\n <defs>\r\n <linearGradient id={gradientId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\r\n <stop offset=\"5%\" stopColor={finalMax} stopOpacity={0.8} />\r\n <stop offset=\"50%\" stopColor={finalMid} stopOpacity={0.5} />\r\n <stop offset=\"95%\" stopColor={finalMin} stopOpacity={0.2} />\r\n </linearGradient>\r\n </defs>\r\n\r\n <CartesianGrid vertical={false} strokeDasharray=\"3 3\" opacity={0.4} />\r\n <XAxis\r\n dataKey=\"t\"\r\n type=\"number\"\r\n scale=\"time\"\r\n domain={['dataMin', 'dataMax']}\r\n tickLine={false}\r\n axisLine={false}\r\n tickMargin={8}\r\n interval=\"preserveStartEnd\"\r\n tickFormatter={formatAxis}\r\n />\r\n <ChartTooltip\r\n cursor={false}\r\n content={\r\n <ChartTooltipContent\r\n // \"dot\" (not \"line\") so the time label renders at the top of the tooltip:\r\n // with a single series + \"line\", ChartTooltipContent nests the label inside the\r\n // per-item row, which the unit/category formatter branch then skips.\r\n indicator=\"dot\"\r\n labelFormatter={(_label, payload) => {\r\n const t = (payload?.[0]?.payload as { t?: number } | undefined)?.t\r\n return t != null ? formatFull(t) : ''\r\n }}\r\n formatter={\r\n unit || valueLabels\r\n ? (value: unknown) => {\r\n const label = valueLabels?.[value as number]\r\n if (label) return label\r\n return unit ? `${value} ${unit}` : String(value)\r\n }\r\n : undefined\r\n }\r\n />\r\n }\r\n />\r\n <Area\r\n dataKey=\"value\"\r\n type=\"natural\"\r\n fill={`url(#${gradientId})`}\r\n stroke={finalMax}\r\n strokeWidth={1}\r\n fillOpacity={1}\r\n />\r\n {showBrush && sensorData.length > 1 && (\r\n <Brush\r\n dataKey=\"t\"\r\n height={26}\r\n travellerWidth={8}\r\n startIndex={brushStartIndex}\r\n endIndex={brushEndIndex}\r\n tickFormatter={(value) => formatAxis(Number(value))}\r\n onChange={(range) => onBrushChange?.(range as { startIndex?: number; endIndex?: number })}\r\n />\r\n )}\r\n </AreaChart>\r\n </ChartContainer>\r\n )}\r\n </CardContent>\r\n <CardFooter>\r\n <div className={`flex w-full items-start gap-2 ${currentSize.footerSize}`}>\r\n <div className=\"grid gap-2\">\r\n <div className=\"flex items-center gap-2 leading-none text-muted-foreground\">\r\n <LR.Timer className=\"h-4 w-4\" />\r\n {updateFrequencyText} {formatDuration(updateFrequency)}\r\n </div>\r\n {sensorData.length > 0 && (\r\n <div className=\"flex items-center gap-2 leading-none text-muted-foreground\">\r\n <LR.Database className=\"h-4 w-4\" />\r\n {sensorData.length} {dataPointsText}{unit ? ` · ${unit}` : ''}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n </CardFooter>\r\n </Card>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
@@ -0,0 +1,9 @@
1
+ import * as React from 'react';
2
+ import type { Sensor, SensorType } from '../../../types/dbTypes';
3
+ export declare function SensorDetailDialog({ open, onOpenChange, sensor, sensorType, }: {
4
+ open: boolean;
5
+ onOpenChange: (open: boolean) => void;
6
+ sensor: Sensor;
7
+ sensorType?: SensorType;
8
+ }): React.ReactElement;
9
+ //# sourceMappingURL=SensorDetailDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SensorDetailDialog.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/ui/Sensors/SensorDetailDialog.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAa9B,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAyDhE,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,UAAU,GACX,EAAE;IACD,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,UAAU,CAAA;CACxB,GAAG,KAAK,CAAC,YAAY,CAsErB"}