feat(servicos): topo, entregáveis e cases por serviço, assunto próprio no WhatsApp e títulos curtos

This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 22:54:18 -03:00
1 parent cf360041bd
commit 2fd8b063d9
33 files changed
+1578 -300

No files matched your search

+2 -2
View File
@@ -1,7 +1,7 @@
import { ImageResponse } from "next/og";
import { OG_CONTENT_TYPE, OG_SIZE, OgCard } from "@/lib/og-card";
export const alt = "Revisão de app feito com IA";
export const alt = "Revisão de sistema e de app feito com IA";
export const size = OG_SIZE;
export const contentType = OG_CONTENT_TYPE;
@@ -9,7 +9,7 @@ export default function Image() {
return new ImageResponse(
<OgCard
eyebrow="IA no desenvolvimento"
title="Revisão de app feito com IA"
title="Revisão de sistema e de app feito com IA"
description="Falhas de acesso a dados, o que aproveitar, o que refazer e um orçamento para pôr em produção."
/>,
OG_SIZE,
+26 -6
View File
@@ -1,18 +1,24 @@
import { render, screen, within } from "@testing-library/react";
import { offerLabels } from "@/lib/offer-labels";
import AiPage, { metadata } from "./page";
it("uses a short search title: with the layout suffix it fits in 44 characters", () => {
it("uses a short search title: up to 44 characters, and 60 with the layout suffix", () => {
// o template de app/layout.tsx acrescenta " | Evandro.dev.br"
const suffix = " | Evandro.dev.br";
expect(metadata.title).toBe("Revisão de app feito com IA");
expect(`${metadata.title}${suffix}`.length).toBeLessThanOrEqual(44);
expect(metadata.title).toBe("Revisão de sistema e de app feito com IA");
expect(String(metadata.title).length).toBeLessThanOrEqual(44);
expect(`${metadata.title}${suffix}`.length).toBeLessThanOrEqual(60);
expect(metadata).toMatchObject({
description: expect.any(String),
alternates: { canonical: "https://evandro.dev.br/ia" },
});
// como nas outras páginas, o título da prévia de compartilhamento é o mesmo da busca, sem o sufixo
expect(metadata.openGraph?.title).toBe("Revisão de app feito com IA");
expect(metadata.twitter?.title).toBe("Revisão de app feito com IA");
expect(metadata.openGraph?.title).toBe(
"Revisão de sistema e de app feito com IA",
);
expect(metadata.twitter?.title).toBe(
"Revisão de sistema e de app feito com IA",
);
});
it("uses no lime outside the contact actions (the quote on the paper background is ink)", () => {
@@ -75,7 +81,7 @@ it("has breadcrumb navigation back to the home", () => {
within(breadcrumb).getByRole("link", { name: "Início" }),
).toHaveAttribute("href", "/");
expect(
within(breadcrumb).getByText("Revisão de app feito com IA"),
within(breadcrumb).getByText(offerLabels.revisao.name),
).toHaveAttribute("aria-current", "page");
});
@@ -232,3 +238,17 @@ it("keeps the incident news collapsed in a <details>, closed by default", () =>
.closest("details"),
).toBeNull();
});
it("names the review like the offer on the home: the trail and the structured data use the offer name, not the search title", () => {
const { container } = render(<AiPage />);
const script = container.querySelector('script[type="application/ld+json"]');
const data = JSON.parse(script?.textContent ?? "{}");
const breadcrumb = data["@graph"].find(
(node: { "@type": string }) => node["@type"] === "BreadcrumbList",
);
expect(
breadcrumb.itemListElement.map(({ name }: { name: string }) => name),
).toEqual(["Início", offerLabels.revisao.name]);
expect(offerLabels.revisao.name).toBe("Revisão de sistema que já existe");
});
+8 -4
View File
@@ -8,13 +8,17 @@ import {
import HoverPrefetchLink from "@/app/components/HoverPrefetchLink";
import WhatsAppLink from "@/app/components/WhatsAppLink";
import { safeJsonLd } from "@/lib/jsonld";
import { offerLabels } from "@/lib/offer-labels";
import { buildBreadcrumbList, buildPageSocialMetadata } from "@/lib/seo";
import { siteUrl } from "@/lib/site-content";
import { AI_REVIEW_SUBJECT } from "@/lib/whatsapp";
const pageUrl = `${siteUrl}/ia`;
/** Curto de propósito: o template de app/layout.tsx acrescenta " | Evandro.dev.br" e o conjunto cabe em 44 caracteres. */
const pageTitle = "Revisão de app feito com IA";
/**
* Título para busca: até 44 caracteres, e 60 com o " | Evandro.dev.br" que o template de app/layout.tsx acrescenta. Cobre a oferta
* (revisão de sistema) e a busca por app feito com IA. O nome da oferta, o mesmo da home, é o da trilha e dos dados estruturados.
*/
const pageTitle = "Revisão de sistema e de app feito com IA";
const pageDescription =
"Fez um app com Lovable, Bolt ou Replit? Eu reviso as falhas de acesso a dados, separo o que aproveitar do que refazer e faço o orçamento para a produção.";
/** Entra na frase "Quero conversar sobre {assunto}." da mensagem do WhatsApp (a mesma do botão flutuante em /ia). */
@@ -167,7 +171,7 @@ export default function AiPage() {
"@graph": [
buildBreadcrumbList(pageUrl, [
["Início", siteUrl],
["Revisão de app feito com IA", pageUrl],
[offerLabels.revisao.name, pageUrl],
] as const),
],
};
@@ -181,7 +185,7 @@ export default function AiPage() {
<Breadcrumb
items={[
{ label: "Início", href: "/" },
{ label: "Revisão de app feito com IA", href: "/ia" },
{ label: offerLabels.revisao.name, href: "/ia" },
]}
/>
<div className="grid items-center gap-sp-8 lg:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)] lg:gap-sp-12">