IO-2049 Document delete&move on server side
This commit is contained in:
@@ -13447,6 +13447,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>deleting</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>
|
<concept_node>
|
||||||
<name>deleting_cloudinary</name>
|
<name>deleting_cloudinary</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
import { useApolloClient, useMutation } from "@apollo/client";
|
import { useApolloClient } from "@apollo/client";
|
||||||
import { Button, Form, notification, Popover, Space } from "antd";
|
import { Button, Form, notification, Popover, Space } from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, 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";
|
||||||
import {
|
import { GET_DOC_SIZE_BY_JOB } from "../../graphql/documents.queries";
|
||||||
GET_DOC_SIZE_BY_JOB,
|
|
||||||
UPDATE_DOCUMENT,
|
|
||||||
} from "../../graphql/documents.queries";
|
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
||||||
|
|
||||||
@@ -23,7 +20,11 @@ export default connect(
|
|||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(JobsDocumentsGalleryReassign);
|
)(JobsDocumentsGalleryReassign);
|
||||||
|
|
||||||
export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages }) {
|
export function JobsDocumentsGalleryReassign({
|
||||||
|
bodyshop,
|
||||||
|
galleryImages,
|
||||||
|
callback,
|
||||||
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
@@ -36,34 +37,33 @@ export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages }) {
|
|||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [updateDocument] = useMutation(UPDATE_DOCUMENT);
|
|
||||||
|
|
||||||
const updateImage = async (i, jobid) => {
|
// const updateImage = async (i, jobid) => {
|
||||||
//Move the cloudinary image
|
// //Move the cloudinary image
|
||||||
|
|
||||||
//Update it in the database.
|
// //Update it in the database.
|
||||||
const result = await updateDocument({
|
// const result = await updateDocument({
|
||||||
variables: {
|
// variables: {
|
||||||
id: i.id,
|
// id: i.id,
|
||||||
document: {
|
// document: {
|
||||||
key: i.public_id,
|
// key: i.public_id,
|
||||||
jobid: jobid,
|
// jobid: jobid,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
|
|
||||||
if (!!result.errors) {
|
// if (!!result.errors) {
|
||||||
notification["error"]({
|
// notification["error"]({
|
||||||
message: t("documents.errors.updating", {
|
// message: t("documents.errors.updating", {
|
||||||
message: JSON.stringify(result.errors),
|
// message: JSON.stringify(result.errors),
|
||||||
}),
|
// }),
|
||||||
});
|
// });
|
||||||
} else {
|
// } else {
|
||||||
notification["success"]({
|
// notification["success"]({
|
||||||
message: t("documents.successes.updated"),
|
// message: t("documents.successes.updated"),
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
const handleFinish = async ({ jobid }) => {
|
const handleFinish = async ({ jobid }) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -96,6 +96,7 @@ export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const res = await axios.post("/media/rename", {
|
const res = await axios.post("/media/rename", {
|
||||||
|
tojobid: jobid,
|
||||||
documents: selectedImages.map((i) => {
|
documents: selectedImages.map((i) => {
|
||||||
//Need to check if the current key folder is null, or another job.
|
//Need to check if the current key folder is null, or another job.
|
||||||
const currentKeys = i.key.split("/");
|
const currentKeys = i.key.split("/");
|
||||||
@@ -110,24 +111,21 @@ export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages }) {
|
|||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
//Add in confirmation & errors.
|
||||||
|
if (callback) callback();
|
||||||
|
|
||||||
res.data
|
if (res.errors) {
|
||||||
.filter((d) => d.error)
|
notification["error"]({
|
||||||
.forEach((d) => {
|
message: t("documents.errors.updating", {
|
||||||
notification["error"]({ message: t("documents.errors.updating") });
|
message: JSON.stringify(res.errors),
|
||||||
console.error("Error updating job document", d);
|
}),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
const proms = [];
|
if (!res.mutationResult?.errors) {
|
||||||
|
notification["success"]({
|
||||||
res.data
|
message: t("documents.successes.updated"),
|
||||||
.filter((d) => !d.error)
|
|
||||||
.forEach((d) => {
|
|
||||||
proms.push(updateImage(d, jobid));
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
await Promise.all(proms);
|
|
||||||
|
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -125,7 +125,10 @@ function JobsDocumentsComponent({
|
|||||||
deletionCallback={billsCallback || refetch}
|
deletionCallback={billsCallback || refetch}
|
||||||
/>
|
/>
|
||||||
{!billId && (
|
{!billId && (
|
||||||
<JobsDocumentsGalleryReassign galleryImages={galleryImages} />
|
<JobsDocumentsGalleryReassign
|
||||||
|
galleryImages={galleryImages}
|
||||||
|
callback={refetch}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
</Col>
|
</Col>
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { QuestionCircleOutlined } from "@ant-design/icons";
|
import { QuestionCircleOutlined } from "@ant-design/icons";
|
||||||
import { useMutation } from "@apollo/client";
|
|
||||||
import { Button, notification, Popconfirm } from "antd";
|
import { Button, notification, Popconfirm } from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { DELETE_DOCUMENTS } from "../../graphql/documents.queries";
|
|
||||||
//Context: currentUserEmail, bodyshop, jobid, invoiceid
|
//Context: currentUserEmail, bodyshop, jobid, invoiceid
|
||||||
|
|
||||||
export default function JobsDocumentsDeleteButton({
|
export default function JobsDocumentsDeleteButton({
|
||||||
@@ -13,7 +11,7 @@ export default function JobsDocumentsDeleteButton({
|
|||||||
deletionCallback,
|
deletionCallback,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [deleteDocument] = useMutation(DELETE_DOCUMENTS);
|
|
||||||
const imagesToDelete = [
|
const imagesToDelete = [
|
||||||
...galleryImages.images.filter((image) => image.isSelected),
|
...galleryImages.images.filter((image) => image.isSelected),
|
||||||
...galleryImages.other.filter((image) => image.isSelected),
|
...galleryImages.other.filter((image) => image.isSelected),
|
||||||
@@ -27,31 +25,10 @@ export default function JobsDocumentsDeleteButton({
|
|||||||
ids: imagesToDelete,
|
ids: imagesToDelete,
|
||||||
});
|
});
|
||||||
|
|
||||||
const successfulDeletes = [];
|
if (res.data.error) {
|
||||||
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) {
|
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
message: t("documents.errors.deleting", {
|
message: t("documents.errors.deleting", {
|
||||||
message: JSON.stringify(delres.errors),
|
error: JSON.stringify(res.data.error.response.errors),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -835,6 +835,7 @@
|
|||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"deletes3": "Error deleting document from storage. ",
|
"deletes3": "Error deleting document from storage. ",
|
||||||
|
"deleting": "Error deleting documents {{error}}",
|
||||||
"deleting_cloudinary": "Error deleting document from storage. {{message}}",
|
"deleting_cloudinary": "Error deleting document from storage. {{message}}",
|
||||||
"getpresignurl": "Error obtaining presigned URL for document. {{message}}",
|
"getpresignurl": "Error obtaining presigned URL for document. {{message}}",
|
||||||
"insert": "Unable to upload file. {{message}}",
|
"insert": "Unable to upload file. {{message}}",
|
||||||
|
|||||||
@@ -835,6 +835,7 @@
|
|||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"deletes3": "Error al eliminar el documento del almacenamiento.",
|
"deletes3": "Error al eliminar el documento del almacenamiento.",
|
||||||
|
"deleting": "",
|
||||||
"deleting_cloudinary": "",
|
"deleting_cloudinary": "",
|
||||||
"getpresignurl": "Error al obtener la URL prescrita para el documento. {{message}}",
|
"getpresignurl": "Error al obtener la URL prescrita para el documento. {{message}}",
|
||||||
"insert": "Incapaz de cargar el archivo. {{message}}",
|
"insert": "Incapaz de cargar el archivo. {{message}}",
|
||||||
|
|||||||
@@ -835,6 +835,7 @@
|
|||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"deletes3": "Erreur lors de la suppression du document du stockage.",
|
"deletes3": "Erreur lors de la suppression du document du stockage.",
|
||||||
|
"deleting": "",
|
||||||
"deleting_cloudinary": "",
|
"deleting_cloudinary": "",
|
||||||
"getpresignurl": "Erreur lors de l'obtention de l'URL présignée pour le document. {{message}}",
|
"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}}",
|
"insert": "Incapable de télécharger le fichier. {{message}}",
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const _ = require("lodash");
|
const _ = require("lodash");
|
||||||
const logger = require("../utils/logger");
|
const logger = require("../utils/logger");
|
||||||
|
const client = require("../graphql-client/graphql-client").client;
|
||||||
|
const queries = require("../graphql-client/queries");
|
||||||
|
|
||||||
require("dotenv").config({
|
require("dotenv").config({
|
||||||
path: path.resolve(
|
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) => {
|
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);
|
logger.log("media-bulk-rename", "DEBUG", req.user.email, null, documents);
|
||||||
|
|
||||||
const proms = [];
|
const proms = [];
|
||||||
@@ -98,8 +127,37 @@ exports.renameKeys = async (req, res) => {
|
|||||||
let result;
|
let result;
|
||||||
|
|
||||||
result = await Promise.all(proms);
|
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.
|
//Also needs to be updated in upload utility and mobile app.
|
||||||
|
|||||||
Reference in New Issue
Block a user