Files
bodyshop/client/src/components/vehicles-list/vehicles-list.component.jsx
Allan Carr a2389b1f26 IO-3710 Visual Board Vehicle Color
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-05-20 13:42:35 -07:00

129 lines
4.2 KiB
JavaScript

import { SyncOutlined } from "@ant-design/icons";
import { Button, Card, Input, Space, Typography } from "antd";
import queryString from "query-string";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link, useLocation, useNavigate } from "react-router-dom";
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
import { pageLimit } from "../../utils/config";
import { alphaSort } from "../../utils/sorters";
import ResponsiveTable from "../responsive-table/responsive-table.component";
export default function VehiclesListComponent({ loading, vehicles, total, refetch, basePath = "/manage" }) {
const search = queryString.parse(useLocation().search);
const { page, pageSize } = search;
const history = useNavigate();
const currentPage = Number.parseInt(page || "1", 10);
const parsedPageSize = Number.parseInt(pageSize || String(pageLimit), 10);
const currentPageSize = Number.isNaN(parsedPageSize) ? pageLimit : parsedPageSize;
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: { text: "" }
});
const { t } = useTranslation();
const columns = [
{
title: t("vehicles.fields.v_vin"),
dataIndex: "v_vin",
key: "v_vin",
sorter: (a, b) => alphaSort(a.v_vin, b.v_vin),
sortOrder: state.sortedInfo.columnKey === "v_vin" && state.sortedInfo.order,
render: (text, record) => (
<Link to={`${basePath}/vehicles/${record.id}`}>
<VehicleVinDisplay>{record.v_vin || "N/A"}</VehicleVinDisplay>
</Link>
)
},
{
title: t("vehicles.fields.description"),
dataIndex: "description",
key: "description",
render: (text, record) => {
return (
<span>{`${record.v_model_yr || ""} ${record.v_color || ""} ${record.v_make_desc || ""} ${record.v_model_desc || ""} `}</span>
);
}
},
{
title: t("vehicles.fields.plate_no"),
dataIndex: "plate_no",
key: "plate_no",
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
sortOrder: state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
render: (text, record) => {
return <span>{`${record.plate_st || ""} | ${record.plate_no || ""}`}</span>;
}
}
];
const handleTableChange = (pagination, filters, sorter) => {
const nextPageSize = pagination?.pageSize || currentPageSize;
const pageSizeChanged = nextPageSize !== currentPageSize;
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
const updatedSearch = {
...search,
pageSize: nextPageSize,
page: pageSizeChanged ? 1 : pagination.current,
sortcolumn: sorter.columnKey,
sortorder: sorter.order
};
history({ search: queryString.stringify(updatedSearch) });
};
return (
<Card
title={t("menus.header.vehicles")}
extra={
<Space wrap>
{search.search && (
<>
<Typography.Title level={4}>
{t("general.labels.searchresults", { search: search.search })}
</Typography.Title>
<Button
onClick={() => {
delete search.search;
history({ search: queryString.stringify(search) });
}}
>
{t("general.actions.clear")}
</Button>
</>
)}
<Button onClick={() => refetch()} icon={<SyncOutlined />} />
<Input.Search
placeholder={search.search || t("general.labels.search")}
onSearch={(value) => {
const updatedSearch = { ...search, search: value };
history({ search: queryString.stringify(updatedSearch) });
}}
enterButton
/>
</Space>
}
>
<ResponsiveTable
loading={loading}
pagination={{
placement: "top",
pageSize: currentPageSize,
current: currentPage,
showSizeChanger: true,
total: total
}}
columns={columns}
mobileColumnKeys={["v_vin", "description", "plate_no"]}
rowKey="id"
scroll={{ x: true }}
dataSource={vehicles}
onChange={handleTableChange}
/>
</Card>
);
}