diff --git a/endpoint.go b/endpoint.go index c490a92a..4136deb9 100644 --- a/endpoint.go +++ b/endpoint.go @@ -66,19 +66,6 @@ func getOAuthRefresh(w http.ResponseWriter, r *http.Request) { 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) { code := chi.URLParam(r, "code") if code == "" { @@ -138,40 +125,6 @@ func getQRCodeReport(w http.ResponseWriter, r *http.Request) { 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) { 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) { htmlSettings(w, r, u) } @@ -326,3 +246,13 @@ func postSignup(w http.ResponseWriter, r *http.Request) { 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) + } +} diff --git a/html.go b/html.go index f91a22a9..35db1201 100644 --- a/html.go +++ b/html.go @@ -8,7 +8,6 @@ import ( "fmt" "html/template" "io" - "log/slog" "math" "net/http" "os" @@ -41,10 +40,13 @@ var ( // Unauthenticated pages var ( + admin = newBuiltTemplate("admin", "base") dataEntry = newBuiltTemplate("data-entry", "base") dataEntryGood = newBuiltTemplate("data-entry-good", "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") reportConfirmation = newBuiltTemplate("report-confirmation", "base") reportContribute = newBuiltTemplate("report-contribute", "base") @@ -64,6 +66,7 @@ var ( signup = newBuiltTemplate("signup", "base") ) var components = [...]string{"header", "map"} +var templatesByFilename = make(map[string]BuiltTemplate, 0) type BreedingSourceSummary struct { ID string @@ -99,15 +102,26 @@ type ContentCell struct { Treatments []Treatment 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 + URLs ContentMockURLs } type ContentReportDetail struct { NextURL string UpdateURL string } type ContentReportDiagnostic struct { - URL string } type ContentDashboard struct { CountInspections int @@ -279,21 +293,6 @@ func htmlCell(ctx context.Context, w http.ResponseWriter, user *models.User, c i 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) { org, err := user.Organization().One(ctx, db.PGInstance.BobDB) if err != nil { @@ -355,6 +354,30 @@ func htmlDashboard(ctx context.Context, w http.ResponseWriter, user *models.User 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) { dp := user.DisplayName data := ContentDashboard{ @@ -367,110 +390,6 @@ func htmlOauthPrompt(w http.ResponseWriter, user *models.User) { 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) { userContent, err := contentForUser(r.Context(), user) if err != nil { @@ -596,7 +515,7 @@ func makeFuncMap() template.FuncMap { } return funcMap } -func newBuiltTemplate(name string, files ...string) BuiltTemplate { +func newBuiltTemplate(name string, files ...string) *BuiltTemplate { files_on_disk := true all_files := append([]string{name}, files...) for _, f := range all_files { @@ -607,18 +526,22 @@ func newBuiltTemplate(name string, files ...string) BuiltTemplate { break } } + var result BuiltTemplate if files_on_disk { - return BuiltTemplate{ + result = BuiltTemplate{ files: all_files, name: name, template: nil, } + } else { + result = BuiltTemplate{ + files: all_files, + name: name, + template: parseEmbedded(all_files), + } } - return BuiltTemplate{ - files: all_files, - name: name, - template: parseEmbedded(all_files), - } + templatesByFilename[name] = result + return &result } func parseEmbedded(files []string) *template.Template { @@ -867,11 +790,11 @@ func trapsBySource(ctx context.Context, org *models.Organization, sourceID strin return traps, nil } -func renderOrError(w http.ResponseWriter, template BuiltTemplate, context interface{}) { +func renderOrError(w http.ResponseWriter, template *BuiltTemplate, context interface{}) { buf := &bytes.Buffer{} err := template.ExecuteTemplate(buf, context) 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) return } diff --git a/main.go b/main.go index 1929e567..42a7a923 100644 --- a/main.go +++ b/main.go @@ -94,29 +94,33 @@ func main() { r.Get("/arcgis/oauth/callback", getArcgisOauthCallback) r.Get("/favicon.ico", getFavicon) - r.Get("/mock/data-entry", getDataEntry) - r.Get("/mock/data-entry/bad", getDataEntryBad) - r.Get("/mock/data-entry/good", getDataEntryGood) + r.Get("/mock", renderMock("mock-root")) + r.Get("/mock/admin", renderMock("admin")) + 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("/phone-call", getPhoneCall) 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.Post("/signin", postSignin) r.Get("/signup", getSignup) diff --git a/templates/phone-call.html b/templates/admin.html similarity index 99% rename from templates/phone-call.html rename to templates/admin.html index 70128dc2..5f9e17b2 100644 --- a/templates/phone-call.html +++ b/templates/admin.html @@ -1,7 +1,8 @@ {{template "base.html" .}} {{define "title"}}Dash{{end}} -{{define "style"}} +{{define "extraheader"}} + {{end}} {{define "content"}} @@ -75,6 +77,7 @@ body { + @@ -117,7 +120,7 @@ body {
Routes are color-coded by technician assignment
+If every day were like today, all pools would be complete on October 27, 2023
+| Select | +Route | +Technician | +Cold Call Pools | +Drone Inspections | +Service Calls | +Warrants | +Est. Time | +Actions | +
|---|---|---|---|---|---|---|---|---|
|
+
+
+
+ |
+
+ A
+ |
+
+
+
+
+ John Davis
+ |
+ 12 | +0 | +5 | +2 | +6h 15m | ++ + | +
|
+
+
+
+ |
+
+ B
+ |
+
+
+
+
+ Sarah Johnson
+ |
+ 8 | +3 | +4 | +1 | +7h 30m | ++ + | +
|
+
+
+
+ |
+
+ C
+ |
+
+
+
+
+ Michael Chen
+ |
+ 10 | +4 | +3 | +0 | +7h 45m | ++ + | +
|
+
+
+
+ |
+
+ D
+ |
+
+
+
+
+ Jessica Martinez
+ |
+ 14 | +2 | +6 | +3 | +8h 00m | ++ + | +
| Technician | +Working Hours | +Truck Assignment | +Warrant Service | +Drone Certified | +Routing Options | +
|---|---|---|---|---|---|
|
+
+
+
+
+
+ John Davis
+ ID: T-1001
+ |
+ 7:00 AM - 3:30 PM | +Truck #103 | +Yes | +No | +
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
|
+
+
+
+
+
+ Sarah Johnson
+ ID: T-1042
+ |
+ 8:00 AM - 4:30 PM | +Truck #118 | +Yes | +Yes | +
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
|
+
+
+
+
+
+ Michael Chen
+ ID: T-1019
+ |
+ 6:30 AM - 3:00 PM | +Truck #107 | +No | +Yes | +
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
|
+
+
+
+
+
+ Jessica Martinez
+ ID: T-1055
+ |
+ 7:30 AM - 4:00 PM | +Truck #112 | +Yes | +Yes | +
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
| Link | +Name | +Description | +
|---|---|---|
| /mock/admin | +Admin | +Used by office admins to handle phone calls and other public-facing responsibilities | +
| /mock/flyover-data-entry | +Flyover Data Entry | +Used to upload CSV files with information about problematic pools | +
| /mock/dispatch | +Dispatch | +Used each day to calculate the working routes for technicians | +
| /mock/report | +Reporting Overview | +Shows examples of text message contents, printable QR codes, and email bodies for sending to the public to help them self-report | +
| /mock/report/abc-123 | +Self-Report | +A page for members of the public to report a green pool. | +
| /mock/service-request | +Service Request | +A page for members of the public to make a direct service request | +
Customers will receive the following text message with a link to begin the reporting process:
Scan this code with your phone camera to report your pool status or schedule an inspection.
-Or visit: {{ .URL }}
+Or visit: {{ .URLs.ReportDetail }}
Please click the button above or visit {{ .URL }} 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.
+Please click the button above or visit {{ .URLs.ReportDetail }} 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.
Thank you for helping keep our community safe and healthy.
@@ -214,7 +216,7 @@
@@ -510,7 +512,7 @@ document.addEventListener('DOMContentLoaded', function() {
© 2023 [District Name] Mosquito Management District
+© 2023 {{ .DistrictName }} Mosquito Management District
Contact: (555) 123-4567 | info@mosquitodistrict.gov
diff --git a/templates/service-request-quick-confirmation.html b/templates/service-request-quick-confirmation.html index babd7457..d9b14980 100644 --- a/templates/service-request-quick-confirmation.html +++ b/templates/service-request-quick-confirmation.html @@ -1,7 +1,8 @@ {{template "base.html" .}} {{define "title"}}Dash{{end}} -{{define "style"}} +{{define "extraheader"}} + {{end}} {{define "content"}} diff --git a/templates/service-request-quick.html b/templates/service-request-quick.html index 7fdc075a..915035cd 100644 --- a/templates/service-request-quick.html +++ b/templates/service-request-quick.html @@ -1,7 +1,8 @@ {{template "base.html" .}} {{define "title"}}Dash{{end}} -{{define "style"}} +{{define "extraheader"}} + {{end}} {{define "content"}} diff --git a/templates/service-request-updates.html b/templates/service-request-updates.html index 7356b82a..2762863f 100644 --- a/templates/service-request-updates.html +++ b/templates/service-request-updates.html @@ -1,7 +1,8 @@ {{template "base.html" .}} {{define "title"}}Dash{{end}} -{{define "style"}} +{{define "extraheader"}} + {{end}} {{define "content"}} @@ -40,7 +42,7 @@
@@ -149,7 +151,7 @@
© 2023 [District Name] Mosquito Management District
+© 2023 {{ .DistrictName }} Mosquito Management District
Contact: (555) 123-4567 | info@mosquitodistrict.gov
diff --git a/templates/service-request.html b/templates/service-request.html index dc1489d1..82821f74 100644 --- a/templates/service-request.html +++ b/templates/service-request.html @@ -1,7 +1,8 @@ {{template "base.html" .}} {{define "title"}}Dash{{end}} -{{define "style"}} +{{define "extraheader"}} + {{end}} {{define "content"}} @@ -28,7 +30,7 @@
@@ -61,7 +63,7 @@
Check on a previous request or view current mosquito activity in your area.
- Get Updates + Get UpdatesReport stagnant water sources like abandoned pools that may breed mosquitoes.
- Report Source + Report SourceReport areas with high adult mosquito activity causing discomfort or concern.
- Report Problem + Report ProblemUse our streamlined form to report mosquito issues in under 60 seconds
© 2023 [District Name] Mosquito Management District
+© 2023 {{.DistrictName}}
Contact: (555) 123-4567 | info@mosquitodistrict.gov
diff --git a/templates/settings.html b/templates/settings.html index 37e484aa..fb7444a8 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -1,7 +1,7 @@ {{template "authenticated.html" .}} {{define "title"}}Dash{{end}} -{{define "style"}} +{{define "extraheader"}} {{end}} {{define "content"}}Imagine settings here
diff --git a/templates/signin.html b/templates/signin.html index 3d85ef5a..7a042fb7 100644 --- a/templates/signin.html +++ b/templates/signin.html @@ -1,7 +1,8 @@ {{template "base.html" .}} {{define "title"}}Login{{end}} -{{define "style"}} +{{define "extraheader"}} + {{end}} {{define "content"}}