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 (
Loading...
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.
{validationErrors.applicationId}
}Remaining Quota
Drag and Drop Here
Or
fileInputRef.current.click()} style={styles.browseLink}>BrowseRecommended size: 300x300 (Max File Size: 2MB)
Supported file types: JPG, JPEG, PNG
File: {selectedImageName}
{file && (Size: {formatFileSize(file.size)}
)}{validationErrors.file}
}{errorMessage}
| 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} |