Simplify available jobs page IO-454
This commit is contained in:
@@ -17238,28 +17238,7 @@
|
|||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>availablenew</name>
|
<name>availablejobs</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>availablesupplements</name>
|
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
<description></description>
|
<description></description>
|
||||||
<comment></comment>
|
<comment></comment>
|
||||||
|
|||||||
@@ -1,167 +0,0 @@
|
|||||||
import { useMutation, useQuery, useApolloClient } from "@apollo/react-hooks";
|
|
||||||
import { notification } from "antd";
|
|
||||||
import Axios from "axios";
|
|
||||||
import Dinero from "dinero.js";
|
|
||||||
import React, { useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { useHistory } from "react-router-dom";
|
|
||||||
import { createStructuredSelector } from "reselect";
|
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
|
||||||
import {
|
|
||||||
DELETE_ALL_AVAILABLE_NEW_JOBS,
|
|
||||||
QUERY_AVAILABLE_NEW_JOBS,
|
|
||||||
} from "../../graphql/available-jobs.queries";
|
|
||||||
import { INSERT_NEW_JOB } from "../../graphql/jobs.queries";
|
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
|
||||||
import AlertComponent from "../alert/alert.component";
|
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
|
||||||
import JobsAvailableComponent from "./jobs-available-new.component";
|
|
||||||
import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
|
||||||
bodyshop: selectBodyshop,
|
|
||||||
});
|
|
||||||
|
|
||||||
export function JobsAvailableContainer({
|
|
||||||
deleteJob,
|
|
||||||
estDataLazyLoad,
|
|
||||||
bodyshop,
|
|
||||||
}) {
|
|
||||||
const { loading, error, data, refetch } = useQuery(QUERY_AVAILABLE_NEW_JOBS, {
|
|
||||||
fetchPolicy: "network-only",
|
|
||||||
});
|
|
||||||
|
|
||||||
const history = useHistory();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
|
||||||
const [selectedOwner, setSelectedOwner] = useState(null);
|
|
||||||
const [insertLoading, setInsertLoading] = useState(false);
|
|
||||||
const [deleteAllNewJobs] = useMutation(DELETE_ALL_AVAILABLE_NEW_JOBS);
|
|
||||||
const [insertNewJob] = useMutation(INSERT_NEW_JOB);
|
|
||||||
const client = useApolloClient();
|
|
||||||
const [loadEstData, estData] = estDataLazyLoad;
|
|
||||||
|
|
||||||
const onModalOk = async () => {
|
|
||||||
logImEXEvent("job_import_new");
|
|
||||||
|
|
||||||
setModalVisible(false);
|
|
||||||
setInsertLoading(true);
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
estData.data &&
|
|
||||||
estData.data.available_jobs_by_pk &&
|
|
||||||
estData.data.available_jobs_by_pk.est_data
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
//We don't have the right data. Error!
|
|
||||||
setInsertLoading(false);
|
|
||||||
notification["error"]({
|
|
||||||
message: t("jobs.errors.creating", { error: "No job data present." }),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newTotals = (
|
|
||||||
await Axios.post("/job/totals", {
|
|
||||||
job: {
|
|
||||||
...estData.data.available_jobs_by_pk.est_data,
|
|
||||||
joblines: estData.data.available_jobs_by_pk.est_data.joblines.data,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
).data;
|
|
||||||
|
|
||||||
let existingVehicles;
|
|
||||||
if (estData.data.available_jobs_by_pk.est_data.vehicle) {
|
|
||||||
//There's vehicle data, need to double check the VIN.
|
|
||||||
existingVehicles = await client.query({
|
|
||||||
query: SEARCH_VEHICLE_BY_VIN,
|
|
||||||
variables: {
|
|
||||||
vin: estData.data.available_jobs_by_pk.est_data.vehicle.data.v_vin,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const newJob = {
|
|
||||||
...estData.data.available_jobs_by_pk.est_data,
|
|
||||||
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
|
||||||
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
|
|
||||||
job_totals: newTotals,
|
|
||||||
queued_for_parts: true,
|
|
||||||
...(existingVehicles && existingVehicles.data.vehicles.length > 0
|
|
||||||
? { vehicleid: existingVehicles.data.vehicles[0].id, vehicle: null }
|
|
||||||
: {}),
|
|
||||||
};
|
|
||||||
|
|
||||||
insertNewJob({
|
|
||||||
variables: {
|
|
||||||
job: selectedOwner
|
|
||||||
? Object.assign(
|
|
||||||
{},
|
|
||||||
newJob,
|
|
||||||
{ owner: null },
|
|
||||||
{ ownerid: selectedOwner }
|
|
||||||
)
|
|
||||||
: newJob,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((r) => {
|
|
||||||
notification["success"]({
|
|
||||||
message: t("jobs.successes.created"),
|
|
||||||
onClick: () => {
|
|
||||||
history.push(`/manage/jobs/${r.data.insert_jobs.returning[0].id}`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
//Job has been inserted. Clean up the available jobs record.
|
|
||||||
|
|
||||||
deleteJob({
|
|
||||||
variables: { id: estData.data.available_jobs_by_pk.id },
|
|
||||||
}).then((r) => {
|
|
||||||
refetch();
|
|
||||||
setInsertLoading(false);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((r) => {
|
|
||||||
//error while inserting
|
|
||||||
notification["error"]({
|
|
||||||
message: t("jobs.errors.creating", { error: r.message }),
|
|
||||||
});
|
|
||||||
refetch();
|
|
||||||
setInsertLoading(false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onModalCancel = () => {
|
|
||||||
setModalVisible(false);
|
|
||||||
setSelectedOwner(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (error) return <AlertComponent type="error" message={error.message} />;
|
|
||||||
return (
|
|
||||||
<LoadingSpinner
|
|
||||||
loading={insertLoading}
|
|
||||||
message={t("jobs.labels.creating_new_job")}
|
|
||||||
>
|
|
||||||
<JobsAvailableComponent
|
|
||||||
loading={loading}
|
|
||||||
data={data}
|
|
||||||
refetch={refetch}
|
|
||||||
deleteJob={deleteJob}
|
|
||||||
deleteAllNewJobs={deleteAllNewJobs}
|
|
||||||
insertNewJob={insertNewJob}
|
|
||||||
estDataLazyLoad={estDataLazyLoad}
|
|
||||||
onModalCancel={onModalCancel}
|
|
||||||
onModalOk={onModalOk}
|
|
||||||
modalVisible={modalVisible}
|
|
||||||
setModalVisible={setModalVisible}
|
|
||||||
selectedOwner={selectedOwner}
|
|
||||||
setSelectedOwner={setSelectedOwner}
|
|
||||||
loadEstData={loadEstData}
|
|
||||||
estData={estData}
|
|
||||||
/>
|
|
||||||
</LoadingSpinner>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export default connect(mapStateToProps, null)(JobsAvailableContainer);
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
import {
|
|
||||||
DeleteFilled,
|
|
||||||
PlusCircleFilled,
|
|
||||||
SyncOutlined,
|
|
||||||
} from "@ant-design/icons";
|
|
||||||
import { Button, notification, Table, Input } from "antd";
|
|
||||||
import React, { useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
|
||||||
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
|
||||||
import { alphaSort } from "../../utils/sorters";
|
|
||||||
import JobsFindModalContainer from "../jobs-find-modal/jobs-find-modal.container";
|
|
||||||
|
|
||||||
export default function JobsAvailableSupplementComponent({
|
|
||||||
loading,
|
|
||||||
data,
|
|
||||||
refetch,
|
|
||||||
deleteJob,
|
|
||||||
onModalOk,
|
|
||||||
onModalCancel,
|
|
||||||
modalVisible,
|
|
||||||
setModalVisible,
|
|
||||||
selectedJob,
|
|
||||||
setSelectedJob,
|
|
||||||
deleteAllNewJobs,
|
|
||||||
loadEstData,
|
|
||||||
estData,
|
|
||||||
importOptionsState,
|
|
||||||
modalSearchState,
|
|
||||||
}) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [searchText, setSearchText] = useState("");
|
|
||||||
|
|
||||||
const [state, setState] = useState({
|
|
||||||
sortedInfo: {},
|
|
||||||
filteredInfo: { text: "" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
|
||||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.cieca_id"),
|
|
||||||
dataIndex: "cieca_id",
|
|
||||||
key: "cieca_id",
|
|
||||||
//width: "8%",
|
|
||||||
// onFilter: (value, record) => record.ro_number.includes(value),
|
|
||||||
// filteredValue: state.filteredInfo.text || null,
|
|
||||||
sorter: (a, b) => alphaSort(a, b),
|
|
||||||
sortOrder:
|
|
||||||
state.sortedInfo.columnKey === "cieca_id" && state.sortedInfo.order,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.ro_number"),
|
|
||||||
dataIndex: "job_id",
|
|
||||||
key: "job_id",
|
|
||||||
//width: "8%",
|
|
||||||
// onFilter: (value, record) => record.ro_number.includes(value),
|
|
||||||
// filteredValue: state.filteredInfo.text || null,
|
|
||||||
sorter: (a, b) => alphaSort(a, b),
|
|
||||||
sortOrder:
|
|
||||||
state.sortedInfo.columnKey === "cieca_id" && state.sortedInfo.order,
|
|
||||||
render: (text, record) => (
|
|
||||||
<div>
|
|
||||||
{(record.job && record.job_ro_number) || t("general.labels.na")}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.owner"),
|
|
||||||
dataIndex: "ownr_name",
|
|
||||||
key: "ownr_name",
|
|
||||||
ellipsis: true,
|
|
||||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
|
||||||
//width: "25%",
|
|
||||||
sortOrder:
|
|
||||||
state.sortedInfo.columnKey === "ownr_name" && state.sortedInfo.order,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.vehicle"),
|
|
||||||
dataIndex: "vehicle_info",
|
|
||||||
key: "vehicle_info",
|
|
||||||
sorter: (a, b) => alphaSort(a.vehicle_info, b.vehicle_info),
|
|
||||||
sortOrder:
|
|
||||||
state.sortedInfo.columnKey === "vehicle_info" && state.sortedInfo.order,
|
|
||||||
//ellipsis: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.clm_no"),
|
|
||||||
dataIndex: "clm_no",
|
|
||||||
key: "clm_no",
|
|
||||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
|
||||||
sortOrder:
|
|
||||||
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
|
|
||||||
//width: "12%",
|
|
||||||
//ellipsis: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.clm_total"),
|
|
||||||
dataIndex: "clm_amt",
|
|
||||||
key: "clm_amt",
|
|
||||||
sorter: (a, b) => a.clm_amt - b.clm_amt,
|
|
||||||
sortOrder:
|
|
||||||
state.sortedInfo.columnKey === "clm_amt" && state.sortedInfo.order,
|
|
||||||
render: (text, record) => (
|
|
||||||
<CurrencyFormatter>{record.clm_amt}</CurrencyFormatter>
|
|
||||||
),
|
|
||||||
//width: "12%",
|
|
||||||
//ellipsis: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.uploaded_by"),
|
|
||||||
dataIndex: "uploaded_by",
|
|
||||||
key: "uploaded_by",
|
|
||||||
sorter: (a, b) => alphaSort(a.uploaded_by, b.uploaded_by),
|
|
||||||
sortOrder:
|
|
||||||
state.sortedInfo.columnKey === "uploaded_by" && state.sortedInfo.order,
|
|
||||||
//width: "12%",
|
|
||||||
//ellipsis: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("jobs.fields.updated_at"),
|
|
||||||
dataIndex: "updated_at",
|
|
||||||
key: "updated_at",
|
|
||||||
sorter: (a, b) => new Date(a.updated_at) - new Date(b.updated_at),
|
|
||||||
sortOrder:
|
|
||||||
state.sortedInfo.columnKey === "updated_at" && state.sortedInfo.order,
|
|
||||||
render: (text, record) => (
|
|
||||||
<TimeAgoFormatter>{record.updated_at}</TimeAgoFormatter>
|
|
||||||
),
|
|
||||||
//width: "12%",
|
|
||||||
//ellipsis: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("general.labels.actions"),
|
|
||||||
key: "actions",
|
|
||||||
render: (text, record, index) => (
|
|
||||||
<span>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
deleteJob({ variables: { id: record.id } }).then((r) => {
|
|
||||||
notification["success"]({
|
|
||||||
message: t("jobs.successes.deleted"),
|
|
||||||
});
|
|
||||||
refetch();
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DeleteFilled />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
loadEstData({ variables: { id: record.id } });
|
|
||||||
modalSearchState[1](record.clm_no);
|
|
||||||
setModalVisible(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PlusCircleFilled />
|
|
||||||
</Button>
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const handleDeleteAll = () => {
|
|
||||||
deleteAllNewJobs()
|
|
||||||
.then((r) => {
|
|
||||||
notification["success"]({
|
|
||||||
message: t("jobs.successes.all_deleted", {
|
|
||||||
count: r.data.delete_available_jobs.affected_rows,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
refetch();
|
|
||||||
})
|
|
||||||
.catch((r) => {
|
|
||||||
notification["error"]({
|
|
||||||
message: t("jobs.errors.deleted") + " " + r.message,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const availableJobs = data
|
|
||||||
? searchText
|
|
||||||
? data.available_jobs.filter(
|
|
||||||
(j) =>
|
|
||||||
(j.ownr_name || "")
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchText.toLowerCase()) ||
|
|
||||||
(j.vehicle_info || "")
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchText.toLowerCase()) ||
|
|
||||||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase())
|
|
||||||
)
|
|
||||||
: data.available_jobs
|
|
||||||
: [];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<JobsFindModalContainer
|
|
||||||
loading={estData.loading}
|
|
||||||
error={estData.error}
|
|
||||||
selectedJob={selectedJob}
|
|
||||||
setSelectedJob={setSelectedJob}
|
|
||||||
importOptionsState={importOptionsState}
|
|
||||||
visible={modalVisible}
|
|
||||||
onOk={onModalOk}
|
|
||||||
onCancel={onModalCancel}
|
|
||||||
modalSearchState={modalSearchState}
|
|
||||||
/>
|
|
||||||
<Table
|
|
||||||
loading={loading}
|
|
||||||
title={() => {
|
|
||||||
return (
|
|
||||||
<div className="imex-table-header">
|
|
||||||
<strong>{t("jobs.labels.availablesupplements")}</strong>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
refetch();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SyncOutlined />
|
|
||||||
</Button>
|
|
||||||
<Button onClick={handleDeleteAll}>
|
|
||||||
{t("general.actions.deleteall")}
|
|
||||||
</Button>
|
|
||||||
<div className="imex-table-header__search">
|
|
||||||
<Input.Search
|
|
||||||
placeholder={t("general.labels.search")}
|
|
||||||
onChange={(e) => {
|
|
||||||
setSearchText(e.currentTarget.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
size="small"
|
|
||||||
pagination={{ position: "top" }}
|
|
||||||
columns={columns}
|
|
||||||
rowKey="id"
|
|
||||||
dataSource={availableJobs}
|
|
||||||
onChange={handleTableChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
import { useApolloClient, useMutation, useQuery } from "@apollo/react-hooks";
|
|
||||||
import { notification } from "antd";
|
|
||||||
import Axios from "axios";
|
|
||||||
import Dinero from "dinero.js";
|
|
||||||
import gql from "graphql-tag";
|
|
||||||
import React, { useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { useHistory } from "react-router-dom";
|
|
||||||
import { createStructuredSelector } from "reselect";
|
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
|
||||||
import {
|
|
||||||
DELETE_ALL_AVAILABLE_SUPPLEMENT_JOBS,
|
|
||||||
QUERY_AVAILABLE_SUPPLEMENT_JOBS,
|
|
||||||
} from "../../graphql/available-jobs.queries";
|
|
||||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
|
||||||
import AlertComponent from "../alert/alert.component";
|
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
|
||||||
import JobsAvailableSupplementComponent from "./jobs-available-supplement.component";
|
|
||||||
import { GetSupplementDelta } from "./jobs-available-supplement.estlines.util";
|
|
||||||
import HeaderFields from "./jobs-available-supplement.headerfields";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
|
||||||
bodyshop: selectBodyshop,
|
|
||||||
});
|
|
||||||
|
|
||||||
export function JobsAvailableSupplementContainer({
|
|
||||||
deleteJob,
|
|
||||||
estDataLazyLoad,
|
|
||||||
bodyshop,
|
|
||||||
}) {
|
|
||||||
const { loading, error, data, refetch } = useQuery(
|
|
||||||
QUERY_AVAILABLE_SUPPLEMENT_JOBS
|
|
||||||
);
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const history = useHistory();
|
|
||||||
const client = useApolloClient();
|
|
||||||
const [deleteAllNewJobs] = useMutation(DELETE_ALL_AVAILABLE_SUPPLEMENT_JOBS);
|
|
||||||
|
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
|
||||||
const [selectedJob, setSelectedJob] = useState(null);
|
|
||||||
const [insertLoading, setInsertLoading] = useState(false);
|
|
||||||
const modalSearchState = useState("");
|
|
||||||
const [updateJob] = useMutation(UPDATE_JOB);
|
|
||||||
const [loadEstData, estData] = estDataLazyLoad;
|
|
||||||
const importOptionsState = useState({ overrideHeaders: false });
|
|
||||||
const importOptions = importOptionsState[0];
|
|
||||||
|
|
||||||
const onModalOk = async () => {
|
|
||||||
logImEXEvent("job_import_supplement");
|
|
||||||
|
|
||||||
setModalVisible(false);
|
|
||||||
setInsertLoading(true);
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
estData.data &&
|
|
||||||
estData.data.available_jobs_by_pk &&
|
|
||||||
estData.data.available_jobs_by_pk.est_data
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
//We don't have the right data. Error!
|
|
||||||
setInsertLoading(false);
|
|
||||||
notification["error"]({
|
|
||||||
message: t("jobs.errors.creating", { error: "No job data present." }),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
//create upsert job
|
|
||||||
let supp = estData.data.available_jobs_by_pk.est_data;
|
|
||||||
|
|
||||||
delete supp.owner;
|
|
||||||
delete supp.vehicle;
|
|
||||||
if (importOptions.overrideHeaders) {
|
|
||||||
HeaderFields.forEach((item) => delete supp[item]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const newTotals = (
|
|
||||||
await Axios.post("/job/totals", {
|
|
||||||
job: {
|
|
||||||
...estData.data.available_jobs_by_pk.est_data,
|
|
||||||
joblines: estData.data.available_jobs_by_pk.est_data.joblines.data,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
).data;
|
|
||||||
|
|
||||||
let suppDelta = await GetSupplementDelta(
|
|
||||||
client,
|
|
||||||
selectedJob,
|
|
||||||
estData.data.available_jobs_by_pk.est_data.joblines.data
|
|
||||||
);
|
|
||||||
|
|
||||||
delete supp.joblines;
|
|
||||||
await client.mutate({
|
|
||||||
mutation: gql`
|
|
||||||
${suppDelta}
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
updateJob({
|
|
||||||
variables: {
|
|
||||||
jobId: selectedJob,
|
|
||||||
job: {
|
|
||||||
...supp,
|
|
||||||
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
|
||||||
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat(
|
|
||||||
"0.00"
|
|
||||||
),
|
|
||||||
job_totals: newTotals,
|
|
||||||
queued_for_parts: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((r) => {
|
|
||||||
notification["success"]({
|
|
||||||
message: t("jobs.successes.supplemented"),
|
|
||||||
onClick: () => {
|
|
||||||
history.push(
|
|
||||||
`/manage/jobs/${r.data.update_jobs.returning[0].id}`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
//Job has been inserted. Clean up the available jobs record.
|
|
||||||
deleteJob({
|
|
||||||
variables: { id: estData.data.available_jobs_by_pk.id },
|
|
||||||
}).then((r) => {
|
|
||||||
refetch();
|
|
||||||
setInsertLoading(false);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((r) => {
|
|
||||||
//error while inserting
|
|
||||||
notification["error"]({
|
|
||||||
message: t("jobs.errors.creating", { error: r.message }),
|
|
||||||
});
|
|
||||||
refetch();
|
|
||||||
setInsertLoading(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onModalCancel = () => {
|
|
||||||
setModalVisible(false);
|
|
||||||
modalSearchState[1]("");
|
|
||||||
setSelectedJob(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (error) return <AlertComponent type="error" message={error.message} />;
|
|
||||||
return (
|
|
||||||
<LoadingSpinner
|
|
||||||
loading={insertLoading}
|
|
||||||
message={t("jobs.labels.creating_new_job")}
|
|
||||||
>
|
|
||||||
<JobsAvailableSupplementComponent
|
|
||||||
loading={loading}
|
|
||||||
data={data}
|
|
||||||
refetch={refetch}
|
|
||||||
deleteJob={deleteJob}
|
|
||||||
updateJob={updateJob}
|
|
||||||
onModalOk={onModalOk}
|
|
||||||
onModalCancel={onModalCancel}
|
|
||||||
modalVisible={modalVisible}
|
|
||||||
setModalVisible={setModalVisible}
|
|
||||||
selectedJob={selectedJob}
|
|
||||||
setSelectedJob={setSelectedJob}
|
|
||||||
deleteAllNewJobs={deleteAllNewJobs}
|
|
||||||
loadEstData={loadEstData}
|
|
||||||
estData={estData}
|
|
||||||
importOptionsState={importOptionsState}
|
|
||||||
modalSearchState={modalSearchState}
|
|
||||||
/>
|
|
||||||
</LoadingSpinner>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export default connect(mapStateToProps, null)(JobsAvailableSupplementContainer);
|
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
import { GET_JOB_LINES_BY_PK } from "../../graphql/jobs-lines.queries";
|
import { GET_JOB_LINES_BY_PK } from "../../graphql/jobs-lines.queries";
|
||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
export const GetSupplementDelta = async (client, jobId, newLines) => {
|
export const GetSupplementDelta = async (client, jobId, newLines) => {
|
||||||
console.log("-----Begin Supplement-----");
|
console.log("-----Begin Supplement-----");
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: { joblines: existingLines },
|
data: { joblines: existingLinesFromDb },
|
||||||
} = await client.query({
|
} = await client.query({
|
||||||
query: GET_JOB_LINES_BY_PK,
|
query: GET_JOB_LINES_BY_PK,
|
||||||
variables: { id: jobId },
|
variables: { id: jobId },
|
||||||
});
|
});
|
||||||
|
const existingLines = _.cloneDeep(existingLinesFromDb);
|
||||||
const linesToInsert = [];
|
const linesToInsert = [];
|
||||||
const linesToUpdate = [];
|
const linesToUpdate = [];
|
||||||
|
|
||||||
@@ -56,11 +58,11 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const generateInsertQuery = (lineToInsert, index, jobId) => {
|
const generateInsertQuery = (lineToInsert, index, jobId) => {
|
||||||
lineToInsert.jobid = jobId;
|
|
||||||
return `
|
return `
|
||||||
insert_joblines${index}: insert_joblines(objects: ${JSON.stringify(
|
insert_joblines${index}: insert_joblines(objects: ${JSON.stringify({
|
||||||
lineToInsert
|
...lineToInsert,
|
||||||
).replace(/"(\w+)"\s*:/g, "$1:")}) {
|
jobid: jobId,
|
||||||
|
}).replace(/"(\w+)"\s*:/g, "$1:")}) {
|
||||||
returning {
|
returning {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
@@ -1,31 +1,30 @@
|
|||||||
import {
|
import {
|
||||||
DeleteFilled,
|
DeleteFilled,
|
||||||
|
DownloadOutlined,
|
||||||
PlusCircleFilled,
|
PlusCircleFilled,
|
||||||
SyncOutlined,
|
SyncOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Button, notification, Table, Input } from "antd";
|
import { useMutation } from "@apollo/react-hooks";
|
||||||
|
import { Button, 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 {
|
||||||
|
DELETE_ALL_AVAILABLE_JOBS,
|
||||||
|
DELETE_AVAILABLE_JOB,
|
||||||
|
} from "../../graphql/available-jobs.queries";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort } from "../../utils/sorters";
|
||||||
import OwnerFindModalContainer from "../owner-find-modal/owner-find-modal.container";
|
|
||||||
|
|
||||||
export default function JobsAvailableComponent({
|
export default function JobsAvailableComponent({
|
||||||
loading,
|
loading,
|
||||||
data,
|
data,
|
||||||
refetch,
|
refetch,
|
||||||
deleteJob,
|
addJobAsNew,
|
||||||
deleteAllNewJobs,
|
addJobAsSupp,
|
||||||
onModalOk,
|
|
||||||
onModalCancel,
|
|
||||||
modalVisible,
|
|
||||||
setModalVisible,
|
|
||||||
selectedOwner,
|
|
||||||
setSelectedOwner,
|
|
||||||
loadEstData,
|
|
||||||
estData,
|
|
||||||
}) {
|
}) {
|
||||||
|
const [deleteAllAvailableJobs] = useMutation(DELETE_ALL_AVAILABLE_JOBS);
|
||||||
|
const [deleteJob] = useMutation(DELETE_AVAILABLE_JOB);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
@@ -38,24 +37,41 @@ export default function JobsAvailableComponent({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
// {
|
{
|
||||||
// title: t("jobs.fields.cieca_id"),
|
title: t("jobs.fields.cieca_id"),
|
||||||
// dataIndex: "cieca_id",
|
dataIndex: "cieca_id",
|
||||||
// key: "cieca_id",
|
key: "cieca_id",
|
||||||
// //width: "8%",
|
sorter: (a, b) => alphaSort(a, b),
|
||||||
// // onFilter: (value, record) => record.ro_number.includes(value),
|
sortOrder:
|
||||||
// // filteredValue: state.filteredInfo.text || null,
|
state.sortedInfo.columnKey === "cieca_id" && state.sortedInfo.order,
|
||||||
// sorter: (a, b) => alphaSort(a, b),
|
},
|
||||||
// sortOrder:
|
{
|
||||||
// state.sortedInfo.columnKey === "cieca_id" && state.sortedInfo.order,
|
title: t("jobs.fields.ro_number"),
|
||||||
// },
|
dataIndex: "job_id",
|
||||||
|
key: "job_id",
|
||||||
|
//width: "8%",
|
||||||
|
// onFilter: (value, record) => record.ro_number.includes(value),
|
||||||
|
// filteredValue: state.filteredInfo.text || null,
|
||||||
|
sorter: (a, b) => alphaSort(a, b),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "cieca_id" && state.sortedInfo.order,
|
||||||
|
render: (text, record) =>
|
||||||
|
record.job ? (
|
||||||
|
<Link to={`/manage/jobs/${record.job.id}`}>
|
||||||
|
{(record.job && record.job_ro_number) || t("general.labels.na")}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
{(record.job && record.job_ro_number) || t("general.labels.na")}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.owner"),
|
title: t("jobs.fields.owner"),
|
||||||
dataIndex: "ownr_name",
|
dataIndex: "ownr_name",
|
||||||
key: "ownr_name",
|
key: "ownr_name",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||||
//width: "25%",
|
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "ownr_name" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "ownr_name" && state.sortedInfo.order,
|
||||||
},
|
},
|
||||||
@@ -66,7 +82,6 @@ export default function JobsAvailableComponent({
|
|||||||
sorter: (a, b) => alphaSort(a.vehicle_info, b.vehicle_info),
|
sorter: (a, b) => alphaSort(a.vehicle_info, b.vehicle_info),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "vehicle_info" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "vehicle_info" && state.sortedInfo.order,
|
||||||
//ellipsis: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.clm_no"),
|
title: t("jobs.fields.clm_no"),
|
||||||
@@ -75,8 +90,6 @@ export default function JobsAvailableComponent({
|
|||||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
|
||||||
//width: "12%",
|
|
||||||
//ellipsis: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ins_co_nm"),
|
title: t("jobs.fields.ins_co_nm"),
|
||||||
@@ -85,8 +98,6 @@ export default function JobsAvailableComponent({
|
|||||||
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm),
|
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "ins_co_nm" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "ins_co_nm" && state.sortedInfo.order,
|
||||||
//width: "12%",
|
|
||||||
//ellipsis: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.clm_total"),
|
title: t("jobs.fields.clm_total"),
|
||||||
@@ -98,8 +109,6 @@ export default function JobsAvailableComponent({
|
|||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<CurrencyFormatter>{record.clm_amt}</CurrencyFormatter>
|
<CurrencyFormatter>{record.clm_amt}</CurrencyFormatter>
|
||||||
),
|
),
|
||||||
//width: "12%",
|
|
||||||
//ellipsis: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.uploaded_by"),
|
title: t("jobs.fields.uploaded_by"),
|
||||||
@@ -108,8 +117,6 @@ export default function JobsAvailableComponent({
|
|||||||
sorter: (a, b) => alphaSort(a.uploaded_by, b.uploaded_by),
|
sorter: (a, b) => alphaSort(a.uploaded_by, b.uploaded_by),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "uploaded_by" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "uploaded_by" && state.sortedInfo.order,
|
||||||
//width: "12%",
|
|
||||||
//ellipsis: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.updated_at"),
|
title: t("jobs.fields.updated_at"),
|
||||||
@@ -121,14 +128,12 @@ export default function JobsAvailableComponent({
|
|||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<TimeAgoFormatter>{record.updated_at}</TimeAgoFormatter>
|
<TimeAgoFormatter>{record.updated_at}</TimeAgoFormatter>
|
||||||
),
|
),
|
||||||
//width: "12%",
|
|
||||||
//ellipsis: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
key: "actions",
|
key: "actions",
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<span>
|
<Space>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
deleteJob({ variables: { id: record.id } }).then((r) => {
|
deleteJob({ variables: { id: record.id } }).then((r) => {
|
||||||
@@ -142,30 +147,19 @@ export default function JobsAvailableComponent({
|
|||||||
<DeleteFilled />
|
<DeleteFilled />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => addJobAsNew(record)}
|
||||||
loadEstData({ variables: { id: record.id } });
|
disabled={record.issupplement}
|
||||||
setModalVisible(true);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<PlusCircleFilled />
|
<PlusCircleFilled />
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
<Button onClick={() => addJobAsSupp(record)}>
|
||||||
|
<DownloadOutlined />
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
),
|
),
|
||||||
//width: "12%",
|
|
||||||
//ellipsis: true
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const owner =
|
|
||||||
estData.data &&
|
|
||||||
estData.data.available_jobs_by_pk &&
|
|
||||||
estData.data.available_jobs_by_pk.est_data &&
|
|
||||||
estData.data.available_jobs_by_pk.est_data.owner &&
|
|
||||||
estData.data.available_jobs_by_pk.est_data.owner.data &&
|
|
||||||
!estData.data.available_jobs_by_pk.issupplement
|
|
||||||
? estData.data.available_jobs_by_pk.est_data.owner.data
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const availableJobs = data
|
const availableJobs = data
|
||||||
? searchText
|
? searchText
|
||||||
? data.available_jobs.filter(
|
? data.available_jobs.filter(
|
||||||
@@ -182,24 +176,13 @@ export default function JobsAvailableComponent({
|
|||||||
: [];
|
: [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Table
|
||||||
<OwnerFindModalContainer
|
loading={loading}
|
||||||
loading={estData.loading}
|
title={() => {
|
||||||
error={estData.error}
|
return (
|
||||||
owner={owner}
|
<div className="imex-table-header">
|
||||||
selectedOwner={selectedOwner}
|
<Space>
|
||||||
setSelectedOwner={setSelectedOwner}
|
<strong>{t("jobs.labels.availablejobs")}</strong>
|
||||||
visible={modalVisible}
|
|
||||||
onOk={onModalOk}
|
|
||||||
onCancel={onModalCancel}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Table
|
|
||||||
loading={loading}
|
|
||||||
title={() => {
|
|
||||||
return (
|
|
||||||
<div className="imex-table-header">
|
|
||||||
<strong>{t("jobs.labels.availablenew")}</strong>
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
refetch();
|
refetch();
|
||||||
@@ -209,7 +192,7 @@ export default function JobsAvailableComponent({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
deleteAllNewJobs()
|
deleteAllAvailableJobs()
|
||||||
.then((r) => {
|
.then((r) => {
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("jobs.successes.all_deleted", {
|
message: t("jobs.successes.all_deleted", {
|
||||||
@@ -227,24 +210,24 @@ export default function JobsAvailableComponent({
|
|||||||
>
|
>
|
||||||
{t("general.actions.deleteall")}
|
{t("general.actions.deleteall")}
|
||||||
</Button>
|
</Button>
|
||||||
<div className="imex-table-header__search">
|
</Space>
|
||||||
<Input.Search
|
<div className="imex-table-header__search">
|
||||||
placeholder={t("general.labels.search")}
|
<Input.Search
|
||||||
onChange={(e) => {
|
placeholder={t("general.labels.search")}
|
||||||
setSearchText(e.currentTarget.value);
|
onChange={(e) => {
|
||||||
}}
|
setSearchText(e.currentTarget.value);
|
||||||
/>
|
}}
|
||||||
</div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
</div>
|
||||||
}}
|
);
|
||||||
size="small"
|
}}
|
||||||
pagination={{ position: "top" }}
|
size="small"
|
||||||
columns={columns}
|
pagination={{ position: "top" }}
|
||||||
rowKey="id"
|
columns={columns}
|
||||||
dataSource={availableJobs}
|
rowKey="id"
|
||||||
onChange={handleTableChange}
|
dataSource={availableJobs}
|
||||||
/>
|
onChange={handleTableChange}
|
||||||
</div>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,315 @@
|
|||||||
|
import {
|
||||||
|
useApolloClient,
|
||||||
|
useLazyQuery,
|
||||||
|
useMutation,
|
||||||
|
useQuery
|
||||||
|
} from "@apollo/react-hooks";
|
||||||
|
import { notification } from "antd";
|
||||||
|
import Axios from "axios";
|
||||||
|
import Dinero from "dinero.js";
|
||||||
|
import gql from "graphql-tag";
|
||||||
|
import _ from "lodash";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { useHistory } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
|
import {
|
||||||
|
DELETE_AVAILABLE_JOB,
|
||||||
|
QUERY_AVAILABLE_JOBS,
|
||||||
|
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK
|
||||||
|
} from "../../graphql/available-jobs.queries";
|
||||||
|
import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||||
|
import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import JobsFindModalContainer from "../jobs-find-modal/jobs-find-modal.container";
|
||||||
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
import OwnerFindModalContainer from "../owner-find-modal/owner-find-modal.container";
|
||||||
|
import { GetSupplementDelta } from "./jobs-available-supplement.estlines.util";
|
||||||
|
import HeaderFields from "./jobs-available-supplement.headerfields";
|
||||||
|
import JobsAvailableTableComponent from "./jobs-available-table.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function JobsAvailableContainer({ bodyshop }) {
|
||||||
|
const { loading, error, data, refetch } = useQuery(QUERY_AVAILABLE_JOBS, {
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
});
|
||||||
|
|
||||||
|
const history = useHistory();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [ownerModalVisible, setOwnerModalVisible] = useState(false);
|
||||||
|
const [jobModalVisible, setJobModalVisible] = useState(false);
|
||||||
|
|
||||||
|
const [selectedJob, setSelectedJob] = useState(null);
|
||||||
|
const [selectedOwner, setSelectedOwner] = useState(null);
|
||||||
|
const [insertLoading, setInsertLoading] = useState(false);
|
||||||
|
const [deleteJob] = useMutation(DELETE_AVAILABLE_JOB);
|
||||||
|
const [updateJob] = useMutation(UPDATE_JOB);
|
||||||
|
|
||||||
|
const [insertNewJob] = useMutation(INSERT_NEW_JOB);
|
||||||
|
const client = useApolloClient();
|
||||||
|
|
||||||
|
const estDataLazyLoad = useLazyQuery(QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK);
|
||||||
|
const [loadEstData, estData] = estDataLazyLoad;
|
||||||
|
|
||||||
|
const importOptionsState = useState({ overrideHeaders: false });
|
||||||
|
const importOptions = importOptionsState[0];
|
||||||
|
const modalSearchState = useState("");
|
||||||
|
|
||||||
|
const onOwnerFindModalOk = async () => {
|
||||||
|
logImEXEvent("job_import_new");
|
||||||
|
|
||||||
|
setOwnerModalVisible(false);
|
||||||
|
setInsertLoading(true);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
estData.data &&
|
||||||
|
estData.data.available_jobs_by_pk &&
|
||||||
|
estData.data.available_jobs_by_pk.est_data
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
//We don't have the right data. Error!
|
||||||
|
setInsertLoading(false);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("jobs.errors.creating", { error: "No job data present." }),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTotals = (
|
||||||
|
await Axios.post("/job/totals", {
|
||||||
|
job: {
|
||||||
|
...estData.data.available_jobs_by_pk.est_data,
|
||||||
|
joblines: estData.data.available_jobs_by_pk.est_data.joblines.data,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
|
||||||
|
let existingVehicles;
|
||||||
|
if (estData.data.available_jobs_by_pk.est_data.vehicle) {
|
||||||
|
//There's vehicle data, need to double check the VIN.
|
||||||
|
existingVehicles = await client.query({
|
||||||
|
query: SEARCH_VEHICLE_BY_VIN,
|
||||||
|
variables: {
|
||||||
|
vin: estData.data.available_jobs_by_pk.est_data.vehicle.data.v_vin,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const newJob = {
|
||||||
|
...estData.data.available_jobs_by_pk.est_data,
|
||||||
|
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
||||||
|
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
|
||||||
|
job_totals: newTotals,
|
||||||
|
queued_for_parts: true,
|
||||||
|
...(existingVehicles && existingVehicles.data.vehicles.length > 0
|
||||||
|
? { vehicleid: existingVehicles.data.vehicles[0].id, vehicle: null }
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
insertNewJob({
|
||||||
|
variables: {
|
||||||
|
job: selectedOwner
|
||||||
|
? Object.assign(
|
||||||
|
{},
|
||||||
|
newJob,
|
||||||
|
{ owner: null },
|
||||||
|
{ ownerid: selectedOwner }
|
||||||
|
)
|
||||||
|
: newJob,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((r) => {
|
||||||
|
notification["success"]({
|
||||||
|
message: t("jobs.successes.created"),
|
||||||
|
onClick: () => {
|
||||||
|
history.push(`/manage/jobs/${r.data.insert_jobs.returning[0].id}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
//Job has been inserted. Clean up the available jobs record.
|
||||||
|
|
||||||
|
deleteJob({
|
||||||
|
variables: { id: estData.data.available_jobs_by_pk.id },
|
||||||
|
}).then((r) => {
|
||||||
|
refetch();
|
||||||
|
setInsertLoading(false);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((r) => {
|
||||||
|
//error while inserting
|
||||||
|
notification["error"]({
|
||||||
|
message: t("jobs.errors.creating", { error: r.message }),
|
||||||
|
});
|
||||||
|
refetch();
|
||||||
|
setInsertLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onJobFindModalOk = async () => {
|
||||||
|
logImEXEvent("job_import_supplement");
|
||||||
|
|
||||||
|
setJobModalVisible(false);
|
||||||
|
setInsertLoading(true);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
estData.data &&
|
||||||
|
estData.data.available_jobs_by_pk &&
|
||||||
|
estData.data.available_jobs_by_pk.est_data
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
//We don't have the right data. Error!
|
||||||
|
setInsertLoading(false);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("jobs.errors.creating", { error: "No job data present." }),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
//create upsert job
|
||||||
|
let supp = _.cloneDeep(estData.data.available_jobs_by_pk.est_data);
|
||||||
|
|
||||||
|
delete supp.owner;
|
||||||
|
delete supp.vehicle;
|
||||||
|
if (importOptions.overrideHeaders) {
|
||||||
|
HeaderFields.forEach((item) => delete supp[item]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTotals = (
|
||||||
|
await Axios.post("/job/totals", {
|
||||||
|
job: {
|
||||||
|
...estData.data.available_jobs_by_pk.est_data,
|
||||||
|
joblines: estData.data.available_jobs_by_pk.est_data.joblines.data,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
|
||||||
|
let suppDelta = await GetSupplementDelta(
|
||||||
|
client,
|
||||||
|
selectedJob,
|
||||||
|
estData.data.available_jobs_by_pk.est_data.joblines.data
|
||||||
|
);
|
||||||
|
|
||||||
|
delete supp.joblines;
|
||||||
|
await client.mutate({
|
||||||
|
mutation: gql`
|
||||||
|
${suppDelta}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
updateJob({
|
||||||
|
variables: {
|
||||||
|
jobId: selectedJob,
|
||||||
|
job: {
|
||||||
|
...supp,
|
||||||
|
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
||||||
|
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat(
|
||||||
|
"0.00"
|
||||||
|
),
|
||||||
|
job_totals: newTotals,
|
||||||
|
queued_for_parts: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((r) => {
|
||||||
|
notification["success"]({
|
||||||
|
message: t("jobs.successes.supplemented"),
|
||||||
|
onClick: () => {
|
||||||
|
history.push(
|
||||||
|
`/manage/jobs/${r.data.update_jobs.returning[0].id}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
//Job has been inserted. Clean up the available jobs record.
|
||||||
|
deleteJob({
|
||||||
|
variables: { id: estData.data.available_jobs_by_pk.id },
|
||||||
|
}).then((r) => {
|
||||||
|
refetch();
|
||||||
|
setInsertLoading(false);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((r) => {
|
||||||
|
//error while inserting
|
||||||
|
notification["error"]({
|
||||||
|
message: t("jobs.errors.creating", { error: r.message }),
|
||||||
|
});
|
||||||
|
refetch();
|
||||||
|
setInsertLoading(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const owner =
|
||||||
|
estData.data &&
|
||||||
|
estData.data.available_jobs_by_pk &&
|
||||||
|
estData.data.available_jobs_by_pk.est_data &&
|
||||||
|
estData.data.available_jobs_by_pk.est_data.owner &&
|
||||||
|
estData.data.available_jobs_by_pk.est_data.owner.data &&
|
||||||
|
!estData.data.available_jobs_by_pk.issupplement
|
||||||
|
? estData.data.available_jobs_by_pk.est_data.owner.data
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const onOwnerModalCancel = () => {
|
||||||
|
setOwnerModalVisible(false);
|
||||||
|
setSelectedOwner(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onJobModalCancel = () => {
|
||||||
|
setJobModalVisible(false);
|
||||||
|
modalSearchState[1]("");
|
||||||
|
setSelectedJob(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addJobAsNew = (record) => {
|
||||||
|
loadEstData({ variables: { id: record.id } });
|
||||||
|
setOwnerModalVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addJobAsSupp = (record) => {
|
||||||
|
loadEstData({ variables: { id: record.id } });
|
||||||
|
modalSearchState[1](record.clm_no);
|
||||||
|
setJobModalVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (error) return <AlertComponent type="error" message={error.message} />;
|
||||||
|
return (
|
||||||
|
<LoadingSpinner
|
||||||
|
loading={insertLoading}
|
||||||
|
message={t("jobs.labels.creating_new_job")}
|
||||||
|
>
|
||||||
|
<OwnerFindModalContainer
|
||||||
|
loading={estData.loading}
|
||||||
|
error={estData.error}
|
||||||
|
owner={owner}
|
||||||
|
selectedOwner={selectedOwner}
|
||||||
|
setSelectedOwner={setSelectedOwner}
|
||||||
|
visible={ownerModalVisible}
|
||||||
|
onOk={onOwnerFindModalOk}
|
||||||
|
onCancel={onOwnerModalCancel}
|
||||||
|
/>
|
||||||
|
<JobsFindModalContainer
|
||||||
|
loading={estData.loading}
|
||||||
|
error={estData.error}
|
||||||
|
selectedJob={selectedJob}
|
||||||
|
setSelectedJob={setSelectedJob}
|
||||||
|
importOptionsState={importOptionsState}
|
||||||
|
visible={jobModalVisible}
|
||||||
|
onOk={onJobFindModalOk}
|
||||||
|
onCancel={onJobModalCancel}
|
||||||
|
modalSearchState={modalSearchState}
|
||||||
|
/>
|
||||||
|
<JobsAvailableTableComponent
|
||||||
|
loading={loading}
|
||||||
|
data={data}
|
||||||
|
refetch={refetch}
|
||||||
|
addJobAsNew={addJobAsNew}
|
||||||
|
addJobAsSupp={addJobAsSupp}
|
||||||
|
/>
|
||||||
|
</LoadingSpinner>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default connect(mapStateToProps, null)(JobsAvailableContainer);
|
||||||
@@ -1,5 +1,29 @@
|
|||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
|
export const QUERY_AVAILABLE_JOBS = gql`
|
||||||
|
query QUERY_AVAILABLE_JOBS {
|
||||||
|
available_jobs(order_by: { updated_at: desc }) {
|
||||||
|
cieca_id
|
||||||
|
clm_amt
|
||||||
|
clm_no
|
||||||
|
created_at
|
||||||
|
id
|
||||||
|
issupplement
|
||||||
|
ownr_name
|
||||||
|
source_system
|
||||||
|
supplement_number
|
||||||
|
updated_at
|
||||||
|
uploaded_by
|
||||||
|
ins_co_nm
|
||||||
|
vehicle_info
|
||||||
|
job {
|
||||||
|
id
|
||||||
|
ro_number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const QUERY_AVAILABLE_NEW_JOBS = gql`
|
export const QUERY_AVAILABLE_NEW_JOBS = gql`
|
||||||
query QUERY_AVAILABLE_NEW_JOBS {
|
query QUERY_AVAILABLE_NEW_JOBS {
|
||||||
available_jobs(
|
available_jobs(
|
||||||
@@ -58,6 +82,14 @@ export const DELETE_AVAILABLE_JOB = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const DELETE_ALL_AVAILABLE_JOBS = gql`
|
||||||
|
mutation DELETE_ALL_AVAILABLE_JOBS {
|
||||||
|
delete_available_jobs(where: {}) {
|
||||||
|
affected_rows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const DELETE_ALL_AVAILABLE_NEW_JOBS = gql`
|
export const DELETE_ALL_AVAILABLE_NEW_JOBS = gql`
|
||||||
mutation DELETE_ALL_AVAILABLE_NEW_JOBS {
|
mutation DELETE_ALL_AVAILABLE_NEW_JOBS {
|
||||||
delete_available_jobs(where: { issupplement: { _eq: false } }) {
|
delete_available_jobs(where: { issupplement: { _eq: false } }) {
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import { Button } from "antd";
|
|
||||||
import JobsAvailableContainer from "../../components/jobs-available-new/jobs-available-new.container";
|
|
||||||
import JobsAvailableSupplementContainer from "../../components/jobs-available-supplement/jobs-available-supplement.container";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
export default function JobsAvailablePageComponent({
|
|
||||||
deleteJob,
|
|
||||||
estDataLazyLoad
|
|
||||||
}) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Link to="/manage/jobs/new">
|
|
||||||
<Button>{t("jobs.actions.manualnew")}</Button>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<JobsAvailableContainer
|
|
||||||
deleteJob={deleteJob}
|
|
||||||
estDataLazyLoad={estDataLazyLoad}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<JobsAvailableSupplementContainer
|
|
||||||
deleteJob={deleteJob}
|
|
||||||
estDataLazyLoad={estDataLazyLoad}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,15 @@
|
|||||||
|
import { Button } from "antd";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useMutation, useLazyQuery } from "@apollo/react-hooks";
|
|
||||||
import {
|
|
||||||
DELETE_AVAILABLE_JOB,
|
|
||||||
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK,
|
|
||||||
} from "../../graphql/available-jobs.queries";
|
|
||||||
import JobsAvailablePageComponent from "./jobs-available.page.component";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import JobsAvailableTableContainer from "../../components/jobs-available-table/jobs-available-table.container";
|
||||||
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) => ({
|
||||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||||
@@ -22,11 +19,8 @@ export function JobsAvailablePageContainer({
|
|||||||
setBreadcrumbs,
|
setBreadcrumbs,
|
||||||
setSelectedHeader,
|
setSelectedHeader,
|
||||||
}) {
|
}) {
|
||||||
const [deleteJob] = useMutation(DELETE_AVAILABLE_JOB);
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const estDataLazyLoad = useLazyQuery(QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = t("titles.jobsavailable");
|
document.title = t("titles.jobsavailable");
|
||||||
setSelectedHeader("availablejobs");
|
setSelectedHeader("availablejobs");
|
||||||
@@ -38,10 +32,10 @@ export function JobsAvailablePageContainer({
|
|||||||
return (
|
return (
|
||||||
<RbacWrapper action="jobs:available-list">
|
<RbacWrapper action="jobs:available-list">
|
||||||
<div>
|
<div>
|
||||||
<JobsAvailablePageComponent
|
<Link to="/manage/jobs/new">
|
||||||
deleteJob={deleteJob}
|
<Button>{t("jobs.actions.manualnew")}</Button>
|
||||||
estDataLazyLoad={estDataLazyLoad}
|
</Link>
|
||||||
/>
|
<JobsAvailableTableContainer />
|
||||||
</div>
|
</div>
|
||||||
</RbacWrapper>
|
</RbacWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1056,8 +1056,7 @@
|
|||||||
"associationwarning": "Any changes to associations will require updating the data from the new parent record to the job.",
|
"associationwarning": "Any changes to associations will require updating the data from the new parent record to the job.",
|
||||||
"audit": "Audit Trail",
|
"audit": "Audit Trail",
|
||||||
"available": "Available",
|
"available": "Available",
|
||||||
"availablenew": "Available New Jobs",
|
"availablejobs": "Available Jobs",
|
||||||
"availablesupplements": "Available Supplements",
|
|
||||||
"cards": {
|
"cards": {
|
||||||
"customer": "Customer Information",
|
"customer": "Customer Information",
|
||||||
"damage": "Area of Damage",
|
"damage": "Area of Damage",
|
||||||
|
|||||||
@@ -1056,8 +1056,7 @@
|
|||||||
"associationwarning": "",
|
"associationwarning": "",
|
||||||
"audit": "",
|
"audit": "",
|
||||||
"available": "",
|
"available": "",
|
||||||
"availablenew": "",
|
"availablejobs": "",
|
||||||
"availablesupplements": "",
|
|
||||||
"cards": {
|
"cards": {
|
||||||
"customer": "Información al cliente",
|
"customer": "Información al cliente",
|
||||||
"damage": "Área de Daño",
|
"damage": "Área de Daño",
|
||||||
|
|||||||
@@ -1056,8 +1056,7 @@
|
|||||||
"associationwarning": "",
|
"associationwarning": "",
|
||||||
"audit": "",
|
"audit": "",
|
||||||
"available": "",
|
"available": "",
|
||||||
"availablenew": "",
|
"availablejobs": "",
|
||||||
"availablesupplements": "",
|
|
||||||
"cards": {
|
"cards": {
|
||||||
"customer": "Informations client",
|
"customer": "Informations client",
|
||||||
"damage": "Zone de dommages",
|
"damage": "Zone de dommages",
|
||||||
|
|||||||
Reference in New Issue
Block a user