169 lines
4 KiB
Vue
169 lines
4 KiB
Vue
<style scoped lang="scss">
|
|
body {
|
|
background-color: #f5f5f5;
|
|
}
|
|
|
|
.left-panel {
|
|
background-color: white;
|
|
height: 100vh;
|
|
overflow-y: auto;
|
|
border-right: 1px solid #dee2e6;
|
|
}
|
|
|
|
.middle-panel {
|
|
background-color: white;
|
|
height: 100vh;
|
|
overflow-y: auto;
|
|
padding: 20px;
|
|
}
|
|
|
|
.right-panel {
|
|
background-color: white;
|
|
height: 100vh;
|
|
overflow-y: auto;
|
|
border-left: 1px solid #dee2e6;
|
|
padding: 20px;
|
|
}
|
|
</style>
|
|
<template>
|
|
<ThreeColumn>
|
|
<template #left>
|
|
<ReviewSiteColumnList
|
|
@doSiteDeselect="siteDeselect"
|
|
@doSiteSelect="siteSelect"
|
|
:selectedSite="selectedSite"
|
|
:sites="storeSite.all()"
|
|
/>
|
|
</template>
|
|
<template #center>
|
|
<ReviewSiteColumnDetail
|
|
:mapFlyoverCamera="mapFlyoverCamera"
|
|
:mapMarkers="mapMarkers"
|
|
:selectedSite="selectedSite"
|
|
/>
|
|
</template>
|
|
<template #right>
|
|
<ReviewSiteColumnAction
|
|
@doRequestComplianceMailer="doRequestComplianceMailer"
|
|
:selectedSite="selectedSite"
|
|
:submitting="submitting"
|
|
/>
|
|
</template>
|
|
</ThreeColumn>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import maplibregl from "maplibre-gl";
|
|
import { computed, onMounted, ref, watch } from "vue";
|
|
import { useRouter } from "vue-router";
|
|
|
|
import { useQueryParam } from "@/composable/use-query-param";
|
|
import ThreeColumn from "@/components/layout/ThreeColumn.vue";
|
|
import ReviewSiteColumnAction from "@/components/ReviewSiteColumnAction.vue";
|
|
import ReviewSiteColumnDetail from "@/components/ReviewSiteColumnDetail.vue";
|
|
import ReviewSiteColumnList from "@/components/ReviewSiteColumnList.vue";
|
|
import { formatAddress } from "@/format";
|
|
import { useStoreSite } from "@/store/site";
|
|
import { useSessionStore } from "@/store/session";
|
|
import type { Changes } from "@/types";
|
|
import { Bounds, Contact, Location, Site } from "@/type/api";
|
|
import { Camera } from "@/type/map";
|
|
import { MapClickEvent, Marker } from "@/types";
|
|
|
|
// Props (you can pass these from parent component or environment)
|
|
interface Props {}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {});
|
|
|
|
const error = ref<string>("");
|
|
const mapFlyoverCamera = ref<Camera>(new Camera());
|
|
const router = useRouter();
|
|
const storeSite = useStoreSite();
|
|
const selectedSiteID = ref<number>(0);
|
|
const paramSite = useQueryParam("site");
|
|
const submitting = ref<boolean>(false);
|
|
const selectedSite = computed((): Site | undefined => {
|
|
if (!selectedSiteID.value) {
|
|
return undefined;
|
|
}
|
|
return storeSite.byID(selectedSiteID.value);
|
|
});
|
|
const mapMarkers = computed<Marker[]>(() => {
|
|
const site = selectedSite.value;
|
|
if (!(site && site.address.location)) {
|
|
return [];
|
|
}
|
|
let markers = [
|
|
{
|
|
color: "#FF0000",
|
|
draggable: false,
|
|
id: "address",
|
|
location: site.address.location,
|
|
},
|
|
];
|
|
for (const feature of site.features) {
|
|
markers.push({
|
|
color: "#00FF00",
|
|
draggable: false,
|
|
id: feature.id.toString(),
|
|
location: feature.location,
|
|
});
|
|
}
|
|
return markers;
|
|
});
|
|
async function doRequestComplianceMailer(id: number) {
|
|
submitting.value = true;
|
|
try {
|
|
const payload: any = {
|
|
site_id: id,
|
|
};
|
|
const response = await fetch("/api/compliance-request/mailer", {
|
|
body: JSON.stringify(payload),
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
method: "POST",
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error("failed to create compliance request");
|
|
}
|
|
} catch (err) {
|
|
error.value = err instanceof Error ? err.message : "Unknown error";
|
|
console.error("Error submitting review:", err);
|
|
} finally {
|
|
submitting.value = false;
|
|
}
|
|
}
|
|
function siteDeselect(id: number): void {
|
|
if (selectedSiteID.value == id) {
|
|
selectedSiteID.value = 0;
|
|
}
|
|
}
|
|
function siteSelect(id: number): void {
|
|
selectedSiteID.value = id;
|
|
|
|
const site = storeSite.byID(id);
|
|
if (!site) {
|
|
console.log("no site", id);
|
|
return;
|
|
}
|
|
mapFlyoverCamera.value = new Camera(site.address.location, 20);
|
|
paramSite.setValue(id.toString());
|
|
console.log("selecting site", id, site);
|
|
}
|
|
|
|
// Lifecycle
|
|
onMounted(async () => {
|
|
storeSite.fetchAll();
|
|
});
|
|
watch(
|
|
paramSite.value,
|
|
(site_id) => {
|
|
if (site_id) {
|
|
const id = parseInt(site_id, 10);
|
|
siteSelect(id);
|
|
}
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
</script>
|