320 lines
16 KiB
PHP
320 lines
16 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="id">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Analisis Rekomendasi - ASGARD SMAN 4 Jember</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;700;800&display=swap" rel="stylesheet">
|
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
|
<style>
|
|
body { font-family: 'Plus Jakarta Sans', sans-serif; background-color: #f8fafc; }
|
|
</style>
|
|
</head>
|
|
<body class="flex flex-col min-h-screen">
|
|
|
|
<nav class="bg-white border-b border-slate-100 px-6 py-4 mb-10">
|
|
<div class="max-w-7xl mx-auto flex justify-between items-center">
|
|
<div class="flex items-center gap-4">
|
|
<div class="w-10 h-10 bg-emerald-100 rounded-xl flex items-center justify-center text-emerald-600 font-bold">AS</div>
|
|
<span class="font-black text-xl tracking-tighter text-emerald-950 uppercase italic">ASGARD.</span>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-3">
|
|
<a href="{{ route('student.profile') }}"
|
|
class="text-sm font-semibold text-slate-600 hover:text-emerald-600 transition">
|
|
Dashboard
|
|
</a>
|
|
<button onclick="handleLogout()"
|
|
class="bg-red-50 hover:bg-red-100 text-red-600 border border-red-100 px-4 py-2 rounded-full transition-all">
|
|
<span class="text-[10px] font-bold uppercase tracking-widest">Logout</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="max-w-4xl mx-auto px-6 pb-20 w-full">
|
|
<h2 class="text-3xl font-extrabold text-slate-800 mb-2">Formulir Rekomendasi Jurusan</h2>
|
|
<p class="text-slate-500 mb-8">Lengkapi data nilai dan jawab kuesioner di bawah ini dengan jujur.</p>
|
|
|
|
<div id="alert-box" class="hidden mb-6 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700"></div>
|
|
|
|
<div class="bg-white p-6 rounded-2xl shadow-sm border border-slate-100 mb-8">
|
|
<h3 class="font-bold text-lg mb-4 text-emerald-700">1. Data Nilai Rapor (Skala 0-100)</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div><label class="text-sm font-semibold">Agama</label><input type="number" id="agama" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
<div><label class="text-sm font-semibold">PKN</label><input type="number" id="pkn" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
<div><label class="text-sm font-semibold">Bahasa Indonesia</label><input type="number" id="bahasa_indonesia" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
<div><label class="text-sm font-semibold">Bahasa Inggris</label><input type="number" id="bahasa_inggris" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
<div><label class="text-sm font-semibold">Matematika</label><input type="number" id="matematika" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
<div><label class="text-sm font-semibold">Fisika</label><input type="number" id="fisika" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
<div><label class="text-sm font-semibold">Kimia</label><input type="number" id="kimia" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
<div><label class="text-sm font-semibold">Biologi</label><input type="number" id="biologi" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
<div><label class="text-sm font-semibold">Sejarah</label><input type="number" id="sejarah" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
<div><label class="text-sm font-semibold">Geografi</label><input type="number" id="geografi" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
<div><label class="text-sm font-semibold">Ekonomi</label><input type="number" id="ekonomi_mapel" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
<div><label class="text-sm font-semibold">Sosiologi</label><input type="number" id="sosiologi" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
<div><label class="text-sm font-semibold">Seni Budaya</label><input type="number" id="seni_budaya" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
<div><label class="text-sm font-semibold">PJOK</label><input type="number" id="pjok" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
<div><label class="text-sm font-semibold">Informatika</label><input type="number" id="informatika" min="0" max="100" class="w-full border rounded-lg p-2 mt-1" value="85"></div>
|
|
</div>
|
|
|
|
<div class="mt-4">
|
|
<label class="text-sm font-semibold text-slate-700">Rata-rata Gaji Orang Tua (Nominal Rupiah)</label>
|
|
<input type="number" id="gaji_ortu" min="0" class="w-full border rounded-lg p-2 mt-1" placeholder="Contoh: 3000000" value="3000000">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-white p-6 rounded-2xl shadow-sm border border-slate-100 mb-8">
|
|
<h3 class="font-bold text-lg mb-1 text-emerald-700">2. Tes Minat Bakat (RIASEC)</h3>
|
|
<p class="text-sm text-slate-500 mb-5">
|
|
Centang kegiatan yang Anda sukai. Setiap jawaban "Ya" bernilai 50 poin, dengan maksimum 100 poin per kategori.
|
|
</p>
|
|
|
|
<div class="space-y-4">
|
|
<div class="p-4 border border-slate-100 rounded-xl bg-slate-50">
|
|
<p class="font-bold text-slate-800 mb-2">Tipe Realistic (R)</p>
|
|
<label class="flex items-center gap-3 mb-2"><input type="checkbox" class="w-5 h-5 riasec-chk" data-type="r"> Saya suka memperbaiki barang elektronik atau mesin kendaraan.</label>
|
|
<label class="flex items-center gap-3"><input type="checkbox" class="w-5 h-5 riasec-chk" data-type="r"> Saya menyukai kegiatan fisik atau bekerja di luar ruangan.</label>
|
|
</div>
|
|
|
|
<div class="p-4 border border-slate-100 rounded-xl bg-slate-50">
|
|
<p class="font-bold text-slate-800 mb-2">Tipe Investigative (I)</p>
|
|
<label class="flex items-center gap-3 mb-2"><input type="checkbox" class="w-5 h-5 riasec-chk" data-type="i"> Saya suka meneliti, mengamati, dan memecahkan teka-teki logika.</label>
|
|
<label class="flex items-center gap-3"><input type="checkbox" class="w-5 h-5 riasec-chk" data-type="i"> Saya tertarik mendalami teori sains, matematika, atau komputer.</label>
|
|
</div>
|
|
|
|
<div class="p-4 border border-slate-100 rounded-xl bg-slate-50">
|
|
<p class="font-bold text-slate-800 mb-2">Tipe Artistic (A)</p>
|
|
<label class="flex items-center gap-3 mb-2"><input type="checkbox" class="w-5 h-5 riasec-chk" data-type="a"> Saya senang menggambar, mendesain, atau mengekspresikan seni.</label>
|
|
<label class="flex items-center gap-3"><input type="checkbox" class="w-5 h-5 riasec-chk" data-type="a"> Saya lebih suka pekerjaan kreatif yang tidak memiliki aturan kaku.</label>
|
|
</div>
|
|
|
|
<div class="p-4 border border-slate-100 rounded-xl bg-slate-50">
|
|
<p class="font-bold text-slate-800 mb-2">Tipe Social (S)</p>
|
|
<label class="flex items-center gap-3 mb-2"><input type="checkbox" class="w-5 h-5 riasec-chk" data-type="s"> Saya merasa puas ketika bisa membantu, mengajar, atau merawat orang lain.</label>
|
|
<label class="flex items-center gap-3"><input type="checkbox" class="w-5 h-5 riasec-chk" data-type="s"> Saya mudah berempati dan suka bekerja dalam lingkungan masyarakat.</label>
|
|
</div>
|
|
|
|
<div class="p-4 border border-slate-100 rounded-xl bg-slate-50">
|
|
<p class="font-bold text-slate-800 mb-2">Tipe Enterprising (E)</p>
|
|
<label class="flex items-center gap-3 mb-2"><input type="checkbox" class="w-5 h-5 riasec-chk" data-type="e"> Saya suka memimpin sebuah tim atau bernegosiasi dalam organisasi.</label>
|
|
<label class="flex items-center gap-3"><input type="checkbox" class="w-5 h-5 riasec-chk" data-type="e"> Saya pandai meyakinkan orang lain dan tertarik pada dunia bisnis.</label>
|
|
</div>
|
|
|
|
<div class="p-4 border border-slate-100 rounded-xl bg-slate-50">
|
|
<p class="font-bold text-slate-800 mb-2">Tipe Conventional (C)</p>
|
|
<label class="flex items-center gap-3 mb-2"><input type="checkbox" class="w-5 h-5 riasec-chk" data-type="c"> Saya sangat teliti dan suka mengatur data, dokumen, atau arsip.</label>
|
|
<label class="flex items-center gap-3"><input type="checkbox" class="w-5 h-5 riasec-chk" data-type="c"> Saya lebih nyaman bekerja dengan angka, jadwal, dan aturan yang jelas.</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button id="btn-submit" class="w-full bg-emerald-600 hover:bg-emerald-700 text-white p-4 rounded-xl font-bold text-lg transition shadow-lg">
|
|
Jalankan AI Engine
|
|
</button>
|
|
</div>
|
|
|
|
<script>
|
|
const alertBox = document.getElementById('alert-box');
|
|
const btnSubmit = document.getElementById('btn-submit');
|
|
|
|
function showError(message) {
|
|
alertBox.classList.remove('hidden');
|
|
alertBox.textContent = message;
|
|
}
|
|
|
|
function hideError() {
|
|
alertBox.classList.add('hidden');
|
|
alertBox.textContent = '';
|
|
}
|
|
|
|
function getNumberValue(id) {
|
|
return Number(document.getElementById(id).value || 0);
|
|
}
|
|
|
|
function getAccessToken() {
|
|
return localStorage.getItem('access_token');
|
|
}
|
|
|
|
function getUserRole() {
|
|
return localStorage.getItem('user_role');
|
|
}
|
|
|
|
function redirectByRole(role) {
|
|
if (role === 'admin') {
|
|
window.location.href = "{{ route('admin.dashboard') }}";
|
|
return;
|
|
}
|
|
|
|
if (role === 'siswa') {
|
|
window.location.href = "{{ route('student.profile') }}";
|
|
return;
|
|
}
|
|
|
|
window.location.href = "{{ route('login') }}";
|
|
}
|
|
|
|
function validateForm(payload) {
|
|
const scoreFields = [
|
|
'agama', 'pkn', 'bahasa_indonesia', 'bahasa_inggris', 'matematika',
|
|
'fisika', 'kimia', 'biologi', 'sejarah', 'geografi',
|
|
'ekonomi_mapel', 'sosiologi', 'seni_budaya', 'pjok', 'informatika'
|
|
];
|
|
|
|
for (const field of scoreFields) {
|
|
const value = Number(payload[field]);
|
|
if (isNaN(value) || value < 0 || value > 100) {
|
|
return `Nilai ${field.replaceAll('_', ' ')} harus berada pada rentang 0 sampai 100.`;
|
|
}
|
|
}
|
|
|
|
if (Number(payload.gaji_ortu) < 0) {
|
|
return 'Gaji orang tua tidak boleh bernilai negatif.';
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
async function handleLogout() {
|
|
const token = getAccessToken();
|
|
|
|
if (token) {
|
|
try {
|
|
await fetch('/api/logout', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${token}`,
|
|
'Accept': 'application/json'
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('Logout error:', error);
|
|
}
|
|
}
|
|
|
|
localStorage.removeItem('access_token');
|
|
localStorage.removeItem('user_role');
|
|
window.location.href = "{{ route('login') }}";
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
const token = getAccessToken();
|
|
const role = getUserRole();
|
|
|
|
if (!token) {
|
|
window.location.href = "{{ route('login') }}";
|
|
return;
|
|
}
|
|
|
|
if (role !== 'siswa') {
|
|
redirectByRole(role);
|
|
}
|
|
});
|
|
|
|
btnSubmit.addEventListener('click', async function () {
|
|
hideError();
|
|
|
|
const token = getAccessToken();
|
|
const role = getUserRole();
|
|
|
|
if (!token) {
|
|
showError('Sesi login tidak ditemukan. Silakan login kembali.');
|
|
setTimeout(() => {
|
|
window.location.href = "{{ route('login') }}";
|
|
}, 1200);
|
|
return;
|
|
}
|
|
|
|
if (role !== 'siswa') {
|
|
redirectByRole(role);
|
|
return;
|
|
}
|
|
|
|
btnSubmit.innerHTML = 'Memproses Analisis...';
|
|
btnSubmit.disabled = true;
|
|
|
|
const scores = { r: 0, i: 0, a: 0, s: 0, e: 0, c: 0 };
|
|
document.querySelectorAll('.riasec-chk:checked').forEach(chk => {
|
|
scores[chk.dataset.type] += 50;
|
|
});
|
|
|
|
const payload = {
|
|
agama: getNumberValue('agama'),
|
|
pkn: getNumberValue('pkn'),
|
|
bahasa_indonesia: getNumberValue('bahasa_indonesia'),
|
|
bahasa_inggris: getNumberValue('bahasa_inggris'),
|
|
matematika: getNumberValue('matematika'),
|
|
fisika: getNumberValue('fisika'),
|
|
kimia: getNumberValue('kimia'),
|
|
biologi: getNumberValue('biologi'),
|
|
sejarah: getNumberValue('sejarah'),
|
|
geografi: getNumberValue('geografi'),
|
|
ekonomi_mapel: getNumberValue('ekonomi_mapel'),
|
|
sosiologi: getNumberValue('sosiologi'),
|
|
seni_budaya: getNumberValue('seni_budaya'),
|
|
pjok: getNumberValue('pjok'),
|
|
informatika: getNumberValue('informatika'),
|
|
riasec_r: scores.r,
|
|
riasec_i: scores.i,
|
|
riasec_a: scores.a,
|
|
riasec_s: scores.s,
|
|
riasec_e: scores.e,
|
|
riasec_c: scores.c,
|
|
gaji_ortu: getNumberValue('gaji_ortu')
|
|
};
|
|
|
|
const validationError = validateForm(payload);
|
|
if (validationError) {
|
|
showError(validationError);
|
|
btnSubmit.innerHTML = 'Jalankan AI Engine';
|
|
btnSubmit.disabled = false;
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch('/api/student/proses-rekomendasi', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
|
'Authorization': `Bearer ${token}`
|
|
},
|
|
body: JSON.stringify(payload)
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (response.ok && result.success) {
|
|
window.location.href = result.redirect_url;
|
|
return;
|
|
}
|
|
|
|
if (response.status === 401) {
|
|
showError('Sesi login berakhir. Silakan login kembali.');
|
|
localStorage.removeItem('access_token');
|
|
localStorage.removeItem('user_role');
|
|
|
|
setTimeout(() => {
|
|
window.location.href = "{{ route('login') }}";
|
|
}, 1200);
|
|
return;
|
|
}
|
|
|
|
if (response.status === 403) {
|
|
showError(result.message || 'Anda tidak memiliki akses ke halaman ini.');
|
|
return;
|
|
}
|
|
|
|
showError(result.message || 'Terjadi kesalahan saat memproses rekomendasi.');
|
|
} catch (error) {
|
|
showError('Kesalahan jaringan. Pastikan Laravel dan API Flask sedang berjalan.');
|
|
} finally {
|
|
btnSubmit.innerHTML = 'Jalankan AI Engine';
|
|
btnSubmit.disabled = false;
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |