/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.
164 lines
4.6 KiB
TypeScript
164 lines
4.6 KiB
TypeScript
import type { ServiceDiagramKind } from "@/lib/site-content";
|
|
|
|
/**
|
|
* Esquema de fluxo no topo do serviço que não tem case com captura gravada, no lugar da coluna vazia ao lado do título.
|
|
* SVG em linha, só com as cores do site (papel e branco sobre o fundo escuro, sem o verde-limão, que é do contato),
|
|
* sem dado real e sem logo de cliente. Para quem lê a tela, o esquema é uma imagem com o fluxo descrito em `aria-label`.
|
|
*/
|
|
|
|
const WIDTH = 480;
|
|
const HEIGHT = 404;
|
|
const ARROW_HEAD = 12;
|
|
|
|
interface DiagramNode {
|
|
label: string;
|
|
x: number;
|
|
y: number;
|
|
width: number;
|
|
height: number;
|
|
}
|
|
|
|
interface DiagramArrow {
|
|
x: number;
|
|
from: number;
|
|
to: number;
|
|
/** Ponta nas duas extremidades: a informação anda nos dois sentidos. */
|
|
both?: boolean;
|
|
}
|
|
|
|
interface DiagramShape {
|
|
description: string;
|
|
nodes: DiagramNode[];
|
|
arrows: DiagramArrow[];
|
|
}
|
|
|
|
/** Quatro etapas em coluna, de cima para baixo, ligadas por setas. */
|
|
function column(labels: [string, string, string, string]) {
|
|
const nodes = labels.map((label, index) => ({
|
|
label,
|
|
x: 90,
|
|
y: 24 + index * 100,
|
|
width: 300,
|
|
height: 56,
|
|
}));
|
|
const arrows = nodes.slice(1).map((node, index) => ({
|
|
x: WIDTH / 2,
|
|
from: nodes[index].y + nodes[index].height,
|
|
to: node.y,
|
|
}));
|
|
return { nodes, arrows };
|
|
}
|
|
|
|
const diagrams: Record<ServiceDiagramKind, DiagramShape> = {
|
|
iot: {
|
|
description:
|
|
"Esquema do caminho dos dados: o dispositivo envia as leituras ao gateway, o gateway repassa à API e a API alimenta o painel.",
|
|
...column(["Dispositivo ou sensor", "Gateway", "API", "Painel"]),
|
|
},
|
|
arquitetura: {
|
|
description:
|
|
"Esquema das camadas de um sistema: a interface chama a API, a API aciona os serviços e os serviços guardam e consultam o banco de dados.",
|
|
...column(["Interface", "API", "Serviços", "Banco de dados"]),
|
|
},
|
|
desktop: {
|
|
description:
|
|
"Esquema de um aplicativo desktop: o aplicativo no computador troca informações, nos dois sentidos, com os arquivos e com os sistemas locais.",
|
|
nodes: [
|
|
{
|
|
label: "Aplicativo no computador",
|
|
x: 90,
|
|
y: 60,
|
|
width: 300,
|
|
height: 64,
|
|
},
|
|
{ label: "Arquivos", x: 30, y: 280, width: 190, height: 64 },
|
|
{ label: "Sistemas locais", x: 260, y: 280, width: 190, height: 64 },
|
|
],
|
|
arrows: [
|
|
{ x: 125, from: 124, to: 280, both: true },
|
|
{ x: 355, from: 124, to: 280, both: true },
|
|
],
|
|
},
|
|
};
|
|
|
|
function Arrow({ x, from, to, both = false }: DiagramArrow) {
|
|
const lineStart = both ? from + ARROW_HEAD : from;
|
|
return (
|
|
<g>
|
|
<line
|
|
x1={x}
|
|
y1={lineStart}
|
|
x2={x}
|
|
y2={to - ARROW_HEAD}
|
|
stroke="var(--paper)"
|
|
strokeOpacity={0.7}
|
|
strokeWidth={2}
|
|
/>
|
|
<polygon
|
|
points={`${x - 7},${to - ARROW_HEAD} ${x + 7},${to - ARROW_HEAD} ${x},${to}`}
|
|
fill="var(--paper)"
|
|
fillOpacity={0.7}
|
|
/>
|
|
{both ? (
|
|
<polygon
|
|
points={`${x - 7},${from + ARROW_HEAD} ${x + 7},${from + ARROW_HEAD} ${x},${from}`}
|
|
fill="var(--paper)"
|
|
fillOpacity={0.7}
|
|
/>
|
|
) : null}
|
|
</g>
|
|
);
|
|
}
|
|
|
|
export function ServiceDiagram({ kind }: { kind: ServiceDiagramKind }) {
|
|
const { description, nodes, arrows } = diagrams[kind];
|
|
|
|
return (
|
|
<figure className="mx-auto w-full min-w-0 max-w-[520px]">
|
|
<svg
|
|
role="img"
|
|
aria-label={description}
|
|
viewBox={`0 0 ${WIDTH} ${HEIGHT}`}
|
|
className="block h-auto w-full border border-white/15 bg-[#0a0a0a]"
|
|
data-diagram={kind}
|
|
>
|
|
{arrows.map((arrow) => (
|
|
<Arrow key={`${arrow.x}-${arrow.from}`} {...arrow} />
|
|
))}
|
|
{nodes.map(({ label, x, y, width, height }) => (
|
|
<g key={label}>
|
|
<rect
|
|
x={x}
|
|
y={y}
|
|
width={width}
|
|
height={height}
|
|
fill="var(--white)"
|
|
fillOpacity={0.06}
|
|
stroke="var(--paper)"
|
|
strokeOpacity={0.35}
|
|
strokeWidth={1.5}
|
|
/>
|
|
<text
|
|
x={x + width / 2}
|
|
y={y + height / 2}
|
|
textAnchor="middle"
|
|
dominantBaseline="central"
|
|
fill="var(--paper)"
|
|
style={{
|
|
fontFamily: "var(--font-display)",
|
|
fontSize: 20,
|
|
fontWeight: 600,
|
|
}}
|
|
>
|
|
{label}
|
|
</text>
|
|
</g>
|
|
))}
|
|
</svg>
|
|
<figcaption className="mt-sp-3 text-[15px] leading-6 text-white/70">
|
|
Esquema ilustrativo, sem dados de clientes.
|
|
</figcaption>
|
|
</figure>
|
|
);
|
|
}
|