Added separate documents display for non-images. BOD-420
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<babeledit_project be_version="2.7.1" version="1.2">
|
||||
<babeledit_project version="1.2" be_version="2.7.1">
|
||||
<!--
|
||||
|
||||
BabelEdit project file
|
||||
@@ -8470,6 +8470,48 @@
|
||||
<folder_node>
|
||||
<name>labels</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>confirmdelete</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>
|
||||
<name>doctype</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>
|
||||
<name>upload</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -16624,6 +16666,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>documents-images</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>
|
||||
<name>documents-other</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>
|
||||
<name>duplicateconfirm</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -35,7 +35,7 @@ export function DocumentsUploadComponent({
|
||||
callback: callbackAfterUpload,
|
||||
})
|
||||
}
|
||||
accept="audio/*,video/*,image/*"
|
||||
accept="audio/*, video/*, image/*, .pdf, .doc, .docx, .xls, .xlsx"
|
||||
showUploadList={false}
|
||||
>
|
||||
<Button type="primary">
|
||||
|
||||
@@ -17,9 +17,7 @@ export const handleUpload = (ev, context) => {
|
||||
|
||||
const { onError, onSuccess, onProgress } = ev;
|
||||
const { bodyshop, jobId } = context;
|
||||
//If PDF, upload directly.
|
||||
//If JPEG, resize and upload.
|
||||
//TODO If this is just an invoice job? Where to put it?
|
||||
|
||||
let key = `${bodyshop.id}/${jobId}/${ev.file.name.replace(/\.[^/.]+$/, "")}`;
|
||||
uploadToCloudinary(
|
||||
key,
|
||||
@@ -30,41 +28,6 @@ export const handleUpload = (ev, context) => {
|
||||
onProgress,
|
||||
context
|
||||
);
|
||||
|
||||
// if (ev.file.type.includes("image")) {
|
||||
// Resizer.imageFileResizer(
|
||||
// ev.file,
|
||||
// 2500,
|
||||
// 2500,
|
||||
// "JPEG",
|
||||
// 75,
|
||||
// 0,
|
||||
// (uri) => {
|
||||
// let file = new File([uri], ev.file.name, {});
|
||||
// file.uid = ev.file.uid;
|
||||
// uploadToCloudinary(
|
||||
// key,
|
||||
// file.type,
|
||||
// file,
|
||||
// onError,
|
||||
// onSuccess,
|
||||
// onProgress,
|
||||
// context
|
||||
// );
|
||||
// },
|
||||
// "blob"
|
||||
// );
|
||||
// } else {
|
||||
// uploadToCloudinary(
|
||||
// key,
|
||||
// ev.file.type,
|
||||
// ev.file,
|
||||
// onError,
|
||||
// onSuccess,
|
||||
// onProgress,
|
||||
// context
|
||||
// );
|
||||
// }
|
||||
};
|
||||
|
||||
export const uploadToCloudinary = async (
|
||||
@@ -84,12 +47,11 @@ export const uploadToCloudinary = async (
|
||||
let tags = `${bodyshop.textid},${
|
||||
tagsArray ? tagsArray.map((tag) => `${tag},`) : ""
|
||||
}`;
|
||||
let eager = process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS;
|
||||
// let eager = process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS;
|
||||
|
||||
//Get the signed url.
|
||||
|
||||
const signedURLResponse = await axios.post("/media/sign", {
|
||||
// eager: eager,
|
||||
public_id: public_id,
|
||||
tags: tags,
|
||||
timestamp: timestamp,
|
||||
@@ -117,15 +79,9 @@ export const uploadToCloudinary = async (
|
||||
};
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
//formData.append("eager", eager);
|
||||
// if (fileType.includes("image")) {
|
||||
console.log("Applying lower quality transforms.");
|
||||
formData.append("upload_preset", "incoming_upload");
|
||||
// formData.append("quality", "auto");
|
||||
// formData.append("width", "500");
|
||||
// formData.append("height", "500");
|
||||
// formData.append("crop", "limit");
|
||||
// }
|
||||
|
||||
formData.append("api_key", process.env.REACT_APP_CLOUDINARY_API_KEY);
|
||||
formData.append("public_id", public_id);
|
||||
formData.append("tags", tags);
|
||||
|
||||
@@ -6,7 +6,10 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
|
||||
export default function JobsDocumentsDownloadButton({ galleryImages }) {
|
||||
const { t } = useTranslation();
|
||||
const imagesToDownload = galleryImages.filter((image) => image.isSelected);
|
||||
const imagesToDownload = [
|
||||
...galleryImages.images.filter((image) => image.isSelected),
|
||||
...galleryImages.other.filter((image) => image.isSelected),
|
||||
];
|
||||
const handleDownload = () => {
|
||||
logImEXEvent("jobs_documents_download");
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Space } from "antd";
|
||||
import { Collapse, Space } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Gallery from "react-grid-gallery";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DocumentsUploadComponent from "../documents-upload/documents-upload.component";
|
||||
import JobsDocumentsDownloadButton from "./jobs-document-gallery.download.component";
|
||||
import JobsDocumentsDeleteButton from "./jobs-documents-gallery.delete.component";
|
||||
@@ -12,27 +13,55 @@ function JobsDocumentsComponent({
|
||||
billId,
|
||||
billsCallback,
|
||||
}) {
|
||||
const [galleryImages, setgalleryImages] = useState([]);
|
||||
|
||||
const [galleryImages, setgalleryImages] = useState({ images: [], other: [] });
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
setgalleryImages(
|
||||
data.reduce((acc, value) => {
|
||||
acc.push({
|
||||
src: `${process.env.REACT_APP_CLOUDINARY_IMAGE_ENDPOINT}/${value.key}`,
|
||||
thumbnail: `${process.env.REACT_APP_CLOUDINARY_IMAGE_ENDPOINT}/${process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS}/${value.key}`,
|
||||
tags: value.type.includes("pdf")
|
||||
? [{ value: "PDF", title: "PDF" }]
|
||||
: [],
|
||||
thumbnailHeight: 200,
|
||||
thumbnailWidth: 200,
|
||||
isSelected: false,
|
||||
key: value.key,
|
||||
id: value.id,
|
||||
});
|
||||
let documents = data.reduce(
|
||||
(acc, value) => {
|
||||
if (value.type.includes("image")) {
|
||||
acc.images.push({
|
||||
src: `${process.env.REACT_APP_CLOUDINARY_IMAGE_ENDPOINT}/${value.key}`,
|
||||
thumbnail: `${process.env.REACT_APP_CLOUDINARY_IMAGE_ENDPOINT}/${process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS}/${value.key}`,
|
||||
thumbnailHeight: 225,
|
||||
thumbnailWidth: 225,
|
||||
isSelected: false,
|
||||
key: value.key,
|
||||
id: value.id,
|
||||
});
|
||||
} else {
|
||||
acc.other.push({
|
||||
src: `${process.env.REACT_APP_CLOUDINARY_IMAGE_ENDPOINT}/${value.key}`,
|
||||
thumbnail: `${process.env.REACT_APP_CLOUDINARY_IMAGE_ENDPOINT}/${process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS}/${value.key}`,
|
||||
tags: [
|
||||
{ value: "PDF", title: t("documents.labels.doctype") },
|
||||
...(value.bill
|
||||
? [
|
||||
{
|
||||
value: value.bill.vendor.name,
|
||||
title: t("vendors.fields.name"),
|
||||
},
|
||||
{ value: value.bill.date, title: t("bills.fields.date") },
|
||||
{
|
||||
value: value.bill.invoice_number,
|
||||
title: t("bills.fields.invoice_number"),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
thumbnailHeight: 225,
|
||||
thumbnailWidth: 225,
|
||||
isSelected: false,
|
||||
key: value.key,
|
||||
id: value.id,
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, [])
|
||||
},
|
||||
{ images: [], other: [] }
|
||||
);
|
||||
}, [data, setgalleryImages]);
|
||||
setgalleryImages(documents);
|
||||
}, [data, setgalleryImages, t]);
|
||||
|
||||
return (
|
||||
<div className="clearfix">
|
||||
@@ -49,17 +78,55 @@ function JobsDocumentsComponent({
|
||||
deletionCallback={billsCallback || refetch}
|
||||
/>
|
||||
</Space>
|
||||
|
||||
<Gallery
|
||||
images={galleryImages}
|
||||
onSelectImage={(index, image) => {
|
||||
setgalleryImages(
|
||||
galleryImages.map((g, idx) =>
|
||||
index === idx ? { ...g, isSelected: !g.isSelected } : g
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Collapse
|
||||
style={{ marginTop: "2rem" }}
|
||||
defaultActiveKey={["images", "other"]}
|
||||
bordered="false"
|
||||
>
|
||||
<Collapse.Panel key="images" header={t("jobs.labels.documents-images")}>
|
||||
<Gallery
|
||||
images={galleryImages.images}
|
||||
backdropClosesModal={true}
|
||||
onClickImage={(props) => {
|
||||
window.open(
|
||||
props.target.src,
|
||||
"_blank",
|
||||
"toolbar=0,location=0,menubar=0"
|
||||
);
|
||||
}}
|
||||
onSelectImage={(index, image) => {
|
||||
setgalleryImages({
|
||||
...galleryImages,
|
||||
images: galleryImages.images.map((g, idx) =>
|
||||
index === idx ? { ...g, isSelected: !g.isSelected } : g
|
||||
),
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel key="other" header={t("jobs.labels.documents-other")}>
|
||||
<Gallery
|
||||
images={galleryImages.other}
|
||||
backdropClosesModal={true}
|
||||
enableLightbox={false}
|
||||
onClickThumbnail={(index) => {
|
||||
window.open(
|
||||
galleryImages.other[index].src,
|
||||
"_blank",
|
||||
"toolbar=0,location=0,menubar=0"
|
||||
);
|
||||
}}
|
||||
onSelectImage={(index) => {
|
||||
setgalleryImages({
|
||||
...galleryImages,
|
||||
other: galleryImages.other.map((g, idx) =>
|
||||
index === idx ? { ...g, isSelected: !g.isSelected } : g
|
||||
),
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Button, notification } from "antd";
|
||||
import { Button, notification, Popconfirm } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import axios from "axios";
|
||||
import { useMutation } from "@apollo/react-hooks";
|
||||
import { DELETE_DOCUMENT } from "../../graphql/documents.queries";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { QuestionCircleOutlined } from "@ant-design/icons";
|
||||
|
||||
import { axiosAuthInterceptorId } from "../../App/App.container";
|
||||
//Context: currentUserEmail, bodyshop, jobid, invoiceid
|
||||
@@ -18,7 +19,10 @@ export default function JobsDocumentsDeleteButton({
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [deleteDocument] = useMutation(DELETE_DOCUMENT);
|
||||
const imagesToDelete = galleryImages.filter((image) => image.isSelected);
|
||||
const imagesToDelete = [
|
||||
...galleryImages.images.filter((image) => image.isSelected),
|
||||
...galleryImages.other.filter((image) => image.isSelected),
|
||||
];
|
||||
const [loading, setLoading] = useState(false);
|
||||
const handleDelete = () => {
|
||||
logImEXEvent("job_documents_delete", { count: imagesToDelete.length });
|
||||
@@ -76,12 +80,18 @@ export default function JobsDocumentsDeleteButton({
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
<Popconfirm
|
||||
disabled={imagesToDelete.length < 1}
|
||||
loading={loading}
|
||||
onClick={handleDelete}
|
||||
icon={<QuestionCircleOutlined style={{ color: "red" }} />}
|
||||
onConfirm={handleDelete}
|
||||
title={t("documents.labels.confirmdelete")}
|
||||
okText={t("general.actions.delete")}
|
||||
okButtonProps={{ type: "danger" }}
|
||||
cancelText={t("general.actions.cancel")}
|
||||
>
|
||||
{t("documents.actions.delete")}
|
||||
</Button>
|
||||
<Button disabled={imagesToDelete.length < 1} loading={loading}>
|
||||
{t("documents.actions.delete")}
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,23 @@ import gql from "graphql-tag";
|
||||
|
||||
export const GET_DOCUMENTS_BY_JOB = gql`
|
||||
query GET_DOCUMENTS_BY_JOB($jobId: uuid!) {
|
||||
documents(where: { jobid: { _eq: $jobId } }) {
|
||||
documents(
|
||||
where: { jobid: { _eq: $jobId } }
|
||||
order_by: { updated_at: desc }
|
||||
) {
|
||||
id
|
||||
name
|
||||
key
|
||||
type
|
||||
bill {
|
||||
id
|
||||
invoice_number
|
||||
date
|
||||
vendor {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -561,6 +561,8 @@
|
||||
"nodocuments": "There are no documents."
|
||||
},
|
||||
"labels": {
|
||||
"confirmdelete": "Are you sure you want to delete these documents. This CANNOT be undone.",
|
||||
"doctype": "Document Type",
|
||||
"upload": "Upload"
|
||||
},
|
||||
"successes": {
|
||||
@@ -1018,6 +1020,8 @@
|
||||
},
|
||||
"difference": "Difference",
|
||||
"documents": "Documents",
|
||||
"documents-images": "Images",
|
||||
"documents-other": "Other Documents",
|
||||
"duplicateconfirm": "Are you sure you want to duplicate this job? Some elements of this job will not be duplicated.",
|
||||
"employeeassignments": "Employee Assignments",
|
||||
"existing_jobs": "Existing Jobs",
|
||||
|
||||
@@ -561,6 +561,8 @@
|
||||
"nodocuments": "No hay documentos"
|
||||
},
|
||||
"labels": {
|
||||
"confirmdelete": "",
|
||||
"doctype": "",
|
||||
"upload": "Subir"
|
||||
},
|
||||
"successes": {
|
||||
@@ -1018,6 +1020,8 @@
|
||||
},
|
||||
"difference": "",
|
||||
"documents": "documentos",
|
||||
"documents-images": "",
|
||||
"documents-other": "",
|
||||
"duplicateconfirm": "",
|
||||
"employeeassignments": "",
|
||||
"existing_jobs": "Empleos existentes",
|
||||
|
||||
@@ -561,6 +561,8 @@
|
||||
"nodocuments": "Il n'y a pas de documents."
|
||||
},
|
||||
"labels": {
|
||||
"confirmdelete": "",
|
||||
"doctype": "",
|
||||
"upload": "Télécharger"
|
||||
},
|
||||
"successes": {
|
||||
@@ -1018,6 +1020,8 @@
|
||||
},
|
||||
"difference": "",
|
||||
"documents": "Les documents",
|
||||
"documents-images": "",
|
||||
"documents-other": "",
|
||||
"duplicateconfirm": "",
|
||||
"employeeassignments": "",
|
||||
"existing_jobs": "Emplois existants",
|
||||
|
||||
Reference in New Issue
Block a user