-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGeneralDataTable.tsx
More file actions
194 lines (178 loc) · 6.28 KB
/
Copy pathGeneralDataTable.tsx
File metadata and controls
194 lines (178 loc) · 6.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// בס"ד
import {
createColumnHelper,
flexRender,
getCoreRowModel,
getSortedRowModel,
useReactTable,
type SortingState,
} from "@tanstack/react-table";
import type {
FuelEvents,
GameTime,
GeneralFuelData,
TeamNumberAndFuelData,
} from "@repo/scouting_types";
import type React from "react";
import { useState, useEffect, useMemo } from "react";
import { FaChevronDown, FaChevronUp } from "react-icons/fa";
import { HiOutlineChevronUpDown } from "react-icons/hi2";
interface TableRow {
teamNumber: number;
generalFuelData: GeneralFuelData;
}
const fetchFuelData = async (filters = {}) => {
const params = new URLSearchParams(filters);
const url = `/api/v1/general/?${params.toString()}`;
try {
const response = await fetch(url, {
method: "GET",
headers: { "Content-Type": "application/json" },
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Server Error: ${errorText}`);
}
const data = await response.json();
return data.calculatedFuel as TeamNumberAndFuelData;
} catch (err) {
console.error("Fetch failed:", err);
throw err;
}
};
interface GeneralDataTableProps {
filters: {};
}
const DIGITS_AFTER_DOT = 1;
export const GeneralDataTable: React.FC<GeneralDataTableProps> = ({
filters,
}) => {
const [teamNumberAndFuelData, setTeamNumberAndFuelData] =
useState<TeamNumberAndFuelData>({});
const [gameTime, setGameTime] = useState<GameTime>("tele");
const [sorting, setSorting] = useState<SortingState>([]);
useEffect(() => {
fetchFuelData(filters).then(setTeamNumberAndFuelData).catch(console.error);
}, [filters]);
const tableData = useMemo(
() =>
Object.entries(teamNumberAndFuelData).map(
([teamNumber, generalFuelData]) => ({
teamNumber: Number(teamNumber),
generalFuelData,
_uiKey: gameTime,
}),
),
[teamNumberAndFuelData, gameTime],
);
const columnHelper = createColumnHelper<TableRow>();
const createColumn = (headerAndId: FuelEvents, style: string) =>
columnHelper.accessor((row) => row.generalFuelData[gameTime][headerAndId], {
id: headerAndId,
header: headerAndId,
cell: (info) => (
<span className={style}>
{info.getValue().toFixed(DIGITS_AFTER_DOT)}
</span>
),
});
const columns = useMemo(
() => [
columnHelper.accessor("teamNumber", {
header: "Team Number",
cell: (info) => (
<span className="font-black text-emerald-400">{info.getValue()}</span>
),
}),
createColumn("scored", "text-emerald-400 font-bold"),
createColumn("missed", "text-rose-500/90 font-medium"),
createColumn("passed", "text-orange-400 font-medium"),
createColumn("shot", "text-slate-300 font-medium"),
],
[gameTime],
);
const table = useReactTable({
data: tableData,
columns,
state: { sorting },
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
});
return (
<div className="flex flex-col gap-6 p-4 bg-slate-950 min-h-screen">
<div className="overflow-hidden rounded-2xl border border-white/10 bg-slate-900/40 backdrop-blur-sm shadow-2xl">
<table className="w-full text-left text-sm border-collapse">
<thead className="bg-slate-800/50 border-b border-white/10">
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th
key={header.id}
className="px-6 py-4 font-black text-slate-400 uppercase tracking-widest text-[10px] cursor-pointer select-none transition-colors hover:bg-slate-800"
onClick={header.column.getToggleSortingHandler()}
>
<div className="flex items-center gap-2">
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
<span className="text-emerald-500/50">
{header.column.getIsSorted() === "asc" ? (
<FaChevronUp size={12} strokeWidth={3} />
) : header.column.getIsSorted() === "desc" ? (
<FaChevronDown size={12} strokeWidth={3} />
) : (
<HiOutlineChevronUpDown size={12} strokeWidth={2} />
)}
</span>
</div>
</th>
))}
</tr>
))}
</thead>
<tbody className="divide-y divide-white/5">
{table.getRowModel().rows.map((row) => {
console.log("data:", tableData, "columns:", columns);
// for some reason these rows dont update unless
//they reference the tableData in them
return (
<tr
key={row.id}
className="hover:bg-emerald-500/5 transition-colors group"
>
{row.getVisibleCells().map((cell) => (
<td key={cell.id} className="px-6 py-4 whitespace-nowrap">
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</td>
))}
</tr>
);
})}
</tbody>
</table>
</div>
<div className="flex gap-1.5 justify-center bg-slate-900/50 p-1.5 rounded-2xl border border-white/5 self-center">
{(["auto", "tele", "fullGame"] as GameTime[]).map((time) => (
<button
key={time}
onClick={() => {
setGameTime(time);
}}
className={`px-5 py-2 rounded-xl text-[11px] font-black uppercase tracking-wider transition-all border ${
gameTime === time
? "bg-emerald-500 text-slate-950 border-emerald-400 shadow-[0_0_15px_rgba(16,185,129,0.2)]"
: "bg-transparent text-slate-500 border-transparent hover:text-slate-300"
}`}
>
{time}
</button>
))}
</div>
</div>
);
};