From e2af49a323da5998ed961d617a07b757ea5686b7 Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Sat, 14 Mar 2026 01:14:30 +0000 Subject: [PATCH] Make lead creation and invalidation for public reports work The only thing wrong at this point that I can tell is that address aren't being correctly populated when I reverse geocode. --- api/lead.go | 95 ++------ api/publicreport.go | 41 ++++ api/review_task.go | 4 +- api/routes.go | 2 + api/signal.go | 28 +-- api/types.go | 5 - db/dbinfo/publicreport.report_location.bob.go | 56 ++++- db/enums/enums.bob.go | 13 +- .../00106_publicreport_location_org_id.sql | 72 ++++++ .../00107_lead_type_publicreport.sql | 4 + db/models/publicreport.report_location.bob.go | 96 ++++---- html/template/sync/communication-root.html | 212 +++++------------- platform/csv/flyover.go | 6 +- platform/csv/pool.go | 4 +- platform/geocode/geocode.go | 90 ++++---- platform/geom/geom.go | 6 +- platform/lead.go | 184 +++++++++++++++ platform/publicreport.go | 40 ++++ platform/publicreport/nuisance.go | 1 + platform/publicreport/water.go | 1 + platform/site.go | 64 ++++++ platform/type.go | 3 + platform/types/location.go | 8 + stadia/geocode_raw.go | 69 ++++++ ...tured_geocode.go => geocode_structured.go} | 27 ++- stadia/request.go | 6 + stadia/reverse_geocode.go | 49 ++++ 27 files changed, 821 insertions(+), 365 deletions(-) create mode 100644 api/publicreport.go create mode 100644 db/migrations/00106_publicreport_location_org_id.sql create mode 100644 db/migrations/00107_lead_type_publicreport.sql create mode 100644 platform/lead.go create mode 100644 platform/publicreport.go create mode 100644 platform/site.go create mode 100644 stadia/geocode_raw.go rename stadia/{structured_geocode.go => geocode_structured.go} (81%) create mode 100644 stadia/request.go create mode 100644 stadia/reverse_geocode.go diff --git a/api/lead.go b/api/lead.go index d274133f..af64a5ac 100644 --- a/api/lead.go +++ b/api/lead.go @@ -3,27 +3,14 @@ package api import ( "context" "net/http" - "time" - "github.com/Gleipnir-Technology/bob" - "github.com/Gleipnir-Technology/bob/dialect/psql" - "github.com/Gleipnir-Technology/bob/dialect/psql/sm" - "github.com/Gleipnir-Technology/bob/dialect/psql/um" - "github.com/Gleipnir-Technology/nidus-sync/db" - "github.com/Gleipnir-Technology/nidus-sync/db/enums" - "github.com/Gleipnir-Technology/nidus-sync/db/models" nhttp "github.com/Gleipnir-Technology/nidus-sync/http" "github.com/Gleipnir-Technology/nidus-sync/platform" - "github.com/Gleipnir-Technology/nidus-sync/platform/geom" - "github.com/aarondl/opt/omit" - "github.com/aarondl/opt/omitnull" - "github.com/rs/zerolog/log" - "github.com/stephenafamo/scan" ) type createLead struct { - PoolLocations map[int]Location `json:"pool_locations"` - SignalIDs []int `json:"signal_ids"` + PoolLocations map[int]platform.Location `json:"pool_locations"` + SignalIDs []int `json:"signal_ids"` } type createdLead struct { ID int32 `json:"id"` @@ -48,75 +35,21 @@ func postLeads(ctx context.Context, r *http.Request, user platform.User, req cre return nil, nhttp.NewErrorStatus(http.StatusBadRequest, "can't make a lead with multiple signals yet") } signal_id := req.SignalIDs[0] - txn, err := db.PGInstance.BobDB.BeginTx(ctx, nil) - defer txn.Rollback(ctx) - - if err != nil { - return nil, nhttp.NewError("start transaction: %w", err) - } - type _Row struct { - ID int32 `db:"site_id"` - } - site, err := bob.One(ctx, db.PGInstance.BobDB, psql.Select( - sm.Columns( - "pool.site_id AS site_id", - ), - sm.From("signal_pool"), - sm.InnerJoin("pool").OnEQ( - psql.Quote("signal_pool", "pool_id"), - psql.Quote("pool", "id"), - ), - sm.InnerJoin("site").On( - psql.Quote("pool", "site_id").EQ(psql.Quote("site", "id")), - ), - sm.Where(psql.Quote("signal_pool", "signal_id").EQ(psql.Arg(signal_id))), - sm.Where(psql.Quote("site", "organization_id").EQ(psql.Arg(user.Organization.ID()))), - ), scan.StructMapper[_Row]()) - if err != nil { - if err.Error() == "sql: no rows in result set" { - return nil, nhttp.NewErrorStatus(http.StatusBadRequest, "Can't make a lead from signal %d: %w", signal_id, err) - } - return nil, nhttp.NewError("failed getting site: %w", err) - } - - lead, err := models.Leads.Insert(&models.LeadSetter{ - Created: omit.From(time.Now()), - Creator: omit.From(int32(user.ID)), - // ID - OrganizationID: omit.From(int32(user.Organization.ID())), - SiteID: omitnull.From(site.ID), - Type: omit.From(enums.LeadtypeGreenPool), - }).One(ctx, txn) - if err != nil { - return nil, nhttp.NewError("failed to create lead: %w", err) - } - _, err = psql.Update( - um.Table("signal"), - um.SetCol("addressed").ToArg(time.Now()), - um.SetCol("addressor").ToArg(user.ID), - um.Where(psql.Quote("id").EQ(psql.Arg(signal_id))), - ).Exec(ctx, txn) - if err != nil { - return nil, nhttp.NewError("failed to update signal %d: %w", signal_id, err) - } - pool_location, ok := req.PoolLocations[signal_id] + var pool_location *platform.Location + l, ok := req.PoolLocations[signal_id] if ok { - log.Info().Float64("lat", pool_location.Latitude).Float64("lng", pool_location.Longitude).Msg("got pool location") - geom_query := geom.PostgisPointQuery(pool_location.Longitude, pool_location.Latitude) - _, err = psql.Update( - um.Table("pool"), - um.SetCol("geometry").To(geom_query), - um.From("signal_pool"), - um.Where(psql.Quote("signal_pool", "pool_id").EQ(psql.Quote("pool", "id"))), - um.Where(psql.Quote("signal_pool", "signal_id").EQ(psql.Arg(signal_id))), - ).Exec(ctx, txn) - if err != nil { - return nil, nhttp.NewError("failed to update pool through signal %d: %w", signal_id, err) - } + pool_location = &l + } + site_id, err := platform.SiteFromSignal(ctx, user, int32(signal_id)) + if err != nil || site_id == nil { + return nil, nhttp.NewError("site from signal: %w", err) + } + lead_id, err := platform.LeadCreate(ctx, user, int32(signal_id), *site_id, pool_location) + if err != nil || lead_id == nil { + return nil, nhttp.NewError("lead create: %w", err) } - txn.Commit(ctx) return &createdLead{ - ID: lead.ID, + ID: *lead_id, }, nil } diff --git a/api/publicreport.go b/api/publicreport.go new file mode 100644 index 00000000..9df70d0a --- /dev/null +++ b/api/publicreport.go @@ -0,0 +1,41 @@ +package api + +import ( + "context" + "net/http" + + "github.com/Gleipnir-Technology/nidus-sync/config" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" + "github.com/Gleipnir-Technology/nidus-sync/platform" +) + +type formPublicreportLead struct { + ReportID string `json:"reportID"` +} + +func postPublicreportLead(ctx context.Context, r *http.Request, user platform.User, req formPublicreportLead) (*createdLead, *nhttp.ErrorWithStatus) { + lead_id, err := platform.LeadCreateFromPublicreport(ctx, user, req.ReportID) + if err != nil { + return nil, nhttp.NewError("create lead: %w", err) + } + return &createdLead{ + ID: *lead_id, + }, nil +} + +type formPublicreportInvalid struct { + ReportID string `json:"reportID"` +} +type createdReport struct { + URI string `json:"uri"` +} + +func postPublicreportInvalid(ctx context.Context, r *http.Request, user platform.User, req formPublicreportLead) (*createdReport, *nhttp.ErrorWithStatus) { + err := platform.PublicreportInvalid(ctx, user, req.ReportID) + if err != nil { + return nil, nhttp.NewError("create lead: %w", err) + } + return &createdReport{ + URI: config.MakeURLNidus("/publicreport/%s", req.ReportID), + }, nil +} diff --git a/api/review_task.go b/api/review_task.go index 8085b35e..2e192cb8 100644 --- a/api/review_task.go +++ b/api/review_task.go @@ -22,7 +22,7 @@ type reviewTaskPool struct { Created time.Time `json:"created"` Creator platform.User `json:"creator"` ID int32 `json:"id"` - Location Location `json:"location"` + Location types.Location `json:"location"` Reviewed *time.Time `json:"addressed"` Reviewer *platform.User `json:"addressor"` } @@ -122,7 +122,7 @@ func listReviewTaskPool(ctx context.Context, r *http.Request, user platform.User Created: row.Created, Creator: *users_by_id[row.CreatorID], ID: row.ID, - Location: Location{ + Location: types.Location{ Latitude: row.Latitude, Longitude: row.Longitude, }, diff --git a/api/routes.go b/api/routes.go index 49f5f521..01dcbb01 100644 --- a/api/routes.go +++ b/api/routes.go @@ -21,6 +21,8 @@ func AddRoutes(r chi.Router) { r.Method("GET", "/leads", authenticatedHandlerJSON(listLead)) r.Method("POST", "/leads", authenticatedHandlerJSONPost(postLeads)) r.Method("GET", "/mosquito-source", auth.NewEnsureAuth(apiMosquitoSource)) + r.Method("POST", "/publicreport/invalid", authenticatedHandlerJSONPost(postPublicreportInvalid)) + r.Method("POST", "/publicreport/lead", authenticatedHandlerJSONPost(postPublicreportLead)) r.Method("POST", "/review/pool", authenticatedHandlerJSONPost(postReviewPool)) r.Method("GET", "/review-task/pool", authenticatedHandlerJSON(listReviewTaskPool)) r.Method("GET", "/service-request", auth.NewEnsureAuth(apiServiceRequest)) diff --git a/api/signal.go b/api/signal.go index ad0ccde4..6c900849 100644 --- a/api/signal.go +++ b/api/signal.go @@ -23,7 +23,7 @@ type signal struct { Created time.Time `json:"created"` Creator platform.User `json:"creator"` ID int32 `json:"id"` - Location Location `json:"location"` + Location types.Location `json:"location"` Species string `json:"species"` Title string `json:"title"` Type string `json:"type"` @@ -34,18 +34,18 @@ type contentListSignal struct { func listSignal(ctx context.Context, r *http.Request, user platform.User, query queryParams) (*contentListSignal, *nhttp.ErrorWithStatus) { type _Row struct { - Address types.Address `db:"address"` - Addressed *time.Time `db:"addressed"` - Addressor *int32 `db:"addressor"` - Created time.Time `db:"created"` - Creator int32 `db:"creator_id"` - ID int32 `db:"id"` - Latitude float64 `db:"latitude"` - Longitude float64 `db:"longitude"` - Location Location `db:"location"` - Species *string `db:"species"` - Title string `db:"title"` - Type string `db:"type"` + Address types.Address `db:"address"` + Addressed *time.Time `db:"addressed"` + Addressor *int32 `db:"addressor"` + Created time.Time `db:"created"` + Creator int32 `db:"creator_id"` + ID int32 `db:"id"` + Latitude float64 `db:"latitude"` + Longitude float64 `db:"longitude"` + Location types.Location `db:"location"` + Species *string `db:"species"` + Title string `db:"title"` + Type string `db:"type"` } limit := 20 if query.Limit != nil { @@ -118,7 +118,7 @@ func listSignal(ctx context.Context, r *http.Request, user platform.User, query Created: row.Created, Creator: *users_by_id[row.Creator], ID: row.ID, - Location: Location{ + Location: types.Location{ Latitude: row.Latitude, Longitude: row.Longitude, }, diff --git a/api/types.go b/api/types.go index 29e9256d..eebc2855 100644 --- a/api/types.go +++ b/api/types.go @@ -34,11 +34,6 @@ func NewBounds() Bounds { } } -type Location struct { - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` -} - type NoteImagePayload struct { UUID string `json:"uuid"` Cell H3Cell `json:"cell"` diff --git a/db/dbinfo/publicreport.report_location.bob.go b/db/dbinfo/publicreport.report_location.bob.go index f61aa376..398c63b0 100644 --- a/db/dbinfo/publicreport.report_location.bob.go +++ b/db/dbinfo/publicreport.report_location.bob.go @@ -31,6 +31,15 @@ var PublicreportReportLocations = Table[ Generated: false, AutoIncr: false, }, + AddressID: column{ + Name: "address_id", + DBType: "integer", + Default: "NULL", + Comment: "", + Nullable: true, + Generated: false, + AutoIncr: false, + }, AddressRaw: column{ Name: "address_raw", DBType: "text", @@ -58,6 +67,33 @@ var PublicreportReportLocations = Table[ Generated: false, AutoIncr: false, }, + LocationLatitude: column{ + Name: "location_latitude", + DBType: "double precision", + Default: "NULL", + Comment: "", + Nullable: true, + Generated: false, + AutoIncr: false, + }, + LocationLongitude: column{ + Name: "location_longitude", + DBType: "double precision", + Default: "NULL", + Comment: "", + Nullable: true, + Generated: false, + AutoIncr: false, + }, + OrganizationID: column{ + Name: "organization_id", + DBType: "integer", + Default: "NULL", + Comment: "", + Nullable: true, + Generated: false, + AutoIncr: false, + }, PublicID: column{ Name: "public_id", DBType: "text", @@ -82,18 +118,22 @@ var PublicreportReportLocations = Table[ } type publicreportReportLocationColumns struct { - ID column - TableName column - AddressRaw column - Created column - Location column - PublicID column - Status column + ID column + TableName column + AddressID column + AddressRaw column + Created column + Location column + LocationLatitude column + LocationLongitude column + OrganizationID column + PublicID column + Status column } func (c publicreportReportLocationColumns) AsSlice() []column { return []column{ - c.ID, c.TableName, c.AddressRaw, c.Created, c.Location, c.PublicID, c.Status, + c.ID, c.TableName, c.AddressID, c.AddressRaw, c.Created, c.Location, c.LocationLatitude, c.LocationLongitude, c.OrganizationID, c.PublicID, c.Status, } } diff --git a/db/enums/enums.bob.go b/db/enums/enums.bob.go index 3a3bebb1..bf23bbd6 100644 --- a/db/enums/enums.bob.go +++ b/db/enums/enums.bob.go @@ -1294,12 +1294,18 @@ func (e *Imagedatatype) Scan(value any) error { // Enum values for Leadtype const ( - LeadtypeGreenPool Leadtype = "green-pool" + LeadtypeUnknown Leadtype = "unknown" + LeadtypeGreenPool Leadtype = "green-pool" + LeadtypePublicreportNuisance Leadtype = "publicreport-nuisance" + LeadtypePublicreportWater Leadtype = "publicreport-water" ) func AllLeadtype() []Leadtype { return []Leadtype{ + LeadtypeUnknown, LeadtypeGreenPool, + LeadtypePublicreportNuisance, + LeadtypePublicreportWater, } } @@ -1311,7 +1317,10 @@ func (e Leadtype) String() string { func (e Leadtype) Valid() bool { switch e { - case LeadtypeGreenPool: + case LeadtypeUnknown, + LeadtypeGreenPool, + LeadtypePublicreportNuisance, + LeadtypePublicreportWater: return true default: return false diff --git a/db/migrations/00106_publicreport_location_org_id.sql b/db/migrations/00106_publicreport_location_org_id.sql new file mode 100644 index 00000000..e109120c --- /dev/null +++ b/db/migrations/00106_publicreport_location_org_id.sql @@ -0,0 +1,72 @@ +-- +goose Up +DROP VIEW publicreport.report_location; +CREATE VIEW publicreport.report_location AS +SELECT + ROW_NUMBER() OVER (ORDER BY table_name, public_id) AS id, + table_name, + address_id, + address_raw, + created, + location, + location_latitude, + location_longitude, + organization_id, + public_id, + status +FROM ( + SELECT + 'nuisance' AS table_name, + address_id, + address_raw, + created, + location, + ST_X(location) AS location_longitude, + ST_Y(location) AS location_latitude, + organization_id, + public_id, + status + FROM publicreport.nuisance + UNION + SELECT + 'water' AS table_name, + address_id, + address_raw, + created, + location, + ST_X(location) AS location_longitude, + ST_Y(location) AS location_latitude, + organization_id, + public_id, + status + FROM publicreport.water +) AS combined_data; +-- +goose Down +DROP VIEW publicreport.report_location; +CREATE VIEW publicreport.report_location AS +SELECT + ROW_NUMBER() OVER (ORDER BY table_name, public_id) AS id, + table_name, + address_raw, + created, + location, + public_id, + status +FROM ( + SELECT + 'nuisance' AS table_name, + address_raw, + created, + location, + public_id, + status + FROM publicreport.nuisance + UNION + SELECT + 'water' AS table_name, + address_raw, + created, + location, + public_id, + status + FROM publicreport.water +) AS combined_data; diff --git a/db/migrations/00107_lead_type_publicreport.sql b/db/migrations/00107_lead_type_publicreport.sql new file mode 100644 index 00000000..09422230 --- /dev/null +++ b/db/migrations/00107_lead_type_publicreport.sql @@ -0,0 +1,4 @@ +-- +goose Up +ALTER TYPE LeadType ADD VALUE 'unknown' BEFORE 'green-pool'; +ALTER TYPE LeadType ADD VALUE 'publicreport-nuisance' AFTER 'green-pool'; +ALTER TYPE LeadType ADD VALUE 'publicreport-water' AFTER 'publicreport-nuisance'; diff --git a/db/models/publicreport.report_location.bob.go b/db/models/publicreport.report_location.bob.go index a2512c1f..d6f268fe 100644 --- a/db/models/publicreport.report_location.bob.go +++ b/db/models/publicreport.report_location.bob.go @@ -16,13 +16,17 @@ import ( // PublicreportReportLocation is an object representing the database table. type PublicreportReportLocation struct { - ID null.Val[int64] `db:"id" ` - TableName null.Val[string] `db:"table_name" ` - AddressRaw null.Val[string] `db:"address_raw" ` - Created null.Val[time.Time] `db:"created" ` - Location null.Val[string] `db:"location" ` - PublicID null.Val[string] `db:"public_id" ` - Status null.Val[enums.PublicreportReportstatustype] `db:"status" ` + ID null.Val[int64] `db:"id" ` + TableName null.Val[string] `db:"table_name" ` + AddressID null.Val[int32] `db:"address_id" ` + AddressRaw null.Val[string] `db:"address_raw" ` + Created null.Val[time.Time] `db:"created" ` + Location null.Val[string] `db:"location" ` + LocationLatitude null.Val[float64] `db:"location_latitude" ` + LocationLongitude null.Val[float64] `db:"location_longitude" ` + OrganizationID null.Val[int32] `db:"organization_id" ` + PublicID null.Val[string] `db:"public_id" ` + Status null.Val[enums.PublicreportReportstatustype] `db:"status" ` } // PublicreportReportLocationSlice is an alias for a slice of pointers to PublicreportReportLocation. @@ -38,29 +42,37 @@ type PublicreportReportLocationsQuery = *psql.ViewQuery[*PublicreportReportLocat func buildPublicreportReportLocationColumns(alias string) publicreportReportLocationColumns { return publicreportReportLocationColumns{ ColumnsExpr: expr.NewColumnsExpr( - "id", "table_name", "address_raw", "created", "location", "public_id", "status", + "id", "table_name", "address_id", "address_raw", "created", "location", "location_latitude", "location_longitude", "organization_id", "public_id", "status", ).WithParent("publicreport.report_location"), - tableAlias: alias, - ID: psql.Quote(alias, "id"), - TableName: psql.Quote(alias, "table_name"), - AddressRaw: psql.Quote(alias, "address_raw"), - Created: psql.Quote(alias, "created"), - Location: psql.Quote(alias, "location"), - PublicID: psql.Quote(alias, "public_id"), - Status: psql.Quote(alias, "status"), + tableAlias: alias, + ID: psql.Quote(alias, "id"), + TableName: psql.Quote(alias, "table_name"), + AddressID: psql.Quote(alias, "address_id"), + AddressRaw: psql.Quote(alias, "address_raw"), + Created: psql.Quote(alias, "created"), + Location: psql.Quote(alias, "location"), + LocationLatitude: psql.Quote(alias, "location_latitude"), + LocationLongitude: psql.Quote(alias, "location_longitude"), + OrganizationID: psql.Quote(alias, "organization_id"), + PublicID: psql.Quote(alias, "public_id"), + Status: psql.Quote(alias, "status"), } } type publicreportReportLocationColumns struct { expr.ColumnsExpr - tableAlias string - ID psql.Expression - TableName psql.Expression - AddressRaw psql.Expression - Created psql.Expression - Location psql.Expression - PublicID psql.Expression - Status psql.Expression + tableAlias string + ID psql.Expression + TableName psql.Expression + AddressID psql.Expression + AddressRaw psql.Expression + Created psql.Expression + Location psql.Expression + LocationLatitude psql.Expression + LocationLongitude psql.Expression + OrganizationID psql.Expression + PublicID psql.Expression + Status psql.Expression } func (c publicreportReportLocationColumns) Alias() string { @@ -96,13 +108,17 @@ func (o PublicreportReportLocationSlice) AfterQueryHook(ctx context.Context, exe } type publicreportReportLocationWhere[Q psql.Filterable] struct { - ID psql.WhereNullMod[Q, int64] - TableName psql.WhereNullMod[Q, string] - AddressRaw psql.WhereNullMod[Q, string] - Created psql.WhereNullMod[Q, time.Time] - Location psql.WhereNullMod[Q, string] - PublicID psql.WhereNullMod[Q, string] - Status psql.WhereNullMod[Q, enums.PublicreportReportstatustype] + ID psql.WhereNullMod[Q, int64] + TableName psql.WhereNullMod[Q, string] + AddressID psql.WhereNullMod[Q, int32] + AddressRaw psql.WhereNullMod[Q, string] + Created psql.WhereNullMod[Q, time.Time] + Location psql.WhereNullMod[Q, string] + LocationLatitude psql.WhereNullMod[Q, float64] + LocationLongitude psql.WhereNullMod[Q, float64] + OrganizationID psql.WhereNullMod[Q, int32] + PublicID psql.WhereNullMod[Q, string] + Status psql.WhereNullMod[Q, enums.PublicreportReportstatustype] } func (publicreportReportLocationWhere[Q]) AliasedAs(alias string) publicreportReportLocationWhere[Q] { @@ -111,12 +127,16 @@ func (publicreportReportLocationWhere[Q]) AliasedAs(alias string) publicreportRe func buildPublicreportReportLocationWhere[Q psql.Filterable](cols publicreportReportLocationColumns) publicreportReportLocationWhere[Q] { return publicreportReportLocationWhere[Q]{ - ID: psql.WhereNull[Q, int64](cols.ID), - TableName: psql.WhereNull[Q, string](cols.TableName), - AddressRaw: psql.WhereNull[Q, string](cols.AddressRaw), - Created: psql.WhereNull[Q, time.Time](cols.Created), - Location: psql.WhereNull[Q, string](cols.Location), - PublicID: psql.WhereNull[Q, string](cols.PublicID), - Status: psql.WhereNull[Q, enums.PublicreportReportstatustype](cols.Status), + ID: psql.WhereNull[Q, int64](cols.ID), + TableName: psql.WhereNull[Q, string](cols.TableName), + AddressID: psql.WhereNull[Q, int32](cols.AddressID), + AddressRaw: psql.WhereNull[Q, string](cols.AddressRaw), + Created: psql.WhereNull[Q, time.Time](cols.Created), + Location: psql.WhereNull[Q, string](cols.Location), + LocationLatitude: psql.WhereNull[Q, float64](cols.LocationLatitude), + LocationLongitude: psql.WhereNull[Q, float64](cols.LocationLongitude), + OrganizationID: psql.WhereNull[Q, int32](cols.OrganizationID), + PublicID: psql.WhereNull[Q, string](cols.PublicID), + Status: psql.WhereNull[Q, enums.PublicreportReportstatustype](cols.Status), } } diff --git a/html/template/sync/communication-root.html b/html/template/sync/communication-root.html index 33d78d3b..ce53ca26 100644 --- a/html/template/sync/communication-root.html +++ b/html/template/sync/communication-root.html @@ -45,10 +45,7 @@ typeFilter: "all", messageText: "", showPhotoModal: false, - showInvalidModal: false, currentPhotoIndex: 0, - invalidReason: "", - invalidNotes: "", showToast: false, toastTitle: "", toastMessage: "", @@ -82,7 +79,7 @@ }); }, - async loadCommunications() { + async fetchCommunications() { try { // Build query parameters from filters const params = new URLSearchParams(); @@ -108,7 +105,7 @@ this.loading = true; this.error = null; try { - await Promise.all([this.loadCommunications()]); + await Promise.all([this.fetchCommunications()]); } catch (err) { this.error = err.message; console.error("Error loading data:", err); @@ -143,54 +140,66 @@ } }, - createLead() { - // TODO: Implement API call to create lead - console.log( - "Creating lead for report:", - this.selectedCommunication.id, - ); - - // Add to activity log - if (!this.selectedCommunication.history) { - this.selectedCommunication.history = []; + async createLead() { + try { + const payload = { + reportID: this.selectedCommunication.id, + }; + const response = await fetch(`api/publicreport/lead`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(payload), + }); + if (!response.ok) { + throw new Error("Failed to submit lead"); + } + // Remove from list after creating lead + this.removeCurrentFromList(); + this.fetchCommunications(); + } catch (err) { + this.error = err.message; + console.error("Error creating lead:", err); } - this.selectedCommunication.history.push({ - action: "Lead created", - timestamp: new Date(), - }); - - this.showNotification( - "Lead Created", - `Lead successfully created for report #${this.selectedCommunication.id}`, - ); - - // Remove from list after creating lead - // this.communications = this.communications.filter(r => r.id !== this.selectedCommunication.id); - // this.selectedCommunication = null; }, - markInvalid() { - // TODO: Implement API call to mark as invalid + async markInvalid() { console.log( "Marking report as invalid:", this.selectedCommunication.id, - this.invalidReason, - this.invalidNotes, ); + const payload = { + reportID: this.selectedCommunication.id, + }; + const response = await fetch(`api/publicreport/invalid`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(payload), + }); this.showNotification( "Report Marked Invalid", - `Report #${this.selectedCommunication.id} has been marked as ${this.invalidReason}`, + `Report #${this.selectedCommunication.id} has been marked as invalid`, ); - - // Remove from list - this.communications = this.communications.filter( - (r) => r.id !== this.selectedCommunication.id, + this.removeCurrentFromList(); + this.fetchCommunications(); + }, + removeCurrentFromList() { + const index = this.communications.findIndex( + (c) => c.id === this.selectedCommunication.id, ); - this.selectedCommunication = null; - this.showInvalidModal = false; - this.invalidReason = ""; - this.invalidNotes = ""; + if (index > -1) { + this.communications = this.communications.splice(index, 1); + } + if (this.communications.length > 0) { + const nextIndex = Math.min(index, this.communications.length - 1); + this.selectedCommunication = this.communications[nextIndex]; + } else { + this.selectedCommunication = null; + } }, sendMessage() { @@ -744,10 +753,7 @@
- @@ -921,122 +927,6 @@ @click="showPhotoModal = false" >
- - - -