Migrate existing ts types from the API into the API module

This makes it possible to start hydrating the types into valid data
types like Dates which means I can get type safety guarantees when
displaying information.
This commit is contained in:
Eli Ribble 2026-04-09 00:25:21 +00:00
parent b2c24a0438
commit f88ca57d97
No known key found for this signature in database
39 changed files with 382 additions and 320 deletions

View file

@ -12,7 +12,7 @@
<script setup lang="ts">
import { onMounted } from "vue";
import { useSessionStore } from "@/store/session";
import { Session } from "@/types";
import { Session } from "@/type/api";
import Sidebar from "./components/layout/Sidebar.vue";
import MainContent from "./components/layout/MainContent.vue";

View file

@ -22,7 +22,7 @@
</template>
<script setup lang="ts">
import { User } from "@/types";
import { User } from "@/type/api";
interface Props {
user: User;

View file

@ -107,9 +107,9 @@
<h6><i class="bi bi-clock-history"></i> Activity Log</h6>
<div class="small">
<div
v-for="entry in selectedCommunication?.public_report?.log ||
[]"
:key="entry.created"
v-for="(entry, index) in selectedCommunication?.public_report
?.log || []"
:key="index"
class="border-start border-2 ps-2 mb-2"
>
<div v-if="entry.type === 'created'">
@ -147,7 +147,7 @@
<script setup lang="ts">
import { ref } from "vue";
import { Communication, User } from "@/types";
import { Communication, User } from "@/type/api";
interface Emits {
(e: "markSignal"): void;
(e: "markInvalid"): void;
@ -173,8 +173,8 @@ function applyMessageTemplate(template: string) {
messageText.value = templates[template as keyof typeof templates];
}
}
function formatDate(date: string) {
return new Date(date).toLocaleString();
function formatDate(date: Date) {
return date.toLocaleString();
}
function handleTemplateChange(event: Event) {
const target = event.target as HTMLSelectElement;

View file

@ -75,7 +75,8 @@ import { computed } from "vue";
import MapMultipoint from "@/components/MapMultipoint.vue";
import PublicreportCard from "@/components/PublicreportCard.vue";
import TimeRelative from "@/components/TimeRelative.vue";
import { Bounds, Communication, Marker, User } from "@/types";
import type { Bounds, Marker } from "@/types";
import type { Communication, User } from "@/type/api";
import { useSessionStore } from "@/store/session";
interface Emits {

View file

@ -144,7 +144,7 @@
import { computed, ref } from "vue";
import TimeRelative from "@/components/TimeRelative.vue";
import { formatAddress } from "@/format";
import { Communication, LogEntry, PublicReport } from "@/types";
import { Communication, LogEntry, PublicReport } from "@/type/api";
interface Props {
all: Communication[] | null;
@ -211,8 +211,8 @@ function filterMatchesLogEntry(filter: string, logs: LogEntry[]) {
}
function filterMatchesPublicReport(filter: string, pr: PublicReport) {
if (
pr.address_raw.includes(filter) ||
pr.public_id.includes(filter) ||
pr.address.raw.includes(filter) ||
pr.id.includes(filter) ||
filterMatchesLogEntry(filter, pr.log)
) {
return true;

View file

@ -97,7 +97,7 @@
<script setup lang="ts">
import { formatDistance } from "@/format";
import { Image } from "@/types";
import { Image } from "@/type/api";
interface Emits {
(e: "close"): void;

View file

@ -45,7 +45,7 @@ interface Props {
const emit = defineEmits<Emits>();
const props = withDefaults(defineProps<Props>(), {
// default bounds cover a bunch of the continental US
bounds: () => {
bounds: (): Bounds => {
return {
max: { longitude: -70, latitude: 50 },
min: { longitude: -125, latitude: 25 },

View file

@ -73,8 +73,8 @@ import PlanningColumnDetailEntry from "@/components/PlanningColumnDetailEntry.vu
import TimeRelative from "@/components/TimeRelative.vue";
import { shortAddress } from "@/format";
import { useSessionStore } from "@/store/session";
import { MapClickEvent, Marker, Signal } from "@/types";
import type { Location } from "@/type/api";
import { MapClickEvent, Marker } from "@/types";
import type { Location, Signal } from "@/type/api";
interface Props {
markers: Marker[];

View file

@ -20,7 +20,7 @@ import FlyoverPoolCard from "@/components/FlyoverPoolCard.vue";
import PublicreportCard from "@/components/PublicreportCard.vue";
import TimeRelative from "@/components/TimeRelative.vue";
import { shortAddress } from "@/format";
import { Signal } from "@/types";
import { Signal } from "@/type/api";
interface Props {
signal: Signal;
}

View file

@ -198,7 +198,7 @@
<script setup lang="ts">
import { ref } from "vue";
import PlanningColumnListEntry from "@/components/PlanningColumnListEntry.vue";
import { Followup, Lead, Signal } from "@/types";
import { Followup, Lead, Signal } from "@/type/api";
interface Emits {
(e: "refresh"): void;

View file

@ -22,7 +22,7 @@
<script setup lang="ts">
import { shortAddress } from "@/format";
import { Signal } from "@/types";
import { Signal } from "@/type/api";
interface Props {
selected: boolean;

View file

@ -25,7 +25,7 @@
Standing Water Report
</span>
</h5>
<small class="text-muted">Report ID: #{{ report.public_id }}</small>
<small class="text-muted">Report ID: #{{ report.id }}</small>
</div>
<span class="badge bg-secondary">
<TimeRelative :time="report.created" />
@ -316,7 +316,7 @@ import MapMultipoint from "@/components/MapMultipoint.vue";
import PublicreportCard from "@/components/PublicreportCard.vue";
import TimeRelative from "@/components/TimeRelative.vue";
import { formatAddress } from "@/format";
import { PublicReport } from "@/types";
import { PublicReport } from "@/type/api";
interface Emits {
(e: "viewImage", index: number): void;

View file

@ -56,7 +56,8 @@
<script setup lang="ts">
import MapMultipoint from "@/components/MapMultipoint.vue";
import MapProxiedArcgisTile from "@/components/MapProxiedArcgisTile.vue";
import { Changes, ReviewTask } from "@/types";
import { Changes } from "@/types";
import { ReviewTask } from "@/type/api";
interface Props {
changes: Changes;

View file

@ -149,15 +149,8 @@ import MapMultipoint from "@/components/MapMultipoint.vue";
import MapProxiedArcgisTile from "@/components/MapProxiedArcgisTile.vue";
import { formatAddress } from "@/format";
import { useSessionStore } from "@/store/session";
import {
Bounds,
Contact,
MapClickEvent,
Marker,
Pool,
ReviewTask,
User,
} from "@/types";
import type { Bounds, MapClickEvent, Marker } from "@/types";
import { Contact, Pool, ReviewTask, User } from "@/type/api";
import type { Location } from "@/type/api";
interface Props {

View file

@ -61,7 +61,7 @@
</template>
<script setup lang="ts">
import { formatAddress } from "@/format";
import { ReviewTask } from "@/types";
import { ReviewTask } from "@/type/api";
interface Emits {
(e: "doSelectTask", id: number): void;

View file

@ -14,8 +14,7 @@ export default defineComponent({
props: {
time: {
type: String,
default: "",
type: Date,
},
},
@ -56,7 +55,7 @@ export default defineComponent({
}
},
formatRelativeTime(timestamp: string): string {
formatRelativeTime(timestamp: Date): string {
const now = new Date();
const date = new Date(timestamp);
const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);

View file

@ -54,7 +54,7 @@ import { ref, onMounted, watch } from "vue";
import { computedAsync } from "@vueuse/core";
import Avatar from "@/components/Avatar.vue";
import { useUserStore } from "@/store/user";
import type { User } from "@/types";
import type { User } from "@/type/api";
interface Props {
modelValue?: User | null;

View file

@ -210,7 +210,7 @@ import { Tooltip, Popover } from "bootstrap";
import NavigationLink from "@/components/common/NavigationLink.vue";
import { SSEManager, type SSEMessage } from "@/SSEManager";
import { useSessionStore } from "@/store/session";
import type { Session } from "@/types";
import type { Session } from "@/type/api";
// Reactive state
const isCollapsed = ref(false);

View file

@ -51,7 +51,7 @@ import { onMounted, ref } from "vue";
import { useSessionStore } from "@/store/session";
import { useUserStore } from "@/store/user";
import UserSelector from "@/components/UserSelector.vue";
import type { Session, User } from "@/types";
import type { Session, User } from "@/type/api";
const session = useSessionStore();
const user = useUserStore();

View file

@ -523,14 +523,14 @@ import ImageUpload, { Image } from "@/components/ImageUpload.vue";
import MapLocator from "@/components/MapLocator.vue";
import { useGeocodeStore } from "@/store/geocode";
import { useLocationStore } from "@/store/location";
import { useStorePublicreport } from "@/store/publicreport";
import { useStorePublicReport } from "@/store/publicreport";
import type { Marker } from "@/types";
import type {
Address,
Geocode,
GeocodeSuggestion,
Location,
Publicreport,
PublicReport,
} from "@/type/api";
import type { Camera } from "@/type/map";
@ -546,7 +546,7 @@ const marker = ref<Marker | null>(null);
const showMore = ref<boolean>(false);
const selectedSuggestion = ref<GeocodeSuggestion | null>(null);
const locationStore = useLocationStore();
const storePublicreport = useStorePublicreport();
const storePublicReport = useStorePublicReport();
const geocode = useGeocodeStore();
const markers = computed((): Marker[] => {
if (marker.value) {
@ -637,8 +637,8 @@ async function doSubmit() {
body: formData,
// Don't set Content-Type, the borwser should do it
});
const data: Publicreport = (await resp.json()) as Publicreport;
storePublicreport.add(data);
const data: PublicReport = (await resp.json()) as PublicReport;
storePublicReport.add(data);
router.push("/submitted/" + data.id);
} catch (error) {
errorMessage.value =

View file

@ -12,6 +12,7 @@ export const useStoreDistrict = defineStore("district", () => {
// Actions
async function byURI(uri: string): Promise<District | undefined> {
let district = _byURI.value.get(uri);
console.log("district by uri", uri, district);
if (district) {
return district;
}
@ -45,6 +46,7 @@ export const useStoreDistrict = defineStore("district", () => {
const data: District[] = await response.json();
data.forEach((d: District) => {
_byURI.value.set(d.uri, d);
console.log("district", d.uri);
});
return data;
} catch (e) {

View file

@ -258,8 +258,8 @@ import { ref, onMounted } from "vue";
import { computedAsync } from "@vueuse/core";
import { useRouter } from "vue-router";
import { useStoreDistrict } from "@/rmo/store/district";
import { useStorePublicreport } from "@/store/publicreport";
import type { District, Publicreport } from "@/type/api";
import { useStorePublicReport } from "@/store/publicreport";
import type { District, PublicReport } from "@/type/api";
interface FormData {
name: string;
@ -286,10 +286,10 @@ const formData = ref<FormData>({
});
const router = useRouter();
const storeDistrict = useStoreDistrict();
const storePublicreport = useStorePublicreport();
const storePublicReport = useStorePublicReport();
const report = computedAsync(async (): Promise<Publicreport | undefined> => {
return await storePublicreport.byID(props.id);
const report = computedAsync(async (): Promise<PublicReport | undefined> => {
return await storePublicReport.byID(props.id);
});
const district = computedAsync(async (): Promise<District | undefined> => {
if (!(report.value && report.value.district)) {

View file

@ -42,7 +42,7 @@
}
</style>
<template>
<HeaderDistrict v-if="district" />
<HeaderDistrict :district="district" v-if="district" />
<Header v-else />
<div class="container my-4" v-if="report">
<!-- Report ID and Status Section -->
@ -83,7 +83,9 @@
<strong><i class="bi bi-images me-2"></i>Images:</strong>
<span>
{{
report.image_count > 0 ? report.image_count : "None provided"
report.images.length > 0
? report.images.length
: "None provided"
}}
</span>
</div>
@ -139,9 +141,9 @@ import Header from "@/rmo/components/Header.vue";
import HeaderDistrict from "@/components/HeaderDistrict.vue";
import MapLocatorDisplay from "@/components/MapLocatorDisplay.vue";
import { useStoreDistrict } from "@/rmo/store/district";
import { useStorePublicreport } from "@/store/publicreport";
import { useStorePublicReport } from "@/store/publicreport";
import type { Marker } from "@/types";
import type { District, Publicreport } from "@/type/api";
import type { District, PublicReport } from "@/type/api";
import { formatTimeRelative } from "@/format";
// Props
@ -151,10 +153,10 @@ interface Props {
const props = defineProps<Props>();
const storeDistrict = useStoreDistrict();
const storePublicreport = useStorePublicreport();
const storePublicReport = useStorePublicReport();
// Computed
const report = computedAsync(async (): Promise<Publicreport | undefined> => {
return await storePublicreport.byID(props.id);
const report = computedAsync(async (): Promise<PublicReport | undefined> => {
return await storePublicReport.byID(props.id);
});
const district = computedAsync(async (): Promise<District | undefined> => {
if (!(report.value && report.value.district)) {

View file

@ -1,8 +1,8 @@
import { defineStore } from "pinia";
import { ref, computed } from "vue";
import { Communication } from "../types";
import { SSEManager, SSEMessage } from "../SSEManager";
import { useSessionStore } from "./session";
import { ref } from "vue";
import { Communication } from "@/type/api";
import { SSEManager, SSEMessage } from "@/SSEManager";
import { useSessionStore } from "@/store/session";
export const useCommunicationStore = defineStore("communication", () => {
// State

View file

@ -1,19 +1,19 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { Publicreport, type PublicreportDTO } from "@/type/api";
import { PublicReport, type PublicReportDTO } from "@/type/api";
export const useStorePublicreport = defineStore("publicreport", () => {
export const useStorePublicReport = defineStore("publicreport", () => {
// State
const _byID = ref<Map<string, Publicreport>>(new Map());
const _byID = ref<Map<string, PublicReport>>(new Map());
const error = ref(null);
const loading = ref(false);
//const ongoingFetch = ref<Promise<Publicreport[]> | null>(null);
//const ongoingFetch = ref<Promise<PublicReport[]> | null>(null);
function add(pr: Publicreport) {
function add(pr: PublicReport) {
_byID.value.set(pr.id, pr);
}
// Actions
async function byID(id: string): Promise<Publicreport | undefined> {
async function byID(id: string): Promise<PublicReport | undefined> {
loading.value = true;
error.value = null;
try {
@ -23,8 +23,8 @@ export const useStorePublicreport = defineStore("publicreport", () => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const body: PublicreportDTO = await response.json();
const report = Publicreport.fromJSON(body);
const body: PublicReportDTO = await response.json();
const report = PublicReport.fromJSON(body);
_byID.value.set(id, report);
return report;
} catch (err) {

View file

@ -1,7 +1,7 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { SSEManager, SSEMessage } from "@/SSEManager";
import { ReviewTask } from "@/types";
import { ReviewTask } from "@/type/api";
import { useSessionStore } from "@/store/session";
export const useReviewTaskStore = defineStore("review-task", () => {

View file

@ -7,7 +7,7 @@ import {
SessionNotificationCounts,
URLs,
User,
} from "@/types";
} from "@/type/api";
export const useSessionStore = defineStore("session", () => {
// State

View file

@ -1,6 +1,6 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { Signal } from "@/types";
import { Signal } from "@/type/api";
import { SSEManager, type SSEMessage } from "@/SSEManager";
import { useSessionStore } from "@/store/session";

View file

@ -1,6 +1,6 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { Upload } from "@/types";
import { Upload } from "@/type/api";
import { SSEManager, type SSEMessage } from "@/SSEManager";
import { useSessionStore } from "@/store/session";

View file

@ -1,6 +1,6 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { User } from "@/types";
import { User } from "@/type/api";
import { SSEManager, type SSEMessage } from "@/SSEManager";
import { useSessionStore } from "@/store/session";

View file

@ -9,6 +9,15 @@ export interface Address {
street: string;
unit: string;
}
export interface Bounds {
min: Location;
max: Location;
}
export interface Contact {
has_email: boolean;
has_phone: boolean;
name?: string;
}
export interface District {
name: string;
phone_office: string;
@ -38,6 +47,26 @@ export interface LogEntryDTO {
type: string;
user_id: number;
}
export interface CSVPoolDetailCount {
existing: number;
new: number;
outside: number;
}
export interface CSVPoolError {
column: number;
line: number;
message: string;
}
export interface Followup {
description: string;
id: number;
title: string;
}
export interface Lead {
description: string;
id: number;
title: string;
}
export class LogEntry {
constructor(
public created: Date,
@ -54,43 +83,305 @@ export class LogEntry {
);
}
}
export interface PublicreportDTO {
export interface Exif {
created: string;
make: string;
model: string;
}
export interface Image {
distance_from_reporter_meters?: number;
exif: Exif;
exif_make: string;
exif_model: string;
exif_datetime: string;
location?: Location;
report_id: number;
url_content: string;
uuid: string;
}
export interface Nuisance {
additional_info: string;
duration: string;
is_location_backyard: boolean;
is_location_frontyard: boolean;
is_location_garden: boolean;
is_location_other: boolean;
is_location_pool: boolean;
source_container: boolean;
source_description: string;
source_gutter: boolean;
source_stagnant: boolean;
time_of_day_day: boolean;
time_of_day_early: boolean;
time_of_day_evening: boolean;
time_of_day_night: boolean;
}
export interface Water {
access_comments: string;
access_gate: boolean;
access_fence: boolean;
access_locked: boolean;
access_dog: boolean;
access_other: boolean;
comments: string;
has_adult: boolean;
has_backyard_permission: boolean;
has_larvae: boolean;
has_pupae: boolean;
is_reporter_confidential: boolean;
is_reporter_owner: boolean;
owner: Contact;
}
export interface PublicReportDTO {
address: Address;
created: string;
district: string;
id: string;
image_count: number;
images: Image[];
location: Location;
log: LogEntryDTO[];
nuisance: Nuisance;
reporter: Contact;
status: string;
type: string;
water: Water;
uri: string;
}
export class Publicreport {
export class PublicReport {
constructor(
public address: Address,
public created: Date,
public district: string,
public id: string,
public image_count: number,
public location: Location,
public images: Image[],
public log: LogEntry[],
public reporter: Contact,
public status: string,
public type: string,
public uri: string,
public location?: Location,
public nuisance?: Nuisance,
public water?: Water,
) {}
static fromJSON(json: PublicreportDTO): Publicreport {
return new Publicreport(
static fromJSON(json: PublicReportDTO): PublicReport {
return new PublicReport(
json.address,
new Date(json.created),
json.district,
json.id,
json.image_count,
json.location,
json.images,
json.log.map((l: LogEntryDTO) => LogEntry.fromJSON(l)),
json.reporter,
json.status,
json.type,
json.uri,
json.location,
json.nuisance,
json.water,
);
}
}
export interface CommunicationDTO {
created: string;
id: string;
public_report?: PublicReportDTO;
type: string;
}
export class Communication {
constructor(
public created: Date,
public id: string,
public type: string,
public public_report?: PublicReport,
) {}
static fromJSON(json: CommunicationDTO): Communication {
return new Communication(
new Date(json.created),
json.id,
json.type,
json.public_report == undefined
? undefined
: PublicReport.fromJSON(json.public_report),
);
}
}
export interface Pool {
condition: string;
id: number;
location: Location;
site: Site;
}
export interface SignalDTO {
address?: Address;
addressed?: string;
addressor?: number;
created: string;
creator: number;
id: number;
location: Location;
pool?: Pool;
report?: PublicReport;
species?: string;
type: string;
}
export class Signal {
constructor(
public created: Date,
public creator: number,
public id: number,
public location: Location,
public type: string,
public address?: Address,
public addressed?: string,
public addressor?: number,
public pool?: Pool,
public report?: PublicReport,
public species?: string,
) {}
static fromJSON(json: SignalDTO): Signal {
return new Signal(
new Date(json.created),
json.creator,
json.id,
json.location,
json.type,
json.address,
json.addressed,
json.addressor,
json.pool,
json.report,
json.species,
);
}
}
export interface Site {
address: Address;
created: string;
creator_id: number;
file_id: number;
id: number;
location: Location;
notes: string;
organization_id: number;
owner?: Contact;
parcel_id?: number;
resident?: Contact;
resident_owned: boolean;
tags: Map<string, string>;
version: number;
}
export interface ReviewTaskPool {
condition: string;
location: Location;
owner: Contact;
site: Site;
}
export interface ReviewTask {
address: Address;
addressed?: string;
addressor?: User;
created: string;
creator: User;
pool?: ReviewTaskPool;
id: number;
}
export interface UploadDTO {
created: string;
filename: string;
id: number;
recordcount: number;
status: string;
type: string;
csv_pool?: CSVPoolDetail;
}
export class Upload {
constructor(
public created: Date,
public filename: string,
public id: number,
public recordcount: number,
public status: string,
public type: string,
public csv_pool?: CSVPoolDetail,
) {}
static fromJSON(json: UploadDTO): Upload {
return new Upload(
new Date(json.created),
json.filename,
json.id,
json.recordcount,
json.status,
json.type,
json.csv_pool,
);
}
}
export interface UploadPoolRow {
address: Address;
condition: string;
errors: UploadPoolError[];
status: string;
tags: Map<string, string>;
}
export interface UploadPoolError {
column: number;
line: number;
message: string;
}
export interface CSVPoolDetail {
count: CSVPoolDetailCount;
errors: CSVPoolError[];
pools: UploadPoolRow[];
}
export interface User {
avatar: string;
display_name: string;
id: number;
initials: string;
is_active: boolean;
role: string;
tags: string[];
uri: string;
username: string;
}
export interface Organization {
id: number;
service_area?: Bounds;
}
export interface UserNotificationCounts {
communication: number;
home: number;
review: number;
}
export interface SessionNotificationCounts {
communication: number;
home: number;
review: number;
}
export interface Session {
impersonating?: string;
notifications: Notification[];
notification_counts: SessionNotificationCounts;
organization: Organization;
self: User;
urls: URLs;
}
export interface URLs {
api: URLsAPI;
tegola: string;
tile: string;
}
// Define interfaces matching your Go structs
interface URLsAPI {
avatar: string;
communication: string;
impersonation: string;
publicreport_message: string;
review_task: string;
signal: string;
upload: string;
user: string;
}

View file

@ -1,5 +1,5 @@
import type { Map as MapLibreMap } from "maplibre-gl";
import { Address, Location } from "@/type/api";
import { Location } from "@/type/api";
export interface Bounds {
min: Location;
@ -10,58 +10,6 @@ export interface Changes {
unchanged: string[];
}
export interface Communication {
created: string;
id: string;
public_report: PublicReport | null;
type: string;
}
export interface Contact {
has_email: boolean;
has_phone: boolean;
name?: string;
}
export interface CSVPoolDetailCount {
existing: number;
new: number;
outside: number;
}
export interface CSVPoolError {
column: number;
line: number;
message: string;
}
export interface CSVPoolDetail {
count: CSVPoolDetailCount;
errors: CSVPoolError[];
pools: UploadPoolRow[];
}
export interface Exif {
created: string;
make: string;
model: string;
}
export interface Followup {
description: string;
id: number;
title: string;
}
export interface Image {
distance_from_reporter_meters?: number;
exif: Exif;
exif_make: string;
exif_model: string;
exif_datetime: string;
location?: Location;
report_id: number;
url_content: string;
uuid: string;
}
export interface Lead {
description: string;
id: number;
title: string;
}
export interface LogEntry {
created: string;
id: number;
@ -82,177 +30,7 @@ export interface Marker {
location: Location;
}
export interface Nuisance {
additional_info: string;
duration: string;
is_location_backyard: boolean;
is_location_frontyard: boolean;
is_location_garden: boolean;
is_location_other: boolean;
is_location_pool: boolean;
source_container: boolean;
source_description: string;
source_gutter: boolean;
source_stagnant: boolean;
time_of_day_day: boolean;
time_of_day_early: boolean;
time_of_day_evening: boolean;
time_of_day_night: boolean;
}
export interface Organization {
id: number;
service_area?: Bounds;
}
export interface Point {
x: number;
y: number;
}
export interface Pool {
condition: string;
id: number;
location: Location;
site: Site;
}
export interface PublicReport {
address: Address;
address_raw: string;
created: string;
images: Image[];
location?: Location;
log: LogEntry[];
nuisance?: Nuisance;
public_id: string;
reporter: Contact;
status: string;
type: string;
water?: Water;
}
export interface Signal {
address?: Address;
addressed?: string;
addressor?: number;
created: string;
creator: number;
id: number;
location: Location;
pool?: Pool;
report?: PublicReport;
species?: string;
type: string;
}
export interface ReviewTask {
address: Address;
addressed?: string;
addressor?: User;
created: string;
creator: User;
pool?: ReviewTaskPool;
id: number;
}
export interface ReviewTaskPool {
condition: string;
location: Location;
owner: Contact;
site: Site;
}
export interface SessionNotificationCounts {
communication: number;
home: number;
review: number;
}
export interface Session {
impersonating?: string;
notifications: Notification[];
notification_counts: SessionNotificationCounts;
organization: Organization;
self: User;
urls: URLs;
}
export interface Site {
address: Address;
created: string;
creator_id: number;
file_id: number;
id: number;
location: Location;
notes: string;
organization_id: number;
owner?: Contact;
parcel_id?: number;
resident?: Contact;
resident_owned: boolean;
tags: Map<string, string>;
version: number;
}
export interface Upload {
created: string;
filename: string;
id: number;
recordcount: number;
status: string;
type: string;
csv_pool?: CSVPoolDetail;
}
export interface UploadPoolRow {
address: Address;
condition: string;
errors: UploadPoolError[];
status: string;
tags: Map<string, string>;
}
export interface UploadPoolError {
column: number;
line: number;
message: string;
}
export interface URLs {
api: URLsAPI;
tegola: string;
tile: string;
}
// Define interfaces matching your Go structs
interface URLsAPI {
avatar: string;
communication: string;
impersonation: string;
publicreport_message: string;
review_task: string;
signal: string;
upload: string;
user: string;
}
export interface User {
avatar: string;
display_name: string;
id: number;
initials: string;
is_active: boolean;
role: string;
tags: string[];
uri: string;
username: string;
}
export interface UserNotificationCounts {
communication: number;
home: number;
review: number;
}
export interface Water {
access_comments: string;
access_gate: boolean;
access_fence: boolean;
access_locked: boolean;
access_dog: boolean;
access_other: boolean;
comments: string;
has_adult: boolean;
has_backyard_permission: boolean;
has_larvae: boolean;
has_pupae: boolean;
is_reporter_confidential: boolean;
is_reporter_owner: boolean;
owner: Contact;
}

View file

@ -68,7 +68,8 @@ import ToastNotification from "@/components/ToastNotification.vue";
import { SSEManager } from "@/SSEManager";
import { useCommunicationStore } from "@/store/communication";
import { useSessionStore } from "@/store/session";
import { Bounds, Communication, Marker } from "@/types";
import type { Bounds, Marker } from "@/types";
import type { Communication } from "@/type/api";
const communication = useCommunicationStore();
const session = useSessionStore();

View file

@ -67,8 +67,8 @@ import ThreeColumn from "@/components/layout/ThreeColumn.vue";
import TimeRelative from "@/components/TimeRelative.vue";
import { useSignalStore } from "@/store/signal";
import { useSessionStore } from "@/store/session";
import { Lead, Point, Signal } from "@/types";
import type { Location } from "@/type/api";
import type { Point } from "@/types";
import type { Lead, Location, Signal } from "@/type/api";
// Refs
const mapTile = ref(null);

View file

@ -160,7 +160,7 @@
import { computed, onMounted } from "vue";
import TimeRelative from "@/components/TimeRelative.vue";
import { useUploadStore } from "@/store/upload";
import { Upload } from "@/types";
import { Upload } from "@/type/api";
const uploadStore = useUploadStore();
const uploads = computed((): Upload[] | null => {

View file

@ -305,7 +305,7 @@ import { useRouter } from "vue-router";
import MapMultipoint from "@/components/MapMultipoint.vue";
import { useUploadStore } from "@/store/upload";
import { useSessionStore } from "@/store/session";
import { CSVPoolDetail, CSVPoolError, Upload, UploadPoolRow } from "@/types";
import { CSVPoolDetail, CSVPoolError, Upload, UploadPoolRow } from "@/type/api";
interface ErrorMessage {
message: string;

View file

@ -103,7 +103,7 @@
import { onMounted, ref } from "vue";
import { computedAsync } from "@vueuse/core";
import { useUserStore } from "@/store/user";
import { User } from "@/types";
import { User } from "@/type/api";
interface URLConfiguration {
userAdd: string;

View file

@ -259,7 +259,7 @@ pre {
import { onMounted, ref, toRaw } from "vue";
import { useSessionStore } from "@/store/session";
import { useUserStore } from "@/store/user";
import { User } from "@/types";
import { User } from "@/type/api";
interface Props {
id: number;

View file

@ -98,15 +98,9 @@ import ThreeColumn from "@/components/layout/ThreeColumn.vue";
import ReviewPoolColumnAction from "@/components/ReviewPoolColumnAction.vue";
import ReviewPoolColumnDetail from "@/components/ReviewPoolColumnDetail.vue";
import ReviewPoolColumnList from "@/components/ReviewPoolColumnList.vue";
import {
Bounds,
Changes,
Contact,
MapClickEvent,
Marker,
ReviewTask,
} from "@/types";
import type { Location } from "@/type/api";
import type { Changes } from "@/types";
import { Contact, Location, ReviewTask } from "@/type/api";
import { Bounds, MapClickEvent, Marker } from "@/types";
interface FormData {
latitude: number;