feat(config, layout, components): enhance security headers, improve SEO, and update project descriptions

- Added security headers in the Next.js configuration to enhance application security.
- Updated metadata in the layout component for improved SEO, including new titles, descriptions, and keywords.
- Introduced JSON-LD schema for projects to enhance search engine visibility.
- Enhanced project descriptions in the portfolio data for clarity and detail.
- Improved header component with logo integration and refined social media links for better accessibility.
This commit is contained in:
Evandro Fonseca Junior committed 2026-01-20 21:00:50 -03:00
1 parent 3f3fca11ab
commit ad6a76efdd
14 files changed
+467 -57

No files matched your search

+10
View File
@@ -138,6 +138,16 @@ export default function About() {
</div>
))}
</div>
{/* SEO Semantic Content - Visible but subtle */}
<div className="mt-16 pt-8 border-t border-slate-800">
<p className="text-xs text-slate-600 text-center max-w-2xl mx-auto leading-relaxed">
Evandro Fonseca Junior é Engenheiro Full-Stack especializado em Next.js,
React, Angular e Node.js. Desenvolvedor de aplicações desktop com Tauri,
sistemas web escaláveis e automação de processos. Experiência em PostgreSQL,
MySQL, computação em nuvem e IoT. Localizado em Garuva, Santa Catarina, Brasil.
</p>
</div>
</div>
</section>
);
+11 -3
View File
@@ -2,7 +2,8 @@
import React, { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { MenuIcon, XIcon, Layers } from "lucide-react";
import { MenuIcon, XIcon } from "lucide-react";
import Image from "next/image";
import { cn } from "@/lib/utils";
// Definição de Tipos para consistência de dados
@@ -59,8 +60,15 @@ export function Header() {
whileHover={{ scale: 1.05 }}
className="flex items-center gap-2 cursor-pointer"
>
<div className="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center shadow-[0_0_15px_rgba(37,99,235,0.4)]">
<Layers className="w-5 h-5 text-white" />
<div className="w-8 h-8 flex items-center justify-center">
<Image
src="/edev_logo.svg"
alt="Evandro.dev Logo"
width={32}
height={32}
className="w-8 h-8"
priority
/>
</div>
<span className="text-xl font-black tracking-tighter text-white">
EVANDRO<span className="text-blue-500">.DEV</span>
+13 -6
View File
@@ -21,7 +21,7 @@ export default function Hero() {
};
return (
<section id="home" className="relative min-h-screen flex items-center justify-center overflow-hidden bg-[#030712]">
<section id="home" className="relative min-h-screen flex items-center justify-center overflow-hidden bg-[#030712] pt-28 md:pt-20">
{/* 1. Background Engineering: Grid Pattern & Radial Shading */}
<div className="absolute inset-0 z-0">
<div className="absolute inset-0 bg-[linear-gradient(to_right,#1f2937_1px,transparent_1px),linear-gradient(to_bottom,#1f2937_1px,transparent_1px)] bg-[size:4rem_4rem] [mask-image:radial-gradient(ellipse_60%_50%_at_50%_0%,#000_70%,transparent_100%)] opacity-20" />
@@ -79,11 +79,17 @@ export default function Hero() {
<div className="flex gap-4 px-4">
{[
{ icon: Github, link: "https://github.com/evandrodevbr" },
{ icon: Linkedin, link: "https://linkedin.com/..." },
{ icon: Mail, link: "mailto:..." }
{ icon: Github, link: "https://github.com/evandrodevbr", rel: "me" },
{ icon: Linkedin, link: "https://www.linkedin.com/in/evandro-fonseca-junior-0969aa1b0/", rel: "me" },
{ icon: Mail, link: "mailto:[email protected]" }
].map((social, i) => (
<a key={i} href={social.link} className="p-3 rounded-lg border border-slate-800 text-slate-400 hover:text-white hover:border-slate-600 transition-all">
<a
key={i}
href={social.link}
rel={social.rel ? `${social.rel} noopener noreferrer` : "noopener noreferrer"}
target={social.link.startsWith("mailto:") ? undefined : "_blank"}
className="p-3 rounded-lg border border-slate-800 text-slate-400 hover:text-white hover:border-slate-600 transition-all"
>
<social.icon className="w-5 h-5" />
</a>
))}
@@ -118,13 +124,14 @@ export default function Hero() {
fill
className="object-cover grayscale hover:grayscale-0 transition-all duration-700 scale-105 hover:scale-100"
priority
fetchPriority="high"
/>
</div>
{/* Floating Stats Badge */}
<div className="absolute -bottom-6 -right-6 bg-slate-900 border border-slate-800 p-4 rounded-2xl shadow-2xl z-20 backdrop-blur-xl">
<p className="text-xs text-slate-500 uppercase font-bold tracking-tighter">Exp. Level</p>
<p className="text-xl font-mono text-blue-400">Senior Dev</p>
<p className="text-xl font-mono text-blue-400">Mid-Level Engineer</p>
</div>
</motion.div>
</div>
+57
View File
@@ -24,6 +24,23 @@ const CATEGORY_THEMES: Record<string, any> = {
desktop: { color: "text-emerald-400", bg: "bg-emerald-500/10", border: "border-emerald-500/20", icon: Terminal },
};
// Funções auxiliares para Schema
function getApplicationCategory(category: string): string {
const map: Record<string, string> = {
"full-stack": "WebApplication",
"iot": "DesktopApplication",
"api": "WebApplication",
"web": "WebApplication",
"desktop": "DesktopApplication"
};
return map[category.toLowerCase()] || "WebApplication";
}
function getOperatingSystem(category: string): string {
if (category.toLowerCase() === "desktop") return "Windows, Linux";
return "Web Browser";
}
export default function Projects() {
const { data, loading, error } = usePortfolioData();
@@ -31,6 +48,38 @@ export default function Projects() {
data?.projects.filter((p) => p.featured) || [],
[data]);
const projectsJsonLd = useMemo(() => {
if (!projects || projects.length === 0) return null;
return {
"@context": "https://schema.org",
"@type": "ItemList",
"itemListElement": projects.map((project, index) => ({
"@type": "ListItem",
"position": index + 1,
"item": {
"@type": "SoftwareApplication",
"name": project.title,
"description": project.description,
"applicationCategory": getApplicationCategory(project.category),
"operatingSystem": getOperatingSystem(project.category),
"url": project.demoUrl || project.githubUrl || `https://evandro.dev.br/#projects`,
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "BRL"
},
"softwareVersion": "1.0.0",
"author": {
"@type": "Person",
"name": "Evandro Fonseca Junior",
"url": "https://evandro.dev.br"
}
}
}))
};
}, [projects]);
if (loading) return <div className="py-20 text-center animate-pulse text-slate-500 font-mono">Loading_Assets...</div>;
if (error) return <div className="py-20 text-center text-red-500">System_Error: Failed to sync projects.</div>;
@@ -118,6 +167,14 @@ export default function Projects() {
);
})}
</BentoGrid>
{/* JSON-LD Schema for Projects */}
{projectsJsonLd && (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(projectsJsonLd) }}
/>
)}
</div>
</section>
);
+8
View File
@@ -0,0 +1,8 @@
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024">
<defs>
<image width="978" height="981" id="img1" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA9IAAAPVCAYAAACKltJmAAAAAXNSR0IB2cksfwAEfEJJREFUeJzsvXm0Jkd1Jxj5vVf1ql5tUpV2CUlVkkAyCNk0NqoqYQw2Mw2a6WamZzxeMCCVBO0+42PPZp85Ph77jGfc03+224dxj+0zAwZj9+nFxjYYA8YsAiQWIwHaN1Sral9e1au35mRkZmRGRN5YMyK/zO+7Pykrv5cZcSMyMpb7i3sjMiEIBALRExw48MA/zU7vT5Lkv9q8eTOhx9zcHJmZmSGzs7Mku07SNM3Djkaj/O/iWpJfY3/TYz1dz440D7++vl7FY2B/s3OSitdllElU4YSwSZ54I35SXuPBXxPCAumzsGl+pHVSJBHuF7fS8lzklS+L8kmJ/GQJ/Khg/qpyouU+KssYKFcxX3WeoGcR4lRPpS5X+V1VYcvnJWX9YMeIlQFJRJl5BPWzcleztAyFJMgQ62Hzfv0mmFRVfSCa+6r3lkpJlrkpi7HOU94uoHcgXRLaU/au2fumdWA0Sqp3IzwbyyNQB6D3Wgso/0nFvPI5rWSTug3w9SKX2Sx2I7TlatG2Vc9enQG58rsqBTTaC8nLgjTLS4EkkfJSlVaiLNc6/TIPpH4HNn2Hqb1CKNrmKI8itG1DexCyK5U7X1Z8PyA8t6HPFZ6Ry0sCPFf9jkijjlR1lt1OxarJ1xFZJj/OVeeqOTXroPx8pvpoKmvbcQsKV1xX10EoP7q0qjSBoFTeCHivVRzCjZtcuZrqJjgGK8onSZvX+LzyZcAuC/WmHItUY0adJsn7XL5PHo1myEwyI+hCqryy/nt1dZVcvnyZLC0tZedFcnnpMlnMzouLxbGysvJcFvwj9PhP/+HPDykzhKjgMeQgEAhEHCCRRiItpItEGok0Emkk0gogkUYijUQaifS4gUQagUBExa/92q9ty07X0CPD6LrrriPXX389/YNce+21ZNu2bTlRpscnP/kXbz148OC7Ll68+OOUQM/NbSQbNmyo7hddljgwFoMjIYSISkVOoEvCAA2caaW0Mm2KNO/XYqWbUjimwBFRZJOvSRc06WuSrIhRMwAXKjCRFuSO1MpyUoap8iQpXVBeeMInpMuIUVrG0JUXH7Z87yPSVE64qgI8MCQ4Nb6bxpNoFCJB0eb0qSqJZsGA9+UUVFnk64pAosBnJVIVEol0RUxKhU4gd/w7atRxUtUD+dl0aYuX0ur1NJ5dniSxRiqUKxhTkMlNgFT/ply1SevHTzjiAKYM56dRBoqJHzCb+Q++fRS5qYiMJEvIAzeRUaXNCW6GpbcYYWpkoup3VXlNEp5aNJV/E4mW43CR8wQEctrod/kOXJ9Go/9o1GOxnJjMVKor0CQVO0NEmqXF95RQVtmzsTM/rlXtRZ6IabwvxTUpjTqccoBj01uC2Lp/b05MQHAh0onQOfKpSpeTpGqtaTkuaV+93I9J1xL+mpRnmEgT+PmTOm+qybKqPym7XNZ2KIEe0cmoZMTVp2ZXy/JBD0qkM7JMlpeXi2NliSxlZ0qsl7MjE3vwql1X/8299+799J7dtx2/cOECWVhYIOfOnSXnz58jp06dIocPHyaHDh2ixPt8Jvo4Pf7oj/7IepScNCCRRiAQUZER6d3ZaS89MvI8pyPSR48eueX48eOvzTrvW2dmZsns7Ew+61rMxI5qfpbWA0oNcZBK2UBZDZiikgpd00FQDZiuypPESuEuBmpOpxR+cMGs8lAQ4bQKlkj36myJJFq+ryNaPIwKLK+MCwRXvA0lWj2LUrSkQPOFRYhYrnx+K0WEFQMrrEQxyEGC6P/ixcS2clR50SuIXFJEramJOVDeNwUtLzYuc4pnU/8UyQx/Fi2/7HqDPXBEUsquioAAeQAnP4S/gEmXSi4ossmxhWorymxAJhygnFS43yQf+jyC4UiT0OqRAr+Txr8qgay/FNK1Sqv4OyVineC9aGQoW6bBMgqFkwQT1hHwIXjCBbY8eULQkLa+jhZ/i0Qalm8zaQDQQiKMd+KfEpHi6rYw7jTbqdB2QUCstdlHQdUeet8uVu48TIO0Kx+aQLPECanHnzq4rjGq+l+uNQETEGn5t1CXpRw3nlNf8PV8TcLJSWpPk1qmOEnK60z0YJbptbW18lglq+VvSrJnZ2bPXblz53M3XH/js5m8iwYiTa3XX82Or2VEel2Z+QkHEmkEAhEVGZH+0ez03uz4hYw8b9ER6QyjbPAZZR39yIaQBIVmRr4RFHCd07kkqp7FpEB5uf0GKDdjWnCsgp8qLDidv08teDWXQSaG0wfTu2r7LmPVBZ98m+q4T/ohZLUDVK8hJNKZk+BEpJttSJ6MglPRSO1lf2EGRKTZGSLSFC7PONRycQE0QSHcNwvg5ufs2kFz8tQRCiItTxgz0h3//aXSWc6Fvo/Mzun6evZ/hkuXFlMDkf5KFuWP6ZER6bUIDzMITG6LRCAQneCXf/mXr85Ou+lxyy23jPbs2UNuvvlmctVVV+XHwYMHb3vppZd+/OzZsz+ekea5K664guzYsaM6Nm3alLsolUR6PLA11xI9+dVZF0B3NRORLiIaZbpYU0zgrVIqecpcQ5Yag3LUPVSKBhJpCqMy20Kht7U2+spUyR0HkW4jzx+2RJoCru/jJtIsD/3qM8yASl2wyLbso6edSNuQaIbEtGapDjl4Ig33PfqJYlP/Xlis18nS0nK+npqtn15cvJSR6gvk9OnT+ZHpbE9fd911X3rLW97ypSNHjqy/8sor5MSJE4uZmJey48X3vve9F8I8Zb8xuS0SgUB0goxIvz47/VR2vDMj0rMykc465h3nzp27cWFh4YYNGzbMFGuf58jGjRvzg20iJiu5rciga3zmCiUNitWfBgWch8sGLtWaU8DxMncNM6Rhs45QlXYjL2LGtHEd5h16iOES6T5ZhF1dYeUwofJi0wZsJ7Fi5KVb4tOeSLdLKwyRNqbcY6INWaYLEuU3iSS3E9c6FqqsXNprDNIfi0i71E7wuVSu3UQk00VKXfQv8PiWMn9zME4znWI99Vp5rBYu4Pn66uVqs7KMSJ/ZunXr4e3btx85dOhQWhLpU5mIz2bH5zIiPRWblfWzJ0IgEL3Eb/zGb2zOTpu3bdu28Q1veAOhx6c//ekfO378+E8vLy//9E033bSBkujsTHbu3Jkf1OLsgrZWK6/4uU7YHKSqnbotyLHrTqi8ezij0Y0hUZF9VyINpS8DItIu1oHhDCZIpLtw/4fCRfGg6BGRjinXMnVi69IqnkOlFZ9Ix/BqCAmdtw6FD4mW47Vpe75wkVX3M/m/rdKtZBoDxCXSyufXEOnid1kWrWm0S9+iaJupLo4qTVLu/cHeZ50XttaaEuwjR47kLt+nTp06tnHjxn939dVX/7sbb7zxhaeffprQIwt3OYu2+Cu/8itL1okPBP3rhRAIRG+REek3ZKc3ZET6FkakFxcXbz127Nibzp87/4+uuPKKmZ1X7iTUfXt+yzzZsmVLvuu2K0IQBneLNPejNCOkijyAm820ItJNi7SKRPOyQhLpXF6dOW2cYRNpCvtyKzD+pwtJPkPBpf6ZENqt2ka268ZWtnkcH9lTEWkoD63VeiCtgphAV0Ohj+1ABlgjHccHPowPSY49WaWW59InWLYnq0BjItJQWE1qbWH3XnXP75YLPjk5KbZxGT3OnTtHzp49m+/knel939q1a9e3FhYWTnJE+uksynczIv2iUwYGgP71QAgEorfIiDT9zvN7MiL9Fkaks9/zq6urO9ZW13ZkpDmhxDn/ZNVssYGY7Lbtgs7XhaWSO1YAFzymCLm4h5tgS6TlPFmT6chE2sU9Pi7SSlEokrSbxXdRLFV12FbZluVA9SkUfKypqnfpswFYWw8UFUKSiH67f6vILX9W58ltwkFNpNldE3lsyjTDFF/Z3sbozQGVRcz23MX6f7V8lcdPI4Z0NqSvyn/jvbCvC9hMpo+fSMepl/K4Fmc85esw+6xWRphXZ2dnz2bHuePHjy9zRPrTWdA/z4j0l6NkZoxAIo1AILQ4fPjg9ux0FT2+/vVH333u3Pn7M6L8ZroW+rbbbss3DJvNP1U124hbuTUNhUi3SNfWIh3Ciucii7douFrUYq2P7g+Rlt0Qm/lK02bZwYpq/q8cGybi6sw0wsppxiLSundiY61VXXedYHB9LiTSgnTSFyKtkjsOIj2W5UIKGbGJtKo9dEukXSzC/FmTvgeRpmL1DtXdEWml/T7SBM84dSd20B2+X3755fyYmZn53Pbt2//69ttv/2IW7CQ9Mh1ysdPMRQISaQQCoUVGpF+bnd6SHfeeP3/hdYuLi3cmyehGfg30KBnlO2+DGHMvY3Lz1t3XDVJW1l1LRdIWshLmQqjbKJBWJJCl4yDXN186uX7yVFYUWNFrkiuTfNkaXRLsRL4Lq322yrFuTaUtGfYvwzrdkJNFujxB6UATRy7PJIeN4UocbyLJj0gbpXoSaXeZdnHkd+zi5uzTl+vqgG97URHpkKRfh7aTF2p59kS6EGue5NVJA6YupTwkfGJSWmV95RNQ8nX7fsCFSCtltOyHQ8K1/2TnS5cu8Tt8P79hw4an5+bmvp/dfjQ7vp4R6aPxct0d+vGWEAhEb5ER6bdmp5+nx6ZNmzfMzW2a3bhx4wwlzvTQzvj2gETn2XAkyvJ9PkxXio4MH6tLDEtMoaKU6StesCmlGES6vUx7YuBGLPhzGSpNG94aRUp2ZFFIwWDhciEZba0YbSeM+PRN+VeRaD6cauLJlL4qrdjutzzC1mH+7CE1ApHm5foSaZ867vNemYVNVa9cnsHmWXy8iGS5NvAl0/rndyPS9LB5VpVEFZFOq23vuLw2ypWSaCBN4JLLe2lLpGNNNPvAyRIvhaVrp4sdv9foTt+ri4uLq0tLS8ey2x+nR0akn4Line truncated
</defs>
<style>
</style>
<use id="Background" href="#img1" x="13" y="21"/>
</svg>

After

Width:  |  Height:  |  Size: 383 KiB

+133 -28
View File
@@ -1,4 +1,4 @@
import type { Metadata } from "next";
import type { Metadata, Viewport } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import Script from "next/script";
// Importação necessária para corrigir o erro de namespace e tipagem do children
@@ -8,27 +8,36 @@ import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
display: "swap",
adjustFontFallback: true,
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
display: "swap",
adjustFontFallback: true,
});
export const metadata: Metadata = {
title: "Evandro Fonseca Junior - Desenvolvedor Full-Stack",
title: "Evandro Fonseca Junior - Engenheiro Full-Stack | Next.js, React, Tauri Specialist",
description:
"Desenvolvedor Full-Stack especializado em soluções web modernas com Angular, React, Node.js e computação em nuvem. Experiência em energia renovável e IoT.",
"Engenheiro Full-Stack especializado em Next.js, React, Angular, Node.js e Tauri. Desenvolvedor de sistemas web escaláveis, aplicações desktop e automação. Experiência em PostgreSQL, MySQL, computação em nuvem e IoT.",
keywords: [
"desenvolvedor",
"full-stack",
"angular",
"react",
"node.js",
"typescript",
"engenheiro full-stack",
"Next.js specialist",
"React developer",
"Tauri developer",
"Angular",
"Node.js",
"TypeScript",
"PostgreSQL",
"MySQL",
"computação em nuvem",
"iot",
"energia renovável",
"IoT",
"automação",
"arquitetura de software",
"sistemas escaláveis",
],
authors: [{ name: "Evandro Fonseca Junior" }],
creator: "Evandro Fonseca Junior",
@@ -38,25 +47,34 @@ export const metadata: Metadata = {
address: false,
telephone: false,
},
metadataBase: new URL("https://evandrofonsecajunior.dev"),
metadataBase: new URL("https://evandro.dev.br"),
alternates: {
canonical: "/",
},
openGraph: {
title: "Evandro Fonseca Junior - Desenvolvedor Full-Stack",
title: "Evandro Fonseca Junior - Engenheiro Full-Stack | Next.js, React, Tauri Specialist",
description:
"Desenvolvedor Full-Stack especializado em soluções web modernas com Angular, React, Node.js e computação em nuvem.",
url: "https://evandrofonsecajunior.dev",
"Engenheiro Full-Stack especializado em Next.js, React, Angular, Node.js e Tauri. Desenvolvedor de sistemas web escaláveis e aplicações desktop.",
url: "https://evandro.dev.br",
siteName: "Evandro Fonseca Junior",
locale: "pt_BR",
type: "website",
images: [
{
url: "/opengraph-image",
width: 1200,
height: 630,
alt: "Evandro Fonseca Junior - Engenheiro Full-Stack | Next.js, React, Tauri Specialist",
},
],
},
twitter: {
card: "summary_large_image",
title: "Evandro Fonseca Junior - Desenvolvedor Full-Stack",
title: "Evandro Fonseca Junior - Engenheiro Full-Stack | Next.js, React, Tauri Specialist",
description:
"Desenvolvedor Full-Stack especializado em soluções web modernas com Angular, React, Node.js e computação em nuvem.",
"Engenheiro Full-Stack especializado em Next.js, React, Angular, Node.js e Tauri. Desenvolvedor de sistemas web escaláveis e aplicações desktop.",
creator: "@evandrodevbr",
images: ["/opengraph-image"],
},
robots: {
index: true,
@@ -71,12 +89,56 @@ export const metadata: Metadata = {
},
};
export const viewport: Viewport = {
themeColor: "#000000",
width: "device-width",
initialScale: 1,
maximumScale: 5,
};
// Interface tipada corretamente
interface RootLayoutProps {
children: ReactNode;
}
export default function RootLayout({ children }: Readonly<RootLayoutProps>) {
const jsonLd = {
"@context": "https://schema.org",
"@type": "ProfilePage",
"mainEntity": {
"@type": "Person",
"name": "Evandro Fonseca Junior",
"alternateName": "Evandro.dev",
"jobTitle": "Engenheiro de Software Full-Stack",
"url": "https://evandro.dev.br",
"sameAs": [
"https://github.com/evandrodevbr",
"https://www.linkedin.com/in/evandro-fonseca-junior-0969aa1b0/",
],
"knowsAbout": [
"Software Engineering",
"Next.js",
"React",
"Angular",
"Node.js",
"TypeScript",
"Tauri",
"System Architecture",
"Cloud Computing",
"IoT",
"Automation",
"PostgreSQL",
"MySQL",
],
"worksFor": {
"@type": "Organization",
"name": "Freelancer / Consultor Independente",
},
"description":
"Engenheiro de software especializado em arquitetura web moderna, automação e sistemas de alta performance. Experiência em Next.js, React, Angular, Node.js e desenvolvimento de aplicações desktop com Tauri.",
},
};
return (
<html lang="pt-BR">
<head>
@@ -98,16 +160,10 @@ export default function RootLayout({ children }: Readonly<RootLayoutProps>) {
<link rel="dns-prefetch" href="//www.linkedin.com" />
<link rel="dns-prefetch" href="//github.com" />
{/* Favicon and icons */}
<link rel="icon" href="/favicon.ico" sizes="any" />
<link rel="apple-touch-icon" href="/favicon.ico" />
<link rel="manifest" href="/manifest.json" />
{/* SEO and Crawling */}
<link rel="canonical" href="https://evandrofonsecajunior.dev" />
<link rel="canonical" href="https://evandro.dev.br" />
{/* Theme color for mobile browsers */}
<meta name="theme-color" content="#000000" />
<meta name="msapplication-TileColor" content="#000000" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta
@@ -118,14 +174,14 @@ export default function RootLayout({ children }: Readonly<RootLayoutProps>) {
{/* Performance hints */}
<link rel="preload" href="/edev.jpeg" as="image" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=5"
/>
</head>
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
{children}
<Script
src="/analytics/api/script.js"
@@ -135,6 +191,55 @@ export default function RootLayout({ children }: Readonly<RootLayoutProps>) {
data-replay-block-class="rr-block"
data-replay-ignore-class="rr-ignore"
strategy="afterInteractive"
data-session-replay="true"
data-track-errors="true"
/>
{/* Google Analytics - Interceptor para bypass de adblocker */}
<Script
id="google-analytics-proxy-interceptor"
strategy="beforeInteractive"
dangerouslySetInnerHTML={{
__html: `
(function() {
// Interceptar fetch
const originalFetch = window.fetch;
window.fetch = function(url, options) {
if (typeof url === 'string' && url.includes('google-analytics.com')) {
url = url.replace('https://www.google-analytics.com', '/ga');
}
return originalFetch.call(this, url, options);
};
// Interceptar XMLHttpRequest
const originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
if (typeof url === 'string' && url.includes('google-analytics.com')) {
url = url.replace('https://www.google-analytics.com', '/ga');
}
return originalOpen.call(this, method, url, ...rest);
};
})();
`,
}}
/>
{/* Google Analytics - Script principal via proxy */}
<Script
src="/gtm/js/G-QTT9QT0T3S"
strategy="afterInteractive"
async
/>
{/* Google Analytics - Inicialização */}
<Script
id="google-analytics-init"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-QTT9QT0T3S');
`,
}}
/>
</body>
</html>
+29
View File
@@ -0,0 +1,29 @@
import { MetadataRoute } from 'next'
export default function manifest(): MetadataRoute.Manifest {
return {
name: 'Evandro Fonseca Junior - Portfólio',
short_name: 'Evandro.dev',
description: 'Portfólio profissional de Evandro Fonseca Junior, Desenvolvedor Full-Stack',
start_url: '/',
display: 'standalone',
background_color: '#000000',
theme_color: '#000000',
orientation: 'portrait-primary',
categories: ['portfolio', 'developer', 'technology'],
lang: 'pt-BR',
dir: 'ltr',
icons: [
{
src: '/favicon.ico',
sizes: 'any',
type: 'image/x-icon',
},
{
src: '/icon.svg',
sizes: 'any',
type: 'image/svg+xml',
},
],
}
}
+38
View File
@@ -0,0 +1,38 @@
import Link from "next/link";
import { Home, ArrowLeft, Code2 } from "lucide-react";
export default function NotFound() {
return (
<div className="min-h-screen bg-[#030712] flex items-center justify-center px-6">
<div className="max-w-2xl mx-auto text-center">
<div className="mb-8">
<Code2 className="w-24 h-24 text-blue-500 mx-auto mb-4 opacity-50" />
<h1 className="text-6xl md:text-8xl font-black text-white mb-4">404</h1>
<h2 className="text-2xl md:text-3xl font-bold text-slate-400 mb-4">
Página não encontrada
</h2>
<p className="text-slate-500 mb-8">
A página que você está procurando não existe ou foi movida.
</p>
</div>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link
href="/"
className="px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-lg font-semibold transition-all flex items-center justify-center gap-2"
>
<Home className="w-5 h-5" />
Voltar para Home
</Link>
<Link
href="/#projects"
className="px-6 py-3 border border-slate-800 hover:border-slate-600 text-slate-400 hover:text-white rounded-lg font-semibold transition-all flex items-center justify-center gap-2"
>
<ArrowLeft className="w-5 h-5" />
Ver Projetos
</Link>
</div>
</div>
</div>
);
}
+51
View File
@@ -0,0 +1,51 @@
import { ImageResponse } from 'next/og'
export const runtime = 'edge'
export const alt = 'Evandro Fonseca Junior - Full Stack Developer'
export const size = {
width: 1200,
height: 630,
}
export const contentType = 'image/png'
export default async function Image() {
return new ImageResponse(
(
<div
style={{
background: '#030712',
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
color: 'white',
fontFamily: 'system-ui',
}}
>
<div
style={{
fontSize: 64,
fontWeight: 'bold',
marginBottom: 20,
textAlign: 'center',
}}
>
Evandro Fonseca Junior
</div>
<div
style={{
fontSize: 32,
color: '#3b82f6',
marginTop: 10,
textAlign: 'center',
}}
>
Desenvolvedor Full-Stack
</div>
</div>
),
{ ...size }
)
}
+14
View File
@@ -0,0 +1,14 @@
import { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: [
{
userAgent: '*',
allow: '/',
disallow: ['/api/', '/_next/', '/admin/'],
},
],
sitemap: 'https://evandro.dev.br/sitemap.xml',
}
}
+45
View File
@@ -0,0 +1,45 @@
import { MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
const baseUrl = 'https://evandro.dev.br'
const currentDate = new Date().toISOString()
return [
{
url: baseUrl,
lastModified: currentDate,
changeFrequency: 'monthly',
priority: 1.0,
},
{
url: `${baseUrl}/#about`,
lastModified: currentDate,
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${baseUrl}/#skills`,
lastModified: currentDate,
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${baseUrl}/#experience`,
lastModified: currentDate,
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${baseUrl}/#projects`,
lastModified: currentDate,
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${baseUrl}/#contact`,
lastModified: currentDate,
changeFrequency: 'monthly',
priority: 0.8,
},
]
}