417 lines
21 KiB
PHP
417 lines
21 KiB
PHP
<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 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(`/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></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(`/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(`/teknisi/${id}`, { headers: { 'Accept': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'X-Requested-With': 'XMLHttpRequest' } })
|
||
.then(r => r.json())
|
||
.then(res => {
|
||
const t = res.data; // ✅ Gunakan 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' ? '/teknisi' : `/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(() => { closeModal(); loadTeknisiData(); });
|
||
}
|
||
|
||
function deleteTeknisi(id) {
|
||
if (!confirm('Hapus data teknisi ini?')) return;
|
||
fetch(`/teknisi/${id}`, { method: 'DELETE', headers: { 'X-CSRF-TOKEN': csrfToken } }).then(() => loadTeknisiData());
|
||
}
|
||
</script>
|
||
|
||
</x-app-layout> |