feat(home): posicionamento com prova, menu sem recarregar e WhatsApp em todo o site

H1 'Sites e sistemas que trazem pedidos pelo WhatsApp e tiram sua operação da planilha.'; HeroWork maior com a SEV; menu Cases, Serviços, Como trabalho, Sobre e IA com next/link e pré-carregamento no hover; menu do celular em tela cheia; WhatsApp flutuante e no rodapé com a mensagem da rota; rodapé em todas as páginas; FAQ da home; /ia como oferta de revisão de app feito com IA; formulário com noscript, validação do mailto e plano B do pop-up; cartões sem foco indevido; sem a fonte Monument.
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 14:15:25 -03:00
1 parent e80f73d075
commit 10f170abf4
56 files changed
+3099 -906

No files matched your search

+100
View File
@@ -0,0 +1,100 @@
import type { ReactElement } from "react";
import { portfolio } from "@/lib/portfolio-data";
import { loadFeaturedProjects } from "@/lib/portfolio-data.server";
import { clientNames, testimonials } from "@/lib/proof";
import { serviceHubLinks } from "@/lib/site-content";
import CinematicHome from "./components/cinematic/CinematicHome";
import Home from "./page";
/**
* Testes de caminhos-base de Home e de yearsOfExperience (que não é exportada, então passa por Home):
* os anos vêm do texto do portfólio, e a home mostra só projetos de cliente.
*/
jest.mock("./components/cinematic/CinematicHome", () => ({
__esModule: true,
default: jest.fn(() => null),
}));
jest.mock("@/lib/portfolio-data.server", () => ({
loadFeaturedProjects: jest.fn(),
}));
type HomeProps = React.ComponentProps<typeof CinematicHome>;
type Loaded = Awaited<ReturnType<typeof loadFeaturedProjects>>;
const originalSummary = portfolio.personal.summary;
function mockProjects(projects: Array<{ id: string; kind?: string }>) {
jest
.mocked(loadFeaturedProjects)
.mockResolvedValue(projects as unknown as Loaded);
}
async function renderHome() {
const element = (await Home()) as ReactElement<HomeProps>;
expect(element.type).toBe(CinematicHome);
return element.props;
}
beforeEach(() => {
mockProjects([]);
});
afterEach(() => {
portfolio.personal.summary = originalSummary;
});
describe("Home: anos de experiência", () => {
it("path: the summary says '5 anos de experiência' -> 5", async () => {
portfolio.personal.summary = "Desenvolvedor com 5 anos de experiência.";
expect((await renderHome()).heroStats).toEqual({ years: 5 });
});
it("path: more than one digit and different case -> the whole number", async () => {
portfolio.personal.summary = "Atuo há 12 ANOS DE EXPERIÊNCIA em web.";
expect((await renderHome()).heroStats).toEqual({ years: 12 });
});
it("path: the summary has no such stretch -> no number, the hero hides it", async () => {
portfolio.personal.summary = "Desenvolvedor de software em Garuva-SC.";
expect((await renderHome()).heroStats).toEqual({ years: undefined });
});
it("path: a number that is not followed by the experience wording -> ignored", async () => {
portfolio.personal.summary = "Formado há 5 anos, com 3 projetos entregues.";
expect((await renderHome()).heroStats?.years).toBeUndefined();
});
});
describe("Home: projetos e provas", () => {
it("path: client, laboratory and unlabelled projects -> only the client ones reach the page", async () => {
mockProjects([
{ id: "a", kind: "cliente" },
{ id: "b", kind: "laboratorio" },
{ id: "c" },
{ id: "d", kind: "cliente" },
]);
const props = await renderHome();
expect(props.projects.map(({ id }) => id)).toEqual(["a", "d"]);
});
it("path: no featured project at all -> an empty list, the page still renders", async () => {
mockProjects([]);
expect((await renderHome()).projects).toEqual([]);
});
it("path: the proof and the service links come straight from their modules", async () => {
const props = await renderHome();
expect(props.clients).toBe(clientNames);
expect(props.testimonials).toBe(testimonials);
expect(props.serviceLinks).toBe(serviceHubLinks);
});
});