Ignore:
Timestamp:
01/21/25 03:08:24 (5 months ago)
Author:
stefan toskovski <stefantoska84@…>
Branches:
main
Parents:
0c6b92a
Message:

F4 Finalna Verzija

Location:
imaps-frontend/src/pages
Files:
7 added
20 edited

Legend:

Unmodified
Added
Removed
  • imaps-frontend/src/pages/AdminPage/AdminPage.jsx

    r0c6b92a r79a0317  
    1111import Profile from "../../components/Profile/Profile.jsx";
    1212import Toast from "../../components/Toast/Toast.jsx";
     13import ListReports from "../../components/ListReports/ListReports.jsx";
    1314
    1415const renderTile = ({data, isDragging}, handleApprove, handleDeny, openMapInfoModal, openPublishForm) => (
     
    8283                gmaps_url: elem.gmaps_url,
    8384                image_url: card,
    84             })).filter((tile) => tile.status === "INVALID");
     85            })).filter((tile) => tile.status === "PENDING");
    8586
    8687            setPendingMaps(mapTiles);
     
    145146
    146147        try {
    147             await httpService.post(url, formData); // Assuming formData contains all required fields
     148            await httpService.post(url, formData);
    148149            setPendingMaps((prev) => prev.filter((map) => map.mapName !== formData.mapName));
    149150            alert(`Map "${formData.mapName}" published successfully.`);
     
    187188            <Profile></Profile>
    188189            <h1>Pending Maps for Approval</h1>
     190            <hr/>
    189191
    190192            {publishFormMap && (
     
    210212                />
    211213            </div>
     214            <hr/>
    212215            {isMapInfoModalOpen && (
    213216                <MapInfoModal
     
    223226            )}
    224227            {toastMessage && <Toast message={toastMessage} type={toastType} onClose={() => setToastMessage(null)}/>}
    225 
     228        <hr/>
     229            <ListReports></ListReports>
    226230        </div>
    227231    );
  • imaps-frontend/src/pages/BrowseMaps/BrowseMaps.jsx

    r0c6b92a r79a0317  
    11import styles from "./Maps.module.css";
    22import "react-tiles-dnd/esm/index.css";
    3 import { TilesContainer } from "react-tiles-dnd";
    4 import { Link } from "react-router-dom";
     3import {TilesContainer} from "react-tiles-dnd";
     4import {Link} from "react-router-dom";
    55import card from "../../assets/card-map.png";
    66import star_icon from "../../assets/star_icon.png"; // Unfilled star icon
    77import star_filled_icon from "../../assets/star_filled_icon.png"; // Filled star icon
    8 import { useEffect, useState } from "react";
     8import {useEffect, useState} from "react";
    99import HttpService from "../../scripts/net/HttpService.js";
    1010import Logo from "../../components/Logo/Logo.jsx";
    1111import Profile from "../../components/Profile/Profile.jsx";
    1212import config from "../../scripts/net/netconfig.js";
    13 import { useAppContext } from "../../components/AppContext/AppContext.jsx";
     13import {useAppContext} from "../../components/AppContext/AppContext.jsx";
     14import FilterMaps from "../../components/FilterMaps/FilterMaps.jsx";
    1415
    1516let loadedTiles = [];
    1617
    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}>
     18const renderTile = ({data, isDragging, toggleFavorite}) => (
     19    <div style={{padding: "1rem", width: "100%", position: "relative"}}>
     20        <Link to={`/Maps/View/${data.text}`} className={styles.linkStyle}>
    2021            <div
    2122                className={`${styles.tile} ${isDragging ? styles.dragging : ""}`}
    22                 style={{ width: "100%", height: "100%" }}
     23                style={{width: "100%", height: "100%"}}
    2324            >
    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"}}>
    2627                    {data.text} {isDragging ? "DRAGGING" : null}
    2728                </div>
     
    3233                src={data.isFavorite ? star_filled_icon : star_icon}
    3334                alt="Favorite Icon"
    34                 style={{ width: "20px", height: "20px" }}
     35                style={{width: "20px", height: "20px"}}
    3536            />
    3637        </div>
     
    4647    const [searchTerm, setSearchTerm] = useState("");
    4748    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([])
    4952
    5053    useEffect(() => {
     
    5255            const httpService = new HttpService();
    5356            let mapTiles = [];
    54 
     57            let mapTypes = ['Hospital', 'Faculty', 'House', 'Other'];
    5558            if (isAuthenticated) {
    5659                // :D
     
    6366                    rows: 1,
    6467                    isFavorite: true,
     68                    type: elem.mapType,
    6569                }));
    6670
     71                console.log("TUKA")
    6772                // Load all maps
    6873                const allResp = await httpService.get(config.view_maps.display);
    6974                console.log("RESPONSE MAPS PUBLIC", allResp);
     75
     76                // mapTypes = allResp.filter(elem => elem.mapType != null && elem.mapType !== "").map(elem => elem.mapType);
     77
    7078
    7179                const nonFavMapTiles = allResp
     
    7684                        rows: 1,
    7785                        isFavorite: false,
     86                        type: elem.mapType,
    7887                    }));
     88
    7989
    8090                mapTiles = [...favMapTiles, ...nonFavMapTiles];
     
    8292                const allResp = await httpService.get(config.view_maps.display);
    8393                console.log("RESPONSE MAPS PUBLIC", allResp);
     94                // mapTypes = allResp.filter(elem => elem.mapType != null && elem.mapType !== "").map(elem => elem.mapType);
    8495
    8596                mapTiles = allResp.map((elem) => ({
     
    8899                    rows: 1,
    89100                    isFavorite: false,
     101                    type: elem.mapType,
    90102                }));
    91103            }
     104
     105            console.log("TYPES:", mapTypes);
     106            setMapFilters(mapTypes);
    92107
    93108            loadedTiles = [...mapTiles];
     
    114129
    115130        const updatedTiles = tiles.map((tile) =>
    116             tile.text === tileName ? { ...tile, isFavorite: !tile.isFavorite } : tile
     131            tile.text === tileName ? {...tile, isFavorite: !tile.isFavorite} : tile
    117132        );
    118133
     
    140155    };
    141156
     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
    142171    return (
    143172        <div className={styles.container}>
     
    152181                    onChange={handleSearchChange}
    153182                />
     183
    154184            </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>}
    156189            <TilesContainer
    157190                data={tiles}
    158                 renderTile={(props) => renderTile({ ...props, toggleFavorite })}
     191                renderTile={(props) => renderTile({...props, toggleFavorite})}
    159192                tileSize={tileSize}
    160                 forceTileWidth={150}
    161                 forceTileHeight={170}
     193                forceTileWidth={170}
     194                forceTileHeight={200}
    162195            />
    163196        </div>
  • imaps-frontend/src/pages/BrowseMaps/Maps.module.css

    r0c6b92a r79a0317  
    1919  margin-bottom: 30px;
    2020  text-align: center;
     21  margin-top: 5em;
    2122}
    2223
    2324.searchBar input {
    24   width: 300px;
     25  /*width: 300px;*/
     26  width: 33vw;
    2527  padding: 10px;
    2628  font-size: 16px;
     
    3436  border-color: #1e90ff;
    3537  box-shadow: 0 0 8px rgba(30, 144, 255, 0.5);
     38}
     39
     40.filterBar{
     41  margin-left: 6vw;
    3642}
    3743
  • imaps-frontend/src/pages/Draw/Draw.jsx

    r0c6b92a r79a0317  
    1 import { useContext, useEffect, useState } from "react";
     1import {useContext, useEffect, useState} from "react";
    22import styles from "./Draw.module.css";
    33import RoomModal from "../../components/Modals/RoomModal/RoomModal.jsx";
     
    88import SaveMap from "../../components/SaveMap/SaveMap.jsx";
    99import Logo from "../../components/Logo/Logo.jsx";
    10 import { Link, useNavigate, useParams, useSearchParams } from "react-router-dom";
     10import {Link, useNavigate, useParams, useSearchParams} from "react-router-dom";
    1111import Profile from "../../components/Profile/Profile.jsx";
    1212import HttpService from "../../scripts/net/HttpService.js";
     
    1818import config from "../../scripts/net/netconfig.js";
    1919import ShapeRegistry from "../../scripts/util/ShapeRegistry.js";
     20import {TopPanel} from "./TopPanel/TopPanel.jsx";
    2021
    2122function Draw() {
    22   const { mapName } = useParams();
    23   const { username } = useAppContext();
     23    const {mapName} = useParams();
     24    const {username} = useAppContext();
    2425
    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();
    2930
    30   const[roomTypes,setRoomTypes] = useState([]);
     31    const [roomTypes, setRoomTypes] = useState([]);
    3132
    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);
    3435
    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();
    3839
    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        }
    4252    };
    4353
    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
    5256
    53   const deleteFloorHandler = async (floorNum) => {
    54     if(floorNum === 0) return
     57        const httpService = new HttpService();
     58        httpService.setAuthenticated();
    5559
    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))
    5863
    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    };
    6273
    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    }, []);
    7879
    7980
    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    };
    9089
     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>
    91123
    92   };
     124                <br/>
    93125
    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            )}
    102149        </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    );
    155151}
    156152
  • imaps-frontend/src/pages/Draw/Draw.module.css

    r0c6b92a r79a0317  
    2525  font-weight: 700;
    2626  text-transform: uppercase;
    27   color: #333333;
     27  color: #c0c0c0;
    2828  font-family: "Oswald", sans-serif;
    2929  margin-bottom: 0;
  • imaps-frontend/src/pages/IMaps/components/Button.css

    r0c6b92a r79a0317  
    2020
    2121.btn--outline {
    22   background-color: transparent;
     22  background-color: #ffffffc2;
    2323
    24   color: #fff;
     24  color: #252627;
    2525  padding: 8px 20px;
     26  border: 1px solid #252627 ;
    2627  /* border: 1px solid var(--primary); */
    2728  transition: all 0.3s ease-out;
     
    3738  font-size: 24px;
    3839}
     40.btn--outline:hover{
     41  transition: all 0.3s ease-out;
     42  background: #000000;
     43  color:#ffffffc2;
     44}
    3945
    4046.btn--large:hover,
     
    4349  background: #ffffffc2;
    4450  color: #000000;
    45   transition: 250ms;
    4651}
  • imaps-frontend/src/pages/IMaps/components/Cards.jsx

    r0c6b92a r79a0317  
    1919              text="Create intricate floor plans for your building with precision and ease."
    2020              label="Create"
    21               path="/services"
     21              path="#"
    2222            />
    2323            <CardItem
     
    2525              text="Explore and navigate through complex building layouts seamlessly."
    2626              label="Explore"
    27               path="/services"
     27              path="#"
    2828            />
    2929          </ul>
     
    3333              text="Add custom icons, labels, and markers to personalize your indoor maps."
    3434              label="Customize"
    35               path="/services"
     35              path="#"
    3636            />
    3737            <CardItem
     
    3939              text="Ensure accessibility by mapping out routes and facilities for all users."
    4040              label="Accessibility"
    41               path="/products"
     41              path="#"
    4242            />
    4343            <CardItem
     
    4545              text="Share your maps with others and collaborate in real-time for efficient space planning."
    4646              label="Collaboration"
    47               path="/sign-up"
     47              path="#"
    4848            />
    4949          </ul>
  • imaps-frontend/src/pages/IMaps/components/Footer.css

    r0c6b92a r79a0317  
    4747.footer-links {
    4848  width: 100%;
    49   max-width: 1000px;
    5049  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%;
    5257}
    5358
    54 .footer-link-wrapper {
    55   display: flex;
    56 }
    5759
    5860.footer-link-items {
    5961  display: flex;
    6062  flex-direction: column;
    61   align-items: flex-start;
     63  align-items: center;
    6264  margin: 16px;
    6365  text-align: left;
     
    100102
    101103.social-media {
    102   max-width: 1000px;
    103   width: 100%;
    104104}
    105105
    106106.social-media-wrap {
    107107  display: flex;
    108   justify-content: space-between;
     108  flex-direction: column;
    109109  align-items: center;
    110   width: 90%;
    111   max-width: 1000px;
    112   margin: 40px auto 0 auto;
    113110}
    114111
     
    123120  color: #fff;
    124121  justify-self: start;
    125   margin-left: 20px;
    126122  cursor: pointer;
    127123  text-decoration: none;
     
    131127  margin-bottom: 16px;
    132128}
     129.social-media-wrap img {
     130  width: 25%;
     131}
    133132
    134133.website-rights {
    135134  color: #fff;
    136   margin-bottom: 16px;
     135  align-content: flex-end;
     136  width: 50%;
     137  margin-top: 2em;
    137138}
    138139
  • imaps-frontend/src/pages/IMaps/components/Footer.jsx

    r0c6b92a r79a0317  
    22import "./Footer.css";
    33import { Button } from "./Button";
    4 import logo from "../../../assets/logo_icon.png";
     4import logo from "../../../assets/novo_logo_nobg_cropped.png";
     5import {Link} from "react-router-dom";
    56
    67function Footer() {
    78  return (
    89    <div className="footer-container">
    9       {/* <div className="footer-links">
     10      <div className="footer-links">
    1011        <div className="footer-link-wrapper">
    1112          <div className="footer-link-items">
     
    1314            <a href="#">Contact</a>
    1415            <a href="#">Support</a>
    15             <a href="#">Destinations</a>
    16             <a href="#">Sponsorships</a>
    1716          </div>
    1817        </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>
    2633          </div>
    2734        </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
    5638    </div>
    5739  );
  • imaps-frontend/src/pages/IMaps/components/Navbar.css

    r0c6b92a r79a0317  
    11.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);
    143  color: #ffffff;
    154  /*color: #333333;*/
     
    2413  border-radius: 8px;
    2514  margin: 5px;
     15  height: 70px;
     16  overflow: hidden;
    2617}
    2718
  • imaps-frontend/src/pages/IMaps/components/Navbar.jsx

    r0c6b92a r79a0317  
    1010function Navbar() {
    1111  const [click, setClick] = useState(false);
    12   const [button, setButton] = useState(true);
    13 
    14   const handleClick = () => setClick(!click);
    15   const closeMobileMenu = () => setClick(false);
    1612
    1713  const { isAuthenticated } = useAppContext();
     
    2319                <div className="navbar-left">
    2420                    <Logo position="relative"/>
    25                     <h1 className="navbar-title">iMaps</h1>
    2621                </div>
    2722
  • imaps-frontend/src/pages/IMaps/components/pages/Home.jsx

    r0c6b92a r79a0317  
    1414        <h2 className="description">Create and explore detailed indoor maps.</h2>
    1515        <div className="hero-btns">
    16           <Link to="/myMaps">
    17             <Button className="btns" buttonSize="btn--large">
    18               Create Maps
     16          <Link to="/Maps">
     17            <Button buttonSize="btn--large" >
     18              Browse Maps <i className="far fa-play-circle" />
    1919            </Button>
    2020          </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
    2424            </Button>
    2525          </Link>
  • imaps-frontend/src/pages/IMaps/components/pages/Home.scss

    r0c6b92a r79a0317  
    5050  width: 100vw;
    5151  /* 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 */
    5356
    5457  -webkit-animation: bg-scrolling-reverse .92s infinite; /* Safari 4+ */
     
    98101
    99102  font-size: 6em;
    100 
    101 
    102103
    103104
  • imaps-frontend/src/pages/Login/Login.jsx

    r0c6b92a r79a0317  
    66import HttpService from "../../scripts/net/HttpService.js";
    77import {useAppContext} from "../../components/AppContext/AppContext.jsx";
    8 import config from "../../scripts/net/netconfig.js";
     8import config, {API_BASE_URL} from "../../scripts/net/netconfig.js";
     9import google_icon from "../../assets/Logo-google-icon-PNG.png"
     10import github_icon from "../../assets/github-mark-white.png";
     11import { v4 as uuidv4 } from 'uuid';
    912
    1013const LoginPage = () => {
     
    2629    const handleLogin = async () => {
    2730        const httpService = new HttpService();
    28         return httpService.post(config.auth.login, payload)
    29 
     31        return httpService.post(config.auth.login, payload);
    3032    };
    31 
    3233
    3334    const login = async (e) => {
     
    3738            .then(resp => {
    3839                if (resp.token) {
    39                     navigate(targetPath)
     40                    navigate(targetPath);
    4041                    localStorage.setItem("token", resp.token);
    4142                    setUsername(resp.username);
    4243                    setIsAuthenticated(true);
    43                     console.log("ROLES",resp.roles)
     44                    console.log("ROLES", resp.roles);
    4445                } else {
    4546                    setError("Invalid username or password.");
     
    4748            }).catch(reason => {
    4849            console.error("Login failed", reason);
    49             setError("Login failed. Please try again.")
    50         })
     50            setError("Login failed. Please try again.");
     51        });
     52    };
    5153
    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
    7777    };
    7878
     
    111111                    <button type="submit">Submit</button>
    112112                </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>
    113124                <p>
    114125                    Don't have an account? <Link to="/Signup"> Sign Up </Link>
  • imaps-frontend/src/pages/Login/Login.module.css

    r0c6b92a r79a0317  
    1313
    1414.wrapper {
    15   display: -webkit-box;
    16   display: -ms-flexbox;
    1715  display: flex;
     16  justify-content: flex-end; /* Move content towards the right */
     17  align-items: center; /* Center content vertically */
    1818  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
    2824
    2925.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 */
    3336}
    3437
    3538.form .heading {
    36   font-size: 1.5rem;
     39  font-size: 1.8rem;
    3740  font-weight: bold;
    3841  text-align: center;
     42  margin-bottom: 1.5rem; /* Spacing between heading and form */
    3943}
    4044
    4145.or {
    42   margin: 1rem 0;
     46  margin-top: 1em;
     47  text-align: center;
     48  font-weight: bold;
     49  color: #666;
    4350}
    4451
     
    4653  display: block;
    4754  margin: 1.25rem 0 1rem 0;
     55  font-size: 0.95rem;
     56  color: #333;
    4857}
    4958
     
    5160  height: 40px;
    5261  width: 100%;
     62  max-width: 350px; /* Slightly larger max-width */
    5363  padding: 15px;
    5464  background-color: #f1f9ff;
    5565  border: 2px solid #bce0fd;
    5666  border-radius: 8px;
     67  box-sizing: border-box;
     68  font-size: 1rem; /* Make input text a bit larger */
    5769}
    5870
     
    6072  height: 40px;
    6173  width: 100%;
     74  max-width: 350px;
     75  margin-top: 1.5rem;
    6276  background-color: #258de6;
    6377  color: white;
     
    6579  letter-spacing: 1px;
    6680  border: none;
    67   display: block;
    68   margin: 0 auto;
    69   font-weight: bold;
    70   margin-top: 1.5rem;
     81  font-weight: bold;
    7182  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;
    72131}
    73132
    74133@media (min-width: 542px) {
    75134  body {
    76     display: -webkit-box;
    77     display: -ms-flexbox;
    78135    display: flex;
    79     -webkit-box-pack: center;
    80     -ms-flex-pack: center;
    81136    justify-content: center;
    82137  }
     138
    83139  .wrapper {
    84     display: -webkit-box;
    85     display: -ms-flexbox;
    86140    display: flex;
    87141    height: 100vh;
    88     -webkit-box-align: center;
    89     -ms-flex-align: center;
    90142    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;
    94145    max-width: 1100px;
    95146  }
     147
    96148  .form {
    97     -ms-flex-preferred-size: auto;
    98149    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
    100155  .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
    103164  .illustration img {
    104     max-width: 80%;
     165    max-width: 100%;
    105166    height: auto;
    106167  }
     
    108169
    109170@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
    110177  .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  }
    119192}
    120193
     
    126199  transform: scale(0.98);
    127200}
     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  
    1414import ShapeRegistry from "../../scripts/util/ShapeRegistry.js";
    1515import {useAppContext} from "../../components/AppContext/AppContext.jsx";
     16import Report from "../../components/Report/Report.jsx";
     17import {Button} from "../IMaps/components/Button.jsx";
    1618
    1719const MapView = ({isPrivate}) => {
    1820    const {mapName} = useParams();
    1921    const {username} = useAppContext();
     22    const {isAuthenticated} = useAppContext();
    2023
    2124    const [mapLoaded, setMapLoaded] = useState(false);
     
    2831    const [searchParams, setSearchParams] = useSearchParams();
    2932    const [mainEntrance, setMainEntrance] = useState({});
     33    const [canDisplayNavDownload,setCanDisplayNavDownload] = useState(false);
     34    const [from,setFrom] = useState("");
     35    const [to,setTo] = useState("");
    3036
    3137    const defaultNavObj = {
     
    115121                setFloors(respFloors);
    116122
     123                console.log("FLOOR DATA: " + tlFloor?.mapData)
     124
    117125                appInstance.loadMapN(tlFloor?.mapData)
    118126                setApp(appInstance);
     
    163171        const toEncoded = encodeURIComponent(toSearch).trimEnd()
    164172
     173        setFrom(fromSearch);
     174        setTo(toSearch);
     175
    165176        httpService.get(`${config.view_maps.navigate}?from=${fromEncoded}&to=${toEncoded}`).then(path => {
    166177            app.drawRouteNEW(path);
     178
     179
    167180        }).catch(reason => {
    168181            console.log("err", reason)
     
    185198    }
    186199
     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
    187212
    188213    useEffect(() => {
     
    216241        app.loadMapN(chFloor.mapData)
    217242        app.floorNum = floorNum;
    218 
    219243
    220244        console.log(`Floor changed to: ${floorNum}`);
     
    257281                                handleFloorChange={handleFloorChange}
    258282                            />
     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>) }
    259294                            <FilterBar map={app} roomTypes={roomTypes}/>
    260295                        </div>
    261296                    )}
     297
     298                    {isAuthenticated && <Report mapName = {mapName}></Report>}
    262299                    <div className={styles.profileContainer}>
    263300                        <Profile position="relative"/>
     
    265302                </div>
    266303            </div>
     304            <div id="temp"></div>
    267305
    268306            <div className={styles.floorSelectorContainer}>
     
    283321            </div>
    284322
     323
    285324        </div>
    286325    );
  • imaps-frontend/src/pages/MyMaps/CreateMaps.module.css

    r0c6b92a r79a0317  
    5555  margin-bottom: 30px;
    5656  text-align: center;
     57  margin-top: 5em;
     58
    5759}
    5860
    5961.searchBar input {
    60   width: 300px;
     62  /*width: 300px;*/
     63  width: 33vw;
    6164  padding: 10px;
    6265  font-size: 16px;
     
    8386  display: flex;
    8487  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;
    8790  padding: 10px;
     91  /*padding-left: 2.5em;*/
    8892  background-color: #f9f9f9;
    8993  border-radius: 8px;
    9094  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    91 }
     95
     96}
     97
    9298
    9399.tile {
     
    127133}
    128134
     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
    129195@media (max-width: 768px) {
    130196  .mapsContainer {
  • imaps-frontend/src/pages/MyMaps/MyMaps.jsx

    r0c6b92a r79a0317  
    1010import {useAppContext} from "../../components/AppContext/AppContext.jsx";
    1111import config from "../../scripts/net/netconfig.js";
    12 import {element} from "prop-types";
    1312import Toast from "../../components/Toast/Toast.jsx";
     13import plus_icon from "../../assets/plus_icon.png";
    1414
    1515const renderTile = ({data, isDragging}, openMapInfo) => (
     
    6565        setToastMessage(message);
    6666        setToastType(type);
    67         setTimeout(() => setToastMessage(null), 3000); // Automatically hide the toast after 3 seconds
    68     };
    69 
     67        setTimeout(() => setToastMessage(null), 3000);
     68    };
    7069
    7170    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        }
    7384    };
    7485
     
    8394                setTiles((prevTiles) => prevTiles.filter((tile) => tile.mapName !== mapName));
    8495                setAllTiles((prevTiles) => prevTiles.filter((tile) => tile.mapName !== mapName));
    85                 showToast("Map deleted", 1)
     96                showToast("Map deleted", 1);
    8697            })
    8798            .catch((error) => {
    8899                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);
    91101            });
    92102    };
     
    97107
    98108        httpService
    99             .put(`${config.my_maps.add}?username=${username}`, mapDetails)
     109            .put(`${config.my_maps.add}?username=${encodeURI(username)}`, mapDetails)
    100110            .then((respMap) => {
    101                 console.log("RESP NEW MAP: " + respMap)
    102111                const mapTile = {
    103112                    mapName: respMap.mapName,
     
    108117                    modified_at: respMap.modifiedAt,
    109118                    published_at: respMap.published_at,
    110                     gmaps_url: respMap.gmaps_url,
     119                    gmaps_url: respMap.gmapsUrl,
    111120                    image_url: card,
    112121                    is_published: respMap.is_published,
     122                    mapType: respMap.mapType
    113123                };
    114124
     
    116126                setTiles((prevTiles) => [...prevTiles, mapTile]);
    117127                showToast("Map added successfully!");
    118 
    119 
    120128            })
    121129            .catch((error) => {
    122                 showToast("Map name already taken", 0)
     130                showToast("Map name already taken", 0);
    123131            });
    124132    };
     
    129137            httpService.setAuthenticated();
    130138
    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            );
    132142
    133143            const mapTiles = respMaps.map((elem) => ({
     
    139149                modified_at: elem.modifiedAt,
    140150                published_at: elem.published_at,
    141                 gmaps_url: elem.gMapsUrl,
     151                gmaps_url: elem.gmapsUrl,
    142152                image_url: card,
    143153                numFavourites: elem.numFavourites,
    144154            }));
    145 
    146155
    147156            setTiles(mapTiles);
     
    154163        setPublicMaps(tiles.filter((tile) => tile.status === "PUBLIC"));
    155164        setPrivateMaps(tiles.filter((tile) => tile.status === "PRIVATE"));
    156         setPendingMaps(tiles.filter((tile) => tile.status === "INVALID"));
     165        setPendingMaps(tiles.filter((tile) => tile.status === "PENDING"));
    157166    }, [tiles]);
    158167
     
    164173    return (
    165174        <div className={styles.container}>
    166             <Logo/>
    167             <Profile/>
     175
     176            <div className={`${styles.logoWrapper}`}>
     177                <Logo/>
     178            </div>
    168179            <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
    175184
    176185            <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                />
    178191            </div>
    179192
     
    201214                        forceTileHeight={170}
    202215                    />
     216                    <button className={styles.plusButton} onClick={openCreateModal}>
     217                        <img src={plus_icon} alt="Add Map"/>
     218                    </button>
    203219                </div>
    204220
     
    222238                onDelete={deleteMap}
    223239                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                    }
    225254                    showToast(`Map ${selectedMap.mapName} published successfully!`);
    226255                    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])
    228257                    closeMapInfoModal()
    229258                }}
     
    236265            />
    237266
    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            )}
    239274        </div>
    240275    );
  • imaps-frontend/src/pages/Signup/Signup.jsx

    r0c6b92a r79a0317  
    44import styles from "./Signup.module.css";
    55import Logo from "../../components/Logo/Logo";
     6import netconfig from "../../scripts/net/netconfig.js";
    67
    78export default function Signup() {
     
    910  const [email, setEmail] = useState("");
    1011  const [password, setPassword] = useState("");
     12  const [confirmPassword, setConfirmPassword] = useState("");
    1113  const [message, setMessage] = useState("");
    12   const [messageType, setMessageType] = useState(""); // New state to manage message type
     14  const [messageType, setMessageType] = useState("");
    1315  const navigate = useNavigate();
    1416
    1517  const handleSubmit = async (e) => {
    1618    e.preventDefault();
     19
     20    if (password !== confirmPassword) {
     21      setMessageType("error");
     22      setMessage("Passwords do not match.");
     23      return;
     24    }
    1725
    1826    const payload = {
     
    2331
    2432    try {
    25       const response = await fetch("http://localhost:8080/api/auth/register", {
     33      const response = await fetch(netconfig.auth.register, {
    2634        method: "POST",
    2735        headers: {
     
    3543        setMessage("User registered successfully!");
    3644
    37         // Wait 3 seconds and then redirect to login page
    3845        setTimeout(() => {
    3946          navigate("/login");
    40         }, 3000);
     47        }, 1000);
    4148      } else if (response.status === 409) {
    4249        setMessageType("error");
     
    5461
    5562  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>
    60126      </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             <input
    67               type="text"
    68               id="name"
    69               value={name}
    70               onChange={(e) => setName(e.target.value)}
    71               placeholder="Enter your username"
    72               required
    73             />
    74           </div>
    75           <div>
    76             <label htmlFor="email">E-Mail</label>
    77             <input
    78               type="email"
    79               id="email"
    80               value={email}
    81               onChange={(e) => setEmail(e.target.value)}
    82               placeholder="Enter your email"
    83               required
    84             />
    85           </div>
    86           <div>
    87             <label htmlFor="password">Password</label>
    88             <input
    89               type="password"
    90               id="password"
    91               value={password}
    92               onChange={(e) => setPassword(e.target.value)}
    93               placeholder="Enter your password"
    94               required
    95             />
    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             OR
    107           </h2>
    108         </form>
    109         <p>
    110           Have an account? <Link to="/Login"> Login </Link>
    111         </p>
    112       </div>
    113     </div>
    114127  );
    115128}
  • 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 
    141.wrapper {
    15   display: -webkit-box;
    16   display: -ms-flexbox;
    172  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 */
    279}
    2810
    2911.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 */
    3319}
    3420
    3521.form .heading {
    36   font-size: 1.5rem;
     22  font-size: 1.8rem;
    3723  font-weight: bold;
    38   text-align: center;
    39 }
    40 
    41 .or {
    42   margin: 1rem 0;
     24  color: #258de6;
     25  margin-bottom: 1rem;
    4326}
    4427
    4528.form label {
    4629  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;
    4834}
    4935
     
    5137  height: 40px;
    5238  width: 100%;
    53   padding: 15px;
     39  padding: 10px 15px;
    5440  background-color: #f1f9ff;
    5541  border: 2px solid #bce0fd;
    5642  border-radius: 8px;
     43  font-size: 1rem;
     44}
     45
     46.form input:focus {
     47  outline: none;
     48  border-color: #258de6;
     49  background-color: #e6f4ff;
    5750}
    5851
    5952.form button {
    60   height: 40px;
     53  height: 45px;
    6154  width: 100%;
    6255  background-color: #258de6;
     
    6558  letter-spacing: 1px;
    6659  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;
    6799  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;
    69137  font-weight: bold;
    70   margin-top: 1.5rem;
     138  text-align: center;
     139  margin-top: 1rem;
     140  padding: 10px;
    71141  border-radius: 8px;
    72142}
    73143
    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;
    107148}
    108149
    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;
    113154}
    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 }
Note: See TracChangeset for help on using the changeset viewer.