diff --git a/src/screens/Biometric/OcrKtp/Verify.jsx b/src/screens/Biometric/OcrKtp/Verify.jsx index 8baa9ed..06fa901 100644 --- a/src/screens/Biometric/OcrKtp/Verify.jsx +++ b/src/screens/Biometric/OcrKtp/Verify.jsx @@ -1,24 +1,38 @@ import React, { useState, useEffect, useRef } from 'react'; -import { Link } from 'react-router-dom'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faChevronDown, faChevronLeft, faImage, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { faImage, faTimes, faChevronDown, faChevronLeft, faCloudUploadAlt } from '@fortawesome/free-solid-svg-icons'; +import { Link } from 'react-router-dom'; +import Select from 'react-select' +import { DummyKtp } from '../../../assets/images'; + +const CustomLabel = ({ overRide, children, ...props }) => { + return ; +}; const Verify = () => { const BASE_URL = process.env.REACT_APP_BASE_URL; const API_KEY = process.env.REACT_APP_API_KEY; - const fileTypes = ["image/jpeg", "image/png"]; // Use MIME types for better file validation + const fileTypes = ["image/jpeg", "image/png"]; const fileInputRef = useRef(null); - const [isSelectOpen, setIsSelectOpen] = useState(false); + const [isMobile, setIsMobile] = useState(false); const [errorMessage, setErrorMessage] = useState(''); const [selectedImageName, setSelectedImageName] = useState(''); const [file, setFile] = useState(null); const [applicationId, setApplicationId] = useState(''); const [isLoading, setIsLoading] = useState(false); const [applicationIds, setApplicationIds] = useState([]); + const [imageUrl, setImageUrl] = useState(''); const [imageError, setImageError] = useState(''); const [data, setData] = useState(null); const [showResult, setShowResult] = useState(false); + const [inputValueApplication, setInputValueApplication] = useState(''); + + // Validation state + const [validationErrors, setValidationErrors] = useState({ + applicationId: '', + file: '' + }); // Fetch Application IDs useEffect(() => { @@ -51,47 +65,91 @@ const Verify = () => { }; fetchApplicationIds(); + + const checkMobile = () => { + setIsMobile(window.innerWidth <= 768); // Example: 768px as the threshold for mobile devices + }; + + // Check on initial load + checkMobile(); + + // Add resize listener to adjust `isMobile` state on window resize + window.addEventListener('resize', checkMobile); + + // Clean up the event listener when the component unmounts + return () => { + window.removeEventListener('resize', checkMobile); + }; }, []); - const handleFocus = () => setIsSelectOpen(true); - const handleBlur = () => setIsSelectOpen(false); - - // Handle file upload - const handleImageUpload = (e) => { - const file = e.target.files[0]; - if (!file) return; - - const fileType = file.type; // Use MIME type instead of file extension - if (!fileTypes.includes(fileType)) { - setImageError('Image format is not supported'); - setFile(null); - return; - } - - if (file.size > 2 * 1024 * 1024) { // 2MB check - setImageError('File size exceeds 2MB'); - setFile(null); - return; - } - - setSelectedImageName(file.name); - setFile(file); - setImageError(''); + const handleInputChangeApplication = (inputValue) => { + setInputValueApplication(inputValue); + }; + + const handleApplicationChange = (selectedOption) => { + setApplicationId(selectedOption ? selectedOption.value : ''); + }; + + const applicationOptions = applicationIds.map(app => ({ + value: app.id, // This is what will be sent when an option is selected + label: app.name // This is what will be displayed in the dropdown + })); + + // Handle file upload + const handleFileDrop = (files) => { + if (files && files[0]) { + handleImageUpload(files[0]); + } else { + console.error('No valid files dropped'); + } + }; + + const handleImageUpload = (file) => { + setFile(file); + setSelectedImageName(file.name); + + // Validate file type + if (!fileTypes.includes(file.type)) { + setImageError('Invalid file type. Only JPG, JPEG, and PNG are allowed.'); + } else if (file.size > 2 * 1024 * 1024) { // Max 2MB + setImageError('File size exceeds 2MB.'); + } else { + setImageError(''); + } }; - // Cancel file upload const handleImageCancel = () => { - setSelectedImageName(''); setFile(null); + setSelectedImageName(''); setImageError(''); fileInputRef.current.value = ''; }; + // Validate form inputs before submitting + const validateForm = () => { + const errors = { + applicationId: '', + file: '' + }; + + if (!applicationId) { + errors.applicationId = 'Please select an Application ID.'; + } + + if (!file) { + errors.file = 'Please upload an image file.'; + } else if (imageError) { + errors.file = imageError; + } + + setValidationErrors(errors); + return Object.values(errors).every(error => error === ''); + }; + // Submit form and trigger OCR API const handleCheckClick = async () => { - if (!file || !applicationId) { - setErrorMessage('Please select an application and upload a file.'); - return; + if (!validateForm()) { + return; // Form is not valid } setIsLoading(true); @@ -99,8 +157,6 @@ const Verify = () => { formData.append('application_id', applicationId); formData.append('file', file); - console.log(`id: ${applicationId}, file: ${file}`) - try { const response = await fetch(`${BASE_URL}/ocr-ktp`, { method: 'POST', @@ -116,36 +172,13 @@ const Verify = () => { } const result = await response.json(); - - console.log('Full response:', result); // Log full response to inspect the structure + + // Log the full result to verify structure + console.log('OCR API Response:', result); if (result.status_code === 201) { - console.log('Success'); - // Correct the path to access the data-ktp object const responseData = result.details.data?.['data-ktp'] || {}; - // Log each field to inspect the data - console.log('NIK:', responseData.nik); - console.log('District:', responseData.kecamatan); - console.log('Name:', responseData.nama); - console.log('City:', responseData.kabkot); - console.log('Date of Birth:', responseData.tanggal_lahir); - console.log('State:', responseData.provinsi); - console.log('Gender:', responseData.jenis_kelamin); - console.log('Religion:', responseData.agama); - console.log('Blood Type:', responseData.golongan_darah); - console.log('Marital Status:', responseData.status_perkawinan); - console.log('Address:', responseData.alamat); - console.log('Occupation:', responseData.pekerjaan); - console.log('RT/RW:', `${responseData.rt}/${responseData.rw}`); - console.log('Nationality:', responseData.kewarganegaraan); - console.log('Image URL:', result.details.image_url); - console.log('Dark:', responseData.dark); - console.log('Blur:', responseData.blur); - console.log('Grayscale:', responseData.grayscale); - console.log('Flashlight:', responseData.flashlight); - - // Map the response data to a new object with default values if the property doesn't exist const data = { nik: responseData.nik || 'N/A', district: responseData.kecamatan || 'N/A', @@ -161,17 +194,22 @@ const Verify = () => { occupation: responseData.pekerjaan || 'N/A', rtRw: `${responseData.rt || 'N/A'}/${responseData.rw || 'N/A'}`, nationality: responseData.kewarganegaraan || 'N/A', - imageUrl: result.details.image_url || '', - dark: responseData.dark || 'N/A', - blur: responseData.blur || 'N/A', - grayscale: responseData.grayscale || 'N/A', - flashlight: responseData.flashlight || 'N/A', + imageUrl: result.details.data?.image_url || '', // Properly access image_url }; + console.log('Image URL from OCR:', result.details.data?.image_url); // Log the image URL correctly + setData(data); setShowResult(true); setErrorMessage(''); setSelectedImageName(''); + + // Fetch image if image URL exists in the result + if (result.details.data?.image_url) { + const imageFileName = result.details.data.image_url.split('/').pop(); // Get the image filename + console.log('Image file name:', imageFileName); // Debug the file name + await fetchImage(imageFileName); // Call the fetchImage function to fetch the image + } } else { setErrorMessage('OCR processing failed'); } @@ -182,15 +220,53 @@ const Verify = () => { } }; + + + // The fetchImage function you already have in your code + const fetchImage = async (imageFileName) => { + setIsLoading(true); + try { + const response = await fetch(`${BASE_URL}/preview/image/${imageFileName}`, { + method: 'GET', + headers: { + 'accept': 'application/json', + 'x-api-key': API_KEY, + } + }); + + if (!response.ok) { + const errorDetails = await response.json(); + console.error('Image fetch error details:', errorDetails); + setErrorMessage('Failed to fetch image, please try again.'); + return; + } + + // Get the image blob + const imageBlob = await response.blob(); + const imageData = URL.createObjectURL(imageBlob); // Create object URL from the blob + + // Debugging: Make sure imageData is correct + console.log('Fetched image URL:', imageData); + + setImageUrl(imageData); // Update imageUrl state with the fetched image data + } catch (error) { + console.error('Error fetching image:', error); + setErrorMessage(error.message); + } finally { + setIsLoading(false); + } + }; + return (
{validationErrors.applicationId}
}@@ -258,16 +327,38 @@ const Verify = () => {
Drag and Drop Here
+Or
+ fileInputRef.current.click()} style={styles.browseLink}>Browse +Recommended size: 300x300 (Max File Size: 2MB)
+Supported file types: JPG, JPEG, PNG
+File: {selectedImageName}
@@ -276,7 +367,7 @@ const Verify = () => {{imageError}
} + {validationErrors.file &&{validationErrors.file}
}NIK | -{data.nik} | -
District | -{data.district} | -
Name | -{data.name} | -
City | -{data.city} | -
Date of Birth | -{data.dob} | -
State | -{data.state} | -
Gender | -{data.gender} | -
Religion | -{data.religion} | -
Blood Type | -{data.bloodType} | -
Marital Status | -{data.maritalStatus} | -
Address | -{data.address} | -
Occupation | -{data.occupation} | -
RT/RW | -{data.rtRw} | -
Nationality | -{data.nationality} | -
Image | -
NIK | +{data.nik} | +
District | +{data.district} | +
Name | +{data.name} | +
City | +{data.city} | +
Date of Birth | +{data.dob} | +
State | +{data.state} | +
Gender | +{data.gender} | +
Religion | +{data.religion} | +
Blood Type | +{data.bloodType} | +
Marital Status | +{data.maritalStatus} | +
Address | +{data.address} | +
Occupation | +{data.occupation} | +
RT/RW | +{data.rtRw} | +
Nationality | +{data.nationality} | +