diff --git a/api/routes.go b/api/routes.go index 48ba55b8..62de89f3 100644 --- a/api/routes.go +++ b/api/routes.go @@ -83,6 +83,7 @@ func AddRoutes(r *mux.Router) { publicreport := resource.Publicreport(router) r.Handle("/publicreport/{id}", handlerJSON(publicreport.ByID)).Methods("GET").Name("publicreport.ByIDGet") r.Handle("/publicreport/{id}", handlerJSONPut(publicreport.Update)).Methods("PUT") + r.Handle("/publicreport/{id}/image", handlerFormPost(publicreport.ImageCreate)).Methods("POST") publicreport_notification := resource.PublicreportNotification(router) r.Handle("/publicreport-notification", handlerJSONPost(publicreport_notification.Create)).Methods("POST") diff --git a/platform/publicreport.go b/platform/publicreport.go index f78d3d79..42bdeafe 100644 --- a/platform/publicreport.go +++ b/platform/publicreport.go @@ -129,6 +129,38 @@ func ReportComplianceCreate(ctx context.Context, setter_report models.Publicrepo return nil }) } +func ReportImageCreate(ctx context.Context, report_id string, images []ImageUpload) error { + txn, err := db.PGInstance.BobDB.BeginTx(ctx, nil) + if err != nil { + return fmt.Errorf("create txn: %w", err) + } + defer txn.Rollback(ctx) + + report, err := reportFromID(ctx, report_id) + if err != nil { + return fmt.Errorf("report from ID: %w", err) + } + saved_images, err := saveImageUploads(ctx, txn, images) + if err != nil { + return fmt.Errorf("Failed to save image uploads: %w", err) + } + if len(saved_images) > 0 { + setters := make([]*models.PublicreportReportImageSetter, 0) + for _, image := range saved_images { + setters = append(setters, &models.PublicreportReportImageSetter{ + ImageID: omit.From(int32(image.ID)), + ReportID: omit.From(int32(report.ID)), + }) + } + _, err = models.PublicreportReportImages.Insert(bob.ToMods(setters...)).Exec(ctx, txn) + if err != nil { + return fmt.Errorf("Failed to save reference to images: %w", err) + } + log.Info().Int("len", len(images)).Msg("saved uploaded images") + } + txn.Commit(ctx) + return nil +} 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) diff --git a/resource/compliance.go b/resource/compliance.go index 8138b684..b18eea20 100644 --- a/resource/compliance.go +++ b/resource/compliance.go @@ -29,14 +29,8 @@ type compliance struct { ID string `json:"id"` URI string `json:"uri"` } -type complianceForm struct { - ClientID string `schema:"client_id"` - DistrictID string `schema:"district"` - Location *types.Location `schema:"location"` - Locator *Locator `schema:"locator"` -} -func (res *complianceR) Create(ctx context.Context, r *http.Request, n complianceForm) (*compliance, *nhttp.ErrorWithStatus) { +func (res *complianceR) Create(ctx context.Context, r *http.Request, n publicreportForm) (*compliance, *nhttp.ErrorWithStatus) { setter_report := models.PublicreportReportSetter{ //AddressID: omitnull.From(latlng.Cell.String()), AddressCountry: omit.From(""), diff --git a/resource/publicreport.go b/resource/publicreport.go index f918c7b0..8a3a7472 100644 --- a/resource/publicreport.go +++ b/resource/publicreport.go @@ -7,11 +7,12 @@ import ( "github.com/aarondl/opt/omit" //"github.com/aarondl/opt/omitnull" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "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/rs/zerolog/log" "github.com/gorilla/mux" + "github.com/rs/zerolog/log" ) type publicreportR struct { @@ -50,9 +51,35 @@ func (res *publicreportR) ByID(ctx context.Context, r *http.Request, query Query return report, nil } +type image struct { + Status string `json:"status"` +} + +func (res *publicreportR) ImageCreate(ctx context.Context, r *http.Request, n nuisanceForm) (*image, *nhttp.ErrorWithStatus) { + vars := mux.Vars(r) + public_id := vars["id"] + if public_id == "" { + return nil, nhttp.NewBadRequest("You must provide an ID") + } + + uploads, err := html.ExtractImageUploads(r) + log.Info().Int("len", len(uploads)).Msg("report image uploads") + if err != nil { + return nil, nhttp.NewError("Failed to extract image uploads: %w", err) + } + + platform.ReportImageCreate(ctx, public_id, uploads) + return &image{Status: "ok"}, nil +} + +type complianceForm struct { + Comments *string `schema:"comments"` +} + type publicreportForm struct { Address *types.Address `schema:"address"` ClientID string `schema:"client_id"` + Compliance *complianceForm `schema:"compliance"` DistrictID string `schema:"district"` Location *types.Location `schema:"location"` Locator *Locator `schema:"locator"` diff --git a/ts/rmo/view/Compliance.vue b/ts/rmo/view/Compliance.vue index ec00d3de..d67a5bd5 100644 --- a/ts/rmo/view/Compliance.vue +++ b/ts/rmo/view/Compliance.vue @@ -106,6 +106,7 @@ const compliance = ref({ uri: "", }); const isLoading = ref(true); +const isUploading = ref(false); const props = defineProps(); const report = ref(); const district = computedAsync(async (): Promise => { @@ -147,7 +148,7 @@ function doAddress() { }); } function doEvidence() { - console.log("evidence", compliance.value); + uploadImages(compliance.value.images); } function doContact() { console.log("contact", compliance.value.contact); @@ -193,6 +194,31 @@ async function updateReport(updates: ComplianceUpdate) { return; } } +async function uploadImages(images: Image[]) { + isUploading.value = true; + const formData = new FormData(); + images.map(async (image, index) => { + formData.append(`image[${index}]`, image.file, image.name); + }); + const url = `${compliance.value.uri}/image`; + const response = await fetch(url, { + body: formData, + method: "POST", + }); + if (!response.ok) { + const content = await response.text(); + console.error( + "Failed to POST images", + url, + response.status, + response.statusText, + content, + ); + isUploading.value = false; + return; + } + isUploading.value = false; +} onMounted(() => { const client_id = storeLocal.getClientID(); const report_uri = storeLocal.getExistingComplianceReportURI();