init backend presensi
This commit is contained in:
507
app/Views/dashboard/discipline.php
Normal file
507
app/Views/dashboard/discipline.php
Normal file
@@ -0,0 +1,507 @@
|
||||
<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">Poin Pelanggaran Siswa</h1>
|
||||
<p class="text-gray-600 dark:text-gray-400 mt-1 text-sm">
|
||||
Guru/Wali Kelas dapat mencatat pelanggaran dan melihat rekap poin perilaku siswa.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<!-- Filter & Form -->
|
||||
<div class="space-y-6 lg:col-span-1">
|
||||
<div class="rounded-2xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-4 shadow-sm space-y-3">
|
||||
<h2 class="font-semibold text-gray-900 dark:text-gray-100 text-sm">Filter</h2>
|
||||
<div class="space-y-3 text-sm">
|
||||
<div>
|
||||
<label class="block mb-1 text-gray-600 dark:text-gray-300">Kelas</label>
|
||||
<select id="filter-class" class="w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 text-sm">
|
||||
<option value="">Semua kelas</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<label class="block mb-1 text-gray-600 dark:text-gray-300">Dari tanggal</label>
|
||||
<input type="date" id="filter-from" class="w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block mb-1 text-gray-600 dark:text-gray-300">Sampai tanggal</label>
|
||||
<input type="date" id="filter-to" class="w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 text-sm">
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" id="btn-filter" class="w-full mt-1 inline-flex items-center justify-center gap-2 px-3 py-2.5 rounded-lg bg-primary text-white text-sm font-medium hover:bg-primary-hover">
|
||||
Terapkan Filter
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-4 shadow-sm space-y-3">
|
||||
<h2 class="font-semibold text-gray-900 dark:text-gray-100 text-sm">Catat Pelanggaran Baru</h2>
|
||||
<div class="space-y-3 text-sm">
|
||||
<div>
|
||||
<label class="block mb-1 text-gray-600 dark:text-gray-300">Kelas</label>
|
||||
<select id="form-class" class="w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 text-sm">
|
||||
<option value="">Pilih kelas</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block mb-1 text-gray-600 dark:text-gray-300">Siswa</label>
|
||||
<select id="form-student" class="w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 text-sm">
|
||||
<option value="">Pilih siswa</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block mb-1 text-gray-600 dark:text-gray-300">Kategori Pelanggaran</label>
|
||||
<select id="form-violation-category" class="w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 text-sm">
|
||||
<option value="">Pilih kategori</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block mb-1 text-gray-600 dark:text-gray-300">Jenis Pelanggaran</label>
|
||||
<select id="form-violation" class="w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 text-sm">
|
||||
<option value="">Pilih jenis pelanggaran</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block mb-1 text-gray-600 dark:text-gray-300">Tanggal & Waktu</label>
|
||||
<input type="datetime-local" id="form-occurred-at" class="w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block mb-1 text-gray-600 dark:text-gray-300">Catatan (opsional)</label>
|
||||
<textarea id="form-notes" rows="2" class="w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 text-sm"></textarea>
|
||||
</div>
|
||||
<button type="button" id="btn-save-violation" class="w-full inline-flex items-center justify-center gap-2 px-3 py-2.5 rounded-lg bg-red-600 text-white text-sm font-medium hover:bg-red-700">
|
||||
Simpan Pelanggaran
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rekap / List -->
|
||||
<div class="lg:col-span-2 space-y-4">
|
||||
<div class="rounded-2xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-4 shadow-sm">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<h2 class="font-semibold text-gray-900 dark:text-gray-100 text-sm">Rekap Pelanggaran</h2>
|
||||
<span id="recap-range" class="text-xs text-gray-500 dark:text-gray-400"></span>
|
||||
</div>
|
||||
<div id="recap-content" class="text-sm text-gray-600 dark:text-gray-300">
|
||||
<p id="recap-empty" class="text-gray-500 dark:text-gray-400">Belum ada data untuk filter ini.</p>
|
||||
<div id="recap-table-wrap" class="hidden overflow-x-auto">
|
||||
<table class="w-full text-left text-xs sm:text-sm">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700/50 text-gray-600 dark:text-gray-400">
|
||||
<tr>
|
||||
<th class="px-3 py-2 font-medium">Siswa</th>
|
||||
<th class="px-3 py-2 font-medium">Kelas</th>
|
||||
<th class="px-3 py-2 font-medium text-right">Total Poin</th>
|
||||
<th class="px-3 py-2 font-medium text-right">Jumlah Kasus</th>
|
||||
<th class="px-3 py-2 font-medium">Level / Tindakan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="recap-tbody" class="divide-y divide-gray-200 dark:divide-gray-700"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-4 shadow-sm">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<h2 class="font-semibold text-gray-900 dark:text-gray-100 text-sm">Riwayat Pelanggaran</h2>
|
||||
</div>
|
||||
<div id="violations-loading" class="py-10 text-center text-gray-500 dark:text-gray-400 hidden">Memuat…</div>
|
||||
<div id="violations-error" class="hidden py-3 text-sm text-red-600 dark:text-red-400"></div>
|
||||
<div id="violations-content" class="hidden overflow-x-auto">
|
||||
<table class="w-full text-left text-xs sm:text-sm">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700/50 text-gray-600 dark:text-gray-400">
|
||||
<tr>
|
||||
<th class="px-3 py-2 font-medium">Tanggal</th>
|
||||
<th class="px-3 py-2 font-medium">Siswa</th>
|
||||
<th class="px-3 py-2 font-medium">Kelas</th>
|
||||
<th class="px-3 py-2 font-medium">Pelanggaran</th>
|
||||
<th class="px-3 py-2 font-medium text-right">Skor</th>
|
||||
<th class="px-3 py-2 font-medium">Guru/Wali</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="violations-tbody" class="divide-y divide-gray-200 dark:divide-gray-700"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="discipline-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 apiClasses = baseUrl + '/api/academic/classes';
|
||||
var apiStudents = baseUrl + '/api/academic/students';
|
||||
var apiViolationsMaster = baseUrl + '/api/discipline/violations';
|
||||
var apiDisciplineLevels = baseUrl + '/api/discipline/levels';
|
||||
var apiStudentViolations = baseUrl + '/api/discipline/student-violations';
|
||||
|
||||
var filterClass = document.getElementById('filter-class');
|
||||
var filterFrom = document.getElementById('filter-from');
|
||||
var filterTo = document.getElementById('filter-to');
|
||||
var btnFilter = document.getElementById('btn-filter');
|
||||
|
||||
var formClass = document.getElementById('form-class');
|
||||
var formStudent = document.getElementById('form-student');
|
||||
var formViolationCategory = document.getElementById('form-violation-category');
|
||||
var formViolation = document.getElementById('form-violation');
|
||||
var formOccurredAt = document.getElementById('form-occurred-at');
|
||||
var formNotes = document.getElementById('form-notes');
|
||||
var btnSave = document.getElementById('btn-save-violation');
|
||||
|
||||
var recapRange = document.getElementById('recap-range');
|
||||
var recapEmpty = document.getElementById('recap-empty');
|
||||
var recapTableWrap = document.getElementById('recap-table-wrap');
|
||||
var recapTbody = document.getElementById('recap-tbody');
|
||||
|
||||
var vLoading = document.getElementById('violations-loading');
|
||||
var vError = document.getElementById('violations-error');
|
||||
var vContent = document.getElementById('violations-content');
|
||||
var vTbody = document.getElementById('violations-tbody');
|
||||
var toastContainer = document.getElementById('discipline-toast-container');
|
||||
|
||||
var classesList = [];
|
||||
var violationsMaster = [];
|
||||
var violationItemsByCategory = {};
|
||||
var disciplineLevels = [];
|
||||
|
||||
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); }, 4000);
|
||||
}
|
||||
|
||||
function fetchJson(url) {
|
||||
return fetch(url, { method: 'GET', credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' } })
|
||||
.then(function(r) { return r.json().then(function(j) { return { ok: r.ok, data: j }; }); });
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
if (str == null) return '';
|
||||
var d = document.createElement('div'); d.textContent = str; return d.innerHTML;
|
||||
}
|
||||
|
||||
function loadClasses(callback) {
|
||||
fetchJson(apiClasses).then(function(r) {
|
||||
var list = (r.ok && r.data && r.data.data) ? r.data.data : [];
|
||||
classesList = list;
|
||||
[filterClass, formClass].forEach(function(sel, idx) {
|
||||
if (!sel) return;
|
||||
var keepFirst = sel.options.length > 0 ? sel.options[0] : null;
|
||||
sel.innerHTML = '';
|
||||
if (keepFirst) sel.appendChild(keepFirst);
|
||||
list.forEach(function(c) {
|
||||
var parts = [];
|
||||
if (c.grade) parts.push(c.grade);
|
||||
if (c.major) parts.push(c.major);
|
||||
if (c.name) parts.push(c.name);
|
||||
var label = c.full_label || parts.join(' ').trim() || ('Kelas ' + c.id);
|
||||
var opt = document.createElement('option');
|
||||
opt.value = c.id;
|
||||
opt.textContent = label;
|
||||
sel.appendChild(opt);
|
||||
});
|
||||
});
|
||||
if (callback) callback();
|
||||
}).catch(function() {
|
||||
showToast('Gagal memuat kelas', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
function loadViolationsMaster() {
|
||||
fetchJson(apiViolationsMaster).then(function(r) {
|
||||
if (!r.ok) { showToast('Gagal memuat daftar pelanggaran', 'error'); return; }
|
||||
violationsMaster = (r.data && r.data.data) ? r.data.data : [];
|
||||
violationItemsByCategory = {};
|
||||
|
||||
// Isi dropdown kategori
|
||||
if (formViolationCategory) {
|
||||
var firstCat = formViolationCategory.options[0];
|
||||
formViolationCategory.innerHTML = '';
|
||||
if (firstCat) formViolationCategory.appendChild(firstCat);
|
||||
violationsMaster.forEach(function(cat) {
|
||||
if (!Array.isArray(cat.items)) return;
|
||||
violationItemsByCategory[cat.id] = cat.items;
|
||||
var opt = document.createElement('option');
|
||||
opt.value = cat.id;
|
||||
opt.textContent = '[' + (cat.code || '') + '] ' + (cat.name || '');
|
||||
formViolationCategory.appendChild(opt);
|
||||
});
|
||||
}
|
||||
|
||||
// Reset dropdown jenis pelanggaran sampai kategori dipilih
|
||||
if (formViolation) {
|
||||
var firstV = formViolation.options[0];
|
||||
formViolation.innerHTML = '';
|
||||
if (firstV) formViolation.appendChild(firstV);
|
||||
}
|
||||
}).catch(function() {
|
||||
showToast('Gagal memuat daftar pelanggaran', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
function loadDisciplineLevels() {
|
||||
fetchJson(apiDisciplineLevels).then(function(r) {
|
||||
if (!r.ok) { showToast('Gagal memuat level disiplin', 'error'); return; }
|
||||
disciplineLevels = (r.data && r.data.data) ? r.data.data : [];
|
||||
}).catch(function() {
|
||||
showToast('Gagal memuat level disiplin', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
function loadStudentsForClass(classId, callback) {
|
||||
if (!classId) {
|
||||
if (formStudent) {
|
||||
var first = formStudent.options[0];
|
||||
formStudent.innerHTML = '';
|
||||
if (first) formStudent.appendChild(first);
|
||||
}
|
||||
if (callback) callback([]);
|
||||
return;
|
||||
}
|
||||
var url = apiStudents + '?class_id=' + encodeURIComponent(classId) + '&per_page=200';
|
||||
fetchJson(url).then(function(r) {
|
||||
var list = (r.ok && r.data && r.data.data) ? r.data.data : [];
|
||||
if (formStudent) {
|
||||
var first = formStudent.options[0];
|
||||
formStudent.innerHTML = '';
|
||||
if (first) formStudent.appendChild(first);
|
||||
list.forEach(function(s) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = s.id;
|
||||
opt.textContent = s.name + (s.nisn ? ' (' + s.nisn + ')' : '');
|
||||
formStudent.appendChild(opt);
|
||||
});
|
||||
}
|
||||
if (callback) callback(list);
|
||||
}).catch(function() {
|
||||
showToast('Gagal memuat siswa', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
function formatDateTime(dt) {
|
||||
if (!dt) return '';
|
||||
var d = new Date(dt);
|
||||
if (isNaN(d.getTime())) return dt;
|
||||
var pad = function(n) { return n < 10 ? '0' + n : '' + n; };
|
||||
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) +
|
||||
' ' + pad(d.getHours()) + ':' + pad(d.getMinutes());
|
||||
}
|
||||
|
||||
function findLevelForScore(score) {
|
||||
if (!Array.isArray(disciplineLevels) || !disciplineLevels.length) return null;
|
||||
for (var i = 0; i < disciplineLevels.length; i++) {
|
||||
var lvl = disciplineLevels[i];
|
||||
var min = lvl.min_score || 0;
|
||||
var max = (lvl.max_score != null) ? lvl.max_score : null;
|
||||
if (score >= min && (max === null || score <= max)) {
|
||||
return lvl;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function applyRecap(list) {
|
||||
if (!Array.isArray(list) || list.length === 0) {
|
||||
recapEmpty.classList.remove('hidden');
|
||||
recapTableWrap.classList.add('hidden');
|
||||
recapTbody.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
var byStudent = {};
|
||||
list.forEach(function(r) {
|
||||
var sid = r.student_id;
|
||||
if (!byStudent[sid]) {
|
||||
byStudent[sid] = {
|
||||
student_name: r.student_name,
|
||||
class_label: r.class_label || '-',
|
||||
total_score: 0,
|
||||
count: 0
|
||||
};
|
||||
}
|
||||
byStudent[sid].total_score += (r.violation_score || 0);
|
||||
byStudent[sid].count += 1;
|
||||
});
|
||||
var rows = Object.keys(byStudent).map(function(id) {
|
||||
var x = byStudent[id];
|
||||
return {
|
||||
student_name: x.student_name,
|
||||
class_label: x.class_label,
|
||||
total_score: x.total_score,
|
||||
count: x.count
|
||||
};
|
||||
});
|
||||
rows.sort(function(a, b) { return b.total_score - a.total_score; });
|
||||
|
||||
recapTbody.innerHTML = '';
|
||||
rows.forEach(function(x) {
|
||||
var lvl = findLevelForScore(x.total_score);
|
||||
var levelText = lvl ? (lvl.title + ' — ' + (lvl.school_action || '')) : '-';
|
||||
var tr = document.createElement('tr');
|
||||
tr.innerHTML =
|
||||
'<td class="px-3 py-2 font-medium">' + escapeHtml(x.student_name) + '</td>' +
|
||||
'<td class="px-3 py-2">' + escapeHtml(x.class_label || '-') + '</td>' +
|
||||
'<td class="px-3 py-2 text-right text-red-600 dark:text-red-400 font-semibold">' + x.total_score + '</td>' +
|
||||
'<td class="px-3 py-2 text-right text-gray-600 dark:text-gray-400">' + x.count + '</td>' +
|
||||
'<td class="px-3 py-2 text-xs text-gray-700 dark:text-gray-300 max-w-xs whitespace-pre-line">' + escapeHtml(levelText) + '</td>';
|
||||
recapTbody.appendChild(tr);
|
||||
});
|
||||
|
||||
recapEmpty.classList.add('hidden');
|
||||
recapTableWrap.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function loadStudentViolations() {
|
||||
vLoading.classList.remove('hidden');
|
||||
vError.classList.add('hidden');
|
||||
vContent.classList.add('hidden');
|
||||
|
||||
var params = [];
|
||||
var classVal = filterClass.value;
|
||||
if (classVal) params.push('class_id=' + encodeURIComponent(classVal));
|
||||
if (filterFrom.value) params.push('from_date=' + encodeURIComponent(filterFrom.value));
|
||||
if (filterTo.value) params.push('to_date=' + encodeURIComponent(filterTo.value));
|
||||
var url = apiStudentViolations;
|
||||
if (params.length) url += '?' + params.join('&');
|
||||
|
||||
fetchJson(url).then(function(r) {
|
||||
vLoading.classList.add('hidden');
|
||||
if (!r.ok) {
|
||||
vError.textContent = (r.data && r.data.message) ? r.data.message : 'Gagal memuat data';
|
||||
vError.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
var list = (r.data && r.data.data) ? r.data.data : [];
|
||||
|
||||
// Recap
|
||||
applyRecap(list);
|
||||
|
||||
vTbody.innerHTML = '';
|
||||
if (!list.length) {
|
||||
vContent.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
list.forEach(function(row) {
|
||||
var tr = document.createElement('tr');
|
||||
tr.innerHTML =
|
||||
'<td class="px-3 py-2 whitespace-nowrap">' + escapeHtml(formatDateTime(row.occurred_at)) + '</td>' +
|
||||
'<td class="px-3 py-2">' + escapeHtml(row.student_name) + '</td>' +
|
||||
'<td class="px-3 py-2">' + escapeHtml(row.class_label || '-') + '</td>' +
|
||||
'<td class="px-3 py-2">' + '[' + escapeHtml(row.category_code || '') + '] ' + escapeHtml(row.violation_title || '') + '</td>' +
|
||||
'<td class="px-3 py-2 text-right text-red-600 dark:text-red-400">' + (row.violation_score || 0) + '</td>' +
|
||||
'<td class="px-3 py-2">' + escapeHtml(row.reported_by_name || '-') + '</td>';
|
||||
vTbody.appendChild(tr);
|
||||
});
|
||||
vContent.classList.remove('hidden');
|
||||
}).catch(function() {
|
||||
vLoading.classList.add('hidden');
|
||||
vError.textContent = 'Gagal memuat data';
|
||||
vError.classList.remove('hidden');
|
||||
});
|
||||
|
||||
var label = '';
|
||||
if (filterFrom.value || filterTo.value) {
|
||||
label = (filterFrom.value || '–') + ' s/d ' + (filterTo.value || '–');
|
||||
}
|
||||
recapRange.textContent = label;
|
||||
}
|
||||
|
||||
btnFilter.addEventListener('click', loadStudentViolations);
|
||||
|
||||
if (formViolationCategory) {
|
||||
formViolationCategory.addEventListener('change', function() {
|
||||
var catId = formViolationCategory.value ? parseInt(formViolationCategory.value, 10) : 0;
|
||||
if (!formViolation) return;
|
||||
var first = formViolation.options[0];
|
||||
formViolation.innerHTML = '';
|
||||
if (first) formViolation.appendChild(first);
|
||||
if (!catId || !violationItemsByCategory[catId]) {
|
||||
return;
|
||||
}
|
||||
violationItemsByCategory[catId].forEach(function(v) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = v.id;
|
||||
opt.textContent = v.title + ' (' + v.score + ' poin)';
|
||||
formViolation.appendChild(opt);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
formClass.addEventListener('change', function() {
|
||||
var cid = formClass.value;
|
||||
loadStudentsForClass(cid);
|
||||
});
|
||||
|
||||
btnSave.addEventListener('click', function() {
|
||||
var studentId = formStudent.value ? parseInt(formStudent.value, 10) : 0;
|
||||
var violationId = formViolation.value ? parseInt(formViolation.value, 10) : 0;
|
||||
var occurredAt = formOccurredAt.value;
|
||||
var notes = formNotes.value.trim() || null;
|
||||
if (!formClass.value) {
|
||||
showToast('Pilih kelas terlebih dahulu', 'error');
|
||||
return;
|
||||
}
|
||||
if (!studentId) {
|
||||
showToast('Pilih siswa terlebih dahulu', 'error');
|
||||
return;
|
||||
}
|
||||
if (!violationId) {
|
||||
showToast('Pilih jenis pelanggaran', 'error');
|
||||
return;
|
||||
}
|
||||
var payload = {
|
||||
student_id: studentId,
|
||||
violation_id: violationId
|
||||
};
|
||||
if (occurredAt) {
|
||||
payload.occurred_at = occurredAt.replace('T', ' ') + ':00';
|
||||
}
|
||||
if (notes) payload.notes = notes;
|
||||
|
||||
btnSave.disabled = true;
|
||||
fetch(apiStudentViolations, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/json', 'Accept': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
}).then(function(r) { return r.json().then(function(j) { return { ok: r.ok, data: j }; }); })
|
||||
.then(function(r) {
|
||||
btnSave.disabled = false;
|
||||
if (!r.ok) {
|
||||
showToast(r.data && r.data.message ? r.data.message : 'Gagal menyimpan pelanggaran', 'error');
|
||||
return;
|
||||
}
|
||||
showToast('Pelanggaran berhasil dicatat');
|
||||
formViolation.value = '';
|
||||
formOccurredAt.value = '';
|
||||
formNotes.value = '';
|
||||
loadStudentViolations();
|
||||
}).catch(function() {
|
||||
btnSave.disabled = false;
|
||||
showToast('Gagal menyimpan pelanggaran', 'error');
|
||||
});
|
||||
});
|
||||
|
||||
// Init defaults
|
||||
var today = new Date();
|
||||
var pad = function(n) { return n < 10 ? '0' + n : '' + n; };
|
||||
var todayStr = today.getFullYear() + '-' + pad(today.getMonth() + 1) + '-' + pad(today.getDate());
|
||||
filterFrom.value = todayStr;
|
||||
filterTo.value = todayStr;
|
||||
|
||||
loadClasses(function() {
|
||||
loadViolationsMaster();
|
||||
loadDisciplineLevels();
|
||||
loadStudentViolations();
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user