Fixing Issue
This commit is contained in:
parent
2a2662df25
commit
2a559da0d3
@ -1,262 +1,192 @@
|
||||
// src/components/dataMenu.js
|
||||
|
||||
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')
|
||||
];
|
||||
|
||||
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: [
|
||||
{
|
||||
name: 'Main Dashboard', // Changed the name
|
||||
target: 'collapseHome',
|
||||
subMenus: [
|
||||
{
|
||||
name: 'Getting Started',
|
||||
link: '/getting-started'
|
||||
items: [createSubMenu('Home', 'collapseHome', mainDashboardMenus)],
|
||||
iconClass: 'fas fa-tachometer-alt'
|
||||
},
|
||||
{
|
||||
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
|
||||
items: [{
|
||||
name: 'Biometric',
|
||||
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
|
||||
],
|
||||
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'
|
||||
},
|
||||
{
|
||||
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
|
||||
items: [{
|
||||
name: 'SMS',
|
||||
target: 'collapseSms',
|
||||
subMenus: [
|
||||
{
|
||||
name: 'SMS Verification', // Changed the name
|
||||
link: '/sms-verify'
|
||||
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'
|
||||
},
|
||||
{
|
||||
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
|
||||
items: [{
|
||||
name: 'WhatsApp',
|
||||
target: 'collapseWa',
|
||||
subMenus: [
|
||||
{
|
||||
name: 'Verify WhatsApp Account', // Changed the name
|
||||
link: '/wa-verify'
|
||||
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'
|
||||
},
|
||||
{
|
||||
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
|
||||
items: [{
|
||||
name: 'Identity',
|
||||
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'},
|
||||
],
|
||||
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'
|
||||
},
|
||||
{
|
||||
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
|
||||
items: [{
|
||||
name: 'Watchlist',
|
||||
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',
|
||||
createSubMenu('Watchlist Screening', 'collapseScreening', watchlistMenus.screening)
|
||||
]
|
||||
}],
|
||||
iconClass: 'fas fa-calendar'
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{
|
||||
name: 'File Management', // Changed the name
|
||||
items: [{
|
||||
name: 'File',
|
||||
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',
|
||||
createSubMenu('File Screening', 'collapseScreening', fileMenus.screening)
|
||||
]
|
||||
}],
|
||||
iconClass: 'fas fa-cogs'
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -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,21 +113,120 @@ 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('');
|
||||
setImageUrl('');
|
||||
@ -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}
|
||||
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}>
|
||||
<div style={styles.imageContainer}>
|
||||
<img
|
||||
src={imageCompareUrl || "path-to-your-image"}
|
||||
alt="Compare Foto"
|
||||
style={styles.imageStyle}
|
||||
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',
|
||||
|
@ -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,21 +137,60 @@ 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)) {
|
||||
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(''); // Clear any previous errors
|
||||
} else {
|
||||
alert('Image format is not supported');
|
||||
setImageError('Image format is not supported');
|
||||
setImageError('');
|
||||
};
|
||||
|
||||
img.onerror = () => {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
setImageError('Invalid image file');
|
||||
setFile(null);
|
||||
}
|
||||
} else {
|
||||
console.error('No file selected or invalid file object.');
|
||||
}
|
||||
setSelectedImageName('');
|
||||
};
|
||||
|
||||
img.src = objectUrl;
|
||||
};
|
||||
|
||||
|
||||
@ -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>
|
||||
|
@ -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');
|
||||
setFile(null);
|
||||
setUploadedFile(null); // Clear uploadedFile if the file is unsupported
|
||||
}
|
||||
} else {
|
||||
console.error('No file selected or invalid file object.');
|
||||
}
|
||||
setImageError('');
|
||||
};
|
||||
|
||||
img.onerror = () => {
|
||||
URL.revokeObjectURL(previewUrl);
|
||||
setImageError('Invalid image file');
|
||||
setFile(null);
|
||||
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('');
|
||||
@ -356,10 +405,10 @@ const Search = () => {
|
||||
},
|
||||
uploadArea: {
|
||||
backgroundColor: '#e6f2ff',
|
||||
height: '40svh',
|
||||
height: '250px',
|
||||
cursor: 'pointer',
|
||||
marginTop: '1rem',
|
||||
padding: '1rem',
|
||||
paddingTop: '22px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
@ -367,6 +416,20 @@ const Search = () => {
|
||||
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',
|
||||
@ -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
|
||||
@ -627,7 +713,6 @@ 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}>
|
||||
@ -638,9 +723,18 @@ const Search = () => {
|
||||
name="file"
|
||||
types={fileTypes}
|
||||
multiple={false}
|
||||
onDrop={(files) => handleImageUpload(files[0])}
|
||||
onTypeError={(err) => {
|
||||
setImageError('Only JPG/JPEG files are allowed');
|
||||
setFile(null);
|
||||
setSelectedImageName('');
|
||||
}}
|
||||
onDrop={(files) => {
|
||||
if (files && files[0]) {
|
||||
handleImageUpload(files[0]);
|
||||
}
|
||||
}}
|
||||
children={
|
||||
<div style={styles.uploadArea}>
|
||||
<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>
|
||||
@ -650,15 +744,12 @@ const Search = () => {
|
||||
</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>
|
||||
|
||||
@ -686,8 +777,7 @@ const Search = () => {
|
||||
</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>
|
||||
);
|
||||
|
@ -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)) {
|
||||
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);
|
||||
setUploadError('');
|
||||
} else {
|
||||
alert('Image format is not supported');
|
||||
setUploadError('Image format is not supported');
|
||||
setImageError('');
|
||||
};
|
||||
|
||||
img.onerror = () => {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
setImageError('Invalid image file');
|
||||
setFile(null);
|
||||
}
|
||||
} else {
|
||||
console.error('No file selected or invalid file object.');
|
||||
}
|
||||
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);
|
||||
<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()}
|
||||
>
|
||||
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={(e) => {
|
||||
e.preventDefault();
|
||||
// Only open file input if no file is selected
|
||||
if (!fileInputRef.current.files.length) {
|
||||
fileInputRef.current.click();
|
||||
}
|
||||
<span
|
||||
style={{
|
||||
color: '#0542cc',
|
||||
textDecoration: 'underline',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
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>
|
||||
</span>
|
||||
<p className="text-muted">Recommended size: 300x300 (Max File Size: 2MB)</p>
|
||||
<p className="text-muted">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])}
|
||||
}
|
||||
/>
|
||||
|
||||
{/* 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>
|
||||
@ -835,15 +851,22 @@ const Verify = () => {
|
||||
<img
|
||||
src={imageUrl || "path-to-your-image"}
|
||||
alt="Example Image"
|
||||
style={styles.imageStyle}
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
objectFit: 'contain',
|
||||
borderRadius: '5px'
|
||||
}}
|
||||
/>
|
||||
<p style={{ marginTop: '10px' }}>
|
||||
File Name: {resultImageLabel} {/* Display the resultImageLabel here */}
|
||||
File Name: {resultImageLabel}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -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,17 +119,61 @@ 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);
|
||||
|
||||
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, JPEG, and PNG are allowed.');
|
||||
} else if (file.size > 2 * 1024 * 1024) { // Max 2MB
|
||||
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.');
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate image dimensions
|
||||
await checkImageDimensions(file);
|
||||
setImageError('');
|
||||
|
||||
} catch (error) {
|
||||
setImageError(error);
|
||||
setFile(null);
|
||||
setSelectedImageName('');
|
||||
}
|
||||
};
|
||||
|
||||
@ -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>
|
||||
|
||||
|
@ -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,17 +119,61 @@ 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);
|
||||
|
||||
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, JPEG, and PNG are allowed.');
|
||||
} else if (file.size > 2 * 1024 * 1024) { // Max 2MB
|
||||
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.');
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate image dimensions
|
||||
await checkImageDimensions(file);
|
||||
setImageError('');
|
||||
|
||||
} catch (error) {
|
||||
setImageError(error);
|
||||
setFile(null);
|
||||
setSelectedImageName('');
|
||||
}
|
||||
};
|
||||
|
||||
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user