diff --git a/src/stories/components/CalendarGrid.stories.tsx b/src/stories/components/CalendarGrid.stories.tsx index 42c999f5..8ce324ff 100644 --- a/src/stories/components/CalendarGrid.stories.tsx +++ b/src/stories/components/CalendarGrid.stories.tsx @@ -1,8 +1,8 @@ import { Meta, StoryObj } from '@storybook/react'; -import CalendarGrid from 'src/views/components/calendar/CalendarGrid/CalendarGrid'; -import { getCourseColors } from 'src/shared/util/colors'; -import { CalendarGridCourse } from 'src/views/hooks/useFlattenedCourseSchedule'; -import { Status } from 'src/shared/types/Course'; +import CalendarGrid from '@views/components/common/CalendarGrid/CalendarGrid'; +import { getCourseColors } from '@shared/util/colors'; +import { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule'; +import { Status } from '@shared/types/Course'; const meta = { title: 'Components/Common/Calendar', @@ -20,33 +20,97 @@ const meta = { export default meta; const testData: CalendarGridCourse[] = [ - { - calendarGridPoint: { - dayIndex: 0, - startIndex: 1, - endIndex: 2, - }, - componentProps: { - courseDeptAndInstr: 'Course 1', - timeAndLocation: '9:00 AM - 10:00 AM, Room 101', - status: Status.OPEN, - colors: getCourseColors('emerald', 500), - }, - }, - { - calendarGridPoint: { - dayIndex: 1, - startIndex: 2, - endIndex: 3, - }, - componentProps: { - courseDeptAndInstr: 'Course 2', - timeAndLocation: '10:00 AM - 11:00 AM, Room 102', - status: Status.CLOSED, - colors: getCourseColors('emerald', 500), - }, - }, - // add more data as needed + { + calendarGridPoint: { + dayIndex: 4, + startIndex: 10, + endIndex: 11, + }, + componentProps: { + courseDeptAndInstr: 'Course 1', + timeAndLocation: '9:00 AM - 10:00 AM, Room 101', + status: Status.OPEN, + colors: getCourseColors('emerald', 500), + }, + }, + { + calendarGridPoint: { + dayIndex: 2, + startIndex: 5, + endIndex: 6, + }, + componentProps: { + courseDeptAndInstr: 'Course 1', + timeAndLocation: '9:00 AM - 10:00 AM, Room 101', + status: Status.OPEN, + colors: getCourseColors('emerald', 500), + }, + }, + { + calendarGridPoint: { + dayIndex: 1, + startIndex: 10, + endIndex: 12, + }, + componentProps: { + courseDeptAndInstr: 'Course 2', + timeAndLocation: '10:00 AM - 11:00 AM, Room 102', + status: Status.CLOSED, + colors: getCourseColors('emerald', 500), + }, + }, + { + calendarGridPoint: { + dayIndex: 4, + startIndex: 10, + endIndex: 11, + }, + componentProps: { + courseDeptAndInstr: 'Course 1', + timeAndLocation: '9:00 AM - 10:00 AM, Room 101', + status: Status.OPEN, + colors: getCourseColors('emerald', 500), + }, + }, + { + calendarGridPoint: { + dayIndex: 1, + startIndex: 10, + endIndex: 12, + }, + componentProps: { + courseDeptAndInstr: 'Course 2', + timeAndLocation: '10:00 AM - 11:00 AM, Room 102', + status: Status.CLOSED, + colors: getCourseColors('emerald', 500), + }, + }, + { + calendarGridPoint: { + dayIndex: 1, + startIndex: 10, + endIndex: 12, + }, + componentProps: { + courseDeptAndInstr: 'Course 3', + timeAndLocation: '10:00 AM - 11:00 AM, Room 102', + status: Status.CLOSED, + colors: getCourseColors('emerald', 500), + }, + }, + { + calendarGridPoint: { + dayIndex: 1, + startIndex: 10, + endIndex: 12, + }, + componentProps: { + courseDeptAndInstr: 'Course 4', + timeAndLocation: '10:00 AM - 11:00 AM, Room 102', + status: Status.CLOSED, + colors: getCourseColors('emerald', 500), + }, + }, ]; type Story = StoryObj; diff --git a/src/views/components/calendar/CalendarCourseCell/CalendarCourseCell.tsx b/src/views/components/calendar/CalendarCourseCell/CalendarCourseCell.tsx index cade1785..0e6a03b3 100644 --- a/src/views/components/calendar/CalendarCourseCell/CalendarCourseCell.tsx +++ b/src/views/components/calendar/CalendarCourseCell/CalendarCourseCell.tsx @@ -36,20 +36,11 @@ const CalendarCourseCell: React.FC = ({ return (
-<<<<<<< HEAD -
- - {courseDeptAndInstr} - - {timeAndLocation && ( - - {timeAndLocation} -=======
= ({ {`${meeting.getTimeString({ separator: '–', capitalize: true })}${ meeting.location ? ` – ${meeting.location.building}` : '' }`} ->>>>>>> 73fe14e (fix calendar course cell spacing) )}
diff --git a/src/views/components/calendar/CalendarGrid/CalendarGrid.module.scss b/src/views/components/calendar/CalendarGrid/CalendarGrid.module.scss index 961b505e..a4869b75 100644 --- a/src/views/components/calendar/CalendarGrid/CalendarGrid.module.scss +++ b/src/views/components/calendar/CalendarGrid/CalendarGrid.module.scss @@ -14,7 +14,9 @@ .calendarGrid { display: grid; grid-template-columns: repeat(6, 1fr); - grid-template-rows: repeat(13, 1fr); + grid-template-rows: repeat(26, 1fr); + width: 100%; + height: 100%; } .calendarRow { @@ -26,6 +28,8 @@ flex-direction: column; gap: 10px; position: relative; // Ensuring that child elements can be positioned in relation to this. + min-width: 800px; + min-height: 500px; } .day { @@ -53,7 +57,7 @@ justify-content: space-between; align-items: flex-start; flex: 1 0 0; - border-radius: var(--border-radius-none, 0px); + border-radius: 0px; } .timeBlock { @@ -124,4 +128,13 @@ height: 30px; width: 1px; background-color: grey; -} \ No newline at end of file +} + +.dot { + height: 75%; /* 75% of the container's height */ + width: 75%; /* 75% of the container's width */ + background-color: #000000; /* Color of the dot */ + border-radius: 50%; /* Rounds the corners into a circle */ + top: 12.5%; /* Centers the dot vertically */ + left: 12.5%; /* Centers the dot horizontally */ + } \ No newline at end of file diff --git a/src/views/components/calendar/CalendarGrid/CalendarGrid.tsx b/src/views/components/calendar/CalendarGrid/CalendarGrid.tsx index 9afa2465..1e8dc929 100644 --- a/src/views/components/calendar/CalendarGrid/CalendarGrid.tsx +++ b/src/views/components/calendar/CalendarGrid/CalendarGrid.tsx @@ -1,14 +1,15 @@ -import React, { useRef } from 'react'; -import * as htmlToImage from 'html-to-image'; +import React, { useState, useRef, useEffect } 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 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'; -const daysOfWeek = Object.keys(DAY_MAP).filter(key => !['S', 'SU'].includes(key)); +/* const daysOfWeek = Object.keys(DAY_MAP).filter(key => !['S', 'SU'].includes(key)); const hoursOfDay = Array.from({ length: 14 }, (_, index) => index + 8); const grid = []; for (let i = 0; i < 13; i++) { @@ -23,7 +24,7 @@ for (let i = 0; i < 13; i++) { ); row.push(Array.from({ length: 5 }, (_, j) => )); grid.push(row); -} +} */ interface Props { courseCells: CalendarGridCourse[]; @@ -35,9 +36,13 @@ interface Props { * @param props */ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren): JSX.Element { + const [grid, setGrid] = useState([]); const calendarRef = useRef(null); // Create a ref for the calendar grid - const saveAsPNG = () => { + const daysOfWeek = Object.keys(DAY_MAP).filter(key => !['S', 'SU'].includes(key)); + const hoursOfDay = Array.from({ length: 14 }, (_, index) => index + 8); + + /* const saveAsPNG = () => { htmlToImage .toPng(calendarRef.current, { backgroundColor: 'white', @@ -68,23 +73,47 @@ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren { console.error('oops, something went wrong!', error); }); - }; + }; */ + + useEffect(() => { + const newGrid = []; + for (let i = 0; i < 13; i++) { + const row = []; + let hour = hoursOfDay[i]; + let styleProp = { + gridColumn: '1', + gridRow: `${2 * i + 2}`, + }; + row.push( +
+
+

{(hour % 12 === 0 ? 12 : hour % 12) + (hour < 12 ? ' AM' : ' PM')}

+
+
+ ); + for (let k = 0; k < 5; k++) { + // let shouldRender = false; + styleProp = { + gridColumn: `${k + 2}`, + gridRow: `${2 * i + 2} / ${2 * i + 4}`, + }; + /* let shouldRenderChild = courseCells[iterator]?.calendarGridPoint && + k === courseCells[iterator].calendarGridPoint.dayIndex && i === courseCells[iterator].calendarGridPoint.startIndex; + let childElement =
; */ + /* let completeGridCell = shouldRenderChild ? + : ; */ + row.push(); + } + newGrid.push(row); + } + setGrid(newGrid); + }, []); return (
{/* Displaying the rest of the calendar */} -
- {/*
-
- {hoursOfDay.map((hour) => ( -
-
-

{hour % 12 === 0 ? 12 : hour % 12} {hour < 12 ? 'AM' : 'PM'}

-
-
- ))} -
*/} +
{/* Displaying day labels */}
@@ -93,40 +122,103 @@ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren ))} - {grid.map((row, index) => ( - {row} - ))} + {grid.map((row, rowIndex) => row)} + {accountForCourseConflicts(courseCells)} + {/* courseCells.map((block: CalendarGridCourse) => ( +
+ +
+ )) */}
- {courseCells.map((block: CalendarGridCourse) => ( -
- -
- ))} -
-
{/* First divider */} - -
{/* Second divider */} - -
); } export default CalendarGrid; + +function accountForCourseConflicts(courseCells: CalendarGridCourse[]): JSX.Element[] { + // Groups by dayIndex to identify overlaps + const days = courseCells.reduce((acc, cell: CalendarGridCourse) => { + const { dayIndex } = cell.calendarGridPoint; + if (!acc[dayIndex]) { + acc[dayIndex] = []; + } + acc[dayIndex].push(cell); + return acc; + }, {}); + + // Check for overlaps within each day and adjust gridColumnIndex and totalColumns + Object.values(days).forEach((dayCells: CalendarGridCourse[]) => { + // Sort by start time to ensure proper columnIndex assignment + dayCells.sort((a, b) => a.calendarGridPoint.startIndex - b.calendarGridPoint.startIndex); + + dayCells.forEach((cell, _, arr) => { + let columnIndex = 1; + cell.totalColumns = 1; + // Check for overlaps and adjust columnIndex as needed + for (let otherCell of arr) { + if (otherCell !== cell) { + const isOverlapping = + 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; + } + } + } + cell.gridColumnStart = columnIndex; + cell.gridColumnEnd = columnIndex + 1; + }); + }); + + return courseCells.map(block => ( +
+ +
+ )); +} + +/*
+
+ +
+ +
*/ diff --git a/src/views/components/calendar/CalendarGridCell/CalendarGridCell.module.scss b/src/views/components/calendar/CalendarGridCell/CalendarGridCell.module.scss index b34ddb32..639c8fbb 100644 --- a/src/views/components/calendar/CalendarGridCell/CalendarGridCell.module.scss +++ b/src/views/components/calendar/CalendarGridCell/CalendarGridCell.module.scss @@ -1,7 +1,7 @@ .calendarCell { display: flex; - width: 213.8px; - height: 44.769px; + width: 100%; + height: 100%; min-width: 45px; min-height: 40px; flex-direction: column; @@ -11,8 +11,8 @@ } .hourLine { - width: 213.8px; + width: 100%; height: 1px; - border-radius: var(--border-radius-none, 0px); + border-radius: 0px; background: rgba(218, 220, 224, 0.25); } diff --git a/src/views/components/calendar/CalendarGridCell/CalendarGridCell.tsx b/src/views/components/calendar/CalendarGridCell/CalendarGridCell.tsx index 600a5712..77c35553 100644 --- a/src/views/components/calendar/CalendarGridCell/CalendarGridCell.tsx +++ b/src/views/components/calendar/CalendarGridCell/CalendarGridCell.tsx @@ -1,14 +1,20 @@ import React from 'react'; import styles from './CalendarGridCell.module.scss'; +interface Props { + styleProp: any; +} + /** * Component representing each 1 hour time block of a calendar * @param props */ -const CalendarCell: React.FC = props => ( -
-
-
-); +function CalendarCell({ styleProp }: Props): JSX.Element { + return ( +
+
+
+ ); +} export default CalendarCell; diff --git a/src/views/hooks/useFlattenedCourseSchedule.ts b/src/views/hooks/useFlattenedCourseSchedule.ts index 5b4a6c4a..fea3d400 100644 --- a/src/views/hooks/useFlattenedCourseSchedule.ts +++ b/src/views/hooks/useFlattenedCourseSchedule.ts @@ -21,6 +21,9 @@ interface CalendarGridPoint { export interface CalendarGridCourse { calendarGridPoint: CalendarGridPoint; componentProps: CalendarCourseCellProps; + gridColumnStart?: number; + gridColumnEnd?: number; + totalColumns?: number; } const convertMinutesToIndex = (minutes: number): number => Math.floor(minutes - 420 / 30);