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) => (