Merge branch 'Longhorn-Developers:main' into master
This commit is contained in:
@@ -84,18 +84,29 @@ type Story = StoryObj<typeof meta>;
|
|||||||
|
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
args: {
|
args: {
|
||||||
courses: [
|
courseCells: [
|
||||||
{
|
{
|
||||||
|
async: true,
|
||||||
|
calendarGridPoint: { dayIndex: -1, endIndex: -1, startIndex: -1 },
|
||||||
|
componentProps: {
|
||||||
colors: getCourseColors('pink', 200),
|
colors: getCourseColors('pink', 200),
|
||||||
courseDeptAndInstr: `${exampleGovCourse.department} ${exampleGovCourse.number} – ${exampleGovCourse.instructors[0]!.lastName}`,
|
courseDeptAndInstr: `${exampleGovCourse.department} ${exampleGovCourse.number} – ${exampleGovCourse.instructors[0]!.lastName}`,
|
||||||
status: exampleGovCourse.status,
|
status: exampleGovCourse.status,
|
||||||
},
|
},
|
||||||
|
course: exampleGovCourse,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
|
async: true,
|
||||||
|
calendarGridPoint: { dayIndex: -1, endIndex: -1, startIndex: -1 },
|
||||||
|
componentProps: {
|
||||||
colors: getCourseColors('slate', 500),
|
colors: getCourseColors('slate', 500),
|
||||||
courseDeptAndInstr: `${examplePsyCourse.department} ${examplePsyCourse.number} – ${examplePsyCourse.instructors[0]!.lastName}`,
|
courseDeptAndInstr: `${examplePsyCourse.department} ${examplePsyCourse.number} – ${examplePsyCourse.instructors[0]!.lastName}`,
|
||||||
status: examplePsyCourse.status,
|
status: examplePsyCourse.status,
|
||||||
},
|
},
|
||||||
|
course: examplePsyCourse,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
|
setCourse: () => {},
|
||||||
},
|
},
|
||||||
render: props => (
|
render: props => (
|
||||||
<div className='outline-red outline w-292.5!'>
|
<div className='outline-red outline w-292.5!'>
|
||||||
@@ -105,7 +116,7 @@ export const Default: Story = {
|
|||||||
};
|
};
|
||||||
export const Empty: Story = {
|
export const Empty: Story = {
|
||||||
args: {
|
args: {
|
||||||
courses: [],
|
courseCells: [],
|
||||||
},
|
},
|
||||||
render: props => (
|
render: props => (
|
||||||
<div className='outline-red outline w-292.5!'>
|
<div className='outline-red outline w-292.5!'>
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ const testData: CalendarGridCourse[] = [
|
|||||||
colors: getCourseColors('emerald', 500),
|
colors: getCourseColors('emerald', 500),
|
||||||
},
|
},
|
||||||
course: ExampleCourse,
|
course: ExampleCourse,
|
||||||
|
async: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
calendarGridPoint: {
|
calendarGridPoint: {
|
||||||
@@ -49,6 +50,7 @@ const testData: CalendarGridCourse[] = [
|
|||||||
colors: getCourseColors('emerald', 500),
|
colors: getCourseColors('emerald', 500),
|
||||||
},
|
},
|
||||||
course: ExampleCourse,
|
course: ExampleCourse,
|
||||||
|
async: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
calendarGridPoint: {
|
calendarGridPoint: {
|
||||||
@@ -63,6 +65,7 @@ const testData: CalendarGridCourse[] = [
|
|||||||
colors: getCourseColors('emerald', 500),
|
colors: getCourseColors('emerald', 500),
|
||||||
},
|
},
|
||||||
course: ExampleCourse,
|
course: ExampleCourse,
|
||||||
|
async: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
calendarGridPoint: {
|
calendarGridPoint: {
|
||||||
@@ -77,6 +80,7 @@ const testData: CalendarGridCourse[] = [
|
|||||||
colors: getCourseColors('emerald', 500),
|
colors: getCourseColors('emerald', 500),
|
||||||
},
|
},
|
||||||
course: ExampleCourse,
|
course: ExampleCourse,
|
||||||
|
async: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
calendarGridPoint: {
|
calendarGridPoint: {
|
||||||
@@ -91,6 +95,7 @@ const testData: CalendarGridCourse[] = [
|
|||||||
colors: getCourseColors('emerald', 500),
|
colors: getCourseColors('emerald', 500),
|
||||||
},
|
},
|
||||||
course: ExampleCourse,
|
course: ExampleCourse,
|
||||||
|
async: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
calendarGridPoint: {
|
calendarGridPoint: {
|
||||||
@@ -105,6 +110,7 @@ const testData: CalendarGridCourse[] = [
|
|||||||
colors: getCourseColors('emerald', 500),
|
colors: getCourseColors('emerald', 500),
|
||||||
},
|
},
|
||||||
course: ExampleCourse,
|
course: ExampleCourse,
|
||||||
|
async: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
calendarGridPoint: {
|
calendarGridPoint: {
|
||||||
@@ -119,6 +125,7 @@ const testData: CalendarGridCourse[] = [
|
|||||||
colors: getCourseColors('emerald', 500),
|
colors: getCourseColors('emerald', 500),
|
||||||
},
|
},
|
||||||
course: ExampleCourse,
|
course: ExampleCourse,
|
||||||
|
async: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export default function Calendar(): JSX.Element {
|
|||||||
<div className='min-h-2xl flex-grow overflow-auto pl-2 pr-4 pt-6 screenshot:min-h-xl'>
|
<div className='min-h-2xl flex-grow overflow-auto pl-2 pr-4 pt-6 screenshot:min-h-xl'>
|
||||||
<CalendarGrid courseCells={courseCells} setCourse={setCourse} />
|
<CalendarGrid courseCells={courseCells} setCourse={setCourse} />
|
||||||
</div>
|
</div>
|
||||||
<CalendarBottomBar />
|
<CalendarBottomBar courseCells={courseCells} setCourse={setCourse} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
|
import type { Course } from '@shared/types/Course';
|
||||||
import { saveAsCal, saveCalAsPng } from '@views/components/calendar/utils';
|
import { saveAsCal, saveCalAsPng } from '@views/components/calendar/utils';
|
||||||
import { Button } from '@views/components/common/Button';
|
import { Button } from '@views/components/common/Button';
|
||||||
import Divider from '@views/components/common/Divider';
|
import Divider from '@views/components/common/Divider';
|
||||||
import Text from '@views/components/common/Text/Text';
|
import Text from '@views/components/common/Text/Text';
|
||||||
|
import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import CalendarMonthIcon from '~icons/material-symbols/calendar-month';
|
import CalendarMonthIcon from '~icons/material-symbols/calendar-month';
|
||||||
import ImageIcon from '~icons/material-symbols/image';
|
import ImageIcon from '~icons/material-symbols/image';
|
||||||
|
|
||||||
import type { CalendarCourseCellProps } from './CalendarCourseCell';
|
|
||||||
import CalendarCourseBlock from './CalendarCourseCell';
|
import CalendarCourseBlock from './CalendarCourseCell';
|
||||||
|
|
||||||
type CalendarBottomBarProps = {
|
type CalendarBottomBarProps = {
|
||||||
courses?: CalendarCourseCellProps[];
|
courseCells?: CalendarGridCourse[];
|
||||||
|
setCourse: React.Dispatch<React.SetStateAction<Course | null>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,8 +23,9 @@ type CalendarBottomBarProps = {
|
|||||||
* @param {Object[]} courses - The list of courses to display in the calendar.
|
* @param {Object[]} courses - The list of courses to display in the calendar.
|
||||||
* @returns {JSX.Element} The rendered bottom bar component.
|
* @returns {JSX.Element} The rendered bottom bar component.
|
||||||
*/
|
*/
|
||||||
export default function CalendarBottomBar({ courses }: CalendarBottomBarProps): JSX.Element {
|
export default function CalendarBottomBar({ courseCells, setCourse }: CalendarBottomBarProps): JSX.Element {
|
||||||
const displayCourses = courses && courses.length > 0;
|
const asyncCourseCells = courseCells?.filter(block => block.async);
|
||||||
|
const displayCourses = asyncCourseCells && asyncCourseCells.length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full flex py-1.25 pl-7.5 pr-6.25'>
|
<div className='w-full flex py-1.25 pl-7.5 pr-6.25'>
|
||||||
@@ -35,15 +38,19 @@ export default function CalendarBottomBar({ courses }: CalendarBottomBarProps):
|
|||||||
<>
|
<>
|
||||||
<Text variant='h4'>Async/Other:</Text>
|
<Text variant='h4'>Async/Other:</Text>
|
||||||
<div className='inline-flex gap-2.5'>
|
<div className='inline-flex gap-2.5'>
|
||||||
{courses.map(({ courseDeptAndInstr, status, colors, className }) => (
|
{asyncCourseCells.map(block => {
|
||||||
|
const { courseDeptAndInstr, status, colors, className } = block.componentProps;
|
||||||
|
return (
|
||||||
<CalendarCourseBlock
|
<CalendarCourseBlock
|
||||||
courseDeptAndInstr={courseDeptAndInstr}
|
courseDeptAndInstr={courseDeptAndInstr}
|
||||||
status={status}
|
status={status}
|
||||||
colors={colors}
|
colors={colors}
|
||||||
key={courseDeptAndInstr}
|
key={courseDeptAndInstr}
|
||||||
className={clsx(className, 'w-35! h-15!')}
|
className={clsx(className, 'w-35! h-15!')}
|
||||||
|
onClick={() => setCourse(block.course)}
|
||||||
/>
|
/>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -123,8 +123,10 @@ function AccountForCourseConflicts({ courseCells, setCourse }: AccountForCourseC
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return courseCells.map((block, i) => {
|
return courseCells
|
||||||
const { courseDeptAndInstr, timeAndLocation, status } = courseCells[i]!.componentProps;
|
.filter(block => !block.async)
|
||||||
|
.map(block => {
|
||||||
|
const { courseDeptAndInstr, timeAndLocation, status } = block.componentProps;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import type { HexColor } from '@shared/types/Color';
|
|
||||||
import type { Course, StatusType } from '@shared/types/Course';
|
import type { Course, StatusType } from '@shared/types/Course';
|
||||||
import type { CourseMeeting } from '@shared/types/CourseMeeting';
|
import type { CourseMeeting } from '@shared/types/CourseMeeting';
|
||||||
import { colors } from '@shared/types/ThemeColors';
|
|
||||||
import type { UserSchedule } from '@shared/types/UserSchedule';
|
import type { UserSchedule } from '@shared/types/UserSchedule';
|
||||||
import type { CalendarCourseCellProps } from '@views/components/calendar/CalendarCourseCell';
|
import type { CalendarCourseCellProps } from '@views/components/calendar/CalendarCourseCell';
|
||||||
|
|
||||||
@@ -34,6 +32,7 @@ export interface CalendarGridCourse {
|
|||||||
calendarGridPoint: CalendarGridPoint;
|
calendarGridPoint: CalendarGridPoint;
|
||||||
componentProps: CalendarCourseCellProps;
|
componentProps: CalendarCourseCellProps;
|
||||||
course: Course;
|
course: Course;
|
||||||
|
async: boolean;
|
||||||
gridColumnStart?: number;
|
gridColumnStart?: number;
|
||||||
gridColumnEnd?: number;
|
gridColumnEnd?: number;
|
||||||
totalColumns?: number;
|
totalColumns?: number;
|
||||||
@@ -103,25 +102,23 @@ function processAsyncCourses({
|
|||||||
courseDeptAndInstr: string;
|
courseDeptAndInstr: string;
|
||||||
status: StatusType;
|
status: StatusType;
|
||||||
course: Course;
|
course: Course;
|
||||||
}) {
|
}): CalendarGridCourse[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
calendarGridPoint: {
|
calendarGridPoint: {
|
||||||
dayIndex: 0,
|
dayIndex: -1,
|
||||||
startIndex: 0,
|
startIndex: -1,
|
||||||
endIndex: 0,
|
endIndex: -1,
|
||||||
},
|
},
|
||||||
componentProps: {
|
componentProps: {
|
||||||
courseDeptAndInstr,
|
courseDeptAndInstr,
|
||||||
status,
|
status,
|
||||||
colors: {
|
colors: course.colors,
|
||||||
primaryColor: colors.ut.gray as HexColor,
|
|
||||||
secondaryColor: colors.ut.gray as HexColor,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
course,
|
course,
|
||||||
|
async: true,
|
||||||
},
|
},
|
||||||
] satisfies CalendarGridCourse[];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,7 +129,7 @@ function processInPersonMeetings(
|
|||||||
courseDeptAndInstr: string,
|
courseDeptAndInstr: string,
|
||||||
status: StatusType,
|
status: StatusType,
|
||||||
course: Course
|
course: Course
|
||||||
) {
|
): CalendarGridCourse[] {
|
||||||
const { days, startTime, endTime, location } = meeting;
|
const { days, startTime, endTime, location } = meeting;
|
||||||
const midnightIndex = 1440;
|
const midnightIndex = 1440;
|
||||||
const normalizingTimeFactor = 720;
|
const normalizingTimeFactor = 720;
|
||||||
@@ -151,13 +148,11 @@ function processInPersonMeetings(
|
|||||||
courseDeptAndInstr,
|
courseDeptAndInstr,
|
||||||
timeAndLocation,
|
timeAndLocation,
|
||||||
status,
|
status,
|
||||||
colors: {
|
colors: course.colors,
|
||||||
primaryColor: colors.ut.orange as HexColor,
|
|
||||||
secondaryColor: colors.ut.orange as HexColor,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
course,
|
course,
|
||||||
})) satisfies CalendarGridCourse[];
|
async: false,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -118,10 +118,29 @@ export async function querySemesterDistribution(course: Course, semester: Semest
|
|||||||
throw new NoDataError(course);
|
throw new NoDataError(course);
|
||||||
}
|
}
|
||||||
|
|
||||||
let row: Required<CourseSQLRow> = {} as Required<CourseSQLRow>;
|
const row: Required<CourseSQLRow> = {} as Required<CourseSQLRow>;
|
||||||
res.columns.forEach((col, i) => {
|
for (let i = 0; i < res.columns.length; i++) {
|
||||||
row[col as keyof CourseSQLRow] = res.values[0]![i]! as never;
|
const col = res.columns[i] as keyof CourseSQLRow;
|
||||||
});
|
switch (col) {
|
||||||
|
case 'A':
|
||||||
|
case 'A_Minus':
|
||||||
|
case 'B_Plus':
|
||||||
|
case 'B':
|
||||||
|
case 'B_Minus':
|
||||||
|
case 'C_Plus':
|
||||||
|
case 'C':
|
||||||
|
case 'C_Minus':
|
||||||
|
case 'D_Plus':
|
||||||
|
case 'D':
|
||||||
|
case 'D_Minus':
|
||||||
|
case 'F':
|
||||||
|
case 'Other':
|
||||||
|
row[col] = res.values.reduce((acc, cur) => acc + (cur[i] as number), 0) as never;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
row[col] = res.columns[i]![0]! as never;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
A: row.A,
|
A: row.A,
|
||||||
|
|||||||
Reference in New Issue
Block a user