공지사항 삭제등록 등11
This commit is contained in:
@@ -63,20 +63,209 @@ export default function AdminLessonsPage() {
|
||||
learningGoal?: string;
|
||||
}>({});
|
||||
|
||||
// CSV 파싱 함수
|
||||
const parseCsv = (csvText: string): string[][] => {
|
||||
const lines: string[][] = [];
|
||||
let currentLine: string[] = [];
|
||||
let currentField = '';
|
||||
let inQuotes = false;
|
||||
|
||||
for (let i = 0; i < csvText.length; i++) {
|
||||
const char = csvText[i];
|
||||
const nextChar = csvText[i + 1];
|
||||
|
||||
if (char === '"') {
|
||||
if (inQuotes && nextChar === '"') {
|
||||
currentField += '"';
|
||||
i++;
|
||||
} else {
|
||||
inQuotes = !inQuotes;
|
||||
}
|
||||
} else if (char === ',' && !inQuotes) {
|
||||
currentLine.push(currentField.trim());
|
||||
currentField = '';
|
||||
} else if ((char === '\n' || char === '\r') && !inQuotes) {
|
||||
if (char === '\r' && nextChar === '\n') {
|
||||
i++;
|
||||
}
|
||||
if (currentField || currentLine.length > 0) {
|
||||
currentLine.push(currentField.trim());
|
||||
lines.push(currentLine);
|
||||
currentLine = [];
|
||||
currentField = '';
|
||||
}
|
||||
} else {
|
||||
currentField += char;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentField || currentLine.length > 0) {
|
||||
currentLine.push(currentField.trim());
|
||||
lines.push(currentLine);
|
||||
}
|
||||
|
||||
return lines;
|
||||
};
|
||||
|
||||
// CSV 파일에서 행 개수 가져오기
|
||||
const getCsvRowCount = async (lecture: any): Promise<number> => {
|
||||
if (!lecture.csvUrl && !lecture.csvKey) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
const baseURL = process.env.NEXT_PUBLIC_API_BASE_URL || 'https://hrdi.coconutmeet.net';
|
||||
const token = typeof window !== 'undefined' ? localStorage.getItem('token') : null;
|
||||
|
||||
let fileKey: string | null = null;
|
||||
|
||||
// csvUrl에서 fileKey 추출
|
||||
if (lecture.csvUrl) {
|
||||
// 전체 URL인 경우 fileKey 추출
|
||||
if (lecture.csvUrl.startsWith('http://') || lecture.csvUrl.startsWith('https://')) {
|
||||
// URL에서 /api/files/ 이후 부분을 fileKey로 사용
|
||||
const filesIndex = lecture.csvUrl.indexOf('/api/files/');
|
||||
if (filesIndex !== -1) {
|
||||
fileKey = lecture.csvUrl.substring(filesIndex + '/api/files/'.length);
|
||||
// URL 디코딩
|
||||
fileKey = decodeURIComponent(fileKey);
|
||||
} else {
|
||||
// URL에서 마지막 경로를 fileKey로 사용
|
||||
const urlParts = lecture.csvUrl.split('/');
|
||||
fileKey = urlParts[urlParts.length - 1];
|
||||
fileKey = decodeURIComponent(fileKey);
|
||||
}
|
||||
} else if (lecture.csvUrl.startsWith('csv/')) {
|
||||
// "csv/" 접두사 제거
|
||||
fileKey = lecture.csvUrl.substring(4);
|
||||
} else {
|
||||
// 그 외의 경우 fileKey로 사용
|
||||
fileKey = lecture.csvUrl;
|
||||
}
|
||||
} else if (lecture.csvKey) {
|
||||
// csvKey가 있으면 fileKey로 사용
|
||||
fileKey = lecture.csvKey;
|
||||
}
|
||||
|
||||
if (!fileKey) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// /api/files/{fileKey} 형태로 요청
|
||||
const csvUrl = `${baseURL}/api/files/${encodeURIComponent(fileKey)}`;
|
||||
|
||||
const csvResponse = await fetch(csvUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
...(token && { Authorization: `Bearer ${token}` }),
|
||||
},
|
||||
});
|
||||
|
||||
if (csvResponse.ok) {
|
||||
const csvText = await csvResponse.text();
|
||||
const parsed = parseCsv(csvText);
|
||||
// 헤더를 제외한 행 개수 반환
|
||||
return parsed.length > 0 ? parsed.length - 1 : 0;
|
||||
} else {
|
||||
console.warn(`CSV 파일 다운로드 실패: ${csvResponse.status} - ${csvUrl}`);
|
||||
return 0;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('CSV 파일 파싱 실패:', error, lecture);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// 교육과정명 매핑 함수
|
||||
const mapCourseNames = useCallback((lectures: any[]) => {
|
||||
const fetchedLessons: Lesson[] = lectures.map((lecture: any) => {
|
||||
const mapCourseNames = useCallback(async (lectures: any[]) => {
|
||||
// 먼저 기본 정보로 강좌 목록 생성
|
||||
const initialLessons: Lesson[] = lectures.map((lecture: any) => {
|
||||
// 첨부파일 정보 구성
|
||||
const attachmentParts: string[] = [];
|
||||
|
||||
// 강좌 영상 개수 계산
|
||||
if (lecture.videoUrl) {
|
||||
if (Array.isArray(lecture.videoUrl)) {
|
||||
const count = lecture.videoUrl.length;
|
||||
if (count > 0) {
|
||||
attachmentParts.push(`강좌영상 ${count}개`);
|
||||
}
|
||||
} else if (typeof lecture.videoUrl === 'string') {
|
||||
attachmentParts.push('강좌영상 1개');
|
||||
}
|
||||
} else if (lecture.videoKeys && Array.isArray(lecture.videoKeys)) {
|
||||
const count = lecture.videoKeys.length;
|
||||
if (count > 0) {
|
||||
attachmentParts.push(`강좌영상 ${count}개`);
|
||||
}
|
||||
} else if (lecture.videoFileKeys && Array.isArray(lecture.videoFileKeys)) {
|
||||
const count = lecture.videoFileKeys.length;
|
||||
if (count > 0) {
|
||||
attachmentParts.push(`강좌영상 ${count}개`);
|
||||
}
|
||||
} else if (lecture.videoFiles && Array.isArray(lecture.videoFiles)) {
|
||||
const count = lecture.videoFiles.length;
|
||||
if (count > 0) {
|
||||
attachmentParts.push(`강좌영상 ${count}개`);
|
||||
}
|
||||
} else if (lecture.videoKey) {
|
||||
attachmentParts.push('강좌영상 1개');
|
||||
}
|
||||
|
||||
// VR 콘텐츠 개수 계산
|
||||
if (lecture.webglUrl) {
|
||||
if (Array.isArray(lecture.webglUrl)) {
|
||||
const count = lecture.webglUrl.length;
|
||||
if (count > 0) {
|
||||
attachmentParts.push(`VR콘텐츠 ${count}개`);
|
||||
}
|
||||
} else if (typeof lecture.webglUrl === 'string') {
|
||||
attachmentParts.push('VR콘텐츠 1개');
|
||||
}
|
||||
} else if (lecture.vrUrl) {
|
||||
if (Array.isArray(lecture.vrUrl)) {
|
||||
const count = lecture.vrUrl.length;
|
||||
if (count > 0) {
|
||||
attachmentParts.push(`VR콘텐츠 ${count}개`);
|
||||
}
|
||||
} else if (typeof lecture.vrUrl === 'string') {
|
||||
attachmentParts.push('VR콘텐츠 1개');
|
||||
}
|
||||
} else if (lecture.webglKeys && Array.isArray(lecture.webglKeys)) {
|
||||
const count = lecture.webglKeys.length;
|
||||
if (count > 0) {
|
||||
attachmentParts.push(`VR콘텐츠 ${count}개`);
|
||||
}
|
||||
} else if (lecture.vrKeys && Array.isArray(lecture.vrKeys)) {
|
||||
const count = lecture.vrKeys.length;
|
||||
if (count > 0) {
|
||||
attachmentParts.push(`VR콘텐츠 ${count}개`);
|
||||
}
|
||||
} else if (lecture.webglFileKeys && Array.isArray(lecture.webglFileKeys)) {
|
||||
const count = lecture.webglFileKeys.length;
|
||||
if (count > 0) {
|
||||
attachmentParts.push(`VR콘텐츠 ${count}개`);
|
||||
}
|
||||
} else if (lecture.vrFileKeys && Array.isArray(lecture.vrFileKeys)) {
|
||||
const count = lecture.vrFileKeys.length;
|
||||
if (count > 0) {
|
||||
attachmentParts.push(`VR콘텐츠 ${count}개`);
|
||||
}
|
||||
} else if (lecture.webglFiles && Array.isArray(lecture.webglFiles)) {
|
||||
const count = lecture.webglFiles.length;
|
||||
if (count > 0) {
|
||||
attachmentParts.push(`VR콘텐츠 ${count}개`);
|
||||
}
|
||||
} else if (lecture.webglKey || lecture.vrKey) {
|
||||
attachmentParts.push('VR콘텐츠 1개');
|
||||
}
|
||||
if (lecture.csvKey) {
|
||||
attachmentParts.push('평가문제 1개');
|
||||
|
||||
// 학습 평가 문제 개수는 나중에 업데이트
|
||||
// csvUrl이 전체 URL이든 상대 경로든 모두 체크
|
||||
if (lecture.csvUrl || lecture.csvKey) {
|
||||
attachmentParts.push('평가문제 확인 중...');
|
||||
}
|
||||
|
||||
const attachments = attachmentParts.length > 0
|
||||
? attachmentParts.join(', ')
|
||||
: '없음';
|
||||
@@ -99,14 +288,46 @@ export default function AdminLessonsPage() {
|
||||
courseName,
|
||||
lessonName: lecture.title || lecture.lessonName || '',
|
||||
attachments,
|
||||
questionCount: lecture.csvKey ? 1 : 0,
|
||||
questionCount: 0, // 초기값은 0, 나중에 업데이트
|
||||
createdBy: lecture.createdBy || lecture.instructorName || '관리자',
|
||||
createdAt: lecture.createdAt
|
||||
? new Date(lecture.createdAt).toISOString().split('T')[0]
|
||||
: new Date().toISOString().split('T')[0],
|
||||
};
|
||||
});
|
||||
setLessons(fetchedLessons);
|
||||
|
||||
// 초기 목록 설정
|
||||
setLessons(initialLessons);
|
||||
|
||||
// CSV 파일 행 개수 병렬로 가져오기
|
||||
const csvRowCounts = await Promise.all(
|
||||
lectures.map(lecture => getCsvRowCount(lecture))
|
||||
);
|
||||
|
||||
// CSV 행 개수로 업데이트
|
||||
const updatedLessons = initialLessons.map((lesson, index) => {
|
||||
const lecture = lectures[index];
|
||||
const rowCount = csvRowCounts[index];
|
||||
|
||||
// 첨부파일 정보 업데이트
|
||||
let attachments = lesson.attachments;
|
||||
if (lecture.csvUrl || lecture.csvKey) {
|
||||
if (rowCount > 0) {
|
||||
attachments = attachments.replace('평가문제 확인 중...', `평가문제 ${rowCount}개`);
|
||||
} else {
|
||||
// CSV 파일이 있지만 다운로드 실패하거나 행이 없는 경우에도 표시
|
||||
attachments = attachments.replace('평가문제 확인 중...', '평가문제 1개');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...lesson,
|
||||
attachments,
|
||||
questionCount: rowCount,
|
||||
};
|
||||
});
|
||||
|
||||
setLessons(updatedLessons);
|
||||
}, [courses]);
|
||||
|
||||
// 교육과정 목록 가져오기
|
||||
@@ -123,32 +344,57 @@ export default function AdminLessonsPage() {
|
||||
fetchCourses();
|
||||
}, []);
|
||||
|
||||
// 강좌 리스트 조회 함수
|
||||
const fetchLectures = useCallback(async () => {
|
||||
try {
|
||||
const response = await apiService.getLectures();
|
||||
if (response.data && Array.isArray(response.data)) {
|
||||
// 원본 데이터 저장
|
||||
rawLecturesRef.current = response.data;
|
||||
// 교육과정명 매핑 함수 호출
|
||||
await mapCourseNames(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('강좌 리스트 조회 오류:', error);
|
||||
setLessons([]);
|
||||
rawLecturesRef.current = [];
|
||||
}
|
||||
}, [mapCourseNames]);
|
||||
|
||||
// 강좌 리스트 조회
|
||||
useEffect(() => {
|
||||
async function fetchLectures() {
|
||||
try {
|
||||
const response = await apiService.getLectures();
|
||||
if (response.data && Array.isArray(response.data)) {
|
||||
// 원본 데이터 저장
|
||||
rawLecturesRef.current = response.data;
|
||||
// 교육과정명 매핑 함수 호출
|
||||
mapCourseNames(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('강좌 리스트 조회 오류:', error);
|
||||
setLessons([]);
|
||||
rawLecturesRef.current = [];
|
||||
}
|
||||
}
|
||||
|
||||
fetchLectures();
|
||||
}, [mapCourseNames]);
|
||||
}, [fetchLectures]);
|
||||
|
||||
// 페이지 포커스 및 가시성 변경 시 리스트 새로고침
|
||||
useEffect(() => {
|
||||
const handleFocus = () => {
|
||||
fetchLectures();
|
||||
};
|
||||
|
||||
const handleVisibilityChange = () => {
|
||||
if (!document.hidden) {
|
||||
fetchLectures();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('focus', handleFocus);
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('focus', handleFocus);
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||
};
|
||||
}, [fetchLectures]);
|
||||
|
||||
// 교육과정 목록이 로드되면 강좌 리스트의 교육과정명 업데이트
|
||||
useEffect(() => {
|
||||
if (rawLecturesRef.current.length > 0) {
|
||||
mapCourseNames(rawLecturesRef.current);
|
||||
}
|
||||
const updateLectures = async () => {
|
||||
if (rawLecturesRef.current.length > 0) {
|
||||
await mapCourseNames(rawLecturesRef.current);
|
||||
}
|
||||
};
|
||||
updateLectures();
|
||||
}, [mapCourseNames]);
|
||||
|
||||
// 현재 사용자 정보 가져오기
|
||||
@@ -527,16 +773,20 @@ export default function AdminLessonsPage() {
|
||||
const vrFileKeys = vrContentFileKeys;
|
||||
const csvFileKey = questionFileKey;
|
||||
|
||||
// 강좌 영상 fileKey 배열 (모든 fileKey를 배열로 저장)
|
||||
let videoUrl: string[] | undefined;
|
||||
if (videoFileKeys.length > 0) {
|
||||
videoUrl = videoFileKeys; // 모든 fileKey를 배열로 저장
|
||||
// 강좌 영상 fileKey (단일 파일은 문자열, 다중 파일은 배열)
|
||||
let videoUrl: string | string[] | undefined;
|
||||
if (videoFileKeys.length === 1) {
|
||||
videoUrl = videoFileKeys[0]; // 단일 파일은 문자열로
|
||||
} else if (videoFileKeys.length > 1) {
|
||||
videoUrl = videoFileKeys; // 다중 파일은 배열로
|
||||
}
|
||||
|
||||
// VR 콘텐츠 fileKey 배열 (모든 fileKey를 배열로 저장)
|
||||
let webglUrl: string[] | undefined;
|
||||
if (vrFileKeys.length > 0) {
|
||||
webglUrl = vrFileKeys; // 모든 fileKey를 배열로 저장
|
||||
// VR 콘텐츠 fileKey (단일 파일은 문자열, 다중 파일은 배열)
|
||||
let webglUrl: string | string[] | undefined;
|
||||
if (vrFileKeys.length === 1) {
|
||||
webglUrl = vrFileKeys[0]; // 단일 파일은 문자열로
|
||||
} else if (vrFileKeys.length > 1) {
|
||||
webglUrl = vrFileKeys; // 다중 파일은 배열로
|
||||
}
|
||||
|
||||
// 학습 평가 문제 fileKey
|
||||
@@ -560,10 +810,10 @@ export default function AdminLessonsPage() {
|
||||
};
|
||||
|
||||
// 선택적 필드 추가
|
||||
if (videoUrl && videoUrl.length > 0) {
|
||||
if (videoUrl) {
|
||||
requestBody.videoUrl = videoUrl;
|
||||
}
|
||||
if (webglUrl && webglUrl.length > 0) {
|
||||
if (webglUrl) {
|
||||
requestBody.webglUrl = webglUrl;
|
||||
}
|
||||
if (csvKey) {
|
||||
|
||||
Reference in New Issue
Block a user