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

Bug fix: Subject cards now say (Not offered) if "Show in Session" filter button is used #241

Closed
wants to merge 10 commits into from
123 changes: 90 additions & 33 deletions src/common/sidebar/components/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { PropsWithChildren, useCallback } from 'react';

import { ChevronRightIcon, AddIcon, InfoOutlineIcon, CloseIcon } from '@chakra-ui/icons';
import { Box, Text, Flex, VStack, IconButton } from '@chakra-ui/react';
import { WarningTwoIcon, ChevronRightIcon, AddIcon, InfoOutlineIcon, CloseIcon } from '@chakra-ui/icons';
import { Box, Text, Flex, VStack, IconButton, Tooltip } from '@chakra-ui/react';
import { Link, useParams } from 'react-router-dom';

import { useSavedCourses } from 'lib/hooks/useSavedCourses';

export interface CardProps {
/**
* The title of course
Expand Down Expand Up @@ -41,9 +40,21 @@ export interface CardProps {
* Boolean to check if in schedule mode
*/
schedule?: boolean;
/**
* Boolean to check if a subject is in session or not
*/
inSessionSubject?: boolean;
}

export function Card({ subject, title, code, selected, schedule, pid }: PropsWithChildren<CardProps>): JSX.Element {
export function Card({
subject,
title,
code,
selected,
schedule,
pid,
inSessionSubject,
}: PropsWithChildren<CardProps>): JSX.Element {
let { term } = useParams();

const { addCourse, deleteCourse, contains } = useSavedCourses();
Expand Down Expand Up @@ -71,7 +82,15 @@ export function Card({ subject, title, code, selected, schedule, pid }: PropsWit
<Box>
<IconButton
aria-label="Select course"
icon={<ChevronRightIcon />}
icon={
inSessionSubject ? (
<ChevronRightIcon />
) : (
<Tooltip label="No courses are offered in this term for this subject!" aria-label="A tooltip">
<WarningTwoIcon color="red" />
</Tooltip>
)
}
size="md"
background="null"
_hover={{ bg: 'none' }}
Expand Down Expand Up @@ -101,32 +120,70 @@ export function Card({ subject, title, code, selected, schedule, pid }: PropsWit
}
};

return (
<Box
bgColor={selected ? undefined : 'white'}
bgGradient={selected ? 'linear(to-l, #2e95d1, #7cbce2)' : undefined}
color={selected ? 'white' : 'black'}
boxShadow="md"
py={2}
px={4}
my={1}
cursor={!schedule ? 'pointer' : 'auto'}
_hover={{
bgGradient: schedule ? undefined : selected ? undefined : 'linear(to-l, #39c686, #80dbb1)',
color: schedule ? undefined : 'white',
}}
>
<Flex direction="row" alignItems="center" justifyContent="space-between">
<VStack alignItems="start" spacing="0">
<Text fontSize="lg" fontWeight="bold" p={0} m={0}>
{subject} {code}
</Text>
<Text fontSize="sm" fontWeight="normal" p={0} m={0}>
{title}
</Text>
</VStack>
{buttons(code, schedule)}
</Flex>
</Box>
);
if (inSessionSubject === undefined) {
return (
<Box
bgColor={selected ? undefined : 'white'}
bgGradient={selected ? 'linear(to-l, #2e95d1, #7cbce2)' : undefined}
color={selected ? 'white' : 'black'}
boxShadow="md"
py={2}
px={4}
my={1}
cursor={!schedule ? 'pointer' : 'auto'}
_hover={{
bgGradient: schedule ? undefined : selected ? undefined : 'linear(to-l, #39c686, #80dbb1)',

color: schedule ? undefined : 'white',
}}
>
<Flex direction="row" alignItems="center" justifyContent="space-between">
<VStack alignItems="start" spacing="0">
<Text fontSize="lg" fontWeight="bold" p={0} m={0}>
{subject} {code}
</Text>
<Text fontSize="sm" fontWeight="normal" p={0} m={0}>
{title}
</Text>
</VStack>
{buttons(code, schedule)}
</Flex>
</Box>
);
} else {
return (
<Box
bgColor={selected ? (inSessionSubject ? undefined : '#d3d3d3') : inSessionSubject ? 'white' : '#d3d3d3'}
bgGradient={selected ? 'linear(to-l, #2e95d1, #7cbce2)' : undefined}
color={selected ? 'white' : 'black'}
boxShadow="md"
py={2}
px={4}
my={1}
cursor={!schedule ? (inSessionSubject ? 'pointer' : 'auto') : 'auto'}
_hover={{
bgGradient: schedule
? undefined
: selected
? undefined
: inSessionSubject
? 'linear(to-l, #39c686, #80dbb1)'
: 'linear(to-l, #d3d3d3, #d3d3d3)',
color: schedule ? undefined : 'white',
}}
>
<Flex direction="row" alignItems="center" justifyContent="space-between">
<VStack alignItems="start" spacing="0">
<Text fontSize="lg" fontWeight="bold" p={0} m={0}>
{subject} {code}
</Text>
<Text fontSize="sm" fontWeight="normal" p={0} m={0}>
{title}
</Text>
</VStack>
{buttons(code, schedule)}
</Flex>
</Box>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be combined and just conditionally pass in the props?

);
}
}
40 changes: 25 additions & 15 deletions src/common/sidebar/components/SubjectsList.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { LinkBox } from '@chakra-ui/layout';
import { LinkBox, Box } from '@chakra-ui/layout';
import { Collapse } from '@chakra-ui/transition';
import { Link, useLocation, useSearchParams } from 'react-router-dom';

import { KualiSubject } from 'lib/fetchers';
import { InSessionSubject } from 'lib/types';

import { Card } from './Card';

Expand All @@ -15,7 +15,7 @@ type SubjectsListProps = {
* Subject to be displayed
* EX) SENG 265 -> SENG
*/
subjects: KualiSubject[];
subjects: InSessionSubject[];
};

export function SubjectsList({ term, subjects }: SubjectsListProps): JSX.Element {
Expand All @@ -27,18 +27,28 @@ export function SubjectsList({ term, subjects }: SubjectsListProps): JSX.Element

return (
<Collapse in style={{ overflowY: 'scroll' }}>
{subjects.map((subject, index) => (
<LinkBox
as={Link}
to={{
pathname: `/${route}/${term}/${subject.subject}`,
search: pid ? `?pid=${pid}` : undefined,
}}
key={index}
>
<Card subject={subject.subject} title={subject.title} />
</LinkBox>
))}
{subjects.map((subject, index) => {
if (subject.inSession) {
return (
<LinkBox
as={Link}
to={{
pathname: `/${route}/${term}/${subject.subject}`,
search: pid ? `?pid=${pid}` : undefined,
}}
key={index}
>
<Card subject={subject.subject} inSessionSubject={subject.inSession} title={subject.title} />
</LinkBox>
);
} else {
return (
<Box key={index}>
<Card subject={subject.subject} inSessionSubject={subject.inSession} title={subject.title} />
</Box>
);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can use tenary conditional so you can remove the ifs and returns.

})}
</Collapse>
);
}
21 changes: 19 additions & 2 deletions src/common/sidebar/containers/SidebarContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Center, Box, Flex, Heading, HStack, Spinner } from '@chakra-ui/react';
import { Route, Routes } from 'react-router';

import { Course, Term, useGetCourses, useSubjects } from 'lib/fetchers';
import { InSessionSubject } from 'lib/types';

import { CoursesList } from '../components/CoursesList';
import { CustomHits } from '../components/SearchResults';
Expand Down Expand Up @@ -48,13 +49,29 @@ export function SidebarContainer({ searchQuery, term }: SidebarContainerProps):
term: term as Term,
queryParams: { in_session: filter },
});
// re assign the type of subjects so inSession property is available
const inSessionSubjects: InSessionSubject[] | null = subjects;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't required since you're mapping against the original thing. You can just return a InSessionSubject from the map below.


const loading = subjectsLoading || coursesLoading;

// sorts the list of subjects alphabetically
const sortedSubjects = useMemo(() => subjects?.sort((a, b) => (a.subject > b.subject ? 1 : -1)), [subjects]);
const parsedCourses = useMemo(() => computeParsedCourses(courses), [courses]);

// sorts the list of subjects alphabetically
const sortedSubjects = useMemo(
() =>
inSessionSubjects
?.sort((a, b) => (a.subject > b.subject ? 1 : -1))
.map((subject) => {
subject.inSession = true;
//checks if subject is not in session
if (filter && !parsedCourses[subject.subject]) {
subject.inSession = false;
}
return subject;
}),
[filter, inSessionSubjects, parsedCourses]
);

const handleFilter = (s: boolean) => {
setFilter(s);
};
Expand Down
6 changes: 6 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { KualiSubject } from './fetchers';

export interface KualiSubjectInSession {
inSession?: boolean;
}
export type InSessionSubject = KualiSubject & KualiSubjectInSession;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be combined into one type declaration. Also, I would suggest not calling it InSessionSubject as we might use it for other stuff :)