Files
MosisService/portal/internal/web/templates.go

132 lines
3.0 KiB
Go

package web
import (
"embed"
"html/template"
"io"
"io/fs"
"path/filepath"
"strings"
)
//go:embed templates/*
var templateFS embed.FS
// Templates holds the parsed templates
type Templates struct {
templates map[string]*template.Template
}
// Template helper functions
var templateFuncs = template.FuncMap{
"divFloat": func(a int64, b int) float64 {
return float64(a) / float64(b)
},
"add": func(a, b int) int {
return a + b
},
"sub": func(a, b int) int {
return a - b
},
"mul": func(a, b int) int {
return a * b
},
"min": func(a, b int) int {
if a < b {
return a
}
return b
},
}
// NewTemplates creates and parses all templates
func NewTemplates() (*Templates, error) {
t := &Templates{
templates: make(map[string]*template.Template),
}
// Load all layout and partial templates first
layoutFiles, err := fs.Glob(templateFS, "templates/layouts/*.html")
if err != nil {
return nil, err
}
partialFiles, err := fs.Glob(templateFS, "templates/partials/*.html")
if err != nil {
return nil, err
}
// Load page templates
pageFiles, err := fs.Glob(templateFS, "templates/pages/*.html")
if err != nil {
return nil, err
}
// Combine layouts and partials
baseFiles := append(layoutFiles, partialFiles...)
// Parse each page template with layouts and partials
for _, pageFile := range pageFiles {
files := append([]string{pageFile}, baseFiles...)
// Read and parse all files
tmpl := template.New(filepath.Base(pageFile)).Funcs(templateFuncs)
for _, file := range files {
content, err := templateFS.ReadFile(file)
if err != nil {
return nil, err
}
_, err = tmpl.Parse(string(content))
if err != nil {
return nil, err
}
}
// Extract page name (e.g., "login" from "templates/pages/login.html")
name := strings.TrimSuffix(filepath.Base(pageFile), ".html")
t.templates[name] = tmpl
}
// Also parse partials standalone for htmx partial responses
for _, partialFile := range partialFiles {
content, err := templateFS.ReadFile(partialFile)
if err != nil {
return nil, err
}
tmpl, err := template.New(filepath.Base(partialFile)).Funcs(templateFuncs).Parse(string(content))
if err != nil {
return nil, err
}
name := "partial:" + strings.TrimSuffix(filepath.Base(partialFile), ".html")
t.templates[name] = tmpl
}
return t, nil
}
// RenderPage renders a full page template
func (t *Templates) RenderPage(w io.Writer, name string, data interface{}) error {
tmpl, ok := t.templates[name]
if !ok {
return &ErrTemplateNotFound{Name: name}
}
return tmpl.ExecuteTemplate(w, "base", data)
}
// RenderPartial renders a partial template (for htmx responses)
func (t *Templates) RenderPartial(w io.Writer, name string, data interface{}) error {
tmpl, ok := t.templates["partial:"+name]
if !ok {
return &ErrTemplateNotFound{Name: name}
}
return tmpl.ExecuteTemplate(w, name, data)
}
// ErrTemplateNotFound is returned when a template is not found
type ErrTemplateNotFound struct {
Name string
}
func (e *ErrTemplateNotFound) Error() string {
return "template not found: " + e.Name
}