feat: revamp portfolio with case studies, curriculo page, and local SEO
Adds dynamic case study and curriculo routes, project health monitoring for live demo status, and strengthens local SEO (structured data, service-area targeting for Garuva/Joinville/Curitiba, a dedicated "criação de sites" service page) so search results better match the professional title (Engenheiro da Computação) and experience (5 anos). Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
9236fdd685
commit
d76bbf5974
41 files changed
+1359
-492
No files matched your search
+24
-27
@@ -1,30 +1,18 @@
|
||||
"use client";
|
||||
|
||||
import { useMemo } from "react";
|
||||
import type { PortfolioProject } from "@/lib/portfolio-data";
|
||||
import { motion, type Variants, useReducedMotion } from "framer-motion";
|
||||
import { usePortfolioData } from "@/lib/usePortfolioData";
|
||||
import { Section } from "./Section";
|
||||
import MagneticButton from "./cinematic/MagneticButton";
|
||||
import { DrawLine } from "./animations/DrawLine";
|
||||
import { SectionLoading } from "./SectionLoading";
|
||||
|
||||
export default function Projects() {
|
||||
const { data, loading, error } = usePortfolioData();
|
||||
export default function Projects({
|
||||
projects,
|
||||
}: {
|
||||
projects: PortfolioProject[];
|
||||
}) {
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
|
||||
const projects = useMemo(
|
||||
() => data?.projects.filter((p) => p.featured) || [],
|
||||
[data],
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
return <SectionLoading dark message="Loading selected work" />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div className="ds-error">Error loading selected work</div>;
|
||||
}
|
||||
|
||||
const gridVariants: Variants = {
|
||||
hidden: {},
|
||||
visible: {
|
||||
@@ -92,16 +80,18 @@ export default function Projects() {
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<MagneticButton as="span" className="ds-tag" strength={0.15}>{project.status}</MagneticButton>
|
||||
<MagneticButton as="span" className="ds-tag" strength={0.15}>
|
||||
{project.status}
|
||||
</MagneticButton>
|
||||
</div>
|
||||
|
||||
<p className="ds-copy">{project.description}</p>
|
||||
|
||||
<ul className="ds-tags mt-sp-6">
|
||||
{project.technologies.slice(0, 5).map((tech: string) => (
|
||||
<MagneticButton key={tech} as="li" className="ds-tag" strength={0.15}>
|
||||
{project.technologies.slice(0, 5).map((tech) => (
|
||||
<li key={tech} className="ds-tag">
|
||||
{tech}
|
||||
</MagneticButton>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -136,16 +126,23 @@ export default function Projects() {
|
||||
</motion.div>
|
||||
|
||||
<div className="mt-sp-8 flex flex-wrap gap-sp-3">
|
||||
<MagneticButton as="a" href="/cases" className="ds-link ds-link-accent" strength={0.2}>
|
||||
<MagneticButton
|
||||
as="a"
|
||||
href="/cases"
|
||||
className="ds-link ds-link-accent"
|
||||
strength={0.2}
|
||||
>
|
||||
Explorar todos os cases
|
||||
</MagneticButton>
|
||||
<MagneticButton as="a" href="/servicos" className="ds-link ds-link-muted" strength={0.2}>
|
||||
<MagneticButton
|
||||
as="a"
|
||||
href="/servicos"
|
||||
className="ds-link ds-link-muted"
|
||||
strength={0.2}
|
||||
>
|
||||
Ver servicos relacionados
|
||||
</MagneticButton>
|
||||
</div>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user