Initial portfolio with Woodpecker CI/CD
4
.env.example
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
SFTP_HOST=access-5020883526.webspace-host.com
|
||||
SFTP_PORT=22
|
||||
SFTP_USER=su1164906
|
||||
SFTP_PASSWORD=your_password_here
|
||||
25
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
.env
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
8
.oxlintrc.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||
"plugins": ["react", "typescript", "oxc"],
|
||||
"rules": {
|
||||
"react/rules-of-hooks": "error",
|
||||
"react/only-export-components": ["warn", { "allowConstantExport": true }]
|
||||
}
|
||||
}
|
||||
43
.woodpecker.yml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Woodpecker CI/CD — portfolio → rafaelgonzalezalbes.com
|
||||
# Secrets: sftp_host, sftp_user, sftp_pass, sftp_remote_path (= dist)
|
||||
|
||||
when:
|
||||
- event: [push, pull_request]
|
||||
|
||||
steps:
|
||||
install:
|
||||
image: node:22-alpine
|
||||
commands:
|
||||
- npm ci
|
||||
|
||||
lint:
|
||||
image: node:22-alpine
|
||||
depends_on: [install]
|
||||
commands:
|
||||
- npm run lint
|
||||
|
||||
build:
|
||||
image: node:22-alpine
|
||||
depends_on: [lint]
|
||||
commands:
|
||||
- npm run build
|
||||
- node check-qa.cjs
|
||||
|
||||
deploy:
|
||||
image: alpine:3.20
|
||||
depends_on: [build]
|
||||
environment:
|
||||
SFTP_HOST:
|
||||
from_secret: sftp_host
|
||||
SFTP_USER:
|
||||
from_secret: sftp_user
|
||||
SFTP_PASS:
|
||||
from_secret: sftp_pass
|
||||
REMOTE_PATH:
|
||||
from_secret: sftp_remote_path
|
||||
commands:
|
||||
- apk add --no-cache lftp
|
||||
- lftp -c "set sftp:auto-confirm yes; open -u $SFTP_USER,$SFTP_PASS sftp://$SFTP_HOST; mirror -R dist/ $REMOTE_PATH --delete --verbose"
|
||||
when:
|
||||
- event: push
|
||||
branch: main
|
||||
129
check-qa.cjs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const distDir = path.resolve(__dirname, 'dist');
|
||||
|
||||
console.log('--------------------------------------------------');
|
||||
console.log('🤖 Running Automated QA Release Check...');
|
||||
console.log('--------------------------------------------------');
|
||||
|
||||
// Helper to log test stages
|
||||
function runStage(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(`✅ [PASS] ${name}`);
|
||||
} catch (err) {
|
||||
console.error(`❌ [FAIL] ${name}`);
|
||||
console.error(` 👉 Reason: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Verify build directory exists
|
||||
runStage('Build Directory Verification', () => {
|
||||
if (!fs.existsSync(distDir)) {
|
||||
throw new Error('Build output directory "dist/" does not exist. Run "npm run build" first.');
|
||||
}
|
||||
});
|
||||
|
||||
const indexHtmlPath = path.join(distDir, 'index.html');
|
||||
let htmlContent = '';
|
||||
|
||||
// 2. Verify index.html exists and is readable
|
||||
runStage('index.html Integrity Check', () => {
|
||||
if (!fs.existsSync(indexHtmlPath)) {
|
||||
throw new Error('index.html is missing from dist/ folder.');
|
||||
}
|
||||
htmlContent = fs.readFileSync(indexHtmlPath, 'utf8');
|
||||
if (!htmlContent || htmlContent.trim().length === 0) {
|
||||
throw new Error('index.html is empty.');
|
||||
}
|
||||
});
|
||||
|
||||
// 3. Check for SEO and duplicate tag bugs
|
||||
runStage('SEO & Metadata Tag Checks', () => {
|
||||
// Check for duplicate <title> tags
|
||||
const titleMatches = htmlContent.match(/<title[^>]*>([\s\S]*?)<\/title>/gi);
|
||||
if (!titleMatches) {
|
||||
throw new Error('No <title> tag found in index.html.');
|
||||
}
|
||||
if (titleMatches.length > 1) {
|
||||
throw new Error(`Duplicate <title> tags detected! Found ${titleMatches.length} title tags.`);
|
||||
}
|
||||
|
||||
// Ensure title contains Rafael's name
|
||||
const titleText = titleMatches[0];
|
||||
if (!titleText.toLowerCase().includes('rafael')) {
|
||||
throw new Error('Optimized title does not contain "Rafael" - SEO validation failed.');
|
||||
}
|
||||
|
||||
// Check for duplicate descriptions
|
||||
const descMatches = htmlContent.match(/<meta\s+name="description"\s+content="[^"]*"\s*\/?>/gi);
|
||||
if (descMatches && descMatches.length > 1) {
|
||||
throw new Error(`Duplicate meta description tags detected! Found ${descMatches.length} descriptions.`);
|
||||
}
|
||||
});
|
||||
|
||||
// 4. Verify critical routing and search engine assets
|
||||
runStage('Core Assets Verification', () => {
|
||||
const criticalFiles = ['.htaccess', 'robots.txt', 'sitemap.xml'];
|
||||
for (const file of criticalFiles) {
|
||||
const filePath = path.join(distDir, file);
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(`Critical file "${file}" is missing from the build directory.`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 5. Verify local asset references exist in dist/
|
||||
runStage('Broken Reference & Asset Verification', () => {
|
||||
// Regex to match local src and href attributes in index.html
|
||||
const linkRegex = /(src|href)="\/([^"]+)"/g;
|
||||
let match;
|
||||
const verifiedAssets = [];
|
||||
|
||||
while ((match = linkRegex.exec(htmlContent)) !== null) {
|
||||
const assetPath = match[2];
|
||||
|
||||
// Skip absolute links, PDF documents, and external URLs
|
||||
if (
|
||||
assetPath.startsWith('http') ||
|
||||
assetPath.startsWith('//') ||
|
||||
assetPath.endsWith('.pdf') ||
|
||||
assetPath.startsWith('sitemap') ||
|
||||
assetPath.startsWith('robots')
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fullAssetPath = path.join(distDir, assetPath.split('?')[0]);
|
||||
if (!fs.existsSync(fullAssetPath)) {
|
||||
throw new Error(`Broken link/reference detected! File "/${assetPath}" was referenced in index.html but does not exist in the build output.`);
|
||||
}
|
||||
verifiedAssets.push(assetPath);
|
||||
}
|
||||
|
||||
if (verifiedAssets.length > 0) {
|
||||
console.log(` 👉 Verified ${verifiedAssets.length} asset references correctly.`);
|
||||
}
|
||||
});
|
||||
|
||||
// 6. Verify language flags exist
|
||||
runStage('Locale Flag Image Integrity Check', () => {
|
||||
const flagsDir = path.join(distDir, 'flags');
|
||||
if (!fs.existsSync(flagsDir)) {
|
||||
throw new Error('flags/ directory is missing from build output.');
|
||||
}
|
||||
const requiredFlags = ['gb.png', 'es.png', 'it.png', 'de.png'];
|
||||
for (const flag of requiredFlags) {
|
||||
const flagPath = path.join(flagsDir, flag);
|
||||
if (!fs.existsSync(flagPath)) {
|
||||
throw new Error(`Required country flag image "${flag}" is missing from flags/ directory.`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('--------------------------------------------------');
|
||||
console.log('🎉 QA Release Check complete. Build is healthy!');
|
||||
console.log('--------------------------------------------------');
|
||||
process.exit(0);
|
||||
56
index.html
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="/android-chrome-192x192.png" />
|
||||
<link rel="icon" type="image/png" sizes="512x512" href="/android-chrome-512x512.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<!-- Cache control - always serve fresh HTML -->
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<meta http-equiv="Expires" content="0" />
|
||||
|
||||
<!-- Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- SEO & Meta Tags -->
|
||||
<title>Rafael González Albes | Professional Software Developer</title>
|
||||
<meta name="description" content="Official portfolio of Rafael González Albes, a Professional Software Developer specializing in high-performance backends (PHP/Symfony), modern frontends (TypeScript/React), and containerized Docker systems." />
|
||||
<meta name="keywords" content="Rafael Gonzalez Albes, Senior Software Engineer, Backend Developer, PHP Developer, Symfony Expert, Full Stack Developer, React, TypeScript, Docker, Hire Developer Europe, Software Architecture" />
|
||||
<meta name="author" content="Rafael González Albes" />
|
||||
<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" />
|
||||
<link rel="canonical" href="https://rafaelgonzalezalbes.com/" />
|
||||
|
||||
<!-- Open Graph / LinkedIn / Facebook -->
|
||||
<meta property="og:type" content="profile" />
|
||||
<meta property="og:url" content="https://rafaelgonzalezalbes.com/" />
|
||||
<meta property="og:title" content="Rafael González Albes | Senior Software Engineer" />
|
||||
<meta property="og:description" content="Specializing in scalable PHP (Symfony) backends, React frontends, and containerized architectures. View my professional timeline and tech stack." />
|
||||
<meta property="og:image" content="/android-chrome-512x512.png" />
|
||||
<meta property="profile:first_name" content="Rafael" />
|
||||
<meta property="profile:last_name" content="González Albes" />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:url" content="https://rafaelgonzalezalbes.com/" />
|
||||
<meta name="twitter:title" content="Rafael González Albes | Senior Software Engineer" />
|
||||
<meta name="twitter:description" content="Specializing in scalable PHP (Symfony) backends, React frontends, and containerized architectures. View my professional timeline and tech stack." />
|
||||
<meta name="twitter:image" content="/android-chrome-512x512.png" />
|
||||
</head>
|
||||
<body>
|
||||
<!--
|
||||
Hey developer!
|
||||
If you're reading this, send an email to rafaelgonzalezalbes@gmail.com
|
||||
with the subject "Under the Hood" and I'll send you a virtual cookie 🍪
|
||||
-->
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
1764
package-lock.json
generated
Normal file
37
package.json
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"name": "app",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "oxlint",
|
||||
"preview": "vite preview",
|
||||
"deploy": "npm run build && node check-qa.cjs && node --env-file=.env deploy.js",
|
||||
"deploy:force": "npm run build && node --env-file=.env deploy.js",
|
||||
"d": "npm run deploy",
|
||||
"df": "npm run deploy:force",
|
||||
"watch-deploy": "node --env-file=.env watch-deploy.js",
|
||||
"test:ui": "playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"i18next": "^26.3.6",
|
||||
"i18next-browser-languagedetector": "^8.2.1",
|
||||
"lucide-react": "^0.469.0",
|
||||
"react": "^19.2.7",
|
||||
"react-dom": "^19.2.7",
|
||||
"react-i18next": "^17.0.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.13.2",
|
||||
"@types/react": "^19.2.17",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^6.0.3",
|
||||
"oxlint": "^1.71.0",
|
||||
"ssh2-sftp-client": "^9.1.0",
|
||||
"typescript": "~6.0.2",
|
||||
"vite": "^8.1.1",
|
||||
"@playwright/test": "1.49.0"
|
||||
}
|
||||
}
|
||||
31
playwright.config.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
testDir: './tests',
|
||||
timeout: 30000,
|
||||
fullyParallel: true,
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
reporter: [['html', { open: 'never' }]],
|
||||
use: {
|
||||
baseURL: process.env.BASE_URL || 'https://rafaelgonzalezalbes.com',
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'on',
|
||||
video: 'retain-on-failure',
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'desktop-chrome',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
{
|
||||
name: 'tablet-ipad',
|
||||
use: { ...devices['iPad Mini'] },
|
||||
},
|
||||
{
|
||||
name: 'mobile-iphone',
|
||||
use: { ...devices['iPhone 14 Pro'] },
|
||||
},
|
||||
],
|
||||
});
|
||||
21
public/.htaccess
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
RewriteRule ^index\.html$ - [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-l
|
||||
RewriteRule . /index.html [L]
|
||||
</IfModule>
|
||||
|
||||
# Never cache HTML files - always fetch fresh
|
||||
<FilesMatch "\.(html|htm)$">
|
||||
Header set Cache-Control "no-cache, no-store, must-revalidate"
|
||||
Header set Pragma "no-cache"
|
||||
Header set Expires 0
|
||||
</FilesMatch>
|
||||
|
||||
# Cache hashed JS/CSS assets for 1 year (they change filename on each build)
|
||||
<FilesMatch "\.(js|css)$">
|
||||
Header set Cache-Control "public, max-age=31536000, immutable"
|
||||
</FilesMatch>
|
||||
BIN
public/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
public/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 234 KiB |
BIN
public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
public/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 840 B |
BIN
public/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
52
public/favicon.svg
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="769.000000pt" height="713.000000pt" viewBox="0 0 769.000000 713.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
|
||||
<g transform="translate(0.000000,713.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M0 3565 l0 -3565 3845 0 3845 0 0 3565 0 3565 -3845 0 -3845 0 0
|
||||
-3565z m3792 3089 c244 -34 460 -114 628 -232 140 -100 295 -281 373 -437 38
|
||||
-77 83 -211 108 -326 20 -90 23 -132 24 -314 0 -231 -11 -310 -67 -479 -113
|
||||
-344 -378 -617 -722 -743 -27 -10 -51 -23 -53 -29 -2 -7 43 -83 102 -169 58
|
||||
-86 105 -163 105 -170 0 -7 -17 -44 -38 -82 -21 -37 -63 -113 -92 -168 -29
|
||||
-55 -87 -163 -129 -240 -42 -77 -81 -149 -86 -160 -6 -11 -17 -20 -24 -20 -8
|
||||
0 -39 41 -71 90 -31 50 -130 204 -218 343 -89 140 -162 256 -162 258 0 3 -22
|
||||
37 -48 77 -65 99 -72 117 -72 202 0 175 57 324 171 449 80 88 126 118 339 219
|
||||
162 77 254 180 319 352 17 46 41 194 41 255 0 136 -45 305 -107 403 -62 99
|
||||
-197 208 -294 238 -127 40 -146 41 -626 46 l-473 5 -1 -384 c-1 -210 -5 -475
|
||||
-8 -588 l-6 -205 -55 -8 c-181 -28 -351 -72 -511 -133 -64 -24 -121 -44 -128
|
||||
-44 -8 0 -11 260 -11 1005 l0 1005 840 0 c708 0 857 -2 952 -16z m1504 -1711
|
||||
c31 -65 284 -575 562 -1133 277 -558 564 -1136 637 -1285 73 -148 257 -520
|
||||
409 -825 152 -305 274 -560 272 -567 -4 -11 -81 -13 -393 -11 l-388 3 -45 90
|
||||
c-25 50 -56 115 -69 145 -14 30 -37 82 -52 115 -15 33 -57 128 -94 210 -37 83
|
||||
-81 180 -98 218 l-30 67 -259 0 c-223 0 -259 2 -264 15 -4 9 1 62 10 118 17
|
||||
109 36 333 36 435 l0 62 95 0 c58 0 95 4 95 10 0 9 -143 331 -217 490 -14 30
|
||||
-45 100 -68 155 -123 286 -217 501 -225 509 -4 4 -34 -45 -65 -110 -128 -268
|
||||
-222 -460 -318 -654 l-102 -205 -352 -3 c-260 -2 -353 1 -353 9 0 10 46 101
|
||||
130 254 15 28 49 91 75 140 26 50 64 122 85 160 21 39 57 106 80 150 23 44 50
|
||||
94 60 110 24 41 74 135 171 317 45 84 95 178 111 208 60 109 97 177 163 305
|
||||
85 162 142 271 165 310 10 17 48 89 85 160 82 158 74 145 85 145 4 0 34 -53
|
||||
66 -117z m-1998 -282 c62 -5 115 -10 117 -13 3 -3 -35 -45 -83 -94 -149 -151
|
||||
-216 -279 -266 -506 -6 -28 -7 -28 -90 -28 -83 0 -228 -18 -321 -40 -234 -56
|
||||
-525 -211 -689 -368 -66 -64 -187 -210 -232 -280 -37 -56 -146 -278 -157 -317
|
||||
-3 -11 -17 -60 -32 -110 -71 -242 -74 -560 -9 -815 126 -492 503 -899 989
|
||||
-1068 200 -70 469 -103 672 -83 265 27 419 72 661 194 238 120 474 363 604
|
||||
621 51 101 90 204 83 217 -4 5 -351 9 -881 9 -552 0 -874 4 -872 10 3 8 108
|
||||
130 411 477 l108 123 1006 0 1006 0 -6 -172 c-3 -95 -10 -204 -17 -243 -21
|
||||
-126 -63 -298 -93 -380 -16 -44 -33 -95 -38 -113 -17 -57 -164 -337 -220 -419
|
||||
-158 -234 -363 -436 -597 -592 -131 -88 -296 -175 -415 -221 -190 -74 -343
|
||||
-114 -582 -152 -121 -19 -503 -16 -640 6 -232 36 -447 97 -613 173 -41 18 -75
|
||||
33 -77 33 -2 0 -52 28 -112 61 -533 302 -921 800 -1068 1369 -78 303 -87 659
|
||||
-26 978 90 465 369 928 736 1223 69 56 242 179 250 179 3 0 28 15 57 34 188
|
||||
119 513 236 788 283 175 29 435 39 648 24z m-583 -894 c4 -13 5 -163 3 -334
|
||||
l-3 -310 -25 -31 c-14 -16 -87 -99 -164 -184 -76 -84 -222 -246 -324 -360
|
||||
-101 -115 -189 -208 -193 -208 -5 0 -9 228 -9 506 l0 506 63 65 c133 139 338
|
||||
273 507 332 76 26 110 36 132 40 4 0 10 -9 13 -22z m1519 -1996 c11 -18 -80
|
||||
-138 -184 -241 -202 -202 -452 -329 -736 -374 -107 -17 -224 -21 -224 -8 0 4
|
||||
32 65 70 136 39 70 70 129 70 131 0 1 22 42 48 91 27 49 70 131 97 182 l48 92
|
||||
403 0 c226 0 405 -4 408 -9z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
BIN
public/flags/de.png
Normal file
|
After Width: | Height: | Size: 99 B |
BIN
public/flags/es.png
Normal file
|
After Width: | Height: | Size: 381 B |
BIN
public/flags/gb.png
Normal file
|
After Width: | Height: | Size: 288 B |
BIN
public/flags/it.png
Normal file
|
After Width: | Height: | Size: 112 B |
24
public/icons.svg
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<symbol id="bluesky-icon" viewBox="0 0 16 17">
|
||||
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
|
||||
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
|
||||
</symbol>
|
||||
<symbol id="discord-icon" viewBox="0 0 20 19">
|
||||
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
|
||||
</symbol>
|
||||
<symbol id="documentation-icon" viewBox="0 0 21 20">
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
|
||||
</symbol>
|
||||
<symbol id="github-icon" viewBox="0 0 19 19">
|
||||
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
|
||||
</symbol>
|
||||
<symbol id="social-icon" viewBox="0 0 20 20">
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
|
||||
</symbol>
|
||||
<symbol id="x-icon" viewBox="0 0 19 19">
|
||||
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
4
public/robots.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://rafaelgonzalezalbes.com/sitemap.xml
|
||||
9
public/sitemap.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://rafaelgonzalezalbes.com/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
369
src/App.css
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
/* Gallery Portal Styling */
|
||||
.gallery-container {
|
||||
min-height: 100vh;
|
||||
background-color: #0b0f19;
|
||||
color: #f8fafc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 40px 20px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gallery-orb {
|
||||
position: fixed;
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
filter: blur(100px);
|
||||
}
|
||||
|
||||
.orb-1 {
|
||||
top: -10%;
|
||||
left: -10%;
|
||||
background: radial-gradient(circle, rgba(57, 211, 83, 0.05) 0%, transparent 60%);
|
||||
}
|
||||
|
||||
.orb-2 {
|
||||
bottom: -10%;
|
||||
right: -10%;
|
||||
background: radial-gradient(circle, rgba(88, 166, 255, 0.05) 0%, transparent 60%);
|
||||
}
|
||||
|
||||
.gallery-header {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto 60px auto;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.gallery-logo {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.gallery-langs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.gallery-lang-btn {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
padding: 5px 7px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.gallery-lang-btn:hover, .gallery-lang-btn.active {
|
||||
border-color: #58a6ff;
|
||||
background: rgba(88, 166, 255, 0.1);
|
||||
opacity: 1;
|
||||
box-shadow: 0 0 8px rgba(88, 166, 255, 0.3);
|
||||
}
|
||||
|
||||
.gallery-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.gallery-title {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 3rem;
|
||||
font-weight: 800;
|
||||
margin-bottom: 12px;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #94a3b8 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.gallery-subtitle {
|
||||
color: #94a3b8;
|
||||
font-size: 1.1rem;
|
||||
max-width: 600px;
|
||||
margin: 0 auto 50px auto;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.gallery-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.gallery-card {
|
||||
cursor: pointer;
|
||||
padding: 30px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
background: rgba(13, 17, 23, 0.4);
|
||||
}
|
||||
|
||||
.gallery-card:hover {
|
||||
transform: translateY(-6px);
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.circadian-card {
|
||||
border: 1.5px solid rgba(234, 179, 8, 0.2);
|
||||
}
|
||||
|
||||
.gallery-card:hover.circadian-card {
|
||||
box-shadow: 0 15px 40px rgba(234, 179, 8, 0.15);
|
||||
border-color: rgba(234, 179, 8, 0.4);
|
||||
}
|
||||
|
||||
.gallery-card:hover:nth-child(2) {
|
||||
box-shadow: 0 15px 40px rgba(57, 211, 83, 0.12);
|
||||
border-color: rgba(57, 211, 83, 0.25);
|
||||
}
|
||||
|
||||
.gallery-card:hover:nth-child(3) {
|
||||
box-shadow: 0 15px 40px rgba(88, 166, 255, 0.12);
|
||||
border-color: rgba(88, 166, 255, 0.25);
|
||||
}
|
||||
|
||||
.gallery-card:hover:nth-child(4) {
|
||||
box-shadow: 0 15px 40px rgba(192, 132, 252, 0.12);
|
||||
border-color: rgba(192, 132, 252, 0.25);
|
||||
}
|
||||
|
||||
.gallery-card:hover:nth-child(5) {
|
||||
box-shadow: 0 15px 40px rgba(197, 168, 128, 0.12);
|
||||
border-color: rgba(197, 168, 128, 0.25);
|
||||
}
|
||||
|
||||
.gallery-card:hover:nth-child(6) {
|
||||
box-shadow: 0 15px 40px rgba(255, 0, 127, 0.12);
|
||||
border-color: rgba(255, 0, 127, 0.25);
|
||||
}
|
||||
|
||||
.gallery-card:hover:nth-child(7) {
|
||||
box-shadow: 0 15px 40px rgba(250, 204, 21, 0.12);
|
||||
border-color: rgba(250, 204, 21, 0.25);
|
||||
}
|
||||
|
||||
.gallery-card:hover:nth-child(8) {
|
||||
box-shadow: 0 15px 40px rgba(100, 116, 139, 0.12);
|
||||
border-color: rgba(100, 116, 139, 0.25);
|
||||
}
|
||||
|
||||
.card-icon-circle {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.gallery-card:hover .card-icon-circle {
|
||||
transform: scale(1.08) rotate(4deg);
|
||||
}
|
||||
|
||||
.gold-glow {
|
||||
background: rgba(234, 179, 8, 0.05);
|
||||
border-color: rgba(234, 179, 8, 0.2);
|
||||
}
|
||||
|
||||
.green-glow {
|
||||
background: rgba(57, 211, 83, 0.05);
|
||||
border-color: rgba(57, 211, 83, 0.2);
|
||||
}
|
||||
|
||||
.blue-glow {
|
||||
background: rgba(88, 166, 255, 0.05);
|
||||
border-color: rgba(88, 166, 255, 0.2);
|
||||
}
|
||||
|
||||
.purple-glow {
|
||||
background: rgba(192, 132, 252, 0.05);
|
||||
border-color: rgba(192, 132, 252, 0.2);
|
||||
}
|
||||
|
||||
.cream-glow {
|
||||
background: rgba(197, 168, 128, 0.05);
|
||||
border-color: rgba(197, 168, 128, 0.2);
|
||||
}
|
||||
|
||||
.pink-glow {
|
||||
background: rgba(255, 0, 127, 0.05);
|
||||
border-color: rgba(255, 0, 127, 0.2);
|
||||
}
|
||||
|
||||
.yellow-glow {
|
||||
background: rgba(250, 204, 21, 0.05);
|
||||
border-color: rgba(250, 204, 21, 0.2);
|
||||
}
|
||||
|
||||
.slate-glow {
|
||||
background: rgba(100, 116, 139, 0.05);
|
||||
border-color: rgba(100, 116, 139, 0.2);
|
||||
}
|
||||
|
||||
.color-gold { color: #eab308; }
|
||||
.color-green { color: #39d353; }
|
||||
.color-blue { color: #58a6ff; }
|
||||
.color-purple { color: #c084fc; }
|
||||
.color-cream { color: #c5a880; }
|
||||
.color-pink { color: #ff007f; }
|
||||
.color-yellow-card { color: #facc15; }
|
||||
.color-slate { color: #64748b; }
|
||||
|
||||
.gallery-card h3 {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.gallery-card p {
|
||||
color: #94a3b8;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 24px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.gallery-select-btn {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 6px;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-heading);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
padding: 8px 20px;
|
||||
width: 100%;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.active-btn {
|
||||
background: linear-gradient(135deg, #eab308 0%, #ca8a04 100%);
|
||||
border-color: #eab308;
|
||||
color: #0b0f19;
|
||||
}
|
||||
|
||||
.gallery-card:hover .gallery-select-btn {
|
||||
background: #ffffff;
|
||||
color: #0b0f19;
|
||||
border-color: #ffffff;
|
||||
}
|
||||
|
||||
.gallery-card:hover .gallery-select-btn.active-btn {
|
||||
background: #facc15;
|
||||
color: #0b0f19;
|
||||
border-color: #facc15;
|
||||
box-shadow: 0 0 10px rgba(234, 179, 8, 0.4);
|
||||
}
|
||||
|
||||
/* Floating Action Button for theme switching */
|
||||
.theme-fab {
|
||||
position: fixed;
|
||||
bottom: 24px;
|
||||
right: 24px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background: rgba(22, 27, 34, 0.7);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
color: #f8fafc;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
|
||||
z-index: 99999;
|
||||
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.theme-fab:hover {
|
||||
transform: scale(1.1) rotate(15deg);
|
||||
background: #ffffff;
|
||||
color: #0b0f19;
|
||||
box-shadow: 0 0 20px rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 968px) {
|
||||
.gallery-grid {
|
||||
grid-template-columns: 1fr;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.gallery-title {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Easter Eggs Styling */
|
||||
body.barrel-roll {
|
||||
animation: barrelRoll 2s ease-in-out;
|
||||
}
|
||||
@keyframes barrelRoll {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.gallery-logo.spin-logo {
|
||||
animation: logoSpin 1s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
@keyframes logoSpin {
|
||||
0% { transform: rotate(0deg) scale(1); }
|
||||
50% { transform: rotate(180deg) scale(1.1); }
|
||||
100% { transform: rotate(360deg) scale(1); }
|
||||
}
|
||||
|
||||
.distortion-banner {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background: rgba(234, 179, 8, 0.95);
|
||||
color: #0b0f19;
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 700;
|
||||
font-size: 0.95rem;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||
z-index: 100000;
|
||||
animation: slideIn 0.3s ease-out;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
@keyframes slideIn {
|
||||
from { transform: translateY(-100%); }
|
||||
to { transform: translateY(0); }
|
||||
}
|
||||
|
||||
419
src/App.tsx
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
import { useState, useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Palette, Terminal as TerminalIcon, Github as GithubIcon, Layers, Sparkles, BookOpen, Zap, HelpCircle, Eye } from 'lucide-react';
|
||||
import GithubTemplate from './templates/GithubTemplate';
|
||||
import TerminalTemplate from './templates/TerminalTemplate';
|
||||
import BentoTemplate from './templates/BentoTemplate';
|
||||
import EditorialTemplate from './templates/EditorialTemplate';
|
||||
import CyberpunkTemplate from './templates/CyberpunkTemplate';
|
||||
import BrutalistTemplate from './templates/BrutalistTemplate';
|
||||
import NeumorphicTemplate from './templates/NeumorphicTemplate';
|
||||
import AuroraTemplate from './templates/AuroraTemplate';
|
||||
import ThreeDTemplate from './templates/ThreeDTemplate';
|
||||
import './App.css';
|
||||
|
||||
const flagMap: Record<string, string> = { en: 'gb', es: 'es', it: 'it', de: 'de' };
|
||||
const FlagImg = ({ code, size = 22 }: { code: string; size?: number }) => {
|
||||
const country = flagMap[code] || code;
|
||||
return (
|
||||
<img
|
||||
src={`/flags/${country}.png`}
|
||||
width={size}
|
||||
alt={code.toUpperCase()}
|
||||
style={{ display: 'inline-block', verticalAlign: 'middle', borderRadius: '2px' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
function App() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const [template, setTemplate] = useState<string | null>(null);
|
||||
const [resolvedTemplate, setResolvedTemplate] = useState<string | null>(null);
|
||||
const logoClicksRef = useRef(0);
|
||||
const [logoSpin, setLogoSpin] = useState(false);
|
||||
const [timeDistortion, setTimeDistortion] = useState(false);
|
||||
|
||||
// Load template from local storage
|
||||
useEffect(() => {
|
||||
const savedTemplate = localStorage.getItem('selected_portfolio_template');
|
||||
if (savedTemplate) {
|
||||
setTemplate(savedTemplate);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleGoHome = () => selectTemplate(null);
|
||||
window.addEventListener('goHome', handleGoHome as EventListener);
|
||||
return () => window.removeEventListener('goHome', handleGoHome as EventListener);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (template === 'auto' || template === null) {
|
||||
const determineTemplate = () => {
|
||||
const hour = new Date().getHours();
|
||||
// 06:00 - 10:00 -> Warm Editorial (clean paper light mode)
|
||||
if (hour >= 6 && hour < 10) return 'editorial';
|
||||
// 10:00 - 14:00 -> Neumorphic (soft light tactile)
|
||||
if (hour >= 10 && hour < 14) return 'neumorphic';
|
||||
// 14:00 - 17:00 -> Brutalist (loud bold yellow)
|
||||
if (hour >= 14 && hour < 17) return 'brutalist';
|
||||
// 17:00 - 20:00 -> Bento Grid (modern frosted grid tiles)
|
||||
if (hour >= 17 && hour < 20) return 'bento';
|
||||
// 20:00 - 23:00 -> GitHub Cyber (developer dark mode)
|
||||
if (hour >= 20 && hour < 23) return 'github';
|
||||
// 23:00 - 02:00 -> Cyberpunk Neon (vaporwave nightlife)
|
||||
if (hour >= 23 || hour < 2) return 'cyberpunk';
|
||||
// 02:00 - 06:00 -> Terminal CLI (monochrome shell)
|
||||
return 'terminal';
|
||||
};
|
||||
setResolvedTemplate(determineTemplate());
|
||||
} else {
|
||||
setResolvedTemplate(template);
|
||||
}
|
||||
}, [template]);
|
||||
|
||||
// Konami Code Easter Egg
|
||||
useEffect(() => {
|
||||
const konamiCode = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'];
|
||||
let konamiIndex = 0;
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
const key = e.key.length === 1 ? e.key.toLowerCase() : e.key;
|
||||
const expectedKey = konamiCode[konamiIndex].length === 1 ? konamiCode[konamiIndex].toLowerCase() : konamiCode[konamiIndex];
|
||||
|
||||
if (key === expectedKey) {
|
||||
konamiIndex++;
|
||||
if (konamiIndex === konamiCode.length) {
|
||||
konamiIndex = 0;
|
||||
triggerBarrelRoll();
|
||||
}
|
||||
} else {
|
||||
konamiIndex = 0;
|
||||
}
|
||||
};
|
||||
|
||||
const triggerBarrelRoll = () => {
|
||||
document.body.classList.add('barrel-roll');
|
||||
setTimeout(() => {
|
||||
document.body.classList.remove('barrel-roll');
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, []);
|
||||
|
||||
// Global Decryption Scramble Easter Egg
|
||||
useEffect(() => {
|
||||
const triggerGlobalScramble = () => {
|
||||
const textNodes: { node: Node; original: string }[] = [];
|
||||
|
||||
const findTextNodes = (node: Node) => {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
const text = node.nodeValue || '';
|
||||
if (text.trim().length > 0) {
|
||||
textNodes.push({ node, original: text });
|
||||
}
|
||||
} else {
|
||||
const name = node.nodeName.toLowerCase();
|
||||
if (name === 'script' || name === 'style' || name === 'svg' || name === 'canvas' || name === 'button') {
|
||||
return;
|
||||
}
|
||||
for (let child = node.firstChild; child; child = child.nextSibling) {
|
||||
findTextNodes(child);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
findTextNodes(document.body);
|
||||
|
||||
const chars = '01ABCDEFGHIJKLMNOPQRSTUVWXYZ@#$%&*+-/<>[]{}';
|
||||
|
||||
textNodes.forEach(({ node, original }) => {
|
||||
let iterations = 0;
|
||||
const totalLen = original.length;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
node.nodeValue = original
|
||||
.split('')
|
||||
.map((char, index) => {
|
||||
if (char === ' ' || char === '\n' || char === '\r') return char;
|
||||
if (index < iterations) {
|
||||
return original[index];
|
||||
}
|
||||
return chars[Math.floor(Math.random() * chars.length)];
|
||||
})
|
||||
.join('');
|
||||
|
||||
if (iterations >= totalLen) {
|
||||
node.nodeValue = original;
|
||||
clearInterval(interval);
|
||||
}
|
||||
|
||||
iterations += Math.ceil(totalLen / 15);
|
||||
}, 40);
|
||||
});
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if (
|
||||
target && (
|
||||
target.tagName === 'INPUT' ||
|
||||
target.tagName === 'TEXTAREA' ||
|
||||
target.isContentEditable
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.key.toLowerCase() === 'c') {
|
||||
triggerGlobalScramble();
|
||||
}
|
||||
};
|
||||
|
||||
const handleScrambleEvent = () => {
|
||||
triggerGlobalScramble();
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
window.addEventListener('triggerScramble', handleScrambleEvent);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
window.removeEventListener('triggerScramble', handleScrambleEvent);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleLogoClick = () => {
|
||||
logoClicksRef.current += 1;
|
||||
if (logoClicksRef.current >= 5) {
|
||||
setLogoSpin(true);
|
||||
setTimeout(() => setLogoSpin(false), 1000);
|
||||
logoClicksRef.current = 0;
|
||||
}
|
||||
};
|
||||
|
||||
const handleCircadianDoubleClick = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
if (timeDistortion) return;
|
||||
|
||||
setTimeDistortion(true);
|
||||
const templates = ['editorial', 'neumorphic', 'brutalist', 'bento', 'github', 'cyberpunk', 'terminal'];
|
||||
let index = 0;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
if (index < templates.length) {
|
||||
setTemplate(templates[index]);
|
||||
index++;
|
||||
} else {
|
||||
clearInterval(interval);
|
||||
setTemplate(null); // return home
|
||||
setTimeDistortion(false);
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const selectTemplate = (name: string | null) => {
|
||||
setTemplate(name);
|
||||
if (name) {
|
||||
localStorage.setItem('selected_portfolio_template', name);
|
||||
} else {
|
||||
localStorage.removeItem('selected_portfolio_template');
|
||||
}
|
||||
};
|
||||
|
||||
const currentLang = i18n.language ? i18n.language.substring(0, 2) : 'en';
|
||||
|
||||
const langs = [
|
||||
{ code: 'en' },
|
||||
{ code: 'es' },
|
||||
{ code: 'it' },
|
||||
{ code: 'de' }
|
||||
];
|
||||
|
||||
if (template !== null) {
|
||||
let ActiveLayoutComponent = GithubTemplate;
|
||||
if (resolvedTemplate === 'terminal') ActiveLayoutComponent = TerminalTemplate;
|
||||
if (resolvedTemplate === 'bento') ActiveLayoutComponent = BentoTemplate;
|
||||
if (resolvedTemplate === 'editorial') ActiveLayoutComponent = EditorialTemplate;
|
||||
if (resolvedTemplate === 'cyberpunk') ActiveLayoutComponent = CyberpunkTemplate;
|
||||
if (resolvedTemplate === 'brutalist') ActiveLayoutComponent = BrutalistTemplate;
|
||||
if (resolvedTemplate === 'neumorphic') ActiveLayoutComponent = NeumorphicTemplate;
|
||||
if (resolvedTemplate === 'aurora') ActiveLayoutComponent = AuroraTemplate;
|
||||
if (resolvedTemplate === 'threed') ActiveLayoutComponent = ThreeDTemplate;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ActiveLayoutComponent />
|
||||
{timeDistortion && (
|
||||
<div className="distortion-banner">
|
||||
<span>⚡ {t('Time Distortion Active: Cycling layouts...')}</span>
|
||||
</div>
|
||||
)}
|
||||
<button onClick={() => selectTemplate(null)} className="theme-fab" title={t('Switch Theme')} aria-label="Switch theme">
|
||||
<Palette size={20} />
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// Design Gallery Portal
|
||||
return (
|
||||
<div className="gallery-container">
|
||||
{/* Background orbs */}
|
||||
<div className="gallery-orb orb-1"></div>
|
||||
<div className="gallery-orb orb-2"></div>
|
||||
|
||||
<header className="gallery-header">
|
||||
<div className={`gallery-logo ${logoSpin ? 'spin-logo' : ''}`} onClick={handleLogoClick} style={{ cursor: 'pointer' }}>
|
||||
<span>rga://<span className="text-gradient">portfolio</span></span>
|
||||
</div>
|
||||
<div className="gallery-langs">
|
||||
{langs.map((lang) => (
|
||||
<button
|
||||
key={lang.code}
|
||||
onClick={() => i18n.changeLanguage(lang.code)}
|
||||
className={`gallery-lang-btn ${currentLang === lang.code ? 'active' : ''}`}
|
||||
title={lang.code.toUpperCase()}
|
||||
>
|
||||
<FlagImg code={lang.code} size={22} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="gallery-content">
|
||||
<h1 className="gallery-title">{t('Design Gallery')}</h1>
|
||||
<p className="gallery-subtitle">
|
||||
{t('Choose a template to experience the portfolio. You can switch back at any time.')}
|
||||
</p>
|
||||
|
||||
<div className="gallery-grid">
|
||||
{/* Circadian Mode Card */}
|
||||
<div className="gallery-card glass-card circadian-card" onClick={() => selectTemplate('auto')} onDoubleClick={handleCircadianDoubleClick}>
|
||||
<div className="card-icon-circle gold-glow">
|
||||
<Sparkles size={32} className="color-gold" />
|
||||
</div>
|
||||
<h3>{t('Circadian Cycle (Auto)')}</h3>
|
||||
<p>
|
||||
{t('Cycles layouts automatically based on the time of day. Daylight is clean editorial/soft neumorphism, afternoons are brutalist, evenings are dark developer mode, late nights are terminal command lines.')}
|
||||
</p>
|
||||
<button className="gallery-select-btn active-btn">{t('Choose')}</button>
|
||||
</div>
|
||||
|
||||
{/* GitHub Card */}
|
||||
<div className="gallery-card glass-card" onClick={() => selectTemplate('github')}>
|
||||
<div className="card-icon-circle green-glow">
|
||||
<GithubIcon size={32} className="color-green" />
|
||||
</div>
|
||||
<h3>{t('GitHub Theme')}</h3>
|
||||
<p>
|
||||
{t("Sleek developer profile inspired by GitHub's dark mode, green commits activity accents, and clean layout.")}
|
||||
</p>
|
||||
<button className="gallery-select-btn">{t('Choose')}</button>
|
||||
</div>
|
||||
|
||||
{/* Interactive 3D Card */}
|
||||
<div className="gallery-card glass-card" onClick={() => selectTemplate('threed')}>
|
||||
<div className="card-icon-circle blue-glow" style={{ background: 'rgba(56,189,248,0.08)', borderColor: 'rgba(56,189,248,0.25)' }}>
|
||||
<Layers size={32} style={{ color: '#38bdf8' }} />
|
||||
</div>
|
||||
<h3>🌌 {t('Interactive 3D')}</h3>
|
||||
<p>
|
||||
{t('An immersive layout featuring mouse-tracking 3D tilt cards, parallax effects, and a custom canvas-rendered 3D rotating starfield constellation.')}
|
||||
</p>
|
||||
<button className="gallery-select-btn">{t('Choose')}</button>
|
||||
</div>
|
||||
|
||||
{/* Terminal Card */}
|
||||
<div className="gallery-card glass-card" onClick={() => selectTemplate('terminal')}>
|
||||
<div className="card-icon-circle blue-glow">
|
||||
<TerminalIcon size={32} className="color-blue" />
|
||||
</div>
|
||||
<h3>{t('Interactive CLI')}</h3>
|
||||
<p>
|
||||
{t('Interactive command-line shell interface. Fast, retro-tech styled, with quick commands selector panel.')}
|
||||
</p>
|
||||
<button className="gallery-select-btn">{t('Choose')}</button>
|
||||
</div>
|
||||
|
||||
{/* Bento Card */}
|
||||
<div className="gallery-card glass-card" onClick={() => selectTemplate('bento')}>
|
||||
<div className="card-icon-circle purple-glow">
|
||||
<Layers size={32} className="color-purple" />
|
||||
</div>
|
||||
<h3>{t('Bento Grid')}</h3>
|
||||
<p>
|
||||
{t('A modern layout grouping technical categories and timelines as frosted glass tiles with neon purple shadows.')}
|
||||
</p>
|
||||
<button className="gallery-select-btn">{t('Choose')}</button>
|
||||
</div>
|
||||
|
||||
{/* Editorial Card */}
|
||||
<div className="gallery-card glass-card" onClick={() => selectTemplate('editorial')}>
|
||||
<div className="card-icon-circle cream-glow">
|
||||
<BookOpen size={32} className="color-cream" />
|
||||
</div>
|
||||
<h3>{t('Warm Editorial')}</h3>
|
||||
<p>
|
||||
{t('Clean newspaper and blog style with high typography contrast, warm cream background, and spacious borders.')}
|
||||
</p>
|
||||
<button className="gallery-select-btn">{t('Choose')}</button>
|
||||
</div>
|
||||
|
||||
{/* Cyberpunk Card */}
|
||||
<div className="gallery-card glass-card" onClick={() => selectTemplate('cyberpunk')}>
|
||||
<div className="card-icon-circle pink-glow">
|
||||
<Zap size={32} className="color-pink" />
|
||||
</div>
|
||||
<h3>{t('Cyberpunk Neon')}</h3>
|
||||
<p>
|
||||
{t('Vibrant vaporwave style with fluorescent borders, glowing shadows, grid backgrounds, and tech scanner overlay.')}
|
||||
</p>
|
||||
<button className="gallery-select-btn">{t('Choose')}</button>
|
||||
</div>
|
||||
|
||||
{/* Brutalist Card */}
|
||||
<div className="gallery-card glass-card" onClick={() => selectTemplate('brutalist')}>
|
||||
<div className="card-icon-circle yellow-glow">
|
||||
<HelpCircle size={32} className="color-yellow-card" />
|
||||
</div>
|
||||
<h3>{t('Neo Brutalist')}</h3>
|
||||
<p>
|
||||
{t('High contrast flat design. Bright warning yellow panels, thick black border boxes, and solid shadows.')}
|
||||
</p>
|
||||
<button className="gallery-select-btn">{t('Choose')}</button>
|
||||
</div>
|
||||
|
||||
{/* Neumorphic Card */}
|
||||
<div className="gallery-card glass-card" onClick={() => selectTemplate('neumorphic')}>
|
||||
<div className="card-icon-circle slate-glow">
|
||||
<Eye size={32} className="color-slate" />
|
||||
</div>
|
||||
<h3>{t('Soft Neumorphism')}</h3>
|
||||
<p>
|
||||
{t('Soft tactile layout using smooth beveled cards, extruded shadows, and recessed active states.')}
|
||||
</p>
|
||||
<button className="gallery-select-btn">{t('Choose')}</button>
|
||||
</div>
|
||||
|
||||
{/* Aurora Card */}
|
||||
<div className="gallery-card glass-card" onClick={() => selectTemplate('aurora')}>
|
||||
<div className="card-icon-circle purple-glow" style={{ background: 'rgba(124,58,237,0.08)', borderColor: 'rgba(124,58,237,0.25)' }}>
|
||||
<Sparkles size={32} style={{ color: '#a78bfa' }} />
|
||||
</div>
|
||||
<h3>✨ {t('Aurora Glass')}</h3>
|
||||
<p>
|
||||
{t('Vivid aurora gradient backgrounds with glassmorphic cards, animated glows, and a modern linear layout.')}
|
||||
</p>
|
||||
<button className="gallery-select-btn">{t('Choose')}</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
BIN
src/assets/hero.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
1
src/assets/react.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 4 KiB |
1
src/assets/vite.svg
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
153
src/components/About.module.css
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
.about {
|
||||
background-color: var(--bg-darker);
|
||||
}
|
||||
|
||||
.subheading {
|
||||
font-size: 1.75rem;
|
||||
margin-bottom: 20px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.paragraph {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.7;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.paragraph strong {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.statsGrid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20px;
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.statCard {
|
||||
text-align: center;
|
||||
padding: 24px 16px !important;
|
||||
}
|
||||
|
||||
.statValue {
|
||||
font-size: 2.25rem;
|
||||
font-weight: 800;
|
||||
font-family: var(--font-heading);
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.statLabel {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.detailsCol {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 28px !important;
|
||||
}
|
||||
|
||||
.cardHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.cardHeader h4 {
|
||||
font-size: 1.25rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.langList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.langItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.langInfo {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.langName {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.langLevel {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.progressBarBg {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
background: var(--bg-dark);
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progressBarFill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--primary), var(--secondary));
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 8px var(--primary-glow);
|
||||
}
|
||||
|
||||
.certList {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.certItem {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.certArrow {
|
||||
color: var(--primary);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.certTitle {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.certIssuer {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
@media (max-width: 968px) {
|
||||
.subheading {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
106
src/components/About.tsx
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import React from 'react';
|
||||
import { Award, Globe, ChevronRight } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './About.module.css';
|
||||
import { getAboutBio } from '../data/portfolioData';
|
||||
|
||||
export const About: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const stats = [
|
||||
{ value: '4.5+', label: t('Years Experience') },
|
||||
{ value: '5', label: t('Languages Spoken') },
|
||||
{ value: '4+', label: t('Academic Qualifications') },
|
||||
{ value: '5+', label: t('Personal Projects') }
|
||||
];
|
||||
|
||||
const languages = [
|
||||
{ name: t('Spanish'), level: t('Native / Bilingual'), percentage: 100 },
|
||||
{ name: t('English'), level: t('Professional (B1 Certified)'), percentage: 70 },
|
||||
{ name: t('Italian'), level: t('Conversational'), percentage: 65 },
|
||||
{ name: t('German'), level: t('Basic / Learning'), percentage: 40 },
|
||||
{ name: t('French'), level: t('Basic / Learning'), percentage: 35 }
|
||||
];
|
||||
|
||||
const certifications = [
|
||||
{ title: t('Oxford Test of English (B1)'), issuer: t('Oxford University Press') },
|
||||
{ title: t('C++ Certification'), issuer: t('Programming Institute') },
|
||||
{ title: t('Java Certification'), issuer: t('Oracle Certified Associate') },
|
||||
{ title: t('SQL Developer Certification'), issuer: t('Database Systems') }
|
||||
];
|
||||
|
||||
// Removed formatText utility as we use <Trans> instead
|
||||
|
||||
return (
|
||||
<section id="about" className={`${styles.about} section`}>
|
||||
<div className="container">
|
||||
<h2 className="section-title">{t('About Me')}</h2>
|
||||
|
||||
<div className="grid-2">
|
||||
<div className={styles.introCol}>
|
||||
<h3 className={styles.subheading}>{t('My Journey as a Developer')}</h3>
|
||||
{getAboutBio(t, styles.paragraph)}
|
||||
<p className={styles.paragraph}>
|
||||
{t("I enjoy solving complex architectural challenges, designing robust APIs, containerizing deployment environments, and exploring deep learning models. Multi-lingual and highly adaptable, I am comfortable collaborating across international, cross-functional teams.")}
|
||||
</p>
|
||||
|
||||
<div className={styles.statsGrid}>
|
||||
{stats.map((stat, i) => (
|
||||
<div key={i} className={`${styles.statCard} glass-card`}>
|
||||
<span className={`${styles.statValue} text-gradient`}>{stat.value}</span>
|
||||
<span className={styles.statLabel}>{stat.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.detailsCol}>
|
||||
{/* Spoken Languages */}
|
||||
<div className={`${styles.card} glass-card`}>
|
||||
<div className={styles.cardHeader}>
|
||||
<Globe className={styles.icon} size={22} />
|
||||
<h4>{t('Spoken Languages')}</h4>
|
||||
</div>
|
||||
<div className={styles.langList}>
|
||||
{languages.map((lang, idx) => (
|
||||
<div key={idx} className={styles.langItem}>
|
||||
<div className={styles.langInfo}>
|
||||
<span className={styles.langName}>{lang.name}</span>
|
||||
<span className={styles.langLevel}>{lang.level}</span>
|
||||
</div>
|
||||
<div className={styles.progressBarBg}>
|
||||
<div
|
||||
className={styles.progressBarFill}
|
||||
style={{ width: `${lang.percentage}%` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Certifications */}
|
||||
<div className={`${styles.card} glass-card`}>
|
||||
<div className={styles.cardHeader}>
|
||||
<Award className={styles.icon} size={22} />
|
||||
<h4>{t('Certifications')}</h4>
|
||||
</div>
|
||||
<ul className={styles.certList}>
|
||||
{certifications.map((cert, idx) => (
|
||||
<li key={idx} className={styles.certItem}>
|
||||
<ChevronRight size={16} className={styles.certArrow} />
|
||||
<div>
|
||||
<span className={styles.certTitle}>{cert.title}</span>
|
||||
<span className={styles.certIssuer}>{cert.issuer}</span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
export default About;
|
||||
236
src/components/Contact.module.css
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
.contact {
|
||||
background-color: var(--bg-deep);
|
||||
}
|
||||
|
||||
.layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 30px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.detailsCard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
padding: 40px !important;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.detailsCard h3 {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 16px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.detailsDesc {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.detailsList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.detailItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.iconCircle {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
background: var(--primary-glow);
|
||||
border: 1px solid rgba(0, 242, 254, 0.2);
|
||||
color: var(--primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.detailLabel {
|
||||
display: block;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.detailValue {
|
||||
font-size: 1.05rem;
|
||||
color: var(--text-primary);
|
||||
font-weight: 600;
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
a.detailValue:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.notes {
|
||||
border-left: 3px solid var(--accent);
|
||||
padding-left: 16px;
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-secondary);
|
||||
margin-top: auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.notesHighlight {
|
||||
color: var(--accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.formCol {
|
||||
padding: 40px !important;
|
||||
}
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.inputGroup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.inputGroup label {
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.input, .textarea {
|
||||
background: var(--bg-darker);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 12px 16px;
|
||||
font-family: var(--font-body);
|
||||
font-size: 0.95rem;
|
||||
transition: var(--transition-fast);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.input:focus, .textarea:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 10px var(--primary-glow);
|
||||
}
|
||||
|
||||
.inputError {
|
||||
border-color: #ef4444 !important;
|
||||
}
|
||||
|
||||
.errorText {
|
||||
color: #ef4444;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.submitBtn {
|
||||
justify-content: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.successState {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.successIconCircle {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
background: rgba(39, 201, 63, 0.1);
|
||||
border: 2px solid #27c93f;
|
||||
color: #27c93f;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 0 20px rgba(39, 201, 63, 0.2);
|
||||
}
|
||||
|
||||
.successState h3 {
|
||||
font-size: 1.6rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.successState p {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.socialsSection {
|
||||
margin-top: 30px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
padding-top: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.socialsSection h4 {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.socialLinks {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.socialLink {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: var(--bg-darker);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 10px 16px;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
transition: var(--transition-fast);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.socialLink:hover {
|
||||
border-color: var(--primary);
|
||||
color: var(--primary);
|
||||
box-shadow: 0 0 10px var(--primary-glow);
|
||||
}
|
||||
186
src/components/Contact.tsx
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Mail, Send, Check, Loader2, Github, Linkedin } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './Contact.module.css';
|
||||
|
||||
export const Contact: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const [formData, setFormData] = useState({ name: '', email: '', message: '' });
|
||||
const [errors, setErrors] = useState<{ [key: string]: string }>({});
|
||||
const [status, setStatus] = useState<'idle' | 'submitting' | 'success' | 'error'>('idle');
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||
if (errors[name]) {
|
||||
setErrors((prev) => ({ ...prev, [name]: '' }));
|
||||
}
|
||||
};
|
||||
|
||||
const validate = () => {
|
||||
const newErrors: { [key: string]: string } = {};
|
||||
if (!formData.name.trim()) newErrors.name = t('Name is required');
|
||||
if (!formData.email.trim()) {
|
||||
newErrors.email = t('Email is required');
|
||||
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
|
||||
newErrors.email = t('Invalid email address');
|
||||
}
|
||||
if (!formData.message.trim()) newErrors.message = t('Message cannot be empty');
|
||||
return newErrors;
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const validationErrors = validate();
|
||||
if (Object.keys(validationErrors).length > 0) {
|
||||
setErrors(validationErrors);
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus('submitting');
|
||||
try {
|
||||
// Simulate form submission
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||
setStatus('success');
|
||||
setFormData({ name: '', email: '', message: '' });
|
||||
} catch {
|
||||
setStatus('error');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section id="contact" className={`${styles.contact} section-padding`}>
|
||||
<div className={`${styles.container} container`}>
|
||||
<div className={styles.sectionHeader}>
|
||||
<span className="subtitle-gradient">{t('Get in Touch')}</span>
|
||||
<h2>{t('Contact Me')}</h2>
|
||||
<div className="section-divider"></div>
|
||||
</div>
|
||||
|
||||
<div className={styles.layout}>
|
||||
{/* Contact Details */}
|
||||
<div className={`${styles.detailsCard} glass-card`}>
|
||||
<h3>{t("Let's build something together")}</h3>
|
||||
<p className={styles.detailsDesc}>
|
||||
{t("I'm always open to discussing new projects, creative ideas, or opportunities. Feel free to reach out via email or send me a message!")}
|
||||
</p>
|
||||
|
||||
<div className={styles.detailsList}>
|
||||
<div className={styles.detailItem}>
|
||||
<div className={styles.iconCircle}>
|
||||
<Mail size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<span className={styles.detailLabel}>{t('Email')}</span>
|
||||
<a href="mailto:rafaelgonzalezalbes@gmail.com" className={styles.detailValue}>
|
||||
rafaelgonzalezalbes@gmail.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.socialsSection}>
|
||||
<h4>{t('Social Profiles')}</h4>
|
||||
<div className={styles.socialLinks}>
|
||||
<a href="https://github.com/anik0z" target="_blank" rel="noopener noreferrer" className={styles.socialLink} aria-label="GitHub">
|
||||
<Github size={18} />
|
||||
<span>GitHub</span>
|
||||
</a>
|
||||
<a href="https://linkedin.com/rafaelgonzalezalbes" target="_blank" rel="noopener noreferrer" className={styles.socialLink} aria-label="LinkedIn">
|
||||
<Linkedin size={18} />
|
||||
<span>LinkedIn</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.notes}>
|
||||
<span className={styles.notesHighlight}>{t('Quick Note:')}</span>
|
||||
{t(" I speak Spanish, English, Italian, and basic German, making collaboration in multi-lingual teams very smooth!")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Form Card */}
|
||||
<div className={`${styles.formCol} glass-card`}>
|
||||
{status === 'success' ? (
|
||||
<div className={styles.successState}>
|
||||
<div className={styles.successIconCircle}>
|
||||
<Check size={40} />
|
||||
</div>
|
||||
<h3>{t('Message Sent Successfully!')}</h3>
|
||||
<p>{t("Thank you for reaching out. I'll get back to you shortly.")}</p>
|
||||
<button onClick={() => setStatus('idle')} className="btn btn-primary">
|
||||
{t('Send Another Message')}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className={styles.form}>
|
||||
<div className={styles.inputGroup}>
|
||||
<label htmlFor="name">{t('Name')}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
className={`${styles.input} ${errors.name ? styles.inputError : ''}`}
|
||||
placeholder={t('Your Name')}
|
||||
disabled={status === 'submitting'}
|
||||
/>
|
||||
{errors.name && <span className={styles.errorText}>{errors.name}</span>}
|
||||
</div>
|
||||
|
||||
<div className={styles.inputGroup}>
|
||||
<label htmlFor="email">{t('Email')}</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
className={`${styles.input} ${errors.email ? styles.inputError : ''}`}
|
||||
placeholder="rafaelgonzalezalbes@gmail.com"
|
||||
disabled={status === 'submitting'}
|
||||
/>
|
||||
{errors.email && <span className={styles.errorText}>{errors.email}</span>}
|
||||
</div>
|
||||
|
||||
<div className={styles.inputGroup}>
|
||||
<label htmlFor="message">{t('Message')}</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
rows={5}
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
className={`${styles.textarea} ${errors.message ? styles.inputError : ''}`}
|
||||
placeholder={t('Type your message here...')}
|
||||
disabled={status === 'submitting'}
|
||||
></textarea>
|
||||
{errors.message && <span className={styles.errorText}>{errors.message}</span>}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className={`btn btn-primary ${styles.submitBtn}`}
|
||||
disabled={status === 'submitting'}
|
||||
>
|
||||
{status === 'submitting' ? (
|
||||
<>
|
||||
<Loader2 size={18} className={styles.spinner} /> {t('Sending...')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{t('Send Message')} <Send size={18} />
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
export default Contact;
|
||||
|
||||
158
src/components/Experience.module.css
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
.experience {
|
||||
background-color: var(--bg-darker);
|
||||
}
|
||||
|
||||
.tabContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.tabBtn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 12px 28px;
|
||||
border-radius: 100px;
|
||||
color: var(--text-secondary);
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: var(--transition-smooth);
|
||||
}
|
||||
|
||||
.tabBtn:hover {
|
||||
border-color: var(--primary);
|
||||
color: var(--primary);
|
||||
background: var(--primary-glow);
|
||||
}
|
||||
|
||||
.tabBtn.active {
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
color: #080c14;
|
||||
border-color: transparent;
|
||||
box-shadow: 0 4px 15px var(--primary-glow);
|
||||
}
|
||||
|
||||
.timeline {
|
||||
position: relative;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.timelineLine {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 31px;
|
||||
width: 2px;
|
||||
background: linear-gradient(to bottom, var(--primary), var(--accent));
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.timelineItem {
|
||||
position: relative;
|
||||
margin-bottom: 40px;
|
||||
padding-left: 80px;
|
||||
animation: fade-in 0.5s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.timelineNode {
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
top: 20px;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 50%;
|
||||
background: var(--bg-darker);
|
||||
border: 2px solid var(--primary);
|
||||
color: var(--primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 0 10px var(--primary-glow);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.timelineContent {
|
||||
padding: 28px !important;
|
||||
}
|
||||
|
||||
.metaRow {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.dateBadge, .locationBadge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.dateBadge {
|
||||
color: var(--primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.roleTitle {
|
||||
font-size: 1.4rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 6px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.companyName {
|
||||
font-size: 1.05rem;
|
||||
font-weight: 500;
|
||||
color: var(--secondary);
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.detailsList {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.detailItem {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.checkIcon {
|
||||
color: var(--primary);
|
||||
flex-shrink: 0;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.timelineLine {
|
||||
left: 21px;
|
||||
}
|
||||
.timelineNode {
|
||||
left: 5px;
|
||||
}
|
||||
.timelineItem {
|
||||
padding-left: 50px;
|
||||
}
|
||||
.timelineContent {
|
||||
padding: 20px !important;
|
||||
}
|
||||
}
|
||||
84
src/components/Experience.tsx
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Briefcase, GraduationCap, Calendar, MapPin, CheckCircle2 } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './Experience.module.css';
|
||||
|
||||
import { getWorkItems, getEducationItems } from '../data/portfolioData';
|
||||
|
||||
export const Experience: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const [activeTab, setActiveTab] = useState<'work' | 'education'>('work');
|
||||
|
||||
const workItems = getWorkItems(t);
|
||||
const educationItems = getEducationItems(t);
|
||||
|
||||
const currentItems = activeTab === 'work' ? workItems : educationItems;
|
||||
|
||||
return (
|
||||
<section id="experience" className={`${styles.experience} section`}>
|
||||
<div className="container">
|
||||
<h2 className="section-title">{t('My Timeline')}</h2>
|
||||
|
||||
{/* Tab Switcher */}
|
||||
<div className={styles.tabContainer}>
|
||||
<button
|
||||
className={`${styles.tabBtn} ${activeTab === 'work' ? styles.active : ''}`}
|
||||
onClick={() => setActiveTab('work')}
|
||||
>
|
||||
<Briefcase size={18} />
|
||||
<span>{t('Work Experience')}</span>
|
||||
</button>
|
||||
<button
|
||||
className={`${styles.tabBtn} ${activeTab === 'education' ? styles.active : ''}`}
|
||||
onClick={() => setActiveTab('education')}
|
||||
>
|
||||
<GraduationCap size={18} />
|
||||
<span>{t('Education')}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Timeline Grid */}
|
||||
<div className={styles.timeline}>
|
||||
<div className={styles.timelineLine}></div>
|
||||
|
||||
{currentItems.map((item, idx) => (
|
||||
<div key={idx} className={styles.timelineItem}>
|
||||
{/* Node indicator */}
|
||||
<div className={styles.timelineNode}>
|
||||
{activeTab === 'work' ? <Briefcase size={16} /> : <GraduationCap size={16} />}
|
||||
</div>
|
||||
|
||||
{/* Content Panel */}
|
||||
<div className={`${styles.timelineContent} glass-card`}>
|
||||
<div className={styles.metaRow}>
|
||||
<span className={styles.dateBadge}>
|
||||
<Calendar size={14} />
|
||||
{item.date}
|
||||
</span>
|
||||
<span className={styles.locationBadge}>
|
||||
<MapPin size={14} />
|
||||
{item.location}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h3 className={styles.roleTitle}>{item.title}</h3>
|
||||
<h4 className={styles.companyName}>{item.company}</h4>
|
||||
|
||||
<ul className={styles.detailsList}>
|
||||
{item.details.map((detail, dIdx) => (
|
||||
<li key={dIdx} className={styles.detailItem}>
|
||||
<CheckCircle2 size={16} className={styles.checkIcon} />
|
||||
<span>{detail}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
export default Experience;
|
||||
|
||||
22
src/components/FlagImg.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import React from 'react';
|
||||
|
||||
const flagMap: Record<string, string> = { en: 'gb', es: 'es', it: 'it', de: 'de' };
|
||||
|
||||
interface FlagImgProps {
|
||||
code: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
const FlagImg: React.FC<FlagImgProps> = ({ code, size = 20 }) => {
|
||||
const country = flagMap[code] || code;
|
||||
return (
|
||||
<img
|
||||
src={`/flags/${country}.png`}
|
||||
width={size}
|
||||
alt={code.toUpperCase()}
|
||||
style={{ display: 'inline-block', verticalAlign: 'middle', borderRadius: '2px' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default FlagImg;
|
||||
93
src/components/Footer.module.css
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
.footer {
|
||||
background: var(--bg-darker);
|
||||
border-top: 1px solid var(--border-color);
|
||||
padding: 40px 0;
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.socials {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.socials a {
|
||||
color: var(--text-secondary);
|
||||
transition: var(--transition-fast);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.socials a:hover {
|
||||
color: var(--primary);
|
||||
border-color: var(--primary);
|
||||
background: var(--primary-glow);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.scrollTop {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-secondary);
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.scrollTop:hover {
|
||||
color: var(--primary);
|
||||
border-color: var(--primary);
|
||||
background: var(--primary-glow);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
.right {
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
47
src/components/Footer.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import React from 'react';
|
||||
import { Github, Linkedin, Mail, ArrowUp } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './Footer.module.css';
|
||||
|
||||
export const Footer: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
return (
|
||||
<footer className={styles.footer}>
|
||||
<div className={`${styles.container} container`}>
|
||||
<div className={styles.left}>
|
||||
<p className={styles.copyright}>
|
||||
{t('© {{year}} Rafael Gonzalez Albes. All rights reserved.', { year: new Date().getFullYear() })}
|
||||
</p>
|
||||
<p className={styles.subtitle}>
|
||||
{t('Built with React, TypeScript & Docker.')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={styles.right}>
|
||||
<div className={styles.socials}>
|
||||
<a href="https://github.com/anik0z" target="_blank" rel="noopener noreferrer" aria-label="GitHub">
|
||||
<Github size={20} />
|
||||
</a>
|
||||
<a href="https://linkedin.com/rafaelgonzalezalbes" target="_blank" rel="noopener noreferrer" aria-label="LinkedIn">
|
||||
<Linkedin size={20} />
|
||||
</a>
|
||||
<a href="mailto:rafaelgonzalezalbes@gmail.com" aria-label="Email">
|
||||
<Mail size={20} />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<button onClick={scrollToTop} className={styles.scrollTop} aria-label="Scroll to top">
|
||||
<ArrowUp size={20} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
export default Footer;
|
||||
|
||||
154
src/components/GithubCalendar.module.css
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
.calendarWrapper {
|
||||
margin-top: 40px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.titleInfo h4 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.scrollContainer {
|
||||
overflow-x: auto;
|
||||
padding-bottom: 10px;
|
||||
width: 100%;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--bg-dark) transparent;
|
||||
}
|
||||
|
||||
.scrollContainer::-webkit-scrollbar {
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.scrollContainer::-webkit-scrollbar-thumb {
|
||||
background: var(--bg-dark);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.calendarGrid {
|
||||
min-width: 780px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.monthsRow {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dayLabelSpacer {
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.monthsLabels {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-grow: 1;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.monthLabel {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
width: calc(100% / 12);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.daysAndGrid {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.dayLabels {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 98px; /* 7 cells * 12px + 6 gaps * 2px = 98px */
|
||||
padding: 4px 0;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.dayLabel {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
.weeksContainer {
|
||||
display: flex;
|
||||
gap: 3px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.weekColumn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.cell {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 2px;
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
|
||||
.cell:hover {
|
||||
transform: scale(1.2);
|
||||
outline: 1px solid var(--primary);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.level0 {
|
||||
background: #161b22;
|
||||
border: 1px solid rgba(255, 255, 255, 0.03);
|
||||
}
|
||||
|
||||
.level1 {
|
||||
background: #0e4429;
|
||||
}
|
||||
|
||||
.level2 {
|
||||
background: #006d32;
|
||||
}
|
||||
|
||||
.level3 {
|
||||
background: #26a641;
|
||||
}
|
||||
|
||||
.level4 {
|
||||
background: #39d353;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.legend .cell {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
109
src/components/GithubCalendar.tsx
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './GithubCalendar.module.css';
|
||||
|
||||
export const GithubCalendar: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Generate simulated activity data (53 weeks * 7 days)
|
||||
const calendarData = useMemo(() => {
|
||||
const data = [];
|
||||
let seed = 42;
|
||||
const random = () => {
|
||||
const x = Math.sin(seed++) * 10000;
|
||||
return x - Math.floor(x);
|
||||
};
|
||||
|
||||
for (let w = 0; w < 53; w++) {
|
||||
const week = [];
|
||||
for (let d = 0; d < 7; d++) {
|
||||
const r = random();
|
||||
let level = 0;
|
||||
if (r > 0.88) level = 4;
|
||||
else if (r > 0.7) level = 3;
|
||||
else if (r > 0.45) level = 2;
|
||||
else if (r > 0.2) level = 1;
|
||||
|
||||
// Add seasonality/empty periods for realism
|
||||
if (w > 12 && w < 15 && r > 0.25) level = 0;
|
||||
if (w > 36 && w < 39 && r > 0.3) level = 0;
|
||||
|
||||
week.push(level);
|
||||
}
|
||||
data.push(week);
|
||||
}
|
||||
return data;
|
||||
}, []);
|
||||
|
||||
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
|
||||
const totalContributions = useMemo(() => {
|
||||
return calendarData.reduce((acc, week) => {
|
||||
return acc + week.reduce((wAcc, level) => wAcc + (level * (level === 4 ? 8 : level === 3 ? 5 : level === 2 ? 3 : level === 1 ? 1 : 0)), 0);
|
||||
}, 0) + 942;
|
||||
}, [calendarData]);
|
||||
|
||||
return (
|
||||
<div className={`${styles.calendarWrapper} glass-card`}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.titleInfo}>
|
||||
<h4>{t('GitHub Activity')}</h4>
|
||||
<span className={styles.subtitle}>
|
||||
{totalContributions} {t('contributions in the last year')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.scrollContainer}>
|
||||
<div className={styles.calendarGrid}>
|
||||
{/* Months headers */}
|
||||
<div className={styles.monthsRow}>
|
||||
<div className={styles.dayLabelSpacer}></div>
|
||||
<div className={styles.monthsLabels}>
|
||||
{months.map((m, idx) => (
|
||||
<span key={idx} className={styles.monthLabel}>{t(m)}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.daysAndGrid}>
|
||||
{/* Day labels */}
|
||||
<div className={styles.dayLabels}>
|
||||
<span className={styles.dayLabel}>{t('Mon')}</span>
|
||||
<span className={styles.dayLabel}>{t('Wed')}</span>
|
||||
<span className={styles.dayLabel}>{t('Fri')}</span>
|
||||
</div>
|
||||
|
||||
{/* Grid */}
|
||||
<div className={styles.weeksContainer}>
|
||||
{calendarData.map((week, wIdx) => (
|
||||
<div key={wIdx} className={styles.weekColumn}>
|
||||
{week.map((level, dIdx) => (
|
||||
<div
|
||||
key={dIdx}
|
||||
className={`${styles.cell} ${styles[`level${level}`]}`}
|
||||
title={`${level === 0 ? t('No') : level === 4 ? t('Very high') : level === 3 ? t('High') : level === 2 ? t('Medium') : t('Low')} ${t('contributions')}`}
|
||||
></div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.footer}>
|
||||
<div className={styles.legend}>
|
||||
<span>{t('Less')}</span>
|
||||
<div className={`${styles.cell} ${styles.level0}`}></div>
|
||||
<div className={`${styles.cell} ${styles.level1}`}></div>
|
||||
<div className={`${styles.cell} ${styles.level2}`}></div>
|
||||
<div className={`${styles.cell} ${styles.level3}`}></div>
|
||||
<div className={`${styles.cell} ${styles.level4}`}></div>
|
||||
<span>{t('More')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default GithubCalendar;
|
||||
197
src/components/Hero.module.css
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
.hero {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
background-color: var(--bg-deep);
|
||||
overflow: hidden;
|
||||
padding-top: 100px;
|
||||
}
|
||||
|
||||
.gridBg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
|
||||
background-size: 50px 50px;
|
||||
background-position: center top;
|
||||
mask-image: radial-gradient(circle at center, black, transparent 80%);
|
||||
-webkit-mask-image: radial-gradient(circle at center, black, transparent 80%);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
z-index: 1;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: rgba(0, 242, 254, 0.06);
|
||||
border: 1px solid rgba(0, 242, 254, 0.2);
|
||||
padding: 6px 14px;
|
||||
border-radius: 100px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: var(--primary);
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.pulseDot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: var(--primary);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 8px var(--primary);
|
||||
animation: pulse-dot 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-dot {
|
||||
0% { transform: scale(0.9); opacity: 0.6; }
|
||||
50% { transform: scale(1.2); opacity: 1; }
|
||||
100% { transform: scale(0.9); opacity: 0.6; }
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 3.5rem;
|
||||
font-weight: 800;
|
||||
line-height: 1.15;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.typingContainer {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
font-family: var(--font-heading);
|
||||
margin-bottom: 24px;
|
||||
color: var(--text-primary);
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.prefix {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.typingText {
|
||||
color: var(--primary);
|
||||
text-shadow: 0 0 10px var(--primary-glow);
|
||||
}
|
||||
|
||||
.cursor {
|
||||
color: var(--primary);
|
||||
animation: blink 0.8s infinite;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%, 100% { opacity: 0; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 36px;
|
||||
max-width: 540px;
|
||||
}
|
||||
|
||||
.ctas {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.visual {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.codeCard {
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
padding: 0 !important;
|
||||
border-radius: var(--radius-md);
|
||||
overflow: hidden;
|
||||
font-family: 'Fira Code', 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
.codeHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: rgba(8, 12, 20, 0.4);
|
||||
padding: 12px 18px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.fileName {
|
||||
margin-left: auto;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
|
||||
.codeContent {
|
||||
padding: 24px;
|
||||
font-size: 0.85rem;
|
||||
color: #a5b4fc;
|
||||
overflow-x: auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.codeFooter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: rgba(8, 12, 20, 0.4);
|
||||
padding: 12px 18px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.runIcon {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.outputVal {
|
||||
color: #27c93f;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@media (max-width: 968px) {
|
||||
.hero {
|
||||
padding-top: 120px;
|
||||
height: auto;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.title {
|
||||
font-size: 2.8rem;
|
||||
}
|
||||
.typingContainer {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
.visual {
|
||||
margin-top: 40px;
|
||||
}
|
||||
}
|
||||
141
src/components/Hero.tsx
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { ArrowRight, Play, Download } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './Hero.module.css';
|
||||
|
||||
export const Hero: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const words = [
|
||||
t('Full-Stack Developer'),
|
||||
t('Backend Specialist'),
|
||||
t('API Designer'),
|
||||
t('System Thinker')
|
||||
];
|
||||
const [currentWordIndex, setCurrentWordIndex] = useState(0);
|
||||
const [displayedText, setDisplayedText] = useState('');
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [typingSpeed, setTypingSpeed] = useState(100);
|
||||
|
||||
useEffect(() => {
|
||||
const activeWord = words[currentWordIndex];
|
||||
if (!activeWord) return;
|
||||
|
||||
const handleTyping = () => {
|
||||
if (!isDeleting) {
|
||||
setDisplayedText(activeWord.substring(0, displayedText.length + 1));
|
||||
if (displayedText === activeWord) {
|
||||
setIsDeleting(true);
|
||||
setTypingSpeed(1500); // Wait before starting to delete
|
||||
} else {
|
||||
setTypingSpeed(80 + Math.random() * 40);
|
||||
}
|
||||
} else {
|
||||
setDisplayedText(activeWord.substring(0, displayedText.length - 1));
|
||||
setTypingSpeed(40);
|
||||
if (displayedText === '') {
|
||||
setIsDeleting(false);
|
||||
setCurrentWordIndex((prevIndex) => (prevIndex + 1) % words.length);
|
||||
setTypingSpeed(200);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const timer = setTimeout(handleTyping, typingSpeed);
|
||||
return () => clearTimeout(timer);
|
||||
}, [displayedText, isDeleting, currentWordIndex, typingSpeed, words]);
|
||||
|
||||
const scrollToSection = (id: string) => {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section id="hero" className={`${styles.hero} section`}>
|
||||
<div className={styles.gridBg}></div>
|
||||
<div className={`${styles.container} container grid-2`}>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.badge}>
|
||||
<span className={styles.pulseDot}></span>
|
||||
<span>{t('Available for new opportunities')}</span>
|
||||
</div>
|
||||
|
||||
<h1 className={styles.title}>
|
||||
{t('Building Scalable')} <br />
|
||||
<span className="text-gradient">{t('Backend Systems')}</span> & <br />
|
||||
{t('Seamless Interfaces')}
|
||||
</h1>
|
||||
|
||||
<div className={styles.typingContainer}>
|
||||
<span className={styles.prefix}>{t('I am a ')}</span>
|
||||
<span className={styles.typingText}>{displayedText}</span>
|
||||
<span className={styles.cursor}>|</span>
|
||||
</div>
|
||||
|
||||
<p className={styles.description}>
|
||||
{t('Professional Software Developer with 4.5+ years of experience building high-performance backend APIs, handling databases, and crafting clean frontend applications. Specialized in PHP (Symfony), TypeScript, and containerized Docker environments.')}
|
||||
</p>
|
||||
|
||||
<div className={styles.ctas}>
|
||||
<button onClick={() => scrollToSection('projects')} className="btn btn-primary">
|
||||
{t('Explore Projects')} <ArrowRight size={18} />
|
||||
</button>
|
||||
<a href="/Rafael_Gonzalez_Albes_CV.pdf" download className="btn btn-secondary" style={{ display: 'inline-flex', alignItems: 'center', gap: '8px', textDecoration: 'none' }}>
|
||||
<Download size={18} /> {t("Download CV")}
|
||||
</a>
|
||||
<button onClick={() => scrollToSection('contact')} className="btn btn-secondary" style={{ background: 'transparent', border: '1px solid var(--border)' }}>
|
||||
{t("Let's Talk")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.visual}>
|
||||
<div className={`${styles.codeCard} glass-card float`}>
|
||||
<div className={styles.codeHeader}>
|
||||
<span className={styles.dot} style={{ backgroundColor: '#ff5f56' }}></span>
|
||||
<span className={styles.dot} style={{ backgroundColor: '#ffbd2e' }}></span>
|
||||
<span className={styles.dot} style={{ backgroundColor: '#27c93f' }}></span>
|
||||
<span className={styles.fileName}>developer.ts</span>
|
||||
</div>
|
||||
<pre className={styles.codeContent}>
|
||||
<code>
|
||||
{`const developer = {
|
||||
name: 'Rafael Gonzalez Albes',
|
||||
role: '${t('Professional Software Developer')}',
|
||||
skills: [
|
||||
'PHP', 'Symfony',
|
||||
'TypeScript', 'React',
|
||||
'Docker', 'SQL', 'Neo4j'
|
||||
],
|
||||
languages: [
|
||||
'${t('Spanish')}', '${t('English')}',
|
||||
'${t('French')}', '${t('Italian')}', '${t('German')}'
|
||||
],
|
||||
passion: '${t('Writing clean, testable code')}',
|
||||
currentWork: 'CHECK24 Vergleichsportal GmbH'
|
||||
};
|
||||
|
||||
function runTest(dev) {
|
||||
return dev.skills.includes('Docker')
|
||||
? '${t('🚀 Local execution ready')}'
|
||||
: '${t('⚠️ Dependency missing')}';
|
||||
}
|
||||
|
||||
console.log(runTest(developer));`}
|
||||
</code>
|
||||
</pre>
|
||||
<div className={styles.codeFooter}>
|
||||
<Play size={14} className={styles.runIcon} />
|
||||
<span>{t('Run Output: ')}</span>
|
||||
<span className={styles.outputVal}>{t('🚀 Local execution ready')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
export default Hero;
|
||||
|
||||
235
src/components/Navbar.module.css
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
.header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
transition: var(--transition-smooth);
|
||||
background: transparent;
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
|
||||
.scrolled {
|
||||
background: rgba(8, 12, 20, 0.75);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 800;
|
||||
cursor: pointer;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.logoIcon {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.desktopNav {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.navLink {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-secondary);
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: var(--transition-fast);
|
||||
padding: 8px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.navLink:hover, .navLink.active {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.navLink::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 2px;
|
||||
background: var(--primary);
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.navLink:hover::after, .navLink.active::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mobileToggle {
|
||||
display: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mobileNav {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--bg-darker);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 20px 24px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.mobileNavLink {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-secondary);
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
padding: 8px 0;
|
||||
width: 100%;
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.mobileNavLink:hover, .mobileNavLink.active {
|
||||
color: var(--primary);
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.rightNavContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.langSelector {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.langBtn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: rgba(20, 30, 48, 0.45);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 6px 12px;
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
transition: var(--transition-fast);
|
||||
font-family: var(--font-heading);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.langBtn:hover {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 12px var(--primary-glow);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.arrow {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.arrowOpen {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.langDropdown {
|
||||
position: absolute;
|
||||
top: calc(100% + 8px);
|
||||
right: 0;
|
||||
background: rgba(8, 12, 20, 0.95);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-sm);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 110px;
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
padding: 4px 0;
|
||||
animation: fadeIn 0.15s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.langOption {
|
||||
background: transparent;
|
||||
border: none;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: flex-start;
|
||||
padding: 8px 16px;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
transition: var(--transition-fast);
|
||||
font-family: var(--font-heading);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.langOption:hover {
|
||||
color: var(--primary);
|
||||
background: rgba(0, 242, 254, 0.05);
|
||||
}
|
||||
|
||||
.langOptionActive {
|
||||
color: var(--primary);
|
||||
font-weight: 700;
|
||||
background: rgba(0, 242, 254, 0.1);
|
||||
}
|
||||
|
||||
.mobileActions {
|
||||
display: none;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.desktopNav {
|
||||
display: none;
|
||||
}
|
||||
.rightNavContainer {
|
||||
display: none;
|
||||
}
|
||||
.mobileToggle {
|
||||
display: block;
|
||||
}
|
||||
.mobileActions {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
194
src/components/Navbar.tsx
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Menu, X, Terminal, ChevronDown } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './Navbar.module.css';
|
||||
|
||||
// Map language codes to local flag image paths (bundled in public/flags/)
|
||||
const flagMap: Record<string, string> = { en: 'gb', es: 'es', it: 'it', de: 'de' };
|
||||
|
||||
const FlagImg = ({ code, size = 24 }: { code: string; size?: number }) => {
|
||||
const country = flagMap[code] || code;
|
||||
return (
|
||||
<img
|
||||
src={`/flags/${country}.png`}
|
||||
width={size}
|
||||
alt={code.toUpperCase()}
|
||||
style={{ display: 'inline-block', verticalAlign: 'middle', borderRadius: '2px' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
interface NavbarProps {
|
||||
activeSection: string;
|
||||
}
|
||||
|
||||
export const Navbar: React.FC<NavbarProps> = ({ activeSection }) => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const [isLangOpen, setIsLangOpen] = useState(false);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const mobileDropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setIsScrolled(window.scrollY > 20);
|
||||
};
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
|
||||
// Close language dropdown when clicking outside
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
dropdownRef.current &&
|
||||
!dropdownRef.current.contains(event.target as Node) &&
|
||||
mobileDropdownRef.current &&
|
||||
!mobileDropdownRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setIsLangOpen(false);
|
||||
}
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, []);
|
||||
|
||||
const navLinks = [
|
||||
{ id: 'hero', label: t('Home') },
|
||||
{ id: 'about', label: t('About') },
|
||||
{ id: 'skills', label: t('Skills') },
|
||||
{ id: 'experience', label: t('Experience') },
|
||||
{ id: 'projects', label: t('Projects') },
|
||||
{ id: 'contact', label: t('Contact') }
|
||||
];
|
||||
|
||||
const languages = [
|
||||
{ code: 'en' },
|
||||
{ code: 'es' },
|
||||
{ code: 'it' },
|
||||
{ code: 'de' }
|
||||
];
|
||||
|
||||
const handleLinkClick = (id: string) => {
|
||||
setIsOpen(false);
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
};
|
||||
|
||||
const changeLanguage = (code: string) => {
|
||||
i18n.changeLanguage(code);
|
||||
setIsLangOpen(false);
|
||||
};
|
||||
|
||||
const currentLang = i18n.language ? i18n.language.substring(0, 2) : 'en';
|
||||
|
||||
return (
|
||||
<header className={`${styles.header} ${isScrolled ? styles.scrolled : ''}`}>
|
||||
<div className={`${styles.container} container`}>
|
||||
<div className={styles.logo} onClick={() => window.dispatchEvent(new CustomEvent('goHome'))} style={{ cursor: 'pointer' }}>
|
||||
<Terminal size={24} className={styles.logoIcon} />
|
||||
<span>rga://<span className="text-gradient">dev</span></span>
|
||||
</div>
|
||||
|
||||
{/* Desktop Container */}
|
||||
<div className={styles.rightNavContainer}>
|
||||
{/* Desktop Nav */}
|
||||
<nav className={styles.desktopNav}>
|
||||
{navLinks.map((link) => (
|
||||
<button
|
||||
key={link.id}
|
||||
onClick={() => handleLinkClick(link.id)}
|
||||
className={`${styles.navLink} ${activeSection === link.id ? styles.active : ''}`}
|
||||
>
|
||||
{link.label}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* Language Switcher Desktop */}
|
||||
<div className={styles.langSelector} ref={dropdownRef}>
|
||||
<button
|
||||
className={styles.langBtn}
|
||||
onClick={() => setIsLangOpen(!isLangOpen)}
|
||||
aria-label="Select language"
|
||||
>
|
||||
<FlagImg code={currentLang} size={20} />
|
||||
<ChevronDown size={14} className={`${styles.arrow} ${isLangOpen ? styles.arrowOpen : ''}`} />
|
||||
</button>
|
||||
|
||||
{isLangOpen && (
|
||||
<div className={styles.langDropdown}>
|
||||
{languages.map((lang) => (
|
||||
<button
|
||||
key={lang.code}
|
||||
onClick={() => changeLanguage(lang.code)}
|
||||
className={`${styles.langOption} ${currentLang === lang.code ? styles.langOptionActive : ''}`}
|
||||
>
|
||||
<FlagImg code={lang.code} size={20} />
|
||||
<span>{lang.code.toUpperCase()}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Actions Header */}
|
||||
<div className={styles.mobileActions}>
|
||||
{/* Language Switcher Mobile */}
|
||||
<div className={styles.langSelector} ref={mobileDropdownRef}>
|
||||
<button
|
||||
className={styles.langBtn}
|
||||
onClick={() => setIsLangOpen(!isLangOpen)}
|
||||
aria-label="Select language"
|
||||
>
|
||||
<FlagImg code={currentLang} size={20} />
|
||||
<ChevronDown size={14} className={`${styles.arrow} ${isLangOpen ? styles.arrowOpen : ''}`} />
|
||||
</button>
|
||||
|
||||
{isLangOpen && (
|
||||
<div className={styles.langDropdown}>
|
||||
{languages.map((lang) => (
|
||||
<button
|
||||
key={lang.code}
|
||||
onClick={() => changeLanguage(lang.code)}
|
||||
className={`${styles.langOption} ${currentLang === lang.code ? styles.langOptionActive : ''}`}
|
||||
>
|
||||
<FlagImg code={lang.code} size={20} />
|
||||
<span>{lang.code.toUpperCase()}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Mobile Toggle */}
|
||||
<button className={styles.mobileToggle} onClick={() => setIsOpen(!isOpen)} aria-label="Toggle menu">
|
||||
{isOpen ? <X size={24} /> : <Menu size={24} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Nav Menu */}
|
||||
{isOpen && (
|
||||
<div className={styles.mobileNav}>
|
||||
{navLinks.map((link) => (
|
||||
<button
|
||||
key={link.id}
|
||||
onClick={() => handleLinkClick(link.id)}
|
||||
className={`${styles.mobileNavLink} ${activeSection === link.id ? styles.active : ''}`}
|
||||
>
|
||||
{link.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navbar;
|
||||
|
||||
290
src/components/Projects.module.css
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
.projects {
|
||||
background-color: var(--bg-deep);
|
||||
}
|
||||
|
||||
.intro {
|
||||
max-width: 700px;
|
||||
margin: -30px auto 40px auto;
|
||||
text-align: center;
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.filterBar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.filterBtn {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 10px 22px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-secondary);
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.filterBtn:hover {
|
||||
border-color: var(--primary);
|
||||
color: var(--primary);
|
||||
background: var(--primary-glow);
|
||||
}
|
||||
|
||||
.filterBtn.active {
|
||||
border-color: var(--primary);
|
||||
color: #080c14;
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
box-shadow: 0 4px 12px var(--primary-glow);
|
||||
}
|
||||
|
||||
.grid {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.projectCard {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.cardVisual {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.iconCircle {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: var(--primary-glow);
|
||||
border: 1px solid rgba(0, 242, 254, 0.2);
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.categoryBadge {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.projectTitle {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.projectDesc {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 20px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.techTags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.tag {
|
||||
background: var(--bg-darker);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.75rem;
|
||||
padding: 4px 10px;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
.tagMore {
|
||||
color: var(--primary);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.detailsBtn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--primary);
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
/* Modal Styling */
|
||||
.modalOverlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(8, 12, 20, 0.85);
|
||||
backdrop-filter: blur(8px);
|
||||
z-index: 2000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.modalContent {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
padding: 40px !important;
|
||||
background: var(--bg-darker) !important;
|
||||
}
|
||||
|
||||
.closeBtn {
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
right: 24px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.closeBtn:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.modalHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.modalIconCircle {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background: var(--primary-glow);
|
||||
border: 1px solid rgba(0, 242, 254, 0.2);
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.modalCategory {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
color: var(--primary);
|
||||
letter-spacing: 0.1em;
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.modalTitle {
|
||||
font-size: 1.8rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.modalBody {
|
||||
display: grid;
|
||||
grid-template-columns: 1.6fr 1fr;
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.descriptionSection h4, .metaSection h4, .archSection h4 {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.descriptionSection p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1rem;
|
||||
line-height: 1.7;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.archSection {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.archSection ul {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.archSection li {
|
||||
position: relative;
|
||||
padding-left: 20px;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.95rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.archSection li::before {
|
||||
content: '→';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.modalTechTags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.modalTag {
|
||||
background: var(--bg-dark);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
font-size: 0.8rem;
|
||||
padding: 6px 12px;
|
||||
border-radius: 100px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.modalLinks {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.modalBody {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 30px;
|
||||
}
|
||||
.modalContent {
|
||||
padding: 24px !important;
|
||||
}
|
||||
}
|
||||
187
src/components/Projects.tsx
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
import React, { useState } from 'react';
|
||||
import { ExternalLink, Github, X } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './Projects.module.css';
|
||||
|
||||
import { getProjects } from '../data/portfolioData';
|
||||
|
||||
interface Project {
|
||||
id: string;
|
||||
title: string;
|
||||
category: 'ai-data' | 'web-iot' | 'mobile';
|
||||
tech: string[];
|
||||
shortDesc: string;
|
||||
fullDesc: string;
|
||||
icon: any;
|
||||
githubUrl?: string;
|
||||
demoUrl?: string;
|
||||
architecture?: string[];
|
||||
}
|
||||
|
||||
export const Projects: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const [filter, setFilter] = useState<'all' | 'ai-data' | 'web-iot' | 'mobile'>('all');
|
||||
const [activeModalProject, setActiveModalProject] = useState<Project | null>(null);
|
||||
|
||||
const projects: Project[] = getProjects(t);
|
||||
|
||||
const getCategoryLabel = (category: string) => {
|
||||
switch (category) {
|
||||
case 'ai-data':
|
||||
return t('AI & Data');
|
||||
case 'web-iot':
|
||||
return t('Web & IoT');
|
||||
case 'mobile':
|
||||
return t('Mobile Apps');
|
||||
default:
|
||||
return category;
|
||||
}
|
||||
};
|
||||
|
||||
const filteredProjects = filter === 'all'
|
||||
? projects
|
||||
: projects.filter(p => p.category === filter);
|
||||
|
||||
return (
|
||||
<section id="projects" className={`${styles.projects} section`}>
|
||||
<div className="container">
|
||||
<h2 className="section-title">{t('Personal Projects')}</h2>
|
||||
|
||||
<p className={styles.intro}>
|
||||
{t('A curated selection of personal creations showing my growth across IoT, Deep Learning, Mobile development, and system designs.')}
|
||||
</p>
|
||||
|
||||
{/* Filter Navigation */}
|
||||
<div className={styles.filterBar}>
|
||||
<button
|
||||
className={`${styles.filterBtn} ${filter === 'all' ? styles.active : ''}`}
|
||||
onClick={() => setFilter('all')}
|
||||
>
|
||||
{t('All Projects')}
|
||||
</button>
|
||||
<button
|
||||
className={`${styles.filterBtn} ${filter === 'ai-data' ? styles.active : ''}`}
|
||||
onClick={() => setFilter('ai-data')}
|
||||
>
|
||||
{t('AI & Data')}
|
||||
</button>
|
||||
<button
|
||||
className={`${styles.filterBtn} ${filter === 'web-iot' ? styles.active : ''}`}
|
||||
onClick={() => setFilter('web-iot')}
|
||||
>
|
||||
{t('Web & IoT')}
|
||||
</button>
|
||||
<button
|
||||
className={`${styles.filterBtn} ${filter === 'mobile' ? styles.active : ''}`}
|
||||
onClick={() => setFilter('mobile')}
|
||||
>
|
||||
{t('Mobile Apps')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Project Grid */}
|
||||
<div className={`${styles.grid} grid-3`}>
|
||||
{filteredProjects.map((project) => (
|
||||
<div
|
||||
key={project.id}
|
||||
className={`${styles.projectCard} glass-card`}
|
||||
onClick={() => setActiveModalProject(project)}
|
||||
>
|
||||
<div className={styles.cardVisual}>
|
||||
<div className={styles.iconCircle}>
|
||||
<project.icon className={styles.projectIcon} size={28} />
|
||||
</div>
|
||||
<span className={styles.categoryBadge}>{getCategoryLabel(project.category).toUpperCase()}</span>
|
||||
</div>
|
||||
|
||||
<h3 className={styles.projectTitle}>{project.title}</h3>
|
||||
<p className={styles.projectDesc}>{project.shortDesc}</p>
|
||||
|
||||
<div className={styles.techTags}>
|
||||
{project.tech.slice(0, 3).map((t, idx) => (
|
||||
<span key={idx} className={styles.tag}>{t}</span>
|
||||
))}
|
||||
{project.tech.length > 3 && (
|
||||
<span className={styles.tagMore}>+{project.tech.length - 3} more</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button className={styles.detailsBtn}>
|
||||
{t('Learn More')} <ExternalLink size={14} />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Modal Overlay */}
|
||||
{activeModalProject && (
|
||||
<div className={styles.modalOverlay} onClick={() => setActiveModalProject(null)}>
|
||||
<div className={`${styles.modalContent} glass-card`} onClick={(e) => e.stopPropagation()}>
|
||||
<button
|
||||
className={styles.closeBtn}
|
||||
onClick={() => setActiveModalProject(null)}
|
||||
aria-label="Close modal"
|
||||
>
|
||||
<X size={24} />
|
||||
</button>
|
||||
|
||||
<div className={styles.modalHeader}>
|
||||
<div className={styles.modalIconCircle}>
|
||||
<activeModalProject.icon className={styles.projectIcon} size={28} />
|
||||
</div>
|
||||
<div>
|
||||
<span className={styles.modalCategory}>
|
||||
{getCategoryLabel(activeModalProject.category).toUpperCase()}
|
||||
</span>
|
||||
<h3 className={styles.modalTitle}>{activeModalProject.title}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.modalBody}>
|
||||
<div className={styles.descriptionSection}>
|
||||
<h4>{t('Overview')}</h4>
|
||||
<p>{activeModalProject.fullDesc}</p>
|
||||
|
||||
{activeModalProject.architecture && (
|
||||
<div className={styles.archSection}>
|
||||
<h4>{t('Technical Flow & Pattern')}</h4>
|
||||
<ul>
|
||||
{activeModalProject.architecture.map((item, idx) => (
|
||||
<li key={idx}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styles.metaSection}>
|
||||
<h4>{t('Tech Stack')}</h4>
|
||||
<div className={styles.modalTechTags}>
|
||||
{activeModalProject.tech.map((t, idx) => (
|
||||
<span key={idx} className={styles.modalTag}>{t}</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className={styles.modalLinks}>
|
||||
{activeModalProject.githubUrl && (
|
||||
<a
|
||||
href={activeModalProject.githubUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="btn btn-primary"
|
||||
>
|
||||
<Github size={18} /> {t('View on GitHub')}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
export default Projects;
|
||||
|
||||
88
src/components/Skills.module.css
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
.skills {
|
||||
background-color: var(--bg-deep);
|
||||
}
|
||||
|
||||
.intro {
|
||||
max-width: 700px;
|
||||
margin: -30px auto 50px auto;
|
||||
text-align: center;
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.grid {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.categoryCard {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.categoryHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.categoryHeader h3 {
|
||||
font-size: 1.35rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.skillList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.skillItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.skillInfo {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.skillName {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.skillLevel {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.meterBg {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
background: var(--bg-darker);
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255, 255, 255, 0.03);
|
||||
}
|
||||
|
||||
.meterFill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--primary), var(--secondary));
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 6px var(--primary-glow);
|
||||
transition: width 1s ease-in-out;
|
||||
}
|
||||
96
src/components/Skills.tsx
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import React from 'react';
|
||||
import { Database, Layout, Server } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './Skills.module.css';
|
||||
|
||||
interface Skill {
|
||||
name: string;
|
||||
level: string;
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
interface SkillCategory {
|
||||
title: string;
|
||||
icon: React.ReactNode;
|
||||
skills: Skill[];
|
||||
}
|
||||
|
||||
export const Skills: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const categories: SkillCategory[] = [
|
||||
{
|
||||
title: t('Backend & Systems'),
|
||||
icon: <Server size={22} className={styles.icon} />,
|
||||
skills: [
|
||||
{ name: t('PHP & Symfony'), level: t('Senior / Expert'), percentage: 95 },
|
||||
{ name: t('Python (Deep Learning)'), level: t('Advanced / Intermediate'), percentage: 75 },
|
||||
{ name: t('Java / OOP'), level: t('Advanced'), percentage: 80 },
|
||||
{ name: t('C / C++'), level: t('Intermediate'), percentage: 70 },
|
||||
{ name: t('Server Maintenance & Bash'), level: t('Advanced'), percentage: 80 }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('Frontend & UI'),
|
||||
icon: <Layout size={22} className={styles.icon} />,
|
||||
skills: [
|
||||
{ name: t('JavaScript / TypeScript'), level: t('Senior'), percentage: 90 },
|
||||
{ name: t('HTML5 & CSS3'), level: t('Expert'), percentage: 95 },
|
||||
{ name: t('JQuery & Bootstrap'), level: t('Expert'), percentage: 90 },
|
||||
{ name: t('React'), level: t('Advanced / Intermediate'), percentage: 75 }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('Databases & Technologies'),
|
||||
icon: <Database size={22} className={styles.icon} />,
|
||||
skills: [
|
||||
{ name: t('SQL (MySQL, PostgreSQL)'), level: t('Senior'), percentage: 90 },
|
||||
{ name: t('Neo4j (Graph DB)'), level: t('Advanced'), percentage: 80 },
|
||||
{ name: t('Docker / Containers'), level: t('Advanced'), percentage: 80 },
|
||||
{ name: t('IoT (Arduino, ESP8266)'), level: t('Advanced / Intermediate'), percentage: 75 }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section id="skills" className={`${styles.skills} section`}>
|
||||
<div className="container">
|
||||
<h2 className="section-title">{t('Technical Expertise')}</h2>
|
||||
|
||||
<p className={styles.intro}>
|
||||
{t('A robust technical foundation bridging low-level systems (C++), high-concurrency web systems (PHP, Node.js), and data analytics (Python, Neo4j).')}
|
||||
</p>
|
||||
|
||||
<div className={`${styles.grid} grid-3`}>
|
||||
{categories.map((cat, idx) => (
|
||||
<div key={idx} className={`${styles.categoryCard} glass-card`}>
|
||||
<div className={styles.categoryHeader}>
|
||||
{cat.icon}
|
||||
<h3>{cat.title}</h3>
|
||||
</div>
|
||||
|
||||
<div className={styles.skillList}>
|
||||
{cat.skills.map((skill, sIdx) => (
|
||||
<div key={sIdx} className={styles.skillItem}>
|
||||
<div className={styles.skillInfo}>
|
||||
<span className={styles.skillName}>{skill.name}</span>
|
||||
<span className={styles.skillLevel}>{skill.level}</span>
|
||||
</div>
|
||||
<div className={styles.meterBg}>
|
||||
<div
|
||||
className={styles.meterFill}
|
||||
style={{ width: `${skill.percentage}%` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
export default Skills;
|
||||
|
||||
4
src/custom.d.ts
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
declare module '*.po' {
|
||||
const content: Record<string, string>;
|
||||
export default content;
|
||||
}
|
||||
198
src/data/portfolioData.tsx
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
import { Database, Cpu, Smartphone, Globe, Bot } from 'lucide-react';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
export const getAboutBio = (_t: any, paragraphClass?: string) => (
|
||||
<>
|
||||
<p className={paragraphClass}>
|
||||
<Trans i18nKey="Proactive & Autonomous Software Developer specializing in robust backend architectures, system optimization, and full-stack integration. Driven by curiosity and a lifetime passion for computing.">
|
||||
Proactive & Autonomous Software Developer specializing in robust backend architectures, system optimization, and full-stack integration. Driven by curiosity and a lifetime passion for computing.
|
||||
</Trans>
|
||||
</p>
|
||||
<p className={paragraphClass}>
|
||||
<Trans i18nKey="A computer enthusiast since childhood with over <1>6 years of professional development experience</1> across Spain and Germany. Proven ability to adapt quickly through self-learning and autonomously solve complex architectural and engineering challenges without requiring permanent supervision. Adept at managing system administration, maintaining databases, and driving collaborative growth within agile team environments.">
|
||||
A computer enthusiast since childhood with over <strong>6 years of professional development experience</strong> across Spain and Germany. Proven ability to adapt quickly through self-learning and autonomously solve complex architectural and engineering challenges without requiring permanent supervision. Adept at managing system administration, maintaining databases, and driving collaborative growth within agile team environments.
|
||||
</Trans>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
|
||||
export const getWorkItems = (t: any) => [
|
||||
{
|
||||
date: '01/2022 – Present',
|
||||
title: t('Professional Software Developer'),
|
||||
company: t('Check24 (Germany)'),
|
||||
location: t('Augsburg, Bavaria, Germany'),
|
||||
details: [
|
||||
t('Architected and optimized high-performance backend microservices and APIs utilizing PHP (Symfony), Python, and Go for comparative portal engines serving millions of monthly active users.'),
|
||||
t('Executed proactive server and database administration to ensure maximum uptime, scalability, and robust data integration pipelines.'),
|
||||
t('Promoted from Junior Software Engineer to Professional Software Engineer in April 2024 in recognition of ownership of critical core systems.'),
|
||||
t('Established containerized local test suites using Docker, reducing onboarding time for new developers and standardizing testing environments.'),
|
||||
t('Participated in agile Scrum ceremonies, writing comprehensive API specifications, and ensuring system maintenance and high availability.')
|
||||
]
|
||||
},
|
||||
{
|
||||
date: '09/2020 – 08/2021',
|
||||
title: t('Full Stack Web Developer'),
|
||||
company: t('Mediaconcept GmbH (Germany)'),
|
||||
location: t('Ulm, Baden-Wurttemberg, Germany'),
|
||||
details: [
|
||||
t('Engineered interactive user interfaces using HTML5, CSS3, JavaScript, and JQuery, ensuring compliance with responsive web design principles.'),
|
||||
t('Created custom backend logic, hooks, and modules using PHP and the Symfony framework to support e-commerce and media tools.'),
|
||||
t('Implemented and customized enterprise content management systems (CMS) using Typo3, writing custom TypoScript extensions.'),
|
||||
t('Collaborated with product designers to map out system dependencies and architecture flows using formal UML diagrams.')
|
||||
]
|
||||
},
|
||||
{
|
||||
date: '02/2019 – 06/2021',
|
||||
title: t('Student Intern (Recommender Systems)'),
|
||||
company: t('University Pablo de Olavide'),
|
||||
location: t('Seville, Spain'),
|
||||
details: [
|
||||
t('Researched and implemented state-of-the-art recommendation systems based on collaborative filtering and fuzzy clustering models.'),
|
||||
t('Processed and analyzed large consumer transaction datasets using Python (Pandas, NumPy) to evaluate recommender accuracy.'),
|
||||
t('Designed and query-tuned graph-oriented database structures in Neo4j to model user-product buying patterns in real time.'),
|
||||
t('Built supporting web portal features in JavaScript, PHP, and Symfony for academic research evaluations.')
|
||||
]
|
||||
},
|
||||
{
|
||||
date: '03/2016 – 02/2019',
|
||||
title: t('Web Developer (JS, PHP, & Symfony)'),
|
||||
company: t('Green Slope S.L.'),
|
||||
location: t('Seville, Spain'),
|
||||
details: [
|
||||
t('Developed cross-platform mobile applications for iOS and Android using the Ionic Framework and Angular.'),
|
||||
t('Created automated enterprise scripts, notifications, and sheet integrations using Google Apps Script.'),
|
||||
t('Managed Linux production and staging servers, configuring Apache/Nginx web servers, security groups, and SSH configurations.'),
|
||||
t('Maintained legacy client websites, writing custom MySQL queries, and migrating servers with minimal downtime.')
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export const getEducationItems = (t: any) => [
|
||||
{
|
||||
date: '2017 – 2021',
|
||||
title: t('Degree in Computer Engineering in Information Systems'),
|
||||
company: t('University Pablo de Olavide'),
|
||||
location: t('Seville, Spain'),
|
||||
details: [
|
||||
t('Graduated with a thesis on Recommender Systems. Coursework: Advanced Algorithms, Database Management Systems, Software Engineering Patterns, Networks, and Operating Systems.')
|
||||
]
|
||||
},
|
||||
{
|
||||
date: '09/2019 – 06/2020',
|
||||
title: t("Master's Degree in Computer Science and Engineering"),
|
||||
company: t('University of Bologna (Erasmus)'),
|
||||
location: t('Bologna, Italy'),
|
||||
details: [
|
||||
t('Erasmus exchange year focusing on high-level systems. Coursework: Software Architectures, Distributed Systems, Advanced Web Technologies, Machine Learning, and Information Retrieval.')
|
||||
]
|
||||
},
|
||||
{
|
||||
date: '2016 – 2017',
|
||||
title: t('Certificate of Higher Education - Multiplatform App Development'),
|
||||
company: t('I.E.S Velázquez'),
|
||||
location: t('Seville, Spain'),
|
||||
details: [
|
||||
t('Focused on native Android development (Java), database design, cross-platform UI architectures, and project management methodologies.')
|
||||
]
|
||||
},
|
||||
{
|
||||
date: '2014 – 2016',
|
||||
title: t('Certificate of Higher Education - Web App Development'),
|
||||
company: t('Center Nuevas Profesiones'),
|
||||
location: t('Seville, Spain'),
|
||||
details: [
|
||||
t('Practical curriculum covering HTML5/CSS3/JavaScript, backend scripting in PHP, database engines (SQL), and deployment of web servers.')
|
||||
]
|
||||
},
|
||||
{
|
||||
date: '2012 – 2014',
|
||||
title: t('Vocational Training in Microcomputer Systems & Networks'),
|
||||
company: t('I.E.S Axati'),
|
||||
location: t('Seville, Spain'),
|
||||
details: [
|
||||
t('Hands-on foundation in computer networks, operating system configuration, PC hardware assembly, and network troubleshooting.')
|
||||
]
|
||||
},
|
||||
{
|
||||
date: '2020 – 2023',
|
||||
title: t('Professional Certifications'),
|
||||
company: t('Various Institutions'),
|
||||
location: t('Online & Academic'),
|
||||
details: [
|
||||
t('Oxford Test of English (Level B1)'),
|
||||
t('Technical Certifications: Advanced C++, Java Enterprise, and SQL Database Design.')
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export const getProjects = (t: any) => [
|
||||
{
|
||||
id: 'recommender',
|
||||
title: t('Supermarket Recommender System'),
|
||||
category: 'ai-data' as const,
|
||||
tech: ['Python', 'Neo4j Graph Database', 'Pandas', 'NumPy', 'Collaborative Filtering'],
|
||||
shortDesc: t('A hybrid recommendation engine utilizing fuzzy clustering and graph databases for market basket analysis.'),
|
||||
fullDesc: t('Developed a high-accuracy recommender system that analyzes consumer purchasing history and recommends items based on fuzzy clustering groups. Built a graph network in Neo4j to model user-to-product buy relationships, allowing fast collaborative filtering queries.'),
|
||||
icon: Database,
|
||||
githubUrl: 'https://github.com/anik0z',
|
||||
architecture: [
|
||||
t('Data extraction & cleaning via Pandas and NumPy'),
|
||||
t('Fuzzy C-Means Clustering to group similar purchasing baskets'),
|
||||
t('Graph DB mapping of nodes (Customers, Products, Categories) in Neo4j'),
|
||||
t('Real-time neighbor matching query logic via Cypher')
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'dumpster',
|
||||
title: t('IoT Smart Dumpster'),
|
||||
category: 'web-iot' as const,
|
||||
tech: ['Arduino (ESP8266)', 'Android App', 'PHP & MySQL', 'HTTP Encrypted API'],
|
||||
shortDesc: t('Intelligent trash container telemetry network using ESP8266, custom PHP backend, and Android monitoring.'),
|
||||
fullDesc: t('Designed a smart container system powered by ESP8266 nodeMCU v3 microcontrollers. It uses ultrasonic sensors to measure fill levels, communicates with a PHP API via encrypted HTTP POST (using MD5 signature verification), and reports real-time dashboard data to a companion Android app.'),
|
||||
icon: Cpu,
|
||||
githubUrl: 'https://github.com/anik0z',
|
||||
architecture: [
|
||||
t('ESP8266 telemetry node running on C++/Arduino sketch'),
|
||||
t('PHP Web API endpoint with MD5 request checksum validation'),
|
||||
t('Background Scheduler running task loops to identify full units'),
|
||||
t('Android Client using HTTP endpoints to fetch status maps')
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'adoption',
|
||||
title: t('Animal Adoption Mobile App'),
|
||||
category: 'mobile' as const,
|
||||
tech: ['Android SDK', 'Java / Kotlin', 'MySQL Backend', 'RESTful API'],
|
||||
shortDesc: t('A native mobile application connecting animal shelters with prospective adopters.'),
|
||||
fullDesc: t('A native Android mobile application that aggregates profiles of adoptable pets from local shelters. Allows users to filter by breed, age, location, and apply for adoption directly through the platform. Connects to a secure MySQL database for real-time applications handling.'),
|
||||
icon: Smartphone,
|
||||
githubUrl: 'https://github.com/anik0z'
|
||||
},
|
||||
{
|
||||
id: 'blog',
|
||||
title: t('Custom Blogging Platform'),
|
||||
category: 'web-iot' as const,
|
||||
tech: ['PHP', 'JavaScript / JQuery', 'MySQL', 'Bootstrap', 'HTML5 & CSS3'],
|
||||
shortDesc: t('A full-featured CMS web application for blogging and publishing article contents.'),
|
||||
fullDesc: t('A custom-built Content Management System (CMS) designed for authors. It features markdown editing, user authentication, profile customizer, dynamic commenting sections, and tags search. Powered by a classic LAMP/LEMP stack architecture.'),
|
||||
icon: Globe,
|
||||
githubUrl: 'https://github.com/anik0z'
|
||||
},
|
||||
{
|
||||
id: 'carla',
|
||||
title: t('Self-driving Car in CARLA'),
|
||||
category: 'ai-data' as const,
|
||||
tech: ['CARLA Simulator', 'Python', 'Keras / TensorFlow', 'Deep Q-Learning', 'Computer Vision'],
|
||||
shortDesc: t('Deep Reinforcement Learning script enabling autonomous vehicle lane tracking and driving.'),
|
||||
fullDesc: t('Programmed an autonomous driving agent inside the CARLA simulator. Utilized a deep reinforcement learning model (Keras/TensorFlow) mapping front-camera frames to steering and throttle actions. Trained the network using Deep Q-Learning (DQN) to maximize reward for staying in lanes and avoiding collisions.'),
|
||||
icon: Bot,
|
||||
githubUrl: 'https://github.com/anik0z',
|
||||
architecture: [
|
||||
t('Video streaming ingestion from CARLA camera sensor nodes'),
|
||||
t('Frame preprocessing & downscaling using OpenCV'),
|
||||
t('Deep Neural Network trained using Keras/TensorFlow framework'),
|
||||
t('DQN reward structure optimization based on forward velocity and lane alignment')
|
||||
]
|
||||
}
|
||||
];
|
||||
36
src/i18n.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
|
||||
// Import PO translation catalogs parsed by the Vite plugin
|
||||
import en from './locales/en.po';
|
||||
import es from './locales/es.po';
|
||||
import it from './locales/it.po';
|
||||
import de from './locales/de.po';
|
||||
|
||||
i18n
|
||||
// detect user language
|
||||
.use(LanguageDetector)
|
||||
// pass the i18n instance to react-i18next
|
||||
.use(initReactI18next)
|
||||
// init i18next
|
||||
.init({
|
||||
resources: {
|
||||
en: { translation: en },
|
||||
es: { translation: es },
|
||||
it: { translation: it },
|
||||
de: { translation: de }
|
||||
},
|
||||
fallbackLng: 'en',
|
||||
debug: false,
|
||||
interpolation: {
|
||||
escapeValue: false, // react already safes from xss
|
||||
},
|
||||
detection: {
|
||||
order: ['querystring', 'localStorage', 'navigator'],
|
||||
lookupQuerystring: 'lang',
|
||||
caches: ['localStorage']
|
||||
}
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
258
src/index.css
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
/* Core Design System & Global Styles */
|
||||
|
||||
:root {
|
||||
/* Font Family */
|
||||
--font-heading: 'Outfit', sans-serif;
|
||||
--font-body: 'Inter', sans-serif;
|
||||
|
||||
/* Color Palette - GitHub Dark Theme */
|
||||
--bg-deep: #0d1117;
|
||||
--bg-darker: #161b22;
|
||||
--bg-dark: #21262d;
|
||||
--bg-card: rgba(22, 27, 34, 0.55);
|
||||
--bg-card-hover: rgba(22, 27, 34, 0.85);
|
||||
|
||||
--primary: #39d353; /* GitHub Commits Green */
|
||||
--primary-glow: rgba(57, 211, 83, 0.15);
|
||||
--primary-double-glow: rgba(57, 211, 83, 0.35);
|
||||
--secondary: #2ea44f; /* GitHub Buttons Green */
|
||||
--accent: #58a6ff; /* GitHub Links Blue */
|
||||
--accent-glow: rgba(88, 166, 255, 0.2);
|
||||
|
||||
--text-primary: #c9d1d9;
|
||||
--text-secondary: #8b949e;
|
||||
--text-muted: #484f58;
|
||||
|
||||
--border-color: #30363d;
|
||||
--border-hover: #8b949e;
|
||||
|
||||
/* Spacing */
|
||||
--transition-smooth: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
--transition-fast: all 0.2s ease;
|
||||
|
||||
/* Border Radius */
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
}
|
||||
|
||||
/* Base resets */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-deep);
|
||||
color: var(--text-primary);
|
||||
font-family: var(--font-body);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Background Gradients and Orbs */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: -20%;
|
||||
left: -20%;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
background: radial-gradient(circle, rgba(57, 211, 83, 0.05) 0%, transparent 60%);
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
filter: blur(80px);
|
||||
}
|
||||
|
||||
body::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
bottom: -20%;
|
||||
right: -20%;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
background: radial-gradient(circle, rgba(88, 166, 255, 0.05) 0%, transparent 60%);
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
filter: blur(80px);
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--text-primary);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Premium Scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--bg-deep);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--bg-dark);
|
||||
border-radius: 5px;
|
||||
border: 2px solid var(--bg-deep);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
/* Utility Layouts */
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 120px 0 60px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 800;
|
||||
margin-bottom: 50px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
background: linear-gradient(135deg, var(--text-primary) 30%, var(--secondary) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.section-title::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 60px;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, var(--primary), var(--accent));
|
||||
margin: 12px auto 0 auto;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 0 10px var(--primary-glow);
|
||||
}
|
||||
|
||||
/* Glassmorphism Styles */
|
||||
.glass-card {
|
||||
background: var(--bg-card);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 32px;
|
||||
transition: var(--transition-smooth);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.glass-card:hover {
|
||||
background: var(--bg-card-hover);
|
||||
border-color: var(--border-hover);
|
||||
box-shadow: 0 12px 40px 0 rgba(0, 242, 254, 0.1);
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
/* Typography Gradient */
|
||||
.text-gradient {
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 50%, var(--accent) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
/* Interactive Button */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 28px;
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 600;
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
transition: var(--transition-smooth);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
color: #080c14;
|
||||
border: none;
|
||||
box-shadow: 0 4px 15px var(--primary-glow);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
box-shadow: 0 6px 20px var(--primary-double-glow);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
border-color: var(--primary);
|
||||
background: rgba(0, 242, 254, 0.05);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* Grid helper */
|
||||
.grid-2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.grid-3 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
@media (max-width: 968px) {
|
||||
.grid-2, .grid-3 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.section {
|
||||
padding: 80px 0 40px 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Keyframes & Animations */
|
||||
@keyframes float {
|
||||
0% { transform: translateY(0px) rotate(0deg); }
|
||||
50% { transform: translateY(-10px) rotate(2deg); }
|
||||
100% { transform: translateY(0px) rotate(0deg); }
|
||||
}
|
||||
|
||||
@keyframes pulse-slow {
|
||||
0% { opacity: 0.3; }
|
||||
50% { opacity: 0.6; }
|
||||
100% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
.pulse {
|
||||
animation: pulse-slow 4s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.float {
|
||||
animation: float 6s ease-in-out infinite;
|
||||
}
|
||||
973
src/locales/de.po
Normal file
|
|
@ -0,0 +1,973 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: de\n"
|
||||
|
||||
# Navbar Links
|
||||
msgid "Home"
|
||||
msgstr "Startseite"
|
||||
|
||||
msgid "About"
|
||||
msgstr "Über Mich"
|
||||
|
||||
msgid "Skills"
|
||||
msgstr "Fähigkeiten"
|
||||
|
||||
msgid "Experience"
|
||||
msgstr "Erfahrung"
|
||||
|
||||
msgid "Projects"
|
||||
msgstr "Projekte"
|
||||
|
||||
msgid "Contact"
|
||||
msgstr "Kontakt"
|
||||
|
||||
# Hero Section
|
||||
msgid "Available for new opportunities"
|
||||
msgstr "Verfügbar für neue Möglichkeiten"
|
||||
|
||||
msgid "Building Scalable"
|
||||
msgstr "Entwicklung skalierbarer"
|
||||
|
||||
msgid "Backend Systems"
|
||||
msgstr "Backend-Systeme"
|
||||
|
||||
msgid "Seamless Interfaces"
|
||||
msgstr "nahtlose Benutzeroberflächen"
|
||||
|
||||
msgid "I am a "
|
||||
msgstr "Ich bin "
|
||||
|
||||
msgid "Full-Stack Developer"
|
||||
msgstr "Full-Stack-Entwickler"
|
||||
|
||||
msgid "Backend Specialist"
|
||||
msgstr "Backend-Spezialist"
|
||||
|
||||
msgid "API Designer"
|
||||
msgstr "API-Designer"
|
||||
|
||||
msgid "System Thinker"
|
||||
msgstr "Systemdenker"
|
||||
|
||||
msgid "Professional Software Developer with 4.5+ years of experience building high-performance backend APIs, handling databases, and crafting clean frontend applications. Specialized in PHP (Symfony), TypeScript, and containerized Docker environments."
|
||||
msgstr "Professioneller Softwareentwickler mit mehr als 4,5 Jahren Erfahrung im Aufbau von leistungsstarken Backend-APIs, der Verwaltung von Datenbanken und der Erstellung sauberer Frontend-Anwendungen. Spezialisiert auf PHP (Symfony), TypeScript und containerisierte Docker-Umgebungen."
|
||||
|
||||
msgid "Explore Projects"
|
||||
msgstr "Projekte erkunden"
|
||||
|
||||
msgid "Let's Talk"
|
||||
msgstr "Lass uns reden"
|
||||
|
||||
msgid "Run Output: "
|
||||
msgstr "Ausgabe: "
|
||||
|
||||
msgid "🚀 Local execution ready"
|
||||
msgstr "🚀 Lokale Ausführung bereit"
|
||||
|
||||
msgid "⚠️ Dependency missing"
|
||||
msgstr "⚠️ Abhängigkeit fehlt"
|
||||
|
||||
# About Section
|
||||
msgid "About Me"
|
||||
msgstr "Über Mich"
|
||||
|
||||
msgid "My Journey as a Developer"
|
||||
msgstr "Mein Weg als Entwickler"
|
||||
|
||||
msgid "I hold a <1>Degree in Computer Engineering in Information Systems</1> from Seville, Spain, and spent an exchange year completing <3>Master's coursework in Computer Science and Engineering</3> at the prestigious <5>University of Bologna</5> in Italy."
|
||||
msgstr "Ich habe einen <1>Abschluss in Computer Engineering in Information Systems</1> aus Sevilla, Spanien, und habe ein Austauschjahr mit <3>Master-Kursen in Computer Science and Engineering</3> an der renommierten <5>Universität Bologna</5> in Italien verbracht."
|
||||
|
||||
msgid "My foundation in software engineering is reinforced by extensive vocational training in web and multiplatform application development, combined with over <1>4 years of professional experience</1> in Germany's leading comparison portal (CHECK24) and media agencies."
|
||||
msgstr "Mein Fundament im Software Engineering wird durch eine umfassende Berufsausbildung im Bereich Web- und Multiplattform-Anwendungsentwicklung gestärkt, kombiniert mit über <1>4 Jahren Berufserfahrung</1> bei Deutschlands führendem Vergleichsportal (CHECK24) und Medienagenturen."
|
||||
|
||||
msgid "I enjoy solving complex architectural challenges, designing robust APIs, containerizing deployment environments, and exploring deep learning models. Multi-lingual and highly adaptable, I am comfortable collaborating across international, cross-functional teams."
|
||||
msgstr "Ich habe Spaß daran, komplexe architektonische Herausforderungen zu lösen, robuste APIs zu entwerfen, Bereitstellungsumgebungen zu containerisieren und Deep-Learning-Modelle zu erforschen. Mehrsprachig und sehr anpassungsfähig, kann ich mich problemlos in internationalen, funktionsübergreifenden Teams einbringen."
|
||||
|
||||
msgid "Years Experience"
|
||||
msgstr "Jahre Erfahrung"
|
||||
|
||||
msgid "Languages Spoken"
|
||||
msgstr "Gesprochene Sprachen"
|
||||
|
||||
msgid "Academic Qualifications"
|
||||
msgstr "Akademische Qualifikationen"
|
||||
|
||||
msgid "Personal Projects"
|
||||
msgstr "Persönliche Projekte"
|
||||
|
||||
msgid "Certifications"
|
||||
msgstr "Zertifikate"
|
||||
|
||||
msgid "Oxford Test of English (B1)"
|
||||
msgstr "Oxford Test of English (B1)"
|
||||
|
||||
msgid "Oxford University Press"
|
||||
msgstr "Oxford University Press"
|
||||
|
||||
msgid "C++ Certification"
|
||||
msgstr "C++ Zertifikat"
|
||||
|
||||
msgid "Programming Institute"
|
||||
msgstr "Programming Institute"
|
||||
|
||||
msgid "Java Certification"
|
||||
msgstr "Java Zertifikat"
|
||||
|
||||
msgid "Oracle Certified Associate"
|
||||
msgstr "Oracle Certified Associate"
|
||||
|
||||
msgid "SQL Developer Certification"
|
||||
msgstr "SQL Developer Zertifikat"
|
||||
|
||||
msgid "Database Systems"
|
||||
msgstr "Datenbanksysteme"
|
||||
|
||||
# Languages
|
||||
msgid "Spanish"
|
||||
msgstr "Spanisch"
|
||||
|
||||
msgid "English"
|
||||
msgstr "Englisch"
|
||||
|
||||
msgid "Italian"
|
||||
msgstr "Italienisch"
|
||||
|
||||
msgid "German"
|
||||
msgstr "Deutsch"
|
||||
|
||||
msgid "French"
|
||||
msgstr "Französisch"
|
||||
|
||||
msgid "Native / Bilingual"
|
||||
msgstr "Muttersprache / Zweisprachig"
|
||||
|
||||
msgid "Professional (B1 Certified)"
|
||||
msgstr "Professionell (B1 zertifiziert)"
|
||||
|
||||
msgid "Conversational"
|
||||
msgstr "Konversationssicher"
|
||||
|
||||
msgid "Basic / Learning"
|
||||
msgstr "Grundkenntnisse / Am Lernen"
|
||||
|
||||
# Skills Section
|
||||
msgid "Technical Expertise"
|
||||
msgstr "Technische Kompetenz"
|
||||
|
||||
msgid "A robust technical foundation bridging low-level systems (C++), high-concurrency web systems (PHP, Node.js), and data analytics (Python, Neo4j)."
|
||||
msgstr "Ein solides technisches Fundament, das eine Brücke schlägt zwischen hardwarenahen Systemen (C++), hoch-performanten Websystemen (PHP, Node.js) und Datenanalyse (Python, Neo4j)."
|
||||
|
||||
msgid "Backend & Systems"
|
||||
msgstr "Backend & Systeme"
|
||||
|
||||
msgid "Frontend & UI"
|
||||
msgstr "Frontend & UI"
|
||||
|
||||
msgid "Databases & Technologies"
|
||||
msgstr "Datenbanken & Technologien"
|
||||
|
||||
# Experience Section
|
||||
msgid "My Timeline"
|
||||
msgstr "Mein Werdegang"
|
||||
|
||||
msgid "Work Experience"
|
||||
msgstr "Berufserfahrung"
|
||||
|
||||
msgid "Education"
|
||||
msgstr "Ausbildung"
|
||||
|
||||
msgid "Check24 (Germany)"
|
||||
msgstr "Check24 (Deutschland)"
|
||||
|
||||
msgid "Augsburg, Bavaria, Germany"
|
||||
msgstr "Augsburg, Bayern, Deutschland"
|
||||
|
||||
msgid "Ulm, Baden-Wurttemberg, Germany"
|
||||
msgstr "Ulm, Baden-Württemberg, Deutschland"
|
||||
|
||||
msgid "Seville, Spain"
|
||||
msgstr "Sevilla, Spanien"
|
||||
|
||||
msgid "Bologna, Italy"
|
||||
msgstr "Bologna, Italien"
|
||||
|
||||
msgid "Full Stack Web Developer"
|
||||
msgstr "Full-Stack-Webentwickler"
|
||||
|
||||
msgid "Student Intern (Recommender Systems)"
|
||||
msgstr "Werkstudent (Empfehlungssysteme)"
|
||||
|
||||
msgid "Web Developer (JS, PHP, & Symfony)"
|
||||
msgstr "Webentwickler (JS, PHP & Symfony)"
|
||||
|
||||
msgid "University Pablo de Olavide"
|
||||
msgstr "Universität Pablo de Olavide"
|
||||
|
||||
msgid "Green Slope S.L."
|
||||
msgstr "Green Slope S.L."
|
||||
|
||||
# Experience Details
|
||||
msgid "Backend development specializing in PHP, building scalable services and microservices."
|
||||
msgstr "Backend-Entwicklung spezialisiert auf PHP, Erstellung skalierbarer Dienste und Mikrodienste."
|
||||
|
||||
msgid "Creation, optimization, and integration of robust RESTful/SOAP APIs."
|
||||
msgstr "Erstellung, Optimierung und Integration robuster RESTful/SOAP APIs."
|
||||
|
||||
msgid "Local dockerization setups and automated testing pipeline maintenance."
|
||||
msgstr "Lokale Docker-Setups und Wartung automatisierter Testpipelines."
|
||||
|
||||
msgid "Large-scale database handling, schema migrations, and query performance tuning."
|
||||
msgstr "Umgang mit großen Datenbanken, Schema-Migrationen und Abfrage-Leistungsoptimierung."
|
||||
|
||||
msgid "Frontend web development utilizing HTML5, CSS3, and JavaScript/TypeScript."
|
||||
msgstr "Frontend-Webentwicklung mit HTML5, CSS3 und JavaScript/TypeScript."
|
||||
|
||||
msgid "Backend development using Symfony PHP framework for production applications."
|
||||
msgstr "Backend-Entwicklung mit dem Symfony PHP-Framework für Produktivanwendungen."
|
||||
|
||||
msgid "Content management system (CMS) integration and customization using Typo3."
|
||||
msgstr "Integration und Anpassung von Content-Management-Systemen (CMS) mit Typo3."
|
||||
|
||||
msgid "Creation of software designs using UML diagrams for team collaboration."
|
||||
msgstr "Erstellung von Softwaredesigns mittels UML-Diagrammen zur Teamkollaboration."
|
||||
|
||||
msgid "Researched and built recommendation algorithms based on collaborative filtering."
|
||||
msgstr "Erforschung und Entwicklung von Empfehlungsalgorithmen auf Basis kollaborativer Filterung."
|
||||
|
||||
msgid "Developed fuzzy clustering engines using Python, pandas, and numpy."
|
||||
msgstr "Entwicklung von Fuzzy-Clustering-Engines mit Python, Pandas und NumPy."
|
||||
|
||||
msgid "Utilized graph-oriented databases (Neo4j) to model product/user relationships."
|
||||
msgstr "Nutzung graphorientierter Datenbanken (Neo4j) zur Modellierung von Produkt/Nutzer-Beziehungen."
|
||||
|
||||
msgid "Developed cross-platform mobile apps using the Ionic Framework."
|
||||
msgstr "Entwicklung plattformübergreifender mobiler Apps mit dem Ionic-Framework."
|
||||
|
||||
msgid "Built automated scripts and workflows using Google Apps Script."
|
||||
msgstr "Erstellung automatisierter Skripte und Workflows mit Google Apps Script."
|
||||
|
||||
msgid "Managed and maintained Linux production servers and VPS environments."
|
||||
msgstr "Verwaltung und Wartung von Linux-Produktionsservern und VPS-Umgebungen."
|
||||
|
||||
msgid "Specialized in information systems design, algorithms, databases, and software design patterns."
|
||||
msgstr "Spezialisiert auf Informationssystemdesign, Algorithmen, Datenbanken und Software-Design-Patterns."
|
||||
|
||||
msgid "Completed advanced master coursework in software architectures, web technologies, and systems engineering during Erasmus exchange."
|
||||
msgstr "Absolvierung fortgeschrittener Masterkurse in Softwarearchitektur, Webtechnologien und Systems Engineering während des Erasmus-Austauschs."
|
||||
|
||||
msgid "Hands-on vocational program focusing on mobile development (Android, iOS) and cross-platform desktop frameworks."
|
||||
msgstr "Praxisorientierte Berufsausbildung mit Fokus auf mobile Entwicklung (Android, iOS) und plattformübergreifende Desktop-Frameworks."
|
||||
|
||||
msgid "Practical training focused on front-end layouts, back-end scripting, SQL databases, and web servers."
|
||||
msgstr "Praktische Ausbildung mit Fokus auf Front-End-Layouts, Back-End-Scripting, SQL-Datenbanken und Webserver."
|
||||
|
||||
msgid "Introduction to hardware assembly, operating systems installation, router configuration, and local network diagnostics."
|
||||
msgstr "Einführung in Hardware-Montage, Betriebssysteminstallation, Router-Konfiguration und lokale Netzwerkdiagnose."
|
||||
|
||||
# Projects Section
|
||||
msgid "Personal Projects"
|
||||
msgstr "Persönliche Projekte"
|
||||
|
||||
msgid "A curated selection of personal creations showing my growth across IoT, Deep Learning, Mobile development, and system designs."
|
||||
msgstr "Eine kuratierte Auswahl persönlicher Kreationen, die mein Wachstum in den Bereichen IoT, Deep Learning, mobile Entwicklung und Systemdesign zeigen."
|
||||
|
||||
msgid "All Projects"
|
||||
msgstr "Alle"
|
||||
|
||||
msgid "AI & Data"
|
||||
msgstr "KI & Daten"
|
||||
|
||||
msgid "Web & IoT"
|
||||
msgstr "Web & IoT"
|
||||
|
||||
msgid "Mobile Apps"
|
||||
msgstr "Mobile Apps"
|
||||
|
||||
msgid "Learn More"
|
||||
msgstr "Mehr erfahren"
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "Übersicht"
|
||||
|
||||
msgid "Technical Flow & Pattern"
|
||||
msgstr "Technischer Ablauf & Muster"
|
||||
|
||||
msgid "Tech Stack"
|
||||
msgstr "Technologie-Stack"
|
||||
|
||||
msgid "View on GitHub"
|
||||
msgstr "Auf GitHub anzeigen"
|
||||
|
||||
# Specific Projects
|
||||
msgid "Supermarket Recommender System"
|
||||
msgstr "Supermarkt-Empfehlungssystem"
|
||||
|
||||
msgid "A hybrid recommendation engine utilizing fuzzy clustering and graph databases for market basket analysis."
|
||||
msgstr "Ein hybrides Empfehlungssystem, das Fuzzy-Clustering und Graphdatenbanken zur Warenkorbanalyse nutzt."
|
||||
|
||||
msgid "Developed a high-accuracy recommender system that analyzes consumer purchasing history and recommends items based on fuzzy clustering groups. Built a graph network in Neo4j to model user-to-product buy relationships, allowing fast collaborative filtering queries."
|
||||
msgstr "Entwicklung eines hochpräzisen Empfehlungssystems, das die Kaufhistorie von Verbrauchern analysiert und Artikel basierend auf Fuzzy-Clustering-Gruppen empfiehlt. Aufbau eines Graphennetzwerks in Neo4j zur Modellierung von Benutzer-Produkt-Kaufbeziehungen für schnelle kollaborative Filterabfragen."
|
||||
|
||||
msgid "Data extraction & cleaning via Pandas and NumPy"
|
||||
msgstr "Datenextraktion & -bereinigung via Pandas und NumPy"
|
||||
|
||||
msgid "Fuzzy C-Means Clustering to group similar purchasing baskets"
|
||||
msgstr "Fuzzy-C-Means-Clustering zur Gruppierung ähnlicher Warenkörbe"
|
||||
|
||||
msgid "Graph DB mapping of nodes (Customers, Products, Categories) in Neo4j"
|
||||
msgstr "Graphdatenbank-Mapping von Knoten (Kunden, Produkte, Kategorien) in Neo4j"
|
||||
|
||||
msgid "Real-time neighbor matching query logic via Cypher"
|
||||
msgstr "Echtzeit-Nachbarabgleichslogik via Cypher"
|
||||
|
||||
msgid "IoT Smart Dumpster"
|
||||
msgstr "Intelligenter IoT-Müllcontainer"
|
||||
|
||||
msgid "Intelligent trash container telemetry network using ESP8266, custom PHP backend, and Android monitoring."
|
||||
msgstr "Intelligentes Telemetrienetzwerk für Müllcontainer mit ESP8266, eigenem PHP-Backend und Android-Überwachung."
|
||||
|
||||
msgid "Designed a smart container system powered by ESP8266 nodeMCU v3 microcontrollers. It uses ultrasonic sensors to measure fill levels, communicates with a PHP API via encrypted HTTP POST (using MD5 signature verification), and reports real-time dashboard data to a companion Android app."
|
||||
msgstr "Entwurf eines intelligenten Containersystems, das von ESP8266 nodeMCU v3 Mikrocontrollern betrieben wird. Es nutzt Ultraschallsensoren zur Messung des Füllstands, kommuniziert mit einer PHP-API via verschlüsseltem HTTP-POST (mit MD5-Signaturverifizierung) und meldet Echtzeit-Dashboard-Daten an eine begleitende Android-App."
|
||||
|
||||
msgid "ESP8266 telemetry node running on C++/Arduino sketch"
|
||||
msgstr "ESP8266-Telemetrieknoten auf Basis von C++/Arduino Sketch"
|
||||
|
||||
msgid "PHP Web API endpoint with MD5 request checksum validation"
|
||||
msgstr "PHP-Web-API-Endpunkt mit MD5-Anforderungs-Prüfsummenvalidierung"
|
||||
|
||||
msgid "Background Scheduler running task loops to identify full units"
|
||||
msgstr "Hintergrund-Planer, der Aufgabenschleifen ausführt, um volle Einheiten zu identifizieren"
|
||||
|
||||
msgid "Android Client using HTTP endpoints to fetch status maps"
|
||||
msgstr "Android-Client, der HTTP-Endpunkte zum Abrufen von Statuskarten nutzt"
|
||||
|
||||
msgid "Animal Adoption Mobile App"
|
||||
msgstr "Mobile App zur Tieradoption"
|
||||
|
||||
msgid "A native mobile application connecting animal shelters with prospective adopters."
|
||||
msgstr "Eine native mobile Anwendung, die Tierheime mit potenziellen Adoptanten verbindet."
|
||||
|
||||
msgid "A native Android mobile application that aggregates profiles of adoptable pets from local shelters. Allows users to filter by breed, age, location, and apply for adoption directly through the platform. Connects to a secure MySQL database for real-time applications handling."
|
||||
msgstr "Eine native Android-App, die Profile adoptierbarer Haustiere aus lokalen Tierheimen sammelt. Ermöglicht Benutzern das Filtern nach Rasse, Alter, Standort und die direkte Bewerbung zur Adoption über die Plattform. Verbindet sich mit einer sicheren MySQL-Datenbank zur Echtzeit-Bewerbungsverwaltung."
|
||||
|
||||
msgid "Custom Blogging Platform"
|
||||
msgstr "Eigene Blog-Plattform"
|
||||
|
||||
msgid "A full-featured CMS web application for blogging and publishing article contents."
|
||||
msgstr "Eine voll ausgestattete CMS-Webanwendung zum Bloggen und Veröffentlichen von Artikelinhalten."
|
||||
|
||||
msgid "A custom-built Content Management System (CMS) designed for authors. It features markdown editing, user authentication, profile customizer, dynamic commenting sections, and tags search. Powered by a classic LAMP/LEMP stack architecture."
|
||||
msgstr "Ein maßgeschneidertes Content Management System (CMS) für Autoren. Es bietet Markdown-Bearbeitung, Benutzerauthentifizierung, Profilanpassung, dynamische Kommentarbereiche und Schlagwortsuche. Betrieben durch eine klassische LAMP/LEMP-Stack-Architektur."
|
||||
|
||||
msgid "Self-driving Car in CARLA"
|
||||
msgstr "Selbstfahrendes Auto in CARLA"
|
||||
|
||||
msgid "Deep Reinforcement Learning script enabling autonomous vehicle lane tracking and driving."
|
||||
msgstr "Deep-Reinforcement-Learning-Skript für die Spurverfolgung und das autonome Fahren von Fahrzeugen."
|
||||
|
||||
msgid "Programmed an autonomous driving agent inside the CARLA simulator. Utilized a deep reinforcement learning model (Keras/TensorFlow) mapping front-camera frames to steering and throttle actions. Trained the network using Deep Q-Learning (DQN) to maximize reward for staying in lanes and avoiding collisions."
|
||||
msgstr "Programmierung eines autonomen Fahragenten im CARLA-Simulator. Verwendung eines Deep-Reinforcement-Learning-Modells (Keras/TensorFlow), das Frontkamera-Frames auf Lenk- und Gasaktionen abbildet. Trainiert wurde das Netzwerk mit Deep Q-Learning (DQN), um die Belohnung für das Verbleiben in Fahrspuren und das Vermeiden von Kollisionen zu maximieren."
|
||||
|
||||
msgid "Video streaming ingestion from CARLA camera sensor nodes"
|
||||
msgstr "Videostreaming-Aufnahme von CARLA-Kamerasensorknoten"
|
||||
|
||||
msgid "Frame preprocessing & downscaling using OpenCV"
|
||||
msgstr "Frame-Vorverarbeitung & -Downscaling mit OpenCV"
|
||||
|
||||
msgid "Deep Neural Network trained using Keras/TensorFlow framework"
|
||||
msgstr "Deep Neural Network trainiert mit Keras/TensorFlow-Framework"
|
||||
|
||||
msgid "DQN reward structure optimization based on forward velocity and lane alignment"
|
||||
msgstr "DQN-Belohnungsstruktur-Optimierung basierend auf Vorwärtsgeschwindigkeit und Spurausrichtung"
|
||||
|
||||
# Contact Section
|
||||
msgid "Get In Touch"
|
||||
msgstr "Kontakt aufnehmen"
|
||||
|
||||
msgid "Have a question or want to discuss a project? Drop a message and I'll get back to you as soon as possible."
|
||||
msgstr "Haben Sie eine Frage oder möchten Sie ein Projekt besprechen? Hinterlassen Sie eine Nachricht und ich werde mich so schnell wie möglich bei Ihnen melden."
|
||||
|
||||
msgid "Let's build something together"
|
||||
msgstr "Lass uns zusammen etwas bauen"
|
||||
|
||||
msgid "Whether you are looking to design a backend API, containerize a legacy application, or create an interactive frontend, I'd love to chat."
|
||||
msgstr "Egal, ob Sie eine Backend-API entwerfen, eine Altanwendung containerisieren oder ein interaktives Frontend erstellen möchten – ich würde mich freuen, mit Ihnen zu sprechen."
|
||||
|
||||
msgid "Email"
|
||||
msgstr "E-Mail"
|
||||
|
||||
msgid "Location"
|
||||
msgstr "Standort"
|
||||
|
||||
msgid "Quick Note:"
|
||||
msgstr "Kurzer Hinweis:"
|
||||
|
||||
msgid " I speak Spanish, English, Italian, and basic German, making collaboration in multi-lingual teams very smooth!"
|
||||
msgstr " Ich spreche Spanisch, Englisch, Italienisch und lerne Deutsch. Das macht die Zusammenarbeit in mehrsprachigen Teams sehr unkompliziert!"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
msgid "Message"
|
||||
msgstr "Nachricht"
|
||||
|
||||
msgid "Your Name"
|
||||
msgstr "Ihr Name"
|
||||
|
||||
msgid "Type your message here..."
|
||||
msgstr "Geben Sie hier Ihre Nachricht ein..."
|
||||
|
||||
msgid "Sending..."
|
||||
msgstr "Wird gesendet..."
|
||||
|
||||
msgid "Send Message"
|
||||
msgstr "Nachricht senden"
|
||||
|
||||
msgid "Message Sent Successfully!"
|
||||
msgstr "Nachricht erfolgreich gesendet!"
|
||||
|
||||
msgid "Thank you for reaching out. I'll get back to you shortly."
|
||||
msgstr "Vielen Dank für Ihre Kontaktaufnahme. Ich werde mich in Kürze bei Ihnen melden."
|
||||
|
||||
msgid "Send Another Message"
|
||||
msgstr "Eine weitere Nachricht senden"
|
||||
|
||||
# Contact Validation Errors
|
||||
msgid "Name is required"
|
||||
msgstr "Name ist erforderlich"
|
||||
|
||||
msgid "Email is required"
|
||||
msgstr "E-Mail ist erforderlich"
|
||||
|
||||
msgid "Please enter a valid email address"
|
||||
msgstr "Bitte geben Sie eine gültige E-Mail-Adresse ein"
|
||||
|
||||
msgid "Message cannot be empty"
|
||||
msgstr "Nachricht darf nicht leer sein"
|
||||
|
||||
# Footer
|
||||
msgid "© {{year}} Rafael Gonzalez Albes. All rights reserved."
|
||||
msgstr "© {{year}} Rafael Gonzalez Albes. Alle Rechte vorbehalten."
|
||||
|
||||
msgid "Built with React, TypeScript & Docker."
|
||||
msgstr "Erstellt mit React, TypeScript & Docker."
|
||||
|
||||
# GitHub Activity Calendar
|
||||
msgid "GitHub Activity"
|
||||
msgstr "GitHub-Aktivität"
|
||||
|
||||
msgid "contributions in the last year"
|
||||
msgstr "Beiträge im letzten Jahr"
|
||||
|
||||
msgid "Less"
|
||||
msgstr "Weniger"
|
||||
|
||||
msgid "More"
|
||||
msgstr "Mehr"
|
||||
|
||||
msgid "Mon"
|
||||
msgstr "Mon"
|
||||
|
||||
msgid "Wed"
|
||||
msgstr "Mit"
|
||||
|
||||
msgid "Fri"
|
||||
msgstr "Fre"
|
||||
|
||||
msgid "Jan"
|
||||
msgstr "Jan"
|
||||
|
||||
msgid "Feb"
|
||||
msgstr "Feb"
|
||||
|
||||
msgid "Mar"
|
||||
msgstr "Mär"
|
||||
|
||||
msgid "Apr"
|
||||
msgstr "Apr"
|
||||
|
||||
msgid "May"
|
||||
msgstr "Mai"
|
||||
|
||||
msgid "Jun"
|
||||
msgstr "Jun"
|
||||
|
||||
msgid "Jul"
|
||||
msgstr "Jul"
|
||||
|
||||
msgid "Aug"
|
||||
msgstr "Aug"
|
||||
|
||||
msgid "Sep"
|
||||
msgstr "Sep"
|
||||
|
||||
msgid "Oct"
|
||||
msgstr "Okt"
|
||||
|
||||
msgid "Nov"
|
||||
msgstr "Nov"
|
||||
|
||||
msgid "Dec"
|
||||
msgstr "Dez"
|
||||
|
||||
msgid "contributions"
|
||||
msgstr "Beiträge"
|
||||
|
||||
msgid "No"
|
||||
msgstr "Keine"
|
||||
|
||||
msgid "Low"
|
||||
msgstr "Niedrige"
|
||||
|
||||
msgid "Medium"
|
||||
msgstr "Mittlere"
|
||||
|
||||
msgid "High"
|
||||
msgstr "Hohe"
|
||||
|
||||
msgid "Very high"
|
||||
msgstr "Sehr hohe"
|
||||
|
||||
# Gallery & Templates
|
||||
msgid "Design Gallery"
|
||||
msgstr "Design-Galerie"
|
||||
|
||||
msgid "Choose a template to experience the portfolio. You can switch back at any time."
|
||||
msgstr "Wählen Sie ein Template aus, um das Portfolio zu erleben. Sie können jederzeit zurückwechseln."
|
||||
|
||||
msgid "GitHub Theme"
|
||||
msgstr "GitHub-Design"
|
||||
|
||||
msgid "Sleek developer profile inspired by GitHub's dark mode, green commits activity accents, and clean layout."
|
||||
msgstr "Elegantes Entwicklerprofil inspiriert von GitHubs Dark Mode, mit grünen Commit-Aktivitätsakzenten und klarem Layout."
|
||||
|
||||
msgid "Interactive CLI"
|
||||
msgstr "Interaktive CLI"
|
||||
|
||||
msgid "Interactive command-line shell interface. Fast, retro-tech styled, with quick commands selector panel."
|
||||
msgstr "Interaktives Befehlszeilen-Shell-Interface. Schnell, im Retro-Tech-Stil, mit schnellem Befehlsauswahl-Panel."
|
||||
|
||||
msgid "Bento Grid"
|
||||
msgstr "Bento-Raster"
|
||||
|
||||
msgid "A modern layout grouping technical categories and timelines as frosted glass tiles with neon purple shadows."
|
||||
msgstr "Ein modernes Layout, das technische Kategorien und Zeitleisten als satiniertes Glas mit neongelben/violetten Schatten gruppiert."
|
||||
|
||||
msgid "Choose"
|
||||
msgstr "Auswählen"
|
||||
|
||||
msgid "Switch Theme"
|
||||
msgstr "Design wechseln"
|
||||
|
||||
msgid "Welcome to Rafael Gonzalez Albes CLI Portfolio v1.0.0"
|
||||
msgstr "Willkommen im Rafael Gonzalez Albes CLI Portfolio v1.0.0"
|
||||
|
||||
msgid "Type 'help' to view all commands, or click the links on the right panel to execute them instantly."
|
||||
msgstr "Geben Sie 'help' ein, um alle Befehle anzuzeigen, oder klicken Sie auf die Links rechts, um sie direkt auszuführen."
|
||||
|
||||
msgid "Quick Commands"
|
||||
msgstr "Schnellbefehle"
|
||||
|
||||
msgid "Click any command to run it in the terminal output grid."
|
||||
msgstr "Klicken Sie auf einen Befehl, um ihn in der Konsole auszuführen."
|
||||
|
||||
msgid "About Me & Developer Journey"
|
||||
msgstr "Über mich & Werdegang"
|
||||
|
||||
msgid "Technical Skills & Expertise"
|
||||
msgstr "Technische Fähigkeiten & Kompetenz"
|
||||
|
||||
msgid "Personal Projects List"
|
||||
msgstr "Liste persönlicher Projekte"
|
||||
|
||||
msgid "Contact Info & Socials"
|
||||
msgstr "Kontaktinfos & Soziale Netze"
|
||||
|
||||
msgid "Clear the terminal screen"
|
||||
msgstr "Konsole leeren"
|
||||
|
||||
msgid "Change language"
|
||||
msgstr "Sprache wechseln"
|
||||
|
||||
msgid "Command not found. Type 'help' for available options."
|
||||
msgstr "Befehl nicht gefunden. Geben Sie 'help' ein für verfügbare Optionen."
|
||||
|
||||
msgid "Language changed to English"
|
||||
msgstr "Sprache auf Englisch geändert"
|
||||
|
||||
msgid "Language changed to Spanish"
|
||||
msgstr "Sprache auf Spanisch geändert"
|
||||
|
||||
msgid "Language changed to Italian"
|
||||
msgstr "Sprache auf Italienisch geändert"
|
||||
|
||||
msgid "Language changed to German"
|
||||
msgstr "Sprache auf Deutsch geändert"
|
||||
|
||||
msgid "Available for new opportunities"
|
||||
msgstr "Verfügbar für neue Möglichkeiten"
|
||||
|
||||
msgid "Spanish"
|
||||
msgstr "Spanisch"
|
||||
|
||||
msgid "English"
|
||||
msgstr "Englisch"
|
||||
|
||||
msgid "Italian"
|
||||
msgstr "Italienisch"
|
||||
|
||||
msgid "German"
|
||||
msgstr "Deutsch"
|
||||
|
||||
msgid "French"
|
||||
msgstr "Französisch"
|
||||
|
||||
msgid "Native / Bilingual"
|
||||
msgstr "Muttersprache / Zweisprachig"
|
||||
|
||||
msgid "Professional (B1 Certified)"
|
||||
msgstr "Professionell (B1 zertifiziert)"
|
||||
|
||||
msgid "Conversational"
|
||||
msgstr "Konversationssicher"
|
||||
|
||||
msgid "Basic / Learning"
|
||||
msgstr "Grundkenntnisse / Am Lernen"
|
||||
|
||||
msgid "Oxford Test of English (B1)"
|
||||
msgstr "Oxford Test of English (B1)"
|
||||
|
||||
msgid "Oxford University Press"
|
||||
msgstr "Oxford University Press"
|
||||
|
||||
msgid "Let's build something together"
|
||||
msgstr "Lassen Sie uns zusammen etwas bauen"
|
||||
|
||||
msgid "My Timeline"
|
||||
msgstr "Mein Werdegang"
|
||||
|
||||
# Cyberpunk & Editorial Layouts
|
||||
msgid "SYSTEM_ACTIVE"
|
||||
msgstr "SYSTEM_AKTIV"
|
||||
|
||||
msgid "Core Stack"
|
||||
msgstr "Technologie-Stack"
|
||||
|
||||
msgid "Chronology"
|
||||
msgstr "Chronologie"
|
||||
|
||||
msgid "Project Modules"
|
||||
msgstr "Projektmodule"
|
||||
|
||||
msgid "Developer Profile"
|
||||
msgstr "Entwicklerprofil"
|
||||
|
||||
msgid "Social Profiles"
|
||||
msgstr "Soziale Profile"
|
||||
|
||||
# Brutalist & Neumorphic Layouts
|
||||
msgid "Connect"
|
||||
msgstr "Verbinden"
|
||||
|
||||
msgid "I'm open for collaboration and backend architectures development."
|
||||
msgstr "Ich bin offen für Zusammenarbeit und die Entwicklung von Backend-Architekturen."
|
||||
|
||||
msgid "Work"
|
||||
msgstr "Arbeit"
|
||||
|
||||
msgid "Timeline"
|
||||
msgstr "Werdegang"
|
||||
|
||||
msgid "Databases & IoT"
|
||||
msgstr "Datenbanken & IoT"
|
||||
|
||||
msgid "Augsburg, Germany — Sevilla, Spain"
|
||||
msgstr "Augsburg, Deutschland — Sevilla, Spanien"
|
||||
|
||||
|
||||
|
||||
|
||||
msgid "Proactive & Autonomous Software Developer specializing in robust backend architectures, system optimization, and full-stack integration. Driven by curiosity and a lifetime passion for computing."
|
||||
msgstr "Proaktiver und autonomer Softwareentwickler mit Spezialisierung auf robuste Backend-Architekturen, Systemoptimierung und Full-Stack-Integration. Angetrieben von Neugier und einer lebenslangen Leidenschaft für die Informatik."
|
||||
|
||||
msgid "A computer enthusiast since childhood with over <1>6 years of professional development experience</1> across Spain and Germany. Proven ability to adapt quickly through self-learning and autonomously solve complex architectural and engineering challenges without requiring permanent supervision. Adept at managing system administration, maintaining databases, and driving collaborative growth within agile team environments."
|
||||
msgstr "Ein Computer-Enthusiast seit der Kindheit mit über <1>6 Jahren professioneller Entwicklungserfahrung</1> in Spanien und Deutschland. Nachgewiesene Fähigkeit, sich durch Selbstlernen schnell anzupassen und komplexe architektonische und technische Herausforderungen ohne ständige Aufsicht autonom zu lösen. Erfahren in der Systemadministration, der Pflege von Datenbanken und der Förderung des gemeinsamen Wachstums in agilen Teamumgebungen."
|
||||
|
||||
msgid "Circadian Cycle (Auto)"
|
||||
msgstr "Zirkadianer Zyklus (Auto)"
|
||||
|
||||
msgid "Cycles layouts automatically based on the time of day. Daylight is clean editorial/soft neumorphism, afternoons are brutalist, evenings are dark developer mode, late nights are terminal command lines."
|
||||
msgstr "Wechselt Layouts automatisch basierend auf der Tageszeit. Tageslicht ist klares Editorial/weiches Neumorphismus, Nachmittage sind brutalistisch, Abende sind dunkler Entwicklermodus, späte Nächte sind Terminal-Befehlszeilen."
|
||||
|
||||
msgid "Interactive 3D"
|
||||
msgstr "Interaktives 3D"
|
||||
|
||||
msgid "An immersive layout featuring mouse-tracking 3D tilt cards, parallax effects, and a custom canvas-rendered 3D rotating starfield constellation."
|
||||
msgstr "Ein immersives Layout mit Maus-Tracking-3D-Kippkarten, Parallaxe-Effekten und einem benutzerdefinierten, in Canvas gerenderten 3D-rotierenden Sternenfeld."
|
||||
|
||||
msgid "Warm Editorial"
|
||||
msgstr "Warmes Editorial"
|
||||
|
||||
msgid "Clean newspaper and blog style with high typography contrast, warm cream background, and spacious borders."
|
||||
msgstr "Sauberer Zeitungs- und Blog-Stil mit hohem Typografie-Kontrast, warmem cremefarbenem Hintergrund und großzügigen Rändern."
|
||||
|
||||
msgid "Cyberpunk Neon"
|
||||
msgstr "Cyberpunk Neon"
|
||||
|
||||
msgid "Vibrant vaporwave style with fluorescent borders, glowing shadows, grid backgrounds, and tech scanner overlay."
|
||||
msgstr "Vibrierender Vaporwave-Stil mit fluoreszierenden Rändern, leuchtenden Schatten, Gitterhintergründen und Tech-Scanner-Overlay."
|
||||
|
||||
msgid "Neo Brutalist"
|
||||
msgstr "Neubrutalistisch"
|
||||
|
||||
msgid "High contrast flat design. Bright warning yellow panels, thick black border boxes, and solid shadows."
|
||||
msgstr "Flaches Design mit hohem Kontrast. Hellgelbe Warnpaneele, dicke schwarze Rahmenboxen und solide Schatten."
|
||||
|
||||
msgid "Soft Neumorphism"
|
||||
msgstr "Weicher Neumorphismus"
|
||||
|
||||
msgid "Soft tactile layout using smooth beveled cards, extruded shadows, and recessed active states."
|
||||
msgstr "Weiches taktiles Layout mit glatten abgeschrägten Karten, extrudierten Schatten und vertieften aktiven Zuständen."
|
||||
|
||||
msgid "Vivid aurora gradient backgrounds with glassmorphic cards, animated glows, and a modern linear layout."
|
||||
msgstr "Lebendige Aurora-Verlaufshintergründe mit glasmorphischen Karten, animiertem Leuchten und einem modernen linearen Layout."
|
||||
|
||||
msgid "Spoken Languages"
|
||||
msgstr "Gesprochene Sprachen"
|
||||
|
||||
msgid "Invalid email address"
|
||||
msgstr "Ungültige E-Mail-Adresse"
|
||||
|
||||
msgid "Get in Touch"
|
||||
msgstr "Kontakt aufnehmen"
|
||||
|
||||
msgid "Contact Me"
|
||||
msgstr "Kontaktieren Sie mich"
|
||||
|
||||
msgid "I'm always open to discussing new projects, creative ideas, or opportunities. Feel free to reach out via email or send me a message!"
|
||||
msgstr "Ich bin immer offen für neue Projekte, kreative Ideen oder Möglichkeiten. Kontaktieren Sie mich gerne per E-Mail oder senden Sie mir eine Nachricht!"
|
||||
|
||||
msgid "Download CV"
|
||||
msgstr "CV herunterladen"
|
||||
|
||||
msgid "Professional Software Developer"
|
||||
msgstr "Professioneller Softwareentwickler"
|
||||
|
||||
msgid "Software Developer"
|
||||
msgstr "Softwareentwickler"
|
||||
|
||||
msgid "Years Exp."
|
||||
msgstr "Jahre Erf."
|
||||
|
||||
msgid "Languages"
|
||||
msgstr "Sprachen"
|
||||
|
||||
msgid "Backend"
|
||||
msgstr "Backend"
|
||||
|
||||
msgid "Frontend"
|
||||
msgstr "Frontend"
|
||||
|
||||
msgid "GONZÁLEZ ALBES"
|
||||
msgstr "GONZÁLEZ ALBES"
|
||||
|
||||
msgid "The Engineer"
|
||||
msgstr "Der Ingenieur"
|
||||
|
||||
msgid "Technical Skills"
|
||||
msgstr "Technische Fähigkeiten"
|
||||
|
||||
msgid "Selected Projects"
|
||||
msgstr "Ausgewählte Projekte"
|
||||
|
||||
msgid "Work Experience Timeline"
|
||||
msgstr "Zeitleiste der Berufserfahrung"
|
||||
|
||||
msgid "Education Timeline"
|
||||
msgstr "Zeitleiste der Ausbildung"
|
||||
|
||||
msgid "I enjoy solving complex architectural challenges, designing robust APIs, containerizing deployment environments, and exploring deep learning models."
|
||||
msgstr "Ich liebe es, komplexe architektonische Herausforderungen zu lösen, robuste APIs zu entwerfen, Bereitstellungsumgebungen zu containerisieren und Deep-Learning-Modelle zu erforschen."
|
||||
|
||||
msgid "A collection of featured projects and engineering work."
|
||||
msgstr "Eine Sammlung von vorgestellten Projekten und Ingenieurarbeiten."
|
||||
|
||||
msgid "Architected and optimized high-performance backend microservices and APIs utilizing PHP (Symfony), Python, and Go for comparative portal engines serving millions of monthly active users."
|
||||
msgstr "Entwarf und optimierte hochleistungsfähige Backend-Mikroservices und -APIs unter Verwendung von PHP (Symfony), Python und Go für Vergleichsportal-Engines, die Millionen von monatlich aktiven Nutzern bedienen."
|
||||
|
||||
msgid "Executed proactive server and database administration to ensure maximum uptime, scalability, and robust data integration pipelines."
|
||||
msgstr "Führte eine proaktive Server- und Datenbankadministration durch, um maximale Betriebszeit, Skalierbarkeit und robuste Datenintegrationspipelines sicherzustellen."
|
||||
|
||||
msgid "Promoted from Junior Software Engineer to Professional Software Engineer in April 2024 in recognition of ownership of critical core systems."
|
||||
msgstr "Im April 2024 vom Junior Software Engineer zum Professional Software Engineer befördert, in Anerkennung der Eigenverantwortung für kritische Kernsysteme."
|
||||
|
||||
msgid "Established containerized local test suites using Docker, reducing onboarding time for new developers and standardizing testing environments."
|
||||
msgstr "Einrichtung containerisierter lokaler Testsuites mit Docker, wodurch die Einarbeitungszeit für neue Entwickler verkürzt und Testumgebungen standardisiert wurden."
|
||||
|
||||
msgid "Participated in agile Scrum ceremonies, writing comprehensive API specifications, and ensuring system maintenance and high availability."
|
||||
msgstr "Teilnahme an agilen Scrum-Ceremonies, Schreiben umfassender API-Spezifikationen und Sicherstellung der Systemwartung und Hochverfügbarkeit."
|
||||
|
||||
msgid "Engineered interactive user interfaces using HTML5, CSS3, JavaScript, and JQuery, ensuring compliance with responsive web design principles."
|
||||
msgstr "Entwickelte interaktive Benutzeroberflächen mit HTML5, CSS3, JavaScript und JQuery unter Einhaltung der Prinzipien des responsiven Webdesigns."
|
||||
|
||||
msgid "Created custom backend logic, hooks, and modules using PHP and the Symfony framework to support e-commerce and media tools."
|
||||
msgstr "Erstellung benutzerdefinierter Backend-Logik, Hooks und Module unter Verwendung von PHP und dem Symfony-Framework zur Unterstützung von E-Commerce- und Medien-Tools."
|
||||
|
||||
msgid "Implemented and customized enterprise content management systems (CMS) using Typo3, writing custom TypoScript extensions."
|
||||
msgstr "Implementierte und passte Enterprise-Content-Management-Systeme (CMS) mit Typo3 an und schrieb benutzerdefinierte TypoScript-Erweiterungen."
|
||||
|
||||
msgid "Collaborated with product designers to map out system dependencies and architecture flows using formal UML diagrams."
|
||||
msgstr "Zusammenarbeit mit Produktdesignern zur Abbildung von Systemabhängigkeiten und Architekturflüssen mithilfe formaler UML-Diagramme."
|
||||
|
||||
msgid "Researched and implemented state-of-the-art recommendation systems based on collaborative filtering and fuzzy clustering models."
|
||||
msgstr "Erforschung und Implementierung modernster Empfehlungssysteme auf der Grundlage von kollaborativer Filterung und Fuzzy-Clustering-Modellen."
|
||||
|
||||
msgid "Processed and analyzed large consumer transaction datasets using Python (Pandas, NumPy) to evaluate recommender accuracy."
|
||||
msgstr "Verarbeitung und Analyse großer Verbrauchertransaktionsdatensätze mit Python (Pandas, NumPy) zur Bewertung der Empfehlungsgenauigkeit."
|
||||
|
||||
msgid "Designed and query-tuned graph-oriented database structures in Neo4j to model user-product buying patterns in real time."
|
||||
msgstr "Entwurf und Abfrageoptimierung von graphorientierten Datenbankstrukturen in Neo4j zur Modellierung von Benutzer-Produkt-Kaufmustern in Echtzeit."
|
||||
|
||||
msgid "Built supporting web portal features in JavaScript, PHP, and Symfony for academic research evaluations."
|
||||
msgstr "Erstellung unterstützender Webportal-Funktionen in JavaScript, PHP und Symfony für akademische Forschungsbewertungen."
|
||||
|
||||
msgid "Developed cross-platform mobile applications for iOS and Android using the Ionic Framework and Angular."
|
||||
msgstr "Entwicklung plattformübergreifender mobiler Anwendungen für iOS und Android unter Verwendung des Ionic Frameworks und Angular."
|
||||
|
||||
msgid "Created automated enterprise scripts, notifications, and sheet integrations using Google Apps Script."
|
||||
msgstr "Erstellung automatisierter Unternehmensskripte, Benachrichtigungen und Tabellenintegrationen unter Verwendung von Google Apps Script."
|
||||
|
||||
msgid "Managed Linux production and staging servers, configuring Apache/Nginx web servers, security groups, and SSH configurations."
|
||||
msgstr "Verwaltung von Linux-Produktions- und Staging-Servern, Konfiguration von Apache/Nginx-Webservern, Sicherheitsgruppen und SSH-Konfigurationen."
|
||||
|
||||
msgid "Maintained legacy client websites, writing custom MySQL queries, and migrating servers with minimal downtime."
|
||||
msgstr "Wartung bestehender Kundenwebsites, Schreiben benutzerdefinierter MySQL-Abfragen und Migration von Servern mit minimaler Ausfallzeit."
|
||||
|
||||
msgid "Degree in Computer Engineering in Information Systems"
|
||||
msgstr "Diplom-Informatiker für Informationssysteme"
|
||||
|
||||
msgid "Graduated with a thesis on Recommender Systems. Coursework: Advanced Algorithms, Database Management Systems, Software Engineering Patterns, Networks, and Operating Systems."
|
||||
msgstr "Abschluss mit einer Abschlussarbeit über Empfehlungssysteme. Lehrveranstaltungen: Fortgeschrittene Algorithmen, Datenbankmanagementsysteme, Software-Engineering-Muster, Netzwerke und Betriebssysteme."
|
||||
|
||||
msgid "Master's Degree in Computer Science and Engineering"
|
||||
msgstr "Master-Studium in Informatik und Ingenieurwesen"
|
||||
|
||||
msgid "Erasmus exchange year focusing on high-level systems. Coursework: Software Architectures, Distributed Systems, Advanced Web Technologies, Machine Learning, and Information Retrieval."
|
||||
msgstr "Erasmus-Austauschjahr mit Schwerpunkt auf High-Level-Systemen. Lehrveranstaltungen: Software-Architekturen, Verteilte Systeme, Fortgeschrittene Webtechnologien, Maschinelles Lernen und Information Retrieval."
|
||||
|
||||
msgid "Certificate of Higher Education - Multiplatform App Development"
|
||||
msgstr "Staatlich geprüfter Informatiker - Fachrichtung Anwendungsentwicklung"
|
||||
|
||||
msgid "Focused on native Android development (Java), database design, cross-platform UI architectures, and project management methodologies."
|
||||
msgstr "Schwerpunkt auf nativer Android-Entwicklung (Java), Datenbankdesign, plattformübergreifenden UI-Architekturen und Projektmanagement-Methoden."
|
||||
|
||||
msgid "Certificate of Higher Education - Web App Development"
|
||||
msgstr "Staatlich geprüfter Informatiker - Fachrichtung Webentwicklung"
|
||||
|
||||
msgid "Practical curriculum covering HTML5/CSS3/JavaScript, backend scripting in PHP, database engines (SQL), and deployment of web servers."
|
||||
msgstr "Praxisorientierter Lehrplan mit HTML5/CSS3/JavaScript, Backend-Scripting in PHP, Datenbank-Engines (SQL) und Bereitstellung von Webservern."
|
||||
|
||||
msgid "Vocational Training in Microcomputer Systems & Networks"
|
||||
msgstr "Ausbildung zum Fachinformatiker für Systemintegration"
|
||||
|
||||
msgid "Hands-on foundation in computer networks, operating system configuration, PC hardware assembly, and network troubleshooting."
|
||||
msgstr "Praktische Grundlagen in Computernetzwerken, Betriebssystemkonfiguration, PC-Hardwaremontage und Netzwerk-Fehlerbehebung."
|
||||
|
||||
msgid "Professional Certifications"
|
||||
msgstr "Professionelle Zertifizierungen"
|
||||
|
||||
msgid "Oxford Test of English (Level B1)"
|
||||
msgstr "Oxford Test of English (Stufe B1)"
|
||||
|
||||
msgid "Technical Certifications: Advanced C++, Java Enterprise, and SQL Database Design."
|
||||
msgstr "Technische Zertifizierungen: Fortgeschrittenes C++, Java Enterprise und SQL-Datenbankdesign."
|
||||
|
||||
msgid "Mediaconcept GmbH (Germany)"
|
||||
msgstr "Mediaconcept GmbH (Deutschland)"
|
||||
|
||||
msgid "University of Bologna (Erasmus)"
|
||||
msgstr "Universität Bologna (Erasmus)"
|
||||
|
||||
msgid "I.E.S Velázquez"
|
||||
msgstr "I.E.S Velázquez"
|
||||
|
||||
msgid "Center Nuevas Profesiones"
|
||||
msgstr "Zentrum Nuevas Profesiones"
|
||||
|
||||
msgid "I.E.S Axati"
|
||||
msgstr "I.E.S Axati"
|
||||
|
||||
msgid "Various Institutions"
|
||||
msgstr "Verschiedene Institutionen"
|
||||
|
||||
msgid "Online & Academic"
|
||||
msgstr "Online & Akademisch"
|
||||
|
||||
msgid "Writing clean, testable code"
|
||||
msgstr "Sauberen, testbaren Code schreiben"
|
||||
|
||||
msgid "PHP & Symfony"
|
||||
msgstr "PHP & Symfony"
|
||||
|
||||
msgid "Senior / Expert"
|
||||
msgstr "Senior / Experte"
|
||||
|
||||
msgid "Python (Deep Learning)"
|
||||
msgstr "Python (Deep Learning)"
|
||||
|
||||
msgid "Advanced / Intermediate"
|
||||
msgstr "Fortgeschritten / Mittler"
|
||||
|
||||
msgid "Java / OOP"
|
||||
msgstr "Java / OOP"
|
||||
|
||||
msgid "Advanced"
|
||||
msgstr "Fortgeschritten"
|
||||
|
||||
msgid "C / C++"
|
||||
msgstr "C / C++"
|
||||
|
||||
msgid "Intermediate"
|
||||
msgstr "Mittler"
|
||||
|
||||
msgid "Server Maintenance & Bash"
|
||||
msgstr "Serverwartung & Bash"
|
||||
|
||||
msgid "JavaScript / TypeScript"
|
||||
msgstr "JavaScript / TypeScript"
|
||||
|
||||
msgid "Senior"
|
||||
msgstr "Senior"
|
||||
|
||||
msgid "HTML5 & CSS3"
|
||||
msgstr "HTML5 & CSS3"
|
||||
|
||||
msgid "Expert"
|
||||
msgstr "Experte"
|
||||
|
||||
msgid "JQuery & Bootstrap"
|
||||
msgstr "JQuery & Bootstrap"
|
||||
|
||||
msgid "React"
|
||||
msgstr "React"
|
||||
|
||||
msgid "SQL (MySQL, PostgreSQL)"
|
||||
msgstr "SQL (MySQL, PostgreSQL)"
|
||||
|
||||
msgid "Neo4j (Graph DB)"
|
||||
msgstr "Neo4j (Graph-Datenbank)"
|
||||
|
||||
msgid "Docker / Containers"
|
||||
msgstr "Docker / Container"
|
||||
|
||||
msgid "IoT (Arduino, ESP8266)"
|
||||
msgstr "IoT (Arduino, ESP8266)"
|
||||
|
||||
msgid "Aurora Glass"
|
||||
msgstr "Aurora-Glas"
|
||||
|
||||
msgid "Time Distortion Active: Cycling layouts..."
|
||||
msgstr "Zeitverzerrung aktiv: Layouts werden durchlaufen..."
|
||||
926
src/locales/en.po
Normal file
|
|
@ -0,0 +1,926 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: en\n"
|
||||
|
||||
# Navbar Links
|
||||
msgid "Home"
|
||||
msgstr "Home"
|
||||
|
||||
msgid "About"
|
||||
msgstr "About"
|
||||
|
||||
msgid "Skills"
|
||||
msgstr "Skills"
|
||||
|
||||
msgid "Experience"
|
||||
msgstr "Experience"
|
||||
|
||||
msgid "Projects"
|
||||
msgstr "Projects"
|
||||
|
||||
msgid "Contact"
|
||||
msgstr "Contact"
|
||||
|
||||
# Hero Section
|
||||
msgid "Available for new opportunities"
|
||||
msgstr "Available for new opportunities"
|
||||
|
||||
msgid "Building Scalable"
|
||||
msgstr "Building Scalable"
|
||||
|
||||
msgid "Backend Systems"
|
||||
msgstr "Backend Systems"
|
||||
|
||||
msgid "Seamless Interfaces"
|
||||
msgstr "Seamless Interfaces"
|
||||
|
||||
msgid "I am a "
|
||||
msgstr "I am a "
|
||||
|
||||
msgid "Full-Stack Developer"
|
||||
msgstr "Full-Stack Developer"
|
||||
|
||||
msgid "Backend Specialist"
|
||||
msgstr "Backend Specialist"
|
||||
|
||||
msgid "API Designer"
|
||||
msgstr "API Designer"
|
||||
|
||||
msgid "System Thinker"
|
||||
msgstr "System Thinker"
|
||||
|
||||
msgid "Professional Software Developer with 4.5+ years of experience building high-performance backend APIs, handling databases, and crafting clean frontend applications. Specialized in PHP (Symfony), TypeScript, and containerized Docker environments."
|
||||
msgstr "Professional Software Developer with 4.5+ years of experience building high-performance backend APIs, handling databases, and crafting clean frontend applications. Specialized in PHP (Symfony), TypeScript, and containerized Docker environments."
|
||||
|
||||
msgid "Explore Projects"
|
||||
msgstr "Explore Projects"
|
||||
|
||||
msgid "Let's Talk"
|
||||
msgstr "Let's Talk"
|
||||
|
||||
msgid "Run Output: "
|
||||
msgstr "Run Output: "
|
||||
|
||||
msgid "🚀 Local execution ready"
|
||||
msgstr "🚀 Local execution ready"
|
||||
|
||||
msgid "⚠️ Dependency missing"
|
||||
msgstr "⚠️ Dependency missing"
|
||||
|
||||
# About Section
|
||||
msgid "About Me"
|
||||
msgstr "About Me"
|
||||
|
||||
msgid "My Journey as a Developer"
|
||||
msgstr "My Journey as a Developer"
|
||||
|
||||
msgid "I hold a <1>Degree in Computer Engineering in Information Systems</1> from Seville, Spain, and spent an exchange year completing <3>Master's coursework in Computer Science and Engineering</3> at the prestigious <5>University of Bologna</5> in Italy."
|
||||
msgstr "I hold a <1>Degree in Computer Engineering in Information Systems</1> from Seville, Spain, and spent an exchange year completing <3>Master's coursework in Computer Science and Engineering</3> at the prestigious <5>University of Bologna</5> in Italy."
|
||||
|
||||
msgid "My foundation in software engineering is reinforced by extensive vocational training in web and multiplatform application development, combined with over <1>4 years of professional experience</1> in Germany's leading comparison portal (CHECK24) and media agencies."
|
||||
msgstr "My foundation in software engineering is reinforced by extensive vocational training in web and multiplatform application development, combined with over <1>4 years of professional experience</1> in Germany's leading comparison portal (CHECK24) and media agencies."
|
||||
|
||||
msgid "I enjoy solving complex architectural challenges, designing robust APIs, containerizing deployment environments, and exploring deep learning models. Multi-lingual and highly adaptable, I am comfortable collaborating across international, cross-functional teams."
|
||||
msgstr "I enjoy solving complex architectural challenges, designing robust APIs, containerizing deployment environments, and exploring deep learning models. Multi-lingual and highly adaptable, I am comfortable collaborating across international, cross-functional teams."
|
||||
|
||||
msgid "Years Experience"
|
||||
msgstr "Years Experience"
|
||||
|
||||
msgid "Languages Spoken"
|
||||
msgstr "Languages Spoken"
|
||||
|
||||
msgid "Academic Qualifications"
|
||||
msgstr "Academic Qualifications"
|
||||
|
||||
msgid "Personal Projects"
|
||||
msgstr "Personal Projects"
|
||||
|
||||
msgid "Certifications"
|
||||
msgstr "Certifications"
|
||||
|
||||
msgid "Oxford Test of English (B1)"
|
||||
msgstr "Oxford Test of English (B1)"
|
||||
|
||||
msgid "Oxford University Press"
|
||||
msgstr "Oxford University Press"
|
||||
|
||||
msgid "C++ Certification"
|
||||
msgstr "C++ Certification"
|
||||
|
||||
msgid "Programming Institute"
|
||||
msgstr "Programming Institute"
|
||||
|
||||
msgid "Java Certification"
|
||||
msgstr "Java Certification"
|
||||
|
||||
msgid "Oracle Certified Associate"
|
||||
msgstr "Oracle Certified Associate"
|
||||
|
||||
msgid "SQL Developer Certification"
|
||||
msgstr "SQL Developer Certification"
|
||||
|
||||
msgid "Database Systems"
|
||||
msgstr "Database Systems"
|
||||
|
||||
# Languages
|
||||
msgid "Spanish"
|
||||
msgstr "Spanish"
|
||||
|
||||
msgid "English"
|
||||
msgstr "English"
|
||||
|
||||
msgid "Italian"
|
||||
msgstr "Italian"
|
||||
|
||||
msgid "German"
|
||||
msgstr "German"
|
||||
|
||||
msgid "French"
|
||||
msgstr "French"
|
||||
|
||||
msgid "Native / Bilingual"
|
||||
msgstr "Native / Bilingual"
|
||||
|
||||
msgid "Professional (B1 Certified)"
|
||||
msgstr "Professional (B1 Certified)"
|
||||
|
||||
msgid "Conversational"
|
||||
msgstr "Conversational"
|
||||
|
||||
msgid "Basic / Learning"
|
||||
msgstr "Basic / Learning"
|
||||
|
||||
# Skills Section
|
||||
msgid "Technical Expertise"
|
||||
msgstr "Technical Expertise"
|
||||
|
||||
msgid "A robust technical foundation bridging low-level systems (C++), high-concurrency web systems (PHP, Node.js), and data analytics (Python, Neo4j)."
|
||||
msgstr "A robust technical foundation bridging low-level systems (C++), high-concurrency web systems (PHP, Node.js), and data analytics (Python, Neo4j)."
|
||||
|
||||
msgid "Backend & Systems"
|
||||
msgstr "Backend & Systems"
|
||||
|
||||
msgid "Frontend & UI"
|
||||
msgstr "Frontend & UI"
|
||||
|
||||
msgid "Databases & Technologies"
|
||||
msgstr "Databases & Technologies"
|
||||
|
||||
# Experience Section
|
||||
msgid "My Timeline"
|
||||
msgstr "My Timeline"
|
||||
|
||||
msgid "Work Experience"
|
||||
msgstr "Work Experience"
|
||||
|
||||
msgid "Education"
|
||||
msgstr "Education"
|
||||
|
||||
msgid "Check24 (Germany)"
|
||||
msgstr "Check24 (Germany)"
|
||||
|
||||
msgid "Augsburg, Bavaria, Germany"
|
||||
msgstr "Augsburg, Bavaria, Germany"
|
||||
|
||||
msgid "Ulm, Baden-Wurttemberg, Germany"
|
||||
msgstr "Ulm, Baden-Wurttemberg, Germany"
|
||||
|
||||
msgid "Seville, Spain"
|
||||
msgstr "Seville, Spain"
|
||||
|
||||
msgid "Bologna, Italy"
|
||||
msgstr "Bologna, Italy"
|
||||
|
||||
msgid "Full Stack Web Developer"
|
||||
msgstr "Full Stack Web Developer"
|
||||
|
||||
msgid "Student Intern (Recommender Systems)"
|
||||
msgstr "Student Intern (Recommender Systems)"
|
||||
|
||||
msgid "Web Developer (JS, PHP, & Symfony)"
|
||||
msgstr "Web Developer (JS, PHP, & Symfony)"
|
||||
|
||||
msgid "University Pablo de Olavide"
|
||||
msgstr "University Pablo de Olavide"
|
||||
|
||||
msgid "Green Slope S.L."
|
||||
msgstr "Green Slope S.L."
|
||||
|
||||
# Experience Details
|
||||
msgid "Backend development specializing in PHP, building scalable services and microservices."
|
||||
msgstr "Backend development specializing in PHP, building scalable services and microservices."
|
||||
|
||||
msgid "Creation, optimization, and integration of robust RESTful/SOAP APIs."
|
||||
msgstr "Creation, optimization, and integration of robust RESTful/SOAP APIs."
|
||||
|
||||
msgid "Local dockerization setups and automated testing pipeline maintenance."
|
||||
msgstr "Local dockerization setups and automated testing pipeline maintenance."
|
||||
|
||||
msgid "Large-scale database handling, schema migrations, and query performance tuning."
|
||||
msgstr "Large-scale database handling, schema migrations, and query performance tuning."
|
||||
|
||||
msgid "Frontend web development utilizing HTML5, CSS3, and JavaScript/TypeScript."
|
||||
msgstr "Frontend web development utilizing HTML5, CSS3, and JavaScript/TypeScript."
|
||||
|
||||
msgid "Backend development using Symfony PHP framework for production applications."
|
||||
msgstr "Backend development using Symfony PHP framework for production applications."
|
||||
|
||||
msgid "Content management system (CMS) integration and customization using Typo3."
|
||||
msgstr "Content management system (CMS) integration and customization using Typo3."
|
||||
|
||||
msgid "Creation of software designs using UML diagrams for team collaboration."
|
||||
msgstr "Creation of software designs using UML diagrams for team collaboration."
|
||||
|
||||
msgid "Researched and built recommendation algorithms based on collaborative filtering."
|
||||
msgstr "Researched and built recommendation algorithms based on collaborative filtering."
|
||||
|
||||
msgid "Developed fuzzy clustering engines using Python, pandas, and numpy."
|
||||
msgstr "Developed fuzzy clustering engines using Python, pandas, and numpy."
|
||||
|
||||
msgid "Utilized graph-oriented databases (Neo4j) to model product/user relationships."
|
||||
msgstr "Utilized graph-oriented databases (Neo4j) to model product/user relationships."
|
||||
|
||||
msgid "Developed cross-platform mobile apps using the Ionic Framework."
|
||||
msgstr "Developed cross-platform mobile apps using the Ionic Framework."
|
||||
|
||||
msgid "Built automated scripts and workflows using Google Apps Script."
|
||||
msgstr "Built automated scripts and workflows using Google Apps Script."
|
||||
|
||||
msgid "Managed and maintained Linux production servers and VPS environments."
|
||||
msgstr "Managed and maintained Linux production servers and VPS environments."
|
||||
|
||||
msgid "Specialized in information systems design, algorithms, databases, and software design patterns."
|
||||
msgstr "Specialized in information systems design, algorithms, databases, and software design patterns."
|
||||
|
||||
msgid "Completed advanced master coursework in software architectures, web technologies, and systems engineering during Erasmus exchange."
|
||||
msgstr "Completed advanced master coursework in software architectures, web technologies, and systems engineering during Erasmus exchange."
|
||||
|
||||
msgid "Hands-on vocational program focusing on mobile development (Android, iOS) and cross-platform desktop frameworks."
|
||||
msgstr "Hands-on vocational program focusing on mobile development (Android, iOS) and cross-platform desktop frameworks."
|
||||
|
||||
msgid "Practical training focused on front-end layouts, back-end scripting, SQL databases, and web servers."
|
||||
msgstr "Practical training focused on front-end layouts, back-end scripting, SQL databases, and web servers."
|
||||
|
||||
msgid "Introduction to hardware assembly, operating systems installation, router configuration, and local network diagnostics."
|
||||
msgstr "Introduction to hardware assembly, operating systems installation, router configuration, and local network diagnostics."
|
||||
|
||||
# Projects Section
|
||||
msgid "Personal Projects"
|
||||
msgstr "Personal Projects"
|
||||
|
||||
msgid "A curated selection of personal creations showing my growth across IoT, Deep Learning, Mobile development, and system designs."
|
||||
msgstr "A curated selection of personal creations showing my growth across IoT, Deep Learning, Mobile development, and system designs."
|
||||
|
||||
msgid "All Projects"
|
||||
msgstr "All Projects"
|
||||
|
||||
msgid "AI & Data"
|
||||
msgstr "AI & Data"
|
||||
|
||||
msgid "Web & IoT"
|
||||
msgstr "Web & IoT"
|
||||
|
||||
msgid "Mobile Apps"
|
||||
msgstr "Mobile Apps"
|
||||
|
||||
msgid "Learn More"
|
||||
msgstr "Learn More"
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "Overview"
|
||||
|
||||
msgid "Technical Flow & Pattern"
|
||||
msgstr "Technical Flow & Pattern"
|
||||
|
||||
msgid "Tech Stack"
|
||||
msgstr "Tech Stack"
|
||||
|
||||
msgid "View on GitHub"
|
||||
msgstr "View on GitHub"
|
||||
|
||||
# Specific Projects
|
||||
msgid "Supermarket Recommender System"
|
||||
msgstr "Supermarket Recommender System"
|
||||
|
||||
msgid "A hybrid recommendation engine utilizing fuzzy clustering and graph databases for market basket analysis."
|
||||
msgstr "A hybrid recommendation engine utilizing fuzzy clustering and graph databases for market basket analysis."
|
||||
|
||||
msgid "Developed a high-accuracy recommender system that analyzes consumer purchasing history and recommends items based on fuzzy clustering groups. Built a graph network in Neo4j to model user-to-product buy relationships, allowing fast collaborative filtering queries."
|
||||
msgstr "Developed a high-accuracy recommender system that analyzes consumer purchasing history and recommends items based on fuzzy clustering groups. Built a graph network in Neo4j to model user-to-product buy relationships, allowing fast collaborative filtering queries."
|
||||
|
||||
msgid "Data extraction & cleaning via Pandas and NumPy"
|
||||
msgstr "Data extraction & cleaning via Pandas and NumPy"
|
||||
|
||||
msgid "Fuzzy C-Means Clustering to group similar purchasing baskets"
|
||||
msgstr "Fuzzy C-Means Clustering to group similar purchasing baskets"
|
||||
|
||||
msgid "Graph DB mapping of nodes (Customers, Products, Categories) in Neo4j"
|
||||
msgstr "Graph DB mapping of nodes (Customers, Products, Categories) in Neo4j"
|
||||
|
||||
msgid "Real-time neighbor matching query logic via Cypher"
|
||||
msgstr "Real-time neighbor matching query logic via Cypher"
|
||||
|
||||
msgid "IoT Smart Dumpster"
|
||||
msgstr "IoT Smart Dumpster"
|
||||
|
||||
msgid "Intelligent trash container telemetry network using ESP8266, custom PHP backend, and Android monitoring."
|
||||
msgstr "Intelligent trash container telemetry network using ESP8266, custom PHP backend, and Android monitoring."
|
||||
|
||||
msgid "Designed a smart container system powered by ESP8266 nodeMCU v3 microcontrollers. It uses ultrasonic sensors to measure fill levels, communicates with a PHP API via encrypted HTTP POST (using MD5 signature verification), and reports real-time dashboard data to a companion Android app."
|
||||
msgstr "Designed a smart container system powered by ESP8266 nodeMCU v3 microcontrollers. It uses ultrasonic sensors to measure fill levels, communicates with a PHP API via encrypted HTTP POST (using MD5 signature verification), and reports real-time dashboard data to a companion Android app."
|
||||
|
||||
msgid "ESP8266 telemetry node running on C++/Arduino sketch"
|
||||
msgstr "ESP8266 telemetry node running on C++/Arduino sketch"
|
||||
|
||||
msgid "PHP Web API endpoint with MD5 request checksum validation"
|
||||
msgstr "PHP Web API endpoint with MD5 request checksum validation"
|
||||
|
||||
msgid "Background Scheduler running task loops to identify full units"
|
||||
msgstr "Background Scheduler running task loops to identify full units"
|
||||
|
||||
msgid "Android Client using HTTP endpoints to fetch status maps"
|
||||
msgstr "Android Client using HTTP endpoints to fetch status maps"
|
||||
|
||||
msgid "Animal Adoption Mobile App"
|
||||
msgstr "Animal Adoption Mobile App"
|
||||
|
||||
msgid "A native mobile application connecting animal shelters with prospective adopters."
|
||||
msgstr "A native mobile application connecting animal shelters with prospective adopters."
|
||||
|
||||
msgid "A native Android mobile application that aggregates profiles of adoptable pets from local shelters. Allows users to filter by breed, age, location, and apply for adoption directly through the platform. Connects to a secure MySQL database for real-time applications handling."
|
||||
msgstr "A native Android mobile application that aggregates profiles of adoptable pets from local shelters. Allows users to filter by breed, age, location, and apply for adoption directly through the platform. Connects to a secure MySQL database for real-time applications handling."
|
||||
|
||||
msgid "Custom Blogging Platform"
|
||||
msgstr "Custom Blogging Platform"
|
||||
|
||||
msgid "A full-featured CMS web application for blogging and publishing article contents."
|
||||
msgstr "A full-featured CMS web application for blogging and publishing article contents."
|
||||
|
||||
msgid "A custom-built Content Management System (CMS) designed for authors. It features markdown editing, user authentication, profile customizer, dynamic commenting sections, and tags search. Powered by a classic LAMP/LEMP stack architecture."
|
||||
msgstr "A custom-built Content Management System (CMS) designed for authors. It features markdown editing, user authentication, profile customizer, dynamic commenting sections, and tags search. Powered by a classic LAMP/LEMP stack architecture."
|
||||
|
||||
msgid "Self-driving Car in CARLA"
|
||||
msgstr "Self-driving Car in CARLA"
|
||||
|
||||
msgid "Deep Reinforcement Learning script enabling autonomous vehicle lane tracking and driving."
|
||||
msgstr "Deep Reinforcement Learning script enabling autonomous vehicle lane tracking and driving."
|
||||
|
||||
msgid "Programmed an autonomous driving agent inside the CARLA simulator. Utilized a deep reinforcement learning model (Keras/TensorFlow) mapping front-camera frames to steering and throttle actions. Trained the network using Deep Q-Learning (DQN) to maximize reward for staying in lanes and avoiding collisions."
|
||||
msgstr "Programmed an autonomous driving agent inside the CARLA simulator. Utilized a deep reinforcement learning model (Keras/TensorFlow) mapping front-camera frames to steering and throttle actions. Trained the network using Deep Q-Learning (DQN) to maximize reward for staying in lanes and avoiding collisions."
|
||||
|
||||
msgid "Video streaming ingestion from CARLA camera sensor nodes"
|
||||
msgstr "Video streaming ingestion from CARLA camera sensor nodes"
|
||||
|
||||
msgid "Frame preprocessing & downscaling using OpenCV"
|
||||
msgstr "Frame preprocessing & downscaling using OpenCV"
|
||||
|
||||
msgid "Deep Neural Network trained using Keras/TensorFlow framework"
|
||||
msgstr "Deep Neural Network trained using Keras/TensorFlow framework"
|
||||
|
||||
msgid "DQN reward structure optimization based on forward velocity and lane alignment"
|
||||
msgstr "DQN reward structure optimization based on forward velocity and lane alignment"
|
||||
|
||||
# Contact Section
|
||||
msgid "Get In Touch"
|
||||
msgstr "Get In Touch"
|
||||
|
||||
msgid "Have a question or want to discuss a project? Drop a message and I'll get back to you as soon as possible."
|
||||
msgstr "Have a question or want to discuss a project? Drop a message and I'll get back to you as soon as possible."
|
||||
|
||||
msgid "Let's build something together"
|
||||
msgstr "Let's build something together"
|
||||
|
||||
msgid "Whether you are looking to design a backend API, containerize a legacy application, or create an interactive frontend, I'd love to chat."
|
||||
msgstr "Whether you are looking to design a backend API, containerize a legacy application, or create an interactive frontend, I'd love to chat."
|
||||
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
|
||||
msgid "Location"
|
||||
msgstr "Location"
|
||||
|
||||
msgid "Quick Note:"
|
||||
msgstr "Quick Note:"
|
||||
|
||||
msgid " I speak Spanish, English, Italian, and basic German, making collaboration in multi-lingual teams very smooth!"
|
||||
msgstr " I speak Spanish, English, Italian, and basic German, making collaboration in multi-lingual teams very smooth!"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
msgid "Message"
|
||||
msgstr "Message"
|
||||
|
||||
msgid "Your Name"
|
||||
msgstr "Your Name"
|
||||
|
||||
msgid "Type your message here..."
|
||||
msgstr "Type your message here..."
|
||||
|
||||
msgid "Sending..."
|
||||
msgstr "Sending..."
|
||||
|
||||
msgid "Send Message"
|
||||
msgstr "Send Message"
|
||||
|
||||
msgid "Message Sent Successfully!"
|
||||
msgstr "Message Sent Successfully!"
|
||||
|
||||
msgid "Thank you for reaching out. I'll get back to you shortly."
|
||||
msgstr "Thank you for reaching out. I'll get back to you shortly."
|
||||
|
||||
msgid "Send Another Message"
|
||||
msgstr "Send Another Message"
|
||||
|
||||
# Contact Validation Errors
|
||||
msgid "Name is required"
|
||||
msgstr "Name is required"
|
||||
|
||||
msgid "Email is required"
|
||||
msgstr "Email is required"
|
||||
|
||||
msgid "Please enter a valid email address"
|
||||
msgstr "Please enter a valid email address"
|
||||
|
||||
msgid "Message cannot be empty"
|
||||
msgstr "Message cannot be empty"
|
||||
|
||||
# Footer
|
||||
msgid "© {{year}} Rafael Gonzalez Albes. All rights reserved."
|
||||
msgstr "© {{year}} Rafael Gonzalez Albes. All rights reserved."
|
||||
|
||||
msgid "Built with React, TypeScript & Docker."
|
||||
msgstr "Built with React, TypeScript & Docker."
|
||||
|
||||
# GitHub Activity Calendar
|
||||
msgid "GitHub Activity"
|
||||
msgstr "GitHub Activity"
|
||||
|
||||
msgid "contributions in the last year"
|
||||
msgstr "contributions in the last year"
|
||||
|
||||
msgid "Less"
|
||||
msgstr "Less"
|
||||
|
||||
msgid "More"
|
||||
msgstr "More"
|
||||
|
||||
msgid "Mon"
|
||||
msgstr "Mon"
|
||||
|
||||
msgid "Wed"
|
||||
msgstr "Wed"
|
||||
|
||||
msgid "Fri"
|
||||
msgstr "Fri"
|
||||
|
||||
msgid "Jan"
|
||||
msgstr "Jan"
|
||||
|
||||
msgid "Feb"
|
||||
msgstr "Feb"
|
||||
|
||||
msgid "Mar"
|
||||
msgstr "Mar"
|
||||
|
||||
msgid "Apr"
|
||||
msgstr "Apr"
|
||||
|
||||
msgid "May"
|
||||
msgstr "May"
|
||||
|
||||
msgid "Jun"
|
||||
msgstr "Jun"
|
||||
|
||||
msgid "Jul"
|
||||
msgstr "Jul"
|
||||
|
||||
msgid "Aug"
|
||||
msgstr "Aug"
|
||||
|
||||
msgid "Sep"
|
||||
msgstr "Sep"
|
||||
|
||||
msgid "Oct"
|
||||
msgstr "Oct"
|
||||
|
||||
msgid "Nov"
|
||||
msgstr "Nov"
|
||||
|
||||
msgid "Dec"
|
||||
msgstr "Dec"
|
||||
|
||||
msgid "contributions"
|
||||
msgstr "contributions"
|
||||
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
msgid "Low"
|
||||
msgstr "Low"
|
||||
|
||||
msgid "Medium"
|
||||
msgstr "Medium"
|
||||
|
||||
msgid "High"
|
||||
msgstr "High"
|
||||
|
||||
msgid "Very high"
|
||||
msgstr "Very high"
|
||||
|
||||
msgid "Databases & IoT"
|
||||
msgstr "Databases & IoT"
|
||||
|
||||
msgid "Augsburg, Germany — Sevilla, Spain"
|
||||
msgstr "Augsburg, Germany — Sevilla, Spain"
|
||||
|
||||
|
||||
msgid "Proactive & Autonomous Software Developer specializing in robust backend architectures, system optimization, and full-stack integration. Driven by curiosity and a lifetime passion for computing."
|
||||
msgstr "Proactive & Autonomous Software Developer specializing in robust backend architectures, system optimization, and full-stack integration. Driven by curiosity and a lifetime passion for computing."
|
||||
|
||||
msgid "A computer enthusiast since childhood with over <1>6 years of professional development experience</1> across Spain and Germany. Proven ability to adapt quickly through self-learning and autonomously solve complex architectural and engineering challenges without requiring permanent supervision. Adept at managing system administration, maintaining databases, and driving collaborative growth within agile team environments."
|
||||
msgstr "A computer enthusiast since childhood with over <1>6 years of professional development experience</1> across Spain and Germany. Proven ability to adapt quickly through self-learning and autonomously solve complex architectural and engineering challenges without requiring permanent supervision. Adept at managing system administration, maintaining databases, and driving collaborative growth within agile team environments."
|
||||
|
||||
msgid "Circadian Cycle (Auto)"
|
||||
msgstr "Circadian Cycle (Auto)"
|
||||
|
||||
msgid "Cycles layouts automatically based on the time of day. Daylight is clean editorial/soft neumorphism, afternoons are brutalist, evenings are dark developer mode, late nights are terminal command lines."
|
||||
msgstr "Cycles layouts automatically based on the time of day. Daylight is clean editorial/soft neumorphism, afternoons are brutalist, evenings are dark developer mode, late nights are terminal command lines."
|
||||
|
||||
msgid "Interactive 3D"
|
||||
msgstr "Interactive 3D"
|
||||
|
||||
msgid "An immersive layout featuring mouse-tracking 3D tilt cards, parallax effects, and a custom canvas-rendered 3D rotating starfield constellation."
|
||||
msgstr "An immersive layout featuring mouse-tracking 3D tilt cards, parallax effects, and a custom canvas-rendered 3D rotating starfield constellation."
|
||||
|
||||
msgid "Warm Editorial"
|
||||
msgstr "Warm Editorial"
|
||||
|
||||
msgid "Clean newspaper and blog style with high typography contrast, warm cream background, and spacious borders."
|
||||
msgstr "Clean newspaper and blog style with high typography contrast, warm cream background, and spacious borders."
|
||||
|
||||
msgid "Cyberpunk Neon"
|
||||
msgstr "Cyberpunk Neon"
|
||||
|
||||
msgid "Vibrant vaporwave style with fluorescent borders, glowing shadows, grid backgrounds, and tech scanner overlay."
|
||||
msgstr "Vibrant vaporwave style with fluorescent borders, glowing shadows, grid backgrounds, and tech scanner overlay."
|
||||
|
||||
msgid "Neo Brutalist"
|
||||
msgstr "Neo Brutalist"
|
||||
|
||||
msgid "High contrast flat design. Bright warning yellow panels, thick black border boxes, and solid shadows."
|
||||
msgstr "High contrast flat design. Bright warning yellow panels, thick black border boxes, and solid shadows."
|
||||
|
||||
msgid "Soft Neumorphism"
|
||||
msgstr "Soft Neumorphism"
|
||||
|
||||
msgid "Soft tactile layout using smooth beveled cards, extruded shadows, and recessed active states."
|
||||
msgstr "Soft tactile layout using smooth beveled cards, extruded shadows, and recessed active states."
|
||||
|
||||
msgid "Vivid aurora gradient backgrounds with glassmorphic cards, animated glows, and a modern linear layout."
|
||||
msgstr "Vivid aurora gradient backgrounds with glassmorphic cards, animated glows, and a modern linear layout."
|
||||
|
||||
msgid "Switch Theme"
|
||||
msgstr "Switch Theme"
|
||||
|
||||
msgid "Choose a template to experience the portfolio. You can switch back at any time."
|
||||
msgstr "Choose a template to experience the portfolio. You can switch back at any time."
|
||||
|
||||
msgid "Choose"
|
||||
msgstr "Choose"
|
||||
|
||||
msgid "Design Gallery"
|
||||
msgstr "Design Gallery"
|
||||
|
||||
msgid "Spoken Languages"
|
||||
msgstr "Spoken Languages"
|
||||
|
||||
msgid "Invalid email address"
|
||||
msgstr "Invalid email address"
|
||||
|
||||
msgid "Get in Touch"
|
||||
msgstr "Get in Touch"
|
||||
|
||||
msgid "Contact Me"
|
||||
msgstr "Contact Me"
|
||||
|
||||
msgid "I'm always open to discussing new projects, creative ideas, or opportunities. Feel free to reach out via email or send me a message!"
|
||||
msgstr "I'm always open to discussing new projects, creative ideas, or opportunities. Feel free to reach out via email or send me a message!"
|
||||
|
||||
msgid "Download CV"
|
||||
msgstr "Download CV"
|
||||
|
||||
msgid "Professional Software Developer"
|
||||
msgstr "Professional Software Developer"
|
||||
|
||||
msgid "Software Developer"
|
||||
msgstr "Software Developer"
|
||||
|
||||
msgid "Years Exp."
|
||||
msgstr "Years Exp."
|
||||
|
||||
msgid "Languages"
|
||||
msgstr "Languages"
|
||||
|
||||
msgid "Backend"
|
||||
msgstr "Backend"
|
||||
|
||||
msgid "Frontend"
|
||||
msgstr "Frontend"
|
||||
|
||||
msgid "GONZÁLEZ ALBES"
|
||||
msgstr "GONZÁLEZ ALBES"
|
||||
|
||||
msgid "The Engineer"
|
||||
msgstr "The Engineer"
|
||||
|
||||
msgid "Technical Skills"
|
||||
msgstr "Technical Skills"
|
||||
|
||||
msgid "Selected Projects"
|
||||
msgstr "Selected Projects"
|
||||
|
||||
msgid "Work Experience Timeline"
|
||||
msgstr "Work Experience Timeline"
|
||||
|
||||
msgid "Education Timeline"
|
||||
msgstr "Education Timeline"
|
||||
|
||||
msgid "I enjoy solving complex architectural challenges, designing robust APIs, containerizing deployment environments, and exploring deep learning models."
|
||||
msgstr "I enjoy solving complex architectural challenges, designing robust APIs, containerizing deployment environments, and exploring deep learning models."
|
||||
|
||||
msgid "Command not found. Type 'help' for available options."
|
||||
msgstr "Command not found. Type 'help' for available options."
|
||||
|
||||
msgid "A collection of featured projects and engineering work."
|
||||
msgstr "A collection of featured projects and engineering work."
|
||||
|
||||
msgid "Architected and optimized high-performance backend microservices and APIs utilizing PHP (Symfony), Python, and Go for comparative portal engines serving millions of monthly active users."
|
||||
msgstr "Architected and optimized high-performance backend microservices and APIs utilizing PHP (Symfony), Python, and Go for comparative portal engines serving millions of monthly active users."
|
||||
|
||||
msgid "Executed proactive server and database administration to ensure maximum uptime, scalability, and robust data integration pipelines."
|
||||
msgstr "Executed proactive server and database administration to ensure maximum uptime, scalability, and robust data integration pipelines."
|
||||
|
||||
msgid "Promoted from Junior Software Engineer to Professional Software Engineer in April 2024 in recognition of ownership of critical core systems."
|
||||
msgstr "Promoted from Junior Software Engineer to Professional Software Engineer in April 2024 in recognition of ownership of critical core systems."
|
||||
|
||||
msgid "Established containerized local test suites using Docker, reducing onboarding time for new developers and standardizing testing environments."
|
||||
msgstr "Established containerized local test suites using Docker, reducing onboarding time for new developers and standardizing testing environments."
|
||||
|
||||
msgid "Participated in agile Scrum ceremonies, writing comprehensive API specifications, and ensuring system maintenance and high availability."
|
||||
msgstr "Participated in agile Scrum ceremonies, writing comprehensive API specifications, and ensuring system maintenance and high availability."
|
||||
|
||||
msgid "Engineered interactive user interfaces using HTML5, CSS3, JavaScript, and JQuery, ensuring compliance with responsive web design principles."
|
||||
msgstr "Engineered interactive user interfaces using HTML5, CSS3, JavaScript, and JQuery, ensuring compliance with responsive web design principles."
|
||||
|
||||
msgid "Created custom backend logic, hooks, and modules using PHP and the Symfony framework to support e-commerce and media tools."
|
||||
msgstr "Created custom backend logic, hooks, and modules using PHP and the Symfony framework to support e-commerce and media tools."
|
||||
|
||||
msgid "Implemented and customized enterprise content management systems (CMS) using Typo3, writing custom TypoScript extensions."
|
||||
msgstr "Implemented and customized enterprise content management systems (CMS) using Typo3, writing custom TypoScript extensions."
|
||||
|
||||
msgid "Collaborated with product designers to map out system dependencies and architecture flows using formal UML diagrams."
|
||||
msgstr "Collaborated with product designers to map out system dependencies and architecture flows using formal UML diagrams."
|
||||
|
||||
msgid "Researched and implemented state-of-the-art recommendation systems based on collaborative filtering and fuzzy clustering models."
|
||||
msgstr "Researched and implemented state-of-the-art recommendation systems based on collaborative filtering and fuzzy clustering models."
|
||||
|
||||
msgid "Processed and analyzed large consumer transaction datasets using Python (Pandas, NumPy) to evaluate recommender accuracy."
|
||||
msgstr "Processed and analyzed large consumer transaction datasets using Python (Pandas, NumPy) to evaluate recommender accuracy."
|
||||
|
||||
msgid "Designed and query-tuned graph-oriented database structures in Neo4j to model user-product buying patterns in real time."
|
||||
msgstr "Designed and query-tuned graph-oriented database structures in Neo4j to model user-product buying patterns in real time."
|
||||
|
||||
msgid "Built supporting web portal features in JavaScript, PHP, and Symfony for academic research evaluations."
|
||||
msgstr "Built supporting web portal features in JavaScript, PHP, and Symfony for academic research evaluations."
|
||||
|
||||
msgid "Developed cross-platform mobile applications for iOS and Android using the Ionic Framework and Angular."
|
||||
msgstr "Developed cross-platform mobile applications for iOS and Android using the Ionic Framework and Angular."
|
||||
|
||||
msgid "Created automated enterprise scripts, notifications, and sheet integrations using Google Apps Script."
|
||||
msgstr "Created automated enterprise scripts, notifications, and sheet integrations using Google Apps Script."
|
||||
|
||||
msgid "Managed Linux production and staging servers, configuring Apache/Nginx web servers, security groups, and SSH configurations."
|
||||
msgstr "Managed Linux production and staging servers, configuring Apache/Nginx web servers, security groups, and SSH configurations."
|
||||
|
||||
msgid "Maintained legacy client websites, writing custom MySQL queries, and migrating servers with minimal downtime."
|
||||
msgstr "Maintained legacy client websites, writing custom MySQL queries, and migrating servers with minimal downtime."
|
||||
|
||||
msgid "Degree in Computer Engineering in Information Systems"
|
||||
msgstr "Degree in Computer Engineering in Information Systems"
|
||||
|
||||
msgid "Graduated with a thesis on Recommender Systems. Coursework: Advanced Algorithms, Database Management Systems, Software Engineering Patterns, Networks, and Operating Systems."
|
||||
msgstr "Graduated with a thesis on Recommender Systems. Coursework: Advanced Algorithms, Database Management Systems, Software Engineering Patterns, Networks, and Operating Systems."
|
||||
|
||||
msgid "Master's Degree in Computer Science and Engineering"
|
||||
msgstr "Master's Degree in Computer Science and Engineering"
|
||||
|
||||
msgid "Erasmus exchange year focusing on high-level systems. Coursework: Software Architectures, Distributed Systems, Advanced Web Technologies, Machine Learning, and Information Retrieval."
|
||||
msgstr "Erasmus exchange year focusing on high-level systems. Coursework: Software Architectures, Distributed Systems, Advanced Web Technologies, Machine Learning, and Information Retrieval."
|
||||
|
||||
msgid "Certificate of Higher Education - Multiplatform App Development"
|
||||
msgstr "Certificate of Higher Education - Multiplatform App Development"
|
||||
|
||||
msgid "Focused on native Android development (Java), database design, cross-platform UI architectures, and project management methodologies."
|
||||
msgstr "Focused on native Android development (Java), database design, cross-platform UI architectures, and project management methodologies."
|
||||
|
||||
msgid "Certificate of Higher Education - Web App Development"
|
||||
msgstr "Certificate of Higher Education - Web App Development"
|
||||
|
||||
msgid "Practical curriculum covering HTML5/CSS3/JavaScript, backend scripting in PHP, database engines (SQL), and deployment of web servers."
|
||||
msgstr "Practical curriculum covering HTML5/CSS3/JavaScript, backend scripting in PHP, database engines (SQL), and deployment of web servers."
|
||||
|
||||
msgid "Vocational Training in Microcomputer Systems & Networks"
|
||||
msgstr "Vocational Training in Microcomputer Systems & Networks"
|
||||
|
||||
msgid "Hands-on foundation in computer networks, operating system configuration, PC hardware assembly, and network troubleshooting."
|
||||
msgstr "Hands-on foundation in computer networks, operating system configuration, PC hardware assembly, and network troubleshooting."
|
||||
|
||||
msgid "Professional Certifications"
|
||||
msgstr "Professional Certifications"
|
||||
|
||||
msgid "Oxford Test of English (Level B1)"
|
||||
msgstr "Oxford Test of English (Level B1)"
|
||||
|
||||
msgid "Technical Certifications: Advanced C++, Java Enterprise, and SQL Database Design."
|
||||
msgstr "Technical Certifications: Advanced C++, Java Enterprise, and SQL Database Design."
|
||||
|
||||
msgid "Work"
|
||||
msgstr "Work"
|
||||
|
||||
msgid "Timeline"
|
||||
msgstr "Timeline"
|
||||
|
||||
msgid "I'm open for collaboration and backend architectures development."
|
||||
msgstr "I'm open for collaboration and backend architectures development."
|
||||
|
||||
msgid "Connect"
|
||||
msgstr "Connect"
|
||||
|
||||
msgid "Mediaconcept GmbH (Germany)"
|
||||
msgstr "Mediaconcept GmbH (Germany)"
|
||||
|
||||
msgid "University of Bologna (Erasmus)"
|
||||
msgstr "University of Bologna (Erasmus)"
|
||||
|
||||
msgid "I.E.S Velázquez"
|
||||
msgstr "I.E.S Velázquez"
|
||||
|
||||
msgid "Center Nuevas Profesiones"
|
||||
msgstr "Center Nuevas Profesiones"
|
||||
|
||||
msgid "I.E.S Axati"
|
||||
msgstr "I.E.S Axati"
|
||||
|
||||
msgid "Various Institutions"
|
||||
msgstr "Various Institutions"
|
||||
|
||||
msgid "Online & Academic"
|
||||
msgstr "Online & Academic"
|
||||
|
||||
msgid "Writing clean, testable code"
|
||||
msgstr "Writing clean, testable code"
|
||||
|
||||
msgid "PHP & Symfony"
|
||||
msgstr "PHP & Symfony"
|
||||
|
||||
msgid "Senior / Expert"
|
||||
msgstr "Senior / Expert"
|
||||
|
||||
msgid "Python (Deep Learning)"
|
||||
msgstr "Python (Deep Learning)"
|
||||
|
||||
msgid "Advanced / Intermediate"
|
||||
msgstr "Advanced / Intermediate"
|
||||
|
||||
msgid "Java / OOP"
|
||||
msgstr "Java / OOP"
|
||||
|
||||
msgid "Advanced"
|
||||
msgstr "Advanced"
|
||||
|
||||
msgid "C / C++"
|
||||
msgstr "C / C++"
|
||||
|
||||
msgid "Intermediate"
|
||||
msgstr "Intermediate"
|
||||
|
||||
msgid "Server Maintenance & Bash"
|
||||
msgstr "Server Maintenance & Bash"
|
||||
|
||||
msgid "JavaScript / TypeScript"
|
||||
msgstr "JavaScript / TypeScript"
|
||||
|
||||
msgid "Senior"
|
||||
msgstr "Senior"
|
||||
|
||||
msgid "HTML5 & CSS3"
|
||||
msgstr "HTML5 & CSS3"
|
||||
|
||||
msgid "Expert"
|
||||
msgstr "Expert"
|
||||
|
||||
msgid "JQuery & Bootstrap"
|
||||
msgstr "JQuery & Bootstrap"
|
||||
|
||||
msgid "React"
|
||||
msgstr "React"
|
||||
|
||||
msgid "SQL (MySQL, PostgreSQL)"
|
||||
msgstr "SQL (MySQL, PostgreSQL)"
|
||||
|
||||
msgid "Neo4j (Graph DB)"
|
||||
msgstr "Neo4j (Graph DB)"
|
||||
|
||||
msgid "Docker / Containers"
|
||||
msgstr "Docker / Containers"
|
||||
|
||||
msgid "IoT (Arduino, ESP8266)"
|
||||
msgstr "IoT (Arduino, ESP8266)"
|
||||
|
||||
msgid "Aurora Glass"
|
||||
msgstr "Aurora Glass"
|
||||
|
||||
msgid "Time Distortion Active: Cycling layouts..."
|
||||
msgstr "Time Distortion Active: Cycling layouts..."
|
||||
|
||||
msgid "GitHub Theme"
|
||||
msgstr "GitHub Theme"
|
||||
|
||||
msgid "Sleek developer profile inspired by GitHub's dark mode, green commits activity accents, and clean layout."
|
||||
msgstr "Sleek developer profile inspired by GitHub's dark mode, green commits activity accents, and clean layout."
|
||||
|
||||
msgid "Interactive CLI"
|
||||
msgstr "Interactive CLI"
|
||||
|
||||
msgid "Interactive command-line shell interface. Fast, retro-tech styled, with quick commands selector panel."
|
||||
msgstr "Interactive command-line shell interface. Fast, retro-tech styled, with quick commands selector panel."
|
||||
|
||||
msgid "Bento Grid"
|
||||
msgstr "Bento Grid"
|
||||
|
||||
msgid "A modern layout grouping technical categories and timelines as frosted glass tiles with neon purple shadows."
|
||||
msgstr "A modern layout grouping technical categories and timelines as frosted glass tiles with neon purple shadows."
|
||||
|
||||
msgid "Social Profiles"
|
||||
msgstr "Social Profiles"
|
||||
|
||||
msgid "SYSTEM_ACTIVE"
|
||||
msgstr "SYSTEM_ACTIVE"
|
||||
|
||||
msgid "Core Stack"
|
||||
msgstr "Core Stack"
|
||||
|
||||
msgid "Developer Profile"
|
||||
msgstr "Developer Profile"
|
||||
|
||||
msgid "Chronology"
|
||||
msgstr "Chronology"
|
||||
|
||||
msgid "Project Modules"
|
||||
msgstr "Project Modules"
|
||||
|
||||
msgid "Welcome to Rafael Gonzalez Albes CLI Portfolio v1.0.0"
|
||||
msgstr "Welcome to Rafael Gonzalez Albes CLI Portfolio v1.0.0"
|
||||
|
||||
msgid "Type 'help' to view all commands, or click the links on the right panel to execute them instantly."
|
||||
msgstr "Type 'help' to view all commands, or click the links on the right panel to execute them instantly."
|
||||
|
||||
msgid "About Me & Developer Journey"
|
||||
msgstr "About Me & Developer Journey"
|
||||
|
||||
msgid "Technical Skills & Expertise"
|
||||
msgstr "Technical Skills & Expertise"
|
||||
|
||||
msgid "Personal Projects List"
|
||||
msgstr "Personal Projects List"
|
||||
|
||||
msgid "Contact Info & Socials"
|
||||
msgstr "Contact Info & Socials"
|
||||
|
||||
msgid "Clear the terminal screen"
|
||||
msgstr "Clear the terminal screen"
|
||||
|
||||
msgid "Change language"
|
||||
msgstr "Change language"
|
||||
|
||||
msgid "Language changed to English"
|
||||
msgstr "Language changed to English"
|
||||
|
||||
msgid "Language changed to Spanish"
|
||||
msgstr "Language changed to Spanish"
|
||||
|
||||
msgid "Language changed to Italian"
|
||||
msgstr "Language changed to Italian"
|
||||
|
||||
msgid "Language changed to German"
|
||||
msgstr "Language changed to German"
|
||||
|
||||
msgid "Quick Commands"
|
||||
msgstr "Quick Commands"
|
||||
|
||||
msgid "Click any command to run it in the terminal output grid."
|
||||
msgstr "Click any command to run it in the terminal output grid."
|
||||
973
src/locales/es.po
Normal file
|
|
@ -0,0 +1,973 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es\n"
|
||||
|
||||
# Navbar Links
|
||||
msgid "Home"
|
||||
msgstr "Inicio"
|
||||
|
||||
msgid "About"
|
||||
msgstr "Sobre Mí"
|
||||
|
||||
msgid "Skills"
|
||||
msgstr "Habilidades"
|
||||
|
||||
msgid "Experience"
|
||||
msgstr "Experiencia"
|
||||
|
||||
msgid "Projects"
|
||||
msgstr "Proyectos"
|
||||
|
||||
msgid "Contact"
|
||||
msgstr "Contacto"
|
||||
|
||||
# Hero Section
|
||||
msgid "Available for new opportunities"
|
||||
msgstr "Disponible para nuevas oportunidades"
|
||||
|
||||
msgid "Building Scalable"
|
||||
msgstr "Construyendo Sistemas"
|
||||
|
||||
msgid "Backend Systems"
|
||||
msgstr "Backend Escalables"
|
||||
|
||||
msgid "Seamless Interfaces"
|
||||
msgstr "Interfaces Intuitivas"
|
||||
|
||||
msgid "I am a "
|
||||
msgstr "Soy "
|
||||
|
||||
msgid "Full-Stack Developer"
|
||||
msgstr "Desarrollador Full-Stack"
|
||||
|
||||
msgid "Backend Specialist"
|
||||
msgstr "Especialista en Backend"
|
||||
|
||||
msgid "API Designer"
|
||||
msgstr "Diseñador de APIs"
|
||||
|
||||
msgid "System Thinker"
|
||||
msgstr "Pensador de Sistemas"
|
||||
|
||||
msgid "Professional Software Developer with 4.5+ years of experience building high-performance backend APIs, handling databases, and crafting clean frontend applications. Specialized in PHP (Symfony), TypeScript, and containerized Docker environments."
|
||||
msgstr "Desarrollador de software profesional con más de 4.5 años de experiencia en la creación de APIs backend de alto rendimiento, gestión de bases de datos y desarrollo de aplicaciones frontend limpias. Especializado en PHP (Symfony), TypeScript y entornos Docker contenedorizados."
|
||||
|
||||
msgid "Explore Projects"
|
||||
msgstr "Explorar Proyectos"
|
||||
|
||||
msgid "Let's Talk"
|
||||
msgstr "Hablemos"
|
||||
|
||||
msgid "Run Output: "
|
||||
msgstr "Resultado: "
|
||||
|
||||
msgid "🚀 Local execution ready"
|
||||
msgstr "🚀 Ejecución local lista"
|
||||
|
||||
msgid "⚠️ Dependency missing"
|
||||
msgstr "⚠️ Falta dependencia"
|
||||
|
||||
# About Section
|
||||
msgid "About Me"
|
||||
msgstr "Sobre Mí"
|
||||
|
||||
msgid "My Journey as a Developer"
|
||||
msgstr "Mi trayectoria como desarrollador"
|
||||
|
||||
msgid "I hold a <1>Degree in Computer Engineering in Information Systems</1> from Seville, Spain, and spent an exchange year completing <3>Master's coursework in Computer Science and Engineering</3> at the prestigious <5>University of Bologna</5> in Italy."
|
||||
msgstr "Soy <1>Graduado en Ingeniería Informática en Sistemas de Información</1> por Sevilla, España, y pasé un año de intercambio cursando asignaturas de <3>Máster en Ciencia e Ingeniería Informática</3> en la prestigiosa <5>Universidad de Bolonia</5> en Italia."
|
||||
|
||||
msgid "My foundation in software engineering is reinforced by extensive vocational training in web and multiplatform application development, combined with over <1>4 years of professional experience</1> in Germany's leading comparison portal (CHECK24) and media agencies."
|
||||
msgstr "Mi base en ingeniería de software está reforzada por una amplia formación profesional en desarrollo de aplicaciones web y multiplataforma, combinada con más de <1>4 años de experiencia profesional</1> en el portal de comparación líder de Alemania (CHECK24) y agencias de medios."
|
||||
|
||||
msgid "I enjoy solving complex architectural challenges, designing robust APIs, containerizing deployment environments, and exploring deep learning models. Multi-lingual and highly adaptable, I am comfortable collaborating across international, cross-functional teams."
|
||||
msgstr "Disfruto resolviendo desafíos arquitectónicos complejos, diseñando APIs robustas, contenedorizando entornos de despliegue y explorando modelos de aprendizaje profundo. Multilingüe y muy adaptable, me siento cómodo colaborando en equipos internacionales y multifuncionales."
|
||||
|
||||
msgid "Years Experience"
|
||||
msgstr "Años de Experiencia"
|
||||
|
||||
msgid "Languages Spoken"
|
||||
msgstr "Idiomas Hablados"
|
||||
|
||||
msgid "Academic Qualifications"
|
||||
msgstr "Cualificaciones Académicas"
|
||||
|
||||
msgid "Personal Projects"
|
||||
msgstr "Proyectos Personales"
|
||||
|
||||
msgid "Certifications"
|
||||
msgstr "Certificaciones"
|
||||
|
||||
msgid "Oxford Test of English (B1)"
|
||||
msgstr "Oxford Test of English (B1)"
|
||||
|
||||
msgid "Oxford University Press"
|
||||
msgstr "Oxford University Press"
|
||||
|
||||
msgid "C++ Certification"
|
||||
msgstr "Certificación de C++"
|
||||
|
||||
msgid "Programming Institute"
|
||||
msgstr "Programming Institute"
|
||||
|
||||
msgid "Java Certification"
|
||||
msgstr "Certificación de Java"
|
||||
|
||||
msgid "Oracle Certified Associate"
|
||||
msgstr "Oracle Certified Associate"
|
||||
|
||||
msgid "SQL Developer Certification"
|
||||
msgstr "Certificación de Desarrollador SQL"
|
||||
|
||||
msgid "Database Systems"
|
||||
msgstr "Sistemas de Bases de Datos"
|
||||
|
||||
# Languages
|
||||
msgid "Spanish"
|
||||
msgstr "Español"
|
||||
|
||||
msgid "English"
|
||||
msgstr "Inglés"
|
||||
|
||||
msgid "Italian"
|
||||
msgstr "Italiano"
|
||||
|
||||
msgid "German"
|
||||
msgstr "Alemán"
|
||||
|
||||
msgid "French"
|
||||
msgstr "Francés"
|
||||
|
||||
msgid "Native / Bilingual"
|
||||
msgstr "Nativo / Bilingüe"
|
||||
|
||||
msgid "Professional (B1 Certified)"
|
||||
msgstr "Profesional (Certificado B1)"
|
||||
|
||||
msgid "Conversational"
|
||||
msgstr "Conversacional"
|
||||
|
||||
msgid "Basic / Learning"
|
||||
msgstr "Básico / Aprendiendo"
|
||||
|
||||
# Skills Section
|
||||
msgid "Technical Expertise"
|
||||
msgstr "Experiencia Técnica"
|
||||
|
||||
msgid "A robust technical foundation bridging low-level systems (C++), high-concurrency web systems (PHP, Node.js), and data analytics (Python, Neo4j)."
|
||||
msgstr "Una sólida base técnica que conecta sistemas de bajo nivel (C++), sistemas web de alta concurrencia (PHP, Node.js) y análisis de datos (Python, Neo4j)."
|
||||
|
||||
msgid "Backend & Systems"
|
||||
msgstr "Backend y Sistemas"
|
||||
|
||||
msgid "Frontend & UI"
|
||||
msgstr "Frontend e Interfaz"
|
||||
|
||||
msgid "Databases & Technologies"
|
||||
msgstr "Bases de Datos y Tecnologías"
|
||||
|
||||
# Experience Section
|
||||
msgid "My Timeline"
|
||||
msgstr "Mi Trayectoria"
|
||||
|
||||
msgid "Work Experience"
|
||||
msgstr "Experiencia Laboral"
|
||||
|
||||
msgid "Education"
|
||||
msgstr "Educación"
|
||||
|
||||
msgid "Check24 (Germany)"
|
||||
msgstr "Check24 (Alemania)"
|
||||
|
||||
msgid "Augsburg, Bavaria, Germany"
|
||||
msgstr "Augsburgo, Baviera, Alemania"
|
||||
|
||||
msgid "Ulm, Baden-Wurttemberg, Germany"
|
||||
msgstr "Ulm, Baden-Wurtemberg, Alemania"
|
||||
|
||||
msgid "Seville, Spain"
|
||||
msgstr "Sevilla, España"
|
||||
|
||||
msgid "Bologna, Italy"
|
||||
msgstr "Bolonia, Italia"
|
||||
|
||||
msgid "Full Stack Web Developer"
|
||||
msgstr "Desarrollador Web Full Stack"
|
||||
|
||||
msgid "Student Intern (Recommender Systems)"
|
||||
msgstr "Estudiante en Prácticas (Sistemas de Recomendación)"
|
||||
|
||||
msgid "Web Developer (JS, PHP, & Symfony)"
|
||||
msgstr "Desarrollador Web (JS, PHP y Symfony)"
|
||||
|
||||
msgid "University Pablo de Olavide"
|
||||
msgstr "Universidad Pablo de Olavide"
|
||||
|
||||
msgid "Green Slope S.L."
|
||||
msgstr "Green Slope S.L."
|
||||
|
||||
# Experience Details
|
||||
msgid "Backend development specializing in PHP, building scalable services and microservices."
|
||||
msgstr "Desarrollo backend especializado en PHP, construyendo servicios y microservicios escalables."
|
||||
|
||||
msgid "Creation, optimization, and integration of robust RESTful/SOAP APIs."
|
||||
msgstr "Creación, optimización e integración de APIs RESTful/SOAP robustas."
|
||||
|
||||
msgid "Local dockerization setups and automated testing pipeline maintenance."
|
||||
msgstr "Configuraciones de dockerización local y mantenimiento de pipelines de pruebas automatizadas."
|
||||
|
||||
msgid "Large-scale database handling, schema migrations, and query performance tuning."
|
||||
msgstr "Gestión de bases de datos a gran escala, migraciones de esquema y optimización del rendimiento de consultas."
|
||||
|
||||
msgid "Frontend web development utilizing HTML5, CSS3, and JavaScript/TypeScript."
|
||||
msgstr "Desarrollo web frontend utilizando HTML5, CSS3 y JavaScript/TypeScript."
|
||||
|
||||
msgid "Backend development using Symfony PHP framework for production applications."
|
||||
msgstr "Desarrollo backend utilizando el framework Symfony PHP para aplicaciones de producción."
|
||||
|
||||
msgid "Content management system (CMS) integration and customization using Typo3."
|
||||
msgstr "Integración y personalización de sistemas de gestión de contenidos (CMS) utilizando Typo3."
|
||||
|
||||
msgid "Creation of software designs using UML diagrams for team collaboration."
|
||||
msgstr "Creación de diseños de software utilizando diagramas UML para la colaboración en equipo."
|
||||
|
||||
msgid "Researched and built recommendation algorithms based on collaborative filtering."
|
||||
msgstr "Investigación y desarrollo de algoritmos de recomendación basados en filtrado colaborativo."
|
||||
|
||||
msgid "Developed fuzzy clustering engines using Python, pandas, and numpy."
|
||||
msgstr "Desarrollo de motores de clustering difuso utilizando Python, pandas y numpy."
|
||||
|
||||
msgid "Utilized graph-oriented databases (Neo4j) to model product/user relationships."
|
||||
msgstr "Uso de bases de datos orientadas a grafos (Neo4j) para modelar relaciones producto/usuario."
|
||||
|
||||
msgid "Developed cross-platform mobile apps using the Ionic Framework."
|
||||
msgstr "Desarrollo de aplicaciones móviles multiplataforma utilizando Ionic Framework."
|
||||
|
||||
msgid "Built automated scripts and workflows using Google Apps Script."
|
||||
msgstr "Construcción de scripts y flujos de trabajo automatizados usando Google Apps Script."
|
||||
|
||||
msgid "Managed and maintained Linux production servers and VPS environments."
|
||||
msgstr "Gestión y mantenimiento de servidores de producción Linux y entornos VPS."
|
||||
|
||||
msgid "Specialized in information systems design, algorithms, databases, and software design patterns."
|
||||
msgstr "Especialización en diseño de sistemas de información, algoritmos, bases de datos y patrones de diseño de software."
|
||||
|
||||
msgid "Completed advanced master coursework in software architectures, web technologies, and systems engineering during Erasmus exchange."
|
||||
msgstr "Cursado de asignaturas avanzadas de máster en arquitectura de software, tecnologías web e ingeniería de sistemas durante el intercambio Erasmus."
|
||||
|
||||
msgid "Hands-on vocational program focusing on mobile development (Android, iOS) and cross-platform desktop frameworks."
|
||||
msgstr "Programa de formación profesional práctica enfocado en el desarrollo móvil (Android, iOS) y frameworks de escritorio multiplataforma."
|
||||
|
||||
msgid "Practical training focused on front-end layouts, back-end scripting, SQL databases, and web servers."
|
||||
msgstr "Formación práctica centrada en maquetación front-end, scripting back-end, bases de datos SQL y servidores web."
|
||||
|
||||
msgid "Introduction to hardware assembly, operating systems installation, router configuration, and local network diagnostics."
|
||||
msgstr "Introducción al montaje de hardware, instalación de sistemas operativos, configuración de routers y diagnóstico de redes locales."
|
||||
|
||||
# Projects Section
|
||||
msgid "Personal Projects"
|
||||
msgstr "Proyectos Personales"
|
||||
|
||||
msgid "A curated selection of personal creations showing my growth across IoT, Deep Learning, Mobile development, and system designs."
|
||||
msgstr "Una selección de mis creaciones personales que muestran mi crecimiento en IoT, Deep Learning, desarrollo móvil y diseño de sistemas."
|
||||
|
||||
msgid "All Projects"
|
||||
msgstr "Todos"
|
||||
|
||||
msgid "AI & Data"
|
||||
msgstr "IA y Datos"
|
||||
|
||||
msgid "Web & IoT"
|
||||
msgstr "Web e IoT"
|
||||
|
||||
msgid "Mobile Apps"
|
||||
msgstr "Apps Móviles"
|
||||
|
||||
msgid "Learn More"
|
||||
msgstr "Saber Más"
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "Visión General"
|
||||
|
||||
msgid "Technical Flow & Pattern"
|
||||
msgstr "Flujo Técnico y Patrones"
|
||||
|
||||
msgid "Tech Stack"
|
||||
msgstr "Tecnologías Usadas"
|
||||
|
||||
msgid "View on GitHub"
|
||||
msgstr "Ver en GitHub"
|
||||
|
||||
# Specific Projects
|
||||
msgid "Supermarket Recommender System"
|
||||
msgstr "Sistema Recomendador de Supermercados"
|
||||
|
||||
msgid "A hybrid recommendation engine utilizing fuzzy clustering and graph databases for market basket analysis."
|
||||
msgstr "Motor de recomendación híbrido que utiliza clustering difuso y bases de datos orientadas a grafos para el análisis de la cesta de la compra."
|
||||
|
||||
msgid "Developed a high-accuracy recommender system that analyzes consumer purchasing history and recommends items based on fuzzy clustering groups. Built a graph network in Neo4j to model user-to-product buy relationships, allowing fast collaborative filtering queries."
|
||||
msgstr "Desarrollador de un sistema de recomendación de alta precisión que analiza el historial de compras de los consumidores y recomienda productos basándose en grupos de clustering difuso. Construcción de una red de grafos en Neo4j para modelar relaciones de compra usuario-producto, permitiendo consultas rápidas de filtrado colaborativo."
|
||||
|
||||
msgid "Data extraction & cleaning via Pandas and NumPy"
|
||||
msgstr "Extracción y limpieza de datos con Pandas y NumPy"
|
||||
|
||||
msgid "Fuzzy C-Means Clustering to group similar purchasing baskets"
|
||||
msgstr "Clustering difuso C-Means para agrupar cestas de compra similares"
|
||||
|
||||
msgid "Graph DB mapping of nodes (Customers, Products, Categories) in Neo4j"
|
||||
msgstr "Mapeo en base de datos de grafos de nodos (Clientes, Productos, Categorías) en Neo4j"
|
||||
|
||||
msgid "Real-time neighbor matching query logic via Cypher"
|
||||
msgstr "Lógica de consulta de emparejamiento de vecinos en tiempo real mediante Cypher"
|
||||
|
||||
msgid "IoT Smart Dumpster"
|
||||
msgstr "Contenedor Inteligente IoT"
|
||||
|
||||
msgid "Intelligent trash container telemetry network using ESP8266, custom PHP backend, and Android monitoring."
|
||||
msgstr "Red de telemetría de contenedores de basura inteligentes mediante ESP8266, backend PHP personalizado y monitorización Android."
|
||||
|
||||
msgid "Designed a smart container system powered by ESP8266 nodeMCU v3 microcontrollers. It uses ultrasonic sensors to measure fill levels, communicates with a PHP API via encrypted HTTP POST (using MD5 signature verification), and reports real-time dashboard data to a companion Android app."
|
||||
msgstr "Diseño de un sistema de contenedor inteligente alimentado por microcontroladores ESP8266 nodeMCU v3. Utiliza sensores de ultrasonido para medir los niveles de llenado, se comunica con una API PHP mediante HTTP POST cifrado (con verificación de firma MD5) y reporta datos del panel en tiempo real a una aplicación Android complementaria."
|
||||
|
||||
msgid "ESP8266 telemetry node running on C++/Arduino sketch"
|
||||
msgstr "Nodo de telemetría ESP8266 basado en C++/Arduino sketch"
|
||||
|
||||
msgid "PHP Web API endpoint with MD5 request checksum validation"
|
||||
msgstr "Endpoint de API Web en PHP con validación de suma de comprobación MD5"
|
||||
|
||||
msgid "Background Scheduler running task loops to identify full units"
|
||||
msgstr "Planificador en segundo plano que ejecuta bucles de tareas para identificar unidades llenas"
|
||||
|
||||
msgid "Android Client using HTTP endpoints to fetch status maps"
|
||||
msgstr "Cliente Android que usa endpoints HTTP para obtener mapas de estado"
|
||||
|
||||
msgid "Animal Adoption Mobile App"
|
||||
msgstr "App Móvil de Adopción de Animales"
|
||||
|
||||
msgid "A native mobile application connecting animal shelters with prospective adopters."
|
||||
msgstr "Aplicación móvil nativa que conecta refugios de animales con posibles adoptantes."
|
||||
|
||||
msgid "A native Android mobile application that aggregates profiles of adoptable pets from local shelters. Allows users to filter by breed, age, location, and apply for adoption directly through the platform. Connects to a secure MySQL database for real-time applications handling."
|
||||
msgstr "Aplicación móvil nativa para Android que agrega perfiles de mascotas adoptables de refugios locales. Permite a los usuarios filtrar por raza, edad, ubicación y solicitar la adopción directamente. Se conecta a una base de datos MySQL segura para gestionar las solicitudes en tiempo real."
|
||||
|
||||
msgid "Custom Blogging Platform"
|
||||
msgstr "Plataforma de Blog Personalizada"
|
||||
|
||||
msgid "A full-featured CMS web application for blogging and publishing article contents."
|
||||
msgstr "Aplicación web CMS completa para publicar artículos y blogs."
|
||||
|
||||
msgid "A custom-built Content Management System (CMS) designed for authors. It features markdown editing, user authentication, profile customizer, dynamic commenting sections, and tags search. Powered by a classic LAMP/LEMP stack architecture."
|
||||
msgstr "Sistema de Gestión de Contenidos (CMS) personalizado diseñado para autores. Cuenta con edición en markdown, autenticación de usuarios, personalización de perfiles, secciones de comentarios dinámicas y búsqueda por etiquetas. Funciona bajo una arquitectura clásica de pila LAMP/LEMP."
|
||||
|
||||
msgid "Self-driving Car in CARLA"
|
||||
msgstr "Coche Autónomo en CARLA"
|
||||
|
||||
msgid "Deep Reinforcement Learning script enabling autonomous vehicle lane tracking and driving."
|
||||
msgstr "Script de aprendizaje por refuerzo profundo que permite el seguimiento de carril y la conducción autónoma de vehículos."
|
||||
|
||||
msgid "Programmed an autonomous driving agent inside the CARLA simulator. Utilized a deep reinforcement learning model (Keras/TensorFlow) mapping front-camera frames to steering and throttle actions. Trained the network using Deep Q-Learning (DQN) to maximize reward for staying in lanes and avoiding collisions."
|
||||
msgstr "Programación de un agente de conducción autónoma en el simulador CARLA. Utilizó un modelo de aprendizaje por refuerzo profundo (Keras/TensorFlow) que asocia los fotogramas de la cámara frontal con acciones de dirección y aceleración. Se entrenó la red mediante Deep Q-Learning (DQN) para maximizar la recompensa al mantenerse en los carriles y evitar colisiones."
|
||||
|
||||
msgid "Video streaming ingestion from CARLA camera sensor nodes"
|
||||
msgstr "Ingesta de flujo de video desde nodos de sensores de cámara CARLA"
|
||||
|
||||
msgid "Frame preprocessing & downscaling using OpenCV"
|
||||
msgstr "Preprocesamiento y reducción de tamaño de fotogramas con OpenCV"
|
||||
|
||||
msgid "Deep Neural Network trained using Keras/TensorFlow framework"
|
||||
msgstr "Red neuronal profunda entrenada usando Keras/TensorFlow"
|
||||
|
||||
msgid "DQN reward structure optimization based on forward velocity and lane alignment"
|
||||
msgstr "Optimización de la estructura de recompensa DQN basada en la velocidad de avance y la alineación del carril"
|
||||
|
||||
# Contact Section
|
||||
msgid "Get In Touch"
|
||||
msgstr "Ponte en Contacto"
|
||||
|
||||
msgid "Have a question or want to discuss a project? Drop a message and I'll get back to you as soon as possible."
|
||||
msgstr "¿Tienes alguna pregunta o quieres hablar de un proyecto? Deja un mensaje y te responderé lo antes posible."
|
||||
|
||||
msgid "Let's build something together"
|
||||
msgstr "Construyamos algo juntos"
|
||||
|
||||
msgid "Whether you are looking to design a backend API, containerize a legacy application, or create an interactive frontend, I'd love to chat."
|
||||
msgstr "Tanto si buscas diseñar una API backend, contenedorizar una aplicación heredada o crear un frontend interactivo, me encantaría hablar."
|
||||
|
||||
msgid "Email"
|
||||
msgstr "Correo Electrónico"
|
||||
|
||||
msgid "Location"
|
||||
msgstr "Ubicación"
|
||||
|
||||
msgid "Quick Note:"
|
||||
msgstr "Nota rápida:"
|
||||
|
||||
msgid " I speak Spanish, English, Italian, and basic German, making collaboration in multi-lingual teams very smooth!"
|
||||
msgstr " ¡Hablo español, inglés, italiano y alemán básico, lo que facilita enormemente la colaboración en equipos multilingües!"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
||||
msgid "Message"
|
||||
msgstr "Mensaje"
|
||||
|
||||
msgid "Your Name"
|
||||
msgstr "Tu Nombre"
|
||||
|
||||
msgid "Type your message here..."
|
||||
msgstr "Escribe tu mensaje aquí..."
|
||||
|
||||
msgid "Sending..."
|
||||
msgstr "Enviando..."
|
||||
|
||||
msgid "Send Message"
|
||||
msgstr "Enviar Mensaje"
|
||||
|
||||
msgid "Message Sent Successfully!"
|
||||
msgstr "¡Mensaje Enviado con Éxito!"
|
||||
|
||||
msgid "Thank you for reaching out. I'll get back to you shortly."
|
||||
msgstr "Gracias por ponerte en contacto. Te responderé a la brevedad."
|
||||
|
||||
msgid "Send Another Message"
|
||||
msgstr "Enviar otro mensaje"
|
||||
|
||||
# Contact Validation Errors
|
||||
msgid "Name is required"
|
||||
msgstr "El nombre es obligatorio"
|
||||
|
||||
msgid "Email is required"
|
||||
msgstr "El correo es obligatorio"
|
||||
|
||||
msgid "Please enter a valid email address"
|
||||
msgstr "Por favor, introduce una dirección de correo válida"
|
||||
|
||||
msgid "Message cannot be empty"
|
||||
msgstr "El mensaje no puede estar vacío"
|
||||
|
||||
# Footer
|
||||
msgid "© {{year}} Rafael Gonzalez Albes. All rights reserved."
|
||||
msgstr "© {{year}} Rafael Gonzalez Albes. Todos los derechos reservados."
|
||||
|
||||
msgid "Built with React, TypeScript & Docker."
|
||||
msgstr "Desarrollado con React, TypeScript y Docker."
|
||||
|
||||
# GitHub Activity Calendar
|
||||
msgid "GitHub Activity"
|
||||
msgstr "Actividad en GitHub"
|
||||
|
||||
msgid "contributions in the last year"
|
||||
msgstr "contribuciones en el último año"
|
||||
|
||||
msgid "Less"
|
||||
msgstr "Menos"
|
||||
|
||||
msgid "More"
|
||||
msgstr "Más"
|
||||
|
||||
msgid "Mon"
|
||||
msgstr "Lun"
|
||||
|
||||
msgid "Wed"
|
||||
msgstr "Mié"
|
||||
|
||||
msgid "Fri"
|
||||
msgstr "Vie"
|
||||
|
||||
msgid "Jan"
|
||||
msgstr "Ene"
|
||||
|
||||
msgid "Feb"
|
||||
msgstr "Feb"
|
||||
|
||||
msgid "Mar"
|
||||
msgstr "Mar"
|
||||
|
||||
msgid "Apr"
|
||||
msgstr "Abr"
|
||||
|
||||
msgid "May"
|
||||
msgstr "May"
|
||||
|
||||
msgid "Jun"
|
||||
msgstr "Jun"
|
||||
|
||||
msgid "Jul"
|
||||
msgstr "Jul"
|
||||
|
||||
msgid "Aug"
|
||||
msgstr "Ago"
|
||||
|
||||
msgid "Sep"
|
||||
msgstr "Sep"
|
||||
|
||||
msgid "Oct"
|
||||
msgstr "Oct"
|
||||
|
||||
msgid "Nov"
|
||||
msgstr "Nov"
|
||||
|
||||
msgid "Dec"
|
||||
msgstr "Dic"
|
||||
|
||||
msgid "contributions"
|
||||
msgstr "contribuciones"
|
||||
|
||||
msgid "No"
|
||||
msgstr "Sin"
|
||||
|
||||
msgid "Low"
|
||||
msgstr "Pocas"
|
||||
|
||||
msgid "Medium"
|
||||
msgstr "Medias"
|
||||
|
||||
msgid "High"
|
||||
msgstr "Muchas"
|
||||
|
||||
msgid "Very high"
|
||||
msgstr "Muchísimas"
|
||||
|
||||
# Gallery & Templates
|
||||
msgid "Design Gallery"
|
||||
msgstr "Galería de Diseños"
|
||||
|
||||
msgid "Choose a template to experience the portfolio. You can switch back at any time."
|
||||
msgstr "Elige una plantilla para experimentar el portafolio. Puedes volver a cambiarla en cualquier momento."
|
||||
|
||||
msgid "GitHub Theme"
|
||||
msgstr "Tema GitHub"
|
||||
|
||||
msgid "Sleek developer profile inspired by GitHub's dark mode, green commits activity accents, and clean layout."
|
||||
msgstr "Perfil de desarrollador elegante inspirado en el modo oscuro de GitHub, detalles de actividad de commits en verde y diseño limpio."
|
||||
|
||||
msgid "Interactive CLI"
|
||||
msgstr "Línea de Comandos Interactiva"
|
||||
|
||||
msgid "Interactive command-line shell interface. Fast, retro-tech styled, with quick commands selector panel."
|
||||
msgstr "Interfaz de terminal interactiva. Rápida, de estilo retro-tecnológico y con panel selector de comandos rápidos."
|
||||
|
||||
msgid "Bento Grid"
|
||||
msgstr "Cuadrícula Bento"
|
||||
|
||||
msgid "A modern layout grouping technical categories and timelines as frosted glass tiles with neon purple shadows."
|
||||
msgstr "Un diseño moderno que agrupa categorías técnicas y cronologías en forma de mosaicos de cristal esmerilado con sombras de neón púrpura."
|
||||
|
||||
msgid "Choose"
|
||||
msgstr "Elegir"
|
||||
|
||||
msgid "Switch Theme"
|
||||
msgstr "Cambiar Tema"
|
||||
|
||||
msgid "Welcome to Rafael Gonzalez Albes CLI Portfolio v1.0.0"
|
||||
msgstr "Bienvenido al Portafolio CLI v1.0.0 de Rafael González Albes"
|
||||
|
||||
msgid "Type 'help' to view all commands, or click the links on the right panel to execute them instantly."
|
||||
msgstr "Escribe 'help' para ver todos los comandos, o haz clic en los enlaces del panel derecho para ejecutarlos al instante."
|
||||
|
||||
msgid "Quick Commands"
|
||||
msgstr "Comandos Rápidos"
|
||||
|
||||
msgid "Click any command to run it in the terminal output grid."
|
||||
msgstr "Haz clic en cualquier comando para ejecutarlo en la terminal."
|
||||
|
||||
msgid "About Me & Developer Journey"
|
||||
msgstr "Sobre Mí y Trayectoria"
|
||||
|
||||
msgid "Technical Skills & Expertise"
|
||||
msgstr "Habilidades Técnicas y Experiencia"
|
||||
|
||||
msgid "Personal Projects List"
|
||||
msgstr "Lista de Proyectos Personales"
|
||||
|
||||
msgid "Contact Info & Socials"
|
||||
msgstr "Información de Contacto y Redes"
|
||||
|
||||
msgid "Clear the terminal screen"
|
||||
msgstr "Limpiar la pantalla de la terminal"
|
||||
|
||||
msgid "Change language"
|
||||
msgstr "Cambiar el idioma"
|
||||
|
||||
msgid "Command not found. Type 'help' for available options."
|
||||
msgstr "Comando no encontrado. Escribe 'help' para ver las opciones disponibles."
|
||||
|
||||
msgid "Language changed to English"
|
||||
msgstr "Idioma cambiado a Inglés"
|
||||
|
||||
msgid "Language changed to Spanish"
|
||||
msgstr "Idioma cambiado a Español"
|
||||
|
||||
msgid "Language changed to Italian"
|
||||
msgstr "Idioma cambiado a Italiano"
|
||||
|
||||
msgid "Language changed to German"
|
||||
msgstr "Idioma cambiado a Alemán"
|
||||
|
||||
msgid "Available for new opportunities"
|
||||
msgstr "Disponible para nuevas oportunidades"
|
||||
|
||||
msgid "Spanish"
|
||||
msgstr "Español"
|
||||
|
||||
msgid "English"
|
||||
msgstr "Inglés"
|
||||
|
||||
msgid "Italian"
|
||||
msgstr "Italiano"
|
||||
|
||||
msgid "German"
|
||||
msgstr "Alemán"
|
||||
|
||||
msgid "French"
|
||||
msgstr "Francés"
|
||||
|
||||
msgid "Native / Bilingual"
|
||||
msgstr "Nativo / Bilingüe"
|
||||
|
||||
msgid "Professional (B1 Certified)"
|
||||
msgstr "Profesional (Certificado B1)"
|
||||
|
||||
msgid "Conversational"
|
||||
msgstr "Conversacional"
|
||||
|
||||
msgid "Basic / Learning"
|
||||
msgstr "Básico / Aprendiendo"
|
||||
|
||||
msgid "Oxford Test of English (B1)"
|
||||
msgstr "Oxford Test of English (B1)"
|
||||
|
||||
msgid "Oxford University Press"
|
||||
msgstr "Oxford University Press"
|
||||
|
||||
msgid "Let's build something together"
|
||||
msgstr "Construyamos algo juntos"
|
||||
|
||||
msgid "My Timeline"
|
||||
msgstr "Mi Cronología"
|
||||
|
||||
# Cyberpunk & Editorial Layouts
|
||||
msgid "SYSTEM_ACTIVE"
|
||||
msgstr "SISTEMA_ACTIVO"
|
||||
|
||||
msgid "Core Stack"
|
||||
msgstr "Tecnologías Principales"
|
||||
|
||||
msgid "Chronology"
|
||||
msgstr "Cronología"
|
||||
|
||||
msgid "Project Modules"
|
||||
msgstr "Módulos de Proyecto"
|
||||
|
||||
msgid "Developer Profile"
|
||||
msgstr "Perfil del Desarrollador"
|
||||
|
||||
msgid "Social Profiles"
|
||||
msgstr "Perfiles Sociales"
|
||||
|
||||
# Brutalist & Neumorphic Layouts
|
||||
msgid "Connect"
|
||||
msgstr "Conectar"
|
||||
|
||||
msgid "I'm open for collaboration and backend architectures development."
|
||||
msgstr "Estoy disponible para colaboraciones y desarrollo de arquitecturas backend."
|
||||
|
||||
msgid "Work"
|
||||
msgstr "Trabajo"
|
||||
|
||||
msgid "Timeline"
|
||||
msgstr "Cronología"
|
||||
|
||||
msgid "Databases & IoT"
|
||||
msgstr "Bases de Datos e IoT"
|
||||
|
||||
msgid "Augsburg, Germany — Sevilla, Spain"
|
||||
msgstr "Augsburgo, Alemania — Sevilla, España"
|
||||
|
||||
|
||||
|
||||
|
||||
msgid "Proactive & Autonomous Software Developer specializing in robust backend architectures, system optimization, and full-stack integration. Driven by curiosity and a lifetime passion for computing."
|
||||
msgstr "Desarrollador de software proactivo y autónomo especializado en arquitecturas backend robustas, optimización de sistemas e integración full-stack. Impulsado por la curiosidad y una pasión de por vida por la informática."
|
||||
|
||||
msgid "A computer enthusiast since childhood with over <1>6 years of professional development experience</1> across Spain and Germany. Proven ability to adapt quickly through self-learning and autonomously solve complex architectural and engineering challenges without requiring permanent supervision. Adept at managing system administration, maintaining databases, and driving collaborative growth within agile team environments."
|
||||
msgstr "Un entusiasta de la informática desde la infancia con más de <1>6 años de experiencia en desarrollo profesional</1> en España y Alemania. Capacidad probada para adaptarse rápidamente a través del autoaprendizaje y resolver de forma autónoma desafíos arquitectónicos y de ingeniería complejos sin requerir supervisión permanente. Experto en gestionar la administración de sistemas, mantener bases de datos y fomentar el crecimiento colaborativo dentro de entornos de equipos ágiles."
|
||||
|
||||
msgid "Circadian Cycle (Auto)"
|
||||
msgstr "Ciclo Circadiano (Auto)"
|
||||
|
||||
msgid "Cycles layouts automatically based on the time of day. Daylight is clean editorial/soft neumorphism, afternoons are brutalist, evenings are dark developer mode, late nights are terminal command lines."
|
||||
msgstr "Cambia el diseño automáticamente según la hora del día. La luz del día es editorial limpia/neumorfismo suave, las tardes son brutalistas, las noches son modo de desarrollador oscuro, las madrugadas son líneas de comando de terminal."
|
||||
|
||||
msgid "Interactive 3D"
|
||||
msgstr "3D Interactivo"
|
||||
|
||||
msgid "An immersive layout featuring mouse-tracking 3D tilt cards, parallax effects, and a custom canvas-rendered 3D rotating starfield constellation."
|
||||
msgstr "Un diseño inmersivo que presenta tarjetas inclinadas en 3D con seguimiento de mouse, efectos de paralaje y una constelación de campo de estrellas giratoria en 3D renderizada en un canvas personalizado."
|
||||
|
||||
msgid "Warm Editorial"
|
||||
msgstr "Editorial Cálido"
|
||||
|
||||
msgid "Clean newspaper and blog style with high typography contrast, warm cream background, and spacious borders."
|
||||
msgstr "Estilo limpio de periódico y blog con alto contraste tipográfico, fondo crema cálido y bordes espaciosos."
|
||||
|
||||
msgid "Cyberpunk Neon"
|
||||
msgstr "Cyberpunk Neón"
|
||||
|
||||
msgid "Vibrant vaporwave style with fluorescent borders, glowing shadows, grid backgrounds, and tech scanner overlay."
|
||||
msgstr "Estilo vaporwave vibrante con bordes fluorescentes, sombras brillantes, fondos de cuadrícula y superposición de escáner tecnológico."
|
||||
|
||||
msgid "Neo Brutalist"
|
||||
msgstr "Neobrutalista"
|
||||
|
||||
msgid "High contrast flat design. Bright warning yellow panels, thick black border boxes, and solid shadows."
|
||||
msgstr "Diseño plano de alto contraste. Paneles amarillos de advertencia brillantes, cajas con bordes negros gruesos y sombras sólidas."
|
||||
|
||||
msgid "Soft Neumorphism"
|
||||
msgstr "Neumorfismo Suave"
|
||||
|
||||
msgid "Soft tactile layout using smooth beveled cards, extruded shadows, and recessed active states."
|
||||
msgstr "Neumorfismo Suave que utiliza tarjetas biseladas lisas, sombras extruidas y estados activos empotrados."
|
||||
|
||||
msgid "Vivid aurora gradient backgrounds with glassmorphic cards, animated glows, and a modern linear layout."
|
||||
msgstr "Fondos de degradado de aurora vívidos con tarjetas glasmórficas, brillos animados y un diseño lineal moderno."
|
||||
|
||||
msgid "Spoken Languages"
|
||||
msgstr "Idiomas Hablados"
|
||||
|
||||
msgid "Invalid email address"
|
||||
msgstr "Dirección de correo no válida"
|
||||
|
||||
msgid "Get in Touch"
|
||||
msgstr "Ponte en Contacto"
|
||||
|
||||
msgid "Contact Me"
|
||||
msgstr "Contactar"
|
||||
|
||||
msgid "I'm always open to discussing new projects, creative ideas, or opportunities. Feel free to reach out via email or send me a message!"
|
||||
msgstr "¡Siempre estoy abierto a discutir nuevos proyectos, ideas creativas u oportunidades. No dudes en contactarme por correo electrónico o enviarme un mensaje!"
|
||||
|
||||
msgid "Download CV"
|
||||
msgstr "Descargar CV"
|
||||
|
||||
msgid "Professional Software Developer"
|
||||
msgstr "Desarrollador de Software Profesional"
|
||||
|
||||
msgid "Software Developer"
|
||||
msgstr "Desarrollador de Software"
|
||||
|
||||
msgid "Years Exp."
|
||||
msgstr "Años de Exp."
|
||||
|
||||
msgid "Languages"
|
||||
msgstr "Idiomas"
|
||||
|
||||
msgid "Backend"
|
||||
msgstr "Backend"
|
||||
|
||||
msgid "Frontend"
|
||||
msgstr "Frontend"
|
||||
|
||||
msgid "GONZÁLEZ ALBES"
|
||||
msgstr "GONZÁLEZ ALBES"
|
||||
|
||||
msgid "The Engineer"
|
||||
msgstr "El Ingeniero"
|
||||
|
||||
msgid "Technical Skills"
|
||||
msgstr "Habilidades Técnicas"
|
||||
|
||||
msgid "Selected Projects"
|
||||
msgstr "Proyectos Seleccionados"
|
||||
|
||||
msgid "Work Experience Timeline"
|
||||
msgstr "Cronología de Experiencia Laboral"
|
||||
|
||||
msgid "Education Timeline"
|
||||
msgstr "Cronología de Educación"
|
||||
|
||||
msgid "I enjoy solving complex architectural challenges, designing robust APIs, containerizing deployment environments, and exploring deep learning models."
|
||||
msgstr "Disfruto resolviendo desafíos arquitectónicos complejos, diseñando APIs robustas, contenedorizando entornos de despliegue y explorando modelos de aprendizaje profundo."
|
||||
|
||||
msgid "A collection of featured projects and engineering work."
|
||||
msgstr "Una colección de proyectos destacados y trabajo de ingeniería."
|
||||
|
||||
msgid "Architected and optimized high-performance backend microservices and APIs utilizing PHP (Symfony), Python, and Go for comparative portal engines serving millions of monthly active users."
|
||||
msgstr "Arquitectó y optimizó microservicios y APIs backend de alto rendimiento utilizando PHP (Symfony), Python y Go para motores de portales comparativos que atienden a millones de usuarios activos mensuales."
|
||||
|
||||
msgid "Executed proactive server and database administration to ensure maximum uptime, scalability, and robust data integration pipelines."
|
||||
msgstr "Ejecutó una administración proactiva de servidores y bases de datos para garantizar el máximo tiempo de actividad, escalabilidad y pipelines de integración de datos robustos."
|
||||
|
||||
msgid "Promoted from Junior Software Engineer to Professional Software Engineer in April 2024 in recognition of ownership of critical core systems."
|
||||
msgstr "Ascendido de Ingeniero de Software Junior a Ingeniero de Software Profesional en abril de 2024 en reconocimiento a la autoría de sistemas centrales críticos."
|
||||
|
||||
msgid "Established containerized local test suites using Docker, reducing onboarding time for new developers and standardizing testing environments."
|
||||
msgstr "Estableció suites de pruebas locales contenedorizadas usando Docker, reduciendo el tiempo de incorporación para nuevos desarrolladores y estandarizando los entornos de prueba."
|
||||
|
||||
msgid "Participated in agile Scrum ceremonies, writing comprehensive API specifications, and ensuring system maintenance and high availability."
|
||||
msgstr "Participó en ceremonias Scrum ágiles, redactando especificaciones detalladas de APIs y asegurando el mantenimiento del sistema y la alta disponibilidad."
|
||||
|
||||
msgid "Engineered interactive user interfaces using HTML5, CSS3, JavaScript, and JQuery, ensuring compliance with responsive web design principles."
|
||||
msgstr "Desarrolló interfaces de usuario interactivas utilizando HTML5, CSS3, JavaScript y JQuery, garantizando el cumplimiento de los principios de diseño web responsivo."
|
||||
|
||||
msgid "Created custom backend logic, hooks, and modules using PHP and the Symfony framework to support e-commerce and media tools."
|
||||
msgstr "Creó lógica backend, ganchos y módulos personalizados utilizando PHP y el framework Symfony para dar soporte a herramientas de comercio electrónico y medios."
|
||||
|
||||
msgid "Implemented and customized enterprise content management systems (CMS) using Typo3, writing custom TypoScript extensions."
|
||||
msgstr "Implementó y personalizó sistemas de gestión de contenidos empresariales (CMS) utilizando Typo3, escribiendo extensiones TypoScript personalizadas."
|
||||
|
||||
msgid "Collaborated with product designers to map out system dependencies and architecture flows using formal UML diagrams."
|
||||
msgstr "Colaboró con diseñadores de productos para mapear dependencias de sistemas y flujos de arquitectura utilizando diagramas UML formales."
|
||||
|
||||
msgid "Researched and implemented state-of-the-art recommendation systems based on collaborative filtering and fuzzy clustering models."
|
||||
msgstr "Investigó e implementó sistemas de recomendación de vanguardia basados en modelos de filtrado colaborativo y agrupamiento difuso."
|
||||
|
||||
msgid "Processed and analyzed large consumer transaction datasets using Python (Pandas, NumPy) to evaluate recommender accuracy."
|
||||
msgstr "Procesó y analizó grandes conjuntos de datos de transacciones de consumidores utilizando Python (Pandas, NumPy) para evaluar la precisión del recomendador."
|
||||
|
||||
msgid "Designed and query-tuned graph-oriented database structures in Neo4j to model user-product buying patterns in real time."
|
||||
msgstr "Diseñó y optimizó consultas de estructuras de bases de datos orientadas a grafos en Neo4j para modelar patrones de compra usuario-producto en tiempo real."
|
||||
|
||||
msgid "Built supporting web portal features in JavaScript, PHP, and Symfony for academic research evaluations."
|
||||
msgstr "Desarrolló características de soporte para portales web en JavaScript, PHP y Symfony para evaluaciones de investigación académica."
|
||||
|
||||
msgid "Developed cross-platform mobile applications for iOS and Android using the Ionic Framework and Angular."
|
||||
msgstr "Desarrolló aplicaciones móviles multiplataforma para iOS y Android utilizando Ionic Framework y Angular."
|
||||
|
||||
msgid "Created automated enterprise scripts, notifications, and sheet integrations using Google Apps Script."
|
||||
msgstr "Creó scripts empresariales automatizados, notificaciones e integraciones de hojas de cálculo utilizando Google Apps Script."
|
||||
|
||||
msgid "Managed Linux production and staging servers, configuring Apache/Nginx web servers, security groups, and SSH configurations."
|
||||
msgstr "Gestionó servidores de producción y pruebas en Linux, configurando servidores web Apache/Nginx, grupos de seguridad y configuraciones SSH."
|
||||
|
||||
msgid "Maintained legacy client websites, writing custom MySQL queries, and migrating servers with minimal downtime."
|
||||
msgstr "Mantuvo sitios web heredados de clientes, escribiendo consultas MySQL personalizadas y migrando servidores con el mínimo tiempo de inactividad."
|
||||
|
||||
msgid "Degree in Computer Engineering in Information Systems"
|
||||
msgstr "Graduado en Ingeniería Informática en Sistemas de Información"
|
||||
|
||||
msgid "Graduated with a thesis on Recommender Systems. Coursework: Advanced Algorithms, Database Management Systems, Software Engineering Patterns, Networks, and Operating Systems."
|
||||
msgstr "Graduado con una tesis sobre Sistemas de Recomendación. Asignaturas: Algoritmos Avanzados, Sistemas de Gestión de Bases de Datos, Patrones de Ingeniería de Software, Redes y Sistemas Operativos."
|
||||
|
||||
msgid "Master's Degree in Computer Science and Engineering"
|
||||
msgstr "Máster en Ciencia e Ingeniería Informática"
|
||||
|
||||
msgid "Erasmus exchange year focusing on high-level systems. Coursework: Software Architectures, Distributed Systems, Advanced Web Technologies, Machine Learning, and Information Retrieval."
|
||||
msgstr "Año de intercambio Erasmus centrado en sistemas de alto nivel. Asignaturas: Arquitecturas de Software, Sistemas Distribuidos, Tecnologías Web Avanzadas, Aprendizaje Automático y Recuperación de Información."
|
||||
|
||||
msgid "Certificate of Higher Education - Multiplatform App Development"
|
||||
msgstr "Técnico Superior en Desarrollo de Aplicaciones Multiplataforma"
|
||||
|
||||
msgid "Focused on native Android development (Java), database design, cross-platform UI architectures, and project management methodologies."
|
||||
msgstr "Centrado en el desarrollo nativo de Android (Java), diseño de bases de datos, arquitecturas de interfaz de usuario multiplataforma y metodologías de gestión de proyectos."
|
||||
|
||||
msgid "Certificate of Higher Education - Web App Development"
|
||||
msgstr "Técnico Superior en Desarrollo de Aplicaciones Web"
|
||||
|
||||
msgid "Practical curriculum covering HTML5/CSS3/JavaScript, backend scripting in PHP, database engines (SQL), and deployment of web servers."
|
||||
msgstr "Plan de estudios práctico que cubre HTML5/CSS3/JavaScript, scripting backend en PHP, motores de bases de datos (SQL) y despliegue de servidores web."
|
||||
|
||||
msgid "Vocational Training in Microcomputer Systems & Networks"
|
||||
msgstr "Técnico en Sistemas Microinformáticos y Redes"
|
||||
|
||||
msgid "Hands-on foundation in computer networks, operating system configuration, PC hardware assembly, and network troubleshooting."
|
||||
msgstr "Base práctica en redes de computadoras, configuración de sistemas operativos, ensamblaje de hardware de PC y resolución de problemas de red."
|
||||
|
||||
msgid "Professional Certifications"
|
||||
msgstr "Certificaciones Profesionales"
|
||||
|
||||
msgid "Oxford Test of English (Level B1)"
|
||||
msgstr "Oxford Test of English (Nivel B1)"
|
||||
|
||||
msgid "Technical Certifications: Advanced C++, Java Enterprise, and SQL Database Design."
|
||||
msgstr "Certificaciones técnicas: C++ Avanzado, Java Enterprise y Diseño de Bases de Datos SQL."
|
||||
|
||||
msgid "Mediaconcept GmbH (Germany)"
|
||||
msgstr "Mediaconcept GmbH (Alemania)"
|
||||
|
||||
msgid "University of Bologna (Erasmus)"
|
||||
msgstr "Universidad de Bolonia (Erasmus)"
|
||||
|
||||
msgid "I.E.S Velázquez"
|
||||
msgstr "I.E.S Velázquez"
|
||||
|
||||
msgid "Center Nuevas Profesiones"
|
||||
msgstr "Centro Nuevas Profesiones"
|
||||
|
||||
msgid "I.E.S Axati"
|
||||
msgstr "I.E.S Axati"
|
||||
|
||||
msgid "Various Institutions"
|
||||
msgstr "Diversas Instituciones"
|
||||
|
||||
msgid "Online & Academic"
|
||||
msgstr "En línea y Académico"
|
||||
|
||||
msgid "Writing clean, testable code"
|
||||
msgstr "Escribir código limpio y testeable"
|
||||
|
||||
msgid "PHP & Symfony"
|
||||
msgstr "PHP y Symfony"
|
||||
|
||||
msgid "Senior / Expert"
|
||||
msgstr "Senior / Experto"
|
||||
|
||||
msgid "Python (Deep Learning)"
|
||||
msgstr "Python (Deep Learning)"
|
||||
|
||||
msgid "Advanced / Intermediate"
|
||||
msgstr "Avanzado / Intermedio"
|
||||
|
||||
msgid "Java / OOP"
|
||||
msgstr "Java / POO"
|
||||
|
||||
msgid "Advanced"
|
||||
msgstr "Avanzado"
|
||||
|
||||
msgid "C / C++"
|
||||
msgstr "C / C++"
|
||||
|
||||
msgid "Intermediate"
|
||||
msgstr "Intermedio"
|
||||
|
||||
msgid "Server Maintenance & Bash"
|
||||
msgstr "Mantenimiento de Servidores y Bash"
|
||||
|
||||
msgid "JavaScript / TypeScript"
|
||||
msgstr "JavaScript / TypeScript"
|
||||
|
||||
msgid "Senior"
|
||||
msgstr "Senior"
|
||||
|
||||
msgid "HTML5 & CSS3"
|
||||
msgstr "HTML5 y CSS3"
|
||||
|
||||
msgid "Expert"
|
||||
msgstr "Experto"
|
||||
|
||||
msgid "JQuery & Bootstrap"
|
||||
msgstr "JQuery y Bootstrap"
|
||||
|
||||
msgid "React"
|
||||
msgstr "React"
|
||||
|
||||
msgid "SQL (MySQL, PostgreSQL)"
|
||||
msgstr "SQL (MySQL, PostgreSQL)"
|
||||
|
||||
msgid "Neo4j (Graph DB)"
|
||||
msgstr "Neo4j (Base de Datos de Grafos)"
|
||||
|
||||
msgid "Docker / Containers"
|
||||
msgstr "Docker / Contenedores"
|
||||
|
||||
msgid "IoT (Arduino, ESP8266)"
|
||||
msgstr "IoT (Arduino, ESP8266)"
|
||||
|
||||
msgid "Aurora Glass"
|
||||
msgstr "Cristal Aurora"
|
||||
|
||||
msgid "Time Distortion Active: Cycling layouts..."
|
||||
msgstr "Distorsión temporal activa: Ciclando diseños..."
|
||||
973
src/locales/it.po
Normal file
|
|
@ -0,0 +1,973 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: it\n"
|
||||
|
||||
# Navbar Links
|
||||
msgid "Home"
|
||||
msgstr "Home"
|
||||
|
||||
msgid "About"
|
||||
msgstr "Chi Sono"
|
||||
|
||||
msgid "Skills"
|
||||
msgstr "Competenze"
|
||||
|
||||
msgid "Experience"
|
||||
msgstr "Esperienza"
|
||||
|
||||
msgid "Projects"
|
||||
msgstr "Progetti"
|
||||
|
||||
msgid "Contact"
|
||||
msgstr "Contatti"
|
||||
|
||||
# Hero Section
|
||||
msgid "Available for new opportunities"
|
||||
msgstr "Disponibile per nuove opportunità"
|
||||
|
||||
msgid "Building Scalable"
|
||||
msgstr "Costruzione di Sistemi"
|
||||
|
||||
msgid "Backend Systems"
|
||||
msgstr "Backend Scalabili"
|
||||
|
||||
msgid "Seamless Interfaces"
|
||||
msgstr "Interfacce Intuitive"
|
||||
|
||||
msgid "I am a "
|
||||
msgstr "Sono un "
|
||||
|
||||
msgid "Full-Stack Developer"
|
||||
msgstr "Sviluppatore Full-Stack"
|
||||
|
||||
msgid "Backend Specialist"
|
||||
msgstr "Specialista Backend"
|
||||
|
||||
msgid "API Designer"
|
||||
msgstr "Progettista API"
|
||||
|
||||
msgid "System Thinker"
|
||||
msgstr "Pensatore Sistemico"
|
||||
|
||||
msgid "Professional Software Developer with 4.5+ years of experience building high-performance backend APIs, handling databases, and crafting clean frontend applications. Specialized in PHP (Symfony), TypeScript, and containerized Docker environments."
|
||||
msgstr "Sviluppatore software professionista con oltre 4.5 anni di esperienza nella creazione di API backend ad alte prestazioni, nella gestione di database e nello sviluppo di applicazioni frontend pulite. Specializzato in PHP (Symfony), TypeScript e ambienti Docker containerizzati."
|
||||
|
||||
msgid "Explore Projects"
|
||||
msgstr "Esplora Progetti"
|
||||
|
||||
msgid "Let's Talk"
|
||||
msgstr "Parliamo"
|
||||
|
||||
msgid "Run Output: "
|
||||
msgstr "Risultato: "
|
||||
|
||||
msgid "🚀 Local execution ready"
|
||||
msgstr "🚀 Esecuzione locale pronta"
|
||||
|
||||
msgid "⚠️ Dependency missing"
|
||||
msgstr "⚠️ Dipendenza mancante"
|
||||
|
||||
# About Section
|
||||
msgid "About Me"
|
||||
msgstr "Chi Sono"
|
||||
|
||||
msgid "My Journey as a Developer"
|
||||
msgstr "Il mio percorso come sviluppatore"
|
||||
|
||||
msgid "I hold a <1>Degree in Computer Engineering in Information Systems</1> from Seville, Spain, and spent an exchange year completing <3>Master's coursework in Computer Science and Engineering</3> at the prestigious <5>University of Bologna</5> in Italy."
|
||||
msgstr "Ho conseguito una <1>Laurea in Ingegneria Informatica nei Sistemi Informativi</1> a Siviglia, in Spagna, e ho trascorso un anno di scambio completando corsi di <3>Laurea Magistrale in Informatica</3> presso la prestigiosa <5>Università di Bologna</5> in Italia."
|
||||
|
||||
msgid "My foundation in software engineering is reinforced by extensive vocational training in web and multiplatform application development, combined with over <1>4 years of professional experience</1> in Germany's leading comparison portal (CHECK24) and media agencies."
|
||||
msgstr "La mia base nell'ingegneria del software è rafforzata da una vasta formazione professionale nello sviluppo di applicazioni web e multipiattaforma, unita a oltre <1>4 anni di esperienza professionale</1> nel principale portale di comparazione in Germania (CHECK24) e in agenzie di comunicazione."
|
||||
|
||||
msgid "I enjoy solving complex architectural challenges, designing robust APIs, containerizing deployment environments, and exploring deep learning models. Multi-lingual and highly adaptable, I am comfortable collaborating across international, cross-functional teams."
|
||||
msgstr "Mi piace risolvere sfide architetturali complesse, progettare API robuste, containerizzare ambienti di sviluppo ed esplorare modelli di deep learning. Multilingue e altamente adattabile, mi trovo a mio agio nel collaborare in team internazionali e interfunzionali."
|
||||
|
||||
msgid "Years Experience"
|
||||
msgstr "Anni di Esperienza"
|
||||
|
||||
msgid "Languages Spoken"
|
||||
msgstr "Lingue Parlate"
|
||||
|
||||
msgid "Academic Qualifications"
|
||||
msgstr "Qualifiche Accademiche"
|
||||
|
||||
msgid "Personal Projects"
|
||||
msgstr "Progetti Personali"
|
||||
|
||||
msgid "Certifications"
|
||||
msgstr "Certificazioni"
|
||||
|
||||
msgid "Oxford Test of English (B1)"
|
||||
msgstr "Oxford Test of English (B1)"
|
||||
|
||||
msgid "Oxford University Press"
|
||||
msgstr "Oxford University Press"
|
||||
|
||||
msgid "C++ Certification"
|
||||
msgstr "Certificazione C++"
|
||||
|
||||
msgid "Programming Institute"
|
||||
msgstr "Programming Institute"
|
||||
|
||||
msgid "Java Certification"
|
||||
msgstr "Certificazione Java"
|
||||
|
||||
msgid "Oracle Certified Associate"
|
||||
msgstr "Oracle Certified Associate"
|
||||
|
||||
msgid "SQL Developer Certification"
|
||||
msgstr "Certificazione SQL Developer"
|
||||
|
||||
msgid "Database Systems"
|
||||
msgstr "Sistemi di Database"
|
||||
|
||||
# Languages
|
||||
msgid "Spanish"
|
||||
msgstr "Spagnolo"
|
||||
|
||||
msgid "English"
|
||||
msgstr "Inglese"
|
||||
|
||||
msgid "Italian"
|
||||
msgstr "Italiano"
|
||||
|
||||
msgid "German"
|
||||
msgstr "Tedesco"
|
||||
|
||||
msgid "French"
|
||||
msgstr "Francese"
|
||||
|
||||
msgid "Native / Bilingual"
|
||||
msgstr "Madrelingua / Bilingue"
|
||||
|
||||
msgid "Professional (B1 Certified)"
|
||||
msgstr "Professionale (Certificato B1)"
|
||||
|
||||
msgid "Conversational"
|
||||
msgstr "Conversazionale"
|
||||
|
||||
msgid "Basic / Learning"
|
||||
msgstr "Elementare / In apprendimento"
|
||||
|
||||
# Skills Section
|
||||
msgid "Technical Expertise"
|
||||
msgstr "Competenze Tecniche"
|
||||
|
||||
msgid "A robust technical foundation bridging low-level systems (C++), high-concurrency web systems (PHP, Node.js), and data analytics (Python, Neo4j)."
|
||||
msgstr "Una solida base tecnica che unisce sistemi a basso livello (C++), sistemi web ad alta concorrenza (PHP, Node.js) e analisi dei dati (Python, Neo4j)."
|
||||
|
||||
msgid "Backend & Systems"
|
||||
msgstr "Backend e Sistemi"
|
||||
|
||||
msgid "Frontend & UI"
|
||||
msgstr "Frontend e UI"
|
||||
|
||||
msgid "Databases & Technologies"
|
||||
msgstr "Database e Tecnologie"
|
||||
|
||||
# Experience Section
|
||||
msgid "My Timeline"
|
||||
msgstr "La Mia Linea Temporale"
|
||||
|
||||
msgid "Work Experience"
|
||||
msgstr "Esperienza Lavorativa"
|
||||
|
||||
msgid "Education"
|
||||
msgstr "Istruzione"
|
||||
|
||||
msgid "Check24 (Germany)"
|
||||
msgstr "Check24 (Germania)"
|
||||
|
||||
msgid "Augsburg, Bavaria, Germany"
|
||||
msgstr "Augusta, Baviera, Germania"
|
||||
|
||||
msgid "Ulm, Baden-Wurttemberg, Germany"
|
||||
msgstr "Ulma, Baden-Württemberg, Germania"
|
||||
|
||||
msgid "Seville, Spain"
|
||||
msgstr "Siviglia, Spagna"
|
||||
|
||||
msgid "Bologna, Italy"
|
||||
msgstr "Bologna, Italia"
|
||||
|
||||
msgid "Full Stack Web Developer"
|
||||
msgstr "Sviluppatore Web Full Stack"
|
||||
|
||||
msgid "Student Intern (Recommender Systems)"
|
||||
msgstr "Tirocinante Universitario (Sistemi di Raccomandazione)"
|
||||
|
||||
msgid "Web Developer (JS, PHP, & Symfony)"
|
||||
msgstr "Sviluppatore Web (JS, PHP e Symfony)"
|
||||
|
||||
msgid "University Pablo de Olavide"
|
||||
msgstr "Università Pablo de Olavide"
|
||||
|
||||
msgid "Green Slope S.L."
|
||||
msgstr "Green Slope S.L."
|
||||
|
||||
# Experience Details
|
||||
msgid "Backend development specializing in PHP, building scalable services and microservices."
|
||||
msgstr "Sviluppo backend specializzato in PHP, realizzando servizi e microservizi scalabili."
|
||||
|
||||
msgid "Creation, optimization, and integration of robust RESTful/SOAP APIs."
|
||||
msgstr "Creazione, ottimizzazione e integrazione di API RESTful/SOAP robuste."
|
||||
|
||||
msgid "Local dockerization setups and automated testing pipeline maintenance."
|
||||
msgstr "Configurazioni di dockerizzazione locale e manutenzione di pipeline di test automatizzati."
|
||||
|
||||
msgid "Large-scale database handling, schema migrations, and query performance tuning."
|
||||
msgstr "Gestione di database su larga scala, migrazione degli schemi e ottimizzazione delle query."
|
||||
|
||||
msgid "Frontend web development utilizing HTML5, CSS3, and JavaScript/TypeScript."
|
||||
msgstr "Sviluppo web frontend con HTML5, CSS3 e JavaScript/TypeScript."
|
||||
|
||||
msgid "Backend development using Symfony PHP framework for production applications."
|
||||
msgstr "Sviluppo backend utilizzando il framework Symfony PHP per applicazioni in produzione."
|
||||
|
||||
msgid "Content management system (CMS) integration and customization using Typo3."
|
||||
msgstr "Integrazione e personalizzazione di sistemi di gestione dei contenuti (CMS) tramite Typo3."
|
||||
|
||||
msgid "Creation of software designs using UML diagrams for team collaboration."
|
||||
msgstr "Creazione di progetti software tramite diagrammi UML per la collaborazione del team."
|
||||
|
||||
msgid "Researched and built recommendation algorithms based on collaborative filtering."
|
||||
msgstr "Ricerca e sviluppo di algoritmi di raccomandazione basati su filtraggio collaborativo."
|
||||
|
||||
msgid "Developed fuzzy clustering engines using Python, pandas, and numpy."
|
||||
msgstr "Sviluppo di motori di clustering fuzzy utilizzando Python, pandas e numpy."
|
||||
|
||||
msgid "Utilized graph-oriented databases (Neo4j) to model product/user relationships."
|
||||
msgstr "Utilizzo di database orientati ai grafi (Neo4j) per modellare le relazioni utente-prodotto."
|
||||
|
||||
msgid "Developed cross-platform mobile apps using the Ionic Framework."
|
||||
msgstr "Sviluppo di applicazioni mobili multipiattaforma utilizzando l'Ionic Framework."
|
||||
|
||||
msgid "Built automated scripts and workflows using Google Apps Script."
|
||||
msgstr "Creazione di script e flussi di lavoro automatizzati tramite Google Apps Script."
|
||||
|
||||
msgid "Managed and maintained Linux production servers and VPS environments."
|
||||
msgstr "Gestione e manutenzione di server di produzione Linux e ambienti VPS."
|
||||
|
||||
msgid "Specialized in information systems design, algorithms, databases, and software design patterns."
|
||||
msgstr "Specializzazione nella progettazione di sistemi informativi, algoritmi, database e pattern di progettazione software."
|
||||
|
||||
msgid "Completed advanced master coursework in software architectures, web technologies, and systems engineering during Erasmus exchange."
|
||||
msgstr "Completato corsi avanzati di laurea magistrale in architetture software, tecnologie web e ingegneria dei sistemi durante lo scambio Erasmus."
|
||||
|
||||
msgid "Hands-on vocational program focusing on mobile development (Android, iOS) and cross-platform desktop frameworks."
|
||||
msgstr "Programma di formazione professionale pratica focalizzato sullo sviluppo mobile (Android, iOS) e framework desktop multipiattaforma."
|
||||
|
||||
msgid "Practical training focused on front-end layouts, back-end scripting, SQL databases, and web servers."
|
||||
msgstr "Formazione pratica incentrata su layout front-end, scripting back-end, database SQL e web server."
|
||||
|
||||
msgid "Introduction to hardware assembly, operating systems installation, router configuration, and local network diagnostics."
|
||||
msgstr "Introduzione all'assemblaggio hardware, installazione di sistemi operativi, configurazione del router e diagnostica della rete locale."
|
||||
|
||||
# Projects Section
|
||||
msgid "Personal Projects"
|
||||
msgstr "Progetti Personali"
|
||||
|
||||
msgid "A curated selection of personal creations showing my growth across IoT, Deep Learning, Mobile development, and system designs."
|
||||
msgstr "Una selezione accurata di creazioni personali che mostrano la mia crescita in IoT, Deep Learning, sviluppo mobile e progettazione di sistemi."
|
||||
|
||||
msgid "All Projects"
|
||||
msgstr "Tutti"
|
||||
|
||||
msgid "AI & Data"
|
||||
msgstr "IA e Dati"
|
||||
|
||||
msgid "Web & IoT"
|
||||
msgstr "Web e IoT"
|
||||
|
||||
msgid "Mobile Apps"
|
||||
msgstr "Applicazioni Mobile"
|
||||
|
||||
msgid "Learn More"
|
||||
msgstr "Scopri di Più"
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "Panoramica"
|
||||
|
||||
msgid "Technical Flow & Pattern"
|
||||
msgstr "Flusso Tecnico e Pattern"
|
||||
|
||||
msgid "Tech Stack"
|
||||
msgstr "Tecnologie Utilizzate"
|
||||
|
||||
msgid "View on GitHub"
|
||||
msgstr "Vedi su GitHub"
|
||||
|
||||
# Specific Projects
|
||||
msgid "Supermarket Recommender System"
|
||||
msgstr "Sistema di Raccomandazione per Supermercati"
|
||||
|
||||
msgid "A hybrid recommendation engine utilizing fuzzy clustering and graph databases for market basket analysis."
|
||||
msgstr "Un motore di raccomandazione ibrido che utilizza clustering fuzzy e database a grafi per l'analisi del carrello della spesa."
|
||||
|
||||
msgid "Developed a high-accuracy recommender system that analyzes consumer purchasing history and recommends items based on fuzzy clustering groups. Built a graph network in Neo4j to model user-to-product buy relationships, allowing fast collaborative filtering queries."
|
||||
msgstr "Sviluppato un sistema di raccomandazione ad alta precisione che analizza la cronologia degli acquisti dei consumatori e consiglia gli articoli in base a gruppi di clustering fuzzy. Costruito una rete a grafi in Neo4j per modellare le relazioni di acquisto utente-prodotto, consentendo query rapide di filtraggio collaborativo."
|
||||
|
||||
msgid "Data extraction & cleaning via Pandas and NumPy"
|
||||
msgstr "Estrazione e pulizia dei dati tramite Pandas e NumPy"
|
||||
|
||||
msgid "Fuzzy C-Means Clustering to group similar purchasing baskets"
|
||||
msgstr "Fuzzy C-Means Clustering per raggruppare carrelli della spesa simili"
|
||||
|
||||
msgid "Graph DB mapping of nodes (Customers, Products, Categories) in Neo4j"
|
||||
msgstr "Mappatura in database a grafi di nodi (Clienti, Prodotti, Categorie) in Neo4j"
|
||||
|
||||
msgid "Real-time neighbor matching query logic via Cypher"
|
||||
msgstr "Lógica di query di corrispondenza dei vicini in tempo reale tramite Cypher"
|
||||
|
||||
msgid "IoT Smart Dumpster"
|
||||
msgstr "Cassonetto Intelligente IoT"
|
||||
|
||||
msgid "Intelligent trash container telemetry network using ESP8266, custom PHP backend, and Android monitoring."
|
||||
msgstr "Rete di telemetria per cassonetti intelligenti che utilizza ESP8266, backend PHP personalizzato e monitoraggio Android."
|
||||
|
||||
msgid "Designed a smart container system powered by ESP8266 nodeMCU v3 microcontrollers. It uses ultrasonic sensors to measure fill levels, communicates with a PHP API via encrypted HTTP POST (using MD5 signature verification), and reports real-time dashboard data to a companion Android app."
|
||||
msgstr "Progettato un sistema di cassonetti intelligenti alimentato da microcontrolatori ESP8266 nodeMCU v3. Utilizza sensori a ultrasuoni per misurare i livelli di riempimento, comunica con un'API PHP tramite HTTP POST crittografato (con verifica della firma MD5) e segnala i dati della dashboard in tempo reale a un'applicazione Android di accompagnamento."
|
||||
|
||||
msgid "ESP8266 telemetry node running on C++/Arduino sketch"
|
||||
msgstr "Nodo di telemetria ESP8266 basato su C++/Arduino sketch"
|
||||
|
||||
msgid "PHP Web API endpoint with MD5 request checksum validation"
|
||||
msgstr "Endpoint API Web PHP con convalida del checksum della richiesta MD5"
|
||||
|
||||
msgid "Background Scheduler running task loops to identify full units"
|
||||
msgstr "Pianificatore in background che esegue cicli di attività per identificare unità piene"
|
||||
|
||||
msgid "Android Client using HTTP endpoints to fetch status maps"
|
||||
msgstr "Client Android che utilizza endpoint HTTP per recuperare le mappe di stato"
|
||||
|
||||
msgid "Animal Adoption Mobile App"
|
||||
msgstr "App Mobile per Adozione Animali"
|
||||
|
||||
msgid "A native mobile application connecting animal shelters with prospective adopters."
|
||||
msgstr "Un'applicazione mobile nativa che collega i rifugi per animali con i potenziali adottanti."
|
||||
|
||||
msgid "A native Android mobile application that aggregates profiles of adoptable pets from local shelters. Allows users to filter by breed, age, location, and apply for adoption directly through the platform. Connects to a secure MySQL database for real-time applications handling."
|
||||
msgstr "Un'applicazione mobile Android nativa che aggrega i profili degli animali adottabili dai rifugi locali. Consente agli utenti di filtrare per razza, età, posizione e richiedere l'adozione direttamente tramite la piattaforma. Si collega a un database MySQL sicuro per la gestione delle domande in tempo reale."
|
||||
|
||||
msgid "Custom Blogging Platform"
|
||||
msgstr "Piattaforma Blog Personalizzata"
|
||||
|
||||
msgid "A full-featured CMS web application for blogging and publishing article contents."
|
||||
msgstr "Applicazione web CMS completa per blog e pubblicazione di articoli."
|
||||
|
||||
msgid "A custom-built Content Management System (CMS) designed for authors. It features markdown editing, user authentication, profile customizer, dynamic commenting sections, and tags search. Powered by a classic LAMP/LEMP stack architecture."
|
||||
msgstr "Un sistema di gestione dei contenuti (CMS) personalizzato progettato per gli autori. Dispone di editing markdown, autenticazione utente, personalizzazione del profilo, sezioni di commento dinamiche e ricerca per tag. Basato su una classica architettura stack LAMP/LEMP."
|
||||
|
||||
msgid "Self-driving Car in CARLA"
|
||||
msgstr "Auto a Guida Autonoma in CARLA"
|
||||
|
||||
msgid "Deep Reinforcement Learning script enabling autonomous vehicle lane tracking and driving."
|
||||
msgstr "Script di Deep Reinforcement Learning che consente il tracciamento della corsia del veicolo e la guida autonoma."
|
||||
|
||||
msgid "Programmed an autonomous driving agent inside the CARLA simulator. Utilized a deep reinforcement learning model (Keras/TensorFlow) mapping front-camera frames to steering and throttle actions. Trained the network using Deep Q-Learning (DQN) to maximize reward for staying in lanes and avoiding collisions."
|
||||
msgstr "Programmato un agente di guida autonoma all'interno del simulatore CARLA. Utilizzato un modello di deep reinforcement learning (Keras/TensorFlow) che mappa i fotogrammi della telecamera anteriore alle azioni di sterzo e acceleratore. Addestrato la rete utilizzando Deep Q-Learning (DQN) per massimizzare la ricompensa per rimanere nelle corsie ed evitare collisioni."
|
||||
|
||||
msgid "Video streaming ingestion from CARLA camera sensor nodes"
|
||||
msgstr "Ingestione di streaming video dai nodi dei sensori della telecamera CARLA"
|
||||
|
||||
msgid "Frame preprocessing & downscaling using OpenCV"
|
||||
msgstr "Preelaborazione e ridimensionamento dei fotogrammi tramite OpenCV"
|
||||
|
||||
msgid "Deep Neural Network trained using Keras/TensorFlow framework"
|
||||
msgstr "Rete neurale profonda addestrata utilizzando il framework Keras/TensorFlow"
|
||||
|
||||
msgid "DQN reward structure optimization based on forward velocity and lane alignment"
|
||||
msgstr "Ottimizzazione della struttura di ricompensa DQN basata sulla velocità di avanzamento e sull'allineamento della corsia"
|
||||
|
||||
# Contact Section
|
||||
msgid "Get In Touch"
|
||||
msgstr "Contattami"
|
||||
|
||||
msgid "Have a question or want to discuss a project? Drop a message and I'll get back to you as soon as possible."
|
||||
msgstr "Hai una domanda o vuoi discutere di un progetto? Invia un messaggio e ti risponderò al più presto."
|
||||
|
||||
msgid "Let's build something together"
|
||||
msgstr "Costruiamo qualcosa insieme"
|
||||
|
||||
msgid "Whether you are looking to design a backend API, containerize a legacy application, or create an interactive frontend, I'd love to chat."
|
||||
msgstr "Sia che tu stia cercando di progettare un'API backend, containerizzare un'applicazione legacy o creare un frontend interattivo, mi piacerebbe fare due chiacchiere."
|
||||
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
|
||||
msgid "Location"
|
||||
msgstr "Posizione"
|
||||
|
||||
msgid "Quick Note:"
|
||||
msgstr "Nota veloce:"
|
||||
|
||||
msgid " I speak Spanish, English, Italian, and basic German, making collaboration in multi-lingual teams very smooth!"
|
||||
msgstr " Parlo spagnolo, inglese, italiano e tedesco di base, il che rende la collaborazione in team multilingue molto fluida!"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "Nome"
|
||||
|
||||
msgid "Message"
|
||||
msgstr "Messaggio"
|
||||
|
||||
msgid "Your Name"
|
||||
msgstr "Il Tuo Nome"
|
||||
|
||||
msgid "Type your message here..."
|
||||
msgstr "Scrivi il tuo messaggio qui..."
|
||||
|
||||
msgid "Sending..."
|
||||
msgstr "Invio in corso..."
|
||||
|
||||
msgid "Send Message"
|
||||
msgstr "Invia Messaggio"
|
||||
|
||||
msgid "Message Sent Successfully!"
|
||||
msgstr "Messaggio Inviato con Successo!"
|
||||
|
||||
msgid "Thank you for reaching out. I'll get back to you shortly."
|
||||
msgstr "Grazie per avermi contattato. Ti risponderò a breve."
|
||||
|
||||
msgid "Send Another Message"
|
||||
msgstr "Invia un Altro Messaggio"
|
||||
|
||||
# Contact Validation Errors
|
||||
msgid "Name is required"
|
||||
msgstr "Il nome è obbligatorio"
|
||||
|
||||
msgid "Email is required"
|
||||
msgstr "L'email è obbligatoria"
|
||||
|
||||
msgid "Please enter a valid email address"
|
||||
msgstr "Inserisci un indirizzo email valido"
|
||||
|
||||
msgid "Message cannot be empty"
|
||||
msgstr "Il messaggio non può essere vuoto"
|
||||
|
||||
# Footer
|
||||
msgid "© {{year}} Rafael Gonzalez Albes. All rights reserved."
|
||||
msgstr "© {{year}} Rafael Gonzalez Albes. Tutti i diritti riservati."
|
||||
|
||||
msgid "Built with React, TypeScript & Docker."
|
||||
msgstr "Sviluppato con React, TypeScript e Docker."
|
||||
|
||||
# GitHub Activity Calendar
|
||||
msgid "GitHub Activity"
|
||||
msgstr "Attività su GitHub"
|
||||
|
||||
msgid "contributions in the last year"
|
||||
msgstr "contributi nell'ultimo anno"
|
||||
|
||||
msgid "Less"
|
||||
msgstr "Meno"
|
||||
|
||||
msgid "More"
|
||||
msgstr "Più"
|
||||
|
||||
msgid "Mon"
|
||||
msgstr "Lun"
|
||||
|
||||
msgid "Wed"
|
||||
msgstr "Mer"
|
||||
|
||||
msgid "Fri"
|
||||
msgstr "Ven"
|
||||
|
||||
msgid "Jan"
|
||||
msgstr "Gen"
|
||||
|
||||
msgid "Feb"
|
||||
msgstr "Feb"
|
||||
|
||||
msgid "Mar"
|
||||
msgstr "Mar"
|
||||
|
||||
msgid "Apr"
|
||||
msgstr "Apr"
|
||||
|
||||
msgid "May"
|
||||
msgstr "Mag"
|
||||
|
||||
msgid "Jun"
|
||||
msgstr "Giu"
|
||||
|
||||
msgid "Jul"
|
||||
msgstr "Lug"
|
||||
|
||||
msgid "Aug"
|
||||
msgstr "Ago"
|
||||
|
||||
msgid "Sep"
|
||||
msgstr "Set"
|
||||
|
||||
msgid "Oct"
|
||||
msgstr "Ott"
|
||||
|
||||
msgid "Nov"
|
||||
msgstr "Nov"
|
||||
|
||||
msgid "Dec"
|
||||
msgstr "Dic"
|
||||
|
||||
msgid "contributions"
|
||||
msgstr "contributi"
|
||||
|
||||
msgid "No"
|
||||
msgstr "Nessun"
|
||||
|
||||
msgid "Low"
|
||||
msgstr "Pochi"
|
||||
|
||||
msgid "Medium"
|
||||
msgstr "Medi"
|
||||
|
||||
msgid "High"
|
||||
msgstr "Molti"
|
||||
|
||||
msgid "Very high"
|
||||
msgstr "Moltissimi"
|
||||
|
||||
# Gallery & Templates
|
||||
msgid "Design Gallery"
|
||||
msgstr "Galleria del Design"
|
||||
|
||||
msgid "Choose a template to experience the portfolio. You can switch back at any time."
|
||||
msgstr "Scegli un modello per visualizzare il portfolio. Puoi cambiarlo in qualsiasi momento."
|
||||
|
||||
msgid "GitHub Theme"
|
||||
msgstr "Tema GitHub"
|
||||
|
||||
msgid "Sleek developer profile inspired by GitHub's dark mode, green commits activity accents, and clean layout."
|
||||
msgstr "Profilo sviluppatore elegante ispirato alla modalità scura di GitHub, dettagli di attività commit in verde e layout pulito."
|
||||
|
||||
msgid "Interactive CLI"
|
||||
msgstr "CLI Interattiva"
|
||||
|
||||
msgid "Interactive command-line shell interface. Fast, retro-tech styled, with quick commands selector panel."
|
||||
msgstr "Interfaccia a riga di comando interattiva. Veloce, in stile retro-tech, con pannello di selezione dei comandi rapidi."
|
||||
|
||||
msgid "Bento Grid"
|
||||
msgstr "Griglia Bento"
|
||||
|
||||
msgid "A modern layout grouping technical categories and timelines as frosted glass tiles with neon purple shadows."
|
||||
msgstr "Un layout moderno che raggruppa categorie tecniche e cronologie sotto forma di tessere di vetro smerigliato con ombre viola neon."
|
||||
|
||||
msgid "Choose"
|
||||
msgstr "Scegli"
|
||||
|
||||
msgid "Switch Theme"
|
||||
msgstr "Cambia Tema"
|
||||
|
||||
msgid "Welcome to Rafael Gonzalez Albes CLI Portfolio v1.0.0"
|
||||
msgstr "Benvenuto nel Portfolio CLI v1.0.0 di Rafael Gonzalez Albes"
|
||||
|
||||
msgid "Type 'help' to view all commands, or click the links on the right panel to execute them instantly."
|
||||
msgstr "Digita 'help' per vedere tutti i comandi, o fai clic sui link nel pannello di destra per eseguirli all'istante."
|
||||
|
||||
msgid "Quick Commands"
|
||||
msgstr "Comandi Rapidi"
|
||||
|
||||
msgid "Click any command to run it in the terminal output grid."
|
||||
msgstr "Fai clic su qualsiasi comando per eseguirlo nella console."
|
||||
|
||||
msgid "About Me & Developer Journey"
|
||||
msgstr "Su di Me e Percorso"
|
||||
|
||||
msgid "Technical Skills & Expertise"
|
||||
msgstr "Competenze Tecniche ed Esperienza"
|
||||
|
||||
msgid "Personal Projects List"
|
||||
msgstr "Elenco dei Progetti Personali"
|
||||
|
||||
msgid "Contact Info & Socials"
|
||||
msgstr "Informazioni di Contatto e Social"
|
||||
|
||||
msgid "Clear the terminal screen"
|
||||
msgstr "Cancella la schermata del terminale"
|
||||
|
||||
msgid "Change language"
|
||||
msgstr "Cambia lingua"
|
||||
|
||||
msgid "Command not found. Type 'help' for available options."
|
||||
msgstr "Comando non trovato. Digita 'help' per le opzioni disponibili."
|
||||
|
||||
msgid "Language changed to English"
|
||||
msgstr "Lingua cambiata in Inglese"
|
||||
|
||||
msgid "Language changed to Spanish"
|
||||
msgstr "Lingua cambiata in Spagnolo"
|
||||
|
||||
msgid "Language changed to Italian"
|
||||
msgstr "Lingua cambiata in Italiano"
|
||||
|
||||
msgid "Language changed to German"
|
||||
msgstr "Lingua cambiata in Tedesco"
|
||||
|
||||
msgid "Available for new opportunities"
|
||||
msgstr "Disponibile per nuove opportunità"
|
||||
|
||||
msgid "Spanish"
|
||||
msgstr "Spagnolo"
|
||||
|
||||
msgid "English"
|
||||
msgstr "Inglese"
|
||||
|
||||
msgid "Italian"
|
||||
msgstr "Italiano"
|
||||
|
||||
msgid "German"
|
||||
msgstr "Tedesco"
|
||||
|
||||
msgid "French"
|
||||
msgstr "Francese"
|
||||
|
||||
msgid "Native / Bilingual"
|
||||
msgstr "Madrelingua / Bilingue"
|
||||
|
||||
msgid "Professional (B1 Certified)"
|
||||
msgstr "Professionale (Certificato B1)"
|
||||
|
||||
msgid "Conversational"
|
||||
msgstr "Conversazionale"
|
||||
|
||||
msgid "Basic / Learning"
|
||||
msgstr "Base / In apprendimento"
|
||||
|
||||
msgid "Oxford Test of English (B1)"
|
||||
msgstr "Oxford Test of English (B1)"
|
||||
|
||||
msgid "Oxford University Press"
|
||||
msgstr "Oxford University Press"
|
||||
|
||||
msgid "Let's build something together"
|
||||
msgstr "Costruiamo qualcosa insieme"
|
||||
|
||||
msgid "My Timeline"
|
||||
msgstr "La mia Cronologia"
|
||||
|
||||
# Cyberpunk & Editorial Layouts
|
||||
msgid "SYSTEM_ACTIVE"
|
||||
msgstr "SISTEMA_ATTIVO"
|
||||
|
||||
msgid "Core Stack"
|
||||
msgstr "Stack Principale"
|
||||
|
||||
msgid "Chronology"
|
||||
msgstr "Cronologia"
|
||||
|
||||
msgid "Project Modules"
|
||||
msgstr "Moduli Progetto"
|
||||
|
||||
msgid "Developer Profile"
|
||||
msgstr "Profilo Sviluppatore"
|
||||
|
||||
msgid "Social Profiles"
|
||||
msgstr "Profili Social"
|
||||
|
||||
# Brutalist & Neumorphic Layouts
|
||||
msgid "Connect"
|
||||
msgstr "Connetti"
|
||||
|
||||
msgid "I'm open for collaboration and backend architectures development."
|
||||
msgstr "Sono disponibile per collaborazioni e sviluppo di architetture backend."
|
||||
|
||||
msgid "Work"
|
||||
msgstr "Lavoro"
|
||||
|
||||
msgid "Timeline"
|
||||
msgstr "Cronologia"
|
||||
|
||||
msgid "Databases & IoT"
|
||||
msgstr "Database e IoT"
|
||||
|
||||
msgid "Augsburg, Germany — Sevilla, Spain"
|
||||
msgstr "Augusta, Germania — Siviglia, Spagna"
|
||||
|
||||
|
||||
|
||||
|
||||
msgid "Proactive & Autonomous Software Developer specializing in robust backend architectures, system optimization, and full-stack integration. Driven by curiosity and a lifetime passion for computing."
|
||||
msgstr "Sviluppatore software proattivo e autonomo specializzato in architetture backend robuste, ottimizzazione dei sistemi e integrazione full-stack. Spinto dalla curiosità e da una passione per l'informatica che dura da tutta la vita."
|
||||
|
||||
msgid "A computer enthusiast since childhood with over <1>6 years of professional development experience</1> across Spain and Germany. Proven ability to adapt quickly through self-learning and autonomously solve complex architectural and engineering challenges without requiring permanent supervision. Adept at managing system administration, maintaining databases, and driving collaborative growth within agile team environments."
|
||||
msgstr "Un appassionato di computer fin dall'infanzia con oltre <1>6 anni di esperienza nello sviluppo professionale</1> in Spagna e Germania. Comprovata capacità di adattarsi rapidamente attraverso l'autoapprendimento e risolvere autonomamente complesse sfide architetturali e ingegneristiche senza richiedere una supervisione permanente. Esperto nella gestione dell'amministrazione di sistema, nella manutenzione dei database e nella promozione della crescita collaborativa all'interno di ambienti di team agili."
|
||||
|
||||
msgid "Circadian Cycle (Auto)"
|
||||
msgstr "Ciclo Circadiano (Auto)"
|
||||
|
||||
msgid "Cycles layouts automatically based on the time of day. Daylight is clean editorial/soft neumorphism, afternoons are brutalist, evenings are dark developer mode, late nights are terminal command lines."
|
||||
msgstr "Cambia il layout automaticamente in base all'ora del giorno. La luce del giorno è un editoriale pulito/morbido neumorfismo, il pomeriggio è brutalista, la sera è in modalità sviluppatore scura, la tarda notte è una riga di comando del terminale."
|
||||
|
||||
msgid "Interactive 3D"
|
||||
msgstr "3D Interattivo"
|
||||
|
||||
msgid "An immersive layout featuring mouse-tracking 3D tilt cards, parallax effects, and a custom canvas-rendered 3D rotating starfield constellation."
|
||||
msgstr "Un layout immersivo con schede inclinabili 3D che seguono il mouse, effetti di parallasse e una costellazione di stelle rotante 3D personalizzata renderizzata su canvas."
|
||||
|
||||
msgid "Warm Editorial"
|
||||
msgstr "Editoriale Caldo"
|
||||
|
||||
msgid "Clean newspaper and blog style with high typography contrast, warm cream background, and spacious borders."
|
||||
msgstr "Stile pulito da giornale e blog con alto contrasto tipografico, caldo sfondo crema e ampi bordi."
|
||||
|
||||
msgid "Cyberpunk Neon"
|
||||
msgstr "Cyberpunk Neon"
|
||||
|
||||
msgid "Vibrant vaporwave style with fluorescent borders, glowing shadows, grid backgrounds, and tech scanner overlay."
|
||||
msgstr "Vibrante stile vaporwave con bordi fluorescenti, ombre luminose, sfondi a griglia e overlay scanner tecnologico."
|
||||
|
||||
msgid "Neo Brutalist"
|
||||
msgstr "Neobrutalista"
|
||||
|
||||
msgid "High contrast flat design. Bright warning yellow panels, thick black border boxes, and solid shadows."
|
||||
msgstr "Design piatto ad alto contrasto. Pannelli gialli di avvertimento luminosi, scatole con bordi neri spessi e ombre solide."
|
||||
|
||||
msgid "Soft Neumorphism"
|
||||
msgstr "Neumorfismo Morbido"
|
||||
|
||||
msgid "Soft tactile layout using smooth beveled cards, extruded shadows, and recessed active states."
|
||||
msgstr "Layout tattile morbido che utilizza schede smussate lisce, ombre estruse e stati attivi incassati."
|
||||
|
||||
msgid "Vivid aurora gradient backgrounds with glassmorphic cards, animated glows, and a modern linear layout."
|
||||
msgstr "Vivaci sfondi sfumati aurora con schede glasmorfiche, bagliori animati e un moderno layout lineare."
|
||||
|
||||
msgid "Spoken Languages"
|
||||
msgstr "Lingue Parlate"
|
||||
|
||||
msgid "Invalid email address"
|
||||
msgstr "Indirizzo email non valido"
|
||||
|
||||
msgid "Get in Touch"
|
||||
msgstr "Mettiti in contatto"
|
||||
|
||||
msgid "Contact Me"
|
||||
msgstr "Contattami"
|
||||
|
||||
msgid "I'm always open to discussing new projects, creative ideas, or opportunities. Feel free to reach out via email or send me a message!"
|
||||
msgstr "Sono sempre aperto a discutere di nuovi progetti, idee creative o opportunità. Sentiti libero di contattarmi via e-mail o inviarmi un messaggio!"
|
||||
|
||||
msgid "Download CV"
|
||||
msgstr "Scarica CV"
|
||||
|
||||
msgid "Professional Software Developer"
|
||||
msgstr "Sviluppatore Software Professionista"
|
||||
|
||||
msgid "Software Developer"
|
||||
msgstr "Sviluppatore Software"
|
||||
|
||||
msgid "Years Exp."
|
||||
msgstr "Anni di Esp."
|
||||
|
||||
msgid "Languages"
|
||||
msgstr "Lingue"
|
||||
|
||||
msgid "Backend"
|
||||
msgstr "Backend"
|
||||
|
||||
msgid "Frontend"
|
||||
msgstr "Frontend"
|
||||
|
||||
msgid "GONZÁLEZ ALBES"
|
||||
msgstr "GONZÁLEZ ALBES"
|
||||
|
||||
msgid "The Engineer"
|
||||
msgstr "L'Ingegnere"
|
||||
|
||||
msgid "Technical Skills"
|
||||
msgstr "Competenze Tecniche"
|
||||
|
||||
msgid "Selected Projects"
|
||||
msgstr "Progetti Selezionati"
|
||||
|
||||
msgid "Work Experience Timeline"
|
||||
msgstr "Cronologia dell'Esperienza Lavorativa"
|
||||
|
||||
msgid "Education Timeline"
|
||||
msgstr "Cronologia dell'Istruzione"
|
||||
|
||||
msgid "I enjoy solving complex architectural challenges, designing robust APIs, containerizing deployment environments, and exploring deep learning models."
|
||||
msgstr "Mi piace risolvere sfide architetturali complesse, progettare API robuste, containerizzare ambienti di distribuzione ed esplorare modelli di deep learning."
|
||||
|
||||
msgid "A collection of featured projects and engineering work."
|
||||
msgstr "Una raccolta di progetti in evidenza e lavori di ingegneria."
|
||||
|
||||
msgid "Architected and optimized high-performance backend microservices and APIs utilizing PHP (Symfony), Python, and Go for comparative portal engines serving millions of monthly active users."
|
||||
msgstr "Progettazione e ottimizzazione di microservizi e API backend ad alte prestazioni utilizzando PHP (Symfony), Python e Go per motori di portali comparativi che servono milioni di utenti attivi mensili."
|
||||
|
||||
msgid "Executed proactive server and database administration to ensure maximum uptime, scalability, and robust data integration pipelines."
|
||||
msgstr "Amministrazione proattiva di server e database per garantire il massimo tempo di attività, scalabilità e solide pipeline di integrazione dei dati."
|
||||
|
||||
msgid "Promoted from Junior Software Engineer to Professional Software Engineer in April 2024 in recognition of ownership of critical core systems."
|
||||
msgstr "Promosso da Junior Software Engineer a Professional Software Engineer ad aprile 2024 in riconoscimento della responsabilità sui sistemi core critici."
|
||||
|
||||
msgid "Established containerized local test suites using Docker, reducing onboarding time for new developers and standardizing testing environments."
|
||||
msgstr "Configurazione di suite di test locali containerizzate utilizzando Docker, riducendo i tempi di inserimento per i nuovi sviluppatori e standardizzando gli ambienti di test."
|
||||
|
||||
msgid "Participated in agile Scrum ceremonies, writing comprehensive API specifications, and ensuring system maintenance and high availability."
|
||||
msgstr "Partecipazione a cerimonie Scrum agili, stesura di specifiche API dettagliate e garanzia di manutenzione del sistema ed elevata disponibilità."
|
||||
|
||||
msgid "Engineered interactive user interfaces using HTML5, CSS3, JavaScript, and JQuery, ensuring compliance with responsive web design principles."
|
||||
msgstr "Sviluppo di interfacce utente interattive utilizzando HTML5, CSS3, JavaScript e JQuery, garantendo la conformità ai principi del responsive web design."
|
||||
|
||||
msgid "Created custom backend logic, hooks, and modules using PHP and the Symfony framework to support e-commerce and media tools."
|
||||
msgstr "Creazione di logica backend, hook e moduli personalizzati utilizzando PHP e il framework Symfony per supportare strumenti di e-commerce e multimediali."
|
||||
|
||||
msgid "Implemented and customized enterprise content management systems (CMS) using Typo3, writing custom TypoScript extensions."
|
||||
msgstr "Implementazione e personalizzazione di sistemi di gestione dei contenuti aziendali (CMS) utilizzando Typo3, scrivendo estensioni TypoScript personalizzate."
|
||||
|
||||
msgid "Collaborated with product designers to map out system dependencies and architecture flows using formal UML diagrams."
|
||||
msgstr "Collaborazione con i product designer per mappare le dipendenze del sistema e i flussi dell'architettura utilizzando diagrammi UML formali."
|
||||
|
||||
msgid "Researched and implemented state-of-the-art recommendation systems based on collaborative filtering and fuzzy clustering models."
|
||||
msgstr "Ricerca e implementazione di sistemi di raccomandazione all'avanguardia basati su modelli di filtraggio collaborativo e fuzzy clustering."
|
||||
|
||||
msgid "Processed and analyzed large consumer transaction datasets using Python (Pandas, NumPy) to evaluate recommender accuracy."
|
||||
msgstr "Elaborazione e analisi di ampi set di dati sulle transazioni dei consumatori utilizzando Python (Pandas, NumPy) per valutare l'accuratezza del raccomandatore."
|
||||
|
||||
msgid "Designed and query-tuned graph-oriented database structures in Neo4j to model user-product buying patterns in real time."
|
||||
msgstr "Progettazione e ottimizzazione delle query di strutture di database orientate ai grafi in Neo4j per modellare i modelli di acquisto utente-prodotto in tempo reale."
|
||||
|
||||
msgid "Built supporting web portal features in JavaScript, PHP, and Symfony for academic research evaluations."
|
||||
msgstr "Creazione di funzionalità di supporto per portali web in JavaScript, PHP e Symfony per valutazioni di ricerca accademica."
|
||||
|
||||
msgid "Developed cross-platform mobile applications for iOS and Android using the Ionic Framework and Angular."
|
||||
msgstr "Sviluppo di applicazioni mobili multipiattaforma per iOS e Android utilizzando Ionic Framework e Angular."
|
||||
|
||||
msgid "Created automated enterprise scripts, notifications, and sheet integrations using Google Apps Script."
|
||||
msgstr "Creazione di script aziendali automatizzati, notifiche e integrazioni di fogli di calcolo utilizzando Google Apps Script."
|
||||
|
||||
msgid "Managed Linux production and staging servers, configuring Apache/Nginx web servers, security groups, and SSH configurations."
|
||||
msgstr "Gestione di server di produzione e di staging Linux, configurazione di server web Apache/Nginx, gruppi di sicurezza e configurazioni SSH."
|
||||
|
||||
msgid "Maintained legacy client websites, writing custom MySQL queries, and migrating servers with minimal downtime."
|
||||
msgstr "Manutenzione dei siti web legacy dei clienti, stesura di query MySQL personalizzate e migrazione dei server con tempi di inattività minimi."
|
||||
|
||||
msgid "Degree in Computer Engineering in Information Systems"
|
||||
msgstr "Laurea in Ingegneria Informatica nei Sistemi Informativi"
|
||||
|
||||
msgid "Graduated with a thesis on Recommender Systems. Coursework: Advanced Algorithms, Database Management Systems, Software Engineering Patterns, Networks, and Operating Systems."
|
||||
msgstr "Laureato con una tesi sui Sistemi di Raccomandazione. Corsi: Algoritmi Avanzati, Sistemi di Gestione di Database, Pattern di Ingegneria del Software, Reti e Sistemi Operativi."
|
||||
|
||||
msgid "Master's Degree in Computer Science and Engineering"
|
||||
msgstr "Laurea Magistrale in Informatica e Ingegneria"
|
||||
|
||||
msgid "Erasmus exchange year focusing on high-level systems. Coursework: Software Architectures, Distributed Systems, Advanced Web Technologies, Machine Learning, and Information Retrieval."
|
||||
msgstr "Anno di scambio Erasmus focalizzato sui sistemi di alto livello. Corsi: Architetture Software, Sistemi Distribuiti, Tecnologie Web Avanzate, Machine Learning e Information Retrieval."
|
||||
|
||||
msgid "Certificate of Higher Education - Multiplatform App Development"
|
||||
msgstr "Diploma di Istruzione Superiore in Sviluppo di Applicazioni Multipiattaforma"
|
||||
|
||||
msgid "Focused on native Android development (Java), database design, cross-platform UI architectures, and project management methodologies."
|
||||
msgstr "Focalizzato sullo sviluppo Android nativo (Java), progettazione di database, architetture UI multipiattaforma e metodologie di gestione dei progetti."
|
||||
|
||||
msgid "Certificate of Higher Education - Web App Development"
|
||||
msgstr "Diploma di Istruzione Superiore in Sviluppo di Applicazioni Web"
|
||||
|
||||
msgid "Practical curriculum covering HTML5/CSS3/JavaScript, backend scripting in PHP, database engines (SQL), and deployment of web servers."
|
||||
msgstr "Programma pratico che copre HTML5/CSS3/JavaScript, scripting backend in PHP, motori di database (SQL) e implementazione di server web."
|
||||
|
||||
msgid "Vocational Training in Microcomputer Systems & Networks"
|
||||
msgstr "Formazione Professionale in Sistemi Microinformatici e Reti"
|
||||
|
||||
msgid "Hands-on foundation in computer networks, operating system configuration, PC hardware assembly, and network troubleshooting."
|
||||
msgstr "Fondamenta pratiche nelle reti informatiche, configurazione del sistema operativo, assemblaggio hardware del PC e risoluzione dei problemi di rete."
|
||||
|
||||
msgid "Professional Certifications"
|
||||
msgstr "Certificazioni Professionali"
|
||||
|
||||
msgid "Oxford Test of English (Level B1)"
|
||||
msgstr "Oxford Test of English (Livello B1)"
|
||||
|
||||
msgid "Technical Certifications: Advanced C++, Java Enterprise, and SQL Database Design."
|
||||
msgstr "Certificazioni tecniche: C++ Avanzado, Java Enterprise e Progettazione di Database SQL."
|
||||
|
||||
msgid "Mediaconcept GmbH (Germany)"
|
||||
msgstr "Mediaconcept GmbH (Germania)"
|
||||
|
||||
msgid "University of Bologna (Erasmus)"
|
||||
msgstr "Università di Bologna (Erasmus)"
|
||||
|
||||
msgid "I.E.S Velázquez"
|
||||
msgstr "I.E.S Velázquez"
|
||||
|
||||
msgid "Center Nuevas Profesiones"
|
||||
msgstr "Centro Nuevas Profesiones"
|
||||
|
||||
msgid "I.E.S Axati"
|
||||
msgstr "I.E.S Axati"
|
||||
|
||||
msgid "Various Institutions"
|
||||
msgstr "Varie Istituzioni"
|
||||
|
||||
msgid "Online & Academic"
|
||||
msgstr "Online e Accademico"
|
||||
|
||||
msgid "Writing clean, testable code"
|
||||
msgstr "Scrittura di codice pulito e testabile"
|
||||
|
||||
msgid "PHP & Symfony"
|
||||
msgstr "PHP e Symfony"
|
||||
|
||||
msgid "Senior / Expert"
|
||||
msgstr "Senior / Esperto"
|
||||
|
||||
msgid "Python (Deep Learning)"
|
||||
msgstr "Python (Deep Learning)"
|
||||
|
||||
msgid "Advanced / Intermediate"
|
||||
msgstr "Avanzado / Intermedio"
|
||||
|
||||
msgid "Java / OOP"
|
||||
msgstr "Java / OOP"
|
||||
|
||||
msgid "Advanced"
|
||||
msgstr "Avanzato"
|
||||
|
||||
msgid "C / C++"
|
||||
msgstr "C / C++"
|
||||
|
||||
msgid "Intermediate"
|
||||
msgstr "Intermedio"
|
||||
|
||||
msgid "Server Maintenance & Bash"
|
||||
msgstr "Manutenzione Server e Bash"
|
||||
|
||||
msgid "JavaScript / TypeScript"
|
||||
msgstr "JavaScript / TypeScript"
|
||||
|
||||
msgid "Senior"
|
||||
msgstr "Senior"
|
||||
|
||||
msgid "HTML5 & CSS3"
|
||||
msgstr "HTML5 e CSS3"
|
||||
|
||||
msgid "Expert"
|
||||
msgstr "Esperto"
|
||||
|
||||
msgid "JQuery & Bootstrap"
|
||||
msgstr "JQuery e Bootstrap"
|
||||
|
||||
msgid "React"
|
||||
msgstr "React"
|
||||
|
||||
msgid "SQL (MySQL, PostgreSQL)"
|
||||
msgstr "SQL (MySQL, PostgreSQL)"
|
||||
|
||||
msgid "Neo4j (Graph DB)"
|
||||
msgstr "Neo4j (Database a Grafi)"
|
||||
|
||||
msgid "Docker / Containers"
|
||||
msgstr "Docker / Container"
|
||||
|
||||
msgid "IoT (Arduino, ESP8266)"
|
||||
msgstr "IoT (Arduino, ESP8266)"
|
||||
|
||||
msgid "Aurora Glass"
|
||||
msgstr "Vetro Aurora"
|
||||
|
||||
msgid "Time Distortion Active: Cycling layouts..."
|
||||
msgstr "Distorsione temporale attiva: Ciclo dei layout in corso..."
|
||||
20
src/main.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import './index.css';
|
||||
import './i18n'; // Initialize i18n translation configuration
|
||||
import App from './App.tsx';
|
||||
|
||||
// Easter egg console message
|
||||
console.log(
|
||||
"%c🕵️♂️ Under the hood, huh?",
|
||||
"color: #39d353; font-size: 20px; font-weight: bold; background: #0d1117; padding: 10px; border-radius: 5px; border: 1px solid #30363d;"
|
||||
);
|
||||
console.log("If you enjoy inspecting my portfolio, let's build something together! Contact me at rafaelgonzalezalbes@gmail.com");
|
||||
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
);
|
||||
|
||||
505
src/templates/AuroraTemplate.module.css
Normal file
|
|
@ -0,0 +1,505 @@
|
|||
/* ============================================================
|
||||
Aurora Glass Template - Vivid Gradient + Glassmorphism
|
||||
============================================================ */
|
||||
|
||||
.auroraWrap {
|
||||
min-height: 100vh;
|
||||
background: #030712;
|
||||
font-family: 'Inter', 'Outfit', sans-serif;
|
||||
color: #f0f4ff;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Animated aurora background */
|
||||
.auroraWrap::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(ellipse 80% 60% at 20% 10%, rgba(124, 58, 237, 0.35) 0%, transparent 60%),
|
||||
radial-gradient(ellipse 60% 80% at 80% 80%, rgba(6, 182, 212, 0.3) 0%, transparent 60%),
|
||||
radial-gradient(ellipse 70% 50% at 50% 50%, rgba(16, 185, 129, 0.15) 0%, transparent 70%);
|
||||
animation: auroraShift 10s ease-in-out infinite alternate;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
@keyframes auroraShift {
|
||||
0% { opacity: 1; transform: scale(1) rotate(0deg); }
|
||||
50% { opacity: 0.85; transform: scale(1.05) rotate(1deg); }
|
||||
100% { opacity: 1; transform: scale(1) rotate(-1deg); }
|
||||
}
|
||||
|
||||
/* ---- Navbar ---- */
|
||||
.nav {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px 40px;
|
||||
background: rgba(3, 7, 18, 0.6);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.navLogo {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.5px;
|
||||
color: #a78bfa;
|
||||
}
|
||||
|
||||
.navLogo span { color: #22d3ee; }
|
||||
|
||||
.navLinks {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.navLink {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(240, 244, 255, 0.55);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.navLink:hover { color: #f0f4ff; }
|
||||
|
||||
.navRight {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.langBtn {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
padding: 5px 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: all 0.2s;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.langBtn:hover, .langBtnActive { opacity: 1; border-color: #7c3aed; }
|
||||
|
||||
.backBtn {
|
||||
background: rgba(124, 58, 237, 0.15);
|
||||
border: 1px solid rgba(124, 58, 237, 0.35);
|
||||
border-radius: 8px;
|
||||
color: #a78bfa;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
padding: 6px 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.backBtn:hover {
|
||||
background: rgba(124, 58, 237, 0.3);
|
||||
border-color: #a78bfa;
|
||||
}
|
||||
|
||||
/* ---- Page ---- */
|
||||
.page {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px 80px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* ---- Hero ---- */
|
||||
.hero {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 40px;
|
||||
align-items: center;
|
||||
padding: 80px 0 60px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.07);
|
||||
}
|
||||
|
||||
.heroTag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: rgba(124, 58, 237, 0.12);
|
||||
border: 1px solid rgba(124, 58, 237, 0.3);
|
||||
border-radius: 99px;
|
||||
padding: 5px 14px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: #a78bfa;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.heroTag::before {
|
||||
content: '';
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: #a78bfa;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.4; transform: scale(0.8); }
|
||||
}
|
||||
|
||||
.heroName {
|
||||
font-size: clamp(2.2rem, 5vw, 3.8rem);
|
||||
font-weight: 800;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -1.5px;
|
||||
margin: 0 0 12px;
|
||||
background: linear-gradient(135deg, #f0f4ff 0%, #a78bfa 50%, #22d3ee 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.heroRole {
|
||||
font-size: 1.15rem;
|
||||
color: rgba(240, 244, 255, 0.55);
|
||||
margin-bottom: 28px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.heroCta {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btnPrimary {
|
||||
background: linear-gradient(135deg, #7c3aed, #06b6d4);
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
color: #fff;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
padding: 11px 24px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
font-family: inherit;
|
||||
box-shadow: 0 0 24px rgba(124, 58, 237, 0.35);
|
||||
}
|
||||
|
||||
.btnPrimary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 0 36px rgba(124, 58, 237, 0.55);
|
||||
}
|
||||
|
||||
.btnSecondary {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 10px;
|
||||
color: rgba(240, 244, 255, 0.8);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
padding: 11px 24px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.btnSecondary:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
.heroStats {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.statCard {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 14px;
|
||||
padding: 18px 24px;
|
||||
text-align: center;
|
||||
backdrop-filter: blur(10px);
|
||||
transition: all 0.3s;
|
||||
min-width: 110px;
|
||||
}
|
||||
|
||||
.statCard:hover {
|
||||
border-color: rgba(124, 58, 237, 0.4);
|
||||
background: rgba(124, 58, 237, 0.08);
|
||||
}
|
||||
|
||||
.statValue {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 800;
|
||||
background: linear-gradient(135deg, #a78bfa, #22d3ee);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.statLabel {
|
||||
font-size: 0.7rem;
|
||||
color: rgba(240, 244, 255, 0.45);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* ---- Glass Card ---- */
|
||||
.glassCard {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 20px;
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
padding: 32px;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.glassCard:hover {
|
||||
border-color: rgba(124, 58, 237, 0.3);
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.14em;
|
||||
color: #a78bfa;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* ---- Two-col layout ---- */
|
||||
.twoCol {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 24px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
/* ---- Skills ---- */
|
||||
.skillGrid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.skillTag {
|
||||
background: rgba(6, 182, 212, 0.08);
|
||||
border: 1px solid rgba(6, 182, 212, 0.2);
|
||||
border-radius: 99px;
|
||||
color: #22d3ee;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 500;
|
||||
padding: 5px 13px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.skillTag:hover {
|
||||
background: rgba(6, 182, 212, 0.16);
|
||||
border-color: rgba(6, 182, 212, 0.45);
|
||||
}
|
||||
|
||||
/* ---- Timeline ---- */
|
||||
.timeline { display: flex; flex-direction: column; gap: 20px; }
|
||||
|
||||
.timelineItem {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.timelineDot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #7c3aed, #22d3ee);
|
||||
margin-top: 6px;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 0 10px rgba(124, 58, 237, 0.5);
|
||||
}
|
||||
|
||||
.timelineDate {
|
||||
font-size: 0.7rem;
|
||||
color: #a78bfa;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.timelineTitle {
|
||||
font-size: 0.92rem;
|
||||
font-weight: 700;
|
||||
color: #f0f4ff;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.timelineSub {
|
||||
font-size: 0.78rem;
|
||||
color: rgba(240, 244, 255, 0.5);
|
||||
}
|
||||
|
||||
/* ---- Projects ---- */
|
||||
.projectGrid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.projectCard {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.07);
|
||||
border-radius: 16px;
|
||||
padding: 22px 26px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 20px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.projectCard:hover {
|
||||
border-color: rgba(6, 182, 212, 0.35);
|
||||
background: rgba(6, 182, 212, 0.04);
|
||||
transform: translateX(6px);
|
||||
}
|
||||
|
||||
.projectNum {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 800;
|
||||
background: linear-gradient(135deg, rgba(124,58,237,0.3), rgba(6,182,212,0.3));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
flex-shrink: 0;
|
||||
line-height: 1;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.projectTitle {
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
color: #f0f4ff;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.projectDesc {
|
||||
font-size: 0.82rem;
|
||||
color: rgba(240, 244, 255, 0.5);
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.projectTags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.projectTag {
|
||||
background: rgba(124, 58, 237, 0.08);
|
||||
border: 1px solid rgba(124, 58, 237, 0.2);
|
||||
border-radius: 99px;
|
||||
color: #a78bfa;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
|
||||
/* ---- Contact ---- */
|
||||
.contactRow {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.contactCard {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 16px;
|
||||
padding: 22px 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
transition: all 0.3s;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.contactCard:hover {
|
||||
border-color: rgba(167, 139, 250, 0.4);
|
||||
background: rgba(124, 58, 237, 0.06);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.contactIcon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
background: rgba(124, 58, 237, 0.12);
|
||||
border: 1px solid rgba(124, 58, 237, 0.25);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #a78bfa;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.contactLabel { font-size: 0.7rem; color: rgba(240, 244, 255, 0.4); text-transform: uppercase; letter-spacing: 0.08em; }
|
||||
.contactValue { font-size: 0.88rem; font-weight: 600; color: #f0f4ff; }
|
||||
|
||||
.span2 {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
/* ---- Responsive ---- */
|
||||
@media (max-width: 768px) {
|
||||
.nav { padding: 14px 20px; }
|
||||
.navLinks { display: none; }
|
||||
.hero { grid-template-columns: 1fr; padding: 50px 0 40px; }
|
||||
.heroStats { flex-direction: row; flex-wrap: wrap; justify-content: center; gap: 12px; }
|
||||
.twoCol { grid-template-columns: 1fr; }
|
||||
.span2 { grid-column: span 1; }
|
||||
.projectCard {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.nav {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
.navRight {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
.page {
|
||||
padding: 0 16px 60px;
|
||||
}
|
||||
.heroName {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
}
|
||||
177
src/templates/AuroraTemplate.tsx
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { getWorkItems, getEducationItems, getProjects } from '../data/portfolioData';
|
||||
import styles from './AuroraTemplate.module.css';
|
||||
|
||||
const flagMap: Record<string, string> = { en: 'gb', es: 'es', it: 'it', de: 'de' };
|
||||
const FlagImg = ({ code }: { code: string }) => {
|
||||
const country = flagMap[code] || code;
|
||||
return (
|
||||
<img
|
||||
src={`/flags/${country}.png`}
|
||||
width={20}
|
||||
alt={code.toUpperCase()}
|
||||
style={{ display: 'inline-block', verticalAlign: 'middle', borderRadius: '2px' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const langs = [{ code: 'en' }, { code: 'es' }, { code: 'it' }, { code: 'de' }];
|
||||
|
||||
export default function AuroraTemplate() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const currentLang = i18n.language ? i18n.language.substring(0, 2) : 'en';
|
||||
|
||||
const skills = ['PHP', 'Symfony', 'TypeScript', 'React', 'Docker', 'MySQL', 'PostgreSQL', 'REST APIs', 'SOAP', 'Typo3', 'Git', 'Linux'];
|
||||
|
||||
const workItems = getWorkItems(t);
|
||||
const eduItems = getEducationItems(t);
|
||||
const projects = getProjects(t);
|
||||
|
||||
return (
|
||||
<div className={styles.auroraWrap}>
|
||||
{/* Navbar */}
|
||||
<nav className={styles.nav}>
|
||||
<div className={styles.navLogo} onClick={() => window.dispatchEvent(new CustomEvent('goHome'))} style={{ cursor: 'pointer' }}>rga://<span>aurora</span></div>
|
||||
<div className={styles.navLinks}>
|
||||
{['about', 'skills', 'experience', 'projects', 'contact'].map(s => (
|
||||
<button key={s} className={styles.navLink}
|
||||
onClick={() => document.getElementById(s)?.scrollIntoView({ behavior: 'smooth' })}>
|
||||
{t(s.charAt(0).toUpperCase() + s.slice(1))}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.navRight}>
|
||||
{langs.map(l => (
|
||||
<button
|
||||
key={l.code}
|
||||
onClick={() => i18n.changeLanguage(l.code)}
|
||||
className={`${styles.langBtn} ${currentLang === l.code ? styles.langBtnActive : ''}`}
|
||||
title={l.code.toUpperCase()}
|
||||
>
|
||||
<FlagImg code={l.code} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div className={styles.page}>
|
||||
{/* Hero */}
|
||||
<section className={styles.hero} id="about">
|
||||
<div>
|
||||
<div className={styles.heroTag}>Open to opportunities</div>
|
||||
<h1 className={styles.heroName}>Rafael González Albes</h1>
|
||||
<p className={styles.heroRole}>{t('Software Developer')} · PHP · TypeScript · React</p>
|
||||
<div className={styles.heroCta}>
|
||||
<a href="mailto:rafaelgonzalezalbes@gmail.com">
|
||||
<button className={styles.btnPrimary}>{t('Contact Me')}</button>
|
||||
</a>
|
||||
<a href="/Rafael_Gonzalez_Albes_CV.pdf" download>
|
||||
<button className={styles.btnSecondary} style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
{t('Download CV')}
|
||||
</button>
|
||||
</a>
|
||||
<a href="https://github.com/anik0z" target="_blank" rel="noreferrer">
|
||||
<button className={styles.btnSecondary}>GitHub</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.heroStats}>
|
||||
{[
|
||||
{ value: '4.5+', label: t('Years Exp.') },
|
||||
{ value: '5', label: t('Languages') },
|
||||
{ value: '5+', label: t('Projects') },
|
||||
].map(s => (
|
||||
<div key={s.label} className={styles.statCard}>
|
||||
<div className={styles.statValue}>{s.value}</div>
|
||||
<div className={styles.statLabel}>{s.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Skills */}
|
||||
<div className={styles.twoCol} id="skills">
|
||||
<div className={`${styles.glassCard} ${styles.span2}`}>
|
||||
<div className={styles.sectionTitle}>⚡ {t('Skills')}</div>
|
||||
<div className={styles.skillGrid}>
|
||||
{skills.map(s => <span key={s} className={styles.skillTag}>{s}</span>)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Work */}
|
||||
<div className={styles.glassCard} id="experience">
|
||||
<div className={styles.sectionTitle}>💼 {t('Experience')}</div>
|
||||
<div className={styles.timeline}>
|
||||
{workItems.map(w => (
|
||||
<div key={w.title} className={styles.timelineItem}>
|
||||
<div className={styles.timelineDot} />
|
||||
<div>
|
||||
<div className={styles.timelineDate}>{w.date}</div>
|
||||
<div className={styles.timelineTitle}>{w.title}</div>
|
||||
<div className={styles.timelineSub}>{w.company} · {w.location}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Education */}
|
||||
<div className={styles.glassCard}>
|
||||
<div className={styles.sectionTitle}>🎓 {t('Education')}</div>
|
||||
<div className={styles.timeline}>
|
||||
{eduItems.map(e => (
|
||||
<div key={e.title} className={styles.timelineItem}>
|
||||
<div className={styles.timelineDot} />
|
||||
<div>
|
||||
<div className={styles.timelineDate}>{e.date}</div>
|
||||
<div className={styles.timelineTitle}>{e.title}</div>
|
||||
<div className={styles.timelineSub}>{e.company} · {e.location}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Projects */}
|
||||
<div id="projects">
|
||||
<div className={styles.sectionTitle} style={{ marginTop: 40 }}>🚀 {t('Projects')}</div>
|
||||
<div className={styles.projectGrid}>
|
||||
{projects.map((p, i) => (
|
||||
<div key={p.title} className={styles.projectCard}>
|
||||
<div className={styles.projectNum}>0{i + 1}</div>
|
||||
<div>
|
||||
<div className={styles.projectTitle}>{p.title}</div>
|
||||
<div className={styles.projectDesc}>{p.shortDesc}</div>
|
||||
<div className={styles.projectTags}>
|
||||
{p.tech.map(tag => <span key={tag} className={styles.projectTag}>{tag}</span>)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact */}
|
||||
<div id="contact">
|
||||
<div className={styles.sectionTitle} style={{ marginTop: 40 }}>✉️ {t('Contact')}</div>
|
||||
<div className={styles.contactRow}>
|
||||
{[
|
||||
{ label: 'Email', value: 'rafaelgonzalezalbes@gmail.com', href: 'mailto:rafaelgonzalezalbes@gmail.com', icon: '✉' },
|
||||
{ label: 'GitHub', value: 'github.com/anik0z', href: 'https://github.com/anik0z', icon: '⌨' },
|
||||
{ label: 'LinkedIn', value: 'rafaelgonzalezalbes', href: 'https://linkedin.com/in/rafaelgonzalezalbes', icon: '💼' },
|
||||
].map(c => (
|
||||
<a key={c.label} href={c.href} target="_blank" rel="noreferrer" className={styles.contactCard}>
|
||||
<div className={styles.contactIcon}>{c.icon}</div>
|
||||
<div>
|
||||
<div className={styles.contactLabel}>{c.label}</div>
|
||||
<div className={styles.contactValue}>{c.value}</div>
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
555
src/templates/BentoTemplate.module.css
Normal file
|
|
@ -0,0 +1,555 @@
|
|||
.bentoContainer {
|
||||
min-height: 100vh;
|
||||
background-color: #0b0717; /* Deep violet-black background */
|
||||
color: #f1f5f9;
|
||||
padding: 40px 24px;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Background glowing lights for Bento theme */
|
||||
.bentoContainer::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: -10%;
|
||||
right: -10%;
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
background: radial-gradient(circle, rgba(127, 0, 255, 0.08) 0%, transparent 60%);
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
filter: blur(80px);
|
||||
}
|
||||
|
||||
.bentoContainer::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
bottom: -10%;
|
||||
left: -10%;
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
background: radial-gradient(circle, rgba(244, 63, 94, 0.06) 0%, transparent 60%);
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
filter: blur(80px);
|
||||
}
|
||||
|
||||
.header {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto 30px auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.logoColor {
|
||||
background: linear-gradient(135deg, #c084fc 0%, #f43f5e 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.headerRight {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.langBtn {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 6px;
|
||||
color: #94a3b8;
|
||||
cursor: pointer;
|
||||
padding: 6px 12px;
|
||||
font-family: var(--font-heading);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.langBtn:hover {
|
||||
border-color: #c084fc;
|
||||
color: #c084fc;
|
||||
}
|
||||
|
||||
.langBtnActive {
|
||||
background: rgba(192, 132, 252, 0.15);
|
||||
border-color: #c084fc;
|
||||
color: #c084fc;
|
||||
}
|
||||
|
||||
/* Bento Grid */
|
||||
.grid {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
gap: 20px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 40px 0 rgba(192, 132, 252, 0.12);
|
||||
border-color: rgba(192, 132, 252, 0.25);
|
||||
}
|
||||
|
||||
/* Bento Card Placements */
|
||||
.cardHero {
|
||||
grid-column: span 7;
|
||||
}
|
||||
|
||||
.cardHero h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 800;
|
||||
margin-top: 15px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.textHighlight {
|
||||
background: linear-gradient(135deg, #c084fc 0%, #f43f5e 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 20px;
|
||||
padding: 4px 12px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.pulseDot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background-color: #10b981;
|
||||
border-radius: 50%;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7); }
|
||||
70% { transform: scale(1); box-shadow: 0 0 0 6px rgba(16, 185, 129, 0); }
|
||||
100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
|
||||
}
|
||||
|
||||
.roleTitle {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: #c084fc;
|
||||
margin: 10px 0 15px 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #94a3b8;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.cardStats {
|
||||
grid-column: span 4;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.cardStats:hover {
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.statsGrid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.statCard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.statVal {
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
color: #f43f5e;
|
||||
}
|
||||
|
||||
.statLabel {
|
||||
font-size: 0.75rem;
|
||||
color: #94a3b8;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.cardLanguages {
|
||||
grid-column: span 4;
|
||||
}
|
||||
|
||||
.cardHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.cardHeader h3 {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: #c084fc;
|
||||
}
|
||||
|
||||
.langList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.langItem {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 0.85rem;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.langName {
|
||||
color: #f1f5f9;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.langLevel {
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.cardAbout {
|
||||
grid-column: span 7;
|
||||
}
|
||||
|
||||
.aboutPara {
|
||||
color: #94a3b8;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.cardSkills {
|
||||
grid-column: span 5;
|
||||
}
|
||||
|
||||
.cardSkills h3 {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.skillsWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.skillsWrapper h5 {
|
||||
font-size: 0.85rem;
|
||||
color: #c084fc;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.skillsWrapper p {
|
||||
font-size: 0.85rem;
|
||||
color: #94a3b8;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.cardTimeline {
|
||||
grid-column: span 8;
|
||||
}
|
||||
|
||||
.timelineHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.timelineHeader h3 {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.tabSwitcher {
|
||||
display: flex;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 8px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.tabBtn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #94a3b8;
|
||||
cursor: pointer;
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.tabBtnActive {
|
||||
background: rgba(192, 132, 252, 0.15);
|
||||
color: #c084fc;
|
||||
}
|
||||
|
||||
.timelineContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
max-height: 380px;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
width: 100%;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(255, 255, 255, 0.1) transparent;
|
||||
}
|
||||
|
||||
.timelineContent::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.timelineContent::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.timelineItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.timelineItem:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.bentoTimelineDetails {
|
||||
list-style-type: disc;
|
||||
margin-left: 16px;
|
||||
margin-top: 6px;
|
||||
padding-left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.bentoTimelineDetails li {
|
||||
font-size: 0.75rem;
|
||||
color: #94a3b8;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.date {
|
||||
font-size: 0.75rem;
|
||||
color: #f43f5e;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 0.95rem;
|
||||
color: #f1f5f9;
|
||||
}
|
||||
|
||||
.sub {
|
||||
font-size: 0.85rem;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.cardProjects {
|
||||
grid-column: span 8;
|
||||
}
|
||||
|
||||
.bentoProjects {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.bentoProjItem {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.bentoProjItem:hover {
|
||||
border-color: rgba(192, 132, 252, 0.15);
|
||||
}
|
||||
|
||||
.projHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.projHeader h5 {
|
||||
font-size: 0.95rem;
|
||||
color: #f1f5f9;
|
||||
}
|
||||
|
||||
.projIcon {
|
||||
color: #94a3b8;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.projIcon:hover {
|
||||
color: #c084fc;
|
||||
}
|
||||
|
||||
.bentoProjItem p {
|
||||
font-size: 0.85rem;
|
||||
color: #94a3b8;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 0.7rem;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 4px;
|
||||
padding: 2px 6px;
|
||||
color: #c084fc;
|
||||
}
|
||||
|
||||
.cardContact {
|
||||
grid-column: span 5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.cardContact h3 {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.contactList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.contactItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 0.8rem;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.contactItem a {
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.contactItem a:hover {
|
||||
color: #c084fc;
|
||||
}
|
||||
|
||||
.socialIcons {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.socialIcons a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
color: #94a3b8;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.socialIcons a:hover {
|
||||
color: #ffffff;
|
||||
border-color: #c084fc;
|
||||
box-shadow: 0 0 10px rgba(192, 132, 252, 0.3);
|
||||
}
|
||||
|
||||
/* Responsive settings */
|
||||
@media (max-width: 968px) {
|
||||
.cardHero, .cardAbout, .cardProjects {
|
||||
grid-column: span 12;
|
||||
}
|
||||
.cardStats, .cardLanguages, .cardSkills, .cardTimeline, .cardContact {
|
||||
grid-column: span 6;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.cardHero, .cardStats, .cardLanguages, .cardAbout, .cardSkills, .cardTimeline, .cardProjects, .cardContact {
|
||||
grid-column: span 1;
|
||||
}
|
||||
.statsGrid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.header {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.bentoContainer {
|
||||
padding: 24px 16px;
|
||||
}
|
||||
.card {
|
||||
padding: 18px;
|
||||
}
|
||||
}
|
||||
221
src/templates/BentoTemplate.tsx
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getAboutBio, getWorkItems, getEducationItems, getProjects } from '../data/portfolioData';
|
||||
import FlagImg from '../components/FlagImg';
|
||||
import { Award, Globe, Briefcase, GraduationCap, Mail, Github, Linkedin, ExternalLink, Download } from 'lucide-react';
|
||||
import styles from './BentoTemplate.module.css';
|
||||
|
||||
export const BentoTemplate: React.FC = () => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const [expTab, setExpTab] = useState<'work' | 'education'>('work');
|
||||
const currentLang = i18n.language ? i18n.language.substring(0, 2) : 'en';
|
||||
|
||||
const langs = [
|
||||
{ code: 'en', label: '🇬🇧' },
|
||||
{ code: 'es', label: '🇪🇸' },
|
||||
{ code: 'it', label: '🇮🇹' },
|
||||
{ code: 'de', label: '🇩🇪' }
|
||||
];
|
||||
|
||||
const changeLanguage = (code: string) => {
|
||||
i18n.changeLanguage(code);
|
||||
};
|
||||
|
||||
const stats = [
|
||||
{ value: '4.5+', label: t('Years Experience') },
|
||||
{ value: '5', label: t('Languages Spoken') },
|
||||
{ value: '4+', label: t('Academic Qualifications') },
|
||||
{ value: '5+', label: t('Personal Projects') }
|
||||
];
|
||||
|
||||
const languagesList = [
|
||||
{ name: t('Spanish'), level: t('Native / Bilingual') },
|
||||
{ name: t('English'), level: t('Professional (B1 Certified)') },
|
||||
{ name: t('Italian'), level: t('Conversational') },
|
||||
{ name: t('German'), level: t('Basic / Learning') },
|
||||
{ name: t('French'), level: t('Basic / Learning') }
|
||||
];
|
||||
|
||||
const workItems = getWorkItems(t);
|
||||
const educationItems = getEducationItems(t);
|
||||
const projects = getProjects(t);
|
||||
|
||||
return (
|
||||
<div className={styles.bentoContainer}>
|
||||
{/* Header Panel */}
|
||||
<header className={styles.header}>
|
||||
<div className={styles.logo} onClick={() => window.dispatchEvent(new CustomEvent('goHome'))} style={{ cursor: 'pointer' }}>
|
||||
<span>rga://<span className={styles.logoColor}>bento</span></span>
|
||||
</div>
|
||||
<div className={styles.headerRight}>
|
||||
{langs.map((lang) => (
|
||||
<button
|
||||
key={lang.code}
|
||||
onClick={() => changeLanguage(lang.code)}
|
||||
className={`${styles.langBtn} ${currentLang === lang.code ? styles.langBtnActive : ''}`}
|
||||
>
|
||||
<FlagImg code={lang.code} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Grid Layout */}
|
||||
<div className={styles.grid}>
|
||||
|
||||
{/* Cell 1: Intro (Hero) */}
|
||||
<div className={`${styles.card} ${styles.cardHero} glass-card`}>
|
||||
<div className={styles.badge}>
|
||||
<span className={styles.pulseDot}></span>
|
||||
<span>{t('Available for new opportunities')}</span>
|
||||
</div>
|
||||
<h1>
|
||||
Rafael <br />
|
||||
<span className={styles.textHighlight}>González Albes</span>
|
||||
</h1>
|
||||
<p className={styles.roleTitle}>{t('Professional Software Developer')}</p>
|
||||
<p className={styles.description}>
|
||||
{t('Professional Software Developer with 4.5+ years of experience building high-performance backend APIs, handling databases, and crafting clean frontend applications. Specialized in PHP (Symfony), TypeScript, and containerized Docker environments.')}
|
||||
</p>
|
||||
<div style={{ marginTop: '1.5rem' }}>
|
||||
<a href="/Rafael_Gonzalez_Albes_CV.pdf" download className="btn btn-primary" style={{ display: 'inline-flex', alignItems: 'center', gap: '8px', textDecoration: 'none' }}>
|
||||
<Download size={16} />
|
||||
{t('Download CV')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Cell 2: Contact Details (Moved to Top Right) */}
|
||||
<div className={`${styles.card} ${styles.cardContact} glass-card`}>
|
||||
<h3>{t("Let's build something together")}</h3>
|
||||
<div className={styles.contactList}>
|
||||
<div className={styles.contactItem}>
|
||||
<Mail size={16} />
|
||||
<a href="mailto:rafaelgonzalezalbes@gmail.com">rafaelgonzalezalbes@gmail.com</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.socialIcons}>
|
||||
<a href="https://github.com/anik0z" target="_blank" rel="noopener noreferrer" aria-label="GitHub"><Github size={18} /></a>
|
||||
<a href="https://linkedin.com/rafaelgonzalezalbes" target="_blank" rel="noopener noreferrer" aria-label="LinkedIn"><Linkedin size={18} /></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Cell 3: Timeline Switcher (Bigger & Highlighted) */}
|
||||
<div className={`${styles.card} ${styles.cardTimeline} glass-card`}>
|
||||
<div className={styles.timelineHeader}>
|
||||
<h3>{t('My Timeline')}</h3>
|
||||
<div className={styles.tabSwitcher}>
|
||||
<button
|
||||
onClick={() => setExpTab('work')}
|
||||
className={`${styles.tabBtn} ${expTab === 'work' ? styles.tabBtnActive : ''}`}
|
||||
aria-label="Work Experience"
|
||||
>
|
||||
<Briefcase size={14} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setExpTab('education')}
|
||||
className={`${styles.tabBtn} ${expTab === 'education' ? styles.tabBtnActive : ''}`}
|
||||
aria-label="Education"
|
||||
>
|
||||
<GraduationCap size={14} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.timelineContent}>
|
||||
{(expTab === 'work' ? workItems : educationItems).map((item, idx) => (
|
||||
<div key={idx} className={styles.timelineItem}>
|
||||
<span className={styles.date}>{item.date}</span>
|
||||
<h5 className={styles.title}>{item.title}</h5>
|
||||
<p className={styles.timelineSubtitle}>{item.company} — {item.location}</p>
|
||||
<ul className={styles.bentoTimelineDetails}>
|
||||
{item.details.map((detail, dIdx) => (
|
||||
<li key={dIdx}>{detail}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Cell 4: Stats Grid */}
|
||||
<div className={`${styles.card} ${styles.cardStats}`}>
|
||||
<div className={styles.statsGrid}>
|
||||
{stats.map((stat, i) => (
|
||||
<div key={i} className={`${styles.statCard} glass-card`}>
|
||||
<span className={styles.statVal}>{stat.value}</span>
|
||||
<span className={styles.statLabel}>{stat.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Cell 5: Projects List */}
|
||||
<div className={`${styles.card} ${styles.cardProjects} glass-card`}>
|
||||
<div className={styles.cardHeader}>
|
||||
<h3>{t('Personal Projects')}</h3>
|
||||
</div>
|
||||
<div className={styles.bentoProjects}>
|
||||
{projects.map((proj, idx) => (
|
||||
<div key={idx} className={styles.bentoProjItem}>
|
||||
<div className={styles.projHeader}>
|
||||
<h5>{proj.title}</h5>
|
||||
<ExternalLink size={14} className={styles.projIcon} />
|
||||
</div>
|
||||
<p>{proj.shortDesc}</p>
|
||||
<div className={styles.tags}>
|
||||
{proj.tech.map((t) => (
|
||||
<span key={t} className={styles.tag}>{t}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Cell 6: Spoken Languages */}
|
||||
<div className={`${styles.card} ${styles.cardLanguages} glass-card`}>
|
||||
<div className={styles.cardHeader}>
|
||||
<Globe className={styles.icon} size={20} />
|
||||
<h3>{t('Spoken Languages')}</h3>
|
||||
</div>
|
||||
<div className={styles.langList}>
|
||||
{languagesList.map((lang, idx) => (
|
||||
<div key={idx} className={styles.langItem}>
|
||||
<span className={styles.langName}>{lang.name}</span>
|
||||
<span className={styles.langLevel}>{lang.level}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Cell 7: About Paragraphs */}
|
||||
<div className={`${styles.card} ${styles.cardAbout} glass-card`}>
|
||||
<div className={styles.cardHeader}>
|
||||
<Award className={styles.icon} size={20} />
|
||||
<h3>{t('About Me')}</h3>
|
||||
</div>
|
||||
{getAboutBio(t)}
|
||||
</div>
|
||||
|
||||
{/* Cell 8: Technical Expertise */}
|
||||
<div className={`${styles.card} ${styles.cardSkills} glass-card`}>
|
||||
<h3>{t('Technical Expertise')}</h3>
|
||||
<div className={styles.skillsWrapper}>
|
||||
<div>
|
||||
<h5>{t('Backend')}</h5>
|
||||
<p>PHP, Symfony, Python, Java, C++, SQL, Docker, Bash</p>
|
||||
</div>
|
||||
<div>
|
||||
<h5>{t('Frontend')}</h5>
|
||||
<p>TypeScript, React, CSS3, HTML5, Bootstrap, JQuery</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BentoTemplate;
|
||||
467
src/templates/BrutalistTemplate.module.css
Normal file
|
|
@ -0,0 +1,467 @@
|
|||
.brutalistContainer {
|
||||
background-color: #f1f3f5;
|
||||
color: #000000;
|
||||
min-height: 100vh;
|
||||
padding: 60px 24px;
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
|
||||
.header {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto 40px auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 3px solid #000000;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.6rem;
|
||||
font-weight: 900;
|
||||
text-shadow: 2px 2px 0px #facc15;
|
||||
}
|
||||
|
||||
.langs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.langBtn {
|
||||
background: #ffffff;
|
||||
border: 2px solid #000000;
|
||||
color: #000000;
|
||||
cursor: pointer;
|
||||
padding: 6px 12px;
|
||||
font-family: var(--font-heading);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 800;
|
||||
box-shadow: 2px 2px 0px #000000;
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
}
|
||||
|
||||
.langBtn:active, .langActive {
|
||||
transform: translate(2px, 2px);
|
||||
box-shadow: 0px 0px 0px #000000;
|
||||
background: #facc15;
|
||||
}
|
||||
|
||||
.main {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Brutalist Card */
|
||||
.card {
|
||||
background: #ffffff;
|
||||
border: 3px solid #000000;
|
||||
box-shadow: 5px 5px 0px #000000;
|
||||
padding: 30px;
|
||||
margin-bottom: 30px;
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 7px 7px 0px #000000;
|
||||
}
|
||||
|
||||
.heroCard {
|
||||
background: #ffde4d; /* Bright brutalist yellow */
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
background: #22c55e; /* Bright green */
|
||||
border: 2px solid #000000;
|
||||
color: #000000;
|
||||
padding: 4px 12px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 2px 2px 0px #000000;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 3rem;
|
||||
font-weight: 900;
|
||||
line-height: 1.1;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.role {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 800;
|
||||
text-decoration: underline;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.6;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Grid Layout */
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.aboutCard {
|
||||
grid-column: span 2;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.aboutCard h2, .stackCard h2, .projectsCard h2, .contactCard h2, .timelineCard h2 {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 900;
|
||||
margin-bottom: 20px;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 3px solid #000000;
|
||||
display: inline-block;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.bodyText {
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.7;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.stackCard {
|
||||
grid-column: span 1;
|
||||
background: #85fefe; /* Neon cyan */
|
||||
}
|
||||
|
||||
.skillList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.skillLabel {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 900;
|
||||
background: #ffffff;
|
||||
border: 1.5px solid #000000;
|
||||
padding: 2px 6px;
|
||||
display: inline-block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.skillItem p {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.span2 {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.projectsCard {
|
||||
grid-column: span 2;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.projectsList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
max-height: 380px;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #000000 transparent;
|
||||
}
|
||||
|
||||
.projectsList::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.projectsList::-webkit-scrollbar-thumb {
|
||||
background: #000000;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.projectItem {
|
||||
border-bottom: 2px solid #000000;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.projectItem:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.projHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.projHeader h4 {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.projLink {
|
||||
color: #000000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.projLink:hover {
|
||||
transform: translate(-2px, -2px) scale(1.1);
|
||||
}
|
||||
|
||||
.projectItem p {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.5;
|
||||
color: #333333;
|
||||
margin-bottom: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 0.7rem;
|
||||
background: #e9ecef;
|
||||
border: 1.5px solid #000000;
|
||||
border-radius: 4px;
|
||||
padding: 2px 6px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.contactCard {
|
||||
grid-column: span 1;
|
||||
background: #ffc0cb; /* Pastel pink */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.contactCard p {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.emailBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
background: #ffffff;
|
||||
border: 2px solid #000000;
|
||||
padding: 10px;
|
||||
box-shadow: 2px 2px 0px #000000;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.emailBox a {
|
||||
color: #000000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.socials {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.socialLink {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: #ffffff;
|
||||
border: 2px solid #000000;
|
||||
padding: 10px;
|
||||
color: #000000;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 800;
|
||||
box-shadow: 2px 2px 0px #000000;
|
||||
text-decoration: none;
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
}
|
||||
|
||||
.socialLink:hover {
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 4px 4px 0px #000000;
|
||||
background: #facc15;
|
||||
}
|
||||
|
||||
.timelineCard {
|
||||
grid-column: span 3;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.timelineHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 3px solid #000000;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.timelineHeader h2 {
|
||||
border-bottom: none !important;
|
||||
margin-bottom: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.tabContainer {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tabBtn {
|
||||
background: #ffffff;
|
||||
border: 2px solid #000000;
|
||||
color: #000000;
|
||||
cursor: pointer;
|
||||
padding: 6px 12px;
|
||||
font-family: var(--font-heading);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 800;
|
||||
box-shadow: 2px 2px 0px #000000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
}
|
||||
|
||||
.tabBtn:active, .tabActive {
|
||||
transform: translate(2px, 2px);
|
||||
box-shadow: 0px 0px 0px #000000;
|
||||
background: #ffc0cb; /* Pastel pink highlight */
|
||||
}
|
||||
|
||||
.timelineList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
max-height: 480px;
|
||||
overflow-y: auto;
|
||||
padding-right: 12px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #000000 transparent;
|
||||
}
|
||||
|
||||
.timelineList::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.timelineList::-webkit-scrollbar-thumb {
|
||||
background: #000000;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.timelineItem {
|
||||
border: 2px solid #000000;
|
||||
padding: 20px;
|
||||
background: #ffffff;
|
||||
box-shadow: 4px 4px 0px #000000;
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
}
|
||||
|
||||
.timelineItem:hover {
|
||||
transform: translate(-1px, -1px);
|
||||
box-shadow: 5px 5px 0px #000000;
|
||||
}
|
||||
|
||||
.timelineItemHeader {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.timelineDate {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 900;
|
||||
background: #facc15; /* Yellow highlight badge */
|
||||
border: 1.5px solid #000000;
|
||||
padding: 2px 8px;
|
||||
display: inline-block;
|
||||
margin-bottom: 6px;
|
||||
box-shadow: 1.5px 1.5px 0px #000000;
|
||||
}
|
||||
|
||||
.timelineTitle {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.15rem;
|
||||
font-weight: 900;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.timelineSubtitle {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
color: #555555;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.timelineDetails {
|
||||
list-style-type: square;
|
||||
margin-left: 20px;
|
||||
padding-left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.timelineDetails li {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
color: #111111;
|
||||
}
|
||||
|
||||
@media (max-width: 968px) {
|
||||
.aboutCard, .stackCard, .projectsCard, .contactCard, .span2, .timelineCard {
|
||||
grid-column: span 3;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.aboutCard, .stackCard, .projectsCard, .contactCard, .span2 {
|
||||
grid-column: span 1;
|
||||
}
|
||||
.title {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
.header {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
.timelineHeader {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.brutalistContainer {
|
||||
padding: 30px 16px;
|
||||
}
|
||||
.card {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
170
src/templates/BrutalistTemplate.tsx
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getAboutBio, getWorkItems, getEducationItems, getProjects } from '../data/portfolioData';
|
||||
import FlagImg from '../components/FlagImg';
|
||||
import { Mail, Github, Linkedin, ExternalLink, Briefcase, GraduationCap, Download } from 'lucide-react';
|
||||
import styles from './BrutalistTemplate.module.css';
|
||||
|
||||
export const BrutalistTemplate: React.FC = () => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const [activeTab, setActiveTab] = useState<'work' | 'education'>('work');
|
||||
|
||||
const currentLang = i18n.language ? i18n.language.substring(0, 2) : 'en';
|
||||
|
||||
const langs = [
|
||||
{ code: 'en', label: '🇬🇧' },
|
||||
{ code: 'es', label: '🇪🇸' },
|
||||
{ code: 'it', label: '🇮🇹' },
|
||||
{ code: 'de', label: '🇩🇪' }
|
||||
];
|
||||
|
||||
const workItems = getWorkItems(t);
|
||||
const educationItems = getEducationItems(t);
|
||||
const projects = getProjects(t);
|
||||
|
||||
return (
|
||||
<div className={styles.brutalistContainer}>
|
||||
<header className={styles.header}>
|
||||
<div className={styles.logo} onClick={() => window.dispatchEvent(new CustomEvent('goHome'))} style={{ cursor: 'pointer' }}>rga://brutal</div>
|
||||
<div className={styles.langs}>
|
||||
{langs.map((lang) => (
|
||||
<button
|
||||
key={lang.code}
|
||||
onClick={() => i18n.changeLanguage(lang.code)}
|
||||
className={`${styles.langBtn} ${currentLang === lang.code ? styles.langActive : ''}`}
|
||||
>
|
||||
<FlagImg code={lang.code} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className={styles.main}>
|
||||
{/* Hero Card */}
|
||||
<section className={`${styles.card} ${styles.heroCard}`}>
|
||||
<span className={styles.badge}>{t('SYSTEM_ACTIVE')}</span>
|
||||
<h1 className={styles.title}>RAFAEL GONZÁLEZ ALBES</h1>
|
||||
<p className={styles.role}>{t('Professional Software Developer')}</p>
|
||||
<p className={styles.desc}>
|
||||
{t('Professional Software Developer with 4.5+ years of experience building high-performance backend APIs, handling databases, and crafting clean frontend applications. Specialized in PHP (Symfony), TypeScript, and containerized Docker environments.')}
|
||||
</p>
|
||||
<div style={{ marginTop: '20px' }}>
|
||||
<a href="/Rafael_Gonzalez_Albes_CV.pdf" download style={{ display: 'inline-flex', alignItems: 'center', gap: '8px', textDecoration: 'none', padding: '12px 24px', background: '#000', color: '#fff', border: '2px solid #000', textTransform: 'uppercase', fontWeight: 'bold' }}>
|
||||
<Download size={16} />
|
||||
{t('Download CV')}
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Bento Grid */}
|
||||
<div className={styles.grid}>
|
||||
{/* About */}
|
||||
<div className={`${styles.card} ${styles.aboutCard}`}>
|
||||
<h2>{t('About Me')}</h2>
|
||||
{getAboutBio(t, styles.bodyText)}
|
||||
</div>
|
||||
|
||||
{/* Stack */}
|
||||
<div className={`${styles.card} ${styles.stackCard}`}>
|
||||
<h2>{t('Core Stack')}</h2>
|
||||
<div className={styles.skillList}>
|
||||
<div className={styles.skillItem}>
|
||||
<span className={styles.skillLabel}>BACKEND</span>
|
||||
<p>PHP, Symfony, Python, Java, SQL, Neo4j, Docker</p>
|
||||
</div>
|
||||
<div className={styles.skillItem}>
|
||||
<span className={styles.skillLabel}>FRONTEND</span>
|
||||
<p>TypeScript, React, HTML5, CSS3, JQuery, Bootstrap</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Timeline Card */}
|
||||
<div className={`${styles.card} ${styles.timelineCard}`}>
|
||||
<div className={styles.timelineHeader}>
|
||||
<h2>{t('My Timeline')}</h2>
|
||||
<div className={styles.tabContainer}>
|
||||
<button
|
||||
onClick={() => setActiveTab('work')}
|
||||
className={`${styles.tabBtn} ${activeTab === 'work' ? styles.tabActive : ''}`}
|
||||
>
|
||||
<Briefcase size={16} />
|
||||
<span>{t('Work')}</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('education')}
|
||||
className={`${styles.tabBtn} ${activeTab === 'education' ? styles.tabActive : ''}`}
|
||||
>
|
||||
<GraduationCap size={16} />
|
||||
<span>{t('Education')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.timelineList}>
|
||||
{(activeTab === 'work' ? workItems : educationItems).map((item, idx) => (
|
||||
<div key={idx} className={styles.timelineItem}>
|
||||
<div className={styles.timelineItemHeader}>
|
||||
<span className={styles.timelineDate}>{item.date}</span>
|
||||
<h4 className={styles.timelineTitle}>{item.title}</h4>
|
||||
<p className={styles.timelineSubtitle}>{item.company} — {item.location}</p>
|
||||
</div>
|
||||
<ul className={styles.timelineDetails}>
|
||||
{item.details.map((detail, dIdx) => (
|
||||
<li key={dIdx}>{detail}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact */}
|
||||
<div className={`${styles.card} ${styles.contactCard}`}>
|
||||
<h2>{t('Connect')}</h2>
|
||||
<p>{t("I'm open for collaboration and backend architectures development.")}</p>
|
||||
<div className={styles.emailBox}>
|
||||
<Mail size={16} />
|
||||
<a href="mailto:rafaelgonzalezalbes@gmail.com">rafaelgonzalezalbes@gmail.com</a>
|
||||
</div>
|
||||
<div className={styles.socials}>
|
||||
<a href="https://github.com/anik0z" target="_blank" rel="noopener noreferrer" className={styles.socialLink}>
|
||||
<Github size={20} />
|
||||
<span>GitHub</span>
|
||||
</a>
|
||||
<a href="https://linkedin.com/rafaelgonzalezalbes" target="_blank" rel="noopener noreferrer" className={styles.socialLink}>
|
||||
<Linkedin size={20} />
|
||||
<span>LinkedIn</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Projects */}
|
||||
<div className={`${styles.card} ${styles.projectsCard} ${styles.span3}`}>
|
||||
<h2>{t('Projects')}</h2>
|
||||
<div className={styles.projectsList}>
|
||||
{projects.map((proj, idx) => (
|
||||
<div key={idx} className={styles.projectItem}>
|
||||
<div className={styles.projHeader}>
|
||||
<h4>{proj.title}</h4>
|
||||
<a href="https://github.com/anik0z" target="_blank" rel="noopener noreferrer" className={styles.projLink}>
|
||||
<ExternalLink size={16} />
|
||||
</a>
|
||||
</div>
|
||||
<p>{proj.shortDesc}</p>
|
||||
<div className={styles.tags}>
|
||||
{proj.tech.map((t) => (
|
||||
<span key={t} className={styles.tag}>{t}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BrutalistTemplate;
|
||||
464
src/templates/CyberpunkTemplate.module.css
Normal file
|
|
@ -0,0 +1,464 @@
|
|||
.cyberContainer {
|
||||
background-color: #03020a;
|
||||
color: #c9d1d9;
|
||||
min-height: 100vh;
|
||||
padding: 50px 24px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
|
||||
/* Cyber grid background overlay */
|
||||
.gridOverlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
linear-gradient(rgba(255, 0, 127, 0.03) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255, 0, 127, 0.03) 1px, transparent 1px);
|
||||
background-size: 30px 30px;
|
||||
background-position: center;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto 40px auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.4rem;
|
||||
font-weight: 900;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.glitchText {
|
||||
color: #ffffff;
|
||||
position: relative;
|
||||
text-shadow: 0.05em 0 0 rgba(255, 0, 127, 0.75), -0.025em -0.05em 0 rgba(0, 243, 255, 0.75), 0.025em 0.05em 0 rgba(176, 38, 255, 0.75);
|
||||
}
|
||||
|
||||
.langs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.langBtn {
|
||||
background: transparent;
|
||||
border: 1px solid #30163d;
|
||||
border-radius: 4px;
|
||||
color: #8b949e;
|
||||
cursor: pointer;
|
||||
padding: 4px 10px;
|
||||
font-family: var(--font-heading);
|
||||
font-size: 0.75rem;
|
||||
font-weight: bold;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.langBtn:hover, .langActive {
|
||||
background: #ff007f;
|
||||
color: #ffffff;
|
||||
border-color: #ff007f;
|
||||
box-shadow: 0 0 10px rgba(255, 0, 127, 0.5);
|
||||
}
|
||||
|
||||
.main {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.hero {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.terminalIconRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.cyberIcon {
|
||||
color: #00f3ff;
|
||||
filter: drop-shadow(0 0 5px #00f3ff);
|
||||
}
|
||||
|
||||
.statusBadge {
|
||||
font-size: 0.7rem;
|
||||
background: rgba(0, 243, 255, 0.1);
|
||||
border: 1px solid #00f3ff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
color: #00f3ff;
|
||||
font-weight: bold;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 3rem;
|
||||
font-weight: 900;
|
||||
line-height: 1.1;
|
||||
color: #ffffff;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.neonText {
|
||||
color: #ff007f;
|
||||
text-shadow: 0 0 10px rgba(255, 0, 127, 0.6), 0 0 20px rgba(255, 0, 127, 0.3);
|
||||
}
|
||||
|
||||
.role {
|
||||
font-size: 1.2rem;
|
||||
color: #00f3ff;
|
||||
font-weight: bold;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.cyberCard {
|
||||
background: rgba(3, 2, 12, 0.7);
|
||||
border-left: 4px solid #00f3ff;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
padding: 20px;
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
max-width: 750px;
|
||||
}
|
||||
|
||||
/* Grid Layout */
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 20px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: rgba(3, 2, 12, 0.85);
|
||||
border: 1px solid #30163d;
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.pinkGlow:hover {
|
||||
border-color: #ff007f;
|
||||
box-shadow: 0 0 15px rgba(255, 0, 127, 0.15);
|
||||
}
|
||||
|
||||
.cyanGlow:hover {
|
||||
border-color: #00f3ff;
|
||||
box-shadow: 0 0 15px rgba(0, 243, 255, 0.15);
|
||||
}
|
||||
|
||||
.violetGlow:hover {
|
||||
border-color: #b026ff;
|
||||
box-shadow: 0 0 15px rgba(176, 38, 255, 0.15);
|
||||
}
|
||||
|
||||
.cardHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.cardIconPink { color: #ff007f; }
|
||||
.cardIconCyan { color: #00f3ff; }
|
||||
|
||||
.cardText {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.skillList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.skillItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.skillCat {
|
||||
font-size: 0.75rem;
|
||||
font-weight: bold;
|
||||
color: #00f3ff;
|
||||
}
|
||||
|
||||
.skillVal {
|
||||
font-size: 0.85rem;
|
||||
color: #c9d1d9;
|
||||
}
|
||||
|
||||
.span2 {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.span3 {
|
||||
grid-column: span 3;
|
||||
}
|
||||
|
||||
.timelineHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 1px solid #30163d;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.tabContainer {
|
||||
display: flex;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border: 1px solid #30163d;
|
||||
border-radius: 6px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.tabBtn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #8b949e;
|
||||
cursor: pointer;
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.tabBtn:hover {
|
||||
color: #b026ff;
|
||||
}
|
||||
|
||||
.tabActive {
|
||||
background: rgba(176, 38, 255, 0.15);
|
||||
color: #b026ff;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
max-height: 360px;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(176, 38, 255, 0.3) transparent;
|
||||
}
|
||||
|
||||
.timeline::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.timeline::-webkit-scrollbar-thumb {
|
||||
background: rgba(176, 38, 255, 0.3);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.timelineItem {
|
||||
border-left: 2px solid #b026ff;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.timeDate {
|
||||
font-size: 0.7rem;
|
||||
font-weight: bold;
|
||||
color: #b026ff;
|
||||
}
|
||||
|
||||
.timelineItem h5 {
|
||||
font-size: 0.9rem;
|
||||
margin: 2px 0;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.timelineItem p {
|
||||
font-size: 0.8rem;
|
||||
color: #8b949e;
|
||||
}
|
||||
|
||||
.cyberTimelineDetails {
|
||||
list-style-type: square;
|
||||
margin-left: 15px;
|
||||
margin-top: 8px;
|
||||
padding-left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.cyberTimelineDetails li {
|
||||
font-size: 0.75rem;
|
||||
color: #8b949e;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.cyberTimelineDetails li::marker {
|
||||
color: #00f3ff;
|
||||
}
|
||||
|
||||
.projectsList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.projectItem {
|
||||
border-bottom: 1px solid #161b22;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.projectItem:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.projHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.projHeader h5 {
|
||||
font-size: 0.95rem;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.projLink {
|
||||
color: #ff007f;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.projectItem p {
|
||||
font-size: 0.8rem;
|
||||
color: #8b949e;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 0.7rem;
|
||||
background: rgba(0, 243, 255, 0.05);
|
||||
border: 1px solid rgba(0, 243, 255, 0.1);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
color: #00f3ff;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 40px;
|
||||
border-top: 1px solid #30163d;
|
||||
padding-top: 24px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.footerContent {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.email {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.email a {
|
||||
color: #ffffff;
|
||||
transition: text-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.email a:hover {
|
||||
text-shadow: 0 0 5px #ff007f;
|
||||
}
|
||||
|
||||
.socials {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.socials a {
|
||||
color: #8b949e;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.socials a:hover {
|
||||
color: #ffffff;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
@media (max-width: 968px) {
|
||||
.span2, .span3 {
|
||||
grid-column: span 3;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.card, .span2, .span3 {
|
||||
grid-column: span 1;
|
||||
}
|
||||
.title {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
.cyberContainer {
|
||||
padding: 30px 16px;
|
||||
}
|
||||
.header {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.footerContent {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
.cyberCard {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
182
src/templates/CyberpunkTemplate.tsx
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getAboutBio, getWorkItems, getEducationItems, getProjects } from '../data/portfolioData';
|
||||
import FlagImg from '../components/FlagImg';
|
||||
import { Terminal, Shield, Zap, Mail, Github, Linkedin, ExternalLink, Briefcase, GraduationCap, Download } from 'lucide-react';
|
||||
import styles from './CyberpunkTemplate.module.css';
|
||||
|
||||
export const CyberpunkTemplate: React.FC = () => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const [timelineTab, setTimelineTab] = useState<'work' | 'education'>('work');
|
||||
|
||||
const currentLang = i18n.language ? i18n.language.substring(0, 2) : 'en';
|
||||
|
||||
const langs = [
|
||||
{ code: 'en', label: '🇬🇧' },
|
||||
{ code: 'es', label: '🇪🇸' },
|
||||
{ code: 'it', label: '🇮🇹' },
|
||||
{ code: 'de', label: '🇩🇪' }
|
||||
];
|
||||
|
||||
const workItems = getWorkItems(t);
|
||||
const educationItems = getEducationItems(t);
|
||||
const projects = getProjects(t);
|
||||
|
||||
return (
|
||||
<div className={styles.cyberContainer}>
|
||||
{/* Grid overlay */}
|
||||
<div className={styles.gridOverlay}></div>
|
||||
|
||||
<header className={styles.header}>
|
||||
<div className={styles.logo} onClick={() => window.dispatchEvent(new CustomEvent('goHome'))} style={{ cursor: 'pointer' }}>
|
||||
<span className={styles.glitchText} data-text="rga://cyber">rga://cyber</span>
|
||||
</div>
|
||||
<div className={styles.headerRight}>
|
||||
<div className={styles.langs}>
|
||||
{langs.map((lang) => (
|
||||
<button
|
||||
key={lang.code}
|
||||
onClick={() => i18n.changeLanguage(lang.code)}
|
||||
className={`${styles.langBtn} ${currentLang === lang.code ? styles.langActive : ''}`}
|
||||
>
|
||||
<FlagImg code={lang.code} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className={styles.main}>
|
||||
{/* Hero Section */}
|
||||
<section className={styles.hero}>
|
||||
<div className={styles.terminalIconRow}>
|
||||
<Terminal size={32} className={styles.cyberIcon} />
|
||||
<span className={styles.statusBadge}>{t('SYSTEM_ACTIVE')}</span>
|
||||
</div>
|
||||
<h1 className={styles.title}>
|
||||
RAFAEL <span className={styles.neonText}>{t('GONZÁLEZ ALBES')}</span>
|
||||
</h1>
|
||||
<p className={styles.role}>{t('Professional Software Developer')}</p>
|
||||
<div className={styles.cyberCard}>
|
||||
<p>
|
||||
{t('Professional Software Developer with 4.5+ years of experience building high-performance backend APIs, handling databases, and crafting clean frontend applications. Specialized in PHP (Symfony), TypeScript, and containerized Docker environments.')}
|
||||
</p>
|
||||
<div style={{ marginTop: '20px' }}>
|
||||
<a href="/Rafael_Gonzalez_Albes_CV.pdf" download style={{ display: 'inline-flex', alignItems: 'center', gap: '8px', padding: '10px 20px', background: 'transparent', border: '1px solid var(--neon-cyan)', color: 'var(--neon-cyan)', textDecoration: 'none', textTransform: 'uppercase', letterSpacing: '1px', fontWeight: 'bold', cursor: 'pointer' }}>
|
||||
<Download size={16} />
|
||||
{t('Download CV')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Info Grid */}
|
||||
<div className={styles.grid}>
|
||||
{/* Box 1: About */}
|
||||
<div className={`${styles.card} ${styles.pinkGlow}`}>
|
||||
<div className={styles.cardHeader}>
|
||||
<Shield size={18} className={styles.cardIconPink} />
|
||||
<h3>{t('Developer Profile')}</h3>
|
||||
</div>
|
||||
{getAboutBio(t, styles.cardText)}
|
||||
</div>
|
||||
|
||||
{/* Box 2: Skills */}
|
||||
<div className={`${styles.card} ${styles.cyanGlow} ${styles.span2}`}>
|
||||
<div className={styles.cardHeader}>
|
||||
<Zap size={18} className={styles.cardIconCyan} />
|
||||
<h3>{t('Core Stack')}</h3>
|
||||
</div>
|
||||
<div className={styles.skillList}>
|
||||
<div className={styles.skillItem}>
|
||||
<span className={styles.skillCat}>{t('Backend & Systems')}:</span>
|
||||
<span className={styles.skillVal}>PHP, Symfony, Python, Java, C++, SQL, Neo4j, Docker, Bash</span>
|
||||
</div>
|
||||
<div className={styles.skillItem}>
|
||||
<span className={styles.skillCat}>{t('Frontend & UI')}:</span>
|
||||
<span className={styles.skillVal}>TypeScript, React, CSS3, HTML5, Bootstrap, JQuery</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Box 3: Experience */}
|
||||
<div className={`${styles.card} ${styles.violetGlow} ${styles.span3}`}>
|
||||
<div className={styles.timelineHeader}>
|
||||
<h3>{t('Chronology')}</h3>
|
||||
<div className={styles.tabContainer}>
|
||||
<button
|
||||
onClick={() => setTimelineTab('work')}
|
||||
className={`${styles.tabBtn} ${timelineTab === 'work' ? styles.tabActive : ''}`}
|
||||
aria-label="Work Experience"
|
||||
>
|
||||
<Briefcase size={14} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setTimelineTab('education')}
|
||||
className={`${styles.tabBtn} ${timelineTab === 'education' ? styles.tabActive : ''}`}
|
||||
aria-label="Education"
|
||||
>
|
||||
<GraduationCap size={14} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.timeline}>
|
||||
{(timelineTab === 'work' ? workItems : educationItems).map((item, idx) => (
|
||||
<div key={idx} className={styles.timelineItem}>
|
||||
<span className={styles.timeDate}>{item.date}</span>
|
||||
<h5>{item.title}</h5>
|
||||
<p className={styles.timelineSubtitle}>{item.company} // {item.location}</p>
|
||||
<ul className={styles.cyberTimelineDetails}>
|
||||
{item.details.map((detail, dIdx) => (
|
||||
<li key={dIdx}>{detail}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Box 4: Projects */}
|
||||
<div className={`${styles.card} ${styles.pinkGlow} ${styles.span3}`}>
|
||||
<h3>{t('Project Modules')}</h3>
|
||||
<div className={styles.projectsList}>
|
||||
{projects.map((proj, idx) => (
|
||||
<div key={idx} className={styles.projectItem}>
|
||||
<div className={styles.projHeader}>
|
||||
<h5>{proj.title}</h5>
|
||||
<a href="https://github.com/anik0z" target="_blank" rel="noopener noreferrer" className={styles.projLink} aria-label="Project details">
|
||||
<ExternalLink size={14} />
|
||||
</a>
|
||||
</div>
|
||||
<p>{proj.shortDesc}</p>
|
||||
<div className={styles.tags}>
|
||||
{proj.tech.map((t) => (
|
||||
<span key={t} className={styles.tag}>{t}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact info */}
|
||||
<footer className={styles.footer}>
|
||||
<div className={styles.footerGlow}></div>
|
||||
<div className={styles.footerContent}>
|
||||
<div className={styles.email}>
|
||||
<Mail size={16} />
|
||||
<a href="mailto:rafaelgonzalezalbes@gmail.com">rafaelgonzalezalbes@gmail.com</a>
|
||||
</div>
|
||||
<div className={styles.socials}>
|
||||
<a href="https://github.com/anik0z" target="_blank" rel="noopener noreferrer" aria-label="GitHub"><Github size={18} /></a>
|
||||
<a href="https://linkedin.com/rafaelgonzalezalbes" target="_blank" rel="noopener noreferrer" aria-label="LinkedIn"><Linkedin size={18} /></a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CyberpunkTemplate;
|
||||
279
src/templates/EditorialTemplate.module.css
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
.editorialContainer {
|
||||
background-color: #faf8f5;
|
||||
color: #1a1a1a;
|
||||
min-height: 100vh;
|
||||
padding: 60px 40px;
|
||||
font-family: var(--font-body);
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.header {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto 50px auto;
|
||||
border-bottom: 2px solid #1a1a1a;
|
||||
padding-bottom: 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.brand {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.4rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.langs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.langBtn {
|
||||
background: transparent;
|
||||
border: 1px solid #d1cfc7;
|
||||
border-radius: 4px;
|
||||
color: #61605a;
|
||||
cursor: pointer;
|
||||
padding: 4px 8px;
|
||||
font-size: 0.75rem;
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.langBtn:hover, .langActive {
|
||||
background: #1a1a1a;
|
||||
color: #faf8f5;
|
||||
border-color: #1a1a1a;
|
||||
}
|
||||
|
||||
.main {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.introSection {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: Georgia, 'Times New Roman', Times, serif;
|
||||
font-size: 3rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.15;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1.5px;
|
||||
color: #61605a;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1px;
|
||||
background-color: #d1cfc7;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.leadParagraph {
|
||||
font-family: Georgia, 'Times New Roman', Times, serif;
|
||||
font-size: 1.3rem;
|
||||
line-height: 1.6;
|
||||
color: #2b2b2b;
|
||||
}
|
||||
|
||||
.contentGrid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 60px;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-family: Georgia, 'Times New Roman', Times, serif;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 400;
|
||||
border-bottom: 1px solid #1a1a1a;
|
||||
padding-bottom: 8px;
|
||||
margin-bottom: 20px;
|
||||
text-transform: uppercase;
|
||||
font-size: 1.1rem;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.bodyText {
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.7;
|
||||
color: #3b3a36;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.timelineItem {
|
||||
border-left: 2px solid #1a1a1a;
|
||||
padding-left: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.timelineDate {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
color: #61605a;
|
||||
}
|
||||
|
||||
.timelineTitle {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.timelineCompany {
|
||||
font-size: 0.85rem;
|
||||
font-style: italic;
|
||||
color: #61605a;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.timelineDesc {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
color: #3b3a36;
|
||||
}
|
||||
|
||||
.skillsGrid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.skillCategory {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
color: #3b3a36;
|
||||
}
|
||||
|
||||
.projectList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.projectCard {
|
||||
border-bottom: 1px solid #d1cfc7;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.projectCard:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.projHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.projTitle {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.projDesc {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
color: #3b3a36;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.projTags {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.projTag {
|
||||
font-size: 0.75rem;
|
||||
background: #f0ede6;
|
||||
border: 1px solid #d1cfc7;
|
||||
border-radius: 4px;
|
||||
padding: 2px 8px;
|
||||
color: #1a1a1a;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.footerDivider {
|
||||
height: 1px;
|
||||
background-color: #1a1a1a;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.footerContent {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.footerEmail {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.footerEmail a {
|
||||
color: #1a1a1a;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.socials {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.socials a {
|
||||
color: #61605a;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.socials a:hover {
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.contentGrid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 30px;
|
||||
}
|
||||
.title {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
.editorialContainer {
|
||||
padding: 30px 16px;
|
||||
}
|
||||
.header {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.footerContent {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
156
src/templates/EditorialTemplate.tsx
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getAboutBio, getWorkItems, getEducationItems, getProjects } from '../data/portfolioData';
|
||||
import FlagImg from '../components/FlagImg';
|
||||
import { Mail, Github, Linkedin, ExternalLink, Download } from 'lucide-react';
|
||||
import styles from './EditorialTemplate.module.css';
|
||||
|
||||
export const EditorialTemplate: React.FC = () => {
|
||||
const { t, i18n } = useTranslation();
|
||||
|
||||
const currentLang = i18n.language ? i18n.language.substring(0, 2) : 'en';
|
||||
|
||||
const langs = [
|
||||
{ code: 'en', label: '🇬🇧' },
|
||||
{ code: 'es', label: '🇪🇸' },
|
||||
{ code: 'it', label: '🇮🇹' },
|
||||
{ code: 'de', label: '🇩🇪' }
|
||||
];
|
||||
|
||||
const workItems = getWorkItems(t);
|
||||
const educationItems = getEducationItems(t);
|
||||
const projects = getProjects(t);
|
||||
|
||||
return (
|
||||
<div className={styles.editorialContainer}>
|
||||
<header className={styles.header}>
|
||||
<div className={styles.brand}>RAFAEL GONZÁLEZ ALBES</div>
|
||||
<div className={styles.navRight}>
|
||||
<div className={styles.langs}>
|
||||
{langs.map((lang) => (
|
||||
<button
|
||||
key={lang.code}
|
||||
onClick={() => i18n.changeLanguage(lang.code)}
|
||||
className={`${styles.langBtn} ${currentLang === lang.code ? styles.langActive : ''}`}
|
||||
>
|
||||
<FlagImg code={lang.code} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className={styles.main}>
|
||||
{/* Intro */}
|
||||
<section className={styles.introSection}>
|
||||
<h1 className={styles.title}>{t('Professional Software Developer')}</h1>
|
||||
<p className={styles.subtitle}>{t('Augsburg, Germany — Sevilla, Spain')}</p>
|
||||
<div className={styles.divider}></div>
|
||||
<p className={styles.leadParagraph}>
|
||||
{t('Professional Software Developer with 4.5+ years of experience building high-performance backend APIs, handling databases, and crafting clean frontend applications. Specialized in PHP (Symfony), TypeScript, and containerized Docker environments.')}
|
||||
</p>
|
||||
<div style={{ marginTop: '20px', textAlign: 'center' }}>
|
||||
<a href="/Rafael_Gonzalez_Albes_CV.pdf" download style={{ display: 'inline-flex', alignItems: 'center', gap: '8px', padding: '10px 20px', backgroundColor: 'var(--text)', color: 'var(--bg)', textDecoration: 'none', fontFamily: 'var(--font-heading)', textTransform: 'uppercase', letterSpacing: '1px', fontSize: '0.9rem', border: '1px solid var(--text)', cursor: 'pointer' }}>
|
||||
<Download size={16} />
|
||||
{t('Download CV')}
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Two column grid */}
|
||||
<div className={styles.contentGrid}>
|
||||
{/* Left Column: About, Experience & Education */}
|
||||
<div className={styles.leftCol}>
|
||||
<article className={styles.aboutArticle}>
|
||||
<h2 className={styles.sectionHeader}>{t('The Engineer')}</h2>
|
||||
{getAboutBio(t, styles.bodyText)}
|
||||
</article>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h2 className={styles.sectionTitle}>{t('Work Experience')}</h2>
|
||||
<div className={styles.timelineContainer}>
|
||||
{workItems.map((item, idx) => (
|
||||
<div key={idx} className={styles.timelineItem}>
|
||||
<span className={styles.timelineDate}>{item.date}</span>
|
||||
<h4 className={styles.timelineTitle}>{item.title}</h4>
|
||||
<p className={styles.timelineCompany}>{item.company} — {item.location}</p>
|
||||
<p className={styles.timelineDesc}>{item.details[0]}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h2 className={styles.sectionTitle}>{t('Education')}</h2>
|
||||
<div className={styles.timelineContainer}>
|
||||
{educationItems.map((item, idx) => (
|
||||
<div key={idx} className={styles.timelineItem}>
|
||||
<span className={styles.timelineDate}>{item.date}</span>
|
||||
<h4 className={styles.timelineTitle}>{item.title}</h4>
|
||||
<p className={styles.timelineCompany}>{item.company} — {item.location}</p>
|
||||
<p className={styles.timelineDesc}>{item.details[0]}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{/* Right Column: Skills & Projects */}
|
||||
<div className={styles.rightCol}>
|
||||
<section className={styles.section}>
|
||||
<h2 className={styles.sectionTitle}>{t('Technical Skills')}</h2>
|
||||
<div className={styles.skillsGrid}>
|
||||
<div className={styles.skillCategory}>
|
||||
<strong>{t('Backend & Systems')}:</strong> PHP, Symfony, Python, Java, C++, SQL (MySQL, PostgreSQL), Docker, Bash
|
||||
</div>
|
||||
<div className={styles.skillCategory}>
|
||||
<strong>{t('Frontend & UI')}:</strong> TypeScript, JavaScript, HTML5, CSS3, JQuery, Bootstrap, React
|
||||
</div>
|
||||
<div className={styles.skillCategory}>
|
||||
<strong>{t('Databases & IoT')}:</strong> Neo4j (Graph DB), Arduino, ESP8266
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h2 className={styles.sectionTitle}>{t('Selected Projects')}</h2>
|
||||
<div className={styles.projectList}>
|
||||
{projects.map((proj, idx) => (
|
||||
<div key={idx} className={styles.projectCard}>
|
||||
<div className={styles.projHeader}>
|
||||
<h4 className={styles.projTitle}>{proj.title}</h4>
|
||||
<ExternalLink size={14} />
|
||||
</div>
|
||||
<p className={styles.projectDesc}>{proj.shortDesc}</p>
|
||||
<div className={styles.projTags}>
|
||||
{proj.tech.map((t) => (
|
||||
<span key={t} className={styles.projTag}>{t}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact Footer */}
|
||||
<footer className={styles.footer}>
|
||||
<div className={styles.footerDivider}></div>
|
||||
<div className={styles.footerContent}>
|
||||
<div className={styles.footerEmail}>
|
||||
<Mail size={16} />
|
||||
<a href="mailto:rafaelgonzalezalbes@gmail.com">rafaelgonzalezalbes@gmail.com</a>
|
||||
</div>
|
||||
<div className={styles.socials}>
|
||||
<a href="https://github.com/anik0z" target="_blank" rel="noopener noreferrer" aria-label="GitHub"><Github size={16} /></a>
|
||||
<a href="https://linkedin.com/rafaelgonzalezalbes" target="_blank" rel="noopener noreferrer" aria-label="LinkedIn"><Linkedin size={16} /></a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditorialTemplate;
|
||||
62
src/templates/GithubTemplate.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import Navbar from '../components/Navbar';
|
||||
import Hero from '../components/Hero';
|
||||
import About from '../components/About';
|
||||
import Skills from '../components/Skills';
|
||||
import Experience from '../components/Experience';
|
||||
import Projects from '../components/Projects';
|
||||
import Contact from '../components/Contact';
|
||||
import Footer from '../components/Footer';
|
||||
|
||||
export const GithubTemplate: React.FC = () => {
|
||||
const [activeSection, setActiveSection] = useState('hero');
|
||||
|
||||
useEffect(() => {
|
||||
const sections = ['hero', 'about', 'skills', 'experience', 'projects', 'contact'];
|
||||
|
||||
const observerOptions = {
|
||||
root: null,
|
||||
rootMargin: '-30% 0px -60% 0px',
|
||||
threshold: 0
|
||||
};
|
||||
|
||||
const observerCallback = (entries: IntersectionObserverEntry[]) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
setActiveSection(entry.target.id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver(observerCallback, observerOptions);
|
||||
|
||||
sections.forEach((id) => {
|
||||
const element = document.getElementById(id);
|
||||
if (element) observer.observe(element);
|
||||
});
|
||||
|
||||
return () => {
|
||||
sections.forEach((id) => {
|
||||
const element = document.getElementById(id);
|
||||
if (element) observer.unobserve(element);
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar activeSection={activeSection} />
|
||||
<main>
|
||||
<Hero />
|
||||
<About />
|
||||
<Skills />
|
||||
<Experience />
|
||||
<Projects />
|
||||
<Contact />
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default GithubTemplate;
|
||||
349
src/templates/NeumorphicTemplate.module.css
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
.neuContainer {
|
||||
background-color: #e0e0e0;
|
||||
color: #334155;
|
||||
min-height: 100vh;
|
||||
padding: 60px 24px;
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
|
||||
.header {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto 40px auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.4rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.logoColor {
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.langs {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.langBtn {
|
||||
background: #e0e0e0;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
color: #475569;
|
||||
cursor: pointer;
|
||||
padding: 8px 16px;
|
||||
font-family: var(--font-heading);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
box-shadow: 4px 4px 8px #bebebe, -4px -4px 8px #ffffff;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.langBtn:hover {
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.langActive {
|
||||
box-shadow: inset 4px 4px 8px #bebebe, inset -4px -4px 8px #ffffff;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.main {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Neumorphic Card */
|
||||
.card {
|
||||
background: #e0e0e0;
|
||||
border-radius: 20px;
|
||||
box-shadow: 8px 8px 16px #bebebe, -8px -8px 16px #ffffff;
|
||||
padding: 30px;
|
||||
margin-bottom: 30px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 2.5rem;
|
||||
font-weight: 800;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.role {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: #475569;
|
||||
margin: 6px 0 20px 0;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
/* Grid Layout */
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.about h3, .timelineCard h3, .projectsCard h3, .contact h3 {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.2rem;
|
||||
font-weight: 800;
|
||||
color: #0f172a;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.bodyText {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
color: #475569;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.timelineCard {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.timelineHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tabContainer {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tabBtn {
|
||||
background: #e0e0e0;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: #475569;
|
||||
cursor: pointer;
|
||||
padding: 6px 12px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
box-shadow: 2px 2px 4px #bebebe, -2px -2px 4px #ffffff;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.tabActive {
|
||||
box-shadow: inset 2px 2px 4px #bebebe, inset -2px -2px 4px #ffffff;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.timelineList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
max-height: 380px;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 0, 0, 0.1) transparent;
|
||||
}
|
||||
|
||||
.timelineList::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.timelineList::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.timelineItem {
|
||||
border-left: 2px solid #94a3b8;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.date {
|
||||
font-size: 0.7rem;
|
||||
font-weight: bold;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.timelineItem h5 {
|
||||
font-size: 0.85rem;
|
||||
font-weight: bold;
|
||||
color: #0f172a;
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
.timelineItem p {
|
||||
font-size: 0.8rem;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.neuTimelineDetails {
|
||||
list-style-type: disc;
|
||||
margin-left: 16px;
|
||||
margin-top: 6px;
|
||||
padding-left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.neuTimelineDetails li {
|
||||
font-size: 0.75rem;
|
||||
color: #475569;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.span2 {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.projectsList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.projectItem {
|
||||
border-bottom: 1px solid #d1d5db;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.projectItem:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.projHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.projHeader h4 {
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.projIcon {
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.projectItem p {
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.5;
|
||||
color: #475569;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 0.7rem;
|
||||
background: #e0e0e0;
|
||||
border-radius: 4px;
|
||||
padding: 2px 8px;
|
||||
font-weight: 700;
|
||||
box-shadow: 1px 1px 2px #bebebe, -1px -1px 2px #ffffff;
|
||||
}
|
||||
|
||||
.contact {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.contactList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.contactItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 0.8rem;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.contactItem a {
|
||||
color: #334155;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.socials {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.socials a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: #e0e0e0;
|
||||
color: #475569;
|
||||
box-shadow: 4px 4px 8px #bebebe, -4px -4px 8px #ffffff;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.socials a:hover {
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.socials a:active {
|
||||
box-shadow: inset 2px 2px 4px #bebebe, inset -2px -2px 4px #ffffff;
|
||||
}
|
||||
|
||||
@media (max-width: 968px) {
|
||||
.about, .timelineCard, .projectsCard, .contact, .span2 {
|
||||
grid-column: span 3;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.about, .timelineCard, .projectsCard, .contact, .span2 {
|
||||
grid-column: span 1;
|
||||
}
|
||||
.hero h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
.header {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.timelineHeader {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.neuContainer {
|
||||
padding: 30px 16px;
|
||||
}
|
||||
.card {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
146
src/templates/NeumorphicTemplate.tsx
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getAboutBio, getWorkItems, getEducationItems, getProjects } from '../data/portfolioData';
|
||||
import FlagImg from '../components/FlagImg';
|
||||
import { Mail, Github, Linkedin, ExternalLink, Download } from 'lucide-react';
|
||||
import styles from './NeumorphicTemplate.module.css';
|
||||
|
||||
export const NeumorphicTemplate: React.FC = () => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const [activeTab, setActiveTab] = useState<'work' | 'education'>('work');
|
||||
|
||||
const currentLang = i18n.language ? i18n.language.substring(0, 2) : 'en';
|
||||
|
||||
const langs = [
|
||||
{ code: 'en', label: '🇬🇧' },
|
||||
{ code: 'es', label: '🇪🇸' },
|
||||
{ code: 'it', label: '🇮🇹' },
|
||||
{ code: 'de', label: '🇩🇪' }
|
||||
];
|
||||
|
||||
const workItems = getWorkItems(t);
|
||||
const educationItems = getEducationItems(t);
|
||||
const projects = getProjects(t);
|
||||
|
||||
return (
|
||||
<div className={styles.neuContainer}>
|
||||
<header className={styles.header}>
|
||||
<div className={styles.logo} onClick={() => window.dispatchEvent(new CustomEvent('goHome'))} style={{ cursor: 'pointer' }}>
|
||||
<span>rga://<span className={styles.logoColor}>soft</span></span>
|
||||
</div>
|
||||
<div className={styles.langs}>
|
||||
{langs.map((lang) => (
|
||||
<button
|
||||
key={lang.code}
|
||||
onClick={() => i18n.changeLanguage(lang.code)}
|
||||
className={`${styles.langBtn} ${currentLang === lang.code ? styles.langActive : ''}`}
|
||||
>
|
||||
<FlagImg code={lang.code} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className={styles.main}>
|
||||
{/* Intro */}
|
||||
<section className={`${styles.card} ${styles.hero}`}>
|
||||
<h1>Rafael González Albes</h1>
|
||||
<p className={styles.role}>{t('Professional Software Developer')}</p>
|
||||
<p className={styles.desc}>
|
||||
{t('Professional Software Developer with 4.5+ years of experience building high-performance backend APIs, handling databases, and crafting clean frontend applications. Specialized in PHP (Symfony), TypeScript, and containerized Docker environments.')}
|
||||
</p>
|
||||
<div style={{ marginTop: '20px' }}>
|
||||
<a href="/Rafael_Gonzalez_Albes_CV.pdf" download className={styles.tabBtn} style={{ display: 'inline-flex', alignItems: 'center', gap: '8px', textDecoration: 'none', padding: '10px 20px', borderRadius: '12px' }}>
|
||||
<Download size={16} />
|
||||
{t('Download CV')}
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Grid */}
|
||||
<div className={styles.grid}>
|
||||
{/* About */}
|
||||
<div className={`${styles.card} ${styles.about}`}>
|
||||
<h3>{t('About Me')}</h3>
|
||||
{getAboutBio(t, styles.bodyText)}
|
||||
</div>
|
||||
|
||||
{/* Timeline Switcher */}
|
||||
<div className={`${styles.card} ${styles.timelineCard}`}>
|
||||
<div className={styles.timelineHeader}>
|
||||
<h3>{t('Timeline')}</h3>
|
||||
<div className={styles.tabContainer}>
|
||||
<button
|
||||
onClick={() => setActiveTab('work')}
|
||||
className={`${styles.tabBtn} ${activeTab === 'work' ? styles.tabActive : ''}`}
|
||||
>
|
||||
{t('Work')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('education')}
|
||||
className={`${styles.tabBtn} ${activeTab === 'education' ? styles.tabActive : ''}`}
|
||||
>
|
||||
{t('Education')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.timelineList}>
|
||||
{(activeTab === 'work' ? workItems : educationItems).map((item, idx) => (
|
||||
<div key={idx} className={styles.timelineItem}>
|
||||
<span className={styles.date}>{item.date}</span>
|
||||
<h5>{item.title}</h5>
|
||||
<p className={styles.timelineSubtitle}>{item.company} | {item.location}</p>
|
||||
<ul className={styles.neuTimelineDetails}>
|
||||
{item.details.map((detail, dIdx) => (
|
||||
<li key={dIdx}>{detail}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Selected Projects */}
|
||||
<div className={`${styles.card} ${styles.projectsCard} ${styles.span2}`}>
|
||||
<h3>{t('Selected Projects')}</h3>
|
||||
<div className={styles.projectsList}>
|
||||
{projects.map((proj, idx) => (
|
||||
<div key={idx} className={styles.projectItem}>
|
||||
<div className={styles.projHeader}>
|
||||
<h4>{proj.title}</h4>
|
||||
<a href="https://github.com/anik0z" target="_blank" rel="noopener noreferrer" className={styles.projIcon}>
|
||||
<ExternalLink size={14} />
|
||||
</a>
|
||||
</div>
|
||||
<p>{proj.shortDesc}</p>
|
||||
<div className={styles.tags}>
|
||||
{proj.tech.map((t) => (
|
||||
<span key={t} className={styles.tag}>{t}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact Details */}
|
||||
<div className={`${styles.card} ${styles.contact}`}>
|
||||
<h3>{t('Get In Touch')}</h3>
|
||||
<div className={styles.contactList}>
|
||||
<div className={styles.contactItem}>
|
||||
<Mail size={16} />
|
||||
<a href="mailto:rafaelgonzalezalbes@gmail.com">rafaelgonzalezalbes@gmail.com</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.socials}>
|
||||
<a href="https://github.com/anik0z" target="_blank" rel="noopener noreferrer" aria-label="GitHub"><Github size={18} /></a>
|
||||
<a href="https://linkedin.com/rafaelgonzalezalbes" target="_blank" rel="noopener noreferrer" aria-label="LinkedIn"><Linkedin size={18} /></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NeumorphicTemplate;
|
||||
311
src/templates/TerminalTemplate.module.css
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
.terminalContainer {
|
||||
background-color: #050507;
|
||||
color: #39d353;
|
||||
font-family: 'Fira Code', 'Courier New', Courier, monospace;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* CRT Scanline effect */
|
||||
.terminalContainer::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
|
||||
background-size: 100% 3px, 6px 100%;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.terminalHeader {
|
||||
background: #161b22;
|
||||
border-bottom: 1px solid #30363d;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.headerLeft {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: #8b949e;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.langSelector {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.langBtn {
|
||||
background: transparent;
|
||||
border: 1px solid #30363d;
|
||||
border-radius: 4px;
|
||||
color: #8b949e;
|
||||
cursor: pointer;
|
||||
padding: 4px 10px;
|
||||
font-family: inherit;
|
||||
font-size: 0.75rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.langBtn:hover {
|
||||
border-color: #39d353;
|
||||
color: #39d353;
|
||||
}
|
||||
|
||||
.langBtnActive {
|
||||
background: rgba(57, 211, 83, 0.1);
|
||||
border-color: #39d353;
|
||||
color: #39d353;
|
||||
}
|
||||
|
||||
.mainLayout {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.terminalScreen {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.consoleHistory {
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding-right: 10px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #21262d transparent;
|
||||
}
|
||||
|
||||
.consoleHistory::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.consoleHistory::-webkit-scrollbar-thumb {
|
||||
background: #21262d;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.historyRow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.inputPromptRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.prompt {
|
||||
color: #58a6ff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.loggedCommand {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.commandOutput {
|
||||
color: #c9d1d9;
|
||||
line-height: 1.5;
|
||||
padding-left: 10px;
|
||||
border-left: 2px solid #21262d;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
color: #39d353;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.ascii {
|
||||
white-space: pre;
|
||||
font-size: 0.7rem;
|
||||
line-height: 1.1;
|
||||
color: #39d353;
|
||||
text-shadow: 0 0 5px rgba(57, 211, 83, 0.5);
|
||||
margin-bottom: 15px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.welcomeText {
|
||||
font-weight: bold;
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.instructions {
|
||||
color: #8b949e;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.inputPrompt {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: #0d1117;
|
||||
border: 1px solid #30363d;
|
||||
border-radius: 6px;
|
||||
padding: 8px 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.consoleInput {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #ffffff;
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
flex-grow: 1;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.sendBtn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #8b949e;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.sendBtn:hover {
|
||||
color: #39d353;
|
||||
}
|
||||
|
||||
.terminalSidebar {
|
||||
width: 280px;
|
||||
background: #0d1117;
|
||||
border-left: 1px solid #30363d;
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
flex-shrink: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.terminalSidebar h4 {
|
||||
font-size: 1rem;
|
||||
color: #ffffff;
|
||||
border-bottom: 1px solid #30363d;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.commandBtns {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.quickBtn {
|
||||
background: #161b22;
|
||||
border: 1px solid #30363d;
|
||||
border-radius: 6px;
|
||||
color: #39d353;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
font-size: 0.9rem;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.quickBtn:hover {
|
||||
background: rgba(57, 211, 83, 0.1);
|
||||
border-color: #39d353;
|
||||
transform: translateX(3px);
|
||||
}
|
||||
|
||||
.sidebarNote {
|
||||
font-size: 0.75rem;
|
||||
color: #8b949e;
|
||||
line-height: 1.4;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.helpList p {
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
.outputBlock h3 {
|
||||
color: #39d353;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px dashed #30363d;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.outputBlock p {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.projectList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.project h4 {
|
||||
color: #58a6ff;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.project p {
|
||||
font-size: 0.85rem;
|
||||
color: #8b949e;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #f85149;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.terminalSidebar {
|
||||
display: none;
|
||||
}
|
||||
.ascii {
|
||||
font-size: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.terminalHeader {
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
padding: 10px;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.headerLeft {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.terminalScreen {
|
||||
padding: 12px;
|
||||
}
|
||||
.inputPrompt {
|
||||
padding: 6px 12px;
|
||||
}
|
||||
}
|
||||
425
src/templates/TerminalTemplate.tsx
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getAboutBio, getWorkItems, getEducationItems, getProjects } from '../data/portfolioData';
|
||||
import FlagImg from '../components/FlagImg';
|
||||
import { Terminal, Send, Download } from 'lucide-react';
|
||||
import styles from './TerminalTemplate.module.css';
|
||||
|
||||
const DestructSequence: React.FC = () => {
|
||||
const [count, setCount] = useState(5);
|
||||
const [finished, setFinished] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (count > 0) {
|
||||
const timer = setTimeout(() => setCount(count - 1), 1000);
|
||||
return () => clearTimeout(timer);
|
||||
} else {
|
||||
setFinished(true);
|
||||
}
|
||||
}, [count]);
|
||||
|
||||
if (finished) {
|
||||
return <p style={{ color: '#ff5555', fontWeight: 'bold' }}>💥 BOOM! Just kidding. I would never destroy my portfolio. 😉</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ color: '#ff5555' }}>
|
||||
<p>⚠️ WARNING: Self-destruct sequence initiated!</p>
|
||||
<p>Time remaining: {count} seconds...</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const MatrixRain: React.FC = () => {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
canvas.width = canvas.parentElement?.clientWidth || window.innerWidth;
|
||||
canvas.height = 300;
|
||||
|
||||
const columns = Math.floor(canvas.width / 20);
|
||||
const yPositions = Array(columns).fill(0);
|
||||
|
||||
const matrixChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ<>[]{}@#$%&*+-/=".split("");
|
||||
|
||||
const draw = () => {
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
ctx.fillStyle = '#0f0';
|
||||
ctx.font = '15px monospace';
|
||||
|
||||
for (let i = 0; i < yPositions.length; i++) {
|
||||
const char = matrixChars[Math.floor(Math.random() * matrixChars.length)];
|
||||
const x = i * 20;
|
||||
const y = yPositions[i];
|
||||
|
||||
ctx.fillText(char, x, y);
|
||||
|
||||
if (y > canvas.height && Math.random() > 0.975) {
|
||||
yPositions[i] = 0;
|
||||
} else {
|
||||
yPositions[i] += 20;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const interval = setInterval(draw, 33);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div style={{ position: 'relative', width: '100%', overflow: 'hidden', borderRadius: '4px', border: '1px solid #333' }}>
|
||||
<canvas ref={canvasRef} style={{ display: 'block', background: '#000' }} />
|
||||
<div style={{ position: 'absolute', top: '10px', left: '10px', background: 'rgba(0,0,0,0.7)', padding: '4px 8px', borderRadius: '4px', fontSize: '12px', color: '#0f0', border: '1px solid #0f0' }}>
|
||||
MATRIX MODE ACTIVE
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface HistoryItem {
|
||||
command: string;
|
||||
output: React.ReactNode;
|
||||
}
|
||||
|
||||
export const TerminalTemplate: React.FC = () => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const [input, setInput] = useState('');
|
||||
const [history, setHistory] = useState<HistoryItem[]>([]);
|
||||
const consoleEndRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const workItems = getWorkItems(t);
|
||||
const educationItems = getEducationItems(t);
|
||||
const projects = getProjects(t);
|
||||
|
||||
const currentLang = i18n.language ? i18n.language.substring(0, 2) : 'en';
|
||||
|
||||
const langs = [
|
||||
{ code: 'en', label: '🇬🇧' },
|
||||
{ code: 'es', label: '🇪🇸' },
|
||||
{ code: 'it', label: '🇮🇹' },
|
||||
{ code: 'de', label: '🇩🇪' }
|
||||
];
|
||||
|
||||
const welcomeMessage = (
|
||||
<div className={styles.welcome}>
|
||||
<p className={styles.ascii}>
|
||||
{` _____ __ _
|
||||
| __ \\ / _| | |
|
||||
| |__) |__ _ | |_ __ _ ___ | |
|
||||
| _ // _\` || _|/ _\` | / _ \\ | |
|
||||
| | \\ \\ (_| || | | (_| || __/ |_|
|
||||
|_| \\_\\__,_||_| \\__,_| \\___| (_)`}
|
||||
</p>
|
||||
<p className={styles.welcomeText}>
|
||||
{t('Welcome to Rafael Gonzalez Albes CLI Portfolio v1.0.0')}
|
||||
</p>
|
||||
<p className={styles.instructions}>
|
||||
{t("Type 'help' to view all commands, or click the links on the right panel to execute them instantly.")}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Auto scroll to bottom
|
||||
useEffect(() => {
|
||||
consoleEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
}, [history]);
|
||||
|
||||
// Focus input on load and click
|
||||
useEffect(() => {
|
||||
inputRef.current?.focus();
|
||||
setHistory([{ command: 'system_init', output: welcomeMessage }]);
|
||||
}, [i18n.language]);
|
||||
|
||||
const handleContainerClick = () => {
|
||||
inputRef.current?.focus();
|
||||
};
|
||||
|
||||
const executeCommand = (cmd: string) => {
|
||||
const trimmedCmd = cmd.trim().toLowerCase();
|
||||
let output: React.ReactNode = null;
|
||||
|
||||
if (!trimmedCmd) return;
|
||||
|
||||
switch (trimmedCmd) {
|
||||
case 'help':
|
||||
output = (
|
||||
<div className={styles.helpList}>
|
||||
<p><strong>about</strong> - {t('About Me & Developer Journey')}</p>
|
||||
<p><strong>experience</strong> - {t('Work Experience Timeline')}</p>
|
||||
<p><strong>education</strong> - {t('Education Timeline')}</p>
|
||||
<p><strong>skills</strong> - {t('Technical Skills & Expertise')}</p>
|
||||
<p><strong>projects</strong> - {t('Personal Projects List')}</p>
|
||||
<p><strong>contact</strong> - {t('Contact Info & Socials')}</p>
|
||||
<p><strong>clear</strong> - {t('Clear the terminal screen')}</p>
|
||||
<p><strong>language [en|es|it|de]</strong> - {t('Change language')}</p>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'about':
|
||||
output = (
|
||||
<div className={styles.outputBlock}>
|
||||
<h3>{t('About Me')}</h3>
|
||||
{getAboutBio(t)}
|
||||
<p>{t("I enjoy solving complex architectural challenges, designing robust APIs, containerizing deployment environments, and exploring deep learning models.")}</p>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'experience':
|
||||
output = (
|
||||
<div className={styles.outputBlock}>
|
||||
<h3>{t('Work Experience')}</h3>
|
||||
<div className={styles.projectList}>
|
||||
{workItems.map((item, idx) => (
|
||||
<div key={idx} className={styles.project}>
|
||||
<h4>💼 {item.title}</h4>
|
||||
<p>{item.company} | {item.date}</p>
|
||||
<p>{item.details[0]}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'education':
|
||||
output = (
|
||||
<div className={styles.outputBlock}>
|
||||
<h3>{t('Education Timeline')}</h3>
|
||||
<div className={styles.projectList}>
|
||||
{educationItems.map((item, idx) => (
|
||||
<div key={idx} className={styles.project}>
|
||||
<h4>🎓 {item.title}</h4>
|
||||
<p>{item.company} | {item.date}</p>
|
||||
<p>{item.details[0]}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'skills':
|
||||
output = (
|
||||
<div className={styles.outputBlock}>
|
||||
<h3>{t('Technical Expertise')}</h3>
|
||||
<p><strong>{t('Backend & Systems')}:</strong> PHP & Symfony, Python, Java, C++, Docker, Bash, SQL</p>
|
||||
<p><strong>{t('Frontend & UI')}:</strong> TypeScript, JavaScript, HTML5, CSS3, JQuery, Bootstrap, React</p>
|
||||
<p><strong>{t('Databases & Technologies')}:</strong> SQL (MySQL, PostgreSQL), Neo4j (Graph DB), IoT (Arduino, ESP8266)</p>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'projects':
|
||||
output = (
|
||||
<div className={styles.outputBlock}>
|
||||
<h3>{t('Personal Projects')}</h3>
|
||||
<div className={styles.projectList}>
|
||||
{projects.map((proj, idx) => (
|
||||
<div key={idx} className={styles.project}>
|
||||
<h4>🚀 {proj.title}</h4>
|
||||
<p>{proj.shortDesc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'contact':
|
||||
output = (
|
||||
<div className={styles.outputBlock}>
|
||||
<h3>{t('Get In Touch')}</h3>
|
||||
<p>📧 Email: <a href="mailto:rafaelgonzalezalbes@gmail.com">rafaelgonzalezalbes@gmail.com</a></p>
|
||||
<p>🔗 LinkedIn: <a href="https://linkedin.com/rafaelgonzalezalbes" target="_blank" rel="noopener noreferrer">linkedin.com/rafaelgonzalezalbes</a></p>
|
||||
<p>💻 GitHub: <a href="https://github.com/anik0z" target="_blank" rel="noopener noreferrer">github.com/anik0z</a></p>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'clear':
|
||||
setHistory([]);
|
||||
setInput('');
|
||||
return;
|
||||
case 'language en':
|
||||
i18n.changeLanguage('en');
|
||||
output = <p>{t('Language changed to English')}</p>;
|
||||
break;
|
||||
case 'language es':
|
||||
i18n.changeLanguage('es');
|
||||
output = <p>{t('Language changed to Spanish')}</p>;
|
||||
break;
|
||||
case 'language it':
|
||||
i18n.changeLanguage('it');
|
||||
output = <p>{t('Language changed to Italian')}</p>;
|
||||
break;
|
||||
case 'language de':
|
||||
i18n.changeLanguage('de');
|
||||
output = <p>{t('Language changed to German')}</p>;
|
||||
break;
|
||||
case 'sudo':
|
||||
output = <p style={{ color: '#ff5555' }}>sudo: Permission denied. This incident will be reported to Santa Claus. 🎅</p>;
|
||||
break;
|
||||
case 'coffee':
|
||||
case 'beer':
|
||||
output = (
|
||||
<div style={{ color: '#ffb627', fontFamily: 'monospace', whiteSpace: 'pre-line', lineHeight: '1.2' }}>
|
||||
{` ( ) ( )
|
||||
) ( ) (
|
||||
[_____]___
|
||||
| | |
|
||||
|'--' |
|
||||
| |___
|
||||
| | \\
|
||||
| | |
|
||||
|________|___/
|
||||
\`--------'
|
||||
Refilling productivity... Done. [████████████████████] 100%`}
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'scramble':
|
||||
case 'cipher':
|
||||
window.dispatchEvent(new Event('triggerScramble'));
|
||||
output = <p style={{ color: '#39d353' }}>[OK] Decryption protocol initiated. Scrambling page text...</p>;
|
||||
break;
|
||||
case 'destruct':
|
||||
output = <DestructSequence />;
|
||||
break;
|
||||
case 'matrix':
|
||||
output = <MatrixRain />;
|
||||
break;
|
||||
default:
|
||||
output = <p className={styles.error}>{t('Command not found. Type \'help\' for available options.')}</p>;
|
||||
}
|
||||
|
||||
setHistory((prev) => [...prev, { command: cmd, output }]);
|
||||
setInput('');
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
executeCommand(input);
|
||||
}
|
||||
};
|
||||
|
||||
const quickLinks = [
|
||||
{ cmd: 'help', label: 'help' },
|
||||
{ cmd: 'about', label: 'about' },
|
||||
{ cmd: 'experience', label: 'experience' },
|
||||
{ cmd: 'education', label: 'education' },
|
||||
{ cmd: 'skills', label: 'skills' },
|
||||
{ cmd: 'projects', label: 'projects' },
|
||||
{ cmd: 'contact', label: 'contact' },
|
||||
{ cmd: 'clear', label: 'clear' }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={styles.terminalContainer} onClick={handleContainerClick}>
|
||||
<div className={styles.terminalHeader}>
|
||||
<div className={styles.headerLeft}>
|
||||
<Terminal size={18} />
|
||||
<span>rafael@gonzalez-albes: ~/portfolio</span>
|
||||
</div>
|
||||
<div className={styles.langSelector}>
|
||||
{langs.map((lang) => (
|
||||
<button
|
||||
key={lang.code}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
executeCommand(`language ${lang.code}`);
|
||||
}}
|
||||
className={`${styles.langBtn} ${currentLang === lang.code ? styles.langBtnActive : ''}`}
|
||||
>
|
||||
<FlagImg code={lang.code} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.mainLayout}>
|
||||
{/* Terminal Screen */}
|
||||
<div className={styles.terminalScreen}>
|
||||
<div className={styles.consoleHistory}>
|
||||
{history.map((item, idx) => (
|
||||
<div key={idx} className={styles.historyRow}>
|
||||
{item.command !== 'system_init' && (
|
||||
<div className={styles.inputPromptRow}>
|
||||
<span className={styles.prompt}>rafael@gonzalez-albes:~$</span>
|
||||
<span className={styles.loggedCommand}>{item.command}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.commandOutput}>{item.output}</div>
|
||||
</div>
|
||||
))}
|
||||
<div ref={consoleEndRef} />
|
||||
</div>
|
||||
|
||||
<div className={styles.inputPrompt}>
|
||||
<span className={styles.prompt}>rafael@gonzalez-albes:~$</span>
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
className={styles.consoleInput}
|
||||
autoFocus
|
||||
autoComplete="off"
|
||||
spellCheck="false"
|
||||
/>
|
||||
<button onClick={() => executeCommand(input)} className={styles.sendBtn} aria-label="Send command">
|
||||
<Send size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sidebar Panel */}
|
||||
<div className={styles.terminalSidebar}>
|
||||
<h4>⚡ {t('Quick Commands')}</h4>
|
||||
<div className={styles.commandBtns}>
|
||||
{quickLinks.map((link) => (
|
||||
<button
|
||||
key={link.cmd}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
executeCommand(link.cmd);
|
||||
}}
|
||||
className={styles.quickBtn}
|
||||
>
|
||||
{link.label}
|
||||
</button>
|
||||
))}
|
||||
<a
|
||||
href="/Rafael_Gonzalez_Albes_CV.pdf"
|
||||
download
|
||||
className={styles.quickBtn}
|
||||
style={{
|
||||
marginTop: '15px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '8px',
|
||||
background: '#39d353',
|
||||
color: '#000',
|
||||
textDecoration: 'none',
|
||||
fontWeight: 'bold'
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Download size={14} />
|
||||
{t('Download CV')}
|
||||
</a>
|
||||
</div>
|
||||
<p className={styles.sidebarNote}>
|
||||
{t("Click any command to run it in the terminal output grid.")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TerminalTemplate;
|
||||
543
src/templates/ThreeDTemplate.module.css
Normal file
|
|
@ -0,0 +1,543 @@
|
|||
.container {
|
||||
min-height: 100vh;
|
||||
color: #f3f4f6;
|
||||
font-family: 'Inter', sans-serif;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
background-color: #030712;
|
||||
}
|
||||
|
||||
.canvasBackground {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1.5rem 2rem;
|
||||
background: rgba(3, 7, 18, 0.6);
|
||||
backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: 'Outfit', sans-serif;
|
||||
font-weight: 800;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.logoAccent {
|
||||
background: linear-gradient(135deg, #60a5fa, #ec4899);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.nav a {
|
||||
color: #9ca3af;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
font-size: 0.95rem;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.nav a:hover {
|
||||
color: #f3f4f6;
|
||||
}
|
||||
|
||||
.langs {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.langBtn {
|
||||
background: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
padding: 0.25rem 0.5rem;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.langBtn:hover {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.langActive {
|
||||
border-color: #60a5fa;
|
||||
background: rgba(96, 165, 250, 0.1);
|
||||
}
|
||||
|
||||
.main {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 4rem 2rem 8rem 2rem;
|
||||
}
|
||||
|
||||
/* Hero */
|
||||
.heroSection {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
padding: 6rem 0 8rem 0;
|
||||
}
|
||||
|
||||
.heroGlow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
background: radial-gradient(circle, rgba(139, 92, 246, 0.15) 0%, rgba(236, 72, 153, 0.05) 50%, transparent 100%);
|
||||
filter: blur(40px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: 'Outfit', sans-serif;
|
||||
font-size: 4rem;
|
||||
font-weight: 900;
|
||||
letter-spacing: -2px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.titleGradient {
|
||||
background: linear-gradient(135deg, #38bdf8, #8b5cf6, #ec4899);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.25rem;
|
||||
color: #9ca3af;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.downloadBtn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.8rem 1.8rem;
|
||||
background: linear-gradient(135deg, rgba(96, 165, 250, 0.1) 0%, rgba(236, 72, 153, 0.1) 100%);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
color: #f3f4f6;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.downloadBtn:hover {
|
||||
border-color: rgba(96, 165, 250, 0.5);
|
||||
box-shadow: 0 0 20px rgba(96, 165, 250, 0.2);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Grid & Cards */
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.span2 {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.span3 {
|
||||
grid-column: span 3;
|
||||
}
|
||||
|
||||
.tiltCard {
|
||||
transition: transform 0.1s ease-out;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
.cardInner {
|
||||
height: 100%;
|
||||
padding: 2.2rem;
|
||||
background: rgba(17, 24, 39, 0.45);
|
||||
backdrop-filter: blur(16px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-radius: 16px;
|
||||
transform: translateZ(20px); /* Creates absolute depth */
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
|
||||
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.tiltCard:hover .cardInner {
|
||||
border-color: rgba(139, 92, 246, 0.3);
|
||||
box-shadow: 0 10px 40px rgba(139, 92, 246, 0.15);
|
||||
}
|
||||
|
||||
.cardHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.cardHeader h3 {
|
||||
font-family: 'Outfit', sans-serif;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.iconPink { color: #ec4899; }
|
||||
.iconBlue { color: #3b82f6; }
|
||||
.iconPurple { color: #8b5cf6; }
|
||||
|
||||
.bioText {
|
||||
color: #9ca3af;
|
||||
line-height: 1.6;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.bioText strong {
|
||||
color: #f3f4f6;
|
||||
}
|
||||
|
||||
/* Skills */
|
||||
.skillsList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.skillGroup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.skillLabel {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.skillGroup p {
|
||||
color: #d1d5db;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Timeline */
|
||||
.timelineHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.titleGroup {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.titleGroup h3 {
|
||||
font-family: 'Outfit', sans-serif;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.tabToggle {
|
||||
display: flex;
|
||||
background: rgba(3, 7, 18, 0.4);
|
||||
padding: 3px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.tabBtn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.4rem 1rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #9ca3af;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.tabActive {
|
||||
background: rgba(139, 92, 246, 0.2);
|
||||
color: #c084fc;
|
||||
}
|
||||
|
||||
.timelineContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.timelineItem {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.timelineItem::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 1.5rem;
|
||||
left: 50%;
|
||||
bottom: -2rem;
|
||||
width: 1px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.timelineItem:last-child::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.timelineMeta {
|
||||
width: 150px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.timelineDate {
|
||||
font-weight: 700;
|
||||
font-size: 0.95rem;
|
||||
color: #f3f4f6;
|
||||
}
|
||||
|
||||
.timelineCompany {
|
||||
font-size: 0.8rem;
|
||||
color: #9ca3af;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.timelineBody {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.timelineBody h4 {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.timelineLocation {
|
||||
font-size: 0.8rem;
|
||||
color: #6b7280;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.timelineDetails {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.timelineDetails li {
|
||||
color: #9ca3af;
|
||||
font-size: 0.9rem;
|
||||
position: relative;
|
||||
padding-left: 1rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.timelineDetails li::before {
|
||||
content: '→';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: #8b5cf6;
|
||||
}
|
||||
|
||||
/* Projects */
|
||||
.projectsHeader {
|
||||
grid-column: span 3;
|
||||
margin-top: 4rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.projectsHeader h2 {
|
||||
font-family: 'Outfit', sans-serif;
|
||||
font-size: 2.2rem;
|
||||
font-weight: 800;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.projectsHeader p {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.projHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.projHeader h4 {
|
||||
font-family: 'Outfit', sans-serif;
|
||||
font-size: 1.15rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.projLink {
|
||||
color: #9ca3af;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.projLink:hover {
|
||||
color: #60a5fa;
|
||||
}
|
||||
|
||||
.projDesc {
|
||||
color: #9ca3af;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 1.5rem;
|
||||
min-height: 4.5rem;
|
||||
}
|
||||
|
||||
.projTags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.projTag {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
padding: 0.2rem 0.6rem;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 4px;
|
||||
color: #60a5fa;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
padding: 3rem 2rem;
|
||||
background: rgba(3, 7, 18, 0.8);
|
||||
}
|
||||
|
||||
.footerMain {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.contactEmail {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.contactEmail a {
|
||||
color: #9ca3af;
|
||||
text-decoration: none;
|
||||
font-size: 0.95rem;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.contactEmail a:hover {
|
||||
color: #f3f4f6;
|
||||
}
|
||||
|
||||
.footerSocials {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.footerSocials a {
|
||||
color: #9ca3af;
|
||||
transition: color 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.footerSocials a:hover {
|
||||
color: #f3f4f6;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
.span3, .projectsHeader {
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.header {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
.nav {
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.span2, .span3, .projectsHeader {
|
||||
grid-column: span 1;
|
||||
}
|
||||
.title {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
.timelineItem {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.timelineItem::after {
|
||||
display: none;
|
||||
}
|
||||
.timelineMeta {
|
||||
width: auto;
|
||||
}
|
||||
.footerMain {
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
.footerSocials {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
352
src/templates/ThreeDTemplate.tsx
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getAboutBio, getWorkItems, getEducationItems, getProjects } from '../data/portfolioData';
|
||||
import FlagImg from '../components/FlagImg';
|
||||
import { Shield, Zap, Mail, Github, Linkedin, ExternalLink, Briefcase, GraduationCap, Download, Layers } from 'lucide-react';
|
||||
import styles from './ThreeDTemplate.module.css';
|
||||
|
||||
// 3D Tilt Card Component
|
||||
interface TiltCardProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
const TiltCard: React.FC<TiltCardProps> = ({ children, className = '', id }) => {
|
||||
const cardRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const card = cardRef.current;
|
||||
if (!card) return;
|
||||
|
||||
const rect = card.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left; // x position within the element
|
||||
const y = e.clientY - rect.top; // y position within the element
|
||||
|
||||
const centerX = rect.width / 2;
|
||||
const centerY = rect.height / 2;
|
||||
|
||||
// Calculate rotation (-10 to 10 degrees)
|
||||
const rotateX = ((centerY - y) / centerY) * 10;
|
||||
const rotateY = ((x - centerX) / centerX) * 10;
|
||||
|
||||
card.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(1.02, 1.02, 1.02)`;
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
const card = cardRef.current;
|
||||
if (!card) return;
|
||||
card.style.transform = 'perspective(1000px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)';
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={cardRef}
|
||||
className={`${styles.tiltCard} ${className}`}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
id={id}
|
||||
>
|
||||
<div className={styles.cardInner}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ThreeDTemplate: React.FC = () => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const [timelineTab, setTimelineTab] = useState<'work' | 'education'>('work');
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const mouseRef = useRef({ x: 0, y: 0, targetX: 0, targetY: 0 });
|
||||
|
||||
const currentLang = i18n.language ? i18n.language.substring(0, 2) : 'en';
|
||||
|
||||
const langs = [
|
||||
{ code: 'en' },
|
||||
{ code: 'es' },
|
||||
{ code: 'it' },
|
||||
{ code: 'de' }
|
||||
];
|
||||
|
||||
const workItems = getWorkItems(t);
|
||||
const educationItems = getEducationItems(t);
|
||||
const projects = getProjects(t);
|
||||
|
||||
// 3D Starfield Canvas Animation
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
let animationFrameId: number;
|
||||
let width = (canvas.width = window.innerWidth);
|
||||
let height = (canvas.height = window.innerHeight);
|
||||
|
||||
const handleResize = () => {
|
||||
if (!canvas) return;
|
||||
width = canvas.width = window.innerWidth;
|
||||
height = canvas.height = window.innerHeight;
|
||||
};
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
mouseRef.current.targetX = (e.clientX - window.innerWidth / 2) * 0.05;
|
||||
mouseRef.current.targetY = (e.clientY - window.innerHeight / 2) * 0.05;
|
||||
};
|
||||
window.addEventListener('mousemove', handleMouseMove);
|
||||
|
||||
// Generate 3D stars
|
||||
const numStars = 150;
|
||||
const stars: Array<{ x: number; y: number; z: number; color: string; size: number }> = [];
|
||||
const colors = ['#60a5fa', '#a78bfa', '#ec4899', '#38bdf8', '#f472b6'];
|
||||
|
||||
for (let i = 0; i < numStars; i++) {
|
||||
stars.push({
|
||||
x: (Math.random() - 0.5) * 2000,
|
||||
y: (Math.random() - 0.5) * 2000,
|
||||
z: Math.random() * 2000,
|
||||
color: colors[Math.floor(Math.random() * colors.length)],
|
||||
size: Math.random() * 2 + 1
|
||||
});
|
||||
}
|
||||
|
||||
const focalLength = 800;
|
||||
let angleY = 0.001;
|
||||
let angleX = 0.0005;
|
||||
|
||||
const animate = () => {
|
||||
ctx.fillStyle = '#030712';
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
// Smooth mouse tracking rotation
|
||||
mouseRef.current.x += (mouseRef.current.targetX - mouseRef.current.x) * 0.05;
|
||||
mouseRef.current.y += (mouseRef.current.targetY - mouseRef.current.y) * 0.05;
|
||||
|
||||
const currentAngleY = angleY + mouseRef.current.x * 0.0001;
|
||||
const currentAngleX = angleX + mouseRef.current.y * 0.0001;
|
||||
|
||||
const cosY = Math.cos(currentAngleY);
|
||||
const sinY = Math.sin(currentAngleY);
|
||||
const cosX = Math.cos(currentAngleX);
|
||||
const sinX = Math.sin(currentAngleX);
|
||||
|
||||
stars.forEach((star) => {
|
||||
// Y-axis rotation
|
||||
const x1 = star.x * cosY - star.z * sinY;
|
||||
const z1 = star.z * cosY + star.x * sinY;
|
||||
|
||||
// X-axis rotation
|
||||
const y2 = star.y * cosX - z1 * sinX;
|
||||
const z2 = z1 * cosX + star.y * sinX;
|
||||
|
||||
star.x = x1;
|
||||
star.y = y2;
|
||||
star.z = z2;
|
||||
|
||||
// Reset if star moves too close or past camera
|
||||
if (star.z <= -focalLength) {
|
||||
star.z += 2000;
|
||||
}
|
||||
|
||||
// Perspective Projection
|
||||
const scale = focalLength / (focalLength + star.z);
|
||||
const projX = width / 2 + star.x * scale;
|
||||
const projY = height / 2 + star.y * scale;
|
||||
|
||||
// Render if within screen boundaries
|
||||
if (projX >= 0 && projX <= width && projY >= 0 && projY <= height) {
|
||||
const alpha = Math.min(Math.max((2000 - star.z) / 2000, 0), 1);
|
||||
ctx.beginPath();
|
||||
ctx.arc(projX, projY, star.size * scale, 0, Math.PI * 2);
|
||||
ctx.fillStyle = star.color;
|
||||
ctx.globalAlpha = alpha;
|
||||
ctx.fill();
|
||||
}
|
||||
});
|
||||
|
||||
ctx.globalAlpha = 1.0;
|
||||
animationFrameId = requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
animate();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
window.removeEventListener('mousemove', handleMouseMove);
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<canvas ref={canvasRef} className={styles.canvasBackground} />
|
||||
|
||||
<header className={styles.header}>
|
||||
<div className={styles.logo} onClick={() => window.dispatchEvent(new CustomEvent('goHome'))}>
|
||||
<span>3D_//<span className={styles.logoAccent}>portfolio</span></span>
|
||||
</div>
|
||||
<nav className={styles.nav}>
|
||||
<a href="#about">{t('About')}</a>
|
||||
<a href="#skills">{t('Skills')}</a>
|
||||
<a href="#experience">{t('Experience')}</a>
|
||||
<a href="#projects">{t('Projects')}</a>
|
||||
</nav>
|
||||
<div className={styles.langs}>
|
||||
{langs.map((lang) => (
|
||||
<button
|
||||
key={lang.code}
|
||||
onClick={() => i18n.changeLanguage(lang.code)}
|
||||
className={`${styles.langBtn} ${currentLang === lang.code ? styles.langActive : ''}`}
|
||||
>
|
||||
<FlagImg code={lang.code} size={18} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className={styles.main}>
|
||||
{/* Hero Section */}
|
||||
<section className={styles.heroSection}>
|
||||
<div className={styles.heroGlow} />
|
||||
<h1 className={styles.title}>
|
||||
RAFAEL <span className={styles.titleGradient}>GONZÁLEZ ALBES</span>
|
||||
</h1>
|
||||
<p className={styles.subtitle}>{t('Professional Software Developer')}</p>
|
||||
<div className={styles.heroCta}>
|
||||
<a href="/Rafael_Gonzalez_Albes_CV.pdf" download className={styles.downloadBtn}>
|
||||
<Download size={18} />
|
||||
{t('Download CV')}
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Content Cards */}
|
||||
<div className={styles.grid} id="about">
|
||||
{/* About Bio Card */}
|
||||
<TiltCard className={styles.span2}>
|
||||
<div className={styles.cardHeader}>
|
||||
<Shield size={20} className={styles.iconPink} />
|
||||
<h3>{t('Developer Profile')}</h3>
|
||||
</div>
|
||||
{getAboutBio(t, styles.bioText)}
|
||||
</TiltCard>
|
||||
|
||||
{/* Skills Card */}
|
||||
<TiltCard id="skills">
|
||||
<div className={styles.cardHeader}>
|
||||
<Zap size={20} className={styles.iconBlue} />
|
||||
<h3>{t('Core Stack')}</h3>
|
||||
</div>
|
||||
<div className={styles.skillsList}>
|
||||
<div className={styles.skillGroup}>
|
||||
<span className={styles.skillLabel}>{t('Backend & Systems')}</span>
|
||||
<p>PHP, Symfony, Python, Java, C++, SQL, Neo4j, Docker, Bash</p>
|
||||
</div>
|
||||
<div className={styles.skillGroup}>
|
||||
<span className={styles.skillLabel}>{t('Frontend & UI')}</span>
|
||||
<p>TypeScript, React, CSS3, HTML5, Bootstrap, JQuery</p>
|
||||
</div>
|
||||
</div>
|
||||
</TiltCard>
|
||||
|
||||
{/* Chronology Card (Experience & Education) */}
|
||||
<TiltCard className={styles.span3} id="experience">
|
||||
<div className={styles.timelineHeader}>
|
||||
<div className={styles.titleGroup}>
|
||||
<Layers size={20} className={styles.iconPurple} />
|
||||
<h3>{t('Chronology')}</h3>
|
||||
</div>
|
||||
<div className={styles.tabToggle}>
|
||||
<button
|
||||
className={`${styles.tabBtn} ${timelineTab === 'work' ? styles.tabActive : ''}`}
|
||||
onClick={() => setTimelineTab('work')}
|
||||
>
|
||||
<Briefcase size={15} />
|
||||
<span>{t('Work')}</span>
|
||||
</button>
|
||||
<button
|
||||
className={`${styles.tabBtn} ${timelineTab === 'education' ? styles.tabActive : ''}`}
|
||||
onClick={() => setTimelineTab('education')}
|
||||
>
|
||||
<GraduationCap size={15} />
|
||||
<span>{t('Education')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.timelineContent}>
|
||||
{(timelineTab === 'work' ? workItems : educationItems).map((item, idx) => (
|
||||
<div key={idx} className={styles.timelineItem}>
|
||||
<div className={styles.timelineMeta}>
|
||||
<span className={styles.timelineDate}>{item.date}</span>
|
||||
<span className={styles.timelineCompany}>{item.company}</span>
|
||||
</div>
|
||||
<div className={styles.timelineBody}>
|
||||
<h4>{item.title}</h4>
|
||||
<p className={styles.timelineLocation}>{item.location}</p>
|
||||
<ul className={styles.timelineDetails}>
|
||||
{item.details.map((detail, dIdx) => (
|
||||
<li key={dIdx}>{detail}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</TiltCard>
|
||||
|
||||
{/* Projects Section */}
|
||||
<div className={styles.projectsHeader} id="projects">
|
||||
<h2>{t('Project Modules')}</h2>
|
||||
<p>{t('A collection of featured projects and engineering work.')}</p>
|
||||
</div>
|
||||
|
||||
{projects.map((project, idx) => (
|
||||
<TiltCard key={idx}>
|
||||
<div className={styles.projHeader}>
|
||||
<h4>{project.title}</h4>
|
||||
<a
|
||||
href="https://github.com/anik0z"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.projLink}
|
||||
aria-label="GitHub Project Link"
|
||||
>
|
||||
<ExternalLink size={16} />
|
||||
</a>
|
||||
</div>
|
||||
<p className={styles.projDesc}>{project.shortDesc}</p>
|
||||
<div className={styles.projTags}>
|
||||
{project.tech.map((tag) => (
|
||||
<span key={tag} className={styles.projTag}>{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
</TiltCard>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer className={styles.footer}>
|
||||
<div className={styles.footerMain}>
|
||||
<div className={styles.contactEmail}>
|
||||
<Mail size={16} />
|
||||
<a href="mailto:rafaelgonzalezalbes@gmail.com">rafaelgonzalezalbes@gmail.com</a>
|
||||
</div>
|
||||
<div className={styles.footerSocials}>
|
||||
<a href="https://github.com/anik0z" target="_blank" rel="noopener noreferrer" aria-label="GitHub">
|
||||
<Github size={20} />
|
||||
</a>
|
||||
<a href="https://linkedin.com/rafaelgonzalezalbes" target="_blank" rel="noopener noreferrer" aria-label="LinkedIn">
|
||||
<Linkedin size={20} />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThreeDTemplate;
|
||||
62
tests/locale.spec.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('I18n Language & Translation Audits', () => {
|
||||
|
||||
const locales = [
|
||||
{
|
||||
code: 'en',
|
||||
galleryTitle: 'Design Gallery',
|
||||
timelineText: 'My Timeline',
|
||||
skillsText: 'Technical Expertise'
|
||||
},
|
||||
{
|
||||
code: 'es',
|
||||
galleryTitle: 'Galería de Diseños',
|
||||
timelineText: 'Mi Cronología',
|
||||
skillsText: 'Experiencia Técnica'
|
||||
},
|
||||
{
|
||||
code: 'it',
|
||||
galleryTitle: 'Galleria del Design',
|
||||
timelineText: 'La mia Cronologia',
|
||||
skillsText: 'Competenze Tecniche'
|
||||
},
|
||||
{
|
||||
code: 'de',
|
||||
galleryTitle: 'Design-Galerie',
|
||||
timelineText: 'Mein Werdegang',
|
||||
skillsText: 'Technische Kompetenz'
|
||||
}
|
||||
];
|
||||
|
||||
for (const locale of locales) {
|
||||
test(`Verify translation catalog maps correctly for language: [${locale.code.toUpperCase()}]`, async ({ page }) => {
|
||||
// 1. Visit gallery portal with lang parameter
|
||||
await page.goto(`/?lang=${locale.code}`);
|
||||
|
||||
// Assert that portal title is correctly translated
|
||||
const titleLocator = page.locator('.gallery-title');
|
||||
await expect(titleLocator).toContainText(locale.galleryTitle);
|
||||
|
||||
// 2. Open Bento template card
|
||||
const bentoCard = page.locator('.gallery-card', { hasText: 'Bento Grid' });
|
||||
await expect(bentoCard).toBeVisible();
|
||||
await bentoCard.click();
|
||||
|
||||
// Let transition finish
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Verify that the template was loaded and is translated
|
||||
const bentoContainer = page.locator('[class*="bentoContainer"]');
|
||||
await expect(bentoContainer).toBeVisible();
|
||||
|
||||
// Check Bento Timeline header translation
|
||||
const timelineHeader = page.locator('h3', { hasText: locale.timelineText }).first();
|
||||
await expect(timelineHeader).toBeVisible();
|
||||
|
||||
// Check Bento Skills header translation
|
||||
const skillsHeader = page.locator('h3', { hasText: locale.skillsText }).first();
|
||||
await expect(skillsHeader).toBeVisible();
|
||||
});
|
||||
}
|
||||
});
|
||||
BIN
tests/screenshots/1-gallery-portal-desktop-chrome.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
tests/screenshots/1-gallery-portal-mobile-iphone.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
BIN
tests/screenshots/1-gallery-portal-tablet-ipad.png
Normal file
|
After Width: | Height: | Size: 238 KiB |
BIN
tests/screenshots/template-aurora-glass-desktop-chrome.png
Normal file
|
After Width: | Height: | Size: 498 KiB |
BIN
tests/screenshots/template-aurora-glass-mobile-iphone.png
Normal file
|
After Width: | Height: | Size: 529 KiB |
BIN
tests/screenshots/template-aurora-glass-tablet-ipad.png
Normal file
|
After Width: | Height: | Size: 616 KiB |
BIN
tests/screenshots/template-github-theme-desktop-chrome.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
tests/screenshots/template-github-theme-mobile-iphone.png
Normal file
|
After Width: | Height: | Size: 9 KiB |
BIN
tests/screenshots/template-github-theme-tablet-ipad.png
Normal file
|
After Width: | Height: | Size: 281 KiB |
BIN
tests/screenshots/template-interactive-3d-desktop-chrome.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
tests/screenshots/template-interactive-3d-mobile-iphone.png
Normal file
|
After Width: | Height: | Size: 317 KiB |
BIN
tests/screenshots/template-interactive-3d-tablet-ipad.png
Normal file
|
After Width: | Height: | Size: 350 KiB |
BIN
tests/screenshots/template-interactive-cli-desktop-chrome.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
tests/screenshots/template-interactive-cli-mobile-iphone.png
Normal file
|
After Width: | Height: | Size: 232 KiB |
BIN
tests/screenshots/template-interactive-cli-tablet-ipad.png
Normal file
|
After Width: | Height: | Size: 167 KiB |
74
tests/visual.spec.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { test, expect } from '@playwright/test';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
test.describe('Portfolio Layout & Responsiveness Audit', () => {
|
||||
|
||||
// Ensure the screenshots folder exists
|
||||
test.beforeAll(() => {
|
||||
const screenshotDir = 'tests/screenshots';
|
||||
if (!fs.existsSync(screenshotDir)) {
|
||||
fs.mkdirSync(screenshotDir, { recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('Verify homepage and audit key design templates', async ({ page }) => {
|
||||
const projectName = test.info().project.name;
|
||||
|
||||
// 1. Visit homepage
|
||||
await page.goto('/');
|
||||
|
||||
// Check that header logo renders correctly
|
||||
await expect(page.locator('.gallery-logo')).toBeVisible();
|
||||
|
||||
// Verify main title and subtitle
|
||||
await expect(page.locator('.gallery-title')).toBeVisible();
|
||||
|
||||
// Take a screenshot of the main design gallery portal
|
||||
await page.screenshot({ path: `tests/screenshots/1-gallery-portal-${projectName}.png` });
|
||||
|
||||
// Design templates to audit
|
||||
const templatesToTest = [
|
||||
{ name: 'GitHub Theme', containerClass: '.githubWrap' },
|
||||
{ name: 'Interactive 3D', containerClass: '[class*="container"]' }, // ThreeDTemplate uses container
|
||||
{ name: 'Interactive CLI', containerClass: '[class*="terminalContainer"]' },
|
||||
{ name: 'Aurora Glass', containerClass: '[class*="auroraWrap"]' }
|
||||
];
|
||||
|
||||
for (const temp of templatesToTest) {
|
||||
console.log(`Auditing template: ${temp.name} on ${projectName}`);
|
||||
|
||||
// Locate card by matching text contents
|
||||
const card = page.locator('.gallery-card', { hasText: temp.name });
|
||||
await expect(card).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Click choice and wait for transition
|
||||
await card.click();
|
||||
|
||||
// Wait for a few seconds to let animations/renders finish
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Verify that the template loaded
|
||||
const layoutExists = await page.locator(temp.containerClass).first().isVisible();
|
||||
if (!layoutExists) {
|
||||
// Fallback check: ensure we aren't still on the gallery page
|
||||
const isStillOnGallery = await page.locator('.gallery-container').isVisible();
|
||||
if (isStillOnGallery) {
|
||||
throw new Error(`Failed to navigate to template "${temp.name}". Still on gallery portal.`);
|
||||
}
|
||||
}
|
||||
|
||||
// Take template view screenshot
|
||||
const safeName = temp.name.toLowerCase().replace(/\s+/g, '-');
|
||||
await page.screenshot({ path: `tests/screenshots/template-${safeName}-${projectName}.png` });
|
||||
|
||||
// Go back to main gallery by dispatching the custom goHome event
|
||||
await page.evaluate(() => {
|
||||
window.dispatchEvent(new CustomEvent('goHome'));
|
||||
});
|
||||
|
||||
// Wait for gallery portal to reload
|
||||
await expect(page.locator('.gallery-container')).toBeVisible({ timeout: 10000 });
|
||||
}
|
||||
});
|
||||
});
|
||||
26
tsconfig.app.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "es2023",
|
||||
"lib": ["ES2023", "DOM"],
|
||||
"module": "esnext",
|
||||
"types": ["vite/client"],
|
||||
"allowArbitraryExtensions": true,
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
7
tsconfig.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
23
tsconfig.node.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "es2023",
|
||||
"lib": ["ES2023"],
|
||||
"types": ["node"],
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"module": "nodenext",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
76
vite.config.ts
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
// Custom Vite plugin to parse Gettext .po files into JSON objects at build/bundle time
|
||||
function poLoader() {
|
||||
return {
|
||||
name: 'po-loader',
|
||||
transform(code: string, id: string) {
|
||||
if (id.endsWith('.po')) {
|
||||
const translations: Record<string, string> = {};
|
||||
const lines = code.split(/\r?\n/);
|
||||
|
||||
let currentKey = '';
|
||||
let currentValue = '';
|
||||
let readingField: 'msgid' | 'msgstr' | '' = '';
|
||||
|
||||
const saveCurrent = () => {
|
||||
if (currentKey) {
|
||||
const key = currentKey.replace(/\\n/g, '\n').replace(/\\"/g, '"');
|
||||
const val = currentValue.replace(/\\n/g, '\n').replace(/\\"/g, '"');
|
||||
translations[key] = val;
|
||||
}
|
||||
currentKey = '';
|
||||
currentValue = '';
|
||||
readingField = '';
|
||||
};
|
||||
|
||||
for (let line of lines) {
|
||||
line = line.trim();
|
||||
if (line.startsWith('#') || !line) continue;
|
||||
|
||||
if (line.startsWith('msgid ')) {
|
||||
saveCurrent();
|
||||
readingField = 'msgid';
|
||||
const match = line.match(/^msgid\s+"(.*)"$/);
|
||||
currentKey = match ? match[1] : '';
|
||||
} else if (line.startsWith('msgstr ')) {
|
||||
readingField = 'msgstr';
|
||||
const match = line.match(/^msgstr\s+"(.*)"$/);
|
||||
currentValue = match ? match[1] : '';
|
||||
} else if (line.startsWith('"') && line.endsWith('"')) {
|
||||
const match = line.match(/^"(.*)"$/);
|
||||
if (match) {
|
||||
const val = match[1];
|
||||
if (readingField === 'msgid') {
|
||||
currentKey += val;
|
||||
} else if (readingField === 'msgstr') {
|
||||
currentValue += val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
saveCurrent();
|
||||
|
||||
return {
|
||||
code: `export default ${JSON.stringify(translations)};`,
|
||||
map: null
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// https://vite.dev/config/
|
||||
const buildId = Date.now().toString(36); // unique per build run
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react(), poLoader()],
|
||||
css: {
|
||||
modules: {
|
||||
// Regenerate class hashes on every build → harder for DOM scrapers to target
|
||||
generateScopedName: `[hash:base64:5]_${buildId}`
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||