Skip to content

Commit

Permalink
feat: bottom bar for the calendar page (#91)
Browse files Browse the repository at this point in the history
  • Loading branch information
doprz committed Mar 6, 2024
1 parent 92462cf commit 0f730d6
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 22 deletions.
101 changes: 101 additions & 0 deletions src/stories/components/CalendarBottomBar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { Course, Status } from '@shared/types/Course';
import Instructor from '@shared/types/Instructor';
import { CalendarBottomBar } from '@views/components/common/CalendarBottomBar/CalendarBottomBar';
import { getCourseColors } from '../../shared/util/colors';

const exampleGovCourse: Course = new Course({
courseName: 'Nope',
creditHours: 3,
department: 'GOV',
description: ['nah', 'aint typing this', 'corndog'],
flags: ['no flag for you >:)'],
fullName: 'GOV 312L Something something',
instructionMode: 'Online',
instructors: [
new Instructor({
firstName: 'Bevo',
lastName: 'Barrymore',
fullName: 'Bevo Barrymore',
}),
],
isReserved: false,
number: '312L',
schedule: {
meetings: [],
},
semester: {
code: '12345',
season: 'Spring',
year: 2024,
},
status: Status.OPEN,
uniqueId: 12345,
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/',
});

const examplePsyCourse: Course = new Course({
courseName: 'Nope Again',
creditHours: 3,
department: 'PSY',
description: ['nah', 'aint typing this', 'corndog'],
flags: ['no flag for you >:)'],
fullName: 'PSY 317L Yada yada',
instructionMode: 'Online',
instructors: [
new Instructor({
firstName: 'Bevo',
lastName: 'Etz',
fullName: 'Bevo Etz',
}),
],
isReserved: false,
number: '317L',
schedule: {
meetings: [],
},
semester: {
code: '12346',
season: 'Spring',
year: 2024,
},
status: Status.CLOSED,
uniqueId: 12346,
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/',
});

const meta = {
title: 'Components/Common/CalendarBottomBar',
component: CalendarBottomBar,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {},
} satisfies Meta<typeof CalendarBottomBar>;
export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
courses: [
{
colors: getCourseColors('pink', 200),
courseDeptAndInstr: `${exampleGovCourse.department} ${exampleGovCourse.number}${exampleGovCourse.instructors[0].lastName}`,
status: exampleGovCourse.status,
},
{
colors: getCourseColors('slate', 500),
courseDeptAndInstr: `${examplePsyCourse.department} ${examplePsyCourse.number}${examplePsyCourse.instructors[0].lastName}`,
status: examplePsyCourse.status,
},
],
},
render: props => (
<div className='outline-red outline w-292.5!'>
<CalendarBottomBar {...props} />
</div>
),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';
import clsx from 'clsx';
import Text from '../Text/Text';
import CalendarCourseBlock, { CalendarCourseCellProps } from '../CalendarCourseCell/CalendarCourseCell';
import { Button } from '../Button/Button';
import ImageIcon from '~icons/material-symbols/image';
import CalendarMonthIcon from '~icons/material-symbols/calendar-month';

type CalendarBottomBarProps = {
courses: CalendarCourseCellProps[];
};

/**
*
*/
export const CalendarBottomBar = ({ courses }: CalendarBottomBarProps): JSX.Element => {
if (courses.length === -1) console.log('foo'); // dumb line to make eslint happy
return (
<div className='w-full flex py-1.25'>
<div className='flex flex-grow items-center gap-3.75 pl-7.5 pr-2.5'>
<Text variant='h4'>Async. and Other:</Text>
<div className='h-14 inline-flex gap-2.5'>
{courses.map(course => (
<CalendarCourseBlock
courseDeptAndInstr={course.courseDeptAndInstr}
status={course.status}
colors={course.colors}
key={course.courseDeptAndInstr}
className={clsx(course.className, 'w-35!')}
/>
))}
</div>
</div>
<div className='flex items-center pl-2.5 pr-7.5'>
<Button variant='single' color='ut-black' icon={CalendarMonthIcon}>
Save as .CAL
</Button>
<Button variant='single' color='ut-black' icon={ImageIcon}>
Save as .PNG
</Button>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@ import WaitlistIcon from '~icons/material-symbols/timelapse';
import CancelledIcon from '~icons/material-symbols/warning';
import Text from '../Text/Text';

export interface CalendarCourseBlockProps {
/** The Course that the meeting is for. */
course: Course;
/* index into course meeting array to display */
meetingIdx?: number;
/** The background color for the course. */
color: string;
export interface CalendarCourseCellProps {
courseDeptAndInstr: string;
timeAndLocation?: string;
status: Status;
colors: CourseColors;
className?: string;
}

const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({ course, meetingIdx }: CalendarCourseCellProps) => {
let meeting: CourseMeeting | null = meetingIdx !== undefined ? course.schedule.meetings[meetingIdx] : null;
const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
courseDeptAndInstr,
timeAndLocation,
status,
colors,
className,
}: CalendarCourseCellProps) => {
let rightIcon: React.ReactNode | null = null;
if (status === Status.WAITLISTED) {
rightIcon = <WaitlistIcon className='h-5 w-5' />;
Expand All @@ -32,7 +36,7 @@ const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({ course, meeting

return (
<div
className={`w-full flex justify-center rounded p-2 ${fontColor}`}
className={clsx('w-full flex justify-center rounded p-2', fontColor, className)}
style={{
backgroundColor: colors.primaryColor,
}}
Expand Down
27 changes: 15 additions & 12 deletions src/views/components/common/CalendarGrid/CalendarGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, {useRef} from 'react';
import React, { useRef } from 'react';
import html2canvas from 'html2canvas';
import { DAY_MAP } from 'src/shared/types/CourseMeeting';
import { CalendarGridCourse } from 'src/views/hooks/useFlattenedCourseSchedule';
import calIcon from 'src/assets/icons/cal.svg';
import pngIcon from 'src/assets/icons/png.svg';
import CalendarCell from '../CalendarGridCell/CalendarGridCell';
import CalendarCourseCell from '../CalendarCourseCell/CalendarCourseCell';
import styles from './CalendarGrid.module.scss';
import calIcon from 'src/assets/icons/cal.svg';
import pngIcon from 'src/assets/icons/png.svg';

const daysOfWeek = Object.keys(DAY_MAP).filter(key => !['S', 'SU'].includes(key));
const hoursOfDay = Array.from({ length: 14 }, (_, index) => index + 8);
Expand Down Expand Up @@ -34,12 +34,12 @@ interface Props {
* Grid of CalendarGridCell components forming the user's course schedule calendar view
* @param props
*/
function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren<Props> ): JSX.Element {
function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren<Props>): JSX.Element {
const calendarRef = useRef(null); // Create a ref for the calendar grid

const saveAsPNG = () => {
if (calendarRef.current) {
html2canvas(calendarRef.current).then((canvas) => {
html2canvas(calendarRef.current).then(canvas => {
// Create an a element to trigger download
const a = document.createElement('a');
a.href = canvas.toDataURL('image/png');
Expand Down Expand Up @@ -85,19 +85,22 @@ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren<Pr
gridRow: `${block.calendarGridPoint.startIndex} / ${block.calendarGridPoint.endIndex}`,
}}
>
<CalendarCourseCell courseDeptAndInstr={block.componentProps.courseDeptAndInstr}
status={block.componentProps.status} colors={block.componentProps.colors}/>
<CalendarCourseCell
courseDeptAndInstr={block.componentProps.courseDeptAndInstr}
status={block.componentProps.status}
colors={block.componentProps.colors}
/>
</div>
))}
))}
<div className={styles.buttonContainer}>
<div className={styles.divider}></div> {/* First divider */}
<div className={styles.divider} /> {/* First divider */}
<button className={styles.calendarButton}>
<img src={calIcon} className={styles.buttonIcon} alt="CAL" />
<img src={calIcon} className={styles.buttonIcon} alt='CAL' />
Save as .CAL
</button>
<div className={styles.divider}></div> {/* Second divider */}
<div className={styles.divider} /> {/* Second divider */}
<button onClick={saveAsPNG} className={styles.calendarButton}>
<img src={pngIcon} className={styles.buttonIcon} alt="PNG" />
<img src={pngIcon} className={styles.buttonIcon} alt='PNG' />
Save as .PNG
</button>
</div>
Expand Down

0 comments on commit 0f730d6

Please sign in to comment.