From 9dccd21cee593e216d7a62397b77d0d65d94e056 Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Thu, 9 Apr 2026 17:21:35 +0000 Subject: [PATCH] RMO frontend checkpoint * Create a nwe AddressAndMapLocator which abstracts out the behavior of selecting a location * Fix the overlay causing render errors on the MapLocator by getting rid of the overlay and just using a lock indicator * Fix MapLocator zooming in to the wrong place by not framing the markers * Remove Latlng from platform and just use Location with optional accuracy * Use nested types with form-encoded POST * Fix styles on water report page --- h3utils/h3.go | 25 +- platform/district.go | 10 +- platform/latlng.go | 44 ---- platform/publicreport.go | 24 +- platform/types/address.go | 4 +- platform/types/location.go | 24 +- resource/nuisance.go | 121 +++------ resource/water.go | 104 ++++---- scss/rmo/nuisance.scss | 0 scss/rmo/root.scss | 39 --- scss/rmo/status.scss | 66 ----- ts/components/AddressSuggestion.vue | 2 +- ts/components/MapLocator.vue | 78 ++---- ts/components/Tooltip.vue | 57 ++++ ts/rmo/components/AddressAndMapLocator.vue | 172 ++++++++++++ ts/rmo/components/Header.vue | 21 ++ ts/rmo/content/Home.vue | 8 + ts/rmo/content/Nuisance.vue | 172 +++--------- ts/rmo/content/Status.vue | 68 +++++ ts/rmo/content/Water.vue | 292 +++++++++++++++++++-- ts/rmo/content/compliance/Address.vue | 76 ++---- ts/rmo/view/Compliance.vue | 7 +- ts/rmo/view/ReportSubmitted.vue | 4 +- ts/type/api.ts | 1 + ts/type/map.ts | 7 +- 25 files changed, 828 insertions(+), 598 deletions(-) delete mode 100644 scss/rmo/nuisance.scss delete mode 100644 scss/rmo/root.scss delete mode 100644 scss/rmo/status.scss create mode 100644 ts/components/Tooltip.vue create mode 100644 ts/rmo/components/AddressAndMapLocator.vue diff --git a/h3utils/h3.go b/h3utils/h3.go index c5c6082d..7ba4510a 100644 --- a/h3utils/h3.go +++ b/h3utils/h3.go @@ -5,7 +5,7 @@ import ( "strings" "github.com/Gleipnir-Technology/go-geojson2h3/v2" - "github.com/tidwall/geojson" + //"github.com/tidwall/geojson" "github.com/uber/h3-go/v4" ) @@ -37,29 +37,6 @@ func H3ToGeoJSON(indexes []h3.Cell) (interface{}, error) { return featureCollection.JSON(), nil } -func main2() { - resolution := 9 - object, err := geojson.Parse(`{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"shape":"Polygon","name":"Unnamed Layer","category":"default"},"geometry":{"type":"Polygon","coordinates":[[[-73.901303,40.756892],[-73.893924,40.743755],[-73.871476,40.756278],[-73.863378,40.764175],[-73.871444,40.768467],[-73.879852,40.760014],[-73.885515,40.764045],[-73.891522,40.761054],[-73.901303,40.756892]]]},"id":"a6ca1b7e-9ddf-4425-ad07-8a895f7d6ccf"}]}`, nil) - if err != nil { - panic(err) - } - - indexes, err := geojson2h3.ToH3(resolution, object) - if err != nil { - panic(err) - } - for _, index := range indexes { - fmt.Printf("h3index: %s\n", index.String()) - } - - featureCollection, err := geojson2h3.ToFeatureCollection(indexes) - if err != nil { - panic(err) - } - fmt.Println("Polyfill:") - fmt.Println(featureCollection.JSON()) -} - // Given a cell at a smaller resolution remap it to the larger resolution func scaleCell(cell h3.Cell, resolution int) (h3.Cell, error) { r := cell.Resolution() diff --git a/platform/district.go b/platform/district.go index d76945a8..a2950342 100644 --- a/platform/district.go +++ b/platform/district.go @@ -39,7 +39,7 @@ func DistrictForLocation(ctx context.Context, lng float64, lat float64) (*models return nil, errors.New("too many organizations") } } -func MatchDistrict(ctx context.Context, longitude, latitude *float64, images []ImageUpload) (*int32, error) { +func MatchDistrict(ctx context.Context, longitude, latitude float64, images []ImageUpload) (*int32, error) { var err error var org *models.Organization for _, image := range images { @@ -58,7 +58,7 @@ func MatchDistrict(ctx context.Context, longitude, latitude *float64, images []I return &org.ID, nil } } - if longitude == nil || latitude == nil { + if longitude == 0 || latitude == 0 { org, err = DistrictCatchall(ctx) if err != nil { return nil, fmt.Errorf("get catchall: %w", err) @@ -66,7 +66,7 @@ func MatchDistrict(ctx context.Context, longitude, latitude *float64, images []I log.Debug().Int32("id", org.ID).Msg("No location from images, no latlng for the report itself, using catchall") return &org.ID, nil } - org, err = DistrictForLocation(ctx, *longitude, *latitude) + org, err = DistrictForLocation(ctx, longitude, latitude) if err != nil { log.Warn().Err(err).Msg("Failed to get district for location") return nil, fmt.Errorf("Failed to get district for location: %w", err) @@ -76,9 +76,9 @@ func MatchDistrict(ctx context.Context, longitude, latitude *float64, images []I if err != nil { return nil, fmt.Errorf("get catchall: %w", err) } - log.Debug().Err(err).Float64("lng", *longitude).Float64("lat", *latitude).Int32("id", org.ID).Msg("No district match by report location, using catchall") + log.Debug().Err(err).Float64("lng", longitude).Float64("lat", latitude).Int32("id", org.ID).Msg("No district match by report location, using catchall") return &org.ID, nil } - log.Debug().Err(err).Int32("org_id", org.ID).Float64("lng", *longitude).Float64("lat", *latitude).Msg("Found district match by report location") + log.Debug().Err(err).Int32("org_id", org.ID).Float64("lng", longitude).Float64("lat", latitude).Msg("Found district match by report location") return &org.ID, nil } diff --git a/platform/latlng.go b/platform/latlng.go index 157e65db..3703e9dc 100644 --- a/platform/latlng.go +++ b/platform/latlng.go @@ -1,13 +1,7 @@ package platform import ( - "errors" - "fmt" - "github.com/Gleipnir-Technology/nidus-sync/db/enums" - "github.com/Gleipnir-Technology/nidus-sync/h3utils" - "github.com/rs/zerolog/log" - "github.com/uber/h3-go/v4" ) type LatLng struct { @@ -17,41 +11,3 @@ type LatLng struct { AccuracyValue float64 AccuracyType enums.PublicreportAccuracytype } - -func (l LatLng) Resolution() uint { - switch l.AccuracyType { - // These accuracy_type strings come from the Mapbox Geocoding API definition and - // are far from scientific - case enums.PublicreportAccuracytypeRooftop: - return 14 - case enums.PublicreportAccuracytypeParcel: - return 13 - case enums.PublicreportAccuracytypePoint: - return 13 - case enums.PublicreportAccuracytypeInterpolated: - return 12 - case enums.PublicreportAccuracytypeApproximate: - return 11 - case enums.PublicreportAccuracytypeIntersection: - return 10 - // This is a special indicator that we got our location from the browser measurements - case enums.PublicreportAccuracytypeBrowser: - return uint(h3utils.MeterAccuracyToH3Resolution(l.AccuracyValue)) - default: - log.Warn().Str("accuracy-type", string(l.AccuracyType)).Msg("unrecognized accuracy type, this indicates either a weird client or misbehaving web page. Defaulting to resolution 13") - return 13 - } -} -func (l LatLng) H3Cell() (*h3.Cell, error) { - if l.Longitude == nil || l.Latitude == nil { - return nil, errors.New("nil lat/lng") - } - result, err := h3utils.GetCell(*l.Longitude, *l.Latitude, int(l.Resolution())) - return &result, err -} -func (l LatLng) GeometryQuery() (string, error) { - if l.Longitude == nil || l.Latitude == nil { - return "", errors.New("nil lat/lng") - } - return fmt.Sprintf("ST_Point(%f, %f, 4326)", *l.Longitude, *l.Latitude), nil -} diff --git a/platform/publicreport.go b/platform/publicreport.go index b5654bb1..2fb175ea 100644 --- a/platform/publicreport.go +++ b/platform/publicreport.go @@ -84,8 +84,8 @@ func PublicReportMessageCreate(ctx context.Context, user User, report_id, messag func PublicReportReporterUpdated(ctx context.Context, org_id int32, report_id string) { event.Updated(event.TypeRMOReport, org_id, report_id) } -func ReportNuisanceCreate(ctx context.Context, setter_report models.PublicreportReportSetter, setter_nuisance models.PublicreportNuisanceSetter, latlng LatLng, address Address, images []ImageUpload) (*models.PublicreportReport, error) { - return reportCreate(ctx, setter_report, latlng, address, images, func(ctx context.Context, txn bob.Executor, report_id int32) error { +func ReportNuisanceCreate(ctx context.Context, setter_report models.PublicreportReportSetter, setter_nuisance models.PublicreportNuisanceSetter, location types.Location, address Address, images []ImageUpload) (*models.PublicreportReport, error) { + return reportCreate(ctx, setter_report, location, address, images, func(ctx context.Context, txn bob.Executor, report_id int32) error { setter_nuisance.ReportID = omit.From(report_id) _, err := models.PublicreportNuisances.Insert(&setter_nuisance).One(ctx, txn) if err != nil { @@ -95,8 +95,8 @@ func ReportNuisanceCreate(ctx context.Context, setter_report models.Publicreport }) } -func ReportWaterCreate(ctx context.Context, setter_report models.PublicreportReportSetter, setter_water models.PublicreportWaterSetter, latlng LatLng, address Address, images []ImageUpload) (*models.PublicreportReport, error) { - return reportCreate(ctx, setter_report, latlng, address, images, func(ctx context.Context, txn bob.Executor, report_id int32) error { +func ReportWaterCreate(ctx context.Context, setter_report models.PublicreportReportSetter, setter_water models.PublicreportWaterSetter, location types.Location, address Address, images []ImageUpload) (*models.PublicreportReport, error) { + return reportCreate(ctx, setter_report, location, address, images, func(ctx context.Context, txn bob.Executor, report_id int32) error { setter_water.ReportID = omit.From(report_id) _, err := models.PublicreportWaters.Insert(&setter_water).One(ctx, txn) if err != nil { @@ -108,7 +108,7 @@ func ReportWaterCreate(ctx context.Context, setter_report models.PublicreportRep type funcSetReportDetail = func(context.Context, bob.Executor, int32) error -func reportCreate(ctx context.Context, setter_report models.PublicreportReportSetter, latlng LatLng, address Address, images []ImageUpload, detail_setter funcSetReportDetail) (result *models.PublicreportReport, err error) { +func reportCreate(ctx context.Context, setter_report models.PublicreportReportSetter, location types.Location, address Address, images []ImageUpload, detail_setter funcSetReportDetail) (result *models.PublicreportReport, err error) { txn, err := db.PGInstance.BobDB.BeginTx(ctx, nil) if err != nil { return nil, fmt.Errorf("create txn: %w", err) @@ -123,10 +123,10 @@ func reportCreate(ctx context.Context, setter_report models.PublicreportReportSe // If we've got an locality value it was set by geocoding so we should save it var a *models.Address - if address.Locality != "" && latlng.Latitude != nil && latlng.Longitude != nil { + if address.Locality != "" && location.Latitude != 0 && location.Longitude != 0 { a, err = geocode.EnsureAddress(ctx, txn, address, types.Location{ - Latitude: *latlng.Latitude, - Longitude: *latlng.Longitude, + Latitude: location.Latitude, + Longitude: location.Longitude, }) if err != nil { return nil, fmt.Errorf("Failed to ensure address: %w", err) @@ -138,7 +138,7 @@ func reportCreate(ctx context.Context, setter_report models.PublicreportReportSe return nil, fmt.Errorf("Failed to save image uploads: %w", err) } var organization_id *int32 - organization_id, err = MatchDistrict(ctx, latlng.Longitude, latlng.Latitude, images) + organization_id, err = MatchDistrict(ctx, location.Longitude, location.Latitude, images) if err != nil { log.Warn().Err(err).Msg("Failed to match district") } @@ -153,9 +153,9 @@ func reportCreate(ctx context.Context, setter_report models.PublicreportReportSe if err != nil { return nil, fmt.Errorf("Failed to create report database record: %w", err) } - if latlng.Latitude != nil && latlng.Longitude != nil { - h3cell, _ := latlng.H3Cell() - geom_query, _ := latlng.GeometryQuery() + if location.Latitude != 0 && location.Longitude != 0 { + h3cell, _ := location.H3Cell() + geom_query, _ := location.GeometryQuery() _, err = psql.Update( um.Table("publicreport.report"), um.SetCol("h3cell").ToArg(h3cell), diff --git a/platform/types/address.go b/platform/types/address.go index ff41a983..a6e3fb67 100644 --- a/platform/types/address.go +++ b/platform/types/address.go @@ -8,11 +8,11 @@ import ( type Address struct { Country string `db:"country" json:"country"` - GID string `db:"gid" json:"gid"` + GID string `db:"gid" json:"gid" schema:"gid"` Locality string `db:"locality" json:"locality"` Number string `db:"number" json:"number"` PostalCode string `db:"postal_code" json:"postal_code"` - Raw string `db:"raw" json:"raw"` + Raw string `db:"raw" json:"raw" schema:"raw"` Region string `db:"region" json:"region"` Street string `db:"street" json:"street"` Unit string `db:"unit" json:"unit"` diff --git a/platform/types/location.go b/platform/types/location.go index d1cd4efb..b5fda97f 100644 --- a/platform/types/location.go +++ b/platform/types/location.go @@ -2,13 +2,33 @@ package types import ( "fmt" + + "github.com/Gleipnir-Technology/nidus-sync/h3utils" + //"github.com/rs/zerolog/log" + "github.com/uber/h3-go/v4" ) type Location struct { - Latitude float64 `db:"latitude" json:"latitude"` - Longitude float64 `db:"longitude" json:"longitude"` + Accuracy *float32 `db:"accuracy" json:"accuracy" schema:"accuracy"` + Latitude float64 `db:"latitude" json:"latitude" schema:"latitude"` + Longitude float64 `db:"longitude" json:"longitude" schema:"longitude"` } func (l Location) String() string { return fmt.Sprintf("%f %f", l.Longitude, l.Latitude) } + +func (l Location) Resolution() uint { + if l.Accuracy != nil { + return uint(h3utils.MeterAccuracyToH3Resolution(float64(*l.Accuracy))) + } else { + return uint(0) + } +} +func (l Location) H3Cell() (*h3.Cell, error) { + result, err := h3utils.GetCell(l.Longitude, l.Latitude, int(l.Resolution())) + return &result, err +} +func (l Location) GeometryQuery() (string, error) { + return fmt.Sprintf("ST_Point(%f, %f, 4326)", l.Longitude, l.Latitude), nil +} diff --git a/resource/nuisance.go b/resource/nuisance.go index 7166fe1c..c1aa72bc 100644 --- a/resource/nuisance.go +++ b/resource/nuisance.go @@ -2,10 +2,8 @@ package resource import ( "context" - "fmt" "net/http" "slices" - "strconv" "time" "github.com/Gleipnir-Technology/nidus-sync/db/enums" @@ -13,6 +11,7 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/html" nhttp "github.com/Gleipnir-Technology/nidus-sync/http" "github.com/Gleipnir-Technology/nidus-sync/platform" + "github.com/Gleipnir-Technology/nidus-sync/platform/types" "github.com/aarondl/opt/omit" "github.com/aarondl/opt/omitnull" "github.com/rs/zerolog/log" @@ -32,84 +31,25 @@ type nuisance struct { ID string `json:"id"` URI string `json:"uri"` } -type nuisanceForm struct { - AdditionalInfo string `schema:"additional-info"` - AddressGID string `schema:"address-gid"` - Address string `schema:"address"` - Duration string `schema:"duration"` - Latitude string `schema:"latitude"` - Longitude string `schema:"longitude"` - LatlngAccuracyType string `schema:"latlng-accuracy-type"` - LatlngAccuracyValue string `schema:"latlng-accuracy-value"` - MapZoom string `schema:"map-zoom"` - SourceStagnant bool `schema:"source-stagnant"` - SourceContainer bool `schema:"source-container"` - SourceDescription string `schema:"source-description"` - SourceGutters bool `schema:"source-gutters"` - SourceLocations []string `schema:"source-location"` - TODEarly bool `schema:"tod-early"` - TODDay bool `schema:"tod-day"` - TODEvening bool `schema:"tod-evening"` - TODNight bool `schema:"tod-night"` +type Locator struct { + Address types.Address `schema:"address"` + Location types.Location `schema:"location"` } - -func parseLatLng(r *http.Request) (platform.LatLng, error) { - result := platform.LatLng{ - AccuracyType: enums.PublicreportAccuracytypeNone, - AccuracyValue: 0.0, - Latitude: nil, - Longitude: nil, - MapZoom: 0.0, - } - latitude_str := r.FormValue("latitude") - longitude_str := r.FormValue("longitude") - latlng_accuracy_type_str := r.PostFormValue("latlng-accuracy-type") - latlng_accuracy_value_str := r.PostFormValue("latlng-accuracy-value") - map_zoom_str := r.PostFormValue("map-zoom") - - var err error - if latlng_accuracy_type_str != "" { - err := result.AccuracyType.Scan(latlng_accuracy_type_str) - if err != nil { - return result, fmt.Errorf("Failed to parse accuracy type '%s': %w", latlng_accuracy_type_str, err) - } - } - if latlng_accuracy_value_str != "" { - var t float64 - t, err = strconv.ParseFloat(latlng_accuracy_value_str, 32) - if err != nil { - return result, fmt.Errorf("Failed to parse latlng_accuracy_value '%s': %w", latlng_accuracy_value_str, err) - } - result.AccuracyValue = float64(t) - } - - if latitude_str != "" { - var t float64 - t, err = strconv.ParseFloat(latitude_str, 64) - if err != nil { - return result, fmt.Errorf("Failed to parse latitude '%s': %w", latitude_str, err) - } - result.Latitude = &t - } - if longitude_str != "" { - var t float64 - t, err := strconv.ParseFloat(longitude_str, 64) - if err != nil { - return result, fmt.Errorf("Failed to parse longitude '%s': %w", longitude_str, err) - } - result.Longitude = &t - } - - if map_zoom_str != "" { - var t float64 - t, err = strconv.ParseFloat(map_zoom_str, 32) - if err != nil { - return result, fmt.Errorf("Failed to parse map_zoom_str '%s': %w", map_zoom_str, err) - } else { - result.MapZoom = float32(t) - } - } - return result, nil +type nuisanceForm struct { + AdditionalInfo string `schema:"additional-info"` + Duration string `schema:"duration"` + Location types.Location `schema:"location"` + Locator Locator `schema:"locator"` + MapZoom string `schema:"map-zoom"` + SourceStagnant bool `schema:"source-stagnant"` + SourceContainer bool `schema:"source-container"` + SourceDescription string `schema:"source-description"` + SourceGutters bool `schema:"source-gutters"` + SourceLocations []string `schema:"source-location"` + TODEarly bool `schema:"tod-early"` + TODDay bool `schema:"tod-day"` + TODEvening bool `schema:"tod-evening"` + TODNight bool `schema:"tod-night"` } func (res *nuisanceR) Create(ctx context.Context, r *http.Request, n nuisanceForm) (*nuisance, *nhttp.ErrorWithStatus) { @@ -120,22 +60,25 @@ func (res *nuisanceR) Create(ctx context.Context, r *http.Request, n nuisanceFor is_location_pool := slices.Contains(n.SourceLocations, "pool-area") is_location_other := slices.Contains(n.SourceLocations, "other") - latlng, err := parseLatLng(r) - - err = duration.Scan(n.Duration) + err := duration.Scan(n.Duration) if err != nil { log.Warn().Err(err).Str("duration_str", n.Duration).Msg("Failed to interpret 'duration'") } uploads, err := html.ExtractImageUploads(r) - log.Info().Int("len", len(uploads)).Msg("extracted uploads") + log.Info().Int("len", len(uploads)).Msg("extracted nuisance uploads") if err != nil { return nil, nhttp.NewError("Failed to extract image uploads: %w", err) } address := platform.Address{ - GID: n.AddressGID, - Raw: n.Address, + GID: n.Locator.Address.GID, + Raw: n.Locator.Address.Raw, } + accuracy := float32(0.0) + if n.Location.Accuracy != nil { + accuracy = *n.Location.Accuracy + } + log.Info().Str("address.raw", address.Raw).Str("address.gid", address.GID).Msg("making nuisance") setter_report := models.PublicreportReportSetter{ //AddressID: omitnull.From(latlng.Cell.String()), AddressCountry: omit.From(""), @@ -148,11 +91,11 @@ func (res *nuisanceR) Create(ctx context.Context, r *http.Request, n nuisanceFor AddressStreet: omit.From(""), Created: omit.From(time.Now()), //H3cell: omitnull.From(latlng.Cell.String()), - LatlngAccuracyType: omit.From(latlng.AccuracyType), - LatlngAccuracyValue: omit.From(float32(latlng.AccuracyValue)), + LatlngAccuracyType: omit.From(enums.PublicreportAccuracytypeBrowser), + LatlngAccuracyValue: omit.From(accuracy), //Location: omitnull.From(fmt.Sprintf("ST_GeometryFromText(Point(%s %s))", longitude, latitude)), Location: omitnull.FromPtr[string](nil), - MapZoom: omit.From(latlng.MapZoom), + MapZoom: omit.From(float32(0.0)), //OrganizationID: omitnull.FromPtr(organization_id), //PublicID: omit.From(public_id), ReporterEmail: omit.From(""), @@ -179,7 +122,7 @@ func (res *nuisanceR) Create(ctx context.Context, r *http.Request, n nuisanceFor TodEvening: omit.From(n.TODEvening), TodNight: omit.From(n.TODNight), } - report, err := platform.ReportNuisanceCreate(ctx, setter_report, setter_nuisance, latlng, address, uploads) + report, err := platform.ReportNuisanceCreate(ctx, setter_report, setter_nuisance, n.Location, address, uploads) if err != nil { return nil, nhttp.NewError("create nuisance report: %w", err) } diff --git a/resource/water.go b/resource/water.go index 948b25e7..d5e97021 100644 --- a/resource/water.go +++ b/resource/water.go @@ -10,9 +10,10 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/html" nhttp "github.com/Gleipnir-Technology/nidus-sync/http" "github.com/Gleipnir-Technology/nidus-sync/platform" + "github.com/Gleipnir-Technology/nidus-sync/platform/types" "github.com/aarondl/opt/omit" - //"github.com/aarondl/opt/omitnull" - //"github.com/rs/zerolog/log" + "github.com/aarondl/opt/omitnull" + "github.com/rs/zerolog/log" ) func Water(r *router) *waterR { @@ -25,69 +26,64 @@ type waterR struct { router *router } type water struct { - ID string `json:"id"` + District string `json:"district"` + ID string `json:"id"` + URI string `json:"uri"` } type waterForm struct { - AccessComments string `schema:"access-comments"` - AccessDog bool `schema:"access-dog"` - AccessFence bool `schema:"access-fence"` - AccessGate bool `schema:"access-gate"` - AccessLocked bool `schema:"access-locked"` - AccessOther bool `schema:"access-other"` - AddressRaw string `schema:"address"` - AddressCountry string `schema:"address-country"` - AddressLocality string `schema:"address-locality"` - AddressNumber string `schema:"address-number"` - AddressPostalCode string `schema:"address-postalcode"` - AddressRegion string `schema:"address-region"` - AddressStreet string `schema:"address-street"` - Comments string `schema:"comments"` - HasAdult bool `schema:"has-adult"` - HasBackyardPermission bool `schema:"backyard-permission"` - HasLarvae bool `schema:"has-larvae"` - HasPupae bool `schema:"has-pupae"` - IsReporterConfidential bool `schema:"reporter-confidential"` - IsReporter_owner bool `schema:"property-ownership"` - OwnerEmail string `schema:"owner-email"` - OwnerName string `schema:"owner-name"` - OwnerPhone string `schema:"owner-phone"` + AccessComments string `schema:"access-comments"` + AccessDog bool `schema:"access-dog"` + AccessFence bool `schema:"access-fence"` + AccessGate bool `schema:"access-gate"` + AccessLocked bool `schema:"access-locked"` + AccessOther bool `schema:"access-other"` + Address string `schema:"address"` + AddressGID string `schema:"address-gid"` + Comments string `schema:"comments"` + HasAdult bool `schema:"has-adult"` + HasBackyardPermission bool `schema:"backyard-permission"` + HasLarvae bool `schema:"has-larvae"` + HasPupae bool `schema:"has-pupae"` + IsReporterConfidential bool `schema:"reporter-confidential"` + IsReporter_owner bool `schema:"property-ownership"` + Location types.Location `schema:"location"` + Locator Locator `schema:"locator"` + OwnerEmail string `schema:"owner-email"` + OwnerName string `schema:"owner-name"` + OwnerPhone string `schema:"owner-phone"` } func (res *waterR) Create(ctx context.Context, r *http.Request, w waterForm) (*water, *nhttp.ErrorWithStatus) { - latlng, err := parseLatLng(r) - if err != nil { - return nil, nhttp.NewError("Failed to parse lat lng for water report: %w", err) - } uploads, err := html.ExtractImageUploads(r) + log.Info().Int("len", len(uploads)).Msg("extracted water uploads") if err != nil { return nil, nhttp.NewError("Failed to extract image uploads: %w", err) } address := platform.Address{ - Country: w.AddressCountry, - Locality: w.AddressLocality, - Number: w.AddressNumber, - PostalCode: w.AddressPostalCode, - Raw: w.AddressRaw, - Region: w.AddressRegion, - Street: w.AddressStreet, - Unit: "", + GID: w.AddressGID, + Raw: w.Address, + } + accuracy := float32(0.0) + if w.Location.Accuracy != nil { + accuracy = *w.Location.Accuracy } setter_report := models.PublicreportReportSetter{ AddressRaw: omit.From(address.Raw), - AddressCountry: omit.From(address.Country), - AddressNumber: omit.From(address.Number), - AddressLocality: omit.From(address.Locality), - AddressPostalCode: omit.From(address.PostalCode), - AddressRegion: omit.From(address.Region), - AddressStreet: omit.From(address.Street), + AddressCountry: omit.From(""), + AddressNumber: omit.From(""), + AddressLocality: omit.From(""), + AddressPostalCode: omit.From(""), + AddressRegion: omit.From(""), + AddressStreet: omit.From(""), Created: omit.From(time.Now()), //H3cell: omitnull.From(geospatial.Cell.String()), - LatlngAccuracyType: omit.From(latlng.AccuracyType), - LatlngAccuracyValue: omit.From(float32(latlng.AccuracyValue)), + LatlngAccuracyType: omit.From(enums.PublicreportAccuracytypeBrowser), + LatlngAccuracyValue: omit.From(accuracy), //Location: add later - MapZoom: omit.From(latlng.MapZoom), + Location: omitnull.FromPtr[string](nil), + MapZoom: omit.From(float32(0.0)), //OrganizationID: omitnull.FromPtr(organization_id), //PublicID: omit.From(public_id), ReporterEmail: omit.From(""), @@ -115,11 +111,21 @@ func (res *waterR) Create(ctx context.Context, r *http.Request, w waterForm) (*w OwnerPhone: omit.From(w.OwnerPhone), //ReportID omit.Val[int32] } - report, err := platform.ReportWaterCreate(ctx, setter_report, setter_water, latlng, address, uploads) + report, err := platform.ReportWaterCreate(ctx, setter_report, setter_water, w.Location, address, uploads) if err != nil { return nil, nhttp.NewError("Failed to save new report: %w", err) } + uri, err := res.router.IDStrToURI("publicreport.ByIDGet", report.PublicID) + if err != nil { + return nil, nhttp.NewError("generate uri: %w", err) + } + district_uri, err := res.router.IDToURI("district.ByIDGet", int(report.OrganizationID)) + if err != nil { + return nil, nhttp.NewError("generate district uri: %w", err) + } return &water{ - ID: report.PublicID, + District: district_uri, + ID: report.PublicID, + URI: uri, }, nil } diff --git a/scss/rmo/nuisance.scss b/scss/rmo/nuisance.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/scss/rmo/root.scss b/scss/rmo/root.scss deleted file mode 100644 index b98631f1..00000000 --- a/scss/rmo/root.scss +++ /dev/null @@ -1,39 +0,0 @@ -.banner { - display: block; -} -@include media-breakpoint-up(xs) { - .banner { - width: 100%; - } -} -@include media-breakpoint-up(xl) { - .banner { - height: 100%; - } -} -.service-card { - transition: transform 0.3s; - height: 100%; -} -.service-card:hover { - transform: translateY(-5px); - box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); -} -.district-logo { - max-height: 80px; - width: auto; -} -.quick-report-mobile { - background-color: #ff9800; -} -.quick-report-desktop { - background-color: #ffefd5; - border-left: 4px solid #ff9800; -} - -.banner-container { - position: relative; - width: 100%; - background-color: #f76436; - overflow: hidden; -} diff --git a/scss/rmo/status.scss b/scss/rmo/status.scss deleted file mode 100644 index da2c897d..00000000 --- a/scss/rmo/status.scss +++ /dev/null @@ -1,66 +0,0 @@ -.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; -} -.search-box { - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); - border-radius: 8px; -} -@media (max-width: 768px) { - .map-container { - height: 300px; - } -} -/* Base styles for circular checkboxes */ -.custom-circle-checkbox .form-check-input { - border-radius: 50%; - width: 20px; - height: 20px; - cursor: pointer; - margin-top: 0.25rem; - background-image: none; /* Remove Bootstrap's default checkmark */ - position: relative; -} - -/* Style for when checked */ -.custom-circle-checkbox .form-check-input:checked { - background-color: currentColor; - border-color: currentColor; -} - -/* Style for when unchecked - just an outline */ -.custom-circle-checkbox .form-check-input:not(:checked) { - background-color: transparent; -} - -/* Colors based on data attribute */ -.custom-circle-checkbox .form-check-input[data-color="danger"] { - border-color: $red; - color: $red; -} - -.custom-circle-checkbox .form-check-input[data-color="success"] { - border-color: $blue; - color: $blue; -} - -.custom-circle-checkbox .form-check-input[data-color="info"] { - border-color: $green; - color: $green; -} diff --git a/ts/components/AddressSuggestion.vue b/ts/components/AddressSuggestion.vue index 8bd7b3e7..5cda764e 100644 --- a/ts/components/AddressSuggestion.vue +++ b/ts/components/AddressSuggestion.vue @@ -30,7 +30,7 @@ width: 100%; max-height: 300px; overflow-y: auto; - z-index: 1000; + z-index: 3; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); background: white; } diff --git a/ts/components/MapLocator.vue b/ts/components/MapLocator.vue index 33899a32..b63d3a37 100644 --- a/ts/components/MapLocator.vue +++ b/ts/components/MapLocator.vue @@ -1,4 +1,4 @@ - + + diff --git a/ts/rmo/components/Header.vue b/ts/rmo/components/Header.vue index 29fadfaa..a15ac2c4 100644 --- a/ts/rmo/components/Header.vue +++ b/ts/rmo/components/Header.vue @@ -1,3 +1,24 @@ +