IO-304 IO-774 IO-812 IO-793

This commit is contained in:
Patrick Fic
2021-03-31 00:14:25 +00:00
110 changed files with 2768 additions and 1733 deletions

View File

@@ -2055,6 +2055,48 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>creditsnotreceived</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>creditsreceived</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>dedfromlbr</name> <name>dedfromlbr</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -2370,6 +2412,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>totalreturns</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children> </children>
</folder_node> </folder_node>
<folder_node> <folder_node>
@@ -8175,6 +8238,48 @@
<folder_node> <folder_node>
<name>labels</name> <name>labels</name>
<children> <children>
<concept_node>
<name>agreement</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>availablecars</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<folder_node> <folder_node>
<name>convertform</name> <name>convertform</name>
<children> <children>
@@ -8243,6 +8348,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>driverinformation</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>noteconvertedfrom</name> <name>noteconvertedfrom</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -8285,6 +8411,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>rates</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>vehicle</name> <name>vehicle</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -9207,6 +9354,48 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>status</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>usage</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>vehicle</name> <name>vehicle</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -13614,6 +13803,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>PAP</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>PAR</name> <name>PAR</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -24132,6 +24342,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>relatedjobs</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>updateowner</name> <name>updateowner</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -27625,6 +27856,174 @@
<folder_node> <folder_node>
<name>templates</name> <name>templates</name>
<children> <children>
<concept_node>
<name>hours_sold_detail_closed</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_detail_closed_source</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_detail_open</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_detail_open_source</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_summary_closed</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_summary_closed_source</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_summary_open</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_summary_open_source</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>payments_by_date_type</name> <name>payments_by_date_type</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -29159,6 +29558,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>timetickets</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children> </children>
</folder_node> </folder_node>
<folder_node> <folder_node>
@@ -31765,6 +32185,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>relatedjobs</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>updatevehicle</name> <name>updatevehicle</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -168,7 +168,6 @@ export default function AccountingPayablesTableComponent({ loading, bills }) {
); );
}} }}
dataSource={dataSource} dataSource={dataSource}
size="small"
pagination={{ position: "top", pageSize: 50 }} pagination={{ position: "top", pageSize: 50 }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"

View File

@@ -175,7 +175,6 @@ export default function AccountingPayablesTableComponent({
); );
}} }}
dataSource={dataSource} dataSource={dataSource}
size="small"
pagination={{ position: "top", pageSize: 50 }} pagination={{ position: "top", pageSize: 50 }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"

View File

@@ -189,7 +189,6 @@ export default function AccountingReceivablesTableComponent({ loading, jobs }) {
); );
}} }}
dataSource={dataSource} dataSource={dataSource}
size="small"
pagination={{ position: "top" }} pagination={{ position: "top" }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"

View File

@@ -8,7 +8,7 @@ import AuditTrailValuesComponent from "../audit-trail-values/audit-trail-values.
export default function AuditTrailListComponent({ loading, data }) { export default function AuditTrailListComponent({ loading, data }) {
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, sortedInfo: {},
filteredInfo: {} filteredInfo: {},
}); });
const { t } = useTranslation(); const { t } = useTranslation();
const columns = [ const columns = [
@@ -22,7 +22,7 @@ export default function AuditTrailListComponent({ loading, data }) {
), ),
sorter: (a, b) => a.created - b.created, sorter: (a, b) => a.created - b.created,
sortOrder: sortOrder:
state.sortedInfo.columnKey === "created" && state.sortedInfo.order state.sortedInfo.columnKey === "created" && state.sortedInfo.order,
}, },
{ {
title: t("audit.fields.operation"), title: t("audit.fields.operation"),
@@ -31,7 +31,7 @@ export default function AuditTrailListComponent({ loading, data }) {
width: "10%", width: "10%",
sorter: (a, b) => alphaSort(a.operation, b.operation), sorter: (a, b) => alphaSort(a.operation, b.operation),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "operation" && state.sortedInfo.order state.sortedInfo.columnKey === "operation" && state.sortedInfo.order,
}, },
{ {
title: t("audit.fields.values"), title: t("audit.fields.values"),
@@ -43,7 +43,7 @@ export default function AuditTrailListComponent({ loading, data }) {
oldV={record.old_val} oldV={record.old_val}
newV={record.new_val} newV={record.new_val}
/> />
) ),
}, },
{ {
title: t("audit.fields.useremail"), title: t("audit.fields.useremail"),
@@ -52,19 +52,19 @@ export default function AuditTrailListComponent({ loading, data }) {
width: "10%", width: "10%",
sorter: (a, b) => alphaSort(a.useremail, b.useremail), sorter: (a, b) => alphaSort(a.useremail, b.useremail),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "useremail" && state.sortedInfo.order state.sortedInfo.columnKey === "useremail" && state.sortedInfo.order,
} },
]; ];
const formItemLayout = { const formItemLayout = {
labelCol: { labelCol: {
xs: { span: 12 }, xs: { span: 12 },
sm: { span: 5 } sm: { span: 5 },
}, },
wrapperCol: { wrapperCol: {
xs: { span: 24 }, xs: { span: 24 },
sm: { span: 12 } sm: { span: 12 },
} },
}; };
const handleTableChange = (pagination, filters, sorter) => { const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
@@ -74,9 +74,8 @@ export default function AuditTrailListComponent({ loading, data }) {
<Table <Table
{...formItemLayout} {...formItemLayout}
loading={loading} loading={loading}
size="small"
pagination={{ position: "top", defaultPageSize: 25 }} pagination={{ position: "top", defaultPageSize: 25 }}
columns={columns.map(item => ({ ...item }))} columns={columns}
rowKey="id" rowKey="id"
dataSource={data} dataSource={data}
onChange={handleTableChange} onChange={handleTableChange}

View File

@@ -1,5 +1,5 @@
import { useMutation, useQuery } from "@apollo/client"; import { useMutation, useQuery } from "@apollo/client";
import { Button, Form, Popconfirm } from "antd"; import { Button, Form, PageHeader, Popconfirm } from "antd";
import moment from "moment"; import moment from "moment";
import queryString from "query-string"; import queryString from "query-string";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
@@ -112,27 +112,36 @@ export default function BillDetailEditcontainer() {
return ( return (
<LoadingSkeleton loading={loading}> <LoadingSkeleton loading={loading}>
<Popconfirm <PageHeader
visible={visible} title={
onConfirm={() => form.submit()} data &&
onCancel={() => setVisible(false)} `${data.bills_by_pk.invoice_number} - ${data.bills_by_pk.vendor.name}`
okButtonProps={{ loading: updateLoading }} }
title={t("bills.labels.editadjwarning")} extra={
> <Popconfirm
<Button visible={visible}
htmlType="submit" onConfirm={() => form.submit()}
disabled={exported} onCancel={() => setVisible(false)}
onClick={handleSave} okButtonProps={{ loading: updateLoading }}
loading={updateLoading} title={t("bills.labels.editadjwarning")}
type="primary" >
> <Button
{t("general.actions.save")} htmlType="submit"
</Button> disabled={exported}
</Popconfirm> onClick={handleSave}
loading={updateLoading}
type="primary"
>
{t("general.actions.save")}
</Button>
</Popconfirm>
}
/>
<Form <Form
form={form} form={form}
onFinish={handleFinish} onFinish={handleFinish}
initialValues={transformData(data)} initialValues={transformData(data)}
layout="vertical"
> >
<BillFormContainer form={form} billEdit disabled={exported} /> <BillFormContainer form={form} billEdit disabled={exported} />
<JobDocumentsGallery <JobDocumentsGallery

View File

@@ -1,12 +1,15 @@
import { SyncOutlined } from "@ant-design/icons"; import { EyeFilled, SyncOutlined } from "@ant-design/icons";
import { import {
Button, Button,
Card,
Checkbox, Checkbox,
Descriptions, Descriptions,
Drawer,
Grid,
Input, Input,
PageHeader,
Space, Space,
Table, Table,
Typography,
} from "antd"; } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React, { useState } from "react"; import React, { useState } from "react";
@@ -45,7 +48,21 @@ export function BillsListTableComponent({
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [selectedBillLinesByBill, setSelectedBillLinesByBill] = useState({}); const [selectedBillLinesByBill, setSelectedBillLinesByBill] = useState({});
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
.slice(-1)[0];
const bpoints = {
xs: "100%",
sm: "100%",
md: "100%",
lg: "75%",
xl: "75%",
xxl: "65%",
};
const drawerPercentage = selectedBreakpoint
? bpoints[selectedBreakpoint[0]]
: "100%";
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, sortedInfo: {},
}); });
@@ -54,6 +71,34 @@ export function BillsListTableComponent({
const Templates = TemplateList("bill"); const Templates = TemplateList("bill");
const bills = billsQuery.data ? billsQuery.data.bills : []; const bills = billsQuery.data ? billsQuery.data.bills : [];
const { refetch } = billsQuery; const { refetch } = billsQuery;
const recordActions = (record, showView = false) => (
<Space wrap>
{showView && (
<Button onClick={() => handleOnRowClick(record)}>
<EyeFilled />
</Button>
)}
{record.exported ? (
<Button disabled>{t("bills.actions.edit")}</Button>
) : (
<Link
to={`/manage/bills?billid=${record.id}&vendorid=${record.vendorid}`}
>
<Button>{t("bills.actions.edit")}</Button>
</Link>
)}
<BillDeleteButton bill={record} />
{record.isinhouse && (
<PrintWrapperComponent
templateObject={{
name: Templates.inhouse_invoice.key,
variables: { id: record.id },
}}
messageObject={{ subject: Templates.inhouse_invoice.subject }}
/>
)}
</Space>
);
const columns = [ const columns = [
{ {
title: t("bills.fields.vendorname"), title: t("bills.fields.vendorname"),
@@ -116,31 +161,10 @@ export function BillsListTableComponent({
title: t("general.labels.actions"), title: t("general.labels.actions"),
dataIndex: "actions", dataIndex: "actions",
key: "actions", key: "actions",
render: (text, record) => ( render: (text, record) => recordActions(record, true),
<Space wrap>
{record.exported ? (
<Button disabled>{t("bills.actions.edit")}</Button>
) : (
<Link
to={`/manage/bills?billid=${record.id}&vendorid=${record.vendorid}`}
>
<Button>{t("bills.actions.edit")}</Button>
</Link>
)}
<BillDeleteButton bill={record} />
{record.isinhouse && (
<PrintWrapperComponent
templateObject={{
name: Templates.inhouse_invoice.key,
variables: { id: record.id },
}}
messageObject={{ subject: Templates.inhouse_invoice.subject }}
/>
)}
</Space>
),
}, },
]; ];
const selectedBillRecord = bills.find((r) => r.id === selectedBill);
const handleTableChange = (pagination, filters, sorter) => { const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
@@ -240,7 +264,6 @@ export function BillsListTableComponent({
), ),
}, },
]; ];
const handleOnBillrowclick = (selectedRows) => { const handleOnBillrowclick = (selectedRows) => {
setSelectedBillLinesByBill({ setSelectedBillLinesByBill({
...selectedBillLinesByBill, ...selectedBillLinesByBill,
@@ -249,10 +272,14 @@ export function BillsListTableComponent({
}; };
return ( return (
<div> <>
<Typography.Title level={3}>{`${t("bills.fields.invoice_number")} ${ <PageHeader
record.invoice_number title={
}`}</Typography.Title> record &&
`${t("bills.fields.invoice_number")} ${record.invoice_number}`
}
extra={recordActions(record)}
/>
<Descriptions> <Descriptions>
<Descriptions.Item label={t("bills.fields.federal_tax_rate")}> <Descriptions.Item label={t("bills.fields.federal_tax_rate")}>
{`${record.federal_tax_rate}%` || ""} {`${record.federal_tax_rate}%` || ""}
@@ -301,9 +328,7 @@ export function BillsListTableComponent({
{t("bills.actions.return")} {t("bills.actions.return")}
</Button> </Button>
<Table <Table
size="small"
scroll={{ x: "50%", y: "40rem" }} scroll={{ x: "50%", y: "40rem" }}
pagination={{ position: "top", defaultPageSize: 25 }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={record.billlines} dataSource={record.billlines}
@@ -318,94 +343,77 @@ export function BillsListTableComponent({
type: "checkbox", type: "checkbox",
}} }}
/> />
</div> </>
); );
}; };
return ( return (
<div> <Card
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title> title={t("bills.labels.bills")}
extra={
<Space wrap>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
{job ? (
<>
<Button
onClick={() => {
setBillEnterContext({
actions: { refetch: billsQuery.refetch },
context: {
job,
},
});
}}
>
{t("jobs.actions.postbills")}
</Button>
<Button
onClick={() => {
setReconciliationContext({
actions: { refetch: billsQuery.refetch },
context: {
job,
bills: (billsQuery.data && billsQuery.data.bills) || [],
},
});
}}
>
{t("jobs.actions.reconcile")}
</Button>
</>
) : null}
<Input.Search
placeholder={t("general.labels.search")}
onChange={(e) => {
e.preventDefault();
}}
/>
</Space>
}
>
<Drawer
placement="right"
onClose={() => handleOnRowClick(null)}
visible={selectedBill}
//getContainer={false}
style={{ position: "absolute" }}
closable
width={drawerPercentage}
>
{selectedBillRecord && rowExpander(selectedBillRecord)}
</Drawer>
<Table <Table
loading={billsQuery.loading} loading={billsQuery.loading}
size="small" scroll={{ x: true, y: "50rem" }}
title={() => (
<div className="imex-table-header">
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
{job ? (
<div>
<Button
onClick={() => {
setBillEnterContext({
actions: { refetch: billsQuery.refetch },
context: {
job,
},
});
}}
>
{t("jobs.actions.postbills")}
</Button>
<Button
onClick={() => {
setReconciliationContext({
actions: { refetch: billsQuery.refetch },
context: {
job,
bills: (billsQuery.data && billsQuery.data.bills) || [],
},
});
}}
>
{t("jobs.actions.reconcile")}
</Button>
</div>
) : null}
<div className="imex-table-header__search">
<Input.Search
placeholder={t("general.labels.search")}
onChange={(e) => {
e.preventDefault();
}}
/>
</div>
</div>
)}
scroll={{ x: "50%", y: "40rem" }}
expandedRowRender={rowExpander}
pagination={{ position: "top", defaultPageSize: 25 }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={bills} dataSource={bills}
onChange={handleTableChange} onChange={handleTableChange}
expandable={{
expandedRowKeys: [selectedBill],
onExpand: (expanded, record) => {
handleOnRowClick(expanded ? record : null);
},
}}
rowSelection={{
onSelect: (record) => {
handleOnRowClick(record);
},
selectedRowKeys: [selectedBill],
type: "radio",
}}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
handleOnRowClick(record);
}, // click row
onDoubleClick: (event) => {}, // double click row
onContextMenu: (event) => {}, // right button click row
onMouseEnter: (event) => {}, // mouse enter row
onMouseLeave: (event) => {}, // mouse leave row
};
}}
/> />
</div> </Card>
); );
} }
export default connect( export default connect(

View File

@@ -95,7 +95,6 @@ export default function BillsVendorsList() {
); );
}} }}
dataSource={dataSource} dataSource={dataSource}
size="small"
pagination={{ position: "top" }} pagination={{ position: "top" }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"

View File

@@ -29,7 +29,6 @@ export function ChatConversationListComponent({
<div className="chat-list-container"> <div className="chat-list-container">
<List <List
bordered bordered
size="small"
dataSource={conversationList} dataSource={conversationList}
renderItem={(item) => ( renderItem={(item) => (
<List.Item <List.Item

View File

@@ -77,10 +77,7 @@ export function ChatMediaSelector({
visible={visible} visible={visible}
onVisibleChange={handleVisibleChange} onVisibleChange={handleVisibleChange}
> >
<Badge <Badge count={selectedMedia.filter((s) => s.isSelected).length}>
size="small"
count={selectedMedia.filter((s) => s.isSelected).length}
>
<PictureFilled style={{ margin: "0 .5rem" }} /> <PictureFilled style={{ margin: "0 .5rem" }} />
</Badge> </Badge>
</Popover> </Popover>

View File

@@ -1,4 +1,4 @@
import { Input, Table } from "antd"; import { Card, Input, Table } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
@@ -97,33 +97,36 @@ export default function ContractsCarsComponent({
); );
return ( return (
<Table <Card
loading={loading} title={t("contracts.labels.availablecars")}
title={() => ( extra={
<Input.Search <Input.Search
placeholder={t("general.labels.search")} placeholder={t("general.labels.search")}
value={state.search} value={state.search}
onChange={(e) => setState({ ...state, search: e.target.value })} onChange={(e) => setState({ ...state, search: e.target.value })}
/> />
)} }
size="small" >
pagination={{ position: "top" }} <Table
columns={columns.map((item) => ({ ...item }))} loading={loading}
rowKey="id" pagination={{ position: "top" }}
dataSource={filteredData} columns={columns}
onChange={handleTableChange} rowKey="id"
rowSelection={{ dataSource={filteredData}
onSelect: handleSelect, onChange={handleTableChange}
type: "radio", rowSelection={{
selectedRowKeys: [selectedCar], onSelect: handleSelect,
}} type: "radio",
onRow={(record, rowIndex) => { selectedRowKeys: [selectedCar],
return { }}
onClick: (event) => { onRow={(record, rowIndex) => {
handleSelect(record); return {
}, onClick: (event) => {
}; handleSelect(record);
}} },
/> };
}}
/>
</Card>
); );
} }

View File

@@ -15,10 +15,6 @@ export default function ContractCarsContainer({ selectedCarState, form }) {
const handleSelect = (record) => { const handleSelect = (record) => {
setSelectedCar(record.id); setSelectedCar(record.id);
console.log(
"🚀 ~ file: contract-cars.container.jsx ~ line 19 ~ record",
record
);
form.setFieldsValue({ form.setFieldsValue({
kmstart: record.mileage, kmstart: record.mileage,
dailyrate: record.dailycost, dailyrate: record.dailycost,
@@ -28,13 +24,11 @@ export default function ContractCarsContainer({ selectedCarState, form }) {
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent message={error.message} type="error" />;
return ( return (
<div> <ContractCarsComponent
<ContractCarsComponent handleSelect={handleSelect}
handleSelect={handleSelect} selectedCar={selectedCar}
selectedCar={selectedCar} loading={loading}
loading={loading} data={data ? data.courtesycars : []}
data={data ? data.courtesycars : []} />
/>
</div>
); );
} }

View File

@@ -1,25 +1,31 @@
import { Card } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Descriptions, Card } from "antd";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import DataLabel from "../data-label/data-label.component";
export default function ContractCourtesyCarBlock({ courtesyCar }) { export default function ContractCourtesyCarBlock({ courtesyCar }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Link to={`/manage/courtesycars/${courtesyCar && courtesyCar.id}`}> <Link to={`/manage/courtesycars/${courtesyCar && courtesyCar.id}`}>
<Card title={t("courtesycars.labels.courtesycar")}> <Card
<Descriptions size="small" column={1}> className="ant-card-grid-hoverable"
<Descriptions.Item label={t("courtesycars.fields.fleetnumber")}> style={{ height: "100%" }}
title={t("courtesycars.labels.courtesycar")}
>
<div>
<DataLabel label={t("courtesycars.fields.fleetnumber")}>
{(courtesyCar && courtesyCar.fleetnumber) || ""} {(courtesyCar && courtesyCar.fleetnumber) || ""}
</Descriptions.Item> </DataLabel>
<Descriptions.Item label={t("courtesycars.fields.plate")}> <DataLabel label={t("courtesycars.fields.plate")}>
{(courtesyCar && courtesyCar.plate) || ""} {(courtesyCar && courtesyCar.plate) || ""}
</Descriptions.Item> </DataLabel>
<Descriptions.Item label={t("courtesycars.labels.vehicle")}> <DataLabel label={t("courtesycars.labels.vehicle")}>
{`${(courtesyCar && courtesyCar.year) || ""} ${(courtesyCar && {`${(courtesyCar && courtesyCar.year) || ""} ${
courtesyCar.make) || (courtesyCar && courtesyCar.make) || ""
""} ${(courtesyCar && courtesyCar.model) || ""}`} } ${(courtesyCar && courtesyCar.model) || ""}`}
</Descriptions.Item> </DataLabel>
</Descriptions> </div>
</Card> </Card>
</Link> </Link>
); );

View File

@@ -12,6 +12,7 @@ import InputPhone, {
} from "../form-items-formatted/phone-form-item.component"; } from "../form-items-formatted/phone-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import ContractFormJobPrefill from "./contract-form-job-prefill.component"; import ContractFormJobPrefill from "./contract-form-job-prefill.component";
export default function ContractFormComponent({ export default function ContractFormComponent({
form, form,
create = false, create = false,
@@ -83,10 +84,11 @@ export default function ContractFormComponent({
</Form.Item> </Form.Item>
)} )}
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow> <LayoutFormRow>
<Form.Item <Form.Item
label={t("contracts.fields.fuelout")} label={t("contracts.fields.fuelout")}
name="fuelout" name="fuelout"
span={8}
rules={[ rules={[
{ {
required: true, required: true,
@@ -97,23 +99,29 @@ export default function ContractFormComponent({
<CourtesyCarFuelSlider /> <CourtesyCarFuelSlider />
</Form.Item> </Form.Item>
{create ? null : ( {create ? null : (
<Form.Item label={t("contracts.fields.fuelin")} name="fuelin"> <Form.Item
label={t("contracts.fields.fuelin")}
name="fuelin"
span={8}
>
<CourtesyCarFuelSlider /> <CourtesyCarFuelSlider />
</Form.Item> </Form.Item>
)} )}
</LayoutFormRow> </LayoutFormRow>
<Space wrap> <div>
{selectedJobState && ( <Space wrap>
<div> {selectedJobState && (
<ContractFormJobPrefill <div>
jobId={selectedJobState && selectedJobState[0]} <ContractFormJobPrefill
form={form} jobId={selectedJobState && selectedJobState[0]}
/> form={form}
</div> />
)} </div>
<ContractLicenseDecodeButton form={form} /> )}
</Space> <ContractLicenseDecodeButton form={form} />
<LayoutFormRow> </Space>
</div>
<LayoutFormRow header={t("contracts.labels.driverinformation")}>
<Form.Item <Form.Item
label={t("contracts.fields.driver_dlnumber")} label={t("contracts.fields.driver_dlnumber")}
name="driver_dlnumber" name="driver_dlnumber"
@@ -222,7 +230,7 @@ export default function ContractFormComponent({
<FormDatePicker /> <FormDatePicker />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow> <LayoutFormRow header={t("contracts.labels.rates")}>
<Form.Item label={t("contracts.fields.dailyrate")} name="dailyrate"> <Form.Item label={t("contracts.fields.dailyrate")} name="dailyrate">
<InputNumber precision={2} /> <InputNumber precision={2} />
</Form.Item> </Form.Item>

View File

@@ -1,26 +1,33 @@
import { Card } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Descriptions, Card } from "antd";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import DataLabel from "../data-label/data-label.component";
export default function ContractJobBlock({ job }) { export default function ContractJobBlock({ job }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Link to={`/manage/jobs/${job && job.id}`}> <Link to={`/manage/jobs/${job && job.id}`}>
<Card title={t("jobs.labels.job")}> <Card
<Descriptions size="small" column={1}> className="ant-card-grid-hoverable"
<Descriptions.Item label={t("jobs.fields.ro_number")}> style={{ height: "100%" }}
title={t("jobs.labels.job")}
>
<div>
<DataLabel label={t("jobs.fields.ro_number")}>
{(job && job.ro_number) || ""} {(job && job.ro_number) || ""}
</Descriptions.Item> </DataLabel>
<Descriptions.Item label={t("jobs.fields.vehicle")}> <DataLabel label={t("jobs.fields.vehicle")}>
{`${(job && job.v_model_yr) || ""} ${(job && job.v_make_desc) || {`${(job && job.v_model_yr) || ""} ${
""} ${(job && job.v_model_desc) || ""}`} (job && job.v_make_desc) || ""
</Descriptions.Item> } ${(job && job.v_model_desc) || ""}`}
<Descriptions.Item label={t("jobs.fields.owner")}> </DataLabel>
{`${(job && job.ownr_fn) || ""} ${(job && job.ownr_ln) || <DataLabel label={t("jobs.fields.owner")}>
""} ${(job && job.ownr_co_nm) || ""}`} {`${(job && job.ownr_fn) || ""} ${(job && job.ownr_ln) || ""} ${
</Descriptions.Item> (job && job.ownr_co_nm) || ""
</Descriptions> }`}
</DataLabel>
</div>
</Card> </Card>
</Link> </Link>
); );

View File

@@ -1,4 +1,4 @@
import { Input, Table } from "antd"; import { Card, Input, Table } from "antd";
import React, { useMemo, useState } from "react"; import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
@@ -162,45 +162,48 @@ export default function ContractsJobsComponent({
const defaultCurrent = useMemo(() => { const defaultCurrent = useMemo(() => {
const page = const page =
Math.floor( Math.floor(
(filteredData.findIndex((v) => v.id === selectedJob) || 0) / 3 (filteredData.findIndex((v) => v.id === selectedJob) || 0) / 10
) + 1; ) + 1;
if (page === 0) return 1;
return page; return page;
}, [filteredData, selectedJob]); }, [filteredData, selectedJob]);
if (loading) return <LoadingSkeleton />; if (loading) return <LoadingSkeleton />;
return ( return (
<Table <Card
loading={loading} title={t("jobs.labels.availablejobs")}
title={() => ( extra={
<Input.Search <Input.Search
placeholder={t("general.labels.search")} placeholder={t("general.labels.search")}
value={state.search} value={state.search}
onChange={(e) => setState({ ...state, search: e.target.value })} onChange={(e) => setState({ ...state, search: e.target.value })}
/> />
)} }
size="small" >
pagination={{ <Table
position: "top", loading={loading}
defaultPageSize: 3, pagination={{
defaultCurrent: defaultCurrent, position: "top",
}} defaultPageSize: 10,
columns={columns} defaultCurrent: defaultCurrent,
rowKey="id" }}
dataSource={filteredData} columns={columns}
onChange={handleTableChange} rowKey="id"
rowSelection={{ dataSource={filteredData}
onSelect: handleSelect, onChange={handleTableChange}
type: "radio", rowSelection={{
selectedRowKeys: [selectedJob], onSelect: handleSelect,
}} type: "radio",
onRow={(record, rowIndex) => { selectedRowKeys: [selectedJob],
return { }}
onClick: (event) => { onRow={(record, rowIndex) => {
handleSelect(record); return {
}, onClick: (event) => {
}; handleSelect(record);
}} },
/> };
}}
/>
</Card>
); );
} }

View File

@@ -25,14 +25,12 @@ export function ContractJobsContainer({ selectedJobState, bodyshop }) {
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent message={error.message} type="error" />;
return ( return (
<div> <ContractJobsComponent
<ContractJobsComponent handleSelect={handleSelect}
handleSelect={handleSelect} selectedJob={selectedJob}
selectedJob={selectedJob} loading={loading}
loading={loading} data={data ? data.jobs : []}
data={data ? data.jobs : []} />
/>
</div>
); );
} }
export default connect(mapStateToProps, null)(ContractJobsContainer); export default connect(mapStateToProps, null)(ContractJobsContainer);

View File

@@ -1,5 +1,5 @@
import { SyncOutlined } from "@ant-design/icons"; import { SyncOutlined } from "@ant-design/icons";
import { Button, Input, Table } from "antd"; import { Button, Card, Input, Space, Table } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -123,39 +123,39 @@ export default function ContractsList({ loading, contracts, refetch, total }) {
}; };
return ( return (
<Table <Card
loading={loading} extra={
title={() => ( <Space wrap>
<div className="imex-table-header">
<Button onClick={() => refetch()}> <Button onClick={() => refetch()}>
<SyncOutlined /> <SyncOutlined />
</Button> </Button>
<div>
<TimeTicketsDatesSelector /> <TimeTicketsDatesSelector />
</div>
<div className="imex-table-header__search"> <Input.Search
<Input.Search placeholder={t("general.labels.search")}
placeholder={t("general.labels.search")} onSearch={(value) => {
onSearch={(value) => { search.search = value;
search.search = value; history.push({ search: queryString.stringify(search) });
history.push({ search: queryString.stringify(search) }); }}
}} />
/> </Space>
</div> }
</div> >
)} <Table
size="small" loading={loading}
scroll={{ x: "50%", y: "40rem" }} scroll={{ x: "50%", y: "40rem" }}
pagination={{ pagination={{
position: "top", position: "top",
pageSize: 25, pageSize: 25,
current: parseInt(page || 1), current: parseInt(page || 1),
total: total, total: total,
}} }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={contracts} dataSource={contracts}
onChange={handleTableChange} onChange={handleTableChange}
/> />
</Card>
); );
} }

View File

@@ -1,4 +1,4 @@
import { Table } from "antd"; import { Card, Table } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -84,19 +84,20 @@ export default function CourtesyCarContractListComponent({
}; };
return ( return (
<Table <Card title={t("menus.header.courtesycars-contracts")}>
size="small" <Table
scroll={{ x: true }} scroll={{ x: true }}
pagination={{ pagination={{
position: "top", position: "top",
pageSize: 25, pageSize: 25,
current: parseInt(page || 1), current: parseInt(page || 1),
total: totalContracts, total: totalContracts,
}} }}
columns={columns.map((item) => ({ ...item }))} columns={columns}
rowKey="id" rowKey="id"
dataSource={contracts} dataSource={contracts}
onChange={handleTableChange} onChange={handleTableChange}
/> />
</Card>
); );
} }

View File

@@ -1,4 +1,4 @@
import { Button, Form, Input, InputNumber } from "antd"; import { Button, Form, Input, InputNumber, PageHeader } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component"; import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
@@ -13,15 +13,21 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div> <div>
<Button <PageHeader
type="primary" title={t("menus.header.courtesycars")}
loading={saveLoading} extra={
onClick={() => form.submit()} <Button
> type="primary"
{t("general.actions.save")} loading={saveLoading}
</Button> onClick={() => form.submit()}
>
{t("general.actions.save")}
</Button>
}
/>
<FormFieldsChanged form={form} /> <FormFieldsChanged form={form} />
<LayoutFormRow> <LayoutFormRow header={t("courtesycars.labels.vehicle")}>
<Form.Item <Form.Item
label={t("courtesycars.fields.make")} label={t("courtesycars.fields.make")}
name="make" name="make"
@@ -95,7 +101,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
<Input /> <Input />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow> <LayoutFormRow header={t("courtesycars.labels.usage")}>
<Form.Item <Form.Item
label={t("courtesycars.fields.mileage")} label={t("courtesycars.fields.mileage")}
name="mileage" name="mileage"
@@ -139,19 +145,21 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
<FormDatePicker /> <FormDatePicker />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<Form.Item
label={t("courtesycars.fields.fuel")} <LayoutFormRow header={t("courtesycars.labels.status")}>
name="fuel" <Form.Item
rules={[ span={24}
{ label={t("courtesycars.fields.fuel")}
required: true, name="fuel"
message: t("general.validation.required"), rules={[
}, {
]} required: true,
> message: t("general.validation.required"),
<CourtesyCarFuelSlider /> },
</Form.Item> ]}
<LayoutFormRow> >
<CourtesyCarFuelSlider />
</Form.Item>
<Form.Item <Form.Item
label={t("courtesycars.fields.status")} label={t("courtesycars.fields.status")}
name="status" name="status"

View File

@@ -27,6 +27,14 @@ const CourtesyCarFuelComponent = (props, ref) => {
}, },
}; };
return <Slider ref={ref} marks={marks} step={null} {...props} />; return (
<Slider
ref={ref}
marks={marks}
step={null}
style={{ marginLeft: "2rem", marginRight: "2rem" }}
{...props}
/>
);
}; };
export default forwardRef(CourtesyCarFuelComponent); export default forwardRef(CourtesyCarFuelComponent);

View File

@@ -1,4 +1,4 @@
import { Table, Button, Input } from "antd"; import { Table, Button, Input, Card, Space } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
@@ -118,10 +118,10 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
) )
: courtesycars; : courtesycars;
return ( return (
<Table <Card
loading={loading} title={t("menus.header.courtesycars")}
title={() => ( extra={
<div className="imex-table-header"> <Space wrap>
<Button onClick={() => refetch()}> <Button onClick={() => refetch()}>
<SyncOutlined /> <SyncOutlined />
</Button> </Button>
@@ -137,14 +137,17 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
value={searchText} value={searchText}
enterButton enterButton
/> />
</div> </Space>
)} }
size="small" >
pagination={{ position: "top" }} <Table
columns={columns.map((item) => ({ ...item }))} loading={loading}
rowKey="id" pagination={{ position: "top" }}
dataSource={tableData} columns={columns}
onChange={handleTableChange} rowKey="id"
/> dataSource={tableData}
onChange={handleTableChange}
/>
</Card>
); );
} }

View File

@@ -118,7 +118,6 @@ export default function CsiResponseListPaginated({
</div> </div>
); );
}} }}
size="small"
pagination={{ pagination={{
position: "top", position: "top",
pageSize: 25, pageSize: 25,

View File

@@ -14,7 +14,7 @@ import Icon, {
UnorderedListOutlined, UnorderedListOutlined,
UserOutlined, UserOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Menu } from "antd"; import { Layout, Menu } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { BsKanban } from "react-icons/bs"; import { BsKanban } from "react-icons/bs";
@@ -67,10 +67,11 @@ function Header({
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div style={{ display: "flex", alignContent: "center" }}> <Layout.Header>
<Menu <Menu
mode="horizontal" mode="horizontal"
theme="light" //theme="light"
theme={"dark"}
style={{ flex: 5 }} style={{ flex: 5 }}
selectedKeys={[selectedHeader]} selectedKeys={[selectedHeader]}
onClick={handleMenuClick} onClick={handleMenuClick}
@@ -351,7 +352,7 @@ function Header({
))} ))}
</Menu.SubMenu> </Menu.SubMenu>
</Menu> </Menu>
</div> </Layout.Header>
); );
} }

View File

@@ -1,4 +1,4 @@
import { Statistic } from "antd"; import { Card, Space, Statistic } from "antd";
import Dinero from "dinero.js"; import Dinero from "dinero.js";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -6,7 +6,12 @@ import AlertComponent from "../alert/alert.component";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import "./job-bills-total.styles.scss"; import "./job-bills-total.styles.scss";
export default function JobBillsTotalComponent({ loading, bills, jobTotals }) { export default function JobBillsTotalComponent({
loading,
bills,
partsOrders,
jobTotals,
}) {
const { t } = useTranslation(); const { t } = useTranslation();
if (loading) return <LoadingSkeleton />; if (loading) return <LoadingSkeleton />;
@@ -20,6 +25,19 @@ export default function JobBillsTotalComponent({ loading, bills, jobTotals }) {
let billTotals = Dinero(); let billTotals = Dinero();
let billCms = Dinero(); let billCms = Dinero();
let lbrAdjustments = Dinero(); let lbrAdjustments = Dinero();
let totalReturns = Dinero();
partsOrders.forEach((p) =>
p.parts_order_lines.forEach((pol) => {
if (p.return) {
totalReturns = totalReturns.add(
Dinero({
amount: Math.round((pol.cost || 0) * 100),
}).multiply(pol.quantity)
);
}
})
);
bills.forEach((i) => bills.forEach((i) =>
i.billlines.forEach((il) => { i.billlines.forEach((il) => {
@@ -50,50 +68,69 @@ export default function JobBillsTotalComponent({ loading, bills, jobTotals }) {
const totalPartsSublet = Dinero(totals.parts.parts.total).add( const totalPartsSublet = Dinero(totals.parts.parts.total).add(
Dinero(totals.parts.sublets.total) Dinero(totals.parts.sublets.total)
); );
const discrepancy = totalPartsSublet.subtract(billTotals); const discrepancy = totalPartsSublet.subtract(billTotals);
const discrepWithLbrAdj = discrepancy.add(lbrAdjustments); const discrepWithLbrAdj = discrepancy.add(lbrAdjustments);
const discrepWithCms = discrepWithLbrAdj.subtract(billCms); const discrepWithCms = discrepWithLbrAdj.subtract(billCms);
const creditsNotReceived = totalReturns.subtract(billCms);
return ( return (
<div className="job-bills-totals-container"> <Card title={t("jobs.labels.jobtotals")}>
<Statistic <Space wrap size="large">
title={t("jobs.labels.rosaletotal")} <Statistic
value={totalPartsSublet.toFormat()} title={t("jobs.labels.rosaletotal")}
/> value={totalPartsSublet.toFormat()}
<Statistic />
title={t("bills.labels.retailtotal")} <Statistic
value={billTotals.toFormat()} title={t("bills.labels.retailtotal")}
/> value={billTotals.toFormat()}
<Statistic />
title={t("bills.labels.discrepancy")} <Statistic
valueStyle={{ title={t("bills.labels.discrepancy")}
color: discrepancy.getAmount === 0 ? "green" : "red", valueStyle={{
}} color: discrepancy.getAmount === 0 ? "green" : "red",
value={discrepancy.toFormat()} }}
/> value={discrepancy.toFormat()}
<Statistic />
title={t("bills.labels.dedfromlbr")} <Statistic
value={lbrAdjustments.toFormat()} title={t("bills.labels.dedfromlbr")}
/> value={lbrAdjustments.toFormat()}
<Statistic />
title={t("bills.labels.discrepwithlbradj")} <Statistic
valueStyle={{ title={t("bills.labels.discrepwithlbradj")}
color: discrepWithLbrAdj.getAmount === 0 ? "green" : "red", valueStyle={{
}} color: discrepWithLbrAdj.getAmount === 0 ? "green" : "red",
value={discrepWithLbrAdj.toFormat()} }}
/> value={discrepWithLbrAdj.toFormat()}
<Statistic />
title={t("bills.labels.billcmtotal")} <Statistic
value={billCms.toFormat()} title={t("bills.labels.billcmtotal")}
/> value={billCms.toFormat()}
<Statistic />
title={t("bills.labels.discrepwithcms")} <Statistic
valueStyle={{ title={t("bills.labels.discrepwithcms")}
color: discrepWithCms.getAmount === 0 ? "green" : "red", valueStyle={{
}} color: discrepWithCms.getAmount === 0 ? "green" : "red",
value={discrepWithCms.toFormat()} }}
/> value={discrepWithCms.toFormat()}
</div> />
<Statistic
title={t("bills.labels.totalreturns")}
value={totalReturns.toFormat()}
/>
<Statistic
title={t("bills.labels.creditsreceived")}
value={billCms.toFormat()}
/>
<Statistic
title={t("bills.labels.creditsnotreceived")}
valueStyle={{
color: creditsNotReceived.getAmount === 0 ? "green" : "red",
}}
value={creditsNotReceived.toFormat()}
/>
</Space>
</Card>
); );
} }

View File

@@ -73,7 +73,6 @@ export default function JobCostingPartsTable({ job, data, summaryData }) {
return ( return (
<div> <div>
<Table <Table
size="small"
title={() => { title={() => {
return ( return (
<div className="imex-table-header"> <div className="imex-table-header">

View File

@@ -26,7 +26,6 @@ export default function JobDetailCardsNotesComponent({ loading, data }) {
{data ? ( {data ? (
<Container> <Container>
<List <List
size="small"
bordered bordered
dataSource={data.notes} dataSource={data.notes}
renderItem={(item) => ( renderItem={(item) => (

View File

@@ -62,16 +62,6 @@ export function JobLinesComponent({
sortOrder: sortOrder:
state.sortedInfo.columnKey === "line_no" && state.sortedInfo.order, state.sortedInfo.columnKey === "line_no" && state.sortedInfo.order,
}, },
{
title: t("joblines.fields.line_ind"),
dataIndex: "line_ind",
key: "line_ind",
fixed: "left",
sorter: (a, b) => alphaSort(a.line_ind, b.line_ind),
sortOrder:
state.sortedInfo.columnKey === "line_ind" && state.sortedInfo.order,
responsive: ["md"],
},
{ {
title: t("joblines.fields.line_desc"), title: t("joblines.fields.line_desc"),
dataIndex: "line_desc", dataIndex: "line_desc",
@@ -192,6 +182,15 @@ export function JobLinesComponent({
sortOrder: sortOrder:
state.sortedInfo.columnKey === "mod_lb_hrs" && state.sortedInfo.order, state.sortedInfo.columnKey === "mod_lb_hrs" && state.sortedInfo.order,
}, },
{
title: t("joblines.fields.line_ind"),
dataIndex: "line_ind",
key: "line_ind",
sorter: (a, b) => alphaSort(a.line_ind, b.line_ind),
sortOrder:
state.sortedInfo.columnKey === "line_ind" && state.sortedInfo.order,
responsive: ["md"],
},
{ {
title: t("joblines.fields.notes"), title: t("joblines.fields.notes"),
dataIndex: "notes", dataIndex: "notes",

View File

@@ -88,7 +88,6 @@ export default function JobReconciliationBillsTable({
return ( return (
<div> <div>
<Table <Table
size="small"
pagination={false} pagination={false}
scroll={{ y: "40vh", x: true }} scroll={{ y: "40vh", x: true }}
columns={columns} columns={columns}

View File

@@ -104,7 +104,6 @@ export default function JobReconcilitionPartsTable({
return ( return (
<div> <div>
<Table <Table
size="small"
pagination={false} pagination={false}
columns={columns} columns={columns}
scroll={{ y: "40vh", x: true }} scroll={{ y: "40vh", x: true }}

View File

@@ -39,7 +39,7 @@ export function JobsTotalsTableComponent({ jobRO, job }) {
return ( return (
<div> <div>
<Row gutter={[32, 32]}> <Row gutter={[16, 16]}>
<Col {...colSpan}> <Col {...colSpan}>
<Card title={t("jobs.labels.labortotals")}> <Card title={t("jobs.labels.labortotals")}>
<JobTotalsTableLabor job={job} /> <JobTotalsTableLabor job={job} />

View File

@@ -87,7 +87,6 @@ export default function JobTotalsTableLabor({ job }) {
]; ];
const handleTableChange = (pagination, filters, sorter) => { const handleTableChange = (pagination, filters, sorter) => {
console.log("sorter :>> ", sorter);
setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
}; };
return ( return (

View File

@@ -1,5 +1,5 @@
import { DownloadOutlined, SyncOutlined } from "@ant-design/icons"; import { DownloadOutlined, SyncOutlined } from "@ant-design/icons";
import { Button, Input, notification, Space, Table } from "antd"; import { Button, Card, Input, notification, Space, Table } from "antd";
import axios from "axios"; import axios from "axios";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -37,9 +37,11 @@ export function JobsAvailableScan({ partnerVersion, refetch }) {
}); });
if (response.data.success) { if (response.data.success) {
//Came through //Came through
if(refetch) refetch() if (refetch) refetch();
} else { } else {
notification["error"]({ message: t("jobs.errors.scanimport", {message: response.data.error}) }); notification["error"]({
message: t("jobs.errors.scanimport", { message: response.data.error }),
});
} }
setLoading(false); setLoading(false);
}; };
@@ -120,40 +122,37 @@ export function JobsAvailableScan({ partnerVersion, refetch }) {
: []; : [];
return ( return (
<Table <Card
loading={loading} title={t("jobs.labels.diskscan")}
title={() => { extra={
return ( <Space wrap>
<div className="imex-table-header"> <Button
<Space> loading={loading}
<strong>{t("jobs.labels.diskscan")}</strong> disabled={!partnerVersion}
<Button onClick={() => {
loading={loading} scanEstimates();
disabled={!partnerVersion} }}
onClick={() => { >
scanEstimates(); <SyncOutlined />
}} </Button>
>
<SyncOutlined /> <Input.Search
</Button> placeholder={t("general.labels.search")}
</Space> onChange={(e) => {
<div className="imex-table-header__search"> setSearchText(e.currentTarget.value);
<Input.Search }}
placeholder={t("general.labels.search")} />
onChange={(e) => { </Space>
setSearchText(e.currentTarget.value); }
}} >
/> <Table
</div> loading={loading}
</div> pagination={{ position: "top" }}
); columns={columns}
}} rowKey="id"
size="small" dataSource={data}
pagination={{ position: "top" }} onChange={handleTableChange}
columns={columns} />
rowKey="id" </Card>
dataSource={data}
onChange={handleTableChange}
/>
); );
} }

View File

@@ -5,7 +5,7 @@ import {
SyncOutlined, SyncOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { Button, Input, notification, Space, Table } from "antd"; import { Button, Card, Input, notification, Space, Table } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
@@ -133,7 +133,7 @@ export default function JobsAvailableComponent({
title: t("general.labels.actions"), title: t("general.labels.actions"),
key: "actions", key: "actions",
render: (text, record) => ( render: (text, record) => (
<Space> <Space wrap>
<Button <Button
onClick={() => { onClick={() => {
deleteJob({ variables: { id: record.id } }).then((r) => { deleteJob({ variables: { id: record.id } }).then((r) => {
@@ -176,58 +176,54 @@ export default function JobsAvailableComponent({
: []; : [];
return ( return (
<Table <Card
loading={loading} title={t("jobs.labels.availablejobs")}
title={() => { extra={
return ( <Space wrap>
<div className="imex-table-header"> <Button
<Space> onClick={() => {
<strong>{t("jobs.labels.availablejobs")}</strong> refetch();
<Button }}
onClick={() => { >
<SyncOutlined />
</Button>
<Button
onClick={() => {
deleteAllAvailableJobs()
.then((r) => {
notification["success"]({
message: t("jobs.successes.all_deleted", {
count: r.data.delete_available_jobs.affected_rows,
}),
});
refetch(); refetch();
}} })
> .catch((r) => {
<SyncOutlined /> notification["error"]({
</Button> message: t("jobs.errors.deleted") + " " + r.message,
<Button });
onClick={() => { });
deleteAllAvailableJobs() }}
.then((r) => { >
notification["success"]({ {t("general.actions.deleteall")}
message: t("jobs.successes.all_deleted", { </Button>
count: r.data.delete_available_jobs.affected_rows,
}), <Input.Search
}); placeholder={t("general.labels.search")}
refetch(); onChange={(e) => {
}) setSearchText(e.currentTarget.value);
.catch((r) => { }}
notification["error"]({ />
message: t("jobs.errors.deleted") + " " + r.message, </Space>
}); }
}); >
}} <Table
> loading={loading}
{t("general.actions.deleteall")} columns={columns}
</Button> rowKey="id"
</Space> dataSource={availableJobs}
<div className="imex-table-header__search"> onChange={handleTableChange}
<Input.Search />
placeholder={t("general.labels.search")} </Card>
onChange={(e) => {
setSearchText(e.currentTarget.value);
}}
/>
</div>
</div>
);
}}
size="small"
pagination={{ position: "top" }}
columns={columns}
rowKey="id"
dataSource={availableJobs}
onChange={handleTableChange}
/>
); );
} }

View File

@@ -5,7 +5,7 @@ import {
useMutation, useMutation,
useQuery, useQuery,
} from "@apollo/client"; } from "@apollo/client";
import { notification } from "antd"; import { Col, notification, Row } from "antd";
import Axios from "axios"; import Axios from "axios";
import Dinero from "dinero.js"; import Dinero from "dinero.js";
import _ from "lodash"; import _ from "lodash";
@@ -354,14 +354,20 @@ export function JobsAvailableContainer({ bodyshop, currentUser }) {
onCancel={onJobModalCancel} onCancel={onJobModalCancel}
modalSearchState={modalSearchState} modalSearchState={modalSearchState}
/> />
<JobsAvailableTableComponent <Row gutter={[16, 16]}>
loading={loading} <Col span={24}>
data={data} <JobsAvailableTableComponent
refetch={refetch} loading={loading}
addJobAsNew={addJobAsNew} data={data}
addJobAsSupp={addJobAsSupp} refetch={refetch}
/> addJobAsNew={addJobAsNew}
<JobsAvailableScan refetch={refetch} /> addJobAsSupp={addJobAsSupp}
/>
</Col>
<Col span={24}>
<JobsAvailableScan refetch={refetch} />
</Col>
</Row>
</LoadingSpinner> </LoadingSpinner>
); );
} }

View File

@@ -30,6 +30,7 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
jobRO={false} jobRO={false}
expanded expanded
required={selected && true} required={selected && true}
form={form}
/> />
<Collapse defaultActiveKey="insurance"> <Collapse defaultActiveKey="insurance">
<Collapse.Panel <Collapse.Panel

View File

@@ -1,6 +1,7 @@
import { Col, Row, Typography } from "antd"; import { Checkbox, Col, Row } from "antd";
import React from "react"; import React, { useContext } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
import JobsCreateOwnerInfoNewComponent from "./jobs-create-owner-info.new.component"; import JobsCreateOwnerInfoNewComponent from "./jobs-create-owner-info.new.component";
import JobsCreateOwnerInfoSearchComponent from "./jobs-create-owner-info.search.component"; import JobsCreateOwnerInfoSearchComponent from "./jobs-create-owner-info.search.component";
@@ -11,12 +12,30 @@ const colSpan = {
export default function JobsCreateOwnerInfoComponent({ loading, owners }) { export default function JobsCreateOwnerInfoComponent({ loading, owners }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [state, setState] = useContext(JobCreateContext);
return ( return (
<div> <div>
<Row> <Row gutter={[16, 16]}>
<Typography.Title>{t("jobs.labels.create.ownerinfo")}</Typography.Title> <Col span={24}>
</Row> <Checkbox
<Row gutter={[32, 32]}> defaultChecked={state.owner.new}
checked={state.owner.new}
onChange={() => {
setState({
...state,
owner: {
...state.owner,
new: !state.owner.new,
selectedid: null,
},
});
}}
>
{t("jobs.labels.create.newowner")}
</Checkbox>
</Col>
<Col {...colSpan}> <Col {...colSpan}>
<JobsCreateOwnerInfoSearchComponent <JobsCreateOwnerInfoSearchComponent
loading={loading} loading={loading}

View File

@@ -1,4 +1,4 @@
import { Form, Input, Checkbox, Switch } from "antd"; import { Form, Input, Switch } from "antd";
import React, { useContext } from "react"; import React, { useContext } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import JobCreateContext from "../../pages/jobs-create/jobs-create.context"; import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
@@ -9,28 +9,11 @@ import FormItemPhone, {
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
export default function JobsCreateOwnerInfoNewComponent() { export default function JobsCreateOwnerInfoNewComponent() {
const [state, setState] = useContext(JobCreateContext); const [state] = useContext(JobCreateContext);
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div> <div>
<Checkbox
defaultChecked={state.owner.new}
checked={state.owner.new}
onChange={() => {
setState({
...state,
owner: {
...state.owner,
new: !state.owner.new,
selectedid: null,
},
});
}}
>
{t("jobs.labels.create.newowner")}
</Checkbox>
<LayoutFormRow header={t("owners.forms.name")} grow> <LayoutFormRow header={t("owners.forms.name")} grow>
<Form.Item <Form.Item
label={t("owners.fields.ownr_ln")} label={t("owners.fields.ownr_ln")}

View File

@@ -1,4 +1,4 @@
import { Input, Table } from "antd"; import { Card, Input, Table } from "antd";
import React, { useContext, useState } from "react"; import React, { useContext, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import JobCreateContext from "../../pages/jobs-create/jobs-create.context"; import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
@@ -91,66 +91,63 @@ export default function JobsCreateOwnerInfoSearchComponent({
}; };
return ( return (
<Table <Card
loading={loading} extra={
title={() => { <Input.Search
return ( placeholder={t("general.labels.search")}
<div className="imex-table-header"> onSearch={(value) => {
<Input.Search
className="imex-table-header__search"
placeholder={t("general.labels.search")}
onSearch={(value) => {
setState({
...state,
owner: { ...state.owner, search: value },
});
}}
enterButton
/>
</div>
);
}}
size="small"
scroll={{ x: true }}
pagination={{ position: "top" }}
columns={columns}
rowKey="id"
dataSource={owners}
onChange={handleTableChange}
rowSelection={{
onSelect: (props) => {
setState({
...state,
owner: { ...state.owner, new: false, selectedid: props.id },
});
},
type: "radio",
selectedRowKeys: [state.owner.selectedid],
}}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
if (record) {
if (record.id) {
setState({
...state,
owner: {
...state.owner,
new: false,
selectedid: record.id,
},
});
return;
}
}
setState({ setState({
...state, ...state,
owner: { ...state.owner, selectedid: null }, owner: { ...state.owner, search: value },
});
}}
enterButton
/>
}
>
<Table
loading={loading}
scroll={{ x: true }}
pagination={{ position: "top" }}
columns={columns}
rowKey="id"
dataSource={owners}
onChange={handleTableChange}
rowSelection={{
onSelect: (props) => {
setState({
...state,
owner: { ...state.owner, new: false, selectedid: props.id },
}); });
}, },
}; type: "radio",
}} selectedRowKeys: [state.owner.selectedid],
/> }}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
if (record) {
if (record.id) {
setState({
...state,
owner: {
...state.owner,
new: false,
selectedid: record.id,
},
});
return;
}
}
setState({
...state,
owner: { ...state.owner, selectedid: null },
});
},
};
}}
/>
</Card>
); );
} }

View File

@@ -1,20 +1,56 @@
import { Col, Row, Typography } from "antd"; import { Checkbox, Col, Row } from "antd";
import React from "react"; import React, { useContext } from "react";
import { useTranslation } from "react-i18next"; import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
import JobsCreateVehicleInfoNewComponent from "./jobs-create-vehicle-info.new.component"; import JobsCreateVehicleInfoNewComponent from "./jobs-create-vehicle-info.new.component";
import JobsCreateVehicleInfoSearchComponent from "./jobs-create-vehicle-info.search.component"; import JobsCreateVehicleInfoSearchComponent from "./jobs-create-vehicle-info.search.component";
import { useTranslation } from "react-i18next";
const colSpan = { const colSpan = {
sm: { span: 24 }, sm: { span: 24 },
lg: { span: 12 }, lg: { span: 12 },
}; };
export default function JobsCreateVehicleInfoComponent({ loading, vehicles }) { export default function JobsCreateVehicleInfoComponent({ loading, vehicles }) {
const [state, setState] = useContext(JobCreateContext);
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div> <div>
<Typography.Title>{t("jobs.labels.create.vehicleinfo")}</Typography.Title> <Row gutter={[16, 16]}>
<Row gutter={[32, 32]}> <Col span={24}>
<Checkbox
defaultChecked={state.vehicle.new}
checked={state.vehicle.new}
onChange={() => {
setState({
...state,
vehicle: {
...state.vehicle,
none: false,
new: !state.vehicle.new,
selectedid: null,
},
});
}}
>
{t("jobs.labels.create.newvehicle")}
</Checkbox>
<Checkbox
defaultChecked={state.vehicle.none}
checked={state.vehicle.none}
onChange={() => {
setState({
...state,
vehicle: {
...state.vehicle,
new: false,
none: !state.vehicle.none,
selectedid: null,
},
});
}}
>
{t("jobs.labels.create.novehicle")}
</Checkbox>
</Col>
<Col {...colSpan}> <Col {...colSpan}>
<JobsCreateVehicleInfoSearchComponent <JobsCreateVehicleInfoSearchComponent
loading={loading} loading={loading}

View File

@@ -1,4 +1,4 @@
import { Checkbox, Form, Input } from "antd"; import { Form, Input } from "antd";
import React, { useContext } from "react"; import React, { useContext } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import JobCreateContext from "../../pages/jobs-create/jobs-create.context"; import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
@@ -6,46 +6,11 @@ import FormDatePicker from "../form-date-picker/form-date-picker.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
export default function JobsCreateVehicleInfoNewComponent() { export default function JobsCreateVehicleInfoNewComponent() {
const [state, setState] = useContext(JobCreateContext); const [state] = useContext(JobCreateContext);
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div> <div>
<Checkbox
defaultChecked={state.vehicle.new}
checked={state.vehicle.new}
onChange={() => {
setState({
...state,
vehicle: {
...state.vehicle,
none: false,
new: !state.vehicle.new,
selectedid: null,
},
});
}}
>
{t("jobs.labels.create.newvehicle")}
</Checkbox>
<Checkbox
defaultChecked={state.vehicle.none}
checked={state.vehicle.none}
onChange={() => {
setState({
...state,
vehicle: {
...state.vehicle,
new: false,
none: !state.vehicle.none,
selectedid: null,
},
});
}}
>
{t("jobs.labels.create.novehicle")}
</Checkbox>
<LayoutFormRow header={t("vehicles.forms.detail")} grow> <LayoutFormRow header={t("vehicles.forms.detail")} grow>
<Form.Item <Form.Item
label={t("vehicles.fields.v_vin")} label={t("vehicles.fields.v_vin")}

View File

@@ -1,6 +1,6 @@
import React, { useContext, useState } from "react"; import React, { useContext, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Table, Input } from "antd"; import { Table, Input, Card, Space } from "antd";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import JobCreateContext from "../../pages/jobs-create/jobs-create.context"; import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
@@ -55,74 +55,77 @@ export default function JobsCreateVehicleInfoSearchComponent({
}; };
return ( return (
<Table <Card
loading={loading} extra={
title={() => { <Space wrap>
return ( <Input.Search
<div className="imex-table-header"> placeholder={t("general.labels.search")}
<Input.Search onSearch={(value) => {
className="imex-table-header__search" setState({
placeholder={t("general.labels.search")} ...state,
onSearch={(value) => { vehicle: { ...state.vehicle, search: value },
setState({ });
...state, }}
vehicle: { ...state.vehicle, search: value }, enterButton
}); />
}} </Space>
enterButton }
/> >
</div> <Table
); loading={loading}
}} scroll={{ x: true }}
size="small" pagination={{ position: "top" }}
scroll={{ x: true }} columns={columns}
pagination={{ position: "top" }} rowKey="id"
columns={columns} dataSource={vehicles}
rowKey="id" onChange={handleTableChange}
dataSource={vehicles} rowSelection={{
onChange={handleTableChange} onSelect: (props) => {
rowSelection={{
onSelect: (props) => {
setState({
...state,
vehicle: {
...state.vehicle,
none: false,
new: false,
selectedid: props.id,
vehicleObj: props,
},
});
},
type: "radio",
selectedRowKeys: [state.vehicle.selectedid],
}}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
if (record) {
if (record.id) {
setState({
...state,
vehicle: {
...state.vehicle,
none: false,
new: false,
selectedid: record.id,
vehicleObj: record,
},
});
return;
}
}
setState({ setState({
...state, ...state,
vehicle: { ...state.vehicle, selectedid: null, vehicleObj: null }, vehicle: {
...state.vehicle,
none: false,
new: false,
selectedid: props.id,
vehicleObj: props,
},
}); });
}, },
}; type: "radio",
}} selectedRowKeys: [state.vehicle.selectedid],
/> }}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
if (record) {
if (record.id) {
setState({
...state,
vehicle: {
...state.vehicle,
none: false,
new: false,
selectedid: record.id,
vehicleObj: record,
},
});
return;
}
}
setState({
...state,
vehicle: {
...state.vehicle,
selectedid: null,
vehicleObj: null,
},
});
},
};
}}
/>
</Card>
); );
} }

View File

@@ -142,46 +142,6 @@ export function JobsDetailHeader({ job, bodyshop }) {
</Col> </Col>
</Row> </Row>
); );
// return (
// <PageHeader
// title={job.ro_number || t("general.labels.na")}
// subTitle={job.status}
// tags={[
// <OwnerTagPopoverComponent key="owner" job={job} />,
// <VehicleTagPopoverComponent key="vehicle" job={job} />,
// <Tag
// color="#f50"
// key="production"
// style={{ display: job.inproduction ? "" : "none" }}
// >
// {t("jobs.labels.inproduction")}
// </Tag>,
// <Tag title={t("jobs.fields.repairtotal")} key="total" color="green">
// <CurrencyFormatter>{job.clm_total}</CurrencyFormatter>
// <span style={{ margin: "0rem .5rem" }}>/</span>
// <CurrencyFormatter>{job.owner_owing}</CurrencyFormatter>
// </Tag>,
// ]}
// extra={menuExtra}
// >
// <div style={{ display: "flex", justifyContent: "flex-end" }}>
// {(job.inproduction || jobInPostProduction) && (
// <>
// <div style={{ display: "flex", flex: 1 }}>
// <div style={{ marginRight: "2rem" }}>
// {t("jobs.fields.production_vars.note")}
// </div>
// <ProductionListColumnProductionNote record={job} />
// </div>
// <Divider type="vertical" />
// </>
// )}
//
// </div>
// </PageHeader>
// );
} }
export default connect(mapStateToProps, mapDispatchToProps)(JobsDetailHeader); export default connect(mapStateToProps, mapDispatchToProps)(JobsDetailHeader);

View File

@@ -1,10 +1,9 @@
import { Col, Row } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors"; import { selectJobReadOnly } from "../../redux/application/application.selectors";
import LaborAllocationsTableComponent from "../labor-allocations-table/labor-allocations-table.component"; import LaborAllocationsTableComponent from "../labor-allocations-table/labor-allocations-table.component";
import TimeTicketEnterButton from "../time-ticket-enter-button/time-ticket-enter-button.component";
import TimeTicketList from "../time-ticket-list/time-ticket-list.component"; import TimeTicketList from "../time-ticket-list/time-ticket-list.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -13,6 +12,41 @@ const mapStateToProps = createStructuredSelector({
export default connect(mapStateToProps, null)(JobsDetailLaborContainer); export default connect(mapStateToProps, null)(JobsDetailLaborContainer);
const ticketSpan = {
xs: {
span: 24,
},
sm: {
span: 24,
},
md: {
span: 24,
},
lg: {
span: 24,
},
xl: {
span: 16,
},
};
const adjSpan = {
xs: {
span: 24,
},
sm: {
span: 24,
},
md: {
span: 24,
},
lg: {
span: 24,
},
xl: {
span: 8,
},
};
export function JobsDetailLaborContainer({ export function JobsDetailLaborContainer({
jobRO, jobRO,
jobId, jobId,
@@ -23,27 +57,26 @@ export function JobsDetailLaborContainer({
techConsole, techConsole,
adjustments, adjustments,
}) { }) {
const { t } = useTranslation();
return ( return (
<div> <Row gutter={[16, 16]}>
{techConsole ? null : ( <Col {...ticketSpan}>
<TimeTicketEnterButton actions={{ refetch }} context={{ jobId: jobId }}> <TimeTicketList
{t("timetickets.actions.enter")} loading={loading}
</TimeTicketEnterButton> timetickets={timetickets}
)} refetch={refetch}
<LaborAllocationsTableComponent techConsole={techConsole}
jobId={jobId} disabled={jobRO}
joblines={joblines} jobId={jobId}
timetickets={timetickets} />
adjustments={adjustments} </Col>
/> <Col {...adjSpan}>
<TimeTicketList <LaborAllocationsTableComponent
loading={loading} jobId={jobId}
timetickets={timetickets} joblines={joblines}
refetch={refetch} timetickets={timetickets}
techConsole={techConsole} adjustments={adjustments}
disabled={jobRO} />
/> </Col>
</div> </Row>
); );
} }

View File

@@ -3,25 +3,8 @@ import React from "react";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import BillsListTable from "../bills-list-table/bills-list-table.component"; import BillsListTable from "../bills-list-table/bills-list-table.component";
import JobBillsTotal from "../job-bills-total/job-bills-total.component"; import JobBillsTotal from "../job-bills-total/job-bills-total.component";
import PartsOrderModal from "../parts-order-modal/parts-order-modal.container";
import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component"; import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component";
const tableCol = { import PartsOrderModal from "../parts-order-modal/parts-order-modal.container";
xs: {
span: 24,
},
md: {
span: 20,
},
};
const totalsCol = {
xs: {
span: 24,
},
md: {
span: 4,
},
};
export default function JobsDetailPliComponent({ export default function JobsDetailPliComponent({
job, job,
@@ -35,26 +18,30 @@ export default function JobsDetailPliComponent({
{billsQuery.error ? ( {billsQuery.error ? (
<AlertComponent message={billsQuery.error.message} type="error" /> <AlertComponent message={billsQuery.error.message} type="error" />
) : null} ) : null}
<Row>
<Col {...tableCol}> <Row gutter={[16, 16]}>
<Col span={24}>
<JobBillsTotal
bills={billsQuery.data ? billsQuery.data.bills : []}
partsOrders={billsQuery.data ? billsQuery.data.parts_orders : []}
loading={billsQuery.loading}
jobTotals={job.job_totals}
/>
</Col>
<Col span={24}>
<PartsOrderListTableComponent <PartsOrderListTableComponent
job={job} job={job}
handleOnRowClick={handlePartsOrderOnRowClick} handleOnRowClick={handlePartsOrderOnRowClick}
billsQuery={billsQuery} billsQuery={billsQuery}
/> />
</Col>
<Col span={24}>
<BillsListTable <BillsListTable
job={job} job={job}
handleOnRowClick={handleBillOnRowClick} handleOnRowClick={handleBillOnRowClick}
billsQuery={billsQuery} billsQuery={billsQuery}
/> />
</Col> </Col>
<Col {...totalsCol}>
<JobBillsTotal
bills={billsQuery.data ? billsQuery.data.bills : []}
loading={billsQuery.loading}
jobTotals={job.job_totals}
/>
</Col>
</Row> </Row>
</div> </div>
); );

View File

@@ -154,7 +154,7 @@ export function JobsDetailRates({ jobRO, form }) {
</Form.Item> </Form.Item>
</FormRow> </FormRow>
<JobsDetailRatesParts /> <JobsDetailRatesParts form={form} />
</div> </div>
); );
} }

View File

@@ -5,12 +5,16 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors"; import { selectJobReadOnly } from "../../redux/application/application.selectors";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly, jobRO: selectJobReadOnly,
}); });
export function JobsDetailRatesParts({ jobRO, expanded, required = true }) { export function JobsDetailRatesParts({
jobRO,
expanded,
required = true,
form,
}) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@@ -43,17 +47,27 @@ export function JobsDetailRatesParts({ jobRO, expanded, required = true }) {
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item <Form.Item shouldUpdate>
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")} {() => {
name={["parts_tax_rates", "PAA", "prt_tax_rt"]} return (
rules={[ <Form.Item
{ label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
required: required, name={["parts_tax_rates", "PAA", "prt_tax_rt"]}
message: t("general.validation.required"), rules={[
}, {
]} required: form.getFieldValue([
> "parts_tax_rates",
<InputNumber min={0} max={1} precision={2} disabled={jobRO} /> "PAA",
"prt_tax_in",
]),
message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAC")}> <LayoutFormRow header={t("joblines.fields.part_types.PAC")}>
@@ -83,17 +97,27 @@ export function JobsDetailRatesParts({ jobRO, expanded, required = true }) {
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item <Form.Item shouldUpdate>
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")} {() => {
name={["parts_tax_rates", "PAC", "prt_tax_rt"]} return (
rules={[ <Form.Item
{ label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
required: required, name={["parts_tax_rates", "PAC", "prt_tax_rt"]}
message: t("general.validation.required"), rules={[
}, {
]} required: form.getFieldValue([
> "parts_tax_rates",
<InputNumber min={0} max={1} precision={2} disabled={jobRO} /> "PAC",
"prt_tax_in",
]),
message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAL")}> <LayoutFormRow header={t("joblines.fields.part_types.PAL")}>
@@ -123,17 +147,27 @@ export function JobsDetailRatesParts({ jobRO, expanded, required = true }) {
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item <Form.Item shouldUpdate>
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")} {() => {
name={["parts_tax_rates", "PAL", "prt_tax_rt"]} return (
rules={[ <Form.Item
{ label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
required: required, name={["parts_tax_rates", "PAL", "prt_tax_rt"]}
message: t("general.validation.required"), rules={[
}, {
]} required: form.getFieldValue([
> "parts_tax_rates",
<InputNumber min={0} max={1} precision={2} disabled={jobRO} /> "PAL",
"prt_tax_in",
]),
message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAG")}> <LayoutFormRow header={t("joblines.fields.part_types.PAG")}>
@@ -163,17 +197,27 @@ export function JobsDetailRatesParts({ jobRO, expanded, required = true }) {
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item <Form.Item shouldUpdate>
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")} {() => {
name={["parts_tax_rates", "PAG", "prt_tax_rt"]} return (
rules={[ <Form.Item
{ label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
required: required, name={["parts_tax_rates", "PAG", "prt_tax_rt"]}
message: t("general.validation.required"), rules={[
}, {
]} required: form.getFieldValue([
> "parts_tax_rates",
<InputNumber min={0} max={1} precision={2} disabled={jobRO} /> "PAG",
"prt_tax_in",
]),
message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAM")}> <LayoutFormRow header={t("joblines.fields.part_types.PAM")}>
@@ -203,17 +247,27 @@ export function JobsDetailRatesParts({ jobRO, expanded, required = true }) {
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item <Form.Item shouldUpdate>
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")} {() => {
name={["parts_tax_rates", "PAM", "prt_tax_rt"]} return (
rules={[ <Form.Item
{ label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
required: required, name={["parts_tax_rates", "PAM", "prt_tax_rt"]}
message: t("general.validation.required"), rules={[
}, {
]} required: form.getFieldValue([
> "parts_tax_rates",
<InputNumber min={0} max={1} precision={2} disabled={jobRO} /> "PAM",
"prt_tax_in",
]),
message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAN")}> <LayoutFormRow header={t("joblines.fields.part_types.PAN")}>
@@ -243,18 +297,128 @@ export function JobsDetailRatesParts({ jobRO, expanded, required = true }) {
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "PAN", "prt_tax_rt"]}
rules={[
{
required: form.getFieldValue([
"parts_tax_rates",
"PAN",
"prt_tax_in",
]),
message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
</LayoutFormRow>{" "}
<LayoutFormRow header={t("joblines.fields.part_types.PAO")}>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAN", "prt_tax_rt"]} name={["parts_tax_rates", "PAO", "prt_discp"]}
rules={[
{
required: required,
message: t("general.validation.required"),
},
]}
> >
<InputNumber min={0} max={1} precision={2} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
name={["parts_tax_rates", "PAO", "prt_mktyp"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAO", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
name={["parts_tax_rates", "PAO", "prt_tax_in"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "PAO", "prt_tax_rt"]}
rules={[
{
required: form.getFieldValue([
"parts_tax_rates",
"PAO",
"prt_tax_in",
]),
message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
</LayoutFormRow>{" "}
<LayoutFormRow header={t("joblines.fields.part_types.PAP")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAP", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
name={["parts_tax_rates", "PAP", "prt_mktyp"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAP", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
name={["parts_tax_rates", "PAP", "prt_tax_in"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "PAP", "prt_tax_rt"]}
rules={[
{
required: form.getFieldValue([
"parts_tax_rates",
"PAP",
"prt_tax_in",
]),
message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAR")}> <LayoutFormRow header={t("joblines.fields.part_types.PAR")}>
<Form.Item <Form.Item
@@ -283,17 +447,27 @@ export function JobsDetailRatesParts({ jobRO, expanded, required = true }) {
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item <Form.Item shouldUpdate>
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")} {() => {
name={["parts_tax_rates", "PAR", "prt_tax_rt"]} return (
rules={[ <Form.Item
{ label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
required: required, name={["parts_tax_rates", "PAR", "prt_tax_rt"]}
message: t("general.validation.required"), rules={[
}, {
]} required: form.getFieldValue([
> "parts_tax_rates",
<InputNumber min={0} max={1} precision={2} disabled={jobRO} /> "PAR",
"prt_tax_in",
]),
message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAS")}> <LayoutFormRow header={t("joblines.fields.part_types.PAS")}>
@@ -323,18 +497,78 @@ export function JobsDetailRatesParts({ jobRO, expanded, required = true }) {
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "PAS", "prt_tax_rt"]}
rules={[
{
required: form.getFieldValue([
"parts_tax_rates",
"PAS",
"prt_tax_in",
]),
message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PASL")}>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAS", "prt_tax_rt"]} name={["parts_tax_rates", "PASL", "prt_discp"]}
rules={[
{
required: required,
message: t("general.validation.required"),
},
]}
> >
<InputNumber min={0} max={1} precision={2} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
name={["parts_tax_rates", "PASL", "prt_mktyp"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PASL", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
name={["parts_tax_rates", "PASL", "prt_tax_in"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "PASL", "prt_tax_rt"]}
rules={[
{
required: form.getFieldValue([
"parts_tax_rates",
"PASL",
"prt_tax_in",
]),
message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.CCDR")}> <LayoutFormRow header={t("joblines.fields.part_types.CCDR")}>
<Form.Item <Form.Item

View File

@@ -1,5 +1,5 @@
import { FileExcelFilled } from "@ant-design/icons"; import { FileExcelFilled } from "@ant-design/icons";
import { Card } from "antd"; import { Card, Col, Row, Space } from "antd";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import Gallery from "react-grid-gallery"; import Gallery from "react-grid-gallery";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -104,78 +104,87 @@ function JobsDocumentsComponent({
}, [data, setgalleryImages, t]); }, [data, setgalleryImages, t]);
return ( return (
<div className="clearfix"> <div className="clea rfix">
<div className="imex-flex-row"> <Row gutter={[16, 16]}>
<JobsDocumentsGallerySelectAllComponent <Col span={24}>
galleryImages={galleryImages} <Space wrap>
setGalleryImages={setgalleryImages} <JobsDocumentsGallerySelectAllComponent
/> galleryImages={galleryImages}
<JobsDocumentsDownloadButton galleryImages={galleryImages} /> setGalleryImages={setgalleryImages}
<JobsDocumentsDeleteButton />
galleryImages={galleryImages} <JobsDocumentsDownloadButton galleryImages={galleryImages} />
deletionCallback={billsCallback || refetch} <JobsDocumentsDeleteButton
/> galleryImages={galleryImages}
<JobsDocumentsGalleryReassign galleryImages={galleryImages} /> deletionCallback={billsCallback || refetch}
</div> />
<DocumentsUploadComponent <JobsDocumentsGalleryReassign galleryImages={galleryImages} />
jobId={jobId} </Space>
billId={billId} </Col>
callbackAfterUpload={billsCallback || refetch} <Col span={24}>
tagsArray={["test"]} <Card>
></DocumentsUploadComponent> <DocumentsUploadComponent
<div style={{ marginTop: "2rem" }}> jobId={jobId}
<Card title={t("jobs.labels.documents-images")}> billId={billId}
<Gallery callbackAfterUpload={billsCallback || refetch}
images={galleryImages.images} />
backdropClosesModal={true} </Card>
onClickImage={(props) => { </Col>
window.open(
props.target.src,
"_blank",
"toolbar=0,location=0,menubar=0"
);
}}
onSelectImage={(index, image) => {
setgalleryImages({
...galleryImages,
images: galleryImages.images.map((g, idx) =>
index === idx ? { ...g, isSelected: !g.isSelected } : g
),
});
}}
/>
</Card>
<Card title={t("jobs.labels.documents-other")}> <Col span={24}>
<Gallery <Card title={t("jobs.labels.documents-images")}>
images={galleryImages.other} <Gallery
backdropClosesModal={true} images={galleryImages.images}
enableLightbox={false} backdropClosesModal={true}
thumbnailStyle={() => { onClickImage={(props) => {
return { window.open(
backgroundImage: <FileExcelFilled />, props.target.src,
height: "100%", "_blank",
width: "100%", "toolbar=0,location=0,menubar=0"
}; );
}} }}
onClickThumbnail={(index) => { onSelectImage={(index, image) => {
window.open( setgalleryImages({
galleryImages.other[index].src, ...galleryImages,
"_blank", images: galleryImages.images.map((g, idx) =>
"toolbar=0,location=0,menubar=0" index === idx ? { ...g, isSelected: !g.isSelected } : g
); ),
}} });
onSelectImage={(index) => { }}
setgalleryImages({ />
...galleryImages, </Card>
other: galleryImages.other.map((g, idx) => </Col>
index === idx ? { ...g, isSelected: !g.isSelected } : g <Col span={24}>
), <Card title={t("jobs.labels.documents-other")}>
}); <Gallery
}} images={galleryImages.other}
/> backdropClosesModal={true}
</Card> enableLightbox={false}
</div> thumbnailStyle={() => {
return {
backgroundImage: <FileExcelFilled />,
height: "100%",
width: "100%",
};
}}
onClickThumbnail={(index) => {
window.open(
galleryImages.other[index].src,
"_blank",
"toolbar=0,location=0,menubar=0"
);
}}
onSelectImage={(index) => {
setgalleryImages({
...galleryImages,
other: galleryImages.other.map((g, idx) =>
index === idx ? { ...g, isSelected: !g.isSelected } : g
),
});
}}
/>
</Card>
</Col>
</Row>
</div> </div>
); );
} }

View File

@@ -1,4 +1,4 @@
import { Button } from "antd"; import { Button, Space } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -49,7 +49,7 @@ export default function JobsDocumentsGallerySelectAllComponent({
}; };
return ( return (
<> <Space wrap>
<Button onClick={handleSelectAll}> <Button onClick={handleSelectAll}>
{t("general.actions.selectall")} {t("general.actions.selectall")}
</Button> </Button>
@@ -62,6 +62,6 @@ export default function JobsDocumentsGallerySelectAllComponent({
<Button onClick={handleDeselectAll}> <Button onClick={handleDeselectAll}>
{t("general.actions.deselectall")} {t("general.actions.deselectall")}
</Button> </Button>
</> </Space>
); );
} }

View File

@@ -155,9 +155,8 @@ export default function JobsFindModalComponent({
/> />
</div> </div>
)} )}
size="small"
pagination={{ position: "bottom" }} pagination={{ position: "bottom" }}
columns={columns.map((item) => ({ ...item }))} columns={columns}
rowKey="id" rowKey="id"
loading={jobsListLoading} loading={jobsListLoading}
dataSource={jobsList} dataSource={jobsList}

View File

@@ -1,5 +1,5 @@
import { SyncOutlined } from "@ant-design/icons"; import { SyncOutlined } from "@ant-design/icons";
import { Button, Input, Table } from "antd"; import { Button, Card, Input, Space, Table } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -191,10 +191,25 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
}; };
return ( return (
<div> <Card
extra={
<Space wrap>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Input.Search
placeholder={t("general.labels.search")}
onSearch={(value) => {
search.search = value;
history.push({ search: queryString.stringify(search) });
}}
enterButton
/>
</Space>
}
>
<Table <Table
loading={loading} loading={loading}
size="small"
scroll={{ x: true }} scroll={{ x: true }}
pagination={{ pagination={{
position: "top", position: "top",
@@ -206,26 +221,8 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
rowKey="id" rowKey="id"
dataSource={jobs} dataSource={jobs}
onChange={handleTableChange} onChange={handleTableChange}
title={() => {
return (
<div className="imex-table-header">
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Input.Search
className="imex-table-header__search"
placeholder={t("general.labels.search")}
onSearch={(value) => {
search.search = value;
history.push({ search: queryString.stringify(search) });
}}
enterButton
/>
</div>
);
}}
/> />
</div> </Card>
); );
} }
export default connect(mapStateToProps, mapDispatchToProps)(JobsList); export default connect(mapStateToProps, mapDispatchToProps)(JobsList);

View File

@@ -244,7 +244,6 @@ export function JobsList({ bodyshop }) {
> >
<Table <Table
loading={loading} loading={loading}
size="small"
pagination={false} pagination={false}
columns={columns} columns={columns}
rowKey="id" rowKey="id"

View File

@@ -5,7 +5,7 @@ import {
EyeInvisibleFilled, EyeInvisibleFilled,
WarningFilled, WarningFilled,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Button, Table } from "antd"; import { Button, Card, Table } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -110,28 +110,32 @@ export function JobNotesComponent({
]; ];
return ( return (
<div> <Card
title={t("jobs.labels.notes")}
extra={
<Button
onClick={() => {
setNoteUpsertContext({
actions: { refetch: refetch },
context: {
jobId: jobId,
},
});
}}
>
{t("notes.actions.new")}
</Button>
}
>
<NoteUpsertModal /> <NoteUpsertModal />
<Button
onClick={() => {
setNoteUpsertContext({
actions: { refetch: refetch },
context: {
jobId: jobId,
},
});
}}
>
{t("notes.actions.new")}
</Button>
<Table <Table
loading={loading} loading={loading}
pagination={{ position: "bottom" }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={data} dataSource={data}
/> />
</div> </Card>
); );
} }
export default connect(mapStateToProps, mapDispatchToProps)(JobNotesComponent); export default connect(mapStateToProps, mapDispatchToProps)(JobNotesComponent);

View File

@@ -1,10 +1,11 @@
import { EditFilled } from "@ant-design/icons"; import { EditFilled } from "@ant-design/icons";
import { Typography } from "antd"; import { Card, Space, Table } from "antd";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import { alphaSort } from "../../utils/sorters";
import LaborAllocationsAdjustmentEdit from "../labor-allocations-adjustment-edit/labor-allocations-adjustment-edit.component"; import LaborAllocationsAdjustmentEdit from "../labor-allocations-adjustment-edit/labor-allocations-adjustment-edit.component";
import "./labor-allocations-table.styles.scss"; import "./labor-allocations-table.styles.scss";
import { CalculateAllocationsTotals } from "./labor-allocations-table.utility"; import { CalculateAllocationsTotals } from "./labor-allocations-table.utility";
@@ -22,6 +23,15 @@ export function LaborAllocationsTable({
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [totals, setTotals] = useState([]); const [totals, setTotals] = useState([]);
const [state, setState] = useState({
sortedInfo: {
columnKey: "cost_center",
field: "cost_center",
order: "ascend",
},
filteredInfo: {},
});
useEffect(() => { useEffect(() => {
if (!!joblines && !!timetickets && !!bodyshop); if (!!joblines && !!timetickets && !!bodyshop);
setTotals( setTotals(
@@ -35,65 +45,92 @@ export function LaborAllocationsTable({
if (!jobId) setTotals([]); if (!jobId) setTotals([]);
}, [joblines, timetickets, bodyshop, adjustments, jobId]); }, [joblines, timetickets, bodyshop, adjustments, jobId]);
const columns = [
{
title: t("timetickets.fields.cost_center"),
dataIndex: "cost_center",
key: "cost_center",
defaultSortOrder: "cost_center",
sorter: (a, b) => alphaSort(a.cost_center, b.cost_center),
sortOrder:
state.sortedInfo.columnKey === "cost_center" && state.sortedInfo.order,
},
{
title: t("jobs.labels.hrs_total"),
dataIndex: "total",
key: "total",
sorter: (a, b) => a.total - b.total,
sortOrder:
state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
render: (text, record) => record.total.toFixed(1),
},
{
title: t("jobs.labels.hrs_claimed"),
dataIndex: "hrs_claimed",
key: "hrs_claimed",
sorter: (a, b) => a.claimed - b.claimed,
sortOrder:
state.sortedInfo.columnKey === "claimed" && state.sortedInfo.order,
render: (text, record) => record.claimed && record.claimed.toFixed(1),
},
{
title: t("jobs.labels.adjustments"),
dataIndex: "adjustments",
key: "adjustments",
sorter: (a, b) => a.adjustments - b.adjustments,
sortOrder:
state.sortedInfo.columnKey === "adjustments" && state.sortedInfo.order,
render: (text, record) => (
<Space wrap>
{record.adjustments.toFixed(1)}
<LaborAllocationsAdjustmentEdit
jobId={jobId}
adjustments={adjustments}
mod_lbr_ty={record.opcode}
>
<EditFilled />
</LaborAllocationsAdjustmentEdit>
</Space>
),
},
{
title: t("jobs.labels.difference"),
dataIndex: "difference",
key: "difference",
sorter: (a, b) => a.difference - b.difference,
sortOrder:
state.sortedInfo.columnKey === "difference" && state.sortedInfo.order,
render: (text, record) => (
<strong
style={{
color: record.difference > 0 ? "green" : "red",
}}
>
{record.difference}
</strong>
),
},
];
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
console.log("totals :>> ", totals);
return ( return (
<div> <Card title={t("jobs.labels.laborallocations")}>
<div className="imex-flex-row" style={{ margin: ".5rem" }}> <Table
<Typography.Title level={3}> columns={columns}
{t("jobs.labels.laborallocations")} rowKey="cost_center"
</Typography.Title> pagination={false}
<div className="labor-allocations-table"> onChange={handleTableChange}
<table> dataSource={totals}
<thead> scroll={{
<tr> x: true,
<th> }}
<strong>{t("timetickets.fields.cost_center")}</strong> />
</th> </Card>
<th>
<strong>{t("jobs.labels.hrs_total")}</strong>
</th>
<th>
<strong>{t("jobs.labels.hrs_claimed")}</strong>
</th>
<th>
<strong>{t("jobs.labels.adjustments")}</strong>
</th>
<th>
<strong>{t("jobs.labels.difference")}</strong>
</th>
</tr>
</thead>
<tbody>
{totals.map((t, idx) => (
<tr key={idx}>
<td>{t.cost_center}</td>
<td>{t.total.toFixed(1)}</td>
<td>{t.claimed.toFixed(1)}</td>
<td>
{t.adjustments.toFixed(1)}
<LaborAllocationsAdjustmentEdit
jobId={jobId}
adjustments={adjustments}
mod_lbr_ty={t.opcode}
>
<EditFilled style={{ marginLeft: ".2rem" }} />
</LaborAllocationsAdjustmentEdit>
</td>
<td>
<strong
style={{
color: t.difference > 0 ? "green" : "red",
}}
>
{t.difference}
</strong>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
); );
} }
export default connect(mapStateToProps, null)(LaborAllocationsTable); export default connect(mapStateToProps, null)(LaborAllocationsTable);

View File

@@ -1,4 +1,4 @@
import { Button, Form, Input, Switch } from "antd"; import { Form, Input, Switch } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component"; import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
@@ -13,20 +13,7 @@ export default function OwnerDetailFormComponent({ form, loading }) {
const { getFieldValue } = form; const { getFieldValue } = form;
return ( return (
<div> <div>
<div className="imex-flex-row imex-flex-row__flex-space-around"> <FormFieldsChanged form={form} />
<Button
className="imex-flex-row__margin-large"
type="primary"
key="submit"
loading={loading}
htmlType="submit"
>
{t("general.actions.save")}
</Button>
<div className="imex-flex-row__grow imex-flex-row__margin-large">
<FormFieldsChanged form={form} />
</div>
</div>
<LayoutFormRow header={t("owners.forms.name")}> <LayoutFormRow header={t("owners.forms.name")}>
<Form.Item label={t("owners.fields.ownr_title")} name="ownr_title"> <Form.Item label={t("owners.fields.ownr_title")} name="ownr_title">

View File

@@ -1,4 +1,4 @@
import { Form, notification } from "antd"; import { Button, Form, notification, PageHeader } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -37,15 +37,29 @@ function OwnerDetailFormContainer({ owner, refetch }) {
}; };
return ( return (
<Form <>
form={form} <PageHeader
onFinish={handleFinish} title={t("menus.header.owners")}
autoComplete="off" extra={
layout="vertical" <Button
initialValues={owner} type="primary"
> loading={loading}
<OwnerDetailFormComponent loading={loading} form={form} /> onClick={() => form.submit()}
</Form> >
{t("general.actions.save")}
</Button>
}
/>
<Form
form={form}
onFinish={handleFinish}
autoComplete="off"
layout="vertical"
initialValues={owner}
>
<OwnerDetailFormComponent loading={loading} form={form} />
</Form>
</>
); );
} }
export default OwnerDetailFormContainer; export default OwnerDetailFormContainer;

View File

@@ -1,4 +1,4 @@
import { Table } from "antd"; import { Card, Table } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -59,44 +59,47 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
]; ];
return ( return (
<Table <Card
title={() => ( title={t("owners.labels.relatedjobs")}
<div> extra={
<OwnerDetailUpdateJobsComponent <OwnerDetailUpdateJobsComponent
selectedJobs={selectedJobs} selectedJobs={selectedJobs}
owner={owner} owner={owner}
disabled={selectedJobs.length === 0} disabled={selectedJobs.length === 0}
/> />
</div> }
)} >
pagination={{ position: "bottom" }} <Table
columns={columns} pagination={{ position: "bottom" }}
scroll={{ x: true }} columns={columns}
rowKey="id" scroll={{ x: true }}
dataSource={owner.jobs} rowKey="id"
rowSelection={{ dataSource={owner.jobs}
onSelect: (record, selected, selectedRows) => { rowSelection={{
setSelectedJobs(selectedRows ? selectedRows.map((i) => i.id) : []); onSelect: (record, selected, selectedRows) => {
}, setSelectedJobs(selectedRows ? selectedRows.map((i) => i.id) : []);
onSelectAll: (selected, selectedRows, changeRows) => { },
setSelectedJobs( onSelectAll: (selected, selectedRows, changeRows) => {
selectedRows setSelectedJobs(
? selectedRows selectedRows
.filter((i) => ? selectedRows
bodyshop.md_ro_statuses.active_statuses.includes(i.status) .filter((i) =>
) bodyshop.md_ro_statuses.active_statuses.includes(i.status)
.map((i) => i.id) )
: [] .map((i) => i.id)
); : []
}, );
selectedRowKeys: selectedJobs, },
getCheckboxProps: (record) => ({ selectedRowKeys: selectedJobs,
disabled: bodyshop.md_ro_statuses.active_statuses getCheckboxProps: (record) => ({
? !bodyshop.md_ro_statuses.active_statuses.includes(record.status) disabled: bodyshop.md_ro_statuses.active_statuses
: true, ? !bodyshop.md_ro_statuses.active_statuses.includes(record.status)
}), : true,
}} }),
/> }}
/>
</Card>
); );
} }
export default connect(mapStateToProps, null)(OwnerDetailJobsComponent); export default connect(mapStateToProps, null)(OwnerDetailJobsComponent);

View File

@@ -67,9 +67,8 @@ export default function OwnerFindModalComponent({
<div> <div>
<Table <Table
title={() => t("owners.labels.existing_owners")} title={() => t("owners.labels.existing_owners")}
size="small"
pagination={{ position: "bottom" }} pagination={{ position: "bottom" }}
columns={columns.map((item) => ({ ...item }))} columns={columns}
rowKey="id" rowKey="id"
loading={ownersListLoading} loading={ownersListLoading}
dataSource={ownersList} dataSource={ownersList}

View File

@@ -9,11 +9,7 @@ export default function OwnerTagPopoverComponent({ job }) {
<div style={{ width: "400px" }}> <div style={{ width: "400px" }}>
<Row> <Row>
<Col span={12}> <Col span={12}>
<Descriptions <Descriptions title={t("owners.labels.fromclaim")} column={1}>
title={t("owners.labels.fromclaim")}
size="small"
column={1}
>
<Descriptions.Item key="1" label={t("jobs.fields.owner")}>{`${ <Descriptions.Item key="1" label={t("jobs.fields.owner")}>{`${
job.ownr_fn || "" job.ownr_fn || ""
} ${job.ownr_ln || ""} ${job.ownr_co_nm || ""}`}</Descriptions.Item> } ${job.ownr_ln || ""} ${job.ownr_co_nm || ""}`}</Descriptions.Item>
@@ -31,11 +27,7 @@ export default function OwnerTagPopoverComponent({ job }) {
</Descriptions> </Descriptions>
</Col> </Col>
<Col span={12}> <Col span={12}>
<Descriptions <Descriptions title={t("owners.labels.fromowner")} column={1}>
title={t("owners.labels.fromowner")}
size="small"
column={1}
>
<Descriptions.Item key="1" label={t("jobs.fields.owner")}>{`${ <Descriptions.Item key="1" label={t("jobs.fields.owner")}>{`${
job.owner.ownr_fn || "" job.owner.ownr_fn || ""
} ${job.owner.ownr_ln || ""} ${ } ${job.owner.ownr_ln || ""} ${

View File

@@ -1,5 +1,5 @@
import { SyncOutlined } from "@ant-design/icons"; import { SyncOutlined } from "@ant-design/icons";
import { Button, Input, Table } from "antd"; import { Button, Card, Input, Space, Table } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -74,38 +74,38 @@ export default function OwnersListComponent({
history.push({ search: queryString.stringify(search) }); history.push({ search: queryString.stringify(search) });
}; };
return ( return (
<Table <Card
loading={loading} title={t("menus.header.owners")}
title={() => { extra={
return ( <Space wrap>
<div className="imex-table-header"> <Button onClick={() => refetch()}>
<Button onClick={() => refetch()}> <SyncOutlined />
<SyncOutlined /> </Button>
</Button> <Input.Search
<Input.Search placeholder={t("general.labels.search")}
className="imex-table-header__search" onSearch={(value) => {
placeholder={t("general.labels.search")} search.search = value;
onSearch={(value) => { history.push({ search: queryString.stringify(search) });
search.search = value; }}
history.push({ search: queryString.stringify(search) }); enterButton
}} />
enterButton </Space>
/> }
</div> >
); <Table
}} loading={loading}
size="small" pagination={{
pagination={{ position: "top",
position: "top", pageSize: 25,
pageSize: 25, current: parseInt(page || 1),
current: parseInt(page || 1), total: total,
total: total, }}
}} columns={columns}
columns={columns} rowKey="id"
rowKey="id" scroll={{ x: true }}
scroll={{ x: true }} dataSource={owners}
dataSource={owners} onChange={handleTableChange}
onChange={handleTableChange} />
/> </Card>
); );
} }

View File

@@ -83,7 +83,7 @@ export function PartsOrderBackorderEta({
{isAlreadyBackordered && ( {isAlreadyBackordered && (
<CalendarFilled style={{ cursor: "pointer" }} onClick={handlePopover} /> <CalendarFilled style={{ cursor: "pointer" }} onClick={handlePopover} />
)} )}
{loading && <Spin size="small" />} {loading && <Spin />}
</Popover> </Popover>
); );
} }

View File

@@ -1,5 +1,15 @@
import { SyncOutlined } from "@ant-design/icons"; import { EyeFilled, SyncOutlined } from "@ant-design/icons";
import { Button, Checkbox, Input, Space, Table, Typography } from "antd"; import {
Button,
Card,
Checkbox,
Drawer,
Grid,
Input,
PageHeader,
Space,
Table,
} from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -40,6 +50,21 @@ export function PartsOrderListTableComponent({
handleOnRowClick, handleOnRowClick,
setPartsReceiveContext, setPartsReceiveContext,
}) { }) {
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
.slice(-1)[0];
const bpoints = {
xs: "100%",
sm: "100%",
md: "100%",
lg: "75%",
xl: "75%",
xxl: "65%",
};
const drawerPercentage = selectedBreakpoint
? bpoints[selectedBreakpoint[0]]
: "100%";
const responsibilityCenters = bodyshop.md_responsibility_centers; const responsibilityCenters = bodyshop.md_responsibility_centers;
const Templates = TemplateList("partsorder"); const Templates = TemplateList("partsorder");
const { t } = useTranslation(); const { t } = useTranslation();
@@ -51,6 +76,86 @@ export function PartsOrderListTableComponent({
const parts_orders = billsQuery.data ? billsQuery.data.parts_orders : []; const parts_orders = billsQuery.data ? billsQuery.data.parts_orders : [];
const { refetch } = billsQuery; const { refetch } = billsQuery;
const recordActions = (record, showView = false) => (
<Space wrap>
{showView && (
<Button onClick={() => handleOnRowClick(record)}>
<EyeFilled />
</Button>
)}
<Button
disabled={jobRO || record.return}
onClick={() => {
logImEXEvent("parts_order_receive_bill");
setPartsReceiveContext({
actions: { refetch: refetch },
context: {
jobId: job.id,
job: job,
partsorderlines: record.parts_order_lines.map((pol) => {
return {
joblineid: pol.job_line_id,
line_desc: pol.line_desc,
quantity: pol.quantity,
};
}),
},
});
}}
>
{t("parts_orders.actions.receive")}
</Button>
<Button
disabled={jobRO}
onClick={() => {
logImEXEvent("parts_order_receive_bill");
setBillEnterContext({
actions: { refetch: refetch },
context: {
job: job,
bill: {
vendorid: record.vendor.id,
is_credit_memo: record.return,
billlines: record.parts_order_lines.map((pol) => {
return {
joblineid: pol.job_line_id,
line_desc: pol.line_desc,
quantity: pol.quantity,
actual_price: pol.act_price,
cost_center: pol.jobline.part_type
? responsibilityCenters.defaults.costs[
pol.jobline.part_type
] || null
: null,
};
}),
},
},
});
}}
>
{t("parts_orders.actions.receivebill")}
</Button>
<PrintWrapper
templateObject={{
name: record.return
? Templates.parts_return_slip.key
: Templates.parts_order.key,
variables: { id: record.id },
}}
messageObject={{
subject: record.return
? Templates.parts_return_slip.subject
: Templates.parts_order.subject,
}}
/>
</Space>
);
const columns = [ const columns = [
{ {
title: t("vendors.fields.name"), title: t("vendors.fields.name"),
@@ -105,79 +210,7 @@ export function PartsOrderListTableComponent({
title: t("general.labels.actions"), title: t("general.labels.actions"),
dataIndex: "actions", dataIndex: "actions",
key: "actions", key: "actions",
render: (text, record) => ( render: (text, record) => recordActions(record, true),
<Space wrap>
<Button
disabled={jobRO || record.return}
onClick={() => {
logImEXEvent("parts_order_receive_bill");
setPartsReceiveContext({
actions: { refetch: refetch },
context: {
jobId: job.id,
job: job,
partsorderlines: record.parts_order_lines.map((pol) => {
return {
joblineid: pol.job_line_id,
line_desc: pol.line_desc,
quantity: pol.quantity,
};
}),
},
});
}}
>
{t("parts_orders.actions.receive")}
</Button>
<Button
disabled={jobRO}
onClick={() => {
logImEXEvent("parts_order_receive_bill");
setBillEnterContext({
actions: { refetch: refetch },
context: {
job: job,
bill: {
vendorid: record.vendor.id,
is_credit_memo: record.return,
billlines: record.parts_order_lines.map((pol) => {
return {
joblineid: pol.job_line_id,
line_desc: pol.line_desc,
quantity: pol.quantity,
actual_price: pol.act_price,
cost_center: pol.jobline.part_type
? responsibilityCenters.defaults.costs[
pol.jobline.part_type
] || null
: null,
};
}),
},
},
});
}}
>
{t("parts_orders.actions.receivebill")}
</Button>
<PrintWrapper
templateObject={{
name: record.return
? Templates.parts_return_slip.key
: Templates.parts_order.key,
variables: { id: record.id },
}}
messageObject={{
subject: record.return
? Templates.parts_return_slip.subject
: Templates.parts_order.subject,
}}
/>
</Space>
),
}, },
]; ];
@@ -200,15 +233,12 @@ export function PartsOrderListTableComponent({
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order, state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
}, },
{ {
title: t("parts_orders.fields.db_price"), title: t("parts_orders.fields.quantity"),
dataIndex: "db_price", dataIndex: "quantity",
key: "db_price", key: "quantity",
sorter: (a, b) => a.db_price - b.db_price, sorter: (a, b) => a.quantity - b.quantity,
sortOrder: sortOrder:
state.sortedInfo.columnKey === "db_price" && state.sortedInfo.order, state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order,
render: (text, record) => (
<CurrencyFormatter>{record.db_price}</CurrencyFormatter>
),
}, },
{ {
@@ -222,7 +252,7 @@ export function PartsOrderListTableComponent({
<CurrencyFormatter>{record.act_price}</CurrencyFormatter> <CurrencyFormatter>{record.act_price}</CurrencyFormatter>
), ),
}, },
...(selectedPartsOrderRecord && selectedPartsOrderRecord.isReturn ...(selectedPartsOrderRecord && selectedPartsOrderRecord.return
? [ ? [
{ {
title: t("parts_orders.fields.cost"), title: t("parts_orders.fields.cost"),
@@ -280,89 +310,70 @@ export function PartsOrderListTableComponent({
dataIndex: "actions", dataIndex: "actions",
key: "actions", key: "actions",
render: (text, record) => ( render: (text, record) => (
<div> <PartsOrderLineBackorderButton
<PartsOrderLineBackorderButton disabled={jobRO}
disabled={jobRO} partsOrderStatus={record.status}
partsOrderStatus={record.status} partsLineId={record.id}
partsLineId={record.id} jobLineId={record.job_line_id}
jobLineId={record.job_line_id} />
/>
</div>
), ),
}, },
]; ];
return ( return (
<div style={{ margin: ".5rem" }}> <>
<PageHeader
title={record && `${record.vendor.name} - ${record.order_number}`}
extra={recordActions(record)}
/>
<Table <Table
size="small" scroll={{ x: true, y: "50rem" }}
scroll={{ x: "50%", y: "40rem" }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={record.parts_order_lines} dataSource={record.parts_order_lines}
/> />
</div> </>
); );
}; };
return ( return (
<div> <Card
<Typography.Title level={4}> title={t("parts_orders.labels.parts_orders")}
{t("parts_orders.labels.parts_orders")} extra={
</Typography.Title> <Space wrap>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Input.Search
placeholder={t("general.labels.search")}
onChange={(e) => {
e.preventDefault();
}}
/>
</Space>
}
>
<PartsReceiveModalContainer /> <PartsReceiveModalContainer />
<Drawer
placement="right"
onClose={() => handleOnRowClick(null)}
visible={selectedpartsorder}
//getContainer={false}
style={{ position: "absolute" }}
closable
width={drawerPercentage}
>
{selectedPartsOrderRecord && rowExpander(selectedPartsOrderRecord)}
</Drawer>
<Table <Table
loading={billsQuery.loading} loading={billsQuery.loading}
size="small" scroll={{ x: true, y: "50rem" }}
title={() => (
<div className="imex-table-header">
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<div className="imex-table-header__search">
<Input.Search
placeholder={t("general.labels.search")}
onChange={(e) => {
e.preventDefault();
}}
/>
</div>
</div>
)}
scroll={{ x: "50%", y: "40rem" }}
expandedRowRender={rowExpander}
pagination={{ position: "top", defaultPageSize: 25 }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={parts_orders} dataSource={parts_orders}
onChange={handleTableChange} onChange={handleTableChange}
expandable={{
expandedRowKeys: [selectedpartsorder],
onExpand: (expanded, record) => {
handleOnRowClick(expanded ? record : null);
},
}}
rowSelection={{
onSelect: (record) => {
handleOnRowClick(record);
},
selectedRowKeys: [selectedpartsorder],
type: "radio",
}}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
handleOnRowClick(record);
}, // click row
onDoubleClick: (event) => {}, // double click row
onContextMenu: (event) => {}, // right button click row
onMouseEnter: (event) => {}, // mouse enter row
onMouseLeave: (event) => {}, // mouse leave row
};
}}
/> />
</div> </Card>
); );
} }
export default connect( export default connect(

View File

@@ -178,7 +178,6 @@ export function PaymentsListPaginated({
<div> <div>
<Table <Table
loading={loading} loading={loading}
size="small"
scroll={{ x: true }} scroll={{ x: true }}
pagination={{ pagination={{
position: "top", position: "top",

View File

@@ -55,7 +55,7 @@ export function PrintCenterItemComponent({
); );
}} }}
/> />
{loading && <Spin size="small" />} {loading && <Spin />}
</li> </li>
); );
} }

View File

@@ -20,7 +20,7 @@ export default function PrintWrapperComponent({
{children || null} {children || null}
<PrinterFilled onClick={() => handlePrint("p")} /> <PrinterFilled onClick={() => handlePrint("p")} />
<MailFilled onClick={() => handlePrint("e")} /> <MailFilled onClick={() => handlePrint("e")} />
{loading && <Spin size="small" />} {loading && <Spin />}
</Space> </Space>
); );
} }

View File

@@ -1,4 +1,4 @@
import { Input } from "antd"; import { Input, PageHeader, Space, Spin } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -17,25 +17,35 @@ export default connect(
mapDispatchToProps mapDispatchToProps
)(ProductionBoardFilters); )(ProductionBoardFilters);
export function ProductionBoardFilters({ bodyshop, filter, setFilter }) { export function ProductionBoardFilters({
bodyshop,
filter,
setFilter,
loading,
}) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div style={{ display: "flex" }}> <PageHeader
<Input.Search extra={
//value={filter.search} <Space wrap>
placeholder={t("general.labels.search")} {loading && <Spin />}
onChange={(e) => { <Input.Search
setFilter({ ...filter, search: e.target.value }); //value={filter.search}
}} placeholder={t("general.labels.search")}
/> onChange={(e) => {
<EmployeeSearchSelectComponent setFilter({ ...filter, search: e.target.value });
options={bodyshop.employees.filter((e) => e.active)} }}
value={filter.employeeId} />
placeholder={t("production.labels.employeesearch")} <EmployeeSearchSelectComponent
onChange={(emp) => setFilter({ ...filter, employeeId: emp })} options={bodyshop.employees.filter((e) => e.active)}
allowClear value={filter.employeeId}
/> placeholder={t("production.labels.employeesearch")}
</div> onChange={(emp) => setFilter({ ...filter, employeeId: emp })}
allowClear
/>
</Space>
}
></PageHeader>
); );
} }

View File

@@ -1,3 +1,9 @@
.imex-kanban-card { .imex-kanban-card {
padding: 0px !important; padding: 0px !important;
.ant-card-body {
padding: 0.8rem;
}
.ant-card-head {
padding: 0rem 0.8rem;
}
} }

View File

@@ -24,8 +24,7 @@ export default function ProductionBoardCard(technician, card) {
<Dropdown overlay={menu} trigger={["contextMenu"]}> <Dropdown overlay={menu} trigger={["contextMenu"]}>
<Card <Card
className="react-kanban-card imex-kanban-card tight-antd-rows" className="react-kanban-card imex-kanban-card tight-antd-rows"
style={{ margin: ".2rem 0rem" }} //style={{ margin: ".2rem 0rem" }}
size="small"
title={`${card.ro_number || t("general.labels.na")} - ${ title={`${card.ro_number || t("general.labels.na")} - ${
card.v_model_yr card.v_model_yr
} ${card.v_make_desc || ""} ${card.v_model_desc || ""}`} } ${card.v_make_desc || ""} ${card.v_model_desc || ""}`}

View File

@@ -116,7 +116,11 @@ export function ProductionBoardKanbanComponent({ data, bodyshop, technician }) {
return ( return (
<div> <div>
<IndefiniteLoading loading={isMoving} /> <IndefiniteLoading loading={isMoving} />
<ProductionBoardFilters filter={filter} setFilter={setFilter} /> <ProductionBoardFilters
filter={filter}
setFilter={setFilter}
loading={isMoving}
/>
<Board <Board
children={boardLanes} children={boardLanes}
disableCardDrag={isMoving} disableCardDrag={isMoving}

View File

@@ -11,11 +11,10 @@ const mapStateToProps = createStructuredSelector({
}); });
export function ProductionBoardKanbanContainer({ bodyshop }) { export function ProductionBoardKanbanContainer({ bodyshop }) {
const { loading, data } = useSubscription(SUBSCRIPTION_JOBS_IN_PRODUCTION, { const { loading, data } = useSubscription(
// variables: { SUBSCRIPTION_JOBS_IN_PRODUCTION,
// statusList: bodyshop.md_ro_statuses.production_statuses || [], {}
// }, );
});
return ( return (
<ProductionBoardKanbanComponent <ProductionBoardKanbanComponent

View File

@@ -47,7 +47,7 @@ export function ProductionListColumnStatus({ record, bodyshop }) {
> >
<div style={{ width: "100%", height: "19px", cursor: "pointer" }}> <div style={{ width: "100%", height: "19px", cursor: "pointer" }}>
{record.status} {record.status}
{loading && <Spin size="small" />} {loading && <Spin />}
</div> </div>
</Dropdown> </Dropdown>
); );

View File

@@ -53,7 +53,7 @@ export default function ProductionListDetail({ jobs }) {
{!loading && data && ( {!loading && data && (
<div> <div>
<JobEmployeeAssignments job={data.jobs_by_pk} refetch={refetch} /> <JobEmployeeAssignments job={data.jobs_by_pk} refetch={refetch} />
<Descriptions bordered size="small" column={1}> <Descriptions bordered column={1}>
<Descriptions.Item label={t("jobs.fields.ro_number")}> <Descriptions.Item label={t("jobs.fields.ro_number")}>
{theJob.ro_number || ""} {theJob.ro_number || ""}
</Descriptions.Item> </Descriptions.Item>

View File

@@ -1,5 +1,14 @@
import { SyncOutlined } from "@ant-design/icons"; import { SyncOutlined } from "@ant-design/icons";
import { Button, Dropdown, Input, Menu, Table } from "antd"; import {
Button,
Card,
Dropdown,
Input,
Menu,
PageHeader,
Space,
Table,
} from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import ReactDragListView from "react-drag-listview"; import ReactDragListView from "react-drag-listview";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -108,24 +117,7 @@ export function ProductionListTable({
.includes(searchText.toLowerCase()) .includes(searchText.toLowerCase())
); );
const tableTitle = () => ( const tableTitle = () => <div style={{ display: "flex" }}></div>;
<div style={{ display: "flex" }}>
<ProductionListColumnsAdd columnState={columnState} />
<ProductionListSaveConfigButton columns={columns} tableState={state} />
<Button
onClick={() => {
if (refetch) refetch();
}}
>
<SyncOutlined />
</Button>
<Input
onChange={(e) => setSearchText(e.target.value)}
placeholder={t("general.labels.search")}
value={searchText}
/>
</div>
);
// const handleSelectRecord = (record) => { // const handleSelectRecord = (record) => {
// if (selected !== record.id) { // if (selected !== record.id) {
@@ -139,6 +131,29 @@ export function ProductionListTable({
return ( return (
<div> <div>
<PageHeader
extra={
<Space wrap>
<ProductionListColumnsAdd columnState={columnState} />
<ProductionListSaveConfigButton
columns={columns}
tableState={state}
/>
<Button
onClick={() => {
if (refetch) refetch();
}}
>
<SyncOutlined />
</Button>
<Input
onChange={(e) => setSearchText(e.target.value)}
placeholder={t("general.labels.search")}
value={searchText}
/>
</Space>
}
/>
<ProductionListDetail jobs={dataSource} /> <ProductionListDetail jobs={dataSource} />
<ReactDragListView.DragColumn <ReactDragListView.DragColumn
onDragEnd={onDragEnd} onDragEnd={onDragEnd}
@@ -146,8 +161,8 @@ export function ProductionListTable({
handleSelector=".prod-header-dropdown" handleSelector=".prod-header-dropdown"
> >
<Table <Table
size="small"
pagination={false} pagination={false}
size="small"
components={{ components={{
header: { header: {
cell: ResizeableTitle, cell: ResizeableTitle,
@@ -169,15 +184,8 @@ export function ProductionListTable({
rowKey="id" rowKey="id"
loading={loading} loading={loading}
dataSource={dataSource} dataSource={dataSource}
scrol={{ x: true }}
onChange={handleTableChange} onChange={handleTableChange}
// rowClassName={(record, index) => {
// // const classes = [];
// // if (!!record.scheduled_completion) {
// // if (new Date(record.scheduled_completion) - Now < OneCalendarDay)
// // classes.push("production-completion-1");
// // }
// // return classes.join(" ");
// }}
/> />
</ReactDragListView.DragColumn> </ReactDragListView.DragColumn>
</div> </div>

View File

@@ -16,11 +16,10 @@ const mapStateToProps = createStructuredSelector({
export default connect(mapStateToProps, null)(ProductionListTableContainer); export default connect(mapStateToProps, null)(ProductionListTableContainer);
export function ProductionListTableContainer({ bodyshop, technician }) { export function ProductionListTableContainer({ bodyshop, technician }) {
const { loading, data } = useSubscription(SUBSCRIPTION_JOBS_IN_PRODUCTION, { const { loading, data } = useSubscription(
// variables: { SUBSCRIPTION_JOBS_IN_PRODUCTION,
// statusList: bodyshop.md_ro_statuses.production_statuses || [], {}
// }, );
});
const columnState = useState( const columnState = useState(
(bodyshop.production_config && (bodyshop.production_config &&

View File

@@ -48,7 +48,6 @@ export default function ProductionSubletsManageComponent({ subletJobLines }) {
const popContent = ( const popContent = (
<div style={{ minWidth: "20rem" }}> <div style={{ minWidth: "20rem" }}>
<List <List
size="small"
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
dataSource={subletJobLines} dataSource={subletJobLines}
renderItem={(s) => ( renderItem={(s) => (

View File

@@ -48,8 +48,7 @@ export default function ProfileShopsComponent({
</Typography.Title> </Typography.Title>
)} )}
loading={loading} loading={loading}
size="small" columns={columns}
columns={columns.map((item) => ({ ...item }))}
rowKey="id" rowKey="id"
dataSource={data} dataSource={data}
/> />

View File

@@ -63,7 +63,7 @@ export function ScheduleJobModalComponent({
}; };
return ( return (
<Row gutter={[32, 32]}> <Row gutter={[16, 16]}>
<Col span={12}> <Col span={12}>
<LayoutFormRow grow> <LayoutFormRow grow>
<Form.Item <Form.Item

View File

@@ -53,7 +53,6 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
const overlay = ( const overlay = (
<div style={{ width: "50vw", padding: "1rem" }}> <div style={{ width: "50vw", padding: "1rem" }}>
<Table <Table
size="small"
pagination={false} pagination={false}
columns={columns} columns={columns}
rowKey="id" rowKey="id"

View File

@@ -70,9 +70,8 @@ export default function ShopEmployeesListComponent({
); );
}} }}
loading={loading} loading={loading}
size="small"
pagination={{ position: "top" }} pagination={{ position: "top" }}
columns={columns.map((item) => ({ ...item }))} columns={columns}
rowKey="id" rowKey="id"
dataSource={employees} dataSource={employees}
rowSelection={{ rowSelection={{

View File

@@ -48,9 +48,8 @@ export default function ShopInfoUsersComponent() {
<div> <div>
<Table <Table
loading={loading} loading={loading}
size="small"
pagination={{ position: "top" }} pagination={{ position: "top" }}
columns={columns.map((item) => ({ ...item }))} columns={columns}
rowKey="id" rowKey="id"
dataSource={data && data.associations} dataSource={data && data.associations}
/> />

View File

@@ -82,7 +82,7 @@ export function TechClockedInList({ technician }) {
<DateTimeFormatter>{ticket.clockon}</DateTimeFormatter> <DateTimeFormatter>{ticket.clockon}</DateTimeFormatter>
</DataLabel> </DataLabel>
<DataLabel label={t("timetickets.fields.cost_center")}> <DataLabel label={t("timetickets.fields.cost_center")}>
{ticket.cost_center}{" "} {ticket.cost_center}
</DataLabel> </DataLabel>
</Card> </Card>
</List.Item> </List.Item>

View File

@@ -179,7 +179,6 @@ export function TechLookupJobsList({ bodyshop }) {
return ( return (
<Table <Table
loading={loading} loading={loading}
size="small"
pagination={false} pagination={false}
columns={columns} columns={columns}
rowKey="id" rowKey="id"

View File

@@ -33,15 +33,13 @@ export default function TimeTicketsDatesSelector() {
}; };
return ( return (
<div> <DatePicker.RangePicker
<DatePicker.RangePicker defaultValue={[
defaultValue={[ start ? moment(start) : moment().startOf("week").subtract(7, "days"),
start ? moment(start) : moment().startOf("week").subtract(7, "days"), end ? moment(end) : moment().endOf("week"),
end ? moment(end) : moment().endOf("week"), ]}
]} ranges={DatePickerRanges}
ranges={DatePickerRanges} onCalendarChange={handleChange}
onCalendarChange={handleChange} />
/>
</div>
); );
} }

View File

@@ -1,4 +1,4 @@
import { Table } from "antd"; import { Card, Space, Table } from "antd";
import moment from "moment"; import moment from "moment";
import React, { useMemo, useState } from "react"; import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -15,6 +15,7 @@ export default function TimeTicketList({
timetickets, timetickets,
refetch, refetch,
techConsole, techConsole,
jobId,
}) { }) {
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, sortedInfo: {},
@@ -157,8 +158,8 @@ export default function TimeTicketList({
dataIndex: "actions", dataIndex: "actions",
key: "actions", key: "actions",
render: (text, record) => ( render: (text, record) => (
<div> <Space wrap>
{!!techConsole && ( {techConsole && (
<TimeTicketEnterButton <TimeTicketEnterButton
actions={{ refetch }} actions={{ refetch }}
context={{ id: record.id, timeticket: record }} context={{ id: record.id, timeticket: record }}
@@ -186,7 +187,7 @@ export default function TimeTicketList({
</TimeTicketEnterButton> </TimeTicketEnterButton>
</RbacWrapper> </RbacWrapper>
)} )}
</div> </Space>
), ),
}, },
]; ];
@@ -196,37 +197,54 @@ export default function TimeTicketList({
}; };
return ( return (
<Table <Card
loading={loading} title={t("timetickets.labels.timetickets")}
size="small" extra={
pagination={{ position: "top" }} jobId &&
columns={columns.map((item) => ({ ...item }))} (techConsole ? null : (
rowKey="id" <TimeTicketEnterButton
dataSource={timetickets} actions={{ refetch }}
onChange={handleTableChange} context={{ jobId: jobId }}
summary={() => { >
return ( {t("timetickets.actions.enter")}
<Table.Summary.Row> </TimeTicketEnterButton>
<Table.Summary.Cell> ))
{t("general.labels.totals")} }
</Table.Summary.Cell> >
<Table.Summary.Cell /> <Table
<Table.Summary.Cell /> loading={loading}
<Table.Summary.Cell>{totals.productivehrs}</Table.Summary.Cell> columns={columns}
<Table.Summary.Cell>{totals.actualhrs}</Table.Summary.Cell> rowKey="id"
<Table.Summary.Cell> scroll={{
{totals.actualhrs === 0 || !totals.actualhrs x: true,
? "∞" }}
: `${((totals.productivehrs / totals.actualhrs) * 100).toFixed( dataSource={timetickets}
2 onChange={handleTableChange}
)}% ${t("timetickets.labels.efficiency")}`} summary={() => {
</Table.Summary.Cell> return (
<Table.Summary.Cell /> <Table.Summary.Row>
<Table.Summary.Cell /> <Table.Summary.Cell>
<Table.Summary.Cell /> {t("general.labels.totals")}
</Table.Summary.Row> </Table.Summary.Cell>
); <Table.Summary.Cell />
}} <Table.Summary.Cell />
/> <Table.Summary.Cell>{totals.productivehrs}</Table.Summary.Cell>
<Table.Summary.Cell>{totals.actualhrs}</Table.Summary.Cell>
<Table.Summary.Cell>
{totals.actualhrs === 0 || !totals.actualhrs
? "∞"
: `${(
(totals.productivehrs / totals.actualhrs) *
100
).toFixed(2)}% ${t("timetickets.labels.efficiency")}`}
</Table.Summary.Cell>
<Table.Summary.Cell />
<Table.Summary.Cell />
<Table.Summary.Cell />
</Table.Summary.Row>
);
}}
/>
</Card>
); );
} }

View File

@@ -1,4 +1,4 @@
import { Button, Form, Input } from "antd"; import { Form, Input } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import FormDatePicker from "../form-date-picker/form-date-picker.component"; import FormDatePicker from "../form-date-picker/form-date-picker.component";
@@ -10,20 +10,7 @@ export default function VehicleDetailFormComponent({ form, loading }) {
return ( return (
<div> <div>
<div className="imex-flex-row imex-flex-row__flex-space-around"> <FormFieldsChanged form={form} />
<Button
className="imex-flex-row__margin-large"
type="primary"
key="submit"
htmlType="submit"
loading={loading}
>
{t("general.actions.save")}
</Button>
<div className="imex-flex-row__grow imex-flex-row__margin-large">
<FormFieldsChanged form={form} />
</div>
</div>
<LayoutFormRow header={t("vehicles.forms.detail")}> <LayoutFormRow header={t("vehicles.forms.detail")}>
<Form.Item <Form.Item
@@ -136,7 +123,6 @@ export default function VehicleDetailFormComponent({ form, loading }) {
<FormDatePicker /> <FormDatePicker />
</Form.Item> </Form.Item>
{ {
//Removed as a part of IO-446. //Removed as a part of IO-446.
//No values have been captured in this field as of yet. //No values have been captured in this field as of yet.
// <Form.Item // <Form.Item

View File

@@ -1,5 +1,5 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Form, notification } from "antd"; import { Button, Form, notification, PageHeader } from "antd";
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import VehicleDetailFormComponent from "./vehicle-detail-form.component"; import VehicleDetailFormComponent from "./vehicle-detail-form.component";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -37,18 +37,32 @@ function VehicleDetailFormContainer({ vehicle, refetch }) {
}; };
return ( return (
<Form <>
onFinish={handleFinish} <PageHeader
form={form} title={t("menus.header.owners")}
autoComplete="off" extra={
layout="vertical" <Button
initialValues={{ type="primary"
...vehicle, loading={loading}
v_prod_dt: vehicle.v_prod_dt ? moment(vehicle.v_prod_dt) : null, onClick={() => form.submit()}
}} >
> {t("general.actions.save")}
<VehicleDetailFormComponent form={form} loading={loading} /> </Button>
</Form> }
/>
<Form
onFinish={handleFinish}
form={form}
autoComplete="off"
layout="vertical"
initialValues={{
...vehicle,
v_prod_dt: vehicle.v_prod_dt ? moment(vehicle.v_prod_dt) : null,
}}
>
<VehicleDetailFormComponent form={form} loading={loading} />
</Form>
</>
); );
} }

View File

@@ -1,4 +1,4 @@
import { Table } from "antd"; import { Card, Table } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -62,44 +62,45 @@ export function VehicleDetailJobsComponent({ vehicle, bodyshop }) {
]; ];
return ( return (
<Table <Card
title={() => ( title={t("vehicles.labels.relatedjobs")}
<div> extra={
<VehicleDetailUpdateJobsComponent <VehicleDetailUpdateJobsComponent
selectedJobs={selectedJobs} selectedJobs={selectedJobs}
vehicle={vehicle} vehicle={vehicle}
disabled={selectedJobs.length === 0} disabled={selectedJobs.length === 0}
/> />
</div> }
)} >
pagination={{ position: "bottom" }} <Table
columns={columns} columns={columns}
rowKey="id" rowKey="id"
scroll={{ x: true }} scroll={{ x: true }}
dataSource={vehicle.jobs} dataSource={vehicle.jobs}
rowSelection={{ rowSelection={{
onSelect: (record, selected, selectedRows) => { onSelect: (record, selected, selectedRows) => {
setSelectedJobs(selectedRows ? selectedRows.map((i) => i.id) : []); setSelectedJobs(selectedRows ? selectedRows.map((i) => i.id) : []);
}, },
onSelectAll: (selected, selectedRows, changeRows) => { onSelectAll: (selected, selectedRows, changeRows) => {
setSelectedJobs( setSelectedJobs(
selectedRows selectedRows
? selectedRows ? selectedRows
.filter((i) => .filter((i) =>
bodyshop.md_ro_statuses.active_statuses.includes(i.status) bodyshop.md_ro_statuses.active_statuses.includes(i.status)
) )
.map((i) => i.id) .map((i) => i.id)
: [] : []
); );
}, },
selectedRowKeys: selectedJobs, selectedRowKeys: selectedJobs,
getCheckboxProps: (record) => ({ getCheckboxProps: (record) => ({
disabled: bodyshop.md_ro_statuses.active_statuses disabled: bodyshop.md_ro_statuses.active_statuses
? !bodyshop.md_ro_statuses.active_statuses.includes(record.status) ? !bodyshop.md_ro_statuses.active_statuses.includes(record.status)
: true, : true,
}), }),
}} }}
/> />
</Card>
); );
} }

View File

@@ -11,11 +11,7 @@ export default function VehicleTagPopoverComponent({ job }) {
<div style={{ width: "400px" }}> <div style={{ width: "400px" }}>
<Row> <Row>
<Col span={12}> <Col span={12}>
<Descriptions <Descriptions title={t("owners.labels.fromclaim")} column={1}>
title={t("owners.labels.fromclaim")}
size="small"
column={1}
>
<Descriptions.Item key="1" label={t("jobs.fields.vehicle")}> <Descriptions.Item key="1" label={t("jobs.fields.vehicle")}>
{`${job.v_model_yr || t("general.labels.na")} ${ {`${job.v_model_yr || t("general.labels.na")} ${
job.v_color || "" job.v_color || ""
@@ -36,11 +32,7 @@ export default function VehicleTagPopoverComponent({ job }) {
</Descriptions> </Descriptions>
</Col> </Col>
<Col span={12}> <Col span={12}>
<Descriptions <Descriptions title={t("vehicles.labels.fromvehicle")} column={1}>
title={t("vehicles.labels.fromvehicle")}
size="small"
column={1}
>
<Descriptions.Item key="1" label={t("jobs.fields.vehicle")}> <Descriptions.Item key="1" label={t("jobs.fields.vehicle")}>
{`${job.vehicle.v_model_yr || t("general.labels.na")} {`${job.vehicle.v_model_yr || t("general.labels.na")}
${job.vehicle.v_make_desc || t("general.labels.na")} ${job.vehicle.v_make_desc || t("general.labels.na")}

View File

@@ -1,5 +1,5 @@
import { SyncOutlined } from "@ant-design/icons"; import { SyncOutlined } from "@ant-design/icons";
import { Button, Input, Table } from "antd"; import { Button, Card, Input, Space, Table } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -62,38 +62,38 @@ export default function VehiclesListComponent({
}; };
return ( return (
<Table <Card
loading={loading} title={t("menus.header.vehicles")}
title={() => { extra={
return ( <Space wrap>
<div className="imex-table-header"> <Button onClick={() => refetch()}>
<Button onClick={() => refetch()}> <SyncOutlined />
<SyncOutlined /> </Button>
</Button> <Input.Search
<Input.Search placeholder={t("general.labels.search")}
className="imex-table-header__search" onSearch={(value) => {
placeholder={t("general.labels.search")} search.search = value;
onSearch={(value) => { history.push({ search: queryString.stringify(search) });
search.search = value; }}
history.push({ search: queryString.stringify(search) }); enterButton
}} />
enterButton </Space>
/> }
</div> >
); <Table
}} loading={loading}
size="small" pagination={{
pagination={{ position: "top",
position: "top", pageSize: 25,
pageSize: 25, current: parseInt(page || 1),
current: parseInt(page || 1), total: total,
total: total, }}
}} columns={columns}
columns={columns} rowKey="id"
rowKey="id" scroll={{ x: true }}
scroll={{ x: true }} dataSource={vehicles}
dataSource={vehicles} onChange={handleTableChange}
onChange={handleTableChange} />
/> </Card>
); );
} }

View File

@@ -99,7 +99,6 @@ export default function VendorsListComponent({
</div> </div>
); );
}} }}
size="small"
pagination={{ position: "top" }} pagination={{ position: "top" }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"

View File

@@ -1,5 +1,5 @@
import { SyncOutlined } from "@ant-design/icons"; import { SyncOutlined } from "@ant-design/icons";
import { Button, Checkbox, Input, Space, Table, Typography } from "antd"; import { Button, Card, Checkbox, Input, Space, Table } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -201,38 +201,39 @@ export function BillsListPage({
}; };
return ( return (
<div> <Card
title={t("bills.labels.bills")}
extra={
<Space wrap>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Button
onClick={() => {
setBillEnterContext({
actions: { refetch: refetch },
context: {},
});
}}
>
{t("jobs.actions.postbills")}
</Button>
<div className="imex-table-header__search">
<Input.Search
placeholder={t("general.labels.search")}
onSearch={(value) => {
search.search = value;
history.push({ search: queryString.stringify(search) });
}}
/>
</div>
</Space>
}
>
<PartsOrderModalContainer /> <PartsOrderModalContainer />
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
<Table <Table
loading={loading} loading={loading}
size="small"
title={() => (
<div className="imex-table-header">
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Button
onClick={() => {
setBillEnterContext({
actions: { refetch: refetch },
context: {},
});
}}
>
{t("jobs.actions.postbills")}
</Button>
<div className="imex-table-header__search">
<Input.Search
placeholder={t("general.labels.search")}
onSearch={(value) => {
search.search = value;
history.push({ search: queryString.stringify(search) });
}}
/>
</div>
</div>
)}
scroll={{ x: "50%", y: "40rem" }} scroll={{ x: "50%", y: "40rem" }}
pagination={{ pagination={{
position: "top", position: "top",
@@ -245,7 +246,7 @@ export function BillsListPage({
dataSource={data} dataSource={data}
onChange={handleTableChange} onChange={handleTableChange}
/> />
</div> </Card>
); );
} }
export default connect(null, mapDispatchToProps)(BillsListPage); export default connect(null, mapDispatchToProps)(BillsListPage);

View File

@@ -1,4 +1,4 @@
import { Button } from "antd"; import { Button, Col, PageHeader, Row } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import ContractCarsContainer from "../../components/contract-cars/contract-cars.container"; import ContractCarsContainer from "../../components/contract-cars/contract-cars.container";
@@ -25,22 +25,31 @@ export default function ContractCreatePageComponent({
return ( return (
<div> <div>
{CreateButton} <Row gutter={[16, 16]}>
<ContractJobsContainer selectedJobState={selectedJobState} /> <Col span={24}>
<ContractCarsContainer selectedCarState={selectedCarState} form={form} /> <ContractJobsContainer selectedJobState={selectedJobState} />
</Col>
<div <Col span={24}>
style={{ <ContractCarsContainer
display: selectedJobState[0] && selectedCarState[0] ? "" : "none", selectedCarState={selectedCarState}
}} form={form}
> />
<ContractFormComponent </Col>
create <Col span={24}>
form={form} <div
selectedJobState={selectedJobState} style={{
/> display: selectedJobState[0] && selectedCarState[0] ? "" : "none",
</div> }}
{CreateButton} >
<ContractFormComponent
create
form={form}
selectedJobState={selectedJobState}
/>
</div>
</Col>
</Row>
<PageHeader extra={CreateButton} />
</div> </div>
); );
} }

View File

@@ -1,4 +1,4 @@
import { Button, Col, Row, Space, Typography, Form } from "antd"; import { Button, Col, Row, Space, Typography, Form, PageHeader } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -28,17 +28,14 @@ export function ContractDetailPage({
return ( return (
<div> <div>
<Row align="middle"> <Row align="middle">
<Typography.Title>{`Agreement ${ <Typography.Title></Typography.Title>
(contract && contract.agreementnumber) || ""
} - ${t((contract && contract.status) || "")}`}</Typography.Title>
</Row> </Row>
<PageHeader
<Row> title={t("contracts.labels.agreement", {
<Col span={4} offset={1}> agreement_num: contract && contract.agreementnumber,
<ContractJobBlock job={job} /> status: t(contract && contract.status),
<ContractCourtesyCarBlock courtesyCar={courtesyCar} /> })}
</Col> extra={
<Col span={18} offset={1}>
<Form.Item shouldUpdate> <Form.Item shouldUpdate>
{() => { {() => {
return ( return (
@@ -94,6 +91,16 @@ export function ContractDetailPage({
); );
}} }}
</Form.Item> </Form.Item>
}
/>
<Row gutter={[16, 16]}>
<Col sm={24} md={12}>
<ContractJobBlock job={job} />
</Col>
<Col sm={24} md={12}>
<ContractCourtesyCarBlock courtesyCar={courtesyCar} />
</Col>
<Col span={24}>
<ContractFormComponent form={form} /> <ContractFormComponent form={form} />
</Col> </Col>
</Row> </Row>

View File

@@ -8,13 +8,11 @@ export default function ContractsPageComponent({
total, total,
}) { }) {
return ( return (
<div> <ContractsList
<ContractsList loading={loading}
loading={loading} contracts={data}
contracts={data} refetch={refetch}
refetch={refetch} total={total}
total={total} />
/>
</div>
); );
} }

View File

@@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import CourtesyCarCreateFormComponent from "../../components/courtesy-car-form/courtesy-car-form.component"; import CourtesyCarCreateFormComponent from "../../components/courtesy-car-form/courtesy-car-form.component";
import CourtesyCarContractListComponent from "../../components/courtesy-car-contract-list/courtesy-car-contract-list.component"; import CourtesyCarContractListComponent from "../../components/courtesy-car-contract-list/courtesy-car-contract-list.component";
import { Col, Divider, Row } from "antd";
export default function CourtesyCarDetailPageComponent({ export default function CourtesyCarDetailPageComponent({
contracts, contracts,
@@ -9,12 +10,17 @@ export default function CourtesyCarDetailPageComponent({
totalContracts, totalContracts,
}) { }) {
return ( return (
<div> <Row gutter={[16, 16]}>
<CourtesyCarCreateFormComponent form={form} saveLoading={saveLoading} /> <Col span={24}>
<CourtesyCarContractListComponent <CourtesyCarCreateFormComponent form={form} saveLoading={saveLoading} />
contracts={contracts} </Col>
totalContracts={totalContracts} <Divider type="horizontal" />
/> <Col span={24}>
</div> <CourtesyCarContractListComponent
contracts={contracts}
totalContracts={totalContracts}
/>
</Col>
</Row>
); );
} }

View File

@@ -1,4 +1,4 @@
import { Button } from "antd"; import { Button, PageHeader } from "antd";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -7,7 +7,7 @@ import JobsAvailableTableContainer from "../../components/jobs-available-table/j
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component"; import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import { import {
setBreadcrumbs, setBreadcrumbs,
setSelectedHeader setSelectedHeader,
} from "../../redux/application/application.actions"; } from "../../redux/application/application.actions";
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
@@ -32,9 +32,14 @@ export function JobsAvailablePageContainer({
return ( return (
<RbacWrapper action="jobs:available-list"> <RbacWrapper action="jobs:available-list">
<div> <div>
<Link to="/manage/jobs/new"> <PageHeader
<Button>{t("jobs.actions.manualnew")}</Button> title={t("titles.bc.availablejobs")}
</Link> extra={
<Link to="/manage/jobs/new">
<Button>{t("jobs.actions.manualnew")}</Button>
</Link>
}
/>
<JobsAvailableTableContainer /> <JobsAvailableTableContainer />
</div> </div>
</RbacWrapper> </RbacWrapper>

View File

@@ -72,7 +72,7 @@ export function JobsChecklistViewContainer({
return ( return (
<RbacWrapper action="jobs:checklist-view"> <RbacWrapper action="jobs:checklist-view">
<Row gutter={[32, 32]}> <Row gutter={[16, 16]}>
<Col span={12}> <Col span={12}>
<Typography.Title level={4}> <Typography.Title level={4}>
{t("jobs.labels.intakechecklist")} {t("jobs.labels.intakechecklist")}

View File

@@ -1,4 +1,4 @@
import { Button, Result, Steps } from "antd"; import { Button, PageHeader, Result, Space, Steps } from "antd";
import React, { useContext, useState } from "react"; import React, { useContext, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
@@ -44,36 +44,68 @@ export default function JobsCreateComponent({ form }) {
}; };
const { Step } = Steps; const { Step } = Steps;
const ProgressButtons = () => { const ProgressButtons = ({ top }) => {
return ( return (
<div style={{ margin: "1rem" }}> <PageHeader
{pageIndex > 0 && <Button onClick={() => prev()}>Previous</Button>} extra={
{pageIndex < steps.length - 1 && ( <Space wrap>
<Button {pageIndex > 0 && <Button onClick={() => prev()}>Previous</Button>}
type="primary" {pageIndex < steps.length - 1 && (
onClick={() => { <Button
form type="primary"
.validateFields() onClick={() => {
.then((r) => { form
if (steps[pageIndex].validation) { .validateFields()
setErrorMessage(null); .then((r) => {
next(); if (steps[pageIndex].validation) {
} else { setErrorMessage(null);
setErrorMessage(steps[pageIndex].error); next();
} } else {
}) setErrorMessage(steps[pageIndex].error);
.catch((error) => console.log("error", error)); }
}} })
> .catch((error) => console.log("error", error));
Next }}
</Button> >
Next
</Button>
)}
{pageIndex === steps.length - 1 && (
<Button type="primary" htmlType="submit">
Done
</Button>
)}
</Space>
}
>
{top && (
<Steps current={pageIndex}>
{steps.map((item, idx) => (
<Step
key={item.title}
title={item.title}
style={{
cursor: "pointer",
fontWeight: idx === pageIndex && "bolder",
}}
onClick={() => {
form
.validateFields()
.then((r) => {
if (steps[pageIndex].validation) {
setErrorMessage(null);
setPageIndex(idx);
} else {
setErrorMessage(steps[pageIndex].error);
}
})
.catch((error) => console.log("error", error));
}}
/>
))}
</Steps>
)} )}
{pageIndex === steps.length - 1 && ( </PageHeader>
<Button type="primary" htmlType="submit">
Done
</Button>
)}
</div>
); );
}; };
@@ -96,29 +128,7 @@ export default function JobsCreateComponent({ form }) {
</div> </div>
) : ( ) : (
<div> <div>
<Steps current={pageIndex}> <ProgressButtons top />
{steps.map((item, idx) => (
<Step
key={item.title}
title={item.title}
style={{ cursor: "pointer" }}
onClick={() => {
form
.validateFields()
.then((r) => {
if (steps[pageIndex].validation) {
setErrorMessage(null);
setPageIndex(idx);
} else {
setErrorMessage(steps[pageIndex].error);
}
})
.catch((error) => console.log("error", error));
}}
/>
))}
</Steps>
<ProgressButtons />
{errorMessage ? ( {errorMessage ? (
<div> <div>

View File

@@ -149,6 +149,7 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
<JobCreateContext.Provider value={contextState}> <JobCreateContext.Provider value={contextState}>
<RbacWrapper action="jobs:create"> <RbacWrapper action="jobs:create">
<Form <Form
scrollToFirstError
form={form} form={form}
onFinish={handleFinish} onFinish={handleFinish}
layout="vertical" layout="vertical"
@@ -171,6 +172,14 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
prt_tax_in: true, prt_tax_in: true,
prt_tax_rt: bodyshop.bill_tax_rates.state_tax_rate / 100, prt_tax_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
}, },
PAG: {
prt_type: "PAG",
prt_discp: 0,
prt_mktyp: false,
prt_mkupp: 0,
prt_tax_in: true,
prt_tax_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
},
PAL: { PAL: {
prt_type: "PAL", prt_type: "PAL",
prt_discp: 0, prt_discp: 0,

View File

@@ -82,7 +82,13 @@ export function JobsDetailPage({
const result = await mutationUpdateJob({ const result = await mutationUpdateJob({
variables: { variables: {
jobId: job.id, jobId: job.id,
job: values, job: {
...values,
parts_tax_rates: {
...job.parts_tax_rates,
...values.parts_tax_rates,
},
},
}, },
}); });
const newTotals = await Axios.post("/job/totalsssu", { const newTotals = await Axios.post("/job/totalsssu", {

Some files were not shown because too many files have changed in this diff Show More