Fix issue error handling (KTP dan NPWP)

This commit is contained in:
Rizqika 2024-12-06 11:20:13 +07:00
parent 2a559da0d3
commit c0709ca144
3 changed files with 301 additions and 241 deletions

View File

@ -126,8 +126,8 @@ const Verify = () => {
img.onload = () => { img.onload = () => {
URL.revokeObjectURL(img.src); URL.revokeObjectURL(img.src);
if (img.width > 300 || img.height > 300) { if (img.width > 320 || img.height > 200) {
reject('Image dimensions must not exceed 300x300 pixels'); reject('Image dimensions must not exceed 320x200 pixels');
} else { } else {
resolve(true); resolve(true);
} }
@ -142,6 +142,7 @@ const Verify = () => {
// Update handleImageUpload to include dimension checking // Update handleImageUpload to include dimension checking
const handleImageUpload = async (file) => { const handleImageUpload = async (file) => {
setErrorMessage('');
setFile(file); setFile(file);
setSelectedImageName(file.name); setSelectedImageName(file.name);
@ -206,7 +207,8 @@ const Verify = () => {
}; };
const handleApiError = (response) => { const handleApiError = (response) => {
// Handle 400 Bad Request // Handle 400 Bad
if (response.status_code === 400) { if (response.status_code === 400) {
console.error('❌ Bad Request:', { console.error('❌ Bad Request:', {
status: response.status_code, status: response.status_code,
@ -233,9 +235,9 @@ const Verify = () => {
}; };
// Submit form and trigger OCR API // Submit form and trigger OCR API
const handleCheckClick = async () => { const handleCheckClick = () => {
if (!validateForm()) { if (!validateForm()) {
return; // Form is not valid return;
} }
setIsLoading(true); setIsLoading(true);
@ -243,86 +245,75 @@ const Verify = () => {
formData.append('application_id', applicationId); formData.append('application_id', applicationId);
formData.append('file', file); formData.append('file', file);
try { fetch(`${BASE_URL}/ocr-ktp`, {
const response = await fetch(`${BASE_URL}/ocr-ktp`, { method: 'POST',
method: 'POST', headers: {
headers: { 'accept': 'application/json',
'accept': 'application/json', 'x-api-key': API_KEY,
'x-api-key': API_KEY, },
}, body: formData,
body: formData, })
}); .then(response => {
if (response.status === 400) {
if (!response.ok) { return response.json().then(errorData => {
throw new Error('OCR processing failed, Please check your input, Please check your input'); throw new Error(errorData.detail);
}
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 = {
nik: responseData.nik || 'N/A',
district: responseData.kecamatan || 'N/A',
name: responseData.nama || 'N/A',
city: responseData.kabkot || 'N/A',
dob: responseData.tanggal_lahir || 'N/A',
state: responseData.provinsi || 'N/A',
gender: responseData.jenis_kelamin || 'N/A',
religion: responseData.agama || 'N/A',
bloodType: responseData.golongan_darah || 'N/A',
maritalStatus: responseData.status_perkawinan || 'N/A',
address: responseData.alamat || 'N/A',
occupation: responseData.pekerjaan || 'N/A',
rtRw: `${responseData.rt || 'N/A'}/${responseData.rw || 'N/A'}`,
nationality: responseData.kewarganegaraan || 'N/A',
imageUrl: result.details.data?.image_url || '', // Properly access image_url
};
setSelectedQuota(updateQuota)
console.log('Image URL from OCR:', result.details.data?.image_url); // Log the image URL correctly
setData(data);
setShowResult(true);
setErrorMessage('');
setSelectedImageName('');
setResultImageLabel(selectedImageName);
// Fetch image if image URL exists in the result
if (result.details.data?.image_url) {
const imageFileName = result.details.data.image_url.split('/').pop(); // Get the image filename
console.log('Image file name:', imageFileName); // Debug the file name
await fetchImage(imageFileName); // Call the fetchImage function to fetch the image
}
} else {
const errorMsg = handleApiError(result);
setErrorMessage(errorMsg);
setShowResult(false);
console.error('❌ OCR Failed:', {
error: errorMsg,
response: result
}); });
} }
} catch (error) { if (!response.ok) {
const errorMsg = 'Internal Server Error'; throw new Error('OCR processing failed, Please check your input');
setErrorMessage(errorMsg); }
return response.json();
})
.then(result => {
console.log('📡 API Response:', result);
if (result.status_code !== 201) {
throw new Error(handleApiError(result));
}
const responseData = result.details.data?.['data-ktp'] || {};
const updateQuota = result.details.data.quota;
const data = {
nik: responseData.nik || 'N/A',
district: responseData.kecamatan || 'N/A',
name: responseData.nama || 'N/A',
city: responseData.kabkot || 'N/A',
dob: responseData.tanggal_lahir || 'N/A',
state: responseData.provinsi || 'N/A',
gender: responseData.jenis_kelamin || 'N/A',
religion: responseData.agama || 'N/A',
bloodType: responseData.golongan_darah || 'N/A',
maritalStatus: responseData.status_perkawinan || 'N/A',
address: responseData.alamat || 'N/A',
occupation: responseData.pekerjaan || 'N/A',
rtRw: `${responseData.rt || 'N/A'}/${responseData.rw || 'N/A'}`,
nationality: responseData.kewarganegaraan || 'N/A',
imageUrl: result.details.data?.image_url || '',
};
setSelectedQuota(updateQuota);
setData(data);
setShowResult(true);
setErrorMessage('');
setSelectedImageName('');
setResultImageLabel(selectedImageName);
if (result.details.data?.image_url) {
const imageFileName = result.details.data.image_url.split('/').pop();
return fetchImage(imageFileName);
}
})
.catch(error => {
console.error('🔥 Request Failed:', error);
setErrorMessage(error.message);
setShowResult(false); setShowResult(false);
console.error('🔥 Request Failed:', { })
error: error.message, .finally(() => {
detail: error
});
} finally {
setIsLoading(false); setIsLoading(false);
} });
}; };
// The fetchImage function you already have in your code // The fetchImage function you already have in your code
const fetchImage = async (imageFileName) => { const fetchImage = async (imageFileName) => {
@ -602,7 +593,7 @@ const Verify = () => {
<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="#" 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: 320x200 (Max File Size: 2MB)</p>
<p className="text-muted" style={styles.uploadText}>Supported file types: JPG, JPEG</p> <p className="text-muted" style={styles.uploadText}>Supported file types: JPG, JPEG</p>
</div> </div>

View File

@ -126,8 +126,8 @@ const Verify = () => {
img.onload = () => { img.onload = () => {
URL.revokeObjectURL(img.src); URL.revokeObjectURL(img.src);
if (img.width > 300 || img.height > 300) { if (img.width > 320 || img.height > 200) {
reject('Image dimensions must not exceed 300x300 pixels'); reject('Image dimensions must not exceed 320x200 pixels');
} else { } else {
resolve(true); resolve(true);
} }
@ -142,6 +142,7 @@ const Verify = () => {
// Update handleImageUpload function // Update handleImageUpload function
const handleImageUpload = async (file) => { const handleImageUpload = async (file) => {
setErrorMessage('');
setFile(file); setFile(file);
setSelectedImageName(file.name); setSelectedImageName(file.name);
@ -190,25 +191,48 @@ const Verify = () => {
applicationId: '', applicationId: '',
file: '' file: ''
}; };
if (!applicationId) { if (!applicationId) {
errors.applicationId = 'Please select an Application ID.'; errors.applicationId = 'Please select an Application ID.';
} }
if (!file) { if (!file) {
errors.file = 'Please upload an image file.'; errors.file = 'Please upload an image file.';
} else if (imageError) { } else if (imageError) {
errors.file = imageError; errors.file = imageError;
} }
setValidationErrors(errors); setValidationErrors(errors);
return Object.values(errors).every(error => error === ''); return Object.values(errors).every(error => error === '');
}; };
// Submit form and trigger OCR API const handleApiError = (response) => {
const handleCheckClick = async () => { if (response.status_code === 400) {
console.error('❌ Bad Request:', {
status: response.status_code,
detail: response.detail || 'Mohon Upload NPWP'
});
return response.detail || 'Mohon Upload NPWP';
}
if (response.status_code >= 500) {
console.error('🔥 Server Error:', {
status: response.status_code,
message: 'Internal Server Error'
});
return 'Internal Server Error';
}
console.error('⚠️ Unknown Error:', {
status: response.status_code,
response
});
return 'Terjadi kesalahan. Silakan coba lagi.';
};
const handleCheckClick = () => {
if (!validateForm()) { if (!validateForm()) {
return; // Form is not valid return;
} }
setIsLoading(true); setIsLoading(true);
@ -216,63 +240,66 @@ const Verify = () => {
formData.append('application_id', applicationId); formData.append('application_id', applicationId);
formData.append('file', file); formData.append('file', file);
try { fetch(`${BASE_URL}/ocr-npwp`, {
const response = await fetch(`${BASE_URL}/ocr-npwp`, { method: 'POST',
method: 'POST', headers: {
headers: { 'accept': 'application/json',
'accept': 'application/json', 'x-api-key': API_KEY,
'x-api-key': API_KEY, },
}, body: formData,
body: formData, })
}); .then(response => {
if (response.status === 400) {
return response.json().then(errorData => {
throw new Error(errorData.detail);
});
}
if (!response.ok) { if (!response.ok) {
throw new Error('OCR processing failed'); throw new Error('OCR processing failed, Please check your input');
}
return response.json();
})
.then(result => {
console.log('📡 API Response:', result);
if (result.status_code !== 201) {
throw new Error(handleApiError(result));
} }
const result = await response.json(); const responseData = result.details.data?.['data-npwp'] || {};
const updateQuota = result.details.data.quota;
// Log the full result to verify structure const data = {
console.log('OCR API Response:', result); npwp: responseData.npwp || 'N/A',
npwpName: responseData.name || 'N/A',
npwpAddress: responseData.address || 'N/A',
npwpX: responseData.npwp_x || 'N/A',
imageUrl: result.details.data?.image_url || '',
};
if (result.status_code === 201) { setSelectedQuota(updateQuota);
const responseData = result.details.data?.['data-npwp'] || {}; setData(data);
const updateQuota = result.details.data.quota setShowResult(true);
setErrorMessage('');
const data = { setSelectedImageName('');
npwp: responseData.npwp || 'N/A', setResultImageLabel(selectedImageName);
npwpName: responseData.name || 'N/A',
npwpAddress: responseData.address || 'N/A',
npwpX: responseData.npwp_x || 'N/A',
imageUrl: result.details.data?.image_url || '', // Properly access image_url
};
setSelectedQuota(updateQuota)
console.log('Image URL from OCR:', result.details.data?.image_url); // Log the image URL correctly if (result.details.data?.image_url) {
const imageFileName = result.details.data.image_url.split('/').pop();
setData(data); return fetchImage(imageFileName);
setShowResult(true);
setErrorMessage('');
setSelectedImageName('');
setResultImageLabel(selectedImageName);
// Fetch image if image URL exists in the result
if (result.details.data?.image_url) {
const imageFileName = result.details.data.image_url.split('/').pop(); // Get the image filename
console.log('Image file name:', imageFileName); // Debug the file name
await fetchImage(imageFileName); // Call the fetchImage function to fetch the image
}
} else {
setErrorMessage('OCR processing failed');
} }
} catch (error) { })
setErrorMessage(error.message || 'Error during OCR processing'); .catch(error => {
} finally { console.error('🔥 Request Failed:', error);
setErrorMessage(error.message);
setShowResult(false);
})
.finally(() => {
setIsLoading(false); setIsLoading(false);
} });
}; };
// The fetchImage function you already have in your code // The fetchImage function you already have in your code
const fetchImage = async (imageFileName) => { const fetchImage = async (imageFileName) => {
setIsLoading(true); setIsLoading(true);
@ -425,16 +452,6 @@ const Verify = () => {
}, },
}; };
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' }}>
@ -549,7 +566,7 @@ const Verify = () => {
<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="#" 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: 320x200 (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, PNG</p>
</div> </div>
@ -567,11 +584,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>

View File

@ -119,19 +119,63 @@ const Verify = () => {
} }
}; };
const handleImageUpload = (file) => { const checkImageDimensions = (file) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = URL.createObjectURL(file);
img.onload = () => {
URL.revokeObjectURL(img.src);
if (img.width > 320 || img.height > 200) {
reject('Image dimensions must not exceed 320x200 pixels');
} else {
resolve(true);
}
};
img.onerror = () => {
URL.revokeObjectURL(img.src);
reject('Failed to load image');
};
});
};
const handleImageUpload = async (file) => {
setErrorMessage('');
setFile(file); setFile(file);
setSelectedImageName(file.name); setSelectedImageName(file.name);
// Validate file type try {
if (!fileTypes.includes(file.type)) { // Check if file is PNG
setImageError('Invalid file type. Only JPG, JPEG, and PNG are allowed.'); if (file.type === 'image/png') {
} else if (file.size > 2 * 1024 * 1024) { // Max 2MB setImageError('The image format is not suitable. Only JPG and JPEG files are allowed.');
setImageError('File size exceeds 2MB.'); setFile(null);
} else { setSelectedImageName('');
return;
}
// Validate file type
if (!fileTypes.includes(file.type)) {
setImageError('Invalid file type. Only JPG and JPEG are allowed.');
return;
}
// Validate file size
if (file.size > 2 * 1024 * 1024) {
setImageError('File size exceeds 2MB.');
return;
}
// Validate image dimensions
await checkImageDimensions(file);
setImageError(''); setImageError('');
} catch (error) {
setImageError(error);
setFile(null);
setSelectedImageName('');
} }
}; };
const handleImageCancel = () => { const handleImageCancel = () => {
setFile(null); setFile(null);
@ -146,25 +190,48 @@ const Verify = () => {
applicationId: '', applicationId: '',
file: '' file: ''
}; };
if (!applicationId) { if (!applicationId) {
errors.applicationId = 'Please select an Application ID.'; errors.applicationId = 'Please select an Application ID.';
} }
if (!file) { if (!file) {
errors.file = 'Please upload an image file.'; errors.file = 'Please upload an image file.';
} else if (imageError) { } else if (imageError) {
errors.file = imageError; errors.file = imageError;
} }
setValidationErrors(errors); setValidationErrors(errors);
return Object.values(errors).every(error => error === ''); return Object.values(errors).every(error => error === '');
}; };
// Submit form and trigger OCR API const handleApiError = (response) => {
const handleCheckClick = async () => { if (response.status_code === 400) {
console.error('❌ Bad Request:', {
status: response.status_code,
detail: response.detail || 'Mohon Upload SIM'
});
return response.detail || 'Mohon Upload SIM';
}
if (response.status_code >= 500) {
console.error('🔥 Server Error:', {
status: response.status_code,
message: 'Internal Server Error'
});
return 'Internal Server Error';
}
console.error('⚠️ Unknown Error:', {
status: response.status_code,
response
});
return 'Terjadi kesalahan. Silakan coba lagi.';
};
const handleCheckClick = () => {
if (!validateForm()) { if (!validateForm()) {
return; // Form is not valid return;
} }
setIsLoading(true); setIsLoading(true);
@ -172,71 +239,74 @@ const Verify = () => {
formData.append('application_id', applicationId); formData.append('application_id', applicationId);
formData.append('file', file); formData.append('file', file);
try { fetch(`${BASE_URL}/ocr-sim`, {
const response = await fetch(`${BASE_URL}/ocr-sim`, { method: 'POST',
method: 'POST', headers: {
headers: { 'accept': 'application/json',
'accept': 'application/json', 'x-api-key': API_KEY,
'x-api-key': API_KEY, },
}, body: formData,
body: formData, })
}); .then(response => {
if (response.status === 400) {
return response.json().then(errorData => {
throw new Error(errorData.detail);
});
}
if (!response.ok) { if (!response.ok) {
throw new Error('OCR processing failed'); throw new Error('OCR processing failed, Please check your input');
}
return response.json();
})
.then(result => {
console.log('📡 API Response:', result);
if (result.status_code !== 201) {
throw new Error(handleApiError(result));
} }
const result = await response.json(); const responseData = result.details.data?.['data-sim'] || {};
const updateQuota = result.details.data.quota;
// Log the full result to verify structure const data = {
console.log('OCR API Response:', result); sim: responseData.sim || 'N/A',
simName: responseData.name || 'N/A',
simAddress: responseData.address || 'N/A',
simType: responseData.type || 'N/A',
simValidUntil: responseData.valid_until || 'N/A',
simDomain: responseData.domain || 'N/A',
simBirthPlace: responseData.birthplace || 'N/A',
simDob: responseData.dob || 'N/A',
simGender: responseData.gender || 'N/A',
simHeight: responseData.height || 'N/A',
simBlood: responseData.blood || 'N/A',
simOccupation: responseData.occupation || 'N/A',
imageUrl: result.details.data?.image_url || '',
};
if (result.status_code === 201) { setSelectedQuota(updateQuota);
const responseData = result.details.data?.['data-sim'] || {}; setData(data);
const updateQuota = result.details.data.quota setShowResult(true);
setErrorMessage('');
const data = { setSelectedImageName('');
sim: responseData.sim || 'N/A', setResultImageLabel(selectedImageName);
simName: responseData.name || 'N/A',
simAddress: responseData.address || 'N/A',
simType: responseData.type || 'N/A',
simValidUntil: responseData.valid_until || 'N/A',
simDomain: responseData.domain || 'N/A',
simBirthPlace: responseData.birthplace || 'N/A',
simDob: responseData.dob || 'N/A',
simGender: responseData.gender || 'N/A',
simHeight: responseData.height || 'N/A',
simBlood: responseData.blood || 'N/A',
simOccupation: responseData.occupation || 'N/A',
imageUrl: result.details.data?.image_url || '', // Properly access image_url
};
setSelectedQuota(updateQuota)
console.log('Image URL from OCR:', result.details.data?.image_url); // Log the image URL correctly if (result.details.data?.image_url) {
const imageFileName = result.details.data.image_url.split('/').pop();
setData(data); return fetchImage(imageFileName);
setShowResult(true);
setErrorMessage('');
setSelectedImageName('');
setResultImageLabel(selectedImageName);
// Fetch image if image URL exists in the result
if (result.details.data?.image_url) {
const imageFileName = result.details.data.image_url.split('/').pop(); // Get the image filename
console.log('Image file name:', imageFileName); // Debug the file name
await fetchImage(imageFileName); // Call the fetchImage function to fetch the image
}
} else {
setErrorMessage('OCR processing failed');
} }
} catch (error) { })
setErrorMessage(error.message || 'Error during OCR processing'); .catch(error => {
} finally { console.error('🔥 Request Failed:', error);
setErrorMessage(error.message);
setShowResult(false);
})
.finally(() => {
setIsLoading(false); setIsLoading(false);
} });
}; };
// The fetchImage function you already have in your code // The fetchImage function you already have in your code
const fetchImage = async (imageFileName) => { const fetchImage = async (imageFileName) => {
setIsLoading(true); setIsLoading(true);
@ -388,16 +458,6 @@ const Verify = () => {
}, },
}; };
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' }}>
@ -512,7 +572,7 @@ const Verify = () => {
<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="#" 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: 320x200 (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, PNG</p>
</div> </div>
@ -523,24 +583,21 @@ 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])}
/> />
{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>
</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>