Major updates to report status page for nuisance reports

This commit is contained in:
Eli Ribble 2026-02-05 02:24:37 +00:00
parent ca9ab6a6f2
commit 287133e35d
No known key found for this signature in database
2 changed files with 69 additions and 88 deletions

View file

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net/http"
"strconv"
"strings"
"time"
@ -25,11 +26,6 @@ import (
"github.com/aarondl/opt/omitnull"
*/)
type Contact struct {
Email string
Name string
Phone string
}
type ContentStatus struct {
District *ContentDistrict
Error string
@ -38,26 +34,29 @@ type ContentStatus struct {
URL ContentURL
}
type ContentStatusByID struct {
District *ContentDistrict
MapboxToken string
Report Report
URL ContentURL
}
type DetailEntry struct {
Name string
Value string
}
type Image struct {
Location string
URL string
}
type Report struct {
Address string
Comments string
Created time.Time
District string
ID string
Images []Image
Location string // GeoJSON
Reporter Contact
SiteOwner Contact
Type string
Address string
Comments string
Created time.Time
Details []DetailEntry
ID string
Images []Image
Location string // GeoJSON
Status string
Type string
}
var (
@ -113,23 +112,45 @@ func contentFromNuisance(ctx context.Context, report_id string) (result ContentS
result.Report.ID = report_id
result.Report.Address = nuisance.Address
result.Report.Created = nuisance.Created
result.Report.Reporter.Email = nuisance.ReporterEmail.GetOr("")
result.Report.Reporter.Name = nuisance.ReporterName.GetOr("")
result.Report.Reporter.Phone = nuisance.ReporterPhone.GetOr("")
result.Report.Status = nuisance.Status.String()
result.Report.Type = nuisance.Status.String()
result.Report.Details = []DetailEntry{
DetailEntry{
Name: "Active early morning?",
Value: "nah",
},
DetailEntry{
Name: "Duration",
Value: nuisance.Duration.String(),
},
DetailEntry{
Name: "Stagnant Water",
Value: strconv.FormatBool(nuisance.SourceStagnant),
},
DetailEntry{
Name: "Container",
Value: strconv.FormatBool(nuisance.SourceContainer),
},
DetailEntry{
Name: "Sprinklers & Gutters",
Value: "guess not",
},
}
type LocationGeoJSON struct {
Location string
}
row, err := bob.One(ctx, db.PGInstance.BobDB, psql.Select(
sm.From(
location, err := bob.One(ctx, db.PGInstance.BobDB, psql.Select(
sm.Columns(
psql.F("ST_AsGeoJSON", "location"),
).As("location"),
),
sm.From("publicreport.quick"),
sm.Where(psql.Quote("public_id").EQ(psql.Arg(report_id))),
), scan.StructMapper[LocationGeoJSON]())
), scan.SingleColumnMapper[string])
if err != nil {
return result, fmt.Errorf("Failed to query nuisance %s: %w", report_id, err)
}
result.Report.Location = row.Location
result.Report.Location = location
return result, err
}
@ -153,10 +174,6 @@ func contentFromQuick(ctx context.Context, report_id string) (result ContentStat
result.Report.Address = quick.Address
result.Report.Comments = quick.Comments
result.Report.Created = quick.Created
result.Report.District = "Unknown"
result.Report.Reporter.Email = quick.ReporterEmail
result.Report.Reporter.Name = "-"
result.Report.Reporter.Phone = quick.ReporterPhone
result.Report.Type = "Quick"
for _, image := range images {
@ -199,8 +216,6 @@ func getStatusByID(w http.ResponseWriter, r *http.Request) {
content, err = contentFromNuisance(ctx, report_id)
case "pool":
content, err = contentFromPool(ctx, report_id)
case "quick":
content, err = contentFromQuick(ctx, report_id)
}
content.MapboxToken = config.MapboxToken
content.URL = makeContentURL(nil)
@ -212,25 +227,6 @@ func getStatusByID(w http.ResponseWriter, r *http.Request) {
}
/*
func getQuick(w http.ResponseWriter, r *http.Request) {
html.RenderOrError(
w,
Quick,
ContentQuick{},
)
}
func getQuickSubmitComplete(w http.ResponseWriter, r *http.Request) {
report := r.URL.Query().Get("report")
html.RenderOrError(
w,
QuickSubmitComplete,
ContentQuickSubmitComplete{
ReportID: report,
},
)
}
func postQuick(w http.ResponseWriter, r *http.Request) {
err := r.ParseMultipartForm(32 << 10) // 32 MB buffer
if err != nil {

View file

@ -64,12 +64,17 @@ document.addEventListener("DOMContentLoaded", onLoad);
</script>
{{end}}
{{define "content"}}
{{if (eq .District nil)}}
{{template "header-rmo" .}}
{{else}}
{{template "header-district" .District}}
{{end}}
<div class="container my-4">
<!-- Report ID and Status Section -->
<div class="card mb-4">
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
<h5 class="mb-0">Report {{.Report.ID|publicReportID}}</h5>
<span class="badge bg-warning text-dark status-badge">Scheduled</span>
<span class="badge bg-warning text-dark status-badge">Reported</span>
</div>
<div class="card-body">
<div class="row">
@ -83,50 +88,30 @@ document.addEventListener("DOMContentLoaded", onLoad);
</div>
<div class="col-md-4 mb-3">
<strong><i class="fas fa-hourglass-half me-2"></i>District:</strong>
<span>{{.Report.District}}</span>
<span>{{if (eq .District nil)}}Unknown{{else}}{{.District.Name}}{{end}}</span>
</div>
</div>
</div>
</div>
<!-- Contact Information -->
<div class="row mb-4">
<div class="col-md-6 mb-3 mb-md-0">
<div class="card h-100">
<div class="card-header bg-secondary text-white">
<h5 class="mb-0"><i class="fas fa-user me-2"></i>Reporter Information</h5>
</div>
<div class="card-body">
<p><strong>Name:</strong>{{.Report.Reporter.Name}}</p>
<p><strong>Phone:</strong>{{.Report.Reporter.Phone}}</p>
<p><strong>Email:</strong>{{.Report.Reporter.Email}}</p>
<div class="row">
<div class="col-md-12">
<strong><i class="fas fa-map-marked-alt me-2"></i>Location:</strong>
<span>{{.Report.Address}}</span>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100">
<div class="card-header bg-secondary text-white">
<h5 class="mb-0"><i class="fas fa-home me-2"></i>Reported Location</h5>
</div>
<div class="card-body">
<p><strong>Site Contact Name:</strong>{{.Report.SiteOwner.Name}}</p>
<p><strong>Site Contact Phone:</strong>{{.Report.SiteOwner.Phone}}</p>
<p><strong>Site Contact Email:</strong>{{.Report.SiteOwner.Email}}</p>
<p><strong>Address:</strong>{{.Report.Address}}</p>
<div class="row">
<div class="col-md-12">
<strong><i class="fas fa-images me-2"></i>Images:</strong>
<span>{{ if gt (len .Report.Images) 0 }}{{len .Report.Images}}{{else}}None provided{{end}}</span>
</div>
</div>
</div>
</div>
<!-- Report Information -->
<div class="card mb-4">
<div class="card-header bg-success text-white">
<h5 class="mb-0"><i class="fas fa-history me-2"></i>Report Detail</h5>
</div>
<div class="card-body">
{{ if not (eq .Report.Comments "") }}
<p><strong>Comments:</strong>{{ .Report.Comments }}</p>
{{ end }}
{{range .Report.Details}}
<div class="row">
<div class="col-md-12">
<strong>{{.Name}}</strong>
<span>{{.Value}}</span>
</div>
</div>
{{end}}
</div>
</div>