diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 3fc2627a3..25c76f851 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -13447,6 +13447,27 @@ + + deleting + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + deleting_cloudinary false diff --git a/client/src/components/jobs-documents-gallery/jobs-document-gallery.reassign.component.jsx b/client/src/components/jobs-documents-gallery/jobs-document-gallery.reassign.component.jsx index f08dedadb..18be6fb62 100644 --- a/client/src/components/jobs-documents-gallery/jobs-document-gallery.reassign.component.jsx +++ b/client/src/components/jobs-documents-gallery/jobs-document-gallery.reassign.component.jsx @@ -1,14 +1,11 @@ -import { useApolloClient, useMutation } from "@apollo/client"; +import { useApolloClient } from "@apollo/client"; import { Button, Form, notification, Popover, Space } from "antd"; import axios from "axios"; import React, { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; -import { - GET_DOC_SIZE_BY_JOB, - UPDATE_DOCUMENT, -} from "../../graphql/documents.queries"; +import { GET_DOC_SIZE_BY_JOB } from "../../graphql/documents.queries"; import { selectBodyshop } from "../../redux/user/user.selectors"; import JobSearchSelect from "../job-search-select/job-search-select.component"; @@ -23,7 +20,11 @@ export default connect( mapDispatchToProps )(JobsDocumentsGalleryReassign); -export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages }) { +export function JobsDocumentsGalleryReassign({ + bodyshop, + galleryImages, + callback, +}) { const { t } = useTranslation(); const [form] = Form.useForm(); @@ -36,34 +37,33 @@ export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages }) { const client = useApolloClient(); const [visible, setVisible] = useState(false); const [loading, setLoading] = useState(false); - const [updateDocument] = useMutation(UPDATE_DOCUMENT); - const updateImage = async (i, jobid) => { - //Move the cloudinary image + // const updateImage = async (i, jobid) => { + // //Move the cloudinary image - //Update it in the database. - const result = await updateDocument({ - variables: { - id: i.id, - document: { - key: i.public_id, - jobid: jobid, - }, - }, - }); + // //Update it in the database. + // const result = await updateDocument({ + // variables: { + // id: i.id, + // document: { + // key: i.public_id, + // jobid: jobid, + // }, + // }, + // }); - if (!!result.errors) { - notification["error"]({ - message: t("documents.errors.updating", { - message: JSON.stringify(result.errors), - }), - }); - } else { - notification["success"]({ - message: t("documents.successes.updated"), - }); - } - }; + // if (!!result.errors) { + // notification["error"]({ + // message: t("documents.errors.updating", { + // message: JSON.stringify(result.errors), + // }), + // }); + // } else { + // notification["success"]({ + // message: t("documents.successes.updated"), + // }); + // } + // }; const handleFinish = async ({ jobid }) => { setLoading(true); @@ -96,6 +96,7 @@ export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages }) { } const res = await axios.post("/media/rename", { + tojobid: jobid, documents: selectedImages.map((i) => { //Need to check if the current key folder is null, or another job. const currentKeys = i.key.split("/"); @@ -110,24 +111,21 @@ export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages }) { }; }), }); + //Add in confirmation & errors. + if (callback) callback(); - res.data - .filter((d) => d.error) - .forEach((d) => { - notification["error"]({ message: t("documents.errors.updating") }); - console.error("Error updating job document", d); + if (res.errors) { + notification["error"]({ + message: t("documents.errors.updating", { + message: JSON.stringify(res.errors), + }), }); - - const proms = []; - - res.data - .filter((d) => !d.error) - .forEach((d) => { - proms.push(updateImage(d, jobid)); + } + if (!res.mutationResult?.errors) { + notification["success"]({ + message: t("documents.successes.updated"), }); - - await Promise.all(proms); - + } setVisible(false); setLoading(false); }; diff --git a/client/src/components/jobs-documents-gallery/jobs-documents-gallery.component.jsx b/client/src/components/jobs-documents-gallery/jobs-documents-gallery.component.jsx index dbc603660..dbd04dddc 100644 --- a/client/src/components/jobs-documents-gallery/jobs-documents-gallery.component.jsx +++ b/client/src/components/jobs-documents-gallery/jobs-documents-gallery.component.jsx @@ -125,7 +125,10 @@ function JobsDocumentsComponent({ deletionCallback={billsCallback || refetch} /> {!billId && ( - + )} diff --git a/client/src/components/jobs-documents-gallery/jobs-documents-gallery.delete.component.jsx b/client/src/components/jobs-documents-gallery/jobs-documents-gallery.delete.component.jsx index 4928c572b..ce5669df2 100644 --- a/client/src/components/jobs-documents-gallery/jobs-documents-gallery.delete.component.jsx +++ b/client/src/components/jobs-documents-gallery/jobs-documents-gallery.delete.component.jsx @@ -1,11 +1,9 @@ import { QuestionCircleOutlined } from "@ant-design/icons"; -import { useMutation } from "@apollo/client"; import { Button, notification, Popconfirm } from "antd"; import axios from "axios"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { logImEXEvent } from "../../firebase/firebase.utils"; -import { DELETE_DOCUMENTS } from "../../graphql/documents.queries"; //Context: currentUserEmail, bodyshop, jobid, invoiceid export default function JobsDocumentsDeleteButton({ @@ -13,7 +11,7 @@ export default function JobsDocumentsDeleteButton({ deletionCallback, }) { const { t } = useTranslation(); - const [deleteDocument] = useMutation(DELETE_DOCUMENTS); + const imagesToDelete = [ ...galleryImages.images.filter((image) => image.isSelected), ...galleryImages.other.filter((image) => image.isSelected), @@ -27,31 +25,10 @@ export default function JobsDocumentsDeleteButton({ ids: imagesToDelete, }); - const successfulDeletes = []; - res.data.forEach((resType) => { - Object.keys(resType.deleted).forEach((key) => { - if (resType.deleted[key] !== "deleted") { - notification["error"]({ - message: t("documents.errors.deleting_cloudinary", { - message: JSON.stringify(resType.deleted[key]), - }), - }); - } else { - successfulDeletes.push(key.replace(/\.[^/.]+$/, "")); - } - }); - }); - const delres = await deleteDocument({ - variables: { - ids: imagesToDelete - .filter((i) => successfulDeletes.includes(i.key)) - .map((i) => i.id), - }, - }); - if (delres.errors) { + if (res.data.error) { notification["error"]({ message: t("documents.errors.deleting", { - message: JSON.stringify(delres.errors), + error: JSON.stringify(res.data.error.response.errors), }), }); } else { diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 2ce90edf6..299c554fa 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -835,6 +835,7 @@ }, "errors": { "deletes3": "Error deleting document from storage. ", + "deleting": "Error deleting documents {{error}}", "deleting_cloudinary": "Error deleting document from storage. {{message}}", "getpresignurl": "Error obtaining presigned URL for document. {{message}}", "insert": "Unable to upload file. {{message}}", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index eab3ad308..bd8f3bcb2 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -835,6 +835,7 @@ }, "errors": { "deletes3": "Error al eliminar el documento del almacenamiento.", + "deleting": "", "deleting_cloudinary": "", "getpresignurl": "Error al obtener la URL prescrita para el documento. {{message}}", "insert": "Incapaz de cargar el archivo. {{message}}", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index d40a31e33..a79c49a92 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -835,6 +835,7 @@ }, "errors": { "deletes3": "Erreur lors de la suppression du document du stockage.", + "deleting": "", "deleting_cloudinary": "", "getpresignurl": "Erreur lors de l'obtention de l'URL présignée pour le document. {{message}}", "insert": "Incapable de télécharger le fichier. {{message}}", diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index 7a01b8a85..db92f3c0e 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -1611,3 +1611,13 @@ exports.INSERT_EMAIL_AUDIT = `mutation INSERT_EMAIL_AUDIT($email: email_audit_tr } } `; + +exports.DELETE_MEDIA_DOCUMENTS = ` +mutation DELETE_DOCUMENTS($ids: [uuid!]!) { + delete_documents(where: { id: { _in: $ids } }) { + returning { + id + } + } +} +`; diff --git a/server/media/media.js b/server/media/media.js index dbd2887ed..7f199cb75 100644 --- a/server/media/media.js +++ b/server/media/media.js @@ -1,6 +1,8 @@ const path = require("path"); const _ = require("lodash"); const logger = require("../utils/logger"); +const client = require("../graphql-client/graphql-client").client; +const queries = require("../graphql-client/queries"); require("dotenv").config({ path: path.resolve( @@ -69,11 +71,38 @@ exports.deleteFiles = async (req, res) => { ); } - res.send(returns); + // Delete it on apollo. + const successfulDeletes = []; + returns.forEach((resType) => { + Object.keys(resType.deleted).forEach((key) => { + if ( + resType.deleted[key] === "deleted" || + resType.deleted[key] === "not_found" + ) { + successfulDeletes.push(key.replace(/\.[^/.]+$/, "")); + } + }); + }); + + try { + const result = await client.request(queries.DELETE_MEDIA_DOCUMENTS, { + ids: ids + .filter((i) => successfulDeletes.includes(i.key)) + .map((i) => i.id), + }); + + res.send({ returns, result }); + } catch (error) { + logger.log("media-delete-error", "ERROR", req.user.email, null, [ + { ids, error: error.message || JSON.stringify(error) }, + ]); + + res.json({ error }); + } }; exports.renameKeys = async (req, res) => { - const { documents } = req.body; + const { documents, tojobid } = req.body; logger.log("media-bulk-rename", "DEBUG", req.user.email, null, documents); const proms = []; @@ -98,8 +127,37 @@ exports.renameKeys = async (req, res) => { let result; result = await Promise.all(proms); + const errors = []; + result + .filter((d) => d.error) + .forEach((d) => { + errors.push(d); + }); - res.send(result); + let mutations = ""; + + result + .filter((d) => !d.error) + .forEach((d, idx) => { + //Create mutation text + + mutations = + mutations + + ` + update_doc${idx}:update_documents_by_pk(pk_columns: { id: "${d.id}" }, _set: {key: "${d.public_id}", jobid: "${tojobid}"}){ + id + } + `; + }); + + if (mutations !== "") { + const mutationResult = await client.request(`mutation { + ${mutations} + }`); + res.json({ errors, mutationResult }); + } else { + res.json({ errors: "No images were succesfully moved on remote server. " }); + } }; //Also needs to be updated in upload utility and mobile app.