From 1d54acccbceea39995fcd6493d17b7c6c1da4348 Mon Sep 17 00:00:00 2001 From: PhuocNTB Date: Tue, 30 Jun 2026 16:15:30 +0700 Subject: [PATCH] fix add more student --- .../src/components/ExamRoomWorkspace.tsx | 111 +++++++++++++++++- management/src/index.css | 11 +- 2 files changed, 115 insertions(+), 7 deletions(-) diff --git a/management/src/components/ExamRoomWorkspace.tsx b/management/src/components/ExamRoomWorkspace.tsx index ef54966..578cc80 100644 --- a/management/src/components/ExamRoomWorkspace.tsx +++ b/management/src/components/ExamRoomWorkspace.tsx @@ -69,6 +69,7 @@ export const ExamRoomWorkspace: React.FC = ({ examId, onBack }) => { const [submissions, setSubmissions] = useState([]); const [searchQ, setSearchQ] = useState(''); const [searchHits, setSearchHits] = useState<{ studentRkId: number; fullName: string; studentCode: string; email: string; classCodes: string }[]>([]); + const [selectedPickerRkIds, setSelectedPickerRkIds] = useState([]); const [paperTitle, setPaperTitle] = useState(''); const [packagePdf, setPackagePdf] = useState(null); const [packageResources, setPackageResources] = useState([]); @@ -210,6 +211,39 @@ export const ExamRoomWorkspace: React.FC = ({ examId, onBack }) => { await fetchOnline(); }; + const toggleHitSelection = (studentRkId: number) => { + setSelectedPickerRkIds((prev) => + prev.includes(studentRkId) ? prev.filter((id) => id !== studentRkId) : [...prev, studentRkId] + ); + }; + + const addSelectedStudents = async () => { + if (selectedPickerRkIds.length === 0) return; + try { + await apiExam.addStudents(examId, selectedPickerRkIds); + setSelectedPickerRkIds([]); + setStudentPickerOpen(false); + await load(); + await fetchOnline(); + } catch (e: any) { + alert(e?.message || 'Thêm sinh viên thất bại'); + } + }; + + const toggleSelectAllHits = () => { + const enrolledIds = new Set(students.map((s) => s.studentRkId)); + const unaddedHits = searchHits.filter((h) => !enrolledIds.has(h.studentRkId)); + const allUnaddedIds = unaddedHits.map((h) => h.studentRkId); + if (allUnaddedIds.length === 0) return; + + const areAllSelected = allUnaddedIds.every((id) => selectedPickerRkIds.includes(id)); + if (areAllSelected) { + setSelectedPickerRkIds((prev) => prev.filter((id) => !allUnaddedIds.includes(id))); + } else { + setSelectedPickerRkIds((prev) => Array.from(new Set([...prev, ...allUnaddedIds]))); + } + }; + const createPackage = async () => { if (!packagePdf) { setErr('Chọn file PDF cho gói đề'); @@ -820,7 +854,7 @@ export const ExamRoomWorkspace: React.FC = ({ examId, onBack }) => { @@ -846,7 +880,7 @@ export const ExamRoomWorkspace: React.FC = ({ examId, onBack }) => { @@ -1168,15 +1202,31 @@ export const ExamRoomWorkspace: React.FC = ({ examId, onBack }) => {
-
+
setSearchQ(e.target.value)} + onChange={(e) => { + setSearchQ(e.target.value); + setSelectedPickerRkIds([]); + }} autoFocus + style={{ flex: 1 }} /> + {searchQ.trim() !== '' && searchHits.some((h) => !enrolledIds.has(h.studentRkId)) && ( + + )}
{searchQ.trim() === '' ? ( @@ -1192,6 +1242,7 @@ export const ExamRoomWorkspace: React.FC = ({ examId, onBack }) => { ) : ( searchHits.map((h) => { const added = enrolledIds.has(h.studentRkId); + const isSelected = selectedPickerRkIds.includes(h.studentRkId); const initials = h.fullName .split(/\s+/) .filter(Boolean) @@ -1199,7 +1250,27 @@ export const ExamRoomWorkspace: React.FC = ({ examId, onBack }) => { .map((w) => w[0]?.toUpperCase() || '') .join(''); return ( -
+
{ + if (!added) toggleHitSelection(h.studentRkId); + }} + > +
e.stopPropagation()} + > + { + if (!added) toggleHitSelection(h.studentRkId); + }} + /> +
{h.fullName} @@ -1213,7 +1284,10 @@ export const ExamRoomWorkspace: React.FC = ({ examId, onBack }) => { type="button" className={`btn btn-sm ${added ? 'btn-secondary' : 'btn-primary'}`} disabled={added} - onClick={() => addStudent(h.studentRkId)} + onClick={(e) => { + e.stopPropagation(); + addStudent(h.studentRkId); + }} > {added ? '✓ Đã thêm' : '+ Thêm'} @@ -1223,6 +1297,31 @@ export const ExamRoomWorkspace: React.FC = ({ examId, onBack }) => { )}
+
+ + +
)} diff --git a/management/src/index.css b/management/src/index.css index cacde50..b7c0d10 100644 --- a/management/src/index.css +++ b/management/src/index.css @@ -4367,7 +4367,7 @@ input:checked + .slider:before { .exam-student-hit { display: grid; - grid-template-columns: auto 1fr auto; + grid-template-columns: auto auto 1fr auto; align-items: center; gap: 0.85rem; padding: 0.85rem 1rem; @@ -4377,6 +4377,15 @@ input:checked + .slider:before { transition: var(--transition); } +.exam-student-hit--added { + opacity: 0.85; +} + +.exam-student-hit--selected { + border-color: var(--accent); + background-color: var(--accent-light, #fff5f5); +} + .exam-student-hit:hover { border-color: var(--accent); box-shadow: 0 2px 10px rgba(187, 33, 38, 0.08);