diff --git a/management/src/components/ExamRoomWorkspace.tsx b/management/src/components/ExamRoomWorkspace.tsx index 578cc80..5b76d00 100644 --- a/management/src/components/ExamRoomWorkspace.tsx +++ b/management/src/components/ExamRoomWorkspace.tsx @@ -61,6 +61,9 @@ export const ExamRoomWorkspace: React.FC = ({ examId, onBack }) => { const [editable, setEditable] = useState(false); const [prepEditable, setPrepEditable] = useState(false); const [displayStatus, setDisplayStatus] = useState('draft'); + const canEditApps = displayStatus === 'draft' || displayStatus === 'ready' || displayStatus === 'active'; + const [searchMode, setSearchMode] = useState<'single' | 'bulk'>('single'); + const [bulkInput, setBulkInput] = useState(''); const [canPublish, setCanPublish] = useState(false); const [canUnpublish, setCanUnpublish] = useState(false); const [canCancel, setCanCancel] = useState(false); @@ -189,14 +192,18 @@ export const ExamRoomWorkspace: React.FC = ({ examId, onBack }) => { setErr(''); setMsg(''); setSaving(true); try { - await apiExam.update(examId, { - name: roomName.trim(), - startTime: localInputToISO(start), - endTime: localInputToISO(end), - allowedApps, - quizUrl: quizUrl.trim(), - }); - setMsg('Đã lưu cấu hình phòng thi'); + const payload: any = {}; + if (editable) { + payload.name = roomName.trim(); + payload.startTime = localInputToISO(start); + payload.endTime = localInputToISO(end); + payload.allowedApps = allowedApps; + payload.quizUrl = quizUrl.trim(); + } else { + payload.allowedApps = allowedApps; + } + await apiExam.update(examId, payload); + setMsg(editable ? 'Đã lưu cấu hình phòng thi' : 'Đã cập nhật danh sách ứng dụng được phép'); await load(); } catch (e: any) { setErr(e?.message || 'Lỗi'); @@ -211,6 +218,29 @@ export const ExamRoomWorkspace: React.FC = ({ examId, onBack }) => { await fetchOnline(); }; + const handleBulkSearch = async () => { + const q = bulkInput.trim(); + if (!q) return; + try { + const res = await apiExam.searchStudents(q); + setSearchHits(res.data); + // Auto-select all unadded hits + const enrolledIds = new Set(students.map((s) => s.studentRkId)); + const unaddedHits = res.data.filter((h) => !enrolledIds.has(h.studentRkId)); + setSelectedPickerRkIds(unaddedHits.map((h) => h.studentRkId)); + } catch (e: any) { + alert(e?.message || 'Tìm kiếm thất bại'); + } + }; + + const handleSwitchTab = (mode: 'single' | 'bulk') => { + setSearchMode(mode); + setSearchHits([]); + setSelectedPickerRkIds([]); + setSearchQ(''); + setBulkInput(''); + }; + const toggleHitSelection = (studentRkId: number) => { setSelectedPickerRkIds((prev) => prev.includes(studentRkId) ? prev.filter((id) => id !== studentRkId) : [...prev, studentRkId] @@ -643,14 +673,19 @@ export const ExamRoomWorkspace: React.FC = ({ examId, onBack }) => {

Từ khóa tiến trình hoặc tiêu đề được phép (ngăn cách bằng dấu phẩy).

+ {!editable && canEditApps && ( +

+ 💡 Phòng thi đã đẩy hoặc đang diễn ra, bạn vẫn có thể thay đổi danh sách ứng dụng được phép bên dưới. +

+ )}