Add breeding source detail page
A bunch of it is currently placeholders, but I got the map marker working so I'm saving it.
This commit is contained in:
parent
6dca03fd0a
commit
f599d831c8
8 changed files with 740 additions and 6 deletions
11
endpoint.go
11
endpoint.go
|
|
@ -233,9 +233,20 @@ func getSignin(w http.ResponseWriter, r *http.Request) {
|
|||
errorCode := r.URL.Query().Get("error")
|
||||
htmlSignin(w, errorCode)
|
||||
}
|
||||
|
||||
func getSignup(w http.ResponseWriter, r *http.Request) {
|
||||
htmlSignup(w, r.URL.Path)
|
||||
}
|
||||
|
||||
func getSource(w http.ResponseWriter, r *http.Request, u *models.User) {
|
||||
globalid := chi.URLParam(r, "globalid")
|
||||
if globalid == "" {
|
||||
respondError(w, "No globalid provided", nil, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
htmlSource(w, r, u, globalid)
|
||||
}
|
||||
|
||||
func getVectorTiles(w http.ResponseWriter, r *http.Request, u *models.User) {
|
||||
org_id := chi.URLParam(r, "org_id")
|
||||
tileset_id := chi.URLParam(r, "tileset_id")
|
||||
|
|
|
|||
71
html.go
71
html.go
|
|
@ -32,6 +32,7 @@ var (
|
|||
dashboard = newBuiltTemplate("dashboard", "authenticated")
|
||||
oauthPrompt = newBuiltTemplate("oauth-prompt", "authenticated")
|
||||
settings = newBuiltTemplate("settings", "authenticated")
|
||||
source = newBuiltTemplate("source", "authenticated")
|
||||
)
|
||||
|
||||
// Unauthenticated pages
|
||||
|
|
@ -57,7 +58,7 @@ var (
|
|||
)
|
||||
var components = [...]string{"header", "map"}
|
||||
|
||||
type BreedingSource struct {
|
||||
type BreedingSourceSummary struct {
|
||||
ID string
|
||||
Type string
|
||||
LastInspected *time.Time
|
||||
|
|
@ -70,17 +71,21 @@ type BuiltTemplate struct {
|
|||
template *template.Template
|
||||
}
|
||||
|
||||
type MapMarker struct {
|
||||
LatLng LatLng
|
||||
}
|
||||
type ComponentMap struct {
|
||||
Center LatLng
|
||||
GeoJSON interface{}
|
||||
MapboxToken string
|
||||
Markers []MapMarker
|
||||
Zoom int
|
||||
}
|
||||
type ContentAuthenticatedPlaceholder struct {
|
||||
User User
|
||||
}
|
||||
type ContentCell struct {
|
||||
BreedingSources []BreedingSource
|
||||
BreedingSources []BreedingSourceSummary
|
||||
CellBoundary h3.CellBoundary
|
||||
Inspections []Inspection
|
||||
MapData ComponentMap
|
||||
|
|
@ -114,6 +119,11 @@ type ContentSignin struct {
|
|||
InvalidCredentials bool
|
||||
}
|
||||
type ContentSignup struct{}
|
||||
type ContentSource struct {
|
||||
MapData ComponentMap
|
||||
Source *BreedingSourceDetail
|
||||
User User
|
||||
}
|
||||
type LatLng struct {
|
||||
Lat float64
|
||||
Lng float64
|
||||
|
|
@ -467,6 +477,47 @@ func htmlSignup(w http.ResponseWriter, path string) {
|
|||
renderOrError(w, signup, data)
|
||||
}
|
||||
|
||||
func htmlSource(w http.ResponseWriter, r *http.Request, user *models.User, id string) {
|
||||
org, err := user.Organization().One(r.Context(), PGInstance.BobDB)
|
||||
if err != nil {
|
||||
respondError(w, "Failed to get org", err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
userContent, err := contentForUser(r.Context(), user)
|
||||
if err != nil {
|
||||
respondError(w, "Failed to get user content", err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
s, err := sourceByGlobalId(r.Context(), org, id)
|
||||
if err != nil {
|
||||
respondError(w, "Failed to get source", err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
data := ContentSource{
|
||||
MapData: ComponentMap{
|
||||
Center: LatLng{
|
||||
Lat: s.GeometryY,
|
||||
Lng: s.GeometryX,
|
||||
},
|
||||
//GeoJSON:
|
||||
MapboxToken: MapboxToken,
|
||||
Markers: []MapMarker{
|
||||
MapMarker{
|
||||
LatLng: LatLng{
|
||||
Lat: s.GeometryY,
|
||||
Lng: s.GeometryX,
|
||||
},
|
||||
},
|
||||
},
|
||||
Zoom: 13,
|
||||
},
|
||||
Source: s,
|
||||
User: userContent,
|
||||
}
|
||||
|
||||
renderOrError(w, source, data)
|
||||
}
|
||||
|
||||
func gisStatement(cb h3.CellBoundary) string {
|
||||
var content strings.Builder
|
||||
for i, p := range cb {
|
||||
|
|
@ -671,8 +722,8 @@ func inspectionsByCell(ctx context.Context, org *models.Organization, c h3.Cell)
|
|||
}
|
||||
return results, nil
|
||||
}
|
||||
func breedingSourcesByCell(ctx context.Context, org *models.Organization, c h3.Cell) ([]BreedingSource, error) {
|
||||
var results []BreedingSource
|
||||
func breedingSourcesByCell(ctx context.Context, org *models.Organization, c h3.Cell) ([]BreedingSourceSummary, error) {
|
||||
var results []BreedingSourceSummary
|
||||
|
||||
boundary, err := c.Boundary()
|
||||
if err != nil {
|
||||
|
|
@ -689,7 +740,7 @@ func breedingSourcesByCell(ctx context.Context, org *models.Organization, c h3.C
|
|||
return results, fmt.Errorf("Failed to query rows: %w", err)
|
||||
}
|
||||
for _, r := range rows {
|
||||
results = append(results, BreedingSource{
|
||||
results = append(results, BreedingSourceSummary{
|
||||
ID: r.Globalid,
|
||||
LastInspected: fsTimestampToTime(r.Lastinspectdate),
|
||||
LastTreated: fsTimestampToTime(r.Lasttreatdate),
|
||||
|
|
@ -706,3 +757,13 @@ func uuidShort(uuid string) string {
|
|||
|
||||
return uuid[:3] + "..." + uuid[len(uuid)-4:]
|
||||
}
|
||||
|
||||
func sourceByGlobalId(ctx context.Context, org *models.Organization, id string) (*BreedingSourceDetail, error) {
|
||||
row, err := org.FSPointlocations(
|
||||
sm.Where(models.FSPointlocations.Columns.Globalid.EQ(psql.Arg(id))),
|
||||
).One(ctx, PGInstance.BobDB)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to get point location: %w", err)
|
||||
}
|
||||
return ConvertToDisplayModel(row), nil
|
||||
}
|
||||
|
|
|
|||
1
main.go
1
main.go
|
|
@ -113,6 +113,7 @@ func main() {
|
|||
// Authenticated endpoints
|
||||
r.Method("GET", "/cell/{cell}", NewEnsureAuth(getCellDetails))
|
||||
r.Method("GET", "/settings", NewEnsureAuth(getSettings))
|
||||
r.Method("GET", "/source/{globalid}", NewEnsureAuth(getSource))
|
||||
r.Method("GET", "/vector-tiles/{org_id}/{tileset_id}/{zoom}/{x}/{y}.{format}", NewEnsureAuth(getVectorTiles))
|
||||
|
||||
localFS := http.Dir("./static")
|
||||
|
|
|
|||
14
migrations/00015_fs_geometry.sql
Normal file
14
migrations/00015_fs_geometry.sql
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
-- +goose Up
|
||||
ALTER TABLE fs_pointlocation ADD COLUMN geom geometry(Point, 3857); -- as specified by the ArcGIS API
|
||||
UPDATE fs_pointlocation SET geom = ST_SetSRID(ST_MakePoint(geometry_x, geometry_y), 3857);
|
||||
|
||||
ALTER TABLE fs_treatment ADD COLUMN geom geometry(Point, 3857); -- as specified by the ArcGIS API
|
||||
UPDATE fs_treatment SET geom = ST_SetSRID(ST_MakePoint(geometry_x, geometry_y), 3857);
|
||||
|
||||
ALTER TABLE fs_mosquitoinspection ADD COLUMN geom geometry(Point, 3857); -- as specified by the ArcGIS API
|
||||
UPDATE fs_mosquitoinspection SET geom = ST_SetSRID(ST_MakePoint(geometry_x, geometry_y), 3857);
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE fs_pointlocation DROP COLUMN geom;
|
||||
ALTER TABLE fs_treatment DROP COLUMN geom;
|
||||
ALTER TABLE fs_mosquitoinspection DROP COLUMN geom;
|
||||
161
model_conversion.go
Normal file
161
model_conversion.go
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/Gleipnir-Technology/nidus-sync/models"
|
||||
"github.com/aarondl/opt/null"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BreedingSourceDetail struct {
|
||||
// Basic Information
|
||||
OrganizationID int32 `json:"organizationId"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
LocationNumber int64 `json:"locationNumber"`
|
||||
ObjectID int32 `json:"objectId"`
|
||||
GlobalID string `json:"globalId"`
|
||||
ExternalID string `json:"externalId"`
|
||||
|
||||
// Status Information
|
||||
Active bool `json:"active"`
|
||||
DeactivateReason string `json:"deactivateReason"`
|
||||
SourceStatus string `json:"sourceStatus"`
|
||||
Priority string `json:"priority"`
|
||||
ScalarPriority int64 `json:"scalarPriority"`
|
||||
|
||||
// Classification
|
||||
SourceType string `json:"sourceType"`
|
||||
Habitat string `json:"habitat"`
|
||||
UseType string `json:"useType"`
|
||||
WaterOrigin string `json:"waterOrigin"`
|
||||
Symbology string `json:"symbology"`
|
||||
|
||||
// Geographical Data
|
||||
X float64 `json:"x"`
|
||||
Y float64 `json:"y"`
|
||||
GeometryX float64 `json:"geometryX"`
|
||||
GeometryY float64 `json:"geometryY"`
|
||||
Zone string `json:"zone"`
|
||||
Zone2 string `json:"zone2"`
|
||||
Jurisdiction string `json:"jurisdiction"`
|
||||
AccessDescription string `json:"accessDescription"`
|
||||
|
||||
// Inspection Data
|
||||
LarvaeInspectInterval int16 `json:"larvaeInspectInterval"`
|
||||
LastInspectionDate time.Time `json:"lastInspectionDate"`
|
||||
LastInspectionActivity string `json:"lastInspectionActivity"`
|
||||
LastInspectionActionTaken string `json:"lastInspectionActionTaken"`
|
||||
LastInspectionAverageLarvae float64 `json:"lastInspectionAverageLarvae"`
|
||||
LastInspectionAveragePupae float64 `json:"lastInspectionAveragePupae"`
|
||||
LastInspectionBreeding string `json:"lastInspectionBreeding"`
|
||||
LastInspectionConditions string `json:"lastInspectionConditions"`
|
||||
LastInspectionFieldSpecies string `json:"lastInspectionFieldSpecies"`
|
||||
LastInspectionLifeStages string `json:"lastInspectionLifeStages"`
|
||||
|
||||
// Treatment Data
|
||||
LastTreatmentDate time.Time `json:"lastTreatmentDate"`
|
||||
LastTreatmentActivity string `json:"lastTreatmentActivity"`
|
||||
LastTreatmentProduct string `json:"lastTreatmentProduct"`
|
||||
LastTreatmentQuantity float64 `json:"lastTreatmentQuantity"`
|
||||
LastTreatmentQuantityUnit string `json:"lastTreatmentQuantityUnit"`
|
||||
|
||||
// Assignment & Schedule
|
||||
AssignedTechnician string `json:"assignedTechnician"`
|
||||
NextActionScheduledDate time.Time `json:"nextActionScheduledDate"`
|
||||
|
||||
// Metadata
|
||||
Created time.Time `json:"created"`
|
||||
Creator string `json:"creator"`
|
||||
EditedAt time.Time `json:"editedAt"`
|
||||
Editor string `json:"editor"`
|
||||
Updated time.Time `json:"updated"`
|
||||
Comments string `json:"comments"`
|
||||
}
|
||||
|
||||
// ConvertToDisplayModel transforms the DB model into the display model
|
||||
func ConvertToDisplayModel(source *models.FSPointlocation) *BreedingSourceDetail {
|
||||
// Helper function to convert unix timestamp to time.Time
|
||||
toTime := func(val null.Val[int64]) time.Time {
|
||||
v, ok := val.Get()
|
||||
if !ok {
|
||||
return time.UnixMilli(0)
|
||||
}
|
||||
t := time.UnixMilli(v)
|
||||
return t
|
||||
}
|
||||
|
||||
// Helper function to convert int16 to bool
|
||||
toBool := func(val null.Val[int16]) bool {
|
||||
if !val.IsValue() {
|
||||
return false
|
||||
}
|
||||
b := val.MustGet() != 0
|
||||
return b
|
||||
}
|
||||
|
||||
return &BreedingSourceDetail{
|
||||
// Basic Information
|
||||
OrganizationID: source.OrganizationID,
|
||||
Name: source.Name.MustGet(),
|
||||
Description: source.Description.MustGet(),
|
||||
LocationNumber: source.Locationnumber.GetOr(0),
|
||||
ObjectID: source.Objectid,
|
||||
GlobalID: source.Globalid,
|
||||
ExternalID: source.Externalid.GetOr(""),
|
||||
|
||||
// Status Information
|
||||
Active: toBool(source.Active),
|
||||
DeactivateReason: source.DeactivateReason.GetOr(""),
|
||||
SourceStatus: source.Sourcestatus.GetOr(""),
|
||||
Priority: source.Priority.GetOr(""),
|
||||
ScalarPriority: source.Scalarpriority.GetOr(0),
|
||||
|
||||
// Classification
|
||||
SourceType: source.Stype.GetOr(""),
|
||||
Habitat: source.Habitat.GetOr(""),
|
||||
UseType: source.Usetype.GetOr(""),
|
||||
WaterOrigin: source.Waterorigin.GetOr(""),
|
||||
Symbology: source.Symbology.GetOr(""),
|
||||
|
||||
// Geographical Data
|
||||
X: source.X.GetOr(0),
|
||||
Y: source.Y.GetOr(0),
|
||||
GeometryX: source.GeometryX,
|
||||
GeometryY: source.GeometryY,
|
||||
Zone: source.Zone.GetOr(""),
|
||||
Zone2: source.Zone2.GetOr(""),
|
||||
Jurisdiction: source.Jurisdiction.GetOr(""),
|
||||
AccessDescription: source.Accessdesc.GetOr(""),
|
||||
|
||||
// Inspection Data
|
||||
LarvaeInspectInterval: source.Larvinspectinterval.GetOr(0),
|
||||
LastInspectionDate: toTime(source.Lastinspectdate),
|
||||
LastInspectionActivity: source.Lastinspectactivity.GetOr(""),
|
||||
LastInspectionActionTaken: source.Lastinspectactiontaken.GetOr(""),
|
||||
LastInspectionAverageLarvae: source.Lastinspectavglarvae.GetOr(0),
|
||||
LastInspectionAveragePupae: source.Lastinspectavgpupae.GetOr(0),
|
||||
LastInspectionBreeding: source.Lastinspectbreeding.GetOr(""),
|
||||
LastInspectionConditions: source.Lastinspectconditions.GetOr(""),
|
||||
LastInspectionFieldSpecies: source.Lastinspectfieldspecies.GetOr(""),
|
||||
LastInspectionLifeStages: source.Lastinspectlstages.GetOr(""),
|
||||
|
||||
// Treatment Data
|
||||
LastTreatmentDate: toTime(source.Lasttreatdate),
|
||||
LastTreatmentActivity: source.Lasttreatactivity.GetOr(""),
|
||||
LastTreatmentProduct: source.Lasttreatproduct.GetOr(""),
|
||||
LastTreatmentQuantity: source.Lasttreatqty.GetOr(0),
|
||||
LastTreatmentQuantityUnit: source.Lasttreatqtyunit.GetOr(""),
|
||||
|
||||
// Assignment & Schedule
|
||||
AssignedTechnician: source.Assignedtech.GetOr(""),
|
||||
NextActionScheduledDate: toTime(source.Nextactiondatescheduled),
|
||||
|
||||
// Metadata
|
||||
Created: toTime(source.Creationdate),
|
||||
Creator: source.Creator.GetOr(""),
|
||||
EditedAt: toTime(source.Editdate),
|
||||
Editor: source.Editor.GetOr(""),
|
||||
Updated: source.Updated,
|
||||
Comments: source.Comments.GetOr(""),
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,21 @@
|
|||
<link href='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.css' rel='stylesheet' />
|
||||
<script>
|
||||
const geojson = JSON.parse({{.GeoJSON}})
|
||||
function addMarkers(map, markers) {
|
||||
for (let i = 0; i < markers.length; i++) {
|
||||
let marker = markers[i];
|
||||
marker.addTo(map);
|
||||
}
|
||||
}
|
||||
function mapMarkers() {
|
||||
const markers = [
|
||||
{{ range .Markers }}
|
||||
new mapboxgl.Marker().setLngLat([{{.LatLng.Lng}}, {{.LatLng.Lat}}])
|
||||
];
|
||||
{{end}}
|
||||
return markers;
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
console.log("Setting up the map...", geojson);
|
||||
mapboxgl.accessToken = {{ .MapboxToken }};
|
||||
|
|
@ -14,6 +29,7 @@ function onLoad() {
|
|||
});
|
||||
map.on("load", function() {
|
||||
console.log("Map post-load...");
|
||||
addMarkers(map, mapMarkers());
|
||||
const sourceId = 'h3-hexes';
|
||||
const layerId = 'h3-hexes-layer';
|
||||
let source = map.getSource(sourceId);
|
||||
|
|
@ -36,6 +52,7 @@ function onLoad() {
|
|||
source = map.getSource(sourceId);
|
||||
}
|
||||
source.setData(geojson);
|
||||
|
||||
console.log("Map post-load done.");
|
||||
});
|
||||
console.log("Map init done.");
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<link href='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.css' rel='stylesheet' />
|
||||
<script>
|
||||
function onLoad() {
|
||||
console.log("Setting up the map...", geojson);
|
||||
console.log("Setting up the map...");
|
||||
mapboxgl.accessToken = {{ .MapData.MapboxToken }};
|
||||
const map = new mapboxgl.Map({
|
||||
container: 'map', // container ID
|
||||
|
|
|
|||
469
templates/source.html
Normal file
469
templates/source.html
Normal file
|
|
@ -0,0 +1,469 @@
|
|||
{{template "authenticated.html" .}}
|
||||
|
||||
{{define "title"}}Dash{{end}}
|
||||
{{define "extraheader"}}
|
||||
{{template "map" .MapData}}
|
||||
<style>
|
||||
.map-container {
|
||||
background-color: #e9ecef;
|
||||
height: 500px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.source-info {
|
||||
background-color: #f8f9fa;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.time-delta-positive {
|
||||
color: #dc3545; /* red for late */
|
||||
}
|
||||
|
||||
.time-delta-negative {
|
||||
color: #28a745; /* green for early */
|
||||
}
|
||||
|
||||
.time-delta-neutral {
|
||||
color: #6c757d; /* gray for on time */
|
||||
}
|
||||
</style>
|
||||
{{end}}
|
||||
{{define "content"}}
|
||||
<div class="container mt-4 mb-5">
|
||||
<!-- Source Header Section -->
|
||||
<div class="row mb-2">
|
||||
<div class="col-12">
|
||||
<h1>Breeding Source Detail</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-2">
|
||||
<div class="col-12">
|
||||
<div class="source-info">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="source-id">Source ID: {{ .Source.GlobalID }}</div>
|
||||
<table class="info-table">
|
||||
<tr>
|
||||
<td class="info-label">Access:</td>
|
||||
<td>{{ .Source.AccessDescription }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Address:</td>
|
||||
<td>Not implemented</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Comments:</td>
|
||||
<td>{{ .Source.Comments }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Deactivate Reason:</td>
|
||||
<td>{{ .Source.DeactivateReason }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Description:</td>
|
||||
<td>{{ .Source.Description }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Habitat:</td>
|
||||
<td>{{ .Source.Habitat }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Jurisdiction:</td>
|
||||
<td>{{ .Source.Jurisdiction }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Location Number:</td>
|
||||
<td>{{ .Source.LocationNumber }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Name:</td>
|
||||
<td>{{ .Source.Name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Status</td>
|
||||
<td>
|
||||
{{ if .Source.Active }}<span class="badge bg-warning">Active</span>
|
||||
{{ else }}<span class="badge bg-info">Inactive</span>
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Priority:</td>
|
||||
<td>{{ .Source.Priority }} ({{.Source.ScalarPriority}})</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">S Type:</td>
|
||||
<td>{{ .Source.SourceType }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Source Status:</td>
|
||||
<td>{{ .Source.SourceStatus }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Symbology:</td>
|
||||
<td>{{ .Source.Symbology }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Use Type:</td>
|
||||
<td>{{ .Source.UseType }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Water Origin:</td>
|
||||
<td>{{ .Source.WaterOrigin }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Zone:</td>
|
||||
<td>{{ .Source.Zone }}.{{ .Source.Zone2 }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<table class="info-table">
|
||||
<tr>
|
||||
<td class="info-label">Creation date</td>
|
||||
<td>{{ .Source.Created|timeSince }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Edit date</td>
|
||||
<td>{{ .Source.EditedAt|timeSince }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Larva Inspect Interval</td>
|
||||
<td>{{ .Source.LarvaeInspectInterval }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Last Inspect Activity</td>
|
||||
<td>{{ .Source.LastInspectionActivity }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Last Inspect Avg Larva</td>
|
||||
<td>{{ .Source.LastInspectionAverageLarvae }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Last Inspect Avg Pupae</td>
|
||||
<td>{{ .Source.LastInspectionAveragePupae }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Last Inspect Breeding</td>
|
||||
<td>{{ .Source.LastInspectionBreeding }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Last Inspect Conditions</td>
|
||||
<td>{{ .Source.LastInspectionConditions }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Last Inspect Date</td>
|
||||
<td>{{ .Source.LastInspectionDate|timeSince }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Last Inspect Species</td>
|
||||
<td>{{ .Source.LastInspectionFieldSpecies }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Last Inspect Life Stages</td>
|
||||
<td>{{ .Source.LastInspectionLifeStages }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Last Treat Activity</td>
|
||||
<td>{{ .Source.LastTreatmentActivity }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Last Treat Date</td>
|
||||
<td>{{ .Source.LastTreatmentDate|timeSince }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Last Treat Product</td>
|
||||
<td>{{ .Source.LastTreatmentProduct }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Last Treat Quantity</td>
|
||||
<td>{{ .Source.LastTreatmentQuantity }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Last Treat Quantity Unit</td>
|
||||
<td>{{ .Source.LastTreatmentQuantityUnit }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Next action date scheduled:</td>
|
||||
<td>{{ .Source.NextActionScheduledDate|timeSince }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Updated:</td>
|
||||
<td>{{ .Source.Updated|timeSince }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="info-label">Treatment Cadence:</td>
|
||||
<td>Not implemented</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Map Section -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="map-container">
|
||||
<div id="map"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Two-Column Layout for Tables -->
|
||||
<div class="row">
|
||||
<!-- Left Column -->
|
||||
<div class="col-md-6">
|
||||
<!-- Treatments Section -->
|
||||
<h2 class="section-header">Treatment History</h2>
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Treatment Date</th>
|
||||
<th>Insecticide Used</th>
|
||||
<th>Cadence Delta</th>
|
||||
<th>Technician Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>04/16/2023</td>
|
||||
<td>Bacillus thuringiensis israelensis (Bti)</td>
|
||||
<td class="time-delta-neutral">On time</td>
|
||||
<td>Applied larvicide to standing water.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>04/02/2023</td>
|
||||
<td>Methoprene</td>
|
||||
<td class="time-delta-neutral">On time</td>
|
||||
<td>Applied to ditch with extended release formula.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>03/18/2023</td>
|
||||
<td>Bacillus sphaericus</td>
|
||||
<td class="time-delta-negative">-1 day</td>
|
||||
<td>Preemptive treatment before rain event.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>03/01/2023</td>
|
||||
<td>Bacillus thuringiensis israelensis (Bti)</td>
|
||||
<td class="time-delta-positive">+3 days</td>
|
||||
<td>Delayed due to weather conditions.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>02/15/2023</td>
|
||||
<td>Methoprene</td>
|
||||
<td class="time-delta-neutral">On time</td>
|
||||
<td>Regular treatment cycle.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<nav aria-label="Treatments pagination">
|
||||
<ul class="pagination justify-content-center">
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" tabindex="-1">Previous</a>
|
||||
</li>
|
||||
<li class="page-item active"><a class="page-link" href="#">1</a></li>
|
||||
<li class="page-item"><a class="page-link" href="#">2</a></li>
|
||||
<li class="page-item"><a class="page-link" href="#">3</a></li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="#">Next</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Inspections Section -->
|
||||
<h2 class="section-header">Inspection History</h2>
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Inspection Date</th>
|
||||
<th>Inspector</th>
|
||||
<th>Larvae Present</th>
|
||||
<th>Comments</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>04/15/2023</td>
|
||||
<td>J. Smith</td>
|
||||
<td><span class="badge bg-danger">Yes</span></td>
|
||||
<td>Moderate larvae presence, treatment recommended.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>04/01/2023</td>
|
||||
<td>M. Johnson</td>
|
||||
<td><span class="badge bg-danger">Yes</span></td>
|
||||
<td>High larvae count, immediate treatment needed.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>03/17/2023</td>
|
||||
<td>J. Smith</td>
|
||||
<td><span class="badge bg-success">No</span></td>
|
||||
<td>No larvae observed, previous treatment effective.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>03/03/2023</td>
|
||||
<td>R. Williams</td>
|
||||
<td><span class="badge bg-danger">Yes</span></td>
|
||||
<td>Low larvae count, monitoring recommended.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>02/14/2023</td>
|
||||
<td>M. Johnson</td>
|
||||
<td><span class="badge bg-danger">Yes</span></td>
|
||||
<td>Moderate larvae count, treatment scheduled.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<nav aria-label="Inspections pagination">
|
||||
<ul class="pagination justify-content-center">
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" tabindex="-1">Previous</a>
|
||||
</li>
|
||||
<li class="page-item active"><a class="page-link" href="#">1</a></li>
|
||||
<li class="page-item"><a class="page-link" href="#">2</a></li>
|
||||
<li class="page-item"><a class="page-link" href="#">3</a></li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="#">Next</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column -->
|
||||
<div class="col-md-6">
|
||||
<!-- Nearby Traps Section -->
|
||||
<h2 class="section-header">Nearby Mosquito Traps</h2>
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Trap ID</th>
|
||||
<th>Distance</th>
|
||||
<th>Collection Date</th>
|
||||
<th>Mosquito Count</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Trap 1 with multiple collections -->
|
||||
<tr>
|
||||
<td rowspan="3">TR-105</td>
|
||||
<td rowspan="3">0.2 mi</td>
|
||||
<td>04/17/2023</td>
|
||||
<td>37</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>04/10/2023</td>
|
||||
<td>52</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>04/03/2023</td>
|
||||
<td>61</td>
|
||||
</tr>
|
||||
|
||||
<!-- Trap 2 with multiple collections -->
|
||||
<tr>
|
||||
<td rowspan="3">TR-108</td>
|
||||
<td rowspan="3">0.4 mi</td>
|
||||
<td>04/17/2023</td>
|
||||
<td>22</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>04/10/2023</td>
|
||||
<td>35</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>04/03/2023</td>
|
||||
<td>41</td>
|
||||
</tr>
|
||||
|
||||
<!-- Trap 3 with multiple collections -->
|
||||
<tr>
|
||||
<td rowspan="3">TR-112</td>
|
||||
<td rowspan="3">0.6 mi</td>
|
||||
<td>04/17/2023</td>
|
||||
<td>18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>04/10/2023</td>
|
||||
<td>24</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>04/03/2023</td>
|
||||
<td>27</td>
|
||||
</tr>
|
||||
|
||||
<!-- Trap 4 with multiple collections -->
|
||||
<tr>
|
||||
<td rowspan="3">TR-117</td>
|
||||
<td rowspan="3">0.8 mi</td>
|
||||
<td>04/17/2023</td>
|
||||
<td>12</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>04/10/2023</td>
|
||||
<td>19</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>04/03/2023</td>
|
||||
<td>15</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Source Analysis Box -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Source Analysis & Recommendations</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p><strong>Risk Level:</strong> <span class="badge bg-danger">High</span></p>
|
||||
<p><strong>Associated Species:</strong> Culex pipiens, Aedes albopictus</p>
|
||||
<p><strong>Trend Analysis:</strong> Mosquito counts in nearby traps show a gradual decrease following recent treatments, indicating positive impact of abatement efforts.</p>
|
||||
<p><strong>Recommendations:</strong></p>
|
||||
<ul>
|
||||
<li>Maintain 14-day treatment schedule through summer months</li>
|
||||
<li>Consider physical modification to reduce standing water retention</li>
|
||||
<li>Increase trap surveillance in May-June (peak breeding season)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
Loading…
Add table
Add a link
Reference in a new issue