Use common logic in mocks, add dispatch mock

This commit is contained in:
Eli Ribble 2025-12-11 00:30:24 +00:00
parent 62dbfb3ebc
commit 3a8d6395d7
No known key found for this signature in database
31 changed files with 671 additions and 291 deletions

View file

@ -66,19 +66,6 @@ func getOAuthRefresh(w http.ResponseWriter, r *http.Request) {
htmlOauthPrompt(w, user) htmlOauthPrompt(w, user)
} }
func getPhoneCall(w http.ResponseWriter, r *http.Request) {
htmlPhoneCall(w)
}
func getDataEntry(w http.ResponseWriter, r *http.Request) {
htmlDataEntry(w)
}
func getDataEntryBad(w http.ResponseWriter, r *http.Request) {
htmlDataEntryBad(w)
}
func getDataEntryGood(w http.ResponseWriter, r *http.Request) {
htmlDataEntryGood(w)
}
func getQRCodeReport(w http.ResponseWriter, r *http.Request) { func getQRCodeReport(w http.ResponseWriter, r *http.Request) {
code := chi.URLParam(r, "code") code := chi.URLParam(r, "code")
if code == "" { if code == "" {
@ -138,40 +125,6 @@ func getQRCodeReport(w http.ResponseWriter, r *http.Request) {
respondError(w, "Error writing response", err, http.StatusInternalServerError) respondError(w, "Error writing response", err, http.StatusInternalServerError)
} }
} }
func getReport(w http.ResponseWriter, r *http.Request) {
//org := r.URL.Query().Get("org")
htmlReport(w)
}
func getReportConfirmation(w http.ResponseWriter, r *http.Request) {
code := chi.URLParam(r, "code")
htmlReportConfirmation(w, code)
}
func getReportContribute(w http.ResponseWriter, r *http.Request) {
code := chi.URLParam(r, "code")
htmlReportContribute(w, code)
}
func getReportDetail(w http.ResponseWriter, r *http.Request) {
code := chi.URLParam(r, "code")
htmlReportDetail(w, code)
}
func getReportEvidence(w http.ResponseWriter, r *http.Request) {
code := chi.URLParam(r, "code")
htmlReportEvidence(w, code)
}
func getReportSchedule(w http.ResponseWriter, r *http.Request) {
code := chi.URLParam(r, "code")
htmlReportSchedule(w, code)
}
func getReportUpdate(w http.ResponseWriter, r *http.Request) {
code := chi.URLParam(r, "code")
htmlReportUpdate(w, code)
}
func getRoot(w http.ResponseWriter, r *http.Request) { func getRoot(w http.ResponseWriter, r *http.Request) {
user, err := getAuthenticatedUser(r) user, err := getAuthenticatedUser(r)
@ -202,39 +155,6 @@ func getRoot(w http.ResponseWriter, r *http.Request) {
} }
} }
func getServiceRequest(w http.ResponseWriter, r *http.Request) {
htmlServiceRequest(w)
}
func getServiceRequestDetail(w http.ResponseWriter, r *http.Request) {
code := chi.URLParam(r, "code")
htmlServiceRequestDetail(w, code)
}
func getServiceRequestLocation(w http.ResponseWriter, r *http.Request) {
htmlServiceRequestLocation(w)
}
func getServiceRequestMosquito(w http.ResponseWriter, r *http.Request) {
htmlServiceRequestMosquito(w)
}
func getServiceRequestPool(w http.ResponseWriter, r *http.Request) {
htmlServiceRequestPool(w)
}
func getServiceRequestQuick(w http.ResponseWriter, r *http.Request) {
htmlServiceRequestQuick(w)
}
func getServiceRequestQuickConfirmation(w http.ResponseWriter, r *http.Request) {
htmlServiceRequestQuickConfirmation(w)
}
func getServiceRequestUpdates(w http.ResponseWriter, r *http.Request) {
htmlServiceRequestUpdates(w)
}
func getSettings(w http.ResponseWriter, r *http.Request, u *models.User) { func getSettings(w http.ResponseWriter, r *http.Request, u *models.User) {
htmlSettings(w, r, u) htmlSettings(w, r, u)
} }
@ -326,3 +246,13 @@ func postSignup(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusFound) http.Redirect(w, r, "/", http.StatusFound)
} }
func renderMock(templateName string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
code := chi.URLParam(r, "code")
if code == "" {
code = "abc-123"
}
htmlMock(templateName, w, code)
}
}

181
html.go
View file

@ -8,7 +8,6 @@ import (
"fmt" "fmt"
"html/template" "html/template"
"io" "io"
"log/slog"
"math" "math"
"net/http" "net/http"
"os" "os"
@ -41,10 +40,13 @@ var (
// Unauthenticated pages // Unauthenticated pages
var ( var (
admin = newBuiltTemplate("admin", "base")
dataEntry = newBuiltTemplate("data-entry", "base") dataEntry = newBuiltTemplate("data-entry", "base")
dataEntryGood = newBuiltTemplate("data-entry-good", "base") dataEntryGood = newBuiltTemplate("data-entry-good", "base")
dataEntryBad = newBuiltTemplate("data-entry-bad", "base") dataEntryBad = newBuiltTemplate("data-entry-bad", "base")
phoneCall = newBuiltTemplate("phone-call", "base") dispatch = newBuiltTemplate("dispatch", "base")
dispatchResults = newBuiltTemplate("dispatch-results", "base")
mockRoot = newBuiltTemplate("mock-root", "base")
report = newBuiltTemplate("report", "base") report = newBuiltTemplate("report", "base")
reportConfirmation = newBuiltTemplate("report-confirmation", "base") reportConfirmation = newBuiltTemplate("report-confirmation", "base")
reportContribute = newBuiltTemplate("report-contribute", "base") reportContribute = newBuiltTemplate("report-contribute", "base")
@ -64,6 +66,7 @@ var (
signup = newBuiltTemplate("signup", "base") signup = newBuiltTemplate("signup", "base")
) )
var components = [...]string{"header", "map"} var components = [...]string{"header", "map"}
var templatesByFilename = make(map[string]BuiltTemplate, 0)
type BreedingSourceSummary struct { type BreedingSourceSummary struct {
ID string ID string
@ -99,15 +102,26 @@ type ContentCell struct {
Treatments []Treatment Treatments []Treatment
User User User User
} }
type ContentPhoneCall struct { type ContentMockURLs struct {
Dispatch string
DispatchResults string
ReportConfirmation string
ReportDetail string
ReportContribute string
ReportEvidence string
ReportSchedule string
ReportUpdate string
Root string
}
type ContentMock struct {
DistrictName string DistrictName string
URLs ContentMockURLs
} }
type ContentReportDetail struct { type ContentReportDetail struct {
NextURL string NextURL string
UpdateURL string UpdateURL string
} }
type ContentReportDiagnostic struct { type ContentReportDiagnostic struct {
URL string
} }
type ContentDashboard struct { type ContentDashboard struct {
CountInspections int CountInspections int
@ -279,21 +293,6 @@ func htmlCell(ctx context.Context, w http.ResponseWriter, user *models.User, c i
renderOrError(w, cell, &data) renderOrError(w, cell, &data)
} }
func htmlDataEntry(w http.ResponseWriter) {
data := ContentPlaceholder{}
renderOrError(w, dataEntry, data)
}
func htmlDataEntryBad(w http.ResponseWriter) {
data := ContentPlaceholder{}
renderOrError(w, dataEntryBad, data)
}
func htmlDataEntryGood(w http.ResponseWriter) {
data := ContentPlaceholder{}
renderOrError(w, dataEntryGood, data)
}
func htmlDashboard(ctx context.Context, w http.ResponseWriter, user *models.User) { func htmlDashboard(ctx context.Context, w http.ResponseWriter, user *models.User) {
org, err := user.Organization().One(ctx, db.PGInstance.BobDB) org, err := user.Organization().One(ctx, db.PGInstance.BobDB)
if err != nil { if err != nil {
@ -355,6 +354,30 @@ func htmlDashboard(ctx context.Context, w http.ResponseWriter, user *models.User
renderOrError(w, dashboard, data) renderOrError(w, dashboard, data)
} }
func htmlMock(t string, w http.ResponseWriter, code string) {
data := ContentMock{
DistrictName: "Delta MVCD",
URLs: ContentMockURLs{
Dispatch: "/mock/dispatch",
DispatchResults: "/mock/dispatch-results",
ReportConfirmation: fmt.Sprintf("/mock/report/%s/confirm", code),
ReportDetail: fmt.Sprintf("/mock/report/%s", code),
ReportContribute: fmt.Sprintf("/mock/report/%s/contribute", code),
ReportEvidence: fmt.Sprintf("/mock/report/%s/evidence", code),
ReportSchedule: fmt.Sprintf("/mock/report/%s/schedule", code),
ReportUpdate: fmt.Sprintf("/mock/report/%s/update", code),
Root: "/mock",
},
}
template, ok := templatesByFilename[t]
if !ok {
log.Error().Str("template", t).Msg("Failed to find template")
respondError(w, "Failed to render template", nil, http.StatusInternalServerError)
return
}
renderOrError(w, &template, data)
}
func htmlOauthPrompt(w http.ResponseWriter, user *models.User) { func htmlOauthPrompt(w http.ResponseWriter, user *models.User) {
dp := user.DisplayName dp := user.DisplayName
data := ContentDashboard{ data := ContentDashboard{
@ -367,110 +390,6 @@ func htmlOauthPrompt(w http.ResponseWriter, user *models.User) {
renderOrError(w, oauthPrompt, data) renderOrError(w, oauthPrompt, data)
} }
func htmlPhoneCall(w http.ResponseWriter) {
data := ContentPhoneCall{
DistrictName: "[District Name]",
}
renderOrError(w, phoneCall, data)
}
func htmlReport(w http.ResponseWriter) {
url := BaseURL + "/report/t78fd3"
data := ContentReportDiagnostic{
URL: url,
}
renderOrError(w, report, data)
}
func htmlReportConfirmation(w http.ResponseWriter, code string) {
url := BaseURL + "/report/" + code + "/history"
data := ContentReportDiagnostic{
URL: url,
}
renderOrError(w, reportConfirmation, data)
}
func htmlReportContribute(w http.ResponseWriter, code string) {
nextURL := BaseURL + "/report/" + code + "/schedule"
data := ContentReportDetail{
NextURL: nextURL,
}
renderOrError(w, reportContribute, data)
}
func htmlReportDetail(w http.ResponseWriter, code string) {
nextURL := BaseURL + "/report/" + code + "/evidence"
data := ContentReportDetail{
NextURL: nextURL,
UpdateURL: BaseURL + "/report/" + code + "/update",
}
renderOrError(w, reportDetail, data)
}
func htmlReportEvidence(w http.ResponseWriter, code string) {
nextURL := BaseURL + "/report/" + code + "/contribute"
data := ContentReportDetail{
NextURL: nextURL,
}
renderOrError(w, reportEvidence, data)
}
func htmlReportSchedule(w http.ResponseWriter, code string) {
nextURL := BaseURL + "/report/" + code + "/confirm"
data := ContentReportDetail{
NextURL: nextURL,
}
renderOrError(w, reportSchedule, data)
}
func htmlReportUpdate(w http.ResponseWriter, code string) {
nextURL := BaseURL + "/report/" + code + "/evidence"
data := ContentReportDetail{
NextURL: nextURL,
}
renderOrError(w, reportUpdate, data)
}
func htmlServiceRequest(w http.ResponseWriter) {
data := ContentPlaceholder{}
renderOrError(w, serviceRequest, data)
}
func htmlServiceRequestDetail(w http.ResponseWriter, code string) {
data := ContentPlaceholder{}
renderOrError(w, serviceRequestDetail, data)
}
func htmlServiceRequestLocation(w http.ResponseWriter) {
data := ContentPlaceholder{}
renderOrError(w, serviceRequestLocation, data)
}
func htmlServiceRequestMosquito(w http.ResponseWriter) {
data := ContentPlaceholder{}
renderOrError(w, serviceRequestMosquito, data)
}
func htmlServiceRequestPool(w http.ResponseWriter) {
data := ContentPlaceholder{}
renderOrError(w, serviceRequestPool, data)
}
func htmlServiceRequestQuick(w http.ResponseWriter) {
data := ContentPlaceholder{}
renderOrError(w, serviceRequestQuick, data)
}
func htmlServiceRequestQuickConfirmation(w http.ResponseWriter) {
data := ContentPlaceholder{}
renderOrError(w, serviceRequestQuickConfirmation, data)
}
func htmlServiceRequestUpdates(w http.ResponseWriter) {
data := ContentPlaceholder{}
renderOrError(w, serviceRequestUpdates, data)
}
func htmlSettings(w http.ResponseWriter, r *http.Request, user *models.User) { func htmlSettings(w http.ResponseWriter, r *http.Request, user *models.User) {
userContent, err := contentForUser(r.Context(), user) userContent, err := contentForUser(r.Context(), user)
if err != nil { if err != nil {
@ -596,7 +515,7 @@ func makeFuncMap() template.FuncMap {
} }
return funcMap return funcMap
} }
func newBuiltTemplate(name string, files ...string) BuiltTemplate { func newBuiltTemplate(name string, files ...string) *BuiltTemplate {
files_on_disk := true files_on_disk := true
all_files := append([]string{name}, files...) all_files := append([]string{name}, files...)
for _, f := range all_files { for _, f := range all_files {
@ -607,18 +526,22 @@ func newBuiltTemplate(name string, files ...string) BuiltTemplate {
break break
} }
} }
var result BuiltTemplate
if files_on_disk { if files_on_disk {
return BuiltTemplate{ result = BuiltTemplate{
files: all_files, files: all_files,
name: name, name: name,
template: nil, template: nil,
} }
} } else {
return BuiltTemplate{ result = BuiltTemplate{
files: all_files, files: all_files,
name: name, name: name,
template: parseEmbedded(all_files), template: parseEmbedded(all_files),
} }
}
templatesByFilename[name] = result
return &result
} }
func parseEmbedded(files []string) *template.Template { func parseEmbedded(files []string) *template.Template {
@ -867,11 +790,11 @@ func trapsBySource(ctx context.Context, org *models.Organization, sourceID strin
return traps, nil return traps, nil
} }
func renderOrError(w http.ResponseWriter, template BuiltTemplate, context interface{}) { func renderOrError(w http.ResponseWriter, template *BuiltTemplate, context interface{}) {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
err := template.ExecuteTemplate(buf, context) err := template.ExecuteTemplate(buf, context)
if err != nil { if err != nil {
slog.Error("Failed to render template", slog.String("err", err.Error()), slog.String("template", template.name)) log.Error().Err(err).Str("template", template.name).Msg("Failed to render template")
respondError(w, "Failed to render template", err, http.StatusInternalServerError) respondError(w, "Failed to render template", err, http.StatusInternalServerError)
return return
} }

42
main.go
View file

@ -94,29 +94,33 @@ func main() {
r.Get("/arcgis/oauth/callback", getArcgisOauthCallback) r.Get("/arcgis/oauth/callback", getArcgisOauthCallback)
r.Get("/favicon.ico", getFavicon) r.Get("/favicon.ico", getFavicon)
r.Get("/mock/data-entry", getDataEntry) r.Get("/mock", renderMock("mock-root"))
r.Get("/mock/data-entry/bad", getDataEntryBad) r.Get("/mock/admin", renderMock("admin"))
r.Get("/mock/data-entry/good", getDataEntryGood) r.Get("/mock/admin/service-request", renderMock("admin-service-request"))
r.Get("/mock/data-entry", renderMock("data-entry"))
r.Get("/mock/data-entry/bad", renderMock("data-entry-bad"))
r.Get("/mock/data-entry/good", renderMock("data-entry-good"))
r.Get("/mock/dispatch", renderMock("dispatch"))
r.Get("/mock/dispatch-results", renderMock("dispatch-results"))
r.Get("/mock/report", renderMock("report"))
r.Get("/mock/report/{code}", renderMock("report-detail"))
r.Get("/mock/report/{code}/confirm", renderMock("report-confirmation"))
r.Get("/mock/report/{code}/contribute", renderMock("report-contribute"))
r.Get("/mock/report/{code}/evidence", renderMock("report-evidence"))
r.Get("/mock/report/{code}/schedule", renderMock("report-schedule"))
r.Get("/mock/report/{code}/update", renderMock("report-update"))
r.Get("/mock/service-request", renderMock("service-request"))
r.Get("/mock/service-request/{code}", renderMock("service-request-detail"))
r.Get("/mock/service-request-location", renderMock("service-request-location"))
r.Get("/mock/service-request-mosquito", renderMock("service-request-mosquito"))
r.Get("/mock/service-request-pool", renderMock("service-request-pool"))
r.Get("/mock/service-request-quick", renderMock("service-request-quick"))
r.Get("/mock/service-request-quick-confirmation", renderMock("service-request-quick-confirmation"))
r.Get("/mock/service-request-updates", renderMock("service-request-updates"))
r.Get("/oauth/refresh", getOAuthRefresh) r.Get("/oauth/refresh", getOAuthRefresh)
r.Get("/phone-call", getPhoneCall)
r.Get("/qr-code/report/{code}", getQRCodeReport) r.Get("/qr-code/report/{code}", getQRCodeReport)
r.Get("/report", getReport)
r.Get("/report/{code}", getReportDetail)
r.Get("/report/{code}/confirm", getReportConfirmation)
r.Get("/report/{code}/contribute", getReportContribute)
r.Get("/report/{code}/evidence", getReportEvidence)
r.Get("/report/{code}/schedule", getReportSchedule)
r.Get("/report/{code}/update", getReportUpdate)
r.Get("/service-request", getServiceRequest)
r.Get("/service-request/{code}", getServiceRequestDetail)
r.Get("/service-request-location", getServiceRequestLocation)
r.Get("/service-request-mosquito", getServiceRequestMosquito)
r.Get("/service-request-pool", getServiceRequestPool)
r.Get("/service-request-quick", getServiceRequestQuick)
r.Get("/service-request-quick-confirmation", getServiceRequestQuickConfirmation)
r.Get("/service-request-updates", getServiceRequestUpdates)
r.Get("/signin", getSignin) r.Get("/signin", getSignin)
r.Post("/signin", postSignin) r.Post("/signin", postSignin)
r.Get("/signup", getSignup) r.Get("/signup", getSignup)

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Dash{{end}} {{define "title"}}Dash{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
body { body {
overflow-x: hidden; overflow-x: hidden;
} }
@ -63,6 +64,7 @@ body {
.tech-table tr td { .tech-table tr td {
vertical-align: middle; vertical-align: middle;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<!-- Header --> <!-- Header -->
@ -75,6 +77,7 @@ body {
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<!--
<div class="collapse navbar-collapse" id="navbarNav"> <div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto"> <ul class="navbar-nav ms-auto">
<li class="nav-item"> <li class="nav-item">
@ -90,7 +93,7 @@ body {
<a class="nav-link" href="#"><i class="bi bi-gear"></i> Settings</a> <a class="nav-link" href="#"><i class="bi bi-gear"></i> Settings</a>
</li> </li>
</ul> </ul>
</div> </div>-->
</div> </div>
</nav> </nav>
@ -117,7 +120,7 @@ body {
<div class="card shadow-sm h-100"> <div class="card shadow-sm h-100">
<div class="card-header bg-success text-white d-flex justify-content-between align-items-center py-2"> <div class="card-header bg-success text-white d-flex justify-content-between align-items-center py-2">
<h5 class="mb-0">Mosquito Activity & Relief</h5> <h5 class="mb-0">Mosquito Activity & Relief</h5>
<a href="/phone-call/service-request" class="btn btn-light btn-sm"><i class="bi bi-plus-circle"></i> New Service Request</a> <a href="/mock/admin/service-request" class="btn btn-light btn-sm"><i class="bi bi-plus-circle"></i> New Service Request</a>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="row mb-2"> <div class="row mb-2">

View file

@ -10,12 +10,7 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
<!-- Fontawesome Icons --> <!-- Fontawesome Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<script> {{block "extraheader" .}} {{end}}
{{block "script" .}} {{end}}
</script>
<style>
{{template "style" .}}
</style>
</head> </head>
<body> <body>
{{template "content" .}} {{template "content" .}}

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Data Entry{{end}} {{define "title"}}Data Entry{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
.results-container { .results-container {
max-width: 1000px; max-width: 1000px;
margin: 0 auto; margin: 0 auto;
@ -28,6 +29,7 @@
.table-header-line { .table-header-line {
width: 20%; width: 20%;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="container mt-4 results-container mb-5"> <div class="container mt-4 results-container mb-5">

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Data Entry{{end}} {{define "title"}}Data Entry{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
.results-container { .results-container {
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
@ -18,6 +19,7 @@
.status-badge { .status-badge {
font-size: 0.85rem; font-size: 0.85rem;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="container mt-4 results-container"> <div class="container mt-4 results-container">

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Data Entry{{end}} {{define "title"}}Data Entry{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
.upload-container { .upload-container {
max-width: 800px; max-width: 800px;
margin: 0 auto; margin: 0 auto;
@ -22,6 +23,7 @@
color: red; color: red;
margin-left: 3px; margin-left: 3px;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="container mt-4 upload-container"> <div class="container mt-4 upload-container">

View file

@ -0,0 +1,260 @@
{{template "base.html" .}}
{{define "title"}}Data Entry{{end}}
{{define "extraheader"}}
<script>
document.addEventListener('DOMContentLoaded', function() {
const checkboxes = document.querySelectorAll('.route-select');
const switchBtn = document.getElementById('switchRoutesBtn');
// Enable/disable switch button based on selection
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', () => {
const checkedBoxes = document.querySelectorAll('.route-select:checked');
switchBtn.disabled = checkedBoxes.length !== 2;
});
});
// For demonstration purposes
switchBtn.addEventListener('click', function() {
const selectedRoutes = Array.from(document.querySelectorAll('.route-select:checked')).map(cb => cb.value);
alert(`Switching routes ${selectedRoutes[0]} and ${selectedRoutes[1]}`);
// In a real application, this would trigger the route switching logic
});
});
</script>
<style>
.main-container {
max-width: 1200px;
margin: 0 auto;
}
.map-container {
height: 400px;
background-color: #e9ecef;
border: 1px solid #dee2e6;
border-radius: 5px;
position: relative;
}
.route-badge {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 1.2rem;
border-radius: 50%;
}
.tech-photo {
width: 40px;
height: 40px;
object-fit: cover;
border-radius: 50%;
}
.action-button {
padding: 10px 20px;
}
.completion-alert {
border-left: 5px solid #0d6efd;
}
</style>
{{end}}
{{define "content"}}
<div class="d-flex justify-content-between align-items-center mb-3">
<h1>Route Calculation Results</h1>
<a href="{{ .URLs.Dispatch }}" class="btn btn-outline-primary">
<i class="bi bi-pencil-square me-1"></i> Edit Parameters
</a>
</div>
<!-- Map View -->
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<h2 class="h4 mb-0">Route Map</h2>
</div>
<div class="card-body p-2">
<div class="map-container d-flex align-items-center justify-content-center">
<div class="text-center">
<i class="bi bi-map fs-1 text-muted"></i>
<h3 class="text-muted">Interactive Map View</h3>
<p class="text-muted">Routes are color-coded by technician assignment</p>
</div>
</div>
</div>
</div>
<!-- Completion Projection -->
<div class="alert alert-info mb-4 completion-alert">
<div class="d-flex align-items-center">
<i class="bi bi-calendar-check fs-4 me-3"></i>
<div>
<h4 class="h5 mb-1">Coverage Projection</h4>
<p class="mb-0">If every day were like today, all pools would be complete on <strong>October 27, 2023</strong></p>
</div>
</div>
</div>
<!-- Route Summary Table -->
<div class="card mb-4">
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
<h2 class="h4 mb-0">Route Summary</h2>
<button class="btn btn-sm btn-light" id="switchRoutesBtn" disabled>
<i class="bi bi-arrow-left-right me-1"></i> Switch Selected Routes
</button>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th width="40">Select</th>
<th width="50">Route</th>
<th>Technician</th>
<th>Cold Call Pools</th>
<th>Drone Inspections</th>
<th>Service Calls</th>
<th>Warrants</th>
<th>Est. Time</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="form-check">
<input class="form-check-input route-select" type="checkbox" value="A" id="routeA">
</div>
</td>
<td>
<div class="route-badge bg-primary text-white">A</div>
</td>
<td>
<div class="d-flex align-items-center">
<img src="https://randomuser.me/api/portraits/men/32.jpg" alt="John Davis" class="tech-photo me-2">
<span>John Davis</span>
</div>
</td>
<td>12</td>
<td>0</td>
<td>5</td>
<td>2</td>
<td>6h 15m</td>
<td>
<button class="btn btn-sm btn-outline-secondary">
<i class="bi bi-eye"></i>
</button>
</td>
</tr>
<tr>
<td>
<div class="form-check">
<input class="form-check-input route-select" type="checkbox" value="B" id="routeB">
</div>
</td>
<td>
<div class="route-badge bg-success text-white">B</div>
</td>
<td>
<div class="d-flex align-items-center">
<img src="https://randomuser.me/api/portraits/women/65.jpg" alt="Sarah Johnson" class="tech-photo me-2">
<span>Sarah Johnson</span>
</div>
</td>
<td>8</td>
<td>3</td>
<td>4</td>
<td>1</td>
<td>7h 30m</td>
<td>
<button class="btn btn-sm btn-outline-secondary">
<i class="bi bi-eye"></i>
</button>
</td>
</tr>
<tr>
<td>
<div class="form-check">
<input class="form-check-input route-select" type="checkbox" value="C" id="routeC">
</div>
</td>
<td>
<div class="route-badge bg-danger text-white">C</div>
</td>
<td>
<div class="d-flex align-items-center">
<img src="https://randomuser.me/api/portraits/men/44.jpg" alt="Michael Chen" class="tech-photo me-2">
<span>Michael Chen</span>
</div>
</td>
<td>10</td>
<td>4</td>
<td>3</td>
<td>0</td>
<td>7h 45m</td>
<td>
<button class="btn btn-sm btn-outline-secondary">
<i class="bi bi-eye"></i>
</button>
</td>
</tr>
<tr>
<td>
<div class="form-check">
<input class="form-check-input route-select" type="checkbox" value="D" id="routeD">
</div>
</td>
<td>
<div class="route-badge bg-warning text-dark">D</div>
</td>
<td>
<div class="d-flex align-items-center">
<img src="https://randomuser.me/api/portraits/women/22.jpg" alt="Jessica Martinez" class="tech-photo me-2">
<span>Jessica Martinez</span>
</div>
</td>
<td>14</td>
<td>2</td>
<td>6</td>
<td>3</td>
<td>8h 00m</td>
<td>
<button class="btn btn-sm btn-outline-secondary">
<i class="bi bi-eye"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="d-flex justify-content-between">
<a href="{{ .URLs.Dispatch }}" class="btn btn-outline-secondary action-button">
<i class="bi bi-arrow-left me-2"></i>Back to Parameters
</a>
<a href="{{ .URLs.Root }}" class="btn btn-success action-button">
<i class="bi bi-check-circle me-2"></i>Approve & Dispatch
</a>
</div>
</div>
<!-- Modal for Route Details -->
<div class="modal fade" id="routeDetailsModal" tabindex="-1" aria-labelledby="routeDetailsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="routeDetailsModalLabel">Route A Details</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<!-- Route details would go here -->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
{{end}}

172
templates/dispatch.html Normal file
View file

@ -0,0 +1,172 @@
{{template "base.html" .}}
{{define "title"}}Data Entry{{end}}
{{define "extraheader"}}
<style>
.tech-photo {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 50%;
}
.main-container {
max-width: 1200px;
margin: 0 auto;
}
.action-button {
padding: 15px 30px;
font-size: 1.2rem;
}
</style>
{{end}}
{{define "content"}}
<div class="container-fluid p-4 main-container">
<h1 class="mb-4">Technician Routing & Dispatch</h1>
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<h2 class="h4 mb-0">Technician Roster</h2>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th>Technician</th>
<th>Working Hours</th>
<th>Truck Assignment</th>
<th>Warrant Service</th>
<th>Drone Certified</th>
<th>Routing Options</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="d-flex align-items-center">
<img src="https://randomuser.me/api/portraits/men/32.jpg" alt="John Davis" class="tech-photo me-3">
<div>
<div class="fw-bold">John Davis</div>
<div class="small text-muted">ID: T-1001</div>
</div>
</div>
</td>
<td>7:00 AM - 3:30 PM</td>
<td>Truck #103</td>
<td><span class="badge bg-success">Yes</span></td>
<td><span class="badge bg-danger">No</span></td>
<td>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="warrant-partner1" data-bs-toggle="tooltip" data-bs-placement="left" title="Require second person for warrant service">
<label class="form-check-label" for="warrant-partner1">Warrant Partner</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="resident-history1" data-bs-toggle="tooltip" data-bs-placement="left" title="Route to previously visited residents">
<label class="form-check-label" for="resident-history1">Prior Interactions</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="include-route1" checked data-bs-toggle="tooltip" data-bs-placement="left" title="Include in route calculations">
<label class="form-check-label" for="include-route1">Include in Routes</label>
</div>
</td>
</tr>
<tr>
<td>
<div class="d-flex align-items-center">
<img src="https://randomuser.me/api/portraits/women/65.jpg" alt="Sarah Johnson" class="tech-photo me-3">
<div>
<div class="fw-bold">Sarah Johnson</div>
<div class="small text-muted">ID: T-1042</div>
</div>
</div>
</td>
<td>8:00 AM - 4:30 PM</td>
<td>Truck #118</td>
<td><span class="badge bg-success">Yes</span></td>
<td><span class="badge bg-success">Yes</span></td>
<td>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="warrant-partner2" data-bs-toggle="tooltip" data-bs-placement="left" title="Require second person for warrant service">
<label class="form-check-label" for="warrant-partner2">Warrant Partner</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="resident-history2" checked data-bs-toggle="tooltip" data-bs-placement="left" title="Route to previously visited residents">
<label class="form-check-label" for="resident-history2">Prior Interactions</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="include-route2" checked data-bs-toggle="tooltip" data-bs-placement="left" title="Include in route calculations">
<label class="form-check-label" for="include-route2">Include in Routes</label>
</div>
</td>
</tr>
<tr>
<td>
<div class="d-flex align-items-center">
<img src="https://randomuser.me/api/portraits/men/44.jpg" alt="Michael Chen" class="tech-photo me-3">
<div>
<div class="fw-bold">Michael Chen</div>
<div class="small text-muted">ID: T-1019</div>
</div>
</div>
</td>
<td>6:30 AM - 3:00 PM</td>
<td>Truck #107</td>
<td><span class="badge bg-danger">No</span></td>
<td><span class="badge bg-success">Yes</span></td>
<td>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="warrant-partner3" disabled data-bs-toggle="tooltip" data-bs-placement="left" title="Require second person for warrant service">
<label class="form-check-label" for="warrant-partner3">Warrant Partner</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="resident-history3" data-bs-toggle="tooltip" data-bs-placement="left" title="Route to previously visited residents">
<label class="form-check-label" for="resident-history3">Prior Interactions</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="include-route3" checked data-bs-toggle="tooltip" data-bs-placement="left" title="Include in route calculations">
<label class="form-check-label" for="include-route3">Include in Routes</label>
</div>
</td>
</tr>
<tr>
<td>
<div class="d-flex align-items-center">
<img src="https://randomuser.me/api/portraits/women/22.jpg" alt="Jessica Martinez" class="tech-photo me-3">
<div>
<div class="fw-bold">Jessica Martinez</div>
<div class="small text-muted">ID: T-1055</div>
</div>
</div>
</td>
<td>7:30 AM - 4:00 PM</td>
<td>Truck #112</td>
<td><span class="badge bg-success">Yes</span></td>
<td><span class="badge bg-success">Yes</span></td>
<td>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="warrant-partner4" checked data-bs-toggle="tooltip" data-bs-placement="left" title="Require second person for warrant service">
<label class="form-check-label" for="warrant-partner4">Warrant Partner</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="resident-history4" checked data-bs-toggle="tooltip" data-bs-placement="left" title="Route to previously visited residents">
<label class="form-check-label" for="resident-history4">Prior Interactions</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="include-route4" checked data-bs-toggle="tooltip" data-bs-placement="left" title="Include in route calculations">
<label class="form-check-label" for="include-route4">Include in Routes</label>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="d-grid gap-2 col-md-6 mx-auto">
<a href="{{ .URLs.DispatchResults }}" class="btn btn-primary btn-lg action-button" type="button">
<i class="bi bi-calculator me-2"></i>Calculate Routes
</a>
</div>
</div>
{{end}}

View file

@ -1,7 +1,7 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Dash{{end}} {{define "title"}}Dash{{end}}
{{define "style"}} {{define "extraheader"}}
{{end}} {{end}}
{{define "content"}} {{define "content"}}
{{end}} {{end}}

51
templates/mock-root.html Normal file
View file

@ -0,0 +1,51 @@
{{template "base.html" .}}
{{define "title"}}Data Entry{{end}}
{{define "extraheader"}}
{{end}}
{{define "content"}}
<div class="container">
<h1>Mock Listing</h1>
<table class="table">
<thead>
<tr>
<th scope="col" style="min-width: 20vw">Link</th>
<th scope="col">Name</th>
<th scope="col">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="/mock/admin">/mock/admin</a></td>
<td>Admin</td>
<td>Used by office admins to handle phone calls and other public-facing responsibilities</td>
</tr>
<tr>
<td><a href="/mock/data-entry">/mock/flyover-data-entry</a></td>
<td>Flyover Data Entry</td>
<td>Used to upload CSV files with information about problematic pools</td>
</tr>
<tr>
<td><a href="/mock/dispatch">/mock/dispatch</a></td>
<td>Dispatch</td>
<td>Used each day to calculate the working routes for technicians</td>
</tr>
<tr>
<td><a href="/mock/report">/mock/report</a></td>
<td>Reporting Overview</td>
<td>Shows examples of text message contents, printable QR codes, and email bodies for sending to the public to help them self-report</td>
</tr>
<tr>
<td><a href="/mock/report/abc-123">/mock/report/abc-123</a></td>
<td>Self-Report</td>
<td>A page for members of the public to report a green pool.</td>
</tr>
<tr>
<td><a href="/mock/service-request">/mock/service-request</a></td>
<td>Service Request</td>
<td>A page for members of the public to make a direct service request</td>
</tr>
</tbody>
</table>
</div>
{{end}}

View file

@ -1,7 +1,8 @@
{{template "authenticated.html" .}} {{template "authenticated.html" .}}
{{define "title"}}Dash{{end}} {{define "title"}}Dash{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
.connect-container { .connect-container {
max-width: 800px; max-width: 800px;
margin: 0 auto; margin: 0 auto;
@ -42,6 +43,7 @@
.connect-btn { .connect-btn {
margin-top: 30px; margin-top: 30px;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="container min-vh-100 d-flex align-items-center justify-content-center py-5"> <div class="container min-vh-100 d-flex align-items-center justify-content-center py-5">

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Login{{end}} {{define "title"}}Login{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
body { body {
background-color: #f8f9fa; background-color: #f8f9fa;
} }
@ -114,6 +115,7 @@
font-weight: 600; font-weight: 600;
letter-spacing: 1px; letter-spacing: 1px;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="page-container"> <div class="page-container">

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Login{{end}} {{define "title"}}Login{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
body { body {
background-color: #f8f9fa; background-color: #f8f9fa;
} }
@ -125,6 +126,7 @@
border-radius: 5px; border-radius: 5px;
margin-bottom: 20px; margin-bottom: 20px;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="page-container"> <div class="page-container">
@ -220,10 +222,10 @@
<!-- Action Buttons --> <!-- Action Buttons -->
<div class="d-flex justify-content-between mt-4"> <div class="d-flex justify-content-between mt-4">
<button type="button" class="btn btn-outline-secondary"> <a href="{{ .URLs.ReportEvidence }}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-2"></i> Back <i class="bi bi-arrow-left me-2"></i> Back
</button> </a>
<a href="{{ .NextURL }}" class="btn btn-primary"> <a href="{{ .URLs.ReportSchedule }}" class="btn btn-primary">
Next Step <i class="bi bi-arrow-right ms-2"></i> Next Step <i class="bi bi-arrow-right ms-2"></i>
</a> </a>
</div> </div>

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Login{{end}} {{define "title"}}Login{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
body { body {
background-color: #f8f9fa; background-color: #f8f9fa;
} }
@ -70,6 +71,7 @@ body {
.progress { .progress {
height: 8px; height: 8px;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="page-container"> <div class="page-container">
@ -113,10 +115,10 @@ body {
<!-- Action Buttons --> <!-- Action Buttons -->
<div class="action-buttons"> <div class="action-buttons">
<a href="{{ .NextURL }}" class="btn btn-success flex-grow-1"> <a href="{{ .URLs.ReportEvidence }}" class="btn btn-success flex-grow-1">
<i class="bi bi-check-circle me-2"></i> Correct <i class="bi bi-check-circle me-2"></i> Correct
</a> </a>
<a href="{{ .UpdateURL }}" class="btn btn-outline-primary flex-grow-1"> <a href="{{ .URLs.ReportUpdate }}" class="btn btn-outline-primary flex-grow-1">
<i class="bi bi-geo-alt me-2"></i> Update Location <i class="bi bi-geo-alt me-2"></i> Update Location
</a> </a>
</div> </div>

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Login{{end}} {{define "title"}}Login{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
body { body {
background-color: #f8f9fa; background-color: #f8f9fa;
} }
@ -84,6 +85,7 @@
.trap-low { .trap-low {
color: #198754; color: #198754;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="page-container"> <div class="page-container">
@ -227,7 +229,7 @@
<!-- Action Button --> <!-- Action Button -->
<div class="d-grid gap-2 mt-4"> <div class="d-grid gap-2 mt-4">
<a href="{{ .NextURL }}" class="btn btn-primary btn-lg"> <a href="{{ .URLs.ReportContribute }}" class="btn btn-primary btn-lg">
<i class="bi bi-arrow-right-circle me-2"></i> Next Step: Upload Current Photos <i class="bi bi-arrow-right-circle me-2"></i> Next Step: Upload Current Photos
</a> </a>
</div> </div>

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Login{{end}} {{define "title"}}Login{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
body { body {
background-color: #f8f9fa; background-color: #f8f9fa;
} }
@ -125,6 +126,7 @@
border-radius: 5px; border-radius: 5px;
margin-bottom: 20px; margin-bottom: 20px;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="page-container"> <div class="page-container">
@ -298,10 +300,10 @@
<!-- Action Buttons --> <!-- Action Buttons -->
<div class="d-flex justify-content-between mt-4"> <div class="d-flex justify-content-between mt-4">
<button type="button" class="btn btn-outline-secondary"> <a href="{{ .URLs.ReportContribute }}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-2"></i> Back <i class="bi bi-arrow-left me-2"></i> Back
</button> </a>
<a href="{{ .NextURL }}" class="btn btn-success"> <a href="{{ .URLs.ReportConfirmation }}" class="btn btn-success">
<i class="bi bi-calendar-check me-2"></i> Confirm Appointment <i class="bi bi-calendar-check me-2"></i> Confirm Appointment
</a> </a>
</div> </div>

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Login{{end}} {{define "title"}}Login{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
body { body {
background-color: #f8f9fa; background-color: #f8f9fa;
} }
@ -98,6 +99,7 @@ body {
.form-section { .form-section {
margin-bottom: 25px; margin-bottom: 25px;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="page-container"> <div class="page-container">

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Login{{end}} {{define "title"}}Login{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
.entry-container { .entry-container {
max-width: 1000px; max-width: 1000px;
margin: 0 auto; margin: 0 auto;
@ -100,6 +101,7 @@
border-bottom: none; border-bottom: none;
border-radius: 20px 20px 0 0; border-radius: 20px 20px 0 0;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="container py-5"> <div class="container py-5">
@ -125,7 +127,7 @@
<p>Customers will receive the following text message with a link to begin the reporting process:</p> <p>Customers will receive the following text message with a link to begin the reporting process:</p>
<div class="sms-mockup"> <div class="sms-mockup">
<strong>Vector Control:</strong> We noticed a potential green pool at your property. Please tap the link to report status or schedule inspection: <a href="{{ .URL }}">{{ .URL }}</a> <strong>Vector Control:</strong> We noticed a potential green pool at your property. Please tap the link to report status or schedule inspection: <a href="{{ .URLs.ReportDetail }}">{{ .URLs.ReportDetail }}</a>
</div> </div>
<div class="mt-3"> <div class="mt-3">
@ -154,7 +156,7 @@
<img src="/qr-code/report/t78fd3" width="256" height="256"/> <img src="/qr-code/report/t78fd3" width="256" height="256"/>
<p><strong>Scan this code</strong> with your phone camera to report your pool status or schedule an inspection.</p> <p><strong>Scan this code</strong> with your phone camera to report your pool status or schedule an inspection.</p>
<p class="small text-muted">Or visit: {{ .URL }}</p> <p class="small text-muted">Or visit: {{ .URLs.ReportDetail }}</p>
</div> </div>
<div class="mt-3"> <div class="mt-3">
@ -192,7 +194,7 @@
<a href="/report/t78fd3" class="btn btn-primary">Report Pool Status or Schedule Inspection</a> <a href="/report/t78fd3" class="btn btn-primary">Report Pool Status or Schedule Inspection</a>
</div> </div>
<p>Please click the button above or visit <a href="{{ .URL }}">{{ .URL }}</a> to complete a brief questionnaire about your pool status. This will help us determine if an inspection is needed or if you've already addressed the issue.</p> <p>Please click the button above or visit <a href="{{ .URLs.ReportDetail }}">{{ .URLs.ReportDetail }}</a> to complete a brief questionnaire about your pool status. This will help us determine if an inspection is needed or if you've already addressed the issue.</p>
<p>Thank you for helping keep our community safe and healthy.</p> <p>Thank you for helping keep our community safe and healthy.</p>
@ -214,7 +216,7 @@
<div class="d-flex justify-content-between mt-4"> <div class="d-flex justify-content-between mt-4">
<a href="/" class="btn btn-outline-primary">Back to Dashboard</a> <a href="/" class="btn btn-outline-primary">Back to Dashboard</a>
<a href="{{ .URL }}" class="btn btn-success">Test Reporting Flow</a> <a href="{{ .URLs.ReportDetail }}" class="btn btn-success">Test Reporting Flow</a>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Dash{{end}} {{define "title"}}Dash{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
.district-logo { .district-logo {
max-height: 80px; max-height: 80px;
width: auto; width: auto;
@ -85,6 +86,7 @@
color: #6c757d; color: #6c757d;
margin-bottom: 2px; margin-bottom: 2px;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<!-- Header --> <!-- Header -->

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Dash{{end}} {{define "title"}}Dash{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
.district-logo { .district-logo {
max-height: 80px; max-height: 80px;
width: auto; width: auto;
@ -69,6 +70,7 @@
.instruction-card { .instruction-card {
border-left: 4px solid #0d6efd; border-left: 4px solid #0d6efd;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<!-- Header --> <!-- Header -->

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Dash{{end}} {{define "title"}}Dash{{end}}
{{define "script"}} {{define "extraheader"}}
<script>
// Handle inspection type selection // Handle inspection type selection
function selectInspectionType(type) { function selectInspectionType(type) {
// Remove selected class from both cards // Remove selected class from both cards
@ -41,8 +42,8 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
}); });
}); });
{{end}} </script>
{{define "style"}} <style>
.district-logo { .district-logo {
max-height: 80px; max-height: 80px;
width: auto; width: auto;
@ -139,6 +140,7 @@ document.addEventListener('DOMContentLoaded', function() {
border-left: 4px solid #0d6efd; border-left: 4px solid #0d6efd;
background-color: #f8f9fa; background-color: #f8f9fa;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<!-- Header --> <!-- Header -->

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Dash{{end}} {{define "title"}}Dash{{end}}
{{define "script"}} {{define "extraheader"}}
<script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const photoUpload = document.getElementById('photoUpload'); const photoUpload = document.getElementById('photoUpload');
const imagePreview = document.getElementById('imagePreview'); const imagePreview = document.getElementById('imagePreview');
@ -74,8 +75,8 @@ document.addEventListener('DOMContentLoaded', function() {
photoUpload.value = ''; photoUpload.value = '';
} }
}); });
{{end}} </script>
{{define "style"}} <style>
.district-logo { .district-logo {
max-height: 80px; max-height: 80px;
width: auto; width: auto;
@ -167,6 +168,7 @@ document.addEventListener('DOMContentLoaded', function() {
border-radius: 5px; border-radius: 5px;
margin-top: 2rem; margin-top: 2rem;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<!-- Header --> <!-- Header -->
@ -174,7 +176,7 @@ document.addEventListener('DOMContentLoaded', function() {
<div class="container"> <div class="container">
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-md-6"> <div class="col-md-6">
<h1 class="district-name">[District Name]</h1> <h1 class="district-name">{{ .DistrictName }}</h1>
</div> </div>
<div class="col-md-6 text-md-end"> <div class="col-md-6 text-md-end">
<img src="placeholder-logo.png" alt="District Logo" class="district-logo"> <img src="placeholder-logo.png" alt="District Logo" class="district-logo">
@ -510,7 +512,7 @@ document.addEventListener('DOMContentLoaded', function() {
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<p class="mb-0">&copy; 2023 [District Name] Mosquito Management District</p> <p class="mb-0">&copy; 2023 {{ .DistrictName }} Mosquito Management District</p>
</div> </div>
<div class="col-md-6 text-md-end"> <div class="col-md-6 text-md-end">
<p class="mb-0">Contact: (555) 123-4567 | info@mosquitodistrict.gov</p> <p class="mb-0">Contact: (555) 123-4567 | info@mosquitodistrict.gov</p>

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Dash{{end}} {{define "title"}}Dash{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
.district-logo { .district-logo {
max-height: 60px; max-height: 60px;
width: auto; width: auto;
@ -41,6 +42,7 @@
height: 80px; height: 80px;
} }
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<!-- Header --> <!-- Header -->

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Dash{{end}} {{define "title"}}Dash{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
.district-logo { .district-logo {
max-height: 60px; max-height: 60px;
width: auto; width: auto;
@ -49,6 +50,7 @@
font-size: 1.5rem; font-size: 1.5rem;
} }
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<!-- Header --> <!-- Header -->

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Dash{{end}} {{define "title"}}Dash{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
.option-card { .option-card {
transition: transform 0.3s; transition: transform 0.3s;
height: 100%; height: 100%;
@ -33,6 +34,7 @@
margin: 2rem 0; margin: 2rem 0;
} }
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<!-- Header --> <!-- Header -->
@ -40,7 +42,7 @@
<div class="container"> <div class="container">
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-md-6"> <div class="col-md-6">
<h1 class="district-name">[District Name]</h1> <h1 class="district-name">{{ .DistrictName }}</h1>
</div> </div>
<div class="col-md-6 text-md-end"> <div class="col-md-6 text-md-end">
<img src="placeholder-logo.png" alt="District Logo" class="district-logo"> <img src="placeholder-logo.png" alt="District Logo" class="district-logo">
@ -149,7 +151,7 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<p class="mb-0">&copy; 2023 [District Name] Mosquito Management District</p> <p class="mb-0">&copy; 2023 {{ .DistrictName }} Mosquito Management District</p>
</div> </div>
<div class="col-md-6 text-md-end"> <div class="col-md-6 text-md-end">
<p class="mb-0">Contact: (555) 123-4567 | info@mosquitodistrict.gov</p> <p class="mb-0">Contact: (555) 123-4567 | info@mosquitodistrict.gov</p>

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Dash{{end}} {{define "title"}}Dash{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
.service-card { .service-card {
transition: transform 0.3s; transition: transform 0.3s;
height: 100%; height: 100%;
@ -21,6 +22,7 @@
background-color: #ffefd5; background-color: #ffefd5;
border-left: 4px solid #ff9800; border-left: 4px solid #ff9800;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<!-- Header --> <!-- Header -->
@ -28,7 +30,7 @@
<div class="container"> <div class="container">
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-md-6"> <div class="col-md-6">
<h1 class="district-name">[District Name]</h1> <h1 class="district-name">{{ .DistrictName }}</h1>
</div> </div>
<div class="col-md-6 text-md-end"> <div class="col-md-6 text-md-end">
<img src="placeholder-logo.png" alt="District Logo" class="district-logo"> <img src="placeholder-logo.png" alt="District Logo" class="district-logo">
@ -61,7 +63,7 @@
<div class="row"> <div class="row">
<div class="col-12 text-center"> <div class="col-12 text-center">
<h4 class="mb-2">On the go?</h4> <h4 class="mb-2">On the go?</h4>
<a href="/service-request-quick" class="btn btn-dark btn-lg">Make a Quick Report</a> <a href="/mock/service-request-quick" class="btn btn-dark btn-lg">Make a Quick Report</a>
<p class="mb-0 mt-2"><small>Report mosquito issues in under 60 seconds</small></p> <p class="mb-0 mt-2"><small>Report mosquito issues in under 60 seconds</small></p>
</div> </div>
</div> </div>
@ -84,7 +86,7 @@
</div> </div>
<h4 class="card-title">Follow-up or Check Status</h4> <h4 class="card-title">Follow-up or Check Status</h4>
<p class="card-text">Check on a previous request or view current mosquito activity in your area.</p> <p class="card-text">Check on a previous request or view current mosquito activity in your area.</p>
<a href="/service-request-updates" class="btn btn-primary mt-3">Get Updates</a> <a href="/mock/service-request-updates" class="btn btn-primary mt-3">Get Updates</a>
</div> </div>
</div> </div>
</div> </div>
@ -100,7 +102,7 @@
</div> </div>
<h4 class="card-title">Report a Green Pool</h4> <h4 class="card-title">Report a Green Pool</h4>
<p class="card-text">Report stagnant water sources like abandoned pools that may breed mosquitoes.</p> <p class="card-text">Report stagnant water sources like abandoned pools that may breed mosquitoes.</p>
<a href="/service-request-pool" class="btn btn-primary mt-3">Report Source</a> <a href="/mock/service-request-pool" class="btn btn-primary mt-3">Report Source</a>
</div> </div>
</div> </div>
</div> </div>
@ -116,7 +118,7 @@
</div> </div>
<h4 class="card-title">Report Mosquito Nuisance</h4> <h4 class="card-title">Report Mosquito Nuisance</h4>
<p class="card-text">Report areas with high adult mosquito activity causing discomfort or concern.</p> <p class="card-text">Report areas with high adult mosquito activity causing discomfort or concern.</p>
<a href="/service-request-mosquito" class="btn btn-primary mt-3">Report Problem</a> <a href="/mock/service-request-mosquito" class="btn btn-primary mt-3">Report Problem</a>
</div> </div>
</div> </div>
</div> </div>
@ -133,7 +135,7 @@
<p class="mb-0">Use our streamlined form to report mosquito issues in under 60 seconds</p> <p class="mb-0">Use our streamlined form to report mosquito issues in under 60 seconds</p>
</div> </div>
<div class="col-md-4 text-md-end mt-3 mt-md-0"> <div class="col-md-4 text-md-end mt-3 mt-md-0">
<a href="/service-request-quick" class="btn btn-warning">Quick Report</a> <a href="/mock/service-request-quick" class="btn btn-warning">Quick Report</a>
</div> </div>
</div> </div>
</div> </div>
@ -149,7 +151,7 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<p class="mb-0">&copy; 2023 [District Name] Mosquito Management District</p> <p class="mb-0">&copy; 2023 {{.DistrictName}}</p>
</div> </div>
<div class="col-md-6 text-md-end"> <div class="col-md-6 text-md-end">
<p class="mb-0">Contact: (555) 123-4567 | info@mosquitodistrict.gov</p> <p class="mb-0">Contact: (555) 123-4567 | info@mosquitodistrict.gov</p>

View file

@ -1,7 +1,7 @@
{{template "authenticated.html" .}} {{template "authenticated.html" .}}
{{define "title"}}Dash{{end}} {{define "title"}}Dash{{end}}
{{define "style"}} {{define "extraheader"}}
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<p>Imagine settings here</p> <p>Imagine settings here</p>

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Login{{end}} {{define "title"}}Login{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
.login-container { .login-container {
max-width: 900px; max-width: 900px;
margin: 0 auto; margin: 0 auto;
@ -21,6 +22,7 @@
.login-header { .login-header {
margin-bottom: 25px; margin-bottom: 25px;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="container min-vh-100 d-flex align-items-center justify-content-center py-5"> <div class="container min-vh-100 d-flex align-items-center justify-content-center py-5">

View file

@ -1,7 +1,8 @@
{{template "base.html" .}} {{template "base.html" .}}
{{define "title"}}Login{{end}} {{define "title"}}Login{{end}}
{{define "style"}} {{define "extraheader"}}
<style>
.register-container { .register-container {
max-width: 900px; max-width: 900px;
margin: 0 auto; margin: 0 auto;
@ -35,6 +36,7 @@
justify-content: center; justify-content: center;
border-radius: 6px; border-radius: 6px;
} }
</style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="container min-vh-100 d-flex align-items-center justify-content-center py-5"> <div class="container min-vh-100 d-flex align-items-center justify-content-center py-5">