diff --git a/client/src/components/header/header.component.jsx b/client/src/components/header/header.component.jsx index 094b5f448..fe0a4997c 100644 --- a/client/src/components/header/header.component.jsx +++ b/client/src/components/header/header.component.jsx @@ -15,107 +15,168 @@ export default ({ }) => { const { t } = useTranslation(); //TODO Add + return ( - + {logo ? ( - - Shop Logo + + Shop Logo ) : null} - - - - - {t("menus.header.home")} - - - - - - - {t("menus.header.schedule")} - - - - {t("menus.header.activejobs")} - - - - {t("menus.header.availablejobs")} - - - + {landingHeader ? ( + + - - - - - {t("menus.header.owners")} - - - - - - {t("menus.header.vehicles")} - - - - - - {t("menus.header.shop_config")} - - - - {t("menus.header.shop_vendors")} - - - - - - - {currentUser.displayName || t("general.labels.unknown")} - - }> - - {t("user.actions.signout")} - - - {t("menus.currentuser.profile")} - - - {t("menus.currentuser.languageselector")} - - }> - - {t("general.languages.english")} +
+ + {currentUser.displayName || t("general.labels.unknown")} +
+ } + > + + {t("user.actions.signout")} - - {t("general.languages.french")} + + + {t("menus.currentuser.profile")} + - - {t("general.languages.spanish")} + + + {t("menus.currentuser.languageselector")} + + } + > + + {t("general.languages.english")} + + + {t("general.languages.french")} + + + {t("general.languages.spanish")} + + +
+
+ ) : ( + + + + + {t("menus.header.home")} + + + + + + + {t("menus.header.schedule")} + + + + {t("menus.header.activejobs")} + + + + {t("menus.header.availablejobs")} + - - + + + + + {t("menus.header.owners")} + + + + + + {t("menus.header.vehicles")} + + + + + + {t("menus.header.shop_config")} + + + + {t("menus.header.shop_vendors")} + + + + + + + {currentUser.displayName || t("general.labels.unknown")} + + } + > + + {t("user.actions.signout")} + + + + {t("menus.currentuser.profile")} + + + + + {t("menus.currentuser.languageselector")} + + } + > + + {t("general.languages.english")} + + + {t("general.languages.french")} + + + {t("general.languages.spanish")} + + + +
+ )} - {!landingHeader ? null : }
); }; 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 9f3232b42..4fef3f7d2 100644 --- a/client/src/components/job-detail-lines/job-lines.component.jsx +++ b/client/src/components/job-detail-lines/job-lines.component.jsx @@ -1,14 +1,13 @@ import { Button, Input, Table } from "antd"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; +import { Link } from "react-router-dom"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; import { alphaSort } from "../../utils/sorters"; -import { Link } from "react-router-dom"; -//import EditableCell from "./job-lines-cell.component"; import AllocationsAssignmentContainer from "../allocations-assignment/allocations-assignment.container"; -import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container"; import AllocationsBulkAssignmentContainer from "../allocations-bulk-assignment/allocations-bulk-assignment.container"; -import JobLineUpsertModalContainer from "../job-lines-upsert-modal/job-lines-upsert-modal.container"; +import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container"; + export default function JobLinesComponent({ loading, refetch, @@ -18,8 +17,7 @@ export default function JobLinesComponent({ setSelectedLines, partsOrderModalVisible, jobId, - editLineModalVisible, - lineToEdit + setJobLineEditContext }) { const [state, setState] = useState({ sortedInfo: {} @@ -145,9 +143,8 @@ export default function JobLinesComponent({ {record.allocations && record.allocations.length > 0 ? record.allocations.map(item => (
{`${item.employee.first_name} ${item.employee.last_name} (${item.hours})`}
+ key={item.id} + >{`${item.employee.first_name} ${item.employee.last_name} (${item.hours})`} )) : null} @@ -201,14 +203,6 @@ export default function JobLinesComponent({ jobId={jobId} /> - - { return ( @@ -222,7 +216,8 @@ export default function JobLinesComponent({ /> (
{t("parts_orders.labels.orderhistory")} @@ -252,11 +247,14 @@ export default function JobLinesComponent({ pagination={{ position: "top", defaultPageSize: 25 }} rowSelection={{ // selectedRowKeys: selectedLines, + onSelectAll: (selected, selectedRows, changeRows) => { + setSelectedLines(selectedRows); + }, onSelect: (record, selected, selectedRows, nativeEvent) => setSelectedLines(selectedRows) }} columns={columns.map(item => ({ ...item }))} - rowKey='id' + rowKey="id" dataSource={jobLines} onChange={handleTableChange} /> diff --git a/client/src/components/job-detail-lines/job-lines.container.jsx b/client/src/components/job-detail-lines/job-lines.container.jsx index 303dab8e8..5ff515c19 100644 --- a/client/src/components/job-detail-lines/job-lines.container.jsx +++ b/client/src/components/job-detail-lines/job-lines.container.jsx @@ -5,9 +5,16 @@ import { GET_JOB_LINES_BY_PK } from "../../graphql/jobs-lines.queries"; import AlertComponent from "../alert/alert.component"; import JobLinesComponent from "./job-lines.component"; -//export default Form.create({ name: "JobsDetailJobLines" })( - -export default function JobLinesContainer({ jobId }) { +import { connect } from "react-redux"; +import { setModalContext } from "../../redux/modals/modals.actions"; +const mapDispatchToProps = dispatch => ({ + setJobLineEditContext: context => + dispatch(setModalContext({ context: context, modal: "jobLineEdit" })) +}); +export default connect( + null, + mapDispatchToProps +)(function JobLinesContainer({ jobId, setJobLineEditContext }) { const { loading, error, data, refetch } = useQuery(GET_JOB_LINES_BY_PK, { variables: { id: jobId }, fetchPolicy: "network-only" @@ -16,10 +23,8 @@ export default function JobLinesContainer({ jobId }) { const [searchText, setSearchText] = useState(""); const [selectedLines, setSelectedLines] = useState([]); const partsOrderModalVisible = useState(false); - const editLineModalVisible = useState(false); - const lineToEdit = useState({}); - if (error) return ; + if (error) return ; return ( ); -} -//); +}); 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 1684db021..4697c66e1 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,31 +1,33 @@ -import { Input, Modal } from "antd"; +import { Modal, Form } from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; export default function JobLinesUpsertModalComponent({ visible, - changeVisibility, - lineState, - setLineState, - updateExistingLine, - insertNewLine + jobLine, + handleOk, + handleCancel, + handleSubmit, + form }) { const { t } = useTranslation(); - const handleChange = e => { - setLineState({ ...lineState, [e.target.name]: e.target.value }); - }; + //const { getFieldDecorator, isFieldsTouched, resetFields } = form; + console.log("jobLine", jobLine); return ( { - lineState.id ? updateExistingLine() : insertNewLine(); - }} - onCancel={() => { - changeVisibility(false); - }}> - + onOk={handleOk} + onCancel={handleCancel} + > +
+ {JSON.stringify(jobLine)} +
); } 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 f7b2a7d9d..6a188c55b 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 @@ -1,71 +1,102 @@ -import { notification } from "antd"; -import React, { useEffect, useState } from "react"; +import { Form, notification } from "antd"; +import React from "react"; import { useMutation } from "react-apollo"; import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; import { INSERT_NEW_JOB_LINE, UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries"; +import { toggleModalVisible } from "../../redux/modals/modals.actions"; +import { selectJobLineEditModal } from "../../redux/modals/modals.selectors"; import JobLinesUpdsertModal from "./job-lines-upsert-modal.component"; -export default function JobLinesUpsertModalContainer({ - jobId, - visible, - changeVisibility, - refetch, - existingLine +const mapStateToProps = createStructuredSelector({ + jobLineEditModal: selectJobLineEditModal +}); +const mapDispatchToProps = dispatch => ({ + toggleModalVisible: () => dispatch(toggleModalVisible("jobLineEdit")) +}); + +function JobLinesUpsertModalContainer({ + jobLineEditModal, + toggleModalVisible, + form }) { const { t } = useTranslation(); const [insertJobLine] = useMutation(INSERT_NEW_JOB_LINE); const [updateJobLine] = useMutation(UPDATE_JOB_LINE); - const [lineState, setLineState] = useState({}); + const handleSubmit = e => { + e.preventDefault(); - useEffect(() => { - //Required to prevent infinite looping. - if (existingLine) { - setLineState(existingLine); - } - }, [existingLine]); - - const insertNewLine = () => { - insertJobLine({ - variables: { - lineInput: [{ ...lineState, jobid: jobId }] + form.validateFieldsAndScroll((err, values) => { + if (err) { + notification["error"]({ + message: t("jobs.errors.validationtitle"), + description: t("jobs.errors.validation") + }); + } + if (!err) { + if (true) { + insertJobLine({ + variables: { + //lineInput: [{ ...lineState, jobid: jobId }] + } + }).then(r => { + if (jobLineEditModal.actions.refetch) + jobLineEditModal.actions.refetch(); + toggleModalVisible(); + notification["success"]({ + message: t("joblines.successes.create") + }); + }); + } + + if (false) { + //Required, otherwise unable to spread in new note prop. + //delete lineState.__typename; + updateJobLine({ + variables: { + //lineId: lineState.id, + //line: lineState + } + }).then(r => { + notification["success"]({ + message: t("joblines.successes.updated") + }); + }); + if (jobLineEditModal.actions.refetch) + jobLineEditModal.actions.refetch(); + toggleModalVisible(); + } } - }).then(r => { - refetch(); - changeVisibility(!visible); - notification["success"]({ - message: t("joblines.successes.create") - }); }); }; - const updateExistingLine = () => { - //Required, otherwise unable to spread in new note prop. - delete lineState.__typename; - updateJobLine({ - variables: { - lineId: lineState.id, - line: lineState - } - }).then(r => { - notification["success"]({ - message: t("joblines.successes.updated") - }); - }); - refetch(); - changeVisibility(!visible); + const handleOk = () => { + //lineState.id ? updateExistingLine() : insertNewLine(); }; + const handleCancel = () => { + toggleModalVisible(); + }; + return ( ); } + +export default connect( + mapStateToProps, + mapDispatchToProps +)( + Form.create({ name: "JobsDetailPageContainer" })(JobLinesUpsertModalContainer) +); diff --git a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx index bf04307a2..7aa413ccc 100644 --- a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx +++ b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx @@ -1,7 +1,12 @@ import { Form, Icon, Tabs } from "antd"; import React, { useContext } from "react"; import { useTranslation } from "react-i18next"; -import { FaHardHat, FaInfo, FaRegStickyNote, FaShieldAlt } from "react-icons/fa"; +import { + FaHardHat, + FaInfo, + FaRegStickyNote, + FaShieldAlt +} from "react-icons/fa"; import ResetForm from "../../components/form-items-formatted/reset-form-item.component"; import JobsLinesContainer from "../../components/job-detail-lines/job-lines.container"; import JobsDetailClaims from "../../components/jobs-detail-claims/jobs-detail-claims.component"; @@ -13,6 +18,7 @@ import JobsDocumentsContainer from "../../components/jobs-documents/jobs-documen import JobNotesContainer from "../../components/jobs-notes/jobs-notes.container"; import ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container"; import JobDetailFormContext from "./jobs-detail.page.context"; +import JobLineUpsertModalContainer from "../../components/job-lines-upsert-modal/job-lines-upsert-modal.container"; export default function JobsDetailPage({ job, @@ -46,6 +52,8 @@ export default function JobsDetailPage({ refetch={refetch} /> + +
({ + type: ModalsActionTypes.TOGGLE_MODAL_VISIBLE, + payload: modalName +}); + +//Modal Context: {context (context object), modal(name of modal)} +export const setModalContext = modalContext => ({ + type: ModalsActionTypes.SET_MODAL_CONTEXT, + payload: modalContext +}); diff --git a/client/src/redux/modals/modals.reducer.js b/client/src/redux/modals/modals.reducer.js new file mode 100644 index 000000000..5fc5588df --- /dev/null +++ b/client/src/redux/modals/modals.reducer.js @@ -0,0 +1,37 @@ +import ModalsActionTypes from "./modals.types"; + +const INITIAL_STATE = { + jobLineEdit: { + visible: false, + context: null, + actions: { + refetch: null + } + } +}; + +const modalsReducer = (state = INITIAL_STATE, action) => { + switch (action.type) { + case ModalsActionTypes.TOGGLE_MODAL_VISIBLE: + return { + ...state, + [action.payload]: { + ...state[action.payload], + visible: !state[action.payload].visible + } + }; + case ModalsActionTypes.SET_MODAL_CONTEXT: + return { + ...state, + [action.payload.modal]: { + ...state[action.payload.modal], + ...action.payload.context, + visible: true + } + }; + default: + return state; + } +}; + +export default modalsReducer; diff --git a/client/src/redux/modals/modals.sagas.js b/client/src/redux/modals/modals.sagas.js new file mode 100644 index 000000000..7cf750062 --- /dev/null +++ b/client/src/redux/modals/modals.sagas.js @@ -0,0 +1,24 @@ +import { all } from "redux-saga/effects"; + +// export function* onSendEmail() { +// yield takeLatest(EmailActionTypes.SEND_EMAIL, sendEmail); +// } +// export function* sendEmail(payload) { +// try { +// console.log("Sending thta email", payload); +// axios.post("/sendemail", payload).then(response => { +// console.log(JSON.stringify(response)); +// put(sendEmailSuccess()); +// }); +// } catch (error) { +// console.log("Error in sendEmail saga."); +// yield put(sendEmailFailure(error.message)); +// } +// } + + +export function* modalsSagas() { + yield all([ + //call(onSendEmail), + ]); +} diff --git a/client/src/redux/modals/modals.selectors.js b/client/src/redux/modals/modals.selectors.js new file mode 100644 index 000000000..bbe7c8ba8 --- /dev/null +++ b/client/src/redux/modals/modals.selectors.js @@ -0,0 +1,9 @@ +import { createSelector } from "reselect"; + +const selectModals = state => state.modals; + +export const selectJobLineEditModal = createSelector( + [selectModals], + modals => modals.jobLineEdit +); + diff --git a/client/src/redux/modals/modals.types.js b/client/src/redux/modals/modals.types.js new file mode 100644 index 000000000..01ea4c793 --- /dev/null +++ b/client/src/redux/modals/modals.types.js @@ -0,0 +1,5 @@ +const ModalActionTypes = { + TOGGLE_MODAL_VISIBLE: "TOGGLE_MODAL_VISIBLE", + SET_MODAL_CONTEXT: "SET_JOBLINEEDIT_CONTEXT" +}; +export default ModalActionTypes; diff --git a/client/src/redux/root.reducer.js b/client/src/redux/root.reducer.js index 70894e672..091bd39fe 100644 --- a/client/src/redux/root.reducer.js +++ b/client/src/redux/root.reducer.js @@ -5,18 +5,19 @@ import storage from "redux-persist/lib/storage"; import userReducer from "./user/user.reducer"; import messagingReducer from "./messaging/messaging.reducer"; import emailReducer from "./email/email.reducer"; - +import modalsReducer from './modals/modals.reducer' const persistConfig = { key: "root", storage, //whitelist: ["user"] - blacklist: ["user", "email", "messaging"] + blacklist: ["user", "email", "messaging", "modals"] }; const rootReducer = combineReducers({ user: userReducer, messaging: messagingReducer, - email: emailReducer + email: emailReducer, + modals: modalsReducer }); export default persistReducer(persistConfig, rootReducer); diff --git a/client/src/redux/root.saga.js b/client/src/redux/root.saga.js index 0507c6396..ce288b568 100644 --- a/client/src/redux/root.saga.js +++ b/client/src/redux/root.saga.js @@ -3,6 +3,8 @@ import { all, call } from "redux-saga/effects"; import { userSagas } from "./user/user.sagas"; import { messagingSagas } from "./messaging/messaging.sagas"; import { emailSagas } from "./email/email.sagas"; +import { modalsSagas } from "./modals/modals.sagas"; export default function* rootSaga() { - yield all([call(userSagas), call(messagingSagas), call(emailSagas)]); + yield all([call(userSagas), call(messagingSagas), call(emailSagas), + call(modalsSagas)]); }