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

Catalog Tabs #637

Open
wants to merge 51 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
1dd8adb
init section table redesign
Jdyn Mar 16, 2023
edac443
revert design change
Jdyn Mar 22, 2023
f9834c3
improve section design
Jdyn Apr 4, 2023
ef2bd6f
remove blue color
Jdyn Apr 6, 2023
c3582b5
simplify app structure
Jdyn Apr 6, 2023
605d1ea
Merge branch 'master' into section-rework
Jdyn Apr 6, 2023
4f335a8
clean up mark-up
Jdyn Apr 6, 2023
569eb5a
add icons
Jdyn Apr 14, 2023
9bd1ef3
move enrollment info up
Jdyn Apr 14, 2023
28be78e
init catalog hook refactor
Jdyn May 13, 2023
b91f707
fix loading skeleton and hook search logic
Jdyn Jun 23, 2023
d235e6e
Merge branch 'master' into catalog-context
Jdyn Jun 23, 2023
14a2d86
remove skeleton in ListItem
Jdyn Jun 23, 2023
c7f0834
refactor toward key enum
Jdyn Aug 25, 2023
e932378
Merge branch 'master' into section-rework
Jdyn Sep 23, 2023
f68c1cd
improve section styles
Jdyn Sep 24, 2023
f100b34
switch info position
Jdyn Sep 24, 2023
eae08e7
Merge branch 'master' into section-rework
Jdyn Sep 24, 2023
f1a8f2e
add WL info
Jdyn Sep 24, 2023
62b27aa
add details to unknown strings
Jdyn Sep 24, 2023
634c21e
bolden enrollment info, remove percentage
Jdyn Sep 26, 2023
edd3cc3
revert positions
Jdyn Oct 3, 2023
ed5e7c9
align text
Jdyn Oct 3, 2023
fd09eaa
Merge branch 'master' into catalog-context
Jdyn Oct 13, 2023
55a2f41
re-apply navigation
Jdyn Oct 13, 2023
0c98611
start tab work
Jdyn Oct 13, 2023
41def7f
catalog grades
Jdyn Oct 13, 2023
a624717
omit internal fields from type
Jdyn Oct 13, 2023
7f64184
move away from enum and infer key types
Jdyn Oct 13, 2023
22b258e
catalog enrollment
Jdyn Oct 14, 2023
a24f4cd
fix empty graph conditional
Jdyn Oct 14, 2023
f07efe0
convert GradesGraph to ts
Jdyn Oct 15, 2023
015c8ba
add missing data message
Jdyn Oct 15, 2023
bc88f26
Merge branch 'master' into section-rework
Jdyn Oct 17, 2023
4f676bf
Merge branch 'section-rework' into catalog-context
Jdyn Oct 17, 2023
39b1071
improve card redesign
Jdyn Oct 17, 2023
d55a193
fix tab border on hover
Jdyn Oct 17, 2023
02890e8
new section table
Jdyn Oct 17, 2023
cc1f080
refactor to separate component
Jdyn Nov 7, 2023
f7e8d3d
revert ts conversion, clean up section style
Jdyn Nov 7, 2023
a9e0620
fix crash if you enter the graph before the data has loaded
Jdyn Nov 7, 2023
29a34a7
Merge branch 'master' into catalog-context
Jdyn Nov 25, 2023
9c8a7dd
rework section card design again
Jdyn Nov 25, 2023
b52701d
clean up styles and improve mobile xp
Jdyn Nov 25, 2023
0129bf1
restructure sectionView, unify modal state with url
Jdyn Nov 25, 2023
8121c5e
saving courses
Jdyn Nov 26, 2023
67a28b3
no panel on mobile
Jdyn Nov 26, 2023
b5f94fd
fix container scrolling and tablet view
Jdyn Nov 27, 2023
4551977
remove used import
Jdyn Nov 27, 2023
c05832e
capital Clear
Jdyn Nov 27, 2023
ca904e3
Merge pull request #658 from asuc-octo/save
mathhulk Nov 27, 2023
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
1 change: 0 additions & 1 deletion frontend/src/Berkeleytime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import useDimensions from 'react-cool-dimensions';
import easterEgg from 'utils/easterEgg';
import Routes from './Routes';
import { fetchEnrollContext, fetchGradeContext } from 'redux/actions';
import { IconoirProvider } from 'iconoir-react';

const Berkeleytime = () => {
const dispatch = useDispatch();
Expand Down
31 changes: 20 additions & 11 deletions frontend/src/app/Catalog/CatalogList/CatalogListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ type CatalogListItemProps = {
handleCourseSelect: (course: CourseFragment) => void;
isSelected: boolean;
};
style: CSSProperties;
style?: CSSProperties;
simple?: boolean;
};

const CatalogListItem = ({ style, data }: CatalogListItemProps) => {
const CatalogListItem = ({ style, data, simple }: CatalogListItemProps) => {
const { course, handleCourseSelect, isSelected } = data;
const [{ course: currentCourse }] = useCatalog();

Expand Down Expand Up @@ -63,20 +64,28 @@ const CatalogListItem = ({ style, data }: CatalogListItemProps) => {
{isSaved ? <BookmarkSaved /> : <BookmarkUnsaved />}
</div>
)}
<span className={`${styles[course.letterAverage[0]]}`}>
{course.letterAverage !== '' ? course.letterAverage : ''}
</span>
{!simple && (
<span className={`${styles[course.letterAverage[0]]}`}>
{course.letterAverage !== '' ? course.letterAverage : ''}
</span>
)}
</div>
</div>
<div className={styles.itemStats}>
<span className={colorEnrollment(course.enrolledPercentage)}>
{formatEnrollment(course.enrolledPercentage)} enrolled
</span>
<span> • {course.units ? formatUnits(course.units) : 'N/A'}</span>
</div>
{!simple && (
<div className={styles.itemStats}>
<span className={colorEnrollment(course.enrolledPercentage)}>
{formatEnrollment(course.enrolledPercentage)} enrolled
</span>
<span> • {course.units ? formatUnits(course.units) : 'N/A'}</span>
</div>
)}
</div>
</div>
);
};

CatalogListItem.defaultProps = {
simple: false
};

export default memo(CatalogListItem, areEqual);
79 changes: 78 additions & 1 deletion frontend/src/app/Catalog/CatalogView/CatalogView.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
padding-right: 10px;
// Add size of fixed header.
padding-top: 30px;

}

&[data-modal='false'] {
Expand Down Expand Up @@ -343,3 +342,81 @@
color: $bt-base-text;
background: #f8f8f8;
}

.saveRoot {
display: flex;
flex-direction: row;
padding: 0 20px;
grid-area: view;
overflow: hidden;
}

@include media(mobile) {
.saveRoot {
display: none;
}
}

.saveContainer {
display: flex;
flex-direction: column;
flex: 1;
overflow-y: scroll;
overflow-x: hidden;
margin: 20px 0px;

&:first-child {
padding-right: 10px;
// border-right: 1.5px solid #eaeaea;
}

&:last-child {
padding-left: 10px;
}
}

.header {
// margin-bottom: 20px;
display: flex;
justify-content: space-between;
flex-direction: row;
align-items: baseline;
gap: 30px;
padding: 6px 23px;

span {
font-size: 20px;
font-weight: 600;
}

border-bottom: 1.5px solid #eaeaea;

button {
color: #2f80ed;
transition: 0.2s;
border: none;
background: none;
padding: 0 8px;
border-radius: 4px;
font-size: 16px;
&:hover {
background: hsla(0, 0%, 92.2%, 0.6196078431);
}
}
}

.seperator {
display: flex;
width: 1.5px;
margin: 20px 0px;
background: #eaeaea;
}

.emptyView {
display: flex;
align-items: center;
justify-content: center;
color: $bt-light-text;
padding: 20px 0;
text-align: center;
}
152 changes: 122 additions & 30 deletions frontend/src/app/Catalog/CatalogView/CatalogView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import book from 'assets/svg/catalog/book.svg';
import launch from 'assets/svg/catalog/launch.svg';
import { ReactComponent as BackArrow } from 'assets/img/images/catalog/backarrow.svg';
import { applyIndicatorPercent, applyIndicatorGrade, formatUnits } from 'utils/utils';
import { PlaylistType, useGetCourseForNameLazyQuery } from 'graphql';
import { CourseFragment, PlaylistType, useGetCourseForNameLazyQuery } from 'graphql';
import { useNavigate, useParams } from 'react-router';
import Skeleton from 'react-loading-skeleton';
import ReadMore from './ReadMore';
Expand All @@ -15,14 +15,36 @@ import { sortPills } from '../service';
import CourseTabs from './Tabs';

import styles from './CatalogView.module.scss';
import { CatalogSlug } from '../types';
import { CatalogSlug, FilterOption } from '../types';
import Meta from 'components/Common/Meta';
import { courseToName } from 'lib/courses/course';
import { useUser } from 'graphql/hooks/user';
import CatalogListItem from '../CatalogList/CatalogListItem';

const skeleton = [...Array(8).keys()];

// Debug saved courses with dummy array since logging in doesn't work in DEV.
// const dummy = [
// {
// id: 'Q291cnNlVHlwZToyMTcxOQ==',
// abbreviation: 'MATH',
// courseNumber: '56',
// description:
// 'This is a first course in Linear Algebra. Core topics include: algebra and geometry of vectors and matrices; systems of linear equations and Gaussian elimination; eigenvalues and eigenvectors; Gram-Schmidt and least squares; symmetric matrices and quadratic forms; singular value decomposition and other factorizations. Time permitting, additional topics may include: Markov chains and Perron-Frobenius, dimensionality reduction, or linear programming. This course differs from Math 54 in that it does not cover Differential Equations, but focuses on Linear Algebra motivated by first applications in Data Science and Statistics.',
// title: 'Linear Algebra ',
// gradeAverage: -1,
// letterAverage: 'A',
// openSeats: 0,
// enrolledPercentage: 1,
// enrolled: 292,
// enrolledMax: 292,
// units: '4.0',
// __typename: 'CourseType'
// }
// ];

const CatalogView = () => {
const [{ course }, dispatch] = useCatalog();
const [{ course, filters, recentCourses }, dispatch] = useCatalog();
const navigate = useNavigate();
const { abbreviation, courseNumber, semester } = useParams<CatalogSlug>();

Expand All @@ -44,26 +66,29 @@ const CatalogView = () => {
}
});

const { user } = useUser();
const savedClasses = useMemo(() => user?.savedClasses ?? [], [user?.savedClasses]);

useEffect(() => {
const [sem, year] = semester?.split(' ') ?? [null, null];
const [sem, year] = semester?.split(' ') ?? [undefined, undefined];

type coursePayload = {
abbreviation: string;
courseNumber: string;
semester: string;
year: string;
};

const variables = {
abbreviation: abbreviation ?? null,
courseNumber: courseNumber ?? null,
semester: sem?.toLowerCase() ?? null,
abbreviation: abbreviation,
courseNumber: courseNumber,
semester: sem?.toLowerCase(),
year: year
};

// Only fetch the course if every parameter has a value.
if (Object.values(variables).every((value) => value !== null))
getCourse({
variables: variables as {
abbreviation: string;
courseNumber: string;
semester: string;
year: string;
}
});
if (Object.values(variables).every((value) => value !== undefined))
getCourse({ variables: variables as coursePayload });
}, [getCourse, abbreviation, courseNumber, semester]);

useEffect(() => {
Expand Down Expand Up @@ -92,22 +117,23 @@ const CatalogView = () => {
const gradePath = legacyId ? `/grades/0-${legacyId}-all-all` : `/grades`;

return (
<div className={`${styles.root}`} data-modal={typeof courseNumber === 'string'}>
<>
<Meta
title={course ? `Catalog | ${courseToName(course)}` : 'Catalog'}
description={course?.description}
/>
<button
className={styles.modalButton}
onClick={() => {
navigate(`/catalog/${semester}`, { replace: true });
}}
>
<BackArrow />
Close
</button>
{course && (
<>
{course ? (
<div className={`${styles.root}`} data-modal={typeof courseNumber === 'string'}>
<button
className={styles.modalButton}
onClick={() => {
navigate(`/catalog/${semester}`, { replace: true });
dispatch({ type: 'setCourse', course: null });
}}
>
<BackArrow />
Close
</button>
<h3>
{course.abbreviation} {course.courseNumber}
</h3>
Expand Down Expand Up @@ -178,9 +204,75 @@ const CatalogView = () => {
</>
</ReadMore>
<CourseTabs />
</>
</div>
) : (
<div className={styles.saveRoot}>
<div className={styles.saveContainer}>
<div className={styles.header}>
<span>Recent</span>
<button onClick={() => dispatch({ type: 'clearRecents' })}>Clear</button>
</div>
{recentCourses.length > 0 ? (
<div>
{recentCourses.map((course) => (
<CatalogListItem
simple
key={course.id}
data={{
course: course as CourseFragment,
handleCourseSelect: (course) => {
dispatch({ type: 'setCourse', course });
navigate({
pathname: `/catalog/${(filters.semester as FilterOption)?.value?.name}/${
course.abbreviation
}/${course.courseNumber}`,
search: location.search
});
},
isSelected: false
}}
/>
))}
</div>
) : (
<div className={styles.emptyView}>Recently viewed courses will appear here!</div>
)}
</div>
<div className={styles.saveContainer}>
<div className={styles.header}>
<span>Saved</span>
</div>
{savedClasses.length > 0 ? (
<div>
{savedClasses.map((course) => (
<CatalogListItem
simple
key={course.id}
data={{
course: course as CourseFragment,
handleCourseSelect: (course) => {
dispatch({ type: 'setCourse', course });
navigate({
pathname: `/catalog/${(filters.semester as FilterOption)?.value?.name}/${
course.abbreviation
}/${course.courseNumber}`,
search: location.search
});
},
isSelected: false
}}
/>
))}
</div>
) : (
<div className={styles.emptyView}>
Click on the bookmarks in the course list while signed-in to save courses!
</div>
)}
</div>
</div>
)}
</div>
</>
);
};

Expand Down
5 changes: 4 additions & 1 deletion frontend/src/app/Catalog/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export type CatalogContext = {
courses: CourseOverviewFragment[];
course: CourseFragment | null;
courseIndex: Fuse<CourseInfo> | null;
recentCourses: CourseFragment[];
};

export type CatalogAction =
Expand All @@ -88,6 +89,8 @@ export type CatalogAction =
| { type: 'filter'; filters: Partial<CatalogContext['filters']> }
| { type: 'setCourseList'; allCourses: CatalogContext['courses'] }
| { type: 'reset'; filters?: Partial<CatalogContext['filters']> }
| { type: 'setPill'; pillItem: PlaylistType };
| { type: 'setPill'; pillItem: PlaylistType }
| { type: 'clearRecents' };


export type CatalogActions = CatalogAction[keyof CatalogAction];
Loading