diff --git a/src/App.vue b/src/App.vue index bcdb8785b46c80452f8ba0745a36d8478eccf50f..811841af0f671e1bde4991f406f109d3d8f67807 100644 --- a/src/App.vue +++ b/src/App.vue @@ -74,24 +74,11 @@ svg { margin: 5px; } -main > *, -.glued { +main .main-sticky { height: 100%; -} - -.scrollable-first-col { - height: 100%; - - > .row { - height: 100%; - > .col, - > div[class^="col-"] { - margin-top: 20px; - margin-bottom: 20px; - height: 100%; - overflow-y: auto; - } + &.main-scroll { + overflow-y: scroll; } } diff --git a/src/components/CreateBookingModal/booking-creation.store.ts b/src/components/CreateBookingModal/booking-creation.store.ts index b74005a24edc4620b95d016bd90497db38cd9120..50bd2e7ac2fdcb9d5a1973eb81d0da9887332bdb 100644 --- a/src/components/CreateBookingModal/booking-creation.store.ts +++ b/src/components/CreateBookingModal/booking-creation.store.ts @@ -97,7 +97,6 @@ export const useBookingCreationStore = defineStore("bookingCreation", () => { millisecond: 0, }); let dateJs = date.toJSDate(); - console.log("bite", config.value.timeSlots, dateJs); return { client: { name: "", @@ -121,10 +120,6 @@ export const useBookingCreationStore = defineStore("bookingCreation", () => { () => { var cloneInit = Object.assign({}, emptyBooking()); var cloneCurrent = Object.assign({}, booking.value); - // delete cloneInit.jsDate; - // delete cloneCurrent.jsDate; - - console.log("chatte", booking.value.jsDate, cloneInit, cloneCurrent); isInProgress.value = JSON.stringify(cloneInit) !== JSON.stringify(cloneCurrent); }, { deep: true }, diff --git a/src/components/Home/BookingsOnDate/BookingsOnDate.vue b/src/components/Home/BookingsOnDate/BookingsOnDate.vue index 56b7f3e4bab568b66289140bd9727c02fb65c04d..df106b73aa0a57831812e62a0ad2f44fcb895216 100644 --- a/src/components/Home/BookingsOnDate/BookingsOnDate.vue +++ b/src/components/Home/BookingsOnDate/BookingsOnDate.vue @@ -10,8 +10,7 @@ <div class="row"> <div class="col-10"> <h5> - <span>{{ booking.datetime.toFormat($formatTimeShort) }}</span - >0 + <span>{{ booking.datetime.toFormat($formatTimeShort) }}</span> </h5> </div> <div class="col-2"> diff --git a/src/components/Home/RoomPlan/RoomPlan.vue b/src/components/Home/RoomPlan/RoomPlan.vue index 6497da2c5b1a71580884d828b5ac99fd27ab3aaa..b9210b8a1a4e6966d9c79f6d96cf8ed752d4a220 100644 --- a/src/components/Home/RoomPlan/RoomPlan.vue +++ b/src/components/Home/RoomPlan/RoomPlan.vue @@ -1,7 +1,21 @@ <template> - <div id="plan_container" oncontextmenu="return false"> + <div id="plan_container" class="main-sticky" oncontextmenu="return false"> + <div id="actions"> + <BButton variant="secondary" @click="zoom(-1)"> + <BootstrapIcon icon="dash-lg" size="lg" variant="light" /> + </BButton> + <BButton variant="secondary" @click="zoom(1)"> + <BootstrapIcon icon="plus" size="lg" variant="light" /> + </BButton> + <BButton variant="primary" @click="saveZoom"> + <BootstrapIcon icon="save" size="lg" variant="light" /> + </BButton> + <BButton variant="primary" @click="resetZoom"> + <BootstrapIcon icon="fullscreen-exit" size="lg" variant="light" /> + </BButton> + </div> <svg id="plan" height="100%" width="100%" xmlns="http://www.w3.org/2000/svg"> - <g id="viewport"> + <g id="viewport" height="100%" width="100%"> <g v-for="zone in props.plan?.zones" :key="zone.id"> <polyline :points="drawzone(zone)" class="plan-zone-shape" /> <text :x="zone.coordinates[0].x + 20" :y="zone.coordinates[0].y - 20" class="plan-zone-text"> @@ -31,14 +45,17 @@ <script lang="ts" setup> import { useConfigStore } from "@/config/config.store"; -import { RoomPlanDto, ZoneDto } from "@/lpr-api"; +import { PlanDto, ZoneDto } from "@/lpr-api"; +import { LoggerProvider } from "@/utils/LoggerProvider"; +import { HTMLElement } from "happy-dom"; import { onMounted } from "vue"; import TablePlan from "./TableSVG/TableSVG.vue"; +const logger = LoggerProvider.get("home.plan"); let configStore = useConfigStore(); const props = defineProps<{ - plan?: RoomPlanDto; + plan?: PlanDto; }>(); const drawzone = (zone: ZoneDto): string => { let orderedCoords = zone.coordinates.sort((a, b) => { @@ -53,19 +70,63 @@ const drawzone = (zone: ZoneDto): string => { }; //////////////////////////// + +interface PlanData { + zoomCount: number; + tx: number; + ty: number; +} + +function initialPlanData(): PlanData { + let savedOffset = localStorage.getItem("planData"); + if (savedOffset) { + return JSON.parse(savedOffset); + } else { + return { + //FIXME + zoomCount: -3, + tx: -48, + ty: -11, + }; + } +} + +var matrix = new DOMMatrix(); +var planData: PlanData = initialPlanData(); +var svgCanvas: HTMLElement; +var viewPort: HTMLElement; +var drag = false; +var factor = 0.1; +var offset = { x: 0, y: 0 }; + +const resetZoom = () => { + matrix.setMatrixValue(new DOMMatrix().toString()); + initialZoom(); +}; +const initialZoom = () => { + logger.debug("Reseting zoom"); + planData = initialPlanData(); + + //Set zoom + let zoomIn = planData.zoomCount >= 0; + var count = zoomIn ? planData.zoomCount : -1 * planData.zoomCount; + for (let i = 0; i < count; i++) { + _zoom(zoomIn ? 1 : -1, 0, 0); + } + + // Set position + matrix.preMultiplySelf(new DOMMatrix().translateSelf(planData.tx, planData.ty)); + viewPort.style.transform = matrix.toString(); +}; +const saveZoom = () => { + localStorage.setItem("planData", JSON.stringify(planData)); +}; + function makeSvgZoomableAndDragable() { // https://stackoverflow.com/questions/55453969/svg-object-how-to-zoom-and-drag-it-properly - var svgCanvas = document.getElementById("plan")!!; - var viewPort = document.getElementById("viewport")!!; - viewPort.style.width = svgCanvas.clientWidth + "px"; viewPort.style.height = svgCanvas.clientHeight + "px"; - var drag = false; - var offset = { x: 0, y: 0 }; - var factor = 0.1; - var matrix = new DOMMatrix(); - svgCanvas.addEventListener("pointerdown", function (event) { if (event.button === 0) { // Left click @@ -89,6 +150,8 @@ function makeSvgZoomableAndDragable() { x: event.offsetX, y: event.offsetY, }; + planData.tx += tx; + planData.ty += ty; matrix.preMultiplySelf(new DOMMatrix().translateSelf(tx, ty)); viewPort.style.transform = matrix.toString(); } @@ -99,32 +162,55 @@ function makeSvgZoomableAndDragable() { }); svgCanvas.addEventListener("wheel", function (event) { - var zoom = event.deltaY > 0 ? -1 : 1; - var scale = 1 + factor * zoom; - offset = { - x: event.offsetX, - y: event.offsetY, - }; - matrix.preMultiplySelf(new DOMMatrix().translateSelf(offset.x, offset.y).scaleSelf(scale, scale).translateSelf(-offset.x, -offset.y)); - viewPort.style.transform = matrix.toString(); + zoom(event.deltaY > 0 ? -1 : 1, event.offsetX, event.offsetY); }); } +function _zoom(delta: number, anchorX: number, anchorY: number) { + logger.debug("Zooming " + (delta > 0 ? "in" : "out")); + var scale = 1 + factor * delta; + matrix.preMultiplySelf(new DOMMatrix().translateSelf(anchorX, anchorY).scaleSelf(scale, scale).translateSelf(-anchorX, -anchorY)); + viewPort.style.transform = matrix.toString(); +} + +function zoom(delta: number, anchorX?: number, anchorY?: number) { + planData.zoomCount += delta; + anchorX = anchorX || 0; + anchorY = anchorY || 0; + _zoom(delta, anchorX, anchorY); +} + onMounted(() => { - makeSvgZoomableAndDragable(); + setTimeout(() => { + svgCanvas = document.getElementById("plan")!!; + viewPort = document.getElementById("viewport")!!; + initialZoom(); + makeSvgZoomableAndDragable(); + }, 1000); }); </script> <style lang="scss" scoped> #plan_container { - display: flex; - width: 100%; - height: 100%; + position: relative; + overflow: hidden; + border: solid 3px $primary; + height: calc(100% - 100px); } #plan { - flex: 1; - height: auto; + max-height: 100%; + max-width: 100%; +} + +#actions { + position: absolute; + bottom: 10px; + right: 10px; + + .btn { + margin-left: 5px; + } } .plan-zone-shape { diff --git a/src/components/Home/RoomPlan/TableSVG/TableSVG.vue b/src/components/Home/RoomPlan/TableSVG/TableSVG.vue index 449b22d0c99a9566d80d94e871fc31321415dc18..9be9fb555a521ef4097fc54089bc073a3cb237b3 100644 --- a/src/components/Home/RoomPlan/TableSVG/TableSVG.vue +++ b/src/components/Home/RoomPlan/TableSVG/TableSVG.vue @@ -119,4 +119,8 @@ const togglePopup = async () => { .booking_selected .plan-table-texte { fill: $plan-booking-selected-text; } + +a { + text-decoration: none !important; +} </style> \ No newline at end of file diff --git a/src/views/ClientsPage/ClientsPage.vue b/src/views/ClientsPage/ClientsPage.vue index 81dacae996cae7651d9b7a12da26a4cc35d40a91..d1e8d6ee94d62ee970de4a806cc50e2d76f815ed 100644 --- a/src/views/ClientsPage/ClientsPage.vue +++ b/src/views/ClientsPage/ClientsPage.vue @@ -1,10 +1,10 @@ <template> - <div class="container-fluid scrollable-first-col"> - <div class="row justify-content-center"> - <div :class="{ 'col-6': !selectedClient, 'col-4': selectedClient }"> + <div class="container-fluid main-sticky"> + <div class="row justify-content-center main-sticky"> + <div id="clients_list" :class="{ 'col-6': !selectedClient, 'col-4': selectedClient }" class="main-sticky"> <h1>Liste des clients</h1> - <div class="row"> - <div class="col"> + <div class="row main-sticky"> + <div class="col main-sticky main-scroll"> <ol class="list-group list-group-numbered"> <li v-for="client in allClients" @@ -23,24 +23,24 @@ </div> </div> - <div v-if="selectedClient" class="col-8"> + <div v-if="selectedClient" class="col-8 main-sticky"> <h2>{{ selectedClient.name }}</h2> <hr /> - <div class="row"> - <div id="chat" class="col-6"> + <div class="row main-sticky"> + <div id="chat" class="col-6 main-sticky"> <h4>Chat</h4> - <div class="row"> - <div class="col"> + <div class="row main-sticky"> + <div class="col main-sticky main-scroll"> <ChatBox :client="selectedClient"></ChatBox> </div> </div> </div> - <div id="details" class="col-6"> + <div id="details" class="col-6 main-sticky"> <h4>Détails</h4> - <div class="row"> - <div class="col"> + <div class="row main-sticky"> + <div class="col main-sticky main-scroll"> <BButton variant="danger" @click="eraseClient()"> <BootstrapIcon icon="trash" /> Effacer les données @@ -49,13 +49,9 @@ <BootstrapIcon icon="journal" /> Choisir pour réservation </BButton> - </div> - </div> - <hr /> - <h4>Réservations</h4> - <div class="row"> - <div class="col"> + <hr /> + <ol class="list-group list-group-numbered"> <li v-for="booking in selectedClientHistory" class="list-group-item d-flex justify-content-between align-items-start"> <div class="ms-2 me-auto"> @@ -172,4 +168,13 @@ onUnmounted(() => { top: 10px; right: 10px; } + +#clients_list { + padding-bottom: 60px; +} + +#chat, +#details { + padding-bottom: 120px; +} </style> \ No newline at end of file diff --git a/src/views/ErrorPage/ErrorPage.vue b/src/views/ErrorPage/ErrorPage.vue index 26c43d8cf74241086dc0841c0fe59ff01d20c8f9..e35d898ed6f831c455a7efe1ad6d71e718a86d52 100644 --- a/src/views/ErrorPage/ErrorPage.vue +++ b/src/views/ErrorPage/ErrorPage.vue @@ -1,10 +1,15 @@ <template> - <section> - <h1>404</h1> - <h2>The page you are looking for doesn't exist.</h2> - <a class="btn" href="/">Back to home </a> - </section> - <!-- TODO--> + <div class="container-fluid main-sticky"> + <div class="row justify-content-center main-sticky"> + <div class="col-6 main-sticky"> + <h1>Oops !</h1> + <p> + Il semblerait que vous soyez perdu... + <router-link to="/">Retournez au plan</router-link> + </p> + </div> + </div> + </div> </template> <script lang="ts" setup> @@ -13,4 +18,12 @@ import { reactive } from "vue"; const state = reactive({ error: { code: 404 } }); </script> -<style lang="scss" scoped></style> \ No newline at end of file +<style lang="scss" scoped> +h1 { + font-size: 3em; +} + +p { + font-size: 2em; +} +</style> \ No newline at end of file diff --git a/src/views/HelpPage/HelpPage.vue b/src/views/HelpPage/HelpPage.vue index 8d02489a1fd924e2a6ce24734bc6943d22ac6444..131a54be7398a60f831bf1f3805ba399d21c96bf 100644 --- a/src/views/HelpPage/HelpPage.vue +++ b/src/views/HelpPage/HelpPage.vue @@ -1,8 +1,8 @@ <template> - <div class="container-fluid scrollable-first-col"> - <div class="row justify-content-center"> - <div class="col-8"> + <div class="container-fluid main-sticky"> + <div class="row justify-content-center main-sticky"> + <div class="col-8 main-sticky main-scroll"> <div class="row"> <div class="col-12"> <h5>Introduction</h5> diff --git a/src/views/HomePage/HomePage.vue b/src/views/HomePage/HomePage.vue index bbdd661f84f8476758c4b1bb27b30c4aeec98da9..d1ce1abfa8888fd02f4db8683e27e4b0ff053b4e 100644 --- a/src/views/HomePage/HomePage.vue +++ b/src/views/HomePage/HomePage.vue @@ -1,14 +1,14 @@ <template> - <div class="container-fluid glued"> - <div class="row glued"> - <div class="glued col-2"> - <aside class="glued"> + <div class="container-fluid main-sticky"> + <div class="row main-sticky"> + <div class="col-2 main-sticky main-scroll"> + <aside> <BookingsOnDate :date="selectedDate" :selected="listSelected" /> </aside> </div> - <div class="glued col-10"> - <section id="plan_container" class="glued"> + <div class="col-10 main-sticky"> + <section id="plan_container" class="main-sticky"> <DateSelector /> <RoomPlan :plan="plan" /> @@ -88,11 +88,11 @@ hr { } #list_selected_overlay { - right: 0; + right: 15px; } #table_selected_overlay { - left: 0; + left: 15px; } svg {