"use client"; import { useEffect, useState, useMemo } from "react"; import { useRouter, useParams } from "next/navigation"; import Image from "next/image"; import { getAnnouncementById, deleteAnnouncement, getDownloadUrl, type Announcement, type AnnouncementFile, } from "@/lib/services"; import { useAuth, useImageModal } from "@/hooks"; import { Download } from "lucide-react"; const IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "webp"]; export default function AnnouncementDetailPage() { const [announcement, setAnnouncement] = useState(null); const [isLoading, setIsLoading] = useState(true); const router = useRouter(); const params = useParams(); const id = params.id as string; const { user } = useAuth(); // 이미지 파일만 필터링 (API에서 signedUrl 포함) const imageFiles = useMemo(() => { if (!announcement?.files) return []; return announcement.files.filter((file) => { const ext = file.fileName.split(".").pop()?.toLowerCase(); return IMAGE_EXTENSIONS.includes(ext || "") && file.signedUrl; }) as (AnnouncementFile & { signedUrl: string })[]; }, [announcement?.files]); const { selectedIndex, isOpen, open, close, next, prev } = useImageModal(imageFiles.length); useEffect(() => { loadData(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [id]); const loadData = async () => { try { // 공지사항 상세 불러오기 (signedUrl 포함) const announcementData = await getAnnouncementById(parseInt(id)); setAnnouncement(announcementData); } catch { alert("공지사항을 불러올 수 없습니다."); router.push("/announcements"); } finally { setIsLoading(false); } }; const handleDelete = async () => { if (!announcement || !user) return; if (announcement.authorId !== user.id) { alert("삭제 권한이 없습니다."); return; } if (!confirm("정말 삭제하시겠습니까?")) return; try { await deleteAnnouncement(announcement.id); alert("삭제되었습니다."); router.push("/announcements"); } catch (err) { const errorMessage = err instanceof Error ? err.message : "삭제에 실패했습니다."; alert(errorMessage); } }; const formatDate = (dateString: string) => { const date = new Date(dateString); return date.toLocaleString("ko-KR", { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", }); }; const handleDownloadAll = async () => { for (const img of imageFiles) { try { const downloadUrl = await getDownloadUrl(img.fileKey, img.fileName); window.open(downloadUrl, '_blank'); } catch (error) { console.error("Download failed:", error); } } }; if (isLoading) { return (
); } if (!announcement) { return null; } const isAuthor = user && announcement.authorId === user.id; return (
{/* 헤더 */}
{announcement.isImportant && ( 중요 )}

{announcement.title}

작성자: {announcement.author.userName} {formatDate(announcement.createdAt)} 조회수: {announcement.viewCount}
{isAuthor && (
)}
{/* 이미지 갤러리 */} {imageFiles.length > 0 && (
{imageFiles.map((img, index) => (
open(index)} > {`${announcement.title}
))} {/* 전체 다운로드 버튼 */}
)} {/* 이미지가 없는 경우 */} {imageFiles.length === 0 && (
첨부된 이미지가 없습니다.
)}
{/* 이미지 모달 */} {isOpen && selectedIndex !== null && (
{imageFiles[selectedIndex]?.signedUrl && ( {`${announcement.title} )}
{selectedIndex + 1} / {imageFiles.length}
)}
); }