Changeset 0c6b92a for imaps-frontend/src


Ignore:
Timestamp:
12/12/24 17:06:06 (5 weeks ago)
Author:
stefan toskovski <stefantoska84@…>
Branches:
main
Parents:
d565449
Message:

Pred finalna verzija

Location:
imaps-frontend/src
Files:
70 added
13 deleted
41 edited
6 moved

Legend:

Unmodified
Added
Removed
  • imaps-frontend/src/App.css

    rd565449 r0c6b92a  
     1
     2
    13body {
    2   background-color: white;
     4  margin: 0;
     5  padding: 0;
     6  height: 100%;
     7  background: white;
     8  backdrop-filter: blur(10px);
    39}
    410.loading-container {
     
    713  justify-content: center;
    814  align-items: center;
    9   height: 100vh; /* Full-screen loader */
    10   background-color: #f7f9fc; /* Light background */
     15  height: 100vh;
     16  background-color: #f7f9fc;
    1117}
    1218
    1319.spinner {
    14   border: 8px solid #f3f3f3; /* Light grey border */
    15   border-top: 8px solid #1e90ff; /* Accent color for spinning part */
     20  border: 8px solid #f3f3f3;
     21  border-top: 8px solid #1e90ff;
    1622  border-radius: 50%;
    1723  width: 60px;
     
    3238  margin-top: 20px;
    3339  font-size: 18px;
    34   color: #2c3e50; /* Dark text for contrast */
     40  color: #2c3e50;
    3541  font-weight: 500;
    3642}
  • imaps-frontend/src/App.jsx

    rd565449 r0c6b92a  
    11// eslint-disable-next-line no-unused-vars
    2 import React, { useState, useEffect } from "react";
    3 import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
    4 import FinkiMaps from "./pages/FinkiMaps/FinkiMaps";
    5 import Maps from "./pages/Maps/Maps";
     2import React from "react";
     3import {BrowserRouter as Router, Route, Routes} from "react-router-dom";
     4import BrowseMaps from "./pages/BrowseMaps/BrowseMaps.jsx";
    65import LoginPage from "./pages/Login/Login";
    76import Signup from "./pages/Signup/Signup";
     
    1110import ProtectedRoute from "./components/ProtectedRoute/ProtectedRoute";
    1211import "./App.css";
    13 import HttpService from "./scripts/net/HttpService";
     12import MapView from "./pages/MapView/MapView.jsx";
     13import MyMaps from "./pages/MyMaps/MyMaps.jsx";
     14
     15import '@fontsource/roboto/300.css';
     16import '@fontsource/roboto/400.css';
     17import '@fontsource/roboto/500.css';
     18import '@fontsource/roboto/700.css';
     19import {AppProvider} from "./components/AppContext/AppContext.jsx";
     20import AdminPage from "./pages/AdminPage/AdminPage.jsx";
     21
    1422
    1523function App() {
    16   const [isAuthenticated, setIsAuthenticated] = useState(false);
    17   const [loading, setLoading] = useState(true);
    1824
    19   useEffect(() => {
    20     const token = localStorage.getItem("token");
    21     const httpService = new HttpService("http://localhost:8080/api/auth");
     25    return (
    2226
    23     const verifyToken = async () => {
    24       try {
    25         const response = await httpService.get(`/verify?token=${token}`);
    26         if (response.username) {
    27           setIsAuthenticated(true);
    28           console.log("good");
    29         }
    30       } catch (error) {
    31         console.log("ERROR: ", error);
    32         setIsAuthenticated(false);
    33       } finally {
    34         setLoading(false);
    35       }
    36     };
     27        <AppProvider>
     28            <Router>
     29                <Routes>
    3730
    38     if (token) {
    39       verifyToken();
    40       //setLoading(false);
    41     } else {
    42       setIsAuthenticated(false);
    43       setLoading(false);
    44     }
    45   }, []);
     31                    <Route element={<ProtectedRoute/>}>
     32                        <Route path="/myMaps/:mapName/Draw" element={<Draw/>}/>
     33                        <Route path="/myMaps" element={<MyMaps/>}/>
     34                        <Route path="/myMaps/:mapName/View" element={<MapView isPrivate={true}/>}/>
     35                        <Route path="/Admin" element={<AdminPage/>}/>
     36                    </Route>
     37                    <Route path="/" element={<IMaps/>}/>
     38                    <Route path="/Maps/:mapName/View" element={<MapView isPrivate={false}/>}/>
     39                    <Route path="/Maps" element={<BrowseMaps/>}/>
    4640
    47   const handleLogin = (token) => {
    48     localStorage.setItem("token", token);
    49     setIsAuthenticated(true);
    50   };
    5141
    52   if (loading) {
    53     return (
    54       <div className="loading-container">
    55         <div className="spinner"></div>
    56         <p>Loading, please wait...</p>
    57       </div>
     42                    <Route path="/Login" element={<LoginPage/>}/>
     43                    <Route path="/Signup" element={<Signup/>}/>
     44
     45                    <Route path="*" element={<Error/>}/>
     46                </Routes>
     47            </Router>
     48        </AppProvider>
     49
     50
    5851    );
    59   }
    60 
    61   return (
    62     <Router>
    63       <Routes>
    64         <Route path="/" element={<IMaps />} />
    65         <Route path="/Maps/FinkiMaps/View" element={<FinkiMaps />} />
    66         <Route path="/Maps" element={<Maps />} />
    67         <Route path="/Login" element={<LoginPage onLogin={handleLogin} />} />
    68         <Route path="/Signup" element={<Signup />} />
    69 
    70         <Route element={<ProtectedRoute isAuthenticated={isAuthenticated} />}>
    71           <Route path="/Maps/FinkiMaps/Draw" element={<Draw />} />
    72         </Route>
    73 
    74         <Route path="*" element={<Error />} />
    75       </Routes>
    76     </Router>
    77   );
    7852}
    7953
  • imaps-frontend/src/TODO.txt

    rd565449 r0c6b92a  
    11PRIORITY HIGH
    22
    3 -Graph na backend
    4 -Connections za infoPins i entrance
    5 -Cuvanje ID vo _info (da se sovpagjat so modalite)
    6 -Crtanje na ruta vo View
    7 -Da se prenesat info od From i To vo Mapata kaj so se crtat
    8 -Da se cuvaat connections vnatre vo js kodoj za da imat prev data ko ke otvoris nanovo modal
    9 
     3- Id fix na shapes vo Draw
    104
    115PRIORITY MEDIUM
    126
    13 -Modluarnost za pojke mapi (ne samo finkimap)
    14 -Refactor na kod
    15 -Hover na sobi na View
    16 -Filter na prostorii (Ke zemame od roomType modal)
    17 -Multiple floor (Prerabotka na baza)
    18 -Zoom na View (imame, da se napret globalno)
    19 -Implementacija na stairs
    20 -Shape sho ke selektiras za klavanje da sejt celo vreme ko selektiran, duri ne kliknis drug shape ili escape.
    21 -Nacin da znes dodeka crtas koi sobi ti se povrzani
    22 -Ako kliknis na room vo view, da ta odnesit do toj room (navigacija);
    23 
     7- Detach event na draw (keys: e, r)
     8- Text na shape vo Draw da e top layer
    249
    2510PRIORITY LOW
    2611
    27 -Page za pregled na svoi mapi
    28 -Refactor (Da se koristit net util klasa za pustanje do backend)
    29 -AutoSave na draw duri crtas (Da se pret update request do baza)
    30 -Refactor na stil na Draw
     12- Admin Page
    3113
    3214
     15- KO KE SMENIS IME VO MODAL,TREBIT VO GRAFOT DA SA UPDATE TOJ ENTRY // got
     16- VO room da sa cuvat entrances, za ko ke mrdas room isto entrance da sa mrdnit
    3317
    34 ZA PREZ
     18
     19bug: selectiras eden shape pa drug, pa celo vreme sa klavat posle shape
  • imaps-frontend/src/components/FilterBar/FilterBar.jsx

    rd565449 r0c6b92a  
    1 import React from "react";
     1import React, { useState, useEffect, useCallback } from "react";
    22import styles from "./FilterBar.module.css";
    33
    4 function FilterBar() {
    5   const filterLocation = (category) => {
    6     console.log(`Filter locations by: ${category}`);
    7     // filtering logic
    8   };
     4export default function FilterBar({roomTypes,map}) {
     5  const [selectedCategory, setSelectedCategory] = useState("all");
     6
     7
     8
     9  const filterLocation = useCallback((category) => {
     10    map.setFilter(category)
     11    setSelectedCategory(category);
     12  },[])
    913
    1014  return (
    1115    <div className={styles.wrapper}>
    1216      <div className={styles.scrollableContainer}>
    13         <button className={styles.buttonValue} onClick={() => filterLocation("all")}>
     17        <button
     18          className={`${styles.buttonValue} ${selectedCategory === "all" ? styles.active : ""}`}
     19          onClick={() => filterLocation("all")}
     20        >
    1421          All
    1522        </button>
    16         <button className={styles.buttonValue} onClick={() => filterLocation("Classrooms")}>
    17           Classrooms
    18         </button>
    19         <button className={styles.buttonValue} onClick={() => filterLocation("Administrative")}>
    20           Administrative
    21         </button>
    22         <button className={styles.buttonValue} onClick={() => filterLocation("Labs")}>
    23           Labs
    24         </button>
    25         <button className={styles.buttonValue} onClick={() => filterLocation("Restrooms")}>
    26           Restrooms
    27         </button>
    28         <button className={styles.buttonValue} onClick={() => filterLocation("Cafeteria")}>
    29           Cafeteria
    30         </button>
     23
     24        {roomTypes?.map((roomType, index) => (
     25          <button
     26            key={index}
     27            className={`${styles.buttonValue} ${selectedCategory === roomType.name ? styles.active : ""}`}
     28            onClick={() => filterLocation(roomType.name)}
     29          >
     30            {roomType.name}
     31          </button>
     32        ))}
    3133      </div>
    3234    </div>
     
    3436}
    3537
    36 export default FilterBar;
  • imaps-frontend/src/components/MapControls/MapControls.jsx

    rd565449 r0c6b92a  
    1 import React, { useState } from "react";
     1import React, {useEffect, useState} from "react";
    22import styles from "./MapControls.module.css";
    33import plusIcon from "../../assets/plus_icon.png";
    44import minusIcon from "../../assets/minus_icon.png";
    55import floorIcon from "../../assets/floor_icon.png";
     6import HttpService from "../../scripts/net/HttpService.js";
    67
    7 export default function MapControls({ onZoomIn, onZoomOut, onFloorChange }) {
    8   const [currentFloor, setCurrentFloor] = useState(1); // Starting floor
     8export default function MapControls({onFloorChange,mapName }) {
     9  const [currentFloor, setCurrentFloor] = useState(0);
     10  const [floors, setFloors] = useState([]);
     11
     12  // Starting floor
    913
    1014  // Handle floor selection
     
    2024      {/* Zoom Controls */}
    2125      <div className={styles.zoomControls}>
    22         <button onClick={onZoomIn} className={styles.zoomButton}>
     26        <button className={styles.zoomButton}>
    2327          <img src={plusIcon} alt="Zoom In" />
    2428        </button>
    25         <button onClick={onZoomOut} className={styles.zoomButton}>
     29        <button className={styles.zoomButton}>
    2630          <img src={minusIcon} alt="Zoom Out" />
    2731        </button>
     
    2933
    3034      {/* Floor Selector */}
    31       <div className={styles.floorSelector}>
    32         <img src={floorIcon} alt="Floor Icon" className={styles.floorIcon} />
    33         <select
    34           value={currentFloor}
    35           onChange={(e) => handleFloorChange(parseInt(e.target.value, 10))}
    36           className={styles.floorDropdown}
    37         >
    38           <option value={1}>1F</option>
    39           <option value={2}>2F</option>
    40           <option value={3}>3F</option>
    41           <option value={4}>4F</option>
    42         </select>
    43       </div>
     35      {/*<div className={styles.floorSelector}>*/}
     36      {/*  <img src={floorIcon} alt="Floor Icon" className={styles.floorIcon} />*/}
     37      {/*  <select*/}
     38      {/*    value={currentFloor}*/}
     39      {/*    onChange={(e) => handleFloorChange(parseInt(e.target.value, 10))}*/}
     40      {/*    className={styles.floorDropdown}*/}
     41      {/*  >*/}
     42      {/*    {floors?.map(floor => (*/}
     43      {/*        <option key={floor.floorNumber} value={floor.floorNumber}>*/}
     44      {/*          {floor.floorNumber}F*/}
     45      {/*        </option>*/}
     46
     47      {/*    ))}*/}
     48      {/*    /!*<option value={1}>1F</option>*!/*/}
     49      {/*    /!*<option value={2}>2F</option>*!/*/}
     50      {/*    /!*<option value={3}>3F</option>*!/*/}
     51      {/*    /!*<option value={4}>4F</option>*!/*/}
     52      {/*  </select>*/}
     53      {/*</div>*/}
    4454    </div>
    4555  );
  • imaps-frontend/src/components/Modals/EntranceModal/EntranceModal.module.css

    rd565449 r0c6b92a  
    1414}
    1515
    16 /* Overlay: Dark background with optional blur */
    1716.overlay {
    18   background: rgba(0, 0, 0, 0.7); /* Darken the background */
    19   backdrop-filter: blur(5px); /* Optional blur effect */
    20   z-index: 999; /* Ensure the overlay appears below the modal */
     17  /*background: rgba(255, 255, 255, 0.05);*/
     18  z-index: 999;
    2119}
    2220
    23 /* Modal content: Centered and styled */
     21.draggableHeader {
     22  cursor: move;
     23  padding: 10px;
     24  background-color: #2c2f33;
     25  color: #ffffff;
     26  border-bottom: 1px solid #444;
     27}
     28
     29
     30
    2431.modalContent {
    2532  position: absolute;
     
    2734  left: 50%;
    2835  transform: translate(-50%, -50%);
    29   background-color: #2c2f33; /* Darker background for modal content */
    30   color: #ffffff; /* White text for contrast */
     36  background-color: #2c2f33;
     37  color: #ffffff !important;
    3138  padding: 20px;
    3239  border-radius: 8px;
     
    3441  width: 100%;
    3542  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
    36   z-index: 1000; /* Ensure the modal appears above the overlay */
     43  z-index: 1000;
    3744}
    3845
    39 /* Heading styles */
    40 h2,
    41 h3 {
     46
     47.title {
    4248  margin-bottom: 15px;
    43   color: #ffffff; /* White text for the headings */
     49  color: #ffffff ;
    4450}
    4551
    46 /* Button for opening the modal */
    4752.btnModal {
    4853  padding: 10px 20px;
     
    6166}
    6267
    63 /* Close button inside the modal */
    6468.closeModal {
    6569  position: absolute;
     
    7882}
    7983
    80 /* Form styles */
    8184.form {
    8285  display: flex;
     
    8891}
    8992
    90 /* Form labels */
    9193.formGroup label {
    9294  display: block;
    9395  margin-bottom: 5px;
    94   color: #ffffff; /* White text for labels */
     96  color: #ffffff;
    9597}
    9698
    97 /* Input, select, and textarea styles */
    9899.formGroup input,
    99100.formGroup select,
     
    103104  border-radius: 4px;
    104105  border: 1px solid #ccc;
    105   background-color: #23272a; /* Darker input background */
    106   color: #ffffff; /* White text inside the input fields */
     106  background-color: #23272a;
     107  color: #ffffff;
    107108}
    108109
    109110.formGroup input::placeholder,
    110111.formGroup textarea::placeholder {
    111   color: #888888; /* Slightly lighter color for placeholder text */
     112  color: #888888;
    112113}
    113114
    114115/* Checkbox alignment fix */
    115116.formGroupCheckbox {
     117
    116118  display: flex;
    117   align-items: center; /* Vertical alignment */
    118   color: #ffffff;
     119  flex-direction: row;
     120  vertical-align: middle;
     121  align-items: flex-end;
     122  /*justify-content: start;!* Vertical alignment *!*/
     123  /*color: #ffffff;*/
     124}
     125
     126.formGroupCheckbox input{
     127  width: 10%;
     128  margin-bottom: 0.88em;
    119129}
    120130
    121131/* Add button */
    122132.addButton {
    123   background-color: #28a745;
     133  background-color: #28a745 !important;
    124134  color: white;
    125135  padding: 8px 15px;
     
    131141
    132142.addButton:hover {
    133   background-color: #218838;
     143  background-color: #218838 !important;
    134144}
    135145
    136146/* Save button styling */
    137147.submitButton {
    138   background-color: #28a745;
     148  background-color: #28a745 !important;
    139149  color: white;
    140150  padding: 10px 20px;
     
    150160}
    151161
    152 /* Pin list */
    153162.pinList {
    154163  list-style-type: none;
     
    157166}
    158167
    159 /* Individual pin item */
    160168.pinItem {
    161169  background-color: #444;
     
    169177}
    170178
    171 /* Remove button */
    172179.removeButton {
    173180  background-color: #dc3545;
  • imaps-frontend/src/components/Profile/Profile.jsx

    rd565449 r0c6b92a  
    1 import React, { useState, useRef, useEffect } from "react";
     1import React, { useState, useRef, useEffect, useContext } from "react";
     2import { useNavigate, Link } from "react-router-dom";
    23import profile from "../../assets/person_icon.png";
    34import styles from "./Profile.module.css";
     5import { useAppContext } from "../AppContext/AppContext.jsx";
    46
    5 function Profile() {
    6   const menus = ["Profile", "Settings", "Support", "Logout"];
    7   const [open, setOpen] = useState(false);
    8   const menuRef = useRef(null);
    9   const imgRef = useRef(null);
     7function Profile({ position = "fixed" }) {
     8    const { username, isAuthenticated } = useAppContext();
     9    const [open, setOpen] = useState(false);
     10    const menuRef = useRef(null);
     11    const imgRef = useRef(null);
     12    const navigate = useNavigate();
    1013
    11   useEffect(() => {
    12     const handleClickOutside = (e) => {
    13       if (menuRef.current && imgRef.current) {
    14         if (!menuRef.current.contains(e.target) && !imgRef.current.contains(e.target)) {
    15           setOpen(false);
     14    const menus = isAuthenticated ? ["My Maps", "Logout"] : ["Login"];
     15
     16    useEffect(() => {
     17        const handleClickOutside = (e) => {
     18            if (menuRef.current && imgRef.current) {
     19                if (!menuRef.current.contains(e.target) && !imgRef.current.contains(e.target)) {
     20                    setOpen(false);
     21                }
     22            }
     23        };
     24
     25        document.addEventListener("click", handleClickOutside);
     26
     27        return () => {
     28            document.removeEventListener("click", handleClickOutside);
     29        };
     30    }, []);
     31
     32    const handleMenuClick = (menu) => {
     33        if (menu === "My Maps") {
     34            navigate("/MyMaps");
     35        } else if (menu === "Logout") {
     36            localStorage.removeItem("token");
     37            window.location.reload();
    1638        }
    17       }
     39        setOpen(false);
    1840    };
    1941
    20     document.addEventListener("click", handleClickOutside);
    21 
    22     return () => {
    23       document.removeEventListener("click", handleClickOutside);
    24     };
    25   }, []);
    26 
    27   return (
    28     <div className={styles.profileContainer}>
    29       <div className={styles.profileWrapper}>
    30         <img
    31           onClick={() => setOpen(!open)}
    32           src={profile}
    33           alt="profile"
    34           className={styles.profileImage}
    35           ref={imgRef}
    36         />
    37         {open && (
    38           <div ref={menuRef} className={styles.dropdownMenu}>
    39             <ul className={styles.menuList}>
    40               {menus.map((menu) => (
    41                 <li key={menu} onClick={() => setOpen(false)} className={styles.menuItem}>
    42                   {menu}
    43                 </li>
    44               ))}
    45             </ul>
    46           </div>
    47         )}
    48       </div>
    49     </div>
    50   );
     42    return (
     43        <div className={position === "fixed" ? styles.fixedProfileContainer : styles.inlineProfileContainer}>
     44            <div className={styles.profileWrapper}>
     45                <div className={styles.profileIconContainer} onClick={() => setOpen(!open)}>
     46                    <img src={profile} alt="profile" className={styles.profileImage} ref={imgRef} />
     47                </div>
     48                {open && (
     49                    <div ref={menuRef} className={styles.dropdownMenu}>
     50                        {isAuthenticated && <div className={styles.username}>{username}</div>}
     51                        <ul className={styles.menuList}>
     52                            {menus.map((menu) =>
     53                                menu === "Login" ? (
     54                                    <li key={menu} className={styles.menuItem}>
     55                                        <Link to="/login" className={styles.linkStyle}>{menu}</Link>
     56                                    </li>
     57                                ) : (
     58                                    <li key={menu} onClick={() => handleMenuClick(menu)} className={styles.menuItem}>
     59                                        {menu}
     60                                    </li>
     61                                )
     62                            )}
     63                        </ul>
     64                    </div>
     65                )}
     66            </div>
     67        </div>
     68    );
    5169}
    5270
  • imaps-frontend/src/components/Profile/Profile.module.css

    rd565449 r0c6b92a  
    1 img:hover,
    2 .menuItem:hover {
     1.fixedProfileContainer {
     2  position: fixed;
     3  top: 1em;
     4  right: 2.5em;
     5  z-index: 1000;
     6}
     7
     8.inlineProfileContainer {
     9  display: inline-block;
    310  cursor: pointer;
    411}
    512
    6 .menuList {
    7   list-style: none;
    8   padding: 0;
    9   margin: 0;
    10   text-align: center;
     13.profileWrapper {
     14  display: flex;
     15  align-items: center;
     16  cursor: pointer;
    1117}
    1218
    13 .profileContainer {
    14   display: flex;
    15   justify-content: center;
    16 }
    17 
    18 .profileWrapper {
    19   position: relative;
     19.profileIconContainer {
     20  position: relative; /* Makes dropdown position relative to this container */
     21  cursor: pointer;
    2022}
    2123
    2224.profileImage {
    23   height: 48px;
    24   width: 48px;
     25  width: 60px;
     26  height: 60px;
    2527  border-radius: 50%;
    26   /* border: 4px solid #d1d5db; */
    27   object-fit: cover;
     28  transition: box-shadow 0.3s ease;
    2829}
    2930
    30 /* Dropdown Menu */
     31.profileImage:hover {
     32  box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.2);
     33}
     34
    3135.dropdownMenu {
    3236  position: absolute;
    33   top: 50px; /* Adjust according to image size and spacing */
    34   left: 50%;
    35   transform: translateX(-50%); /* Center the menu below the image */
    36   width: 13rem;
    37   background-color: white;
    38   padding: 1rem;
    39   box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
     37  top: 70px;
     38  margin-left: 1.8em;
     39  transform: translateX(-50%);
     40  background-color: #ffffff;
     41  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
     42  border-radius: 12px;
     43  padding: 12px;
     44  width: 180px;
     45  transition: opacity 0.3s ease;
     46  opacity: 1;
    4047}
    4148
    42 /* Menu List and Items */
     49.username {
     50  padding: 10px 0;
     51  font-weight: 600;
     52  text-align: center;
     53  color: #333;
     54  border-bottom: 1px solid #eee;
     55}
     56
    4357.menuList {
    4458  list-style-type: none;
     
    4963.menuItem {
    5064  padding: 10px;
     65  cursor: pointer;
     66  transition: background-color 0.2s ease, color 0.2s ease;
    5167  text-align: center;
    52   border-radius: 5px;
    53   transition: background-color 0.3s ease;
     68  color: #333;
     69  font-weight: 500;
     70  border-radius: 8px;
    5471}
    5572
    5673.menuItem:hover {
    57   background-color: #f0f0f0;
     74  background-color: #f5f5f5;
     75  color: #007bff; /* Optional: Adds a blue highlight on hover */
    5876}
  • imaps-frontend/src/components/ProtectedRoute/ProtectedRoute.jsx

    rd565449 r0c6b92a  
    1 import React from "react";
    2 import { Outlet, Navigate } from "react-router-dom";
     1import { Navigate, Outlet, useLocation } from "react-router-dom";
     2import {useAppContext} from "../AppContext/AppContext.jsx";
     3import HttpService from "../../scripts/net/HttpService.js";
     4import React, {useEffect} from "react";
     5import {LoadingContainer} from "../LoadingContainer/LoadingContainer.jsx";
     6import config from "../../scripts/net/netconfig.js";
     7import {verifyToken} from "../../scripts/util/verifyToken.js";
    38
    4 const ProtectedRoute = ({ isAuthenticated }) => {
    5   return isAuthenticated ? <Outlet /> : <Navigate to="/Login" />;
     9const ProtectedRoute = () => {
     10  const location = useLocation();
     11  const {loading,isAuthenticated} = useAppContext();
     12
     13
     14  if(loading){
     15    return <LoadingContainer/>
     16  }
     17
     18
     19  if (!isAuthenticated) {
     20    return <Navigate to="/Login" state={{ targetPath: location }} replace />;
     21  }
     22
     23  return <Outlet />;
    624};
    725
  • imaps-frontend/src/components/SaveMap/SaveMap.jsx

    rd565449 r0c6b92a  
    22import HttpService from '../../scripts/net/HttpService';
    33import styles from './SaveMap.module.css';
     4import triggerMapSave from "../../scripts/util/triggerMapSave.js";
    45
    56const SaveMap = ({submitHandler}) => {
     
    89    const handleSubmit = (event) => {
    910        event.preventDefault();
    10         submitHandler(name);
     11        // submitHandler();
     12        // const mapSaveEvent = new CustomEvent("mapsave",{});
     13        // window.dispatchEvent(mapSaveEvent)
     14        triggerMapSave();
    1115    };
    1216
     
    1519        <div className={styles.saveMapContainer}>
    1620            <form onSubmit={handleSubmit} className={styles.saveMapForm}>
    17                 <div>
    18                     <label htmlFor="name">Map Name: </label>
    19                     <input
    20                         type="text"
    21                         id="name"
    22                         value={name}
    23                         onChange={(e) => setName(e.target.value)}
    24                         placeholder="Enter map name"
    25                         required
    26                         className={styles.saveMapInput}
    27                     />
    28                 </div>
    2921                <div>
    3022                    <button type="submit" className={styles.saveMapButton}>Save Map</button>
  • imaps-frontend/src/components/SaveMap/SaveMap.module.css

    rd565449 r0c6b92a  
    2121
    2222.saveMapButton {
    23   background-color: #e67e22;
    24   color: white;
     23  width: 100%;
     24  height: 50px;
     25  background-color: #e67e22 !important;
     26  color: #ffffff !important;
    2527  border: none;
    26   border-radius: 5px;
     28  border-radius: 8px;
     29  font-size: 20px;
     30  font-weight: bold;
    2731  cursor: pointer;
    28   width: 8vw;
    29   height: 2vw;
    30   text-align: center;
     32  display: flex;
     33  justify-content: center;
     34  align-items: center;
     35  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
     36  transition: background-color 0.3s, transform 0.2s, box-shadow 0.3s;
     37  appearance: none;
     38  -webkit-appearance: none;
    3139}
    3240
    3341.saveMapButton:hover {
    34   background-color: #b35418;
     42  background-color: #d2691e !important;
     43  transform: scale(1.05);
     44  box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);
     45}
     46
     47.saveMapButton:active {
     48  background-color: #b35418 !important;
    3549  transform: scale(0.98);
    3650  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  • imaps-frontend/src/components/SearchBar/SearchBar.jsx

    rd565449 r0c6b92a  
    1 import React, { useState } from "react";
     1import React, { useState, useEffect, useRef } from "react";
     2import ReactDOM from "react-dom";
    23import searchIcon from "../../assets/search_icon.png";
    34import routeIcon from "../../assets/route_icon.png";
     
    56import styles from "./SearchBar.module.css";
    67
    7 function SearchBar(props) {
     8function SearchBar({ map, handleDirectionsSubmit, setIsPanelOpen, setSelectedRoom, availableShapes,handleFloorChange }) {
    89  const [isExpanded, setIsExpanded] = useState(false);
    910  const [from, setFrom] = useState("");
    1011  const [to, setTo] = useState("");
     12  const [availableOptions, setAvailableOptions] = useState([]);
     13  const [filteredOptions, setFilteredOptions] = useState([]);
     14  const [dropdownVisible, setDropdownVisible] = useState(false);
     15  const [inputFieldType, setInputFieldType] = useState("");
     16  const dropdownRef = useRef(null);
    1117
    1218  const toggleExpanded = () => {
     
    1420  };
    1521
    16   function searchRoom(){
    17     props.map.search();
     22  function searchRoom() {
     23    let foundRoom = availableShapes.find(sh => sh.info.name === from)
     24    console.log("map fnum",map.floorNum)
     25    if(foundRoom.floorNum !== map.floorNum){
     26      handleFloorChange(foundRoom.floorNum);
     27    }
    1828
     29
     30    console.log("FOUND ROOM: " + foundRoom)
     31    map.highlightShape(from);
     32    setSelectedRoom(foundRoom);
     33    setIsPanelOpen(true);
    1934  }
    2035
    21   const handleDirectionsSubmit = () => {
    22     console.log(`From: ${from}, To: ${to}`);
    23     const url = new URL('http://localhost:8080/api/public/navigate');
    24     url.searchParams.append('from', from);
    25     url.searchParams.append('to', to);
     36  const handleInputFocus = (field) => {
     37    if (availableOptions.length === 0 && map) {
     38      setAvailableOptions(
     39          availableShapes
     40              .filter((sh) => sh.className === "RenderedRoom")
     41              .map((shape) => shape.info.name)
     42      );
     43    }
     44    setDropdownVisible(true);
     45    setInputFieldType(field);
     46  };
    2647
    27     fetch(url)
    28             .then(response => {
    29                 if (!response.ok) {
    30                     throw new Error('Network response was not ok');
    31                 }
    32                 return response.json();
    33             })
    34             .then(data => {
    35                 console.log('Success:', data);
    36                 const points = data.map(item => item.coordinates);
    37                 props.map.drawRoute(points);
    38             })
    39             .catch(error => {
    40                 console.error('Error:', error);
    41             });
     48  const handleInputChange = (setter) => (event) => {
     49    const value = event.target.value;
     50    setter(value);
     51    setDropdownVisible(true);
     52
     53    const filtered = availableOptions.filter((option) =>
     54        option.toLowerCase().includes(value.toLowerCase())
     55    );
     56    setFilteredOptions(filtered);
     57  };
     58
     59  const handleOptionClick = (option) => {
     60    if (inputFieldType === "from") {
     61      setFrom(option);
     62    } else if (inputFieldType === "to") {
     63      setTo(option);
     64    }
     65    setDropdownVisible(false);
     66  };
     67
     68  const renderDropdown = () => {
     69    if (!dropdownVisible || filteredOptions.length === 0) return null;
     70
     71    const position = dropdownRef.current?.getBoundingClientRect() || { top: 0, left: 0, width: 0 };
     72
     73    return ReactDOM.createPortal(
     74        <ul
     75            className={styles.dropdown}
     76            style={{
     77              position: "absolute",
     78              top: position.top + position.height,
     79              left: position.left,
     80              width: position.width,
     81            }}
     82        >
     83          {filteredOptions.map((option, index) => (
     84              <li
     85                  key={index}
     86                  className={styles.dropdownItem}
     87                  onClick={() => handleOptionClick(option)}
     88              >
     89                {option}
     90              </li>
     91          ))}
     92        </ul>,
     93        document.body // Portal renders outside the parent hierarchy
     94    );
    4295  };
    4396
    4497  return (
    45     <div className={styles.wrapper}>
    46       {/* Regular search bar */}
    47       {!isExpanded ? (
    48         <div className={styles.searchBar}>
    49           <input
    50             type="search"
    51             className={styles.inputField}
    52             placeholder="Search location"
    53             aria-label="Search"
    54           />
    55           <div className={styles.buttons}>
    56             <button type="button" className={styles.iconButton} onClick={searchRoom}>
    57               <img src={searchIcon} alt="Search Icon" />
    58             </button>
    59             <button type="button" className={styles.iconButton} onClick={toggleExpanded}>
    60               <img src={routeIcon} alt="Route Icon" />
    61             </button>
    62           </div>
    63         </div>
    64       ) : (
    65         /* Expanded view for directions */
    66         <div className={styles.directionsContainer}>
    67           <div className={styles.directionsInputs}>
    68             <input
    69               type="text"
    70               placeholder="From"
    71               aria-label="From"
    72               value={from}
    73               onChange={(e) => setFrom(e.target.value)}
    74               className={styles.inputField}
    75             />
    76             <input
    77               type="text"
    78               placeholder="To"
    79               aria-label="To"
    80               value={to}
    81               onChange={(e) => setTo(e.target.value)}
    82               className={styles.inputField}
    83             />
    84           </div>
    85           <div className={styles.buttons}>
    86             <button type="button" className={styles.iconButton} onClick={handleDirectionsSubmit}>
    87               <img src={searchIcon} alt="Submit Directions" />
    88             </button>
    89             <button type="button" className={styles.iconButton} onClick={toggleExpanded}>
    90               <img src={closeIcon} alt="Close Icon" />
    91             </button>
    92           </div>
    93         </div>
    94       )}
    95     </div>
     98      <div className={styles.wrapper}>
     99        {!isExpanded ? (
     100            <div className={styles.searchBar}>
     101              <input
     102                  type="search"
     103                  className={styles.inputField}
     104                  placeholder="Search location"
     105                  aria-label="Search"
     106                  ref={dropdownRef} // Attach the input to calculate dropdown position
     107                  onFocus={() => handleInputFocus("from")}
     108                  onChange={handleInputChange(setFrom)}
     109                  value={from}
     110              />
     111              {renderDropdown()}
     112              <div className={styles.buttons}>
     113                <button type="button" className={styles.iconButton} onClick={searchRoom}>
     114                  <img src={searchIcon} alt="Search Icon" />
     115                </button>
     116                <button type="button" className={styles.iconButton} onClick={toggleExpanded}>
     117                  <img src={routeIcon} alt="Route Icon" />
     118                </button>
     119              </div>
     120            </div>
     121        ) : (
     122            <div className={styles.directionsContainer}>
     123              <div className={styles.directionsInputs}>
     124                <input
     125                    type="text"
     126                    placeholder="From"
     127                    aria-label="From"
     128                    value={from}
     129                    onFocus={() => handleInputFocus("from")}
     130                    onChange={handleInputChange(setFrom)}
     131                    className={styles.inputField}
     132                    ref={inputFieldType === "from" ? dropdownRef : null}
     133                />
     134                <input
     135                    type="text"
     136                    placeholder="To"
     137                    aria-label="To"
     138                    value={to}
     139                    onFocus={() => handleInputFocus("to")}
     140                    onChange={handleInputChange(setTo)}
     141                    className={styles.inputField}
     142                    ref={inputFieldType === "to" ? dropdownRef : null}
     143                />
     144                {renderDropdown()}
     145              </div>
     146              <div className={styles.buttons}>
     147                <button
     148                    type="button"
     149                    className={styles.iconButton}
     150                    onClick={() => handleDirectionsSubmit(from, to)}
     151                >
     152                  <img src={searchIcon} alt="Submit Directions" />
     153                </button>
     154                <button type="button" className={styles.iconButton} onClick={toggleExpanded}>
     155                  <img src={closeIcon} alt="Close Icon" />
     156                </button>
     157              </div>
     158            </div>
     159        )}
     160      </div>
    96161  );
    97162}
  • imaps-frontend/src/components/SearchBar/SearchBar.module.css

    rd565449 r0c6b92a  
    11.wrapper {
     2  position: relative;
    23  display: flex;
    34  justify-content: center;
    4   margin: 0px auto;
     5  margin: 0 auto;
     6  z-index: 105;
    57  max-width: 600px;
    68  width: 100%;
     9  padding: 10px;
    710}
    811
    912.searchBar,
    1013.directionsContainer {
     14  position: relative;
    1115  display: flex;
    12   align-items: end; /* Ensure all items remain aligned in the center */
    13   width: 100%;
     16  align-items: center;
     17  /*width: 100%;*/
    1418  background-color: white;
    1519  padding: 10px;
     
    1721  border-radius: 30px;
    1822  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    19   flex-wrap: nowrap; /* Prevent items from wrapping to the next line */
     23  flex-wrap: nowrap;
    2024}
    2125
     
    2832  outline: none;
    2933  margin-right: 10px;
    30   min-width: 0; /* Ensure the input field remains flexible */
     34  min-width: 0;
     35
    3136}
     37
     38.dropdown {
     39  position: absolute; /* Position absolute to detach from wrapper flow */
     40  top: 100%; /* Place below the search bar */
     41  left: 0;
     42  background-color: white;
     43  border: 1px solid #ddd;
     44  border-radius: 5px;
     45  width: 100%; /* Match the search bar width */
     46  max-height: 200px;
     47  overflow-y: auto;
     48  z-index: 9999; /* Highest level to ensure it's above everything */
     49  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
     50}
     51
     52
     53.dropdownItem {
     54  padding: 10px;
     55  cursor: pointer;
     56  transition: background-color 0.3s ease, color 0.3s ease;
     57}
     58
     59.dropdownItem:hover {
     60  background-color: #007bff; /* Highlight color */
     61  color: white; /* Ensure text is readable */
     62}
     63
    3264
    3365.buttons {
     
    5082  font-size: 16px;
    5183  transition: background-color 0.3s ease;
    52   width: auto;
    5384}
    5485
     
    6293}
    6394
    64 /* Expanded view for the direction inputs */
    6595.directionsContainer {
    66   flex-direction: column;
    6796  gap: 10px;
    6897}
     
    73102  gap: 10px;
    74103}
    75 
    76 .directionsInputs .inputField {
    77   flex: 1;
    78 }
    79 
    80 /* Media query for responsive design */
    81 @media (max-width: 768px) {
    82   .wrapper {
    83     max-width: 100%;
    84     padding: 10px;
    85   }
    86 
    87   /* Keep buttons and input fields inline */
    88   .searchBar,
    89   .directionsContainer {
    90     flex-direction: row; /* Keep items in a row */
    91     align-items: center; /* Keep buttons and inputs on the same line */
    92     flex-wrap: nowrap; /* Prevent items from wrapping */
    93     padding: 8px;
    94   }
    95 
    96   .inputField {
    97     width: 100%;
    98     flex-grow: 1;
    99     margin-right: 10px;
    100   }
    101 
    102   .buttons {
    103     width: auto;
    104     gap: 5px;
    105   }
    106 
    107   .iconButton {
    108     width: auto;
    109     padding: 10px;
    110   }
    111 }
  • imaps-frontend/src/components/SideBar/SideBar.jsx

    rd565449 r0c6b92a  
    2424            </li>
    2525            <li>
    26               <Link to="/Maps">Maps</Link>
     26              <Link to="/Maps">Browse Maps</Link>
    2727            </li>
    2828            <li>
    29               <Link to="/Maps/FinkiMaps/Draw">Draw</Link>
     29              <Link to="/myMaps">My Maps</Link>
    3030            </li>
    3131          </ul>
  • imaps-frontend/src/components/SideBar/SideBar.module.css

    rd565449 r0c6b92a  
    9090  top: 0;
    9191  left: 0;
    92   width: 100%;
    93   height: 100%;
     92  width: 100vw;
     93  height: 100vh;
    9494  background-color: rgba(0, 0, 0, 0.4);
    95   z-index: 900;
     95  z-index: 100;
    9696}
  • imaps-frontend/src/index.css

    rd565449 r0c6b92a  
     1
     2
    13html,
    24body,
  • imaps-frontend/src/pages/BrowseMaps/Maps.module.css

    rd565449 r0c6b92a  
     1
     2
    13.container {
    24  max-width: 1200px;
     
    79
    810h1 {
     11  font-family: exo, sans-serif;
    912  font-size: 2.5rem;
    10   color: #34495e;
     13  color: #0f71da;
    1114  margin-bottom: 20px;
    1215}
     
    6265}
    6366
     67.favorite{
     68  position: absolute;
     69  top: 20px;
     70  right: 20px;
     71  cursor: pointer;
     72}
     73
    6474/* When dragging a tile */
    6575.dragging {
  • imaps-frontend/src/pages/Draw/Draw.jsx

    rd565449 r0c6b92a  
    1 import { useEffect, useState } from "react";
    2 import { MapBuilder } from "../../scripts/main/MapBuilder.js";
     1import { useContext, useEffect, useState } from "react";
    32import styles from "./Draw.module.css";
    4 import RoomModal from "../../components/RoomModal/RoomModal.jsx";
    5 import SideBar from "../../components/SideBar/SideBar.jsx";
    6 import EntranceModal from "../../components/EntranceModal/EntranceModal.jsx";
     3import RoomModal from "../../components/Modals/RoomModal/RoomModal.jsx";
     4import EntranceModal from "../../components/Modals/EntranceModal/EntranceModal.jsx";
    75import DrawGuide from "../../components/DrawGuide/DrawGuide.jsx";
    8 import RoomTypeModal from "../../components/RoomTypeModal/RoomTypeModal.jsx";
    9 import InfoPinModal from "../../components/InfoPinModal/InfoPinModal.jsx";
     6import RoomTypeModal from "../../components/Modals/RoomTypeModal/RoomTypeModal.jsx";
     7import InfoPinModal from "../../components/Modals/InfoPinModal/InfoPinModal.jsx";
     8import SaveMap from "../../components/SaveMap/SaveMap.jsx";
     9import Logo from "../../components/Logo/Logo.jsx";
     10import { Link, useNavigate, useParams, useSearchParams } from "react-router-dom";
     11import Profile from "../../components/Profile/Profile.jsx";
    1012import HttpService from "../../scripts/net/HttpService.js";
    11 import SaveMap from "../../components/SaveMap/SaveMap.jsx";
    12 import logo from "../../assets/logo_icon.png";
    13 import MapTemplateSelector from "../../components/MapTemplateSelector/LoadMap.jsx";
    14 import KeymapPanel from "../../components/KeyMappingsGuidePanel/KeymapPanel.jsx";
    15 import { Link } from "react-router-dom";
     13import StairsModal from "../../components/Modals/StairsModal/StairsModal.jsx";
     14import useMapLoader from "./Hooks/useMapLoader.js";
     15import {FloorSelector} from "./FloorSelector.jsx";
     16import {useRoomTypesLoader} from "./Hooks/useRoomTypesLoader.js";
     17import {useAppContext} from "../../components/AppContext/AppContext.jsx";
     18import config from "../../scripts/net/netconfig.js";
     19import ShapeRegistry from "../../scripts/util/ShapeRegistry.js";
    1620
    1721function Draw() {
    18   const [selectedFloor, setSelectedFloor] = useState(1);
    19   const [app, setApp] = useState(null);
     22  const { mapName } = useParams();
     23  const { username } = useAppContext();
     24
    2025  const [isPopupVisible, setIsPopupVisible] = useState(false);
     26  const [errorMessage, setErrorMessage] = useState("Error");
     27  const [hasError, setHasError] = useState(false);
     28  const [searchParams, setSearchParams] = useSearchParams();
     29
     30  const[roomTypes,setRoomTypes] = useState([]);
     31
     32  const {app,floors,saveFloor,setFloors} = useMapLoader(mapName,username,searchParams,setSearchParams)
     33  const {addRoomType} = useRoomTypesLoader(setRoomTypes,mapName,username);
     34
     35  const addFloorHandler = async (newFloorNum) => {
     36    const httpService = new HttpService();
     37    httpService.setAuthenticated();
     38
     39    const payload = {
     40      num: newFloorNum,
     41      mapName: mapName,
     42    };
     43
     44    try {
     45      await httpService.put(`${config.floors.add}`, payload);
     46      console.log(`Added floor ${newFloorNum}`);
     47      setFloors((prevFloors) => [...prevFloors, { num: newFloorNum }]);
     48    } catch (error) {
     49      console.error("Error adding floor:", error);
     50    }
     51  };
     52
     53  const deleteFloorHandler = async (floorNum) => {
     54    if(floorNum === 0) return
     55
     56    const httpService = new HttpService();
     57    httpService.setAuthenticated();
     58
     59    try {
     60      await httpService.delete(`${config.floors.delete}?floorNum=${floorNum}&mapName=${mapName}`);
     61      setFloors((prevFloors) => prevFloors.filter(f => f.num !== floorNum))
     62
     63      const currFloor = searchParams.get("floor");
     64      if(currFloor == floorNum){
     65        setSearchParams({floor:"0"},{replace:true})
     66      }
     67      console.log(`Deleted floor ${floorNum}`);
     68    } catch (error) {
     69      console.error("Error deleting floor:", error);
     70    }
     71  };
     72
    2173  useEffect(() => {
    22     const app = new MapBuilder("container");
    23     setApp(app);
    24     // fpsCounterLoop();
     74    return () => {
     75      ShapeRegistry.clear();
     76    }
    2577  }, []);
    2678
    27   const handleFloorChange = (event) => {
    28     setSelectedFloor(event.target.value);
    29     console.log(`Floor changed to: ${event.target.value}`);
    30   };
    3179
    32   const handleRenderClick = () => {
    33     setIsPopupVisible(true);
     80  const handleSaveClick = async () => {
     81    saveFloor().then(r => {
     82     floors.forEach(flr => {
     83       setIsPopupVisible(true);
     84       setTimeout(() => {
     85         setIsPopupVisible(false);},
     86           3000);
     87       console.log("floor after save: " + JSON.stringify(flr))
     88     })
     89    });
    3490
    35     setTimeout(() => {
    36       setIsPopupVisible(false);
    37     }, 3000);
    38   };
    3991
    40   const handleSaveClick = async (mapName) => {
    41     const resp = await app.saveMap(mapName);
    42   };
    43   const handleLoadMapClick = (data) => {
    44     app.deserializeMap(data);
    4592  };
    4693
    4794  return (
    4895    <div className={styles.wrapper} id="wrapper">
    49       <SideBar></SideBar>
     96      {/* <SideBar></SideBar> */}
     97      <Logo></Logo>
    5098      <div id="container" className={styles.cont}></div>
    5199      <div className={styles.panel}>
    52         <Link to="/">
    53           <img src={logo} alt="Finki Logo" className={styles.logo} />
     100        <div className={styles.topPanelH}>
     101          <Profile position="inline"></Profile>
     102        </div>
     103        <Link to={`/myMaps/${mapName}/View`} className={styles.titleLink}>
     104          <h1 className={styles.title}>{mapName}</h1>
    54105        </Link>
    55         <h1 className={styles.title}>Map Builder</h1>
    56         {/* <div id="fpscont" className={styles.fpscounter}>
    57           <p id="fpsCounter"></p>
    58         </div> */}
    59106        <div className={styles.guideWrapper}>
    60           <DrawGuide />
     107          <DrawGuide/>
    61108        </div>
    62 
    63         {/* <div className={styles.floorSelector}>
    64           <label htmlFor="floorSelect">Select Floor:</label>
    65           <select
    66             id="floorSelect"
    67             value={selectedFloor}
    68             onChange={handleFloorChange}
    69             className={styles.floorDropdown}
    70           >
    71             <option value={1}>1st Floor</option>
    72             <option value={2}>2nd Floor</option>
    73             <option value={3}>3rd Floor</option>
    74             <option value={4}>4th Floor</option>
    75           </select>
    76         </div> */}
    77         {/* <h2 className={styles.paragraph}>Objects:</h2> */}
     109        <hr/>
     110        <br/>
     111        {/* {<h2 className={styles.paragraph}>Objects:</h2>} */}
    78112        <ul className={styles.shapeOptions} id="shapeOptions">
    79113          <li data-info="Entrance" className={`${styles.shapeOption} ${styles.entrance}`}></li>
    80114          <li data-info="Wall" className={`${styles.shapeOption} ${styles.wall}`} id="wall"></li>
    81115          <li data-info="Room" className={`${styles.shapeOption} ${styles.room}`} id="room"></li>
     116          <li data-info="Stairs" className={`${styles.shapeOption} ${styles.stairs}`} id="stairs"></li>
    82117        </ul>
    83         <br />
    84         <RoomTypeModal map={app}></RoomTypeModal>
     118        <RoomTypeModal map={app} roomTypes={roomTypes} addRoomTypeDB={addRoomType}></RoomTypeModal>
     119        <br/>
     120        <hr/>
     121        <br/>
     122        <FloorSelector floorConfig={{
     123          floors,searchParams,
     124          setSearchParams,addFloorHandler,
     125          deleteFloorHandler
     126        }}></FloorSelector>
    85127
    86         <div id="render" className={styles.buttonContainer}>
    87           <button
    88             id="render-button"
    89             type="button"
    90             className={styles.renderButton}
    91             onClick={handleRenderClick}
    92           >
    93             Render
    94           </button>
    95         </div>
     128        <br/>
     129
     130        <hr/>
     131        <br/>
     132        {hasError && <p style={{color: "red", textAlign: "center"}}>{errorMessage}</p>}
    96133        <div className={styles.templateCont}>
    97134          <SaveMap submitHandler={handleSaveClick}></SaveMap>
    98           <MapTemplateSelector loadHandler={handleLoadMapClick}></MapTemplateSelector>
    99135        </div>
    100136
    101137        <div className={styles.hide}>
    102           <RoomModal map={app}></RoomModal>
     138          <RoomModal map={app} roomTypes={roomTypes}></RoomModal>
    103139          <EntranceModal map={app}></EntranceModal>
    104140          <InfoPinModal map={app}></InfoPinModal>
     141          <StairsModal map={app}></StairsModal>
    105142        </div>
    106143      </div>
    107144
    108145      {isPopupVisible && (
    109         <div className={styles.popup}>
    110           <div className={styles.popupContent}>
    111             <h2>Map Rendered!</h2>
    112             <p>Your map has been successfully rendered.</p>
     146          <div className={styles.popup}>
     147            <div className={styles.popupContent}>
     148              <h2>Map Saved!</h2>
     149              <p>Your map has been successfully saved.</p>
     150            </div>
    113151          </div>
    114         </div>
    115152      )}
    116153    </div>
  • imaps-frontend/src/pages/Draw/Draw.module.css

    rd565449 r0c6b92a  
    44  padding: 0;
    55  height: 100%;
    6   overflow: hidden;
     6  overflow-x: hidden;
    77  background-color: #141414;
    88}
    99
     10.topPanelH {
     11  width: 100%;
     12  display: flex;
     13  justify-content: center;
     14  align-items: center;
     15  margin-bottom: 20px;
     16}
     17
    1018.logo {
    11   width: 100px;
    12   margin: 10px;
    13   align-self: center;
     19  width: 80px;
    1420}
    1521
    1622.title {
    17   margin-top: 20px;
    18   color: #804047;
    19   font-size: 120%;
     23  text-align: center;
     24  font-size: 2.5em;
     25  font-weight: 700;
     26  text-transform: uppercase;
     27  color: #333333;
     28  font-family: "Oswald", sans-serif;
     29  margin-bottom: 0;
     30  text-decoration: none;
     31}
     32
     33.titleLink {
     34  text-decoration: none;
     35}
     36.titleLink:hover{
     37  text-decoration: none;
    2038}
    2139
     
    2341  color: #cccccc;
    2442}
     43
    2544.guideWrapper {
    2645  display: flex;
     
    2847  align-items: center;
    2948  margin: 0;
     49  width: 100%;
    3050}
    3151
     
    5171  display: flex;
    5272  flex-direction: column;
     73  align-items: center; /* Center elements horizontally */
    5374  padding: 20px;
    5475  background-color: #1c1c1c;
     
    5879  margin-right: 10px;
    5980  border-radius: 20px;
     81  overflow-x: hidden;
    6082}
    6183
    6284.buttonContainer {
    6385  display: flex;
    64   justify-content: space-between;
     86  justify-content: center;
    6587  align-items: center;
    6688  margin-top: 2rem;
    6789  gap: 10px;
    68 }
    69 
    70 /* .hide {
    71   visibility: hidden;
    72 } */
     90  width: 100%;
     91}
    7392
    7493.renderButton {
     
    104123  list-style: none;
    105124  padding: 0;
    106   margin: 0 0;
    107   display: flex;
    108   flex-direction: column;
     125  margin: 0;
     126  display: grid;
     127  grid-template-columns: repeat(2, 1fr);
    109128  gap: 15px;
     129  justify-items: center;
    110130  align-items: center;
    111131}
     
    127147}
    128148
    129 .templateCont {
    130   display: flex;
    131   flex-direction: column;
    132 }
    133 
    134149.shapeOption:hover {
    135150  background-color: #94183b;
     
    137152}
    138153
     154.templateCont {
     155  display: flex;
     156  flex-direction: column;
     157  align-items: center; /* Center section horizontally */
     158  width: 100%;
     159}
     160
    139161.fpscounter {
    140162  border: 1px solid #00b822;
    141163  margin: 20px 0;
     164  text-align: center;
     165  width: 100%;
    142166}
    143167
     
    145169  color: #00740c;
    146170  font-weight: bold;
    147   text-align: center;
    148171  margin: 0;
    149172  padding: 5px 0;
    150173}
    151174
    152 .floorSelector {
    153   margin-bottom: 20px;
    154   display: flex;
    155   flex-direction: column;
    156   gap: 8px;
    157   color: #ffffff;
    158 }
    159 
    160 .floorSelector label {
    161   font-size: 14px;
    162 }
    163 
    164 .floorDropdown {
    165   padding: 8px;
    166   font-size: 14px;
    167   border-radius: 5px;
    168   border: 1px solid #ccc;
    169   background-color: #3a3a3a;
    170   color: #ffffff;
    171   cursor: pointer;
    172   width: 100%;
    173   transition: border-color 0.3s;
    174 }
    175 
    176 .floorDropdown:focus {
    177   outline: none;
    178   border-color: #1e90ff;
    179 }
    180175
    181176.popup {
     
    226221  background-image: url("./shapeImages/room_icon.png");
    227222}
     223.stairs {
     224  background-image: url("./shapeImages/stairs_icon.png");
     225}
     226.floorSection {
     227  display: flex;
     228  flex-direction: column;
     229  align-items: center;
     230  gap: 20px;
     231  background-color: #2d2d2d;
     232  padding: 20px;
     233  border-radius: 8px;
     234  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
     235  width: 100%;
     236}
     237
     238.floorLabel {
     239  font-size: 16px;
     240  font-weight: bold;
     241  color: #ffffff;
     242  margin-bottom: 10px;
     243  text-align: center;
     244}
     245
     246.floorList {
     247  display: flex;
     248  flex-direction: column;
     249  align-items: center;
     250  gap: 15px;
     251  width: 100%;
     252}
     253
     254.floorItems {
     255  display: flex;
     256  flex-direction: column;
     257  align-items: center;
     258  gap: 10px;
     259  width: 100%;
     260}
     261
     262.addFloorButton {
     263  background-color: #e67e22;
     264  border: none;
     265  border-radius: 50%;
     266  width: 40px;
     267  height: 40px;
     268  display: flex;
     269  justify-content: center;
     270  align-items: center;
     271  cursor: pointer;
     272  transition: background-color 0.3s, transform 0.2s;
     273}
     274
     275.addFloorButton:hover {
     276  background-color: #d2691e;
     277  transform: scale(1.1);
     278}
     279
     280.addFloorButton:active {
     281  background-color: #b35418;
     282  transform: scale(0.95);
     283}
     284
     285.icon {
     286  width: 20px;
     287  height: 20px;
     288  filter: invert(1);
     289}
     290
     291.floorButton {
     292  padding: 10px 15px;
     293  font-size: 14px;
     294  color: #ffffff;
     295  background-color: #3a3a3a;
     296  border: 1px solid #555555;
     297  border-radius: 8px;
     298  width: 100%;
     299  text-align: center;
     300  cursor: pointer;
     301  transition: background-color 0.3s, transform 0.2s;
     302}
     303
     304.floorButton:hover {
     305  background-color: #4a4a4a;
     306}
     307
     308.floorButton.active {
     309  background-color: #1e90ff;
     310  color: #ffffff;
     311  font-weight: bold;
     312}
     313
     314.floorInput {
     315  width: 60%;
     316  padding: 8px;
     317  font-size: 14px;
     318  border: 1px solid #ccc;
     319  border-radius: 5px;
     320  background-color: #3a3a3a;
     321  color: #ffffff;
     322  text-align: center;
     323  transition: border-color 0.3s;
     324}
     325
     326.floorInput:focus {
     327  outline: none;
     328  border-color: #1e90ff;
     329}
     330
     331.floorActions {
     332  display: flex;
     333  gap: 10px;
     334  justify-content: center;
     335  align-items: center;
     336  margin-top: 10px;
     337}
     338
     339.errorMessage {
     340  color: red;
     341  font-size: 12px;
     342  margin-top: 10px;
     343  text-align: center;
     344}
     345.floorItemWrapper {
     346  display: flex;
     347  align-items: center;
     348  gap: 10px;
     349  width: 100%;
     350}
     351
     352.floorItem {
     353  padding: 10px 15px;
     354  font-size: 14px;
     355  color: #ffffff;
     356  background-color: #3a3a3a;
     357  border: 1px solid #555555;
     358  border-radius: 8px;
     359  text-align: center;
     360  cursor: pointer;
     361  flex-grow: 1;
     362  transition: background-color 0.3s;
     363}
     364
     365.floorItem:hover {
     366  background-color: #4a4a4a;
     367}
     368
     369.activeFloor {
     370  background-color: #1e90ff;
     371  color: #ffffff;
     372  font-weight: bold;
     373}
     374
     375.deleteFloorButton {
     376  background-color: #ff4d4d;
     377  border: none;
     378  border-radius: 8px;
     379  padding: 10px;
     380  color: white;
     381  cursor: pointer;
     382  transition: background-color 0.3s, transform 0.2s;
     383}
     384
     385.deleteFloorButton:hover {
     386  background-color: #e60000;
     387  transform: scale(1.1);
     388}
     389
     390.deleteFloorButton:active {
     391  background-color: #cc0000;
     392  transform: scale(0.95);
     393}
  • imaps-frontend/src/pages/IMaps/IMaps.jsx

    rd565449 r0c6b92a  
    44import styles from "./IMaps.module.css";
    55import Home from "./components/pages/Home";
     6import Profile from "../../components/Profile/Profile";
     7import Logo from "../../components/Logo/Logo";
     8import Cards from "./components/Cards";
    69
    710function IMaps() {
    811  return (
    9     <>
    10       <div className={styles.home}>
    11         <Navbar></Navbar>
    12         <Home></Home>
    13       </div>
    14     </>
     12    <div className="homebg">
     13      <Navbar></Navbar>
     14      <Home></Home>
     15    </div>
    1516  );
    1617}
  • imaps-frontend/src/pages/IMaps/IMaps.module.css

    rd565449 r0c6b92a  
    55html,
    66body {
    7   overflow: auto;
    8   overflow-x: hidden;
     7  margin: 0;
    98}
  • imaps-frontend/src/pages/IMaps/components/Button.css

    rd565449 r0c6b92a  
     1@import url(https://fonts.googleapis.com/css?family=Exo:500);
    12:root {
    2   --primary: #fff;
     3  --primary: #252627;
    34}
    45
    56.btn {
    67  padding: 8px 20px;
    7 
     8  font-family: exo, sans-serif;
    89  border-radius: 20px;
    910  outline: none;
     
    1415.btn--primary {
    1516  background-color: var(--primary);
    16 
    17   color: #242424;
     17  color: #ffffff;
    1818  border: 1px solid var(--primary);
    1919}
     
    2424  color: #fff;
    2525  padding: 8px 20px;
    26   border: 1px solid var(--primary);
     26  /* border: 1px solid var(--primary); */
    2727  transition: all 0.3s ease-out;
    2828}
     
    3030.btn--medium {
    3131  padding: 8px 20px;
    32   font-size: 18px;
     32  font-size: 22px;
    3333}
    3434
    3535.btn--large {
    3636  padding: 12px 26px;
    37   font-size: 20px;
     37  font-size: 24px;
    3838}
    3939
     
    4141.btn--medium:hover {
    4242  transition: all 0.3s ease-out;
    43   background: #fff;
    44   color: #242424;
     43  background: #ffffffc2;
     44  color: #000000;
    4545  transition: 250ms;
    4646}
  • imaps-frontend/src/pages/IMaps/components/HeroSection.css

    rd565449 r0c6b92a  
     1@import url(https://fonts.googleapis.com/css?family=Exo:500);
     2
    13video {
    24  object-fit: cover;
     
    79}
    810
     11
     12
     13
    914.hero-container {
    10   background: url("../images/img-home.jpg") center center/cover no-repeat;
    1115  height: 100vh;
    1216  width: 100%;
     
    1519  justify-content: center;
    1620  align-items: center;
    17   box-shadow: inset 0 0 0 1000px rgba(0, 0, 0, 0.4);
     21
    1822  object-fit: contain;
     23
     24
     25
    1926}
    2027
     
    2330  font-size: 100px;
    2431  margin-top: -100px;
     32  font-family: exo, sans-serif;
     33  text-transform: uppercase;
     34  text-shadow: 1px 1px 0px #ef404e,
     35  1px 2px 0px #ef404e,
     36  1px 3px 0px #ef404e,
     37  1px 4px 0px #ef404e,
     38  1px 5px 0px #ef404e,
     39  1px 6px 0px #ef404e,
     40  1px 10px 5px rgba(16, 16, 16, 0.5),
     41  1px 15px 10px rgba(16, 16, 16, 0.4),
     42  1px 20px 30px rgba(16, 16, 16, 0.3),
     43  1px 25px 50px rgba(16, 16, 16, 0.2);
    2544}
    2645
  • imaps-frontend/src/pages/IMaps/components/HeroSection.jsx

    rd565449 r0c6b92a  
    77function HeroSection() {
    88  return (
    9     <div className="hero-container">
    10       <h1>Map Your World</h1>
    11       <p>Create and explore detailed indoor maps.</p>
    12       <div className="hero-btns">
    13         <Link to="/Maps/FinkiMaps/Draw">
    14           <Button className="btns" buttonStyle="btn--outline" buttonSize="btn--large">
    15             Create maps
    16           </Button>
    17         </Link>
    18         <Link to="/Maps">
    19           <Button
    20             className="btns"
    21             buttonStyle="btn--primary"
    22             buttonSize="btn--large"
    23             onClick={console.log("hey")}
    24           >
    25             Browse Maps <i className="far fa-play-circle" />
    26           </Button>
    27         </Link>
     9      <div className="hero-container">
     10          <h1>Map Your World</h1>
     11          <p>Create and explore detailed indoor maps.</p>
     12          <div className="hero-btns">
     13              <Link to="/myMaps">
     14                  <Button className="btns" buttonStyle="btn--outline" buttonSize="btn--large">
     15                      Create maps
     16                  </Button>
     17              </Link>
     18              <Link to="/Maps">
     19                  <Button
     20                      className="btns"
     21                      buttonStyle="btn--primary"
     22                      buttonSize="btn--large"
     23                  >
     24                      Browse Maps <i className="far fa-play-circle"/>
     25                  </Button>
     26              </Link>
     27          </div>
    2828      </div>
    29     </div>
    3029  );
    3130}
  • imaps-frontend/src/pages/IMaps/components/Navbar.css

    rd565449 r0c6b92a  
    1 .navbar {
    2   background: linear-gradient(90deg, rgb(28, 27, 27) 0%, rgb(26, 23, 23) 100%);
    3   height: 80px;
     1.modern-navbar {
     2  /*background-color: #ffffff;*/
     3  /*background: linear-gradient(*/
     4  /*        90deg,*/
     5  /*        #fafafa 0%,*/
     6  /*        #c07e7e 25%,*/
     7  /*        #af2525 50%,*/
     8  /*        #443a3a 75%,*/
     9  /*        #8d1010 100%*/
     10  /*);*/
     11  /*background: linear-gradient(to right, #ffffff, #c12c2c);*/
     12  /*background: linear-gradient(90deg, #3b3131, #ffffff);*/
     13  background: linear-gradient(90deg, #9b1818, #efefef);
     14  color: #ffffff;
     15  /*color: #333333;*/
    416  display: flex;
    517  justify-content: center;
    618  align-items: center;
    7   font-size: 1.2rem;
     19  padding: 0.8rem 1.5rem;
     20  box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
    821  position: sticky;
    922  top: 0;
    10   z-index: 999;
    11 }
    12 
    13 .pad-bot {
    14   /* padding-bottom: 1.5rem; */
     23  z-index: 1000;
     24  border-radius: 8px;
     25  margin: 5px;
    1526}
    1627
    1728.navbar-container {
    1829  display: flex;
    19   justify-content: center;
     30  justify-content: space-between;
    2031  align-items: center;
    2132  width: 100%;
     33  max-width: 1200px;
    2234}
    2335
    24 .navbar-logo {
    25   color: #fff;
    26   justify-self: start;
    27   margin-left: 20px;
    28   cursor: pointer;
    29   text-decoration: none;
    30   font-size: 2rem;
     36.navbar-left {
    3137  display: flex;
    3238  align-items: center;
     39  gap: 1rem;
    3340}
    3441
    35 .fa-typo3 {
    36   margin-left: 0.5rem;
    37   font-size: 1.8rem;
     42.navbar-title {
     43  font-size: 1.6rem;
     44  font-weight: 700;
     45  color: #ffffff;
     46  margin: 0;
     47  font-family: "exo", sans-serif;
    3848}
    3949
    40 .nav-menu {
    41   display: grid;
    42   grid-template-columns: repeat(4, auto);
    43   grid-gap: 10px;
    44   list-style: none;
    45   text-align: center;
    46   width: 70vw;
    47   justify-content: end;
    48   margin-right: 2rem;
     50.navbar-right {
     51  display: flex;
     52  align-items: center;
     53  gap: 1rem;
    4954}
    5055
    51 .nav-item {
    52   height: 80px;
     56.navbar-btn {
     57  padding: 0.4rem 1.5rem;
     58  font-size: 0.9rem;
     59  border: 2px solid transparent;
     60  border-radius: 20px;
     61  text-decoration: none;
     62  color: #ffffff;
     63  font-weight: 500;
     64  transition: all 0.3s ease-in-out;
     65  font-family: "exo", sans-serif;
     66  cursor: pointer;
     67  margin-left: 4em;
     68  margin-right: 1em;
    5369}
    5470
    55 .nav-links {
    56   color: #fff;
    57   display: flex;
    58   align-items: center;
    59   text-decoration: none;
    60   padding: 0.5rem 1rem;
    61   height: 100%;
     71.navbar-login {
     72  background-color: #252627;
     73  border: 2px solid #3f3232;
    6274}
    6375
    64 .nav-links:hover {
    65   border-bottom: 4px solid #fff;
    66   transition: all 0.2s ease-out;
     76.navbar-login:hover {
     77  background-color: #ffffff;
     78  color: #212121;
     79  border: 1px solid rgba(19, 18, 18, 0.82);
    6780}
    6881
    69 .fa-bars {
    70   color: #fff;
     82.navbar-signup {
     83  background-color: #252627;
     84  color: #ffffff;
     85  border: 2px solid #343a40;
    7186}
    7287
    73 .nav-links-mobile {
    74   display: none;
     88.navbar-signup:hover {
     89  background-color: #ffffff;
     90  color: #343a40;
     91  border: 2px solid #343a40;
    7592}
    76 
    77 .menu-icon {
    78   display: none;
    79 }
    80 
    81 @media screen and (max-width: 960px) {
    82   .NavbarItems {
    83     position: relative;
    84   }
    85 
    86   .nav-menu {
    87     display: flex;
    88     flex-direction: column;
    89     width: 100%;
    90     height: 90vh;
    91     position: absolute;
    92     top: 80px;
    93     left: -100%;
    94     opacity: 1;
    95     transition: all 0.5s ease;
    96   }
    97 
    98   .nav-menu.active {
    99     background: #242222;
    100     left: 0;
    101     opacity: 1;
    102     transition: all 0.5s ease;
    103     z-index: 1;
    104   }
    105 
    106   .nav-links {
    107     text-align: center;
    108     padding: 2rem;
    109     width: 100%;
    110     display: table;
    111   }
    112 
    113   .nav-links:hover {
    114     background-color: #fff;
    115     color: #242424;
    116     border-radius: 0;
    117   }
    118 
    119   .navbar-logo {
    120     position: absolute;
    121     top: 0;
    122     left: 0;
    123     transform: translate(25%, 50%);
    124   }
    125 
    126   .menu-icon {
    127     display: block;
    128     position: absolute;
    129     top: 0;
    130     right: 0;
    131     transform: translate(-100%, 60%);
    132     font-size: 1.8rem;
    133     cursor: pointer;
    134   }
    135 
    136   .fa-times {
    137     color: #fff;
    138     font-size: 2rem;
    139   }
    140 
    141   .nav-links-mobile {
    142     display: block;
    143     text-align: center;
    144     margin: 2rem auto;
    145     border-radius: 4px;
    146     width: 80%;
    147     text-decoration: none;
    148     font-size: 1.5rem;
    149     background-color: transparent;
    150     color: #fff;
    151     padding: 14px 20px;
    152     border: 1px solid #fff;
    153     transition: all 0.3s ease-out;
    154   }
    155 
    156   .nav-links-mobile:hover {
    157     background: #fff;
    158     color: #242424;
    159     transition: 250ms;
    160   }
    161 }
  • imaps-frontend/src/pages/IMaps/components/Navbar.jsx

    rd565449 r0c6b92a  
    1 import React, { useState, useEffect } from "react";
    2 import { Button } from "./Button";
     1import React, {useContext, useState} from "react";
     2import { Link } from "react-router-dom";
     3import logo_icon from "../../../assets/logo_icon.png";
     4//import { AuthContext } from "../../../components/AuthContext/AuthContext";
     5import Logo from "../../../components/Logo/Logo.jsx";
     6import Profile from "../../../components/Profile/Profile.jsx";
     7import {useAppContext} from "../../../components/AppContext/AppContext.jsx";
    38import "./Navbar.css";
    4 import { Link } from "react-router-dom";
    5 import logo from "../../../assets/logo_icon.png";
    69
    710function Navbar() {
     
    1215  const closeMobileMenu = () => setClick(false);
    1316
    14   const showButton = () => {
    15     if (window.innerWidth <= 960) {
    16       setButton(false);
    17     } else {
    18       setButton(true);
    19     }
    20   };
     17  const { isAuthenticated } = useAppContext();
    2118
    22   useEffect(() => {
    23     showButton();
    24   }, []);
     19    return (
     20        <nav className="modern-navbar">
     21            <div className="navbar-container">
     22                {/* Left Section - Logo and Title */}
     23                <div className="navbar-left">
     24                    <Logo position="relative"/>
     25                    <h1 className="navbar-title">iMaps</h1>
     26                </div>
    2527
    26   window.addEventListener("resize", showButton);
    27 
    28   return (
    29     <>
    30       <nav className="navbar">
    31         <div className="navbar-container">
    32           <a href="#" className="navbar-logo pad-bot" onClick={closeMobileMenu}>
    33             iMaps
    34           </a>
    35           <div className="menu-icon" onClick={handleClick}>
    36             <i className={click ? "fas fa-times" : "fas fa-bars"} />
    37           </div>
    38           <ul className={click ? "nav-menu active" : "nav-menu"}>
    39             <Link to="/Signup">
    40               <li className="nav-item">
    41                 <a href="#" className="nav-links" onClick={closeMobileMenu}>
    42                   SignUp
    43                 </a>
    44               </li>
    45             </Link>
    46           </ul>
    47           <div className="pad-bot">
    48             <Link to="/Login">{button && <Button buttonStyle="btn--outline">LOG IN</Button>}</Link>
    49           </div>
    50         </div>
    51       </nav>
    52     </>
    53   );
     28                {/* Right Section - Login/Signup or Profile */}
     29                <div className="navbar-right">
     30                    {isAuthenticated ? (
     31                        <Profile position="relative"/>
     32                    ) : (
     33                        <>
     34                            <Link to="/Login" className="navbar-btn navbar-login">
     35                                Log In
     36                            </Link>
     37                            <Link to="/Signup" className="navbar-btn navbar-signup">
     38                                Sign Up
     39                            </Link>
     40                        </>
     41                    )}
     42                </div>
     43            </div>
     44        </nav>
     45    );
    5446}
    5547
  • imaps-frontend/src/pages/IMaps/components/pages/Home.jsx

    rd565449 r0c6b92a  
    11import React from "react";
    2 import "./Home.css";
    3 import Cards from "../Cards";
    4 import HeroSection from "../HeroSection";
    5 import Footer from "../Footer";
     2import "./Home.scss";
     3import { Link } from "react-router-dom";
     4import { Button } from "../Button.jsx";
     5import Cards from "../Cards.jsx";
     6import Footer from "../Footer.jsx";
     7import sl from "../../../../assets/bg-home-light-gray.png"
    68
    79function Home() {
    810  return (
    9     <>
    10       <div className="home">
    11         <HeroSection />
    12         {/* <Cards /> */}
    13         <Footer />
     11    <div className="home">
     12      <div className="hero-container">
     13        <h1>Map Your World</h1>
     14        <h2 className="description">Create and explore detailed indoor maps.</h2>
     15        <div className="hero-btns">
     16          <Link to="/myMaps">
     17            <Button className="btns" buttonSize="btn--large">
     18              Create Maps
     19            </Button>
     20          </Link>
     21          <Link to="/Maps">
     22            <Button className="btns" buttonSize="btn--large">
     23              Browse Maps <i className="far fa-play-circle" />
     24            </Button>
     25          </Link>
     26        </div>
    1427      </div>
    15     </>
     28      <Cards />
     29      <Footer></Footer>
     30    </div>
    1631  );
    1732}
  • imaps-frontend/src/pages/Login/Login.jsx

    rd565449 r0c6b92a  
    1 import React, { useState } from "react";
    2 import { Link, useNavigate } from "react-router-dom";
     1import React, {useContext, useState} from "react";
     2import {Link, useLocation, useNavigate} from "react-router-dom";
    33import styles from "./Login.module.css";
    44import illustration from "../../assets/illustration_img.png";
     5import Logo from "../../components/Logo/Logo.jsx";
     6import HttpService from "../../scripts/net/HttpService.js";
     7import {useAppContext} from "../../components/AppContext/AppContext.jsx";
     8import config from "../../scripts/net/netconfig.js";
    59
    6 const LoginPage = ({onLogin}) => {
    7   const [username, setUsername] = useState("");
    8   const [password, setPassword] = useState("");
    9   const [error, setError] = useState(null);
    10   const navigate = useNavigate();
     10const LoginPage = () => {
     11    const [formUsername, setFormUsername] = useState("");
     12    const [password, setPassword] = useState("");
     13    const [error, setError] = useState(null);
     14    const navigate = useNavigate();
     15    const location = useLocation();
    1116
    12   const payload = {
    13     username: username,
    14     password: password
    15   };
     17    const {setUsername, setIsAuthenticated} = useAppContext();
    1618
    17   const handleLogin = (e) => {
    18     e.preventDefault();
     19    const {targetPath} = location.state || {targetPath: {pathname: "/"}};
    1920
    20     fetch("http://localhost:8080/api/auth/login", {
    21       method: "POST",
    22       headers: {
    23         "Content-Type": "application/json",
    24       },
    25       body: JSON.stringify(payload),
    26     })
    27       .then((response) => {
    28         if (!response.ok) {
    29           throw new Error("Login failed: resp = " + response.statusText);
    30         }
    31         return response.json();
    32       })
    33       .then((data) => {
    34         if (data.token) {
    35           navigate("/Maps/FinkiMaps/Draw");
    36           onLogin(data.token)
    37         } else {
    38           setError("Invalid username or password.");
    39         }
    40       })
    41       .catch((error) => {
    42         console.error("Login failed", error);
    43         setError("Login failed. Please try again.");
    44       });
    45    
    46   };
     21    const payload = {
     22        username: formUsername,
     23        password: password,
     24    };
    4725
    48   return (
    49     <div className={styles.wrapper}>
    50       <div className={styles.illustration}>
    51         <img src={illustration} alt="illustration" />
    52       </div>
    53       <div className={styles.form}>
    54         <div className={styles.heading}>LOGIN</div>
    55         <form onSubmit={handleLogin}>
    56           <div>
    57             <label htmlFor="username">Username</label>
    58             <input
    59               type="text"
    60               id="name"
    61               placeholder="Enter your username"
    62               onChange={(e) => setUsername(e.target.value)}
    63               value={username}
    64               required
    65             />
    66           </div>
    67           <div>
    68             <label htmlFor="password">Password</label>
    69             <input
    70               type="password"
    71               id="password"
    72               placeholder="Enter your password"
    73               onChange={(e) => setPassword(e.target.value)}
    74               value={password}
    75               required
    76             />
    77           </div>
    78           {error && <p className={styles.error}>{error}</p>}
    79           <button type="submit">Submit</button>
    80         </form>
    81         <p>
    82           Don't have an account? <Link to="/Signup"> Sign Up </Link>
    83         </p>
    84       </div>
    85     </div>
    86   );
     26    const handleLogin = async () => {
     27        const httpService = new HttpService();
     28        return httpService.post(config.auth.login, payload)
     29
     30    };
     31
     32
     33    const login = async (e) => {
     34        e.preventDefault();
     35
     36        handleLogin()
     37            .then(resp => {
     38                if (resp.token) {
     39                    navigate(targetPath)
     40                    localStorage.setItem("token", resp.token);
     41                    setUsername(resp.username);
     42                    setIsAuthenticated(true);
     43                    console.log("ROLES",resp.roles)
     44                } else {
     45                    setError("Invalid username or password.");
     46                }
     47            }).catch(reason => {
     48            console.error("Login failed", reason);
     49            setError("Login failed. Please try again.")
     50        })
     51
     52        // fetch("http://localhost:8080/api/auth/login", {
     53        //   method: "POST",
     54        //   headers: {
     55        //     "Content-Type": "application/json",
     56        //   },
     57        //   body: JSON.stringify(payload),
     58        // })
     59        //   .then((response) => {
     60        //     if (!response.ok) {
     61        //       throw new Error("Login failed: resp = " + response.statusText);
     62        //     }
     63        //     return response.json();
     64        //   })
     65        //   .then((data) => {
     66        //     if (data.token) {
     67        //       navigate(targetPath);
     68        //       handleLogin(data);
     69        //     } else {
     70        //       setError("Invalid username or password.");
     71        //     }
     72        //   })
     73        //   .catch((error) => {
     74        //     console.error("Login failed", error);
     75        //     setError("Login failed. Please try again.");
     76        //   });
     77    };
     78
     79    return (
     80        <div className={styles.wrapper}>
     81            <Logo></Logo>
     82            <div className={styles.illustration}>
     83                <img src={illustration} alt="illustration"/>
     84            </div>
     85            <div className={styles.form}>
     86                <div className={styles.heading}>LOGIN</div>
     87                <form onSubmit={login}>
     88                    <div>
     89                        <label htmlFor="username">Username</label>
     90                        <input
     91                            type="text"
     92                            id="name"
     93                            placeholder="Enter your username"
     94                            onChange={(e) => setFormUsername(e.target.value)}
     95                            value={formUsername}
     96                            required
     97                        />
     98                    </div>
     99                    <div>
     100                        <label htmlFor="password">Password</label>
     101                        <input
     102                            type="password"
     103                            id="password"
     104                            placeholder="Enter your password"
     105                            onChange={(e) => setPassword(e.target.value)}
     106                            value={password}
     107                            required
     108                        />
     109                    </div>
     110                    {error && <p className={styles.error}>{error}</p>}
     111                    <button type="submit">Submit</button>
     112                </form>
     113                <p>
     114                    Don't have an account? <Link to="/Signup"> Sign Up </Link>
     115                </p>
     116            </div>
     117        </div>
     118    );
    87119};
    88120
  • imaps-frontend/src/pages/Signup/Signup.jsx

    rd565449 r0c6b92a  
    33import illustration from "../../assets/illustration_img.png";
    44import styles from "./Signup.module.css";
     5import Logo from "../../components/Logo/Logo";
    56
    67export default function Signup() {
     
    5455  return (
    5556    <div className={styles.wrapper}>
     57      <Logo></Logo>
    5658      <div className={styles.illustration}>
    5759        <img src={illustration} alt="illustration" />
  • imaps-frontend/src/scripts/base/MapShape.js

    rd565449 r0c6b92a  
    66            throw new Error("Cannot instantiate abstract class BaseShape directly.");
    77        }
     8
     9        console.log(config.x,"FFF");
    810        super(config);
    911        this.layer = layer;
     
    1214        this.snappable = snap;
    1315        this._info = {};
    14         this.modalEventName = "";
     16        this.eventName = "";
    1517        this.infoText = null;
     18        this.floorNum;
    1619
    1720        this.shadowForStrokeEnabled(false);
    1821        this.on("mouseover", () => (document.body.style.cursor = "pointer"));
    1922        this.on("mouseout", () => (document.body.style.cursor = "default"));
    20         // this.on("dblclick", (e) => {
    21         //   this.moveToTop();
    22         //   this.getLayer()
    23         //     .find("Transformer")
    24         //     .forEach((t) => t.moveToTop());
    25         // });
     23        this.on("click", (e) => {
     24            if(e.evt.altKey){
     25                this.moveToTop();
     26                this.getLayer()
     27                    .find("Transformer")
     28                    .forEach((t) => t.moveToTop());
     29            }
     30
     31        });
    2632
    2733        if (snap) {
    2834            this.on("dragend", this.snapToGrid.bind(this));
    29             this.on('dblclick', this.snapToGrid.bind(this))
     35            this.on('dblclick', this.snapToGrid.bind(this));
    3036        }
    3137
     
    3642        });
    3743
    38         this.on('dragend', () => {
     44
     45        this.on('dragmove', () => {
    3946            if (this.infoText) {
    4047                this.updateTextPosition();
     
    7683    }
    7784
     85
     86    destroy() {
     87        super.destroy();
     88        if (this.infoText !== null) {
     89            this.infoText.remove()
     90            console.log("cleared text")
     91        }
     92    }
     93
     94    setInfo(infoObj){
     95        this.info = infoObj;
     96    }
     97
     98    load(){
     99        console.log("Abstract function")
     100    }
     101
    78102    _sceneFunc(context) {
    79103        let width = this.width();
     
    93117    }
    94118
     119    updateText(shapeName){
     120            this.infoText.text = shapeName;
     121            console.log("Updated text to : " + shapeName)
     122    }
     123
    95124    snapToGrid() {
    96125        this.position({
     
    98127            y: Math.round(this.y() / this.blockSize) * this.blockSize,
    99128        });
     129
    100130    }
    101131
  • imaps-frontend/src/scripts/main/MapBuilder.js

    rd565449 r0c6b92a  
    22import Konva from "konva";
    33import HttpService from "../net/HttpService.js";
    4 import { zoomStage } from "../util/zoomStage.js";
     4import {zoomStage} from "../util/zoomStage.js";
     5import {addEventHandling} from "../util/addEventHandling.js";
     6import MapNode from "../base/MapNode.js";
     7import {json} from "react-router-dom";
     8import log from "eslint-plugin-react/lib/util/log.js";
     9import ShapeRegistry from "../util/ShapeRegistry.js";
     10import shapeRegistry from "../util/ShapeRegistry.js";
     11import triggerMapSave from "../util/triggerMapSave.js";
    512
    613export class MapBuilder {
    7   constructor(containerId) {
    8     this.container = document.getElementById(containerId);
    9     this.stage = new Konva.Stage({
    10       container: containerId,
    11       width: this.container.clientWidth,
    12       height: this.container.clientHeight,
    13     });
    14 
    15     // TODO AKO DRAGNIT NEKOJ OD POCETOK NA STAGE POZICIIVE KE SA ZEZNAT
    16     // TODO jwt vo cookie
    17     // TODO placed shape i mouseMoveHandler da ne callback ( da ne vrakjat funkcija)
    18 
    19     this.gridLayer = new Konva.Layer();
    20     this.mainLayer = new Konva.Layer();
    21     this.dragLayer = new Konva.Layer();
    22     this.infoPinLayer = new Konva.Layer();
    23     this.textLayer = new Konva.Layer();
    24     this.gridLayer.listening(false);
    25 
    26     this.originalWidth = this.container.clientWidth;
    27     this.originalHeight = this.container.clientHeight;
    28 
    29     this.shapes = [];
    30     this.blockSize = 10;
    31     this.efficientDrawingMode = false;
    32     this.roomTypes = [];
    33 
    34     this.gridLine = new Konva.Line({
    35       points: [],
    36       stroke: "grey",
    37       strokeWidth: 1,
    38       opacity: 0.3,
    39     });
    40 
    41     this.gridLine.cache();
    42 
    43     this.mainTransformer = new Konva.Transformer({
    44       centeredScaling: false,
    45       rotationSnaps: [0, 90, 180, 270],
    46       anchorSize: 5,
    47       padding: 2,
    48       anchorFill: "#ef7539",
    49       borderStroke: "black",
    50       anchorStroke: "black",
    51       cornerRadius: 20,
    52       anchorCornerRadius: 10,
    53       anchorDragBoundFunc: this.transformerSnapFunc(),
    54     });
    55 
    56     this.selectionRectangle = new Konva.Rect({
    57       fill: "rgba(200,0,255,0.5)",
    58       visible: false,
    59       listening: false,
    60       zIndex: 100,
    61     });
    62 
    63     this.x1 = 0;
    64     this.y1 = 0;
    65     this.x2 = 0;
    66     this.y2 = 0;
    67 
    68     this.selecting = false;
    69 
    70     this.initialize();
    71   }
    72 
    73   initialize() {
    74     this.drawGrid();
    75     this.mainLayer.add(this.mainTransformer);
    76     this.mainLayer.add(this.selectionRectangle);
    77     this.stage.add(this.gridLayer);
    78     this.stage.add(this.dragLayer);
    79     this.stage.add(this.mainLayer);
    80     this.stage.add(this.infoPinLayer);
    81     this.stage.add(this.textLayer);
    82     this.setupEventListeners();
    83   }
    84 
    85   setupEventListeners() {
    86     document.getElementById("shapeOptions").addEventListener("click", this.selectShape.bind(this));
    87     document.getElementById("render-button").addEventListener("click", this.render.bind(this));
    88     window.addEventListener("keydown", this.handleExitSelection.bind(this));
    89     window.addEventListener("keydown", this.handleDelete.bind(this));
    90     window.addEventListener("keydown", this.rotateShapesBy90Deg.bind(this));
    91     window.addEventListener("keydown", this.toggleEfficientDrawingMode.bind(this));
    92     window.addEventListener("resize", this.handleResize.bind(this));
    93     this.stage.on("mousedown touchstart", this.handleMouseDown.bind(this));
    94     this.stage.on("mousemove touchmove", this.handleMouseMove.bind(this));
    95     this.stage.on("mouseup touchend", this.handleMouseUp.bind(this));
    96     this.stage.on("click tap", this.handleStageClick.bind(this));
    97     this.stage.on("contextmenu", this.placeInfoPin.bind(this));
    98     this.stage.on("dragmove", this.dragStage.bind(this));
    99     this.stage.on("wheel", this.zoom.bind(this));
    100   }
    101 
    102   detachKeyPressEventListeners() {
    103     window.removeEventListener("keydown", this.handleExitSelection.bind(this));
    104     window.removeEventListener("keydown", this.handleDelete.bind(this));
    105     window.removeEventListener("keydown", this.rotateShapesBy90Deg.bind(this));
    106     window.removeEventListener("keydown", this.toggleEfficientDrawingMode.bind(this));
    107   }
    108   attachKeyPressEventListeners() {
    109     window.addEventListener("keydown", this.handleExitSelection.bind(this));
    110     window.addEventListener("keydown", this.handleDelete.bind(this));
    111     window.addEventListener("keydown", this.rotateShapesBy90Deg.bind(this));
    112     window.addEventListener("keydown", this.toggleEfficientDrawingMode.bind(this));
    113   }
    114 
    115   dragStage(e) {
    116     if (!e.evt.shiftKey) return;
    117     this.drawGrid();
    118   }
    119 
    120   transformerSnapFunc() {
    121     return (oldPos, newPos) => {
    122       const snapDistance = 8;
    123 
    124       if (this.mainTransformer.getActiveAnchor() === "rotater") {
    125         return newPos;
    126       }
    127 
    128       const distance = Math.sqrt(Math.pow(newPos.x - oldPos.x, 2) + Math.pow(newPos.y - oldPos.y, 2));
    129 
    130       if (distance > snapDistance) {
    131         return newPos;
    132       }
    133 
    134       const nextX = Math.round(newPos.x / this.blockSize) * this.blockSize;
    135       const diffX = Math.abs(newPos.x - nextX);
    136 
    137       const nextY = Math.round(newPos.y / this.blockSize) * this.blockSize;
    138       const diffY = Math.abs(newPos.y - nextY);
    139 
    140       const snapToX = diffX < snapDistance;
    141       const snapToY = diffY < snapDistance;
    142 
    143       if (snapToX && !snapToY) {
     14    constructor(containerId,floorNum,mapName) {
     15        this.container = document.getElementById(containerId);
     16        this.stage = new Konva.Stage({
     17            container: containerId,
     18            width: this.container.clientWidth,
     19            height: this.container.clientHeight,
     20        });
     21
     22        // TODO AKO DRAGNIT NEKOJ OD POCETOK NA STAGE POZICIIVE KE SA ZEZNAT
     23        // TODO jwt vo cookie
     24        // TODO placed shape i mouseMoveHandler da ne callback ( da ne vrakjat funkcija)
     25        // TODO text na top layer sekogas
     26
     27        this._floorNum = floorNum;
     28        this.mapName = mapName;
     29
     30        this.gridLayer = new Konva.Layer();
     31        this.mainLayer = new Konva.Layer();
     32        this.dragLayer = new Konva.Layer();
     33        this.infoPinLayer = new Konva.Layer();
     34        this.prioLayer = new Konva.Layer();
     35        this.textLayer = new Konva.Layer();
     36        this.gridLayer.listening(false);
     37
     38
     39        this.othStairs = [];
     40
     41        this.blockSize = 10;
     42        this.efficientDrawingMode = false;
     43        this.roomTypes = [];
     44
     45        this.gridLine = new Konva.Line({
     46            points: [],
     47            stroke: "grey",
     48            strokeWidth: 1,
     49            opacity: 0.3,
     50        });
     51
     52        this.gridLine.cache();
     53
     54        this.mainTransformer = new Konva.Transformer({
     55            centeredScaling: false,
     56            rotationSnaps: [0, 90, 180, 270],
     57            anchorSize: 5,
     58            padding: 2,
     59            anchorFill: "#f6031f",
     60            borderStroke: "black",
     61            anchorStroke: "black",
     62            cornerRadius: 20,
     63            anchorCornerRadius: 10,
     64            anchorDragBoundFunc: this.transformerSnapFunc(),
     65        });
     66
     67        this.selectionRectangle = new Konva.Rect({
     68            fill: "rgba(56,194,245,0.5)",
     69            visible: false,
     70            listening: false,
     71            zIndex: 100,
     72        });
     73
     74        this.x1 = 0;
     75        this.y1 = 0;
     76        this.x2 = 0;
     77        this.y2 = 0;
     78
     79        this.selecting = false;
     80
     81        this.initialize();
     82    }
     83
     84    initialize() {
     85        this.drawGrid();
     86        this.mainLayer.add(this.mainTransformer);
     87        this.mainLayer.add(this.selectionRectangle);
     88        this.stage.add(this.gridLayer);
     89        this.stage.add(this.dragLayer);
     90        this.stage.add(this.mainLayer);
     91        this.stage.add(this.infoPinLayer);
     92        this.stage.add(this.textLayer);
     93        this.setupEventListeners();
     94    }
     95
     96    setupEventListeners() {
     97        document.getElementById("shapeOptions").addEventListener("click", this.selectShape.bind(this));
     98        window.addEventListener("keydown", this.handleExitSelection.bind(this));
     99        window.addEventListener("keydown", this.handleDelete.bind(this));
     100        window.addEventListener("keydown", this.rotateShapesBy90Deg.bind(this));
     101        window.addEventListener("keydown", this.toggleEfficientDrawingMode.bind(this));
     102        window.addEventListener("resize", this.handleResize.bind(this));
     103
     104        this.boundEscapeEventListener = this.handleExitSelection.bind(this);
     105        this.boundDeleteEventListener = this.handleDelete.bind(this);
     106        this.boundRotateShapeEventListener = this.rotateShapesBy90Deg.bind(this)
     107        this.boundEfficientDrawingModeEventListener = this.toggleEfficientDrawingMode.bind(this);
     108
     109        //this.attachKeyPressEventListeners();
     110
     111        this.stage.on("mousedown touchstart", this.handleMouseDown.bind(this));
     112        this.stage.on("mousemove touchmove", this.handleMouseMove.bind(this));
     113        this.stage.on("mouseup touchend", this.handleMouseUp.bind(this));
     114        this.stage.on("click tap", this.handleStageClick.bind(this));
     115        this.stage.on("contextmenu", this.placeInfoPin.bind(this));
     116        this.stage.on("dragmove", this.dragStage.bind(this));
     117        this.stage.on("wheel", this.zoom.bind(this));
     118    }
     119
     120    detachKeyPressEventListeners() {
     121        window.removeEventListener("keydown", this.boundEscapeEventListener);
     122        window.removeEventListener("keydown", this.boundDeleteEventListener);
     123        window.removeEventListener("keydown", this.boundRotateShapeEventListener);
     124        window.removeEventListener("keydown", this.boundEfficientDrawingModeEventListener);
     125    }
     126
     127    attachKeyPressEventListeners() {
     128        window.addEventListener("keydown", this.boundEscapeEventListener);
     129        window.addEventListener("keydown", this.boundDeleteEventListener);
     130        window.addEventListener("keydown", this.boundRotateShapeEventListener);
     131        window.addEventListener("keydown", this.boundEfficientDrawingModeEventListener);
     132    }
     133
     134    dragStage(e) {
     135        if (!e.evt.shiftKey) return;
     136        this.drawGrid();
     137    }
     138
     139    transformerSnapFunc() {
     140        return (oldPos, newPos) => {
     141            const snapDistance = 8;
     142
     143            if (this.mainTransformer.getActiveAnchor() === "rotater") {
     144                return newPos;
     145            }
     146
     147            const distance = Math.sqrt(Math.pow(newPos.x - oldPos.x, 2) + Math.pow(newPos.y - oldPos.y, 2));
     148
     149            if (distance > snapDistance) {
     150                return newPos;
     151            }
     152
     153            const nextX = Math.round(newPos.x / this.blockSize) * this.blockSize;
     154            const diffX = Math.abs(newPos.x - nextX);
     155
     156            const nextY = Math.round(newPos.y / this.blockSize) * this.blockSize;
     157            const diffY = Math.abs(newPos.y - nextY);
     158
     159            const snapToX = diffX < snapDistance;
     160            const snapToY = diffY < snapDistance;
     161
     162            if (snapToX && !snapToY) {
     163                return {
     164                    x: nextX,
     165                    y: oldPos.y,
     166                };
     167            } else if (!snapToX && snapToY) {
     168                return {
     169                    x: oldPos.x,
     170                    y: nextY,
     171                };
     172            } else if (snapToX && snapToY) {
     173                return {
     174                    x: nextX,
     175                    y: nextY,
     176                };
     177            }
     178
     179            return newPos;
     180        };
     181    }
     182
     183    handleResize() {
     184        this.stage.width(this.container.offsetWidth);
     185        this.stage.height(this.container.offsetHeight);
     186        this.drawGrid();
     187    }
     188
     189    zoom(e) {
     190        zoomStage(e, this.stage, true);
     191        this.drawGrid();
     192    }
     193
     194    get floorNum(){
     195        return this._floorNum;
     196    }
     197
     198    set floorNum(val){
     199        this._floorNum = val;
     200    }
     201
     202    drawGrid() {
     203        this.gridLayer.destroyChildren();
     204
     205        let width = this.stage.width();
     206        let height = this.stage.height();
     207
     208        //presmetka od globalen koordinaten sistem vo lokalen na canvasot
     209        let transform = this.stage.getAbsoluteTransform().copy().invert();
     210        let topLeft = transform.point({
     211            x: 0,
     212            y: 0,
     213        });
     214
     215        let bottomRight = transform.point({
     216            x: width,
     217            y: height,
     218        });
     219
     220        let startX = Math.floor(topLeft.x / this.blockSize) * this.blockSize;
     221        let startY = Math.floor(topLeft.y / this.blockSize) * this.blockSize;
     222
     223        let endX = Math.ceil(bottomRight.x / this.blockSize) * this.blockSize;
     224        let endY = Math.ceil(bottomRight.y / this.blockSize) * this.blockSize;
     225
     226        for (let x = startX; x <= endX; x += this.blockSize) {
     227            let line = this.gridLine.clone({
     228                points: [x + 0.5, topLeft.y - this.blockSize, x + 0.5, bottomRight.y + this.blockSize],
     229            });
     230
     231            line.transformsEnabled("position");
     232            line.perfectDrawEnabled(false);
     233            line.shadowForStrokeEnabled(false);
     234
     235            this.gridLayer.add(line);
     236        }
     237
     238        for (let y = startY; y <= endY; y += this.blockSize) {
     239            let line = this.gridLine.clone({
     240                points: [topLeft.x - this.blockSize, y + 0.5, bottomRight.x + this.blockSize, y + 0.5],
     241            });
     242
     243            line.perfectDrawEnabled(false);
     244            line.shadowForStrokeEnabled(false);
     245            line.transformsEnabled("position");
     246            this.gridLayer.add(line);
     247        }
     248
     249        this.mainLayer.moveToTop();
     250        this.infoPinLayer.moveToTop();
     251
     252        this.gridLayer.batchDraw();
     253    }
     254
     255    placeInfoPin(e) {
     256        e.evt.preventDefault();
     257        let mousePos = this.stage.getRelativePointerPosition();
     258        const attrs = {
     259            type: "InfoPin",
     260            position: mousePos,
     261            blockSize: this.blockSize,
     262            layer: this.mainLayer,
     263            rotation: 0,
     264            scaleX: 1,
     265            scaleY: 1,
     266            increment: true,
     267            floorNum: this.floorNum
     268        };
     269        let infoPin = Factory.createShape("InfoPin", attrs);
     270        addEventHandling(infoPin, this, "dblclick");
     271        //this.shapes.push(infoPin);
     272        ShapeRegistry.add(infoPin)
     273        this.mainLayer.add(infoPin);
     274        infoPin.displayName(this.textLayer);
     275        triggerMapSave()
     276
     277        console.log(infoPin.name());
     278    }
     279
     280    toggleEfficientDrawingMode(e) {
     281        if (e.key === "e" || e.key === "E") {
     282            this.efficientDrawingMode = !this.efficientDrawingMode;
     283            console.log("EFFICIENT DRAWING MODE is: ", this.efficientDrawingMode);
     284
     285            if (!this.efficientDrawingMode) {
     286                this.stopDrawing();
     287            }
     288        }
     289    }
     290
     291    placeShape() {
     292        const mousePos = this.stage.getRelativePointerPosition();
     293        const attrs = {
     294            position: mousePos,
     295            width: this.blockSize,
     296            height: this.blockSize,
     297            layer: this.mainLayer,
     298            rotation: this.hoverObj.rotation(),
     299            scaleX: 1,
     300            scaleY: 1,
     301            increment: true,
     302            snap: true,
     303            fromLoad: false,
     304            blockSize: this.blockSize,
     305            floorNum: this.floorNum
     306        };
     307
     308        const placedObj = Factory.createShape(this.hoverObj.type, attrs);
     309        if (!placedObj) return;
     310
     311        console.info("ATTRS FNUM",attrs.floorNum)
     312
     313        this.mainLayer.add(placedObj);
     314        //this.shapes.push(placedObj);
     315        console.log("VO PLACED SHAEPS WALL ZITI SE: " + placedObj.className);
     316        ShapeRegistry.add(placedObj);
     317        addEventHandling(placedObj, this, "dblclick");
     318        this.mainLayer.draw();
     319
     320        // site ovie func da se vo edna funkcija vo shape.
     321
     322        placedObj.displayName(this.textLayer);
     323        placedObj.snapToGrid();
     324
     325        triggerMapSave();
     326
     327        if (!this.efficientDrawingMode) {
     328            this.stopDrawing();
     329        }
     330    }
     331
     332    stopDrawing() {
     333        this.mainTransformer.nodes([]);
     334        if (this.hoverObj != null) this.hoverObj.remove();
     335        this.dragLayer.removeChildren();
     336        this.stage.off("mousemove", this.boundMouseMoveHandler);
     337        this.stage.off("click", this.boundPlaceShapeHandler);
     338    }
     339
     340    mouseMoveHandler() {
     341        const mousePos = this.stage.getRelativePointerPosition();
     342        this.hoverObj.position({x: mousePos.x, y: mousePos.y});
     343        this.hoverObj.visible(true);
     344    }
     345
     346    startDrawing(shapeType) {
     347        const attrs = {
     348            position: {x: 0, y: 0},
     349            width: this.blockSize,
     350            height: this.blockSize,
     351            layer: this.mainLayer,
     352            rotation: 0,
     353            scaleX: 1,
     354            scaleY: 1,
     355            increment: false,
     356            snap: true,
     357            fromLoad: false,
     358            blockSize: this.blockSize
     359        };
     360        this.hoverObj = Factory.createShape(shapeType, attrs);
     361
     362        console.log("HOVBER OBK:", this.hoverObj)
     363
     364        this.hoverObj.visible(false);
     365        this.dragLayer.add(this.hoverObj);
     366        this.dragLayer.moveToTop();
     367        this.boundMouseMoveHandler = this.mouseMoveHandler.bind(this);
     368        this.boundPlaceShapeHandler = this.placeShape.bind(this);
     369
     370        this.stage.on("mousemove", this.boundMouseMoveHandler);
     371        this.stage.on("click", this.boundPlaceShapeHandler);
     372    }
     373
     374    selectShape(e) {
     375        if (e.target.tagName === "LI") {
     376            const shapeType = e.target.getAttribute("data-info");
     377            this.startDrawing(shapeType);
     378            this.mainTransformer.nodes([]);
     379        }
     380    }
     381
     382    rotateShapesBy90Deg(e) {
     383        if (e.key === "r" || e.key === "R") {
     384            if (this.hoverObj) {
     385                this.hoverObj.rotate(90);
     386            }
     387            this.mainTransformer.nodes().forEach((node) => {
     388                node.rotate(90);
     389            });
     390        }
     391    }
     392
     393    handleDelete(e) {
     394        if (e.key === "Delete") {
     395            this.mainTransformer.nodes().forEach((node) => {
     396                node.remove();
     397                node.destroy();
     398                ShapeRegistry.delete(node);
     399                triggerMapSave();
     400            });
     401            this.mainTransformer.nodes([]);
     402            this.mainLayer.batchDraw();
     403        }
     404    }
     405
     406    handleExitSelection(e) {
     407        if (e.key === "Escape") {
     408            this.mainTransformer.nodes([]);
     409            this.stopDrawing();
     410        }
     411    }
     412
     413    handleMouseDown(e) {
     414        this.stage.draggable(e.evt.shiftKey);
     415
     416        if (e.target !== this.stage) {
     417            return;
     418        }
     419
     420        e.evt.preventDefault();
     421        this.x1 = this.stage.getRelativePointerPosition().x;
     422        this.y1 = this.stage.getRelativePointerPosition().y;
     423        this.x2 = this.stage.getRelativePointerPosition().x;
     424        this.y2 = this.stage.getRelativePointerPosition().y;
     425
     426        this.selectionRectangle.width(0);
     427        this.selectionRectangle.height(0);
     428        this.selecting = true;
     429    }
     430
     431    handleMouseMove(e) {
     432        if (!this.selecting) {
     433            return;
     434        }
     435        e.evt.preventDefault();
     436        this.x2 = this.stage.getRelativePointerPosition().x;
     437        this.y2 = this.stage.getRelativePointerPosition().y;
     438
     439        this.selectionRectangle.setAttrs({
     440            visible: true,
     441            x: Math.min(this.x1, this.x2),
     442            y: Math.min(this.y1, this.y2),
     443            width: Math.abs(this.x2 - this.x1),
     444            height: Math.abs(this.y2 - this.y1),
     445        });
     446    }
     447
     448    handleMouseUp(e) {
     449        this.selecting = false;
     450        this.stage.draggable(false);
     451
     452        if (!this.selectionRectangle.visible()) {
     453            return;
     454        }
     455
     456        e.evt.preventDefault();
     457        this.selectionRectangle.visible(false);
     458        const shapes = this.stage.find(".mapObj");
     459        const box = this.selectionRectangle.getClientRect();
     460        const selected = shapes.filter((shape) => Konva.Util.haveIntersection(box, shape.getClientRect()));
     461        this.mainTransformer.nodes(selected);
     462        console.log(this.mainTransformer.nodes());
     463    }
     464
     465    saveShapeDetails() {
     466        // this.shapes.forEach(shape => {
     467        //     shape.saveShapeDetails();
     468        //     console.log(shape.info);
     469        // });
     470        ShapeRegistry.saveDetails();
     471        console.log("thisflornum",this.floorNum)
     472        return  {
     473            shapes: ShapeRegistry.getShapes(this.floorNum),
     474            roomTypes: JSON.stringify(this.roomTypes),
     475            mapName: this.mapName,
     476            floorNum: this.floorNum
     477        }
     478    }
     479
     480    getPayload(){
     481        this.saveShapeDetails();
    144482        return {
    145           x: nextX,
    146           y: oldPos.y,
    147         };
    148       } else if (!snapToX && snapToY) {
    149         return {
    150           x: oldPos.x,
    151           y: nextY,
    152         };
    153       } else if (snapToX && snapToY) {
    154         return {
    155           x: nextX,
    156           y: nextY,
    157         };
    158       }
    159 
    160       return newPos;
    161     };
    162   }
    163 
    164   handleResize() {
    165     this.stage.width(this.container.offsetWidth);
    166     this.stage.height(this.container.offsetHeight);
    167     this.drawGrid();
    168   }
    169 
    170   zoom(e) {
    171    zoomStage(e,this.stage);
    172    this.drawGrid();
    173   }
    174 
    175   drawGrid() {
    176     this.gridLayer.destroyChildren();
    177 
    178     let width = this.stage.width();
    179     let height = this.stage.height();
    180 
    181     //presmetka od globalen koordinaten sistem vo lokalen na canvasot
    182     let transform = this.stage.getAbsoluteTransform().copy().invert();
    183     let topLeft = transform.point({
    184       x: 0,
    185       y: 0,
    186     });
    187 
    188     let bottomRight = transform.point({
    189       x: width,
    190       y: height,
    191     });
    192 
    193     let startX = Math.floor(topLeft.x / this.blockSize) * this.blockSize;
    194     let startY = Math.floor(topLeft.y / this.blockSize) * this.blockSize;
    195 
    196     let endX = Math.ceil(bottomRight.x / this.blockSize) * this.blockSize;
    197     let endY = Math.ceil(bottomRight.y / this.blockSize) * this.blockSize;
    198 
    199     for (let x = startX; x <= endX; x += this.blockSize) {
    200       let line = this.gridLine.clone({
    201         points: [x + 0.5, topLeft.y - this.blockSize, x + 0.5, bottomRight.y + this.blockSize],
    202       });
    203 
    204       line.transformsEnabled("position");
    205       line.perfectDrawEnabled(false);
    206       line.shadowForStrokeEnabled(false);
    207 
    208       this.gridLayer.add(line);
    209     }
    210 
    211     for (let y = startY; y <= endY; y += this.blockSize) {
    212       let line = this.gridLine.clone({
    213         points: [topLeft.x - this.blockSize, y + 0.5, bottomRight.x + this.blockSize, y + 0.5],
    214       });
    215 
    216       line.perfectDrawEnabled(false);
    217       line.shadowForStrokeEnabled(false);
    218       line.transformsEnabled("position");
    219       this.gridLayer.add(line);
    220     }
    221 
    222     this.mainLayer.moveToTop();
    223     this.infoPinLayer.moveToTop();
    224 
    225     this.gridLayer.batchDraw();
    226   }
    227 
    228   placeInfoPin(e) {
    229     e.evt.preventDefault();
    230     let mousePos = this.stage.getRelativePointerPosition();
    231     let infoPin = Factory.createShape("InfoPin", mousePos, this.blockSize, this.mainLayer, 0,1,1,true);
    232     this.addModalHandling(infoPin);
    233     this.shapes.push(infoPin);
    234     this.mainLayer.add(infoPin)
    235     infoPin.displayName(this.textLayer);
    236     console.log(infoPin.name());
    237   }
    238 
    239   toggleEfficientDrawingMode(e) {
    240     if (e.key === "e" || e.key === "E") {
    241       this.efficientDrawingMode = !this.efficientDrawingMode;
    242       console.log("EFFICIENT DRAWING MODE is: ", this.efficientDrawingMode);
    243 
    244       if (!this.efficientDrawingMode) {
    245         this.stopDrawing();
    246       }
    247     }
    248   }
    249 
    250   placeShape() {
    251     const mousePos = this.stage.getRelativePointerPosition();
    252     const placedObj = Factory.createShape(
    253       this.hoverObj.type,
    254       mousePos,
    255       this.blockSize,
    256       this.mainLayer,
    257       this.hoverObj.rotation(),
    258         1,
    259         1,
    260         true
    261     );
    262 
    263     if (!placedObj) return;
    264 
    265     this.mainLayer.add(placedObj);
    266     this.shapes.push(placedObj);
    267     this.addModalHandling(placedObj);
    268     this.mainLayer.draw();
    269     placedObj.displayName(this.textLayer);
    270     placedObj.snapToGrid();
    271 
    272     if (!this.efficientDrawingMode) {
    273       this.stopDrawing();
    274     }
    275   }
    276 
    277   stopDrawing() {
    278     this.mainTransformer.nodes([]);
    279     this.hoverObj.remove();
    280     this.dragLayer.removeChildren();
    281     this.stage.off("mousemove", this.boundMouseMoveHandler);
    282     this.stage.off("click", this.boundPlaceShapeHandler);
    283   }
    284 
    285   mouseMoveHandler() {
    286     const mousePos = this.stage.getRelativePointerPosition();
    287     this.hoverObj.position({ x: mousePos.x, y: mousePos.y });
    288     this.hoverObj.visible(true);
    289   }
    290 
    291   startDrawing(shapeType) {
    292     let pos = { x: 0, y: 0 };
    293     this.hoverObj = Factory.createShape(shapeType, pos, this.blockSize, this.dragLayer, 0);
    294 
    295     this.hoverObj.visible(false);
    296     this.dragLayer.add(this.hoverObj);
    297     this.dragLayer.moveToTop();
    298     this.boundMouseMoveHandler = this.mouseMoveHandler.bind(this);
    299     this.boundPlaceShapeHandler = this.placeShape.bind(this);
    300 
    301     this.stage.on("mousemove", this.boundMouseMoveHandler);
    302     this.stage.on("click", this.boundPlaceShapeHandler);
    303   }
    304 
    305   selectShape(e) {
    306     if (e.target.tagName === "LI") {
    307       const shapeType = e.target.getAttribute("data-info");
    308       this.startDrawing(shapeType);
    309       this.mainTransformer.nodes([]);
    310     }
    311   }
    312 
    313   addModalHandling(shape) {
    314     shape.on("dblclick", () => {
    315       const eventName = shape.modalEventName;
    316       if (eventName) {
    317         const data = {
    318           room: shape,
    319           map: this,
    320         };
    321         const event = new CustomEvent(eventName, { detail: data });
    322         window.dispatchEvent(event);
    323       }
    324     });
    325   }
    326 
    327   rotateShapesBy90Deg(e) {
    328     if (e.key === "r" || e.key === "R") {
    329       if (this.hoverObj) {
    330         this.hoverObj.rotate(90);
    331       }
    332       this.mainTransformer.nodes().forEach((node) => {
    333         node.rotate(90);
    334       });
    335     }
    336   }
    337 
    338   handleDelete(e) {
    339     if (e.key === "Delete") {
    340       this.mainTransformer.nodes().forEach((node) => {
    341         node.remove();
    342         this.shapes.splice(this.shapes.indexOf(node), 1);
    343       });
    344       this.mainTransformer.nodes([]);
    345       this.mainLayer.batchDraw();
    346     }
    347   }
    348 
    349   handleExitSelection(e) {
    350     if (e.key === "Escape") {
    351       this.mainTransformer.nodes([]);
    352       this.stopDrawing();
    353     }
    354   }
    355 
    356   handleMouseDown(e) {
    357     this.stage.draggable(e.evt.shiftKey);
    358 
    359     if (e.target !== this.stage) {
    360       return;
    361     }
    362 
    363     e.evt.preventDefault();
    364     this.x1 = this.stage.getRelativePointerPosition().x;
    365     this.y1 = this.stage.getRelativePointerPosition().y;
    366     this.x2 = this.stage.getRelativePointerPosition().x;
    367     this.y2 = this.stage.getRelativePointerPosition().y;
    368 
    369     this.selectionRectangle.width(0);
    370     this.selectionRectangle.height(0);
    371     this.selecting = true;
    372   }
    373 
    374   handleMouseMove(e) {
    375     if (!this.selecting) {
    376       return;
    377     }
    378     e.evt.preventDefault();
    379     this.x2 = this.stage.getRelativePointerPosition().x;
    380     this.y2 = this.stage.getRelativePointerPosition().y;
    381 
    382     this.selectionRectangle.setAttrs({
    383       visible: true,
    384       x: Math.min(this.x1, this.x2),
    385       y: Math.min(this.y1, this.y2),
    386       width: Math.abs(this.x2 - this.x1),
    387       height: Math.abs(this.y2 - this.y1),
    388     });
    389   }
    390 
    391   handleMouseUp(e) {
    392     this.selecting = false;
    393     this.stage.draggable(false);
    394 
    395     if (!this.selectionRectangle.visible()) {
    396       return;
    397     }
    398 
    399     e.evt.preventDefault();
    400     this.selectionRectangle.visible(false);
    401     const shapes = this.stage.find(".mapObj");
    402     const box = this.selectionRectangle.getClientRect();
    403     const selected = shapes.filter((shape) => Konva.Util.haveIntersection(box, shape.getClientRect()));
    404     this.mainTransformer.nodes(selected);
    405     console.log(this.mainTransformer.nodes());
    406   }
    407 
    408   saveShapeDetails() {
    409     this.shapes.forEach((room) => {
    410       room.saveShapeDetails();
    411       console.log(room.info);
    412     });
    413   }
    414 
    415   async render() {
    416     this.saveShapeDetails();
    417     const httpService = new HttpService("http://localhost:8080/api/protected", true);
    418     try {
    419       const response = await httpService.post("/render", this.shapes);
    420       console.log(response);
    421     } catch (err) {
    422       console.log("ERROR --> Could not render map --->", err);
    423     }
    424   }
    425 
    426   async saveMap(mapName) {
    427     this.saveShapeDetails();
    428     const httpService = new HttpService("http://localhost:8080/api/protected/maps", true);
    429     try {
    430       const response = await httpService.put(`/save?mapName=${mapName}`, this.shapes);
    431       console.log(response, "resp in builder");
    432     } catch (err) {
    433       console.log("ERROR --> Could not Save map --->", err);
    434     }
    435   }
    436 
    437   handleStageClick(e) {
    438     if (this.selectionRectangle.visible()) {
    439       return;
    440     }
    441 
    442     if (e.target === this.stage) {
    443       this.mainTransformer.nodes([]);
    444       return;
    445     }
    446 
    447     if (!e.target.hasName("mapObj")) {
    448       return;
    449     }
    450 
    451     const metaPressed = e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey;
    452     const isSelected = this.mainTransformer.nodes().indexOf(e.target) >= 0;
    453 
    454     if (!metaPressed && !isSelected) {
    455       this.mainTransformer.nodes([e.target]);
    456       console.log("Sel 1");
    457     } else if (metaPressed && isSelected) {
    458       const nodes = this.mainTransformer.nodes().slice();
    459       nodes.splice(nodes.indexOf(e.target), 1);
    460       this.mainTransformer.nodes(nodes);
    461     } else if (metaPressed && !isSelected) {
    462       const nodes = this.mainTransformer.nodes().concat([e.target]);
    463       this.mainTransformer.nodes(nodes);
    464     }
    465   }
    466 
    467   addRoomType(roomType) {
    468     this.roomTypes.push(roomType);
    469   }
    470 
    471   getRoomTypes() {
    472     return this.roomTypes;
    473   }
    474 
    475   getRooms() {
    476     return this.getShapeInfoByType("Room");
    477   }
    478 
    479   getPins() {
    480     return this.getShapeInfoByType("InfoPin");
    481   }
    482 
    483   getEntrances() {
    484     return this.getShapeInfoByType("Entrance");
    485   }
    486 
    487   getConnections() {
    488     const pins = this.getShapeInfoByType("InfoPin");
    489     const entrances = this.getShapeInfoByType("Entrance");
    490     return [...pins, ...entrances];
    491   }
    492 
    493   getShapeInfoByType(type) {
    494     return this.shapes.filter((shape) => shape.className === type).map((shape) => shape.info);
    495   }
    496 
    497   updateConnections() {
    498     console.log("Update");
    499 
    500     this.shapes.forEach((shape) => {
    501       if (shape.className === "InfoPin" || shape.className === "Entrance") {
    502         shape.info.selectedPins.forEach((connectedShapeName) => {
    503           const connectedShape = this.shapes.find((s) => s.info.name === connectedShapeName);
    504           if (
    505             connectedShape &&
    506             (connectedShape.className === "InfoPin" || connectedShape.className === "Entrance")
    507           ) {
    508             if (!connectedShape.info.selectedPins.includes(shape.info.name)) {
    509               connectedShape.info.selectedPins.push(shape.info.name);
    510             }
    511           }
    512         });
    513       }
    514     });
    515   }
    516 
    517   removeConnection(from, to) {
    518     this.shapes
    519       .filter((s) => s.info.name === from || s.info.name === to)
    520       .forEach((s) => {
    521         s.info.selectedPins = s.info.selectedPins.filter((pin) => pin !== from && pin !== to);
    522       });
    523     console.log("Remove");
    524   }
    525 
    526   updateRoomNames() {
    527     this.textLayer.removeChildren();
    528     this.shapes.forEach((shape) => {
    529       shape.displayName(this.textLayer);
    530     });
    531     this.textLayer.children.forEach((child) => console.log(child));
    532   }
    533 
    534   clearMap() {
    535     this.mainLayer.removeChildren();
    536     this.shapes = [];
    537     this.hoverObj = null;
    538   }
    539 
    540   deserializeMap(data) {
    541     console.log("DESERIALIZING: ", data);
    542     this.clearMap();
    543     let dsrData = JSON.parse(data);
    544     dsrData.forEach((child) => {
    545       const shape = JSON.parse(child);
    546       const loadedShape = Factory.createShape(
    547         shape.className,
    548         { x: shape.attrs.x, y: shape.attrs.y },
    549         this.blockSize,
    550         this.mainLayer,
    551         shape.attrs.rotation,
    552           shape.attrs.scaleX,
    553           shape.attrs.scaleY
    554       );
    555       loadedShape.loadInfo(shape.attrs);
    556       this.shapes.push(loadedShape);
    557       this.addModalHandling(loadedShape);
    558       this.mainLayer.add(loadedShape);
    559     });
    560     this.mainTransformer.nodes([]);
    561     this.mainLayer.add(this.mainTransformer);
    562     this.mainLayer.add(this.selectionRectangle);
    563 
    564     this.shapes.forEach((shape) => shape.displayName(this.textLayer));
    565   }
     483            shapes: ShapeRegistry.getShapes(this.floorNum),
     484            roomTypes: JSON.stringify(this.roomTypes),
     485            mapName: this.mapName,
     486            floorNum: this.floorNum
     487        }
     488    }
     489
     490
     491    handleStageClick(e) {
     492        if (this.selectionRectangle.visible()) {
     493            return;
     494        }
     495
     496        if (e.target === this.stage) {
     497            this.mainTransformer.nodes([]);
     498            return;
     499        }
     500
     501        if (!e.target.hasName("mapObj")) {
     502            return;
     503        }
     504
     505        const metaPressed = e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey;
     506        const isSelected = this.mainTransformer.nodes().indexOf(e.target) >= 0;
     507
     508        if (!metaPressed && !isSelected) {
     509            this.mainTransformer.nodes([e.target]);
     510            console.log("Sel 1");
     511        } else if (metaPressed && isSelected) {
     512            const nodes = this.mainTransformer.nodes().slice();
     513            nodes.splice(nodes.indexOf(e.target), 1);
     514            this.mainTransformer.nodes(nodes);
     515        } else if (metaPressed && !isSelected) {
     516            const nodes = this.mainTransformer.nodes().concat([e.target]);
     517            this.mainTransformer.nodes(nodes);
     518        }
     519    }
     520
     521    addRoomType(type) {
     522        this.roomTypes.push(type);
     523    }
     524
     525    removeRoomType(targetType) {
     526        this.roomTypes = this.roomTypes.filter(type => type !== targetType);
     527    }
     528
     529    getRoomTypes() {
     530        return this.roomTypes;
     531    }
     532
     533    getRooms() {
     534        return this.getShapeInfoByType("Room");
     535    }
     536
     537    getPins() {
     538        return this.getShapeInfoByType("InfoPin");
     539    }
     540
     541    getEntrances() {
     542        return this.getShapeInfoByType("Entrance");
     543    }
     544
     545
     546
     547    getShapeInfoByType(type) {
     548        return ShapeRegistry.getShapes(this.floorNum).filter((shape) => shape.className === type).map((shape) => shape.info);
     549    }
     550
     551
     552    drawConnection(node1Name, node2Name) {
     553
     554        ShapeRegistry.drawConnection(node1Name,node2Name);
     555    }
     556
     557    getNodeByName(name) {
     558        return ShapeRegistry.getShapes(this.floorNum).filter(shape => shape instanceof MapNode && shape.info.name === name)[0];
     559    }
     560
     561    removeConnection(from, to) {
     562        ShapeRegistry.removeConnection(from,to);
     563    }
     564
     565    updateRoomNames() {
     566        this.textLayer.removeChildren();
     567        ShapeRegistry.getShapes(this.floorNum).forEach((shape) => {
     568            shape.displayName(this.textLayer);
     569        });
     570
     571    }
     572
     573    isMainEntranceSelected() {
     574        console.log(this.getEntrances().forEach((en) => console.log(en.isMainEntrance, "asdsad")));
     575
     576        let hasMainEntrance = false;
     577
     578        this.getEntrances().forEach((entrance) => {
     579            if (entrance.isMainEntrance === true) hasMainEntrance = true;
     580        });
     581
     582        return hasMainEntrance;
     583
     584    }
     585
     586    clearMap() {
     587        this.mainLayer.removeChildren();
     588        this.hoverObj = null;
     589    }
     590
     591
     592    // ova  klasa i map display da nasledbat od glavna klasa
     593
     594    loadNewFloor(floor) {
     595
     596        this._floorNum = floor?.num;
     597        let data = floor?.mapData;
     598
     599        if (data == null || data === "") return;
     600
     601        this.deserializeMap(data);
     602        shapeRegistry.getShapes(this.floorNum).forEach((shape) => {
     603            this.mainLayer.add(shape);
     604        });
     605
     606
     607    }
     608
     609    //nov
     610    deserializeMap(data) {
     611        console.log("DESERIALIZING: ", data);
     612        ShapeRegistry.clear(this.floorNum);
     613
     614        if (data != null) {
     615            const dsrData = JSON.parse(data);
     616            //load shapes
     617            dsrData.forEach((shape) => {
     618                const attrs = {
     619                    position: {x: shape.attrs.x, y: shape.attrs.y},
     620                    width: shape.attrs.width,
     621                    height: shape.attrs.height,
     622                    layer: this.mainLayer,
     623                    blockSize: this.blockSize,
     624                    rotation: shape.attrs.rotation,
     625                    scaleX: shape.attrs.scaleX,
     626                    scaleY: shape.attrs.scaleY,
     627                    increment: false,
     628                    snap: true,
     629                    fromLoad: true,
     630                    floorNum: this.floorNum
     631                };
     632
     633                const loadedShape = Factory.createShape(shape.className, attrs);
     634                loadedShape.loadInfo(shape.attrs);
     635                ShapeRegistry.add(loadedShape);
     636                // na destroy trebit events da sa trgnat
     637                addEventHandling(loadedShape, this, "dblclick");
     638            });
     639            let nodes = ShapeRegistry.getShapes(this.floorNum).filter((shape) => shape.className === "InfoPin" || shape.className === "Entrance" || shape.className === "Stairs");
     640            nodes.forEach((pin) => {
     641                let connectedPins = pin.info.selectedPins;
     642                if (connectedPins) {
     643                    connectedPins.forEach((slPin) => {
     644                        console.log("CONN node1: " + pin + "conn node2: " + slPin)
     645                        this.drawConnection(pin.info.name, slPin);
     646                    });
     647                }
     648            });
     649        }
     650
     651        this.mainTransformer.nodes([]);
     652        this.mainLayer.add(this.mainTransformer);
     653        this.mainLayer.add(this.selectionRectangle);
     654
     655        ShapeRegistry.getShapes(this.floorNum).forEach((shape) => shape.displayName(this.textLayer));
     656    }
     657
    566658}
  • imaps-frontend/src/scripts/main/MapDisplay.js

    rd565449 r0c6b92a  
    33import HttpService from "../net/HttpService.js";
    44import {zoomStage} from "../util/zoomStage.js";
     5import {addEventHandling} from "../util/addEventHandling.js";
     6import triggerNavigate from "../util/triggerNavigate.js";
     7import config from "../net/netconfig.js";
     8
    59export class MapDisplay {
    6   constructor(containerId) {
    7     this.container = document.getElementById(containerId);
    8     this.containerId = containerId;
    9     this.stage = new Konva.Stage({
    10       container: containerId,
    11       width: window.innerWidth,
    12       height: window.innerHeight,
    13       draggable: true,
    14     });
    15 
    16     this.shapes = [];
    17     this.loaded = false;
    18     this.mainLayer = new Konva.Layer();
    19     this.routeLayer = new Konva.Layer();
    20     this.textLayer = new Konva.Layer();
    21     this.stage.add(this.mainLayer);
    22     this.stage.add(this.routeLayer);
    23     this.stage.add(this.textLayer);
    24     this.route = new Konva.Line();
    25 
    26     this.stage.on("resize", () => {
    27       this.stage.width = window.innerWidth;
    28       this.stage.height = window.innerHeight;
    29     });
    30 
    31     this.stage.on("wheel",(e) => {
    32         zoomStage(e,this.stage);
    33     })
    34 
    35    
    36   }
    37 
    38   deserializeMap(data) {
    39     data.forEach((child) => {
    40         const shape = JSON.parse(child);
    41         if (shape.className !== "InfoPin") {
    42             const renderedShape = Factory.createRenderedShape(
    43                 shape.className,
    44                 shape.attrs
    45             );
    46             this.shapes.push(renderedShape);
    47       }
    48     });
    49   }
    50 
    51   displayRoomNames(){
    52     console.log("VLEZE")
    53     this.shapes.forEach(shape => {
    54       shape.displayName(this.textLayer);
    55     })
    56 
    57     this.textLayer.children.forEach(child => console.log(child,"DECAAA"));
    58   }
    59 
    60   async loadMap() {
    61     const httpService = new HttpService();
    62     const mapData = await httpService.get("/public/mapData");
    63     console.log("DESERIALIZED --->",mapData);
    64     this.deserializeMap(mapData);
    65     this.shapes.forEach((shape) => {
    66       this.mainLayer.add(shape);
    67     });
    68     this.displayRoomNames();
    69   }
    70 
    71   drawRoute(path) {
    72     this.routeLayer.removeChildren();
    73     console.log("====PATH====");
    74     path.forEach((point) => console.log(point.x, point.y));
    75 
    76     const pointsArray = path.flatMap((point) => [point.x, point.y]);
    77 
    78     console.log(pointsArray, "POINTS");
    79 
    80     let buff = [];
    81     let count = 0;
    82     let index = 0;
    83 
    84     const drawNextSegment = () => {
    85         if (index >= pointsArray.length) return;
    86 
    87         buff.push(pointsArray[index]);
    88         count++;
    89 
    90         if (count % 4 === 0) {
    91             const line = new Konva.Arrow({
    92                 points: buff,
    93                 stroke: "#e91332",
    94                 strokeWidth: 2.5,
    95                 dash: [5, 4],
    96                 lineCap: 'round',
    97                 lineJoin: 'round',
    98                 pointerLength: 7,
    99                 pointerWidth: 7,
    100                 fill:'red',
    101             });
    102 
    103             this.routeLayer.add(line);
    104             this.routeLayer.draw();
    105 
    106             console.log(buff, "BUFFER");
    107             buff = [];
    108             index -= 2;
     10    constructor(containerId, floorNum) {
     11        this.container = document.getElementById(containerId);
     12        this.containerId = containerId;
     13        this.stage = new Konva.Stage({
     14            container: containerId,
     15            width: window.innerWidth,
     16            height: window.innerHeight,
     17            draggable: true,
     18        });
     19
     20        this.shapes = [];
     21        this.roomTypes = [];
     22        this.loaded = false;
     23        this.mainLayer = new Konva.Layer();
     24        this.routeLayer = new Konva.Layer();
     25        this.textLayer = new Konva.Layer();
     26        this.stage.add(this.mainLayer);
     27        this.stage.add(this.routeLayer);
     28        this.stage.add(this.textLayer);
     29
     30        this.floorNum = floorNum;
     31
     32        this.navArrow = new Konva.Arrow({
     33            stroke: "#bb0000",
     34            strokeWidth: 3,
     35            dash: [12, 7],
     36            lineCap: "round",
     37            tension: 10,
     38            pointerLength: 2,
     39            pointerWidth: 3,
     40        });
     41
     42        this.navArrow.cache();
     43
     44        this.stage.on("resize", () => {
     45            this.stage.width = window.innerWidth;
     46            this.stage.height = window.innerHeight;
     47        });
     48
     49        this.stage.on("wheel", (e) => {
     50            zoomStage(e, this.stage);
     51        });
     52    }
     53
     54    clearMap() {
     55        this.mainLayer.removeChildren();
     56        this.shapes.forEach(shape => shape.clearText())
     57        this.shapes = [];
     58    }
     59
     60    deserializeMap(data) {
     61        this.clearMap();
     62
     63        let dsrData = JSON.parse(data);
     64        dsrData.forEach((shape) => {
     65            if (shape.className !== "InfoPin") {
     66                const renderedShape = Factory.createRenderedShape(shape.className, shape.attrs);
     67                addEventHandling(renderedShape, this, "click");
     68                this.shapes.push(renderedShape);
     69            }
     70        });
     71    }
     72
     73    displayRoomNames() {
     74        this.shapes.forEach((shape) => {
     75            shape.displayName(this.textLayer);
     76        });
     77
     78    }
     79
     80
     81
     82    loadMapN(floorData) {
     83        if (floorData == null || floorData === "") return;
     84
     85        this.deserializeMap(floorData);
     86        this.shapes.forEach((shape) => {
     87            this.mainLayer.add(shape);
     88        });
     89        this.displayRoomNames();
     90        this.initializeRoomTypes();
     91
     92    }
     93
     94    clearRoute() {
     95        this.routeLayer.removeChildren();
     96    }
     97
     98
     99    drawRouteNEW(nodes, offset = 0) {
     100
     101        this.clearRoute();
     102        console.log("====PATH====");
     103        nodes.forEach((node) => console.log("NODE", node));
     104
     105        let idx = offset;
     106        let buff = [nodes[idx].coordinates.x, nodes[idx].coordinates.y];
     107
     108
     109        ++idx;
     110
     111        console.log("INIT BUFFER", buff);
     112        console.log("INIT IDX", idx);
     113
     114        const drawNextSegment = () => {
     115
     116            if (idx >= nodes.length){
     117                return;
     118            }
     119
     120            const currentNode = nodes[idx - 1];
     121            const nextNode = nodes[idx];
     122
     123            if (nextNode.floorNumber !== currentNode.floorNumber) {
     124                triggerNavigate(nodes, idx, nextNode.floorNumber, nextNode);
     125                return;
     126            }
     127
     128            const startX = currentNode.coordinates.x;
     129            const startY = currentNode.coordinates.y;
     130            const endX = nextNode.coordinates.x;
     131            const endY = nextNode.coordinates.y;
     132
     133            const numSegments = 12;
     134
     135            const deltaX = (endX - startX) / numSegments;
     136            const deltaY = (endY - startY) / numSegments;
     137
     138            const drawSegment = (i) => {
     139                const segmentX = startX + deltaX * i;
     140                const segmentY = startY + deltaY * i;
     141
     142                buff.push(segmentX, segmentY);
     143
     144                let line = this.navArrow.clone({ points: [...buff] });
     145                this.routeLayer.add(line);
     146                this.routeLayer.draw();
     147
     148                buff = [segmentX, segmentY];
     149            };
     150
     151            let segmentIdx = 1;
     152            const interval = setInterval(() => {
     153                drawSegment(segmentIdx);
     154                segmentIdx++;
     155
     156                if (segmentIdx > numSegments) {
     157                    clearInterval(interval);
     158                    idx++;
     159                    setTimeout(drawNextSegment, 150);
     160                }
     161            }, 50);
     162        };
     163
     164        drawNextSegment();
     165    }
     166
     167
     168
     169    initializeRoomTypes() {
     170        this.roomTypes = this.shapes
     171            .filter((shape) => shape.class === "Room" && shape.info.type !== "")
     172            .map((shape) => shape.info.type);
     173    }
     174
     175    getRoomTypes() {
     176        return this.roomTypes;
     177    }
     178
     179
     180    getShapeByName(name){
     181        return this.shapes.find(shape => shape.info.name === name)
     182    }
     183
     184    getShapeByType(type) {
     185        return this.shapes.filter((shape) => shape.class === type)
     186    }
     187
     188    toggleSearchRoom() {
     189        this.toggleSearch = !this.toggleSearch;
     190    }
     191
     192    //ova e loso ne trebit vaka
     193    highlightShape(roomName) {
     194        let foundShape = this.shapes.filter((shape) => shape.info.name === roomName)[0];
     195        foundShape.highlight();
     196    }
     197
     198    getMainEntrance() {
     199        return this.shapes.filter(shape => shape.class === "Entrance").filter(el => el.info.isMainEntrance === true)[0];
     200    }
     201
     202    setFilter(filter) {
     203        let rooms = this.getShapeByType("Room")
     204        if (filter === "All") {
     205            rooms.forEach((shape) => {
     206                shape.unHighlight()
     207            })
     208        } else {
     209            rooms.filter((shape) => shape.info.type === filter).forEach((shape) => {
     210                shape.highlight()
     211            })
     212            rooms.filter((shape) => shape.info.type !== filter).forEach((shape) => {
     213                shape.unHighlight()
     214            })
    109215        }
    110216
    111         index++;
    112 
    113         setTimeout(drawNextSegment, 25);
    114     };
    115 
    116     drawNextSegment();
     217    }
    117218}
    118 
    119 
    120   search() {
    121     console.log("VLEZE VO SEARCH");
    122   }
    123 }
  • imaps-frontend/src/scripts/net/HttpService.js

    rd565449 r0c6b92a  
    33
    44class HttpService {
    5     constructor(URL = config.apiBaseUrl, auth = false) {
    6       this.baseURL = URL;
     5    constructor(auth = false) {
    76      this.auth = auth;
    87    }
     
    3332      }
    3433 
    35       const response = await fetch(`${this.baseURL}${endpoint}`, options);
     34      const response = await fetch(`${endpoint}`, options);
    3635     
    3736      if (!response.ok) {
  • imaps-frontend/src/scripts/net/netconfig.js

    rd565449 r0c6b92a  
     1export const API_BASE_URL = "http://localhost:8080/api";
     2export const API_PROTECTED = `${API_BASE_URL}/protected`
     3export const API_PUBLIC = `${API_BASE_URL}/public`
     4export const API_AUTH = `${API_BASE_URL}/auth`
     5
    16
    27const config = {
    3     apiBaseUrl: "http://localhost:8080/api",
     8    my_maps: {
     9        display: `${API_PROTECTED}/my-maps`,
     10        save: `${API_PROTECTED}/my-maps/save`,
     11        add: `${API_PROTECTED}/my-maps/create`,
     12        load: `${API_PROTECTED}/my-maps/load`,
     13        delete: `${API_PROTECTED}/my-maps/delete`,
     14        publish: `${API_PROTECTED}/publish/add`,
     15        publish_get: `${API_PROTECTED}/publish/get`,
     16    },
     17    room_types: {
     18        display: (auth) => {
     19            return auth ? `${API_PROTECTED}/room-types` : `${API_PUBLIC}/room-types`
     20        },
     21        add: `${API_PROTECTED}/room-types/add`,
    422
    5     endpoints: {
    6         login: "/auth/login",
    7         register: "/auth/register",
    8         verify: "/auth/verify",
    9         render: "/protected/render",
    10         publicMapData: "/public/mapData"
     23    },
     24    floors: {
     25        add: `${API_PROTECTED}/floors/add`,
     26        load: `${API_PROTECTED}/my-maps/load`,
     27        delete: `${API_PROTECTED}/floors/delete`,
     28    },
     29    view_maps: {
     30        display: `${API_PUBLIC}/maps`,
     31        load: (auth) => {
     32            return auth ? `${API_PROTECTED}/load-map` : `${API_PUBLIC}/load-map`
     33        },
     34        navigate: `${API_PUBLIC}/navigate`,
     35        add_favourite: `${API_PROTECTED}/favourites/add`,
     36    },
     37    favourites: {
     38        display: `${API_PROTECTED}/favourites`,
     39        add: `${API_PROTECTED}/favourites/add`,
     40        delete: `${API_PROTECTED}/favourites/delete`,
     41    },
     42    auth:{
     43        login: `${API_AUTH}/login`,
     44        register: `${API_AUTH}/register`,
     45        verify: `${API_AUTH}/verify`
     46
     47    },
     48    admin:{
     49        display: `${API_BASE_URL}/admin`,
     50        load_pr: `${API_BASE_URL}/admin/load-pr`,
     51        approve_pr: `${API_BASE_URL}/admin/pr/approve`,
     52        deny_pr: `${API_BASE_URL}/admin/pr/deny`
    1153    }
    1254};
    1355
     56
    1457export default config;
  • imaps-frontend/src/scripts/rendered_shapes/RenderedEntrance.js

    rd565449 r0c6b92a  
    1 import RenderedMapShape from "./RenderedMapShape.js";
     1import RenderedMapShape from "../base/RenderedMapShape.js";
     2import {_registerNode} from "konva/lib/Global";
     3import RenderedRoom from "./RenderedRoom.js";
    24
    3 export default class RenderedEntrance extends RenderedMapShape{
    4     constructor(attrs,scaleX,scaleY){
    5         super({
    6             x: attrs.x,
    7             y: attrs.y,
    8             width: attrs.width * scaleX,
    9             height: attrs.height * scaleY,
    10             fill: '#1c3cff',
    11             stroke: 'black',
    12             strokeWidth: 1,
    13             draggable: false,
    14             rotation: attrs.rotation,
    15             cornerRadius:3,
    16             zIndex: 0
    17         });
     5export default class RenderedEntrance extends RenderedMapShape {
     6  constructor(attrs, scaleX, scaleY) {
     7    super({
     8      x: attrs.x,
     9      y: attrs.y,
     10      width: attrs.width * scaleX,
     11      height: attrs.height * scaleY,
     12      fill: "#7fef83",
     13      stroke: "black",
     14      strokeWidth: 1,
     15      draggable: false,
     16      rotation: attrs.rotation,
     17      cornerRadius: 3,
     18      zIndex: 0,
     19    });
    1820
    19         this.info.name = attrs.obj_name;
    20         this.info.description = attrs.description;
    21        
    22    
    23         this.on("mouseenter",() => {
    24         console.log("HOVER ROOM IN", this.x());
    25         console.log(this.info.name,"NAME");
    26         this.stroke('purple');
    27         })
    28         this.on("mouseleave", () => {
    29             console.log("HOVER ROOM OUT");
    30             this.opacity(1);
    31             this.stroke('black')
     21    this.info.name = attrs.obj_name;
     22    this.info.description = attrs.description;
     23    this.info.isMainEntrance = attrs.is_main_entrance;
    3224
    33         })
     25    this.floorNum = attrs.floor_num
    3426
    35         this.initText()
    36     }
     27    this.class = "Entrance";
     28
     29    console.log("ATTRS VIEW:  " + attrs)
     30
     31    this.on("mouseenter", () => {
     32      this.stroke("purple");
     33    });
     34    this.on("mouseleave", () => {
     35      this.opacity(1);
     36      this.stroke("black");
     37    });
     38
     39    //this.initText();
     40  }
    3741}
     42
     43RenderedEntrance.prototype.className = "RenderedEntrance";
     44_registerNode(RenderedEntrance);
  • imaps-frontend/src/scripts/rendered_shapes/RenderedRoom.js

    rd565449 r0c6b92a  
    1 import RenderedMapShape from "./RenderedMapShape.js";
     1import RenderedMapShape from "../base/RenderedMapShape.js";
     2import {_registerNode} from "konva/lib/Global";
    23
    3 export default class RenderedRoom extends RenderedMapShape{
    4     constructor(attrs,scaleX,scaleY){
    5         super({
    6             x: attrs.x,
    7             y: attrs.y,
    8             width: attrs.width * scaleX,
    9             height: attrs.height * scaleY,
    10             fill: 'white',
    11             stroke: 'black',
    12             strokeWidth: 1,
    13             draggable: false,
    14             rotation: attrs.rotation,
    15             cornerRadius:3,
    16             zIndex: 0
    17         });
     4export default class RenderedRoom extends RenderedMapShape {
     5  constructor(attrs, scaleX, scaleY) {
     6    super({
     7      x: attrs.x,
     8      y: attrs.y,
     9      width: attrs.width * scaleX,
     10      height: attrs.height * scaleY,
     11      fill: "#A2D9FF",
     12      stroke: "black",
     13      strokeWidth: 1,
     14      draggable: false,
     15      rotation: attrs.rotation,
     16      cornerRadius: 3
     17    });
    1818
    19         this.info.name = attrs.obj_name;
    20         this.info.description = attrs.description;
     19    console.info("FNUM RENDER:",attrs.floor_num)
    2120
    22         this.textOffsetX = -50
    23        
    24    
    25         this.on("mouseenter",() => {
    26         console.log("HOVER ROOM IN", this.x());
    27         console.log(this.info.name,"NAME");
    28         this.opacity(0.7);
    29         this.fill("pink");
    30         })
    31         this.on("mouseleave", () => {
    32             console.log("HOVER ROOM OUT");
    33             this.opacity(1);
    34             this.fill("white");
    35         })
     21    this.floorNum = attrs.floor_num
    3622
    37         this.initText()
    38     }
     23    this.info.name = attrs.obj_name;
     24    this.info.type = attrs.room_type;
     25    this.info.description = attrs.description;
     26    this.class = "Room";
     27    this.textOffsetX = -50;
    3928
    40    
     29    this.eventName = "openRoomInfoPanel"
     30
     31    this.on("mouseenter", () => {
     32      console.log(this.info.name, "NAME");
     33      this.fill("#65c3f8");
     34    });
     35    this.on("mouseleave", () => {
     36      this.fill("#A2D9FF");
     37    });
     38
     39
     40
     41    // searched(){
     42    //   this.fill("#b92d39")
     43    // }
     44    // unsearched(){
     45    //   this.fill("#A2D9FF");
     46    // }
     47
     48    // console.log("ATTRS: " + attrs);
     49
     50    this.initText();
     51  }
     52  highlight(){
     53    this.fill("rgba(29,238,78,0.49)");
     54    this.strokeWidth(2)
     55  }
     56  unHighlight(){
     57    this.fill("#A2D9FF");
     58    this.strokeWidth(1);
     59  }
    4160}
     61RenderedRoom.prototype.className = "RenderedRoom";
     62_registerNode(RenderedRoom);
  • imaps-frontend/src/scripts/rendered_shapes/RenderedWall.js

    rd565449 r0c6b92a  
    1 import RenderedMapShape from "./RenderedMapShape.js";
     1import RenderedMapShape from "../base/RenderedMapShape.js";
     2import {_registerNode} from "konva/lib/Global";
    23
    34export default class RenderedWall extends RenderedMapShape{
     
    89            width: attrs.width * scaleX,
    910            height: attrs.height * scaleY,
    10             fill: 'grey',
     11            fill: '#4B4B4B',
    1112            stroke: 'black',
    1213            strokeWidth: 1,
     
    1819    }
    1920}
     21
     22RenderedWall.prototype.className = "RenderedWall";
     23_registerNode(RenderedWall);
  • imaps-frontend/src/scripts/shapes/Entrance.js

    rd565449 r0c6b92a  
    1 import Konva from "konva";
    2 import MapShape from "./MapShape";
    3 import { _registerNode } from "konva/lib/Global";
    4 export default class Entrance extends MapShape {
    5   constructor(mousePos, blockSize, layer, rotation, snap,id,scaleX = 1, scaleY = 1) {
    6     super(
    7       {
    8         x: mousePos.x,
    9         y: mousePos.y,
    10         width: blockSize * scaleX,
    11         height: blockSize * 2 * scaleY,
    12         fill: "#0051ff",
    13         stroke: "grey",
    14         strokeWidth: 1,
    15         opacity: 0.7,
    16         name: "mapObj",
    17         draggable: true,
    18         rotation: rotation,
    19         zIndex: 1,
    20       },
    21       layer,
    22       blockSize,
    23       snap
    24     );
    25     this.type = "Entrance";
    26     this.modalEventName = "openEntranceModalEvent";
     1import {_registerNode} from "konva/lib/Global";
     2import MapNode from "../base/MapNode.js";
     3import {node} from "prop-types";
    274
    28     this.id = id;
     5export default class Entrance extends MapNode {
    296
    30     this._info = {
    31       name: `Entrance ${id}`,
    32       connectedRoom: "",
    33       description: "",
    34       isMainEntrance: false,
    35       selectedPin: "",
    36       selectedPins: [],
    37     };
     7    constructor(attrs, id) {
    388
    39     this.initText();
    40   }
     9        if (!attrs.fromLoad) {
     10            attrs.height *= 2;
     11        }
     12        super(
     13            {
     14                x: attrs.position.x,
     15                y: attrs.position.y,
     16                width: attrs.width,
     17                height: attrs.height,
     18                fill: "rgb(126,238,167)",
     19                stroke: "#252627",
     20                strokeWidth: 1,
     21                opacity: 0.9,
     22                name: "mapObj",
     23                draggable: true,
     24                rotation: attrs.rotation,
     25                zIndex: 1,
     26            },
     27            attrs.layer,
     28            attrs.blockSize,
     29            attrs.snap
     30        );
     31        this.type = "Entrance";
     32        this.eventName = "openEntranceModalEvent";
     33        this.floorNum = attrs.floorNum
    4134
    42   loadInfo(attrs) {
    43     this.info.name = attrs.obj_name;
    44     this.info.connectedRoom = attrs.connected_room;
    45     this.info.description = attrs.description;
    46     this.info.isMainEntrance = attrs.is_main_entrance;
    47     this.info.selectedPins = attrs.connected_pins;
    48   }
     35        this.id = id;
    4936
    50   saveShapeDetails() {
    51     this.setAttr("connected_pins", this.info.selectedPins);
    52     this.setAttr("obj_name", this.info.name);
    53     this.setAttr("description", this.info.description);
    54     this.setAttr("is_main_entrance", this.info.isMainEntrance);
    55     this.setAttr("connected_room", this.info.connectedRoom);
    56   }
     37        this._info = {
     38            name: `Entrance${id} [${this.floorNum}F]`,
     39            connectedRoom: "",
     40            description: "",
     41            isMainEntrance: false,
     42            selectedPin: "",
     43            selectedPins: [],
     44        };
     45
     46        this.initText();
     47        this.moveToTop();
     48    }
     49
     50    loadInfo(attrs) {
     51        this.info.name = attrs.obj_name;
     52        this.info.connectedRoom = attrs.connected_room;
     53        this.info.description = attrs.description;
     54        this.info.isMainEntrance = attrs.is_main_entrance;
     55        this.info.selectedPins = attrs.connected_pins;
     56        this.floorNum = attrs.floor_num;
     57    }
     58
     59    saveShapeDetails() {
     60        console.info("fnum entrance",this.attrs.floorNum)
     61
     62        this.setAttr("connected_pins", this.info.selectedPins);
     63        this.setAttr("obj_name", this.info.name);
     64        this.setAttr("description", this.info.description);
     65        this.setAttr("is_main_entrance", this.info.isMainEntrance);
     66        this.setAttr("connected_room", this.info.connectedRoom);
     67        this.setAttr("floor_num",this.floorNum);
     68    }
     69
     70    connect(node, draw = true) {
     71        if(this.floorNum !== node.floorNum) return;
     72
     73        super.connect(node)
     74    }
     75
     76    setInfo(infoObj) {
     77        console.log("SA VIKNA SETINFO")
     78        this.info = infoObj;
     79        if(this.info.connectedRoom == null || this.info.connectedRoom === "" ){
     80            this.strokeWidth(2);
     81            this.stroke("#a10114")
     82        }else{
     83            this.strokeWidth(1)
     84            this.stroke("black")
     85        }
     86    }
    5787}
    5888
  • imaps-frontend/src/scripts/shapes/InfoPin.js

    rd565449 r0c6b92a  
    1 import Konva from "konva";
    2 import MapShape from "./MapShape";
    3 import Factory from "../util/Factory";
     1
    42import { _registerNode } from "konva/lib/Global";
    5 export default class InfoPin extends MapShape {
    6   constructor(mousePos, blockSize, layer, snappable,id) {
     3import MapNode from "../base/MapNode.js";
     4import {node} from "prop-types";
     5import draw from "../../pages/Draw/Draw.jsx";
     6export default class InfoPin extends MapNode {
     7  constructor(attrs,id) {
     8    attrs.snap = false;
    79    super(
    810      {
    9         x: mousePos.x,
    10         y: mousePos.y,
    11         radiusX: blockSize * 0.5,
    12         radiusY: blockSize * 0.7,
    13         tailHeight: blockSize * 1.2,
    14         fill: "#d70113",
     11        x: attrs.position.x,
     12        y: attrs.position.y,
     13        radiusX: attrs.blockSize * 0.5,
     14        radiusY: attrs.blockSize * 0.7,
     15        tailHeight: attrs.blockSize * 1.2,
     16        fill: "#f60000",
    1517        stroke: "#1b1b1b",
    16         strokeWidth: 0.2,
     18        strokeWidth: 1,
    1719        draggable: true,
    1820        name: "mapObj",
    1921      },
    20       layer,
    21       blockSize,
    22       snappable
     22      attrs.layer,
     23      attrs.blockSize,
     24      attrs.snap
    2325    );
    2426
    2527    this.id = id;
     28    this.eventName = "openPinModalEvent";
     29    this.floorNum = attrs.floorNum;
    2630
    27     this.modalEventName = "openPinModalEvent";
    2831    this.type = "InfoPin";
    2932    this._info = {
    30       name: `Pin ${id}`,
     33      name: `Pin${id} [${this.floorNum}F]`,
    3134      selectedPins: [],
    3235      description: "",
     
    3437
    3538    this.on("mouseover", () => {
    36       this.fill("yellow");
     39      this.fill("#FFD700");
    3740    });
    3841    this.on("mouseout", () => {
    39       this.fill("red");
     42      this.fill("#f60000");
    4043    });
     44
    4145
    4246    this.initText();
    4347  }
    4448  _sceneFunc(context, shape) {
    45     const { radiusX, radiusY, tailHeight } = this.attrs;
     49    const { radiusX, radiusY, tailHeight } = this.attrs; // attrs od konva
    4650
    4751    context.beginPath();
     
    5963  }
    6064
     65
    6166  loadInfo(attrs) {
    6267    this.info.name = attrs.obj_name;
    6368    this.info.selectedPins = attrs.connected_pins;
    6469    this.info.description = attrs.description;
     70    this.floorNum = attrs.floor_num
    6571  }
    6672
     
    6975    this.setAttr("connected_pins", this.info.selectedPins);
    7076    this.setAttr("description", this.info.description);
     77    this.setAttr("floor_num",this.floorNum)
    7178    console.log(this.info, "vnatre vo info");
     79  }
     80
     81  connect(node, draw = true) {
     82    if(this.floorNum !== node.floorNum) return;
     83    super.connect(node)
    7284  }
    7385}
  • imaps-frontend/src/scripts/shapes/Room.js

    rd565449 r0c6b92a  
    11import Konva from "konva";
    2 import MapShape from "./MapShape";
     2import MapShape from "../base/MapShape.js";
    33import { _registerNode } from "konva/lib/Global";
    44export default class Room extends MapShape {
    5   constructor(mousePos, blockSize, layer, rotation, snap, id, scaleX = 1, scaleY = 1){
     5  constructor(attrs,id){
     6
     7      if(!attrs.fromLoad){
     8          attrs.width *= 12;
     9          attrs.height *= 8;
     10      }
     11
     12      console.log(attrs.position,"hehe")
    613    super(
    714      {
    8         x: mousePos.x,
    9         y: mousePos.y,
    10         width: blockSize * 8 * scaleX,
    11         height: blockSize * 4 * scaleY,
    12         fill: "#DDE0F8",
     15        x: attrs.position.x,
     16        y: attrs.position.y,
     17        width: attrs.width * attrs.scaleX,
     18        height: attrs.height * attrs.scaleY,
     19        fill: "rgb(86,168,253)",
    1320        stroke: "grey",
    1421        strokeWidth: 1,
    1522        name: "mapObj",
    16         rotation: rotation,
     23        rotation: attrs.rotation,
    1724        draggable: true,
    1825      },
    19       layer,
    20       blockSize,
    21       snap
     26        attrs.layer,
     27        attrs.blockSize,
     28        attrs.snap
    2229    );
    2330
     31      this.floorNum = attrs.floorNum;
     32
    2433    this._info = {
    25       name: `Room ${id}`,
     34      name: `Room${id} [${this.floorNum}F]`,
    2635      type: "",
    2736      description: "",
     
    2938
    3039    this.type = "Room";
    31     this.modalEventName = "openRoomModalEvent";
     40    this.eventName = "openRoomModalEvent";
    3241    this.id = id;
    3342
     
    3948    this.info.type = attrs.room_type;
    4049    this.info.description = attrs.description;
     50    this.floorNum = attrs.floor_num;
    4151  }
    4252
     
    4555    this.setAttr("room_type", this.info.type);
    4656    this.setAttr("description", this.info.description);
     57    this.setAttr("floor_num",this.floorNum);
    4758  }
    4859}
  • imaps-frontend/src/scripts/shapes/Stairs.js

    rd565449 r0c6b92a  
    11import Konva from "konva";
    2 import { MapShape } from "./MapShape";
     2import MapNode from "../base/MapNode.js";
     3import {_registerNode} from "konva/lib/Global.js";
     4import Room from "./Room.js";
     5export default class Stairs extends MapNode{
     6    constructor(attrs,id) {
    37
     8        if(!attrs.fromLoad){
     9            attrs.width *= 4;
     10        }
     11
     12        super({
     13                x: attrs.position.x,
     14                y: attrs.position.y,
     15                width: attrs.width * attrs.scaleX,
     16                height: attrs.height * attrs.scaleY,
     17                fill: "rgb(225,213,124)",
     18                stroke: "rgb(16,15,15)",
     19                strokeWidth: 1,
     20                name: "mapObj",
     21                rotation: attrs.rotation,
     22                draggable: true
     23            },
     24            attrs.layer,
     25            attrs.blockSize,
     26            attrs.snap
     27        );
     28
     29        this.floorNum = attrs.floorNum;
     30
     31        this.type = "Stairs"
     32        this._info = {
     33            name: `Stairs${id} [${this.floorNum}F]`,
     34            description: "",
     35            selectedPins: []
     36        };
     37
     38        this.id = id;
     39        this.eventName = "openStairsModalEvent";
     40        this.initText();
     41    }
     42
     43
     44    _sceneFunc(context, shape) {
     45        const { width, height} = this.attrs;
     46
     47        let steps = 5;
     48        context.beginPath()
     49        for(let i = 0; i < steps; i++){
     50            context.rect((-this.blockSize) * i,(this.blockSize * 0.6) * i,width * 0.86,height/2)
     51            context.fillStrokeShape(shape);
     52        }
     53        context.closePath()
     54    }
     55
     56    loadInfo(attrs) {
     57        this.info.name = attrs.obj_name;
     58        this.info.description = attrs.description;
     59        this.info.selectedPins = attrs.connected_pins;
     60        this.floorNum = attrs.floor_num;
     61    }
     62
     63    saveShapeDetails() {
     64        this.setAttr("connected_pins", this.info.selectedPins);
     65        this.setAttr("obj_name", this.info.name);
     66        this.setAttr("description", this.info.description);
     67        this.setAttr("floor_num",this.floorNum)
     68    }
     69    connect(node,draw = true) {
     70        let canDraw = this.floorNum === node.floorNum;
     71        super.connect(node,canDraw);
     72    }
     73
     74}
     75
     76Stairs.prototype.className = "Stairs";
     77_registerNode(Stairs);
    478//TODO
  • imaps-frontend/src/scripts/shapes/Wall.js

    rd565449 r0c6b92a  
    11import Konva from "konva";
    2 import MapShape from "./MapShape";
     2import MapShape from "../base/MapShape.js";
    33import { _registerNode } from 'konva/lib/Global';
    44export default class Wall extends MapShape {
    5     constructor(mousePos, blockSize, layer, rotation,snap, draggable = true,scaleX = 1, scaleY = 1){
     5    constructor(attrs){
     6        if(!attrs.fromLoad){
     7            attrs.height *= 8;
     8        }
     9
     10
    611      super(
    712        {
    8           x: mousePos.x,
    9           y: mousePos.y,
    10           width: blockSize * scaleX,
    11           height: blockSize * 8 * scaleY,
    12           fill: "#DDE0F8",
     13          x: attrs.position.x,
     14          y: attrs.position.y,
     15          width: attrs.width * attrs.scaleX,
     16          height: attrs.height * attrs.scaleY,
     17          fill: "#d3d3d3",
    1318          stroke: "grey",
    1419          strokeWidth: 1,
    1520          name: "mapObj",
    16           draggable: draggable,
    17           rotation: rotation,
     21          draggable: true,
     22          rotation: attrs.rotation,
    1823          zIndex: 0,
    1924        },
    20         layer,
    21         blockSize,
    22         snap,
     25        attrs.layer,
     26        attrs.blockSize,
     27        attrs.snap,
    2328      );
    24  
    2529      this.type = "Wall";
     30
     31        this.floorNum = attrs.floorNum;
    2632    }
    27   }
     33
     34    loadInfo(attrs) {
     35        this.floorNum = attrs.floor_num;
     36    }
     37
     38    saveShapeDetails() {
     39        this.setAttr("floor_num",this.floorNum)
     40    }
     41}
    2842
    2943  Wall.prototype.className = 'Wall'
  • imaps-frontend/src/scripts/util/Factory.js

    rd565449 r0c6b92a  
    66import RenderedEntrance from "../rendered_shapes/RenderedEntrance";
    77import RenderedWall from "../rendered_shapes/RenderedWall.js";
     8import Stairs from "../shapes/Stairs.js";
     9import RenderedStairs from "../rendered_shapes/RenderedStairs.js";
     10import {updateShapeCount, getShapeCount} from "./ShapeCount.js";
    811export default class Factory {
     12  // BUG KO KE NAPRES REFRESH SA RESETVAT COUNTS!
    913
    10   static infoPinCount = 0;
    11   static wallCount = 0;
    12   static entranceCount = 0;
    13   static roomCount = 0;
     14  static createShape(shapeType,attrs) {
     15    console.log(attrs,"attrs in factory")
     16    console.log("position in factory: " + attrs.position.x)
    1417
    15   static createShape(shapeType, position, blockSize, layer, rotation,scaleX = 1, scaleY = 1, increment = false) {
     18    let scaleX = (attrs.scaleX ? parseFloat(attrs.scaleX) : 1);
     19    let scaleY =  (attrs.scaleY ? parseFloat(attrs.scaleY) : 1);
     20
     21    attrs.scaleX = scaleX;
     22    attrs.scaleY = scaleY;
     23
    1624    switch (shapeType) {
    1725      case "Entrance":
    18         if(increment) this.entranceCount++;
    19         return new Entrance(position, blockSize, layer, rotation,true,this.entranceCount,scaleX,scaleY);
     26        if(attrs.increment) updateShapeCount("entrance")
     27        return new Entrance(attrs,getShapeCount("entrance"));
    2028      case "Room":
    21         if(increment) this.roomCount++;
    22         return new Room(position, blockSize, layer, rotation,true, this.roomCount,scaleX,scaleY);
    23       case "Wall":
    24         return new Wall(position, blockSize, layer, rotation,true,scaleX,scaleY);
    25       case "InfoPin":
    26         if(increment) this.infoPinCount++;
    27         return new InfoPin(position, blockSize, layer, false,this.infoPinCount);
     29        if(attrs.increment) updateShapeCount("room");
     30        return new Room(attrs,getShapeCount("room"));
     31       case "Wall":
     32         return new Wall(attrs);
     33       case "InfoPin":
     34        if(attrs.increment) updateShapeCount("infoPin")
     35          return new InfoPin(attrs,getShapeCount("infoPin"));
     36      case "Stairs":
     37        if(attrs.increment) updateShapeCount("stairs")
     38        return new Stairs(attrs,getShapeCount("stairs"))
    2839      default:
    2940        throw new Error("Invalid shape type: " + shapeType);
     
    4152      case "Wall":
    4253        return new RenderedWall(attrs,scaleX,scaleY);
     54      case "Stairs":
     55        return new RenderedStairs(attrs,scaleX,scaleY)
    4356      default:
    4457        throw new Error("Invalid shape type." + shapeType);
  • imaps-frontend/src/scripts/util/zoomStage.js

    rd565449 r0c6b92a  
    1 export const zoomStage = (e,stage) => {
    2     if (!e.evt.shiftKey) return;
     1export const zoomStage = (e,stage,shift=false) => {
     2    if (shift && !e.evt.shiftKey) return;
    33
    44    e.evt.preventDefault();
Note: See TracChangeset for help on using the changeset viewer.