- clear stage

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-02-16 12:50:51 -05:00
13 changed files with 977 additions and 22 deletions

View File

@@ -42,6 +42,7 @@ export default function ScoreboardAddButton({
const handleFinish = async (values) => {
logImEXEvent("job_close_add_to_scoreboard");
values.date = dayjs(values.date).format("YYYY-MM-DD");
setLoading(true);
let result;
@@ -169,7 +170,7 @@ export default function ScoreboardAddButton({
return acc + job.lbr_adjustments[val];
}, 0);
form.setFieldsValue({
date: new dayjs(),
date: dayjs(),
bodyhrs: Math.round(v.bodyhrs * 10) / 10,
painthrs: Math.round(v.painthrs * 10) / 10,
});

View File

@@ -1,6 +1,6 @@
import {gql, useApolloClient, useLazyQuery, useMutation, useQuery,} from "@apollo/client";
import {useSplitTreatments} from "@splitsoftware/splitio-react";
import {Col, notification, Row} from "antd";
import {Col, Row, notification} from "antd";
import Axios from "axios";
import Dinero from "dinero.js";
import dayjs from "../../utils/day";
@@ -21,8 +21,8 @@ import {INSERT_NEW_NOTE} from "../../graphql/notes.queries";
import {SEARCH_VEHICLE_BY_VIN} from "../../graphql/vehicles.queries";
import {insertAuditTrail} from "../../redux/application/application.actions";
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
import confirmDialog from "../../utils/asyncConfirm";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import confirmDialog from "../../utils/asyncConfirm";
import CriticalPartsScan from "../../utils/criticalPartsScan";
import AlertComponent from "../alert/alert.component";
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
@@ -63,7 +63,15 @@ export function JobsAvailableContainer({bodyshop, currentUser, insertAuditTrail,
const [selectedJob, setSelectedJob] = useState(null);
const [selectedOwner, setSelectedOwner] = useState(null);
const [partsQueueToggle, setPartsQueueToggle] = useState(bodyshop.md_functionality_toggles.parts_queue_toggle);
const [partsQueueToggle, setPartsQueueToggle] = useState(
bodyshop.md_functionality_toggles.parts_queue_toggle
);
const [updateSchComp, setSchComp] = useState({
actual_in: dayjs(),
checked: false,
scheduled_completion: dayjs(),
automatic: false,
});
const [insertLoading, setInsertLoading] = useState(false);
@@ -187,8 +195,10 @@ export function JobsAvailableContainer({bodyshop, currentUser, insertAuditTrail,
notification["error"]({
message: t("jobs.errors.creating", {error: err.message}),
});
refetch().catch(e => {
console.error(`Something went wrong in jobs available table container - ${err.message || ''}`)
refetch().catch((e) => {
console.error(`Something went wrong in jobs available table container - ${err.message || ""
}`
);
});
setInsertLoading(false);
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
@@ -217,6 +227,22 @@ export function JobsAvailableContainer({bodyshop, currentUser, insertAuditTrail,
//IO-539 Check for Parts Rate on PAL for SGI use case.
await CheckTaxRates(supp, bodyshop);
if (updateSchComp.checked === true) {
if (updateSchComp.automatic === true) {
const job_hrs = supp.joblines.data.reduce(
(acc, val) => acc + val.mod_lb_hrs,
0
);
const num_days = job_hrs / bodyshop.target_touchtime;
supp.actual_in = updateSchComp.actual_in;
supp.scheduled_completion = dayjs(updateSchComp.actual_in).add(
num_days,
"days"
);
} else {
supp.scheduled_completion = updateSchComp.scheduled_completion;
}
}
delete supp.owner;
delete supp.vehicle;
delete supp.ins_co_nm;
@@ -389,7 +415,8 @@ export function JobsAvailableContainer({bodyshop, currentUser, insertAuditTrail,
onCancel={onJobModalCancel}
modalSearchState={modalSearchState}
partsQueueToggle={partsQueueToggle}
setPartsQueueToggle={setPartsQueueToggle}
setPartsQueueToggle={setPartsQueueToggle} updateSchComp={updateSchComp}
setSchComp={setSchComp}
/>
<Row gutter={[16, 16]}>
<Col span={24}>

View File

@@ -132,7 +132,7 @@ export function JobsDetailHeader({job, bodyshop, disabled}) {
<ProductionListColumnProductionNote record={job}/>
</DataLabel>
<Space>
<Space wrap>
{job.special_coverage_policy && (
<Tag color="tomato">
<Space>

View File

@@ -1,9 +1,11 @@
import {SyncOutlined} from "@ant-design/icons";
import {Button, Checkbox, Divider, Input, Table} from "antd";
import React from "react";
import {Button, Checkbox, Divider, Input, Space, Table} from "antd";
import dayjs from "../../utils/day";
import React, {useState} from "react";
import {useTranslation} from "react-i18next";
import {Link} from "react-router-dom";
import PhoneFormatter from "../../utils/PhoneFormatter";
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
export default function JobsFindModalComponent({
@@ -16,11 +18,13 @@ export default function JobsFindModalComponent({
jobsListRefetch,
partsQueueToggle,
setPartsQueueToggle,
updateSchComp,
setSchComp,
}) {
const {t} = useTranslation();
const [modalSearch, setModalSearch] = modalSearchState;
const [importOptions, setImportOptions] = importOptionsState;
const [checkUTT, setCheckUTT] = useState(false);
const columns = [
{
title: t("jobs.fields.ro_number"),
@@ -142,6 +146,35 @@ export default function JobsFindModalComponent({
if (record) {
if (record.id) {
setSelectedJob(record.id);
if (record.actual_in && record.scheduled_completion) {
setSchComp({
...updateSchComp,
actual_in: record.actual_in,
scheduled_completion: record.scheduled_completion,
});
} else {
if (record.actual_in && !record.scheduled_completion) {
setSchComp({
...updateSchComp,
actual_in: record.actual_in,
scheduled_completion: dayjs(),
});
}
if (!record.actual_in && record.scheduled_completion) {
setSchComp({
...updateSchComp,
actual_in: dayjs(),
scheduled_completion: dayjs(record.scheduled_completion),
});
}
if (!record.actual_in && !record.scheduled_completion) {
setSchComp({
...updateSchComp,
actual_in: dayjs(),
scheduled_completion: dayjs(),
});
}
}
return;
}
}
@@ -177,6 +210,35 @@ export default function JobsFindModalComponent({
rowSelection={{
onSelect: (props) => {
setSelectedJob(props.id);
if (props.actual_in && props.scheduled_completion) {
setSchComp({
...updateSchComp,
actual_in: props.actual_in,
scheduled_completion: props.scheduled_completion,
});
} else {
if (props.actual_in && !props.scheduled_completion) {
setSchComp({
...updateSchComp,
actual_in: props.actual_in,
scheduled_completion: dayjs(),
});
}
if (!props.actual_in && props.scheduled_completion) {
setSchComp({
...updateSchComp,
actual_in: dayjs(),
scheduled_completion: dayjs(props.scheduled_completion),
});
}
if (!props.actual_in && !props.scheduled_completion) {
setSchComp({
...updateSchComp,
actual_in: dayjs(),
scheduled_completion: dayjs(),
});
}
}
},
type: "radio",
selectedRowKeys: [selectedJob],
@@ -189,7 +251,7 @@ export default function JobsFindModalComponent({
};
}}
/>
<Divider/>
<Divider/><Space>
<Checkbox
defaultChecked={importOptions.overrideHeader}
onChange={(e) =>
@@ -206,7 +268,40 @@ export default function JobsFindModalComponent({
onChange={(e) => setPartsQueueToggle(e.target.checked)}
>
{t("bodyshop.fields.md_functionality_toggles.parts_queue_toggle")}
</Checkbox>
</Checkbox><Checkbox
checked={updateSchComp.checked}
onChange={(e) =>
setSchComp({...updateSchComp, checked: e.target.checked})
}
>
{t("jobs.labels.update_scheduled_completion")}
</Checkbox>
{updateSchComp.checked === true ? (
<>
{checkUTT === false ? (
<FormDateTimePickerComponent
value={updateSchComp.scheduled_completion}
onChange={(e) => {
setSchComp({...updateSchComp, scheduled_completion: e});
}}
/>
) : null}
<Checkbox
checked={checkUTT}
onChange={(e) => {
setCheckUTT(e.target.checked);
setSchComp({
...updateSchComp,
scheduled_completion: null,
automatic: true,
});
}}
>
{t("jobs.labels.calc_scheuled_completion")}
</Checkbox>
</>
) : null}
</Space>
</div>
);
}

View File

@@ -27,7 +27,8 @@ export default connect(
modalSearchState,
partsQueueToggle,
setPartsQueueToggle,
...modalProps
updateSchComp,
setSchComp, ...modalProps
}) {
const {t} = useTranslation();
@@ -95,7 +96,8 @@ export default connect(
modalSearchState={modalSearchState}
partsQueueToggle={partsQueueToggle}
setPartsQueueToggle={setPartsQueueToggle}
/>
updateSchComp={updateSchComp}
setSchComp={setSchComp}/>
</Modal>
);
});

View File

@@ -0,0 +1,277 @@
import {Button, Card, Checkbox, Col, Form, Input, InputNumber, Row, Select} from "antd";
import React, {useEffect, useState} from "react";
import {fetchFilterData} from "../../utils/RenderTemplate";
import {DeleteFilled} from "@ant-design/icons";
import {useTranslation} from "react-i18next";
import {getOperatorsByType} from "../../utils/graphQLmodifier";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
export default function ReportCenterModalFiltersSortersComponent({form}) {
return (
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
return <RenderFilters form={form} templateId={key}/>;
}}
</Form.Item>
);
}
function RenderFilters({templateId, form}) {
const [state, setState] = useState(null);
const [visible, setVisible] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const {t} = useTranslation();
useEffect(() => {
const fetch = async () => {
setIsLoading(true);
const data = await fetchFilterData({name: templateId});
if (data?.success) {
setState(data.data);
} else {
setState(null);
}
setIsLoading(false);
};
if (templateId) {
fetch();
}
}, [templateId]);
// Conditional display of filters and sorters
if (!templateId) return null;
if (isLoading) return <LoadingSkeleton/>;
if (!state) return null;
// Filters and Sorters data available
return (
<div style={{marginTop: '10px'}}>
<Checkbox
checked={visible}
onChange={(e) => setVisible(e.target.checked)}
children={t('reportcenter.labels.advanced_filters')}
/>
{visible && (
<div>
{state.filters && state.filters.length > 0 && (
<Card type='inner' title={t('reportcenter.labels.advanced_filters_filters')}
style={{marginTop: '10px'}}>
<Form.List name={["filters"]}>
{(fields, {add, remove, move}) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<Row gutter={[16, 16]}>
<Col span={10}>
<Form.Item
key={`${index}field`}
label="field"
name={[field.name, "field"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
options={
state.filters
? state.filters.map((f) => {
return {
value: f.name,
label: f?.translation ? (t(f.translation) === f.translation ? f.label : t(f.translation)) : f.label,
}
})
: []
}
/>
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
dependencies={[['filters', field.name, "field"]]}>
{
() => {
const name = form.getFieldValue(['filters', field.name, "field"]);
const type = state.filters.find(f => f.name === name)?.type;
return <Form.Item
key={`${index}operator`}
label="operator"
name={[field.name, "operator"]}
dependencies={[]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select options={getOperatorsByType(type)}/>
</Form.Item>
}
}
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
dependencies={[['filters', field.name, "field"]]}>
{
() => {
const name = form.getFieldValue(['filters', field.name, "field"]);
const type = state.filters.find(f => f.name === name)?.type;
return <Form.Item
key={`${index}value`}
label="value"
name={[field.name, "value"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
{type === 'number' ?
<InputNumber
onChange={(value) => {
form.setFieldsValue({[field.name]: {value: parseInt(value)}});
}}
/>
:
<Input
onChange={(value) => {
form.setFieldsValue({[field.name]: {value: value.toString()}});
}}
/>
}
</Form.Item>
}
}
</Form.Item>
</Col>
<Col span={2}>
<DeleteFilled
style={{margin: "1rem"}}
onClick={() => {
remove(field.name);
}}
/>
</Col>
</Row>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{width: "100%"}}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</Card>
)}
{state.sorters && state.sorters.length > 0 && (
<Card type='inner' title={t('reportcenter.labels.advanced_filters_sorters')}
style={{marginTop: '10px'}}>
<Form.List name={["sorters"]}>
{(fields, {add, remove, move}) => {
return (
<div>
Sorters
{fields.map((field, index) => (
<Form.Item key={field.key}>
<Row gutter={[16, 16]}>
<Col span={11}>
<Form.Item
key={`${index}field`}
label="field"
name={[field.name, "field"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
options={
state.sorters
? state.sorters.map((f) => ({
value: f.name,
label: f?.translation ? (t(f.translation) === f.translation ? f.label : t(f.translation)) : f.label,
}))
: []
}
/>
</Form.Item>
</Col>
<Col span={11}>
<Form.Item
key={`${index}direction`}
label="direction"
name={[field.name, "direction"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
options={[
{value: "desc", label: "Descending"},
{value: "asc", label: "Ascending"},
]}
/>
</Form.Item>
</Col>
<Col span={2}>
<DeleteFilled
style={{margin: "1rem"}}
onClick={() => {
remove(field.name);
}}
/>
</Col>
</Row>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{width: "100%"}}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</Card>
)}
</div>
)}
</div>
);
}

View File

@@ -9,12 +9,13 @@ import {createStructuredSelector} from "reselect";
import {QUERY_ACTIVE_EMPLOYEES} from "../../graphql/employees.queries";
import {QUERY_ALL_VENDORS} from "../../graphql/vendors.queries";
import {selectReportCenter} from "../../redux/modals/modals.selectors";
import DatePIckerRanges from "../../utils/DatePickerRanges";
import DatePickerRanges from "../../utils/DatePickerRanges";
import {GenerateDocument} from "../../utils/RenderTemplate";
import {TemplateList} from "../../utils/TemplateConstants";
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
import "./report-center-modal.styles.scss";
import ReportCenterModalFiltersSortersComponent from "./report-center-modal-filters-sorters-component";
const mapStateToProps = createStructuredSelector({
reportCenterModal: selectReportCenter,
@@ -78,6 +79,8 @@ export function ReportCenterModalComponent({reportCenterModal}) {
...(id ? {id: id} : {}),
},
filters: values.filters,
sorters: values.sorters,
},
{
to: values.to,
@@ -148,7 +151,7 @@ export function ReportCenterModalComponent({reportCenterModal}) {
<Typography.Title level={4}>
{t(`reportcenter.labels.groups.${key}`)}
</Typography.Title>
<ul style={{columns: "2 auto"}}>
<ul style={{listStyleType: 'none', columns: "2 auto"}}>
{grouped[key].map((item) => (
<li key={item.key}>
<Radio key={item.key} value={item.key}>
@@ -180,6 +183,7 @@ export function ReportCenterModalComponent({reportCenterModal}) {
);
}}
</Form.Item>
<ReportCenterModalFiltersSortersComponent form={form}/>
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
@@ -248,7 +252,7 @@ export function ReportCenterModalComponent({reportCenterModal}) {
>
<DatePicker.RangePicker
format="MM/DD/YYYY"
presets={DatePIckerRanges}
presets={DatePickerRanges}
/>
</Form.Item>
);
@@ -304,3 +308,4 @@ export function ReportCenterModalComponent({reportCenterModal}) {
</div>
);
}