BOD-52 #comment Implemented job updating based on owner fields. Currently, which fields are updated are hardcoded.

This commit is contained in:
Patrick Fic
2020-03-23 15:35:59 -07:00
parent edba734605
commit f04ba766ad
9 changed files with 376 additions and 43 deletions

View File

@@ -11,7 +11,7 @@
<preset_collections/> <preset_collections/>
<framework>react-intl</framework> <framework>react-intl</framework>
<filename>bodyshop_translations.babel</filename> <filename>bodyshop_translations.babel</filename>
<source_root_dir></source_root_dir> <source_root_dir>client</source_root_dir>
<folder_node> <folder_node>
<name></name> <name></name>
<children> <children>
@@ -1723,6 +1723,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>open_statuses</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>
<concept_node> <concept_node>
@@ -1751,6 +1772,48 @@
<folder_node> <folder_node>
<name>labels</name> <name>labels</name>
<children> <children>
<concept_node>
<name>alljobstatuses</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>allopenjobstatuses</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>jobstatuses</name> <name>jobstatuses</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -7986,6 +8049,32 @@
<folder_node> <folder_node>
<name>owners</name> <name>owners</name>
<children> <children>
<folder_node>
<name>actions</name>
<children>
<concept_node>
<name>update</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>
</folder_node>
<folder_node> <folder_node>
<name>errors</name> <name>errors</name>
<children> <children>
@@ -8398,6 +8487,69 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>fromclaim</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>fromowner</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>updateowner</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>
@@ -10266,7 +10418,7 @@
</editor_configuration> </editor_configuration>
<primary_language>en-US</primary_language> <primary_language>en-US</primary_language>
<configuration> <configuration>
<definitions></definitions> <definitions>client/src</definitions>
<indent>tab</indent> <indent>tab</indent>
<format>namespaced-json</format> <format>namespaced-json</format>
<support_arrays>true</support_arrays> <support_arrays>true</support_arrays>

View File

@@ -1,10 +1,20 @@
import React from "react";
import { Table } from "antd"; import { Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
export default function OwnerDetailJobsComponent({ owner }) { import OwnerDetailUpdateJobsComponent from "../owner-detail-update-jobs/owner-detail-update-jobs.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
function OwnerDetailJobsComponent({ bodyshop, owner }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [selectedJobs, setSelectedJobs] = useState([]);
const columns = [ const columns = [
{ {
title: t("jobs.fields.ro_number"), title: t("jobs.fields.ro_number"),
@@ -50,10 +60,31 @@ export default function OwnerDetailJobsComponent({ owner }) {
return ( return (
<Table <Table
title={() => (
<div>
<OwnerDetailUpdateJobsComponent
selectedJobs={selectedJobs}
owner={owner}
/>
</div>
)}
pagination={{ position: "bottom" }} pagination={{ position: "bottom" }}
columns={columns.map(item => ({ ...item }))} columns={columns.map(item => ({ ...item }))}
rowKey="id" rowKey='id'
dataSource={owner.jobs} dataSource={owner.jobs}
rowSelection={{
onSelect: props => {
setSelectedJobs([...selectedJobs, props.id]);
},
// type: "radio",
selectedRowKeys: selectedJobs,
getCheckboxProps: record => ({
disabled: bodyshop.md_ro_statuses.open_statuses
? !bodyshop.md_ro_statuses.open_statuses.includes(record.status)
: true
})
}}
/> />
); );
} }
export default connect(mapStateToProps, null)(OwnerDetailJobsComponent);

View File

@@ -0,0 +1,37 @@
import React from "react";
import { Button } from "antd";
import { useTranslation } from "react-i18next";
import { useMutation } from "@apollo/react-hooks";
import { UPDATE_JOBS } from "../../graphql/jobs.queries";
export default function OwnerDetailUpdateJobsComponent({
owner,
selectedJobs
}) {
const { t } = useTranslation();
const [updateJobs] = useMutation(UPDATE_JOBS);
const handlecClick = e => {
updateJobs({
variables: {
jobIds: selectedJobs,
fields: {
ownr_addr1: owner["ownr_addr1"],
ownr_addr2: owner["ownr_addr2"],
ownr_co_nm: owner["ownr_co_nm"],
ownr_city: owner["ownr_city"],
ownr_ctry: owner["ownr_ctry"],
ownr_ea: owner["ownr_ea"],
ownr_fn: owner["ownr_fn"],
ownr_ph1: owner["ownr_ph1"],
ownr_ln: owner["ownr_ln"],
ownr_ph2: owner["ownr_ph2"],
ownr_st: owner["ownr_st"],
ownr_title: owner["ownr_title"],
ownr_zip: owner["ownr_zip"]
}
}
});
};
return <Button onClick={handlecClick}>{t("owners.actions.update")}</Button>;
}

View File

@@ -1,26 +1,75 @@
import { Button, Col, Popover, Row, Tag } from "antd"; import { Button, Col, Popover, Row, Tag, Descriptions } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import PhoneFormatter from "../../utils/PhoneFormatter";
export default function OwnerTagPopoverComponent({ job }) { export default function OwnerTagPopoverComponent({ job }) {
const { t } = useTranslation(); const { t } = useTranslation();
const content = ( const content = (
<div> <div style={{ width: "400px" }}>
The Content
<Row> <Row>
<Col span={12}>Claim Info</Col> <Col span={12}>
<Col span={12}>Owner Info</Col> <Descriptions
title={t("owners.labels.fromclaim")}
size='small'
column={1}>
<Descriptions.Item
key='1'
label={t("jobs.fields.owner")}>{`${job.ownr_fn ||
""} ${job.ownr_ln || ""} ${job.ownr_co_nm ||
""}`}</Descriptions.Item>
<Descriptions.Item key='2' label={t("jobs.fields.ownr_ph1")}>
<PhoneFormatter>{job.ownr_ph1 || ""}</PhoneFormatter>
</Descriptions.Item>
<Descriptions.Item key='3' label={t("owners.fields.address")}>
{`${job.ownr_addr1 || ""} ${job.ownr_addr2 ||
""} ${job.ownr_city || ""} ${job.ownr_st ||
""} ${job.ownr_zip || ""} ${job.ownr_ctry ||
""} ${job.ownr_city || ""}`}
</Descriptions.Item>
<Descriptions.Item key='4' label={t("owners.fields.ownr_ea")}>
{job.ownr_ea || ""}
{
//TODO Should add an email formatter.
}
</Descriptions.Item>
</Descriptions>
</Col>
<Col span={12}>
<Descriptions
title={t("owners.labels.fromowner")}
size='small'
column={1}>
<Descriptions.Item key='1' label={t("jobs.fields.owner")}>{`${job
.owner.ownr_fn || ""} ${job.owner.ownr_ln || ""} ${job.owner
.ownr_co_nm || ""}`}</Descriptions.Item>
<Descriptions.Item key='2' label={t("jobs.fields.ownr_ph1")}>
<PhoneFormatter>{job.owner.ownr_ph1 || ""}</PhoneFormatter>
</Descriptions.Item>
<Descriptions.Item key='3' label={t("owners.fields.address")}>
{`${job.owner.ownr_addr1 || ""} ${job.owner.ownr_addr2 ||
""} ${job.owner.ownr_city || ""} ${job.owner.ownr_st ||
""} ${job.owner.ownr_zip || ""} ${job.owner.ownr_ctry ||
""} ${job.owner.ownr_city || ""}`}
</Descriptions.Item>
<Descriptions.Item key='4' label={t("owners.fields.ownr_ea")}>
{job.owner.ownr_ea || ""}
{
//TODO Should add an email formatter.
}
</Descriptions.Item>
</Descriptions>
</Col>
</Row> </Row>
<Link to={`/manage/owners/${job.owner.id}`}> <Link to={`/manage/owners/${job.owner.id}`}>
<Button>{t("owners.actions.update")}</Button> <Button>{t("owners.labels.updateowner")}</Button>
</Link> </Link>
</div> </div>
); );
return ( return (
<Popover placement="bottom" content={content}> <Popover placement='bottom' content={content}>
<Tag color="red"> <Tag color='red'>
{job.owner {job.owner
? `${job.ownr_co_nm || ""}${job.ownr_fn || ""} ${job.ownr_ln || ""}` ? `${job.ownr_co_nm || ""}${job.ownr_fn || ""} ${job.ownr_ln || ""}`
: t("jobs.errors.noowner")} : t("jobs.errors.noowner")}

View File

@@ -6,7 +6,7 @@ import styled from "styled-components";
const SelectorDiv = styled.div` const SelectorDiv = styled.div`
.ant-form-item .ant-select { .ant-form-item .ant-select {
width: 125px; width: 200px;
} }
`; `;
//TODO Fix up styles. //TODO Fix up styles.
@@ -24,7 +24,8 @@ export default function ShopInfoROStatusComponent({ form }) {
return ( return (
<div> <div>
<Row> <Row>
<Col span={12}> <Col span={8}>
{t("bodyshop.labels.alljobstatuses")}
<Form.List name={["md_ro_statuses", "statuses"]}> <Form.List name={["md_ro_statuses", "statuses"]}>
{(fields, { add, remove }) => { {(fields, { add, remove }) => {
return ( return (
@@ -32,8 +33,7 @@ export default function ShopInfoROStatusComponent({ form }) {
{fields.map((field, index) => ( {fields.map((field, index) => (
<Form.Item <Form.Item
key={field.key} key={field.key}
style={{ padding: 0, margin: 2 }} style={{ padding: 0, margin: 2 }}>
>
<div style={{ display: "flex" }}> <div style={{ display: "flex" }}>
<Form.Item <Form.Item
style={{ padding: 0, margin: 2 }} style={{ padding: 0, margin: 2 }}
@@ -45,8 +45,7 @@ export default function ShopInfoROStatusComponent({ form }) {
required: true, required: true,
message: t("general.validation.required") message: t("general.validation.required")
} }
]} ]}>
>
<Input onBlur={handleBlur} /> <Input onBlur={handleBlur} />
</Form.Item> </Form.Item>
<DeleteFilled <DeleteFilled
@@ -59,12 +58,11 @@ export default function ShopInfoROStatusComponent({ form }) {
))} ))}
<Form.Item> <Form.Item>
<Button <Button
type="dashed" type='dashed'
onClick={() => { onClick={() => {
add(); add();
}} }}
style={{ width: "100%" }} style={{ width: "100%" }}>
>
{t("bodyshop.actions.newstatus")} {t("bodyshop.actions.newstatus")}
</Button> </Button>
</Form.Item> </Form.Item>
@@ -75,6 +73,24 @@ export default function ShopInfoROStatusComponent({ form }) {
</Col> </Col>
<Col span={12}> <Col span={12}>
<SelectorDiv> <SelectorDiv>
<Form.Item
name={["md_ro_statuses", "open_statuses"]}
label={t("bodyshop.fields.statuses.open_statuses")}
rules={[
{
required: true,
message: t("general.validation.required"),
type: "array"
}
]}>
<Select mode='multiple'>
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.statuses.default_scheduled")} label={t("bodyshop.fields.statuses.default_scheduled")}
rules={[ rules={[
@@ -83,8 +99,7 @@ export default function ShopInfoROStatusComponent({ form }) {
message: t("general.validation.required") message: t("general.validation.required")
} }
]} ]}
name={["md_ro_statuses", "default_scheduled"]} name={["md_ro_statuses", "default_scheduled"]}>
>
<Select> <Select>
{options.map((item, idx) => ( {options.map((item, idx) => (
<Select.Option key={idx}>{item}</Select.Option> <Select.Option key={idx}>{item}</Select.Option>
@@ -99,8 +114,7 @@ export default function ShopInfoROStatusComponent({ form }) {
message: t("general.validation.required") message: t("general.validation.required")
} }
]} ]}
name={["md_ro_statuses", "default_arrived"]} name={["md_ro_statuses", "default_arrived"]}>
>
<Select> <Select>
{options.map((item, idx) => ( {options.map((item, idx) => (
<Select.Option key={idx}>{item}</Select.Option> <Select.Option key={idx}>{item}</Select.Option>
@@ -115,8 +129,7 @@ export default function ShopInfoROStatusComponent({ form }) {
message: t("general.validation.required") message: t("general.validation.required")
} }
]} ]}
name={["md_ro_statuses", "default_exported"]} name={["md_ro_statuses", "default_exported"]}>
>
<Select> <Select>
{options.map((item, idx) => ( {options.map((item, idx) => (
<Select.Option key={idx}>{item}</Select.Option> <Select.Option key={idx}>{item}</Select.Option>
@@ -131,8 +144,7 @@ export default function ShopInfoROStatusComponent({ form }) {
message: t("general.validation.required") message: t("general.validation.required")
} }
]} ]}
name={["md_ro_statuses", "default_imported"]} name={["md_ro_statuses", "default_imported"]}>
>
<Select> <Select>
{options.map((item, idx) => ( {options.map((item, idx) => (
<Select.Option key={idx}>{item}</Select.Option> <Select.Option key={idx}>{item}</Select.Option>
@@ -147,8 +159,7 @@ export default function ShopInfoROStatusComponent({ form }) {
message: t("general.validation.required") message: t("general.validation.required")
} }
]} ]}
name={["md_ro_statuses", "default_invoiced"]} name={["md_ro_statuses", "default_invoiced"]}>
>
<Select> <Select>
{options.map((item, idx) => ( {options.map((item, idx) => (
<Select.Option key={idx}>{item}</Select.Option> <Select.Option key={idx}>{item}</Select.Option>
@@ -163,8 +174,7 @@ export default function ShopInfoROStatusComponent({ form }) {
message: t("general.validation.required") message: t("general.validation.required")
} }
]} ]}
name={["md_ro_statuses", "default_completed"]} name={["md_ro_statuses", "default_completed"]}>
>
<Select> <Select>
{options.map((item, idx) => ( {options.map((item, idx) => (
<Select.Option key={idx}>{item}</Select.Option> <Select.Option key={idx}>{item}</Select.Option>
@@ -179,8 +189,7 @@ export default function ShopInfoROStatusComponent({ form }) {
message: t("general.validation.required") message: t("general.validation.required")
} }
]} ]}
name={["md_ro_statuses", "default_delivered"]} name={["md_ro_statuses", "default_delivered"]}>
>
<Select> <Select>
{options.map((item, idx) => ( {options.map((item, idx) => (
<Select.Option key={idx}>{item}</Select.Option> <Select.Option key={idx}>{item}</Select.Option>

View File

@@ -153,10 +153,26 @@ export const GET_JOB_BY_PK = gql`
adjustment_bottom_line adjustment_bottom_line
ownr_fn ownr_fn
ownr_ln ownr_ln
ownr_ea
ownr_addr1
ownr_addr2
ownr_city
ownr_st
ownr_zip
ownr_ctry
ownr_ph1
owner { owner {
id id
ownr_fn ownr_fn
ownr_ln ownr_ln
ownr_ea
ownr_addr1
ownr_addr2
ownr_city
ownr_st
ownr_zip
ownr_ctry
ownr_ph1
} }
labor_rate_desc labor_rate_desc
rate_lab rate_lab
@@ -303,6 +319,18 @@ export const UPDATE_JOB = gql`
} }
`; `;
export const UPDATE_JOBS = gql`
mutation UPDATE_JOBS($jobIds: [uuid!]!, $fields: jobs_set_input!) {
update_jobs(where: { id: { _in: $jobIds } }, _set: $fields) {
returning {
id
est_ph1
est_ea
}
}
}
`;
export const CONVERT_JOB_TO_RO = gql` export const CONVERT_JOB_TO_RO = gql`
mutation CONVERT_JOB_TO_RO($jobId: uuid!) { mutation CONVERT_JOB_TO_RO($jobId: uuid!) {
update_jobs(where: { id: { _eq: $jobId } }, _set: { converted: true }) { update_jobs(where: { id: { _eq: $jobId } }, _set: { converted: true }) {

View File

@@ -118,11 +118,14 @@
"default_invoiced": "Default Invoiced Status", "default_invoiced": "Default Invoiced Status",
"default_ordered": "Default Ordered Status", "default_ordered": "Default Ordered Status",
"default_received": "Default Received Status", "default_received": "Default Received Status",
"default_scheduled": "Default Scheduled Status" "default_scheduled": "Default Scheduled Status",
"open_statuses": "Open Statuses"
}, },
"zip_post": "Zip/Postal Code" "zip_post": "Zip/Postal Code"
}, },
"labels": { "labels": {
"alljobstatuses": "All Job Statuses",
"allopenjobstatuses": "All Open Job Statuses",
"jobstatuses": "Job Statuses", "jobstatuses": "Job Statuses",
"orderstatuses": "Order Statuses", "orderstatuses": "Order Statuses",
"responsibilitycenters": { "responsibilitycenters": {
@@ -523,6 +526,9 @@
} }
}, },
"owners": { "owners": {
"actions": {
"update": "Update Selected Records"
},
"errors": { "errors": {
"noaccess": "The record does not exist or you do not have access to it. ", "noaccess": "The record does not exist or you do not have access to it. ",
"selectexistingornew": "Select an existing owner record or create a new one. " "selectexistingornew": "Select an existing owner record or create a new one. "
@@ -546,7 +552,10 @@
}, },
"labels": { "labels": {
"create_new": "Create a new owner record.", "create_new": "Create a new owner record.",
"existing_owners": "Existing Owners" "existing_owners": "Existing Owners",
"fromclaim": "Info From Claim",
"fromowner": "Info From Previous",
"updateowner": "Update Owner"
}, },
"successes": { "successes": {
"save": "Owner saved successfully." "save": "Owner saved successfully."

View File

@@ -118,11 +118,14 @@
"default_invoiced": "", "default_invoiced": "",
"default_ordered": "", "default_ordered": "",
"default_received": "", "default_received": "",
"default_scheduled": "" "default_scheduled": "",
"open_statuses": ""
}, },
"zip_post": "" "zip_post": ""
}, },
"labels": { "labels": {
"alljobstatuses": "",
"allopenjobstatuses": "",
"jobstatuses": "", "jobstatuses": "",
"orderstatuses": "", "orderstatuses": "",
"responsibilitycenters": { "responsibilitycenters": {
@@ -523,6 +526,9 @@
} }
}, },
"owners": { "owners": {
"actions": {
"update": ""
},
"errors": { "errors": {
"noaccess": "El registro no existe o no tiene acceso a él.", "noaccess": "El registro no existe o no tiene acceso a él.",
"selectexistingornew": "" "selectexistingornew": ""
@@ -546,7 +552,10 @@
}, },
"labels": { "labels": {
"create_new": "Crea un nuevo registro de propietario.", "create_new": "Crea un nuevo registro de propietario.",
"existing_owners": "Propietarios existentes" "existing_owners": "Propietarios existentes",
"fromclaim": "",
"fromowner": "",
"updateowner": ""
}, },
"successes": { "successes": {
"save": "Propietario guardado con éxito." "save": "Propietario guardado con éxito."

View File

@@ -118,11 +118,14 @@
"default_invoiced": "", "default_invoiced": "",
"default_ordered": "", "default_ordered": "",
"default_received": "", "default_received": "",
"default_scheduled": "" "default_scheduled": "",
"open_statuses": ""
}, },
"zip_post": "" "zip_post": ""
}, },
"labels": { "labels": {
"alljobstatuses": "",
"allopenjobstatuses": "",
"jobstatuses": "", "jobstatuses": "",
"orderstatuses": "", "orderstatuses": "",
"responsibilitycenters": { "responsibilitycenters": {
@@ -523,6 +526,9 @@
} }
}, },
"owners": { "owners": {
"actions": {
"update": ""
},
"errors": { "errors": {
"noaccess": "L'enregistrement n'existe pas ou vous n'y avez pas accès.", "noaccess": "L'enregistrement n'existe pas ou vous n'y avez pas accès.",
"selectexistingornew": "" "selectexistingornew": ""
@@ -546,7 +552,10 @@
}, },
"labels": { "labels": {
"create_new": "Créez un nouvel enregistrement de propriétaire.", "create_new": "Créez un nouvel enregistrement de propriétaire.",
"existing_owners": "Propriétaires existants" "existing_owners": "Propriétaires existants",
"fromclaim": "",
"fromowner": "",
"updateowner": ""
}, },
"successes": { "successes": {
"save": "Le propriétaire a bien enregistré." "save": "Le propriétaire a bien enregistré."