diff --git a/client/index.html b/client/index.html index ae866848..61bfb7fd 100644 --- a/client/index.html +++ b/client/index.html @@ -4,6 +4,12 @@ Shubble + + + + + +
diff --git a/client/src/App.css b/client/src/App.css index da10bf95..9862b02b 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -1,6 +1,6 @@ :root { --text-color: #141301; - --header-color: #a1c3ff; + --header-color: #578FCA; } .App { @@ -14,12 +14,21 @@ width: 100%; } +.title, +.title:link, +.title:visited{ + font-family: "KoHo", sans-serif; + font-weight: 700; + color: var(--text-color); + text-decoration: none; +} + nav.small ul { margin: 0; padding: 0; box-sizing: border-box; display: flex; - gap: 10px; + gap: 50px; flex-direction: row; width: 100%; list-style: none; @@ -29,13 +38,15 @@ nav.small ul { nav.big ul { margin: 0; padding: 0; + padding-top:10px; box-sizing: border-box; display: flex; gap: 30px; flex-direction: row; width: 100%; list-style: none; - font-size: 0.8em; + font-size: 0.6em; + font-family: "Karla", sans-serif; } li { @@ -54,15 +65,14 @@ header { box-sizing: border-box; max-height: 100px; background-color: var(--header-color); - padding: 20px; + padding: 10px; margin: 0; font-size: 2em; - font-weight: bold; z-index: 1000; display: flex; flex-direction: row; - justify-content: space-between; - gap: 10px; + gap: 30px; + justify-content: center; } @media (max-width: 1024px) { @@ -77,6 +87,7 @@ header { footer { border-radius: 20px 20px 0 0; + background-color: #e1e1e1; } } @@ -92,6 +103,7 @@ header { footer { border-radius: 0; + /* background-color: #f0f0f0; */ } } @@ -118,14 +130,21 @@ footer { padding: 20px; box-sizing: border-box; width: 100%; - background-color: #ddd; + /* background-color: ; */ + justify-content: center; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; } .big-footer { display: flex; - flex-direction: row; - justify-content: space-between; + flex-direction: column; + justify-content: center; align-items: center; + font-family: "Karla", sans-serif; + background-color: #ffffff; } .git-copy { @@ -137,36 +156,12 @@ footer { padding: 0 20px; } -.banner { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - width: 100%; - background-color: #184b7f; - color: #ffffff; - padding: 1px; - box-sizing: border-box; - text-align: center; - font-weight: bold; - font-size: 18px; - margin: 0; -} - - -.banner a { - color: #ffffff; - text-decoration: underline; -} - -.banner p { - margin: 4px 0; +.line { + height: 1px; + background-color: #d9d9d9; + width: 1200px; } - -.banner * { - margin: 5px; -} - +/* /*shubble text link styling*/ .site-title, .site-title:link, @@ -174,4 +169,4 @@ footer { color: var(--text-color); text-decoration: none; /* remove underline */ } - + */ diff --git a/client/src/components/Navigation.tsx b/client/src/components/Navigation.tsx index f610adcb..ffa91e5b 100644 --- a/client/src/components/Navigation.tsx +++ b/client/src/components/Navigation.tsx @@ -7,7 +7,7 @@ export default function Navigation({ GIT_REV }: { GIT_REV: string }) { return ( <>
- SHUBBLE + SHUBBLE
+
diff --git a/client/src/components/RouteToggle.jsx b/client/src/components/RouteToggle.jsx new file mode 100644 index 00000000..e2988f14 --- /dev/null +++ b/client/src/components/RouteToggle.jsx @@ -0,0 +1,25 @@ +import '../styles/RouteToggle.css'; +import rawAggregatedSchedule from '../data/aggregated_schedule.json'; + +export default function RouteToggle({selectedRoute, setSelectedRoute}) { + const today = new Date(); + const keys = Object.keys(rawAggregatedSchedule[today.getDay()]) + + // const [active, setActive] = useState("north"); + return( +
+ + +
+ ); +} \ No newline at end of file diff --git a/client/src/components/Schedule.tsx b/client/src/components/Schedule.tsx index 43621a3e..48b2553c 100644 --- a/client/src/components/Schedule.tsx +++ b/client/src/components/Schedule.tsx @@ -2,8 +2,9 @@ import { useState, useEffect } from 'react'; import '../styles/Schedule.css'; import rawRouteData from '../data/routes.json'; import rawAggregatedSchedule from '../data/aggregated_schedule.json'; +import RouteToggle from './RouteToggle'; import type { AggregatedDaySchedule, AggregatedScheduleType } from '../ts/types/schedule'; -import type { ShuttleRouteData, ShuttleStopData } from '../ts/types/route'; +import type { ShuttleRouteData } from '../ts/types/route'; const aggregatedSchedule: AggregatedScheduleType = rawAggregatedSchedule as unknown as AggregatedScheduleType; @@ -14,17 +15,19 @@ type ScheduleProps = { setSelectedRoute: (route: string | null) => void; }; -export default function Schedule({ selectedRoute, setSelectedRoute }: ScheduleProps) { +export default function Schedule() { // Validate props once at the top - if (typeof setSelectedRoute !== 'function') { - throw new Error('setSelectedRoute must be a function'); - } + // if (typeof setSelectedRoute !== 'function') { + // throw new Error('setSelectedRoute must be a function'); + // } const now = new Date(); - const [selectedDay, setSelectedDay] = useState(now.getDay()); + const [selectedDay] = useState(now.getDay()); const [routeNames, setRouteNames] = useState(Object.keys(aggregatedSchedule[selectedDay])); const [stopNames, setStopNames] = useState([]); const [schedule, setSchedule] = useState(aggregatedSchedule[selectedDay]); + const [selectedStop, setSelectedStop] = useState("all"); + const [selectedRoute, setSelectedRoute] = useState(routeNames[0]); // Define safe values to avoid repeated null checks const safeSelectedRoute = selectedRoute || routeNames[0]; @@ -40,17 +43,6 @@ export default function Schedule({ selectedRoute, setSelectedRoute }: SchedulePr } }, [selectedDay, selectedRoute, setSelectedRoute]); - // Update stopNames when selectedRoute changes - useEffect(() => { - if (!safeSelectedRoute || !(safeSelectedRoute in routeData)) return; - setStopNames(routeData[safeSelectedRoute as keyof typeof routeData].STOPS); - }, [selectedRoute]); - - // Handle day change from dropdown - const handleDayChange = (e: React.ChangeEvent) => { - setSelectedDay(parseInt(e.target.value)); - } - const timeToDate = (timeStr: string): Date => { const [time, modifier] = timeStr.trim().split(" "); @@ -69,6 +61,23 @@ export default function Schedule({ selectedRoute, setSelectedRoute }: SchedulePr return dateObj; } + // Update stopNames when selectedRoute changes + useEffect(() => { + if (!safeSelectedRoute || !(safeSelectedRoute in routeData)) return; + setStopNames(routeData[safeSelectedRoute as keyof typeof routeData].STOPS); + }, [selectedRoute]); + + useEffect(() => { + setSchedule(aggregatedSchedule[selectedDay]); + setRouteNames(Object.keys(aggregatedSchedule[selectedDay])); + }, [selectedDay]); + useEffect(() => { + if (!(selectedStop in rawRouteData[selectedRoute])) { + setSelectedStop("all"); + } + setStopNames(routeData[selectedRoute].STOPS); + }, [selectedRoute]); + // Function to offset schedule time by given minutes const offsetTime = (time: string, offset: number) => { const date = timeToDate(time); @@ -81,7 +90,7 @@ export default function Schedule({ selectedRoute, setSelectedRoute }: SchedulePr const scheduleDiv = document.querySelector('.schedule-scroll'); if (!scheduleDiv) return; - if (selectedDay !== now.getDay()) return; // only scroll if viewing today's schedule + // if (selectedDay !== now.getDay()) return; // only scroll if viewing today's schedule const currentTimeRow = Array.from(scheduleDiv.querySelectorAll('td.outdented')).find(td => { const timeStr = td.textContent?.trim(); @@ -94,33 +103,22 @@ export default function Schedule({ selectedRoute, setSelectedRoute }: SchedulePr if (currentTimeRow) { currentTimeRow.scrollIntoView({ behavior: "auto" }); } - }, [selectedRoute, selectedDay, schedule]); - + }, [selectedRoute, selectedDay, selectedStop]); - const daysOfTheWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; return (
-

Schedule

-
- - -
+

Today's schedule

+ +
- - setSelectedStop(e.target.value)}> + { - routeNames.map((route, index) => - ) } diff --git a/client/src/styles/LiveLocation.css b/client/src/styles/LiveLocation.css index d8c236d2..64fab1b5 100644 --- a/client/src/styles/LiveLocation.css +++ b/client/src/styles/LiveLocation.css @@ -4,8 +4,8 @@ gap: 40px; width: 90%; justify-content: center; - padding-top: 20px; - padding-bottom: 20px; + padding: 20px; + box-sizing:border-box; } .schedule-table { diff --git a/client/src/styles/MapKitMap.css b/client/src/styles/MapKitMap.css index d29a6045..1e4e1090 100644 --- a/client/src/styles/MapKitMap.css +++ b/client/src/styles/MapKitMap.css @@ -1,6 +1,6 @@ .map { - width: 800px; - height: 500px; + width: 760px; + height: 450px; touch-action: manipulation; -webkit-overflow-scrolling: touch; background-color: #f0f0f0; diff --git a/client/src/styles/RouteToggle.css b/client/src/styles/RouteToggle.css new file mode 100644 index 00000000..df0d703d --- /dev/null +++ b/client/src/styles/RouteToggle.css @@ -0,0 +1,47 @@ +/* want 2 north button classes, one for on and off*/ + +button { + width: 140px; + height: 36px; + border-radius: 0; + border-width: 0; + font-family: "Karla", sans-serif; + color:white; +} + +button:hover { + cursor: pointer; +} + +.north-off { + background-color:red; + opacity: 0.42; + font-size: 20px; + box-shadow: inset 1px 4px 4px rgba(0, 0, 0, 0.4); +} + +.north-on { + background-color: red; + font-size: 24px; + font-weight:bold; + box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.20); +} + +.west-off { + background-color: blue; + opacity:0.3; + font-size:20px; + box-shadow: inset 1px 4px 4px rgba(0, 0, 0, 0.4); +} + +.west-on { + background-color: blue; + font-size: 24px; + font-weight: bold; + box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.20); +} + +.toggle-div { + display: flex; + flex-direction: row; +} \ No newline at end of file diff --git a/client/src/styles/Schedule.css b/client/src/styles/Schedule.css index 39162853..d648a025 100644 --- a/client/src/styles/Schedule.css +++ b/client/src/styles/Schedule.css @@ -32,3 +32,16 @@ th.schedule-header { text-align: left; } + +h2 { + font-family: "Karla", sans-serif; + font-weight: bold; + font-size:30px; + margin-bottom: 10px; + margin-top:10px +} + +label { + font-family: "Karla", sans-serif; + font-size: 20px; +} \ No newline at end of file diff --git a/frontend-documentation.md b/frontend-documentation.md new file mode 100644 index 00000000..f7ff85c5 --- /dev/null +++ b/frontend-documentation.md @@ -0,0 +1,6 @@ +# Shubble Frontend Documentation +Shubble pages (About, Live Location, Schedule) are located within ./cliemt/src/pages. +Components are located at ./cliemt/src/components +CSS styling is written as classes, not in-line, and are located within ./cliemt/src/styles. This includes styling for all pages and components. + +App.tsx renders all pages, and each page renders the components. \ No newline at end of file