Services_Bugs :- UI changes of Purchase Modal. #523

Merged
pramod.mahajan merged 21 commits from Services_Bugs into Purchase_Invoice_Management 2025-11-28 06:42:44 +00:00
4 changed files with 149 additions and 60 deletions
Showing only changes of commit a1a8dd4447 - Show all commits

View File

@ -72,7 +72,7 @@ export const FilelistView = ({ files, viewFile }) => {
e.preventDefault(); e.preventDefault();
viewFile({ viewFile({
IsOpen: true, IsOpen: true,
Image: file.preSignedUrl, Image: files,
}); });
}} }}
> >

View File

@ -1,80 +1,169 @@
import { useState } from "react"; import { useState, useRef, useEffect } from "react";
const PreviewDocument = ({ files = [] }) => {
const images = Array.isArray(files) ? files : [files];
const PreviewDocument = ({ imageUrl }) => { const [index, setIndex] = useState(0);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [rotation, setRotation] = useState(0); const [rotation, setRotation] = useState(0);
const [scale, setScale] = useState(1); const [scale, setScale] = useState(1);
const [position, setPosition] = useState({ x: 0, y: 0 });
const [dragging, setDragging] = useState(false);
const startPos = useRef({ x: 0, y: 0 });
const MIN_ZOOM = 0.4;
const MAX_ZOOM = 3;
const currentImage = images[index];
// Reset on image change
useEffect(() => {
setRotation(0);
setScale(1);
setPosition({ x: 0, y: 0 });
setLoading(true);
}, [index]);
const zoomIn = () => setScale((prev) => Math.min(prev + 0.2, MAX_ZOOM));
const zoomOut = () => setScale((prev) => Math.max(prev - 0.2, MIN_ZOOM));
const zoomIn = () => setScale((prev) => Math.min(prev + 0.2, 3));
const zoomOut = () => setScale((prev) => Math.max(prev - 0.2, 0.4));
const resetAll = () => { const resetAll = () => {
setRotation(0); setRotation(0);
setScale(1); setScale(1);
setPosition({ x: 0, y: 0 });
}; };
const nextImage = () => {
if (index < images.length - 1) setIndex((i) => i + 1);
};
const prevImage = () => {
if (index > 0) setIndex((i) => i - 1);
};
const handleWheel = (e) => {
e.preventDefault();
if (e.ctrlKey) {
const delta = e.deltaY > 0 ? -0.1 : 0.1;
setScale((prev) => {
let next = prev + delta;
if (next < MIN_ZOOM) next = MIN_ZOOM;
if (next > MAX_ZOOM) next = MAX_ZOOM;
return next;
});
} else {
if (e.deltaY > 0) nextImage();
else prevImage();
}
};
const handleMouseDown = (e) => {
setDragging(true);
startPos.current = {
x: e.clientX - position.x,
y: e.clientY - position.y,
};
};
const handleMouseMove = (e) => {
if (!dragging) return;
setPosition({
x: e.clientX - startPos.current.x,
y: e.clientY - startPos.current.y,
});
};
const handleMouseUp = () => setDragging(false);
const handleDoubleClick = () => resetAll();
return ( return (
<> <>
<div className="d-flex justify-content-start gap-3 mb-2"> {/* Top Controls */}
<div className="d-flex justify-content-start align-items-center mb-2">
{/* Left */}
<div className="d-flex gap-3">
<i <i
className="bx bx-rotate-right cursor-pointer fs-4" className="bx bx-rotate-right cursor-pointer fs-4"
title="Rotate"
onClick={() => setRotation((prev) => prev + 90)} onClick={() => setRotation((prev) => prev + 90)}
></i> title="Rotate"
/>
<i <i
className="bx bx-zoom-in cursor-pointer fs-4" className="bx bx-zoom-in cursor-pointer fs-4"
title="Zoom In"
onClick={zoomIn} onClick={zoomIn}
></i> title="Zoom In"
/>
<i <i
className="bx bx-zoom-out cursor-pointer fs-4" className="bx bx-zoom-out cursor-pointer fs-4"
title="Zoom Out"
onClick={zoomOut} onClick={zoomOut}
></i> title="Zoom Out"
/>
<i
className="bx bx-reset cursor-pointer fs-4"
onClick={resetAll}
title="Reset"
/>
</div>
</div> </div>
<div <div
className="position-relative d-flex flex-column justify-content-center align-items-center overflow-hidden" onWheel={handleWheel}
style={{ minHeight: "80vh" }} onMouseDown={handleMouseDown}
> onMouseMove={handleMouseMove}
{loading && ( onMouseUp={handleMouseUp}
<div className="text-secondary text-center mb-2"> onMouseLeave={handleMouseUp}
Loading... onDoubleClick={handleDoubleClick}
</div> className="position-relative d-flex justify-content-center align-items-center bg-light-secondary overflow-hidden"
)}
<div className="mb-3 d-flex justify-content-center align-items-center">
<img
src={imageUrl}
alt="Full View"
className="img-fluid"
style={{ style={{
maxHeight: "80vh", minHeight: "70vh",
objectFit: "contain", userSelect: "none",
borderRadius: "10px",
}}
>
{loading && <div className="text-secondary">Loading...</div>}
<img
src={currentImage?.preSignedUrl}
alt="Preview"
draggable="false"
style={{
maxHeight: "60vh",
display: loading ? "none" : "block", display: loading ? "none" : "block",
transform: `rotate(${rotation}deg) scale(${scale})`, transform: `
transition: "transform 0.3s ease", translate(${position.x}px, ${position.y}px)
cursor: "grab", scale(${scale})
rotate(${rotation}deg)
`,
transition: dragging ? "none" : "transform 0.2s ease",
cursor: dragging ? "grabbing" : "grab",
}} }}
onLoad={() => setLoading(false)} onLoad={() => setLoading(false)}
/> />
</div> </div>
<div className="position-absolute bottom-0 start-0 m-2"> <div className="d-flex justify-content-between">
<button <div className="text-center text-muted mt-2 small">
className="btn btn-outline-secondary" Scroll = change image | Double click = reset
onClick={resetAll} </div>
> <div className="d-flex align-items-center gap-2">
<i className="bx bx-reset"></i> Reset <i
</button> className="bx bx-chevron-left cursor-pointer fs-4"
onClick={prevImage}
/>
<span>
{index + 1} / {images.length}
</span>
<i
className="bx bx-chevron-right cursor-pointer fs-4"
onClick={nextImage}
/>
</div> </div>
</div> </div>
</> </>
); );
}; };
export default PreviewDocument; export default PreviewDocument;

View File

@ -393,7 +393,7 @@ const tdsPercentage = Number(watch("tdsPercentage")) || 0;
if (isImage) { if (isImage) {
setDocumentView({ setDocumentView({
IsOpen: true, IsOpen: true,
Image: doc.preSignedUrl, Images: data?.documents,
}); });
} }
}} }}

View File

@ -62,7 +62,7 @@ const ExpensePage = () => {
const [ViewDocument, setDocumentView] = useState({ const [ViewDocument, setDocumentView] = useState({
IsOpen: false, IsOpen: false,
Image: null, Images: null,
}); });
const IsCreatedAble = useHasUserPermission(CREATE_EXEPENSE); const IsCreatedAble = useHasUserPermission(CREATE_EXEPENSE);
@ -208,10 +208,10 @@ const ExpensePage = () => {
<GlobalModel <GlobalModel
isOpen isOpen
size="md" size="md"
key={ViewDocument.Image ?? "doc"} key={ViewDocument.Images ?? "doc"}
closeModal={() => setDocumentView({ IsOpen: false, Image: null })} closeModal={() => setDocumentView({ IsOpen: false, Images: null })}
> >
<PreviewDocument imageUrl={ViewDocument.Image} /> <PreviewDocument files={ViewDocument.Images} />
</GlobalModel> </GlobalModel>
)} )}
</div> </div>