Added parts receive modal & line status component IO-567

This commit is contained in:
Patrick Fic
2021-02-10 09:04:18 -08:00
parent c91d677fc0
commit c14327f303
12 changed files with 393 additions and 4 deletions

View File

@@ -22856,6 +22856,27 @@
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>allpartsto</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>email</name>
<definition_loaded>false</definition_loaded>
@@ -22982,6 +23003,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>receive</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>returnpartsorder</name>
<definition_loaded>false</definition_loaded>

View File

@@ -44,10 +44,6 @@ function BillEnterModalContainer({
const client = useApolloClient();
const handleFinish = async (values) => {
console.log(
"🚀 ~ file: bill-enter-modal.container.jsx ~ line 41 ~ handleFinish ~ values",
values
);
setLoading(true);
const { upload, location, ...remainingValues } = values;

View File

@@ -14,11 +14,13 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters";
import JobLineLocationPopup from "../job-line-location-popup/job-line-location-popup.component";
import JobLineNotePopup from "../job-line-note-popup/job-line-note-popup.component";
import JobLineStatusPopup from "../job-line-status-popup/job-line-status-popup.component";
import JobLinesBillRefernece from "../job-lines-bill-reference/job-lines-bill-reference.component";
// import AllocationsAssignmentContainer from "../allocations-assignment/allocations-assignment.container";
// import AllocationsBulkAssignmentContainer from "../allocations-bulk-assignment/allocations-bulk-assignment.container";
// import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container";
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
jobRO: selectJobReadOnly,
@@ -257,6 +259,9 @@ export function JobLinesComponent({
})) ||
[],
onFilter: (value, record) => value.includes(record.status),
render: (text, record) => (
<JobLineStatusPopup jobline={record} disabled={jobRO} />
),
},
// {
// title: t("allocations.fields.employee"),

View File

@@ -0,0 +1,82 @@
import { notification, Select } from "antd";
import React, { useEffect, useState } from "react";
import { useMutation } from "react-apollo";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function JobLineStatusPopup({ bodyshop, jobline, disabled }) {
const [editing, setEditing] = useState(false);
const [loading, setLoading] = useState(false);
const [status, setStatus] = useState(jobline.status);
const [updateJob] = useMutation(UPDATE_JOB_LINE);
const { t } = useTranslation();
useEffect(() => {
if (editing) setStatus(jobline.status);
}, [editing, jobline.status]);
const handleChange = (e) => {
setStatus(e);
};
const handleSave = async (e) => {
setLoading(true);
const result = await updateJob({
variables: { lineId: jobline.id, line: { status: status || "" } },
});
if (!!!result.errors) {
notification["success"]({ message: t("joblines.successes.saved") });
} else {
notification["error"]({
message: t("joblines.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
}
setLoading(false);
setEditing(false);
};
if (editing)
return (
<div>
<LoadingSpinner loading={loading}>
<Select
autoFocus
dropdownMatchSelectWidth={100}
value={status}
onSelect={handleChange}
onBlur={handleSave}
>
{bodyshop.md_order_statuses.statuses.map((s, idx) => (
<Select.Option key={idx} value={s}>
{s}
</Select.Option>
))}
</Select>
</LoadingSpinner>
</div>
);
return (
<div
style={{ width: "100%", minHeight: "2rem", cursor: "pointer" }}
onClick={() => !disabled && setEditing(true)}
>
{jobline.status}
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(JobLineStatusPopup);

View File

@@ -15,6 +15,7 @@ import { DateFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
import { TemplateList } from "../../utils/TemplateConstants";
import PartsOrderLineBackorderButton from "../parts-order-line-backorder-button/parts-order-line-backorder-button.component";
import PartsReceiveModalContainer from "../parts-receive-modal/parts-receive-modal.container";
import PrintWrapper from "../print-wrapper/print-wrapper.component";
const mapStateToProps = createStructuredSelector({
@@ -25,6 +26,8 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({
setBillEnterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "billEnter" })),
setPartsReceiveContext: (context) =>
dispatch(setModalContext({ context: context, modal: "partsReceive" })),
});
export function PartsOrderListTableComponent({
@@ -34,6 +37,7 @@ export function PartsOrderListTableComponent({
job,
billsQuery,
handleOnRowClick,
setPartsReceiveContext,
}) {
const responsibilityCenters = bodyshop.md_responsibility_centers;
@@ -102,6 +106,29 @@ export function PartsOrderListTableComponent({
key: "actions",
render: (text, record) => (
<Space>
<Button
disabled={jobRO || record.return}
onClick={() => {
logImEXEvent("parts_order_receive_bill");
setPartsReceiveContext({
actions: { refetch: refetch },
context: {
jobId: job.id,
job: job,
partsorderlines: record.parts_order_lines.map((pol) => {
return {
joblineid: pol.job_line_id,
line_desc: pol.line_desc,
quantity: pol.quantity,
};
}),
},
});
}}
>
{t("parts_orders.actions.receive")}
</Button>
<Button
disabled={jobRO}
onClick={() => {
@@ -250,6 +277,7 @@ export function PartsOrderListTableComponent({
<Typography.Title level={4}>
{t("parts_orders.labels.parts_orders")}
</Typography.Title>
<PartsReceiveModalContainer />
<Table
loading={billsQuery.loading}
size="small"

View File

@@ -0,0 +1,109 @@
import { DeleteFilled } from "@ant-design/icons";
import { Form, Input, Select, Typography } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
export default connect(mapStateToProps, null)(PartsReceiveModalComponent);
export function PartsReceiveModalComponent({ bodyshop, form }) {
const { t } = useTranslation();
return (
<div>
<LayoutFormRow>
<Form.Item name="location" label={t("parts_orders.labels.allpartsto")}>
<Select
style={{ width: "10rem" }}
onSelect={(value) => {
form.setFieldsValue({
partsorderlines: form
.getFieldValue("partsorderlines")
.map((l) => {
return { ...l, location: value };
}),
});
}}
>
{bodyshop.md_parts_locations.map((loc, idx) => (
<Select.Option key={idx} value={loc}>
{loc}
</Select.Option>
))}
</Select>
</Form.Item>
</LayoutFormRow>
<Typography.Title level={4}>
{t("parts_orders.labels.inthisorder")}
</Typography.Title>
<Form.List name={["partsorderlines"]}>
{(fields, { add, remove, move }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item required={false} key={field.key}>
<div style={{ display: "flex", alignItems: "center" }}>
<Form.Item
style={{ display: "none" }}
key={`${index}joblineid`}
name={[field.name, "joblineid"]}
>
<Input />
</Form.Item>
<LayoutFormRow grow style={{ flex: 1 }}>
<Form.Item
label={t("parts_orders.fields.line_desc")}
key={`${index}line_desc`}
name={[field.name, "line_desc"]}
rules={[
{
required: true,
message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("joblines.fields.location")}
key={`${index}location`}
name={[field.name, "location"]}
>
<Select style={{ width: "10rem" }}>
{bodyshop.md_parts_locations.map((loc, idx) => (
<Select.Option key={idx} value={loc}>
{loc}
</Select.Option>
))}
</Select>
</Form.Item>
</LayoutFormRow>
<DeleteFilled
style={{ margin: "1rem" }}
onClick={() => {
remove(field.name);
}}
/>
<FormListMoveArrows
move={move}
index={index}
total={fields.length}
/>
</div>
</Form.Item>
))}
</div>
);
}}
</Form.List>
</div>
);
}

View File

@@ -0,0 +1,115 @@
import { useMutation } from "@apollo/react-hooks";
import { Form, Modal, notification } from "antd";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectPartsReceive } from "../../redux/modals/modals.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import PartsReceiveModalComponent from "./parts-receive-modal.component";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
bodyshop: selectBodyshop,
partsOrderModal: selectPartsReceive,
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("partsReceive")),
});
export function PartsReceiveModalContainer({
partsOrderModal,
toggleModalVisible,
currentUser,
bodyshop,
}) {
const { t } = useTranslation();
const { visible, context, actions } = partsOrderModal;
const { jobId, partsorderlines } = context;
const { refetch } = actions;
const [form] = Form.useForm();
const [updateJobLines] = useMutation(UPDATE_JOB_LINE);
const handleFinish = async (values) => {
logImEXEvent("parts_order_receive");
const result = await Promise.all(
values.partsorderlines.map((li) => {
return updateJobLines({
variables: {
lineId: li.joblineid,
line: {
location: li.location,
status:
bodyshop.md_order_statuses.default_received || "Received*",
},
},
});
})
);
result.forEach((jobLinesResult) => {
if (jobLinesResult.errors) {
notification["error"]({
message: t("parts_orders.errors.creating"),
description: JSON.stringify(jobLinesResult.errors),
});
}
});
notification["success"]({
message: values.isReturn
? t("parts_orders.successes.return_created")
: t("parts_orders.successes.created"),
});
if (refetch) refetch();
toggleModalVisible();
};
const initialValues = {
partsorderlines: partsorderlines,
};
useEffect(() => {
if (visible && !!partsorderlines) {
form.resetFields();
}
}, [visible, partsorderlines, form]);
return (
<Modal
visible={visible}
title={t("parts_orders.labels.receive")}
onCancel={() => toggleModalVisible()}
onOk={() => form.submit()}
destroyOnClose
forceRender
>
<Form
form={form}
layout="vertical"
autoComplete="no"
onFinish={handleFinish}
initialValues={initialValues}
>
<PartsReceiveModalComponent form={form} />
</Form>
</Modal>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(PartsReceiveModalContainer);

View File

@@ -21,6 +21,7 @@ const INITIAL_STATE = {
payment: { ...baseModal },
jobCosting: { ...baseModal },
reportCenter: { ...baseModal },
partsReceive: { ...baseModal },
};
const modalsReducer = (state = INITIAL_STATE, action) => {

View File

@@ -60,3 +60,8 @@ export const selectReportCenter = createSelector(
[selectModals],
(modals) => modals.reportCenter
);
export const selectPartsReceive = createSelector(
[selectModals],
(modals) => modals.partsReceive
);

View File

@@ -1387,12 +1387,14 @@
"status": "Status"
},
"labels": {
"allpartsto": "All Parts Location",
"email": "Send by Email",
"inthisorder": "Parts in this Order",
"newpartsorder": "New Parts Order",
"orderhistory": "Order History",
"parts_orders": "Parts Orders",
"print": "Show Printed Form",
"receive": "Receive Parts Order",
"returnpartsorder": "Return Parts Order"
},
"successes": {

View File

@@ -1387,12 +1387,14 @@
"status": ""
},
"labels": {
"allpartsto": "",
"email": "Enviar por correo electrónico",
"inthisorder": "Partes en este pedido",
"newpartsorder": "",
"orderhistory": "Historial de pedidos",
"parts_orders": "",
"print": "Mostrar formulario impreso",
"receive": "",
"returnpartsorder": ""
},
"successes": {

View File

@@ -1387,12 +1387,14 @@
"status": ""
},
"labels": {
"allpartsto": "",
"email": "Envoyé par email",
"inthisorder": "Pièces dans cette commande",
"newpartsorder": "",
"orderhistory": "Historique des commandes",
"parts_orders": "",
"print": "Afficher le formulaire imprimé",
"receive": "",
"returnpartsorder": ""
},
"successes": {