# Template Rendering Diagram ## Template System Architecture ``` ┌──────────────────────────────────────────────────────────────┐ │ Template System Architecture │ └──────────────────────────────────────────────────────────────┘ internal/templates/ ├── manager.go Template manager (caching, rendering) └── functions.go Custom template functions templates/ ├── index.html Main page template ├── partials/ Reusable components │ ├── header.html │ ├── footer.html │ ├── cv_content.html │ ├── experience.html │ ├── education.html │ ├── skills.html │ └── languages.html └── layouts/ Layout templates └── base.html ``` ## Template Manager ``` ┌──────────────────────────────────────────────────────────────┐ │ Template Manager (internal/templates/manager.go) │ ├──────────────────────────────────────────────────────────────┤ │ type Manager struct { │ │ templates map[string]*template.Template │ │ config *config.TemplateConfig │ │ mu sync.RWMutex // Thread-safe access │ │ } │ │ │ │ type TemplateConfig struct { │ │ Dir string // templates/ │ │ PartialsDir string // templates/partials/ │ │ HotReload bool // Reload on every render │ │ } │ │ │ │ Methods: │ │ ├─ NewManager(config) (*Manager, error) │ │ │ └─→ Initialize and load all templates │ │ │ │ │ ├─ Render(w, name, data) error │ │ │ └─→ Execute template with data │ │ │ │ │ ├─ loadTemplates() error │ │ │ └─→ Parse and cache all templates │ │ │ │ │ └─ reloadIfNeeded() error │ │ └─→ Reload templates if hot reload enabled │ └──────────────────────────────────────────────────────────────┘ ``` ## Template Loading Flow ``` ┌────────────────────────────────────────────────────────────┐ │ Template Loading Flow │ └────────────────────────────────────────────────────────────┘ Application Start │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ NewManager(config) │ │ (internal/templates/manager.go) │ │ │ │ 1. Create manager │ │ m := &Manager{ │ │ templates: make(map[string]*template.Template), │ │ config: config, │ │ } │ │ │ │ 2. Load all templates │ │ if err := m.loadTemplates(); err != nil { │ │ return nil, err │ │ } │ │ │ │ 3. Return manager │ │ return m, nil │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ loadTemplates() │ │ │ │ 1. Scan template directory │ │ files, err := filepath.Glob(config.Dir + "/*.html") │ │ │ │ 2. For each template file: │ │ ├─ Create new template │ │ │ tmpl := template.New(name) │ │ │ │ │ ├─ Add custom functions │ │ │ tmpl.Funcs(customFunctions()) │ │ │ │ │ ├─ Parse main template │ │ │ tmpl.ParseFiles(file) │ │ │ │ │ ├─ Parse partials │ │ │ tmpl.ParseGlob(config.PartialsDir + "/*.html") │ │ │ │ │ └─ Cache template │ │ m.templates[name] = tmpl │ │ │ │ 3. Log loaded templates │ │ log.Printf("Loaded %d templates", len(m.templates)) │ └─────────────────────────────────────────────────────────────┘ ``` ## Template Rendering Flow ``` ┌────────────────────────────────────────────────────────────┐ │ Template Rendering Flow │ └────────────────────────────────────────────────────────────┘ Handler calls Render() │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Manager.Render(w, "index.html", data) │ │ (internal/templates/manager.go) │ │ │ │ 1. Lock for reading │ │ m.mu.RLock() │ │ defer m.mu.RUnlock() │ │ │ │ 2. Hot reload check │ │ if m.config.HotReload { │ │ m.mu.RUnlock() │ │ m.mu.Lock() │ │ m.loadTemplates() // Reload all templates │ │ m.mu.Unlock() │ │ m.mu.RLock() │ │ } │ │ │ │ 3. Get template from cache │ │ tmpl, ok := m.templates[name] │ │ if !ok { │ │ return fmt.Errorf("template not found: %s", name) │ │ } │ │ │ │ 4. Execute template │ │ err := tmpl.Execute(w, data) │ │ if err != nil { │ │ return fmt.Errorf("template execution: %w", err) │ │ } │ │ │ │ 5. Return │ │ return nil │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Template Execution │ │ │ │ 1. Parse template directives │ │ {{.CV.Personal.Name}} │ │ {{range .CV.Experience}}...{{end}} │ │ {{template "partials/header.html" .}} │ │ │ │ 2. Execute custom functions │ │ {{formatDate .StartDate}} │ │ {{join .Highlights ", "}} │ │ {{lower .CVLanguage}} │ │ │ │ 3. Include partials │ │ {{template "partials/cv_content.html" .}} │ │ {{template "partials/experience.html" .}} │ │ │ │ 4. Generate HTML │ │ Write to http.ResponseWriter │ └─────────────────────────────────────────────────────────────┘ ``` ## Template Hierarchy ``` ┌────────────────────────────────────────────────────────────┐ │ Template Hierarchy │ └────────────────────────────────────────────────────────────┘ index.html (Main Template) │ ├─→ {{template "partials/header.html" .}} │ └─→ Navigation, language toggle, theme toggle │ ├─→ {{template "partials/cv_content.html" .}} │ │ │ ├─→ {{template "partials/experience.html" .}} │ │ └─→ {{range .CV.Experience}} │ │ ├─ Company, position, dates │ │ ├─ {{.Duration}} (calculated) │ │ └─ {{range .Highlights}} │ │ │ ├─→ {{template "partials/education.html" .}} │ │ └─→ {{range .CV.Education}} │ │ ├─ Institution, degree, field │ │ └─ Dates, GPA, honors │ │ │ ├─→ {{template "partials/skills.html" .}} │ │ └─→ {{range .SkillsColumns}} │ │ └─ {{range .}} │ │ ├─ Skill name │ │ ├─ Level badge │ │ └─ Icon (if enabled) │ │ │ └─→ {{template "partials/languages.html" .}} │ └─→ {{range .CV.Languages}} │ ├─ Language name │ └─ Proficiency level │ └─→ {{template "partials/footer.html" .}} └─→ PDF export button, copyright ``` ## Template Data Structure ``` ┌────────────────────────────────────────────────────────────┐ │ Template Data Structure │ └────────────────────────────────────────────────────────────┘ Data passed to templates: map[string]interface{}{ // CV Data "CV": &cvmodel.CV{ Personal: cvmodel.Personal{ Name: "John Doe", Title: "Senior Software Engineer", Email: "john@example.com", Location: "San Francisco, CA", }, Experience: []cvmodel.Experience{ { Company: "Tech Corp", Position: "Senior Engineer", StartDate: "2020-01", EndDate: "", Current: true, Duration: "3 years 2 months", // Calculated Highlights: []string{...}, }, }, Education: []cvmodel.Education{...}, Skills: cvmodel.Skills{...}, Languages: []cvmodel.Language{...}, }, // UI Strings "UI": &uimodel.UI{ Sections: uimodel.Sections{ Summary: "Professional Summary", Experience: "Work Experience", Education: "Education", Skills: "Technical Skills", Languages: "Languages", }, Buttons: uimodel.Buttons{...}, Messages: uimodel.Messages{...}, }, // User Preferences "Preferences": &middleware.Preferences{ CVLength: "long", CVIcons: "show", CVLanguage: "es", CVTheme: "default", ColorTheme: "light", }, // Processed Data "SkillsColumns": [][]cvmodel.Skill{ []cvmodel.Skill{...}, // Column 1 []cvmodel.Skill{...}, // Column 2 []cvmodel.Skill{...}, // Column 3 }, // SEO Metadata "PageTitle": "John Doe - Senior Software Engineer", "MetaDescription": "Professional CV of John Doe...", "CanonicalURL": "http://localhost:8080/", "OGImage": "http://localhost:8080/static/images/og-image.png", } ``` ## Custom Template Functions ``` ┌────────────────────────────────────────────────────────────┐ │ Custom Template Functions │ │ (internal/templates/functions.go) │ └────────────────────────────────────────────────────────────┘ template.FuncMap{ // String manipulation "lower": strings.ToLower, "upper": strings.ToUpper, "title": strings.Title, // Date formatting "formatDate": func(date string) string { if date == "" { return "Present" } t, _ := time.Parse("2006-01", date) return t.Format("Jan 2006") }, // Array operations "join": strings.Join, "split": strings.Split, // Math "add": func(a, b int) int { return a + b }, "multiply": func(a, b int) int { return a * b }, // Conditional helpers "eq": func(a, b interface{}) bool { return a == b }, "ne": func(a, b interface{}) bool { return a != b }, // HTML safety "safe": func(s string) template.HTML { return template.HTML(s) }, } Usage in templates: {{formatDate .StartDate}} // "2020-01" → "Jan 2020" {{join .Highlights ", "}} // ["foo", "bar"] → "foo, bar" {{if eq .CVLength "long"}} {{end}} {{.Description | safe}} // Render HTML without escaping ``` ## Template Conditionals ``` ┌────────────────────────────────────────────────────────────┐ │ Template Conditionals │ └────────────────────────────────────────────────────────────┘ Show/Hide based on CV length: {{if eq .Preferences.CVLength "long"}}