Added owners detail fields and owner jobs list.

This commit is contained in:
Patrick Fic
2020-02-07 15:27:53 -08:00
parent 7f06df66fd
commit d3bd68d40a
14 changed files with 542 additions and 18 deletions

View File

@@ -4668,9 +4668,56 @@
<folder_node>
<name>owners</name>
<children>
<folder_node>
<name>errors</name>
<children>
<concept_node>
<name>noaccess</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>
<name>fields</name>
<children>
<concept_node>
<name>allow_text_message</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>ownr_addr1</name>
<definition_loaded>false</definition_loaded>
@@ -4692,6 +4739,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ownr_addr2</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>ownr_city</name>
<definition_loaded>false</definition_loaded>
@@ -4713,6 +4781,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ownr_ctry</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>ownr_ea</name>
<definition_loaded>false</definition_loaded>
@@ -4797,6 +4886,90 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ownr_st</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>ownr_title</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>ownr_zip</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>preferred_contact</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>
@@ -4825,6 +4998,32 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>successes</name>
<children>
<concept_node>
<name>save</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>
</children>
</folder_node>
<folder_node>

View File

@@ -5,9 +5,13 @@ function FormItemEmail(props, ref) {
<Input
{...props}
addonAfter={
<a href={`mailto:${props.email}`}>
props.email ? (
<a href={`mailto:${props.email}`}>
<Icon type="mail" />
</a>
) : (
<Icon type="mail" />
</a>
)
}
/>
);

View File

@@ -0,0 +1,106 @@
import { Button, Col, Form, Input, Row, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import FormItemEmail from "../form-items-formatted/email-form-item.component";
import FormItemPhone from "../form-items-formatted/phone-form-item.component";
import ResetForm from "../form-items-formatted/reset-form-item.component";
export default function OwnerDetailFormComponent({ form, owner }) {
const { t } = useTranslation();
const {
isFieldsTouched,
resetFields,
getFieldDecorator,
getFieldValue
} = form;
return (
<div>
{isFieldsTouched() ? <ResetForm resetFields={resetFields} /> : null}
<Button type="primary" key="submit" htmlType="submit">
{t("general.labels.save")}
</Button>
<Row>
<Col span={8}>
<Form.Item label={t("owners.fields.ownr_ln")}>
{getFieldDecorator("ownr_ln", {
initialValue: owner.ownr_ln
})(<Input name="ownr_ln" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_fn")}>
{getFieldDecorator("ownr_fn", {
initialValue: owner.ownr_fn
})(<Input name="ownr_fn" />)}
</Form.Item>
<Form.Item label={t("owners.fields.allow_text_message")}>
{getFieldDecorator("allow_text_message", {
initialValue: owner.allow_text_message,
valuePropName: "checked"
})(<Switch name="allow_text_message" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_addr1")}>
{getFieldDecorator("ownr_addr1", {
initialValue: owner.ownr_addr1
})(<Input name="ownr_addr1" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_addr2")}>
{getFieldDecorator("ownr_addr2", {
initialValue: owner.ownr_addr2
})(<Input name="ownr_addr2" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_city")}>
{getFieldDecorator("ownr_city", {
initialValue: owner.ownr_city
})(<Input name="ownr_city" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_ctry")}>
{getFieldDecorator("ownr_ctry", {
initialValue: owner.ownr_ctry
})(<Input name="ownr_ctry" />)}
</Form.Item>
</Col>
<Col span={8}>
{" "}
<Form.Item label={t("owners.fields.ownr_ea")}>
{getFieldDecorator("ownr_ea", {
initialValue: owner.ownr_ea,
rules: [
{
type: "email",
message: "This is not a valid email address."
}
]
})(
<FormItemEmail name="ownr_ea" email={getFieldValue("ownr_ea")} />
)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_ph1")}>
{getFieldDecorator("ownr_ph1", {
initialValue: owner.ownr_ph1
})(<FormItemPhone customInput={Input} name="ownr_ph1" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_st")}>
{getFieldDecorator("ownr_st", {
initialValue: owner.ownr_st
})(<Input name="ownr_st" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_zip")}>
{getFieldDecorator("ownr_zip", {
initialValue: owner.ownr_zip
})(<Input name="ownr_zip" />)}
</Form.Item>
<Form.Item label={t("owners.fields.preferred_contact")}>
{getFieldDecorator("preferred_contact", {
initialValue: owner.preferred_contact
})(<Input name="preferred_contact" />)}
</Form.Item>
<Form.Item label={t("owners.fields.ownr_title")}>
{getFieldDecorator("ownr_title", {
initialValue: owner.ownr_title
})(<Input name="ownr_title" />)}
</Form.Item>
</Col>
</Row>
</div>
);
}

View File

@@ -0,0 +1,46 @@
import { Form, notification } from "antd";
import React from "react";
import { useMutation } from "react-apollo";
import { useTranslation } from "react-i18next";
import { UPDATE_OWNER } from "../../graphql/owners.queries";
import OwnerDetailFormComponent from "./owner-detail-form.component";
function OwnerDetailFormContainer({ form, owner, refetch }) {
const { t } = useTranslation();
const [updateOwner] = useMutation(UPDATE_OWNER);
const handleSubmit = e => {
e.preventDefault();
form.validateFieldsAndScroll((err, values) => {
if (err) {
notification["error"]({
message: t("owners.errors.validationtitle"),
description: t("owners.errors.validation")
});
}
if (!err) {
updateOwner({
variables: { ownerId: owner.id, owner: values }
}).then(r => {
notification["success"]({
message: t("owners.successes.save")
});
//TODO: Better way to reset the field decorators?
if (refetch) refetch().then();
form.resetFields();
});
}
});
};
return (
<Form onSubmit={handleSubmit} autoComplete="off">
<OwnerDetailFormComponent form={form} owner={owner} />
</Form>
);
}
export default Form.create({ name: "OwnerDetailFormContainer" })(
OwnerDetailFormContainer
);

View File

@@ -0,0 +1,59 @@
import React from "react";
import { Table } from "antd";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
export default function OwnerDetailJobsComponent({ owner }) {
const { t } = useTranslation();
const columns = [
{
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
ellipsis: true,
render: (text, record) => (
<Link to={`/manage/jobs/${record.id}`}>
{record.ro_number ? record.ro_number : `EST ${record.est_number}`}
</Link>
)
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "owner",
key: "owner",
render: (text, record) => (
<Link to={`/manage/vehicles/${record.vehicle.id}`}>
{`${record.vehicle.v_model_yr} ${record.vehicle.v_make_desc} ${record.vehicle.v_model_desc}`}
</Link>
)
},
{
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
key: "clm_no"
},
{
title: t("jobs.fields.status"),
dataIndex: "status",
key: "status"
},
{
title: t("jobs.fields.clm_total"),
dataIndex: "clm_total",
key: "clm_total",
render: (text, record) => (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
)
}
];
return (
<Table
pagination={{ position: "bottom" }}
columns={columns.map(item => ({ ...item }))}
rowKey="id"
dataSource={owner.jobs}
/>
);
}

View File

@@ -18,3 +18,50 @@ export const QUERY_SEARCH_OWNER_BY_IDX = gql`
}
}
`;
export const QUERY_OWNER_BY_ID = gql`
query QUERY_OWNER_BY_ID($id: uuid!) {
owners_by_pk(id: $id) {
id
allow_text_message
ownr_addr1
ownr_addr2
ownr_co_nm
ownr_city
ownr_ctry
ownr_ea
ownr_fn
ownr_ph1
ownr_ln
ownr_ph2
ownr_st
ownr_title
ownr_zip
preferred_contact
jobs {
id
ro_number
est_number
clm_no
status
clm_total
vehicle {
id
v_model_yr
v_model_desc
v_make_desc
}
}
}
}
`;
export const UPDATE_OWNER = gql`
mutation UPDATE_OWNER($ownerId: uuid!, $owner: owners_set_input!) {
update_owners(where: { id: { _eq: $ownerId } }, _set: $owner) {
returning {
id
}
}
}
`;

View File

@@ -2,7 +2,7 @@ import { gql } from "apollo-boost";
export const QUERY_VEHICLE_BY_ID = gql`
query QUERY_VEHICLE_BY_ID($id: uuid!) {
vehicles(where: { id: { _eq: $id } }) {
vehicles_by_pk(id: $id) {
created_at
db_v_code
id

View File

@@ -1,5 +1,11 @@
import React from "react";
export default function OwnersDetailComponent() {
return <div>Owner Detail</div>;
import OwnerDetailForm from "../../components/owner-detail-form/owner-detail-form.container";
import OwnerDetailJobsComponent from "../../components/owner-detail-jobs/owner-detail-jobs.component";
export default function OwnersDetailComponent({ owner, refetch }) {
return (
<div>
<OwnerDetailForm owner={owner} refetch={refetch} />
<OwnerDetailJobsComponent owner={owner} />
</div>
);
}

View File

@@ -1,8 +1,28 @@
import React from "react";
import OwnersDetailComponent from "./owners-detail.page.component";
import { useTranslation } from "react-i18next";
import { useQuery } from "react-apollo";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import AlertComponent from "../../components/alert/alert.component";
import { QUERY_OWNER_BY_ID } from "../../graphql/owners.queries";
export default function OwnersDetailContainer({ match }) {
const { ownerId } = match.params;
console.log("ownerId", ownerId);
return <OwnersDetailComponent />;
const { t } = useTranslation();
const { loading, data, error, refetch } = useQuery(QUERY_OWNER_BY_ID, {
variables: { id: ownerId },
fetchPolicy: "network-only"
});
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />;
if (data.owners_by_pk)
return (
<OwnersDetailComponent owner={data.owners_by_pk} refetch={refetch} />
);
else
return (
<AlertComponent message={t("owners.errors.noaccess")} type="error" />
);
}

View File

@@ -5,8 +5,6 @@ import VehicleDetailJobsComponent from "../../components/vehicle-detail-jobs/veh
export default function VehicleDetailComponent({ vehicle, refetch }) {
return (
<div>
Veh detail <span>{vehicle.v_vin}</span>
<div>Vehicle Fields</div>
<VehicleDetailFormContainer vehicle={vehicle} refetch={refetch} />
<VehicleDetailJobsComponent vehicle={vehicle} />
</div>

View File

@@ -17,8 +17,8 @@ export default function VehicleDetailContainer({ match }) {
useEffect(() => {
document.title = t("titles.vehicledetail", {
vehicle:
data && data.vehicles[0]
? `${data.vehicles[0].v_model_yr} ${data.vehicles[0].v_make_desc} ${data.vehicles[0].v_model_desc}`
data && data.vehicles_by_pk
? `${data.vehicles_by_pk.v_model_yr} ${data.vehicles_by_pk.v_make_desc} ${data.vehicles_by_pk.v_model_desc}`
: ""
});
}, [t, data]);
@@ -26,9 +26,9 @@ export default function VehicleDetailContainer({ match }) {
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />;
if (data.vehicles[0])
if (data.vehicles_by_pk)
return (
<VehicleDetailComponent vehicle={data.vehicles[0]} refetch={refetch} />
<VehicleDetailComponent vehicle={data.vehicles_by_pk} refetch={refetch} />
);
else
return (

View File

@@ -291,16 +291,29 @@
}
},
"owners": {
"errors": {
"noaccess": "The record does not exist or you do not have access to it. "
},
"fields": {
"allow_text_message": "Permission to Text?",
"ownr_addr1": "Address",
"ownr_addr2": "Address 2",
"ownr_city": "City",
"ownr_ctry": "Country",
"ownr_ea": "Email",
"ownr_fn": "First Name",
"ownr_ln": "Last Name",
"ownr_ph1": "Phone 1"
"ownr_ph1": "Phone 1",
"ownr_st": "State/Province",
"ownr_title": "Title",
"ownr_zip": "Zip/Postal Code",
"preferred_contact": "Preferred Contact Method"
},
"labels": {
"existing_owners": "Existing Owners"
},
"successes": {
"save": "Owner saved successfully."
}
},
"profile": {

View File

@@ -291,16 +291,29 @@
}
},
"owners": {
"errors": {
"noaccess": "El registro no existe o no tiene acceso a él."
},
"fields": {
"allow_text_message": "Permiso de texto?",
"ownr_addr1": "Dirección",
"ownr_addr2": "Dirección 2",
"ownr_city": "ciudad",
"ownr_ctry": "País",
"ownr_ea": "Email",
"ownr_fn": "Nombre de pila",
"ownr_ln": "Apellido",
"ownr_ph1": ""
"ownr_ph1": "Teléfono 1",
"ownr_st": "Provincia del estado",
"ownr_title": "Título",
"ownr_zip": "código postal",
"preferred_contact": "Método de Contacto Preferido"
},
"labels": {
"existing_owners": "Propietarios existentes"
},
"successes": {
"save": "Propietario guardado con éxito."
}
},
"profile": {

View File

@@ -291,16 +291,29 @@
}
},
"owners": {
"errors": {
"noaccess": "L'enregistrement n'existe pas ou vous n'y avez pas accès."
},
"fields": {
"allow_text_message": "Autorisation de texte?",
"ownr_addr1": "Adresse",
"ownr_addr2": "Adresse 2 ",
"ownr_city": "Ville",
"ownr_ctry": "Pays",
"ownr_ea": "Email",
"ownr_fn": "Prénom",
"ownr_ln": "Nom de famille",
"ownr_ph1": ""
"ownr_ph1": "Téléphone 1",
"ownr_st": "Etat / Province",
"ownr_title": "Titre",
"ownr_zip": "Zip / code postal",
"preferred_contact": "Méthode de contact préférée"
},
"labels": {
"existing_owners": "Propriétaires existants"
},
"successes": {
"save": "Le propriétaire a bien enregistré."
}
},
"profile": {