diff --git a/data/cv-en.json b/data/cv-en.json
index f81e79a..9b9a74e 100644
--- a/data/cv-en.json
+++ b/data/cv-en.json
@@ -309,27 +309,6 @@
],
"skills": {
"technical": [
- {
- "category": "AI-Assisted Development",
- "proficiency": 5,
- "items": [
- "AI Development Workflows (Claude Code, Copilot, GPT-4)",
- "Agent-Based & Spec-Driven Development",
- "Prompt Engineering & AI Integration",
- "Automated Code Generation & Documentation",
- "OpenAI & Anthropic APIs"
- ]
- },
- {
- "category": "SAP Technologies",
- "proficiency": 5,
- "items": [
- "SAP Customer Data Cloud (CDC)",
- "SAP Cloud Platform",
- "SAP S/4HANA",
- "GDPR Compliance & Data Protection"
- ]
- },
{
"category": "Programming Languages",
"proficiency": 4,
@@ -347,6 +326,27 @@
"Assembler"
]
},
+ {
+ "category": "SAP Technologies",
+ "proficiency": 5,
+ "items": [
+ "SAP Customer Data Cloud (CDC)",
+ "SAP Cloud Platform",
+ "SAP S/4HANA",
+ "GDPR Compliance & Data Protection"
+ ]
+ },
+ {
+ "category": "AI-Assisted Development",
+ "proficiency": 5,
+ "items": [
+ "AI Development Workflows (Claude Code, Copilot, GPT-4)",
+ "Agent-Based & Spec-Driven Development",
+ "Prompt Engineering & AI Integration",
+ "Automated Code Generation & Documentation",
+ "OpenAI & Anthropic APIs"
+ ]
+ },
{
"category": "Go Ecosystem",
"proficiency": 5,
@@ -414,20 +414,6 @@
"Enterprise Application Servers (Tomcat, JBoss, WebLogic)"
]
},
- {
- "category": "Databases",
- "proficiency": 4,
- "sidebar": "right",
- "items": [
- "PostgreSQL",
- "MySQL",
- "SQLite",
- "Oracle",
- "MongoDB (NoSQL)",
- "SQL Knowledge",
- "Database Design & Optimization"
- ]
- },
{
"category": "Infrastructure & Servers",
"proficiency": 5,
@@ -452,6 +438,20 @@
"Process Automation & Scripting"
]
},
+ {
+ "category": "Databases",
+ "proficiency": 4,
+ "sidebar": "right",
+ "items": [
+ "PostgreSQL",
+ "MySQL",
+ "SQLite",
+ "Oracle",
+ "MongoDB (NoSQL)",
+ "SQL Knowledge",
+ "Database Design & Optimization"
+ ]
+ },
{
"category": "Team Management",
"proficiency": 4,
@@ -763,8 +763,8 @@
"title": "La Porra.club - Football Prediction Platform",
"url": "https://laporra.club",
"projectLogo": "laporra.png",
+ "gitRepoUrl": "/Users/txeo/laporra",
"location": "Online",
- "startDate": "2024-06",
"current": true,
"technologies": ["Node.js", "Hono", "HTMX", "Panini Templates", "Server-Side Rendering"],
"shortDescription": "Private invitation-only platform for friends to predict football competition results. Features gamification with digital rewards and competitive scoring system.",
@@ -799,14 +799,15 @@
"projectLogo": "",
"location": "Various",
"startDate": "2015",
+ "endDate": "2016",
"current": false,
"technologies": ["JavaScript", "React", "Node.js", "PHP", "WordPress", "Web Development"],
"shortDescription": "Collection of client projects and websites including Lidering, Jorpack, Delivery Bikes BCN, and Mobbeel where I contributed to development, implementation, and technical solutions across various industries.",
"responsibilities": [
- "
Lidering (via Twentic): Real estate and property management platform development
",
- "
Jorpack (via Twentic): Industrial packaging solutions and corporate website
",
- "
",
- "Mobbeel: Biometric authentication and identity verification solutions website
"
+ "
Lidering (via Twentic)
2015: Developed and implemented comprehensive real estate and property management platform with advanced search functionality, property listings, and client management features
",
+ "
Jorpack (via Twentic)
2015: Created corporate website and e-commerce solution for industrial packaging company, featuring product catalog, custom quote system, and business process integration
",
+ "
Delivery Bikes BCN 2016: Built web platform for bicycle delivery service in Barcelona, including route optimization, real-time tracking, and customer booking system
",
+ "Mobbeel 2015: Designed and developed corporate website for biometric authentication and identity verification solutions provider, showcasing security products and enterprise services
"
]
}
],
@@ -862,11 +863,11 @@
}
],
"other": {
- "driverLicense": "Type B"
+ "driverLicense": "Type B"
},
"meta": {
- "version": "2024",
- "lastUpdated": "2024-10-18",
+ "version": "2025-11-09",
+ "lastUpdated": "2025-11-08",
"format": "JSON Resume Extended",
"language": "en"
}
diff --git a/data/cv-es.json b/data/cv-es.json
index 662a0e8..5b18dca 100644
--- a/data/cv-es.json
+++ b/data/cv-es.json
@@ -309,27 +309,6 @@
],
"skills": {
"technical": [
- {
- "category": "Desarrollo Asistido por IA",
- "proficiency": 5,
- "items": [
- "Flujos de Desarrollo con IA (Claude Code, Copilot, GPT-4)",
- "Desarrollo Basado en Agentes y Especificaciones",
- "Ingeniería de Prompts e Integración de IA",
- "Generación Automática de Código y Documentación",
- "APIs OpenAI y Anthropic"
- ]
- },
- {
- "category": "Tecnologías SAP",
- "proficiency": 5,
- "items": [
- "SAP Customer Data Cloud (CDC)",
- "SAP Cloud Platform",
- "SAP S/4HANA",
- "Cumplimiento GDPR y Protección de Datos"
- ]
- },
{
"category": "Lenguajes de Programación",
"proficiency": 4,
@@ -347,6 +326,27 @@
"Assembler"
]
},
+ {
+ "category": "Tecnologías SAP",
+ "proficiency": 5,
+ "items": [
+ "SAP Customer Data Cloud (CDC)",
+ "SAP Cloud Platform",
+ "SAP S/4HANA",
+ "Cumplimiento GDPR y Protección de Datos"
+ ]
+ },
+ {
+ "category": "Desarrollo Asistido por IA",
+ "proficiency": 5,
+ "items": [
+ "Flujos de Desarrollo con IA (Claude Code, Copilot, GPT-4)",
+ "Desarrollo Basado en Agentes y Especificaciones",
+ "Ingeniería de Prompts e Integración de IA",
+ "Generación Automática de Código y Documentación",
+ "APIs OpenAI y Anthropic"
+ ]
+ },
{
"category": "Ecosistema Go",
"proficiency": 5,
@@ -414,20 +414,6 @@
"Servidores de Aplicaciones Enterprise (Tomcat, JBoss, WebLogic)"
]
},
- {
- "category": "Bases de Datos",
- "proficiency": 4,
- "sidebar": "right",
- "items": [
- "PostgreSQL",
- "MySQL",
- "SQLite",
- "Oracle",
- "MongoDB (NoSQL)",
- "Dominio de SQL",
- "Diseño y Optimización de Bases de Datos"
- ]
- },
{
"category": "Infraestructura y Servidores",
"proficiency": 5,
@@ -452,6 +438,20 @@
"Automatización de Procesos y Scripting"
]
},
+ {
+ "category": "Bases de Datos",
+ "proficiency": 4,
+ "sidebar": "right",
+ "items": [
+ "PostgreSQL",
+ "MySQL",
+ "SQLite",
+ "Oracle",
+ "MongoDB (NoSQL)",
+ "Dominio de SQL",
+ "Diseño y Optimización de Bases de Datos"
+ ]
+ },
{
"category": "Gestión de Equipos",
"proficiency": 4,
@@ -768,8 +768,8 @@
"title": "La Porra.club - Plataforma de Predicción de Fútbol",
"url": "https://laporra.club",
"projectLogo": "laporra.png",
+ "gitRepoUrl": "/Users/txeo/laporra",
"location": "Online",
- "startDate": "2024-06",
"current": true,
"technologies": ["Node.js", "Hono", "HTMX", "Plantillas Panini", "Renderizado del Lado del Servidor"],
"shortDescription": "Plataforma privada de acceso por invitación para amigos para predecir resultados de competiciones de fútbol. Incluye gamificación con recompensas digitales y sistema de puntuación competitivo.",
@@ -804,14 +804,15 @@
"projectLogo": "",
"location": "Varios",
"startDate": "2015",
+ "endDate": "2016",
"current": false,
"technologies": ["JavaScript", "React", "Node.js", "PHP", "WordPress", "Desarrollo Web"],
"shortDescription": "Colección de proyectos de clientes y sitios web incluyendo Lidering, Jorpack, Delivery Bikes BCN y Mobbeel donde contribuí al desarrollo, implementación y soluciones técnicas en diversas industrias.",
"responsibilities": [
- "
Lidering (a través de Twentic): Desarrollo de plataforma de gestión inmobiliaria y propiedades
",
- "
Jorpack (a través de Twentic): Soluciones de embalaje industrial y sitio web corporativo
",
- "
",
- "Mobbeel: Sitio web de soluciones de autenticación biométrica y verificación de identidad
"
+ "
Lidering (a través de Twentic)
2015: Desarrollé e implementé plataforma integral de gestión inmobiliaria y propiedades con funcionalidad avanzada de búsqueda, listado de propiedades y gestión de clientes
",
+ "
Jorpack (a través de Twentic)
2015: Creé sitio web corporativo y solución e-commerce para empresa de embalaje industrial, con catálogo de productos, sistema de presupuestos personalizados e integración de procesos de negocio
",
+ "
Delivery Bikes BCN 2016: Construí plataforma web para servicio de entrega en bicicleta en Barcelona, incluyendo optimización de rutas, seguimiento en tiempo real y sistema de reservas para clientes
",
+ "Mobbeel 2015: Diseñé y desarrollé sitio web corporativo para proveedor de soluciones de autenticación biométrica y verificación de identidad, mostrando productos de seguridad y servicios empresariales
"
]
}
],
@@ -867,11 +868,11 @@
}
],
"other": {
- "driverLicense": "Tipo B"
+ "driverLicense": "Tipo B"
},
"meta": {
- "version": "2024",
- "lastUpdated": "2024-10-18",
+ "version": "2025-11-09",
+ "lastUpdated": "2025-11-08",
"format": "JSON Resume Extended",
"language": "es"
}
diff --git a/internal/handlers/cv.go b/internal/handlers/cv.go
index 1a3eae2..f2591c4 100644
--- a/internal/handlers/cv.go
+++ b/internal/handlers/cv.go
@@ -4,6 +4,9 @@ import (
"fmt"
"log"
"net/http"
+ "os"
+ "os/exec"
+ "strings"
"time"
"github.com/juanatsap/cv-site/internal/models"
@@ -58,6 +61,11 @@ func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) {
)
}
+ // Process projects for dynamic dates
+ for i := range cv.Projects {
+ processProjectDates(&cv.Projects[i], lang)
+ }
+
// Split skills between left and right sidebars
skillsLeft, skillsRight := splitSkills(cv.Skills.Technical)
@@ -122,6 +130,11 @@ func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request) {
)
}
+ // Process projects for dynamic dates
+ for i := range cv.Projects {
+ processProjectDates(&cv.Projects[i], lang)
+ }
+
// Split skills between left and right sidebars
skillsLeft, skillsRight := splitSkills(cv.Skills.Technical)
@@ -325,3 +338,77 @@ func calculateDuration(startDate, endDate string, current bool, lang string) str
return result
}
+
+// processProjectDates calculates dynamic dates for projects
+// If a project has a gitRepoUrl, it fetches the first commit date
+// For current projects, it sets the current system date
+func processProjectDates(project *models.Project, lang string) {
+ now := time.Now()
+
+ // Set dynamic current date for ongoing projects
+ if project.Current {
+ if lang == "es" {
+ project.DynamicDate = "Presente"
+ } else {
+ project.DynamicDate = "Present"
+ }
+ }
+
+ // If project has a git repository URL, fetch the first commit date
+ if project.GitRepoUrl != "" {
+ commitDate := getGitRepoFirstCommitDate(project.GitRepoUrl)
+ if commitDate != "" {
+ project.ComputedStartDate = commitDate
+ }
+ }
+
+ // If no computed date and no static date, use current date for current projects
+ if project.ComputedStartDate == "" && project.StartDate == "" && project.Current {
+ project.ComputedStartDate = now.Format("2006-01")
+ }
+
+ // If we have a computed date but no static date, use the computed one
+ if project.ComputedStartDate != "" && project.StartDate == "" {
+ project.StartDate = project.ComputedStartDate
+ }
+}
+
+// getGitRepoFirstCommitDate fetches the first commit date from a git repository
+// Supports local git repository paths
+func getGitRepoFirstCommitDate(repoPath string) string {
+ // Check if the path exists and is a directory
+ info, err := os.Stat(repoPath)
+ if err != nil || !info.IsDir() {
+ return ""
+ }
+
+ // Execute git command to get the first commit date
+ // Format: YYYY-MM (to match StartDate format)
+ cmd := exec.Command("git", "-C", repoPath, "log", "--reverse", "--format=%ci", "--date=format:%Y-%m")
+ cmd.Dir = repoPath
+
+ output, err := cmd.Output()
+ if err != nil {
+ return ""
+ }
+
+ // Parse the output to get the first commit date
+ lines := strings.Split(strings.TrimSpace(string(output)), "\n")
+ if len(lines) == 0 {
+ return ""
+ }
+
+ // Extract YYYY-MM from the first commit timestamp
+ // Format of output: "2024-06-15 10:30:45 +0200"
+ firstLine := lines[0]
+ parts := strings.Fields(firstLine)
+ if len(parts) > 0 {
+ datePart := parts[0] // "2024-06-15"
+ dateParts := strings.Split(datePart, "-")
+ if len(dateParts) >= 2 {
+ return dateParts[0] + "-" + dateParts[1] // "2024-06"
+ }
+ }
+
+ return ""
+}
diff --git a/internal/models/cv.go b/internal/models/cv.go
index f465d0b..e945e74 100644
--- a/internal/models/cv.go
+++ b/internal/models/cv.go
@@ -102,14 +102,19 @@ type Language struct {
type Project struct {
Title string `json:"title"`
URL string `json:"url"`
- ProjectLogo string `json:"projectLogo,omitempty"` // Optional logo filename
+ ProjectLogo string `json:"projectLogo,omitempty"` // Optional logo filename
+ GitRepoUrl string `json:"gitRepoUrl,omitempty"` // Optional git repository URL for dynamic dates
Location string `json:"location"`
- StartDate string `json:"startDate"`
+ StartDate string `json:"startDate,omitempty"` // Optional static start date
Current bool `json:"current"`
MaintainedBy string `json:"maintainedBy,omitempty"` // Optional maintainer name (e.g., "SAP")
Technologies []string `json:"technologies"`
ShortDescription string `json:"shortDescription"`
Responsibilities []string `json:"responsibilities"`
+
+ // Computed fields (not stored in JSON)
+ ComputedStartDate string `json:"-"` // Dynamically calculated from git repo or system
+ DynamicDate string `json:"-"` // Current date for ongoing projects
}
type Award struct {
diff --git a/static/css/main.css b/static/css/main.css
index 6bc4c5a..73bb25b 100644
--- a/static/css/main.css
+++ b/static/css/main.css
@@ -850,10 +850,11 @@ iconify-icon {
}
.language-item {
- font-size: 0.9rem;
+ font-size: 1.1rem!important;
color: var(--text-dark);
- margin-bottom: 0.3rem;
- line-height: 1.4;
+ margin-bottom: 0.3rem!important;
+ line-height: 1.4!important;
+ margin-left: 2rem!important;
}
.language-item small {
@@ -1073,12 +1074,14 @@ iconify-icon {
/* References */
.reference-item {
- margin-bottom: 0.6rem;
- line-height: 1.4;
+ margin-bottom: 0!important;
+ line-height: 1.4!important;
+ margin-left: 2rem!important;
+ font-size: 1.1rem!important;
}
.reference-item a {
- font-size: 0.9em;
+
color: var(--accent-blue);
text-decoration: none;
word-break: break-word;
@@ -1509,7 +1512,10 @@ a:focus {
.language-item,
.reference-item,
.other-content {
- margin-bottom: 1em;
+ margin-bottom: 0!important;
+ line-height: 1.4!important;
+ margin-left: 2rem!important;
+ font-size: 1.1rem!important;
}
/* Award item with logo */
@@ -1526,11 +1532,68 @@ a:focus {
/* Keep border on all award items including last one */
+/* ========================================
+ HIDE LOGOS, ICONS, AND BADGES MODE
+ ======================================== */
+
/* Adjust gap when logos are hidden */
.cv-paper:not(.show-logos) .award-item {
gap: 0;
}
+/* Hide all logos when .show-logos is not present */
+.cv-paper:not(.show-logos) .company-logo,
+.cv-paper:not(.show-logos) .award-logo,
+.cv-paper:not(.show-logos) .project-icon,
+.cv-paper:not(.show-logos) .course-icon {
+ display: none !important;
+}
+
+/* Hide logos inside responsibilities (Drolosoft sub-clients, Third Party projects) */
+.cv-paper:not(.show-logos) .responsibilities li img,
+.cv-paper:not(.show-logos) .responsibilities li iconify-icon.default-company-icon {
+ display: none !important;
+}
+
+/* Adjust layout for responsibilities without logos */
+.cv-paper:not(.show-logos) .responsibilities li:has(img),
+.cv-paper:not(.show-logos) .responsibilities li:has(iconify-icon) {
+ display: block !important;
+ grid-template-columns: none !important;
+ padding-left: 1.2rem !important;
+}
+
+/* Restore bullet points for responsibilities without logos */
+.cv-paper:not(.show-logos) .responsibilities li:has(img):before,
+.cv-paper:not(.show-logos) .responsibilities li:has(iconify-icon):before {
+ display: block !important;
+}
+
+/* Hide all section icons */
+.cv-paper:not(.show-logos) .section-icon {
+ display: none !important;
+}
+
+/* Hide all badges (Current, Expired, Maintained By) */
+.cv-paper:not(.show-logos) .current-badge,
+.cv-paper:not(.show-logos) .expired-badge,
+.cv-paper:not(.show-logos) .maintained-badge {
+ display: none !important;
+}
+
+/* Adjust experience items layout when logos are hidden */
+.cv-paper:not(.show-logos) .experience-item {
+ display: block !important;
+}
+
+/* Adjust project and course items layout when icons are hidden */
+.cv-paper:not(.show-logos) .project-item,
+.cv-paper:not(.show-logos) .course-item,
+.cv-paper:not(.show-logos) .award-item {
+ display: block !important;
+ gap: 0 !important;
+}
+
.award-logo {
flex-shrink: 0;
display: block;
diff --git a/templates/cv-content.html b/templates/cv-content.html
index c971b4a..98546e0 100644
--- a/templates/cv-content.html
+++ b/templates/cv-content.html
@@ -197,7 +197,7 @@
- {{if eq .Lang "es"}}Proyectos Personales{{else}}Personal Projects{{end}}
+ {{if eq .Lang "es"}}Proyectos Personales / Freelance{{else}}Personal / Freelance Projects{{end}}
{{range .CV.Projects}}
@@ -227,7 +227,16 @@
{{if eq $.Lang "es"}}MANTENIDO POR{{else}}MAINTAINED BY{{end}} {{.MaintainedBy}}
{{end}}
- {{.StartDate}} {{if .Current}}/ {{if eq $.Lang "es"}}presente{{else}}now{{end}}{{end}}
+
+ {{if .StartDate}}{{.StartDate}}{{end}}
+ {{if .Current}}
+ {{if .DynamicDate}}
+ / {{.DynamicDate}}
+ {{else}}
+ / {{if eq $.Lang "es"}}presente{{else}}now{{end}}
+ {{end}}
+ {{end}}
+
-
({{.Location}})
diff --git a/templates/index.html b/templates/index.html
index d45a3fe..4e9e834 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -205,6 +205,10 @@
{{if eq .Lang "es"}}Premios y Reconocimientos{{else}}Awards{{end}}
+