- Merge client update into test-beta
Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
@@ -1,125 +1,125 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Card, Form, notification, Popover, Space } from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { INSERT_VACATION } from "../../graphql/employees.queries";
|
||||
import {useMutation} from "@apollo/client";
|
||||
import {Button, Card, Form, notification, Popover, Space} from "antd";
|
||||
import dayjs from "../../utils/day";
|
||||
import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {logImEXEvent} from "../../firebase/firebase.utils";
|
||||
import {INSERT_VACATION} from "../../graphql/employees.queries";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
|
||||
export default function ShopEmployeeAddVacation({ employee }) {
|
||||
const { t } = useTranslation();
|
||||
const [insertVacation] = useMutation(INSERT_VACATION);
|
||||
export default function ShopEmployeeAddVacation({employee}) {
|
||||
const {t} = useTranslation();
|
||||
const [insertVacation] = useMutation(INSERT_VACATION);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
const [visibility, setVisibility] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
const [visibility, setVisibility] = useState(false);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
logImEXEvent("employee_add_vacation");
|
||||
const handleFinish = async (values) => {
|
||||
logImEXEvent("employee_add_vacation");
|
||||
|
||||
setLoading(true);
|
||||
let result;
|
||||
setLoading(true);
|
||||
let result;
|
||||
|
||||
result = await insertVacation({
|
||||
variables: { vacation: { ...values, employeeid: employee.id } },
|
||||
update(cache, { data }) {
|
||||
cache.modify({
|
||||
id: cache.identify({ id: employee.id, __typename: "employees" }),
|
||||
fields: {
|
||||
employee_vacations(ex) {
|
||||
return [data.insert_employee_vacation_one, ...ex];
|
||||
result = await insertVacation({
|
||||
variables: {vacation: {...values, employeeid: employee.id}},
|
||||
update(cache, {data}) {
|
||||
cache.modify({
|
||||
id: cache.identify({id: employee.id, __typename: "employees"}),
|
||||
fields: {
|
||||
employee_vacations(ex) {
|
||||
return [data.insert_employee_vacation_one, ...ex];
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
if (!!result.errors) {
|
||||
notification["error"]({
|
||||
message: t("employees.errors.adding", {
|
||||
message: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
notification["success"]({
|
||||
message: t("employees.successes.vacationadded"),
|
||||
});
|
||||
}
|
||||
form.resetFields();
|
||||
setLoading(false);
|
||||
setVisibility(false);
|
||||
};
|
||||
if (!!result.errors) {
|
||||
notification["error"]({
|
||||
message: t("employees.errors.adding", {
|
||||
message: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
notification["success"]({
|
||||
message: t("employees.successes.vacationadded"),
|
||||
});
|
||||
}
|
||||
form.resetFields();
|
||||
setLoading(false);
|
||||
setVisibility(false);
|
||||
};
|
||||
|
||||
const overlay = (
|
||||
<Card>
|
||||
<Form form={form} layout="vertical" onFinish={handleFinish}>
|
||||
<Form.Item
|
||||
label={t("employees.fields.vacation.start")}
|
||||
name="start"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.vacation.end")}
|
||||
name="end"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
({ getFieldValue }) => ({
|
||||
async validator(rule, value) {
|
||||
if (value) {
|
||||
const { start } = form.getFieldsValue();
|
||||
if (moment(start).isAfter(moment(value))) {
|
||||
return Promise.reject(
|
||||
t("employees.labels.endmustbeafterstart")
|
||||
);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
const overlay = (
|
||||
<Card>
|
||||
<Form form={form} layout="vertical" onFinish={handleFinish}>
|
||||
<Form.Item
|
||||
label={t("employees.fields.vacation.start")}
|
||||
name="start"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<FormDatePicker/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.vacation.end")}
|
||||
name="end"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
({getFieldValue}) => ({
|
||||
async validator(rule, value) {
|
||||
if (value) {
|
||||
const {start} = form.getFieldsValue();
|
||||
if (dayjs(start).isAfter(dayjs(value))) {
|
||||
return Promise.reject(
|
||||
t("employees.labels.endmustbeafterstart")
|
||||
);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<FormDatePicker/>
|
||||
</Form.Item>
|
||||
|
||||
<Space wrap>
|
||||
<Button type="primary" htmlType="submit">
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Button onClick={() => setVisibility(false)}>
|
||||
{t("general.actions.cancel")}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
</Card>
|
||||
);
|
||||
<Space wrap>
|
||||
<Button type="primary" htmlType="submit">
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Button onClick={() => setVisibility(false)}>
|
||||
{t("general.actions.cancel")}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
</Card>
|
||||
);
|
||||
|
||||
const handleClick = (e) => {
|
||||
setVisibility(true);
|
||||
};
|
||||
const handleClick = (e) => {
|
||||
setVisibility(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover content={overlay} visible={visibility}>
|
||||
<Button
|
||||
loading={loading}
|
||||
disabled={!employee?.active}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{t("employees.actions.addvacation")}
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
return (
|
||||
<Popover content={overlay} open={visibility}>
|
||||
<Button
|
||||
loading={loading}
|
||||
disabled={!employee?.active}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{t("employees.actions.addvacation")}
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,438 +1,431 @@
|
||||
import { DeleteFilled } from "@ant-design/icons";
|
||||
import { useApolloClient, useMutation, useQuery } from "@apollo/client";
|
||||
import {DeleteFilled} from "@ant-design/icons";
|
||||
import {useApolloClient, useMutation, useQuery} from "@apollo/client";
|
||||
import {Button, Card, Form, Input, InputNumber, notification, Select, Switch, Table,} from "antd";
|
||||
import {useForm} from "antd/es/form/Form";
|
||||
import dayjs from "../../utils/day";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useLocation, useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {logImEXEvent} from "../../firebase/firebase.utils";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
notification,
|
||||
Select,
|
||||
Switch,
|
||||
Table,
|
||||
} from "antd";
|
||||
|
||||
import moment from "moment";
|
||||
import querystring from "query-string";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import {
|
||||
CHECK_EMPLOYEE_NUMBER,
|
||||
DELETE_VACATION,
|
||||
INSERT_EMPLOYEES,
|
||||
QUERY_EMPLOYEE_BY_ID,
|
||||
QUERY_USERS_BY_EMAIL,
|
||||
UPDATE_EMPLOYEE,
|
||||
CHECK_EMPLOYEE_NUMBER,
|
||||
DELETE_VACATION,
|
||||
INSERT_EMPLOYEES,
|
||||
QUERY_EMPLOYEE_BY_ID,
|
||||
QUERY_USERS_BY_EMAIL,
|
||||
UPDATE_EMPLOYEE,
|
||||
} from "../../graphql/employees.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import CiecaSelect from "../../utils/Ciecaselect";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import {DateFormatter} from "../../utils/DateFormatter";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import ShopEmployeeAddVacation from "./shop-employees-add-vacation.component";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import queryString from "query-string";
|
||||
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export function ShopEmployeesFormComponent({ bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const history = useHistory();
|
||||
const search = querystring.parse(useLocation().search);
|
||||
const [deleteVacation] = useMutation(DELETE_VACATION);
|
||||
const { error, data } = useQuery(QUERY_EMPLOYEE_BY_ID, {
|
||||
variables: { id: search.employeeId },
|
||||
skip: !search.employeeId || search.employeeId === "new",
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const { Enhanced_Payroll } = useTreatments(
|
||||
["Enhanced_Payroll"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const client = useApolloClient();
|
||||
useEffect(() => {
|
||||
if (data && data.employees_by_pk) form.setFieldsValue(data.employees_by_pk);
|
||||
else {
|
||||
form.resetFields();
|
||||
}
|
||||
}, [form, data, search.employeeId]);
|
||||
export function ShopEmployeesFormComponent({bodyshop}) {
|
||||
const {t} = useTranslation();
|
||||
const [form] = useForm();
|
||||
const history = useNavigate();
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const [deleteVacation] = useMutation(DELETE_VACATION);
|
||||
const {error, data} = useQuery(QUERY_EMPLOYEE_BY_ID, {
|
||||
variables: {id: search.employeeId},
|
||||
skip: !search.employeeId || search.employeeId === "new",
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
const [updateEmployee] = useMutation(UPDATE_EMPLOYEE);
|
||||
const [insertEmployees] = useMutation(INSERT_EMPLOYEES);
|
||||
const {treatments: {Enhanced_Payroll}} = useSplitTreatments({
|
||||
attributes: {},
|
||||
names: ["Enhanced_Payroll"],
|
||||
splitKey: bodyshop.imexshopid,
|
||||
});
|
||||
|
||||
const handleFinish = (values) => {
|
||||
if (search.employeeId && search.employeeId !== "new") {
|
||||
//Update a record.
|
||||
logImEXEvent("shop_employee_update");
|
||||
const client = useApolloClient();
|
||||
useEffect(() => {
|
||||
if (data && data.employees_by_pk) form.setFieldsValue(data.employees_by_pk);
|
||||
else {
|
||||
form.resetFields();
|
||||
}
|
||||
}, [form, data, search.employeeId]);
|
||||
|
||||
updateEmployee({
|
||||
variables: {
|
||||
id: search.employeeId,
|
||||
employee: {
|
||||
...values,
|
||||
user_email: values.user_email === "" ? null : values.user_email,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
notification["success"]({
|
||||
message: t("employees.successes.save"),
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
notification["error"]({
|
||||
message: t("employees.errors.save", {
|
||||
message: JSON.stringify(error),
|
||||
}),
|
||||
});
|
||||
});
|
||||
} else {
|
||||
//New record, insert it.
|
||||
logImEXEvent("shop_employee_insert");
|
||||
const [updateEmployee] = useMutation(UPDATE_EMPLOYEE);
|
||||
const [insertEmployees] = useMutation(INSERT_EMPLOYEES);
|
||||
|
||||
insertEmployees({
|
||||
variables: { employees: [{ ...values, shopid: bodyshop.id }] },
|
||||
refetchQueries: ["QUERY_EMPLOYEES"],
|
||||
}).then((r) => {
|
||||
search.employeeId = r.data.insert_employees.returning[0].id;
|
||||
history.push({ search: querystring.stringify(search) });
|
||||
notification["success"]({
|
||||
message: t("employees.successes.save"),
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
const handleFinish = (values) => {
|
||||
if (search.employeeId && search.employeeId !== "new") {
|
||||
//Update a record.
|
||||
logImEXEvent("shop_employee_update");
|
||||
|
||||
if (!search.employeeId) return null;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("employees.fields.vacation.start"),
|
||||
dataIndex: "start",
|
||||
key: "start",
|
||||
render: (text, record) => <DateFormatter>{text}</DateFormatter>,
|
||||
},
|
||||
{
|
||||
title: t("employees.fields.vacation.end"),
|
||||
dataIndex: "end",
|
||||
key: "end",
|
||||
render: (text, record) => <DateFormatter>{text}</DateFormatter>,
|
||||
},
|
||||
{
|
||||
title: t("employees.fields.vacation.length"),
|
||||
dataIndex: "length",
|
||||
key: "length",
|
||||
render: (text, record) =>
|
||||
moment(record.end).diff(moment(record.start), "days", true).toFixed(1),
|
||||
},
|
||||
{
|
||||
title: t("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<Button
|
||||
onClick={async () => {
|
||||
await deleteVacation({
|
||||
variables: { id: record.id },
|
||||
update(cache) {
|
||||
cache.modify({
|
||||
id: cache.identify({
|
||||
id: data.employees_by_pk.id,
|
||||
__typename: "employees",
|
||||
}),
|
||||
|
||||
fields: {
|
||||
employee_vacations(ex, { readField }) {
|
||||
return ex.filter(
|
||||
(vacaref) => record.id !== readField("id", vacaref)
|
||||
);
|
||||
updateEmployee({
|
||||
variables: {
|
||||
id: search.employeeId,
|
||||
employee: {
|
||||
...values,
|
||||
user_email: values.user_email === "" ? null : values.user_email,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
notification["success"]({
|
||||
message: t("employees.successes.save"),
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
notification["error"]({
|
||||
message: t("employees.errors.save", {
|
||||
message: JSON.stringify(error),
|
||||
}),
|
||||
});
|
||||
});
|
||||
} else {
|
||||
//New record, insert it.
|
||||
logImEXEvent("shop_employee_insert");
|
||||
|
||||
insertEmployees({
|
||||
variables: {employees: [{...values, shopid: bodyshop.id}]},
|
||||
refetchQueries: ["QUERY_EMPLOYEES"],
|
||||
}).then((r) => {
|
||||
search.employeeId = r.data.insert_employees.returning[0].id;
|
||||
history({search: queryString.stringify(search)});
|
||||
notification["success"]({
|
||||
message: t("employees.successes.save"),
|
||||
});
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<DeleteFilled />
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
extra={
|
||||
<Button type="primary" onClick={() => form.submit()}>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Form
|
||||
onFinish={handleFinish}
|
||||
autoComplete={"off"}
|
||||
layout="vertical"
|
||||
form={form}
|
||||
>
|
||||
<LayoutFormRow>
|
||||
<Form.Item
|
||||
name="first_name"
|
||||
label={t("employees.fields.first_name")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.last_name")}
|
||||
name="last_name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="employee_number"
|
||||
label={t("employees.fields.employee_number")}
|
||||
validateTrigger="onBlur"
|
||||
hasFeedback
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
({ getFieldValue }) => ({
|
||||
async validator(rule, value) {
|
||||
if (value) {
|
||||
const response = await client.query({
|
||||
query: CHECK_EMPLOYEE_NUMBER,
|
||||
variables: {
|
||||
employeenumber: value,
|
||||
},
|
||||
});
|
||||
if (!search.employeeId) return null;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
if (
|
||||
response.data.employees_aggregate.aggregate.count === 0
|
||||
) {
|
||||
return Promise.resolve();
|
||||
} else if (
|
||||
response.data.employees_aggregate.nodes.length === 1 &&
|
||||
response.data.employees_aggregate.nodes[0].id ===
|
||||
form.getFieldValue("id")
|
||||
) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(
|
||||
t("employees.validation.unique_employee_number")
|
||||
);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.pin")}
|
||||
name="pin"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow>
|
||||
<Form.Item
|
||||
label={t("employees.fields.active")}
|
||||
valuePropName="checked"
|
||||
name="active"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.flat_rate")}
|
||||
name="flat_rate"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="hire_date"
|
||||
label={t("employees.fields.hire_date")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.termination_date")}
|
||||
name="termination_date"
|
||||
>
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.user_email")}
|
||||
name="user_email"
|
||||
validateTrigger="onBlur"
|
||||
rules={[
|
||||
({ getFieldValue }) => ({
|
||||
async validator(rule, value) {
|
||||
const user_email = getFieldValue("user_email");
|
||||
const columns = [
|
||||
{
|
||||
title: t("employees.fields.vacation.start"),
|
||||
dataIndex: "start",
|
||||
key: "start",
|
||||
render: (text, record) => <DateFormatter>{text}</DateFormatter>,
|
||||
},
|
||||
{
|
||||
title: t("employees.fields.vacation.end"),
|
||||
dataIndex: "end",
|
||||
key: "end",
|
||||
render: (text, record) => <DateFormatter>{text}</DateFormatter>,
|
||||
},
|
||||
{
|
||||
title: t("employees.fields.vacation.length"),
|
||||
dataIndex: "length",
|
||||
key: "length",
|
||||
render: (text, record) =>
|
||||
dayjs(record.end).diff(dayjs(record.start), "day", true).toFixed(1),
|
||||
},
|
||||
{
|
||||
title: t("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<Button
|
||||
onClick={async () => {
|
||||
await deleteVacation({
|
||||
variables: {id: record.id},
|
||||
update(cache) {
|
||||
cache.modify({
|
||||
id: cache.identify({
|
||||
id: data.employees_by_pk.id,
|
||||
__typename: "employees",
|
||||
}),
|
||||
|
||||
if (user_email && value) {
|
||||
const response = await client.query({
|
||||
query: QUERY_USERS_BY_EMAIL,
|
||||
variables: {
|
||||
email: user_email,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.data.users.length === 1) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(
|
||||
t("bodyshop.validation.useremailmustexist")
|
||||
);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.external_id")}
|
||||
name="external_id"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<Form.List name={["rates"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
<div>
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item key={field.key} style={{ padding: 0, margin: 2 }}>
|
||||
<LayoutFormRow grow>
|
||||
<Form.Item
|
||||
label={t("employees.fields.cost_center")}
|
||||
key={`${index}`}
|
||||
name={[field.name, "cost_center"]}
|
||||
valuePropName="value"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
<Select.Option
|
||||
key={"shift"}
|
||||
value="timetickets.labels.shift"
|
||||
>
|
||||
{t("timetickets.labels.shift")}
|
||||
</Select.Option>
|
||||
|
||||
{bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === "on"
|
||||
? CiecaSelect(false, true)
|
||||
: bodyshop.md_responsibility_centers.costs.map(
|
||||
(c) => (
|
||||
<Select.Option key={c.name} value={c.name}>
|
||||
{c.name}
|
||||
</Select.Option>
|
||||
)
|
||||
)}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.rate")}
|
||||
key={`${index}`}
|
||||
name={[field.name, "rate"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} precision={2} />
|
||||
</Form.Item>
|
||||
<DeleteFilled
|
||||
onClick={() => {
|
||||
remove(field.name);
|
||||
}}
|
||||
/>
|
||||
<FormListMoveArrows
|
||||
move={move}
|
||||
index={index}
|
||||
total={fields.length}
|
||||
/>
|
||||
</LayoutFormRow>
|
||||
</Form.Item>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
add();
|
||||
fields: {
|
||||
employee_vacations(ex, {readField}) {
|
||||
return ex.filter(
|
||||
(vacaref) => record.id !== readField("id", vacaref)
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}}
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{t("employees.actions.newrate")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Form.List>
|
||||
</Form>
|
||||
>
|
||||
<DeleteFilled/>
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
<Table
|
||||
title={() => (
|
||||
<ShopEmployeeAddVacation employee={data && data.employees_by_pk} />
|
||||
)}
|
||||
columns={columns}
|
||||
rowKey={"id"}
|
||||
dataSource={data ? data.employees_by_pk.employee_vacations : []}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
return (
|
||||
<Card
|
||||
extra={
|
||||
<Button type="primary" onClick={() => form.submit()}>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Form
|
||||
onFinish={handleFinish}
|
||||
autoComplete={"off"}
|
||||
layout="vertical"
|
||||
form={form}
|
||||
>
|
||||
<LayoutFormRow>
|
||||
<Form.Item
|
||||
name="first_name"
|
||||
label={t("employees.fields.first_name")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.last_name")}
|
||||
name="last_name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="employee_number"
|
||||
label={t("employees.fields.employee_number")}
|
||||
validateTrigger="onBlur"
|
||||
hasFeedback
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
({getFieldValue}) => ({
|
||||
async validator(rule, value) {
|
||||
if (value) {
|
||||
const response = await client.query({
|
||||
query: CHECK_EMPLOYEE_NUMBER,
|
||||
variables: {
|
||||
employeenumber: value,
|
||||
},
|
||||
});
|
||||
|
||||
if (
|
||||
response.data.employees_aggregate.aggregate.count === 0
|
||||
) {
|
||||
return Promise.resolve();
|
||||
} else if (
|
||||
response.data.employees_aggregate.nodes.length === 1 &&
|
||||
response.data.employees_aggregate.nodes[0].id ===
|
||||
form.getFieldValue("id")
|
||||
) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(
|
||||
t("employees.validation.unique_employee_number")
|
||||
);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<Input/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.pin")}
|
||||
name="pin"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input/>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow>
|
||||
<Form.Item
|
||||
label={t("employees.fields.active")}
|
||||
valuePropName="checked"
|
||||
name="active"
|
||||
>
|
||||
<Switch/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.flat_rate")}
|
||||
name="flat_rate"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="hire_date"
|
||||
label={t("employees.fields.hire_date")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<FormDatePicker/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.termination_date")}
|
||||
name="termination_date"
|
||||
>
|
||||
<FormDatePicker/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.user_email")}
|
||||
name="user_email"
|
||||
validateTrigger="onBlur"
|
||||
rules={[
|
||||
({getFieldValue}) => ({
|
||||
async validator(rule, value) {
|
||||
const user_email = getFieldValue("user_email");
|
||||
|
||||
if (user_email && value) {
|
||||
const response = await client.query({
|
||||
query: QUERY_USERS_BY_EMAIL,
|
||||
variables: {
|
||||
email: user_email,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.data.users.length === 1) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(
|
||||
t("bodyshop.validation.useremailmustexist")
|
||||
);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<Input/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.external_id")}
|
||||
name="external_id"
|
||||
>
|
||||
<Input/>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<Form.List name={["rates"]}>
|
||||
{(fields, {add, remove, move}) => {
|
||||
return (
|
||||
<div>
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item key={field.key} style={{padding: 0, margin: 2}}>
|
||||
<LayoutFormRow grow>
|
||||
<Form.Item
|
||||
label={t("employees.fields.cost_center")}
|
||||
key={`${index}`}
|
||||
name={[field.name, "cost_center"]}
|
||||
valuePropName="value"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
<Select.Option
|
||||
key={"shift"}
|
||||
value="timetickets.labels.shift"
|
||||
>
|
||||
{t("timetickets.labels.shift")}
|
||||
</Select.Option>
|
||||
|
||||
{bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === "on"
|
||||
? CiecaSelect(false, true)
|
||||
: bodyshop.md_responsibility_centers.costs.map(
|
||||
(c) => (
|
||||
<Select.Option key={c.name} value={c.name}>
|
||||
{c.name}
|
||||
</Select.Option>
|
||||
)
|
||||
)}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.rate")}
|
||||
key={`${index}`}
|
||||
name={[field.name, "rate"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} precision={2}/>
|
||||
</Form.Item>
|
||||
<DeleteFilled
|
||||
onClick={() => {
|
||||
remove(field.name);
|
||||
}}
|
||||
/>
|
||||
<FormListMoveArrows
|
||||
move={move}
|
||||
index={index}
|
||||
total={fields.length}
|
||||
/>
|
||||
</LayoutFormRow>
|
||||
</Form.Item>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
add();
|
||||
}}
|
||||
style={{width: "100%"}}
|
||||
>
|
||||
{t("employees.actions.newrate")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Form.List>
|
||||
</Form>
|
||||
|
||||
<Table
|
||||
title={() => (
|
||||
<ShopEmployeeAddVacation employee={data && data.employees_by_pk}/>
|
||||
)}
|
||||
columns={columns}
|
||||
rowKey={"id"}
|
||||
dataSource={data ? data.employees_by_pk.employee_vacations : []}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ShopEmployeesFormComponent);
|
||||
|
||||
@@ -1,87 +1,86 @@
|
||||
import { Button, Table } from "antd";
|
||||
import {Button, Table} from "antd";
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useLocation, useNavigate} from "react-router-dom";
|
||||
|
||||
export default function ShopEmployeesListComponent({ loading, employees }) {
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const search = queryString.parse(useLocation().search);
|
||||
export default function ShopEmployeesListComponent({loading, employees}) {
|
||||
const {t} = useTranslation();
|
||||
const history = useNavigate();
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const handleOnRowClick = (record) => {
|
||||
if (record) {
|
||||
search.employeeId = record.id;
|
||||
history({search: queryString.stringify(search)});
|
||||
} else {
|
||||
delete search.employeeId;
|
||||
history({search: queryString.stringify(search)});
|
||||
}
|
||||
};
|
||||
const columns = [
|
||||
{
|
||||
title: t("employees.fields.employee_number"),
|
||||
dataIndex: "employee_number",
|
||||
key: "employee_number",
|
||||
},
|
||||
{
|
||||
title: t("employees.fields.first_name"),
|
||||
dataIndex: "first_name",
|
||||
key: "first_name",
|
||||
},
|
||||
{
|
||||
title: t("employees.fields.last_name"),
|
||||
dataIndex: "last_name",
|
||||
key: "last_name",
|
||||
},
|
||||
|
||||
const handleOnRowClick = (record) => {
|
||||
if (record) {
|
||||
search.employeeId = record.id;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
} else {
|
||||
delete search.employeeId;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
}
|
||||
};
|
||||
const columns = [
|
||||
{
|
||||
title: t("employees.fields.employee_number"),
|
||||
dataIndex: "employee_number",
|
||||
key: "employee_number",
|
||||
},
|
||||
{
|
||||
title: t("employees.fields.first_name"),
|
||||
dataIndex: "first_name",
|
||||
key: "first_name",
|
||||
},
|
||||
{
|
||||
title: t("employees.fields.last_name"),
|
||||
dataIndex: "last_name",
|
||||
key: "last_name",
|
||||
},
|
||||
|
||||
{
|
||||
title: t("employees.labels.rate_type"),
|
||||
dataIndex: "rate_type",
|
||||
key: "rate_type",
|
||||
render: (text, record) =>
|
||||
record.flat_rate
|
||||
? t("employees.labels.flat_rate")
|
||||
: t("employees.labels.straight_time"),
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div>
|
||||
<Table
|
||||
title={() => {
|
||||
return (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
search.employeeId = "new";
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
}}
|
||||
>
|
||||
{t("employees.actions.new")}
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
loading={loading}
|
||||
pagination={{ position: "top" }}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={employees}
|
||||
rowSelection={{
|
||||
onSelect: (props) => {
|
||||
search.employeeId = props.id;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
},
|
||||
type: "radio",
|
||||
selectedRowKeys: [search.employeeId],
|
||||
}}
|
||||
onRow={(record, rowIndex) => {
|
||||
return {
|
||||
onClick: (event) => {
|
||||
handleOnRowClick(record);
|
||||
},
|
||||
};
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
{
|
||||
title: t("employees.labels.rate_type"),
|
||||
dataIndex: "rate_type",
|
||||
key: "rate_type",
|
||||
render: (text, record) =>
|
||||
record.flat_rate
|
||||
? t("employees.labels.flat_rate")
|
||||
: t("employees.labels.straight_time"),
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div>
|
||||
<Table
|
||||
title={() => {
|
||||
return (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
search.employeeId = "new";
|
||||
history({search: queryString.stringify(search)});
|
||||
}}
|
||||
>
|
||||
{t("employees.actions.new")}
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
loading={loading}
|
||||
pagination={{position: "top"}}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={employees}
|
||||
rowSelection={{
|
||||
onSelect: (props) => {
|
||||
search.employeeId = props.id;
|
||||
history({search: queryString.stringify(search)});
|
||||
},
|
||||
type: "radio",
|
||||
selectedRowKeys: [search.employeeId],
|
||||
}}
|
||||
onRow={(record, rowIndex) => {
|
||||
return {
|
||||
onClick: (event) => {
|
||||
handleOnRowClick(record);
|
||||
},
|
||||
};
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,36 +1,37 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_EMPLOYEES } from "../../graphql/employees.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {QUERY_EMPLOYEES} from "../../graphql/employees.queries";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import ShopEmployeesFormComponent from "./shop-employees-form.component";
|
||||
import ShopEmployeesListComponent from "./shop-employees-list.component";
|
||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
function ShopEmployeesContainer({ bodyshop }) {
|
||||
const { loading, error, data } = useQuery(QUERY_EMPLOYEES, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
function ShopEmployeesContainer({bodyshop}) {
|
||||
const {loading, error, data} = useQuery(QUERY_EMPLOYEES, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RbacWrapper action="employees:page">
|
||||
<ShopEmployeesListComponent
|
||||
employees={data ? data.employees : []}
|
||||
loading={loading}
|
||||
/>
|
||||
<ShopEmployeesFormComponent />
|
||||
</RbacWrapper>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<RbacWrapper action="employees:page">
|
||||
<ShopEmployeesListComponent
|
||||
employees={data ? data.employees : []}
|
||||
loading={loading}
|
||||
/>
|
||||
<ShopEmployeesFormComponent/>
|
||||
</RbacWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(ShopEmployeesContainer);
|
||||
|
||||
Reference in New Issue
Block a user