From f1e317bd4b2ea00f97cc596f2f75bfe98671815b Mon Sep 17 00:00:00 2001 From: zoi23333 <110458414+zoi23333@users.noreply.github.com> Date: Fri, 29 Sep 2023 16:44:45 +0200 Subject: [PATCH] Redo road details page structure --- .gitignore | 1 + frontend/src/App.tsx | 23 +- frontend/src/Components/Navbar.test.tsx | 25 -- frontend/src/Components/Navbar.tsx | 32 --- .../Components/RoadDetails/ImageGallery.tsx | 133 +++++++++++ .../src/Components/RoadDetails/MapArea.tsx | 21 ++ .../src/Components/RoadDetails/RoadImage.tsx | 15 ++ .../src/Components/RoadDetails/TopBar.tsx | 63 +++++ frontend/src/css/road_details.css | 217 ++++++++++++++++++ frontend/src/pages/RoadDetails.tsx | 21 ++ 10 files changed, 479 insertions(+), 72 deletions(-) delete mode 100644 frontend/src/Components/Navbar.test.tsx delete mode 100644 frontend/src/Components/Navbar.tsx create mode 100644 frontend/src/Components/RoadDetails/ImageGallery.tsx create mode 100644 frontend/src/Components/RoadDetails/MapArea.tsx create mode 100644 frontend/src/Components/RoadDetails/RoadImage.tsx create mode 100644 frontend/src/Components/RoadDetails/TopBar.tsx create mode 100644 frontend/src/css/road_details.css create mode 100644 frontend/src/pages/RoadDetails.tsx diff --git a/.gitignore b/.gitignore index 9f11b755..68729d6b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .idea/ +.DS_Store \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 2df6e1bd..4571136d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,30 +1,23 @@ import { FC } from 'react'; -import { - BrowserRouter as Router, - Navigate, - Route, - Routes, -} from 'react-router-dom'; - -import Navbar from './Components/Navbar'; -import RoadConditions from './pages/RoadConditions'; -import Conditions from './pages/Conditions'; +import { BrowserRouter as Router, Navigate } from 'react-router-dom'; import './App.css'; +import { Route, Routes } from 'react-router-dom'; +import RoadDetails from './pages/RoadDetails'; + +import Conditions from './pages/Conditions'; const App: FC = () => { return (
- - } /> - - + } /> + } /> + } />
); }; - export default App; diff --git a/frontend/src/Components/Navbar.test.tsx b/frontend/src/Components/Navbar.test.tsx deleted file mode 100644 index 7e8497b9..00000000 --- a/frontend/src/Components/Navbar.test.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import Navbar from './Navbar'; -import { BrowserRouter as Router } from 'react-router-dom'; - -function TestRouter() { - return ( - - - - ); -} -test('Check links in navbar', () => { - render(); - const linkConditionsML = screen.getByText(/Conditions \(ML\)/i); - expect(linkConditionsML).toBeInTheDocument(); - expect( - screen.getByRole('link', { name: /Conditions \(ML\)/i }), - ).toHaveAttribute('href', '/conditions'); - - const linkConditionsGP = screen.getByText(/Conditions \(GP\)/i); - expect(linkConditionsGP).toBeInTheDocument(); - expect( - screen.getByRole('link', { name: /Conditions \(GP\)/i }), - ).toHaveAttribute('href', '/road_conditions'); -}); diff --git a/frontend/src/Components/Navbar.tsx b/frontend/src/Components/Navbar.tsx deleted file mode 100644 index 9d1398cc..00000000 --- a/frontend/src/Components/Navbar.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { FC } from 'react'; -import { NavLink } from 'react-router-dom'; - -import '../css/navbar.css'; - -interface NavBtnProps { - to: string; - name: string; -} - -const NavBtn: FC = ({ to, name }) => { - return ( - - {name} - - ); -}; - -const Navbar: FC = () => { - return ( -
-
-
- - -
-
-
- ); -}; - -export default Navbar; diff --git a/frontend/src/Components/RoadDetails/ImageGallery.tsx b/frontend/src/Components/RoadDetails/ImageGallery.tsx new file mode 100644 index 00000000..53732eba --- /dev/null +++ b/frontend/src/Components/RoadDetails/ImageGallery.tsx @@ -0,0 +1,133 @@ +import React, { useState } from 'react'; + +interface Image { + id: number; + url: string; +} + +const images: Image[] = [ + { + id: 1, + url: 'https://upload.wikimedia.org/wikipedia/commons/4/43/2015-04-02_18_21_50_View_north_along_U.S._Route_95_in_the_Forty_Mile_Desert_of_Churchill_County%2C_Nevada.JPG', + }, + { + id: 2, + url: 'https://upload.wikimedia.org/wikipedia/commons/4/43/2015-04-02_18_21_50_View_north_along_U.S._Route_95_in_the_Forty_Mile_Desert_of_Churchill_County%2C_Nevada.JPG', + }, + { + id: 3, + url: 'https://upload.wikimedia.org/wikipedia/commons/4/43/2015-04-02_18_21_50_View_north_along_U.S._Route_95_in_the_Forty_Mile_Desert_of_Churchill_County%2C_Nevada.JPG', + }, + { + id: 4, + url: 'https://upload.wikimedia.org/wikipedia/commons/4/43/2015-04-02_18_21_50_View_north_along_U.S._Route_95_in_the_Forty_Mile_Desert_of_Churchill_County%2C_Nevada.JPG', + }, + { + id: 5, + url: 'https://upload.wikimedia.org/wikipedia/commons/4/43/2015-04-02_18_21_50_View_north_along_U.S._Route_95_in_the_Forty_Mile_Desert_of_Churchill_County%2C_Nevada.JPG', + }, + { + id: 6, + url: 'https://upload.wikimedia.org/wikipedia/commons/4/43/2015-04-02_18_21_50_View_north_along_U.S._Route_95_in_the_Forty_Mile_Desert_of_Churchill_County%2C_Nevada.JPG', + }, + { + id: 7, + url: 'https://upload.wikimedia.org/wikipedia/commons/4/43/2015-04-02_18_21_50_View_north_along_U.S._Route_95_in_the_Forty_Mile_Desert_of_Churchill_County%2C_Nevada.JPG', + }, + { + id: 8, + url: 'https://hips.hearstapps.com/hmg-prod/images/1/roadbootie-main-1520457496.jpg?crop=0.848xw:1xh;center,top&resize=1200:*', + }, + { + id: 9, + url: 'https://hips.hearstapps.com/hmg-prod/images/1/roadbootie-main-1520457496.jpg?crop=0.848xw:1xh;center,top&resize=1200:*', + }, + { + id: 10, + url: 'https://hips.hearstapps.com/hmg-prod/images/1/roadbootie-main-1520457496.jpg?crop=0.848xw:1xh;center,top&resize=1200:*', + }, + { + id: 11, + url: 'https://hips.hearstapps.com/hmg-prod/images/1/roadbootie-main-1520457496.jpg?crop=0.848xw:1xh;center,top&resize=1200:*', + }, + { + id: 12, + url: 'https://hips.hearstapps.com/hmg-prod/images/1/roadbootie-main-1520457496.jpg?crop=0.848xw:1xh;center,top&resize=1200:*', + }, + { + id: 13, + url: 'https://hips.hearstapps.com/hmg-prod/images/1/roadbootie-main-1520457496.jpg?crop=0.848xw:1xh;center,top&resize=1200:*', + }, + { + id: 14, + url: 'https://hips.hearstapps.com/hmg-prod/images/1/roadbootie-main-1520457496.jpg?crop=0.848xw:1xh;center,top&resize=1200:*', + }, + { + id: 15, + url: 'https://hips.hearstapps.com/hmg-prod/images/1/roadbootie-main-1520457496.jpg?crop=0.848xw:1xh;center,top&resize=1200:*', + }, +]; + +const ImageGallery: React.FC = () => { + const [scrollPosition, setScrollPosition] = useState(0); + + const openImageInNewTab = (imageUrl: string) => { + window.open(imageUrl, '_blank'); + }; + + const handleScroll = (scrollOffset: number) => { + const newScrollPosition = scrollPosition + scrollOffset; + + // Calculate the maximum scroll limit based on the total image width + const maxScrollLimit = (images.length - 11.4) * 124; // 120 is the width of each image + + // Ensure the scroll stays within bounds + const boundedScrollPosition = Math.max( + 0, + Math.min(newScrollPosition, maxScrollLimit), + ); + + setScrollPosition(boundedScrollPosition); + }; + + return ( +
+
+
+ {images.map((image) => ( + {`Image openImageInNewTab(image.url)} + className="image-thumbnail" + /> + ))} +
+
+ + +
+ ); +}; + +export default ImageGallery; diff --git a/frontend/src/Components/RoadDetails/MapArea.tsx b/frontend/src/Components/RoadDetails/MapArea.tsx new file mode 100644 index 00000000..0d435662 --- /dev/null +++ b/frontend/src/Components/RoadDetails/MapArea.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import ImageGallery from './ImageGallery'; +import MapWrapper from '../Map/MapWrapper'; + +/** + * The map area op the road details(road inspect) page + */ +const MapArea: React.FC = () => { + return ( +
+
+ +
+
+ {/* Use the imageGallery component */} +
+
+ ); +}; + +export default MapArea; diff --git a/frontend/src/Components/RoadDetails/RoadImage.tsx b/frontend/src/Components/RoadDetails/RoadImage.tsx new file mode 100644 index 00000000..640320b1 --- /dev/null +++ b/frontend/src/Components/RoadDetails/RoadImage.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +const maproadStyles = { + margin: 0, + padding: 20, + color: '#333', +}; + +const road_area =

Road surface image

; + +const RoadImage: React.FC = () => { + return
{road_area}
; +}; + +export default RoadImage; diff --git a/frontend/src/Components/RoadDetails/TopBar.tsx b/frontend/src/Components/RoadDetails/TopBar.tsx new file mode 100644 index 00000000..774e5e00 --- /dev/null +++ b/frontend/src/Components/RoadDetails/TopBar.tsx @@ -0,0 +1,63 @@ +// The TopBar for RoadDetails page +import React from 'react'; +import { Link, useNavigate } from 'react-router-dom'; // Import useNavigate + +interface TopBarProps { + /** + * The toggle button value + */ + isToggleOn: React.Dispatch>; +} + +const return_btn = ( + + + +); + +/** + * The Topbar with return and toggle button inside + */ +const TopBar: React.FC = ({ isToggleOn }) => { + const navigate = useNavigate(); // Get the navigate function + + const handleReturn = () => { + // Navigate back to the home page when the button is clicked + navigate('/'); + }; + + const handleToggleMiddleArea = () => { + // Toggle the state to show/hide MiddleArea2 + isToggleOn((prevState) => !prevState); + }; + + return ( +
+ + {return_btn} + +
+
+ Map mode + + Road mode +
+
+
+ ); +}; + +export default TopBar; diff --git a/frontend/src/css/road_details.css b/frontend/src/css/road_details.css new file mode 100644 index 00000000..43eab5fa --- /dev/null +++ b/frontend/src/css/road_details.css @@ -0,0 +1,217 @@ +/* src/styles.css */ + +/* Define styles for the top bar */ +.top-bar { + background-color: #333; /* Example color for the top bar */ + height: 50px; + display: flex; /* Enable flex layout */ + align-items: center; /* Vertically center content */ + justify-content: flex-start; /* Align content to the left (40px margin) */ +} + +/* Define styles for the middle area */ +.road-image, +.map-area { + /* background-color: #eee; Example color for the middle area */ + height: 60vh; /* Occupying half of the page vertically */ + display: flex; + flex-direction: column; + justify-content: flex-start; +} + +/* Define styles for the plot area */ +.plot-area { + background-color: #333; /* Grey with 50% opacity */ + height: 40vh; +} + +/* To center the Return button vertically */ +.btnLinkContainer { + display: flex; /* Enable flex layout */ + align-items: center; /* Vertically center content */ + justify-content: flex-start; /* Align content to the left (40px margin) */ + padding-left: 30px; + background: none; /* Remove background color */ + border: none; /* Remove borders */ + font-size: inherit; /* Inherit font size */ + color: inherit; /* Inherit text color */ + cursor: pointer; /* Add a pointer cursor on hover */ + text-decoration: none; /* Remove underlines on hover */ +} + +.btnWhite { + color: #fff; + text-decoration: 'none'; + background: none; +} + +/* _____________________________________________________ */ +/* Style for the toggle button */ +.toggle-switch { + position: relative; + display: inline-block; + width: 40px; + height: 20px; + background-color: #ccc; + border-radius: 20px; +} + +/* Style for the slider (the switch itself) */ +.slider { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #2196f3; + border-radius: 20px; + transition: 0.4s; +} + +/* Style to make the slider round */ +.slider:before { + content: ''; + position: absolute; + width: 16px; + height: 16px; + background-color: white; + border-radius: 50%; + left: 2px; + top: 2px; + transition: 0.4s; +} + +/* Style to change the background color when switched on */ +input:checked + .slider { + background-color: #4caf50; +} + +/* Style to move the slider to the right when switched on */ +input:checked + .slider:before { + transform: translateX(20px); +} + +/* Hide the default checkbox input */ +input[type='checkbox'] { + display: none; +} + +/* Style for the toggle button container */ +.topBar-container { + display: flex; + align-items: center; /* Vertically center items */ + justify-content: space-between; /* Add space between items */ +} + +.toggle-container { + padding-right: 30px; + display: flex; + align-items: center; /* Vertically center items */ + justify-content: space-between; /* Add space between items */ +} + +/* Style for the toggle button label */ +.toggle-label { + display: flex; + align-items: center; /* Vertically center items */ +} + +/* Style for the text on both sides of the switch */ +.toggle-text { + margin: 0 10px 0 10px; /* Adjust the spacing as needed */ +} + +/* Style for image Gallery */ + +.image-gallery { + display: flex; +} + +.image-thumbnail:hover { + transform: scale(1.1); /* Increase the size on hover (optional) */ +} + +/* CSS (styles.css or within a styled-components setup) */ +.overlay { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.7); /* Semi-transparent black background */ + z-index: 999; /* Ensure the overlay appears above other content */ + justify-content: center; + align-items: center; +} + +.overlay img { + max-width: 90%; /* Adjust the size of the modal image as needed */ + max-height: 90vh; /* Limit the height of the modal image to 90% of the viewport height */ +} + +/* Image gallery scroll */ + +.imageGallery_container { + background-color: #999; + height: 14vh; + margin-top: auto; + overflow: auto; + white-space: nowrap; + overflow: hidden; +} + +.image-gallery-container { + display: flex; /* Add display: flex */ + flex-direction: column; /* Stack children vertically */ + justify-content: center; /* Center children vertically */ + position: relative; +} + +.image-gallery-page { + display: flex; + overflow-x: auto; /* Enable horizontal scrolling */ + max-width: 100%; /* Set a maximum width */ +} + +.image-container { + display: flex; + align-items: center; + margin-left: 0.5vh; +} + +.image-thumbnail { + width: 120px; /* Set the fixed width */ + height: 105px; /* Set a fixed height to maintain aspect ratio */ + object-fit: cover; /* Preserve aspect ratio and cover the container */ + cursor: pointer; + transition: transform 0.3s ease-in-out; + margin-right: 4px; + margin-top: 0.5vh; + text-align: center; +} + +.arrow-button { + background-color: #fff; + border: none; + height: 50px; + width: 50px; + border-radius: 50%; + position: absolute; + box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 13px; + /* color: #222; */ +} + +.left-arrow { + left: 10px; + transform: rotate(180deg); +} + +.right-arrow { + right: 10px; +} diff --git a/frontend/src/pages/RoadDetails.tsx b/frontend/src/pages/RoadDetails.tsx new file mode 100644 index 00000000..b6cf86ba --- /dev/null +++ b/frontend/src/pages/RoadDetails.tsx @@ -0,0 +1,21 @@ +import { useState } from 'react'; + +import '../css/road_details.css'; // Import the CSS file +import MapArea from '../Components/RoadDetails/MapArea'; +import RoadImage from '../Components/RoadDetails/RoadImage'; +import TopBar from '../Components/RoadDetails/TopBar'; + +const RoadDetails = () => { + const [showMapImageMode, setShowRoadImageMode] = useState(false); + + return ( +
+ + {showMapImageMode ? : } + {/* TODO: putting plots here */} +
{/* Content for the plot area */}
+
+ ); +}; + +export default RoadDetails;