From 64a9ac0d86c8b3dc0aead1a624972192bfbbac84 Mon Sep 17 00:00:00 2001 From: Devin Matte Date: Thu, 16 May 2024 19:29:46 -0400 Subject: [PATCH] Subscribe to calendar (#37) * Fixing calendar generation * Ability to subscribe to calendar * Hide devtools on prod * Update src/components/CalendarSubscribeButton.tsx Co-authored-by: Preston Mueller * Fixing linting --------- Co-authored-by: Preston Mueller --- package-lock.json | 79 +++++++++++++- package.json | 5 + server/app.py | 8 +- src/components/CalendarSubscribeButton.tsx | 117 +++++++++++++++++++++ src/components/LineGraph.tsx | 6 +- 5 files changed, 208 insertions(+), 7 deletions(-) create mode 100644 src/components/CalendarSubscribeButton.tsx diff --git a/package-lock.json b/package-lock.json index be611be..25efbdb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,10 @@ "version": "0.0.1", "hasInstallScript": true, "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.5.2", + "@fortawesome/free-regular-svg-icons": "^6.5.2", + "@fortawesome/free-solid-svg-icons": "^6.5.2", + "@fortawesome/react-fontawesome": "^0.2.0", "@headlessui/react": "^1.7.19", "@heroicons/react": "^2.1.3", "@tanstack/react-query": "^5.34.1", @@ -27,6 +31,7 @@ "react-responsive": "^10.0.0", "react-scroll": "^1.9.0", "react-toggle-dark-mode": "^1.1.1", + "usehooks-ts": "^3.1.0", "zustand": "^4.5.2" }, "devDependencies": { @@ -2596,6 +2601,63 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz", + "integrity": "sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.2.tgz", + "integrity": "sha512-5CdaCBGl8Rh9ohNdxeeTMxIj8oc3KNBgIeLMvJosBMdslK/UnEB8rzyDRrbKdL1kDweqBPo4GT9wvnakHWucZw==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.2.tgz", + "integrity": "sha512-iabw/f5f8Uy2nTRtJ13XZTS1O5+t+anvlamJ3zJGLEVE2pKsAWhPv2lq01uQlfgCX7VaveT3EVs515cCN9jRbw==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.2.tgz", + "integrity": "sha512-QWFZYXFE7O1Gr1dTIp+D6UcFUF0qElOnZptpi7PBUMylJh+vFmIedVe1Ir6RM1t2tEQLLSV1k7bR4o92M+uqlw==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", + "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -9649,8 +9711,7 @@ "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "peer": true + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -13654,6 +13715,20 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/usehooks-ts": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-3.1.0.tgz", + "integrity": "sha512-bBIa7yUyPhE1BCc0GmR96VU/15l/9gP1Ch5mYdLcFBaFGQsdmXkvjV0TtOqW1yUd6VjIwDunm+flSciCQXujiw==", + "dependencies": { + "lodash.debounce": "^4.0.8" + }, + "engines": { + "node": ">=16.15.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index bcbccf9..19dd75e 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,10 @@ }, "proxy": "http://localhost:5000", "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.5.2", + "@fortawesome/free-regular-svg-icons": "^6.5.2", + "@fortawesome/free-solid-svg-icons": "^6.5.2", + "@fortawesome/react-fontawesome": "^0.2.0", "@headlessui/react": "^1.7.19", "@heroicons/react": "^2.1.3", "@tanstack/react-query": "^5.34.1", @@ -38,6 +42,7 @@ "react-responsive": "^10.0.0", "react-scroll": "^1.9.0", "react-toggle-dark-mode": "^1.1.1", + "usehooks-ts": "^3.1.0", "zustand": "^4.5.2" }, "devDependencies": { diff --git a/server/app.py b/server/app.py index bdbe073..55b946d 100644 --- a/server/app.py +++ b/server/app.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timedelta from zoneinfo import ZoneInfo from icalendar import Calendar, Event import json @@ -59,9 +59,9 @@ def calendar(): # Handle dates (set end date to day after stop date to include the whole day in the event) start_date = datetime.strptime(shutdown["start_date"], "%Y-%m-%d").replace(tzinfo=EASTERN_TIME) - stop_date = datetime.strptime(shutdown["stop_date"], "%Y-%m-%d").replace( - tzinfo=EASTERN_TIME - ) + datetime.timedelta(days=1) + stop_date = datetime.strptime(shutdown["stop_date"], "%Y-%m-%d").replace(tzinfo=EASTERN_TIME) + timedelta( + days=1 + ) event.add("dtstart", start_date.date()) event.add("dtend", stop_date.date()) diff --git a/src/components/CalendarSubscribeButton.tsx b/src/components/CalendarSubscribeButton.tsx new file mode 100644 index 0000000..f46843b --- /dev/null +++ b/src/components/CalendarSubscribeButton.tsx @@ -0,0 +1,117 @@ +import { faCalendarPlus } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Dialog, Transition } from '@headlessui/react'; +import { CheckIcon, DocumentDuplicateIcon } from '@heroicons/react/16/solid'; +import { useState } from 'react'; +import { Fragment } from 'react/jsx-runtime'; +import { useCopyToClipboard } from 'usehooks-ts'; + +interface SubscribeModalProps { + isOpen: boolean; + setIsOpen: (isOpen: boolean) => void; +} + +const SubscribeModal: React.FC = ({ isOpen, setIsOpen }) => { + const [, copy] = useCopyToClipboard(); + const [copied, setCopied] = useState(false); + + return ( + + + +
+ + +
+
+ + +
+
+ +
+
+ + Add to Calendar + +

+ Copy the link below to subscribe by URL in your calendar app. It will update + automatically as the shutdown schedule changes. +

+ +
+
+
+ +
+
+
+
+
+
+
+ ); +}; + +export const CalendarSubscribeButton: React.FC = () => { + const [isOpen, setIsOpen] = useState(false); + + const openModal = () => { + setIsOpen(true); + }; + + return ( + + ); +}; diff --git a/src/components/LineGraph.tsx b/src/components/LineGraph.tsx index 5504ab4..9b3b22b 100644 --- a/src/components/LineGraph.tsx +++ b/src/components/LineGraph.tsx @@ -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 { CalendarSubscribeButton } from './CalendarSubscribeButton'; dayjs.extend(utc); @@ -98,7 +99,10 @@ export const LineGraph: React.FunctionComponent = ({ return (
-
Timeline
+
+
Timeline
+ +
} className="ml-2 sm:ml-0 flex flex-row gap-4"