From 49ddf568e101f9c7d89a571a5e6bd6ada3ed3d1f Mon Sep 17 00:00:00 2001 From: chfhtw Date: Wed, 6 Aug 2025 08:46:02 +0200 Subject: [PATCH 1/8] filter component: keep column visibility on reload --- public/js/components/filter/Filter.js | 58 ++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/public/js/components/filter/Filter.js b/public/js/components/filter/Filter.js index fc4ed81c5..07f88cccc 100644 --- a/public/js/components/filter/Filter.js +++ b/public/js/components/filter/Filter.js @@ -91,7 +91,6 @@ export const CoreFilterCmpt = { dataset: null, datasetMetadata: null, selectedFields: null, - notSelectedFields: null, filterFields: null, availableFilters: null, @@ -103,6 +102,8 @@ export const CoreFilterCmpt = { fetchCmptApiFunctionParams: null, fetchCmptDataFetched: null, + fetchResult: null, + tabulator: null, tableBuilt: false, tabulatorHasSelector: false, @@ -118,6 +119,11 @@ export const CoreFilterCmpt = { }; }, computed: { + notSelectedFields() { + if (!this.fields || !this.selectedFields) + return null; + return this.fields.filter(x => this.selectedFields.indexOf(x) === -1) + }, filteredData() { if (!this.dataset) return []; @@ -215,6 +221,32 @@ export const CoreFilterCmpt = { await this.$p.loadCategory('ui'); placeholder = this.$p.t('ui/keineDatenVorhanden'); } + + if (!this.tableOnly) { + // prefetch data to get fields & selectedFields for filteredColumns & filteredData + await new Promise(resolve => { + const filterId = window.location.hash ? window.location.hash.slice(1) : null; + + const resolvePromiseFunc = data => { + this.setRenderData(data); + resolve(); + }; + // get the filter data + if (filterId === null) + this.startFetchCmpt( + wsParams => this.$api.call(ApiFilter.getFilter(wsParams)), + null, + resolvePromiseFunc + ); + else + this.startFetchCmpt( + wsParams => this.$api.call(ApiFilter.getFilterById(wsParams)), + { filterId }, + resolvePromiseFunc + ); + }); + } + // Define a default tabulator options in case it was not provided let tabulatorOptions = {...{ layout: "fitDataStretchFrozen", @@ -236,6 +268,11 @@ export const CoreFilterCmpt = { if (!this.tableOnly) { tabulatorOptions.data = this.filteredData; tabulatorOptions.columns = this.filteredColumns; + } else { + tabulatorOptions.columns.forEach(col => { + if (col.visible === undefined) + col.visible = true; + }); } if (tabulatorOptions.selectable || (tabulatorOptions.columns && tabulatorOptions.columns.filter(el => el.formatter == 'rowSelection').length)) @@ -355,18 +392,14 @@ export const CoreFilterCmpt = { this.render ); }, - /** - * - */ - render(response) { - let data = response; + setRenderData(data) { + this.fetchResult = data; this.filterName = data.filterName; this.dataset = data.dataset; this.datasetMetadata = data.datasetMetadata; this.fields = data.fields; this.selectedFields = data.selectedFields; - this.notSelectedFields = this.fields.filter(x => this.selectedFields.indexOf(x) === -1); this.filterFields = []; for (let i = 0; i < data.datasetMetadata.length; i++) @@ -383,6 +416,14 @@ export const CoreFilterCmpt = { } } } + }, + /** + * + */ + render(response) { + let data = response; + + this.setRenderData(data); // If the side menu is active if (this.sideMenu === true) @@ -623,11 +664,10 @@ export const CoreFilterCmpt = { this.$emit('uuidDefined', this.uuid) }, mounted() { - this.initTabulator().then(() => { if (!this.tableOnly) { this.selectedFilter = window.location.hash ? window.location.hash.slice(1) : null; - this.getFilter(); // get the filter data + this.render(this.fetchResult); } }); From 42a40072fa0eb741a767b3fb00dc9b3f272d7e46 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Wed, 6 Aug 2025 10:55:58 +0200 Subject: [PATCH 2/8] show all errors in one alert - refactor Dropdown.js: use new factory for overwriting errors - move functionaliy to show Errors and InfoAlerts to Api.js - extend FhcAlert (primeVue) with slot for toast for ability to use html --- .../api/frontend/v1/stv/Status.php | 7 +- .../Details/Status/Dropdown.js | 49 +++++------- public/js/plugins/Api.js | 76 ++++++++++++++++++- public/js/plugins/FhcAlert.js | 19 +++-- 4 files changed, 110 insertions(+), 41 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Status.php b/application/controllers/api/frontend/v1/stv/Status.php index fcc8007cd..7e649f3cf 100644 --- a/application/controllers/api/frontend/v1/stv/Status.php +++ b/application/controllers/api/frontend/v1/stv/Status.php @@ -24,7 +24,6 @@ class Status extends FHCAPI_Controller 'updateStatus' => ['admin:rw', 'assistenz:rw'], 'advanceStatus' => ['admin:rw', 'assistenz:rw'], 'confirmStatus' => ['admin:rw', 'assistenz:rw'], - ]); //Load Models @@ -435,9 +434,10 @@ class Status extends FHCAPI_Controller ]); if (!$this->form_validation->run()) + { $this->terminateWithValidationErrors($this->form_validation->error_array()); + } - $this->load->library('PrestudentLib'); $this->db->trans_start(); @@ -623,8 +623,9 @@ class Status extends FHCAPI_Controller ]); if (!$this->form_validation->run()) + { $this->terminateWithValidationErrors($this->form_validation->error_array()); - + } // Start DB transaction $this->db->trans_start(); diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Status/Dropdown.js b/public/js/components/Stv/Studentenverwaltung/Details/Status/Dropdown.js index ab8f83fab..cff6b2be6 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Status/Dropdown.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Status/Dropdown.js @@ -142,14 +142,15 @@ export default { this.addStudent({status_kurzbz: 'student', statusgrund_id}); }, addStudent(data) { - Promise - .allSettled( - this.prestudentIds.map(prestudent_id => this.$api.call( - ApiStvStatus.addStudent(prestudent_id, data), - { errorHeader: prestudent_id } - )) - ) - .then(res => this.showFeedback(res, data.status_kurzbz)); + this.$api.call(this.prestudentIds.map(prestudent_id => [ + prestudent_id, + ApiStvStatus.addStudent(prestudent_id, data), + { errorHeader: prestudent_id } + ])) + .then(() => { + this.$emit('reloadTable'); + this.$reloadList(); + }); }, changeStatusToAbbrecher(statusgrund_id) { this @@ -242,31 +243,15 @@ export default { return askForSemester(); }, changeStatus(data) { - Promise - .allSettled( - this.prestudentIds.map(prestudent_id => this.$api.call( - ApiStvStatus.changeStatus(prestudent_id, data), - { errorHeader: prestudent_id } - )) - ) - .then(res => this.showFeedback(res, data.status_kurzbz)); - }, - showFeedback(results, status_kurzbz) { - const countSuccess = results.filter(result => result.status == "fulfilled").length; - const countError = results.length - countSuccess; - - //Feedback Success als infoalert - this.$fhcAlert.alertInfo(this.$p.t('ui', 'successNewStatus', { - countSuccess, - status: status_kurzbz, - countError - })); - - if(results.length == 1 && countSuccess > 0){ + this.$api.call(this.prestudentIds.map(prestudent_id => [ + prestudent_id, + ApiStvStatus.changeStatus(prestudent_id, data) + ])) + .then(() => { this.$emit('reloadTable'); - } - this.$reloadList(); - } + this.$reloadList(); + }); + }, }, created() { this.$api diff --git a/public/js/plugins/Api.js b/public/js/plugins/Api.js index d0ffaa439..47139b79f 100644 --- a/public/js/plugins/Api.js +++ b/public/js/plugins/Api.js @@ -42,10 +42,13 @@ export default { } function _clean_return_value(response) { + if (typeof response.data === 'string' || response.data instanceof String) + return _clean_return_value({ data: response }); + const result = response.data; delete response.data; if (!result.meta) - result.meta = {response}; + result.meta = { response }; else result.meta.response = response; return result; @@ -159,6 +162,77 @@ export default { return fhcApiAxios.post(uri, data, config); }, call(factory, configoverwrite, form) { + if (Array.isArray(factory)) { + const $fhcAlert = app.config.globalProperties.$fhcAlert; + const $api = app.config.globalProperties.$api; + + Promise + .allSettled(factory.map((config, index) => { + if (Array.isArray(config)) + return $api.call(config[1], { + errorHeader: config[0], + errorHandling: false + }); + else + return $api.call(config, { + errorHeader: '#' + index, + errorHandling: false + }); + })) + .then(res => { + // TODO(chris): obey form & configoverwrite + let messagesError = []; + let messagesSuccessful = []; + + res.forEach(result => { + if (result.status === 'fulfilled') { + //console.log(JSON.parse(result.value.data)); + const successTitle = "
" + result.value.data + "
"; + messagesSuccessful.push(successTitle + "ok"); + } else { + const errorTitle = "
" + result.reason.config.errorHeader + "
"; + const errorMsg = JSON.parse(result.reason.request.response); + const fullMessage = errorMsg.errors.map(error => { + if (error.type == 'validation') { + // TODO(chris): do we want the keys? + return '
' + Object.values(error.messages).join("
") + '
'; + } + // TODO(chris): other types + if (error.message) + return '
' + error.message + '
'; + if (error.messages) + return '
' + error.messages.join("\n") + '
'; + // TODO(chris): what to do here + return '
' + "Generic Error" + '
'; // TODO(chris): translate + }).join("\n"); + messagesError.push(errorTitle + fullMessage); + } + }); + + if (messagesError.length) + { + const test = document.createElement('b'); + $fhcAlert.alertDefault( + 'error', + messagesError.length + " Fehler", // TODO(chris): translate + '
' + messagesError.join("") + '
', + true, + true + ); + } + if (messagesSuccessful.length) + { + const test = document.createElement('b'); + $fhcAlert.alertDefault( + 'info', + 'Feedback', + messagesSuccessful.length + " erfolgreiche Statusänderung(en) durchgeführt", // TODO(chris): translate + false, + true + ); + } + }); + } let {method, url, params, config} = factory; if (configoverwrite !== undefined) { config = configoverwrite; diff --git a/public/js/plugins/FhcAlert.js b/public/js/plugins/FhcAlert.js index f90b61ac2..5d84b1de9 100644 --- a/public/js/plugins/FhcAlert.js +++ b/public/js/plugins/FhcAlert.js @@ -140,7 +140,16 @@ const helperApp = Vue.createApp({ } }, template: /* html */` - + + +