feat(cases): prova certa por serviço, páginas internas com trabalho no topo
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.
This commit is contained in:
1 parent
7b633f0f4a
commit
de6af74a0c
37 files changed
+1062
-254
No files matched your search
@@ -0,0 +1,68 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user