Files
portfolio/app/Projects/[slug]/page.tsx
Denshooter af35a071a7 refactor: clean up project details URL handling
Removes unnecessary TypeScript error suppression for window 
object usage in the ProjectDetails component. This change 
improves code clarity and maintains functionality by ensuring 
the project data is removed from the URL without reloading 
the page.
2025-02-12 17:23:05 +01:00

131 lines
3.8 KiB
TypeScript

"use client";
import {
useRouter,
useSearchParams,
useParams,
usePathname,
} from "next/navigation";
import { useEffect, useState } from "react";
import Footer_Back from "@/app/components/Footer_Back";
import Header from "@/app/components/Header";
import Image from "next/image";
import "@/app/styles/ghostContent.css"; // Import the global styles
interface Project {
slug: string;
id: string;
title: string;
feature_image: string;
visibility: string;
published_at: string;
updated_at: string;
html: string;
reading_time: number;
meta_description: string;
}
const ProjectDetails = () => {
const router = useRouter();
const searchParams = useSearchParams();
const params = useParams();
const pathname = usePathname();
const [project, setProject] = useState<Project | null>(null);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
setTimeout(() => {
setIsVisible(true);
}, 150); // Delay to start the animation
}, []);
useEffect(() => {
const projectData = searchParams.get("project");
if (projectData) {
setProject(JSON.parse(projectData as string));
// Remove the project data from the URL without reloading the page
if (typeof window !== "undefined") {
const url = new URL(window.location.href);
url.searchParams.delete("project");
window.history.replaceState({}, "", url.toString());
}
} else {
// Fetch project data based on slug from URL
const slug = params.slug as string;
fetchProjectData(slug);
}
}, [searchParams, router, params, pathname]);
const fetchProjectData = async (slug: string) => {
try {
const response = await fetch(`/api/fetchProject?slug=${slug}`);
if (!response.ok) {
throw new Error("Failed to fetch project data");
}
const projectData = (await response.json()) as { posts: Project[] };
setProject(projectData.posts[0]);
} catch (error) {
console.error("Failed to fetch project data:", error);
}
};
if (!project) {
return (
<div className="min-h-screen flex flex-col bg-radiant">
<Header />
<div className="flex-grow flex items-center justify-center">
<div className="loader ease-linear rounded-full border-8 border-t-8 border-gray-200 h-32 w-32"></div>
</div>
<Footer_Back />
</div>
);
}
const featureImageUrl = project.feature_image
? `/api/fetchImage?url=${encodeURIComponent(project.feature_image)}`
: "";
return (
<div
className={`min-h-screen flex flex-col bg-radiant ${isVisible ? "animate-fly-in" : "opacity-0"}`}
>
<Header />
<div className="flex-grow">
<div className="flex justify-center mt-14 md:mt-28 px-4 md:px-0">
{featureImageUrl && (
<div className="relative w-full max-w-4xl h-0 pb-[56.25%] rounded-2xl overflow-hidden">
<Image
src={featureImageUrl}
alt={project.title}
fill
style={{ objectFit: "cover" }}
className="rounded-2xl"
priority={true}
/>
</div>
)}
</div>
<div className="flex items-center justify-center mt-4">
<h1 className="text-4xl md:text-6xl font-bold text-gray-600">
{project.title}
</h1>
</div>
{/* Project Content */}
<div className="p-10 pt-12">
<div className="flex flex-col p-8 bg-gradient-to-br from-white/60 to-white/30 backdrop-blur-lg rounded-2xl shadow-xl">
<div
className="content mt-4 text-gray-600 text-lg leading-relaxed"
dangerouslySetInnerHTML={{ __html: project.html }}
></div>
</div>
</div>
</div>
<Footer_Back />
</div>
);
};
export default ProjectDetails;