Files
portfolio/app/Projects/[slug]/page.tsx
Denshooter 635c244c06 feat: update dependencies and enhance privacy policy
Replace "@vercel/analytics" with "@tryghost/content-api" and add 
"node-fetch" to dependencies. Remove "@vercel/speed-insights" to 
streamline the package. Update robots.txt to dis access to 
"/legal-notice" and "/privacy-policy". Change <p> tags to <div> in 
the Privacy Policy for better structure. Update the last modified 
date in the Legal Notice. Add a new API route for fetching images 
with error handling for missing URL parameters.
2025-02-12 12:59:15 +01:00

128 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));
// Remove the project data from the URL without reloading the page
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();
setProject(projectData.posts[0]); // Assuming the API returns an array of posts
} 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 = `/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">
{/* Hero Section */}
<div className="flex justify-center md:mt-28 px-4 md:px-0">
<div className="relative w-full max-w-4xl h-0 pb-[56.25%] rounded-2xl overflow-hidden">
{" "}
{/* 16:9 Aspect Ratio */}
<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-white">
{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;