BOD-17 Basic creation of a contract functioning.

This commit is contained in:
Patrick Fic
2020-03-31 12:11:48 -07:00
parent 9ae3843b3b
commit 72f4d31b05
17 changed files with 884 additions and 190 deletions

View File

@@ -0,0 +1,119 @@
import { Input, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { alphaSort } from "../../utils/sorters";
export default function ContractsCarsComponent({
loading,
data,
selectedCar,
handleSelect
}) {
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: { text: "" },
search: ""
});
const { t } = useTranslation();
const columns = [
{
title: t("courtesycars.fields.fleetnumber"),
dataIndex: "fleetnumber",
key: "fleetnumber",
sorter: (a, b) => alphaSort(a.fleetnumber, b.fleetnumber),
sortOrder:
state.sortedInfo.columnKey === "fleetnumber" && state.sortedInfo.order
},
{
title: t("courtesycars.fields.status"),
dataIndex: "status",
key: "status",
sorter: (a, b) => alphaSort(a.status, b.status),
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order
},
{
title: t("courtesycars.fields.year"),
dataIndex: "year",
key: "year",
sorter: (a, b) => alphaSort(a.year, b.year),
sortOrder: state.sortedInfo.columnKey === "year" && state.sortedInfo.order
},
{
title: t("courtesycars.fields.make"),
dataIndex: "make",
key: "make",
sorter: (a, b) => alphaSort(a.make, b.make),
sortOrder: state.sortedInfo.columnKey === "make" && state.sortedInfo.order
},
{
title: t("courtesycars.fields.model"),
dataIndex: "model",
key: "model",
sorter: (a, b) => alphaSort(a.model, b.model),
sortOrder:
state.sortedInfo.columnKey === "model" && state.sortedInfo.order
},
{
title: t("courtesycars.fields.plate"),
dataIndex: "plate",
key: "plate",
sorter: (a, b) => alphaSort(a.plate, b.plate),
sortOrder:
state.sortedInfo.columnKey === "plate" && state.sortedInfo.order
}
];
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const filteredData =
state.search === ""
? data
: data.filter(
cc =>
(cc.fleetnumber || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(cc.status || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(cc.year || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(cc.make || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(cc.model || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(cc.plate || "").toLowerCase().includes(state.search.toLowerCase())
);
console.log("filteredData", filteredData);
return (
<Table
loading={loading}
title={() => (
<Input.Search
placeholder={t("general.labels.search")}
value={state.search}
onChange={e => setState({ ...state, search: e.target.value })}
/>
)}
size="small"
pagination={{ position: "top" }}
columns={columns.map(item => ({ ...item }))}
rowKey="id"
dataSource={filteredData}
onChange={handleTableChange}
rowSelection={{
onSelect: handleSelect,
type: "radio",
selectedRowKeys: [selectedCar]
}}
/>
);
}

View File

@@ -0,0 +1,29 @@
import { useQuery } from "@apollo/react-hooks";
import React from "react";
import { QUERY_AVAILABLE_CC } from "../../graphql/courtesy-car.queries";
import AlertComponent from "../alert/alert.component";
import ContractCarsComponent from "./contract-cars.component";
export default function ContractCarsContainer({ selectedCarState, bodyshop }) {
const { loading, error, data } = useQuery(QUERY_AVAILABLE_CC);
const [selectedCar, setSelectedCar] = selectedCarState;
const handleSelect = record => {
setSelectedCar(record.id);
};
if (error) return <AlertComponent message={error.message} type="error" />;
return (
<div>
<ContractCarsComponent
handleSelect={handleSelect}
selectedCar={selectedCar}
loading={loading}
data={data ? data.courtesycars : []}
/>
</div>
);
}

View File

@@ -2,6 +2,8 @@ import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Form, Input, DatePicker, InputNumber, Button } from "antd";
import aamva from "aamva";
import InputPhone from "../form-items-formatted/phone-form-item.component";
import ContractStatusSelector from "../contract-status-select/contract-status-select.component";
export default function ContractFormComponent() {
const [state, setState] = useState("");
@@ -15,9 +17,7 @@ export default function ContractFormComponent() {
//let data = state;
var data =
"%FLDELRAY BEACH^DOE$JOHN$^4818 S FEDERAL BLVD^ ?\
;6360100462172082009=2101198299090=?\
#! 33435 I 1600 ECCECC00000?";
"%FLDELRAY BEACH^DOE$JOHN$^4818 S FEDERAL BLVD^ ? ;6360100462172082009=2101198299090=? #! 33435 I 1600 ECCECC00000?";
data = data.replace(/\n/, "");
// replace spaces with regular space
data = data.replace(/\s/g, " ");
@@ -32,7 +32,7 @@ export default function ContractFormComponent() {
</Button>
<Form.Item
label={t("contracts.fields.status")}
name="status "
name="status"
rules={[
{
required: true,
@@ -40,7 +40,7 @@ export default function ContractFormComponent() {
}
]}
>
<Input />
<ContractStatusSelector />
</Form.Item>
<Form.Item
label={t("contracts.fields.start")}
@@ -66,16 +66,7 @@ export default function ContractFormComponent() {
>
<DatePicker />
</Form.Item>
<Form.Item
label={t("contracts.fields.actualreturn")}
name="actualreturn"
rules={[
{
required: true,
message: t("general.validation.required")
}
]}
>
<Form.Item label={t("contracts.fields.actualreturn")} name="actualreturn">
<DatePicker />
</Form.Item>
<Form.Item
@@ -90,16 +81,7 @@ export default function ContractFormComponent() {
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("contracts.fields.kmend")}
name="kmend"
rules={[
{
required: true,
message: t("general.validation.required")
}
]}
>
<Form.Item label={t("contracts.fields.kmend")} name="kmend">
<InputNumber />
</Form.Item>
<Form.Item
@@ -174,16 +156,7 @@ export default function ContractFormComponent() {
>
<Input />
</Form.Item>
<Form.Item
label={t("contracts.fields.driver_addr2")}
name="driver_addr2"
rules={[
{
required: true,
message: t("general.validation.required")
}
]}
>
<Form.Item label={t("contracts.fields.driver_addr2")} name="driver_addr2">
<Input />
</Form.Item>
<Form.Item
@@ -232,7 +205,7 @@ export default function ContractFormComponent() {
}
]}
>
<Input />
<InputPhone />
</Form.Item>
<Form.Item
label={t("contracts.fields.driver_dob")}

View File

@@ -0,0 +1,187 @@
import { Table, Input } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { alphaSort } from "../../utils/sorters";
export default function ContractsJobsComponent({
loading,
data,
selectedJob,
handleSelect
}) {
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: { text: "" },
search: ""
});
const { t } = useTranslation();
const columns = [
{
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
width: "8%",
sorter: (a, b) =>
alphaSort(
a.ro_number ? a.ro_number : "EST-" + a.est_number,
b.ro_number ? b.ro_number : "EST-" + b.est_number
),
sortOrder:
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
render: (text, record) => (
<span>
{record.ro_number ? record.ro_number : "EST-" + record.est_number}
</span>
)
},
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
key: "owner",
ellipsis: true,
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
width: "25%",
sortOrder:
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
render: (text, record) => {
return record.owner ? (
<span>
{record.ownr_fn} {record.ownr_ln}
</span>
) : (
<span>{`${record.ownr_fn} ${record.ownr_ln}`}</span>
);
}
},
{
title: t("jobs.fields.status"),
dataIndex: "status",
key: "status",
width: "10%",
ellipsis: true,
sorter: (a, b) => alphaSort(a.status, b.status),
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
render: (text, record) => {
return record.status || t("general.labels.na");
}
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
key: "vehicle",
width: "15%",
ellipsis: true,
render: (text, record) => {
return record.vehicleid ? (
<span>
{`${record.v_model_yr || ""} ${record.v_make_desc ||
""} ${record.v_model_desc || ""}`}
</span>
) : (
t("jobs.errors.novehicle")
);
}
},
{
title: t("vehicles.fields.plate_no"),
dataIndex: "plate_no",
key: "plate_no",
width: "8%",
ellipsis: true,
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
sortOrder:
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
render: (text, record) => {
return record.plate_no ? (
<span>{record.plate_no}</span>
) : (
t("general.labels.unknown")
);
}
},
{
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
key: "clm_no",
width: "12%",
ellipsis: true,
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder:
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
render: (text, record) => {
return record.clm_no ? (
<span>{record.clm_no}</span>
) : (
t("general.labels.unknown")
);
}
}
];
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const filteredData =
state.search === ""
? data
: data.filter(
j =>
(j.est_number || "")
.toString()
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(j.ro_number || "")
.toString()
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(j.ownr_fn || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(j.ownr_ln || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(j.clm_no || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(j.v_make_desc || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(j.v_model_desc || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(j.plate_no || "")
.toLowerCase()
.includes(state.search.toLowerCase())
);
console.log("filteredData", filteredData);
return (
<Table
loading={loading}
title={() => (
<Input.Search
placeholder={t("general.labels.search")}
value={state.search}
onChange={e => setState({ ...state, search: e.target.value })}
/>
)}
size="small"
pagination={{ position: "top" }}
columns={columns.map(item => ({ ...item }))}
rowKey="id"
dataSource={filteredData}
onChange={handleTableChange}
rowSelection={{
onSelect: handleSelect,
type: "radio",
selectedRowKeys: [selectedJob]
}}
/>
);
}

View File

@@ -0,0 +1,38 @@
import { useQuery } from "@apollo/react-hooks";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import ContractJobsComponent from "./contract-jobs.component";
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop
});
export function ContractJobsContainer({ selectedJobState, bodyshop }) {
const { loading, error, data } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
variables: {
statuses: bodyshop.md_ro_statuses.open_statuses || ["Open"]
}
});
const [selectedJob, setSelectedJob] = selectedJobState;
const handleSelect = record => {
setSelectedJob(record.id);
};
if (error) return <AlertComponent message={error.message} type="error" />;
return (
<div>
<ContractJobsComponent
handleSelect={handleSelect}
selectedJob={selectedJob}
loading={loading}
data={data ? data.jobs : []}
/>
</div>
);
}
export default connect(mapStateToProps, null)(ContractJobsContainer);

View File

@@ -0,0 +1,33 @@
import React, { useState } from "react";
import { Select } from "antd";
import { useTranslation } from "react-i18next";
const { Option } = Select;
const ContractStatusComponent = ({ value = "", onChange }) => {
const [option, setOption] = useState("contracts.status.new");
const { t } = useTranslation();
const onChangeSelect = newOption => {
setOption(newOption);
if (onChange) {
onChange(newOption);
}
};
return (
<Select
value={option}
style={{
width: 100
}}
onChange={onChangeSelect}
>
<Option value="contracts.status.new">{t("contracts.status.new")}</Option>
<Option value="contracts.status.out">{t("contracts.status.out")}</Option>
<Option value="contracts.status.returned">
{t("contracts.status.returned")}
</Option>
</Select>
);
};
export default ContractStatusComponent;

View File

@@ -1,15 +1,8 @@
import {
CalendarFilled,
CarFilled,
GlobalOutlined,
HomeFilled,
TeamOutlined,
FileAddFilled,
FileFilled
} from "@ant-design/icons";
import Icon, { CarFilled, FileAddFilled, FileFilled, GlobalOutlined, HomeFilled, TeamOutlined } from "@ant-design/icons";
import { Avatar, Col, Menu, Row } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { FaCalendarAlt, FaCarCrash } from "react-icons/fa";
import { Link } from "react-router-dom";
import UserImage from "../../assets/User.svg";
import ManageSignInButton from "../manage-sign-in-button/manage-sign-in-button.component";
@@ -100,10 +93,17 @@ export default ({
{t("menus.header.home")}
</Link>
</Menu.Item>
<Menu.SubMenu title={t("menus.header.jobs")}>
<Menu.SubMenu
title={
<span>
<Icon component={FaCarCrash} />
<span>{t("menus.header.jobs")}</span>
</span>
}
>
<Menu.Item key="schedule">
<Link to="/manage/schedule">
<CalendarFilled />
<Icon component={FaCalendarAlt} />
{t("menus.header.schedule")}
</Link>
</Menu.Item>
@@ -131,7 +131,14 @@ export default ({
</Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu title={t("menus.header.courtesycars")}>
<Menu.SubMenu
title={
<span>
<CarFilled />
<span>{t("menus.header.courtesycars")}</span>
</span>
}
>
<Menu.Item key="courtesycarsall">
<Link to="/manage/courtesycars">
<CarFilled />

View File

@@ -0,0 +1,11 @@
import { gql } from "apollo-boost";
export const INSERT_NEW_CONTRACT = gql`
mutation INSERT_NEW_CONTRACT($contract: [cccontracts_insert_input!]!) {
insert_cccontracts(objects: $contract) {
returning {
id
}
}
}
`;

View File

@@ -12,6 +12,24 @@ export const INSERT_NEW_COURTESY_CAR = gql`
}
`;
export const QUERY_AVAILABLE_CC = gql`
query QUERY_AVAILABLE_CC {
courtesycars(where: {serviceenddate: {_is_null: true}}) {
color
dailycost
damage
fleetnumber
fuel
id
make
model
plate
status
year
}
}
`;
export const QUERY_ALL_CC = gql`
query QUERY_ALL_CC {
courtesycars {

View File

@@ -1,9 +1,20 @@
import React from "react";
import ContractFormComponent from "../../components/contract-form/contract-form.component";
import { Button } from "antd";
import { useTranslation } from "react-i18next";
import ContractJobsContainer from "../../components/contract-jobs/contract-jobs.container";
import ContractCarsContainer from "../../components/contract-cars/contract-cars.container";
export default function ContractCreatePageComponent() {
export default function ContractCreatePageComponent({
selectedJobState,
selectedCarState
}) {
const { t } = useTranslation();
return (
<div>
<Button htmlType="submit">{t("general.actions.create")}</Button>
<ContractJobsContainer selectedJobState={selectedJobState} />
<ContractCarsContainer selectedCarState={selectedCarState} />
<ContractFormComponent />
</div>
);

View File

@@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import React, { useEffect, useState } from "react";
import ContractCreatePageComponent from "./contract-create.page.component";
import { connect } from "react-redux";
@@ -6,7 +6,8 @@ import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { Form, notification } from "antd";
import { useTranslation } from "react-i18next";
import { INSERT_NEW_CONTRACT } from "../../graphql/cccontracts.queries";
import { useMutation } from "@apollo/react-hooks";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
@@ -14,15 +15,33 @@ const mapStateToProps = createStructuredSelector({
export function ContractCreatePageContainer({ bodyshop }) {
const [form] = Form.useForm();
const { t } = useTranslation();
const selectedCarState = useState(null);
const selectedJobState = useState(null);
const [insertContract] = useMutation(INSERT_NEW_CONTRACT);
const handleFinish = values => {
// insertCourtesyCar({
// variables: { courtesycar: { ...values, bodyshopid: bodyshop.id } }
// })
// .then(response => {
notification["success"]({ message: t("courtesycars.successes.saved") });
// })
// .catch(error => console.log("error", error));
if (!!selectedCarState[0] && !!selectedJobState[0]) {
insertContract({
variables: {
contract: {
...values,
courtesycarid: selectedCarState[0],
jobid: selectedJobState[0]
}
}
})
.then(response => {
notification["success"]({
message: t("contracts.successes.saved")
});
})
.catch(error => console.log("error", error));
} else {
notification["error"]({
message: t("contracts.errors.selectjobandcar")
});
}
};
useEffect(() => {
@@ -31,7 +50,11 @@ export function ContractCreatePageContainer({ bodyshop }) {
return (
<Form form={form} autoComplete="no" onFinish={handleFinish}>
<ContractCreatePageComponent />
<button onClick={() => console.log(form.getFieldsValue())}>t</button>
<ContractCreatePageComponent
selectedJobState={selectedJobState}
selectedCarState={selectedCarState}
/>
</Form>
);
}

View File

@@ -140,6 +140,9 @@
}
},
"contracts": {
"errors": {
"selectjobandcar": "Please ensure both a car and job are selected."
},
"fields": {
"actualreturn": "Actual Return Date",
"cc_cardholder": "Cardholder Name",
@@ -162,6 +165,14 @@
"scheduledreturn": "Scheduled Return",
"start": "Contract Start",
"status": "Status"
},
"status": {
"new": "New Contract",
"out": "Current",
"returned": "Returned"
},
"successess": {
"saved": "Contract saved successfully. "
}
},
"courtesycars": {
@@ -190,6 +201,11 @@
"vin": "VIN",
"year": "Year"
},
"statuses": {
"in": "Available",
"inservice": "In Service",
"out": "Rented"
},
"successes": {
"saved": "Courtesy Car saved successfully."
}

View File

@@ -140,6 +140,9 @@
}
},
"contracts": {
"errors": {
"selectjobandcar": ""
},
"fields": {
"actualreturn": "",
"cc_cardholder": "",
@@ -162,6 +165,14 @@
"scheduledreturn": "",
"start": " ",
"status": ""
},
"status": {
"new": "",
"out": "",
"returned": ""
},
"successess": {
"saved": ""
}
},
"courtesycars": {
@@ -190,6 +201,11 @@
"vin": "",
"year": ""
},
"statuses": {
"in": "",
"inservice": "",
"out": ""
},
"successes": {
"saved": ""
}

View File

@@ -140,6 +140,9 @@
}
},
"contracts": {
"errors": {
"selectjobandcar": ""
},
"fields": {
"actualreturn": "",
"cc_cardholder": "",
@@ -162,6 +165,14 @@
"scheduledreturn": "",
"start": "",
"status": ""
},
"status": {
"new": "",
"out": "",
"returned": ""
},
"successess": {
"saved": ""
}
},
"courtesycars": {
@@ -190,6 +201,11 @@
"vin": "",
"year": ""
},
"statuses": {
"in": "",
"inservice": "",
"out": ""
},
"successes": {
"saved": ""
}