Merged in release/2025-09-26 (pull request #2562)
Release/2025 09 26 into master-AIO IO-3365, IO-3366, IO-3369
This commit is contained in:
@@ -233,9 +233,7 @@ export function App({
|
|||||||
path="/parts/*"
|
path="/parts/*"
|
||||||
element={
|
element={
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<SocketProvider bodyshop={bodyshop} navigate={navigate} currentUser={currentUser}>
|
<PrivateRoute isAuthorized={currentUser.authorized} />
|
||||||
<PrivateRoute isAuthorized={currentUser.authorized} />
|
|
||||||
</SocketProvider>
|
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -109,6 +109,13 @@ export function BillsListTableComponent({
|
|||||||
key: "vendorname",
|
key: "vendorname",
|
||||||
sorter: (a, b) => alphaSort(a.vendor.name, b.vendor.name),
|
sorter: (a, b) => alphaSort(a.vendor.name, b.vendor.name),
|
||||||
sortOrder: state.sortedInfo.columnKey === "vendorname" && state.sortedInfo.order,
|
sortOrder: state.sortedInfo.columnKey === "vendorname" && state.sortedInfo.order,
|
||||||
|
filters: bills
|
||||||
|
? [...new Set(bills.map((bill) => bill.vendor.name))].map((name) => ({
|
||||||
|
text: name,
|
||||||
|
value: name
|
||||||
|
}))
|
||||||
|
: [],
|
||||||
|
onFilter: (value, record) => record.vendor.name === value,
|
||||||
render: (text, record) => <span>{record.vendor.name}</span>
|
render: (text, record) => <span>{record.vendor.name}</span>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ export function GlobalFooter({ isPartsEntry }) {
|
|||||||
rome: t("titles.romeonline")
|
rome: t("titles.romeonline")
|
||||||
})} - ${import.meta.env.VITE_APP_GIT_SHA_DATE}`}
|
})} - ${import.meta.env.VITE_APP_GIT_SHA_DATE}`}
|
||||||
</div>
|
</div>
|
||||||
<WssStatusDisplayComponent />
|
|
||||||
<Link to="/disclaimer" target="_blank" style={{ color: "#ccc" }}>
|
<Link to="/disclaimer" target="_blank" style={{ color: "#ccc" }}>
|
||||||
Disclaimer & Notices
|
Disclaimer & Notices
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -10,9 +10,12 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
const mapDispatchToProps = () => ({
|
const mapDispatchToProps = () => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const DEFAULT_COL_LAYOUT = { xs: 24, sm: 24, md: 8, lg: 4, xl: 4, xxl: 4 };
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(JobPartsQueueCount);
|
export default connect(mapStateToProps, mapDispatchToProps)(JobPartsQueueCount);
|
||||||
|
|
||||||
export function JobPartsQueueCount({ bodyshop, parts, style }) {
|
export function JobPartsQueueCount({ bodyshop, parts, defaultColLayout = DEFAULT_COL_LAYOUT }) {
|
||||||
const partsStatus = useMemo(() => {
|
const partsStatus = useMemo(() => {
|
||||||
if (!parts) return null;
|
if (!parts) return null;
|
||||||
return parts.reduce(
|
return parts.reduce(
|
||||||
@@ -35,35 +38,34 @@ export function JobPartsQueueCount({ bodyshop, parts, style }) {
|
|||||||
}, [bodyshop, parts]);
|
}, [bodyshop, parts]);
|
||||||
|
|
||||||
if (!parts) return null;
|
if (!parts) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row style={style}>
|
<Row>
|
||||||
<Col span={4}>
|
<Col {...defaultColLayout}>
|
||||||
<Tooltip title="Total">
|
<Tooltip title="Total">
|
||||||
<Tag>{partsStatus.total}</Tag>
|
<Tag>{partsStatus.total}</Tag>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={4}>
|
<Col {...defaultColLayout}>
|
||||||
<Tooltip title="No Status">
|
<Tooltip title="No Status">
|
||||||
<Tag color="gold">{partsStatus["null"]}</Tag>
|
<Tag color="gold">{partsStatus["null"]}</Tag>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={4}>
|
<Col {...defaultColLayout}>
|
||||||
<Tooltip title={bodyshop.md_order_statuses.default_ordered}>
|
<Tooltip title={bodyshop.md_order_statuses.default_ordered}>
|
||||||
<Tag color="blue">{partsStatus[bodyshop.md_order_statuses.default_ordered]}</Tag>
|
<Tag color="blue">{partsStatus[bodyshop.md_order_statuses.default_ordered]}</Tag>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={4}>
|
<Col {...defaultColLayout}>
|
||||||
<Tooltip title={bodyshop.md_order_statuses.default_received}>
|
<Tooltip title={bodyshop.md_order_statuses.default_received}>
|
||||||
<Tag color="green">{partsStatus[bodyshop.md_order_statuses.default_received]}</Tag>
|
<Tag color="green">{partsStatus[bodyshop.md_order_statuses.default_received]}</Tag>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={4}>
|
<Col {...defaultColLayout}>
|
||||||
<Tooltip title={bodyshop.md_order_statuses.default_returned}>
|
<Tooltip title={bodyshop.md_order_statuses.default_returned}>
|
||||||
<Tag color="orange">{partsStatus[bodyshop.md_order_statuses.default_returned]}</Tag>
|
<Tag color="orange">{partsStatus[bodyshop.md_order_statuses.default_returned]}</Tag>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={4}>
|
<Col {...defaultColLayout}>
|
||||||
<Tooltip title={bodyshop.md_order_statuses.default_bo}>
|
<Tooltip title={bodyshop.md_order_statuses.default_bo}>
|
||||||
<Tag color="red">{partsStatus[bodyshop.md_order_statuses.default_bo]}</Tag>
|
<Tag color="red">{partsStatus[bodyshop.md_order_statuses.default_bo]}</Tag>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ export function PartsQueueListComponent({ bodyshop }) {
|
|||||||
title: t("jobs.fields.partsstatus"),
|
title: t("jobs.fields.partsstatus"),
|
||||||
dataIndex: "partsstatus",
|
dataIndex: "partsstatus",
|
||||||
key: "partsstatus",
|
key: "partsstatus",
|
||||||
render: (text, record) => <JobPartsQueueCount style={{ minWidth: "10rem" }} parts={record.joblines_status} />
|
render: (text, record) => <JobPartsQueueCount parts={record.joblines_status} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.comment"),
|
title: t("jobs.fields.comment"),
|
||||||
|
|||||||
@@ -1183,7 +1183,17 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
{fields.map((field, index) => (
|
{fields.map((field, index) => (
|
||||||
<Form.Item key={field.key}>
|
<Form.Item key={field.key}>
|
||||||
<LayoutFormRow noDivider>
|
<LayoutFormRow noDivider>
|
||||||
<Form.Item label={t("general.labels.label")} key={`${index}label`} name={[field.name, "label"]}>
|
<Form.Item
|
||||||
|
label={t("general.labels.label")}
|
||||||
|
key={`${index}label`}
|
||||||
|
name={[field.name, "label"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -1294,7 +1304,17 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
{fields.map((field, index) => (
|
{fields.map((field, index) => (
|
||||||
<Form.Item key={field.key}>
|
<Form.Item key={field.key}>
|
||||||
<LayoutFormRow noDivider>
|
<LayoutFormRow noDivider>
|
||||||
<Form.Item label={t("general.labels.label")} key={`${index}label`} name={[field.name, "label"]}>
|
<Form.Item
|
||||||
|
label={t("general.labels.label")}
|
||||||
|
key={`${index}label`}
|
||||||
|
name={[field.name, "label"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -1483,15 +1503,31 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
{fields.map((field, index) => (
|
{fields.map((field, index) => (
|
||||||
<Form.Item key={field.key}>
|
<Form.Item key={field.key}>
|
||||||
<LayoutFormRow noDivider>
|
<LayoutFormRow noDivider>
|
||||||
<Form.Item label={t("general.labels.label")} key={`${index}label`} name={[field.name, "label"]}>
|
<Form.Item
|
||||||
|
label={t("general.labels.label")}
|
||||||
|
key={`${index}label`}
|
||||||
|
name={[field.name, "label"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.labels.md_to_emails_emails")}
|
label={t("bodyshop.labels.md_to_emails_emails")}
|
||||||
key={`${index}emails`}
|
key={`${index}emails`}
|
||||||
name={[field.name, "emails"]}
|
name={[field.name, "emails"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Select mode="tags" tokenSeparators={[",", ";"]} />
|
<FormItemEmail email={form.getFieldValue([field.name, "emails"])} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Space>
|
<Space>
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ export function SimplifiedPartsJobsListComponent({
|
|||||||
title: t("jobs.fields.partsstatus"),
|
title: t("jobs.fields.partsstatus"),
|
||||||
dataIndex: "partsstatus",
|
dataIndex: "partsstatus",
|
||||||
key: "partsstatus",
|
key: "partsstatus",
|
||||||
render: (text, record) => <JobPartsQueueCount style={{ minWidth: "10rem" }} parts={record.joblines_status} />
|
render: (text, record) => <JobPartsQueueCount parts={record.joblines_status} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.comment"),
|
title: t("jobs.fields.comment"),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { EditFilled, SyncOutlined } from "@ant-design/icons";
|
import { EditFilled, SyncOutlined } from "@ant-design/icons";
|
||||||
import { Button, Card, Checkbox, Input, Space, Table, Typography } from "antd";
|
import { Button, Card, Checkbox, Input, Space, Table, Typography } from "antd";
|
||||||
|
import { useQuery } from "@apollo/client";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -16,6 +17,7 @@ import { TemplateList } from "../../utils/TemplateConstants";
|
|||||||
import { pageLimit } from "../../utils/config";
|
import { pageLimit } from "../../utils/config";
|
||||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import useLocalStorage from "../../utils/useLocalStorage";
|
import useLocalStorage from "../../utils/useLocalStorage";
|
||||||
|
import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries";
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" }))
|
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" }))
|
||||||
@@ -33,25 +35,22 @@ export function BillsListPage({ loading, data, refetch, total, setBillEnterConte
|
|||||||
});
|
});
|
||||||
const Templates = TemplateList("bill");
|
const Templates = TemplateList("bill");
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const { data: vendorsData } = useQuery(QUERY_ALL_VENDORS);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("bills.fields.vendorname"),
|
title: t("bills.fields.vendorname"),
|
||||||
dataIndex: "vendorname",
|
dataIndex: "vendorname",
|
||||||
key: "vendorname",
|
key: "vendorname",
|
||||||
// sortObject: (direction) => {
|
sorter: (a, b) => alphaSort(a.vendor.name, b.vendor.name),
|
||||||
// return {
|
sortObject: (order) => ({
|
||||||
// vendor: {
|
vendor: { name: order === "descend" ? "desc" : "asc" }
|
||||||
// name: direction
|
}),
|
||||||
// ? direction === "descend"
|
filters: (vendorsData?.vendors || []).map((v) => ({ text: v.name, value: v.id })),
|
||||||
// ? "desc"
|
filteredValue: state.filteredInfo.vendorname || null,
|
||||||
// : "asc"
|
onFilter: (value, record) => record.vendorid === value,
|
||||||
// : "desc",
|
sortOrder: state.sortedInfo.columnKey === "vendorname" && state.sortedInfo.order,
|
||||||
// },
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
// sorter: (a, b) => alphaSort(a.vendor.name, b.vendor.name),
|
|
||||||
// sortOrder:
|
|
||||||
// state.sortedInfo.columnKey === "vendorname" && state.sortedInfo.order,
|
|
||||||
render: (text, record) => <span>{record.vendor.name}</span>
|
render: (text, record) => <span>{record.vendor.name}</span>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -65,20 +64,11 @@ export function BillsListPage({ loading, data, refetch, total, setBillEnterConte
|
|||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
dataIndex: "ro_number",
|
dataIndex: "ro_number",
|
||||||
key: "ro_number",
|
key: "ro_number",
|
||||||
// sortObject: (direction) => {
|
sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
|
||||||
// return {
|
sortObject: (order) => ({
|
||||||
// job: {
|
job: { ro_number: order === "descend" ? "desc" : "asc" }
|
||||||
// ro_number: direction
|
}),
|
||||||
// ? direction === "descend"
|
sortOrder: state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
// ? "desc"
|
|
||||||
// : "asc"
|
|
||||||
// : "desc",
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
// sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
|
|
||||||
// sortOrder:
|
|
||||||
// state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
|
||||||
render: (text, record) => record.job && <Link to={`/manage/jobs/${record.job.id}`}>{record.job.ro_number}</Link>
|
render: (text, record) => record.job && <Link to={`/manage/jobs/${record.job.id}`}>{record.job.ro_number}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -175,7 +165,8 @@ export function BillsListPage({ loading, data, refetch, total, setBillEnterConte
|
|||||||
];
|
];
|
||||||
|
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
// Persist filters (including vendorname) and sorting
|
||||||
|
setState({ ...state, filteredInfo: { ...state.filteredInfo, ...filters }, sortedInfo: sorter });
|
||||||
search.page = pagination.current;
|
search.page = pagination.current;
|
||||||
if (sorter && sorter.column && sorter.column.sortObject) {
|
if (sorter && sorter.column && sorter.column.sortObject) {
|
||||||
search.searchObj = JSON.stringify(sorter.column.sortObject(sorter.order));
|
search.searchObj = JSON.stringify(sorter.column.sortObject(sorter.order));
|
||||||
|
|||||||
Reference in New Issue
Block a user