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",
|
"React",
|
||||||
"Node.js",
|
"Node.js",
|
||||||
"API Integration"
|
"API Integration"
|
||||||
]
|
],
|
||||||
|
"companyLogo": "olympic-broadcasting.png",
|
||||||
|
"shortDescription": "SAP CDC solutions for international broadcasting events. Custom implementations and technical guidance."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": "Senior Technical Consultant",
|
"position": "Senior Technical Consultant",
|
||||||
@@ -64,7 +66,9 @@
|
|||||||
"highlights": [
|
"highlights": [
|
||||||
"Successfully deployed authentication system for all AENA airports in Spain",
|
"Successfully deployed authentication system for all AENA airports in Spain",
|
||||||
"Managed identity flows for millions of users across web and mobile platforms"
|
"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",
|
"position": "Senior Technical Consultant",
|
||||||
@@ -87,7 +91,9 @@
|
|||||||
"JavaScript",
|
"JavaScript",
|
||||||
"Cloud Platforms",
|
"Cloud Platforms",
|
||||||
"Technical Documentation"
|
"Technical Documentation"
|
||||||
]
|
],
|
||||||
|
"companyLogo": "sap.png",
|
||||||
|
"shortDescription": "SAP Customer Data Cloud technical consulting, troubleshooting, and stakeholder education on GDPR compliance."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": "Junior Technical Consultant",
|
"position": "Junior Technical Consultant",
|
||||||
@@ -109,7 +115,9 @@
|
|||||||
"JavaScript",
|
"JavaScript",
|
||||||
"Customer Support",
|
"Customer Support",
|
||||||
"System Monitoring"
|
"System Monitoring"
|
||||||
]
|
],
|
||||||
|
"companyLogo": "gigya.png",
|
||||||
|
"shortDescription": "Technical support and problem-solving for Gigya platform. System monitoring and training program development."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": "Fullstack Developer",
|
"position": "Fullstack Developer",
|
||||||
@@ -130,7 +138,9 @@
|
|||||||
"Video Processing",
|
"Video Processing",
|
||||||
"Database Design",
|
"Database Design",
|
||||||
"PostgreSQL"
|
"PostgreSQL"
|
||||||
]
|
],
|
||||||
|
"companyLogo": "megabanner.png",
|
||||||
|
"shortDescription": "Full-stack development with video system integration for advertisement inclusion in gas station networks."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": "Fullstack Developer",
|
"position": "Fullstack Developer",
|
||||||
@@ -152,7 +162,9 @@
|
|||||||
"API Design",
|
"API Design",
|
||||||
"CI/CD",
|
"CI/CD",
|
||||||
"DevOps"
|
"DevOps"
|
||||||
]
|
],
|
||||||
|
"companyLogo": "everis.png",
|
||||||
|
"shortDescription": "API design and automated deployment pipelines. Software testing and scalability implementation."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": "FullStack Developer",
|
"position": "FullStack Developer",
|
||||||
@@ -170,7 +182,9 @@
|
|||||||
"JavaScript",
|
"JavaScript",
|
||||||
"Redux",
|
"Redux",
|
||||||
"Webpack"
|
"Webpack"
|
||||||
]
|
],
|
||||||
|
"companyLogo": "everis.png",
|
||||||
|
"shortDescription": "React application development for multiple clients."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": "Fullstack Developer",
|
"position": "Fullstack Developer",
|
||||||
@@ -187,7 +201,9 @@
|
|||||||
"Java",
|
"Java",
|
||||||
"JavaScript",
|
"JavaScript",
|
||||||
"Web Development"
|
"Web Development"
|
||||||
]
|
],
|
||||||
|
"companyLogo": "indra.png",
|
||||||
|
"shortDescription": "Project management and customer feedback collection across development stages."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": "Technical Director / Programmer",
|
"position": "Technical Director / Programmer",
|
||||||
@@ -212,7 +228,8 @@
|
|||||||
"highlights": [
|
"highlights": [
|
||||||
"Reduced production times by 75% through optimized pipelines",
|
"Reduced production times by 75% through optimized pipelines",
|
||||||
"Successfully managed technical team and product development"
|
"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)",
|
"position": "Programmer Analyst (Freelance)",
|
||||||
@@ -230,7 +247,8 @@
|
|||||||
"PHP",
|
"PHP",
|
||||||
"MySQL",
|
"MySQL",
|
||||||
"JavaScript"
|
"JavaScript"
|
||||||
]
|
],
|
||||||
|
"shortDescription": "WordPress and PHP website development as freelance programmer."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": "Analyst Programmer / Expert Technician",
|
"position": "Analyst Programmer / Expert Technician",
|
||||||
@@ -248,7 +266,8 @@
|
|||||||
"Java",
|
"Java",
|
||||||
"System Configuration",
|
"System Configuration",
|
||||||
"Technical Support"
|
"Technical Support"
|
||||||
]
|
],
|
||||||
|
"shortDescription": "Software and hardware configuration, technical problem-solving, and team mentoring."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": "Senior Programmer",
|
"position": "Senior Programmer",
|
||||||
@@ -266,7 +285,8 @@
|
|||||||
"Java",
|
"Java",
|
||||||
"Search Engine Technology",
|
"Search Engine Technology",
|
||||||
"European R&D Projects"
|
"European R&D Projects"
|
||||||
]
|
],
|
||||||
|
"shortDescription": "European R&D project for revolutionary search engine development."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": "Junior Programmer",
|
"position": "Junior Programmer",
|
||||||
@@ -285,7 +305,8 @@
|
|||||||
"Java Applets",
|
"Java Applets",
|
||||||
"Data Visualization",
|
"Data Visualization",
|
||||||
"Chart Generation"
|
"Chart Generation"
|
||||||
]
|
],
|
||||||
|
"shortDescription": "JAVA development specialized in data chart generation and applet development."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"education": [
|
"education": [
|
||||||
|
|||||||
@@ -35,7 +35,9 @@
|
|||||||
"React",
|
"React",
|
||||||
"Node.js",
|
"Node.js",
|
||||||
"Integración de APIs"
|
"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",
|
"position": "Consultor Técnico Senior",
|
||||||
@@ -64,7 +66,9 @@
|
|||||||
"highlights": [
|
"highlights": [
|
||||||
"Despliegue exitoso del sistema de autenticación para todos los aeropuertos AENA en España",
|
"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"
|
"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",
|
"position": "Consultor Técnico Senior",
|
||||||
@@ -87,7 +91,9 @@
|
|||||||
"JavaScript",
|
"JavaScript",
|
||||||
"Plataformas Cloud",
|
"Plataformas Cloud",
|
||||||
"Documentación Técnica"
|
"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",
|
"position": "Consultor Técnico Junior",
|
||||||
@@ -109,7 +115,9 @@
|
|||||||
"JavaScript",
|
"JavaScript",
|
||||||
"Soporte al Cliente",
|
"Soporte al Cliente",
|
||||||
"Monitoreo de Sistemas"
|
"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",
|
"position": "Desarrollador Fullstack",
|
||||||
@@ -130,7 +138,9 @@
|
|||||||
"Procesamiento de Video",
|
"Procesamiento de Video",
|
||||||
"Diseño de Bases de Datos",
|
"Diseño de Bases de Datos",
|
||||||
"PostgreSQL"
|
"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",
|
"position": "Desarrollador Fullstack",
|
||||||
@@ -152,7 +162,9 @@
|
|||||||
"Diseño de APIs",
|
"Diseño de APIs",
|
||||||
"CI/CD",
|
"CI/CD",
|
||||||
"DevOps"
|
"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",
|
"position": "Desarrollador FullStack",
|
||||||
@@ -170,7 +182,9 @@
|
|||||||
"JavaScript",
|
"JavaScript",
|
||||||
"Redux",
|
"Redux",
|
||||||
"Webpack"
|
"Webpack"
|
||||||
]
|
],
|
||||||
|
"companyLogo": "everis.png",
|
||||||
|
"shortDescription": "Desarrollo de aplicaciones React para múltiples clientes."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": "Desarrollador Fullstack",
|
"position": "Desarrollador Fullstack",
|
||||||
@@ -187,7 +201,9 @@
|
|||||||
"Java",
|
"Java",
|
||||||
"JavaScript",
|
"JavaScript",
|
||||||
"Desarrollo Web"
|
"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",
|
"position": "Director Técnico / Programador",
|
||||||
@@ -212,7 +228,8 @@
|
|||||||
"highlights": [
|
"highlights": [
|
||||||
"Reducción del 75% en tiempos de producción mediante pipelines optimizados",
|
"Reducción del 75% en tiempos de producción mediante pipelines optimizados",
|
||||||
"Gestión exitosa de equipo técnico y desarrollo de productos"
|
"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)",
|
"position": "Analista Programador (Freelance)",
|
||||||
@@ -230,7 +247,8 @@
|
|||||||
"PHP",
|
"PHP",
|
||||||
"MySQL",
|
"MySQL",
|
||||||
"JavaScript"
|
"JavaScript"
|
||||||
]
|
],
|
||||||
|
"shortDescription": "Desarrollo de sitios web WordPress y PHP como programador freelance."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": "Analista Programador / Técnico Experto",
|
"position": "Analista Programador / Técnico Experto",
|
||||||
@@ -248,7 +266,8 @@
|
|||||||
"Java",
|
"Java",
|
||||||
"Configuración de Sistemas",
|
"Configuración de Sistemas",
|
||||||
"Soporte Técnico"
|
"Soporte Técnico"
|
||||||
]
|
],
|
||||||
|
"shortDescription": "Configuración de software y hardware, resolución de problemas técnicos y mentoría de equipos."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": "Programador Senior",
|
"position": "Programador Senior",
|
||||||
@@ -266,7 +285,8 @@
|
|||||||
"Java",
|
"Java",
|
||||||
"Tecnología de Motores de Búsqueda",
|
"Tecnología de Motores de Búsqueda",
|
||||||
"Proyectos Europeos I+D"
|
"Proyectos Europeos I+D"
|
||||||
]
|
],
|
||||||
|
"shortDescription": "Proyecto europeo I+D para desarrollo de motor de búsqueda revolucionario."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": "Programador Junior",
|
"position": "Programador Junior",
|
||||||
@@ -285,7 +305,8 @@
|
|||||||
"Applets Java",
|
"Applets Java",
|
||||||
"Visualización de Datos",
|
"Visualización de Datos",
|
||||||
"Generación de Gráficos"
|
"Generación de Gráficos"
|
||||||
]
|
],
|
||||||
|
"shortDescription": "Desarrollo JAVA especializado en generación de gráficos de datos y desarrollo de applets."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"education": [
|
"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 {
|
type Experience struct {
|
||||||
Position string `json:"position"`
|
Position string `json:"position"`
|
||||||
Company string `json:"company"`
|
Company string `json:"company"`
|
||||||
|
CompanyLogo string `json:"companyLogo"`
|
||||||
Location string `json:"location"`
|
Location string `json:"location"`
|
||||||
StartDate string `json:"startDate"`
|
StartDate string `json:"startDate"`
|
||||||
EndDate string `json:"endDate"`
|
EndDate string `json:"endDate"`
|
||||||
Current bool `json:"current"`
|
Current bool `json:"current"`
|
||||||
|
ShortDescription string `json:"shortDescription"`
|
||||||
Responsibilities []string `json:"responsibilities"`
|
Responsibilities []string `json:"responsibilities"`
|
||||||
Technologies []string `json:"technologies"`
|
Technologies []string `json:"technologies"`
|
||||||
Highlights []string `json:"highlights"`
|
Highlights []string `json:"highlights"`
|
||||||
|
|||||||
@@ -124,13 +124,39 @@ a:hover {
|
|||||||
min-height: 11in;
|
min-height: 11in;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Header */
|
/* Header - Photo on right, inline with text */
|
||||||
.cv-header {
|
.cv-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 2rem;
|
||||||
border-bottom: 2px solid var(--text-dark);
|
border-bottom: 2px solid var(--text-dark);
|
||||||
padding-bottom: 1.5rem;
|
padding-bottom: 1.5rem;
|
||||||
margin-bottom: 2rem;
|
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 {
|
.cv-name {
|
||||||
font-size: 2.5rem;
|
font-size: 2.5rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -170,9 +196,15 @@ a:hover {
|
|||||||
text-align: justify;
|
text-align: justify;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Experience */
|
/* Experience - with separators */
|
||||||
.experience-item {
|
.experience-item {
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
border-bottom: 1px solid var(--border-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.experience-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.experience-header {
|
.experience-header {
|
||||||
@@ -180,6 +212,27 @@ a:hover {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
gap: 1rem;
|
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 {
|
.position {
|
||||||
@@ -335,6 +388,17 @@ footer {
|
|||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cv-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-photo {
|
||||||
|
order: -1;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.experience-header,
|
.experience-header,
|
||||||
.project-header,
|
.project-header,
|
||||||
.education-header {
|
.education-header {
|
||||||
@@ -342,10 +406,91 @@ footer {
|
|||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-bar-content {
|
.company-logo {
|
||||||
flex-direction: column;
|
display: none;
|
||||||
gap: 1rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-print {}
|
.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,15 +1,22 @@
|
|||||||
<!-- CV Content Template - Minimal Design -->
|
<!-- CV Content Template - Minimal Design -->
|
||||||
<div class="cv-header">
|
<div class="cv-header">
|
||||||
<div class="cv-header-main">
|
<div class="cv-header-left">
|
||||||
<h1 class="cv-name">{{.CV.Personal.Name}}</h1>
|
<div class="cv-header-main">
|
||||||
<h2 class="cv-title">{{.CV.Personal.Title}}</h2>
|
<h1 class="cv-name">{{.CV.Personal.Name}}</h1>
|
||||||
|
<h2 class="cv-title">{{.CV.Personal.Title}}</h2>
|
||||||
|
</div>
|
||||||
|
<div class="cv-contact">
|
||||||
|
<div class="contact-item">{{.CV.Personal.Location}}</div>
|
||||||
|
<div class="contact-item"><a href="mailto:{{.CV.Personal.Email}}">{{.CV.Personal.Email}}</a></div>
|
||||||
|
<div class="contact-item">{{.CV.Personal.Phone}}</div>
|
||||||
|
<div class="contact-item"><a href="{{.CV.Personal.LinkedIn}}" target="_blank">LinkedIn</a></div>
|
||||||
|
<div class="contact-item"><a href="{{.CV.Personal.GitHub}}" target="_blank">GitHub</a></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="cv-contact">
|
<div class="cv-header-right">
|
||||||
<div class="contact-item">{{.CV.Personal.Location}}</div>
|
<div class="cv-photo">
|
||||||
<div class="contact-item"><a href="mailto:{{.CV.Personal.Email}}">{{.CV.Personal.Email}}</a></div>
|
<img src="/static/images/profile/photo.jpg" alt="{{.CV.Personal.Name}}" onerror="this.src='/static/images/profile/placeholder.svg'">
|
||||||
<div class="contact-item">{{.CV.Personal.Phone}}</div>
|
</div>
|
||||||
<div class="contact-item"><a href="{{.CV.Personal.LinkedIn}}" target="_blank">LinkedIn</a></div>
|
|
||||||
<div class="contact-item"><a href="{{.CV.Personal.GitHub}}" target="_blank">GitHub</a></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -26,6 +33,11 @@
|
|||||||
{{range .CV.Experience}}
|
{{range .CV.Experience}}
|
||||||
<div class="experience-item">
|
<div class="experience-item">
|
||||||
<div class="experience-header">
|
<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">
|
<div class="experience-title">
|
||||||
<h4 class="position">{{.Position}}</h4>
|
<h4 class="position">{{.Position}}</h4>
|
||||||
<div class="company">{{.Company}}, {{.Location}}</div>
|
<div class="company">{{.Company}}, {{.Location}}</div>
|
||||||
@@ -35,14 +47,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="responsibilities">
|
{{if .ShortDescription}}
|
||||||
|
<p class="short-desc">{{.ShortDescription}}</p>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<ul class="responsibilities long-only">
|
||||||
{{range .Responsibilities}}
|
{{range .Responsibilities}}
|
||||||
<li>{{.}}</li>
|
<li>{{.}}</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{{if .Technologies}}
|
{{if .Technologies}}
|
||||||
<div class="technologies">
|
<div class="technologies long-only">
|
||||||
{{range $index, $tech := .Technologies}}{{if $index}}, {{end}}{{$tech}}{{end}}
|
{{range $index, $tech := .Technologies}}{{if $index}}, {{end}}{{$tech}}{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -42,6 +42,19 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</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">
|
<div class="export-actions">
|
||||||
<button
|
<button
|
||||||
class="export-btn"
|
class="export-btn"
|
||||||
@@ -68,5 +81,30 @@
|
|||||||
<p>© {{.CV.Meta.LastUpdated}} {{.CV.Personal.Name}} |
|
<p>© {{.CV.Meta.LastUpdated}} {{.CV.Personal.Name}} |
|
||||||
{{if eq .Lang "es"}}Última actualización{{else}}Last updated{{end}}: {{.CV.Meta.LastUpdated}}</p>
|
{{if eq .Lang "es"}}Última actualización{{else}}Last updated{{end}}: {{.CV.Meta.LastUpdated}}</p>
|
||||||
</footer>
|
</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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||