shoc-frontend-new/src/pages/workorders/list/List.js
Adam Moussa ddbe1ae375 Update total count to reflect active filters
- Toolbar shows "X of Y" when status or assignee filter is active
- Pagination bar totalCount reflects filtered row count
- Extracted filter logic into filteredRows variable for reuse
2026-04-17 12:52:59 -04:00

144 lines
4.8 KiB
JavaScript

import { useState, useEffect } from "react";
import { useNavigate } from "react-router";
import { toast } from "react-toastify";
import usePaginatedList from "../../../hooks/usePaginatedList";
import { fetchWorkOrdersPaged } from "./api";
import { fetchUsers } from "../api";
import Table from "./Table";
import DeleteModal from "../../../components/DeleteModal";
import "./styles.css";
export default function WorkOrdersList() {
const navigate = useNavigate();
const {
rows, search, setSearch, page, setPage, pageSize, setPageSize,
totalCount, loading, err, sortBy, setSortBy, sortDir, setSortDir
} = usePaginatedList(fetchWorkOrdersPaged);
const [deleteModal, setDeleteModal] = useState({
isOpen: false,
item: null,
loading: false
});
const totalPages = Math.max(1, Math.ceil(totalCount / pageSize));
const handleDeleteClick = (item) => {
setDeleteModal({ isOpen: true, item, loading: false });
};
const handleDeleteConfirm = async () => {
setDeleteModal((prev) => ({ ...prev, loading: true }));
try {
await new Promise((resolve) => setTimeout(resolve, 500));
toast.success("Work Order deleted successfully!");
setDeleteModal({ isOpen: false, item: null, loading: false });
} catch {
toast.error("Failed to delete work order");
setDeleteModal((prev) => ({ ...prev, loading: false }));
}
};
const [statusFilter, setStatusFilter] = useState("");
const [assigneeFilter, setAssigneeFilter] = useState("");
const [users, setUsers] = useState([]);
const statuses = ["Open", "In Progress", "On Hold", "Done", "Cancelled"];
useEffect(() => {
fetchUsers().then(setUsers).catch(() => {});
}, []);
const filteredRows = rows.filter((r) => {
if (statusFilter && r.status !== statusFilter) return false;
if (assigneeFilter === "__unassigned" && r.assignedTo) return false;
if (assigneeFilter && assigneeFilter !== "__unassigned" && r.assignedTo !== assigneeFilter) return false;
return true;
});
const hasFilter = statusFilter || assigneeFilter;
return (
<>
<section className="listPage">
<div className="woListToolbar">
<div className="woListToolbarLeft">
<h2 className="woListTitle">Work Orders</h2>
<span className="woListCount">
{hasFilter ? `${filteredRows.length} of ${totalCount}` : `${totalCount} total`}
</span>
</div>
<div className="woListToolbarRight">
<input
className="woSearchInput"
value={search}
onChange={(e) => { setSearch(e.target.value); setPage(1); }}
placeholder="Search..."
/>
<button className="woNewBtn" onClick={() => navigate("/workorders/new")}>
+ New Work Order
</button>
</div>
</div>
<div className="woFilterBar">
<button
className={`woFilterChip ${statusFilter === "" ? "active" : ""}`}
onClick={() => setStatusFilter("")}
>
All
</button>
{statuses.map((s) => (
<button
key={s}
className={`woFilterChip ${statusFilter === s ? "active" : ""}`}
onClick={() => setStatusFilter(statusFilter === s ? "" : s)}
>
{s}
</button>
))}
<span className="woFilterDivider" />
<select
className="woFilterSelect"
value={assigneeFilter}
onChange={(e) => setAssigneeFilter(e.target.value)}
>
<option value="">All Assignees</option>
<option value="__unassigned">Unassigned</option>
{users.map((u) => (
<option key={u.id} value={u.name}>{u.name}</option>
))}
</select>
</div>
<Table
loading={loading}
error={err}
rows={filteredRows}
onEdit={(item) => navigate(`/workorders/${item.id}`)}
onDelete={handleDeleteClick}
page={page}
setPage={setPage}
pageSize={pageSize}
setPageSize={setPageSize}
totalCount={hasFilter ? filteredRows.length : totalCount}
totalPages={totalPages}
sortBy={sortBy}
setSortBy={setSortBy}
sortDir={sortDir}
setSortDir={setSortDir}
/>
<DeleteModal
isOpen={deleteModal.isOpen}
onClose={() =>
setDeleteModal({ isOpen: false, item: null, loading: false })
}
onConfirm={handleDeleteConfirm}
title="Delete Work Order"
message={`Are you sure you want to delete "${deleteModal.item?.title}"? This action cannot be undone.`}
loading={deleteModal.loading}
/>
</section>
</>
);
}