Skip to content

Commit 1b51f7d

Browse files
committed
feat(portal): modal konfirmasi hasil absen mandiri
Sebelumnya hasil absen hanya muncul sbg alert box di dasar halaman — di frame mobile ketutup tab bar & jauh dari tombol, jadi user tak sadar berhasil. - modal terpusat: ikon status besar (hijau/kuning/merah), pesan, jam absen - di luar radius → kuning + catatan tunggu persetujuan; gagal → merah - CTA Kembali ke Beranda + Lihat Riwayat; alert box tetap sbg fallback - stop stream kamera setelah sukses (lampu mati, hemat baterai) Verifikasi: store 200 (sukses+jam), 422 (tanpa selfie); modal render benar.
1 parent e7748e4 commit 1b51f7d

1 file changed

Lines changed: 56 additions & 10 deletions

File tree

resources/views/portal/attendance_checkin.blade.php

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
.geo-status .ic { width:38px; height:38px; border-radius:10px; display:grid; place-items:center; background:#94a3b8; color:#fff; flex:none; font-size:20px; }
2828
.geo-status.in .ic { background:#22c55e; }
2929
.geo-status.out .ic { background:#ef4444; }
30+
.rm-icon { width:72px; height:72px; border-radius:50%; display:grid; place-items:center; margin:0 auto; font-size:40px; color:#fff; background:#22c55e; }
31+
.rm-icon.is-warn { background:#f59e0b; }
32+
.rm-icon.is-error { background:#ef4444; }
3033
</style>
3134

3235
<div class="row justify-content-center g-3">
@@ -73,6 +76,25 @@
7376

7477
<div id="result" class="alert d-none mt-3" role="alert"></div>
7578

79+
{{-- Modal konfirmasi hasil absen — feedback yang jelas & di tengah layar,
80+
supaya karyawan langsung tahu absennya berhasil atau perlu tindakan. --}}
81+
<div class="modal fade" id="resultModal" tabindex="-1" data-bs-backdrop="static" aria-hidden="true">
82+
<div class="modal-dialog modal-dialog-centered">
83+
<div class="modal-content text-center">
84+
<div class="modal-body p-4">
85+
<div id="rm-icon" class="rm-icon mb-3"><i class="la la-check"></i></div>
86+
<h4 id="rm-title" class="mb-1">Absen tercatat</h4>
87+
<div id="rm-time" class="text-muted mb-2"></div>
88+
<div id="rm-note" class="alert alert-warning small py-2 mb-3 d-none"></div>
89+
<a href="{{ route('portal.dashboard') }}" class="btn btn-primary w-100 btn-lg">
90+
<i class="la la-home"></i> Kembali ke Beranda
91+
</a>
92+
<a href="{{ route('portal.attendance') }}" class="btn btn-link w-100 mt-1">Lihat Riwayat Kehadiran</a>
93+
</div>
94+
</div>
95+
</div>
96+
</div>
97+
7698
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
7799
<script>
78100
(function () {
@@ -193,25 +215,49 @@ function maybeEnable() {
193215
const data = await res.json().catch(() => ({}));
194216
195217
if (!res.ok) {
196-
resultBox.className = 'alert alert-danger mt-3';
197-
resultBox.textContent = data.message || 'Gagal menyimpan absensi.';
198-
resultBox.classList.remove('d-none');
218+
showResult('error', 'Gagal menyimpan', data.message || 'Gagal menyimpan absensi.', null);
199219
btn.disabled = false;
200220
return;
201221
}
202222
203-
resultBox.className = 'alert ' + (data.outside ? 'alert-warning' : 'alert-success') + ' mt-3';
204-
resultBox.innerHTML = '<b>' + data.message + '</b>' +
205-
(data.outside ? '<br>Di luar radius — menunggu persetujuan manajer.' : '');
206-
resultBox.classList.remove('d-none');
223+
// Stop kamera setelah sukses supaya lampu kamera mati & hemat baterai.
224+
try { (video.srcObject?.getTracks() || []).forEach(t => t.stop()); } catch (e) {}
225+
226+
const timeStr = data.time ? new Date(data.time.replace(' ', 'T')).toLocaleTimeString('id-ID', { hour:'2-digit', minute:'2-digit' }) : '';
227+
showResult(
228+
data.outside ? 'warn' : 'success',
229+
data.message || 'Absen tercatat.',
230+
timeStr ? ('Pukul ' + timeStr + ' WIB') : '',
231+
data.outside ? 'Lokasi di luar radius kantor — menunggu persetujuan manajer.' : null
232+
);
207233
hint.textContent = 'Selesai.';
208234
} catch (e) {
209-
resultBox.className = 'alert alert-danger mt-3';
210-
resultBox.textContent = 'Gagal terhubung — periksa koneksi.';
211-
resultBox.classList.remove('d-none');
235+
showResult('error', 'Gagal terhubung', 'Periksa koneksi lalu coba lagi.', null);
212236
btn.disabled = false;
213237
}
214238
});
239+
240+
// Tampilkan modal hasil di tengah layar + fallback ke alert box.
241+
const resultModalEl = document.getElementById('resultModal');
242+
const resultModal = resultModalEl ? new bootstrap.Modal(resultModalEl) : null;
243+
244+
function showResult(kind, title, time, note) {
245+
// Alert box (fallback bila modal gagal).
246+
resultBox.className = 'alert mt-3 ' + (kind === 'success' ? 'alert-success' : kind === 'warn' ? 'alert-warning' : 'alert-danger');
247+
resultBox.innerHTML = '<b>' + title + '</b>' + (note ? '<br>' + note : '');
248+
resultBox.classList.remove('d-none');
249+
250+
if (!resultModal) return;
251+
const icon = document.getElementById('rm-icon');
252+
icon.className = 'rm-icon mb-3' + (kind === 'warn' ? ' is-warn' : kind === 'error' ? ' is-error' : '');
253+
icon.querySelector('i').className = 'la ' + (kind === 'error' ? 'la-times' : kind === 'warn' ? 'la-exclamation' : 'la-check');
254+
document.getElementById('rm-title').textContent = title;
255+
document.getElementById('rm-time').textContent = time || '';
256+
const noteEl = document.getElementById('rm-note');
257+
if (note) { noteEl.textContent = note; noteEl.classList.remove('d-none'); }
258+
else { noteEl.classList.add('d-none'); }
259+
resultModal.show();
260+
}
215261
})();
216262
</script>
217263
@endsection

0 commit comments

Comments
 (0)