Add photo, company logos, and short/long CV toggle
Features: - Profile photo display (right side, inline with header) - Company logos for all major employers (8 logos downloaded) - Short/Long CV toggle for condensed/detailed view - Short descriptions (1-2 lines) for quick overview - Experience separators with border lines Photo Implementation: - Circular photo (120px) on right side of header - Placeholder SVG if photo not uploaded - Instructions in ADDING-YOUR-PHOTO.md - Photo stored in static/images/profile/ Company Logos: - Olympic Broadcasting Services, AENA, SAP, Gigya - Accenture, Everis, Indra, Megabanner - 40px logos displayed inline with experience - Auto-hide if logo missing - Mobile: logos hidden for cleaner layout Short/Long Toggle: - Toggle buttons in action bar (Corto/Largo) - Short mode: shows shortDescription only - Long mode: shows full responsibilities + technologies - CSS-based show/hide (no page reload) - Defaults to short view Layout Updates: - Header: text left, photo right, inline alignment - Experience items: separated by border lines - Responsive: photo centers on mobile - Print-optimized: smaller photo in PDF Data Updates: - Added shortDescription field to Experience struct - 13 short descriptions for all positions (EN/ES) - Added companyLogo field with filename mapping - JSON updated with all new fields Tech: - Pure CSS toggle (no HTMX needed) - Vanilla JavaScript for button states - Maintains bilingual support (ES/EN)
@@ -0,0 +1,72 @@
|
||||
# Cómo Añadir tu Foto al CV
|
||||
|
||||
## 📸 Paso 1: Prepara tu Foto
|
||||
|
||||
1. **Busca una foto profesional** (preferiblemente tipo LinkedIn)
|
||||
2. **Formato recomendado**: JPG o PNG
|
||||
3. **Tamaño**: Al menos 300x300 píxeles (cuadrada es mejor)
|
||||
4. **Calidad**: Fondo neutro, buena iluminación
|
||||
|
||||
## 📁 Paso 2: Guarda la Foto
|
||||
|
||||
Guarda tu foto en:
|
||||
```
|
||||
static/images/profile/photo.jpg
|
||||
```
|
||||
|
||||
Puedes usar cualquiera de estos nombres:
|
||||
- `photo.jpg` ✅ (recomendado)
|
||||
- `photo.png` (cambiar en template)
|
||||
- `profile.jpg` (cambiar en template)
|
||||
|
||||
## 🔄 Paso 3: Actualizar (si usas otro nombre)
|
||||
|
||||
Si tu foto se llama diferente a `photo.jpg`, edita `templates/cv-content.html`:
|
||||
|
||||
```html
|
||||
<!-- Cambiar esta línea: -->
|
||||
<img src="/static/images/profile/photo.jpg" ...>
|
||||
|
||||
<!-- Por ejemplo, si tu foto es profile.png: -->
|
||||
<img src="/static/images/profile/profile.png" ...>
|
||||
```
|
||||
|
||||
## 🖼️ Descargar desde LinkedIn
|
||||
|
||||
### Opción 1: Manualmente
|
||||
1. Abre tu perfil de LinkedIn
|
||||
2. Click derecho en tu foto → "Guardar imagen como..."
|
||||
3. Guárdala como `photo.jpg` en `static/images/profile/`
|
||||
|
||||
### Opción 2: Desde tu sitio actual
|
||||
Si ya tienes tu foto en https://juan.andres.morenoyrubio.com:
|
||||
|
||||
```bash
|
||||
cd static/images/profile
|
||||
# Abre el inspector del navegador, busca tu foto, y copia la URL
|
||||
curl -o photo.jpg "URL_DE_TU_FOTO"
|
||||
```
|
||||
|
||||
## ✅ Verificar
|
||||
|
||||
1. Reinicia el servidor: `./cv-server`
|
||||
2. Abre http://localhost:8080
|
||||
3. Deberías ver tu foto en la esquina superior izquierda
|
||||
|
||||
Si no funciona, verás un placeholder gris con el texto "Add your photo".
|
||||
|
||||
## 🎨 Ajustar el Tamaño (Opcional)
|
||||
|
||||
La foto se muestra como un círculo de 120px. Para cambiar el tamaño, edita `static/css/main.css`:
|
||||
|
||||
```css
|
||||
.cv-photo {
|
||||
width: 150px; /* Cambiar aquí */
|
||||
height: 150px; /* Y aquí */
|
||||
border-radius: 50%;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Nota**: El template ya incluye un fallback automático al placeholder si la foto no existe, así que el sitio funcionará con o sin foto.
|
||||
@@ -35,7 +35,9 @@
|
||||
"React",
|
||||
"Node.js",
|
||||
"API Integration"
|
||||
]
|
||||
],
|
||||
"companyLogo": "olympic-broadcasting.png",
|
||||
"shortDescription": "SAP CDC solutions for international broadcasting events. Custom implementations and technical guidance."
|
||||
},
|
||||
{
|
||||
"position": "Senior Technical Consultant",
|
||||
@@ -64,7 +66,9 @@
|
||||
"highlights": [
|
||||
"Successfully deployed authentication system for all AENA airports in Spain",
|
||||
"Managed identity flows for millions of users across web and mobile platforms"
|
||||
]
|
||||
],
|
||||
"companyLogo": "aena.png",
|
||||
"shortDescription": "Lead Technical Consultant for AENA Airports Authentication System serving millions of passengers across all Spanish airports."
|
||||
},
|
||||
{
|
||||
"position": "Senior Technical Consultant",
|
||||
@@ -87,7 +91,9 @@
|
||||
"JavaScript",
|
||||
"Cloud Platforms",
|
||||
"Technical Documentation"
|
||||
]
|
||||
],
|
||||
"companyLogo": "sap.png",
|
||||
"shortDescription": "SAP Customer Data Cloud technical consulting, troubleshooting, and stakeholder education on GDPR compliance."
|
||||
},
|
||||
{
|
||||
"position": "Junior Technical Consultant",
|
||||
@@ -109,7 +115,9 @@
|
||||
"JavaScript",
|
||||
"Customer Support",
|
||||
"System Monitoring"
|
||||
]
|
||||
],
|
||||
"companyLogo": "gigya.png",
|
||||
"shortDescription": "Technical support and problem-solving for Gigya platform. System monitoring and training program development."
|
||||
},
|
||||
{
|
||||
"position": "Fullstack Developer",
|
||||
@@ -130,7 +138,9 @@
|
||||
"Video Processing",
|
||||
"Database Design",
|
||||
"PostgreSQL"
|
||||
]
|
||||
],
|
||||
"companyLogo": "megabanner.png",
|
||||
"shortDescription": "Full-stack development with video system integration for advertisement inclusion in gas station networks."
|
||||
},
|
||||
{
|
||||
"position": "Fullstack Developer",
|
||||
@@ -152,7 +162,9 @@
|
||||
"API Design",
|
||||
"CI/CD",
|
||||
"DevOps"
|
||||
]
|
||||
],
|
||||
"companyLogo": "everis.png",
|
||||
"shortDescription": "API design and automated deployment pipelines. Software testing and scalability implementation."
|
||||
},
|
||||
{
|
||||
"position": "FullStack Developer",
|
||||
@@ -170,7 +182,9 @@
|
||||
"JavaScript",
|
||||
"Redux",
|
||||
"Webpack"
|
||||
]
|
||||
],
|
||||
"companyLogo": "everis.png",
|
||||
"shortDescription": "React application development for multiple clients."
|
||||
},
|
||||
{
|
||||
"position": "Fullstack Developer",
|
||||
@@ -187,7 +201,9 @@
|
||||
"Java",
|
||||
"JavaScript",
|
||||
"Web Development"
|
||||
]
|
||||
],
|
||||
"companyLogo": "indra.png",
|
||||
"shortDescription": "Project management and customer feedback collection across development stages."
|
||||
},
|
||||
{
|
||||
"position": "Technical Director / Programmer",
|
||||
@@ -212,7 +228,8 @@
|
||||
"highlights": [
|
||||
"Reduced production times by 75% through optimized pipelines",
|
||||
"Successfully managed technical team and product development"
|
||||
]
|
||||
],
|
||||
"shortDescription": "Technical Director leading development of backend and 5 websites. Reduced production times by 75%."
|
||||
},
|
||||
{
|
||||
"position": "Programmer Analyst (Freelance)",
|
||||
@@ -230,7 +247,8 @@
|
||||
"PHP",
|
||||
"MySQL",
|
||||
"JavaScript"
|
||||
]
|
||||
],
|
||||
"shortDescription": "WordPress and PHP website development as freelance programmer."
|
||||
},
|
||||
{
|
||||
"position": "Analyst Programmer / Expert Technician",
|
||||
@@ -248,7 +266,8 @@
|
||||
"Java",
|
||||
"System Configuration",
|
||||
"Technical Support"
|
||||
]
|
||||
],
|
||||
"shortDescription": "Software and hardware configuration, technical problem-solving, and team mentoring."
|
||||
},
|
||||
{
|
||||
"position": "Senior Programmer",
|
||||
@@ -266,7 +285,8 @@
|
||||
"Java",
|
||||
"Search Engine Technology",
|
||||
"European R&D Projects"
|
||||
]
|
||||
],
|
||||
"shortDescription": "European R&D project for revolutionary search engine development."
|
||||
},
|
||||
{
|
||||
"position": "Junior Programmer",
|
||||
@@ -285,7 +305,8 @@
|
||||
"Java Applets",
|
||||
"Data Visualization",
|
||||
"Chart Generation"
|
||||
]
|
||||
],
|
||||
"shortDescription": "JAVA development specialized in data chart generation and applet development."
|
||||
}
|
||||
],
|
||||
"education": [
|
||||
|
||||
@@ -35,7 +35,9 @@
|
||||
"React",
|
||||
"Node.js",
|
||||
"Integración de APIs"
|
||||
]
|
||||
],
|
||||
"companyLogo": "olympic-broadcasting.png",
|
||||
"shortDescription": "Soluciones SAP CDC para eventos de transmisión internacional. Implementaciones personalizadas y orientación técnica."
|
||||
},
|
||||
{
|
||||
"position": "Consultor Técnico Senior",
|
||||
@@ -64,7 +66,9 @@
|
||||
"highlights": [
|
||||
"Despliegue exitoso del sistema de autenticación para todos los aeropuertos AENA en España",
|
||||
"Gestión de flujos de identidad para millones de usuarios en plataformas web y móviles"
|
||||
]
|
||||
],
|
||||
"companyLogo": "aena.png",
|
||||
"shortDescription": "Consultor Técnico Principal del Sistema de Autenticación de Aeropuertos AENA sirviendo a millones de pasajeros en todos los aeropuertos españoles."
|
||||
},
|
||||
{
|
||||
"position": "Consultor Técnico Senior",
|
||||
@@ -87,7 +91,9 @@
|
||||
"JavaScript",
|
||||
"Plataformas Cloud",
|
||||
"Documentación Técnica"
|
||||
]
|
||||
],
|
||||
"companyLogo": "sap.png",
|
||||
"shortDescription": "Consultoría técnica SAP Customer Data Cloud, resolución de problemas y educación de stakeholders en cumplimiento GDPR."
|
||||
},
|
||||
{
|
||||
"position": "Consultor Técnico Junior",
|
||||
@@ -109,7 +115,9 @@
|
||||
"JavaScript",
|
||||
"Soporte al Cliente",
|
||||
"Monitoreo de Sistemas"
|
||||
]
|
||||
],
|
||||
"companyLogo": "gigya.png",
|
||||
"shortDescription": "Soporte técnico y resolución de problemas para plataforma Gigya. Monitoreo de sistemas y desarrollo de programas de formación."
|
||||
},
|
||||
{
|
||||
"position": "Desarrollador Fullstack",
|
||||
@@ -130,7 +138,9 @@
|
||||
"Procesamiento de Video",
|
||||
"Diseño de Bases de Datos",
|
||||
"PostgreSQL"
|
||||
]
|
||||
],
|
||||
"companyLogo": "megabanner.png",
|
||||
"shortDescription": "Desarrollo full-stack con integración de sistema de video para inclusión de anuncios en redes de estaciones de servicio."
|
||||
},
|
||||
{
|
||||
"position": "Desarrollador Fullstack",
|
||||
@@ -152,7 +162,9 @@
|
||||
"Diseño de APIs",
|
||||
"CI/CD",
|
||||
"DevOps"
|
||||
]
|
||||
],
|
||||
"companyLogo": "everis.png",
|
||||
"shortDescription": "Diseño de APIs y pipelines de despliegue automatizados. Testing de software e implementación de escalabilidad."
|
||||
},
|
||||
{
|
||||
"position": "Desarrollador FullStack",
|
||||
@@ -170,7 +182,9 @@
|
||||
"JavaScript",
|
||||
"Redux",
|
||||
"Webpack"
|
||||
]
|
||||
],
|
||||
"companyLogo": "everis.png",
|
||||
"shortDescription": "Desarrollo de aplicaciones React para múltiples clientes."
|
||||
},
|
||||
{
|
||||
"position": "Desarrollador Fullstack",
|
||||
@@ -187,7 +201,9 @@
|
||||
"Java",
|
||||
"JavaScript",
|
||||
"Desarrollo Web"
|
||||
]
|
||||
],
|
||||
"companyLogo": "indra.png",
|
||||
"shortDescription": "Gestión de proyectos y recopilación de feedback de clientes en diferentes etapas de desarrollo."
|
||||
},
|
||||
{
|
||||
"position": "Director Técnico / Programador",
|
||||
@@ -212,7 +228,8 @@
|
||||
"highlights": [
|
||||
"Reducción del 75% en tiempos de producción mediante pipelines optimizados",
|
||||
"Gestión exitosa de equipo técnico y desarrollo de productos"
|
||||
]
|
||||
],
|
||||
"shortDescription": "Director Técnico liderando desarrollo de backend y 5 sitios web. Reducción del 75% en tiempos de producción."
|
||||
},
|
||||
{
|
||||
"position": "Analista Programador (Freelance)",
|
||||
@@ -230,7 +247,8 @@
|
||||
"PHP",
|
||||
"MySQL",
|
||||
"JavaScript"
|
||||
]
|
||||
],
|
||||
"shortDescription": "Desarrollo de sitios web WordPress y PHP como programador freelance."
|
||||
},
|
||||
{
|
||||
"position": "Analista Programador / Técnico Experto",
|
||||
@@ -248,7 +266,8 @@
|
||||
"Java",
|
||||
"Configuración de Sistemas",
|
||||
"Soporte Técnico"
|
||||
]
|
||||
],
|
||||
"shortDescription": "Configuración de software y hardware, resolución de problemas técnicos y mentoría de equipos."
|
||||
},
|
||||
{
|
||||
"position": "Programador Senior",
|
||||
@@ -266,7 +285,8 @@
|
||||
"Java",
|
||||
"Tecnología de Motores de Búsqueda",
|
||||
"Proyectos Europeos I+D"
|
||||
]
|
||||
],
|
||||
"shortDescription": "Proyecto europeo I+D para desarrollo de motor de búsqueda revolucionario."
|
||||
},
|
||||
{
|
||||
"position": "Programador Junior",
|
||||
@@ -285,7 +305,8 @@
|
||||
"Applets Java",
|
||||
"Visualización de Datos",
|
||||
"Generación de Gráficos"
|
||||
]
|
||||
],
|
||||
"shortDescription": "Desarrollo JAVA especializado en generación de gráficos de datos y desarrollo de applets."
|
||||
}
|
||||
],
|
||||
"education": [
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Download company logos
|
||||
cd static/images/companies
|
||||
|
||||
# Olympic Broadcasting Services
|
||||
curl -sL "https://logo.clearbit.com/obs.tv" -o olympic-broadcasting.png 2>/dev/null || echo "OBS logo not found"
|
||||
|
||||
# AENA
|
||||
curl -sL "https://logo.clearbit.com/aena.es" -o aena.png 2>/dev/null || echo "AENA logo not found"
|
||||
|
||||
# SAP
|
||||
curl -sL "https://logo.clearbit.com/sap.com" -o sap.png 2>/dev/null || echo "SAP logo not found"
|
||||
|
||||
# Gigya (now SAP CDC)
|
||||
curl -sL "https://logo.clearbit.com/gigya.com" -o gigya.png 2>/dev/null || echo "Gigya logo not found"
|
||||
|
||||
# Accenture
|
||||
curl -sL "https://logo.clearbit.com/accenture.com" -o accenture.png 2>/dev/null || echo "Accenture logo not found"
|
||||
|
||||
# Megabanner
|
||||
curl -sL "https://logo.clearbit.com/megabanner.es" -o megabanner.png 2>/dev/null || echo "Megabanner logo not found"
|
||||
|
||||
# Everis
|
||||
curl -sL "https://logo.clearbit.com/everis.com" -o everis.png 2>/dev/null || echo "Everis logo not found"
|
||||
|
||||
# Indra
|
||||
curl -sL "https://logo.clearbit.com/indra.es" -o indra.png 2>/dev/null || echo "Indra logo not found"
|
||||
|
||||
echo "✅ Company logos downloaded"
|
||||
ls -lh
|
||||
@@ -41,10 +41,12 @@ type Personal struct {
|
||||
type Experience struct {
|
||||
Position string `json:"position"`
|
||||
Company string `json:"company"`
|
||||
CompanyLogo string `json:"companyLogo"`
|
||||
Location string `json:"location"`
|
||||
StartDate string `json:"startDate"`
|
||||
EndDate string `json:"endDate"`
|
||||
Current bool `json:"current"`
|
||||
ShortDescription string `json:"shortDescription"`
|
||||
Responsibilities []string `json:"responsibilities"`
|
||||
Technologies []string `json:"technologies"`
|
||||
Highlights []string `json:"highlights"`
|
||||
|
||||
@@ -124,13 +124,39 @@ a:hover {
|
||||
min-height: 11in;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
/* Header - Photo on right, inline with text */
|
||||
.cv-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 2rem;
|
||||
border-bottom: 2px solid var(--text-dark);
|
||||
padding-bottom: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.cv-header-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.cv-header-right {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cv-photo {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
border: 3px solid var(--border-gray);
|
||||
}
|
||||
|
||||
.cv-photo img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.cv-name {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
@@ -170,9 +196,15 @@ a:hover {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
/* Experience */
|
||||
/* Experience - with separators */
|
||||
.experience-item {
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
border-bottom: 1px solid var(--border-gray);
|
||||
}
|
||||
|
||||
.experience-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.experience-header {
|
||||
@@ -180,6 +212,27 @@ a:hover {
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.company-logo img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.experience-title {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.position {
|
||||
@@ -335,6 +388,17 @@ footer {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.cv-header {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cv-photo {
|
||||
order: -1;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.experience-header,
|
||||
.project-header,
|
||||
.education-header {
|
||||
@@ -342,10 +406,91 @@ footer {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.action-bar-content {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
.company-logo {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.no-print {}
|
||||
|
||||
/* Print Styles for Photo */
|
||||
@media print {
|
||||
.cv-photo {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
/* CV Length Toggle */
|
||||
.cv-length-toggle {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.length-btn {
|
||||
padding: 0.4rem 1rem;
|
||||
border: 1px solid var(--border-gray);
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.length-btn:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.length-btn.active {
|
||||
background: var(--accent-blue);
|
||||
color: white;
|
||||
border-color: var(--accent-blue);
|
||||
}
|
||||
|
||||
/* Short CV - Hide detailed content */
|
||||
.cv-short .long-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cv-short .short-desc {
|
||||
display: block;
|
||||
color: var(--text-gray);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
/* Long CV - Hide short descriptions */
|
||||
.cv-long .short-desc,
|
||||
.short-desc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cv-long .long-only {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Ensure lists display correctly in long mode */
|
||||
.cv-long .responsibilities {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.action-bar-content {
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.cv-length-toggle {
|
||||
order: 1;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
@@ -0,0 +1 @@
|
||||
Not Found
|
||||
@@ -0,0 +1 @@
|
||||
Not Found
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg width="150" height="150" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="150" height="150" fill="#e0e0e0"/>
|
||||
<circle cx="75" cy="60" r="25" fill="#999"/>
|
||||
<path d="M 35 110 Q 75 90 115 110" fill="#999"/>
|
||||
<text x="75" y="140" text-anchor="middle" font-size="10" fill="#666">Add your photo</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 314 B |
@@ -1,5 +1,6 @@
|
||||
<!-- CV Content Template - Minimal Design -->
|
||||
<div class="cv-header">
|
||||
<div class="cv-header-left">
|
||||
<div class="cv-header-main">
|
||||
<h1 class="cv-name">{{.CV.Personal.Name}}</h1>
|
||||
<h2 class="cv-title">{{.CV.Personal.Title}}</h2>
|
||||
@@ -12,6 +13,12 @@
|
||||
<div class="contact-item"><a href="{{.CV.Personal.GitHub}}" target="_blank">GitHub</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cv-header-right">
|
||||
<div class="cv-photo">
|
||||
<img src="/static/images/profile/photo.jpg" alt="{{.CV.Personal.Name}}" onerror="this.src='/static/images/profile/placeholder.svg'">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Summary -->
|
||||
<section class="cv-section">
|
||||
@@ -26,6 +33,11 @@
|
||||
{{range .CV.Experience}}
|
||||
<div class="experience-item">
|
||||
<div class="experience-header">
|
||||
{{if .CompanyLogo}}
|
||||
<div class="company-logo">
|
||||
<img src="/static/images/companies/{{.CompanyLogo}}" alt="{{.Company}}" onerror="this.style.display='none'">
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="experience-title">
|
||||
<h4 class="position">{{.Position}}</h4>
|
||||
<div class="company">{{.Company}}, {{.Location}}</div>
|
||||
@@ -35,14 +47,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="responsibilities">
|
||||
{{if .ShortDescription}}
|
||||
<p class="short-desc">{{.ShortDescription}}</p>
|
||||
{{end}}
|
||||
|
||||
<ul class="responsibilities long-only">
|
||||
{{range .Responsibilities}}
|
||||
<li>{{.}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
|
||||
{{if .Technologies}}
|
||||
<div class="technologies">
|
||||
<div class="technologies long-only">
|
||||
{{range $index, $tech := .Technologies}}{{if $index}}, {{end}}{{$tech}}{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -42,6 +42,19 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="cv-length-toggle">
|
||||
<button
|
||||
class="length-btn active"
|
||||
onclick="toggleCVLength('short')">
|
||||
{{if eq .Lang "es"}}Corto{{else}}Short{{end}}
|
||||
</button>
|
||||
<button
|
||||
class="length-btn"
|
||||
onclick="toggleCVLength('long')">
|
||||
{{if eq .Lang "es"}}Largo{{else}}Long{{end}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="export-actions">
|
||||
<button
|
||||
class="export-btn"
|
||||
@@ -68,5 +81,30 @@
|
||||
<p>© {{.CV.Meta.LastUpdated}} {{.CV.Personal.Name}} |
|
||||
{{if eq .Lang "es"}}Última actualización{{else}}Last updated{{end}}: {{.CV.Meta.LastUpdated}}</p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
function toggleCVLength(length) {
|
||||
// Update button states
|
||||
document.querySelectorAll('.length-btn').forEach(btn => {
|
||||
btn.classList.remove('active');
|
||||
});
|
||||
event.target.classList.add('active');
|
||||
|
||||
// Toggle visibility
|
||||
const paper = document.querySelector('.cv-paper');
|
||||
if (length === 'short') {
|
||||
paper.classList.add('cv-short');
|
||||
paper.classList.remove('cv-long');
|
||||
} else {
|
||||
paper.classList.add('cv-long');
|
||||
paper.classList.remove('cv-short');
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize with short version
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.querySelector('.cv-paper').classList.add('cv-short');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||