From fd79564f43de846e34cfdb8eb5e434471fe47863 Mon Sep 17 00:00:00 2001 From: Thomas Hackl <hackl@data-quest.de> Date: Tue, 26 Sep 2023 15:20:21 +0000 Subject: [PATCH] =?UTF-8?q?Resolve=20"Vorlesungsverzeichnis:=20Suche=20suc?= =?UTF-8?q?ht=20nicht=20(nur)=20im=20ausgew=C3=A4hlen=20Bereich"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #3041 Merge request studip/studip!2169 --- resources/vue/components/SearchWidget.vue | 24 +++++++- resources/vue/components/tree/StudipTree.vue | 11 ++-- .../vue/components/tree/StudipTreeList.vue | 7 ++- .../vue/components/tree/StudipTreeTable.vue | 7 ++- .../vue/components/tree/TreeSearchResult.vue | 59 +++++++++++++++---- resources/vue/mixins/TreeMixin.js | 2 +- 6 files changed, 89 insertions(+), 21 deletions(-) diff --git a/resources/vue/components/SearchWidget.vue b/resources/vue/components/SearchWidget.vue index 4b17820b0bb..b583d834242 100644 --- a/resources/vue/components/SearchWidget.vue +++ b/resources/vue/components/SearchWidget.vue @@ -5,7 +5,7 @@ <ul class="needles"> <li> <div class="input-group files-search"> - <input type="text" id="searchterm" name="searchterm" v-model="searchterm" + <input type="text" id="searchterm" name="searchterm" v-model.trim="searchterm" :placeholder="$gettext('Veranstaltung suchen')" :aria-label="$gettext('Veranstaltung suchen')"> <a v-if="isActive" @click.prevent="cancelSearch" class="reset-search"> @@ -13,7 +13,10 @@ </a> <button type="submit" class="submit-search" :title="$gettext('Suchen')" @click.prevent="doSearch"> - <studip-icon shape="search" :size="20"></studip-icon> + <studip-icon shape="search" + :role="maySearch ? 'clickable' : 'inactive'" + :size="20" + ></studip-icon> </button> </div> </li> @@ -33,17 +36,34 @@ export default { StudipIcon, SidebarWidget }, + props: { + minLength: { + type: Number, + default: 0, + } + }, data() { return { searchterm: '', isActive: false }; }, + computed: { + maySearch() { + return this.searchterm.length >= this.minLength; + } + }, methods: { doSearch() { + if (!this.maySearch) { + return; + } + if (this.searchterm !== '') { this.isActive = true; STUDIP.eventBus.emit('do-search', this.searchterm); + } else { + this.cancelSearch(); } }, cancelSearch() { diff --git a/resources/vue/components/tree/StudipTree.vue b/resources/vue/components/tree/StudipTree.vue index ff493739f98..be57f16ddf1 100644 --- a/resources/vue/components/tree/StudipTree.vue +++ b/resources/vue/components/tree/StudipTree.vue @@ -31,7 +31,7 @@ <tree-search-result :search-config="searchConfig"></tree-search-result> </div> <MountingPortal v-if="withSearch" mountTo="#search-widget" name="sidebar-search"> - <search-widget></search-widget> + <search-widget :min-length="3"></search-widget> </MountingPortal> </div> </template> @@ -199,9 +199,12 @@ export default { axios.interceptors.request.eject(loadingIndicator); this.globalOn('do-search', searchterm => { - this.searchConfig.searchterm = searchterm; - this.searchConfig.semester = this.semester; - this.searchConfig.classname = this.startNode.attributes.classname; + this.searchConfig = { + searchterm, + semester: this.semester, + classname: this.startNode.attributes.classname, + startId: this.currentNode.id, + }; this.isSearching = true; }); diff --git a/resources/vue/components/tree/StudipTreeList.vue b/resources/vue/components/tree/StudipTreeList.vue index 0ba4b550224..b607e02197a 100644 --- a/resources/vue/components/tree/StudipTreeList.vue +++ b/resources/vue/components/tree/StudipTreeList.vue @@ -99,7 +99,7 @@ </tr> </tbody> </table> - <MountingPortal v-if="withExport" mountTo="#export-widget" name="sidebar-export"> + <MountingPortal v-if="showExport" mountTo="#export-widget" name="sidebar-export"> <tree-export-widget v-if="courses.length > 0" :title="$gettext('Veranstaltungen exportieren')" :url="exportUrl()" :export-data="courses"></tree-export-widget> @@ -202,6 +202,11 @@ export default { showingAllCourses: false } }, + computed: { + showExport() { + return this.withExport && document.getElementById('export-widget'); + } + }, methods: { openNode(node, pushState = true) { this.currentNode = node; diff --git a/resources/vue/components/tree/StudipTreeTable.vue b/resources/vue/components/tree/StudipTreeTable.vue index 0bfc244fb75..585acd42604 100644 --- a/resources/vue/components/tree/StudipTreeTable.vue +++ b/resources/vue/components/tree/StudipTreeTable.vue @@ -115,7 +115,7 @@ </tr> </draggable> </table> - <MountingPortal v-if="withExport" mountTo="#export-widget" name="sidebar-export"> + <MountingPortal v-if="showExport" mountTo="#export-widget" name="sidebar-export"> <tree-export-widget v-if="courses.length > 0" :title="$gettext('Download des Ergebnisses')" :url="exportUrl()" :export-data="courses"></tree-export-widget> </MountingPortal> @@ -218,6 +218,11 @@ export default { showingAllCourses: false } }, + computed: { + showExport() { + return this.withExport && document.getElementById('export-widget'); + } + }, methods: { openNode(node, pushState = true) { this.currentNode = node; diff --git a/resources/vue/components/tree/TreeSearchResult.vue b/resources/vue/components/tree/TreeSearchResult.vue index 19bc6b927a4..c4e0f415502 100644 --- a/resources/vue/components/tree/TreeSearchResult.vue +++ b/resources/vue/components/tree/TreeSearchResult.vue @@ -1,6 +1,8 @@ <template> <div v-if="isLoading"> - <studip-progress-indicator></studip-progress-indicator> + <studip-progress-indicator v-if="showLoadingAnimation" + :description="searchDescription" + ></studip-progress-indicator> </div> <article v-else class="studip-tree-table"> <table v-if="courses.length > 0" class="default studip-tree-table"> @@ -45,6 +47,10 @@ </tr> </tbody> </table> + <studip-message-box v-else type="info"> + {{ $gettextInterpolate($gettext('Es wurden keine Ergebnisse zu Ihrem Suchbegriff "%{term}" gefunden.'), + { term: searchConfig.searchterm }) }} + </studip-message-box> </article> </template> @@ -54,10 +60,11 @@ import StudipProgressIndicator from '../StudipProgressIndicator.vue'; import StudipIcon from '../StudipIcon.vue'; import TreeNodeCoursePath from './TreeNodeCoursePath.vue'; import TreeCourseDetails from './TreeCourseDetails.vue'; +import StudipMessageBox from "../StudipMessageBox.vue"; export default { name: 'TreeSearchResult', - components: { StudipIcon, StudipProgressIndicator, TreeNodeCoursePath, TreeCourseDetails }, + components: {StudipMessageBox, StudipIcon, StudipProgressIndicator, TreeNodeCoursePath, TreeCourseDetails }, mixins: [ TreeMixin ], props: { searchConfig: { @@ -67,19 +74,47 @@ export default { }, data() { return { + courses: [], + isLoading: true, node: null, - isLoading: false, - isLoaded: false, - courses: [] + showLoadingAnimation: false, + timeout: null, } }, - mounted() { - this.getNode(this.searchConfig.classname + '_root').then(response => { - this.getNodeCourses(response.data.data, this.searchConfig.semester,0, this.searchConfig.searchterm, true) - .then(courses => { - this.courses = courses.data.data; - }); - }); + computed: { + searchDescription() { + return this.$gettextInterpolate( + this.$gettext('Suche nach dem Begriff "%{ term }"'), + {term: this.searchConfig.searchterm} + ); + } + }, + watch: { + searchConfig: { + handler(current, previous) { + if (current.searchterm !== previous?.searchterm) { + this.isLoading = true; + + this.timeout = setTimeout( + () => this.showLoadingAnimation = true, + this.showProgressIndicatorTimeout + ); + + this.getNode(this.searchConfig.startId).then(response => { + return this.getNodeCourses(response.data.data, this.searchConfig.semester, 0, this.searchConfig.searchterm, true); + }).then(courses => { + this.courses = courses.data.data; + + clearTimeout(this.timeout); + + this.isLoading = false; + this.showLoadingAnimation = false; + }); + } + }, + immediate: true, + deep: true + } } } </script> diff --git a/resources/vue/mixins/TreeMixin.js b/resources/vue/mixins/TreeMixin.js index 1e72bbedfbc..9c4c859d7b0 100644 --- a/resources/vue/mixins/TreeMixin.js +++ b/resources/vue/mixins/TreeMixin.js @@ -37,7 +37,7 @@ export const TreeMixin = { parameters['filter[semclass]'] = semClass; } - if (recursive) { + if (node.attributes['has-children'] && recursive) { parameters['filter[recursive]'] = true; } -- GitLab