"use client"; import { useState, useMemo, useCallback } from "react"; export type SortDirection = "asc" | "desc" | null; export interface SortState { column: string | null; direction: SortDirection; } function getValue(obj: Record, key: string): unknown { return obj[key]; } function compareValues(a: unknown, b: unknown, direction: "asc" | "desc"): number { // Handle nulls/undefined — push them to the end always if (a == null && b == null) return 0; if (a == null) return 1; if (b == null) return -1; const aVal = a; const bVal = b; // Numeric comparison const aNum = Number(aVal); const bNum = Number(bVal); if (!isNaN(aNum) && !isNaN(bNum) && String(aVal).trim() !== "" && String(bVal).trim() !== "") { return direction === "asc" ? aNum - bNum : bNum - aNum; } // Date comparison const aStr = String(aVal); const bStr = String(bVal); const aDate = Date.parse(aStr); const bDate = Date.parse(bStr); if (!isNaN(aDate) && !isNaN(bDate) && aStr.length > 4 && bStr.length > 4) { return direction === "asc" ? aDate - bDate : bDate - aDate; } // String comparison const cmp = aStr.localeCompare(bStr, undefined, { sensitivity: "base", numeric: true }); return direction === "asc" ? cmp : -cmp; } export function useTableSort>(data: T[]) { const [sort, setSort] = useState({ column: null, direction: null }); const toggleSort = useCallback((column: string) => { setSort((prev) => { if (prev.column !== column) return { column, direction: "asc" }; if (prev.direction === "asc") return { column, direction: "desc" }; return { column: null, direction: null }; }); }, []); const sorted = useMemo(() => { if (!sort.column || !sort.direction) return data; return [...data].sort((a, b) => compareValues(getValue(a, sort.column!), getValue(b, sort.column!), sort.direction!) ); }, [data, sort]); return { sorted, sort, toggleSort }; }