diff --git a/src/Pages/StockMaster/InvoiceImageExtractor.jsx b/src/Pages/StockMaster/InvoiceImageExtractor.jsx index b8128a1..f093587 100644 --- a/src/Pages/StockMaster/InvoiceImageExtractor.jsx +++ b/src/Pages/StockMaster/InvoiceImageExtractor.jsx @@ -2,16 +2,9 @@ import { useState, useRef, useEffect } from 'react'; import { Upload, Button, - Spin, Form, Input, - Select, message, - Divider, - Row, - Col, - Table, - Image, DatePicker, } from 'antd'; import dayjs from 'dayjs'; @@ -21,7 +14,6 @@ import { CheckOutlined, CloseOutlined, ReloadOutlined, - EyeOutlined, DeleteFilled, CameraOutlined, EditOutlined, @@ -37,218 +29,8 @@ import { InputField } from '../../Components/Forms/InputField.jsx'; import TextAreaInput from '../../Components/Forms/TextArea.jsx'; import { uploadImage } from '../../Features/upload/upload.js'; import { getSession } from '../../Services/Others.js'; - -const { Option } = Select; -const { Column } = Table; - -const CameraModal = ({ - visible, - onClose, - onCapture, - setCameraModalVisible = () => { }, -}) => { - const videoRef = useRef(null); - const canvasRef = useRef(null); - const [stream, setStream] = useState(null); - const [image, setImage] = useState(null); - const [cameraOn, setCameraOn] = useState(false); - - const startCamera = async () => { - console.log('Starting camera function called...'); - - // First test - just set camera on without stream - setCameraOn(true); - console.log('Camera state forced to ON for testing'); - - try { - console.log('Checking getUserMedia support...'); - if (!navigator.mediaDevices?.getUserMedia) { - console.error('getUserMedia not supported'); - message.error('Camera not supported on this device'); - setCameraModalVisible(false); - return; - } - - console.log('Requesting camera access...'); - let mediaStream; - try { - // Try high quality first - mediaStream = await navigator.mediaDevices.getUserMedia({ - video: { - facingMode: 'environment', - width: { ideal: 1920 }, - height: { ideal: 1080 }, - }, - }); - } catch (highResError) { - console.log( - 'High resolution failed, trying basic constraints:', - highResError.message - ); - // Fallback to basic constraints - mediaStream = await navigator.mediaDevices.getUserMedia({ - video: { facingMode: 'environment' }, - }); - } - console.log('Got media stream:', mediaStream); - - if (videoRef.current) { - console.log('Setting video source...'); - videoRef.current.srcObject = mediaStream; - setStream(mediaStream); - console.log('Stream set successfully'); - } else { - console.error('Video ref is null'); - } - } catch (err) { - console.error('Camera error:', err.name, err.message); - message.error(`Camera error: ${err.message}`); - setCameraModalVisible(false); - } - }; - - const stopCamera = () => { - console.log('Stopping camera...'); - if (stream) { - stream.getTracks().forEach((track) => { - console.log('Stopping track:', track); - track.stop(); - }); - } - setStream(null); - setCameraOn(false); - console.log('Camera stopped'); - }; - - const captureImage = () => { - const video = videoRef.current; - const canvas = canvasRef.current; - canvas.width = video.videoWidth; - canvas.height = video.videoHeight; - const ctx = canvas.getContext('2d'); - ctx.imageSmoothingEnabled = true; - ctx.imageSmoothingQuality = 'high'; - ctx.drawImage(video, 0, 0); - const imageData = canvas.toDataURL('image/jpeg', 0.95); - setImage(imageData); - stopCamera(); - }; - - const retake = () => { - setImage(null); - startCamera(); - }; - - const handleUse = () => { - canvasRef.current.toBlob( - (blob) => { - const file = new File([blob], 'camera-capture.jpg', { - type: 'image/jpeg', - }); - onCapture(file, true); // Pass true to indicate direct submit - handleClose(); - }, - 'image/jpeg', - 0.95 - ); - }; - - const handleClose = () => { - stopCamera(); - setImage(null); - setCameraOn(false); - onClose(); - }; - - useEffect(() => { - if (visible) { - startCamera(); // Auto-start camera when modal opens - return () => stopCamera(); - } - }, [visible]); - - return ( - -
- {/*
-

Debug: Camera={cameraOn ? 'ON' : 'OFF'}, Stream={stream ? 'Active' : 'None'}, Image={image ? 'Yes' : 'No'}

-
*/} - - {!cameraOn && !image && ( -
-

- Starting camera... -

-
- )} - - {cameraOn && ( -
-

Camera is ON - Video should appear below:

-
- )} - - {image && ( - <> -
- - -
- Captured - - )} - - -
-
- ); -}; +import ResizableCropper from '../../Components/PedalOCR/CropperArea.jsx'; +import ThumbnailsList from '../../Components/PedalOCR/ThumbnailsList.jsx'; const ScanModal = ({ visible = false, @@ -258,42 +40,77 @@ const ScanModal = ({ processingStep = null, ocrLoading = false, handleClearImage = () => { }, - uploadedImage = null, selectedFile = null, + images = [], + setImages = () => { }, + finalImages = [], + setCurrentIndex = () => { }, + setFinalImages = () => { }, + currentIndex = null, }) => { + + const cameraRef = useRef(null); const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( navigator.userAgent ) || 'ontouchstart' in window || navigator.maxTouchPoints > 0; - const [cameraModalVisible, setCameraModalVisible] = useState(false); + const [buttonDisable, setButtonDisable] = useState(false); - const handleCameraCapture = (file, directSubmit = false) => { - console.log( - 'Camera capture received:', - file, - 'Direct submit:', - directSubmit - ); + const handleCapture = (file) => { + console.log('Handle capture called with file:', file?.name); - handleUpload(file); - - if (directSubmit) { - setButtonDisable(true); - setTimeout(() => { - try { - onSubmit(file, true); - console.log('Triggering submit after camera capture'); - } catch (err) { - console.error('Submit Error:', err?.message); - } finally { - setButtonDisable(false); - } - }, 100); - } + const reader = new FileReader(); + reader.onload = (e) => { + const newImage = { + id: Date.now(), + src: e.target.result, + file: file, + }; + setImages([newImage]); + setCurrentIndex(0); + }; + reader.readAsDataURL(file); }; + + const handleCropComplete = (croppedImage) => { + setFinalImages((prev) => { + const newImages = [...prev]; + newImages[currentIndex] = croppedImage; + return newImages; + }); + fetch(croppedImage) + .then((res) => res.blob()) + .then((blob) => { + const file = new File([blob], 'camera-capture.jpg', { + type: 'image/jpeg', + }); + handleUpload(file); + }); + } + + const handleRemoveImage = (index) => { + setFinalImages((prev) => prev.filter((_, i) => i !== index)); + handleClearImage(); + }; + + const handleRecropImage = (index) => { + const imageToRecrop = finalImages[index]; + setFinalImages((prev) => prev.filter((_, i) => i !== index)); + // Add back to images for re-cropping + const newImage = { id: Date.now(), src: imageToRecrop }; + setImages((prev) => [...prev, newImage]); + setCurrentIndex(images.length); + }; + + const resetAll = () => { + setImages([]); + setFinalImages([]); + setCurrentIndex(null); + }; + return ( + { + const rawFile = e?.target?.files?.[0]; + if (!rawFile) return; + handleCapture(rawFile); + }} + /> + {currentIndex !== null && images[currentIndex] && ( + { + if (currentIndex + 1 < images.length) { + setCurrentIndex(currentIndex + 1); + } else { + setCurrentIndex(null); + setImages([]); + } + }} + /> + )} + {finalImages.length > 0 && currentIndex === null && ( + <> +

Uploaded Image :

+ + + )} {isMobile && ( - - Preview - - )} - setCameraModalVisible(false)} - onCapture={handleCameraCapture} - setCameraModalVisible={setCameraModalVisible} - /> )} @@ -498,6 +318,9 @@ const InvoiceImageExtractorModal = ({ const extractedTableRef = useRef(null); const [form] = Form.useForm(); + const [images, setImages] = useState([]); + const [finalImages, setFinalImages] = useState([]); + const [currentIndex, setCurrentIndex] = useState(null); const [ocrLoading, setOcrLoading] = useState(false); const [uploadedImage, setUploadedImage] = useState(null); const [selectedFile, setSelectedFile] = useState(null); @@ -661,6 +484,9 @@ const InvoiceImageExtractorModal = ({ cleanupUploadedURL(); setProcessingStep(null); setSelectedFile(null); + setImages([]); + setCurrentIndex(null); + setFinalImages([]); if (visible || submit) { form.resetFields(); setExtractedText(''); @@ -728,6 +554,7 @@ const InvoiceImageExtractorModal = ({ CompId, BranchId } + console.log('Dispatching OCR request with data:', data); const response = await dispatch( getInvoiceImageData(data) ).unwrap(); @@ -823,6 +650,9 @@ const InvoiceImageExtractorModal = ({ } else if (uploadMoreVisible) { setUploadMoreVisible(false); } + setImages([]); + setCurrentIndex(null); + setFinalImages([]); message.success('Invoice processed successfully!'); } else { if (response?.statusCode === 1 && response?.data?.length === 0) { @@ -832,7 +662,7 @@ const InvoiceImageExtractorModal = ({ } } } catch (err) { - console.error('API OCR Error:', err); + console.error('API OCR Error:', err?.message); message.error('Failed to process invoice.'); } finally { setOcrLoading(false); @@ -954,8 +784,13 @@ const InvoiceImageExtractorModal = ({ processingStep={processingStep} ocrLoading={ocrLoading} handleClearImage={handleClearImage} - uploadedImage={uploadedImage} selectedFile={selectedFile} + images={images} + setImages={setImages} + finalImages={finalImages} + setFinalImages={setFinalImages} + currentIndex={currentIndex} + setCurrentIndex={setCurrentIndex} /> {/* PREVIEW MODAL */} @@ -1213,8 +1048,13 @@ const InvoiceImageExtractorModal = ({ processingStep={processingStep} ocrLoading={ocrLoading} handleClearImage={handleClearImage} - uploadedImage={uploadedImage} selectedFile={selectedFile} + images={images} + setImages={setImages} + finalImages={finalImages} + setFinalImages={setFinalImages} + currentIndex={currentIndex} + setCurrentIndex={setCurrentIndex} />