diff --git a/client/package.json b/client/package.json index 58a493a79..85b34916e 100644 --- a/client/package.json +++ b/client/package.json @@ -46,6 +46,7 @@ "react-grid-layout": "^1.3.4", "react-i18next": "^12.1.1", "react-icons": "^4.7.1", + "react-image-lightbox": "^5.1.4", "react-number-format": "^5.1.2", "react-redux": "^8.0.5", "react-resizable": "^3.0.4", 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 dbd04dddc..925f5f520 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 @@ -1,7 +1,7 @@ import { EditFilled, FileExcelFilled, SyncOutlined } from "@ant-design/icons"; import { Button, Card, Col, Row, Space } from "antd"; import React, { useEffect, useState } from "react"; -import Gallery from "react-grid-gallery"; +import { Gallery } from "react-grid-gallery"; import { useTranslation } from "react-i18next"; import DocumentsUploadComponent from "../documents-upload/documents-upload.component"; import { DetermineFileType } from "../documents-upload/documents-upload.utility"; @@ -10,7 +10,8 @@ import JobsDocumentsDownloadButton from "./jobs-document-gallery.download.compon import JobsDocumentsGalleryReassign from "./jobs-document-gallery.reassign.component"; import JobsDocumentsDeleteButton from "./jobs-documents-gallery.delete.component"; import JobsDocumentsGallerySelectAllComponent from "./jobs-documents-gallery.selectall.component"; - +import Lightbox from "react-image-lightbox"; +import "react-image-lightbox/style.css"; function JobsDocumentsComponent({ data, jobId, @@ -23,11 +24,7 @@ function JobsDocumentsComponent({ }) { const [galleryImages, setgalleryImages] = useState({ images: [], other: [] }); const { t } = useTranslation(); - const [index, setIndex] = useState(0); - - const onCurrentImageChange = (index) => { - setIndex(index); - }; + const [modalState, setModalState] = useState({ open: false, index: 0 }); useEffect(() => { let documents = data.reduce( @@ -35,10 +32,12 @@ function JobsDocumentsComponent({ const fileType = DetermineFileType(value.type); if (value.type.startsWith("image")) { acc.images.push({ - src: GenerateSrcUrl(value), - thumbnail: GenerateThumbUrl(value), - thumbnailHeight: 225, - thumbnailWidth: 225, + // src: GenerateSrcUrl(value), + // thumbnail: GenerateThumbUrl(value), + fullsize: GenerateSrcUrl(value), + src: GenerateThumbUrl(value), + height: 225, + width: 225, isSelected: false, key: value.key, extension: value.extension, @@ -62,8 +61,8 @@ function JobsDocumentsComponent({ const fileName = value.key.split("/").pop(); acc.other.push({ source: GenerateSrcUrl(value), - src: "", - thumbnail: thumb, + //src: "", + src: thumb, tags: [ { value: fileName, @@ -85,8 +84,8 @@ function JobsDocumentsComponent({ ] : []), ], - thumbnailHeight: 225, - thumbnailWidth: 225, + height: 225, + width: 225, isSelected: false, extension: value.extension, @@ -148,35 +147,15 @@ function JobsDocumentsComponent({ { - const newWindow = window.open( - `${window.location.protocol}//${window.location.host}/edit?documentId=${galleryImages.images[index].id}`, - "_blank", - "noopener,noreferrer" - ); - if (newWindow) newWindow.opener = null; - }} - > - - , - ]} - onClickImage={(props) => { - window.open( - props.target.src, - "_blank", - "toolbar=0,location=0,menubar=0" - ); + onClick={(index, item) => { + setModalState({ open: true, index: index }); + // window.open( + // item.fullsize, + // "_blank", + // "toolbar=0,location=0,menubar=0" + // ); }} - onSelectImage={(index, image) => { + onSelect={(index, image) => { setgalleryImages({ ...galleryImages, images: galleryImages.images.map((g, idx) => @@ -191,8 +170,6 @@ function JobsDocumentsComponent({ { return { backgroundImage: , @@ -201,14 +178,14 @@ function JobsDocumentsComponent({ cursor: "pointer", }; }} - onClickThumbnail={(index) => { + onClick={(index) => { window.open( galleryImages.other[index].source, "_blank", "toolbar=0,location=0,menubar=0" ); }} - onSelectImage={(index) => { + onSelect={(index) => { setgalleryImages({ ...galleryImages, other: galleryImages.other.map((g, idx) => @@ -220,6 +197,53 @@ function JobsDocumentsComponent({ + {modalState.open && ( + { + const newWindow = window.open( + `${window.location.protocol}//${ + window.location.host + }/edit?documentId=${ + galleryImages.images[modalState.index].id + }`, + "_blank", + "noopener,noreferrer" + ); + if (newWindow) newWindow.opener = null; + }} + />, + ]} + mainSrc={galleryImages.images[modalState.index].fullsize} + nextSrc={ + galleryImages.images[ + (modalState.index + 1) % galleryImages.images.length + ].fullsize + } + prevSrc={ + galleryImages.images[ + (modalState.index + galleryImages.images.length - 1) % + galleryImages.images.length + ].fullsize + } + onCloseRequest={() => setModalState({ open: false, index: 0 })} + onMovePrevRequest={() => + setModalState({ + ...modalState, + index: + (modalState.index + galleryImages.images.length - 1) % + galleryImages.images.length, + }) + } + onMoveNextRequest={() => + setModalState({ + ...modalState, + index: (modalState.index + 1) % galleryImages.images.length, + }) + } + /> + )} ); } diff --git a/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.container.jsx b/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.container.jsx index 5370971d9..ff326043b 100644 --- a/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.container.jsx +++ b/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.container.jsx @@ -1,7 +1,7 @@ import { SyncOutlined, FileExcelFilled } from "@ant-design/icons"; import { Alert, Button, Card, Space } from "antd"; -import React, { useEffect } from "react"; -import Gallery from "react-grid-gallery"; +import React, { useEffect, useState } from "react"; +import { Gallery } from "react-grid-gallery"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -18,6 +18,8 @@ import JobsDocumentsLocalDeleteButton from "./jobs-documents-local-gallery.delet import JobsLocalGalleryDownloadButton from "./jobs-documents-local-gallery.download"; import JobsDocumentsLocalGalleryReassign from "./jobs-documents-local-gallery.reassign.component"; import JobsDocumentsLocalGallerySelectAllComponent from "./jobs-documents-local-gallery.selectall.component"; +import Lightbox from "react-image-lightbox"; +import "react-image-lightbox/style.css"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -49,6 +51,7 @@ export function JobsDocumentsLocalGallery({ vendorid, }) { const { t } = useTranslation(); + const [modalState, setModalState] = useState({ open: false, index: 0 }); useEffect(() => { if (job) { if (invoice_number) { @@ -58,6 +61,7 @@ export function JobsDocumentsLocalGallery({ } } }, [job, invoice_number, getJobMedia, getBillMedia]); + let optimized; const jobMedia = allMedia && allMedia[job.id] @@ -70,12 +74,20 @@ export function JobsDocumentsLocalGallery({ ) { acc.images.push({ ...val, + fullsize: val.src, + src: val.thumbnail, + height: val.thumbnailHeight, + width: val.thumbnailWidth, ...(val.optimized && { src: val.optimized, fullsize: val.src }), }); if (val.optimized) optimized = true; } else { acc.other.push({ ...val, + fullsize: val.src, + src: val.thumbnail, + height: val.thumbnailHeight, + width: val.thumbnailWidth, tags: [{ value: val.filename, title: val.filename }], }); } @@ -120,8 +132,7 @@ export function JobsDocumentsLocalGallery({ { + onSelect={(index, image) => { toggleMediaSelected({ jobid: job.id, filename: image.filename }); }} {...(optimized && { @@ -133,24 +144,23 @@ export function JobsDocumentsLocalGallery({ />, ], })} - onClickImage={(props) => { - const media = allMedia[job.id].find( - (m) => m.optimized === props.target.src - ); + onClick={(index, item) => { + setModalState({ open: true, index: index }); + // const media = allMedia[job.id].find( + // (m) => m.optimized === item.src + // ); - window.open( - media ? media.src : props.target.src, - "_blank", - "toolbar=0,location=0,menubar=0" - ); + // window.open( + // media ? media.fullsize : item.fullsize, + // "_blank", + // "toolbar=0,location=0,menubar=0" + // ); }} /> { return { backgroundImage: , @@ -159,18 +169,48 @@ export function JobsDocumentsLocalGallery({ cursor: "pointer", }; }} - onClickThumbnail={(index) => { + onClick={(index) => { window.open( - jobMedia.other[index].src, + jobMedia.other[index].fullsize, "_blank", "toolbar=0,location=0,menubar=0" ); }} - onSelectImage={(index, image) => { + onSelect={(index, image) => { toggleMediaSelected({ jobid: job.id, filename: image.filename }); }} /> + {modalState.open && ( + setModalState({ open: false, index: 0 })} + onMovePrevRequest={() => + setModalState({ + ...modalState, + index: + (modalState.index + jobMedia.images.length - 1) % + jobMedia.images.length, + }) + } + onMoveNextRequest={() => + setModalState({ + ...modalState, + index: (modalState.index + 1) % jobMedia.images.length, + }) + } + /> + )} ); } diff --git a/client/yarn.lock b/client/yarn.lock index 64a747502..91a7f7e0b 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -5888,6 +5888,11 @@ executable@^4.1.1: dependencies: pify "^2.2.0" +exenv@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" + integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw== + exifr@^7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/exifr/-/exifr-7.1.3.tgz#f6218012c36dbb7d843222011b27f065fddbab6f" @@ -10317,6 +10322,14 @@ react-icons@^4.7.1: resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.7.1.tgz#0f4b25a5694e6972677cb189d2a72eabea7a8345" integrity sha512-yHd3oKGMgm7zxo3EA7H2n7vxSoiGmHk5t6Ou4bXsfcgWyhfDKMpyKfhHR6Bjnn63c+YXBLBPUql9H4wPJM6sXw== +react-image-lightbox@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/react-image-lightbox/-/react-image-lightbox-5.1.4.tgz#5b847dcb79e9efdf9d7cd5621a92e0f156d2cf30" + integrity sha512-kTiAODz091bgT7SlWNHab0LSMZAPJtlNWDGKv7pLlLY1krmf7FuG1zxE0wyPpeA8gPdwfr3cu6sPwZRqWsc3Eg== + dependencies: + prop-types "^15.7.2" + react-modal "^3.11.1" + react-is@^16.10.2, react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -10332,11 +10345,21 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react-lifecycles-compat@^3.0.4: +react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== +react-modal@^3.11.1: + version "3.16.1" + resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.16.1.tgz#34018528fc206561b1a5467fc3beeaddafb39b2b" + integrity sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg== + dependencies: + exenv "^1.2.0" + prop-types "^15.7.2" + react-lifecycles-compat "^3.0.0" + warning "^4.0.3" + react-number-format@^5.1.2: version "5.1.3" resolved "https://registry.yarnpkg.com/react-number-format/-/react-number-format-5.1.3.tgz#5534f5141cea29e0fe889f76c73dfad11ddf2e17"