import { useDashboardStore } from "@/store/useDashboardStore.jsx";
import VdapApi from "../../hooks/useVdapApi.js";
import useBuildUrl from "../../hooks/useBuildUrl.js";
import {
Footprints,
Plane,
Sailboat,
TrendingUp,
TrendingDown,
} from "lucide-react";
import {
Box,
Collapsible,
Flex,
Spinner,
Text,
Timeline,
Center,
} from "@chakra-ui/react";
import { useMemo, useCallback } from "react";
export default function AlertChanges({ settings, widgetID }) {
// 0) CREATE SELF AWARENESS, by finding this specific widgets object in the state-list of active/displayed widgets
const thisWidget = useDashboardStore(
useCallback(
(state) => state.widgets.find((widget) => widget.id === widgetID),
[widgetID],
),
);
// 1) BUILD URL, default is 7 days past
const url = useBuildUrl(
"/api/v2/alert_changes",
settings?.startDate,
settings?.endDate,
);
// 2) CALL API hook to retrieve data
const { data: apiData, isLoading, error } = VdapApi(url);
// 3) SORT the data by grouping into volcanoes and sorting form newest to oldest
// doing this 1st bc the filters might change w/ user input but the sorts (currently) wont and thus will
// only run when the api retrieves new data
const sortedData = useMemo(() => {
if (apiData.length === 0) return [];
// .reduce() is a built-in array method used to transform an entire array into a single value (like an object)
// here we sort the data by volcano, and return it as a single massive object w/ volcano names as keys
const grouped = apiData.reduce((accumulator, item) => {
if (!accumulator[item.volcano]) accumulator[item.volcano] = [];
accumulator[item.volcano].push(item);
return accumulator;
}, {});
// converts the 'grouped' object back into an array of [key, value] pairs
const group_arr = Object.entries(grouped);
// sort data newest to oldest within each volcano
const sorted = group_arr.map((item) => [
item[0], // volcano name
[...item[1]].sort( // item[1] is the array of al changes for a volcano
(a, b) =>
new Date(b.alert_start_date_local) -
new Date(a.alert_start_date_local),
),
]);
// the volcanoes with the most recent issued alert level change appear at the top of the list (newest -> oldest)
const sorted_entries = [...sorted].sort(
(a, b) =>
new Date(b[1][0].alert_start_date_local) -
new Date(a[1][0].alert_start_date_local),
);
// additionally, seperates out data into domains within a volcano for sm widgets,
// to list all most recent al changes w/in a volcano w/in a domain
if (thisWidget?.size === "small") {
return sorted_entries.map((volcano) => {
const domain = volcano[1].reduce((acc, curr) => {
if (!acc[curr.alert_scheme_domain])
acc[curr.alert_scheme_domain] = [];
acc[curr.alert_scheme_domain].push(curr);
return acc;
}, {});
const domain_arr = Object.entries(domain);
console.log("sm data: ", [volcano[0], domain_arr]);
return [volcano[0], domain_arr];
});
}
console.log("lg data: ", sorted_entries);
return sorted_entries;
}, [apiData, thisWidget]);
// 4) FILTER according to what is set in the widgets settings
const filteredData = useMemo(() => {
if (sortedData.length === 0) return [];
let filtered_arr = [];
// must show at least one inc or dec alert levels, otherwise no data is displayed
if (
!settings?.filters?.levelIncreases &&
!settings?.filters?.levelDecreases
)
return [];
// return all data
if (
settings?.filters?.levelIncreases &&
settings?.filters?.levelDecreases &&
!settings?.filters.elevatedLevels
)
return sortedData;
// only elevated alert levels
filtered_arr = settings?.filters.elevatedLevels
? sortedData.map((volcano) => [
volcano[0],
volcano[1].filter(
(item) =>
item.alert_scheme_highest - Number(item.alert_level_rank) <= 1,
),
])
.filter((volcano) => volcano[1].length > 0)
: sortedData;
// only decreases (rm inc)
filtered_arr = !settings?.filters.levelIncreases
? filtered_arr.map((volcano) => [
volcano[0],
volcano[1].filter((item) => !(item.change_type === "increase")),
])
.filter((volcano) => volcano[1].length > 0)
: filtered_arr;
// only increases (rm dec)
filtered_arr = !settings?.filters?.levelDecreases
? filtered_arr.map((volcano) => [
volcano[0],
volcano[1].filter((item) => !(item.change_type === "decrease")),
])
.filter((volcano) => volcano[1].length > 0)
: filtered_arr;
console.log("filtered_arr: ", filtered_arr);
return filtered_arr;
}, [sortedData, settings]);
// Alert colors are now light/dark mode responsive
const alertColors = {
Red: "alertRed",
Orange: "alertOrange",
Yellow: "alertYellow",
Green: "alertGreen",
};
const options = {
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
second: "2-digit",
timeZoneName: "short"
};
if (isLoading)
return (
);
if (error)
return (
{error}
);
if (filteredData.length === 0)
return (
No alert level data available for the selected dates.
);
return (
{filteredData.map((volcano, index) => {
if (thisWidget?.size === "large")
return (
{(collapsible) => (
{volcano[0]}
{new Date(volcano[1][0]?.alert_start_date_local)
.toLocaleString('en-US', options)
}
{volcano[1][0]?.change_type === "increase" ? (
) : (
)}
{volcano[1][0]?.alert_level_name}
)}
{volcano[1]?.map((object, index) => (
{object?.alert_scheme_domain === "terrestrial" && }
{object?.alert_scheme_domain === "aviation" && }
{object?.alert_scheme_domain === "maritime" && }
{object?.change_type === "increase" ? (
) : (
)}
{new Date(object?.alert_start_date_local)
.toLocaleString('en-US', {...options, timeZone: 'UTC'})
}
{object?.alert_level_name}
Alert Scale:
{Array.from(
{
length:
Number(object?.alert_scheme_highest) -
Number(object?.alert_scheme_lowest) + 1,
},
(v, i) => Number(object?.alert_scheme_lowest) + i
).map((num) => (
{num}
))}
))}
);
else if (thisWidget?.size === "small")
return volcano[1]?.map((domain, index) => (
{domain[0] === "aviation" && }
{domain[0] === "terrestrial" && }
{domain[0] === "maritime" && }
{volcano[0] || ""}
{domain[1]?.[0]?.alert_start_date_local || ""}
{domain[1]?.[0]?.change_type === "increase" ? (
) : (
)}
{domain[1]?.[0]?.alert_level_name}
));
})}
);
}