This commit is contained in:
Leandro Afonso
2025-09-27 18:17:32 +01:00
commit 24e7da0447
24 changed files with 18093 additions and 0 deletions

61
app/app.vue Normal file
View File

@@ -0,0 +1,61 @@
<template>
<div class="min-h-screen bg-gray-900 text-white">
<TheHeader />
<main>
<NuxtPage />
</main>
<TheFooter />
</div>
</template>
<style>
/* Global styles */
html {
scroll-behavior: smooth;
}
body {
font-family:
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue',
Arial, sans-serif;
}
/* Custom scrollbar for webkit browsers */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #1f2937;
}
::-webkit-scrollbar-thumb {
background: #4b5563;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #6b7280;
}
/* Selection color */
::selection {
background-color: #3b82f6;
color: white;
}
/* Focus styles for accessibility */
*:focus {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
/* Ensure links have proper focus styles */
a:focus {
outline: 2px solid #3b82f6;
outline-offset: 2px;
border-radius: 4px;
}
</style>

View File

@@ -0,0 +1,178 @@
<script setup lang="ts">
import { type Project } from '~/data/projects'
// Define the props this component accepts
type Props = {
project: Project
}
const { project } = defineProps<Props>()
</script>
<template>
<NuxtLink
:to="`/projects/${project.slug}`"
class="group relative block overflow-hidden rounded-2xl bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 hover:border-blue-400/50 transition-all duration-500 transform hover:-translate-y-2 hover:shadow-2xl hover:shadow-blue-500/20">
<!-- Background Gradient Overlay -->
<div
class="absolute inset-0 bg-gradient-to-br from-blue-500/5 to-purple-500/5 opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
<!-- Content -->
<div class="relative p-8 flex flex-col h-full">
<!-- Project Category Badge -->
<div class="flex items-center justify-between mb-4">
<span
class="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium transition-all duration-300"
:class="
project.group === 'Academic'
? 'bg-purple-500/10 text-purple-400 border border-purple-500/20 rounded-lg group-hover:bg-purple-500/20 group-hover:border-purple-400/40'
: 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 rounded-full group-hover:bg-emerald-500/20 group-hover:border-emerald-400/40'
">
<!-- Academic Icon -->
<svg
v-if="project.group === 'Academic'"
class="w-3.5 h-3.5"
fill="currentColor"
viewBox="0 0 24 24">
<path
d="M12,3L1,9L12,15L21,9V16H23V9M5,13.18V17.18L12,21L19,17.18V13.18L12,17L5,13.18Z" />
</svg>
<!-- Freelance Icon -->
<svg
v-else
class="w-3.5 h-3.5"
fill="currentColor"
viewBox="0 0 24 24">
<path
d="M20,6H16V4A2,2 0 0,0 14,2H10A2,2 0 0,0 8,4V6H4A2,2 0 0,0 2,8V19A2,2 0 0,0 4,21H20A2,2 0 0,0 22,19V8A2,2 0 0,0 20,6M10,4H14V6H10V4Z" />
</svg>
{{ project.category }}
</span>
<div
class="w-8 h-8 rounded-full opacity-60 group-hover:opacity-100 transition-all duration-300"
:class="
project.group === 'Academic'
? 'bg-gradient-to-br from-purple-400 to-pink-400'
: 'bg-gradient-to-br from-emerald-400 to-teal-400'
" />
</div>
<!-- Project Title -->
<h3
class="text-2xl font-bold text-white mb-3 group-hover:text-blue-400 transition-colors duration-300 h-16">
{{ project.title }}
</h3>
<!-- Project Description -->
<p
class="text-gray-400 line-clamp-3 group-hover:text-gray-300 transition-colors duration-300 mb-6">
{{
project.description_points?.[0] ||
'A fascinating project showcasing technical expertise and innovation.'
}}
</p>
<div class="flex flex-col gap-6 mt-auto">
<!-- Tags -->
<div class="flex flex-wrap gap-2">
<span
v-for="(tag, index) in project.tags.slice(0, 4)"
:key="tag"
class="bg-gray-700/50 text-gray-300 text-sm font-medium px-3 py-1 rounded-full border border-gray-600 group-hover:border-gray-500 group-hover:bg-gray-600/50 transition-all duration-300"
:class="{ 'animate-fade-in-tag': true }"
:style="`animation-delay: ${index * 0.1}s`">
{{ tag }}
</span>
<span
v-if="project.tags.length > 4"
class="bg-gray-700/50 text-gray-400 text-sm font-medium px-3 py-1 rounded-full border border-gray-600">
+{{ project.tags.length - 4 }}
</span>
</div>
<!-- Project Link Indicator -->
<div class="flex items-center justify-between">
<div
class="flex items-center space-x-2 text-gray-400 group-hover:text-blue-400 transition-colors duration-300">
<span class="text-sm font-medium">View Project</span>
<svg
class="w-4 h-4 transform group-hover:translate-x-1 transition-transform duration-300"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</div>
<!-- Live Site Indicator -->
<div
v-if="project.link && !project.link.includes('github.com')"
class="flex items-center space-x-1 text-green-400">
<div class="w-2 h-2 bg-green-400 rounded-full animate-pulse" />
<span class="text-xs font-medium">Live</span>
</div>
<!-- GitHub Repository Indicator -->
<div
v-else-if="project.link && project.link.includes('github.com')"
class="flex items-center space-x-1 text-gray-400">
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 24 24">
<path
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.30.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
<span class="text-xs font-medium">Source</span>
</div>
<!-- Files Available Indicator -->
<div
v-else-if="project.files && project.files.length > 0"
class="flex items-center space-x-1 text-amber-400">
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 24 24">
<path
d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z" />
</svg>
<span class="text-xs font-medium">Files</span>
</div>
</div>
</div>
</div>
<!-- Hover Effect Border -->
<div
class="absolute inset-0 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-500 pointer-events-none">
<div
class="absolute inset-0 rounded-2xl blur-sm"
:class="
project.group === 'Academic'
? 'bg-gradient-to-r from-purple-400/20 to-pink-400/20'
: 'bg-gradient-to-r from-emerald-400/20 to-teal-400/20'
" />
</div>
</NuxtLink>
</template>
<style scoped>
.line-clamp-3 {
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
@keyframes fade-in-tag {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in-tag {
animation: fade-in-tag 0.5s ease-out forwards;
}
</style>

View File

@@ -0,0 +1,127 @@
<template>
<footer class="bg-gradient-to-t from-gray-900 via-gray-800 to-gray-900 text-gray-400 mt-20 border-t border-gray-800">
<div class="container mx-auto p-8">
<!-- Main Footer Content -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-8 mb-8">
<!-- Brand Section -->
<div class="space-y-4">
<div class="flex items-center space-x-2">
<div class="w-8 h-8 bg-gradient-to-br from-blue-400 to-purple-400 rounded-lg flex items-center justify-center">
<span class="text-white font-bold text-sm">LA</span>
</div>
<span class="text-xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
Leandro Afonso
</span>
</div>
<p class="text-gray-400 leading-relaxed max-w-md">
Cybersecurity specialist and systems engineer passionate about building secure, scalable solutions.
</p>
</div>
<!-- Quick Links -->
<div class="space-y-4">
<h3 class="text-lg font-semibold text-white">Quick Links</h3>
<ul class="space-y-2">
<li>
<NuxtLink to="/" class="hover:text-blue-400 transition-colors duration-300 flex items-center group">
<svg class="w-4 h-4 mr-2 opacity-60 group-hover:opacity-100 transition-opacity" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
Home
</NuxtLink>
</li>
<li>
<NuxtLink to="/projects" class="hover:text-blue-400 transition-colors duration-300 flex items-center group">
<svg class="w-4 h-4 mr-2 opacity-60 group-hover:opacity-100 transition-opacity" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
Projects
</NuxtLink>
</li>
<li>
<NuxtLink to="/about" class="hover:text-blue-400 transition-colors duration-300 flex items-center group">
<svg class="w-4 h-4 mr-2 opacity-60 group-hover:opacity-100 transition-opacity" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
About
</NuxtLink>
</li>
</ul>
</div>
<!-- Contact Info -->
<div class="space-y-4">
<h3 class="text-lg font-semibold text-white">Get In Touch</h3>
<div class="space-y-3">
<a
href="mailto:leo@0x1eo.dev"
class="flex items-center space-x-3 hover:text-blue-400 transition-colors duration-300 group"
>
<div class="w-8 h-8 bg-gray-800 rounded-lg flex items-center justify-center group-hover:bg-blue-600 transition-colors duration-300">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 4.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
</div>
<span>leo@0x1eo.dev</span>
</a>
<a
href="https://www.linkedin.com/in/0x1eo"
target="_blank"
rel="noopener noreferrer"
class="flex items-center space-x-3 hover:text-blue-400 transition-colors duration-300 group"
>
<div class="w-8 h-8 bg-gray-800 rounded-lg flex items-center justify-center group-hover:bg-blue-600 transition-colors duration-300">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
</svg>
</div>
<span>LinkedIn</span>
</a>
<a
href="https://0x1eo.dev/"
target="_blank"
rel="noopener noreferrer"
class="flex items-center space-x-3 hover:text-blue-400 transition-colors duration-300 group"
>
<div class="w-8 h-8 bg-gray-800 rounded-lg flex items-center justify-center group-hover:bg-blue-600 transition-colors duration-300">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9v-9m0-9v9m0 9c-5 0-9-4-9-9s4-9 9-9" />
</svg>
</div>
<span>Portfolio Website</span>
</a>
<a
href="tel:+351910074564"
class="flex items-center space-x-3 hover:text-blue-400 transition-colors duration-300 group"
>
<div class="w-8 h-8 bg-gray-800 rounded-lg flex items-center justify-center group-hover:bg-blue-600 transition-colors duration-300">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
</div>
<span>+351 910 074 564</span>
</a>
</div>
</div>
</div>
<!-- Bottom Bar -->
<div class="border-t border-gray-700 pt-6 flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0">
<p class="text-sm text-gray-500">
&copy; {{ new Date().getFullYear() }} Leandro Afonso. All rights reserved.
</p>
<div class="flex items-center space-x-6 text-sm text-gray-500">
<span>Built with using Nuxt & Tailwind</span>
<div class="flex items-center space-x-1">
<div class="w-2 h-2 bg-green-400 rounded-full animate-pulse" />
<span>Available for new opportunities</span>
</div>
</div>
</div>
</div>
</footer>
</template>

View File

@@ -0,0 +1,186 @@
<template>
<header
class="bg-gray-900/80 backdrop-blur-md text-white sticky top-0 z-50 border-b border-gray-800">
<nav class="container mx-auto flex justify-between items-center p-4">
<!-- Logo/Brand -->
<div class="flex flex-1 justify-start">
<NuxtLink to="/" class="group flex items-center space-x-2">
<div
class="w-10 h-10 bg-gradient-to-br from-blue-400 to-purple-400 rounded-lg flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<span class="text-white font-bold text-lg">LA</span>
</div>
<span
class="text-2xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent group-hover:from-blue-300 group-hover:to-purple-300 transition-all duration-300">
Leandro Afonso
</span>
</NuxtLink>
</div>
<!-- Desktop Navigation -->
<ul class="hidden md:flex space-x-8 text-lg">
<li>
<NuxtLink
to="/"
class="relative py-2 hover:text-blue-400 transition-colors duration-300 group"
:class="{ 'text-blue-400': $route.path === '/' }">
Home
<span
class="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-blue-400 to-purple-400 group-hover:w-full transition-all duration-300" />
</NuxtLink>
</li>
<li>
<NuxtLink
to="/projects"
class="relative py-2 hover:text-blue-400 transition-colors duration-300 group"
:class="{ 'text-blue-400': $route.path.startsWith('/projects') }">
Projects
<span
class="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-blue-400 to-purple-400 group-hover:w-full transition-all duration-300" />
</NuxtLink>
</li>
<li>
<NuxtLink
to="/about"
class="relative py-2 hover:text-blue-400 transition-colors duration-300 group"
:class="{ 'text-blue-400': $route.path === '/about' }">
About
<span
class="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-blue-400 to-purple-400 group-hover:w-full transition-all duration-300" />
</NuxtLink>
</li>
</ul>
<!-- Contact Button -->
<div class="hidden md:flex flex-1 justify-end">
<a
href="mailto:leo@0x1eo.dev"
class="inline-flex items-center px-4 py-2 bg-gradient-to-r from-blue-600 to-purple-600 text-white font-semibold rounded-lg hover:from-blue-700 hover:to-purple-700 transform hover:scale-105 transition-all duration-300 shadow-lg">
<svg
class="w-4 h-4 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 4.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
Contact
</a>
</div>
<!-- Mobile Menu Button -->
<button
class="md:hidden p-2 rounded-lg hover:bg-gray-800 transition-colors duration-300"
@click="toggleMobileMenu">
<svg
class="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
v-if="!mobileMenuOpen"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h16" />
<path
v-else
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</nav>
<!-- Mobile Menu -->
<div
v-if="mobileMenuOpen"
class="md:hidden bg-gray-900/95 backdrop-blur-md border-t border-gray-800 animate-fade-in">
<div class="container mx-auto p-4 space-y-4">
<NuxtLink
to="/"
class="block py-3 px-4 rounded-lg hover:bg-gray-800 transition-colors duration-300"
:class="{ 'bg-gray-800 text-blue-400': $route.path === '/' }"
@click="closeMobileMenu">
Home
</NuxtLink>
<NuxtLink
to="/projects"
class="block py-3 px-4 rounded-lg hover:bg-gray-800 transition-colors duration-300"
:class="{
'bg-gray-800 text-blue-400': $route.path.startsWith('/projects')
}"
@click="closeMobileMenu">
Projects
</NuxtLink>
<NuxtLink
to="/about"
class="block py-3 px-4 rounded-lg hover:bg-gray-800 transition-colors duration-300"
:class="{ 'bg-gray-800 text-blue-400': $route.path === '/about' }"
@click="closeMobileMenu">
About
</NuxtLink>
<a
href="mailto:leo@0x1eo.dev"
class="flex items-center justify-center py-3 px-4 bg-gradient-to-r from-blue-600 to-purple-600 text-white font-semibold rounded-lg hover:from-blue-700 hover:to-purple-700 transition-all duration-300">
<svg
class="w-4 h-4 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 4.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
Contact Me
</a>
</div>
</div>
</header>
</template>
<script setup>
import { ref } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const mobileMenuOpen = ref(false)
const toggleMobileMenu = () => {
mobileMenuOpen.value = !mobileMenuOpen.value
}
const closeMobileMenu = () => {
mobileMenuOpen.value = false
}
// Close mobile menu when route changes
watch(
() => route.path,
() => {
mobileMenuOpen.value = false
}
)
</script>
<style scoped>
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fade-in 0.3s ease-out;
}
</style>

175
app/data/projects.ts Normal file
View File

@@ -0,0 +1,175 @@
// slug: A unique, URL-friendly identifier for the project.
// group: Can be 'Freelance' or 'Academic' (for now)
// title: The project's title.
// category: The context of the project.
// date: The timeline of the project.
// link: A live URL for the project if available
// description_points: An array of key achievements.
// tags: Keywords for technologies and concepts used.
export type Project = {
slug: string
group: 'Freelance' | 'Academic'
title: string
category: string
date: string
link: string | null
description_points: string[]
tags: string[]
files?: Array<{
name: string
filename: string
type: string
description: string
}>
}
export const projects: Project[] = [
{
slug: 'playlix-website',
group: 'Freelance',
title: 'Playlix Website',
category: 'Freelance Project',
date: 'May 2025 Jun 2025',
link: 'https://playlix.pt/',
description_points: [
'Designed and developed the official Playlix website using Vue.js, delivering a responsive and fast-loading interface.',
'Worked directly with the founding team to shape the initial web presence and branding.'
],
tags: ['Vue.js', 'Web Development', 'UI/UX Design', 'Branding', 'Freelance']
},
{
slug: 'diet-platform-pro',
group: 'Academic',
title: 'Diet Platform Pro',
category: ' Web Programming',
date: '2024 2025',
link: null,
description_points: [
'Developed a full-stack web platform to connect nutritionists and patients.',
'Built a RESTful API using Node.js/Express.js and a Single Page Application (SPA) with Angular.',
'Implemented role-based authentication with JSON Web Tokens (JWT) for Admin, Nutritionist, and Patient roles.',
'Integrated real-time chat and notifications using Socket.IO.',
'Utilized MongoDB with Mongoose for robust data storage and modeling.'
],
tags: [
'Angular',
'Node.js',
'Express.js',
'MongoDB',
'Socket.IO',
'JWT',
'REST API',
'Full-Stack'
]
},
{
slug: 'penetration-testing-exploitation',
group: 'Academic',
title: 'Penetration Testing & Vulnerability Exploitation',
category: 'Penetration Testing and Ethical Hacking',
date: '2024 2025',
link: null,
description_points: [
'Conducted network reconnaissance and service enumeration using Nmap in a virtualized lab environment.',
'Exploited common vulnerabilities like Log4j and EternalBlue using the Metasploit Framework.',
'Implemented data exfiltration techniques via DNS, ICMP, and HTTP tunneling to bypass network security controls.',
'Deployed a pfSense firewall and Suricata Intrusion Prevention System (IPS) to contain and detect the simulated attacks.'
],
tags: [
'Metasploit',
'Nmap',
'Suricata',
'pfSense',
'Penetration Testing',
'Vulnerability Exploitation',
'Red Teaming'
],
files: [
{
name: 'TPHE Report I',
filename: 'TPHE_Relatorio_I.pdf',
type: 'pdf',
description:
'First evaluation report demonstrating penetration testing methodologies and findings'
},
{
name: 'TPHE Report II',
filename: 'TPHE_Relatorio_II.pdf',
type: 'pdf',
description:
'Second evaluation report covering advanced exploitation techniques and security assessments'
}
]
},
{
slug: 'high-availability-web-cluster',
group: 'Academic',
title: 'High-Availability Web Cluster',
category: 'Critical Systems',
date: '2024 2025',
link: 'https://github.com/0x1eo/HAWC',
description_points: [
'Built a fault-tolerant e-commerce web cluster utilizing NGINX for load balancing, MariaDB Cluster for database replication, and GlusterFS for distributed file storage.',
'Deployed the entire infrastructure on Debian Virtual Machines with full service redundancy and automated failover.'
],
tags: [
'NGINX',
'MariaDB Cluster',
'GlusterFS',
'Debian',
'High Availability',
'Load Balancing',
'Systems Engineering'
],
files: [
{
name: 'SC Report',
filename: 'SC_Relatorio.pdf',
type: 'pdf',
description:
'Critical Systems project report detailing the high-availability web cluster implementation'
}
]
},
{
slug: 'football-simulator',
group: 'Academic',
title: 'Football Simulator',
category: 'Programming Paradigms',
date: '2024 2025',
link: 'https://github.com/0x1eo/FootballSim',
description_points: [
'Developed a comprehensive football league management and simulation system using Java and Maven.',
'Implemented object-oriented design patterns including Strategy, Factory, Observer, and MVC for modular architecture.',
'Built a complete match simulation engine with realistic events, player statistics, and season management.',
'Created dynamic squad management with multiple tactical formations (4-4-2, 4-3-3, 5-3-2, 3-5-2).',
'Implemented data persistence through JSON import/export and automated HTML report generation.',
'Designed a console-based user interface for interactive game management and real-time match simulation.'
],
tags: [
'Java',
'Maven',
'OOP',
'Design Patterns',
'JSON',
'HTML Generation',
'Game Simulation',
'Console UI'
]
},
{
slug: 'order-management-system-c',
group: 'Academic',
title: 'Order Management System in C',
category: 'Programming Laboratory',
date: '2022 2023',
link: 'https://github.com/0x1eo/LP',
description_points: [
'Developed a command-line (CLI) CRUD application in C for managing customer orders and client data.',
'Managed application state using dynamic memory allocation and complex data structures (structs).',
'Implemented file I/O operations for data persistence, allowing the application to save and load state between sessions.'
],
tags: ['C', 'CLI', 'Data Structures', 'Dynamic Memory', 'File I/O']
}
]

184
app/pages/about.vue Normal file
View File

@@ -0,0 +1,184 @@
<script setup>
// Set page meta for better SEO
useHead({
title: 'About - Leandro Afonso',
meta: [
{ name: 'description', content: 'Learn more about Leandro Afonso, a cybersecurity specialist and systems engineer passionate about building secure, scalable solutions.' }
]
});
</script>
<template>
<div class="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900">
<!-- Hero Section -->
<section class="relative py-20 px-4 overflow-hidden">
<!-- Background Pattern -->
<div class="absolute inset-0 opacity-5">
<div class="absolute inset-0" style="background-image: radial-gradient(circle at 25% 25%, #3b82f6 0%, transparent 70%), radial-gradient(circle at 75% 75%, #8b5cf6 0%, transparent 70%);" />
</div>
<!-- Animated Geometric Shapes -->
<div class="absolute inset-0 overflow-hidden">
<div class="absolute top-1/4 right-1/4 w-64 h-64 bg-blue-500/10 rounded-full blur-3xl animate-pulse" />
<div class="absolute bottom-1/3 left-1/3 w-96 h-96 bg-purple-500/10 rounded-full blur-3xl animate-pulse" style="animation-delay: 2s;" />
</div>
<div class="relative z-10 max-w-4xl mx-auto">
<!-- Page Header -->
<div class="text-center mb-16">
<h1 class="text-5xl md:text-6xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-400 mb-6">
About Me
</h1>
<div class="w-24 h-1 bg-gradient-to-r from-blue-400 to-purple-400 mx-auto mb-6" />
<p class="text-xl text-gray-400 max-w-2xl mx-auto">
Passionate about cybersecurity and systems engineering
</p>
</div>
</div>
</section>
<!-- Main Content -->
<section class="py-20 px-4">
<div class="max-w-6xl mx-auto">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center mb-20">
<!-- Profile Image -->
<div class="relative group">
<div class="relative overflow-hidden rounded-2xl bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700">
<!-- Profile photo -->
<div class="aspect-square">
<img
src="/data/files/profile.jpg"
alt="Leandro Afonso - Cybersecurity Specialist"
class="w-full h-full object-cover"
>
</div>
<!-- Hover Effect -->
<div class="absolute inset-0 bg-gradient-to-br from-blue-400/10 to-purple-400/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
</div>
</div>
<!-- About Text -->
<div class="space-y-6">
<div>
<h2 class="text-3xl font-bold text-white mb-4">Hello, I'm Leandro</h2>
<p class="text-lg text-gray-300 leading-relaxed">
I'm a cybersecurity specialist and systems engineer with a passion for building secure, scalable solutions.
My journey in technology has led me to explore the intricate world of cybersecurity, where I focus on
protecting digital infrastructures and developing innovative security frameworks.
</p>
</div>
<div>
<p class="text-lg text-gray-300 leading-relaxed">
With expertise spanning from penetration testing to secure system architecture, I thrive on solving
complex security challenges and implementing robust solutions that safeguard critical systems and data.
</p>
</div>
<div>
<p class="text-lg text-gray-300 leading-relaxed">
When I'm not diving deep into security research or building systems, I enjoy sharing knowledge with
the cybersecurity community and staying updated with the latest threats and defensive techniques.
</p>
</div>
</div>
</div>
<!-- Skills Section -->
<div class="mb-20">
<h2 class="text-3xl font-bold text-center text-white mb-12">Core Expertise</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<!-- Cybersecurity -->
<div class="bg-gradient-to-br from-gray-800/50 to-gray-900/50 backdrop-blur-sm border border-gray-700 rounded-xl p-6 hover:border-blue-500/50 transition-all duration-300 group">
<div class="w-12 h-12 bg-gradient-to-br from-red-500 to-orange-500 rounded-lg flex items-center justify-center mb-4 group-hover:scale-110 transition-transform duration-300">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
</div>
<h3 class="text-xl font-semibold text-white mb-3">Cybersecurity</h3>
<p class="text-gray-400">Penetration testing, vulnerability assessment, security audits, and threat analysis.</p>
</div>
<!-- Systems Engineering -->
<div class="bg-gradient-to-br from-gray-800/50 to-gray-900/50 backdrop-blur-sm border border-gray-700 rounded-xl p-6 hover:border-blue-500/50 transition-all duration-300 group">
<div class="w-12 h-12 bg-gradient-to-br from-blue-500 to-cyan-500 rounded-lg flex items-center justify-center mb-4 group-hover:scale-110 transition-transform duration-300">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z" />
</svg>
</div>
<h3 class="text-xl font-semibold text-white mb-3">Systems Engineering</h3>
<p class="text-gray-400">System architecture, infrastructure design, automation, and scalable solutions.</p>
</div>
<!-- Security Research -->
<div class="bg-gradient-to-br from-gray-800/50 to-gray-900/50 backdrop-blur-sm border border-gray-700 rounded-xl p-6 hover:border-blue-500/50 transition-all duration-300 group">
<div class="w-12 h-12 bg-gradient-to-br from-purple-500 to-pink-500 rounded-lg flex items-center justify-center mb-4 group-hover:scale-110 transition-transform duration-300">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</div>
<h3 class="text-xl font-semibold text-white mb-3">Security Research</h3>
<p class="text-gray-400">Threat intelligence, exploit development, security tools creation, and vulnerability research.</p>
</div>
</div>
</div>
<!-- Contact Section -->
<div class="text-center">
<h2 class="text-3xl font-bold text-white mb-8">Let's Connect</h2>
<p class="text-xl text-gray-400 mb-8 max-w-2xl mx-auto">
Interested in collaborating on cybersecurity projects or discussing security challenges?
I'm always open to connecting with fellow security professionals and enthusiasts.
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center">
<a
href="mailto:leo@0x1eo.dev"
class="inline-flex items-center px-8 py-4 bg-gradient-to-r from-blue-600 to-purple-600 text-white font-semibold rounded-lg hover:from-blue-700 hover:to-purple-700 transform hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl"
>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 4.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
Get In Touch
</a>
<a
href="https://www.linkedin.com/in/0x1eo"
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center px-8 py-4 border-2 border-gray-600 text-gray-300 font-semibold rounded-lg hover:border-blue-400 hover:text-blue-400 transform hover:scale-105 transition-all duration-300"
>
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 24 24">
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
</svg>
LinkedIn
</a>
</div>
</div>
</div>
</section>
</div>
</template>
<style scoped>
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in-up {
animation: fade-in-up 0.8s ease-out forwards;
opacity: 0;
}
/* Smooth scrolling */
html {
scroll-behavior: smooth;
}
</style>

203
app/pages/index.vue Normal file
View File

@@ -0,0 +1,203 @@
<script setup>
import { projects } from '~/data/projects'
// Set page meta for better SEO
useHead({
title: 'Leandro Afonso - Cybersecurity & Systems Engineering Portfolio',
meta: [
{
name: 'description',
content:
'Portfolio showcasing cybersecurity and systems engineering projects by Leandro Afonso.'
}
]
})
// Debug: Log projects to console
if (process.env.NODE_ENV === 'development') {
console.log('Projects loaded:', projects)
}
</script>
<template>
<div
class="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900">
<!-- Hero Section -->
<section
class="relative min-h-screen flex items-center justify-center overflow-hidden">
<!-- Background Pattern -->
<div class="absolute inset-0 opacity-5">
<div
class="absolute inset-0"
style="
background-image:
radial-gradient(circle at 25% 25%, #3b82f6 0%, transparent 70%),
radial-gradient(circle at 75% 75%, #8b5cf6 0%, transparent 70%);
" />
</div>
<!-- Animated Geometric Shapes -->
<div class="absolute inset-0 overflow-hidden">
<div
class="absolute top-1/4 left-1/4 w-64 h-64 bg-blue-500/10 rounded-full blur-3xl animate-pulse" />
<div
class="absolute bottom-1/4 right-1/4 w-96 h-96 bg-purple-500/10 rounded-full blur-3xl animate-pulse"
style="animation-delay: 2s" />
</div>
<div class="relative z-10 text-center px-4 max-w-5xl mx-auto">
<!-- Main Title with Animation -->
<div class="space-y-6">
<h1
class="text-6xl md:text-7xl lg:text-8xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 via-purple-400 to-cyan-400 animate-fade-in">
<span class="block leading-tight">Cybersecurity</span>
<span
class="block leading-tight text-4xl md:text-5xl lg:text-6xl text-gray-300 mt-2"
>&</span
>
<span class="block leading-tight">Systems Engineering</span>
</h1>
<div
class="space-y-4 animate-fade-in-up"
style="animation-delay: 0.5s">
<p
class="text-xl md:text-2xl text-gray-300 max-w-3xl mx-auto leading-relaxed">
Building secure, scalable systems and exploring the frontiers of
cybersecurity
</p>
<p class="text-lg text-blue-400 font-semibold">
Portfolio by Leandro Afonso
</p>
</div>
<!-- CTA Buttons -->
<div
class="flex flex-col sm:flex-row gap-4 justify-center mt-8 animate-fade-in-up"
style="animation-delay: 1s">
<a
href="#projects"
class="inline-flex items-center px-8 py-4 bg-gradient-to-r from-blue-600 to-purple-600 text-white font-semibold rounded-lg hover:from-blue-700 hover:to-purple-700 transform hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl">
View My Work
<svg
class="w-5 h-5 ml-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 9l-7 7-7-7" />
</svg>
</a>
<a
href="mailto:leo@0x1eo.dev"
class="inline-flex items-center px-8 py-4 border-2 border-gray-600 text-gray-300 font-semibold rounded-lg hover:border-blue-400 hover:text-blue-400 transform hover:scale-105 transition-all duration-300">
Get In Touch
<svg
class="w-5 h-5 ml-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 4.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
</a>
</div>
</div>
</div>
</section>
<!-- Projects Section -->
<section id="projects" class="py-20 px-4">
<div class="max-w-7xl mx-auto">
<!-- Section Header -->
<div class="text-center mb-16">
<h2
class="text-5xl md:text-6xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-400 mb-6">
Featured Projects
</h2>
<div
class="w-24 h-1 bg-gradient-to-r from-blue-400 to-purple-400 mx-auto mb-6" />
<p class="text-xl text-gray-400 max-w-2xl mx-auto">
Exploring cybersecurity challenges and engineering innovative
solutions
</p>
</div>
<!-- Projects Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-8">
<ProjectCard
v-for="(project, index) in projects"
:key="project.slug"
:project="project"
:class="`animate-fade-in-up`"
:style="`animation-delay: ${index * 0.1}s`" />
</div>
<!-- View All Projects Link -->
<div class="text-center mt-12">
<NuxtLink
to="/projects"
class="inline-flex items-center px-6 py-3 border-2 border-blue-400 text-blue-400 font-semibold rounded-lg hover:bg-blue-400 hover:text-gray-900 transform hover:scale-105 transition-all duration-300">
View All Projects
<svg
class="w-5 h-5 ml-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</NuxtLink>
</div>
</div>
</section>
</div>
</template>
<style scoped>
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fade-in 1s ease-out forwards;
opacity: 0;
}
.animate-fade-in-up {
animation: fade-in-up 0.8s ease-out forwards;
opacity: 0;
}
/* Smooth scrolling */
html {
scroll-behavior: smooth;
}
</style>

View File

@@ -0,0 +1,284 @@
<script setup>
import { projects } from '~/data/projects';
import { useRoute } from 'vue-router';
const route = useRoute();
// Find the project that matches the slug from the URL
const project = projects.find(p => p.slug === route.params.slug);
// Set page meta
useHead({
title: project ? `${project.title} - Leandro Afonso` : 'Project Not Found - Leandro Afonso',
meta: [
{ name: 'description', content: project?.description_points?.[0] || 'Project details' }
]
});
</script>
<template>
<div class="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900">
<div v-if="project" class="container mx-auto px-4 py-12">
<!-- Back Button -->
<div class="mb-8">
<NuxtLink
to="/projects"
class="inline-flex items-center text-blue-400 hover:text-blue-300 transition-colors duration-300 group"
>
<svg class="w-5 h-5 mr-2 transform group-hover:-translate-x-1 transition-transform duration-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
Back to Projects
</NuxtLink>
</div>
<!-- Project Header -->
<div class="max-w-4xl mx-auto">
<div class="text-center mb-12">
<!-- Project Category -->
<div class="mb-4">
<span class="inline-flex items-center px-4 py-2 rounded-full text-sm font-medium bg-blue-500/10 text-blue-400 border border-blue-500/20">
{{ project.category }}
</span>
</div>
<!-- Project Title -->
<h1 class="text-5xl md:text-6xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-400 mb-6">
{{ project.title }}
</h1>
<!-- Project Timeline -->
<div class="flex items-center justify-center space-x-4 text-gray-400 mb-8">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2-2v14a2 2 0 002 2z" />
</svg>
<span class="text-lg">{{ project.date }}</span>
</div>
<!-- Project Link -->
<div v-if="project.link" class="mb-8">
<!-- Live Website -->
<a
v-if="!project.link.includes('github.com')"
:href="project.link"
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center px-6 py-3 bg-gradient-to-r from-blue-600 to-purple-600 text-white font-semibold rounded-lg hover:from-blue-700 hover:to-purple-700 transform hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl"
>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
</svg>
View Live Project
</a>
<!-- GitHub Repository -->
<a
v-else
:href="project.link"
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center px-6 py-3 bg-gradient-to-r from-gray-700 to-gray-800 text-white font-semibold rounded-lg hover:from-gray-600 hover:to-gray-700 border border-gray-600 hover:border-gray-500 transform hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl"
>
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
View Repository
</a>
</div>
</div>
<!-- Project Content -->
<div class="bg-gray-800/50 backdrop-blur-md rounded-2xl border border-gray-700 p-8 md:p-12">
<!-- Project Description -->
<div class="mb-10">
<h2 class="text-2xl font-bold text-white mb-6 flex items-center">
<svg class="w-6 h-6 mr-3 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
Project Overview
</h2>
<div class="space-y-4">
<div v-for="(point, index) in project.description_points" :key="index" class="flex items-start space-x-3">
<div class="w-2 h-2 bg-blue-400 rounded-full mt-3 flex-shrink-0" />
<p class="text-gray-300 leading-relaxed text-lg">{{ point }}</p>
</div>
</div>
</div>
<!-- Technologies Used -->
<div class="mb-10">
<h2 class="text-2xl font-bold text-white mb-6 flex items-center">
<svg class="w-6 h-6 mr-3 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
</svg>
Technologies & Skills
</h2>
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3">
<div
v-for="(tag, index) in project.tags"
:key="tag"
class="group relative bg-gray-700/50 border border-gray-600 rounded-lg p-4 text-center hover:border-blue-400/50 hover:bg-gray-600/50 transition-all duration-300 transform hover:-translate-y-1"
:class="`animate-fade-in-tag`"
:style="`animation-delay: ${index * 0.05}s`"
>
<span class="text-gray-300 font-medium group-hover:text-blue-400 transition-colors duration-300">
{{ tag }}
</span>
<!-- Hover Effect -->
<div class="absolute inset-0 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none">
<div class="absolute inset-0 rounded-lg bg-gradient-to-r from-blue-400/10 to-purple-400/10" />
</div>
</div>
</div>
</div>
<!-- Project Files -->
<div v-if="project.files && project.files.length > 0" class="mb-10">
<h2 class="text-2xl font-bold text-white mb-6 flex items-center">
<svg class="w-6 h-6 mr-3 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
Project Documentation
</h2>
<div class="grid gap-4 md:grid-cols-2">
<div
v-for="(file, index) in project.files"
:key="file.filename"
class="group relative bg-gray-700/50 border border-gray-600 rounded-lg p-6 hover:border-green-400/50 hover:bg-gray-600/50 transition-all duration-300 transform hover:-translate-y-1"
:class="`animate-fade-in-tag`"
:style="`animation-delay: ${index * 0.1}s`"
>
<div class="flex items-start space-x-4">
<!-- PDF Icon -->
<div class="flex-shrink-0">
<svg class="w-12 h-12 text-red-400" fill="currentColor" viewBox="0 0 24 24">
<path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z" />
</svg>
</div>
<!-- File Info -->
<div class="flex-1 min-w-0">
<h3 class="text-lg font-semibold text-white group-hover:text-green-400 transition-colors duration-300 mb-2">
{{ file.name }}
</h3>
<p class="text-gray-400 text-sm mb-3 leading-relaxed">
{{ file.description }}
</p>
<!-- Download Link -->
<a
:href="`/data/files/${file.filename}`"
target="_blank"
class="inline-flex items-center text-green-400 hover:text-green-300 font-medium text-sm transition-colors duration-300"
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
Download PDF
</a>
</div>
</div>
<!-- Hover Effect -->
<div class="absolute inset-0 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none">
<div class="absolute inset-0 rounded-lg bg-gradient-to-r from-green-400/10 to-blue-400/10" />
</div>
</div>
</div>
</div>
<!-- Project Type & Links -->
<div class="border-t border-gray-700 pt-8">
<div class="flex flex-col md:flex-row justify-between items-start md:items-center space-y-4 md:space-y-0">
<div>
<h3 class="text-lg font-semibold text-white mb-2">Project Type</h3>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gradient-to-r from-green-500/10 to-blue-500/10 text-green-400 border border-green-500/20">
{{ project.group }}
</span>
</div>
<div v-if="project.link && !project.link.includes('github.com')" class="flex items-center space-x-2 text-green-400">
<div class="w-3 h-3 bg-green-400 rounded-full animate-pulse" />
<span class="font-medium">Live & Active</span>
</div>
<div v-else-if="project.link && project.link.includes('github.com')" class="flex items-center space-x-2 text-gray-400">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
<span class="font-medium">Open Source</span>
</div>
</div>
</div>
</div>
<!-- Related Projects -->
<div class="mt-16">
<h2 class="text-3xl font-bold text-center text-white mb-8">Explore More Projects</h2>
<div class="flex justify-center space-x-4">
<NuxtLink
to="/projects"
class="inline-flex items-center px-6 py-3 border-2 border-blue-400 text-blue-400 font-semibold rounded-lg hover:bg-blue-400 hover:text-gray-900 transform hover:scale-105 transition-all duration-300"
>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
All Projects
</NuxtLink>
<NuxtLink
to="/"
class="inline-flex items-center px-6 py-3 border-2 border-gray-600 text-gray-300 font-semibold rounded-lg hover:border-gray-400 hover:text-gray-100 transform hover:scale-105 transition-all duration-300"
>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
Back Home
</NuxtLink>
</div>
</div>
</div>
</div>
<!-- Project Not Found -->
<div v-else class="container mx-auto px-4 py-20 text-center">
<div class="max-w-md mx-auto">
<div class="mb-8">
<svg class="w-24 h-24 mx-auto text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M9.172 16.172a4 4 0 015.656 0M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
</div>
<h1 class="text-4xl font-bold text-white mb-4">Project Not Found</h1>
<p class="text-gray-400 mb-8">The project you're looking for doesn't exist or may have been moved.</p>
<NuxtLink
to="/projects"
class="inline-flex items-center px-6 py-3 bg-gradient-to-r from-blue-600 to-purple-600 text-white font-semibold rounded-lg hover:from-blue-700 hover:to-purple-700 transform hover:scale-105 transition-all duration-300"
>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
Back to Projects
</NuxtLink>
</div>
</div>
</div>
</template>
<style scoped>
@keyframes fade-in-tag {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in-tag {
animation: fade-in-tag 0.6s ease-out forwards;
opacity: 0;
}
</style>

View File

@@ -0,0 +1,120 @@
<script setup>
import { projects } from '~/data/projects';
import { ref } from 'vue';
// Set page meta
useHead({
title: 'Projects - Leandro Afonso',
meta: [
{ name: 'description', content: 'Browse all cybersecurity and systems engineering projects by Leandro Afonso.' }
]
});
// Filter functionality
const selectedGroup = ref('All');
const groups = ['All', 'Freelance', 'Academic'];
const filteredProjects = computed(() => {
if (selectedGroup.value === 'All') {
return projects;
}
return projects.filter(project => project.group === selectedGroup.value);
});
</script>
<template>
<div class="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900">
<div class="container mx-auto px-4 py-12">
<!-- Page Header -->
<div class="text-center mb-16">
<h1 class="text-5xl md:text-6xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-400 mb-6">
All Projects
</h1>
<div class="w-24 h-1 bg-gradient-to-r from-blue-400 to-purple-400 mx-auto mb-6" />
<p class="text-xl text-gray-400 max-w-2xl mx-auto">
A comprehensive collection of cybersecurity research, systems engineering, and technical projects
</p>
</div>
<!-- Filter Buttons -->
<div class="flex flex-wrap justify-center gap-4 mb-12">
<button
v-for="group in groups"
:key="group"
class="px-6 py-3 rounded-lg font-semibold transition-all duration-300 transform hover:scale-105"
:class="selectedGroup === group
? 'bg-gradient-to-r from-blue-600 to-purple-600 text-white shadow-lg'
: 'bg-gray-800 text-gray-300 border border-gray-700 hover:border-blue-400 hover:text-blue-400'"
@click="selectedGroup = group"
>
{{ group }}
<span v-if="group !== 'All'" class="ml-2 text-sm opacity-75">
({{ projects.filter(p => p.group === group).length }})
</span>
<span v-else class="ml-2 text-sm opacity-75">
({{ projects.length }})
</span>
</button>
</div>
<!-- Projects Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-8 max-w-7xl mx-auto">
<ProjectCard
v-for="(project, index) in filteredProjects"
:key="project.slug"
:project="project"
class="animate-fade-in-up"
:style="`animation-delay: ${index * 0.1}s`"
/>
</div>
<!-- Empty State -->
<div v-if="filteredProjects.length === 0" class="text-center py-20">
<div class="mb-8">
<svg class="w-24 h-24 mx-auto text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</div>
<h3 class="text-2xl font-bold text-white mb-4">No Projects Found</h3>
<p class="text-gray-400 mb-8">No projects match the selected filter.</p>
<button
class="inline-flex items-center px-6 py-3 bg-gradient-to-r from-blue-600 to-purple-600 text-white font-semibold rounded-lg hover:from-blue-700 hover:to-purple-700 transition-all duration-300"
@click="selectedGroup = 'All'"
>
Show All Projects
</button>
</div>
<!-- Back to Home -->
<div class="text-center mt-16">
<NuxtLink
to="/"
class="inline-flex items-center text-blue-400 hover:text-blue-300 transition-colors duration-300 group"
>
<svg class="w-5 h-5 mr-2 transform group-hover:-translate-x-1 transition-transform duration-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
Back to Home
</NuxtLink>
</div>
</div>
</div>
</template>
<style scoped>
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in-up {
animation: fade-in-up 0.8s ease-out forwards;
opacity: 0;
}
</style>