import './bootstrap'; import Alpine from 'alpinejs'; import Swal from 'sweetalert2'; window.Alpine = Alpine; window.Swal = Swal; Alpine.start(); document.addEventListener('DOMContentLoaded', () => { document.querySelectorAll('.delete-form').forEach((form) => { form.addEventListener('submit', (event) => { event.preventDefault(); const entity = form.dataset.entity || 'data ini'; Swal.fire({ title: 'Hapus data?', text: `Yakin ingin menghapus ${entity}? Tindakan ini tidak bisa dibatalkan.`, icon: 'warning', showCancelButton: true, confirmButtonColor: '#e11d48', cancelButtonColor: '#475569', confirmButtonText: 'Ya, hapus', cancelButtonText: 'Batal', reverseButtons: true, }).then((result) => { if (result.isConfirmed) { form.submit(); } }); }); }); const passwordInput = document.querySelector('[data-password-input]'); const passwordToggle = document.querySelector('[data-password-toggle]'); if (passwordInput && passwordToggle) { passwordToggle.addEventListener('click', () => { const isPassword = passwordInput.getAttribute('type') === 'password'; passwordInput.setAttribute('type', isPassword ? 'text' : 'password'); passwordToggle.setAttribute('aria-label', isPassword ? 'Sembunyikan password' : 'Tampilkan password'); }); } const documentTypeSelect = document.querySelector('[data-document-type-select]'); const maxFileSizeInfo = document.querySelector('[data-max-file-size-info]'); const allowedExtensionsInfo = document.querySelector('[data-allowed-extensions-info]'); if (documentTypeSelect && maxFileSizeInfo && allowedExtensionsInfo) { const updateDocumentTypeInfo = () => { const selectedOption = documentTypeSelect.options[documentTypeSelect.selectedIndex]; const maxSizeKb = Number(selectedOption?.dataset?.maxSizeKb || 5120); const maxSizeMb = (maxSizeKb / 1024).toFixed(2); const extensions = (selectedOption?.dataset?.extensions || 'jpg,jpeg,png,pdf,webp') .split(',') .map((item) => item.trim().toUpperCase()) .join(', '); maxFileSizeInfo.textContent = `Maksimal ukuran file untuk jenis dokumen ini: ${maxSizeMb} MB`; allowedExtensionsInfo.textContent = `Format diizinkan: ${extensions}.`; }; documentTypeSelect.addEventListener('change', updateDocumentTypeInfo); updateDocumentTypeInfo(); } const studentHoverTriggers = document.querySelectorAll('.student-hover-trigger'); if (studentHoverTriggers.length > 0) { const hoverCard = document.createElement('div'); hoverCard.className = 'pointer-events-none fixed z-[9999] hidden w-64 overflow-hidden rounded-[20px] border border-slate-200 bg-white p-2.5 shadow-[0_20px_45px_-20px_rgba(15,23,42,0.35)] transition-opacity duration-150'; document.body.appendChild(hoverCard); const renderHoverCard = (trigger) => { const photoUrl = trigger.dataset.photoUrl; const studentName = trigger.dataset.studentName || '-'; const studentNis = trigger.dataset.studentNis || '-'; const studentClass = trigger.dataset.studentClass || '-'; const studentDepartment = trigger.dataset.studentDepartment || '-'; const studentInitial = studentName.charAt(0).toUpperCase(); hoverCard.innerHTML = `
${photoUrl ? `
${studentName}
` : `
${studentInitial}
No Photo
`}
${studentName}
NIS ยท ${studentNis}
${studentClass}
${studentDepartment}
`; }; const positionHoverCard = (trigger) => { const rect = trigger.getBoundingClientRect(); const cardWidth = 256; const cardHeight = 120; let left = rect.left; let top = rect.bottom + 12; if (left + cardWidth > window.innerWidth - 16) { left = window.innerWidth - cardWidth - 16; } if (top + cardHeight > window.innerHeight - 16) { top = rect.top - cardHeight - 12; } if (top < 16) { top = 16; } hoverCard.style.left = `${Math.max(left, 16)}px`; hoverCard.style.top = `${top}px`; }; const showHoverCard = (trigger) => { renderHoverCard(trigger); positionHoverCard(trigger); hoverCard.classList.remove('hidden'); hoverCard.style.opacity = '1'; }; const hideHoverCard = () => { hoverCard.style.opacity = '0'; hoverCard.classList.add('hidden'); }; studentHoverTriggers.forEach((trigger) => { trigger.addEventListener('mouseenter', () => showHoverCard(trigger)); trigger.addEventListener('mouseleave', hideHoverCard); trigger.addEventListener('focus', () => showHoverCard(trigger)); trigger.addEventListener('blur', hideHoverCard); }); window.addEventListener('scroll', hideHoverCard, true); window.addEventListener('resize', hideHoverCard); } const bulkDeleteStudentsForm = document.querySelector('#bulk-delete-students-form'); if (bulkDeleteStudentsForm) { const selectAllCheckbox = document.querySelector('#select-all-students'); const rowCheckboxes = bulkDeleteStudentsForm.querySelectorAll('.student-row-checkbox'); const bulkDeleteButton = document.querySelector('#bulk-delete-students-button'); const syncBulkDeleteState = () => { const checkedCount = Array.from(rowCheckboxes).filter((checkbox) => checkbox.checked).length; if (bulkDeleteButton) { bulkDeleteButton.disabled = checkedCount === 0; bulkDeleteButton.classList.toggle('opacity-50', checkedCount === 0); bulkDeleteButton.classList.toggle('cursor-not-allowed', checkedCount === 0); } if (selectAllCheckbox) { selectAllCheckbox.checked = checkedCount > 0 && checkedCount === rowCheckboxes.length; selectAllCheckbox.indeterminate = checkedCount > 0 && checkedCount < rowCheckboxes.length; } }; selectAllCheckbox?.addEventListener('change', () => { rowCheckboxes.forEach((checkbox) => { checkbox.checked = selectAllCheckbox.checked; }); syncBulkDeleteState(); }); rowCheckboxes.forEach((checkbox) => { checkbox.addEventListener('change', syncBulkDeleteState); }); bulkDeleteStudentsForm.addEventListener('submit', (event) => { const checkedCount = Array.from(rowCheckboxes).filter((checkbox) => checkbox.checked).length; if (checkedCount === 0) { event.preventDefault(); return; } event.preventDefault(); Swal.fire({ title: 'Hapus banyak data siswa?', text: `${checkedCount} data siswa yang dipilih akan dihapus. Tindakan ini tidak bisa dibatalkan.`, icon: 'warning', showCancelButton: true, confirmButtonColor: '#e11d48', cancelButtonColor: '#475569', confirmButtonText: 'Ya, hapus semua', cancelButtonText: 'Batal', reverseButtons: true, }).then((result) => { if (result.isConfirmed) { bulkDeleteStudentsForm.submit(); } }); }); syncBulkDeleteState(); } const promotionForm = document.querySelector('[data-student-promotion-form]'); if (promotionForm) { const rows = promotionForm.querySelectorAll('tbody tr'); const renderTargetOptions = (targetSelect, actionValue) => { if (!targetSelect) { return 0; } const optionKey = actionValue === 'repeat' ? 'optionsRepeat' : 'optionsPromote'; const selectedKey = actionValue === 'repeat' ? 'selectedRepeat' : 'selectedPromote'; const options = JSON.parse(targetSelect.dataset[optionKey] || '[]'); const selectedValue = targetSelect.dataset[selectedKey] || ''; targetSelect.innerHTML = ''; options.forEach((option) => { const optionElement = document.createElement('option'); optionElement.value = option.id; optionElement.textContent = option.name; if (String(option.id) === String(selectedValue)) { optionElement.selected = true; } targetSelect.appendChild(optionElement); }); return options.length; }; const applyRowActionState = (row, actionValue) => { const targetSelect = row.querySelector('.promotion-target-select'); const targetHelp = row.querySelector('.promotion-target-help'); const graduatePlaceholder = row.querySelector('.promotion-graduate-placeholder'); const hiddenTarget = row.querySelector('[data-promotion-target-hidden]'); if (targetSelect) { const requiresTarget = actionValue === 'promote' || actionValue === 'repeat'; const optionCount = requiresTarget ? renderTargetOptions(targetSelect, actionValue) : 0; targetSelect.disabled = !requiresTarget; targetSelect.classList.toggle('bg-slate-100', !requiresTarget); targetSelect.classList.toggle('text-slate-400', !requiresTarget); targetSelect.classList.toggle('hidden', !requiresTarget); if (!requiresTarget) { targetSelect.value = ''; } if (targetHelp) { if (actionValue === 'skip') { targetHelp.textContent = 'Siswa ini akan dilewati saat proses promosi.'; } else if (actionValue === 'graduate') { targetHelp.textContent = 'Aksi lulus tidak memerlukan kelas tujuan.'; } else if (optionCount === 0) { targetHelp.textContent = actionValue === 'repeat' ? 'Belum ada kelas aktif dengan level yang sama pada tahun ajaran target.' : 'Belum ada kelas aktif level berikutnya pada tahun ajaran target.'; } else { targetHelp.textContent = actionValue === 'repeat' ? 'Pilih kelas tujuan dengan level yang sama untuk skenario tinggal kelas.' : 'Pilih kelas tujuan level berikutnya untuk skenario naik kelas.'; } } } if (graduatePlaceholder) { graduatePlaceholder.classList.toggle('hidden', actionValue !== 'graduate'); graduatePlaceholder.classList.toggle('opacity-60', actionValue === 'skip'); } if (hiddenTarget && (actionValue === 'graduate' || actionValue === 'skip')) { hiddenTarget.value = ''; } row.classList.toggle('opacity-60', actionValue === 'skip'); }; rows.forEach((row) => { const actionSelect = row.querySelector('.promotion-action-select'); if (!actionSelect) { return; } applyRowActionState(row, actionSelect.value); actionSelect.addEventListener('change', () => applyRowActionState(row, actionSelect.value)); }); promotionForm.querySelectorAll('[data-promotion-bulk-action]').forEach((button) => { button.addEventListener('click', () => { const actionValue = button.dataset.promotionBulkAction; rows.forEach((row) => { const actionSelect = row.querySelector('.promotion-action-select'); if (!actionSelect) { return; } if (actionValue === 'promote' && actionSelect.querySelector('option[value="promote"]')) { actionSelect.value = 'promote'; } else if (actionValue === 'repeat') { actionSelect.value = 'repeat'; } else if (actionValue === 'skip') { actionSelect.value = 'skip'; } applyRowActionState(row, actionSelect.value); }); }); }); } });