- Auth: TOTP replay protection with counter tracking, constant-time backup code comparison, atomic lockout increment, per-token logout - Invoices/PDFs: net-based VAT calculation, dangerous URL scheme stripping in cleanQuillHtml, orders-pdf error handling - Orders: reject item changes on status transition, cascading delete cleanup, take:1 with orderBy - Projects: atomic rename collision handling, MIME/extension validation, empty customer name rejection - Attendance: Czech public holiday awareness in frontend fund calculation, leave_hours 0 handling, invalid date NaN guard, bounded per-month queries in workfund - Users/Admin: profile audit logging + password validation, session revocation guard, session ID validation, dashboard DB aggregation, soft-deleted record protection in scope templates - Frontend: FormField label linkage, Pagination ARIA, error handling in OrderConfirmationModal, 401 propagation, GPS emoji hidden from screen readers, table sort state fix, geolocation race/abort cleanup, Leaflet popup DOM safety, Vehicles toggleActive minimal body, CompanySettings ref mutation fix, OfferDetail unlock abort, AttendanceBalances combined fetches - Utils: env validation, Puppeteer concurrency mutex, invoice alert cron cleanup on shutdown, body limit alignment, TOTP error logging, trustProxy from env, symlink rejection, rate cache Map usage Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
32 lines
805 B
TypeScript
32 lines
805 B
TypeScript
import { useState, useCallback } from "react";
|
|
|
|
interface SortState {
|
|
sort: string;
|
|
order: "asc" | "desc";
|
|
}
|
|
|
|
export default function useTableSort(
|
|
defaultSort = "id",
|
|
defaultOrder: "asc" | "desc" = "desc",
|
|
) {
|
|
const [state, setState] = useState<SortState>({
|
|
sort: defaultSort,
|
|
order: defaultOrder,
|
|
});
|
|
const [userClicked, setUserClicked] = useState(false);
|
|
|
|
const handleSort = useCallback((column: string) => {
|
|
setUserClicked(true);
|
|
setState((prev) => {
|
|
if (prev.sort === column) {
|
|
return { sort: column, order: prev.order === "asc" ? "desc" : "asc" };
|
|
}
|
|
return { sort: column, order: "desc" };
|
|
});
|
|
}, []);
|
|
|
|
const activeSort = userClicked ? state.sort : null;
|
|
|
|
return { sort: state.sort, order: state.order, handleSort, activeSort };
|
|
}
|