add htmx web frontend with templates and session auth
This commit is contained in:
109
portal/internal/web/templates.go
Normal file
109
portal/internal/web/templates.go
Normal file
@@ -0,0 +1,109 @@
|
||||
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
|
||||
}
|
||||
|
||||
// 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))
|
||||
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)).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
|
||||
}
|
||||
Reference in New Issue
Block a user