mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-02 12:03:12 +00:00
Move sorting to server-side for full dataset ordering
- usePaginatedList now manages sortBy/sortDir state - Sort params passed to fetch function and sent to API - SharedTable receives sort state as props instead of managing internally - Clicking column headers triggers API refetch with new sort - Removes client-side sorting that only worked on current page
This commit is contained in:
parent
79f7b4782a
commit
e2fc183636
5 changed files with 32 additions and 38 deletions
|
|
@ -87,14 +87,16 @@ const SharedTable = ({
|
|||
setPageSize,
|
||||
totalCount,
|
||||
loading,
|
||||
error
|
||||
error,
|
||||
sortBy,
|
||||
setSortBy,
|
||||
sortDir,
|
||||
setSortDir
|
||||
}) => {
|
||||
const totalPages = Math.max(1, Math.ceil(totalCount / pageSize));
|
||||
const [colWidths, setColWidths] = useState(null);
|
||||
const [columnOrder, setColumnOrder] = useState(() => columns.map((c) => c.key));
|
||||
const [hiddenColumns, setHiddenColumns] = useState([]);
|
||||
const [sortKey, setSortKey] = useState("lastUpdated");
|
||||
const [sortDir, setSortDir] = useState("desc");
|
||||
const gridRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -166,25 +168,16 @@ const SharedTable = ({
|
|||
}, [snapshotWidths, visibleColumns]);
|
||||
|
||||
const handleSort = (key) => {
|
||||
if (sortKey === key) {
|
||||
setSortDir((d) => (d === "asc" ? "desc" : "asc"));
|
||||
if (!setSortBy || !setSortDir) return;
|
||||
if (sortBy === key) {
|
||||
setSortDir(sortDir === "asc" ? "desc" : "asc");
|
||||
} else {
|
||||
setSortKey(key);
|
||||
setSortBy(key);
|
||||
setSortDir("asc");
|
||||
}
|
||||
if (setPage) setPage(1);
|
||||
};
|
||||
|
||||
const sortedRows = [...rows].sort((a, b) => {
|
||||
if (!sortKey) return 0;
|
||||
let aVal = a[sortKey] ?? "";
|
||||
let bVal = b[sortKey] ?? "";
|
||||
if (typeof aVal === "string") aVal = aVal.toLowerCase();
|
||||
if (typeof bVal === "string") bVal = bVal.toLowerCase();
|
||||
if (aVal < bVal) return sortDir === "asc" ? -1 : 1;
|
||||
if (aVal > bVal) return sortDir === "asc" ? 1 : -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (loading) return <div className="loading">Loading...</div>;
|
||||
if (error) return <div className="error">{error}</div>;
|
||||
|
||||
|
|
@ -243,11 +236,11 @@ const SharedTable = ({
|
|||
<div
|
||||
key={col.key}
|
||||
data-col={col.key}
|
||||
className={`gridCell gridHeader gridSortable ${sortKey === col.key ? "sorted" : ""}`}
|
||||
className={`gridCell gridHeader gridSortable ${sortBy === col.key ? "sorted" : ""}`}
|
||||
onClick={() => handleSort(col.key)}
|
||||
>
|
||||
{col.label}
|
||||
{sortKey === col.key && <span className="sortArrow">{sortDir === "asc" ? " ▲" : " ▼"}</span>}
|
||||
{sortBy === col.key && <span className="sortArrow">{sortDir === "asc" ? " ▲" : " ▼"}</span>}
|
||||
<span
|
||||
className="colResizeHandle"
|
||||
onMouseDown={(e) => { e.stopPropagation(); handleMouseDown(e, col.key); }}
|
||||
|
|
@ -256,7 +249,7 @@ const SharedTable = ({
|
|||
))}
|
||||
<div className="gridCell gridHeader">Actions</div>
|
||||
|
||||
{sortedRows.map((row, idx) => (
|
||||
{rows.map((row, idx) => (
|
||||
<React.Fragment key={row.id || idx}>
|
||||
<div className="gridCell gridBody">
|
||||
<input type="checkbox" />
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ export default function usePaginatedList(fetchFn, extraParams = {}) {
|
|||
const [totalCount, setTotalCount] = useState(0);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [err, setErr] = useState("");
|
||||
const [sortBy, setSortBy] = useState("lastUpdated");
|
||||
const [sortDir, setSortDir] = useState("desc");
|
||||
const debounceRef = useRef(null);
|
||||
|
||||
const extraKey = JSON.stringify(extraParams);
|
||||
|
|
@ -25,19 +27,19 @@ export default function usePaginatedList(fetchFn, extraParams = {}) {
|
|||
const reload = useCallback(() => {
|
||||
setLoading(true);
|
||||
setErr("");
|
||||
fetchFn({ search: debouncedSearch, page, pageSize, ...extraParams })
|
||||
fetchFn({ search: debouncedSearch, page, pageSize, sortBy, sortDir, ...extraParams })
|
||||
.then(({ rows, totalCount }) => {
|
||||
setRows(rows);
|
||||
setTotalCount(totalCount);
|
||||
})
|
||||
.catch((e) => setErr(e.message || "Failed to load"))
|
||||
.finally(() => setLoading(false));
|
||||
}, [fetchFn, debouncedSearch, page, pageSize, extraKey]);
|
||||
}, [fetchFn, debouncedSearch, page, pageSize, sortBy, sortDir, extraKey]);
|
||||
|
||||
useEffect(() => { reload(); }, [reload]);
|
||||
|
||||
return useMemo(() => ({
|
||||
rows, search, setSearch, page, setPage, pageSize, setPageSize,
|
||||
totalCount, loading, err, reload
|
||||
}), [rows, search, page, pageSize, totalCount, loading, err, reload]);
|
||||
totalCount, loading, err, reload, sortBy, setSortBy, sortDir, setSortDir
|
||||
}), [rows, search, page, pageSize, totalCount, loading, err, reload, sortBy, sortDir]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,16 +11,8 @@ import "./styles.css";
|
|||
export default function WorkOrdersList() {
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
rows,
|
||||
search,
|
||||
setSearch,
|
||||
page,
|
||||
setPage,
|
||||
pageSize,
|
||||
setPageSize,
|
||||
totalCount,
|
||||
loading,
|
||||
err
|
||||
rows, search, setSearch, page, setPage, pageSize, setPageSize,
|
||||
totalCount, loading, err, sortBy, setSortBy, sortDir, setSortDir
|
||||
} = usePaginatedList(fetchWorkOrdersPaged);
|
||||
|
||||
const [deleteModal, setDeleteModal] = useState({
|
||||
|
|
@ -124,6 +116,10 @@ export default function WorkOrdersList() {
|
|||
setPageSize={setPageSize}
|
||||
totalCount={totalCount}
|
||||
totalPages={totalPages}
|
||||
sortBy={sortBy}
|
||||
setSortBy={setSortBy}
|
||||
sortDir={sortDir}
|
||||
setSortDir={setSortDir}
|
||||
/>
|
||||
|
||||
<DeleteModal
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ function AssignDropdown({ value, rowId, onUpdate }) {
|
|||
);
|
||||
}
|
||||
|
||||
export default function Table({ loading, error, rows, onEdit, onDelete, page, setPage, pageSize, setPageSize, totalCount, totalPages, onRowUpdate }) {
|
||||
export default function Table({ loading, error, rows, onEdit, onDelete, page, setPage, pageSize, setPageSize, totalCount, totalPages, onRowUpdate, sortBy, setSortBy, sortDir, setSortDir }) {
|
||||
const [localRows, setLocalRows] = useState(rows);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -194,6 +194,6 @@ export default function Table({ loading, error, rows, onEdit, onDelete, page, se
|
|||
];
|
||||
|
||||
return (
|
||||
<SharedTable rows={localRows} columns={columns} onEdit={onEdit} onDelete={onDelete} page={page} setPage={setPage} pageSize={pageSize} setPageSize={setPageSize} totalCount={totalCount} loading={loading} error={error} />
|
||||
<SharedTable rows={localRows} columns={columns} onEdit={onEdit} onDelete={onDelete} page={page} setPage={setPage} pageSize={pageSize} setPageSize={setPageSize} totalCount={totalCount} loading={loading} error={error} sortBy={sortBy} setSortBy={setSortBy} sortDir={sortDir} setSortDir={setSortDir} />
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import { get } from "../../../apiUtil";
|
||||
import { API_URL } from "../../../constants";
|
||||
|
||||
export const fetchWorkOrdersPaged = async ({ search = "", page = 1, pageSize = 12 }) => {
|
||||
const res = await get(`${API_URL}/WorkOrder/GetWorkOrderList?search=${encodeURIComponent(search)}&page=${page}&pageSize=${pageSize}`);
|
||||
export const fetchWorkOrdersPaged = async ({ search = "", page = 1, pageSize = 12, sortBy = "", sortDir = "" }) => {
|
||||
const params = new URLSearchParams({ search, page, pageSize });
|
||||
if (sortBy) params.append("sortBy", sortBy);
|
||||
if (sortDir) params.append("sortDir", sortDir);
|
||||
const res = await get(`${API_URL}/WorkOrder/GetWorkOrderList?${params.toString()}`);
|
||||
const rows = (res?.data || res?.Data || []).map(x => ({
|
||||
id: x.id ?? x.Id,
|
||||
number: x.internalWONumber ?? x.number ?? x.workOrderNumber ?? "",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue