package handlers import ( "fmt" "log" "net/http" "os" "os/exec" "strings" "time" "github.com/juanatsap/cv-site/internal/models" "github.com/juanatsap/cv-site/internal/pdf" "github.com/juanatsap/cv-site/internal/templates" ) // CVHandler handles CV-related requests type CVHandler struct { templates *templates.Manager pdfGenerator *pdf.Generator serverAddr string } // NewCVHandler creates a new CV handler func NewCVHandler(tmpl *templates.Manager, serverAddr string) *CVHandler { return &CVHandler{ templates: tmpl, pdfGenerator: pdf.NewGenerator(30 * time.Second), serverAddr: serverAddr, } } // Home renders the full CV page func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) { // Get language from query parameter, default to English lang := r.URL.Query().Get("lang") if lang == "" { lang = "en" } // Validate language if lang != "en" && lang != "es" { HandleError(w, r, BadRequestError("Unsupported language. Use 'en' or 'es'")) return } // Load CV data cv, err := models.LoadCV(lang) if err != nil { HandleError(w, r, DataLoadError(err, "CV")) return } // Load UI translations ui, err := models.LoadUI(lang) if err != nil { HandleError(w, r, DataLoadError(err, "UI")) return } // Calculate duration for each experience for i := range cv.Experience { cv.Experience[i].Duration = calculateDuration( cv.Experience[i].StartDate, cv.Experience[i].EndDate, cv.Experience[i].Current, lang, ) } // Process projects for dynamic dates for i := range cv.Projects { processProjectDates(&cv.Projects[i], lang) } // Split skills between left and right sidebars skillsLeft, skillsRight := splitSkills(cv.Skills.Technical) // Calculate years of experience yearsOfExperience := calculateYearsOfExperience() // Get current year currentYear := time.Now().Year() // Prepare template data data := map[string]interface{}{ "CV": cv, "UI": ui, "Lang": lang, "SkillsLeft": skillsLeft, "SkillsRight": skillsRight, "YearsOfExperience": yearsOfExperience, "CurrentYear": currentYear, } // Render template tmpl, err := h.templates.Render("index.html") if err != nil { HandleError(w, r, TemplateError(err, "index.html")) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := tmpl.Execute(w, data); err != nil { HandleError(w, r, TemplateError(err, "index.html")) return } } // CVContent renders just the CV content for HTMX swaps func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request) { // Get language from query parameter lang := r.URL.Query().Get("lang") if lang == "" { lang = "en" } // Validate language if lang != "en" && lang != "es" { HandleError(w, r, BadRequestError("Unsupported language. Use 'en' or 'es'")) return } // Load CV data cv, err := models.LoadCV(lang) if err != nil { HandleError(w, r, DataLoadError(err, "CV")) return } // Load UI translations ui, err := models.LoadUI(lang) if err != nil { HandleError(w, r, DataLoadError(err, "UI")) return } // Calculate duration for each experience for i := range cv.Experience { cv.Experience[i].Duration = calculateDuration( cv.Experience[i].StartDate, cv.Experience[i].EndDate, cv.Experience[i].Current, lang, ) } // Process projects for dynamic dates for i := range cv.Projects { processProjectDates(&cv.Projects[i], lang) } // Split skills between left and right sidebars skillsLeft, skillsRight := splitSkills(cv.Skills.Technical) // Calculate years of experience yearsOfExperience := calculateYearsOfExperience() // Get current year currentYear := time.Now().Year() // Prepare template data data := map[string]interface{}{ "CV": cv, "UI": ui, "Lang": lang, "SkillsLeft": skillsLeft, "SkillsRight": skillsRight, "YearsOfExperience": yearsOfExperience, "CurrentYear": currentYear, } // Render template tmpl, err := h.templates.Render("cv-content.html") if err != nil { HandleError(w, r, TemplateError(err, "cv-content.html")) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := tmpl.Execute(w, data); err != nil { HandleError(w, r, TemplateError(err, "cv-content.html")) return } } // ExportPDF handles PDF export requests using chromedp // TEMPORARILY DISABLED - Work in progress func (h *CVHandler) ExportPDF(w http.ResponseWriter, r *http.Request) { // Get language from query parameter lang := r.URL.Query().Get("lang") if lang == "" { lang = "en" } // Validate language if lang != "en" && lang != "es" { HandleError(w, r, BadRequestError("Unsupported language. Use 'en' or 'es'")) return } log.Printf("PDF export requested but temporarily disabled (redirecting to print friendly)") // Return HTML with message and redirect to print friendly message := "PDF Export - Work in Progress" body := "The PDF export feature is currently being improved. Please use the Print Friendly button instead (Ctrl+P or Cmd+P to save as PDF)." if lang == "es" { message = "Exportación PDF - En Desarrollo" body = "La función de exportación a PDF está siendo mejorada. Por favor, usa el botón Imprimir Amigable en su lugar (Ctrl+P o Cmd+P para guardar como PDF)." } w.Header().Set("Content-Type", "text/html; charset=utf-8") w.WriteHeader(http.StatusOK) redirectMsg := "Redirecting in 5 seconds..." if lang == "es" { redirectMsg = "Redirigiendo en 5 segundos..." } html := fmt.Sprintf(`
%s