init backend presensi
This commit is contained in:
215
app/Views/dashboard/subjects.php
Normal file
215
app/Views/dashboard/subjects.php
Normal file
@@ -0,0 +1,215 @@
|
||||
<div class="space-y-6">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
<div>
|
||||
<h1 class="text-xl font-semibold">Mata Pelajaran</h1>
|
||||
<p class="text-gray-600 dark:text-gray-400 mt-1">Kelola data mata pelajaran.</p>
|
||||
</div>
|
||||
<button type="button" id="btn-add-subject" class="inline-flex items-center gap-2 px-4 py-2.5 rounded-lg bg-primary text-white font-medium hover:bg-primary-hover transition-colors">
|
||||
<i class="bx bx-plus text-xl"></i>
|
||||
<span>Tambah Mata Pelajaran</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="subjects-loading" class="rounded-2xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-12 text-center text-gray-500 dark:text-gray-400">Memuat…</div>
|
||||
<div id="subjects-error" class="hidden rounded-2xl border border-red-200 dark:border-red-800 bg-red-50 dark:bg-red-900/20 p-6 text-red-700 dark:text-red-300"></div>
|
||||
<div id="subjects-content" class="hidden rounded-2xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-sm overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700/50 text-sm text-gray-600 dark:text-gray-400">
|
||||
<tr>
|
||||
<th class="px-6 py-3 font-medium">Nama</th>
|
||||
<th class="px-6 py-3 font-medium">Kode</th>
|
||||
<th class="px-6 py-3 font-medium text-right">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="subjects-tbody" class="divide-y divide-gray-200 dark:divide-gray-700"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add/Edit Modal -->
|
||||
<div id="modal-subject" class="fixed inset-0 z-50 hidden" aria-hidden="true">
|
||||
<div class="fixed inset-0 bg-black/50" id="modal-subject-backdrop"></div>
|
||||
<div class="fixed inset-0 flex items-center justify-center p-4">
|
||||
<div class="relative w-full max-w-md rounded-2xl bg-white dark:bg-gray-800 shadow-xl border border-gray-200 dark:border-gray-700">
|
||||
<div class="p-6">
|
||||
<h2 id="modal-subject-title" class="text-lg font-semibold mb-4">Tambah Mata Pelajaran</h2>
|
||||
<form id="form-subject">
|
||||
<input type="hidden" id="form-subject-id" value="">
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="form-subject-name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Nama <span class="text-red-500">*</span></label>
|
||||
<input type="text" id="form-subject-name" name="name" required maxlength="255" class="w-full px-4 py-2.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-primary focus:border-transparent" placeholder="Contoh: Matematika">
|
||||
</div>
|
||||
<div>
|
||||
<label for="form-subject-code" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Kode</label>
|
||||
<input type="text" id="form-subject-code" name="code" maxlength="50" class="w-full px-4 py-2.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-primary focus:border-transparent" placeholder="Contoh: MAT">
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3 mt-6">
|
||||
<button type="submit" id="form-subject-submit" class="flex-1 px-4 py-2.5 rounded-lg bg-primary text-white font-medium hover:bg-primary-hover">Simpan</button>
|
||||
<button type="button" id="form-subject-cancel" class="px-4 py-2.5 rounded-lg border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700">Batal</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete confirmation -->
|
||||
<div id="modal-subject-delete" class="fixed inset-0 z-50 hidden" aria-hidden="true">
|
||||
<div class="fixed inset-0 bg-black/50" id="modal-subject-delete-backdrop"></div>
|
||||
<div class="fixed inset-0 flex items-center justify-center p-4">
|
||||
<div class="relative w-full max-w-sm rounded-2xl bg-white dark:bg-gray-800 shadow-xl border border-gray-200 dark:border-gray-700 p-6">
|
||||
<h2 class="text-lg font-semibold mb-2">Hapus Mata Pelajaran</h2>
|
||||
<p id="modal-subject-delete-message" class="text-gray-600 dark:text-gray-400 mb-6">Yakin ingin menghapus?</p>
|
||||
<div class="flex gap-3">
|
||||
<button type="button" id="modal-subject-delete-confirm" class="flex-1 px-4 py-2.5 rounded-lg bg-red-600 text-white font-medium hover:bg-red-700">Hapus</button>
|
||||
<button type="button" id="modal-subject-delete-cancel" class="px-4 py-2.5 rounded-lg border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700">Batal</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="toast-container" class="fixed bottom-6 right-6 z-[60] flex flex-col gap-2 pointer-events-none"></div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var baseUrl = '<?= base_url() ?>'.replace(/\/$/, '');
|
||||
var apiSubjects = baseUrl + '/api/academic/subjects';
|
||||
|
||||
var loading = document.getElementById('subjects-loading');
|
||||
var errorEl = document.getElementById('subjects-error');
|
||||
var content = document.getElementById('subjects-content');
|
||||
var tbody = document.getElementById('subjects-tbody');
|
||||
var btnAdd = document.getElementById('btn-add-subject');
|
||||
var modal = document.getElementById('modal-subject');
|
||||
var modalTitle = document.getElementById('modal-subject-title');
|
||||
var form = document.getElementById('form-subject');
|
||||
var formId = document.getElementById('form-subject-id');
|
||||
var formName = document.getElementById('form-subject-name');
|
||||
var formCode = document.getElementById('form-subject-code');
|
||||
var formSubmit = document.getElementById('form-subject-submit');
|
||||
var formCancel = document.getElementById('form-subject-cancel');
|
||||
var modalBackdrop = document.getElementById('modal-subject-backdrop');
|
||||
var modalDelete = document.getElementById('modal-subject-delete');
|
||||
var modalDeleteMsg = document.getElementById('modal-subject-delete-message');
|
||||
var modalDeleteConfirm = document.getElementById('modal-subject-delete-confirm');
|
||||
var modalDeleteCancel = document.getElementById('modal-subject-delete-cancel');
|
||||
var modalDeleteBackdrop = document.getElementById('modal-subject-delete-backdrop');
|
||||
var toastContainer = document.getElementById('toast-container');
|
||||
var deleteTargetId = null;
|
||||
|
||||
function showLoading() { loading.classList.remove('hidden'); errorEl.classList.add('hidden'); content.classList.add('hidden'); }
|
||||
function showError(msg) { loading.classList.add('hidden'); content.classList.add('hidden'); errorEl.classList.remove('hidden'); errorEl.textContent = msg; }
|
||||
function showContent() { loading.classList.add('hidden'); errorEl.classList.add('hidden'); content.classList.remove('hidden'); }
|
||||
|
||||
function escapeHtml(str) { if (str == null) return ''; var d = document.createElement('div'); d.textContent = str; return d.innerHTML; }
|
||||
|
||||
function showToast(message, type) {
|
||||
type = type || 'success';
|
||||
var el = document.createElement('div');
|
||||
el.className = 'pointer-events-auto px-4 py-3 rounded-lg shadow-lg text-sm font-medium ' + (type === 'success' ? 'bg-green-600 text-white' : 'bg-red-600 text-white');
|
||||
el.textContent = message;
|
||||
toastContainer.appendChild(el);
|
||||
setTimeout(function() { if (el.parentNode) el.parentNode.removeChild(el); }, 3000);
|
||||
}
|
||||
|
||||
var fetchOpts = { method: 'GET', credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' } };
|
||||
function postOpts(body) { return { method: 'POST', credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(body) }; }
|
||||
function putOpts(body) { return { method: 'PUT', credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(body) }; }
|
||||
function deleteOpts() { return { method: 'DELETE', credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' } }; }
|
||||
|
||||
function loadSubjects() {
|
||||
showLoading();
|
||||
fetch(apiSubjects, fetchOpts)
|
||||
.then(function(res) { return res.json().then(function(j) { return { ok: res.ok, data: j }; }); })
|
||||
.then(function(r) {
|
||||
if (!r.ok) { showError(r.data && r.data.message ? r.data.message : 'Gagal memuat'); return; }
|
||||
var list = (r.data && r.data.data) ? r.data.data : (Array.isArray(r.data) ? r.data : []);
|
||||
tbody.innerHTML = '';
|
||||
list.forEach(function(row) {
|
||||
var tr = document.createElement('tr');
|
||||
tr.className = 'hover:bg-gray-50 dark:hover:bg-gray-700/30';
|
||||
tr.innerHTML =
|
||||
'<td class="px-6 py-3 font-medium">' + escapeHtml(row.name) + '</td>' +
|
||||
'<td class="px-6 py-3 text-gray-600 dark:text-gray-400">' + (row.code ? escapeHtml(row.code) : '–') + '</td>' +
|
||||
'<td class="px-6 py-3 text-right">' +
|
||||
'<button type="button" class="btn-edit px-3 py-1.5 rounded-lg text-primary hover:bg-primary/10" data-id="' + row.id + '" data-name="' + escapeHtml(row.name) + '" data-code="' + escapeHtml(row.code || '') + '">Edit</button> ' +
|
||||
'<button type="button" class="btn-delete px-3 py-1.5 rounded-lg text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20" data-id="' + row.id + '" data-name="' + escapeHtml(row.name) + '">Hapus</button>' +
|
||||
'</td>';
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
tbody.querySelectorAll('.btn-edit').forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
formId.value = btn.getAttribute('data-id');
|
||||
formName.value = btn.getAttribute('data-name') || '';
|
||||
formCode.value = btn.getAttribute('data-code') || '';
|
||||
modalTitle.textContent = 'Edit Mata Pelajaran';
|
||||
modal.classList.remove('hidden');
|
||||
});
|
||||
});
|
||||
tbody.querySelectorAll('.btn-delete').forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
deleteTargetId = parseInt(btn.getAttribute('data-id'), 10);
|
||||
modalDeleteMsg.textContent = 'Yakin ingin menghapus "' + (btn.getAttribute('data-name') || '') + '"?';
|
||||
modalDelete.classList.remove('hidden');
|
||||
});
|
||||
});
|
||||
showContent();
|
||||
})
|
||||
.catch(function() { showError('Gagal memuat data'); });
|
||||
}
|
||||
|
||||
btnAdd.addEventListener('click', function() {
|
||||
formId.value = ''; formName.value = ''; formCode.value = '';
|
||||
modalTitle.textContent = 'Tambah Mata Pelajaran';
|
||||
modal.classList.remove('hidden');
|
||||
});
|
||||
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
var id = formId.value ? parseInt(formId.value, 10) : null;
|
||||
var payload = { name: formName.value.trim(), code: formCode.value.trim() || null };
|
||||
formSubmit.disabled = true;
|
||||
var url = apiSubjects;
|
||||
var opts = postOpts(payload);
|
||||
if (id) { url = apiSubjects + '/' + id; opts = putOpts(payload); }
|
||||
fetch(url, opts)
|
||||
.then(function(res) { return res.json().then(function(j) { return { ok: res.ok, data: j }; }); })
|
||||
.then(function(r) {
|
||||
formSubmit.disabled = false;
|
||||
if (!r.ok) { showToast(r.data && r.data.message ? r.data.message : 'Gagal menyimpan', 'error'); return; }
|
||||
showToast(id ? 'Berhasil diubah' : 'Berhasil ditambahkan');
|
||||
modal.classList.add('hidden');
|
||||
loadSubjects();
|
||||
})
|
||||
.catch(function() { formSubmit.disabled = false; showToast('Gagal menyimpan', 'error'); });
|
||||
});
|
||||
|
||||
formCancel.addEventListener('click', function() { modal.classList.add('hidden'); });
|
||||
modalBackdrop.addEventListener('click', function() { modal.classList.add('hidden'); });
|
||||
|
||||
modalDeleteConfirm.addEventListener('click', function() {
|
||||
if (!deleteTargetId) return;
|
||||
var id = deleteTargetId;
|
||||
modalDeleteConfirm.disabled = true;
|
||||
fetch(apiSubjects + '/' + id, deleteOpts())
|
||||
.then(function(res) { return res.json().then(function(j) { return { ok: res.ok, data: j }; }); })
|
||||
.then(function(r) {
|
||||
modalDeleteConfirm.disabled = false;
|
||||
if (!r.ok) { showToast(r.data && r.data.message ? r.data.message : 'Gagal menghapus', 'error'); return; }
|
||||
showToast('Berhasil dihapus');
|
||||
modalDelete.classList.add('hidden');
|
||||
deleteTargetId = null;
|
||||
loadSubjects();
|
||||
})
|
||||
.catch(function() { modalDeleteConfirm.disabled = false; showToast('Gagal menghapus', 'error'); });
|
||||
});
|
||||
modalDeleteCancel.addEventListener('click', function() { modalDelete.classList.add('hidden'); deleteTargetId = null; });
|
||||
modalDeleteBackdrop.addEventListener('click', function() { modalDelete.classList.add('hidden'); deleteTargetId = null; });
|
||||
|
||||
loadSubjects();
|
||||
})();
|
||||
</script>
|
||||
Reference in New Issue
Block a user