diff --git a/website/src/assets/arrow-down-1-9-solid.svg b/website/src/assets/arrow-down-1-9-solid.svg new file mode 100644 index 0000000..3485b40 --- /dev/null +++ b/website/src/assets/arrow-down-1-9-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/src/assets/arrow-up-9-1-solid.svg b/website/src/assets/arrow-up-9-1-solid.svg new file mode 100644 index 0000000..393cacc --- /dev/null +++ b/website/src/assets/arrow-up-9-1-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/src/assets/filter-solid.svg b/website/src/assets/filter-solid.svg new file mode 100644 index 0000000..7ddb60f --- /dev/null +++ b/website/src/assets/filter-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/src/assets/question-solid.svg b/website/src/assets/question-solid.svg new file mode 100644 index 0000000..1bd2883 --- /dev/null +++ b/website/src/assets/question-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/src/components/FachDisplay.vue b/website/src/components/FachDisplay.vue index 345cadb..2b2416b 100644 --- a/website/src/components/FachDisplay.vue +++ b/website/src/components/FachDisplay.vue @@ -1,23 +1,33 @@ \ No newline at end of file diff --git a/website/src/components/FilterComponent.vue b/website/src/components/FilterComponent.vue new file mode 100644 index 0000000..ec551d6 --- /dev/null +++ b/website/src/components/FilterComponent.vue @@ -0,0 +1,223 @@ + + + + + diff --git a/website/src/components/ModulleList.vue b/website/src/components/ModulleList.vue index 1c13192..c66312f 100644 --- a/website/src/components/ModulleList.vue +++ b/website/src/components/ModulleList.vue @@ -13,14 +13,14 @@
, + required: true, + }, + sorting: { + type: Object as PropType, + required: true, + }, }); const pflichtbereichListe = computed(() => { @@ -101,9 +112,19 @@ const chosenModules = computed(() => const chosenInOtherModules = computed(() => chosenInOtherModulesIds.value.map((id) => state().getModulById(id)) ); +const filteredChosenInOtherModules = computed(() => + chosenInOtherModules.value.filter(applyFilter).sort((a, b) => + sort(a, b, props.sorting.sortBy) * props.sorting.direction + ) +); const chosableModules = computed(() => chosableIds.value.map((id) => state().getModulById(id)) ); +const filteredChosableModules = computed(() => + chosableModules.value.filter(applyFilter).sort((a, b) => + sort(a, b, props.sorting.sortBy) * props.sorting.direction + ) +); function exedsLimit(modul: Modul) { return !canAcceptChoice( @@ -117,4 +138,65 @@ function exedsLimit(modul: Modul) { function addModul(modul: Modul) { state().addModul(props.slot, modul.id, props.wahlbereichIndex); } + +function applyFilter(modul: Modul) { + if (props.filter.minEcts > modul.lp || props.filter.maxEcts < modul.lp) { + return false; + } + if (props.filter.stammmoduleOnly && !Stammmodule.includes(modul.name.trim())) { + return false; + } + + // semesterfilter + const isSoSe = modul.turnus.toLocaleLowerCase().includes("sose") || modul.turnus.toLocaleLowerCase().includes("ss") || modul.turnus.toLocaleLowerCase().includes("sommersemester"); + const isWiSe = modul.turnus.toLocaleLowerCase().includes("wise") || modul.turnus.toLocaleLowerCase().includes("ws") || modul.turnus.toLocaleLowerCase().includes("wintersemester"); + if (isSoSe && !props.filter.semester.includes("SoSe")) { + return false; + } + if (isWiSe && !props.filter.semester.includes("WiSe")) { + return false; + } + if (!isSoSe && !isWiSe && !props.filter.semester.includes("unknown")) { + return false; + } + + // languagefilter + const isGerman = modul.sprache.toLocaleLowerCase().includes("german") || modul.sprache.toLocaleLowerCase().includes("deutsch"); + const isEnglish = modul.sprache.toLocaleLowerCase().includes("englisch") || modul.sprache.toLocaleLowerCase().includes("english"); + if (isGerman && !props.filter.language.includes("De")) { + return false; + } + if (isEnglish && !props.filter.language.includes("Eng")) { + return false; + } + if (!isGerman && !isEnglish && !props.filter.language.includes("unknown")) { + return false; + } + + if (props.filter.searchString == "") { + return true; + } + const searchParts = props.filter.searchString.toLocaleLowerCase().split(" "); + const nameParts = modul.name.toLocaleLowerCase().split(" "); + return searchParts.some((searchPart) => { + return nameParts.some(n => n.includes(searchPart)); + }); +} + +function sort(a: Modul, b: Modul, sorting: SortingOptions) { + switch (sorting) { + case SortingOptions.NAME: + return a.name.localeCompare(b.name); + case SortingOptions.ECTS: + return a.lp - b.lp; + case SortingOptions.DOZENT: + return a.verantwortlicher.localeCompare(b.verantwortlicher); + case SortingOptions.SEMSTER: + return b.turnus.localeCompare(a.turnus); + case SortingOptions.SPRACHE: + return b.sprache.localeCompare(a.sprache); + default: + return 0; + } +} diff --git a/website/src/components/WahlbereichDisplay.vue b/website/src/components/WahlbereichDisplay.vue index 32f0473..371f5e7 100644 --- a/website/src/components/WahlbereichDisplay.vue +++ b/website/src/components/WahlbereichDisplay.vue @@ -2,7 +2,7 @@

{{name}}

{{getRestrictionString(wahlbereich, lpList)}}

- +
@@ -13,6 +13,8 @@ import getRestrictionString from '../utils/choiceRestrictionStringBuilder'; import ModulleList from './ModulleList.vue'; import FachSlotNames from '../model/FachSlotNames'; import state from '../store/store'; +import { FilterState } from '../model/ui/FilterState'; +import { SortingState } from '../model/ui/SortingState'; const props = defineProps({ wahlbereichIndex: { @@ -22,6 +24,14 @@ const props = defineProps({ slot: { type: String as PropType, required: true + }, + filter: { + type: Object as PropType, + required: true, + }, + sorting: { + type: Object as PropType, + required: true, } }) diff --git a/website/src/components/WahlbereichList.vue b/website/src/components/WahlbereichList.vue index f62c2e6..203119a 100644 --- a/website/src/components/WahlbereichList.vue +++ b/website/src/components/WahlbereichList.vue @@ -4,12 +4,16 @@ :wahlbereich-index="0" :slot="slot" :remaining-lp="remainingLp" + :filter="filter" + :sorting="sorting" />
@@ -20,12 +24,22 @@ import ModulleList from "./ModulleList.vue"; import WahlbereichDisplay from "./WahlbereichDisplay.vue"; import FachSlotNames from "../model/FachSlotNames"; import state from "../store/store"; +import { FilterState } from "../model/ui/FilterState"; +import { SortingState } from "../model/ui/SortingState"; const props = defineProps({ slot: { type: String as PropType, required: true, }, + filter: { + type: Object as PropType, + required: true, + }, + sorting: { + type: Object as PropType, + required: true, + } }); const wahlbereicheCount = computed( diff --git a/website/src/model/ui/FilterState.ts b/website/src/model/ui/FilterState.ts new file mode 100644 index 0000000..5cb0ab5 --- /dev/null +++ b/website/src/model/ui/FilterState.ts @@ -0,0 +1,11 @@ +export interface FilterState { + searchString: string + minEcts: number + maxEcts: number + stammmoduleOnly: boolean + semester: Semester[] + language: Language[] +} + +export type Semester = 'SoSe'|'WiSe'|'unknown' +export type Language = 'De'|'Eng'|'unknown' \ No newline at end of file diff --git a/website/src/model/ui/SortingState.ts b/website/src/model/ui/SortingState.ts new file mode 100644 index 0000000..6897f13 --- /dev/null +++ b/website/src/model/ui/SortingState.ts @@ -0,0 +1,12 @@ +export enum SortingOptions { + NAME = "Alphabetisch", + DOZENT = "Dozent", + ECTS = "ECTS", + SEMSTER = "Semester", + SPRACHE = "Sprache", +} + +export interface SortingState { + sortBy: SortingOptions + direction: 1|-1 +} \ No newline at end of file