Files
stats/apps/web/src/components/report/chart/ReportBarChart.tsx
2023-10-29 21:15:09 +01:00

193 lines
6.3 KiB
TypeScript

import { ColorSquare } from "@/components/ColorSquare";
import { type IChartData } from "@/types";
import { type RouterOutputs } from "@/utils/api";
import { getChartColor } from "@/utils/theme";
import {
useReactTable,
getCoreRowModel,
flexRender,
createColumnHelper,
getSortedRowModel,
type SortingState,
} from "@tanstack/react-table";
import { useMemo, useState } from "react";
import { useElementSize } from "usehooks-ts";
import { useChartContext } from "./ChartProvider";
import { ChevronDown, ChevronUp } from "lucide-react";
import { cn } from "@/utils/cn";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
const columnHelper =
createColumnHelper<RouterOutputs["chart"]["chart"]["series"][number]>();
type ReportBarChartProps = {
data: IChartData;
};
export function ReportBarChart({ data }: ReportBarChartProps) {
const { editMode } = useChartContext();
const [ref, { width }] = useElementSize();
const [sorting, setSorting] = useState<SortingState>([]);
const table = useReactTable({
data: useMemo(
() => (editMode ? data.series : data.series.slice(0, 20)),
[editMode, data],
),
columns: useMemo(() => {
return [
columnHelper.accessor((row) => row.name, {
id: "label",
header: () => "Label",
cell(info) {
return (
<div className="flex items-center gap-2">
<ColorSquare>{info.row.original.event.id}</ColorSquare>
{info.getValue()}
</div>
);
},
footer: (info) => info.column.id,
size: width ? width * 0.3 : undefined,
}),
columnHelper.accessor((row) => row.totalCount, {
id: "totalCount",
cell: (info) => (
<div className="text-right font-medium">{info.getValue()}</div>
),
header: () => "Count",
footer: (info) => info.column.id,
size: width ? width * 0.1 : undefined,
enableSorting: true,
}),
columnHelper.accessor((row) => row.totalCount, {
id: "graph",
cell: (info) => (
<div
className="shine h-4 rounded [.mini_&]:h-3"
style={{
width:
(info.getValue() / info.row.original.meta.highest) * 100 +
"%",
background: getChartColor(info.row.index),
}}
/>
),
header: () => "Graph",
footer: (info) => info.column.id,
size: width ? width * 0.6 : undefined,
}),
];
}, [width]),
columnResizeMode: "onChange",
state: {
sorting,
},
getCoreRowModel: getCoreRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
debugTable: true,
debugHeaders: true,
debugColumns: true,
});
return (
<div ref={ref}>
{editMode && (
<div className="mb-8 flex flex-wrap gap-4">
{data.events.map((event) => {
return (
<div className="border border-border p-4" key={event.id}>
<div className="flex items-center gap-2 text-lg font-medium">
<ColorSquare>{event.id}</ColorSquare> {event.name}
</div>
<div className="mt-6 font-mono text-5xl font-light">
{new Intl.NumberFormat("en-IN", {
maximumSignificantDigits: 20,
}).format(event.count)}
</div>
</div>
);
})}
</div>
)}
<div className="overflow-x-auto">
<Table
{...{
className: editMode ? '' : 'mini',
style: {
width: table.getTotalSize(),
},
}}
>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead
key={header.id}
{...{
colSpan: header.colSpan,
style: {
width: header.getSize(),
},
}}
>
<div
{...{
className: cn(
"flex items-center gap-2",
header.column.getCanSort() &&
"cursor-pointer select-none",
),
onClick: header.column.getToggleSortingHandler(),
}}
>
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
{{
asc: <ChevronUp className="ml-auto" size={14} />,
desc: <ChevronDown className="ml-auto" size={14} />,
}[header.column.getIsSorted() as string] ?? null}
</div>
<div
{...(editMode
? {
onMouseDown: header.getResizeHandler(),
onTouchStart: header.getResizeHandler(),
className: `resizer ${
header.column.getIsResizing() ? "isResizing" : ""
}`,
style: {},
}
: {})}
/>
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows.map((row) => (
<TableRow key={row.id}>
{row.getVisibleCells().map((cell) => (
<TableCell
key={cell.id}
{...{
style: {
width: cell.column.getSize(),
},
}}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
);
}