'use client'; import { useEffect, useState, useMemo } from 'react'; import { useRouter } from 'next/navigation'; import ChevronDownSvg from '../../svgs/chevrondownsvg'; // 드롭다운 아이콘 컴포넌트 function ArrowDownIcon({ className }: { className?: string }) { return ( ); } // 검색 아이콘 컴포넌트 function SearchIcon({ className }: { className?: string }) { return ( ); } // 태그 컴포넌트 function StatusTag({ text, type = 'default', color = 'primary' }: { text: string; type?: 'default' | 'emphasis'; color?: 'primary' | 'gray' }) { if (type === 'default' && color === 'primary') { return (
{text}
); } if (type === 'default' && color === 'gray') { return (
{text}
); } return (
{text}
); } type LearnerProgress = { id: string; courseName: string; lessonName: string; learnerName: string; enrollmentDate: string; lastStudyDate: string; progressRate: number; hasSubmitted: boolean; score: number | null; isCompleted: boolean; }; export default function InstructorCoursesPage() { const router = useRouter(); const [userRole, setUserRole] = useState(''); const [isLoading, setIsLoading] = useState(true); const [currentPage, setCurrentPage] = useState(1); // 필터 상태 const [selectedCourse, setSelectedCourse] = useState('all'); const [selectedSubmissionStatus, setSelectedSubmissionStatus] = useState('all'); const [selectedCompletionStatus, setSelectedCompletionStatus] = useState('all'); const [searchQuery, setSearchQuery] = useState(''); // 드롭다운 열림 상태 const [isCourseDropdownOpen, setIsCourseDropdownOpen] = useState(false); const [isSubmissionDropdownOpen, setIsSubmissionDropdownOpen] = useState(false); const [isCompletionDropdownOpen, setIsCompletionDropdownOpen] = useState(false); // 데이터 const [courses, setCourses] = useState<{ id: string; name: string }[]>([]); const [learnerProgress, setLearnerProgress] = useState([]); const ITEMS_PER_PAGE = 10; // 사용자 정보 및 권한 확인 useEffect(() => { let isMounted = true; async function fetchUserInfo() { try { const localStorageToken = localStorage.getItem('token'); const cookieToken = document.cookie .split('; ') .find(row => row.startsWith('token=')) ?.split('=')[1]; const token = localStorageToken || cookieToken; if (!token) { router.push('/login'); return; } const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL ? `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/me` : 'https://hrdi.coconutmeet.net/auth/me'; const response = await fetch(apiUrl, { method: 'GET', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, }); if (!response.ok) { if (response.status === 401) { localStorage.removeItem('token'); document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'; if (isMounted) { router.push('/login'); } } return; } const data = await response.json(); if (isMounted) { const role = data.role || data.userRole || ''; setUserRole(role); // admin이 아니면 접근 불가 if (role !== 'ADMIN' && role !== 'admin') { router.push('/'); return; } } } catch (error) { console.error('사용자 정보 조회 오류:', error); if (isMounted) { router.push('/login'); } } } fetchUserInfo(); return () => { isMounted = false; }; }, [router]); // 교육 과정 목록 가져오기 useEffect(() => { async function fetchCourses() { try { const token = localStorage.getItem('token') || document.cookie .split('; ') .find(row => row.startsWith('token=')) ?.split('=')[1]; const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL ? `${process.env.NEXT_PUBLIC_API_BASE_URL}/subjects` : 'https://hrdi.coconutmeet.net/subjects'; const response = await fetch(apiUrl, { method: 'GET', headers: { 'Content-Type': 'application/json', ...(token && { Authorization: `Bearer ${token}` }), }, }); if (response.ok) { const data = await response.json(); const coursesArray = Array.isArray(data) ? data : (data.items || data.courses || data.data || []); setCourses(coursesArray.map((item: any) => ({ id: String(item.id || item.subjectId || ''), name: item.courseName || item.name || item.subjectName || '', }))); } } catch (error) { console.error('교육 과정 목록 조회 오류:', error); } } fetchCourses(); }, []); // 학습자 진행 상황 데이터 (더미 데이터 - 실제 API로 교체 필요) useEffect(() => { async function fetchLearnerProgress() { try { setIsLoading(true); // TODO: 실제 API 호출로 교체 // 현재는 더미 데이터 사용 const dummyData: LearnerProgress[] = [ { id: '1', courseName: '방사선 관리', lessonName: '{강좌명}', learnerName: '김하늘', enrollmentDate: '2025-09-10', lastStudyDate: '2025-09-10', progressRate: 100, hasSubmitted: true, score: 100, isCompleted: true, }, { id: '2', courseName: '방사선 관리', lessonName: '{강좌명}', learnerName: '김하늘', enrollmentDate: '2025-09-10', lastStudyDate: '2025-09-10', progressRate: 100, hasSubmitted: true, score: 100, isCompleted: true, }, { id: '3', courseName: '방사선 관리', lessonName: '{강좌명}', learnerName: '김하늘', enrollmentDate: '2025-09-10', lastStudyDate: '2025-09-10', progressRate: 100, hasSubmitted: true, score: 100, isCompleted: true, }, { id: '4', courseName: '원자로 운전 및 계통', lessonName: '6. 원자로 시동, 운전 및 정지 절차', learnerName: '김하늘', enrollmentDate: '2025-09-10', lastStudyDate: '2025-09-10', progressRate: 100, hasSubmitted: true, score: 60, isCompleted: true, }, { id: '5', courseName: '방사선 관리', lessonName: '{강좌명}', learnerName: '김하늘', enrollmentDate: '2025-09-10', lastStudyDate: '2025-09-10', progressRate: 100, hasSubmitted: true, score: 30, isCompleted: false, }, { id: '6', courseName: '방사선 관리', lessonName: '{강좌명}', learnerName: '김하늘', enrollmentDate: '2025-09-10', lastStudyDate: '2025-09-10', progressRate: 100, hasSubmitted: true, score: 30, isCompleted: false, }, { id: '7', courseName: '방사선 관리', lessonName: '{강좌명}', learnerName: '김하늘', enrollmentDate: '2025-09-10', lastStudyDate: '2025-09-10', progressRate: 100, hasSubmitted: false, score: null, isCompleted: false, }, ]; setLearnerProgress(dummyData); } catch (error) { console.error('학습자 진행 상황 조회 오류:', error); } finally { setIsLoading(false); } } fetchLearnerProgress(); }, []); // 필터링된 데이터 const filteredData = useMemo(() => { return learnerProgress.filter((item) => { // 교육 과정 필터 if (selectedCourse !== 'all' && item.courseName !== selectedCourse) { return false; } // 문제 제출 여부 필터 if (selectedSubmissionStatus === 'submitted' && !item.hasSubmitted) { return false; } if (selectedSubmissionStatus === 'not-submitted' && item.hasSubmitted) { return false; } // 수료 여부 필터 if (selectedCompletionStatus === 'completed' && !item.isCompleted) { return false; } if (selectedCompletionStatus === 'not-completed' && item.isCompleted) { return false; } // 검색어 필터 if (searchQuery && !item.learnerName.toLowerCase().includes(searchQuery.toLowerCase())) { return false; } return true; }); }, [learnerProgress, selectedCourse, selectedSubmissionStatus, selectedCompletionStatus, searchQuery]); // 페이지네이션 const totalPages = Math.ceil(filteredData.length / ITEMS_PER_PAGE); const paginatedData = useMemo(() => { const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; const endIndex = startIndex + ITEMS_PER_PAGE; return filteredData.slice(startIndex, endIndex); }, [filteredData, currentPage]); // 드롭다운 외부 클릭 감지 useEffect(() => { const handleClickOutside = (event: MouseEvent) => { const target = event.target as HTMLElement; if (!target.closest('.dropdown-container')) { setIsCourseDropdownOpen(false); setIsSubmissionDropdownOpen(false); setIsCompletionDropdownOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, []); return (

강좌 현황

{/* 필터 및 검색 영역 */}
{/* 교육 과정 드롭다운 */}
{isCourseDropdownOpen && (
{courses.map((course) => ( ))}
)}
{/* 문제 제출 여부 드롭다운 */}
{isSubmissionDropdownOpen && (
)}
{/* 수료 여부 드롭다운 */}
{isCompletionDropdownOpen && (
)}
{/* 검색바 */}
setSearchQuery(e.target.value)} className="flex-1 text-[16px] font-normal leading-[1.5] text-[#b1b8c0] outline-none placeholder:text-[#b1b8c0]" />
{/* 테이블 영역 */}
{isLoading ? (

로딩 중...

) : filteredData.length === 0 ? (

데이터가 없습니다.

) : ( <> {/* 테이블 헤더 */}
교육 과정명
강좌명
학습자명
가입일
마지막 수강일
진도율
문제 제출 여부
평가 점수
수료 여부
{/* 테이블 바디 */}
{paginatedData.map((item) => (
{item.courseName}
{item.lessonName}
{item.learnerName}
{item.enrollmentDate}
{item.lastStudyDate}
{item.progressRate}%
{item.hasSubmitted ? ( ) : ( )}
{item.score !== null ? `${item.score}점` : '-'}
{item.isCompleted ? ( ) : ( )}
))}
)}
{/* 페이지네이션 */} {filteredData.length > ITEMS_PER_PAGE && (
{/* First */} {/* Prev */} {/* Page Numbers */} {Array.from({ length: Math.min(10, totalPages) }, (_, i) => { const pageNum = i + 1; const isActive = pageNum === currentPage; return ( ); })} {/* Next */} {/* Last */}
)}
); }