feat(archive): implement sortable columns in archive table with toggle functionality

This commit is contained in:
Nik Afiq 2025-12-18 01:01:35 +09:00
parent 2889a38ab6
commit 5b02eb1226
2 changed files with 84 additions and 9 deletions

View File

@ -530,6 +530,16 @@ kbd {
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.08);
}
.archive-head-btn {
all: unset;
cursor: pointer;
font-weight: 800;
color: var(--subtle);
display: inline-flex;
align-items: center;
gap: 4px;
}
.archive-head-btn:hover { color: var(--text); }
.archive-row {
background: rgba(255,255,255,0.03);
border: 1px solid rgba(255,255,255,0.05);

View File

@ -5,6 +5,9 @@ import { useAuth } from "../auth/AuthProvider";
import { toastError } from "../utils/toastBus";
import { logApiError } from "../utils/logger";
type SortKey = "id" | "ep_num" | "ep_title" | "season_name" | "start_time" | "playback_length" | "date_created" | "date_archived";
type SortDir = "asc" | "desc" | null;
function formatTimestamp(ts: string) {
if (!ts) return "";
const d = new Date(ts);
@ -17,6 +20,7 @@ export default function ArchivePage() {
const [items, setItems] = React.useState<ArchiveItem[]>([]);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState<string | null>(null);
const [sort, setSort] = React.useState<{ key: SortKey | null; dir: SortDir }>({ key: null, dir: null });
const load = React.useCallback(async () => {
if (!idToken) {
@ -46,6 +50,51 @@ export default function ArchivePage() {
}
}, [enabled, isAdmin, idToken, load]);
const sortedItems = React.useMemo(() => {
const { key, dir } = sort;
if (!key || !dir) return items;
const next = [...items];
next.sort((a, b) => {
let av: string | number = "";
let bv: string | number = "";
switch (key) {
case "id":
case "ep_num":
av = a[key];
bv = b[key];
break;
case "date_created":
case "date_archived":
av = new Date(a[key]).getTime();
bv = new Date(b[key]).getTime();
break;
default:
av = (a as Record<string, unknown>)[key] as string | number | undefined ?? "";
bv = (b as Record<string, unknown>)[key] as string | number | undefined ?? "";
}
if (av === bv) return 0;
const asc = av > bv ? 1 : -1;
return dir === "asc" ? asc : -asc;
});
return next;
}, [items, sort]);
function toggleSort(key: SortKey) {
setSort((prev) => {
if (prev.key !== key) return { key, dir: "asc" };
if (prev.dir === "asc") return { key, dir: "desc" };
if (prev.dir === "desc") return { key: null, dir: null };
return { key, dir: "asc" };
});
}
const sortIndicator = (key: SortKey) => {
if (sort.key !== key) return "";
if (sort.dir === "asc") return "▲";
if (sort.dir === "desc") return "▼";
return "";
};
if (!enabled) {
return <div className="subtle"></div>;
}
@ -77,16 +126,32 @@ export default function ArchivePage() {
<div className="archive-scroll">
<div className="archive-table">
<div className="archive-header">
<span>ID</span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<button className="archive-head-btn" onClick={() => toggleSort("id")}>
ID {sortIndicator("id")}
</button>
<button className="archive-head-btn" onClick={() => toggleSort("ep_num")}>
{sortIndicator("ep_num")}
</button>
<button className="archive-head-btn" onClick={() => toggleSort("ep_title")}>
{sortIndicator("ep_title")}
</button>
<button className="archive-head-btn" onClick={() => toggleSort("season_name")}>
{sortIndicator("season_name")}
</button>
<button className="archive-head-btn" onClick={() => toggleSort("start_time")}>
{sortIndicator("start_time")}
</button>
<button className="archive-head-btn" onClick={() => toggleSort("playback_length")}>
{sortIndicator("playback_length")}
</button>
<button className="archive-head-btn" onClick={() => toggleSort("date_created")}>
{sortIndicator("date_created")}
</button>
<button className="archive-head-btn" onClick={() => toggleSort("date_archived")}>
{sortIndicator("date_archived")}
</button>
</div>
{items.map((it) => (
{sortedItems.map((it) => (
<div key={it.id} className="archive-row">
<span>#{it.id}</span>
<span>{it.ep_num}</span>