import React, { useCallback, useMemo } from "react"; import { Button, Stack, TableBody, TableRow } from "@mui/material"; import { useDashboardFilter } from "../contexts/DashboardFilterContext"; import { formatDate } from "../util/format_date"; import { TableCell } from "./Table/TableCell"; import TableHead from "./Table/TableHead"; import TableHeading from "./Table/TableHeading"; import Table from "./Table"; interface DataItem { [key: string]: string | number | boolean | Date; } interface StatsDataTableProps { data: DataItem[]; dateKey?: string; csvFileName?: string; } export default function StatsDataTable({ data, dateKey, csvFileName }: StatsDataTableProps) { const { filter } = useDashboardFilter(); const mappedData = useMemo(() => { return data .map((item) => { const dateEntry = getDateEntry(item, dateKey); if (dateEntry === undefined) return undefined; const [key, date] = dateEntry; const { [key]: _, ...rest } = item; return { [key]: date, ...rest }; }) .filter((item) => item !== undefined); }, [data, dateKey]); const handleExportToCSV = useCallback(() => { if (mappedData.length === 0) return; const headers = Object.keys(mappedData[0]); const csvRows = [ headers.join(","), // Header row ...mappedData.map((row) => headers .map((key) => { const value = row[key]; if (value instanceof Date) return `"${value.toISOString()}"`; if (typeof value === "string") return `"${value.replace(/"/g, '""')}"`; // Escape quotes return value; }) .join(","), ), ]; const csvContent = csvRows.join("\n"); const blob = new Blob([csvContent], { type: "text/csv" }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = `${csvFileName || "data"}_${filter.startDate.toISO()?.split("T")[0]}_${filter.endDate.toISO()?.split("T")[0]}.csv`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); }, [mappedData, filter.startDate, filter.endDate, filter.granularity]); return ( {mappedData.length > 0 && Object.keys(mappedData[0]).map((key) => { return {formatKey(key)}; })} {mappedData.map((item, i) => ( {Object.entries(item).map(([key, value]) => ( {value instanceof Date ? formatDate(value, filter.granularity) : value.toString()} ))} ))}
); } function formatKey(key: string) { return key .split("_") .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) .join(" "); } function getDateEntry(item: DataItem, dateKey?: string): [string, Date] | undefined { if (dateKey !== undefined && item.hasOwnProperty(dateKey)) { let date = item[dateKey]; date = typeof date === "string" ? new Date(date) : date; if (!(date instanceof Date)) return undefined; return [dateKey, date]; } const dateEntry = Object.entries(item).find(([_, value]) => isDateOrString(value)) as | [string, Date | string] | undefined; if (dateEntry === undefined) { return undefined; } const dateObj = dateEntry[1] instanceof Date ? dateEntry[1] : new Date(dateEntry[1]); return [dateEntry[0], dateObj]; } function isDateOrString(value: unknown): value is Date | string { return ( (value instanceof Date && !Number.isNaN(value.getTime())) || (typeof value === "string" && !Number.isNaN(Date.parse(value))) ); }