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
@@ -4,6 +4,7 @@ import {
|
||||
buildCaseJsonLd,
|
||||
buildPageSocialMetadata,
|
||||
buildPersonJsonLd,
|
||||
buildProfessionalServiceJsonLd,
|
||||
buildProfilePageJsonLd,
|
||||
buildServiceJsonLd,
|
||||
buildWebSiteJsonLd,
|
||||
@@ -14,6 +15,7 @@ import {
|
||||
import {
|
||||
caseStudies,
|
||||
isClientCase,
|
||||
serviceOffers,
|
||||
servicePages,
|
||||
siteName,
|
||||
siteUrl,
|
||||
@@ -27,6 +29,7 @@ it("uses one canonical identity across website, profile and service", () => {
|
||||
website: `${siteUrl}/#website`,
|
||||
person: `${siteUrl}/#person`,
|
||||
profilePage: `${siteUrl}/curriculo#profile-page`,
|
||||
professionalService: `${siteUrl}/#professional-service`,
|
||||
});
|
||||
expect(buildWebSiteJsonLd().publisher).toEqual({ "@id": SEO_IDS.person });
|
||||
expect(buildProfilePageJsonLd()).toMatchObject({
|
||||
@@ -98,6 +101,47 @@ it("builds a canonical service with only the verified service area", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("describes the business as a ProfessionalService with the service area and the three offers as a catalog", () => {
|
||||
const business = buildProfessionalServiceJsonLd();
|
||||
|
||||
expect(business).toMatchObject({
|
||||
"@type": "ProfessionalService",
|
||||
"@id": SEO_IDS.professionalService,
|
||||
name: siteName,
|
||||
url: siteUrl,
|
||||
founder: { "@id": SEO_IDS.person },
|
||||
});
|
||||
expect(business.areaServed.map(({ name }) => name)).toEqual([
|
||||
"Garuva",
|
||||
"Joinville",
|
||||
"Santa Catarina",
|
||||
"Brasil",
|
||||
]);
|
||||
|
||||
const catalog = business.hasOfferCatalog;
|
||||
expect(catalog["@type"]).toBe("OfferCatalog");
|
||||
// as mesmas três ofertas da home, com o mesmo nome e a página de cada uma
|
||||
expect(
|
||||
catalog.itemListElement.map(({ itemOffered }) => itemOffered.name),
|
||||
).toEqual([
|
||||
"Site que traz pedidos pelo WhatsApp",
|
||||
"Sistema para tirar a operação da planilha",
|
||||
"Revisão de sistema que já existe, inclusive feito com IA",
|
||||
]);
|
||||
expect(
|
||||
catalog.itemListElement.map(({ itemOffered }) => itemOffered.url),
|
||||
).toEqual(serviceOffers.map(({ href }) => `${siteUrl}${href}`));
|
||||
for (const offer of catalog.itemListElement) {
|
||||
expect(offer["@type"]).toBe("Offer");
|
||||
expect(offer.itemOffered["@type"]).toBe("Service");
|
||||
expect(offer.itemOffered.description.length).toBeGreaterThan(30);
|
||||
}
|
||||
// nada que o site não publique: sem preço, prazo, endereço de rua, telefone ou e-mail
|
||||
expect(JSON.stringify(business)).not.toMatch(
|
||||
/price|priceCurrency|availability|telephone|email|streetAddress|PostalAddress|geo|aggregateRating|review/i,
|
||||
);
|
||||
});
|
||||
|
||||
function getCase(id: string) {
|
||||
const caseStudy = caseStudies.find((item) => item.id === id);
|
||||
if (!caseStudy) throw new Error(`Missing case ${id}`);
|
||||
@@ -214,6 +258,10 @@ it("lists the client cases and the AI page, each with a fixed modification date"
|
||||
false,
|
||||
);
|
||||
}
|
||||
// a data é a do conteúdo de cada rota, não uma data única para o mapa inteiro
|
||||
expect(
|
||||
new Set(entries.map(({ lastModified }) => String(lastModified))).size,
|
||||
).toBeGreaterThan(1);
|
||||
});
|
||||
|
||||
it("gives every page a complete social card, so it never inherits the home one", () => {
|
||||
|
||||
Reference in new issue
Block a user