New Text Document
New Text Document
if (value) {
const suggestions = teacherEmailData.filter((teacher) =>
teacher.email.toLowerCase().includes(value.toLowerCase())
);
const formattedSuggestions = suggestions.map((teacher) => ({
display: `${teacher.email} (${teacher.code})`,
email: teacher.email,
code: teacher.code,
}));
setFilteredEmails(formattedSuggestions);
} else {
const allSuggestions = teacherEmailData.map((teacher) => ({
display: `${teacher.email} (${teacher.code})`,
email: teacher.email,
code: teacher.code,
}));
setFilteredEmails(allSuggestions);
}
};
if (domain.parent) {
const parentId = domain.parent.id;
if (domainsMap[parentId]) {
domainsMap[parentId].children.push(domainsMap[domain.id]);
}
}
});
const result = Object.values(domainsMap).filter(domain => domain.parentId
=== null);
setDomainOptions(result);
}
const fetchSemester = async () => {
const response = await fetchSettingSemester();
if (response && response.error === true) {
handleApiError("errorFetchSubject", response);
} else {
const sortedSemesters = (response.body?.content || []).sort((a, b) =>
b.id - a.id);
setSemesterOptions(sortedSemesters);
}
};
useEffect(() => {
fetchSubjectData();
}, [subjectId]);
useEffect(() => {
fetchSubjectData();
}, [activeTab, currentPage, sortField, descend, key]);
</div>
), width: '80px'
},
];
case '2':
return [
{ Header: "Id", accessor: "id", width: '60px' },
{ Header: "Chapter No.", accessor: "number", width: '60px' },
{ Header: "Title", accessor: "title", start: 'start' },
{ Header: "Content", accessor: "content", start: 'start' },
{ Header: "Status", accessor: "status", Filter:
SelectColumnFilter },
{
Header: "Actions", id: "actions",
Cell: ({ row }) => (
<div className="d-flex justify-content-start align-
items-center">
<EditButton btnStyle='btn-outline-info me-2'
icon='bi bi-pencil-square' onClick={() => toggleEditChapterModal(row)} />{' '}
<StatusButton status={row.original.status}
onClick={() => toggleStatusChapterModal(row.original.id)} />
</div>
), width: '80px'
},
];
default:
return [
{ Header: "Id", accessor: "id", width: '60px' },
{ Header: "Chapter No.", accessor: "number", width: '60px' },
{ Header: "Title", accessor: "title", start: 'start' },
{ Header: "Content", accessor: "content", start: 'start' },
{ Header: "Status", accessor: "status", Filter:
SelectColumnFilter },
{
Header: "Actions", id: "actions",
Cell: ({ row }) => (
<div className="d-flex justify-content-start align-
items-center">
<EditButton btnStyle='btn-outline-info me-2'
icon='bi bi-pencil-square' onClick={() => toggleEditChapterModal(row)} />{' '}
<StatusButton status={row.original.status}
onClick={() => toggleStatusChapterModal(row.original.id)} />
</div>
), width: '80px'
},
];
}
}, [activeTab, expandedQuestionId]);
const tableData = useMemo(() => {
if (activeTab === '6') {
return questionData;
} else if (activeTab === '5') {
return questionData;
} else if (activeTab === '4') {
return courseData;
} else if (activeTab === '3') {
return domainData;
} else {
return chapterData;
}
}, [activeTab, chapterData, courseData, domainData, questionData]);
const formAddTest = [
{ label: 'Title', id: 'title', type: 'text', name: 'title', required:
true },
{ label: 'Time Limit (Mins)', id: 'timeLimit', type: 'number', name:
'timeLimit', min: '0', required: true },
];
const formAddCourse = [
{ label: 'Description', id: 'description', type: 'text', name:
'description', required: true },
{ label: 'Class Code', id: 'code', type: 'text', name: 'code', required:
true },
{
label: 'Semester', id: 'semesterSettingId', type: 'select', name:
'semesterSettingId', required: true,
options: Array.isArray(semesterOptions) && semesterOptions.length > 0
? semesterOptions.map(opt => ({ label: opt.name + ' - ' +
opt.value, value: opt.id }))
: []
},
];
const formCreateDomain = [
{ label: 'Name', id: 'name', type: 'text', name: 'name', required: true },
{
label: 'Domain parent', id: 'parent', type: 'select', name: 'parent',
options: Array.isArray(domainData) && domainData.length > 0 ?
domainData
.filter(opt => opt.parent === null && opt.status === true).map(opt
=> ({ label: opt.name, value: opt.id }))
: []
},
];
const formEditDomain = [
{ label: 'Id', id: 'id', type: 'text', name: 'id', required: true,
disabled: true },
{ label: 'Name', id: 'name', type: 'text', name: 'name', required: true },
];
const formEditCourse = [
{ label: 'Id', id: 'id', type: 'text', name: 'id', required: true,
disabled: true },
{ label: 'Name', id: 'name', type: 'text', name: 'name', required: true },
{ label: 'Code', id: 'code', type: 'text', name: 'code', required: true },
{
label: 'Semester', id: 'semesterSettingId', type: 'select', name:
'semesterSettingId', required: true,
options: [
...(semesterOptions && semesterOptions.length > 0
? semesterOptions.map(opt => ({ label: opt.name + ' - ' +
opt.value, value: opt.id }))
: []
)
],
},
{ label: 'Description', id: 'description', type: 'textarea', name:
'description' }
];
/**Function */
/** Toggle Modal */
const toggleAddChapterModal = () => { setAddChapterModal(!addChapterModal); }
const toggleAddTestModal = () => { setAddTestModal(!addTestModal); }
// const toggleImportQuestionModal = () => { setImportTestquestionModal(!
importTestquestionModal); }
const toggleImportQuestionModal = (testId) => {
setCurrentTestId(testId); // Lưu testId khi mở modal
setImportTestquestionModal(!importTestquestionModal);
};
const toggleAddCourseModal = () => { setAddCourseModal(!addCourseModal); }
const toggleCreateDommainModal = () => { setCreateDomainModal(!
createDomainModal); }
const toggleAddQuestionModal = () => { setAddQuestionModal(!
addQuestionModal); }
const toggleEditChapterModal = (row) => {
if (row && row.original) {
setEditChapterData(row.original);
}
setEditChapterModal(!editChapterModal);
}
const toggleEditCourseModal = (row) => {
return childDomains.length ? (
<span>
{childDomains.map((child) => (
<div key={child.id} className="row mx-1 mb-2">
<span className="col-1 border-end px-2 text-
end">{child.id}</span>
<span className="col-6 border-end px-2">{child.name}</span>
<span className={`col-2 px-2 ${child.status ? 'text-
success' : 'text-danger'}`}>
{child.status ? 'Active' : 'Inactive'}
</span>
</div>
))}
</span>
) : (
<span className="ms-2 text-muted fs-6">{'( No child domain )'}</span>
);
};
setStatusDomainModal(!statusDomainModal);
};
return (
<div>
<Helmet>
<title>{subjectData && subjectData.code}</title>
</Helmet>
<Breadcrumb>
<BreadcrumbItem>
<Link to="/department">Department</Link>
</BreadcrumbItem>
<BreadcrumbItem>
<Link to={`/department/${subjectData &&
subjectData.department.id}`}>{subjectData &&
subjectData.department.departmentName}</Link>
</BreadcrumbItem>
<BreadcrumbItem active>{subjectData &&
subjectData.name}</BreadcrumbItem>
</Breadcrumb>
<Row>
<Col sm="12" lg="8" xl="8" xxl="8">
<Card>
<CardBody>
<CardTitle tag="h3" className="fw-bold">{subjectData &&
`${subjectData.name} - ${subjectData.code}`}</CardTitle>
</CardBody>
</Card>
</Col>
<Col sm="12" lg="4" xl="4" xxl="4">
<Card>
<CardBody>
<CardTitle tag="h4"
className="fw-bold">Resources</CardTitle>
<CustomButton btnStyle='btn-outline-primary'
title='Questions and terms' onClick={() => navigate(`/question-bank/${subjectId}`)}
type='button' icon='bi bi-arrow-right' />
</CardBody>
</Card>
</Col>
</Row>
{/* <Row> */}
{/* <Col sm="12" lg="9" xl="8" xxl="7" className='mb-3'> */}
<Nav tabs className="border-none">
<NavItem>
<NavLink className={classnames({ active: activeTab === '1',
'bg-white': activeTab === '1' })}
onClick={() => { toggleTab('1'); }} style={{ borderLeft:
'none', borderBottom: 'none' }}>
General
</NavLink>
</NavItem>
<NavItem>
<NavLink className={classnames({ active: activeTab === '2',
'bg-white': activeTab === '2' })}
onClick={() => { toggleTab('2'); }} style={{ borderBottom:
'none' }}>
Chapters
</NavLink>
</NavItem>
<NavItem>
<NavLink className={classnames({ active: activeTab === '3',
'bg-white': activeTab === '3' })}
onClick={() => { toggleTab('3'); }} style={{ borderBottom:
'none' }}>
Domains
</NavLink>
</NavItem>
<NavItem>
<NavLink className={classnames({ active: activeTab === '4',
'bg-white': activeTab === '4' })}
onClick={() => { toggleTab('4'); }} style={{ borderBottom:
'none' }}>
Classes
</NavLink>
</NavItem>
<NavItem>
<NavLink className={classnames({ active: activeTab === '6',
'bg-white': activeTab === '6' })}
onClick={() => { toggleTab('6'); }} style={{ borderBottom:
'none' }}>
Exams
</NavLink>
</NavItem>
</Nav>
<TabContent activeTab={activeTab} className="bg-white px-3 pt-1 pb-3">
<TabPane tabId="1">
<CardTitle tag="h6" className="border-bottom d-flex py-2 px-3
justify-content-between align-items-center" >
<div>
<i className="bi-book me-2"></i>
<span className="fs-5">Subject Infomation</span>
</div>
</CardTitle>
<AlertBox message={message.editSubject} />
<Row >
</Row>
<CardBody className="p-2">
<Row>
<Col>
<Form onSubmit={(e) => { e.preventDefault();
handleEditSubject(); }}>
<FormGroup>
<Label for="name">
<strong>Name: </strong>
<span hidden={editingSubject}>
{formData.name} </span>
<span hidden={!editingSubject}
className="text-danger"> * </span>
</Label>
<Input type="text" name="name" id="name"
placeholder="Enter subject name"
value={formData.name}
onChange={handleChangeSubjectData}
hidden={!editingSubject} disabled={!
editingSubject} required />
</FormGroup>
<FormGroup>
<Label for="code">
<strong>Code: </strong>
<span hidden={editingSubject}>
{formData.code} </span>
<span hidden={!editingSubject}
className="text-danger"> * </span>
</Label>
<Input type="text" name="code" id="code"
placeholder="Enter subject code"
value={formData.code}
onChange={handleChangeSubjectData} hidden={!editingSubject} disabled={!
editingSubject} required />
</FormGroup>
<div hidden={editingSubject} className="d-flex
justify-content-start mb-3">
<strong hidden={editingSubject}>Status:
</strong>
<span className={formData.status ? "text-
success ms-2" : "text-danger ms-2"} hidden={editingSubject}>
{formData ? (formData.status ? "Active"
: "Inactive") : "Loading..."}
</span>
<span className="ms-4"
hidden={editingSubject}>
<StatusButton status={formData.status}
onClick={toggleConfirmModal} />
</span>
</div>
<FormGroup>
<Label for="departmentName">
<strong>Department: </strong>
<span hidden={editingSubject}>
{formData.departmentName} </span>
</Label>
<Input type="text" name="departmentName"
id="departmentName" placeholder="Enter department"
value={formData.departmentName}
onChange={handleChangeSubjectData} hidden={!editingSubject} disabled />
</FormGroup>
<FormGroup>
<Label for="description">
<strong>Description: </strong>
<span hidden={editingSubject}>
{formData.description} </span>
</Label>
<Input type="textarea" name="description"
id="description" placeholder="Enter description"
value={formData.description}
onChange={handleChangeSubjectData} hidden={!editingSubject} disabled={!
editingSubject} />
</FormGroup>
<AlertBox error={true}
message={error.errorEditSubject} />
{!editingSubject && <CustomButton
btnStyle='btn-outline-primary' icon='bi bi-pencil-square' title='Edit subject
information' onClick={() => setEditingSubject(true)} />}
{editingSubject && <CustomButton btnStyle='btn-
primary' icon='bi bi-pencil-square' type='submit' title='Save' />}
</Form>
</Col>
</Row>
</CardBody>
<ConfirmModal isOpen={confirmModal} toggle={toggleConfirmModal}
handleConfirm={() => handleToggleStatus(currentRow)} message="Confirm changing this
status?" />
</TabPane>
<TabPane tabId="2">
<CardTitle tag="h6" className="border-bottom d-flex py-2 px-3
justify-content-between align-items-center" >
<div>
<i className="bi bi-archive me-2"></i>
<span className="fs-5">Chapter</span>
</div>
<div className="pagination d-flex align-items-center
position-relative">
<span className="d-flex align-items-center">
<CustomButton type='button' btnStyle='btn-outline-
primary' icon='bi bi-plus' title='New chapter'
onClick={toggleAddChapterModal}></CustomButton>
</span>
</div>
</CardTitle>
{isArrangingChapter ? (
<DragDropContext onDragEnd={handleOnDragEnd}>
<div className="d-flex justify-content-between my-2">
<div className="ms-2 mb-1 fs-5"> Total <strong>
{chapterData.length} </strong> chapters</div>
<div>
<CustomButton type='button' btnStyle='btn-
primary' title='Save' onClick={() => handleArrangChapter()} />
<CustomButton type='button' btnStyle='btn-
outline-secondary' title='Cancel' onClick={() => { setIsArrangingChapter(!
isArrangingChapter); fetchChapter() }} />
</div>
</div>
<Droppable droppableId="chapterList">
{(provided) => (
<div {...provided.droppableProps}
ref={provided.innerRef}>
{chapters && chapters.length > 0 ? (
chapters.map((chapter, index) => (
<Draggable key={chapter.id}
draggableId={chapter.id.toString()} index={index}>
{(provided) => (
<div className="border py-1
px-3 mb-2 rounded" ref={provided.innerRef} {...provided.draggableProps}
{...provided.dragHandleProps} style={{ ...provided.draggableProps.style }}>
<div className="row">
<div
className="col-1 d-flex align-items-center">
<div
className="fw-bold fs-5 text-primary" style={{ whiteSpace: "nowrap" }}>#{index +
1}</div>
</div>
<div
className="col-10 border-start" style={{ whiteSpace: "nowrap", overflow: "hidden",
textOverflow: "ellipsis" }}>
<div
className="fs-5"><span className="fw-bold"> Chapter {chapter.number}:</span>
{chapter.title}<span className="text-muted"> {'( ID: ' + chapter.id + '
)'}</span><span className={chapter.status ? "text-success ms-2" : "text-danger ms-
2"}>{chapter.status ? "( Active )" : "( Inactive )"}</span></div>
<div
className="text-muted ms-3" style={{ whiteSpace: "nowrap", overflow: "hidden",
textOverflow: "ellipsis" }}>{chapter.content || "(Chapter content empty)"}</div>
</div>
<div
className="col-1 d-flex align-items-center">
<i
className="bi bi-arrows-expand fs-5 text-primary" style={{ transform: "scale(1)",
cursor: "pointer", transition: "transform 0.3s ease, color 0.3s ease" }}
onMouseEnter={(e) => { e.target.style.transform = "scale(1.5)"; }}
onMouseLeave={(e) => { e.target.style.transform = "scale(1)"; }}></i>
</div>
</div>
</div>
)}
</Draggable>
))
) : (
<div className="text-center text-
muted">No chapters available.</div>
)}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
) : (
<CardBody>
<div className="d-flex justify-content-between my-2">
<div className="ms-2 mb-1 fs-5"> Total <strong>
{chapterData.length} </strong> chapters</div>
<CustomButton type='button' btnStyle='btn-outline-
primary' icon='bi bi-arrows-expand' title='Arange' onClick={() =>
setIsArrangingChapter(!isArrangingChapter)}></CustomButton>
</div>
<AlertBox message={message.msgAddChapter} />
<DataTable getTableProps={getTableProps}
getTableBodyProps={getTableBodyProps} headerGroups={headerGroups} page={page}
prepareRow={prepareRow} columns={columns} />
<AddModal toggle={toggleAddChapterModal}
isOpen={addChapterModal} formData={newChapter} setFormData={setNewChapter}
modalTitle='Create new chapter' formFields={formAddChapter}
handleAdd={handleAddChapter} error={error.errAddChapter} />
<EditModal title='Edit chapter'
toggle={toggleEditChapterModal} isOpen={editChapterModal}
editData={editChapterData} setEditData={setEditChapterData}
handleEdit={handleEditChapter} errors={error.errEditChapter} setErrors={setError}
formFields={formEditChapter} />
<ConfirmModal isOpen={statusChapterModal}
toggle={toggleStatusChapterModal} handleConfirm={() =>
handleChapterStatus(currentRow)} message="Confirm changing this status?" />
</CardBody>
)}
</TabPane>
<TabPane tabId="3">
<CardTitle tag="h6" className="border-bottom d-flex py-2 px-3
justify-content-between align-items-center">
<div>
<i className="bi bi-clipboard me-2"></i>
<span className="fs-5">Domain</span>
</div>
<div className="pagination d-flex align-items-center
position-relative">
<span className="d-flex align-items-center">
<CustomButton type='button' btnStyle='btn-outline-
primary' icon='bi bi-plus' title='Create new domain'
onClick={toggleCreateDommainModal}></CustomButton>
</span>
</div>
</CardTitle>
<CardBody>
<div className="d-flex justify-content-between my-2">
<div className="ms-2 mb-1 fs-5">Total
<strong>{domainData.length}</strong> domains</div>
<CustomButton type="button" btnStyle="btn-outline-info"
icon="bi bi-toggle-on" title={isDataTable ? 'Edit Table' : 'Data Table'}
onClick={() => setIsDataTable(!isDataTable)} />
</div>
<AlertBox message={message.msgAddDomain} />
{!isDataTable ? (
<DataTable getTableProps={getTableProps}
getTableBodyProps={getTableBodyProps} headerGroups={headerGroups} page={page}
prepareRow={prepareRow} columns={columns} />
) : (
<Table bordered >
<thead>
<tr >
<th style={{ backgroundColor:
'inherit' }}>ID</th>
<th style={{ backgroundColor:
'inherit' }}>Parent Domain</th>
<th style={{ backgroundColor:
'inherit' }}>Status</th>
</tr>
</thead>
<tbody>
{domainData.filter(domain => !
domain.parent).map((domain) => (
<React.Fragment key={domain.id}>
<tr>
<td style={{ width: '60px',
backgroundColor: 'inherit' }}>{domain.id}</td>
<td onClick={() =>
toggleRow(domain.id)} style={{ cursor: 'pointer', backgroundColor: 'inherit' }}>
{expandedRows[domain.id] ? <i
className="bi bi-chevron-down"></i> : <i className="bi bi-chevron-right"></i>}
{domain.name}
</td>
<td className={domain.status ?
'text-success' : 'text-danger'} style={{ width: '120px', backgroundColor: 'inherit'
}}>
{domain.status ? 'Active' :
'Inactive'}
</td>
</tr>
{expandedRows[domain.id] && (
<tr>
<td style={{ backgroundColor:
'inherit' }}> </td>
<td style={{ backgroundColor:
'inherit' }}> {renderChildDomains(domain.id)} </td>
<td style={{ backgroundColor:
'inherit' }}> </td>
</tr>
)}
</React.Fragment>
))}
</tbody>
</Table>
)}
</CardBody>
<AddModal toggle={toggleCreateDommainModal}
isOpen={createDomainModal} formData={newDomain} setFormData={setNewDomain}
modalTitle='Create new domain for this course' formFields={formCreateDomain}
handleAdd={handleCreateDomain} error={error.errorAddDomain} />
<EditModal title='Change domain data' isOpen={editDomainModal}
toggle={toggleEditDommainModal} editData={editDomainData}
setEditData={setEditDomainData} handleEdit={handleEditDomain}
errors={error.errorEditDomain} setErrors={setError} formFields={formEditDomain} />
<ConfirmModal isOpen={statusDomainModal}
toggle={toggleStatusDomainModal} handleConfirm={() =>
handleDomainStatus(currentRow)} message="Confirm changing this status?" />
</TabPane>
<TabPane tabId="4">
<CardTitle tag="h6" className="border-bottom d-flex py-2 px-3
justify-content-between align-items-center" >
<div>
<i className="bi bi-laptop me-2"></i>
<span className="fs-5">Class</span>
</div>
<div className="pagination d-flex align-items-center
position-relative">
<span className="d-flex align-items-center">
<CustomButton type='button' btnStyle='btn-outline-
primary' icon='bi bi-plus' title='Create new class'
onClick={toggleAddCourseModal}></CustomButton>
</span>
</div>
</CardTitle>
<CardBody>
<div className="d-flex justify-content-between my-2">
<div className="ms-2 mb-1 fs-5"> Total <strong>
{courseData.length} </strong> classes</div>
</div>
<AlertBox message={message.editCourse} />
<DataTable getTableProps={getTableProps}
getTableBodyProps={getTableBodyProps}
headerGroups={headerGroups} page={page}
prepareRow={prepareRow} columns={columns} />
<AddClassModal emailList={teacherEmailData}
subjectName={subjectData?.name} toggle={toggleAddCourseModal}
isOpen={addCourseModal} formData={newCourse} setFormData={setNewCourse}
modalTitle='Create new class' formFields={formAddCourse}
handleAdd={handleAddCourse} error={error.errorAddCourse} />
<EditClassModal emailList={teacherEmailData} title='Change
course data' isOpen={editCourseModal} toggle={toggleEditCourseModal}
editData={editCourseData} setEditData={setEditCourseData}
handleEdit={handleEditCourse} errors={error.errorEditCourse} setErrors={setError}
formFields={formEditCourse} />
<ConfirmModal isOpen={statusCourseModal}
toggle={toggleStatusCourseModal} handleConfirm={() =>
handleCourseStatus(currentRow)} message="Confirm changing this status?" />
</CardBody>
</TabPane>
<TabPane tabId="6">
<CardTitle tag="h6" className="border-bottom d-flex py-2 px-3
justify-content-between align-items-center" >
<div>
<i className="bi bi-lightbulb me-2"></i>
<span className="fs-5"> Simulation Exam </span>
</div>
<div className="pagination d-flex align-items-center
position-relative">
<span className="d-flex align-items-center">
<CustomButton type='button' btnStyle='btn-outline-
primary' icon='bi bi-plus' title='Create new Exam'
onClick={toggleAddTestModal}></CustomButton>
</span>
</div>
</CardTitle>
<CardBody>
<div className="d-flex justify-content-between my-2">
<div className="ms-2 mb-1 fs-5"> Total <strong>
{simulationTestData.length} </strong> Exams</div>
</div>
{message.editTest && <AlertBox
message={message.editTest} />}
{simulationTestData.length > 0 ? (
simulationTestData.map((test, index) => (
<div className="border p-3 mb-2 border rounded
border-bottom d-flex justify-content-between" key={index}>
<div className="">
<h3>{test.title || test.description ||
'Test number ' + (index + 1)}</h3>
<div>
<div className="fs-5 mb-1">ID:
{test.id} </div>
<span className="fs-5 mx-1 badge bg-
primary">{test.numOfQuestions} questions</span>
<span className="fs-5 mx-1 badge bg-
info"><i className="bi bi-clock"></i> {test.timeLimit} Mins</span>
{test.numOfQuestions === 0 ? <span
className="fs-5 mx-1 badge bg-dark">Invalid</span> : <span className="fs-5 mx-1
badge bg-success">Valid</span>}
</div>
</div>
<div>
<div className="mb-2">
{test.numOfQuestions > 0 &&
<CustomButton btnStyle='btn-outline-primary' icon='bi bi-play-circle' title='Start
this exam' onClick={() => navigate(`/test-practice/${test.id}`)} />}
<CustomButton btnStyle='btn-outline-
success' icon='bi bi-file-earmark' title='Import Questions' onClick={() =>
toggleImportQuestionModal(test.id)} />
</div>
<div className="d-flex float-end">
<EditButton btnStyle='btn-outline-info'
icon='bi bi-pencil-square' />
<StatusButton status={test.status} />
</div>
</div>
</div>
))
) : (<p> No Exam available. </p>)}
{/* <PaginationSection currentPage={currentPage}
setCurrentPages={setCurrentPages} totalPages={totalPages} /> */}