import React, { useState, useRef, useEffect } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faChevronLeft, faChevronDown, faTimes, faImage } from '@fortawesome/free-solid-svg-icons'; import { FileUploader } from 'react-drag-drop-files'; import Select from 'react-select' const Verify = () => { const BASE_URL = process.env.REACT_APP_BASE_URL; const API_KEY = process.env.REACT_APP_API_KEY; const fileTypes = ["JPG", "JPEG", "PNG"]; const [file, setFile] = useState(null); const [isSelectOpen, setIsSelectOpen] = useState(false); const [errorMessage, setErrorMessage] = useState(''); const [uploadError, setUploadError] = useState(''); const [applicationError, setApplicationError] = useState(''); const [subjectError, setSubjectError] = useState(''); const [thresholdError, setThresholdError] = useState(''); const [selectedImageName, setSelectedImageName] = useState(''); const fileInputRef = useRef(null); const [showResult, setShowResult] = useState(false); const [applicationId, setApplicationId] = useState(''); const [thresholdId, setThresholdId] = useState(''); const [selectedQuota, setSelectedQuota] = useState(0); const [subjectId, setSubjectId] = useState(''); const [isLoading, setIsLoading] = useState(false); const [verified, setVerified] = useState(null); const [imageUrl, setImageUrl] = useState(''); const [applicationIds, setApplicationIds] = useState([]); const [subjectIds, setSubjectIds] = useState([]); const [subjectAvailabilityMessage, setSubjectAvailabilityMessage] = useState(''); // Message for subject availability const [inputValueApplication, setInputValueApplication] = useState(''); // Controlled input value for Application ID const [isMobile, setIsMobile] = useState(window.innerWidth <= 768); const thresholdIds = [ { id: 1, name: 'cosine', displayName: 'Basic' }, { id: 2, name: 'euclidean', displayName: 'Medium' }, { id: 3, name: 'euclidean_l2', displayName: 'High' }, ]; const options = subjectIds.map(id => ({ value: id, label: id })); const [inputValue, setInputValue] = useState(''); const applicationOptions = applicationIds.map(app => ({ value: app.id, label: app.name })); useEffect(() => { const fetchApplicationIds = async () => { try { setIsLoading(true); const url = `${BASE_URL}/application/list`; const response = await fetch(url, { method: 'GET', headers: { 'accept': 'application/json', 'x-api-key': `${API_KEY}`, }, }); const data = await response.json(); if (data.status_code === 200) { setApplicationIds(data.details.data); } else { console.error('Failed to fetch data:', data.details.message); } } catch (error) { console.error('Error fetching application IDs:', error); } finally { setIsLoading(false); } }; fetchApplicationIds(); const handleResize = () => setIsMobile(window.innerWidth <= 768); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); const styles = { formGroup: { marginTop: '-45px', }, selectWrapper: { position: 'relative', marginTop: '0', }, select: { width: '100%', paddingRight: '30px', }, chevronIcon: { position: 'absolute', right: '10px', top: '50%', transform: 'translateY(-50%)', pointerEvents: 'none', }, remainingQuota: { display: 'flex', flexDirection: 'row', alignItems: 'center', marginTop: '4px', }, quotaText: { fontSize: '40px', color: '#0542cc', fontWeight: '600', }, timesText: { marginLeft: '8px', verticalAlign: 'super', fontSize: '20px', }, uploadArea: { backgroundColor: '#e6f2ff', height: '250px', cursor: 'pointer', marginTop: '1rem', padding: '22px 10px 0 20px', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', border: '1px solid #ced4da', borderRadius: '0.25rem', }, uploadIcon: { fontSize: '40px', color: '#0542cc', marginBottom: '7px', }, uploadText: { color: '#1f2d3d', fontWeight: '400', fontSize: '16px', lineHeight: '13px', }, wrapper: { border: '1px solid #ddd', borderRadius: '6px', padding: '18px 10px 0 8px', // Padding lebih seragam height: '13svh', // Tinggi lebih kecil untuk menyesuaikan tampilan display: 'flex', alignItems: 'center', justifyContent: 'space-between', backgroundColor: '#f9f9f9', overflow: 'hidden', }, fileWrapper: { display: 'flex', alignItems: 'center', flex: '1', }, textContainer: { flex: '1', fontSize: '16px', // Ukuran font lebih kecil marginLeft: '6px', overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis', marginTop: '1rem' }, fileSize: { fontSize: '12px', color: '#555', marginBottom: '2rem', }, closeButtonContainer: { display: 'flex', alignItems: 'center', marginLeft: 'auto', }, closeButton: { background: 'transparent', border: 'none', cursor: 'pointer', padding: '0', }, imageIcon: { color: '#0542cc', fontSize: '18px', // Ukuran ikon sedikit lebih kecil marginRight: '6px', }, closeIcon: { color: 'red', fontSize: '18px', }, submitButton: { marginLeft: 'auto', marginTop: '4rem', textAlign: 'start', position: 'relative', zIndex: 1, }, uploadError: { color: 'red', fontSize: '12px', marginTop: '5px', }, containerResultStyle: { padding: '20px', border: '1px solid #0053b3', borderRadius: '5px', width: '100%', maxWidth: '600px', margin: '20px auto', }, resultContainer: { display: 'flex', alignItems: 'center', width: '100%', }, tableStyle: { width: '60%', borderCollapse: 'collapse', }, resultsTableMobile: { width: '100%', borderCollapse: 'collapse', fontSize: '14px', }, resultsCell: { border: '0.1px solid gray', padding: '8px', }, resultsValueCell: { border: '0.1px solid gray', padding: '8px', width: '60%', }, imageContainer: { display: 'flex', flexDirection: 'column', alignItems: 'center', marginTop: isMobile ? '20px' : '0', }, imageStyle: { width: '300px', height: '300px', borderRadius: '5px', objectFit: 'cover', }, imageStyleMobile: { width: '100%', height: 'auto', }, loadingOverlay: { position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0, 0, 0, 0.2)', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', zIndex: 1000, }, spinner: { border: '4px solid rgba(0, 0, 0, 0.1)', borderLeftColor: '#0542cc', borderRadius: '50%', width: '90px', height: '90px', animation: 'spin 1s ease-in-out infinite', }, loadingText: { marginTop: '10px', fontSize: '1.2rem', color: '#fff', textAlign: 'center', }, }; const handleApplicationChange = async (selectedOption) => { if (!selectedOption) { console.error("Selected option is undefined"); return; } const selectedId = selectedOption.value; const selectedApp = applicationIds.find(app => app.id === parseInt(selectedId)); if (selectedApp) { setSelectedQuota(selectedApp.quota); } setApplicationId(selectedId); if (selectedId) { await fetchSubjectIds(selectedId); } else { setSubjectIds([]); setSubjectAvailabilityMessage(''); } }; const fetchSubjectIds = async (appId) => { setIsLoading(true); try { const response = await fetch(`${BASE_URL}/trx_face/list/subject?application_id=${appId}&search=${subjectId}&limit=99`, { method: 'GET', headers: { 'accept': 'application/json', 'x-api-key': API_KEY, }, }); const data = await response.json(); console.log("Fetched Subject IDs:", data); // Log data fetched from API if (data.status_code === 200) { setSubjectIds(data.details.data); } else { console.error('Failed to fetch subject IDs:', data.details.message); } } catch (error) { console.error('Error fetching subject IDs:', error); } finally { setIsLoading(false); } }; const handleFocus = () => { setIsSelectOpen(true); }; const handleBlur = () => { setIsSelectOpen(false); }; const handleImageUpload = (file) => { // Ensure file exists before accessing its properties if (!file) { console.error('File is undefined'); setUploadError('Please upload a valid image file.'); return; } const fileExtension = file.name.split('.').pop().toUpperCase(); if (fileTypes.includes(fileExtension)) { setSelectedImageName(file.name); setFile(file); setUploadError(''); // Clear any previous errors } else { // Show an alert if the file type is not supported alert('Image format is not supported'); setUploadError('Image format is not supported'); // Optionally set error message to display on the UI setFile(null); // Optionally clear the selected file } }; const handleImageCancel = () => { setSelectedImageName(''); setFile(null); if (fileInputRef.current) { fileInputRef.current.value = ''; } }; const handleCheckClick = async () => { // Reset previous error messages setErrorMessage(''); setApplicationError(''); setSubjectError(''); setThresholdError(''); setUploadError(''); let hasError = false; // Track if any errors occur if (!applicationId) { setApplicationError('Please select an Application ID before enrolling.'); hasError = true; // Mark that an error occurred } if (!subjectId) { setSubjectError('Please enter a Subject ID before enrolling.'); hasError = true; // Mark that an error occurred } const selectedThreshold = thresholdIds.find(threshold => threshold.name === thresholdId)?.name; if (!selectedThreshold) { setThresholdError('Invalid threshold selected.'); hasError = true; // Mark that an error occurred } if (!selectedImageName) { setUploadError('Please upload a face photo before verifying.'); hasError = true; // Mark that an error occurred } // If there are any errors, stop the function if (hasError) { return; } // Log the input values console.log('Selected Image Name:', selectedImageName); console.log('Application ID:', applicationId); console.log('Subject ID:', subjectId); console.log('Selected Threshold:', selectedThreshold); const formData = new FormData(); formData.append('application_id', applicationId); formData.append('threshold', selectedThreshold); formData.append('subject_id', subjectId); // const file = fileInputRef.current.files[0]; if (file) { formData.append('file', file, file.name); } else { setUploadError('Please upload an image file.'); return; } setIsLoading(true); try { const response = await fetch(`${BASE_URL}/face_recognition/verifiy`, { method: 'POST', headers: { 'accept': 'application/json', 'x-api-key': `${API_KEY}`, }, body: formData, }); const data = await response.json(); if (response.ok) { if (data.details && data.details.data && data.details.data.result && data.details.data.result.image_url) { const imageFileName = data.details.data.result.image_url.split('/').pop(); await fetchImage(imageFileName); } setShowResult(true); setVerified(data.details.data.result.verified); } else { const errorMessage = data.message || data.detail || data.details?.message || 'An unknown error occurred.'; setErrorMessage(errorMessage); } } catch (error) { console.error('Error:', error); setErrorMessage('An error occurred while making the request.'); } finally { setIsLoading(false); } }; 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) { setErrorMessage('Failed to fetch image, please try again.'); return; } const imageBlob = await response.blob(); const imageData = URL.createObjectURL(imageBlob); setImageUrl(imageData); } catch (error) { console.error('Error fetching image:', error); setErrorMessage(error.message); } finally { setIsLoading(false); } }; const CustomLabel = ({ overRide, children, ...props }) => { // We intentionally don't pass `overRide` to the label return ( ); }; const handleInputChangeApplication = (newInputValue) => { // Limit input to 15 characters for Application ID if (newInputValue.length <= 15) { setInputValueApplication(newInputValue); } }; // Fungsi untuk mengonversi ukuran file dari byte ke KB/MB 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...
Remaining Quota
Drag and Drop Here
Or
fileInputRef.current.click()}>BrowseRecommended size: 300x300 (Max File Size: 2MB)
Supported file types: JPG, JPEG
{selectedImageName}
{file && (Size: {formatFileSize(file.size)}
)}Similarity | {verified !== null ? (verified ? 'True' : 'False') : 'N/A'} |
File Name: {selectedImageName}