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 @@
-
{{ slot }}
+
+
{{ slot }}
+
+
+
+
+
{{getRestrictionString(fach ?? {}, lpListForFach)}}
\ 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 @@
+
+
+
+
+ LP min:
+
+ max:
+
+
+
+ Nur Stammmodule:
+
+
+
+
+
Sort by:
+
+
+ {{ option }}
+
+
+
+
+
+
+
+
+
+
+
+
+
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