Update simulasi-mk.html
This commit is contained in:
377
simulasi-mk.html
377
simulasi-mk.html
@@ -25,8 +25,13 @@
|
||||
.course-card.dragging { opacity: 0.5; border: 2px dashed #a8a29e; background-color: #f5f5f4; }
|
||||
|
||||
/* Drop Zone Styles */
|
||||
.semester-list { min-height: 120px; transition: background-color 0.2s; }
|
||||
.semester-list.drag-over { background-color: #f0fdf4; border: 2px dashed #4ade80; }
|
||||
.semester-list { min-height: 120px; transition: background-color 0.2s, opacity 0.2s; }
|
||||
|
||||
/* Valid Drop Target */
|
||||
.semester-list.valid-target { background-color: #f0fdf4; border: 2px dashed #4ade80; }
|
||||
|
||||
/* Invalid Drop Target (Wrong Parity) */
|
||||
.semester-list.invalid-target { opacity: 0.3; background-color: #e5e5e5; pointer-events: none; border: 2px solid #d4d4d4; }
|
||||
|
||||
/* Warning States */
|
||||
.semester-container { transition: all 0.3s; }
|
||||
@@ -54,11 +59,10 @@
|
||||
</head>
|
||||
<body class="bg-stone-50 text-stone-800">
|
||||
|
||||
<!-- Chosen Palette: Warm Neutrals (Stone) with Logic-based Alerts (Red/Green) -->
|
||||
<!-- Chosen Palette: Warm Neutrals with Logic Alerts -->
|
||||
<!-- Application Structure Plan:
|
||||
- Added "GPA Simulation" controls to each semester header.
|
||||
- Semester containers now react visually to SKS Overload based on previous semester's GPA.
|
||||
- Drag & Drop enables users to "Fail/Postpone" courses to future semesters to fit the SKS limit.
|
||||
- Updated Drag & Drop Logic: Enforce Odd/Even semester constraints.
|
||||
- Visual Feedback: Grey out invalid semesters when dragging.
|
||||
-->
|
||||
<!-- CONFIRMATION: NO SVG graphics used. NO Mermaid JS used. -->
|
||||
|
||||
@@ -68,7 +72,9 @@
|
||||
<header class="mb-10 text-center">
|
||||
<h1 class="text-3xl md:text-4xl font-bold text-stone-900 mb-4">Simulasi Perjalanan Mahasiswa & KRS</h1>
|
||||
<p class="text-lg text-stone-600 max-w-3xl mx-auto">
|
||||
Atur strategi studimu. <span class="font-bold text-orange-600">Simulasikan IP rendah</span>, lihat dampak batas SKS, dan geser mata kuliah untuk menyusun ulang rencana (mengulang/menunda).
|
||||
Atur strategi studimu. <span class="font-bold text-orange-600">Simulasikan IP rendah</span>, lihat dampak batas SKS, dan geser mata kuliah untuk menyusun ulang rencana.
|
||||
<br>
|
||||
<span class="text-sm bg-red-100 text-red-800 px-2 py-1 rounded mt-2 inline-block font-bold">Aturan Baru: MK Ganjil hanya bisa ke Sem Ganjil, Genap ke Genap.</span>
|
||||
</p>
|
||||
|
||||
<div class="mt-6 flex flex-wrap justify-center gap-4 text-sm">
|
||||
@@ -169,7 +175,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="semester-list p-3 space-y-2 flex-grow" ondrop="drop(event)" ondragover="allowDrop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)">
|
||||
<div class="semester-list p-3 space-y-2 flex-grow relative" ondrop="drop(event)" ondragover="allowDrop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)">
|
||||
<!-- Course Cards go here -->
|
||||
</div>
|
||||
|
||||
@@ -181,45 +187,46 @@
|
||||
|
||||
<script>
|
||||
// --- DATA ---
|
||||
|
||||
const generalistSemestersBlueprint = {
|
||||
1: [
|
||||
{code: "MK06", name: "Dasar Pemrograman*", sks: 3},
|
||||
{code: "MK07", name: "Algoritma & Logika", sks: 2},
|
||||
{code: "MK04", name: "Kalkulus", sks: 3},
|
||||
{code: "MK05", name: "Pengantar Informatika", sks: 2},
|
||||
{code: "MK01", name: "Pendidikan Pancasila", sks: 2},
|
||||
{code: "MK02", name: "Pendidikan Agama", sks: 3},
|
||||
{code: "MK03", name: "Bahasa Inggris", sks: 2},
|
||||
{code: "MK08", name: "Kom. Profesional", sks: 2}
|
||||
{code: "MK06", name: "Dasar Pemrograman*", sks: 3, priority: "core"},
|
||||
{code: "MK07", name: "Algoritma & Logika", sks: 2, priority: "core"},
|
||||
{code: "MK04", name: "Kalkulus", sks: 3, priority: "core"},
|
||||
{code: "MK05", name: "Pengantar Informatika", sks: 2, priority: "standard"},
|
||||
{code: "MK01", name: "Pendidikan Pancasila", sks: 2, priority: "standard"},
|
||||
{code: "MK02", name: "Pendidikan Agama", sks: 3, priority: "standard"},
|
||||
{code: "MK03", name: "Bahasa Inggris", sks: 2, priority: "support"},
|
||||
{code: "MK08", name: "Kom. Profesional", sks: 2, priority: "support"}
|
||||
],
|
||||
2: [
|
||||
{code: "MK15", name: "Struktur Data & Algo*", sks: 3},
|
||||
{code: "MK12", name: "Matematika Diskrit", sks: 3},
|
||||
{code: "MK11", name: "Sistem Digital", sks: 2},
|
||||
{code: "MK13", name: "Org. & Ars. Komputer", sks: 3},
|
||||
{code: "MK14", name: "Sistem Operasi*", sks: 3},
|
||||
{code: "MK16", name: "Literasi Digital", sks: 2},
|
||||
{code: "MK10", name: "Bahasa Indonesia", sks: 2},
|
||||
{code: "MK09", name: "Kewarganegaraan", sks: 2}
|
||||
{code: "MK15", name: "Struktur Data & Algo*", sks: 3, priority: "core"},
|
||||
{code: "MK12", name: "Matematika Diskrit", sks: 3, priority: "core"},
|
||||
{code: "MK11", name: "Sistem Digital", sks: 2, priority: "core"},
|
||||
{code: "MK13", name: "Org. & Ars. Komputer", sks: 3, priority: "core"},
|
||||
{code: "MK14", name: "Sistem Operasi*", sks: 3, priority: "core"},
|
||||
{code: "MK16", name: "Literasi Digital", sks: 2, priority: "support"},
|
||||
{code: "MK10", name: "Bahasa Indonesia", sks: 2, priority: "support"},
|
||||
{code: "MK09", name: "Kewarganegaraan", sks: 2, priority: "support"}
|
||||
],
|
||||
3: [
|
||||
{code: "MK20", name: "Basis Data*", sks: 3},
|
||||
{code: "MK21", name: "Jaringan Komputer*", sks: 3},
|
||||
{code: "MK27", name: "PBO*", sks: 3},
|
||||
{code: "MK24", name: "IMK", sks: 2},
|
||||
{code: "MK22", name: "Strategi Algoritma*", sks: 3},
|
||||
{code: "MK17", name: "Aljabar Linier", sks: 3},
|
||||
{code: "MK19", name: "Sistem Informasi", sks: 2},
|
||||
{code: "MK18", name: "Etika & Keberlanjutan", sks: 2}
|
||||
{code: "MK20", name: "Basis Data*", sks: 3, priority: "core"},
|
||||
{code: "MK21", name: "Jaringan Komputer*", sks: 3, priority: "core"},
|
||||
{code: "MK27", name: "PBO*", sks: 3, priority: "core"},
|
||||
{code: "MK22", name: "Strategi Algoritma*", sks: 3, priority: "core"},
|
||||
{code: "MK17", name: "Aljabar Linier", sks: 3, priority: "core"},
|
||||
{code: "MK24", name: "IMK", sks: 2, priority: "support"},
|
||||
{code: "MK19", name: "Sistem Informasi", sks: 2, priority: "support"},
|
||||
{code: "MK18", name: "Etika & Keberlanjutan", sks: 2, priority: "support"}
|
||||
],
|
||||
4: [
|
||||
{code: "MK23", name: "RPL*", sks: 3},
|
||||
{code: "MK32", name: "Probstat", sks: 3},
|
||||
{code: "MK31", name: "Pemrograman Web*", sks: 3},
|
||||
{code: "MK30", name: "Kecerdasan Buatan", sks: 3},
|
||||
{code: "MK25", name: "APSI", sks: 3},
|
||||
{code: "MK29", name: "Pemrog. Jaringan*", sks: 3},
|
||||
{code: "MK26", name: "Sis. Paralel", sks: 2}
|
||||
{code: "MK23", name: "RPL*", sks: 3, priority: "core"},
|
||||
{code: "MK32", name: "Probstat", sks: 3, priority: "core"},
|
||||
{code: "MK31", name: "Pemrograman Web*", sks: 3, priority: "core"},
|
||||
{code: "MK30", name: "Kecerdasan Buatan", sks: 3, priority: "core"},
|
||||
{code: "MK25", name: "APSI", sks: 3, priority: "standard"},
|
||||
{code: "MK29", name: "Pemrog. Jaringan*", sks: 3, priority: "standard"},
|
||||
{code: "MK26", name: "Sis. Paralel", sks: 2, priority: "standard"}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -229,31 +236,31 @@
|
||||
color: "text-blue-800",
|
||||
desc: "Fokus: Data, ML, Vision.",
|
||||
sem5: [
|
||||
{code: "MK35", name: "Machine Learning", sks: 3},
|
||||
{code: "MK28", name: "Metode Numerik*", sks: 3},
|
||||
{code: "MK36", name: "Proyek PL*", sks: 3},
|
||||
{code: "MK37", name: "Proposal TA", sks: 2},
|
||||
{code: "MKP", name: "Data Mining", sks: 3},
|
||||
{code: "MKP", name: "Big Data", sks: 3},
|
||||
{code: "MK33", name: "IoT", sks: 3}
|
||||
{code: "MK35", name: "Machine Learning", sks: 3, priority: "core"},
|
||||
{code: "MK28", name: "Metode Numerik*", sks: 3, priority: "core"},
|
||||
{code: "MK36", name: "Proyek PL*", sks: 3, priority: "core"},
|
||||
{code: "MK37", name: "Proposal TA", sks: 2, priority: "core"},
|
||||
{code: "MKP", name: "Data Mining", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Big Data", sks: 3, priority: "standard"},
|
||||
{code: "MK33", name: "IoT", sks: 3, priority: "standard"}
|
||||
],
|
||||
sem6: [
|
||||
{code: "MK38", name: "Comp. Vision*", sks: 3},
|
||||
{code: "MK43", name: "NLP", sks: 3},
|
||||
{code: "MK44", name: "Kerja Praktik", sks: 2},
|
||||
{code: "MKP", name: "Deep Learning", sks: 3},
|
||||
{code: "MKP", name: "AI Ethics", sks: 2},
|
||||
{code: "MKP", name: "Pilihan Lintas KK", sks: 3},
|
||||
{code: "MK42", name: "Keamanan Info*", sks: 2}
|
||||
{code: "MK38", name: "Comp. Vision*", sks: 3, priority: "core"},
|
||||
{code: "MK43", name: "NLP", sks: 3, priority: "core"},
|
||||
{code: "MK44", name: "Kerja Praktik", sks: 2, priority: "core"},
|
||||
{code: "MKP", name: "Deep Learning", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "AI Ethics", sks: 2, priority: "standard"},
|
||||
{code: "MKP", name: "Pilihan Lintas KK", sks: 3, priority: "standard"},
|
||||
{code: "MK42", name: "Keamanan Info*", sks: 2, priority: "standard"}
|
||||
],
|
||||
sem7: [
|
||||
{code: "MK48", name: "TA 1", sks: 2},
|
||||
{code: "MK45", name: "Teknopreneur [K]", sks: 3},
|
||||
{code: "MK47", name: "Uji PL", sks: 3},
|
||||
{code: "MK42", name: "Keamanan Info", sks: 3},
|
||||
{code: "MKP", name: "Health Info", sks: 3},
|
||||
{code: "MKP", name: "Reinforcement L.", sks: 3},
|
||||
{code: "MKP", name: "Quantum/Bebas", sks: 5}
|
||||
{code: "MK48", name: "TA 1", sks: 2, priority: "core"},
|
||||
{code: "MK45", name: "Teknopreneur [K]", sks: 3, priority: "standard"},
|
||||
{code: "MK47", name: "Uji PL", sks: 3, priority: "standard"},
|
||||
{code: "MK42", name: "Keamanan Info", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Health Info", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Reinforcement L.", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Quantum/Bebas", sks: 5, priority: "standard"}
|
||||
]
|
||||
},
|
||||
rpl: {
|
||||
@@ -261,30 +268,30 @@
|
||||
color: "text-orange-800",
|
||||
desc: "Fokus: Apps, Architecture, UX.",
|
||||
sem5: [
|
||||
{code: "MK36", name: "Proyek PL*", sks: 3},
|
||||
{code: "MKP", name: "UX/UI Design", sks: 3},
|
||||
{code: "MKP", name: "Secure SE", sks: 3},
|
||||
{code: "MKP", name: "Web Lanjut", sks: 3},
|
||||
{code: "MK28", name: "Metode Numerik*", sks: 3},
|
||||
{code: "MK37", name: "Proposal TA", sks: 2},
|
||||
{code: "MK33", name: "IoT", sks: 3}
|
||||
{code: "MK36", name: "Proyek PL*", sks: 3, priority: "core"},
|
||||
{code: "MKP", name: "UX/UI Design", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Secure SE", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Web Lanjut", sks: 3, priority: "standard"},
|
||||
{code: "MK28", name: "Metode Numerik*", sks: 3, priority: "standard"},
|
||||
{code: "MK37", name: "Proposal TA", sks: 2, priority: "core"},
|
||||
{code: "MK33", name: "IoT", sks: 3, priority: "standard"}
|
||||
],
|
||||
sem6: [
|
||||
{code: "MK39", name: "Pemrog. Mobile*", sks: 3},
|
||||
{code: "MKP", name: "Microservices", sks: 3},
|
||||
{code: "MKP", name: "Game Dev", sks: 3},
|
||||
{code: "MK44", name: "Kerja Praktik", sks: 2},
|
||||
{code: "MK41", name: "Sis. Enterprise", sks: 3},
|
||||
{code: "MKP", name: "Pil. Lintas KK", sks: 4}
|
||||
{code: "MK39", name: "Pemrog. Mobile*", sks: 3, priority: "core"},
|
||||
{code: "MKP", name: "Microservices", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Game Dev", sks: 3, priority: "standard"},
|
||||
{code: "MK44", name: "Kerja Praktik", sks: 2, priority: "core"},
|
||||
{code: "MK41", name: "Sis. Enterprise", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Pil. Lintas KK", sks: 4, priority: "standard"}
|
||||
],
|
||||
sem7: [
|
||||
{code: "MK48", name: "TA 1", sks: 2},
|
||||
{code: "MK45", name: "Teknopreneur [K]", sks: 3},
|
||||
{code: "MK47", name: "Uji PL", sks: 3},
|
||||
{code: "MK42", name: "Keamanan Info", sks: 3},
|
||||
{code: "MKP", name: "DevOps", sks: 3},
|
||||
{code: "MKP", name: "Soft. Arch", sks: 3},
|
||||
{code: "MKP", name: "Pil. Bebas", sks: 5}
|
||||
{code: "MK48", name: "TA 1", sks: 2, priority: "core"},
|
||||
{code: "MK45", name: "Teknopreneur [K]", sks: 3, priority: "standard"},
|
||||
{code: "MK47", name: "Uji PL", sks: 3, priority: "standard"},
|
||||
{code: "MK42", name: "Keamanan Info", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "DevOps", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Soft. Arch", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Pil. Bebas", sks: 5, priority: "standard"}
|
||||
]
|
||||
},
|
||||
net: {
|
||||
@@ -292,30 +299,30 @@
|
||||
color: "text-emerald-800",
|
||||
desc: "Fokus: Infra, Security, IoT.",
|
||||
sem5: [
|
||||
{code: "MK33", name: "IoT", sks: 3},
|
||||
{code: "MK34", name: "Manajemen Jar.*", sks: 3},
|
||||
{code: "MKP", name: "Embedded Sys", sks: 3},
|
||||
{code: "MKP", name: "Ethical Hack", sks: 3},
|
||||
{code: "MK36", name: "Proyek PL*", sks: 3},
|
||||
{code: "MK28", name: "Metode Numerik*", sks: 3},
|
||||
{code: "MK37", name: "Proposal TA", sks: 2}
|
||||
{code: "MK33", name: "IoT", sks: 3, priority: "core"},
|
||||
{code: "MK34", name: "Manajemen Jar.*", sks: 3, priority: "core"},
|
||||
{code: "MKP", name: "Embedded Sys", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Ethical Hack", sks: 3, priority: "standard"},
|
||||
{code: "MK36", name: "Proyek PL*", sks: 3, priority: "core"},
|
||||
{code: "MK28", name: "Metode Numerik*", sks: 3, priority: "standard"},
|
||||
{code: "MK37", name: "Proposal TA", sks: 2, priority: "core"}
|
||||
],
|
||||
sem6: [
|
||||
{code: "MK42", name: "Keamanan Info*", sks: 3},
|
||||
{code: "MKP", name: "Wireless Sensor", sks: 3},
|
||||
{code: "MKP", name: "Cryptography", sks: 3},
|
||||
{code: "MK44", name: "Kerja Praktik", sks: 2},
|
||||
{code: "MK39", name: "Mobile (IoT)*", sks: 3},
|
||||
{code: "MKP", name: "Pil. Lintas KK", sks: 3}
|
||||
{code: "MK42", name: "Keamanan Info*", sks: 3, priority: "core"},
|
||||
{code: "MKP", name: "Wireless Sensor", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Cryptography", sks: 3, priority: "standard"},
|
||||
{code: "MK44", name: "Kerja Praktik", sks: 2, priority: "core"},
|
||||
{code: "MK39", name: "Mobile (IoT)*", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Pil. Lintas KK", sks: 3, priority: "standard"}
|
||||
],
|
||||
sem7: [
|
||||
{code: "MK48", name: "TA 1", sks: 2},
|
||||
{code: "MK45", name: "Teknopreneur [K]", sks: 3},
|
||||
{code: "MK47", name: "Uji PL", sks: 3},
|
||||
{code: "MKP", name: "Cloud IoT", sks: 3},
|
||||
{code: "MKP", name: "Blockchain", sks: 3},
|
||||
{code: "MKP", name: "Pil. Bebas 1", sks: 3},
|
||||
{code: "MKP", name: "Pil. Bebas 2", sks: 6}
|
||||
{code: "MK48", name: "TA 1", sks: 2, priority: "core"},
|
||||
{code: "MK45", name: "Teknopreneur [K]", sks: 3, priority: "standard"},
|
||||
{code: "MK47", name: "Uji PL", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Cloud IoT", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Blockchain", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Pil. Bebas 1", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Pil. Bebas 2", sks: 6, priority: "standard"}
|
||||
]
|
||||
},
|
||||
si: {
|
||||
@@ -323,38 +330,38 @@
|
||||
color: "text-purple-800",
|
||||
desc: "Fokus: Business, GIS, Governance.",
|
||||
sem5: [
|
||||
{code: "MKP", name: "Business Intel", sks: 3},
|
||||
{code: "MKP", name: "GIS II", sks: 3},
|
||||
{code: "MK36", name: "Proyek PL*", sks: 3},
|
||||
{code: "MK28", name: "Metode Numerik*", sks: 3},
|
||||
{code: "MK37", name: "Proposal TA", sks: 2},
|
||||
{code: "MKP", name: "E-Business", sks: 3},
|
||||
{code: "MK33", name: "IoT", sks: 3}
|
||||
{code: "MKP", name: "Business Intel", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "GIS II", sks: 3, priority: "standard"},
|
||||
{code: "MK36", name: "Proyek PL*", sks: 3, priority: "core"},
|
||||
{code: "MK28", name: "Metode Numerik*", sks: 3, priority: "standard"},
|
||||
{code: "MK37", name: "Proposal TA", sks: 2, priority: "core"},
|
||||
{code: "MKP", name: "E-Business", sks: 3, priority: "standard"},
|
||||
{code: "MK33", name: "IoT", sks: 3, priority: "standard"}
|
||||
],
|
||||
sem6: [
|
||||
{code: "MK41", name: "Sis. Enterprise", sks: 3},
|
||||
{code: "MK40", name: "SIG", sks: 3},
|
||||
{code: "MKP", name: "Enterprise Arch", sks: 3},
|
||||
{code: "MKP", name: "Smart City", sks: 3},
|
||||
{code: "MK44", name: "Kerja Praktik", sks: 2},
|
||||
{code: "MKP", name: "Pil. Lintas KK", sks: 3}
|
||||
{code: "MK41", name: "Sis. Enterprise", sks: 3, priority: "core"},
|
||||
{code: "MK40", name: "SIG", sks: 3, priority: "core"},
|
||||
{code: "MKP", name: "Enterprise Arch", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Smart City", sks: 3, priority: "standard"},
|
||||
{code: "MK44", name: "Kerja Praktik", sks: 2, priority: "core"},
|
||||
{code: "MKP", name: "Pil. Lintas KK", sks: 3, priority: "standard"}
|
||||
],
|
||||
sem7: [
|
||||
{code: "MK48", name: "TA 1", sks: 2},
|
||||
{code: "MK45", name: "Teknopreneur [K]", sks: 3},
|
||||
{code: "MK47", name: "Uji PL", sks: 3},
|
||||
{code: "MK42", name: "Keamanan Info", sks: 3},
|
||||
{code: "MKP", name: "SCM", sks: 3},
|
||||
{code: "MKP", name: "IT Governance", sks: 3},
|
||||
{code: "MKP", name: "Pil. Bebas", sks: 6}
|
||||
{code: "MK48", name: "TA 1", sks: 2, priority: "core"},
|
||||
{code: "MK45", name: "Teknopreneur [K]", sks: 3, priority: "standard"},
|
||||
{code: "MK47", name: "Uji PL", sks: 3, priority: "standard"},
|
||||
{code: "MK42", name: "Keamanan Info", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "SCM", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "IT Governance", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Pil. Bebas", sks: 6, priority: "standard"}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const mbkmData = [
|
||||
{code: "MK46", name: "PMKM (Wajib)", sks: 2},
|
||||
{code: "MK45", name: "Teknopreneur (Konversi)", sks: 3},
|
||||
{code: "MKP", name: "Pilihan Bebas (Ops)", sks: 3}
|
||||
{code: "MK46", name: "PMKM (Wajib)", sks: 2, priority: "core"},
|
||||
{code: "MK45", name: "Teknopreneur (Konversi)", sks: 3, priority: "standard"},
|
||||
{code: "MKP", name: "Pilihan Bebas (Ops)", sks: 3, priority: "standard"}
|
||||
];
|
||||
|
||||
// --- STATE ---
|
||||
@@ -391,7 +398,7 @@
|
||||
currentPlan[5] = JSON.parse(JSON.stringify(track.sem5));
|
||||
currentPlan[6] = JSON.parse(JSON.stringify(track.sem6));
|
||||
currentPlan[7] = JSON.parse(JSON.stringify(track.sem7));
|
||||
currentPlan[8] = [{code: "MK49", name: "TA 2 (Sidang)", sks: 4}];
|
||||
currentPlan[8] = [{code: "MK49", name: "TA 2 (Sidang)", sks: 4, priority: "core"}];
|
||||
}
|
||||
|
||||
function renderSem(semId, containerId) {
|
||||
@@ -451,21 +458,24 @@
|
||||
semesterStats[semId].gpa = val;
|
||||
gpaValEl.textContent = val.toFixed(2);
|
||||
|
||||
// Update display of next limit immediately
|
||||
if(semId < 8) nextMaxEl.textContent = calculateMaxSKS(val);
|
||||
|
||||
// Trigger re-render of NEXT semester (to show overload warning)
|
||||
// We re-render everything to be safe and simple
|
||||
// Debounce slighty could be good but for now direct call
|
||||
});
|
||||
gpaSlider.addEventListener('change', () => {
|
||||
renderAll(); // Re-render all to propagate red status to next semester
|
||||
renderAll();
|
||||
});
|
||||
|
||||
// Populate List
|
||||
const listContainer = clone.querySelector('.semester-list');
|
||||
listContainer.id = `list-sem-${semId}`; // For drag logic ref
|
||||
listContainer.setAttribute('ondrop', `drop(event, ${semId})`); // Update drop target ID
|
||||
listContainer.id = `list-sem-${semId}`;
|
||||
|
||||
// --- NEW: Parity Enforcement Logic ---
|
||||
const isOddSem = semId % 2 !== 0;
|
||||
const parityClass = isOddSem ? 'odd-semester' : 'even-semester';
|
||||
listContainer.classList.add(parityClass);
|
||||
listContainer.dataset.parity = isOddSem ? 'odd' : 'even';
|
||||
// -------------------------------------
|
||||
|
||||
listContainer.setAttribute('ondrop', `drop(event, ${semId})`);
|
||||
|
||||
courses.forEach((c, idx) => {
|
||||
const el = document.createElement('div');
|
||||
@@ -473,12 +483,24 @@
|
||||
el.setAttribute('draggable', 'true');
|
||||
el.setAttribute('ondragstart', `drag(event, ${semId}, ${idx})`);
|
||||
|
||||
// Styling for special items
|
||||
// Styling
|
||||
if(c.name.includes('KONVERSI')) el.classList.add('bg-green-50', 'border-green-200');
|
||||
|
||||
let badgeHTML = '';
|
||||
if(c.priority === 'core') {
|
||||
el.classList.add('border-l-4', 'border-l-blue-500');
|
||||
badgeHTML = '<span class="text-[9px] bg-blue-100 text-blue-700 px-1 rounded ml-1 border border-blue-200" title="Pondasi Wajib">PONDASI</span>';
|
||||
} else if(c.priority === 'support') {
|
||||
el.classList.add('bg-stone-50', 'opacity-90');
|
||||
badgeHTML = '<span class="text-[9px] bg-stone-200 text-stone-600 px-1 rounded ml-1 border border-stone-300" title="Bisa Ditunda">TUNDA?</span>';
|
||||
}
|
||||
|
||||
el.innerHTML = `
|
||||
<div class="overflow-hidden">
|
||||
<div class="text-[10px] text-stone-400 font-mono">${c.code}</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<div class="text-[10px] text-stone-400 font-mono">${c.code}</div>
|
||||
${badgeHTML}
|
||||
</div>
|
||||
<div class="text-xs font-bold text-stone-700 truncate w-32" title="${c.name}">${c.name}</div>
|
||||
</div>
|
||||
<div class="text-xs font-bold bg-stone-100 text-stone-600 px-1.5 py-0.5 rounded border border-stone-200 ml-2">${c.sks}</div>
|
||||
@@ -491,18 +513,14 @@
|
||||
}
|
||||
|
||||
function renderAll() {
|
||||
// Render 1-4
|
||||
renderSem(1, 'container-sem-1');
|
||||
renderSem(2, 'container-sem-2');
|
||||
renderSem(3, 'container-sem-3');
|
||||
renderSem(4, 'container-sem-4');
|
||||
|
||||
// Render 5-8 (Inside Specialist Section)
|
||||
const track = tracksBlueprint[currentTrack];
|
||||
const contentDiv = document.getElementById('specialist-content');
|
||||
|
||||
// We recreate the grid structure for 5-8 inside the content div
|
||||
// This is a bit brute force but keeps logic simple
|
||||
contentDiv.innerHTML = `
|
||||
<div class="mb-4">
|
||||
<h3 class="text-xl font-bold ${track.color}">${track.name}</h3>
|
||||
@@ -510,7 +528,6 @@
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div id="container-sem-5"></div>
|
||||
<!-- MBKM Slot Visual -->
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="bg-gradient-to-br from-green-50 to-emerald-100 rounded-xl border border-green-200 p-3 shadow-sm h-auto relative">
|
||||
<div class="absolute top-0 right-0 bg-green-200 text-green-800 text-[9px] font-bold px-2 py-1 rounded-bl">LIBURAN</div>
|
||||
@@ -518,7 +535,10 @@
|
||||
<div class="space-y-2">
|
||||
${mbkmData.map(c => `
|
||||
<div class="bg-white/60 p-2 rounded border border-green-100 flex justify-between items-center">
|
||||
<span class="text-[10px] font-bold text-green-800">${c.name}</span>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[10px] font-bold text-green-800">${c.name}</span>
|
||||
<span class="text-[8px] text-green-600">${c.priority === 'core' ? 'Wajib' : 'Opsional'}</span>
|
||||
</div>
|
||||
<span class="text-[9px] bg-green-200 text-green-800 px-1 rounded">${c.sks}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
@@ -546,30 +566,85 @@
|
||||
renderAll();
|
||||
}
|
||||
|
||||
// --- DRAG & DROP ---
|
||||
// --- DRAG & DROP WITH PARITY CHECK ---
|
||||
let draggedItem = null;
|
||||
|
||||
function drag(ev, semId, idx) {
|
||||
draggedItem = { semId, idx };
|
||||
ev.dataTransfer.effectAllowed = "move";
|
||||
ev.target.style.opacity = '0.4';
|
||||
|
||||
// Set global parity state for visual feedback
|
||||
const isOddSource = semId % 2 !== 0;
|
||||
document.body.classList.add(isOddSource ? 'dragging-odd' : 'dragging-even');
|
||||
|
||||
// Visual Update: Dim invalid targets
|
||||
document.querySelectorAll('.semester-list').forEach(el => {
|
||||
const targetIsOdd = el.dataset.parity === 'odd';
|
||||
if(isOddSource !== targetIsOdd) {
|
||||
el.classList.add('invalid-target');
|
||||
} else {
|
||||
el.classList.add('valid-target-hint'); // Optional hint style
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function dragEndCleanup() {
|
||||
document.body.classList.remove('dragging-odd', 'dragging-even');
|
||||
document.querySelectorAll('.semester-list').forEach(el => {
|
||||
el.classList.remove('drag-over', 'valid-target', 'invalid-target', 'valid-target-hint');
|
||||
});
|
||||
if(draggedItem) {
|
||||
// Reset opacity if drag failed/cancelled
|
||||
// Ideally found by element ref, but re-render fixes it anyway
|
||||
}
|
||||
draggedItem = null;
|
||||
}
|
||||
|
||||
function allowDrop(ev) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
function allowDrop(ev) { ev.preventDefault(); }
|
||||
function dragEnter(ev) {
|
||||
ev.preventDefault();
|
||||
ev.currentTarget.classList.add('drag-over');
|
||||
const target = ev.currentTarget;
|
||||
|
||||
// Check parity match visual
|
||||
if(!draggedItem) return;
|
||||
const sourceParity = draggedItem.semId % 2 !== 0 ? 'odd' : 'even';
|
||||
const targetParity = target.dataset.parity;
|
||||
|
||||
if(sourceParity === targetParity) {
|
||||
target.classList.add('valid-target');
|
||||
}
|
||||
}
|
||||
|
||||
function dragLeave(ev) {
|
||||
ev.currentTarget.classList.remove('drag-over');
|
||||
ev.currentTarget.classList.remove('valid-target');
|
||||
}
|
||||
|
||||
function drop(ev, targetSem) {
|
||||
ev.preventDefault();
|
||||
document.querySelectorAll('.semester-list').forEach(el => el.classList.remove('drag-over'));
|
||||
|
||||
if (!draggedItem) return;
|
||||
if (!draggedItem) {
|
||||
dragEndCleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
const { semId, idx } = draggedItem;
|
||||
|
||||
// --- VALIDATION: PARITY CHECK ---
|
||||
const sourceParity = semId % 2;
|
||||
const targetParity = targetSem % 2;
|
||||
|
||||
if (sourceParity !== targetParity) {
|
||||
// Invalid Drop
|
||||
// alert("Mata kuliah Ganjil hanya bisa diambil di Semester Ganjil, dan Genap di Genap.");
|
||||
dragEndCleanup();
|
||||
renderAll(); // Reset visual state
|
||||
return;
|
||||
}
|
||||
// --------------------------------
|
||||
|
||||
if (semId !== targetSem) {
|
||||
// Move logic
|
||||
@@ -577,12 +652,16 @@
|
||||
currentPlan[semId].splice(idx, 1);
|
||||
currentPlan[targetSem].push(course);
|
||||
|
||||
renderAll(); // Re-calc status
|
||||
dragEndCleanup();
|
||||
renderAll();
|
||||
} else {
|
||||
renderAll(); // Just reset opacity
|
||||
dragEndCleanup();
|
||||
renderAll();
|
||||
}
|
||||
draggedItem = null;
|
||||
}
|
||||
|
||||
// Add global dragend listener to ensure cleanup happens even if dropped outside
|
||||
document.addEventListener('dragend', dragEndCleanup);
|
||||
|
||||
// --- CHARTS ---
|
||||
let sksChart = null;
|
||||
|
||||
Reference in New Issue
Block a user