Fixing Issue
This commit is contained in:
parent
2a2662df25
commit
2a559da0d3
@ -1,262 +1,192 @@
|
|||||||
// src/components/dataMenu.js
|
// 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 = [
|
const dataMenu = [
|
||||||
{
|
{
|
||||||
items: [
|
items: [createSubMenu('Home', 'collapseHome', mainDashboardMenus)],
|
||||||
{
|
iconClass: 'fas fa-tachometer-alt'
|
||||||
name: 'Main Dashboard', // Changed the name
|
|
||||||
target: 'collapseHome',
|
|
||||||
subMenus: [
|
|
||||||
{
|
|
||||||
name: 'Getting Started',
|
|
||||||
link: '/getting-started'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Dashboard Overview', // Changed the name
|
items: [{
|
||||||
link: '/dashboard'
|
name: 'Biometric',
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Application Settings', // Changed the name
|
|
||||||
link: '/application'
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
iconClass: 'fas fa-tachometer-alt',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: 'Biometric Systems', // Changed the name
|
|
||||||
target: 'collapseBiometric',
|
target: 'collapseBiometric',
|
||||||
subMenus: [
|
subMenus: [
|
||||||
{
|
createSubMenu('Face Recognition System', 'collapseFaceRecog', biometricSubMenus.faceRecognition),
|
||||||
name: 'Face Recognition System', // Changed the name
|
createSubMenu('KTP OCR', 'collapseOcrKtp', biometricSubMenus.ktpOcr),
|
||||||
target: 'collapseFaceRecog',
|
createSubMenu('NPWP OCR', 'collapseOcrNpwp', biometricSubMenus.npwpOcr),
|
||||||
subMenus: [
|
createSubMenu('SIM OCR', 'collapseOcrSim', biometricSubMenus.simOcr),
|
||||||
{ name: 'Verify Identity', link: '/face-verify'}, // Changed the name
|
createSubMenu('Document OCR', 'collapseOcrDocument', biometricSubMenus.documentOcr)
|
||||||
{ name: 'Summary Report', link: '/face-summary'}, // Changed the name
|
]
|
||||||
{ name: 'Transaction Log', link: '/face-transaction'}, // Changed the name
|
}],
|
||||||
],
|
iconClass: 'fas fa-user'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'KTP OCR', // Changed the name
|
items: [{
|
||||||
target: 'collapseOcrKtp',
|
name: 'SMS',
|
||||||
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',
|
target: 'collapseSms',
|
||||||
subMenus: [
|
subMenus: [
|
||||||
{
|
createMenuItem('SMS Verification', '/sms-verify'),
|
||||||
name: 'SMS Verification', // Changed the name
|
createSubMenu('SMS OTP Management', 'collapseSmsOtp', smsServiceMenus.smsOtp),
|
||||||
link: '/sms-verify'
|
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
|
items: [{
|
||||||
target: 'collapseSmsOtp',
|
name: 'WhatsApp',
|
||||||
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',
|
target: 'collapseWa',
|
||||||
subMenus: [
|
subMenus: [
|
||||||
{
|
createMenuItem('Verify WhatsApp Account', '/wa-verify'),
|
||||||
name: 'Verify WhatsApp Account', // Changed the name
|
createSubMenu('WhatsApp Management', 'collapseWaManage', whatsAppMenus.management),
|
||||||
link: '/wa-verify'
|
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
|
items: [{
|
||||||
target: 'collapseWaManage',
|
name: 'Identity',
|
||||||
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',
|
target: 'collapseIdentify',
|
||||||
subMenus: [
|
subMenus: [
|
||||||
{
|
createSubMenu('Electronic Certificate Verification', 'collapseElectro', identityMenus.electronic),
|
||||||
name: 'Electronic Certificate Verification', // Changed the name
|
createSubMenu('NPWP Verification', 'collapseIdentifyNpwp', identityMenus.npwp),
|
||||||
target: 'collapseElectro',
|
createSubMenu('Tax Number Verification', 'collapseTax', identityMenus.tax),
|
||||||
subMenus: [
|
createSubMenu('Income Verification', 'collapseIncome', identityMenus.income),
|
||||||
{ name: 'Verify Certificate', link: '/identity-electro-verify'}, // Changed the name
|
createSubMenu('ID Verification', 'collapseIdVerification', identityMenus.id)
|
||||||
{ name: 'Electronic Transaction', link: '/identity-electro-transaction'},
|
]
|
||||||
],
|
}],
|
||||||
|
iconClass: 'fas fa-edit'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'NPWP Verification', // Changed the name
|
items: [{
|
||||||
target: 'collapseIdentifyNpwp',
|
name: 'Watchlist',
|
||||||
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',
|
target: 'collapseWatchlist',
|
||||||
subMenus: [
|
subMenus: [
|
||||||
{
|
createSubMenu('Watchlist Screening', 'collapseScreening', watchlistMenus.screening)
|
||||||
name: 'Watchlist Screening', // Changed the name
|
]
|
||||||
target: 'collapseScreening',
|
}],
|
||||||
subMenus: [
|
iconClass: 'fas fa-calendar'
|
||||||
{ 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: [
|
items: [{
|
||||||
{
|
name: 'File',
|
||||||
name: 'File Management', // Changed the name
|
|
||||||
target: 'collapseFiles',
|
target: 'collapseFiles',
|
||||||
subMenus: [
|
subMenus: [
|
||||||
{
|
createSubMenu('File Screening', 'collapseScreening', fileMenus.screening)
|
||||||
name: 'File Screening', // Changed the name
|
]
|
||||||
target: 'collapseScreening',
|
}],
|
||||||
subMenus: [
|
iconClass: 'fas fa-cogs'
|
||||||
{ 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',
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ const Compare = () => {
|
|||||||
const [imageCompareUrl, setImageCompareUrl] = useState('');
|
const [imageCompareUrl, setImageCompareUrl] = useState('');
|
||||||
const [verified, setVerified] = useState(null);
|
const [verified, setVerified] = useState(null);
|
||||||
|
|
||||||
const fileTypes = ["JPG", "JPEG", "PNG"];
|
const fileTypes = ["JPG", "JPEG"];
|
||||||
const [file, setFile] = useState(null); // For the first image
|
const [file, setFile] = useState(null); // For the first image
|
||||||
const [compareFile, setCompareFile] = useState(null); // For the second imag
|
const [compareFile, setCompareFile] = useState(null); // For the second imag
|
||||||
|
|
||||||
@ -113,21 +113,120 @@ const Compare = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleImageUpload = (file) => {
|
const handleImageUpload = (file) => {
|
||||||
if (file && fileTypes.includes(file.name.split('.').pop().toUpperCase())) {
|
if (!file) {
|
||||||
setSelectedImageName(file.name);
|
setUploadError('Please select a file');
|
||||||
setFile(file); // Store the file directly in state
|
return;
|
||||||
setUploadError(''); // Clear error if valid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) => {
|
const handleCompareImageUpload = (file) => {
|
||||||
if (file && fileTypes.includes(file.name.split('.').pop().toUpperCase())) {
|
if (!file) {
|
||||||
setSelectedCompareImageName(file.name);
|
setCompareUploadError('Please select a file');
|
||||||
setCompareFile(file); // Store the compare file directly in state
|
return;
|
||||||
setCompareUploadError(''); // Clear error if valid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 = () => {
|
const handleImageCancel = () => {
|
||||||
setSelectedImageName('');
|
setSelectedImageName('');
|
||||||
setImageUrl('');
|
setImageUrl('');
|
||||||
@ -289,23 +388,23 @@ const Compare = () => {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: '20px' }}>
|
<div style={styles.imagesWrapper}>
|
||||||
<div style={styles.imageContainer}>
|
<div style={styles.imageContainer}>
|
||||||
<img
|
<img
|
||||||
src={imageUrl || "path-to-your-image"}
|
src={imageUrl || "path-to-your-image"}
|
||||||
alt="Original Foto"
|
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>
|
||||||
|
|
||||||
<div style={styles.imageCompareContainer}>
|
<div style={styles.imageContainer}>
|
||||||
<img
|
<img
|
||||||
src={imageCompareUrl || "path-to-your-image"}
|
src={imageCompareUrl || "path-to-your-image"}
|
||||||
alt="Compare Foto"
|
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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -445,18 +544,27 @@ const Compare = () => {
|
|||||||
name="file"
|
name="file"
|
||||||
types={fileTypes}
|
types={fileTypes}
|
||||||
multiple={false}
|
multiple={false}
|
||||||
|
onTypeError={(err) => {
|
||||||
|
setUploadError('Only JPG/JPEG files are allowed');
|
||||||
|
setFile(null);
|
||||||
|
setSelectedImageName('');
|
||||||
|
}}
|
||||||
children={
|
children={
|
||||||
<div style={styles.uploadArea}>
|
<div style={styles.uploadArea}>
|
||||||
<i className="fas fa-cloud-upload-alt" style={styles.uploadIcon}></i>
|
<i className="fas fa-cloud-upload-alt" style={styles.uploadIcon}></i>
|
||||||
<p style={styles.uploadText}>Drag and Drop Here</p>
|
<p style={styles.uploadText}>Drag and Drop Here</p>
|
||||||
<p>Or</p>
|
<p>Or</p>
|
||||||
<a href="#">Browse</a>
|
<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>
|
<p className="text-muted">Supported file types: JPG, JPEG</p>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{uploadError && <small style={styles.uploadError}>{uploadError}</small>}
|
{uploadError && (
|
||||||
|
<small className="text-danger mt-2" style={{ fontSize: '12px' }}>
|
||||||
|
{uploadError}
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Display uploaded image name */}
|
{/* Display uploaded image name */}
|
||||||
@ -485,18 +593,27 @@ const Compare = () => {
|
|||||||
name="file"
|
name="file"
|
||||||
types={fileTypes}
|
types={fileTypes}
|
||||||
multiple={false}
|
multiple={false}
|
||||||
|
onTypeError={(err) => {
|
||||||
|
setCompareUploadError('Only JPG/JPEG files are allowed');
|
||||||
|
setCompareFile(null);
|
||||||
|
setSelectedCompareImageName('');
|
||||||
|
}}
|
||||||
children={
|
children={
|
||||||
<div style={styles.uploadArea}>
|
<div style={styles.uploadArea}>
|
||||||
<i className="fas fa-cloud-upload-alt" style={styles.uploadIcon}></i>
|
<i className="fas fa-cloud-upload-alt" style={styles.uploadIcon}></i>
|
||||||
<p style={styles.uploadText}>Drag and Drop Here</p>
|
<p style={styles.uploadText}>Drag and Drop Here</p>
|
||||||
<p>Or</p>
|
<p>Or</p>
|
||||||
<a href="#">Browse</a>
|
<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>
|
<p className="text-muted">Supported file types: JPG, JPEG</p>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{compareUploadError && <small style={styles.uploadError}>{compareUploadError}</small>}
|
{compareUploadError && (
|
||||||
|
<small className="text-danger mt-2" style={{ fontSize: '12px' }}>
|
||||||
|
{compareUploadError}
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Display uploaded image name */}
|
{/* Display uploaded image name */}
|
||||||
@ -678,25 +795,42 @@ const styles = {
|
|||||||
borderCollapse: 'collapse',
|
borderCollapse: 'collapse',
|
||||||
marginBottom: '20px',
|
marginBottom: '20px',
|
||||||
},
|
},
|
||||||
|
imagesWrapper: {
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: '2rem',
|
||||||
|
width: '100%',
|
||||||
|
margin: '2rem 0',
|
||||||
|
},
|
||||||
imageContainer: {
|
imageContainer: {
|
||||||
|
flex: '1 1 300px',
|
||||||
|
maxWidth: '500px',
|
||||||
|
minWidth: '280px',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
flex: 1,
|
|
||||||
padding: '10px',
|
|
||||||
},
|
},
|
||||||
imageCompareContainer: {
|
|
||||||
display: 'flex',
|
responsiveImage: {
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
flex: 1,
|
|
||||||
padding: '10px',
|
|
||||||
},
|
|
||||||
imageStyle: {
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
maxWidth: '150px', // Limit image width
|
maxHeight: '500px',
|
||||||
|
objectFit: 'contain',
|
||||||
borderRadius: '8px',
|
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) => ({
|
similarityText: (verified) => ({
|
||||||
border: '0.1px solid gray',
|
border: '0.1px solid gray',
|
||||||
|
@ -10,7 +10,7 @@ const Enroll = () => {
|
|||||||
const BASE_URL = process.env.REACT_APP_BASE_URL
|
const BASE_URL = process.env.REACT_APP_BASE_URL
|
||||||
const API_KEY = process.env.REACT_APP_API_KEY
|
const API_KEY = process.env.REACT_APP_API_KEY
|
||||||
|
|
||||||
const fileTypes = ["JPG", "JPEG", "PNG"];
|
const fileTypes = ["JPG", "JPEG"];
|
||||||
const [file, setFile] = useState(null);
|
const [file, setFile] = useState(null);
|
||||||
|
|
||||||
const [errorMessage, setErrorMessage] = useState('');
|
const [errorMessage, setErrorMessage] = useState('');
|
||||||
@ -137,21 +137,60 @@ const Enroll = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleImageUpload = (file) => {
|
const handleImageUpload = (file) => {
|
||||||
// Ensure the file is not undefined or null before accessing its properties
|
if (!file) {
|
||||||
if (file && file.name) {
|
setImageError('Please select a file');
|
||||||
const fileExtension = file.name.split('.').pop().toUpperCase();
|
return;
|
||||||
if (fileTypes.includes(fileExtension)) {
|
}
|
||||||
|
|
||||||
|
// 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);
|
setSelectedImageName(file.name);
|
||||||
setFile(file);
|
setFile(file);
|
||||||
setImageError(''); // Clear any previous errors
|
setImageError('');
|
||||||
} else {
|
};
|
||||||
alert('Image format is not supported');
|
|
||||||
setImageError('Image format is not supported');
|
img.onerror = () => {
|
||||||
|
URL.revokeObjectURL(objectUrl);
|
||||||
|
setImageError('Invalid image file');
|
||||||
setFile(null);
|
setFile(null);
|
||||||
}
|
setSelectedImageName('');
|
||||||
} else {
|
};
|
||||||
console.error('No file selected or invalid file object.');
|
|
||||||
}
|
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 = {
|
const styles = {
|
||||||
formGroup: {
|
formGroup: {
|
||||||
marginTop: '-45px',
|
marginTop: '-45px',
|
||||||
@ -633,6 +661,12 @@ const Enroll = () => {
|
|||||||
color: 'white',
|
color: 'white',
|
||||||
marginTop: '10px',
|
marginTop: '10px',
|
||||||
},
|
},
|
||||||
|
errorText: {
|
||||||
|
color: '#dc3545',
|
||||||
|
fontSize: '12px',
|
||||||
|
marginTop: '5px',
|
||||||
|
display: 'block'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isServer) {
|
if (!isServer) {
|
||||||
@ -745,6 +779,11 @@ const Enroll = () => {
|
|||||||
name="file"
|
name="file"
|
||||||
types={fileTypes}
|
types={fileTypes}
|
||||||
multiple={false}
|
multiple={false}
|
||||||
|
onTypeError={(err) => {
|
||||||
|
setImageError('Only JPG/JPEG files are allowed');
|
||||||
|
setFile(null);
|
||||||
|
setSelectedImageName('');
|
||||||
|
}}
|
||||||
onDrop={(files) => {
|
onDrop={(files) => {
|
||||||
if (files && files[0]) {
|
if (files && files[0]) {
|
||||||
handleImageUpload(files[0]);
|
handleImageUpload(files[0]);
|
||||||
@ -761,26 +800,18 @@ const Enroll = () => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<input
|
|
||||||
type="file"
|
{imageError && (
|
||||||
id="fileUpload"
|
<small className="text-danger mt-2" style={{ fontSize: '12px' }}>
|
||||||
ref={fileInputRef}
|
{imageError}
|
||||||
style={{ display: 'none' }}
|
</small>
|
||||||
accept="image/jpeg, image/jpg"
|
)}
|
||||||
onChange={e => handleImageUpload(e.target.files[0])}
|
|
||||||
/>
|
|
||||||
{imageError && <small style={{ color: 'red' }}>{imageError}</small>}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{selectedImageName && (
|
{selectedImageName && (
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<p><strong>File:</strong> {selectedImageName}</p>
|
<p><strong>File:</strong> {selectedImageName}</p>
|
||||||
{file && (
|
|
||||||
<p style={styles.fileSize}>
|
|
||||||
Size: {formatFileSize(file.size)}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<button className="btn btn-danger" onClick={handleImageCancel}>
|
<button className="btn btn-danger" onClick={handleImageCancel}>
|
||||||
<FontAwesomeIcon icon={faTimes} className="me-2" />Cancel
|
<FontAwesomeIcon icon={faTimes} className="me-2" />Cancel
|
||||||
</button>
|
</button>
|
||||||
@ -810,10 +841,15 @@ const Enroll = () => {
|
|||||||
<img
|
<img
|
||||||
src={imageUrl || "path-to-your-image"}
|
src={imageUrl || "path-to-your-image"}
|
||||||
alt="Contoh Foto"
|
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}>
|
<p style={isMobile ? { ...styles.imageDetails, fontSize: '14px' } : styles.imageDetails}>
|
||||||
{resultImageLabel} {/* Display resultImageLabel instead of selectedImageName */}
|
{resultImageLabel}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@ import { FileUploader } from 'react-drag-drop-files';
|
|||||||
import Select from 'react-select'
|
import Select from 'react-select'
|
||||||
import { ServerDownAnimation } from '../../../../assets/images';
|
import { ServerDownAnimation } from '../../../../assets/images';
|
||||||
|
|
||||||
const fileTypes = ["JPG", "JPEG", "PNG"]; // Allowed file types
|
const fileTypes = ["JPG", "JPEG"]; // Allowed file types
|
||||||
|
|
||||||
|
|
||||||
const Search = () => {
|
const Search = () => {
|
||||||
@ -126,34 +126,83 @@ const Search = () => {
|
|||||||
setIsSelectOpen(false);
|
setIsSelectOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add this after your existing state declarations
|
||||||
const handleImageUpload = (file) => {
|
const handleImageUpload = (file) => {
|
||||||
// Ensure the file is not undefined or null before accessing its properties
|
if (!file) {
|
||||||
if (file && file.name) {
|
setImageError('Please select a file');
|
||||||
const fileExtension = file.name.split('.').pop().toUpperCase();
|
return;
|
||||||
if (fileTypes.includes(fileExtension)) {
|
}
|
||||||
|
|
||||||
|
// 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);
|
setSelectedImageName(file.name);
|
||||||
setFile(file);
|
setFile(file);
|
||||||
setUploadedFile(file); // Set uploadedFile to the selected file
|
setImageError('');
|
||||||
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.');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
img.onerror = () => {
|
||||||
|
URL.revokeObjectURL(previewUrl);
|
||||||
|
setImageError('Invalid image file');
|
||||||
|
setFile(null);
|
||||||
|
setSelectedImageName('');
|
||||||
|
setUploadedFile(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
img.src = previewUrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update the handleImageCancel function
|
||||||
const handleImageCancel = () => {
|
const handleImageCancel = () => {
|
||||||
setSelectedImageName('');
|
setSelectedImageName('');
|
||||||
setFile(null);
|
setFile(null);
|
||||||
|
setUploadedFile(null);
|
||||||
if (fileInputRef.current) {
|
if (fileInputRef.current) {
|
||||||
fileInputRef.current.value = '';
|
fileInputRef.current.value = '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleCheckClick = async () => {
|
const handleCheckClick = async () => {
|
||||||
// Clear existing errors
|
// Clear existing errors
|
||||||
setApplicationIdError('');
|
setApplicationIdError('');
|
||||||
@ -356,10 +405,10 @@ const Search = () => {
|
|||||||
},
|
},
|
||||||
uploadArea: {
|
uploadArea: {
|
||||||
backgroundColor: '#e6f2ff',
|
backgroundColor: '#e6f2ff',
|
||||||
height: '40svh',
|
height: '250px',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
marginTop: '1rem',
|
marginTop: '1rem',
|
||||||
padding: '1rem',
|
paddingTop: '22px',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@ -367,6 +416,20 @@ const Search = () => {
|
|||||||
border: '1px solid #ced4da',
|
border: '1px solid #ced4da',
|
||||||
borderRadius: '0.25rem',
|
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: {
|
uploadIcon: {
|
||||||
fontSize: '40px',
|
fontSize: '40px',
|
||||||
color: '#0542cc',
|
color: '#0542cc',
|
||||||
@ -479,14 +542,37 @@ const Search = () => {
|
|||||||
gridTemplateColumns: isMobile ? 'repeat(2, 1fr)' : 'repeat(5, 1fr)', // Adjust based on isMobile
|
gridTemplateColumns: isMobile ? 'repeat(2, 1fr)' : 'repeat(5, 1fr)', // Adjust based on isMobile
|
||||||
gap: '16px',
|
gap: '16px',
|
||||||
},
|
},
|
||||||
resultItem: {
|
imageWrapper: {
|
||||||
border: '1px solid #ddd',
|
width: '100%',
|
||||||
|
height: '200px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundColor: '#f8f9fa',
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
padding: '12px',
|
},
|
||||||
boxShadow: '0 2px 5px rgba(0, 0, 0, 0.1)',
|
|
||||||
textAlign: 'center',
|
responsiveImage: {
|
||||||
transition: 'transform 0.3s ease', // Added transition for hover effect
|
maxWidth: '100%',
|
||||||
cursor: 'pointer',
|
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: {
|
resultItemHover: {
|
||||||
transform: 'scale(1.05)', // Slightly enlarge the card on hover
|
transform: 'scale(1.05)', // Slightly enlarge the card on hover
|
||||||
@ -627,7 +713,6 @@ const Search = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Upload Section */}
|
{/* Upload Section */}
|
||||||
{/* Drag and Drop File Uploader */}
|
|
||||||
<div className='col-md-6'>
|
<div className='col-md-6'>
|
||||||
<div className="row form-group mt-4">
|
<div className="row form-group mt-4">
|
||||||
<CustomLabel htmlFor="uploadPhoto" style={styles.customLabel}>
|
<CustomLabel htmlFor="uploadPhoto" style={styles.customLabel}>
|
||||||
@ -638,9 +723,18 @@ const Search = () => {
|
|||||||
name="file"
|
name="file"
|
||||||
types={fileTypes}
|
types={fileTypes}
|
||||||
multiple={false}
|
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={
|
children={
|
||||||
<div style={styles.uploadArea}>
|
<div style={isMobile ? styles.uploadAreaMobile : styles.uploadArea}>
|
||||||
<i className="fas fa-cloud-upload-alt" style={styles.uploadIcon}></i>
|
<i className="fas fa-cloud-upload-alt" style={styles.uploadIcon}></i>
|
||||||
<p style={styles.uploadText}>Drag and Drop Here</p>
|
<p style={styles.uploadText}>Drag and Drop Here</p>
|
||||||
<p>Or</p>
|
<p>Or</p>
|
||||||
@ -650,15 +744,12 @@ const Search = () => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<input
|
|
||||||
type="file"
|
{imageError && (
|
||||||
id="fileUpload"
|
<small className="text-danger mt-2" style={{ fontSize: '12px' }}>
|
||||||
ref={fileInputRef}
|
{imageError}
|
||||||
style={{ display: 'none' }}
|
</small>
|
||||||
accept="image/jpeg, image/jpg"
|
)}
|
||||||
onChange={e => handleImageUpload(e.target.files[0])}
|
|
||||||
/>
|
|
||||||
{imageError && <small style={styles.uploadError}>{imageError}</small>}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -686,8 +777,7 @@ const Search = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Results Section */}
|
{/* Results Section */}
|
||||||
{
|
{showResult && results.length > 0 && (
|
||||||
showResult && results.length > 0 && (
|
|
||||||
<div style={styles.containerResultStyle}>
|
<div style={styles.containerResultStyle}>
|
||||||
<h1 style={{ color: '#0542cc', textAlign: 'center' }}>Results</h1>
|
<h1 style={{ color: '#0542cc', textAlign: 'center' }}>Results</h1>
|
||||||
<div style={styles.resultGrid}>
|
<div style={styles.resultGrid}>
|
||||||
@ -698,13 +788,18 @@ const Search = () => {
|
|||||||
<p style={styles.resultText}>Similarity: {result.similarity}%</p>
|
<p style={styles.resultText}>Similarity: {result.similarity}%</p>
|
||||||
<p style={styles.resultText}>Distance: {result.distance}</p>
|
<p style={styles.resultText}>Distance: {result.distance}</p>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -3,12 +3,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|||||||
import { faChevronDown, faTimes } from '@fortawesome/free-solid-svg-icons';
|
import { faChevronDown, faTimes } from '@fortawesome/free-solid-svg-icons';
|
||||||
import Select from 'react-select'
|
import Select from 'react-select'
|
||||||
import { ServerDownAnimation } from '../../../../assets/images';
|
import { ServerDownAnimation } from '../../../../assets/images';
|
||||||
|
import { FileUploader } from 'react-drag-drop-files';
|
||||||
|
|
||||||
const Verify = () => {
|
const Verify = () => {
|
||||||
const BASE_URL = process.env.REACT_APP_BASE_URL;
|
const BASE_URL = process.env.REACT_APP_BASE_URL;
|
||||||
const API_KEY = process.env.REACT_APP_API_KEY;
|
const API_KEY = process.env.REACT_APP_API_KEY;
|
||||||
|
|
||||||
const fileTypes = ["JPG", "JPEG", "PNG"];
|
const fileTypes = ["JPG", "JPEG"];
|
||||||
const [file, setFile] = useState(null);
|
const [file, setFile] = useState(null);
|
||||||
|
|
||||||
const [errorMessage, setErrorMessage] = useState('');
|
const [errorMessage, setErrorMessage] = useState('');
|
||||||
@ -18,7 +19,7 @@ const Verify = () => {
|
|||||||
const [thresholdError, setThresholdError] = useState('');
|
const [thresholdError, setThresholdError] = useState('');
|
||||||
const [selectedImageName, setSelectedImageName] = useState('');
|
const [selectedImageName, setSelectedImageName] = useState('');
|
||||||
const [resultImageLabel, setResultImageLabel] = useState('');
|
const [resultImageLabel, setResultImageLabel] = useState('');
|
||||||
const fileInputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
const [showResult, setShowResult] = useState(false);
|
const [showResult, setShowResult] = useState(false);
|
||||||
const [applicationId, setApplicationId] = useState('');
|
const [applicationId, setApplicationId] = useState('');
|
||||||
const [thresholdId, setThresholdId] = useState('');
|
const [thresholdId, setThresholdId] = useState('');
|
||||||
@ -33,6 +34,7 @@ const Verify = () => {
|
|||||||
const [inputValueApplication, setInputValueApplication] = useState(''); // Controlled input value for Application ID
|
const [inputValueApplication, setInputValueApplication] = useState(''); // Controlled input value for Application ID
|
||||||
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
|
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
|
||||||
const [isServer, setIsServer] = useState(true);
|
const [isServer, setIsServer] = useState(true);
|
||||||
|
const [imageError, setImageError] = useState('');
|
||||||
|
|
||||||
const thresholdIds = [
|
const thresholdIds = [
|
||||||
{ id: 1, name: 'cosine', displayName: 'Basic' },
|
{ id: 1, name: 'cosine', displayName: 'Basic' },
|
||||||
@ -135,28 +137,68 @@ const Verify = () => {
|
|||||||
|
|
||||||
|
|
||||||
const handleImageUpload = (file) => {
|
const handleImageUpload = (file) => {
|
||||||
if (file && file.name) {
|
if (!file) {
|
||||||
const fileExtension = file.name.split('.').pop().toUpperCase();
|
setImageError('Please select a file');
|
||||||
if (fileTypes.includes(fileExtension)) {
|
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);
|
setSelectedImageName(file.name);
|
||||||
setFile(file);
|
setFile(file);
|
||||||
setUploadError('');
|
setImageError('');
|
||||||
} else {
|
};
|
||||||
alert('Image format is not supported');
|
|
||||||
setUploadError('Image format is not supported');
|
img.onerror = () => {
|
||||||
|
URL.revokeObjectURL(objectUrl);
|
||||||
|
setImageError('Invalid image file');
|
||||||
setFile(null);
|
setFile(null);
|
||||||
}
|
setSelectedImageName('');
|
||||||
} else {
|
};
|
||||||
console.error('No file selected or invalid file object.');
|
|
||||||
}
|
img.src = objectUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleImageCancel = () => {
|
const handleImageCancel = () => {
|
||||||
setSelectedImageName('');
|
setSelectedImageName('');
|
||||||
setFile(null);
|
setFile(null);
|
||||||
if (fileInputRef.current) {
|
if (inputRef.current) {
|
||||||
fileInputRef.current.value = '';
|
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) {
|
if (!isServer) {
|
||||||
return (
|
return (
|
||||||
<div style={{ textAlign: 'center', marginTop: '50px' }}>
|
<div style={{ textAlign: 'center', marginTop: '50px' }}>
|
||||||
@ -734,60 +765,50 @@ const Verify = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Upload Section */}
|
{/* Upload Section */}
|
||||||
<div className="col-md-6">
|
<div className='col-md-6'>
|
||||||
<div className="row form-group mt-4">
|
<div className="row form-group mt-4">
|
||||||
<CustomLabel htmlFor="uploadPhoto" style={styles.customLabel}>
|
<CustomLabel htmlFor="uploadPhoto" style={styles.customLabel}>
|
||||||
Upload Face Photo
|
Upload Face Photo
|
||||||
</CustomLabel>
|
</CustomLabel>
|
||||||
<div style={styles.uploadWrapper}>
|
<FileUploader
|
||||||
<div
|
handleChange={handleImageUpload}
|
||||||
style={styles.uploadArea}
|
name="file"
|
||||||
onDrop={(e) => {
|
types={fileTypes}
|
||||||
e.preventDefault();
|
multiple={false}
|
||||||
// Handle drop event
|
onTypeError={(err) => {
|
||||||
const file = e.dataTransfer.files[0];
|
setImageError('Only JPG/JPEG files are allowed');
|
||||||
handleImageUpload(file);
|
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>
|
<i className="fas fa-cloud-upload-alt" style={styles.uploadIcon}></i>
|
||||||
<p style={styles.uploadText}>Drag and Drop Here</p>
|
<p style={styles.uploadText}>Drag and Drop Here</p>
|
||||||
<p>Or</p>
|
<p>Or</p>
|
||||||
<a
|
<span
|
||||||
href="#"
|
style={{
|
||||||
onClick={(e) => {
|
color: '#0542cc',
|
||||||
e.preventDefault();
|
textDecoration: 'underline',
|
||||||
// Only open file input if no file is selected
|
cursor: 'pointer'
|
||||||
if (!fileInputRef.current.files.length) {
|
|
||||||
fileInputRef.current.click();
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
style={styles.browseLink}
|
|
||||||
>
|
>
|
||||||
Browse
|
Browse
|
||||||
</a>
|
</span>
|
||||||
<p className="text-muted" style={styles.uploadText}>
|
<p className="text-muted">Recommended size: 300x300 (Max File Size: 2MB)</p>
|
||||||
Recommended size: 300x300 (Max File Size: 2MB)
|
<p className="text-muted">Supported file types: JPG, JPEG</p>
|
||||||
</p>
|
|
||||||
<p className="text-muted" style={styles.uploadText}>
|
|
||||||
Supported file types: JPG, JPEG
|
|
||||||
</p>
|
|
||||||
</div>
|
</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])}
|
|
||||||
/>
|
/>
|
||||||
|
{imageError && (
|
||||||
{/* Error Message */}
|
<small className="text-danger mt-2" style={{ fontSize: '12px' }}>
|
||||||
{uploadError && <small style={styles.uploadError}>{uploadError}</small>}
|
{imageError}
|
||||||
</div>
|
</small>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -795,11 +816,6 @@ const Verify = () => {
|
|||||||
{selectedImageName && (
|
{selectedImageName && (
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<p><strong>File:</strong> {selectedImageName}</p>
|
<p><strong>File:</strong> {selectedImageName}</p>
|
||||||
{file && (
|
|
||||||
<p style={styles.fileSize}>
|
|
||||||
Size: {formatFileSize(file.size)}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<button className="btn btn-danger" onClick={handleImageCancel}>
|
<button className="btn btn-danger" onClick={handleImageCancel}>
|
||||||
<FontAwesomeIcon icon={faTimes} className="me-2" />Cancel
|
<FontAwesomeIcon icon={faTimes} className="me-2" />Cancel
|
||||||
</button>
|
</button>
|
||||||
@ -835,15 +851,22 @@ const Verify = () => {
|
|||||||
<img
|
<img
|
||||||
src={imageUrl || "path-to-your-image"}
|
src={imageUrl || "path-to-your-image"}
|
||||||
alt="Example Image"
|
alt="Example Image"
|
||||||
style={styles.imageStyle}
|
style={{
|
||||||
|
maxWidth: '100%',
|
||||||
|
width: 'auto',
|
||||||
|
height: 'auto',
|
||||||
|
objectFit: 'contain',
|
||||||
|
borderRadius: '5px'
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<p style={{ marginTop: '10px' }}>
|
<p style={{ marginTop: '10px' }}>
|
||||||
File Name: {resultImageLabel} {/* Display the resultImageLabel here */}
|
File Name: {resultImageLabel}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ const CustomLabel = ({ overRide, children, ...props }) => {
|
|||||||
const Verify = () => {
|
const Verify = () => {
|
||||||
const BASE_URL = process.env.REACT_APP_BASE_URL;
|
const BASE_URL = process.env.REACT_APP_BASE_URL;
|
||||||
const API_KEY = process.env.REACT_APP_API_KEY;
|
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 fileInputRef = useRef(null);
|
||||||
|
|
||||||
const [isMobile, setIsMobile] = useState(false);
|
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);
|
setFile(file);
|
||||||
setSelectedImageName(file.name);
|
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
|
// Validate file type
|
||||||
if (!fileTypes.includes(file.type)) {
|
if (!fileTypes.includes(file.type)) {
|
||||||
setImageError('Invalid file type. Only JPG, JPEG, and PNG are allowed.');
|
setImageError('Invalid file type. Only JPG and JPEG are allowed.');
|
||||||
} else if (file.size > 2 * 1024 * 1024) { // Max 2MB
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate file size
|
||||||
|
if (file.size > 2 * 1024 * 1024) {
|
||||||
setImageError('File size exceeds 2MB.');
|
setImageError('File size exceeds 2MB.');
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate image dimensions
|
||||||
|
await checkImageDimensions(file);
|
||||||
setImageError('');
|
setImageError('');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
setImageError(error);
|
||||||
|
setFile(null);
|
||||||
|
setSelectedImageName('');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -161,6 +205,33 @@ const Verify = () => {
|
|||||||
return Object.values(errors).every(error => error === '');
|
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
|
// Submit form and trigger OCR API
|
||||||
const handleCheckClick = async () => {
|
const handleCheckClick = async () => {
|
||||||
if (!validateForm()) {
|
if (!validateForm()) {
|
||||||
@ -183,16 +254,18 @@ const Verify = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
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();
|
const result = await response.json();
|
||||||
|
console.log('📡 API Response:', result);
|
||||||
|
|
||||||
// Log the full result to verify structure
|
// Log the full result to verify structure
|
||||||
console.log('OCR API Response:', result);
|
console.log('OCR API Response:', result);
|
||||||
|
|
||||||
if (result.status_code === 201) {
|
if (result.status_code === 201) {
|
||||||
const responseData = result.details.data?.['data-ktp'] || {};
|
const responseData = result.details.data?.['data-ktp'] || {};
|
||||||
|
console.log('✅ OCR Success:', result);
|
||||||
const updateQuota = result.details.data.quota
|
const updateQuota = result.details.data.quota
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
@ -230,10 +303,22 @@ const Verify = () => {
|
|||||||
await fetchImage(imageFileName); // Call the fetchImage function to fetch the image
|
await fetchImage(imageFileName); // Call the fetchImage function to fetch the image
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setErrorMessage('OCR processing failed');
|
const errorMsg = handleApiError(result);
|
||||||
|
setErrorMessage(errorMsg);
|
||||||
|
setShowResult(false);
|
||||||
|
console.error('❌ OCR Failed:', {
|
||||||
|
error: errorMsg,
|
||||||
|
response: result
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} 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 {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@ -350,9 +435,10 @@ const Verify = () => {
|
|||||||
borderRadius: '4px',
|
borderRadius: '4px',
|
||||||
},
|
},
|
||||||
errorText: {
|
errorText: {
|
||||||
color: '#721c24',
|
color: '#dc3545', // Bootstrap danger color
|
||||||
fontSize: '14px',
|
fontSize: '12px', // Small text
|
||||||
margin: '0',
|
marginTop: '5px',
|
||||||
|
fontWeight: '400'
|
||||||
},
|
},
|
||||||
loadingOverlay: {
|
loadingOverlay: {
|
||||||
position: 'fixed', // Gunakan fixed untuk overlay penuh layar
|
position: 'fixed', // Gunakan fixed untuk overlay penuh layar
|
||||||
@ -501,8 +587,9 @@ const Verify = () => {
|
|||||||
<CustomLabel htmlFor="imageInput" style={styles.customLabel}>
|
<CustomLabel htmlFor="imageInput" style={styles.customLabel}>
|
||||||
Upload Image (KTP)
|
Upload Image (KTP)
|
||||||
</CustomLabel>
|
</CustomLabel>
|
||||||
|
|
||||||
<div style={styles.uploadWrapper}>
|
<div style={styles.uploadWrapper}>
|
||||||
{/* Drag and Drop File Input */}
|
{/* Existing drag & drop area */}
|
||||||
<div
|
<div
|
||||||
style={styles.uploadArea}
|
style={styles.uploadArea}
|
||||||
onDrop={(e) => {
|
onDrop={(e) => {
|
||||||
@ -516,20 +603,21 @@ const Verify = () => {
|
|||||||
<p>Or</p>
|
<p>Or</p>
|
||||||
<a href="#" onClick={() => fileInputRef.current.click()} style={styles.browseLink}>Browse</a>
|
<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}>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>
|
</div>
|
||||||
|
|
||||||
{/* File Input */}
|
{/* File input */}
|
||||||
<input
|
<input
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
type="file"
|
type="file"
|
||||||
id="imageInput"
|
id="imageInput"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
style={{ display: 'none' }}
|
style={{ display: 'none' }}
|
||||||
accept="image/jpeg, image/png"
|
accept="image/jpeg, image/jpg"
|
||||||
onChange={(e) => handleImageUpload(e.target.files[0])}
|
onChange={(e) => handleImageUpload(e.target.files[0])}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Display selected file info */}
|
||||||
{selectedImageName && (
|
{selectedImageName && (
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<p><strong>File:</strong> {selectedImageName}</p>
|
<p><strong>File:</strong> {selectedImageName}</p>
|
||||||
@ -543,8 +631,12 @@ const Verify = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Display validation errors */}
|
||||||
{validationErrors.file && <p style={styles.errorText}>{validationErrors.file}</p>}
|
{validationErrors.file && <p style={styles.errorText}>{validationErrors.file}</p>}
|
||||||
|
{imageError && <p style={styles.errorText}>{imageError}</p>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ const CustomLabel = ({ overRide, children, ...props }) => {
|
|||||||
const Verify = () => {
|
const Verify = () => {
|
||||||
const BASE_URL = process.env.REACT_APP_BASE_URL;
|
const BASE_URL = process.env.REACT_APP_BASE_URL;
|
||||||
const API_KEY = process.env.REACT_APP_API_KEY;
|
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 fileInputRef = useRef(null);
|
||||||
|
|
||||||
const [isMobile, setIsMobile] = useState(false);
|
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);
|
setFile(file);
|
||||||
setSelectedImageName(file.name);
|
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
|
// Validate file type
|
||||||
if (!fileTypes.includes(file.type)) {
|
if (!fileTypes.includes(file.type)) {
|
||||||
setImageError('Invalid file type. Only JPG, JPEG, and PNG are allowed.');
|
setImageError('Invalid file type. Only JPG and JPEG are allowed.');
|
||||||
} else if (file.size > 2 * 1024 * 1024) { // Max 2MB
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate file size
|
||||||
|
if (file.size > 2 * 1024 * 1024) {
|
||||||
setImageError('File size exceeds 2MB.');
|
setImageError('File size exceeds 2MB.');
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate image dimensions
|
||||||
|
await checkImageDimensions(file);
|
||||||
setImageError('');
|
setImageError('');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
setImageError(error);
|
||||||
|
setFile(null);
|
||||||
|
setSelectedImageName('');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -340,9 +384,10 @@ const Verify = () => {
|
|||||||
borderRadius: '4px',
|
borderRadius: '4px',
|
||||||
},
|
},
|
||||||
errorText: {
|
errorText: {
|
||||||
color: '#721c24',
|
color: '#dc3545',
|
||||||
fontSize: '14px',
|
fontSize: '12px',
|
||||||
margin: '0',
|
marginTop: '5px',
|
||||||
|
fontWeight: '400'
|
||||||
},loadingOverlay: {
|
},loadingOverlay: {
|
||||||
position: 'fixed', // Gunakan fixed untuk overlay penuh layar
|
position: 'fixed', // Gunakan fixed untuk overlay penuh layar
|
||||||
top: 0,
|
top: 0,
|
||||||
@ -515,7 +560,7 @@ const Verify = () => {
|
|||||||
id="imageInput"
|
id="imageInput"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
style={{ display: 'none' }}
|
style={{ display: 'none' }}
|
||||||
accept="image/jpeg, image/png"
|
accept="image/jpeg, image/jpg"
|
||||||
onChange={(e) => handleImageUpload(e.target.files[0])}
|
onChange={(e) => handleImageUpload(e.target.files[0])}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -532,7 +577,9 @@ const Verify = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{/* Display validation errors */}
|
||||||
{validationErrors.file && <p style={styles.errorText}>{validationErrors.file}</p>}
|
{validationErrors.file && <p style={styles.errorText}>{validationErrors.file}</p>}
|
||||||
|
{imageError && <p style={styles.errorText}>{imageError}</p>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user