import { useState, useCallback, useMemo } from 'react'; import { Link, Navigate, useParams } from 'react-router-dom'; import { MOCK_BUGS, MOCK_BUG_COMMENTS } from '../../data/mockData'; import { useAuth } from '../../contexts/AuthContext'; import { formatDate, formatDateTime } from '../../utils/format'; import type { BugComment, BugReport, BugSeverity, BugStatus } from '../../types'; // ── Helpers ──────────────────────────────────────────────────────────────────── function StatusBadge({ status }: { status: BugStatus }) { const map: Record = { open: 'badge-open', in_progress: 'badge-progress', resolved: 'badge-resolved', closed: 'badge-closed', }; const labels: Record = { open: 'Open', in_progress: 'In Progress', resolved: 'Resolved', closed: 'Closed', }; return {labels[status]}; } function SeverityBadge({ severity }: { severity: BugSeverity }) { const map: Record = { low: 'badge-low', medium: 'badge-medium', high: 'badge-high', critical: 'badge-critical', }; return {severity}; } // ── Comment component ────────────────────────────────────────────────────────── function CommentItem({ comment }: { comment: BugComment }) { return (
{comment.authorName} {formatDateTime(comment.createdAt)}
{comment.content}
); } // ── Bug Detail Page ──────────────────────────────────────────────────────────── export default function BugDetailPage() { const { id } = useParams<{ id: string }>(); const { user, isAuthenticated } = useAuth(); // Local state — mirrors the global bug list in memory const [bugs, setBugs] = useState(MOCK_BUGS); const [comments, setComments] = useState(MOCK_BUG_COMMENTS); const [newComment, setNewComment] = useState(''); const [commentError, setCommentError] = useState(''); const [submitting, setSubmitting] = useState(false); const bug = useMemo(() => bugs.find((b) => b.id === id), [bugs, id]); const bugComments = useMemo( () => comments.filter((c) => c.bugReportId === id).sort((a, b) => a.createdAt.localeCompare(b.createdAt)), [comments, id] ); // "I have this too" logic const alreadyVoted = useMemo( () => !!user && !!bug && bug.meTooBugs.includes(user.id), [user, bug] ); const isOwnReport = useMemo( () => !!user && !!bug && bug.submittedById === user.id, [user, bug] ); const handleMeToo = useCallback(() => { if (!user || !bug || alreadyVoted || isOwnReport) return; setBugs((prev) => prev.map((b) => b.id === bug.id ? { ...b, meTooBugs: [...b.meTooBugs, user.id] } : b ) ); }, [user, bug, alreadyVoted, isOwnReport]); const handleComment = useCallback(async () => { if (!user) return; if (!newComment.trim()) { setCommentError('Comment cannot be empty.'); return; } if (newComment.trim().length < 5) { setCommentError('Comment must be at least 5 characters.'); return; } setCommentError(''); setSubmitting(true); await new Promise((r) => setTimeout(r, 250)); const comment: BugComment = { id: `bc${Date.now()}`, bugReportId: id!, authorId: user.id, authorName: user.username, content: newComment.trim(), createdAt: new Date().toISOString(), }; setComments((prev) => [...prev, comment]); setNewComment(''); setSubmitting(false); }, [user, newComment, id]); if (!bug) { return ; } const metooCount = bug.meTooBugs.length; return (
{/* Breadcrumb */}
Bug Reports {' '}>{' '} {bug.uniqueCode}
{/* Header */}
{/* Badges */}
{bug.uniqueCode}
{/* Title */}

{bug.title}

{/* Meta grid */}
{[ { label: 'Submitted by', value: bug.submittedByName }, { label: 'Date', value: formatDate(bug.createdAt) }, { label: 'Game Version', value: `v${bug.gameVersion}` }, { label: 'Assigned to', value: bug.assignedToName ?? 'Unassigned' }, ].map(({ label, value }) => (
{label}
{value}
))}
{/* Description */}
Description
{bug.description}
{/* Steps to reproduce */}
Steps to Reproduce
{bug.stepsToReproduce}
{/* "I have this too" section */}
{/* Count */}
{metooCount}{' '} {metooCount === 1 ? 'user has' : 'users have'} this issue
{/* Button logic */} {!isAuthenticated ? (
Login to confirm you have this issue
) : isOwnReport ? ( (this is your report) ) : alreadyVoted ? (
✓ You reported this too
) : ( )}
{/* Comments section */}
Discussion {bugComments.length}
{/* Comment list */} {bugComments.length === 0 ? (
No comments yet. Be the first to comment.
) : ( bugComments.map((comment) => ( )) )} {/* Add comment */}
{isAuthenticated ? (
Add a Comment