Projektarbeit Studierendenverwaltung: now possible to save and edit Projektbetreuer

This commit is contained in:
Alexei Karpenko
2025-05-16 13:37:21 +02:00
parent 405062f549
commit 382006aa8b
14 changed files with 1002 additions and 934 deletions
@@ -0,0 +1,73 @@
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export default {
getProjektbetreuer(projektarbeit_id ) {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektbetreuer/getProjektbetreuer',
params: { projektarbeit_id }
};
},
getBetreuerarten() {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektbetreuer/getBetreuerarten'
};
},
getDefaultStundensaetze(person_id, studiensemester_kurzbz) {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektbetreuer/getDefaultStundensaetze',
params: { person_id, studiensemester_kurzbz }
};
},
getNoten() {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektbetreuer/getNoten'
};
},
saveProjektbetreuer(projektarbeit_id, projektbetreuerListe) {
return {
method: 'post',
url: 'api/frontend/v1/stv/projektbetreuer/saveProjektbetreuer',
params: { projektarbeit_id, projektbetreuerListe }
};
},
deleteProjektbetreuer(projektarbeit_id, person_id, betreuerart_kurzbz) {
return {
method: 'post',
url: 'api/frontend/v1/stv/projektbetreuer/deleteProjektbetreuer',
params: { projektarbeit_id, person_id, betreuerart_kurzbz }
};
},
getProjektbetreuerBySearchQuery(searchString) {
return {
method: 'get',
url: 'api/frontend/v1/stv/projektbetreuer/getProjektbetreuerBySearchQuery',
params: { searchString }
};
},
validateProjektbetreuer(projektbetreuer) {
return {
method: 'post',
url: 'api/frontend/v1/stv/projektbetreuer/validateProjektbetreuer',
params: { projektbetreuer }
};
}
};
@@ -1,847 +0,0 @@
import {CoreFilterCmpt} from "../../../../filter/Filter.js";
import FormForm from '../../../../Form/Form.js';
import FormInput from '../../../../Form/Input.js';
import PvAutoComplete from "../../../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js";
import ApiStvAbschlusspruefung from '../../../../../api/factory/stv/abschlusspruefung.js';
export default {
components: {
CoreFilterCmpt,
BsModal,
FormForm,
FormInput,
PvAutoComplete,
AbschlusspruefungDropdown,
PruefungList
},
inject: {
cisRoot: {
from: 'cisRoot'
},
config: {
from: 'config',
required: true
},
$reloadList: {
from: '$reloadList',
required: true
},
isBerechtigtDocAndOdt: {
from: 'hasPermissionOutputformat',
default: false
}
},
computed: {
studentUids() {
if (this.student.uid)
{
return [this.student.uid];
}
return this.student.map(e => e.uid);
},
studentKzs(){
if (this.student.uid)
{
return [this.student.studiengang_kz];
}
return this.student.map(e => e.studiengang_kz);
},
stg_kz(){
return this.studentKzs[0];
},
},
props: {
student: Object
},
data() {
return {
tabulatorOptions: {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(ApiStvAbschlusspruefung.getAbschlusspruefung(this.student.uid)),
ajaxResponse: (url, params, response) => response.data,
columns: [
{title: "vorsitz", field: "vorsitz_nachname"},
{title: "abschlussbeurteilung", field: "beurteilung_bezeichnung"},
{title: "prueferIn1", field: "p1_nachname", visible: false},
{title: "prueferIn2", field: "p2_nachname", visible: false},
{title: "prueferIn3", field: "p3_nachname", visible: false},
{
title: "datum",
field: "datum",
formatter: function (cell) {
const dateStr = cell.getValue();
if (!dateStr) return "";
const date = new Date(dateStr);
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour12: false
});
}
},
{title: "uhrzeit", field: "uhrzeit"},
{
title: "freigabe",
field: "freigabedatum",
formatter: function (cell) {
const dateStr = cell.getValue();
if (!dateStr) return "";
const date = new Date(dateStr);
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour12: false
});
}
},
{title: "pruefungsantritt", field: "antritt_bezeichnung"},
{
title: "sponsion",
field: "sponsion",
formatter: function (cell) {
const dateStr = cell.getValue();
if (!dateStr) return "";
const date = new Date(dateStr);
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour12: false
});
}
},
{title: "anmerkung", field: "anmerkung"},
{title: "abschlusspruefung_id", field: "abschlusspruefung_id", visible: false},
{title: "typ", field: "pruefungstyp_kurzbz", visible: false},
{
title: 'Aktionen', field: 'actions',
minWidth: 150, // Ensures Action-buttons will be always fully displayed
formatter: (cell, formatterParams, onRendered) => {
let container = document.createElement('div');
container.className = "d-flex gap-2";
let button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-edit"></i>';
button.title = this.$p.t('ui', 'bearbeiten');
button.addEventListener('click', (event) =>
this.actionEditAbschlusspruefung(cell.getData().abschlusspruefung_id)
);
container.append(button);
button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-xmark"></i>';
button.title = this.$p.t('ui', 'loeschen');
button.addEventListener('click', () =>
this.actionDeleteAbschlusspruefung(cell.getData().abschlusspruefung_id)
);
container.append(button);
container.append(cell.getData().actionDiv);
return container;
},
frozen: true
},
],
layout: 'fitDataFill',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: '200',
selectable: true,
index: 'abschlusspruefung_id',
persistenceID: 'stv-details-finalexam'
},
tabulatorEvents: [
{
event: 'dataLoaded',
handler: data => this.tabulatorData = data.map(item => {
item.actionDiv = document.createElement('div');
return item;
}),
},
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['global', 'person', 'stv', 'abschlusspruefung', 'ui']);
let cm = this.$refs.table.tabulator.columnManager;
cm.getColumnByField('vorsitz_nachname').component.updateDefinition({
title: this.$p.t('abschlusspruefung', 'vorsitz_header')
});
cm.getColumnByField('beurteilung_bezeichnung').component.updateDefinition({
title: this.$p.t('abschlusspruefung', 'abschlussbeurteilung')
});
cm.getColumnByField('p1_nachname').component.updateDefinition({
title: this.$p.t('abschlusspruefung', 'pruefer1')
});
cm.getColumnByField('p2_nachname').component.updateDefinition({
title: this.$p.t('abschlusspruefung', 'pruefer2')
});
cm.getColumnByField('p3_nachname').component.updateDefinition({
title: this.$p.t('abschlusspruefung', 'pruefer3')
});
cm.getColumnByField('datum').component.updateDefinition({
title: this.$p.t('global', 'datum')
});
cm.getColumnByField('uhrzeit').component.updateDefinition({
title: this.$p.t('global', 'uhrzeit')
});
cm.getColumnByField('freigabedatum').component.updateDefinition({
title: this.$p.t('abschlusspruefung', 'freigabe')
});
cm.getColumnByField('antritt_bezeichnung').component.updateDefinition({
title: this.$p.t('abschlusspruefung', 'pruefungsantritt')
});
cm.getColumnByField('sponsion').component.updateDefinition({
title: this.$p.t('abschlusspruefung', 'sponsion')
});
cm.getColumnByField('anmerkung').component.updateDefinition({
title: this.$p.t('global', 'anmerkung')
});
cm.getColumnByField('pruefungstyp_kurzbz').component.updateDefinition({
title: this.$p.t('global', 'typ')
});
cm.getColumnByField('abschlusspruefung_id').component.updateDefinition({
title: this.$p.t('ui', 'abschlusspruefung_id')
});
/*
cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'aktionen')
});
*/
}
}
],
tabulatorData: [],
lastSelected: null,
formData: {
typStg: null,
pruefungstyp_kurzbz: null,
akadgrad_id: null,
vorsitz: null,
pruefungsantritt_kurzbz: null,
abschlussbeurteilung_kurzbz: null,
datum: null,
sponsion: null,
pruefer1: null,
pruefer2: null,
pruefer3: null,
anmerkung: null,
protokoll: null,
note: null,
link: null
},
statusNew: true,
arrTypen: [],
arrAntritte: [],
arrBeurteilungen: [],
arrAkadGrad: [],
arrNoten: [],
filteredMitarbeiter: [],
filteredPruefer: [],
abortController: {
mitarbeiter: null,
pruefer: null
},
stgInfo: { typ: '', oe_kurzbz: '' }
}
},
watch: {
student(){
if (this.$refs.table) {
this.$refs.table.reloadTable();
}
this.getStudiengangByKz();
}
},
methods: {
getStudiengangByKz(){
this.stgInfo = { typ: '', oe_kurzbz: '' };
this.$api
.call(ApiStudiengang.getStudiengangByKz(this.stg_kz))
.then(result => this.stgInfo = result.data)
.catch(this.$fhcAlert.handleSystemError);
},
actionNewAbschlusspruefung() {
this.resetForm();
this.statusNew = true;
this.$refs.finalexamModal.show();
this.setDefaultFormData();
},
actionEditAbschlusspruefung(abschlusspruefung_id) {
this.resetForm();
this.statusNew = false;
this.$refs.finalexamModal.show();
this.loadAbschlusspruefung(abschlusspruefung_id);
},
actionDeleteAbschlusspruefung(abschlusspruefung_id) {
this.$fhcAlert
.confirmDelete()
.then(result => result
? abschlusspruefung_id
: Promise.reject({handled: true}))
.then(this.deleteAbschlusspruefung)
.catch(this.$fhcAlert.handleSystemError);
},
addNewAbschlusspruefung() {
const dataToSend = {
uid: this.student.uid,
formData: this.formData
};
return this.$refs.formFinalExam
.call(ApiStvAbschlusspruefung.addNewAbschlusspruefung(dataToSend))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
this.hideModal('finalexamModal');
this.resetForm();
})
.catch(this.$fhcAlert.handleSystemError)
.finally(() => {
this.reload();
});
},
hideModal(modalRef){
this.$refs[modalRef].hide();
},
reload() {
this.$refs.table.reloadTable();
},
loadAbschlusspruefung(abschlusspruefung_id) {
return this.$api
.call(ApiStvAbschlusspruefung.loadAbschlusspruefung(abschlusspruefung_id))
.then(result => {
this.formData = result.data;
//TODO(Manu) check if cisRoot is okay
this.formData.link = this.cisRoot + 'index.ci.php/lehre/Pruefungsprotokoll/showProtokoll?abschlusspruefung_id=' + this.formData.abschlusspruefung_id + '&fhc_controller_id=67481e5ed5490';
return result;
})
.catch(this.$fhcAlert.handleSystemError);
},
updateAbschlusspruefung(abschlusspruefung_id) {
const dataToSend = {
id: abschlusspruefung_id,
formData: this.formData
};
return this.$refs.formFinalExam
.call(ApiStvAbschlusspruefung.updateAbschlusspruefung(dataToSend))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
this.hideModal('finalexamModal');
this.resetForm();
})
.catch(this.$fhcAlert.handleSystemError)
.finally(() => {
this.reload();
});
},
deleteAbschlusspruefung(abschlusspruefung_id) {
return this.$api
.call(ApiStvAbschlusspruefung.deleteAbschlusspruefung(abschlusspruefung_id))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
})
.catch(this.$fhcAlert.handleSystemError)
.finally(() => {
this.reload();
});
},
resetForm() {
this.formData.pruefungstyp_kurzbz = null;
this.formData.akadgrad_id = null;
this.formData.vorsitz = null;
this.formData.pruefungsantritt_kurzbz = null;
this.formData.abschlussbeurteilung_kurzbz = null;
this.formData.datum = null; //oder new Date();
this.formData.sponsion = null;
this.formData.pruefer1 = null;
this.formData.pruefer2 = null;
this.formData.pruefer3 = null;
this.formData.anmerkung = null;
this.formData.protokoll = null;
this.formData.note = null;
this.formData.p1 = null;
this.formData.p2 = null;
this.formData.p3 = null;
this.formData.pv = null;
},
search(event) {
if (this.abortController.mitarbeiter) {
this.abortController.mitarbeiter.abort();
}
this.abortController.mitarbeiter = new AbortController();
return this.$api
.call(ApiStvAbschlusspruefung.getMitarbeiter(event.query))
.then(result => {
this.filteredMitarbeiter = result.data.retval;
});
},
searchNotAkad(event) {
if (this.abortController.pruefer) {
this.abortController.pruefer.abort();
}
this.abortController.pruefer = new AbortController();
return this.$api
.call(ApiStvAbschlusspruefung.getPruefer(event.query))
.then(result => {
this.filteredPruefer = result.data.retval;
});
},
setDefaultFormData() {
this.resetForm();
if (this.stgInfo.typ === 'b') {
this.formData.pruefungstyp_kurzbz = 'Bachelor';
this.formData.protokoll = this.$p.t('abschlusspruefung', 'pruefungsnotizenMaster');
}
if (this.stgInfo.typ === 'd' || this.stgInfo === 'm') {
this.formData.pruefungstyp_kurzbz = 'Diplom';
this.formData.protokoll = this.$p.t('abschlusspruefung', 'pruefungsnotizenMaster');
}
if (this.stgInfo.typ === 'lg') {
this.formData.pruefungstyp_kurzbz = 'lgabschluss';
}
if (!this.formData.akadgrad_id && this.arrAkadGrad.length > 0) {
this.formData.akadgrad_id = this.arrAkadGrad[0].akadgrad_id;
}
},
printDocument(link) {
window.open(link, '_blank');
},
},
created() {
this.$api
.call(ApiStvAbschlusspruefung.getTypenAbschlusspruefung())
.then(result => {
this.arrTypen = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
this.$api
.call(ApiStvAbschlusspruefung.getTypenAntritte())
.then(result => {
this.arrAntritte = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
this.$api
.call(ApiStvAbschlusspruefung.getBeurteilungen())
.then(result => {
this.arrBeurteilungen = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
this.$api
.call(ApiStvAbschlusspruefung.getNoten())
.then(result => {
this.arrNoten = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
this.$api
.call(ApiStvAbschlusspruefung.getAkadGrade(this.student.studiengang_kz))
.then(result => {
this.arrAkadGrad = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
if (!this.student.length) {
this.$api
.call(ApiStudiengang.getStudiengangByKz(this.student.studiengang_kz))
.then(result => {
this.stgInfo = result.data;
this.setDefaultFormData();
})
.catch(this.$fhcAlert.handleSystemError);
} else
this.getStudiengangByKz();
},
template: `
<div class="stv-details-abschlusspruefung h-100 pb-3">
<h4>{{this.$p.t('stv','tab_finalexam')}}</h4>
<div v-if="this.student.length">
<abschlusspruefung-dropdown
:showAllFormats="isBerechtigtDocAndOdt"
:studentUids="studentUids"
:showDropDownMulti="true"
:stgTyp="stgInfo.typ"
:stgKz="stg_kz"
:cisRoot="cisRoot"
@linkGenerated="printDocument"
></abschlusspruefung-dropdown>
</div>
<core-filter-cmpt
v-if="!this.student.length"
ref="table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
:side-menu="false"
reload
new-btn-show
:new-btn-label="this.$p.t('stv', 'tab_finalexam')"
@click:new="actionNewAbschlusspruefung"
>
</core-filter-cmpt>
<!--Modal: finalexamModal-->
<bs-modal ref="finalexamModal" dialog-class="modal-xl modal-dialog-scrollable">
<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>
</template>
<pruefung-list ref="pruefungList" :uid="student.uid"></pruefung-list>
<form-form v-if="!this.student.length" ref="formFinalExam" @submit.prevent>
<legend>{{this.$p.t('global','details')}}</legend>
<p v-if="statusNew">[{{$p.t('ui', 'neu')}}]</p>
<div class="row mb-3">
<form-input
container-class="col-6 stv-details-abschlusspruefung-typ"
:label="$p.t('global', 'typ')"
type="select"
v-model="formData.pruefungstyp_kurzbz"
name="pruefungstyp_kurzbz"
>
<option
v-for="typ in arrTypen"
:key="typ.pruefungstyp_kurzbz"
:value="typ.pruefungstyp_kurzbz"
>
{{typ.beschreibung}}
</option>
</form-input>
<form-input
container-class="col-6 stv-details-abschlusspruefung-note"
:label="$p.t('abschlusspruefung', 'notekommpruefung')"
type="select"
v-model="formData.note"
name="note"
>
<option :value="null"> -- {{$p.t('fehlermonitoring', 'keineAuswahl')}} -- </option>
<option
v-for="note in arrNoten"
:key="note.note"
:value="note.note"
>
{{note.bezeichnung}}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
container-class="col-6 stv-details-abschlusspruefung-pruefungsantritt"
:label="$p.t('abschlusspruefung', 'pruefungsantritt')"
type="select"
v-model="formData.pruefungsantritt_kurzbz"
name="pruefungsantritt_kurzbz"
>
<option :value="null"> -- {{$p.t('fehlermonitoring', 'keineAuswahl')}} -- </option>
<option
v-for="antritt in arrAntritte"
:key="antritt.pruefungsantritt_kurzbz"
:value="antritt.pruefungsantritt_kurzbz"
>
{{antritt.bezeichnung}}
</option>
</form-input>
</div>
<div class="row mb-3">
<template v-if="statusNew">
<form-input
container-class="col-6 stv-details-abschlusspruefung-vorsitz"
:label="$p.t('abschlusspruefung', 'vorsitz_header')"
type="autocomplete"
optionLabel="mitarbeiter"
v-model="formData.vorsitz"
name="vorsitz"
:suggestions="filteredMitarbeiter"
@complete="search"
:min-length="3"
>
</form-input>
</template>
<template v-else >
<form-input
v-if= "formData.pv"
container-class="col-6 stv-details-abschlusspruefung-vorsitz"
type="text"
name="name"
:label="$p.t('abschlusspruefung', 'vorsitz_header')"
v-model="formData.pv"
>
</form-input>
<form-input
v-else
container-class="col-6 stv-details-abschlusspruefung-vorsitz"
:label="$p.t('abschlusspruefung', 'vorsitz_header')"
type="autocomplete"
optionLabel="mitarbeiter"
v-model="formData.vorsitz"
name="vorsitz"
:suggestions="filteredMitarbeiter"
@complete="search"
:min-length="3"
>
</form-input>
</template>
<template v-if="statusNew">
<form-input
container-class="col-6 stv-details-abschlusspruefung-pruefer1"
:label="$p.t('abschlusspruefung', 'pruefer1')"
type="autocomplete"
optionLabel="mitarbeiter"
v-model="formData.pruefer1"
name="pruefer1"
:suggestions="filteredPruefer"
@complete="searchNotAkad"
:min-length="3"
>
</form-input>
</template>
<template v-else >
<form-input
v-if= "formData.p1"
container-class="col-6 stv-details-abschlusspruefung-pruefer1"
type="text"
name="name"
:label="$p.t('abschlusspruefung', 'pruefer1')"
v-model="formData.p1"
>
</form-input>
<form-input
v-else
container-class="col-6 stv-details-abschlusspruefung-pruefer1"
:label="$p.t('abschlusspruefung', 'pruefer1')"
type="autocomplete"
optionLabel="mitarbeiter"
v-model="formData.pruefer1"
name="pruefer1"
:suggestions="filteredPruefer"
@complete="searchNotAkad"
:min-length="3"
>
</form-input>
</template>
</div>
<div class="row mb-3">
<form-input
container-class="col-6 stv-details-abschlusspruefung-abschlussbeurteilung_kurzbz"
:label="$p.t('abschlusspruefung', 'abschlussbeurteilung')"
type="select"
v-model="formData.abschlussbeurteilung_kurzbz"
name="abschlussbeurteilung_kurzbz"
>
<option :value="null"> -- {{$p.t('fehlermonitoring', 'keineAuswahl')}} -- </option>
<option
v-for="beurteilung in arrBeurteilungen"
:key="beurteilung.abschlussbeurteilung_kurzbz"
:value="beurteilung.abschlussbeurteilung_kurzbz"
>
{{beurteilung.bezeichnung}}
</option>
</form-input>
<template v-if="statusNew">
<form-input
container-class="col-6 stv-details-abschlusspruefung-pruefer2"
:label="$p.t('abschlusspruefung', 'pruefer2')"
type="autocomplete"
optionLabel="mitarbeiter"
v-model="formData.pruefer2"
name="pruefer2"
:suggestions="filteredPruefer"
@complete="searchNotAkad"
:min-length="3"
>
</form-input>
</template>
<template v-else >
<form-input
v-if= "formData.p2"
container-class="col-6 stv-details-abschlusspruefung-pruefer2"
type="text"
name="name"
:label="$p.t('abschlusspruefung', 'pruefer2')"
v-model="formData.p2"
>
</form-input>
<form-input
v-else
container-class="col-6 stv-details-abschlusspruefung-pruefer2"
:label="$p.t('abschlusspruefung', 'pruefer2')"
type="autocomplete"
optionLabel="mitarbeiter"
v-model="formData.pruefer2"
name="pruefer2"
:suggestions="filteredPruefer"
@complete="searchNotAkad"
:min-length="3"
>
</form-input>
</template>
</div>
<div class="row mb-3">
<form-input
container-class="col-6 stv-details-abschlusspruefung-akadgrad"
:label="$p.t('abschlusspruefung', 'akadGrad')"
type="select"
v-model="formData.akadgrad_id"
name="akadgrad_id"
>
<option
v-for="grad in arrAkadGrad"
:key="grad.akadgrad_id"
:value="grad.akadgrad_id"
>
{{grad.titel}}
</option>
</form-input>
<template v-if="statusNew">
<form-input
container-class="col-6 stv-details-abschlusspruefung-pruefer3"
:label="$p.t('abschlusspruefung', 'pruefer3')"
type="autocomplete"
optionLabel="mitarbeiter"
v-model="formData.pruefer3"
name="pruefer3"
:suggestions="filteredPruefer"
@complete="searchNotAkad"
:min-length="3"
>
</form-input>
</template>
<template v-else >
<form-input
v-if= "formData.p3"
container-class="col-6 stv-details-abschlusspruefung-pruefer3"
type="text"
name="name"
:label="$p.t('abschlusspruefung', 'pruefer3')"
v-model="formData.p3"
>
</form-input>
<form-input
v-else
container-class="col-6 stv-details-abschlusspruefung-pruefer3"
:label="$p.t('abschlusspruefung', 'pruefer3')"
type="autocomplete"
optionLabel="mitarbeiter"
v-model="formData.pruefer3"
name="pruefer3"
:suggestions="filteredPruefer"
@complete="searchNotAkad"
:min-length="3"
>
</form-input>
</template>
</div>
<div class="row mb-3">
<form-input
container-class="col-6 stv-details-abschlusspruefung-datum"
:label="$p.t('global', 'datum')"
type="DatePicker"
v-model="formData.datum"
auto-apply
:enable-time-picker="false"
format="dd.MM.yyyy"
name="datum"
:teleport="true"
>
</form-input>
<form-input
container-class="col-6 stv-details-abschlusspruefung-anmerkung"
:label="$p.t('global', 'anmerkung')"
type="textarea"
v-model="formData.anmerkung"
name="anmerkung"
>
</form-input>
</div>
<div class="row mb-3">
<form-input
container-class="col-6 stv-details-abschlusspruefung-sponsion"
:label="$p.t('abschlusspruefung', 'sponsion')"
type="DatePicker"
v-model="formData.sponsion"
auto-apply
:enable-time-picker="false"
format="dd.MM.yyyy"
name="sponsion"
:teleport="true"
>
</form-input>
<form-input
container-class="col-6 stv-details-abschlusspruefung-protokoll"
:label="$p.t('abschlusspruefung', 'protokoll')"
type="textarea"
v-model="formData.protokoll"
name="protokoll"
:rows= 10
>
</form-input>
</div>
<div class="row mb-3 col-6">
<div class="col">
<p >{{$p.t('abschlusspruefung', 'zurBeurteilung')}}</p>
</div>
<div class="col">
<p>
<a :href="formData.link" target="_blank" rel="noopener noreferrer">
{{$p.t('abschlusspruefung', 'pruefungsprotokoll')}}
</a>
</p>
</div>
</div>
</form-form>
<template #footer>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{$p.t('ui', 'abbrechen')}}</button>
<button v-if="statusNew" class="btn btn-primary" @click="addNewAbschlusspruefung()"> {{$p.t('ui', 'speichern')}}</button>
<button v-else class="btn btn-primary" @click="updateAbschlusspruefung(formData.abschlusspruefung_id)"> {{$p.t('ui', 'speichern')}}</button>
</template>
</bs-modal>
<Teleport v-for="data in tabulatorData" :key="data.abschlusspruefung_id" :to="data.actionDiv">
<abschlusspruefung-dropdown
:showAllFormats="isBerechtigtDocAndOdt"
:showDropDownMulti="false"
:abschlusspruefung_id="data.abschlusspruefung_id"
:studentUids="data.student_uid"
:stgPrfgTyp="data.pruefungstyp_kurzbz"
:stgKz="stg_kz"
:cisRoot="cisRoot"
@linkGenerated="printDocument"
></abschlusspruefung-dropdown>
</Teleport>
</div>
`
}
@@ -1,4 +1,3 @@
import {CoreFilterCmpt} from "../../../../filter/Filter.js";
import FormForm from '../../../../Form/Form.js';
import FormInput from '../../../../Form/Input.js';
import PvAutoComplete from "../../../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js";
@@ -7,7 +6,6 @@ import ApiStvProjektarbeit from '../../../../../api/factory/stv/projektarbeit.js
export default {
components: {
CoreFilterCmpt,
FormForm,
FormInput,
PvAutoComplete
@@ -48,23 +46,12 @@ export default {
}
return lehreinheiten;
},
//~ preparedFormData() {
//~ console.log("FOR DATA CALLEd");
//~ if (this.formData.projektarbeit_id == null) delete(this.formData.projektarbeit_id);
//~ if (this.formData.firma) this.formData.firma_id = this.formData.firma.firma_id;
//~ delete(this.formData.firma);
//~ delete(this.formData.firma_name);
//~ delete(this.formData.lehrveranstaltung_id);
//~ // convert null value fields from string to null
//~ return this.formData;
//~ }
}
},
props: {
statusNew: Boolean,
student: Object,
projektarbeit: Object,
stg_kz: Number
projektarbeit: Object
},
data() {
return {
@@ -177,21 +164,13 @@ export default {
})
.catch(this.$fhcAlert.handleSystemError);
},
setFormData(arrTypen, arrLvs, arrNoten, projektarbeitData) {
this.arrTypen = arrTypen;
this.arrLvs = arrLvs;
this.arrNoten = arrNoten;
if (projektarbeitData) {
projektarbeitData.firma = {firma_id: projektarbeitData.firma_id, name: projektarbeitData.firma_name};
this.formData = projektarbeitData;
}
},
loadProjektarbeit(projektarbeit_id) {
return this.$api
.call(ApiStvProjektarbeit.loadProjektarbeit(projektarbeit_id))
.then(result => {
this.formData = result.data;
if (this.formData.firma_id) this.formData.firma = {firma_id: this.formData.firma_id, name: this.formData.firma_name};
return result;
})
.catch(this.$fhcAlert.handleSystemError)
@@ -7,6 +7,7 @@ import PvAutoComplete from "../../../../../../../index.ci.php/public/js/componen
import ApiStvProjektarbeit from '../../../../../api/factory/stv/projektarbeit.js';
import ProjektarbeitDetails from "./Details.js";
import Projektbetreuer from "./Projektbetreuer.js";
export default {
components: {
@@ -15,7 +16,8 @@ export default {
FormForm,
FormInput,
PvAutoComplete,
ProjektarbeitDetails
ProjektarbeitDetails,
Projektbetreuer
},
inject: {
cisRoot: {
@@ -174,7 +176,9 @@ export default {
button.title = this.$p.t('ui', 'bearbeiten');
button.addEventListener('click', (event) => {
let data = cell.getData();
this.actionEditProjektarbeit(data.projektarbeit_id, data.studiensemester_kurzbz, data.lehrveranstaltung_id);
this.actionEditProjektarbeit(
data.projektarbeit_id, data.studiensemester_kurzbz, data.lehrveranstaltung_id, data.projekttyp_kurzbz
);
});
container.append(button);
@@ -195,7 +199,6 @@ export default {
},
],
layout: 'fitDataFill',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: '200',
selectable: 1,
@@ -240,25 +243,20 @@ export default {
lehrveranstaltung_id: null
}
},
//~ watch: {
//~ student(){
//~ if (this.$refs.table) {
//~ this.$refs.table.reloadTable();
//~ }
//~ this.getStudiengangByKz();
//~ }
//~ },
methods: {
actionNewProjektarbeit() {
this.statusNew = true;
this.$refs.projektarbeitDetails.resetForm();
this.$refs.projektarbeitDetails.getFormData();
this.$refs.projektbetreuer.getData();
this.$refs.projektarbeitModal.show();
},
actionEditProjektarbeit(projektarbeit_id, studiensemester_kurzbz, lehrveranstaltung_id) {
actionEditProjektarbeit(projektarbeit_id, studiensemester_kurzbz, lehrveranstaltung_id, projekttyp_kurzbz) {
this.statusNew = false;
this.$refs.projektarbeitDetails.getFormData(this.statusNew, studiensemester_kurzbz, lehrveranstaltung_id);
// TODO: maybe preload projektarbeit? not just on edit?
this.$refs.projektarbeitDetails.loadProjektarbeit(projektarbeit_id);
this.$refs.projektbetreuer.getData(projektarbeit_id, studiensemester_kurzbz, projekttyp_kurzbz);
this.$refs.projektarbeitModal.show();
},
actionDeleteProjektarbeit(projektarbeit_id) {
@@ -271,41 +269,41 @@ export default {
.catch(this.$fhcAlert.handleSystemError);
},
addNewProjektarbeit() {
Promise.allSettled([
this.$refs.projektarbeitDetails.addNewProjektarbeit()
]).then((results) => {
let hasError = false;
results.forEach((promise_result) => {
this.$refs.projektbetreuer.validateProjektbetreuer()
.then(() => {
return this.$refs.projektarbeitDetails.addNewProjektarbeit();
})
.then((result) => {
const projektarbeit_id = result.data;
console.log(projektarbeit_id);
if (!(promise_result.status === 'fulfilled' && promise_result.value.meta.status === "success")) {
hasError = true;
if (!isNaN(projektarbeit_id)) {
return this.$refs.projektbetreuer.saveProjektbetreuer(projektarbeit_id);
}
});
if (!hasError) {
})
.then((result) => {
console.log(result);
this.projektarbeitSaved();
}
});
})
.catch(this.$fhcAlert.handleSystemError);
},
updateProjektarbeit() {
Promise.allSettled(
[
this.$refs.projektarbeitDetails.updateProjektarbeit()
]).then((results) => {
let hasError = false;
results.forEach((promise_result) => {
this.$refs.projektbetreuer.validateProjektbetreuer()
.then(() => {
return this.$refs.projektarbeitDetails.updateProjektarbeit();
})
.then((result) => {
const projektarbeit_id = result.data;
console.log(projektarbeit_id);
if (!(promise_result.status === 'fulfilled' && promise_result.value.meta.status === "success")) {
hasError = true;
if (!isNaN(projektarbeit_id)) {
return this.$refs.projektbetreuer.saveProjektbetreuer(projektarbeit_id);
}
});
if (!hasError) {
})
.then((result) => {
this.projektarbeitSaved();
}
});
})
.catch(this.$fhcAlert.handleSystemError);
},
deleteProjektarbeit(projektarbeit_id) {
return this.$api
@@ -325,7 +323,6 @@ export default {
this.$refs.projektarbeitDetails.resetForm();
},
rowSelectionChanged(data) {
console.log("selection changed");
this.lastSelected = data.length > 0 ? data[0] : null;
},
hideModal(modalRef){
@@ -362,7 +359,14 @@ export default {
<p v-else class="fw-bold mt-3">{{$p.t('projektarbeit', 'projektarbeitBearbeiten')}}</p>
</template>
<projektarbeit-details ref="projektarbeitDetails" :student="student" :stg_kz="stg_kz"></projektarbeit-details>
<div class="row">
<div class="col-6">
<projektarbeit-details ref="projektarbeitDetails" :student="student"></projektarbeit-details>
</div>
<div class="col-6">
<projektbetreuer ref="projektbetreuer" :config="config"></projektbetreuer>
</div>
</div>
<template #footer>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{$p.t('ui', 'abbrechen')}}</button>
@@ -0,0 +1,421 @@
import {CoreFilterCmpt} from "../../../../filter/Filter.js";
import FormForm from '../../../../Form/Form.js';
import FormInput from '../../../../Form/Input.js';
import PvAutoComplete from "../../../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js";
import ApiStvProjektbetreuer from '../../../../../api/factory/stv/projektbetreuer.js';
export default {
components: {
CoreFilterCmpt,
FormForm,
FormInput,
PvAutoComplete
},
inject: {
},
computed: {
},
props: {
config: {
type: Object,
default: {}
}
},
data() {
return {
tabulatorOptions: {
columns: [
{title: "Nachname", field: "nachname"},
{title: "Vorname", field: "vorname"},
{title: "Note", field: "note"},
{title: "Punkte", field: "punkte"},
{title: "Stunden", field: "stunden"},
{title: "Stundensatz", field: "stundensatz", visible: false},
{title: "Art", field: "betreuerart_kurzbz", visible: false},
{title: "Person ID", field: "person_id", visible: false},
{title: "Vertrag ID", field: "vertrag_id", visible: false},
{title: "Projektarbeit ID", field: "projektarbeit_id", visible: false},
{
title: 'Aktionen', field: 'actions',
minWidth: 150, // Ensures Action-buttons will be always fully displayed
formatter: (cell, formatterParams, onRendered) => {
let container = document.createElement('div');
container.className = "d-flex gap-2";
let button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-edit"></i>';
button.title = this.$p.t('ui', 'bearbeiten');
button.addEventListener('click', (event) => {
let data = cell.getData();
this.actionEditProjektbetreuer(data.projektarbeit_id, data.person_id, data.betreuerart_kurzbz);
});
container.append(button);
button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-xmark"></i>';
button.title = this.$p.t('ui', 'loeschen');
button.addEventListener('click', () => {
const data = cell.getData();
this.actionDeleteProjektbetreuer(data.betreuer_id, data.projektarbeit_id, data.person_id, data.betreuerart_kurzbz)
});
container.append(button);
//container.append(cell.getData().actionDiv);
return container;
},
frozen: true
},
],
layout: 'fitDataFill',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: '100',
selectable: true,
selectable: 1,
index: 'betreuer_id',
persistenceID: 'stv-details-projektbetreuer'
},
tabulatorEvents: [
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['global', 'person', 'stv', 'projektarbeit', 'ui']);
}
}
],
formData: {
betreuerart_kurzbz: null,
note: null,
stunden: null,
stundensatz: null
},
initialFormData: null,
defaultFormDataValues: {stunden: null, stundensatz: null},
projektarbeit_id: null,
statusNew: true,
editedBetreuerIdx: -1,
arrBetreuerart: [],
arrNoten: [],
filteredBetreuer: [],
autocompleteSelectedBetreuer: null,
abortController: {
betreuer: null
}
}
},
methods: {
actionNewProjektbetreuer() {
this.resetForm();
this.statusNew = true;
this.captureFormData();
},
actionEditProjektbetreuer(projektarbeit_id, person_id, betreuerart_kurzbz) {
this.$api
.call(ApiStvProjektbetreuer.getDefaultStundensaetze(person_id, this.studiensemester_kurzbz))
.then(result => {
this.resetForm();
this.statusNew = false;
let projektbetreuerListe = this.$refs.table.tabulator.getData();
const idx = projektbetreuerListe.findIndex(
betr =>
betr.person_id === person_id &&
betr.projektarbeit_id === projektarbeit_id &&
betr.betreuerart_kurzbz === betreuerart_kurzbz
);
let betreuer = [];
if (idx >= 0) {
betreuer = projektbetreuerListe[idx];
this.formData = betreuer;
this.autocompleteSelectedBetreuer = {
person_id: this.formData.person_id,
name: this.formData.name,
vorname: this.formData.vorname,
nachname: this.formData.nachname
};
}
if (this.formData.stundensatz == null) this.formData.stundensatz = result.data;
this.captureFormData();
})
.catch(this.$fhcAlert.handleSystemError);
},
actionDeleteProjektbetreuer(betreuer_id, projektarbeit_id, person_id, betreuerart_kurzbz) {
this.$fhcAlert
.confirmDelete()
.then(result => result
? {projektarbeit_id, person_id, betreuerart_kurzbz}
: Promise.reject({handled: true}))
.then(result => {
return this.deleteProjektbetreuer(projektarbeit_id, person_id, betreuerart_kurzbz)
})
.then(result => {
this.$refs.table.tabulator.deleteRow(betreuer_id);
})
.catch(this.$fhcAlert.handleSystemError);
},
getData(projektarbeit_id, studiensemester_kurzbz, projekttyp_kurzbz) {
console.log(projekttyp_kurzbz);
this.studiensemester_kurzbz = studiensemester_kurzbz;
this.defaultFormDataValues.stunden = '0.0';
if (projekttyp_kurzbz == 'Bachelor') this.defaultFormDataValues.stunden = this.config.defaultProjektbetreuerStunden;
if (projekttyp_kurzbz == 'Diplom') this.defaultFormDataValues.stunden = this.config.defaultProjektbetreuerStundenDiplom;
this.defaultFormDataValues.stundensatz = this.config.defaultProjektbetreuerStundensatz;
this.$api
.call(ApiStvProjektbetreuer.getBetreuerarten())
.then(result => {
this.arrBetreuerart = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
this.$api
.call(ApiStvProjektbetreuer.getNoten())
.then(result => {
this.arrNoten = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
if (projektarbeit_id) {
this.projektarbeit_id = projektarbeit_id;
this.$api
.call(ApiStvProjektbetreuer.getProjektbetreuer(projektarbeit_id))
.then(result => {
this.$refs.table.tabulator.setData(this.addIds(result.data));
this.resetForm();
})
.catch(this.$fhcAlert.handleSystemError);
} else {
this.$refs.table.tabulator.setData([]);
this.resetForm();
}
},
confirmProjektbetreuer() {
if (!this.formDataModified()) return;
if (this.statusNew) {
this.formData.betreuer_id = this.getNewBetreuerId();
this.$refs.table.tabulator.addData(this.addAutoCompleteBetreuerToFormData(this.formData));
} else {
this.$refs.table.tabulator.updateData([this.formData]);
this.statusNew = true;
}
},
confirmProjektbetreuerAfterValidation() {
if (!this.formDataModified()) return;
this.validateProjektbetreuer()
.then(result => {
this.confirmProjektbetreuer();
this.resetForm();
})
.catch(this.$fhcAlert.handleSystemError);
},
saveProjektbetreuer(projektarbeit_id) {
this.confirmProjektbetreuer();
return this.$refs.formProjektbetreuer.call(
ApiStvProjektbetreuer.saveProjektbetreuer(projektarbeit_id, this.$refs.table.tabulator.getData())
);
},
deleteProjektbetreuer(projektarbeit_id, person_id, betreuerart_kurzbz) {
return this.$api
.call(ApiStvProjektbetreuer.deleteProjektbetreuer(projektarbeit_id, person_id, betreuerart_kurzbz))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
})
.catch(this.$fhcAlert.handleSystemError)
},
searchBetreuer(event) {
if (this.abortController.betreuer) {
this.abortController.betreuer.abort();
}
this.abortController.betreuer = new AbortController();
return this.$api
.call(ApiStvProjektbetreuer.getProjektbetreuerBySearchQuery(event.query))
.then(result => {
this.filteredBetreuer = result.data;
});
},
validateProjektbetreuer() {
let alleBetreuer = this.$refs.table.tabulator.getData();
if (this.formDataModified()) {
alleBetreuer.push(this.addAutoCompleteBetreuerToFormData(this.formData));
}
return this.$api.call(ApiStvProjektbetreuer.validateProjektbetreuer(alleBetreuer));
},
resetForm() {
this.formData = this.getDefaultFormData();
this.autocompleteSelectedBetreuer = null;
this.initialFormData = null;
},
getDefaultFormData() {
let formData = {betreuerart_kurzbz : null, note: null};
for (const name in this.defaultFormDataValues) {
formData[name] = this.defaultFormDataValues[name];
}
return formData;
},
captureFormData() {
this.initialFormData = JSON.parse(JSON.stringify(this.formData)); // deep copy
},
addIds(betreuerListe) {
for (const idx in betreuerListe) {
let betreuer = betreuerListe[idx];
betreuer.person_id_old = betreuer.person_id;
betreuer.betreuerart_kurzbz_old = betreuer.betreuerart_kurzbz;
betreuer.betreuer_id = parseInt(idx);
}
return betreuerListe;
},
addAutoCompleteBetreuerToFormData() {
let preparedFormData = this.formData;
preparedFormData.projektarbeit_id = this.projektarbeit_id;
if (this.autocompleteSelectedBetreuer) {
preparedFormData.person_id = this.autocompleteSelectedBetreuer.person_id;
preparedFormData.name = this.autocompleteSelectedBetreuer.name;
preparedFormData.vorname = this.autocompleteSelectedBetreuer.vorname;
preparedFormData.nachname = this.autocompleteSelectedBetreuer.nachname;
}
return preparedFormData;
},
getNewBetreuerId() {
let max = 0;
for (const betreuer of this.$refs.table.tabulator.getData()) {
console.log(typeof betreuer.betreuer_id);
if (betreuer.betreuer_id > max) max = betreuer.betreuer_id;
}
return max + 1;
},
formDataModified() {
if (this.autocompleteSelectedBetreuer != null) return true;
console.log(this.formData);
console.log(this.initialFormData);
for (const prop in this.initialFormData) {
if (typeof this.formData[prop] == 'undefined') return true;
if (this.formData[prop] != this.initialFormData[prop]) return true;
}
return false;
},
reload() {
this.$refs.table.reloadTable();
}
},
template: `
<div class="stv-details-projektbetreuer h-100 pb-3">
<legend>{{this.$p.t('stv','betreuer')}}</legend>
<!-- <p v-if="statusNew">[{{$p.t('ui', 'neu')}}]</p> -->
<core-filter-cmpt
ref="table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
:side-menu="false"
new-btn-show
:new-btn-label="this.$p.t('stv', 'betreuer')"
@click:new="actionNewProjektbetreuer"
>
</core-filter-cmpt>
<form-form ref="formProjektbetreuer" @submit.prevent>
<div class="row mb-3">
<form-input
container-class="stv-details-projektarbeit-betreuer"
:label="$p.t('projektarbeit', 'betreuer')"
type="autocomplete"
optionLabel="name"
v-model="autocompleteSelectedBetreuer"
name="betreuer"
:suggestions="filteredBetreuer"
@complete="searchBetreuer"
:min-length="3"
>
</form-input>
</div>
<div class="row mb-3">
<form-input
container-class="col-6 stv-details-projektbetreuer-betreuerart"
:label="$p.t('global', 'betreuer')"
type="select"
v-model="formData.betreuerart_kurzbz"
name="betreuerart_kurzbz"
>
<option
v-for="art in arrBetreuerart"
:key="art.betreuerart_kurzbz"
:value="art.betreuerart_kurzbz"
>
{{art.beschreibung}}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
container-class="col-8 stv-details-projektbetreuer-note"
:label="$p.t('projektarbeit', 'note')"
type="select"
v-model="formData.note"
name="note"
>
<option :value="null"> -- {{$p.t('fehlermonitoring', 'keineAuswahl')}} -- </option>
<option
v-for="note in arrNoten"
:key="note.note"
:value="note.note"
>
{{note.bezeichnung}}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
container-class="stv-details-projektarbeit-stunden"
type="text"
name="stunden"
:label="$p.t('projektarbeit', 'stunden')"
v-model="formData.stunden"
>
</form-input>
</div>
<div class="row mb-3">
<form-input
container-class="stv-details-projektarbeit-stundensatz"
type="text"
name="stundensatz"
:label="$p.t('projektarbeit', 'stundensatz')"
v-model="formData.stundensatz"
>
</form-input>
</div>
</form-form>
<button class="btn btn-primary" @click="confirmProjektbetreuerAfterValidation">{{ $p.t('projektarbeit', 'betreuerBestaetigen') }}</button>
</div>
`
}