diff --git a/src/screens/Biometric/FaceRecognition/Section/Compare.jsx b/src/screens/Biometric/FaceRecognition/Section/Compare.jsx index 3e8d6db..243efac 100644 --- a/src/screens/Biometric/FaceRecognition/Section/Compare.jsx +++ b/src/screens/Biometric/FaceRecognition/Section/Compare.jsx @@ -3,7 +3,6 @@ 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 { height } from '@fortawesome/free-solid-svg-icons/fa0'; const Compare = () => { @@ -425,34 +424,20 @@ const Compare = () => { {/* Display uploaded image name */} {selectedImageName && ( -
-
- -
-
Uploaded File:
-

{selectedImageName}

- {file && ( -

- Size: {formatFileSize(file.size)} -

- )} -
-
- -
-
+
+

File: {selectedImageName}

+ {file && ( +

+ Size: {formatFileSize(file.size)} +

+ )} +
)}
+ {errorMessage && {errorMessage}} {/* Upload Image #2 */}
@@ -479,31 +464,16 @@ const Compare = () => { {/* Display uploaded image name */} {selectedCompareImageName && ( -
-
- -
-
Uploaded File:
-

{selectedCompareImageName}

- {file && ( -

- Size: {formatFileSize(file.size)} -

- )} -
-
- -
-
+
+

File: {selectedCompareImageName}

+ {compareFile && ( +

+ Size: {formatFileSize(compareFile.size)} +

+ )} +
)}
@@ -518,7 +488,14 @@ const Compare = () => { {/* Results Section */} {showResult && ( - + )}
) diff --git a/src/screens/Biometric/FaceRecognition/Section/Enroll.jsx b/src/screens/Biometric/FaceRecognition/Section/Enroll.jsx index 6456a6b..a50fcec 100644 --- a/src/screens/Biometric/FaceRecognition/Section/Enroll.jsx +++ b/src/screens/Biometric/FaceRecognition/Section/Enroll.jsx @@ -334,17 +334,16 @@ const Enroll = () => { }; const styles = { - // Existing styles formGroup: { marginTop: '-45px', }, selectWrapper: { position: 'relative', - marginTop: '0', + marginTop: '0', }, select: { width: '100%', - paddingRight: '30px', + paddingRight: '30px', }, chevronIcon: { position: 'absolute', @@ -354,10 +353,10 @@ const Enroll = () => { pointerEvents: 'none', }, remainingQuota: { - display: 'flex', + display: 'flex', flexDirection: 'row', alignItems: 'center', - marginTop: '4px', + marginTop: '4px', }, quotaText: { fontSize: '40px', @@ -367,36 +366,34 @@ const Enroll = () => { timesText: { marginLeft: '8px', verticalAlign: 'super', - fontSize: '20px', + fontSize: '20px', }, uploadArea: { - backgroundColor: '#e6f2ff', - height: '250px', // Default height for non-mobile devices - cursor: 'pointer', - marginTop: '1rem', - paddingTop: '22px', - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - border: '1px solid #ced4da', - borderRadius: '0.25rem', + backgroundColor: '#e6f2ff', + height: '250px', + cursor: 'pointer', + marginTop: '1rem', + paddingTop: '22px', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + border: '1px solid #ced4da', + borderRadius: '0.25rem', }, - - // Mobile responsive styles for upload area uploadAreaMobile: { - backgroundColor: '#e6f2ff', - height: '50svh', // Reduced height for mobile - cursor: 'pointer', - marginTop: '1rem', - paddingTop: '18px', // Adjusted padding for mobile - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - border: '1px solid #ced4da', - borderRadius: '0.25rem', - padding: '20px' + backgroundColor: '#e6f2ff', + height: '50svh', + cursor: 'pointer', + marginTop: '1rem', + paddingTop: '18px', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + border: '1px solid #ced4da', + borderRadius: '0.25rem', + padding: '20px', }, uploadIcon: { fontSize: '40px', @@ -410,81 +407,79 @@ const Enroll = () => { 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', + border: '1px solid #ddd', + borderRadius: '6px', + padding: '18px 10px 0 8px', + height: '13svh', + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + backgroundColor: '#f9f9f9', + overflow: 'hidden', }, fileWrapper: { - display: 'flex', - alignItems: 'center', - flex: '1', + 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' + flex: '1', + fontSize: '16px', + marginLeft: '6px', + overflow: 'hidden', + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', + marginTop: '1rem', }, fileSize: { - fontSize: '12px', - color: '#555', - marginBottom: '2rem', + fontSize: '12px', + color: '#555', + marginBottom: '2rem', }, closeButtonContainer: { - display: 'flex', - alignItems: 'center', - marginLeft: 'auto', + display: 'flex', + alignItems: 'center', + marginLeft: 'auto', }, closeButton: { - background: 'transparent', - border: 'none', - cursor: 'pointer', - padding: '0', + background: 'transparent', + border: 'none', + cursor: 'pointer', + padding: '0', }, imageIcon: { - color: '#0542cc', - fontSize: '18px', // Ukuran ikon sedikit lebih kecil - marginRight: '6px', + color: '#0542cc', + fontSize: '18px', + marginRight: '6px', }, closeIcon: { - color: 'red', - fontSize: '18px', + color: 'red', + fontSize: '18px', }, submitButton: { marginLeft: 'auto', marginTop: '4rem', textAlign: 'start', - position: 'relative', - zIndex: 1, + position: 'relative', + zIndex: 1, }, uploadError: { color: 'red', fontSize: '12px', marginTop: '5px', }, - - // New styles added and merged containerResultStyle: { - padding: '20px', - border: '1px solid #0053b3', - borderRadius: '5px', - width: '100%', - margin: '20px auto', + padding: '20px', + border: '1px solid #0053b3', + borderRadius: '5px', + width: '100%', + margin: '20px auto', }, resultContainer: { display: 'flex', - justifyContent: 'space-between', // Horizontal alignment - alignItems: 'flex-start', // Align items at the top - flexDirection: isMobile ? 'column' : 'row', // Stack vertically on mobile + justifyContent: 'space-between', + alignItems: 'flex-start', + flexDirection: isMobile ? 'column' : 'row', width: '100%', }, resultsTable: { @@ -512,9 +507,9 @@ const Enroll = () => { imageContainer: { display: 'flex', flexDirection: 'column', - alignItems: isMobile ? 'center' : 'flex-start', // Center image on mobile + alignItems: isMobile ? 'center' : 'flex-start', width: '100%', - marginTop: isMobile ? '10px' : '0', // Add margin for spacing on mobile + marginTop: isMobile ? '10px' : '0', }, imageStyle: { width: '300px', @@ -522,13 +517,13 @@ const Enroll = () => { borderRadius: '5px', }, imageStyleMobile: { - width: '100%', // Make image responsive on mobile + width: '100%', height: 'auto', borderRadius: '5px', }, imageDetails: { marginTop: '10px', - fontSize: isMobile ? '14px' : '16px', // Adjust font size on mobile + fontSize: isMobile ? '14px' : '16px', color: '#1f2d3d', }, loadingOverlay: { @@ -539,7 +534,7 @@ const Enroll = () => { bottom: 0, backgroundColor: 'rgba(0, 0, 0, 0.2)', display: 'flex', - flexDirection: 'column', + flexDirection: 'column', justifyContent: 'center', alignItems: 'center', zIndex: 1000, @@ -573,34 +568,14 @@ const Enroll = () => { marginTop: '0.2rem', }, uploadedFileText: { - fontSize: '16px', - color: '#1f2d3d', - }, - resultsTable: { - width: '60%', - borderCollapse: 'collapse', - }, - resultsRow: { - border: '0.1px solid gray', - padding: '8px', - }, - resultsCell: { - padding: '8px', - width: '30%', - }, - resultsValueCell: { - padding: '8px', - width: '70%', - color: 'red', - }, - resultsTrueValue: { - color: 'inherit', + fontSize: '16px', + color: '#1f2d3d', }, customLabel: { - fontWeight: 600, fontSize: '14px', color: '#212529' + fontWeight: 600, + fontSize: '14px', + color: '#212529', }, - - // Mobile responsiveness adjustments (if necessary) responsiveImageStyle: { width: '100%', maxHeight: '250px', @@ -644,195 +619,168 @@ const Enroll = () => { color: 'white', marginTop: '10px', }, - } - + }; + return ( -
- {/* Inject keyframes for the spinner */} - - - {isLoading && ( -
-
-

Loading...

-
- )} - - {/* Application ID Selection */} -
-
-
- fetchSubjectIds(applicationId)} - maxLength={15} - /> - {subjectError && {subjectError}} - {subjectAvailabilityMessage && ( - - {subjectAvailabilityMessage} - - )} -
-
- - {/* Upload Section */} -
-
- - Upload Face Photo - - { - if (files && files[0]) { - handleImageUpload(files[0]); - } else { - console.error('No valid files dropped'); - } - }} - children={( -
- -

Drag and Drop Here

-

Or

- fileInputRef.current.click()}>Browse -

Recommended size: 300x300 (Max File Size: 2MB)

-

Supported file types: JPG, JPEG

-
- )} - /> - handleImageUpload(e.target.files[0])} - /> - {imageError && {imageError}} -
-
- - {/* Display uploaded image name */} - {selectedImageName && ( -
-
- -
-
Uploaded File:
-

{selectedImageName}

- {file && ( -

- Size: {formatFileSize(file.size)} -

- )} -
-
- -
-
-
- )} - - {/* Submit Button */} -
- -
- - {/* Result Section */} - {showResult && ( -
-

Results

-
- {/* Table Styling: responsive */} - - - - - - - {/* More rows can go here */} - -
SimilarityTrue
- - {/* Image and Details Container */} -
- Contoh Foto + + + {isLoading && ( +
+
+

Loading...

+
+ )} + +
+
+
+ fetchSubjectIds(applicationId)} + maxLength={15} + /> + {subjectError && {subjectError}} + {subjectAvailabilityMessage && ( + + {subjectAvailabilityMessage} + + )} +
+
+ +
+
+ + Upload Face Photo + + { + if (files && files[0]) { + handleImageUpload(files[0]); + } + }} + children={ +
+ +

Drag and Drop Here

+

Or

+ fileInputRef.current.click()}>Browse +

Recommended size: 300x300 (Max File Size: 2MB)

+

Supported file types: JPG, JPEG

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

File: {selectedImageName}

+ {file && ( +

+ Size: {formatFileSize(file.size)} +

+ )} + +
+ )} + {errorMessage && {errorMessage}} + +
+ +
+ + {showResult && ( +
+

Results

+
+ + + + + + + +
SimilarityTrue
+
+ Contoh Foto +

+ {selectedImageName} +

+
+
+
+ )} +
); + } export default Enroll; diff --git a/src/screens/Biometric/FaceRecognition/Section/Search.jsx b/src/screens/Biometric/FaceRecognition/Section/Search.jsx index d0e8a3a..e952e42 100644 --- a/src/screens/Biometric/FaceRecognition/Section/Search.jsx +++ b/src/screens/Biometric/FaceRecognition/Section/Search.jsx @@ -121,25 +121,25 @@ const Search = () => { }; const handleImageUpload = (file) => { - // Ensure file exists before accessing its properties - if (!file) { - console.error('File is undefined'); - setImageError('Please upload a valid image file.'); - return; - } - - const fileExtension = file.name.split('.').pop().toUpperCase(); - if (fileTypes.includes(fileExtension)) { - setSelectedImageName(file.name); - setFile(file); - setImageError(''); // Clear any previous errors + // Ensure the file is not undefined or null before accessing its properties + if (file && file.name) { + const fileExtension = file.name.split('.').pop().toUpperCase(); + if (fileTypes.includes(fileExtension)) { + setSelectedImageName(file.name); + setFile(file); + setUploadedFile(file); // Set uploadedFile to the selected file + setImageError(''); // Clear any previous errors + } else { + alert('Image format is not supported'); + setImageError('Image format is not supported'); + setFile(null); + setUploadedFile(null); // Clear uploadedFile if the file is unsupported + } } else { - // Show an alert if the file type is not supported - alert('Image format is not supported'); - setImageError('Image format is not supported'); // Optionally set error message to display on the UI - setFile(null); // Optionally clear the selected file + console.error('No file selected or invalid file object.'); } - }; + }; + const handleImageCancel = () => { @@ -156,87 +156,86 @@ const Search = () => { setLimitIdError(''); setImageError(''); setErrorMessage(''); - + // Initialize validation flags let hasError = false; - + // Validate Application ID if (!applicationId) { - setApplicationIdError('Please select an Application ID before searching.'); - hasError = true; // Set error flag + setApplicationIdError('Please select an Application ID before searching.'); + hasError = true; } - + // Validate Limit ID if (!limitId) { - setLimitIdError('Please select a Limit before searching.'); - hasError = true; // Set error flag + setLimitIdError('Please select a Limit before searching.'); + hasError = true; } - + // Validate Image Upload if (!selectedImageName) { - setImageError('Please upload an image file.'); - hasError = true; // Set error flag + setImageError('Please upload an image file.'); + hasError = true; } - + // Check if the file is uploaded if (!uploadedFile) { - setErrorMessage('Please upload an image file.'); - hasError = true; // Set error flag + setErrorMessage('Please upload an image file.'); + hasError = true; } - + // If any errors were found, do not proceed if (hasError) { - return; + return; } - + const parsedLimitId = parseInt(limitId, 10); - + const formData = new FormData(); formData.append('application_id', applicationId); formData.append('threshold', 1); formData.append('limit', parsedLimitId); formData.append('file', uploadedFile, uploadedFile.name); // Use the uploaded file - + setIsLoading(true); - + try { - const response = await fetch(`${BASE_URL}/face_recognition/search`, { - method: 'POST', - headers: { - 'accept': 'application/json', - 'x-api-key': `${API_KEY}`, - }, - body: formData, - }); - - const data = await response.json(); - - console.log('Response Data:', data); // Log the response - - if (response.ok) { - const resultsArray = Array.isArray(data.details.data) ? data.details.data : []; - const processedResults = resultsArray.map(item => ({ - identity: item.identity, - similarity: item.similarity, - imageUrl: item.image_url, - distance: item.distance, - })); - - // Fetch images using their URLs - await Promise.all(processedResults.map(async result => { - const imageFileName = result.imageUrl.split('/').pop(); // Extract file name if needed - await fetchImage(imageFileName); // Fetch image - console.log('multiple image data: ', result.imageUrl); // Log the URL - })); - - setResults(processedResults); - setShowResult(true); - - } else { - console.error('Error response:', JSON.stringify(data, null, 2)); - const errorMessage = data.message || data.detail || data.details?.message || 'An unknown error occurred.'; - setErrorMessage(errorMessage); - } + const response = await fetch(`${BASE_URL}/face_recognition/search`, { + method: 'POST', + headers: { + 'accept': 'application/json', + 'x-api-key': `${API_KEY}`, + }, + body: formData, + }); + + const data = await response.json(); + + console.log('Response Data:', data); + + if (response.ok) { + const resultsArray = Array.isArray(data.details.data) ? data.details.data : []; + const processedResults = resultsArray.map(item => ({ + identity: item.identity, + similarity: item.similarity, + imageUrl: item.image_url, + distance: item.distance, + })); + + // Fetch images using their URLs + await Promise.all(processedResults.map(async result => { + const imageFileName = result.imageUrl.split('/').pop(); // Extract file name if needed + await fetchImage(imageFileName); // Fetch image + console.log('multiple image data: ', result.imageUrl); + })); + + setResults(processedResults); + setShowResult(true); + } else { + console.error('Error response:', JSON.stringify(data, null, 2)); + 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.'); @@ -245,6 +244,7 @@ const Search = () => { } }; + const fetchImage = async (imageFileName) => { setIsLoading(true); try { @@ -322,7 +322,7 @@ const Search = () => { }, uploadArea: { backgroundColor: '#e6f2ff', - height: '50svh', + height: '40svh', cursor: 'pointer', marginTop: '1rem', padding: '1rem', @@ -438,18 +438,18 @@ const Search = () => { backgroundColor: '#f7f7f7', borderRadius: '8px', margin: '1rem', - width: isMobile ? '100%' : '50%', + width: '100%', // Full width for large screens }, resultContainer: { display: 'flex', - flexDirection: isMobile ? 'column' : 'row', + flexDirection: 'row', flexWrap: 'wrap', gap: '1rem', - justifyContent: 'center', + justifyContent: 'center', }, resultItem: { display: 'flex', - flexDirection: isMobile ? 'row' : 'column', + flexDirection: 'column', alignItems: 'center', textAlign: 'center', padding: '0.5rem', @@ -457,10 +457,10 @@ const Search = () => { border: '1px solid #ddd', borderRadius: '8px', boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.1)', - width: isMobile ? '100%' : '150px', + width: '150px', // Default width for larger screens }, resultTextContainer: { - marginBottom: isMobile ? '0' : '0.5rem', + marginBottom: '0.5rem', }, resultText: { fontSize: '0.9rem', @@ -471,9 +471,21 @@ const Search = () => { width: '80px', height: '80px', borderRadius: '50%', - marginTop: isMobile ? '0' : '0.5rem', + marginTop: '0.5rem', + }, + + // Media query for mobile responsiveness + '@media screen and (max-width: 768px)': { + containerResultStyle: { + width: '100%', // Full width on mobile + }, + resultContainer: { + flexDirection: 'column', // Stack results vertically on small screens + }, + resultItem: { + width: '100%', // Make result items take full width on mobile + }, }, - }; return ( @@ -599,34 +611,21 @@ const Search = () => {
- {selectedImageName && ( -
-
- -
-
Uploaded File:
-

{selectedImageName}

- {file && ( -

- Size: {formatFileSize(file.size)} -

- )} -
-
- -
+ {selectedImageName && ( +
+

File: {selectedImageName}

+ {file && ( +

+ Size: {formatFileSize(file.size)} +

+ )} +
-
- )} + )} + + {errorMessage && {errorMessage}} {/* Submit Button */}
diff --git a/src/screens/Biometric/FaceRecognition/Section/Verify.jsx b/src/screens/Biometric/FaceRecognition/Section/Verify.jsx index d8a0716..11ec051 100644 --- a/src/screens/Biometric/FaceRecognition/Section/Verify.jsx +++ b/src/screens/Biometric/FaceRecognition/Section/Verify.jsx @@ -78,203 +78,6 @@ const Verify = () => { 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"); @@ -325,35 +128,23 @@ const Verify = () => { } }; - - 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 + // Ensure the file is not undefined or null before accessing its properties + if (file && file.name) { + const fileExtension = file.name.split('.').pop().toUpperCase(); + if (fileTypes.includes(fileExtension)) { + setSelectedImageName(file.name); + setFile(file); + setUploadError(''); // Clear any previous errors + } else { + alert('Image format is not supported'); + setUploadError('Image format is not supported'); + setFile(null); + } } 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 + console.error('No file selected or invalid file object.'); } - }; + }; const handleImageCancel = () => { @@ -509,6 +300,295 @@ const Verify = () => { } }; + 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', + paddingTop: '22px', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + border: '1px solid #ced4da', + borderRadius: '0.25rem', + }, + uploadAreaMobile: { + backgroundColor: '#e6f2ff', + height: '50svh', // Use viewport height for a more responsive size + cursor: 'pointer', + marginTop: '1rem', + paddingTop: '18px', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + border: '1px solid #ced4da', + borderRadius: '0.25rem', + padding: '20px', + }, + 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', + height: '13svh', + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + backgroundColor: '#f9f9f9', + overflow: 'hidden', + }, + fileWrapper: { + display: 'flex', + alignItems: 'center', + flex: '1', + }, + textContainer: { + flex: '1', + fontSize: '16px', + 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', + 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%', + margin: '20px auto', + }, + resultContainer: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'flex-start', + flexDirection: 'row', + width: '100%', + flexWrap: 'wrap', // Allow wrapping on smaller screens + }, + resultsTable: { + width: '60%', + borderCollapse: 'collapse', + }, + resultsTableMobile: { + width: '100%', + borderCollapse: 'collapse', + }, + resultsCell: { + padding: '8px', + width: '30%', + fontSize: '16px', + }, + resultsValueCell: { + padding: '8px', + width: '70%', + fontSize: '16px', + color: 'red', + }, + resultsTrueValue: { + color: 'inherit', + }, + imageContainer: { + display: 'flex', + flexDirection: 'column', + alignItems: 'flex-start', + width: '100%', + marginTop: '20px', + }, + imageStyle: { + width: isMobile ? '200px' : '320px', // 200px for mobile, 320px for larger screens + height: isMobile ? '200px' : '320px', + borderRadius: '5px', + objectFit: 'cover', // Ensures the image fits within the specified dimensions + }, + imageStyleMobile: { + width: '100%', + height: 'auto', + borderRadius: '5px', + }, + imageDetails: { + marginTop: '10px', + fontSize: '16px', + color: '#1f2d3d', + }, + 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', + }, + uploadedFileWrapper: { + backgroundColor: '#fff', + border: '0.2px solid gray', + padding: '15px 0 0 17px', + borderRadius: '5px', + display: 'flex', + alignItems: 'center', + gap: '10px', + justifyContent: 'space-between', + }, + uploadedFileInfo: { + marginRight: '18rem', + marginTop: '0.2rem', + }, + uploadedFileText: { + fontSize: '16px', + color: '#1f2d3d', + }, + customLabel: { + fontWeight: 600, + fontSize: '14px', + color: '#212529', + }, + responsiveImageStyle: { + width: '100%', + maxHeight: '250px', + objectFit: 'cover', + marginTop: '20px', + }, + responsiveResultContainer: { + padding: '1rem', + border: '1px solid #ccc', + borderRadius: '8px', + marginTop: '20px', + }, + responsiveImageContainer: { + marginTop: '20px', + textAlign: 'center', + }, + responsiveSubmitButton: { + marginTop: '1rem', + }, + responsiveLoadingOverlay: { + position: 'absolute', + top: '0', + left: '0', + width: '100%', + height: '100%', + backgroundColor: 'rgba(0,0,0,0.5)', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + zIndex: '10', + }, + responsiveSpinner: { + border: '4px solid #f3f3f3', + borderTop: '4px solid #3498db', + borderRadius: '50%', + width: '50px', + height: '50px', + animation: 'spin 2s linear infinite', + }, + responsiveLoadingText: { + color: 'white', + marginTop: '10px', + }, + }; return (
@@ -533,16 +613,16 @@ const Verify = () => {
option.value === subjectId)} - onChange={selectedOption => setSubjectId(selectedOption ? selectedOption.value : '')} - onInputChange={(value) => { - // Check if input value length is within the 15-character limit - if (value.length <= 15) { - setInputValue(value); // Set the input value if within limit - } - }} - onFocus={() => fetchSubjectIds(applicationId)} // Fetch subject IDs on focus - placeholder="Enter Subject ID" - isClearable - noOptionsMessage={() => ( -
Subject ID not registered.
- )} - inputValue={inputValue} // Bind the inputValue state to control the input - /> - {subjectError && {subjectError}} - {subjectAvailabilityMessage && ( - - {subjectAvailabilityMessage} - - )} -
- -
-
- - - {thresholdError && {thresholdError}} -
-
-
- - {/* Upload Section */} + {/* Subject ID Input */} +
-
- - Upload Face Photo - - handleImageUpload(files[0])} - children={ -
- -

Drag and Drop Here

-

Or

- fileInputRef.current.click()}>Browse -

Recommended size: 300x300 (Max File Size: 2MB)

-

Supported file types: JPG, JPEG

-
+ { + setThresholdId(e.target.value); + setThresholdError(''); // Clear error if valid + }} + > + + {thresholdIds.map((app) => ( + + ))} + + - {uploadError && {uploadError}} + {thresholdError && {thresholdError}}
- - {/* Display uploaded image name */} - {selectedImageName && ( -
-
- -
-
Uploaded File:
-

{selectedImageName}

- {file && ( -

- Size: {formatFileSize(file.size)} -

- )} -
-
- -
+
+ + {/* Upload Section */} +
+
+ + Upload Face Photo + + handleImageUpload(files[0])} + children={ +
+ +

Drag and Drop Here

+

Or

+ fileInputRef.current.click()}>Browse +

Recommended size: 300x300 (Max File Size: 2MB)

+

Supported file types: JPG, JPEG

-
- )} - - {/* Submit Button */} -
-
+
+ + {/* Display uploaded image name */} + {selectedImageName && ( +
+

File: {selectedImageName}

+ {file && ( +

+ Size: {formatFileSize(file.size)} +

+ )} +
+ )} + {errorMessage && {errorMessage}} - {/* Results Section */} - {showResult && ( -
-

Results

-
- - - - - - - -
Similarity - {verified !== null ? (verified ? 'True' : 'False') : 'N/A'} -
+ {/* Submit Button */} +
+ +
-
- Example Image -

- File Name: {selectedImageName} -

-
+ {/* Results Section */} + {showResult && ( +
+

Results

+
+ + + + + + + +
Similarity + {verified !== null ? (verified ? 'True' : 'False') : 'N/A'} +
+ +
+ Example Image +

+ File Name: {selectedImageName} +

- )} +
+ )}
); + } export default Verify; diff --git a/src/screens/Biometric/OcrKtp/Verify.jsx b/src/screens/Biometric/OcrKtp/Verify.jsx index 06fa901..b55dde1 100644 --- a/src/screens/Biometric/OcrKtp/Verify.jsx +++ b/src/screens/Biometric/OcrKtp/Verify.jsx @@ -27,6 +27,7 @@ const Verify = () => { 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({ @@ -87,7 +88,14 @@ const Verify = () => { }; 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 => ({ @@ -220,8 +228,6 @@ const Verify = () => { } }; - - // The fetchImage function you already have in your code const fetchImage = async (imageFileName) => { setIsLoading(true); @@ -257,6 +263,132 @@ const Verify = () => { } }; + + 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 */} @@ -315,11 +447,11 @@ const Verify = () => { {validationErrors.applicationId &&

{validationErrors.applicationId}

}
-

+

Remaining Quota

- 0 + {selectedQuota} (times)
@@ -362,6 +494,11 @@ const Verify = () => { {selectedImageName && (

File: {selectedImageName}

+ {file && ( +

+ Size: {formatFileSize(file.size)} +

+ )} @@ -477,117 +614,3 @@ const Verify = () => { export default Verify; -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: '58svh', - 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', - }, -};