'use client'; import { useState, useMemo, useRef, ChangeEvent, useEffect } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import AdminSidebar from "@/app/components/AdminSidebar"; import ChevronDownSvg from "@/app/svgs/chevrondownsvg"; import BackArrowSvg from "@/app/svgs/backarrow"; import { type Resource } from "@/app/admin/resources/mockData"; import apiService from "@/app/lib/apiService"; export default function AdminResourcesPage() { const router = useRouter(); const searchParams = useSearchParams(); const [resources, setResources] = useState([]); const [currentPage, setCurrentPage] = useState(1); const [isWritingMode, setIsWritingMode] = useState(false); const [title, setTitle] = useState(''); const [content, setContent] = useState(''); const [attachedFile, setAttachedFile] = useState(null); const [fileKey, setFileKey] = useState(null); const [isLoading, setIsLoading] = useState(false); const [showToast, setShowToast] = useState(false); const fileInputRef = useRef(null); // 날짜를 yyyy-mm-dd 형식으로 포맷팅 const formatDate = (dateString: string): string => { if (!dateString) return ''; try { const date = new Date(dateString); if (isNaN(date.getTime())) { // 이미 yyyy-mm-dd 형식인 경우 그대로 반환 if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) { return dateString; } return dateString; } const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } catch { return dateString; } }; // API에서 학습 자료 목록 가져오기 useEffect(() => { async function fetchResources() { try { setIsLoading(true); const response = await apiService.getLibrary(); const data = response.data; // API 응답이 배열이 아닌 경우 처리 (예: { items: [...] } 형태) let resourcesArray: any[] = []; if (Array.isArray(data)) { resourcesArray = data; } else if (data && typeof data === 'object') { resourcesArray = data.items || data.resources || data.data || data.list || []; } // API 응답 데이터를 Resource 형식으로 변환 const transformedResources: Resource[] = resourcesArray.map((resource: any) => ({ id: resource.id || resource.resourceId || 0, title: resource.title || '', date: resource.date || resource.createdAt || resource.createdDate || new Date().toISOString().split('T')[0], views: resource.views || resource.viewCount || 0, writer: resource.writer || resource.author || resource.createdBy || '관리자', content: resource.content ? (Array.isArray(resource.content) ? resource.content : [resource.content]) : undefined, hasAttachment: resource.hasAttachment || resource.attachment || false, })); setResources(transformedResources); } catch (error) { console.error('학습 자료 목록 조회 오류:', error); // 에러 발생 시 빈 배열로 설정 setResources([]); } finally { setIsLoading(false); } } fetchResources(); }, []); // 수정 완료 쿼리 파라미터 확인 및 토스트 표시 useEffect(() => { if (searchParams.get('updated') === 'true') { setShowToast(true); // URL에서 쿼리 파라미터 제거 router.replace('/admin/resources'); // 토스트 자동 닫기 const timer = setTimeout(() => { setShowToast(false); }, 3000); return () => clearTimeout(timer); } }, [searchParams, router]); const totalCount = useMemo(() => resources.length, [resources]); const characterCount = useMemo(() => content.length, [content]); const handleBack = () => { setIsWritingMode(false); setTitle(''); setContent(''); setAttachedFile(null); setFileKey(null); // 파일 입력 초기화 if (fileInputRef.current) { fileInputRef.current.value = ''; } }; const handleFileAttach = () => { fileInputRef.current?.click(); }; const handleFileChange = async (e: ChangeEvent) => { const file = e.target.files?.[0]; if (file) { if (file.size > 30 * 1024 * 1024) { alert('30MB 미만의 파일만 첨부할 수 있습니다.'); return; } try { setIsLoading(true); // 단일 파일 업로드 const uploadResponse = await apiService.uploadFile(file); // 응답에서 fileKey 추출 let extractedFileKey: string | null = null; if (uploadResponse.data?.fileKey) { extractedFileKey = uploadResponse.data.fileKey; } else if (uploadResponse.data?.key) { extractedFileKey = uploadResponse.data.key; } else if (uploadResponse.data?.id) { extractedFileKey = uploadResponse.data.id; } else if (uploadResponse.data?.imageKey) { extractedFileKey = uploadResponse.data.imageKey; } else if (uploadResponse.data?.fileId) { extractedFileKey = uploadResponse.data.fileId; } else if (uploadResponse.data?.data && (uploadResponse.data.data.key || uploadResponse.data.data.fileKey)) { extractedFileKey = uploadResponse.data.data.key || uploadResponse.data.data.fileKey; } 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) { extractedFileKey = result.fileKey; } } if (extractedFileKey) { setFileKey(extractedFileKey); setAttachedFile(file); } else { throw new Error('파일 키를 받아오지 못했습니다.'); } } catch (error) { console.error('파일 업로드 실패:', error); alert('파일 업로드에 실패했습니다. 다시 시도해주세요.'); setAttachedFile(null); setFileKey(null); } finally { setIsLoading(false); // 파일 입력 초기화 if (fileInputRef.current) { fileInputRef.current.value = ''; } } } }; const handleSave = async () => { if (!title.trim() || !content.trim()) { alert('제목과 내용을 입력해주세요.'); return; } try { setIsLoading(true); // 학습 자료 생성 API 호출 const resourceData: any = { title: title.trim(), content: content.trim(), }; // fileKey와 파일 정보가 있으면 attachments 배열로 포함 if (fileKey && attachedFile) { resourceData.attachments = [ { fileKey: fileKey, filename: attachedFile.name, mimeType: attachedFile.type || 'application/octet-stream', size: attachedFile.size, }, ]; } const response = await apiService.createLibraryItem(resourceData); // API 응답 후 목록 새로고침 const fetchResponse = await apiService.getLibrary(); const data = fetchResponse.data; // API 응답이 배열이 아닌 경우 처리 let resourcesArray: any[] = []; if (Array.isArray(data)) { resourcesArray = data; } else if (data && typeof data === 'object') { resourcesArray = data.items || data.resources || data.data || data.list || []; } // API 응답 데이터를 Resource 형식으로 변환 const transformedResources: Resource[] = resourcesArray.map((resource: any) => ({ id: resource.id || resource.resourceId || 0, title: resource.title || '', date: resource.date || resource.createdAt || resource.createdDate || new Date().toISOString().split('T')[0], views: resource.views || resource.viewCount || 0, writer: resource.writer || resource.author || resource.createdBy || '관리자', content: resource.content ? (Array.isArray(resource.content) ? resource.content : [resource.content]) : undefined, hasAttachment: resource.hasAttachment || resource.attachment || !!resource.fileKey || false, })); setResources(transformedResources); handleBack(); } catch (error) { console.error('학습 자료 저장 실패:', error); alert('학습 자료 저장에 실패했습니다. 다시 시도해주세요.'); } finally { setIsLoading(false); } }; const handleCancel = () => { if (title.trim() || content.trim() || attachedFile || fileKey) { if (confirm('작성 중인 내용이 있습니다. 정말 취소하시겠습니까?')) { handleBack(); } } else { handleBack(); } }; const ITEMS_PER_PAGE = 10; const sortedResources = useMemo(() => { return [...resources].sort((a, b) => { // 생성일 내림차순 정렬 (최신 날짜가 먼저) return b.date.localeCompare(a.date); }); }, [resources]); const totalPages = Math.ceil(sortedResources.length / ITEMS_PER_PAGE); const paginatedResources = useMemo(() => { const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; const endIndex = startIndex + ITEMS_PER_PAGE; return sortedResources.slice(startIndex, endIndex); }, [sortedResources, currentPage]); return (
{/* 메인 레이아웃 */}
{/* 사이드바 */}
{/* 메인 콘텐츠 */}
{isWritingMode ? ( <> {/* 작성 모드 헤더 */}

학습 자료 작성

{/* 작성 폼 */}
{/* 제목 입력 */}
setTitle(e.target.value)} placeholder="제목을 입력해 주세요." className="w-full h-[40px] px-3 py-2 rounded-[8px] border border-[#dee1e6] bg-white text-[16px] font-normal leading-[1.5] text-[#1b2027] placeholder:text-[#b1b8c0] focus:outline-none focus:ring-2 focus:ring-[#1f2b91] focus:border-transparent" />
{/* 내용 입력 */}