refactor: move ProjectCard component and add prop types validation

This commit is contained in:
Alvalens 2025-03-18 12:37:11 +07:00
parent f1edb19b5a
commit 3aa7aedeca
3 changed files with 88 additions and 22 deletions

View file

@ -1,10 +1,10 @@
import Link from "next/link";
import { motion } from "framer-motion";
import Image from "next/image";
export default function projectCard({project, index, activeCategory}){
console.log(project.category[0], activeCategory);
console.log(project.category.includes(activeCategory));
return (
import { motion } from "framer-motion";
import PropTypes from "prop-types";
export default function ProjectCard({ project, index, activeCategory }) {
return (
<>
{project.category.includes(parseInt(activeCategory)) && (
<Link href={"projects/" + project.slug} key={index}>
@ -56,3 +56,10 @@ export default function projectCard({project, index, activeCategory}){
</>
);
}
ProjectCard.propTypes = {
project: PropTypes.object.isRequired,
index: PropTypes.number.isRequired,
activeCategory: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,
};

View file

@ -0,0 +1,65 @@
import Link from "next/link";
import Image from "next/image";
import { motion } from "framer-motion";
import PropTypes from "prop-types";
export default function ProjectCard({ project, index, activeCategory }) {
return (
<>
{project.category.includes(parseInt(activeCategory)) && (
<Link href={"projects/" + project.slug} key={index}>
<motion.div
className="z-10 relative flex justify-center items-start flex-col mb-5 md:px-10 w-full h-auto bg-gray-400 group/tes py-20 px-5 md:py-2 aspect-video "
initial={{
opacity: 0,
x: -200,
}}
whileInView={{
opacity: 1,
x: 0,
}}
transition={{
type: "spring",
}}>
<Image
src={project.thumbnail}
alt="Alvalens"
layout="fill"
objectFit="cover"
placeholder="blur"
className="bg-slate-950 opacity-10 group-hover/tes:opacity-100 transition-all ease duration-500"
blurDataURL={project.thumbnail}
/>
<div className="absolute top-0 left-0 bg-gray-600 px-4 py-2">
<h4 className="text-white">{project.year}</h4>
</div>
<div className="transition-all ease duration-500 opacity-100 content text-center group-hover/tes:opacity-0 z-10">
<h1 className="text-3xl font-bold mb-3">{project.title}</h1>
<p>
{project.desc[0].length > 125
? `${project.desc[0].slice(0, 125)}...`
: project.desc[0]}
</p>
<div className="flex justify-center items-center flex-row mt-5 flex-wrap">
{project.tech.map((t, index) => (
<span
key={index}
className="m-1 px-4 py-2 bg-gray-600 text-white ">
{t}
</span>
))}
</div>
</div>
</motion.div>
</Link>
)}
</>
);
}
ProjectCard.propTypes = {
project: PropTypes.object.isRequired,
index: PropTypes.number.isRequired,
activeCategory: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,
};

View file

@ -12,7 +12,7 @@ import ReactChat3 from "@/public/image/projects/web/trivia/trivia3.png";
import ProjectAll from "@/public/image/projects.png";
import Hr from "@/components/Hr";
import ProjectCard from "./(project-card)";
import ProjectCard from "./components/ProjectCard";
import Projects from "@/json/data.json";
import FixedButon from "@/components/FixedButton";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@ -22,12 +22,11 @@ const category = {
1: "Web Development",
2: "AI & Machine Learning",
9: "Other",
}
};
export default function Page() {
const [activeCategory, setActiveCategory] = useState(1);
const projects = Projects.Projects.filter ((item) => item.show === true
);
const projects = Projects.Projects.filter((item) => item.show === true);
useEffect(() => {
window.scrollTo(0, 0);
@ -36,10 +35,7 @@ export default function Page() {
<>
<main className="overflow-hidden">
<FixedButon href="/#projects">
<FontAwesomeIcon
icon={faChevronLeft}
className="text-black pr-10"
/>
<FontAwesomeIcon icon={faChevronLeft} className="text-black pr-10" />
</FixedButon>
<div className="relative h-screen w-screen gap-4 p-10 flex justify-center items-center flex-col mb-10 overflow-hidden">
<div className="z-0 mb-48 md:mb-0 md:absolute top-1/4 md:right-[10%] md:-translate-y-16 ">
@ -181,15 +177,13 @@ export default function Page() {
Generative Trivia
</h2>
<p className="text-gray-600 text-justify title text-lg">
A generative trivia website built with Next.js,
Firebase, NextAuth, Google OAuth, and TypeScript.
Users can generate trivia questions for predefined
school subjects and answer them with a unique
interactive input style. The application supports
user authentication with Google and stores data in
Firestore. You can see the source code of this
project on my github account or preview the project
by clicking the button below.
A generative trivia website built with Next.js, Firebase,
NextAuth, Google OAuth, and TypeScript. Users can generate trivia
questions for predefined school subjects and answer them with a
unique interactive input style. The application supports user
authentication with Google and stores data in Firestore. You can
see the source code of this project on my github account or
preview the project by clicking the button below.
</p>{" "}
<div className="mt-3">
<Button variation="primary">