Refactor widgets to use theme better. Alert change and 7day vaa
This commit is contained in:
@@ -60,38 +60,38 @@ export default function AlertChanges({ settings, widgetID}) {
|
|||||||
return sorted_entries;
|
return sorted_entries;
|
||||||
}, [apiData, thisWidget]);
|
}, [apiData, thisWidget]);
|
||||||
|
|
||||||
// list of alert colors used for coloring text below
|
// Alert colors are now light/dark mode responsive
|
||||||
const alertColors = {
|
const alertColors = {
|
||||||
Red: "red.400",
|
Red: "alertRed",
|
||||||
Orange: "orange.400",
|
Orange: "alertOrange",
|
||||||
Yellow: "yellow.400",
|
Yellow: "alertYellow",
|
||||||
Green: "green.400",
|
Green: "alertGreen",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isLoading) return (
|
if (isLoading) return (
|
||||||
<Flex w="100%" h="100%" align="center" justify="center" borderRadius="lg" bg="gray.800">
|
<Flex w="100%" h="100%" align="center" justify="center" bg="transparent">
|
||||||
<Spinner size="xl" color="blue.400" thickness="4px"/>
|
<Spinner size="xl" color="blue.400" thickness="4px"/>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error) return (
|
if (error) return (
|
||||||
<Flex w="100%" h="100%" align="center" justify="center" borderRadius="lg" bg="gray.800">
|
<Flex w="100%" h="100%" align="center" justify="center" bg="transparent">
|
||||||
<Text color="red.400">{error}</Text>
|
<Text color="red.400">{error}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (data.length === 0) return (
|
if (data.length === 0) return (
|
||||||
<Flex w="100%" h="100%" align="center" justify="center" borderRadius="lg" bg="gray.800">
|
<Flex w="100%" h="100%" align="center" justify="center" bg="transparent">
|
||||||
<Text textAlign="center" color="gray.400">No flight level data available for the selected dates.</Text>
|
<Text textAlign="center" color="widgetTextMuted">No flight level data available for the selected dates.</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box display="flex" flexDirection="column" gap={2} p={2} w="100%" h="100%" borderRadius="lg" bg="gray.800" overflowY="auto">
|
// bg="transparent" to let the glass panel show through
|
||||||
|
<Box display="flex" flexDirection="column" gap={2} p={2} w="100%" h="100%" bg="transparent" overflowY="auto">
|
||||||
{data.map((volcano, index) => {
|
{data.map((volcano, index) => {
|
||||||
if (thisWidget?.size === "large") return (
|
if (thisWidget?.size === "large") return (
|
||||||
<Collapsible.Root key={index} w="100%">
|
<Collapsible.Root key={index} w="100%">
|
||||||
{/* Using context to dynamically change the border radii of the triggers */}
|
|
||||||
<Collapsible.Context>
|
<Collapsible.Context>
|
||||||
{(collapsible) => (
|
{(collapsible) => (
|
||||||
<>
|
<>
|
||||||
@@ -102,57 +102,58 @@ export default function AlertChanges({ settings, widgetID}) {
|
|||||||
p={1} pl={3} pr={3}
|
p={1} pl={3} pr={3}
|
||||||
borderTopRadius="lg"
|
borderTopRadius="lg"
|
||||||
borderBottomRadius={collapsible.open ? "0" : "lg"}
|
borderBottomRadius={collapsible.open ? "0" : "lg"}
|
||||||
bg="gray.600"
|
bg="widgetItemBg"
|
||||||
>
|
>
|
||||||
<Flex direction="column" w="100%" minW={0} textAlign="left" color="gray.100">
|
<Flex direction="column" w="100%" minW={0} textAlign="left">
|
||||||
<Text truncate >{volcano[0]}</Text> {/* volcano name */}
|
<Text truncate color="text" fontWeight="medium">{volcano[0]}</Text>
|
||||||
<Text truncate fontSize="xs" color="gray.300/90">{volcano[1][0]?.alert_start_date_local}</Text>
|
<Text truncate fontSize="xs" color="widgetTextMuted">{volcano[1][0]?.alert_start_date_local}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex w="100%" minW={0} justify="right">
|
<Flex w="100%" minW={0} justify="right">
|
||||||
<Text truncate pl={2} pr={2} borderRadius="lg" bg="gray.800/50" color="gray.100" fontWeight="semibold">{volcano[1][0]?.alert_level_name}</Text>
|
<Text truncate pl={2} pr={2} borderRadius="lg" bg="widgetBadgeBg" color="text" fontWeight="semibold">
|
||||||
|
{volcano[1][0]?.alert_level_name}
|
||||||
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Collapsible.Trigger>
|
</Collapsible.Trigger>
|
||||||
<Collapsible.Content w="100%" p={3} borderBottomRadius="lg" bg="gray.700">
|
|
||||||
|
<Collapsible.Content w="100%" p={3} borderBottomRadius="lg" bg="widgetItemExpandedBg">
|
||||||
<Timeline.Root size="md" variant="subtle">
|
<Timeline.Root size="md" variant="subtle">
|
||||||
{volcano[1]?.map((object, index) => (
|
{volcano[1]?.map((object, index) => (
|
||||||
<Timeline.Item key={index} w="100%" h="100%">
|
<Timeline.Item key={index} w="100%" h="100%">
|
||||||
<Timeline.Connector >
|
<Timeline.Connector >
|
||||||
{/*<Timeline.Separator borderWidth="1px" borderColor="gray.600"/>*/}
|
<Timeline.Indicator outline="none" bg="widgetBadgeBg" color="iconColor" size="lg">
|
||||||
<Timeline.Indicator outline="none" bg="gray.200" color="gray.500" size="lg">
|
|
||||||
{object?.alert_scheme_domain === "terrestrial" && <Footprints size={15} />}
|
{object?.alert_scheme_domain === "terrestrial" && <Footprints size={15} />}
|
||||||
{object?.alert_scheme_domain === "aviation" && <Plane size={15} />}
|
{object?.alert_scheme_domain === "aviation" && <Plane size={15} />}
|
||||||
{object?.alert_scheme_domain === "maritime" && <Sailboat size={15} />}
|
{object?.alert_scheme_domain === "maritime" && <Sailboat size={15} />}
|
||||||
</Timeline.Indicator>
|
</Timeline.Indicator>
|
||||||
<Timeline.Indicator h="50%" outline="none" bg="gray.700" color="gray.100" size="lg">
|
<Timeline.Indicator h="50%" outline="none" bg="widgetBadgeBg" color="iconColor" size="lg">
|
||||||
{object?.change_type === "increase" ? <TrendingUp /> : <TrendingDown />}
|
{object?.change_type === "increase" ? <TrendingUp /> : <TrendingDown />}
|
||||||
</Timeline.Indicator>
|
</Timeline.Indicator>
|
||||||
</Timeline.Connector>
|
</Timeline.Connector>
|
||||||
<Timeline.Content w="100%" h="100%">
|
<Timeline.Content w="100%" h="100%">
|
||||||
{/*<Timeline.Title>{object?.volcano}</Timeline.Title>*/}
|
|
||||||
<Timeline.Description w="100%" h="100%" gap={1}>
|
<Timeline.Description w="100%" h="100%" gap={1}>
|
||||||
<Flex direction="column" gap={1}>
|
<Flex direction="column" gap={1}>
|
||||||
<Text w="100%" h="100%" color="gray.200">{object?.alert_start_date_local}</Text>
|
<Text w="100%" h="100%" color="widgetTextMuted">{object?.alert_start_date_local}</Text>
|
||||||
<Text w="100%" h="100%"
|
<Text w="100%" h="100%"
|
||||||
color={alertColors[object?.alert_level_name] || "gray.200"}
|
color={alertColors[object?.alert_level_name] || "text"}
|
||||||
fontSize="md"
|
fontSize="md"
|
||||||
fontWeight="semibold"
|
fontWeight="semibold"
|
||||||
>
|
>
|
||||||
{object?.alert_level_name}
|
{object?.alert_level_name}
|
||||||
</Text>
|
</Text>
|
||||||
<Flex w="100%" h="20px" direction="row" justify="left" align="center">
|
<Flex w="100%" h="20px" direction="row" justify="left" align="center">
|
||||||
<Text color="gray.300">Alert Scale: </Text>
|
<Text color="widgetTextMuted">Alert Scale: </Text>
|
||||||
{(Array.from({ length: Number(object?.alert_scheme_highest) - Number(object?.alert_scheme_lowest) + 1}, (v, i) => Number(object?.alert_scheme_lowest) + i))
|
{(Array.from({ length: Number(object?.alert_scheme_highest) - Number(object?.alert_scheme_lowest) + 1}, (v, i) => Number(object?.alert_scheme_lowest) + i))
|
||||||
.map((num) => (
|
.map((num) => (
|
||||||
<Center
|
<Center
|
||||||
key={num}
|
key={num}
|
||||||
w="20px" h="100%"
|
w="20px" h="100%"
|
||||||
color="gray.300"
|
color="text"
|
||||||
bg={num === Number(object?.alert_level_rank) ? "gray.300/20" : null} // alert level rank is a string for some reason
|
bg={num === Number(object?.alert_level_rank) ? "widgetBadgeBg" : "transparent"}
|
||||||
borderRadius={num === Number(object?.alert_level_rank) ? "xl" : null}
|
borderRadius={num === Number(object?.alert_level_rank) ? "xl" : "none"}
|
||||||
textAlign="center"
|
textAlign="center"
|
||||||
fontSize="sm"
|
fontSize="sm"
|
||||||
fontWeight="semibold"
|
fontWeight="bold"
|
||||||
>
|
>
|
||||||
{num}
|
{num}
|
||||||
</Center>
|
</Center>
|
||||||
@@ -181,30 +182,30 @@ export default function AlertChanges({ settings, widgetID}) {
|
|||||||
justify="space-between" align="center"
|
justify="space-between" align="center"
|
||||||
p={1} pl={3} pr={3}
|
p={1} pl={3} pr={3}
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
bg="gray.600"
|
bg="widgetItemBg"
|
||||||
>
|
>
|
||||||
<Flex direction="column" w="100%" minW={0} textAlign="left" color="gray.100">
|
<Flex direction="column" w="100%" minW={0} textAlign="left" color="text">
|
||||||
<Flex direction="row" w="100%" gap={2} align="center">
|
<Flex direction="row" w="100%" gap={2} align="center">
|
||||||
<Flex color="gray.300">
|
<Flex color="iconColor">
|
||||||
{domain[0] === "aviation" && <Plane size={15}/>}
|
{domain[0] === "aviation" && <Plane size={15}/>}
|
||||||
{domain[0] === "terrestrial" && <Footprints size={15}/>}
|
{domain[0] === "terrestrial" && <Footprints size={15}/>}
|
||||||
{domain[0] === "maritime" && <Sailboat size={15}/>}
|
{domain[0] === "maritime" && <Sailboat size={15}/>}
|
||||||
</Flex>
|
</Flex>
|
||||||
<Text truncate>{volcano[0] || ""}</Text> {/* volcano name */}
|
<Text truncate fontWeight="medium">{volcano[0] || ""}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Text truncate w="100%" fontSize="xs" color="gray.300/90">{domain[1]?.[0]?.alert_start_date_local || ""}</Text>
|
<Text truncate w="100%" fontSize="xs" color="widgetTextMuted">{domain[1]?.[0]?.alert_start_date_local || ""}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex direction="row" w="100%" minW={0} justify="right" gap={2} color="gray.200">
|
<Flex direction="row" w="100%" minW={0} justify="right" gap={2} color="text">
|
||||||
<Flex w="25px" justify="right">
|
<Flex w="25px" justify="right" color="iconColor">
|
||||||
{domain[1]?.[0]?.change_type === "increase" ? <TrendingUp/> : <TrendingDown/>}
|
{domain[1]?.[0]?.change_type === "increase" ? <TrendingUp/> : <TrendingDown/>}
|
||||||
</Flex>
|
</Flex>
|
||||||
<Text
|
<Text
|
||||||
truncate
|
truncate
|
||||||
pl={2} pr={2}
|
pl={2} pr={2}
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
bg="gray.800/50"
|
bg="widgetBadgeBg"
|
||||||
color={alertColors[domain[1]?.[0]?.alert_level_name] || "gray.100"}
|
color={alertColors[domain[1]?.[0]?.alert_level_name] || "text"}
|
||||||
fontWeight="semibold"
|
fontWeight="semibold"
|
||||||
>
|
>
|
||||||
{domain[1]?.[0]?.alert_level_name}
|
{domain[1]?.[0]?.alert_level_name}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ export default function VaaActivity7Day({ settings, widgetID }) {
|
|||||||
'hexagon', 'octagon', 'cross', 'x'
|
'hexagon', 'octagon', 'cross', 'x'
|
||||||
];
|
];
|
||||||
|
|
||||||
// Sort data descending by flight level (matching Observable behavior)
|
|
||||||
const sortedData = [...apiData].sort((a, b) => b.obs_fl - a.obs_fl);
|
const sortedData = [...apiData].sort((a, b) => b.obs_fl - a.obs_fl);
|
||||||
|
|
||||||
// Efficiently group into Plotly traces by volcano
|
// Efficiently group into Plotly traces by volcano
|
||||||
@@ -44,102 +43,90 @@ export default function VaaActivity7Day({ settings, widgetID }) {
|
|||||||
mode: 'markers',
|
mode: 'markers',
|
||||||
marker: {
|
marker: {
|
||||||
size: 7,
|
size: 7,
|
||||||
line: { width: .5, color: 'rgba(255, 255, 255, 0.7)' },
|
line: { width: 0.5, color: 'var(--chakra-colors-textInverted)' },
|
||||||
symbol: Symbolway[index % Symbolway.length],
|
symbol: Symbolway[index % Symbolway.length],
|
||||||
},
|
},
|
||||||
// Replicating the clean Observable tooltip
|
// Replicating the clean Observable tooltip
|
||||||
hovertemplate:
|
hovertemplate:
|
||||||
`<b>${volcanoName}</b><br>` +
|
`<b>${volcanoName}</b><br>` +
|
||||||
'Date: %{x|%b %d %Y %H:%M}<br>' +
|
'Date: %{x|%b %d %Y %H:%M}<br>' +
|
||||||
'Flight Level: %{y}<extra></extra>'
|
'Flight Level: %{y}<extra></extra>'
|
||||||
}));
|
}));
|
||||||
}, [apiData]);
|
}, [apiData]);
|
||||||
|
|
||||||
// 2. Build the Layout (Styling and Grid)
|
// 2. Build the Layout (Styling and Grid)
|
||||||
const layout = useMemo(() => {
|
const layout = useMemo(() => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
autosize: true,
|
autosize: true,
|
||||||
paper_bgcolor: 'transparent', // Let Chakra UI background show through
|
paper_bgcolor: 'transparent', // Let Chakra UI background show through
|
||||||
plot_bgcolor: 'transparent',
|
plot_bgcolor: 'transparent',
|
||||||
font: { color: '#d1d5db' }, // gray.300
|
font: { color: 'var(--chakra-colors-widgetTextMuted)' },
|
||||||
margin: { t: 40, 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' },
|
|
||||||
// x: 0.05
|
|
||||||
// },
|
|
||||||
xaxis: {
|
xaxis: {
|
||||||
title: {
|
title: {
|
||||||
text: 'Date (UTC)',
|
text: 'Date (UTC)',
|
||||||
font: { color: 'white', size: 12 }
|
font: { color: 'var(--chakra-colors-text)', size: 12 }
|
||||||
},
|
},
|
||||||
type: 'date',
|
type: 'date',
|
||||||
tickformat: '%b %d<br>%Y %H:%M', // Multiline ticks from Observable
|
tickformat: '%b %d<br>%Y %H:%M',
|
||||||
gridcolor: '#4b5563',
|
gridcolor: 'var(--chakra-colors-glassSeperator)',
|
||||||
zerolinecolor: '#4b5563',
|
zerolinecolor: 'var(--chakra-colors-glassSeperator)',
|
||||||
automargin: true,
|
automargin: true,
|
||||||
},
|
},
|
||||||
yaxis: {
|
yaxis: {
|
||||||
title: {
|
title: {
|
||||||
text: 'Flight Level',
|
text: 'Flight Level',
|
||||||
font: { color: 'white', size: 12 }
|
font: { color: 'var(--chakra-colors-text)', size: 12 }
|
||||||
},
|
},
|
||||||
type: plotYAxisType,
|
type: plotYAxisType,
|
||||||
gridcolor: '#4b5563',
|
gridcolor: 'var(--chakra-colors-glassSeperator)',
|
||||||
zerolinecolor: '#ffffff',
|
zerolinecolor: 'var(--chakra-colors-text)',
|
||||||
automargin: true,
|
automargin: true,
|
||||||
},
|
},
|
||||||
// legend: {
|
|
||||||
// orientation: 'h',
|
|
||||||
// y: -0.2,
|
|
||||||
// x: 0.5,
|
|
||||||
// xanchor: 'center'
|
|
||||||
// },
|
|
||||||
hovermode: 'closest',
|
hovermode: 'closest',
|
||||||
shapes: [
|
shapes: [
|
||||||
{
|
{
|
||||||
type: 'line',
|
type: 'line',
|
||||||
x0: 0,
|
x0: 0,
|
||||||
x1: 1,
|
x1: 1,
|
||||||
xref: 'paper', // Spans entire chart width
|
xref: 'paper',
|
||||||
y0: 0,
|
y0: 0,
|
||||||
y1: 0,
|
y1: 0,
|
||||||
yref: 'y',
|
yref: 'y',
|
||||||
line: { color: 'white', width: 1 }
|
line: { color: 'var(--chakra-colors-text)', width: 1 }
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}, [apiData, plotYAxisType]);
|
}, [apiData, plotYAxisType]);
|
||||||
|
|
||||||
if (isLoading) return (
|
if (isLoading) return (
|
||||||
<Flex w="100%" h="100%" bg="gray.800" borderRadius="md" align="center" justify="center">
|
<Flex w="100%" h="100%" bg="transparent" align="center" justify="center">
|
||||||
<Spinner size="xl" color="blue.400" thickness="4px" />
|
<Spinner size="xl" color="blue.400" thickness="4px" />
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error) return (
|
if (error) return (
|
||||||
<Flex w="100%" h="100%" bg="gray.800" borderRadius="md" align="center" justify="center" p={4}>
|
<Flex w="100%" h="100%" bg="transparent" align="center" justify="center" p={4}>
|
||||||
<Text color="red.400">{error}</Text>
|
<Text color="red.400">{error}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (plotTraces.length === 0) return (
|
if (plotTraces.length === 0) return (
|
||||||
<Flex height="100%" width="100%" align="center" justify="center" bg="gray.800" borderRadius="md">
|
<Flex height="100%" width="100%" align="center" justify="center" bg="transparent">
|
||||||
<Text color="gray.400">No flight level data available.</Text>
|
<Text color="widgetTextMuted">No flight level data available.</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box height="100%" width="100%" bg="gray.800" borderRadius="md" p={2} overflow="hidden">
|
<Box height="100%" width="100%" bg="transparent" overflow="hidden">
|
||||||
<Plot
|
<Plot
|
||||||
key={plotYAxisType}
|
key={plotYAxisType}
|
||||||
data={plotTraces}
|
data={plotTraces}
|
||||||
layout={layout}
|
layout={layout}
|
||||||
useResizeHandler={true} // Essential for dashboard grid resizing!
|
useResizeHandler={true}
|
||||||
style={{ width: '100%', height: '100%' }}
|
style={{ width: '100%', height: '100%' }}
|
||||||
config={{ responsive: true, displayModeBar: true, displaylogo: false }}
|
config={{ responsive: true, displayModeBar: true, displaylogo: false }}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -572,6 +572,18 @@ const config = defineConfig({
|
|||||||
iconColor: { value: { base: "gray.600", _dark: "gray.300" } }, // Fixed: Visible in dark mode
|
iconColor: { value: { base: "gray.600", _dark: "gray.300" } }, // Fixed: Visible in dark mode
|
||||||
glassSeperator: { value: { base: "#0000001A", _dark: "#FFFFFF3F" } },
|
glassSeperator: { value: { base: "#0000001A", _dark: "#FFFFFF3F" } },
|
||||||
gooeyButtonBackground: {value: {base: "rgba(255, 255, 255, 0.1)", _dark: "rgba(255, 255, 255, 0.1)"}},
|
gooeyButtonBackground: {value: {base: "rgba(255, 255, 255, 0.1)", _dark: "rgba(255, 255, 255, 0.1)"}},
|
||||||
|
|
||||||
|
// WIDGET SPECIFIC VALUES
|
||||||
|
widgetItemBg: { value: { base: "rgba(0, 0, 0, 0.04)", _dark: "rgba(255, 255, 255, 0.05)" } },
|
||||||
|
widgetItemExpandedBg: { value: { base: "rgba(0, 0, 0, 0.08)", _dark: "rgba(0, 0, 0, 0.3)" } },
|
||||||
|
widgetBadgeBg: { value: { base: "rgba(255, 255, 255, 0.6)", _dark: "rgba(0, 0, 0, 0.5)" } },
|
||||||
|
widgetTextMuted: { value: { base: "gray.600", _dark: "gray.400" } },
|
||||||
|
|
||||||
|
// ALERT COLORS
|
||||||
|
alertRed: { value: { base: "red.600", _dark: "red.400" } },
|
||||||
|
alertOrange: { value: { base: "orange.600", _dark: "orange.400" } },
|
||||||
|
alertYellow: { value: { base: "yellow.600", _dark: "yellow.400" } },
|
||||||
|
alertGreen: { value: { base: "green.600", _dark: "green.400" } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tokens: {
|
tokens: {
|
||||||
|
|||||||
Reference in New Issue
Block a user