Initial commit: Go + HTMX CV Site
- Minimal, professional CV design with paper-on-gray layout - Bilingual support (Spanish/English) with HTMX language switching - JSON-based content management (cv-en.json, cv-es.json) - Print-optimized CSS for PDF export - Responsive design for all devices - Go backend with stdlib net/http - Clean, maintainable codebase Features: - 18+ years professional experience - SAP CDC expertise - Complete project history - Education, certifications, awards - Multi-language support Tech stack: Go, HTMX, vanilla CSS
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/juanatsap/cv-site/internal/config"
|
||||
)
|
||||
|
||||
// Manager handles template parsing and rendering
|
||||
type Manager struct {
|
||||
templates *template.Template
|
||||
config *config.TemplateConfig
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewManager creates a new template manager
|
||||
func NewManager(cfg *config.TemplateConfig) (*Manager, error) {
|
||||
m := &Manager{
|
||||
config: cfg,
|
||||
}
|
||||
|
||||
if err := m.loadTemplates(); err != nil {
|
||||
return nil, fmt.Errorf("failed to load templates: %w", err)
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// loadTemplates parses all templates from the configured directory
|
||||
func (m *Manager) loadTemplates() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
// Create template with custom functions
|
||||
funcMap := template.FuncMap{
|
||||
"iterate": func(count int) []int {
|
||||
var result []int
|
||||
for i := 0; i < count; i++ {
|
||||
result = append(result, i)
|
||||
}
|
||||
return result
|
||||
},
|
||||
"eq": func(a, b string) bool {
|
||||
return a == b
|
||||
},
|
||||
}
|
||||
|
||||
// Parse main templates
|
||||
pattern := filepath.Join(m.config.Dir, "*.html")
|
||||
tmpl, err := template.New("").Funcs(funcMap).ParseGlob(pattern)
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
log.Printf("Warning: error parsing partials: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
m.templates = tmpl
|
||||
log.Printf("✓ Templates loaded successfully from %s", m.config.Dir)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reload reloads all templates (useful for hot-reload in development)
|
||||
func (m *Manager) Reload() error {
|
||||
return m.loadTemplates()
|
||||
}
|
||||
|
||||
// Render executes a template with the given data
|
||||
func (m *Manager) Render(name string) (*template.Template, error) {
|
||||
// Hot reload in development mode
|
||||
if m.config.HotReload {
|
||||
if err := m.Reload(); err != nil {
|
||||
log.Printf("Warning: template reload failed: %v", err)
|
||||
// Continue with cached templates
|
||||
}
|
||||
}
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
tmpl := m.templates.Lookup(name)
|
||||
if tmpl == nil {
|
||||
return nil, fmt.Errorf("template %q not found", name)
|
||||
}
|
||||
|
||||
return tmpl, nil
|
||||
}
|
||||
Reference in New Issue
Block a user