From 13faf47044bdb2358fec95c5244b1292360f2149 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 24 Feb 2020 13:49:39 -0800 Subject: [PATCH] Implemented jobline upsert modal. Updated allocations display on jobline edit to include removal. --- bodyshop_translations.babel | 256 ++++++++++++++++++ .../allocations-assignment.container.jsx | 18 +- .../allocations-employee-label.component.jsx | 19 ++ .../allocations-employee-label.container.jsx | 29 ++ .../reset-form-item.component.jsx | 2 +- .../job-detail-lines/job-lines.component.jsx | 29 +- .../job-lines-upsert-modal.component.jsx | 46 +++- .../job-lines-upsert-modal.container.jsx | 56 ++-- client/src/graphql/allocations.queries.js | 10 + client/src/redux/modals/modals.reducer.js | 2 +- client/src/redux/store.js | 4 +- client/src/translations/en_us/common.json | 21 ++ client/src/translations/es/common.json | 21 ++ client/src/translations/fr/common.json | 21 ++ package.json | 2 +- yarn.lock | 8 +- 16 files changed, 495 insertions(+), 49 deletions(-) create mode 100644 client/src/components/allocations-employee-label/allocations-employee-label.component.jsx create mode 100644 client/src/components/allocations-employee-label/allocations-employee-label.container.jsx diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index f7d5f1109..f11240b8f 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -47,6 +47,53 @@ + + errors + + + deleting + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + saving + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + fields @@ -73,6 +120,53 @@ + + successes + + + deleted + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + save + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + @@ -1635,6 +1729,79 @@ joblines + + actions + + + new + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + + + errors + + + creating + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + updating + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + fields @@ -1743,6 +1910,27 @@ + + mod_lbr_ty + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + oem_partno false @@ -1764,6 +1952,27 @@ + + op_code_desc + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + part_type false @@ -1876,6 +2085,53 @@ + + successes + + + created + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + updated + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + diff --git a/client/src/components/allocations-assignment/allocations-assignment.container.jsx b/client/src/components/allocations-assignment/allocations-assignment.container.jsx index 914312109..461b06627 100644 --- a/client/src/components/allocations-assignment/allocations-assignment.container.jsx +++ b/client/src/components/allocations-assignment/allocations-assignment.container.jsx @@ -20,13 +20,19 @@ export default function AllocationsAssignmentContainer({ const [insertAllocation] = useMutation(INSERT_ALLOCATION); const handleAssignment = () => { - insertAllocation({ variables: { alloc: { ...assignment } } }).then(r => { - notification["success"]({ - message: t("employees.successes.save") + insertAllocation({ variables: { alloc: { ...assignment } } }) + .then(r => { + notification["success"]({ + message: t("allocations.successes.save") + }); + visibilityState[1](false); + if (refetch) refetch(); + }) + .catch(error => { + notification["error"]({ + message: t("employees.errors.saving", { message: error.message }) + }); }); - visibilityState[1](false); - if (refetch) refetch(); - }); }; return ( diff --git a/client/src/components/allocations-employee-label/allocations-employee-label.component.jsx b/client/src/components/allocations-employee-label/allocations-employee-label.component.jsx new file mode 100644 index 000000000..e67be1dcc --- /dev/null +++ b/client/src/components/allocations-employee-label/allocations-employee-label.component.jsx @@ -0,0 +1,19 @@ +import { Icon } from "antd"; +import React from "react"; +import { MdRemoveCircleOutline } from "react-icons/md"; + +export default function AllocationsLabelComponent({ allocation, handleClick }) { + return ( +
+ + {`${allocation.employee.first_name || ""} ${allocation.employee + .last_name || ""} (${allocation.hours || ""})`} + + +
+ ); +} diff --git a/client/src/components/allocations-employee-label/allocations-employee-label.container.jsx b/client/src/components/allocations-employee-label/allocations-employee-label.container.jsx new file mode 100644 index 000000000..227e7eb0c --- /dev/null +++ b/client/src/components/allocations-employee-label/allocations-employee-label.container.jsx @@ -0,0 +1,29 @@ +import React from "react"; +import { useMutation } from "react-apollo"; +import { DELETE_ALLOCATION } from "../../graphql/allocations.queries"; +import AllocationsLabelComponent from "./allocations-employee-label.component"; +import { notification } from "antd"; +import { useTranslation } from "react-i18next"; +export default function AllocationsLabelContainer({ allocation, refetch }) { + const [deleteAllocation] = useMutation(DELETE_ALLOCATION); + const { t } = useTranslation(); + const handleClick = e => { + e.preventDefault(); + deleteAllocation({ variables: { id: allocation.id } }) + .then(r => { + notification["success"]({ + message: t("allocations.successes.deleted") + }); + if (refetch) refetch(); + }) + .catch(error => { + notification["error"]({ message: t("allocations.errors.deleting") }); + }); + }; + return ( + + ); +} diff --git a/client/src/components/form-items-formatted/reset-form-item.component.jsx b/client/src/components/form-items-formatted/reset-form-item.component.jsx index b0ef84a84..54ef8e357 100644 --- a/client/src/components/form-items-formatted/reset-form-item.component.jsx +++ b/client/src/components/form-items-formatted/reset-form-item.component.jsx @@ -10,7 +10,7 @@ export default function ResetForm({ resetFields }) { message={
{t("general.messages.unsavedchanges")} -
diff --git a/client/src/components/job-detail-lines/job-lines.component.jsx b/client/src/components/job-detail-lines/job-lines.component.jsx index 4fef3f7d2..c60a18208 100644 --- a/client/src/components/job-detail-lines/job-lines.component.jsx +++ b/client/src/components/job-detail-lines/job-lines.component.jsx @@ -6,6 +6,7 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter"; import { alphaSort } from "../../utils/sorters"; 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"; export default function JobLinesComponent({ @@ -135,16 +136,26 @@ export default function JobLinesComponent({ dataIndex: "employee", key: "employee", width: "10%", - sorter: (a, b) => a.act_price - b.act_price, //TODO Fix employee sorting. + sorter: (a, b) => + alphaSort( + a.allocations[0] && + a.allocations[0].employee.first_name + + a.allocations[0].employee.last_name, + b.allocations[0] && + b.allocations[0].employee.first_name + + b.allocations[0].employee.last_name + ), sortOrder: state.sortedInfo.columnKey === "employee" && state.sortedInfo.order, render: (text, record) => ( {record.allocations && record.allocations.length > 0 ? record.allocations.map(item => ( -
{`${item.employee.first_name} ${item.employee.last_name} (${item.hours})`}
+ refetch={refetch} + allocation={item} + /> )) : null} {t("general.actions.edit")} @@ -224,6 +233,16 @@ export default function JobLinesComponent({ jobLines={selectedLines} refetch={refetch} /> + ); }} diff --git a/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.component.jsx b/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.component.jsx index 4697c66e1..aa87ea5f4 100644 --- a/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.component.jsx +++ b/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.component.jsx @@ -1,6 +1,7 @@ -import { Modal, Form } from "antd"; +import { Modal, Form, Input, InputNumber } from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; +import ResetForm from "../form-items-formatted/reset-form-item.component"; export default function JobLinesUpsertModalComponent({ visible, @@ -11,8 +12,8 @@ export default function JobLinesUpsertModalComponent({ form }) { const { t } = useTranslation(); - //const { getFieldDecorator, isFieldsTouched, resetFields } = form; - console.log("jobLine", jobLine); + const { getFieldDecorator, isFieldsTouched, resetFields } = form; + return ( + {isFieldsTouched() ? : null}
- {JSON.stringify(jobLine)} + + {getFieldDecorator("line_desc", { + initialValue: jobLine.line_desc + })()} + + + {getFieldDecorator("oem_partno", { + initialValue: jobLine.oem_partno + })()} + + + {getFieldDecorator("part_type", { + initialValue: jobLine.part_type + })()} + + + {getFieldDecorator("mod_lbr_ty", { + initialValue: jobLine.mod_lbr_ty + })()} + + + {getFieldDecorator("op_code_desc", { + initialValue: jobLine.op_code_desc + })()} + + + {getFieldDecorator("mod_lb_hrs", { + initialValue: jobLine.mod_lb_hrs + })()} + + + {getFieldDecorator("act_price", { + initialValue: jobLine.act_price + })()} +
); diff --git a/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.container.jsx b/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.container.jsx index 6a188c55b..ebcae6d30 100644 --- a/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.container.jsx +++ b/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.container.jsx @@ -39,34 +39,46 @@ function JobLinesUpsertModalContainer({ }); } if (!err) { - if (true) { + if (!jobLineEditModal.context.id) { insertJobLine({ variables: { - //lineInput: [{ ...lineState, jobid: jobId }] + lineInput: [{ jobid: jobLineEditModal.context.jobid, ...values }] } - }).then(r => { - if (jobLineEditModal.actions.refetch) - jobLineEditModal.actions.refetch(); - toggleModalVisible(); - notification["success"]({ - message: t("joblines.successes.create") + }) + .then(r => { + if (jobLineEditModal.actions.refetch) + jobLineEditModal.actions.refetch(); + toggleModalVisible(); + notification["success"]({ + message: t("joblines.successes.created") + }); + }) + .catch(error => { + notification["error"]({ + message: t("joblines.errors.creating", { + message: error.message + }) + }); }); - }); - } - - if (false) { - //Required, otherwise unable to spread in new note prop. - //delete lineState.__typename; + } else { updateJobLine({ variables: { - //lineId: lineState.id, - //line: lineState + lineId: jobLineEditModal.context.id, + line: values } - }).then(r => { - notification["success"]({ - message: t("joblines.successes.updated") + }) + .then(r => { + notification["success"]({ + message: t("joblines.successes.updated") + }); + }) + .catch(error => { + notification["success"]({ + message: t("joblines.errors.updating", { + message: error.message + }) + }); }); - }); if (jobLineEditModal.actions.refetch) jobLineEditModal.actions.refetch(); toggleModalVisible(); @@ -75,9 +87,6 @@ function JobLinesUpsertModalContainer({ }); }; - const handleOk = () => { - //lineState.id ? updateExistingLine() : insertNewLine(); - }; const handleCancel = () => { toggleModalVisible(); }; @@ -87,7 +96,6 @@ function JobLinesUpsertModalContainer({ visible={jobLineEditModal.visible} jobLine={jobLineEditModal.context} handleSubmit={handleSubmit} - handleOk={handleOk} handleCancel={handleCancel} form={form} /> diff --git a/client/src/graphql/allocations.queries.js b/client/src/graphql/allocations.queries.js index 728f5534c..5c4d8e169 100644 --- a/client/src/graphql/allocations.queries.js +++ b/client/src/graphql/allocations.queries.js @@ -9,3 +9,13 @@ export const INSERT_ALLOCATION = gql` } } `; + +export const DELETE_ALLOCATION = gql` + mutation DELETE_ALLOCATION($id: uuid!) { + delete_allocations(where: { id: { _eq: $id } }) { + returning { + id + } + } + } +`; diff --git a/client/src/redux/modals/modals.reducer.js b/client/src/redux/modals/modals.reducer.js index 5fc5588df..a9218ec44 100644 --- a/client/src/redux/modals/modals.reducer.js +++ b/client/src/redux/modals/modals.reducer.js @@ -3,7 +3,7 @@ import ModalsActionTypes from "./modals.types"; const INITIAL_STATE = { jobLineEdit: { visible: false, - context: null, + context: {}, actions: { refetch: null } diff --git a/client/src/redux/store.js b/client/src/redux/store.js index 736d68033..7a5034e60 100644 --- a/client/src/redux/store.js +++ b/client/src/redux/store.js @@ -1,6 +1,6 @@ import { createStore, applyMiddleware } from "redux"; import { persistStore } from "redux-persist"; -import logger from "redux-logger"; +import { createLogger } from "redux-logger"; import createSagaMiddleware from "redux-saga"; import rootReducer from "./root.reducer"; import rootSaga from "./root.saga"; @@ -8,7 +8,7 @@ import rootSaga from "./root.saga"; const sagaMiddleWare = createSagaMiddleware(); const middlewares = [sagaMiddleWare]; if (process.env.NODE_ENV === "development") { - middlewares.push(logger); + middlewares.push(createLogger({ collapsed: true, diff: true })); } export const store = createStore(rootReducer, applyMiddleware(...middlewares)); diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 28f268853..1366e6593 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -4,8 +4,16 @@ "actions": { "assign": "Assign" }, + "errors": { + "deleting": "Error encountered while deleting allocation. {{message}}", + "saving": "Error while allocating. {{message}}" + }, "fields": { "employee": "Allocated To" + }, + "successes": { + "deleted": "Allocation deleted successfully.", + "save": "Allocated successfully. " } }, "appointments": { @@ -136,13 +144,22 @@ } }, "joblines": { + "actions": { + "new": "New Line" + }, + "errors": { + "creating": "Error encountered while creating job line. {{message}}", + "updating": "Error encountered updating job line. {{message}}" + }, "fields": { "act_price": "Actual Price", "db_price": "Database Price", "line_desc": "Line Description", "line_ind": "S#", "mod_lb_hrs": "Labor Hours", + "mod_lbr_ty": "Labor Type", "oem_partno": "OEM Part #", + "op_code_desc": "Operation Code Description", "part_type": "Part Type", "status": "Status", "unq_seq": "Seq #" @@ -150,6 +167,10 @@ "labels": { "edit": "Edit Line", "new": "New Line" + }, + "successes": { + "created": "Job line created successfully.", + "updated": "Job line updated successfully." } }, "jobs": { diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 5d0ad5457..2df497701 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -4,8 +4,16 @@ "actions": { "assign": "Asignar" }, + "errors": { + "deleting": "", + "saving": "" + }, "fields": { "employee": "Asignado a" + }, + "successes": { + "deleted": "", + "save": "" } }, "appointments": { @@ -136,13 +144,22 @@ } }, "joblines": { + "actions": { + "new": "" + }, + "errors": { + "creating": "", + "updating": "" + }, "fields": { "act_price": "Precio actual", "db_price": "Precio de base de datos", "line_desc": "Descripción de línea", "line_ind": "S#", "mod_lb_hrs": "Horas laborales", + "mod_lbr_ty": "Tipo de trabajo", "oem_partno": "OEM parte #", + "op_code_desc": "", "part_type": "Tipo de parte", "status": "Estado", "unq_seq": "Seq #" @@ -150,6 +167,10 @@ "labels": { "edit": "Línea de edición", "new": "Nueva línea" + }, + "successes": { + "created": "", + "updated": "" } }, "jobs": { diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 492d8e413..88fd98d61 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -4,8 +4,16 @@ "actions": { "assign": "Attribuer" }, + "errors": { + "deleting": "", + "saving": "" + }, "fields": { "employee": "Alloué à" + }, + "successes": { + "deleted": "", + "save": "" } }, "appointments": { @@ -136,13 +144,22 @@ } }, "joblines": { + "actions": { + "new": "" + }, + "errors": { + "creating": "", + "updating": "" + }, "fields": { "act_price": "Prix actuel", "db_price": "Prix de la base de données", "line_desc": "Description de la ligne", "line_ind": "S#", "mod_lb_hrs": "Heures de travail", + "mod_lbr_ty": "Type de travail", "oem_partno": "Pièce OEM #", + "op_code_desc": "", "part_type": "Type de pièce", "status": "Statut", "unq_seq": "Seq #" @@ -150,6 +167,10 @@ "labels": { "edit": "Ligne d'édition", "new": "Nouvelle ligne" + }, + "successes": { + "created": "", + "updated": "" } }, "jobs": { diff --git a/package.json b/package.json index ebb9e31f7..0c7a238fa 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,6 @@ "concurrently": "^4.0.1", "eslint": "^6.7.2", "eslint-plugin-promise": "^4.2.1", - "hasura-cli": "^1.0.0-beta.10" + "hasura-cli": "^1.1.0" } } diff --git a/yarn.lock b/yarn.lock index c72d7c808..6f36c5799 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1950,10 +1950,10 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= -hasura-cli@^1.0.0-beta.10: - version "1.0.0-rc.1" - resolved "https://registry.yarnpkg.com/hasura-cli/-/hasura-cli-1.0.0-rc.1.tgz#481453f88e7624f468f329c75a88fbbde3407f00" - integrity sha512-w6DGAhJZ6l7U89SD6QIxYetP3/dDxJc4jEVzjMAYsueeYWQKDeGgtZBFBW1sdgAlUtRmtIa5wbiFLXuagB6JqA== +hasura-cli@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hasura-cli/-/hasura-cli-1.1.0.tgz#a095e94c654d30354d8979602b8c0047c7b8a9e1" + integrity sha512-D1qXoYydx9Mgq7VQdCmOOvTlYhd1RcjQtn4s7pN1wb5w1ORIcDFLm1rS3w97bsx7wPRotIl0reyhc3+FDq+FFg== dependencies: axios "^0.19.0" chalk "^2.4.2"