From bd2f22f059ec6c946c532aec09e2386d8b046901 Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Tue, 8 Jun 2021 15:37:24 -0700
Subject: [PATCH 1/8] WIP Deleting
---
...obs-documents-gallery.delete.component.jsx | 109 ++++++++----------
.../vehicles-list/vehicles-list.component.jsx | 12 +-
client/src/graphql/documents.queries.js | 10 +-
server.js | 1 +
server/media/media.js | 41 +++++++
5 files changed, 108 insertions(+), 65 deletions(-)
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 bbde76390..9383cf979 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
@@ -5,9 +5,7 @@ import axios from "axios";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
-import { DELETE_DOCUMENT } from "../../graphql/documents.queries";
-import cleanAxios from "../../utils/CleanAxios";
-import { DetermineFileType } from "../documents-upload/documents-upload.utility";
+import { DELETE_DOCUMENTS } from "../../graphql/documents.queries";
//Context: currentUserEmail, bodyshop, jobid, invoiceid
export default function JobsDocumentsDeleteButton({
@@ -15,73 +13,62 @@ export default function JobsDocumentsDeleteButton({
deletionCallback,
}) {
const { t } = useTranslation();
- const [deleteDocument] = useMutation(DELETE_DOCUMENT);
+ const [deleteDocument] = useMutation(DELETE_DOCUMENTS);
const imagesToDelete = [
...galleryImages.images.filter((image) => image.isSelected),
...galleryImages.other.filter((image) => image.isSelected),
];
const [loading, setLoading] = useState(false);
- const handleDelete = () => {
+ console.log(
+ "π ~ file: jobs-documents-gallery.delete.component.jsx ~ line 29 ~ imagesToDelete",
+ imagesToDelete
+ );
+ const handleDelete = async () => {
logImEXEvent("job_documents_delete", { count: imagesToDelete.length });
setLoading(true);
- imagesToDelete.forEach((image) => {
- let timestamp = Math.floor(Date.now() / 1000);
- let public_id = image.key;
-
- axios
- .post("/media/sign", {
- public_id: public_id,
- timestamp: timestamp,
- })
- .then((response) => {
- var signature = response.data;
- var options = {
- headers: { "X-Requested-With": "XMLHttpRequest" },
- };
- const formData = new FormData();
- formData.append("api_key", process.env.REACT_APP_CLOUDINARY_API_KEY);
- formData.append("public_id", public_id);
- formData.append("timestamp", timestamp);
- formData.append("signature", signature);
-
- cleanAxios
- .post(
- `${
- process.env.REACT_APP_CLOUDINARY_ENDPOINT_API
- }/${DetermineFileType(image.type)}/destroy`,
- formData,
- options
- )
- .then((response) => {
- deleteDocument({ variables: { id: image.id } })
- .then((r) => {
- notification.open({
- key: "docdeletedsuccesfully",
- type: "success",
- message: t("documents.successes.delete"),
- });
-
- if (deletionCallback) deletionCallback();
- })
- .catch((error) => {
- notification["error"]({
- message: t("documents.errors.deleting", {
- message: JSON.stringify(error),
- }),
- });
- });
- //Delete it from our database.
- })
- .catch((error) => {
- notification["error"]({
- message: t("documents.errors.deleting_cloudinary", {
- message: JSON.stringify(error),
- }),
- });
- });
- });
+ const res = await axios.post("/media/delete", {
+ ids: imagesToDelete,
});
+ console.log(
+ "π ~ file: jobs-documents-gallery.delete.component.jsx ~ line 31 ~ res",
+ res
+ );
+ const successfulDeletes = [];
+ Object.keys(res.data.deleted).forEach((key) => {
+ if (res.data.deleted[key] !== "deleted") {
+ notification["error"]({
+ message: t("documents.errors.deleting_cloudinary", {
+ message: JSON.stringify(res.data.deleted[key]),
+ }),
+ });
+ } else {
+ successfulDeletes.push(key);
+ }
+ });
+ const delres = await deleteDocument({
+ variables: {
+ ids: imagesToDelete
+ .filter((i) => successfulDeletes.includes(i.key))
+ .map((i) => i.id),
+ },
+ });
+ if (delres.errors) {
+ notification["error"]({
+ message: t("documents.errors.deleting", {
+ message: JSON.stringify(delres.errors),
+ }),
+ });
+ } else {
+ notification.open({
+ key: "docdeletedsuccesfully",
+ type: "success",
+ message: t("documents.successes.delete"),
+ });
+
+ if (deletionCallback) deletionCallback();
+ }
+
setLoading(false);
};
diff --git a/client/src/components/vehicles-list/vehicles-list.component.jsx b/client/src/components/vehicles-list/vehicles-list.component.jsx
index 294dfb7ea..ca9091e67 100644
--- a/client/src/components/vehicles-list/vehicles-list.component.jsx
+++ b/client/src/components/vehicles-list/vehicles-list.component.jsx
@@ -30,7 +30,9 @@ export default function VehiclesListComponent({
dataIndex: "v_vin",
key: "v_vin",
render: (text, record) => (
- {record.v_vin}
+
+ {record.v_vin || "N/A"}
+
),
},
{
@@ -39,7 +41,9 @@ export default function VehiclesListComponent({
key: "description",
render: (text, record) => {
return (
- {`${record.v_model_yr} ${record.v_make_desc} ${record.v_model_desc} ${record.v_color}`}
+ {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
+ record.v_model_desc || ""
+ } ${record.v_color || ""}`}
);
},
},
@@ -48,7 +52,9 @@ export default function VehiclesListComponent({
dataIndex: "plate",
key: "plate",
render: (text, record) => {
- return {`${record.plate_st} | ${record.plate_no}`};
+ return (
+ {`${record.plate_st || ""} | ${record.plate_no || ""}`}
+ );
},
},
];
diff --git a/client/src/graphql/documents.queries.js b/client/src/graphql/documents.queries.js
index f9cc8877b..b2b754949 100644
--- a/client/src/graphql/documents.queries.js
+++ b/client/src/graphql/documents.queries.js
@@ -84,7 +84,15 @@ export const DELETE_DOCUMENT = gql`
}
}
`;
-
+export const DELETE_DOCUMENTS = gql`
+ mutation DELETE_DOCUMENTS($ids: [uuid!]!) {
+ delete_documents(where: { id: { _in: $ids } }) {
+ returning {
+ id
+ }
+ }
+ }
+`;
export const QUERY_TEMPORARY_DOCS = gql`
query QUERY_TEMPORARY_DOCS {
documents(
diff --git a/server.js b/server.js
index 13f6b818e..8f37796a6 100644
--- a/server.js
+++ b/server.js
@@ -78,6 +78,7 @@ app.post(
);
app.post("/media/download", fb.validateFirebaseIdToken, media.downloadFiles);
app.post("/media/rename", fb.validateFirebaseIdToken, media.renameKeys);
+app.post("/media/delete", fb.validateFirebaseIdToken, media.deleteFiles);
//SMS/Twilio Paths
var smsReceive = require("./server/sms/receive");
diff --git a/server/media/media.js b/server/media/media.js
index e02beeac5..727c040c9 100644
--- a/server/media/media.js
+++ b/server/media/media.js
@@ -1,4 +1,5 @@
const path = require("path");
+const _ = require("lodash");
require("dotenv").config({
path: path.resolve(
process.cwd(),
@@ -22,6 +23,7 @@ exports.createSignedUploadURL = (req, res) => {
exports.downloadFiles = (req, res) => {
const { ids } = req.body;
+
const url = cloudinary.utils.download_zip_url({
public_ids: ids,
flatten_folders: true,
@@ -29,6 +31,45 @@ exports.downloadFiles = (req, res) => {
res.send(url);
};
+exports.deleteFiles = async (req, res) => {
+ const { ids } = req.body;
+ const types = _.groupBy(ids, (x) => DetermineFileType(x.type));
+ console.log("π ~ file: media.js ~ line 28 ~ types", types);
+
+ const returns = [];
+ if (types.image) {
+ //delete images
+
+ returns.push(
+ await cloudinary.api.delete_resources(
+ types.image.map((x) => x.key),
+ { resource_type: "image" }
+ )
+ );
+ }
+ if (types.video) {
+ //delete images returns.push(
+ returns.push(
+ await cloudinary.api.delete_resources(
+ types.video.map((x) => x.key),
+ { resource_type: "video" }
+ )
+ );
+ }
+ if (types.raw) {
+ //delete images returns.push(
+ returns.push(
+ await cloudinary.api.delete_resources(
+ types.raw.map((x) => x.key),
+ { resource_type: "raw" }
+ )
+ );
+ }
+ console.log("π ~ file: media.js ~ line 40 ~ returns", returns);
+
+ res.send(returns);
+};
+
exports.renameKeys = async (req, res) => {
const { documents } = req.body;
//{id: "", from: "", to:""}
From 0fa214f029d7a428dda6a0c8b74e2bdf93b005ed Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Tue, 8 Jun 2021 16:59:16 -0700
Subject: [PATCH 2/8] IO-117 Deleting docuemnts server side.
---
...obs-documents-gallery.delete.component.jsx | 31 ++++++++-----------
client/src/redux/user/user.sagas.js | 1 +
2 files changed, 14 insertions(+), 18 deletions(-)
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 9383cf979..9d3764daf 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
@@ -20,31 +20,26 @@ export default function JobsDocumentsDeleteButton({
];
const [loading, setLoading] = useState(false);
- console.log(
- "π ~ file: jobs-documents-gallery.delete.component.jsx ~ line 29 ~ imagesToDelete",
- imagesToDelete
- );
const handleDelete = async () => {
logImEXEvent("job_documents_delete", { count: imagesToDelete.length });
setLoading(true);
const res = await axios.post("/media/delete", {
ids: imagesToDelete,
});
- console.log(
- "π ~ file: jobs-documents-gallery.delete.component.jsx ~ line 31 ~ res",
- res
- );
+
const successfulDeletes = [];
- Object.keys(res.data.deleted).forEach((key) => {
- if (res.data.deleted[key] !== "deleted") {
- notification["error"]({
- message: t("documents.errors.deleting_cloudinary", {
- message: JSON.stringify(res.data.deleted[key]),
- }),
- });
- } else {
- successfulDeletes.push(key);
- }
+ 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);
+ }
+ });
});
const delres = await deleteDocument({
variables: {
diff --git a/client/src/redux/user/user.sagas.js b/client/src/redux/user/user.sagas.js
index 9a969bee0..8c56e27d3 100644
--- a/client/src/redux/user/user.sagas.js
+++ b/client/src/redux/user/user.sagas.js
@@ -237,6 +237,7 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
);
if (authRecord[0] && authRecord[0].user.validemail) {
+ console.log("$crisp user email", authRecord[0].user.email);
window.$crisp.push(["set", "user:email", [authRecord[0].user.email]]);
}
From c6df38e753e1e7afe12e4109c953f0bd9822ca0c Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Tue, 8 Jun 2021 17:14:47 -0700
Subject: [PATCH 3/8] IO-1059 Auto validation for bill on exported job.
---
client/src/components/bill-form/bill-form.component.jsx | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/client/src/components/bill-form/bill-form.component.jsx b/client/src/components/bill-form/bill-form.component.jsx
index f74046c61..b686da87e 100644
--- a/client/src/components/bill-form/bill-form.component.jsx
+++ b/client/src/components/bill-form/bill-form.component.jsx
@@ -51,6 +51,11 @@ export function BillFormComponent({
setDiscount(opt.discount);
};
+ useEffect(() => {
+ if (job) form.validateFields(["is_credit_memo"]);
+ }, [job, form]);
+ console.log("π ~ file: bill-form.component.jsx ~ line 57 ~ job", job);
+
useEffect(() => {
if (form.getFieldValue("vendorid") && vendorAutoCompleteOptions) {
const vendorId = form.getFieldValue("vendorid");
@@ -188,10 +193,10 @@ export function BillFormComponent({
label={t("bills.fields.is_credit_memo")}
name="is_credit_memo"
valuePropName="checked"
- dependencies={["jobid"]}
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
+ console.log("VALIDATOR FIRED");
if (
(job.status === bodyshop.md_ro_statuses.default_invoiced ||
job.status === bodyshop.md_ro_statuses.default_exported ||
From a45bf6d9590d5fb3f73a99d1a40050c8b02cd257 Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Tue, 8 Jun 2021 17:17:20 -0700
Subject: [PATCH 4/8] IO-541 Payments table allow for closed
---
client/src/components/job-payments/job-payments.component.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/src/components/job-payments/job-payments.component.jsx b/client/src/components/job-payments/job-payments.component.jsx
index 0e1a81d2b..50a12d6db 100644
--- a/client/src/components/job-payments/job-payments.component.jsx
+++ b/client/src/components/job-payments/job-payments.component.jsx
@@ -155,7 +155,7 @@ export function JobPayments({
extra={
- {t("general.actions.edit")}
+
- {t("general.actions.edit")}
+
)}
{!techConsole && (
@@ -216,7 +217,7 @@ export function TimeTicketList({
: !record.jobid
}
>
- {t("general.actions.edit")}
+
)}
From 41c30fe704a6a956b931a90b4ebd1197d8327806 Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Wed, 9 Jun 2021 10:52:33 -0700
Subject: [PATCH 7/8] IO-1117 IO-1087
---
.../job-3rd-party-modal.component.jsx | 4 +---
.../print-center-jobs.component.jsx | 14 ++++++++------
.../shop-info/shop-info.general.component.jsx | 1 -
client/src/translations/en_us/common.json | 2 +-
4 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx b/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx
index 5803c0c6f..f597d156e 100644
--- a/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx
+++ b/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx
@@ -87,9 +87,7 @@ export function Jobd3RdPartyModal({ bodyshop, jobId }) {
return (
<>
-
- {t("printcenter.jobs.3rdpartypayer")}
-
+ {t("printcenter.jobs.3rdpartypayer")}