Tell the user when they don't give us an address

Issue: #25
This commit is contained in:
Eli Ribble 2026-05-24 21:27:16 +00:00
parent 34f4980ad5
commit f4756637d6
No known key found for this signature in database
5 changed files with 67 additions and 48 deletions

View file

@ -29,6 +29,8 @@ import (
"source.gleipnir.technology/Gleipnir/nidus-sync/platform/types" "source.gleipnir.technology/Gleipnir/nidus-sync/platform/types"
) )
var ErrNoAddress = errors.New("no-address")
// GenerateReportID creates a 12-character random string using only unambiguous // GenerateReportID creates a 12-character random string using only unambiguous
// capital letters and numbers // capital letters and numbers
func GenerateReportID() (string, error) { func GenerateReportID() (string, error) {
@ -366,7 +368,7 @@ func publicReportCreate(ctx context.Context, setter_report modelpublicreport.Rep
} }
addr = &geo_res.Address addr = &geo_res.Address
} else { } else {
return result, fmt.Errorf("empty address") return result, ErrNoAddress
} }
} }

View file

@ -2,6 +2,7 @@ package resource
import ( import (
"context" "context"
"errors"
"net/http" "net/http"
"slices" "slices"
"time" "time"
@ -125,6 +126,9 @@ func (res *nuisanceR) Create(ctx context.Context, r *http.Request, n nuisanceFor
} }
report, err := platform.PublicReportNuisanceCreate(ctx, setter_report, setter_nuisance, n.Location, n.Address, uploads) report, err := platform.PublicReportNuisanceCreate(ctx, setter_report, setter_nuisance, n.Location, n.Address, uploads)
if err != nil { if err != nil {
if errors.Is(err, platform.ErrNoAddress) {
return nil, nhttp.NewBadRequest("empty-address")
}
return nil, nhttp.NewError("create nuisance report: %w", err) return nil, nhttp.NewError("create nuisance report: %w", err)
} }
uri, err := res.router.IDStrToURI("publicreport.ByIDGetPublic", report.PublicID) uri, err := res.router.IDStrToURI("publicreport.ByIDGetPublic", report.PublicID)

View file

@ -1,80 +1,80 @@
<style scoped> <style scoped>
.error-notification { .error-notification {
background-color: #f8d7da; background-color: #f8d7da;
border: 1px solid #f5c6cb; border: 1px solid #f5c6cb;
border-radius: 0.375rem; border-radius: 0.375rem;
padding: 1rem 1.25rem; padding: 1rem 1.25rem;
margin-bottom: 1rem; margin-bottom: 1rem;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.75rem; gap: 0.75rem;
} }
.error-icon { .error-icon {
font-size: 1.25rem; font-size: 1.25rem;
flex-shrink: 0; flex-shrink: 0;
} }
.error-text { .error-text {
color: #721c24; color: #721c24;
flex: 1; flex: 1;
} }
.error-dismiss { .error-dismiss {
background: none; background: none;
border: none; border: none;
color: #721c24; color: #721c24;
font-size: 1.25rem; font-size: 1.25rem;
cursor: pointer; cursor: pointer;
padding: 0 0.25rem; padding: 0 0.25rem;
line-height: 1; line-height: 1;
opacity: 0.7; opacity: 0.7;
} }
.error-dismiss:hover { .error-dismiss:hover {
opacity: 1; opacity: 1;
} }
</style> </style>
<template> <template>
<div v-if="visible" class="error-notification" role="alert"> <div v-if="visible" class="error-notification" role="alert">
<span class="error-icon" aria-hidden="true"></span> <span class="error-text">{{ message }}</span>
<span class="error-text">{{ message }}</span> <button
<button v-if="dismissible"
v-if="dismissible" type="button"
type="button" class="error-dismiss"
class="error-dismiss" aria-label="Dismiss error"
aria-label="Dismiss error" @click="dismiss"
@click="dismiss" >
> ×
× </button>
</button> </div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { ref } from "vue";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
message?: string; message?: string;
dismissible?: boolean; dismissible?: boolean;
}>(), }>(),
{ {
message: "Something went wrong. Your request could not be completed. Please try again.", message:
dismissible: true, "Something went wrong. Your request could not be completed. Please try again.",
}, dismissible: true,
},
); );
const emit = defineEmits<{ const emit = defineEmits<{
dismissed: []; dismissed: [];
}>(); }>();
const visible = ref(true); const visible = ref(true);
function dismiss() { function dismiss() {
visible.value = false; visible.value = false;
emit("dismissed"); emit("dismissed");
} }
function show() { function show() {
visible.value = true; visible.value = true;
} }
defineExpose({ show, dismiss }); defineExpose({ show, dismiss });

View file

@ -477,6 +477,7 @@ import { useStoreLocal } from "@/store/local";
import { useStorePublicReport } from "@/store/publicreport"; import { useStorePublicReport } from "@/store/publicreport";
import type { Marker } from "@/types"; import type { Marker } from "@/types";
import { import {
type APIError,
type Geocode, type Geocode,
type GeocodeSuggestion, type GeocodeSuggestion,
type PublicReport, type PublicReport,
@ -535,7 +536,16 @@ async function doSubmit() {
// Don't set Content-Type, the browser should do it // Don't set Content-Type, the browser should do it
}); });
if (!resp.ok) { if (!resp.ok) {
errorMessage.value = "Something went wrong. Your request could not be completed. Please try again."; if (resp.status == 400) {
const data: APIError = (await resp.json()) as APIError;
if (data.message == "empty-address") {
errorMessage.value =
"You must provide either an address, or a location on the map, otherwise we don't have much to work with.";
}
} else {
errorMessage.value =
"Something went wrong. Your request could not be completed. Please try again.";
}
return; return;
} }
const data: PublicReport = (await resp.json()) as PublicReport; const data: PublicReport = (await resp.json()) as PublicReport;

View file

@ -31,6 +31,9 @@ export class Address {
public location?: Location, public location?: Location,
) {} ) {}
} }
export interface APIError {
message: string;
}
export interface TegolaURLs { export interface TegolaURLs {
nidus: string; nidus: string;
rmo: string; rmo: string;