Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion data/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"parkingRoutePace": {
"walkMinutesPerMile": 24,
"dashMilesPerHour": 12,
"dashBoardingWaitMinutes": 5
"dashBoardingWaitMinutes": 5,
"driveMilesPerHour": 25
},
"linkTexts": {
"viewTransitRoute": "View transit route on Google Maps",
Expand Down
8 changes: 8 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,14 @@ <h2 class="font-semibold text-lg">How each mode works</h2>
</div>
<div class="relative min-h-0 flex-1 min-w-0 w-full">
<div id="parkingAppMap" class="absolute inset-0 min-h-0 w-full"></div>
<button
type="button"
id="parkingLocateBtn"
class="parking-map-control-help parking-map-control-locate"
aria-label="Show my location on the map"
aria-pressed="false"
title="Show my location on the map"
></button>
<button
type="button"
id="parkingLegendHelpBtn"
Expand Down
17 changes: 13 additions & 4 deletions src/bootstrap.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
haversineMiles,
roundCoord5,
MODES_PAGE_EMPTY_MAP_CENTER,
FALLBACK_DATA,
PARKING_PRICE_NOT_LISTED_LABEL,
getParkingDataViewOverrideSourceFields,
} from "./shared/data-loader.mjs";
Expand Down Expand Up @@ -108,9 +107,7 @@ const DATA_ROUTES_STOP_MAX_MILES_FROM_CENTER = 1.5;
let validModes = null;

function modesPageOrderedList() {
const base = Array.isArray(validModes)
? validModes
: FALLBACK_DATA.validModes;
const base = Array.isArray(validModes) ? validModes : [];
return MODES_PAGE_ORDER.filter((m) => base.includes(m));
}

Expand Down Expand Up @@ -2404,6 +2401,18 @@ async function init() {
prepareParkingShellVisibility();
}
await loadData();
if (!appData) {
const appView = document.getElementById("appView");
if (appView) {
appView.textContent =
"Could not load app data. Check the console and refresh.";
appView.classList.remove("hidden");
}
hideDataView();
hideModesView();
hideParkingView();
return;
}
rewriteDeferredDestinationHashIfNeeded();
if (isParkingRoute()) {
prepareParkingShellVisibility();
Expand Down
54 changes: 1 addition & 53 deletions src/shared/data-loader.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -419,58 +419,6 @@ function applyDriveParkingDatasetDisplayNames(parking) {
parking.categoryNames.airGarageLots = "Private Parking Lots (AirGarage)";
}

export const FALLBACK_DATA = {
validModes: [
"drive",
"rideshare",
"transit",
"micromobility",
"shuttle",
"bike",
],
modeLabels: {
drive: "driving",
rideshare: "Uber/Lyft",
transit: "The Rapid",
bike: "biking",
micromobility: "Lime",
walk: "walking",
shuttle: "DASH",
},
costLabels: {
drive: "Willing to pay",
rideshare: "Willing to pay",
transit: "Willing to pay",
bike: "Willing to pay",
micromobility: "Willing to pay",
walk: "Willing to pay",
shuttle: "Willing to pay",
},
defaults: {
flexibilityEarlyMins: 15,
flexibilityLateMins: 0,
people: 1,
walkMiles: 1.5,
parkingMins: 10,
costDollars: 40,
},
parkingPrivateUnknown: {
lotAssumedDollars: 20,
garageAssumedDollars: 30,
cardCopy:
"Typical cost is a planning estimate when no rate is listed—confirm posted prices before you park.",
},
parkingRoutePace: {
walkMinutesPerMile: 24,
dashMilesPerHour: 12,
dashBoardingWaitMinutes: 5,
},
destinations: [],
linkTexts: {},
parking: {},
busRoutes: null,
};

export let appData = null;

/** When true, the venue stays out of browse UI and map placeholder pins until linked (e.g. `#/<slug>` → `#/visit/<slug>`). */
Expand Down Expand Up @@ -609,6 +557,6 @@ export async function loadData() {
};
} catch (error) {
console.error("Failed to load data:", error);
appData = { ...FALLBACK_DATA };
appData = null;
}
}
10 changes: 9 additions & 1 deletion src/visit/route-planning.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ export const FALLBACK_PARKING_WALK_MINUTES_PER_MILE = 24;
export const FALLBACK_PARKING_DASH_MILES_PER_HOUR = 12;
/** Typical wait at the stop before the next DASH shuttle (`#/visit` multimodal time). */
export const FALLBACK_PARKING_DASH_BOARDING_WAIT_MINUTES = 5;
/** Urban driving pace for `#/visit` drive-step estimates from the user's location. */
export const FALLBACK_PARKING_DRIVE_MILES_PER_HOUR = 25;

/**
* @param {unknown} configObj — `appData.parkingRoutePace` or subset
* @returns {{ walkMinutesPerMile: number; dashMilesPerHour: number; dashBoardingWaitMinutes: number }}
* @returns {{ walkMinutesPerMile: number; dashMilesPerHour: number; dashBoardingWaitMinutes: number; driveMilesPerHour: number }}
*/
export function resolveParkingRoutePace(configObj) {
const o = configObj != null && typeof configObj === "object" ? configObj : {};
Expand All @@ -20,6 +22,8 @@ export function resolveParkingRoutePace(configObj) {
const d = /** @type {{ dashMilesPerHour?: unknown }} */ (o).dashMilesPerHour;
const wait = /** @type {{ dashBoardingWaitMinutes?: unknown }} */ (o)
.dashBoardingWaitMinutes;
const drive = /** @type {{ driveMilesPerHour?: unknown }} */ (o)
.driveMilesPerHour;
return {
walkMinutesPerMile:
typeof w === "number" && Number.isFinite(w) && w > 0
Expand All @@ -33,6 +37,10 @@ export function resolveParkingRoutePace(configObj) {
typeof wait === "number" && Number.isFinite(wait) && wait >= 0
? wait
: FALLBACK_PARKING_DASH_BOARDING_WAIT_MINUTES,
driveMilesPerHour:
typeof drive === "number" && Number.isFinite(drive) && drive > 0
? drive
: FALLBACK_PARKING_DRIVE_MILES_PER_HOUR,
};
}

Expand Down
27 changes: 23 additions & 4 deletions src/visit/visit.css
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,25 @@ main.parking-map-active {
flex-shrink: 0;
}

.parking-map-control-locate {
top: 48px;
}

.parking-map-control-locate-icon {
width: 23px;
height: 23px;
}

.parking-map-control-locate--active {
color: rgb(202 138 4);
border-color: rgb(202 138 4);
}

.parking-map-control-help[aria-busy="true"] {
opacity: 0.65;
cursor: wait;
}

/* Parking legend modal — Leaflet fills the Tailwind-sized tile */
.parking-legend-mini-map.leaflet-container {
height: 100%;
Expand Down Expand Up @@ -187,11 +206,11 @@ main.parking-map-active {
border: 1px solid transparent;
}

/* Drive (park here) — green fill + faint neutral edge (slate-300) */
/* Drive (from your location) — yellow to match the map drive line / steering-wheel pin */
.parking-route-step-badge--drive {
color: rgb(22 101 52);
background-color: rgb(220 252 231);
border-color: rgb(203 213 225);
color: rgb(146 64 14);
background-color: rgb(254 249 195);
border-color: rgb(250 204 21);
}

.parking-route-step-badge--walk {
Expand Down
Loading
Loading