Files
cv-site/templates/partials/navigation/hamburger-menu.html
T
juanatsap f91a24ea9b feat: Add plain text CV endpoint and contact form with security
Plain text endpoint:
- Add /text route for plain text CV (for curl/AI crawlers)
- Use k3a/html2text library for HTML-to-text conversion
- Add Plain Text button to hamburger menu with UI translations

Contact form feature:
- Add ContactHandler with proper email service integration
- Add CSRF protection middleware
- Add rate limiting (5 submissions/hour per IP)
- Add honeypot and timing-based bot protection
- Add input validation with detailed error messages
- Add security logging middleware
- Add browser-only middleware for API protection

Code quality:
- Fix all golangci-lint errcheck warnings for w.Write calls
- Remove duplicate getClientIP functions
- Wire up ContactHandler in routes.Setup
2025-11-30 13:47:49 +00:00

209 lines
11 KiB
HTML

{{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>{{.UI.Navigation.CvSections}}</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"
_="on click call scrollToSection(event, 'education')">
<iconify-icon icon="mdi:school" width="20" height="20"></iconify-icon>
<span>{{.UI.Navigation.Training}}</span>
</a>
<a href="#skills" class="menu-item"
_="on click call scrollToSection(event, 'skills')">
<iconify-icon icon="mdi:brain" width="20" height="20"></iconify-icon>
<span>{{.UI.Navigation.Skills}}</span>
</a>
<a href="#experience" class="menu-item"
_="on click call scrollToSection(event, 'experience')">
<iconify-icon icon="mdi:office-building" width="20" height="20"></iconify-icon>
<span>{{.UI.Navigation.Experience}}</span>
</a>
<a href="#awards" class="menu-item"
_="on click call scrollToSection(event, 'awards')">
<iconify-icon icon="mdi:trophy" width="20" height="20"></iconify-icon>
<span>{{.UI.Navigation.Awards}}</span>
</a>
<a href="#projects" class="menu-item"
_="on click call scrollToSection(event, 'projects')">
<iconify-icon icon="mdi:web" width="20" height="20"></iconify-icon>
<span>{{.UI.Navigation.Projects}}</span>
</a>
<a href="#courses" class="menu-item"
_="on click call scrollToSection(event, 'courses')">
<iconify-icon icon="mdi:school" width="20" height="20"></iconify-icon>
<span>{{.UI.Navigation.Courses}}</span>
</a>
<a href="#languages" class="menu-item"
_="on click call scrollToSection(event, 'languages')">
<iconify-icon icon="mdi:translate" width="20" height="20"></iconify-icon>
<span>{{.UI.Navigation.Languages}}</span>
</a>
<a href="#references" class="menu-item"
_="on click call scrollToSection(event, 'references')">
<iconify-icon icon="mdi:link-variant" width="20" height="20"></iconify-icon>
<span>{{.UI.Navigation.References}}</span>
</a>
<a href="#other" class="menu-item"
_="on click call scrollToSection(event, 'other')">
<iconify-icon icon="mdi:information" width="20" height="20"></iconify-icon>
<span>{{.UI.Navigation.Other}}</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>{{.UI.Navigation.QuickActions}}</span>
</div>
<a href="#" class="menu-item menu-item-action" _="on click call collapseAllSections(event)">
<iconify-icon icon="mdi:arrow-collapse-all" width="20" height="20"></iconify-icon>
<span>{{.UI.Navigation.CollapseAll}}</span>
</a>
<a href="#" class="menu-item menu-item-action" _="on click call expandAllSections(event)">
<iconify-icon icon="mdi:arrow-expand-all" width="20" height="20"></iconify-icon>
<span>{{.UI.Navigation.ExpandAll}}</span>
</a>
<a href="#" id="show-zoom-menu-btn" class="menu-item menu-item-action zoom-hidden"
_="on click call showZoomControl()">
<iconify-icon icon="mdi:magnify" width="20" height="20"></iconify-icon>
<span>{{.UI.Navigation.Zoom}}</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>{{.UI.Navigation.ViewControls}}</span>
</div>
<!-- CV Length toggle -->
<div class="menu-control-item" id="mobile-length-toggle">
<label class="menu-control-label">
<iconify-icon icon="mdi:file-document-outline" width="20" height="20"></iconify-icon>
<span>{{.UI.ViewControls.Length}}</span>
</label>
<div style="display: flex; align-items: center; gap: 8px;">
<label class="icon-toggle">
<input type="checkbox"
id="lengthToggleMenu"
{{if eq .CVLengthClass "cv-long"}}checked{{end}}
hx-post="/toggle/length?lang={{.Lang}}"
hx-swap="none"
_="on change call toggleCVLength(my.checked)">
<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>
<iconify-icon icon="mdi:loading"
class="htmx-indicator spinning small dark"
width="14"
height="14"
aria-label="Saving"></iconify-icon>
</div>
</div>
<!-- Icon toggle -->
<div class="menu-control-item" id="mobile-icon-toggle">
<label class="menu-control-label">
<iconify-icon icon="mdi:image-multiple-outline" width="20" height="20"></iconify-icon>
<span>{{.UI.ViewControls.Icons}}</span>
</label>
<div style="display: flex; align-items: center; gap: 8px;">
<label class="icon-toggle">
<input type="checkbox"
id="iconToggleMenu"
{{if .ShowIcons}}checked{{end}}
hx-post="/toggle/icons?lang={{.Lang}}"
hx-swap="none"
_="on change call toggleIcons(my.checked)">
<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>
<iconify-icon icon="mdi:loading"
class="htmx-indicator spinning small dark"
width="14"
height="14"
aria-label="Saving"></iconify-icon>
</div>
</div>
<!-- Theme toggle -->
<div class="menu-control-item" id="mobile-theme-toggle">
<label class="menu-control-label">
<iconify-icon icon="mdi:page-layout-sidebar-left" width="20" height="20"></iconify-icon>
<span>{{.UI.ViewControls.View}}</span>
</label>
<div style="display: flex; align-items: center; gap: 8px;">
<label class="icon-toggle">
<input type="checkbox"
id="themeToggleMenu"
{{if .ThemeClean}}checked{{end}}
hx-post="/toggle/theme?lang={{.Lang}}"
hx-swap="none"
_="on change call toggleTheme(my.checked)">
<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>
<iconify-icon icon="mdi:loading"
class="htmx-indicator spinning small dark"
width="14"
height="14"
aria-label="Saving"></iconify-icon>
</div>
</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>{{.UI.Navigation.Actions}}</span>
</div>
<button class="menu-action-btn menu-pdf-btn"
onclick="document.getElementById('pdf-modal').showModal()"
_="on mouseenter call syncPdfHover(true)
on mouseleave call syncPdfHover(false)">
<iconify-icon icon="catppuccin:pdf" width="20" height="20"></iconify-icon>
<span>{{.UI.Widgets.ActionButtons.DownloadPdf}}</span>
</button>
<button class="menu-action-btn menu-print-btn"
_="on click call printFriendly()
on mouseenter call syncPrintHover(true)
on mouseleave call syncPrintHover(false)">
<iconify-icon icon="mdi:leaf" width="20" height="20"></iconify-icon>
<span>{{.UI.Widgets.ActionButtons.PrintFriendly}}</span>
</button>
<button class="menu-action-btn menu-contact-btn"
onclick="document.getElementById('contact-modal').showModal()">
<iconify-icon icon="mdi:email-outline" width="20" height="20"></iconify-icon>
<span>{{.UI.Widgets.ActionButtons.Contact}}</span>
</button>
<a href="/text?lang={{.Lang}}" class="menu-action-btn menu-text-btn" target="_blank">
<iconify-icon icon="mdi:text-box-outline" width="20" height="20"></iconify-icon>
<span>{{.UI.Widgets.ActionButtons.PlainText}}</span>
</a>
</div>
</div>
</nav>
{{end}}