Add cell debug page.

This makes it much easier to troubleshoot information related to a cell
by showing detailed data about a single cell. At this point much is a
placeholder, but we at least get the cell boundary coordinates and a
map.

This also starts to make some code common around doing things like
mapping.
This commit is contained in:
Eli Ribble 2025-11-19 15:21:06 +00:00
parent 7c2d7eef25
commit c0b527c9a3
No known key found for this signature in database
9 changed files with 465 additions and 48 deletions

102
html.go
View file

@ -19,6 +19,7 @@ import (
"github.com/aarondl/opt/null"
//"github.com/riverqueue/river/rivershared/util/slogutil"
"github.com/stephenafamo/bob/dialect/psql/sm"
"github.com/uber/h3-go/v4"
)
//go:embed templates/*
@ -26,6 +27,7 @@ var embeddedFiles embed.FS
// Authenticated pages
var (
cell = newBuiltTemplate("cell", "authenticated")
dashboard = newBuiltTemplate("dashboard", "authenticated")
oauthPrompt = newBuiltTemplate("oauth-prompt", "authenticated")
settings = newBuiltTemplate("settings", "authenticated")
@ -52,7 +54,7 @@ var (
signin = newBuiltTemplate("signin", "base")
signup = newBuiltTemplate("signup", "base")
)
var components = [...]string{"header"}
var components = [...]string{"header", "map"}
type BuiltTemplate struct {
files []string
@ -60,13 +62,20 @@ type BuiltTemplate struct {
template *template.Template
}
type Link struct {
Href string
Title string
type ComponentMap struct {
Center LatLng
GeoJSON interface{}
MapboxToken string
Zoom int
}
type ContentAuthenticatedPlaceholder struct {
User User
}
type ContentCell struct {
CellBoundary h3.CellBoundary
MapData ComponentMap
User User
}
type ContentPhoneCall struct {
DistrictName string
}
@ -82,8 +91,8 @@ type ContentDashboard struct {
CountMosquitoSources int
CountServiceRequests int
Geo template.JS
MapboxToken string
LastSync *time.Time
MapData ComponentMap
Org string
RecentRequests []ServiceRequestSummary
User User
@ -94,6 +103,14 @@ type ContentSignin struct {
InvalidCredentials bool
}
type ContentSignup struct{}
type LatLng struct {
Lat float64
Lng float64
}
type Link struct {
Href string
Title string
}
type ServiceRequestSummary struct {
Date time.Time
Location string
@ -165,12 +182,45 @@ func extractInitials(name string) string {
return initials.String()
}
func htmlDashboard(ctx context.Context, w http.ResponseWriter, user *models.User) {
geo, err := sampleGeoJSON()
func htmlCell(ctx context.Context, w http.ResponseWriter, user *models.User, c int64) {
userContent, err := contentForUser(ctx, user)
if err != nil {
respondError(w, "Failed to get geo", err, http.StatusInternalServerError)
respondError(w, "Failed to get user", err, http.StatusInternalServerError)
return
}
center, err := h3.Cell(c).LatLng()
if err != nil {
respondError(w, "Failed to get center", err, http.StatusInternalServerError)
return
}
boundary, err := h3.Cell(c).Boundary()
if err != nil {
respondError(w, "Failed to get boundary", err, http.StatusInternalServerError)
return
}
geojson, err := h3ToGeoJSON([]h3.Cell{h3.Cell(c)})
if err != nil {
respondError(w, "Failed to get boundaries", err, http.StatusInternalServerError)
return
}
resolution := h3.Cell(c).Resolution()
data := ContentCell{
CellBoundary: boundary,
MapData: ComponentMap{
Center: LatLng{
Lat: center.Lat,
Lng: center.Lng,
},
GeoJSON: geojson,
MapboxToken: MapboxToken,
Zoom: resolution + 5,
},
User: userContent,
}
renderOrError(w, cell, &data)
}
func htmlDashboard(ctx context.Context, w http.ResponseWriter, user *models.User) {
org, err := user.Organization().One(ctx, PGInstance.BobDB)
if err != nil {
respondError(w, "Failed to get org", err, http.StatusInternalServerError)
@ -220,12 +270,13 @@ func htmlDashboard(ctx context.Context, w http.ResponseWriter, user *models.User
CountInspections: int(inspectionCount),
CountMosquitoSources: int(sourceCount),
CountServiceRequests: int(serviceCount),
Geo: template.JS(geo),
LastSync: lastSync,
MapboxToken: MapboxToken,
Org: org.Name.MustGet(),
RecentRequests: requests,
User: userContent,
MapData: ComponentMap{
MapboxToken: MapboxToken,
},
Org: org.Name.MustGet(),
RecentRequests: requests,
User: userContent,
}
renderOrError(w, dashboard, data)
}
@ -369,11 +420,30 @@ func htmlSignup(w http.ResponseWriter, path string) {
renderOrError(w, signup, data)
}
func latLngDisplay(ll h3.LatLng) string {
latDir := "N"
latVal := ll.Lat
if ll.Lat < 0 {
latDir = "S"
latVal = -ll.Lat
}
lngDir := "E"
lngVal := ll.Lng
if ll.Lng < 0 {
lngDir = "W"
lngVal = -ll.Lng
}
return fmt.Sprintf("%.4f° %s, %.4f° %s", latVal, latDir, lngVal, lngDir)
}
func makeFuncMap() template.FuncMap {
funcMap := template.FuncMap{
"bigNumber": bigNumber,
"timeElapsed": timeElapsed,
"timeSince": timeSince,
"bigNumber": bigNumber,
"latLngDisplay": latLngDisplay,
"timeElapsed": timeElapsed,
"timeSince": timeSince,
}
return funcMap
}