Skip to content
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

refactor: section table body row #1102

Merged
merged 6 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { Button } from '@material-ui/core';
import { Search } from '@material-ui/icons';
import { Button } from '@mui/material';
import { AACourse } from '@packages/antalmanac-types';
import { useCallback } from 'react';

import { useQuickSearchForClasses } from '$lib/helpers';

import RightPaneStore from '$components/RightPane/RightPaneStore';
import { useCoursePaneStore } from '$stores/CoursePaneStore';
import { useTabStore } from '$stores/TabStore';

/**
* Routes the user to the corresponding search result
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { TableBody } from '@material-ui/core';
import { AACourse, AASection } from '@packages/antalmanac-types';
import { useCallback, useEffect, useState } from 'react';

import SectionTableBodyRow from '$components/RightPane/SectionTable/SectionTableBody/SectionTableBodyRow';
import { SectionTableBodyRow } from '$components/RightPane/SectionTable/SectionTableBody/SectionTableBodyRow';
import AppStore from '$stores/AppStore';
import { normalizeTime, parseDaysString } from '$stores/calendarizeHelpers';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Chip, Tooltip } from '@mui/material';
import { useState } from 'react';

import { TableBodyCellContainer } from '$components/RightPane/SectionTable/SectionTableBody/SectionTableBodyCells/TableBodyCellContainer';
import analyticsEnum, { logAnalytics } from '$lib/analytics';
import { clickToCopy } from '$lib/helpers';
import { useThemeStore } from '$stores/SettingsStore';

interface CourseCodeCellProps {
sectionCode: string;
}

export const CourseCodeCell = ({ sectionCode }: CourseCodeCellProps) => {
const isDark = useThemeStore((store) => store.isDark);
const [isHovered, setIsHovered] = useState(false);

const handleMouseEnter = () => {
setIsHovered(true);
};

const handleMouseLeave = () => {
setIsHovered(false);
};

return (
<TableBodyCellContainer sx={{ width: '8%' }}>
<Tooltip title="Click to copy course code" placement="bottom" enterDelay={150}>
<Chip
onClick={(event) => {
clickToCopy(event, sectionCode);
logAnalytics({
category: analyticsEnum.classSearch.title,
action: analyticsEnum.classSearch.actions.COPY_COURSE_CODE,
});
}}
// className={classes.sectionCode}
label={sectionCode}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
style={{
color: isHovered ? (isDark ? 'gold' : 'blueviolet') : '',
}}
size="small"
/>
</Tooltip>
</TableBodyCellContainer>
);
};

/**
* {
display: 'inline-flex',
cursor: 'pointer',
'&:hover': {
cursor: 'pointer',
},
alignSelf: 'center',
},
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Box } from '@mui/material';
import { WebsocSectionMeeting } from '@packages/antalmanac-types';

import { TableBodyCellContainer } from '$components/RightPane/SectionTable/SectionTableBody/SectionTableBodyCells/TableBodyCellContainer';
import { useTimeFormatStore } from '$stores/SettingsStore';
import { formatTimes } from '$stores/calendarizeHelpers';

interface DayAndTimeCellProps {
meetings: WebsocSectionMeeting[];
}

export const DayAndTimeCell = ({ meetings }: DayAndTimeCellProps) => {
const { isMilitaryTime } = useTimeFormatStore();

return (
<TableBodyCellContainer>
{meetings.map((meeting) => {
if (meeting.timeIsTBA) {
return <Box key={meeting.timeIsTBA.toString()}>TBA</Box>;
}

if (meeting.startTime && meeting.endTime) {
const timeString = formatTimes(meeting.startTime, meeting.endTime, isMilitaryTime);

return <Box key={meeting.timeIsTBA + meeting.bldg[0]}>{`${meeting.days} ${timeString}`}</Box>;
}
})}
</TableBodyCellContainer>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Box, useMediaQuery } from '@mui/material';
import { WebsocSectionType } from '@packages/antalmanac-types';

import { MOBILE_BREAKPOINT } from '../../../../../globals';

import { TableBodyCellContainer } from '$components/RightPane/SectionTable/SectionTableBody/SectionTableBodyCells/TableBodyCellContainer';

const SECTION_COLORS = {
Act: { color: '#c87137' },
Col: { color: '#ff40b5' },
Dis: { color: '#ff6e00' },
Fld: { color: '#1ac805' },
Lab: { color: '#1abbe9' },
Lec: { color: '#d40000' },
Qiz: { color: '#8e5c41' },
Res: { color: '#ff2466' },
Sem: { color: '#2155ff' },
Stu: { color: '#179523' },
Tap: { color: '#8d2df0' },
Tut: { color: '#ffc705' },
};

interface DetailCellProps {
sectionType: WebsocSectionType;
sectionNum: string;
units: number;
}

export const DetailsCell = ({ sectionType, sectionNum, units }: DetailCellProps) => {
const isMobileScreen = useMediaQuery(`(max-width: ${MOBILE_BREAKPOINT})`);

return (
<TableBodyCellContainer sx={isMobileScreen ? { textAlign: 'center' } : {}}>
<Box sx={SECTION_COLORS[sectionType]}>{sectionType}</Box>
<Box>
{!isMobileScreen && <>Sec: </>}
{sectionNum}
</Box>
<Box>
{!isMobileScreen && <>Units: </>}
{units}
</Box>
</TableBodyCellContainer>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Box } from '@mui/material';
import { WebsocSectionEnrollment } from '@packages/antalmanac-types';

import { TableBodyCellContainer } from '$components/RightPane/SectionTable/SectionTableBody/SectionTableBodyCells/TableBodyCellContainer';

interface EnrollmentCellProps {
numCurrentlyEnrolled: WebsocSectionEnrollment;
maxCapacity: number;

/**
* This is a string because sometimes it's "n/a"
*/
numOnWaitlist: string;

/**
* This is a string because numOnWaitlist is a string. I haven't seen this be "n/a" but it seems possible and I don't want it to break if that happens.
*/
numNewOnlyReserved: string;
}

export const EnrollmentCell = ({
numCurrentlyEnrolled,
maxCapacity,
numOnWaitlist,
numNewOnlyReserved,
}: EnrollmentCellProps) => {
return (
<TableBodyCellContainer>
<Box>
<Box>
<strong>
{numCurrentlyEnrolled.totalEnrolled} / {maxCapacity}
</strong>
</Box>
{numOnWaitlist !== '' && <Box>WL: {numOnWaitlist}</Box>}
{numNewOnlyReserved !== '' && <Box>NOR: {numNewOnlyReserved}</Box>}
</Box>
</TableBodyCellContainer>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Button, Popover, useMediaQuery } from '@mui/material';
import { useCallback, useEffect, useState } from 'react';

import { MOBILE_BREAKPOINT } from '../../../../../globals';

import GradesPopup from '$components/RightPane/SectionTable/GradesPopup';
import { TableBodyCellContainer } from '$components/RightPane/SectionTable/SectionTableBody/SectionTableBodyCells/TableBodyCellContainer';
import { Grades } from '$lib/grades';
import { useThemeStore } from '$stores/SettingsStore';


async function getGpaData(deptCode: string, courseNumber: string, instructors: string[]) {
const namedInstructors = instructors.filter((instructor) => instructor !== 'STAFF');

// Get the GPA of the first instructor of this section where data exists
for (const instructor of namedInstructors) {
const grades = await Grades.queryGrades(deptCode, courseNumber, instructor, false);
if (grades?.averageGPA) {
return {
gpa: grades.averageGPA.toFixed(2).toString(),
instructor: instructor,
};
}
}

return undefined;
}

interface GpaCellProps {
deptCode: string;
courseNumber: string;
instructors: string[];
}

export const GpaCell = ({ deptCode, courseNumber, instructors }: GpaCellProps) => {
const isDark = useThemeStore((store) => store.isDark);
const isMobile = useMediaQuery(`(max-width: ${MOBILE_BREAKPOINT}`);

const [gpa, setGpa] = useState('');
const [instructor, setInstructor] = useState('');
const [anchorEl, setAnchorEl] = useState<Element>();

const handleClick = useCallback((event: React.MouseEvent<HTMLElement>) => {
setAnchorEl((currentAnchorEl) => (currentAnchorEl ? undefined : event.currentTarget));
}, []);

const hideDistribution = useCallback(() => {
setAnchorEl(undefined);
}, []);

useEffect(() => {
getGpaData(deptCode, courseNumber, instructors)
.then((data) => {
if (data) {
setGpa(data.gpa);
setInstructor(data.instructor);
}
})
.catch(console.log);
}, [deptCode, courseNumber, instructors]);

return (
<TableBodyCellContainer>
<Button
style={{
color: isDark ? 'dodgerblue' : 'blue',
padding: 0,
minWidth: 0,
fontWeight: 400,
fontSize: '1rem',
}}
onClick={handleClick}
variant="text"
>
{gpa}
</Button>
<Popover
open={Boolean(anchorEl)}
onClose={hideDistribution}
anchorEl={anchorEl}
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
disableRestoreFocus
>
<GradesPopup
deptCode={deptCode}
courseNumber={courseNumber}
instructor={instructor}
isMobileScreen={isMobile}
/>
</Popover>
</TableBodyCellContainer>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Box } from '@mui/material';

import { TableBodyCellContainer } from '$components/RightPane/SectionTable/SectionTableBody/SectionTableBodyCells/TableBodyCellContainer';

interface InstructorsCellProps {
instructors: string[];
}

export const InstructorsCell = ({ instructors }: InstructorsCellProps) => {
const links = instructors.map((profName, index) => {
if (profName === 'STAFF') {
return <Box key={profName + index}>{profName}</Box>; // The key should be fine as we're not changing ['STAFF, 'STAFF']
}

const lastName = profName.substring(0, profName.indexOf(','));
return (
<Box key={profName}>
<a
href={`https://www.ratemyprofessors.com/search/professors/1074?q=${lastName}`}
target="_blank"
rel="noopener noreferrer"
>
{profName}
</a>
</Box>
);
});

return <TableBodyCellContainer>{links}</TableBodyCellContainer>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Box } from '@mui/material';
import { WebsocSectionMeeting } from '@packages/antalmanac-types';
import { Fragment, useCallback } from 'react';
import { Link } from 'react-router-dom';

import { TableBodyCellContainer } from '$components/RightPane/SectionTable/SectionTableBody/SectionTableBodyCells/TableBodyCellContainer';
import locationIds from '$lib/location_ids';
import { useThemeStore } from '$stores/SettingsStore';
import { useTabStore } from '$stores/TabStore';

interface LocationsCellProps {
meetings: WebsocSectionMeeting[];
courseName: string;
}

export const LocationsCell = ({ meetings }: LocationsCellProps) => {
const isDark = useThemeStore((store) => store.isDark);
const { setActiveTab } = useTabStore();

const focusMap = useCallback(() => {
setActiveTab(2);
}, [setActiveTab]);

return (
<TableBodyCellContainer>
{meetings.map((meeting) => {
return !meeting.timeIsTBA ? (
meeting.bldg.map((bldg) => {
const [buildingName = ''] = bldg.split(' ');
const buildingId = locationIds[buildingName];
return (
<Fragment key={meeting.timeIsTBA + bldg}>
<Link
style={{
textDecoration: 'none',
}}
to={`/map?location=${buildingId}`}
onClick={focusMap}
color={isDark ? 'dodgerblue' : 'blue'}
>
{bldg}
</Link>
<br />
</Fragment>
);
})
) : (
<Box>{'TBA'}</Box>
);
})}
</TableBodyCellContainer>
);
};
Loading
Loading