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

622 add semester dropdown #645

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion frontend/src/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const router = createBrowserRouter([
ErrorBoundary: Error,
children: [
{ path: '/catalog/:semester?/:abbreviation?/:courseNumber?', Component: Catalog },
{ path: '/scheduler/new', lazy: LocalScheduler }
{ path: '/scheduler/new/', lazy: LocalScheduler }
]
}
]);
Expand Down
47 changes: 34 additions & 13 deletions frontend/src/app/Scheduler/LocalSchedulerPage.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap';

import BTLoader from 'components/Common/BTLoader';
import { DEFAULT_SCHEDULE, Schedule, SCHEDULER_LOCALSTORAGE_KEY } from 'utils/scheduler/scheduler';
import { useUser } from 'graphql/hooks/user';
import { useCreateSchedule } from 'graphql/hooks/schedule';
import { useLocalStorageState } from 'utils/hooks';
import ScheduleEditor from '../../components/Scheduler/ScheduleEditor';
import { useNavigate } from 'react-router-dom';
import { useSemester } from 'graphql/hooks/semester';
import Callout from '../../components/Scheduler/Callout';
import { ReduxState } from 'redux/store';
import { useUser } from 'graphql/hooks/user';
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap';
import { useSelector } from 'react-redux';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { ReduxState } from 'redux/store';
import { useLocalStorageState } from 'utils/hooks';
import { DEFAULT_SCHEDULE, SCHEDULER_LOCALSTORAGE_KEY, Schedule } from 'utils/scheduler/scheduler';
import Callout from '../../components/Scheduler/Callout';
import ScheduleEditor from '../../components/Scheduler/ScheduleEditor';

export function Component() {
const [schedule, setSchedule] = useLocalStorageState<Schedule>(
Expand All @@ -21,7 +20,31 @@ export function Component() {
const { isLoggedIn, loading: loadingUser } = useUser();
const navigate = useNavigate();

const { semester, error: semesterError } = useSemester();
const [searchParams, setSearchParams] = useSearchParams();

const stringToSemester = (string: string | null) => {
if (!string) {
return undefined
}
const [semester, year] = string.trim().toLowerCase().split(' ');
return {
semester,
year
};
};

const { semester, error: semesterError } = useSemester(
searchParams.has('semester') && searchParams.get('semester')
? stringToSemester(searchParams.get('semester'))
: undefined
);

// useEffect(() => {
// const hasSemester = searchParams.has('semester');
// if (!hasSemester) return;
// searchParams.delete('semester');
// setSearchParams(searchParams);
// }, [searchParams, setSearchParams]);

const [createScheduleMutation, { loading: isSaving, error: creationError }] = useCreateSchedule({
onCompleted: (data) => {
Expand Down Expand Up @@ -60,9 +83,7 @@ export function Component() {
return <BTLoader message="Loading semester information..." error={semesterError} fill />;
}

const createSchedule = async () =>
// @ts-ignore
await createScheduleMutation(schedule, semester!);
const createSchedule = async () => await createScheduleMutation(schedule, semester!);

const saveButton = (
<Button
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/assets/scss/bt/scheduler/_onboard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
font-weight: 600;
}

.semester-selector {
border: 1px solid #c7c7c7;
}

.saved-schedules {
margin-top: 3rem;

Expand All @@ -45,6 +49,18 @@
text-align: initial !important;
}
}

.buttons {
width: 100%;
display: flex;
justify-content: center;
gap: 1rem;

.dropdown {
width: 10rem;
flex: none;
}
}
}

.select-classes {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/Scheduler/CourseSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const CourseSelector = ({
Scheduler
</h2>
<BTSelect
courseSearch
isVirtual
value={null}
name="selectClass"
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/Scheduler/Onboard/CourseSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const CourseSelector = ({ allCourses, schedule, setSchedule }: Props) => {
return (
<div className="course-selector">
<BTSelect
courseSearch
value={null}
name="selectClass"
placeholder="Search for a class..."
Expand Down
83 changes: 68 additions & 15 deletions frontend/src/components/Scheduler/Onboard/Welcome.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
import { Container, Row, Col } from 'react-bootstrap';
import { useUser } from '../../../graphql/hooks/user';
import ProfileScheduleCard from './../../Profile/ProfileScheduleCard';
import { Button } from 'bt/custom';
import BTSelect from 'components/Custom/Select';
import { useGetSemestersQuery } from 'graphql';
import { useEffect, useMemo, useState } from 'react';
import { Col, Container, Row } from 'react-bootstrap';
import { useNavigate } from 'react-router-dom';
import { getNodes } from 'utils/graphql';
import { useLocalStorageState } from 'utils/hooks';
import {
DEFAULT_SCHEDULE,
isScheduleEmpty,
Schedule,
SCHEDULER_LOCALSTORAGE_KEY
Semester,
playlistToSemester,
semesterToString
} from 'utils/playlists/semesters';
import {
DEFAULT_SCHEDULE,
SCHEDULER_LOCALSTORAGE_KEY,
Schedule,
isScheduleEmpty
} from 'utils/scheduler/scheduler';
import { Button } from 'bt/custom';
// import { Button } from 'bt/custom';
import { useUser } from '../../../graphql/hooks/user';
import ProfileScheduleCard from './../../Profile/ProfileScheduleCard';

type Props = {
updatePage: (i: number) => void;
const SEMESTER_VALUES: { [label: string]: number } = {
spring: 0.0,
summer: 0.1,
fall: 0.2
};

const Welcome = ({ updatePage }: Props) => {
const Welcome = () => {
const { user } = useUser();

const [schedule, setSchedule] = useLocalStorageState<Schedule>(
Expand All @@ -25,10 +35,35 @@ const Welcome = ({ updatePage }: Props) => {
);

const savedSchedules = user
? getNodes(user.schedules).sort((a, b) => Date.parse(b.dateCreated) - Date.parse(a.dateCreated))
? getNodes(user.schedules).sort(
(a, b) => Date.parse(b.dateCreated as string) - Date.parse(a.dateCreated as string)
)
: [];

const resetDraft = () => setSchedule(DEFAULT_SCHEDULE);
const SemesterToValue = (semester: Semester) => {
return parseInt(semester.year, 10) + SEMESTER_VALUES[semester.semester];
};

const { data } = useGetSemestersQuery({});

type Option = { label: string; value: Semester };

const allSemesters = useMemo((): Option[] => {
return data
? getNodes(data.allPlaylists)
.map((semester) => playlistToSemester(semester))
.sort((a, b) => SemesterToValue(b) - SemesterToValue(a))
.map((semester) => ({ label: semesterToString(semester), value: semester }))
: [];
}, [data]);

const [selectedSemester, setSelectedSemester] = useState<Option | null>(null);

const navigate = useNavigate();

useEffect(() => {
setSelectedSemester(allSemesters[0]);
}, [allSemesters]);

return (
<Container className="welcome">
Expand All @@ -46,7 +81,25 @@ const Welcome = ({ updatePage }: Props) => {
Continue Draft
</Button>
)}
<Button href="/scheduler/new" onClick={resetDraft}>
<BTSelect
className="dropdown"
value={selectedSemester}
closeMenuOnSelect={true}
isSearchable={false}
options={allSemesters}
onChange={(newValue) => {
newValue && setSelectedSemester(newValue);
}}
defaultValue={allSemesters[0]}
/>
<Button
onClick={() => {
setSchedule(DEFAULT_SCHEDULE);
navigate(
`/scheduler/new/${selectedSemester ? '?semester=' + selectedSemester.label : ''}`
);
}}
>
Start
</Button>
</div>
Expand Down
22 changes: 11 additions & 11 deletions frontend/src/graphql/hooks/semester.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useGetSemestersQuery } from 'graphql';
import { ApolloError } from '@apollo/client';
import { useGetSemestersQuery } from 'graphql';
import { getNodes } from 'utils/graphql';
import {
getLatestSemester,
playlistToSemester,
Semester,
semesterToString,
SemesterWithPlaylist
Expand All @@ -21,9 +22,6 @@ export const useSemester = (
error: ApolloError | undefined;
} => {
const { data, loading, error } = useGetSemestersQuery({
variables: {
name: semester && semesterToString(semester)
},
skip: !!semester?.playlistId
});

Expand All @@ -32,13 +30,15 @@ export const useSemester = (
if (semester?.playlistId) {
latestSemester = semester as SemesterWithPlaylist;
} else if (data?.allPlaylists && data.allPlaylists.edges.length >= 1) {
latestSemester = getLatestSemester(getNodes(data.allPlaylists));
}

// Overriding the latest semester because the dev db has data from 2020
if (process.env.NODE_ENV === 'development' && latestSemester) {
latestSemester.semester = 'fall';
latestSemester.year = '2020';
if (semester) {
latestSemester = getNodes(data.allPlaylists)
.map((node) => playlistToSemester(node))
.filter((s) => {
return semesterToString(s) === semesterToString(semester);
})[0];
} else {
latestSemester = getLatestSemester(getNodes(data.allPlaylists));
}
}

return { semester: latestSemester, loading, error };
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/utils/playlists/semesters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ export type SemesterWithPlaylist = Semester & { playlistId: string };
export function getLatestSemester(playlists: FilterablePlaylist[]): SemesterWithPlaylist | null {
const semesterPlaylists = playlists
.filter(
(p) =>
p.category === 'semester' &&
(process.env.NODE_ENV !== 'development' || p.name === 'Fall 2020')
(p) => p.category === 'semester'
// &&
// (process.env.NODE_ENV !== 'development' || p.name === 'Fall 2020')
)
.sort((a, b) => playlistToTimeComparable(b) - playlistToTimeComparable(a));

Expand All @@ -52,15 +52,15 @@ export function getLatestSemester(playlists: FilterablePlaylist[]): SemesterWith
/**
* Converts playlist to semester
*/
function playlistToSemester(playlist: FilterablePlaylist): SemesterWithPlaylist {
export function playlistToSemester(playlist: FilterablePlaylist): SemesterWithPlaylist {
return stringToSemester(playlist.name, playlist.id);
}

/**
* Convert a string to a semester.
*/
function stringToSemester(string: string, playlistId: string): SemesterWithPlaylist;
function stringToSemester(string: string, playlistId?: string): Semester {
export function stringToSemester(string: string, playlistId: string): SemesterWithPlaylist;
export function stringToSemester(string: string, playlistId?: string): Semester {
const [semester, year] = string.trim().toLowerCase().split(' ');
return {
semester,
Expand Down