From d14a31c1bc60c7fa500b5fda435b0aaac81f13b8 Mon Sep 17 00:00:00 2001 From: knownotunknown <78577376+knownotunknown@users.noreply.github.com> Date: Sat, 24 Feb 2024 15:28:28 -0600 Subject: [PATCH] setCourse and calendar header need work --- src/pages/background/lib/createSchedule.ts | 2 +- .../calendar/CalendarGrid.stories.tsx | 8 +-- src/views/components/PopupMain.tsx | 14 +++-- .../components/calendar/Calendar/Calendar.tsx | 22 ++++--- .../CalendarCourseCell/CalendarCourseCell.tsx | 5 +- .../calendar/CalendarGrid/CalendarGrid.tsx | 22 +++---- .../CalendarHeader/CalenderHeader.tsx | 6 +- .../CalendarSchedules/CalendarSchedules.tsx | 54 ++++++++++++++---- src/views/components/common/List/List.tsx | 11 ++-- .../ScheduleListItem/ScheduleListItem.tsx | 12 ++-- .../CourseCatalogInjectedPopup.tsx | 16 +++--- .../HeadingAndActions.tsx | 2 +- src/views/hooks/useFlattenedCourseSchedule.ts | 57 ++++++++++++++++--- src/views/hooks/useSchedules.ts | 2 +- 14 files changed, 162 insertions(+), 71 deletions(-) diff --git a/src/pages/background/lib/createSchedule.ts b/src/pages/background/lib/createSchedule.ts index 97faf8b65..3e0a9486f 100644 --- a/src/pages/background/lib/createSchedule.ts +++ b/src/pages/background/lib/createSchedule.ts @@ -14,7 +14,7 @@ export default async function createSchedule(scheduleName: string): Promise); -export const exampleCalendarGridCourses: CalendarGridCourse[] = [ +/* export const exampleCalendarGridCourses: CalendarGridCourse[] = [ { calendarGridPoint: { dayIndex: 4, @@ -166,13 +166,13 @@ export const exampleCalendarGridCourses: CalendarGridCourse[] = [ colors: getCourseColors('emerald', 500), }, }, -]; +]; */ type Story = StoryObj; -export const Default: Story = { +/* export const Default: Story = { args: { saturdayClass: true, courseCells: exampleCalendarGridCourses, }, -}; +}; */ diff --git a/src/views/components/PopupMain.tsx b/src/views/components/PopupMain.tsx index 2fd6ba0d7..60846a41c 100644 --- a/src/views/components/PopupMain.tsx +++ b/src/views/components/PopupMain.tsx @@ -12,6 +12,7 @@ import List from './common/List/List'; // Ensure this path is correctly pointing import useSchedules from '../hooks/useSchedules'; import { handleOpenCalendar } from './injected/CourseCatalogInjectedPopup/HeadingAndActions'; import { openTabFromContentScript } from '../lib/openNewTabFromContentScript'; +import { act } from 'react-dom/test-utils'; /** @@ -26,8 +27,11 @@ export const handleOpenOptions = async () => { // Not sure if it's bad practice * Chrome extension main popup (when you click extension icon) */ export default function PopupMain() { - const [activeSchedule] = useSchedules(); - + const [activeSchedule, schedules] = useSchedules(); + const coursesLength = activeSchedule ? activeSchedule.courses.length : 0; + if (!activeSchedule) { + return; + } const draggableElements = activeSchedule?.courses.map((course, i) => (
- MAIN SCHEDULE: + {`${activeSchedule.name}`}:
- 22 HOURS - 8 Courses + {`${activeSchedule.hours} HOURS`} + {`${coursesLength} HOURS`}
{/* Integrate the List component here */} diff --git a/src/views/components/calendar/Calendar/Calendar.tsx b/src/views/components/calendar/Calendar/Calendar.tsx index 0422028ff..c6b4a7459 100644 --- a/src/views/components/calendar/Calendar/Calendar.tsx +++ b/src/views/components/calendar/Calendar/Calendar.tsx @@ -3,12 +3,14 @@ import { Course } from 'src/shared/types/Course'; import { exampleCourse } from 'src/stories/components/PopupCourseBlock.stories'; import CalendarHeader from 'src/views/components/calendar/CalendarHeader/CalenderHeader'; import { useFlattenedCourseSchedule } from 'src/views/hooks/useFlattenedCourseSchedule'; +import { UserSchedule } from 'src/shared/types/UserSchedule'; import CourseCatalogInjectedPopup from '../../injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup'; import { CalendarBottomBar } from '../CalendarBottomBar/CalendarBottomBar'; import CalendarGrid from '../CalendarGrid/CalendarGrid'; import { CalendarSchedules } from '../CalendarSchedules/CalendarSchedules'; import ImportantLinks from '../ImportantLinks'; + export const flags = ['WR', 'QR', 'GC', 'CD', 'E', 'II']; interface Props { @@ -20,12 +22,12 @@ interface Props { * @returns */ export function Calendar(): JSX.Element { - const courseCells = useFlattenedCourseSchedule(); - const [showPopup, setShowPopup] = React.useState(false); - console.log(courseCells); + const { courseCells, activeSchedule } = useFlattenedCourseSchedule(); + const [course, setCourse] = React.useState(null); + return ( - <> - +
+
@@ -33,16 +35,18 @@ export function Calendar(): JSX.Element {
-
+
- +
- {showPopup ? setShowPopup(false)}/> : null} - + {/* TODO: Doesn't work when exampleCourse is replaced with an actual course through setCourse. + Check CalendarGrid.tsx and AccountForCourseConflicts for an example */} + {course ? setCourse(null)}/> : null} +
); } diff --git a/src/views/components/calendar/CalendarCourseCell/CalendarCourseCell.tsx b/src/views/components/calendar/CalendarCourseCell/CalendarCourseCell.tsx index d313035e4..33a5dcff1 100644 --- a/src/views/components/calendar/CalendarCourseCell/CalendarCourseCell.tsx +++ b/src/views/components/calendar/CalendarCourseCell/CalendarCourseCell.tsx @@ -13,7 +13,7 @@ export interface CalendarCourseCellProps { status: Status; colors: CourseColors; className?: string; - onClick?: React.MouseEventHandler; + onClick?: React.MouseEventHandler; } const CalendarCourseCell: React.FC = ({ @@ -38,10 +38,11 @@ const CalendarCourseCell: React.FC = ({ return (
>; + setCourse: React.Dispatch>; } /** * Grid of CalendarGridCell components forming the user's course schedule calendar view * @param props */ -function CalendarGrid({ courseCells, saturdayClass, setShowPopup }: React.PropsWithChildren): JSX.Element { +function CalendarGrid({ courseCells, saturdayClass, setCourse }: React.PropsWithChildren): JSX.Element { // const [grid, setGrid] = useState([]); const calendarRef = useRef(null); // Create a ref for the calendar grid const daysOfWeek = Object.keys(DAY_MAP).filter(key => !['S', 'SU'].includes(key)); const hoursOfDay = Array.from({ length: 14 }, (_, index) => index + 8); - console.log(courseCells); + /* const saveAsPNG = () => { htmlToImage .toPng(calendarRef.current, { @@ -85,7 +87,6 @@ function CalendarGrid({ courseCells, saturdayClass, setShowPopup }: React.PropsW } grid.push(row); } - // setGrid(newGrid); return ( @@ -98,7 +99,7 @@ function CalendarGrid({ courseCells, saturdayClass, setShowPopup }: React.PropsW
))} {grid.map((row, rowIndex) => row)} - {courseCells ? : null} + {courseCells ? : null}
); } @@ -107,10 +108,10 @@ export default CalendarGrid; interface AccountForCourseConflictsProps { courseCells: CalendarGridCourse[]; - setShowPopup: React.Dispatch>; + setCourse: React.Dispatch>; } -function AccountForCourseConflicts({ courseCells, setShowPopup }: AccountForCourseConflictsProps): JSX.Element[] { +function AccountForCourseConflicts({ courseCells, setCourse }: AccountForCourseConflictsProps): JSX.Element[] { // Groups by dayIndex to identify overlaps const days = courseCells.reduce((acc, cell: CalendarGridCourse) => { const { dayIndex } = cell.calendarGridPoint; @@ -136,11 +137,9 @@ function AccountForCourseConflicts({ courseCells, setShowPopup }: AccountForCour otherCell.calendarGridPoint.startIndex < cell.calendarGridPoint.endIndex && otherCell.calendarGridPoint.endIndex > cell.calendarGridPoint.startIndex; if (isOverlapping) { - console.log('Found overlapping element'); // Adjust columnIndex to not overlap with the otherCell if (otherCell.gridColumnStart && otherCell.gridColumnStart >= columnIndex) { columnIndex = otherCell.gridColumnStart + 1; - console.log(columnIndex); } cell.totalColumns += 1; } @@ -151,6 +150,9 @@ function AccountForCourseConflicts({ courseCells, setShowPopup }: AccountForCour }); }); + // Part of TODO: block.course is definitely a course object + console.log(courseCells); + return courseCells.map(block => (
setShowPopup(true)} + onClick={() => setCourse(block.course)} />
)); diff --git a/src/views/components/calendar/CalendarHeader/CalenderHeader.tsx b/src/views/components/calendar/CalendarHeader/CalenderHeader.tsx index 3d5a5422c..879ed0673 100644 --- a/src/views/components/calendar/CalendarHeader/CalenderHeader.tsx +++ b/src/views/components/calendar/CalendarHeader/CalenderHeader.tsx @@ -13,8 +13,8 @@ import ScheduleTotalHoursAndCourses from '../../common/ScheduleTotalHoursAndCour import Text from '../../common/Text/Text'; import { handleOpenOptions } from '../../PopupMain'; -const CalendarHeader = () => ( -
+const CalendarHeader = ( { totalHours, totalCourses, scheduleName } ) => ( +
@@ -32,7 +32,7 @@ const CalendarHeader = () => (
- + DATA UPDATED ON: 12:00 AM 02/01/2024
diff --git a/src/views/components/calendar/CalendarSchedules/CalendarSchedules.tsx b/src/views/components/calendar/CalendarSchedules/CalendarSchedules.tsx index cb9265434..bc28a652e 100644 --- a/src/views/components/calendar/CalendarSchedules/CalendarSchedules.tsx +++ b/src/views/components/calendar/CalendarSchedules/CalendarSchedules.tsx @@ -1,11 +1,12 @@ import type { UserSchedule } from '@shared/types/UserSchedule'; -import React, { useState } from 'react'; - +import React, { useState, useEffect } from 'react'; +import useSchedules from 'src/views/hooks/useSchedules'; +import createSchedule from 'src/pages/background/lib/createSchedule'; import AddSchedule from '~icons/material-symbols/add'; - import List from '../../common/List/List'; import ScheduleListItem from '../../common/ScheduleListItem/ScheduleListItem'; import Text from '../../common/Text/Text'; +import switchSchedule from 'src/pages/background/lib/switchSchedule'; export type Props = { style?: React.CSSProperties; @@ -14,12 +15,42 @@ export type Props = { }; export function CalendarSchedules(props: Props) { - const [activeScheduleIndex, setActiveScheduleIndex] = useState(props.dummyActiveIndex || 0); - const [schedules, setSchedules] = useState(props.dummySchedules || []); + const [activeScheduleIndex, setActiveScheduleIndex] = useState(0); + const [newSchedule, setNewSchedule] = useState(''); + const [activeSchedule, schedules] = useSchedules(); + + useEffect(() => { + const index = schedules.findIndex(schedule => schedule.name === activeSchedule.name); + if (index !== -1) { + setActiveScheduleIndex(index); + } + }, [activeSchedule, schedules]); + + const handleKeyDown = (event) => { + if (event.code === 'Enter') { + createSchedule(newSchedule); + setNewSchedule(''); + } + }; - const scheduleComponents = schedules.map((schedule, index) => ( - - )); + const handleScheduleInputChange = (e: React.ChangeEvent) => { + setNewSchedule(e.target.value); + }; + + const selectItem = (index: number) => { + setActiveScheduleIndex(index); + switchSchedule(schedules[index].name); + }; + + const scheduleComponents = schedules.map((schedule, index) => { + return ( + selectItem(index)} + /> + ); + }); return (
@@ -31,8 +62,11 @@ export function CalendarSchedules(props: Props) {
- - +
+ + +
); } diff --git a/src/views/components/common/List/List.tsx b/src/views/components/common/List/List.tsx index c61314d43..236ff00af 100644 --- a/src/views/components/common/List/List.tsx +++ b/src/views/components/common/List/List.tsx @@ -1,5 +1,5 @@ import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'; -import React, { ReactElement, useCallback, useState } from 'react'; +import React, { ReactElement, useCallback, useState, useEffect } from 'react'; import { areEqual } from 'react-window'; /* @@ -84,7 +84,11 @@ const Row: React.FC = React.memo(({ data: { items, gap }, index, style */ const List: React.FC = ({ draggableElements, itemHeight, listHeight, listWidth, gap = 12 }: ListProps) => { const [items, setItems] = useState(() => initial(draggableElements)); - + + useEffect(() => { + setItems(initial(draggableElements)); + }, [draggableElements]); + const onDragEnd = useCallback( result => { if (!result.destination) { @@ -113,8 +117,7 @@ const List: React.FC = ({ draggableElements, itemHeight, listHeight, const transform = style?.transform; if (snapshot.isDragging && transform) { - console.log(transform); - let [, _x, y] = transform.match(/translate\(([-\d]+)px, ([-\d]+)px\)/) || []; + let [, , y] = transform.match(/translate\(([-\d]+)px, ([-\d]+)px\)/) || []; style.transform = `translate3d(0px, ${y}px, 0px)`; // Apply constrained y value } diff --git a/src/views/components/common/ScheduleListItem/ScheduleListItem.tsx b/src/views/components/common/ScheduleListItem/ScheduleListItem.tsx index 0c7aba08b..3677bd1bc 100644 --- a/src/views/components/common/ScheduleListItem/ScheduleListItem.tsx +++ b/src/views/components/common/ScheduleListItem/ScheduleListItem.tsx @@ -10,26 +10,28 @@ export type Props = { active?: boolean; name: string; dragHandleProps?: any; + onClick?: (index) => void; }; /** * This is a reusable dropdown component that can be used to toggle the visiblity of information */ export default function ScheduleListItem(props: Props) { - const { dragHandleProps } = props; - console.log(props); + const { dragHandleProps, onClick } = props; + return (
  • -
    -
    +
    = ({ course, activeSchedule, onClose }) => ( - -
    - - - -
    -
    -); + +
    + + + +
    +
    + ) export default CourseCatalogInjectedPopup; diff --git a/src/views/components/injected/CourseCatalogInjectedPopup/HeadingAndActions.tsx b/src/views/components/injected/CourseCatalogInjectedPopup/HeadingAndActions.tsx index 11b0a7a15..d8d757567 100644 --- a/src/views/components/injected/CourseCatalogInjectedPopup/HeadingAndActions.tsx +++ b/src/views/components/injected/CourseCatalogInjectedPopup/HeadingAndActions.tsx @@ -39,7 +39,7 @@ export const handleOpenCalendar = async () => { // Not sure if it's bad practic */ const HeadingAndActions: React.FC = ({ course, onClose, activeSchedule }) => { const { courseName, department, number: courseNumber, uniqueId, instructors, flags, schedule } = course; - const courseAdded = activeSchedule.courses.some(course => course.uniqueId === uniqueId); + const courseAdded = activeSchedule.courses.some(ourCourse => ourCourse.uniqueId === uniqueId); const instructorString = instructors .map(instructor => { diff --git a/src/views/hooks/useFlattenedCourseSchedule.ts b/src/views/hooks/useFlattenedCourseSchedule.ts index 076df3902..ea006c4b2 100644 --- a/src/views/hooks/useFlattenedCourseSchedule.ts +++ b/src/views/hooks/useFlattenedCourseSchedule.ts @@ -1,7 +1,9 @@ import type { CalendarCourseCellProps } from 'src/views/components/calendar/CalendarCourseCell/CalendarCourseCell'; import { CourseMeeting, TimeStringOptions } from 'src/shared/types/CourseMeeting'; -import type { Status } from 'src/shared/types/Course'; +import type { Status, Course } from 'src/shared/types/Course'; import useSchedules from './useSchedules'; +import { UserSchedule } from 'src/shared/types/UserSchedule'; + const dayToNumber: { [day: string]: number } = { @@ -18,31 +20,62 @@ interface CalendarGridPoint { endIndex: number; } +interface componentProps { + calendarCourseCellProps: CalendarCourseCellProps; +} + /** * Return type of useFlattenedCourseSchedule */ export interface CalendarGridCourse { calendarGridPoint: CalendarGridPoint; componentProps: CalendarCourseCellProps; + course: Course; gridColumnStart?: number; gridColumnEnd?: number; totalColumns?: number; } +export interface FlattenedCourseSchedule { + courseCells: CalendarGridCourse[]; + activeSchedule?: UserSchedule; +} + const convertMinutesToIndex = (minutes: number): number => Math.floor((minutes - 420) / 30); /** * Get the active schedule, and convert it to be render-able into a calendar. * @returns CalendarGridCourse */ -export function useFlattenedCourseSchedule(): CalendarGridCourse[] { +export function useFlattenedCourseSchedule(): FlattenedCourseSchedule { const [activeSchedule] = useSchedules(); + if (!activeSchedule) { - return []; + return { + courseCells: [] as CalendarGridCourse[], + activeSchedule: { + name: 'Something may have went wrong', + courses: [], + hours: 0, + } as UserSchedule, + } as FlattenedCourseSchedule; } - const { courses } = activeSchedule; + else if (activeSchedule.courses.length === 0) { + return { + courseCells: [] as CalendarGridCourse[], + activeSchedule: { + name: activeSchedule.name, + courses: activeSchedule.courses, + hours: activeSchedule.hours, + containsCourse: activeSchedule.containsCourse, + } as UserSchedule, + } as FlattenedCourseSchedule; - return courses + } + + const { courses, name, hours } = activeSchedule; + + const processedCourses = courses .flatMap(course => { const { status, @@ -56,7 +89,7 @@ export function useFlattenedCourseSchedule(): CalendarGridCourse[] { schedule: { meetings: CourseMeeting[] }; }; const courseDeptAndInstr = `${department} ${instructors[0].lastName}`; - + if (meetings.length === 0) { // asynch, online course return [ @@ -75,10 +108,11 @@ export function useFlattenedCourseSchedule(): CalendarGridCourse[] { secondaryColor: 'ut-gray', }, }, - }, + course, + } as CalendarGridCourse, ]; } - + // in-person return meetings.flatMap((meeting) => { const { days, startTime, endTime, location } = meeting; @@ -101,6 +135,7 @@ export function useFlattenedCourseSchedule(): CalendarGridCourse[] { secondaryColor: 'ut-orange', }, }, + course, })); }); }) @@ -113,6 +148,12 @@ export function useFlattenedCourseSchedule(): CalendarGridCourse[] { } return a.calendarGridPoint.endIndex - b.calendarGridPoint.endIndex; }); + + return { + courseCells: processedCourses as CalendarGridCourse[], + activeSchedule: { name, courses, hours } as UserSchedule, + } as FlattenedCourseSchedule; + } function getTimeString(options: TimeStringOptions, startTime: number, endTime: number): string { diff --git a/src/views/hooks/useSchedules.ts b/src/views/hooks/useSchedules.ts index 89f6fde02..8f84b7d4f 100644 --- a/src/views/hooks/useSchedules.ts +++ b/src/views/hooks/useSchedules.ts @@ -30,7 +30,7 @@ export default function useSchedules(): [active: UserSchedule | null, schedules: UserScheduleStore.removeListener(l1); UserScheduleStore.removeListener(l2); }; - }, []); + }, [activeIndex, schedules]); return [activeSchedule, schedules]; }