From 5cc0a00a4bba20ea7768863364c1df311cbcd60f Mon Sep 17 00:00:00 2001 From: Dimas Atmodjo Date: Thu, 5 Dec 2024 15:33:24 +0700 Subject: [PATCH] adding teacher bulk student register --- src/assets/styles/teacher.css | 9 +- .../manage_students/hooks/useStudents.jsx | 236 +++++++++ .../services/serviceStudents.jsx | 87 ++++ .../manage_students/views/ManageStudents.jsx | 479 ++++++++++++++++++ 4 files changed, 810 insertions(+), 1 deletion(-) create mode 100644 src/roles/teacher/manage_students/hooks/useStudents.jsx create mode 100644 src/roles/teacher/manage_students/services/serviceStudents.jsx create mode 100644 src/roles/teacher/manage_students/views/ManageStudents.jsx diff --git a/src/assets/styles/teacher.css b/src/assets/styles/teacher.css index dd258d6..d33e766 100644 --- a/src/assets/styles/teacher.css +++ b/src/assets/styles/teacher.css @@ -42,7 +42,7 @@ height: 30px; position: absolute; top: calc(50% - 30px); - left: calc(100% - 15px); + left: calc(100% - 12px); padding: 0; } @@ -112,6 +112,13 @@ +.teacher-main-layout{ + height: calc(100vh - 58px); + overflow-y: auto; +} + + + .modal-admin .modal-header, .modal-admin .modal-body{ diff --git a/src/roles/teacher/manage_students/hooks/useStudents.jsx b/src/roles/teacher/manage_students/hooks/useStudents.jsx new file mode 100644 index 0000000..a674a85 --- /dev/null +++ b/src/roles/teacher/manage_students/hooks/useStudents.jsx @@ -0,0 +1,236 @@ +import { useState, useEffect } from 'react'; +import studentService from '../services/serviceStudents'; + +const useStudents = () => { + const [students, setStudents] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [fileImport, setFileImport] = useState(null); + + const [selectedStudent, setSelectedStudent] = useState(null); + const [formData, setFormData] = useState({ + fullName: '', + nisn: '', + email: '', + pass: '', + confirm_pass: '', + }); + + const [show, setShow] = useState(false); + + const [showLoader, setShowLoader] = useState(false); + const [loaderState, setLoaderState] = useState({ loading: false, successMessage: '', title: '', description: '', confirmAction: false }); + + const resetForm = () =>{ + setFormData({ + fullName: '', + nisn: '', + email: '', + pass: '', + confirm_pass: '', + }); + } + + const resetImportFile = () => {setFileImport(null)} + + const createStudent = async (data) => { + try { + const newStudent ={ + NAME_USERS: data.fullName, + NISN: data.nisn, + }; + const createdStudent = await studentService.createData(newStudent); + setStudents((prevStudents) => [...prevStudents, createdStudent.payload]); + } catch (err) { + setError(err); + }finally{ + resetForm(); + setLoaderState(prev => ({ + ...prev, + loading: false, + successMessage: 'Your new entry has been successfully created and saved.' + })); + } + }; + + const importRegister = async () => { + const fileData = new FormData(); + const file = fileImport; + fileData.append('file', file); + console.log(file); + try { + const response = await studentService.registerImport(fileData); + console.log(response.data); + } catch (err) { + setError(err); + }finally{ + resetForm(); + setLoaderState(prev => ({ + ...prev, + loading: false, + successMessage: 'Your new entry has been successfully created and saved.' + })); + } + } + + const editStudent = async (id, data) => { + const formData = new FormData(); + formData.append('NAME_USERS', data.fullName); + formData.append('EMAIL', data.email); + formData.append('NISN', data.nisn); + try { + const student = await studentService.updateData(id, formData); + setStudents((prevStudents) => + prevStudents.map((s) => (s.ID === id ? student.payload : s)) + ); + } catch (err) { + setError(err); + }finally{ + setLoaderState(prev => ({ + ...prev, + loading: false, + successMessage: 'Your data has been successfully updated.' + })); + } + }; + + const deleteStudent = async (id) => { + handleShowLoader('Deleted', '', true) + try { + await studentService.deleteData(id); + } catch (err) { + setError(err); + }finally{ + setStudents((prevStudents) => prevStudents.filter((s) => s.ID !== id)); + setLoaderState(prev => ({ + ...prev, + loading: false, + successMessage: 'Your data has been successfully deleted.' + })); + } + }; + + const handleFormChange = (e) => { + setFormData({ ...formData, [e.target.name]: e.target.value }); + }; + + const handleFileChange = (e) => { + setFileImport(e.target.files[0]); + }; + + const handleShow = (data) => { + setSelectedStudent(data); + setFormData({ + fullName: data?.NAME_USERS || '', + nisn: data?.NISN || '', + email: data?.EMAIL || '' + }); + setShow(true); + }; + const handleClose = () => {setShow(false); resetForm(); setSelectedStudent(null)}; + + const handleCloseLoader = () => setShowLoader(false); + const handleShowLoader = (title, description, loading = false, successMessage = '', confirmAction = false) => { + setLoaderState({ title, description, loading, successMessage, confirmAction }); + setShowLoader(true); + }; + + const [search, setSearch] = useState(""); + const [sort, setSort] = useState(""); + const [page, setCurrentPage] = useState(1); + const [limit, setLimit] = useState(7); + const [totalPages, setTotalPages] = useState(0); + const [totalData, setTotalData] = useState(null); + + const fetchData = async () => { + setLoading(true); + try { + const data = await studentService.fetchData(search, sort, page, limit); + setTotalPages(data.payload.totalPages); + setTotalData(data.payload.totalStudents); + setStudents(data.payload.students); + } catch (err) { + setError(err); + } finally { + setLoading(false); + } + }; + + const handleSerachChange = () => { + fetchData(); + setCurrentPage(1); + } + + const handlePageChange = (pages) => { + setCurrentPage(pages); + } + + const handleLimitsChange = (e) => { + setLimit(e.target.value); + setCurrentPage(1); + } + + useEffect(() => { + fetchData(); + }, [page, limit]); + + + const handleDownloadTemplate = async () => {try { + const response = await studentService.getTemplate(); + + const blob = new Blob([response], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); + const downloadUrl = URL.createObjectURL(blob); + + const link = document.createElement('a'); + link.href = downloadUrl; + link.download = 'template.xls'; + link.click(); + + URL.revokeObjectURL(downloadUrl); + } catch (error) { + console.error('Error downloading file:', error); + alert('Gagal mengunduh file'); + } + } + + return { + students, + loading, + error, + selectedStudent, + formData, + + createStudent, + editStudent, + deleteStudent, + setSelectedStudent, + setFormData, + handleFormChange, + resetForm, + + show, + setShow, + handleShow, + handleClose, + showLoader, + handleShowLoader, + handleCloseLoader, + loaderState, + setLoaderState, + + totalPages, + totalData, + setSearch, + page, + handlePageChange, + handleLimitsChange, + handleSerachChange, + handleFileChange, + handleDownloadTemplate, + importRegister, + fileImport, + resetImportFile + }; +}; + +export default useStudents; diff --git a/src/roles/teacher/manage_students/services/serviceStudents.jsx b/src/roles/teacher/manage_students/services/serviceStudents.jsx new file mode 100644 index 0000000..c27a960 --- /dev/null +++ b/src/roles/teacher/manage_students/services/serviceStudents.jsx @@ -0,0 +1,87 @@ +import axiosInstance from '../../../../utils/axiosInstance'; + +const fetchData= async (search, sort, page, limit) => { + try { + const response = await axiosInstance.get(`/user/student?search=${search}&sort=${sort}&page=${page}&limit=${limit}`); + return response.data; + } catch (error) { + console.error('Error fetching sections:', error); + throw error; + } +}; + +const getStudentById = async (id) => { + try { + const response = await axiosInstance.get(`/student/${id}`); + return response.data; + } catch (error) { + console.error(`Error fetching student with ID ${id}:`, error); + throw error; + } +}; + +const createData = async (studentData) => { + try { + const response = await axiosInstance.post(`/admin/register/student`, studentData); + return response.data; + } catch (error) { + console.error('Error adding student:', error); + throw error; + } +}; + +const updateData = async (id, studentData) => { + try { + const response = await axiosInstance.put(`/user/update/${id}`, studentData); + return response.data; + } catch (error) { + console.error(`Error updating student with ID ${id}:`, error); + throw error; + } +}; + +const deleteData = async (id) => { + try { + const response = await axiosInstance.delete(`/user/delete/${id}`); + return response.data; + } catch (error) { + console.error(`Error deleting student with ID ${id}:`, error); + throw error; + } +}; + +const getTemplate = async () => { + const configs = { + headers: { + Authorization: localStorage.getItem('token') + }, + responseType: 'blob', + }; + try { + const response = await axiosInstance.get(`/user/sendExcelExample`, configs); + return response.data; + } catch (error) { + console.error(`Error get file:`, error); + throw error; + } +}; + +const registerImport = async (file) => { + try { + const response = await axiosInstance.post(`/admin/register/student/csv`, file); + return response.data; + } catch (error) { + console.error('Error adding student:', error); + throw error; + } +}; + +export default{ + fetchData, + getStudentById, + createData, + updateData, + deleteData, + getTemplate, + registerImport +}; diff --git a/src/roles/teacher/manage_students/views/ManageStudents.jsx b/src/roles/teacher/manage_students/views/ManageStudents.jsx new file mode 100644 index 0000000..0f4a02b --- /dev/null +++ b/src/roles/teacher/manage_students/views/ManageStudents.jsx @@ -0,0 +1,479 @@ +import React, { useState } from 'react'; +import { Table, Row, Col, Nav, Tab, Button, Form, InputGroup, Modal, Spinner } from 'react-bootstrap'; +import useStudents from '../hooks/useStudents'; +import ModalOperation from '../../../../components/ui/adminMessageModal/ModalOperation'; +import TablePaginate from '../../../../components/ui/TablePaginate'; + +const ManageStudents = () => { + const { + students, + loading, + error, + selectedStudent, + formData, + createStudent, + editStudent, + deleteStudent, + setSelectedStudent, + setFormData, + handleFormChange, + resetForm, + + show, + setShow, + handleShow, + handleClose, + showLoader, + handleShowLoader, + handleCloseLoader, + loaderState, + setLoaderState, + + page, + totalData, + totalPages, + setSearch, + handlePageChange, + handleLimitsChange, + handleSerachChange, + handleFileChange, + handleDownloadTemplate, + importRegister, + fileImport, + resetImportFile + } = useStudents(); + + const handleCreate = async (e) => { + handleShowLoader('Created', '', true) + e.preventDefault(); + await createStudent(formData); + }; + + const handleImport = async (e) => { + handleShowLoader('Created', '', true) + e.preventDefault(); + await importRegister(); + }; + + const handleUpdate = async (e) => { + handleClose(); + handleShowLoader('Updated', '', true) + e.preventDefault(); + await editStudent(selectedStudent.ID, formData); + }; + + const handleDelete = async (id) => { + handleShowLoader('Deleted', 'Are you sure you want to delete this student?', false, '', true); + const confirmDelete = async () => { + handleCloseLoader(); + await deleteStudent(id); + } + setLoaderState(prev => ({ ...prev, handleConfirm: confirmDelete })); + } + + const handleDragOver = (e) => { + e.preventDefault(); // Mencegah browser membuka file + e.stopPropagation(); + }; + + const handleDrop = (e) => { + e.preventDefault(); + e.stopPropagation(); + + const droppedFiles = e.dataTransfer.files; // Ambil file dari drag event + if (droppedFiles.length) { + // Kirim file ke input + handleFileChange({ target: { files: droppedFiles } }); + } + }; + + const handleDragEnter = (e) => { + e.preventDefault(); + e.stopPropagation(); + e.target.classList.add('dragging'); + }; + + const handleDragLeave = (e) => { + e.preventDefault(); + e.stopPropagation(); + e.target.classList.remove('dragging'); + }; + + if (error) { + <>{error} + } + + return ( +
+

Manage your students to classes!

+

Easily add students to their classes and organize them efficiently.

+ + + + + + + + + + +
+
+

Student List

+
+
+
{ e.preventDefault(); handleSerachChange(); }}> + { setSearch(e.target.value); }} + /> + + + + + + + + + + + + + + {loading?( + + + + ):( + students.length > 0?( + students.map((student, index) => ( + + + + + + + + )) + ):( + + + + ) + )} + +
NoNISNFull NameEmail AddressAction
+ + + + + + +
{index + 1}{student.NISN}{student.NAME_USERS}{student.EMAIL} + + +
+

Empty Data

+
+
+
+ Item per page + + + + + + of {totalData} +
+ +
+
+
+
+ +
+
+

Add Student Data

+
+
+
+ + + NISN* + + + + + + + + Full Name* + + + + + + + + {/* + + Role* + + + + + + + + Full Name* + + + + + + + + + NISN* + + + + + + + + Email Address* + + + + + + + + + Password* + + + + + + + + Confirm Password* + + + + + + */} +
+ + +
+
+
+
+ +
+
+

Add Student Data via Excel

+ {handleDownloadTemplate()}}>Download Template Excel +
+
+
+ + + + +
+ + +
+
+
+
+
+
+ +
+
+ + + + Edit Student + + +
+ + Role* + + + + + + + Full Name* + + + + + + + NISN* + + + + + + + Email Address* + + + + + +
+ +
+
+
+
+ + +
+ ); +}; + +export default ManageStudents;