From 7f416aca3851ac92277a9f7d9ab3fa136630b476 Mon Sep 17 00:00:00 2001 From: nic Date: Mon, 14 Oct 2024 16:42:48 -0400 Subject: [PATCH] updated to use embedded templates; need to rethink structure/layout --- apps/web/main.go | 5 ++ go.mod | 6 +-- go.sum | 12 ++--- internal/handlers/web/admin.go | 23 +++++--- internal/handlers/web/assets.go | 23 +++++--- internal/handlers/web/companies.go | 27 ++++++++-- internal/handlers/web/dashboard.go | 16 ++---- internal/handlers/web/invoices.go | 38 +++++++------ internal/handlers/web/login.go | 5 +- templates/layout.html | 87 +++++++++++++++--------------- templates/partials/admin.html | 4 +- templates/partials/assets.html | 4 +- templates/partials/companies.html | 4 +- templates/partials/jobs.html | 4 +- web_templates.go | 55 +++++++++++++++++++ 15 files changed, 203 insertions(+), 110 deletions(-) create mode 100644 web_templates.go diff --git a/apps/web/main.go b/apps/web/main.go index 596b14b..4b0e855 100644 --- a/apps/web/main.go +++ b/apps/web/main.go @@ -4,6 +4,7 @@ import ( "log" "net/http" + root "marmic/servicetrade-toolbox" "marmic/servicetrade-toolbox/internal/handlers/web" "marmic/servicetrade-toolbox/internal/middleware" @@ -11,6 +12,10 @@ import ( ) func main() { + err := root.InitializeWebTemplates() // Note the change here + if err != nil { + log.Fatalf("Failed to initialize web templates: %v", err) + } r := mux.NewRouter() // Serve static files diff --git a/go.mod b/go.mod index 9e806a0..468e217 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( ) require ( - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/term v0.24.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/term v0.25.0 // indirect ) diff --git a/go.sum b/go.sum index 2144e42..e7a6e59 100644 --- a/go.sum +++ b/go.sum @@ -2,9 +2,9 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/inancgumus/screen v0.0.0-20190314163918-06e984b86ed3 h1:fO9A67/izFYFYky7l1pDP5Dr0BTCRkaQJUG6Jm5ehsk= github.com/inancgumus/screen v0.0.0-20190314163918-06e984b86ed3/go.mod h1:Ey4uAp+LvIl+s5jRbOHLcZpUDnkjLBROl15fZLwPlTM= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= diff --git a/internal/handlers/web/admin.go b/internal/handlers/web/admin.go index ff9dd20..34f3dcd 100644 --- a/internal/handlers/web/admin.go +++ b/internal/handlers/web/admin.go @@ -1,19 +1,28 @@ package web import ( - "html/template" + "log" + root "marmic/servicetrade-toolbox" + "marmic/servicetrade-toolbox/internal/api" "net/http" ) func AdminHandler(w http.ResponseWriter, r *http.Request) { - tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/partials/admin.html")) + session, ok := r.Context().Value("session").(*api.Session) + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + tmpl := root.WebTemplates data := map[string]interface{}{ - "Title": "Admin", + "Title": "Admin Dashboard", + "Session": session, } - if r.Header.Get("HX-Request") == "true" { - tmpl.ExecuteTemplate(w, "content", data) - } else { - tmpl.Execute(w, data) + if err := tmpl.ExecuteTemplate(w, "admin.html", data); err != nil { + log.Printf("Template execution error: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return } } diff --git a/internal/handlers/web/assets.go b/internal/handlers/web/assets.go index 12d9d35..2b03bda 100644 --- a/internal/handlers/web/assets.go +++ b/internal/handlers/web/assets.go @@ -1,19 +1,28 @@ package web import ( - "html/template" + "log" + root "marmic/servicetrade-toolbox" + "marmic/servicetrade-toolbox/internal/api" "net/http" ) func AssetsHandler(w http.ResponseWriter, r *http.Request) { - tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/partials/assets.html")) + session, ok := r.Context().Value("session").(*api.Session) + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + tmpl := root.WebTemplates data := map[string]interface{}{ - "Title": "Assets", + "Title": "Admin Dashboard", + "Session": session, } - if r.Header.Get("HX-Request") == "true" { - tmpl.ExecuteTemplate(w, "content", data) - } else { - tmpl.Execute(w, data) + if err := tmpl.ExecuteTemplate(w, "assets.html", data); err != nil { + log.Printf("Template execution error: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return } } diff --git a/internal/handlers/web/companies.go b/internal/handlers/web/companies.go index d10e968..10507dd 100644 --- a/internal/handlers/web/companies.go +++ b/internal/handlers/web/companies.go @@ -1,19 +1,36 @@ package web import ( - "html/template" + "log" + root "marmic/servicetrade-toolbox" + "marmic/servicetrade-toolbox/internal/api" "net/http" ) func CompaniesHandler(w http.ResponseWriter, r *http.Request) { - tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/partials/companies.html")) + session, ok := r.Context().Value("session").(*api.Session) + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + tmpl := root.WebTemplates data := map[string]interface{}{ - "Title": "Companies", + "Title": "Companies", + "Session": session, + "View": "company_content", } + var err error if r.Header.Get("HX-Request") == "true" { - tmpl.ExecuteTemplate(w, "content", data) + err = tmpl.ExecuteTemplate(w, "companies.html", data) } else { - tmpl.Execute(w, data) + err = tmpl.Execute(w, data) + } + + if err != nil { + log.Printf("Template execution error: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return } } diff --git a/internal/handlers/web/dashboard.go b/internal/handlers/web/dashboard.go index 0bd11e3..84fd432 100644 --- a/internal/handlers/web/dashboard.go +++ b/internal/handlers/web/dashboard.go @@ -1,24 +1,14 @@ package web import ( - "html/template" "log" + root "marmic/servicetrade-toolbox" "net/http" ) func DashboardHandler(w http.ResponseWriter, r *http.Request) { - tmpl, err := template.ParseFiles( - "templates/layout.html", - "templates/dashboard.html", - "templates/partials/invoice_search.html", - "templates/partials/invoice_search_results.html", - ) - if err != nil { - log.Printf("Template parsing error: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - + var err error + tmpl := root.WebTemplates data := struct{}{} // Empty struct as data if r.Header.Get("HX-Request") == "true" { diff --git a/internal/handlers/web/invoices.go b/internal/handlers/web/invoices.go index b45b50e..d791068 100644 --- a/internal/handlers/web/invoices.go +++ b/internal/handlers/web/invoices.go @@ -4,9 +4,9 @@ import ( "bytes" "encoding/json" "fmt" - "html/template" "io" "log" + root "marmic/servicetrade-toolbox" "marmic/servicetrade-toolbox/internal/api" "net/http" "strings" @@ -23,7 +23,7 @@ var statusButtons = []StatusButton{ {"draft", "Draft Invoice", "success-button", "Are you sure you want to draft this invoice?"}, {"ok", "Ok Invoice", "success-button", "Are you sure you want to mark this invoice as OK?"}, {"fail", "Fail Invoice", "caution-button", "Are you sure you want to fail this invoice?"}, - {"pending", "Pending Invoice", "caution-button", "Are you sure you want to mark this invoice as pending?"}, + {"pending_accounting", "Pending Invoice", "caution-button", "Are you sure you want to mark this invoice as pending?"}, {"processed", "Process Invoice", "warning-button", "Are you sure you want to process this invoice?"}, {"void", "Void Invoice", "warning-button", "Are you sure you want to void this invoice?"}, } @@ -40,19 +40,28 @@ func InvoicesHandler(w http.ResponseWriter, r *http.Request) { return } - tmpl := template.Must(template.ParseFiles("templates/layout.html", "templates/partials/invoices.html")) + tmpl := root.WebTemplates data := map[string]interface{}{ "Title": "Invoices", } + var err error + if r.Header.Get("HX-Request") == "true" { - tmpl.ExecuteTemplate(w, "content", data) + err = tmpl.ExecuteTemplate(w, "content", data) } else { - tmpl.Execute(w, data) + err = tmpl.ExecuteTemplate(w, "layout.html", data) + } + + if err != nil { + log.Printf("Template execution error: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return } } func handleInvoiceSearch(w http.ResponseWriter, r *http.Request, session *api.Session) { + tmpl := root.WebTemplates searchTerm := strings.TrimSpace(r.URL.Query().Get("search")) if searchTerm == "" { @@ -74,7 +83,6 @@ func handleInvoiceSearch(w http.ResponseWriter, r *http.Request, session *api.Se if strings.Contains(err.Error(), "access forbidden") { errorMsg = "You do not have permission to view this invoice." } - tmpl := template.Must(template.ParseFiles("templates/partials/invoice_search_results.html")) tmpl.ExecuteTemplate(w, "invoice_search_results", map[string]interface{}{ "Error": true, "ErrorMsg": errorMsg, @@ -86,7 +94,6 @@ func handleInvoiceSearch(w http.ResponseWriter, r *http.Request, session *api.Se if invoice == nil { log.Printf("No invoice found for: %s", searchTerm) w.WriteHeader(http.StatusOK) - tmpl := template.Must(template.ParseFiles("templates/partials/invoice_search_results.html")) tmpl.ExecuteTemplate(w, "invoice_search_results", map[string]interface{}{ "NotFound": true, "ErrorMsg": fmt.Sprintf("No invoice found for: %s", searchTerm), @@ -106,7 +113,6 @@ func handleInvoiceSearch(w http.ResponseWriter, r *http.Request, session *api.Se invoice["buttons"] = getInvoiceStatusButtons(invoice["id"].(string), invoice["status"].(string)) } - tmpl := template.Must(template.ParseFiles("templates/partials/invoice_search_results.html")) err = tmpl.ExecuteTemplate(w, "invoice_search_results", invoice) if err != nil { log.Printf("Error executing template: %v", err) @@ -137,6 +143,7 @@ func getInvoiceStatusButtons(invoiceID, currentStatus string) []map[string]strin default: // For all other statuses, show all buttons except the current status for _, button := range statusButtons { + fmt.Printf("btn status: %s, curr status: %s", button.Status, currentStatus) if button.Status != currentStatus { buttons = append(buttons, map[string]string{ "Action": fmt.Sprintf("/%s-invoice/%s", button.Status, invoiceID), @@ -182,12 +189,12 @@ func UpdateInvoiceStatusHandler(w http.ResponseWriter, r *http.Request) { // Validate the status validStatuses := map[string]bool{ - "draft": true, - "fail": true, - "ok": true, - "pending": true, - "processed": true, - "void": true, + "draft": true, + "fail": true, + "ok": true, + "pending_accounting": true, + "processed": true, + "void": true, } if !validStatuses[status] { @@ -231,8 +238,7 @@ func UpdateInvoiceStatusHandler(w http.ResponseWriter, r *http.Request) { log.Printf("Updated invoice after status change to %s: %+v", status, invoice) // Render the updated invoice details - tmpl := template.Must(template.ParseFiles("templates/partials/invoice_search_results.html")) - err = tmpl.ExecuteTemplate(w, "invoice_search_results", invoice) + err = root.WebTemplates.ExecuteTemplate(w, "invoice_search_results", invoice) if err != nil { log.Printf("Error executing template: %v", err) http.Error(w, "Error rendering template", http.StatusInternalServerError) diff --git a/internal/handlers/web/login.go b/internal/handlers/web/login.go index 43d2a0c..83531ca 100644 --- a/internal/handlers/web/login.go +++ b/internal/handlers/web/login.go @@ -1,8 +1,8 @@ package web import ( - "html/template" "log" + root "marmic/servicetrade-toolbox" "marmic/servicetrade-toolbox/internal/api" "marmic/servicetrade-toolbox/internal/middleware" "net/http" @@ -11,8 +11,7 @@ import ( func LoginHandler(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { - tmpl := template.Must(template.ParseFiles("templates/login.html")) - tmpl.Execute(w, nil) + root.WebTemplates.ExecuteTemplate(w, "login.html", nil) return } diff --git a/templates/layout.html b/templates/layout.html index 692e5f4..c0b3acb 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -1,46 +1,49 @@ - - - - ServiceTrade Tools - - - - - - - -
- -
- -
+ + + + ServiceTrade Tools + + + - -
{{template "content" .}}
-
- - + + + + + +
+ +
+ +
+ + +
{{template "content" .}}
+
+ + + \ No newline at end of file diff --git a/templates/partials/admin.html b/templates/partials/admin.html index fc569b3..4f6ad6a 100644 --- a/templates/partials/admin.html +++ b/templates/partials/admin.html @@ -1,5 +1,5 @@ -{{define "content"}} +{{define "admin"}}

Admin

Manage your assets here.

-{{end}} +{{end}} \ No newline at end of file diff --git a/templates/partials/assets.html b/templates/partials/assets.html index 54d4c23..b0d1c29 100644 --- a/templates/partials/assets.html +++ b/templates/partials/assets.html @@ -1,5 +1,5 @@ -{{define "content"}} +{{define "assets"}}

Assets

Manage your assets here.

-{{end}} +{{end}} \ No newline at end of file diff --git a/templates/partials/companies.html b/templates/partials/companies.html index 3d6f05f..55b5084 100644 --- a/templates/partials/companies.html +++ b/templates/partials/companies.html @@ -1,5 +1,5 @@ -{{define "content"}} +{{define "companies"}}

Companies

Manage your companies here.

-{{end}} +{{end}} \ No newline at end of file diff --git a/templates/partials/jobs.html b/templates/partials/jobs.html index 4e97baf..9687863 100644 --- a/templates/partials/jobs.html +++ b/templates/partials/jobs.html @@ -1,4 +1,4 @@ -{{define "content"}} +{{define "jobs"}} -{{end}} +{{end}} \ No newline at end of file diff --git a/web_templates.go b/web_templates.go new file mode 100644 index 0000000..d549b98 --- /dev/null +++ b/web_templates.go @@ -0,0 +1,55 @@ +package root + +import ( + "embed" + "html/template" + "io/fs" + "log" + "path/filepath" +) + +//go:embed templates +var webTemplateFS embed.FS + +var WebTemplates *template.Template + +// InitializeWebTemplates parses all HTML templates in the embedded filesystem +func InitializeWebTemplates() error { + var err error + WebTemplates, err = parseWebTemplates() + return err +} + +func parseWebTemplates() (*template.Template, error) { + tmpl := template.New("") + + err := fs.WalkDir(webTemplateFS, "templates", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + return nil + } + + if filepath.Ext(path) != ".html" { + return nil + } + + log.Printf("Parsing template: %s", path) + + content, err := webTemplateFS.ReadFile(path) + if err != nil { + return err + } + + _, err = tmpl.New(filepath.Base(path)).Parse(string(content)) + return err + }) + + if err != nil { + return nil, err + } + + return tmpl, nil +}