import React, { useState, useEffect, useRef } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 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"]; const fileInputRef = useRef(null); 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(''); const [selectedQuota, setSelectedQuota] = useState(0); // Validation state const [validationErrors, setValidationErrors] = useState({ applicationId: '', file: '' }); // Fetch Application IDs useEffect(() => { const fetchApplicationIds = async () => { try { setIsLoading(true); const response = await fetch(`${BASE_URL}/application/list`, { method: 'GET', headers: { 'accept': 'application/json', 'x-api-key': API_KEY, }, }); if (!response.ok) { throw new Error('Failed to fetch application IDs'); } const data = await response.json(); if (data.status_code === 200) { setApplicationIds(data.details.data); } else { throw new Error('Failed to fetch application IDs'); } } catch (error) { setErrorMessage(error.message || 'Error fetching application IDs'); } finally { setIsLoading(false); } }; 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 handleInputChangeApplication = (inputValue) => { setInputValueApplication(inputValue); }; const handleApplicationChange = (selectedOption) => { const selectedId = selectedOption.value; const selectedApp = applicationIds.find(app => app.id === parseInt(selectedId)); setApplicationId(selectedOption ? selectedOption.value : ''); if (selectedApp) { setSelectedQuota(selectedApp.quota); } }; 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(''); } }; const handleImageCancel = () => { 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 (!validateForm()) { return; // Form is not valid } setIsLoading(true); const formData = new FormData(); formData.append('application_id', applicationId); formData.append('file', file); try { const response = await fetch(`${BASE_URL}/ocr-ktp`, { method: 'POST', headers: { 'accept': 'application/json', 'x-api-key': API_KEY, }, body: formData, }); if (!response.ok) { throw new Error('OCR processing failed'); } const result = await response.json(); // Log the full result to verify structure console.log('OCR API Response:', result); if (result.status_code === 201) { const responseData = result.details.data?.['data-ktp'] || {}; const data = { nik: responseData.nik || 'N/A', district: responseData.kecamatan || 'N/A', name: responseData.nama || 'N/A', city: responseData.kabkot || 'N/A', dob: responseData.tanggal_lahir || 'N/A', state: responseData.provinsi || 'N/A', gender: responseData.jenis_kelamin || 'N/A', religion: responseData.agama || 'N/A', bloodType: responseData.golongan_darah || 'N/A', maritalStatus: responseData.status_perkawinan || 'N/A', address: responseData.alamat || 'N/A', occupation: responseData.pekerjaan || 'N/A', rtRw: `${responseData.rt || 'N/A'}/${responseData.rw || 'N/A'}`, nationality: responseData.kewarganegaraan || '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'); } } catch (error) { setErrorMessage(error.message || 'Error during OCR processing'); } finally { setIsLoading(false); } }; // 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); } }; const styles = { selectWrapper: { position: 'relative', display: 'inline-block', width: '100%', }, select: { fontSize: '16px', padding: '10px', width: '100%', borderRadius: '4px', border: '1px solid #ccc', }, remainingQuota: { display: 'flex', alignItems: 'center', }, quotaText: { fontSize: '24px', fontWeight: '600', }, timesText: { fontSize: '16px', fontWeight: '300', }, customLabel: { fontSize: '18px', fontWeight: '600', color: '#1f2d3d', }, uploadWrapper: { marginTop: '1rem', }, uploadArea: { backgroundColor: '#e6f2ff', height: !isMobile? '30svh' : '50svh', cursor: 'pointer', marginTop: '1rem', paddingTop: '22px', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', border: '1px solid #ced4da', borderRadius: '0.25rem', textAlign: 'center', }, uploadIcon: { fontSize: '40px', color: '#0542cc', marginBottom: '10px', }, uploadText: { color: '#1f2d3d', fontWeight: '400', fontSize: '16px', }, browseLink: { color: '#0542cc', textDecoration: 'none', fontWeight: 'bold', }, uploadError: { color: 'red', fontSize: '12px', marginTop: '10px', }, errorContainer: { marginTop: '10px', padding: '10px', backgroundColor: '#f8d7da', border: '1px solid #f5c6cb', borderRadius: '4px', }, errorText: { color: '#721c24', fontSize: '14px', margin: '0', },loadingOverlay: { position: 'fixed', // Gunakan fixed untuk overlay penuh layar top: 0, left: 0, right: 0, bottom: 0, // Pastikan menutupi seluruh layar display: 'flex', justifyContent: 'center', // Posisikan spinner di tengah secara horizontal alignItems: 'center', // Posisikan spinner di tengah secara vertikal backgroundColor: 'rgba(0, 0, 0, 0.5)', // Semitransparan di background color: 'white', fontSize: '24px', zIndex: 1000, // Pastikan overlay muncul di atas konten lainnya }, spinner: { border: '4px solid #f3f3f3', // Border gray untuk spinner borderTop: '4px solid #3498db', // Border biru untuk spinner borderRadius: '50%', // Membuat spinner bulat width: '50px', // Ukuran spinner height: '50px', // Ukuran spinner animation: 'spin 2s linear infinite', // Menambahkan animasi spin }, loadingText: { marginLeft: '20px', }, tableStyle: { width: '100%', borderCollapse: 'collapse', marginTop: '20px', }, tableCell: { padding: '8px 15px', border: '1px solid #ccc', textAlign: 'left', }, }; const formatFileSize = (sizeInBytes) => { if (sizeInBytes < 1024) { return `${sizeInBytes} bytes`; // Jika ukuran lebih kecil dari 1 KB } else if (sizeInBytes < 1048576) { return `${(sizeInBytes / 1024).toFixed(2)} KB`; // Jika ukuran lebih kecil dari 1 MB } else { return `${(sizeInBytes / 1048576).toFixed(2)} MB`; // Jika ukuran lebih besar dari 1 MB } }; return (
{/* Inject keyframes for the spinner */} {isLoading && (

Loading...

)}

Alert

Get started now by creating an Application ID and explore all the demo services available on the dashboard. Experience the ease and flexibility of trying out all our features firsthand.

handleImageUpload(e.target.files[0])} /> {selectedImageName && (

File: {selectedImageName}

{file && (

Size: {formatFileSize(file.size)}

)}
)} {validationErrors.file &&

{validationErrors.file}

}
{errorMessage && (

{errorMessage}

)}
{showResult && data && (

OCR KTP Result

{/* Gambar di kolom pertama */}
KTP Image
{/* Tabel di kolom kedua */}
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}
)}
); }; export default Verify;