SIPDAM/samooapk/resources/views/Admin/KelolaTeknisi/Absensi.blade.php

407 lines
23 KiB
PHP

<x-app-layout>
@push('styles')
<link rel="stylesheet" href="{{ asset('css/akunteknisi.css') }}">
<link rel="stylesheet" href="{{ asset('css/tugas.css') }}">
<style>
.akt-inner { max-width: 1400px; }
.time-select {
border-radius: 8px; border: 1px solid #e2e8f0; padding: 8px;
font-family: var(--akt-font); font-size: 13px; outline: none;
cursor: pointer; background: #fff;
}
.time-select:focus { border-color: var(--akt-green); }
.badge-kategori {
padding: 4px 10px; border-radius: 8px; font-size: 10px; font-weight: 800;
display: inline-flex; align-items: center; gap: 5px;
}
.badge-urgent { background: #fffbeb; color: #b45309; border: 1px solid #fde68a; }
.badge-normal { background: #f0fdf4; color: #15803d; border: 1px solid #bbf7d0; }
/* Override tugas.css for consistency with Data Teknisi */
.pug-stats { grid-template-columns: repeat(3, 1fr); gap: 20px; margin-bottom: 30px; }
.pug-stat { padding: 24px; border-radius: 20px; }
</style>
@endpush
<div class="akt-wrap">
<div class="akt-inner">
{{-- ── PAGE HEADER ── --}}
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:30px;">
<div style="display:flex; align-items:center; gap:15px;">
<div class="pug-icon-wrap" style="width: 54px; height: 54px; font-size: 22px;">
<i class="fas fa-user-check"></i>
</div>
<div>
<h1 class="pug-page-title" style="font-size: 24px;">Monitoring Absensi</h1>
<p class="pug-page-sub">Kelola & monitor kehadiran teknisi lapangan</p>
</div>
</div>
</div>
{{-- ── STATISTICS CARDS ── --}}
<div class="pug-stats">
<div class="pug-stat pug-stat-total" onclick="setStatus('')" style="cursor: pointer;" title="Semua Data">
<div class="pug-stat-icon"><i class="fas fa-users"></i></div>
<div class="pug-stat-label">TOTAL ABSENSI</div>
<div class="pug-stat-val">{{ $counts['total'] ?? 0 }}</div>
<div class="pug-page-sub" style="margin-top: 5px;">Semua teknisi terdaftar</div>
</div>
<div class="pug-stat pug-stat-selesai" onclick="setStatus('hadir')" style="cursor: pointer;" title="Filter: Hadir">
<div class="pug-stat-icon" style="background: var(--pug-green-l); color: var(--pug-green);"><i class="fas fa-check-circle"></i></div>
<div class="pug-stat-label">HADIR</div>
<div class="pug-stat-val">{{ $counts['hadir'] ?? 0 }}</div>
<div class="pug-page-sub" style="margin-top: 5px;">Teknisi yang sudah masuk</div>
</div>
<div class="pug-stat pug-stat-proses" onclick="setStatus('izin')" style="cursor: pointer;" title="Filter: Izin/Sakit">
<div class="pug-stat-icon" style="background: var(--pug-violet-l); color: var(--pug-violet);"><i class="fas fa-info-circle"></i></div>
<div class="pug-stat-label">IZIN / SAKIT</div>
<div class="pug-stat-val">{{ $counts['izin'] ?? 0 }}</div>
<div class="pug-page-sub" style="margin-top: 5px;">Izin berhalangan kerja</div>
</div>
</div>
<!-- Toolbar Filter -->
<form action="{{ route('absensi.index') }}" method="GET" class="pug-toolbar" style="margin-bottom: 24px; padding: 18px 24px;">
<input type="hidden" name="status" id="statusInput" value="{{ request('status') }}">
<div class="pug-toolbar-form">
<div class="pug-toolbar-item" style="flex: 0 0 auto;">
<label class="pug-field-label">Rentang Tanggal</label>
<div class="d-flex align-items-center gap-2">
<input type="date" name="start_date" class="pug-input" value="{{ request('start_date') }}" style="width: 150px; padding: 10px 8px;">
<span class="text-muted small">s/d</span>
<input type="date" name="end_date" class="pug-input" value="{{ request('end_date') }}" style="width: 150px; padding: 10px 8px;">
</div>
</div>
<div class="pug-toolbar-item" style="flex: 1; min-width: 200px;">
<label class="pug-field-label">Cari Teknisi</label>
<div class="pug-input-icon">
<i class="fas fa-user-tie"></i>
<select name="teknisi" class="pug-input" onchange="this.form.submit()">
<option value="">Semua Teknisi</option>
@foreach($teknisis as $t)
<option value="{{ $t->id_teknisi }}" {{ request('teknisi') == $t->id_teknisi ? 'selected' : '' }}>{{ $t->nama }}</option>
@endforeach
</select>
</div>
</div>
<div class="pug-toolbar-item item-btn" style="flex: 0 0 auto;">
<div class="d-flex align-items-center gap-2">
<a href="{{ route('absensi.index') }}" class="pug-reset-btn" style="height: 44px;">
<i class="fas fa-undo"></i> Reset
</a>
</div>
</div>
</div>
</form>
{{-- ── PANEL TABLE ── --}}
<div class="pug-panel">
<div class="pug-panel-head">
<div class="pug-panel-head-left">
<i class="fas fa-table"></i> DAFTAR LIST ABSENSI
</div>
<div class="pug-panel-head-right">
Menampilkan {{ $absensis->total() }} data absensi
</div>
</div>
<div class="table-responsive">
<table class="pug-table">
<thead>
<tr>
<th width="60">#</th>
<th>Teknisi</th>
<th>Tanggal</th>
<th>Jam Masuk</th>
<th>Jam Keluar</th>
<th>Durasi</th>
<th>Status</th>
<th width="100">Aksi</th>
</tr>
</thead>
<tbody>
@forelse($absensis as $index => $abs)
<tr>
<td>
<span class="pug-rownum">{{ str_pad($absensis->firstItem() + $index, 2, '0', STR_PAD_LEFT) }}</span>
</td>
<td>
<div style="display:flex; align-items:center; gap:12px;">
<div class="pug-av">
{{ strtoupper(substr($abs->teknisi->nama ?? 'T', 0, 1)) }}
</div>
<div>
<div class="pug-av-name">{{ $abs->teknisi->nama ?? 'Unknown' }}</div>
<div class="pug-av-sub">{{ $abs->teknisi->email ?? '—' }}</div>
</div>
</div>
</td>
<td>
<div class="pug-date">{{ \Carbon\Carbon::parse($abs->tanggal)->format('d/m/Y') }}</div>
<div style="font-size: 11px; color: var(--pug-t4);">{{ \Carbon\Carbon::parse($abs->tanggal)->isoFormat('dddd') }}</div>
</td>
<td>
@if($abs->jam_masuk)
<div style="font-weight: 700; color: var(--pug-t1);">{{ \Carbon\Carbon::parse($abs->jam_masuk)->format('H:i') }}</div>
@php
$kat = $abs->kategori_kerja;
$badgeClass = $kat == 'Kerja Urgent' ? 'badge-urgent' : 'badge-normal';
$icon = $kat == 'Kerja Urgent' ? 'exclamation-triangle' : 'sun';
@endphp
<div class="badge-kategori {{ $badgeClass }}" style="margin-top: 4px;">
<i class="fas fa-{{ $icon }}"></i> {{ $kat }}
</div>
@else
<span style="color: var(--pug-t4);"></span>
@endif
</td>
<td>
<span style="font-weight: 700;">{{ $abs->jam_keluar ? \Carbon\Carbon::parse($abs->jam_keluar)->format('H:i') : '—' }}</span>
</td>
<td>
<span style="font-weight: 600;">{{ $abs->durasi_kerja_formatted ?? '—' }}</span>
</td>
<td>
@php
$s = strtolower($abs->status);
$badgeClass = 'pug-badge-green';
$icon = 'check-circle';
if($s == 'izin' || $s == 'sakit') { $badgeClass = 'pug-badge-violet'; $icon = 'info-circle'; }
@endphp
<span class="pug-badge {{ $badgeClass }}">
<i class="fas fa-{{ $icon }}"></i> {{ ucfirst($abs->status) }}
</span>
</td>
<td>
<div class="pug-actions">
@if(!empty($abs->latitude) && !empty($abs->longitude) && $abs->latitude !== '0' && $abs->longitude !== '0')
<a href="https://maps.google.com/?q={{ $abs->latitude }},{{ $abs->longitude }}" target="_blank" class="pug-action-btn" style="background: #eff6ff; color: #3b82f6; border-color: #bfdbfe;" title="Cek Lokasi GPS">
<i class="fas fa-map-marker-alt"></i>
</a>
@endif
<button class="pug-action-btn pug-action-view" onclick="showDetail({{ $abs->id_absensi }})" title="Lihat Detail">
<i class="fas fa-eye"></i>
</button>
<button class="pug-action-btn pug-action-edit" onclick="editAbsensi({{ $abs->id_absensi }})" title="Edit Data">
<i class="fas fa-edit"></i>
</button>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="8">
<div class="pug-empty">
<div class="pug-empty-icon"><i class="fas fa-calendar-times"></i></div>
<div class="pug-empty-title">Data Absensi Tidak Ditemukan</div>
<div class="pug-empty-sub">Gunakan filter untuk mencari data di tanggal lain.</div>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($absensis->hasPages())
<div class="pug-pag">
{{ $absensis->appends(request()->query())->links() }}
</div>
@endif
</div>
</div>
</div>
<!-- Modal Detail Absensi -->
<div class="pug-overlay" id="detailModal">
<div class="pug-modal pug-modal-lg">
<div class="pug-modal-header">
<div class="pug-modal-title"><i class="fas fa-info-circle"></i> DETAIL ABSENSI TEKNISI</div>
<button class="pug-close-btn" onclick="closeModal('detailModal')"><i class="fas fa-times"></i></button>
</div>
<div id="detailContent"></div>
</div>
</div>
<!-- Modal Edit Absensi -->
<div class="pug-overlay" id="editModal">
<div class="pug-modal pug-modal-sm">
<div class="pug-modal-header">
<div class="pug-modal-title"><i class="fas fa-edit"></i> EDIT DATA ABSENSI</div>
<button class="pug-close-btn" onclick="closeModal('editModal')"><i class="fas fa-times"></i></button>
</div>
<form id="editForm">
@csrf @method('PUT')
<input type="hidden" id="edit_id">
<div class="pug-form-grid">
<div class="pug-form-full">
<label class="pug-form-label"><i class="fas fa-user"></i> TEKNISI & TANGGAL</label>
<p id="edit_nama" style="font-weight: 700; color: #1e293b; margin-top: 4px;"></p>
<p id="edit_tanggal" style="font-size: 12px; color: #64748b;"></p>
</div>
<div class="pug-form-field">
<label class="pug-form-label">Jam Masuk</label>
<div class="d-flex gap-2">
<select id="h_masuk" class="pug-select">
<option value="">Jam</option>
@for($i=0; $i<24; $i++) <option value="{{ sprintf('%02d', $i) }}">{{ sprintf('%02d', $i) }}</option> @endfor
</select>
<select id="m_masuk" class="pug-select">
<option value="">Menit</option>
@for($i=0; $i<60; $i++) <option value="{{ sprintf('%02d', $i) }}">{{ sprintf('%02d', $i) }}</option> @endfor
</select>
</div>
</div>
<div class="pug-form-field">
<label class="pug-form-label">Jam Keluar</label>
<div class="d-flex gap-2">
<select id="h_keluar" class="pug-select">
<option value="">Jam</option>
@for($i=0; $i<24; $i++) <option value="{{ sprintf('%02d', $i) }}">{{ sprintf('%02d', $i) }}</option> @endfor
</select>
<select id="m_keluar" class="pug-select">
<option value="">Menit</option>
@for($i=0; $i<60; $i++) <option value="{{ sprintf('%02d', $i) }}">{{ sprintf('%02d', $i) }}</option> @endfor
</select>
</div>
</div>
<div class="pug-form-field">
<label class="pug-form-label">Status</label>
<select name="status" id="edit_status" class="pug-select">
<option value="hadir">Hadir</option>
<option value="izin">Izin</option>
<option value="sakit">Sakit</option>
</select>
</div>
<div class="pug-form-field">
<label class="pug-form-label">Keterangan</label>
<textarea name="keterangan" id="edit_keterangan" class="pug-textarea" rows="2"></textarea>
</div>
</div>
<div class="pug-modal-footer">
<button type="button" onclick="closeModal('editModal')" class="pug-btn-cancel">Batal</button>
<button type="button" onclick="submitEdit()" class="pug-btn-submit">Simpan Perubahan</button>
</div>
</form>
</div>
</div>
<script>
const appUrl = "{{ url('/') }}";
function setStatus(val) {
document.getElementById('statusInput').value = val;
document.querySelector('.pug-toolbar').submit();
}
function closeModal(id) { document.getElementById(id).classList.remove('show'); }
function showDetail(id) {
const modal = document.getElementById('detailModal');
const content = document.getElementById('detailContent');
modal.classList.add('show');
content.innerHTML = '<div class="text-center py-5"><div class="spinner-border text-success"></div><p class="mt-2 text-muted">Memuat data...</p></div>';
fetch(`${appUrl}/absensi/${id}`, { headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' } })
.then(r => r.json())
.then(res => {
if(res.success) {
const d = res.data;
content.innerHTML = `
<div class="row">
<div class="col-md-7">
<div class="pug-detail-section">
<div class="pug-detail-section-title">INFORMASI TEKNISI</div>
<div class="pug-detail-grid">
<div class="pug-form-field">
<div class="pug-detail-item-label">Nama</div>
<div class="pug-detail-item-val">${d.teknisi ? d.teknisi.nama : '—'}</div>
</div>
<div class="pug-form-field">
<div class="pug-detail-item-label">Status Absen</div>
<span class="pug-badge ${d.status == 'hadir' ? 'pug-badge-green' : 'pug-badge-violet'}">${d.status_formatted}</span>
</div>
<div class="pug-form-field">
<div class="pug-detail-item-label">Jam Masuk</div>
<div class="pug-detail-item-val">${d.jam_masuk || '-'}</div>
</div>
<div class="pug-form-field">
<div class="pug-detail-item-label">Jam Keluar</div>
<div class="pug-detail-item-val">${d.jam_keluar || '-'}</div>
</div>
</div>
</div>
<div class="pug-detail-section">
<div class="pug-detail-section-title">KATEGORI & CATATAN</div>
<div class="pug-detail-item-val mb-2" style="color:${d.kategori_kerja === 'Kerja Urgent' ? '#f59e0b' : '#15803d'};">
<i class="fas fa-${d.kategori_kerja === 'Kerja Urgent' ? 'exclamation-triangle' : 'sun'}"></i> ${d.kategori_kerja}
</div>
<p class="small text-muted mb-0 italic">${d.keterangan || 'Tidak ada catatan.'}</p>
</div>
</div>
<div class="col-md-5">
<div style="display: flex; gap: 12px;">
<div class="pug-photo-container mb-0" style="flex: 1;">
<div class="pug-photo-label"><i class="fas fa-sign-in-alt"></i> FOTO MASUK</div>
${d.foto_masuk_url ? `<img src="${d.foto_masuk_url}" class="pug-photo-item" onclick="window.open('${d.foto_masuk_url}', '_blank')">` : '<div class="pug-photo-empty">Tidak ada foto</div>'}
</div>
<div class="pug-photo-container" style="flex: 1;">
<div class="pug-photo-label"><i class="fas fa-sign-out-alt"></i> FOTO KELUAR</div>
${d.foto_keluar_url ? `<img src="${d.foto_keluar_url}" class="pug-photo-item" onclick="window.open('${d.foto_keluar_url}', '_blank')">` : '<div class="pug-photo-empty">Tidak ada foto</div>'}
</div>
</div>
<div style="font-size: 11px; color: var(--pug-t3); text-align: center; margin-top: 8px;">Klik foto untuk melihat ukuran penuh</div>
</div>
</div>
`;
}
});
}
function editAbsensi(id) {
const modal = document.getElementById('editModal');
modal.classList.add('show');
fetch(`${appUrl}/absensi/${id}`, { headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' } })
.then(r => r.json())
.then(res => {
if(res.success) {
const d = res.data;
document.getElementById('edit_id').value = d.id;
document.getElementById('edit_nama').textContent = d.teknisi.nama;
document.getElementById('edit_tanggal').textContent = d.tanggal_full;
if(d.jam_masuk && d.jam_masuk !== '-') {
const [h, m] = d.jam_masuk.split(':');
document.getElementById('h_masuk').value = h;
document.getElementById('m_masuk').value = m;
}
if(d.jam_keluar && d.jam_keluar !== '-') {
const [h, m] = d.jam_keluar.split(':');
document.getElementById('h_keluar').value = h;
document.getElementById('m_keluar').value = m;
}
document.getElementById('edit_status').value = d.status;
document.getElementById('edit_keterangan').value = d.keterangan !== '-' ? d.keterangan : '';
}
});
}
function submitEdit() {
const id = document.getElementById('edit_id').value;
const formData = new FormData(document.getElementById('editForm'));
const hM = document.getElementById('h_masuk').value; const mM = document.getElementById('m_masuk').value;
const hK = document.getElementById('h_keluar').value; const mK = document.getElementById('m_keluar').value;
if(hM && mM) formData.append('jam_masuk', `${hM}:${mM}`);
if(hK && mK) formData.append('jam_keluar', `${hK}:${mK}`);
fetch(`${appUrl}/absensi/${id}`, {
method: 'POST',
headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}', 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' },
body: formData
})
.then(r => r.json())
.then(res => { if(res.success) location.reload(); else alert('Gagal simpan!'); });
}
</script>
</x-app-layout>