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

456 lines
22 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<x-app-layout>
@push('styles')
<link rel="stylesheet" href="{{ asset('css/teknisi.css') }}">
<link rel="stylesheet" href="{{ asset('css/tugas.css') }}">
<style>
.tek-inner { max-width: 1400px; }
.pug-stats { grid-template-columns: repeat(3, 1fr); gap: 20px; margin-bottom: 30px; }
</style>
@endpush
<div class="tek-wrap">
<div class="tek-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-users-cog"></i>
</div>
<div>
<h1 class="pug-page-title" style="font-size: 24px;">Data Teknisi</h1>
<p class="pug-page-sub">Kelola & monitor profil lengkap teknisi lapangan</p>
</div>
</div>
<button onclick="openModal('create')" class="pug-btn-primary" style="padding: 12px 24px; border-radius: 12px;">
<i class="fas fa-plus"></i> Tambah Teknisi Baru
</button>
</div>
{{-- ── STATISTICS CARDS ── --}}
<div class="pug-stats">
<div class="pug-stat pug-stat-total">
<div class="pug-stat-icon"><i class="fas fa-users"></i></div>
<div class="pug-stat-label">TOTAL TEKNISI</div>
<div class="pug-stat-val" id="totalTeknisi">0</div>
<div class="pug-page-sub" style="margin-top: 5px;">Semua teknisi terdaftar</div>
</div>
<div class="pug-stat pug-stat-selesai">
<div class="pug-stat-icon" style="background: var(--pug-green-l); color: var(--pug-green);"><i class="fas fa-user-check"></i></div>
<div class="pug-stat-label">TEKNISI AKTIF</div>
<div class="pug-stat-val" id="totalAktif">0</div>
<div class="pug-page-sub" style="margin-top: 5px;">Sedang aktif bertugas</div>
</div>
<div class="pug-stat pug-stat-garansi">
<div class="pug-stat-icon" style="background: var(--pug-rose-l); color: var(--pug-rose);"><i class="fas fa-user-times"></i></div>
<div class="pug-stat-label">TIDAK AKTIF</div>
<div class="pug-stat-val" id="totalTidakAktif">0</div>
<div class="pug-page-sub" style="margin-top: 5px;">Tidak sedang bertugas</div>
</div>
</div>
{{-- ── TOOLBAR (SEARCH) ── --}}
<div class="pug-toolbar" style="margin-bottom: 24px; padding: 18px 24px;">
<div class="pug-toolbar-form">
<div class="pug-toolbar-item" style="flex: 1;">
<label class="pug-field-label">Cari Data Teknisi</label>
<div class="pug-input-icon">
<i class="fas fa-search"></i>
<input type="text" id="searchInput" class="pug-input"
placeholder="Cari nama, email, atau no. telepon..."
autocomplete="off">
</div>
</div>
<div class="pug-toolbar-item item-btn" style="flex: 0 0 auto;">
<button id="refreshBtn" class="pug-reset-btn" onclick="handleRefresh()" style="height: 44px; border: 1px solid #e2e8f0; background: #fff;">
<i class="fas fa-sync-alt"></i> Refresh Data
</button>
</div>
</div>
</div>
{{-- ── PANEL TABLE ── --}}
<div class="pug-panel">
<div class="pug-panel-head">
<div class="pug-panel-head-left">
<i class="fas fa-id-badge"></i> DAFTAR INFORMASI TEKNISI
</div>
<div class="pug-panel-head-right" id="result-label">Memuat data…</div>
</div>
<div class="table-responsive">
<table class="pug-table">
<thead>
<tr>
<th width="50">#</th>
<th>Nama Teknisi</th>
<th>No. Telepon</th>
<th>Tgl Masuk</th>
<th width="120">Status</th>
<th width="110">Aksi</th>
</tr>
</thead>
<tbody id="teknisiTable">
<tr>
<td colspan="6">
<div class="pug-empty">
<div class="spinner-border text-success" style="width: 2rem; height: 2rem;"></div>
<div class="pug-empty-title mt-2">Memuat Data...</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="pug-pag" style="border-top: 1px solid #f1f5f9; padding: 15px 24px;">
<div style="display:flex; align-items:center; gap:8px; color: #64748b; font-size: 13px;">
<i class="fas fa-info-circle" style="color: var(--pug-primary);"></i>
<span>Menampilkan <b id="showingStart">0</b> <b id="showingEnd">0</b> dari <b id="totalData">0</b> teknisi</span>
</div>
</div>
</div>
</div>
</div>
{{-- ── MODAL DETAIL TEKNISI ── --}}
<div id="detailModal" class="pug-overlay">
<div class="pug-modal" style="max-width: 500px;">
<div class="pug-modal-header">
<div class="pug-modal-title"><i class="fas fa-info-circle"></i> Detail Profil Teknisi</div>
<button class="pug-close-btn" onclick="closeModalDetail()"><i class="fas fa-times"></i></button>
</div>
<div id="detailContent" style="padding: 24px;">
<div style="text-align: center; margin-bottom: 20px;">
<div id="detAv" class="pug-av" style="width: 80px; height: 80px; font-size: 32px; margin: 0 auto 12px;"></div>
<h3 id="detNama" style="font-size: 18px; font-weight: 700; color: var(--pug-t1); margin-bottom: 4px;"></h3>
<p id="detStatus" style="font-size: 13px;"></p>
</div>
<div class="detail-list" style="display: flex; flex-direction: column; gap: 15px;">
<div class="det-item">
<label style="font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase;">ID Teknisi</label>
<div id="detId" style="font-size: 14px; color: #1e293b; font-weight: 600;"></div>
</div>
<div class="det-item">
<label style="font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase;">Kontak Email</label>
<div id="detEmail" style="font-size: 14px; color: #1e293b;"></div>
</div>
<div class="det-item">
<label style="font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase;">No. Telepon</label>
<div id="detTelp" style="font-size: 14px; color: #1e293b;"></div>
</div>
<div class="det-item">
<label style="font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase;">Alamat</label>
<div id="detAlamat" style="font-size: 14px; color: #1e293b; line-height: 1.5;"></div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
<div>
<label style="font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase;">Info Kelahiran</label>
<div id="detTglL" style="font-size: 14px; color: #1e293b;"></div>
</div>
<div>
<label style="font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase;">Tgl Masuk</label>
<div id="detTglM" style="font-size: 14px; color: #1e293b;"></div>
</div>
</div>
</div>
</div>
<div class="pug-modal-footer">
<button type="button" class="pug-btn-submit" onclick="closeModalDetail()" style="width: 100%;">Tutup Detail</button>
</div>
</div>
</div>
{{-- ── MODAL TAMBAH / EDIT ── --}}
<div id="modal" class="pug-overlay">
<div class="pug-modal pug-modal-lg">
<div class="pug-modal-header">
<div class="pug-modal-title">
<i id="modalIcon" class="fas fa-user-plus"></i>
<span id="modalTitle">Tambah Teknisi</span>
</div>
<button class="pug-close-btn" onclick="closeModal()"><i class="fas fa-times"></i></button>
</div>
<form id="teknisiForm" onsubmit="submitForm(event)">
<input type="hidden" id="teknisiId">
<input type="hidden" id="formMethod" value="POST">
<div class="pug-form-grid" style="padding: 24px;">
<div class="pug-form-field">
<label class="pug-form-label">Nama Lengkap <span class="text-danger">*</span></label>
<input type="text" id="nama" class="pug-input" placeholder="Masukkan nama lengkap" required>
</div>
<div class="pug-form-field">
<label class="pug-form-label">Tanggal Lahir <span id="lblTglLahirReq" class="text-danger">*</span> <span id="lblTglLahirHint" class="text-muted small" style="display:none;">(Tidak dapat diubah)</span></label>
<input type="date" id="tglLahir" class="pug-input" required>
</div>
<div class="pug-form-full">
<label class="pug-form-label">Alamat Lengkap <span class="text-danger">*</span></label>
<textarea id="alamat" class="pug-textarea" rows="3" placeholder="Masukkan alamat domisili saat ini" required></textarea>
</div>
<div class="pug-form-field">
<label class="pug-form-label">Email <span class="text-muted small">(Opsional)</span></label>
<input type="email" id="email" class="pug-input" placeholder="contoh@email.com">
</div>
<div class="pug-form-field">
<label class="pug-form-label">No. Telepon <span class="text-danger">*</span></label>
<input type="tel" id="noTelephone" class="pug-input" placeholder="08xx..." required>
</div>
<div class="pug-form-field">
<label class="pug-form-label">Tanggal Masuk <span id="lblTglMasukReq" class="text-danger">*</span> <span id="lblTglMasukHint" class="text-muted small" style="display:none;">(Tidak dapat diubah)</span></label>
<input type="date" id="tglMasuk" class="pug-input" required>
</div>
<div class="pug-form-field">
<label class="pug-form-label">Status <span class="text-danger">*</span></label>
<select id="status" class="pug-select" required>
<option value=""> Pilih Status </option>
<option value="aktif">Aktif</option>
<option value="tidak_aktif">Tidak Aktif</option>
</select>
</div>
</div>
<div class="pug-modal-footer">
<button type="button" class="pug-btn-cancel" onclick="closeModal()">Batal</button>
<button type="submit" id="submitBtn" class="pug-btn-submit">
<i class="fas fa-save"></i> <span id="submitLabel">Simpan Data</span>
</button>
</div>
</form>
</div>
</div>
<script>
const BASE_URL = '{{ rtrim(config("app.url"), "/") }}';
const csrfToken = '{{ csrf_token() }}';
let searchTimeout;
document.addEventListener('DOMContentLoaded', () => {
loadTeknisiData();
document.getElementById('searchInput').addEventListener('input', function () {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => loadTeknisiData(this.value), 300);
});
});
function handleRefresh() {
const btn = document.getElementById('refreshBtn');
btn.innerHTML = '<i class="fas fa-sync-alt fa-spin"></i> Refreshing...';
loadTeknisiData(document.getElementById('searchInput').value, () => {
btn.innerHTML = '<i class="fas fa-sync-alt"></i> Refresh Data';
});
}
function loadTeknisiData(search = '', done = null) {
document.getElementById('result-label').textContent = 'Memuat…';
fetch(`${BASE_URL}/teknisi?q=${encodeURIComponent(search)}`, {
headers: { 'Accept': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'X-Requested-With': 'XMLHttpRequest' }
})
.then(r => r.json())
.then(res => {
let data = Array.isArray(res) ? res : (res.data || []);
renderTable(data);
updateStats(data);
if (done) done();
});
}
function renderTable(data) {
const tbody = document.getElementById('teknisiTable');
document.getElementById('result-label').textContent = 'Total ' + data.length + ' teknisi ditemukan';
if (!data.length) {
tbody.innerHTML = `<tr><td colspan="6"><div class="pug-empty"><div class="pug-empty-icon"><i class="fas fa-search"></i></div><div class="pug-empty-title">Data Tidak Ditemukan</div></div></td></tr>`;
updatePag(0, 0, 0); return;
}
tbody.innerHTML = data.map((t, i) => {
const aktif = t.status === 'aktif';
return `<tr>
<td><span class="pug-rownum">${(i+1 < 10 ? '0' : '') + (i+1)}</span></td>
<td>
<div style="display:flex; align-items:center; gap:12px;">
<div class="pug-av">${(t.nama || '?').charAt(0).toUpperCase()}</div>
<div><div class="pug-av-name">${t.nama || 'N/A'}</div><div class="pug-av-sub">ID: ${t.id_teknisi || '—'}</div></div>
</div>
</td>
<td><span style="font-weight:600; color:var(--pug-t1);">${t.no_telephone || '—'}</span></td>
<td><span style="font-size:12px; color:#64748b;">${fmtDate(t.tanggal_masuk)}</span></td>
<td>
<span class="pug-badge ${aktif ? 'pug-badge-green' : 'pug-badge-rose'}"><i class="fas fa-${aktif ? 'check-circle' : 'times-circle'}"></i> ${aktif ? 'Aktif' : 'Nonaktif'}</span>
${t.has_unpaid_kasbon ? '<span class="pug-badge pug-badge-rose" style="margin-left:4px;"><i class="fas fa-exclamation-triangle"></i> Hutang</span>' : ''}
</td>
<td>
<div class="pug-actions">
<button class="pug-action-btn pug-action-view" onclick="showDetail(${t.id_teknisi})" title="Detail"><i class="fas fa-eye"></i></button>
<button class="pug-action-btn pug-action-edit" onclick="editTeknisi(${t.id_teknisi})" title="Edit"><i class="fas fa-pen"></i></button>
<button class="pug-action-btn pug-action-del" onclick="deleteTeknisi(${t.id_teknisi})" title="Hapus"><i class="fas fa-trash"></i></button>
</div>
</td>
</tr>`;
}).join('');
updatePag(1, data.length, data.length);
}
function fmtDate(d) {
if (!d) return '—';
return new Date(d).toLocaleDateString('id-ID', { day: '2-digit', month: 'short', year: 'numeric' });
}
function updateStats(data) {
document.getElementById('totalTeknisi').textContent = data.length;
document.getElementById('totalAktif').textContent = data.filter(t => t.status === 'aktif').length;
document.getElementById('totalTidakAktif').textContent = data.filter(t => t.status === 'tidak_aktif').length;
}
function updatePag(s, e, total) {
document.getElementById('showingStart').textContent = s;
document.getElementById('showingEnd').textContent = e;
document.getElementById('totalData').textContent = total;
}
function openModal(mode, id = null) {
document.getElementById('teknisiForm').reset();
const isEdit = mode === 'edit';
document.getElementById('modalIcon').className = isEdit ? 'fas fa-user-edit' : 'fas fa-user-plus';
document.getElementById('modalTitle').textContent = isEdit ? 'Edit Data Teknisi' : 'Tambah Teknisi Baru';
document.getElementById('submitLabel').textContent = isEdit ? 'Update Data' : 'Simpan Data';
document.getElementById('formMethod').value = isEdit ? 'PUT' : 'POST';
const tglL = document.getElementById('tglLahir'); const tglM = document.getElementById('tglMasuk');
if (isEdit) {
tglL.setAttribute('readonly', ''); tglM.removeAttribute('readonly');
document.getElementById('lblTglLahirReq').style.display = 'none'; document.getElementById('lblTglMasukReq').style.display = '';
document.getElementById('lblTglLahirHint').style.display = ''; document.getElementById('lblTglMasukHint').style.display = 'none';
loadDetail(id);
} else {
tglL.removeAttribute('readonly'); tglM.removeAttribute('readonly');
document.getElementById('lblTglLahirReq').style.display = ''; document.getElementById('lblTglMasukReq').style.display = '';
document.getElementById('lblTglLahirHint').style.display = 'none'; document.getElementById('lblTglMasukHint').style.display = 'none';
}
document.getElementById('modal').classList.add('show');
}
function loadDetail(id) {
fetch(`${BASE_URL}/teknisi/${id}`, {
headers: { 'Accept': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'X-Requested-With': 'XMLHttpRequest' }
})
.then(r => r.json())
.then(res => {
const t = res.data;
if (!t) return;
document.getElementById('teknisiId').value = t.id_teknisi;
document.getElementById('nama').value = t.nama;
document.getElementById('tglLahir').value = t.tanggal_lahir;
document.getElementById('alamat').value = t.alamat;
document.getElementById('email').value = t.email || '';
document.getElementById('noTelephone').value = t.no_telephone;
document.getElementById('tglMasuk').value = t.tanggal_masuk;
document.getElementById('status').value = t.status;
});
}
function closeModal() { document.getElementById('modal').classList.remove('show'); }
function closeModalDetail() { document.getElementById('detailModal').classList.remove('show'); }
function editTeknisi(id) { openModal('edit', id); }
function showDetail(id) {
fetch(`${BASE_URL}/teknisi/${id}`, {
headers: { 'Accept': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'X-Requested-With': 'XMLHttpRequest' }
})
.then(r => r.json())
.then(res => {
const t = res.data;
if (!t) return;
document.getElementById('detAv').textContent = (t.nama || '?').charAt(0).toUpperCase();
document.getElementById('detNama').textContent = t.nama || 'N/A';
document.getElementById('detId').textContent = 'ID: ' + (t.id_teknisi || '—');
document.getElementById('detEmail').textContent = t.email || '—';
document.getElementById('detTelp').textContent = t.no_telephone || '—';
document.getElementById('detAlamat').textContent = t.alamat || '—';
document.getElementById('detTglL').textContent = fmtDate(t.tanggal_lahir);
document.getElementById('detTglM').textContent = fmtDate(t.tanggal_masuk);
const statusEl = document.getElementById('detStatus');
const aktif = t.status === 'aktif';
statusEl.className = 'pug-badge ' + (aktif ? 'pug-badge-green' : 'pug-badge-rose');
statusEl.innerHTML = `<i class="fas fa-${aktif ? 'check-circle' : 'times-circle'}"></i> ${aktif ? 'Aktif' : 'Nonaktif'}`;
document.getElementById('detailModal').classList.add('show');
});
}
function submitForm(e) {
e.preventDefault();
const method = document.getElementById('formMethod').value;
const id = document.getElementById('teknisiId').value;
const url = method === 'POST' ? `${BASE_URL}/teknisi` : `${BASE_URL}/teknisi/${id}`;
const body = {
nama: document.getElementById('nama').value,
alamat: document.getElementById('alamat').value,
email: document.getElementById('email').value || null,
no_telephone: document.getElementById('noTelephone').value,
status: document.getElementById('status').value,
tanggal_masuk: document.getElementById('tglMasuk').value,
};
if (method === 'POST') { body.tanggal_lahir = document.getElementById('tglLahir').value; }
if (method === 'PUT') { body._method = 'PUT'; }
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-CSRF-TOKEN': csrfToken },
body: JSON.stringify(body)
})
.then(r => r.json())
.then(data => {
if (data.success) {
closeModal();
loadTeknisiData();
} else {
let errMsg = data.message || 'Gagal menyimpan data.';
if (data.errors) {
const firstKey = Object.keys(data.errors)[0];
errMsg = data.errors[firstKey][0];
}
alert(errMsg);
}
})
.catch(err => {
console.error(err);
alert('Terjadi kesalahan koneksi.');
});
}
function deleteTeknisi(id) {
if (!confirm('Hapus data teknisi ini?')) return;
fetch(`${BASE_URL}/teknisi/${id}`, {
method: 'DELETE',
headers: { 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' }
})
.then(r => r.json())
.then(res => {
if (res.success) {
loadTeknisiData();
} else {
alert(res.message || 'Gagal menghapus data teknisi.');
}
})
.catch(err => {
console.error(err);
alert('Terjadi kesalahan koneksi.');
});
}
</script>
</x-app-layout>