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:
Evandro Fonseca Junior committed 2026-10-14 19:14:46 -03:00
1 parent e63f3c2ebd
commit fc44a69fb3
41 files changed
+2486 -550

No files matched your search

+186 -22
View File
@@ -4,6 +4,25 @@ import { isClientProject } from "./proof";
export const siteUrl = "https://evandro.dev.br";
export const siteName = "Evandro.dev.br";
/** Aparelho cuja captura abre a página do serviço: o site no computador (moldura de navegador) ou no celular (moldura de celular). */
export type HeroDevice = "desktop" | "mobile";
/** Esquema de fluxo que ocupa o topo do serviço sem case com captura gravada (app/servicos/_components/ServiceDiagram.tsx). */
export type ServiceDiagramKind = "desktop" | "iot" | "arquitetura";
/**
* Rótulos dos botões e links de ação, numa lista só: o contato diz sempre a mesma coisa, e o case também.
* O verde-limão fica só no contato; os demais são contorno ou link de texto.
*/
export const buttonLabels = {
contact: "Falar sobre meu projeto",
case: "Ver case",
site: "Abrir o site",
service: "Ver o serviço",
allCases: "Ver os cases",
allServices: "Ver todos os serviços",
} as const;
export interface ServicePageContent {
slug: string;
navLabel: string;
@@ -12,6 +31,7 @@ export interface ServicePageContent {
metaTitle: string;
/** Descrição para busca, com até 155 caracteres. */
metaDescription: string;
/** Título da página (h1). Nos serviços que são uma das ofertas da home, é o nome da oferta. */
headline: string;
intro: string;
answer: string;
@@ -21,9 +41,14 @@ export interface ServicePageContent {
examples: string[];
technicalDetails: string[];
stack: string[];
ctaLabel: string;
/** Ids dos cases relacionados; os de cliente aparecem primeiro na página. */
relatedCaseIds: string[];
/** Capa do topo: o case (um dos relacionados) e o aparelho da captura. Sem ela, o topo mostra o esquema `diagram`. */
heroCase?: { id: string; device: HeroDevice };
/** Esquema de fluxo do topo, para o serviço sem case com captura: nunca leva dado real nem logo de cliente. */
diagram?: ServiceDiagramKind;
/** Data (AAAA-MM-DD) da última mudança no conteúdo da página: vira o `lastmod` do mapa do site. Atualize ao mudar o texto. */
updatedAt: string;
/** Por que cada case relacionado interessa a este serviço, em uma frase (chave: id do case). */
caseRelevance: Record<string, string>;
/** Experiência profissional citada junto dos cases, só em texto, sem link nem detalhe interno. */
@@ -59,6 +84,10 @@ export interface CaseStudyContent {
facts?: string[];
/** O que foi feito, escrito como benefício. */
deliveries: string[];
/** O que o cliente passou a poder fazer (2 ou 3 itens, sem número), só com o que as entregas já sustentam. Sem itens, a seção não aparece. */
capabilities?: string[];
/** Data (AAAA-MM-DD) da última mudança no conteúdo da página: vira o `lastmod` do mapa do site. Atualize ao mudar o texto. */
updatedAt: string;
/** Resultado com dado real e autorizado. Sem dado, o campo fica de fora e a seção não aparece. */
result?: string[];
/** Detalhes técnicos: tecnologias e decisões de projeto (ficam recolhidos, logo depois do que foi feito). */
@@ -77,7 +106,7 @@ export const servicePages: ServicePageContent[] = [
metaTitle: "Criação de sites em Joinville e SC",
metaDescription:
"Site profissional para apresentar sua empresa e levar o cliente ao WhatsApp. Atendimento em Garuva e Joinville (SC) e remoto em todo o Brasil.",
headline: "Um site que explica seu negócio.",
headline: "Site que traz pedidos pelo WhatsApp",
answer:
"Crio sites para apresentar sua empresa, mostrar o que você oferece e facilitar o contato. O projeto considera leitura no celular e no computador, conteúdo, páginas e funcionalidades combinadas antes do desenvolvimento.",
intro:
@@ -109,11 +138,10 @@ export const servicePages: ServicePageContent[] = [
"Metadados, URLs canônicas, sitemap e dados estruturados ajudam os buscadores a entender o site.",
],
stack: ["Next.js", "TypeScript", "Tailwind CSS", "SEO técnico", "Vercel"],
ctaLabel: "Conversar sobre meu site",
relatedCaseIds: ["sev-modaintima", "lavacao-047", "sc-plus", "as-servicos"],
relatedCaseIds: ["lavacao-047", "sc-plus", "as-servicos"],
heroCase: { id: "lavacao-047", device: "desktop" },
updatedAt: "2026-10-14",
caseRelevance: {
"sev-modaintima":
"Um catálogo com busca e filtros que leva o pedido da cliente direto ao WhatsApp da loja.",
"lavacao-047":
"Um site local com preço por porte do veículo e pedido pelo WhatsApp, que o dono atualiza sozinho.",
"sc-plus":
@@ -146,7 +174,7 @@ export const servicePages: ServicePageContent[] = [
metaTitle: "Sistemas web e aplicativos sob medida",
metaDescription:
"Sistema web ou aplicativo sob medida para organizar pedidos, clientes e rotinas. Atendimento em Garuva e Joinville (SC) e remoto em todo o Brasil.",
headline: "Sistemas e apps para o seu trabalho.",
headline: "Sistema para tirar a operação da planilha",
answer:
"Desenvolvo sistemas web, usados no navegador, e aplicativos mobile para celular. Também conecto ferramentas e evoluo sistemas existentes. Definimos juntos os usuários, as funções, as plataformas atendidas e a primeira entrega útil para o negócio.",
intro:
@@ -178,8 +206,9 @@ export const servicePages: ServicePageContent[] = [
"Em aplicativos mobile, a tecnologia é escolhida depois de avaliar os dispositivos e os recursos necessários, como câmera, notificações e uso sem internet.",
],
stack: ["Next.js", "TypeScript", "Node.js", "PostgreSQL", "APIs REST"],
ctaLabel: "Conversar sobre meu sistema ou app",
relatedCaseIds: ["sev-modaintima", "lavacao-047"],
heroCase: { id: "sev-modaintima", device: "mobile" },
updatedAt: "2026-10-14",
caseRelevance: {
"sev-modaintima":
"Um sistema com painel para cadastrar produtos, controlar o estoque por tamanho e acompanhar pedidos.",
@@ -243,15 +272,14 @@ export const servicePages: ServicePageContent[] = [
"Auditoria de metadados, canônicas, sitemap, dados estruturados e links internos, preservando URLs e requisitos de indexação nas mudanças.",
],
stack: ["Next.js", "React", "TypeScript", "Tailwind", "SEO técnico"],
ctaLabel: "Conversar sobre meu projeto Next.js",
relatedCaseIds: ["sev-modaintima", "lavacao-047", "sc-plus"],
relatedCaseIds: ["sc-plus", "lavacao-047"],
heroCase: { id: "sc-plus", device: "desktop" },
updatedAt: "2026-10-14",
caseRelevance: {
"sev-modaintima":
"Uma loja que construí em Next.js 16, com catálogo, busca, estoque e painel.",
"lavacao-047":
"Um site que construí em Next.js 16, em que o conteúdo do painel alimenta a página, o sitemap e os dados para os buscadores.",
"sc-plus":
"Uma página em Next.js 15 com título, descrição, endereço canônico e dados estruturados para os buscadores.",
"lavacao-047":
"Um site que construí em Next.js 16, em que o conteúdo do painel alimenta a página, o sitemap e os dados para os buscadores.",
},
faq: [
{
@@ -315,11 +343,12 @@ export const servicePages: ServicePageContent[] = [
"Aplicativos desktop",
"Integração com o sistema",
],
ctaLabel: "Conversar sobre meu aplicativo desktop",
relatedCaseIds: ["gcc-installer"],
diagram: "desktop",
updatedAt: "2026-10-14",
caseRelevance: {
"gcc-installer":
"Um aplicativo para Windows feito por mim, em Python e não em Tauri: lista as versões do compilador, instala e ajusta o PATH em uma única janela.",
"Um projeto próprio de aplicativo para Windows: lista as versões do compilador, instala e ajusta o PATH em uma única janela.",
},
faq: [
{
@@ -378,8 +407,9 @@ export const servicePages: ServicePageContent[] = [
"O Twins Lab, protótipo de laboratório, simula um reator e envia o estado dele a um painel a cada dois segundos, por eventos do servidor.",
],
stack: ["IoT", "WebSockets", "APIs", "Painéis", "Integração de sistemas"],
ctaLabel: "Avaliar minha integração",
relatedCaseIds: ["twins-lab"],
diagram: "iot",
updatedAt: "2026-10-14",
caseRelevance: {
"twins-lab":
"Um reator simulado envia seu estado a um painel a cada dois segundos, com o histórico guardado.",
@@ -449,8 +479,9 @@ export const servicePages: ServicePageContent[] = [
"Integrações",
"Sistemas críticos",
],
ctaLabel: "Conversar sobre um diagnóstico",
relatedCaseIds: [],
diagram: "arquitetura",
updatedAt: "2026-10-14",
caseRelevance: {},
experienceNote:
"Desde 2022 desenvolvo sistemas internos, aplicativos e APIs na Apolo Energia e participo do planejamento e da arquitetura de novas soluções. Por serem sistemas de uso interno, não há link nem detalhes públicos.",
@@ -482,7 +513,7 @@ export const caseStudies: CaseStudyContent[] = [
metaTitle: "SEV Moda Íntima: loja virtual com WhatsApp",
metaDescription:
"Loja virtual de moda íntima: a cliente escolhe o tamanho e envia o pedido pelo WhatsApp, e a loja controla produtos e estoque em um painel.",
headline: "Loja virtual de moda íntima com pedido direto pelo WhatsApp",
headline: "Loja virtual de moda íntima com pedido pelo WhatsApp",
summary:
"Catálogo digital e loja virtual de moda íntima. A cliente encontra o produto, escolhe o tamanho e envia o pedido pelo WhatsApp. A loja cuida de tudo em um painel próprio.",
impact:
@@ -499,6 +530,12 @@ export const caseStudies: CaseStudyContent[] = [
"Frete calculado pela distância até o endereço da cliente.",
"Uma vitrine com animações que dão destaque aos produtos e deixam a navegação agradável.",
],
capabilities: [
"A cliente encontra a peça, escolhe o tamanho e envia o pedido pelo WhatsApp.",
"A loja cadastra as peças e acompanha os pedidos em um painel próprio.",
"A loja controla o estoque de cada tamanho e sabe o que ainda tem para vender.",
],
updatedAt: "2026-10-14",
stack: [
"Next.js 16",
"TypeScript",
@@ -529,9 +566,9 @@ export const caseStudies: CaseStudyContent[] = [
impact:
"O cliente descobre o preço e pede pelo WhatsApp; o dono atualiza o site sem depender de um desenvolvedor.",
context:
"A Lavação 047 é uma lavação automotiva em Garuva, Santa Catarina. O site apresenta os serviços, a tabela de preços e a localização, e leva o cliente direto para o WhatsApp.",
"A Lavação 047 é uma lavação automotiva em Garuva, Santa Catarina. O site apresenta os serviços, a tabela de preços e a localização, e leva o cliente ao WhatsApp.",
problem:
"O cliente chega com perguntas diretas: quanto custa para o meu carro, onde fica e como pedir. O preço muda com o porte do veículo, e o contato é pelo WhatsApp. O dono, por sua vez, precisa mudar preços, textos e fotos quando quiser, sem esperar por um desenvolvedor.",
"O cliente chega com perguntas simples: quanto custa para o meu carro, onde fica e como pedir. O preço muda com o porte do veículo, e o contato é pelo WhatsApp. O dono, por sua vez, precisa mudar preços, textos e fotos quando quiser, sem esperar por um desenvolvedor.",
deliveries: [
"Tabela de preços por porte do veículo: o cliente vê quanto paga pelo seu carro.",
"Pedido pelo WhatsApp com mensagem pronta: um toque abre a conversa já com o serviço escolhido.",
@@ -541,6 +578,12 @@ export const caseStudies: CaseStudyContent[] = [
"Informações organizadas para o Google e para assistentes de IA entenderem o que a lavação faz e onde fica.",
"Uma animação de rolagem que leva o carro do sujo ao brilho e perguntas frequentes montadas com os dados reais do negócio.",
],
capabilities: [
"O dono muda preços, textos, fotos e o mapa sem pedir a ninguém, e a mudança aparece no site sem nova publicação.",
"O cliente vê o preço pelo porte do veículo e pede o serviço pelo WhatsApp com a mensagem já pronta.",
"O cliente confere a galeria de trabalhos de cada serviço antes de chamar.",
],
updatedAt: "2026-10-14",
stack: [
"Next.js 16",
"React 19",
@@ -587,6 +630,7 @@ export const caseStudies: CaseStudyContent[] = [
"A camada de acesso à base externa cancela requisições após oito segundos e separa a busca por nome da busca por código.",
],
demoUrl: "https://nutriscan.evandro.dev.br",
updatedAt: "2026-10-14",
},
{
id: "twins-lab",
@@ -617,6 +661,7 @@ export const caseStudies: CaseStudyContent[] = [
"A resposta usa text/event-stream (SSE), envia estados, dados e erros em intervalos de dois segundos e limita a conexão a dez minutos.",
"O simulador salva dados e estado no MongoDB e notifica os clientes quando a execução ou o cenário mudam.",
],
updatedAt: "2026-10-14",
},
{
id: "reddit-explorer",
@@ -647,6 +692,7 @@ export const caseStudies: CaseStudyContent[] = [
"O endpoint de filtro valida as datas e o campo de ordenação (votos ou comentários) e responde com erro 400 quando faltam parâmetros ou eles são inválidos.",
"O Astro roda com saída de servidor e o adaptador Node em modo standalone.",
],
updatedAt: "2026-10-14",
},
{
id: "sc-plus",
@@ -667,11 +713,17 @@ export const caseStudies: CaseStudyContent[] = [
"A instituição tem várias áreas de atuação e projetos. Quem chega pela primeira vez precisa entender o que ela faz e saber como entrar em contato. Sem um endereço próprio, isso não fica reunido em um só lugar.",
deliveries: [
"Uma página própria que apresenta a instituição, as áreas de atuação e os projetos.",
"Contato direto na própria página.",
"Contato na própria página.",
"Uma seção de apoiadores e de participação, para quem quer apoiar ou participar.",
"Título, descrição e imagem de compartilhamento próprios, para o link aparecer bem nos buscadores e nas redes.",
"Dados estruturados que ajudam o Google a entender de quem é a página.",
],
capabilities: [
"Quem chega pela primeira vez vê a instituição, as áreas de atuação e os projetos, e tem o contato na própria página.",
"Quem quer apoiar ou participar encontra o caminho na própria página.",
"O link aparece com título, descrição e imagem próprios nos buscadores e nas redes.",
],
updatedAt: "2026-10-14",
stack: ["Next.js 15", "React 19", "Tailwind CSS 4", "TypeScript"],
decisions: [
"A página publica título, descrição, endereço canônico e imagem de compartilhamento.",
@@ -708,6 +760,7 @@ export const caseStudies: CaseStudyContent[] = [
"Adicionar ao PATH grava em HKCU\\Environment e avisa o Windows com WM_SETTINGCHANGE.",
"Os 23 testes automatizados cobrem a lógica do núcleo, que não depende da plataforma.",
],
updatedAt: "2026-10-14",
},
{
id: "as-servicos",
@@ -731,6 +784,12 @@ export const caseStudies: CaseStudyContent[] = [
"A qualidade da cena se ajusta ao aparelho: celulares, conexões mais simples e quem prefere menos movimento recebem uma versão mais leve.",
"Um painel para controle de projetos, desenvolvido junto com a primeira página do site.",
],
capabilities: [
"A empresa conta a engenharia física e o software na mesma história, em vez de duas listas de serviços sem relação.",
"Quem visita no celular ou com conexão mais simples recebe uma versão mais leve da cena.",
"A empresa passou a ter um painel para controle de projetos.",
],
updatedAt: "2026-10-14",
stack: ["Next.js 16", "Three.js", "TypeScript"],
decisions: [
"A cena é gerada em código, sem arquivo 3D pronto: o motor monta casa, instalação elétrica e placa-mãe com geometrias e linhas do Three.js.",
@@ -756,6 +815,111 @@ export const labCases = caseStudies.filter(
(caseStudy) => !isClientCase(caseStudy),
);
/**
* Ordem dos clientes na lista de /cases e no mosaico de /servicos: a SEV Moda Íntima, que já abre a página de sistemas
* e é a prova da oferta de sistema, vem por último e fica fora das três capas do mosaico. Cliente fora desta lista vai para o fim.
*/
const clientListingOrder = [
"lavacao-047",
"sc-plus",
"as-servicos",
"sev-modaintima",
];
function listingRank({ id }: { id: string }) {
const rank = clientListingOrder.indexOf(id);
return rank === -1 ? clientListingOrder.length : rank;
}
export const clientCasesForListing = [...clientCases].sort(
(a, b) => listingRank(a) - listingRank(b),
);
/** Data (AAAA-MM-DD) da última mudança no conteúdo de cada página fixa: vira o `lastmod` do mapa do site. */
export const pageUpdatedAt = {
home: "2026-10-14",
servicos: "2026-10-14",
cases: "2026-10-14",
ia: "2026-10-14",
curriculo: "2026-10-14",
// a própria página informa a vigência (13 de setembro de 2026)
privacidade: "2026-09-13",
} as const;
export interface ServiceOffer {
id: "site" | "sistema" | "revisao";
/** Nome da oferta, o mesmo da home. */
name: string;
/** Uma frase sobre a oferta (descrição dos dados estruturados). */
summary: string;
/** Página que explica a oferta. */
href: string;
/** O que a pessoa recebe, em 3 ou 4 itens, só com o que os cases ou a página da oferta já sustentam. */
deliverables: string[];
/** Onde ver a oferta funcionando: o texto que abre a linha, os cases (links) e uma observação opcional. */
proof: { label: string; caseIds: string[]; note?: string };
}
/**
* As três ofertas da home, na ordem do H1, com o mesmo nome. Cada texto só diz o que os cases citados, a experiência
* profissional ou a página /ia já sustentam; sem prazo nem preço.
*/
export const serviceOffers: ServiceOffer[] = [
{
id: "site",
name: "Site que traz pedidos pelo WhatsApp",
summary:
"Catálogo ou tabela de preços, mensagem pronta no WhatsApp e um painel para você editar preços, textos e fotos sozinho.",
href: "/servicos/criacao-de-sites",
deliverables: [
"Tabela de preços ou catálogo, para o cliente ver o valor antes de chamar.",
"Botão de WhatsApp com a mensagem pronta, para o pedido chegar com o serviço escolhido.",
"Painel para você mudar preços, textos e fotos sem pedir a ninguém.",
"Leitura boa no celular e informações organizadas para o Google.",
],
proof: {
label: "Veja funcionando",
caseIds: ["lavacao-047", "sev-modaintima"],
},
},
{
id: "sistema",
name: "Sistema para tirar a operação da planilha",
summary:
"Pedidos, estoque e rotinas que hoje vivem em planilhas e mensagens passam para um painel próprio, feito para a sua equipe.",
href: "/servicos/engenharia-de-software",
deliverables: [
"Catálogo e cadastro de produtos em um painel próprio.",
"Pedidos e estoque acompanhados em um só lugar, no lugar de planilhas e mensagens.",
"Acesso por perfil: cada pessoa vê só o que precisa.",
"Uma primeira versão com as funções prioritárias, ampliada em etapas.",
],
proof: {
label: "Veja funcionando",
caseIds: ["sev-modaintima"],
note: "Na Apolo Energia, desde 2022, desenvolvo aplicações web e mobile para rotinas internas, APIs e painéis de operação. Por serem sistemas de uso interno, não há link nem detalhes públicos.",
},
},
{
id: "revisao",
name: "Revisão de sistema que já existe, inclusive feito com IA",
summary:
"Avaliação do que já existe, teste das falhas de acesso a dados e separação do que dá para aproveitar do que é melhor refazer.",
href: "/ia",
deliverables: [
"As falhas de acesso a dados: quem consegue ver ou alterar o dado de quem.",
"O que dá para aproveitar do que já está pronto.",
"O que é melhor refazer, com o motivo de cada parte.",
"Um orçamento por escrito para pôr o sistema em produção.",
],
proof: {
label: "Veja como funciona",
caseIds: [],
note: "A página da revisão mostra o que você recebe, três episódios públicos com plataformas de apps feitos com IA e os cuidados com dados pessoais (LGPD).",
},
},
];
export const serviceHubLinks = servicePages.map((service) => ({
href: `/servicos/${service.slug}`,
label: service.navLabel,