Files
portifolio-evandrodevbr/lib/whatsapp.ts
T
Evandro Fonseca Junior 9e2d65a926 fix(home): oferta leva ao formulário, ofertas logo depois da vitrine
O botão das ofertas rola até o contato com o tipo marcado e não quebra mais as âncoras; ofertas com o que você recebe e primeiro passo, logo depois da vitrine; bloco de IA fora da home; cartão largo da SEV no hero do celular; separador da faixa de clientes só entre itens; favicon de 13 para 1,8 KB; pré-carregamento só no hover e nunca de /#... fora da home; X do menu e cabeçalho opaco; WhatsApp com "o case"; /ia com 3 passos e modelo de relatório ilustrativo; Sobre alinhado ao currículo; CSP Report-Only com os domínios do Ads.
2026-10-14 19:14:47 -03:00

119 lines
4.3 KiB
TypeScript

const COUNTRY = ["5", "5"];
const AREA = ["4", "7"];
const NINE = ["9"];
const FIRST = ["9", "2", "7", "4"];
const LAST = ["2", "3", "6", "5"];
export function getWhatsAppNumber() {
return [...COUNTRY, ...AREA, ...NINE, ...FIRST, ...LAST].join("");
}
export function getWhatsAppDisplayNumber() {
return `+${COUNTRY.join("")} ${AREA.join("")} ${NINE.join("")}${FIRST.join("")}-${LAST.join("")}`;
}
export function buildWhatsAppUrl(message: string) {
return `https://wa.me/${getWhatsAppNumber()}?text=${encodeURIComponent(message)}`;
}
/**
* Nomes dos serviços e dos cases, montados no servidor (app/layout.tsx) a partir de lib/site-content.ts:
* esse arquivo reúne o texto de todas as páginas e não deve ir inteiro para o navegador.
*/
export interface WhatsAppContexts {
/** slug do serviço -> nome do serviço */
services: Record<string, string>;
/** id do case -> nome do projeto ou do cliente */
cases: Record<string, string>;
}
/** Entra na frase "Quero conversar sobre {assunto}." da mensagem. */
export const AI_REVIEW_SUBJECT = "a revisão de um aplicativo feito com IA";
/**
* Assuntos das páginas sem slug. Cabeçalho, botão flutuante e corpo da mesma página usam a mesma frase;
* /cases e /servicos repetem a que os botões do corpo dessas páginas já usam.
*/
export const HOME_SUBJECT = "um site ou sistema para a minha empresa";
export const CASES_SUBJECT = "um projeto parecido com os cases do portfólio";
export const SERVICES_SUBJECT = "Sites, sistemas e aplicativos";
export const RESUME_SUBJECT = "um projeto, depois de ver o seu currículo";
/** Frase padrão para quem quer algo como um projeto publicado (case de cliente ou de laboratório). "O case" concorda com qualquer título: sem ele, saía "o Lavação 047" e "o SEV Moda Íntima". */
export function similarProjectSubject(title: string) {
return `um projeto parecido com o case ${title}`;
}
/** Assunto de cada página sem parâmetro, pelo caminho sem a barra final (só a home mantém a barra). */
const INDEX_SUBJECTS: Readonly<Record<string, string>> = {
"/": HOME_SUBJECT,
"/ia": AI_REVIEW_SUBJECT,
"/cases": CASES_SUBJECT,
"/servicos": SERVICES_SUBJECT,
"/curriculo": RESUME_SUBJECT,
};
/** Páginas com um parâmetro no caminho: em que tabela de nomes ele se procura e como o nome vira assunto. */
const DETAIL_ROUTES = [
{
pattern: /^\/servicos\/([^/]+)$/,
table: "services",
subject: (name: string) => name,
},
{
pattern: /^\/cases\/([^/]+)$/,
table: "cases",
subject: similarProjectSubject,
},
] as const;
/**
* Nome pela chave; chave ausente, nome vazio ou só com espaços valem como ausente, para a mensagem cair no assunto
* padrão em vez de "sobre .".
*/
function lookup(
table: Record<string, string> | undefined,
key: string | undefined,
) {
const name =
table && key !== undefined && Object.hasOwn(table, key)
? table[key].trim()
: "";
return name || undefined;
}
/**
* Assunto da conversa de WhatsApp para a rota atual. Em /servicos/<slug> cita o serviço, em /cases/<slug> o case,
* em /ia a revisão de aplicativo e nas páginas-índice (home, /cases, /servicos, /curriculo) o assunto da própria
* página; nas demais rotas devolve undefined e a mensagem fala de "um projeto".
*/
export function whatsAppSubjectForPath(
pathname: string | null | undefined,
contexts?: WhatsAppContexts,
): string | undefined {
const path = pathname?.replace(/(.)\/$/, "$1") ?? "";
if (Object.hasOwn(INDEX_SUBJECTS, path)) return INDEX_SUBJECTS[path];
for (const { pattern, table, subject } of DETAIL_ROUTES) {
const name = lookup(contexts?.[table], pattern.exec(path)?.[1]);
if (name) return subject(name);
}
return undefined;
}
/** Páginas de serviço e de case. */
export function isServiceOrCasePath(pathname: string | null | undefined) {
return /^\/(servicos|cases)\/[^/]+\/?$/.test(pathname ?? "");
}
/**
* Páginas em que o botão do cabeçalho abre o WhatsApp com o assunto da página, em vez de ir ao formulário da home:
* serviço e case, e também /ia, /cases, /servicos e /curriculo. Na home e nas demais rotas ele leva ao formulário.
*/
export function headerOpensWhatsApp(pathname: string | null | undefined) {
return (
isServiceOrCasePath(pathname) ||
/^\/(ia|cases|servicos|curriculo)\/?$/.test(pathname ?? "")
);
}