Changeset 79a0317 for imaps-frontend/src
- Timestamp:
- 01/21/25 03:08:24 (3 days ago)
- Branches:
- main
- Parents:
- 0c6b92a
- Location:
- imaps-frontend/src
- Files:
-
- 25 added
- 58 edited
Legend:
- Unmodified
- Added
- Removed
-
imaps-frontend/src/App.jsx
r0c6b92a r79a0317 19 19 import {AppProvider} from "./components/AppContext/AppContext.jsx"; 20 20 import AdminPage from "./pages/AdminPage/AdminPage.jsx"; 21 import {AuthCallback} from "./pages/AuthCallback.jsx"; 22 import PrivacyPolicy from "./pages/PrivacyPolicy/PrivacyPolicy.jsx"; 23 import TermsOfService from "./pages/TermsOfService/TermsOfService.jsx"; 21 24 22 25 … … 30 33 31 34 <Route element={<ProtectedRoute/>}> 32 <Route path="/myMaps/ :mapName/Draw" element={<Draw/>}/>35 <Route path="/myMaps/Draw/:mapName" element={<Draw/>}/> 33 36 <Route path="/myMaps" element={<MyMaps/>}/> 34 <Route path="/myMaps/ :mapName/View" element={<MapView isPrivate={true}/>}/>37 <Route path="/myMaps/View/:mapName" element={<MapView isPrivate={true}/>}/> 35 38 <Route path="/Admin" element={<AdminPage/>}/> 36 39 </Route> 37 40 <Route path="/" element={<IMaps/>}/> 38 <Route path="/Maps/ :mapName/View" element={<MapView isPrivate={false}/>}/>41 <Route path="/Maps/View/:mapName" element={<MapView isPrivate={false}/>}/> 39 42 <Route path="/Maps" element={<BrowseMaps/>}/> 43 <Route path="/auth-callback" element={<AuthCallback/>}/> 44 <Route path={"/privacy-policy"} element={<PrivacyPolicy/>}/> 45 <Route path={"/terms-of-service"} element={<TermsOfService/>}/> 40 46 41 47 -
imaps-frontend/src/TODO.txt
r0c6b92a r79a0317 1 PRIORITY HIGH 2 3 - Id fix na shapes vo Draw 4 5 PRIORITY MEDIUM 6 7 - Detach event na draw (keys: e, r) 8 - Text na shape vo Draw da e top layer 9 10 PRIORITY LOW 11 12 - Admin Page 13 14 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 17 18 19 bug: selectiras eden shape pa drug, pa celo vreme sa klavat posle shape 1 -IDs za shape posebno od iminja 2 - Refactor style na my maps i browse, da ne e tolku prazno od okolu 3 - Admin link da izlegvit za admin users 4 - Google maps url da sa pokazvit vo browse na mapite -
imaps-frontend/src/components/DrawGuide/DrawGuide.jsx
r0c6b92a r79a0317 18 18 <div onClick={toggleModal} className={styles.overlay}></div> 19 19 <div className={styles.modalContent}> 20 <h2 >How to Use the Map Builder</h2>21 <p >Welcome to the Map Builder! Here you can create an indoor map, render it and enjoy!:</p>20 <h2 className={styles.title}>How to Use the Map Builder</h2> 21 <p className={styles.paragraph}>Welcome to the Map Builder! Here you can create an indoor map, render it and enjoy!:</p> 22 22 <br></br> 23 23 <ul> … … 35 35 </ul> 36 36 <br></br> 37 <p >After completing your map, click the render button and go to the View page to see your full featured map!</p>37 <p className={styles.paragraph}>After completing your map, click the render button and go to the View page to see your full featured map!</p> 38 38 39 39 <button className={styles.closeModal} onClick={toggleModal}> -
imaps-frontend/src/components/DrawGuide/DrawGuide.module.css
r0c6b92a r79a0317 18 18 backdrop-filter: blur(5px); /* Optional blur effect */ 19 19 z-index: 999; 20 21 } 22 23 .title{ 24 color: white; 25 } 26 27 .paragraph{ 28 color: #ababab; 20 29 } 21 30 … … 25 34 left: 50%; 26 35 transform: translate(-50%, -50%); 27 background-color: #2c2f33; /* Darker background for modal content */36 background-color: #2c2f33; 28 37 color: #ffffff; /* White text for contrast */ 29 38 padding: 20px; … … 35 44 } 36 45 37 h2 { 38 margin-bottom: 15px; 39 color: #ffffff; /* White text for the heading*/40 } 46 /*h2 {*/ 47 /* margin-bottom: 15px;*/ 48 /* color: #ffffff; !* White text for the heading *!*/ 49 /*}*/ 41 50 42 51 .btnModal { -
imaps-frontend/src/components/FilterBar/FilterBar.module.css
r0c6b92a r79a0317 1 /* Filter Bar.module.css */1 /* FilterMaps.module.css */ 2 2 * { 3 3 font-family: "Poppins", sans-serif; … … 18 18 padding: 0px 0; /* Adds some space around the buttons */ 19 19 width: 100%; 20 overflow-y: hidden; 20 21 } 21 22 22 23 .buttonValue { 23 border: 2px solid #6759ff;24 border: 2px solid rgb(37,111,123); 24 25 padding: 0.5em 1.5em; 25 26 border-radius: 3em; 26 27 background-color: transparent; 27 color: #6759ff;28 color: rgb(37,111,123); 28 29 cursor: pointer; 29 30 transition: background-color 0.3s, color 0.3s; … … 33 34 .buttonValue:hover, 34 35 .buttonValue:focus { 35 background-color: #6759ff;36 background-color: rgb(33, 98, 108); 36 37 color: #ffffff; 37 38 } 38 39 39 40 .active { 40 background-color: #6759ff;41 background-color: rgb(37,111,123); 41 42 color: #ffffff; 42 43 } … … 47 48 48 49 .scrollableContainer::-webkit-scrollbar-thumb { 49 background-color: #6759ff;50 background-color: rgb(37,111,123); 50 51 border-radius: 3px; 51 52 } -
imaps-frontend/src/components/LoadingContainer/LoadingContainer.jsx
r0c6b92a r79a0317 1 1 import React from "react"; 2 import styles from "./LoadingContainer.module.css" 2 3 3 4 export const LoadingContainer = () => { … … 5 6 <div className="loading-container"> 6 7 <div className="spinner"></div> 7 <p>Loading, please wait...</p>8 8 </div> 9 9 ); -
imaps-frontend/src/components/Logo/Logo.jsx
r0c6b92a r79a0317 1 1 import React from "react"; 2 2 import { useNavigate } from "react-router-dom"; 3 import logoImage from "../../assets/logo_icon.png";3 import novo_logo from "../../assets/novo_logo_nobg_cropped.png"; 4 4 import styles from "./Logo.module.css"; 5 5 … … 16 16 onClick={handleClick} 17 17 > 18 <img src={ logoImage} alt="Logo" className={styles.logoImage} />18 <img src={novo_logo} alt="Logo" className={styles.logoImage} /> 19 19 </div> 20 20 ); -
imaps-frontend/src/components/Logo/Logo.module.css
r0c6b92a r79a0317 8 8 9 9 .logoImage { 10 width: 50px; /* Adjust the size as needed */10 width: 80px; 11 11 height: auto; 12 12 transition: opacity 0.2s ease; 13 border-radius: 20px; 13 14 } 14 15 … … 30 31 } 31 32 32 .logoImage {33 width: 50px; /* Adjust size as needed */34 height: auto;35 transition: opacity 0.2s ease;36 }37 38 33 .logoImage:hover { 39 34 opacity: 0.8; /* Hover effect */ -
imaps-frontend/src/components/MapInfoModal/MapInfoModal.jsx
r0c6b92a r79a0317 1 import React, { useEffect, useState} from "react";1 import React, { useEffect, useState } from "react"; 2 2 import styles from "./MapInfoModal.module.css"; 3 import {json, useNavigate} from "react-router-dom";4 3 import edit_icon from "../../assets/edit_icon_black.png"; 5 4 import PublishForm from "../PublishForm/PublishForm.jsx"; 6 5 import HttpService from "../../scripts/net/HttpService.js"; 7 6 import config from "../../scripts/net/netconfig.js"; 8 import {useAppContext} from "../AppContext/AppContext.jsx"; 9 10 export default function MapInfoModal({isOpen, onClose, map, onDelete, onUpdate, onPublish, published=false}) { 7 import { useAppContext } from "../AppContext/AppContext.jsx"; 8 import { useNavigate } from "react-router-dom"; 9 10 export default function MapInfoModal({ 11 isOpen, 12 onClose, 13 map, 14 onDelete, 15 onUpdate, 16 onPublish, 17 published = false, 18 }) { 11 19 const [isEditPopupOpen, setEditPopupOpen] = useState(false); 12 const [editedName, setEditedName] = useState(map?.mapName || ""); 13 const [editedGmapsUrl, setEditedGmapsUrl] = useState(map?.gmaps_url || ""); 14 const [publishFormOpen,setPublishFormOpen] = useState(false) 20 const [editedName, setEditedName] = useState(""); 21 const [editedGmapsUrl, setEditedGmapsUrl] = useState(""); 22 const [editedType, setEditedType] = useState(""); 23 const [publishFormOpen, setPublishFormOpen] = useState(false); 15 24 const navigate = useNavigate(); 16 const[loadedFormData,setLoadedFormData] = useState(null) 17 25 const [loadedFormData, setLoadedFormData] = useState(null); 26 const { setLoading } = useAppContext(); 27 const { loading } = useAppContext(); 28 const { username } = useAppContext(); 18 29 19 30 useEffect(() => { 20 console.log("GMAPS: " + JSON.stringify(map)) 21 }, []); 22 23 const {username} = useAppContext(); 31 if (map) { 32 setEditedName(map.mapName || ""); 33 setEditedGmapsUrl(map.gmaps_url || ""); 34 setEditedType(map.mapType || ""); 35 } 36 }, [map, isEditPopupOpen]); 24 37 25 38 if (!isOpen || !map) return null; 26 39 27 40 const handleView = () => { 28 navigate(`/myMaps/ ${map.mapName}/View`)41 navigate(`/myMaps/View/${map.mapName}`); 29 42 }; 30 43 31 44 const handleEdit = () => { 32 navigate(`/myMaps/ ${map.mapName}/Draw`)45 navigate(`/myMaps/Draw/${map.mapName}`); 33 46 }; 34 47 35 48 const handleDelete = () => { 36 if (window.confirm(`Are you sure you want to delete the map "${map.mapName}"?`)) { 49 if ( 50 window.confirm(`Are you sure you want to delete the map "${map.mapName}"?`) 51 ) { 37 52 onDelete(map.mapName); 38 53 onClose(); … … 40 55 }; 41 56 42 43 57 const openEditPopup = () => { 44 58 setEditPopupOpen(true); … … 51 65 const handleEditSubmit = async () => { 52 66 const updatedMap = { 53 ...map, 54 mapName: editedName, 55 gmaps_url: editedGmapsUrl, 67 initialName: map.mapName, 68 name: editedName, 69 gmapsUrl: editedGmapsUrl, 70 type: editedType, 56 71 }; 57 58 try { 59 //await onUpdate(updatedMap); 60 setEditPopupOpen(false); 61 } catch (error) { 62 console.error("Error updating map:", error); 63 } 64 }; 65 66 const openPublishModal = async () => { 67 const httpService = new HttpService(true); 68 const respForm = await httpService.get(`${config.my_maps.publish_get}?mapName=${map.mapName}`) 69 setLoadedFormData(respForm); 70 setPublishFormOpen(true) 71 }; 72 73 const sendPublishRequest = async (formData) => { 74 const httpService = new HttpService(true); 75 formData.mapName = map.mapName; 76 console.log("FORMDATA: "+JSON.stringify(formData)) 77 await httpService.post(`${config.my_maps.publish}?username=${username}`,formData); 78 setPublishFormOpen(false) 79 onPublish() 80 } 72 onUpdate(updatedMap); 73 closeEditPopup() 74 }; 81 75 82 76 return ( 83 77 <div className={styles.modalOverlay} onClick={onClose}> 84 <div className={styles.modalContent} onClick={(e) => e.stopPropagation()}> 85 {publishFormOpen && ( 86 <PublishForm mapName={map.mapName} formData={loadedFormData} onSubmit={sendPublishRequest} onCancel={() => setPublishFormOpen(false)}/> 87 )} 88 89 <img src={map.image_url} alt="Map Thumbnail" className={styles.mapImage}/> 90 <h2 className={styles.title}> 91 {map.mapName} 92 <img 93 src={edit_icon} 94 alt="Edit" 95 className={styles.editIcon} 96 onClick={openEditPopup} 97 /> 98 </h2> 99 <p><strong>Status:</strong> {map.status}</p> 100 101 <p><strong>Created At:</strong> {new Date(map.created_at).toLocaleString()}</p> 102 <p><strong>Modified At:</strong> {new Date(map.modified_at).toLocaleString()}</p> 103 <p><strong>Published 104 At:</strong> {map.published_at ? new Date(map.published_at).toLocaleString() : "Not published yet"} 105 </p> 106 <p> 107 <strong>Google Maps URL:</strong> 108 <a href={map.gMapsUrl} rel="noopener noreferrer"> 109 Open in Google Maps 110 </a> 111 </p> 112 113 <div className={styles.buttonContainer}> 114 <button className={styles.viewButton} onClick={handleView}> 115 View 116 </button> 117 <button className={styles.editButton} onClick={handleEdit}> 118 Edit 119 </button> 120 <button className={styles.deleteButton} onClick={handleDelete}> 121 Delete 122 </button> 123 {!map.is_published && !published && ( 124 <button className={styles.publishButton} onClick={openPublishModal}> 125 Publish 78 <div 79 className={styles.modalContent} 80 onClick={(e) => e.stopPropagation()} 81 > 82 {!loading && ( 83 <> 84 <img 85 src={map.image_url} 86 alt="Map Thumbnail" 87 className={styles.mapImage} 88 /> 89 <h2 className={styles.title}> 90 {map.mapName} 91 <img 92 src={edit_icon} 93 alt="Edit" 94 className={styles.editIcon} 95 onClick={openEditPopup} 96 /> 97 </h2> 98 <p> 99 <strong>Status:</strong> {map.status} 100 </p> 101 <p> 102 <strong>Created At:</strong>{" "} 103 {new Date(map.created_at).toLocaleString()} 104 </p> 105 <p> 106 <strong>Modified At:</strong>{" "} 107 {new Date(map.modified_at).toLocaleString()} 108 </p> 109 <p> 110 <strong>Published At:</strong>{" "} 111 {map.published_at 112 ? new Date(map.published_at).toLocaleString() 113 : "Not published yet"} 114 </p> 115 <p> 116 <strong>Google Maps URL:</strong>{" "} 117 <a href={`${map.gmaps_url}`} rel="noopener noreferrer"> 118 Open in Google Maps 119 </a> 120 </p> 121 122 <div className={styles.buttonContainer}> 123 <button className={styles.viewButton} onClick={handleView}> 124 View 125 </button> 126 <button className={styles.editButton} onClick={handleEdit}> 127 Edit 128 </button> 129 <button className={styles.deleteButton} onClick={handleDelete}> 130 Delete 131 </button> 132 {!map.is_published && !published && ( 133 <button 134 className={styles.publishButton} 135 onClick={() => setPublishFormOpen(true)} 136 > 137 Publish 138 </button> 139 )} 140 </div> 141 <button className={styles.closeButton} onClick={onClose}> 142 Close 126 143 </button> 127 )} 128 </div> 129 <button className={styles.closeButton} onClick={onClose}> 130 Close 131 </button> 132 133 {isEditPopupOpen && ( 134 <div className={styles.editPopupOverlay} onClick={closeEditPopup}> 135 <div 136 className={styles.editPopupContent} 137 onClick={(e) => e.stopPropagation()} 138 > 139 <h3 className={styles.title}>Edit Map Details</h3> 140 <div className={styles.editField}> 141 <label>Map Name:</label> 142 <input 143 type="text" 144 value={editedName} 145 onChange={(e) => setEditedName(e.target.value)} 146 /> 144 145 {isEditPopupOpen && ( 146 <div 147 className={styles.editPopupOverlay} 148 onClick={closeEditPopup} 149 > 150 <div 151 className={styles.editPopupContent} 152 onClick={(e) => e.stopPropagation()} 153 > 154 <h3 className={styles.title}>Edit Map Details</h3> 155 <div className={styles.editField}> 156 <label>Map Name:</label> 157 <input 158 type="text" 159 value={editedName} 160 onChange={(e) => setEditedName(e.target.value)} 161 /> 162 </div> 163 <div className={styles.editField}> 164 <label>Google Maps URL:</label> 165 <input 166 type="text" 167 value={editedGmapsUrl} 168 onChange={(e) => setEditedGmapsUrl(e.target.value)} 169 /> 170 </div> 171 <div className={styles.editField}> 172 <label>Map Type:</label> 173 <input 174 type="text" 175 value={editedType} 176 onChange={(e) => setEditedType(e.target.value)} 177 /> 178 </div> 179 <div className={styles.editPopupButtons}> 180 <button 181 className={styles.submitButton} 182 onClick={handleEditSubmit} 183 > 184 Submit 185 </button> 186 <button 187 className={styles.cancelButton} 188 onClick={closeEditPopup} 189 > 190 Cancel 191 </button> 192 </div> 193 </div> 147 194 </div> 148 <div className={styles.editField}> 149 <label>Google Maps URL:</label> 150 <input 151 type="text" 152 value={editedGmapsUrl} 153 onChange={(e) => setEditedGmapsUrl(e.target.value)} 154 /> 155 </div> 156 <div className={styles.editPopupButtons}> 157 <button className={styles.submitButton} onClick={handleEditSubmit}> 158 Submit 159 </button> 160 <button className={styles.cancelButton} onClick={closeEditPopup}> 161 Cancel 162 </button> 163 </div> 164 </div> 165 </div> 195 )} 196 </> 166 197 )} 167 198 </div> -
imaps-frontend/src/components/MapInfoModal/MapInfoModal.module.css
r0c6b92a r79a0317 14 14 15 15 .modalContent { 16 position: relative; 16 17 background-color: white; 17 18 padding: 20px; … … 22 23 text-align: center; 23 24 animation: fadeIn 0.3s ease-out; 25 24 26 } 25 27 -
imaps-frontend/src/components/Modals/CreateMapModal/CreateMapModal.jsx
r0c6b92a r79a0317 10 10 const mapDetails = { 11 11 name: mapName, 12 type: mapType,13 12 }; 14 13 … … 34 33 /> 35 34 </label> 36 <label>37 Map Type:38 <input39 type="text"40 value={mapType}41 onChange={(e) => setMapType(e.target.value)}42 required43 />44 </label>45 35 <div className={styles.modalButtons}> 46 36 <button type="submit" className={styles.modalSubmitButton}> -
imaps-frontend/src/components/Modals/CreateMapModal/CreateMapModal.module.css
r0c6b92a r79a0317 36 36 /* Modal Buttons Container */ 37 37 .modalButtons { 38 margin-top: 1.5rem;39 38 display: flex; 40 39 justify-content: space-between; -
imaps-frontend/src/components/Modals/EntranceModal/EntranceModal.jsx
r0c6b92a r79a0317 42 42 setConnections(connections); 43 43 setIsOpen(true); 44 event.detail.map.detachKeyPressEventListeners(); 44 45 console.log(connections, "Loaded pins on modal open"); 45 46 },"openEntranceModalEvent") … … 52 53 <ModalSelectRoom shapeInfo={shapeInfo} availableRooms={ShapeQuery.findAllByTypeAndFloor(shape?.floorNum,"Room")} updateModalData={updateModalData}/> 53 54 <ModalSelectConnections2 54 availableShapes={ShapeQuery.findAllByType ("InfoPin","Entrance")} // najubo ke e entrance samo so room da mozit55 availableShapes={ShapeQuery.findAllByTypeAndFloor(map?.floorNum,"Entrance","InfoPin")} // najubo ke e entrance samo so room da mozit 55 56 addConnection={addConnection} 56 57 updateModalData={updateModalData} -
imaps-frontend/src/components/Modals/EntranceModal/EntranceModal.module.css
r0c6b92a r79a0317 34 34 left: 50%; 35 35 transform: translate(-50%, -50%); 36 background-color: #2c2f33 ;36 background-color: #2c2f33f7; 37 37 color: #ffffff !important; 38 38 padding: 20px; … … 90 90 margin-bottom: 15px; 91 91 } 92 93 /*tuka novo start*/ 94 95 label { 96 font-size: 16px; 97 font-weight: bold; 98 margin-bottom: 8px; 99 } 100 101 .customFileInput { 102 position: relative; 103 } 104 105 .uploadButton { 106 background-color: #007bff; 107 color: white; 108 border: none; 109 padding: 10px 20px; 110 border-radius: 5px; 111 cursor: pointer; 112 font-size: 16px; 113 transition: background-color 0.3s ease; 114 } 115 116 .uploadButton:hover { 117 background-color: #0056b3; 118 } 119 120 .uploadButton:focus { 121 outline: none; 122 box-shadow: 0 0 4px rgba(0, 123, 255, 0.6); 123 } 124 125 .uploadButton:active { 126 background-color: #003f7f; 127 } 128 129 /*tuka novo end*/ 92 130 93 131 .formGroup label { -
imaps-frontend/src/components/Modals/Hooks/useConnections.jsx
r0c6b92a r79a0317 5 5 6 6 const addConnection = () => { 7 console.log("TOSKA",shapeInfo)8 7 if (!shapeInfo.selectedPin || connections.includes(shapeInfo.selectedPin)) return; 9 8 -
imaps-frontend/src/components/Modals/Hooks/useModalState.jsx
r0c6b92a r79a0317 13 13 map.updateRoomNames(); 14 14 triggerMapSave(); 15 console.log("SHAPE BEF CLOSE:",shape.info)15 map.attachKeyPressEventListeners(); 16 16 } 17 17 setIsOpen(!isOpen); -
imaps-frontend/src/components/Modals/InfoPinModal/InfoPinModal.jsx
r0c6b92a r79a0317 37 37 setConnections(shape.info.selectedPins || []); 38 38 setIsOpen(true); 39 event.detail.map.detachKeyPressEventListeners(); 39 40 40 41 console.log(shape.info.selectedPins, "Loaded pins on modal open"); -
imaps-frontend/src/components/Modals/RoomModal/RoomModal.jsx
r0c6b92a r79a0317 7 7 import useModalState from "../Hooks/useModalState.jsx"; 8 8 import {useModalEvent} from "../Hooks/useModalEvent.jsx"; 9 import ModalUploadRoomImage from "../Components/ModalUploadRoomImage.jsx"; 9 10 10 11 export default function RoomModal({map,roomTypes}) { … … 20 21 setShapeInfo(shape.info); 21 22 setIsOpen(true); 23 event.detail.map.detachKeyPressEventListeners(); 22 24 23 25 },"openRoomModalEvent") … … 30 32 <ModalRoomTypes updateModalData={updateModalData} shapeInfo={shapeInfo} roomTypes={roomTypes}/> 31 33 <ModalDescriptionField shapeInfo={shapeInfo} updateModalData={updateModalData}/> 34 <ModalUploadRoomImage></ModalUploadRoomImage> 32 35 <ModalSaveButton saveDetails={saveDetails}/> 33 36 </Modal> -
imaps-frontend/src/components/Modals/RoomTypeModal/RoomTypeModal.jsx
r0c6b92a r79a0317 31 31 <div onClick={toggleModal} className={styles.overlay}></div> 32 32 <div className={styles.modalContent}> 33 <h2 >Manage Room Types</h2>33 <h2 style={{color: 'white'}}>Manage Room Types</h2> 34 34 35 35 <form className={styles.form}> -
imaps-frontend/src/components/Modals/StairsModal/StairsModal.jsx
r0c6b92a r79a0317 33 33 setIsOpen(true); 34 34 console.log(roomObj.info.selectedPins, "Loaded pins on modal open"); 35 event.detail.map.detachKeyPressEventListeners(); 35 36 },"openStairsModalEvent") 36 37 -
imaps-frontend/src/components/Profile/Profile.jsx
r0c6b92a r79a0317 1 import React, { useState, useRef, useEffect, useContext } from "react"; 2 import { useNavigate, Link } from "react-router-dom"; 1 import React, { useState, useRef, useEffect } from "react"; 2 import ReactDOM from "react-dom"; 3 import { useNavigate } from "react-router-dom"; 3 4 import profile from "../../assets/person_icon.png"; 4 5 import styles from "./Profile.module.css"; … … 8 9 const { username, isAuthenticated } = useAppContext(); 9 10 const [open, setOpen] = useState(false); 10 const menuRef = useRef(null);11 const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 }); 11 12 const imgRef = useRef(null); 12 13 const navigate = useNavigate(); … … 16 17 useEffect(() => { 17 18 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 } 19 if (imgRef.current && !imgRef.current.contains(e.target)) { 20 setOpen(false); 22 21 } 23 22 }; … … 30 29 }, []); 31 30 31 const toggleMenu = () => { 32 if (imgRef.current) { 33 const rect = imgRef.current.getBoundingClientRect(); 34 setMenuPosition({ 35 top: rect.bottom + window.scrollY, 36 left: rect.left + window.scrollX, 37 }); 38 } 39 setOpen(!open); 40 }; 41 32 42 const handleMenuClick = (menu) => { 33 43 if (menu === "My Maps") { … … 36 46 localStorage.removeItem("token"); 37 47 window.location.reload(); 48 } else if (menu === "Login") { 49 navigate("/Login"); 38 50 } 39 51 setOpen(false); 40 52 }; 41 53 54 const renderDropdown = () => { 55 if (!open) return null; 56 57 return ReactDOM.createPortal( 58 <div 59 className={styles.dropdownMenu} 60 style={{ 61 position: "absolute", 62 top: menuPosition.top, 63 left: menuPosition.left, 64 }} 65 > 66 {isAuthenticated && <div className={styles.username}>{username}</div>} 67 <ul className={styles.menuList}> 68 {menus.map((menu) => ( 69 <li 70 key={menu} 71 className={styles.menuItem} 72 onClick={() => handleMenuClick(menu)} 73 > 74 {menu} 75 </li> 76 ))} 77 </ul> 78 </div>, 79 document.body 80 ); 81 }; 82 42 83 return ( 43 <div className={position === "fixed" ? styles.fixedProfileContainer : styles.inlineProfileContainer}> 84 <div 85 className={ 86 position === "fixed" ? styles.fixedProfileContainer : styles.inlineProfileContainer 87 } 88 > 44 89 <div className={styles.profileWrapper}> 45 <div className={styles.profileIconContainer} onClick={() => setOpen(!open)}> 46 <img src={profile} alt="profile" className={styles.profileImage} ref={imgRef} /> 90 <div 91 className={styles.profileIconContainer} 92 onClick={toggleMenu} 93 ref={imgRef} 94 > 95 <img src={profile} alt="profile" className={styles.profileImage} /> 47 96 </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 )} 97 {renderDropdown()} 66 98 </div> 67 99 </div> -
imaps-frontend/src/components/PublishForm/PublishForm.jsx
r0c6b92a r79a0317 15 15 ); 16 16 const [errors, setErrors] = useState({}); 17 const [denyReason, setDenyReason] = useState(''); 18 const [isDenying, setIsDenying] = useState(false); 17 19 18 20 const mapTypeOptions = ['Hospital', 'Faculty', 'House', 'Other']; … … 42 44 onSubmit(form); 43 45 } 46 }; 47 48 const handleDeny = () => { 49 if (!denyReason.trim()) { 50 alert('Please provide a reason for denial.'); 51 return; 52 } 53 onDeny(form.id, form.mapName, denyReason); 54 setIsDenying(false); 44 55 }; 45 56 … … 130 141 <button 131 142 className={styles.cancelButton} 132 onClick={onCancel} 143 onClick={() => { 144 setIsDenying(false); 145 onCancel(); 146 }} 133 147 > 134 148 Cancel … … 136 150 <button 137 151 className={styles.approveButton} 138 onClick={() => onApprove(form.id, form.mapName)}152 onClick={() => onApprove(form.id, form.mapName)} 139 153 > 140 154 Approve … … 142 156 <button 143 157 className={styles.denyButton} 144 onClick={() => onDeny(form.id,form.mapName,"Reason")}158 onClick={() => setIsDenying(true)} 145 159 > 146 160 Deny 147 161 </button> 148 162 </div> 163 {isDenying && ( 164 <div className={styles.denyReason}> 165 <textarea className={styles.denyReasonTextArea} 166 placeholder="Enter reason for denial..." 167 value={denyReason} 168 onChange={(e) => setDenyReason(e.target.value)} 169 /> 170 <div className={styles.buttonGroup}> 171 <button 172 className={styles.denySubmitButton} 173 onClick={handleDeny} 174 > 175 Submit 176 </button> 177 </div> 178 </div> 179 )} 149 180 </div> 150 181 )} -
imaps-frontend/src/components/PublishForm/PublishForm.module.css
r0c6b92a r79a0317 10 10 z-index: 999; /* Behind the modal */ 11 11 } 12 13 .denyReason { 14 display: flex; 15 flex-direction: column; 16 margin-top: 10px; 17 border-radius: 20px; 18 } 19 .denyReasonTextArea{ 20 padding-bottom: 4rem; 21 } 22 23 .denySubmitButton { 24 color: white; 25 margin-right: 10px; 26 } 27 12 28 13 29 /* Modal styles */ -
imaps-frontend/src/components/RoomInfoPanel/RoomInfoPanel.jsx
r0c6b92a r79a0317 56 56 Navigate 57 57 </button> 58 <p style={{fontSize: '0.8em'}}><i>From Main Entrance</i></p> 58 59 </> 59 60 ) : ( -
imaps-frontend/src/components/SearchBar/SearchBar.jsx
r0c6b92a r79a0317 6 6 import styles from "./SearchBar.module.css"; 7 7 8 function SearchBar({ map, handleDirectionsSubmit, setIsPanelOpen, setSelectedRoom, availableShapes,handleFloorChange }) { 8 function SearchBar({ 9 map, 10 handleDirectionsSubmit, 11 setIsPanelOpen, 12 setSelectedRoom, 13 availableShapes, 14 handleFloorChange, 15 }) { 9 16 const [isExpanded, setIsExpanded] = useState(false); 10 17 const [from, setFrom] = useState(""); … … 14 21 const [dropdownVisible, setDropdownVisible] = useState(false); 15 22 const [inputFieldType, setInputFieldType] = useState(""); 16 const dropdownRef = useRef(null); 17 18 const toggleExpanded = () => { 19 setIsExpanded(!isExpanded); 20 }; 21 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){ 23 24 const wrapperRef = useRef(null); 25 const dropdownContainerRef = useRef(null); 26 const activeInputRef = useRef(null); // Track the currently focused input field 27 28 const toggleExpanded = () => setIsExpanded(!isExpanded); 29 30 const handleClickOutside = (event) => { 31 if ( 32 wrapperRef.current && 33 !wrapperRef.current.contains(event.target) && 34 dropdownContainerRef.current && 35 !dropdownContainerRef.current.contains(event.target) 36 ) { 37 setDropdownVisible(false); 38 } 39 }; 40 41 useEffect(() => { 42 document.addEventListener("mousedown", handleClickOutside); 43 return () => { 44 document.removeEventListener("mousedown", handleClickOutside); 45 }; 46 }, []); 47 48 const searchRoom = () => { 49 const foundRoom = availableShapes.find((sh) => sh.info.name === from); 50 if (foundRoom && foundRoom.floorNum !== map.floorNum) { 26 51 handleFloorChange(foundRoom.floorNum); 27 52 } 28 29 30 console.log("FOUND ROOM: " + foundRoom)31 53 map.highlightShape(from); 32 54 setSelectedRoom(foundRoom); 33 55 setIsPanelOpen(true); 34 } 35 36 const handleInputFocus = (field ) => {56 }; 57 58 const handleInputFocus = (field, inputRef) => { 37 59 if (availableOptions.length === 0 && map) { 38 60 setAvailableOptions( … … 42 64 ); 43 65 } 66 setInputFieldType(field); 44 67 setDropdownVisible(true); 45 setInputFieldType(field);68 activeInputRef.current = inputRef; // Set the active input ref 46 69 }; 47 70 … … 69 92 if (!dropdownVisible || filteredOptions.length === 0) return null; 70 93 71 const position = dropdownRef.current?.getBoundingClientRect() || { top: 0, left: 0, width: 0 }; 94 const position = activeInputRef.current?.getBoundingClientRect() || { 95 top: 0, 96 left: 0, 97 width: 0, 98 }; 72 99 73 100 return ReactDOM.createPortal( 74 101 <ul 102 ref={dropdownContainerRef} 75 103 className={styles.dropdown} 76 104 style={{ 77 105 position: "absolute", 78 top: position.top + position.height ,79 left: position.left ,106 top: position.top + position.height + window.scrollY, 107 left: position.left + window.scrollX, 80 108 width: position.width, 81 109 }} … … 91 119 ))} 92 120 </ul>, 93 document.body // Portal renders outside the parent hierarchy121 document.body 94 122 ); 95 123 }; 96 124 97 125 return ( 98 <div className={styles.wrapper} >126 <div className={styles.wrapper} ref={wrapperRef}> 99 127 {!isExpanded ? ( 100 128 <div className={styles.searchBar}> … … 104 132 placeholder="Search location" 105 133 aria-label="Search" 106 ref={dropdownRef} // Attach the input to calculate dropdown position 107 onFocus={() => handleInputFocus("from")} 134 onFocus={(e) => handleInputFocus("from", e.target)} 108 135 onChange={handleInputChange(setFrom)} 109 136 value={from} … … 111 138 {renderDropdown()} 112 139 <div className={styles.buttons}> 113 <button type="button" className={styles.iconButton} onClick={searchRoom}> 140 <button 141 type="button" 142 className={styles.iconButton} 143 onClick={searchRoom} 144 > 114 145 <img src={searchIcon} alt="Search Icon" /> 115 146 </button> 116 <button type="button" className={styles.iconButton} onClick={toggleExpanded}> 147 <button 148 type="button" 149 className={styles.iconButton} 150 onClick={toggleExpanded} 151 > 117 152 <img src={routeIcon} alt="Route Icon" /> 118 153 </button> … … 127 162 aria-label="From" 128 163 value={from} 129 onFocus={( ) => handleInputFocus("from")}164 onFocus={(e) => handleInputFocus("from", e.target)} 130 165 onChange={handleInputChange(setFrom)} 131 166 className={styles.inputField} 132 ref={inputFieldType === "from" ? dropdownRef : null}133 167 /> 134 168 <input … … 137 171 aria-label="To" 138 172 value={to} 139 onFocus={( ) => handleInputFocus("to")}173 onFocus={(e) => handleInputFocus("to", e.target)} 140 174 onChange={handleInputChange(setTo)} 141 175 className={styles.inputField} 142 ref={inputFieldType === "to" ? dropdownRef : null}143 176 /> 144 177 {renderDropdown()} … … 152 185 <img src={searchIcon} alt="Submit Directions" /> 153 186 </button> 154 <button type="button" className={styles.iconButton} onClick={toggleExpanded}> 187 <button 188 type="button" 189 className={styles.iconButton} 190 onClick={toggleExpanded} 191 > 155 192 <img src={closeIcon} alt="Close Icon" /> 156 193 </button> -
imaps-frontend/src/components/SearchBar/SearchBar.module.css
r0c6b92a r79a0317 26 26 .inputField { 27 27 flex-grow: 1; 28 padding: 10px 15px;28 /*padding: 10px 15px;*/ 29 29 font-size: 16px; 30 30 border: 1px solid #ddd; … … 33 33 margin-right: 10px; 34 34 min-width: 0; 35 35 margin-top: 12px; 36 36 } 37 37 -
imaps-frontend/src/main.jsx
r0c6b92a r79a0317 4 4 import './index.css' 5 5 6 if (typeof GestureEvent === "undefined") { 7 window.GestureEvent = class GestureEvent {}; 8 } 9 10 6 11 ReactDOM.createRoot(document.getElementById('root')).render( 7 12 <App /> -
imaps-frontend/src/pages/AdminPage/AdminPage.jsx
r0c6b92a r79a0317 11 11 import Profile from "../../components/Profile/Profile.jsx"; 12 12 import Toast from "../../components/Toast/Toast.jsx"; 13 import ListReports from "../../components/ListReports/ListReports.jsx"; 13 14 14 15 const renderTile = ({data, isDragging}, handleApprove, handleDeny, openMapInfoModal, openPublishForm) => ( … … 82 83 gmaps_url: elem.gmaps_url, 83 84 image_url: card, 84 })).filter((tile) => tile.status === " INVALID");85 })).filter((tile) => tile.status === "PENDING"); 85 86 86 87 setPendingMaps(mapTiles); … … 145 146 146 147 try { 147 await httpService.post(url, formData); // Assuming formData contains all required fields148 await httpService.post(url, formData); 148 149 setPendingMaps((prev) => prev.filter((map) => map.mapName !== formData.mapName)); 149 150 alert(`Map "${formData.mapName}" published successfully.`); … … 187 188 <Profile></Profile> 188 189 <h1>Pending Maps for Approval</h1> 190 <hr/> 189 191 190 192 {publishFormMap && ( … … 210 212 /> 211 213 </div> 214 <hr/> 212 215 {isMapInfoModalOpen && ( 213 216 <MapInfoModal … … 223 226 )} 224 227 {toastMessage && <Toast message={toastMessage} type={toastType} onClose={() => setToastMessage(null)}/>} 225 228 <hr/> 229 <ListReports></ListReports> 226 230 </div> 227 231 ); -
imaps-frontend/src/pages/BrowseMaps/BrowseMaps.jsx
r0c6b92a r79a0317 1 1 import styles from "./Maps.module.css"; 2 2 import "react-tiles-dnd/esm/index.css"; 3 import { TilesContainer} from "react-tiles-dnd";4 import { Link} from "react-router-dom";3 import {TilesContainer} from "react-tiles-dnd"; 4 import {Link} from "react-router-dom"; 5 5 import card from "../../assets/card-map.png"; 6 6 import star_icon from "../../assets/star_icon.png"; // Unfilled star icon 7 7 import star_filled_icon from "../../assets/star_filled_icon.png"; // Filled star icon 8 import { useEffect, useState} from "react";8 import {useEffect, useState} from "react"; 9 9 import HttpService from "../../scripts/net/HttpService.js"; 10 10 import Logo from "../../components/Logo/Logo.jsx"; 11 11 import Profile from "../../components/Profile/Profile.jsx"; 12 12 import config from "../../scripts/net/netconfig.js"; 13 import { useAppContext } from "../../components/AppContext/AppContext.jsx"; 13 import {useAppContext} from "../../components/AppContext/AppContext.jsx"; 14 import FilterMaps from "../../components/FilterMaps/FilterMaps.jsx"; 14 15 15 16 let loadedTiles = []; 16 17 17 const renderTile = ({ data, isDragging, toggleFavorite}) => (18 <div style={{ padding: "1rem", width: "100%", position: "relative"}}>19 <Link to={`/Maps/ ${data.text}/View`} className={styles.linkStyle}>18 const renderTile = ({data, isDragging, toggleFavorite}) => ( 19 <div style={{padding: "1rem", width: "100%", position: "relative"}}> 20 <Link to={`/Maps/View/${data.text}`} className={styles.linkStyle}> 20 21 <div 21 22 className={`${styles.tile} ${isDragging ? styles.dragging : ""}`} 22 style={{ width: "100%", height: "100%"}}23 style={{width: "100%", height: "100%"}} 23 24 > 24 <img src={card} className={styles.imgStyle} alt="Map Thumbnail" 25 <div style={{ fontFamily: "exo"}}>25 <img src={card} className={styles.imgStyle} alt="Map Thumbnail"/> 26 <div style={{fontFamily: "exo"}}> 26 27 {data.text} {isDragging ? "DRAGGING" : null} 27 28 </div> … … 32 33 src={data.isFavorite ? star_filled_icon : star_icon} 33 34 alt="Favorite Icon" 34 style={{ width: "20px", height: "20px"}}35 style={{width: "20px", height: "20px"}} 35 36 /> 36 37 </div> … … 46 47 const [searchTerm, setSearchTerm] = useState(""); 47 48 const [tiles, setTiles] = useState([]); 48 const { username, isAuthenticated } = useAppContext(); 49 const {username, isAuthenticated} = useAppContext(); 50 const [filter, setFilter] = useState("all") 51 const [mapFilters, setMapFilters] = useState([]) 49 52 50 53 useEffect(() => { … … 52 55 const httpService = new HttpService(); 53 56 let mapTiles = []; 54 57 let mapTypes = ['Hospital', 'Faculty', 'House', 'Other']; 55 58 if (isAuthenticated) { 56 59 // :D … … 63 66 rows: 1, 64 67 isFavorite: true, 68 type: elem.mapType, 65 69 })); 66 70 71 console.log("TUKA") 67 72 // Load all maps 68 73 const allResp = await httpService.get(config.view_maps.display); 69 74 console.log("RESPONSE MAPS PUBLIC", allResp); 75 76 // mapTypes = allResp.filter(elem => elem.mapType != null && elem.mapType !== "").map(elem => elem.mapType); 77 70 78 71 79 const nonFavMapTiles = allResp … … 76 84 rows: 1, 77 85 isFavorite: false, 86 type: elem.mapType, 78 87 })); 88 79 89 80 90 mapTiles = [...favMapTiles, ...nonFavMapTiles]; … … 82 92 const allResp = await httpService.get(config.view_maps.display); 83 93 console.log("RESPONSE MAPS PUBLIC", allResp); 94 // mapTypes = allResp.filter(elem => elem.mapType != null && elem.mapType !== "").map(elem => elem.mapType); 84 95 85 96 mapTiles = allResp.map((elem) => ({ … … 88 99 rows: 1, 89 100 isFavorite: false, 101 type: elem.mapType, 90 102 })); 91 103 } 104 105 console.log("TYPES:", mapTypes); 106 setMapFilters(mapTypes); 92 107 93 108 loadedTiles = [...mapTiles]; … … 114 129 115 130 const updatedTiles = tiles.map((tile) => 116 tile.text === tileName ? { ...tile, isFavorite: !tile.isFavorite} : tile131 tile.text === tileName ? {...tile, isFavorite: !tile.isFavorite} : tile 117 132 ); 118 133 … … 140 155 }; 141 156 157 const onFilter = (selectedFilter) => { 158 setFilter(selectedFilter); 159 160 if (selectedFilter === "all") { 161 // Show all tiles 162 setTiles(loadedTiles); 163 } else { 164 // Filter tiles by selected type 165 const filteredTiles = loadedTiles.filter((tile) => tile.type === selectedFilter); 166 setTiles(filteredTiles); 167 } 168 }; 169 170 142 171 return ( 143 172 <div className={styles.container}> … … 152 181 onChange={handleSearchChange} 153 182 /> 183 154 184 </div> 155 185 <div className={styles.filterBar}> 186 <FilterMaps mapTypes={mapFilters} setFilter={onFilter}></FilterMaps> 187 </div> 188 {filter !== "all" && tiles.length === 0 && <p>No maps of type {filter} found</p>} 156 189 <TilesContainer 157 190 data={tiles} 158 renderTile={(props) => renderTile({ ...props, toggleFavorite})}191 renderTile={(props) => renderTile({...props, toggleFavorite})} 159 192 tileSize={tileSize} 160 forceTileWidth={1 50}161 forceTileHeight={ 170}193 forceTileWidth={170} 194 forceTileHeight={200} 162 195 /> 163 196 </div> -
imaps-frontend/src/pages/BrowseMaps/Maps.module.css
r0c6b92a r79a0317 19 19 margin-bottom: 30px; 20 20 text-align: center; 21 margin-top: 5em; 21 22 } 22 23 23 24 .searchBar input { 24 width: 300px; 25 /*width: 300px;*/ 26 width: 33vw; 25 27 padding: 10px; 26 28 font-size: 16px; … … 34 36 border-color: #1e90ff; 35 37 box-shadow: 0 0 8px rgba(30, 144, 255, 0.5); 38 } 39 40 .filterBar{ 41 margin-left: 6vw; 36 42 } 37 43 -
imaps-frontend/src/pages/Draw/Draw.jsx
r0c6b92a r79a0317 1 import { useContext, useEffect, useState} from "react";1 import {useContext, useEffect, useState} from "react"; 2 2 import styles from "./Draw.module.css"; 3 3 import RoomModal from "../../components/Modals/RoomModal/RoomModal.jsx"; … … 8 8 import SaveMap from "../../components/SaveMap/SaveMap.jsx"; 9 9 import Logo from "../../components/Logo/Logo.jsx"; 10 import { Link, useNavigate, useParams, useSearchParams} from "react-router-dom";10 import {Link, useNavigate, useParams, useSearchParams} from "react-router-dom"; 11 11 import Profile from "../../components/Profile/Profile.jsx"; 12 12 import HttpService from "../../scripts/net/HttpService.js"; … … 18 18 import config from "../../scripts/net/netconfig.js"; 19 19 import ShapeRegistry from "../../scripts/util/ShapeRegistry.js"; 20 import {TopPanel} from "./TopPanel/TopPanel.jsx"; 20 21 21 22 function Draw() { 22 const { mapName} = useParams();23 const { username} = useAppContext();23 const {mapName} = useParams(); 24 const {username} = useAppContext(); 24 25 25 const [isPopupVisible, setIsPopupVisible] = useState(false);26 const [errorMessage, setErrorMessage] = useState("Error");27 const [hasError, setHasError] = useState(false);28 const [searchParams, setSearchParams] = useSearchParams();26 const [isPopupVisible, setIsPopupVisible] = useState(false); 27 const [errorMessage, setErrorMessage] = useState("Error"); 28 const [hasError, setHasError] = useState(false); 29 const [searchParams, setSearchParams] = useSearchParams(); 29 30 30 const[roomTypes,setRoomTypes] = useState([]);31 const [roomTypes, setRoomTypes] = useState([]); 31 32 32 const {app,floors,saveFloor,setFloors} = useMapLoader(mapName,username,searchParams,setSearchParams)33 const {addRoomType} = useRoomTypesLoader(setRoomTypes,mapName,username);33 const {app, floors, saveFloor, setFloors} = useMapLoader(mapName, username, searchParams, setSearchParams) 34 const {addRoomType} = useRoomTypesLoader(setRoomTypes, mapName, username); 34 35 35 const addFloorHandler = async (newFloorNum) => {36 const httpService = new HttpService();37 httpService.setAuthenticated();36 const addFloorHandler = async (newFloorNum) => { 37 const httpService = new HttpService(); 38 httpService.setAuthenticated(); 38 39 39 const payload = { 40 num: newFloorNum, 41 mapName: mapName, 40 const payload = { 41 num: newFloorNum, 42 mapName: mapName, 43 }; 44 45 try { 46 await httpService.put(`${config.floors.add}`, payload); 47 console.log(`Added floor ${newFloorNum}`); 48 setFloors((prevFloors) => [...prevFloors, {num: newFloorNum}]); 49 } catch (error) { 50 console.error("Error adding floor:", error); 51 } 42 52 }; 43 53 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 }; 54 const deleteFloorHandler = async (floorNum) => { 55 if (floorNum === 0) return 52 56 53 const deleteFloorHandler = async (floorNum) => {54 if(floorNum === 0) return57 const httpService = new HttpService(); 58 httpService.setAuthenticated(); 55 59 56 const httpService = new HttpService(); 57 httpService.setAuthenticated(); 60 try { 61 await httpService.delete(`${config.floors.delete}?floorNum=${floorNum}&mapName=${mapName}`); 62 setFloors((prevFloors) => prevFloors.filter(f => f.num !== floorNum)) 58 63 59 try { 60 await httpService.delete(`${config.floors.delete}?floorNum=${floorNum}&mapName=${mapName}`); 61 setFloors((prevFloors) => prevFloors.filter(f => f.num !== floorNum)) 64 const currFloor = searchParams.get("floor"); 65 if (currFloor == floorNum) { 66 setSearchParams({floor: "0"}, {replace: true}) 67 } 68 console.log(`Deleted floor ${floorNum}`); 69 } catch (error) { 70 console.error("Error deleting floor:", error); 71 } 72 }; 62 73 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 73 useEffect(() => { 74 return () => { 75 ShapeRegistry.clear(); 76 } 77 }, []); 74 useEffect(() => { 75 return () => { 76 ShapeRegistry.clear(); 77 } 78 }, []); 78 79 79 80 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 }); 81 const handleSaveClick = async () => { 82 saveFloor() 83 setIsPopupVisible(true); 84 setTimeout(() => { 85 setIsPopupVisible(false); 86 }, 87 3000); 88 }; 90 89 90 return ( 91 <div className={styles.wrapper} id="wrapper"> 92 {/* <SideBar></SideBar> */} 93 <Logo></Logo> 94 <div id="container" className={styles.cont}></div> 95 <div className={styles.panel}> 96 <div className={styles.topPanelH}> 97 <Profile position="inline"></Profile> 98 </div> 99 <Link to={`/myMaps/View/${mapName}`} className={styles.titleLink}> 100 <h1 className={styles.title}>{mapName}</h1> 101 </Link> 102 <div className={styles.guideWrapper}> 103 <DrawGuide/> 104 </div> 105 <hr/> 106 <br/> 107 {/* {<h2 className={styles.paragraph}>Objects:</h2>} */} 108 <ul className={styles.shapeOptions} id="shapeOptions"> 109 <li data-info="Entrance" className={`${styles.shapeOption} ${styles.entrance}`}></li> 110 <li data-info="Wall" className={`${styles.shapeOption} ${styles.wall}`} id="wall"></li> 111 <li data-info="Room" className={`${styles.shapeOption} ${styles.room}`} id="room"></li> 112 <li data-info="Stairs" className={`${styles.shapeOption} ${styles.stairs}`} id="stairs"></li> 113 </ul> 114 <RoomTypeModal map={app} roomTypes={roomTypes} addRoomTypeDB={addRoomType}></RoomTypeModal> 115 <br/> 116 <hr/> 117 <br/> 118 <FloorSelector floorConfig={{ 119 floors, searchParams, 120 setSearchParams, addFloorHandler, 121 deleteFloorHandler 122 }}></FloorSelector> 91 123 92 };124 <br/> 93 125 94 return ( 95 <div className={styles.wrapper} id="wrapper"> 96 {/* <SideBar></SideBar> */} 97 <Logo></Logo> 98 <div id="container" className={styles.cont}></div> 99 <div className={styles.panel}> 100 <div className={styles.topPanelH}> 101 <Profile position="inline"></Profile> 126 <hr/> 127 <br/> 128 {hasError && <p style={{color: "red", textAlign: "center"}}>{errorMessage}</p>} 129 <div className={styles.templateCont}> 130 <SaveMap submitHandler={handleSaveClick}></SaveMap> 131 </div> 132 133 <div className={styles.hide}> 134 <RoomModal map={app} roomTypes={roomTypes}></RoomModal> 135 <EntranceModal map={app}></EntranceModal> 136 <InfoPinModal map={app}></InfoPinModal> 137 <StairsModal map={app}></StairsModal> 138 </div> 139 </div> 140 141 {isPopupVisible && ( 142 <div className={styles.popup}> 143 <div className={styles.popupContent}> 144 <h2>Map Saved!</h2> 145 <p>Your map has been successfully saved.</p> 146 </div> 147 </div> 148 )} 102 149 </div> 103 <Link to={`/myMaps/${mapName}/View`} className={styles.titleLink}> 104 <h1 className={styles.title}>{mapName}</h1> 105 </Link> 106 <div className={styles.guideWrapper}> 107 <DrawGuide/> 108 </div> 109 <hr/> 110 <br/> 111 {/* {<h2 className={styles.paragraph}>Objects:</h2>} */} 112 <ul className={styles.shapeOptions} id="shapeOptions"> 113 <li data-info="Entrance" className={`${styles.shapeOption} ${styles.entrance}`}></li> 114 <li data-info="Wall" className={`${styles.shapeOption} ${styles.wall}`} id="wall"></li> 115 <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> 117 </ul> 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> 127 128 <br/> 129 130 <hr/> 131 <br/> 132 {hasError && <p style={{color: "red", textAlign: "center"}}>{errorMessage}</p>} 133 <div className={styles.templateCont}> 134 <SaveMap submitHandler={handleSaveClick}></SaveMap> 135 </div> 136 137 <div className={styles.hide}> 138 <RoomModal map={app} roomTypes={roomTypes}></RoomModal> 139 <EntranceModal map={app}></EntranceModal> 140 <InfoPinModal map={app}></InfoPinModal> 141 <StairsModal map={app}></StairsModal> 142 </div> 143 </div> 144 145 {isPopupVisible && ( 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> 151 </div> 152 )} 153 </div> 154 ); 150 ); 155 151 } 156 152 -
imaps-frontend/src/pages/Draw/Draw.module.css
r0c6b92a r79a0317 25 25 font-weight: 700; 26 26 text-transform: uppercase; 27 color: # 333333;27 color: #c0c0c0; 28 28 font-family: "Oswald", sans-serif; 29 29 margin-bottom: 0; -
imaps-frontend/src/pages/IMaps/components/Button.css
r0c6b92a r79a0317 20 20 21 21 .btn--outline { 22 background-color: transparent;22 background-color: #ffffffc2; 23 23 24 color: # fff;24 color: #252627; 25 25 padding: 8px 20px; 26 border: 1px solid #252627 ; 26 27 /* border: 1px solid var(--primary); */ 27 28 transition: all 0.3s ease-out; … … 37 38 font-size: 24px; 38 39 } 40 .btn--outline:hover{ 41 transition: all 0.3s ease-out; 42 background: #000000; 43 color:#ffffffc2; 44 } 39 45 40 46 .btn--large:hover, … … 43 49 background: #ffffffc2; 44 50 color: #000000; 45 transition: 250ms;46 51 } -
imaps-frontend/src/pages/IMaps/components/Cards.jsx
r0c6b92a r79a0317 19 19 text="Create intricate floor plans for your building with precision and ease." 20 20 label="Create" 21 path=" /services"21 path="#" 22 22 /> 23 23 <CardItem … … 25 25 text="Explore and navigate through complex building layouts seamlessly." 26 26 label="Explore" 27 path=" /services"27 path="#" 28 28 /> 29 29 </ul> … … 33 33 text="Add custom icons, labels, and markers to personalize your indoor maps." 34 34 label="Customize" 35 path=" /services"35 path="#" 36 36 /> 37 37 <CardItem … … 39 39 text="Ensure accessibility by mapping out routes and facilities for all users." 40 40 label="Accessibility" 41 path=" /products"41 path="#" 42 42 /> 43 43 <CardItem … … 45 45 text="Share your maps with others and collaborate in real-time for efficient space planning." 46 46 label="Collaboration" 47 path=" /sign-up"47 path="#" 48 48 /> 49 49 </ul> -
imaps-frontend/src/pages/IMaps/components/Footer.css
r0c6b92a r79a0317 47 47 .footer-links { 48 48 width: 100%; 49 max-width: 1000px;50 49 display: flex; 51 justify-content: center; 50 justify-content: space-evenly; 51 } 52 .footer-link-wrapper a { 53 margin-top: 1em; 54 } 55 .footer-link-wrapper h2 { 56 width: 100%; 52 57 } 53 58 54 .footer-link-wrapper {55 display: flex;56 }57 59 58 60 .footer-link-items { 59 61 display: flex; 60 62 flex-direction: column; 61 align-items: flex-start;63 align-items: center; 62 64 margin: 16px; 63 65 text-align: left; … … 100 102 101 103 .social-media { 102 max-width: 1000px;103 width: 100%;104 104 } 105 105 106 106 .social-media-wrap { 107 107 display: flex; 108 justify-content: space-between;108 flex-direction: column; 109 109 align-items: center; 110 width: 90%;111 max-width: 1000px;112 margin: 40px auto 0 auto;113 110 } 114 111 … … 123 120 color: #fff; 124 121 justify-self: start; 125 margin-left: 20px;126 122 cursor: pointer; 127 123 text-decoration: none; … … 131 127 margin-bottom: 16px; 132 128 } 129 .social-media-wrap img { 130 width: 25%; 131 } 133 132 134 133 .website-rights { 135 134 color: #fff; 136 margin-bottom: 16px; 135 align-content: flex-end; 136 width: 50%; 137 margin-top: 2em; 137 138 } 138 139 -
imaps-frontend/src/pages/IMaps/components/Footer.jsx
r0c6b92a r79a0317 2 2 import "./Footer.css"; 3 3 import { Button } from "./Button"; 4 import logo from "../../../assets/logo_icon.png"; 4 import logo from "../../../assets/novo_logo_nobg_cropped.png"; 5 import {Link} from "react-router-dom"; 5 6 6 7 function Footer() { 7 8 return ( 8 9 <div className="footer-container"> 9 {/*<div className="footer-links">10 <div className="footer-links"> 10 11 <div className="footer-link-wrapper"> 11 12 <div className="footer-link-items"> … … 13 14 <a href="#">Contact</a> 14 15 <a href="#">Support</a> 15 <a href="#">Destinations</a>16 <a href="#">Sponsorships</a>17 16 </div> 18 17 </div> 19 <div className="footer-link-wrapper"> 20 <div className="footer-link-items"> 21 <h2>Social Media</h2> 22 <a href="#">Instagram</a> 23 <a href="#">Facebook</a> 24 <a href="#">Youtube</a> 25 <a href="#">Twitter</a> 18 <div className="social-media-wrap"> 19 20 <div className="footer-logo"> 21 <img src={logo} alt={"logo"}/> 22 </div> 23 <small className="website-rights">iMaps © {2024}</small> 24 </div> 25 <div> 26 <div className="footer-link-wrapper"> 27 <div className="footer-link-items"> 28 <h2>Legal & Privacy</h2> 29 <Link to={"/privacy-policy"}>Privacy Policy</Link> 30 <Link to={"/terms-of-service"}>Terms Of Service</Link> 31 32 </div> 26 33 </div> 27 34 </div> 28 </div> */} 29 <section className="social-media"> 30 <div className="social-media-wrap"> 31 <div className="footer-logo"> 32 <a href="#" className="social-logo"> 33 iMaps 34 </a> 35 </div> 36 <small className="website-rights">iMaps © {2024}</small> 37 <div className="social-icons"> 38 <a className="social-icon-link facebook" href="#" aria-label="Facebook"> 39 <i className="fab fa-facebook-f" /> 40 </a> 41 <a className="social-icon-link instagram" href="#" aria-label="Instagram"> 42 <i className="fab fa-instagram" /> 43 </a> 44 <a className="social-icon-link youtube" href="#" aria-label="Youtube"> 45 <i className="fab fa-youtube" /> 46 </a> 47 <a className="social-icon-link twitter" href="#" aria-label="Twitter"> 48 <i className="fab fa-twitter" /> 49 </a> 50 <a className="social-icon-link linkedin" href="#" aria-label="LinkedIn"> 51 <i className="fab fa-linkedin" /> 52 </a> 53 </div> 54 </div> 55 </section> 35 36 </div> 37 56 38 </div> 57 39 ); -
imaps-frontend/src/pages/IMaps/components/Navbar.css
r0c6b92a r79a0317 1 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); 2 background: linear-gradient(90deg, #ffffff, #fffefe); 14 3 color: #ffffff; 15 4 /*color: #333333;*/ … … 24 13 border-radius: 8px; 25 14 margin: 5px; 15 height: 70px; 16 overflow: hidden; 26 17 } 27 18 -
imaps-frontend/src/pages/IMaps/components/Navbar.jsx
r0c6b92a r79a0317 10 10 function Navbar() { 11 11 const [click, setClick] = useState(false); 12 const [button, setButton] = useState(true);13 14 const handleClick = () => setClick(!click);15 const closeMobileMenu = () => setClick(false);16 12 17 13 const { isAuthenticated } = useAppContext(); … … 23 19 <div className="navbar-left"> 24 20 <Logo position="relative"/> 25 <h1 className="navbar-title">iMaps</h1>26 21 </div> 27 22 -
imaps-frontend/src/pages/IMaps/components/pages/Home.jsx
r0c6b92a r79a0317 14 14 <h2 className="description">Create and explore detailed indoor maps.</h2> 15 15 <div className="hero-btns"> 16 <Link to="/ myMaps">17 <Button className="btns" buttonSize="btn--large">18 Create Maps16 <Link to="/Maps"> 17 <Button buttonSize="btn--large" > 18 Browse Maps <i className="far fa-play-circle" /> 19 19 </Button> 20 20 </Link> 21 <Link to="/ Maps">22 <Button className="btns" buttonSize="btn--large">23 Browse Maps <i className="far fa-play-circle" />21 <Link to="/myMaps"> 22 <Button buttonSize="btn--large" buttonStyle="btn--outline"> 23 Create Maps 24 24 </Button> 25 25 </Link> -
imaps-frontend/src/pages/IMaps/components/pages/Home.scss
r0c6b92a r79a0317 50 50 width: 100vw; 51 51 /* img size is 50x50 */ 52 background: url($bg-url) repeat 0 0; 52 /* Set a base background color */ 53 background: #87898d url($bg-url) repeat 0 0; 54 55 /* Apply hue rotation to the image */ 53 56 54 57 -webkit-animation: bg-scrolling-reverse .92s infinite; /* Safari 4+ */ … … 98 101 99 102 font-size: 6em; 100 101 102 103 103 104 -
imaps-frontend/src/pages/Login/Login.jsx
r0c6b92a r79a0317 6 6 import HttpService from "../../scripts/net/HttpService.js"; 7 7 import {useAppContext} from "../../components/AppContext/AppContext.jsx"; 8 import config from "../../scripts/net/netconfig.js"; 8 import config, {API_BASE_URL} from "../../scripts/net/netconfig.js"; 9 import google_icon from "../../assets/Logo-google-icon-PNG.png" 10 import github_icon from "../../assets/github-mark-white.png"; 11 import { v4 as uuidv4 } from 'uuid'; 9 12 10 13 const LoginPage = () => { … … 26 29 const handleLogin = async () => { 27 30 const httpService = new HttpService(); 28 return httpService.post(config.auth.login, payload) 29 31 return httpService.post(config.auth.login, payload); 30 32 }; 31 32 33 33 34 const login = async (e) => { … … 37 38 .then(resp => { 38 39 if (resp.token) { 39 navigate(targetPath) 40 navigate(targetPath); 40 41 localStorage.setItem("token", resp.token); 41 42 setUsername(resp.username); 42 43 setIsAuthenticated(true); 43 console.log("ROLES", resp.roles)44 console.log("ROLES", resp.roles); 44 45 } else { 45 46 setError("Invalid username or password."); … … 47 48 }).catch(reason => { 48 49 console.error("Login failed", reason); 49 setError("Login failed. Please try again.") 50 }) 50 setError("Login failed. Please try again."); 51 }); 52 }; 51 53 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 // }); 54 const continueWithGitHub = async () => { 55 const httpService = new HttpService(); 56 httpService.setResponseType('text'); 57 const state = await httpService.get(config.auth.oauth.github.state) 58 const clientId = 'Iv23liqzhX5wMYNDHtnz'; 59 const redirectUri = encodeURI(`${API_BASE_URL}/oauth/callback/github`); 60 61 const githubAuthUrl = `https://github.com/login/oauth/authorize?client_id=${encodeURI(clientId)}&redirect_uri=${redirectUri}&state=${encodeURI(state)}&scope=user:email`; 62 63 window.location.href = githubAuthUrl; 64 65 }; 66 67 const continueWithGoogle = async () => { 68 console.log("Continue with Google"); 69 const httpService = new HttpService(); 70 httpService.setResponseType('text'); 71 const state = await httpService.get(config.auth.oauth.github.state) 72 const clientId = '1024418489231-ml40ukvqcg9ad1h5ejor5dm6ipt6p8fo.apps.googleusercontent.com'; 73 const redirectUri = encodeURI(`${API_BASE_URL}/oauth/callback/google`); 74 const googleAuthUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${encodeURIComponent(clientId)} 75 &redirect_uri=${encodeURIComponent(redirectUri)}&state=${encodeURIComponent(state)}&response_type=code&scope=${encodeURIComponent("openid profile email")}`; 76 window.location.href = googleAuthUrl 77 77 }; 78 78 … … 111 111 <button type="submit">Submit</button> 112 112 </form> 113 <div className={styles.or}>OR</div> 114 <div className={styles.socialButtons}> 115 <button className={styles.socialButton} onClick={continueWithGoogle}> 116 <img src={google_icon} alt="Facebook Icon" className={styles.socialIcon}/> 117 Sign In With Google 118 </button> 119 <button className={styles.socialButton} onClick={continueWithGitHub}> 120 <img src={github_icon} alt="GitHub Icon" className={styles.socialIcon}/> 121 Sign In With GitHub 122 </button> 123 </div> 113 124 <p> 114 125 Don't have an account? <Link to="/Signup"> Sign Up </Link> -
imaps-frontend/src/pages/Login/Login.module.css
r0c6b92a r79a0317 13 13 14 14 .wrapper { 15 display: -webkit-box;16 display: -ms-flexbox;17 15 display: flex; 16 justify-content: flex-end; /* Move content towards the right */ 17 align-items: center; /* Center content vertically */ 18 18 height: 100vh; 19 -webkit-box-align: center; 20 -ms-flex-align: center; 21 align-items: center; 22 } 23 24 .wrapper p { 25 font-size: 0.85rem; 26 margin-top: 1rem; 27 } 19 margin-left: 30%; 20 padding: 2rem; 21 } 22 23 28 24 29 25 .form { 30 padding: 1.5rem; 31 -ms-flex-preferred-size: 100vw; 32 flex-basis: 100vw; 26 padding: 2rem; 27 display: flex; 28 flex-direction: column; /* Stack form elements vertically */ 29 align-items: center; /* Center form elements horizontally */ 30 justify-content: center; /* Vertically center the form */ 31 background-color: #fff; 32 border-radius: 8px; /* Rounded corners for the form */ 33 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Add subtle shadow to form */ 34 width: 100%; 35 max-width: 400px; /* Restrict the form width for better appearance */ 33 36 } 34 37 35 38 .form .heading { 36 font-size: 1. 5rem;39 font-size: 1.8rem; 37 40 font-weight: bold; 38 41 text-align: center; 42 margin-bottom: 1.5rem; /* Spacing between heading and form */ 39 43 } 40 44 41 45 .or { 42 margin: 1rem 0; 46 margin-top: 1em; 47 text-align: center; 48 font-weight: bold; 49 color: #666; 43 50 } 44 51 … … 46 53 display: block; 47 54 margin: 1.25rem 0 1rem 0; 55 font-size: 0.95rem; 56 color: #333; 48 57 } 49 58 … … 51 60 height: 40px; 52 61 width: 100%; 62 max-width: 350px; /* Slightly larger max-width */ 53 63 padding: 15px; 54 64 background-color: #f1f9ff; 55 65 border: 2px solid #bce0fd; 56 66 border-radius: 8px; 67 box-sizing: border-box; 68 font-size: 1rem; /* Make input text a bit larger */ 57 69 } 58 70 … … 60 72 height: 40px; 61 73 width: 100%; 74 max-width: 350px; 75 margin-top: 1.5rem; 62 76 background-color: #258de6; 63 77 color: white; … … 65 79 letter-spacing: 1px; 66 80 border: none; 67 display: block; 68 margin: 0 auto; 69 font-weight: bold; 70 margin-top: 1.5rem; 81 font-weight: bold; 71 82 border-radius: 8px; 83 font-size: 1rem; /* Increase font size for better readability */ 84 } 85 86 .socialButtons { 87 display: flex; 88 flex-direction: column; /* Stack social buttons vertically */ 89 align-items: center; /* Center them horizontally */ 90 } 91 92 .socialButton { 93 display: inline-flex; /* Align text and image inline */ 94 align-items: center; /* Vertically center content */ 95 justify-content: flex-start; /* Align items to the left */ 96 padding: 10px 15px; 97 border: none; 98 border-radius: 8px; 99 cursor: pointer; 100 font-size: 0.8rem; 101 font-weight: bold; 102 transition: background-color 0.2s, transform 0.2s; 103 width: 100%; 104 max-width: 350px; /* Limit max width */ 105 } 106 107 .socialButton img { 108 width: 20px; 109 height: 20px; 110 margin-right: 10px; 111 vertical-align: middle; 112 } 113 114 .socialButton:hover { 115 filter: brightness(95%); 116 } 117 118 .socialButton:active { 119 transform: scale(0.98); 120 } 121 122 .socialButton:nth-child(2) { 123 background-color: #333; /* GitHub button */ 124 color: white; 125 } 126 127 .socialButton:nth-child(1) { 128 background-color: #ffffff; /* Google button */ 129 color: #000000; 130 box-shadow: 1px 2px #2f2525; 72 131 } 73 132 74 133 @media (min-width: 542px) { 75 134 body { 76 display: -webkit-box;77 display: -ms-flexbox;78 135 display: flex; 79 -webkit-box-pack: center;80 -ms-flex-pack: center;81 136 justify-content: center; 82 137 } 138 83 139 .wrapper { 84 display: -webkit-box;85 display: -ms-flexbox;86 140 display: flex; 87 141 height: 100vh; 88 -webkit-box-align: center;89 -ms-flex-align: center;90 142 align-items: center; 91 -ms-flex-pack: distribute; 92 justify-content: space-around; 93 padding: 1.5rem; 143 justify-content: flex-end; /* Keep the form to the right */ 144 padding: 2rem; 94 145 max-width: 1100px; 95 146 } 147 96 148 .form { 97 -ms-flex-preferred-size: auto;98 149 flex-basis: auto; 99 } 150 align-items: center; /* Center form in larger screens */ 151 width: 100%; 152 max-width: 400px; /* Keep form max width consistent */ 153 } 154 100 155 .form input { 101 width: 250px; 102 } 156 width: 100%; /* Ensures the input expands fully */ 157 max-width: 350px; 158 } 159 160 .illustration{ 161 margin-right: 40%; 162 } 163 103 164 .illustration img { 104 max-width: 80%;165 max-width: 100%; 105 166 height: auto; 106 167 } … … 108 169 109 170 @media (max-width: 680px) { 171 .wrapper { 172 flex-direction: column; /* Stack form and illustration vertically */ 173 align-items: center; /* Center content horizontally */ 174 padding: 1rem; 175 } 176 110 177 .illustration { 111 display: none; 112 } 113 } 114 115 .signUp .illustration { 116 order: 2; 117 justify-self: flex-end; 118 margin-left: 2rem; 178 display: none; /* Hide the illustration on mobile */ 179 } 180 181 .form { 182 width: 100%; /* Full width for mobile */ 183 align-items: center; /* Center form elements horizontally */ 184 } 185 186 .form input, 187 .form button, 188 .socialButton { 189 width: 100%; /* Full width for mobile */ 190 max-width: 300px; /* Optional: Limit max width */ 191 } 119 192 } 120 193 … … 126 199 transform: scale(0.98); 127 200 } 201 202 .error { 203 color: red; 204 font-size: 0.9rem; 205 margin-top: 0.5rem; 206 } -
imaps-frontend/src/pages/MapView/MapView.jsx
r0c6b92a r79a0317 14 14 import ShapeRegistry from "../../scripts/util/ShapeRegistry.js"; 15 15 import {useAppContext} from "../../components/AppContext/AppContext.jsx"; 16 import Report from "../../components/Report/Report.jsx"; 17 import {Button} from "../IMaps/components/Button.jsx"; 16 18 17 19 const MapView = ({isPrivate}) => { 18 20 const {mapName} = useParams(); 19 21 const {username} = useAppContext(); 22 const {isAuthenticated} = useAppContext(); 20 23 21 24 const [mapLoaded, setMapLoaded] = useState(false); … … 28 31 const [searchParams, setSearchParams] = useSearchParams(); 29 32 const [mainEntrance, setMainEntrance] = useState({}); 33 const [canDisplayNavDownload,setCanDisplayNavDownload] = useState(false); 34 const [from,setFrom] = useState(""); 35 const [to,setTo] = useState(""); 30 36 31 37 const defaultNavObj = { … … 115 121 setFloors(respFloors); 116 122 123 console.log("FLOOR DATA: " + tlFloor?.mapData) 124 117 125 appInstance.loadMapN(tlFloor?.mapData) 118 126 setApp(appInstance); … … 163 171 const toEncoded = encodeURIComponent(toSearch).trimEnd() 164 172 173 setFrom(fromSearch); 174 setTo(toSearch); 175 165 176 httpService.get(`${config.view_maps.navigate}?from=${fromEncoded}&to=${toEncoded}`).then(path => { 166 177 app.drawRouteNEW(path); 178 179 167 180 }).catch(reason => { 168 181 console.log("err", reason) … … 185 198 } 186 199 200 useEffect(() => { 201 const handleNavigateEnd = (event) => { 202 console.log("DETAIL END",event.detail) 203 setCanDisplayNavDownload(true); 204 } 205 206 window.addEventListener("navend",handleNavigateEnd) 207 return () => { 208 window.removeEventListener("navend",handleNavigateEnd) 209 } 210 }, [app]); 211 187 212 188 213 useEffect(() => { … … 216 241 app.loadMapN(chFloor.mapData) 217 242 app.floorNum = floorNum; 218 219 243 220 244 console.log(`Floor changed to: ${floorNum}`); … … 257 281 handleFloorChange={handleFloorChange} 258 282 /> 283 {canDisplayNavDownload && 284 (<div className={styles.downloadRouteButton}> 285 <button onClick={() => { 286 app.getRouteImages({ 287 mapName: mapName, 288 from: from, 289 to: to 290 }) 291 setCanDisplayNavDownload(false) 292 }}> Download Route</button> 293 </div>) } 259 294 <FilterBar map={app} roomTypes={roomTypes}/> 260 295 </div> 261 296 )} 297 298 {isAuthenticated && <Report mapName = {mapName}></Report>} 262 299 <div className={styles.profileContainer}> 263 300 <Profile position="relative"/> … … 265 302 </div> 266 303 </div> 304 <div id="temp"></div> 267 305 268 306 <div className={styles.floorSelectorContainer}> … … 283 321 </div> 284 322 323 285 324 </div> 286 325 ); -
imaps-frontend/src/pages/MyMaps/CreateMaps.module.css
r0c6b92a r79a0317 55 55 margin-bottom: 30px; 56 56 text-align: center; 57 margin-top: 5em; 58 57 59 } 58 60 59 61 .searchBar input { 60 width: 300px; 62 /*width: 300px;*/ 63 width: 33vw; 61 64 padding: 10px; 62 65 font-size: 16px; … … 83 86 display: flex; 84 87 flex-direction: column; 85 align-items: center; /* Center items horizontally */86 justify-content: flex-start; /* Ensure tiles stack from the top */88 align-items: center; 89 justify-content: flex-start; 87 90 padding: 10px; 91 /*padding-left: 2.5em;*/ 88 92 background-color: #f9f9f9; 89 93 border-radius: 8px; 90 94 box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); 91 } 95 96 } 97 92 98 93 99 .tile { … … 127 133 } 128 134 135 .plusButton { 136 width: 50px; 137 height: 50px; 138 border: none; 139 cursor: pointer; 140 display: flex; 141 align-items: center; 142 justify-content: center; 143 transition: transform 0.3s ease-in-out; 144 background: #4ebc3b; 145 margin-right: 20px; 146 } 147 148 .plusButton img { 149 width: 50px; 150 height: auto; 151 margin-left: 0; 152 } 153 154 .plusButton:hover { 155 transform: scale(1.1); 156 background: #46be2f; 157 } 158 159 .header { 160 display: flex; 161 align-items: center; 162 justify-content: space-between; 163 background-color: #f8f9fa; /* Light grey background for a clean look */ 164 border-bottom: 1px solid #ddd; /* Subtle separation from the rest of the page */ 165 padding: 10px 20px; /* Add some space around the content */ 166 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* Light shadow for subtle elevation */ 167 } 168 169 .header .logo-wrapper, 170 .header .profile-wrapper { 171 display: flex; 172 align-items: center; 173 } 174 175 .header h1 { 176 font-size: 1.5rem; /* Slightly larger font size */ 177 color: #333; /* Dark grey for a professional tone */ 178 margin: 0 auto; /* Center-align the title */ 179 font-weight: 600; /* Semi-bold text for emphasis */ 180 text-align: center; 181 flex-grow: 1; /* Allow it to take space in the middle */ 182 } 183 184 .logo-wrapper { 185 margin-right: auto; /* Push logo to the far left */ 186 } 187 188 .profile-wrapper { 189 margin-left: auto; /* Push profile to the far right */ 190 } 191 192 193 194 129 195 @media (max-width: 768px) { 130 196 .mapsContainer { -
imaps-frontend/src/pages/MyMaps/MyMaps.jsx
r0c6b92a r79a0317 10 10 import {useAppContext} from "../../components/AppContext/AppContext.jsx"; 11 11 import config from "../../scripts/net/netconfig.js"; 12 import {element} from "prop-types";13 12 import Toast from "../../components/Toast/Toast.jsx"; 13 import plus_icon from "../../assets/plus_icon.png"; 14 14 15 15 const renderTile = ({data, isDragging}, openMapInfo) => ( … … 65 65 setToastMessage(message); 66 66 setToastType(type); 67 setTimeout(() => setToastMessage(null), 3000); // Automatically hide the toast after 3 seconds 68 }; 69 67 setTimeout(() => setToastMessage(null), 3000); 68 }; 70 69 71 70 const handleUpdate = async (updatedMap) => { 72 // Placeholder for map update logic 71 try { 72 let httpService = new HttpService(true); 73 const response = await httpService.post( 74 config.my_maps.edit_map_info, 75 updatedMap 76 ); 77 closeMapInfoModal() 78 window.location.reload(); 79 80 } catch (error) { 81 showToast("Map Name already taken", 0) 82 closeMapInfoModal() 83 } 73 84 }; 74 85 … … 83 94 setTiles((prevTiles) => prevTiles.filter((tile) => tile.mapName !== mapName)); 84 95 setAllTiles((prevTiles) => prevTiles.filter((tile) => tile.mapName !== mapName)); 85 showToast("Map deleted", 1) 96 showToast("Map deleted", 1); 86 97 }) 87 98 .catch((error) => { 88 99 const errorMessage = error.response?.data?.error || error.message || "Unknown error"; 89 // alert(`Error deleting the map: ${errorMessage}`); 90 showToast(`Error deleting the map: ${errorMessage}`, 0) 100 showToast(`Error deleting the map: ${errorMessage}`, 0); 91 101 }); 92 102 }; … … 97 107 98 108 httpService 99 .put(`${config.my_maps.add}?username=${ username}`, mapDetails)109 .put(`${config.my_maps.add}?username=${encodeURI(username)}`, mapDetails) 100 110 .then((respMap) => { 101 console.log("RESP NEW MAP: " + respMap)102 111 const mapTile = { 103 112 mapName: respMap.mapName, … … 108 117 modified_at: respMap.modifiedAt, 109 118 published_at: respMap.published_at, 110 gmaps_url: respMap.gmaps _url,119 gmaps_url: respMap.gmapsUrl, 111 120 image_url: card, 112 121 is_published: respMap.is_published, 122 mapType: respMap.mapType 113 123 }; 114 124 … … 116 126 setTiles((prevTiles) => [...prevTiles, mapTile]); 117 127 showToast("Map added successfully!"); 118 119 120 128 }) 121 129 .catch((error) => { 122 showToast("Map name already taken", 0) 130 showToast("Map name already taken", 0); 123 131 }); 124 132 }; … … 129 137 httpService.setAuthenticated(); 130 138 131 const respMaps = await httpService.get(`${config.my_maps.display}?username=${username}`); 139 const respMaps = await httpService.get( 140 `${config.my_maps.display}?username=${encodeURI(username)}` 141 ); 132 142 133 143 const mapTiles = respMaps.map((elem) => ({ … … 139 149 modified_at: elem.modifiedAt, 140 150 published_at: elem.published_at, 141 gmaps_url: elem.g MapsUrl,151 gmaps_url: elem.gmapsUrl, 142 152 image_url: card, 143 153 numFavourites: elem.numFavourites, 144 154 })); 145 146 155 147 156 setTiles(mapTiles); … … 154 163 setPublicMaps(tiles.filter((tile) => tile.status === "PUBLIC")); 155 164 setPrivateMaps(tiles.filter((tile) => tile.status === "PRIVATE")); 156 setPendingMaps(tiles.filter((tile) => tile.status === " INVALID"));165 setPendingMaps(tiles.filter((tile) => tile.status === "PENDING")); 157 166 }, [tiles]); 158 167 … … 164 173 return ( 165 174 <div className={styles.container}> 166 <Logo/> 167 <Profile/> 175 176 <div className={`${styles.logoWrapper}`}> 177 <Logo/> 178 </div> 168 179 <h1>Your Maps</h1> 169 170 <div className={styles.actionButtons}> 171 <button className={styles.createMapsButton} onClick={openCreateModal}> 172 Create Map 173 </button> 174 </div> 180 <div className={`${styles.profileWrapper}`}> 181 <Profile/> 182 </div> 183 175 184 176 185 <div className={styles.searchBar}> 177 <input type="text" placeholder="Search for maps..." onChange={handleSearch}/> 186 <input 187 type="text" 188 placeholder="Search for maps..." 189 onChange={handleSearch} 190 /> 178 191 </div> 179 192 … … 201 214 forceTileHeight={170} 202 215 /> 216 <button className={styles.plusButton} onClick={openCreateModal}> 217 <img src={plus_icon} alt="Add Map"/> 218 </button> 203 219 </div> 204 220 … … 222 238 onDelete={deleteMap} 223 239 onUpdate={handleUpdate} 224 onPublish={() => { 240 onPublish={(updatedMap = null) => { 241 242 const updatedTile = { 243 mapName: updatedMap.mapName, 244 cols: 1, 245 rows: 1, 246 status: updatedMap.mapStatus, 247 created_at: updatedMap.createdAt, 248 modified_at: updatedMap.modifiedAt, 249 published_at: updatedMap.published_at, 250 gmaps_url: updatedMap.gMapsUrl, 251 image_url: card, 252 numFavourites: updatedMap.numFavourites, 253 } 225 254 showToast(`Map ${selectedMap.mapName} published successfully!`); 226 255 setPrivateMaps((prevMaps) => prevMaps.filter(m => m.mapName !== selectedMap.mapName)) 227 setPendingMaps((prevMaps) => [...prevMaps, allTiles.find(m => m.mapName = selectedMap.mapName)])256 setPendingMaps((prevMaps) => [...prevMaps, updatedTile]) 228 257 closeMapInfoModal() 229 258 }} … … 236 265 /> 237 266 238 {toastMessage && <Toast message={toastMessage} type={toastType} onClose={() => setToastMessage(null)}/>} 267 {toastMessage && ( 268 <Toast 269 message={toastMessage} 270 type={toastType} 271 onClose={() => setToastMessage(null)} 272 /> 273 )} 239 274 </div> 240 275 ); -
imaps-frontend/src/pages/Signup/Signup.jsx
r0c6b92a r79a0317 4 4 import styles from "./Signup.module.css"; 5 5 import Logo from "../../components/Logo/Logo"; 6 import netconfig from "../../scripts/net/netconfig.js"; 6 7 7 8 export default function Signup() { … … 9 10 const [email, setEmail] = useState(""); 10 11 const [password, setPassword] = useState(""); 12 const [confirmPassword, setConfirmPassword] = useState(""); 11 13 const [message, setMessage] = useState(""); 12 const [messageType, setMessageType] = useState(""); // New state to manage message type14 const [messageType, setMessageType] = useState(""); 13 15 const navigate = useNavigate(); 14 16 15 17 const handleSubmit = async (e) => { 16 18 e.preventDefault(); 19 20 if (password !== confirmPassword) { 21 setMessageType("error"); 22 setMessage("Passwords do not match."); 23 return; 24 } 17 25 18 26 const payload = { … … 23 31 24 32 try { 25 const response = await fetch( "http://localhost:8080/api/auth/register", {33 const response = await fetch(netconfig.auth.register, { 26 34 method: "POST", 27 35 headers: { … … 35 43 setMessage("User registered successfully!"); 36 44 37 // Wait 3 seconds and then redirect to login page38 45 setTimeout(() => { 39 46 navigate("/login"); 40 }, 3000);47 }, 1000); 41 48 } else if (response.status === 409) { 42 49 setMessageType("error"); … … 54 61 55 62 return ( 56 <div className={styles.wrapper}> 57 <Logo></Logo> 58 <div className={styles.illustration}> 59 <img src={illustration} alt="illustration" /> 63 <div className={styles.wrapper}> 64 <Logo></Logo> 65 <div className={styles.illustration}> 66 <img src={illustration} alt="illustration" /> 67 </div> 68 <div className={styles.form}> 69 <div className={styles.heading}>CREATE AN ACCOUNT</div> 70 <form onSubmit={handleSubmit}> 71 <div> 72 <label htmlFor="name">Username</label> 73 <input 74 type="text" 75 id="name" 76 value={name} 77 onChange={(e) => setName(e.target.value)} 78 placeholder="Enter your username" 79 required 80 /> 81 </div> 82 <div> 83 <label htmlFor="email">E-Mail</label> 84 <input 85 type="email" 86 id="email" 87 value={email} 88 onChange={(e) => setEmail(e.target.value)} 89 placeholder="Enter your email" 90 required 91 /> 92 </div> 93 <div> 94 <label htmlFor="password">Password</label> 95 <input 96 type="password" 97 id="password" 98 value={password} 99 onChange={(e) => setPassword(e.target.value)} 100 placeholder="Enter your password" 101 required 102 /> 103 </div> 104 <div> 105 <label htmlFor="confirmPassword">Confirm Password</label> 106 <input 107 type="password" 108 id="confirmPassword" 109 value={confirmPassword} 110 onChange={(e) => setConfirmPassword(e.target.value)} 111 placeholder="Confirm your password" 112 required 113 /> 114 </div> 115 <button type="submit">Submit</button> 116 {message && ( 117 <p className={messageType === "success" ? styles.successMessage : styles.errorMessage}> 118 {message} 119 </p> 120 )} 121 </form> 122 <p> 123 Have an account? <Link to="/Login"> Login </Link> 124 </p> 125 </div> 60 126 </div> 61 <div className={styles.form}>62 <div className={styles.heading}>CREATE AN ACCOUNT</div>63 <form onSubmit={handleSubmit}>64 <div>65 <label htmlFor="name">Username</label>66 <input67 type="text"68 id="name"69 value={name}70 onChange={(e) => setName(e.target.value)}71 placeholder="Enter your username"72 required73 />74 </div>75 <div>76 <label htmlFor="email">E-Mail</label>77 <input78 type="email"79 id="email"80 value={email}81 onChange={(e) => setEmail(e.target.value)}82 placeholder="Enter your email"83 required84 />85 </div>86 <div>87 <label htmlFor="password">Password</label>88 <input89 type="password"90 id="password"91 value={password}92 onChange={(e) => setPassword(e.target.value)}93 placeholder="Enter your password"94 required95 />96 </div>97 <button type="submit">Submit</button>98 99 {/* Display message with appropriate styling */}100 {message && (101 <p className={messageType === "success" ? styles.successMessage : styles.errorMessage}>102 {message}103 </p>104 )}105 <h2 align="center" className={styles.or}>106 OR107 </h2>108 </form>109 <p>110 Have an account? <Link to="/Login"> Login </Link>111 </p>112 </div>113 </div>114 127 ); 115 128 } -
imaps-frontend/src/pages/Signup/Signup.module.css
r0c6b92a r79a0317 1 * {2 margin: 0;3 padding: 0;4 -webkit-box-sizing: border-box;5 box-sizing: border-box;6 font-family: sans-serif;7 }8 9 .illustration img {10 max-height: 500px;11 width: auto;12 }13 14 1 .wrapper { 15 display: -webkit-box;16 display: -ms-flexbox;17 2 display: flex; 18 height: 100vh; 19 -webkit-box-align: center; 20 -ms-flex-align: center; 21 align-items: center; 22 } 23 24 .wrapper p { 25 font-size: 0.85rem; 26 margin-top: 1rem; 3 align-items: center; /* Vertically center content */ 4 justify-content: center; /* Horizontally center content */ 5 height: 100vh; /* Full viewport height */ 6 background-color: #ffffff; /* Light background */ 7 padding: 1rem; 8 gap: 2rem; /* Space between form and image */ 27 9 } 28 10 29 11 .form { 30 padding: 1.5rem; 31 -ms-flex-preferred-size: 100vw; 32 flex-basis: 100vw; 12 padding: 2rem; 13 background-color: white; 14 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); 15 border-radius: 10px; 16 flex-basis: 100%; 17 max-width: 400px; /* Restrict form width */ 18 text-align: center; /* Center text inside the form */ 33 19 } 34 20 35 21 .form .heading { 36 font-size: 1. 5rem;22 font-size: 1.8rem; 37 23 font-weight: bold; 38 text-align: center; 39 } 40 41 .or { 42 margin: 1rem 0; 24 color: #258de6; 25 margin-bottom: 1rem; 43 26 } 44 27 45 28 .form label { 46 29 display: block; 47 margin: 1.25rem 0 1rem 0; 30 margin: 1.25rem 0 0.5rem 0; 31 font-size: 0.9rem; 32 font-weight: bold; 33 color: #555; 48 34 } 49 35 … … 51 37 height: 40px; 52 38 width: 100%; 53 padding: 1 5px;39 padding: 10px 15px; 54 40 background-color: #f1f9ff; 55 41 border: 2px solid #bce0fd; 56 42 border-radius: 8px; 43 font-size: 1rem; 44 } 45 46 .form input:focus { 47 outline: none; 48 border-color: #258de6; 49 background-color: #e6f4ff; 57 50 } 58 51 59 52 .form button { 60 height: 4 0px;53 height: 45px; 61 54 width: 100%; 62 55 background-color: #258de6; … … 65 58 letter-spacing: 1px; 66 59 border: none; 60 margin-top: 1.5rem; 61 font-weight: bold; 62 border-radius: 8px; 63 cursor: pointer; 64 transition: all 0.2s ease; 65 } 66 67 .form button:hover { 68 filter: brightness(90%); 69 } 70 71 .form button:active { 72 transform: scale(0.96); 73 } 74 75 .form p { 76 text-align: center; 77 margin-top: 1rem; 78 font-size: 0.9rem; 79 } 80 81 .form p a { 82 color: #258de6; 83 text-decoration: none; 84 font-weight: bold; 85 } 86 87 .form p a:hover { 88 text-decoration: underline; 89 } 90 91 .illustration { 92 text-align: center; /* Center image container */ 93 } 94 95 .illustration img { 96 max-width: 100%; 97 height: auto; 98 border-radius: 10px; 67 99 display: block; 68 margin: 0 auto; 100 margin: 0 auto; /* Center the image */ 101 } 102 103 @media (min-width: 768px) { 104 .wrapper { 105 flex-direction: row; 106 gap: 4rem; /* Space between form and image */ 107 } 108 109 .form { 110 flex-basis: 50%; 111 } 112 113 .illustration { 114 flex-basis: 50%; 115 } 116 117 .illustration img { 118 max-height: 600px; 119 width: auto; 120 } 121 } 122 123 @media (max-width: 768px) { 124 .wrapper { 125 flex-direction: column; 126 text-align: center; /* Center all content in column layout */ 127 } 128 129 .illustration { 130 margin-bottom: 2rem; /* Space between image and form */ 131 } 132 } 133 134 .errorMessage, 135 .successMessage { 136 font-size: 0.9rem; 69 137 font-weight: bold; 70 margin-top: 1.5rem; 138 text-align: center; 139 margin-top: 1rem; 140 padding: 10px; 71 141 border-radius: 8px; 72 142 } 73 143 74 @media (min-width: 542px) { 75 body { 76 display: -webkit-box; 77 display: -ms-flexbox; 78 display: flex; 79 -webkit-box-pack: center; 80 -ms-flex-pack: center; 81 justify-content: center; 82 } 83 .wrapper { 84 display: -webkit-box; 85 display: -ms-flexbox; 86 display: flex; 87 height: 100vh; 88 -webkit-box-align: center; 89 -ms-flex-align: center; 90 align-items: center; 91 -ms-flex-pack: distribute; 92 justify-content: space-around; 93 padding: 1.5rem; 94 max-width: 1100px; 95 } 96 .form { 97 -ms-flex-preferred-size: auto; 98 flex-basis: auto; 99 } 100 .form input { 101 width: 250px; 102 } 103 .illustration img { 104 max-width: 80%; 105 height: auto; 106 } 144 .errorMessage { 145 color: #e74c3c; 146 background-color: #fbeaea; 147 border: 1px solid #e74c3c; 107 148 } 108 149 109 @media (max-width: 680px){110 .illustration {111 display: none;112 }150 .successMessage { 151 color: #2ecc71; 152 background-color: #ebf9f1; 153 border: 1px solid #2ecc71; 113 154 } 114 115 .signUp .illustration {116 order: 2;117 justify-self: flex-end;118 margin-left: 2rem;119 }120 121 button:hover {122 filter: brightness(95%);123 }124 125 button:active {126 transform: scale(0.98);127 }128 129 /* Error message style */130 .errorMessage {131 color: #e74c3c; /* Red color for error */132 background-color: #fbeaea;133 padding: 10px;134 border: 1px solid #e74c3c;135 border-radius: 5px;136 margin-top: 1rem;137 text-align: center;138 font-size: 0.9rem;139 font-weight: bold;140 }141 142 /* Success message style */143 .successMessage {144 color: #2ecc71; /* Green color for success */145 background-color: #ebf9f1;146 padding: 10px;147 border: 1px solid #2ecc71;148 border-radius: 5px;149 margin-top: 1rem;150 text-align: center;151 font-size: 0.9rem;152 font-weight: bold;153 } -
imaps-frontend/src/scripts/base/MapShape.js
r0c6b92a r79a0317 7 7 } 8 8 9 console.log(config.x,"FFF");10 9 super(config); 11 10 this.layer = layer; … … 64 63 text: this._info.name, 65 64 fontSize: 12, 66 fontFamily: ' Verdana',65 fontFamily: 'Exo', 67 66 fill: 'white', 68 67 }); … … 134 133 } 135 134 135 onPlace(){ 136 //this.snapToGrid(); 137 } 138 136 139 137 140 get info() { -
imaps-frontend/src/scripts/main/MapBuilder.js
r0c6b92a r79a0317 21 21 22 22 // TODO AKO DRAGNIT NEKOJ OD POCETOK NA STAGE POZICIIVE KE SA ZEZNAT 23 // TODO jwt vo cookie24 23 // TODO placed shape i mouseMoveHandler da ne callback ( da ne vrakjat funkcija) 25 24 // TODO text na top layer sekogas … … 35 34 this.textLayer = new Konva.Layer(); 36 35 this.gridLayer.listening(false); 36 37 this.isDrawEventBound = false; 37 38 38 39 … … 96 97 setupEventListeners() { 97 98 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));99 // window.addEventListener("keydown", this.handleExitSelection.bind(this)); 100 // window.addEventListener("keydown", this.handleDelete.bind(this)); 101 // window.addEventListener("keydown", this.rotateShapesBy90Deg.bind(this)); 102 // window.addEventListener("keydown", this.toggleEfficientDrawingMode.bind(this)); 102 103 window.addEventListener("resize", this.handleResize.bind(this)); 103 104 … … 107 108 this.boundEfficientDrawingModeEventListener = this.toggleEfficientDrawingMode.bind(this); 108 109 109 //this.attachKeyPressEventListeners();110 this.attachKeyPressEventListeners(); 110 111 111 112 this.stage.on("mousedown touchstart", this.handleMouseDown.bind(this)); … … 119 120 120 121 detachKeyPressEventListeners() { 122 console.log("DETACH") 121 123 window.removeEventListener("keydown", this.boundEscapeEventListener); 122 124 window.removeEventListener("keydown", this.boundDeleteEventListener); … … 126 128 127 129 attachKeyPressEventListeners() { 130 console.log("ATTACH") 128 131 window.addEventListener("keydown", this.boundEscapeEventListener); 129 132 window.addEventListener("keydown", this.boundDeleteEventListener); … … 269 272 let infoPin = Factory.createShape("InfoPin", attrs); 270 273 addEventHandling(infoPin, this, "dblclick"); 271 //this.shapes.push(infoPin);272 274 ShapeRegistry.add(infoPin) 273 275 this.mainLayer.add(infoPin); … … 307 309 308 310 const placedObj = Factory.createShape(this.hoverObj.type, attrs); 311 309 312 if (!placedObj) return; 310 313 … … 312 315 313 316 this.mainLayer.add(placedObj); 314 //this.shapes.push(placedObj);315 console.log("VO PLACED SHAEPS WALL ZITI SE: " + placedObj.className);316 317 ShapeRegistry.add(placedObj); 317 318 addEventHandling(placedObj, this, "dblclick"); 318 319 this.mainLayer.draw(); 319 320 320 // site ovie func da se vo edna funkcija vo shape. 321 321 322 322 placedObj.displayName(this.textLayer); 323 323 placedObj.snapToGrid(); 324 placedObj.onPlace(); 324 325 325 326 triggerMapSave(); … … 328 329 this.stopDrawing(); 329 330 } 331 this.mainTransformer.nodes([]) 330 332 } 331 333 332 334 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); 335 if(this.isDrawEventBound){ 336 this.mainTransformer.nodes([]); 337 if (this.hoverObj != null) this.hoverObj.remove(); 338 this.dragLayer.removeChildren(); 339 this.stage.off("mousemove", this.boundMouseMoveHandler); 340 this.stage.off("click", this.boundPlaceShapeHandler); 341 this.isDrawEventBound = false; 342 } 343 338 344 } 339 345 … … 365 371 this.dragLayer.add(this.hoverObj); 366 372 this.dragLayer.moveToTop(); 373 374 367 375 this.boundMouseMoveHandler = this.mouseMoveHandler.bind(this); 368 376 this.boundPlaceShapeHandler = this.placeShape.bind(this); 377 this.isDrawEventBound = true; 369 378 370 379 this.stage.on("mousemove", this.boundMouseMoveHandler); … … 374 383 selectShape(e) { 375 384 if (e.target.tagName === "LI") { 385 this.stopDrawing() 376 386 const shapeType = e.target.getAttribute("data-info"); 377 387 this.startDrawing(shapeType); 378 this.mainTransformer.nodes([]) ;388 this.mainTransformer.nodes([]) 379 389 } 380 390 } … … 464 474 465 475 saveShapeDetails() { 466 // this.shapes.forEach(shape => {467 // shape.saveShapeDetails();468 // console.log(shape.info);469 // });470 476 ShapeRegistry.saveDetails(); 471 477 console.log("thisflornum",this.floorNum) … … 478 484 } 479 485 480 getPayload(){481 this.saveShapeDetails();482 return {483 shapes: ShapeRegistry.getShapes(this.floorNum),484 roomTypes: JSON.stringify(this.roomTypes),485 mapName: this.mapName,486 floorNum: this.floorNum487 }488 }489 486 490 487 491 488 handleStageClick(e) { 492 489 if (this.selectionRectangle.visible()) { 490 console.log("STAGECLICK1") 493 491 return; 494 492 } 495 493 496 494 if (e.target === this.stage) { 495 console.log("STAGECLICK2") 497 496 this.mainTransformer.nodes([]); 498 497 return; … … 500 499 501 500 if (!e.target.hasName("mapObj")) { 501 console.log("STAGECLICK3") 502 502 return; 503 503 } … … 505 505 const metaPressed = e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey; 506 506 const isSelected = this.mainTransformer.nodes().indexOf(e.target) >= 0; 507 507 508 508 509 if (!metaPressed && !isSelected) { … … 527 528 } 528 529 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 530 552 531 drawConnection(node1Name, node2Name) { 553 532 554 533 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 534 } 560 535 … … 571 546 } 572 547 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 548 586 549 clearMap() { … … 653 616 this.mainLayer.add(this.selectionRectangle); 654 617 655 ShapeRegistry.getShapes(this.floorNum).forEach((shape) => shape.displayName(this.textLayer)); 618 ShapeRegistry.getShapes(this.floorNum).forEach((shape) => { 619 shape.displayName(this.textLayer); 620 if(shape.className === "Entrance"){ 621 shape.setHighlight(); 622 } 623 }); 656 624 } 657 625 -
imaps-frontend/src/scripts/main/MapDisplay.js
r0c6b92a r79a0317 4 4 import {zoomStage} from "../util/zoomStage.js"; 5 5 import {addEventHandling} from "../util/addEventHandling.js"; 6 import ShapeRegistry from "../util/ShapeRegistry.js"; 6 7 import triggerNavigate from "../util/triggerNavigate.js"; 7 8 import config from "../net/netconfig.js"; 9 import {dispatchCustomEvent} from "../util/dispatchCustomEvent.js"; 10 import { jsPDF } from "jspdf"; 11 8 12 9 13 export class MapDisplay { … … 18 22 }); 19 23 24 this.selectedRoom = { 25 id: 1, 26 name: "223", 27 type: "Laboratory", 28 image: 29 "data:image/jpeg;base64,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", 30 description: "223 is a laboratory with a capacity of 40 people", 31 }; 32 this.toggleSearch = false; 33 20 34 this.shapes = []; 21 35 this.roomTypes = []; … … 28 42 this.stage.add(this.textLayer); 29 43 44 45 this.cachedCanvases = []; 46 30 47 this.floorNum = floorNum; 31 48 … … 64 81 dsrData.forEach((shape) => { 65 82 if (shape.className !== "InfoPin") { 83 console.log("SHAPE ATTRS CREATE: " + JSON.stringify(shape.attrs)); 66 84 const renderedShape = Factory.createRenderedShape(shape.className, shape.attrs); 67 85 addEventHandling(renderedShape, this, "click"); 68 86 this.shapes.push(renderedShape); 87 console.log(renderedShape) 69 88 } 70 89 }); … … 78 97 } 79 98 80 81 82 99 loadMapN(floorData) { 83 100 if (floorData == null || floorData === "") return; … … 97 114 98 115 116 99 117 drawRouteNEW(nodes, offset = 0) { 100 118 … … 106 124 let buff = [nodes[idx].coordinates.x, nodes[idx].coordinates.y]; 107 125 108 109 126 ++idx; 110 127 … … 115 132 116 133 if (idx >= nodes.length){ 134 console.log("FLOOR NUM DISPLAY: " + this.floorNum) 135 let stageInfo = {stage:this.stage.clone(),floor:this.floorNum}; 136 this.cachedCanvases.push(stageInfo); 137 dispatchCustomEvent("navend",this.downloadURI) 117 138 return; 118 139 } … … 122 143 123 144 if (nextNode.floorNumber !== currentNode.floorNumber) { 145 let stageInfo = {stage:this.stage.clone(),floor:this.floorNum}; 146 this.cachedCanvases.push(stageInfo); 124 147 triggerNavigate(nodes, idx, nextNode.floorNumber, nextNode); 125 148 return; … … 131 154 const endY = nextNode.coordinates.y; 132 155 133 const numSegments = 1 2;156 const numSegments = 10; 134 157 135 158 const deltaX = (endX - startX) / numSegments; … … 157 180 clearInterval(interval); 158 181 idx++; 159 setTimeout(drawNextSegment, 150);182 setTimeout(drawNextSegment, 60); 160 183 } 161 }, 50);184 }, 30); 162 185 }; 163 186 164 187 drawNextSegment(); 165 188 } 166 167 168 189 169 190 initializeRoomTypes() { … … 173 194 } 174 195 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 196 getShapeByType(type) { 185 197 return this.shapes.filter((shape) => shape.class === type) 186 }187 188 toggleSearchRoom() {189 this.toggleSearch = !this.toggleSearch;190 198 } 191 199 … … 196 204 } 197 205 198 getMainEntrance() { 199 return this.shapes.filter(shape => shape.class === "Entrance").filter(el => el.info.isMainEntrance === true)[0]; 200 } 201 202 setFilter(filter) { 206 downloadURI(uri, name) { 207 let link = document.createElement('a'); 208 link.download = name; 209 link.href = uri; 210 document.body.appendChild(link); 211 link.click(); 212 document.body.removeChild(link); 213 } 214 215 216 getRouteImages(mapDetails = { mapName: "mapName", from: "from", to: "to" }) { 217 218 const pdf = new jsPDF("p", "mm", "a4"); 219 const pageWidth = pdf.internal.pageSize.getWidth(); 220 const pageHeight = pdf.internal.pageSize.getHeight(); 221 const margin = 10; 222 let yOffset = margin; 223 224 const imagesData = []; 225 226 let minWidth = 800; 227 228 this.cachedCanvases.forEach((canvas, index) => { 229 console.log("CANVASL " + JSON.stringify(canvas.stage)) 230 let dsrStage = Konva.Node.create(canvas.stage, document.createElement("div")); 231 232 let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; 233 234 dsrStage.getLayers().forEach(layer => { 235 layer.getChildren().forEach(shape => { 236 const shapeX = shape.x(); 237 const shapeY = shape.y(); 238 const width = shape.width() * shape.scaleX(); 239 const height = shape.height() * shape.scaleY(); 240 241 minX = Math.min(minX, shapeX - shape.offsetX() * shape.scaleX()); 242 minY = Math.min(minY, shapeY - shape.offsetY() * shape.scaleY()); 243 maxX = Math.max(maxX, shapeX + width - shape.offsetX() * shape.scaleX()); 244 maxY = Math.max(maxY, shapeY + height - shape.offsetY() * shape.scaleY()); 245 }); 246 }); 247 248 let padding = 50; 249 250 const virtualWidth = maxX - minX + padding; 251 const virtualHeight = maxY - minY + padding; 252 253 const scaleDownFactor = Math.min(pageWidth / virtualWidth, pageHeight / virtualHeight); 254 255 dsrStage.size({ width: virtualWidth, height: virtualHeight }); 256 dsrStage.position({ x: -minX, y: -minY }); 257 dsrStage.batchDraw(); 258 259 let canvasImageURI = dsrStage.toDataURL({ 260 pixelRatio: 1 261 }); 262 263 imagesData.push(canvasImageURI); 264 console.log(`Generated Image ${index + 1}`); 265 }); 266 267 268 269 imagesData.forEach((imageURI, index) => { 270 const text = `Floor: ${this.cachedCanvases[index].floor}`; 271 pdf.text(text, margin, yOffset + 5); 272 273 const img = new Image(); 274 img.src = imageURI; 275 276 const maxWidth = pageWidth - 2 * margin; 277 const maxHeight = pageHeight - 2 * margin; 278 let imgWidth = maxWidth; 279 let imgHeight = maxHeight / 2 280 281 if (imgHeight > maxHeight) { 282 imgHeight = maxHeight; 283 imgWidth = (pageWidth * maxHeight) / pageHeight; 284 } 285 286 if (yOffset + imgHeight > pageHeight - margin) { 287 pdf.addPage(); 288 yOffset = margin; 289 } 290 291 pdf.addImage(imageURI, "PNG", margin, yOffset, imgWidth, imgHeight); 292 yOffset += imgHeight + 10; 293 }); 294 295 pdf.save(`${mapDetails.mapName}-${mapDetails.from}-->${mapDetails.to}.route.pdf`); 296 } 297 298 299 300 setFilter(filter) { 203 301 let rooms = this.getShapeByType("Room") 204 302 if (filter === "All") { -
imaps-frontend/src/scripts/net/HttpService.js
r0c6b92a r79a0317 5 5 constructor(auth = false) { 6 6 this.auth = auth; 7 this.responseType = 'json'; 7 8 } 8 9 … … 10 11 this.auth = true; 11 12 } 13 setResponseType(type){ 14 if(type === 'text'){ 15 this.responseType = 'text'; 16 } else if (type === 'json'){ 17 this.responseType = 'json' 18 } else { 19 console.error('unsupported response type in http service') 20 } 21 } 22 12 23 13 24 async request(method, endpoint, data = null) { … … 53 64 54 65 console.log("HTTPSERVICE: RESPONSE:",response); 55 56 return response.json(); 66 67 if(this.responseType === 'json'){ 68 return response.json(); 69 } else if (this.responseType === 'text'){ 70 return response.text() 71 } else{ 72 console.error('bad resp type') 73 } 57 74 } 58 75 -
imaps-frontend/src/scripts/net/netconfig.js
r0c6b92a r79a0317 14 14 publish: `${API_PROTECTED}/publish/add`, 15 15 publish_get: `${API_PROTECTED}/publish/get`, 16 edit_map_info: `${API_PROTECTED}/my-maps/edit`, 16 17 }, 17 18 room_types: { … … 34 35 navigate: `${API_PUBLIC}/navigate`, 35 36 add_favourite: `${API_PROTECTED}/favourites/add`, 37 add_report: `${API_PROTECTED}/reports/create`, 36 38 }, 37 39 favourites: { … … 43 45 login: `${API_AUTH}/login`, 44 46 register: `${API_AUTH}/register`, 45 verify: `${API_AUTH}/verify` 47 verify: `${API_AUTH}/verify`, 48 oauth: { 49 github: { 50 state: `${API_BASE_URL}/oauth/state`, 51 redirectUri : `${API_BASE_URL}/oauth/callback` 52 } 53 } 46 54 47 55 }, … … 50 58 load_pr: `${API_BASE_URL}/admin/load-pr`, 51 59 approve_pr: `${API_BASE_URL}/admin/pr/approve`, 52 deny_pr: `${API_BASE_URL}/admin/pr/deny` 60 deny_pr: `${API_BASE_URL}/admin/pr/deny`, 61 load_reports: `${API_BASE_URL}/admin/load-reports` 53 62 } 54 63 }; -
imaps-frontend/src/scripts/rendered_shapes/RenderedEntrance.js
r0c6b92a r79a0317 8 8 x: attrs.x, 9 9 y: attrs.y, 10 width: attrs.width * scaleX,11 height: attrs.height * scaleY,10 width: attrs.width, 11 height: attrs.height, 12 12 fill: "#7fef83", 13 13 stroke: "black", -
imaps-frontend/src/scripts/rendered_shapes/RenderedRoom.js
r0c6b92a r79a0317 7 7 x: attrs.x, 8 8 y: attrs.y, 9 width: attrs.width * scaleX,10 height: attrs.height * scaleY,9 width: attrs.width, 10 height: attrs.height, 11 11 fill: "#A2D9FF", 12 12 stroke: "black", -
imaps-frontend/src/scripts/rendered_shapes/RenderedStairs.js
r0c6b92a r79a0317 8 8 x: attrs.x, 9 9 y: attrs.y, 10 width: attrs.width * scaleX,11 height: attrs.height * scaleY,10 width: attrs.width , 11 height: attrs.height, 12 12 fill: "rgb(199,190,133)", 13 13 stroke: "rgb(16,15,15)", -
imaps-frontend/src/scripts/rendered_shapes/RenderedWall.js
r0c6b92a r79a0317 7 7 x: attrs.x, 8 8 y: attrs.y, 9 width: attrs.width * scaleX,10 height: attrs.height * scaleY,9 width: attrs.width, 10 height: attrs.height, 11 11 fill: '#4B4B4B', 12 12 stroke: 'black', -
imaps-frontend/src/scripts/shapes/Entrance.js
r0c6b92a r79a0317 2 2 import MapNode from "../base/MapNode.js"; 3 3 import {node} from "prop-types"; 4 import ShapeRegistry from "../util/ShapeRegistry.js"; 5 import ShapeQuery from "../util/ShapeQuery.js"; 6 import Konva from "konva"; 4 7 5 8 export default class Entrance extends MapNode { … … 14 17 x: attrs.position.x, 15 18 y: attrs.position.y, 16 width: attrs.width ,17 height: attrs.height ,19 width: attrs.width * attrs.scaleX, 20 height: attrs.height * attrs.scaleY, 18 21 fill: "rgb(126,238,167)", 19 22 stroke: "#252627", … … 46 49 this.initText(); 47 50 this.moveToTop(); 51 52 console.log("room CONNECT: " + attrs.connected_room,this.info.connectedRoom + "CON") 53 console.log("entrance: " + this.info.name) 48 54 } 49 55 50 56 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 this.info.name = attrs.obj_name ?? `Entrance${this.id} [${this.floorNum}F]`; 58 this.info.connectedRoom = attrs.connected_room ?? ``; 59 this.info.description = attrs.description ?? ``; 60 this.info.isMainEntrance = attrs.is_main_entrance ?? false; 61 this.info.selectedPins = attrs.connected_pins ?? []; 57 62 } 63 58 64 59 65 saveShapeDetails() { 60 66 console.info("fnum entrance",this.attrs.floorNum) 61 62 67 this.setAttr("connected_pins", this.info.selectedPins); 63 68 this.setAttr("obj_name", this.info.name); … … 75 80 76 81 setInfo(infoObj) { 77 console.log("SA VIKNA SETINFO")78 82 this.info = infoObj; 83 this.setHighlight(); 84 } 85 86 setHighlight(){ 87 console.log("info room: " + this.info.connectedRoom) 79 88 if(this.info.connectedRoom == null || this.info.connectedRoom === "" ){ 89 console.log("vleze if") 80 90 this.strokeWidth(2); 81 this.stroke("# a10114")91 this.stroke("#8a000d") 82 92 }else{ 83 93 this.strokeWidth(1) … … 85 95 } 86 96 } 97 98 onPlace() { 99 ShapeQuery 100 .findAllByTypeAndFloor(this.floorNum,"Room") 101 .forEach(room => { 102 if(Konva.Util.haveIntersection(room.getClientRect(),this.getClientRect())){ 103 this.info.connectedRoom = room.info.name; 104 } 105 }) 106 this.setHighlight() 107 } 87 108 } 88 109 -
imaps-frontend/src/scripts/util/Factory.js
r0c6b92a r79a0317 45 45 let scaleX = (attrs.scaleX ? parseFloat(attrs.scaleX) : 1); 46 46 let scaleY = (attrs.scaleY ? parseFloat(attrs.scaleY) : 1); 47 48 attrs.width *= scaleX; 49 attrs.height *= scaleY 50 51 console.log("SCALEX " + scaleX) 52 console.log("SCALEY " + scaleY) 47 53 switch (shapeType) { 48 54 case "Entrance":
Note:
See TracChangeset
for help on using the changeset viewer.