homemanager-fe/src/views/building/floors/FloorView.vue

224 lines
6.9 KiB
Vue
Raw Normal View History

2023-01-24 19:04:01 +00:00
<template>
<PageHead with-actions>
<template #actions>
<RouterLink
class="px-2 py-2"
:to="{
name: 'planner',
query: { buildingId: building?.id, floorId: floor?.id },
}"
>Edit floor plan</RouterLink
>
</template>
<template #default>
<div class="flex flex-col">
<h1 class="text-2xl font-bold">{{ floor?.displayName }}</h1>
<span class="text-sm font-light text-gray-800 line-clamp-1"
>Floor {{ floor?.number }} in {{ building?.displayName }}</span
>
</div>
</template>
</PageHead>
2023-01-24 20:40:01 +00:00
<button
@click="showMap = !showMap"
:class="[
showMap ? 'bg-gray-100' : 'rounded-md ring-1 ring-black ring-opacity-5',
'ml-auto flex items-center space-x-2 rounded-tl-lg rounded-tr-lg px-2 py-2 shadow-md',
]"
2023-01-24 19:04:01 +00:00
>
2023-01-24 20:40:01 +00:00
<span class="sr-only">{ showMap ? 'Hide' : 'Show' }}</span> Floor plan
<ChevronUpIcon v-if="showMap" class="h-4 w-4" />
<ChevronDownIcon v-else class="h-4 w-4" />
</button>
<Transition
name="menu-transition"
enter-active-class="transition-height ease-out duration-200"
enter-from-class="h-0"
enter-to-class="h-[70vh]"
leave-active-class="transition-height ease-in duration-150"
leave-from-class="h-[70vh]"
leave-to-class="h-0"
>
<div v-if="showMap" class="z-10 overflow-hidden bg-gray-100 shadow-md">
<HousePlanner
v-if="floorDocument"
:key="`fdoc${floorDocument.id}`"
ref="canvas"
class="h-[70vh]"
:editable="false"
:grid="false"
transparent
headless
:floor-document="floorDocument"
@click="clickOnRoom(undefined, $event)"
2023-01-24 19:04:01 +00:00
>
2023-01-24 20:40:01 +00:00
<RoomPolygon
v-for="room of rooms"
:room="room"
:highlighted="selectedRoom?.id"
@clicked-in="(ev) => clickOnRoom(room, ev)"
>
<template v-if="selectedRoom?.id === room.id">
<StorageBubble
:storage="storage"
v-for="storage of storages"
:class="{ 'z-20': storage.id === hoveredBubble }"
@mouseenter="() => (hoveredBubble = storage.id)"
@mouseleave="() => (hoveredBubble = undefined)"
2023-01-24 19:04:01 +00:00
>
2023-01-24 20:40:01 +00:00
<span class="text-md font-bold">{{ storage.displayName }}</span>
<span class="text-sm">Stored items: {{ storage.itemCount }}</span>
</StorageBubble>
2023-01-24 19:04:01 +00:00
</template>
2023-01-24 20:40:01 +00:00
</RoomPolygon>
</HousePlanner>
</div>
</Transition>
<div class="grid grid-cols-3">
<div class="flex flex-col">
<button
v-for="room of rooms"
@click="selectRoomFromList(room)"
:class="[
selectedRoom?.id === room.id
? 'bg-blue-100 hover:bg-blue-200'
: 'hover:bg-blue-100',
'flex items-center justify-between border-b-2 border-gray-100 py-2 px-2',
]"
>
{{ room.displayName }} <ChevronRightIcon class="h-4 w-4" />
</button>
</div>
<div class="flex flex-col" v-if="selectedRoom?.id">
<button
v-for="storage of storages"
:class="[
'hover:bg-blue-100',
'flex items-center justify-between border-b-2 border-gray-100 py-2 px-2',
]"
>
{{ storage.displayName }} ({{ storage.itemCount }})
<ChevronRightIcon class="h-4 w-4" />
</button>
</div>
</div>
2023-01-24 19:04:01 +00:00
</template>
<script setup lang="ts">
2023-01-24 20:40:01 +00:00
import {
ChevronDownIcon,
ChevronRightIcon,
ChevronUpIcon,
} from '@heroicons/vue/24/outline';
2023-01-24 19:04:01 +00:00
import { storeToRefs } from 'pinia';
import PageHead from '../../../components/PageHead.vue';
import { useRoute } from 'vue-router';
import { useBuildingStore } from '../../../store/building.store';
import { computed, ref } from '@vue/reactivity';
import HousePlanner from '../../../components/house-planner/HousePlanner.vue';
import { boundingBox } from '../../../modules/house-planner/utils';
import { Vec2 } from '../../../modules/house-planner/interfaces';
import { watch } from 'vue';
import { RoomLayoutObject } from '../../../interfaces/room.interfaces';
import jfetch from '../../../utils/jfetch';
import { BACKEND_URL } from '../../../constants';
import { useAccessToken } from '../../../composables/useAccessToken';
import { StorageListItem } from '../../../interfaces/storage.interfaces';
2023-01-24 20:40:01 +00:00
import StorageBubble from './StorageBubble.vue';
import RoomPolygon from './RoomPolygon.vue';
import { useLocalStorage } from '@vueuse/core';
2023-01-24 19:04:01 +00:00
const route = useRoute();
const buildingStore = useBuildingStore();
const { building } = storeToRefs(buildingStore);
const { authHeader } = useAccessToken();
const canvas = ref<InstanceType<typeof HousePlanner>>();
const storages = ref<StorageListItem[]>([]);
2023-01-24 20:40:01 +00:00
const selectedRoom = ref<RoomLayoutObject>();
const showMap = useLocalStorage('showRoomMap', true, { writeDefaults: false });
const hoveredBubble = ref<number>();
const selectedStorage = ref<number>();
2023-01-24 19:04:01 +00:00
const floor = computed(() =>
building.value?.floors.find(
(floor) => floor.number === Number(route.params.number)
)
);
const floorDocument = computed(
() => floor.value?.plan && JSON.parse(floor.value.plan)
);
2023-01-24 20:40:01 +00:00
const roomCoordinate = (ev: MouseEvent) => {
const rect = (ev.target as HTMLElement).getBoundingClientRect();
const [x, y] = [ev.clientX - rect.left, ev.clientY - rect.top];
console.log(x, y);
};
const clickOnRoom = (room: RoomLayoutObject | undefined, ev: MouseEvent) => {
2023-01-24 19:04:01 +00:00
canvas.value?.setViewRectangle(room?.boundingBox);
2023-01-24 20:40:01 +00:00
selectedStorage.value = undefined;
if (room && (!selectedRoom.value || room.id !== selectedRoom.value?.id)) {
2023-01-24 19:04:01 +00:00
getRoomStorages(room);
2023-01-24 20:40:01 +00:00
} else if (room?.id == selectedRoom.value?.id) {
roomCoordinate(ev);
} else {
2023-01-24 19:04:01 +00:00
storages.value = [];
}
2023-01-24 20:40:01 +00:00
selectedRoom.value = room;
};
const selectRoomFromList = (room: RoomLayoutObject) => {
canvas.value?.setViewRectangle(room?.boundingBox);
selectedStorage.value = undefined;
getRoomStorages(room);
selectedRoom.value = room;
2023-01-24 19:04:01 +00:00
};
const rooms = computed<RoomLayoutObject[]>(
() =>
(floor.value &&
floor.value.rooms
.filter((room) => room.plan?.includes('polygon'))
.map((room) => {
const { polygon } = JSON.parse(room.plan);
const aabb = boundingBox(polygon, [
[Infinity, Infinity],
[0, 0],
]);
return {
...room,
plan: polygon as Vec2[],
boundingBox: aabb,
storages: [],
} as RoomLayoutObject;
})) ||
[]
);
const getRoomStorages = async (room: RoomLayoutObject) => {
2023-01-24 20:40:01 +00:00
storages.value = [];
2023-01-24 19:04:01 +00:00
try {
const { data: storageList } = await jfetch(
`${BACKEND_URL}/storage/room/${room.id}?includeWithSets=false`,
{
headers: authHeader.value,
}
);
const { data: setList } = await jfetch(
`${BACKEND_URL}/storage/set/room/${room.id}`,
{
headers: authHeader.value,
}
);
storages.value = [...storageList, ...setList];
} catch (e) {
console.error(e);
}
};
</script>