- Merge client update into test-beta
Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
@@ -1,91 +1,96 @@
|
||||
import { Divider, Form, Select } from "antd";
|
||||
import {Divider, Form, Select} from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {selectTechnician} from "../../redux/tech/tech.selectors";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
||||
import JobsDetailLaborContainer from "../jobs-detail-labor/jobs-detail-labor.container";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
technician: selectTechnician,
|
||||
bodyshop: selectBodyshop,
|
||||
technician: selectTechnician,
|
||||
});
|
||||
|
||||
export function TechClockInComponent({ form, bodyshop, technician }) {
|
||||
const { t } = useTranslation();
|
||||
const { Enhanced_Payroll } = useTreatments(
|
||||
["Enhanced_Payroll"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const emps = bodyshop.employees.filter((e) => e.id === technician.id)[0];
|
||||
return (
|
||||
<div>
|
||||
<LayoutFormRow grow noDivider>
|
||||
<Form.Item
|
||||
name="jobid"
|
||||
label={t("jobs.fields.ro_number")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<JobSearchSelect
|
||||
convertedOnly={!bodyshop.tt_allow_post_to_invoiced}
|
||||
notExported={!bodyshop.tt_allow_post_to_invoiced}
|
||||
notInvoiced={!bodyshop.tt_allow_post_to_invoiced}
|
||||
/>
|
||||
</Form.Item>
|
||||
export function TechClockInComponent({form, bodyshop, technician}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
<Form.Item
|
||||
name="cost_center"
|
||||
label={t("timetickets.fields.cost_center")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
{emps &&
|
||||
emps.rates.map((item) => (
|
||||
<Select.Option key={item.cost_center} value={item.cost_center}>
|
||||
{item.cost_center === "timetickets.labels.shift"
|
||||
? t(item.cost_center)
|
||||
: bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === "on"
|
||||
? t(
|
||||
`joblines.fields.lbr_types.${item.cost_center.toUpperCase()}`
|
||||
)
|
||||
: item.cost_center}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<Divider />
|
||||
<Form.Item
|
||||
shouldUpdate={(prevValues, curValues) =>
|
||||
prevValues.jobid !== curValues.jobid
|
||||
}
|
||||
>
|
||||
{() => {
|
||||
if (!form.getFieldValue("jobid")) return null;
|
||||
return (
|
||||
<JobsDetailLaborContainer
|
||||
jobId={form.getFieldValue("jobid")}
|
||||
techConsole
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
|
||||
const {treatments: {Enhanced_Payroll}} = useSplitTreatments({
|
||||
attributes: {},
|
||||
names: ["Enhanced_Payroll"],
|
||||
splitKey: bodyshop.imexshopid,
|
||||
});
|
||||
|
||||
const emps = bodyshop.employees.filter((e) => e.id === technician.id)[0];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<LayoutFormRow grow noDivider>
|
||||
<Form.Item
|
||||
name="jobid"
|
||||
label={t("jobs.fields.ro_number")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<JobSearchSelect
|
||||
convertedOnly={!bodyshop.tt_allow_post_to_invoiced}
|
||||
notExported={!bodyshop.tt_allow_post_to_invoiced}
|
||||
notInvoiced={!bodyshop.tt_allow_post_to_invoiced}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="cost_center"
|
||||
label={t("timetickets.fields.cost_center")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
{emps &&
|
||||
emps.rates.map((item) => (
|
||||
<Select.Option key={item.cost_center} value={item.cost_center}>
|
||||
{item.cost_center === "timetickets.labels.shift"
|
||||
? t(item.cost_center)
|
||||
: bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === "on"
|
||||
? t(
|
||||
`joblines.fields.lbr_types.${item.cost_center.toUpperCase()}`
|
||||
)
|
||||
: item.cost_center}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<Divider/>
|
||||
<Form.Item
|
||||
shouldUpdate={(prevValues, curValues) =>
|
||||
prevValues.jobid !== curValues.jobid
|
||||
}
|
||||
>
|
||||
{() => {
|
||||
if (!form.getFieldValue("jobid")) return null;
|
||||
return (
|
||||
<JobsDetailLaborContainer
|
||||
jobId={form.getFieldValue("jobid")}
|
||||
techConsole
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(TechClockInComponent);
|
||||
|
||||
@@ -1,160 +1,168 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Card, Form, notification, Space } from "antd";
|
||||
import {useMutation} from "@apollo/client";
|
||||
import {Button, Card, Form, notification, Space} from "antd";
|
||||
import axios from "axios";
|
||||
import moment from "moment";
|
||||
import momenttz from "moment-timezone";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { INSERT_NEW_TIME_TICKET } from "../../graphql/timetickets.queries";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import dayjs from "../../utils/day";
|
||||
import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {INSERT_NEW_TIME_TICKET} from "../../graphql/timetickets.queries";
|
||||
import {setModalContext} from "../../redux/modals/modals.actions";
|
||||
import {selectTechnician} from "../../redux/tech/tech.selectors";
|
||||
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||
import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component";
|
||||
import TechClockInComponent from "./tech-job-clock-in-form.component";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
technician: selectTechnician,
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
technician: selectTechnician,
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setTimeTicketContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
||||
setTimeTicketContext: (context) =>
|
||||
dispatch(setModalContext({context: context, modal: "timeTicket"})),
|
||||
});
|
||||
|
||||
export function TechClockInContainer({
|
||||
setTimeTicketContext,
|
||||
technician,
|
||||
bodyshop,
|
||||
currentUser,
|
||||
}) {
|
||||
const { Enhanced_Payroll } = useTreatments(
|
||||
["Enhanced_Payroll"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [insertTimeTicket] = useMutation(INSERT_NEW_TIME_TICKET, {
|
||||
refetchQueries: ["QUERY_ACTIVE_TIME_TICKETS"],
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
|
||||
const emps = bodyshop.employees.filter(
|
||||
(e) => e.id === (technician && technician.id)
|
||||
)[0];
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
const theTime = (await axios.post("/utils/time")).data;
|
||||
const result = await insertTimeTicket({
|
||||
variables: {
|
||||
timeTicketInput: [
|
||||
{
|
||||
bodyshopid: bodyshop.id,
|
||||
employeeid: technician.id,
|
||||
date:
|
||||
typeof bodyshop.timezone === "string"
|
||||
? momenttz.tz(theTime, bodyshop.timezone).format("YYYY-MM-DD")
|
||||
: typeof bodyshop.timezone === "number"
|
||||
? moment(theTime)
|
||||
.format("YYYY-MM-DD")
|
||||
.utcOffset(bodyshop.timezone)
|
||||
: moment(theTime).format("YYYY-MM-DD"),
|
||||
clockon: moment(theTime),
|
||||
jobid: values.jobid,
|
||||
cost_center: values.cost_center,
|
||||
ciecacode:
|
||||
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === 'on'
|
||||
? values.cost_center
|
||||
: Object.keys(
|
||||
bodyshop.md_responsibility_centers.defaults.costs
|
||||
).find((key) => {
|
||||
return (
|
||||
bodyshop.md_responsibility_centers.defaults.costs[key] ===
|
||||
values.cost_center
|
||||
);
|
||||
}),
|
||||
created_by: currentUser.email.concat(
|
||||
" | ",
|
||||
technician.employee_number
|
||||
.concat(" ", technician.first_name, " ", technician.last_name)
|
||||
.trim()
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
setTimeTicketContext,
|
||||
technician,
|
||||
bodyshop,
|
||||
currentUser,
|
||||
}) {
|
||||
const {treatments: {Enhanced_Payroll}} = useSplitTreatments({
|
||||
attributes: {},
|
||||
names: ["Enhanced_Payroll"],
|
||||
splitKey: bodyshop.imexshopid,
|
||||
});
|
||||
|
||||
if (!!result.errors) {
|
||||
notification["error"]({
|
||||
message: t("timetickets.errors.clockingin", {
|
||||
message: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
notification["success"]({
|
||||
message: t("timetickets.successes.clockedin"),
|
||||
});
|
||||
}
|
||||
form.resetFields();
|
||||
setLoading(false);
|
||||
};
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [insertTimeTicket] = useMutation(INSERT_NEW_TIME_TICKET, {
|
||||
refetchQueries: ["QUERY_ACTIVE_TIME_TICKETS"],
|
||||
});
|
||||
const {t} = useTranslation();
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t("timetickets.labels.clockintojob")}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setTimeTicketContext({
|
||||
actions: {},
|
||||
context: {
|
||||
timeticket: {
|
||||
employeeid: technician.id,
|
||||
flat_rate: emps.flat_rate,
|
||||
},
|
||||
created_by: currentUser.email.concat(
|
||||
" | ",
|
||||
technician.employee_number
|
||||
.concat(
|
||||
" ",
|
||||
technician.first_name,
|
||||
" ",
|
||||
technician.last_name
|
||||
)
|
||||
.trim()
|
||||
),
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("timetickets.actions.enter")}
|
||||
</Button>
|
||||
<TechJobPrintTickets attendacePrint={false} />
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => form.submit()}
|
||||
loading={loading}
|
||||
>
|
||||
{t("timetickets.actions.clockin")}
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Form form={form} layout="vertical" onFinish={handleFinish}>
|
||||
<TechClockInComponent form={form} />
|
||||
</Form>
|
||||
</Card>
|
||||
);
|
||||
const emps = bodyshop.employees.filter(
|
||||
(e) => e.id === (technician && technician.id)
|
||||
)[0];
|
||||
|
||||
const TechForm = () => {
|
||||
if (technician) {
|
||||
return <Form form={form} layout="vertical" onFinish={handleFinish}>
|
||||
<TechClockInComponent form={form}/>
|
||||
</Form>
|
||||
} else {
|
||||
return <div/>
|
||||
}
|
||||
}
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
const theTime = (await axios.post("/utils/time")).data;
|
||||
const result = await insertTimeTicket({
|
||||
variables: {
|
||||
timeTicketInput: [
|
||||
{
|
||||
bodyshopid: bodyshop.id,
|
||||
employeeid: technician.id,
|
||||
date:
|
||||
typeof bodyshop.timezone === "string"
|
||||
// TODO: Client Update - This may be broken
|
||||
? dayjs.tz(theTime, bodyshop.timezone).format("YYYY-MM-DD")
|
||||
: typeof bodyshop.timezone === "number"
|
||||
? dayjs(theTime)
|
||||
.format("YYYY-MM-DD")
|
||||
.utcOffset(bodyshop.timezone)
|
||||
: dayjs(theTime).format("YYYY-MM-DD"),
|
||||
clockon: dayjs(theTime),
|
||||
jobid: values.jobid,
|
||||
cost_center: values.cost_center,
|
||||
ciecacode:
|
||||
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === 'on'
|
||||
? values.cost_center
|
||||
: Object.keys(
|
||||
bodyshop.md_responsibility_centers.defaults.costs
|
||||
).find((key) => {
|
||||
return (
|
||||
bodyshop.md_responsibility_centers.defaults.costs[key] ===
|
||||
values.cost_center
|
||||
);
|
||||
}),
|
||||
created_by: currentUser.email.concat(
|
||||
" | ",
|
||||
technician.employee_number
|
||||
.concat(" ", technician.first_name, " ", technician.last_name)
|
||||
.trim()
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
if (!!result.errors) {
|
||||
notification["error"]({
|
||||
message: t("timetickets.errors.clockingin", {
|
||||
message: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
notification["success"]({
|
||||
message: t("timetickets.successes.clockedin"),
|
||||
});
|
||||
}
|
||||
form.resetFields();
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t("timetickets.labels.clockintojob")}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setTimeTicketContext({
|
||||
actions: {},
|
||||
context: {
|
||||
timeticket: {
|
||||
employeeid: technician.id,
|
||||
flat_rate: emps.flat_rate,
|
||||
},
|
||||
created_by: currentUser.email.concat(
|
||||
" | ",
|
||||
technician.employee_number
|
||||
.concat(
|
||||
" ",
|
||||
technician.first_name,
|
||||
" ",
|
||||
technician.last_name
|
||||
)
|
||||
.trim()
|
||||
),
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("timetickets.actions.enter")}
|
||||
</Button>
|
||||
<TechJobPrintTickets attendacePrint={false}/>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => form.submit()}
|
||||
loading={loading}
|
||||
>
|
||||
{t("timetickets.actions.clockin")}
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<TechForm/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(TechClockInContainer);
|
||||
|
||||
Reference in New Issue
Block a user