162 lines
6.0 KiB
TypeScript
162 lines
6.0 KiB
TypeScript
"use client";
|
|
import React, { useEffect, useState } from "react";
|
|
import Skeleton from "react-loading-skeleton";
|
|
import "react-loading-skeleton/dist/skeleton.css";
|
|
|
|
type Category = {
|
|
label: string;
|
|
active: boolean;
|
|
};
|
|
|
|
type Project = {
|
|
id: number;
|
|
title: string;
|
|
category: string;
|
|
images: { src: string; position: string; zIndex: number }[];
|
|
created_at: string;
|
|
updated_at: string;
|
|
};
|
|
|
|
export default function MyProjects() {
|
|
const [categories, setCategories] = useState<Category[]>([]);
|
|
const [projects, setProjects] = useState<Project[]>([]);
|
|
const [selectedCategory, setSelectedCategory] = useState<string>("Newest");
|
|
|
|
useEffect(() => {
|
|
const fetchProjects = async () => {
|
|
const res = await fetch("/api/projects?sort=desc&per_page=20");
|
|
const data = await res.json();
|
|
const resProjects = data.projects.data;
|
|
const resCategories = data.categories;
|
|
|
|
// Convert JSON string to array
|
|
const updatedData = resProjects.map((project: { images: string }) => ({
|
|
...project,
|
|
images: JSON.parse(project.images),
|
|
}));
|
|
|
|
setProjects(updatedData);
|
|
|
|
// Extract unique categories
|
|
const uniqueCategories: Category[] = [{ label: "Newest", active: false }];
|
|
const onlyFiveCategoris: Category[] = [
|
|
...resCategories.map((category: string) => ({
|
|
label: category,
|
|
active: false,
|
|
})),
|
|
].slice(0, 5);
|
|
uniqueCategories.push(...onlyFiveCategoris);
|
|
setCategories(uniqueCategories);
|
|
};
|
|
|
|
fetchProjects();
|
|
}, []);
|
|
|
|
// Group projects by category
|
|
const groupProjectsByCategory = (projects: Project[]) => {
|
|
return projects.reduce<Record<string, Project[]>>((acc, project) => {
|
|
const category = project.category;
|
|
if (!acc[category]) {
|
|
acc[category] = [];
|
|
}
|
|
acc[category].push(project);
|
|
return acc;
|
|
}, {});
|
|
};
|
|
|
|
const groupedProjects = groupProjectsByCategory(projects);
|
|
|
|
// Filter projects by selected category
|
|
const filteredProjects: Project[] =
|
|
selectedCategory === "Newest"
|
|
? projects.slice(0, 6) // Get first 6 projects for "Newest"
|
|
: groupedProjects[selectedCategory]?.slice(0, 6) || []; // Get first 6 projects from selected category
|
|
|
|
return (
|
|
<div
|
|
className="flex w-full max-w-[90%] flex-wrap items-center justify-center"
|
|
id="projects"
|
|
>
|
|
<h2 className="my-10 text-[55px] font-[800] text-black">My Projects</h2>
|
|
<p className="text-justify text-[21px] font-[400] text-black">
|
|
Lorem ipsum dolor sit amet consectetur. Mollis erat duis aliquam mauris
|
|
est risus lectus. Phasellus consequat urna tellus
|
|
</p>
|
|
<ul className="mb-10 mt-20 flex w-full flex-wrap items-center justify-center gap-5">
|
|
{categories.length > 0 ? (
|
|
categories.map((category, index) => (
|
|
<li
|
|
key={index}
|
|
onClick={() => setSelectedCategory(category.label)}
|
|
className={`select-none whitespace-nowrap rounded-md border border-[#545454] bg-[#F8F8F8] px-[20px] py-[10px] text-[18px] font-[600] hover:cursor-pointer hover:border-[#FD6F00] ${
|
|
selectedCategory === category.label
|
|
? "bg-[#FD6F00] text-[#FFFFFF]"
|
|
: "text-[#000]"
|
|
}`}
|
|
>
|
|
{category.label}
|
|
</li>
|
|
))
|
|
) : (
|
|
<div className="flex w-full flex-wrap gap-5">
|
|
<Skeleton containerClassName={"w-[45%] md:w-[21%]"} height={60} />
|
|
<Skeleton containerClassName={"w-[45%] md:w-[21%]"} height={60} />
|
|
<Skeleton containerClassName={"w-[45%] md:w-[21%]"} height={60} />
|
|
<Skeleton containerClassName={"w-[45%] md:w-[21%]"} height={60} />
|
|
</div>
|
|
)}
|
|
</ul>
|
|
<div className="flex w-full flex-wrap items-start justify-center gap-8">
|
|
{filteredProjects.length > 0 ? (
|
|
filteredProjects.map((project, index) => (
|
|
<div
|
|
key={index}
|
|
className="project flex w-[45%] flex-wrap md:w-[30%]"
|
|
>
|
|
<div className="relative my-5 h-[200px] w-full overflow-hidden bg-[#FFEBDB] md:h-[250px] lg:h-[300px]">
|
|
{project.images.map((image, idx) => (
|
|
<img
|
|
key={idx}
|
|
src={image.src}
|
|
alt="UI/UX Vector"
|
|
className={`absolute ${image.position} z-${image.zIndex} project-image max-w-[350px]`}
|
|
/>
|
|
))}
|
|
</div>
|
|
<h5 className="w-full text-[15px] font-[600] text-[#FD6F00] lg:text-[20px]">
|
|
{project.category}
|
|
</h5>
|
|
<p className="text-[18px] font-[800] text-black hover:cursor-pointer lg:text-[22px]">
|
|
{project.title}
|
|
</p>
|
|
</div>
|
|
))
|
|
) : (
|
|
<div className="flex w-full flex-wrap justify-center gap-5">
|
|
<div className="w-[45%] md:w-[30%]">
|
|
<Skeleton containerClassName={"w-full"} height={150} />
|
|
<Skeleton containerClassName={"w-full"} height={40} />
|
|
<Skeleton containerClassName={"w-full"} height={60} />
|
|
</div>
|
|
<div className="w-[45%] md:w-[30%]">
|
|
<Skeleton containerClassName={"w-full"} height={150} />
|
|
<Skeleton containerClassName={"w-full"} height={40} />
|
|
<Skeleton containerClassName={"w-full"} height={60} />
|
|
</div>
|
|
<div className="w-[45%] md:w-[30%]">
|
|
<Skeleton containerClassName={"w-full"} height={150} />
|
|
<Skeleton containerClassName={"w-full"} height={40} />
|
|
<Skeleton containerClassName={"w-full"} height={60} />
|
|
</div>
|
|
<div className="w-[45%] md:w-[30%]">
|
|
<Skeleton containerClassName={"w-full"} height={150} />
|
|
<Skeleton containerClassName={"w-full"} height={40} />
|
|
<Skeleton containerClassName={"w-full"} height={60} />
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|