import { CarOutlined, SettingOutlined, SyncOutlined } from "@ant-design/icons"; import { Button, Card, Input, Space, Table, Typography } from "antd"; import axios from "axios"; import _ from "lodash"; import queryString from "query-string"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { Link, useLocation, useNavigate } from "react-router-dom"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; import { pageLimit } from "../../utils/config"; import { alphaSort, statusSort } from "../../utils/sorters"; import useLocalStorage from "../../utils/useLocalStorage"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component"; import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component"; import { selectDarkMode, selectIsPartsEntry } from "../../redux/application/application.selectors"; import * as Sentry from "@sentry/react"; import getPartsBasePath from "../../utils/getPartsBasePath.js"; import { toggleDarkMode } from "../../redux/application/application.actions.js"; import { FaMoon, FaSun } from "react-icons/fa"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, isPartsEntry: selectIsPartsEntry, darkMode: selectDarkMode }); const mapDispatchToProps = (dispatch) => ({ toggleDarkMode: () => dispatch(toggleDarkMode()) }); export function SimplifiedPartsJobsListComponent({ bodyshop, refetch, loading, jobs, total, isPartsEntry, darkMode, toggleDarkMode }) { const search = queryString.parse(useLocation().search); const [openSearchResults, setOpenSearchResults] = useState([]); const [searchLoading, setSearchLoading] = useState(false); const [filter, setFilter] = useLocalStorage("filter_jobs_all", null); const { page, sortcolumn, sortorder } = search; const history = useNavigate(); const basePath = getPartsBasePath(isPartsEntry); const { t } = useTranslation(); const handleDarkModeToggle = useCallback(() => { toggleDarkMode(); }, [toggleDarkMode]); const columns = [ { title: t("jobs.fields.ro_number"), dataIndex: "ro_number", key: "ro_number", sorter: search?.search ? (a, b) => parseInt((a.ro_number || "0").replace(/\D/g, "")) - parseInt((b.ro_number || "0").replace(/\D/g, "")) : true, sortOrder: sortcolumn === "ro_number" && sortorder, render: (text, record) => ( {record.ro_number || t("general.labels.na")} ) }, { title: t("jobs.fields.owner"), dataIndex: "ownr_ln", key: "ownr_ln", ellipsis: true, //sorter: true, // (a, b) => alphaSort(a.ownr_ln, b.ownr_ln), //sortOrder: sortcolumn === "ownr_ln" && sortorder, render: (text, record) => { return record.ownerid ? ( ) : ( ); } }, { title: t("jobs.fields.status"), dataIndex: "status", key: "status", ellipsis: true, sorter: search?.search ? (a, b) => statusSort(a.status, b.status, bodyshop.md_ro_statuses?.parts_active_statuses) : true, sortOrder: sortcolumn === "status" && sortorder, render: (text, record) => { return record.status || t("general.labels.na"); }, filteredValue: filter?.status || null, filters: bodyshop.md_ro_statuses?.parts_statuses.map((s) => { return { text: s, value: [s] }; }), onFilter: (value, record) => value.includes(record.status) }, { title: t("jobs.fields.vehicle"), dataIndex: "vehicle", key: "vehicle", ellipsis: true, render: (text, record) => { return record.vehicleid ? ( {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${record.v_model_desc || ""}`} ) : ( {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${record.v_model_desc || ""}`} ); } }, { title: t("vehicles.fields.plate_no"), dataIndex: "plate_no", key: "plate_no", ellipsis: true, sorter: search?.search ? (a, b) => alphaSort(a.plate_no, b.plate_no) : true, sortOrder: sortcolumn === "plate_no" && sortorder, render: (text, record) => { return record.plate_no ? record.plate_no : ""; } }, { title: t("jobs.fields.clm_no"), dataIndex: "clm_no", key: "clm_no", ellipsis: true, sorter: search?.search ? (a, b) => alphaSort(a.clm_no, b.clm_no) : true, sortOrder: sortcolumn === "clm_no" && sortorder, render: (text, record) => `${record.clm_no || ""}${record.po_number ? ` (PO: ${record.po_number})` : ""}` }, { title: t("jobs.fields.partsstatus"), dataIndex: "partsstatus", key: "partsstatus", render: (text, record) => }, { title: t("jobs.fields.comment"), dataIndex: "comment", key: "comment", ellipsis: true } ]; const handleTableChange = (pagination, filters, sorter) => { search.page = pagination.current; search.sortcolumn = sorter.column && sorter.column.key; search.sortorder = sorter.order; if (filters.status) { search.statusFilters = JSON.stringify(_.flattenDeep(filters.status)); } else { delete search.statusFilters; } setFilter(filters); history({ search: queryString.stringify(search) }); }; useEffect(() => { if (search.search && search.search.trim() !== "") { searchJobs(); } }, []); async function searchJobs(value) { try { setSearchLoading(true); const searchData = await axios.post("/search", { search: value || search.search, index: "jobs" }); setOpenSearchResults(searchData.data.hits.hits.map((s) => s._source)); } catch (error) { Sentry.captureMessage(`Error while fetching search results: ${error.message}`, "warning"); } finally { setSearchLoading(false); } } return ( )} { search.search = value; history({ search: queryString.stringify(search) }); searchJobs(value); }} loading={loading || searchLoading} enterButton /> } > ); } export default connect(mapStateToProps, mapDispatchToProps)(SimplifiedPartsJobsListComponent);