adding teacher bulk student register
This commit is contained in:
parent
1c0c2ba39c
commit
5cc0a00a4b
|
|
@ -42,7 +42,7 @@
|
||||||
height: 30px;
|
height: 30px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(50% - 30px);
|
top: calc(50% - 30px);
|
||||||
left: calc(100% - 15px);
|
left: calc(100% - 12px);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,6 +112,13 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.teacher-main-layout{
|
||||||
|
height: calc(100vh - 58px);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.modal-admin .modal-header,
|
.modal-admin .modal-header,
|
||||||
.modal-admin .modal-body{
|
.modal-admin .modal-body{
|
||||||
|
|
|
||||||
236
src/roles/teacher/manage_students/hooks/useStudents.jsx
Normal file
236
src/roles/teacher/manage_students/hooks/useStudents.jsx
Normal file
|
|
@ -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;
|
||||||
|
|
@ -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
|
||||||
|
};
|
||||||
479
src/roles/teacher/manage_students/views/ManageStudents.jsx
Normal file
479
src/roles/teacher/manage_students/views/ManageStudents.jsx
Normal file
|
|
@ -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 (
|
||||||
|
<div className='admin-students'>
|
||||||
|
<h2 className='page-title'>Manage your students to classes!</h2>
|
||||||
|
<p className='page-desc'>Easily add students to their classes and organize them efficiently.</p>
|
||||||
|
<Tab.Container id="left-tabs-example" defaultActiveKey="detail" onSelect={resetForm}>
|
||||||
|
<Row className='mb-45'>
|
||||||
|
<Col xs={12}>
|
||||||
|
<Nav variant="pills" className='col-tabs'>
|
||||||
|
<Nav.Item>
|
||||||
|
<Nav.Link eventKey="detail">View Entries</Nav.Link>
|
||||||
|
</Nav.Item>
|
||||||
|
<Nav.Item>
|
||||||
|
<Nav.Link eventKey="create">Create Data</Nav.Link>
|
||||||
|
</Nav.Item>
|
||||||
|
</Nav>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row className='mb-45'>
|
||||||
|
<Col xs={12} className='col-tabs-content'>
|
||||||
|
<Tab.Content>
|
||||||
|
<Tab.Pane eventKey="detail">
|
||||||
|
<div className="cards">
|
||||||
|
<div className="cards-title">
|
||||||
|
<h4>Student List</h4>
|
||||||
|
</div>
|
||||||
|
<div className="cards-body">
|
||||||
|
<Form className="mb-3 d-flex align-items-strech" onSubmit={(e) => { e.preventDefault(); handleSerachChange(); }}>
|
||||||
|
<Form.Control type='search'
|
||||||
|
aria-label="Large"
|
||||||
|
aria-describedby="inputGroup-sizing-sm"
|
||||||
|
placeholder='Search'
|
||||||
|
className='table-input-search mb-0 me-2 rounded-3'
|
||||||
|
onChange={(e) => { setSearch(e.target.value); }}
|
||||||
|
/>
|
||||||
|
<Button type='submit' variant='blue rounded-3'>Search</Button>
|
||||||
|
</Form>
|
||||||
|
<Table hover>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>No</th>
|
||||||
|
<th>NISN</th>
|
||||||
|
<th>Full Name</th>
|
||||||
|
<th>Email Address</th>
|
||||||
|
<th className='text-center'>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{loading?(
|
||||||
|
<tr>
|
||||||
|
<td colSpan={5} style={{height:"20vh"}}>
|
||||||
|
<Spinner animation="grow" variant="primary" />
|
||||||
|
<Spinner animation="grow" variant="secondary" />
|
||||||
|
<Spinner animation="grow" variant="success" />
|
||||||
|
<Spinner animation="grow" variant="danger" />
|
||||||
|
<Spinner animation="grow" variant="warning" />
|
||||||
|
<Spinner animation="grow" variant="info" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
):(
|
||||||
|
students.length > 0?(
|
||||||
|
students.map((student, index) => (
|
||||||
|
<tr key={student.ID}>
|
||||||
|
<td>{index + 1}</td>
|
||||||
|
<td>{student.NISN}</td>
|
||||||
|
<td>{student.NAME_USERS}</td>
|
||||||
|
<td>{student.EMAIL}</td>
|
||||||
|
<td className='text-center action-col d-flex justify-content-center'>
|
||||||
|
<Button size='sm' className='btn-edit' onClick={() => handleShow(student)}>
|
||||||
|
<i className="bi bi-pencil-square"></i>
|
||||||
|
</Button>
|
||||||
|
<Button size='sm' className='btn-delete' onClick={() => handleDelete(student.ID)}>
|
||||||
|
<i className="bi bi-trash3"></i>
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
):(
|
||||||
|
<tr>
|
||||||
|
<td colSpan={5} style={{height:'20vh'}}>
|
||||||
|
<h3>Empty Data</h3>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</Table>
|
||||||
|
<div className="mt-2 w-100 d-flex justify-content-between align-items-center">
|
||||||
|
<div className='d-flex align-items-center'>
|
||||||
|
<small className="me-2">Item per page</small>
|
||||||
|
<Form.Select
|
||||||
|
size='sm'
|
||||||
|
className='py-0 px-1 me-2'
|
||||||
|
aria-label="Default select example"
|
||||||
|
defaultValue='7'
|
||||||
|
onChange={handleLimitsChange}
|
||||||
|
style={{ width: '50px' }}
|
||||||
|
>
|
||||||
|
<option value="7">7</option>
|
||||||
|
<option value="10">10</option>
|
||||||
|
<option value="20">20</option>
|
||||||
|
</Form.Select>
|
||||||
|
<small>of {totalData}</small>
|
||||||
|
</div>
|
||||||
|
<TablePaginate
|
||||||
|
totalPages={totalPages}
|
||||||
|
currentPage={page}
|
||||||
|
onPageChange={handlePageChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tab.Pane>
|
||||||
|
<Tab.Pane eventKey="create">
|
||||||
|
<div className="cards mb-4">
|
||||||
|
<div className="cards-title">
|
||||||
|
<h4>Add Student Data</h4>
|
||||||
|
</div>
|
||||||
|
<div className="cards-body">
|
||||||
|
<Form onSubmit={handleCreate}>
|
||||||
|
<Row className="mb-2">
|
||||||
|
<Form.Group as={Col} controlId="formGridNISN">
|
||||||
|
<Form.Label>NISN<sup className='text-red fw-bold'>*</sup></Form.Label>
|
||||||
|
<InputGroup className="mb-2 input-group-icon">
|
||||||
|
<InputGroup.Text id="basic-addon1"><i className="bi bi-123"></i></InputGroup.Text>
|
||||||
|
<Form.Control
|
||||||
|
name="nisn"
|
||||||
|
value={formData.nisn || ''}
|
||||||
|
onChange={handleFormChange}
|
||||||
|
placeholder="Enter NISN"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
|
<Form.Group as={Col} controlId="formGridName">
|
||||||
|
<Form.Label>Full Name<sup className='text-red fw-bold'>*</sup></Form.Label>
|
||||||
|
<InputGroup className="mb-2 input-group-icon">
|
||||||
|
<InputGroup.Text id="basic-addon1"><i className="bi bi-person"></i></InputGroup.Text>
|
||||||
|
<Form.Control
|
||||||
|
name="fullName"
|
||||||
|
value={formData.fullName || ''}
|
||||||
|
onChange={handleFormChange}
|
||||||
|
placeholder="Enter Full Name"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Form.Group>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
{/* <Row className="mb-2">
|
||||||
|
<Form.Group as={Col} controlId="formGridRole">
|
||||||
|
<Form.Label>Role<sup className='text-red fw-bold'>*</sup></Form.Label>
|
||||||
|
<InputGroup className="mb-2 input-group-icon disabled">
|
||||||
|
<InputGroup.Text id="basic-addon1"><i className="bi bi-person-vcard"></i></InputGroup.Text>
|
||||||
|
<Form.Control
|
||||||
|
disabled
|
||||||
|
value="Student"
|
||||||
|
aria-label="role"
|
||||||
|
aria-describedby="basic-addon1"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
|
<Form.Group as={Col} controlId="formGridName">
|
||||||
|
<Form.Label>Full Name<sup className='text-red fw-bold'>*</sup></Form.Label>
|
||||||
|
<InputGroup className="mb-2 input-group-icon">
|
||||||
|
<InputGroup.Text id="basic-addon1"><i className="bi bi-person"></i></InputGroup.Text>
|
||||||
|
<Form.Control
|
||||||
|
name="fullName"
|
||||||
|
value={formData.fullName || ''}
|
||||||
|
onChange={handleFormChange}
|
||||||
|
placeholder="Enter Full Name"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Form.Group>
|
||||||
|
</Row>
|
||||||
|
<Row className="mb-2">
|
||||||
|
<Form.Group as={Col} controlId="formGridNISN">
|
||||||
|
<Form.Label>NISN<sup className='text-red fw-bold'>*</sup></Form.Label>
|
||||||
|
<InputGroup className="mb-2 input-group-icon">
|
||||||
|
<InputGroup.Text id="basic-addon1"><i className="bi bi-123"></i></InputGroup.Text>
|
||||||
|
<Form.Control
|
||||||
|
name="nisn"
|
||||||
|
value={formData.nisn || ''}
|
||||||
|
onChange={handleFormChange}
|
||||||
|
placeholder="Enter NISN"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
|
<Form.Group as={Col} controlId="formGridEmail">
|
||||||
|
<Form.Label>Email Address<sup className='text-red fw-bold'>*</sup></Form.Label>
|
||||||
|
<InputGroup className="mb-2 input-group-icon">
|
||||||
|
<InputGroup.Text id="basic-addon1"><i className="bi bi-envelope"></i></InputGroup.Text>
|
||||||
|
<Form.Control
|
||||||
|
name="email"
|
||||||
|
value={formData.email || ''}
|
||||||
|
onChange={handleFormChange}
|
||||||
|
placeholder="Enter Email Address"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Form.Group>
|
||||||
|
</Row>
|
||||||
|
<Row className="mb-2">
|
||||||
|
<Form.Group as={Col} controlId="formGridPassword">
|
||||||
|
<Form.Label>Password<sup className='text-red fw-bold'>*</sup></Form.Label>
|
||||||
|
<InputGroup className="mb-2 input-group-icon">
|
||||||
|
<InputGroup.Text id="basic-addon1"><i className="bi bi-key"></i></InputGroup.Text>
|
||||||
|
<Form.Control
|
||||||
|
type='password'
|
||||||
|
name="pass"
|
||||||
|
value={formData.pass || ''}
|
||||||
|
onChange={handleFormChange}
|
||||||
|
placeholder="Create Password"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
|
<Form.Group as={Col} controlId="formGridConfirm">
|
||||||
|
<Form.Label>Confirm Password<sup className='text-red fw-bold'>*</sup></Form.Label>
|
||||||
|
<InputGroup className="mb-2 input-group-icon">
|
||||||
|
<InputGroup.Text id="basic-addon1"><i className="bi bi-key-fill"></i></InputGroup.Text>
|
||||||
|
<Form.Control
|
||||||
|
type='password'
|
||||||
|
name="confirm_pass"
|
||||||
|
value={formData.confirm_pass || ''}
|
||||||
|
onChange={handleFormChange}
|
||||||
|
placeholder="Confirm Password"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Form.Group>
|
||||||
|
</Row> */}
|
||||||
|
<div className="d-flex justify-content-end">
|
||||||
|
<Button variant="outline-blue" type="reset" className='ms-auto py-2 rounded-35'
|
||||||
|
onClick={resetForm}
|
||||||
|
>
|
||||||
|
reset
|
||||||
|
</Button>
|
||||||
|
<Button variant="blue" type="submit" className='ms-2 py-2 px-5 rounded-35'
|
||||||
|
// onClick={handleCreate}
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="cards">
|
||||||
|
<div className="cards-title d-flex justify-content-between aligb-items-center">
|
||||||
|
<h4>Add Student Data via Excel</h4>
|
||||||
|
<span className='text-blue cursor-pointer' onClick={() => {handleDownloadTemplate()}}><i className="bi bi-download me-2"></i>Download Template Excel</span>
|
||||||
|
</div>
|
||||||
|
<div className="cards-body">
|
||||||
|
<Form onSubmit={handleImport}>
|
||||||
|
<Row className="mb-2">
|
||||||
|
<Form.Control
|
||||||
|
id='fileUpload'
|
||||||
|
accept='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
type='file'
|
||||||
|
name="file"
|
||||||
|
onChange={handleFileChange}
|
||||||
|
placeholder="Masukan File"
|
||||||
|
className='d-none'
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="fileUpload"
|
||||||
|
className={`drop-zone rounded-3 ${fileImport ? `active` : ``}`}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
onDragEnter={handleDragEnter}
|
||||||
|
onDragLeave={handleDragLeave}
|
||||||
|
>
|
||||||
|
<h5 className={`m-0 ${fileImport ? `text-green fs-3` : `text-grey`}`}>{fileImport ? fileImport.name : "Upload Excel file here"}</h5>
|
||||||
|
<span className={`fs-14p ${fileImport ? `text-green` : `text-grey`}`}>{fileImport ? "Your file is ready to import" : "Please make sure your Excel file follows the template."}</span>
|
||||||
|
</label>
|
||||||
|
</Row>
|
||||||
|
<div className="d-flex justify-content-end">
|
||||||
|
<Button variant="outline-blue" type="reset" className='ms-auto py-2 rounded-35'
|
||||||
|
onClick={resetImportFile}
|
||||||
|
>
|
||||||
|
reset
|
||||||
|
</Button>
|
||||||
|
<Button variant="blue" type="submit" className='ms-2 py-2 px-5 rounded-35' disabled={fileImport ? false : true}>
|
||||||
|
Import
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tab.Pane>
|
||||||
|
</Tab.Content>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Tab.Container>
|
||||||
|
|
||||||
|
<Modal show={show} onHide={handleClose} className='modal-admin' size='lg' centered>
|
||||||
|
<Modal.Header closeButton>
|
||||||
|
<Modal.Title>Edit Student</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<Form onSubmit={handleUpdate}>
|
||||||
|
<Form.Group as={Col} controlId="modalGridRole" className='mb-3'>
|
||||||
|
<Form.Label>Role<sup className='text-red fw-bold'>*</sup></Form.Label>
|
||||||
|
<InputGroup className="mb-2 input-group-icon disabled">
|
||||||
|
<InputGroup.Text><i className="bi bi-person-vcard"></i></InputGroup.Text>
|
||||||
|
<Form.Control
|
||||||
|
disabled
|
||||||
|
value="Student"
|
||||||
|
aria-label="role"
|
||||||
|
aria-describedby="basic-addon1"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Form.Group>
|
||||||
|
<Form.Group as={Col} controlId="modalGridName" className='mb-3'>
|
||||||
|
<Form.Label>Full Name<sup className='text-red fw-bold'>*</sup></Form.Label>
|
||||||
|
<InputGroup className="mb-2 input-group-icon">
|
||||||
|
<InputGroup.Text><i className="bi bi-person"></i></InputGroup.Text>
|
||||||
|
<Form.Control
|
||||||
|
placeholder="Enter Full Name"
|
||||||
|
name="fullName"
|
||||||
|
value={formData.fullName || ''}
|
||||||
|
onChange={handleFormChange}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Form.Group>
|
||||||
|
<Form.Group as={Col} controlId="modalGridNisn" className="mb-3">
|
||||||
|
<Form.Label>NISN<sup className='text-red fw-bold'>*</sup></Form.Label>
|
||||||
|
<InputGroup className="mb-2 input-group-icon">
|
||||||
|
<InputGroup.Text><i className="bi bi-123"></i></InputGroup.Text>
|
||||||
|
<Form.Control
|
||||||
|
placeholder="Enter NISN"
|
||||||
|
name="nisn"
|
||||||
|
value={formData.nisn || ''}
|
||||||
|
onChange={handleFormChange}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Form.Group>
|
||||||
|
<Form.Group as={Col} controlId="modalGridEmail" className="mb-3">
|
||||||
|
<Form.Label>Email Address<sup className='text-red fw-bold'>*</sup></Form.Label>
|
||||||
|
<InputGroup className="mb-2 input-group-icon">
|
||||||
|
<InputGroup.Text><i className="bi bi-envelope"></i></InputGroup.Text>
|
||||||
|
<Form.Control
|
||||||
|
placeholder="Enter Email Address"
|
||||||
|
name="email"
|
||||||
|
value={formData.email || ''}
|
||||||
|
onChange={handleFormChange}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Form.Group>
|
||||||
|
<div className="d-flex justify-content-end">
|
||||||
|
<Button variant="blue" type="submit" className='py-2 px-5 w-100 rounded-35'>
|
||||||
|
Update
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</Modal.Body>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<ModalOperation
|
||||||
|
show={showLoader}
|
||||||
|
handleClose={handleCloseLoader}
|
||||||
|
title={loaderState.title}
|
||||||
|
description={loaderState.description}
|
||||||
|
loading={loaderState.loading}
|
||||||
|
successMessage={loaderState.successMessage}
|
||||||
|
confirmAction={loaderState.confirmAction}
|
||||||
|
handleConfirm={loaderState.handleConfirm}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ManageStudents;
|
||||||
Loading…
Reference in New Issue
Block a user