@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.
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.js.map +1 -1
- package/dist/core/components/ui/Comments/Comment.js.map +1 -1
- package/dist/core/components/ui/Comments/CommentActionButtons.d.ts +5 -1
- package/dist/core/components/ui/Comments/CommentActionButtons.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CommentActionButtons.js +37 -9
- package/dist/core/components/ui/Comments/CommentActionButtons.js.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts +1 -2
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +66 -94
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
- package/dist/core/components/ui/Sensors/Sensor.d.ts +16 -3
- package/dist/core/components/ui/Sensors/Sensor.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/Sensor.js +76 -37
- package/dist/core/components/ui/Sensors/Sensor.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorChart.d.ts +12 -2
- package/dist/core/components/ui/Sensors/SensorChart.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorChart.js +49 -12
- package/dist/core/components/ui/Sensors/SensorChart.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts +9 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.js +128 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.js.map +1 -0
- package/dist/core/components/ui/Sensors/SensorInput.d.ts +6 -1
- package/dist/core/components/ui/Sensors/SensorInput.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorInput.js +94 -8
- package/dist/core/components/ui/Sensors/SensorInput.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts +3 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.js +4 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts +1 -3
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js +33 -8
- package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts +7 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.js +56 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.js.map +1 -0
- package/dist/core/components/ui/Sensors/sensorData.d.ts +42 -0
- package/dist/core/components/ui/Sensors/sensorData.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/sensorData.js +118 -0
- package/dist/core/components/ui/Sensors/sensorData.js.map +1 -0
- package/dist/core/components/ui/Sensors/sensorRange.d.ts +24 -0
- package/dist/core/components/ui/Sensors/sensorRange.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/sensorRange.js +34 -0
- package/dist/core/components/ui/Sensors/sensorRange.js.map +1 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.d.ts +15 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.js +63 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.js.map +1 -0
- package/dist/core/components/viewers/Viewer.js +1 -1
- package/dist/core/components/viewers/Viewer.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts +1 -3
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js +2 -2
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts +1 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js +25 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts +1 -22
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js +2 -105
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts +8 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js +15 -3
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts +2 -23
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js +2 -24
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts +33 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js +90 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts +7 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js +26 -230
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts +7 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js +39 -11
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.d.ts +1 -2
- package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.js +2 -2
- package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts +1 -2
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.js +2 -2
- package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts +1 -3
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js +74 -39
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.js +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts +1 -3
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js +2 -2
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js.map +1 -1
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts +19 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js +108 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts +24 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js +27 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/types.d.ts +66 -0
- package/dist/core/components/viewers/shared/markers/types.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/types.js +1 -0
- package/dist/core/components/viewers/shared/markers/types.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts +22 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js +233 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js.map +1 -0
- package/dist/core/hooks/ui/useResizableSidebarWidth.js.map +1 -1
- package/dist/core/i18n/messages/en.json +5 -1
- package/dist/core/i18n/messages/es.json +5 -1
- package/dist/core/i18n/messages/fr.json +5 -1
- package/dist/core/store/AppConfig/context.d.ts.map +1 -1
- package/dist/core/store/AppConfig/context.js +4 -1
- package/dist/core/store/AppConfig/context.js.map +1 -1
- package/dist/core/store/AppConfig/reducer.d.ts +10 -0
- package/dist/core/store/AppConfig/reducer.d.ts.map +1 -1
- package/dist/core/store/AppConfig/reducer.js +10 -0
- package/dist/core/store/AppConfig/reducer.js.map +1 -1
- package/dist/core/store/Menus/context.d.ts.map +1 -1
- package/dist/core/store/Menus/context.js +5 -2
- package/dist/core/store/Menus/context.js.map +1 -1
- package/dist/core/store/Menus/reducer.d.ts +45 -1
- package/dist/core/store/Menus/reducer.d.ts.map +1 -1
- package/dist/core/store/Menus/reducer.js +21 -3
- package/dist/core/store/Menus/reducer.js.map +1 -1
- package/dist/core/utils/timeUtils.d.ts +24 -1
- package/dist/core/utils/timeUtils.d.ts.map +1 -1
- package/dist/core/utils/timeUtils.js +52 -3
- package/dist/core/utils/timeUtils.js.map +1 -1
- 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":";AA4JY,SAiCA,UAjCA,KAgBA,YAhBA;AAvJZ,SAAS,cAAc;AACvB,YAAY,QAAQ;AACpB,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,YAAY,WAAW;AAEvB,SAAS,oBAAoB;AAC7B,SAAS,eAAe;AACxB,SAAS,UAAU;AACnB,SAAS,cAA8B;AACvC,SAAS,aAAa;AACtB,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAE3B,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,yBAAyB;AAkB3B,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AACF,GAA+B;AAhD/B;AAiDE,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,YAAW,8CAAY,KAAK,QAAQ,MAAM,SAA/B,YAAuC;AAExD,QAAM,aAAa,kBAAkB,yCAAY,IAAI;AAGrD,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,QAAM,cAAiC,sCAAQ,SAAR,YAAgB;AAEvD,QAAM,EAAE,QAAQ,YAAY,IAAI,aAAa;AAC7C,QAAM,EAAE,aAAa,IAAI,YAAY,UAAU,OAAO,EAAE;AAExD,QAAM,eAAe,OAAO,QAAgB;AA/H9C,QAAAA;AAgII,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;AApIjD,QAAAA;AAqII,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 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 {
|
|
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;
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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: "
|
|
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:
|
|
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(
|
|
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"}
|