-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Today/Start of Term Calendar Button #396
Comments
I started implementing a but didn't continue as I'm not sure what the behaviour should be exactly. start-of-term-button-implementation.mov |
updating this as it's a good enhancement |
If anyone wants to take this feel free 👍 I deleted the repository because I didn't think I'd work on this issue again any time soon, but below is the code that I started. It is a rough implementation, but gets the job done 😄 |
./src/pages/scheduler/components/SchedulerCalendar.tsx import { useEffect, useMemo, useState } from 'react';
import 'react-big-calendar/lib/sass/styles.scss';
import { format, getDay, parse, set, startOfWeek } from 'date-fns';
import { enUS } from 'date-fns/locale';
import { Calendar, dateFnsLocalizer } from 'react-big-calendar';
import { useDarkMode } from 'lib/hooks/useDarkMode';
import { useSmallScreen } from 'lib/hooks/useSmallScreen';
import { CalendarEvent } from 'pages/scheduler/components/Event';
import { CalendarToolBar } from 'pages/scheduler/components/Toolbar';
import { useInitialDateTime } from 'pages/scheduler/hooks/useInitialDatetime';
import { useStartOfTermDateTime } from 'pages/scheduler/hooks/useStartOfTermDateTime';
import { coursesToVCalendar } from 'pages/scheduler/shared/exporter';
import { courseCalEventsToCustomEvents } from 'pages/scheduler/shared/transformers';
import { CustomEvent } from 'pages/scheduler/shared/types';
import { eventPropGetter } from 'pages/scheduler/styles/eventPropGetter';
import { slotPropGetter } from 'pages/scheduler/styles/slotPropGetter';
import { CourseCalendarEvent } from '../shared/types';
const localizer = dateFnsLocalizer({
format,
parse,
startOfWeek,
getDay,
locales: { 'en-US': enUS },
});
export interface SchedulerCalendarProps {
/**
* CalendarEvents
* Parses events that can go into the calendar from this
*/
courseCalendarEvents?: CourseCalendarEvent[];
term: string;
}
export const SchedulerCalendar = ({ term, courseCalendarEvents = [] }: SchedulerCalendarProps): JSX.Element => {
// for darkmode
const mode = useDarkMode();
const today = useMemo(() => new Date(), []);
const smallScreen = useSmallScreen();
// initialize selected date
const [selectedDate, setSelectedDate] = useState(today);
// state for today button
const [isToday, setIsToday] = useState(false);
// initialize initial view
const [view, setView] = useState<'day' | 'work_week'>('work_week');
// determine what date to position the calendar on.
const initialSelectedDate = useInitialDateTime(term);
// determine the start of the term
const startOfTermDate = useStartOfTermDateTime(term);
// determines the max hour to display on the calendar. defaults to 8 pm
const [maxTime, setMaxTime] = useState<{ hours: number; minutes: number }>({ hours: 20, minutes: 0 });
// create events compatible with the calendar from courses
const vCalendar = useMemo(() => {
return courseCalendarEvents.length !== 0 ? coursesToVCalendar(courseCalendarEvents) : undefined;
}, [courseCalendarEvents]);
const events = useMemo(() => {
const { events, maxHours: maxHour, maxMinutes } = courseCalEventsToCustomEvents(courseCalendarEvents);
if (maxHour >= maxTime.hours && maxMinutes >= maxTime.minutes) {
setMaxTime({ hours: maxHour, minutes: maxMinutes });
}
return events;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [courseCalendarEvents]);
useEffect(() => {
setSelectedDate(initialSelectedDate);
setIsToday(startOfTermDate.toLocaleDateString() !== initialSelectedDate.toLocaleDateString());
setView(smallScreen ? 'day' : 'work_week');
}, [initialSelectedDate, smallScreen, startOfTermDate, courseCalendarEvents.length]);
return (
<Calendar<CustomEvent>
localizer={localizer}
events={events}
min={set(today, { hours: 8, minutes: 0 })}
max={set(today, { hours: maxTime.hours, minutes: maxTime.minutes })}
views={['work_week', 'day']}
onView={(view) => view}
view={view}
onNavigate={(date) => setSelectedDate(date)}
date={selectedDate}
eventPropGetter={eventPropGetter}
slotPropGetter={slotPropGetter(mode)}
components={{
toolbar: CalendarToolBar(setSelectedDate, setIsToday, isToday, startOfTermDate, term, smallScreen, vCalendar),
event: CalendarEvent,
}}
dayLayoutAlgorithm="no-overlap"
formats={{
dayFormat: (date: Date, culture: any, localizer: any) => localizer.format(date, 'EEEE', culture),
dayHeaderFormat: (date: Date, culture: any, localizer: any) => localizer.format(date, 'EE MMM do', culture),
}}
/>
);
}; ./src/pages/scheduler/components/Toolbar.tsx import { ChevronLeftIcon, ChevronRightIcon, DownloadIcon } from '@chakra-ui/icons';
import { Button, Flex, HStack, IconButton, Text } from '@chakra-ui/react';
import { ToolbarProps } from 'react-big-calendar';
import { FaRegCalendar, FaRegCalendarAlt } from 'react-icons/fa';
import { Term } from 'lib/fetchers';
import { logEvent } from 'lib/utils/logEvent';
import { ShareButton } from './share/ShareButton';
export const CalendarToolBar =
(
onSelectedDateChange: (date: Date) => void,
onTodayChange: (today: boolean) => void,
isToday: boolean,
startOfTerm: Date,
term: string,
smallScreen: boolean,
vCalendar?: string
) =>
({ label, date }: ToolbarProps) => {
const handleClick = (offset?: number) => () => {
if (offset) {
const d = new Date(date);
d.setDate(d.getDate() + offset);
onSelectedDateChange(d);
if (d.toLocaleDateString() !== startOfTerm.toLocaleDateString()) {
onTodayChange(true);
} else {
onTodayChange(false);
}
} else {
if (isToday) {
onSelectedDateChange(startOfTerm);
onTodayChange(false);
} else {
onSelectedDateChange(new Date());
onTodayChange(true);
}
}
};
const handleDownload = () => {
if (vCalendar) {
logEvent('calendar_download', { term });
const element = document.createElement('a');
const file = new Blob([vCalendar], { type: 'text/plain' });
element.href = URL.createObjectURL(file);
element.download = `${term}_calendar.ics`;
element.click();
document.body.removeChild(element);
}
};
return (
<Flex pb="0.5em" justifyContent="space-between" alignItems="center">
<Text fontSize={{ base: 'xs', sm: 'xl' }}>{label}</Text>
<HStack pb="0.2em">
<ShareButton term={term as Term} disabled={!vCalendar} />
{smallScreen ? (
<IconButton
icon={<DownloadIcon />}
aria-label="Download timetable"
size="sm"
colorScheme="blue"
onClick={handleDownload}
disabled={!vCalendar}
/>
) : (
<Button size="sm" colorScheme="blue" onClick={handleDownload} disabled={!vCalendar}>
Download
</Button>
)}
{smallScreen ? (
<IconButton
aria-label={isToday ? 'Start of Term' : 'Today'}
colorScheme="gray"
icon={isToday ? <FaRegCalendarAlt /> : <FaRegCalendar />}
size="sm"
onClick={handleClick()}
/>
) : (
<Button size="sm" colorScheme="gray" onClick={handleClick()}>
{isToday ? 'Start of Term' : 'Today'}
</Button>
)}
<IconButton
aria-label="Previous Week"
colorScheme="gray"
icon={<ChevronLeftIcon />}
size="sm"
onClick={handleClick(smallScreen ? -1 : -7)}
/>
<IconButton
aria-label="Next Week"
colorScheme="gray"
icon={<ChevronRightIcon />}
size="sm"
onClick={handleClick(smallScreen ? 1 : 7)}
/>
</HStack>
</Flex>
);
}; ./src/pages/scheduler/hooks/useStartOfTermDateTime.tsx import { useMemo } from 'react';
export const useStartOfTermDateTime = (term: string) => {
// determine the start of the term (i.e., the first day of the month)
return useMemo<Date>(() => {
// eg. 202105 => 2021, 05
const month = parseInt(term.substring(4, 6));
const year = parseInt(term.substring(0, 4));
return new Date(year, month - 1, 1);
}, [term]);
}; |
Describe the Bug/Issue
The timetable has a
Today
button that brings users to today's date. However, there is no way to go back to theStart of Term
. Users must refresh the page, or select another term and then re-select the desired term.To Reproduce
Steps to reproduce the behaviour/bug/issue:
Today
button.Start of Term
.Expected Behaviour
Be able to go back to the "Start of Term" after selecting
Today
or navigating away from the "Start of Term".Screenshots
courseup-today-button.mov
Desktop/Mobile
Additional Context
Not sure what the expected behaviour should be because it is a weird behaviour.
Discussion Points
Today
button.Today
button only visible on theStart of Term
.Start of Term
button.Would love to hear from you folks on your ideas and ideal behaviour.
The text was updated successfully, but these errors were encountered: