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

@@ -618,12 +618,415 @@
</folder_node>
</children>
</folder_node>
<folder_node>
<name>employees</name>
<children>
<folder_node>
<name>actions</name>
<children>
<concept_node>
<name>new</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>errors</name>
<children>
<concept_node>
<name>delete</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>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>
<concept_node>
<name>validation</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>validationtitle</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>active</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>base_rate</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>cost_center</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>employee_number</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>first_name</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>flat_rate</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>hire_date</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>last_name</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>termination_date</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>successes</name>
<children>
<concept_node>
<name>delete</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>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>
<name>general</name>
<children>
<folder_node>
<name>actions</name>
<children>
<concept_node>
<name>delete</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>edit</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>reset</name>
<definition_loaded>false</definition_loaded>
@@ -645,6 +1048,27 @@
</translation>
</translations>
</concept_node>
<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>
<folder_node>
@@ -671,6 +1095,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>barcode</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>in</name>
<definition_loaded>false</definition_loaded>
@@ -914,6 +1359,32 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>validation</name>
<children>
<concept_node>
<name>required</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>
@@ -4143,6 +4614,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>shop</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>vehicles</name>
<definition_loaded>false</definition_loaded>
@@ -5228,6 +5720,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>owners</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>profile</name>
<definition_loaded>false</definition_loaded>
@@ -5270,6 +5783,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>shop</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>vehicledetail</name>
<definition_loaded>false</definition_loaded>
@@ -5291,6 +5825,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>vehicles</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>

View File

@@ -19,6 +19,7 @@
"node-sass": "^4.13.1",
"react": "^16.12.0",
"react-apollo": "^3.1.3",
"react-barcode": "^1.4.0",
"react-big-calendar": "^0.23.0",
"react-chartjs-2": "^2.9.0",
"react-dom": "^16.12.0",

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({

View File

@@ -0,0 +1,48 @@
import { gql } from "apollo-boost";
export const QUERY_EMPLOYEES = gql`
query QUERY_EMPLOYEES {
employees {
last_name
id
first_name
employee_number
active
termination_date
hire_date
flat_rate
cost_center
base_rate
}
}
`;
export const INSERT_EMPLOYEES = gql`
mutation INSERT_EMPLOYEES($employees: [employees_insert_input!]!) {
insert_employees(objects: $employees) {
returning {
id
}
}
}
`;
export const UPDATE_EMPLOYEE = gql`
mutation UPDATE_EMPLOYEE($id: uuid!, $employee: employees_set_input) {
update_employees(where: { id: { _eq: $id } }, _set: $employee) {
returning {
id
}
}
}
`;
export const DELETE_EMPLOYEE = gql`
mutation DELETE_EMPLOYEE($id: uuid!) {
delete_employees(where: { id: { _eq: $id } }) {
returning {
id
}
}
}
`;

View File

@@ -1,11 +1,10 @@
import React, { useEffect, useState } from "react";
import { useQuery } from "@apollo/react-hooks";
import AlertComponent from "../../components/alert/alert.component";
import { Col } from "antd";
import { QUERY_ALL_OPEN_JOBS } from "../../graphql/jobs.queries";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import JobsList from "../../components/jobs-list/jobs-list.component";
import AlertComponent from "../../components/alert/alert.component";
import JobDetailCards from "../../components/job-detail-cards/job-detail-cards.component";
import JobsList from "../../components/jobs-list/jobs-list.component";
import { QUERY_ALL_OPEN_JOBS } from "../../graphql/jobs.queries";
//TODO: Implement pagination for this.
export default function JobsPage({ match, location }) {
@@ -21,9 +20,9 @@ export default function JobsPage({ match, location }) {
const { hash } = location;
const [selectedJob, setSelectedJob] = useState(hash ? hash.substr(1) : null);
if (error) return <AlertComponent message={error.message} type='error' />;
if (error) return <AlertComponent message={error.message} type="error" />;
return (
<Col span={22} offset={1}>
<div>
<JobsList
loading={loading}
selectedJob={selectedJob}
@@ -31,6 +30,6 @@ export default function JobsPage({ match, location }) {
jobs={data ? data.jobs : null}
/>
<JobDetailCards selectedJob={selectedJob} />
</Col>
</div>
);
}

View File

@@ -39,6 +39,8 @@ const OwnersContainer = lazy(() => import("../owners/owners.page.container"));
const OwnersDetailContainer = lazy(() =>
import("../owners-detail/owners-detail.page.container")
);
const ShopPage = lazy(() => import("../shop/shop.page.component"));
const { Header, Content, Footer } = Layout;
export default function Manage({ match }) {
@@ -56,7 +58,10 @@ export default function Manage({ match }) {
<Layout>
<ChatWindowContainer />
<Content className="content-container" style={{ margin: "0px" }}>
<Content
className="content-container"
style={{ padding: "0em 4em 4em" }}
>
<ErrorBoundary>
<Suspense
fallback={
@@ -113,6 +118,8 @@ export default function Manage({ match }) {
path={`${match.path}/available`}
component={JobsAvailablePage}
/>
<Route exact path={`${match.path}/shop/`} component={ShopPage} />
</Suspense>
</ErrorBoundary>
</Content>

View File

@@ -1,8 +1,12 @@
import React from 'react'
import OwnersPageComponent from './owners.page.component'
import React, { useEffect } from "react";
import OwnersPageComponent from "./owners.page.component";
import { useTranslation } from "react-i18next";
export default function OwnersPageContainer() {
return (
<OwnersPageComponent />
)
const { t } = useTranslation();
useEffect(() => {
document.title = t("titles.owners");
}, [t]);
return <OwnersPageComponent />;
}

View File

@@ -0,0 +1,25 @@
import { Tabs } from "antd";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import ShopEmployeesContainer from "../../components/shop-employees/shop-employees.container";
export default function ShopPage() {
const { t } = useTranslation();
useEffect(() => {
document.title = t("titles.shop");
}, [t]);
return (
<Tabs>
<Tabs.TabPane tab="Shop Info" key="info">
Shop INfo
</Tabs.TabPane>
<Tabs.TabPane tab="Employees" key="employees">
<ShopEmployeesContainer />
</Tabs.TabPane>
<Tabs.TabPane tab="Licensing" key="licensing">
Licensing
</Tabs.TabPane>
</Tabs>
);
}

View File

@@ -1,6 +1,10 @@
import React from "react";
import React, { useEffect } from "react";
import VehiclesPageComponent from "./vehicles.page.component";
import { useTranslation } from "react-i18next";
export default function VehiclesPageContainer() {
const { t } = useTranslation();
useEffect(() => {
document.title = t("titles.vehicles");
}, [t]);
return <VehiclesPageComponent />;
}

View File

@@ -55,12 +55,42 @@
"insert": "Uploaded document successfully. "
}
},
"employees": {
"actions": {
"new": "New Employee"
},
"errors": {
"delete": "Error encountered while deleting employee.",
"save": "Error encountered saving employee.",
"validation": "Please check all fields.",
"validationtitle": "Unable to save employee."
},
"fields": {
"active": "Active?",
"base_rate": "Base Rate",
"cost_center": "Cost Center",
"employee_number": "Employee Number",
"first_name": "First Name",
"flat_rate": "Flat Rate (Disabled is Straight Time)",
"hire_date": "Hire Date",
"last_name": "Last Name",
"termination_date": "Termination Date"
},
"successes": {
"delete": "Employee deleted successfully.",
"save": "Employee saved successfully."
}
},
"general": {
"actions": {
"reset": "Reset to original."
"delete": "Delete",
"edit": "Edit",
"reset": "Reset to original.",
"save": "Save"
},
"labels": {
"actions": "Actions",
"barcode": "Barcode",
"in": "In",
"loading": "Loading...",
"loadingapp": "Loading Bodyshop.app",
@@ -76,6 +106,9 @@
},
"messages": {
"unsavedchanges": "You have unsaved changes."
},
"validation": {
"required": "This field is required. "
}
},
"joblines": {
@@ -251,6 +284,7 @@
"jobs": "Jobs",
"owners": "Owners",
"schedule": "Schedule",
"shop": "My Shop",
"vehicles": "Vehicles"
},
"jobsdetail": {
@@ -330,9 +364,12 @@
"jobsdetail": "Job {{ro_number}} | $t(titles.app)",
"jobsdocuments": "Job Documents {{ro_number}} | $t(titles.app)",
"manageroot": "Home | $t(titles.app)",
"owners": "All Owners | $t(titles.app)",
"profile": "My Profile | $t(titles.app)",
"schedule": "Schedule | $t(titles.app)",
"vehicledetail": "Vehicle Details {{vehicle}} | $t(titles.app)"
"shop": "My Shop | $t(titles.app)",
"vehicledetail": "Vehicle Details {{vehicle}} | $t(titles.app)",
"vehicles": "All Vehicles | $t(titles.app)"
},
"user": {
"actions": {

View File

@@ -55,12 +55,42 @@
"insert": "Documento cargado con éxito."
}
},
"employees": {
"actions": {
"new": "Nuevo empleado"
},
"errors": {
"delete": "Se encontró un error al eliminar al empleado.",
"save": "Se encontró un error al salvar al empleado.",
"validation": "Por favor verifique todos los campos.",
"validationtitle": "No se puede salvar al empleado."
},
"fields": {
"active": "¿Activo?",
"base_rate": "Tasa básica",
"cost_center": "Centro de costos",
"employee_number": "Numero de empleado",
"first_name": "Nombre de pila",
"flat_rate": "Tarifa plana (deshabilitado es tiempo recto)",
"hire_date": "Fecha de contratación",
"last_name": "Apellido",
"termination_date": "Fecha de conclusión"
},
"successes": {
"delete": "Empleado eliminado con éxito.",
"save": "Empleado guardado con éxito."
}
},
"general": {
"actions": {
"reset": "Restablecer a original."
"delete": "Borrar",
"edit": "Editar",
"reset": "Restablecer a original.",
"save": "Salvar"
},
"labels": {
"actions": "Comportamiento",
"barcode": "código de barras",
"in": "en",
"loading": "Cargando...",
"loadingapp": "Cargando Bodyshop.app",
@@ -76,6 +106,9 @@
},
"messages": {
"unsavedchanges": "Usted tiene cambios no guardados."
},
"validation": {
"required": "Este campo es requerido."
}
},
"joblines": {
@@ -251,6 +284,7 @@
"jobs": "Trabajos",
"owners": "propietarios",
"schedule": "Programar",
"shop": "Mi tienda",
"vehicles": "Vehículos"
},
"jobsdetail": {
@@ -330,9 +364,12 @@
"jobsdetail": "Trabajo {{ro_number}} | $t(titles.app)",
"jobsdocuments": "Documentos de trabajo {{ro_number}} | $ t (títulos.app)",
"manageroot": "Casa | $t(titles.app)",
"owners": "Todos los propietarios | $t(titles.app)",
"profile": "Mi perfil | $t(titles.app)",
"schedule": "Horario | $t(titles.app)",
"vehicledetail": "Detalles del vehículo {{vehicle}} | $t(titles.app)"
"shop": "Mi tienda | $t(titles.app)",
"vehicledetail": "Detalles del vehículo {{vehicle}} | $t(titles.app)",
"vehicles": "Todos los vehiculos | $t(titles.app)"
},
"user": {
"actions": {

View File

@@ -55,12 +55,42 @@
"insert": "Document téléchargé avec succès."
}
},
"employees": {
"actions": {
"new": "Nouvel employé"
},
"errors": {
"delete": "Erreur rencontrée lors de la suppression de l'employé.",
"save": "Une erreur s'est produite lors de l'enregistrement de l'employé.",
"validation": "Veuillez cocher tous les champs.",
"validationtitle": "Impossible d'enregistrer l'employé."
},
"fields": {
"active": "Actif?",
"base_rate": "Taux de base",
"cost_center": "Centre de coûts",
"employee_number": "Numéro d'employé",
"first_name": "Prénom",
"flat_rate": "Taux fixe (désactivé est le temps normal)",
"hire_date": "Date d'embauche",
"last_name": "Nom de famille",
"termination_date": "Date de résiliation"
},
"successes": {
"delete": "L'employé a bien été supprimé.",
"save": "L'employé a enregistré avec succès."
}
},
"general": {
"actions": {
"reset": "Rétablir l'original."
"delete": "Effacer",
"edit": "modifier",
"reset": "Rétablir l'original.",
"save": "sauvegarder"
},
"labels": {
"actions": "actes",
"barcode": "code à barre",
"in": "dans",
"loading": "Chargement...",
"loadingapp": "Chargement de Bodyshop.app",
@@ -76,6 +106,9 @@
},
"messages": {
"unsavedchanges": "Vous avez des changements non enregistrés."
},
"validation": {
"required": "Ce champ est requis."
}
},
"joblines": {
@@ -251,6 +284,7 @@
"jobs": "Emplois",
"owners": "Propriétaires",
"schedule": "Programme",
"shop": "Mon magasin",
"vehicles": "Véhicules"
},
"jobsdetail": {
@@ -330,9 +364,12 @@
"jobsdetail": "Travail {{ro_number}} | $t(titles.app)",
"jobsdocuments": "Documents de travail {{ro_number}} | $ t (titres.app)",
"manageroot": "Accueil | $t(titles.app)",
"owners": "Tous les propriétaires | $t(titles.app)",
"profile": "Mon profil | $t(titles.app)",
"schedule": "Horaire | $t(titles.app)",
"vehicledetail": "Détails du véhicule {{vehicle} | $t(titles.app)"
"shop": "Mon magasin | $t(titles.app)",
"vehicledetail": "Détails du véhicule {{vehicle} | $t(titles.app)",
"vehicles": "Tous les véhicules | $t(titles.app)"
},
"user": {
"actions": {

View File

@@ -8000,6 +8000,11 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
jsbarcode@^3.8.0:
version "3.11.0"
resolved "https://registry.yarnpkg.com/jsbarcode/-/jsbarcode-3.11.0.tgz#20623e008b101ef45d0cce9c8022cdf49be28547"
integrity sha512-/ozCd7wsa+VIHo9sUc03HneVEQrH7cVWfJolUT/WOW1m8mJ2e3iYZje6C9X3LFHdczlesqFHRpxLtbVsNtjyow==
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
@@ -11302,6 +11307,14 @@ react-app-polyfill@^1.0.6:
regenerator-runtime "^0.13.3"
whatwg-fetch "^3.0.0"
react-barcode@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/react-barcode/-/react-barcode-1.4.0.tgz#ebc85ff1b8b49ac4b947b7f3745d684c6c363902"
integrity sha512-xDxIc9WUTQPMcwc3kbCVsjh03WiPyynms9OSdsNSh2AAgB6XqMiy9hYWkCNHgdNdvurkvKB6hm25AZHXyQfvlQ==
dependencies:
jsbarcode "^3.8.0"
prop-types "^15.6.2"
react-big-calendar@^0.23.0:
version "0.23.0"
resolved "https://registry.yarnpkg.com/react-big-calendar/-/react-big-calendar-0.23.0.tgz#cc780481548fcefabb04cea05dd1a08456702a67"

View File

@@ -0,0 +1,3 @@
- args:
sql: ALTER TABLE "public"."employees" DROP COLUMN "hire_date";
type: run_sql

View File

@@ -0,0 +1,3 @@
- args:
sql: ALTER TABLE "public"."employees" ADD COLUMN "hire_date" date NULL;
type: run_sql

View File

@@ -0,0 +1,3 @@
- args:
sql: ALTER TABLE "public"."employees" DROP COLUMN "termination_date";
type: run_sql

View File

@@ -0,0 +1,3 @@
- args:
sql: ALTER TABLE "public"."employees" ADD COLUMN "termination_date" date NULL;
type: run_sql

View File

@@ -0,0 +1,3 @@
- args:
sql: ALTER TABLE "public"."employees" DROP COLUMN "base_rate";
type: run_sql

View File

@@ -0,0 +1,3 @@
- args:
sql: ALTER TABLE "public"."employees" ADD COLUMN "base_rate" numeric NULL;
type: run_sql

View File

@@ -0,0 +1,3 @@
- args:
sql: ALTER TABLE "public"."employees" DROP COLUMN "cost_center";
type: run_sql

View File

@@ -0,0 +1,3 @@
- args:
sql: ALTER TABLE "public"."employees" ADD COLUMN "cost_center" text NOT NULL;
type: run_sql

View File

@@ -0,0 +1,10 @@
- args:
sql: ALTER TABLE ONLY "public"."employees" ALTER COLUMN "base_rate" SET DEFAULT
0;
type: run_sql
- args:
sql: ALTER TABLE "public"."employees" ALTER COLUMN "base_rate" DROP NOT NULL;
type: run_sql
- args:
sql: COMMENT ON COLUMN "public"."employees"."base_rate" IS E'null'
type: run_sql

View File

@@ -0,0 +1,10 @@
- args:
sql: ALTER TABLE ONLY "public"."employees" ALTER COLUMN "base_rate" SET DEFAULT
0;
type: run_sql
- args:
sql: ALTER TABLE "public"."employees" ALTER COLUMN "base_rate" SET NOT NULL;
type: run_sql
- args:
sql: COMMENT ON COLUMN "public"."employees"."base_rate" IS E''
type: run_sql

View File

@@ -0,0 +1,3 @@
- args:
sql: ALTER TABLE "public"."employees" DROP COLUMN "pay_type";
type: run_sql

View File

@@ -0,0 +1,4 @@
- args:
sql: ALTER TABLE "public"."employees" ADD COLUMN "pay_type" integer NOT NULL DEFAULT
0;
type: run_sql

View File

@@ -0,0 +1,9 @@
- args:
sql: ALTER TABLE "public"."employees" ADD COLUMN "pay_type" int4
type: run_sql
- args:
sql: ALTER TABLE "public"."employees" ALTER COLUMN "pay_type" DROP NOT NULL
type: run_sql
- args:
sql: ALTER TABLE "public"."employees" ALTER COLUMN "pay_type" SET DEFAULT 0
type: run_sql

View File

@@ -0,0 +1,3 @@
- args:
sql: ALTER TABLE "public"."employees" DROP COLUMN "pay_type" CASCADE
type: run_sql

View File

@@ -0,0 +1,3 @@
- args:
sql: ALTER TABLE "public"."employees" DROP COLUMN "flat_rate";
type: run_sql

View File

@@ -0,0 +1,4 @@
- args:
sql: ALTER TABLE "public"."employees" ADD COLUMN "flat_rate" boolean NOT NULL
DEFAULT false;
type: run_sql

View File

@@ -0,0 +1,35 @@
- args:
role: user
table:
name: employees
schema: public
type: drop_insert_permission
- args:
permission:
check:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- active
- employee_number
- first_name
- last_name
- created_at
- updated_at
- id
- shopid
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: employees
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,40 @@
- args:
role: user
table:
name: employees
schema: public
type: drop_insert_permission
- args:
permission:
check:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- id
- created_at
- updated_at
- first_name
- last_name
- employee_number
- shopid
- active
- hire_date
- termination_date
- base_rate
- cost_center
- flat_rate
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: employees
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,33 @@
- args:
role: user
table:
name: employees
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- active
- employee_number
- first_name
- last_name
- created_at
- updated_at
- id
- shopid
computed_fields: []
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: employees
schema: public
type: create_select_permission

View File

@@ -0,0 +1,38 @@
- args:
role: user
table:
name: employees
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- active
- flat_rate
- hire_date
- termination_date
- base_rate
- cost_center
- employee_number
- first_name
- last_name
- created_at
- updated_at
- id
- shopid
computed_fields: []
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: employees
schema: public
type: create_select_permission

View File

@@ -0,0 +1,35 @@
- args:
role: user
table:
name: employees
schema: public
type: drop_update_permission
- args:
permission:
columns:
- active
- employee_number
- first_name
- last_name
- created_at
- updated_at
- id
- shopid
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: employees
schema: public
type: create_update_permission

View File

@@ -0,0 +1,40 @@
- args:
role: user
table:
name: employees
schema: public
type: drop_update_permission
- args:
permission:
columns:
- active
- flat_rate
- hire_date
- termination_date
- base_rate
- cost_center
- employee_number
- first_name
- last_name
- created_at
- updated_at
- id
- shopid
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: employees
schema: public
type: create_update_permission