templating phase i
This commit is contained in:
@@ -53,6 +53,21 @@ func (m *Manager) loadTemplates() error {
|
||||
"safeHTML": func(s string) template.HTML {
|
||||
return template.HTML(s)
|
||||
},
|
||||
// dict creates a map from key-value pairs for passing to sub-templates
|
||||
"dict": func(values ...interface{}) (map[string]interface{}, error) {
|
||||
if len(values)%2 != 0 {
|
||||
return nil, fmt.Errorf("dict requires even number of arguments")
|
||||
}
|
||||
dict := make(map[string]interface{}, len(values)/2)
|
||||
for i := 0; i < len(values); i += 2 {
|
||||
key, ok := values[i].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("dict keys must be strings")
|
||||
}
|
||||
dict[key] = values[i+1]
|
||||
}
|
||||
return dict, nil
|
||||
},
|
||||
}
|
||||
|
||||
// Parse main templates
|
||||
@@ -62,13 +77,23 @@ func (m *Manager) loadTemplates() error {
|
||||
return fmt.Errorf("error parsing templates from %s: %w", pattern, err)
|
||||
}
|
||||
|
||||
// Try to parse partials if they exist
|
||||
partialsPattern := filepath.Join(m.config.PartialsDir, "*.html")
|
||||
partials, _ := filepath.Glob(partialsPattern)
|
||||
if len(partials) > 0 {
|
||||
tmpl, err = tmpl.ParseGlob(partialsPattern)
|
||||
// Parse partials recursively from all subdirectories
|
||||
partialsPattern := filepath.Join(m.config.PartialsDir, "**", "*.html")
|
||||
partialsMatches, _ := filepath.Glob(partialsPattern)
|
||||
|
||||
// Also match direct children
|
||||
partialsDirectPattern := filepath.Join(m.config.PartialsDir, "*.html")
|
||||
directMatches, _ := filepath.Glob(partialsDirectPattern)
|
||||
|
||||
// Combine all matches
|
||||
allPartials := append(partialsMatches, directMatches...)
|
||||
|
||||
if len(allPartials) > 0 {
|
||||
tmpl, err = tmpl.ParseFiles(allPartials...)
|
||||
if err != nil {
|
||||
log.Printf("Warning: error parsing partials: %v", err)
|
||||
} else {
|
||||
log.Printf("✓ Loaded %d partial templates", len(allPartials))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-361
@@ -1,15 +1,6 @@
|
||||
<!-- PAGE 1 -->
|
||||
<div class="cv-page page-1">
|
||||
<!-- Professional Title Badges - Full Width Top Bar -->
|
||||
<div class="cv-title-badges-header">
|
||||
<span class="title-badge">{{if eq .Lang "es"}}CONSULTOR TÉCNICO{{else}}TECHNICAL CONSULTANT{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}INGENIERO FULL-STACK{{else}}FULL-STACK ENGINEER{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}ESPECIALISTA EN AUTENTICACIÓN{{else}}AUTHENTICATION SPECIALIST{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}ARQUITECTO DE SOLUCIONES{{else}}SOLUTION ARCHITECT{{end}}</span>
|
||||
</div>
|
||||
{{template "title-badges" .}}
|
||||
|
||||
<!-- Page 1 Content Grid: Left Sidebar + Main Content -->
|
||||
<div class="page-content">
|
||||
@@ -21,14 +12,14 @@
|
||||
<iconify-icon icon="mdi:chevron-down" width="20" height="20" class="chevron"></iconify-icon>
|
||||
</div>
|
||||
<div class="sidebar-accordion-content">
|
||||
{{range $index, $category := .SkillsLeft}}
|
||||
{{range .SkillsLeft}}
|
||||
<section class="sidebar-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="sidebar-title">{{$category.Category}}</h3>
|
||||
<h3 class="sidebar-title">{{.Category}}</h3>
|
||||
</summary>
|
||||
<div class="sidebar-content">
|
||||
{{range $category.Items}}<div class="skill-item">{{.}}</div>{{end}}
|
||||
{{range .Items}}<div class="skill-item">{{.}}</div>{{end}}
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
@@ -38,322 +29,28 @@
|
||||
|
||||
<!-- Main Content Area - Page 1 -->
|
||||
<main class="cv-main">
|
||||
<!-- Header with Name and Photo -->
|
||||
<div class="cv-header">
|
||||
<div class="cv-header-content">
|
||||
<div class="cv-header-left">
|
||||
<h1 class="cv-name">Moreno Rubio, Juan Andrés</h1>
|
||||
<p class="years-experience">{{.YearsOfExperience}} {{if eq .Lang "es"}}años de experiencia{{else}}years of experience{{end}}</p>
|
||||
|
||||
<!-- Photo positioned for mobile (centered between name and intro) -->
|
||||
<div class="cv-photo">
|
||||
<img src="/static/images/profile/dni.jpeg" alt="{{.CV.Personal.Name}}" onerror="this.src='/static/images/profile/placeholder.svg'">
|
||||
</div>
|
||||
|
||||
<!-- Intro/Excerpt Text - No section heading, just the text -->
|
||||
<div class="intro-text">{{.CV.Summary}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Education -->
|
||||
<section id="education" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:school" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Formación{{else}}Training{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Education}}
|
||||
<div class="education-item">
|
||||
<strong>{{.Degree}}</strong> ({{.StartDate}}-{{.EndDate}}) {{if eq $.Lang "es"}}obtenido de{{else}}obtained from the{{end}} <strong>{{.Institution}}</strong> ({{.Location}})
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<!-- Skills Summary -->
|
||||
<section id="skills" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:brain" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Competencias{{else}}Skills{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
<p class="summary-text">
|
||||
{{if eq .Lang "es"}}
|
||||
Desarrollador <strong>full-stack</strong> con experiencia en <strong>Go</strong>, <strong>Node.js</strong>, <strong>React</strong> y <strong>HTMX</strong> para <strong>aplicaciones modernas</strong>, además de conocimientos en Java y PHP para proyectos legacy. He trabajado en <strong>unos 20 sitios web</strong> y realizado <strong>consultoría para 35-40 clientes internacionales</strong>, desde e-commerce y plataformas empresariales hasta <strong>sistemas de autenticación</strong> que gestionan <strong>millones de usuarios</strong>. Familiarizado con flujos de trabajo asistidos por <strong>IA</strong> y gestión de infraestructura (<strong>Linux</strong>, <strong>Docker</strong>, <strong>CI/CD</strong>). Me adapto bien tanto al trabajo independiente como colaborativo en equipos internacionales.
|
||||
{{else}}
|
||||
<strong>Full-stack</strong> developer with experience in <strong>Go</strong>, <strong>Node.js</strong>, <strong>React</strong>, and <strong>HTMX</strong> for <strong>modern applications</strong>, plus Java and PHP knowledge for legacy projects. I've worked on <strong>around 20 websites</strong> and provided <strong>consulting for 35-40 international clients</strong>, from e-commerce and enterprise platforms to <strong>authentication systems</strong> managing <strong>millions of users</strong>. Familiar with <strong>AI-assisted development</strong> workflows and infrastructure management (<strong>Linux</strong>, <strong>Docker</strong>, <strong>CI/CD</strong>). I adapt well to both independent work and collaborative teams across different countries.
|
||||
{{end}}
|
||||
</p>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<!-- Experience -->
|
||||
<section id="experience" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:office-building" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Experiencia{{else}}Experience{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
|
||||
{{range .CV.Experience}}
|
||||
<div class="experience-item">
|
||||
<div class="company-logo">
|
||||
{{if .CompanyLogo}}
|
||||
<img src="/static/images/companies/{{.CompanyLogo}}" alt="{{.Company}} logo" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:office-building\' width=\'60\' height=\'60\' class=\'default-company-icon\'></iconify-icon>'">
|
||||
{{else}}
|
||||
<iconify-icon icon="mdi:office-building" width="60" height="60" class="default-company-icon"></iconify-icon>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="experience-content">
|
||||
<strong>{{.Position}}{{if .Company}} - {{if .CompanyURL}}<a href="{{.CompanyURL}}" target="_blank" rel="noopener noreferrer">{{.Company}}</a>{{else}}{{.Company}}{{end}}{{if .Duration}} - <span class="duration-text">{{.Duration}}</span>{{end}}{{end}}</strong>
|
||||
{{if .Current}}<span class="current-badge">{{if eq $.Lang "es"}}ACTUAL{{else}}CURRENT{{end}}</span>{{end}}
|
||||
{{if .Expired}}<span class="expired-badge">{{if eq $.Lang "es"}}EXPIRADO{{else}}EXPIRED{{end}}</span>{{end}}
|
||||
<br>
|
||||
<small>{{.StartDate}} / {{if .Current}}{{if eq $.Lang "es"}}presente{{else}}now{{end}}{{else}}{{.EndDate}}{{end}} - ({{.Location}})</small>
|
||||
|
||||
{{if .ShortDescription}}
|
||||
<p class="experience-desc short-desc">{{.ShortDescription | safeHTML}}</p>
|
||||
{{end}}
|
||||
|
||||
{{if .Responsibilities}}
|
||||
<ul class="responsibilities long-only">
|
||||
{{range .Responsibilities}}
|
||||
<li>{{. | safeHTML}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
{{template "section-header" .}}
|
||||
{{template "section-education" .}}
|
||||
{{template "section-skills-summary" .}}
|
||||
{{template "section-experience" .}}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PAGE 2 -->
|
||||
<div class="cv-page page-2">
|
||||
<!-- Professional Title Badges - Same as Page 1 -->
|
||||
<div class="cv-title-badges-header">
|
||||
<span class="title-badge">{{if eq .Lang "es"}}CONSULTOR TÉCNICO{{else}}TECHNICAL CONSULTANT{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}INGENIERO FULL-STACK{{else}}FULL-STACK ENGINEER{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}ESPECIALISTA EN AUTENTICACIÓN{{else}}AUTHENTICATION SPECIALIST{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}ARQUITECTO DE SOLUCIONES{{else}}SOLUTION ARCHITECT{{end}}</span>
|
||||
</div>
|
||||
{{template "title-badges" .}}
|
||||
|
||||
<!-- Page 2 Content Grid: Main Content + Right Sidebar -->
|
||||
<div class="page-content">
|
||||
<!-- Main Content Area - Page 2 -->
|
||||
<main class="cv-main">
|
||||
<!-- Awards Section -->
|
||||
{{if .CV.Awards}}
|
||||
<section id="awards" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:trophy" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Premios y Reconocimientos{{else}}Awards{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Awards}}
|
||||
<div class="award-item">
|
||||
{{if .AwardLogo}}
|
||||
<div class="award-logo">
|
||||
<img src="/static/images/companies/{{.AwardLogo}}" alt="{{.Title}} logo" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:trophy\' width=\'60\' height=\'60\' class=\'default-award-icon\'></iconify-icon>'">
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="award-content">
|
||||
<strong>{{.Title}}</strong><br>
|
||||
<small>{{.Issuer}} - {{.Date}}</small>
|
||||
|
||||
{{if .ShortDescription}}
|
||||
<p class="award-desc short-desc">{{.ShortDescription | safeHTML}}</p>
|
||||
{{end}}
|
||||
|
||||
{{if .Responsibilities}}
|
||||
<ul class="responsibilities long-only">
|
||||
{{range .Responsibilities}}
|
||||
<li>{{. | safeHTML}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
<!-- Projects Section -->
|
||||
{{if .CV.Projects}}
|
||||
<section id="projects" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:web" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Proyectos Personales / Freelance{{else}}Personal / Freelance Projects{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Projects}}
|
||||
<div class="project-item">
|
||||
{{if .ProjectLogo}}
|
||||
<div class="project-icon">
|
||||
<img src="/static/images/projects/{{.ProjectLogo}}" alt="{{.Title}} logo" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:web\' width=\'80\' height=\'80\' class=\'default-project-icon\'></iconify-icon>'">
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="project-icon">
|
||||
<iconify-icon icon="mdi:web" width="80" height="80" class="default-project-icon"></iconify-icon>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="project-content">
|
||||
<strong>
|
||||
{{if .ProjectName}}
|
||||
{{if .URL}}<a href="{{.URL}}" target="_blank" rel="noopener noreferrer">{{.ProjectName}}</a>{{else}}{{.ProjectName}}{{end}}{{if .ProjectDesc}} - {{.ProjectDesc}}{{end}}
|
||||
{{else}}
|
||||
{{if .URL}}<a href="{{.URL}}" target="_blank" rel="noopener noreferrer">{{.Title}}</a>{{else}}{{.Title}}{{end}}
|
||||
{{end}}
|
||||
</strong>
|
||||
{{if .Current}}<span class="live-badge"><iconify-icon icon="mdi:wifi" width="14" height="14"></iconify-icon>LIVE</span>{{end}}
|
||||
{{if .MaintainedBy}}<span class="maintained-badge">{{if eq $.Lang "es"}}MANTENIDO POR{{else}}MAINTAINED BY{{end}} {{.MaintainedBy}}</span>{{end}}
|
||||
<br>
|
||||
<small>{{if .StartDate}}{{.StartDate}}{{if .Current}}{{if .DynamicDate}} / {{.DynamicDate}}{{else}} / {{if eq $.Lang "es"}}presente{{else}}ahora{{end}}{{end}}{{end}}{{end}} - ({{.Location}})</small>
|
||||
|
||||
{{if .ShortDescription}}
|
||||
<p class="project-desc short-desc">{{.ShortDescription | safeHTML}}</p>
|
||||
{{end}}
|
||||
|
||||
{{if .Responsibilities}}
|
||||
<ul class="responsibilities long-only">
|
||||
{{range .Responsibilities}}
|
||||
<li>{{. | safeHTML}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
|
||||
{{if .Technologies}}
|
||||
<div class="project-technologies long-only">
|
||||
<strong>{{if eq $.Lang "es"}}Tecnologías:{{else}}Technologies:{{end}}</strong>
|
||||
{{range $index, $tech := .Technologies}}{{if $index}}, {{end}}{{$tech}}{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<!-- Link to full portfolio -->
|
||||
<div class="projects-footer">
|
||||
<p>{{if eq .Lang "es"}}Ver todos los proyectos en mi{{else}}See all projects on my{{end}}
|
||||
<a href="{{.CV.Personal.Domestika}}" target="_blank" rel="noopener noreferrer"><strong>{{if eq .Lang "es"}}portfolio de Domestika{{else}}Domestika portfolio{{end}}</strong></a></p>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
<!-- Courses Section -->
|
||||
{{if .CV.Courses}}
|
||||
<section id="courses" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:school" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Cursos Realizados{{else}}Courses{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Courses}}
|
||||
<div class="course-item">
|
||||
{{if .CourseLogo}}
|
||||
<div class="course-icon">
|
||||
<img src="/static/images/courses/{{.CourseLogo}}" alt="{{.Title}} logo" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:school\' width=\'80\' height=\'80\' class=\'default-course-icon\'></iconify-icon>'">
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="course-icon">
|
||||
<iconify-icon icon="mdi:school" width="80" height="80" class="default-course-icon"></iconify-icon>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="course-content">
|
||||
<strong>{{.Title}}</strong><br>
|
||||
<small>{{.Institution}} - {{.Date}} - ({{.Location}})</small>
|
||||
|
||||
{{if .ShortDescription}}
|
||||
<p class="course-desc short-desc">{{.ShortDescription}}</p>
|
||||
{{end}}
|
||||
|
||||
{{if .Responsibilities}}
|
||||
<ul class="responsibilities long-only">
|
||||
{{range .Responsibilities}}
|
||||
<li>{{. | safeHTML}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
<!-- Languages Section -->
|
||||
<section id="languages" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:translate" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Idiomas{{else}}Languages{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Languages}}
|
||||
<div class="language-item">
|
||||
<strong>{{.Language}}:</strong> {{.Proficiency}}{{if .Detail}} {{.Detail}}{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<!-- References Section -->
|
||||
{{if .CV.References}}
|
||||
<section id="references" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:link-variant" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Referencias{{else}}References{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.References}}
|
||||
<div class="reference-item">
|
||||
{{if .TextBefore}}{{.TextBefore}} {{end}}{{if eq .Action "downloadPDF"}}<a href="{{.URL}}" onclick="event.preventDefault(); openPdfModal(); return false;"><strong>{{if .LinkText}}{{.LinkText}}{{else}}{{.Title}}{{end}}</strong></a>{{else}}<a href="{{.URL}}" {{if and (eq .Type "cv") (ne .URL "/?lang=en") (ne .URL "/?lang=es")}}download{{else}}target="_blank" rel="noopener noreferrer"{{end}}><strong>{{if .LinkText}}{{.LinkText}}{{else}}{{.Title}}{{end}}</strong></a>{{end}}{{if .TextAfter}} {{.TextAfter}}{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
<!-- Other Section (Driver's License) -->
|
||||
{{if .CV.Other.DriverLicense}}
|
||||
<section id="other" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:information" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Otros{{else}}Other{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
<div class="other-content">
|
||||
{{if eq .Lang "es"}}Carnet de conducir tipo <strong>{{.CV.Other.DriverLicense}}</strong>{{else}}Driving License type <strong>{{.CV.Other.DriverLicense}}</strong>{{end}}
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
{{template "section-awards" .}}
|
||||
{{template "section-projects" .}}
|
||||
{{template "section-courses" .}}
|
||||
{{template "section-languages" .}}
|
||||
{{template "section-references" .}}
|
||||
{{template "section-other" .}}
|
||||
</main>
|
||||
|
||||
<!-- Right Sidebar - Skills (second half) -->
|
||||
@@ -364,14 +61,14 @@
|
||||
<iconify-icon icon="mdi:chevron-down" width="20" height="20" class="chevron"></iconify-icon>
|
||||
</div>
|
||||
<div class="sidebar-accordion-content">
|
||||
{{range $index, $category := .SkillsRight}}
|
||||
{{range .SkillsRight}}
|
||||
<section class="sidebar-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="sidebar-title">{{$category.Category}}</h3>
|
||||
<h3 class="sidebar-title">{{.Category}}</h3>
|
||||
</summary>
|
||||
<div class="sidebar-content">
|
||||
{{range $category.Items}}<div class="skill-item">{{.}}</div>{{end}}
|
||||
{{range .Items}}<div class="skill-item">{{.}}</div>{{end}}
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
@@ -380,44 +77,5 @@
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<!-- Footer - Only on Page 2 -->
|
||||
<footer class="cv-footer">
|
||||
<ul class="footer-content">
|
||||
<li>
|
||||
<div class="footer-label">linkedin_</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="{{.CV.Personal.LinkedIn}}" target="_blank" rel="noopener noreferrer">{{.CV.Personal.LinkedIn}}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="footer-label">github_</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="{{.CV.Personal.GitHub}}" target="_blank" rel="noopener noreferrer">{{.CV.Personal.GitHub}}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="footer-label">domestika_</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="{{.CV.Personal.Domestika}}" target="_blank" rel="noopener noreferrer">{{.CV.Personal.Domestika}}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="footer-label">email@</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="mailto:{{.CV.Personal.Email}}" target="_blank" rel="noopener noreferrer">{{.CV.Personal.Email}}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="footer-label">phone#</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="tel:+34676875420" target="_blank" rel="noopener noreferrer">+34 676 875 420</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
{{template "cv-footer" .}}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,423 @@
|
||||
<!-- PAGE 1 -->
|
||||
<div class="cv-page page-1">
|
||||
<!-- Professional Title Badges - Full Width Top Bar -->
|
||||
<div class="cv-title-badges-header">
|
||||
<span class="title-badge">{{if eq .Lang "es"}}CONSULTOR TÉCNICO{{else}}TECHNICAL CONSULTANT{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}INGENIERO FULL-STACK{{else}}FULL-STACK ENGINEER{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}ESPECIALISTA EN AUTENTICACIÓN{{else}}AUTHENTICATION SPECIALIST{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}ARQUITECTO DE SOLUCIONES{{else}}SOLUTION ARCHITECT{{end}}</span>
|
||||
</div>
|
||||
|
||||
<!-- Page 1 Content Grid: Left Sidebar + Main Content -->
|
||||
<div class="page-content">
|
||||
<!-- Left Sidebar - Skills (first half) -->
|
||||
<aside class="cv-sidebar cv-sidebar-left">
|
||||
<div class="sidebar-accordion-header" onclick="toggleSidebar(this)">
|
||||
<iconify-icon icon="mdi:brain" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Competencias Técnicas{{else}}Technical Skills{{end}}</span>
|
||||
<iconify-icon icon="mdi:chevron-down" width="20" height="20" class="chevron"></iconify-icon>
|
||||
</div>
|
||||
<div class="sidebar-accordion-content">
|
||||
{{range $index, $category := .SkillsLeft}}
|
||||
<section class="sidebar-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="sidebar-title">{{$category.Category}}</h3>
|
||||
</summary>
|
||||
<div class="sidebar-content">
|
||||
{{range $category.Items}}<div class="skill-item">{{.}}</div>{{end}}
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content Area - Page 1 -->
|
||||
<main class="cv-main">
|
||||
<!-- Header with Name and Photo -->
|
||||
<div class="cv-header">
|
||||
<div class="cv-header-content">
|
||||
<div class="cv-header-left">
|
||||
<h1 class="cv-name">Moreno Rubio, Juan Andrés</h1>
|
||||
<p class="years-experience">{{.YearsOfExperience}} {{if eq .Lang "es"}}años de experiencia{{else}}years of experience{{end}}</p>
|
||||
|
||||
<!-- Photo positioned for mobile (centered between name and intro) -->
|
||||
<div class="cv-photo">
|
||||
<img src="/static/images/profile/dni.jpeg" alt="{{.CV.Personal.Name}}" onerror="this.src='/static/images/profile/placeholder.svg'">
|
||||
</div>
|
||||
|
||||
<!-- Intro/Excerpt Text - No section heading, just the text -->
|
||||
<div class="intro-text">{{.CV.Summary}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Education -->
|
||||
<section id="education" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:school" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Formación{{else}}Training{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Education}}
|
||||
<div class="education-item">
|
||||
<strong>{{.Degree}}</strong> ({{.StartDate}}-{{.EndDate}}) {{if eq $.Lang "es"}}obtenido de{{else}}obtained from the{{end}} <strong>{{.Institution}}</strong> ({{.Location}})
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<!-- Skills Summary -->
|
||||
<section id="skills" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:brain" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Competencias{{else}}Skills{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
<p class="summary-text">
|
||||
{{if eq .Lang "es"}}
|
||||
Desarrollador <strong>full-stack</strong> con experiencia en <strong>Go</strong>, <strong>Node.js</strong>, <strong>React</strong> y <strong>HTMX</strong> para <strong>aplicaciones modernas</strong>, además de conocimientos en Java y PHP para proyectos legacy. He trabajado en <strong>unos 20 sitios web</strong> y realizado <strong>consultoría para 35-40 clientes internacionales</strong>, desde e-commerce y plataformas empresariales hasta <strong>sistemas de autenticación</strong> que gestionan <strong>millones de usuarios</strong>. Familiarizado con flujos de trabajo asistidos por <strong>IA</strong> y gestión de infraestructura (<strong>Linux</strong>, <strong>Docker</strong>, <strong>CI/CD</strong>). Me adapto bien tanto al trabajo independiente como colaborativo en equipos internacionales.
|
||||
{{else}}
|
||||
<strong>Full-stack</strong> developer with experience in <strong>Go</strong>, <strong>Node.js</strong>, <strong>React</strong>, and <strong>HTMX</strong> for <strong>modern applications</strong>, plus Java and PHP knowledge for legacy projects. I've worked on <strong>around 20 websites</strong> and provided <strong>consulting for 35-40 international clients</strong>, from e-commerce and enterprise platforms to <strong>authentication systems</strong> managing <strong>millions of users</strong>. Familiar with <strong>AI-assisted development</strong> workflows and infrastructure management (<strong>Linux</strong>, <strong>Docker</strong>, <strong>CI/CD</strong>). I adapt well to both independent work and collaborative teams across different countries.
|
||||
{{end}}
|
||||
</p>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<!-- Experience -->
|
||||
<section id="experience" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:office-building" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Experiencia{{else}}Experience{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
|
||||
{{range .CV.Experience}}
|
||||
<div class="experience-item">
|
||||
<div class="company-logo">
|
||||
{{if .CompanyLogo}}
|
||||
<img src="/static/images/companies/{{.CompanyLogo}}" alt="{{.Company}} logo" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:office-building\' width=\'60\' height=\'60\' class=\'default-company-icon\'></iconify-icon>'">
|
||||
{{else}}
|
||||
<iconify-icon icon="mdi:office-building" width="60" height="60" class="default-company-icon"></iconify-icon>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="experience-content">
|
||||
<strong>{{.Position}}{{if .Company}} - {{if .CompanyURL}}<a href="{{.CompanyURL}}" target="_blank" rel="noopener noreferrer">{{.Company}}</a>{{else}}{{.Company}}{{end}}{{if .Duration}} - <span class="duration-text">{{.Duration}}</span>{{end}}{{end}}</strong>
|
||||
{{if .Current}}<span class="current-badge">{{if eq $.Lang "es"}}ACTUAL{{else}}CURRENT{{end}}</span>{{end}}
|
||||
{{if .Expired}}<span class="expired-badge">{{if eq $.Lang "es"}}EXPIRADO{{else}}EXPIRED{{end}}</span>{{end}}
|
||||
<br>
|
||||
<small>{{.StartDate}} / {{if .Current}}{{if eq $.Lang "es"}}presente{{else}}now{{end}}{{else}}{{.EndDate}}{{end}} - ({{.Location}})</small>
|
||||
|
||||
{{if .ShortDescription}}
|
||||
<p class="experience-desc short-desc">{{.ShortDescription | safeHTML}}</p>
|
||||
{{end}}
|
||||
|
||||
{{if .Responsibilities}}
|
||||
<ul class="responsibilities long-only">
|
||||
{{range .Responsibilities}}
|
||||
<li>{{. | safeHTML}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PAGE 2 -->
|
||||
<div class="cv-page page-2">
|
||||
<!-- Professional Title Badges - Same as Page 1 -->
|
||||
<div class="cv-title-badges-header">
|
||||
<span class="title-badge">{{if eq .Lang "es"}}CONSULTOR TÉCNICO{{else}}TECHNICAL CONSULTANT{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}INGENIERO FULL-STACK{{else}}FULL-STACK ENGINEER{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}ESPECIALISTA EN AUTENTICACIÓN{{else}}AUTHENTICATION SPECIALIST{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}ARQUITECTO DE SOLUCIONES{{else}}SOLUTION ARCHITECT{{end}}</span>
|
||||
</div>
|
||||
|
||||
<!-- Page 2 Content Grid: Main Content + Right Sidebar -->
|
||||
<div class="page-content">
|
||||
<!-- Main Content Area - Page 2 -->
|
||||
<main class="cv-main">
|
||||
<!-- Awards Section -->
|
||||
{{if .CV.Awards}}
|
||||
<section id="awards" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:trophy" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Premios y Reconocimientos{{else}}Awards{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Awards}}
|
||||
<div class="award-item">
|
||||
{{if .AwardLogo}}
|
||||
<div class="award-logo">
|
||||
<img src="/static/images/companies/{{.AwardLogo}}" alt="{{.Title}} logo" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:trophy\' width=\'60\' height=\'60\' class=\'default-award-icon\'></iconify-icon>'">
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="award-content">
|
||||
<strong>{{.Title}}</strong><br>
|
||||
<small>{{.Issuer}} - {{.Date}}</small>
|
||||
|
||||
{{if .ShortDescription}}
|
||||
<p class="award-desc short-desc">{{.ShortDescription | safeHTML}}</p>
|
||||
{{end}}
|
||||
|
||||
{{if .Responsibilities}}
|
||||
<ul class="responsibilities long-only">
|
||||
{{range .Responsibilities}}
|
||||
<li>{{. | safeHTML}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
<!-- Projects Section -->
|
||||
{{if .CV.Projects}}
|
||||
<section id="projects" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:web" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Proyectos Personales / Freelance{{else}}Personal / Freelance Projects{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Projects}}
|
||||
<div class="project-item">
|
||||
{{if .ProjectLogo}}
|
||||
<div class="project-icon">
|
||||
<img src="/static/images/projects/{{.ProjectLogo}}" alt="{{.Title}} logo" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:web\' width=\'80\' height=\'80\' class=\'default-project-icon\'></iconify-icon>'">
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="project-icon">
|
||||
<iconify-icon icon="mdi:web" width="80" height="80" class="default-project-icon"></iconify-icon>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="project-content">
|
||||
<strong>
|
||||
{{if .ProjectName}}
|
||||
{{if .URL}}<a href="{{.URL}}" target="_blank" rel="noopener noreferrer">{{.ProjectName}}</a>{{else}}{{.ProjectName}}{{end}}{{if .ProjectDesc}} - {{.ProjectDesc}}{{end}}
|
||||
{{else}}
|
||||
{{if .URL}}<a href="{{.URL}}" target="_blank" rel="noopener noreferrer">{{.Title}}</a>{{else}}{{.Title}}{{end}}
|
||||
{{end}}
|
||||
</strong>
|
||||
{{if .Current}}<span class="live-badge"><iconify-icon icon="mdi:wifi" width="14" height="14"></iconify-icon>LIVE</span>{{end}}
|
||||
{{if .MaintainedBy}}<span class="maintained-badge">{{if eq $.Lang "es"}}MANTENIDO POR{{else}}MAINTAINED BY{{end}} {{.MaintainedBy}}</span>{{end}}
|
||||
<br>
|
||||
<small>{{if .StartDate}}{{.StartDate}}{{if .Current}}{{if .DynamicDate}} / {{.DynamicDate}}{{else}} / {{if eq $.Lang "es"}}presente{{else}}ahora{{end}}{{end}}{{end}}{{end}} - ({{.Location}})</small>
|
||||
|
||||
{{if .ShortDescription}}
|
||||
<p class="project-desc short-desc">{{.ShortDescription | safeHTML}}</p>
|
||||
{{end}}
|
||||
|
||||
{{if .Responsibilities}}
|
||||
<ul class="responsibilities long-only">
|
||||
{{range .Responsibilities}}
|
||||
<li>{{. | safeHTML}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
|
||||
{{if .Technologies}}
|
||||
<div class="project-technologies long-only">
|
||||
<strong>{{if eq $.Lang "es"}}Tecnologías:{{else}}Technologies:{{end}}</strong>
|
||||
{{range $index, $tech := .Technologies}}{{if $index}}, {{end}}{{$tech}}{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<!-- Link to full portfolio -->
|
||||
<div class="projects-footer">
|
||||
<p>{{if eq .Lang "es"}}Ver todos los proyectos en mi{{else}}See all projects on my{{end}}
|
||||
<a href="{{.CV.Personal.Domestika}}" target="_blank" rel="noopener noreferrer"><strong>{{if eq .Lang "es"}}portfolio de Domestika{{else}}Domestika portfolio{{end}}</strong></a></p>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
<!-- Courses Section -->
|
||||
{{if .CV.Courses}}
|
||||
<section id="courses" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:school" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Cursos Realizados{{else}}Courses{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Courses}}
|
||||
<div class="course-item">
|
||||
{{if .CourseLogo}}
|
||||
<div class="course-icon">
|
||||
<img src="/static/images/courses/{{.CourseLogo}}" alt="{{.Title}} logo" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:school\' width=\'80\' height=\'80\' class=\'default-course-icon\'></iconify-icon>'">
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="course-icon">
|
||||
<iconify-icon icon="mdi:school" width="80" height="80" class="default-course-icon"></iconify-icon>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="course-content">
|
||||
<strong>{{.Title}}</strong><br>
|
||||
<small>{{.Institution}} - {{.Date}} - ({{.Location}})</small>
|
||||
|
||||
{{if .ShortDescription}}
|
||||
<p class="course-desc short-desc">{{.ShortDescription}}</p>
|
||||
{{end}}
|
||||
|
||||
{{if .Responsibilities}}
|
||||
<ul class="responsibilities long-only">
|
||||
{{range .Responsibilities}}
|
||||
<li>{{. | safeHTML}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
<!-- Languages Section -->
|
||||
<section id="languages" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:translate" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Idiomas{{else}}Languages{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Languages}}
|
||||
<div class="language-item">
|
||||
<strong>{{.Language}}:</strong> {{.Proficiency}}{{if .Detail}} {{.Detail}}{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<!-- References Section -->
|
||||
{{if .CV.References}}
|
||||
<section id="references" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:link-variant" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Referencias{{else}}References{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.References}}
|
||||
<div class="reference-item">
|
||||
{{if .TextBefore}}{{.TextBefore}} {{end}}{{if eq .Action "downloadPDF"}}<a href="{{.URL}}" onclick="event.preventDefault(); openPdfModal(); return false;"><strong>{{if .LinkText}}{{.LinkText}}{{else}}{{.Title}}{{end}}</strong></a>{{else}}<a href="{{.URL}}" {{if and (eq .Type "cv") (ne .URL "/?lang=en") (ne .URL "/?lang=es")}}download{{else}}target="_blank" rel="noopener noreferrer"{{end}}><strong>{{if .LinkText}}{{.LinkText}}{{else}}{{.Title}}{{end}}</strong></a>{{end}}{{if .TextAfter}} {{.TextAfter}}{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
<!-- Other Section (Driver's License) -->
|
||||
{{if .CV.Other.DriverLicense}}
|
||||
<section id="other" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:information" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Otros{{else}}Other{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
<div class="other-content">
|
||||
{{if eq .Lang "es"}}Carnet de conducir tipo <strong>{{.CV.Other.DriverLicense}}</strong>{{else}}Driving License type <strong>{{.CV.Other.DriverLicense}}</strong>{{end}}
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
</main>
|
||||
|
||||
<!-- Right Sidebar - Skills (second half) -->
|
||||
<aside class="cv-sidebar cv-sidebar-right">
|
||||
<div class="sidebar-accordion-header" onclick="toggleSidebar(this)">
|
||||
<iconify-icon icon="mdi:brain" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Más Competencias{{else}}More Skills{{end}}</span>
|
||||
<iconify-icon icon="mdi:chevron-down" width="20" height="20" class="chevron"></iconify-icon>
|
||||
</div>
|
||||
<div class="sidebar-accordion-content">
|
||||
{{range $index, $category := .SkillsRight}}
|
||||
<section class="sidebar-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="sidebar-title">{{$category.Category}}</h3>
|
||||
</summary>
|
||||
<div class="sidebar-content">
|
||||
{{range $category.Items}}<div class="skill-item">{{.}}</div>{{end}}
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<!-- Footer - Only on Page 2 -->
|
||||
<footer class="cv-footer">
|
||||
<ul class="footer-content">
|
||||
<li>
|
||||
<div class="footer-label">linkedin_</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="{{.CV.Personal.LinkedIn}}" target="_blank" rel="noopener noreferrer">{{.CV.Personal.LinkedIn}}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="footer-label">github_</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="{{.CV.Personal.GitHub}}" target="_blank" rel="noopener noreferrer">{{.CV.Personal.GitHub}}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="footer-label">domestika_</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="{{.CV.Personal.Domestika}}" target="_blank" rel="noopener noreferrer">{{.CV.Personal.Domestika}}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="footer-label">email@</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="mailto:{{.CV.Personal.Email}}" target="_blank" rel="noopener noreferrer">{{.CV.Personal.Email}}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="footer-label">phone#</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="tel:+34676875420" target="_blank" rel="noopener noreferrer">+34 676 875 420</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
+16
-391
@@ -99,405 +99,30 @@
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Single Black Bar with Everything -->
|
||||
<div class="action-bar no-print" role="navigation" aria-label="Language and export controls">
|
||||
<div class="action-bar-content">
|
||||
<!-- Left: Site Title + Hamburger Menu + Language -->
|
||||
<div class="site-title">
|
||||
<!-- Left group: Logo + Hamburger -->
|
||||
<div class="site-title-left">
|
||||
<a href="/" class="site-logo-link" id="logoLink" aria-label="Home">
|
||||
<iconify-icon icon="mdi:file-account" width="24" height="24" class="site-icon"></iconify-icon>
|
||||
</a>
|
||||
|
||||
<!-- Hamburger Menu Button -->
|
||||
<button class="hamburger-btn" aria-label="Toggle navigation menu">
|
||||
<iconify-icon icon="mdi:menu" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Center: Title -->
|
||||
<a href="/" class="site-title-link" id="titleLink">
|
||||
<iconify-icon icon="mdi:file-account" width="20" height="20" class="site-icon-mobile"></iconify-icon>
|
||||
<span class="site-title-text">CV JAMR<span class="site-title-year"> - {{.CurrentYear}}</span></span>
|
||||
</a>
|
||||
|
||||
<!-- Right: Language selector -->
|
||||
<div class="language-selector">
|
||||
<button class="selector-btn {{if eq .Lang "en"}}active{{end}}" data-short="EN" onclick="selectLanguage('en')" aria-label="English">
|
||||
English
|
||||
</button>
|
||||
<button class="selector-btn {{if eq .Lang "es"}}active{{end}}" data-short="ES" onclick="selectLanguage('es')" aria-label="Español">
|
||||
Español
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Center: View controls with labels -->
|
||||
<div class="view-controls-center">
|
||||
<!-- CV Length toggle -->
|
||||
<div class="selector-group">
|
||||
<label class="selector-label">{{if eq .Lang "es"}}Longitud{{else}}Length{{end}}:</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="lengthToggle" onchange="toggleCVLength()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:file-document-outline" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:file-document-multiple-outline" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Logo toggle -->
|
||||
<div class="selector-group">
|
||||
<label class="selector-label">{{if eq .Lang "es"}}Logos{{else}}Logos{{end}}:</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="logoToggle" checked onchange="toggleLogos()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:image-off-outline" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:image-multiple-outline" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Theme toggle -->
|
||||
<div class="selector-group">
|
||||
<label class="selector-label">{{if eq .Lang "es"}}Vista{{else}}View{{end}}:</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="themeToggle" onchange="toggleTheme()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:page-layout-sidebar-left" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:page-layout-body" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: Action buttons -->
|
||||
<div class="action-buttons-right">
|
||||
<button
|
||||
class="action-btn pdf-btn"
|
||||
onclick="openPdfModal()"
|
||||
aria-label="{{if eq .Lang "es"}}Descargar como PDF{{else}}Download as PDF{{end}}">
|
||||
<iconify-icon icon="mdi:download" width="18" height="18"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Descargar como PDF{{else}}Download as PDF{{end}}
|
||||
</button>
|
||||
<button
|
||||
class="action-btn print-btn"
|
||||
onclick="printFriendly()"
|
||||
aria-label="{{if eq .Lang "es"}}Imprimir amigable{{else}}Print Friendly{{end}}">
|
||||
<iconify-icon icon="mdi:leaf" width="18" height="18"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Imprimir amigable{{else}}Print Friendly{{end}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<span id="loading"
|
||||
class="htmx-indicator"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
aria-label="Loading">
|
||||
<span class="loader"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Menu (Hidden by default) -->
|
||||
<nav id="navigation-menu" class="navigation-menu no-print" role="navigation" aria-label="CV sections">
|
||||
<div class="menu-content">
|
||||
<!-- CV Sections - Quick Navigation -->
|
||||
<div class="menu-item-submenu">
|
||||
<a href="#" class="menu-item has-submenu">
|
||||
<iconify-icon icon="mdi:menu" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Secciones CV{{else}}CV Sections{{end}}</span>
|
||||
<iconify-icon icon="mdi:chevron-right" width="16" height="16" class="submenu-arrow"></iconify-icon>
|
||||
</a>
|
||||
<div class="submenu-content">
|
||||
<a href="#education" class="menu-item" onclick="scrollToSection('education')">
|
||||
<iconify-icon icon="mdi:school" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Formación{{else}}Training{{end}}</span>
|
||||
</a>
|
||||
<a href="#skills" class="menu-item" onclick="scrollToSection('skills')">
|
||||
<iconify-icon icon="mdi:brain" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Competencias{{else}}Skills{{end}}</span>
|
||||
</a>
|
||||
<a href="#experience" class="menu-item" onclick="scrollToSection('experience')">
|
||||
<iconify-icon icon="mdi:office-building" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Experiencia{{else}}Experience{{end}}</span>
|
||||
</a>
|
||||
<a href="#awards" class="menu-item" onclick="scrollToSection('awards')">
|
||||
<iconify-icon icon="mdi:trophy" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Premios y Reconocimientos{{else}}Awards{{end}}</span>
|
||||
</a>
|
||||
<a href="#projects" class="menu-item" onclick="scrollToSection('projects')">
|
||||
<iconify-icon icon="mdi:web" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Proyectos Personales / Freelance{{else}}Personal / Freelance Projects{{end}}</span>
|
||||
</a>
|
||||
<a href="#courses" class="menu-item" onclick="scrollToSection('courses')">
|
||||
<iconify-icon icon="mdi:school" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Cursos Realizados{{else}}Courses{{end}}</span>
|
||||
</a>
|
||||
<a href="#languages" class="menu-item" onclick="scrollToSection('languages')">
|
||||
<iconify-icon icon="mdi:translate" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Idiomas{{else}}Languages{{end}}</span>
|
||||
</a>
|
||||
<a href="#references" class="menu-item" onclick="scrollToSection('references')">
|
||||
<iconify-icon icon="mdi:link-variant" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Referencias{{else}}References{{end}}</span>
|
||||
</a>
|
||||
<a href="#other" class="menu-item" onclick="scrollToSection('other')">
|
||||
<iconify-icon icon="mdi:information" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Otros{{else}}Other{{end}}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions Section -->
|
||||
<div class="menu-section-wrapper">
|
||||
<div class="menu-item menu-item-header">
|
||||
<iconify-icon icon="mdi:cog-outline" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Acciones Rápidas{{else}}Quick Actions{{end}}</span>
|
||||
</div>
|
||||
|
||||
<a href="#" class="menu-item menu-item-action" onclick="collapseAllSections(event)">
|
||||
<iconify-icon icon="mdi:arrow-collapse-all" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Colapsar Todo{{else}}Collapse All{{end}}</span>
|
||||
</a>
|
||||
<a href="#" class="menu-item menu-item-action" onclick="expandAllSections(event)">
|
||||
<iconify-icon icon="mdi:arrow-expand-all" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Expandir Todo{{else}}Expand All{{end}}</span>
|
||||
</a>
|
||||
<a href="#" id="show-zoom-menu-btn" class="menu-item menu-item-action" onclick="showZoomControl(event)" style="display: none;">
|
||||
<iconify-icon icon="mdi:magnify" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Zoom{{else}}Zoom{{end}}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- View Controls in menu (visible only on mobile < 900px) -->
|
||||
<div class="menu-controls-section">
|
||||
<div class="menu-item menu-item-header">
|
||||
<iconify-icon icon="mdi:tune-variant" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Controles de Vista{{else}}View Controls{{end}}</span>
|
||||
</div>
|
||||
|
||||
<!-- CV Length toggle -->
|
||||
<div class="menu-control-item">
|
||||
<label class="menu-control-label">
|
||||
<iconify-icon icon="mdi:file-document-outline" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Longitud{{else}}Length{{end}}</span>
|
||||
</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="lengthToggleMenu" onchange="toggleCVLength()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:file-document-outline" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:file-document-multiple-outline" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Logo toggle -->
|
||||
<div class="menu-control-item">
|
||||
<label class="menu-control-label">
|
||||
<iconify-icon icon="mdi:image-multiple-outline" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Logos{{else}}Logos{{end}}</span>
|
||||
</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="logoToggleMenu" checked onchange="toggleLogos()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:image-off-outline" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:image-multiple-outline" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Theme toggle -->
|
||||
<div class="menu-control-item">
|
||||
<label class="menu-control-label">
|
||||
<iconify-icon icon="mdi:page-layout-sidebar-left" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Vista{{else}}View{{end}}</span>
|
||||
</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="themeToggleMenu" onchange="toggleTheme()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:page-layout-sidebar-left" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:page-layout-body" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons in menu (visible only on mobile < 900px) -->
|
||||
<div class="menu-actions-section">
|
||||
<div class="menu-item menu-item-header">
|
||||
<iconify-icon icon="mdi:lightning-bolt" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Acciones{{else}}Actions{{end}}</span>
|
||||
</div>
|
||||
|
||||
<button class="menu-action-btn" onclick="openPdfModal()">
|
||||
<iconify-icon icon="mdi:download" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Descargar como PDF{{else}}Download as PDF{{end}}</span>
|
||||
</button>
|
||||
|
||||
<button class="menu-action-btn" onclick="printFriendly()">
|
||||
<iconify-icon icon="mdi:leaf" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Imprimir amigable{{else}}Print Friendly{{end}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
{{template "action-bar" .}}
|
||||
{{template "hamburger-menu" .}}
|
||||
|
||||
<!-- Zoom Wrapper (for zoom functionality) -->
|
||||
<div id="zoom-wrapper" class="zoom-wrapper">
|
||||
<!-- CV Content Container -->
|
||||
<div class="cv-container">
|
||||
<main id="cv-content"
|
||||
class="cv-paper"
|
||||
role="main"
|
||||
aria-live="polite">
|
||||
{{template "cv-content.html" .}}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Footer (hidden in print) -->
|
||||
<footer class="no-print">
|
||||
<p style="text-align: center; margin-bottom: 0.5rem;">
|
||||
<a href="https://github.com/juanatsap/cv-site" target="_blank" rel="noopener noreferrer" class="github-repo-link" style="text-decoration: none; display: inline-flex; align-items: center; gap: 0.5rem;">
|
||||
<iconify-icon icon="mdi:github" width="20" height="20"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Ver este proyecto en GitHub{{else}}View this project on GitHub{{end}}
|
||||
</a>
|
||||
</p>
|
||||
<p>© {{.CV.Meta.LastUpdated}} {{.CV.Personal.Name}} |
|
||||
{{if eq .Lang "es"}}Última actualización{{else}}Last updated{{end}}: {{.CV.Meta.LastUpdated}}</p>
|
||||
</footer>
|
||||
|
||||
<!-- Error Toast -->
|
||||
<div id="error-toast" class="error-toast no-print" role="alert" aria-live="assertive" style="display: none;">
|
||||
<span class="error-icon">⚠️</span>
|
||||
<span id="error-message"></span>
|
||||
<button onclick="this.parentElement.style.display='none'" aria-label="Close error message" class="error-close">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Back to Top Button -->
|
||||
<button id="back-to-top" class="back-to-top no-print" aria-label="{{if eq .Lang "es"}}Volver arriba{{else}}Back to top{{end}}" style="display: none;">
|
||||
<iconify-icon icon="mdi:arrow-up" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
|
||||
<!-- Info Button (Bottom Left) -->
|
||||
<button id="info-button" class="info-button no-print" aria-label="{{if eq .Lang "es"}}Información{{else}}Information{{end}}" onclick="openInfoModal()">
|
||||
<iconify-icon icon="mdi:information-outline" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
|
||||
<!-- Info Modal -->
|
||||
<div id="info-modal" class="info-modal no-print" onclick="closeInfoModalOnBackdrop(event)">
|
||||
<div class="info-modal-content" onclick="event.stopPropagation()">
|
||||
<button class="info-modal-close" onclick="closeInfoModal()" aria-label="Close">
|
||||
<iconify-icon icon="mdi:close" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
|
||||
<div class="info-modal-header">
|
||||
<h2>{{.UI.InfoModal.Title}}</h2>
|
||||
<div class="info-modal-cv-title">
|
||||
CV {{.CurrentYear}} JAMR -
|
||||
<span class="photo-bracket-wrapper">
|
||||
<img src="/static/images/profile/dni.jpeg" alt="JAMR" class="info-modal-photo">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-modal-body">
|
||||
<p class="info-modal-description">
|
||||
{{.UI.InfoModal.Description}}
|
||||
</p>
|
||||
|
||||
<div class="info-modal-tech">
|
||||
<div class="info-tech-item">
|
||||
<iconify-icon icon="mdi:language-go" width="32" height="32"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.TechStack.GoHono}}</span>
|
||||
</div>
|
||||
<div class="info-tech-item">
|
||||
<iconify-icon icon="mdi:lightning-bolt" width="32" height="32"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.TechStack.HTMX}}</span>
|
||||
</div>
|
||||
<div class="info-tech-item">
|
||||
<iconify-icon icon="mdi:language-html5" width="32" height="32"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.TechStack.HTML5}}</span>
|
||||
</div>
|
||||
<div class="info-tech-item">
|
||||
<iconify-icon icon="mdi:language-css3" width="32" height="32"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.TechStack.CSS3}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="info-modal-github-subtext">{{.UI.InfoModal.ViewSourceSubtext}}</p>
|
||||
<a href="https://github.com/juanatsap/cv-site" target="_blank" rel="noopener noreferrer" class="info-modal-github">
|
||||
<iconify-icon icon="mdi:github" width="24" height="24"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.ViewSource}}</span>
|
||||
<iconify-icon icon="mdi:arrow-right" width="20" height="20"></iconify-icon>
|
||||
</a>
|
||||
</div>
|
||||
<main id="cv-content"
|
||||
class="cv-paper"
|
||||
role="main"
|
||||
aria-live="polite">
|
||||
{{template "cv-content.html" .}}
|
||||
</main>
|
||||
</div>
|
||||
</div> <!-- End cv-container -->
|
||||
|
||||
{{template "page-footer" .}}
|
||||
</div> <!-- End zoom-wrapper -->
|
||||
|
||||
<!-- PDF Export Modal -->
|
||||
<div id="pdf-modal" class="info-modal no-print" onclick="closePdfModalOnBackdrop(event)">
|
||||
<div class="info-modal-content" onclick="event.stopPropagation()">
|
||||
<button class="info-modal-close" onclick="closePdfModal()" aria-label="Close">
|
||||
<iconify-icon icon="mdi:close" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
|
||||
<div class="info-modal-header">
|
||||
<div class="icon" style="font-size: 3rem; margin-bottom: 1rem;">🚧</div>
|
||||
<h2>{{if eq .Lang "es"}}Exportación PDF - En Desarrollo{{else}}PDF Export - Work in Progress{{end}}</h2>
|
||||
</div>
|
||||
|
||||
<div class="info-modal-body">
|
||||
<p class="info-modal-description">
|
||||
{{if eq .Lang "es"}}
|
||||
La función de exportación a PDF está siendo mejorada. Por favor, usa el botón <strong>Imprimir Amigable</strong> en su lugar (Ctrl+P o Cmd+P para guardar como PDF).
|
||||
{{else}}
|
||||
The PDF export feature is currently being improved. Please use the <strong>Print Friendly</strong> button instead (Ctrl+P or Cmd+P to save as PDF).
|
||||
{{end}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Zoom Control (Fixed Bottom Center, Draggable) -->
|
||||
<div id="zoom-control" class="zoom-control no-print" role="group" aria-label="{{if eq .Lang "es"}}Control de zoom{{else}}Zoom control{{end}}">
|
||||
<button
|
||||
id="zoom-close"
|
||||
class="zoom-close-btn"
|
||||
aria-label="{{if eq .Lang "es"}}Cerrar control de zoom{{else}}Close zoom control{{end}}"
|
||||
title="{{if eq .Lang "es"}}Cerrar{{else}}Close{{end}}">
|
||||
<iconify-icon icon="mdi:close" width="16" height="16"></iconify-icon>
|
||||
</button>
|
||||
|
||||
<span class="zoom-value zoom-value-min" aria-hidden="true">25</span>
|
||||
|
||||
<input
|
||||
type="range"
|
||||
id="zoom-slider"
|
||||
class="zoom-slider"
|
||||
min="25"
|
||||
max="175"
|
||||
step="1"
|
||||
value="100"
|
||||
aria-label="{{if eq .Lang "es"}}Ajustar nivel de zoom del CV{{else}}Adjust CV zoom level{{end}}"
|
||||
aria-valuemin="25"
|
||||
aria-valuemax="175"
|
||||
aria-valuenow="100"
|
||||
aria-valuetext="100%">
|
||||
|
||||
<span class="zoom-value zoom-value-max" aria-hidden="true">175</span>
|
||||
|
||||
<button
|
||||
id="zoom-reset"
|
||||
class="zoom-reset-btn"
|
||||
aria-label="{{if eq .Lang "es"}}Restablecer zoom al 100%{{else}}Reset zoom to 100%{{end}}"
|
||||
title="{{if eq .Lang "es"}}Restablecer{{else}}Reset{{end}}"
|
||||
aria-live="polite">
|
||||
<span id="zoom-value-current">100</span>
|
||||
</button>
|
||||
</div>
|
||||
{{template "error-toast" .}}
|
||||
{{template "back-to-top" .}}
|
||||
{{template "info-button" .}}
|
||||
{{template "info-modal" .}}
|
||||
{{template "pdf-modal" .}}
|
||||
{{template "zoom-control" .}}
|
||||
|
||||
<!-- External JavaScript - CSP Compliant -->
|
||||
<script src="/static/js/main.js"></script>
|
||||
|
||||
@@ -0,0 +1,521 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{if eq .Lang "es"}}es{{else}}en{{end}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<!-- Primary Meta Tags -->
|
||||
<title>{{.CV.Personal.Name}} - {{if eq .Lang "es"}}Curriculum Vitae{{else}}Curriculum Vitae{{end}}</title>
|
||||
<meta name="title" content="{{.CV.Personal.Name}} - {{if eq .Lang "es"}}CV Profesional{{else}}Professional CV{{end}}">
|
||||
<meta name="description" content="{{.CV.Personal.Title}} | {{if eq .Lang "es"}}18 años de experiencia en desarrollo web, SAP CDC, React, Node.js, Go, HTMX y desarrollo asistido por IA{{else}}18 years of experience in web development, SAP CDC, React, Node.js, Go, HTMX and AI-assisted development{{end}}">
|
||||
<meta name="keywords" content="{{if eq .Lang "es"}}CV, Curriculum Vitae, {{.CV.Personal.Name}}, Desarrollador FullStack, SAP CDC, React, Node.js, Go, HTMX, IA, Desarrollo Web, Consultor Técnico{{else}}CV, Resume, {{.CV.Personal.Name}}, FullStack Developer, SAP CDC, React, Node.js, Go, HTMX, AI, Web Development, Technical Consultant{{end}}">
|
||||
<meta name="author" content="{{.CV.Personal.Name}}">
|
||||
<meta name="robots" content="index, follow">
|
||||
<link rel="canonical" href="{{.CanonicalURL}}">
|
||||
|
||||
<!-- Hreflang tags for international SEO -->
|
||||
<link rel="alternate" hreflang="en" href="{{.AlternateEN}}">
|
||||
<link rel="alternate" hreflang="es" href="{{.AlternateES}}">
|
||||
<link rel="alternate" hreflang="x-default" href="https://juan.andres.morenorub.io/?lang=en">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="profile">
|
||||
<meta property="og:url" content="{{.CV.Personal.Website}}">
|
||||
<meta property="og:title" content="{{.CV.Personal.Name}} - {{if eq .Lang "es"}}CV Profesional{{else}}Professional CV{{end}}">
|
||||
<meta property="og:description" content="{{.CV.Personal.Title}} | {{if eq .Lang "es"}}Consultor Técnico Senior con 18 años de experiencia{{else}}Senior Technical Consultant with 18 years of experience{{end}}">
|
||||
<meta property="og:image" content="{{.CV.Personal.Website}}/static/images/profile.jpg">
|
||||
<meta property="og:locale" content="{{if eq .Lang "es"}}es_ES{{else}}en_US{{end}}">
|
||||
<meta property="og:site_name" content="{{.CV.Personal.Name}}">
|
||||
<meta property="profile:first_name" content="Juan Andrés">
|
||||
<meta property="profile:last_name" content="Moreno Rubio">
|
||||
<meta property="profile:username" content="txeo">
|
||||
|
||||
<!-- Social Media Card (Generic) -->
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:title" content="{{.CV.Personal.Name}} - {{if eq .Lang "es"}}CV Profesional{{else}}Professional CV{{end}}">
|
||||
<meta name="twitter:description" content="{{.CV.Personal.Title}}">
|
||||
<meta name="twitter:image" content="{{.CV.Personal.Website}}/static/images/profile.jpg">
|
||||
|
||||
<!-- HTMX Configuration -->
|
||||
<meta name="htmx-config" content='{"timeout":5000,"defaultSwapStyle":"innerHTML","defaultSwapDelay":0,"defaultSettleDelay":20}'>
|
||||
|
||||
<!-- HTMX with SRI (Subresource Integrity) -->
|
||||
<script src="https://unpkg.com/htmx.org@1.9.10"
|
||||
integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC"
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
<!-- Iconify - Load synchronously for immediate rendering -->
|
||||
<script src="https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js"></script>
|
||||
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="/static/css/main.css">
|
||||
<link rel="stylesheet" href="/static/css/logo-toggle.css">
|
||||
<link rel="stylesheet" href="/static/css/print.css" media="print">
|
||||
|
||||
<!-- Fonts with Preload -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Structured Data (JSON-LD) -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Person",
|
||||
"name": "{{.CV.Personal.Name}}",
|
||||
"jobTitle": "{{.CV.Personal.Title}}",
|
||||
"url": "{{.CV.Personal.Website}}",
|
||||
"email": "{{.CV.Personal.Email}}",
|
||||
"telephone": "{{.CV.Personal.Phone}}",
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
"addressLocality": "{{.CV.Personal.Location}}"
|
||||
},
|
||||
"sameAs": [
|
||||
"{{.CV.Personal.LinkedIn}}",
|
||||
"{{.CV.Personal.GitHub}}",
|
||||
"{{.CV.Personal.Domestika}}"
|
||||
],
|
||||
"alumniOf": {
|
||||
"@type": "EducationalOrganization",
|
||||
"name": "Universidad de Extremadura"
|
||||
},
|
||||
"knowsAbout": [
|
||||
"Web Development",
|
||||
"SAP Customer Data Cloud",
|
||||
"React",
|
||||
"Node.js",
|
||||
"Go",
|
||||
"HTMX",
|
||||
"AI-Assisted Development",
|
||||
"Full Stack Development"
|
||||
],
|
||||
"worksFor": {
|
||||
"@type": "Organization",
|
||||
"name": "Olympic Broadcasting Services"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Single Black Bar with Everything -->
|
||||
<div class="action-bar no-print" role="navigation" aria-label="Language and export controls">
|
||||
<div class="action-bar-content">
|
||||
<!-- Left: Site Title + Hamburger Menu + Language -->
|
||||
<div class="site-title">
|
||||
<!-- Left group: Logo + Hamburger -->
|
||||
<div class="site-title-left">
|
||||
<a href="/" class="site-logo-link" id="logoLink" aria-label="Home">
|
||||
<iconify-icon icon="mdi:file-account" width="24" height="24" class="site-icon"></iconify-icon>
|
||||
</a>
|
||||
|
||||
<!-- Hamburger Menu Button -->
|
||||
<button class="hamburger-btn" aria-label="Toggle navigation menu">
|
||||
<iconify-icon icon="mdi:menu" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Center: Title -->
|
||||
<a href="/" class="site-title-link" id="titleLink">
|
||||
<iconify-icon icon="mdi:file-account" width="20" height="20" class="site-icon-mobile"></iconify-icon>
|
||||
<span class="site-title-text">CV JAMR<span class="site-title-year"> - {{.CurrentYear}}</span></span>
|
||||
</a>
|
||||
|
||||
<!-- Right: Language selector -->
|
||||
<div class="language-selector">
|
||||
<button class="selector-btn {{if eq .Lang "en"}}active{{end}}" data-short="EN" onclick="selectLanguage('en')" aria-label="English">
|
||||
English
|
||||
</button>
|
||||
<button class="selector-btn {{if eq .Lang "es"}}active{{end}}" data-short="ES" onclick="selectLanguage('es')" aria-label="Español">
|
||||
Español
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Center: View controls with labels -->
|
||||
<div class="view-controls-center">
|
||||
<!-- CV Length toggle -->
|
||||
<div class="selector-group">
|
||||
<label class="selector-label">{{if eq .Lang "es"}}Longitud{{else}}Length{{end}}:</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="lengthToggle" onchange="toggleCVLength()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:file-document-outline" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:file-document-multiple-outline" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Logo toggle -->
|
||||
<div class="selector-group">
|
||||
<label class="selector-label">{{if eq .Lang "es"}}Logos{{else}}Logos{{end}}:</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="logoToggle" checked onchange="toggleLogos()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:image-off-outline" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:image-multiple-outline" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Theme toggle -->
|
||||
<div class="selector-group">
|
||||
<label class="selector-label">{{if eq .Lang "es"}}Vista{{else}}View{{end}}:</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="themeToggle" onchange="toggleTheme()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:page-layout-sidebar-left" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:page-layout-body" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: Action buttons -->
|
||||
<div class="action-buttons-right">
|
||||
<button
|
||||
class="action-btn pdf-btn"
|
||||
onclick="openPdfModal()"
|
||||
aria-label="{{if eq .Lang "es"}}Descargar como PDF{{else}}Download as PDF{{end}}">
|
||||
<iconify-icon icon="mdi:download" width="18" height="18"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Descargar como PDF{{else}}Download as PDF{{end}}
|
||||
</button>
|
||||
<button
|
||||
class="action-btn print-btn"
|
||||
onclick="printFriendly()"
|
||||
aria-label="{{if eq .Lang "es"}}Imprimir amigable{{else}}Print Friendly{{end}}">
|
||||
<iconify-icon icon="mdi:leaf" width="18" height="18"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Imprimir amigable{{else}}Print Friendly{{end}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<span id="loading"
|
||||
class="htmx-indicator"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
aria-label="Loading">
|
||||
<span class="loader"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Menu (Hidden by default) -->
|
||||
<nav id="navigation-menu" class="navigation-menu no-print" role="navigation" aria-label="CV sections">
|
||||
<div class="menu-content">
|
||||
<!-- CV Sections - Quick Navigation -->
|
||||
<div class="menu-item-submenu">
|
||||
<a href="#" class="menu-item has-submenu">
|
||||
<iconify-icon icon="mdi:menu" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Secciones CV{{else}}CV Sections{{end}}</span>
|
||||
<iconify-icon icon="mdi:chevron-right" width="16" height="16" class="submenu-arrow"></iconify-icon>
|
||||
</a>
|
||||
<div class="submenu-content">
|
||||
<a href="#education" class="menu-item" onclick="scrollToSection('education')">
|
||||
<iconify-icon icon="mdi:school" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Formación{{else}}Training{{end}}</span>
|
||||
</a>
|
||||
<a href="#skills" class="menu-item" onclick="scrollToSection('skills')">
|
||||
<iconify-icon icon="mdi:brain" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Competencias{{else}}Skills{{end}}</span>
|
||||
</a>
|
||||
<a href="#experience" class="menu-item" onclick="scrollToSection('experience')">
|
||||
<iconify-icon icon="mdi:office-building" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Experiencia{{else}}Experience{{end}}</span>
|
||||
</a>
|
||||
<a href="#awards" class="menu-item" onclick="scrollToSection('awards')">
|
||||
<iconify-icon icon="mdi:trophy" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Premios y Reconocimientos{{else}}Awards{{end}}</span>
|
||||
</a>
|
||||
<a href="#projects" class="menu-item" onclick="scrollToSection('projects')">
|
||||
<iconify-icon icon="mdi:web" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Proyectos Personales / Freelance{{else}}Personal / Freelance Projects{{end}}</span>
|
||||
</a>
|
||||
<a href="#courses" class="menu-item" onclick="scrollToSection('courses')">
|
||||
<iconify-icon icon="mdi:school" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Cursos Realizados{{else}}Courses{{end}}</span>
|
||||
</a>
|
||||
<a href="#languages" class="menu-item" onclick="scrollToSection('languages')">
|
||||
<iconify-icon icon="mdi:translate" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Idiomas{{else}}Languages{{end}}</span>
|
||||
</a>
|
||||
<a href="#references" class="menu-item" onclick="scrollToSection('references')">
|
||||
<iconify-icon icon="mdi:link-variant" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Referencias{{else}}References{{end}}</span>
|
||||
</a>
|
||||
<a href="#other" class="menu-item" onclick="scrollToSection('other')">
|
||||
<iconify-icon icon="mdi:information" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Otros{{else}}Other{{end}}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions Section -->
|
||||
<div class="menu-section-wrapper">
|
||||
<div class="menu-item menu-item-header">
|
||||
<iconify-icon icon="mdi:cog-outline" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Acciones Rápidas{{else}}Quick Actions{{end}}</span>
|
||||
</div>
|
||||
|
||||
<a href="#" class="menu-item menu-item-action" onclick="collapseAllSections(event)">
|
||||
<iconify-icon icon="mdi:arrow-collapse-all" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Colapsar Todo{{else}}Collapse All{{end}}</span>
|
||||
</a>
|
||||
<a href="#" class="menu-item menu-item-action" onclick="expandAllSections(event)">
|
||||
<iconify-icon icon="mdi:arrow-expand-all" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Expandir Todo{{else}}Expand All{{end}}</span>
|
||||
</a>
|
||||
<a href="#" id="show-zoom-menu-btn" class="menu-item menu-item-action" onclick="showZoomControl(event)" style="display: none;">
|
||||
<iconify-icon icon="mdi:magnify" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Zoom{{else}}Zoom{{end}}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- View Controls in menu (visible only on mobile < 900px) -->
|
||||
<div class="menu-controls-section">
|
||||
<div class="menu-item menu-item-header">
|
||||
<iconify-icon icon="mdi:tune-variant" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Controles de Vista{{else}}View Controls{{end}}</span>
|
||||
</div>
|
||||
|
||||
<!-- CV Length toggle -->
|
||||
<div class="menu-control-item">
|
||||
<label class="menu-control-label">
|
||||
<iconify-icon icon="mdi:file-document-outline" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Longitud{{else}}Length{{end}}</span>
|
||||
</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="lengthToggleMenu" onchange="toggleCVLength()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:file-document-outline" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:file-document-multiple-outline" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Logo toggle -->
|
||||
<div class="menu-control-item">
|
||||
<label class="menu-control-label">
|
||||
<iconify-icon icon="mdi:image-multiple-outline" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Logos{{else}}Logos{{end}}</span>
|
||||
</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="logoToggleMenu" checked onchange="toggleLogos()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:image-off-outline" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:image-multiple-outline" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Theme toggle -->
|
||||
<div class="menu-control-item">
|
||||
<label class="menu-control-label">
|
||||
<iconify-icon icon="mdi:page-layout-sidebar-left" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Vista{{else}}View{{end}}</span>
|
||||
</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="themeToggleMenu" onchange="toggleTheme()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:page-layout-sidebar-left" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:page-layout-body" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons in menu (visible only on mobile < 900px) -->
|
||||
<div class="menu-actions-section">
|
||||
<div class="menu-item menu-item-header">
|
||||
<iconify-icon icon="mdi:lightning-bolt" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Acciones{{else}}Actions{{end}}</span>
|
||||
</div>
|
||||
|
||||
<button class="menu-action-btn" onclick="openPdfModal()">
|
||||
<iconify-icon icon="mdi:download" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Descargar como PDF{{else}}Download as PDF{{end}}</span>
|
||||
</button>
|
||||
|
||||
<button class="menu-action-btn" onclick="printFriendly()">
|
||||
<iconify-icon icon="mdi:leaf" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Imprimir amigable{{else}}Print Friendly{{end}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Zoom Wrapper (for zoom functionality) -->
|
||||
<div id="zoom-wrapper" class="zoom-wrapper">
|
||||
<!-- CV Content Container -->
|
||||
<div class="cv-container">
|
||||
<main id="cv-content"
|
||||
class="cv-paper"
|
||||
role="main"
|
||||
aria-live="polite">
|
||||
{{template "cv-content.html" .}}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Footer (hidden in print) -->
|
||||
<footer class="no-print">
|
||||
<p style="text-align: center; margin-bottom: 0.5rem;">
|
||||
<a href="https://github.com/juanatsap/cv-site" target="_blank" rel="noopener noreferrer" class="github-repo-link" style="text-decoration: none; display: inline-flex; align-items: center; gap: 0.5rem;">
|
||||
<iconify-icon icon="mdi:github" width="20" height="20"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Ver este proyecto en GitHub{{else}}View this project on GitHub{{end}}
|
||||
</a>
|
||||
</p>
|
||||
<p>© {{.CV.Meta.LastUpdated}} {{.CV.Personal.Name}} |
|
||||
{{if eq .Lang "es"}}Última actualización{{else}}Last updated{{end}}: {{.CV.Meta.LastUpdated}}</p>
|
||||
</footer>
|
||||
|
||||
<!-- Error Toast -->
|
||||
<div id="error-toast" class="error-toast no-print" role="alert" aria-live="assertive" style="display: none;">
|
||||
<span class="error-icon">⚠️</span>
|
||||
<span id="error-message"></span>
|
||||
<button onclick="this.parentElement.style.display='none'" aria-label="Close error message" class="error-close">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Back to Top Button -->
|
||||
<button id="back-to-top" class="back-to-top no-print" aria-label="{{if eq .Lang "es"}}Volver arriba{{else}}Back to top{{end}}" style="display: none;">
|
||||
<iconify-icon icon="mdi:arrow-up" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
|
||||
<!-- Info Button (Bottom Left) -->
|
||||
<button id="info-button" class="info-button no-print" aria-label="{{if eq .Lang "es"}}Información{{else}}Information{{end}}" onclick="openInfoModal()">
|
||||
<iconify-icon icon="mdi:information-outline" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
|
||||
<!-- Info Modal -->
|
||||
<div id="info-modal" class="info-modal no-print" onclick="closeInfoModalOnBackdrop(event)">
|
||||
<div class="info-modal-content" onclick="event.stopPropagation()">
|
||||
<button class="info-modal-close" onclick="closeInfoModal()" aria-label="Close">
|
||||
<iconify-icon icon="mdi:close" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
|
||||
<div class="info-modal-header">
|
||||
<h2>{{.UI.InfoModal.Title}}</h2>
|
||||
<div class="info-modal-cv-title">
|
||||
CV {{.CurrentYear}} JAMR -
|
||||
<span class="photo-bracket-wrapper">
|
||||
<img src="/static/images/profile/dni.jpeg" alt="JAMR" class="info-modal-photo">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-modal-body">
|
||||
<p class="info-modal-description">
|
||||
{{.UI.InfoModal.Description}}
|
||||
</p>
|
||||
|
||||
<div class="info-modal-tech">
|
||||
<div class="info-tech-item">
|
||||
<iconify-icon icon="mdi:language-go" width="32" height="32"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.TechStack.GoHono}}</span>
|
||||
</div>
|
||||
<div class="info-tech-item">
|
||||
<iconify-icon icon="mdi:lightning-bolt" width="32" height="32"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.TechStack.HTMX}}</span>
|
||||
</div>
|
||||
<div class="info-tech-item">
|
||||
<iconify-icon icon="mdi:language-html5" width="32" height="32"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.TechStack.HTML5}}</span>
|
||||
</div>
|
||||
<div class="info-tech-item">
|
||||
<iconify-icon icon="mdi:language-css3" width="32" height="32"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.TechStack.CSS3}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="info-modal-github-subtext">{{.UI.InfoModal.ViewSourceSubtext}}</p>
|
||||
<a href="https://github.com/juanatsap/cv-site" target="_blank" rel="noopener noreferrer" class="info-modal-github">
|
||||
<iconify-icon icon="mdi:github" width="24" height="24"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.ViewSource}}</span>
|
||||
<iconify-icon icon="mdi:arrow-right" width="20" height="20"></iconify-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- End cv-container -->
|
||||
</div> <!-- End zoom-wrapper -->
|
||||
|
||||
<!-- PDF Export Modal -->
|
||||
<div id="pdf-modal" class="info-modal no-print" onclick="closePdfModalOnBackdrop(event)">
|
||||
<div class="info-modal-content" onclick="event.stopPropagation()">
|
||||
<button class="info-modal-close" onclick="closePdfModal()" aria-label="Close">
|
||||
<iconify-icon icon="mdi:close" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
|
||||
<div class="info-modal-header">
|
||||
<div class="icon" style="font-size: 3rem; margin-bottom: 1rem;">🚧</div>
|
||||
<h2>{{if eq .Lang "es"}}Exportación PDF - En Desarrollo{{else}}PDF Export - Work in Progress{{end}}</h2>
|
||||
</div>
|
||||
|
||||
<div class="info-modal-body">
|
||||
<p class="info-modal-description">
|
||||
{{if eq .Lang "es"}}
|
||||
La función de exportación a PDF está siendo mejorada. Por favor, usa el botón <strong>Imprimir Amigable</strong> en su lugar (Ctrl+P o Cmd+P para guardar como PDF).
|
||||
{{else}}
|
||||
The PDF export feature is currently being improved. Please use the <strong>Print Friendly</strong> button instead (Ctrl+P or Cmd+P to save as PDF).
|
||||
{{end}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Zoom Control (Fixed Bottom Center, Draggable) -->
|
||||
<div id="zoom-control" class="zoom-control no-print" role="group" aria-label="{{if eq .Lang "es"}}Control de zoom{{else}}Zoom control{{end}}">
|
||||
<button
|
||||
id="zoom-close"
|
||||
class="zoom-close-btn"
|
||||
aria-label="{{if eq .Lang "es"}}Cerrar control de zoom{{else}}Close zoom control{{end}}"
|
||||
title="{{if eq .Lang "es"}}Cerrar{{else}}Close{{end}}">
|
||||
<iconify-icon icon="mdi:close" width="16" height="16"></iconify-icon>
|
||||
</button>
|
||||
|
||||
<span class="zoom-value zoom-value-min" aria-hidden="true">25</span>
|
||||
|
||||
<input
|
||||
type="range"
|
||||
id="zoom-slider"
|
||||
class="zoom-slider"
|
||||
min="25"
|
||||
max="175"
|
||||
step="1"
|
||||
value="100"
|
||||
aria-label="{{if eq .Lang "es"}}Ajustar nivel de zoom del CV{{else}}Adjust CV zoom level{{end}}"
|
||||
aria-valuemin="25"
|
||||
aria-valuemax="175"
|
||||
aria-valuenow="100"
|
||||
aria-valuetext="100%">
|
||||
|
||||
<span class="zoom-value zoom-value-max" aria-hidden="true">175</span>
|
||||
|
||||
<button
|
||||
id="zoom-reset"
|
||||
class="zoom-reset-btn"
|
||||
aria-label="{{if eq .Lang "es"}}Restablecer zoom al 100%{{else}}Reset zoom to 100%{{end}}"
|
||||
title="{{if eq .Lang "es"}}Restablecer{{else}}Reset{{end}}"
|
||||
aria-live="polite">
|
||||
<span id="zoom-value-current">100</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- External JavaScript - CSP Compliant -->
|
||||
<script src="/static/js/main.js"></script>
|
||||
|
||||
<!-- Matomo Analytics - Nonce-based CSP -->
|
||||
<script nonce="{{.CSPNonce}}">
|
||||
var _paq = window._paq = window._paq || [];
|
||||
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var u="https://matomo.drolo.club/";
|
||||
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||
_paq.push(['setSiteId', '4']);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
</script>
|
||||
<!-- End Matomo Code -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
{{define "cv-footer"}}
|
||||
<!-- Footer - Only on Page 2 -->
|
||||
<footer class="cv-footer">
|
||||
<ul class="footer-content">
|
||||
<li>
|
||||
<div class="footer-label">linkedin_</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="{{.CV.Personal.LinkedIn}}" target="_blank" rel="noopener noreferrer">{{.CV.Personal.LinkedIn}}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="footer-label">github_</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="{{.CV.Personal.GitHub}}" target="_blank" rel="noopener noreferrer">{{.CV.Personal.GitHub}}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="footer-label">domestika_</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="{{.CV.Personal.Domestika}}" target="_blank" rel="noopener noreferrer">{{.CV.Personal.Domestika}}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="footer-label">email@</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="mailto:{{.CV.Personal.Email}}" target="_blank" rel="noopener noreferrer">{{.CV.Personal.Email}}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="footer-label">phone#</div>
|
||||
<div class="footer-separator"><i class="fa fa-circle"></i></div>
|
||||
<div class="footer-value">
|
||||
<a href="tel:+34676875420" target="_blank" rel="noopener noreferrer">+34 676 875 420</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
{{end}}
|
||||
@@ -0,0 +1,13 @@
|
||||
{{define "page-footer"}}
|
||||
<!-- Footer (hidden in print) -->
|
||||
<footer class="no-print">
|
||||
<p style="text-align: center; margin-bottom: 0.5rem;">
|
||||
<a href="https://github.com/juanatsap/cv-site" target="_blank" rel="noopener noreferrer" class="github-repo-link" style="text-decoration: none; display: inline-flex; align-items: center; gap: 0.5rem;">
|
||||
<iconify-icon icon="mdi:github" width="20" height="20"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Ver este proyecto en GitHub{{else}}View this project on GitHub{{end}}
|
||||
</a>
|
||||
</p>
|
||||
<p>© {{.CV.Meta.LastUpdated}} {{.CV.Personal.Name}} |
|
||||
{{if eq .Lang "es"}}Última actualización{{else}}Last updated{{end}}: {{.CV.Meta.LastUpdated}}</p>
|
||||
</footer>
|
||||
{{end}}
|
||||
@@ -0,0 +1,24 @@
|
||||
{{define "sidebar"}}
|
||||
<!-- Sidebar - Reusable for left and right -->
|
||||
<aside class="cv-sidebar {{.SidebarClass}}">
|
||||
<div class="sidebar-accordion-header" onclick="toggleSidebar(this)">
|
||||
<iconify-icon icon="mdi:brain" width="20" height="20"></iconify-icon>
|
||||
<span>{{.SidebarTitle}}</span>
|
||||
<iconify-icon icon="mdi:chevron-down" width="20" height="20" class="chevron"></iconify-icon>
|
||||
</div>
|
||||
<div class="sidebar-accordion-content">
|
||||
{{range .Skills}}
|
||||
<section class="sidebar-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="sidebar-title">{{.Category}}</h3>
|
||||
</summary>
|
||||
<div class="sidebar-content">
|
||||
{{range .Items}}<div class="skill-item">{{.}}</div>{{end}}
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
</div>
|
||||
</aside>
|
||||
{{end}}
|
||||
@@ -0,0 +1,12 @@
|
||||
{{define "title-badges"}}
|
||||
<!-- Professional Title Badges - Full Width Top Bar -->
|
||||
<div class="cv-title-badges-header">
|
||||
<span class="title-badge">{{if eq .Lang "es"}}CONSULTOR TÉCNICO{{else}}TECHNICAL CONSULTANT{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}INGENIERO FULL-STACK{{else}}FULL-STACK ENGINEER{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}ESPECIALISTA EN AUTENTICACIÓN{{else}}AUTHENTICATION SPECIALIST{{end}}</span>
|
||||
<span class="badge-separator">|</span>
|
||||
<span class="title-badge">{{if eq .Lang "es"}}ARQUITECTO DE SOLUCIONES{{else}}SOLUTION ARCHITECT{{end}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -0,0 +1,52 @@
|
||||
{{define "info-modal"}}
|
||||
<!-- Info Modal -->
|
||||
<div id="info-modal" class="info-modal no-print" onclick="closeInfoModalOnBackdrop(event)">
|
||||
<div class="info-modal-content" onclick="event.stopPropagation()">
|
||||
<button class="info-modal-close" onclick="closeInfoModal()" aria-label="Close">
|
||||
<iconify-icon icon="mdi:close" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
|
||||
<div class="info-modal-header">
|
||||
<h2>{{.UI.InfoModal.Title}}</h2>
|
||||
<div class="info-modal-cv-title">
|
||||
CV {{.CurrentYear}} JAMR -
|
||||
<span class="photo-bracket-wrapper">
|
||||
<img src="/static/images/profile/dni.jpeg" alt="JAMR" class="info-modal-photo">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-modal-body">
|
||||
<p class="info-modal-description">
|
||||
{{.UI.InfoModal.Description}}
|
||||
</p>
|
||||
|
||||
<div class="info-modal-tech">
|
||||
<div class="info-tech-item">
|
||||
<iconify-icon icon="mdi:language-go" width="32" height="32"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.TechStack.GoHono}}</span>
|
||||
</div>
|
||||
<div class="info-tech-item">
|
||||
<iconify-icon icon="mdi:lightning-bolt" width="32" height="32"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.TechStack.HTMX}}</span>
|
||||
</div>
|
||||
<div class="info-tech-item">
|
||||
<iconify-icon icon="mdi:language-html5" width="32" height="32"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.TechStack.HTML5}}</span>
|
||||
</div>
|
||||
<div class="info-tech-item">
|
||||
<iconify-icon icon="mdi:language-css3" width="32" height="32"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.TechStack.CSS3}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="info-modal-github-subtext">{{.UI.InfoModal.ViewSourceSubtext}}</p>
|
||||
<a href="https://github.com/juanatsap/cv-site" target="_blank" rel="noopener noreferrer" class="info-modal-github">
|
||||
<iconify-icon icon="mdi:github" width="24" height="24"></iconify-icon>
|
||||
<span>{{.UI.InfoModal.ViewSource}}</span>
|
||||
<iconify-icon icon="mdi:arrow-right" width="20" height="20"></iconify-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -0,0 +1,25 @@
|
||||
{{define "pdf-modal"}}
|
||||
<!-- PDF Export Modal -->
|
||||
<div id="pdf-modal" class="info-modal no-print" onclick="closePdfModalOnBackdrop(event)">
|
||||
<div class="info-modal-content" onclick="event.stopPropagation()">
|
||||
<button class="info-modal-close" onclick="closePdfModal()" aria-label="Close">
|
||||
<iconify-icon icon="mdi:close" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
|
||||
<div class="info-modal-header">
|
||||
<div class="icon" style="font-size: 3rem; margin-bottom: 1rem;">🚧</div>
|
||||
<h2>{{if eq .Lang "es"}}Exportación PDF - En Desarrollo{{else}}PDF Export - Work in Progress{{end}}</h2>
|
||||
</div>
|
||||
|
||||
<div class="info-modal-body">
|
||||
<p class="info-modal-description">
|
||||
{{if eq .Lang "es"}}
|
||||
La función de exportación a PDF está siendo mejorada. Por favor, usa el botón <strong>Imprimir Amigable</strong> en su lugar (Ctrl+P o Cmd+P para guardar como PDF).
|
||||
{{else}}
|
||||
The PDF export feature is currently being improved. Please use the <strong>Print Friendly</strong> button instead (Ctrl+P or Cmd+P to save as PDF).
|
||||
{{end}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -0,0 +1,41 @@
|
||||
{{define "action-bar"}}
|
||||
<!-- Single Black Bar with Everything -->
|
||||
<div class="action-bar no-print" role="navigation" aria-label="Language and export controls">
|
||||
<div class="action-bar-content">
|
||||
<!-- Left: Site Title + Hamburger Menu + Language -->
|
||||
<div class="site-title">
|
||||
<!-- Left group: Logo + Hamburger -->
|
||||
<div class="site-title-left">
|
||||
<a href="/" class="site-logo-link" id="logoLink" aria-label="Home">
|
||||
<iconify-icon icon="mdi:file-account" width="24" height="24" class="site-icon"></iconify-icon>
|
||||
</a>
|
||||
|
||||
<!-- Hamburger Menu Button -->
|
||||
<button class="hamburger-btn" aria-label="Toggle navigation menu">
|
||||
<iconify-icon icon="mdi:menu" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Center: Title -->
|
||||
<a href="/" class="site-title-link" id="titleLink">
|
||||
<iconify-icon icon="mdi:file-account" width="20" height="20" class="site-icon-mobile"></iconify-icon>
|
||||
<span class="site-title-text">CV JAMR<span class="site-title-year"> - {{.CurrentYear}}</span></span>
|
||||
</a>
|
||||
|
||||
<!-- Right: Language selector -->
|
||||
{{template "language-selector" .}}
|
||||
</div>
|
||||
|
||||
{{template "view-controls" .}}
|
||||
{{template "action-buttons" .}}
|
||||
|
||||
<span id="loading"
|
||||
class="htmx-indicator"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
aria-label="Loading">
|
||||
<span class="loader"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -0,0 +1,19 @@
|
||||
{{define "action-buttons"}}
|
||||
<!-- Right: Action buttons -->
|
||||
<div class="action-buttons-right">
|
||||
<button
|
||||
class="action-btn pdf-btn"
|
||||
onclick="openPdfModal()"
|
||||
aria-label="{{if eq .Lang "es"}}Descargar como PDF{{else}}Download as PDF{{end}}">
|
||||
<iconify-icon icon="mdi:download" width="18" height="18"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Descargar como PDF{{else}}Download as PDF{{end}}
|
||||
</button>
|
||||
<button
|
||||
class="action-btn print-btn"
|
||||
onclick="printFriendly()"
|
||||
aria-label="{{if eq .Lang "es"}}Imprimir amigable{{else}}Print Friendly{{end}}">
|
||||
<iconify-icon icon="mdi:leaf" width="18" height="18"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Imprimir amigable{{else}}Print Friendly{{end}}
|
||||
</button>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -0,0 +1,145 @@
|
||||
{{define "hamburger-menu"}}
|
||||
<!-- Navigation Menu (Hidden by default) -->
|
||||
<nav id="navigation-menu" class="navigation-menu no-print" role="navigation" aria-label="CV sections">
|
||||
<div class="menu-content">
|
||||
<!-- CV Sections - Quick Navigation -->
|
||||
<div class="menu-item-submenu">
|
||||
<a href="#" class="menu-item has-submenu">
|
||||
<iconify-icon icon="mdi:menu" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Secciones CV{{else}}CV Sections{{end}}</span>
|
||||
<iconify-icon icon="mdi:chevron-right" width="16" height="16" class="submenu-arrow"></iconify-icon>
|
||||
</a>
|
||||
<div class="submenu-content">
|
||||
<a href="#education" class="menu-item" onclick="scrollToSection('education')">
|
||||
<iconify-icon icon="mdi:school" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Formación{{else}}Training{{end}}</span>
|
||||
</a>
|
||||
<a href="#skills" class="menu-item" onclick="scrollToSection('skills')">
|
||||
<iconify-icon icon="mdi:brain" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Competencias{{else}}Skills{{end}}</span>
|
||||
</a>
|
||||
<a href="#experience" class="menu-item" onclick="scrollToSection('experience')">
|
||||
<iconify-icon icon="mdi:office-building" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Experiencia{{else}}Experience{{end}}</span>
|
||||
</a>
|
||||
<a href="#awards" class="menu-item" onclick="scrollToSection('awards')">
|
||||
<iconify-icon icon="mdi:trophy" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Premios y Reconocimientos{{else}}Awards{{end}}</span>
|
||||
</a>
|
||||
<a href="#projects" class="menu-item" onclick="scrollToSection('projects')">
|
||||
<iconify-icon icon="mdi:web" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Proyectos Personales / Freelance{{else}}Personal / Freelance Projects{{end}}</span>
|
||||
</a>
|
||||
<a href="#courses" class="menu-item" onclick="scrollToSection('courses')">
|
||||
<iconify-icon icon="mdi:school" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Cursos Realizados{{else}}Courses{{end}}</span>
|
||||
</a>
|
||||
<a href="#languages" class="menu-item" onclick="scrollToSection('languages')">
|
||||
<iconify-icon icon="mdi:translate" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Idiomas{{else}}Languages{{end}}</span>
|
||||
</a>
|
||||
<a href="#references" class="menu-item" onclick="scrollToSection('references')">
|
||||
<iconify-icon icon="mdi:link-variant" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Referencias{{else}}References{{end}}</span>
|
||||
</a>
|
||||
<a href="#other" class="menu-item" onclick="scrollToSection('other')">
|
||||
<iconify-icon icon="mdi:information" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Otros{{else}}Other{{end}}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions Section -->
|
||||
<div class="menu-section-wrapper">
|
||||
<div class="menu-item menu-item-header">
|
||||
<iconify-icon icon="mdi:cog-outline" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Acciones Rápidas{{else}}Quick Actions{{end}}</span>
|
||||
</div>
|
||||
|
||||
<a href="#" class="menu-item menu-item-action" onclick="collapseAllSections(event)">
|
||||
<iconify-icon icon="mdi:arrow-collapse-all" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Colapsar Todo{{else}}Collapse All{{end}}</span>
|
||||
</a>
|
||||
<a href="#" class="menu-item menu-item-action" onclick="expandAllSections(event)">
|
||||
<iconify-icon icon="mdi:arrow-expand-all" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Expandir Todo{{else}}Expand All{{end}}</span>
|
||||
</a>
|
||||
<a href="#" id="show-zoom-menu-btn" class="menu-item menu-item-action" onclick="showZoomControl(event)" style="display: none;">
|
||||
<iconify-icon icon="mdi:magnify" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Zoom{{else}}Zoom{{end}}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- View Controls in menu (visible only on mobile < 900px) -->
|
||||
<div class="menu-controls-section">
|
||||
<div class="menu-item menu-item-header">
|
||||
<iconify-icon icon="mdi:tune-variant" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Controles de Vista{{else}}View Controls{{end}}</span>
|
||||
</div>
|
||||
|
||||
<!-- CV Length toggle -->
|
||||
<div class="menu-control-item">
|
||||
<label class="menu-control-label">
|
||||
<iconify-icon icon="mdi:file-document-outline" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Longitud{{else}}Length{{end}}</span>
|
||||
</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="lengthToggleMenu" onchange="toggleCVLength()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:file-document-outline" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:file-document-multiple-outline" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Logo toggle -->
|
||||
<div class="menu-control-item">
|
||||
<label class="menu-control-label">
|
||||
<iconify-icon icon="mdi:image-multiple-outline" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Logos{{else}}Logos{{end}}</span>
|
||||
</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="logoToggleMenu" checked onchange="toggleLogos()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:image-off-outline" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:image-multiple-outline" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Theme toggle -->
|
||||
<div class="menu-control-item">
|
||||
<label class="menu-control-label">
|
||||
<iconify-icon icon="mdi:page-layout-sidebar-left" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Vista{{else}}View{{end}}</span>
|
||||
</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="themeToggleMenu" onchange="toggleTheme()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:page-layout-sidebar-left" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:page-layout-body" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons in menu (visible only on mobile < 900px) -->
|
||||
<div class="menu-actions-section">
|
||||
<div class="menu-item menu-item-header">
|
||||
<iconify-icon icon="mdi:lightning-bolt" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Acciones{{else}}Actions{{end}}</span>
|
||||
</div>
|
||||
|
||||
<button class="menu-action-btn" onclick="openPdfModal()">
|
||||
<iconify-icon icon="mdi:download" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Descargar como PDF{{else}}Download as PDF{{end}}</span>
|
||||
</button>
|
||||
|
||||
<button class="menu-action-btn" onclick="printFriendly()">
|
||||
<iconify-icon icon="mdi:leaf" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Imprimir amigable{{else}}Print Friendly{{end}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
{{end}}
|
||||
@@ -0,0 +1,11 @@
|
||||
{{define "language-selector"}}
|
||||
<!-- Language selector -->
|
||||
<div class="language-selector">
|
||||
<button class="selector-btn {{if eq .Lang "en"}}active{{end}}" data-short="EN" onclick="selectLanguage('en')" aria-label="English">
|
||||
English
|
||||
</button>
|
||||
<button class="selector-btn {{if eq .Lang "es"}}active{{end}}" data-short="ES" onclick="selectLanguage('es')" aria-label="Español">
|
||||
Español
|
||||
</button>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -0,0 +1,40 @@
|
||||
{{define "view-controls"}}
|
||||
<!-- Center: View controls with labels -->
|
||||
<div class="view-controls-center">
|
||||
<!-- CV Length toggle -->
|
||||
<div class="selector-group">
|
||||
<label class="selector-label">{{if eq .Lang "es"}}Longitud{{else}}Length{{end}}:</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="lengthToggle" onchange="toggleCVLength()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:file-document-outline" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:file-document-multiple-outline" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Logo toggle -->
|
||||
<div class="selector-group">
|
||||
<label class="selector-label">{{if eq .Lang "es"}}Logos{{else}}Logos{{end}}:</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="logoToggle" checked onchange="toggleLogos()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:image-off-outline" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:image-multiple-outline" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Theme toggle -->
|
||||
<div class="selector-group">
|
||||
<label class="selector-label">{{if eq .Lang "es"}}Vista{{else}}View{{end}}:</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="themeToggle" onchange="toggleTheme()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:page-layout-sidebar-left" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:page-layout-body" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -0,0 +1,40 @@
|
||||
{{define "section-awards"}}
|
||||
<!-- Awards Section -->
|
||||
{{if .CV.Awards}}
|
||||
<section id="awards" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:trophy" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Premios y Reconocimientos{{else}}Awards{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Awards}}
|
||||
<div class="award-item">
|
||||
{{if .AwardLogo}}
|
||||
<div class="award-logo">
|
||||
<img src="/static/images/companies/{{.AwardLogo}}" alt="{{.Title}} logo" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:trophy\' width=\'60\' height=\'60\' class=\'default-award-icon\'></iconify-icon>'">
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="award-content">
|
||||
<strong>{{.Title}}</strong><br>
|
||||
<small>{{.Issuer}} - {{.Date}}</small>
|
||||
|
||||
{{if .ShortDescription}}
|
||||
<p class="award-desc short-desc">{{.ShortDescription | safeHTML}}</p>
|
||||
{{end}}
|
||||
|
||||
{{if .Responsibilities}}
|
||||
<ul class="responsibilities long-only">
|
||||
{{range .Responsibilities}}
|
||||
<li>{{. | safeHTML}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,44 @@
|
||||
{{define "section-courses"}}
|
||||
<!-- Courses Section -->
|
||||
{{if .CV.Courses}}
|
||||
<section id="courses" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:school" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Cursos Realizados{{else}}Courses{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Courses}}
|
||||
<div class="course-item">
|
||||
{{if .CourseLogo}}
|
||||
<div class="course-icon">
|
||||
<img src="/static/images/courses/{{.CourseLogo}}" alt="{{.Title}} logo" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:school\' width=\'80\' height=\'80\' class=\'default-course-icon\'></iconify-icon>'">
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="course-icon">
|
||||
<iconify-icon icon="mdi:school" width="80" height="80" class="default-course-icon"></iconify-icon>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="course-content">
|
||||
<strong>{{.Title}}</strong><br>
|
||||
<small>{{.Institution}} - {{.Date}} - ({{.Location}})</small>
|
||||
|
||||
{{if .ShortDescription}}
|
||||
<p class="course-desc short-desc">{{.ShortDescription}}</p>
|
||||
{{end}}
|
||||
|
||||
{{if .Responsibilities}}
|
||||
<ul class="responsibilities long-only">
|
||||
{{range .Responsibilities}}
|
||||
<li>{{. | safeHTML}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,18 @@
|
||||
{{define "section-education"}}
|
||||
<!-- Education -->
|
||||
<section id="education" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:school" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Formación{{else}}Training{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Education}}
|
||||
<div class="education-item">
|
||||
<strong>{{.Degree}}</strong> ({{.StartDate}}-{{.EndDate}}) {{if eq $.Lang "es"}}obtenido de{{else}}obtained from the{{end}} <strong>{{.Institution}}</strong> ({{.Location}})
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
@@ -0,0 +1,44 @@
|
||||
{{define "section-experience"}}
|
||||
<!-- Experience -->
|
||||
<section id="experience" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:office-building" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Experiencia{{else}}Experience{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
|
||||
{{range .CV.Experience}}
|
||||
<div class="experience-item">
|
||||
<div class="company-logo">
|
||||
{{if .CompanyLogo}}
|
||||
<img src="/static/images/companies/{{.CompanyLogo}}" alt="{{.Company}} logo" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:office-building\' width=\'60\' height=\'60\' class=\'default-company-icon\'></iconify-icon>'">
|
||||
{{else}}
|
||||
<iconify-icon icon="mdi:office-building" width="60" height="60" class="default-company-icon"></iconify-icon>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="experience-content">
|
||||
<strong>{{.Position}}{{if .Company}} - {{if .CompanyURL}}<a href="{{.CompanyURL}}" target="_blank" rel="noopener noreferrer">{{.Company}}</a>{{else}}{{.Company}}{{end}}{{if .Duration}} - <span class="duration-text">{{.Duration}}</span>{{end}}{{end}}</strong>
|
||||
{{if .Current}}<span class="current-badge">{{if eq $.Lang "es"}}ACTUAL{{else}}CURRENT{{end}}</span>{{end}}
|
||||
{{if .Expired}}<span class="expired-badge">{{if eq $.Lang "es"}}EXPIRADO{{else}}EXPIRED{{end}}</span>{{end}}
|
||||
<br>
|
||||
<small>{{.StartDate}} / {{if .Current}}{{if eq $.Lang "es"}}presente{{else}}now{{end}}{{else}}{{.EndDate}}{{end}} - ({{.Location}})</small>
|
||||
|
||||
{{if .ShortDescription}}
|
||||
<p class="experience-desc short-desc">{{.ShortDescription | safeHTML}}</p>
|
||||
{{end}}
|
||||
|
||||
{{if .Responsibilities}}
|
||||
<ul class="responsibilities long-only">
|
||||
{{range .Responsibilities}}
|
||||
<li>{{. | safeHTML}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
@@ -0,0 +1,19 @@
|
||||
{{define "section-header"}}
|
||||
<!-- Header with Name and Photo -->
|
||||
<div class="cv-header">
|
||||
<div class="cv-header-content">
|
||||
<div class="cv-header-left">
|
||||
<h1 class="cv-name">Moreno Rubio, Juan Andrés</h1>
|
||||
<p class="years-experience">{{.YearsOfExperience}} {{if eq .Lang "es"}}años de experiencia{{else}}years of experience{{end}}</p>
|
||||
|
||||
<!-- Photo positioned for mobile (centered between name and intro) -->
|
||||
<div class="cv-photo">
|
||||
<img src="/static/images/profile/dni.jpeg" alt="{{.CV.Personal.Name}}" onerror="this.src='/static/images/profile/placeholder.svg'">
|
||||
</div>
|
||||
|
||||
<!-- Intro/Excerpt Text - No section heading, just the text -->
|
||||
<div class="intro-text">{{.CV.Summary}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -0,0 +1,18 @@
|
||||
{{define "section-languages"}}
|
||||
<!-- Languages Section -->
|
||||
<section id="languages" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:translate" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Idiomas{{else}}Languages{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Languages}}
|
||||
<div class="language-item">
|
||||
<strong>{{.Language}}:</strong> {{.Proficiency}}{{if .Detail}} {{.Detail}}{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
@@ -0,0 +1,18 @@
|
||||
{{define "section-other"}}
|
||||
<!-- Other Section (Driver's License) -->
|
||||
{{if .CV.Other.DriverLicense}}
|
||||
<section id="other" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:information" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Otros{{else}}Other{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
<div class="other-content">
|
||||
{{if eq .Lang "es"}}Carnet de conducir tipo <strong>{{.CV.Other.DriverLicense}}</strong>{{else}}Driving License type <strong>{{.CV.Other.DriverLicense}}</strong>{{end}}
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,66 @@
|
||||
{{define "section-projects"}}
|
||||
<!-- Projects Section -->
|
||||
{{if .CV.Projects}}
|
||||
<section id="projects" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:web" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Proyectos Personales / Freelance{{else}}Personal / Freelance Projects{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.Projects}}
|
||||
<div class="project-item">
|
||||
{{if .ProjectLogo}}
|
||||
<div class="project-icon">
|
||||
<img src="/static/images/projects/{{.ProjectLogo}}" alt="{{.Title}} logo" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:web\' width=\'80\' height=\'80\' class=\'default-project-icon\'></iconify-icon>'">
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="project-icon">
|
||||
<iconify-icon icon="mdi:web" width="80" height="80" class="default-project-icon"></iconify-icon>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="project-content">
|
||||
<strong>
|
||||
{{if .ProjectName}}
|
||||
{{if .URL}}<a href="{{.URL}}" target="_blank" rel="noopener noreferrer">{{.ProjectName}}</a>{{else}}{{.ProjectName}}{{end}}{{if .ProjectDesc}} - {{.ProjectDesc}}{{end}}
|
||||
{{else}}
|
||||
{{if .URL}}<a href="{{.URL}}" target="_blank" rel="noopener noreferrer">{{.Title}}</a>{{else}}{{.Title}}{{end}}
|
||||
{{end}}
|
||||
</strong>
|
||||
{{if .Current}}<span class="live-badge"><iconify-icon icon="mdi:wifi" width="14" height="14"></iconify-icon>LIVE</span>{{end}}
|
||||
{{if .MaintainedBy}}<span class="maintained-badge">{{if eq $.Lang "es"}}MANTENIDO POR{{else}}MAINTAINED BY{{end}} {{.MaintainedBy}}</span>{{end}}
|
||||
<br>
|
||||
<small>{{if .StartDate}}{{.StartDate}}{{if .Current}}{{if .DynamicDate}} / {{.DynamicDate}}{{else}} / {{if eq $.Lang "es"}}presente{{else}}ahora{{end}}{{end}}{{end}}{{end}} - ({{.Location}})</small>
|
||||
|
||||
{{if .ShortDescription}}
|
||||
<p class="project-desc short-desc">{{.ShortDescription | safeHTML}}</p>
|
||||
{{end}}
|
||||
|
||||
{{if .Responsibilities}}
|
||||
<ul class="responsibilities long-only">
|
||||
{{range .Responsibilities}}
|
||||
<li>{{. | safeHTML}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
|
||||
{{if .Technologies}}
|
||||
<div class="project-technologies long-only">
|
||||
<strong>{{if eq $.Lang "es"}}Tecnologías:{{else}}Technologies:{{end}}</strong>
|
||||
{{range $index, $tech := .Technologies}}{{if $index}}, {{end}}{{$tech}}{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<!-- Link to full portfolio -->
|
||||
<div class="projects-footer">
|
||||
<p>{{if eq .Lang "es"}}Ver todos los proyectos en mi{{else}}See all projects on my{{end}}
|
||||
<a href="{{.CV.Personal.Domestika}}" target="_blank" rel="noopener noreferrer"><strong>{{if eq .Lang "es"}}portfolio de Domestika{{else}}Domestika portfolio{{end}}</strong></a></p>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,20 @@
|
||||
{{define "section-references"}}
|
||||
<!-- References Section -->
|
||||
{{if .CV.References}}
|
||||
<section id="references" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:link-variant" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Referencias{{else}}References{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
{{range .CV.References}}
|
||||
<div class="reference-item">
|
||||
{{if .TextBefore}}{{.TextBefore}} {{end}}{{if eq .Action "downloadPDF"}}<a href="{{.URL}}" onclick="event.preventDefault(); openPdfModal(); return false;"><strong>{{if .LinkText}}{{.LinkText}}{{else}}{{.Title}}{{end}}</strong></a>{{else}}<a href="{{.URL}}" {{if and (eq .Type "cv") (ne .URL "/?lang=en") (ne .URL "/?lang=es")}}download{{else}}target="_blank" rel="noopener noreferrer"{{end}}><strong>{{if .LinkText}}{{.LinkText}}{{else}}{{.Title}}{{end}}</strong></a>{{end}}{{if .TextAfter}} {{.TextAfter}}{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,20 @@
|
||||
{{define "section-skills-summary"}}
|
||||
<!-- Skills Summary -->
|
||||
<section id="skills" class="cv-section">
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:brain" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Competencias{{else}}Skills{{end}}
|
||||
</h3>
|
||||
</summary>
|
||||
<p class="summary-text">
|
||||
{{if eq .Lang "es"}}
|
||||
Desarrollador <strong>full-stack</strong> con experiencia en <strong>Go</strong>, <strong>Node.js</strong>, <strong>React</strong> y <strong>HTMX</strong> para <strong>aplicaciones modernas</strong>, además de conocimientos en Java y PHP para proyectos legacy. He trabajado en <strong>unos 20 sitios web</strong> y realizado <strong>consultoría para 35-40 clientes internacionales</strong>, desde e-commerce y plataformas empresariales hasta <strong>sistemas de autenticación</strong> que gestionan <strong>millones de usuarios</strong>. Familiarizado con flujos de trabajo asistidos por <strong>IA</strong> y gestión de infraestructura (<strong>Linux</strong>, <strong>Docker</strong>, <strong>CI/CD</strong>). Me adapto bien tanto al trabajo independiente como colaborativo en equipos internacionales.
|
||||
{{else}}
|
||||
<strong>Full-stack</strong> developer with experience in <strong>Go</strong>, <strong>Node.js</strong>, <strong>React</strong>, and <strong>HTMX</strong> for <strong>modern applications</strong>, plus Java and PHP knowledge for legacy projects. I've worked on <strong>around 20 websites</strong> and provided <strong>consulting for 35-40 international clients</strong>, from e-commerce and enterprise platforms to <strong>authentication systems</strong> managing <strong>millions of users</strong>. Familiar with <strong>AI-assisted development</strong> workflows and infrastructure management (<strong>Linux</strong>, <strong>Docker</strong>, <strong>CI/CD</strong>). I adapt well to both independent work and collaborative teams across different countries.
|
||||
{{end}}
|
||||
</p>
|
||||
</details>
|
||||
</section>
|
||||
{{end}}
|
||||
@@ -0,0 +1,6 @@
|
||||
{{define "back-to-top"}}
|
||||
<!-- Back to Top Button -->
|
||||
<button id="back-to-top" class="back-to-top no-print" aria-label="{{if eq .Lang "es"}}Volver arriba{{else}}Back to top{{end}}" style="display: none;">
|
||||
<iconify-icon icon="mdi:arrow-up" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
{{end}}
|
||||
@@ -0,0 +1,8 @@
|
||||
{{define "error-toast"}}
|
||||
<!-- Error Toast -->
|
||||
<div id="error-toast" class="error-toast no-print" role="alert" aria-live="assertive" style="display: none;">
|
||||
<span class="error-icon">⚠️</span>
|
||||
<span id="error-message"></span>
|
||||
<button onclick="this.parentElement.style.display='none'" aria-label="Close error message" class="error-close">×</button>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -0,0 +1,6 @@
|
||||
{{define "info-button"}}
|
||||
<!-- Info Button (Bottom Left) -->
|
||||
<button id="info-button" class="info-button no-print" aria-label="{{if eq .Lang "es"}}Información{{else}}Information{{end}}" onclick="openInfoModal()">
|
||||
<iconify-icon icon="mdi:information-outline" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
{{end}}
|
||||
@@ -0,0 +1,39 @@
|
||||
{{define "zoom-control"}}
|
||||
<!-- Zoom Control (Fixed Bottom Center, Draggable) -->
|
||||
<div id="zoom-control" class="zoom-control no-print" role="group" aria-label="{{if eq .Lang "es"}}Control de zoom{{else}}Zoom control{{end}}">
|
||||
<button
|
||||
id="zoom-close"
|
||||
class="zoom-close-btn"
|
||||
aria-label="{{if eq .Lang "es"}}Cerrar control de zoom{{else}}Close zoom control{{end}}"
|
||||
title="{{if eq .Lang "es"}}Cerrar{{else}}Close{{end}}">
|
||||
<iconify-icon icon="mdi:close" width="16" height="16"></iconify-icon>
|
||||
</button>
|
||||
|
||||
<span class="zoom-value zoom-value-min" aria-hidden="true">25</span>
|
||||
|
||||
<input
|
||||
type="range"
|
||||
id="zoom-slider"
|
||||
class="zoom-slider"
|
||||
min="25"
|
||||
max="175"
|
||||
step="1"
|
||||
value="100"
|
||||
aria-label="{{if eq .Lang "es"}}Ajustar nivel de zoom del CV{{else}}Adjust CV zoom level{{end}}"
|
||||
aria-valuemin="25"
|
||||
aria-valuemax="175"
|
||||
aria-valuenow="100"
|
||||
aria-valuetext="100%">
|
||||
|
||||
<span class="zoom-value zoom-value-max" aria-hidden="true">175</span>
|
||||
|
||||
<button
|
||||
id="zoom-reset"
|
||||
class="zoom-reset-btn"
|
||||
aria-label="{{if eq .Lang "es"}}Restablecer zoom al 100%{{else}}Reset zoom to 100%{{end}}"
|
||||
title="{{if eq .Lang "es"}}Restablecer{{else}}Reset{{end}}"
|
||||
aria-live="polite">
|
||||
<span id="zoom-value-current">100</span>
|
||||
</button>
|
||||
</div>
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user