|
|
|
@@ -1,293 +1,371 @@
|
|
|
|
|
|
|
|
|
|
let button_read_mykad = document.getElementById("button_read_mykad");
|
|
|
|
|
let button_close_modal = document.querySelector("#daftar_pelanggan_mykad_reader button.btn.btn-danger");
|
|
|
|
|
let original_photo_loader = document.querySelector("#photo-mykad").src;
|
|
|
|
|
let photo_mykad = document.querySelector("#photo-mykad");
|
|
|
|
|
let original_photo_loader = photo_mykad ? photo_mykad.src : "";
|
|
|
|
|
|
|
|
|
|
let showLoader = () => {
|
|
|
|
|
let pageLoader = document.getElementById('page_loader');
|
|
|
|
|
pageLoader.removeAttribute('class');
|
|
|
|
|
pageLoader.classList.add('loading');
|
|
|
|
|
};
|
|
|
|
|
const MYKAD_READER_TIMEOUT_MS = 45000;
|
|
|
|
|
const MYKAD_READER_DEFAULT_ENDPOINT = "http://localhost:17321/read";
|
|
|
|
|
|
|
|
|
|
let hideLoader = () => {
|
|
|
|
|
let pageLoader = document.getElementById('page_loader');
|
|
|
|
|
pageLoader.removeAttribute('class');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function handleMyKadReader()
|
|
|
|
|
{
|
|
|
|
|
showLoader();
|
|
|
|
|
WebSocketManager.init("wss://localhost:5963/mykadwebsocket");
|
|
|
|
|
WebSocketManager.listReader(callbackDoneGetListCardReader);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function callbackDoneGetListCardReader(data, respcode)
|
|
|
|
|
{
|
|
|
|
|
console.log('done get list card reader');
|
|
|
|
|
|
|
|
|
|
if(respcode === -1){
|
|
|
|
|
console.log({'location': 'callbackDoneGetListCardReader', 'data' : data} );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let isDevicesConnected = Object.keys(data).length > 0;
|
|
|
|
|
|
|
|
|
|
if(! isDevicesConnected){
|
|
|
|
|
hideLoader();
|
|
|
|
|
|
|
|
|
|
Swal.fire({ icon: 'error', title: 'Pembaca MyKad tidak dapat dikesan, sila periksa sambungan', });
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let slots = data.toString().split(',');
|
|
|
|
|
WebSocketManager.open(doneOpenReaderConnection, slots[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function doneOpenReaderConnection()
|
|
|
|
|
{
|
|
|
|
|
WebSocketManager.readAllText(callBackAfterReadMykad);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleMyKadReaderError(sw12AndApiName)
|
|
|
|
|
{
|
|
|
|
|
hideLoader();
|
|
|
|
|
|
|
|
|
|
let error_handler_dictionary = {
|
|
|
|
|
'0x80100069' : 'Tiada MyKad dikesan, sila masukkan Mykad untuk dibaca',
|
|
|
|
|
'0x80100066' : 'MyKad mungkin dimasukkan terbalik, sila masukkan dengan betul',
|
|
|
|
|
'default' : sw12AndApiName,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let error_data = sw12AndApiName.split(" ");
|
|
|
|
|
|
|
|
|
|
let error_code = error_data.filter(x => x.startsWith('0x')).splice(-1)
|
|
|
|
|
|
|
|
|
|
let error_message = error_handler_dictionary['default'];
|
|
|
|
|
|
|
|
|
|
let hasDefinedError = error_handler_dictionary.hasOwnProperty(error_code);
|
|
|
|
|
|
|
|
|
|
if(hasDefinedError){
|
|
|
|
|
error_message = error_handler_dictionary[error_code];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Swal.fire({ icon: 'error', title: error_message, });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function callBackAfterReadMykad(data, respcode, sw12AndApiName)
|
|
|
|
|
{
|
|
|
|
|
if(respcode !== 0){
|
|
|
|
|
handleMyKadReaderError(sw12AndApiName);
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
checkIfMyKadExists(data.IDNum)
|
|
|
|
|
.then(response => {
|
|
|
|
|
let isResponseArray = Array.isArray(response);
|
|
|
|
|
resolved_response = isResponseArray ? response.pop() : response;
|
|
|
|
|
|
|
|
|
|
if (resolved_response === 'no-data') {
|
|
|
|
|
populateDaftarPelangganModal(data, handleReadPhoto);
|
|
|
|
|
} else {
|
|
|
|
|
let input_search_noic = document.getElementById("noic");
|
|
|
|
|
let form_search_noic = document.querySelector("form[name='frmsearch']");
|
|
|
|
|
|
|
|
|
|
let getMyKadNumberWithoutHypens = myKadNumber => myKadNumber.replace(/-/g, '');
|
|
|
|
|
input_search_noic.value = getMyKadNumberWithoutHypens(data.IDNum);
|
|
|
|
|
form_search_noic.submit();
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
Swal.fire({
|
|
|
|
|
icon: 'error',
|
|
|
|
|
title: error,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
hideLoader();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function checkIfMyKadExists(myKadNumber) {
|
|
|
|
|
let myKadNumberWithoutHyphens = myKadNumber.replace(/-/g, '');
|
|
|
|
|
|
|
|
|
|
const url = wakil_caricustomer_endpoint + '/?icno=' + myKadNumberWithoutHyphens;
|
|
|
|
|
|
|
|
|
|
const connectionConfig = {
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
"Accept": "application/json",
|
|
|
|
|
"X-Requested-With": "XMLHttpRequest",
|
|
|
|
|
"X-CSRF-Token": document.querySelector('input[name="_token"]').value
|
|
|
|
|
},
|
|
|
|
|
credentials: "same-origin",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return fetch(url, connectionConfig)
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let populateDaftarPelangganModal = (data, callbackAfterPopulateDaftarPelanggan) => {
|
|
|
|
|
$('#daftar_pelanggan_mykad_reader').modal('show');
|
|
|
|
|
let modal_form = document.querySelector("#daftar_pelanggan_mykad_reader .modal-body .row.form-group");
|
|
|
|
|
|
|
|
|
|
const religions = [
|
|
|
|
|
['01', 'Islam'],
|
|
|
|
|
['02', 'Hindu'],
|
|
|
|
|
['03', 'Buddha'],
|
|
|
|
|
['04', 'Kristian'],
|
|
|
|
|
['05', 'Lain-lain'],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const races = [
|
|
|
|
|
['M', 'Melayu'],
|
|
|
|
|
['C', 'Cina'],
|
|
|
|
|
['I', 'India'],
|
|
|
|
|
['L', 'Lain-lain'],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
let getCodeFromValue = (needle, haystack) => {
|
|
|
|
|
let title_cased_text = needle.split(' ')
|
|
|
|
|
.map(w => w[0].toUpperCase() + w.substring(1).toLowerCase())
|
|
|
|
|
.join(' ');
|
|
|
|
|
|
|
|
|
|
return haystack.filter(x => x[1] == title_cased_text)[0][0];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let getMyKadNumberWithoutHypens = (IDNum) => {
|
|
|
|
|
return IDNum.replace(/-/g, '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let getFormattedDateOfBirth = (date_of_birth) => {
|
|
|
|
|
|
|
|
|
|
let date_of_birth_hyphenated = date_of_birth.replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3");
|
|
|
|
|
|
|
|
|
|
const [year, month, day] = date_of_birth_hyphenated.split('-');
|
|
|
|
|
return day + '/' + month + '/' + year;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let getAgeFromDateOfBirth = (date_of_birth) => {
|
|
|
|
|
|
|
|
|
|
let date_of_birth_hyphenated = date_of_birth.replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3");
|
|
|
|
|
|
|
|
|
|
const [birth_year, birth_month, birth_day] = date_of_birth_hyphenated.split('-');
|
|
|
|
|
|
|
|
|
|
let formatBirthMonth = (birth_month) => {
|
|
|
|
|
let newbirthMonth = birth_month < 9 ? birth_month[1] : birth_month;
|
|
|
|
|
return newbirthMonth;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const count_leap_year = countLeapYears(birth_year, formatBirthMonth(birth_month), birth_day) - 1;
|
|
|
|
|
|
|
|
|
|
const start = new Date(date_of_birth_hyphenated),
|
|
|
|
|
end = new Date(),
|
|
|
|
|
diff = new Date(end - start),
|
|
|
|
|
days = ((diff / 1000 / 60 / 60 / 24) - count_leap_year) / 365;
|
|
|
|
|
|
|
|
|
|
return parseInt(days);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let getBumiputeraStatus = (race) => {
|
|
|
|
|
|
|
|
|
|
const non_bumiputera_ethnicity = ['INDIA', 'CINA'];
|
|
|
|
|
|
|
|
|
|
let isBumiputera = !non_bumiputera_ethnicity.includes(race);
|
|
|
|
|
|
|
|
|
|
return isBumiputera;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let disablePointerEventsOnClickableInputs = () => {
|
|
|
|
|
modal_form.querySelector("input[name='bumiputera']").style.pointerEvents = 'none';
|
|
|
|
|
modal_form.querySelector("input[name='warganegara']").style.pointerEvents = 'none';
|
|
|
|
|
|
|
|
|
|
modal_form.querySelector("select[name='bangsa']").style.pointerEvents = 'none';
|
|
|
|
|
modal_form.querySelector("select[name='agama']").style.pointerEvents = 'none';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mykad_modal = document.querySelector("#daftar_pelanggan_mykad_reader");
|
|
|
|
|
let photo_container = mykad_modal.querySelector("#photo-mykad-container");
|
|
|
|
|
|
|
|
|
|
modal_form.querySelector("textarea[name='name']").value = data.OrgName;
|
|
|
|
|
modal_form.querySelector("input[name='noic']").value = getMyKadNumberWithoutHypens(data.IDNum);
|
|
|
|
|
modal_form.querySelector("input[name='tarikh_lahir']").value = getFormattedDateOfBirth(data.BirthDate);
|
|
|
|
|
modal_form.querySelector("input[name='negeri_lahir']").value = data.BirthPlace;
|
|
|
|
|
modal_form.querySelector("input[name='jantina']").value = data.Gender == 'LELAKI' ? 'Lelaki' : 'Perempuan';
|
|
|
|
|
modal_form.querySelector("input[name='warganegara']").checked = data.Citizenship === 'WARGANEGARA' ? true : false;
|
|
|
|
|
modal_form.querySelector("input[name='bumiputera']").checked = getBumiputeraStatus(data.Race);
|
|
|
|
|
modal_form.querySelector("select[name='agama']").value = getCodeFromValue(data.Religion, religions);
|
|
|
|
|
modal_form.querySelector("textarea[name='alamat']").value = [data.Address1, data.Address2, data.Address3].join(", ");
|
|
|
|
|
|
|
|
|
|
let getPostCode = (postCodeCity) => postCodeCity.replace(/\D/g, '');
|
|
|
|
|
modal_form.querySelector("input[name='poskod']").value = getPostCode(data.PostcodeCity);
|
|
|
|
|
|
|
|
|
|
let getCityName = (postCodeCity) => postCodeCity.replace(/\d/g, '');
|
|
|
|
|
modal_form.querySelector("input[name='daerah']").value = getCityName(data.PostcodeCity).trim();
|
|
|
|
|
|
|
|
|
|
modal_form.querySelector("input[name='negeri']").value = data.State;
|
|
|
|
|
modal_form.querySelector("input[name='age']").value = getAgeFromDateOfBirth(data.BirthDate);
|
|
|
|
|
modal_form.querySelector("select[name='bangsa']").value = getCodeFromValue(data.Race, races);
|
|
|
|
|
|
|
|
|
|
calculatePassportAge();
|
|
|
|
|
|
|
|
|
|
photo_container.classList.remove('d-none');
|
|
|
|
|
photo_container.classList.add('d-flex');
|
|
|
|
|
|
|
|
|
|
$('#daftar_pelanggan_mykad_reader').modal('show');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
disablePointerEventsOnClickableInputs();
|
|
|
|
|
callbackAfterPopulateDaftarPelanggan();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function handleReadPhoto()
|
|
|
|
|
{
|
|
|
|
|
WebSocketManager.init("wss://localhost:5963/mykadwebsocket");
|
|
|
|
|
WebSocketManager.listReader(callbackDoneGetListCardReader2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function callbackDoneGetListCardReader2(data, respcode)
|
|
|
|
|
{
|
|
|
|
|
if(respcode === -1){
|
|
|
|
|
console.log({'location': 'callbackDoneGetListCardReader', 'data' : data} );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let slots = data.toString().split(',');
|
|
|
|
|
WebSocketManager.open(doneOpenReaderConnection2, slots[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function doneOpenReaderConnection2()
|
|
|
|
|
{
|
|
|
|
|
WebSocketManager.readPhoto(callbackAfterReadPhoto2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function callbackAfterReadPhoto2(data, RespCode, RespSW12AndApiName)
|
|
|
|
|
{
|
|
|
|
|
let mykad_modal = document.querySelector("#daftar_pelanggan_mykad_reader");
|
|
|
|
|
let photo = mykad_modal.querySelector("#photo-mykad");
|
|
|
|
|
|
|
|
|
|
let isDataEmpty = data
|
|
|
|
|
&& Object.keys(data).length === 0
|
|
|
|
|
&& Object.getPrototypeOf(data) === Object.prototype;
|
|
|
|
|
|
|
|
|
|
if(isDataEmpty){
|
|
|
|
|
photo.src = original_photo_loader;
|
|
|
|
|
let showLoader = () => {
|
|
|
|
|
let pageLoader = document.getElementById('page_loader');
|
|
|
|
|
if (!pageLoader) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
pageLoader.removeAttribute('class');
|
|
|
|
|
pageLoader.classList.add('loading');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
photo.src = 'data:image/jpeg;base64, ' + data.Base64Photo;
|
|
|
|
|
let hideLoader = () => {
|
|
|
|
|
let pageLoader = document.getElementById('page_loader');
|
|
|
|
|
if (!pageLoader) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
pageLoader.removeAttribute('class');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function getMyKadReaderEndpoint() {
|
|
|
|
|
let endpoint = (typeof mykad_reader_endpoint !== 'undefined' && mykad_reader_endpoint)
|
|
|
|
|
? String(mykad_reader_endpoint).trim()
|
|
|
|
|
: '';
|
|
|
|
|
|
|
|
|
|
if (!endpoint || endpoint.includes('MYKADJSON') || endpoint.includes(':8085')) {
|
|
|
|
|
return MYKAD_READER_DEFAULT_ENDPOINT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return endpoint;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function parseJsonResponse(response) {
|
|
|
|
|
let text = await response.text();
|
|
|
|
|
let trimmed = (text || '').trim();
|
|
|
|
|
|
|
|
|
|
if (!trimmed) {
|
|
|
|
|
throw new Error('Pembaca MyKad tidak memulangkan data. Sila cuba lagi.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (trimmed.charAt(0) === '<') {
|
|
|
|
|
throw new Error('Pembaca MyKad memulangkan halaman web, bukan data kad. Sila pastikan perkhidmatan mykad-helper sedang berjalan di localhost:17321.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
return JSON.parse(trimmed);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw new Error('Pembaca MyKad memulangkan data yang tidak sah. Sila cuba lagi.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getMyKadNumberWithoutHyphens(nric) {
|
|
|
|
|
return String(nric || '').replace(/-/g, '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function titleCase(value) {
|
|
|
|
|
return String(value || '')
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
.split(' ')
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
|
|
|
.join(' ');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getCodeFromValue(needle, haystack, fallback) {
|
|
|
|
|
let normalised = titleCase(needle);
|
|
|
|
|
let match = haystack.find(item => item[1].toLowerCase() === normalised.toLowerCase());
|
|
|
|
|
|
|
|
|
|
return match ? match[0] : fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mapGender(gender) {
|
|
|
|
|
let value = String(gender || '').toLowerCase();
|
|
|
|
|
|
|
|
|
|
if (['male', 'lelaki', 'l', 'm'].includes(value)) {
|
|
|
|
|
return 'Lelaki';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (['female', 'perempuan', 'p', 'f'].includes(value)) {
|
|
|
|
|
return 'Perempuan';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return gender || '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatDateOfBirth(dob) {
|
|
|
|
|
if (!dob) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let hyphenated = String(dob).includes('-')
|
|
|
|
|
? String(dob)
|
|
|
|
|
: String(dob).replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3');
|
|
|
|
|
|
|
|
|
|
let [year, month, day] = hyphenated.split('-');
|
|
|
|
|
|
|
|
|
|
if (!year || !month || !day) {
|
|
|
|
|
return dob;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return day + '/' + month + '/' + year;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getAgeFromDateOfBirth(dob) {
|
|
|
|
|
if (!dob) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let hyphenated = String(dob).includes('-')
|
|
|
|
|
? String(dob)
|
|
|
|
|
: String(dob).replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3');
|
|
|
|
|
|
|
|
|
|
let birthDate = new Date(hyphenated);
|
|
|
|
|
if (Number.isNaN(birthDate.getTime())) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let today = new Date();
|
|
|
|
|
let age = today.getFullYear() - birthDate.getFullYear();
|
|
|
|
|
let monthDiff = today.getMonth() - birthDate.getMonth();
|
|
|
|
|
|
|
|
|
|
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
|
|
|
|
|
age -= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return age;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getBumiputeraStatus(race) {
|
|
|
|
|
const non_bumiputera_ethnicity = ['INDIA', 'CINA', 'INDIAN', 'CHINESE'];
|
|
|
|
|
return !non_bumiputera_ethnicity.includes(String(race || '').toUpperCase());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function joinAddress(data) {
|
|
|
|
|
return [data.address1, data.address2, data.address3]
|
|
|
|
|
.map(line => String(line || '').trim())
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
.join(', ');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mapReaderErrorCode(rawMessage) {
|
|
|
|
|
const error_handler_dictionary = {
|
|
|
|
|
'0x80100069': 'Tiada MyKad dikesan, sila masukkan MyKad untuk dibaca',
|
|
|
|
|
'0x8010000C': 'Tiada MyKad dikesan, sila masukkan MyKad untuk dibaca',
|
|
|
|
|
'0x80100066': 'MyKad mungkin dimasukkan terbalik, sila masukkan dengan betul',
|
|
|
|
|
'0x80100067': 'MyKad tidak dapat dikesan. Sila cabut dan masukkan semula kad',
|
|
|
|
|
'0x80100016': 'Gagal membaca MyKad. Sila pastikan kad dimasukkan dengan betul dan cuba lagi',
|
|
|
|
|
'0x8010002E': 'Pembaca MyKad tidak dapat dikesan, sila periksa sambungan',
|
|
|
|
|
'0x80100017': 'Pembaca MyKad tidak dapat dikesan, sila periksa sambungan',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let message = String(rawMessage || '');
|
|
|
|
|
let codes = message.match(/0x[0-9a-fA-F]+/g) || [];
|
|
|
|
|
let matched = codes.reverse().find(code => error_handler_dictionary[code]);
|
|
|
|
|
|
|
|
|
|
return matched ? error_handler_dictionary[matched] : (message || null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getReaderErrorMessage(payload) {
|
|
|
|
|
if (!payload) {
|
|
|
|
|
return 'Pembaca MyKad tidak dapat membaca kad. Sila cuba lagi.';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let raw = payload.message || payload.error || payload.detail || '';
|
|
|
|
|
return mapReaderErrorCode(raw)
|
|
|
|
|
|| 'Pembaca MyKad tidak dapat membaca kad. Sila pastikan MyKad dimasukkan dengan betul.';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getFetchErrorTitle(error) {
|
|
|
|
|
if (error && error.name === 'AbortError') {
|
|
|
|
|
return 'Masa menunggu pembaca MyKad telah tamat. Sila cuba lagi.';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 'Pembaca MyKad tidak dapat dikesan. Sila pastikan perkhidmatan pembaca MyKad sedang berjalan.';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getDisplayError(error) {
|
|
|
|
|
if (!error) {
|
|
|
|
|
return getFetchErrorTitle(error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (error.name === 'AbortError') {
|
|
|
|
|
return getFetchErrorTitle(error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let message = String(error.message || '');
|
|
|
|
|
if (!message || message.startsWith('Failed to fetch') || message.includes('NetworkError')) {
|
|
|
|
|
return getFetchErrorTitle(error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function handleMyKadReader() {
|
|
|
|
|
showLoader();
|
|
|
|
|
|
|
|
|
|
const controller = new AbortController();
|
|
|
|
|
const timeoutId = setTimeout(() => controller.abort(), MYKAD_READER_TIMEOUT_MS);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(getMyKadReaderEndpoint(), {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'Accept': 'application/json',
|
|
|
|
|
},
|
|
|
|
|
signal: controller.signal,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const payload = await parseJsonResponse(response);
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
throw new Error(getReaderErrorMessage(payload));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!payload || payload.ok !== true || !payload.data) {
|
|
|
|
|
throw new Error(getReaderErrorMessage(payload));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await handleMyKadData(payload.data);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
Swal.fire({
|
|
|
|
|
icon: 'error',
|
|
|
|
|
title: getDisplayError(error),
|
|
|
|
|
});
|
|
|
|
|
} finally {
|
|
|
|
|
clearTimeout(timeoutId);
|
|
|
|
|
hideLoader();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleMyKadData(data) {
|
|
|
|
|
return checkIfMyKadExists(data.nric)
|
|
|
|
|
.then(response => {
|
|
|
|
|
let isResponseArray = Array.isArray(response);
|
|
|
|
|
let resolved_response = isResponseArray ? response.pop() : response;
|
|
|
|
|
|
|
|
|
|
if (!resolved_response || resolved_response === 'no-data') {
|
|
|
|
|
populateDaftarPelangganModal(data);
|
|
|
|
|
} else {
|
|
|
|
|
let input_search_noic = document.querySelector("form[name='frmsearch'] input[name='noic']");
|
|
|
|
|
let form_search_noic = document.querySelector("form[name='frmsearch']");
|
|
|
|
|
|
|
|
|
|
input_search_noic.value = getMyKadNumberWithoutHyphens(data.nric);
|
|
|
|
|
form_search_noic.submit();
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(() => {
|
|
|
|
|
populateDaftarPelangganModal(data);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function checkIfMyKadExists(myKadNumber) {
|
|
|
|
|
let myKadNumberWithoutHyphens = getMyKadNumberWithoutHyphens(myKadNumber);
|
|
|
|
|
let lookupUrl = new URL(wakil_caricustomer_endpoint, window.location.origin);
|
|
|
|
|
lookupUrl.searchParams.set('icno', myKadNumberWithoutHyphens);
|
|
|
|
|
|
|
|
|
|
const csrfToken = document.querySelector('input[name="_token"]');
|
|
|
|
|
|
|
|
|
|
return fetch(lookupUrl.toString(), {
|
|
|
|
|
method: 'GET',
|
|
|
|
|
headers: {
|
|
|
|
|
"Accept": "application/json",
|
|
|
|
|
"X-Requested-With": "XMLHttpRequest",
|
|
|
|
|
"X-CSRF-Token": csrfToken ? csrfToken.value : ""
|
|
|
|
|
},
|
|
|
|
|
credentials: "same-origin",
|
|
|
|
|
}).then(async response => {
|
|
|
|
|
let text = await response.text();
|
|
|
|
|
let trimmed = (text || '').trim();
|
|
|
|
|
|
|
|
|
|
if (!trimmed || trimmed.charAt(0) === '<') {
|
|
|
|
|
return 'no-data';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
return JSON.parse(trimmed);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return 'no-data';
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let populateDaftarPelangganModal = (data) => {
|
|
|
|
|
$('#daftar_pelanggan_mykad_reader').modal('show');
|
|
|
|
|
let modal_form = document.querySelector("#daftar_pelanggan_mykad_reader .modal-body .row.form-group");
|
|
|
|
|
|
|
|
|
|
const religions = [
|
|
|
|
|
['01', 'Islam'],
|
|
|
|
|
['02', 'Hindu'],
|
|
|
|
|
['03', 'Buddha'],
|
|
|
|
|
['04', 'Kristian'],
|
|
|
|
|
['05', 'Lain-lain'],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const races = [
|
|
|
|
|
['M', 'Melayu'],
|
|
|
|
|
['C', 'Cina'],
|
|
|
|
|
['I', 'India'],
|
|
|
|
|
['L', 'Lain-lain'],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
let disablePointerEventsOnClickableInputs = () => {
|
|
|
|
|
modal_form.querySelector("input[name='bumiputera']").style.pointerEvents = 'none';
|
|
|
|
|
modal_form.querySelector("input[name='warganegara']").style.pointerEvents = 'none';
|
|
|
|
|
modal_form.querySelector("select[name='bangsa']").style.pointerEvents = 'none';
|
|
|
|
|
modal_form.querySelector("select[name='agama']").style.pointerEvents = 'none';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mykad_modal = document.querySelector("#daftar_pelanggan_mykad_reader");
|
|
|
|
|
let photo_container = mykad_modal.querySelector("#photo-mykad-container");
|
|
|
|
|
let photo = mykad_modal.querySelector("#photo-mykad");
|
|
|
|
|
|
|
|
|
|
modal_form.querySelector("textarea[name='name']").value = data.name || data.gmpc_name || '';
|
|
|
|
|
modal_form.querySelector("input[name='noic']").value = getMyKadNumberWithoutHyphens(data.nric);
|
|
|
|
|
modal_form.querySelector("input[name='tarikh_lahir']").value = formatDateOfBirth(data.dob);
|
|
|
|
|
modal_form.querySelector("input[name='negeri_lahir']").value = data.birth_place || '';
|
|
|
|
|
modal_form.querySelector("input[name='jantina']").value = mapGender(data.gender);
|
|
|
|
|
modal_form.querySelector("input[name='warganegara']").checked = String(data.citizenship || '').toUpperCase() === 'WARGANEGARA';
|
|
|
|
|
modal_form.querySelector("input[name='bumiputera']").checked = getBumiputeraStatus(data.race);
|
|
|
|
|
modal_form.querySelector("select[name='agama']").value = getCodeFromValue(data.religion, religions, '05');
|
|
|
|
|
modal_form.querySelector("textarea[name='alamat']").value = joinAddress(data);
|
|
|
|
|
modal_form.querySelector("input[name='poskod']").value = data.postcode || '';
|
|
|
|
|
modal_form.querySelector("input[name='daerah']").value = data.city || '';
|
|
|
|
|
modal_form.querySelector("input[name='negeri']").value = data.state || '';
|
|
|
|
|
modal_form.querySelector("input[name='age']").value = data.age != null && data.age !== ''
|
|
|
|
|
? data.age
|
|
|
|
|
: getAgeFromDateOfBirth(data.dob);
|
|
|
|
|
modal_form.querySelector("select[name='bangsa']").value = getCodeFromValue(data.race, races, 'L');
|
|
|
|
|
|
|
|
|
|
if (data.has_photo && data.photo_base64) {
|
|
|
|
|
let mime = data.photo_mime || 'image/jpeg';
|
|
|
|
|
photo.src = 'data:' + mime + ';base64,' + data.photo_base64;
|
|
|
|
|
photo_container.classList.remove('d-none');
|
|
|
|
|
photo_container.classList.add('d-flex');
|
|
|
|
|
} else {
|
|
|
|
|
photo.src = original_photo_loader;
|
|
|
|
|
photo_container.classList.add('d-none');
|
|
|
|
|
photo_container.classList.remove('d-flex');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
disablePointerEventsOnClickableInputs();
|
|
|
|
|
$(modal_form.querySelector("input[name='poskod']")).trigger('change');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let hideMykadPhoto = () => {
|
|
|
|
|
|
|
|
|
|
WebSocketManager.close(() => console.log('Websocket closed'));
|
|
|
|
|
|
|
|
|
|
let photo = document.getElementById("photo-mykad");
|
|
|
|
|
let photo_container = document.getElementById("photo-mykad-container");
|
|
|
|
|
|
|
|
|
|
photo.src = original_photo_loader;
|
|
|
|
|
if (photo) {
|
|
|
|
|
photo.src = original_photo_loader;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
photo_container.classList.add('d-none');
|
|
|
|
|
photo_container.classList.remove('d-flex');
|
|
|
|
|
if (photo_container) {
|
|
|
|
|
photo_container.classList.add('d-none');
|
|
|
|
|
photo_container.classList.remove('d-flex');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
button_read_mykad.addEventListener("click", handleMyKadReader);
|
|
|
|
|
button_close_modal.addEventListener("click", hideMykadPhoto);
|
|
|
|
|
if (button_read_mykad) {
|
|
|
|
|
button_read_mykad.addEventListener("click", handleMyKadReader);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (button_close_modal) {
|
|
|
|
|
button_close_modal.addEventListener("click", hideMykadPhoto);
|
|
|
|
|
}
|
|
|
|
|