package web import ( "fmt" "html/template" "net/http" "strings" "time" ) var funcMap = template.FuncMap{ "join": func(items []string, sep string) string { return strings.Join(items, sep) }, "fmtTime": func(t time.Time) string { if t.IsZero() { return "-" } return t.Format("2006-01-02 15:04:05") }, "timeAgo": timeAgo, "deref": func(t *time.Time) time.Time { if t == nil { return time.Time{} } return *t }, "deref64": func(v *int64) int64 { if v == nil { return 0 } return *v }, "slice": func(items ...int) []int { return items }, "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 }, "iterate": func(n int) []int { out := make([]int, n) for i := range out { out[i] = i } return out }, "statusClass": func(status int) string { switch { case status >= 500: return "text-danger" case status >= 400: return "text-warning" case status >= 300: return "text-info" default: return "text-success" } }, } func timeAgo(t time.Time) string { if t.IsZero() { return "never" } d := time.Since(t) switch { case d < time.Minute: return "just now" case d < time.Hour: return fmt.Sprintf("%dm ago", int(d.Minutes())) case d < 24*time.Hour: return fmt.Sprintf("%dh ago", int(d.Hours())) default: return fmt.Sprintf("%dd ago", int(d.Hours()/24)) } } var templates *template.Template func init() { t, err := template.New("").Funcs(funcMap).ParseFS(templatesFS, "templates/*.html", "templates/partials/*.html") if err != nil { panic(err) } templates = t } // PageData is the common envelope passed to every page template. type PageData struct { Active string // nav item key for sidebar highlighting Title string CSRFToken string Flash string FlashKind string // success|danger|warning Data any } func render(w http.ResponseWriter, name string, data PageData) { w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := templates.ExecuteTemplate(w, name, data); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }