Skip to content

Commit

Permalink
Merge branch 'main' into mattapan-trolley
Browse files Browse the repository at this point in the history
  • Loading branch information
devinmatte authored Dec 6, 2024
2 parents a7c8105 + 7f09c7a commit 556ffa5
Show file tree
Hide file tree
Showing 107 changed files with 8,755 additions and 9,331 deletions.
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,18 @@ This is the repository for the TransitMatters Data Dashboard. Client code is wri

1. Add `MBTA_V3_API_KEY` to your shell environment:
- `export MBTA_V3_API_KEY='KEY'` in ~/.bashrc or ~/.zshrc
2. Add your AWS credentials (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY) to your shell environment, OR add them to a .boto config file with awscli command `aws configure`.
3. In the root directory, run `npm install` to install all frontend and backend dependencies
4. Run `npm start` to start both the JavaScript development server and the Python backend at the same time.
5. Navigate to [http://localhost:3000](http://localhost:3000) (or the url provided after running `npm start`)
2. In the root directory, run `npm install` to install all frontend and backend dependencies
3. Run `npm start` to start both the JavaScript development server and the Python backend at the same time.
4. Navigate to [http://localhost:3000](http://localhost:3000) (or the url provided after running `npm start`)

### AWS Setup

If you have access to AWS credentials, add them to your local setup for a better development experience

1. Add your AWS credentials (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY) to your shell environment, OR add them to a .boto config file with awscli command `aws configure`.
2. Ensure that AWS is set to read from/to `us-east-1` via `export AWS_DEFAULT_REGION=us-east-1` or with an aws config

AWS access is not required for development, but may be required for certain charts and data to appear

## Deployment Instructions

Expand Down
15 changes: 15 additions & 0 deletions common/api/delays.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { FetchAlertDelaysByLineParams, type FetchAlertDelaysByLineOptions } from '../types/api';
import type { LineDelays } from '../types/delays';
import { apiFetch } from './utils/fetch';

export const fetchLineDelaysByLine = async (
options: FetchAlertDelaysByLineOptions
): Promise<LineDelays[]> => {
if (!options[FetchAlertDelaysByLineParams.line]) return [];

return await apiFetch({
path: '/api/linedelays',
options,
errorMessage: 'Failed to fetch delay metrics',
});
};
13 changes: 13 additions & 0 deletions common/api/hooks/delays.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useQuery } from '@tanstack/react-query';
import { ONE_HOUR } from '../../constants/time';
import { fetchLineDelaysByLine } from '../delays';
import type { FetchAlertDelaysByLineOptions } from '../../types/api';

export const useAlertDelays = (options: FetchAlertDelaysByLineOptions, enabled?: boolean) => {
return useQuery({
queryKey: ['lineDelays', options],
queryFn: () => fetchLineDelaysByLine(options),
enabled: enabled,
staleTime: ONE_HOUR,
});
};
51 changes: 42 additions & 9 deletions common/components/charts/ByHourHistogram/ByHourHistogram.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useMemo } from 'react';
import React, { useMemo, useRef } from 'react';
import { Bar as BarChart } from 'react-chartjs-2';
import ChartjsPluginWatermark from 'chartjs-plugin-watermark';
import {
Chart as ChartJS,
CategoryScale,
Expand All @@ -10,7 +11,9 @@ import {
} from 'chart.js';
import Color from 'color';

import type { Context } from 'chartjs-plugin-datalabels';
import { useBreakpoint } from '../../../hooks/useBreakpoint';
import { watermarkLayout } from '../../../constants/charts';
import type { ByHourDataset, DisplayStyle, ValueAxis as ValueAxis } from './types';
import { resolveStyle } from './styles';

Expand All @@ -20,6 +23,8 @@ interface Props {
style?: Partial<DisplayStyle>;
data: ByHourDataset[];
valueAxis: ValueAxis;
/** Whether the dataset is roundtrips, and we want to additionally convert and show the values as headways */
datasetRoundTrips?: boolean;
}

const allTimeLabels = ['AM', 'PM']
Expand Down Expand Up @@ -51,13 +56,33 @@ const stripZeroHoursAndRotateMidnightToEnd = (
};

export const ByHourHistogram: React.FC<Props> = (props) => {
const { data: dataWithZeros, valueAxis, style: baseStyle = null } = props;
const {
data: dataWithZeros,
valueAxis,
style: baseStyle = null,
datasetRoundTrips = false,
} = props;
const { data, timeLabels } = useMemo(
() => stripZeroHoursAndRotateMidnightToEnd(dataWithZeros),
[dataWithZeros]
);
const ref = useRef();
const isMobile = !useBreakpoint('md');

const tooltipFormat = React.useCallback(
({ datasetIndex, dataIndex }: Context) => {
const dataset = data[datasetIndex];
const value = dataset.data[dataIndex];
const { label } = dataset;

if (datasetRoundTrips) {
return `${label}: ${value} ${valueAxis.tooltipItemLabel ?? ''} (${Math.round(60 / value)}m headways)`.trim();
}
return `${label}: ${value} ${valueAxis.tooltipItemLabel ?? ''}`.trim();
},
[data, datasetRoundTrips, valueAxis.tooltipItemLabel]
);

const chartData = useMemo(() => {
return {
labels: timeLabels,
Expand Down Expand Up @@ -96,6 +121,9 @@ export const ByHourHistogram: React.FC<Props> = (props) => {
},
},
},
responsive: true,
maintainAspectRatio: false,
watermark: watermarkLayout(isMobile),
plugins: {
legend: {
display: true,
Expand All @@ -105,17 +133,22 @@ export const ByHourHistogram: React.FC<Props> = (props) => {
mode: 'index' as const,
callbacks: {
label: (context) => {
const { datasetIndex, dataIndex } = context;
const dataset = data[datasetIndex];
const value = dataset.data[dataIndex];
const { label } = dataset;
return `${label}: ${value} ${valueAxis.tooltipItemLabel ?? ''}`.trim();
return tooltipFormat(context);
},
},
},
},
};
}, [valueAxis.title, valueAxis.tooltipItemLabel, data]);
}, [valueAxis.title, isMobile, tooltipFormat]);

return <BarChart data={chartData} options={chartOptions} height={isMobile ? 50 : 70} />;
return (
<BarChart
redraw
ref={ref}
data={chartData}
options={chartOptions}
height={isMobile ? 200 : 75}
plugins={[ChartjsPluginWatermark]}
/>
);
};
2 changes: 1 addition & 1 deletion common/components/charts/ChartDiv.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ interface ChartDivProps {

export const ChartDiv: React.FC<ChartDivProps> = ({ children, isMobile = false }) => {
return (
<div className={classNames(isMobile ? 'h-50' : 'h-60', 'flex w-full flex-row')}>{children}</div>
<div className={classNames(isMobile ? 'h-48' : 'h-60', 'flex w-full flex-row')}>{children}</div>
);
};
4 changes: 3 additions & 1 deletion common/components/charts/TimeSeriesChart/TimeSeriesChart.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useMemo } from 'react';
import { Line as LineChart } from 'react-chartjs-2';
import ChartjsPluginWatermark from 'chartjs-plugin-watermark';
import {
Chart as ChartJS,
CategoryScale,
Expand All @@ -17,7 +18,6 @@ import {
} from 'chart.js';
import Annotation from 'chartjs-plugin-annotation';
import ChartDataLabels from 'chartjs-plugin-datalabels';
import ChartjsPluginWatermark from 'chartjs-plugin-watermark';
import 'chartjs-adapter-date-fns';
import type { ChartData } from 'chart.js';

Expand All @@ -26,6 +26,7 @@ import { useBreakpoint } from '../../../hooks/useBreakpoint';
import { ChartDiv } from '../ChartDiv';
import { CHART_COLORS, COLORS } from '../../../constants/colors';

import { watermarkLayout } from '../../../constants/charts';
import type {
AppliedDisplayStyle,
Benchmark,
Expand Down Expand Up @@ -225,6 +226,7 @@ export const TimeSeriesChart = <Data extends Dataset[]>(props: Props<Data>) => {
interaction: {
intersect: false,
},
watermark: watermarkLayout(isMobile),
plugins: {
datalabels: {
display: false,
Expand Down
34 changes: 34 additions & 0 deletions common/components/inputs/BranchSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { ButtonGroup } from '../general/ButtonGroup';
import type { LineRouteId } from '../../types/lines';

interface BranchSelectorProps {
routeId: LineRouteId;
setRouteId: (routeId: LineRouteId) => void;
}

enum GreenLineBranchOptions {
'Green-B' = 'B Branch',
'Green-C' = 'C Branch',
'Green-D' = 'D Branch',
'Green-E' = 'E Branch',
}

export const BranchSelector: React.FunctionComponent<BranchSelectorProps> = ({
routeId,
setRouteId,
}) => {
const selectedIndex = Object.keys(GreenLineBranchOptions).findIndex((route) => route === routeId);

return (
<div className={'flex w-full justify-center pt-2'}>
<ButtonGroup
selectedIndex={selectedIndex}
pressFunction={setRouteId}
options={Object.entries(GreenLineBranchOptions)}
additionalDivClass="md:w-auto"
additionalButtonClass="md:w-fit"
/>
</div>
);
};
8 changes: 5 additions & 3 deletions common/components/inputs/DateSelection/DatePickers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,11 @@ export const DatePickers: React.FC<DatePickerProps> = ({ range, setRange, type,
placeholder={'mm/dd/yyyy'}
options={FLAT_PICKER_OPTIONS[tab]}
onChange={(dates, currentDateString) => {
isSingleDate
? handleDateChange(currentDateString)
: handleStartDateChange(currentDateString);
if (isSingleDate) {
handleDateChange(currentDateString);
} else {
handleStartDateChange(currentDateString);
}
}}
onMonthChange={() => updateColor(line)}
onOpen={() => updateColor(line)}
Expand Down
8 changes: 5 additions & 3 deletions common/components/nav/CommuterRailDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { SidebarTabs } from '../../../modules/navigation/SidebarTabs';
import { TRIP_PAGES, COMMUTER_RAIL_OVERVIEW } from '../../constants/pages';
import { COMMUTER_RAIL_OVERVIEW } from '../../constants/pages';
import { CommuterRailRouteSelection } from './CommuterRailRouteSelection';

interface CommuterRailDropdownProps {
Expand All @@ -18,8 +18,10 @@ export const CommuterRailDropdown: React.FC<CommuterRailDropdownProps> = ({ clos
role={'navigation'}
>
<SidebarTabs tabs={COMMUTER_RAIL_OVERVIEW} close={close} />
<hr className="h-[1px] w-3/4 self-center border-neutral-500" />
<SidebarTabs tabs={TRIP_PAGES} close={close} />

{/* TODO: Once we have reliable travel data for CR */}
{/* <hr className="h-[1px] w-3/4 self-center border-neutral-500" /> */}
{/* <SidebarTabs tabs={TRIP_PAGES} close={close} /> */}
</div>
</div>
);
Expand Down
21 changes: 13 additions & 8 deletions common/components/nav/MenuDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import classNames from 'classnames';
import Link from 'next/link';
import React, { useEffect, useState } from 'react';
import { LINE_OBJECTS } from '../../constants/lines';
import { BUS_DEFAULTS } from '../../state/defaults/dateDefaults';
import { BUS_DEFAULTS, COMMUTER_RAIL_DEFAULTS } from '../../state/defaults/dateDefaults';
import { lineColorBackground } from '../../styles/general';
import type { Line } from '../../types/lines';
import type { Route } from '../../types/router';
Expand Down Expand Up @@ -39,15 +39,20 @@ export const MenuDropdown: React.FC<MenuDropdownProps> = ({ line, route, childre
}
}, [line]);

const href = React.useMemo(() => {
switch (line) {
case 'line-bus':
return `/bus/trips/single?busRoute=1&date=${BUS_DEFAULTS.singleTripConfig.date}`;
case 'line-commuter-rail':
return `/commuter-rail/ridership?crRoute=CR-Lowell&startDate=${COMMUTER_RAIL_DEFAULTS.lineConfig.startDate}&endDate=${COMMUTER_RAIL_DEFAULTS.lineConfig.endDate}`;
default:
return getLineSelectionItemHref(line, route);
}
}, [line, route]);

return (
<div className={classNames('w-full')}>
<Link
href={
line === 'line-bus'
? `/bus/trips/single?busRoute=1&date=${BUS_DEFAULTS.singleTripConfig.date}`
: getLineSelectionItemHref(line, route)
}
>
<Link href={href}>
<div
className={classNames(
'flex w-full flex-row items-center gap-2 rounded-t-md py-1 pl-1 text-sm',
Expand Down
9 changes: 5 additions & 4 deletions common/components/notices/BetaDataNotice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const BetaDataNotice: React.FC = () => {
const isEndDateAfterBusMaxDay = endDate !== undefined && dayjs(endDate).isAfter(BUS_MAX_DAY);

if (
(line === 'line-bus' || linePath === 'bus') &&
(line === 'line-commuter-rail' || linePath === 'commuter-rail') &&
(isStartDateAfterBusMaxDay || isEndDateAfterBusMaxDay)
) {
return (
Expand All @@ -30,7 +30,9 @@ export const BetaDataNotice: React.FC = () => {
<ExclamationTriangleIcon className="h-5 w-5 text-yellow-400" aria-hidden="true" />
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-yellow-800">Real-time bus data is in beta</h3>
<h3 className="text-sm font-medium text-yellow-800">
Real-time Commuter Rail data is in beta
</h3>
<div className="mt-2 text-sm text-yellow-700">
<p>
TransitMatters collects this data using the{' '}
Expand All @@ -46,8 +48,7 @@ export const BetaDataNotice: React.FC = () => {
Please expect reduced accuracy.
</p>
<p>
We favor official performance data from the MBTA when it's available. Technical
details of our data collection are available in our{' '}
Technical details of our data collection are available in our{' '}
<Link
href="https://github.com/transitmatters/gobble"
rel="noopener noreferrer"
Expand Down
6 changes: 4 additions & 2 deletions common/components/notices/GobbleDataNotice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ export const GobbleDataNotice: React.FC = () => {
const isEndDateAfterBusMaxDay = endDate !== undefined && dayjs(endDate).isAfter(BUS_MAX_DAY);

if (
(line === 'line-bus' || linePath === 'bus') &&
(isStartDateAfterBusMaxDay || isEndDateAfterBusMaxDay)
((line === 'line-bus' || linePath === 'bus') &&
(isStartDateAfterBusMaxDay || isEndDateAfterBusMaxDay)) ||
line === 'line-commuter-rail' ||
linePath === 'commuter-rail'
) {
return (
<div className={'flex items-center'}>
Expand Down
Loading

0 comments on commit 556ffa5

Please sign in to comment.