ClassFilter.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <template>
  2. <div class="flex gap-2">
  3. <select
  4. v-if="showType"
  5. v-model="ruleset.column"
  6. class="w-2/6 md:w-3/12"
  7. name="searchType"
  8. @change="filterTypeChanged"
  9. >
  10. <option
  11. :value="ClassSelectorColumn.Module"
  12. default
  13. >
  14. Modul
  15. </option>
  16. <option
  17. :value="ClassSelectorColumn.Degree"
  18. default
  19. >
  20. Studiengang
  21. </option>
  22. <option :value="ClassSelectorColumn.Room">
  23. Raum
  24. </option>
  25. <option :value="ClassSelectorColumn.Class">
  26. Klasse
  27. </option>
  28. <option :value="ClassSelectorColumn.Lecturer">
  29. Dozent
  30. </option>
  31. <option :value="ClassSelectorColumn.Time">
  32. Zeit
  33. </option>
  34. <option :value="ClassSelectorColumn.TeachingType">
  35. Art
  36. </option>
  37. </select>
  38. <div class="w-full justify-center">
  39. <div
  40. v-if="ruleset.column == ClassSelectorColumn.Time"
  41. class="w-full gap-2 grid grid-cols-1"
  42. >
  43. <div class="flex items-center">
  44. <span class="hidden md:inline w-12">Von:</span>
  45. <WeekdayTimePicker
  46. v-model="(ruleset.filterData as Record<string, number>).startTime"
  47. :disable-greater-than="disableUpper"
  48. class="w-full"
  49. />
  50. </div>
  51. <div class="flex items-center">
  52. <span class="hidden md:inline w-12">Bis:</span>
  53. <WeekdayTimePicker
  54. v-model="(ruleset.filterData as Record<string, number>).endTime"
  55. :disable-lower-than="disableLower"
  56. class="w-full"
  57. />
  58. </div>
  59. </div>
  60. <select
  61. v-else-if="ruleset.column == ClassSelectorColumn.Degree"
  62. id="degreeSelector"
  63. v-model="ruleset.filterData.degree"
  64. class="w-full"
  65. @change="
  66. () =>
  67. stateStore.updateLastSelectedDegreeProgram(
  68. ruleset.filterData.degree ?? '',
  69. )
  70. "
  71. >
  72. <option
  73. default
  74. value=""
  75. >
  76. Alle
  77. </option>
  78. <option
  79. v-for="name in classesStore.degreePrograms"
  80. :key="name"
  81. :value="name"
  82. >
  83. {{ name }}
  84. </option>
  85. </select>
  86. <select
  87. v-else-if="ruleset.column == ClassSelectorColumn.TeachingType"
  88. id="teachingTypeSelector"
  89. v-model="ruleset.filterData.teachingType"
  90. class="w-full capitalize"
  91. >
  92. <option
  93. default
  94. value=""
  95. >
  96. Alle
  97. </option>
  98. <option
  99. v-for="name in TeachingType"
  100. :key="name"
  101. :value="name"
  102. >
  103. {{ name }}
  104. </option>
  105. </select>
  106. <input
  107. v-else
  108. v-model="ruleset.filterData.term"
  109. class="w-full"
  110. name="search"
  111. type="text"
  112. :placeholder="searchPlaceholderText"
  113. autocomplete="off"
  114. >
  115. </div>
  116. <div
  117. v-if="showAddRemove"
  118. class="w-32 flex justify-center items-center"
  119. >
  120. <button
  121. class="action-button"
  122. title="Neuen Filter nach diesem einfügen"
  123. @click="$emit('addFilter', ruleset)"
  124. >
  125. <font-awesome-icon icon="fa-solid fa-plus" />
  126. </button>
  127. <button
  128. :disabled="isLastFilter"
  129. class="action-button"
  130. title="Diesen Filter entfernen"
  131. @click="$emit('removeFilter', ruleset)"
  132. >
  133. <font-awesome-icon icon="fa-solid fa-minus" />
  134. </button>
  135. <div
  136. title="Filtering ein/ausschalten"
  137. class="action-checkbox"
  138. @click="filterEnabledClick(false)"
  139. >
  140. <input
  141. v-model="ruleset.enabled"
  142. type="checkbox"
  143. @change="filterEnabledClick(true)"
  144. >
  145. </div>
  146. </div>
  147. </div>
  148. </template>
  149. <script lang="ts">
  150. import { PropType } from "vue";
  151. import { useClassesStore } from "../../stores/classes";
  152. import { useStateStore } from "../../stores/state";
  153. import { ClassSelectorColumn, FilterRule, TeachingType } from "../../types";
  154. import WeekdayTimePicker from "../general/WeekdayTimePicker.vue";
  155. export default {
  156. name: "ClassFilter",
  157. components: { WeekdayTimePicker },
  158. props: {
  159. modelValue: {
  160. required: true,
  161. type: Object as PropType<FilterRule>,
  162. },
  163. isLastFilter: {
  164. required: false,
  165. type: Boolean,
  166. default: true,
  167. },
  168. showType: {
  169. required: false,
  170. type: Boolean,
  171. default: true,
  172. },
  173. showAddRemove: {
  174. required: false,
  175. type: Boolean,
  176. default: true,
  177. },
  178. },
  179. emits: ["update:modelValue", "addFilter", "removeFilter"],
  180. setup() {
  181. const classesStore = useClassesStore();
  182. const stateStore = useStateStore();
  183. return {
  184. ClassSelectorColumn,
  185. classesStore,
  186. stateStore,
  187. TeachingType,
  188. };
  189. },
  190. data() {
  191. return {
  192. ruleset: this.modelValue,
  193. };
  194. },
  195. computed: {
  196. disableUpper(): number {
  197. return Math.floor(
  198. ((this.ruleset.filterData as Record<string, number>).endTime ??
  199. 86400 * 7) / 86400,
  200. );
  201. },
  202. disableLower(): number {
  203. return Math.floor(
  204. ((this.ruleset.filterData as Record<string, number>).startTime ?? 0) /
  205. 86400,
  206. );
  207. },
  208. searchPlaceholderText(): string {
  209. switch (this.ruleset.column) {
  210. case ClassSelectorColumn.Module:
  211. return "Suche nach Modulkürzel oder Name";
  212. case ClassSelectorColumn.Room:
  213. return "Suche nach Raumbezeichnung";
  214. case ClassSelectorColumn.Class:
  215. return "Suche nach Klassenbezeichnung";
  216. case ClassSelectorColumn.Lecturer:
  217. return "Suche nach Dozentenkürzel";
  218. case ClassSelectorColumn.Time:
  219. break;
  220. }
  221. return "";
  222. },
  223. },
  224. methods: {
  225. filterEnabledClick(direct: boolean) {
  226. if (!direct) {
  227. // Someone pressed the box around the checkbox.
  228. this.ruleset.enabled = !this.ruleset.enabled;
  229. } else {
  230. // Only notify changes when the value change is detected directly
  231. this.filterChanged();
  232. }
  233. },
  234. filterChanged() {
  235. this.$emit("update:modelValue", this.ruleset);
  236. },
  237. filterTypeChanged() {
  238. if (this.ruleset.column == ClassSelectorColumn.Degree) {
  239. if (!this.ruleset.filterData.degree)
  240. this.ruleset.filterData.degree = this.classesStore.degreePrograms[0];
  241. }
  242. if (this.ruleset.column == ClassSelectorColumn.TeachingType) {
  243. if (!this.ruleset.filterData.teachingType)
  244. this.ruleset.filterData.teachingType = "";
  245. }
  246. },
  247. },
  248. };
  249. </script>
  250. <style scoped>
  251. @reference "../../style.css";
  252. .action-button {
  253. @apply h-5
  254. text-xs
  255. md:h-7
  256. md:text-base
  257. mx-0.5
  258. md:mx-1
  259. block
  260. bg-gray-200
  261. hover:bg-gray-300
  262. active:bg-gray-400
  263. disabled:bg-gray-200
  264. disabled:text-gray-400
  265. dark:bg-gray-700
  266. dark:hover:bg-gray-600
  267. dark:active:bg-gray-700
  268. dark:disabled:bg-gray-800
  269. dark:disabled:text-gray-600
  270. disabled:pointer-events-none
  271. aspect-square
  272. rounded-sm
  273. transition-all
  274. duration-200;
  275. }
  276. .action-checkbox {
  277. @apply h-5
  278. text-xs
  279. m-2
  280. md:h-7
  281. md:text-base
  282. mx-0.5
  283. md:mx-1
  284. bg-gray-200
  285. hover:bg-gray-300
  286. active:bg-gray-400
  287. disabled:bg-gray-200
  288. disabled:text-gray-400
  289. dark:bg-gray-700
  290. dark:hover:bg-gray-600
  291. dark:active:bg-gray-700
  292. dark:disabled:bg-gray-800
  293. dark:disabled:text-gray-600
  294. disabled:pointer-events-none
  295. aspect-square
  296. rounded-sm
  297. transition-all
  298. duration-200
  299. items-center
  300. flex
  301. cursor-pointer;
  302. }
  303. .action-checkbox input {
  304. @apply cursor-pointer;
  305. }
  306. </style>