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' import { ServerDownAnimation } from '../../../../assets/images'; const Compare = () => { const BASE_URL = process.env.REACT_APP_BASE_URL const API_KEY = process.env.REACT_APP_API_KEY const [isSelectOpen, setIsSelectOpen] = useState(false); const [errorMessage, setErrorMessage] = useState(''); const [selectedImageName, setSelectedImageName] = useState(''); const [selectedCompareImageName, setSelectedCompareImageName] = useState(''); const [resultImageLabel, setResultImageLabel] = useState(''); const [resultCompareImageLabel, setResultCompareImageLabel] = useState(''); const fileInputRef = useRef(null); const fileCompareInputRef = useRef(null); const [showResult, setShowResult] = useState(false); const [applicationId, setApplicationId] = useState(''); const [selectedQuota, setSelectedQuota] = useState(0); const [thresholdId, setTresholdId] = useState(''); const [isLoading, setIsLoading] = useState(false); const [imageUrl, setImageUrl] = useState(''); const [imageCompareUrl, setImageCompareUrl] = useState(''); const [verified, setVerified] = useState(null); const fileTypes = ["JPG", "JPEG"]; const [file, setFile] = useState(null); // For the first image const [compareFile, setCompareFile] = useState(null); // For the second imag const [applicationIds, setApplicationIds] = useState([]); const [inputValueApplication, setInputValueApplication] = useState(''); // Controlled input value for Application ID const [isServer, setIsServer] = useState(true); const thresholdIds = [ { id: 1, name: 'cosine', displayName: 'Basic' }, { id: 2, name: 'euclidean', displayName: 'Medium' }, { id: 3, name: 'euclidean_l2', displayName: 'High' }, ]; const [applicationError, setApplicationError] = useState(''); const [thresholdError, setThresholdError] = useState(''); const [uploadError, setUploadError] = useState(''); const [compareUploadError, setCompareUploadError] = useState(''); useEffect(() => { const fetchApplicationIds = async () => { try { setIsLoading(true) const url = `${BASE_URL}/application/list`; console.log('Fetching URL:', url); // Log the URL 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) { const ids = data.details.data.map(app => app.id); setIsServer(true) console.log('Application Id: ' + ids); // Log the IDs setApplicationIds(data.details.data); // Update state with the fetched data } else { console.error('Failed to fetch data:', data.details.message); setIsServer(false) } } catch (error) { console.error('Error fetching application IDs:', error); setIsServer(false) } finally { setIsLoading(false) } }; fetchApplicationIds(); }, []); const handleApplicationChange = (selectedOption) => { if (selectedOption) { const selectedId = selectedOption.value; const selectedApp = applicationIds.find(app => app.id === parseInt(selectedId)); if (selectedApp) { setApplicationId(selectedId); setSelectedQuota(selectedApp.quota); // Set the selected quota } } }; const handleInputChangeApplication = (newInputValue) => { // Limit input to 15 characters for Application ID if (newInputValue.length <= 15) { setInputValueApplication(newInputValue); } }; const handleFocus = () => { setIsSelectOpen(true); }; const handleBlur = () => { setIsSelectOpen(false); }; const handleImageUpload = (file) => { setShowResult(false); if (!file) { setUploadError('Please select a file'); return; } // Check file size (2MB = 2 * 1024 * 1024 bytes) const maxSize = 2 * 1024 * 1024; if (file.size > maxSize) { setUploadError('File size exceeds 2MB limit'); setFile(null); setSelectedImageName(''); return; } // Check file type const fileExtension = file.name.split('.').pop().toLowerCase(); const validExtensions = ['jpg', 'jpeg']; const validMimeTypes = ['image/jpeg', 'image/jpg']; if (!validExtensions.includes(fileExtension) || !validMimeTypes.includes(file.type)) { setUploadError('Only JPG/JPEG files are allowed'); setFile(null); setSelectedImageName(''); return; } // Set file and filename if validation passes const previewUrl = URL.createObjectURL(file); setImageUrl(previewUrl); setFile(file); setSelectedImageName(file.name); // Add this line setUploadError(''); // Clear any previous errors }; const handleCompareImageUpload = (file) => { setShowResult(false); if (!file) { setCompareUploadError('Please select a file'); return; } // Check file size (2MB = 2 * 1024 * 1024 bytes) const maxSize = 2 * 1024 * 1024; if (file.size > maxSize) { setCompareUploadError('File size exceeds 2MB limit'); setCompareFile(null); setSelectedCompareImageName(''); return; } // Check file type const fileExtension = file.name.split('.').pop().toLowerCase(); const validExtensions = ['jpg', 'jpeg']; const validMimeTypes = ['image/jpeg', 'image/jpg']; if (!validExtensions.includes(fileExtension) || !validMimeTypes.includes(file.type)) { setCompareUploadError('Only JPG/JPEG files are allowed'); setCompareFile(null); setSelectedCompareImageName(''); return; } // Set file and filename if validation passes const previewUrl = URL.createObjectURL(file); setImageCompareUrl(previewUrl); setCompareFile(file); setSelectedCompareImageName(file.name); // Add this line setCompareUploadError(''); // Clear any previous errors }; const handleImageCancel = () => { setSelectedImageName(''); setFile(null); setImageUrl(''); setShowResult(false); if (fileInputRef.current) fileInputRef.current.value = ''; }; const handleCompareImageCancel = () => { setSelectedCompareImageName(''); setCompareFile(null); setImageCompareUrl(''); setShowResult(false); if (fileCompareInputRef.current) fileCompareInputRef.current.value = ''; }; const validateForm = () => { // Reset all error states setApplicationError(''); setThresholdError(''); setUploadError(''); setCompareUploadError(''); setErrorMessage(''); let isValid = true; // Validate Application ID if (!applicationId) { setApplicationError('Please select an Application ID before compare.'); isValid = false; } // Validate Threshold const selectedThreshold = thresholdIds.find(threshold => threshold.name === thresholdId)?.name; if (!selectedThreshold) { setThresholdError('Invalid threshold selected.'); isValid = false; } // Validate Images if (!selectedImageName) { setUploadError('Please upload a face photo before compare.'); isValid = false; } if (!selectedCompareImageName) { setCompareUploadError('Please upload a compare face photo before compare.'); isValid = false; } return isValid; }; const handleCheckClick = () => { if (!validateForm()) { return; } setIsLoading(true); setErrorMessage(''); const selectedThreshold = thresholdIds.find(threshold => threshold.name === thresholdId)?.name; const formData = new FormData(); formData.append('application_id', applicationId); formData.append('file1', file); formData.append('file2', compareFile); formData.append('threshold', selectedThreshold); // Log request data console.log('📝 Request Data:', { applicationId, threshold: selectedThreshold, file1: selectedImageName, file2: selectedCompareImageName, }); fetch(`${BASE_URL}/face_recognition/compare`, { method: 'POST', headers: { 'accept': 'application/json', 'x-api-key': API_KEY, }, body: formData, }) .then(response => { if (!response.ok) { return response.json().then(errorData => { throw new Error(errorData.detail || 'Comparison failed, please try again'); }); } return response.json(); }) .then(result => { console.log('📡 API Response:', result); const { result: compareResult } = result.details.data; // Process image URLs if (compareResult.image_url1) { fetchImage(compareResult.image_url1, setImageUrl); } if (compareResult.image_url2) { fetchImage(compareResult.image_url2, setImageCompareUrl); } // Update states setVerified(compareResult.verified); setResultImageLabel(selectedImageName); setResultCompareImageLabel(selectedCompareImageName); setSelectedQuota(compareResult.quota); setShowResult(true); }) .catch(error => { console.error('🔥 Request Failed:', error); setErrorMessage(error.message); setShowResult(false); }) .finally(() => { setIsLoading(false); }); }; const fetchImage = async (imageUrl, setImageUrl) => { setIsLoading(true); try { const response = await fetch(imageUrl, { method: 'GET', headers: { 'accept': 'application/json', 'x-api-key': `${API_KEY}`, // Ensure this is valid } }); if (!response.ok) { const errorDetails = await response.json(); console.error('Image fetch error details:', errorDetails); setErrorMessage('Failed to fetch image, please try again.'); return; } const imageBlob = await response.blob(); const imageData = URL.createObjectURL(imageBlob); console.log('Fetched image URL:', imageData); setImageUrl(imageData); // Set the state with the blob URL } catch (error) { console.error('Error fetching image:', error); setErrorMessage(error.message); } finally { setIsLoading(false); } }; const applicationOptions = applicationIds.map(app => ({ value: app.id, label: app.name })); const ResultsSection = ({ showResult, verified, imageUrl, selectedImageName, imageCompareUrl, selectedCompareImageName }) => ( showResult && (

Results

Similarity {verified !== null ? (verified ? 'True' : 'False') : 'N/A'}
Original Foto

File Name: {resultImageLabel}

Compare Foto

File Name: {resultCompareImageLabel}

) ); 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 } }; if (!isServer) { return (
Server Down Animation

Server tidak dapat diakses

{errorMessage || 'Silakan periksa koneksi internet Anda atau coba lagi nanti.'}

); } return (
{/* Inject keyframes for the spinner */} {isLoading && (

Loading...

)} {/* Application ID Selection */}
setTresholdId(e.target.value)} onFocus={handleFocus} onBlur={handleBlur} > {thresholdIds.map((app) => ( ))} {thresholdError && {thresholdError}}
{/* Upload Image #1 */}
{ setUploadError('Only JPG/JPEG files are allowed'); setFile(null); setSelectedImageName(''); }} children={

Drag and Drop Here

Or

Browse

Recommended size: 320x200 (Max File Size: 2MB)

Supported file types: JPG, JPEG

} /> {uploadError && ( {uploadError} )}
{/* Display uploaded image name */} {selectedImageName && (
Selected preview

File: {selectedImageName}

{file && (

Size: {formatFileSize(file.size)}

)}
)}
{errorMessage && {errorMessage}} {/* Upload Image #2 */}
{ setCompareUploadError('Only JPG/JPEG files are allowed'); setCompareFile(null); setSelectedCompareImageName(''); }} children={

Drag and Drop Here

Or

Browse

Recommended size: 320x200 (Max File Size: 2MB)

Supported file types: JPG, JPEG

} /> {compareUploadError && ( {compareUploadError} )}
{/* Display uploaded image name */} {selectedCompareImageName && (
Compare preview

File: {selectedCompareImageName}

{compareFile && (

Size: {formatFileSize(compareFile.size)}

)}
)}
{/* Submit Button */}
{/* Results Section */} {showResult && ( )}
) } export default Compare 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: '40svh', cursor: 'pointer', marginTop: '1rem', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', border: '1px solid #ced4da', borderRadius: '0.25rem', padding: '25px 10px 10px 10px' }, 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: { margin: '20px 0', padding: '10px', border: '1px solid #e0e0e0', borderRadius: '8px', backgroundColor: '#f9f9f9', }, resultContainer: { display: 'flex', flexDirection: 'column', alignItems: 'center', }, tableStyle: { width: '100%', borderCollapse: 'collapse', marginBottom: '20px', }, imagesWrapper: { display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: '2rem', width: '100%', margin: '2rem 0', }, imageContainer: { flex: '1 1 300px', maxWidth: '500px', minWidth: '280px', display: 'flex', flexDirection: 'column', alignItems: 'center', }, responsiveImage: { width: '100%', height: 'auto', maxHeight: '500px', objectFit: 'contain', borderRadius: '8px', boxShadow: '0 2px 4px rgba(0,0,0,0.1)', transition: 'transform 0.3s ease', cursor: 'pointer', '&:hover': { transform: 'scale(1.02)', }, }, imageLabel: { marginTop: '1rem', textAlign: 'center', fontSize: '0.9rem', color: '#666', }, similarityText: (verified) => ({ border: '0.1px solid gray', padding: '8px', color: verified ? 'green' : 'red', fontWeight: 'bold', }), 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', }, };