2026-04-15 00:12:19 +00:00
|
|
|
<style scoped>
|
|
|
|
|
.map-container {
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
|
|
|
|
height: 500px;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
/* Prevent touch scrolling issues */
|
|
|
|
|
touch-action: pan-y pinch-zoom;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
2026-03-28 09:14:09 -07:00
|
|
|
<template>
|
2026-04-16 05:36:28 +00:00
|
|
|
<div class="mb-4">
|
|
|
|
|
<!-- No Selection State -->
|
|
|
|
|
<div
|
|
|
|
|
v-show="!selectedTask"
|
|
|
|
|
class="h-100 align-items-center justify-content-center text-muted"
|
|
|
|
|
>
|
|
|
|
|
<div class="text-center">
|
|
|
|
|
<i class="bi bi-cursor-fill" style="font-size: 48px"></i>
|
|
|
|
|
<p class="mt-2">Select an entry from the list to review</p>
|
|
|
|
|
</div>
|
2026-03-28 09:14:09 -07:00
|
|
|
</div>
|
2026-04-16 05:36:28 +00:00
|
|
|
<div v-show="selectedTask !== undefined">
|
2026-04-15 17:22:01 +00:00
|
|
|
<h4 class="mb-3">Entry #{{ selectedTask?.id ?? "" }} Details</h4>
|
2026-03-28 09:14:09 -07:00
|
|
|
|
2026-04-16 05:36:28 +00:00
|
|
|
<div class="row mb-3">
|
|
|
|
|
<label class="col-sm-3 col-form-label fw-bold">Address:</label>
|
|
|
|
|
<div class="col-sm-9">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
class="form-control"
|
|
|
|
|
:value="modelValue.address"
|
|
|
|
|
readonly
|
|
|
|
|
/>
|
2026-03-28 09:14:09 -07:00
|
|
|
</div>
|
2026-04-16 05:36:28 +00:00
|
|
|
</div>
|
2026-03-28 09:14:09 -07:00
|
|
|
|
2026-04-16 05:36:28 +00:00
|
|
|
<div class="row mb-3">
|
|
|
|
|
<label class="col-sm-3 col-form-label fw-bold">Longitude:</label>
|
|
|
|
|
<div class="col-sm-9">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
class="form-control"
|
|
|
|
|
v-model="modelValue.location.longitude"
|
|
|
|
|
:class="{
|
|
|
|
|
'border-warning':
|
|
|
|
|
modelValue.location.longitude !==
|
|
|
|
|
selectedTask?.pool?.location?.longitude,
|
|
|
|
|
}"
|
|
|
|
|
/>
|
2026-03-28 09:14:09 -07:00
|
|
|
</div>
|
2026-04-16 05:36:28 +00:00
|
|
|
</div>
|
2026-03-28 09:14:09 -07:00
|
|
|
|
2026-04-16 05:36:28 +00:00
|
|
|
<div class="row mb-3">
|
|
|
|
|
<label class="col-sm-3 col-form-label fw-bold">Latitude:</label>
|
|
|
|
|
<div class="col-sm-9">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
class="form-control"
|
|
|
|
|
v-model="modelValue.location.latitude"
|
|
|
|
|
:class="{
|
|
|
|
|
'border-warning':
|
|
|
|
|
modelValue.location.latitude !==
|
|
|
|
|
selectedTask?.pool?.location?.latitude,
|
|
|
|
|
}"
|
|
|
|
|
/>
|
2026-03-28 09:14:09 -07:00
|
|
|
</div>
|
2026-04-16 05:36:28 +00:00
|
|
|
</div>
|
2026-03-28 09:14:09 -07:00
|
|
|
|
2026-04-16 05:36:28 +00:00
|
|
|
<div class="row mb-3">
|
|
|
|
|
<label class="col-sm-3 col-form-label fw-bold">Pool Condition:</label>
|
|
|
|
|
<div class="col-sm-9">
|
|
|
|
|
<select
|
|
|
|
|
class="form-select"
|
|
|
|
|
v-model="modelValue.condition"
|
|
|
|
|
:class="{
|
|
|
|
|
'border-warning':
|
|
|
|
|
modelValue.condition !== selectedTask?.pool?.condition,
|
|
|
|
|
}"
|
|
|
|
|
>
|
|
|
|
|
<option value="">-- Select --</option>
|
|
|
|
|
<option value="blue">Blue</option>
|
|
|
|
|
<option value="dry">Dry</option>
|
|
|
|
|
<option value="false pool">False Pool</option>
|
|
|
|
|
<option value="unknown">Unknown</option>
|
|
|
|
|
<option value="green">Green</option>
|
|
|
|
|
<option value="murky">Murky</option>
|
|
|
|
|
</select>
|
2026-03-28 09:14:09 -07:00
|
|
|
</div>
|
2026-04-16 05:36:28 +00:00
|
|
|
</div>
|
2026-03-28 09:14:09 -07:00
|
|
|
|
2026-04-16 05:36:28 +00:00
|
|
|
<div class="row mb-3">
|
|
|
|
|
<label class="col-sm-3 col-form-label fw-bold">Owner Contact:</label>
|
|
|
|
|
<div class="col-sm-9">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
class="form-control"
|
2026-04-16 07:25:06 +00:00
|
|
|
v-model="modelValue.owner"
|
2026-04-16 05:36:28 +00:00
|
|
|
:class="{
|
|
|
|
|
'border-warning':
|
2026-04-16 07:25:06 +00:00
|
|
|
modelValue.owner !==
|
|
|
|
|
(selectedTask?.pool?.site.owner?.name ?? ''),
|
2026-04-16 05:36:28 +00:00
|
|
|
}"
|
|
|
|
|
/>
|
2026-03-28 09:14:09 -07:00
|
|
|
</div>
|
2026-04-16 05:36:28 +00:00
|
|
|
</div>
|
2026-03-28 09:14:09 -07:00
|
|
|
|
2026-04-16 05:36:28 +00:00
|
|
|
<div class="row mb-4">
|
|
|
|
|
<label class="col-sm-3 col-form-label fw-bold">
|
|
|
|
|
Resident Contact:
|
|
|
|
|
</label>
|
|
|
|
|
<div class="col-sm-9">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
class="form-control"
|
|
|
|
|
v-model="siteResident.name"
|
|
|
|
|
:class="{
|
|
|
|
|
'border-warning':
|
|
|
|
|
siteResident.name !==
|
|
|
|
|
(selectedTask?.pool?.site.resident?.name ?? ''),
|
|
|
|
|
}"
|
|
|
|
|
/>
|
2026-03-28 09:14:09 -07:00
|
|
|
</div>
|
2026-04-16 05:36:28 +00:00
|
|
|
</div>
|
2026-03-28 09:14:09 -07:00
|
|
|
</div>
|
2026-04-16 05:36:28 +00:00
|
|
|
</div>
|
2026-03-28 09:14:09 -07:00
|
|
|
|
2026-04-16 05:36:28 +00:00
|
|
|
<!-- Map Components -->
|
|
|
|
|
<div class="map-container" v-if="session.organization">
|
|
|
|
|
<MapLocator
|
|
|
|
|
@click="doPoolLocation"
|
|
|
|
|
:markers="mapMarkers"
|
|
|
|
|
:useSatellite="true"
|
|
|
|
|
v-model="mapCamera"
|
|
|
|
|
></MapLocator>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else>
|
|
|
|
|
<p>loading...</p>
|
|
|
|
|
</div>
|
2026-03-28 09:14:09 -07:00
|
|
|
|
2026-04-16 05:36:28 +00:00
|
|
|
<div class="map-container" v-if="session.organization && session.urls">
|
|
|
|
|
<MapProxiedArcgisTile
|
|
|
|
|
@click="doPoolLocation"
|
|
|
|
|
:markers="mapMarkers"
|
|
|
|
|
:organizationId="session.organization.id"
|
|
|
|
|
:tegola="session.urls!.tegola"
|
|
|
|
|
:urlTiles="session.urls!.tile"
|
|
|
|
|
v-model="_mapFlyoverCamera"
|
|
|
|
|
></MapProxiedArcgisTile>
|
2026-03-28 09:14:09 -07:00
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<script setup lang="ts">
|
2026-04-15 16:22:08 +00:00
|
|
|
import { computed, ref, watch } from "vue";
|
2026-04-15 00:12:19 +00:00
|
|
|
import MapLocator from "@/components/MapLocator.vue";
|
2026-03-28 09:14:09 -07:00
|
|
|
import MapProxiedArcgisTile from "@/components/MapProxiedArcgisTile.vue";
|
2026-03-31 14:52:53 +00:00
|
|
|
import { useSessionStore } from "@/store/session";
|
2026-04-09 01:02:25 +00:00
|
|
|
import type { MapClickEvent, Marker } from "@/types";
|
|
|
|
|
import { Bounds, Contact, Pool, ReviewTask, User } from "@/type/api";
|
2026-04-06 16:54:48 +00:00
|
|
|
import type { Location } from "@/type/api";
|
2026-04-15 00:12:19 +00:00
|
|
|
import { Camera } from "@/type/map";
|
2026-03-28 09:14:09 -07:00
|
|
|
|
2026-04-15 16:50:43 +00:00
|
|
|
interface Emits {
|
|
|
|
|
(e: "update:modelValue", value: ReviewTaskPoolForm): void;
|
|
|
|
|
}
|
2026-04-15 16:42:16 +00:00
|
|
|
export interface ReviewTaskPoolForm {
|
|
|
|
|
address: string;
|
|
|
|
|
condition: string;
|
|
|
|
|
location: Location;
|
|
|
|
|
owner: string;
|
|
|
|
|
resident: string;
|
|
|
|
|
}
|
2026-03-28 09:14:09 -07:00
|
|
|
interface Props {
|
2026-03-28 12:35:12 -07:00
|
|
|
loading: boolean;
|
|
|
|
|
mapBounds?: Bounds;
|
2026-04-15 16:22:08 +00:00
|
|
|
mapFlyoverCamera: Camera;
|
2026-03-28 12:35:12 -07:00
|
|
|
mapMarkers: Marker[];
|
2026-04-15 16:42:16 +00:00
|
|
|
modelValue: ReviewTaskPoolForm;
|
2026-04-16 05:36:28 +00:00
|
|
|
selectedTask: ReviewTask | undefined;
|
2026-03-28 09:14:09 -07:00
|
|
|
}
|
2026-04-15 16:50:43 +00:00
|
|
|
const emit = defineEmits<Emits>();
|
2026-04-15 00:12:19 +00:00
|
|
|
const mapCamera = ref<Camera>(new Camera());
|
2026-04-15 16:22:08 +00:00
|
|
|
const _mapFlyoverCamera = ref<Camera>(new Camera());
|
2026-03-28 09:14:09 -07:00
|
|
|
const props = defineProps<Props>();
|
2026-04-13 22:07:56 +00:00
|
|
|
const siteResident = ref<Contact>(new Contact());
|
2026-03-31 14:52:53 +00:00
|
|
|
const session = useSessionStore();
|
|
|
|
|
function doPoolLocation(event: MapClickEvent) {
|
2026-04-15 16:50:43 +00:00
|
|
|
emit("update:modelValue", {
|
|
|
|
|
address: props.modelValue.address,
|
|
|
|
|
condition: props.modelValue.condition,
|
|
|
|
|
location: event.location,
|
|
|
|
|
owner: props.modelValue.owner,
|
|
|
|
|
resident: props.modelValue.resident,
|
|
|
|
|
});
|
2026-03-28 12:35:12 -07:00
|
|
|
}
|
2026-04-15 16:22:08 +00:00
|
|
|
watch(
|
|
|
|
|
() => props.mapFlyoverCamera,
|
|
|
|
|
(newMapFlyoverCamera: Camera) => {
|
|
|
|
|
console.log("map flyover camera update", newMapFlyoverCamera);
|
|
|
|
|
_mapFlyoverCamera.value = newMapFlyoverCamera;
|
|
|
|
|
},
|
|
|
|
|
);
|
2026-03-28 09:14:09 -07:00
|
|
|
</script>
|