From df8bcd376e25eacde5db643f6d7643c127a9ec19 Mon Sep 17 00:00:00 2001 From: Christopher Hight Date: Fri, 10 Jul 2026 14:56:11 -0700 Subject: [PATCH] Update widgets and added settings --- .../components/Widgets/AlertLevelWidget.jsx | 1 - .../components/Widgets/VaaActivity7Day.jsx | 12 +++-- .../Widgets/settings/VaaActivitySettings.jsx | 37 ++++++++++++++ client/src/constants/widgetRegistry.jsx | 9 ++++ client/src/pages/home/tabs/MyDashboard.jsx | 48 +++++++++++++++---- client/src/store/useDashboardStore.jsx | 11 +++++ 6 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 client/src/components/Widgets/settings/VaaActivitySettings.jsx diff --git a/client/src/components/Widgets/AlertLevelWidget.jsx b/client/src/components/Widgets/AlertLevelWidget.jsx index ca546d6..b3b3afb 100644 --- a/client/src/components/Widgets/AlertLevelWidget.jsx +++ b/client/src/components/Widgets/AlertLevelWidget.jsx @@ -9,7 +9,6 @@ export default function AlertLevel({id, isEditable, isDelete, DeleteWidget}) { const [isSubmit, setIsSubmit] = useState(false); const onSubmit = (data) => { - console.log("data: ", data); setIsSubmit(true); reset(); } diff --git a/client/src/components/Widgets/VaaActivity7Day.jsx b/client/src/components/Widgets/VaaActivity7Day.jsx index 62e151a..85d35c1 100644 --- a/client/src/components/Widgets/VaaActivity7Day.jsx +++ b/client/src/components/Widgets/VaaActivity7Day.jsx @@ -3,7 +3,11 @@ import { Box, Flex, Spinner, Text } from '@chakra-ui/react'; import Plot from 'react-plotly.js'; import VdapApi from '../../hooks/useVdapApi.js'; // Adjust path as needed -export default function VaaActivity7Day() { +export default function VaaActivity7Day({ settings }) { + console.log("3. Chart received settings:", settings); + // Plotly expects 'linear' for normal axes + const plotYAxisType = settings?.yAxisScale === 'symlog' ? 'symlog' : 'linear'; + // Clean, single-line data fetching const { data: apiData, isLoading, error } = VdapApi('/api/v2/comm_coor_vaa'); @@ -59,7 +63,7 @@ export default function VaaActivity7Day() { paper_bgcolor: 'transparent', // Let Chakra UI background show through plot_bgcolor: 'transparent', font: { color: '#d1d5db' }, // gray.300 - margin: { t: 20, r: 20, l: 20, b: 20 }, + margin: { t: 40, r: 20, l: 20, b: 20 }, // title: { // text: '7 Day Consolidated Flight Levels', // font: { size: 16, color: 'white' }, @@ -81,6 +85,7 @@ export default function VaaActivity7Day() { text: 'Flight Level', font: { color: 'white', size: 12 } }, + type: plotYAxisType, gridcolor: '#4b5563', zerolinecolor: '#ffffff', automargin: true, @@ -105,7 +110,7 @@ export default function VaaActivity7Day() { } ], }; - }, [apiData]); + }, [apiData, plotYAxisType]); if (isLoading) return ( @@ -128,6 +133,7 @@ export default function VaaActivity7Day() { return ( state.updateWidgetSettings); + + // 1. Convert the saved string into a boolean for the toggle UI + const isSymlog = currentSettings?.yAxisScale === 'symlog'; + + const handleToggle = (details) => { + // 2. Chakra v3 passes { checked: boolean } in the details object. + // Convert it back to the string Plotly expects. + console.log("1. Toggle Clicked! Raw details from Chakra:", details); + + const newScale = details.checked ? 'symlog' : 'normal'; + + updateSettings(widgetId, { yAxisScale: newScale }); + }; + + return ( + + Y-Axis Settings + + + + + + + Use Symlog Scale + + + ); +} \ No newline at end of file diff --git a/client/src/constants/widgetRegistry.jsx b/client/src/constants/widgetRegistry.jsx index 43d5f77..d0193b3 100644 --- a/client/src/constants/widgetRegistry.jsx +++ b/client/src/constants/widgetRegistry.jsx @@ -13,6 +13,15 @@ export const WIDGET_REGISTRY = { category: "Aviation", description: "Displays 7-day flight level timelines across multiple volcanoes.", component: lazy(() => import('../components/widgets/VaaActivity7Day')), + + // The specific settings UI for this widget + settingsComponent: lazy(() => import('../components/widgets/settings/VaaActivitySettings')), + + // The baseline data every new VAA widget starts with + defaultSettings: { + yAxisScale: 'normal' // Options: 'normal' | 'symlog' + }, + allowedSizes: { xlarge: { w: 12, h: 12 }, } diff --git a/client/src/pages/home/tabs/MyDashboard.jsx b/client/src/pages/home/tabs/MyDashboard.jsx index 9889124..b7b3398 100644 --- a/client/src/pages/home/tabs/MyDashboard.jsx +++ b/client/src/pages/home/tabs/MyDashboard.jsx @@ -1,5 +1,5 @@ -import { Box, Text, Flex, IconButton, Spinner } from "@chakra-ui/react"; -import { X } from "lucide-react"; +import { Box, Text, Flex, IconButton, Spinner, Popover } from "@chakra-ui/react"; +import { X, MoreVertical } from "lucide-react"; import { Responsive, WidthProvider } from "react-grid-layout"; import { useDashboardStore } from "@/store/useDashboardStore.jsx"; import { Suspense, useEffect, useRef } from 'react'; @@ -62,30 +62,60 @@ export default function MyDashboard() { if (registryEntry && registryEntry.component) { const Component = registryEntry.component; + // 1. Check if this widget type has a settings component registered + const SettingsUI = registryEntry.settingsComponent; + return ( // The "Frame" that holds both the Title and the Chart - {/* 1. THE TITLE BAR (Dynamically pulled from registry!) */} + {/* THE TITLE BAR */} {registryEntry.name} - {/* (Optional) This is exactly where the ellipsis/settings icon will go later! */} - + {/* THE SETTINGS ELLIPSIS */} + {SettingsUI ? ( + + + + + + + + {/* NEW: The Positioner lifts the content out of the Flexbox flow */} + + + }> + + + + + + ) : ( + + )} - {/* 2. THE CHART CANVAS */} + {/* THE CHART CANVAS */} - + {/* Pass the settings object into the actual chart component */} + diff --git a/client/src/store/useDashboardStore.jsx b/client/src/store/useDashboardStore.jsx index cfde6d7..43803cb 100644 --- a/client/src/store/useDashboardStore.jsx +++ b/client/src/store/useDashboardStore.jsx @@ -43,6 +43,17 @@ export const useDashboardStore = create((set, get) => ({ } }, + updateWidgetSettings: (id, newSettings) => set((state) => { + console.log("2. Zustand updating widget:", id, "with:", newSettings); + return { + widgets: state.widgets.map(widget => + widget.id === id + ? { ...widget, settings: { ...widget.settings, ...newSettings } } + : widget + ) + }; + }), + addWidget: (widgetId, sizeKey) => { const registryEntry = WIDGET_REGISTRY[widgetId];