- Merge client update into test-beta

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-01-18 19:20:08 -05:00
696 changed files with 92291 additions and 107075 deletions

View File

@@ -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);

View File

@@ -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);