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,30 +245,34 @@ 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) {
return response.json().then(errorData => {
throw new Error(errorData.detail);
}); });
if (!response.ok) {
throw new Error('OCR processing failed, Please check your input, Please check your input');
} }
if (!response.ok) {
const result = await response.json(); throw new Error('OCR processing failed, Please check your input');
}
return response.json();
})
.then(result => {
console.log('📡 API Response:', result); console.log('📡 API Response:', result);
// Log the full result to verify structure if (result.status_code !== 201) {
console.log('OCR API Response:', result); throw new Error(handleApiError(result));
}
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 = {
nik: responseData.nik || 'N/A', nik: responseData.nik || 'N/A',
@ -283,47 +289,32 @@ const Verify = () => {
occupation: responseData.pekerjaan || 'N/A', occupation: responseData.pekerjaan || 'N/A',
rtRw: `${responseData.rt || 'N/A'}/${responseData.rw || 'N/A'}`, rtRw: `${responseData.rt || 'N/A'}/${responseData.rw || 'N/A'}`,
nationality: responseData.kewarganegaraan || 'N/A', nationality: responseData.kewarganegaraan || 'N/A',
imageUrl: result.details.data?.image_url || '', // Properly access image_url imageUrl: result.details.data?.image_url || '',
}; };
setSelectedQuota(updateQuota) setSelectedQuota(updateQuota);
console.log('Image URL from OCR:', result.details.data?.image_url); // Log the image URL correctly
setData(data); setData(data);
setShowResult(true); setShowResult(true);
setErrorMessage(''); setErrorMessage('');
setSelectedImageName(''); setSelectedImageName('');
setResultImageLabel(selectedImageName); setResultImageLabel(selectedImageName);
// Fetch image if image URL exists in the result
if (result.details.data?.image_url) { if (result.details.data?.image_url) {
const imageFileName = result.details.data.image_url.split('/').pop(); // Get the image filename const imageFileName = result.details.data.image_url.split('/').pop();
console.log('Image file name:', imageFileName); // Debug the file name return fetchImage(imageFileName);
await fetchImage(imageFileName); // Call the fetchImage function to fetch the image
} }
} else { })
const errorMsg = handleApiError(result); .catch(error => {
setErrorMessage(errorMsg); console.error('🔥 Request Failed:', error);
setErrorMessage(error.message);
setShowResult(false); setShowResult(false);
console.error('❌ OCR Failed:', { })
error: errorMsg, .finally(() => {
response: result
});
}
} catch (error) {
const errorMsg = 'Internal Server Error';
setErrorMessage(errorMsg);
setShowResult(false);
console.error('🔥 Request Failed:', {
error: error.message,
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) => {
setIsLoading(true); setIsLoading(true);
@ -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);
@ -205,10 +206,33 @@ const Verify = () => {
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();
// Log the full result to verify structure
console.log('OCR API Response:', result);
if (result.status_code === 201) {
const responseData = result.details.data?.['data-npwp'] || {}; const responseData = result.details.data?.['data-npwp'] || {};
const updateQuota = result.details.data.quota const updateQuota = result.details.data.quota;
const data = { const data = {
npwp: responseData.npwp || 'N/A', npwp: responseData.npwp || 'N/A',
npwpName: responseData.name || 'N/A', npwpName: responseData.name || 'N/A',
npwpAddress: responseData.address || 'N/A', npwpAddress: responseData.address || 'N/A',
npwpX: responseData.npwp_x || 'N/A', npwpX: responseData.npwp_x || 'N/A',
imageUrl: result.details.data?.image_url || '', // Properly access image_url imageUrl: result.details.data?.image_url || '',
}; };
setSelectedQuota(updateQuota) setSelectedQuota(updateQuota);
console.log('Image URL from OCR:', result.details.data?.image_url); // Log the image URL correctly
setData(data); setData(data);
setShowResult(true); setShowResult(true);
setErrorMessage(''); setErrorMessage('');
setSelectedImageName(''); setSelectedImageName('');
setResultImageLabel(selectedImageName); setResultImageLabel(selectedImageName);
// Fetch image if image URL exists in the result
if (result.details.data?.image_url) { if (result.details.data?.image_url) {
const imageFileName = result.details.data.image_url.split('/').pop(); // Get the image filename const imageFileName = result.details.data.image_url.split('/').pop();
console.log('Image file name:', imageFileName); // Debug the file name return fetchImage(imageFileName);
await fetchImage(imageFileName); // Call the fetchImage function to fetch the image
} }
} else { })
setErrorMessage('OCR processing failed'); .catch(error => {
} console.error('🔥 Request Failed:', error);
} catch (error) { setErrorMessage(error.message);
setErrorMessage(error.message || 'Error during OCR processing'); setShowResult(false);
} finally { })
.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,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 > 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);
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,10 +205,33 @@ const Verify = () => {
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,28 +239,34 @@ 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();
// Log the full result to verify structure
console.log('OCR API Response:', result);
if (result.status_code === 201) {
const responseData = result.details.data?.['data-sim'] || {}; const responseData = result.details.data?.['data-sim'] || {};
const updateQuota = result.details.data.quota const updateQuota = result.details.data.quota;
const data = { const data = {
sim: responseData.sim || 'N/A', sim: responseData.sim || 'N/A',
@ -208,35 +281,32 @@ const Verify = () => {
simHeight: responseData.height || 'N/A', simHeight: responseData.height || 'N/A',
simBlood: responseData.blood || 'N/A', simBlood: responseData.blood || 'N/A',
simOccupation: responseData.occupation || 'N/A', simOccupation: responseData.occupation || 'N/A',
imageUrl: result.details.data?.image_url || '', // Properly access image_url imageUrl: result.details.data?.image_url || '',
}; };
setSelectedQuota(updateQuota) setSelectedQuota(updateQuota);
console.log('Image URL from OCR:', result.details.data?.image_url); // Log the image URL correctly
setData(data); setData(data);
setShowResult(true); setShowResult(true);
setErrorMessage(''); setErrorMessage('');
setSelectedImageName(''); setSelectedImageName('');
setResultImageLabel(selectedImageName); setResultImageLabel(selectedImageName);
// Fetch image if image URL exists in the result
if (result.details.data?.image_url) { if (result.details.data?.image_url) {
const imageFileName = result.details.data.image_url.split('/').pop(); // Get the image filename const imageFileName = result.details.data.image_url.split('/').pop();
console.log('Image file name:', imageFileName); // Debug the file name return fetchImage(imageFileName);
await fetchImage(imageFileName); // Call the fetchImage function to fetch the image
} }
} else { })
setErrorMessage('OCR processing failed'); .catch(error => {
} console.error('🔥 Request Failed:', error);
} catch (error) { setErrorMessage(error.message);
setErrorMessage(error.message || 'Error during OCR processing'); setShowResult(false);
} finally { })
.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>