diff --git a/comms/email/template/initial-contact.html b/comms/email/template/initial-contact.html index 48f9e7c0..5b53946d 100644 --- a/comms/email/template/initial-contact.html +++ b/comms/email/template/initial-contact.html @@ -1,99 +1,124 @@ - + - - - - Welcome - - - -
- {{if not .IsBrowser}} -
- Email not displaying correctly? View it in your browser -
- {{end}} - -
- - -
- -
-

Welcome

- -

We're sending you this email because it's the first time we've gotten this email address ({{.C.Destination}}).

- -

If you'd rather not receive emails from us you can reply with "Unsubscribe" in the subject or body of the email. You can also use the "Unsubscribe" feature of your mail client, if it supports list unsubscribes.

- -

If instead you'd like to confirm that you're willing to receive emails at this address, you can do so by clicking below:

+ + + + Welcome + + + +
+ {{ if not .IsBrowser }} +
+ Email not displaying correctly? + View it in your browser +
+ {{ end }} -
- I want emails from Report Mosquitoes Online + +
+ + +
+ +
+

Welcome

+ +

+ We're sending you this email because it's the first time we've gotten + this email address ({{ .C.Destination }}). +

+ +

+ If you'd rather not receive emails from us you can reply with + "Unsubscribe" in the subject or body of the email. You can also use + the "Unsubscribe" feature of your mail client, if it supports list + unsubscribes. +

+ +

+ If instead you'd like to confirm that you're willing to receive emails + at this address, you can do so by clicking below: +

+ + +
+ +
- - -
- + diff --git a/html/embed.go b/html/embed.go new file mode 100644 index 00000000..4faac68b --- /dev/null +++ b/html/embed.go @@ -0,0 +1,106 @@ +package html + +import ( + "bytes" + "embed" + //"errors" + "fmt" + "html/template" + //"io" + "io/fs" + //"math" + "net/http" + //"path" + //"strconv" + "strings" + //"time" + + //"github.com/Gleipnir-Technology/nidus-sync/config" + //"github.com/aarondl/opt/null" + //"github.com/google/uuid" + "github.com/rs/zerolog/log" +) + +//go:embed template/* +var embeddedFiles embed.FS + +type templateSystemEmbed struct { + allTemplates *template.Template + nameToTemplate map[string]*template.Template + sourceFS fs.FS +} + +func (ts templateSystemEmbed) loadAll() error { + ts.nameToTemplate = make(map[string]*template.Template, 0) + err := fs.WalkDir(ts.sourceFS, "template", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + return nil + } + short_path := removeLeadingDir(path) + + new_t, err := loadTemplateEmbedded(ts.sourceFS, short_path) + if err != nil { + return fmt.Errorf("Failed to add load template '%s': %w", short_path, err) + } + _, err = ts.allTemplates.AddParseTree(new_t.Name(), new_t.Tree) + if err != nil { + return fmt.Errorf("Failed to add parsed template '%s': %w", path, err) + } + ts.nameToTemplate[short_path] = new_t + log.Debug().Str("path", path).Str("short_path", short_path).Msg("Loaded template") + return nil + }) + if err != nil { + return fmt.Errorf("failed to load embeded templates: %w", err) + } + return nil +} +func (ts templateSystemEmbed) renderOrError(w http.ResponseWriter, template_name string, context interface{}) { + buf := &bytes.Buffer{} + t, err := loadTemplateEmbedded(ts.sourceFS, template_name) + if err != nil { + log.Error().Err(err).Str("template_name", template_name).Msg("Failed to load embedded template") + RespondError(w, "Failed to load template", err, http.StatusInternalServerError) + return + } + for name, templ := range ts.nameToTemplate { + _, err := t.AddParseTree(name, templ.Tree) + if err != nil { + log.Error().Err(err).Str("name", name).Msg("Failed to add parse tree") + RespondError(w, "Failed to add template", err, http.StatusInternalServerError) + return + } + } + err = t.ExecuteTemplate(buf, template_name, context) + if err != nil { + log.Error().Err(err).Str("template_name", template_name).Msg("Failed to render embedded template") + RespondError(w, "Failed to render template", err, http.StatusInternalServerError) + return + } + buf.WriteTo(w) +} +func loadTemplateEmbedded(sourceFS fs.FS, path string) (*template.Template, error) { + content, err := fs.ReadFile(sourceFS, "template/"+path) + if err != nil { + return nil, fmt.Errorf("error reading template template/%s: %w", path, err) + } + + new_t := template.New(path) + addFuncMap(new_t) + _, err = new_t.Parse(string(content)) + if err != nil { + return nil, fmt.Errorf("error parsing '%s': %w", path, err) + } + return new_t, nil +} +func removeLeadingDir(path string) string { + parts := strings.SplitN(path, "/", 2) + if len(parts) == 2 { + return parts[1] + } + return path +} diff --git a/html/filesystem.go b/html/filesystem.go new file mode 100644 index 00000000..0862c0df --- /dev/null +++ b/html/filesystem.go @@ -0,0 +1,244 @@ +package html + +import ( + "bytes" + //"embed" + //"errors" + "fmt" + "html/template" + //"io" + "io/fs" + //"math" + "net/http" + "os" + //"path" + //"strconv" + //"strings" + //"time" + + //"github.com/Gleipnir-Technology/nidus-sync/config" + //"github.com/aarondl/opt/null" + //"github.com/google/uuid" + "github.com/rs/zerolog/log" +) + +// The filesystem being used +var templates templateSystem + +type templateSystem interface { + loadAll() error + renderOrError(http.ResponseWriter, string, interface{}) +} + +type templateSystemDisk struct { + sourceFS fs.FS +} + +func LoadTemplates() error { + _, err := os.Stat("html/template") + if err == nil { + templates = templateSystemDisk{ + sourceFS: os.DirFS("./html/template"), + } + } else { + templates = templateSystemEmbed{ + allTemplates: template.New("all"), + sourceFS: embeddedFiles, + } + templates.loadAll() + } + return nil +} + +func (ts templateSystemDisk) loadAll() error { + return nil +} +func (ts templateSystemDisk) renderOrError(w http.ResponseWriter, template_name string, context interface{}) { + t, err := ts.parseTemplate(template_name) + if err != nil { + log.Error().Err(err).Str("template_name", template_name).Msg("Failed to parse template") + RespondError(w, "Failed to parse template", err, http.StatusInternalServerError) + return + } + err = ts.addSupportingTemplates(t) + if err != nil { + log.Error().Err(err).Str("template_name", template_name).Msg("Failed to add supporting templates") + RespondError(w, "Failed to add supporting templates", err, http.StatusInternalServerError) + return + } + err = ts.addSVGTemplates(t) + if err != nil { + log.Error().Err(err).Str("template_name", template_name).Msg("Failed to add supporting templates") + RespondError(w, "Failed to add supporting templates", err, http.StatusInternalServerError) + return + } + + buf := &bytes.Buffer{} + err = t.Execute(buf, context) + if err != nil { + log.Error().Err(err).Msg("Failed to render template") + RespondError(w, "Failed to render template", err, http.StatusInternalServerError) + return + } + buf.WriteTo(w) +} +func (ts templateSystemDisk) addSupportingTemplates(t *template.Template) error { + err := fs.WalkDir(ts.sourceFS, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + return nil + } + + content, err := fs.ReadFile(ts.sourceFS, path) + if err != nil { + return fmt.Errorf("error reading template %s: %w", path, err) + } + + new_t := template.New(path) + addFuncMap(new_t) + _, err = new_t.Parse(string(content)) + if err != nil { + return fmt.Errorf("error parsing '%s': %w", path, err) + } + _, err = t.AddParseTree(new_t.Name(), new_t.Tree) + if err != nil { + return fmt.Errorf("error adding parse tree '%s': %w", path, err) + } + log.Debug().Str("path", path).Msg("Read template") + return nil + }) + if err != nil { + return fmt.Errorf("error walking template directory: %w", err) + } + return nil +} +func (ts templateSystemDisk) addSVGTemplates(t *template.Template) error { + svg_fs, err := fs.Sub(ts.sourceFS, "svg") + if err != nil { + return fmt.Errorf("Failed to get svg subdir: %w", err) + } + svgs, err := fs.ReadDir(svg_fs, ".") + if err != nil { + log.Warn().Msg("Failed to read svg directory") + return nil + } + for _, svg := range svgs { + content, err := fs.ReadFile(svg_fs, svg.Name()) + if err != nil { + return fmt.Errorf("Failed to read svg '%s' from embedded filesystem: %w", svg, err) + } + svg_name := svg.Name() + svg_template := fmt.Sprintf("{{define \"%s\"}}%s{{end}}", svg_name, string(content)) + svg_t, err := template.New(svg_name).Parse(svg_template) + if err != nil { + return fmt.Errorf("Failed to parse svg '%s' from embedded filesystem: %v", svg, err) + } + _, err = t.AddParseTree(svg_t.Name(), svg_t.Tree) + if err != nil { + return fmt.Errorf("Failed to add svg '%s' to embedded template: %v", svg, err) + } + log.Debug().Str("name", svg_name).Msg("add svg template") + } + return nil +} +func (ts templateSystemDisk) parseTemplate(filename string) (*template.Template, error) { + t := template.New(filename) + log.Debug().Str("filename", filename).Msg("parsing template") + addFuncMap(t) + content, err := fs.ReadFile(ts.sourceFS, filename) + if err != nil { + return nil, fmt.Errorf("error reading template %s: %w", filename, err) + } + _, err = t.Parse(string(content)) + if err != nil { + return nil, fmt.Errorf("error parsing '%s': %w", filename, err) + } + return t, nil +} +func addSVGTemplates(fsys fs.FS, templ *template.Template) error { + svgs, err := fs.ReadDir(fsys, ".") + if err != nil { + log.Warn().Msg("Failed to read svg directory") + return nil + } + for _, svg := range svgs { + content, err := fs.ReadFile(fsys, svg.Name()) + if err != nil { + return fmt.Errorf("Failed to read svg '%s' from embedded filesystem: %w", svg, err) + } + svg_name := svg.Name() + svg_template := fmt.Sprintf("{{define \"%s\"}}%s{{end}}", svg_name, string(content)) + svg_t, err := template.New(svg_name).Parse(svg_template) + if err != nil { + return fmt.Errorf("Failed to parse svg '%s' from embedded filesystem: %v", svg, err) + } + _, err = templ.AddParseTree(svg_t.Name(), svg_t.Tree) + if err != nil { + return fmt.Errorf("Failed to add svg '%s' to embedded template: %v", svg, err) + } + //log.Debug().Str("name", svg_name).Msg("add svg template") + } + return nil +} + +/* +func executeTemplate(w io.Writer, data any) error { + if bt.template == nil { + name := path.Base(bt.files[0]) + templ, err := parseFromDisk(bt.subdir, bt.files) + if err != nil { + return fmt.Errorf("Failed to parse template file: %w", err) + } + if templ == nil { + w.Write([]byte("Failed to read from disk: ")) + return errors.New("Template parsing failed") + } + //log.Debug().Str("name", templ.Name()).Msg("Parsed template") + return templ.ExecuteTemplate(w, name, data) + } else { + name := path.Base(bt.files[0]) + return bt.template.ExecuteTemplate(w, name, data) + } +} +func parseEmbedded(embeddedFiles embed.FS, subdir string, files []string) *template.Template { + funcMap := makeFuncMap() + // Remap the file names to embedded paths + embeddedFilePaths := make([]string, 0) + for _, f := range files { + embeddedFilePaths = append(embeddedFilePaths, strings.TrimPrefix(f, subdir)) + } + name := path.Base(embeddedFilePaths[0]) + log.Debug().Str("name", name).Strs("paths", embeddedFilePaths).Msg("Parsing embedded template") + t, err := template.New(name).Funcs(funcMap).ParseFS(embeddedFiles, embeddedFilePaths...) + if err != nil { + panic(fmt.Sprintf("Failed to parse embedded template %s: %v", name, err)) + } + svg_fs, err := fs.Sub(embeddedFiles, "template/svg") + if err != nil { + panic(fmt.Sprintf("Failed to read static/svg: %v", err)) + } + err = addSVGTemplates(svg_fs, t) + if err != nil { + panic(fmt.Sprintf("Failed to add SVG templates: %v", err)) + } + return t +} +func parseFromDisk(subdir string, files []string) (*template.Template, error) { + funcMap := makeFuncMap() + name := path.Base(files[0]) + //log.Debug().Str("name", name).Strs("files", files).Msg("parsing from disk") + templ, err := template.New(name).Funcs(funcMap).ParseFiles(files...) + if err != nil { + return nil, fmt.Errorf("Failed to parse %s: %w", files, err) + } + fsys := os.DirFS(subdir + "/template/svg") + err = addSVGTemplates(fsys, templ) + if err != nil { + return nil, fmt.Errorf("Failed to add SVGs from disk: %w", err) + } + return templ, nil +} +*/ diff --git a/html/func.go b/html/func.go new file mode 100644 index 00000000..c857c96a --- /dev/null +++ b/html/func.go @@ -0,0 +1,211 @@ +package html + +import ( + //"bytes" + "fmt" + "html/template" + //"io/fs" + "math" + //"net/http" + //"os" + "strconv" + "strings" + "time" + + "github.com/aarondl/opt/null" + "github.com/google/uuid" + "github.com/rs/zerolog/log" +) + +func addFuncMap(t *template.Template) { + funcMap := template.FuncMap{ + "bigNumber": bigNumber, + "html": unescapeHTML, + "json": unescapeJS, + "GISStatement": gisStatement, + "latLngDisplay": latLngDisplay, + "publicReportID": publicReportID, + "timeAsRelativeDate": timeAsRelativeDate, + "timeDelta": timeDelta, + "timeElapsed": timeElapsed, + "timeInterval": timeInterval, + "timeSince": timeSince, + "timeSincePtr": timeSincePtr, + "uuidShort": uuidShort, + } + t.Funcs(funcMap) +} + +func bigNumber(n int) string { + // Convert the number to a string + numStr := strconv.FormatInt(int64(n), 10) + + // Add commas every three digits from the right + var result strings.Builder + for i, char := range numStr { + if i > 0 && (len(numStr)-i)%3 == 0 { + result.WriteByte(',') + } + result.WriteRune(char) + } + + return result.String() +} + +func publicReportID(s string) string { + if len(s) != 12 { + return s + } + return s[0:4] + "-" + s[4:8] + "-" + s[8:12] +} +func timeAsRelativeDate(d time.Time) string { + return d.Format("01-02") +} + +// FormatTimeDuration returns a human-readable string representing a time.Duration +// as "X units early" or "X units late" +func timeDelta(d time.Duration) string { + suffix := "late" + if d < 0 { + suffix = "early" + d = -d // Make duration positive for calculations + } + + const ( + day = 24 * time.Hour + week = 7 * day + ) + + log.Info().Int64("delta", int64(d)).Str("suffix", suffix).Msg("Time delta") + switch { + case d >= week: + weeks := d / week + if weeks == 1 { + return "1 week " + suffix + } + return fmt.Sprintf("%d weeks %s", weeks, suffix) + + case d >= day: + days := d / day + if days == 1 { + return "1 day " + suffix + } + return fmt.Sprintf("%d days %s", days, suffix) + + case d >= time.Hour: + hours := d / time.Hour + if hours == 1 { + return "1 hour " + suffix + } + return fmt.Sprintf("%d hours %s", hours, suffix) + + case d >= time.Minute: + minutes := d / time.Minute + if minutes == 1 { + return "1 minute " + suffix + } + return fmt.Sprintf("%d minutes %s", minutes, suffix) + + default: + seconds := d / time.Second + if seconds == 1 { + return "1 second " + suffix + } + return fmt.Sprintf("%d seconds %s", seconds, suffix) + } +} + +func timeElapsed(seconds null.Val[float32]) string { + if !seconds.IsValue() { + return "none" + } + s := int(seconds.MustGet()) + hours := s / 3600 + remainder := s - (hours * 3600) + minutes := remainder / 60 + remainder = remainder - (minutes * 60) + if hours > 0 { + return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, remainder) + } else if minutes > 0 { + return fmt.Sprintf("%02d:%02d", minutes, remainder) + } else { + return fmt.Sprintf("%d seconds", remainder) + } +} + +func timeInterval(d time.Duration) string { + seconds := d.Seconds() + + // Less than 120 seconds -> show in seconds + if seconds < 120 { + return fmt.Sprintf("every %d seconds", int(math.Round(seconds))) + } + + minutes := d.Minutes() + // Less than 120 minutes -> show in minutes + if minutes < 120 { + return fmt.Sprintf("every %d minutes", int(math.Round(minutes))) + } + + hours := d.Hours() + // Less than 48 hours -> show in hours + if hours < 48 { + return fmt.Sprintf("every %d hours", int(math.Round(hours))) + } + + days := hours / 24 + // Less than 14 days -> show in days + if days < 14 { + return fmt.Sprintf("every %d days", int(math.Round(days))) + } + + weeks := days / 7 + // Less than 8 weeks -> show in weeks + if weeks < 8 { + return fmt.Sprintf("every %d weeks", int(math.Round(weeks))) + } + + months := days / 30 + // Less than 24 months -> show in months + if months < 24 { + return fmt.Sprintf("every %d months", int(math.Round(months))) + } + + years := days / 365 + return fmt.Sprintf("every %d years", int(math.Round(years))) +} +func timeSincePtr(t *time.Time) string { + if t == nil { + return "never" + } + return timeSince(*t) +} +func timeSince(t time.Time) string { + now := time.Now() + diff := now.Sub(t) + + hours := diff.Hours() + if hours < 1 { + minutes := diff.Minutes() + return fmt.Sprintf("%d minutes ago", int(minutes)) + } else if hours < 24 { + return fmt.Sprintf("%d hours ago", int(hours)) + } else { + days := hours / 24 + return fmt.Sprintf("%d days ago", int(days)) + } +} +func unescapeHTML(s string) template.HTML { + return template.HTML(s) +} +func unescapeJS(s string) template.JS { + return template.JS(s) +} +func uuidShort(uuid uuid.UUID) string { + s := uuid.String() + if len(s) < 7 { + return s // Return as is if too short + } + + return s[:3] + "..." + s[len(s)-4:] +} diff --git a/html/html.go b/html/html.go index 884aadb3..209fc7ae 100644 --- a/html/html.go +++ b/html/html.go @@ -1,351 +1,9 @@ package html import ( - "bytes" - "embed" - "errors" - "fmt" - "html/template" - "io" - "io/fs" - "math" "net/http" - "os" - "path" - "strconv" - "strings" - "time" - - "github.com/Gleipnir-Technology/nidus-sync/config" - "github.com/aarondl/opt/null" - "github.com/google/uuid" - "github.com/rs/zerolog/log" ) -var TemplatesByFilename = make(map[string]BuiltTemplate, 0) - -type BuiltTemplate struct { - files []string - subdir string - // Nil if we are going to read templates off disk every time we render - // because we are in development mode. - template *template.Template -} - -func (bt *BuiltTemplate) executeTemplate(w io.Writer, data any) error { - if bt.template == nil { - name := path.Base(bt.files[0]) - templ, err := parseFromDisk(bt.subdir, bt.files) - if err != nil { - return fmt.Errorf("Failed to parse template file: %w", err) - } - if templ == nil { - w.Write([]byte("Failed to read from disk: ")) - return errors.New("Template parsing failed") - } - //log.Debug().Str("name", templ.Name()).Msg("Parsed template") - return templ.ExecuteTemplate(w, name, data) - } else { - name := path.Base(bt.files[0]) - return bt.template.ExecuteTemplate(w, name, data) - } -} - -func NewBuiltTemplate(embeddedFiles embed.FS, subdir string, files ...string) *BuiltTemplate { - files_on_disk := true - for _, f := range files { - _, err := os.Stat(f) - if err != nil { - files_on_disk = false - if !config.IsProductionEnvironment() { - log.Warn().Str("file", f).Msg("template file is not on disk") - } - break - } - } - var result BuiltTemplate - if files_on_disk { - result = BuiltTemplate{ - files: files, - subdir: subdir, - template: nil, - } - } else { - result = BuiltTemplate{ - files: files, - subdir: subdir, - template: parseEmbedded(embeddedFiles, subdir, files), - } - } - TemplatesByFilename[path.Base(files[0])] = result - return &result -} - -func RenderOrError(w http.ResponseWriter, template *BuiltTemplate, context interface{}) { - buf := &bytes.Buffer{} - err := template.executeTemplate(buf, context) - if err != nil { - log.Error().Err(err).Strs("files", template.files).Msg("Failed to render template") - RespondError(w, "Failed to render template", err, http.StatusInternalServerError) - return - } - buf.WriteTo(w) -} - -func bigNumber(n int) string { - // Convert the number to a string - numStr := strconv.FormatInt(int64(n), 10) - - // Add commas every three digits from the right - var result strings.Builder - for i, char := range numStr { - if i > 0 && (len(numStr)-i)%3 == 0 { - result.WriteByte(',') - } - result.WriteRune(char) - } - - return result.String() -} - -func makeFuncMap() template.FuncMap { - funcMap := template.FuncMap{ - "bigNumber": bigNumber, - "html": unescapeHTML, - "json": unescapeJS, - "GISStatement": gisStatement, - "latLngDisplay": latLngDisplay, - "publicReportID": publicReportID, - "timeAsRelativeDate": timeAsRelativeDate, - "timeDelta": timeDelta, - "timeElapsed": timeElapsed, - "timeInterval": timeInterval, - "timeSince": timeSince, - "timeSincePtr": timeSincePtr, - "uuidShort": uuidShort, - } - return funcMap -} -func addSVGTemplates(fsys fs.FS, templ *template.Template) error { - svgs, err := fs.ReadDir(fsys, ".") - if err != nil { - log.Warn().Msg("Failed to read svg directory") - return nil - } - for _, svg := range svgs { - content, err := fs.ReadFile(fsys, svg.Name()) - if err != nil { - return fmt.Errorf("Failed to read svg '%s' from embedded filesystem: %w", svg, err) - } - svg_name := svg.Name() - svg_template := fmt.Sprintf("{{define \"%s\"}}%s{{end}}", svg_name, string(content)) - svg_t, err := template.New(svg_name).Parse(svg_template) - if err != nil { - return fmt.Errorf("Failed to parse svg '%s' from embedded filesystem: %v", svg, err) - } - _, err = templ.AddParseTree(svg_t.Name(), svg_t.Tree) - if err != nil { - return fmt.Errorf("Failed to add svg '%s' to embedded template: %v", svg, err) - } - //log.Debug().Str("name", svg_name).Msg("add svg template") - } - return nil -} -func parseEmbedded(embeddedFiles embed.FS, subdir string, files []string) *template.Template { - funcMap := makeFuncMap() - // Remap the file names to embedded paths - embeddedFilePaths := make([]string, 0) - for _, f := range files { - embeddedFilePaths = append(embeddedFilePaths, strings.TrimPrefix(f, subdir)) - } - name := path.Base(embeddedFilePaths[0]) - log.Debug().Str("name", name).Strs("paths", embeddedFilePaths).Msg("Parsing embedded template") - t, err := template.New(name).Funcs(funcMap).ParseFS(embeddedFiles, embeddedFilePaths...) - if err != nil { - panic(fmt.Sprintf("Failed to parse embedded template %s: %v", name, err)) - } - svg_fs, err := fs.Sub(embeddedFiles, "template/svg") - if err != nil { - panic(fmt.Sprintf("Failed to read static/svg: %v", err)) - } - err = addSVGTemplates(svg_fs, t) - if err != nil { - panic(fmt.Sprintf("Failed to add SVG templates: %v", err)) - } - return t -} - -func parseFromDisk(subdir string, files []string) (*template.Template, error) { - funcMap := makeFuncMap() - name := path.Base(files[0]) - //log.Debug().Str("name", name).Strs("files", files).Msg("parsing from disk") - templ, err := template.New(name).Funcs(funcMap).ParseFiles(files...) - if err != nil { - return nil, fmt.Errorf("Failed to parse %s: %w", files, err) - } - fsys := os.DirFS(subdir + "/template/svg") - err = addSVGTemplates(fsys, templ) - if err != nil { - return nil, fmt.Errorf("Failed to add SVGs from disk: %w", err) - } - return templ, nil -} - -func publicReportID(s string) string { - if len(s) != 12 { - return s - } - return s[0:4] + "-" + s[4:8] + "-" + s[8:12] -} - -func timeAsRelativeDate(d time.Time) string { - return d.Format("01-02") -} - -// FormatTimeDuration returns a human-readable string representing a time.Duration -// as "X units early" or "X units late" -func timeDelta(d time.Duration) string { - suffix := "late" - if d < 0 { - suffix = "early" - d = -d // Make duration positive for calculations - } - - const ( - day = 24 * time.Hour - week = 7 * day - ) - - log.Info().Int64("delta", int64(d)).Str("suffix", suffix).Msg("Time delta") - switch { - case d >= week: - weeks := d / week - if weeks == 1 { - return "1 week " + suffix - } - return fmt.Sprintf("%d weeks %s", weeks, suffix) - - case d >= day: - days := d / day - if days == 1 { - return "1 day " + suffix - } - return fmt.Sprintf("%d days %s", days, suffix) - - case d >= time.Hour: - hours := d / time.Hour - if hours == 1 { - return "1 hour " + suffix - } - return fmt.Sprintf("%d hours %s", hours, suffix) - - case d >= time.Minute: - minutes := d / time.Minute - if minutes == 1 { - return "1 minute " + suffix - } - return fmt.Sprintf("%d minutes %s", minutes, suffix) - - default: - seconds := d / time.Second - if seconds == 1 { - return "1 second " + suffix - } - return fmt.Sprintf("%d seconds %s", seconds, suffix) - } -} - -func timeElapsed(seconds null.Val[float32]) string { - if !seconds.IsValue() { - return "none" - } - s := int(seconds.MustGet()) - hours := s / 3600 - remainder := s - (hours * 3600) - minutes := remainder / 60 - remainder = remainder - (minutes * 60) - if hours > 0 { - return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, remainder) - } else if minutes > 0 { - return fmt.Sprintf("%02d:%02d", minutes, remainder) - } else { - return fmt.Sprintf("%d seconds", remainder) - } -} - -func timeInterval(d time.Duration) string { - seconds := d.Seconds() - - // Less than 120 seconds -> show in seconds - if seconds < 120 { - return fmt.Sprintf("every %d seconds", int(math.Round(seconds))) - } - - minutes := d.Minutes() - // Less than 120 minutes -> show in minutes - if minutes < 120 { - return fmt.Sprintf("every %d minutes", int(math.Round(minutes))) - } - - hours := d.Hours() - // Less than 48 hours -> show in hours - if hours < 48 { - return fmt.Sprintf("every %d hours", int(math.Round(hours))) - } - - days := hours / 24 - // Less than 14 days -> show in days - if days < 14 { - return fmt.Sprintf("every %d days", int(math.Round(days))) - } - - weeks := days / 7 - // Less than 8 weeks -> show in weeks - if weeks < 8 { - return fmt.Sprintf("every %d weeks", int(math.Round(weeks))) - } - - months := days / 30 - // Less than 24 months -> show in months - if months < 24 { - return fmt.Sprintf("every %d months", int(math.Round(months))) - } - - years := days / 365 - return fmt.Sprintf("every %d years", int(math.Round(years))) -} -func timeSincePtr(t *time.Time) string { - if t == nil { - return "never" - } - return timeSince(*t) -} -func timeSince(t time.Time) string { - now := time.Now() - diff := now.Sub(t) - - hours := diff.Hours() - if hours < 1 { - minutes := diff.Minutes() - return fmt.Sprintf("%d minutes ago", int(minutes)) - } else if hours < 24 { - return fmt.Sprintf("%d hours ago", int(hours)) - } else { - days := hours / 24 - return fmt.Sprintf("%d days ago", int(days)) - } -} -func unescapeHTML(s string) template.HTML { - return template.HTML(s) -} -func unescapeJS(s string) template.JS { - return template.JS(s) -} -func uuidShort(uuid uuid.UUID) string { - s := uuid.String() - if len(s) < 7 { - return s // Return as is if too short - } - - return s[:3] + "..." + s[len(s)-4:] +func RenderOrError(w http.ResponseWriter, template_name string, content interface{}) { + templates.renderOrError(w, template_name, content) } diff --git a/rmo/template/base.html b/html/template/rmo/base.html similarity index 97% rename from rmo/template/base.html rename to html/template/rmo/base.html index d96bdd81..e6420289 100644 --- a/rmo/template/base.html +++ b/html/template/rmo/base.html @@ -42,7 +42,7 @@ {{ template "content" . }} - {{ template "footer" . }} + {{ template "rmo/component/footer.html" . }} diff --git a/rmo/template/component/footer.html b/html/template/rmo/component/footer.html similarity index 90% rename from rmo/template/component/footer.html rename to html/template/rmo/component/footer.html index 060bb73e..c829cc52 100644 --- a/rmo/template/component/footer.html +++ b/html/template/rmo/component/footer.html @@ -1,4 +1,4 @@ -{{ define "footer" }} +{{ define "rmo/component/footer.html" }}
diff --git a/html/template/rmo/component/header-district.html b/html/template/rmo/component/header-district.html new file mode 100644 index 00000000..8207664d --- /dev/null +++ b/html/template/rmo/component/header-district.html @@ -0,0 +1,14 @@ +{{ define "rmo/component/header-district.html" }} + +{{ end }} diff --git a/html/template/rmo/component/header-rmo.html b/html/template/rmo/component/header-rmo.html new file mode 100644 index 00000000..6534904e --- /dev/null +++ b/html/template/rmo/component/header-rmo.html @@ -0,0 +1,17 @@ +{{ define "rmo/component/header-rmo.html" }} + +{{ end }} diff --git a/html/template/rmo/component/map-header.html b/html/template/rmo/component/map-header.html new file mode 100644 index 00000000..fae7974a --- /dev/null +++ b/html/template/rmo/component/map-header.html @@ -0,0 +1,2 @@ +{{ define "rmo/component/map-header.html" }} +{{ end }} diff --git a/rmo/template/component/map.html b/html/template/rmo/component/map.html similarity index 98% rename from rmo/template/component/map.html rename to html/template/rmo/component/map.html index 080c4785..c4a5840d 100644 --- a/rmo/template/component/map.html +++ b/html/template/rmo/component/map.html @@ -1,4 +1,4 @@ -{{ define "map" }} +{{ define "rmo/component/map.html" }} .photo-upload-area { border: 2px dashed #ccc; diff --git a/rmo/template/component/photo-upload.html b/html/template/rmo/component/photo-upload.html similarity index 96% rename from rmo/template/component/photo-upload.html rename to html/template/rmo/component/photo-upload.html index 14553545..864b98aa 100644 --- a/rmo/template/component/photo-upload.html +++ b/html/template/rmo/component/photo-upload.html @@ -1,4 +1,4 @@ -{{ define "photo-upload" }} +{{ define "rmo/component/photo-upload.html" }}
+
+

District List

+ + + + + + + + {{ range .Districts }} + + + + + + {{ end }} + +
LogoNameURL
+ + {{ .Name }}{{ .URLRMO }}
+ +{{ end }} diff --git a/rmo/template/email-confirm-complete.html b/html/template/rmo/email-confirm-complete.html similarity index 95% rename from rmo/template/email-confirm-complete.html rename to html/template/rmo/email-confirm-complete.html index d5559d6f..d45d6fea 100644 --- a/rmo/template/email-confirm-complete.html +++ b/html/template/rmo/email-confirm-complete.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Main{{ end }} {{ define "extraheader" }} diff --git a/rmo/template/email-confirm.html b/html/template/rmo/email-confirm.html similarity index 96% rename from rmo/template/email-confirm.html rename to html/template/rmo/email-confirm.html index ededde76..d72a6f24 100644 --- a/rmo/template/email-confirm.html +++ b/html/template/rmo/email-confirm.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Main{{ end }} {{ define "extraheader" }} diff --git a/rmo/template/email-subscribe-confirm.html b/html/template/rmo/email-subscribe-confirm.html similarity index 95% rename from rmo/template/email-subscribe-confirm.html rename to html/template/rmo/email-subscribe-confirm.html index a48bfced..0b861d9e 100644 --- a/rmo/template/email-subscribe-confirm.html +++ b/html/template/rmo/email-subscribe-confirm.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Main{{ end }} {{ define "extraheader" }} diff --git a/rmo/template/email-subscribe.html b/html/template/rmo/email-subscribe.html similarity index 95% rename from rmo/template/email-subscribe.html rename to html/template/rmo/email-subscribe.html index ee8dfda9..d71c3da7 100644 --- a/rmo/template/email-subscribe.html +++ b/html/template/rmo/email-subscribe.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Main{{ end }} {{ define "extraheader" }} diff --git a/rmo/template/email-unsubscribe-complete.html b/html/template/rmo/email-unsubscribe-complete.html similarity index 95% rename from rmo/template/email-unsubscribe-complete.html rename to html/template/rmo/email-unsubscribe-complete.html index e79b54f4..78068b3a 100644 --- a/rmo/template/email-unsubscribe-complete.html +++ b/html/template/rmo/email-unsubscribe-complete.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Main{{ end }} {{ define "extraheader" }} diff --git a/rmo/template/email-unsubscribe.html b/html/template/rmo/email-unsubscribe.html similarity index 96% rename from rmo/template/email-unsubscribe.html rename to html/template/rmo/email-unsubscribe.html index bfa3f5d8..2cbb50de 100644 --- a/rmo/template/email-unsubscribe.html +++ b/html/template/rmo/email-unsubscribe.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Main{{ end }} {{ define "extraheader" }} diff --git a/rmo/template/mock/district-root.html b/html/template/rmo/mock/district-root.html similarity index 100% rename from rmo/template/mock/district-root.html rename to html/template/rmo/mock/district-root.html diff --git a/rmo/template/mock/nuisance-submit-complete.html b/html/template/rmo/mock/nuisance-submit-complete.html similarity index 100% rename from rmo/template/mock/nuisance-submit-complete.html rename to html/template/rmo/mock/nuisance-submit-complete.html diff --git a/rmo/template/mock/nuisance.html b/html/template/rmo/mock/nuisance.html similarity index 100% rename from rmo/template/mock/nuisance.html rename to html/template/rmo/mock/nuisance.html diff --git a/rmo/template/mock/root.html b/html/template/rmo/mock/root.html similarity index 100% rename from rmo/template/mock/root.html rename to html/template/rmo/mock/root.html diff --git a/rmo/template/mock/water.html b/html/template/rmo/mock/water.html similarity index 100% rename from rmo/template/mock/water.html rename to html/template/rmo/mock/water.html diff --git a/rmo/template/nuisance.html b/html/template/rmo/nuisance.html similarity index 99% rename from rmo/template/nuisance.html rename to html/template/rmo/nuisance.html index 84e61d6d..a7951992 100644 --- a/rmo/template/nuisance.html +++ b/html/template/rmo/nuisance.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Report Nuisance{{ end }} {{ define "extraheader" }} @@ -165,9 +165,9 @@ document.addEventListener('DOMContentLoaded', function() { {{ end }} {{ define "content" }} {{ if (eq .District nil) }} - {{ template "header-rmo" . }} + {{ template "rmo/component/header-rmo.html" . }} {{ else }} - {{ template "header-district" .District }} + {{ template "rmo/component/header-district.html" .District }} {{ end }}
diff --git a/rmo/template/privacy.html b/html/template/rmo/privacy.html similarity index 99% rename from rmo/template/privacy.html rename to html/template/rmo/privacy.html index 4ee48999..5314f780 100644 --- a/rmo/template/privacy.html +++ b/html/template/rmo/privacy.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Privacy Policy{{ end }} {{ define "extraheader" }} {{ end }} diff --git a/rmo/template/quick-submit-complete.html b/html/template/rmo/quick-submit-complete.html similarity index 99% rename from rmo/template/quick-submit-complete.html rename to html/template/rmo/quick-submit-complete.html index 62892e98..693c3828 100644 --- a/rmo/template/quick-submit-complete.html +++ b/html/template/rmo/quick-submit-complete.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Quick Report Complete{{ end }} {{ define "extraheader" }} diff --git a/rmo/template/quick.html b/html/template/rmo/quick.html similarity index 99% rename from rmo/template/quick.html rename to html/template/rmo/quick.html index 2c15ccb9..d95bc9cc 100644 --- a/rmo/template/quick.html +++ b/html/template/rmo/quick.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Quick Report{{ end }} {{ define "extraheader" }} diff --git a/rmo/template/register-notifications-complete.html b/html/template/rmo/register-notifications-complete.html similarity index 99% rename from rmo/template/register-notifications-complete.html rename to html/template/rmo/register-notifications-complete.html index 4a3f4d15..aa35332e 100644 --- a/rmo/template/register-notifications-complete.html +++ b/html/template/rmo/register-notifications-complete.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Notification Request Complete{{ end }} {{ define "extraheader" }} diff --git a/rmo/template/root.html b/html/template/rmo/root.html similarity index 98% rename from rmo/template/root.html rename to html/template/rmo/root.html index 98abd7b6..67c010b8 100644 --- a/rmo/template/root.html +++ b/html/template/rmo/root.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Main{{ end }} {{ define "extraheader" }} diff --git a/html/template/rmo/search.html b/html/template/rmo/search.html new file mode 100644 index 00000000..62f7332c --- /dev/null +++ b/html/template/rmo/search.html @@ -0,0 +1,265 @@ +{{ template "rmo/base.html" . }} + +{{ define "title" }}Status{{ end }} +{{ define "extraheader" }} + + + + + + + + +{{ end }} +{{ define "content" }} +
+ + + + +
+
+
Reports Map
+
+
+ +
+
+ + +
+
+
Reports Near You
+ 15 Reports Found +
+
+
+ +
+
+
+ + +
+ +
+ + +
+ +
+
+{{ end }} diff --git a/rmo/template/status-by-id.html b/html/template/rmo/status-by-id.html similarity index 96% rename from rmo/template/status-by-id.html rename to html/template/rmo/status-by-id.html index b2f449c1..7fad3afa 100644 --- a/rmo/template/status-by-id.html +++ b/html/template/rmo/status-by-id.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Status of report {{ .Report.ID|publicReportID }}{{ end }} {{ define "extraheader" }} @@ -57,9 +57,9 @@ document.addEventListener("DOMContentLoaded", onLoad); {{ end }} {{ define "content" }} {{ if (eq .District nil) }} - {{ template "header-rmo" . }} + {{ template "rmo/component/header-rmo.html" . }} {{ else }} - {{ template "header-district" .District }} + {{ template "rmo/component/header-district.html" .District }} {{ end }}
diff --git a/rmo/template/status.html b/html/template/rmo/status.html similarity index 98% rename from rmo/template/status.html rename to html/template/rmo/status.html index 099dcf8d..5d95b6da 100644 --- a/rmo/template/status.html +++ b/html/template/rmo/status.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Status{{ end }} {{ define "extraheader" }} @@ -168,9 +168,9 @@ document.addEventListener('DOMContentLoaded', onLoad); {{ end }} {{ define "content" }} {{ if (eq .District nil) }} - {{ template "header-rmo" . }} + {{ template "rmo/component/header-rmo.html" . }} {{ else }} - {{ template "header-district" .District }} + {{ template "rmo/component/header-district.html" .District }} {{ end }}
diff --git a/rmo/template/submit-complete.html b/html/template/rmo/submit-complete.html similarity index 99% rename from rmo/template/submit-complete.html rename to html/template/rmo/submit-complete.html index e01d296d..09de1990 100644 --- a/rmo/template/submit-complete.html +++ b/html/template/rmo/submit-complete.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Report Submission Complete{{ end }} {{ define "extraheader" }} diff --git a/rmo/template/terms.html b/html/template/rmo/terms.html similarity index 98% rename from rmo/template/terms.html rename to html/template/rmo/terms.html index b5ad63b2..2f069b7a 100644 --- a/rmo/template/terms.html +++ b/html/template/rmo/terms.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Privacy Policy{{ end }} {{ define "extraheader" }} {{ end }} diff --git a/rmo/template/water.html b/html/template/rmo/water.html similarity index 99% rename from rmo/template/water.html rename to html/template/rmo/water.html index 337a9af9..1f2ce8b0 100644 --- a/rmo/template/water.html +++ b/html/template/rmo/water.html @@ -1,4 +1,4 @@ -{{ template "base.html" . }} +{{ template "rmo/base.html" . }} {{ define "title" }}Report Standing Water{{ end }} {{ define "extraheader" }} @@ -146,9 +146,9 @@ document.addEventListener('DOMContentLoaded', function() { {{ end }} {{ define "content" }} {{ if (eq .District nil) }} - {{ template "header-rmo" . }} + {{ template "rmo/component/header-rmo.html" . }} {{ else }} - {{ template "header-district" .District }} + {{ template "rmo/component/header-district.html" .District }} {{ end }}
diff --git a/rmo/template/svg/check-report-color.svg b/html/template/svg/check-report-color.svg similarity index 100% rename from rmo/template/svg/check-report-color.svg rename to html/template/svg/check-report-color.svg diff --git a/rmo/template/svg/check-report.svg b/html/template/svg/check-report.svg similarity index 100% rename from rmo/template/svg/check-report.svg rename to html/template/svg/check-report.svg diff --git a/rmo/template/svg/mosquito-color.svg b/html/template/svg/mosquito-color.svg similarity index 100% rename from rmo/template/svg/mosquito-color.svg rename to html/template/svg/mosquito-color.svg diff --git a/rmo/template/svg/mosquito.svg b/html/template/svg/mosquito.svg similarity index 100% rename from rmo/template/svg/mosquito.svg rename to html/template/svg/mosquito.svg diff --git a/rmo/template/svg/pond-color.svg b/html/template/svg/pond-color.svg similarity index 100% rename from rmo/template/svg/pond-color.svg rename to html/template/svg/pond-color.svg diff --git a/rmo/template/svg/pond.svg b/html/template/svg/pond.svg similarity index 100% rename from rmo/template/svg/pond.svg rename to html/template/svg/pond.svg diff --git a/html/template/sync/admin.html b/html/template/sync/admin.html new file mode 100644 index 00000000..a12b8884 --- /dev/null +++ b/html/template/sync/admin.html @@ -0,0 +1,503 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} + + + +
+
+
+
+
+
+
+ + +
+ Quick search for request ID, address, coordinates, contact + name, or phone number +
+
+
+
+
+ +
+ +
+
+
+
Mosquito Activity & Relief
+ New Service Request +
+
+
+
+ +
+
+ +

Interactive map will be loaded here

+
+
+
+
+ +
+
+
Recent Trap Counts
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Trap LocationCurrentWeek ΔMonth ΔYoY
Elmwood Park47-12%-23%+5%
Riverside Dr32+8%-5%-10%
Oakdale Creek53+15%+22%+17%
+
+
+ +
+
Nearby Service Requests
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateStatusTypeDistance
10/15/23CompletedGreen Pool0.2 mi
10/18/23 + Scheduled + Mosquito Nuisance0.3 mi
10/19/23 + Accepted + Previous Source0.5 mi
+
+
+
+
+
+
+ + +
+
+
+
Calendar
+
+
+
+
+ +
October 2023
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SuMoTuWeThFrSa
1234567
891011 + 12 + 1314
15 + 16 + 17 + 18 + 19 + 20 + 21
22 + 23 + 2425262728
2930311234
+
+ + Light + + Medium + + Heavy +
+
+ +
Today's Schedule - October 23, 2023
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TimeAddressTypeTechnician
8:00 AM123 Maple StNuisanceS. Johnson
9:30 AM456 Oak AveGreen PoolM. Williams
11:00 AM789 Pine LnPrev SourceL. Rodriguez
+
+
+
+
+
+ + +
+
+
+
+
Today's Technician Roster
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TechnicianScheduledCompletedPhoneStatusLocation
+
+ Photo + Sarah Johnson +
+
85(555) 234-5678Servicing123 Maple St, Zone 3
+
+ Photo + Mark Williams +
+
73(555) 345-6789 + On Break + Office - Lunchroom
+
+ Photo + Lisa Rodriguez +
+
96(555) 456-7890In TransitEn route to 789 Pine Ln
+
+ Photo + Carlos Martinez +
+
64(555) 567-8901Servicing202 Birch Dr, Zone 2
+
+
+
+
+
+
+{{ end }} diff --git a/html/template/sync/authenticated.html b/html/template/sync/authenticated.html new file mode 100644 index 00000000..41554875 --- /dev/null +++ b/html/template/sync/authenticated.html @@ -0,0 +1,42 @@ + + + + + + {{ template "title" . }} - Nidus Sync + + + + + + + + + {{ block "extraheader" . }}{{ end }} + + + {{ template "sync/component/icons.html" }} +
+ {{ if .User }} + {{ template "sync/component/sidebar.html" .User }} + {{ end }} +
+ + {{ template "content" . }} + + + + diff --git a/html/template/sync/base.html b/html/template/sync/base.html new file mode 100644 index 00000000..967ef497 --- /dev/null +++ b/html/template/sync/base.html @@ -0,0 +1,27 @@ + + + + + + {{ template "title" . }} - Nidus Sync + + + + + + + + + {{ block "extraheader" . }}{{ end }} + + + {{ template "content" . }} + + + diff --git a/html/template/sync/cell.html b/html/template/sync/cell.html new file mode 100644 index 00000000..d70b18f3 --- /dev/null +++ b/html/template/sync/cell.html @@ -0,0 +1,257 @@ +{{ template "sync/authenticated.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + {{ template "map" .MapData }} + +{{ end }} +{{ define "content" }} +
+ +
+
+

Location Data View

+
+
+ + +
+
+
+
+
+
+
+
+
+
Approximate Address:
+

+ 123 Main St, Anytown, ST 12345 +

+ +
+ +
Cell Coordinates (Hexagon):
+
+ + + {{ range $i, $cb := .CellBoundary }} + + + + + {{ end }} + +
Vertex {{ $i }}:{{ $cb|latLngDisplay }}
+
+
+

{{ .CellBoundary|GISStatement }}

+
+
+
+
+ + +
+ +
+ +

Mosquito Breeding Sources

+
+
+
+ + + + + + + + + + + {{ range .BreedingSources }} + + + + + + + {{ end }} + +
IDSource TypeLast InspectedLast Treated
+ {{ .ID|uuidShort }} + {{ .Type }}{{ .LastInspected|timeSincePtr }}{{ .LastTreated|timeSincePtr }}
+
+ +
+
+ + +

Inspections History

+
+
+
+ + + + + + + + + + + + {{ range .Inspections }} + + + + + + + + {{ end }} + +
LocationIDLocationDateActionNotes
+ {{ .LocationID|uuidShort }} + {{ .Location }}{{ .Date|timeSincePtr }}{{ .Action }}{{ .Notes }}
+
+ + +
+
+
+ + +
+

Traps

+ {{ if gt (len .Traps) 0 }} +
+
+
+ + + + + + + + + + {{ range .Traps }} + + + + + + {{ end }} + +
IDActiveComments
+ {{ .GlobalID|uuidShort }} + {{ .Active }}{{ .Comments }}
+
+
+
+ {{ else }} +

No traps

+ {{ end }} + + + +

Treatment History

+
+
+
+ + + + + + + + + + + {{ range .Treatments }} + + + + + + + {{ end }} + +
LocationTreatment DateInsecticide UsedTechnician Notes
+ {{ .LocationID|uuidShort }} + {{ .Date|timeSincePtr }}{{ .Product }}{{ .Notes }}
+
+ + +
+
+
+
+
+{{ end }} diff --git a/html/template/sync/component/header.html b/html/template/sync/component/header.html new file mode 100644 index 00000000..0a520e12 --- /dev/null +++ b/html/template/sync/component/header.html @@ -0,0 +1,149 @@ +{{ define "sync/component/header.html" }} + +{{ end }} diff --git a/html/template/sync/component/icons.html b/html/template/sync/component/icons.html new file mode 100644 index 00000000..39856882 --- /dev/null +++ b/html/template/sync/component/icons.html @@ -0,0 +1,109 @@ +{{ define "sync/component/icons.html" }} + + + Bootstrap + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +{{ end }} diff --git a/sync/template/components/map.html b/html/template/sync/component/map.html similarity index 67% rename from sync/template/components/map.html rename to html/template/sync/component/map.html index 75e8a175..7f486c8c 100644 --- a/sync/template/components/map.html +++ b/html/template/sync/component/map.html @@ -1,6 +1,9 @@ -{{define "map"}} - - +{{ define "sync/component/map.html" }} + + - -{{end}} + +{{ end }} diff --git a/html/template/sync/component/sidebar.html b/html/template/sync/component/sidebar.html new file mode 100644 index 00000000..1a85d28f --- /dev/null +++ b/html/template/sync/component/sidebar.html @@ -0,0 +1,57 @@ +{{ define "sync/component/sidebar.html" }} + +{{ end }} diff --git a/html/template/sync/dashboard.html b/html/template/sync/dashboard.html new file mode 100644 index 00000000..81f533aa --- /dev/null +++ b/html/template/sync/dashboard.html @@ -0,0 +1,246 @@ +{{ template "sync/authenticated.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + + + + + +{{ end }} +{{ define "content" }} +
+ +
+
+

{{ .User.Organization.Name }} Dashboard

+

+ Overview of mosquito control activities in your district +

+
+
+ {{ if .IsSyncOngoing }} +

+ Syncing now... +

+ {{ else }} +

+ Last updated: + {{ .LastSync | timeSincePtr }} + +

+ {{ end }} +
+
+ + +
+ +
+
+
+
+ +
+
Last Data Refresh
+

{{ .LastSync | timeSincePtr }}

+ +
+
+
+ + +
+
+
+
+ +
+
Service Requests
+ {{ if .IsSyncOngoing }} +

+ {{ .CountServiceRequests | bigNumber }}...? +

+ {{ else }} +

+ {{ .CountServiceRequests | bigNumber }} +

+ {{ end }} + +
+
+
+ + +
+
+
+
+ +
+
Mosquito Sources
+ {{ if .IsSyncOngoing }} +

+ {{ .CountMosquitoSources | bigNumber }}..? +

+ {{ else }} +

+ {{ .CountMosquitoSources | bigNumber }} +

+ {{ end }} + +
+
+
+ + +
+
+
+
+ +
+
Traps
+ {{ if .IsSyncOngoing }} +

{{ .CountTraps | bigNumber }}...?

+ {{ else }} +

{{ .CountTraps | bigNumber }}

+ {{ end }} + +
+
+
+
+ + +

Mosquito Activity Heatmap

+
+
+ +
+
+ + +

Recent Activity

+
+
+
+
+
+ + + + + + + + + + + + {{ range $i, $sr := .RecentRequests }} + + + + + + + + {{ end }} + +
DateTypeLocationStatusAction
{{ $sr.Date | timeSincePtr }}Service Request{{ $sr.Location }}Completed + View +
+
+
+
+
+
+
+{{ end }} diff --git a/html/template/sync/data-entry-bad.html b/html/template/sync/data-entry-bad.html new file mode 100644 index 00000000..0febab27 --- /dev/null +++ b/html/template/sync/data-entry-bad.html @@ -0,0 +1,235 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Data Entry{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+
+

Upload Failed: pools-data-2023.csv

+ + Validation Errors + +
+ + + +
+
+
Error Summary
+ + Download Error Log + +
+
+

+ We found 12 errors in your CSV file. The most common + issues are: +

+
    +
  • + Missing required column: The "Latitude" column is + not present in your file +
  • +
  • + Invalid data format: 8 GPS coordinates contain + non-numeric values +
  • +
  • + Empty required fields: 3 records are missing Plat + ID values +
  • +
+ + +
+
+ +
+
+
Detailed Error Report
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Line NumberErrorSuggestion
1 + MISSING_COLUMN
+ Required column "Latitude" is missing from the header row +
+ Add a "Latitude" column to your CSV file. Make sure the spelling + and capitalization match exactly. +
5 + INVALID_DATA_FORMAT
+ GPS coordinate "37.45N" is not a valid decimal number +
+ Change "37.45N" to a decimal format (e.g., "37.45"). Remove any + non-numeric characters except for the decimal point. +
8 + EMPTY_REQUIRED_FIELD
+ Plat ID is empty or missing +
+ Add a Plat ID value for this record. Each pool must have a + unique identifier. +
12 + INVALID_DATA_FORMAT
+ GPS coordinate "unknown" is not a valid decimal number +
+ Replace "unknown" with the actual longitude value in decimal + format (e.g., "-122.4194"). +
17 + INVALID_DATA_FORMAT
+ GPS coordinate "N/A" is not a valid decimal number +
+ Replace "N/A" with the actual latitude value in decimal format + (e.g., "37.7749"). +
21 + EMPTY_REQUIRED_FIELD
+ Plat ID is empty or missing +
+ Add a Plat ID value for this record. Each pool must have a + unique identifier. +
+
+ +
+ +
+
+
Next Steps
+
+
+
    +
  1. + Download the error log for a complete list of issues (optional) +
  2. +
  3. Fix the errors in your CSV file
  4. +
  5. Re-upload the corrected file using the button below
  6. +
+ + + +
+ +
+
+
+ + +
+{{ end }} diff --git a/html/template/sync/data-entry-good.html b/html/template/sync/data-entry-good.html new file mode 100644 index 00000000..db222785 --- /dev/null +++ b/html/template/sync/data-entry-good.html @@ -0,0 +1,243 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Data Entry{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+
+

Upload Results: pools-data-2023.csv

+ + File Parsed Successfully + +
+ +
+
+
+
+

45

+
Existing Pools
+

Matches found in previous records

+
+
+
+
+
+
+

23

+
New Pools
+

Not found in existing records

+
+
+
+
+
+
+

4

+
Outside District
+

Potential geocoding errors

+
+
+
+
+ +
+
+
Data Preview
+
+ + +
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Plat IDLatitudeLongitudeStreet AddressStatusIn District
P1234537.7749-122.4194123 Main St, Anytown, CA + Existing + + Yes +
P2345637.3352-121.8811456 Oak Ave, Someville, CA + Existing + + Yes +
P3456738.5816-121.4944789 Pine Rd, Outtown, CANew + + No - Outside northern boundary +
P4567837.4419-122.1430101 Elm St, Cityville, CANew + Yes +
P5678937.3541-121.9552202 Maple Dr, Townburg, CA + Existing + + Yes +
P6789035.3733-119.0187303 Cedar Ln, Farville, CANew + + No - Outside southern boundary +
P7890137.8044-122.2712404 Birch Ave, Metroburg, CA + Existing + + Yes +
P8901237.4032-123.9612505 Walnut St, Edgetown, CANew + + No - Outside western boundary +
+
+ + +
+
+ +
+
+
Notes & Recommendations
+
+
+
+

Issues detected:

+
    +
  • + 4 pools appear to be outside district boundaries (possible + geocoding errors) +
  • +
  • All required fields are present and properly formatted
  • +
+
+ + +
+
+ +
+ + Upload Edited File + + +
+
+{{ end }} diff --git a/html/template/sync/data-entry.html b/html/template/sync/data-entry.html new file mode 100644 index 00000000..fb5e3eab --- /dev/null +++ b/html/template/sync/data-entry.html @@ -0,0 +1,131 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Data Entry{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+

Upload Pool Data

+ +
+
+
CSV Upload Requirements
+
+
+

+ Your CSV file must contain the following columns in any order. Please + ensure your data matches the required format. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescriptionFormatRequired
LatitudeGPS latitude coordinateDecimal (e.g., 37.7749)Yes
LongitudeGPS longitude coordinateDecimal (e.g., -122.4194)Yes
Plat IDUnique identifier for the propertyAlphanumeric (e.g., P12345)Yes
Street AddressNearest street address to the poolText (e.g., 123 Main St)No
+ +
+ Need a template? + Download sample CSV file +
+
+
+ +
+
+
Upload Data
+
+
+
+ + + + +
Select your CSV file
+

Drag and drop a file here or click to browse

+ +
+ + +
+
+ +
+ Need assistance? Contact + support@example.com +
+
+{{ end }} diff --git a/html/template/sync/dispatch-results.html b/html/template/sync/dispatch-results.html new file mode 100644 index 00000000..70b02431 --- /dev/null +++ b/html/template/sync/dispatch-results.html @@ -0,0 +1,330 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Data Entry{{ end }} +{{ define "extraheader" }} + + +{{ end }} +{{ define "content" }} +
+

Route Calculation Results

+ + Edit Parameters + +
+ + +
+
+

Route Map

+
+
+
+
+ +

Interactive Map View

+

+ Routes are color-coded by technician assignment +

+
+
+
+
+ + +
+
+ +
+

Coverage Projection

+

+ If every day were like today, all pools would be complete on + October 27, 2023 +

+
+
+
+ + +
+
+

Route Summary

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SelectRouteTechnicianCold Call PoolsDrone InspectionsService CallsWarrantsEst. TimeActions
+
+ +
+
+
A
+
+
+ John Davis + John Davis +
+
120526h 15m + +
+
+ +
+
+
B
+
+
+ Sarah Johnson + Sarah Johnson +
+
83417h 30m + +
+
+ +
+
+
C
+
+
+ Michael Chen + Michael Chen +
+
104307h 45m + +
+
+ +
+
+
D
+
+
+ Jessica Martinez + Jessica Martinez +
+
142638h 00m + +
+
+
+
+ + + + + + +{{ end }} diff --git a/html/template/sync/dispatch.html b/html/template/sync/dispatch.html new file mode 100644 index 00000000..74bbe1c0 --- /dev/null +++ b/html/template/sync/dispatch.html @@ -0,0 +1,308 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Data Entry{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+

Technician Routing & Dispatch

+ +
+
+

Technician Roster

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TechnicianWorking HoursTruck AssignmentWarrant ServiceDrone CertifiedRouting Options
+
+ John Davis +
+
John Davis
+
ID: T-1001
+
+
+
7:00 AM - 3:30 PMTruck #103YesNo +
+ + +
+
+ + +
+
+ + +
+
+
+ Sarah Johnson +
+
Sarah Johnson
+
ID: T-1042
+
+
+
8:00 AM - 4:30 PMTruck #118YesYes +
+ + +
+
+ + +
+
+ + +
+
+
+ Michael Chen +
+
Michael Chen
+
ID: T-1019
+
+
+
6:30 AM - 3:00 PMTruck #107NoYes +
+ + +
+
+ + +
+
+ + +
+
+
+ Jessica Martinez +
+
Jessica Martinez
+
ID: T-1055
+
+
+
7:30 AM - 4:00 PMTruck #112YesYes +
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ + +
+{{ end }} diff --git a/sync/template/district.html b/html/template/sync/district.html similarity index 59% rename from sync/template/district.html rename to html/template/sync/district.html index b1cf612c..be3d8853 100644 --- a/sync/template/district.html +++ b/html/template/sync/district.html @@ -1,14 +1,17 @@ -{{template "base.html" .}} +{{ template "sync/base.html" . }} -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - - - - - - - +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + + + + + + + - -{{end}} -{{define "content"}} -
-
-
- + body { + background-color: #f8f9fa; + } + .detail-label { + font-size: 0.8rem; + text-transform: uppercase; + color: #6c757d; + margin-bottom: 2px; + font-weight: 600; + } + .dashboard-container { + padding: 20px 0; + } + .stats-card { + border-radius: 10px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); + transition: transform 0.2s; + height: 100%; + } + .stats-card:hover { + transform: translateY(-5px); + } + .map-container { + background-color: #e9ecef; + border-radius: 10px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); + height: 500px; + display: flex; + align-items: center; + justify-content: center; + margin-top: 20px; + } + #map { + height: 500px; + width: 100%; + margin-bottom: 10px; + } + #map img { + max-width: none; + min-width: 0px; + height: auto; + } + .section-title { + margin: 30px 0 20px; + padding-bottom: 10px; + border-bottom: 1px solid #dee2e6; + } + .last-refreshed { + color: #6c757d; + } + .logo-placeholder { + width: 100px; + height: 40px; + background-color: #e9ecef; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + } + .metric-icon { + font-size: 2rem; + margin-bottom: 10px; + display: inline-block; + width: 50px; + height: 50px; + line-height: 50px; + text-align: center; + border-radius: 50%; + } + .metric-value { + font-size: 2rem; + font-weight: bold; + } + .syncing { + color: #28a745; + animation: fa-spin 2s linear infinite; + } + +{{ end }} +{{ define "content" }} +
+
+
+ - + api-key="{{ .MapboxToken }}" + > + +
+
-
-
-
-
-
-
-
-
-
-
-
- +
+
+
-
-
-
District Details
-
-
-
Agency
-
-
-
-
-
Manager
-
-
-
-
-
Phone
-
-
-
-
-
Website
-
-
+
+
+
+ +
+
+
+
+
District Details
+
+
+
Agency
+
-
+
+
+
Manager
+
-
+
+
+
Phone
+
-
+
+
+
Website
+
-
+
-
-{{end}} +{{ end }} diff --git a/html/template/sync/empty-auth.html b/html/template/sync/empty-auth.html new file mode 100644 index 00000000..cfdab34a --- /dev/null +++ b/html/template/sync/empty-auth.html @@ -0,0 +1,7 @@ +{{ template "sync/authenticated.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} +{{ end }} +{{ define "content" }} +{{ end }} diff --git a/html/template/sync/empty.html b/html/template/sync/empty.html new file mode 100644 index 00000000..068470fc --- /dev/null +++ b/html/template/sync/empty.html @@ -0,0 +1,7 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} +{{ end }} +{{ define "content" }} +{{ end }} diff --git a/html/template/sync/layout-test.html b/html/template/sync/layout-test.html new file mode 100644 index 00000000..f82868ea --- /dev/null +++ b/html/template/sync/layout-test.html @@ -0,0 +1,77 @@ +{{ template "sync/authenticated.html" . }} +{{ define "title" }}Layout Test{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} + +
+
+
+
+ + + + + + + + + + +
+ +
+
+
+
Card 1
+

Some example content for the first card.

+
+
+
+
+
+
+
Card 2
+

Some example content for the second card.

+
+
+
+
+
+
+
+
Primary
+
Primary-100
+
Primary-200
+
Primary-300
+
+
+
Secondary
+
+
+
Success
+
+
+
+
+
Danger
+
+
+
Warning
+
+
+
Info
+
+
+
+{{ end }} diff --git a/html/template/sync/mock-root.html b/html/template/sync/mock-root.html new file mode 100644 index 00000000..2fddbbe7 --- /dev/null +++ b/html/template/sync/mock-root.html @@ -0,0 +1,66 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Data Entry{{ end }} +{{ define "extraheader" }} +{{ end }} +{{ define "content" }} +
+

Mock Listing

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LinkNameDescription
/mock/adminAdmin + Used by office admins to handle phone calls and other public-facing + responsibilities +
/mock/flyover-data-entryFlyover Data Entry + Used to upload CSV files with information about problematic pools +
/mock/dispatchDispatchUsed each day to calculate the working routes for technicians
/mock/reportReporting 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-123Self-ReportA page for members of the public to report a green pool.
/mock/service-requestService Request + A page for members of the public to make a direct service request +
/mock/settingSettingsA page for management to control the behavior of Nidus
+
+{{ end }} diff --git a/html/template/sync/oauth-prompt.html b/html/template/sync/oauth-prompt.html new file mode 100644 index 00000000..a023f620 --- /dev/null +++ b/html/template/sync/oauth-prompt.html @@ -0,0 +1,115 @@ +{{ template "sync/authenticated.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+
+
+
+

Connect Your ArcGIS Account

+

Link your data to get started

+
+ +
+

+ To provide you with the best experience, we need to connect to your + ArcGIS account. This allows us to securely access and visualize your + spatial data within our platform. +

+ +
+

What to expect:

+ +
+
1. Secure Authentication
+

+ When you click the "Connect to ArcGIS" button below, you'll be + redirected to the official ArcGIS login page. This connection is + secure and uses OAuth 2.0 protocol. +

+
+ +
+
2. Grant Permissions
+

+ After logging in with your ArcGIS credentials, you'll be asked + to approve permissions for our application to access your data. + We only request access to what's needed for the platform to + function. +

+
+ +
+
3. Return to Platform
+

+ Once authentication is complete, you'll be automatically + redirected back to our platform where your data will be + available to work with. +

+
+
+ +
+ Note: You'll need an active ArcGIS Online account + or ArcGIS Enterprise account to proceed. If you don't have one, you + can + create an ArcGIS account here. +
+ +

By connecting your ArcGIS account, you'll be able to:

+
    +
  • Access and visualize your spatial data
  • +
  • Perform advanced analysis using our integrated tools
  • +
  • Share results with team members securely
  • +
  • Keep your data synchronized across platforms
  • +
+ +
+ + Connect to ArcGIS + +

+ You can disconnect your account at any time in settings +

+
+
+
+
+
+{{ end }} diff --git a/html/template/sync/privacy.html b/html/template/sync/privacy.html new file mode 100644 index 00000000..40fdadd6 --- /dev/null +++ b/html/template/sync/privacy.html @@ -0,0 +1,561 @@ +{{ template "sync/base.html" . }} +{{ define "title" }}Privacy Policy{{ end }} +{{ define "extraheader" }} +{{ end }} +{{ define "content" }} +
+

Privacy Policy

+

Last updated: January 20, 2026

+

+ This Privacy Policy describes Our policies and procedures on the + collection, use and disclosure of Your information when You use the + Service and tells You about Your privacy rights and how the law protects + You. +

+

+ We use Your Personal Data to provide and improve the Service. By using the + Service, You agree to the collection and use of information in accordance + with this Privacy Policy. +

+

Interpretation and Definitions

+

Interpretation

+

+ The words whose initial letters are capitalized have meanings defined + under the following conditions. The following definitions shall have the + same meaning regardless of whether they appear in singular or in plural. +

+

Definitions

+

For the purposes of this Privacy Policy:

+
    +
  • +

    + Account means a unique account created for You to + access our Service or parts of our Service. +

    +
  • +
  • +

    + Affiliate means an entity that controls, is + controlled by, or is under common control with a party, where + "control" means ownership of 50% or more of the shares, + equity interest or other securities entitled to vote for election of + directors or other managing authority. +

    +
  • +
  • +

    + Company (referred to as either "the + Company", "We", "Us" or "Our" in + this Privacy Policy) refers to {{ .Company }}, + {{ .Address }} +

    +
  • +
  • +

    + Cookies are small files that are placed on Your + computer, mobile device or any other device by a website, containing + the details of Your browsing history on that website among its many + uses. +

    +
  • +
  • +

    Country refers to: Arizona, United States

    +
  • +
  • +

    + Device means any device that can access the Service + such as a computer, a cell phone or a digital tablet. +

    +
  • +
  • +

    + Personal Data (or "Personal Information") + is any information that relates to an identified or identifiable + individual. +

    +

    + We use "Personal Data" and "Personal Information" + interchangeably unless a law uses a specific term. +

    +
  • +
  • +

    Service refers to the Website.

    +
  • +
  • +

    + Service Provider means any natural or legal person + who processes the data on behalf of the Company. It refers to + third-party companies or individuals employed by the Company to + facilitate the Service, to provide the Service on behalf of the + Company, to perform services related to the Service or to assist the + Company in analyzing how the Service is used. +

    +
  • +
  • +

    + Usage Data refers to data collected automatically, + either generated by the use of the Service or from the Service + infrastructure itself (for example, the duration of a page visit). +

    +
  • +
  • +

    + Website refers to {{ .Site }}, accessible from + {{ .URLSync }}. +

    +
  • +
  • +

    + You means the individual accessing or using the + Service, or the company, or other legal entity on behalf of which such + individual is accessing or using the Service, as applicable. +

    +
  • +
+

Collecting and Using Your Personal Data

+

Types of Data Collected

+

Personal Data

+

+ While using Our Service, We may ask You to provide Us with certain + personally identifiable information that can be used to contact or + identify You. Personally identifiable information may include, but is not + limited to: +

+
    +
  • Email address
  • +
  • First name and last name
  • +
  • Phone number
  • +
  • Address, State, Province, ZIP/Postal code, City
  • +
+

Usage Data

+

Usage Data is collected automatically when using the Service.

+

+ Usage Data may include information such as Your Device's Internet Protocol + address (e.g. IP address), browser type, browser version, the pages of our + Service that You visit, the time and date of Your visit, the time spent on + those pages, unique device identifiers and other diagnostic data. +

+

+ When You access the Service by or through a mobile device, We may collect + certain information automatically, including, but not limited to, the type + of mobile device You use, Your mobile device's unique ID, the IP address + of Your mobile device, Your mobile operating system, the type of mobile + Internet browser You use, unique device identifiers and other diagnostic + data. +

+

+ We may also collect information that Your browser sends whenever You visit + Our Service or when You access the Service by or through a mobile device. +

+

Tracking Technologies and Cookies

+

+ We use Cookies and similar tracking technologies to track the activity on + Our Service and store certain information. Tracking technologies We use + include beacons, tags, and scripts to collect and track information and to + improve and analyze Our Service. The technologies We use may include: +

+
    +
  • + Cookies or Browser Cookies. A cookie is a small file + placed on Your Device. You can instruct Your browser to refuse all + Cookies or to indicate when a Cookie is being sent. However, if You do + not accept Cookies, You may not be able to use some parts of our + Service. +
  • +
+

+ Cookies can be "Persistent" or "Session" Cookies. + Persistent Cookies remain on Your personal computer or mobile device when + You go offline, while Session Cookies are deleted as soon as You close + Your web browser. +

+

+ Where required by law, we use non-essential cookies (such as analytics, + advertising, and remarketing cookies) only with Your consent. You can + withdraw or change Your consent at any time using Our cookie preferences + tool (if available) or through Your browser/device settings. Withdrawing + consent does not affect the lawfulness of processing based on consent + before its withdrawal. +

+

+ We use both Session and Persistent Cookies for the purposes set out below: +

+
    +
  • +

    Necessary / Essential Cookies

    +

    Type: Session Cookies

    +

    Administered by: Us

    +

    + Purpose: These Cookies are essential to provide You with services + available through the Website and to enable You to use some of its + features. They help to authenticate users and prevent fraudulent use + of user accounts. Without these Cookies, the services that You have + asked for cannot be provided, and We only use these Cookies to provide + You with those services. +

    +
  • +
  • +

    Functionality Cookies

    +

    Type: Persistent Cookies

    +

    Administered by: Us

    +

    + Purpose: These Cookies allow Us to remember choices You make when You + use the Website, such as remembering your login details or language + preference. The purpose of these Cookies is to provide You with a more + personal experience and to avoid You having to re-enter your + preferences every time You use the Website. +

    +
  • +
+

+ For more information about the cookies we use and your choices regarding + cookies, please visit our Cookies Policy or the Cookies section of Our + Privacy Policy. +

+

Use of Your Personal Data

+

The Company may use Personal Data for the following purposes:

+
    +
  • +

    + To provide and maintain our Service, including to + monitor the usage of our Service. +

    +
  • +
  • +

    + To manage Your Account: to manage Your registration + as a user of the Service. The Personal Data You provide can give You + access to different functionalities of the Service that are available + to You as a registered user. +

    +
  • +
  • +

    + For the performance of a contract: the development, + compliance and undertaking of the purchase contract for the products, + items or services You have purchased or of any other contract with Us + through the Service. +

    +
  • +
  • +

    + To contact You: To contact You by email, telephone + calls, SMS, or other equivalent forms of electronic communication, + such as a mobile application's push notifications regarding updates or + informative communications related to the functionalities, products or + contracted services, including the security updates, when necessary or + reasonable for their implementation. +

    +
  • +
  • +

    + To provide You with news, special offers, and general + information about other goods, services and events which We offer that + are similar to those that you have already purchased or inquired about + unless You have opted not to receive such information. +

    +
  • +
  • +

    + To manage Your requests: To attend and manage Your + requests to Us. +

    +
  • +
  • +

    + For business transfers: We may use Your Personal Data + to evaluate or conduct a merger, divestiture, restructuring, + reorganization, dissolution, or other sale or transfer of some or all + of Our assets, whether as a going concern or as part of bankruptcy, + liquidation, or similar proceeding, in which Personal Data held by Us + about our Service users is among the assets transferred. +

    +
  • +
  • +

    + For other purposes: We may use Your information for + other purposes, such as data analysis, identifying usage trends, + determining the effectiveness of our promotional campaigns and to + evaluate and improve our Service, products, services, marketing and + your experience. +

    +
  • +
+

We may share Your Personal Data in the following situations:

+
    +
  • + With Service Providers: We may share Your Personal Data + with Service Providers to monitor and analyze the use of our Service, to + contact You. +
  • +
  • + For business transfers: We may share or transfer Your + Personal Data in connection with, or during negotiations of, any merger, + sale of Company assets, financing, or acquisition of all or a portion of + Our business to another company. +
  • +
  • + With Affiliates: We may share Your Personal Data with + Our affiliates, in which case we will require those affiliates to honor + this Privacy Policy. Affiliates include Our parent company and any other + subsidiaries, joint venture partners or other companies that We control + or that are under common control with Us. +
  • +
  • + With business partners: We may share Your Personal Data + with Our business partners to offer You certain products, services or + promotions. +
  • +
  • + With other users: If Our Service offers public areas, + when You share Personal Data or otherwise interact in the public areas + with other users, such information may be viewed by all users and may be + publicly distributed outside. +
  • +
  • + With Your consent: We may disclose Your Personal Data + for any other purpose with Your consent. +
  • +
+

Retention of Your Personal Data

+

+ The Company will retain Your Personal Data only for as long as is + necessary for the purposes set out in this Privacy Policy. We will retain + and use Your Personal Data to the extent necessary to comply with our + legal obligations (for example, if We are required to retain Your data to + comply with applicable laws), resolve disputes, and enforce our legal + agreements and policies. +

+

+ Where possible, We apply shorter retention periods and/or reduce + identifiability by deleting, aggregating, or anonymizing data. Unless + otherwise stated, the retention periods below are maximum periods + ("up to") and We may delete or anonymize data sooner when it is + no longer needed for the relevant purpose. We apply different retention + periods to different categories of Personal Data based on the purpose of + processing and legal obligations: +

+
    +
  • +

    Account Information

    +
      +
    • + User Accounts: retained for the duration of your account + relationship plus up to 24 months after account closure to handle + any post-termination issues or resolve disputes. +
    • +
    +
  • +
  • +

    Customer Support Data

    +
      +
    • + Support tickets and correspondence: up to 24 months from the date of + ticket closure to resolve follow-up inquiries, track service + quality, and defend against potential legal claims +
    • +
    • + Chat transcripts: up to 24 months for quality assurance and staff + training purposes. +
    • +
    +
  • +
  • +

    Usage Data

    +
      +
    • +

      + Website analytics data (cookies, IP addresses, device + identifiers): up to 24 months from the date of collection, which + allows us to analyze trends while respecting privacy principles. +

      +
    • +
    • +

      + Server logs (IP addresses, access times): up to 24 months for + security monitoring and troubleshooting purposes. +

      +
    • +
    +
  • +
+

+ Usage Data is retained in accordance with the retention periods described + above, and may be retained longer only where necessary for security, fraud + prevention, or legal compliance. +

+

+ We may retain Personal Data beyond the periods stated above for different + reasons: +

+
    +
  • + Legal obligation: We are required by law to retain specific data (e.g., + financial records for tax authorities). +
  • +
  • + Legal claims: Data is necessary to establish, exercise, or defend legal + claims. +
  • +
  • Your explicit request: You ask Us to retain specific information.
  • +
  • + Technical limitations: Data exists in backup systems that are scheduled + for routine deletion. +
  • +
+

+ You may request information about how long We will retain Your Personal + Data by contacting Us. +

+

+ When retention periods expire, We securely delete or anonymize Personal + Data according to the following procedures: +

+
    +
  • + Deletion: Personal Data is removed from Our systems and no longer + actively processed. +
  • +
  • + Backup retention: Residual copies may remain in encrypted backups for a + limited period consistent with our backup retention schedule and are not + restored except where necessary for security, disaster recovery, or + legal compliance. +
  • +
  • + Anonymization: In some cases, We convert Personal Data into anonymous + statistical data that cannot be linked back to You. This anonymized data + may be retained indefinitely for research and analytics. +
  • +
+

Transfer of Your Personal Data

+

+ Your information, including Personal Data, is processed at the Company's + operating offices and in any other places where the parties involved in + the processing are located. It means that this information may be + transferred to — and maintained on — computers located outside of Your + state, province, country or other governmental jurisdiction where the data + protection laws may differ from those from Your jurisdiction. +

+

+ Where required by applicable law, We will ensure that international + transfers of Your Personal Data are subject to appropriate safeguards and + supplementary measures where appropriate. The Company will take all steps + reasonably necessary to ensure that Your data is treated securely and in + accordance with this Privacy Policy and no transfer of Your Personal Data + will take place to an organization or a country unless there are adequate + controls in place including the security of Your data and other personal + information. +

+

Delete Your Personal Data

+

+ You have the right to delete or request that We assist in deleting the + Personal Data that We have collected about You. +

+

+ Our Service may give You the ability to delete certain information about + You from within the Service. +

+

+ You may update, amend, or delete Your information at any time by signing + in to Your Account, if you have one, and visiting the account settings + section that allows you to manage Your personal information. You may also + contact Us to request access to, correct, or delete any Personal Data that + You have provided to Us. +

+

+ Please note, however, that We may need to retain certain information when + we have a legal obligation or lawful basis to do so. +

+

Disclosure of Your Personal Data

+

Business Transactions

+

+ If the Company is involved in a merger, acquisition or asset sale, Your + Personal Data may be transferred. We will provide notice before Your + Personal Data is transferred and becomes subject to a different Privacy + Policy. +

+

Law enforcement

+

+ Under certain circumstances, the Company may be required to disclose Your + Personal Data if required to do so by law or in response to valid requests + by public authorities (e.g. a court or a government agency). +

+

Other legal requirements

+

+ The Company may disclose Your Personal Data in the good faith belief that + such action is necessary to: +

+
    +
  • Comply with a legal obligation
  • +
  • Protect and defend the rights or property of the Company
  • +
  • + Prevent or investigate possible wrongdoing in connection with the + Service +
  • +
  • Protect the personal safety of Users of the Service or the public
  • +
  • Protect against legal liability
  • +
+

Security of Your Personal Data

+

+ The security of Your Personal Data is important to Us, but remember that + no method of transmission over the Internet, or method of electronic + storage is 100% secure. While We strive to use commercially reasonable + means to protect Your Personal Data, We cannot guarantee its absolute + security. +

+

Children's Privacy

+

+ Our Service does not address anyone under the age of 16. We do not + knowingly collect personally identifiable information from anyone under + the age of 16. If You are a parent or guardian and You are aware that Your + child has provided Us with Personal Data, please contact Us. If We become + aware that We have collected Personal Data from anyone under the age of 16 + without verification of parental consent, We take steps to remove that + information from Our servers. +

+

+ If We need to rely on consent as a legal basis for processing Your + information and Your country requires consent from a parent, We may + require Your parent's consent before We collect and use that information. +

+

Links to Other Websites

+

+ Our Service may contain links to other websites that are not operated by + Us. If You click on a third party link, You will be directed to that third + party's site. We strongly advise You to review the Privacy Policy of every + site You visit. +

+

+ We have no control over and assume no responsibility for the content, + privacy policies or practices of any third party sites or services. +

+

Changes to this Privacy Policy

+

+ We may update Our Privacy Policy from time to time. We will notify You of + any changes by posting the new Privacy Policy on this page. +

+

+ We will let You know via email and/or a prominent notice on Our Service, + prior to the change becoming effective and update the "Last + updated" date at the top of this Privacy Policy. +

+

+ You are advised to review this Privacy Policy periodically for any + changes. Changes to this Privacy Policy are effective when they are posted + on this page. +

+

Contact Us

+

+ If you have any questions about this Privacy Policy, You can contact us: +

+
    +
  • By email: privacy@gleipnir.technology
  • +
+
+{{ end }} diff --git a/html/template/sync/report-confirmation.html b/html/template/sync/report-confirmation.html new file mode 100644 index 00000000..4c320494 --- /dev/null +++ b/html/template/sync/report-confirmation.html @@ -0,0 +1,250 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Login{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+ +
+ County Vector Control +
+ + +
+ + + + +
+

Thank You for Your Submission!

+

+ Your green pool report has been successfully submitted. +

+
+ + +
+
Appointment Confirmed
+

Our inspector will visit your property at the scheduled time:

+ +
+
+
Date
+
Thursday, June 22, 2023
+
+
+
Time
+
10:00 AM
+
+
+
Confirmation #
+
GP-23685
+
+
+
+ + +
+
What Happens Next?
+
    +
  • + A confirmation email has been sent to the email address you + provided. +
  • +
  • + You'll receive a reminder notification 24 hours before your + scheduled appointment. +
  • +
  • + Our team will review your report and contact you by the next + business day if any additional information is needed. +
  • +
  • + During the scheduled visit, our inspector will assess the pool + condition and discuss treatment options if necessary. +
  • +
+

+ You can use the link below to track your report status and view the + photos you've submitted. +

+
+ + + +
+ Track Your Report Status +

View photos and check for updates

+
+ +
+ + +
+
Questions or Concerns?
+

+ If you have any questions about your report or need to change your + appointment, please contact us: +

+ +
+ +
+ (555) 123-4567 +
Monday-Friday, 8:00 AM - 5:00 PM
+
+
+ +
+ +
+ greenpool@vectorcontrol.county.gov +
+
+ +

+ Please include your confirmation number (GP-23685) in all + correspondence. +

+
+ + +
+ +
+
+ + +
+

+ Thank you for helping keep our community safe from mosquito-borne + diseases. +

+
+
+{{ end }} diff --git a/html/template/sync/report-contribute.html b/html/template/sync/report-contribute.html new file mode 100644 index 00000000..230f550a --- /dev/null +++ b/html/template/sync/report-contribute.html @@ -0,0 +1,295 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Login{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+ +
+ County Vector Control +
+ + +
+
+ Upload Photos + 3 of 4 +
+
+
+
+
+ + +
+

Upload Current Pool Photos

+

+ Please provide current photos of your pool to help us assess its + condition. +

+ + +
+
Photo Tips
+
    +
  • + Take photos from as high an angle as possible + (second story window, deck, etc.) +
  • +
  • Try to capture the entire pool in your photo
  • +
  • Ensure photos are clear and well-lit
  • +
  • + You can add multiple photos from different angles +
  • +
+
+ + +
Photo Examples:
+
+
+ Good photo example +
+ Good: High angle, full view +
+
+ +
+ Poor photo example +
+ Poor: Ground level, partial view +
+
+
+ + +
+
+ +
+
Add Pool Photos
+

Take a new photo or upload from your device

+ +
+ + +
+ + +
+ + +
+
Uploaded Photos (2)
+
+
+ Uploaded pool photo + +
+
+ Uploaded pool photo + +
+
+
+ + +
+ You can add up to 5 photos to provide a complete view of your pool + area. We recommend taking photos from multiple angles. +
+ + + +
+ + +
+

+ If you need assistance, please contact Vector Control at (555) 123-4567 +

+
+
+{{ end }} diff --git a/html/template/sync/report-detail.html b/html/template/sync/report-detail.html new file mode 100644 index 00000000..438afa4d --- /dev/null +++ b/html/template/sync/report-detail.html @@ -0,0 +1,151 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Login{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+ +
+ County Vector Control +
+ + +
+
+ Location + 1 of 4 +
+
+
+
+
+ + +
+

Confirm Property Location

+ + +
+
+
+ +
+
+ + +
+
Detected Address:
+
123 Maple Street, Riverside, CA 92501
+
+ +
+

Is this the correct location of the property in question?

+
+ + + +
+ + +
+

+ If you need assistance, please contact Vector Control at (555) 123-4567 +

+
+
+{{ end }} diff --git a/html/template/sync/report-evidence.html b/html/template/sync/report-evidence.html new file mode 100644 index 00000000..9eee024c --- /dev/null +++ b/html/template/sync/report-evidence.html @@ -0,0 +1,281 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Login{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+ +
+ County Vector Control +
+ + +
+
+ Evidence + 2 of 4 +
+
+
+
+
+ + +
+

Evidence of Potential Breeding Site

+ + +
+
+ Aerial Surveillance Photos +
+

+ These photos were taken during routine aerial surveillance of the + area. +

+ + +
+ + +
+
+ Historical Inspection Data +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateInspectorFindingsAction
Mar 15, 2023J. MartinezPool water stagnant, greenTreatment applied, owner notified
Nov 02, 2022L. JohnsonPool water clear, maintainedNo action needed
Aug 18, 2022S. WilliamsMinor algae formationOwner provided maintenance resources
+
+
+ + +
+
+ Mosquito Trap Count Data +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Date CollectedCountDistanceLevel
Jun 12, 2023420.3 milesHigh
Jun 05, 2023360.3 milesHigh
May 29, 2023280.3 milesMedium
May 22, 2023150.3 milesLow
May 15, 2023120.3 milesLow
+
+
+ + +
+
Why This Matters
+

+ The data above shows mosquito activity in your area. Recent trap + counts indicate elevated mosquito populations, which increases the + risk of mosquito-borne diseases like West Nile virus. +

+

+ Unmaintained swimming pools can produce thousands of mosquitoes each + week. By addressing potential breeding sites, you're helping protect + your family and neighbors from these health risks. +

+

+ We need your help to ensure we maintain public health + by keeping mosquito counts low in your neighborhood. Your cooperation + makes a significant difference in community safety. +

+
+ + + +
+ + +
+

+ If you need assistance, please contact Vector Control at (555) 123-4567 +

+
+
+{{ end }} diff --git a/html/template/sync/report-schedule.html b/html/template/sync/report-schedule.html new file mode 100644 index 00000000..ac253bb2 --- /dev/null +++ b/html/template/sync/report-schedule.html @@ -0,0 +1,344 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Login{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+ +
+ County Vector Control +
+ + +
+
+ Schedule Follow-up + 4 of 4 +
+
+
+
+
+ + +
+

Schedule a Follow-up Inspection

+

+ Please select a convenient date and time for our inspector to visit your + property. +

+ + +
+
+ +
Select Date
+
+ + +
+
+
Jun
+
20
+
Tue
+
+
+
Jun
+
21
+
Wed
+
+
+
Jun
+
22
+
Thu
+
+
+
Jun
+
23
+
Fri
+
+
+
Jun
+
26
+
Mon
+
+
+
Jun
+
27
+
Tue
+
+
+
Jun
+
28
+
Wed
+
+
+
Jun
+
29
+
Thu
+
+
+
Jun
+
30
+
Fri
+
+
+
Jul
+
03
+
Mon
+
+
+ + +
+ +
Select Time
+
+ +
+
8:00 AM
+
9:00 AM
+
10:00 AM
+
11:00 AM
+
1:00 PM
+
2:00 PM
+
3:00 PM
+
4:00 PM
+
+ + +
+
+ Selected Appointment: + Thursday, June 22, 2023 at 10:00 AM +
+
+
+ + +
+
+ +
Contact Information
+
+ +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ + +
+
+
+ + +
+ Our inspector will need access to view your pool area. If you + won't be home during the appointment, please provide access + instructions in the notes. +
+ + + +
+
+
+ + +
+

+ If you need assistance, please contact Vector Control at (555) 123-4567 +

+
+
+{{ end }} diff --git a/html/template/sync/report-update.html b/html/template/sync/report-update.html new file mode 100644 index 00000000..d56118cc --- /dev/null +++ b/html/template/sync/report-update.html @@ -0,0 +1,225 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Login{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+ +
+ County Vector Control +
+ + +
+

Update Property Location

+ + +
+
+ Two Ways to Update Location +
+

+ You can update the property location by either clicking on the map or + entering an address below. Both methods will automatically update each + other. +

+
+ + +
+
Option 1: Select Location on Map
+
+
+
+ +
+
+ Click or tap anywhere on the + map to set the location +
+
+
+ + +
OR
+ + +
+
Option 2: Enter Address
+
+
+ + +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+ + +
+
+ Current Coordinates: + 33.9806° N, 117.3755° W +
+
+ + +
+ + +
+
+ + +
+

+ If you need assistance, please contact Vector Control at (555) 123-4567 +

+
+
+{{ end }} diff --git a/html/template/sync/report.html b/html/template/sync/report.html new file mode 100644 index 00000000..205e675c --- /dev/null +++ b/html/template/sync/report.html @@ -0,0 +1,266 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Login{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+
+
+
+

Green Pool Reporting

+

Entry Points Diagnostic Page

+
+ +
+ + This page demonstrates the various ways customers can access the Green + Pool Reporting system. +
+ + +
+
+ +

Text Message Entry Point

+
+ +

+ Customers will receive the following text message with a link to + begin the reporting process: +

+ +
+ Vector Control: We noticed a potential green pool + at your property. Please tap the link to report status or schedule + inspection: + {{ .URLs.ReportDetail }} +
+ +
+

SMS Details:

+
    +
  • Sent via automated system after aerial detection
  • +
  • Contains unique tracking link for each property
  • +
  • Customers tap link to open mobile browser
  • +
+
+
+ + +
+
+ +

Door Hanger QR Code Entry Point

+
+ +

+ Inspectors will leave door hangers with a QR code for properties + where no one is home: +

+ +
+
IMPORTANT NOTICE
+

We visited regarding a potential mosquito breeding site.

+ + + +

+ Scan this code with your phone camera to report + your pool status or schedule an inspection. +

+

Or visit: {{ .URLs.ReportDetail }}

+
+ +
+

Door Hanger Details:

+
    +
  • Physical notices left on the door handle
  • +
  • QR code contains property-specific link
  • +
  • Fallback URL provided for manual entry
  • +
+
+
+ + +
+
+ +

Email Notification Entry Point

+
+ +

+ Property owners will receive this email as a follow-up to other + communication attempts: +

+ + + +
+

Email Details:

+
    +
  • + Sent as follow-up or for property owners with registered email + addresses +
  • +
  • + Contains clear call-to-action button and alternative text link +
  • +
  • Explains reason for contact and next steps
  • +
+
+
+ + +
+
+
+{{ end }} diff --git a/html/template/sync/service-request-detail.html b/html/template/sync/service-request-detail.html new file mode 100644 index 00000000..9979bed4 --- /dev/null +++ b/html/template/sync/service-request-detail.html @@ -0,0 +1,387 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} + +
+
+
+
+

[District Name]

+
+
+ +
+
+
+
+ + +
+
+ +
+
+

Report #MMD-2023-12345

+
+
+ + Green Pool + +
+
+ + +
+
+
+ +
+
Submitted
+
+
+
+ +
+
Accepted
+
+
+
+ +
+
Scheduled
+
+
+
+ +
+
Complete
+
+
+ +
+ +
+
+
+
Location
+
+
+ +
+
+ +

Map of Report Location

+
+
+ + +
+
Address
+

123 Mosquito Ave, Lakeside, CA 92040

+

+ 32.8573° N, 116.9222° W +

+
+
+
+
+ + +
+
+
+
Report Details
+
+
+
+
+
Report Source
+

Phone Call

+
+
+
Report Date
+

October 15, 2023 at 2:45 PM

+
+
+ +
+
Description
+

+ I noticed my neighbor's backyard pool has turned green and + there's nobody living in the house currently. I'm concerned it + might be breeding mosquitoes as I've noticed more of them in + my yard recently. The house seems to be vacant for about 3 + months now. +

+
+ +
+
+
Pool Status
+

+ + Stagnant/Green +

+
+
+
Scheduled Appointment
+

October 20, 2023, 9:00 AM - 11:00 AM

+
+
+ +
+
Contact Information
+
+
+

Reported By: John Smith

+

Phone: (555) 123-4567

+
+
+

+ Email: john.smith@example.com +

+

+ Preferred Contact: Phone +

+
+
+
+
+
+
+
+ + +
+
+
+
+
Notes & Updates
+ 3 Notes +
+
+
+
+ Added by System on Oct 15, 2023, 2:45 PM +
+
+ Report created via phone call to district office. +
+
+
+
+ Added by Sarah Johnson (Office Staff) on Oct 16, 2023, 9:30 AM +
+
+ Verified location information. Property appears to be vacant + according to county records. Left voicemail with property + management company listed in county database. +
+
+
+
+ Added by Mike Davis (Technician) on Oct 18, 2023, 11:15 AM +
+
+ Scheduled inspection for Oct 20. Will need access to backyard. + Contacted reporter to confirm they'll be available to provide + access information on day of service. +
+
+
+
+
+
+ + +
+
+
+
+
Next Steps
+

+ Technician scheduled to inspect the property on October 20, + 2023, between 9:00 AM - 11:00 AM. If access to the property is + not possible, treatment may be conducted from outside the + property or additional follow-up may be required. +

+

+ Note: You will receive a notification when the + status of this report changes. +

+
+
+
+
+ + +
+
+
+
+
Add Information
+
+
+

+ Do you have additional information about this report? Add it + below to update the technician. +

+
+
+ + +
+
+ + +
+ Provide your phone number if you'd like to be contacted + about this update. +
+
+ +
+
+
+
+
+ + + +
+
+ + +
+
+
+
+

+ © 2023 [District Name] Mosquito Management District +

+
+
+

+ Contact: (555) 123-4567 | info@mosquitodistrict.gov +

+
+
+
+
+{{ end }} diff --git a/html/template/sync/service-request-location.html b/html/template/sync/service-request-location.html new file mode 100644 index 00000000..8b54fd7f --- /dev/null +++ b/html/template/sync/service-request-location.html @@ -0,0 +1,423 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} + +
+
+
+
+

[District Name]

+
+
+ +
+
+
+
+ + +
+
+ +
+
+

Lookup Reports by Location

+

Find reports and mosquito activity in your area

+
+
+ + +
+
+
+
+
+ How to use this tool +
+

+ You can either enter an address in the search + box or navigate the map by dragging and zooming + to find reports in your area. The table below will update + automatically to show reports within the current map view. +

+
+
+
+
+ + +
+
+ +
+
+
Search by Address
+
+
+
+
+ +
+ + +
+
+
+ + +
+
+ +
+ Currently showing reports + within 1 mile of map center +
+
+
+
+ +
+ +
+
+ +

Interactive Map Area

+

+ The map will display here and allow you to navigate the area +

+
+ + +
+ + + +
+ + +
+
Current View
+
Lakeside, CA
+
32.857° N, 116.922° W
+
+
+
+
+ + +
+
+
+
+
Reports in This Area
+ Showing 12 of 37 total reports +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeLocationSubmittedStatusReport ID
+ + + + Green Pool + 123 Mosquito Ave + Oct 15, 2023 +
5 days ago
+
+ Scheduled + + MMD-2023-12345 +
+ + + + Mosquito Nuisance + 456 Lake Dr + Oct 12, 2023 +
8 days ago
+
+ Complete + + MMD-2023-12341 +
+ + + + Fish Request + 789 Creek Rd + Oct 18, 2023 +
2 days ago
+
+ Acknowledged + + MMD-2023-12350 +
+ + + + Mosquito Nuisance + 101 Pond Ln + Sep 25, 2023 +
25 days ago
+
+ Complete + + MMD-2023-12289 +
+ + + + Green Pool + 202 Highland Ave + Oct 19, 2023 +
1 day ago
+
+ Submitted + + MMD-2023-12356 +
+ + + + Green Pool + 303 Marsh Way + Aug 15, 2023 +
2 months ago
+
+ Complete + + MMD-2023-12056 +
+
+
+ +
+
+
+ + + +
+
+ + +
+
+
+
+

+ © 2023 [District Name] Mosquito Management District +

+
+
+

+ Contact: (555) 123-4567 | info@mosquitodistrict.gov +

+
+
+
+
+{{ end }} diff --git a/html/template/sync/service-request-mosquito.html b/html/template/sync/service-request-mosquito.html new file mode 100644 index 00000000..7d22c3f2 --- /dev/null +++ b/html/template/sync/service-request-mosquito.html @@ -0,0 +1,738 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + + +{{ end }} +{{ define "content" }} + +
+
+
+
+

[District Name]

+
+
+ +
+
+
+
+ + +
+
+ +
+
+

Report Mosquito Nuisance

+

Help us identify mosquito activity in your area

+
+
+ + +
+
+ +
+
+ + +
+ +
+
+ +

Mosquito Activity Information

+ optional +
+

+ The time when mosquitoes are active can help us identify the species + and likely breeding sources. +

+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+ + +
+ + +
+
+
Minor
+ Occasional mosquito +
+
+
Moderate
+ Regular presence +
+
+
Severe
+ Many mosquitoes +
+
+
+ Current selection: 3/5 +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ +

Potential Mosquito Sources

+ optional +
+

+ Have you noticed any of these common mosquito breeding sources in + your area? +

+ +
+
+
Did you know?
+

+ Mosquitoes can breed in as little as a bottle cap of water! + Eliminating standing water is the most effective way to reduce + mosquito populations. +

+
+
+ +
+ +
+
+
+
+ +
+
Stagnant Water
+

+ Green pools, ponds, fountains, or birdbaths that aren't + maintained +

+
+ + +
+
+
+
+ + +
+
+
+
+ +
+
Containers
+

+ Buckets, planters, toys, tires, or any items that collect + rainwater +

+
+ + +
+
+
+
+ + +
+
+
+
+ +
+
Roof & Gutters
+

+ Clogged gutters, flat roofs, or AC units that collect water +

+
+ + +
+
+
+
+
+ + + +
+
+ + +
+
+
+ + +
+
+ +

Inspection Request

+
+

+ Would you like our technicians to inspect for potential mosquito + sources? +

+ +
+
+
+
+ Property Inspection +
+

+ Request a technician to inspect your property for mosquito + sources. We'll contact you to schedule a convenient time. +

+
+ + +
+
+
+ +
+
+
Neighborhood Inspection
+

+ Request a general inspection of your neighborhood. We'll + survey the area for potential mosquito breeding sources. +

+
+ + +
+
+
+
+ + + +
+ + +
+
+ +

Location & Contact Information

+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+ We'll use this to send you a confirmation and follow-up + information. +
+
+
+
+ + +
+
+ +

Additional Information

+ optional +
+ +
+
+ + +
+
+
+ + +
+
+
+

+ Thank you for reporting this mosquito issue. +

+

+ After submission, you'll receive a confirmation with a report ID + and further information. +

+
+
+ +
+
+
+
+ + + +
+
+ + +
+
+
+
+

+ © 2023 [District Name] Mosquito Management District +

+
+
+

+ Contact: (555) 123-4567 | info@mosquitodistrict.gov +

+
+
+
+
+{{ end }} diff --git a/sync/template/service-request-pool.html b/html/template/sync/service-request-pool.html similarity index 56% rename from sync/template/service-request-pool.html rename to html/template/sync/service-request-pool.html index 432e3e1c..19138bec 100644 --- a/sync/template/service-request-pool.html +++ b/html/template/sync/service-request-pool.html @@ -1,82 +1,83 @@ -{{template "base.html" .}} +{{ template "sync/base.html" . }} -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - - -{{end}} -{{define "content"}} + +{{ end }} +{{ define "content" }}
@@ -179,7 +180,11 @@ document.addEventListener('DOMContentLoaded', function() {

{{ .DistrictName }}

- +
@@ -192,7 +197,10 @@ document.addEventListener('DOMContentLoaded', function() {

Report a Green Pool or Mosquito Source

-

Help us locate and treat potential mosquito breeding sources in your area

+

+ Help us locate and treat potential mosquito breeding sources in your + area +

@@ -200,8 +208,14 @@ document.addEventListener('DOMContentLoaded', function() {
@@ -215,21 +229,36 @@ document.addEventListener('DOMContentLoaded', function() {

Photos

optional
-

Photos help us identify the severity of the issue and may contain location data that can help us find the source.

- +

+ Photos help us identify the severity of the issue and may contain + location data that can help us find the source. +

+
- +

Drag and drop photos here

- or -

- -

You can upload multiple photos (maximum 5)

+

+ You can upload multiple photos (maximum 5) +

- +
@@ -243,21 +272,36 @@ document.addEventListener('DOMContentLoaded', function() {

Location

optional
-

Please provide the location of the potential mosquito breeding source. We may be able to extract this information from your photos if they contain location data.

- +

+ Please provide the location of the potential mosquito breeding + source. We may be able to extract this information from your photos + if they contain location data. +

+
- - + +
- -

You can also click on the map to mark the location precisely

+ +

+ You can also click on the map to mark the location precisely +

Interactive Map

-

Click to set the location of the mosquito source

+

+ Click to set the location of the mosquito source +

@@ -269,10 +313,12 @@ document.addEventListener('DOMContentLoaded', function() {

Source Details

optional
- +
- +
- +
- +
- +
- +
- + @@ -314,43 +371,71 @@ document.addEventListener('DOMContentLoaded', function() {

Access Information

optional
-

Please provide any details about how to access the mosquito source. This helps our technicians when they visit the site.

- +

+ Please provide any details about how to access the mosquito source. + This helps our technicians when they visit the site. +

+
- - + +
- +
- +
- +
- +
- - + +
- +
- - + +
@@ -365,42 +450,47 @@ document.addEventListener('DOMContentLoaded', function() {

Contact Information

optional
- +
Property Owner Information (if known)
- +
- +
- +
- +
Your Contact Information (for updates)
- +
- +
- +
- + @@ -416,12 +506,22 @@ document.addEventListener('DOMContentLoaded', function() {

Additional Information

optional
-

Please provide any other information that might help us address this mosquito source.

- +

+ Please provide any other information that might help us address this + mosquito source. +

+
- - + +
@@ -430,8 +530,16 @@ document.addEventListener('DOMContentLoaded', function() {
-

Thank you for helping us keep our community safe from mosquito-borne illnesses.

-

After submission, you will receive a confirmation with a report ID for tracking purposes.

+

+ Thank you for helping us keep our community safe from + mosquito-borne illnesses. +

+

+ After submission, you will receive a confirmation with a report + ID for tracking purposes. +

-
- -{{end}} +{{ end }} diff --git a/html/template/sync/service-request-quick-confirmation.html b/html/template/sync/service-request-quick-confirmation.html new file mode 100644 index 00000000..88d22e0b --- /dev/null +++ b/html/template/sync/service-request-quick-confirmation.html @@ -0,0 +1,150 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} + +
+
+
+
+

[District Name]

+
+
+ +
+
+
+
+ + +
+
+
+
+
+ +
+ + + +
+ +

Report Received!

+ +

+ Thank you for contributing to the health and well-being of our + community. +

+ +
+

+ Your mosquito report has been submitted successfully and will be + reviewed by our team. Your effort helps us identify problem + areas and better manage mosquito populations throughout our + district. +

+
+ +

+ Report ID: + #MM + +

+ + Return Home +
+
+ + +
+
+
What happens next?
+

+ Our team reviews all reports daily. Depending on the nature of + your report, we may deploy field technicians to assess the area or + add it to our scheduled mosquito control activities. For urgent + matters, we prioritize responses based on public health risk + factors. +

+
+
+
+
+
+ + +
+
+
+
+

+ © 2023 [District Name] Mosquito Management District +

+
+
+

Contact: (555) 123-4567

+
+
+
+
+{{ end }} diff --git a/html/template/sync/service-request-quick.html b/html/template/sync/service-request-quick.html new file mode 100644 index 00000000..15cb9749 --- /dev/null +++ b/html/template/sync/service-request-quick.html @@ -0,0 +1,233 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} + +
+
+
+
+

[District Name]

+
+
+ +
+
+
+
+ + +
+
+
+
+
+

Quick Mosquito Report

+ + +
+ +
+ + + + + Your location and current time will be automatically + collected with your report. +
+ + +
+ +
+ + + + +
+ + +
+ Take pictures of the mosquito problem area + + +
+ + Preview +
+
+
+ + +
+ + +
+ + + +
+
+
+ + + +
+
+
+ + +
+
+
+
+

+ © 2023 [District Name] Mosquito Management District +

+
+
+

Contact: (555) 123-4567

+
+
+
+
+{{ end }} diff --git a/html/template/sync/service-request-updates.html b/html/template/sync/service-request-updates.html new file mode 100644 index 00000000..5116eb8a --- /dev/null +++ b/html/template/sync/service-request-updates.html @@ -0,0 +1,222 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} + +
+
+
+
+

{{ .DistrictName }}

+
+
+ +
+
+
+
+ + +
+ +
+
+

Check Status or Follow-up

+
+
+ + +
+
+
+
+

+ Choose one of the following options to check on mosquito activity + or follow up on a previous report. +

+
+
+ +
+ +
+
+
+
+ + + + +
+

+ Look up by Report ID +

+

+ If you have a report ID from a previous request, enter it + below to view the details and current status. +

+ +
+
+ + +
Example: MMD-2023-12345
+
+ +
+
+
+
+ + +
+
+
+ + +
+
+
+
+ + + + +
+

Look up by Location

+

+ Don't have a report ID? You can check mosquito activity and + reports in your area by providing your location information. +

+

+ This option will guide you through selecting your location to + find relevant information about mosquito activity near you. +

+ +
+
+
+
+ + + +
+
+
+ + +
+
+
+
+

+ © 2023 {{ .DistrictName }} Mosquito Management District +

+
+
+

+ Contact: (555) 123-4567 | info@mosquitodistrict.gov +

+
+
+
+
+{{ end }} diff --git a/html/template/sync/service-request.html b/html/template/sync/service-request.html new file mode 100644 index 00000000..41947f51 --- /dev/null +++ b/html/template/sync/service-request.html @@ -0,0 +1,230 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} + +
+
+
+
+

{{ .DistrictName }}

+
+
+ +
+
+
+
+ + +
+ +
+
+
+
+

+ Welcome to Our Mosquito Management Services +

+

+ We are dedicated to protecting public health and improving quality + of life by reducing mosquito populations and the diseases they can + carry. Our district provides comprehensive mosquito surveillance, + control, and education services to our community. +

+
+
+
+
+ + +
+
+
+
+

On the go?

+ Make a Quick Report +

+ Report mosquito issues in under 60 seconds +

+
+
+
+
+ + +
+
+

How Can We Help You Today?

+
+ +
+
+
+
+ + + +
+

Follow-up or Check Status

+

+ Check on a previous request or view current mosquito activity + in your area. +

+ Get Updates +
+
+
+ + +
+
+
+
+ + + +
+

Report a Green Pool

+

+ Report stagnant water sources like abandoned pools that may + breed mosquitoes. +

+ Report Source +
+
+
+ + +
+
+
+
+ + + +
+

Report Mosquito Nuisance

+

+ Report areas with high adult mosquito activity causing + discomfort or concern. +

+ Report Problem +
+
+
+
+ + +
+
+
+
+
+
+
Need to make a quick report?
+

+ Use our streamlined form to report mosquito issues in + under 60 seconds +

+
+ +
+
+
+
+
+
+
+
+ + +
+
+
+
+

© 2023 {{ .DistrictName }}

+
+
+

+ Contact: (555) 123-4567 | info@mosquitodistrict.gov +

+
+
+
+
+{{ end }} diff --git a/html/template/sync/setting-integration.html b/html/template/sync/setting-integration.html new file mode 100644 index 00000000..5b09e10b --- /dev/null +++ b/html/template/sync/setting-integration.html @@ -0,0 +1,367 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+ +
+

Integrations

+
+ + Important: This page allows you to configure + integration with third-party services. The credentials and tokens stored + here provide access to external systems and should be protected. Only + authorized personnel should modify these settings. +
+
+ + +
+
+
+

Frontier Precision's FieldSeeker GIS

+
+ FieldSeeker Logo +
+
+
+ + + + + + + + + + + + + + + + + + + +
OAuth Token Status + + Active + +
Token Expiration26 days remaining (Expires on Dec 31, 2025)
Integration MethodWeb Hooks
Permission LevelRead & Write
+
+
+ + +
+
+
+ + +
+
+
+

VectorSurv

+
+ VectorSurv Logo +
+
+
+ + + + + + + + + + + + + + + +
API Token + vs_9f72b5e3******************************c11d +
Last SynchronizationDecember 5, 2025 at 08:34 AM (2 days ago)
Synchronization Status + + Active + (Scheduled daily at 2:00 AM) + +
+
+
+ + +
+
+
+ + +
+
+
+

VeeMac

+
+ VeeMac Logo +
+
+
+ + + + + + + + + + + + + + + + + + + +
Usernamemosquito_district21
Password••••••••••••
Last SynchronizationDecember 6, 2025 at 11:15 PM (Yesterday)
Synchronization Status + + Inactive (Manual + sync only) + +
+
+
+ + +
+
+
+
+ + + + + + +{{ end }} diff --git a/html/template/sync/setting-mock.html b/html/template/sync/setting-mock.html new file mode 100644 index 00000000..a29bbb32 --- /dev/null +++ b/html/template/sync/setting-mock.html @@ -0,0 +1,218 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+
+
+

Settings

+

+ Configure your organization's preferences and integrations +

+
+
+ +
+ +
+
+
+
+ +
+

User Management

+

+ Manage staff accounts, roles, and permissions for your + organization. +

+
+ + Manage Users + + + 23 users +
+
+
+
+ + +
+
+
+
+ +
+

Pesticide Products

+

+ Configure products, application rates, and field recommendations. +

+
+ + Manage Products + + + 12 active products +
+
+
+
+ + +
+
+
+
+ +
+

Integrations

+

+ Configure connections with FieldSeeker, VectorSurv, and other + services. +

+
+ + Manage Integrations + + + 3 active connections +
+
+
+
+ + +
+
+
+
+ +
+

Equipment

+

+ Manage your field equipment inventory, calibration, and + maintenance. +

+
+ + Manage Equipment + + + Updated 5 days ago +
+
+
+
+ + +
+
+
+
+ +
+

Notifications

+

+ Configure email alerts, SMS notifications, and reporting + preferences. +

+
+ + Manage Notifications + + + 5 active alerts +
+
+
+
+ + +
+
+
+
+ +
+

General Settings

+

+ Configure organization details, branding, and system preferences. +

+
+ + Manage Settings + + + Updated yesterday +
+
+
+
+
+ +
+

+ + All changes made in settings are logged for audit purposes +

+
+
+{{ end }} diff --git a/html/template/sync/setting-pesticide-add.html b/html/template/sync/setting-pesticide-add.html new file mode 100644 index 00000000..f35683f6 --- /dev/null +++ b/html/template/sync/setting-pesticide-add.html @@ -0,0 +1,253 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+ + + + +
+
+ +
+
+

VectoMax FG

+

+ Biological larvicide granules combining Bacillus thuringiensis + subspecies israelensis and Bacillus sphaericus for extended + residual control of mosquito larvae. +

+
+ + Enabled + +
+ + +
+

General Information

+
+
+
Formulation
+
Granule
+
+
+
EPA Registration Number
+
73049-429
+
+
+
Active Ingredients
+
+ Bacillus thuringiensis subspecies israelensis (2.7%)
+ Bacillus sphaericus (4.5%) +
+
+
+
Biological Targeting
+
+ I1 + I2 + I3 + I4 + P +
+
+
+
Application Rates
+
+ Low: 5 lbs/acre
+ High: 20 lbs/acre +
+
+
+
Residual
+
Up to 30 days (environmental conditions dependent)
+
+
+
+ + +
+
+
+ +
+
+
Key Usage Notes
+

+ Apply evenly across water surface. Use higher rate when L4 + present or when organic load is high. Avoid application in ponds + with fish unless approved by a supervisor. +

+
+
+
+ + +
+

PPE Requirements

+
+ + Gloves + + + Eye Protection + + + Respirator (Optional) + +
+
+ + +
+

Equipment Supported

+
+ + Backpack Spreader + + + Hand Spreader + + + Truck Granule Unit + +
+
+ + +
+

Suitability

+
+
+
Pools
+
Recommended
+
+
+
Vegetation
+
OK
+
+
+
High Organics
+
OK
+
+
+
Organic Crop Restriction
+
None
+
+
+
+ + +
+ + +
+
+
+
+{{ end }} diff --git a/html/template/sync/setting-pesticide.html b/html/template/sync/setting-pesticide.html new file mode 100644 index 00000000..150f8d4e --- /dev/null +++ b/html/template/sync/setting-pesticide.html @@ -0,0 +1,293 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+
+

Pesticide Products Configuration

+ + Add New Product + +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ProductFormulationTargetsResidual (days)Low RateMax RatePoolsInfoActions
BVA OilLiquid + I1 + I2 + I3 + I4 + P + 10.5 gal/acre5 gal/acreRecommended + + + + + + +
VectoMax FGGranule + I1 + I2 + I3 + I4 + P + 305 lbs/acre20 lbs/acreRecommended + + + + + + +
CensorLiquid + I1 + I2 + I3 + I4 + P + 210.75 gal/acre2.5 gal/acreAllowed + + + + + + +
AquaBac XTLiquid + I1 + I2 + I3 + I4 + P + 140.25 gal/acre2 gal/acreProhibited + + + + + + +
Natular G30Granule + I1 + I2 + I3 + I4 + P + 305 lbs/acre12 lbs/acreDiscouraged + + + + + + +
+
+
+
+
+{{ end }} diff --git a/html/template/sync/setting-user-add.html b/html/template/sync/setting-user-add.html new file mode 100644 index 00000000..f1710458 --- /dev/null +++ b/html/template/sync/setting-user-add.html @@ -0,0 +1,161 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+
+
+
+
+
+

Add New User

+ + Back to Users + +
+
+
+
+ +
+ + +
+ Please provide the user's full name. +
+
+ + +
+ + +
+ Please provide a valid email address. +
+
+ An invitation will be sent to this email address. +
+
+ + +
+ + +
Please provide a username.
+
+ Username must be unique and contain only letters, numbers, and + underscores. +
+
+ +
+ +
+ + +
Please select a role.
+
+ + +
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+ + Cancel + + +
+
+
+
+
+
+
+{{ end }} diff --git a/html/template/sync/setting-user.html b/html/template/sync/setting-user.html new file mode 100644 index 00000000..206d238b --- /dev/null +++ b/html/template/sync/setting-user.html @@ -0,0 +1,168 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+
+

User Management

+ + Add New User + +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Full NameEmail AddressUsernameRoleServe WarrantsStatusLast LoginActions
John Doejohn.doe@example.comjohndoe + + +
+ +
+
+ Active + 2023-06-15 09:45 AM + +
Jane Smithjane.smith@example.comjanesmith + + +
+ +
+
+ Active + 2023-06-17 14:20 PM + +
Robert Johnsonrobert.j@example.comrobertj + + +
+ +
+
+ Deactivated + 2023-06-10 11:30 AM + +
Emily Wilsonemily.w@example.comemilyw + + +
+ +
+
+ Invited + Never + +
+
+
+
+
+{{ end }} diff --git a/html/template/sync/settings.html b/html/template/sync/settings.html new file mode 100644 index 00000000..4308af37 --- /dev/null +++ b/html/template/sync/settings.html @@ -0,0 +1,8 @@ +{{ template "sync/authenticated.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} +{{ end }} +{{ define "content" }} +

Imagine settings here

+{{ end }} diff --git a/html/template/sync/signin.html b/html/template/sync/signin.html new file mode 100644 index 00000000..b58dcd99 --- /dev/null +++ b/html/template/sync/signin.html @@ -0,0 +1,105 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Login{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+ +
+{{ end }} diff --git a/html/template/sync/signup.html b/html/template/sync/signup.html new file mode 100644 index 00000000..91c4512a --- /dev/null +++ b/html/template/sync/signup.html @@ -0,0 +1,131 @@ +{{ template "sync/base.html" . }} + +{{ define "title" }}Login{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+
+
+ +
+
+

Create an Account

+

Join us today to get started

+
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+

Already have an account? Sign in

+
+
+
+ + +
+
+
+ +
+ +
+
Who should register?
+

+ This platform is designed for professionals who need to manage + projects and collaborate with team members. Whether you're a + freelancer, small business owner, or part of a larger + organization, our tools can help streamline your workflow. +

+
+ +
+
What happens after registration?
+

+ After you register with your email, you'll receive a + confirmation message with instructions to complete your account + setup. You'll then have access to all features and can customize + your workspace based on your specific needs. +

+
+ +
+ For any questions about account types or registration, please + contact our support team at support@yourproduct.com +
+
+
+
+
+
+{{ end }} diff --git a/html/template/sync/source.html b/html/template/sync/source.html new file mode 100644 index 00000000..ab9d9227 --- /dev/null +++ b/html/template/sync/source.html @@ -0,0 +1,367 @@ +{{ template "sync/authenticated.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + {{ template "map" .MapData }} + +{{ end }} +{{ define "content" }} +
+ +
+
+

Breeding Source Detail

+
+
+ +
+
+
+
+
+
Source ID: {{ .Source.GlobalID }}
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Access:{{ .Source.AccessDescription }}
Address:Not implemented
Comments:{{ .Source.Comments }}
Deactivate Reason:{{ .Source.DeactivateReason }}
Description:{{ .Source.Description }}
Habitat:{{ .Source.Habitat }}
Jurisdiction:{{ .Source.Jurisdiction }}
Location Number:{{ .Source.LocationNumber }}
Name:{{ .Source.Name }}
Status + {{ if .Source.Active }} + Active + {{ else }} + Inactive + {{ end }} +
Priority:{{ .Source.Priority }} ({{ .Source.ScalarPriority }})
S Type:{{ .Source.SourceType }}
Source Status:{{ .Source.SourceStatus }}
Symbology:{{ .Source.Symbology }}
Use Type:{{ .Source.UseType }}
Water Origin:{{ .Source.WaterOrigin }}
Zone:{{ .Source.Zone }}.{{ .Source.Zone2 }}
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Creation date{{ .Source.Created|timeSincePtr }}
Edit date{{ .Source.EditedAt|timeSincePtr }}
Larva Inspect Interval{{ .Source.LarvaeInspectInterval }}
Last Inspect Activity{{ .Source.LastInspectionActivity }}
Last Inspect Avg Larva{{ .Source.LastInspectionAverageLarvae }}
Last Inspect Avg Pupae{{ .Source.LastInspectionAveragePupae }}
Last Inspect Breeding{{ .Source.LastInspectionBreeding }}
Last Inspect Conditions{{ .Source.LastInspectionConditions }}
Last Inspect Date{{ .Source.LastInspectionDate|timeSincePtr }}
Last Inspect Species{{ .Source.LastInspectionFieldSpecies }}
Last Inspect Life Stages{{ .Source.LastInspectionLifeStages }}
Last Treat Activity{{ .Source.LastTreatmentActivity }}
Last Treat Date{{ .Source.LastTreatmentDate|timeSincePtr }}
Last Treat Product{{ .Source.LastTreatmentProduct }}
Last Treat Quantity{{ .Source.LastTreatmentQuantity }}
Last Treat Quantity Unit{{ .Source.LastTreatmentQuantityUnit }}
Next action date scheduled:{{ .Source.NextActionScheduledDate|timeSincePtr }}
Treatment Cadence:Not implemented
+
+
+
+
+
+ + +
+
+
+
+
+
+
+ + +
+ +
+ +

Treatment History

+
+
+
+ + + + + + + + + {{ range .TreatmentModels }} + + + + + + + {{ end }} + +
YearStartEndInterval
{{ .Year }}{{ .SeasonStart|timeAsRelativeDate }}{{ .SeasonEnd|timeAsRelativeDate }}{{ .Interval|timeInterval }}
+ + + + + + + + + + + {{ range .Treatments }} + + + + + + + {{ end }} + +
Treatment DateInsecticide UsedCadence DeltaTechnician Notes
{{ .Date|timeSincePtr }}{{ .Product }} + {{ .CadenceDelta|timeDelta }} + {{ .Notes }}
+
+
+
+
+ + +
+ +

Inspection History

+
+
+
+ + + + + + + + + + {{ range .Inspections }} + + + + + + {{ end }} + +
Inspection DateActionNotes
{{ .Date|timeSincePtr }}{{ .Action }}{{ .Notes }}
+
+
+
+
+
+
+
+

Nearby Mosquito Traps

+ {{ range .Traps }} +
+ + + + + + + + + + + +
Trap ID:{{ .ID }}
Distance{{ .Distance }}
+
+
+ + + + + + + + + + + + {{ range .Counts }} + + + + + + + {{ end }} + +
Collection DateFemale CountMale CountTotal Count
{{ .Ended|timeSincePtr }}{{ .Females }}{{ .Males }}{{ .Total }}
+
+ {{ end }} +
+
+
+{{ end }} diff --git a/html/template/sync/template-test.html b/html/template/sync/template-test.html new file mode 100644 index 00000000..ba19b8f0 --- /dev/null +++ b/html/template/sync/template-test.html @@ -0,0 +1,3 @@ + +

Hi Eli

+ diff --git a/html/template/sync/text-messages.html b/html/template/sync/text-messages.html new file mode 100644 index 00000000..7d5202c8 --- /dev/null +++ b/html/template/sync/text-messages.html @@ -0,0 +1,181 @@ +{{ template "sync/authenticated.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + +{{ end }} +{{ define "content" }} +
+ +
+
+
+
+
+ User avatar +
+
+
Chat with Sarah Johnson
+ Last active 5 minutes ago +
+
+
+
+
+ + +
+
+
+
+ +
+ Today, 2:30 PM +
+ + +
+ Receiver avatar +
+
+ Hi there! How's the project coming along? +
+
2:31 PM
+
+
+ + +
+ Sender avatar +
+
+ Hey! It's going pretty well. I'm working on the UI mockups + right now. +
+
2:33 PM
+
+
+ + +
+ Receiver avatar +
+
+ That's great to hear! When do you think you'll be able to + share them with the team? +
+
2:35 PM
+
+
+ + +
+ Sender avatar +
+
+ I'm hoping to have something ready by tomorrow afternoon. I'm + just working out some details with the responsive design. +
+
2:36 PM
+
+
+ + +
+ Sender avatar +
+
+ Do you have any specific feedback on the initial concept I + shared last week? +
+
2:37 PM
+
+
+ + +
+ Receiver avatar +
+
+ Yes! The team loved it. The color scheme was particularly well + received. We just had some minor suggestions about the + navigation that I can share during our next call. +
+
2:40 PM
+
+
+ + +
+ Sender avatar +
+
+ That sounds great! Looking forward to the feedback. +
+
2:41 PM
+
+
+
+
+
+
+
+{{ end }} diff --git a/html/template/sync/trap.html b/html/template/sync/trap.html new file mode 100644 index 00000000..4cf5cee9 --- /dev/null +++ b/html/template/sync/trap.html @@ -0,0 +1,131 @@ +{{ template "sync/authenticated.html" . }} + +{{ define "title" }}Dash{{ end }} +{{ define "extraheader" }} + {{ template "map" .MapData }} + +{{ end }} +{{ define "content" }} +
+ +
+
+

Trap Detail

+
+
+ +
+
+
+
+
+
Trap ID: {{ .Trap.GlobalID }}
+ + + + + + + + + + + + + +
Active:{{ .Trap.Active }}
Comments:{{ .Trap.Comments }}
Description:{{ .Trap.Description }}
+
+
+
+
+
+ + +
+
+
+
+
+
+
+ +
+
+

Trap Collections

+
+ + + + + + + + + + + + + {{ range .Trap.Collections }} + + + + + + + + {{ end }} + +
Collection DateCollection IDFemalesMaleTotal
{{ .EndDateTime|timeSincePtr }}{{ .GlobalID }}{{ .Count.Females }}{{ .Count.Males }}{{ .Count.Total }}
+
+
+
+
+{{ end }} diff --git a/main.go b/main.go index 6211f76d..99d5ebc8 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,7 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/comms/email" "github.com/Gleipnir-Technology/nidus-sync/config" "github.com/Gleipnir-Technology/nidus-sync/db" + "github.com/Gleipnir-Technology/nidus-sync/html" "github.com/Gleipnir-Technology/nidus-sync/llm" "github.com/Gleipnir-Technology/nidus-sync/platform/text" "github.com/Gleipnir-Technology/nidus-sync/rmo" @@ -75,16 +76,21 @@ func main() { os.Exit(3) } + err = html.LoadTemplates() + if err != nil { + log.Error().Err(err).Msg("Failed to load html templates") + os.Exit(4) + } err = email.LoadTemplates() if err != nil { log.Error().Err(err).Msg("Failed to load email templates") - os.Exit(4) + os.Exit(5) } err = text.StoreSources() if err != nil { log.Error().Err(err).Msg("Failed to store text source phone numbers") - os.Exit(5) + os.Exit(6) } router_logger := log.With().Logger() @@ -121,7 +127,7 @@ func main() { err = llm.CreateOpenAIClient(ctx, &openai_logger) if err != nil { log.Error().Err(err).Msg("Failed to start openAI client") - os.Exit(6) + os.Exit(7) } background.Start(ctx) server := &http.Server{ diff --git a/rmo/district.go b/rmo/district.go index e33ddf9d..25ce2cbe 100644 --- a/rmo/district.go +++ b/rmo/district.go @@ -22,10 +22,6 @@ type ContentDistrictList struct { URL ContentURL } -var ( - DistrictListT = buildTemplate("district-list", "base") -) - func districtBySlug(r *http.Request) (*models.Organization, error) { slug := chi.URLParam(r, "slug") district, err := models.Organizations.Query( @@ -49,7 +45,7 @@ func getDistrictList(w http.ResponseWriter, r *http.Request) { } html.RenderOrError( w, - DistrictListT, + "rmo/district-list.html", ContentDistrictList{ Districts: districts, URL: makeContentURL(nil), diff --git a/rmo/email.go b/rmo/email.go index 769ea767..0b879ad6 100644 --- a/rmo/email.go +++ b/rmo/email.go @@ -15,13 +15,6 @@ type ContentEmail struct { Email string } -var ( - EmailConfirmT = buildTemplate("email-confirm", "base") - EmailConfirmCompleteT = buildTemplate("email-confirm-complete", "base") - EmailUnsubscribeT = buildTemplate("email-unsubscribe", "base") - EmailUnsubscribeCompleteT = buildTemplate("email-unsubscribe-complete", "base") -) - func getEmailByCode(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "code") //id := r.FormValue("id") @@ -48,7 +41,7 @@ func getEmailReportUnsubscribe(w http.ResponseWriter, r *http.Request) { email := r.FormValue("email") html.RenderOrError( w, - EmailConfirmT, + "rmo/email-unsubscribe.html", ContentEmail{ Email: email, }, @@ -63,7 +56,7 @@ func getEmailConfirm(w http.ResponseWriter, r *http.Request) { html.RenderOrError( w, - EmailConfirmT, + "rmo/email-confirm.html", ContentEmail{ Email: email, }, @@ -72,7 +65,7 @@ func getEmailConfirm(w http.ResponseWriter, r *http.Request) { func getEmailConfirmComplete(w http.ResponseWriter, r *http.Request) { html.RenderOrError( w, - EmailConfirmCompleteT, + "rmo/email-confirm-complete.html", map[string]string{}, ) } @@ -80,7 +73,7 @@ func getEmailUnsubscribe(w http.ResponseWriter, r *http.Request) { email := r.FormValue("email") html.RenderOrError( w, - EmailUnsubscribeT, + "rmo/email-unsubscribe.html", ContentEmail{ Email: email, }, @@ -89,7 +82,7 @@ func getEmailUnsubscribe(w http.ResponseWriter, r *http.Request) { func getEmailUnsubscribeComplete(w http.ResponseWriter, r *http.Request) { html.RenderOrError( w, - EmailUnsubscribeCompleteT, + "rmo/email-unsubscribe-complete.html", map[string]string{}, ) } diff --git a/rmo/mock.go b/rmo/mock.go index 80518b23..fb69e55d 100644 --- a/rmo/mock.go +++ b/rmo/mock.go @@ -8,14 +8,6 @@ import ( "github.com/go-chi/chi/v5" ) -var ( - mockDistrictRootT = buildTemplate("mock/district-root", "base") - mockNuisanceT = buildTemplate("mock/nuisance", "base") - mockNuisanceSubmitCompleteT = buildTemplate("mock/nuisance-submit-complete", "base") - mockRootT = buildTemplate("mock/root", "base") - mockWaterT = buildTemplate("mock/water", "base") -) - type ContentMock struct { District ContentDistrict MapboxToken string @@ -24,13 +16,13 @@ type ContentMock struct { } func addMockRoutes(r chi.Router) { - r.Get("/", renderMock(mockRootT)) - r.Get("/district/{slug}", renderMock(mockDistrictRootT)) - r.Get("/district/{slug}/nuisance", renderMock(mockNuisanceT)) - r.Get("/district/{slug}/nuisance-submit-complete", renderMock(mockNuisanceSubmitCompleteT)) - r.Get("/district/{slug}/water", renderMock(mockWaterT)) - r.Get("/nuisance", renderMock(mockNuisanceT)) - r.Get("/nuisance-submit-complete", renderMock(mockNuisanceSubmitCompleteT)) + r.Get("/", renderMock("rmo/mock/root.html")) + r.Get("/district/{slug}", renderMock("rmo/mock/district-root.html")) + r.Get("/district/{slug}/nuisance", renderMock("rmo/mock/nuisance.html")) + r.Get("/district/{slug}/nuisance-submit-complete", renderMock("rmo/mock/nuisance-submit-complete.html")) + r.Get("/district/{slug}/water", renderMock("rmo/mock/water.html")) + r.Get("/nuisance", renderMock("rmo/mock/nuisance.html")) + r.Get("/nuisance-submit-complete", renderMock("rmo/mock/nuisance-submit-complete.html")) } func makeContentURLMock(slug string) ContentURL { @@ -44,7 +36,7 @@ func makeContentURLMock(slug string) ContentURL { func makeURLMock(slug, p string) string { return config.MakeURLReport("/mock/district/%s/%s", slug, p) } -func renderMock(t *html.BuiltTemplate) func(http.ResponseWriter, *http.Request) { +func renderMock(t string) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { slug := chi.URLParam(r, "slug") if slug == "" { diff --git a/rmo/notification.go b/rmo/notification.go index 97e1a79d..9027db1d 100644 --- a/rmo/notification.go +++ b/rmo/notification.go @@ -10,10 +10,6 @@ import ( "github.com/rs/zerolog/log" ) -var ( - registerNotificationsCompleteT = buildTemplate("register-notifications-complete", "base") -) - func postRegisterNotifications(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { diff --git a/rmo/nuisance.go b/rmo/nuisance.go index b106f45f..a6f85b56 100644 --- a/rmo/nuisance.go +++ b/rmo/nuisance.go @@ -33,15 +33,10 @@ type ContentNuisanceSubmitComplete struct { URL ContentURL } -var ( - NuisanceT = buildTemplate("nuisance", "base") - SubmitCompleteT = buildTemplate("submit-complete", "base") -) - func getNuisance(w http.ResponseWriter, r *http.Request) { html.RenderOrError( w, - NuisanceT, + "rmo/nuisance.html", ContentNuisance{ District: nil, MapboxToken: config.MapboxToken, @@ -57,7 +52,7 @@ func getNuisanceDistrict(w http.ResponseWriter, r *http.Request) { } html.RenderOrError( w, - NuisanceT, + "rmo/nuisance.html", ContentNuisance{ District: newContentDistrict(district), MapboxToken: config.MapboxToken, @@ -74,7 +69,7 @@ func getSubmitComplete(w http.ResponseWriter, r *http.Request) { } html.RenderOrError( w, - SubmitCompleteT, + "rmo/submit-complete.html", ContentNuisanceSubmitComplete{ District: newContentDistrict(district), ReportID: report_id, diff --git a/rmo/quick.go b/rmo/quick.go index dab8a744..c14f01aa 100644 --- a/rmo/quick.go +++ b/rmo/quick.go @@ -36,15 +36,10 @@ type District struct { Name string } -var ( - quickT = buildTemplate("quick", "base") - quickSubmitCompleteT = buildTemplate("quick-submit-complete", "base") -) - func getQuick(w http.ResponseWriter, r *http.Request) { html.RenderOrError( w, - quickT, + "rmo/quick.html", ContentQuick{}, ) } @@ -80,7 +75,7 @@ func getQuickSubmitComplete(w http.ResponseWriter, r *http.Request) { } html.RenderOrError( w, - quickSubmitCompleteT, + "rmo/quick-submit-complete.html", ContentQuickSubmitComplete{ District: district, ReportID: report.PublicID, @@ -91,7 +86,7 @@ func getRegisterNotificationsComplete(w http.ResponseWriter, r *http.Request) { report := r.URL.Query().Get("report") html.RenderOrError( w, - registerNotificationsCompleteT, + "rmo/register-notificition-complete.html", ContentRegisterNotificationsComplete{ ReportID: report, }, diff --git a/rmo/root.go b/rmo/root.go index 38097fc7..407b0782 100644 --- a/rmo/root.go +++ b/rmo/root.go @@ -30,12 +30,6 @@ type ContentURL struct { WaterSubmit string } -var ( - PrivacyT = buildTemplate("privacy", "base") - RootT = buildTemplate("root", "base") - TermsT = buildTemplate("terms", "base") -) - func boolFromForm(r *http.Request, k string) bool { s := r.PostFormValue(k) if s == "on" { @@ -47,7 +41,7 @@ func boolFromForm(r *http.Request, k string) bool { func getPrivacy(w http.ResponseWriter, r *http.Request) { html.RenderOrError( w, - PrivacyT, + "rmo/privacy.html", ContentPrivacy{ Address: "2726 S Quinn Ave, Gilbert, AZ, USA", Company: "Gleipnir LLC", @@ -59,7 +53,7 @@ func getPrivacy(w http.ResponseWriter, r *http.Request) { func getRoot(w http.ResponseWriter, r *http.Request) { html.RenderOrError( w, - RootT, + "rmo/root.html", ContentRoot{ URL: makeContentURL(nil), }, @@ -73,7 +67,7 @@ func getRootDistrict(w http.ResponseWriter, r *http.Request) { } html.RenderOrError( w, - RootT, + "rmo/root.html", ContentRoot{ District: newContentDistrict(district), URL: makeContentURL(district), @@ -88,7 +82,7 @@ func getRobots(w http.ResponseWriter, r *http.Request) { func getTerms(w http.ResponseWriter, r *http.Request) { html.RenderOrError( w, - TermsT, + "rmo/terms.html", ContentRoot{ URL: makeContentURL(nil), }, diff --git a/rmo/search.go b/rmo/search.go index d37dbd3a..c9c708d8 100644 --- a/rmo/search.go +++ b/rmo/search.go @@ -12,14 +12,10 @@ type ContentSearch struct { URLTegola string } -var ( - Search = buildTemplate("search", "base") -) - func getSearch(w http.ResponseWriter, r *http.Request) { html.RenderOrError( w, - Search, + "rmo/search.html", ContentSearch{ MapboxToken: config.MapboxToken, URLTegola: config.MakeURLTegola("/"), diff --git a/rmo/status.go b/rmo/status.go index b1b4e396..d13cf2c0 100644 --- a/rmo/status.go +++ b/rmo/status.go @@ -61,11 +61,6 @@ type TimelineEntry struct { Title string } -var ( - Status = buildTemplate("status", "base") - StatusByID = buildTemplate("status-by-id", "base") -) - func formatReportID(s string) string { // truncate down if too long if len(s) > 12 { @@ -95,14 +90,14 @@ func getStatus(w http.ResponseWriter, r *http.Request) { URL: makeContentURL(nil), } if report_id_str == "" { - html.RenderOrError(w, Status, content) + html.RenderOrError(w, "rmo/status.html", content) return } report_id := sanitizeReportID(report_id_str) report_id_str = formatReportID(report_id) //some_report, e := report.FindSomeReport(r.Context(), report_id) content.Error = "Sorry, we can't find that report" - html.RenderOrError(w, Status, content) + html.RenderOrError(w, "rmo/status.html", content) } func contentFromNuisance(ctx context.Context, report_id string) (result ContentStatusByID, err error) { nuisance, err := models.PublicreportNuisances.Query( @@ -333,7 +328,7 @@ func getStatusByID(w http.ResponseWriter, r *http.Request) { content.URL = makeContentURL(nil) html.RenderOrError( w, - StatusByID, + "rmo/status-by-id.html", content, ) } diff --git a/rmo/template.go b/rmo/template.go deleted file mode 100644 index 470c2596..00000000 --- a/rmo/template.go +++ /dev/null @@ -1,25 +0,0 @@ -package rmo - -import ( - "embed" - "fmt" - - "github.com/Gleipnir-Technology/nidus-sync/html" -) - -//go:embed template/* -var embeddedFiles embed.FS - -var components = [...]string{"footer", "header-district", "header-rmo", "photo-upload", "photo-upload-header"} - -func buildTemplate(files ...string) *html.BuiltTemplate { - subdir := "rmo" - full_files := make([]string, 0) - for _, f := range files { - full_files = append(full_files, fmt.Sprintf("%s/template/%s.html", subdir, f)) - } - for _, c := range components { - full_files = append(full_files, fmt.Sprintf("%s/template/component/%s.html", subdir, c)) - } - return html.NewBuiltTemplate(embeddedFiles, "rmo/", full_files...) -} diff --git a/rmo/template/component/header-district.html b/rmo/template/component/header-district.html deleted file mode 100644 index b80301b8..00000000 --- a/rmo/template/component/header-district.html +++ /dev/null @@ -1,13 +0,0 @@ -{{define "header-district"}} - - -{{end}} diff --git a/rmo/template/component/header-rmo.html b/rmo/template/component/header-rmo.html deleted file mode 100644 index dddf7eaf..00000000 --- a/rmo/template/component/header-rmo.html +++ /dev/null @@ -1,12 +0,0 @@ -{{define "header-rmo"}} - - -{{end}} diff --git a/rmo/template/component/map-header.html b/rmo/template/component/map-header.html deleted file mode 100644 index 38a0b080..00000000 --- a/rmo/template/component/map-header.html +++ /dev/null @@ -1,2 +0,0 @@ -{{ define "map-header" }} -{{ end }} diff --git a/rmo/template/district-list.html b/rmo/template/district-list.html deleted file mode 100644 index d53eab39..00000000 --- a/rmo/template/district-list.html +++ /dev/null @@ -1,28 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Districts{{end}} -{{define "extraheader"}} -{{end}} -{{define "content"}} - -
-

District List

- - - - - - - - {{ range .Districts }} - - - - - - {{ end }} - -
LogoNameURL
{{.Name}}{{.URLRMO}}
-
- -{{end}} diff --git a/rmo/template/search.html b/rmo/template/search.html deleted file mode 100644 index 2e9ff32d..00000000 --- a/rmo/template/search.html +++ /dev/null @@ -1,223 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Status{{end}} -{{define "extraheader"}} - - - - - - - - -{{end}} -{{define "content"}} -
- - - - -
-
-
Reports Map
-
-
- -
-
- - -
-
-
Reports Near You
- 15 Reports Found -
-
-
- -
-
- -
- - -
- -
- - -
- -
-
-{{end}} diff --git a/rmo/water.go b/rmo/water.go index 79eeff24..53d2b2e2 100644 --- a/rmo/water.go +++ b/rmo/water.go @@ -24,14 +24,10 @@ type ContentPool struct { URL ContentURL } -var ( - WaterT = buildTemplate("water", "base") -) - func getWater(w http.ResponseWriter, r *http.Request) { html.RenderOrError( w, - WaterT, + "rmo/water.html", ContentPool{ District: nil, MapboxToken: config.MapboxToken, @@ -47,7 +43,7 @@ func getWaterDistrict(w http.ResponseWriter, r *http.Request) { } html.RenderOrError( w, - WaterT, + "rmo/water.html", ContentPool{ District: newContentDistrict(district), MapboxToken: config.MapboxToken, diff --git a/sync/dash.go b/sync/dash.go index 22ef7987..a2a61037 100644 --- a/sync/dash.go +++ b/sync/dash.go @@ -21,15 +21,7 @@ import ( ) // Authenticated pages -var ( - cellT = buildTemplate("cell", "authenticated") - dashboardT = buildTemplate("dashboard", "authenticated") - districtT = buildTemplate("district", "base") - layoutTestT = buildTemplate("layout-test", "authenticated") - settingsT = buildTemplate("settings", "authenticated") - sourceT = buildTemplate("source", "authenticated") - trapT = buildTemplate("trap", "authenticated") -) +var () type Config struct { URLTegola string @@ -97,7 +89,7 @@ func getDistrict(w http.ResponseWriter, r *http.Request) { context := ContextDistrict{ MapboxToken: config.MapboxToken, } - html.RenderOrError(w, districtT, &context) + html.RenderOrError(w, "sync/district.html", &context) } func getLayoutTest(w http.ResponseWriter, r *http.Request, u *models.User) { @@ -106,7 +98,7 @@ func getLayoutTest(w http.ResponseWriter, r *http.Request, u *models.User) { respondError(w, "Failed to get user", err, http.StatusInternalServerError) return } - html.RenderOrError(w, layoutTestT, &ContentLayoutTest{User: userContent}) + html.RenderOrError(w, "sync/layout-test.html", &ContentLayoutTest{User: userContent}) } func getRoot(w http.ResponseWriter, r *http.Request) { @@ -162,6 +154,9 @@ func getSource(w http.ResponseWriter, r *http.Request, u *models.User) { source(w, r, u, globalid) } +func getTemplateTest(w http.ResponseWriter, r *http.Request) { + html.RenderOrError(w, "sync/template-test.html", nil) +} func getTrap(w http.ResponseWriter, r *http.Request, u *models.User) { globalid_s := chi.URLParam(r, "globalid") if globalid_s == "" { @@ -241,7 +236,7 @@ func cell(ctx context.Context, w http.ResponseWriter, user *models.User, c int64 Treatments: treatments, User: userContent, } - html.RenderOrError(w, cellT, &data) + html.RenderOrError(w, "sync/cell.html", &data) } func dashboard(ctx context.Context, w http.ResponseWriter, user *models.User) { @@ -310,7 +305,7 @@ func dashboard(ctx context.Context, w http.ResponseWriter, user *models.User) { RecentRequests: requests, User: userContent, } - html.RenderOrError(w, dashboardT, data) + html.RenderOrError(w, "sync/dashboard.html", data) } func settings(w http.ResponseWriter, r *http.Request, user *models.User) { @@ -322,7 +317,7 @@ func settings(w http.ResponseWriter, r *http.Request, user *models.User) { data := ContentAuthenticatedPlaceholder{ User: userContent, } - html.RenderOrError(w, settingsT, data) + html.RenderOrError(w, "sync/settings.html", data) } func source(w http.ResponseWriter, r *http.Request, user *models.User, id uuid.UUID) { @@ -383,7 +378,7 @@ func source(w http.ResponseWriter, r *http.Request, user *models.User, id uuid.U User: userContent, } - html.RenderOrError(w, sourceT, data) + html.RenderOrError(w, "sync/source.html", data) } func trap(w http.ResponseWriter, r *http.Request, user *models.User, id uuid.UUID) { @@ -423,5 +418,5 @@ func trap(w http.ResponseWriter, r *http.Request, user *models.User, id uuid.UUI User: userContent, } - html.RenderOrError(w, trapT, data) + html.RenderOrError(w, "sync/trap.html", data) } diff --git a/sync/mock.go b/sync/mock.go index e61dd847..a70b4313 100644 --- a/sync/mock.go +++ b/sync/mock.go @@ -8,12 +8,12 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/config" "github.com/Gleipnir-Technology/nidus-sync/html" "github.com/go-chi/chi/v5" - "github.com/rs/zerolog/log" + //"github.com/rs/zerolog/log" "github.com/skip2/go-qrcode" ) // Unauthenticated pages -var ( +/* admin = buildTemplate("admin", "base") dataEntry = buildTemplate("data-entry", "base") dataEntryGood = buildTemplate("data-entry-good", "base") @@ -42,7 +42,7 @@ var ( settingPesticideAdd = buildTemplate("setting-pesticide-add", "base") settingUsers = buildTemplate("setting-user", "base") settingUsersAdd = buildTemplate("setting-user-add", "base") -) +*/ func getQRCodeReport(w http.ResponseWriter, r *http.Request) { code := chi.URLParam(r, "code") @@ -125,13 +125,7 @@ func mock(t string, w http.ResponseWriter, code string) { SettingUserAdd: "/mock/setting/user/add", }, } - template, ok := html.TemplatesByFilename[t+".html"] - if !ok { - log.Error().Str("template", t).Msg("Failed to find template") - respondError(w, "Failed to render template", nil, http.StatusInternalServerError) - return - } - html.RenderOrError(w, &template, data) + html.RenderOrError(w, t, data) } func renderMock(templateName string) http.HandlerFunc { diff --git a/sync/oauth.go b/sync/oauth.go index 7dc9d443..de73b2bb 100644 --- a/sync/oauth.go +++ b/sync/oauth.go @@ -13,10 +13,6 @@ import ( "github.com/rs/zerolog/log" ) -var ( - oauthPromptT = buildTemplate("oauth-prompt", "authenticated") -) - type ContextOauthPrompt struct { User User } @@ -89,5 +85,5 @@ func oauthPrompt(w http.ResponseWriter, r *http.Request, user *models.User) { data := ContextOauthPrompt{ User: userContent, } - html.RenderOrError(w, oauthPromptT, data) + html.RenderOrError(w, "sync/oauth-prompt.html", data) } diff --git a/sync/page.go b/sync/page.go deleted file mode 100644 index 50d812ee..00000000 --- a/sync/page.go +++ /dev/null @@ -1,33 +0,0 @@ -package sync - -import ( - "embed" - "fmt" - "net/http" - - "github.com/Gleipnir-Technology/nidus-sync/html" - "github.com/rs/zerolog/log" -) - -//go:embed template/* -var embeddedFiles embed.FS - -var components = [...]string{"header", "icons", "map", "sidebar"} - -func buildTemplate(files ...string) *html.BuiltTemplate { - subdir := "sync" - full_files := make([]string, 0) - for _, f := range files { - full_files = append(full_files, fmt.Sprintf("%s/template/%s.html", subdir, f)) - } - for _, c := range components { - full_files = append(full_files, fmt.Sprintf("%s/template/components/%s.html", subdir, c)) - } - return html.NewBuiltTemplate(embeddedFiles, "sync/", full_files...) -} - -// Respond with an error that is visible to the user -func respondError(w http.ResponseWriter, m string, e error, s int) { - log.Warn().Int("status", s).Err(e).Str("user message", m).Msg("Responding with an error from sync pages") - http.Error(w, m, s) -} diff --git a/sync/privacy.go b/sync/privacy.go index a1e98279..63dcb0e4 100644 --- a/sync/privacy.go +++ b/sync/privacy.go @@ -14,14 +14,10 @@ type ContentPrivacy struct { URLSync string } -var ( - PrivacyT = buildTemplate("privacy", "base") -) - func getPrivacy(w http.ResponseWriter, r *http.Request) { html.RenderOrError( w, - PrivacyT, + "sync/privacy.html", ContentPrivacy{ Address: "2726 S Quinn Ave, Gilbert, AZ, USA", Company: "Gleipnir LLC", diff --git a/sync/routes.go b/sync/routes.go index 86c14bb9..954e25bf 100644 --- a/sync/routes.go +++ b/sync/routes.go @@ -17,35 +17,35 @@ func Router() chi.Router { r.Get("/arcgis/oauth/callback", getArcgisOauthCallback) r.Get("/district", getDistrict) - 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("/mock/setting", renderMock("setting-mock")) - r.Get("/mock/setting/integration", renderMock("setting-integration")) - r.Get("/mock/setting/pesticide", renderMock("setting-pesticide")) - r.Get("/mock/setting/pesticide/add", renderMock("setting-pesticide-add")) - r.Get("/mock/setting/user", renderMock("setting-user")) - r.Get("/mock/setting/user/add", renderMock("setting-user-add")) + r.Get("/mock", renderMock("mock-root.html")) + r.Get("/mock/admin", renderMock("admin.html")) + r.Get("/mock/admin/service-request", renderMock("admin-service-request.html")) + r.Get("/mock/data-entry", renderMock("data-entry.html")) + r.Get("/mock/data-entry/bad", renderMock("data-entry-bad.html")) + r.Get("/mock/data-entry/good", renderMock("data-entry-good.html")) + r.Get("/mock/dispatch", renderMock("dispatch.html")) + r.Get("/mock/dispatch-results", renderMock("dispatch-results.html")) + r.Get("/mock/report", renderMock("report.html")) + r.Get("/mock/report/{code}", renderMock("report-detail.html")) + r.Get("/mock/report/{code}/confirm", renderMock("report-confirmation.html")) + r.Get("/mock/report/{code}/contribute", renderMock("report-contribute.html")) + r.Get("/mock/report/{code}/evidence", renderMock("report-evidence.html")) + r.Get("/mock/report/{code}/schedule", renderMock("report-schedule.html")) + r.Get("/mock/report/{code}/update", renderMock("report-update.html")) + r.Get("/mock/service-request", renderMock("service-request.html")) + r.Get("/mock/service-request/{code}", renderMock("service-request-detail.html")) + r.Get("/mock/service-request-location", renderMock("service-request-location.html")) + r.Get("/mock/service-request-mosquito", renderMock("service-request-mosquito.html")) + r.Get("/mock/service-request-pool", renderMock("service-request-pool.html")) + r.Get("/mock/service-request-quick", renderMock("service-request-quick.html")) + r.Get("/mock/service-request-quick-confirmation", renderMock("service-request-quick-confirmation.html")) + r.Get("/mock/service-request-updates", renderMock("service-request-updates.html")) + r.Get("/mock/setting", renderMock("setting-mock.html")) + r.Get("/mock/setting/integration", renderMock("setting-integration.html")) + r.Get("/mock/setting/pesticide", renderMock("setting-pesticide.html")) + r.Get("/mock/setting/pesticide/add", renderMock("setting-pesticide-add.html")) + r.Get("/mock/setting/user", renderMock("setting-user.html")) + r.Get("/mock/setting/user/add", renderMock("setting-user-add.html")) r.Get("/oauth/refresh", getOAuthRefresh) @@ -56,6 +56,7 @@ func Router() chi.Router { r.Post("/signin", postSignin) r.Get("/signup", getSignup) r.Post("/signup", postSignup) + r.Get("/template-test", getTemplateTest) // Authenticated endpoints r.Route("/api", api.AddRoutes) diff --git a/sync/signin.go b/sync/signin.go index 40c91861..157ebfb7 100644 --- a/sync/signin.go +++ b/sync/signin.go @@ -11,11 +11,6 @@ import ( "github.com/rs/zerolog/log" ) -var ( - signinT = buildTemplate("signin", "base") - signupT = buildTemplate("signup", "base") -) - func getSignin(w http.ResponseWriter, r *http.Request) { errorCode := r.URL.Query().Get("error") signin(w, errorCode) @@ -92,10 +87,10 @@ func signin(w http.ResponseWriter, errorCode string) { data := ContentSignin{ InvalidCredentials: errorCode == "invalid-credentials", } - html.RenderOrError(w, signinT, data) + html.RenderOrError(w, "sync/signin.html", data) } func signup(w http.ResponseWriter, path string) { data := ContentSignup{} - html.RenderOrError(w, signupT, data) + html.RenderOrError(w, "sync/signup.html", data) } diff --git a/sync/template.go b/sync/template.go new file mode 100644 index 00000000..441ac448 --- /dev/null +++ b/sync/template.go @@ -0,0 +1,13 @@ +package sync + +import ( + "net/http" + + "github.com/rs/zerolog/log" +) + +// Respond with an error that is visible to the user +func respondError(w http.ResponseWriter, m string, e error, s int) { + log.Warn().Int("status", s).Err(e).Str("user message", m).Msg("Responding with an error from sync pages") + http.Error(w, m, s) +} diff --git a/sync/template/admin.html b/sync/template/admin.html deleted file mode 100644 index 5f9e17b2..00000000 --- a/sync/template/admin.html +++ /dev/null @@ -1,412 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} - - - -
-
-
-
-
-
-
- - -
- Quick search for request ID, address, coordinates, contact name, or phone number -
-
-
-
-
- -
- -
-
-
-
Mosquito Activity & Relief
- New Service Request -
-
-
-
- -
-
- -

Interactive map will be loaded here

-
-
-
-
- -
-
-
Recent Trap Counts
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Trap LocationCurrentWeek ΔMonth ΔYoY
Elmwood Park47-12%-23%+5%
Riverside Dr32+8%-5%-10%
Oakdale Creek53+15%+22%+17%
-
-
- -
-
Nearby Service Requests
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DateStatusTypeDistance
10/15/23CompletedGreen Pool0.2 mi
10/18/23ScheduledMosquito Nuisance0.3 mi
10/19/23AcceptedPrevious Source0.5 mi
-
-
-
-
-
-
- - -
-
-
-
Calendar
-
-
-
-
- -
October 2023
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SuMoTuWeThFrSa
1234567
891011121314
15161718192021
22232425262728
2930311234
-
- Light - Medium - Heavy -
-
- -
Today's Schedule - October 23, 2023
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TimeAddressTypeTechnician
8:00 AM123 Maple StNuisanceS. Johnson
9:30 AM456 Oak AveGreen PoolM. Williams
11:00 AM789 Pine LnPrev SourceL. Rodriguez
-
-
-
-
-
- - -
-
-
-
-
Today's Technician Roster
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TechnicianScheduledCompletedPhoneStatusLocation
-
- Photo - Sarah Johnson -
-
85(555) 234-5678Servicing123 Maple St, Zone 3
-
- Photo - Mark Williams -
-
73(555) 345-6789On BreakOffice - Lunchroom
-
- Photo - Lisa Rodriguez -
-
96(555) 456-7890In TransitEn route to 789 Pine Ln
-
- Photo - Carlos Martinez -
-
64(555) 567-8901Servicing202 Birch Dr, Zone 2
-
-
-
-
-
-
-{{end}} diff --git a/sync/template/authenticated.html b/sync/template/authenticated.html deleted file mode 100644 index ba12bf54..00000000 --- a/sync/template/authenticated.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - {{template "title" .}} - Nidus Sync - - - - - - - - - {{block "extraheader" .}} {{end}} - - -{{template "icons"}} -
- {{if .User}} - {{template "sidebar" .User}} - {{end}} -
- -{{template "content" .}} - - - - diff --git a/sync/template/base.html b/sync/template/base.html deleted file mode 100644 index d73e39a4..00000000 --- a/sync/template/base.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - {{template "title" .}} - Nidus Sync - - - - - - - - - {{block "extraheader" .}} {{end}} - - -{{template "content" .}} - - - diff --git a/sync/template/cell.html b/sync/template/cell.html deleted file mode 100644 index 8dd86ca5..00000000 --- a/sync/template/cell.html +++ /dev/null @@ -1,240 +0,0 @@ -{{template "authenticated.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} -{{template "map" .MapData}} - -{{end}} -{{define "content"}} -
- -
-
-

Location Data View

-
-
- - -
-
-
-
-
-
-
-
-
-
Approximate Address:
-

123 Main St, Anytown, ST 12345

- -
- -
Cell Coordinates (Hexagon):
-
- - - {{ range $i, $cb := .CellBoundary }} - - - - - {{end}} - -
Vertex {{$i}}:{{$cb|latLngDisplay}}
-
-
-

{{.CellBoundary|GISStatement}}

-
-
-
-
- - -
- -
- -

Mosquito Breeding Sources

-
-
-
- - - - - - - - - - - {{ range .BreedingSources }} - - - - - - - {{ end }} - -
IDSource TypeLast InspectedLast Treated
{{.ID|uuidShort}}{{.Type}}{{.LastInspected|timeSincePtr}}{{.LastTreated|timeSincePtr}}
-
- -
-
- - -

Inspections History

-
-
-
- - - - - - - - - - - - {{ range .Inspections }} - - - - - - - - {{ end }} - -
LocationIDLocationDateActionNotes
{{.LocationID|uuidShort}}{{.Location}}{{.Date|timeSincePtr}}{{.Action}}{{.Notes}}
-
- - -
-
-
- - -
-

Traps

- {{ if gt (len .Traps) 0}} -
-
-
- - - - - - - - - - {{ range .Traps }} - - - - - - {{ end }} - -
IDActiveComments
{{.GlobalID|uuidShort}}{{.Active}}{{.Comments}}
-
-
-
- {{ else }} -

No traps

- {{ end }} - - -

Treatment History

-
-
-
- - - - - - - - - - - {{ range .Treatments }} - - - - - - - {{ end }} - -
LocationTreatment DateInsecticide UsedTechnician Notes
{{.LocationID|uuidShort}}{{.Date|timeSincePtr}}{{.Product}}{{.Notes}}
-
- - -
-
-
-
-
-{{end}} diff --git a/sync/template/components/header.html b/sync/template/components/header.html deleted file mode 100644 index 20967b2b..00000000 --- a/sync/template/components/header.html +++ /dev/null @@ -1,80 +0,0 @@ -{{define "header"}} - -{{end}} diff --git a/sync/template/components/icons.html b/sync/template/components/icons.html deleted file mode 100644 index 424e1ccc..00000000 --- a/sync/template/components/icons.html +++ /dev/null @@ -1,60 +0,0 @@ -{{define "icons"}} - - - Bootstrap - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{{end}} diff --git a/sync/template/components/sidebar.html b/sync/template/components/sidebar.html deleted file mode 100644 index 8a24ae3b..00000000 --- a/sync/template/components/sidebar.html +++ /dev/null @@ -1,57 +0,0 @@ -{{define "sidebar"}} - -{{end}} diff --git a/sync/template/dashboard.html b/sync/template/dashboard.html deleted file mode 100644 index f7880123..00000000 --- a/sync/template/dashboard.html +++ /dev/null @@ -1,225 +0,0 @@ -{{template "authenticated.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - - - - - -{{end}} -{{define "content"}} -
- -
-
-

{{ .User.Organization.Name }} Dashboard

-

Overview of mosquito control activities in your district

-
-
- {{ if .IsSyncOngoing }} -

- Syncing now... -

- {{ else }} -

- Last updated: {{ .LastSync | timeSincePtr }} - -

- {{ end }} -
-
- - -
- -
-
-
-
- -
-
Last Data Refresh
-

{{ .LastSync | timeSincePtr }}

- -
-
-
- - -
-
-
-
- -
-
Service Requests
- {{ if .IsSyncOngoing }} -

{{ .CountServiceRequests | bigNumber }}...?

- {{ else }} -

{{ .CountServiceRequests | bigNumber }}

- {{ end }} - -
-
-
- - -
-
-
-
- -
-
Mosquito Sources
- {{ if .IsSyncOngoing }} -

{{ .CountMosquitoSources | bigNumber }}..?

- {{ else }} -

{{ .CountMosquitoSources | bigNumber }}

- {{ end }} - -
-
-
- - -
-
-
-
- -
-
Traps
- {{ if .IsSyncOngoing }} -

{{ .CountTraps | bigNumber }}...?

- {{ else }} -

{{ .CountTraps | bigNumber }}

- {{ end }} - -
-
-
-
- - -

Mosquito Activity Heatmap

-
-
- -
-
- - -

Recent Activity

-
-
-
-
-
- - - - - - - - - - - - {{ range $i, $sr := .RecentRequests }} - - - - - - - - {{ end }} - -
DateTypeLocationStatusAction
{{ $sr.Date | timeSincePtr }}Service Request{{ $sr.Location }}CompletedView
-
-
-
-
-
-
-{{end}} diff --git a/sync/template/data-entry-bad.html b/sync/template/data-entry-bad.html deleted file mode 100644 index 271c9a43..00000000 --- a/sync/template/data-entry-bad.html +++ /dev/null @@ -1,200 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Data Entry{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
-
-

Upload Failed: pools-data-2023.csv

- - Validation Errors - -
- - - -
-
-
Error Summary
- - Download Error Log - -
-
-

We found 12 errors in your CSV file. The most common issues are:

-
    -
  • Missing required column: The "Latitude" column is not present in your file
  • -
  • Invalid data format: 8 GPS coordinates contain non-numeric values
  • -
  • Empty required fields: 3 records are missing Plat ID values
  • -
- - -
-
- -
-
-
Detailed Error Report
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Line NumberErrorSuggestion
1 - MISSING_COLUMN
- Required column "Latitude" is missing from the header row -
- Add a "Latitude" column to your CSV file. Make sure the spelling and capitalization match exactly. -
5 - INVALID_DATA_FORMAT
- GPS coordinate "37.45N" is not a valid decimal number -
- Change "37.45N" to a decimal format (e.g., "37.45"). Remove any non-numeric characters except for the decimal point. -
8 - EMPTY_REQUIRED_FIELD
- Plat ID is empty or missing -
- Add a Plat ID value for this record. Each pool must have a unique identifier. -
12 - INVALID_DATA_FORMAT
- GPS coordinate "unknown" is not a valid decimal number -
- Replace "unknown" with the actual longitude value in decimal format (e.g., "-122.4194"). -
17 - INVALID_DATA_FORMAT
- GPS coordinate "N/A" is not a valid decimal number -
- Replace "N/A" with the actual latitude value in decimal format (e.g., "37.7749"). -
21 - EMPTY_REQUIRED_FIELD
- Plat ID is empty or missing -
- Add a Plat ID value for this record. Each pool must have a unique identifier. -
-
- -
- -
-
-
Next Steps
-
-
-
    -
  1. Download the error log for a complete list of issues (optional)
  2. -
  3. Fix the errors in your CSV file
  4. -
  5. Re-upload the corrected file using the button below
  6. -
- - - -
- -
-
-
- - -
-{{end}} diff --git a/sync/template/data-entry-good.html b/sync/template/data-entry-good.html deleted file mode 100644 index 52329322..00000000 --- a/sync/template/data-entry-good.html +++ /dev/null @@ -1,212 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Data Entry{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
-
-

Upload Results: pools-data-2023.csv

- - File Parsed Successfully - -
- -
-
-
-
-

45

-
Existing Pools
-

Matches found in previous records

-
-
-
-
-
-
-

23

-
New Pools
-

Not found in existing records

-
-
-
-
-
-
-

4

-
Outside District
-

Potential geocoding errors

-
-
-
-
- -
-
-
Data Preview
-
- - -
-
-
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Plat IDLatitudeLongitudeStreet AddressStatusIn District
P1234537.7749-122.4194123 Main St, Anytown, CAExisting Yes
P2345637.3352-121.8811456 Oak Ave, Someville, CAExisting Yes
P3456738.5816-121.4944789 Pine Rd, Outtown, CANew - - No - Outside northern boundary -
P4567837.4419-122.1430101 Elm St, Cityville, CANew Yes
P5678937.3541-121.9552202 Maple Dr, Townburg, CAExisting Yes
P6789035.3733-119.0187303 Cedar Ln, Farville, CANew - - No - Outside southern boundary -
P7890137.8044-122.2712404 Birch Ave, Metroburg, CAExisting Yes
P8901237.4032-123.9612505 Walnut St, Edgetown, CANew - - No - Outside western boundary -
-
- - -
-
- -
-
-
Notes & Recommendations
-
-
-
-

Issues detected:

-
    -
  • 4 pools appear to be outside district boundaries (possible geocoding errors)
  • -
  • All required fields are present and properly formatted
  • -
-
- - -
-
- -
- - Upload Edited File - - -
-
-{{end}} diff --git a/sync/template/data-entry.html b/sync/template/data-entry.html deleted file mode 100644 index 48432b72..00000000 --- a/sync/template/data-entry.html +++ /dev/null @@ -1,114 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Data Entry{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
-

Upload Pool Data

- -
-
-
CSV Upload Requirements
-
-
-

Your CSV file must contain the following columns in any order. Please ensure your data matches the required format.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldDescriptionFormatRequired
LatitudeGPS latitude coordinateDecimal (e.g., 37.7749)Yes
LongitudeGPS longitude coordinateDecimal (e.g., -122.4194)Yes
Plat IDUnique identifier for the propertyAlphanumeric (e.g., P12345)Yes
Street AddressNearest street address to the poolText (e.g., 123 Main St)No
- -
- Need a template? Download sample CSV file -
-
-
- -
-
-
Upload Data
-
-
-
- - - - -
Select your CSV file
-

Drag and drop a file here or click to browse

- -
- - -
-
- -
- Need assistance? Contact support@example.com -
-
-{{end}} diff --git a/sync/template/dispatch-results.html b/sync/template/dispatch-results.html deleted file mode 100644 index f9189ee4..00000000 --- a/sync/template/dispatch-results.html +++ /dev/null @@ -1,260 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Data Entry{{end}} -{{define "extraheader"}} - - -{{end}} -{{define "content"}} -
-

Route Calculation Results

- - Edit Parameters - -
- - -
-
-

Route Map

-
-
-
-
- -

Interactive Map View

-

Routes are color-coded by technician assignment

-
-
-
-
- - -
-
- -
-

Coverage Projection

-

If every day were like today, all pools would be complete on October 27, 2023

-
-
-
- - -
-
-

Route Summary

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SelectRouteTechnicianCold Call PoolsDrone InspectionsService CallsWarrantsEst. TimeActions
-
- -
-
-
A
-
-
- John Davis - John Davis -
-
120526h 15m - -
-
- -
-
-
B
-
-
- Sarah Johnson - Sarah Johnson -
-
83417h 30m - -
-
- -
-
-
C
-
-
- Michael Chen - Michael Chen -
-
104307h 45m - -
-
- -
-
-
D
-
-
- Jessica Martinez - Jessica Martinez -
-
142638h 00m - -
-
-
-
- - - -
- - - -{{end}} diff --git a/sync/template/dispatch.html b/sync/template/dispatch.html deleted file mode 100644 index 75053f86..00000000 --- a/sync/template/dispatch.html +++ /dev/null @@ -1,172 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Data Entry{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
-

Technician Routing & Dispatch

- -
-
-

Technician Roster

-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TechnicianWorking HoursTruck AssignmentWarrant ServiceDrone CertifiedRouting Options
-
- John Davis -
-
John Davis
-
ID: T-1001
-
-
-
7:00 AM - 3:30 PMTruck #103YesNo -
- - -
-
- - -
-
- - -
-
-
- Sarah Johnson -
-
Sarah Johnson
-
ID: T-1042
-
-
-
8:00 AM - 4:30 PMTruck #118YesYes -
- - -
-
- - -
-
- - -
-
-
- Michael Chen -
-
Michael Chen
-
ID: T-1019
-
-
-
6:30 AM - 3:00 PMTruck #107NoYes -
- - -
-
- - -
-
- - -
-
-
- Jessica Martinez -
-
Jessica Martinez
-
ID: T-1055
-
-
-
7:30 AM - 4:00 PMTruck #112YesYes -
- - -
-
- - -
-
- - -
-
-
-
-
- - -
-{{end}} diff --git a/sync/template/empty-auth.html b/sync/template/empty-auth.html deleted file mode 100644 index 086e408f..00000000 --- a/sync/template/empty-auth.html +++ /dev/null @@ -1,7 +0,0 @@ -{{template "authenticated.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} -{{end}} -{{define "content"}} -{{end}} diff --git a/sync/template/empty.html b/sync/template/empty.html deleted file mode 100644 index b419f57d..00000000 --- a/sync/template/empty.html +++ /dev/null @@ -1,7 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} -{{end}} -{{define "content"}} -{{end}} diff --git a/sync/template/layout-test.html b/sync/template/layout-test.html deleted file mode 100644 index f5894c89..00000000 --- a/sync/template/layout-test.html +++ /dev/null @@ -1,77 +0,0 @@ -{{template "authenticated.html" . }} -{{define "title"}}Layout Test{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} - -
-
-
-
- - - - - - - - - - -
- -
-
-
-
Card 1
-

Some example content for the first card.

-
-
-
-
-
-
-
Card 2
-

Some example content for the second card.

-
-
-
-
-
-
-
-
Primary
-
Primary-100
-
Primary-200
-
Primary-300
-
-
-
Secondary
-
-
-
Success
-
-
-
-
-
Danger
-
-
-
Warning
-
-
-
Info
-
-
-
-{{end}} diff --git a/sync/template/mock-root.html b/sync/template/mock-root.html deleted file mode 100644 index fcda80dd..00000000 --- a/sync/template/mock-root.html +++ /dev/null @@ -1,56 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Data Entry{{end}} -{{define "extraheader"}} -{{end}} -{{define "content"}} -
-

Mock Listing

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LinkNameDescription
/mock/adminAdminUsed by office admins to handle phone calls and other public-facing responsibilities
/mock/flyover-data-entryFlyover Data EntryUsed to upload CSV files with information about problematic pools
/mock/dispatchDispatchUsed each day to calculate the working routes for technicians
/mock/reportReporting OverviewShows examples of text message contents, printable QR codes, and email bodies for sending to the public to help them self-report
/mock/report/abc-123Self-ReportA page for members of the public to report a green pool.
/mock/service-requestService RequestA page for members of the public to make a direct service request
/mock/settingSettingsA page for management to control the behavior of Nidus
-
-{{end}} diff --git a/sync/template/oauth-prompt.html b/sync/template/oauth-prompt.html deleted file mode 100644 index 0bc6a645..00000000 --- a/sync/template/oauth-prompt.html +++ /dev/null @@ -1,87 +0,0 @@ -{{template "authenticated.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
-
-
-
-

Connect Your ArcGIS Account

-

Link your data to get started

-
- -
-

To provide you with the best experience, we need to connect to your ArcGIS account. This allows us to securely access and visualize your spatial data within our platform.

- -
-

What to expect:

- -
-
1. Secure Authentication
-

When you click the "Connect to ArcGIS" button below, you'll be redirected to the official ArcGIS login page. This connection is secure and uses OAuth 2.0 protocol.

-
- -
-
2. Grant Permissions
-

After logging in with your ArcGIS credentials, you'll be asked to approve permissions for our application to access your data. We only request access to what's needed for the platform to function.

-
- -
-
3. Return to Platform
-

Once authentication is complete, you'll be automatically redirected back to our platform where your data will be available to work with.

-
-
- -
- Note: You'll need an active ArcGIS Online account or ArcGIS Enterprise account to proceed. If you don't have one, you can create an ArcGIS account here. -
- -

By connecting your ArcGIS account, you'll be able to:

-
    -
  • Access and visualize your spatial data
  • -
  • Perform advanced analysis using our integrated tools
  • -
  • Share results with team members securely
  • -
  • Keep your data synchronized across platforms
  • -
- -
- - Connect to ArcGIS - -

You can disconnect your account at any time in settings

-
-
-
-
-
-{{end}} diff --git a/sync/template/privacy.html b/sync/template/privacy.html deleted file mode 100644 index 4070ab89..00000000 --- a/sync/template/privacy.html +++ /dev/null @@ -1,214 +0,0 @@ -{{template "base.html" .}} -{{define "title"}}Privacy Policy{{end}} -{{define "extraheader"}} -{{end}} -{{define "content"}} -
-

Privacy Policy

-

Last updated: January 20, 2026

-

This Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your information when You use the Service and tells You about Your privacy rights and how the law protects You.

-

We use Your Personal Data to provide and improve the Service. By using the Service, You agree to the collection and use of information in accordance with this Privacy Policy.

-

Interpretation and Definitions

-

Interpretation

-

The words whose initial letters are capitalized have meanings defined under the following conditions. The following definitions shall have the same meaning regardless of whether they appear in singular or in plural.

-

Definitions

-

For the purposes of this Privacy Policy:

- -

Collecting and Using Your Personal Data

-

Types of Data Collected

-

Personal Data

-

While using Our Service, We may ask You to provide Us with certain personally identifiable information that can be used to contact or identify You. Personally identifiable information may include, but is not limited to:

- -

Usage Data

-

Usage Data is collected automatically when using the Service.

-

Usage Data may include information such as Your Device's Internet Protocol address (e.g. IP address), browser type, browser version, the pages of our Service that You visit, the time and date of Your visit, the time spent on those pages, unique device identifiers and other diagnostic data.

-

When You access the Service by or through a mobile device, We may collect certain information automatically, including, but not limited to, the type of mobile device You use, Your mobile device's unique ID, the IP address of Your mobile device, Your mobile operating system, the type of mobile Internet browser You use, unique device identifiers and other diagnostic data.

-

We may also collect information that Your browser sends whenever You visit Our Service or when You access the Service by or through a mobile device.

-

Tracking Technologies and Cookies

-

We use Cookies and similar tracking technologies to track the activity on Our Service and store certain information. Tracking technologies We use include beacons, tags, and scripts to collect and track information and to improve and analyze Our Service. The technologies We use may include:

- -

Cookies can be "Persistent" or "Session" Cookies. Persistent Cookies remain on Your personal computer or mobile device when You go offline, while Session Cookies are deleted as soon as You close Your web browser.

-

Where required by law, we use non-essential cookies (such as analytics, advertising, and remarketing cookies) only with Your consent. You can withdraw or change Your consent at any time using Our cookie preferences tool (if available) or through Your browser/device settings. Withdrawing consent does not affect the lawfulness of processing based on consent before its withdrawal.

-

We use both Session and Persistent Cookies for the purposes set out below:

- -

For more information about the cookies we use and your choices regarding cookies, please visit our Cookies Policy or the Cookies section of Our Privacy Policy.

-

Use of Your Personal Data

-

The Company may use Personal Data for the following purposes:

- -

We may share Your Personal Data in the following situations:

- -

Retention of Your Personal Data

-

The Company will retain Your Personal Data only for as long as is necessary for the purposes set out in this Privacy Policy. We will retain and use Your Personal Data to the extent necessary to comply with our legal obligations (for example, if We are required to retain Your data to comply with applicable laws), resolve disputes, and enforce our legal agreements and policies.

-

Where possible, We apply shorter retention periods and/or reduce identifiability by deleting, aggregating, or anonymizing data. Unless otherwise stated, the retention periods below are maximum periods ("up to") and We may delete or anonymize data sooner when it is no longer needed for the relevant purpose. We apply different retention periods to different categories of Personal Data based on the purpose of processing and legal obligations:

- -

Usage Data is retained in accordance with the retention periods described above, and may be retained longer only where necessary for security, fraud prevention, or legal compliance.

-

We may retain Personal Data beyond the periods stated above for different reasons:

- -

You may request information about how long We will retain Your Personal Data by contacting Us.

-

When retention periods expire, We securely delete or anonymize Personal Data according to the following procedures:

- -

Transfer of Your Personal Data

-

Your information, including Personal Data, is processed at the Company's operating offices and in any other places where the parties involved in the processing are located. It means that this information may be transferred to — and maintained on — computers located outside of Your state, province, country or other governmental jurisdiction where the data protection laws may differ from those from Your jurisdiction.

-

Where required by applicable law, We will ensure that international transfers of Your Personal Data are subject to appropriate safeguards and supplementary measures where appropriate. The Company will take all steps reasonably necessary to ensure that Your data is treated securely and in accordance with this Privacy Policy and no transfer of Your Personal Data will take place to an organization or a country unless there are adequate controls in place including the security of Your data and other personal information.

-

Delete Your Personal Data

-

You have the right to delete or request that We assist in deleting the Personal Data that We have collected about You.

-

Our Service may give You the ability to delete certain information about You from within the Service.

-

You may update, amend, or delete Your information at any time by signing in to Your Account, if you have one, and visiting the account settings section that allows you to manage Your personal information. You may also contact Us to request access to, correct, or delete any Personal Data that You have provided to Us.

-

Please note, however, that We may need to retain certain information when we have a legal obligation or lawful basis to do so.

-

Disclosure of Your Personal Data

-

Business Transactions

-

If the Company is involved in a merger, acquisition or asset sale, Your Personal Data may be transferred. We will provide notice before Your Personal Data is transferred and becomes subject to a different Privacy Policy.

-

Law enforcement

-

Under certain circumstances, the Company may be required to disclose Your Personal Data if required to do so by law or in response to valid requests by public authorities (e.g. a court or a government agency).

-

Other legal requirements

-

The Company may disclose Your Personal Data in the good faith belief that such action is necessary to:

- -

Security of Your Personal Data

-

The security of Your Personal Data is important to Us, but remember that no method of transmission over the Internet, or method of electronic storage is 100% secure. While We strive to use commercially reasonable means to protect Your Personal Data, We cannot guarantee its absolute security.

-

Children's Privacy

-

Our Service does not address anyone under the age of 16. We do not knowingly collect personally identifiable information from anyone under the age of 16. If You are a parent or guardian and You are aware that Your child has provided Us with Personal Data, please contact Us. If We become aware that We have collected Personal Data from anyone under the age of 16 without verification of parental consent, We take steps to remove that information from Our servers.

-

If We need to rely on consent as a legal basis for processing Your information and Your country requires consent from a parent, We may require Your parent's consent before We collect and use that information.

-

Links to Other Websites

-

Our Service may contain links to other websites that are not operated by Us. If You click on a third party link, You will be directed to that third party's site. We strongly advise You to review the Privacy Policy of every site You visit.

-

We have no control over and assume no responsibility for the content, privacy policies or practices of any third party sites or services.

-

Changes to this Privacy Policy

-

We may update Our Privacy Policy from time to time. We will notify You of any changes by posting the new Privacy Policy on this page.

-

We will let You know via email and/or a prominent notice on Our Service, prior to the change becoming effective and update the "Last updated" date at the top of this Privacy Policy.

-

You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are effective when they are posted on this page.

-

Contact Us

-

If you have any questions about this Privacy Policy, You can contact us:

- -
-{{end}} diff --git a/sync/template/report-confirmation.html b/sync/template/report-confirmation.html deleted file mode 100644 index 1f089b1b..00000000 --- a/sync/template/report-confirmation.html +++ /dev/null @@ -1,216 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Login{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
- -
- County Vector Control -
- - -
- - - - -
-

Thank You for Your Submission!

-

Your green pool report has been successfully submitted.

-
- - -
-
Appointment Confirmed
-

Our inspector will visit your property at the scheduled time:

- -
-
-
Date
-
Thursday, June 22, 2023
-
-
-
Time
-
10:00 AM
-
-
-
Confirmation #
-
GP-23685
-
-
-
- - -
-
What Happens Next?
-
    -
  • A confirmation email has been sent to the email address you provided.
  • -
  • You'll receive a reminder notification 24 hours before your scheduled appointment.
  • -
  • Our team will review your report and contact you by the next business day if any additional information is needed.
  • -
  • During the scheduled visit, our inspector will assess the pool condition and discuss treatment options if necessary.
  • -
-

You can use the link below to track your report status and view the photos you've submitted.

-
- - - -
- Track Your Report Status -

View photos and check for updates

-
- -
- - -
-
Questions or Concerns?
-

If you have any questions about your report or need to change your appointment, please contact us:

- -
- -
- (555) 123-4567 -
Monday-Friday, 8:00 AM - 5:00 PM
-
-
- -
- -
- greenpool@vectorcontrol.county.gov -
-
- -

Please include your confirmation number (GP-23685) in all correspondence.

-
- - -
- -
-
- - -
-

Thank you for helping keep our community safe from mosquito-borne diseases.

-
-
-{{end}} diff --git a/sync/template/report-contribute.html b/sync/template/report-contribute.html deleted file mode 100644 index afcf31c9..00000000 --- a/sync/template/report-contribute.html +++ /dev/null @@ -1,239 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Login{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
- -
- County Vector Control -
- - -
-
- Upload Photos - 3 of 4 -
-
-
-
-
- - -
-

Upload Current Pool Photos

-

Please provide current photos of your pool to help us assess its condition.

- - -
-
Photo Tips
-
    -
  • Take photos from as high an angle as possible (second story window, deck, etc.)
  • -
  • Try to capture the entire pool in your photo
  • -
  • Ensure photos are clear and well-lit
  • -
  • You can add multiple photos from different angles
  • -
-
- - -
Photo Examples:
-
-
- Good photo example -
Good: High angle, full view
-
- -
- Poor photo example -
Poor: Ground level, partial view
-
-
- - -
-
- -
-
Add Pool Photos
-

Take a new photo or upload from your device

- -
- - -
- - -
- - -
-
Uploaded Photos (2)
-
-
- Uploaded pool photo - -
-
- Uploaded pool photo - -
-
-
- - -
- You can add up to 5 photos to provide a complete view of your pool area. We recommend taking photos from multiple angles. -
- - - -
- - -
-

If you need assistance, please contact Vector Control at (555) 123-4567

-
-
-{{end}} diff --git a/sync/template/report-detail.html b/sync/template/report-detail.html deleted file mode 100644 index 54354538..00000000 --- a/sync/template/report-detail.html +++ /dev/null @@ -1,132 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Login{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
- -
- County Vector Control -
- - -
-
- Location - 1 of 4 -
-
-
-
-
- - -
-

Confirm Property Location

- - -
-
-
- -
-
- - -
-
Detected Address:
-
123 Maple Street, Riverside, CA 92501
-
- -
-

Is this the correct location of the property in question?

-
- - - -
- - -
-

If you need assistance, please contact Vector Control at (555) 123-4567

-
-
-{{end}} diff --git a/sync/template/report-evidence.html b/sync/template/report-evidence.html deleted file mode 100644 index 619d17f4..00000000 --- a/sync/template/report-evidence.html +++ /dev/null @@ -1,243 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Login{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
- -
- County Vector Control -
- - -
-
- Evidence - 2 of 4 -
-
-
-
-
- - -
-

Evidence of Potential Breeding Site

- - -
-
- Aerial Surveillance Photos -
-

These photos were taken during routine aerial surveillance of the area.

- - -
- - -
-
- Historical Inspection Data -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DateInspectorFindingsAction
Mar 15, 2023J. MartinezPool water stagnant, greenTreatment applied, owner notified
Nov 02, 2022L. JohnsonPool water clear, maintainedNo action needed
Aug 18, 2022S. WilliamsMinor algae formationOwner provided maintenance resources
-
-
- - -
-
- Mosquito Trap Count Data -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Date CollectedCountDistanceLevel
Jun 12, 2023420.3 milesHigh
Jun 05, 2023360.3 milesHigh
May 29, 2023280.3 milesMedium
May 22, 2023150.3 milesLow
May 15, 2023120.3 milesLow
-
-
- - -
-
Why This Matters
-

The data above shows mosquito activity in your area. Recent trap counts indicate elevated mosquito populations, which increases the risk of mosquito-borne diseases like West Nile virus.

-

Unmaintained swimming pools can produce thousands of mosquitoes each week. By addressing potential breeding sites, you're helping protect your family and neighbors from these health risks.

-

We need your help to ensure we maintain public health by keeping mosquito counts low in your neighborhood. Your cooperation makes a significant difference in community safety.

-
- - - -
- - -
-

If you need assistance, please contact Vector Control at (555) 123-4567

-
-
-{{end}} diff --git a/sync/template/report-schedule.html b/sync/template/report-schedule.html deleted file mode 100644 index 61e3324f..00000000 --- a/sync/template/report-schedule.html +++ /dev/null @@ -1,319 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Login{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
- -
- County Vector Control -
- - -
-
- Schedule Follow-up - 4 of 4 -
-
-
-
-
- - -
-

Schedule a Follow-up Inspection

-

Please select a convenient date and time for our inspector to visit your property.

- - -
-
- -
Select Date
-
- - -
-
-
Jun
-
20
-
Tue
-
-
-
Jun
-
21
-
Wed
-
-
-
Jun
-
22
-
Thu
-
-
-
Jun
-
23
-
Fri
-
-
-
Jun
-
26
-
Mon
-
-
-
Jun
-
27
-
Tue
-
-
-
Jun
-
28
-
Wed
-
-
-
Jun
-
29
-
Thu
-
-
-
Jun
-
30
-
Fri
-
-
-
Jul
-
03
-
Mon
-
-
- - -
- -
Select Time
-
- -
-
- 8:00 AM -
-
- 9:00 AM -
-
- 10:00 AM -
-
- 11:00 AM -
-
- 1:00 PM -
-
- 2:00 PM -
-
- 3:00 PM -
-
- 4:00 PM -
-
- - -
-
- Selected Appointment: - Thursday, June 22, 2023 at 10:00 AM -
-
-
- - -
-
- -
Contact Information
-
- -
-
-
- - -
- -
- - -
- -
- - -
- -
- - -
- -
-
- - -
-
-
- - -
- Our inspector will need access to view your pool area. If you won't be home during the appointment, please provide access instructions in the notes. -
- - - -
-
-
- - -
-

If you need assistance, please contact Vector Control at (555) 123-4567

-
-
-{{end}} diff --git a/sync/template/report-update.html b/sync/template/report-update.html deleted file mode 100644 index f7714674..00000000 --- a/sync/template/report-update.html +++ /dev/null @@ -1,196 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Login{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
- -
- County Vector Control -
- - -
-

Update Property Location

- - -
-
Two Ways to Update Location
-

You can update the property location by either clicking on the map or entering an address below. Both methods will automatically update each other.

-
- - -
-
Option 1: Select Location on Map
-
-
-
- -
-
- Click or tap anywhere on the map to set the location -
-
-
- - -
OR
- - -
-
Option 2: Enter Address
-
-
- - -
- -
-
- - -
- -
- - -
- -
- - -
-
-
-
- - -
-
- Current Coordinates: - 33.9806° N, 117.3755° W -
-
- - -
- - -
-
- - -
-

If you need assistance, please contact Vector Control at (555) 123-4567

-
-
-{{end}} diff --git a/sync/template/report.html b/sync/template/report.html deleted file mode 100644 index 99f3d410..00000000 --- a/sync/template/report.html +++ /dev/null @@ -1,224 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Login{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
-
-
-
-

Green Pool Reporting

-

Entry Points Diagnostic Page

-
- -
- - This page demonstrates the various ways customers can access the Green Pool Reporting system. -
- - -
-
- -

Text Message Entry Point

-
- -

Customers will receive the following text message with a link to begin the reporting process:

- -
- Vector Control: We noticed a potential green pool at your property. Please tap the link to report status or schedule inspection: {{ .URLs.ReportDetail }} -
- -
-

SMS Details:

-
    -
  • Sent via automated system after aerial detection
  • -
  • Contains unique tracking link for each property
  • -
  • Customers tap link to open mobile browser
  • -
-
-
- - -
-
- -

Door Hanger QR Code Entry Point

-
- -

Inspectors will leave door hangers with a QR code for properties where no one is home:

- -
-
IMPORTANT NOTICE
-

We visited regarding a potential mosquito breeding site.

- - - -

Scan this code with your phone camera to report your pool status or schedule an inspection.

-

Or visit: {{ .URLs.ReportDetail }}

-
- -
-

Door Hanger Details:

-
    -
  • Physical notices left on the door handle
  • -
  • QR code contains property-specific link
  • -
  • Fallback URL provided for manual entry
  • -
-
-
- - -
-
- -

Email Notification Entry Point

-
- -

Property owners will receive this email as a follow-up to other communication attempts:

- - - -
-

Email Details:

-
    -
  • Sent as follow-up or for property owners with registered email addresses
  • -
  • Contains clear call-to-action button and alternative text link
  • -
  • Explains reason for contact and next steps
  • -
-
-
- - -
-
-
-{{end}} diff --git a/sync/template/service-request-detail.html b/sync/template/service-request-detail.html deleted file mode 100644 index 18ca9363..00000000 --- a/sync/template/service-request-detail.html +++ /dev/null @@ -1,318 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} - -
-
-
-
-

[District Name]

-
-
- -
-
-
-
- - -
-
- -
-
-

Report #MMD-2023-12345

-
-
- - Green Pool - -
-
- - -
-
-
- -
-
Submitted
-
-
-
- -
-
Accepted
-
-
-
- -
-
Scheduled
-
-
-
- -
-
Complete
-
-
- -
- -
-
-
-
Location
-
-
- -
-
- -

Map of Report Location

-
-
- - -
-
Address
-

123 Mosquito Ave, Lakeside, CA 92040

-

- 32.8573° N, 116.9222° W -

-
-
-
-
- - -
-
-
-
Report Details
-
-
-
-
-
Report Source
-

Phone Call

-
-
-
Report Date
-

October 15, 2023 at 2:45 PM

-
-
- -
-
Description
-

I noticed my neighbor's backyard pool has turned green and there's nobody living in the house currently. I'm concerned it might be breeding mosquitoes as I've noticed more of them in my yard recently. The house seems to be vacant for about 3 months now.

-
- -
-
-
Pool Status
-

Stagnant/Green

-
-
-
Scheduled Appointment
-

October 20, 2023, 9:00 AM - 11:00 AM

-
-
- -
-
Contact Information
-
-
-

Reported By: John Smith

-

Phone: (555) 123-4567

-
-
-

Email: john.smith@example.com

-

Preferred Contact: Phone

-
-
-
-
-
-
-
- - -
-
-
-
-
Notes & Updates
- 3 Notes -
-
-
-
Added by System on Oct 15, 2023, 2:45 PM
-
Report created via phone call to district office.
-
-
-
Added by Sarah Johnson (Office Staff) on Oct 16, 2023, 9:30 AM
-
Verified location information. Property appears to be vacant according to county records. Left voicemail with property management company listed in county database.
-
-
-
Added by Mike Davis (Technician) on Oct 18, 2023, 11:15 AM
-
Scheduled inspection for Oct 20. Will need access to backyard. Contacted reporter to confirm they'll be available to provide access information on day of service.
-
-
-
-
-
- - -
-
-
-
-
Next Steps
-

Technician scheduled to inspect the property on October 20, 2023, between 9:00 AM - 11:00 AM. If access to the property is not possible, treatment may be conducted from outside the property or additional follow-up may be required.

-

Note: You will receive a notification when the status of this report changes.

-
-
-
-
- - -
-
-
-
-
Add Information
-
-
-

Do you have additional information about this report? Add it below to update the technician.

-
-
- - -
-
- - -
Provide your phone number if you'd like to be contacted about this update.
-
- -
-
-
-
-
- - - -
-
- - - -{{end}} diff --git a/sync/template/service-request-location.html b/sync/template/service-request-location.html deleted file mode 100644 index 36814e95..00000000 --- a/sync/template/service-request-location.html +++ /dev/null @@ -1,367 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} - -
-
-
-
-

[District Name]

-
-
- -
-
-
-
- - -
-
- -
-
-

Lookup Reports by Location

-

Find reports and mosquito activity in your area

-
-
- - -
-
-
-
-
How to use this tool
-

You can either enter an address in the search box or navigate the map by dragging and zooming to find reports in your area. The table below will update automatically to show reports within the current map view.

-
-
-
-
- - -
-
- -
-
-
Search by Address
-
-
-
-
- -
- - -
-
-
- - -
-
- -
- Currently showing reports within 1 mile of map center -
-
-
-
- -
- -
-
- -

Interactive Map Area

-

The map will display here and allow you to navigate the area

-
- - -
- - - -
- - -
-
Current View
-
Lakeside, CA
-
32.857° N, 116.922° W
-
-
-
-
- - -
-
-
-
-
Reports in This Area
- Showing 12 of 37 total reports -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeLocationSubmittedStatusReport ID
- - - - Green Pool - 123 Mosquito Ave - Oct 15, 2023 -
5 days ago
-
- Scheduled - - MMD-2023-12345 -
- - - - Mosquito Nuisance - 456 Lake Dr - Oct 12, 2023 -
8 days ago
-
- Complete - - MMD-2023-12341 -
- - - - Fish Request - 789 Creek Rd - Oct 18, 2023 -
2 days ago
-
- Acknowledged - - MMD-2023-12350 -
- - - - Mosquito Nuisance - 101 Pond Ln - Sep 25, 2023 -
25 days ago
-
- Complete - - MMD-2023-12289 -
- - - - Green Pool - 202 Highland Ave - Oct 19, 2023 -
1 day ago
-
- Submitted - - MMD-2023-12356 -
- - - - Green Pool - 303 Marsh Way - Aug 15, 2023 -
2 months ago
-
- Complete - - MMD-2023-12056 -
-
-
- -
-
-
- - - -
-
- - - -{{end}} diff --git a/sync/template/service-request-mosquito.html b/sync/template/service-request-mosquito.html deleted file mode 100644 index 2a44adc0..00000000 --- a/sync/template/service-request-mosquito.html +++ /dev/null @@ -1,541 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - - -{{end}} -{{define "content"}} - -
-
-
-
-

[District Name]

-
-
- -
-
-
-
- - -
-
- -
-
-

Report Mosquito Nuisance

-

Help us identify mosquito activity in your area

-
-
- - -
-
- -
-
- - -
- -
-
- -

Mosquito Activity Information

- optional -
-

The time when mosquitoes are active can help us identify the species and likely breeding sources.

- - -
-
- -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
- - -
-
- - -
- - -
- - -
-
-
Minor
- Occasional mosquito -
-
-
Moderate
- Regular presence -
-
-
Severe
- Many mosquitoes -
-
-
- Current selection: 3/5 -
-
-
- - -
-
- - -
-
-
- - -
-
- -

Potential Mosquito Sources

- optional -
-

Have you noticed any of these common mosquito breeding sources in your area?

- -
-
-
Did you know?
-

Mosquitoes can breed in as little as a bottle cap of water! Eliminating standing water is the most effective way to reduce mosquito populations.

-
-
- -
- -
-
-
-
- -
-
Stagnant Water
-

Green pools, ponds, fountains, or birdbaths that aren't maintained

-
- - -
-
-
-
- - -
-
-
-
- -
-
Containers
-

Buckets, planters, toys, tires, or any items that collect rainwater

-
- - -
-
-
-
- - -
-
-
-
- -
-
Roof & Gutters
-

Clogged gutters, flat roofs, or AC units that collect water

-
- - -
-
-
-
-
- - - -
-
- - -
-
-
- - -
-
- -

Inspection Request

-
-

Would you like our technicians to inspect for potential mosquito sources?

- -
-
-
-
Property Inspection
-

Request a technician to inspect your property for mosquito sources. We'll contact you to schedule a convenient time.

-
- - -
-
-
- -
-
-
Neighborhood Inspection
-

Request a general inspection of your neighborhood. We'll survey the area for potential mosquito breeding sources.

-
- - -
-
-
-
- - - -
- - -
-
- -

Location & Contact Information

-
- -
-
- - -
-
- -
-
- - -
-
- - -
-
- - -
We'll use this to send you a confirmation and follow-up information.
-
-
-
- - -
-
- -

Additional Information

- optional -
- -
-
- - -
-
-
- - -
-
-
-

Thank you for reporting this mosquito issue.

-

After submission, you'll receive a confirmation with a report ID and further information.

-
-
- -
-
-
-
- - - -
-
- - - -{{end}} diff --git a/sync/template/service-request-quick-confirmation.html b/sync/template/service-request-quick-confirmation.html deleted file mode 100644 index d9b14980..00000000 --- a/sync/template/service-request-quick-confirmation.html +++ /dev/null @@ -1,115 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} - -
-
-
-
-

[District Name]

-
-
- -
-
-
-
- - -
-
-
-
-
- -
- - - -
- -

Report Received!

- -

Thank you for contributing to the health and well-being of our community.

- -
-

Your mosquito report has been submitted successfully and will be reviewed by our team. Your effort helps us identify problem areas and better manage mosquito populations throughout our district.

-
- -

Report ID: #MM

- - - Return Home - -
-
- - -
-
-
What happens next?
-

Our team reviews all reports daily. Depending on the nature of your report, we may deploy field technicians to assess the area or add it to our scheduled mosquito control activities. For urgent matters, we prioritize responses based on public health risk factors.

-
-
-
-
-
- - - -{{end}} diff --git a/sync/template/service-request-quick.html b/sync/template/service-request-quick.html deleted file mode 100644 index 915035cd..00000000 --- a/sync/template/service-request-quick.html +++ /dev/null @@ -1,154 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} - -
-
-
-
-

[District Name]

-
-
- -
-
-
-
- - -
-
-
-
-
-

Quick Mosquito Report

- - -
- -
- - - - - Your location and current time will be automatically collected with your report. -
- - -
- -
- - - - -
- - -
- Take pictures of the mosquito problem area - - -
- - Preview -
-
-
- - -
- - -
- - - -
-
-
- - - -
-
-
- - - -{{end}} diff --git a/sync/template/service-request-updates.html b/sync/template/service-request-updates.html deleted file mode 100644 index 2762863f..00000000 --- a/sync/template/service-request-updates.html +++ /dev/null @@ -1,162 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} - -
-
-
-
-

{{ .DistrictName }}

-
-
- -
-
-
-
- - -
- -
-
-

Check Status or Follow-up

-
-
- - -
-
-
-
-

- Choose one of the following options to check on mosquito activity or follow up on a previous report. -

-
-
- -
- -
-
-
-
- - - - -
-

Look up by Report ID

-

- If you have a report ID from a previous request, enter it below to view the details and current status. -

- -
-
- - -
Example: MMD-2023-12345
-
- -
-
-
-
- - -
-
-
- - -
-
-
-
- - - - -
-

Look up by Location

-

- Don't have a report ID? You can check mosquito activity and reports in your area by providing your location information. -

-

- This option will guide you through selecting your location to find relevant information about mosquito activity near you. -

- -
-
-
-
- - - -
-
-
- - - -{{end}} diff --git a/sync/template/service-request.html b/sync/template/service-request.html deleted file mode 100644 index 82821f74..00000000 --- a/sync/template/service-request.html +++ /dev/null @@ -1,163 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} - -
-
-
-
-

{{ .DistrictName }}

-
-
- -
-
-
-
- - -
- -
-
-
-
-

Welcome to Our Mosquito Management Services

-

- We are dedicated to protecting public health and improving quality of life by reducing - mosquito populations and the diseases they can carry. Our district provides comprehensive - mosquito surveillance, control, and education services to our community. -

-
-
-
-
- - -
-
-
-
-

On the go?

- Make a Quick Report -

Report mosquito issues in under 60 seconds

-
-
-
-
- - -
-
-

How Can We Help You Today?

-
- -
-
-
-
- - - -
-

Follow-up or Check Status

-

Check on a previous request or view current mosquito activity in your area.

- Get Updates -
-
-
- - -
-
-
-
- - - -
-

Report a Green Pool

-

Report stagnant water sources like abandoned pools that may breed mosquitoes.

- Report Source -
-
-
- - -
-
-
-
- - - -
-

Report Mosquito Nuisance

-

Report areas with high adult mosquito activity causing discomfort or concern.

- Report Problem -
-
-
-
- - -
-
-
-
-
-
-
Need to make a quick report?
-

Use our streamlined form to report mosquito issues in under 60 seconds

-
- -
-
-
-
-
-
-
-
- - - - -{{end}} diff --git a/sync/template/setting-integration.html b/sync/template/setting-integration.html deleted file mode 100644 index 5ffb73eb..00000000 --- a/sync/template/setting-integration.html +++ /dev/null @@ -1,257 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
- -
-

Integrations

-
- - Important: This page allows you to configure integration with third-party services. The credentials and tokens stored here provide access to external systems and should be protected. Only authorized personnel should modify these settings. -
-
- - -
-
-
-

Frontier Precision's FieldSeeker GIS

-
- FieldSeeker Logo -
-
-
- - - - - - - - - - - - - - - - - - - -
OAuth Token Status - - Active - -
Token Expiration26 days remaining (Expires on Dec 31, 2025)
Integration MethodWeb Hooks
Permission LevelRead & Write
-
-
- - -
-
-
- - -
-
-
-

VectorSurv

-
- VectorSurv Logo -
-
-
- - - - - - - - - - - - - - - -
API Token - vs_9f72b5e3******************************c11d -
Last SynchronizationDecember 5, 2025 at 08:34 AM (2 days ago)
Synchronization Status - - Active (Scheduled daily at 2:00 AM) - -
-
-
- - -
-
-
- - -
-
-
-

VeeMac

-
- VeeMac Logo -
-
-
- - - - - - - - - - - - - - - - - - - -
Usernamemosquito_district21
Password••••••••••••
Last SynchronizationDecember 6, 2025 at 11:15 PM (Yesterday)
Synchronization Status - - Inactive (Manual sync only) - -
-
-
- - -
-
-
-
- - - - - - -{{end}} diff --git a/sync/template/setting-mock.html b/sync/template/setting-mock.html deleted file mode 100644 index 82e101b4..00000000 --- a/sync/template/setting-mock.html +++ /dev/null @@ -1,192 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
-
-
-

Settings

-

Configure your organization's preferences and integrations

-
-
- -
- -
-
-
-
- -
-

User Management

-

Manage staff accounts, roles, and permissions for your organization.

-
- - Manage Users - - - 23 users -
-
-
-
- - -
-
-
-
- -
-

Pesticide Products

-

Configure products, application rates, and field recommendations.

-
- - Manage Products - - - 12 active products -
-
-
-
- - -
-
-
-
- -
-

Integrations

-

Configure connections with FieldSeeker, VectorSurv, and other services.

-
- - Manage Integrations - - - 3 active connections -
-
-
-
- - -
-
-
-
- -
-

Equipment

-

Manage your field equipment inventory, calibration, and maintenance.

-
- - Manage Equipment - - - Updated 5 days ago -
-
-
-
- - -
-
-
-
- -
-

Notifications

-

Configure email alerts, SMS notifications, and reporting preferences.

-
- - Manage Notifications - - - 5 active alerts -
-
-
-
- - -
-
-
-
- -
-

General Settings

-

Configure organization details, branding, and system preferences.

-
- - Manage Settings - - - Updated yesterday -
-
-
-
-
- -
-

- - All changes made in settings are logged for audit purposes -

-
-
-{{end}} diff --git a/sync/template/setting-pesticide-add.html b/sync/template/setting-pesticide-add.html deleted file mode 100644 index 6d8d4909..00000000 --- a/sync/template/setting-pesticide-add.html +++ /dev/null @@ -1,231 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
- - - - -
-
- -
-
-

VectoMax FG

-

Biological larvicide granules combining Bacillus thuringiensis subspecies israelensis and Bacillus sphaericus for extended residual control of mosquito larvae.

-
- - Enabled - -
- - -
-

General Information

-
-
-
Formulation
-
Granule
-
-
-
EPA Registration Number
-
73049-429
-
-
-
Active Ingredients
-
Bacillus thuringiensis subspecies israelensis (2.7%)
- Bacillus sphaericus (4.5%)
-
-
-
Biological Targeting
-
- I1 - I2 - I3 - I4 - P -
-
-
-
Application Rates
-
Low: 5 lbs/acre
- High: 20 lbs/acre
-
-
-
Residual
-
Up to 30 days (environmental conditions dependent)
-
-
-
- - -
-
-
- -
-
-
Key Usage Notes
-

Apply evenly across water surface. Use higher rate when L4 present or when organic load is high. Avoid application in ponds with fish unless approved by a supervisor.

-
-
-
- - -
-

PPE Requirements

-
- - Gloves - - - Eye Protection - - - Respirator (Optional) - -
-
- - -
-

Equipment Supported

-
- - Backpack Spreader - - - Hand Spreader - - - Truck Granule Unit - -
-
- - -
-

Suitability

-
-
-
Pools
-
Recommended
-
-
-
Vegetation
-
OK
-
-
-
High Organics
-
OK
-
-
-
Organic Crop Restriction
-
None
-
-
-
- - -
- - -
-
-
-
-{{end}} diff --git a/sync/template/setting-pesticide.html b/sync/template/setting-pesticide.html deleted file mode 100644 index c545ee79..00000000 --- a/sync/template/setting-pesticide.html +++ /dev/null @@ -1,211 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
-
-

Pesticide Products Configuration

- - Add New Product - -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductFormulationTargetsResidual (days)Low RateMax RatePoolsInfoActions
BVA OilLiquid - I1 - I2 - I3 - I4 - P - 10.5 gal/acre5 gal/acreRecommended - - - - - - -
VectoMax FGGranule - I1 - I2 - I3 - I4 - P - 305 lbs/acre20 lbs/acreRecommended - - - - - - -
CensorLiquid - I1 - I2 - I3 - I4 - P - 210.75 gal/acre2.5 gal/acreAllowed - - - - - - -
AquaBac XTLiquid - I1 - I2 - I3 - I4 - P - 140.25 gal/acre2 gal/acreProhibited - - - - - - -
Natular G30Granule - I1 - I2 - I3 - I4 - P - 305 lbs/acre12 lbs/acreDiscouraged - - - - - - -
-
-
-
-
-{{end}} diff --git a/sync/template/setting-user-add.html b/sync/template/setting-user-add.html deleted file mode 100644 index fee9968d..00000000 --- a/sync/template/setting-user-add.html +++ /dev/null @@ -1,123 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
-
-
-
-
-
-

Add New User

- - Back to Users - -
-
-
-
- -
- - -
- Please provide the user's full name. -
-
- - -
- - -
- Please provide a valid email address. -
-
An invitation will be sent to this email address.
-
- - -
- - -
- Please provide a username. -
-
Username must be unique and contain only letters, numbers, and underscores.
-
- -
- -
- - -
- Please select a role. -
-
- - -
- - -
-
- - -
- -
- - -
-
- - -
-
- - -
-
- -
- - -
- - Cancel - - -
-
-
-
-
-
-
-{{end}} diff --git a/sync/template/setting-user.html b/sync/template/setting-user.html deleted file mode 100644 index b5845612..00000000 --- a/sync/template/setting-user.html +++ /dev/null @@ -1,144 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
-
-

User Management

- - Add New User - -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Full NameEmail AddressUsernameRoleServe WarrantsStatusLast LoginActions
John Doejohn.doe@example.comjohndoe - - -
- -
-
Active2023-06-15 09:45 AM - -
Jane Smithjane.smith@example.comjanesmith - - -
- -
-
Active2023-06-17 14:20 PM - -
Robert Johnsonrobert.j@example.comrobertj - - -
- -
-
Deactivated2023-06-10 11:30 AM - -
Emily Wilsonemily.w@example.comemilyw - - -
- -
-
InvitedNever - -
-
-
-
-
-{{end}} diff --git a/sync/template/settings.html b/sync/template/settings.html deleted file mode 100644 index fb7444a8..00000000 --- a/sync/template/settings.html +++ /dev/null @@ -1,8 +0,0 @@ -{{template "authenticated.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} -{{end}} -{{define "content"}} -

Imagine settings here

-{{end}} diff --git a/sync/template/signin.html b/sync/template/signin.html deleted file mode 100644 index 7a042fb7..00000000 --- a/sync/template/signin.html +++ /dev/null @@ -1,90 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Login{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
- -
-{{end}} diff --git a/sync/template/signup.html b/sync/template/signup.html deleted file mode 100644 index 57d2b072..00000000 --- a/sync/template/signup.html +++ /dev/null @@ -1,98 +0,0 @@ -{{template "base.html" .}} - -{{define "title"}}Login{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
-
-
- -
-
-

Create an Account

-

Join us today to get started

-
- -
-
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- -
- -
-

Already have an account? Sign in

-
-
-
- - -
-
-
- -
- -
-
Who should register?
-

This platform is designed for professionals who need to manage projects and collaborate with team members. Whether you're a freelancer, small business owner, or part of a larger organization, our tools can help streamline your workflow.

-
- -
-
What happens after registration?
-

After you register with your email, you'll receive a confirmation message with instructions to complete your account setup. You'll then have access to all features and can customize your workspace based on your specific needs.

-
- -
- For any questions about account types or registration, please contact our support team at support@yourproduct.com -
-
-
-
-
-
-{{end}} diff --git a/sync/template/source.html b/sync/template/source.html deleted file mode 100644 index 15d7976e..00000000 --- a/sync/template/source.html +++ /dev/null @@ -1,364 +0,0 @@ -{{template "authenticated.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} -{{template "map" .MapData}} - -{{end}} -{{define "content"}} -
- -
-
-

Breeding Source Detail

-
-
- -
-
-
-
-
-
Source ID: {{ .Source.GlobalID }}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Access:{{ .Source.AccessDescription }}
Address:Not implemented
Comments:{{ .Source.Comments }}
Deactivate Reason:{{ .Source.DeactivateReason }}
Description:{{ .Source.Description }}
Habitat:{{ .Source.Habitat }}
Jurisdiction:{{ .Source.Jurisdiction }}
Location Number:{{ .Source.LocationNumber }}
Name:{{ .Source.Name }}
Status - {{ if .Source.Active }}Active - {{ else }}Inactive - {{ end }} -
Priority:{{ .Source.Priority }} ({{.Source.ScalarPriority}})
S Type:{{ .Source.SourceType }}
Source Status:{{ .Source.SourceStatus }}
Symbology:{{ .Source.Symbology }}
Use Type:{{ .Source.UseType }}
Water Origin:{{ .Source.WaterOrigin }}
Zone:{{ .Source.Zone }}.{{ .Source.Zone2 }}
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Creation date{{ .Source.Created|timeSincePtr }}
Edit date{{ .Source.EditedAt|timeSincePtr }}
Larva Inspect Interval{{ .Source.LarvaeInspectInterval }}
Last Inspect Activity{{ .Source.LastInspectionActivity }}
Last Inspect Avg Larva{{ .Source.LastInspectionAverageLarvae }}
Last Inspect Avg Pupae{{ .Source.LastInspectionAveragePupae }}
Last Inspect Breeding{{ .Source.LastInspectionBreeding }}
Last Inspect Conditions{{ .Source.LastInspectionConditions }}
Last Inspect Date{{ .Source.LastInspectionDate|timeSincePtr }}
Last Inspect Species{{ .Source.LastInspectionFieldSpecies }}
Last Inspect Life Stages{{ .Source.LastInspectionLifeStages }}
Last Treat Activity{{ .Source.LastTreatmentActivity }}
Last Treat Date{{ .Source.LastTreatmentDate|timeSincePtr }}
Last Treat Product{{ .Source.LastTreatmentProduct }}
Last Treat Quantity{{ .Source.LastTreatmentQuantity }}
Last Treat Quantity Unit{{ .Source.LastTreatmentQuantityUnit }}
Next action date scheduled:{{ .Source.NextActionScheduledDate|timeSincePtr }}
Treatment Cadence:Not implemented
-
-
-
-
-
- - -
-
-
-
-
-
-
- - -
- -
- -

Treatment History

-
-
-
- - - - - - - - - {{ range .TreatmentModels }} - - - - - - - {{ end }} - -
YearStartEndInterval
{{.Year}}{{.SeasonStart|timeAsRelativeDate}}{{.SeasonEnd|timeAsRelativeDate}}{{.Interval|timeInterval}}
- - - - - - - - - - - {{ range .Treatments }} - - - - - - - {{ end }} - -
Treatment DateInsecticide UsedCadence DeltaTechnician Notes
{{.Date|timeSincePtr}}{{.Product}}{{.CadenceDelta|timeDelta}}{{.Notes}}
-
-
-
- -
- - -
- -

Inspection History

-
-
-
- - - - - - - - - - {{ range .Inspections }} - - - - - - {{ end }} - -
Inspection DateActionNotes
{{.Date|timeSincePtr}}{{.Action}}{{.Notes}}
-
-
-
-
-
-
-
-

Nearby Mosquito Traps

- {{ range .Traps }} -
- - - - - - - - - - - -
Trap ID:{{ .ID }}
Distance{{ .Distance }}
-
-
- - - - - - - - - - - - {{ range .Counts }} - - - - - - - {{ end }} - {{ end }} - -
Collection DateFemale CountMale CountTotal Count
{{ .Ended|timeSincePtr }}{{ .Females }}{{ .Males }}{{ .Total }}
-
-
-
-
-{{end}} diff --git a/sync/template/text-messages.html b/sync/template/text-messages.html deleted file mode 100644 index c38c243e..00000000 --- a/sync/template/text-messages.html +++ /dev/null @@ -1,143 +0,0 @@ -{{template "authenticated.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} - -{{end}} -{{define "content"}} -
- -
-
-
-
-
- User avatar -
-
-
Chat with Sarah Johnson
- Last active 5 minutes ago -
-
-
-
-
- - -
-
-
-
- -
- Today, 2:30 PM -
- - -
- Receiver avatar -
-
- Hi there! How's the project coming along? -
-
2:31 PM
-
-
- - -
- Sender avatar -
-
- Hey! It's going pretty well. I'm working on the UI mockups right now. -
-
2:33 PM
-
-
- - -
- Receiver avatar -
-
- That's great to hear! When do you think you'll be able to share them with the team? -
-
2:35 PM
-
-
- - -
- Sender avatar -
-
- I'm hoping to have something ready by tomorrow afternoon. I'm just working out some details with the responsive design. -
-
2:36 PM
-
-
- - -
- Sender avatar -
-
- Do you have any specific feedback on the initial concept I shared last week? -
-
2:37 PM
-
-
- - -
- Receiver avatar -
-
- Yes! The team loved it. The color scheme was particularly well received. We just had some minor suggestions about the navigation that I can share during our next call. -
-
2:40 PM
-
-
- - -
- Sender avatar -
-
- That sounds great! Looking forward to the feedback. -
-
2:41 PM
-
-
-
-
-
-
-
-{{end}} diff --git a/sync/template/trap.html b/sync/template/trap.html deleted file mode 100644 index cecc11a6..00000000 --- a/sync/template/trap.html +++ /dev/null @@ -1,131 +0,0 @@ -{{template "authenticated.html" .}} - -{{define "title"}}Dash{{end}} -{{define "extraheader"}} -{{template "map" .MapData}} - -{{end}} -{{define "content"}} -
- -
-
-

Trap Detail

-
-
- -
-
-
-
-
-
Trap ID: {{ .Trap.GlobalID }}
- - - - - - - - - - - - - -
Active:{{ .Trap.Active }}
Comments:{{ .Trap.Comments }}
Description:{{ .Trap.Description }}
-
-
-
-
-
- - -
-
-
-
-
-
-
- -
-
-

Trap Collections

-
- - - - - - - - - - - - - {{ range .Trap.Collections }} - - - - - - - - {{ end }} - -
Collection DateCollection IDFemalesMaleTotal
{{ .EndDateTime|timeSincePtr }}{{ .GlobalID }}{{ .Count.Females }}{{ .Count.Males }}{{ .Count.Total }}
-
-
-
-
-{{end}} diff --git a/sync/text.go b/sync/text.go index 473b51e7..49495597 100644 --- a/sync/text.go +++ b/sync/text.go @@ -11,10 +11,6 @@ type ContentTextMessages struct { User User } -var ( - textMessagesT = buildTemplate("text-messages", "authenticated") -) - func getTextMessages(w http.ResponseWriter, r *http.Request, u *models.User) { userContent, err := contentForUser(r.Context(), u) if err != nil { @@ -24,5 +20,5 @@ func getTextMessages(w http.ResponseWriter, r *http.Request, u *models.User) { content := ContentTextMessages{ User: userContent, } - html.RenderOrError(w, textMessagesT, content) + html.RenderOrError(w, "sync/text-messages.html", content) }