Files
cv-site/internal/models/cv.go
T
juanatsap 27c2f4b44f feat: enhance experience section with icons, duration, and improved styling
Experience Section Improvements:
- Increased company logo size from 48px to 64px
- Added default office building icon for companies without logos
- Increased spacing between entries (2.5rem margin, 2rem padding)
- Added visible separator lines (2px solid #ddd)
- Made dates bold (weight: 600) and larger (1.05rem)
- Changed date color to #555 for better contrast

Dynamic Duration Calculation:
- Added automatic years/months calculation for each position
- Format: "(4 years 10 months)", "(2 years)", "(6 months)"
- Smart pluralization in English and Spanish
- Handles current positions (calculates to today)
- Added Duration field to Experience model

Iconify Integration:
- Added Iconify library (v3.1.1) for icon management
- Replaced EN/ES text with round flag icons (circle-flags:us, circle-flags:es)
- Updated CV site icon to mdi:file-account
- Replaced toggle text with intuitive icons:
  * Short/Long: mdi:file-document-outline / mdi:file-document-multiple-outline
  * Logos: mdi:image-off-outline / mdi:image-multiple-outline
- Default company icon: mdi:office-building (64x64px, light gray background)

Logo Display:
- Logos now show by default (toggle checked on page load)
- Toggle controls icon visibility
- Consistent spacing with default icon placeholder

Files modified:
- internal/handlers/cv.go: Added calculateDuration() function
- internal/models/cv.go: Added Duration field to Experience struct
- templates/index.html: Iconify integration, flag icons, toggle icons
- templates/cv-content.html: Duration display, default icon logic
- static/css/main.css: Bold dates, larger font sizes
- static/css/logo-toggle.css: Icon styling, separator lines, spacing
2025-11-06 10:36:00 +00:00

174 lines
5.1 KiB
Go

package models
import (
"encoding/json"
"fmt"
"os"
)
// CV represents the complete curriculum vitae structure
type CV struct {
Personal Personal `json:"personal"`
Summary string `json:"summary"`
Experience []Experience `json:"experience"`
AIDevelopment AIDevelopment `json:"ai_development"`
Education []Education `json:"education"`
Skills Skills `json:"skills"`
Languages []Language `json:"languages"`
Projects []Project `json:"projects"`
Awards []Award `json:"awards"`
Certifications []Certification `json:"certifications"`
Courses []Course `json:"courses"`
References []Reference `json:"references"`
Other Other `json:"other"`
Meta Meta `json:"meta"`
}
type Personal struct {
Name string `json:"name"`
Title string `json:"title"`
Location string `json:"location"`
Email string `json:"email"`
Phone string `json:"phone"`
DateOfBirth string `json:"dateOfBirth"`
PlaceOfBirth string `json:"placeOfBirth"`
Citizenship string `json:"citizenship"`
LinkedIn string `json:"linkedin"`
GitHub string `json:"github"`
Behance string `json:"behance"`
Website string `json:"website"`
Photo string `json:"photo"`
}
type Experience struct {
Position string `json:"position"`
Company string `json:"company"`
CompanyURL string `json:"companyURL,omitempty"` // Optional URL for company website
CompanyLogo string `json:"companyLogo"`
Location string `json:"location"`
StartDate string `json:"startDate"`
EndDate string `json:"endDate"`
Current bool `json:"current"`
ShortDescription string `json:"shortDescription"`
Responsibilities []string `json:"responsibilities"`
Technologies []string `json:"technologies"`
Highlights []string `json:"highlights"`
Duration string `json:"-"` // Calculated field, not from JSON
}
type AIDevelopment struct {
Title string `json:"title"`
Period string `json:"period"`
Description string `json:"description"`
Skills []AISkill `json:"skills"`
Achievements []string `json:"achievements"`
}
type AISkill struct {
Category string `json:"category"`
Proficiency string `json:"proficiency"`
Items []string `json:"items"`
}
type Education struct {
Degree string `json:"degree"`
Institution string `json:"institution"`
Location string `json:"location"`
StartDate string `json:"startDate"`
EndDate string `json:"endDate"`
Field string `json:"field"`
}
type Skills struct {
Technical []SkillCategory `json:"technical"`
SoftSkills []string `json:"soft_skills"`
}
type SkillCategory struct {
Category string `json:"category"`
Proficiency int `json:"proficiency"`
Items []string `json:"items"`
}
type Language struct {
Language string `json:"language"`
Proficiency string `json:"proficiency"`
Level int `json:"level"`
Detail string `json:"detail,omitempty"` // Optional detail like "Oral (Medio/Alto) Escrito (Alto)"
}
type Project struct {
Name string `json:"name"`
Role string `json:"role"`
URL string `json:"url"`
Period string `json:"period"`
Description string `json:"description"`
Technologies []string `json:"technologies"`
Highlights []string `json:"highlights"`
}
type Award struct {
Title string `json:"title"`
Issuer string `json:"issuer"`
Date string `json:"date"`
Description string `json:"description"`
}
type Certification struct {
Name string `json:"name"`
Issuer string `json:"issuer"`
Date string `json:"date"`
Description string `json:"description"`
}
type Course struct {
Title string `json:"title"`
Institution string `json:"institution"`
Location string `json:"location"`
Date string `json:"date"`
Duration string `json:"duration"`
Description string `json:"description"`
}
type Reference struct {
Title string `json:"title"`
URL string `json:"url"`
Type string `json:"type"` // "recommendation", "portfolio", "profile", "cv", "presentation"
}
type Other struct {
DriverLicense string `json:"driverLicense"`
}
type Meta struct {
Version string `json:"version"`
LastUpdated string `json:"lastUpdated"`
Format string `json:"format"`
Language string `json:"language"`
}
// LoadCV loads CV data from a JSON file for the specified language
func LoadCV(lang string) (*CV, error) {
// Validate language
if lang != "en" && lang != "es" {
return nil, fmt.Errorf("unsupported language: %s", lang)
}
// Determine which JSON file to load
filename := fmt.Sprintf("data/cv-%s.json", lang)
// Read the JSON file
data, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("error reading file %s: %w", filename, err)
}
// Parse JSON
var cv CV
if err := json.Unmarshal(data, &cv); err != nil {
return nil, fmt.Errorf("error parsing JSON: %w", err)
}
return &cv, nil
}