({records, insideProgramPage = false, style = {}, refreshSlot = null})
| 160 | /* ------------------------------ main table ------------------------------- */ |
| 161 | |
| 162 | export default function RecordTable({records, insideProgramPage = false, style = {}, refreshSlot = null}) { |
| 163 | const navigate = useNavigate(); |
| 164 | const location = useLocation(); |
| 165 | const chip = useChipColors(); |
| 166 | const theme = useTheme(); |
| 167 | const dark = theme.palette.mode === "dark"; |
| 168 | const columns = useMemo( |
| 169 | () => COLUMNS.filter((c) => !(insideProgramPage && c.programOnly)), |
| 170 | [insideProgramPage] |
| 171 | ); |
| 172 | const tableWidth = columns.reduce((sum, c) => sum + c.width, 0); |
| 173 | const cssVars = { |
| 174 | "--rt-border": theme.palette.divider, |
| 175 | "--rt-head-bg": theme.palette.surfaceVariant, |
| 176 | "--rt-row-hover": alpha(theme.palette.primary.main, dark ? 0.12 : 0.06), |
| 177 | "--rt-text": theme.palette.text.primary, |
| 178 | "--rt-text-2": theme.palette.text.secondary, |
| 179 | "--rt-group-bg": alpha(theme.palette.text.primary, dark ? 0.06 : 0.035), |
| 180 | "--rt-success": theme.palette.admit.main, |
| 181 | "--rt-input-bg": alpha(theme.palette.text.primary, dark ? 0.07 : 0.045), |
| 182 | "--rt-input-bg-hover": alpha(theme.palette.text.primary, dark ? 0.11 : 0.07), |
| 183 | "--rt-accent": theme.palette.primary.main, |
| 184 | "--rt-accent-soft": alpha(theme.palette.primary.main, dark ? 0.25 : 0.4), |
| 185 | "--rt-min-width": `${tableWidth}px`, |
| 186 | }; |
| 187 | |
| 188 | const [search, setSearch] = useState(""); |
| 189 | const [statusFilter, setStatusFilter] = useState([]); |
| 190 | const [seasonFilter, setSeasonFilter] = useState([]); |
| 191 | const [finalFilter, setFinalFilter] = useState(""); // '', 'true', 'false' |
| 192 | const [grouped, setGrouped] = useState(true); |
| 193 | const [sortKey, setSortKey] = useState(null); |
| 194 | const [sortDir, setSortDir] = useState(null); // 'asc' | 'desc' | null |
| 195 | |
| 196 | const seasonOptions = useMemo(() => { |
| 197 | const set = new Set(records.map((r) => `${r.ProgramYear} ${r.Semester}`)); |
| 198 | return [...set].sort((a, b) => b.localeCompare(a)); |
| 199 | }, [records]); |
| 200 | |
| 201 | const filtered = useMemo(() => { |
| 202 | const q = search.trim().toLowerCase(); |
| 203 | return records.filter((r) => { |
| 204 | if (statusFilter.length && !statusFilter.includes(r.Status)) return false; |
| 205 | if (seasonFilter.length && !seasonFilter.includes(`${r.ProgramYear} ${r.Semester}`)) return false; |
| 206 | if (finalFilter !== "" && r.Final !== (finalFilter === "true")) return false; |
| 207 | if (q) { |
| 208 | const hay = `${r.ApplicantID} ${r.ProgramID} ${r.Status} ${r.ProgramYear} ${r.Semester} ${r.Detail ?? ""}`.toLowerCase(); |
| 209 | if (!hay.includes(q)) return false; |
| 210 | } |
| 211 | return true; |
| 212 | }); |
| 213 | }, [records, search, statusFilter, seasonFilter, finalFilter]); |
| 214 | |
| 215 | const rows = useMemo(() => { |
| 216 | const comparator = makeComparator(sortKey, sortDir); |
| 217 | if (grouped && !insideProgramPage) { |
| 218 | // group by program (in default program order), sort within each group |
| 219 | const order = defaultSort(filtered); |
nothing calls this directly
no test coverage detected