feat(servicos): ofertas no topo de /servicos, capa por assunto e diagramas
/servicos abre com as 3 ofertas e depois outros serviços; H1 de criação de sites e de engenharia com o nome da oferta; capa de cada serviço pelo assunto (SEV fora do próprio case cai de ~11 para 2 imagens); diagrama de fluxo em SVG nos serviços sem case; cases com o que o cliente passou a poder fazer e contato logo depois de Como ficou; FAQ num componente só; lastmod por rota; ProfessionalService com areaServed.
This commit is contained in:
1 parent
e63f3c2ebd
commit
fc44a69fb3
41 files changed
+2486
-550
No files matched your search
@@ -11,6 +11,7 @@ import {
|
||||
primaryButton,
|
||||
secondaryButtonDark,
|
||||
} from "@/app/components/button-styles";
|
||||
import { Faq } from "@/app/components/Faq";
|
||||
import HoverPrefetchLink from "@/app/components/HoverPrefetchLink";
|
||||
import WhatsAppLink from "@/app/components/WhatsAppLink";
|
||||
import { safeJsonLd } from "@/lib/jsonld";
|
||||
@@ -20,12 +21,14 @@ import {
|
||||
buildServiceJsonLd,
|
||||
} from "@/lib/seo";
|
||||
import {
|
||||
buttonLabels,
|
||||
caseStudies,
|
||||
getRelatedCases,
|
||||
getServicePage,
|
||||
servicePages,
|
||||
siteUrl,
|
||||
} from "@/lib/site-content";
|
||||
import { ServiceFaq } from "../_components/ServiceFaq";
|
||||
import { ServiceDiagram } from "../_components/ServiceDiagram";
|
||||
import { ServiceHeroCover } from "../_components/ServiceHeroCover";
|
||||
import { ServiceOffer } from "../_components/ServiceOffer";
|
||||
import { ServiceRelatedCases } from "../_components/ServiceRelatedCases";
|
||||
@@ -75,9 +78,16 @@ export default async function ServicePage({ params }: ServicePageProps) {
|
||||
}
|
||||
|
||||
const relatedCases = getRelatedCases(service.relatedCaseIds);
|
||||
const heroCase = relatedCases.find((caseStudy) =>
|
||||
getCaseMedia(caseStudy.id, "desktop"),
|
||||
// capa do topo: o case escolhido para o serviço, se tiver captura gravada do aparelho pedido; senão, o esquema de fluxo
|
||||
const heroDevice = service.heroCase?.device ?? "desktop";
|
||||
const heroCase = caseStudies.find(
|
||||
({ id }) =>
|
||||
id === service.heroCase?.id && Boolean(getCaseMedia(id, heroDevice)),
|
||||
);
|
||||
const diagram = heroCase ? undefined : service.diagram;
|
||||
const hasHeroSide = Boolean(heroCase || diagram);
|
||||
// o case da capa é citado pela legenda dela, e não ganha cartão em "Onde já apliquei isso"
|
||||
const hasCaseCards = relatedCases.some(({ id }) => id !== heroCase?.id);
|
||||
const pageUrl = `${siteUrl}/servicos/${service.slug}`;
|
||||
|
||||
const jsonLd = {
|
||||
@@ -98,11 +108,11 @@ export default async function ServicePage({ params }: ServicePageProps) {
|
||||
|
||||
<section
|
||||
className={`ds-section ds-section-dark ${pageTop} ${
|
||||
heroCase ? "" : compactHero
|
||||
hasHeroSide ? "" : compactHero
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`ds-section-shell ${heroCase ? heroWithMediaGrid : ""}`}
|
||||
className={`ds-section-shell ${hasHeroSide ? heroWithMediaGrid : ""}`}
|
||||
>
|
||||
<div className="max-w-[860px]">
|
||||
<Breadcrumb
|
||||
@@ -113,7 +123,7 @@ export default async function ServicePage({ params }: ServicePageProps) {
|
||||
]}
|
||||
/>
|
||||
<p className="ds-eyebrow">{service.eyebrow}</p>
|
||||
<h1 className="ds-section-title max-w-[14ch] text-balance text-white">
|
||||
<h1 className="ds-section-title max-w-[22ch] text-balance text-white">
|
||||
{service.headline}
|
||||
</h1>
|
||||
<p className="ds-copy max-w-[64ch] !text-[rgba(255,255,255,0.78)]">
|
||||
@@ -125,21 +135,30 @@ export default async function ServicePage({ params }: ServicePageProps) {
|
||||
placement="service_detail_hero"
|
||||
className={primaryButton}
|
||||
>
|
||||
{service.ctaLabel} no WhatsApp
|
||||
{buttonLabels.contact}
|
||||
</WhatsAppLink>
|
||||
{relatedCases.length > 0 ? (
|
||||
{hasCaseCards ? (
|
||||
<a href="#casos-relacionados" className={secondaryButtonDark}>
|
||||
Ver projetos parecidos
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
<p className="mt-sp-4 text-[15px] leading-6 text-white/70">
|
||||
Fale diretamente com Evandro. Abre uma conversa, sem precisar
|
||||
preencher um formulário. Garuva e Joinville (SC) · atendimento
|
||||
remoto em todo o Brasil.
|
||||
O botão abre uma conversa no WhatsApp, sem formulário. Garuva e
|
||||
Joinville (SC) · atendimento remoto em todo o Brasil.
|
||||
</p>
|
||||
</div>
|
||||
<ServiceHeroCover caseStudy={heroCase} />
|
||||
{diagram ? (
|
||||
<ServiceDiagram kind={diagram} />
|
||||
) : (
|
||||
<ServiceHeroCover
|
||||
caseStudy={heroCase}
|
||||
device={heroDevice}
|
||||
relevance={
|
||||
heroCase ? service.caseRelevance[heroCase.id] : undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -147,6 +166,7 @@ export default async function ServicePage({ params }: ServicePageProps) {
|
||||
cases={relatedCases}
|
||||
relevance={service.caseRelevance}
|
||||
experienceNote={service.experienceNote}
|
||||
heroCaseId={heroCase?.id}
|
||||
/>
|
||||
|
||||
<section className="ds-section">
|
||||
@@ -232,7 +252,7 @@ export default async function ServicePage({ params }: ServicePageProps) {
|
||||
|
||||
<ServiceOffer service={service} />
|
||||
|
||||
<ServiceFaq faq={service.faq} />
|
||||
<Faq items={service.faq} />
|
||||
|
||||
<section className="ds-section ds-section-dark">
|
||||
<div className="ds-section-shell">
|
||||
@@ -252,13 +272,13 @@ export default async function ServicePage({ params }: ServicePageProps) {
|
||||
placement="service_detail_footer"
|
||||
className={primaryButton}
|
||||
>
|
||||
{service.ctaLabel} no WhatsApp
|
||||
{buttonLabels.contact}
|
||||
</WhatsAppLink>
|
||||
<HoverPrefetchLink
|
||||
href="/servicos"
|
||||
className={secondaryButtonDark}
|
||||
>
|
||||
Ver todos os serviços
|
||||
{buttonLabels.allServices}
|
||||
</HoverPrefetchLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user