Begin addign loading state to mutations BOD-134

This commit is contained in:
Patrick Fic
2020-07-22 10:46:34 -07:00
parent 5f2ced9b45
commit 8f8c26af54
8 changed files with 125 additions and 88 deletions

View File

@@ -1,5 +1,5 @@
import { Form, Modal, notification } from "antd"; import { Form, Modal, notification } from "antd";
import React from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
@@ -13,48 +13,51 @@ import { useMutation } from "@apollo/react-hooks";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
courtesyCarReturnModal: selectCourtesyCarReturn, courtesyCarReturnModal: selectCourtesyCarReturn,
bodyshop: selectBodyshop bodyshop: selectBodyshop,
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("courtesyCarReturn")) toggleModalVisible: () => dispatch(toggleModalVisible("courtesyCarReturn")),
}); });
export function InvoiceEnterModalContainer({ export function InvoiceEnterModalContainer({
courtesyCarReturnModal, courtesyCarReturnModal,
toggleModalVisible, toggleModalVisible,
bodyshop bodyshop,
}) { }) {
const [loading, setLoading] = useState(false);
const { visible, context, actions } = courtesyCarReturnModal; const { visible, context, actions } = courtesyCarReturnModal;
const { t } = useTranslation(); const { t } = useTranslation();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [updateContract] = useMutation(RETURN_CONTRACT); const [updateContract] = useMutation(RETURN_CONTRACT);
const handleFinish = values => { const handleFinish = (values) => {
setLoading(true);
updateContract({ updateContract({
variables: { variables: {
contractId: context.contractId, contractId: context.contractId,
cccontract: { cccontract: {
kmend: values.kmend, kmend: values.kmend,
actualreturn: values.actualreturn, actualreturn: values.actualreturn,
status: "contracts.status.returned" status: "contracts.status.returned",
}, },
courtesycarid: context.courtesyCarId, courtesycarid: context.courtesyCarId,
courtesycar: { courtesycar: {
status: "courtesycars.status.in", status: "courtesycars.status.in",
fuel: values.fuel, fuel: values.fuel,
mileage: values.kmend mileage: values.kmend,
} },
} },
}) })
.then(r => { .then((r) => {
if (actions.refetch) actions.refetch(); if (actions.refetch) actions.refetch();
toggleModalVisible(); toggleModalVisible();
}) })
.catch(error => { .catch((error) => {
notification["error"]({ notification["error"]({
message: t("contracts.errors.returning", { error: error }) message: t("contracts.errors.returning", { error: error }),
}); });
}); });
setLoading(false);
}; };
return ( return (
@@ -65,7 +68,7 @@ export function InvoiceEnterModalContainer({
width={"90%"} width={"90%"}
okText={t("general.actions.save")} okText={t("general.actions.save")}
onOk={() => form.submit()} onOk={() => form.submit()}
okButtonProps={{ htmlType: "submit" }} okButtonProps={{ htmlType: "submit", loading: loading }}
> >
<Form <Form
form={form} form={form}

View File

@@ -2,7 +2,7 @@ import { useMutation, useQuery } from "@apollo/react-hooks";
import { Form, Button } from "antd"; import { Form, Button } from "antd";
import moment from "moment"; import moment from "moment";
import queryString from "query-string"; import queryString from "query-string";
import React, { useEffect } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
@@ -26,6 +26,7 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const { t } = useTranslation(); const { t } = useTranslation();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [updateLoading, setUpdateLoading] = useState(false);
const [updateInvoice] = useMutation(UPDATE_INVOICE); const [updateInvoice] = useMutation(UPDATE_INVOICE);
const [updateInvoiceLine] = useMutation(UPDATE_INVOICE_LINE); const [updateInvoiceLine] = useMutation(UPDATE_INVOICE_LINE);
@@ -35,9 +36,9 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
}); });
const handleFinish = async (values) => { const handleFinish = async (values) => {
setUpdateLoading(true);
const { invoicelines, upload, ...invoice } = values; const { invoicelines, upload, ...invoice } = values;
const updates = []; const updates = [];
console.log("Start");
updates.push( updates.push(
updateInvoice({ updateInvoice({
variables: { invoiceId: search.invoiceid, invoice: invoice }, variables: { invoiceId: search.invoiceid, invoice: invoice },
@@ -54,6 +55,7 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
}); });
await Promise.all(updates); await Promise.all(updates);
setUpdateLoading(false);
}; };
useEffect(() => { useEffect(() => {
@@ -99,7 +101,7 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
: {} : {}
} }
> >
<Button htmlType="submit" type="primary"> <Button htmlType="submit" loading={updateLoading} type="primary">
{t("general.actions.save")} {t("general.actions.save")}
</Button> </Button>
<InvoiceFormContainer form={form} invoiceEdit /> <InvoiceFormContainer form={form} invoiceEdit />

View File

@@ -1,7 +1,7 @@
import { useMutation } from "@apollo/react-hooks"; import { useMutation } from "@apollo/react-hooks";
import { Button, Form, notification, Switch } from "antd"; import { Button, Form, notification, Switch } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { useHistory, useLocation, useParams } from "react-router-dom"; import { useHistory, useLocation, useParams } from "react-router-dom";
@@ -23,6 +23,7 @@ const mapDispatchToProps = (dispatch) => ({
export function JobIntakeForm({ formItems, bodyshop }) { export function JobIntakeForm({ formItems, bodyshop }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [intakeJob] = useMutation(UPDATE_JOB); const [intakeJob] = useMutation(UPDATE_JOB);
const [loading, setLoading] = useState(false);
const [markAptArrived] = useMutation(MARK_LATEST_APPOINTMENT_AS_ARRIVED); const [markAptArrived] = useMutation(MARK_LATEST_APPOINTMENT_AS_ARRIVED);
const { jobId } = useParams(); const { jobId } = useParams();
const history = useHistory(); const history = useHistory();
@@ -30,6 +31,7 @@ export function JobIntakeForm({ formItems, bodyshop }) {
const handleFinish = async (values) => { const handleFinish = async (values) => {
console.log("values", values); console.log("values", values);
setLoading(true);
logImEXEvent("job_complete_intake"); logImEXEvent("job_complete_intake");
const result = await intakeJob({ const result = await intakeJob({
@@ -70,6 +72,7 @@ export function JobIntakeForm({ formItems, bodyshop }) {
}); });
} }
console.log("handleFinish -> result", result); console.log("handleFinish -> result", result);
setLoading(false);
}; };
const [form] = Form.useForm(); const [form] = Form.useForm();
@@ -78,34 +81,40 @@ export function JobIntakeForm({ formItems, bodyshop }) {
<Form <Form
form={form} form={form}
onFinish={handleFinish} onFinish={handleFinish}
initialValues={{ addToProduction: true }}> initialValues={{ addToProduction: true }}
>
{t("intake.labels.checklist")} {t("intake.labels.checklist")}
<ConfigFormComponents componentList={formItems} /> <ConfigFormComponents componentList={formItems} />
<Form.Item <Form.Item
name='addToProduction' name="addToProduction"
valuePropName='checked' valuePropName="checked"
label={t("intake.labels.addtoproduction")}> label={t("intake.labels.addtoproduction")}
>
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name='scheduledCompletion' name="scheduledCompletion"
label={t("jobs.fields.scheduled_completion")} label={t("jobs.fields.scheduled_completion")}
rules={[ rules={[
{ {
required: true, required: true,
message: t("general.validation.required"), message: t("general.validation.required"),
}, },
]}> ]}
>
<DateTimePicker /> <DateTimePicker />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name='scheduledDelivery' name="scheduledDelivery"
label={t("jobs.fields.scheduled_delivery")}> label={t("jobs.fields.scheduled_delivery")}
>
<DateTimePicker /> <DateTimePicker />
</Form.Item> </Form.Item>
<Button htmlType='submit'>{t("general.actions.submit")}</Button> <Button loading={loading} htmlType="submit">
{t("general.actions.submit")}
</Button>
</Form> </Form>
); );
} }

View File

@@ -7,7 +7,8 @@ export default function JobLinesUpsertModalComponent({
visible, visible,
jobLine, jobLine,
handleCancel, handleCancel,
handleFinish handleFinish,
loading,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [form] = Form.useForm(); const [form] = Form.useForm();
@@ -32,6 +33,7 @@ export default function JobLinesUpsertModalComponent({
visible={visible} visible={visible}
okText={t("general.actions.save")} okText={t("general.actions.save")}
onOk={() => form.submit()} onOk={() => form.submit()}
okButtonProps={{ loading: loading }}
onCancel={handleCancel} onCancel={handleCancel}
> >
<Form.Item <Form.Item
@@ -39,8 +41,8 @@ export default function JobLinesUpsertModalComponent({
rules={[ rules={[
{ {
required: true, required: true,
message: t("general.validation.required") message: t("general.validation.required"),
} },
]} ]}
name="line_desc" name="line_desc"
> >

View File

@@ -1,73 +1,78 @@
import { notification } from "antd"; import { notification } from "antd";
import React from "react"; import React, { useState } from "react";
import { useMutation } from "@apollo/react-hooks"; import { useMutation } from "@apollo/react-hooks";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { INSERT_NEW_JOB_LINE, UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries"; import {
INSERT_NEW_JOB_LINE,
UPDATE_JOB_LINE,
} from "../../graphql/jobs-lines.queries";
import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectJobLineEditModal } from "../../redux/modals/modals.selectors"; import { selectJobLineEditModal } from "../../redux/modals/modals.selectors";
import JobLinesUpdsertModal from "./job-lines-upsert-modal.component"; import JobLinesUpdsertModal from "./job-lines-upsert-modal.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
jobLineEditModal: selectJobLineEditModal jobLineEditModal: selectJobLineEditModal,
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("jobLineEdit")) toggleModalVisible: () => dispatch(toggleModalVisible("jobLineEdit")),
}); });
function JobLinesUpsertModalContainer({ function JobLinesUpsertModalContainer({
jobLineEditModal, jobLineEditModal,
toggleModalVisible toggleModalVisible,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [insertJobLine] = useMutation(INSERT_NEW_JOB_LINE); const [insertJobLine] = useMutation(INSERT_NEW_JOB_LINE);
const [updateJobLine] = useMutation(UPDATE_JOB_LINE); const [updateJobLine] = useMutation(UPDATE_JOB_LINE);
const [loading, setLoading] = useState(false);
const handleFinish = values => { const handleFinish = (values) => {
setLoading(true);
if (!jobLineEditModal.context.id) { if (!jobLineEditModal.context.id) {
insertJobLine({ insertJobLine({
variables: { variables: {
lineInput: [{ jobid: jobLineEditModal.context.jobid, ...values }] lineInput: [{ jobid: jobLineEditModal.context.jobid, ...values }],
} },
}) })
.then(r => { .then((r) => {
if (jobLineEditModal.actions.refetch) if (jobLineEditModal.actions.refetch)
jobLineEditModal.actions.refetch(); jobLineEditModal.actions.refetch();
toggleModalVisible(); toggleModalVisible();
notification["success"]({ notification["success"]({
message: t("joblines.successes.created") message: t("joblines.successes.created"),
}); });
}) })
.catch(error => { .catch((error) => {
notification["error"]({ notification["error"]({
message: t("joblines.errors.creating", { message: t("joblines.errors.creating", {
message: error.message message: error.message,
}) }),
}); });
}); });
} else { } else {
updateJobLine({ updateJobLine({
variables: { variables: {
lineId: jobLineEditModal.context.id, lineId: jobLineEditModal.context.id,
line: values line: values,
} },
}) })
.then(r => { .then((r) => {
notification["success"]({ notification["success"]({
message: t("joblines.successes.updated") message: t("joblines.successes.updated"),
}); });
}) })
.catch(error => { .catch((error) => {
notification["success"]({ notification["success"]({
message: t("joblines.errors.updating", { message: t("joblines.errors.updating", {
message: error.message message: error.message,
}) }),
}); });
}); });
if (jobLineEditModal.actions.refetch) jobLineEditModal.actions.refetch(); if (jobLineEditModal.actions.refetch) jobLineEditModal.actions.refetch();
toggleModalVisible(); toggleModalVisible();
} }
setLoading(false);
}; };
const handleCancel = () => { const handleCancel = () => {
@@ -80,6 +85,7 @@ function JobLinesUpsertModalContainer({
jobLine={jobLineEditModal.context} jobLine={jobLineEditModal.context}
handleFinish={handleFinish} handleFinish={handleFinish}
handleCancel={handleCancel} handleCancel={handleCancel}
loading={loading}
/> />
); );
} }

View File

@@ -1,5 +1,5 @@
import { Button, notification } from "antd"; import { Button, notification } from "antd";
import React from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import axios from "axios"; import axios from "axios";
import { useMutation } from "@apollo/react-hooks"; import { useMutation } from "@apollo/react-hooks";
@@ -13,9 +13,10 @@ export default function JobsDocumentsDeleteButton({
const { t } = useTranslation(); const { t } = useTranslation();
const [deleteDocument] = useMutation(DELETE_DOCUMENT); const [deleteDocument] = useMutation(DELETE_DOCUMENT);
const imagesToDelete = galleryImages.filter((image) => image.isSelected); const imagesToDelete = galleryImages.filter((image) => image.isSelected);
const [loading, setLoading] = useState(false);
const handleDelete = () => { const handleDelete = () => {
logImEXEvent("job_documents_delete", { count: imagesToDelete.length }); logImEXEvent("job_documents_delete", { count: imagesToDelete.length });
setLoading(true);
imagesToDelete.forEach((image) => { imagesToDelete.forEach((image) => {
let timestamp = Math.floor(Date.now() / 1000); let timestamp = Math.floor(Date.now() / 1000);
let public_id = image.key; let public_id = image.key;
@@ -65,10 +66,15 @@ export default function JobsDocumentsDeleteButton({
}); });
}); });
}); });
setLoading(false);
}; };
return ( return (
<Button disabled={imagesToDelete.length < 1} onClick={handleDelete}> <Button
disabled={imagesToDelete.length < 1}
loading={loading}
onClick={handleDelete}
>
{t("documents.actions.delete")} {t("documents.actions.delete")}
</Button> </Button>
); );

View File

@@ -1,31 +1,49 @@
import React from "react"; import { useMutation, useQuery } from "@apollo/react-hooks";
import JobNotesComponent from "./jobs.notes.component"; import { notification } from "antd";
import { useQuery, useMutation } from "@apollo/react-hooks"; import React, { useState } from "react";
import AlertComponent from "../../components/alert/alert.component";
//import SpinComponent from "../../components/loading-spinner/loading-spinner.component"; //import SpinComponent from "../../components/loading-spinner/loading-spinner.component";
import { import { useTranslation } from "react-i18next";
QUERY_NOTES_BY_JOB_PK, import AlertComponent from "../../components/alert/alert.component";
DELETE_NOTE import { logImEXEvent } from "../../firebase/firebase.utils";
} from "../../graphql/notes.queries"; import { DELETE_NOTE, QUERY_NOTES_BY_JOB_PK } from "../../graphql/notes.queries";
import JobNotesComponent from "./jobs.notes.component";
export default function JobNotesContainer({ jobId }) { export default function JobNotesContainer({ jobId }) {
const { loading, error, data, refetch } = useQuery(QUERY_NOTES_BY_JOB_PK, { const { loading, error, data, refetch } = useQuery(QUERY_NOTES_BY_JOB_PK, {
variables: { id: jobId }, variables: { id: jobId },
fetchPolicy: "network-only" fetchPolicy: "network-only",
}); });
const [deleteNote] = useMutation(DELETE_NOTE); const [deleteNote] = useMutation(DELETE_NOTE);
const { t } = useTranslation();
const [deleteLoading, setDeleteLoading] = useState(false);
const handleNoteDelete = (id) => {
logImEXEvent("job_note_delete");
setDeleteLoading(true);
deleteNote({
variables: {
noteId: id,
},
}).then((r) => {
refetch();
notification["success"]({
message: t("notes.successes.deleted"),
});
});
setDeleteLoading(false);
};
//if (loading) return <SpinComponent />; //if (loading) return <SpinComponent />;
if (error) return <AlertComponent message={error.message} type='error' />; if (error) return <AlertComponent message={error.message} type="error" />;
return ( return (
<JobNotesComponent <JobNotesComponent
jobId={jobId} jobId={jobId}
loading={loading} loading={loading}
data={data ? data.jobs_by_pk.notes : null} data={data ? data.jobs_by_pk.notes : null}
refetch={refetch} refetch={refetch}
deleteNote={deleteNote} deleteLoading={deleteLoading}
handleNoteDelete={handleNoteDelete}
/> />
); );
} }

View File

@@ -4,12 +4,11 @@ import {
EyeInvisibleFilled, EyeInvisibleFilled,
WarningFilled WarningFilled
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Button, notification, Table } from "antd"; import { Button, Table } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import Moment from "react-moment"; import Moment from "react-moment";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import NoteUpsertModal from "../note-upsert-modal/note-upsert-modal.container"; import NoteUpsertModal from "../note-upsert-modal/note-upsert-modal.container";
@@ -22,9 +21,10 @@ export function JobNotesComponent({
loading, loading,
data, data,
refetch, refetch,
deleteNote, handleNoteDelete,
jobId, jobId,
setNoteUpsertContext, setNoteUpsertContext,
deleteLoading,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -59,7 +59,7 @@ export function JobNotesComponent({
sorter: (a, b) => new Date(a.updated_at) - new Date(b.updated_at), sorter: (a, b) => new Date(a.updated_at) - new Date(b.updated_at),
render: (text, record) => ( render: (text, record) => (
<span> <span>
<Moment format='MM/DD/YYYY @ HH:mm'>{record.updated_at}</Moment> <Moment format="MM/DD/YYYY @ HH:mm">{record.updated_at}</Moment>
</span> </span>
), ),
}, },
@@ -77,20 +77,9 @@ export function JobNotesComponent({
render: (text, record) => ( render: (text, record) => (
<span> <span>
<Button <Button
onClick={() => { loading={deleteLoading}
logImEXEvent("job_note_delete"); onClick={() => handleNoteDelete(record.id)}
>
deleteNote({
variables: {
noteId: record.id,
},
}).then((r) => {
refetch();
notification["success"]({
message: t("notes.successes.deleted"),
});
});
}}>
<DeleteFilled /> <DeleteFilled />
</Button> </Button>
<Button <Button
@@ -102,7 +91,8 @@ export function JobNotesComponent({
existingNote: record, existingNote: record,
}, },
}); });
}}> }}
>
<EditFilled /> <EditFilled />
</Button> </Button>
</span> </span>
@@ -121,14 +111,15 @@ export function JobNotesComponent({
jobId: jobId, jobId: jobId,
}, },
}); });
}}> }}
>
{t("notes.actions.new")} {t("notes.actions.new")}
</Button> </Button>
<Table <Table
loading={loading} loading={loading}
pagination={{ position: "bottom" }} pagination={{ position: "bottom" }}
columns={columns.map((item) => ({ ...item }))} columns={columns.map((item) => ({ ...item }))}
rowKey='id' rowKey="id"
dataSource={data} dataSource={data}
/> />
</div> </div>