feat: v0 of portfolio
This commit is contained in:
48
src/components/About.tsx
Normal file
48
src/components/About.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import image from '../assets/sketch.png';
|
||||
|
||||
const taglines = [
|
||||
"Amateur Writer",
|
||||
"Bookworm",
|
||||
"Star Gazer",
|
||||
"Physics Enthusiast",
|
||||
"Lifelong Learner",
|
||||
"Tea Enthusiast",
|
||||
"Cosmic Explorer",
|
||||
"Knowledge Seeker",
|
||||
];
|
||||
|
||||
export default function About({ theme }) {
|
||||
const [tagline, setTagline] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const randomTagline = taglines[Math.floor(Math.random() * taglines.length)];
|
||||
setTagline(randomTagline);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section id="about" class="pt-8">
|
||||
<div class="flex flex-col md:flex-row gap-8 items-center">
|
||||
<div class="flex-1">
|
||||
<h1 class={`text-4xl md:text-5xl mb-4 ${theme.accent} font-serif font-semibold`}>Alejandro Laguna</h1>
|
||||
<h2 class="text-xl md:text-2xl mb-6 text-[#b8c4b5]/80 font-medium font-serif">Software Engineer & {tagline}</h2>
|
||||
<p class="text-lg mb-6 text-[#b8c4b5]/90">
|
||||
Crafting digital projects that inspire me, while exploring everything from books and tea to space and science. First-year computer science student.
|
||||
</p>
|
||||
<div class="flex gap-4">
|
||||
<a href="#projects" class={`px-6 py-2 rounded-full border ${theme.border} ${theme.hover} transition-all hover:-translate-y-0.5`}>
|
||||
View Work
|
||||
</a>
|
||||
<a href="mailto:alejandro.laguna@epitech.eu" class={`px-6 py-2 rounded-full ${theme.button} transition-all hover:-translate-y-0.5`}>
|
||||
Contact
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 flex justify-center">
|
||||
<img src={image} alt="Alex Clark's Sketch" class="w-64 h-64 rounded-full object-cover" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
36
src/components/Education.tsx
Normal file
36
src/components/Education.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import EducationCard from './EducationCard';
|
||||
|
||||
const education = [
|
||||
{
|
||||
institution: "European Institute of Technology (EPITECH)",
|
||||
degree: "Degree + Master in Computer Science",
|
||||
duration: "2024 - 2029",
|
||||
highlights: [
|
||||
"ONGOING",
|
||||
"Multiple courses passed with Honors",
|
||||
"Completed over 30 projects from different fields"
|
||||
]
|
||||
},
|
||||
{
|
||||
institution: "INS Cendrassos",
|
||||
degree: "Microcomputer systems & networks",
|
||||
duration: "2022 - 2024",
|
||||
highlights: [
|
||||
"First of the class",
|
||||
"ERASMUS+ Scholarship",
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export default function Education({ theme }) {
|
||||
return (
|
||||
<section id="education">
|
||||
<h2 class={`text-3xl mb-8 font-serif font-semibold ${theme.accent}`}>Education</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{education.map((edu, i) => (
|
||||
<EducationCard key={i} education={edu} theme={theme} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
18
src/components/EducationCard.tsx
Normal file
18
src/components/EducationCard.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
export default function EducationCard({ education, theme }) {
|
||||
return (
|
||||
<div class={`p-4 rounded-xl border ${theme.border} ${theme.card} transition-all hover:-translate-y-1 group`}>
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<div>
|
||||
<h3 class="text-lg">{education.degree}</h3>
|
||||
<p class="text-[#b8c4b5]/80">{education.institution}</p>
|
||||
</div>
|
||||
<span class="text-sm text-[#b8c4b5]/50">{education.duration}</span>
|
||||
</div>
|
||||
<ul class="list-disc pl-6 space-y-2 mb-4">
|
||||
{education.highlights.map((highlight, j) => (
|
||||
<li key={j} class="text-[#b8c4b5]/90">{highlight}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
39
src/components/Experience.tsx
Normal file
39
src/components/Experience.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import ExperienceCard from './ExperienceCard';
|
||||
|
||||
const experience = [
|
||||
{
|
||||
company: "Avantiam Inc - Figueres, Spain",
|
||||
role: "Full-Stack Developer",
|
||||
duration: "Sep 2023 - Sep 2024",
|
||||
technologies: ["Laravel", "Livewire", "AlpineJS", "TailwindCSS", "MariaDB/MySQL"],
|
||||
highlights: [
|
||||
"Developed and maintained a comprehensive business management service used by over 100 companies",
|
||||
"Managed all aspects of the project lifecycle, including server administration, performance tuning, and user experience improvements.",
|
||||
"Recommendation letter available upon request."
|
||||
]
|
||||
},
|
||||
{
|
||||
company: "Niblu Group - Maribor, Slovenia",
|
||||
role: "Backend Developer Intern",
|
||||
duration: "Summer 2023",
|
||||
technologies: ["NextJS", "TailwindCSS", "TypeScript", "FastAPI", "AWS S3 Bucket"],
|
||||
highlights: [
|
||||
"Developed a secure authentication system and features for managing courses across different user roles.",
|
||||
"Integrated AWS S3 for seamless storage and retrieval of course materials.",
|
||||
"Gained valuable personal and professional growth through the ERASMUS experience, adapting to new challenges abroad alone."
|
||||
]
|
||||
}
|
||||
];
|
||||
export default function Experience({ theme }) {
|
||||
return (
|
||||
<section id="experience">
|
||||
<h2 class={`text-3xl mb-8 ${theme.accent} font-semibold font-serif`}>Work Experience</h2>
|
||||
<div class="space-y-8">
|
||||
{experience.map((exp, i) => (
|
||||
<ExperienceCard key={i} experience={exp} theme={theme} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
36
src/components/ExperienceCard.tsx
Normal file
36
src/components/ExperienceCard.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
export default function ExperienceCard({ experience, theme }) {
|
||||
return (
|
||||
<div class={`p-6 rounded-xl border ${theme.border} ${theme.card} transition-all hover:-translate-y-1 group`}>
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<div>
|
||||
<h3 class="text-xl">{experience.company}</h3>
|
||||
<p class="text-[#b8c4b5]/80">{experience.role}</p>
|
||||
</div>
|
||||
<span class="text-sm text-[#b8c4b5]/50">{experience.duration}</span>
|
||||
</div>
|
||||
<ul class="list-disc pl-6 space-y-2 mb-4">
|
||||
{experience.highlights.map((highlight, j) => (
|
||||
<li key={j} class="text-[#b8c4b5]/90">{highlight}</li>
|
||||
))}
|
||||
</ul>
|
||||
{experience.technologies && (
|
||||
<div class="flex flex-wrap gap-2 mb-4">
|
||||
<h4 class="text-lg font-semibold text-[#b8c4b5]/90">Technologies Used:</h4>
|
||||
{experience.technologies.map((tech, i) => (
|
||||
<span key={i} class={`px-3 rounded-full ${theme.bg} text-[#b8c4b5]/80`}>{tech}</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{experience.link && (
|
||||
<a
|
||||
href={experience.link}
|
||||
class={`inline-flex items-center text-sm ${theme.accent} ${theme.hover}`}
|
||||
>
|
||||
View More
|
||||
<span class="ml-2 transition-transform group-hover:translate-x-1">→</span>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
26
src/components/Footer.tsx
Normal file
26
src/components/Footer.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
export default function Footer({ theme }) {
|
||||
const handleDownload = () => {
|
||||
const link = document.createElement('a');
|
||||
link.href = '/public/resume_eng.pdf';
|
||||
link.download = 'alejandrolaguna_resume_eng.pdf';
|
||||
link.click();
|
||||
};
|
||||
|
||||
return (
|
||||
<footer class={`pt-8 border-t border-opacity-20 ${theme.border}`}>
|
||||
<div class="flex flex-col md:flex-row justify-between items-center">
|
||||
<p class="text-sm text-[#b8c4b5]/50 mb-4 md:mb-0">© 2025 Alejandro Laguna</p>
|
||||
<div class="flex gap-6">
|
||||
<a href="https://github.com/alejandrolaguna20" class={`text-sm ${theme.text} ${theme.hover}`}>GitHub</a>
|
||||
<a href="https://www.linkedin.com/in/alejandro-laguna-939687278/" class={`text-sm ${theme.text} ${theme.hover}`}>LinkedIn</a>
|
||||
<button
|
||||
onClick={handleDownload}
|
||||
class={`text-sm ${theme.text} ${theme.hover} cursor-pointer`}
|
||||
>
|
||||
CV
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
@@ -1,18 +1,22 @@
|
||||
import { useLocation } from 'preact-iso';
|
||||
|
||||
export function Header() {
|
||||
const { url } = useLocation();
|
||||
export function Header({ theme }) {
|
||||
|
||||
return (
|
||||
<header>
|
||||
<nav>
|
||||
<a href="/" class={url == '/' && 'active'}>
|
||||
Home
|
||||
</a>
|
||||
<a href="/404" class={url == '/404' && 'active'}>
|
||||
404
|
||||
</a>
|
||||
</nav>
|
||||
<header class="fixed w-full top-0 z-50 backdrop-blur-sm">
|
||||
<div class="max-w-5xl mx-auto px-4 py-3 flex justify-between items-center">
|
||||
<h1 class={`text-xl font-bold opacity-50 ${theme.accent} tracking-wider font-serif`}>alejandrolaguna.dev</h1>
|
||||
<nav class="hidden md:flex gap-6">
|
||||
{['About', 'Projects', 'Experience'].map((item) => (
|
||||
<a
|
||||
href={`#${item.toLowerCase()}`}
|
||||
class={`text-sm ${theme.text} ${theme.hover} transition-colors`}
|
||||
>
|
||||
{item}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
10
src/components/Layout.tsx
Normal file
10
src/components/Layout.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
export default function Layout({ children, theme }) {
|
||||
return (
|
||||
<div class={`min-h-screen transition-colors duration-300 ${theme.bg} ${theme.text}`}>
|
||||
<main class="max-w-5xl mx-auto px-4 pt-24 pb-16 space-y-16">
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
26
src/components/ProjectCard.tsx
Normal file
26
src/components/ProjectCard.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
export default function ProjectCard({ project, theme }) {
|
||||
return (
|
||||
<div class={`p-6 rounded-xl border ${theme.border} ${theme.card} transition-all hover:-translate-y-1 group`}>
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<h3 class="text-xl">{project.title}</h3>
|
||||
<span class="text-sm text-[#b8c4b5]/50">{project.year}</span>
|
||||
</div>
|
||||
<p class="mb-4 text-[#b8c4b5]/80">{project.description}</p>
|
||||
<div class="flex flex-wrap gap-2 mb-4">
|
||||
{project.tech.map((tech, j) => (
|
||||
<span key={j} class={`px-3 py-1 rounded-full ${theme.bg} text-[#b8c4b5]/80`}>{tech}</span>
|
||||
))}
|
||||
</div>
|
||||
{project.link && (
|
||||
<a
|
||||
href={project.link}
|
||||
class={`inline-flex items-center text-sm ${theme.accent} ${theme.hover}`}
|
||||
>
|
||||
View Details
|
||||
<span class="ml-2 transition-transform group-hover:translate-x-1">→</span>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
46
src/components/Projects.tsx
Normal file
46
src/components/Projects.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import ProjectCard from './ProjectCard';
|
||||
|
||||
const projects = [
|
||||
{
|
||||
title: "Codertype",
|
||||
description: "A monkeytype-like application designed specifically for developers",
|
||||
tech: ["Laravel", "Livewire", "AlpineJS", "TailwindCSS", "PostgreSQL"],
|
||||
year: "2025",
|
||||
link: "https://github.com/alejandrolaguna20/codertype"
|
||||
},
|
||||
{
|
||||
title: "Nook.nvim",
|
||||
description: "A tiny Neovim plugin created to manage tasks in a way that perfectly fits my personal workflow ",
|
||||
tech: ["Lua"],
|
||||
year: "2025",
|
||||
link: "https://github.com/alejandrolaguna20/nook.nvim"
|
||||
},
|
||||
{
|
||||
title: "Portfolio",
|
||||
description: "The website you are currently browsing, my little corner on the web.",
|
||||
tech: ["Typescript", "PreactJS", "TailwindCSS", "Vite"],
|
||||
year: "2025",
|
||||
link: "https://github.com/alejandrolaguna20/portfolio"
|
||||
},
|
||||
{
|
||||
title: "Epitech Projects",
|
||||
description: "A variety of projects I am building in my first year @ Epitech that range from algorithm implementation, graphical and mathematical simulations, and developing from scratch different Linux commands and programs, including a shell.",
|
||||
tech: ["C", "Bash", "Python"],
|
||||
year: "2025",
|
||||
link: "https://www.epitech.eu/programme-grande-ecole-informatique/"
|
||||
}
|
||||
];
|
||||
|
||||
export default function Projects({ theme }) {
|
||||
return (
|
||||
<section id="projects">
|
||||
<h2 class={`text-3xl font-semibold font-serif mb-8 ${theme.accent}`}>Projects</h2>
|
||||
<div class="grid gap-6">
|
||||
{projects.map((project, i) => (
|
||||
<ProjectCard key={i} project={project} theme={theme} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user