Files
portifolio-evandrodevbr/app/servicos/[slug]/page.tsx
T
Evandro Fonseca JuniorandClaude Sonnet 5 d76bbf5974 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]>
2026-07-15 01:31:46 -03:00

289 lines
9.7 KiB
TypeScript

import Link from "next/link";
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import {
getRelatedCases,
getServicePage,
regionCities,
servicePages,
siteName,
siteUrl,
} from "@/lib/site-content";
import HeroBackground from "@/app/components/cinematic/HeroBackground";
import MagneticButton from "@/app/components/cinematic/MagneticButton";
interface ServicePageProps {
params: Promise<{
slug: string;
}>;
}
export function generateStaticParams() {
return servicePages.map((service) => ({ slug: service.slug }));
}
export async function generateMetadata({
params,
}: ServicePageProps): Promise<Metadata> {
const { slug } = await params;
const service = getServicePage(slug);
if (!service) {
return {
title: `Servico nao encontrado | ${siteName}`,
};
}
const url = `${siteUrl}/servicos/${service.slug}`;
return {
title: service.seoTitle,
description: service.metaDescription,
alternates: {
canonical: url,
},
openGraph: {
title: service.seoTitle,
description: service.metaDescription,
url,
siteName,
type: "article",
},
twitter: {
card: "summary_large_image",
title: service.seoTitle,
description: service.metaDescription,
},
};
}
export default async function ServicePage({ params }: ServicePageProps) {
const { slug } = await params;
const service = getServicePage(slug);
if (!service) {
notFound();
}
const relatedCases = getRelatedCases(service.relatedCaseIds);
const pageUrl = `${siteUrl}/servicos/${service.slug}`;
const jsonLd = {
"@context": "https://schema.org",
"@graph": [
{
"@type": "Service",
"@id": `${pageUrl}#service`,
name: service.navLabel,
description: service.metaDescription,
serviceType: service.navLabel,
provider: {
"@type": "Person",
name: "Evandro Fonseca Junior",
url: siteUrl,
},
areaServed: [
...regionCities.map((name) => ({ "@type": "City", name })),
{ "@type": "AdministrativeArea", name: "Norte de Santa Catarina" },
{
"@type": "Country",
name: "Brasil",
},
{
"@type": "Place",
name: "Remote worldwide",
},
],
url: pageUrl,
},
{
"@type": "BreadcrumbList",
itemListElement: [
{
"@type": "ListItem",
position: 1,
name: "Inicio",
item: siteUrl,
},
{
"@type": "ListItem",
position: 2,
name: "Servicos",
item: `${siteUrl}/servicos`,
},
{
"@type": "ListItem",
position: 3,
name: service.navLabel,
item: pageUrl,
},
],
},
],
};
return (
<main>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<section className="ds-section ds-section-dark relative min-h-[340px] overflow-hidden">
<HeroBackground />
<div className="ds-section-shell relative z-10">
<div className="max-w-[860px]">
<p className="ds-eyebrow">{service.eyebrow}</p>
<h1 className="ds-section-title max-w-[14ch] text-balance text-white">
{service.headline}
</h1>
<p className="ds-copy max-w-[64ch] !text-[rgba(255,255,255,0.78)]">
{service.intro}
</p>
<div className="mt-sp-8 flex flex-wrap gap-sp-3">
<MagneticButton as="a" href={service.ctaHref} className="ds-link ds-link-accent" strength={0.25}>
{service.ctaLabel}
</MagneticButton>
<MagneticButton as="a" href="/cases" className="ds-link ds-link-muted" strength={0.2}>
Ver casos relacionados
</MagneticButton>
</div>
</div>
</div>
</section>
<section className="ds-section">
<div className="ds-section-shell">
<div className="ds-grid-2">
<article className="ds-card">
<div className="ds-kicker">Para quem faz sentido</div>
<h2 className="ds-card-title">Contextos em que esse servico encaixa melhor</h2>
<ul className="ds-list mt-sp-5 pl-[1.15rem]">
{service.audience.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</article>
<article className="ds-card">
<div className="ds-kicker">O que muda</div>
<h2 className="ds-card-title">Resultados que eu busco destravar</h2>
<ul className="ds-list mt-sp-5 pl-[1.15rem]">
{service.outcomes.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</article>
</div>
</div>
</section>
<section className="ds-section">
<div className="ds-section-shell">
<div className="ds-grid-2">
<article className="ds-card">
<div className="ds-kicker">Como eu estruturo</div>
<h2 className="ds-card-title">Processo de trabalho</h2>
<ol className="list-none mt-sp-5 space-y-sp-4 text-[14px] leading-8 text-[var(--ink-80)]">
{service.process.map((step, index) => (
<li key={step} className="border-t border-[var(--border)] pt-sp-4 first:border-t-0 first:pt-0">
<span className="mr-sp-3 inline-flex h-7 min-w-7 items-center justify-center rounded-full border border-[var(--border)] text-[11px] font-semibold text-[var(--meta)]">
{index + 1}
</span>
{step}
</li>
))}
</ol>
</article>
<article className="ds-card">
<div className="ds-kicker">Stack e repertorio</div>
<h2 className="ds-card-title">Tecnologias e capacidades envolvidas</h2>
<p className="ds-copy">
A stack entra como meio para resolver o problema certo, com criterio de manutencao,
observabilidade e evolucao do produto.
</p>
<ul className="ds-tags">
{service.stack.map((item) => (
<MagneticButton key={item} as="li" className="ds-tag" strength={0.15}>
{item}
</MagneticButton>
))}
</ul>
</article>
</div>
</div>
</section>
<section className="ds-section">
<div className="ds-section-shell">
<div className="ds-section-header">
<div className="ds-section-meta">
<p className="ds-eyebrow">Casos relacionados</p>
<h2 className="ds-section-title">Projetos que reforcam esse tipo de entrega</h2>
<p className="ds-section-description">
Alguns exemplos de contexto tecnico em que esse servico se conecta a entregas reais.
</p>
</div>
</div>
<div className="ds-grid-2">
{relatedCases.map((caseStudy) => (
<article key={caseStudy.id} className="ds-card">
<div className="ds-kicker">{caseStudy.title}</div>
<h3 className="ds-card-title">{caseStudy.subtitle}</h3>
<p className="ds-copy">{caseStudy.summary}</p>
<p className="mt-sp-4 text-[13px] leading-7 text-[var(--meta)]">
{caseStudy.impact}
</p>
<ul className="ds-tags mt-sp-6">
{caseStudy.stack.map((item) => (
<li key={item} className="ds-tag">
{item}
</li>
))}
</ul>
<div className="mt-sp-6 flex flex-wrap gap-sp-3">
{caseStudy.demoUrl ? (
<a href={caseStudy.demoUrl} className="ds-link ds-link-accent" target="_blank" rel="noreferrer">
Abrir projeto
</a>
) : null}
{caseStudy.githubUrl ? (
<a href={caseStudy.githubUrl} className="ds-link ds-link-muted" target="_blank" rel="noreferrer">
Ver codigo
</a>
) : null}
</div>
</article>
))}
</div>
</div>
</section>
<section className="ds-section ds-section-dark">
<div className="ds-section-shell">
<div className="ds-card border-white/10 bg-[rgba(255,255,255,0.03)]">
<div className="ds-kicker">Proximo passo</div>
<h2 className="ds-card-title text-white">
Se esse servico parece aderente ao seu contexto, a conversa inicial ja pode ser objetiva.
</h2>
<p className="ds-copy !text-[rgba(255,255,255,0.76)]">
Traga o sistema, o gargalo e o objetivo de negocio. Eu estruturo a leitura tecnica e
indico o caminho mais seguro para avancar.
</p>
<div className="mt-sp-6 flex flex-wrap gap-sp-3">
<MagneticButton as="a" href={service.ctaHref} className="ds-link ds-link-accent" strength={0.25}>
{service.ctaLabel}
</MagneticButton>
<MagneticButton as="a" href="/servicos" className="ds-link ds-link-muted" strength={0.2}>
Ver todos os servicos
</MagneticButton>
</div>
</div>
</div>
</section>
</main>
);
}