Refactor project data parsing to ensure type safety by casting the project data as a string. Enhance the sitemap generation by fetching data from a dynamic API route, allowing for more accurate and up-to-date sitemap entries. Remove unused project markdown files to clean up the project structure. These changes improve code reliability and maintainability.
86 lines
2.5 KiB
TypeScript
86 lines
2.5 KiB
TypeScript
import React, { useEffect, useState } from "react";
|
|
import Link from "next/link";
|
|
|
|
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;
|
|
}
|
|
|
|
interface ProjectsData {
|
|
posts: Project[];
|
|
}
|
|
|
|
export default function Projects() {
|
|
const [projects, setProjects] = useState<Project[]>([]);
|
|
const [isVisible, setIsVisible] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const fetchProjects = async () => {
|
|
try {
|
|
const response = await fetch("/api/fetchAllProjects");
|
|
if (!response.ok) {
|
|
throw new Error("Failed to fetch projects from Ghost");
|
|
}
|
|
const projectsData = (await response.json()) as ProjectsData;
|
|
setProjects(projectsData.posts);
|
|
|
|
setTimeout(() => {
|
|
setIsVisible(true);
|
|
}, 250); // Delay to start the animation after Hero
|
|
} catch (error) {
|
|
console.error("Failed to fetch projects:", error);
|
|
}
|
|
};
|
|
fetchProjects();
|
|
}, []);
|
|
|
|
console.log(projects.at(0)?.feature_image);
|
|
|
|
const numberOfProjects = projects.length;
|
|
return (
|
|
<section
|
|
id="projects"
|
|
className={`p-10 ${isVisible ? "animate-fly-in" : "opacity-0"}`}
|
|
>
|
|
<h2 className="text-3xl font-bold text-center text-gray-800">Projects</h2>
|
|
<div className="mt-6 grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
{projects.map((project, index) => (
|
|
<Link
|
|
key={project.id}
|
|
href={{
|
|
pathname: `/projects/${project.slug}`,
|
|
query: { project: JSON.stringify(project) },
|
|
}}
|
|
className="cursor-pointer"
|
|
>
|
|
<div
|
|
className={`p-4 border shadow-lg bg-white/45 rounded-2xl animate-fly-in`}
|
|
style={{ animationDelay: `${index * 0.1}s` }}
|
|
>
|
|
<h3 className="text-2xl font-bold text-gray-800">
|
|
{project.title}
|
|
</h3>
|
|
<p className="mt-2 text-gray-500">{project.meta_description}</p>
|
|
</div>
|
|
</Link>
|
|
))}
|
|
<div
|
|
className={`p-4 border shadow-lg bg-white/45 rounded-2xl animate-fly-in`}
|
|
style={{ animationDelay: `${(numberOfProjects + 1) * 0.1}s` }}
|
|
>
|
|
<h3 className="text-2xl font-bold text-gray-800">More to come</h3>
|
|
<p className="mt-2 text-gray-500">...</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|