diff --git a/client/src/components/Widgets/VaaActivity7Day.jsx b/client/src/components/Widgets/VaaActivity7Day.jsx
index f7ff9cb..9742756 100644
--- a/client/src/components/Widgets/VaaActivity7Day.jsx
+++ b/client/src/components/Widgets/VaaActivity7Day.jsx
@@ -4,34 +4,109 @@ import Plot from 'react-plotly.js';
import VdapApi from '../../hooks/useVdapApi.js'; // Adjust path as needed
export default function VaaActivity7Day() {
- // ONE LINE to handle all fetching, loading, and error states!
+ // Clean, single-line data fetching
const { data: apiData, isLoading, error } = VdapApi('/api/v2/comm_coor_vaa');
+ // 1. Build the Traces (Data parsing)
const plotTraces = useMemo(() => {
if (!apiData || apiData.length === 0) return [];
- const groupedData = apiData.reduce((acc, curr) => {
+ // Array of distinct Plotly shapes
+ const Symbolway = [
+ 'circle', 'square', 'diamond',
+ 'triangle-up', 'triangle-down',
+ 'star', 'hexagram', 'pentagon',
+ '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);
+
+ // Efficiently group into Plotly traces by volcano
+ const groupedData = sortedData.reduce((acc, curr) => {
const name = curr.volc_name || 'Unknown';
- if (!acc[name]) acc[name] = { x: [], y: [], text: [] };
+ if (!acc[name]) acc[name] = { x: [], y: [] };
acc[name].x.push(new Date(curr.obs_va_epoch));
acc[name].y.push(curr.obs_fl);
- acc[name].text.push(`Volcano: ${name}
Flight Level: ${curr.obs_fl}`);
return acc;
}, {});
- return Object.keys(groupedData).map((volcanoName) => ({
+ return Object.keys(groupedData).map((volcanoName, index) => ({
name: volcanoName,
x: groupedData[volcanoName].x,
y: groupedData[volcanoName].y,
- text: groupedData[volcanoName].text,
type: 'scatter',
mode: 'markers',
- hoverinfo: 'text',
- marker: { size: 8, line: { width: 1, color: 'white' } }
+ marker: {
+ size: 7,
+ line: { width: .5, color: 'rgba(255, 255, 255, 0.7)' },
+ symbol: Symbolway[index % Symbolway.length],
+ },
+ // Replicating the clean Observable tooltip
+ hovertemplate:
+ `${volcanoName}
` +
+ 'Date: %{x|%b %d %Y %H:%M}
' +
+ 'Flight Level: %{y}'
}));
}, [apiData]);
+ // 2. Build the Layout (Styling and Grid)
+ const layout = useMemo(() => {
+
+ return {
+ autosize: true,
+ paper_bgcolor: 'transparent', // Let Chakra UI background show through
+ plot_bgcolor: 'transparent',
+ font: { color: '#d1d5db' }, // gray.300
+ // margin: { t: 40, r: 20, l: 30, b: 60 }, // Extra bottom margin for legend
+ title: {
+ text: '7 Day Consolidated Flight Levels',
+ font: { size: 16, color: 'white' },
+ x: 0.05
+ },
+ xaxis: {
+ title: {
+ text: 'Date (UTC)',
+ font: { color: 'white', size: 12 }
+ },
+ type: 'date',
+ tickformat: '%b %d
%Y %H:%M', // Multiline ticks from Observable
+ gridcolor: '#4b5563',
+ zerolinecolor: '#4b5563',
+ automargin: true,
+ },
+ yaxis: {
+ title: {
+ text: 'Flight Level',
+ font: { color: 'white', size: 12 }
+ },
+ gridcolor: '#4b5563',
+ zerolinecolor: '#ffffff',
+ automargin: true,
+ },
+ // legend: {
+ // orientation: 'h',
+ // y: -0.2,
+ // x: 0.5,
+ // xanchor: 'center'
+ // },
+ hovermode: 'closest',
+ shapes: [
+ {
+ type: 'line',
+ x0: 0,
+ x1: 1,
+ xref: 'paper', // Spans entire chart width
+ y0: 0,
+ y1: 0,
+ yref: 'y',
+ line: { color: 'white', width: 1 }
+ }
+ ],
+ };
+ }, [apiData]);
+
if (isLoading) return (
@@ -51,23 +126,12 @@ export default function VaaActivity7Day() {
);
return (
-
+
diff --git a/client/src/components/Widgets/VaaActivity7Day.stories.jsx b/client/src/components/Widgets/VaaActivity7Day.stories.jsx
index 9f1b698..371b82e 100644
--- a/client/src/components/Widgets/VaaActivity7Day.stories.jsx
+++ b/client/src/components/Widgets/VaaActivity7Day.stories.jsx
@@ -9,7 +9,7 @@ export default {
decorators: [
(Story) => (