import axios from "axios"; import { Result } from "antd"; import * as markerjs2 from "markerjs2"; import { useCallback, useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors"; import { handleUpload } from "../documents-local-upload/documents-local-upload.utility"; import LoadingSpinner from "../loading-spinner/loading-spinner.component"; import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; const mapStateToProps = createStructuredSelector({ currentUser: selectCurrentUser, bodyshop: selectBodyshop }); const mapDispatchToProps = () => ({}); export function DocumentEditorLocalComponent({ imageUrl, filename, jobid }) { const imgRef = useRef(null); const [loading, setLoading] = useState(false); const [uploaded, setuploaded] = useState(false); const [loadedImageUrl, setLoadedImageUrl] = useState(null); const [imageLoaded, setImageLoaded] = useState(false); const [imageLoading, setImageLoading] = useState(true); const markerArea = useRef(null); const { t } = useTranslation(); const notification = useNotification(); const [uploading, setUploading] = useState(false); const triggerUpload = useCallback( async (dataUrl) => { if (uploading) return; setUploading(true); const blob = await b64toBlob(dataUrl); const nameWithoutExt = filename.split(".").slice(0, -1).join(".").trim(); const parts = nameWithoutExt.split("-"); const baseParts = []; for (let i = 0; i < parts.length; i++) { if (/^\d+$/.test(parts[i])) { break; } baseParts.push(parts[i]); } const adjustedBase = baseParts.length > 0 ? baseParts.join("-") : "edited"; const adjustedFilename = `${adjustedBase}.jpg`; const file = new File([blob], adjustedFilename, { type: "image/jpeg" }); handleUpload({ ev: { file: file, filename: adjustedFilename, onSuccess: () => { setUploading(false); setLoading(false); setuploaded(true); }, onError: () => { setUploading(false); setLoading(false); } }, context: { jobid: jobid, callback: () => {} // Optional callback }, notification }); }, [filename, jobid, notification, uploading] ); useEffect(() => { if (imgRef.current !== null && imageLoaded && !markerArea.current) { // create a marker.js MarkerArea markerArea.current = new markerjs2.MarkerArea(imgRef.current); // attach an event handler to assign annotated image back to our image element markerArea.current.addEventListener("close", () => { // NO OP }); markerArea.current.addEventListener("render", (event) => { const dataUrl = event.dataUrl; imgRef.current.src = dataUrl; markerArea.current.close(); triggerUpload(dataUrl); }); // launch marker.js markerArea.current.renderAtNaturalSize = true; markerArea.current.renderImageType = "image/jpeg"; markerArea.current.renderImageQuality = 1; //markerArea.current.settings.displayMode = "inline"; markerArea.current.show(); } }, [triggerUpload, imageLoaded]); useEffect(() => { // Load the image from imageUrl let isCancelled = false; const loadImage = async () => { if (!imageUrl) return; setImageLoaded(false); setImageLoading(true); try { const response = await axios.get(imageUrl, { responseType: "blob" }); if (isCancelled) return; const blobUrl = URL.createObjectURL(response.data); setLoadedImageUrl((prevUrl) => { if (prevUrl) URL.revokeObjectURL(prevUrl); return blobUrl; }); } catch (error) { console.error("Failed to fetch image blob", error); } finally { if (!isCancelled) { setImageLoading(false); } } }; loadImage(); return () => { isCancelled = true; }; }, [imageUrl]); async function b64toBlob(url) { const res = await fetch(url); return await res.blob(); } return (