homemanager-fe/src/views/HousePlanner.vue

177 lines
4.7 KiB
Vue

<template>
<div class="relative h-full bg-gray-100">
<PlannerBuildingSelect
:buildings="buildings"
:floors="floors"
:status="status"
:selected-building-id="selectedBuildingId"
:selected-floor-id="selectedFloorId"
@update:selected-building-id="(newValue) => buildingSelected(newValue)"
@update:selected-floor-id="(newValue) => (selectedFloorId = newValue)"
/>
<HousePlanner
v-if="selectedFloorId"
editable
class="h-full"
ref="plannerRef"
:key="`planner-${selectedFloorId}`"
:floor-document="floorPlan"
@update="
(layers, rooms, boundingBox) =>
updateDocument(layers, rooms, boundingBox)
"
@edited="status = 'Modified'"
/>
<div
class="flex h-full w-full select-none items-center justify-center text-lg"
v-else
>
<span class="font-bold uppercase text-gray-300"
>Choose a floor to edit from the top left</span
>
</div>
</div>
</template>
<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { computed, onMounted, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { defaultRoomData } from '../components/house-planner/helpers/default-room';
import HousePlanner from '../components/house-planner/HousePlanner.vue';
import { FloorDocument } from '../components/house-planner/interfaces/floor-document.interface';
import PlannerBuildingSelect from '../components/house-planner/PlannerBuildingSelect.vue';
import { UpsertRoomItem } from '../interfaces/room.interfaces';
import { Line, Vec2Box } from '../modules/house-planner/interfaces';
import { useBuildingStore } from '../store/building.store';
import extractLinePoints from '../utils/extract-line-points';
const building = useBuildingStore();
const route = useRoute();
const { buildings, floors } = storeToRefs(building);
const selectedBuildingId = ref<number>();
const selectedFloorId = ref<number>();
const status = ref('No changes');
const plannerRef = ref<InstanceType<typeof HousePlanner>>();
const currentFloor = computed(
() =>
selectedFloorId.value &&
floors.value.find((floor) => floor.id === selectedFloorId.value)
);
const floorPlan = computed(
() =>
currentFloor.value &&
Object.assign({
...defaultRoomData,
...JSON.parse(currentFloor.value.plan || '{}'),
id: selectedFloorId.value,
})
);
const buildingSelected = async (id: number) => {
if (id == null) return;
selectedFloorId.value = undefined;
selectedBuildingId.value = id;
await building.getFloors(id);
};
const updateRooms = async (data: FloorDocument, rooms: Line[]) => {
if (
!selectedBuildingId.value ||
!selectedFloorId.value ||
!currentFloor.value
)
return data;
const extractedRooms: UpsertRoomItem[] = rooms.map((room) => ({
id: room.databaseId,
canvasObjectId: room.id,
displayName: room.name,
plan: JSON.stringify({ polygon: extractLinePoints(room) }),
}));
if (!extractedRooms.length) {
return data;
}
const createdRooms = await building.upsertFloorRooms(
selectedBuildingId.value,
currentFloor.value.number,
extractedRooms
);
if (createdRooms?.length) {
createdRooms.forEach((room) => {
if (!room.canvasObjectId || !room.id) return;
rooms.forEach((existing) => {
if (existing.id !== room.canvasObjectId) return;
existing.databaseId = room.id;
});
});
if (plannerRef.value) {
data = plannerRef.value.updateLocalDocument();
}
}
return data;
};
const updateDocument = async (
data: FloorDocument,
rooms: Line[],
boundingBox?: Vec2Box
) => {
if (
!selectedBuildingId.value ||
!selectedFloorId.value ||
selectedFloorId.value !== data.id ||
!currentFloor.value ||
status.value === 'Saving...'
)
return;
status.value = 'Saving...';
try {
data = await updateRooms(data, rooms);
// Prevent useless requests
const floorPlan = JSON.stringify({ ...data, boundingBox });
if (currentFloor.value.plan === floorPlan) {
status.value = 'Saved!';
return;
}
await building.saveFloor(
selectedBuildingId.value,
currentFloor.value.number,
{
plan: floorPlan,
}
);
status.value = 'Saved!';
} catch (e) {
console.error(`Failed to save floor document: ${(e as Error).stack}`);
status.value = 'Failed to save!';
}
};
const initialize = async () => {
await building.getBuildings();
if (route.query.buildingId) {
await buildingSelected(Number(route.query.buildingId));
if (route.query.floorId) {
selectedFloorId.value = Number(route.query.floorId);
}
}
};
onMounted(() => {
floors.value = [];
initialize();
});
</script>