177 lines
6.1 KiB
TypeScript
177 lines
6.1 KiB
TypeScript
import * as React from 'react';
|
|
import type { IChartData } from '@/app/_trpc/client';
|
|
import { Pagination, usePagination } from '@/components/pagination';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Checkbox } from '@/components/ui/checkbox';
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from '@/components/ui/table';
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipTrigger,
|
|
} from '@/components/ui/tooltip';
|
|
import { useFormatDateInterval } from '@/hooks/useFormatDateInterval';
|
|
import { useMappings } from '@/hooks/useMappings';
|
|
import { useNumber } from '@/hooks/useNumerFormatter';
|
|
import { useSelector } from '@/redux';
|
|
import { getChartColor } from '@/utils/theme';
|
|
|
|
import { PreviousDiffIndicator } from '../PreviousDiffIndicator';
|
|
|
|
interface ReportTableProps {
|
|
data: IChartData;
|
|
visibleSeries: IChartData['series'];
|
|
setVisibleSeries: React.Dispatch<React.SetStateAction<string[]>>;
|
|
}
|
|
|
|
const ROWS_LIMIT = 50;
|
|
|
|
export function ReportTable({
|
|
data,
|
|
visibleSeries,
|
|
setVisibleSeries,
|
|
}: ReportTableProps) {
|
|
const { setPage, paginate, page } = usePagination(ROWS_LIMIT);
|
|
const number = useNumber();
|
|
const interval = useSelector((state) => state.report.interval);
|
|
const formatDate = useFormatDateInterval(interval);
|
|
const getLabel = useMappings();
|
|
|
|
function handleChange(name: string, checked: boolean) {
|
|
setVisibleSeries((prev) => {
|
|
if (checked) {
|
|
return [...prev, name];
|
|
} else {
|
|
return prev.filter((item) => item !== name);
|
|
}
|
|
});
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div className="grid grid-cols-[200px_1fr] border border-border rounded-md overflow-hidden">
|
|
<Table className="rounded-none border-t-0 border-l-0 border-b-0">
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>Name</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{paginate(data.series).map((serie, index) => {
|
|
const checked = !!visibleSeries.find(
|
|
(item) => item.name === serie.name
|
|
);
|
|
|
|
return (
|
|
<TableRow key={serie.name}>
|
|
<TableCell className="h-10">
|
|
<div className="flex items-center gap-2">
|
|
<Checkbox
|
|
onCheckedChange={(checked) =>
|
|
handleChange(serie.name, !!checked)
|
|
}
|
|
style={
|
|
checked
|
|
? {
|
|
background: getChartColor(index),
|
|
borderColor: getChartColor(index),
|
|
}
|
|
: undefined
|
|
}
|
|
checked={checked}
|
|
/>
|
|
<Tooltip delayDuration={200}>
|
|
<TooltipTrigger asChild>
|
|
<div className="min-w-0 overflow-hidden whitespace-nowrap text-ellipsis">
|
|
{getLabel(serie.name)}
|
|
</div>
|
|
</TooltipTrigger>
|
|
<TooltipContent>
|
|
<p>{getLabel(serie.name)}</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</div>
|
|
</TableCell>
|
|
</TableRow>
|
|
);
|
|
})}
|
|
</TableBody>
|
|
</Table>
|
|
<div className="overflow-auto">
|
|
<Table className="rounded-none border-none">
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>Total</TableHead>
|
|
<TableHead>Average</TableHead>
|
|
{data.series[0]?.data.map((serie) => (
|
|
<TableHead
|
|
key={serie.date.toString()}
|
|
className="whitespace-nowrap"
|
|
>
|
|
{formatDate(serie.date)}
|
|
</TableHead>
|
|
))}
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{paginate(data.series).map((serie) => {
|
|
return (
|
|
<TableRow key={serie.name}>
|
|
<TableCell className="h-10">
|
|
<div className="flex items-center gap-2 font-medium">
|
|
{number.format(serie.metrics.sum)}
|
|
<PreviousDiffIndicator
|
|
{...serie.metrics.previous.sum}
|
|
/>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell className="h-10">
|
|
<div className="flex items-center gap-2 font-medium">
|
|
{number.format(serie.metrics.average)}
|
|
<PreviousDiffIndicator
|
|
{...serie.metrics.previous.average}
|
|
/>
|
|
</div>
|
|
</TableCell>
|
|
|
|
{serie.data.map((item) => {
|
|
return (
|
|
<TableCell className="h-10" key={item.date.toString()}>
|
|
<div className="flex items-center gap-2">
|
|
{number.format(item.count)}
|
|
<PreviousDiffIndicator {...item.previous} />
|
|
</div>
|
|
</TableCell>
|
|
);
|
|
})}
|
|
</TableRow>
|
|
);
|
|
})}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col-reverse gap-4 md:flex-row md:justify-between md:items-center">
|
|
<div className="flex gap-1 flex-wrap">
|
|
<Badge>Total: {number.format(data.metrics.sum)}</Badge>
|
|
<Badge>Average: {number.format(data.metrics.average)}</Badge>
|
|
<Badge>Min: {number.format(data.metrics.min)}</Badge>
|
|
<Badge>Max: {number.format(data.metrics.max)}</Badge>
|
|
</div>
|
|
<Pagination
|
|
cursor={page}
|
|
setCursor={setPage}
|
|
take={ROWS_LIMIT}
|
|
count={data.series.length}
|
|
/>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|