강좌 삭제 구현1

This commit is contained in:
2025-11-28 21:37:05 +09:00
parent 03b4fa108a
commit 5a26d96386
6 changed files with 877 additions and 250 deletions

View File

@@ -26,13 +26,16 @@ export default function LessonEditPage() {
const [courseVideoCount, setCourseVideoCount] = useState(0);
const [courseVideoFiles, setCourseVideoFiles] = useState<string[]>([]);
const [courseVideoFileObjects, setCourseVideoFileObjects] = useState<File[]>([]);
const [courseVideoFileKeys, setCourseVideoFileKeys] = useState<string[]>([]);
const [existingVideoFiles, setExistingVideoFiles] = useState<Array<{fileName: string, fileKey?: string, url?: string}>>([]);
const [vrContentCount, setVrContentCount] = useState(0);
const [vrContentFiles, setVrContentFiles] = useState<string[]>([]);
const [vrContentFileObjects, setVrContentFileObjects] = useState<File[]>([]);
const [vrContentFileKeys, setVrContentFileKeys] = useState<string[]>([]);
const [existingVrFiles, setExistingVrFiles] = useState<Array<{fileName: string, fileKey?: string, url?: string}>>([]);
const [questionFileCount, setQuestionFileCount] = useState(0);
const [questionFileObject, setQuestionFileObject] = useState<File | null>(null);
const [questionFileKey, setQuestionFileKey] = useState<string | null>(null);
const [existingQuestionFile, setExistingQuestionFile] = useState<{fileName: string, fileKey?: string} | null>(null);
// 원본 데이터 저장 (변경사항 비교용)
@@ -271,61 +274,41 @@ export default function LessonEditPage() {
return;
}
// 새로 업로드된 파일들 처리
// 이미 업로드된 fileKey 배열 사용
let videoUrl: string | undefined;
let webglUrl: string | undefined;
let csvUrl: string | undefined;
// 강좌 영상 업로드 (새 파일이 있는 경우)
if (courseVideoFileObjects.length > 0) {
try {
const uploadResponse = await apiService.uploadFiles(courseVideoFileObjects);
// 다중 파일 업로드 응답 처리 (실제 API 응답 구조에 맞게 조정 필요)
if (uploadResponse.data && uploadResponse.data.length > 0) {
videoUrl = uploadResponse.data[0].url || uploadResponse.data[0].fileKey;
}
} catch (error) {
console.error('강좌 영상 업로드 실패:', error);
throw new Error('강좌 영상 업로드에 실패했습니다.');
}
// 강좌 영상 fileKey (새로 업로드된 파일이 있는 경우)
if (courseVideoFileKeys.length > 0) {
videoUrl = courseVideoFileKeys[0]; // 첫 번째 fileKey 사용
// TODO: API가 배열을 받는 경우 courseVideoFileKeys 배열 전체를 저장해야 함
} else if (existingVideoFiles.length > 0 && existingVideoFiles[0].url) {
// 기존 파일 URL 유지
videoUrl = existingVideoFiles[0].url;
} else if (existingVideoFiles.length > 0 && existingVideoFiles[0].fileKey) {
// 기존 파일 fileKey 사용
videoUrl = existingVideoFiles[0].fileKey;
}
// VR 콘텐츠 업로드 (새 파일이 있는 경우)
if (vrContentFileObjects.length > 0) {
try {
const uploadResponse = await apiService.uploadFiles(vrContentFileObjects);
if (uploadResponse.data && uploadResponse.data.length > 0) {
webglUrl = uploadResponse.data[0].url || uploadResponse.data[0].fileKey;
}
} catch (error) {
console.error('VR 콘텐츠 업로드 실패:', error);
throw new Error('VR 콘텐츠 업로드에 실패했습니다.');
}
// VR 콘텐츠 fileKey (새로 업로드된 파일이 있는 경우)
if (vrContentFileKeys.length > 0) {
webglUrl = vrContentFileKeys[0]; // 첫 번째 fileKey 사용
// TODO: API가 배열을 받는 경우 vrContentFileKeys 배열 전체를 저장해야 함
} else if (existingVrFiles.length > 0 && existingVrFiles[0].url) {
// 기존 파일 URL 유지
webglUrl = existingVrFiles[0].url;
} else if (existingVrFiles.length > 0 && existingVrFiles[0].fileKey) {
// 기존 파일 fileKey 사용
webglUrl = existingVrFiles[0].fileKey;
}
// 학습 평가 문제 업로드 (새 파일이 있는 경우)
if (questionFileObject) {
try {
const uploadResponse = await apiService.uploadFile(questionFileObject);
const fileKey = uploadResponse.data?.key || uploadResponse.data?.fileKey || uploadResponse.data?.csvKey;
if (fileKey) {
csvUrl = `csv/${fileKey}`;
} else if (uploadResponse.data?.url) {
csvUrl = uploadResponse.data.url;
}
} catch (error) {
console.error('학습 평가 문제 업로드 실패:', error);
throw new Error('학습 평가 문제 업로드에 실패했습니다.');
}
// 학습 평가 문제 fileKey (새로 업로드된 파일이 있는 경우)
if (questionFileKey) {
csvUrl = questionFileKey;
} else if (existingQuestionFile?.fileKey) {
// 기존 파일 URL 유지
csvUrl = `csv/${existingQuestionFile.fileKey}`;
// 기존 파일 fileKey 사용
csvUrl = existingQuestionFile.fileKey;
} else if (originalData.csvUrl) {
// 원본 데이터의 csvUrl 유지
csvUrl = originalData.csvUrl;
@@ -634,36 +617,74 @@ export default function LessonEditPage() {
if (!files) return;
const MAX_SIZE = 30 * 1024 * 1024; // 30MB
const MAX_COUNT = 10; // 최대 10개
const validFiles: File[] = [];
const oversizedFiles: string[] = [];
const invalidTypeFiles: string[] = [];
Array.from(files).forEach((file) => {
// mp4 파일인지 확인
if (!file.name.toLowerCase().endsWith('.mp4')) {
invalidTypeFiles.push(file.name);
return;
}
if (file.size < MAX_SIZE) {
// 각 파일이 30MB 이하인지 검사
if (file.size <= MAX_SIZE) {
validFiles.push(file);
} else {
oversizedFiles.push(file.name);
}
});
if (oversizedFiles.length > 0) {
alert(`다음 파일은 30MB 미만이어야 합니다:\n${oversizedFiles.join('\n')}`);
// 파일 타입 오류
if (invalidTypeFiles.length > 0) {
alert(`다음 파일은 MP4 형식만 가능합니다:\n${invalidTypeFiles.join('\n')}`);
}
if (existingVideoFiles.length + courseVideoFiles.length + validFiles.length > 10) {
alert('강좌 영상은 최대 10개까지 첨부할 수 있습니다.');
// 30MB 초과 파일이 있으면 알림
if (oversizedFiles.length > 0) {
alert(`다음 파일은 30MB 이하여야 합니다:\n${oversizedFiles.join('\n')}`);
}
// 파일 개수 제한 확인
const totalCount = existingVideoFiles.length + courseVideoFiles.length + validFiles.length;
if (totalCount > MAX_COUNT) {
const currentCount = existingVideoFiles.length + courseVideoFiles.length;
const availableCount = MAX_COUNT - currentCount;
alert(`강좌 영상은 최대 ${MAX_COUNT}개까지 첨부할 수 있습니다. (현재 ${currentCount}개, 추가 가능 ${availableCount > 0 ? availableCount : 0}개)`);
e.target.value = '';
return;
}
// 30MB 초과 파일이나 잘못된 타입 파일만 있는 경우 중단
if (validFiles.length === 0 && (oversizedFiles.length > 0 || invalidTypeFiles.length > 0)) {
e.target.value = '';
return;
}
if (validFiles.length > 0) {
try {
await apiService.uploadFiles(validFiles);
setCourseVideoFiles(prev => [...prev, ...validFiles.map(f => f.name)]);
setCourseVideoFileObjects(prev => [...prev, ...validFiles]);
setCourseVideoCount(prev => prev + validFiles.length);
// 다중 파일 업로드
const uploadResponse = await apiService.uploadFiles(validFiles);
// 응답에서 fileKey 배열 추출
const fileKeys: string[] = [];
if (uploadResponse.data?.results && Array.isArray(uploadResponse.data.results)) {
uploadResponse.data.results.forEach((result: any) => {
if (result.ok && result.fileKey) {
fileKeys.push(result.fileKey);
}
});
}
if (fileKeys.length > 0) {
setCourseVideoFiles(prev => [...prev, ...validFiles.map(f => f.name)]);
setCourseVideoFileObjects(prev => [...prev, ...validFiles]);
setCourseVideoFileKeys(prev => [...prev, ...fileKeys]);
setCourseVideoCount(prev => prev + validFiles.length);
} else {
throw new Error('파일 업로드는 완료되었지만 fileKey를 받지 못했습니다.');
}
} catch (error) {
console.error('강좌 영상 업로드 실패:', error);
alert('파일 업로드에 실패했습니다. 다시 시도해주세요.');
@@ -717,6 +738,7 @@ export default function LessonEditPage() {
onClick={() => {
setCourseVideoFiles(prev => prev.filter((_, i) => i !== index));
setCourseVideoFileObjects(prev => prev.filter((_, i) => i !== index));
setCourseVideoFileKeys(prev => prev.filter((_, i) => i !== index));
setCourseVideoCount(prev => prev - 1);
}}
className="size-[16px] flex items-center justify-center cursor-pointer hover:opacity-70 transition-opacity shrink-0"
@@ -754,36 +776,74 @@ export default function LessonEditPage() {
if (!files) return;
const MAX_SIZE = 30 * 1024 * 1024; // 30MB
const MAX_COUNT = 10; // 최대 10개
const validFiles: File[] = [];
const oversizedFiles: string[] = [];
const invalidTypeFiles: string[] = [];
Array.from(files).forEach((file) => {
// zip 파일인지 확인
if (!file.name.toLowerCase().endsWith('.zip')) {
invalidTypeFiles.push(file.name);
return;
}
if (file.size < MAX_SIZE) {
// 각 파일이 30MB 이하인지 검사
if (file.size <= MAX_SIZE) {
validFiles.push(file);
} else {
oversizedFiles.push(file.name);
}
});
if (oversizedFiles.length > 0) {
alert(`다음 파일은 30MB 미만이어야 합니다:\n${oversizedFiles.join('\n')}`);
// 파일 타입 오류
if (invalidTypeFiles.length > 0) {
alert(`다음 파일은 ZIP 형식만 가능합니다:\n${invalidTypeFiles.join('\n')}`);
}
if (existingVrFiles.length + vrContentFiles.length + validFiles.length > 10) {
alert('VR 콘텐츠는 최대 10개까지 첨부할 수 있습니다.');
// 30MB 초과 파일이 있으면 알림
if (oversizedFiles.length > 0) {
alert(`다음 파일은 30MB 이하여야 합니다:\n${oversizedFiles.join('\n')}`);
}
// 파일 개수 제한 확인
const totalCount = existingVrFiles.length + vrContentFiles.length + validFiles.length;
if (totalCount > MAX_COUNT) {
const currentCount = existingVrFiles.length + vrContentFiles.length;
const availableCount = MAX_COUNT - currentCount;
alert(`VR 콘텐츠는 최대 ${MAX_COUNT}개까지 첨부할 수 있습니다. (현재 ${currentCount}개, 추가 가능 ${availableCount > 0 ? availableCount : 0}개)`);
e.target.value = '';
return;
}
// 30MB 초과 파일이나 잘못된 타입 파일만 있는 경우 중단
if (validFiles.length === 0 && (oversizedFiles.length > 0 || invalidTypeFiles.length > 0)) {
e.target.value = '';
return;
}
if (validFiles.length > 0) {
try {
await apiService.uploadFiles(validFiles);
setVrContentFiles(prev => [...prev, ...validFiles.map(f => f.name)]);
setVrContentFileObjects(prev => [...prev, ...validFiles]);
setVrContentCount(prev => prev + validFiles.length);
// 다중 파일 업로드
const uploadResponse = await apiService.uploadFiles(validFiles);
// 응답에서 fileKey 배열 추출
const fileKeys: string[] = [];
if (uploadResponse.data?.results && Array.isArray(uploadResponse.data.results)) {
uploadResponse.data.results.forEach((result: any) => {
if (result.ok && result.fileKey) {
fileKeys.push(result.fileKey);
}
});
}
if (fileKeys.length > 0) {
setVrContentFiles(prev => [...prev, ...validFiles.map(f => f.name)]);
setVrContentFileObjects(prev => [...prev, ...validFiles]);
setVrContentFileKeys(prev => [...prev, ...fileKeys]);
setVrContentCount(prev => prev + validFiles.length);
} else {
throw new Error('파일 업로드는 완료되었지만 fileKey를 받지 못했습니다.');
}
} catch (error) {
console.error('VR 콘텐츠 업로드 실패:', error);
alert('파일 업로드에 실패했습니다. 다시 시도해주세요.');
@@ -837,6 +897,7 @@ export default function LessonEditPage() {
onClick={() => {
setVrContentFiles(prev => prev.filter((_, i) => i !== index));
setVrContentFileObjects(prev => prev.filter((_, i) => i !== index));
setVrContentFileKeys(prev => prev.filter((_, i) => i !== index));
setVrContentCount(prev => prev - 1);
}}
className="size-[16px] flex items-center justify-center cursor-pointer hover:opacity-70 transition-opacity shrink-0"
@@ -876,10 +937,30 @@ export default function LessonEditPage() {
const file = files[0];
if (file.name.toLowerCase().endsWith('.csv')) {
try {
await apiService.uploadFile(file);
setQuestionFileObject(file);
setQuestionFileCount(1);
setExistingQuestionFile(null);
// 단일 파일 업로드
const uploadResponse = await apiService.uploadFile(file);
// 응답에서 fileKey 추출
let fileKey: string | null = null;
if (uploadResponse.data?.fileKey) {
fileKey = uploadResponse.data.fileKey;
} else if (uploadResponse.data?.key) {
fileKey = uploadResponse.data.key;
} else if (uploadResponse.data?.results && Array.isArray(uploadResponse.data.results) && uploadResponse.data.results.length > 0) {
const result = uploadResponse.data.results[0];
if (result.ok && result.fileKey) {
fileKey = result.fileKey;
}
}
if (fileKey) {
setQuestionFileObject(file);
setQuestionFileKey(fileKey);
setQuestionFileCount(1);
setExistingQuestionFile(null);
} else {
throw new Error('파일 업로드는 완료되었지만 fileKey를 받지 못했습니다.');
}
} catch (error) {
console.error('학습 평가 문제 업로드 실패:', error);
alert('파일 업로드에 실패했습니다. 다시 시도해주세요.');
@@ -900,6 +981,7 @@ export default function LessonEditPage() {
type="button"
onClick={() => {
setQuestionFileObject(null);
setQuestionFileKey(null);
setExistingQuestionFile(null);
setQuestionFileCount(0);
}}