diff --git a/client/package-lock.json b/client/package-lock.json
deleted file mode 100644
index 7131c12b8..000000000
--- a/client/package-lock.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "name": "client",
- "version": "0.0.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "client"
- }
- }
-}
diff --git a/client/src/App.css b/client/src/App.css
index da10bf951..d7399b53f 100644
--- a/client/src/App.css
+++ b/client/src/App.css
@@ -48,7 +48,7 @@ li {
}
header {
- position: sticky;
+ position: flex;
top: 0;
width: 100%;
box-sizing: border-box;
@@ -61,12 +61,64 @@ header {
z-index: 1000;
display: flex;
flex-direction: row;
- justify-content: space-between;
gap: 10px;
}
+header .title-container {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-left: auto;
+}
+
+header .title-logo {
+ height: 40px;
+ width: auto;
+}
+
+/* Dropdown container - hidden on desktop, shown on mobile */
+.dropmenu-container {
+ display: none;
+}
+
@media (max-width: 1024px) {
/* mobile mode*/
+ @import url('https://fonts.googleapis.com/css2?family=KoHo:wght@400;500;600;700;800&display=swap');
+
+ :root {
+ --header-color: #578FCA;
+ }
+
+ header {
+ position: fixed;
+ border-bottom-left-radius: 50px;
+ max-height: 60px;
+ justify-content: flex-start;
+ align-items: center;
+ z-index:1000;
+ }
+
+ header .title {
+ font-family: 'KoHo', sans-serif;
+ font-weight: 700;
+ /* font-size: 0.8em; */
+ }
+
+
+ header .title-containers {
+ margin-left: auto;
+ }
+
+ .dropdown-item a {
+ color: white;
+ text-decoration: none;
+ font-weight: 500;
+ display: block;
+ width: 100%;
+ padding: 0.5rem 1rem;
+ transition: color 0.2s ease, background-color 0.2s ease;
+ }
+
.big {
display: none;
}
@@ -76,7 +128,33 @@ header {
}
footer {
- border-radius: 20px 20px 0 0;
+ display: none;
+ }
+
+ .big footer {
+ display: none;
+ }
+
+
+ /* Show dropdown container on mobile */
+ .dropmenu-container {
+ display: block;
+ position: absolute; /* Change from 'fixed' to 'absolute' */
+ top: 10px;
+ left: 10px;
+ z-index: 999;
+ }
+
+
+ .dropdown-content {
+ position: fixed;
+ top: 60px;
+ left: 10px;
+ z-index: 500;
+ }
+
+ .feedback-container{
+ display:none;
}
}
@@ -86,6 +164,10 @@ header {
@media (min-width: 1024px) {
/* desktop mode*/
+ .dropmenu-container {
+ display: none;
+ }
+
.schedule {
display: block;
}
@@ -93,6 +175,7 @@ header {
footer {
border-radius: 0;
}
+
}
li a {
@@ -153,7 +236,6 @@ footer {
margin: 0;
}
-
.banner a {
color: #ffffff;
text-decoration: underline;
diff --git a/client/src/components/Button.jsx b/client/src/components/Button.jsx
new file mode 100644
index 000000000..e69de29bb
diff --git a/client/src/components/DropDown.css b/client/src/components/DropDown.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/client/src/components/DropDown.jsx b/client/src/components/DropDown.jsx
new file mode 100644
index 000000000..e69de29bb
diff --git a/client/src/components/Dropdown/Dropdown.css b/client/src/components/Dropdown/Dropdown.css
new file mode 100644
index 000000000..d34a4a4e8
--- /dev/null
+++ b/client/src/components/Dropdown/Dropdown.css
@@ -0,0 +1,5 @@
+.dropdown {
+ position: relative;
+ display: inline-block;
+ z-index: 1001; /* Add this */
+}
\ No newline at end of file
diff --git a/client/src/components/Dropdown/Dropdown.jsx b/client/src/components/Dropdown/Dropdown.jsx
new file mode 100644
index 000000000..078937b86
--- /dev/null
+++ b/client/src/components/Dropdown/Dropdown.jsx
@@ -0,0 +1,34 @@
+import React, { useState } from "react";
+import DropdownButton from "../DropdownButton/DropdownButton";
+import DropdownContent from "../DropdownContent/DropdownContent";
+import "./Dropdown.css";
+
+const Dropdown = ({ buttonText, content }) => {
+ const [open, setOpen] = useState(false);
+ const toggleDropdown = () => setOpen(!open);
+ const closeDropdown = () => setOpen(false);
+
+ return (
+
+
+ {buttonText}
+
+
+
+ {/* ✅ Wrap the content with a click handler */}
+ {
+ // only close when clicking a link, not when toggling dropdown
+ if (e.target.tagName === "A") {
+ closeDropdown();
+ }
+ }}
+ >
+ {content}
+
+
+
+ );
+};
+
+export default Dropdown;
diff --git a/client/src/components/DropdownButton/DropdownButton.css b/client/src/components/DropdownButton/DropdownButton.css
new file mode 100644
index 000000000..af0a57a93
--- /dev/null
+++ b/client/src/components/DropdownButton/DropdownButton.css
@@ -0,0 +1,40 @@
+/* .dropdown-btn{
+ display:flex;
+ align-items:center;
+ width:fit-content;
+ padding:1rem;
+ background-color:white;
+ border-radius: 0.5rem;
+ cursor:pointer;
+}
+
+.toggle-icon {
+ display:flex;
+ align-items:center;
+ justify-content:center;
+ margin-left:1rem;
+}
+
+.button-open {
+ border: #578FCA 2px solid;
+
+} */
+
+.dropdownbtn {
+ display: flex;
+ align-items: center;
+ width: fit-content;
+ margin-top: -20px;
+ margin-left: 20px;
+ padding: 0.15rem;
+ background-color: #578FCA;
+ border: 2px solid transparent; /* Always has same thickness */
+ border-radius: 0.5rem;
+ cursor: pointer;
+ transition: border-color 0.2s ease;
+ box-sizing: border-box; /* Ensures borders don’t change size */
+}
+
+.button-open {
+ border-color: #578FCA; /* Only the color changes */
+}
diff --git a/client/src/components/DropdownButton/DropdownButton.jsx b/client/src/components/DropdownButton/DropdownButton.jsx
new file mode 100644
index 000000000..5595a151c
--- /dev/null
+++ b/client/src/components/DropdownButton/DropdownButton.jsx
@@ -0,0 +1,20 @@
+import "./DropdownButton.css"
+// import {FaChevronDown} from "react-icons/fa"
+
+const DropdownButton = ({children, open, toggle}) => {
+ return (
+
+
+ {children}
+
+ {/* */}
+
+
+ )
+
+};
+
+export default DropdownButton;
\ No newline at end of file
diff --git a/client/src/components/DropdownContent/DropdownContent.css b/client/src/components/DropdownContent/DropdownContent.css
new file mode 100644
index 000000000..c9e6f911a
--- /dev/null
+++ b/client/src/components/DropdownContent/DropdownContent.css
@@ -0,0 +1,39 @@
+.dropdown-content {
+position: fixed;
+ top: 55px;
+ left: 10px;
+ z-index: 500;
+ width: 220px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding-top: 1rem;
+ margin-top: 0.5rem;
+ background-color: #124170;
+ border-radius: 0.5rem;
+ border: 1px solid #578FCA;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ max-height: 40vh;
+ overflow-y: scroll;
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+
+ /* Hidden by default */
+ opacity: 0;
+ visibility: hidden;
+ transform: translateY(-10px);
+ transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s;
+ pointer-events: none;
+}
+
+.dropdown-content::-webkit-scrollbar {
+ width: 0;
+ height: 0;
+}
+
+.content-open {
+ opacity: 1;
+ visibility: visible;
+ transform: translateY(0);
+ pointer-events: auto;
+}
\ No newline at end of file
diff --git a/client/src/components/DropdownContent/DropdownContent.jsx b/client/src/components/DropdownContent/DropdownContent.jsx
new file mode 100644
index 000000000..36a7b3e19
--- /dev/null
+++ b/client/src/components/DropdownContent/DropdownContent.jsx
@@ -0,0 +1,12 @@
+import React from "react";
+import "./DropdownContent.css";
+
+const DropdownContent = ({ children, open }) => {
+ return (
+
+ {children}
+
+ );
+};
+
+export default DropdownContent;
diff --git a/client/src/components/DropdownItem/DropdownItem.css b/client/src/components/DropdownItem/DropdownItem.css
new file mode 100644
index 000000000..a8a1440fb
--- /dev/null
+++ b/client/src/components/DropdownItem/DropdownItem.css
@@ -0,0 +1,11 @@
+.dropdown-item {
+ /* padding: 0.5rem; */
+ /* margin:0.1rem; */
+ padding-top:28px;
+ width:100%;
+ border-radius:0.5rem;
+ cursor:pointer;
+
+}
+/* .dropdown-item:hover {
+ background-color: rgba(87, 143, 202, 0.3); /* Semi-transparent blue */
diff --git a/client/src/components/DropdownItem/DropdownItem.jsx b/client/src/components/DropdownItem/DropdownItem.jsx
new file mode 100644
index 000000000..b9f3eb7b0
--- /dev/null
+++ b/client/src/components/DropdownItem/DropdownItem.jsx
@@ -0,0 +1,13 @@
+import React from "react";
+import "./DropdownItem.css"
+
+const DropdownItem = ({ children, onClick }) => {
+ return (
+
+ {children}
+
+ );
+};
+
+export default DropdownItem;
diff --git a/client/src/components/LoopToggle.jsx b/client/src/components/LoopToggle.jsx
new file mode 100644
index 000000000..95bec5513
--- /dev/null
+++ b/client/src/components/LoopToggle.jsx
@@ -0,0 +1,26 @@
+import { useState, useEffect, useLayoutEffect } from 'react';
+import '../styles/LoopToggle.css';
+import scheduleData from '../data/schedule.json';
+import routeData from '../data/routes.json';
+import { aggregatedSchedule } from '../data/parseSchedule';
+ import "../styles/LoopToggle.css";
+export default function LoopToggle() {
+
+ const [active, setActive] = useState("north");
+ return(
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/client/src/components/Navbar.css b/client/src/components/Navbar.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/client/src/components/Navbar.jsx b/client/src/components/Navbar.jsx
new file mode 100644
index 000000000..e69de29bb
diff --git a/client/src/components/Navigation.tsx b/client/src/components/Navigation.tsx
index f610adcb4..b273842dd 100644
--- a/client/src/components/Navigation.tsx
+++ b/client/src/components/Navigation.tsx
@@ -2,12 +2,61 @@ import { Link, Outlet } from "react-router";
import Feedback from "./Feedback";
import WarningBanner from "./WarningBanner";
import config from "../ts/config";
+import Dropdown from './components/Dropdown/Dropdown';
+import DropdownItem from './components/DropdownItem/DropdownItem';
-export default function Navigation({ GIT_REV }: { GIT_REV: string }) {
+
+function App() {
+ const [selectedRoute, setSelectedRoute] = useState(null);
+ const [selectedStop, setSelectedStop] = useState('all');
+ const staging = import.meta.env.VITE_DEPLOY_MODE !== 'production';
+ const GIT_REV = import.meta.env.GIT_REV || 'unknown';
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
+ const items = [
+ { label: 'About', path: '/about' },
+ { label: 'Live Location', path: '/' },
+ { label: 'Full Schedule', path: '/schedule' },
+];
+
+
+
+ // const toggleMenu = () => {
+ // setIsMenuOpen(!isMenuOpen);
+ // };
+
return (
<>
- SHUBBLE
+
+
+
+
+
+ }
+ content={
+ <>
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+ >
+ }
+ />
+
+
+
+ SHUBBLE
+
+
+
+
+
+
+ {/* {staging && } */}
{config.isStaging &&
}
diff --git a/client/src/components/Schedule.tsx b/client/src/components/Schedule.tsx
index 43621a3e6..6747d8031 100644
--- a/client/src/components/Schedule.tsx
+++ b/client/src/components/Schedule.tsx
@@ -92,7 +92,7 @@ export default function Schedule({ selectedRoute, setSelectedRoute }: SchedulePr
});
if (currentTimeRow) {
- currentTimeRow.scrollIntoView({ behavior: "auto" });
+ // currentTimeRow.scrollIntoView({ behavior: "auto" });
}
}, [selectedRoute, selectedDay, schedule]);
@@ -101,9 +101,10 @@ export default function Schedule({ selectedRoute, setSelectedRoute }: SchedulePr
return (
-
Schedule
-
-
+
Today's schedule
+
+ {/*
+
-
-
-
+
*/}
+ {/*
+
+
*/}
+
+
+
diff --git a/client/src/pages/LiveLocation.tsx b/client/src/pages/LiveLocation.tsx
index c1444d741..53c7d3690 100644
--- a/client/src/pages/LiveLocation.tsx
+++ b/client/src/pages/LiveLocation.tsx
@@ -11,6 +11,7 @@ import type { VehicleInformationMap } from '../ts/types/vehicleLocation';
import type { ShuttleRouteData } from '../ts/types/route';
import aggregatedSchedule from '../data/aggregated_schedule.json';
+
export default function LiveLocation() {
const [location, setLocation] = useState(null);
@@ -63,7 +64,7 @@ export default function LiveLocation() {
selectedRoute={selectedRoute}
setSelectedRoute={setSelectedRoute}
/>
-