Merge branch 'afiqhamzah-feat-mykad-reader-registration' into 'new-super'

Improve MyKad scan speed

See merge request online-sistem1/kaunter!216
This commit is contained in:
hafifie rahman
2023-02-28 08:11:04 +00:00
13 changed files with 1025 additions and 10 deletions
+3
View File
@@ -44,3 +44,6 @@ AWS_BUCKET=
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
MYKAD_READER_PORT=8085
MYKAD_READER_ENABLED_CAWANGAN='' # IF NO CAWANGAN STATED, ALL CAWANGAN IS ENABLED
+5 -7
View File
@@ -47,12 +47,8 @@ class CustomersController extends Controller
$auth_user = Auth::user();
$api_url = config('api.url');
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
$customer_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/customers/'. Session::get('noic'))->json();
// if($customers_info){
// $customer_info = $customers_info[0];
// }else{
// $customer_info = $customers_info;
// }
$komoditi = Komoditi::first();
$min_komoditi = $komoditi['min_komoditi'];
@@ -115,7 +111,7 @@ class CustomersController extends Controller
if(sizeof($customers_info) > 0)
return $customers_info;
else
return "no-data";
return response(["no-data"], 200);
}
/**
@@ -204,7 +200,9 @@ class CustomersController extends Controller
'tujuangadai'=>$request->tujuan_gadai,
]);
Session::put('customer_info',$customer_info);
Session::put('customer_info', $customer_info);
Session::put('noic', $request->noic);
event(new PelangganNotify('lulus',$auth_user['cawangan']));
return redirect()->route('customer_info')->with( ['customer_info' => $customer_info] );
}
+6
View File
@@ -3,6 +3,7 @@
namespace App;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
@@ -37,4 +38,9 @@ class User extends Authenticatable
protected $casts = [
'email_verified_at' => 'datetime',
];
public function cawangan_kerja(): HasOne
{
return $this->hasOne(Cawangan::class, 'kodcaw', 'cawangan');
}
}
+9
View File
@@ -0,0 +1,9 @@
<?php
$default_port = '8085';
$getReaderEndpoint = fn($x) => 'http://localhost:' . $x . '/?MYKADJSON';
return[
'endpoint' => env('MYKAD_READER_ENDPOINT', $getReaderEndpoint(env('MYKAD_READER_PORT', $default_port))),
'enabled_cawangan' => array_map(fn ($cawangan) => trim($cawangan), explode(',', env('MYKAD_READER_ENABLED_CAWANGAN'))) ?? [],
];
+121
View File
@@ -0,0 +1,121 @@
/* Absolute Center Spinner */
.loading {
position: fixed;
z-index: 999;
height: 2em;
width: 2em;
overflow: show;
margin: auto;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
/* Transparent Overlay */
.loading:before {
content: '';
display: block;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(rgba(20, 20, 20,.8), rgba(0, 0, 0, .8));
background: -webkit-radial-gradient(rgba(20, 20, 20,.8), rgba(0, 0, 0,.8));
}
/* :not(:required) hides these rules from IE9 and below */
.loading:not(:required) {
/* hide "loading..." text */
font: 0/0 a;
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}
.loading:not(:required):after {
content: '';
display: block;
font-size: 10px;
width: 1em;
height: 1em;
margin-top: -0.5em;
-webkit-animation: spinner 150ms infinite linear;
-moz-animation: spinner 150ms infinite linear;
-ms-animation: spinner 150ms infinite linear;
-o-animation: spinner 150ms infinite linear;
animation: spinner 150ms infinite linear;
border-radius: 0.5em;
-webkit-box-shadow: rgba(255,255,255, 0.75) 1.5em 0 0 0, rgba(255,255,255, 0.75) 1.1em 1.1em 0 0, rgba(255,255,255, 0.75) 0 1.5em 0 0, rgba(255,255,255, 0.75) -1.1em 1.1em 0 0, rgba(255,255,255, 0.75) -1.5em 0 0 0, rgba(255,255,255, 0.75) -1.1em -1.1em 0 0, rgba(255,255,255, 0.75) 0 -1.5em 0 0, rgba(255,255,255, 0.75) 1.1em -1.1em 0 0;
box-shadow: rgba(255,255,255, 0.75) 1.5em 0 0 0, rgba(255,255,255, 0.75) 1.1em 1.1em 0 0, rgba(255,255,255, 0.75) 0 1.5em 0 0, rgba(255,255,255, 0.75) -1.1em 1.1em 0 0, rgba(255,255,255, 0.75) -1.5em 0 0 0, rgba(255,255,255, 0.75) -1.1em -1.1em 0 0, rgba(255,255,255, 0.75) 0 -1.5em 0 0, rgba(255,255,255, 0.75) 1.1em -1.1em 0 0;
}
/* Animation */
@-webkit-keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-moz-keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-o-keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spinner {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+258
View File
@@ -0,0 +1,258 @@
WebSocketManager = {
url:"",
init: function(url) {
WebSocketManager.url = url;
},
open: function(callback, slot) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"OPEN","Slot":"'+ slot +'"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
wsocket.close();
callback(obj);
};
},
listReader: function(callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"LIST_READER"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode);
};
},
close: function(callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"CLOSE"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
wsocket.close();
callback(obj);
};
},
verifyFPTemplate: function(callback, ThumbCode, Bas64FPLeft, Bas64FPRight) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"VERIFY_FP_TEMPLATE","ThumbCode":"'+ ThumbCode +'","Bas64FPLeft":"'+ Bas64FPLeft +'","Bas64FPRight":"'+ Bas64FPRight +'"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
wsocket.close();
callback(obj.RespCode, obj.RespMessage);
};
},
readMyKad: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"READ_MYKAD"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode, obj.RespSW12AndApiName);
};
},
readAllText: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"READ_ALL_TEXT"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode, obj.RespSW12AndApiName);
};
},
readPhoto: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"READ_PHOTO"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode, obj.RespSW12AndApiName);
};
},
readBio: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"READ_BIO"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode, obj.RespSW12AndApiName);
};
},
readMyKid: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"READ_MYKID"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode, obj.RespSW12AndApiName);
};
},
FPBioEnrol: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"FP_BIO_ENROL"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
//var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(obj);
};
},
getThumbImage: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"GET_MYKAD_THUMB_IMAGE"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode, obj.RespSW12AndApiName);
};
},
getThumbBin1: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"GET_THUMB_BIN1"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode, obj.RespSW12AndApiName);
};
},
getThumbBin2: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"GET_THUMB_BIN2"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode, obj.RespSW12AndApiName);
};
},
FPBioEnrolBin1: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"FP_BIO_ENROL_BIN1"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
//var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(obj);
};
},
FPBioEnrolBin2: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"FP_BIO_ENROL_BIN2"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
//var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(obj);
};
},
FPBioVerify: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"FP_BIO_VERIFY"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode, obj.RespSW12AndApiName);
};
},
FPBioVerifyBin: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"FP_BIO_VERIFY_BIN"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode, obj.RespSW12AndApiName);
};
},
FPBioVerifyLeft: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"FP_BIO_VERIFY_LEFT"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode, obj.RespSW12AndApiName);
};
},
FPBioVerifyBinLeft: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"FP_BIO_VERIFY_BIN_LEFT"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode, obj.RespSW12AndApiName);
};
},
FPBioVerifyRight: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"FP_BIO_VERIFY_RIGHT"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode, obj.RespSW12AndApiName);
};
},
FPBioVerifyBinRight: function (callback) {
wsocket = new WebSocket(WebSocketManager.url);
wsocket.onopen = function () {
wsocket.send('{"Cmd":"FP_BIO_VERIFY_BIN_RIGHT"}');
};
wsocket.onmessage = function (evt) {
var obj = JSON.parse(evt.data);
var data = JSON.parse(obj.RespMessage);
wsocket.close();
callback(data, obj.RespCode, obj.RespSW12AndApiName);
};
}
}
+293
View File
@@ -0,0 +1,293 @@
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 showLoader = () => {
let pageLoader = document.getElementById('page_loader');
pageLoader.removeAttribute('class');
pageLoader.classList.add('loading');
};
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("input[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;
return;
}
photo.src = 'data:image/jpeg;base64, ' + data.Base64Photo;
}
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;
photo_container.classList.add('d-none');
photo_container.classList.remove('d-flex');
};
button_read_mykad.addEventListener("click", handleMyKadReader);
button_close_modal.addEventListener("click", hideMykadPhoto);
+320
View File
@@ -37,6 +37,27 @@
<button class="btn btn-outline-primary" type="submit" id="button-addon2">Cari</button>
</div>
</div>
@php
$user_cawangan = strtolower(auth()->user()->cawangan_kerja->details_caw);
$enabled_cawangan = array_map(fn($x) => strtolower($x), config('mykad-reader.enabled_cawangan'));
$is_enabled_to_use_mykad_reader = function () use($user_cawangan, $enabled_cawangan){
if(sizeof($enabled_cawangan) == 0){
return true;
}
if(sizeof($enabled_cawangan) == 1 && $enabled_cawangan[0] === ''){
return true;
}
return in_array($user_cawangan, $enabled_cawangan);
};
@endphp
@if( $is_enabled_to_use_mykad_reader() )
<span class="btn btn-outline-primary" id="button_read_mykad">Baca MyKad</span>
@endif
</div>
</div>
</form>
@@ -61,6 +82,9 @@
<form action="{{ route('customer.store') }}" onsubmit="verifycustomer()" method="POST">
@csrf
<div class="row form-group">
<div class="col-md-12 d-flex justify-content-center pb-3 d-none" id="photo-mykad-container">
<img src="{{ asset('img/spinner-loader.gif') }}" alt="" id="photo-mykad" width="150" height="200" object-fit="cover">
</div>
<div class="col-md-6">
<div class="row form-group">
<div class="col-md-4">Nama</div>
@@ -177,6 +201,140 @@
<button type="button" class="btn btn-danger" data-dismiss="modal">Batal</button>
<input type="submit" name="submit" class="btn btn-primary" id="terima_ic" value="Terima">
</div>
</form>
</div>
</div>
</div>
<!-- Modal MyKad Reader -->
<div class="modal fade" id="daftar_pelanggan_mykad_reader" tabindex="-1" data-backdrop="static" aria-labelledby="ModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ModalLabel">Daftar Pelanggan</h5>
</div>
<div class="modal-body">
<form action="{{ route('customer.store') }}" onsubmit="verifycustomerMyKadReader()" method="POST">
@csrf
<div class="row form-group">
<div class="col-md-12 d-flex justify-content-center pb-3 d-none" id="photo-mykad-container">
<img src="{{ asset('img/spinner-loader.gif') }}" alt="" id="photo-mykad" width="150" height="200" object-fit="cover">
</div>
<div class="col-md-6">
<div class="row form-group">
<div class="col-md-4">Nama</div>
<div class="col-md-8"><input class="form-control" type="text" name="name" id="name" autocomplete="off" placeholder="Nama" readonly></div>
<div class="col-md-4">No KP</div>
<div class="col-md-8"><input class="form-control" type="text" name="noic" id="noic" value="{{$noic ?? ''}}" readonly></div>
<div class="col-md-4">Tarikh Lahir</div>
<div class="col-md-8"><input class="form-control" type="text" name="tarikh_lahir" id="tarikh_lahir" readonly></div>
<div class="col-md-4">Negeri Lahir</div>
<div class="col-md-8"><input class="form-control" type="text" name="negeri_lahir" id="negeri_lahir" readonly></div>
<div class="col-md-4">Jantina</div>
<div class="col-md-8"><input class="form-control" type="text" name="jantina" id="jantina" readonly></div>
<div class="col-md-4">No Telefon 1</div>
<div class="col-md-8"><input class="form-control" type="text" name="main_phone" id="main_phone" autocomplete="off" placeholder="No Telefon"></div>
<div class="col-md-4">No Telefon 2</div>
<div class="col-md-8"><input class="form-control" type="text" name="alternate_phone" id="alternate_phone" autocomplete="off" placeholder="No Telefon"></div>
<div class="col-md-12 ml-4">
<div class="form-group row">
<div class="form-check form-check-inline">
<input checked class="form-check-input" type="checkbox" id="warganegara" name="warganegara" value="warganegara" readonly>
<label class="form-check-label" for="warganegara">Warganegara</label>
</div>
<div class="form-check form-check-inline">
<input checked class="form-check-input" type="checkbox" id="bumiputera" name="bumiputera" value="bumiputera" readonly>
<label class="form-check-label" for="bumiputera">Bumiputera</label>
</div>
</div>
</div>
<div class="col-md-4">Agama</div>
<div class="col-md-8">
<select class="form-control" name="agama" id="agama" readonly>
<option selected value="01">Islam</option>
<option value="02">Hindu</option>
<option value="03">Buddha</option>
<option value="04">Kristian</option>
<option value="05">Lain-lain</option>
</select>
</div>
<div class="col-md-4">Ahli</div>
<div class="col-md-8">
<select class="form-control" name="kumpulan_sahabat" id="kumpulan_sahabat">
<option value="0">Tidak</option>
<option value="1">Kumpulan PKINK</option>
<option value="2">Sahabat</option>
</select>
</div>
</div>
</div>
<div class="col-md-6">
<div class="row form-group">
<div class="col-md-4">Alamat</div>
<div class="col-md-8"><textarea class="form-control" style="resize:none" name="alamat" id="alamat" cols="30" rows="3" placeholder="Alamat Pelanggan" readonly></textarea></div>
<div class="col-md-4">Poskod</div>
<div class="col-md-8"><input class="form-control" type="text" name="poskod" id="poskod" autocomplete="off" placeholder="Poskod" readonly></div>
<div class="col-md-4">Daerah</div>
<div class="col-md-8"><input class="form-control" type="text" name="daerah" id="daerah" readonly autocomplete="off" readonly></div>
<div class="col-md-4">Negeri</div>
<div class="col-md-8"><input class="form-control" type="text" name="negeri" id="negeri" readonly autocomplete="off" readonly></div>
<div class="col-md-4">Umur</div>
<div class="col-md-8"><input class="form-control" type="text" name="age" id="age" readonly></div>
<div class="col-md-4">Bangsa</div>
<div class="col-md-8">
<select class="form-control" name="bangsa" id="bangsa" readonly>
<option selected value="M">Melayu</option>
<option value="C">Cina</option>
<option value="I">India</option>
<option value="L">Lain-lain</option>
</select>
</div>
<div class="col-md-4">Bank</div>
<div class="col-md-8">
<select class="form-control" name="bank_name" data-placeholder="Pilih Bank" id="bank_name">
<option disabled selected value> -- Pilih Bank -- </option>
@foreach($bank as $banks)
<option value="{{ $banks['abbreviation'] }}">{{ $banks['name'] }}</option>
@endforeach
</select>
</div>
<div class="col-md-4">No Akaun</div>
<div class="col-md-8"><input class="form-control" type="text" name="no_akaun" id="no_akaun" placeholder="No Akaun Bank"></div>
<div class="col-md-4">Tujuan Gadai</div>
@php
$tujuangadai = Http::withOptions(['verify' => config('app.api_verify')])->get($api_url . '/api/tujuangadai')->json();
@endphp
<div class="col-md-8">
<select class="form-control" name="tujuan_gadai" data-placeholder="Pilih Tujuan Gadai" id="tujuan_gadai">
<option disabled selected value> -- Pilih Tujuan Gadai -- </option>
@foreach($tujuangadai as $tujuan)
<option value="{{ $tujuan['keterangan'] }}">{{ $tujuan['keterangan'] }}</option>
@endforeach
</select>
</div>
<div class="col-md-4">Pekerjaan</div>
@php
$pekerjaan = Http::withOptions(['verify' => config('app.api_verify')])->get($api_url . '/api/pekerjaan')->json();
@endphp
<div class="col-md-8">
<select class="form-control" name="pekerjaan" data-placeholder="Pilih Pekerjaan" id="pekerjaan">
<option disabled selected value> -- Pilih Pekerjaan -- </option>
@foreach($pekerjaan as $kerja)
<option value="{{ $kerja['kodkerja'] }}">{{ $kerja['keterangan'] }}</option>
@endforeach
</select>
</div>
<div class="col-md-4">Nota</div>
<div class="col-md-8"><input class="form-control" type="text" name="nota" id="nota" placeholder="Nota"></div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<input type="text" hidden name="idtype" value="nokp">
<button type="button" class="btn btn-danger" data-dismiss="modal">Batal</button>
<input type="submit" name="submit" class="btn btn-primary" id="terima_ic" value="Terima">
</div>
</form>
</div>
</div>
@@ -448,6 +606,16 @@
});
function verifycustomer(){
// Construct URLSearchParams object instance from current URL querystring.
var queryParams = new URLSearchParams(window.location.search);
let updated_icno = document.querySelector('#daftar_pelanggan input[name="noic"]').value
queryParams.set("noic", updated_icno);
history.replaceState(null, null, "?"+queryParams.toString());
$("#terima_ic").prop('disabled', true);
var customername = $('#name').val();
var customeralamat = $('#alamat').val();
@@ -575,6 +743,146 @@
return true;
}
function verifycustomerMyKadReader(){
let myKadModal = $('#daftar_pelanggan_mykad_reader');
// Construct URLSearchParams object instance from current URL querystring.
var queryParams = new URLSearchParams(window.location.search);
let updated_icno = document.querySelector('#daftar_pelanggan_mykad_reader input[name="noic"]').value
queryParams.set("noic", updated_icno);
history.replaceState(null, null, "?"+queryParams.toString());
$("#terima_ic", myKadModal).prop('disabled', true);
var customername = $('#name', myKadModal).val();
var customeralamat = $('#alamat', myKadModal).val();
var customerposkod = $('#poskod', myKadModal).val();
var customerphone = $('#main_phone', myKadModal).val();
var customerphone_alternate = $('#alternate_phone', myKadModal).val();
var age = $('#age', myKadModal).val();
if(age < 18){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Umur Pelanggan Tidak Mencukupi 18 Tahun'
});
event.preventDefault();
return false;
}
if(customername == ''){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Masukkan Nama Pelanggan'
});
event.preventDefault();
return false;
}
if(customeralamat == ''){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Masukkan Alamat Pelanggan'
});
event.preventDefault();
return false;
}
if(customerposkod == ''){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Masukkan Poskod'
});
event.preventDefault();
return false;
}else{
if(customerposkod.length != 5){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Periksa Poskod'
});
event.preventDefault();
return false;
}else{
if($.isNumeric(customerposkod) == false){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Periksa Poskod'
});
event.preventDefault();
return false;
}
}
}
if(customerphone == ''){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Masukkan No Telefon Pelanggan'
});
event.preventDefault();
return false;
}else{
if((customerphone.length < 9 || customerphone.length >= 12)){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Periksa No Telefon Pelanggan'
});
event.preventDefault();
return false;
}else{
if($.isNumeric(customerphone) == false){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Masukkan Nombor Sahaja Pada No Telefon Pelanggan'
});
event.preventDefault();
return false;
}
}
}
if(customerphone_alternate != ''){
if(customerphone_alternate.length < 8 && customerphone_alternate.length > 12){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Periksa No Telefon Pelanggan'
});
event.preventDefault();
return false;
}else{
if($.isNumeric(customerphone_alternate) == false){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Masukkan Nombor Sahaja Pada No Telefon Pelanggan'
});
event.preventDefault();
return false;
}
}
}
return true;
}
function verifycustomerpassport(){
$("#terima_passport").prop('disabled', true);
var customername = $('#passport_name').val();
@@ -892,6 +1200,18 @@
// return false;
// }
}
}
</script>
@push('additional_scripts')
<script>
// mycard-read endpoint initialization
let mykad_reader_endpoint = "{{ config('mykad-reader.endpoint') }}";
let wakil_caricustomer_endpoint = "{{ route('wakil.caricustomer') }}";
</script>
<script src="{{ asset('js/iris-mykad-websocket-client-api.js') }}"></script>
<script src="{{ asset('js/mykad-reader-customer.js') }}"></script>
@endpush
@endsection
+9 -2
View File
@@ -21,6 +21,7 @@
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
<link href="{{ asset('css/main.css') }}" rel="stylesheet">
<link href="{{ asset('css/loader.css') }}" rel="stylesheet">
<link href="{{ asset('vendor/adminlte/dist/css/adminlte.css') }}" rel="stylesheet">
<link href="{{ asset('vendor/fontawesome-free/css/all.css') }}" rel="stylesheet">
<link href="{{ asset('vendor/DataTables/datatables.min.css') }}" rel="stylesheet">
@@ -91,6 +92,7 @@
}
@endphp
<div id="app">
<div id="page_loader" class="not-loading"></div>
<nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm" style="height:100px;">
<div class="container">
<a class="navbar-brand" href="#" style="cursor:not-allowed;">
@@ -293,13 +295,16 @@
</div>
@php
$gadaiNumber = $gadai == null ? '0' : count($gadaiNumber);
$listSerahNumber = $listserah == null ? '0' : count($listserah);
if($auth_user['role_harian'] == 'teller')
{
echo '<div class="col-3">
<div class="col-12 pl-5">
<div class="row">
<h5 class="pr-3">G : <span class="text-success">' . count($gadai) . '</span></h5>
<h5>T : <span class="text-danger">' . count($listserah) . '</span></h5>
<h5 class="pr-3">G : <span class="text-success">' .$gadaiNumber. '</span></h5>
<h5>T : <span class="text-danger">' .$listSerahNumber. '</span></h5>
</div>
</div>
</div>';
@@ -362,4 +367,6 @@
@yield('footer')
</body>
{{-- JS Stack--}}
@stack('additional_scripts')
</html>
+1 -1
View File
@@ -18,7 +18,7 @@ Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
Route::post('/chat-bot', 'CustomerController@listenToReplies');
Route::post('/chat-bot', 'CustomersController@listenToReplies');
Route::group(['prefix' => 'dashboard'], function () {
Route::post('/login', 'Auth\LoginController@loginDashboard');