122 lines
3.9 KiB
TypeScript
122 lines
3.9 KiB
TypeScript
// ============================================================================
|
|
// SEO - Meta tags dinamicas por pagina
|
|
// ============================================================================
|
|
// Uso: <SEO title="..." description="..." />
|
|
//
|
|
// Cada pagina deve usar este componente com suas proprias tags.
|
|
// O canonical e gerado automaticamente a partir da URL atual.
|
|
// ============================================================================
|
|
|
|
import { Helmet } from 'react-helmet-async';
|
|
import { useLocation } from 'react-router-dom';
|
|
import { ENV_CONFIG } from '../config/environment';
|
|
|
|
interface SEOProps {
|
|
title?: string;
|
|
description?: string;
|
|
keywords?: string;
|
|
ogImage?: string;
|
|
ogType?: string;
|
|
noindex?: boolean; // Se true, forca noindex mesmo em producao
|
|
schema?: Record<string, any>;
|
|
}
|
|
|
|
const SITE_URL = 'https://avanzato.com.br';
|
|
const DEFAULT_TITLE = 'Avanzato Tecnologia | TI Gerenciada, Cloud e Seguranca';
|
|
const DEFAULT_DESC = 'Solucoes em TI Gerenciada, Cloud Computing e Seguranca da Informacao. +23 anos de experiencia, +500 clientes atendidos. Suporte 24/7 em Guarulhos e regiao.';
|
|
const DEFAULT_OG_IMAGE = 'https://avanzato.com.br/og-image.jpg';
|
|
|
|
export default function SEO({
|
|
title,
|
|
description,
|
|
keywords,
|
|
ogImage,
|
|
ogType = 'website',
|
|
noindex = false,
|
|
schema,
|
|
}: SEOProps) {
|
|
const location = useLocation();
|
|
const pathname = location.pathname;
|
|
const canonicalUrl = `${SITE_URL}${pathname}`;
|
|
const fullTitle = title ? `${title} | Avanzato` : DEFAULT_TITLE;
|
|
|
|
return (
|
|
<Helmet>
|
|
{/* Title */}
|
|
<title>{fullTitle}</title>
|
|
<meta name="title" content={fullTitle} />
|
|
|
|
{/* Description */}
|
|
{description && <meta name="description" content={description} />}
|
|
{!description && <meta name="description" content={DEFAULT_DESC} />}
|
|
|
|
{/* Keywords */}
|
|
{keywords && <meta name="keywords" content={keywords} />}
|
|
|
|
{/* Canonical - DINAMICO por pagina */}
|
|
<link rel="canonical" href={canonicalUrl} />
|
|
|
|
{/* Robots - automatico por ambiente, override com prop noindex */}
|
|
{noindex || !ENV_CONFIG.allowIndexing ? (
|
|
<meta name="robots" content="noindex, nofollow" />
|
|
) : (
|
|
<meta name="robots" content="index, follow" />
|
|
)}
|
|
|
|
{/* Open Graph */}
|
|
<meta property="og:type" content={ogType} />
|
|
<meta property="og:url" content={canonicalUrl} />
|
|
<meta property="og:title" content={fullTitle} />
|
|
<meta property="og:description" content={description || DEFAULT_DESC} />
|
|
<meta property="og:image" content={ogImage || DEFAULT_OG_IMAGE} />
|
|
<meta property="og:locale" content="pt_BR" />
|
|
<meta property="og:site_name" content="Avanzato Tecnologia" />
|
|
|
|
{/* Twitter */}
|
|
<meta property="twitter:card" content="summary_large_image" />
|
|
<meta property="twitter:url" content={canonicalUrl} />
|
|
<meta property="twitter:title" content={fullTitle} />
|
|
<meta property="twitter:description" content={description || DEFAULT_DESC} />
|
|
<meta property="twitter:image" content={ogImage || DEFAULT_OG_IMAGE} />
|
|
|
|
{/* Schema.org */}
|
|
{schema && (
|
|
<script type="application/ld+json">
|
|
{JSON.stringify(schema)}
|
|
</script>
|
|
)}
|
|
</Helmet>
|
|
);
|
|
}
|
|
|
|
// Schema helpers
|
|
export const createServiceSchema = (name: string, description: string, urlPath: string) => ({
|
|
'@context': 'https://schema.org',
|
|
'@type': 'Service',
|
|
name,
|
|
description,
|
|
provider: {
|
|
'@type': 'Organization',
|
|
name: 'Avanzato Tecnologia',
|
|
url: SITE_URL,
|
|
},
|
|
url: `${SITE_URL}${urlPath}`,
|
|
areaServed: {
|
|
'@type': 'City',
|
|
name: 'Guarulhos',
|
|
},
|
|
});
|
|
|
|
export const createFAQSchema = (questions: Array<{q: string; a: string}>) => ({
|
|
'@context': 'https://schema.org',
|
|
'@type': 'FAQPage',
|
|
mainEntity: questions.map(({q, a}) => ({
|
|
'@type': 'Question',
|
|
name: q,
|
|
acceptedAnswer: {
|
|
'@type': 'Answer',
|
|
text: a,
|
|
},
|
|
})),
|
|
});
|