diff --git a/frontend/src/views/Curriculum/CurriculumAdmin.vue b/frontend/src/views/Curriculum/CurriculumAdmin.vue index 405c89f0cc8c79c62ad63d1112263344256e84e2..ce60330848d342c7336ed2bc601afea589ce4293 100644 --- a/frontend/src/views/Curriculum/CurriculumAdmin.vue +++ b/frontend/src/views/Curriculum/CurriculumAdmin.vue @@ -1,22 +1,38 @@ <template> <div class="row m-0 bg-primary-six"> + <div + v-if="successUpdateCourse" + class="alert {{ alertType }} border-0 rounded alert-dismissible fade show text-center" + role="alert" + :class="{ + 'alert-success': isAlertSuccess, + 'alert-danger': isAlertDanger + }" + > + {{ alertText }} + <div + type="button" + class="btn-close" + @click="successUpdateCourse = false" + ></div> + </div> <div class="col-auto p-0"> <sidebar /> </div> <div class="col p-4 container-curriculum-admin"> - <div class="row"> - <div class="col-md-10"> + <div class="d-flex flex-row justify-content-between"> + <div class="p-2"> <h1 data-aos="fade-down" data-aos-duration="1500" class="mb-4"> Kurikulum </h1> </div> - <div class="col-md-2"> + <div class="p-2"> <button type="button" - class="btn btn-primary-six" - data-aos="fade-down" - data-aos-duration="1500" - @click="manageCurriculumButton" + :class=" + allowManageCurriculum ? 'btn-primary-six' : 'btn-disabled-six' + " + @click="allowManageCurriculum ? manageCurriculumButton() : null" > Sunting Kurikulum </button> @@ -44,89 +60,80 @@ class="col-auto d-flex align-items-center justify-content-end" > <b>Filter by</b> - </div> - <!-- Filter by Fakultas --> - <div class="col-auto dropdown"> - <button - class="btn btn-dropdown-six dropdown-toggle" - type="button" - id="dropdownMenuButton" - data-toggle="dropdown" - aria-haspopup="true" - aria-expanded="false" - > - Fakultas - </button> - <div - class="dropdown-menu" - aria-labelledby="dropdownMenuButton" - > - <a class="dropdown-item" href="#">Action</a> - <a class="dropdown-item" href="#">Another action</a> - <a class="dropdown-item" href="#">Something else here</a> + <!-- Filter by Fakultas --> + <div class="col-auto dropdown"> + <select + class="btn btn-dropdown-six dropdown-toggle" + aria-label="Fakultas" + @change="updateCurrentFacultyFilter($event.target.value)" + > + <option value="all" selected>Fakultas</option> + <option + v-for="faculty in faculties" + :key="faculty.id" + :value="faculty.id" + > + {{ faculty.shortName }} + </option> + </select> </div> - </div> - <!-- Filter by Jurusan --> - <div class="col-auto px-0 dropdown"> - <button - class="btn btn-dropdown-six dropdown-toggle" - type="button" - id="dropdownMenuButton" - data-toggle="dropdown" - aria-haspopup="true" - aria-expanded="false" - > - Jurusan - </button> - <div - class="dropdown-menu" - aria-labelledby="dropdownMenuButton" - > - <a class="dropdown-item" href="#">Action</a> - <a class="dropdown-item" href="#">Another action</a> - <a class="dropdown-item" href="#">Something else here</a> + <!-- Filter by Jurusan --> + <div class="col-auto px-0 dropdown"> + <select + class="btn btn-dropdown-six dropdown-toggle" + aria-label="Jurusan" + @change="updateCurrentMajorFilter($event.target.value)" + id="filter-prodi" + > + <option value="all" selected>Semua Prodi</option> + <option + v-for="major in majors" + :key="major.id" + :value="major.id" + > + {{ major.name }} + </option> + </select> </div> - </div> - <!-- Filter by Tahun Kurikulum --> - <div class="col-auto dropdown"> - <button - class="btn btn-dropdown-six dropdown-toggle" - type="button" - id="dropdownMenuButton" - data-toggle="dropdown" - aria-haspopup="true" - aria-expanded="false" - > - Tahun Kurikulum - </button> - <div - class="dropdown-menu" - aria-labelledby="dropdownMenuButton" - > - <a class="dropdown-item" href="#">Action</a> - <a class="dropdown-item" href="#">Another action</a> - <a class="dropdown-item" href="#">Something else here</a> + <!-- Filter by Tahun Kurikulum --> + <div class="col-auto dropdown"> + <select + class="btn btn-dropdown-six dropdown-toggle" + aria-label="Jurusan" + @change="updateCurriculumYearFilter($event.target.value)" + id="filter-prodi" + > + <option value="all" selected>Semua Kurikulum</option> + <option v-for="item in curriculumYears" :key="item.id"> + {{ item }} + </option> + </select> </div> </div> <!-- Search Title --> <div class="col d-flex align-items-center justify-content-end"> <b>Cari</b> - </div> - <!-- Search Field --> - <div class="col-5"> - <form class="d-flex"> - <input - class="py-2 form-control" - type="search" - placeholder="Cari Mata Kuliah" - aria-label="Search" - /> - <button class="btn btn-primary-six px-3 py-0" type="submit"> - <span - ><i class="fa fa-search" aria-hidden="true"></i - ></span> - </button> - </form> + <div class="col-5"> + <div class="d-flex"> + <input + class="py-2 form-control" + type="search" + placeholder="Cari Mata Kuliah" + aria-label="Search" + v-model="searchQuery" + @keyup="searchCourse" + /> + <button + class="btn btn-primary-six px-3 py-0" + @click="searchCourse" + > + <span + ><i class="fa fa-search" aria-hidden="true"></i + ></span> + </button> + </div> + </div> + <!-- Search Field --> </div> </div> <!-- Table --> @@ -136,47 +143,21 @@ <th scope="col">#</th> <th scope="col">Kode</th> <th scope="col">Mata Kuliah</th> + <th scope="col" class="text-center">Tahun Kurikulum</th> <th scope="col" class="text-center">Aksi</th> </tr> </thead> <tbody> - <tr> - <th scope="row">1</th> - <td>IF1212</td> - <td>Dasar Pemrograman</td> - <td class="text-center"> - <button - type="button" - class="btn btn-secondary-six py-1 px-2" - @click="openDetailCourse" - > - Sunting - </button> - </td> - </tr> - <tr> - <th scope="row">2</th> - <td>IF1212</td> - <td>Dasar Pemrograman</td> - <td class="text-center"> - <button - type="button" - class="btn btn-secondary-six py-1 px-2" - @click="openDetailCourse" - > - Sunting - </button> - </td> - </tr> - <tr> - <th scope="row">3</th> - <td>IF1212</td> - <td>Dasar Pemrograman</td> + <tr v-for="(course, index) in showedCourses" :key="course.id"> + <th scope="row">{{ startIndex + 1 + index }}</th> + <td>{{ course.code }}</td> + <td>{{ course.name }}</td> + <td class="text-center">{{ course.curriculumYear }}</td> <td class="text-center"> <button type="button" class="btn btn-secondary-six py-1 px-2" - @click="openDetailCourse" + @click="openDetailCourse(course.id)" > Sunting </button> @@ -187,34 +168,17 @@ <!-- Pagination --> <div class="row"> <div class="col"> - <span>1-10 of 372 rows </span> - <button - class="btn btn-dropdown-six dropdown-toggle" - type="button" - id="dropdownMenuButton" - data-toggle="dropdown" - aria-haspopup="true" - aria-expanded="false" - > - 10 - </button> - <div - class="dropdown-menu" - aria-labelledby="dropdownMenuButton" - > - <a class="dropdown-item" href="#">5</a> - <a class="dropdown-item" href="#">10</a> - <a class="dropdown-item" href="#">20</a> - </div> - <span> rows per page</span> + <span> + Total: {{ filteredCoursesList.length }} mata kuliah + </span> </div> <div class="col d-flex justify-content-end"> <v-pagination v-model="page" - :pages="10" - :range-size="1" + :pages="pageNumberTotal" + :range-size="coursesPerPage" active-color="#DCEDFF" - @update:modelValue="updateHandler" + @update:modelValue="updatePage" /> </div> </div> @@ -227,7 +191,27 @@ id="openDetailCourse" tabindex="-1" role="dialog" + aria-labelledby="openDetailCourse" > + <!-- Alert Create New Course --> + <div class="row"> + <div + v-if="sentUpdateCourse" + class="alert {{ alertType }} border-0 rounded alert-dismissible fade show text-center" + role="alert" + :class="{ + 'alert-success': isAlertSuccess, + 'alert-danger': isAlertDanger + }" + > + {{ alertText }} + <div + type="button" + class="btn-close" + @click="sentUpdateCourse = false" + ></div> + </div> + </div> <div class="modal-dialog modal-dialog-centered" role="document"> <div class="modal-content p-3 "> <div class="modal-header"> @@ -246,8 +230,9 @@ <div class="col-12"> <input type="text" + disabled placeholder="Masukan Kode Mata Kuliah" - v-model="courseInfo" + v-model="updateCourseRequestBody.code" /> </div> </div> @@ -260,7 +245,7 @@ <input type="text" placeholder="Masukan Nama Mata Kuliah" - v-model="courseInfo" + v-model="updateCourseRequestBody.name" /> </div> </div> @@ -273,13 +258,12 @@ <input type="text" placeholder="Masukan SKS" - v-model="courseInfo" + v-model="updateCourseRequestBody.credits" /> </div> </div> </div> </div> - <div class="row"> <!-- second line --> <!-- course type --> @@ -290,20 +274,20 @@ <input type="text" placeholder="PILIHAN / WAJIB" - v-model="courseInfo" + v-model="updateCourseRequestBody.type" /> </div> </div> </div> <!-- course default term --> <div class="form-group col-4"> - <label><b>Semester</b></label> + <label><b>Default Semester</b></label> <div class="row"> <div class="col-12"> <input type="text" placeholder="Masukan Semester" - v-model="courseInfo" + v-model="updateCourseRequestBody.defaultSemester" /> </div> </div> @@ -314,15 +298,14 @@ <div class="row"> <div class="col-12"> <input - type="text" + disabled placeholder="Pilih Program Studi / Opsi" - v-model="courseInfo" + v-model="updateCourseRequestBody.Major.name" /> </div> </div> </div> </div> - <div class="row"> <!-- third line --> <!-- Short Syllabus --> @@ -334,7 +317,7 @@ class="mt-2" type="text" placeholder="Masukan Silabus Singkat" - v-model="courseInfo" + v-model="updateCourseRequestBody.shortSyllabus" /> </div> </div> @@ -348,7 +331,7 @@ class="mt-2" type="text" placeholder="Masukan Silabus Lengkap" - v-model="courseInfo" + v-model="updateCourseRequestBody.completeSyllabus" /> </div> </div> @@ -362,7 +345,7 @@ class="mt-2" type="text" placeholder="Masukan Luaran" - v-model="courseInfo" + v-model="updateCourseRequestBody.outcomes" /> </div> </div> @@ -375,7 +358,7 @@ <button type="button" class="btn btn-secondary-six col-12" - @click="cancelZeroCredits" + @click="cancelDetailCourse" > Batal </button> @@ -384,18 +367,10 @@ <div class="form-group col-6"> <button type="button" - class="btn col-12" - :class=" - studyPlanChoosen === 2 - ? 'btn-disabled-six' - : 'btn-primary-six' - " - @click="submitZeroCredits" - :disabled="studyPlanChoosen === 2" + class="btn btn-primary-six col-12" + @click="submitDetailCourse" > - {{ - studyPlanChoosen === 2 ? "Sudah Diajukan" : "Ajukan" - }} + Simpan </button> </div> </div> @@ -414,6 +389,7 @@ import Sidebar from "@/components/Sidebar/Sidebar"; import VPagination from "vue3-pagination"; import "vue3-pagination/dist/vue3-pagination.css"; import { Modal } from "bootstrap"; +import axios from "axios"; export default { components: { @@ -421,15 +397,411 @@ export default { VPagination }, name: "CurriculumAdmin", + data() { + return { + editClassModal: null, + isAdmin: false, + faculties: [], + majors: [], + curriculumYears: [], + coursesList: [], + filteredCoursesList: [], + showedCourses: [], + coursesPerPage: 5, + page: 1, + pageNumberTotal: 1, + startIndex: 0, + filterMajorId: "all", + filterCurriculumYear: "all", + searchQuery: null, + startYear: + new Date().getMonth() < 7 + ? new Date().getFullYear() - 1 + : new Date().getFullYear(), // startYear change if month >= august, + currentYear: + new Date().getMonth() < 7 + ? new Date().getFullYear() - 1 + : new Date().getFullYear(), + semester: new Date().getMonth() < 7 ? "2" : "1", // return 2 if january <= month <= august + //--- Alert ---- + successUpdateCourse: false, + sentUpdateCourse: false, + isAlertSuccess: true, + isAlertDanger: false, + alertText: "", + //--- Work --- + allowManageCurriculum: false, + selectedCourse: null, + updateCourseRequestBody: { + code: "", + name: "", + credits: "", + type: "", + defaultSemester: "", + shortSyllabus: "", + completeSyllabus: "", + outcomes: "", + majorId: "", + Major: { name: "" }, + curriculumYear: "" + } + }; + }, + mounted() { + this.getAllCourses(); + this.getAllFaculties(); + this.getAllMajors(); + }, methods: { - openDetailCourse() { + // Button + openDetailCourse(courseId) { + this.successUpdateCourse = false; + this.sentUpdateCourse = false; + this.getCourseById(courseId); this.detailCourseModal = new Modal( document.getElementById("openDetailCourse") ); this.detailCourseModal.show(); }, + cancelDetailCourse() { + this.sentUpdateCourse = false; + this.detailCourseModal.hide(); + }, manageCurriculumButton() { - this.$router.push("/manage-curriculum"); + this.$router.push({ + path: "/manage-curriculum", + query: { + majorId: this.filterMajorId, + curriculumYear: this.filterCurriculumYear + } + }); + }, + submitDetailCourse() { + this.sentUpdateCourse = false; + this.isAlertSuccess = true; + this.isAlertDanger = false; + if (this.updateCourseRequestBody.code === "") { + this.sentUpdateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "Kode Mata Kuliah tidak boleh kosong"; + } else if (this.updateCourseRequestBody.name === "") { + this.sentUpdateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "Nama Mata Kuliah tidak boleh kosong"; + } else if (this.updateCourseRequestBody.credits === "") { + this.sentUpdateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "SKS tidak boleh kosong"; + } else if (!this.checkCourseType(this.updateCourseRequestBody.type)) { + this.sentUpdateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + } else if ( + !this.checkCourseSemester(this.updateCourseRequestBody.defaultSemester) + ) { + this.sentUpdateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + } else if (this.updateCourseRequestBody.shortSyllabus === "") { + this.sentUpdateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "Silabus singkat tidak boleh kosong"; + } else if (this.updateCourseRequestBody.completeSyllabus === "") { + this.sentUpdateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "Silabus Lengkap tidak boleh kosong"; + } else if (this.updateCourseRequestBody.outcomes === "") { + this.sentUpdateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "Luaran tidak boleh kosong"; + } + if (!this.sentUpdateCourse) { + this.sendupdateCourseRequestBody(this.updateCourseRequestBody); + } + }, + // Error Handling + checkCourseType(courseType) { + if (courseType === "") { + this.alertText = "Tipe Mata Kuliah tidak boleh kosong"; + return false; + } else if (courseType.toUpperCase() === "PILIHAN") { + return true; + } else if (courseType.toUpperCase() === "WAJIB") { + return true; + } + this.alertText = "Tipe Mata Kuliah tidak valid"; + return false; + }, + checkCourseSemester(courseSemester) { + if (courseSemester === "") { + this.alertText = "Semester tidak boleh kosong"; + return false; + } else if (parseInt(courseSemester) < 1) { + this.alertText = "Semester tidak valid"; + return false; + } + return true; + }, + // Send Request + sendupdateCourseRequestBody(updateCourseRequestBody) { + const requestBody = { + code: updateCourseRequestBody.code, + name: updateCourseRequestBody.name, + type: updateCourseRequestBody.type.toUpperCase(), + credits: updateCourseRequestBody.credits, + defaultSemester: updateCourseRequestBody.defaultSemester, + shortSyllabus: updateCourseRequestBody.shortSyllabus, + completeSyllabus: updateCourseRequestBody.completeSyllabus, + outcomes: updateCourseRequestBody.outcomes, + curriculumYear: updateCourseRequestBody.curriculumYear + }; + axios + .put( + process.env.VUE_APP_API_ENDPOINT + + "/course/" + + this.selectedCourse.id, + requestBody + ) + .then((response) => { + if (response.status == 200) { + this.successUpdateCourse = true; + this.isAlertSuccess = true; + this.isAlertDanger = false; + this.alertText = "Mata Kuliah berhasil diperbaharui"; + this.getAllCourses(); + this.detailCourseModal.hide(); + } + }) + .catch((error) => { + this.sentUpdateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "Kode mata kuliah sudah terdaftar"; + console.error(error); + }); + }, + // Get Methods + async getAllCourses() { + await axios + .get(process.env.VUE_APP_API_ENDPOINT + "/course") + .then((response) => { + if (response.status == 200) { + this.coursesList.splice(0, this.coursesList.length); + this.coursesList = response.data; + this.getCurriculumYears(response.data); + this.filteredCoursesList = response.data; + this.pageNumberTotal = Math.ceil( + this.filteredCoursesList.length / this.coursesPerPage + ); + this.updatePage(1); + this.page = 1; + } + }) + .catch((error) => { + console.error(error); + }); + }, + async getCourseById(courseId) { + await axios + .get(process.env.VUE_APP_API_ENDPOINT + "/course/" + courseId) + .then((response) => { + if (response.status == 200) { + this.selectedCourse = response.data; + this.updateCourseRequestBody = response.data; + } + }) + .catch((error) => { + console.error(error); + }); + }, + async getAllFaculties() { + await axios + .get(process.env.VUE_APP_API_ENDPOINT + "/faculty") + .then((response) => { + if (response.status == 200) { + this.faculties = response.data; + } + }) + .catch((error) => { + console.error(error); + }); + }, + async getAllMajors() { + await axios + .get(process.env.VUE_APP_API_ENDPOINT + "/major") + .then((response) => { + if (response.status == 200) { + this.majors = response.data; + this.editMajorName(); + } + }) + .catch((error) => { + console.error(error); + }); + }, + async getMajorsByFacultyId(facultyId) { + document.getElementById("filter-prodi").value = "all"; + if (facultyId === "all") { + this.getAllMajors(); + } else { + await axios + .get( + process.env.VUE_APP_API_ENDPOINT + "/major?facultyId=" + facultyId + ) + .then((response) => { + if (response.status == 200) { + console.log("test"); + this.majors = response.data; + this.editMajorName(); + } + }) + .catch((error) => { + console.error(error); + }); + } + }, + // Update Filter Methods + async updateCurrentFacultyFilter(facultyId) { + this.filteredCoursesList = []; + this.searchQuery = null; + this.filterFacultyId = facultyId; + if (this.filterMajorId !== "all" && this.filterCurriculumYear !== "all") { + this.allowManageCurriculum = true; + } else { + this.allowManageCurriculum = false; + } + this.getMajorsByFacultyId(facultyId); + this.getAllCourses(); + }, + async updateCurrentMajorFilter(majorId) { + this.searchQuery = null; + this.filterMajorId = majorId; + if (this.filterMajorId !== "all" && this.filterCurriculumYear !== "all") { + this.allowManageCurriculum = true; + } else { + this.allowManageCurriculum = false; + } + this.filterCourses(); + }, + updateCurriculumYearFilter(curriculumYear) { + this.searchQuery = null; + this.filterCurriculumYear = curriculumYear; + if (this.filterMajorId !== "all" && this.filterCurriculumYear !== "all") { + this.allowManageCurriculum = true; + } else { + this.allowManageCurriculum = false; + } + this.filterCourses(); + }, + // Filter Course + filterCourses() { + this.filteredCoursesList = []; + if (this.filterMajorId === "all" && this.filterCurriculumYear === "all") { + this.filteredCoursesList = this.coursesList; + } else if ( + this.filterMajorId === "all" && + this.filterCurriculumYear !== "all" + ) { + for (let i = 0; i < this.coursesList.length; i++) { + if ( + parseInt(this.coursesList[i].curriculumYear) == + parseInt(this.filterCurriculumYear) + ) { + this.filteredCoursesList.push(this.coursesList[i]); + } + } + } else if ( + this.filterMajorId !== "all" && + this.filterCurriculumYear === "all" + ) { + for (let i = 0; i < this.coursesList.length; i++) { + if (this.coursesList[i].majorId === this.filterMajorId) { + this.filteredCoursesList.push(this.coursesList[i]); + } + } + } else { + for (let i = 0; i < this.coursesList.length; i++) { + if ( + this.coursesList[i].majorId === this.filterMajorId && + parseInt(this.coursesList[i].curriculumYear) == + parseInt(this.filterCurriculumYear) + ) { + this.filteredCoursesList.push(this.coursesList[i]); + } + } + } + this.pageNumberTotal = Math.ceil( + this.filteredCoursesList.length / this.coursesPerPage + ); + this.updatePage(1); + this.page = 1; + }, + // Search Course + searchCourse() { + this.filteredCoursesList = []; + if (this.searchQuery) { + this.filterCourses(); + let filteredCourses = this.filteredCoursesList; + this.filteredCoursesList = []; + for (let i = 0; i < filteredCourses.length; i++) { + if ( + filteredCourses[i].code + .toUpperCase() + .includes(this.searchQuery.toUpperCase()) || + filteredCourses[i].name + .toUpperCase() + .includes(this.searchQuery.toUpperCase()) + ) { + this.filteredCoursesList.push(filteredCourses[i]); + } + } + } else { + this.filterCourses(); + } + this.pageNumberTotal = Math.ceil( + this.filteredCoursesList.length / this.coursesPerPage + ); + this.updatePage(1); + this.page = 1; + }, + // Utility Methods + getCurriculumYears(coursesList) { + for (let i = 0; i < coursesList.length; i++) { + if (!this.curriculumYears.includes(coursesList[i].curriculumYear)) { + this.curriculumYears.push(coursesList[i].curriculumYear); + } + } + if (!this.curriculumYears.includes(this.currentYear)) { + this.curriculumYears.push(this.currentYear); + } + }, + editMajorName() { + for (let i = 0; i < this.majors.length; i++) { + if (this.majors[i].name.includes("Sarjana")) { + this.majors[i].name = this.majors[i].name.replace("Sarjana", "S1"); + } else if (this.majors[i].name.includes("Magister")) { + this.majors[i].name = this.majors[i].name.replace("Magister", "S2"); + } else if (this.majors[i].name.includes("Doktor")) { + this.majors[i].name = this.majors[i].name.replace("Doktor", "S3"); + } + } + }, + updatePage(pageNumber) { + let minIndex = pageNumber * this.coursesPerPage - this.coursesPerPage; + let maxIndex = pageNumber * this.coursesPerPage; + + if (maxIndex > this.filteredCoursesList.length) { + maxIndex = this.filteredCoursesList.length; + } + + this.showedCourses = this.filteredCoursesList.slice(minIndex, maxIndex); + this.startIndex = minIndex; } } }; diff --git a/frontend/src/views/Curriculum/ManageCurriculum.vue b/frontend/src/views/Curriculum/ManageCurriculum.vue index a7bc23ef97b9feb3b66d7dfcb23665ecce232867..ef3d5bc53e16de871e75c5e0b46f2edcd74bc507 100644 --- a/frontend/src/views/Curriculum/ManageCurriculum.vue +++ b/frontend/src/views/Curriculum/ManageCurriculum.vue @@ -1,11 +1,28 @@ <template> <div class="row m-0 bg-primary-six"> + <!-- Alert Create New Course --> + <div + v-if="sentCreateCourse" + class="alert {{ alertType }} border-0 rounded alert-dismissible fade show text-center" + role="alert" + :class="{ + 'alert-success': isAlertSuccess, + 'alert-danger': isAlertDanger + }" + > + {{ alertText }} + <div + type="button" + class="btn-close" + @click="sentCreateCourse = false" + ></div> + </div> <div class="col-auto p-0"> <sidebar /> </div> <div class="col p-4 container-curriculum"> <h1 data-aos="fade-down" data-aos-duration="1500" class="mb-4"> - Kurikulum - 2019 + Kurikulum - {{ currentCurriculumYear }} </h1> <!-- Container List course --> <div class="row mt-2 container-manage-curriculum"> @@ -72,7 +89,7 @@ <input type="text" placeholder="Masukan Kode Mata Kuliah" - v-model="courseInfo" + v-model="createCourseRequestBody.code" /> </div> </div> @@ -85,7 +102,7 @@ <input type="text" placeholder="Masukan Nama Mata Kuliah" - v-model="courseInfo" + v-model="createCourseRequestBody.name" /> </div> </div> @@ -98,13 +115,12 @@ <input type="text" placeholder="Masukan SKS" - v-model="courseInfo" + v-model="createCourseRequestBody.credits" /> </div> </div> </div> </div> - <div class="row"> <!-- second line --> <!-- course type --> @@ -115,20 +131,22 @@ <input type="text" placeholder="PILIHAN / WAJIB" - v-model="courseInfo" + v-model="createCourseRequestBody.type" /> </div> </div> </div> <!-- course default term --> <div class="form-group col-4"> - <label><b>Semester</b></label> + <label><b>Default Semester</b></label> <div class="row"> <div class="col-12"> <input type="text" placeholder="Masukan Semester" - v-model="courseInfo" + v-model=" + createCourseRequestBody.defaultSemester + " /> </div> </div> @@ -139,15 +157,39 @@ <div class="row"> <div class="col-12"> <input + v-if="!isOption" type="text" + disabled placeholder="Pilih Program Studi / Opsi" - v-model="courseInfo" + v-model="currentMajor.name" /> + <!-- Upper bound --> + <div class="col-auto" v-if="isOption"> + <div class="form-group mb-0"> + <select + class="form-select" + aria-label="option" + @change=" + updateCurrentOption( + $event.target.value + ) + " + > + <option + v-for="option in options" + :key="option.id" + :value="option.id" + > + {{ option.name }} + </option> + </select> + </div> + </div> + <!-- Lower Bound --> </div> </div> </div> </div> - <div class="row"> <!-- third line --> <!-- Short Syllabus --> @@ -159,7 +201,9 @@ class="mt-2" type="text" placeholder="Masukan Silabus Singkat" - v-model="courseInfo" + v-model=" + createCourseRequestBody.shortSyllabus + " /> </div> </div> @@ -173,7 +217,9 @@ class="mt-2" type="text" placeholder="Masukan Silabus Lengkap" - v-model="courseInfo" + v-model=" + createCourseRequestBody.completeSyllabus + " /> </div> </div> @@ -187,21 +233,20 @@ class="mt-2" type="text" placeholder="Masukan Luaran" - v-model="courseInfo" + v-model="createCourseRequestBody.outcomes" /> </div> </div> </div> </div> - <div class="row"> - <div class="col-md-9"></div> - <div class="col-md-3"> + <div class="d-flex flex-row-reverse mt-3"> + <div class="p-2"> <button type="button" class="btn btn-primary-six" data-aos="fade-down" data-aos-duration="1500" - @click="ManageCurriculumButton" + @click="CreateNewCourseButton()" > Tambahkan pada Kurikulum </button> @@ -226,16 +271,18 @@ </div> <!-- Search Field --> <div class="col-5"> - <form class="d-flex"> + <div class="d-flex"> <input class="py-2 form-control" type="search" placeholder="Cari Mata Kuliah" aria-label="Search" + v-model="searchQuery" + @keyup="searchCourse" /> <button class="btn btn-primary-six px-3 py-0" - type="submit" + @click="searchCourse" > <span ><i @@ -244,7 +291,7 @@ ></i ></span> </button> - </form> + </div> </div> </div> <!-- Tabel Mata Kuliah Pilihan --> @@ -261,94 +308,72 @@ </tr> </thead> <tbody> - <tr> - <td>1</td> - <td>IF5122</td> - <td>Pembangunan Perangkat Lunak</td> - <td class="text-center">2013</td> + <tr + v-for="(item, i) of showedCourses" + :key="'course' + i" + > + <th scope="row">{{ i + 1 }}</th> + <td>{{ item.code }}</td> + <td>{{ item.name }}</td> <td class="text-center"> - <div class="custom-control custom-checkbox"> - <input - type="checkbox" - class="custom-control-input" - id="customCheck1" - /> - <label - class="custom-control-label" - for="customCheck1" - ></label> - </div> + {{ item.curriculumYear }} </td> - </tr> - <tr> - <td>2</td> - <td>IF5123</td> - <td>Kualitas Perangkat Lunak</td> - <td class="text-center">2013</td> <td class="text-center"> - <div class="custom-control custom-checkbox"> + <div + v-if=" + item.curriculumYear != + currentCurriculumYear && !item.duplicated + " + class="custom-control custom-checkbox" + > <input type="checkbox" class="custom-control-input" - id="customCheck1" + :id="'customCheck1' + i" + :checked="item.selected === true" + @click="toggleCourseSelection(item)" /> <label class="custom-control-label" - for="customCheck1" - ></label> + :for="'customCheck1' + i" + > + </label> </div> </td> </tr> </tbody> </table> - <div class="row"> - <div class="col-md-9"></div> - <div class="col-md-3"> - <button - type="button" - class="btn btn-primary-six" - data-aos="fade-down" - data-aos-duration="1500" - @click="ManageCurriculumButton" - > - Tambahkan pada Kurikulum - </button> - </div> - </div> <!-- Pagination --> <div class="row"> <div class="col"> - <span>1-10 of 372 rows </span> - <button - class="btn btn-dropdown-six dropdown-toggle" - type="button" - id="dropdownMenuButton" - data-toggle="dropdown" - aria-haspopup="true" - aria-expanded="false" - > - 10 - </button> - <div - class="dropdown-menu" - aria-labelledby="dropdownMenuButton" - > - <a class="dropdown-item" href="#">5</a> - <a class="dropdown-item" href="#">10</a> - <a class="dropdown-item" href="#">20</a> - </div> - <span> rows per page</span> + <span> + Total: {{ coursesList.length }} mata kuliah + </span> </div> <div class="col d-flex justify-content-end"> <v-pagination v-model="page" - :pages="10" - :range-size="1" + :pages="pageNumberTotal" + :range-size="coursesPerPage" active-color="#DCEDFF" - @update:modelValue="updateHandler" + @update:modelValue="updatePage" /> </div> </div> + <!-- Confirmation button --> + <div class="d-flex flex-row-reverse mt-3"> + <div class="p-2"> + <button + type="button" + class="btn btn-primary-six" + data-aos="fade-down" + data-aos-duration="1500" + @click="importPreviousCourseButton()" + > + Tambahkan pada Kurikulum + </button> + </div> + </div> </div> </div> </div> @@ -366,10 +391,423 @@ import Sidebar from "@/components/Sidebar/Sidebar"; import VPagination from "vue3-pagination"; import "vue3-pagination/dist/vue3-pagination.css"; +import axios from "axios"; export default { components: { Sidebar, VPagination }, - name: "ManageCurriculum" + name: "ManageCurriculum", + data() { + return { + currentCurriculumYear: "", + currentMajorId: "", + currentMajor: { name: "" }, + majors: [], + options: [], + coursesList: [], + filteredCoursesList: [], + showedCourses: [], + importCoursesList: [], + coursesPerPage: 5, + page: 1, + pageNumberTotal: 1, + startIndex: 0, + filterFacultyId: "all", + filterMajorId: "all", + searchQuery: null, + keywordProdi: "", + startYear: + new Date().getMonth() < 7 + ? new Date().getFullYear() - 1 + : new Date().getFullYear(), // startYear change if month >= august, + currentYear: + new Date().getMonth() < 7 + ? new Date().getFullYear() - 1 + : new Date().getFullYear(), + semester: new Date().getMonth() < 7 ? "2" : "1", // return 2 if january <= month <= august + //--- Alert ---- + sentCreateCourse: false, + isAlertSuccess: true, + isAlertDanger: false, + alertText: "", + //--- Create Course Request Body --- + isOption: false, + currentOptionId: null, + createCourseRequestBody: { + code: "", + name: "", + type: "", + credits: "", + defaultSemester: "", + shortSyllabus: "", + completeSyllabus: "", + outcomes: "", + curriculumYear: "", + majorId: "", + majorName: "" + } + }; + }, + mounted() { + if (this.$route.query.majorId) { + this.currentMajorId = this.$route.query.majorId; + } + if (this.$route.query.curriculumYear) { + this.currentCurriculumYear = this.$route.query.curriculumYear; + } + this.getIsS1Major(); + this.getCoursesByMajorId(this.currentMajorId); + }, + methods: { + // Button + importPreviousCourseButton() { + for (let i = 0; i < this.importCoursesList.length; i++) { + let requestBody = { + previousId: this.importCoursesList[i].id, + curriculumYear: this.currentCurriculumYear + }; + axios + .post(process.env.VUE_APP_API_ENDPOINT + "/course", requestBody) + .then((response) => { + if (response.status == 200) { + this.sentCreateCourse = true; + this.isAlertSuccess = true; + this.isAlertDanger = false; + this.alertText = "Mata Kuliah berhasil ditambahkan"; + this.importCoursesList.splice(0, this.importCoursesList.length); + this.getCoursesByMajorId(this.currentMajorId); + } + }) + .catch((error) => { + console.error(error); + this.sentCreateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "Mata Kuliah gagal ditambahkan"; + }); + } + this.importCoursesList.splice(0, this.importCoursesList.length); + this.getCoursesByMajorId(this.currentMajorId); + }, + CreateNewCourseButton() { + this.createCourseRequestBody.curriculumYear = this.currentCurriculumYear; + if (this.createCourseRequestBody.code === "") { + this.sentCreateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "Kode Mata Kuliah tidak boleh kosong"; + } else if (this.createCourseRequestBody.name === "") { + this.sentCreateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "Nama Mata Kuliah tidak boleh kosong"; + } else if (this.createCourseRequestBody.credits === "") { + this.sentCreateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "SKS tidak boleh kosong"; + } else if (!this.checkCourseType(this.createCourseRequestBody.type)) { + this.sentCreateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + } else if ( + !this.checkCourseSemester(this.createCourseRequestBody.defaultSemester) + ) { + this.sentCreateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + } else if (this.createCourseRequestBody.shortSyllabus === "") { + this.sentCreateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "Silabus singkat tidak boleh kosong"; + } else if (this.createCourseRequestBody.completeSyllabus === "") { + this.sentCreateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "Silabus Lengkap tidak boleh kosong"; + } else if (this.createCourseRequestBody.outcomes === "") { + this.sentCreateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "Luaran tidak boleh kosong"; + } + if (!this.sentCreateCourse) { + this.sendCreateCourseRequestBody(this.createCourseRequestBody); + } + }, + updateCurrentOption(newOption) { + this.currentOptionId = newOption; + }, + // Send Request + sendCreateCourseRequestBody(createCourseRequestBody) { + var requestBody = {}; + if (this.isOption) { + requestBody = { + code: createCourseRequestBody.code, + name: createCourseRequestBody.name, + type: createCourseRequestBody.type.toUpperCase(), + credits: createCourseRequestBody.credits, + defaultSemester: createCourseRequestBody.defaultSemester, + shortSyllabus: createCourseRequestBody.shortSyllabus, + completeSyllabus: createCourseRequestBody.completeSyllabus, + outcomes: createCourseRequestBody.outcomes, + curriculumYear: createCourseRequestBody.curriculumYear, + optionId: this.currentOptionId + }; + } else { + requestBody = { + code: createCourseRequestBody.code, + name: createCourseRequestBody.name, + type: createCourseRequestBody.type.toUpperCase(), + credits: createCourseRequestBody.credits, + defaultSemester: createCourseRequestBody.defaultSemester, + shortSyllabus: createCourseRequestBody.shortSyllabus, + completeSyllabus: createCourseRequestBody.completeSyllabus, + outcomes: createCourseRequestBody.outcomes, + curriculumYear: createCourseRequestBody.curriculumYear, + majorId: this.currentMajor.id + }; + } + console.log(requestBody); + axios + .post(process.env.VUE_APP_API_ENDPOINT + "/course", requestBody) + .then((response) => { + if (response.status == 200) { + this.sentCreateCourse = true; + this.isAlertSuccess = true; + this.isAlertDanger = false; + this.alertText = "Mata Kuliah berhasil ditambahkan"; + this.resetCreateCourseRequestBody(); + this.getCoursesByMajorId(this.currentMajorId); + this.importCoursesList.splice(0, this.importCoursesList.length); + } + }) + .catch((error) => { + console.error(error); + this.sentCreateCourse = true; + this.isAlertSuccess = false; + this.isAlertDanger = true; + this.alertText = "Mata Kuliah gagal ditambahkan"; + }); + }, + // Error Handling + checkCourseType(courseType) { + if (courseType === "") { + this.alertText = "Tipe Mata Kuliah tidak boleh kosong"; + return false; + } else if (courseType.toUpperCase() === "PILIHAN") { + return true; + } else if (courseType.toUpperCase() === "WAJIB") { + return true; + } + this.alertText = "Tipe Mata Kuliah tidak valid"; + return false; + }, + checkCourseSemester(courseSemester) { + if (courseSemester === "") { + this.alertText = "Semester tidak boleh kosong"; + return false; + } else if (parseInt(courseSemester) < 1) { + this.alertText = "Semester tidak valid"; + return false; + } + return true; + }, + checkForDuplicates(selectedCourse) { + let count = 0; + for (let i = 0; i < this.coursesList.length; i++) { + if ( + selectedCourse.code === this.coursesList[i].code && + this.coursesList[i].curriculumYear == this.currentCurriculumYear + ) { + count++; + } + } + if (count > 0) { + return true; + } else { + return false; + } + }, + // Get Methods + async getCoursesByMajorId(majorId) { + await axios + .get(process.env.VUE_APP_API_ENDPOINT + "/course/?majorId=" + majorId) + .then((response) => { + if (response.status == 200) { + this.coursesList = response.data; + this.filteredCoursesList = this.coursesList; + for (let i = 0; i < this.coursesList.length; i++) { + this.coursesList[i].duplicated = this.checkForDuplicates( + this.coursesList[i] + ); + } + this.pageNumberTotal = Math.ceil( + this.coursesList.length / this.coursesPerPage + ); + this.updatePage(1); + } + }) + .catch((error) => { + console.error(error); + }); + }, + async getIsS1Major() { + await axios + .get(process.env.VUE_APP_API_ENDPOINT + "/option") + .then((response) => { + if (response.status == 200) { + for (let i = 0; i < response.data.length; i++) { + if (response.data[i].majorId === this.currentMajorId) { + this.isOption = true; + } + } + } + }) + .catch((error) => { + console.error(error); + }); + if (this.isOption) { + this.getAllOptions(); + } else { + this.getCurrentMajor(); + } + }, + async getAllOptions() { + await axios + .get(process.env.VUE_APP_API_ENDPOINT + "/option") + .then((response) => { + if (response.status == 200) { + this.options = response.data; + this.currentOptionId = this.options[0].id; + } + }) + .catch((error) => { + console.error(error); + }); + }, + async getCurrentMajor() { + await axios + .get(process.env.VUE_APP_API_ENDPOINT + "/major") + .then((response) => { + if (response.status == 200) { + for (let i = 0; i < response.data.length; i++) { + if (response.data[i].id === this.currentMajorId) { + this.currentMajor = response.data[i]; + } + } + } + }) + .catch((error) => { + console.error(error); + }); + }, + // Utility Methods + editMajorName() { + for (let i = 0; i < this.majors.length; i++) { + if (this.majors[i].name.includes("S1")) { + this.majors[i].name = this.majors[i].name.replace("S1", "Sarjana"); + } else if (this.majors[i].name.includes("S2")) { + this.majors[i].name = this.majors[i].name.replace("S2", "Magister"); + } else if (this.majors[i].name.includes("S3")) { + this.majors[i].name = this.majors[i].name.replace("S3", "Doktor"); + } + } + }, + toggleCourseSelection(selectedCourse) { + console.log(selectedCourse.duplicated); + selectedCourse.selected = !selectedCourse.selected; + if (selectedCourse.selected) { + this.importCoursesList.push(selectedCourse); + } else { + this.importCoursesList.pop(selectedCourse); + } + }, + filterCourses(majorId) { + this.searchQuery = null; + this.filterMajorId = majorId; + this.filteredCoursesList = []; + if (majorId === "all") { + this.filteredCoursesList = this.coursesList; + } else { + for (let i = 0; i < this.coursesList.length; i++) { + if (this.coursesList[i].majorId === majorId) { + this.filteredCoursesList.push(this.coursesList[i]); + } + } + } + this.pageNumberTotal = Math.ceil( + this.filteredCoursesList.length / this.coursesPerPage + ); + this.updatePage(1); + this.page = 1; + }, + searchCourse() { + this.filteredCoursesList = []; + if (this.searchQuery) { + for (let i = 0; i < this.coursesList.length; i++) { + if (this.filterMajorId === "all") { + if ( + this.coursesList[i].code + .toUpperCase() + .includes(this.searchQuery.toUpperCase()) || + this.coursesList[i].name + .toUpperCase() + .includes(this.searchQuery.toUpperCase()) + ) { + this.filteredCoursesList.push(this.coursesList[i]); + } + } else { + if ( + this.coursesList[i].code + .toUpperCase() + .includes(this.searchQuery.toUpperCase()) || + this.coursesList[i].name + .toUpperCase() + .includes(this.searchQuery.toUpperCase()) + ) { + if (this.coursesList[i].majorId === this.filterMajorId) { + this.filteredCoursesList.push(this.coursesList[i]); + } + } + } + } + } else { + this.filterCourses(this.filterMajorId); + } + this.pageNumberTotal = Math.ceil( + this.filteredCoursesList.length / this.coursesPerPage + ); + this.updatePage(1); + this.page = 1; + }, + resetCreateCourseRequestBody() { + this.createCourseRequestBody = { + code: "", + name: "", + type: "", + credits: "", + defaultSemester: "", + shortSyllabus: "", + completeSyllabus: "", + outcomes: "", + curriculumYear: "", + majorId: "", + majorName: "" + }; + }, + updatePage(pageNumber) { + let minIndex = pageNumber * this.coursesPerPage - this.coursesPerPage; + let maxIndex = pageNumber * this.coursesPerPage; + + if (maxIndex > this.filteredCoursesList.length) { + maxIndex = this.filteredCoursesList.length; + } + + this.showedCourses = this.filteredCoursesList.slice(minIndex, maxIndex); + this.startIndex = minIndex; + } + } }; </script>