import RootLayout from "~/layout/RootLayout"; import type { Route } from "../+types/root"; import { useNavigate, useParams } from "react-router"; import { useEffect, useState, type FormEventHandler } from "react"; import { API } from "~/api/api"; import Icon, { ICONS } from "~/components/Icon"; import { getTagColor } from "../utils/tags"; import type { Post } from "~/types/post"; import Modal from "~/components/Modal"; import type { Comment } from "~/types/comment"; import Loading from "~/components/Loading"; import { getApiErrorMessage, useAuth } from "~/context/AuthContext"; import { NotFound } from "~/components/NotFound"; import type { PaginatedResponse } from "~/types/PaginatedResponse"; type ActionButtonProps = { onClick?: React.MouseEventHandler; icon: string; children: React.ReactNode; className?: string; }; export function meta({}: Route.MetaArgs) { return [ { title: "ImgBoard - Viewing Post" }, ]; } function formatDate(dateString: string): string { const date = new Date(dateString); return date.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } export default function Post() { const { id } = useParams(); const navigate = useNavigate(); if (!id) { return } const idNum = parseInt(id, 10); if (isNaN(idNum)) { return } const [post, setPost] = useState(null); const [notFound, setNotFound] = useState(false); const [isEditPostModalOpen, setIsEditPostModalOpen] = useState(false); const [paginatedComments, setPaginatedComments] = useState | null>(null); const [currentCommentPage, setCurrentCommentPage] = useState(1); const [commentToEdit, setCommentToEdit] = useState(null); const [isEditCommentModalOpen, setIsEditCommentModalOpen] = useState(false); console.log("Post ID from params:", id); useEffect(() => { // Fetch post details using the id console.log(`Fetching post with id: ${id}`); async function fetchData() { try { const postData = await API.fetchPostById(idNum); setPost(postData); } catch (error: any) { if (error.status === 404) { setNotFound(true); } else { console.error("Failed to fetch post:", error); // Optionally, you could navigate to a generic error page // or show a toast notification. alert(getApiErrorMessage(error)); } } } fetchData(); }, [id]); useEffect(() => { async function fetchComments() { const comments = await API.fetchCommentsByPostId(idNum, currentCommentPage); setPaginatedComments(comments); } fetchComments(); }, [id, currentCommentPage]); const { user } = useAuth(); if (notFound) { return ; } if (!post) { return ; } /** * Button component for actions like Edit, Delete, View Raw, etc. */ const ActionButton: React.FC = ({ onClick, icon, children, className }) => ( ); const Pagination = ({ itemsPerPage, totalItems, paginate, currentPage, }: { itemsPerPage: number; totalItems: number; paginate: (pageNumber: number) => void; currentPage: number; }) => { const pageNumbers = []; for (let i = 1; i <= Math.ceil(totalItems / itemsPerPage); i++) { pageNumbers.push(i); } if(pageNumbers.length <= 1) return null; return ( ); }; const EditPostModal = () => { const [title, setTitle] = useState(''); const [description, setDescription] = useState(''); const [tags, setTags] = useState(''); const [error, setError] = useState(''); useEffect(() => { if (post) { setTitle(post.title); setDescription(post.description); setTags(post.tags.map(t => t.name).join(' ')); } }, [post]); const handleSubmit: FormEventHandler = async (e) => { e.preventDefault(); setError(''); if (!user) return; const enteredTags = tags.split(' ').filter(t => t); if (title && description && tags) { try { const res = await API.updatePost(post.id, title, description, enteredTags, user); const data = await res.json(); if (!res.ok) { setError(getApiErrorMessage(data)); return; } setPost(data); setIsEditPostModalOpen(false); } catch (err: any) { setError(getApiErrorMessage(err)); } } }; if (!post) return null; return ( setIsEditPostModalOpen(false)}>

Edit Post

setTitle(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700"/>
{error &&

{error}

} ); }; const EditCommentModal = () => { const [newText, setNewText] = useState(commentToEdit?.text || ''); useEffect(() => { if (commentToEdit) { setNewText(commentToEdit.text); } }, [commentToEdit]); const handleSubmit: FormEventHandler = async (e) => { e.preventDefault(); if (newText && commentToEdit && newText !== commentToEdit.text && user) { try { const res = await API.updateComment(post.id, commentToEdit.id, newText, user); if (!res.ok) { const err = await res.json(); alert(`Failed to update comment: ${getApiErrorMessage(err)}`); return; } setPaginatedComments(prev => prev ? { ...prev, items: prev.items.map(c => c.id === commentToEdit.id ? {...c, text: newText} : c) } : null); } catch (err: any) { alert(`Failed to update comment: ${getApiErrorMessage(err)}`); } } setIsEditCommentModalOpen(false) }; return ( setIsEditCommentModalOpen(false)}>

Edit Comment