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

Pred finalna verzija

Location:
imaps-frontend/src/components
Files:
37 added
8 deleted
11 edited
4 moved

Legend:

Unmodified
Added
Removed
  • imaps-frontend/src/components/FilterBar/FilterBar.jsx

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

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

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

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

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

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

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

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

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

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

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

    rd565449 r0c6b92a  
    9090  top: 0;
    9191  left: 0;
    92   width: 100%;
    93   height: 100%;
     92  width: 100vw;
     93  height: 100vh;
    9494  background-color: rgba(0, 0, 0, 0.4);
    95   z-index: 900;
     95  z-index: 100;
    9696}
Note: See TracChangeset for help on using the changeset viewer.