diff --git a/client/src/components/Widgets/SeismicEvents.jsx b/client/src/components/Widgets/SeismicEvents.jsx
new file mode 100644
index 0000000..037ab48
--- /dev/null
+++ b/client/src/components/Widgets/SeismicEvents.jsx
@@ -0,0 +1,155 @@
+import VdapApi from '../../hooks/useVdapApi.js';
+import { useMemo } from 'react';
+import Plot from 'react-plotly.js';
+import {Box, Flex, Spinner, Text} from "@chakra-ui/react";
+
+export default function SeismicEvents({ settings, widgetID}) {
+ const url = useMemo(() =>{
+ if (settings?.startDate && settings?.endDate)
+ return '/api/v2/seismic_events?start_date=' + settings?.startDate + '&end_date=' + settings?.endDate;
+ else {
+ const base_url = '/api/v2/seismic_events';
+ let today = new Date();
+ let sevenDaysAgo = new Date(today);
+ sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
+
+ sevenDaysAgo = sevenDaysAgo.toLocaleDateString('en-CA');
+ today = today.toLocaleDateString('en-CA');
+
+ return base_url + '?start_date=' + sevenDaysAgo + '&end_date=' + today;
+ }
+ }, [settings?.startDate, settings?.endDate]);
+
+ console.log(url);
+ const { data: apiData, isLoading, error } = VdapApi(url);
+
+ const plotTraces = useMemo(() => {
+ if (!apiData || apiData.length === 0) return [];
+
+ // 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 seismic events count so smaller dots draw on top of larger ones
+ const sortedData = [...apiData].sort((a, b) => b.number_events - a.number_events);
+
+ // Efficiently group into Plotly traces by volcano
+ const groupedData = sortedData.reduce((acc, curr) => {
+ const name = curr.volcano_name || 'Unknown';
+ if (!acc[name]) acc[name] = { x: [], y: [] };
+
+ // Map the seismic-specific keys
+ acc[name].x.push(new Date(curr.ts_epoch));
+ acc[name].y.push(curr.number_events);
+ return acc;
+ }, {});
+
+ return Object.keys(groupedData).map((volcanoName, index) => ({
+ name: volcanoName,
+ x: groupedData[volcanoName].x,
+ y: groupedData[volcanoName].y,
+ type: 'scatter',
+ mode: 'markers',
+ marker: {
+ size: 7,
+ line: { width: .5, color: 'rgba(255, 255, 255, 0.7)' },
+ symbol: Symbolway[index % Symbolway.length],
+ },
+ // Replicating the clean D3/Observable tooltip
+ hovertemplate:
+ `${volcanoName}
` +
+ 'Date (UTC): %{x|%b %d %Y %H:%M}
' +
+ 'Number of Seismic Events: %{y}'
+ }));
+ }, [apiData]);
+
+ 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: 20, b: 20 },
+ // title: {
+ // text: '',
+ // font: { size: 16, color: 'white' },
+ // x: 0.05
+ // },
+ xaxis: {
+ title: {
+ text: 'Date (UTC)',
+ font: { color: 'white', size: 12 }
+ },
+ type: 'date',
+ tickformat: '%b %d
%Y', // D3 requested %b %d \n %Y formatting
+ gridcolor: '#4b5563',
+ zerolinecolor: '#4b5563',
+ automargin: true,
+ },
+ yaxis: {
+ title: {
+ text: 'Seismic Count',
+ font: { color: 'white', size: 12 }
+ },
+ type: 'linear',
+ 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 }
+ }
+ ],
+ };
+ }, []);
+
+ if (isLoading) return (
+
+
+
+ );
+
+ if (error) return (
+
+ {error}
+
+ );
+
+ if (plotTraces.length === 0) return (
+
+ No data available for current filter.
+
+ );
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/client/src/components/Widgets/settings/SeismicEventsSettings.jsx b/client/src/components/Widgets/settings/SeismicEventsSettings.jsx
new file mode 100644
index 0000000..03bc0ba
--- /dev/null
+++ b/client/src/components/Widgets/settings/SeismicEventsSettings.jsx
@@ -0,0 +1,64 @@
+import {Input, Field, Text, Flex} from "@chakra-ui/react";
+import {useDashboardStore} from "@/store/useDashboardStore.jsx";
+import { useState } from "react";
+
+export default function SeismicEventsSettings({ widgetId, currentSettings }) {
+ const [startError, setStartError] = useState(null);
+ const [endError, setEndError] = useState(null);
+ const updateSettings = useDashboardStore(state => state.updateWidgetSettings);
+
+ return (
+
+
+
+
+ Start Date
+
+
+ {
+ const value = e.target.value;
+ updateSettings(widgetId, {...currentSettings, startDate: value});
+
+ const dateSelected = new Date(value);
+ const today = new Date();
+ today.setHours(0, 0, 0, 0);
+
+ if (value && (dateSelected > today)) setStartError("Can't Choose a Future Date");
+ else setStartError("");
+ }}
+ />
+ {startError && {startError}}
+
+
+
+
+ End Date
+
+
+ {
+ const value = e.target.value;
+ updateSettings(widgetId, {...currentSettings, endDate: value});
+
+ const dateSelected = new Date(value);
+ const today = new Date();
+ today.setHours(0, 0, 0, 0);
+
+ if (value && (dateSelected > today)) setEndError("Can't Choose a Future Date");
+ else setEndError("");
+ }}
+ />
+ {endError && {endError}}
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/client/src/constants/widgetRegistry.jsx b/client/src/constants/widgetRegistry.jsx
index 39b63f1..912c5f8 100644
--- a/client/src/constants/widgetRegistry.jsx
+++ b/client/src/constants/widgetRegistry.jsx
@@ -53,7 +53,7 @@ export const WIDGET_REGISTRY = {
allowedSizes: {
small: { w: 3, h: 6 },
medium: { w: 3, h: 9 },
- large: { w: 6, h: 9 },
+ large: { w: 3, h: 12 },
}
},
@@ -106,6 +106,18 @@ export const WIDGET_REGISTRY = {
}
},
+ "seismic_events": {
+ name: "Seismic Events",
+ category: "Daily Volcanic Activity",
+ description: "Plot of number of seismic events over a weeks time.",
+ component: lazy(() => import('../components/Widgets/SeismicEvents.jsx')),
+ settingsComponent: lazy(() => import('../components/Widgets/settings/SeismicEventsSettings.jsx')),
+ defaultSettings: {},
+ allowedSizes: {
+ medium: { w: 6, h: 12 }
+ }
+ },
+
"llm_al_changes_quad_plot": {
name: "Alert Level Changes Quad Plot",
category: "Computer Science",