Diagnóstico sem case que não cabe; desktop com o GCC Installer como projeto próprio; IoT com o Twins Lab e a nota da Apolo. Mosaico de pôsteres em /servicos e /cases, faixa baixa em uma coluna no currículo, privacidade e laboratório; Laboratório como lista compacta; SEV começando por catálogo, pedido no WhatsApp e painel; Joinville e SC nos títulos; imagens do LCP com fetchPriority e sizes reais; pré-carregamento só no hover; 404 com Open Graph próprio.
69 lines
2.1 KiB
TypeScript
69 lines
2.1 KiB
TypeScript
import Image from "next/image";
|
|
import { coverSizes, priorityImageProps } from "./CaseCover";
|
|
import { getCaseMedia } from "./case-media";
|
|
|
|
export interface MosaicPoster {
|
|
id: string;
|
|
poster: string;
|
|
}
|
|
|
|
/** Quantos pôsteres entram no mosaico: um grande em cima e dois menores embaixo. */
|
|
export const MOSAIC_SIZE = 3;
|
|
|
|
/**
|
|
* Pôsteres gravados dos cases, na ordem dada, só dos que têm captura no disco (no máximo `MOSAIC_SIZE`).
|
|
* Roda no servidor, como `getCaseMedia`.
|
|
*/
|
|
export function getMosaicPosters(ids: readonly string[]): MosaicPoster[] {
|
|
return ids
|
|
.flatMap((id) => {
|
|
const media = getCaseMedia(id, "desktop");
|
|
return media ? [{ id, poster: media.poster }] : [];
|
|
})
|
|
.slice(0, MOSAIC_SIZE);
|
|
}
|
|
|
|
const frame =
|
|
"relative aspect-[16/10] overflow-hidden border border-white/15 bg-[#0a0a0a]";
|
|
|
|
/**
|
|
* Mosaico de pôsteres dos sites no topo das páginas internas, na coluna que ficaria vazia ao lado do título.
|
|
* É decorativo (texto alternativo vazio): quem lê os cases tem os links logo abaixo. O primeiro pôster é o maior
|
|
* e o que abre a página, então carrega na hora e com prioridade alta; no celular só ele aparece, e os outros dois
|
|
* (que não são baixados enquanto estão ocultos) entram a partir de 1024 px.
|
|
*/
|
|
export function PosterMosaic({
|
|
posters,
|
|
}: {
|
|
posters: readonly MosaicPoster[];
|
|
}) {
|
|
const [main, ...others] = posters;
|
|
if (!main) return null;
|
|
|
|
return (
|
|
<div className="grid min-w-0 grid-cols-2 gap-sp-3" aria-hidden="true">
|
|
<div className={`${frame} col-span-2`}>
|
|
<Image
|
|
src={main.poster}
|
|
alt=""
|
|
fill
|
|
sizes={coverSizes.hero}
|
|
{...priorityImageProps}
|
|
className="object-cover object-top"
|
|
/>
|
|
</div>
|
|
{others.map(({ id, poster }) => (
|
|
<div key={id} className={`${frame} hidden lg:block`}>
|
|
<Image
|
|
src={poster}
|
|
alt=""
|
|
fill
|
|
sizes="(min-width: 1440px) 274px, 20vw"
|
|
className="object-cover object-top"
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|