Update widgets and added settings

This commit is contained in:
2026-07-10 14:56:11 -07:00
parent c69ac2ba42
commit df8bcd376e
6 changed files with 105 additions and 13 deletions
@@ -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();
}
@@ -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 (
<Flex w="100%" h="100%" bg="gray.800" borderRadius="md" align="center" justify="center">
@@ -128,6 +133,7 @@ export default function VaaActivity7Day() {
return (
<Box height="100%" width="100%" bg="gray.800" borderRadius="md" p={2} overflow="hidden">
<Plot
key={plotYAxisType}
data={plotTraces}
layout={layout}
useResizeHandler={true} // Essential for dashboard grid resizing!
@@ -0,0 +1,37 @@
import { Box, Text, Switch } from "@chakra-ui/react";
import { useDashboardStore } from "../../../store/useDashboardStore.jsx";
export default function VaaActivitySettings({ widgetId, currentSettings }) {
const updateSettings = useDashboardStore(state => 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 (
<Box>
<Text fontWeight="bold" mb={3} fontSize="sm">Y-Axis Settings</Text>
<Switch.Root
checked={isSymlog}
onCheckedChange={handleToggle}
colorPalette="blue"
>
<Switch.HiddenInput />
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Label>Use Symlog Scale</Switch.Label>
</Switch.Root>
</Box>
);
}
+9
View File
@@ -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 },
}
+39 -9
View File
@@ -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
<Flex direction="column" height="100%" width="100%" bg="gray.800">
{/* 1. THE TITLE BAR (Dynamically pulled from registry!) */}
{/* THE TITLE BAR */}
<Flex
justify="space-between"
align="center"
px={3}
py={2}
// borderBottom="1px solid"
// borderColor="gray.700"
>
<Text color="gray.100" fontWeight="semibold" fontSize="md" isTruncated>
{registryEntry.name}
</Text>
{/* (Optional) This is exactly where the ellipsis/settings icon will go later! */}
<Box w="20px" h="20px" />
{/* THE SETTINGS ELLIPSIS */}
{SettingsUI ? (
<Popover.Root positioning={{ placement: "bottom-end" }}>
<Popover.Trigger asChild>
<IconButton
size="xs"
variant="ghost"
color="gray.400"
_hover={{ color: "white", bg: "gray.700" }}
aria-label="Widget Settings"
>
<MoreVertical size={16} />
</IconButton>
</Popover.Trigger>
{/* NEW: The Positioner lifts the content out of the Flexbox flow */}
<Popover.Positioner zIndex={10}>
<Popover.Content bg="gray.50" p={4} boxShadow="xl" borderRadius="md">
<Suspense fallback={<Spinner size="sm" />}>
<SettingsUI
widgetId={widget.id}
currentSettings={widget.settings || {}}
/>
</Suspense>
</Popover.Content>
</Popover.Positioner>
</Popover.Root>
) : (
<Box w="20px" h="20px" />
)}
</Flex>
{/* 2. THE CHART CANVAS */}
{/* THE CHART CANVAS */}
<Box flex="1" overflow="hidden" position="relative" p={1}>
<Component />
{/* Pass the settings object into the actual chart component */}
<Component settings={widget.settings || {}} />
</Box>
</Flex>
+11
View File
@@ -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];