prototype report page

This commit is contained in:
2025-12-28 14:38:06 -07:00
parent 49bbe281df
commit 051cf1da5d
13 changed files with 330 additions and 17 deletions
Binary file not shown.
Binary file not shown.
+107 -13
View File
@@ -1,26 +1,120 @@
package site
import (
"encoding/json"
"fmt"
"html/template"
"net/http"
"net/url"
"path"
"strings"
"github.com/angelbeltran/templater"
"ruben/inventory2/internal/domains/raw_events"
)
func NewSiteHandler() http.Handler {
func NewSiteHandler(dir string, db *raw_events.Store) http.Handler {
mux := http.NewServeMux()
// non-html routes
scfs := http.FileServer(http.Dir(dir + "/scripts"))
mux.Handle("/scripts/", http.StripPrefix("/scripts", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/javascript")
if path.Ext(r.URL.Path) == ".gz" {
w.Header().Set("Content-Encoding", "gzip")
}
scfs.ServeHTTP(w, r)
})))
mux.Handle("/styles/", http.StripPrefix("/styles", http.FileServer(http.Dir(dir+"/styles"))))
// html page routes
tmplr := templater.NewTemplater(
dir+"/templates",
func() template.FuncMap {
return template.FuncMap{
"buildSitePath": func(parts ...any) string {
strParts := make([]string, len(parts))
for i, p := range parts {
strParts[i] = fmt.Sprint(p)
}
// TODO: make "/site" dynamic somehow
return path.Join(append([]string{"/site"}, strParts...)...)
},
"prettyPrintJSON": func(j json.RawMessage) string {
b, err := json.MarshalIndent(j, " ", "")
if err != nil {
return string(j)
}
return string(b)
},
"addInt": func(a, b int) int {
return a + b
},
"subInt": func(a, b int) int {
return a - b
},
"multInt": func(a, b int) int {
return a * b
},
}
},
)
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
w.Write([]byte(`
<DOCTYPE html>
<html>
<head>
<title>TEST</title>
</head>
<body>
TEST
</body>
</html>
`))
b, err := tmplr.ExecutePage(
getPageTemplateNameForURL(r.URL),
"Request",
r,
// add services here
"RawEvents",
db.WithContext(r.Context()),
"URLCalc",
newURLCalculator(r.URL),
)
if err != nil {
// TODO: handle 'not found' as a 404?
fmt.Println("[ERROR]: failed to load or parse layout template:", err)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
w.Write(b)
})
return mux
}
func getPageTemplateNameForURL(u *url.URL) string {
filepath := strings.TrimPrefix(strings.TrimSuffix(u.Path, ".html"), "/")
if filepath == "" {
// "/" maps to "/home"
filepath = "home"
}
return filepath
}
type URLCalculator struct {
url *url.URL
}
func newURLCalculator(u *url.URL) URLCalculator {
cpy := *u
return URLCalculator{
url: &cpy,
}
}
func (c URLCalculator) SetQueryParam(k string, v any) string {
u := *c.url
q := u.Query()
q.Set(k, fmt.Sprint(v))
u.RawQuery = q.Encode()
return u.String()
}
+10
View File
@@ -0,0 +1,10 @@
ul, ol {
/* remove default agent spacing */
margin: 0;
padding: 0;
list-style: none;
& > li {
display: inline-block;
}
}
@@ -0,0 +1,16 @@
<nav>
<ul>
<li>
<a href="{{ buildSitePath }}">
Home
</a>
</li>
<li>
<a href="{{ buildSitePath "reports" }}">
Reports
</a>
</li>
</ul>
</nav>
+19
View File
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>WIP</title>
<link rel="stylesheet" href="/site/styles/index.css">
<script src="/site/scripts/htmx.min.js.gz"></script>
<script src="/site/scripts/_hyperscript.min.js.gz"></script>
{{/* This is where the page_head template will be inserted into the page */}}
{{- block "head" . }}{{ end }}
</head>
<body>
{{/* This is where the page_body template will be inserted into the page */}}
{{- block "body" . }}{{ end }}
</body>
</html>
@@ -0,0 +1,3 @@
{{ componentBody "nav_bar" }}
<h1>Home</h1>
@@ -0,0 +1,89 @@
{{ componentBody "nav_bar" }}
{{ $storeID := .Request.URL.Query.Get "store-id" }}
<h1>Reports</h1>
<main>
<select
_='
on change
make a URL from window.location.href called currentURL
set currentQuery to searchParams of currentURL
set storeID to event.target.value
js(currentQuery, storeID) currentQuery.set("store-id", storeID) end
set search of currentURL to currentQuery.toString()
set window.location to currentURL
'
>
{{ if not $storeID }}
<option selected disabled>
choose a store
</option>
{{ end }}
<option {{ if eq $storeID "test-store-1" }}selected{{ end }} >
test-store-1
</option>
<option {{ if eq $storeID "test-store-2" }}selected{{ end }} >
test-store-2
</option>
<option {{ if eq $storeID "test-store-3" }}selected{{ end }} >
test-store-3
</option>
</select>
{{- if $storeID }}
<section
style="
display: grid;
grid-template-columns: auto auto auto 1fr;
column-gap: 1em;
row-gap: 0.5em;
margin: 1em 0;
"
>
<div style="border-bottom: solid black 1px;">
Date
</div>
<div style="border-bottom: solid black 1px;">
Time
</div>
<div style="border-bottom: solid black 1px;">
ID
</div>
<div style="border-bottom: solid black 1px;">
Payload
</div>
{{ $events := .RawEvents.LoadEventsForStore "etsy" $storeID }}
{{ range $e := $events }}
<div>
{{ printf "%d/%02d/%02d" $e.EventTimestamp.Year $e.EventTimestamp.Month $e.EventTimestamp.Day }}
</div>
<div>
{{ $suffix := "am" }}
{{ $ts := $e.EventTimestamp }}
{{- $hr := $ts.Hour }}
{{- if eq $hr 0 }}
{{- $hr = 12 }}
{{- else if eq $hr 12 }}
{{- $suffix = "pm" }}
{{- else if gt $hr 12 }}
{{- $hr = subInt $hr 12 }}
{{- $suffix = "pm" }}
{{- end }}
{{- $hrColorHex := multInt $hr 10 | subInt (subInt 256 128) | printf "%02x" }}
{{- $minColorHex := multInt $ts.Minute 2 | subInt (subInt 256 128) | printf "%02x" }}
{{- $secColorHex := multInt $ts.Second 2 | subInt (subInt 256 128) | printf "%02x" }}
<span style="color: #{{ $hrColorHex }}{{ $hrColorHex }}{{ $hrColorHex }};">{{ printf "%02d" $hr }}</span>:<span style="color: #{{ $minColorHex }}{{ $minColorHex }}{{ $minColorHex }};">{{ printf "%02d" $ts.Minute }}</span>:<span style="color: #{{ $secColorHex }}{{ $secColorHex }}{{ $secColorHex }};">{{ printf "%02d" $ts.Second }}</span>{{ printf " %s" $suffix }}
</div>
<div>
{{ $e.EventID }}
</div>
<div>
{{ prettyPrintJSON $e.Payload }}
</div>
{{ end }}
</section>
{{- end }}
</main>