Added my shop page and ability to add, edit and delete employees.

This commit is contained in:
Patrick Fic
2020-02-10 12:45:20 -08:00
parent 9d9de14cf0
commit cac3716e03
42 changed files with 1444 additions and 52 deletions

View File

@@ -0,0 +1,22 @@
import { Tag, Popover } from "antd";
import React from "react";
import Barcode from "react-barcode";
import { useTranslation } from "react-i18next";
export default function BarcodePopupComponent({ value }) {
const { t } = useTranslation();
return (
<div>
<Popover
content={
<Barcode
value={value}
background="transparent"
displayValue={false}
/>
}
>
<Tag>{t("general.labels.barcode")}</Tag>
</Popover>
</div>
);
}

View File

@@ -72,6 +72,12 @@ export default ({
</Menu.Item>
</Menu.SubMenu>
<Menu.Item key="shop">
<Link to="/manage/shop">
{t("menus.header.shop")}
</Link>
</Menu.Item>
<Menu.SubMenu
title={
<div>

View File

@@ -20,6 +20,7 @@ import { createStructuredSelector } from "reselect";
import CarImage from "../../assets/car.svg";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import BarcodePopup from "../barcode-popup/barcode-popup.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
@@ -49,32 +50,6 @@ export default connect(
</div>
);
const tombstoneSubtitle = (
<div>
<Tag color="red">
{job.owner ? (
<Link to={`/manage/owners/${job.owner.id}`}>
{`${job.ownr_co_nm || ""}${job.ownr_fn || ""} ${job.ownr_ln || ""}`}
</Link>
) : (
t("jobs.errors.noowner")
)}
</Tag>
<Tag color="green">
{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}
</Tag>
</div>
);
const statusmenu = (
<Menu
onClick={e => {
@@ -137,10 +112,32 @@ export default connect(
border: "1px solid rgb(235, 237, 240)"
}}
title={tombstoneTitle}
subTitle={tombstoneSubtitle}
//subTitle={tombstoneSubtitle}
tags={
<span key="job-status">
{job.status ? <Tag color="blue">{job.status}</Tag> : null}
<Tag color="red">
{job.owner ? (
<Link to={`/manage/owners/${job.owner.id}`}>
{`${job.ownr_co_nm || ""}${job.ownr_fn || ""} ${job.ownr_ln ||
""}`}
</Link>
) : (
t("jobs.errors.noowner")
)}
</Tag>
<Tag color="green">
{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}
</Tag>
<BarcodePopup value={job.id} />
</span>
}
extra={menuExtra}

View File

@@ -0,0 +1,112 @@
import { Button, DatePicker, Form, Input, InputNumber, Switch } from "antd";
import moment from "moment";
import React from "react";
import { useTranslation } from "react-i18next";
export default function ShopEmployeesFormComponent({
form,
selectedEmployee,
handleSubmit
}) {
const { t } = useTranslation();
const { getFieldDecorator } = form;
if (!selectedEmployee) return "//TODO No employee selected.";
return (
<Form onSubmit={handleSubmit} autoComplete={"off"}>
<Button type="primary" htmlType="submit">
{t("general.actions.save")}
</Button>
<Form.Item label={t("employees.fields.first_name")}>
{getFieldDecorator("first_name", {
initialValue: selectedEmployee.first_name,
rules: [
{
required: true,
message: t("general.validation.required")
}
]
})(<Input name="first_name" />)}
</Form.Item>
<Form.Item label={t("employees.fields.last_name")}>
{getFieldDecorator("last_name", {
initialValue: selectedEmployee.last_name,
rules: [
{
required: true,
message: t("general.validation.required")
}
]
})(<Input name="last_name" />)}
</Form.Item>
<Form.Item label={t("employees.fields.employee_number")}>
{getFieldDecorator("employee_number", {
initialValue: selectedEmployee.employee_number,
rules: [
{
required: true,
message: t("general.validation.required")
}
]
})(<Input name="employee_number" />)}
</Form.Item>
<Form.Item label={t("employees.fields.active")}>
{getFieldDecorator("active", {
initialValue: selectedEmployee.active,
valuePropName: "checked"
})(<Switch name="active" />)}
</Form.Item>
<Form.Item label={t("employees.fields.flat_rate")}>
{getFieldDecorator("flat_rate", {
initialValue: selectedEmployee.flat_rate,
valuePropName: "checked"
})(<Switch name="active" />)}
</Form.Item>
<Form.Item label={t("employees.fields.hire_date")}>
{getFieldDecorator("hire_date", {
initialValue: selectedEmployee.hire_date
? moment(selectedEmployee.hire_date)
: null,
rules: [
{
required: true,
message: t("general.validation.required")
}
]
})(<DatePicker name="hire_date" />)}
</Form.Item>
<Form.Item label={t("employees.fields.termination_date")}>
{getFieldDecorator("termination_date", {
initialValue: selectedEmployee.termination_date
? moment(selectedEmployee.termination_date)
: null
})(<DatePicker name="termination_date" />)}
</Form.Item>
{
//TODO Make this a picklist.
}
<Form.Item label={t("employees.fields.cost_center")}>
{getFieldDecorator("cost_center", {
initialValue: selectedEmployee.cost_center,
rules: [
{
required: true,
message: t("general.validation.required")
}
]
})(<Input name="cost_center" />)}
</Form.Item>
<Form.Item label={t("employees.fields.base_rate")}>
{getFieldDecorator("base_rate", {
initialValue: selectedEmployee.base_rate,
rules: [
{
required: true,
message: t("general.validation.required")
}
]
})(<InputNumber name="base_rate" />)}
</Form.Item>
</Form>
);
}

View File

@@ -0,0 +1,52 @@
import { Button, List } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
export default function ShopEmployeesListComponent({
loading,
employees,
setSelectedEmployee,
handleDelete
}) {
const { t } = useTranslation();
return (
<div>
<Button
type="primary"
onClick={() => {
setSelectedEmployee({});
}}
>
{t("employees.actions.new")}
</Button>
<List
loading={loading}
itemLayout="horizontal"
dataSource={employees}
renderItem={item => (
<List.Item
actions={[
<Button key="edit" onClick={() => setSelectedEmployee(item)}>
{t("general.actions.edit")}
</Button>,
<Button key="delete" onClick={() => handleDelete(item.id)}>
{t("general.actions.delete")}
</Button>
]}
>
<List.Item.Meta
title={`${item.first_name || ""} ${item.last_name ||
""} | ${item.employee_number || ""}`}
description={
<span>
{item.cost_center} @{" "}
<CurrencyFormatter>{item.base_rate}</CurrencyFormatter>
</span>
}
/>
</List.Item>
)}
/>
</div>
);
}

View File

@@ -0,0 +1,34 @@
import { Col, Row } from "antd";
import React from "react";
import ShopEmployeesFormComponent from "./shop-employees-form.component";
import ShopEmployeesListComponent from "./shop-employees-list.component";
export default function ShopEmployeeComponent({
form,
loading,
employees,
employeeState,
handleSubmit,
handleDelete
}) {
const [selectedEmployee, setSelectedEmployee] = employeeState;
return (
<Row>
<Col span={6}>
<ShopEmployeesListComponent
employees={employees}
loading={loading}
setSelectedEmployee={setSelectedEmployee}
handleDelete={handleDelete}
/>
</Col>
<Col span={18}>
<ShopEmployeesFormComponent
handleSubmit={handleSubmit}
form={form}
selectedEmployee={selectedEmployee}
/>
</Col>
</Row>
);
}

View File

@@ -0,0 +1,112 @@
import { Form, notification } from "antd";
import React, { useState } from "react";
import { useMutation, useQuery } from "react-apollo";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { DELETE_EMPLOYEE, INSERT_EMPLOYEES, QUERY_EMPLOYEES, UPDATE_EMPLOYEE } from "../../graphql/employees.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import ShopEmployeeComponent from "./shop-employees.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
function ShopEmployeesContainer({ form, bodyshop }) {
const { t } = useTranslation();
const employeeState = useState(null);
const { loading, error, data, refetch } = useQuery(QUERY_EMPLOYEES, {
fetchPolicy: "network-only"
});
const [updateEmployee] = useMutation(UPDATE_EMPLOYEE);
const [insertEmployees] = useMutation(INSERT_EMPLOYEES);
const [deleteEmployee] = useMutation(DELETE_EMPLOYEE);
const handleDelete = id => {
deleteEmployee({ variables: { id: id } })
.then(r => {
notification["success"]({
message: t("employees.successes.delete")
});
//TODO: Better way to reset the field decorators?
employeeState[1](null);
refetch().then(r => form.resetFields());
})
.catch(error => {
notification["error"]({
message: t("employees.errors.delete")
});
});
};
const handleSubmit = e => {
e.preventDefault();
form.validateFieldsAndScroll((err, values) => {
if (err) {
notification["error"]({
message: t("employees.errors.validationtitle"),
description: t("employees.errors.validation")
});
}
if (!err) {
if (employeeState[0].id) {
//Update a record.
updateEmployee({
variables: { id: employeeState[0].id, employee: values }
})
.then(r => {
notification["success"]({
message: t("employees.successes.save")
});
//TODO: Better way to reset the field decorators?
employeeState[1](null);
refetch().then(r => form.resetFields());
})
.catch(error => {
notification["error"]({
message: t("employees.errors.save")
});
});
} else {
//New record, insert it.
insertEmployees({
variables: { employees: [{ ...values, shopid: bodyshop.id }] }
}).then(r => {
notification["success"]({
message: t("employees.successes.save")
});
//TODO: Better way to reset the field decorators?
employeeState[1](null);
refetch()
.then(r => form.resetFields())
.catch(error => {
notification["error"]({
message: t("employees.errors.save")
});
});
});
}
}
});
};
if (error) return <AlertComponent message={error.message} type="error" />;
return (
<ShopEmployeeComponent
handleSubmit={handleSubmit}
handleDelete={handleDelete}
form={form}
loading={loading}
employeeState={employeeState}
employees={data ? data.employees : []}
/>
);
}
export default connect(
mapStateToProps,
null
)(Form.create({ name: "ShopEmployeesContainer" })(ShopEmployeesContainer));

View File

@@ -1,9 +1,8 @@
import { Input, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { alphaSort } from "../../utils/sorters";
import { Link } from "react-router-dom";
import PhoneFormatter from "../../utils/PhoneFormatter";
import { Table, Icon, Input } from "antd";
import { alphaSort } from "../../utils/sorters";
export default function VehiclesListComponent({ loading, vehicles, refetch }) {
const [state, setState] = useState({