diff --git a/public/js/apps/Studentenverwaltung.js b/public/js/apps/Studentenverwaltung.js index 8be451297..5eda16dd6 100644 --- a/public/js/apps/Studentenverwaltung.js +++ b/public/js/apps/Studentenverwaltung.js @@ -48,6 +48,33 @@ const router = VueRouter.createRouter({ next(); } }, + { + name: 'studiengang', + path: `/${ciPath}/studentenverwaltung/:studiensemester_kurzbz/:studiengang`, + component: FhcStudentenverwaltung, + props: (route) => { + return { + url_studiensemester_kurzbz: route.params.studiensemester_kurzbz, + url_studiengang: route.params.studiengang, + }; + }, + beforeEnter: (to, from, next) => { + const isSemester = /^[WS]S\d{4}$/.test(to.params.studiensemester_kurzbz); + if (!isSemester) { + return next({name: 'index'}); + } + const isStudiengang = /^[A-Z]{3}/.test(to.params.studiengang); + if (!isStudiengang) { + return next({ + name: 'studiensemester', + params: { + studiensemester_kurzbz: to.params.studiensemester_kurzbz + } + }); + } + next(); + } + }, { path: `/${ciPath}/studentenverwaltung/:studiensemester_kurzbz/prestudent/:prestudent_id`, component: FhcStudentenverwaltung, @@ -131,11 +158,14 @@ const router = VueRouter.createRouter({ }); router.afterEach((to, from, failure) => { - if (to.params.studiensemester_kurzbz) { - document.title = to.params.studiensemester_kurzbz + ' - Studierendenverwaltung FH-Complete'; - } else { - document.title = 'Studierendenverwaltung FH-Complete'; + let title = 'Studierendenverwaltung FH-Complete'; + if (to.params.studiengang) { + title = to.params.studiengang + ' ' + title; } + if (to.params.studiensemester_kurzbz) { + title = to.params.studiensemester_kurzbz + ' ' + title; + } + document.title = title; }); const app = Vue.createApp({ diff --git a/public/js/components/Stv/Studentenverwaltung.js b/public/js/components/Stv/Studentenverwaltung.js index 05b71aa48..d0543ad59 100644 --- a/public/js/components/Stv/Studentenverwaltung.js +++ b/public/js/components/Stv/Studentenverwaltung.js @@ -24,6 +24,7 @@ import StvStudiensemester from "./Studentenverwaltung/Studiensemester.js"; import ApiSearchbar from "../../api/factory/searchbar.js"; import ApiStv from "../../api/factory/stv.js"; +import ApiStvVerband from '../../api/factory/stv/verband.js'; export default { @@ -42,7 +43,12 @@ export default { permissions: Object, stvRoot: String, cisRoot: String, - activeAddons: String // semicolon separated list of active addons + activeAddons: String, // semicolon separated list of active addons + url_studiensemester_kurzbz: String, + url_mode: String, + url_prestudent_id: String, + url_tab: String, + url_studiengang: String }, provide() { return { @@ -113,23 +119,30 @@ export default { mergeResults: 'person' }, studiengangKz: undefined, + studiengangKuerzel: '', studiensemesterKurzbz: this.defaultSemester, lists: { nations: [], sprachen: [], geschlechter: [] - } + }, + verbandEndpoint: ApiStvVerband } }, watch: { - '$attrs.url_studiensemester_kurzbz': function (newVal, oldVal) { + 'url_studiensemester_kurzbz': function (newVal, oldVal) { if (newVal !== oldVal) { this.studiensemesterKurzbz = newVal; this.$refs.stvList.updateUrl(); this.$refs.details.reload(); } }, - '$attrs.url_mode': function () { + 'url_studiengang': function (newVal, oldVal) { + if (newVal !== oldVal) { + this.checkUrlStudiengang(); + } + }, + 'url_mode': function () { this.handlePersonUrl(); } }, @@ -173,16 +186,32 @@ export default { { urlpath = 'CURRENT_SEMESTER' + '/' + urlpath; } + this.$refs.stvList.updateUrl(ApiStv.students.verband(urlpath)); + this.studiengangKz = studiengang_kz; - this.$refs.stvList.updateUrl( - ApiStv.students.verband(urlpath) - ); - this.$router.push({ - name: 'studiensemester', - params: { - studiensemester_kurzbz: this.studiensemesterKurzbz - } + const stg = this.lists.stgs.find((element) => { + return (element.studiengang_kz === this.studiengangKz); }); + if (stg) + { + this.studiengangKuerzel = (stg.typ + stg.kurzbz).toUpperCase() + this.$router.push({ + name: 'studiengang', + params: { + studiensemester_kurzbz: this.studiensemesterKurzbz, + studiengang: this.studiengangKuerzel + } + }); + } else + { + this.studiengangKuerzel = ''; + this.$router.push({ + name: 'studiensemester', + params: { + studiensemester_kurzbz: this.studiensemesterKurzbz + } + }); + } }, studiensemesterChanged(v) { this.studiensemesterKurzbz = v; @@ -219,10 +248,29 @@ export default { true ); } + }, + checkUrlStudiengang() { + if (this.url_studiengang) { + const stg = this.lists.stgs.find((element) => { + const kuerzel = (element.typ + element.kurzbz).toUpperCase(); + return (this.url_studiengang === kuerzel); + }); + if (stg) { + this.studiengangKz = stg.studiengang_kz; + this.studiengangKuerzel = (stg.typ + stg.kurzbz).toUpperCase(); + } else { + this.$router.replace({ + name: 'studiensemester', + params: { + studiensemester_kurzbz: this.studiensemesterKurzbz + } + }); + } + } } }, created() { - if (!this.$attrs?.url_studiensemester_kurzbz) { + if (!this.url_studiensemester_kurzbz) { this.$router.replace({ name: 'studiensemester', params: { @@ -230,7 +278,7 @@ export default { } }); } else { - this.studiensemesterKurzbz = this.$attrs.url_studiensemester_kurzbz; + this.studiensemesterKurzbz = this.url_studiensemester_kurzbz; } this.$api @@ -266,6 +314,7 @@ export default { .then(result => { this.lists.stgs = result.data; this.lists.active_stgs = this.lists.stgs.filter(stg => stg.aktiv); + this.checkUrlStudiengang(); }) .catch(this.$fhcAlert.handleSystemError); @@ -299,7 +348,7 @@ export default { template: `
- +
diff --git a/public/js/components/Stv/Studentenverwaltung/Verband.js b/public/js/components/Stv/Studentenverwaltung/Verband.js index 8ae2b6b40..a5d0e55a8 100644 --- a/public/js/components/Stv/Studentenverwaltung/Verband.js +++ b/public/js/components/Stv/Studentenverwaltung/Verband.js @@ -5,8 +5,6 @@ import PvTree from "../../../../../index.ci.php/public/js/components/primevue/tr import PvTreetable from "../../../../../index.ci.php/public/js/components/primevue/treetable/treetable.esm.min.js"; import PvColumn from "../../../../../index.ci.php/public/js/components/primevue/column/column.esm.min.js"; -import ApiStvVerband from '../../../api/factory/stv/verband.js'; - export default { components: { PvTree, @@ -16,11 +14,22 @@ export default { emits: [ 'selectVerband' ], + props: { + endpoint: { + type: Object, + required: true, + }, + preselectedKey: { + type: String, + default: null + } + }, data() { return { loading: true, nodes: [], selectedKey: [], + expandedKeys: {}, filters: {}, // TODO(chris): filter only 1st level? favnodes: [], favorites: {on: false, list: []} @@ -32,6 +41,13 @@ export default { return this.favorites.on ? this.favnodes : this.nodes; } }, + watch: { + 'preselectedKey': function (newVal, oldVal) { + if (newVal !== oldVal) { + this.setPreselection(); + } + } + }, methods: { findNodeByKey(key, arr) { if (!arr) @@ -44,7 +60,7 @@ export default { return res.pop(); return null; }, - onExpandTreeNode(node) { + async onExpandTreeNode(node) { if (!node.children) { if (node.data.link) { let activeEl = null; @@ -55,8 +71,8 @@ export default { }); this.loading = true; - this.$api - .call(ApiStvVerband.get(node.data.link)) + return this.$api + .call(this.endpoint.get(node.data.link)) .then(result => result.data) .then(result => { const subNodes = result.map(this.mapResultToTreeData); @@ -68,7 +84,7 @@ export default { let treeitem = this.$refs.tree.$el.querySelector('[data-tree-item-key="' + node.key + '"]'); treeitem = treeitem.closest('[role="row"]'); - + this.$nextTick(() => { if (activeEl == document.activeElement) treeitem.dispatchEvent(new KeyboardEvent('keydown', { @@ -108,7 +124,7 @@ export default { } this.favorites.on = !this.favorites.on; this.$api - .call(ApiStvVerband.favorites.set( + .call(this.endpoint.favorites.set( JSON.stringify(this.favorites) )); this.loading = false; @@ -135,7 +151,7 @@ export default { for (let parent in sortedInParents) promises.push( this.$api - .call(ApiStvVerband.get(parent == '_' ? '' : parent)) + .call(this.endpoint.get(parent == '_' ? '' : parent)) .then(res => res.data) .then(res => res.filter(node => sortedInParents[parent].includes(node.link + ''))) ); @@ -160,9 +176,9 @@ export default { this.favnodes.push((await this.loadNodes([key.data.link])).pop()); this.favorites.list.push(key.data.link + ''); } - + this.$api - .call(ApiStvVerband.favorites.set( + .call(this.endpoint.favorites.set( JSON.stringify(this.favorites) )); }, @@ -181,19 +197,55 @@ export default { let items = e.target.querySelectorAll('[data-link-fav-add][tabindex="-1"]'); items.forEach(el => el.tabIndex = 0); } + }, + async setPreselection() + { + if (!this.preselectedKey) + return; + + let rawKey = this.preselectedKey + + if (!rawKey || typeof rawKey !== 'string') + return; + + const parts = this.preselectedKey.split('/'); + let currentKey = parts[0]; + let currentNode = this.findNodeByKey(currentKey); + + if (!currentNode) + return; + + for (let i = 1; i < parts.length; i++) + { + this.expandedKeys[currentNode.key] = true; + + await this.onExpandTreeNode(currentNode); + + currentKey += '-' + parts[i]; + currentNode = this.findNodeByKey(currentKey); + + if (!currentNode) + { + return; + } + } + + this.selectedKey = {[currentNode.key]: true}; + this.onSelectTreeNode(currentNode); } }, mounted() { this.$api - .call(ApiStvVerband.get()) + .call(this.endpoint.get()) .then(result => { this.nodes = result.data.map(this.mapResultToTreeData); + this.setPreselection(); this.loading = false; }) .catch(this.$fhcAlert.handleSystemError); this.$api - .call(ApiStvVerband.favorites.get()) + .call(this.endpoint.favorites.get()) .then(result => { if (result.data) { let f = JSON.parse(result.data); @@ -218,6 +270,7 @@ export default { :value="filteredNodes" @node-expand="onExpandTreeNode" selection-mode="single" + v-model:expanded-keys="expandedKeys" v-model:selection-keys="selectedKey" @node-select="onSelectTreeNode" scrollable