diff --git a/frontend/src/Berkeleytime.tsx b/frontend/src/Berkeleytime.tsx
index 9d6f45d6a..84387818a 100644
--- a/frontend/src/Berkeleytime.tsx
+++ b/frontend/src/Berkeleytime.tsx
@@ -22,6 +22,7 @@ const Berkeleytime = () => {
});
useEffect(() => {
+ observe(document.getElementById('root'));
// Fetch enrollment context early on for catalog and enrollment page.
dispatch(fetchEnrollContext());
@@ -41,15 +42,9 @@ const Berkeleytime = () => {
if (localStorage.getItem(key) === null) {
localStorage.setItem(key, key);
}
- }, [dispatch]);
-
- return (
-
-
-
-
-
- );
+ }, [dispatch, observe]);
+
+ return ;
};
export default memo(Berkeleytime);
diff --git a/frontend/src/app/Catalog/CatalogList/CatalogList.module.scss b/frontend/src/app/Catalog/CatalogList/CatalogList.module.scss
index b9f198436..3d9c3a67b 100644
--- a/frontend/src/app/Catalog/CatalogList/CatalogList.module.scss
+++ b/frontend/src/app/Catalog/CatalogList/CatalogList.module.scss
@@ -103,9 +103,13 @@
}
}
-.grade {
+.gradeWrapper {
display: flex;
- justify-content: center;
+ flex-direction: column;
+
+ span {
+ line-height: 20px;
+ }
}
.A {
diff --git a/frontend/src/app/Catalog/CatalogList/CatalogListItem.tsx b/frontend/src/app/Catalog/CatalogList/CatalogListItem.tsx
index f5370a199..583329872 100644
--- a/frontend/src/app/Catalog/CatalogList/CatalogListItem.tsx
+++ b/frontend/src/app/Catalog/CatalogList/CatalogListItem.tsx
@@ -55,7 +55,7 @@ const CatalogListItem = ({ style, data }: CatalogListItemProps) => {
{`${course.abbreviation} ${course.courseNumber}`}
{course.title}
-
+
{user && (
{
{isSaved ? : }
)}
-
+
{course.letterAverage !== '' ? course.letterAverage : ''}
- {formatEnrollment(course.enrolledPercentage)}
+ {formatEnrollment(course.enrolledPercentage)} enrolled
• {course.units ? formatUnits(course.units) : 'N/A'}
diff --git a/frontend/src/app/Catalog/CatalogView/CatalogView.module.scss b/frontend/src/app/Catalog/CatalogView/CatalogView.module.scss
index bb576282e..751d10755 100644
--- a/frontend/src/app/Catalog/CatalogView/CatalogView.module.scss
+++ b/frontend/src/app/Catalog/CatalogView/CatalogView.module.scss
@@ -23,7 +23,7 @@
flex-direction: column;
padding: 30px;
gap: 15px;
- overflow-y: auto;
+ overflow-y: overlay;
overflow-x: hidden;
-webkit-animation: fadeIn 0.1s;
animation: fadeIn 0.1s;
@@ -37,10 +37,6 @@
color: $bt-base-text;
font-weight: 700;
font-size: 18px;
-
- span {
- color: $bt-blue;
- }
}
h6 {
@@ -210,29 +206,59 @@
.sectionItem {
display: flex;
flex-direction: row;
- // background: #F8F8F8;
- padding: 12px 24px;
- border-radius: 12px !important;
+ padding: 12px;
+ border-radius: 16px;
border: 1.5px solid #eaeaea;
- gap: 20px;
- h5 {
+ gap: 30px;
+
+ h6 {
font-size: 16px;
+ min-height: 21px;
margin: 0;
+ font-weight: bold;
+ color: $bt-base-text;
}
+}
+
+.sectionInfo {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
h6 {
- font-size: 14px;
- span {
- text-transform: capitalize;
- }
+ display: flex;
+ align-items: center;
+ min-height: 24px;
}
}
-.sectionInfo {
+.instructor {
display: flex;
gap: 5px;
+ align-items: center;
+ font-size: 14px;
+ color: $bt-light-grey;
+ text-transform: capitalize;
+ gap: 4px;
+ min-height: 24px;
+ color: $bt-light-text;
+}
+
+.sectionContent {
+ display: flex;
flex-direction: column;
- flex: 1;
+ justify-content: flex-start;
+ font-size: 14px;
+
+ span {
+ display: inline-flex;
+ justify-content: flex-end;
+ align-items: center;
+ line-height: 20px;
+ gap: 4px;
+ color: $bt-light-text;
+ text-align: right;
+ }
}
.sectionStats {
@@ -241,14 +267,14 @@
flex-wrap: wrap;
gap: 5px;
color: $bt-light-text;
- margin-top: 5px;
+ margin-top: 12px;
font-size: 14px;
text-transform: none !important;
}
.enrolled {
display: flex;
- justify-content: center;
+ justify-content: flex-end;
align-items: center;
font-size: 14px;
font-weight: 600;
diff --git a/frontend/src/app/Catalog/CatalogView/CatalogView.tsx b/frontend/src/app/Catalog/CatalogView/CatalogView.tsx
index de4f1c8a7..f2362c5a2 100644
--- a/frontend/src/app/Catalog/CatalogView/CatalogView.tsx
+++ b/frontend/src/app/Catalog/CatalogView/CatalogView.tsx
@@ -44,7 +44,7 @@ const CatalogView = (props: CatalogViewProps) => {
)?.id ?? null
);
- const [getCourse, { data, loading }] = useGetCourseForNameLazyQuery({
+ const [getCourse, { data }] = useGetCourseForNameLazyQuery({
onCompleted: (data) => {
const course = data.allCourses.edges[0].node;
if (course) {
@@ -89,10 +89,15 @@ const CatalogView = (props: CatalogViewProps) => {
const [playlists, sections] = useMemo(() => {
let playlists = null;
let sections = null;
+ // let semesters = null;
if (course?.playlistSet) {
const { edges } = course.playlistSet;
playlists = catalogService.sortPills(edges.map((e) => e.node as PlaylistType));
+
+ // semesters = catalogService.sortSemestersByLatest(
+ // edges.map((e) => e.node).filter((n) => n.category === 'semester')
+ // );
}
if (course?.sectionSet) {
@@ -100,6 +105,7 @@ const CatalogView = (props: CatalogViewProps) => {
sections = sortSections(edges.map((e) => e.node));
}
+ // return [playlists ?? skeleton, sections ?? [], semesters];
return [playlists ?? skeleton, sections ?? null];
}, [course]);
@@ -132,19 +138,19 @@ const CatalogView = (props: CatalogViewProps) => {
return (
-
{course && (
<>
+
{course.abbreviation} {course.courseNumber}
@@ -211,16 +217,7 @@ const CatalogView = (props: CatalogViewProps) => {
>
Class Times - {semester ?? ''}
- {sections && sections.length > 0 ? (
-
- ) : !loading ? (
-
There are no class times for the selected course.
- ) : null}
-
- {/*
- Redesigned catalog sections
-
- */}
+
{/* Good feature whenever we want...
Past Offerings
diff --git a/frontend/src/app/Catalog/CatalogView/SectionTable.tsx b/frontend/src/app/Catalog/CatalogView/SectionTable.tsx
index f3b70bf21..326dcd811 100644
--- a/frontend/src/app/Catalog/CatalogView/SectionTable.tsx
+++ b/frontend/src/app/Catalog/CatalogView/SectionTable.tsx
@@ -1,6 +1,8 @@
import { SectionFragment } from 'graphql';
import { CSSProperties } from 'react';
-import { Table } from 'react-bootstrap';
+import { formatSectionTime } from 'utils/sections/section';
+import catalogService from '../service';
+import Skeleton from 'react-loading-skeleton';
import denero from 'assets/img/eggs/denero.png';
import hug from 'assets/img/eggs/hug.png';
@@ -9,9 +11,13 @@ import sahai from 'assets/img/eggs/sahai.png';
import scott from 'assets/img/eggs/scott.png';
import kubi from 'assets/img/eggs/kubi.png';
import garcia from 'assets/img/eggs/garcia.png';
-import { formatSectionTime } from 'utils/sections/section';
+import { Clock, Group, PinAlt, User } from 'iconoir-react';
+
+import styles from './CatalogView.module.scss';
-const easterEggImages = new Map
([
+const { colorEnrollment } = catalogService;
+
+const easterEggImages = new Map([
['DENERO J', denero],
['HUG J', hug],
['SAHAI A', sahai],
@@ -35,52 +41,66 @@ function findInstructor(instr: string | null): CSSProperties {
return {};
}
-type Props = {
+interface Props {
sections: SectionFragment[] | null;
-};
+}
const SectionTable = ({ sections }: Props) => {
+ if (!sections) {
+ return (
+
+ );
+ }
+
return (
-
-
-
-
-
- Type |
- CCN |
- Instructor |
- Time |
- Location |
- Enrolled |
- Waitlist |
-
-
-
- {sections?.map((section) => {
- return (
-
- {section.kind} |
- {section.ccn} |
- {section.instructor} |
- {section.startTime && section.endTime ? (
-
- {section.wordDays} {formatSectionTime(section)}
- |
- ) : (
- |
- )}
- {section.locationName} |
-
+
+ {sections.length > 0 ? (
+ sections.map((section) => {
+ const color = colorEnrollment(section.enrolled / section.enrolledMax);
+
+ return (
+
+
+ {section.kind}
+
+
+ {section.instructor?.toLowerCase() || 'unknown'}
+
+
+
+
{section.enrolled}/{section.enrolledMax}
- |
- {section.waitlisted} |
-
- );
- })}
-
-
-
-
+
+ • {section.waitlisted} Waitlisted
+ {/* • CCN {section.ccn} */}
+
+
+
+
+
+ {section.locationName || 'Unknown'}
+
+
+
+ {section.wordDays} {formatSectionTime(section)}
+
+
+
+ );
+ })
+ ) : (
+ There are no class sections for this course.
+ )}
+
);
};
diff --git a/frontend/src/app/Catalog/CatalogView/__new_SectionTable.tsx b/frontend/src/app/Catalog/CatalogView/__new_SectionTable.tsx
deleted file mode 100644
index a33b596c1..000000000
--- a/frontend/src/app/Catalog/CatalogView/__new_SectionTable.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import { SectionFragment } from 'graphql';
-import { CSSProperties } from 'react';
-import { formatSectionTime } from 'utils/sections/section';
-import catalogService from '../service';
-import Skeleton from 'react-loading-skeleton';
-
-import people from 'assets/svg/catalog/people.svg';
-import denero from 'assets/img/eggs/denero.png';
-import hug from 'assets/img/eggs/hug.png';
-import hilf from 'assets/img/eggs/hilf.png';
-import sahai from 'assets/img/eggs/sahai.png';
-import scott from 'assets/img/eggs/scott.png';
-import kubi from 'assets/img/eggs/kubi.png';
-import garcia from 'assets/img/eggs/garcia.png';
-
-import styles from './CatalogView.module.scss';
-
-const { colorEnrollment, formatEnrollment } = catalogService;
-
-const easterEggImages = new Map([
- ['DENERO J', denero],
- ['HUG J', hug],
- ['SAHAI A', sahai],
- ['HILFINGER P', hilf],
- ['SHENKER S', scott],
- ['KUBIATOWICZ J', kubi],
- ['GARCIA D', garcia]
-]);
-
-function findInstructor(instr: string | null): CSSProperties {
- if (instr === null) return {};
-
- for (const [name, eggUrl] of easterEggImages) {
- if (instr.includes(name)) {
- return {
- cursor: `url("${eggUrl}"), pointer`
- } as CSSProperties;
- }
- }
-
- return {};
-}
-
-type Props = {
- sections: SectionFragment[] | null;
-};
-
-const CatalogViewSections = ({ sections }: Props) => {
- if (!sections) {
- return (
-
- );
- }
-
- return (
-
- {sections.length > 0 ? (
- sections.map((section) => (
-
-
-
- {section.kind} -{' '}
- {section.locationName ? section.locationName : 'Unknown Location'}
-
-
- {section?.instructor?.toLowerCase() ?? 'instructor'},{' '}
- {section.wordDays} {formatSectionTime(section)}
-
-
-
- {formatEnrollment(section.enrolled / section.enrolledMax)}
-
- • {section.waitlisted} waitlisted
- • CCN: {section.ccn}
-
-
-
-
- {section.enrolled}/{section.enrolledMax}
-
-
- ))
- ) : (
-
There are no class sections for this course.
- )}
-
- );
-};
-
-export default CatalogViewSections;
diff --git a/frontend/src/app/Catalog/service.ts b/frontend/src/app/Catalog/service.ts
index cf410484e..1fd66d5c4 100644
--- a/frontend/src/app/Catalog/service.ts
+++ b/frontend/src/app/Catalog/service.ts
@@ -272,8 +272,12 @@ export const sortByName = (arr: T) =>
arr.sort((a, b) => a.name.localeCompare(b.name));
function formatEnrollment(percentage: number) {
- if (percentage === -1) return 'N/A';
- return `${Math.floor(percentage * 100)}% enrolled`;
+ if (percentage === -1 || isNaN(percentage)) return 'N/A';
+ /*
+ Take the min of the percentage and 100 to prevent
+ absurd percentages from being displayed (e.g. 1000% enrolled)
+ */
+ return `${Math.min(Math.floor(percentage * 100), 100)}%`;
}
function colorEnrollment(percentage: number) {
diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx
index 401078b17..404bff4fd 100644
--- a/frontend/src/index.tsx
+++ b/frontend/src/index.tsx
@@ -1,6 +1,7 @@
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { ApolloProvider } from '@apollo/client';
+import { IconoirProvider } from 'iconoir-react';
import Berkeleytime from './Berkeleytime';
import store from './redux/store';
diff --git a/frontend/src/utils/sections/section.ts b/frontend/src/utils/sections/section.ts
index 15f5743b7..40b69c63c 100644
--- a/frontend/src/utils/sections/section.ts
+++ b/frontend/src/utils/sections/section.ts
@@ -56,7 +56,7 @@ export const formatSectionTime = (section: SectionFragment, showNoTime = true):
section.startTime && section.endTime
? `${formatTime(section.startTime)} \u{2013} ${formatTime(section.endTime)}`
: showNoTime
- ? `no time`
+ ? `Unknown time`
: '';
export const formatSectionEnrollment = (section: SectionFragment): ReactNode =>