Fixing Issue

This commit is contained in:
Rizqika 2024-12-05 17:59:06 +07:00
parent 2a2662df25
commit 2a559da0d3
7 changed files with 905 additions and 548 deletions

View File

@ -1,263 +1,193 @@
// src/components/dataMenu.js
const dataMenu = [
{
items: [
{
name: 'Main Dashboard', // Changed the name
target: 'collapseHome',
subMenus: [
{
name: 'Getting Started',
link: '/getting-started'
},
{
name: 'Dashboard Overview', // Changed the name
link: '/dashboard'
},
{
name: 'Application Settings', // Changed the name
link: '/application'
},
],
},
],
iconClass: 'fas fa-tachometer-alt',
},
{
items: [
{
name: 'Biometric Systems', // Changed the name
target: 'collapseBiometric',
subMenus: [
{
name: 'Face Recognition System', // Changed the name
target: 'collapseFaceRecog',
subMenus: [
{ name: 'Verify Identity', link: '/face-verify'}, // Changed the name
{ name: 'Summary Report', link: '/face-summary'}, // Changed the name
{ name: 'Transaction Log', link: '/face-transaction'}, // Changed the name
],
},
{
name: 'KTP OCR', // Changed the name
target: 'collapseOcrKtp',
subMenus: [
{ name: 'Verify KTP', link: '/ktp-verify'}, // Changed the name
{ name: 'Manage Basic Auth', link: '/ktp-manage'},
{ name: 'Summary of KTPs', link: '/ktp-summary'}, // Changed the name
{ name: 'KTP Transaction History', link: '/ktp-transaction'}, // Changed the name
],
},
{
name: 'NPWP OCR', // Changed the name
target: 'collapseOcrNpwp',
subMenus: [
{ name: 'Verify NPWP', link: '/npwp-verify'}, // Changed the name
{ name: 'NPWP Summary', link: '/npwp-summary'}, // Changed the name
{ name: 'NPWP Transaction Log', link: '/npwp-transaction'}, // Changed the name
],
},
{
name: 'SIM OCR', // Changed the name
target: 'collapseOcrSim',
subMenus: [
{ name: 'Verify SIM', link: '/sim-verify'}, // Changed the name
{ name: 'SIM Summary', link: '/sim-summary'}, // Changed the name
{ name: 'SIM Transaction Log', link: '/sim-transaction'}, // Changed the name
],
},
{
name: 'Document OCR', // Changed the name
target: 'collapseOcrDocument',
subMenus: [
{ name: 'Verify Document', link: '/document-verify'}, // Changed the name
{ name: 'Document Summary', link: '/document-summary'}, // Changed the name
{ name: 'Document Transaction History', link: '/document-transaction'}, // Changed the name
],
},
],
},
],
iconClass: 'fas fa-user',
},
{
items: [
{
name: 'SMS Services', // Changed the name
target: 'collapseSms',
subMenus: [
{
name: 'SMS Verification', // Changed the name
link: '/sms-verify'
},
{
name: 'SMS OTP Management', // Changed the name
target: 'collapseSmsOtp',
subMenus: [
{ name: 'Settings', link: '/sms-otp-settings'},
{ name: 'Summary Report', link: '/sms-otp-summary'}, // Changed the name
{ name: 'Transaction Log', link: '/sms-otp-transaction'}, // Changed the name
{ name: 'Detail View', link: '/sms-otp-detail'}, // Changed the name
],
},
{
name: 'SMS Announcements', // Changed the name
target: 'collapseAnnouncement',
subMenus: [
{ name: 'Bulk Message', link: '/sms-announcement-bulk'}, // Changed the name
{ name: 'Announcement Summary', link: '/sms-announcement-summary'}, // Changed the name
{ name: 'Transaction Logs', link: '/sms-announcement-transaction'},
],
},
{
name: 'Blocked Numbers', // Changed the name
link: '/sms-block'
},
{
name: 'SMS Anomaly Report', // Changed the name
link: '/sms-anomaly'
},
],
},
],
iconClass: 'fas fa-phone',
},
{
items: [
{
name: 'WhatsApp Communication', // Changed the name
target: 'collapseWa',
subMenus: [
{
name: 'Verify WhatsApp Account', // Changed the name
link: '/wa-verify'
},
{
name: 'WhatsApp Management', // Changed the name
target: 'collapseWaManage',
subMenus: [
{ name: 'Register Business Account', link: '/wa-registration'}, // Changed the name
{ name: 'WhatsApp Profile Settings', link: '/wa-profile'}, // Changed the name
{ name: 'Message Templates', link: '/wa-template'}, // Changed the name
{ name: 'Integration Settings', link: '/wa-integration'}, // Changed the name
],
},
{
name: 'WhatsApp Activity', // Changed the name
target: 'collapseActivity',
subMenus: [
{ name: 'Settings', link: '/wa-settings'}, // Changed the name
{ name: 'Activity Summary', link: '/wa-summary'}, // Changed the name
{ name: 'WA Transaction Logs', link: '/wa-transaction'},
{ name: 'Bulk Sending', link: '/wa-bulk'}, // Changed the name
],
},
{
name: 'WhatsApp Inbox', // Changed the name
link: '/wa-inbox'
},
{
name: 'Blocked WhatsApp Numbers', // Changed the name
link: '/wa-block'
},
],
},
],
iconClass: 'fab fa-whatsapp',
},
{
items: [
{
name: 'Identity Verification', // Changed the name
target: 'collapseIdentify',
subMenus: [
{
name: 'Electronic Certificate Verification', // Changed the name
target: 'collapseElectro',
subMenus: [
{ name: 'Verify Certificate', link: '/identity-electro-verify'}, // Changed the name
{ name: 'Electronic Transaction', link: '/identity-electro-transaction'},
],
},
{
name: 'NPWP Verification', // Changed the name
target: 'collapseIdentifyNpwp',
subMenus: [
{ name: 'Npwp Transaction', link: '/identity-npwp-transaction'}
],
},
{
name: 'Tax Number Verification', // Changed the name
target: 'collapseTax',
subMenus: [
{ name: 'Verify Tax Number', link: '/identity-tax-verify'}, // Changed the name
{ name: 'Tax Transaction', link: '/identity-tax-transaction'}
],
},
{
name: 'Income Verification', // Changed the name
target: 'collapseIncome',
subMenus: [
{ name: 'Verify Income', link: '/identity-income-verify'}, // Changed the name
{ name: 'Income Transaction', link: '/identity-income-transaction'}
],
},
{
name: 'ID Verification', // Changed the name
target: 'collapseIdVerification',
subMenus: [
{ name: 'Verify ID', link: '/identity-id-verify'}, // Changed the name
{ name: 'Verify ID Transaction', link: '/identity-id-transaction'}
],
},
],
},
],
iconClass: 'fas fa-edit',
},
{
items: [
{
name: 'Watchlist Management', // Changed the name
target: 'collapseWatchlist',
subMenus: [
{
name: 'Watchlist Screening', // Changed the name
target: 'collapseScreening',
subMenus: [
{ name: 'Verify Watchlist', link: '/watchlist-screening-verify'}, // Changed the name
{ name: 'Admin Settings', link: '/watchlist-screening-admin'},
{ name: 'Search Watchlist', link: '/watchlist-screening-search'}, // Changed the name
{ name: 'Transaction Logs', link: '/watchlist-screening-transaction'},
{ name: 'Monitor Watchlist', link: '/watchlist-screening-monitor'},
],
},
],
},
],
iconClass: 'fas fa-calendar',
},
{
items: [
{
name: 'File Management', // Changed the name
target: 'collapseFiles',
subMenus: [
{
name: 'File Screening', // Changed the name
target: 'collapseScreening',
subMenus: [
{ name: 'Verify File', link: '/files-screening-verify'}, // Changed the name
{ name: 'Search Files', link: '/files-screening-search'}, // Changed the name
{ name: 'File Management Settings', link: '/files-screening-admin'},
],
},
],
},
],
iconClass: 'fas fa-cogs',
}
const createMenuItem = (name, link) => ({ name, link });
const createSubMenu = (name, target, subMenus) => ({ name, target, subMenus });
const mainDashboardMenus = [
createMenuItem('Getting Started', '/getting-started'),
createMenuItem('Dashboard Overview', '/dashboard'),
createMenuItem('Application Settings', '/application')
];
export default dataMenu;
const biometricSubMenus = {
faceRecognition: [
createMenuItem('Verify Identity', '/face-verify'),
createMenuItem('Summary Report', '/face-summary'),
createMenuItem('Transaction Log', '/face-transaction')
],
ktpOcr: [
createMenuItem('Verify KTP', '/ktp-verify'),
createMenuItem('Manage Basic Auth', '/ktp-manage'),
createMenuItem('Summary of KTPs', '/ktp-summary'),
createMenuItem('KTP Transaction History', '/ktp-transaction')
],
npwpOcr: [
createMenuItem('Verify NPWP', '/npwp-verify'),
createMenuItem('NPWP Summary', '/npwp-summary'),
createMenuItem('NPWP Transaction Log', '/npwp-transaction')
],
simOcr: [
createMenuItem('Verify SIM', '/sim-verify'),
createMenuItem('SIM Summary', '/sim-summary'),
createMenuItem('SIM Transaction Log', '/sim-transaction')
],
documentOcr: [
createMenuItem('Verify Document', '/document-verify'),
createMenuItem('Document Summary', '/document-summary'),
createMenuItem('Document Transaction History', '/document-transaction')
]
};
const smsServiceMenus = {
smsOtp: [
createMenuItem('Settings', '/sms-otp-settings'),
createMenuItem('Summary Report', '/sms-otp-summary'),
createMenuItem('Transaction Log', '/sms-otp-transaction'),
createMenuItem('Detail View', '/sms-otp-detail')
],
announcement: [
createMenuItem('Bulk Message', '/sms-announcement-bulk'),
createMenuItem('Announcement Summary', '/sms-announcement-summary'),
createMenuItem('Transaction Logs', '/sms-announcement-transaction')
]
};
const whatsAppMenus = {
management: [
createMenuItem('Register Business Account', '/wa-registration'),
createMenuItem('WhatsApp Profile Settings', '/wa-profile'),
createMenuItem('Message Templates', '/wa-template'),
createMenuItem('Integration Settings', '/wa-integration')
],
activity: [
createMenuItem('Settings', '/wa-settings'),
createMenuItem('Activity Summary', '/wa-summary'),
createMenuItem('WA Transaction Logs', '/wa-transaction'),
createMenuItem('Bulk Sending', '/wa-bulk')
]
};
const identityMenus = {
electronic: [
createMenuItem('Verify Certificate', '/identity-electro-verify'),
createMenuItem('Electronic Transaction', '/identity-electro-transaction')
],
npwp: [
createMenuItem('Npwp Transaction', '/identity-npwp-transaction')
],
tax: [
createMenuItem('Verify Tax Number', '/identity-tax-verify'),
createMenuItem('Tax Transaction', '/identity-tax-transaction')
],
income: [
createMenuItem('Verify Income', '/identity-income-verify'),
createMenuItem('Income Transaction', '/identity-income-transaction')
],
id: [
createMenuItem('Verify ID', '/identity-id-verify'),
createMenuItem('Verify ID Transaction', '/identity-id-transaction')
]
};
const watchlistMenus = {
screening: [
createMenuItem('Verify Watchlist', '/watchlist-screening-verify'),
createMenuItem('Admin Settings', '/watchlist-screening-admin'),
createMenuItem('Search Watchlist', '/watchlist-screening-search'),
createMenuItem('Transaction Logs', '/watchlist-screening-transaction'),
createMenuItem('Monitor Watchlist', '/watchlist-screening-monitor')
]
};
const fileMenus = {
screening: [
createMenuItem('Verify File', '/files-screening-verify'),
createMenuItem('Search Files', '/files-screening-search'),
createMenuItem('File Management Settings', '/files-screening-admin')
]
};
const dataMenu = [
{
items: [createSubMenu('Home', 'collapseHome', mainDashboardMenus)],
iconClass: 'fas fa-tachometer-alt'
},
{
items: [{
name: 'Biometric',
target: 'collapseBiometric',
subMenus: [
createSubMenu('Face Recognition System', 'collapseFaceRecog', biometricSubMenus.faceRecognition),
createSubMenu('KTP OCR', 'collapseOcrKtp', biometricSubMenus.ktpOcr),
createSubMenu('NPWP OCR', 'collapseOcrNpwp', biometricSubMenus.npwpOcr),
createSubMenu('SIM OCR', 'collapseOcrSim', biometricSubMenus.simOcr),
createSubMenu('Document OCR', 'collapseOcrDocument', biometricSubMenus.documentOcr)
]
}],
iconClass: 'fas fa-user'
},
{
items: [{
name: 'SMS',
target: 'collapseSms',
subMenus: [
createMenuItem('SMS Verification', '/sms-verify'),
createSubMenu('SMS OTP Management', 'collapseSmsOtp', smsServiceMenus.smsOtp),
createSubMenu('SMS Announcements', 'collapseAnnouncement', smsServiceMenus.announcement),
createMenuItem('Blocked Numbers', '/sms-block'),
createMenuItem('SMS Anomaly Report', '/sms-anomaly')
]
}],
iconClass: 'fas fa-phone'
},
{
items: [{
name: 'WhatsApp',
target: 'collapseWa',
subMenus: [
createMenuItem('Verify WhatsApp Account', '/wa-verify'),
createSubMenu('WhatsApp Management', 'collapseWaManage', whatsAppMenus.management),
createSubMenu('WhatsApp Activity', 'collapseActivity', whatsAppMenus.activity),
createMenuItem('WhatsApp Inbox', '/wa-inbox'),
createMenuItem('Blocked WhatsApp Numbers', '/wa-block')
]
}],
iconClass: 'fab fa-whatsapp'
},
{
items: [{
name: 'Identity',
target: 'collapseIdentify',
subMenus: [
createSubMenu('Electronic Certificate Verification', 'collapseElectro', identityMenus.electronic),
createSubMenu('NPWP Verification', 'collapseIdentifyNpwp', identityMenus.npwp),
createSubMenu('Tax Number Verification', 'collapseTax', identityMenus.tax),
createSubMenu('Income Verification', 'collapseIncome', identityMenus.income),
createSubMenu('ID Verification', 'collapseIdVerification', identityMenus.id)
]
}],
iconClass: 'fas fa-edit'
},
{
items: [{
name: 'Watchlist',
target: 'collapseWatchlist',
subMenus: [
createSubMenu('Watchlist Screening', 'collapseScreening', watchlistMenus.screening)
]
}],
iconClass: 'fas fa-calendar'
},
{
items: [{
name: 'File',
target: 'collapseFiles',
subMenus: [
createSubMenu('File Screening', 'collapseScreening', fileMenus.screening)
]
}],
iconClass: 'fas fa-cogs'
}
];
export default dataMenu;

View File

@ -28,7 +28,7 @@ const Compare = () => {
const [imageCompareUrl, setImageCompareUrl] = useState('');
const [verified, setVerified] = useState(null);
const fileTypes = ["JPG", "JPEG", "PNG"];
const fileTypes = ["JPG", "JPEG"];
const [file, setFile] = useState(null); // For the first image
const [compareFile, setCompareFile] = useState(null); // For the second imag
@ -113,20 +113,119 @@ const Compare = () => {
};
const handleImageUpload = (file) => {
if (file && fileTypes.includes(file.name.split('.').pop().toUpperCase())) {
setSelectedImageName(file.name);
setFile(file); // Store the file directly in state
setUploadError(''); // Clear error if valid
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 using both extension and MIME 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;
}
// Check image dimensions
const img = new Image();
const objectUrl = URL.createObjectURL(file);
img.onload = () => {
URL.revokeObjectURL(objectUrl);
if (img.width > 300 || img.height > 300) {
setUploadError('Image dimensions must not exceed 300x300 pixels');
setFile(null);
setSelectedImageName('');
return;
}
// All validations passed
setSelectedImageName(file.name);
setFile(file);
setUploadError('');
};
img.onerror = () => {
URL.revokeObjectURL(objectUrl);
setUploadError('Invalid image file');
setFile(null);
setSelectedImageName('');
};
img.src = objectUrl;
};
const handleCompareImageUpload = (file) => {
if (file && fileTypes.includes(file.name.split('.').pop().toUpperCase())) {
setSelectedCompareImageName(file.name);
setCompareFile(file); // Store the compare file directly in state
setCompareUploadError(''); // Clear error if valid
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 using both extension and MIME 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;
}
// Check image dimensions
const img = new Image();
const objectUrl = URL.createObjectURL(file);
img.onload = () => {
URL.revokeObjectURL(objectUrl);
if (img.width > 300 || img.height > 300) {
setCompareUploadError('Image dimensions must not exceed 300x300 pixels');
setCompareFile(null);
setSelectedCompareImageName('');
return;
}
// All validations passed
setSelectedCompareImageName(file.name);
setCompareFile(file);
setCompareUploadError('');
};
img.onerror = () => {
URL.revokeObjectURL(objectUrl);
setCompareUploadError('Invalid image file');
setCompareFile(null);
setSelectedCompareImageName('');
};
img.src = objectUrl;
};
const handleImageCancel = () => {
setSelectedImageName('');
@ -289,23 +388,23 @@ const Compare = () => {
</tbody>
</table>
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: '20px' }}>
<div style={styles.imagesWrapper}>
<div style={styles.imageContainer}>
<img
src={imageUrl || "path-to-your-image"}
alt="Original Foto"
style={styles.imageStyle}
<img
src={imageUrl || "path-to-your-image"}
alt="Original Foto"
style={styles.responsiveImage}
/>
<p style={{ marginTop: '1rem', textAlign: 'center' }}>File Name: {resultImageLabel}</p>
<p style={styles.imageLabel}>File Name: {resultImageLabel}</p>
</div>
<div style={styles.imageCompareContainer}>
<img
src={imageCompareUrl || "path-to-your-image"}
alt="Compare Foto"
style={styles.imageStyle}
<div style={styles.imageContainer}>
<img
src={imageCompareUrl || "path-to-your-image"}
alt="Compare Foto"
style={styles.responsiveImage}
/>
<p style={{ marginTop: '1rem', textAlign: 'center' }}>File Name: {resultCompareImageLabel}</p>
<p style={styles.imageLabel}>File Name: {resultCompareImageLabel}</p>
</div>
</div>
</div>
@ -445,18 +544,27 @@ const Compare = () => {
name="file"
types={fileTypes}
multiple={false}
onTypeError={(err) => {
setUploadError('Only JPG/JPEG files are allowed');
setFile(null);
setSelectedImageName('');
}}
children={
<div style={styles.uploadArea}>
<i className="fas fa-cloud-upload-alt" style={styles.uploadIcon}></i>
<p style={styles.uploadText}>Drag and Drop Here</p>
<p>Or</p>
<a href="#">Browse</a>
<p className="text-muted">Recommended size: 250x250 (Max File Size: 2MB)</p>
<p className="text-muted">Recommended size: 300x300 (Max File Size: 2MB)</p>
<p className="text-muted">Supported file types: JPG, JPEG</p>
</div>
}
/>
{uploadError && <small style={styles.uploadError}>{uploadError}</small>}
{uploadError && (
<small className="text-danger mt-2" style={{ fontSize: '12px' }}>
{uploadError}
</small>
)}
</div>
{/* Display uploaded image name */}
@ -485,18 +593,27 @@ const Compare = () => {
name="file"
types={fileTypes}
multiple={false}
onTypeError={(err) => {
setCompareUploadError('Only JPG/JPEG files are allowed');
setCompareFile(null);
setSelectedCompareImageName('');
}}
children={
<div style={styles.uploadArea}>
<i className="fas fa-cloud-upload-alt" style={styles.uploadIcon}></i>
<p style={styles.uploadText}>Drag and Drop Here</p>
<p>Or</p>
<a href="#">Browse</a>
<p className="text-muted">Recommended size: 250x250 (Max File Size: 2MB)</p>
<p className="text-muted">Recommended size: 300x300 (Max File Size: 2MB)</p>
<p className="text-muted">Supported file types: JPG, JPEG</p>
</div>
}
/>
{compareUploadError && <small style={styles.uploadError}>{compareUploadError}</small>}
{compareUploadError && (
<small className="text-danger mt-2" style={{ fontSize: '12px' }}>
{compareUploadError}
</small>
)}
</div>
{/* Display uploaded image name */}
@ -678,25 +795,42 @@ const styles = {
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',
flex: 1,
padding: '10px',
},
imageCompareContainer: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
flex: 1,
padding: '10px',
},
imageStyle: {
responsiveImage: {
width: '100%',
height: 'auto',
maxWidth: '150px', // Limit image width
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',

View File

@ -10,7 +10,7 @@ const Enroll = () => {
const BASE_URL = process.env.REACT_APP_BASE_URL
const API_KEY = process.env.REACT_APP_API_KEY
const fileTypes = ["JPG", "JPEG", "PNG"];
const fileTypes = ["JPG", "JPEG"];
const [file, setFile] = useState(null);
const [errorMessage, setErrorMessage] = useState('');
@ -137,24 +137,63 @@ const Enroll = () => {
};
const handleImageUpload = (file) => {
// 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);
setImageError(''); // Clear any previous errors
} else {
alert('Image format is not supported');
setImageError('Image format is not supported');
setFile(null);
}
} else {
console.error('No file selected or invalid file object.');
}
if (!file) {
setImageError('Please select a file');
return;
}
// Check file size (2MB = 2 * 1024 * 1024 bytes)
const maxSize = 2 * 1024 * 1024;
if (file.size > maxSize) {
setImageError('File size exceeds 2MB limit');
setFile(null);
setSelectedImageName('');
return;
}
// Check file type using both extension and MIME 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)) {
setImageError('Only JPG/JPEG files are allowed');
setFile(null);
setSelectedImageName('');
return;
}
// Check image dimensions
const img = new Image();
const objectUrl = URL.createObjectURL(file);
img.onload = () => {
URL.revokeObjectURL(objectUrl);
if (img.width > 300 || img.height > 300) {
setImageError('Image dimensions must not exceed 300x300 pixels');
setFile(null);
setSelectedImageName('');
return;
}
// All validations passed
setSelectedImageName(file.name);
setFile(file);
setImageError('');
};
img.onerror = () => {
URL.revokeObjectURL(objectUrl);
setImageError('Invalid image file');
setFile(null);
setSelectedImageName('');
};
img.src = objectUrl;
};
const handleImageCancel = () => {
setSelectedImageName('');
setFile(null);
@ -336,17 +375,6 @@ const Enroll = () => {
}
};
// 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
}
};
const styles = {
formGroup: {
marginTop: '-45px',
@ -633,6 +661,12 @@ const Enroll = () => {
color: 'white',
marginTop: '10px',
},
errorText: {
color: '#dc3545',
fontSize: '12px',
marginTop: '5px',
display: 'block'
}
};
if (!isServer) {
@ -745,6 +779,11 @@ const Enroll = () => {
name="file"
types={fileTypes}
multiple={false}
onTypeError={(err) => {
setImageError('Only JPG/JPEG files are allowed');
setFile(null);
setSelectedImageName('');
}}
onDrop={(files) => {
if (files && files[0]) {
handleImageUpload(files[0]);
@ -761,26 +800,18 @@ const Enroll = () => {
</div>
}
/>
<input
type="file"
id="fileUpload"
ref={fileInputRef}
style={{ display: 'none' }}
accept="image/jpeg, image/jpg"
onChange={e => handleImageUpload(e.target.files[0])}
/>
{imageError && <small style={{ color: 'red' }}>{imageError}</small>}
{imageError && (
<small className="text-danger mt-2" style={{ fontSize: '12px' }}>
{imageError}
</small>
)}
</div>
</div>
{selectedImageName && (
<div className="mt-3">
<p><strong>File:</strong> {selectedImageName}</p>
{file && (
<p style={styles.fileSize}>
Size: {formatFileSize(file.size)}
</p>
)}
<button className="btn btn-danger" onClick={handleImageCancel}>
<FontAwesomeIcon icon={faTimes} className="me-2" />Cancel
</button>
@ -810,10 +841,15 @@ const Enroll = () => {
<img
src={imageUrl || "path-to-your-image"}
alt="Contoh Foto"
style={isMobile ? styles.imageStyleMobile : styles.imageStyle}
style={{
maxWidth: '100%',
height: 'auto',
objectFit: 'contain',
borderRadius: '5px'
}}
/>
<p style={isMobile ? { ...styles.imageDetails, fontSize: '14px' } : styles.imageDetails}>
{resultImageLabel} {/* Display resultImageLabel instead of selectedImageName */}
{resultImageLabel}
</p>
</div>
</div>

View File

@ -5,7 +5,7 @@ import { FileUploader } from 'react-drag-drop-files';
import Select from 'react-select'
import { ServerDownAnimation } from '../../../../assets/images';
const fileTypes = ["JPG", "JPEG", "PNG"]; // Allowed file types
const fileTypes = ["JPG", "JPEG"]; // Allowed file types
const Search = () => {
@ -126,34 +126,83 @@ const Search = () => {
setIsSelectOpen(false);
};
// Add this after your existing state declarations
const handleImageUpload = (file) => {
// 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)) {
if (!file) {
setImageError('Please select a file');
return;
}
// Store the uploaded file
setUploadedFile(file);
// Check file size (2MB = 2 * 1024 * 1024 bytes)
const maxSize = 2 * 1024 * 1024;
if (file.size > maxSize) {
setImageError('File size exceeds 2MB limit');
setFile(null);
setSelectedImageName('');
setUploadedFile(null);
return;
}
// Check file type using both extension and MIME 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)) {
setImageError('Only JPG/JPEG files are allowed');
setFile(null);
setSelectedImageName('');
setUploadedFile(null);
return;
}
// Create preview URL for the uploaded image
const previewUrl = URL.createObjectURL(file);
// Check image dimensions
const img = new Image();
img.onload = () => {
URL.revokeObjectURL(previewUrl);
if (img.width > 300 || img.height > 300) {
setImageError('Image dimensions must not exceed 300x300 pixels');
setFile(null);
setSelectedImageName('');
setUploadedFile(null);
return;
}
// All validations passed
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');
setImageError('');
};
img.onerror = () => {
URL.revokeObjectURL(previewUrl);
setImageError('Invalid image file');
setFile(null);
setUploadedFile(null); // Clear uploadedFile if the file is unsupported
}
} else {
console.error('No file selected or invalid file object.');
}
setSelectedImageName('');
setUploadedFile(null);
};
img.src = previewUrl;
};
// Update the handleImageCancel function
const handleImageCancel = () => {
setSelectedImageName('');
setFile(null);
setUploadedFile(null);
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
};
const handleCheckClick = async () => {
// Clear existing errors
setApplicationIdError('');
@ -355,17 +404,31 @@ const Search = () => {
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: '40svh',
height: '50svh', // Use viewport height for a more responsive size
cursor: 'pointer',
marginTop: '1rem',
padding: '1rem',
paddingTop: '18px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
border: '1px solid #ced4da',
borderRadius: '0.25rem',
padding: '20px',
},
uploadIcon: {
fontSize: '40px',
@ -377,7 +440,7 @@ const Search = () => {
fontWeight: '400',
fontSize: '16px',
lineHeight: '13px',
},
},
wrapper: {
border: '1px solid #ddd',
borderRadius: '6px',
@ -479,14 +542,37 @@ const Search = () => {
gridTemplateColumns: isMobile ? 'repeat(2, 1fr)' : 'repeat(5, 1fr)', // Adjust based on isMobile
gap: '16px',
},
resultItem: {
border: '1px solid #ddd',
imageWrapper: {
width: '100%',
height: '200px',
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#f8f9fa',
borderRadius: '8px',
padding: '12px',
boxShadow: '0 2px 5px rgba(0, 0, 0, 0.1)',
textAlign: 'center',
transition: 'transform 0.3s ease', // Added transition for hover effect
cursor: 'pointer',
},
responsiveImage: {
maxWidth: '100%',
maxHeight: '100%',
width: 'auto',
height: 'auto',
objectFit: 'contain',
transition: 'transform 0.3s ease',
'&:hover': {
transform: 'scale(1.05)',
},
},
resultItem: {
padding: '15px',
backgroundColor: '#ffffff',
borderRadius: '10px',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
transition: 'transform 0.3s ease',
'&:hover': {
transform: 'translateY(-5px)',
},
},
resultItemHover: {
transform: 'scale(1.05)', // Slightly enlarge the card on hover
@ -556,10 +642,10 @@ const Search = () => {
</style>
{isLoading && (
<div style={styles.loadingOverlay}>
<div style={styles.spinner}></div>
<p style={styles.loadingText}>Loading...</p>
</div>
<div style={styles.loadingOverlay}>
<div style={styles.spinner}></div>
<p style={styles.loadingText}>Loading...</p>
</div>
)}
{/* Application ID Selection */}
@ -627,38 +713,43 @@ const Search = () => {
</div>
{/* Upload Section */}
{/* Drag and Drop File Uploader */}
<div className='col-md-6'>
<div className="row form-group mt-4">
<CustomLabel htmlFor="uploadPhoto" style={styles.customLabel}>
Upload Face Photo
</CustomLabel>
<FileUploader
handleChange={handleImageUpload}
name="file"
types={fileTypes}
multiple={false}
onDrop={(files) => handleImageUpload(files[0])}
children={
<div style={styles.uploadArea}>
<i className="fas fa-cloud-upload-alt" style={styles.uploadIcon}></i>
<p style={styles.uploadText}>Drag and Drop Here</p>
<p>Or</p>
<a href="#" onClick={() => fileInputRef.current.click()}>Browse</a>
<p className="text-muted">Recommended size: 300x300 (Max File Size: 2MB)</p>
<p className="text-muted">Supported file types: JPG, JPEG</p>
</div>
}
handleChange={handleImageUpload}
name="file"
types={fileTypes}
multiple={false}
onTypeError={(err) => {
setImageError('Only JPG/JPEG files are allowed');
setFile(null);
setSelectedImageName('');
}}
onDrop={(files) => {
if (files && files[0]) {
handleImageUpload(files[0]);
}
}}
children={
<div style={isMobile ? styles.uploadAreaMobile : styles.uploadArea}>
<i className="fas fa-cloud-upload-alt" style={styles.uploadIcon}></i>
<p style={styles.uploadText}>Drag and Drop Here</p>
<p>Or</p>
<a href="#" onClick={() => fileInputRef.current.click()}>Browse</a>
<p className="text-muted">Recommended size: 300x300 (Max File Size: 2MB)</p>
<p className="text-muted">Supported file types: JPG, JPEG</p>
</div>
}
/>
<input
type="file"
id="fileUpload"
ref={fileInputRef}
style={{ display: 'none' }}
accept="image/jpeg, image/jpg"
onChange={e => handleImageUpload(e.target.files[0])}
/>
{imageError && <small style={styles.uploadError}>{imageError}</small>}
{imageError && (
<small className="text-danger mt-2" style={{ fontSize: '12px' }}>
{imageError}
</small>
)}
</div>
</div>
@ -679,15 +770,14 @@ const Search = () => {
{errorMessage && <small style={styles.uploadError}>{errorMessage}</small>}
{/* Submit Button */}
<div style={styles.submitButton}>
<button onClick={handleCheckClick} className="btn d-flex justify-content-center align-items-center me-2" style={{ backgroundColor: '#0542CC' }}>
<p className="text-white mb-0">Check Now</p>
</button>
</div>
<div style={styles.submitButton}>
<button onClick={handleCheckClick} className="btn d-flex justify-content-center align-items-center me-2" style={{ backgroundColor: '#0542CC' }}>
<p className="text-white mb-0">Check Now</p>
</button>
</div>
{/* Results Section */}
{
showResult && results.length > 0 && (
{showResult && results.length > 0 && (
<div style={styles.containerResultStyle}>
<h1 style={{ color: '#0542cc', textAlign: 'center' }}>Results</h1>
<div style={styles.resultGrid}>
@ -698,13 +788,18 @@ const Search = () => {
<p style={styles.resultText}>Similarity: {result.similarity}%</p>
<p style={styles.resultText}>Distance: {result.distance}</p>
</div>
<img src={imageUrls[index]} alt={`Result ${index + 1}`} style={styles.resultImage} />
<div style={styles.imageWrapper}>
<img
src={imageUrls[index]}
alt={`Result ${index + 1}`}
style={styles.responsiveImage}
/>
</div>
</div>
))}
</div>
</div>
)
}
)}
</div>
);

View File

@ -3,12 +3,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faChevronDown, faTimes } from '@fortawesome/free-solid-svg-icons';
import Select from 'react-select'
import { ServerDownAnimation } from '../../../../assets/images';
import { FileUploader } from 'react-drag-drop-files';
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 fileTypes = ["JPG", "JPEG"];
const [file, setFile] = useState(null);
const [errorMessage, setErrorMessage] = useState('');
@ -18,7 +19,7 @@ const Verify = () => {
const [thresholdError, setThresholdError] = useState('');
const [selectedImageName, setSelectedImageName] = useState('');
const [resultImageLabel, setResultImageLabel] = useState('');
const fileInputRef = useRef(null);
const inputRef = useRef(null);
const [showResult, setShowResult] = useState(false);
const [applicationId, setApplicationId] = useState('');
const [thresholdId, setThresholdId] = useState('');
@ -33,6 +34,7 @@ const Verify = () => {
const [inputValueApplication, setInputValueApplication] = useState(''); // Controlled input value for Application ID
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
const [isServer, setIsServer] = useState(true);
const [imageError, setImageError] = useState('');
const thresholdIds = [
{ id: 1, name: 'cosine', displayName: 'Basic' },
@ -135,28 +137,68 @@ const Verify = () => {
const handleImageUpload = (file) => {
if (file && file.name) {
const fileExtension = file.name.split('.').pop().toUpperCase();
if (fileTypes.includes(fileExtension)) {
setSelectedImageName(file.name);
setFile(file);
setUploadError('');
} else {
alert('Image format is not supported');
setUploadError('Image format is not supported');
setFile(null);
}
} else {
console.error('No file selected or invalid file object.');
if (!file) {
setImageError('Please select a file');
return;
}
};
// Check file size (2MB = 2 * 1024 * 1024 bytes)
const maxSize = 2 * 1024 * 1024;
if (file.size > maxSize) {
setImageError('File size exceeds 2MB limit');
setFile(null);
setSelectedImageName('');
return;
}
// Check file type using both extension and MIME 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)) {
setImageError('Only JPG/JPEG files are allowed');
setFile(null);
setSelectedImageName('');
return;
}
// Check image dimensions
const img = new Image();
const objectUrl = URL.createObjectURL(file);
img.onload = () => {
URL.revokeObjectURL(objectUrl);
if (img.width > 300 || img.height > 300) {
setImageError('Image dimensions must not exceed 300x300 pixels');
setFile(null);
setSelectedImageName('');
return;
}
// All validations passed
setSelectedImageName(file.name);
setFile(file);
setImageError('');
};
img.onerror = () => {
URL.revokeObjectURL(objectUrl);
setImageError('Invalid image file');
setFile(null);
setSelectedImageName('');
};
img.src = objectUrl;
};
const handleImageCancel = () => {
setSelectedImageName('');
setFile(null);
if (fileInputRef.current) {
fileInputRef.current.value = '';
if (inputRef.current) {
inputRef.current.value = '';
}
};
@ -308,17 +350,6 @@ const Verify = () => {
}
};
// 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
}
};
if (!isServer) {
return (
<div style={{ textAlign: 'center', marginTop: '50px' }}>
@ -734,60 +765,50 @@ const Verify = () => {
</div>
{/* Upload Section */}
<div className="col-md-6">
<div className='col-md-6'>
<div className="row form-group mt-4">
<CustomLabel htmlFor="uploadPhoto" style={styles.customLabel}>
Upload Face Photo
</CustomLabel>
<div style={styles.uploadWrapper}>
<div
style={styles.uploadArea}
onDrop={(e) => {
e.preventDefault();
// Handle drop event
const file = e.dataTransfer.files[0];
handleImageUpload(file);
<CustomLabel htmlFor="uploadPhoto" style={styles.customLabel}>
Upload Face Photo
</CustomLabel>
<FileUploader
handleChange={handleImageUpload}
name="file"
types={fileTypes}
multiple={false}
onTypeError={(err) => {
setImageError('Only JPG/JPEG files are allowed');
setFile(null);
setSelectedImageName('');
}}
onDragOver={(e) => e.preventDefault()}
>
<i className="fas fa-cloud-upload-alt" style={styles.uploadIcon}></i>
<p style={styles.uploadText}>Drag and Drop Here</p>
<p>Or</p>
<a
href="#"
onClick={(e) => {
e.preventDefault();
// Only open file input if no file is selected
if (!fileInputRef.current.files.length) {
fileInputRef.current.click();
onDrop={(files) => {
if (files && files[0]) {
handleImageUpload(files[0]);
}
}}
style={styles.browseLink}
>
Browse
</a>
<p className="text-muted" style={styles.uploadText}>
Recommended size: 300x300 (Max File Size: 2MB)
</p>
<p className="text-muted" style={styles.uploadText}>
Supported file types: JPG, JPEG
</p>
</div>
{/* File Input */}
<input
ref={fileInputRef}
type="file"
id="uploadPhoto"
className="form-control"
style={{ display: 'none' }}
accept="image/jpeg, image/jpg"
onChange={(e) => handleImageUpload(e.target.files[0])}
}}
children={
<div style={isMobile ? styles.uploadAreaMobile : styles.uploadArea}>
<i className="fas fa-cloud-upload-alt" style={styles.uploadIcon}></i>
<p style={styles.uploadText}>Drag and Drop Here</p>
<p>Or</p>
<span
style={{
color: '#0542cc',
textDecoration: 'underline',
cursor: 'pointer'
}}
>
Browse
</span>
<p className="text-muted">Recommended size: 300x300 (Max File Size: 2MB)</p>
<p className="text-muted">Supported file types: JPG, JPEG</p>
</div>
}
/>
{/* Error Message */}
{uploadError && <small style={styles.uploadError}>{uploadError}</small>}
</div>
{imageError && (
<small className="text-danger mt-2" style={{ fontSize: '12px' }}>
{imageError}
</small>
)}
</div>
</div>
@ -795,11 +816,6 @@ const Verify = () => {
{selectedImageName && (
<div className="mt-3">
<p><strong>File:</strong> {selectedImageName}</p>
{file && (
<p style={styles.fileSize}>
Size: {formatFileSize(file.size)}
</p>
)}
<button className="btn btn-danger" onClick={handleImageCancel}>
<FontAwesomeIcon icon={faTimes} className="me-2" />Cancel
</button>
@ -817,33 +833,40 @@ const Verify = () => {
{/* Results Section */}
{showResult && (
<div style={styles.containerResultStyle}>
<h1 style={{ color: '#0542cc', fontSize: '2rem' }}>Results</h1>
<div style={styles.resultContainer}>
<table style={styles.resultsTableMobile}>
<tbody>
<tr>
<td style={styles.resultsCell}>Similarity</td>
<td style={{ ...styles.resultsValueCell, color: verified ? 'green' : 'red' }}>
<strong>{verified !== null ? (verified ? 'True' : 'False') : 'N/A'}</strong>
</td>
</tr>
</tbody>
</table>
<div style={styles.containerResultStyle}>
<h1 style={{ color: '#0542cc', fontSize: '2rem' }}>Results</h1>
<div style={styles.resultContainer}>
<table style={styles.resultsTableMobile}>
<tbody>
<tr>
<td style={styles.resultsCell}>Similarity</td>
<td style={{ ...styles.resultsValueCell, color: verified ? 'green' : 'red' }}>
<strong>{verified !== null ? (verified ? 'True' : 'False') : 'N/A'}</strong>
</td>
</tr>
</tbody>
</table>
<div style={styles.imageContainer}>
<img
src={imageUrl || "path-to-your-image"}
alt="Example Image"
style={styles.imageStyle}
/>
<p style={{ marginTop: '10px' }}>
File Name: {resultImageLabel} {/* Display the resultImageLabel here */}
</p>
</div>
<div style={styles.imageContainer}>
<img
src={imageUrl || "path-to-your-image"}
alt="Example Image"
style={{
maxWidth: '100%',
width: 'auto',
height: 'auto',
objectFit: 'contain',
borderRadius: '5px'
}}
/>
<p style={{ marginTop: '10px' }}>
File Name: {resultImageLabel}
</p>
</div>
</div>
</div>
</div>
)}
</div>
);

View File

@ -12,7 +12,7 @@ const CustomLabel = ({ overRide, children, ...props }) => {
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 fileTypes = ["image/jpeg", "image/jpg"];
const fileInputRef = useRef(null);
const [isMobile, setIsMobile] = useState(false);
@ -119,19 +119,63 @@ const Verify = () => {
}
};
const handleImageUpload = (file) => {
const checkImageDimensions = (file) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = URL.createObjectURL(file);
img.onload = () => {
URL.revokeObjectURL(img.src);
if (img.width > 300 || img.height > 300) {
reject('Image dimensions must not exceed 300x300 pixels');
} else {
resolve(true);
}
};
img.onerror = () => {
URL.revokeObjectURL(img.src);
reject('Failed to load image');
};
});
};
// Update handleImageUpload to include dimension checking
const handleImageUpload = async (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 {
try {
// Check if file is PNG
if (file.type === 'image/png') {
setImageError('The image format is not suitable. Only JPG and JPEG files are allowed.');
setFile(null);
setSelectedImageName('');
return;
}
// Validate file type
if (!fileTypes.includes(file.type)) {
setImageError('Invalid file type. Only JPG and JPEG are allowed.');
return;
}
// Validate file size
if (file.size > 2 * 1024 * 1024) {
setImageError('File size exceeds 2MB.');
return;
}
// Validate image dimensions
await checkImageDimensions(file);
setImageError('');
} catch (error) {
setImageError(error);
setFile(null);
setSelectedImageName('');
}
};
};
const handleImageCancel = () => {
setFile(null);
@ -161,6 +205,33 @@ const Verify = () => {
return Object.values(errors).every(error => error === '');
};
const handleApiError = (response) => {
// Handle 400 Bad Request
if (response.status_code === 400) {
console.error('❌ Bad Request:', {
status: response.status_code,
detail: response.detail || 'Mohon Upload KTP'
});
return response.detail || 'Mohon Upload KTP';
}
// Handle 500 Internal Server Error
if (response.status_code >= 500) {
console.error('🔥 Server Error:', {
status: response.status_code,
message: 'Internal Server Error'
});
return 'Internal Server Error';
}
// Default error message
console.error('⚠️ Unknown Error:', {
status: response.status_code,
response
});
return 'Terjadi kesalahan. Silakan coba lagi.';
};
// Submit form and trigger OCR API
const handleCheckClick = async () => {
if (!validateForm()) {
@ -183,16 +254,18 @@ const Verify = () => {
});
if (!response.ok) {
throw new Error('OCR processing failed');
throw new Error('OCR processing failed, Please check your input, Please check your input');
}
const result = await response.json();
console.log('📡 API Response:', result);
// 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'] || {};
console.log('✅ OCR Success:', result);
const updateQuota = result.details.data.quota
const data = {
@ -230,10 +303,22 @@ const Verify = () => {
await fetchImage(imageFileName); // Call the fetchImage function to fetch the image
}
} else {
setErrorMessage('OCR processing failed');
const errorMsg = handleApiError(result);
setErrorMessage(errorMsg);
setShowResult(false);
console.error('❌ OCR Failed:', {
error: errorMsg,
response: result
});
}
} catch (error) {
setErrorMessage(error.message || 'Error during OCR processing');
const errorMsg = 'Internal Server Error';
setErrorMessage(errorMsg);
setShowResult(false);
console.error('🔥 Request Failed:', {
error: error.message,
detail: error
});
} finally {
setIsLoading(false);
}
@ -350,9 +435,10 @@ const Verify = () => {
borderRadius: '4px',
},
errorText: {
color: '#721c24',
fontSize: '14px',
margin: '0',
color: '#dc3545', // Bootstrap danger color
fontSize: '12px', // Small text
marginTop: '5px',
fontWeight: '400'
},
loadingOverlay: {
position: 'fixed', // Gunakan fixed untuk overlay penuh layar
@ -501,8 +587,9 @@ const Verify = () => {
<CustomLabel htmlFor="imageInput" style={styles.customLabel}>
Upload Image (KTP)
</CustomLabel>
<div style={styles.uploadWrapper}>
{/* Drag and Drop File Input */}
{/* Existing drag & drop area */}
<div
style={styles.uploadArea}
onDrop={(e) => {
@ -516,20 +603,21 @@ const Verify = () => {
<p>Or</p>
<a href="#" onClick={() => fileInputRef.current.click()} style={styles.browseLink}>Browse</a>
<p className="text-muted" style={styles.uploadText}>Recommended size: 300x300 (Max File Size: 2MB)</p>
<p className="text-muted" style={styles.uploadText}>Supported file types: JPG, JPEG, PNG</p>
<p className="text-muted" style={styles.uploadText}>Supported file types: JPG, JPEG</p>
</div>
{/* File Input */}
{/* File input */}
<input
ref={fileInputRef}
type="file"
id="imageInput"
className="form-control"
style={{ display: 'none' }}
accept="image/jpeg, image/png"
accept="image/jpeg, image/jpg"
onChange={(e) => handleImageUpload(e.target.files[0])}
/>
{/* Display selected file info */}
{selectedImageName && (
<div className="mt-3">
<p><strong>File:</strong> {selectedImageName}</p>
@ -543,8 +631,12 @@ const Verify = () => {
</button>
</div>
)}
{/* Display validation errors */}
{validationErrors.file && <p style={styles.errorText}>{validationErrors.file}</p>}
{imageError && <p style={styles.errorText}>{imageError}</p>}
</div>
</div>
</div>

View File

@ -12,7 +12,7 @@ const CustomLabel = ({ overRide, children, ...props }) => {
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 fileTypes = ["image/jpeg", "image/jpg"];
const fileInputRef = useRef(null);
const [isMobile, setIsMobile] = useState(false);
@ -119,19 +119,63 @@ const Verify = () => {
}
};
const handleImageUpload = (file) => {
const checkImageDimensions = (file) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = URL.createObjectURL(file);
img.onload = () => {
URL.revokeObjectURL(img.src);
if (img.width > 300 || img.height > 300) {
reject('Image dimensions must not exceed 300x300 pixels');
} else {
resolve(true);
}
};
img.onerror = () => {
URL.revokeObjectURL(img.src);
reject('Failed to load image');
};
});
};
// Update handleImageUpload function
const handleImageUpload = async (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 {
try {
// Check if file is PNG
if (file.type === 'image/png') {
setImageError('The image format is not suitable. Only JPG and JPEG files are allowed.');
setFile(null);
setSelectedImageName('');
return;
}
// Validate file type
if (!fileTypes.includes(file.type)) {
setImageError('Invalid file type. Only JPG and JPEG are allowed.');
return;
}
// Validate file size
if (file.size > 2 * 1024 * 1024) {
setImageError('File size exceeds 2MB.');
return;
}
// Validate image dimensions
await checkImageDimensions(file);
setImageError('');
} catch (error) {
setImageError(error);
setFile(null);
setSelectedImageName('');
}
};
};
const handleImageCancel = () => {
setFile(null);
@ -340,9 +384,10 @@ const Verify = () => {
borderRadius: '4px',
},
errorText: {
color: '#721c24',
fontSize: '14px',
margin: '0',
color: '#dc3545',
fontSize: '12px',
marginTop: '5px',
fontWeight: '400'
},loadingOverlay: {
position: 'fixed', // Gunakan fixed untuk overlay penuh layar
top: 0,
@ -515,7 +560,7 @@ const Verify = () => {
id="imageInput"
className="form-control"
style={{ display: 'none' }}
accept="image/jpeg, image/png"
accept="image/jpeg, image/jpg"
onChange={(e) => handleImageUpload(e.target.files[0])}
/>
@ -532,7 +577,9 @@ const Verify = () => {
</button>
</div>
)}
{/* Display validation errors */}
{validationErrors.file && <p style={styles.errorText}>{validationErrors.file}</p>}
{imageError && <p style={styles.errorText}>{imageError}</p>}
</div>
</div>
</div>