From 5064bd1ebd791478da8ef82d3f92a0c51f17da1d Mon Sep 17 00:00:00 2001 From: Nutthapat Pongtanyavichai <59821765+Leomotors@users.noreply.github.com> Date: Fri, 11 Aug 2023 09:57:39 +0700 Subject: [PATCH] feat: complete s/u filter --- apps/api/src/course/course.enum.graphql | 12 ++++++++++ apps/api/src/course/course.graphql | 5 ++++ apps/api/src/course/course.service.ts | 9 +++++++ apps/api/src/graphql.ts | 2 ++ .../src/common/components/Chips/config.tsx | 12 ++++++++-- .../src/common/context/Analytics/constants.ts | 1 + .../components/CheckboxGroup/index.tsx | 3 ++- .../components/FilterSection/constants.tsx | 15 ++++++++++-- .../hooks/useFilterBar/index.tsx | 23 +++++++++++++++--- .../components/FilterSection/index.tsx | 21 ++++++++++++++-- .../CourseSearch/components/TagList/index.tsx | 24 +++++++++++++++---- .../useSearchCourseQueryParams/index.tsx | 1 + .../hooks/useSearchCourseQueryParams/types.ts | 1 + .../utils/extractSearchVarsFromQuery/index.ts | 6 +++-- apps/web/src/services/apollo/index.ts | 1 + packages/codegen/src/generated/index.tsx | 9 +++++++ 16 files changed, 128 insertions(+), 17 deletions(-) diff --git a/apps/api/src/course/course.enum.graphql b/apps/api/src/course/course.enum.graphql index 306d8f76b..ed1778738 100644 --- a/apps/api/src/course/course.enum.graphql +++ b/apps/api/src/course/course.enum.graphql @@ -61,3 +61,15 @@ enum GenEdType { """ NO } + +enum GradingType { + """ + S/U + """ + S_U + + """ + Letter Grade + """ + LETTER +} diff --git a/apps/api/src/course/course.graphql b/apps/api/src/course/course.graphql index 7856ecdbf..272077248 100644 --- a/apps/api/src/course/course.graphql +++ b/apps/api/src/course/course.graphql @@ -102,6 +102,11 @@ input FilterInput { """ genEdTypes: [GenEdType!] + """ + List of `GradingTypes`. This filter is passed IF the course's has S/U in Credit Hours. If both filter are given, filters have no effect. + """ + gradingTypes: [GradingType!] + """ List of `DayOfWeeks`. This filter is passed IF ANY of the course's sections have class in ANY of the `dayOfWeeks` in the list. """ diff --git a/apps/api/src/course/course.service.ts b/apps/api/src/course/course.service.ts index c8640ba1c..5580324b8 100644 --- a/apps/api/src/course/course.service.ts +++ b/apps/api/src/course/course.service.ts @@ -57,6 +57,7 @@ export class CourseService { { keyword = '', genEdTypes = [], + gradingTypes, dayOfWeeks = [], limit = 10, offset = 0, @@ -83,6 +84,14 @@ export class CourseService { query.genEdType = { $in: genEdTypes } } + if (gradingTypes && !(gradingTypes.includes('LETTER') && gradingTypes.includes('SU'))) { + if (gradingTypes.includes('LETTER')) { + query.creditHours = { $not: /S\/U/ } + } else if (gradingTypes.includes('SU')) { + query.creditHours = /S\/U/ + } + } + if (dayOfWeeks.length > 0) { query['sections.classes.dayOfWeek'] = { $in: dayOfWeeks } } diff --git a/apps/api/src/graphql.ts b/apps/api/src/graphql.ts index 50f0301ad..1ab1c95b4 100644 --- a/apps/api/src/graphql.ts +++ b/apps/api/src/graphql.ts @@ -10,6 +10,7 @@ export type DayOfWeek = "MO" | "TU" | "WE" | "TH" | "FR" | "SA" | "SU" | "IA" | "AR"; export type StudyProgram = "S" | "T" | "I"; export type GenEdType = "SO" | "HU" | "SC" | "IN" | "NO"; +export type GradingType = "S_U" | "LETTER"; export type ReviewInteractionType = "L" | "D"; export type ReviewStatus = "APPROVED" | "REJECTED" | "PENDING"; @@ -37,6 +38,7 @@ export class PeriodRangeInput { export class FilterInput { keyword?: Nullable; genEdTypes?: Nullable; + gradingTypes?: Nullable; dayOfWeeks?: Nullable; periodRange?: Nullable; limit?: Nullable; diff --git a/apps/web/src/common/components/Chips/config.tsx b/apps/web/src/common/components/Chips/config.tsx index 1d11530b4..b4fc14b5c 100644 --- a/apps/web/src/common/components/Chips/config.tsx +++ b/apps/web/src/common/components/Chips/config.tsx @@ -5,7 +5,7 @@ import { ChipOutlinedHighlightColor, } from '@web/configs/theme/overrides/chip' -import { DayOfWeek, GenEdType } from '@cgr/codegen' +import { DayOfWeek, GenEdType, GradingType } from '@cgr/codegen' /* * This configuration file provide the Chip's types. @@ -33,6 +33,13 @@ export const genEdChipConfig: GenEdChipConfigProps = { NO: { label: 'ไม่ใช่ Gened', color: 'deepGrayOutlined', variant: 'outlined' }, } +export type GradingChipKey = GradingType +export type GradingChipConfigProps = Record +export const gradingChipConfig: GradingChipConfigProps = { + S_U: { label: 'S/U Grade', color: 'blueOutlined', variant: 'outlined' }, + LETTER: { label: 'Letter Grade', color: 'orangeOutlined', variant: 'outlined' }, +} + export type DayChipKey = DayOfWeek export type DayChipConfigProps = Record export const dayChipConfig: DayChipConfigProps = { @@ -67,9 +74,10 @@ export const otherChipConfig: OtherChipConfigProps = { // Add more Chips here; don't forget to add key into `OtherChipKey` too } -export type GeneralChipKey = GenEdChipKey | DayChipKey | OtherChipKey +export type GeneralChipKey = GenEdChipKey | GradingChipKey | DayChipKey | OtherChipKey export const chipConfig = { ...genEdChipConfig, + ...gradingChipConfig, ...dayChipConfig, ...otherChipConfig, } diff --git a/apps/web/src/common/context/Analytics/constants.ts b/apps/web/src/common/context/Analytics/constants.ts index a49385597..f5b763dfd 100644 --- a/apps/web/src/common/context/Analytics/constants.ts +++ b/apps/web/src/common/context/Analytics/constants.ts @@ -10,6 +10,7 @@ export const SNACKBAR_BUTTON = 'snackbar_button' export const SELECTED_COURSES_BUTTON = 'selected_courses_button' export const DAY_FILTER = 'day_search_filter' export const GENED_FILTER = 'gened_search_filter' +export const GRADING_FILTER = 'grading_search_filter' export const PERIOD_RANGE_FILTER = 'period_range_filter' export const SHOPPING_CART_BUTTON = 'shopping_cart_button' export const SHOPPING_CART_REMOVE_COURSE = 'shopping_cart_remove_course' diff --git a/apps/web/src/modules/CourseSearch/components/CheckboxGroup/index.tsx b/apps/web/src/modules/CourseSearch/components/CheckboxGroup/index.tsx index db6e9be23..2820bad6a 100644 --- a/apps/web/src/modules/CourseSearch/components/CheckboxGroup/index.tsx +++ b/apps/web/src/modules/CourseSearch/components/CheckboxGroup/index.tsx @@ -22,9 +22,10 @@ export const CheckboxGroup: React.FC = ({ title, checkboxes, const hasChecked = (tag: GeneralChipKey) => { const genEdTags: string[] = searchCourseQueryParams.filter?.genEdTypes ?? [] + const gradingTypes: string[] = searchCourseQueryParams.filter?.gradingTypes ?? [] const dayOfWeeks: string[] = searchCourseQueryParams.filter?.dayOfWeeks ?? [] - return genEdTags.includes(tag) || dayOfWeeks.includes(tag) + return genEdTags.includes(tag) || gradingTypes.includes(tag) || dayOfWeeks.includes(tag) } return ( diff --git a/apps/web/src/modules/CourseSearch/components/FilterSection/constants.tsx b/apps/web/src/modules/CourseSearch/components/FilterSection/constants.tsx index e0ed242c0..032b7a3cb 100644 --- a/apps/web/src/modules/CourseSearch/components/FilterSection/constants.tsx +++ b/apps/web/src/modules/CourseSearch/components/FilterSection/constants.tsx @@ -1,7 +1,7 @@ -import { DayChipKey, GenEdChipKey } from '@web/common/components/Chips/config' +import { DayChipKey, GenEdChipKey, GradingChipKey } from '@web/common/components/Chips/config' import { CreateCheckbox } from '@web/modules/CourseSearch/components/FilterSection/hooks/useFilterBar' -import { DayOfWeek, GenEdType } from '@cgr/codegen' +import { DayOfWeek, GenEdType, GradingType } from '@cgr/codegen' export const createGenEdCheckboxes: CreateCheckbox[] = [ { @@ -26,6 +26,17 @@ export const createGenEdCheckboxes: CreateCheckbox[] = [ }, ] +export const createGradingCheckboxes: CreateCheckbox[] = [ + { + label: 'S/U Grade', + value: GradingType.SU, + }, + { + label: 'Letter Grade', + value: GradingType.Letter, + }, +] + export const createDayOfWeekCheckboxes: CreateCheckbox[] = [ { label: 'วันจันทร์', diff --git a/apps/web/src/modules/CourseSearch/components/FilterSection/hooks/useFilterBar/index.tsx b/apps/web/src/modules/CourseSearch/components/FilterSection/hooks/useFilterBar/index.tsx index 8a5df2afe..7f532e6a2 100644 --- a/apps/web/src/modules/CourseSearch/components/FilterSection/hooks/useFilterBar/index.tsx +++ b/apps/web/src/modules/CourseSearch/components/FilterSection/hooks/useFilterBar/index.tsx @@ -1,6 +1,11 @@ import { useCallback, useMemo } from 'react' -import { DayChipKey, GenEdChipKey, GeneralChipKey } from '@web/common/components/Chips/config' +import { + DayChipKey, + GenEdChipKey, + GeneralChipKey, + GradingChipKey, +} from '@web/common/components/Chips/config' import { useSearchCourseQueryParams } from '@web/modules/CourseSearch/hooks/useSearchCourseQueryParams' import { SearchCourseQueryVariables } from '@cgr/codegen' @@ -16,7 +21,10 @@ export interface CreateCheckbox { export function useFilterBar( initCheckboxes: CreateCheckbox[], - type?: keyof Pick + type?: keyof Pick< + SearchCourseQueryVariables['filter'], + 'genEdTypes' | 'gradingTypes' | 'dayOfWeeks' + > ) { const { setFilter, searchCourseQueryParams } = useSearchCourseQueryParams() @@ -30,7 +38,16 @@ export function useFilterBar( : (removeTag(searchCourseQueryParams.filter.genEdTypes, tag) as GenEdChipKey[]) setFilter({ ...searchCourseQueryParams.filter, genEdTypes: genEdTypes }) - } else if (type == 'dayOfWeeks') { + } else if (type === 'gradingTypes') { + const gradingTypes: GradingChipKey[] = checked + ? ([tag] as GradingChipKey[]) + : (removeTag(searchCourseQueryParams.filter.gradingTypes, tag) as GradingChipKey[]) + + setFilter({ + ...searchCourseQueryParams.filter, + gradingTypes, + }) + } else if (type === 'dayOfWeeks') { const dayOfWeeks: DayChipKey[] = checked ? (addTag(searchCourseQueryParams.filter.dayOfWeeks, tag) as DayChipKey[]) : (removeTag(searchCourseQueryParams.filter.dayOfWeeks, tag) as DayChipKey[]) diff --git a/apps/web/src/modules/CourseSearch/components/FilterSection/index.tsx b/apps/web/src/modules/CourseSearch/components/FilterSection/index.tsx index 07834d08c..695708f99 100644 --- a/apps/web/src/modules/CourseSearch/components/FilterSection/index.tsx +++ b/apps/web/src/modules/CourseSearch/components/FilterSection/index.tsx @@ -2,12 +2,13 @@ import { DialogContent, Stack, useTheme } from '@mui/material' import useMediaQuery from '@mui/material/useMediaQuery' import useGoogleOptimize from '@react-hook/google-optimize' -import { DayChipKey, GenEdChipKey } from '@web/common/components/Chips/config' +import { DayChipKey, GenEdChipKey, GradingChipKey } from '@web/common/components/Chips/config' import { ResponsiveDialog } from '@web/common/components/ResponsiveDialog' import { Analytics } from '@web/common/context/Analytics/components/Analytics' import { DAY_FILTER, GENED_FILTER, + GRADING_FILTER, PERIOD_RANGE_FILTER, } from '@web/common/context/Analytics/constants' import { GOOGLE_OPTIMIZA_FILTER_ORDER } from '@web/env' @@ -15,6 +16,7 @@ import { CheckboxGroup } from '@web/modules/CourseSearch/components/CheckboxGrou import { createDayOfWeekCheckboxes, // createSpecialCheckboxes, createGenEdCheckboxes, + createGradingCheckboxes, } from '@web/modules/CourseSearch/components/FilterSection/constants' import { FilterSectionProps } from '@web/modules/CourseSearch/components/FilterSection/types' import { tail } from '@web/utils/tail' @@ -29,10 +31,15 @@ export const FilterSection: React.FC = ({ open, handleClose createGenEdCheckboxes, 'genEdTypes' ) + const { checkboxes: gradingCheckboxes } = useFilterBar( + createGradingCheckboxes, + 'gradingTypes' + ) const { checkboxes: dayOfWeekCheckboxes } = useFilterBar( createDayOfWeekCheckboxes, 'dayOfWeeks' ) + // const { checkboxes: specialCheckboxes } = useFilterBar(createSpecialCheckboxes) const hasTags = useHasTags() @@ -42,7 +49,7 @@ export const FilterSection: React.FC = ({ open, handleClose const isExperimentOrder = useGoogleOptimize(GOOGLE_OPTIMIZA_FILTER_ORDER, [false, true]) const filters = [ - + {({ log }) => ( = ({ open, handleClose /> )} , + + {({ log }) => ( + + )} + , {({ log }) => ( { const { searchCourseQueryParams, setFilter } = useSearchCourseQueryParams() const { filter } = searchCourseQueryParams - const { genEdTypes, dayOfWeeks, periodRange } = filter + const { genEdTypes, gradingTypes, dayOfWeeks, periodRange } = filter const { t } = useTranslation('tagList') const handleDeleteGenEdTag = (tag: GenEdChipKey) => { @@ -18,6 +23,12 @@ export const TagList: React.FC = () => { setFilter({ ...searchCourseQueryParams.filter, genEdTypes: modifiedGenEdTags }) } + const handleDeleteGradingTag = (tag: GradingChipKey) => { + if (!gradingTypes) return + const modifiedGradingTags = gradingTypes.filter((currentTag) => currentTag !== tag) + setFilter({ ...searchCourseQueryParams.filter, gradingTypes: modifiedGradingTags }) + } + const handleDeleteDayOfWeekTag = (tag: DayChipKey) => { if (!dayOfWeeks) return const modifiedDayOfWeekTags = dayOfWeeks.filter((currentTag) => currentTag !== tag) @@ -28,7 +39,7 @@ export const TagList: React.FC = () => { setFilter({ ...searchCourseQueryParams.filter, periodRange: undefined }) } - if (!genEdTypes?.length && !dayOfWeeks?.length && !periodRange) { + if (!genEdTypes?.length && !gradingTypes?.length && !dayOfWeeks?.length && !periodRange) { return null } @@ -37,6 +48,9 @@ export const TagList: React.FC = () => { {genEdTypes?.map((tag) => ( handleDeleteGenEdTag(tag)} /> ))} + {gradingTypes?.map((tag) => ( + handleDeleteGradingTag(tag)} /> + ))} {dayOfWeeks?.map((tag) => ( handleDeleteDayOfWeekTag(tag)} /> ))} @@ -55,6 +69,6 @@ export const TagList: React.FC = () => { export function useHasTags() { const { searchCourseQueryParams } = useSearchCourseQueryParams() const { filter } = searchCourseQueryParams - const { genEdTypes, dayOfWeeks } = filter - return !!(genEdTypes?.length || dayOfWeeks?.length) + const { genEdTypes, gradingTypes, dayOfWeeks } = filter + return !!(genEdTypes?.length || gradingTypes?.length || dayOfWeeks?.length) } diff --git a/apps/web/src/modules/CourseSearch/hooks/useSearchCourseQueryParams/index.tsx b/apps/web/src/modules/CourseSearch/hooks/useSearchCourseQueryParams/index.tsx index f7eef53e0..931ebdb8b 100644 --- a/apps/web/src/modules/CourseSearch/hooks/useSearchCourseQueryParams/index.tsx +++ b/apps/web/src/modules/CourseSearch/hooks/useSearchCourseQueryParams/index.tsx @@ -29,6 +29,7 @@ export const useSearchCourseQueryParams = () => { endTime: filterVars?.periodRange?.end, keyword: filterVars.keyword, genEdTypes: filterVars.genEdTypes?.join(','), + gradingTypes: filterVars.gradingTypes?.join(','), dayOfWeeks: filterVars.dayOfWeeks?.join(','), } diff --git a/apps/web/src/modules/CourseSearch/hooks/useSearchCourseQueryParams/types.ts b/apps/web/src/modules/CourseSearch/hooks/useSearchCourseQueryParams/types.ts index d9e58ce27..6b9f8e184 100644 --- a/apps/web/src/modules/CourseSearch/hooks/useSearchCourseQueryParams/types.ts +++ b/apps/web/src/modules/CourseSearch/hooks/useSearchCourseQueryParams/types.ts @@ -3,6 +3,7 @@ import { StudyProgram } from '@cgr/codegen' export interface QueryParams { keyword?: string | null genEdTypes?: string + gradingTypes?: string dayOfWeeks?: string limit?: number offset?: number diff --git a/apps/web/src/modules/CourseSearch/hooks/useSearchCourseQueryParams/utils/extractSearchVarsFromQuery/index.ts b/apps/web/src/modules/CourseSearch/hooks/useSearchCourseQueryParams/utils/extractSearchVarsFromQuery/index.ts index 352783c7b..d7bcbdda2 100644 --- a/apps/web/src/modules/CourseSearch/hooks/useSearchCourseQueryParams/utils/extractSearchVarsFromQuery/index.ts +++ b/apps/web/src/modules/CourseSearch/hooks/useSearchCourseQueryParams/utils/extractSearchVarsFromQuery/index.ts @@ -1,6 +1,6 @@ import { CourseGroup } from '@web/common/hooks/useCourseGroup/types' -import { DayOfWeek, GenEdType, SearchCourseQueryVariables } from '@cgr/codegen' +import { DayOfWeek, GenEdType, GradingType, SearchCourseQueryVariables } from '@cgr/codegen' import { QueryParams } from '../../types' import { removeUndefinedValue } from '../removeUndefinedValue' @@ -9,14 +9,16 @@ export function extractSearchVarsFromQuery( query: QueryParams, courseGroup: CourseGroup ): SearchCourseQueryVariables { - const { keyword, genEdTypes, dayOfWeeks, startTime, endTime } = query + const { keyword, genEdTypes, gradingTypes, dayOfWeeks, startTime, endTime } = query const genEdTypeArray = genEdTypes ? genEdTypes.split(',') : undefined + const gradingTypeArray = gradingTypes ? gradingTypes.split(',') : undefined const dayOfWeekArray = dayOfWeeks ? dayOfWeeks.split(',') : undefined const filter = removeUndefinedValue({ keyword: keyword ? keyword : undefined, genEdTypes: genEdTypeArray ? (genEdTypeArray as GenEdType[]) : undefined, + gradingTypes: gradingTypeArray ? (gradingTypeArray as GradingType[]) : undefined, dayOfWeeks: dayOfWeekArray ? (dayOfWeekArray as DayOfWeek[]) : undefined, periodRange: startTime && endTime diff --git a/apps/web/src/services/apollo/index.ts b/apps/web/src/services/apollo/index.ts index 76c49920a..b4c5d5cf3 100644 --- a/apps/web/src/services/apollo/index.ts +++ b/apps/web/src/services/apollo/index.ts @@ -24,6 +24,7 @@ const cache = new InMemoryCache({ const filter = a.filter hash += ':' + (filter.dayOfWeeks ? JSON.stringify(filter.dayOfWeeks) : '') hash += ':' + (filter.genEdTypes ? JSON.stringify(filter.genEdTypes) : '') + hash += ':' + (filter.gradingTypes ? JSON.stringify(filter.gradingTypes) : '') hash += ':' + (filter.periodRange ? JSON.stringify(filter.periodRange) : '') hash += ':' + (filter.keyword || '') return hash diff --git a/packages/codegen/src/generated/index.tsx b/packages/codegen/src/generated/index.tsx index 11fffcd68..cc3e91df4 100644 --- a/packages/codegen/src/generated/index.tsx +++ b/packages/codegen/src/generated/index.tsx @@ -183,6 +183,8 @@ export type FilterInput = { dayOfWeeks?: InputMaybe>; /** List of `GenEdTypes`. This filter is passed IF the course's `genEdType` matches ANY of the `genEdTypes` in the list. */ genEdTypes?: InputMaybe>; + /** List of `GradingTypes`. This filter is passed IF the course's has S/U in Credit Hours. If both filter are given, filters have no effect. */ + gradingTypes?: InputMaybe>; /** * Keyword to search for courses. This filter is passed IF any of `courseNo`, `abbrName`, * `courseNameTh`, or `courseNameEn` contains the keyword as a substring (except for `courseNo` @@ -213,6 +215,13 @@ export enum GenEdType { So = 'SO' } +export enum GradingType { + /** Letter Grade */ + Letter = 'LETTER', + /** S/U */ + SU = 'S_U' +} + export type Mutation = { __typename?: 'Mutation'; /**