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 all 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
6 changes: 4 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
},
"dependencies": {
"@apollo/client": "^3.2.5",
"@radix-ui/react-tabs": "^1.0.4",
"axios": "^1.2.6",
"bootstrap": "^4.5.2",
"clsx": "^2.0.0",
"color": "^3.1.3",
"date-fns": "^2.21.1",
"fuse.js": "^6.6.2",
Expand All @@ -41,7 +43,7 @@
"react-select": "^5.7.2",
"react-tooltip": "^5.7.2",
"react-window": "^1.8.5",
"recharts": "^2.3.2",
"recharts": "^2.8.0",
"redux": "^4.0.4",
"redux-thunk": "^2.3.0"
},
Expand Down Expand Up @@ -79,7 +81,7 @@
"sass": "^1.50.1",
"serve": "^14.2.0",
"typescript": "^4.9.5",
"vite": "^4.3.1",
"vite": "^4.4.11",
"vite-plugin-html": "^3.2.0",
"vite-plugin-svgr": "^2.4.0",
"vite-tsconfig-paths": "^4.2.0"
Expand Down
21 changes: 8 additions & 13 deletions frontend/src/Berkeleytime.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { memo, useEffect } from 'react';
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { openBanner, enterMobile, exitMobile, openLandingModal } from './redux/common/actions';
import useDimensions from 'react-cool-dimensions';
import easterEgg from 'utils/easterEgg';
import Routes from './Routes';
import { fetchEnrollContext } from 'redux/actions';
import { IconoirProvider } from 'iconoir-react';
import { fetchEnrollContext, fetchGradeContext } from 'redux/actions';

const Berkeleytime = () => {
const dispatch = useDispatch();
Expand All @@ -22,8 +21,10 @@ const Berkeleytime = () => {
});

useEffect(() => {
observe(document.getElementById('root'));
// Fetch enrollment context early on for catalog and enrollment page.
dispatch(fetchEnrollContext());
dispatch(fetchGradeContext());

const bannerType = 'fa23recruitment'; // should match value in ./redux/common/reducer.ts
if (localStorage.getItem('bt-hide-banner') !== bannerType) {
Expand All @@ -41,15 +42,9 @@ const Berkeleytime = () => {
if (localStorage.getItem(key) === null) {
localStorage.setItem(key, key);
}
}, [dispatch]);

return (
<div ref={observe} className="app">
<IconoirProvider iconProps={{ strokeWidth: 2 }}>
<Routes />
</IconoirProvider>
</div>
);
}, [dispatch, observe]);

return <Routes />;
};

export default memo(Berkeleytime);
export default Berkeleytime;
55 changes: 0 additions & 55 deletions frontend/src/app/Catalog/Catalog.tsx

This file was deleted.

3 changes: 0 additions & 3 deletions frontend/src/app/Catalog/CatalogFilters/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,77 +1,47 @@
import {
Dispatch,
memo,
SetStateAction,
useCallback,
useEffect,
useMemo,
useRef,
useState
} from 'react';
import { memo, useEffect, useRef, useState } from 'react';
import { ActionMeta } from 'react-select';
import BTSelect from 'components/Custom/Select';

import catalogService from '../service';
import { ReactComponent as SearchIcon } from 'assets/svg/common/search.svg';
import { ReactComponent as FilterIcon } from 'assets/svg/catalog/filter.svg';
import BTInput from 'components/Custom/Input';
import { CurrentFilters, FilterOption, SortOption, CatalogFilterKeys, CatalogSlug } from '../types';

import { FilterOption, SortOption, CatalogFilterKey, CatalogSlug, FilterTemplate } from '../types';
import { useGetFiltersQuery } from 'graphql';
import BTLoader from 'components/Common/BTLoader';
import { useNavigate, useParams } from 'react-router-dom';

import { useNavigate, useLocation, useParams } from 'react-router';
import styles from './CatalogFilters.module.scss';
import { SortDown, SortUp } from 'iconoir-react';
import useCatalog from '../useCatalog';
import { FILTER_TEMPLATE, SORT_OPTIONS, putFilterOptions } from '../service';

type CatalogFilterProps = {
currentFilters: CurrentFilters;
sortQuery: SortOption;
searchQuery: string;
setCurrentFilters: Dispatch<SetStateAction<CurrentFilters>>;
setSortQuery: Dispatch<SetStateAction<SortOption>>;
setSearchQuery: Dispatch<SetStateAction<string>>;
setDir: Dispatch<SetStateAction<boolean>>;
sortDir: boolean;
};

const { SORT_OPTIONS, FILTER_TEMPLATE, INITIAL_FILTERS } = catalogService;

const CatalogFilters = (props: CatalogFilterProps) => {
const {
currentFilters,
setCurrentFilters,
sortQuery,
searchQuery,
setSortQuery,
setSearchQuery,
setDir,
sortDir
} = props;
const CatalogFilters = () => {
const [template, setTemplate] = useState<FilterTemplate | null>(null);
const { loading, error } = useGetFiltersQuery({
onCompleted: (data) => setTemplate(putFilterOptions(FILTER_TEMPLATE, data))
});

const { data, loading, error } = useGetFiltersQuery();
const [isOpen, setOpen] = useState(false);
const filters = useMemo(() => catalogService.processFilterData(data), [data]);
const navigate = useNavigate();
const location = useLocation();
const slug = useParams<CatalogSlug>();
const [isOpen, setOpen] = useState(false);
const modalRef = useRef<HTMLDivElement>(null);
const [{ sortDir, sortQuery, searchQuery, filters }, dispatch] = useCatalog();

const filterList = useMemo(
() => catalogService.putFilterOptions(FILTER_TEMPLATE, filters),
[filters]
);
useEffect(() => {
const params = new URLSearchParams(location.search);
dispatch({ type: 'search', query: params.get('q') ?? '' });
}, [dispatch, location.search]);

useEffect(() => {
if (filterList?.semester) {
const options = filterList.semester.options as FilterOption[];
if (template?.semester) {
const options = template.semester.options as FilterOption[];
const semester = options.find(({ label }) => label === slug?.semester) ?? null;

setCurrentFilters((prev) => ({
...prev,
semester: semester ?? options[0]
}));
dispatch({
type: 'filter',
filters: { semester: semester ?? options[0] }
});
}
}, [filterList, setCurrentFilters, slug?.semester]);
}, [template, slug?.semester, dispatch]);

useEffect(() => {
const ref = modalRef;
Expand All @@ -88,33 +58,24 @@ const CatalogFilters = (props: CatalogFilterProps) => {
};
}, [isOpen, modalRef, setOpen]);

const handleFilterReset = useCallback(() => {
setSortQuery(SORT_OPTIONS[0]);
setSearchQuery('');

if (filterList) {
const semester = filterList.semester.options[0] as FilterOption;
setCurrentFilters({
...INITIAL_FILTERS,
semester
});

const handleFilterReset = () => {
if (template) {
const semester = template.semester.options[0] as FilterOption;
dispatch({ type: 'reset', filters: { semester } });
navigate({ pathname: `/catalog/${semester.value.name}` });
}
}, [filterList, navigate, setCurrentFilters, setSearchQuery, setSortQuery]);
};

const handleFilterChange = (
newValue: FilterOption | readonly FilterOption[] | null,
meta: ActionMeta<FilterOption>
) => {
const key = meta.name as CatalogFilterKeys;
setCurrentFilters((prev) => ({
...prev,
[key]: newValue
}));

dispatch({
type: 'filter',
filters: { [meta.name as CatalogFilterKey]: newValue }
});
// Update the url slug if semester filter changes.
if (key === 'semester') {
if (meta.name === 'semester') {
navigate({
pathname: `/catalog/${(newValue as FilterOption)?.value?.name}`
.concat(slug?.abbreviation ? `/${slug.abbreviation}` : '')
Expand All @@ -131,8 +92,11 @@ const CatalogFilters = (props: CatalogFilterProps) => {
style={{ border: 'none', width: '100%' }}
value={searchQuery}
onChange={(e) => {
navigate({ pathname: location.pathname, search: `q=${e.target.value}` });
setSearchQuery(e.target.value);
navigate(
{ pathname: location.pathname, search: `q=${e.target.value}` },
{ replace: true }
);
dispatch({ type: 'search', query: e.target.value });
}}
type="search"
placeholder="Search for a class..."
Expand All @@ -153,8 +117,11 @@ const CatalogFilters = (props: CatalogFilterProps) => {
<BTInput
value={searchQuery}
onChange={(e) => {
navigate({ pathname: location.pathname, search: `q=${e.target.value}` });
setSearchQuery(e.target.value);
navigate(
{ pathname: location.pathname, search: `q=${e.target.value}` },
{ replace: true }
);
dispatch({ type: 'search', query: e.target.value });
}}
type="search"
placeholder="Search for a class..."
Expand All @@ -168,20 +135,22 @@ const CatalogFilters = (props: CatalogFilterProps) => {
isClearable={false}
options={SORT_OPTIONS}
isSearchable={false}
onChange={(newValue) => setSortQuery(newValue as SortOption)}
onChange={(newValue) =>
dispatch({ type: 'sort', query: newValue as SortOption })
}
/>
<button onClick={() => setDir((prev) => !prev)}>
{sortDir ? <SortUp color="#8A8A8A" /> : <SortDown color="#8A8A8A" />}
<button onClick={() => dispatch({ type: 'sortDir' })}>
{sortDir === 'DESC' ? <SortUp color="#8A8A8A" /> : <SortDown color="#8A8A8A" />}
</button>
</div>

{filterList &&
Object.entries(filterList).map(([key, filter]) => (
{template &&
Object.entries(template).map(([key, filter]) => (
<div className={styles.item} key={key}>
<p>{filter.name}</p>
<BTSelect
name={key}
value={currentFilters[key as CatalogFilterKeys]}
value={filters[key as CatalogFilterKey]}
isClearable={filter.isClearable}
isMulti={filter.isMulti}
closeMenuOnSelect={filter.closeMenuOnSelect}
Expand Down
18 changes: 11 additions & 7 deletions frontend/src/app/Catalog/CatalogList/CatalogList.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -103,29 +103,33 @@
}
}

.grade {
.gradeWrapper {
display: flex;
justify-content: center;
flex-direction: column;

span {
line-height: 20px;
}
}

.A {
color: $bt-indicator-green;
color: $bt-indicator-green !important;
}

.B {
color: $bt-indicator-yellow;
color: $bt-indicator-yellow !important;
}

.C {
color: $bt-indicator-orange;
color: $bt-indicator-orange !important;
}

.D {
color: $bt-indicator-squash;
color: $bt-indicator-squash !important;
}

.F {
color: $bt-indicator-red;
color: $bt-indicator-red !important;
}

.itemStats {
Expand Down
Loading