diff --git a/application/config/stv.php b/application/config/stv.php index e03c00084..84b148362 100644 --- a/application/config/stv.php +++ b/application/config/stv.php @@ -61,7 +61,11 @@ $config['tabs'] = 'notes' => [ //if true, the count of Messages will be shown in the header of the Tab Messages 'showCountNotes' => true - ] + ], + 'combinePeople' => [ + //multitab should only be shown with this length of selection + 'validCountMulti' => 2, + ], ]; // List of fields to show when ZGV_DOKTOR_ANZEIGEN is defined @@ -117,5 +121,6 @@ $config['students_tab_order'] = [ 'status', 'groups', 'finalexam', + 'combinePeople', 'archive', ]; diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php index 97d626246..922c6a8e7 100644 --- a/application/controllers/api/frontend/v1/stv/Config.php +++ b/application/controllers/api/frontend/v1/stv/Config.php @@ -406,6 +406,11 @@ class Config extends FHCAPI_Controller 'showEdit' => $this->permissionlib->isBerechtigt('admin') ] ]; + $result['combinePeople'] = [ + 'title' => $this->p->t('stv', 'tab_combine_people'), + 'component' => './Stv/Studentenverwaltung/Details/CombinePeople.js', + 'config' => $config['combinePeople'] + ]; $result['kontaktieren'] = [ 'title' => $this->p->t('stv', 'tab_kontaktieren'), diff --git a/application/controllers/api/frontend/v1/stv/Students.php b/application/controllers/api/frontend/v1/stv/Students.php index 12440f036..9dbea65f2 100644 --- a/application/controllers/api/frontend/v1/stv/Students.php +++ b/application/controllers/api/frontend/v1/stv/Students.php @@ -765,6 +765,86 @@ class Students extends FHCAPI_Controller $this->terminateWithSuccess($data); } + /** + * @param string $studiensemester_kurzbz + * + * @return void + */ + public function search($studiensemester_kurzbz) + { + $this->addMeta('ci_method', __FUNCTION__); + $this->addMeta('ci_params', array( + 'studiensemester_kurzbz' => $studiensemester_kurzbz + )); + + $this->load->library('SearchLib', [ 'config' => 'searchstv' ]); + $this->load->library('form_validation'); + + $this->form_validation->set_rules('searchstr', 'searchstr', 'required'); + $this->form_validation->set_rules('types[]', 'types', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $result = $this->searchlib->search($this->input->post('searchstr'), $this->input->post('types')); + + $data = $this->getDataOrTerminateWithError($result); + + + $this->load->model('crm/Prestudent_model', 'PrestudentModel'); + + $this->prepareQuery($studiensemester_kurzbz); + + $this->PrestudentModel->addSelect("COALESCE(v.semester::text, CASE WHEN public.get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL) IN ('Aufgenommener', 'Bewerber', 'Wartender', 'interessent') THEN public.get_absem_prestudent(tbl_prestudent.prestudent_id, NULL)::text ELSE ''::text END) AS semester", false); + $this->PrestudentModel->addSelect('v.verband'); + $this->PrestudentModel->addSelect('v.gruppe'); + + //add status per semester + $this->PrestudentModel->addSelect( + "( + SELECT status_kurzbz + FROM public.tbl_prestudentstatus pss + WHERE pss.prestudent_id = public.tbl_prestudent.prestudent_id + AND pss.studiensemester_kurzbz = " . $this->PrestudentModel->escape($studiensemester_kurzbz) . " + ORDER BY GREATEST(pss.datum, '0001-01-01') DESC + LIMIT 1 + ) AS statusofsemester" + ); + + $this->addSelectPrioRel(); + + $this->addFilter($studiensemester_kurzbz); + + $prestudent_ids = []; + $student_uids = []; + $this->addMeta('data', $data); + foreach ($data as $row) { + $dataset = json_decode($row->data); + if ($row->type == 'prestudent') { + $prestudent_ids[] = $dataset->prestudent_id; + } elseif ($row->type == 'student') { + $student_uids[] = $dataset->uid; + } + } + + if ($prestudent_ids && $student_uids) { + $this->PrestudentModel->db->where_in('tbl_prestudent.prestudent_id', $prestudent_ids); + $this->PrestudentModel->db->or_where_in('s.student_uid', $student_uids); + } elseif ($prestudent_ids) { + $this->PrestudentModel->db->where_in('tbl_prestudent.prestudent_id', $prestudent_ids); + } elseif ($student_uids) { + $this->PrestudentModel->db->where_in('s.student_uid', $student_uids); + } else { + $this->terminateWithSuccess([]); + } + + $result = $this->PrestudentModel->load(); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + /** * @param string|null $studiensemester_kurzbz * @param string $type diff --git a/public/js/api/factory/stv/students.js b/public/js/api/factory/stv/students.js index 07d4453d8..cae2f31b2 100644 --- a/public/js/api/factory/stv/students.js +++ b/public/js/api/factory/stv/students.js @@ -46,6 +46,14 @@ export default { url: url }; }, + search(params, studiensemester_kurzbz) { + return { + method: 'post', + url: 'api/frontend/v1/stv/students/search/' + + encodeURIComponent(studiensemester_kurzbz), + params + }; + }, verband(relative_path) { return { method: 'get', diff --git a/public/js/apps/Studentenverwaltung.js b/public/js/apps/Studentenverwaltung.js index 5eda16dd6..e6f77d5f5 100644 --- a/public/js/apps/Studentenverwaltung.js +++ b/public/js/apps/Studentenverwaltung.js @@ -148,6 +148,44 @@ const router = VueRouter.createRouter({ next(); } }, + { + name: 'search', + path: `/${ciPath}/studentenverwaltung/:studiensemester_kurzbz/search/:searchstr`, + component: FhcStudentenverwaltung, + props(route) { + return { + url_studiensemester_kurzbz: route.params.studiensemester_kurzbz, + url_mode: 'search', + url_prestudent_id: route.params.searchstr + }; + }, + beforeEnter(to, from, next) { + const isSemester = /^[WS]S\d{4}$/.test(to.params.studiensemester_kurzbz); + if (!isSemester) { + return next({name: 'index'}); + } + next(); + } + }, + { + name: 'search_w_types', + path: `/${ciPath}/studentenverwaltung/:studiensemester_kurzbz/search/:types/:searchstr`, + component: FhcStudentenverwaltung, + props(route) { + return { + url_studiensemester_kurzbz: route.params.studiensemester_kurzbz, + url_mode: 'search', + url_prestudent_id: route.params.type + '/' + route.params.searchstr + }; + }, + beforeEnter(to, from, next) { + const isSemester = /^[WS]S\d{4}$/.test(to.params.studiensemester_kurzbz); + if (!isSemester) { + return next({name: 'index'}); + } + next(); + } + }, { path: '/:pathMatch(.*)*', redirect: { diff --git a/public/js/components/Stv/Studentenverwaltung.js b/public/js/components/Stv/Studentenverwaltung.js index a1e38875d..fc0430454 100644 --- a/public/js/components/Stv/Studentenverwaltung.js +++ b/public/js/components/Stv/Studentenverwaltung.js @@ -146,6 +146,9 @@ export default { }, 'url_mode': function () { this.handlePersonUrl(); + }, + url_prestudent_id() { + this.handlePersonUrl(); } }, methods: { @@ -159,7 +162,7 @@ export default { } }, buildPrestudentSearchResultLink(data) { - return this.$fhcApi.getUri( + return this.$api.getUri( '/studentenverwaltung' + '/' + this.studiensemesterKurzbz + '/prestudent/' @@ -167,7 +170,7 @@ export default { ); }, buildStudentSearchResultLink(data) { - return this.$fhcApi.getUri( + return this.$api.getUri( '/studentenverwaltung' + '/' + this.studiensemesterKurzbz + '/student/' @@ -175,7 +178,7 @@ export default { ); }, buildPersonSearchResultLink(data) { - return this.$fhcApi.getUri( + return this.$api.getUri( '/studentenverwaltung' + '/' + this.studiensemesterKurzbz + '/person/' @@ -249,6 +252,21 @@ export default { ApiStv.students.person(this.$route.params.person_id, 'CURRENT_SEMESTER'), true ); + } else if (this.$route.params.searchstr) { + const searchsettings = { + searchstr: this.$route.params.searchstr, + types: this.$route.params.types?.split('+') || [] + }; + + // init into student list + this.$refs.stvList.updateUrl( + ApiStv.students.search(searchsettings, this.studiensemesterKurzbz) + ); + + // init into searchbar + this.$refs.searchbar.searchsettings.searchstr = searchsettings.searchstr; + this.$refs.searchbar.searchsettings.types = searchsettings.types; + this.$nextTick(this.blurSearchbar); } }, checkUrlStudiengang() { @@ -269,6 +287,36 @@ export default { }); } } + }, + onSearch(e) { + const searchsettings = { ...this.$refs.searchbar.searchsettings }; + if (searchsettings.searchstr.length >= 2) { + this.blurSearchbar(); + + if (!searchsettings.types.length || searchsettings.types.length == this.$refs.searchbar.types.length) { + this.$router.push({ + name: 'search', + params: { + studiensemester_kurzbz: this.studiensemesterKurzbz, + searchstr: searchsettings.searchstr + } + }); + } else { + this.$router.push({ + name: 'search_w_types', + params: { + studiensemester_kurzbz: this.studiensemesterKurzbz, + searchstr: searchsettings.searchstr, + types: searchsettings.types.join('+') + } + }); + } + } + }, + blurSearchbar() { + this.$refs.searchbar.$refs.input.blur(); + this.$refs.searchbar.abort(); + this.$refs.searchbar.hideresult(); } }, created() { @@ -376,9 +424,12 @@ export default {
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/CombinePeople.js b/public/js/components/Stv/Studentenverwaltung/Details/CombinePeople.js new file mode 100644 index 000000000..81c1a6860 --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/CombinePeople.js @@ -0,0 +1,83 @@ +export default { + name: "TabCombinePeople", + inject: { + cisRoot: { + from: 'cisRoot' + }, + }, + props: { + modelValue: Object, + }, + data(){ + return { + iframeUrl: null, + viewLoaded: false + } + }, + computed: { + personIds() { + return Array.isArray(this.modelValue) + ? this.modelValue.map(e => e.person_id) + : [this.modelValue.person_id]; + }, + detailStringPerson1(){ + let person1 = this.modelValue[0]; + return person1.vorname + " " + person1.nachname + "(" + person1.person_id + ")"; + }, + detailStringPerson2(){ + let person2 = this.modelValue[1]; + return person2.vorname + " " + person2.nachname + "(" + person2.person_id+ ")"; + }, + + }, + methods: { + combinePeople(){ + this.viewLoaded = true; + let person1_id = this.personIds[0]; + let person2_id = this.personIds[1]; + + if(person1_id == person2_id) { + return this.$fhcAlert.alertError(this.$p.t('stv', 'error_combinePeople_samePerson')); + } + + let linkCombinePeople = this.cisRoot + 'vilesci/stammdaten/personen_wartung.php?person_id_1=' + person1_id + '&person_id_2='+ person2_id; + this.openLink(linkCombinePeople); + }, + openLink(url) { + this.iframeUrl = url; + }, + goBack(){ + this.viewLoaded = false; + this.iframeUrl = null; + } + }, + template: /*html*/ ` +
+ +
+

Personen zusammenlegen

+
+
+

{{$p.t('stv', 'question_combine_people', { person1: detailStringPerson1, person2: detailStringPerson2 })}}

+ +
+
+ ungültige Anzahl: {{this.modelValue.length}} +
+
+
+
+ +
+ + + + +
+ ` + }; \ No newline at end of file diff --git a/public/js/components/Stv/Studentenverwaltung/List.js b/public/js/components/Stv/Studentenverwaltung/List.js index 2614ebc1f..10c012fb0 100644 --- a/public/js/components/Stv/Studentenverwaltung/List.js +++ b/public/js/components/Stv/Studentenverwaltung/List.js @@ -133,7 +133,12 @@ export default { { return Promise.resolve({ data: []}); } - return this.$api.call({method: 'post', url, params}); + /** + * NOTE(chris): Because of a bug in Tabulator + * we need to get the params from elsewhere. + * @see https://github.com/olifolkerd/tabulator/issues/4318 + */ + return this.$api.call({...config, url, params: this.tabulatorOptions.ajaxParams}); }, ajaxResponse: (url, params, response) => { return response?.data; @@ -294,16 +299,17 @@ export default { if (this.filter.length) params.filter = this.filter; + this.tabulatorOptions.ajaxURL = endpoint.url; + this.tabulatorOptions.ajaxParams = { ...params }; + this.tabulatorOptions.ajaxConfig = method; if (!this.$refs.table.tableBuilt) { - if (!this.$refs.table.tabulator) { - this.tabulatorOptions.ajaxURL = endpoint.url; - this.tabulatorOptions.ajaxParams = params; - } else + if (this.$refs.table.tabulator) { this.$refs.table.tabulator.on("tableBuilt", () => { - this.$refs.table.tabulator.setData(endpoint.url, params); + this.$refs.table.tabulator.setData(endpoint.url, params, method); }); + } } else - this.$refs.table.tabulator.setData(endpoint.url, params); + this.$refs.table.tabulator.setData(endpoint.url, params, method); }, dragCleanup(evt) { if (evt.dataTransfer.dropEffect == 'none') diff --git a/public/js/components/Tabs.js b/public/js/components/Tabs.js index 0b9b82027..56506bd9d 100644 --- a/public/js/components/Tabs.js +++ b/public/js/components/Tabs.js @@ -33,7 +33,8 @@ export default { data() { return { current: null, - tabs: {} + tabs: {}, + count: null } }, computed: { @@ -113,10 +114,12 @@ export default { }; } - if (Array.isArray(config)) + if (Array.isArray(config)) { config.forEach((item, key) => _addToTabs(key, item)); - else + } + else { Object.entries(config).forEach(([key, item]) => _addToTabs(key, item)); + } if (this.current === null || !tabs[this.current]) { if (tabs[this.default]) @@ -129,6 +132,57 @@ export default { updateSuffix() { this.getTabSuffix(this.currentTab); }, + removeInvalidCountTabs(){ + if(this.modelValue.length) + { + let countIst = this.modelValue.length; + const tabsToDelete = []; + + Object.entries(this.config).forEach(([key, item]) => { + + const target = item?.config ? item : item?.value || item; + + // check config for validCountMulti + if (target.config?.validCountMulti !== undefined) { + let tab; + let countSoll; + tab = key; + countSoll = target.config.validCountMulti; + + //check if tab is existing + if (countSoll !== undefined && countSoll == countIst) { + //add tab if it was removed before + if (tab in this.tabs == false) { + const value = Vue.reactive({ + suffix: '', + showSuffix: item.showSuffix || false + }); + + this.tabs[tab] = { + component: Vue.markRaw(Vue.defineAsyncComponent(() => import(item.component))), + title: Vue.computed(() => item.title || tab), + config: item.config, + tab, + value, + suffixhelper: item.suffixhelper ?? null + }; + } + } + + //add to toDeleteArray if count is not allowed + if (countSoll !== undefined && countSoll !== countIst) { + tabsToDelete.push(tab); + } + } + }); + + // Delete all tabs with count not allowed + tabsToDelete.forEach(k => { + delete this.tabs[k]; + }); + + } + }, async getTabSuffix(tab) { if (!tab.value.showSuffix) { return; @@ -151,9 +205,11 @@ export default { }, mounted() { this.getTabSuffixes(); + this.removeInvalidCountTabs(); }, updated() { this.getTabSuffixes(); + this.removeInvalidCountTabs(); }, template: `