first
This commit is contained in:
105
resources/views/counter/index.blade.php
Normal file
105
resources/views/counter/index.blade.php
Normal file
@@ -0,0 +1,105 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Kaunter Kehadiran')
|
||||
|
||||
@section('content')
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-9">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body counter-search">
|
||||
<label class="fw-bold mb-2 fs-5"><i class="bi bi-search text-teal"></i> Cari Anggota</label>
|
||||
<input type="text" id="searchBox" class="form-control" autocomplete="off"
|
||||
placeholder="Taip nama / no. KP / no. pekerja..." autofocus>
|
||||
<div class="form-text">Carian automatik. Boleh guna nama, nombor kad pengenalan atau nombor pekerja MBIP.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="searchStatus" class="text-center text-muted py-2"></div>
|
||||
<div id="resultArea"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
const searchUrl = "{{ route('counter.search') }}";
|
||||
const storeUrl = "{{ route('counter.store') }}";
|
||||
let timer = null, lastTerm = '';
|
||||
|
||||
const $box = $('#searchBox'), $status = $('#searchStatus'), $area = $('#resultArea');
|
||||
|
||||
$box.on('input', function(){
|
||||
const term = this.value.trim();
|
||||
clearTimeout(timer);
|
||||
if (term.length < 2) { $area.empty(); $status.text(''); return; }
|
||||
timer = setTimeout(() => doSearch(term), 220);
|
||||
});
|
||||
|
||||
function doSearch(term){
|
||||
lastTerm = term;
|
||||
$status.html('<span class="spinner-border spinner-border-sm text-teal"></span> Mencari...');
|
||||
$.getJSON(searchUrl, { q: term })
|
||||
.done(res => { if (term !== lastTerm) return; render(res); })
|
||||
.fail(() => $status.html('<span class="text-danger">Ralat carian.</span>'));
|
||||
}
|
||||
|
||||
function render(res){
|
||||
$area.empty();
|
||||
if (!res.data || res.data.length === 0){
|
||||
$status.html('');
|
||||
$area.html(`<div class="alert alert-danger d-flex align-items-center gap-2 fs-6">
|
||||
<i class="bi bi-x-octagon-fill fs-3"></i>
|
||||
<div><b>Rekod tidak dijumpai.</b><br>Individu ini bukan anggota koperasi yang berdaftar.</div></div>`);
|
||||
return;
|
||||
}
|
||||
$status.html(`<small>${res.data.length} rekod dijumpai</small>`);
|
||||
res.data.forEach(m => $area.append(cardHtml(m)));
|
||||
}
|
||||
|
||||
function cardHtml(m){
|
||||
const hadirBadge = m.hadir
|
||||
? `<span class="badge badge-hadir"><i class="bi bi-check-circle"></i> Sudah Hadir</span>`
|
||||
: `<span class="badge badge-belum">Belum Hadir</span>`;
|
||||
const timeInfo = m.hadir ? `<div class="small text-success mt-1"><i class="bi bi-clock"></i> Masa hadir: ${m.attended_at ?? '-'}</div>` : '';
|
||||
const aktif = m.status_aktif ? '' : `<span class="badge bg-warning text-dark ms-1">Tidak Aktif</span>`;
|
||||
const btn = m.hadir
|
||||
? `<button class="btn btn-success" disabled><i class="bi bi-check2-all"></i> Telah Hadir</button>`
|
||||
: `<button class="btn btn-teal btn-lg px-4" onclick="recordAttend(${m.id}, this)"><i class="bi bi-person-check"></i> Rekod Hadir</button>`;
|
||||
|
||||
return `<div class="card result-card mb-2 ${m.hadir ? 'hadir':''}" id="m-${m.id}">
|
||||
<div class="card-body d-flex flex-wrap justify-content-between align-items-center gap-3">
|
||||
<div>
|
||||
<div class="fs-5 fw-bold">${escapeHtml(m.nama)} ${aktif}</div>
|
||||
<div class="text-muted small">
|
||||
<i class="bi bi-person-badge"></i> ${m.no_pekerja ?? '-'} ·
|
||||
<i class="bi bi-credit-card-2-front"></i> ${m.masked_kp ?? '-'} ·
|
||||
<i class="bi bi-building"></i> ${escapeHtml(m.jabatan ?? '-')} / ${escapeHtml(m.bahagian ?? '-')}
|
||||
</div>
|
||||
<div class="mt-1">${hadirBadge}</div>
|
||||
${timeInfo}
|
||||
</div>
|
||||
<div class="text-end" id="act-${m.id}">${btn}</div>
|
||||
</div></div>`;
|
||||
}
|
||||
|
||||
function recordAttend(id, btn){
|
||||
$(btn).prop('disabled', true).html('<span class="spinner-border spinner-border-sm"></span> Menyimpan...');
|
||||
$.post(storeUrl, { member_id: id })
|
||||
.done(res => {
|
||||
const m = res.member;
|
||||
$(`#m-${id}`).addClass('hadir');
|
||||
$(`#act-${id}`).html('<button class="btn btn-success" disabled><i class="bi bi-check2-all"></i> Telah Hadir</button>');
|
||||
if (res.status === 'ok'){
|
||||
Swal.fire({ icon:'success', title:'Kehadiran Direkod!', text: res.message, timer: 1600, showConfirmButton:false });
|
||||
} else {
|
||||
Swal.fire({ icon:'info', title:'Sudah Hadir', text: res.message });
|
||||
}
|
||||
})
|
||||
.fail(xhr => {
|
||||
$(btn).prop('disabled', false).html('<i class="bi bi-person-check"></i> Rekod Hadir');
|
||||
Swal.fire({ icon:'error', title:'Ralat', text: (xhr.responseJSON?.message) || 'Gagal merekod kehadiran.' });
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(s){ return (s ?? '').toString().replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); }
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
Reference in New Issue
Block a user