Merge branch 'feature-77537/Studvw_Feedback' into studvw_2026_05_rc_tags

This commit is contained in:
Harald Bamberger
2026-07-17 11:38:46 +02:00
13 changed files with 161 additions and 45 deletions
+1 -1
View File
@@ -385,7 +385,7 @@ $config['navigation_menu']['apps'] = [
],
'lav' => [
'link' => site_url('lehre/lehrauftrag/Lehrauftrag/Dashboard'),
'description' => 'Lehraufträge',
'description' => 'Lehrauftragsverwaltung',
#'icon' => 'person-chalkboard',
'requiredPermissions' => array('lehre/lehrauftrag_bestellen:r', 'lehre/lehrauftrag_erteilen:r')
]
@@ -15,7 +15,9 @@ class Tags extends Tag_Controller
'addTag' => self::BERECHTIGUNG_KURZBZ,
'updateTag' => self::BERECHTIGUNG_KURZBZ,
'doneTag' => self::BERECHTIGUNG_KURZBZ,
'deleteTag' => self::BERECHTIGUNG_KURZBZ
'deleteTag' => self::BERECHTIGUNG_KURZBZ,
'getAllTags' => self::BERECHTIGUNG_KURZBZ,
'rebuildTagsForTypeId' => self::BERECHTIGUNG_KURZBZ,
]);
$this->config->load('stv');
+1 -1
View File
@@ -7,7 +7,7 @@ class Tag_Controller extends FHCAPI_Controller
{
private $_uid;
const BERECHTIGUNG_KURZBZ = 'admin:rw';
const BERECHTIGUNG_KURZBZ = ['admin:rw'];
public function __construct($permissions)
{
+1 -1
View File
@@ -72,7 +72,7 @@ class StudentListLib
$this->addSelect('pls.bestaetigtam AS status_bestaetigung');
$this->addSelect("
CASE
WHEN pls.status_kurzbz = 'Interessent'
WHEN pls.status_kurzbz IN ('Interessent','Aufgenommener','Bewerber','Wartender')
THEN pls.ausbildungssemester
ELSE s.semester
END AS semester_berechnet
+2 -1
View File
@@ -60,6 +60,7 @@ class Bisio_model extends DB_Model
JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id)
JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz)
WHERE ss.studiensemester_kurzbz = ?
AND nation_code <> 'A'
AND (
tbl_bisio.von <= ss.ende
AND (
@@ -83,7 +84,7 @@ class Bisio_model extends DB_Model
JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id)
JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz)
WHERE ss.studiensemester_kurzbz = ?
--AND pss.status_kurzbz = 'Student'
AND nation_code <> 'A'
AND (
tbl_bisio.von <= ss.ende
AND (
+7
View File
@@ -96,6 +96,13 @@ html.fs_huge {
overflow: visible !important;
}
/* to enable scrolling for many student entries*/
.stv-details-abschlusspruefung-student-names {
max-height: 80px;
overflow-y: auto;
overflow-x: hidden;
}
.p-dropdown-item{
z-index: 1201 !important;
}
+20 -1
View File
@@ -46,7 +46,6 @@
background-color: #ffA500ff;
}
.tag_braun {
background-color: #6d4c41;
}
@@ -72,6 +71,26 @@
color: black;
}
.tag_turquoise {
background-color: #89cccf;
color: black;
}
.tag_bordeaux {
background-color: #7B1B38;
color: white;
}
.tag_olive {
background-color: #868d07;
color: white;
}
.tag_beige {
background-color: #E4D5B4;
color: black;
}
.tag_done {
text-decoration: line-through;
}
@@ -59,10 +59,6 @@ export default {
from: 'configStvTagsEnabled',
default: false
},
currentSemester: {
from: 'currentSemester',
required: true
},
lists: {
from: 'lists',
required: true
@@ -293,8 +289,10 @@ export default {
this.semesterDates.ende
);
this.$refs.tagWrapper.innerHTML = '';
this.$refs.tagWrapper.appendChild(container);
if(this.headerData.length == 1) {
this.$refs.tagWrapper.innerHTML = '';
this.$refs.tagWrapper.appendChild(container);
}
},
getAllTags(prestudent_id){
return this.$api
@@ -437,6 +435,12 @@ export default {
{{headerData[0].verband}}
<strong v-if="headerData[0].gruppe !== null && headerData[0].gruppe != ' '" class="text-muted"> | {{$p.t('lehre', 'gruppe')}} </strong>
{{headerData[0].gruppe}}
<strong v-if="headerData[0].status=='Interessent'
|| headerData[0].status=='Aufgenommener'
|| headerData[0].status=='Bewerber'
|| headerData[0].status=='Wartender'"
class="text-muted"> | Einstiegssemester {{headerData[0].semester_berechnet}}
</strong>
</template>
<template v-else>
<strong class="text-muted"> | {{$p.t('lehre', 'semester')}} </strong>
@@ -456,6 +460,13 @@ export default {
<span v-else>
<pv-skeleton width="10rem"></pv-skeleton>
</span>
<strong class="text-muted"> | {{$p.t('ui', 'private')}} </strong>
<span v-if="!isLoading">
<a :href="'mailto:'+headerData[0]?.mail_privat">{{headerData[0].mail_privat}}</a>
</span>
<span v-else>
<pv-skeleton width="10rem"></pv-skeleton>
</span>
<strong class="text-muted"> | Status </strong>
<span v-if="noCurrentStatus">
<strong class="text-danger">{{$p.t('lehre', 'textNoStatusInSem', { sem: currentSemester}) }}</strong>
@@ -78,7 +78,7 @@ export default {
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: '200',
minHeight: '200'
}
},
computed: {
@@ -302,7 +302,7 @@ export default {
//prepare local Storage
let STORAGE_KEY = 'finalExamDefaultData';
const id = '20260224_02';
const id = '20260625_01';
const stored = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
if (stored[id]) {
@@ -312,6 +312,7 @@ export default {
this.formData.datum = data.datum;
this.formData.sponsion = data.sponsion;
this.formData.akadgrad_id = data.akadgrad_id;
this.formData.pruefungsantritt_kurzbz = data.pruefungsantritt_kurzbz;
if (data.vorsitz_uid) {
this.selectedVorsitz = {
@@ -393,11 +394,12 @@ export default {
saveOrUpdateLocalStorage(){
let STORAGE_KEY = 'finalExamDefaultData';
const id = '20260224_02';
const id = '20260625_01';
const stored = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
stored[id] = {
pruefungstyp_kurzbz: this.formData.pruefungstyp_kurzbz,
pruefungsantritt_kurzbz: this.formData.pruefungsantritt_kurzbz,
vorsitz_uid: this.selectedVorsitz?.mitarbeiter_uid || null,
vorsitz_person_id: this.selectedVorsitz?.person_id || null,
vorsitz_label: this.selectedVorsitz?.label || null,
@@ -435,20 +437,37 @@ export default {
},
async addNewAbschlusspruefungMulti(){
try {
for (const student of this.studentUids) {
const arraySuccessfullySent = [];
const arrayError = [];
await this.$refs.formFinalExam.call(
ApiStvAbschlusspruefung.addNewAbschlusspruefung({
uid: student,
formData: this.formData
})
);
for (const student of this.studentUids) {
try {
await this.$refs.formFinalExam.call(
ApiStvAbschlusspruefung.addNewAbschlusspruefung({
uid: student,
formData: this.formData
})
);
arraySuccessfullySent.push(student);
} catch (error) {
arrayError.push(student);
}
}
if (arraySuccessfullySent.length) {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
//TODO(Manu) check if really needed in case of success
//this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave') + ': Uids: ' + arraySuccessfullySent.join(", "));
}
if (arrayError.length) {
this.$fhcAlert.alertError(this.$p.t('ui', 'errorSavingData') + ': Uids: ' + arrayError.join(", "));
}
if (arraySuccessfullySent.length) {
this.saveOrUpdateLocalStorage();
this.hideModal("finalexamModal");
this.resetForm();
}
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
//save formData fields in LocalStorage
this.saveOrUpdateLocalStorage();
this.hideModal('finalexamModal');
this.resetForm();
} catch (error) {
this.$fhcAlert.handleSystemError(error);
}
@@ -680,7 +699,12 @@ export default {
<template #title>
<p v-if="statusNew" class="fw-bold mt-3">{{$p.t('abschlusspruefung', 'abschluessPruefungAnlegen')}}</p>
<p v-else class="fw-bold mt-3">{{$p.t('abschlusspruefung', 'abschluessPruefungBearbeiten')}}</p>
<small v-if="this.student.length" class="text-muted">{{studentNames}}</small>
<div
v-if="this.student.length"
class="stv-details-abschlusspruefung-student-names"
>
<small class="text-muted">{{studentNames}}</small>
</div>
</template>
<form-form ref="formFinalExam" @submit.prevent>
@@ -93,7 +93,7 @@ export default {
}
},
{title:"Geschlecht", field:"geschlecht", headerFilter: "list", headerFilterParams: {values:{'m':'männlich','w':'weiblich','x':'divers','u':'unbekannt'}, listOnEmpty:true, autocomplete:true}},
{title:"Sem.", field:"semester_berechnet", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title: "Sem.", field:"semester_berechnet", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title:"Verb.", field:"verband", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title:"Grp.", field:"gruppe", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title:"Studiengang", field:"studiengang", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
@@ -283,7 +283,6 @@ export default {
};
});
},
//TODO(Manu) check: replace download or additional entry?
downloadConfig() {
return {
csv: {
@@ -292,8 +291,9 @@ export default {
options: {
delimiter: ';',
bom: true,
}
}
},
rowRange: this.selectedcount > 0 ? "selected" : "all",
},
};
},
fileString() {
@@ -420,8 +420,12 @@ export default {
rowSelectionChanged(data, rows, selected, deselected) {
this.selectedcount = data.length;
if(selected.length > 0 || deselected.length > 0){
//in case of empty selection (eg. in future or past semester of selected student without sem)
if(selected.length == 0 ) {
this.lastSelected = this.selected;
}
if(selected.length > 0 || deselected.length > 0){
//for tags
this.selectedRows = this.$refs.table.tabulator.getSelectedRows();
@@ -510,6 +514,14 @@ export default {
this.$reloadList();
},
onKeydown(e) { // TODO(chris): this should be in the filter component
if ((e.ctrlKey || e.metaKey) && e.code === "KeyA") {
e.preventDefault();
this.$refs.table.tabulator.deselectRow();
this.$refs.table.tabulator.selectRow();
}
if (!this.focusObj)
return;
@@ -638,7 +650,7 @@ export default {
// TODO(chris): focusin, focusout, keydown and tabindex should be in the filter component
// TODO(chris): filter component column chooser has no accessibilty features
template: `
<div class="stv-list h-100 pt-3">
<div class="stv-list h-100 pt-3">
<div
class="tabulator-container d-flex flex-column h-100"
:class="{'has-filter': filter.length}"
@@ -202,7 +202,7 @@ export default {
return true;
},
download(config) {
this.tabulator.download(config.formatter, config.file, config.options)
this.tabulator.download(config.formatter, config.file, config.options, config.rowRange)
}
},
template: `
@@ -16,28 +16,28 @@ if(!@$db->db_query("SELECT taglib FROM public.tbl_notiz_typ LIMIT 1"))
$tags = [
[
'typ_kurzbz' => 'wh_auto',
'bezeichnung' => '{Wiederholer, repeater}',
'bezeichnung' => '{WH, R}',
'style' => 'tag_dark_grey',
'prioritaet' => 11,
'taglib' => 'tags/CoreWiederholerTagLib'
],
[
'typ_kurzbz' => 'dd_auto',
'bezeichnung' => '{DoubleDegree, DoubleDegree}',
'bezeichnung' => '{DD, DD}',
'style' => 'tag_braun',
'prioritaet' => 10,
'taglib' => 'tags/CoreDoubleDegreeTagLib'
],
[
'typ_kurzbz' => 'out_auto',
'bezeichnung' => '{Outgoing, Outgoing}',
'bezeichnung' => '{Out, Out}',
'style' => 'tag_limette',
'prioritaet' => 12,
'taglib' => 'tags/CoreOutgoingTagLib'
],
[
'typ_kurzbz' => 'prewh_auto',
'bezeichnung' => '{Pre-Wiederholer, pre-repeater}',
'bezeichnung' => '{Pre-WH, pre-R}',
'style' => 'tag_light_grey',
'prioritaet' => 13,
'taglib' => 'tags/CorePrewiederholerTagLib'
@@ -45,28 +45,28 @@ $tags = [
[
'typ_kurzbz' => 'zgv_auto',
'bezeichnung' => '{ZGV offen, ZGV missing}',
'style' => 'tag_lavendel',
'style' => 'tag_beige',
'prioritaet' => 14,
'taglib' => 'tags/CoreMissingZgvTagLib'
],
[
'typ_kurzbz' => 'unterbrecher_auto',
'bezeichnung' => '{Unterbrecher, Interrupter}',
'style' => 'tag_blau',
'bezeichnung' => '{UB, I}',
'style' => 'tag_turquoise',
'prioritaet' => 15,
'taglib' => 'tags/CoreUnterbrecherTagLib'
],
[
'typ_kurzbz' => 'stbtr_erh_auto',
'bezeichnung' => '{erh.Studienbeitrag, Incr. Tuition Fees}',
'style' => 'tag_pfirsich',
'style' => 'tag_bordeaux',
'prioritaet' => 16,
'taglib' => 'tags/CoreStbErhoehtTagLib'
],
[
'typ_kurzbz' => 'jgv_auto',
'bezeichnung' => '{JGV, Year Group Representative}',
'style' => 'tag_gelb',
'bezeichnung' => '{JGV, YGR}',
'style' => 'tag_olive',
'prioritaet' => 17,
'taglib' => 'tags/CoreJgvTagLib'
],
+40
View File
@@ -60938,6 +60938,46 @@ I have been informed that I am under no obligation to consent to the transmissio
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'errorSavingData',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Speichern der Daten nicht erfolgreich',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' =>'Data saving failed',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'private',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Privat',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'private',
'description' => '',
'insertvon' => 'system'
)
)
),
);