{thread.title}
{thread.category && ( {thread.category} )}添付ファイル
投稿: {thread.createdAt} / 更新: {thread.updatedAt}
コメント ({comments.total})
{comments.items.map((comment) => ({comment.createdAt}
import { redirect } from 'next/navigation'; import { getCurrentUser, toPublicUser } from '@/lib/auth/getCurrentUser'; import { createBoardService } from '@/lib/api/services'; import { Header } from '@/components/layout/Header'; import { ProjectNav } from '@/components/layout/ProjectNav'; import { MarkdownBody } from '@/components/board/MarkdownBody'; import { CommentForm } from '@/components/board/CommentForm'; import { AttachmentList } from '@/components/files/AttachmentList'; import type { AttachmentView } from '@/lib/types'; import { ForbiddenError, NotFoundError } from '@/lib/errors'; export const dynamic = 'force-dynamic'; export default async function ThreadDetailPage({ params, }: { params: Promise<{ projectId: string; threadId: string }>; }) { const user = await getCurrentUser(); if (!user) redirect('/login'); const { projectId, threadId } = await params; const boardService = createBoardService(); let thread; let comments; let attachments: { thread: AttachmentView[]; comments: AttachmentView[] }; try { thread = boardService.getThread(user.id, Number(threadId)); comments = boardService.listComments(user.id, Number(threadId)); attachments = boardService.getAttachments( user.id, thread.id, comments.items.map((c) => c.id) ); } catch (error) { if (error instanceof ForbiddenError || error instanceof NotFoundError) { redirect(`/projects/${projectId}/board`); } throw error; } // コメントIDごとに添付をグループ化 const commentAttachments = new Map( attachments.comments.map((a) => [ a.targetId, [] as typeof attachments.comments, ]) ); for (const a of attachments.comments) { const list = commentAttachments.get(a.targetId); if (list) list.push(a); } return (
添付ファイル
投稿: {thread.createdAt} / 更新: {thread.updatedAt}
{comment.createdAt}