Skip to content

Commit

Permalink
Fixes filtering and chart sizing (#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
devinmatte authored Jun 8, 2024
1 parent 88a3895 commit 7f936cd
Show file tree
Hide file tree
Showing 10 changed files with 1,994 additions and 1,966 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/first-interaction.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: first-contribution

on:
pull_request:
branches: [main]
types: [opened]

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/first-interaction@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
pr-message: |
Hello! Thank you for your contribution.
TransitMatters is a volunteer-run organization, so if you haven't yet we'd appreciate you filling out our volunteer form: https://transitmatters.org/volunteer
If you have already filled out the form, but have not yet heard back please let us know on this PR and we'll follow up with you!
3,694 changes: 1,840 additions & 1,854 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.7",
"postcss": "^8.4.38",
"prettier": "3.2.5",
"tailwindcss": "^3.4.3",
"prettier": "3.3.1",
"tailwindcss": "^3.4.4",
"typescript": "^5.4.5",
"vite": "^5.2.11"
"vite": "^5.2.13"
}
}
54 changes: 28 additions & 26 deletions src/components/CalendarSubscribeButton.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { faCalendarPlus } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Dialog, Transition } from '@headlessui/react';
import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react';
import { CheckIcon, DocumentDuplicateIcon } from '@heroicons/react/16/solid';
import React from 'react';
import { useState } from 'react';
import { Fragment } from 'react/jsx-runtime';
import { useCopyToClipboard } from 'usehooks-ts';

interface SubscribeModalProps {
Expand All @@ -15,44 +15,46 @@ const SubscribeModal: React.FC<SubscribeModalProps> = ({ isOpen, setIsOpen }) =>
const [, copy] = useCopyToClipboard();
const [copied, setCopied] = useState(false);

const closeModal = () => {
setIsOpen(false);
};

return (
<Transition.Root show={isOpen} as={Fragment}>
<Dialog className="relative z-10" onClose={setIsOpen}>
<Transition.Child
as={Fragment}
<Transition appear show={isOpen}>
<Dialog className="relative z-10" onClose={closeModal}>
<TransitionChild
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
enterFrom="opacity-0 transform-[scale(95%)]"
enterTo="opacity-100 transform-[scale(100%)]"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
leaveFrom="opacity-100 transform-[scale(100%)]"
leaveTo="opacity-0 transform-[scale(95%)]"
>
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
</Transition.Child>
</TransitionChild>

<div className="fixed inset-0 z-10 w-screen overflow-y-auto">
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<Transition.Child
as={Fragment}
<TransitionChild
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
enterFrom="opacity-0 transform-[scale(95%)]"
enterTo="opacity-100 transform-[scale(100%)]"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
leaveFrom="opacity-100 transform-[scale(100%)]"
leaveTo="opacity-0 transform-[scale(95%)]"
>
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white dark:bg-gray-500 px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6">
<DialogPanel className="relative transform overflow-hidden rounded-lg bg-white dark:bg-gray-500 px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6">
<div>
<div className="mx-auto flex h-16 w-16 items-center justify-center rounded-full bg-yellow-400 text-white">
<FontAwesomeIcon icon={faCalendarPlus} size="xl" />
</div>
<div className="mt-3 text-center sm:mt-5">
<Dialog.Title
<DialogTitle
as="h3"
className="text-base font-semibold leading-6 text-slate-900 dark:text-slate-100"
>
Add to Calendar
</Dialog.Title>
</DialogTitle>
<p className="text-slate-700 my-2 dark:text-slate-50">
Copy the link below to subscribe by URL in your calendar app. It will update
automatically as the shutdown schedule changes.
Expand Down Expand Up @@ -80,26 +82,26 @@ const SubscribeModal: React.FC<SubscribeModalProps> = ({ isOpen, setIsOpen }) =>
<button
type="button"
className="inline-flex w-full justify-center rounded-md bg-yellow-400 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-yellow-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-yellow-600"
onClick={() => setIsOpen(false)}
onClick={closeModal}
>
Back to tracker
</button>
</div>
</Dialog.Panel>
</Transition.Child>
</DialogPanel>
</TransitionChild>
</div>
</div>
</Dialog>
</Transition.Root>
</Transition>
);
};

export const CalendarSubscribeButton: React.FC = () => {
const [isOpen, setIsOpen] = useState(false);

const openModal = () => {
const openModal = React.useCallback(() => {
setIsOpen(true);
};
}, []);

return (
<button
Expand Down
3 changes: 2 additions & 1 deletion src/components/LineButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ export const LineButtons = () => {
<div className="pt-3 text-white font-bold flex flex-col sm:flex-row gap-4 text-xs md:text-base">
{(['all', 'red', 'blue', 'orange', 'green'] as (Lines | 'all')[]).map((color) => {
return (
<Link to={'/$line'} params={{ line: color }}>
<Link key={color} to={'/$line'} params={{ line: color }}>
<button
tabIndex={-1}
className={classNames(
colorToStyle[color].bg,
`uppercase px-3 py-1 hover:ring-2`,
Expand Down
82 changes: 45 additions & 37 deletions src/components/LineGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { shutdowns } from '../constants/shutdowns';
import { watermarkLayout } from '../utils/watermark';
import { useBreakpoint } from '../hooks/useBreakpoint';
import { cardStyles } from '../constants/styles';
import { Shutdown } from '../types';
import { CalendarSubscribeButton } from './CalendarSubscribeButton';

dayjs.extend(utc);
Expand All @@ -34,20 +35,11 @@ export const LineGraph: React.FunctionComponent<LineGraphProps> = ({ line: selec
const navigate = useNavigate({});

useEffect(() => {
const calculateDistance = () => {
if (divRef.current) {
const rect = divRef.current.getBoundingClientRect();
const distance = window.innerHeight - rect.bottom;
setDistanceToBottom(Math.max(distance - 50, 400));
}
};

// Calculate it initially and also on window resize
calculateDistance();
window.addEventListener('resize', calculateDistance);

// Cleanup
return () => window.removeEventListener('resize', calculateDistance);
if (divRef.current) {
const rect = divRef.current.getBoundingClientRect();
const distance = window.innerHeight - rect.bottom;
setDistanceToBottom(Math.max(distance - 50, 500));
}
}, []);

const routes = useMemo(
Expand All @@ -56,13 +48,23 @@ export const LineGraph: React.FunctionComponent<LineGraphProps> = ({ line: selec
new Set(
Object.entries(shutdowns)
.filter(([line]) => line === selectedLine || selectedLine === 'all')
.map(([line, shutdowns]): [string, Shutdown[]] => [
line,
shutdowns.filter((sd) =>
range === 'upcoming'
? dayjs(new Date()).isBefore(dayjs(sd.stop_date))
: range === 'past'
? dayjs(new Date()).isAfter(dayjs(sd.start_date))
: true
),
])
.map(([, shutdowns]) =>
shutdowns.map((sd) => `${sd.start_station?.stop_name}-${sd.end_station?.stop_name}`)
)
.flat()
)
),
[selectedLine]
[range, selectedLine]
);

const mappedShutdowns = useMemo(
Expand All @@ -71,26 +73,34 @@ export const LineGraph: React.FunctionComponent<LineGraphProps> = ({ line: selec
Object.entries(shutdowns)
.filter(([line]) => line === selectedLine || selectedLine === 'all')
.map(([line, shutdowns]) => {
const mappedData = shutdowns.map((sd) => {
const sdStartDate = dayjs.utc(sd.start_date);
const sdEndDate = dayjs.utc(sd.stop_date);
const szTimePeriod = [
sdStartDate.format('YYYY-MM-DD'),
sdEndDate.format('YYYY-MM-DD'),
];
return {
x: szTimePeriod,
y: szTimePeriod,
id: `${sd.start_station?.stop_name}-${sd.end_station?.stop_name}`,
start_station: sd.start_station?.stop_name,
end_station: sd.end_station?.stop_name,
line: line,
};
});
const mappedData = shutdowns
.filter((sd) =>
range === 'upcoming'
? dayjs(new Date()).isBefore(dayjs(sd.stop_date))
: range === 'past'
? dayjs(new Date()).isAfter(dayjs(sd.start_date))
: true
)
.map((sd) => {
const sdStartDate = dayjs.utc(sd.start_date);
const sdEndDate = dayjs.utc(sd.stop_date);
const szTimePeriod = [
sdStartDate.format('YYYY-MM-DD'),
sdEndDate.format('YYYY-MM-DD'),
];
return {
x: szTimePeriod,
y: szTimePeriod,
id: `${sd.start_station?.stop_name}-${sd.end_station?.stop_name}`,
start_station: sd.start_station?.stop_name,
end_station: sd.end_station?.stop_name,
line: line,
};
});
return [line, mappedData];
})
),
[selectedLine]
[range, selectedLine]
);

return (
Expand All @@ -107,6 +117,7 @@ export const LineGraph: React.FunctionComponent<LineGraphProps> = ({ line: selec
<Bar
ref={ref}
id={`gnatt-chart`}
height={distanceToBottom}
data={{
labels: routes,
datasets: Object.entries(mappedShutdowns).map(([line, s]) => {
Expand All @@ -116,13 +127,10 @@ export const LineGraph: React.FunctionComponent<LineGraphProps> = ({ line: selec
backgroundColor: COLORS.mbta[line as Lines],
borderSkipped: false,
data: s,
barPercentage: selectedLine === 'all' ? 50 : 0.95,
categoryPercentage: selectedLine === 'all' ? 0.05 : 1,
};

// If we're in system mode
if (Object.entries(mappedShutdowns).length > 1) {
datasetObject.barPercentage = 50;
datasetObject.categoryPercentage = 0.05;
}
return datasetObject;
}),
}}
Expand Down
42 changes: 16 additions & 26 deletions src/components/Shutdowns/ShutdownContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,20 @@ export const ShutdownCards: React.FunctionComponent<ShutdownCardsProps> = ({
}>(
(acc, [line, shutdownList]) => {
shutdownList.forEach((sd, index) => {
if (
(range === 'past' &&
(dayjs(sd.stop_date).isBefore(now, 'day') ||
dayjs(sd.stop_date).isSame(now, 'day'))) ||
(range === 'upcoming' &&
(dayjs(sd.start_date).isAfter(now, 'day') ||
dayjs(sd.stop_date).isSame(now, 'day'))) ||
range === 'all'
) {
const shutdownCard = (
<ShutdownCard
key={`${line}-${sd.start_date}-${sd.stop_date}-${index}`}
line={line as Lines}
shutdown={sd}
/>
);
const shutdownCard = (
<ShutdownCard
key={`${line}-${sd.start_date}-${sd.stop_date}-${index}`}
line={line as Lines}
shutdown={sd}
/>
);

if (dayjs(sd.start_date).isAfter(now, 'day')) {
acc.upcoming.push({ card: shutdownCard, date: dayjs(sd.start_date) });
} else if (dayjs(sd.stop_date).isBefore(now, 'day')) {
acc.completed.push({ card: shutdownCard, date: dayjs(sd.start_date) });
} else {
acc.active.push({ card: shutdownCard, date: dayjs(sd.start_date) });
}
if (dayjs(sd.start_date).isAfter(now, 'day')) {
acc.upcoming.push({ card: shutdownCard, date: dayjs(sd.start_date) });
} else if (dayjs(sd.stop_date).isBefore(now, 'day')) {
acc.completed.push({ card: shutdownCard, date: dayjs(sd.start_date) });
} else {
acc.active.push({ card: shutdownCard, date: dayjs(sd.start_date) });
}
});
return acc;
Expand All @@ -70,7 +60,7 @@ export const ShutdownCards: React.FunctionComponent<ShutdownCardsProps> = ({
const sortedCompleted = shutdownsByGroup.completed.sort(sortByDate).map((item) => item.card);

return { active: sortedActive, upcoming: sortedUpcoming, completed: sortedCompleted };
}, [range, selectedLine]);
}, [selectedLine]);

return (
<div>
Expand All @@ -82,15 +72,15 @@ export const ShutdownCards: React.FunctionComponent<ShutdownCardsProps> = ({
</div>
</div>
)}
{!!groupedAndSortedShutdowns.upcoming.length && (
{!!groupedAndSortedShutdowns.upcoming.length && range !== 'past' && (
<div className="md:my-8 my-4">
<h3 className="md:text-xl font-medium mb-4 dark:text-white">Upcoming Shutdowns 🔜</h3>
<div className="w-full overflow-y-hidden grid md:grid-cols-3 gap-4">
{groupedAndSortedShutdowns.upcoming}
</div>
</div>
)}
{!!groupedAndSortedShutdowns.completed.length && (
{!!groupedAndSortedShutdowns.completed.length && range !== 'upcoming' && (
<div className="md:my-8 my-4">
<h3 className="md:text-xl mb-4 font-medium dark:text-white">Completed Shutdowns ✅</h3>
<div className="w-full overflow-y-hidden grid md:grid-cols-3 gap-4">
Expand Down
Loading

0 comments on commit 7f936cd

Please sign in to comment.