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
This commit is contained in:
ma0068
2025-08-06 10:55:58 +02:00
parent 49ddf568e1
commit 42a40072fa
4 changed files with 110 additions and 41 deletions
@@ -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();
@@ -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
+75 -1
View File
@@ -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 = "<dt>" + result.value.data + "</dt>";
messagesSuccessful.push(successTitle + "ok");
} else {
const errorTitle = "<dt>" + result.reason.config.errorHeader + "</dt>";
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 '<dd>' + Object.values(error.messages).join("</dd><dd>") + '</dd>';
}
// TODO(chris): other types
if (error.message)
return '<dd>' + error.message + '</dd>';
if (error.messages)
return '<dd>' + error.messages.join("\n") + '</dd>';
// TODO(chris): what to do here
return '<dd>' + "Generic Error" + '</dd>'; // 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
'<dl>' + messagesError.join("") + '</dl>',
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;
+14 -5
View File
@@ -140,7 +140,16 @@ const helperApp = Vue.createApp({
}
},
template: /* html */`
<pv-toast ref="toast" class="fhc-alert" :base-z-index="99999"></pv-toast>
<pv-toast ref="toast" class="fhc-alert" :base-z-index="99999">
<template #message="{ message }">
<!--span :class="slotProps.iconClass"></span-->
<div class="p-toast-message-text">
<span class="p-toast-summary">{{ message.summary }}</span>
<div v-if="message.detail && message.html" class="p-toast-detail" v-html="message.detail"></div>
<div v-else-if="message.detail" class="p-toast-detail">{{ message.detail }}</div>
</div>
</template>
</pv-toast>
<pv-toast ref="alert" class="fhc-alert" :base-z-index="99999" position="center">
<template #message="slotProps">
<i class="fa fa-circle-exclamation fa-2xl mt-3"></i>
@@ -263,18 +272,18 @@ export default {
});
});
},
alertDefault(severity, title, message, sticky = false) {
let options = { severity: severity, summary: title, detail: message};
alertDefault(severity, title, message, sticky = false, html = false) {
let options = { severity: severity, summary: title, detail: message, html };
if (!sticky)
options.life = 3000;
helperAppInstance.$refs.toast.add(options);
},
alertMultiple(messageArray, severity = 'info', title = 'Info', sticky = false){
alertMultiple(messageArray, severity = 'info', title = 'Info', sticky = false, html = false){
// Check, if array has only string values
if (messageArray.every(message => typeof message === 'string')) {
messageArray.forEach(message => this.alertDefault(severity, title, message, sticky));
messageArray.forEach(message => this.alertDefault(severity, title, message, sticky, html));
return true;
}
return false;