Complete refactor of jobs detail screen.

This commit is contained in:
Patrick Fic
2020-01-27 16:20:33 -08:00
parent 9553062045
commit b5e9c65aee
38 changed files with 3409 additions and 401 deletions

View File

@@ -1,4 +1,4 @@
import { useQuery, useApolloClient } from "@apollo/react-hooks";
import { useApolloClient, useQuery } from "@apollo/react-hooks";
import { Avatar, Col, Dropdown, Icon, Menu, Row } from "antd";
import i18next from "i18next";
import React from "react";
@@ -52,7 +52,7 @@ export default function CurrentUserDropdown() {
);
if (loading) return null;
if (error) return <AlertComponent message={error.message} />;
if (error) return <AlertComponent message={error.message} type='error' />;
const { currentUser } = data;
@@ -62,10 +62,8 @@ export default function CurrentUserDropdown() {
<Col span={8}>
<Avatar size='large' alt='Avatar' src={UserImage} />
</Col>
<Col span={16}>
<Link to='/manage/profile'>
{currentUser?.displayName || t("general.labels.unknown")}
</Link>
<Col span={16} style={{ color: "white" }}>
{currentUser.displayName || t("general.labels.unknown")}
</Col>
</Row>
</Dropdown>

View File

@@ -1,6 +1,5 @@
import React, { useState } from "react";
import { Row, Col, Button } from "antd";
import ChatWindowContainer from "../chat-window/chat-window.container";
import { Col, Row } from "antd";
import React from "react";
export default function FooterComponent() {
return (

View File

@@ -1,44 +1,65 @@
import { useApolloClient } from "@apollo/react-hooks";
import { Col, Icon, Menu, Row } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import CurrentUserDropdown from "../current-user-dropdown/current-user-dropdown.component";
import GlobalSearch from "../global-search/global-search.component";
import ManageSignInButton from "../manage-sign-in-button/manage-sign-in-button.component";
import "./header.styles.scss";
export default ({ landingHeader, navItems, selectedNavItem }) => {
const apolloClient = useApolloClient();
const { t } = useTranslation();
const handleClick = e => {
apolloClient.writeData({ data: { selectedNavItem: e.key } });
};
return (
<Row type="flex" justify="space-around">
<Row type='flex' justify='space-around'>
<Col span={16}>
<Menu
theme="dark"
className="header"
theme='dark'
className='header'
onClick={handleClick}
selectedKeys={selectedNavItem}
mode="horizontal"
>
mode='horizontal'>
<Menu.Item>
<GlobalSearch />
</Menu.Item>
{navItems.map(navItem => (
<Menu.Item key={navItem.title}>
<Link to={navItem.path}>
{navItem.icontype ? <Icon type={navItem.icontype} /> : null}
{navItem.title}
<Menu.Item key='home'>
<Link to='/manage'>
<Icon type='home' />
{t("menus.header.home")}
</Link>
</Menu.Item>
<Menu.SubMenu title={t("menus.header.jobs")}>
<Menu.Item key='jobs'>
<Link to='/manage/jobs'>
<Icon type='home' />
{t("menus.header.activejobs")}
</Link>
</Menu.Item>
))}
<Menu.Item key='availablejobs'>
<Link to='/manage/available'>
<Icon type='home' />
{t("menus.header.availablejobs")}
</Link>
</Menu.Item>
</Menu.SubMenu>
{!landingHeader ? (
null
) : (
{
// navItems.map(navItem => (
// <Menu.Item key={navItem.title}>
// <Link to={navItem.path}>
// {navItem.icontype ? <Icon type={navItem.icontype} /> : null}
// {navItem.title}
// </Link>
// </Menu.Item>
// ))
}
{!landingHeader ? null : (
<Menu.Item>
<ManageSignInButton />
</Menu.Item>

View File

@@ -5,8 +5,7 @@ import AlertComponent from "../../components/alert/alert.component";
import { GET_JOB_LINES_BY_PK } from "../../graphql/jobs-lines.queries";
export default function JobLinesContainer({ match }) {
const { jobId } = match.params;
export default function JobLinesContainer({ jobId }) {
const { loading, error, data } = useQuery(GET_JOB_LINES_BY_PK, {
variables: { id: jobId },

View File

@@ -1,217 +0,0 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
import AlertComponent from "../alert/alert.component";
import {
Form,
Input,
Row,
Col,
Button,
Typography,
PageHeader,
Descriptions,
Tag,
notification,
Avatar,
Layout
} from "antd";
import { UPDATE_JOB, CONVERT_JOB_TO_RO } from "../../graphql/jobs.queries";
import { useMutation } from "@apollo/react-hooks";
import FormItemPhone from "../form-items-formatted/phone-form-item.component";
import { useTranslation } from "react-i18next";
import CarImage from "../../assets/car.svg";
const { Content } = Layout;
const formItemLayout = {
// labelCol: {
// xs: { span: 12 },
// sm: { span: 5 }
// },
// wrapperCol: {
// xs: { span: 24 },
// sm: { span: 12 }
// }
};
function JobTombstone({ job, ...otherProps }) {
const [jobContext, setJobContext] = useState(job);
const [mutationUpdateJob] = useMutation(UPDATE_JOB);
const [mutationConvertJob] = useMutation(CONVERT_JOB_TO_RO);
const { t } = useTranslation();
if (!job) {
return <AlertComponent message={t("jobs.errors.noaccess")} type='error' />;
}
const handleSubmit = e => {
e.preventDefault();
otherProps.form.validateFieldsAndScroll((err, values) => {
if (err) {
notification["error"]({
message: t("jobs.errors.validationtitle"),
description: t("jobs.errors.validation")
});
}
if (!err) {
mutationUpdateJob({
variables: { jobId: jobContext.id, job: values }
}).then(r =>
notification["success"]({
message: t("jobs.successes.savetitle")
})
);
}
});
};
const handleChange = event => {
const { name, value } = event.target ? event.target : event;
setJobContext({ ...jobContext, [name]: value });
};
const { getFieldDecorator } = otherProps.form;
const tombstoneTitle = (
<div>
<Avatar size='large' alt='Vehicle Image' src={CarImage} />
{`${t("jobs.fields.ro_number")} ${
jobContext.ro_number ? jobContext.ro_number : t("general.labels.na")
}`}
</div>
);
return (
<Content>
<Form onSubmit={handleSubmit} {...formItemLayout}>
<PageHeader
style={{
border: "1px solid rgb(235, 237, 240)"
}}
title={tombstoneTitle}
subTitle={
jobContext.owner
? (jobContext.owner?.first_name || "") +
" " +
(jobContext.owner?.last_name || "")
: t("jobs.errors.noowner")
}
tags={
<span key='job-status'>
{jobContext.job_status ? (
<Tag color='blue'>{jobContext.job_status.name}</Tag>
) : null}
</span>
}
extra={[
<Button
key='convert'
type='dashed'
disabled={jobContext.converted}
onClick={() => {
mutationConvertJob({
variables: { jobId: jobContext.id }
}).then(r => {
console.log("r", r);
setJobContext({
...jobContext,
converted: true,
ro_number: r.data.update_jobs.returning[0].ro_number
});
notification["success"]({
message: t("jobs.successes.converted")
});
});
}}>
{t("jobs.actions.convert")}
</Button>,
<Button type='primary' key='submit' htmlType='submit'>
{t("general.labels.save")}
</Button>
]}>
<Descriptions size='small' column={5}>
<Descriptions.Item label={t("jobs.fields.vehicle")}>
<Link to={`/manage/vehicles/${jobContext.vehicle?.id}`}>
{jobContext.vehicle?.v_model_yr || t("general.labels.na")}{" "}
{jobContext.vehicle?.v_make_desc || t("general.labels.na")}{" "}
{jobContext.vehicle?.v_model_desc || t("general.labels.na")} |{" "}
{jobContext.vehicle?.plate_no || t("general.labels.na")}
</Link>
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.est_number")}>
{jobContext.est_number}
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.claim_total")}>
$ {jobContext.claim_total?.toFixed(2)}
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.deductible")}>
$ {jobContext.deductible?.toFixed(2)}
</Descriptions.Item>
</Descriptions>
</PageHeader>
<Row>
<Typography.Title level={4}>Information</Typography.Title>
{
// <Form.Item label='Estimate #'>
// {getFieldDecorator("est_number", {
// initialValue: jobContext.est_number
// })(<Input name='est_number' readOnly onChange={handleChange} />)}
// </Form.Item>
}
</Row>
<Row>
<Typography.Title level={4}>Insurance Information</Typography.Title>
<Form.Item label='Insurance Company'>
{getFieldDecorator("est_co_nm", {
initialValue: jobContext.est_co_nm
})(<Input name='est_co_nm' onChange={handleChange} />)}
</Form.Item>
<Col span={8}>
<Form.Item label='Estimator Last Name'>
{getFieldDecorator("est_ct_ln", {
initialValue: jobContext.est_ct_ln
})(<Input name='est_ct_ln' onChange={handleChange} />)}
</Form.Item>
<Form.Item label='Estimator First Name'>
{getFieldDecorator("est_ct_fn", {
initialValue: jobContext.est_ct_fn
})(<Input name='est_ct_fn' onChange={handleChange} />)}
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label='Estimator Phone #'>
{getFieldDecorator("est_ph1", {
initialValue: jobContext.est_ph1
})(
<FormItemPhone
customInput={Input}
name='est_ph1'
onValueChange={handleChange}
/>
)}
</Form.Item>
<Form.Item label='Estimator Email'>
{getFieldDecorator("est_ea", {
initialValue: jobContext.est_ea,
rules: [
{
type: "email",
message: "This is not a valid email address."
}
]
})(<Input name='est_ea' onChange={handleChange} />)}
</Form.Item>
</Col>
</Row>
</Form>
</Content>
);
}
export default Form.create({ name: "JobTombstone" })(JobTombstone);

View File

@@ -0,0 +1,117 @@
import {
Avatar,
Button,
Descriptions,
notification,
PageHeader,
Tag,
Input,
Form,
Checkbox
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import Moment from "react-moment";
import { Link } from "react-router-dom";
import CarImage from "../../assets/car.svg";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
export default function JobsDetailHeader({
job,
mutationConvertJob,
refetch,
getFieldDecorator
}) {
const { t } = useTranslation();
const tombstoneTitle = (
<div>
<Avatar size='large' alt='Vehicle Image' src={CarImage} />
{`${t("jobs.fields.ro_number")} ${
job.ro_number ? job.ro_number : t("general.labels.na")
}`}
</div>
);
const tombstoneSubtitle = (
<div>
{job.owner
? (job.owner.first_name || "") + " " + (job.owner.last_name || "")
: t("jobs.errors.noowner")}
{job.vehicle ? (
<Link to={`/manage/vehicles/${job.vehicle.id}`}>
{job.vehicle.v_model_yr || t("general.labels.na")}{" "}
{job.vehicle.v_make_desc || t("general.labels.na")}{" "}
{job.vehicle.v_model_desc || t("general.labels.na")} |{" "}
{job.vehicle.plate_no || t("general.labels.na")} |{" "}
{job.vehicle.v_vin || t("general.labels.na")}
</Link>
) : null}
</div>
);
const menuExtra = [
<Button
key='convert'
type='dashed'
disabled={job.converted}
onClick={() => {
mutationConvertJob({
variables: { jobId: job.id }
}).then(r => {
refetch();
notification["success"]({
message: t("jobs.successes.converted")
});
});
}}>
{t("jobs.actions.convert")}
</Button>,
<Button type='primary' key='submit' htmlType='submit'>
{t("general.labels.save")}
</Button>
];
return (
<PageHeader
style={{
border: "1px solid rgb(235, 237, 240)"
}}
title={tombstoneTitle}
subTitle={tombstoneSubtitle}
tags={
<span key='job-status'>
{job.job_status ? (
<Tag color='blue'>{job.job_status.name}</Tag>
) : null}
</span>
}
extra={menuExtra}>
<Descriptions size='small' column={5}>
<Descriptions.Item label={t("jobs.fields.repairtotal")}>
<CurrencyFormatter>{job.claim_total}</CurrencyFormatter>
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.customerowing")}>
$NO BINDING YET
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.specialcoveragepolicy")}>
<Checkbox checked={job.special_coverage_policy} />
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.scheduled_completion")}>
{job.scheduled_completion ? (
<Moment format='MM/DD/YYYY'>{job.scheduled_completion}</Moment>
) : null}
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.servicecar")}>
{job.service_car}
</Descriptions.Item>
</Descriptions>
</PageHeader>
);
}

View File

@@ -37,12 +37,12 @@ export default function NoteUpsertModalContainer({
]
}
}).then(r => {
refetch();
changeVisibility(!visible);
notification["success"]({
message: t("notes.successes.create")
});
});
refetch();
changeVisibility(!visible);
};
const updateExistingNote = () => {