feat(cases): cases de clientes, serviços ligados à prova e SEO por página
Cases da SEV Moda Íntima e da Lavação 047; case no formato cliente, problema, entrega, mídia e contato, com detalhes técnicos recolhidos; sem GitHub, ressalvas e fontes de commit. /cases separa clientes e Laboratório. Serviços mostram o case de cliente certo e o bloco de investimento e prazo só aparece preenchido. Metadata social própria por página, not-found com noindex, sitemap com /ia.
This commit is contained in:
1 parent
3756905a1b
commit
6e1022b58c
44 files changed
+2691
-1424
No files matched your search
+138
-94
@@ -1,32 +1,90 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import HeroBackground from "@/app/components/cinematic/HeroBackground";
|
||||
import MagneticButton from "@/app/components/cinematic/MagneticButton";
|
||||
import { Breadcrumb } from "@/app/components/Breadcrumb";
|
||||
import {
|
||||
pageTop,
|
||||
primaryButton,
|
||||
secondaryButtonDark,
|
||||
secondaryButtonLight,
|
||||
textLink,
|
||||
} from "@/app/components/button-styles";
|
||||
import WhatsAppLink from "@/app/components/WhatsAppLink";
|
||||
import { safeJsonLd } from "@/lib/jsonld";
|
||||
import { buildBreadcrumbList } from "@/lib/seo";
|
||||
import { caseStudies, intentCtas, siteName, siteUrl } from "@/lib/site-content";
|
||||
import { buildBreadcrumbList, buildPageSocialMetadata } from "@/lib/seo";
|
||||
import {
|
||||
type CaseStudyContent,
|
||||
clientCases,
|
||||
labCases,
|
||||
siteUrl,
|
||||
} from "@/lib/site-content";
|
||||
import { CaseCover } from "./_components/CaseCover";
|
||||
import { getCaseMedia } from "./_components/case-media";
|
||||
|
||||
const pageUrl = `${siteUrl}/cases`;
|
||||
const pageTitle = "Cases de Engenharia de Software, Next.js e IoT";
|
||||
const pageTitle = "Cases: Sites e Sistemas Feitos para Empresas";
|
||||
const pageDescription =
|
||||
"Seis projetos mostram aplicações de engenharia de software em web, desktop, IoT, integrações e experiências orientadas a dados.";
|
||||
"Veja o que já foi feito para empresas: loja virtual com pedido pelo WhatsApp, site com painel para o dono, páginas institucionais e mais. Confira cada projeto e converse sobre o seu.";
|
||||
const allCases = [...clientCases, ...labCases];
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: pageTitle,
|
||||
description: pageDescription,
|
||||
alternates: {
|
||||
canonical: pageUrl,
|
||||
},
|
||||
openGraph: {
|
||||
locale: "pt_BR",
|
||||
...buildPageSocialMetadata({
|
||||
url: pageUrl,
|
||||
title: pageTitle,
|
||||
description: pageDescription,
|
||||
url: pageUrl,
|
||||
siteName,
|
||||
type: "website",
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
function CaseCard({
|
||||
caseStudy,
|
||||
withCover,
|
||||
ctaLabel,
|
||||
}: {
|
||||
caseStudy: CaseStudyContent;
|
||||
withCover: boolean;
|
||||
ctaLabel: string;
|
||||
}) {
|
||||
return (
|
||||
<article className="ds-card !h-auto">
|
||||
{withCover && getCaseMedia(caseStudy.id, "desktop") ? (
|
||||
<Link
|
||||
href={`/cases/${caseStudy.id}`}
|
||||
aria-hidden="true"
|
||||
tabIndex={-1}
|
||||
className="mb-sp-5 block"
|
||||
>
|
||||
<CaseCover
|
||||
id={caseStudy.id}
|
||||
title={caseStudy.title}
|
||||
demoUrl={caseStudy.demoUrl}
|
||||
sizes="(min-width: 1024px) 560px, 100vw"
|
||||
/>
|
||||
</Link>
|
||||
) : null}
|
||||
<div className="ds-kicker">{caseStudy.title}</div>
|
||||
<h3 className="ds-card-title">{caseStudy.headline}</h3>
|
||||
<p className="ds-copy">{caseStudy.summary}</p>
|
||||
<div className="mt-sp-6 flex flex-wrap items-center gap-sp-3">
|
||||
<Link href={`/cases/${caseStudy.id}`} className={secondaryButtonLight}>
|
||||
{ctaLabel}
|
||||
</Link>
|
||||
{caseStudy.demoUrl ? (
|
||||
<a
|
||||
href={caseStudy.demoUrl}
|
||||
className={textLink}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Abrir o site
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
export default function CasesPage() {
|
||||
const jsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
@@ -40,7 +98,7 @@ export default function CasesPage() {
|
||||
},
|
||||
{
|
||||
"@type": "ItemList",
|
||||
itemListElement: caseStudies.map((caseStudy, index) => ({
|
||||
itemListElement: allCases.map((caseStudy, index) => ({
|
||||
"@type": "ListItem",
|
||||
position: index + 1,
|
||||
name: caseStudy.title,
|
||||
@@ -58,32 +116,18 @@ export default function CasesPage() {
|
||||
<main>
|
||||
<script type="application/ld+json">{safeJsonLd(jsonLd)}</script>
|
||||
|
||||
<section className="ds-section ds-section-dark relative min-h-[340px] overflow-hidden">
|
||||
<HeroBackground />
|
||||
<div className="ds-section-shell relative z-10">
|
||||
<section className={`ds-section ds-section-dark ${pageTop}`}>
|
||||
<div className="ds-section-shell">
|
||||
<div className="max-w-[860px]">
|
||||
<nav aria-label="Breadcrumb" className="mb-sp-6">
|
||||
<ol className="flex flex-wrap items-center gap-sp-2 text-sm">
|
||||
<li>
|
||||
<Link className="ds-link ds-link-muted" href="/">
|
||||
Início
|
||||
</Link>
|
||||
</li>
|
||||
<li className="flex items-center gap-sp-2">
|
||||
<span aria-hidden="true">/</span>
|
||||
<Link
|
||||
aria-current="page"
|
||||
className="ds-link ds-link-muted"
|
||||
href="/cases"
|
||||
>
|
||||
Cases
|
||||
</Link>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<p className="ds-eyebrow">Selected work</p>
|
||||
<h1 className="ds-section-title max-w-[14ch] text-balance text-white">
|
||||
Cases em software, Next.js, integrações e produtos conectados.
|
||||
<Breadcrumb
|
||||
items={[
|
||||
{ label: "Início", href: "/" },
|
||||
{ label: "Cases", href: "/cases" },
|
||||
]}
|
||||
/>
|
||||
<p className="ds-eyebrow">Trabalhos entregues</p>
|
||||
<h1 className="ds-section-title max-w-[18ch] text-balance text-white">
|
||||
Veja o que já fiz para empresas.
|
||||
</h1>
|
||||
<p className="ds-copy max-w-[64ch] !text-[rgba(255,255,255,0.78)]">
|
||||
{pageDescription}
|
||||
@@ -92,65 +136,65 @@ export default function CasesPage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="ds-section">
|
||||
<section className="ds-section" aria-labelledby="cases-clientes">
|
||||
<div className="ds-section-shell">
|
||||
<div className="ds-grid-2 items-start">
|
||||
{caseStudies.map((caseStudy) => (
|
||||
<article key={caseStudy.id} className="ds-card !h-auto">
|
||||
<div className="ds-kicker">{caseStudy.title}</div>
|
||||
<h2 className="ds-card-title">{caseStudy.subtitle}</h2>
|
||||
<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">
|
||||
<Link
|
||||
href={`/cases/${caseStudy.id}`}
|
||||
className="ds-link ds-link-accent"
|
||||
>
|
||||
Ler case
|
||||
</Link>
|
||||
{caseStudy.demoUrl ? (
|
||||
<MagneticButton
|
||||
as="a"
|
||||
href={caseStudy.demoUrl}
|
||||
className="ds-link ds-link-muted"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
strength={0.25}
|
||||
>
|
||||
Abrir projeto
|
||||
</MagneticButton>
|
||||
) : null}
|
||||
</div>
|
||||
</article>
|
||||
<h2 id="cases-clientes" className="ds-card-title !mt-0">
|
||||
Projetos de clientes
|
||||
</h2>
|
||||
<div className="ds-grid-2 mt-sp-8 items-start">
|
||||
{clientCases.map((caseStudy) => (
|
||||
<CaseCard
|
||||
key={caseStudy.id}
|
||||
caseStudy={caseStudy}
|
||||
withCover
|
||||
ctaLabel="Ver o case"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="mt-sp-10 flex flex-wrap gap-sp-3">
|
||||
<MagneticButton
|
||||
as="a"
|
||||
href={intentCtas.project.href}
|
||||
className="ds-link ds-link-accent"
|
||||
strength={0.25}
|
||||
<section className="ds-section" aria-labelledby="cases-laboratorio">
|
||||
<div className="ds-section-shell">
|
||||
<h2 id="cases-laboratorio" className="ds-card-title !mt-0">
|
||||
Laboratório
|
||||
</h2>
|
||||
<p className="ds-copy max-w-[64ch]">
|
||||
Projetos pessoais e de estudo, feitos para praticar e testar ideias.
|
||||
</p>
|
||||
<div className="ds-grid-2 mt-sp-8 items-start">
|
||||
{labCases.map((caseStudy) => (
|
||||
<CaseCard
|
||||
key={caseStudy.id}
|
||||
caseStudy={caseStudy}
|
||||
withCover={false}
|
||||
ctaLabel="Ver o projeto"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="ds-section ds-section-dark">
|
||||
<div className="ds-section-shell">
|
||||
<h2 className="ds-card-title !mt-0 text-white">
|
||||
Quer um projeto parecido com algum destes?
|
||||
</h2>
|
||||
<p className="ds-copy max-w-[60ch]">
|
||||
Conte o que você precisa. A conversa começa direto no WhatsApp, sem
|
||||
formulário.
|
||||
</p>
|
||||
<div className="mt-sp-6 flex flex-wrap items-center gap-sp-3">
|
||||
<WhatsAppLink
|
||||
service="um projeto parecido com os cases do portfólio"
|
||||
placement="cases_hub_footer"
|
||||
className={primaryButton}
|
||||
>
|
||||
{intentCtas.project.label}
|
||||
</MagneticButton>
|
||||
<MagneticButton
|
||||
as="a"
|
||||
href={intentCtas.consulting.href}
|
||||
className="ds-link ds-link-muted"
|
||||
strength={0.2}
|
||||
>
|
||||
{intentCtas.consulting.label}
|
||||
</MagneticButton>
|
||||
Conversar sobre meu projeto
|
||||
</WhatsAppLink>
|
||||
<Link href="/servicos" className={secondaryButtonDark}>
|
||||
Ver os serviços
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in new issue
Block a user