diff --git a/src/app/(main)/projects/components/project-card-effect.tsx b/src/app/(main)/projects/components/project-card-effect.tsx new file mode 100644 index 00000000..0725f78a --- /dev/null +++ b/src/app/(main)/projects/components/project-card-effect.tsx @@ -0,0 +1,29 @@ +'use client' + +import { ReactNode } from 'react' +import Tilt from 'react-parallax-tilt' + +export function ProjectCardEffect({ + children, + className +}: { + children: ReactNode + className?: string +}) { + return ( + + {children} + + ) +} diff --git a/src/app/(main)/projects/components/project-card.tsx b/src/app/(main)/projects/components/project-card.tsx new file mode 100644 index 00000000..5e3a4019 --- /dev/null +++ b/src/app/(main)/projects/components/project-card.tsx @@ -0,0 +1,106 @@ +import Image from 'next/image' +import { Project } from 'contentlayer/generated' +import { FiFolder, FiGithub, FiGlobe } from 'react-icons/fi' +import { BsDot } from 'react-icons/bs' +import { techIcons } from './tech-icons' +import { ProjectCardEffect } from './project-card-effect' + +interface Props { + data: Project +} + +export function ProjectCard({ data }: Props) { + const Title = () => ( +
+

{data.title}

+ + + + + {data.core_techs.map(coreTech => { + const TechIcon = techIcons[coreTech] + return ( + + + + ) + })} + +
+ ) + const Tags = () => ( +
+ {data.tags.map(tag => ( + + {tag} + + ))} +
+ ) + + return ( + +
+ {data.image && ( +
+ Project image +
+ )} +
+
+ + <p className="text-justify md:text-left">{data.description}</p> + <Tags /> + </div> + <div className="mt-9 flex flex-1 items-end justify-center gap-6"> + {data.repository && ( + <> + <a + href={data.repository} + target="_blank" + rel="noreferrer" + className="flex items-center gap-2 rounded-2xl border border-[#181717] p-4 text-[#181717] hover:bg-[#181717] hover:text-[#F6F8FA] dark:border-[#F6F8FA] dark:text-[#F6F8FA] dark:hover:bg-[#F6F8FA] hover:dark:text-[#181717]" + > + Repositório <FiGithub /> + </a> + </> + )} + {data.files && ( + <> + <a + href={data.files} + target="_blank" + rel="noreferrer" + className="flex items-center gap-2 rounded-2xl border border-blue-500 p-4 text-blue-500 hover:bg-blue-500 hover:text-neutral-50" + > + Arquivos <FiFolder /> + </a> + </> + )} + {data.website && ( + <> + <a + href={data.website} + target="_blank" + rel="noreferrer" + className="flex items-center gap-2 rounded-2xl border border-blue-700 bg-blue-700/10 p-4 text-blue-700 hover:bg-blue-700 hover:text-neutral-50 dark:border-blue-600 dark:bg-blue-600/5 dark:text-blue-600 dark:hover:bg-blue-600 dark:hover:text-neutral-50" + > + Website <FiGlobe /> + </a> + </> + )} + </div> + </div> + </div> + </ProjectCardEffect> + ) +} diff --git a/src/app/(main)/projects/components/tech-icons.tsx b/src/app/(main)/projects/components/tech-icons.tsx new file mode 100644 index 00000000..3e933a9e --- /dev/null +++ b/src/app/(main)/projects/components/tech-icons.tsx @@ -0,0 +1,38 @@ +import { + SiTypescript, + SiReact, + SiExpo, + SiNextdotjs, + SiTailwindcss, + SiVite, + SiNodedotjs, + SiPrisma, + SiVuedotjs, + SiMdx, + SiJavascript +} from 'react-icons/si' + +export const techIcons = { + javascript: () => ( + <SiJavascript className="hover:text-[#F7DF1E]" title="Javascript" /> + ), + typescript: () => ( + <SiTypescript className="hover:text-[#358EF1]" title="Typescript" /> + ), + 'react-native': () => ( + <SiReact className="hover:text-[#61DBFB]" title="React Native" /> + ), + expo: () => <SiExpo title="Expo" />, + reactjs: () => <SiReact className="hover:text-[#61DBFB]" title="React.js" />, + nextjs: () => <SiNextdotjs title="Next.js" />, + tailwindcss: () => ( + <SiTailwindcss className="hover:text-[#38BDF8]" title="Tailwind CSS" /> + ), + vite: () => <SiVite className="hover:text-[#FFC119]" title="Vite" />, + nodejs: () => ( + <SiNodedotjs className="hover:text-[#66CC33]" title="Node.js" /> + ), + prisma: () => <SiPrisma className="hover:text-[#4C51BF]" title="Prisma" />, + vue: () => <SiVuedotjs className="hover:text-[#4FC08D]" title="Vue.js" />, + mdx: () => <SiMdx className="hover:text-[#1B1F24]" title="MDX" /> +} diff --git a/src/app/(main)/projects/layout.tsx b/src/app/(main)/projects/layout.tsx new file mode 100644 index 00000000..1b7d637b --- /dev/null +++ b/src/app/(main)/projects/layout.tsx @@ -0,0 +1,14 @@ +import { Metadata } from 'next' + +export const metadata: Metadata = { + title: 'Projects', + description: 'My personal projects and apps' +} + +export default function AboutLayout({ + children +}: { + children: React.ReactNode +}) { + return <div className="content-vertical-spaces">{children}</div> +} diff --git a/src/app/(main)/projects/page.tsx b/src/app/(main)/projects/page.tsx new file mode 100644 index 00000000..0ecc2101 --- /dev/null +++ b/src/app/(main)/projects/page.tsx @@ -0,0 +1,36 @@ +import { allProjects } from 'contentlayer/generated' +import { ProjectCard } from './components/project-card' + +export default function Page() { + const featuredProjects = allProjects + .filter(project => project.featured) + .sort((a, b) => Number(a.priority) - Number(b.priority)) + const otherProjectsWithImage = allProjects + .filter(project => !project.featured && project.image) + .sort((a, b) => Number(a.priority) - Number(b.priority)) + const otherProjectsWithoutImage = allProjects + .filter(project => !project.featured && !project.image) + .sort((a, b) => Number(a.priority) - Number(b.priority)) + + return ( + <div className="blog-content-w m-auto"> + <h1 className="mb-8 bg-gradient-to-br from-blue-700 to-blue-400 bg-clip-text text-center text-4xl font-bold text-transparent md:w-fit md:text-left"> + Projects + </h1> + <div className="grid grid-cols-1 gap-6 md:grid-cols-2"> + {featuredProjects.length > 0 && + featuredProjects.map(project => ( + <ProjectCard data={project} key={project._id} /> + ))} + {otherProjectsWithImage.length > 0 && + otherProjectsWithImage.map(project => ( + <ProjectCard data={project} key={project._id} /> + ))} + {otherProjectsWithoutImage.length > 0 && + otherProjectsWithoutImage.map(project => ( + <ProjectCard data={project} key={project._id} /> + ))} + </div> + </div> + ) +}