Skip to content

Commit

Permalink
feat: add project full card and github skill
Browse files Browse the repository at this point in the history
  • Loading branch information
NedcloarBR committed Jan 12, 2025
1 parent bf52eb3 commit 334bb50
Show file tree
Hide file tree
Showing 14 changed files with 251 additions and 25 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"next-themes": "^0.4.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"simple-icons": "^14.2.0",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7"
},
Expand Down
1 change: 1 addition & 0 deletions public/icons/GitHub.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion public/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@
},
"ESLint": {
"Description": "ESLint is a code linting tool for JavaScript. It is used to identify and report problematic patterns found in JavaScript code. It is highly configurable and can be customized to meet the specific needs of your project."
},
},
"GitHub": {
"Description": "GitHub is a web-based platform for version control using Git. It allows developers to collaborate on projects, track changes, and manage code repositories. It provides a wide range of features, such as issue tracking, pull requests, code review, and project management."
},
"HTML5": {
"Description": "HTML5 is a markup language used for structuring and presenting content on the World Wide Web. It is the fifth and final major version of HTML, recommended by the World Wide Web Consortium (W3C)."
},
Expand Down Expand Up @@ -95,6 +98,8 @@
},
"Projects": {
"Title": "Projects",
"ViewSource": "View on GitHub",
"ViewDeploy": "View Deploy",
"N-D-B": {
"Description": "N-D-B is a Discord bot developed using DiscordJS and Necord an library for NestJS. It is a multipurpose bot that has several features, such as moderation, music, and fun commands. The bot is designed to be easy to use and has a simple and intuitive interface. It is constantly being updated with new features and improvements."
}
Expand Down
5 changes: 5 additions & 0 deletions public/locales/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"ESLint": {
"Description": "ESLint é uma ferramenta de linting de código para JavaScript. Ele é usado para identificar e relatar padrões problemáticos encontrados no código JavaScript. Ele é altamente configurável e pode ser personalizado para atender às necessidades específicas do seu projeto."
},
"GitHub": {
"Description": "GitHub é uma plataforma baseada na web para controle de versão usando Git. Ele permite que os desenvolvedores colaborem em projetos, rastreiem alterações e gerenciem repositórios de código. Ele oferece uma ampla gama de recursos, como rastreamento de problemas, solicitações de pull, revisão de código e gerenciamento de projetos."
},
"HTML5": {
"Description": "HTML5 é uma linguagem de marcação usada para estruturar e apresentar conteúdo na World Wide Web. É a quinta e última grande versão do HTML que é uma recomendação do World Wide Web Consortium (W3C)."
},
Expand Down Expand Up @@ -95,6 +98,8 @@
},
"Projects": {
"Title": "Projetos",
"ViewSource": "Ver no GitHub",
"ViewDeploy": "Ver Deploy",
"N-D-B": {
"Description": "N-D-B é um bot do Discord desenvolvido usando DiscordJS e Necord uma biblioteca para o NestJS. É um bot multipropósito que possui várias funcionalidades, como moderação, música e comandos divertidos. O bot é projetado para ser fácil de usar e possui uma interface simples e intuitiva. Ele está sendo constantemente atualizado com novos recursos e melhorias."
}
Expand Down
2 changes: 2 additions & 0 deletions src/@types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export type ProjectCategories = "";
export type Project = {
name: string;
description: string;
github: string;
deploy: string;
techs: Techs[];
categories: ProjectCategories[];
};
37 changes: 37 additions & 0 deletions src/app/api/images/[project]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import fs from "node:fs";
import path from "node:path";
import { NextResponse } from "next/server";

export async function GET(
request: Request,
{ params }: { params: { project: string } }
) {
const { project } = await params;

if (!project) {
return NextResponse.json(
{ error: "Project name is required" },
{ status: 400 }
);
}

const imagesPath = path.join(process.cwd(), "public", "images", project);

try {
const files = fs.readdirSync(imagesPath);
const images = files
.filter((file) => /\.(png|jpg|jpeg|gif|svg)$/i.test(file))
.map((file, index) => ({
id: index,
src: `/images/${project}/${file}`,
alt: `${project} ${file}`,
}));

return NextResponse.json(images);
} catch (error) {
return NextResponse.json(
{ error: "Unable to fetch images" },
{ status: 500 }
);
}
}
72 changes: 70 additions & 2 deletions src/components/projects/full-card.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,71 @@
export function ProjectsFullCard() {
return <section id="projects" className="h-screen"></section>;
"use client";

import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
} from "../ui/dialog";
import { DialogTitle } from "@radix-ui/react-dialog";
import type { Dispatch, SetStateAction } from "react";
import { ProjectCarousel } from "./project-carousel";
import { Project } from "@/@types";
import { Icon } from "../icon";
import { LucideUpload } from "lucide-react";
import { Button, Separator } from "../ui";
import { useTranslations } from "next-intl";
import Link from "next/link";

interface ProjectsFullCardProps {
openState: [boolean, Dispatch<SetStateAction<boolean>>];
info: Project;
}

export function ProjectsFullCard({
openState,
info,
}: Readonly<ProjectsFullCardProps>) {
const [isOpen, setIsOpen] = openState;
const t = useTranslations("Projects");

return (
<Dialog open={isOpen} onOpenChange={() => setIsOpen((prev) => !prev)}>
<DialogContent>
<DialogHeader className="flex items-center justify-center">
<ProjectCarousel name={info.name} />
<DialogTitle className="flex items-center justify-center text-2xl">
{info.name}
</DialogTitle>
</DialogHeader>
<Separator />

<DialogDescription>{t(info.description)}</DialogDescription>
<Separator />
<div>
<Button variant="link" size="sm" className="flex">
<Icon name="GitHub" className="size-8" />
<Link href={info.github} />
{t("ViewSource")}
</Button>
<Button variant="link" size="sm" className="flex">
<LucideUpload className="size-8" />
<Link href={info.deploy} />
{t("ViewDeploy")}
</Button>
</div>
<Separator />

<div className="flex flex-wrap items-center justify-center gap-2">
{info.techs.map((tech) => (
<Icon
key={tech}
name={tech}
extension={tech === "Necord" ? "png" : "svg"}
className="size-10"
/>
))}
</div>
</DialogContent>
</Dialog>
);
}
2 changes: 2 additions & 0 deletions src/components/projects/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { ProjectsFullCard } from "./full-card";
import { ProjectCarousel } from "./project-carousel";
import { ProjectsSection } from "./projects";
import { ProjectsSmallCard } from "./small-card";

export const Projects = {
Section: ProjectsSection,
SmallCard: ProjectsSmallCard,
FullCard: ProjectsFullCard,
Carousel: ProjectCarousel,
};
80 changes: 80 additions & 0 deletions src/components/projects/project-carousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"use client";

import { Card, CardContent } from "@/components/ui/card";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
import { useEffect, useState } from "react";

interface ProjectCarouselProps {
name: string;
}

export function ProjectCarousel({ name }: ProjectCarouselProps) {
const [pictures, setPictures] = useState<{ id: number, src: string; alt: string }[]>([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
const fetchPictures = async () => {
try {
const response = await fetch(`/api/images/${name}`);
if (response.ok) {
const data = await response.json();
setPictures(data);
} else {
console.error("Failed to fetch images");
}
} catch (error) {
console.error("Error fetching images:", error);
} finally {
setLoading(false);
}
};

fetchPictures();
}, [name]);

if(loading) {
return (
<Carousel>
<CarouselContent className="w-full max-w-64">
<CarouselItem>
<div className="p-1">
<Card className="w-[230px]">
<CardContent className="flex aspect-square items-center justify-center p-6">
<div className="animate-pulse bg-gray-300 w-full h-full rounded-sm"></div>
</CardContent>
</Card>
</div>
</CarouselItem>
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
);
}

return (
<Carousel>
<CarouselContent className="w-full max-w-64">
{pictures.map((picture) => (
<CarouselItem key={picture.id}>
<div className="p-1">
<Card className="w-[230px]">
<CardContent className="flex aspect-square items-center justify-center p-6">
<img src={picture.src} alt={picture.alt} />
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
)
}
2 changes: 1 addition & 1 deletion src/components/projects/projects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function ProjectsSection() {
</div>
<div>
{projects.map((project) => (
<ProjectsSmallCard key={project.name} name={project.name} description={t(project.description)} techs={project.techs} />
<ProjectsSmallCard key={project.name} info={project} />
))}
</div>
</section>
Expand Down
50 changes: 29 additions & 21 deletions src/components/projects/small-card.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client"

import {
Card,
CardContent,
Expand All @@ -8,32 +10,38 @@ import {
} from "@/components/ui/card";
import { Icon } from "../icon";
import { Separator } from "../ui";
import { Techs } from "@/@types";
import { Project } from "@/@types";
import { ProjectsFullCard } from "./full-card";
import { useState } from "react";
import { useTranslations } from "next-intl";

interface ProjectsSmallCardProps {
name: string;
description: string;
techs: Techs[];
info: Project
}

export function ProjectsSmallCard(props: Readonly<ProjectsSmallCardProps>) {
const uniqueTechs = Array.from(new Set(props.techs));
export function ProjectsSmallCard({ info }: Readonly<ProjectsSmallCardProps>) {
const uniqueTechs = Array.from(new Set(info.techs));
const openState = useState(false);
const t = useTranslations("Projects");

return (
<Card className="w-[230px] card-hover cursor-pointer">
<CardHeader>
<img src={`images/${props.name}/Logo.png`} alt={`${props.name} Logo`} className="w-[210px] h-[90px] rounded-sm" />
<Separator />
<CardTitle className="flex justify-center">{props.name}</CardTitle>
</CardHeader>
<CardContent>
<CardDescription>{props.description.split(".")[0]+"."}</CardDescription>
</CardContent>
<CardFooter className="flex justify-center flex-wrap gap-2">
{uniqueTechs.map((tech) => (
<Icon key={tech} name={tech} extension={tech === "Necord" ? "png" : "svg"} className="size-6"/>
))}
</CardFooter>
</Card>
<>
<Card className="w-[230px] card-hover cursor-pointer" onClick={() => openState[1](true)}>
<CardHeader>
<img src={`images/${info.name}/Logo.png`} alt={`${info.name} Logo`} className="w-[210px] h-[90px] rounded-sm" />
<Separator />
<CardTitle className="flex justify-center">{info.name}</CardTitle>
</CardHeader>
<CardContent>
<CardDescription>{t(info.description).split(".")[0]+"."}</CardDescription>
</CardContent>
<CardFooter className="flex justify-center flex-wrap gap-2">
{uniqueTechs.map((tech) => (
<Icon key={tech} name={tech} extension={tech === "Necord" ? "png" : "svg"} className="size-6"/>
))}
</CardFooter>
</Card>
<ProjectsFullCard openState={openState} info={info}/>
</>
);
}
3 changes: 3 additions & 0 deletions src/constants/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export const projects: Project[] = [
name: "N-D-B",
description: "N-D-B.Description",
categories: [],
github: "https://github.com/N-D-B-Project/N-D-B",
deploy:
"https://discord.com/oauth2/authorize?client_id=708822043420000366&permissions=8&redirect_uri=http%3A%2F%2Flocalhost%3A3001%2Fapi%2Fauth%2Fredirect&scope=bot%20applications.commands",
techs: [
"NodeJS",
"TypeScript",
Expand Down
6 changes: 6 additions & 0 deletions src/constants/skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ export const skills: SkillData[] = [
categories: ["Tools"],
url: "https://eslint.org/",
},
{
name: "GitHub",
description: "GitHub.Description",
categories: ["Tools"],
url: "https://github.com/NedcloarBR",
},
{
name: "HTML5",
description: "HTML5.Description",
Expand Down
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2397,6 +2397,7 @@ __metadata:
postcss: "npm:^8.4.49"
react: "npm:^19.0.0"
react-dom: "npm:^19.0.0"
simple-icons: "npm:^14.2.0"
tailwind-merge: "npm:^2.6.0"
tailwindcss: "npm:^3.4.17"
tailwindcss-animate: "npm:^1.0.7"
Expand Down Expand Up @@ -2830,6 +2831,13 @@ __metadata:
languageName: node
linkType: hard

"simple-icons@npm:^14.2.0":
version: 14.2.0
resolution: "simple-icons@npm:14.2.0"
checksum: 10c0/47b3656ecdd33f0b58d516bd2d67f06c6bd9b39ffa5cea26cbdfb6799af2ce6c07d0b525920e25dd5d9bdf491904fb35ac8a20f1cfe20cfbef82b7b10bbc935c
languageName: node
linkType: hard

"simple-swizzle@npm:^0.2.2":
version: 0.2.2
resolution: "simple-swizzle@npm:0.2.2"
Expand Down

0 comments on commit 334bb50

Please sign in to comment.