| 84 | /* --------------------------- sorting comparator -------------------------- */ |
| 85 | |
| 86 | function makeComparator(sortKey, sortDir) { |
| 87 | if (!sortKey || !sortDir) return null; |
| 88 | const col = COLUMNS.find((c) => c.key === sortKey); |
| 89 | const sign = sortDir === "asc" ? 1 : -1; |
| 90 | const value = (r) => { |
| 91 | switch (col.sortType) { |
| 92 | case "string": |
| 93 | return sortKey === "applicant" ? r.ApplicantID : r.ProgramID; |
| 94 | case "status": |
| 95 | return STATUS_RANK[r.Status] ?? 99; |
| 96 | case "bool": |
| 97 | return r.Final ? 1 : 0; |
| 98 | case "season": |
| 99 | return r.ProgramYear * 10 + (SEMESTER_WEIGHT[r.Semester] ?? 0); |
| 100 | case "date": |
| 101 | return dateText(r.TimeLine?.[TIMELINE_KEY[sortKey]]); |
| 102 | default: |
| 103 | return ""; |
| 104 | } |
| 105 | }; |
| 106 | return (a, b) => { |
| 107 | const va = value(a); |
| 108 | const vb = value(b); |
| 109 | if (typeof va === "number" && typeof vb === "number") return (va - vb) * sign; |
| 110 | return String(va).localeCompare(String(vb)) * sign; |
| 111 | }; |
| 112 | } |
| 113 | |
| 114 | /* default order: keep program grouping order, newest season first within program */ |
| 115 | function defaultSort(records) { |