Merge branch 'master' into feature-68296/Vue_Router_via_Extensions_erweitern_ma0080

This commit is contained in:
Harald Bamberger
2026-03-10 12:14:35 +01:00
125 changed files with 7889 additions and 2412 deletions
+102 -6
View File
@@ -12,22 +12,22 @@ html {
font-size: .875em;
}
html.fs_xx-small {
font-size: .5em;
font-size: .625em;
}
html.fs_x-small {
font-size: .625em;
font-size: .6875em;
}
html.fs_small {
font-size: .75em;
}
html.fs_normal {
font-size: .875em;
font-size: .8125em;
}
html.fs_big {
font-size: 1em;
font-size: .875em;
}
html.fs_huge {
font-size: 1.125em;
font-size: 1em;
}
#appMenu {
@@ -74,6 +74,12 @@ html.fs_huge {
color: var(--gray-500);
}
/* Aufnahme Termine: background color green*/
.stv-details-admission-table .row-green{
background-color: lightgreen !important;
//color: var(--green-200);
}
/* Dropdown Toolbar Interessent, submenu */
.dropend .dropdown-toggle.d-flex::after {
height: 0;
@@ -116,6 +122,15 @@ html.fs_huge {
position: inherit;
z-index: 1;
}
.sidebar-collapsed #sidebarMenu {
display: none !important;
}
.sidebar-collapsed .container-fluid > .row > main {
flex: 0 0 100%;
max-width: 100%;
}
}
@@ -168,7 +183,7 @@ html.fs_huge {
}
.has-filter .fa-filter {
color: var(--bs-success);
color: var(--bs-danger);
}
.override_filtercmpt_actions_style div.d-flex.align-items-baseline {
align-items: end !important;
@@ -185,3 +200,84 @@ html.fs_huge {
.tiny-90 div.tox.tox-tinymce {
height: 90% !important;
}
/* slim begin */
.stv .form-label {
margin-bottom: .15rem;
font-weight: bold;
}
.stv .form-control,
.stv .form-select,
.stv .input-group-text {
padding-top: .15rem;
padding-bottom: .15rem;
}
:root {
--bs-body-line-height: 1.2;
}
.stv .tabulator-row .tabulator-cell,
.stv .tabulator-header-filter input {
padding-top: 1px !important;
padding-bottom: 1px !important;
}
.stv .tabulator-row {
min-height: 18px;
}
.stv .btn {
--bs-btn-padding-y: 0.25rem;
--bs-btn-line-height: 1.2;
}
.stv .p-button.p-button-icon-only {
padding-top: 0;
padding-bottom: 0;
}
.stv .p-tabview .p-tabview-nav li .p-tabview-nav-link {
padding: 0.25rem .5rem;
}
/*
.stv .p-tabview .p-tabview-panels {
background-color: #e5eff5;
}
.stv .p-tabview .p-tabview-nav li.p-highlight .p-tabview-nav-link {
background: #e5eff5;
border-color: #dee2e6 #dee2e6 #e5eff5 #dee2e6;
}
*/
.stv-details-details-foto img {
max-height: 120px;
}
.stv .tabulator-row .tabulator-frozen,
.stv .tabulator-row .tabulator-cell {
border-bottom: none;
}
/*
.stv .p-treetable .p-treetable-thead > tr > th,
.stv .p-treetable .p-treetable-tbody > tr {
background-color: #e5eff5;
}
.stv .p-treetable .p-treetable-tbody > tr.p-highlight {
background: #007bff;
color: #fff;
}
.stv .p-treetable.p-treetable-hoverable-rows .p-treetable-tbody > tr:not(.p-highlight):hover {
background: #fff;
color: #212529;
}
.tabulator-row.tabulator-row-even .tabulator-cell {
background-color: #e5eff5;
}
*/
/* slim ende */
+20
View File
@@ -0,0 +1,20 @@
@import './components/verticalsplit.css';
html {
font-size: .875em;
}
.vv{
display: flex;
flex-direction: column;
height: 100vh;
}
.vv> header {
flex: 0 0 auto;
}
.vv> div {
flex: 1 1 auto;
}
.vv {
margin-left: 0 !important;
}
+64
View File
@@ -0,0 +1,64 @@
.foto-container:hover .fotoedit {
opacity: 1 !important;
}
.bg-unruly {
background-color: #ad1010 !important;
color: white;
}
/*.fotosperre {
z-index: 1;
font-size: 1rem;
width: 1.25rem;
height: 1.25rem;
}*/
.foto-container .fotoedit {
opacity: 0;
transition: opacity 0.3s;
backdrop-filter: blur(2px);
}
.fotoedit {
background-color: rgba(0, 0, 0, 0.4);
color: white;
border: none;
z-index: 4;
font-size: 1rem;
width: 1.8rem;
height: 1.8rem;
opacity:0;
transition: opacity 0.2s;
top:20%;
}
/* fotoeditMa {
background-color: rgba(0, 0, 0, 0.4);
color: white;
border: none;
z-index: 4;
font-size: 1rem;
opacity:0;
transition: opacity 0.2s;
top:20%;
}*/
.buttonleft {
margin-left: .25rem;
}
.buttonright {
margin-right: .25rem;
}
/*.fotoEditMa {
z-index: 104;
font-size: 1rem;
width: 2.5rem;
height: 2.5rem;
opacity:0;
transition: opacity 0.2s;
top:13%;"
z-index: 104; font-size: 1rem; width: 2.5rem; height: 2.5rem; opacity:0; transition: opacity 0.2s; top:13%;"
}*/
+6
View File
@@ -3578,6 +3578,12 @@
transition: box-shadow 0.15s;
margin-right: 0.5rem;
}
.p-treetable .p-treetable-tbody > tr > td .p-treetable-toggler > svg,
.p-treetable .p-treetable-tbody > tr > td .p-treetable-toggler > svg * {
pointer-events: none;
}
.p-treetable .p-treetable-tbody > tr > td .p-treetable-toggler:enabled:hover {
color: #495057;
border-color: transparent;
+3 -3
View File
@@ -19,19 +19,19 @@ export default {
getHeader(person_id){
return {
method: 'get',
url: 'api/frontend/v1/vertraege/vertraege/getHeader/' + person_id,
url: 'api/frontend/v1/detailheader/detailheader/getHeader/' + person_id,
};
},
getPersonAbteilung(mitarbeiter_uid){
return {
method: 'get',
url: 'api/frontend/v1/vertraege/vertraege/getPersonAbteilung/' + mitarbeiter_uid,
url: 'api/frontend/v1/detailheader/detailheader/getPersonAbteilung/' + mitarbeiter_uid,
};
},
getLeitungOrg(oekurzbz){
return {
method: 'get',
url: 'api/frontend/v1/vertraege/vertraege/getLeitungOrg/' + oekurzbz,
url: 'api/frontend/v1/detailheader/detailheader/getLeitungOrg/' + oekurzbz,
};
},
}
+32
View File
@@ -0,0 +1,32 @@
/**
* 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 {
uploadFoto(person_id, params) {
return {
method: 'post',
url: 'api/frontend/v1/fotoHandling/Foto/uploadFoto/' + person_id,
params
};
},
deleteFoto(person_id){
return {
method: 'post',
url: 'api/frontend/v1/fotoHandling/Foto/deleteFoto/' + person_id
};
}
}
+29
View File
@@ -0,0 +1,29 @@
/**
* 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/>.
*/
import Person from './person.js';
export default {
...Person,
getNotizen(id, type) {
return {
method: 'get',
url: 'api/frontend/v1/notiz/notizAnrechnung/getNotizen/' + encodeURIComponent(id) + '/' + encodeURIComponent(type)
};
},
};
+29
View File
@@ -0,0 +1,29 @@
/**
* 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/>.
*/
import Person from './person.js';
export default {
...Person,
getNotizen(id, type) {
return {
method: 'get',
url: 'api/frontend/v1/notiz/notizBestellung/getNotizen/' + encodeURIComponent(id) + '/' + encodeURIComponent(type)
};
},
};
@@ -26,4 +26,29 @@ export default {
url: 'api/frontend/v1/notiz/notizLehreinheit/getNotizen/' + encodeURIComponent(id) + '/' + encodeURIComponent(type)
};
},
addNewNotiz(lehreinheit_id, params) {
return {
method: 'post',
url: 'api/frontend/v1/notiz/notizLehreinheit/addNewNotiz/' + lehreinheit_id,
params
};
},
updateNotiz(notiz_id, params) {
return {
method: 'post',
url: 'api/frontend/v1/notiz/notizLehreinheit/updateNotiz/' + notiz_id,
params
};
},
deleteNotiz(notiz_id, type_id, id) {
return {
method: 'post',
url: 'api/frontend/v1/notiz/notizLehreinheit/deleteNotiz/',
params: {
notiz_id,
type_id,
id
}
};
},
};
@@ -0,0 +1,29 @@
/**
* 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/>.
*/
import Person from './person.js';
export default {
...Person,
getNotizen(id, type) {
return {
method: 'get',
url: 'api/frontend/v1/notiz/notizMitarbeiter/getNotizen/' + encodeURIComponent(id) + '/' + encodeURIComponent(type)
};
},
};
+29
View File
@@ -0,0 +1,29 @@
/**
* 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/>.
*/
import Person from './person.js';
export default {
...Person,
getNotizen(id, type) {
return {
method: 'get',
url: 'api/frontend/v1/notiz/notizPrestudent/getNotizen/' + encodeURIComponent(id) + '/' + encodeURIComponent(type)
};
},
};
+29
View File
@@ -0,0 +1,29 @@
/**
* 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/>.
*/
import Person from './person.js';
export default {
...Person,
getNotizen(id, type) {
return {
method: 'get',
url: 'api/frontend/v1/notiz/notizProjekt/getNotizen/' + encodeURIComponent(id) + '/' + encodeURIComponent(type)
};
},
};
@@ -0,0 +1,29 @@
/**
* 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/>.
*/
import Person from './person.js';
export default {
...Person,
getNotizen(id, type) {
return {
method: 'get',
url: 'api/frontend/v1/notiz/notizProjektphase/getNotizen/' + encodeURIComponent(id) + '/' + encodeURIComponent(type)
};
},
};
@@ -0,0 +1,29 @@
/**
* 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/>.
*/
import Person from './person.js';
export default {
...Person,
getNotizen(id, type) {
return {
method: 'get',
url: 'api/frontend/v1/notiz/notizProjekttask/getNotizen/' + encodeURIComponent(id) + '/' + encodeURIComponent(type)
};
},
};
+54
View File
@@ -0,0 +1,54 @@
export default {
getTag(data)
{
return {
method: 'get',
url: 'api/frontend/v1/stv/Tags/getTag',
params: data
};
},
getTags(data)
{
return {
method: 'get',
url: 'api/frontend/v1/stv/Tags/getTags'
};
},
addTag(data)
{
return {
method: 'post',
url: 'api/frontend/v1/stv/Tags/addTag',
params: data
};
},
updateTag(data)
{
return {
method: 'post',
url: 'api/frontend/v1/stv/Tags/updateTag',
params: data
};
},
doneTag(data)
{
return {
method: 'post',
url: 'api/frontend/v1/stv/Tags/doneTag',
params: data
};
},
deleteTag(data)
{
return {
method: 'post',
url: 'api/frontend/v1/stv/Tags/deleteTag',
params: data
};
},
};
@@ -0,0 +1,136 @@
/**
* 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 {
getAllVertraege(person_id) {
return {
method: 'get',
url: 'api/frontend/v1/vertraege/Vertraege/getAllVertraege/' + person_id
};
},
getAllContractsNotAssigned(person_id) {
return {
method: 'get',
url: 'api/frontend/v1/vertraege/Vertraege/getAllContractsNotAssigned/' + person_id
};
},
getAllContractsAssigned(person_id, vertrag_id) {
return {
method: 'get',
url: 'api/frontend/v1/vertraege/Vertraege/getAllContractsAssigned/' + person_id + '/' + vertrag_id + ''
};
},
getAllContractTypes() {
return {
method: 'get',
url: 'api/frontend/v1/vertraege/Vertraege/getAllContractTypes/'
};
},
getStatiOfContract(person_id, vertrag_id){
return {
method: 'get',
url: 'api/frontend/v1/vertraege/Vertraege/getStatiOfContract/' + person_id + '/' + vertrag_id
};
},
configPrintDocument() {
return {
method: 'get',
url: 'api/frontend/v1/vertraege/Config/printDocument/'
};
},
getAllContractStati() {
return {
method: 'get',
url: 'api/frontend/v1/vertraege/vertraege/getAllContractStati/'
};
},
deleteContract(vertrag_id) {
return {
method: 'post',
url: 'api/frontend/v1/vertraege/vertraege/deleteContract/' + vertrag_id
};
},
addNewContract(params) {
return {
method: 'post',
url: 'api/frontend/v1/vertraege/vertraege/addNewContract/',
params
};
},
loadContract(vertrag_id){
return {
method: 'get',
url: 'api/frontend/v1/vertraege/vertraege/loadContract/' + vertrag_id
};
},
updateContract(params) {
return {
method: 'post',
url: 'api/frontend/v1/vertraege/vertraege/updateContract/',
params
};
},
loadContractStatus(params){
return {
method: 'get',
url: 'api/frontend/v1/vertraege/vertraege/loadContractStatus/',
params
};
},
insertContractStatus(params) {
return {
method: 'post',
url: 'api/frontend/v1/vertraege/vertraege/insertContractStatus/',
params
};
},
updateContractStatus(params) {
return {
method: 'post',
url: 'api/frontend/v1/vertraege/vertraege/updateContractStatus/',
params
};
},
deleteContractStatus(params) {
return {
method: 'post',
url: 'api/frontend/v1/vertraege/vertraege/deleteContractStatus/',
params
};
},
deleteLehrauftrag(params) {
return {
method: 'post',
url: 'api/frontend/v1/vertraege/vertraege/deleteLehrauftrag/',
params
};
},
deleteBetreuung(params) {
return {
method: 'post',
url: 'api/frontend/v1/vertraege/vertraege/deleteBetreuung/',
params
};
},
//loaded by mitarbeiter_header_js
getMitarbeiter(){
return {
method: 'get',
url: 'api/frontend/v1/vertraege/vertraege/getMitarbeiter/',
};
},
};
+3 -1
View File
@@ -39,6 +39,7 @@ import studiengang from "./studiengang.js";
import menu from "./menu.js";
import dashboard from "./dashboard.js";
import authinfo from "./authinfo.js";
import vertraege from "./vertraege.js";
import studium from "./studium.js";
import language from "./language.js";
@@ -68,6 +69,7 @@ export default {
studiengang,
menu,
authinfo,
vertraege,
studium,
language
language
};
+19 -2
View File
@@ -1,5 +1,22 @@
import person from "./notiz/person.js";
import prestudent from "./notiz/prestudent.js";
import mitarbeiter from "./notiz/mitarbeiter.js";
import projekt from "./notiz/projekt.js";
import anrechnung from "./notiz/anrechnung.js";
import bestellung from "./notiz/bestellung.js";
import lehreinheit from "./notiz/lehreinheit.js";
import projektphase from "./notiz/projektphase.js";
import projekttask from "./notiz/projekttask.js";
export default {
person
}
person,
prestudent,
mitarbeiter,
anrechnung,
bestellung,
lehreinheit,
projekt,
projektphase,
projekttask,
}
+38
View File
@@ -0,0 +1,38 @@
export default {
getNotizen(url, config, params){
return this.$fhcApi.get('api/frontend/v1/notiz/notizAnrechnung/getNotizen/' + params.id + '/' + params.type);
},
getUid(){
return this.$fhcApi.get('api/frontend/v1/notiz/notizAnrechnung/getUid/');
},
addNewNotiz(id, formData) {
return this.$fhcApi.post('api/frontend/v1/notiz/notizAnrechnung/addNewNotiz/' + id,
formData
);
},
loadNotiz(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizAnrechnung/loadNotiz/', {
notiz_id
});
},
loadDokumente(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizAnrechnung/loadDokumente/', {
notiz_id
});
},
deleteNotiz(notiz_id, type_id, id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizAnrechnung/deleteNotiz/', {
notiz_id,
type_id,
id
});
},
updateNotiz(notiz_id, formData){
return this.$fhcApi.post('api/frontend/v1/notiz/notizAnrechnung/updateNotiz/' + notiz_id,
formData
);
},
getMitarbeiter(event){
return this.$fhcApi.get('api/frontend/v1/notiz/notizAnrechnung/getMitarbeiter/' + event);
}
}
+38
View File
@@ -0,0 +1,38 @@
export default {
getNotizen(url, config, params){
return this.$fhcApi.get('api/frontend/v1/notiz/notizBestellung/getNotizen/' + params.id + '/' + params.type);
},
getUid(){
return this.$fhcApi.get('api/frontend/v1/notiz/notizBestellung/getUid/');
},
addNewNotiz(id, formData) {
return this.$fhcApi.post('api/frontend/v1/notiz/notizBestellung/addNewNotiz/' + id,
formData
);
},
loadNotiz(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizBestellung/loadNotiz/', {
notiz_id
});
},
loadDokumente(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizBestellung/loadDokumente/', {
notiz_id
});
},
deleteNotiz(notiz_id, type_id, id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizBestellung/deleteNotiz/', {
notiz_id,
type_id,
id
});
},
updateNotiz(notiz_id, formData){
return this.$fhcApi.post('api/frontend/v1/notiz/notizBestellung/updateNotiz/' + notiz_id,
formData
);
},
getMitarbeiter(event){
return this.$fhcApi.get('api/frontend/v1/notiz/notizBestellung/getMitarbeiter/' + event);
}
}
+38
View File
@@ -0,0 +1,38 @@
export default {
getNotizen(url, config, params){
return this.$fhcApi.get('api/frontend/v1/notiz/notizLehreinheit/getNotizen/' + params.id + '/' + params.type);
},
getUid(){
return this.$fhcApi.get('api/frontend/v1/notiz/notizLehreinheit/getUid/');
},
addNewNotiz(id, formData) {
return this.$fhcApi.post('api/frontend/v1/notiz/notizLehreinheit/addNewNotiz/' + id,
formData
);
},
loadNotiz(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizLehreinheit/loadNotiz/', {
notiz_id
});
},
loadDokumente(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizLehreinheit/loadDokumente/', {
notiz_id
});
},
deleteNotiz(notiz_id, type_id, id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizLehreinheit/deleteNotiz/', {
notiz_id,
type_id,
id
});
},
updateNotiz(notiz_id, formData){
return this.$fhcApi.post('api/frontend/v1/notiz/notizLehreinheit/updateNotiz/' + notiz_id,
formData
);
},
getMitarbeiter(event){
return this.$fhcApi.get('api/frontend/v1/notiz/notizLehreinheit/getMitarbeiter/' + event);
}
}
+38
View File
@@ -0,0 +1,38 @@
export default {
getNotizen(url, config, params){
return this.$fhcApi.get('api/frontend/v1/notiz/notizMitarbeiter/getNotizen/' + params.id + '/' + params.type);
},
getUid(){
return this.$fhcApi.get('api/frontend/v1/notiz/notizMitarbeiter/getUid/');
},
addNewNotiz(id, formData) {
return this.$fhcApi.post('api/frontend/v1/notiz/notizMitarbeiter/addNewNotiz/' + id,
formData
);
},
loadNotiz(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizMitarbeiter/loadNotiz/', {
notiz_id
});
},
loadDokumente(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizMitarbeiter/loadDokumente/', {
notiz_id
});
},
deleteNotiz(notiz_id, type_id, id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizMitarbeiter/deleteNotiz/', {
notiz_id,
type_id,
id
});
},
updateNotiz(notiz_id, formData){
return this.$fhcApi.post('api/frontend/v1/notiz/notizMitarbeiter/updateNotiz/' + notiz_id,
formData
);
},
getMitarbeiter(event){
return this.$fhcApi.get('api/frontend/v1/notiz/notizMitarbeiter/getMitarbeiter/' + event);
}
}
+1 -1
View File
@@ -38,4 +38,4 @@ export default {
isBerechtigt(id, type_id){
return this.$fhcApi.get('api/frontend/v1/notiz/notizPerson/isBerechtigt/');
}
}
}
+38
View File
@@ -0,0 +1,38 @@
export default {
getNotizen(url, config, params){
return this.$fhcApi.get('api/frontend/v1/notiz/notizPrestudent/getNotizen/' + params.id + '/' + params.type);
},
getUid(){
return this.$fhcApi.get('api/frontend/v1/notiz/notizPrestudent/getUid/');
},
addNewNotiz(id, formData) {
return this.$fhcApi.post('api/frontend/v1/notiz/notizPrestudent/addNewNotiz/' + id,
formData
);
},
loadNotiz(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizPrestudent/loadNotiz/', {
notiz_id
});
},
loadDokumente(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizPrestudent/loadDokumente/', {
notiz_id
});
},
deleteNotiz(notiz_id, type_id, id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizPrestudent/deleteNotiz/', {
notiz_id,
type_id,
id
});
},
updateNotiz(notiz_id, formData){
return this.$fhcApi.post('api/frontend/v1/notiz/notizPrestudent/updateNotiz/' + notiz_id,
formData
);
},
getMitarbeiter(event){
return this.$fhcApi.get('api/frontend/v1/notiz/notizPrestudent/getMitarbeiter/' + event);
}
}
+38
View File
@@ -0,0 +1,38 @@
export default {
getNotizen(url, config, params){
return this.$fhcApi.get('api/frontend/v1/notiz/NotizProjekt/getNotizen/' + params.id + '/' + params.type);
},
getUid(){
return this.$fhcApi.get('api/frontend/v1/notiz/NotizProjekt/getUid/');
},
addNewNotiz(id, formData) {
return this.$fhcApi.post('api/frontend/v1/notiz/NotizProjekt/addNewNotiz/' + id,
formData
);
},
loadNotiz(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/NotizProjekt/loadNotiz/', {
notiz_id
});
},
loadDokumente(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/NotizProjekt/loadDokumente/', {
notiz_id
});
},
deleteNotiz(notiz_id, type_id, id){
return this.$fhcApi.post('api/frontend/v1/notiz/NotizProjekt/deleteNotiz/', {
notiz_id,
type_id,
id
});
},
updateNotiz(notiz_id, formData){
return this.$fhcApi.post('api/frontend/v1/notiz/NotizProjekt/updateNotiz/' + notiz_id,
formData
);
},
getMitarbeiter(event){
return this.$fhcApi.get('api/frontend/v1/notiz/NotizProjekt/getMitarbeiter/' + event);
}
}
+38
View File
@@ -0,0 +1,38 @@
export default {
getNotizen(url, config, params){
return this.$fhcApi.get('api/frontend/v1/notiz/notizProjektphase/getNotizen/' + params.id + '/' + params.type);
},
getUid(){
return this.$fhcApi.get('api/frontend/v1/notiz/notizProjektphase/getUid/');
},
addNewNotiz(id, formData) {
return this.$fhcApi.post('api/frontend/v1/notiz/notizProjektphase/addNewNotiz/' + id,
formData
);
},
loadNotiz(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizProjektphase/loadNotiz/', {
notiz_id
});
},
loadDokumente(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizProjektphase/loadDokumente/', {
notiz_id
});
},
deleteNotiz(notiz_id, type_id, id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizProjektphase/deleteNotiz/', {
notiz_id,
type_id,
id
});
},
updateNotiz(notiz_id, formData){
return this.$fhcApi.post('api/frontend/v1/notiz/notizProjektphase/updateNotiz/' + notiz_id,
formData
);
},
getMitarbeiter(event){
return this.$fhcApi.get('api/frontend/v1/notiz/notizProjektphase/getMitarbeiter/' + event);
}
}
+38
View File
@@ -0,0 +1,38 @@
export default {
getNotizen(url, config, params){
return this.$fhcApi.get('api/frontend/v1/notiz/notizProjekttask/getNotizen/' + params.id + '/' + params.type);
},
getUid(){
return this.$fhcApi.get('api/frontend/v1/notiz/notizProjekttask/getUid/');
},
addNewNotiz(id, formData) {
return this.$fhcApi.post('api/frontend/v1/notiz/notizProjekttask/addNewNotiz/' + id,
formData
);
},
loadNotiz(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizProjekttask/loadNotiz/', {
notiz_id
});
},
loadDokumente(notiz_id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizProjekttask/loadDokumente/', {
notiz_id
});
},
deleteNotiz(notiz_id, type_id, id){
return this.$fhcApi.post('api/frontend/v1/notiz/notizProjekttask/deleteNotiz/', {
notiz_id,
type_id,
id
});
},
updateNotiz(notiz_id, formData){
return this.$fhcApi.post('api/frontend/v1/notiz/notizProjekttask/updateNotiz/' + notiz_id,
formData
);
},
getMitarbeiter(event){
return this.$fhcApi.get('api/frontend/v1/notiz/notizProjekttask/getMitarbeiter/' + event);
}
}
+8
View File
@@ -0,0 +1,8 @@
import person from "./vertraege/person.js";
export default {
person,
configPrintDocument() {
return this.$fhcApi.get('api/frontend/v1/vertraege/config/printDocument');
}
}
+69
View File
@@ -0,0 +1,69 @@
export default {
getAllVertraege(url, config, params){
return this.$fhcApi.get('api/frontend/v1/vertraege/vertraege/getAllVertraege/' + params.person_id);
},
getAllContractsNotAssigned(url, config, params){
return this.$fhcApi.get('api/frontend/v1/vertraege/vertraege/getAllContractsNotAssigned/' + params.person_id);
},
getAllContractsAssigned(url, config, params){
return this.$fhcApi.get('api/frontend/v1/vertraege/vertraege/getAllContractsAssigned/' + params.person_id + '/' + params.vertrag_id);
},
getAllContractsNotAssigned2(person_id){
return this.$fhcApi.get('api/frontend/v1/vertraege/vertraege/getAllContractsNotAssigned/' + person_id);
},
getStatiOfContract(url, config, params){
return this.$fhcApi.get('api/frontend/v1/vertraege/vertraege/getStatiOfContract/' + params.vertrag_id);
},
getAllContractTypes(){
return this.$fhcApi.get('api/frontend/v1/vertraege/vertraege/getAllContractTypes/');
},
getAllContractStati(){
return this.$fhcApi.get('api/frontend/v1/vertraege/vertraege/getAllContractStati/');
},
addNewContract(form, data) {
return this.$fhcApi.post(form,'api/frontend/v1/vertraege/vertraege/addNewContract/', data);
},
loadContract(vertrag_id){
return this.$fhcApi.post('api/frontend/v1/vertraege/vertraege/loadContract/' + vertrag_id);
},
updateContract(form, data) {
return this.$fhcApi.post(form,'api/frontend/v1/vertraege/vertraege/updateContract/', data);
},
deleteContract(vertrag_id){
return this.$fhcApi.post('api/frontend/v1/vertraege/vertraege/deleteContract/' + vertrag_id);
},
loadContractStatus(params){
return this.$fhcApi.post('api/frontend/v1/vertraege/vertraege/loadContractStatus/' + params.vertrag_id, params);
},
insertContractStatus(form, params) {
return this.$fhcApi.post(form,'api/frontend/v1/vertraege/vertraege/insertContractStatus/' + params.vertrag_id, params);
},
updateContractStatus(form, params) {
return this.$fhcApi.post(form,'api/frontend/v1/vertraege/vertraege/updateContractStatus/' + params.vertrag_id, params);
},
deleteContractStatus(params) {
return this.$fhcApi.post('api/frontend/v1/vertraege/vertraege/deleteContractStatus/' + params.vertrag_id, params);
},
deleteLehrauftrag(params) {
return this.$fhcApi.post('api/frontend/v1/vertraege/vertraege/deleteLehrauftrag/' + params.vertrag_id, params);
},
deleteBetreuung(params) {
return this.$fhcApi.post('api/frontend/v1/vertraege/vertraege/deleteBetreuung/' + params.vertrag_id, params);
},
getMitarbeiter(params){
return this.$fhcApi.post('api/frontend/v1/vertraege/vertraege/getMitarbeiter/');
},
getHeader(person_id){
return this.$fhcApi.post('api/frontend/v1/vertraege/vertraege/getHeader/' + person_id);
},
getPersonAbteilung(person_id){
return this.$fhcApi.post('api/frontend/v1/vertraege/vertraege/getPersonAbteilung/' + person_id);
},
getLeitungOrg(oekurzbz){
return this.$fhcApi.post('api/frontend/v1/vertraege/vertraege/getLeitungOrg/' + oekurzbz);
},
getMitarbeiterUid(person_id){
return this.$fhcApi.get('api/frontend/v1/vertraege/vertraege/getMitarbeiterUid/' + person_id);
},
}
+25
View File
@@ -0,0 +1,25 @@
import Vertragsverwaltung from "../components/Vertraege/Vertragsverwaltung.js";
import Phrasen from "../plugins/Phrasen.js";
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(),
routes: [
{ path: `/${ciPath}/vertragsverwaltung`, component: Vertragsverwaltung },
]
});
const app = Vue.createApp({
name: 'VertragsverwaltungApp'
});
app
.use(router)
.use(primevue.config.default, {
zIndex: {
overlay: 1100
}
})
.use(Phrasen)
.mount('#main');
@@ -43,12 +43,28 @@ export default {
},
data() {
return {
tabulatorOptions: {
listBetriebsmitteltyp: [],
formData: {
ausgegebenam : new Date(),
betriebsmitteltyp: 'Zutrittskarte'
},
statusNew: true,
filteredInventar: [],
layout: 'fitColumns',
layoutColumnsOnNewData: false,
height: '550',
}
},
computed: {
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
this.endpoint.getAllBetriebsmittel(this.typeId, this.id, (this.filterByProvidedTypes ? this.betriebsmittelTypes : null))
),
ajaxResponse: (url, params, response) => response.data,
persistenceID: 'core-betriebsmittel-20260217',
selectableRows: true,
columns: [
{title: "Nummer", field: "nummer", width: 150},
{title: "PersonId", field: "person_id", visible: false},
@@ -115,7 +131,7 @@ export default {
'/content/pdfExport.php?xml=betriebsmittelperson.rdf.php&xsl=Uebernahme&id=' + cellData.betriebsmittelperson_id + '&output=pdf';
window.open(linkToPdf, '_blank');
});
});
container.append(button);
button = document.createElement('button');
@@ -143,64 +159,47 @@ export default {
return container;
},
frozen: true
}],
layout: 'fitColumns',
layoutColumnsOnNewData: false,
height: '550',
persistenceID: 'core-betriebsmittel'
},
tabulatorEvents: [
}
],
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['wawi', 'global', 'infocenter', 'betriebsmittel', 'person']);
let cm = this.$refs.table.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('nummer').component.updateDefinition({
title: this.$p.t('wawi', 'nummer')
});
cm.getColumnByField('betriebsmitteltyp').component.updateDefinition({
title: this.$p.t('global', 'typ')
});
cm.getColumnByField('anmerkung').component.updateDefinition({
title: this.$p.t('global', 'anmerkung')
});
cm.getColumnByField('retouram').component.updateDefinition({
title: this.$p.t('wawi', 'retourdatum')
});
cm.getColumnByField('beschreibung').component.updateDefinition({
title: this.$p.t('global', 'beschreibung')
});
cm.getColumnByField('kaution').component.updateDefinition({
title: this.$p.t('infocenter', 'kaution')
});
cm.getColumnByField('ausgegebenam').component.updateDefinition({
title: this.$p.t('wawi', 'ausgabedatum')
});
cm.getColumnByField('betriebsmittel_id').component.updateDefinition({
title: this.$p.t('ui', 'betriebsmittel_id')
});
cm.getColumnByField('betriebsmittelperson_id').component.updateDefinition({
title: this.$p.t('ui', 'betriebsmittelperson_id')
});
cm.getColumnByField('person_id').component.updateDefinition({
title: this.$p.t('person', 'person_id')
});
cm.getColumnByField('uid').component.updateDefinition({
title: this.$p.t('person', 'uid')
});
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('nummer', this.$p.t('wawi', 'nummer'));
setHeader('betriebsmitteltyp', this.$p.t('global', 'typ'));
setHeader('anmerkung', this.$p.t('global', 'anmerkung'));
setHeader('retouram', this.$p.t('wawi', 'retourdatum'));
setHeader('beschreibung', this.$p.t('global', 'beschreibung'));
setHeader('kaution', this.$p.t('infocenter', 'kaution'));
setHeader('ausgegebenam', this.$p.t('wawi', 'ausgabedatum'));
setHeader('betriebsmittel_id', this.$p.t('ui', 'betriebsmittel_id'));
setHeader('betriebsmittelperson_id', this.$p.t('ui', 'betriebsmittelperson_id'));
setHeader('person_id', this.$p.t('person', 'person_id'));
setHeader('uid', this.$p.t('person', 'uid'));
}
}
],
listBetriebsmitteltyp: [],
formData: {
ausgegebenam : new Date(),
betriebsmitteltyp: 'Zutrittskarte'
},
statusNew: true,
filteredInventar: []
];
return events;
}
},
watch: {
@@ -466,5 +465,4 @@ export default {
</template>
</bs-modal>
</div>`
}
}
@@ -17,6 +17,7 @@ export const AbgabeMitarbeiterDetail = {
Message: primevue.message,
VueDatePicker
},
emits: ['paUpdated'],
inject: [
'abgabeTypeOptions',
'abgabetypenBetreuer',
@@ -132,8 +133,8 @@ export const AbgabeMitarbeiterDetail = {
const noteOptExisting = this.allowedNotenOptions.find(opt => opt.note == existingTerminRes.note)
existingTerminRes.note = noteOptExisting
const existingTerminResCurrObj = this.projektarbeit.abgabetermine.find(paa => paa.paabgabe_id == existingTerminRes.paabgabe_id)
existingTerminResCurrObj.noteBackend = noteOpt // do NOT take noteOptExisting -> should reflect the "yes the qgate grade is confirmed in backend ux behaviour"
termin.paabgabetyp_kurzbz = newTerminRes.paabgabetyp_kurzbz
termin.noteBackend = noteOpt // do NOT take noteOptExisting -> should reflect the "yes the qgate grade is confirmed in backend ux behaviour"
termin.dateStyle = getDateStyleClass(termin, this.notenOptions)
}
@@ -174,6 +175,8 @@ export const AbgabeMitarbeiterDetail = {
} else {
this.showAutomagicModalPhrase = false
}
this.$emit("paUpdated", this.projektarbeit)
} else if(res?.meta?.status == 'error'){
this.$fhcAlert.alertError()
}
@@ -257,6 +260,7 @@ export const AbgabeMitarbeiterDetail = {
// this.$p.t('global/tooltipLektorDeleteKontrolle', [this.$entryParams.permissions.kontrolleDeleteMaxReach ])
const deletedTerminIndex = this.projektarbeit.abgabetermine.findIndex(t => t.paabgabe_id === termin.paabgabe_id)
this.projektarbeit.abgabetermine.splice(deletedTerminIndex, 1)
this.$emit("paUpdated", this.projektarbeit)
} else if(res?.meta?.status == 'error'){
this.$fhcAlert.alertError()
}
@@ -210,12 +210,12 @@ export const AbgabetoolAssistenz = {
headerFilter: dateFilter,
headerFilterFunc: this.headerFilterTerminCol,
sorter: this.sortFuncTerminCol,
field: 'prevTermin', formatter: this.abgabterminFormatter, widthGrow: 1, width: 220, tooltip: false},
field: 'prevTermin', formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nextAbgabetermin'))), field: 'nextTermin',
headerFilter: dateFilter,
headerFilterFunc: this.headerFilterTerminCol,
sorter: this.sortFuncTerminCol,
formatter: this.abgabterminFormatter, widthGrow: 1, width: 220, tooltip: false},
formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate1Status'))),
headerFilter: 'list',
headerFilterParams: { valuesLookup: this.getQGateStatusList },
@@ -226,7 +226,7 @@ export const AbgabetoolAssistenz = {
field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false},
],
persistence: false,
persistenceID: "abgabetool_2026_02"
persistenceID: "abgabetool_2026_02_26"
},
abgabeTableEventHandlers: [
{
@@ -247,6 +247,10 @@ export const AbgabetoolAssistenz = {
]};
},
methods: {
handlePaUpdated(projektarbeit) {
this.checkAbgabetermineProjektarbeit(projektarbeit)
this.$refs.abgabeTable.tabulator.redraw(true)
},
getQGateStatusList() {
return [
this.$p.t('abgabetool/c4keinTerminVorhanden'),
@@ -309,11 +313,13 @@ export const AbgabetoolAssistenz = {
return false
},
sammelMailStudent(param) {
const emails = this.selectedData
.map(row => `${row.student_uid}@${this.domain}`)
.join(',');
const uniqueRecipients = [...new Set(emails)];
const recipientList = [];
this.selectedData.forEach(d => {
recipientList.push(`${d.student_uid}@${this.domain}`)
})
const uniqueRecipients = [...new Set(recipientList)];
const subject = this.$p.t('abgabetool/c4sammelmailStudentBetreff', [this.selectedStudiengangOption?.bezeichnung]);
splitMailsHelper(uniqueRecipients, param.originalEvent, subject, this.$fhcAlert, this.$p)
},
@@ -362,7 +368,6 @@ export const AbgabetoolAssistenz = {
return false;
},
checkQualityGateStatus(projekt) {
// TODO: might refine the representation of these states and maybe refactor code a little
const qgate1Termine = []
const qgate2Termine = []
@@ -382,7 +387,7 @@ export const AbgabetoolAssistenz = {
// reuse luxon calculated diffMs (termin.datum in relation to today) from previous datestyle check
qgate1Termine.forEach(qgate => {
if(qgate.note != null && projekt.qgate1StatusRank <= 5) {
const noteOpt = this.notenOptions.find(opt => opt.note == qgate.note)
const noteOpt = typeof qgate.note !== 'object' ? this.notenOptions.find(opt => opt.note == qgate.note) : qgate.note
if(noteOpt.positiv) {
projekt.qgate1Status = this.$p.t('abgabetool/c4positivBenotet')
projekt.qgate1StatusRank = 5
@@ -404,7 +409,7 @@ export const AbgabetoolAssistenz = {
qgate2Termine.forEach(qgate => {
if(qgate.note != null && projekt.qgate1StatusRank <= 5) {
const noteOpt = this.notenOptions.find(opt => opt.note == qgate.note)
const noteOpt = typeof qgate.note !== 'object' ? this.notenOptions.find(opt => opt.note == qgate.note) : qgate.note
if(noteOpt.positiv) {
projekt.qgate2Status = this.$p.t('abgabetool/c4positivBenotet')
projekt.qgate2StatusRank = 5
@@ -1287,7 +1292,13 @@ export const AbgabetoolAssistenz = {
</div>
</template>
<template v-slot:default>
<AbgabeDetail :projektarbeit="selectedProjektarbeit" :isFullscreen="detailIsFullscreen" :assistenzMode="true"></AbgabeDetail>
<AbgabeDetail
:projektarbeit="selectedProjektarbeit"
:isFullscreen="detailIsFullscreen"
:assistenzMode="true"
@paUpdated="handlePaUpdated">
</AbgabeDetail>
</template>
</bs-modal>
@@ -6,6 +6,7 @@ import ApiAbgabe from '../../../api/factory/abgabe.js'
import FhcOverlay from "../../Overlay/FhcOverlay.js";
import { getDateStyleClass } from "./getDateStyleClass.js";
import { dateFilter } from '../../../tabulator/filters/Dates.js';
import {splitMailsHelper} from "../../../helpers/EmailHelpers.js";
export const AbgabetoolMitarbeiter = {
name: "AbgabetoolMitarbeiter",
@@ -16,6 +17,7 @@ export const AbgabetoolMitarbeiter = {
Checkbox: primevue.checkbox,
Dropdown: primevue.dropdown,
Textarea: primevue.textarea,
TieredMenu: primevue.tieredmenu,
VueDatePicker,
FhcOverlay
},
@@ -48,6 +50,7 @@ export const AbgabetoolMitarbeiter = {
phrasenResolved: false,
turnitin_link: null,
old_abgabe_beurteilung_link: null,
BETREUER_SAMMELMAIL_BUTTON_STUDENT: null,
saving: false,
loading: false,
abgabeTypeOptions: null,
@@ -139,7 +142,6 @@ export const AbgabetoolMitarbeiter = {
},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', formatter: this.detailFormatter, headerFilter: false, headerSort: false, widthGrow: 1, tooltip: false, cssClass: 'sticky-col'},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4personenkennzeichen'))), headerFilter: true, field: 'pkz', formatter: this.pkzTextFormatter, widthGrow: 1, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4kontakt'))), field: 'mail', formatter: this.mailFormatter, widthGrow: 1, tooltip: false, visible: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'vorname', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4projekttyp'))), field: 'projekttyp_kurzbz', formatter: this.centeredTextFormatter, widthGrow: 1},
@@ -151,12 +153,12 @@ export const AbgabetoolMitarbeiter = {
headerFilter: dateFilter,
headerFilterFunc: this.headerFilterTerminCol,
sorter: this.sortFuncTerminCol,
formatter: this.abgabterminFormatter, widthGrow: 1, width: 220, tooltip: false},
formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nextAbgabetermin'))), field: 'nextTermin',
headerFilter: dateFilter,
headerFilterFunc: this.headerFilterTerminCol,
sorter: this.sortFuncTerminCol,
formatter: this.abgabterminFormatter, widthGrow: 1, width: 220, tooltip: false},
formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate1Status'))),
headerFilter: 'list',
headerFilterParams: { valuesLookup: this.getQGateStatusList },
@@ -167,7 +169,7 @@ export const AbgabetoolMitarbeiter = {
field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false}
],
persistence: false,
persistenceID: 'abgabeTableBetreuer2026-02-24'
persistenceID: 'abgabeTableBetreuer2026-02-26'
},
abgabeTableEventHandlers: [{
event: "tableBuilt",
@@ -203,6 +205,20 @@ export const AbgabetoolMitarbeiter = {
]};
},
methods: {
handlePaUpdated(projektarbeit) {
this.checkAbgabetermineProjektarbeit(projektarbeit)
this.$refs.abgabeTable.tabulator.redraw(true)
},
sammelMailStudent(param) {
const recipientList = [];
this.selectedData.forEach(d => {
recipientList.push(`${d.student_uid}@${this.domain}`)
})
const uniqueRecipients = [...new Set(recipientList)];
const subject = ""; // empty subject line
splitMailsHelper(uniqueRecipients, param.originalEvent, subject, this.$fhcAlert, this.$p)
},
getQGateStatusList() {
return [
this.$p.t('abgabetool/c4keinTerminVorhanden'),
@@ -375,7 +391,6 @@ export const AbgabetoolMitarbeiter = {
});
},
checkQualityGateStatus(projekt) {
// TODO: might refine the representation of these states and maybe refactor code a little
const qgate1Termine = []
const qgate2Termine = []
@@ -395,7 +410,7 @@ export const AbgabetoolMitarbeiter = {
// reuse luxon calculated diffMs (termin.datum in relation to today) from previous datestyle check
qgate1Termine.forEach(qgate => {
if(qgate.note != null && projekt.qgate1StatusRank <= 5) {
const noteOpt = this.notenOptions.find(opt => opt.note == qgate.note)
const noteOpt = typeof qgate.note !== 'object' ? this.notenOptions.find(opt => opt.note == qgate.note) : qgate.note
if(noteOpt.positiv) {
projekt.qgate1Status = this.$p.t('abgabetool/c4positivBenotet')
projekt.qgate1StatusRank = 5
@@ -417,7 +432,7 @@ export const AbgabetoolMitarbeiter = {
qgate2Termine.forEach(qgate => {
if(qgate.note != null && projekt.qgate1StatusRank <= 5) {
const noteOpt = this.notenOptions.find(opt => opt.note == qgate.note)
const noteOpt = typeof qgate.note !== 'object' ? this.notenOptions.find(opt => opt.note == qgate.note) : qgate.note
if(noteOpt.positiv) {
projekt.qgate2Status = this.$p.t('abgabetool/c4positivBenotet')
projekt.qgate2StatusRank = 5
@@ -677,12 +692,13 @@ export const AbgabetoolMitarbeiter = {
}
pa.abgabetermine.forEach(termin => {
termin.note = this.allowedNotenOptions.find(opt => opt.note == termin.note)
const noteOpt = this.allowedNotenOptions.find(opt => opt.note == termin.note)
if(noteOpt) termin.note = noteOpt
termin.file = []
// only set this if it has not been set yet and abgabetermin has a note (qgate)
if(!termin.noteBackend && termin.note) {
termin.noteBackend = termin.note
if(!termin.noteBackend && noteOpt) {
termin.noteBackend = noteOpt
}
// update 08-01-2026: everybody is allowed to do everything in client, critical checks happen at backend level
@@ -719,11 +735,6 @@ export const AbgabetoolMitarbeiter = {
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
'<a><i class="fa fa-folder-open" style="color:#00649C"></i></a></div>'
},
mailFormatter(cell) {
const val = cell.getValue()
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
'<a href='+val+'><i class="fa fa-envelope" style="color:#00649C"></i></a></div>'
},
beurteilungFormatter(cell) {
const val = cell.getValue()
if(val) {
@@ -828,6 +839,29 @@ export const AbgabetoolMitarbeiter = {
},
},
computed: {
emailItems() {
const menu = []
if(this.BETREUER_SAMMELMAIL_BUTTON_STUDENT){
menu.push({
label: this.$p.t('abgabetool/c4sendEmailStudierendev2', [this.uniqueStudentEmailCount]),
command: this.sammelMailStudent
})
}
return menu
},
uniqueStudentEmailCount() {
const emails = new Set();
this.selectedData.forEach(row => {
if (row.student_uid) {
emails.add(row.student_uid); // actually dont need domain for this
}
});
return emails.size;
},
getAllowedAbgabeTypeOptions() {
return this.abgabeTypeOptions.filter(opt => this.abgabetypenBetreuer.includes(opt.paabgabetyp_kurzbz))
}
@@ -840,6 +874,7 @@ export const AbgabetoolMitarbeiter = {
this.turnitin_link = res.data?.turnitin_link
this.old_abgabe_beurteilung_link = res.data?.old_abgabe_beurteilung_link
this.abgabetypenBetreuer = res.data?.abgabetypenBetreuer
this.BETREUER_SAMMELMAIL_BUTTON_STUDENT = res.data?.BETREUER_SAMMELMAIL_BUTTON_STUDENT
}).catch(e => {
this.loading = false
})
@@ -952,7 +987,11 @@ export const AbgabetoolMitarbeiter = {
</div>
</template>
<template v-slot:default>
<AbgabeDetail :projektarbeit="selectedProjektarbeit" :isFullscreen="detailIsFullscreen"></AbgabeDetail>
<AbgabeDetail
:projektarbeit="selectedProjektarbeit"
:isFullscreen="detailIsFullscreen"
@paUpdated="handlePaUpdated">
</AbgabeDetail>
</template>
</bs-modal>
@@ -988,7 +1027,17 @@ export const AbgabetoolMitarbeiter = {
<i class="fa fa-hourglass-end"></i>
{{ $p.t('abgabetool/showDeadlines') }}
</button>
<button
v-if="emailItems.length"
role="button"
@click="evt => $refs.menu.toggle(evt)"
class="btn btn-outline-secondary dropdown-toggle"
aria-haspopup="true"
>
<i class="fa fa-envelope"></i>
</button>
<tiered-menu ref="menu" :model="emailItems" popup :autoZIndex="false" />
</template>
</core-filter-cmpt>
+243 -78
View File
@@ -1,12 +1,11 @@
import ApiDetailHeader from "../../api/factory/detailHeader.js";
import ApiHandleFoto from "../../api/factory/fotoHandling.js";
import ModalUploadFoto from "./Modal/UploadFoto.js";
export default {
name: 'DetailHeader',
inject: {
domain: {
from: 'configDomain',
default: 'technikum-wien.at'
},
components: {
ModalUploadFoto
},
props: {
headerData: {
@@ -17,6 +16,19 @@ export default {
type: Number,
required: false
},
mitarbeiter_uid: {
type: String,
required: false
},
fotoEditable: {
type: Boolean,
required: false,
default: false
},
domain: {
type: String,
required: false
},
typeHeader: {
type: String,
default: 'student',
@@ -36,31 +48,40 @@ export default {
if (this.typeHeader === 'student') return this.headerData;
if (this.typeHeader === 'mitarbeiter') return this.person_id;
return null;
}
},
hasTileAlphaSlot() {
return !!this.$slots.titleAlphaTile
},
hasTileBetaSlot() {
return !!this.$slots.titleBetaTile
},
hasTileGammaSlot() {
return !!this.$slots.titleGammaTile
},
hasTileUIDSlot() {
return !!this.$slots.uid
},
},
created(){
if(this.person_id) {
this.getHeader(this.person_id);
this.loadDepartmentData(this.mitarbeiter_uid)
.then(() => {
// Call getLeitungOrg only after departmentData is loaded
this.getLeitungOrg(this.departmentData.oe_kurzbz);
})
.catch((error) => {
console.error("Error loading department data:", error);
});
if (this.typeHeader === 'student') {
if (!this.headerData) {
throw new Error('[DetailHeader] "headerData" is required.')
}
} else if (this.typeHeader === 'mitarbeiter') {
if (!this.person_id || !this.mitarbeiter_uid || !this.domain) {
throw new Error(
'[DetailHeader] "person_id", "mitarbeiter_uid", and "domain" are requried.'
)
}
this.loadHeaderData(this.person_id, this.mitarbeiter_uid);
}
},
watch: {
person_id: {
handler(newVal) {
if (newVal) {
this.getHeader(this.person_id);
this.loadDepartmentData(this.person_id).
then(() => {
this.getLeitungOrg(this.departmentData.oe_kurzbz);
});
this.loadHeaderData(newVal, this.mitarbeiter_uid);
}
},
deep: true,
@@ -71,9 +92,21 @@ export default {
headerDataMa: {},
departmentData: {},
leitungData: {},
isFetchingIssues: false
};
},
methods: {
loadHeaderData(person_id, mitarbeiter_uid){
this.getHeader(person_id);
this.loadDepartmentData(mitarbeiter_uid)
.then(() => {
// Call getLeitungOrg only after departmentData is loaded
this.getLeitungOrg(this.departmentData.oe_kurzbz);
})
.catch((error) => {
console.error("Error loading header data: ", error);
});
},
getHeader(person_id) {
return this.$api
.call(ApiDetailHeader.getHeader(person_id))
@@ -99,9 +132,46 @@ export default {
})
.catch(this.$fhcAlert.handleSystemError);
},
redirectToLeitung(){
async goToLeitung() {
this.loadHeaderData(this.leitungData.person_id, this.leitungData.uid);
this.redirectToLeitung();
},
redirectToLeitung() {
this.$emit('redirectToLeitung', {
person_id: this.leitungData.person_id});
person_id: this.leitungData.person_id,
uid: this.leitungData.uid
});
},
showModal(person_id){
this.$refs.modalFoto.open(person_id);
},
showDeleteModal(person_id){
this.$fhcAlert
.confirmDelete()
.then(result => result
? person_id
: Promise.reject({handled: true}))
.then(this.deleteFoto)
.catch(this.$fhcAlert.handleSystemError);
},
deleteFoto(person_id){
return this.$api
.call(ApiHandleFoto.deleteFoto(person_id))
.then(result => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
})
.catch(this.$fhcAlert.handleSystemError)
.finally(()=> {
this.reload();
});
},
reload() {
if(this.person_id) {
this.loadHeaderData(this.person_id, this.mitarbeiter_uid);
}
else {
this.$emit('reload');
}
},
getFotoSrc(foto) {
if(foto === null) {
@@ -112,74 +182,127 @@ export default {
}
},
template: `
<div class="core-header d-flex justify-content-start align-items-center w-100 overflow-auto pb-3 gap-3" style="max-height:9rem; min-width: 37.5rem;">
<div class="core-header d-flex justify-content-start align-items-center w-100 overflow-auto pb-2 gap-3" style="max-height:9rem; min-width: 37.5rem;">
<modal-upload-foto
v-if="person_id"
ref="modalFoto"
:person_id="person_id"
@reload="reload"
>
</modal-upload-foto>
<modal-upload-foto
v-else
ref="modalFoto"
:person_id="headerData[0].person_id"
@reload="reload"
>
</modal-upload-foto>
<template v-if="typeHeader==='student'">
<div
v-for="person in headerData"
:key="person.person_id"
class="d-flex flex-column align-items-center h-100 position-relative d-inline-block"
>
<img
class="d-block rounded"
style="height: 84px;"
alt="Profilbild"
:src="getFotoSrc(person.foto)"
/>
<template v-if="person.foto_sperre">
<i
class=" fa fa-lock text-secondary bg-light rounded d-flex justify-content-center align-items-center position-absolute top-0 end-0"
style="z-index: 1; font-size: 1rem; width: 1.25rem; height: 1.25rem;"
></i>
</template>
<div
v-for="person in headerData"
:key="person.person_id"
class="foto-container d-flex flex-column align-items-center h-100 position-relative d-inline-block"
>
<img
class="d-block rounded"
style="height: 75px;"
alt="Profilbild"
:src="getFotoSrc(person.foto)"
/>
<template v-if="person.foto_sperre">
<i class="fa fa-lock text-secondary bg-light rounded d-flex justify-content-center align-items-center position-absolute top-0 end-0"
style="z-index: 1; font-size: 1rem; width: 1.25rem; height: 1.25rem;"
>
</i>
</template>
<template v-if="fotoEditable">
<button
type="button"
class="fotoedit buttonleft btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute start-0"
@click="showDeleteModal(headerData[0].person_id)">
<i class="fa fa-xmark"></i>
</button>
<button
type="button"
class="fotoedit buttonright btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute end-0"
@click="showModal(headerData[0].person_id)">
<i class="fa fa-pen"></i>
</button>
</template>
<small class="text-muted">{{person.uid}}</small>
</div>
</div>
<div v-if="headerData.length == 1">
<h2 class="h4">
{{headerData[0].titelpre}}
{{headerData[0].vorname}}
{{headerData[0].nachname}}<span v-if="headerData[0].titelpost">, </span>
{{headerData[0].titelpost}}
</h2>
<div class="d-flex align-items-center gap-3">
<h2 class="h4">
{{headerData[0].titelpre}}
{{headerData[0].vorname}}
{{headerData[0].nachname}}
<span v-if="headerData[0].titelpost">, </span>
{{headerData[0].titelpost}}
</h2>
<h6 v-if="headerData[0].unruly" class="badge" :class="'bg-unruly rounded-0'"><strong>unruly</strong></h6>
</div>
<h5 class="h6">
<strong class="text-muted">Person ID </strong>
{{headerData[0].person_id}}
<strong class="text-muted">| {{$p.t('lehre', 'studiengang')}} </strong>
{{headerData[0].stg_bezeichnung}} ({{headerData[0].studiengang}})
<strong v-if="headerData[0].semester" class="text-muted"> | {{$p.t('lehre', 'semester')}} </strong>
{{headerData[0].semester}}
<strong v-if="headerData[0].verband" class="text-muted"> | {{$p.t('lehre', 'verband')}}</strong>
{{headerData[0].verband}}
<strong v-if="headerData[0].gruppe" class="text-muted"> | {{$p.t('lehre', 'gruppe')}} </strong>
{{headerData[0].gruppe}}
</h5>
<h5 class="h6">
<strong class="text-muted">{{$p.t('lehre', 'studiengang')}} </strong>
{{headerData[0].stg_bezeichnung}} ({{headerData[0].studiengang}})
<strong v-if="headerData[0].semester" class="text-muted"> | {{$p.t('lehre', 'semester')}} </strong>
{{headerData[0].semester}}
<strong v-if="headerData[0].verband" class="text-muted"> | {{$p.t('lehre', 'verband')}}</strong>
{{headerData[0].verband}}
<strong v-if="headerData[0].gruppe" class="text-muted"> | {{$p.t('lehre', 'gruppe')}} </strong>
{{headerData[0].gruppe}}
</h5>
<h5 class="h6">
<h5 class="h6">
<strong class="text-muted">Email </strong>
<span>
<a :href="'mailto:'+headerData[0]?.mail_intern">{{headerData[0].mail_intern}}</a>
</span>
<strong v-if="headerData[0].statusofsemester" class="text-muted"> | Status </strong>
{{headerData[0].statusofsemester}}
<strong class="text-muted"> | {{$p.t('person', 'matrikelnummer')}} </strong>
{{headerData[0].matr_nr}}
</h5>
</div>
<div v-if="headerData.length == 1" class="col-md-1 d-flex flex-column align-items-end justify-content-start ms-auto">
<div class="d-flex py-1">
<div class="px-2" style="min-width: 100px;">
<slot name="issues"></slot>
</div>
<div v-if="hasTileGammaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleGammaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueGammaTile"></slot></h6>
</div>
<div v-if="hasTileBetaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleBetaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueBetaTile"></slot></h6>
</div>
<div v-if="hasTileAlphaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleAlphaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueAlphaTile"></slot></h6>
</div>
<div v-if="hasTileUIDSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center">UID</h4>
<h6 class="text-muted text-center"><slot name="uid"></slot></h6>
</div>
</div>
</div>
</template>
<template v-if="typeHeader==='mitarbeiter'">
<div class="row">
<div class="col-md-2 d-flex justify-content-start align-items-center w-30 pb-3 gap-3 position-relative"
style="max-height: 8rem; max-width: 6rem; overflow: hidden;">
<div class="foto-container col-md-2 d-flex justify-content-start align-items-center w-30 pb-3 gap-3 mt-3 position-relative" style="max-height: 8rem; max-width: 6rem; overflow: hidden;">
<img
class="d-block rounded"
style="height: 84px;"
class="d-block w-100 h-100 rounded"
style="height: 84px; object-fit: contain;"
alt="Profilbild"
:src="'data:image/jpeg;base64,' + headerDataMa.foto"
:src="getFotoSrc(headerDataMa.foto)"
/>
<template v-if="headerDataMa.foto_sperre">
<i
@@ -187,31 +310,73 @@ export default {
style="z-index: 1; font-size: 1rem; width: 1.25rem; height: 1.25rem;"
></i>
</template>
<template v-if="fotoEditable">
<button
type="button"
class="fotoedit btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute start-0"
style="z-index: 104; font-size: 1rem; width: 2.5rem; height: 2.5rem; opacity:0; transition: opacity 0.2s; top:13%;"
@click="showDeleteModal(person_id)">
<i class="fa fa-xmark"></i>
</button>
<button
type="button"
class="fotoedit btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute end-0"
style="z-index: 104; font-size: 1rem; width: 2.5rem; height: 2.5rem; opacity:0; transition: opacity 0.2s; top:13%;"
@click="showModal(person_id)">
<i class="fa fa-pen"></i>
</button>
</template>
</div>
<!--show Ma-Details-->
<div class="col-md-10">
<h5>{{headerDataMa.titelpre}} {{headerDataMa.vorname}} {{headerDataMa.nachname}}<span v-if="headerDataMa?.titelpost">, </span> {{headerDataMa.titelpost}}
</h5>
<div class="col-md-9 text-nowrap mt-2">
<h4>{{headerDataMa.titelpre}} {{headerDataMa.vorname}} {{headerDataMa.nachname}}<span v-if="headerDataMa?.titelpost">, </span> {{headerDataMa.titelpost}}</h4>
<strong class="text-muted">{{departmentData.organisationseinheittyp_kurzbz}}</strong>
{{departmentData.bezeichnung}}
<span v-if="leitungData.uid"> | </span>
<strong v-if="leitungData.uid" class="text-muted">Vorgesetzte*r </strong>
<a href="#" @click.prevent="redirectToLeitung">
<a href="#" @click.prevent="goToLeitung">
{{leitungData.titelpre}} {{leitungData.vorname}} {{leitungData.nachname}}
</a>
<p>
<strong class="text-muted">Email </strong>
<span v-if="!headerDataMa?.alias">
<a :href="'mailto:'+headerDataMa?.uid+'@'+domain">{{headerDataMa.uid}}@{{domain}}</a>
<span v-if="headerDataMa && (headerDataMa.alias === undefined || headerDataMa.alias === null || headerDataMa.alias === '')">
<a :href="'mailto:' + mitarbeiter_uid + '@' + domain">
{{ mitarbeiter_uid }}@{{ domain }}
</a>
</span>
<span v-if="headerDataMa?.alias">
<span v-else>
<a :href="'mailto:'+headerDataMa?.alias+'@'+domain">{{headerDataMa.alias}}@{{domain}}</a>
</span>
<span v-if="headerDataMa?.telefonklappe" class="mb-2"> | <strong class="text-muted">DW </strong>{{headerDataMa?.telefonklappe}}</span>
</p>
<slot name="tag"></slot>
</div>
</div>
<div class="col-md-1 d-flex flex-column align-items-end justify-content-start ms-auto">
<div class="d-flex py-1">
<div class="px-2" style="min-width: 100px;">
<slot name="issues"></slot>
</div>
<div v-if="hasTileGammaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleGammaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueGammaTile"></slot></h6>
</div>
<div v-if="hasTileBetaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleBetaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueBetaTile" :valueBetaTile="valueBetaTile"></slot></h6>
</div>
<div v-if="hasTileAlphaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleAlphaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueAlphaTile"></slot></h6>
</div>
<div v-if="hasTileUIDSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center">UID</h4>
<h6 class="text-muted text-center"><slot name="uid"></slot></h6>
</div>
</div>
</div>
</template>
</div>
`
@@ -0,0 +1,103 @@
import BsModal from "../../Bootstrap/Modal.js";
import FormForm from "../../Form/Form.js";
import FormInput from "../../Form/Input.js";
import ApiHandleFoto from "../../../../js/api/factory/fotoHandling.js";
export default {
name: "modalUploadFoto",
components: {
BsModal,
FormForm,
FormInput,
},
props: {
person_id: {
type: Number,
required: true
}
},
data(){
return{
base64Image: null,
file: null,
preview: null,
}
},
methods:{
open(person_id){
this.$refs.modalUploadFoto.show(person_id);
},
onFileChange(e) {
this.file = e.target.files[0];
if (!this.file) return;
// convert File in base64
const reader = new FileReader();
reader.onload = (event) => {
this.base64Image = event.target.result;
this.preview = this.base64Image;
};
reader.readAsDataURL(this.file);
},
async uploadImage(person_id) {
if (!this.base64Image) {
this.$fhcAlert.alertInfo(this.$p.t('header', 'alert_chooseFoto'));
return;
}
return this.$api
.call(ApiHandleFoto.uploadFoto(person_id, {
image: this.base64Image,
filename: this.file.name
}))
.then(result => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successFotoUpload'));
this.closeModal();
this.$emit('reload');
})
.catch(this.$fhcAlert.handleSystemError);
},
resetModal(){
this.base64Image = [];
this.file = null;
},
closeModal(){
this.resetModal();
this.$refs.modalUploadFoto.hide();
}
},
template: `
<bs-modal
ref="modalUploadFoto"
dialog-class="modal-dialog-scrollable"
>
<template #title>
Upload Foto
</template>
<form-form
ref="formUploadFoto"
>
<div>
<input class="form-control" type="file" @change="onFileChange" accept="image/*" />
<div class="mt-3">
<div>
<img :src="base64Image" style="width:100px"/>
</div>
</div>
</div>
</form-form>
<template #footer>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
<button class="btn btn-secondary" @click="closeModal()">{{$p.t('ui', 'cancel')}}</button>
<button type="button" class="btn btn-primary" @click="uploadImage(person_id)">{{$p.t('ui', 'hochladen')}}</button>
</div>
</template>
</bs-modal>
`,
}
+76 -77
View File
@@ -33,26 +33,71 @@ export default {
},
data(){
return {
tabulatorOptions: {
isFilterSet: true,
listOrgHeads: [],
listOrgUnits: [], //Old
listAllOrgUnits: [],
listOrgUnits_GST: [],
listOrgUnits_GMBH: [],
formData: {
head: 'gst',
oe_kurzbz: '',
funktion_kurzbz: null,
label:'',
//funktion_label: '',
funktion: null,
},
statusNew: true,
listAllFunctions: [],
abortController: {
oes: null,
functions: null
},
filteredOes: [],
filteredFunctions: [],
newBtnStyle: '',
selectedFunction: null,
selectedOe: null,
layout: 'fitDataFill',
layoutColumnsOnNewData: false,
height: '300'
}
},
computed: {
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
ApiCoreFunktion.getAllUserFunctions(this.personUID)
),
ajaxResponse: (url, params, response) => response.data,
persistenceID: 'core-functions-20260217',
columns: [
{
title: "dienstverhaeltnis_unternehmen",
field: "dienstverhaeltnis_unternehmen",
headerFilter: "list",
headerFilterParams: {valuesLookup: true, autocomplete: true, sort: "asc"},
headerFilterParams: {
valuesLookup: true, autocomplete: true, sort: "asc"
},
width: 140,
//Field Company: if visible show link to dv
visible: this.showDvCompany,
formatter: this.companyLinkFormatter
},
{
title: "funktion_beschreibung", field: "funktion_beschreibung", headerFilter: "list",
headerFilterParams: {valuesLookup: true, autocomplete: true, sort: "asc"},
headerFilterParams: {
valuesLookup: true, autocomplete: true, sort: "asc"
},
width: 140
},
{
title: "funktion_oebezeichnung", field: "funktion_oebezeichnung", headerFilter: "list",
headerFilterParams: {valuesLookup: true, autocomplete: true, sort: "asc"}
headerFilterParams: {
valuesLookup: true, autocomplete: true, sort: "asc"
},
width: 140
},
{title: "wochenstunden", field: "wochenstunden", headerFilter: true},
{
@@ -87,7 +132,7 @@ export default {
});
},
},
{title: "bezeichnung", field: "bezeichnung", headerFilter: true},
{title: "bezeichnung", field: "bezeichnung", headerFilter: true, width: 140},
{title: "aktiv", field: "aktiv", visible: false},
{title: "benutzerfunktion_id", field: "benutzerfunktion_id", visible: false},
{title: "uid", field: "uid", visible: false},
@@ -142,87 +187,41 @@ export default {
frozen: true
}
],
layout: 'fitDataFill',
layoutColumnsOnNewData: false,
height: '300',
persistenceID: 'core-functions',
},
tabulatorEvents: [
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async () => {
await this.$p.loadCategory(['global', 'lehre', 'person', 'ui']);
let cm = this.$refs.table.tabulator.columnManager;
//Field Company: if visible show link to dv
const column = cm.getColumnByField('dienstverhaeltnis_unternehmen');
const companyDv = {
title: this.$p.t('person', 'dv_unternehmen'),
width: 140,
visible: this.showDvCompany,
formatter: this.companyLinkFormatter
};
column.component.updateDefinition(companyDv);
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('funktion_beschreibung').component.updateDefinition({
title: this.$p.t('person', 'zuordnung_taetigkeit'),
width: 140
});
cm.getColumnByField('funktion_oebezeichnung').component.updateDefinition({
title: this.$p.t('lehre', 'organisationseinheit'),
width: 140
});
cm.getColumnByField('wochenstunden').component.updateDefinition({
title: this.$p.t('person', 'wochenstunden')
});
const el = col.getElement();
if (!el || !el.querySelector) return;
const columnDatumVon = cm.getColumnByField('datum_von');
const fieldVonDatum = {
title: this.$p.t('ui', 'from')
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
columnDatumVon.component.updateDefinition(fieldVonDatum);
const columnDatumBis = cm.getColumnByField('datum_bis');
const fieldBisDatum = {
title: this.$p.t('global', 'bis'),
};
columnDatumBis.component.updateDefinition(fieldBisDatum);
cm.getColumnByField('bezeichnung').component.updateDefinition({
title: this.$p.t('ui', 'bezeichnung'),
width: 140
});
setHeader('dienstverhaeltnis_unternehmen', this.$p.t('person', 'dv_unternehmen'));
setHeader('funktion_beschreibung', this.$p.t('person', 'zuordnung_taetigkeit'));
setHeader('funktion_oebezeichnung', this.$p.t('lehre', 'organisationseinheit'));
setHeader('wochenstunden', this.$p.t('person', 'wochenstunden'));
setHeader('datum_von', this.$p.t('ui', 'from'));
setHeader('datum_bis', this.$p.t('global', 'bis'));
setHeader('bezeichnung', this.$p.t('ui', 'bezeichnung'));
}
}
],
isFilterSet: true,
listOrgHeads: [],
listOrgUnits: [], //Old
listAllOrgUnits: [],
listOrgUnits_GST: [],
listOrgUnits_GMBH: [],
formData: {
head: 'gst',
oe_kurzbz: '',
funktion_kurzbz: null,
label:'',
//funktion_label: '',
funktion: null,
},
statusNew: true,
listAllFunctions: [],
abortController: {
oes: null,
functions: null
},
filteredOes: [],
filteredFunctions: [],
newBtnStyle: '',
selectedFunction: null,
selectedOe: null
}
];
return events;
},
},
watch: {
selectedFunction(newVal) {
@@ -340,6 +339,8 @@ export default {
this.formData.head = 'gst';
this.formData.oe_kurzbz = '';
this.formData.funktion_kurzbz = '';
this.selectedFunction = null;
this.selectedOe= null;
},
filterFunctions(event) {
const query = event.query.toLowerCase();
@@ -460,14 +461,12 @@ export default {
</template>
<form-form class="row pt-3" ref="functionData">
<form-input
container-class="mb-3 col-8"
type="select"
name="companies"
:label="$p.t('core/unternehmen')"
v-model="formData.head"
@change="getOrgetsForCompanyOld"
>
<option
v-for="org in listOrgHeads"
+10 -161
View File
@@ -4,6 +4,8 @@ import DetailsForm from "../Details/Form.js";
import CoreTag from '../../Tag/Tag.js';
import { tagHeaderFilter } from '../../../../js/tabulator/filters/extendedHeaderFilter.js';
import { extendedHeaderFilter } from "../../../../js/tabulator/filters/extendedHeaderFilter.js";
import { tagFormatter } from "../../../../js/tabulator/formatter/tags.js";
import { addTagInTable, deleteTagInTable, updateTagInTable } from "../../../../js/helpers/TagHelper.js";
import ApiLv from "../../../api/lehrveranstaltung.js";
import ApiTag from "../../../api/lehrveranstaltung/tag.js";
@@ -223,72 +225,7 @@ export default {
headerFilter: "input",
headerFilterFunc: tagHeaderFilter,
headerFilterFuncParams: {field: 'tags'},
formatter: (cell) => {
let tags = cell.getValue();
if (!tags) return;
let container = document.createElement('div');
container.className = "d-flex gap-1";
let parsedTags = JSON.parse(tags);
let maxVisibleTags = 2;
const rowData = cell.getRow().getData();
if (rowData._tagExpanded === undefined) {
rowData._tagExpanded = false;
}
const renderTags = () => {
container.innerHTML = '';
parsedTags = parsedTags.filter(item => item !== null);
parsedTags.sort((a, b) => {
let adone = a.done ? 1 : 0;
let bbone = b.done ? 1 : 0;
if (adone !== bbone)
{
return adone - bbone;
}
return b.id - a.id;
});
const tagsToShow = rowData._tagExpanded ? parsedTags : parsedTags.slice(0, maxVisibleTags);
tagsToShow.forEach(tag => {
if (!tag) return;
let tagElement = document.createElement('span');
tagElement.innerText = tag.beschreibung;
tagElement.title = tag.notiz;
tagElement.className = "tag " + tag.style;
if (tag.done) tagElement.className += " tag_done";
tagElement.addEventListener('click', (event) => {
event.stopPropagation();
event.preventDefault();
this.$refs.tagComponent.editTag(tag.id);
});
container.appendChild(tagElement);
});
if (parsedTags.length > maxVisibleTags) {
let toggle = document.createElement('button');
toggle.innerText = (rowData._tagExpanded ? '- ' : '+ ') + (parsedTags.length - maxVisibleTags);
toggle.className = "display_all";
toggle.title = rowData._tagExpanded ? "Tags ausblenden" : "Tags einblenden";
toggle.addEventListener('click', () => {
rowData._tagExpanded = !rowData._tagExpanded;
renderTags();
});
container.appendChild(toggle);
}
};
renderTags();
return container;
},
formatter: (cell) => tagFormatter(cell, this.$refs.tagComponent),
width: 150,
},
{
@@ -444,104 +381,16 @@ export default {
},
addedTag(addedTag)
{
const table = this.$refs.table.tabulator;
this.selectedRows.forEach(row =>
{
if (Array.isArray(addedTag.response))
{
addedTag.response.forEach(tag => {
const targetRow = this.allRows.find(row => row.getData().lehreinheit_id === tag.lehreinheit_id);
if (targetRow)
{
const rowData = targetRow.getData();
let tags = [];
try {
tags = JSON.parse(rowData.tags || '[]');
} catch (e) {}
const tagExists = tags.some((t) => t.id === tag.id);
if (!tagExists)
{
addedTag.id = tag.id;
tags.unshift({ ...addedTag });
targetRow.update({ tags: JSON.stringify(tags) });
targetRow.reformat();
}
}
});
}
});
addTagInTable(addedTag, this.allRows, 'lehreinheit_id')
},
deletedTag(deletedTag) {
const targetRow = this.allRows.find(row => {
const rowData = row.getData();
let tags = [];
try {
tags = JSON.parse(rowData.tags || '[]');
} catch (e) {}
return tags.some(tag => tag.id === deletedTag);
});
if (targetRow) {
const rowData = targetRow.getData();
let tags = [];
try {
tags = JSON.parse(rowData.tags || '[]');
} catch (e) {}
const filteredTags = tags.filter(t => t.id !== deletedTag);
const updatedTags = JSON.stringify(filteredTags);
if (updatedTags !== rowData.tags) {
targetRow.update({
tags: updatedTags
});
targetRow.reformat();
}
}
deletedTag(deletedTag)
{
deleteTagInTable(deletedTag, this.allRows);
},
updatedTag(updatedTag) {
const targetRow = this.allRows.find(row => {
const rowData = row.getData();
let tags = [];
try {
tags = JSON.parse(rowData.tags || '[]');
} catch (e) {}
return tags.some(t => t?.id === updatedTag.id);
});
if (targetRow)
{
const rowData = targetRow.getData();
let tags = [];
try {
tags = JSON.parse(rowData.tags || '[]');
} catch (e) {}
let changed = false;
const tagIndex = tags.findIndex(tag => tag?.id === updatedTag.id);
if (tagIndex !== -1) {
tags[tagIndex] = { ...updatedTag };
changed = true;
}
if (changed)
{
targetRow.update({
tags: JSON.stringify(tags),
});
targetRow.reformat();
}
}
updatedTag(updatedTag)
{
updateTagInTable(updatedTag, this.allRows);
},
async copyLehreinheit(row, art)
{
@@ -47,6 +47,7 @@ export default {
<fhc-tabs
v-if="lv.length === 1"
ref="tabs"
:useprimevue="true"
:modelValue="lv[0]"
:config="configLVTabs"
:default="$route.params.tab"
@@ -25,247 +25,11 @@ export default {
},
data(){
return {
tabulatorOptions: {
ajaxURL: 'dummy',
ajaxRequestFunc: this.loadAjaxCall,
ajaxParams: () => {
return {
id: this.id,
type: this.typeId
};
},
ajaxResponse: (url, params, response) => this.buildTreemap(response),
columns: [
{title: "subject", field: "subject", headerFilter: true},
{title: "body", field: "body", formatter: "html", visible: false, headerFilter: true},
{title: "message_id", field: "message_id", visible: false, headerFilter: true},
{
title: "Datum",
field: "insertamum",
headerFilter: true,
formatter: function (cell) {
const dateStr = cell.getValue();
const date = new Date(dateStr); // Convert to Date object
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: false
});
},
headerFilterFunc(headerValue, rowValue) {
const matches = headerValue.match(/^(([0-9]{2})\.)?([0-9]{2})\.([0-9]{4})?$/);
let comparestr = headerValue;
if(matches !== null) {
const year = (matches[4] !== undefined) ? matches[4] : '';
const month = matches[3];
const day = (matches[2] !== undefined) ? matches[2] : '';
comparestr = year + '-' + month + '-' + day;
}
return rowValue.match(comparestr);
}
},
{title: "sender", field: "sender", headerFilter: true},
{title: "recipient", field: "recipient", headerFilter: true},
{title: "senderId", field: "sender_id", headerFilter: true},
{title: "recipientId", field: "recipient_id", headerFilter: true},
{title: "Relationmessage ID", field: "relationmessage_id", headerFilter: true},
{
title: "Status",
field: "status",
headerFilter: true,
formatterParams: [
"unread",
"read",
"archived",
"deleted"
],
formatter: (cell, formatterParams) => {
return formatterParams[cell.getValue()];
},
},
{
title: "letzte Änderung",
field: "statusdatum",
headerFilter: true,
formatter: function (cell) {
const dateStr = cell.getValue();
const date = new Date(dateStr); // Convert to Date object
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: false
});
},
headerFilterFunc(headerValue, rowValue) {
const matches = headerValue.match(/^(([0-9]{2})\.)?([0-9]{2})\.([0-9]{4})?$/);
let comparestr = headerValue;
if(matches !== null) {
const year = (matches[4] !== undefined) ? matches[4] : '';
const month = matches[3];
const day = (matches[2] !== undefined) ? matches[2] : '';
comparestr = year + '-' + month + '-' + day;
}
return rowValue.match(comparestr);
}
},
{
title: 'Aktionen', field: 'actions',
width: 100,
formatter: (cell, formatterParams, onRendered) => {
let container = document.createElement('div');
container.className = "d-flex gap-2";
let button = document.createElement('button');
if (this.personId != cell.getData().sender_id) {
button.disabled = true;
button.style = "visibility: hidden";
button.ariaHidden = true;
}
button.className = 'btn btn-outline-secondary btn-action';
button.title = this.$p.t('global', 'reply');
button.innerHTML = '<i class="fa fa-reply"></i>';
button.addEventListener(
'click',
(event) =>
this.actionReplyToMessage(cell.getData().message_id)
);
container.append(button);
button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.title = this.$p.t('ui', 'loeschen');
button.innerHTML = '<i class="fa fa-xmark"></i>';
button.addEventListener(
'click',
() =>
this.actionDeleteMessage(cell.getData().message_id)
);
container.append(button);
return container;
},
frozen: true
}
],
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: '400',
selectable: 1,
selectableRangeMode: 'click',
index: 'message_id',
pagination: true,
paginationMode: "remote",
paginationSize: 15,
paginationInitialPage: 1,
dataTree: true,
headerSort: true,
dataTreeChildField: "children",
dataTreeCollapseElement:"<i class='fas fa-minus-square'></i>",
dataTreeChildIndent: 15,
dataTreeStartExpanded: false,
persistenceID: 'core-message-2025112401',
locale: 'de',
"langs": {
"de":{ //German language definition
"data":{
"loading":"Lädt", //data loader text
"error":"Fehler", //data error text
},
"pagination":{
"first":"Erste",
"first_title":"Erste Seite",
"last":"Letzte",
"last_title":"Letzte Seite",
"prev":"Vorige",
"prev_title":"Vorige Seite",
"next":"Nächste",
"next_title":"Nächste Seite",
"all":"Alle"
},
},
}
},
tabulatorEvents: [
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['global', 'person', 'stv', 'messages', 'ui', 'notiz']);
let cm = this.$refs.table.tabulator.columnManager;
cm.getColumnByField('subject').component.updateDefinition({
title: this.$p.t('global', 'betreff')
});
cm.getColumnByField('body').component.updateDefinition({
title: this.$p.t('messages', 'body')
});
cm.getColumnByField('message_id').component.updateDefinition({
title: this.$p.t('messages', 'message_id')
});
cm.getColumnByField('insertamum').component.updateDefinition({
title: this.$p.t('global', 'datum')
});
cm.getColumnByField('sender').component.updateDefinition({
title: this.$p.t('messages', 'sender')
});
cm.getColumnByField('recipient').component.updateDefinition({
title: this.$p.t('messages', 'recipient')
});
cm.getColumnByField('sender_id').component.updateDefinition({
title: this.$p.t('messages', 'senderId')
});
cm.getColumnByField('recipient_id').component.updateDefinition({
title: this.$p.t('messages', 'recipientId')
});
cm.getColumnByField('statusdatum').component.updateDefinition({
title: this.$p.t('notiz', 'letzte_aenderung')
});
cm.getColumnByField('status').component.updateDefinition({
formatterParams: [
this.$p.t('messages/unread'),
this.$p.t('messages/read'),
this.$p.t('messages/archived'),
this.$p.t('messages/deleted')
]
});
this.$refs.table.tabulator.rowManager.getDisplayRows();
/*
cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'aktionen')
});
*/
this.$emit('tabulator_tablebuilt');
}
},
{
event: 'rowClick',
handler: (e, row) => {
const selectedMessage = row.getData().message_id;
const body = row.getData().body;
this.previewBody = body;
}
},
/*
{
event: 'pageLoaded',
handler: (pageno) => {
this.pageNo = pageno+1;
}
}
*/
],
previewBody: "",
open: false,
personId: null,
layoutColumnsOnNewData: false,
height: '400',
}
},
methods: {
@@ -339,10 +103,10 @@ export default {
}
});
// to avoid endless loop
if (iteration > messages.length) break;
// to avoid endless loop
if (iteration > messages.length) break;
}
return {data: messageNested, last_page: last_page};
return {data: messageNested, last_page: last_page};
},
loadAjaxCall(url, config, params){
return this.$api.call(
@@ -351,24 +115,231 @@ export default {
}
},
computed: {
statusText(){
return {
0: this.$p.t('messsages', 'unread'),
1: this.$p.t('messsages', 'read'),
2: this.$p.t('messsages', 'archived'),
3: this.$p.t('messsages', 'deleted')
}
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: this.loadAjaxCall,
ajaxParams: () => {
return {
id: this.id,
type: this.typeId
};
},
ajaxResponse: (url, params, response) => this.buildTreemap(response),
layout: 'fitDataStretchFrozen',
index: 'message_id',
persistenceID: 'core-message-20260217',
selectableRows: 1,
selectableRowsRangeMode: 'click',
columns: [
{title: "subject", field: "subject", headerFilter: true},
{title: "body", field: "body", formatter: "html", visible: false, headerFilter: true},
{title: "message_id", field: "message_id", visible: false, headerFilter: true},
{
title: "Datum",
field: "insertamum",
headerFilter: true,
formatter: function (cell) {
const dateStr = cell.getValue();
const date = new Date(dateStr); // Convert to Date object
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: false
});
},
headerFilterFunc(headerValue, rowValue) {
const matches = headerValue.match(/^(([0-9]{2})\.)?([0-9]{2})\.([0-9]{4})?$/);
let comparestr = headerValue;
if(matches !== null) {
const year = (matches[4] !== undefined) ? matches[4] : '';
const month = matches[3];
const day = (matches[2] !== undefined) ? matches[2] : '';
comparestr = year + '-' + month + '-' + day;
}
return rowValue.match(comparestr);
}
},
{title: "sender", field: "sender", headerFilter: true},
{title: "recipient", field: "recipient", headerFilter: true},
{title: "senderId", field: "sender_id", headerFilter: true},
{title: "recipientId", field: "recipient_id", headerFilter: true},
{title: "Relationmessage ID", field: "relationmessage_id", headerFilter: true},
{
title: "Status",
field: "status",
headerFilter: true,
formatterParams: [
"unread",
"read",
"archived",
"deleted"
],
formatter: (cell, formatterParams) => {
const key = formatterParams[cell.getValue()];
return this.$p.t('messages', key);
},
},
{
title: "letzte Änderung",
field: "statusdatum",
headerFilter: true,
formatter: function (cell) {
const dateStr = cell.getValue();
const date = new Date(dateStr); // Convert to Date object
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: false
});
},
headerFilterFunc(headerValue, rowValue) {
const matches = headerValue.match(/^(([0-9]{2})\.)?([0-9]{2})\.([0-9]{4})?$/);
let comparestr = headerValue;
if(matches !== null) {
const year = (matches[4] !== undefined) ? matches[4] : '';
const month = matches[3];
const day = (matches[2] !== undefined) ? matches[2] : '';
comparestr = year + '-' + month + '-' + day;
}
return rowValue.match(comparestr);
}
},
{
title: 'Aktionen', field: 'actions',
width: 100,
formatter: (cell, formatterParams, onRendered) => {
let container = document.createElement('div');
container.className = "d-flex gap-2";
let button = document.createElement('button');
if (this.personId != cell.getData().sender_id) {
button.disabled = true;
button.style = "visibility: hidden";
button.ariaHidden = true;
}
button.className = 'btn btn-outline-secondary btn-action';
button.title = this.$p.t('global', 'reply');
button.innerHTML = '<i class="fa fa-reply"></i>';
button.addEventListener(
'click',
(event) =>
this.actionReplyToMessage(cell.getData().message_id)
);
container.append(button);
button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.title = this.$p.t('ui', 'loeschen');
button.innerHTML = '<i class="fa fa-xmark"></i>';
button.addEventListener(
'click',
() =>
this.actionDeleteMessage(cell.getData().message_id)
);
container.append(button);
return container;
},
frozen: true
}
],
pagination: true,
paginationMode: "remote",
paginationSize: 15,
paginationInitialPage: 1,
dataTree: true,
headerSort: true,
dataTreeChildField: "children",
dataTreeCollapseElement:"<i class='fas fa-minus-square'></i>",
dataTreeChildIndent: 15,
dataTreeStartExpanded: false,
locale: 'de',
"langs": {
"de":{ //German language definition
"data":{
"loading":"Lädt", //data loader text
"error":"Fehler", //data error text
},
"pagination":{
"first":"Erste",
"first_title":"Erste Seite",
"last":"Letzte",
"last_title":"Letzte Seite",
"prev":"Vorige",
"prev_title":"Vorige Seite",
"next":"Nächste",
"next_title":"Nächste Seite",
"all":"Alle"
},
},
}
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['global', 'person', 'stv', 'messages', 'ui', 'notiz']);
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('subject', this.$p.t('global', 'betreff'));
setHeader('body', this.$p.t('messages', 'body'));
setHeader('message_id', this.$p.t('messages', 'message_id'));
setHeader('insertamum', this.$p.t('global', 'datum'));
setHeader('sender', this.$p.t('messages', 'sender'));
setHeader('recipient', this.$p.t('messages', 'recipient'));
setHeader('sender_id', this.$p.t('messages', 'senderId'));
setHeader('recipient_id', this.$p.t('messages', 'recipientId'));
setHeader('statusdatum', this.$p.t('notiz', 'letzte_aenderung'));
this.$refs.table.tabulator.rowManager.getDisplayRows();
this.$emit('tabulator_tablebuilt');
}
},
{
event: 'rowClick',
handler: (e, row) => {
const selectedMessage = row.getData().message_id;
const body = row.getData().body;
this.previewBody = body;
}
},
];
return events;
},
},
mounted() {
// change to target="_blank"
/* this.$nextTick(() => {
const links = document.querySelectorAll('.preview a');
links.forEach(link => {
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener noreferrer'); // Sicherheitsmaßnahme
});
});*/
/* this.$nextTick(() => {
const links = document.querySelectorAll('.preview a');
links.forEach(link => {
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener noreferrer'); // Sicherheitsmaßnahme
});
});*/
},
created(){
if(this.typeId != 'person_id' && Array.isArray(this.id) && this.id.length === 1) {
@@ -383,6 +354,7 @@ export default {
})
.catch(this.$fhcAlert.handleSystemError);
}
},
template: `
<div class="messages-detail-table">
@@ -392,7 +364,7 @@ export default {
<div class="row">
<!--table-->
<div class="col-sm-6 pt-6">
<div class="col-sm-6 pt-1">
<core-filter-cmpt
ref="table"
:tabulator-options="tabulatorOptions"
@@ -408,9 +380,9 @@ export default {
</div>
<!--preview wysiwyg-window-->
<div class="col-sm-6 pt-6">
<br><br><br><br>
<div ref="preview">
<div class="col-sm-6 pt-5">
<div class="msg-preview-spacer pt-2" aria-hidden="true"></div>
<div ref="preview" class="bg-white">
<div v-html="previewBody" class="p-3 border rounded overflow-scroll twoColumns"></div>
</div>
@@ -452,4 +424,4 @@ export default {
</div>
`
}
}
+138 -200
View File
@@ -1,5 +1,4 @@
import VueDatePicker from '../vueDatepicker.js.php';
import PvAutoComplete from "../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js";
import FormUploadDms from '../Form/Upload/Dms.js';
import {CoreFilterCmpt} from "../filter/Filter.js";
import BsModal from "../Bootstrap/Modal.js";
@@ -11,7 +10,6 @@ export default {
components: {
CoreFilterCmpt,
VueDatePicker,
PvAutoComplete,
FormUploadDms,
FormForm,
FormInput,
@@ -50,7 +48,49 @@ export default {
},
data() {
return {
tabulatorOptions: {
notizen: [],
multiupload: true,
mitarbeiter: [],
filteredMitarbeiter: [],
zwischenvar: '',
editorInitialized: false,
editor: null,
notizData: {
typeId: this.typeId,
id: this.id,
titel: null,
statusNew: true,
text: '',
lastUpdate: null,
von: null,
bis: null,
document: null,
erledigt: false,
verfasser: null,
bearbeiter: null,
anhang: []
},
showVariables: {
showTitel: false,
showText: false,
showVerfasser: false,
showBearbeiter: false,
showVon: false,
showBis: false,
showDokumente: false,
showErledigt: false,
showNotiz_id: false,
showNotizzuordnung_id: false,
showType_id: false,
showId: false,
showLastupdate: false
},
currentVerfasserUid: null
}
},
computed: {
tabulatorOptions: function() {
return {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(this.endpoint.getNotizen(this.id, this.typeId)),
ajaxParams: () => {
@@ -65,6 +105,7 @@ export default {
title: "Titel",
field: "titel",
width: 100,
visible: this.showVariables.showTitel,
tooltip:function(e, cell, onRendered){
var el = document.createElement("div");
el.style.backgroundColor = "white";
@@ -87,6 +128,7 @@ export default {
width: 250,
formatter: "html",
//clipContents: true,
visible: this.showVariables.showText,
tooltip:function(e, cell, onRendered){
var el = document.createElement("div");
el.style.backgroundColor = "white";
@@ -101,18 +143,18 @@ export default {
return el;
},
},
{title: "VerfasserIn", field: "verfasser", width: 124},
{title: "BearbeiterIn", field: "bearbeiter", width: 126},
{title: "VerfasserIn", field: "verfasser", width: 124, visible: this.showVariables.showVerfasser},
{title: "BearbeiterIn", field: "bearbeiter", width: 126, visible: this.showVariables.showBearbeiter},
{title: "Verfasser UID", field: "verfasser_uid", width: 124, visible: false},
{title: "Bearbeiter UID", field: "bearbeiter_uid", width: 126, visible: false},
{title: "Start", field: "start_format", width: 86, visible: false},
{title: "Ende", field: "ende_format", width: 86, visible: false},
{title: "Dokumente", field: "countdoc", width: 100, visible: false},
{title: "Start", field: "start_format", width: 86, visible: this.showVariables.showVon},
{title: "Ende", field: "ende_format", width: 86, visible: this.showVariables.showBis},
{title: "Dokumente", field: "countdoc", width: 100, visible: this.showVariables.showDokumente},
{
title: "Erledigt",
field: "erledigt",
width: 97,
visible: false,
visible: this.showVariables.showErledigt,
formatter:"tickCross",
hozAlign:"center",
formatterParams: {
@@ -120,15 +162,15 @@ export default {
crossElement: '<i class="fa fa-xmark text-danger"></i>'
}
},
{title: "Notiz_id", field: "notiz_id", width: 92, visible: false},
{title: "Notizzuordnung_id", field: "notizzuordnung_id", width: 164, visible: false},
{title: "type_id", field: "type_id", width: 164, visible: false},
{title: "extension_id", field: "id", width: 135, visible: false},
{title: "Notiz_id", field: "notiz_id", width: 92, visible: this.showVariables.showNotiz_id},
{title: "Notizzuordnung_id", field: "notizzuordnung_id", width: 164, visible: this.showVariables.showNotizzuordnung_id},
{title: "type_id", field: "type_id", width: 164, visible: this.showVariables.showType_id},
{title: "extension_id", field: "id", width: 135, visible: this.showVariables.showId},
{
title: "letzte Änderung",
title: "letzte Änderung",
field: "lastupdate",
width: 146,
visible: false,
visible: this.showVariables.showLastupdate,
formatter: function (cell) {
const dateStr = cell.getValue();
if (!dateStr) return "";
@@ -185,129 +227,60 @@ export default {
index: 'notiz_id',
persistenceID: this.tabulatorPersistenceId,
persistence: {
sort: false,
sort: true,
columns: ["width", "visible", "frozen"],
filter: false,
headerFilter: false,
group: false,
page: false,
}
},
tabulatorEvents: [
};
},
tabulatorEvents: function () {
return [
{
event: 'tableBuilt',
handler: async () => {
//to avoid js error
if (!this.$refs.table) return;
await this.$p.loadCategory(['notiz', 'global', 'ui']);
let cm = this.$refs.table.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('verfasser').component.updateDefinition({
title: this.$p.t('notiz', 'verfasser'),
visible: this.showVariables.showVerfasser
});
cm.getColumnByField('verfasser_uid').component.updateDefinition({
title: this.$p.t('ui', 'verfasser_uid'),
});
cm.getColumnByField('titel').component.updateDefinition({
title: this.$p.t('global', 'titel'),
//visible: this.showVariables.showTitel
});
cm.getColumnByField('bearbeiter').component.updateDefinition({
title: this.$p.t('notiz', 'bearbeiter'),
visible: this.showVariables.showBearbeiter
});
cm.getColumnByField('bearbeiter_uid').component.updateDefinition({
title: this.$p.t('ui', 'bearbeiter_uid'),
});
cm.getColumnByField('start_format').component.updateDefinition({
title: this.$p.t('global', 'gueltigVon'),
visible: this.showVariables.showVon
});
cm.getColumnByField('ende_format').component.updateDefinition({
title: this.$p.t('global', 'gueltigBis'),
visible: this.showVariables.showBis
});
cm.getColumnByField('countdoc').component.updateDefinition({
title: this.$p.t('notiz', 'document'),
visible: this.showVariables.showDokumente
});
cm.getColumnByField('erledigt').component.updateDefinition({
title: this.$p.t('notiz', 'erledigt'),
visible: this.showVariables.showErledigt
});
cm.getColumnByField('lastupdate').component.updateDefinition({
title: this.$p.t('notiz', 'letzte_aenderung'),
visible: this.showVariables.showLastupdate
});
cm.getColumnByField('notiz_id').component.updateDefinition({
visible: this.showVariables.showNotiz_id,
title: this.$p.t('ui', 'notiz_id')
});
cm.getColumnByField('notizzuordnung_id').component.updateDefinition({
visible: this.showVariables.showNotizzuordnung_id,
title: this.$p.t('ui', 'notizzuordnung_id')
});
cm.getColumnByField('type_id').component.updateDefinition({
visible: this.showVariables.showType_id,
title: this.$p.t('ui', 'type_id')
});
cm.getColumnByField('id').component.updateDefinition({
visible: this.showVariables.showId,
title: this.$p.t('ui', 'extension_id')
});
cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'aktionen')
});
const el = col.getElement();
if (!el || !el.querySelector) return;
cm.getColumnByField('text_stripped').component.updateDefinition({
title: this.$p.t('global', 'text'),
width: 250,
tooltip: true,
//clipContents: true,
});
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('verfasser', this.$p.t('notiz', 'verfasser'));
setHeader('verfasser_uid', this.$p.t('ui', 'verfasser_uid'));
setHeader('titel', this.$p.t('global', 'titel'));
setHeader('bearbeiter', this.$p.t('notiz', 'bearbeiter'));
setHeader('bearbeiter_uid', this.$p.t('ui', 'bearbeiter_uid'));
setHeader('start_format', this.$p.t('global', 'gueltigVon'));
setHeader('ende_format', this.$p.t('global', 'gueltigBis'));
setHeader('countdoc', this.$p.t('notiz', 'document'));
setHeader('erledigt', this.$p.t('notiz', 'erledigt'));
setHeader('lastupdate', this.$p.t('notiz', 'letzte_aenderung'));
setHeader('notiz_id', this.$p.t('ui', 'notiz_id'));
setHeader('notizzuordnung_id', this.$p.t('ui', 'notizzuordnung_id'));
setHeader('type_id', this.$p.t('ui', 'type_id'));
setHeader('id', this.$p.t('ui', 'extension_id'));
setHeader('text_stripped', this.$p.t('global', 'text'));
// Force layout recalculation for handling overflow text
this.$refs.table.tabulator.redraw(true);
}
}
],
notizen: [],
multiupload: true,
mitarbeiter: [],
filteredMitarbeiter: [],
zwischenvar: '',
editorInitialized: false,
editor: null,
notizData: {
typeId: this.typeId,
titel: null,
statusNew: true,
text: '',
lastUpdate: null,
von: null,
bis: null,
document: null,
erledigt: false,
verfasser: null,
bearbeiter: null,
anhang: []
},
showVariables: {
showTitel: false,
showText: false,
showVerfasser: false,
showBearbeiter: false,
showVon: false,
showBis: false,
showDokumente: false,
showErledigt: false,
showNotiz_id: false,
showNotizzuordnung_id: false,
showType_id: false,
showId: false,
showLastupdate: false
}
];
}
},
methods: {
@@ -335,8 +308,6 @@ export default {
this.notizData.bis = this.notizen.ende;
this.notizData.document = this.notizen.dms_id;
this.notizData.erledigt = this.notizen.erledigt;
this.notizData.verfasser = this.notizen.verfasser_uid;
this.notizData.intVerfasser = this.notizen.verfasser_uid;
this.notizData.intBearbeiter = this.notizen.bearbeiter_uid;
this.notizData.bearbeiter = this.notizen.bearbeiter_uid;
}
@@ -359,6 +330,7 @@ export default {
},
addNewNotiz() {
const formData = new FormData();
this.notizData.id = this.id;
formData.append('data', JSON.stringify(this.notizData));
Object.entries(this.notizData.anhang).forEach(([k, v]) => formData.append(k, v));
@@ -402,6 +374,7 @@ export default {
this.notizData = result.data;
this.notizData.typeId = this.typeId;
this.notizData.anhang = [];
this.currentVerfasserUid = result.data.verfasser_uid;
return result;
})
.catch(this.$fhcAlert.handleSystemError);
@@ -455,16 +428,17 @@ export default {
bis: null,
document: null,
erledigt: false,
verfasser: this.uid,
bearbeiter: null,
anhang: []
anhang: [],
};
this.currentVerfasserUid = this.uid
},
getUid() {
return this.$api
.call(this.endpoint.getUid())
.then(result => {
this.notizData.intVerfasser = result.data;
this.currentVerfasserUid = result.data;
this.uid = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
},
@@ -547,14 +521,7 @@ export default {
},
deep: true
},
'notizData.intVerfasser': {
handler(newVal) {
if (typeof newVal === 'object') {
this.notizData.verfasser = newVal.mitarbeiter_uid;
}
},
deep: true
},
id() {
this.reload();
}
@@ -654,24 +621,23 @@ export default {
<div class="row mb-3">
<label for="bis" class="form-label col-sm-2">{{$p.t('notiz','verfasser')}}</label>
<div v-if="notizData.verfasser_uid" class="col-sm-3">
<input type="text" :readonly="readonly" class="form-control" id="name" v-model="notizData.verfasser_uid">
</div>
<div v-else class="col-sm-3">
<PvAutoComplete v-model="notizData.intVerfasser" optionLabel="mitarbeiter" :suggestions="filteredMitarbeiter" @complete="search" minLength="3"/>
<div class="col-sm-3">
<input type="text" readonly="readonly" class="form-control" id="name" v-model="currentVerfasserUid">
</div>
<label for="von" class="form-label col-sm-1">{{$p.t('global','gueltigVon')}}</label>
<div class="col-sm-3">
<vue-date-picker
id="von"
v-model="notizData.start"
clearable="false"
<form-input
type="DatePicker"
v-model="notizData['start']"
name="von"
auto-apply
:enable-time-picker="false"
format="dd.MM.yyyy"
preview-format="dd.MM.yyyy"
:teleport="true"
preview-format="dd.MM.yyyy"></vue-date-picker>
>
</form-input>
</div>
</div>
@@ -679,26 +645,35 @@ export default {
<label for="bis" class="form-label col-sm-2">{{$p.t('notiz','bearbeiter')}}</label>
<div v-if="notizData.bearbeiter_uid" class="col-sm-3">
<input type="text" :readonly="readonly" class="form-control" id="name" v-model="notizData.bearbeiter_uid">
<input type="text" class="form-control" id="name" v-model="notizData.bearbeiter_uid">
</div>
<div v-else class="col-sm-3">
<PvAutoComplete v-model="notizData.intBearbeiter" optionLabel="mitarbeiter" :suggestions="filteredMitarbeiter" @complete="search" minlength="3"/>
<form-input
type="autocomplete"
v-model="notizData.intBearbeiter"
:suggestions="filteredMitarbeiter"
@complete="search"
optionLabel="mitarbeiter"
minlength="3"
>
</form-input>
</div>
<label for="bis" class="form-label col-sm-1">{{$p.t('global','gueltigBis')}}</label>
<div class="col-sm-3">
<vue-date-picker
id="bis"
<form-input
type="DatePicker"
v-model="notizData.ende"
clearable="false"
name="bis"
auto-apply
:enable-time-picker="false"
format="dd.MM.yyyy"
preview-format="dd.MM.yyyy"
:teleport="true"
preview-format="dd.MM.yyyy">
</vue-date-picker>
>
</form-input>
</div>
</div>
@@ -842,28 +817,15 @@ export default {
<div class="row mb-3">
<form-input
v-if="notizData.verfasser_uid"
container-class="col-6"
:label="$p.t('notiz', 'verfasser')"
type="text"
v-model="notizData.verfasser_uid"
name="titel"
readonly="readonly"
v-model="currentVerfasserUid"
name="verfasser"
>
</form-input>
<form-input
v-else
container-class="col-6"
:label="$p.t('notiz', 'verfasser')"
type="autocomplete"
v-model="notizData.intVerfasser"
:suggestions="filteredMitarbeiter"
@complete="search"
optionLabel="mitarbeiter"
minLength="3"
>
</form-input>
<form-input
v-if="notizData.bearbeiter_uid"
container-class="col-6"
@@ -1008,25 +970,12 @@ export default {
<div class="row mb-3">
<form-input
v-if="notizData.verfasser_uid"
container-class="col-6"
:label="$p.t('notiz', 'verfasser')"
type="text"
v-model="notizData.verfasser_uid"
name="titel"
>
</form-input>
<form-input
v-else
container-class="col-6"
:label="$p.t('notiz', 'verfasser')"
type="autocomplete"
v-model="notizData.intVerfasser"
:suggestions="filteredMitarbeiter"
@complete="search"
optionLabel="mitarbeiter"
minLength="3"
readonly="readonly"
v-model="currentVerfasserUid"
name="verfasser"
>
</form-input>
@@ -1195,33 +1144,21 @@ export default {
<div class="row mb-3">
<form-input
v-if="notizData.verfasser_uid"
container-class="col-6"
:label="$p.t('notiz', 'verfasser')"
type="text"
v-model="notizData.verfasser_uid"
name="titel"
readonly="readonly"
v-model="currentVerfasserUid"
name="verfasser"
>
</form-input>
<form-input
v-else
container-class="col-6"
:label="$p.t('notiz', 'verfasser')"
type="autocomplete"
v-model="notizData.intVerfasser"
:suggestions="filteredMitarbeiter"
@complete="search"
optionLabel="mitarbeiter"
minLength="3"
>
</form-input>
<form-input
v-if="notizData.bearbeiter_uid"
container-class="col-6"
:label="$p.t('notiz', 'bearbeiter')"
v-model="notizData.bearbeiter_uid"
name="bearbeiter"
minlength="3"
>
</form-input>
@@ -1235,6 +1172,7 @@ export default {
:suggestions="filteredMitarbeiter"
@complete="search"
optionLabel="mitarbeiter"
name="bearbeiter"
minlength="3"
>
</form-input>
@@ -1292,4 +1230,4 @@ export default {
<p v-else>Kein Layout übergeben</p>
</div>
</div>`,
}
}
@@ -84,6 +84,8 @@ export default {
configShowAufnahmegruppen: this.config.showAufnahmegruppen,
configAllowUebernahmePunkte: this.config.allowUebernahmePunkte,
configUseReihungstestPunkte: this.config.useReihungstestPunkte,
configHasExcludedAreas: this.config.hasExcludedAreas,
configStvTagsEnabled: this.config.stvTagsEnabled,
appConfig: Vue.computed(() => this.appconfig),
hasZGVBakkPermission: this.permissions['student/editBakkZgv'],
hasZGVMasterPermission: this.permissions['student/editMakkZgv'],
@@ -94,6 +96,7 @@ export default {
},
data() {
return {
sidebarCollapsed: false,
appconfig: {},
configEndpoints: ApiStvConfig,
selected: [],
@@ -146,7 +149,8 @@ export default {
sprachen: [],
geschlechter: []
},
verbandEndpoint: ApiStvVerband
verbandEndpoint: ApiStvVerband,
filter: []
}
},
computed: {
@@ -392,6 +396,7 @@ export default {
}
},
onSearch(e) {
this.deleteCustomFilter();
const searchsettings = { ...this.$refs.searchbar.searchsettings };
if (searchsettings.searchstr.length >= 2) {
this.blurSearchbar();
@@ -420,6 +425,12 @@ export default {
this.$refs.searchbar.$refs.input.blur();
this.$refs.searchbar.abort();
this.$refs.searchbar.hideresult();
},
handleCustomFilter(filter){
this.filter = filter;
},
deleteCustomFilter(){
this.$refs.stvList.resetFilter();
}
},
created() {
@@ -494,12 +505,10 @@ export default {
.catch(this.$fhcAlert.handleSystemError);
},
mounted() {
//Test manu Systemerror
//FHC_JS_DATA_STORAGE_OBJECT.systemerror_mailto = 'ma0068@technikum-wien.at';this.$fhcAlert.handleSystemError(1);
this.handlePersonUrl();
},
template: /* html */`
<div class="stv">
<div class="stv" :class="{ 'sidebar-collapsed': sidebarCollapsed }">
<header class="navbar navbar-expand-lg navbar-dark bg-dark flex-md-nowrap p-0 shadow">
<div class="col-md-4 col-lg-3 col-xl-2 d-flex align-items-center">
<button
@@ -514,6 +523,14 @@ export default {
<span class="svg-icon svg-icon-apps"></span>
</button>
<a class="navbar-brand me-0" :href="stvRoot">StudVw: {{studiensemesterKurzbz}} {{studiengangKuerzel}}</a>
<button
class="btn btn-outline-light border-0 d-none d-md-inline-flex m-1 ms-auto"
type="button"
@click="sidebarCollapsed = !sidebarCollapsed"
:aria-label="$p.t('ui/toggle_nav')"
>
<span class="fa-solid fa-list"></span>
</button>
</div>
<button
class="btn btn-outline-light border-0 d-md-none m-1 collapsed"
@@ -625,10 +642,10 @@ export default {
<main class="col-md-8 ms-sm-auto col-lg-9 col-xl-10">
<vertical-split>
<template #top>
<stv-list ref="stvList" v-model:selected="selected" :studiengang-kz="studiengangKz" :studiensemester-kurzbz="studiensemesterKurzbz"></stv-list>
<stv-list ref="stvList" v-model:selected="selected" :studiengang-kz="studiengangKz" :studiensemester-kurzbz="studiensemesterKurzbz" @filterActive="handleCustomFilter"></stv-list>
</template>
<template #bottom>
<stv-details ref="details" :students="selected"></stv-details>
<stv-details ref="details" :students="selected" @reload="reloadList"></stv-details>
</template>
</vertical-split>
</main>
@@ -39,7 +39,19 @@ export default {
return Object.fromEntries(Object.entries(this.configStudents).filter(([ , value ]) => !value.showOnlyWithUid));
}
return Object.fromEntries(Object.entries(this.configStudents).filter(([ , value ]) => !value.showOnlyWithUid && !value.showOnlyWithUid));
}
},
tile_PersId(){
let tile = this.students[0].person_id != null ? this.students[0].person_id : '-';
return tile;
},
tile_MatrNr(){
let tile = this.students[0].matr_nr != null ? this.students[0].matr_nr : '-';
return tile;
},
tile_PersKz(){
let tile = this.students[0].matrikelnr != null ? this.students[0].matrikelnr : '-';
return tile;
},
},
watch: {
'$p.user_language.value'(n, o) {
@@ -65,7 +77,10 @@ export default {
reload() {
if (this.$refs.tabs?.$refs?.current?.reload)
this.$refs.tabs.$refs.current.reload();
}
},
reloadList() {
this.$emit('reload');
},
},
created() {
this.loadConfig();
@@ -79,7 +94,16 @@ export default {
<fhc-header
:headerData="students"
typeHeader="student"
@reload="reloadList"
fotoEditable
>
<template #uid>{{students[0].uid}}</template>
<template #titleAlphaTile>PersID</template>
<template #valueAlphaTile>{{tile_PersId}}</template>
<template #titleBetaTile>MatrNr</template>
<template #valueBetaTile>{{tile_MatrNr}}</template>
<template #titleGammaTile>PersKz</template>
<template #valueGammaTile>{{tile_PersKz}}</template>
</fhc-header>
<fhc-tabs
v-if="students.length == 1"
@@ -34,43 +34,61 @@ export default {
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];
},
showAllFormats() {
if( this.isBerechtigtDocAndOdt === false
|| !Array.isArray(this.isBerechtigtDocAndOdt) )
{
return false;
}
let retval = this.isBerechtigtDocAndOdt.includes(this.stgInfo.oe_kurzbz);
return retval;
}
},
props: {
student: Object
},
data() {
return {
tabulatorOptions: {
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: [],
selectedVorsitz: null,
filteredMitarbeiter: [],
filteredPersons: [],
selectedPruefer1: null,
selectedPruefer2: null,
selectedPruefer3: null,
stgInfo: { typ: '', oe_kurzbz: '' },
abortController: {
mitarbeiter: null,
persons: null
},
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: '200',
}
},
computed: {
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(ApiStvAbschlusspruefung.getAbschlusspruefung(this.student.uid)),
ajaxResponse: (url, params, response) => response.data,
index: 'abschlusspruefung_id',
persistenceID: 'stv-details-finalexam-20260217',
columns: [
{title: "vorsitz", field: "vorsitz_nachname"},
{title: "abschlussbeurteilung", field: "beurteilung_bezeichnung"},
@@ -163,14 +181,11 @@ export default {
frozen: true
},
],
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: '200',
index: 'abschlusspruefung_id',
persistenceID: 'stv-details-finalexam-2025112401'
},
tabulatorEvents: [
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'dataLoaded',
handler: data => this.tabulatorData = data.map(item => {
@@ -181,94 +196,66 @@ export default {
{
event: 'tableBuilt',
handler: async() => {
if (!this.$refs.table) return;
await this.$p.loadCategory(['global', 'person', 'stv', 'abschlusspruefung', 'ui']);
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
let cm = this.$refs.table.tabulator.columnManager;
const el = col.getElement();
if (!el || !el.querySelector) return;
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('abschlusspruefung', 'abschlusspruefung_id')
});
/*
cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'aktionen')
});
*/
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('vorsitz_nachname', this.$p.t('abschlusspruefung', 'vorsitz_header'));
setHeader('beurteilung_bezeichnung', this.$p.t('abschlusspruefung', 'abschlussbeurteilung'));
setHeader('p1_nachname', this.$p.t('abschlusspruefung', 'pruefer1'));
setHeader('p2_nachname', this.$p.t('abschlusspruefung', 'pruefer2'));
setHeader('p3_nachname', this.$p.t('abschlusspruefung', 'pruefer3'));
setHeader('datum', this.$p.t('global', 'datum'));
setHeader('uhrzeit', this.$p.t('global', 'uhrzeit'));
setHeader('freigabedatum', this.$p.t('abschlusspruefung', 'freigabe'));
setHeader('antritt_bezeichnung', this.$p.t('abschlusspruefung', 'pruefungsantritt'));
setHeader('sponsion', this.$p.t('abschlusspruefung', 'sponsion'));
setHeader('anmerkung', this.$p.t('global', 'anmerkung'));
setHeader('pruefungstyp_kurzbz', this.$p.t('global', 'typ'));
setHeader('abschlusspruefung_id', this.$p.t('abschlusspruefung', 'abschlusspruefung_id'));
}
}
],
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: [],
selectedVorsitz: null,
filteredMitarbeiter: [],
filteredPersons: [],
selectedPruefer1: null,
selectedPruefer2: null,
selectedPruefer3: null,
stgInfo: { typ: '', oe_kurzbz: '' },
abortController: {
mitarbeiter: null,
persons: null
},
];
return events;
},
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];
},
showAllFormats() {
if( this.isBerechtigtDocAndOdt === false
|| !Array.isArray(this.isBerechtigtDocAndOdt) )
{
return false;
}
let retval = this.isBerechtigtDocAndOdt.includes(this.stgInfo.oe_kurzbz);
return retval;
}
},
watch: {
@@ -339,7 +326,7 @@ export default {
},
getPersonLabel(titelpre, nachname, vorname, titelpost, uid) {
return nachname + ' ' + vorname + (titelpre ? ' ' + titelpre : '') + (titelpost ? ' ' + titelpost : '') + (uid ? ' (' + uid + ')' : '');
},
actionDeleteAbschlusspruefung(abschlusspruefung_id) {
this.$fhcAlert
@@ -841,4 +828,3 @@ export default {
</div>
`
}
@@ -31,12 +31,30 @@ export default {
},
data() {
return {
tabulatorOptions: {
formData: {},
listBegruendungen: [],
listNewLehrveranstaltungen: [],
listLektoren: [],
listKompatibleLehrveranstaltungen: [],
statusNew: true,
showNotizen: false,
currentAnrechnung_id: null,
endpoint: ApiNotizPerson,
}
},
computed: {
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
ApiStvExemptions.getAnrechnungen(this.student.prestudent_id)
),
ajaxResponse: (url, params, response) => response.data,
layout: 'fitDataStretchFrozen',
height: '500',
index: 'anrechnung_id',
persistenceID: 'stv-details-anrechnungen-20260217',
selectableRows: true,
columns: [
{title: "anrechnung_id", field: "anrechnung_id", visible: false},
{title: "lehrveranstaltung_id", field: "lehrveranstaltung_id", visible: false},
@@ -107,63 +125,43 @@ export default {
frozen: true
},
],
layout: 'fitDataStretchFrozen',
height: '500',
index: 'anrechnung_id',
persistenceID: 'stv-details-anrechnungen-2025112401'
},
tabulatorEvents: [
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async () => {
await this.$p.loadCategory(['anrechnungen', 'global', 'ui', 'lehre']);
let cm = this.$refs.table.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('anrechnung_id').component.updateDefinition({
title: this.$p.t('ui', 'anrechnung_id'),
});
cm.getColumnByField('lehrveranstaltung_id').component.updateDefinition({
title: this.$p.t('lehre', 'lehrveranstaltung_id'),
});
cm.getColumnByField('bez_lehrveranstaltung').component.updateDefinition({
title: this.$p.t('lehre', 'lehrveranstaltung'),
});
cm.getColumnByField('begruendung').component.updateDefinition({
title: this.$p.t('global', 'begruendung'),
});
cm.getColumnByField('lehrveranstaltung_id_kompatibel').component.updateDefinition({
title: this.$p.t('anrechnung', 'lehrveranstaltung_id_kompatibel'),
});
cm.getColumnByField('lehrveranstaltung_bez_kompatibel').component.updateDefinition({
title: this.$p.t('anrechnung', 'lehrveranstaltung_bez_kompatibel'),
});
cm.getColumnByField('status').component.updateDefinition({
title: this.$p.t('global', 'status'),
});
cm.getColumnByField('genehmigt_von').component.updateDefinition({
title: this.$p.t('anrechnung', 'genehmigtVon'),
});
cm.getColumnByField('notizen_anzahl').component.updateDefinition({
title: this.$p.t('anrechnung', 'existingNotes'),
});
cm.getColumnByField('insertamum').component.updateDefinition({
title: this.$p.t('global', 'datum'),
});
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader( 'anrechnung_id', this.$p.t('ui', 'anrechnung_id'));
setHeader( 'lehrveranstaltung_id', this.$p.t('lehre', 'lehrveranstaltung_id'));
setHeader( 'bez_lehrveranstaltung', this.$p.t('lehre', 'lehrveranstaltung'));
setHeader( 'begruendung', this.$p.t('global', 'begruendung'));
setHeader( 'lehrveranstaltung_id_kompatibel', this.$p.t('anrechnung', 'lehrveranstaltung_id_kompatibel'));
setHeader( 'lehrveranstaltung_bez_kompatibel', this.$p.t('anrechnung', 'lehrveranstaltung_bez_kompatibel'));
setHeader( 'status', this.$p.t('global', 'status'));
setHeader( 'genehmigt_von', this.$p.t('anrechnung', 'genehmigtVon'));
setHeader( 'notizen_anzahl', this.$p.t('anrechnung', 'existingNotes'));
setHeader( 'insertamum', this.$p.t('global', 'datum'));
}
}
],
formData: {},
listBegruendungen: [],
listNewLehrveranstaltungen: [],
listLektoren: [],
listKompatibleLehrveranstaltungen: [],
statusNew: true,
showNotizen: false,
currentAnrechnung_id: null,
endpoint: ApiNotizPerson
];
return events;
}
},
watch: {
@@ -91,12 +91,24 @@ export default {
ajaxResponse: (url, params, response) => response.data,
layout:"fitDataTable",
index: 'akte_id',
persistenceID: 'stv-details-archiv',
persistenceID: 'stv-details-archiv-20260217',
columns: [
{title: "Akte Id", field: "akte_id", visible: false},
{title: this.$p.t('stv', 'archiv_title'), field: "titel"},
{title: this.$p.t('stv', 'archiv_description'), field: "bezeichnung"},
{title: this.$p.t('stv', 'archiv_creation_date'), field: "erstelltam"},
{title: this.$p.t('stv', 'archiv_creation_date'), field: "erstelltam",
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: this.$p.t('stv', 'archiv_signiert'),
field: "signiert",
@@ -117,7 +129,22 @@ export default {
crossElement: '<i class="fa fa-xmark text-danger"></i>'
},
},
{title: this.$p.t('stv', 'archiv_accepted_on_at'), field: "akzeptiertamum"},
{title: this.$p.t('stv', 'archiv_accepted_on_at'), field: "akzeptiertamum",
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",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
});
}},
{
title: this.$p.t('stv', 'archiv_gedruckt'),
field: "gedruckt",
@@ -23,6 +23,10 @@ export default {
from: 'configUseReihungstestPunkte',
default: true
},
hasExcludedAreas: {
from: 'configHasExcludedAreas',
default: false
},
$reloadList: {
from: '$reloadList',
required: true
@@ -35,159 +39,20 @@ export default {
student: Object
},
data() {
let self = this;
return {
tabulatorOptions: {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
ApiStvAdmissionDates.getAufnahmetermine(this.student.person_id)
),
ajaxResponse: (url, params, response) => response.data,
columns: [
{title: "rt_id", field: "rt_id", visible: false},
{title: "rt_person_id", field: "rt_person_id", visible: false},
{title: "person_id", field: "person_id", 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",
});
}
},
{title: "stufe", field: "stufe"},
{title: "studiensemester", field: "studiensemester"},
{title: "anmerkung", field: "anmerkung", visible: false},
{title: "anmeldedatum", field: "anmeldedatum", visible: false,
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",
});
}
},
{title: "punkte", field: "punkte"},
{
title: "teilgenommen", field: "teilgenommen",
formatter: "tickCross",
hozAlign: "center",
formatterParams: {
tickElement: '<i class="fa fa-check text-success"></i>',
crossElement: '<i class="fa fa-xmark text-danger"></i>'
}
},
{title: "ort", field: "ort", visible: false},
{title: "studienplan", field: "studienplan", visible: false},
{title: "studienplan_id", field: "studienplan_id", visible: false},
{title: "stg", field: "studiengangkurzbzlang"},
{title: "Stg", field: "stg_kuerzel"},
{
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.actionEditPlacementTest(cell.getData().rt_person_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.actionDeletePlacementTest(cell.getData().rt_person_id)
);
container.append(button);
return container;
},
frozen: true
}
],
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: 200,
index: 'aufnahmetermin_id',
persistenceID: 'stv-details-table_admission-dates-2025112401'
},
tabulatorEvents: [
{
event: 'tableBuilt',
handler: async () => {
await this.$p.loadCategory(['admission', 'global', 'person', 'ui', 'projektarbeitsbeurteilung']);
let cm = this.$refs.table.tabulator.columnManager;
cm.getColumnByField('rt_id').component.updateDefinition({
title: this.$p.t('ui', 'reihungstest_id')
});
cm.getColumnByField('rt_person_id').component.updateDefinition({
title: this.$p.t('ui', 'reihungstest_person_id')
});
cm.getColumnByField('person_id').component.updateDefinition({
title: this.$p.t('person', 'person_id')
});
cm.getColumnByField('datum').component.updateDefinition({
title: this.$p.t('global', 'datum')
});
cm.getColumnByField('stufe').component.updateDefinition({
title: this.$p.t('admission', 'stufe')
});
cm.getColumnByField('studiensemester').component.updateDefinition({
title: this.$p.t('lehre', 'studiensemester')
});
cm.getColumnByField('anmerkung').component.updateDefinition({
title: this.$p.t('global', 'anmerkung')
});
cm.getColumnByField('anmeldedatum').component.updateDefinition({
title: this.$p.t('admission', 'anmeldedatum')
});
cm.getColumnByField('punkte').component.updateDefinition({
title: this.$p.t('exam', 'punkte')
});
cm.getColumnByField('teilgenommen').component.updateDefinition({
title: this.$p.t('admission', 'teilgenommen')
});
cm.getColumnByField('ort').component.updateDefinition({
title: this.$p.t('person', 'ort')
});
cm.getColumnByField('studienplan').component.updateDefinition({
title: this.$p.t('lehre', 'studienplan')
});
cm.getColumnByField('studienplan_id').component.updateDefinition({
title: this.$p.t('ui', 'studienplan_id')
});
cm.getColumnByField('studiengangkurzbzlang').component.updateDefinition({
title: this.$p.t('projektarbeitsbeurteilung', 'studiengang')
});
cm.getColumnByField('stg_kuerzel').component.updateDefinition({
title: this.$p.t('admission', 'stg_kurz')
});
}
}
],
formData: {},
statusNew: true,
listPlacementTests: [],
listStudyPlans: [],
filterOnlyFutureTestsSet: false,
filteredPlacementTests: []
filteredPlacementTests: [],
youngestSemester: null,
stgRtPers: null,
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: 200
}
},
methods: {
@@ -197,11 +62,12 @@ export default {
this.formData.anmeldedatum = new Date();
this.$refs.placementTestModal.show();
},
actionEditPlacementTest(rt_person_id) {
actionEditPlacementTest(rt_person_id, stg_kz) {
this.resetForm();
this.statusNew = false;
this.loadPlacementTest(rt_person_id);
this.$refs.placementTestModal.show();
this.stgRtPers = stg_kz;
},
actionDeletePlacementTest(rt_person_id) {
this.$fhcAlert
@@ -271,12 +137,13 @@ export default {
this.reload();
});
},
getResultReihungstest(reihungstest_id){
getResultReihungstest(reihungstest_id, stg_kz){
const paramsRt = {
reihungstest_id: reihungstest_id,
person_id: this.student.person_id,
punkte: this.useReihungstestPunkte,
studiengang_kz: this.student.studiengang_kz
studiengang_kz: stg_kz,
hasExcludedAreas: this.hasExcludedAreas
};
return this.$api
@@ -330,13 +197,188 @@ export default {
},
resetForm() {
this.formData = {};
this.stgRtPers = null;
},
parseSemester(semester) {
const type = semester.slice(0, 2).toUpperCase(); // "WS" or "SS"
const year = parseInt(semester.slice(2), 10);
// WS > SS
return year * 10 + (type === 'SS' ? 1 : 2);
}
},
computed: {
tabulatorOptions() {
const self = this;
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
ApiStvAdmissionDates.getAufnahmetermine(this.student.person_id)
),
ajaxResponse: (url, params, response) => {
const data = response.data;
const filtered = data.filter(item =>
item.studiengang_kz_ber === this.student.studiengang_kz
);
if (filtered.length > 0) {
filtered.sort((a, b) =>
this.parseSemester(b.studiensemester) - this.parseSemester(a.studiensemester)
);
self.youngestSemester = filtered[0].studiensemester;
} else {
self.youngestSemester = null;
}
return data;
},
rowFormatter: function(row) {
let data = row.getData();
if (data.studiengang_kz_ber === self.student.studiengang_kz &&
data.studiensemester === self.youngestSemester) {
let cells = row.getCells();
cells.forEach((c) => {
c.getElement().classList.add("row-green");
}
);
}
},
dataLoaded: function() {
this.redraw(true);
},
index: 'aufnahmetermin_id',
persistenceID: 'stv-details-table_admission-dates-20260217',
columns: [
{title: "rt_id", field: "rt_id", visible: false},
{title: "rt_person_id", field: "rt_person_id", visible: false},
{title: "person_id", field: "person_id", 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",
});
}
},
{title: "stufe", field: "stufe"},
{title: "studiensemester", field: "studiensemester"},
{title: "anmerkung", field: "anmerkung", visible: false},
{title: "anmeldedatum", field: "anmeldedatum", visible: false,
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",
});
}
},
{title: "punkte", field: "punkte"},
{
title: "teilgenommen", field: "teilgenommen",
formatter: "tickCross",
hozAlign: "center",
formatterParams: {
tickElement: '<i class="fa fa-check text-success"></i>',
crossElement: '<i class="fa fa-xmark text-danger"></i>'
}
},
{title: "ort", field: "ort", visible: false},
{title: "studienplan", field: "studienplan", visible: false},
{title: "studienplan_id", field: "studienplan_id", visible: false},
//{title: "stg", field: "studiengangkurzbzlang"},
{title: "stg_ber", field: "studiengangkurzbzlang_ber"},
{title: "Stg_Kz", field: "studiengang_kz_ber", 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.actionEditPlacementTest(cell.getData().rt_person_id, cell.getData().studiengang_kz_ber)
);
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.actionDeletePlacementTest(cell.getData().rt_person_id)
);
container.append(button);
return container;
},
frozen: true
}
],
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async () => {
await this.$p.loadCategory(['admission', 'global', 'person', 'ui', 'projektarbeitsbeurteilung']);
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('rt_id', this.$p.t('ui', 'reihungstest_id'));
setHeader('rt_person_id', this.$p.t('ui', 'reihungstest_person_id'));
setHeader('person_id', this.$p.t('person', 'person_id'));
setHeader('datum', this.$p.t('global', 'datum'));
setHeader('stufe', this.$p.t('admission', 'stufe'));
setHeader('studiensemester', this.$p.t('lehre', 'studiensemester'));
setHeader('anmerkung', this.$p.t('global', 'anmerkung'));
setHeader('anmeldedatum', this.$p.t('admission', 'anmeldedatum'));
setHeader('punkte', this.$p.t('exam', 'punkte'));
setHeader('teilgenommen', this.$p.t('admission', 'teilgenommen'));
setHeader('ort', this.$p.t('person', 'ort'));
setHeader('studienplan', this.$p.t('lehre', 'studienplan'));
setHeader('studienplan_id', this.$p.t('ui', 'studienplan_id'));
setHeader('studiengangkurzbzlang_ber', this.$p.t('projektarbeitsbeurteilung', 'studiengang'));
setHeader('studiengang_kz_ber', this.$p.t('admission', 'stg_kurz'));
}
}
];
return events;
}
},
created() {
this.$api
.call(ApiStvAdmissionDates.getListPlacementTests(this.student.prestudent_id))
.then(result => {
this.listPlacementTests = this.filteredPlacementTests = result.data;
if(result.data)
this.listPlacementTests = this.filteredPlacementTests = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
@@ -488,7 +530,7 @@ export default {
<div v-if="allowUebernahmePunkte" class="col-4">
<label class="form-label" style="color:transparent;">getPunkte</label>
<button class="btn btn-outline-secondary w-100" @click="getResultReihungstest(formData.rt_id)">{{ $p.t('admission', 'getRTErgebnis') }}</button>
<button class="btn btn-outline-secondary w-100" @click="getResultReihungstest(formData.rt_id, stgRtPers)">{{ $p.t('admission', 'getRTErgebnis') }}</button>
</div>
</div>
@@ -50,11 +50,24 @@ export default {
this.listAufnahmetermine = result.data;
const listAufnahmetermineFiltered = this.listAufnahmetermine
.filter(item => item.studiengangkurzbzlang == this.student.studiengang)
.filter(item => item.studiengang_kz_ber == this.student.studiengang_kz)
.sort((a, b) => this.parseSemester(b.studiensemester) - this.parseSemester(a.studiensemester));
const elementSemYoungest = listAufnahmetermineFiltered[0];
this.formData.rt_gesamtpunkte = elementSemYoungest.punkte;
let pointsSemStg = 0;
if(listAufnahmetermineFiltered.length > 0){
const youngestSemester = listAufnahmetermineFiltered[0].studiensemester;
//sum of all rt-points of studiengang of youngest sem
pointsSemStg = listAufnahmetermineFiltered
.filter(item => item.studiensemester === youngestSemester)
.reduce((sum, item) => sum + (Number(item.punkte) || 0), 0);
}
else
pointsSemStg = 0;
this.formData.rt_gesamtpunkte = pointsSemStg;
})
.catch(this.$fhcAlert.handleSystemError);
@@ -91,8 +104,8 @@ export default {
},
template: `
<div class="stv-details-admission-header-placement h-100 pb-3">
<h4>{{ $p.t('lehre', 'studiengang') }}</h4>
<h4>{{ $p.t('lehre', 'studiengang') }}</h4> {{student.studiengang}}
<form-form class="mt-3" ref="formRtGesamtData" @submit.prevent>
<div v-if="showAufnahmegruppen" class="row mb-3">
<div class="col-1">
@@ -164,17 +164,17 @@ export default {
this.updateStudent(this.modelValue);
},
template: `
<core-form ref="form" class="stv-details-details" @submit.prevent="save">
<core-form ref="form" class="stv-details-details mb-4" @submit.prevent="save">
<div class="position-sticky top-0 z-1">
<button type="submit" class="btn btn-primary position-absolute top-0 end-0" :disabled="!changedLength">{{$p.t('ui', 'speichern')}}</button>
</div>
<fieldset class="overflow-hidden">
<fieldset class="overflow-hidden mb-2">
<legend>Person</legend>
<template v-if="data">
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('person_id')"
container-class="col-4 stv-details-details-person_id"
container-class="col stv-details-details-person_id"
:label="$p.t('person', 'person_id')"
type="text"
v-model="data.person_id"
@@ -182,7 +182,7 @@ export default {
readonly
>
</form-input>
<div v-if="showZugangscode && !config.hiddenFields.includes('zugangscode')" class="col-4 stv-details-details-zugangscode">
<div v-if="showZugangscode && !config.hiddenFields.includes('zugangscode')" class="col stv-details-details-zugangscode">
<label>{{$p.t('global', 'zugangscode')}}</label>
<div class="align-self-center">
<span class="form-text">
@@ -192,7 +192,7 @@ export default {
</div>
<form-input
v-if="showBpk && !config.hiddenFields.includes('bpk')"
container-class="col-4 stv-details-details-bpk"
container-class="col stv-details-details-bpk"
:label="$p.t('person', 'bpk')"
type="text"
v-model="data.bpk"
@@ -200,11 +200,21 @@ export default {
maxlength="28"
>
</form-input>
<form-input
v-if="!config.hiddenFields.includes('ersatzkennzeichen')"
container-class="col stv-details-details-ersatzkennzeichen"
:label="$p.t('person', 'ersatzkennzeichen')"
type="text"
v-model="data.ersatzkennzeichen"
name="ersatzkennzeichen"
maxlength="10"
>
</form-input>
</div>
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('anrede')"
container-class="col-4 stv-details-details-anrede"
container-class="col stv-details-details-anrede"
:label="$p.t('person', 'anrede')"
type="text"
v-model="data.anrede"
@@ -214,7 +224,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('titelpre')"
container-class="col-4 stv-details-details-titelpre"
container-class="col stv-details-details-titelpre"
:label="$p.t('person', 'titelpre')"
type="text"
v-model="data.titelpre"
@@ -224,7 +234,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('titelpost')"
container-class="col-4 stv-details-details-titelpost"
container-class="col stv-details-details-titelpost"
:label="$p.t('person', 'titelpost')"
type="text"
v-model="data.titelpost"
@@ -233,10 +243,10 @@ export default {
>
</form-input>
</div>
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('nachname')"
container-class="col-4 stv-details-details-nachname"
container-class="col stv-details-details-nachname"
:label="$p.t('person', 'nachname')"
type="text"
v-model="data.nachname"
@@ -246,7 +256,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('vorname')"
container-class="col-4 stv-details-details-vorname"
container-class="col stv-details-details-vorname"
:label="$p.t('person', 'vorname')"
type="text"
v-model="data.vorname"
@@ -256,7 +266,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('vornamen')"
container-class="col-4 stv-details-details-vornamen"
container-class="col stv-details-details-vornamen"
:label="$p.t('person', 'vornamen')"
type="text"
v-model="data.vornamen"
@@ -264,11 +274,9 @@ export default {
maxlength="128"
>
</form-input>
</div>
<div class="row mb-3">
<form-input
v-if="!config.hiddenFields.includes('wahlname')"
container-class="col-4 stv-details-details-wahlname"
container-class="col stv-details-details-wahlname"
:label="$p.t('person', 'wahlname')"
type="text"
v-model="data.wahlname"
@@ -276,11 +284,12 @@ export default {
maxlength="128"
>
</form-input>
</div>
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('gebdatum')"
container-class="col-4 stv-details-details-gebdatum"
container-class="col stv-details-details-gebdatum"
:label="$p.t('person', 'geburtsdatum')"
type="DatePicker"
v-model="data.gebdatum"
@@ -297,7 +306,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('gebort')"
container-class="col-4 stv-details-details-gebort"
container-class="col stv-details-details-gebort"
:label="$p.t('person', 'geburtsort')"
type="text"
v-model="data.gebort"
@@ -307,7 +316,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('geburtsnation')"
container-class="col-4 stv-details-details-geburtsnation"
container-class="col stv-details-details-geburtsnation"
:label="$p.t('person', 'geburtsnation')"
type="select"
v-model="data.geburtsnation"
@@ -316,23 +325,9 @@ export default {
<option value="">-- {{$p.t('fehlermonitoring', 'keineAuswahl')}} --</option>
<option v-for="nation in lists.nations" :key="nation.nation_code" :value="nation.nation_code" :disabled="nation.sperre">{{nation.kurztext}}</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
v-if="!config.hiddenFields.includes('ersatzkennzeichen')"
container-class="col-4 stv-details-details-ersatzkennzeichen"
:label="$p.t('person', 'ersatzkennzeichen')"
type="text"
v-model="data.ersatzkennzeichen"
name="ersatzkennzeichen"
maxlength="10"
>
</form-input>
</div>
<div class="row mb-3">
<form-input
v-if="!config.hiddenFields.includes('staatsbuergerschaft')"
container-class="col-4 stv-details-details-staatsbuergerschaft"
container-class="col stv-details-details-staatsbuergerschaft"
:label="$p.t('person', 'staatsbuergerschaft')"
type="select"
v-model="data.staatsbuergerschaft"
@@ -341,9 +336,12 @@ export default {
<option value="">-- {{$p.t('fehlermonitoring', 'keineAuswahl')}} --</option>
<option v-for="nation in lists.nations" :key="nation.nation_code" :value="nation.nation_code" :disabled="nation.sperre">{{nation.kurztext}}</option>
</form-input>
</div>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('matr_nr')"
container-class="col-4 stv-details-details-matr_nr"
container-class="col stv-details-details-matr_nr"
:label="$p.t('person', 'matrikelnummer')"
type="text"
v-model="data.matr_nr"
@@ -353,7 +351,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('sprache')"
container-class="col-4 stv-details-details-sprache"
container-class="col stv-details-details-sprache"
:label="$p.t('person', 'sprache')"
type="select"
v-model="data.sprache"
@@ -361,11 +359,9 @@ export default {
>
<option v-for="sprache in lists.sprachen" :key="sprache.sprache" :value="sprache.sprache">{{sprache.sprache}}</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
v-if="!config.hiddenFields.includes('geschlecht')"
container-class="col-4 stv-details-details-geschlecht"
container-class="col stv-details-details-geschlecht"
:label="$p.t('person', 'geschlecht')"
type="select"
v-model="data.geschlecht"
@@ -375,7 +371,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('familienstand')"
container-class="col-4 stv-details-details-familienstand"
container-class="col stv-details-details-familienstand"
:label="$p.t('person', 'familienstand')"
type="select"
v-model="data.familienstand"
@@ -384,10 +380,10 @@ export default {
<option v-for="(bezeichnung, key) in familienstaende" :key="key" :value="key">{{bezeichnung}}</option>
</form-input>
</div>
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('foto')"
container-class="col-4 stv-details-details-foto"
container-class="col stv-details-details-foto"
:label="$p.t('person', 'foto')"
type="UploadImage"
v-model="data.foto"
@@ -397,7 +393,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('anmerkung')"
container-class="col-4 stv-details-details-anmerkung"
container-class="col stv-details-details-anmerkung"
:label="$p.t('global', 'anmerkung')"
type="textarea"
v-model="data.anmerkung"
@@ -407,7 +403,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('homepage')"
container-class="col-4 stv-details-details-homepage"
container-class="col stv-details-details-homepage"
:label="$p.t('person', 'homepage')"
type="text"
v-model="data.homepage"
@@ -424,19 +420,19 @@ export default {
v-if="!config.hideUDFs"
@load="udfsLoaded"
v-model="data"
class="row-cols-3 g-3 mb-3"
class="row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 gx-3 gy-1 mb-1"
ci-model="person/person"
:pk="{person_id:modelValue.person_id}"
>
</core-udf>
</fieldset>
<fieldset v-if="data?.student_uid" class="overflow-hidden">
<fieldset v-if="data?.student_uid" class="overflow-hidden mb-2">
<legend>{{$p.t('person', 'studentIn')}}</legend>
<template v-if="data">
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('student_uid')"
container-class="col-4 stv-details-details-student_uid"
container-class="col stv-details-details-student_uid"
:label="$p.t('person', 'uid')"
type="text"
v-model="data.student_uid"
@@ -446,7 +442,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('matrikelnr')"
container-class="col-4 stv-details-details-matrikelnr"
container-class="col stv-details-details-matrikelnr"
:label="$p.t('person', 'personenkennzeichen')"
type="text"
v-model="data.matrikelnr"
@@ -454,22 +450,31 @@ export default {
readonly
>
</form-input>
<div class="col-4 pt-4 d-flex align-items-center">
<template class="col-4 pt-4 d-flex align-items-center">
<form-input
v-if="!config.hiddenFields.includes('aktiv')"
container-class="form-check stv-details-details-aktiv"
container-class="col form-check stv-details-details-aktiv"
:label="$p.t('person', 'aktiv')"
type="checkbox"
v-model="data.aktiv"
name="aktiv"
>
</form-input>
</div>
</template>
<form-input
v-if="!config.hiddenFields.includes('alias')"
:label="$p.t('person', 'alias')"
type="text"
v-model="data.alias"
name="alias"
:disabled="aliasNotAllowed"
>
</form-input>
</div>
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('semester')"
container-class="col-4 stv-details-details-semester"
container-class="col stv-details-details-semester"
:label="$p.t('lehre', 'semester')"
type="text"
v-model="data.semester"
@@ -479,7 +484,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('verband')"
container-class="col-4 stv-details-details-verband"
container-class="col stv-details-details-verband"
:label="$p.t('lehre', 'verband')"
type="text"
v-model="data.verband"
@@ -489,7 +494,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('gruppe')"
container-class="col-4 stv-details-details-gruppe"
container-class="col stv-details-details-gruppe"
:label="$p.t('lehre', 'gruppe')"
type="text"
v-model="data.gruppe"
@@ -498,19 +503,7 @@ export default {
>
</form-input>
</div>
<div class="row mb-3">
<form-input
v-if="!config.hiddenFields.includes('alias')"
container-class="col-4 stv-details-details-alias"
:label="$p.t('person', 'alias')"
type="text"
v-model="data.alias"
name="alias"
:disabled="aliasNotAllowed"
>
</form-input>
</div>
</template>
</template>
<div v-else>
{{$p.t('ui', 'dropdownLoading')}}...
</div>
@@ -17,7 +17,14 @@ export default {
},
data(){
return {
tabulatorOptions: {
listDocuments: [],
layoutColumnsOnNewData: false,
height: 300,
}
},
computed: {
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
ApiStvDocuments.getDocumentsAccepted({
@@ -27,7 +34,13 @@ export default {
ajaxResponse: (url, params, response) => {
return response.data;
},
layout: 'fitDataStretchFrozen',
index: 'akte_id',
selectableRows: true,
selectableRowsRangeMode: 'click',
persistenceID: 'stv-details-accepted-20260217',
columns: [
{title: "akte_id", field: "akte_id", visible: false},
{title: "Dokument", field: "bezeichnung"},
{title: "Akzeptiertdatum", field: "docdatum",
formatter: function (cell) {
@@ -67,7 +80,6 @@ export default {
crossElement: '<i class="fa fa-xmark text-secondary"></i>'
}},
{title: "Infotext", field: "infotext"},
{title: "akte_id", field: "akte_id"},
{title: "dms_id", field: "dms_id", visible: false},
{title: "titel", field: "titel_intern", visible: false},
{title: "vorhanden", field: "vorhanden",
@@ -146,58 +158,40 @@ export default {
frozen: true
},
],
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 300,
selectable: true,
selectableRangeMode: 'click',
persistenceID: 'core-details-documents-accepted-2025072101',
listDocuments: [],
},
tabulatorEvents: [
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async () => {
await this.$p.loadCategory(['global', 'dokumente', 'ui', 'mobility', 'ampeln']);
let cm = this.$refs.table.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('bezeichnung').component.updateDefinition({
title: this.$p.t('global', 'dokument')
});
cm.getColumnByField('docdatum').component.updateDefinition({
title: this.$p.t('dokumente', 'datumAkzeptiert')
});
cm.getColumnByField('dokument_kurzbz').component.updateDefinition({
title: this.$p.t('mobility', 'kurzbz')
});
cm.getColumnByField('insertvonma').component.updateDefinition({
title: this.$p.t('dokumente', 'akzeptiertVon')
});
cm.getColumnByField('hochgeladenamum').component.updateDefinition({
title: this.$p.t('global', 'uploaddatum')
});
cm.getColumnByField('nachgereicht').component.updateDefinition({
title: this.$p.t('dokumente', 'nachgereicht')
});
cm.getColumnByField('vorhanden').component.updateDefinition({
title: this.$p.t('dokumente', 'vorhanden')
});
cm.getColumnByField('dms_id').component.updateDefinition({
title: this.$p.t('global', 'dms_id')
});
cm.getColumnByField('titel_intern').component.updateDefinition({
title: this.$p.t('global', 'titel')
});
cm.getColumnByField('anmerkung_intern').component.updateDefinition({
title: this.$p.t('global', 'anmerkung')
});
cm.getColumnByField('akte_id').component.updateDefinition({
title: this.$p.t('global', 'akte_id')
});
cm.getColumnByField('nachgereicht_am').component.updateDefinition({
title: this.$p.t('dokumente', 'nachreichungAm')
});
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('bezeichnung', this.$p.t('global', 'dokument'));
setHeader('docdatum', this.$p.t('dokumente', 'datumAkzeptiert'));
setHeader('dokument_kurzbz', this.$p.t('mobility', 'kurzbz'));
setHeader('insertvonma', this.$p.t('dokumente', 'akzeptiertVon'));
setHeader('hochgeladenamum', this.$p.t('global', 'uploaddatum'));
setHeader('nachgereicht', this.$p.t('dokumente', 'nachgereicht'));
setHeader('vorhanden', this.$p.t('dokumente', 'vorhanden'));
setHeader('dms_id', this.$p.t('global', 'dms_id'));
setHeader('titel_intern', this.$p.t('global', 'titel'));
setHeader('anmerkung_intern', this.$p.t('global', 'anmerkung'));
setHeader('akte_id', this.$p.t('global', 'akte_id'));
setHeader('nachgereicht_am', this.$p.t('dokumente', 'nachreichungAm'));
}
},
{
@@ -210,14 +204,15 @@ export default {
}
}
}
]
}
];
return events;
},
},
methods: {
actionDownloadFile(akte_id){
return FHC_JS_DATA_STORAGE_OBJECT.app_root
+ FHC_JS_DATA_STORAGE_OBJECT.ci_router
+ '/api/frontend/v1/stv/dokumente/download?akte_id='
+ FHC_JS_DATA_STORAGE_OBJECT.ci_router
+ '/api/frontend/v1/stv/dokumente/download?akte_id='
+ encodeURIComponent(akte_id);
},
actionUploadFile(dokument_kurzbz){
@@ -314,7 +309,7 @@ export default {
}
},
template: `
<div class="stv-details-documents h-100 pb-3">
<div class="stv-details-aceepted h-100 pb-3">
<h5>{{$p.t('dokumente', 'accepted')}}</h5>
<modal-edit
@@ -17,7 +17,15 @@ export default {
},
data(){
return {
tabulatorOptions: {
layoutColumnsOnNewData: false,
height: 300,
listDocuments: [],
prestudentDocumentData: [],
}
},
computed: {
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
ApiStvDocuments.getDocumentsUnaccepted({
@@ -25,6 +33,10 @@ export default {
studiengang_kz: this.studiengang_kz})
),
ajaxResponse: (url, params, response) => response.data,
layout: 'fitDataStretchFrozen',
persistenceID: 'stv-details-unaccepted-20260217',
selectableRows: true,
selectableRowsRangeMode: 'click',
columns: [
{title: "Dokument", field: "bezeichnung"},
{title: "Kurzbz", field: "dokument_kurzbz", visible: false},
@@ -42,7 +54,7 @@ export default {
hour12: false
});
}
},
},
{title: "nachgereicht", field: "nachgereicht", visible: false,
formatter:"tickCross",
hozAlign:"center",
@@ -76,7 +88,7 @@ export default {
return `${tickCrossIcon} ${pill}`;
},
hozAlign: "center"
},
},
{title: "Infotext", field: "infotext"},
{title: "akte_id", field: "akte_id"},
{title: "titel_intern", field: "titel_intern"},
@@ -162,53 +174,39 @@ export default {
frozen: true
},
],
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 300,
selectable: true,
selectableRangeMode: 'click',
persistenceID: 'core-details-documents-unaccepted',
listDocuments: [],
prestudentDocumentData: [],
},
tabulatorEvents: [
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async () => {
await this.$p.loadCategory(['global', 'dokumente', 'ui', 'mobility', 'ampeln']);
let cm = this.$refs.table.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('bezeichnung').component.updateDefinition({
title: this.$p.t('global', 'dokument')
});
cm.getColumnByField('dokument_kurzbz').component.updateDefinition({
title: this.$p.t('mobility', 'kurzbz')
});
cm.getColumnByField('hochgeladenamum').component.updateDefinition({
title: this.$p.t('global', 'uploaddatum')
});
cm.getColumnByField('nachgereicht').component.updateDefinition({
title: this.$p.t('dokumente', 'nachgereicht')
});
cm.getColumnByField('vorhanden').component.updateDefinition({
title: this.$p.t('dokumente', 'vorhanden')
});
cm.getColumnByField('titel_intern').component.updateDefinition({
title: this.$p.t('global', 'titel')
});
cm.getColumnByField('anmerkung_intern').component.updateDefinition({
title: this.$p.t('global', 'anmerkung')
});
cm.getColumnByField('akte_id').component.updateDefinition({
title: this.$p.t('global', 'akte_id')
});
cm.getColumnByField('pflicht').component.updateDefinition({
title: this.$p.t('ampeln', 'mandatory')
});
cm.getColumnByField('nachgereicht_am').component.updateDefinition({
title: this.$p.t('global', 'dokument')
});
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('bezeichnung', this.$p.t('global', 'dokument'));
setHeader('dokument_kurzbz', this.$p.t('mobility', 'kurzbz'));
setHeader('hochgeladenamum', this.$p.t('global', 'uploaddatum'));
setHeader('nachgereicht', this.$p.t('dokumente', 'nachgereicht'));
setHeader('vorhanden', this.$p.t('dokumente', 'vorhanden'));
setHeader('titel_intern', this.$p.t('global', 'titel'));
setHeader('anmerkung_intern', this.$p.t('global', 'anmerkung'));
setHeader('akte_id', this.$p.t('global', 'akte_id'));
setHeader('pflicht', this.$p.t('ampeln', 'mandatory'));
setHeader('nachgereicht_am', this.$p.t('global', 'dokument'));
}
},
{
@@ -221,14 +219,15 @@ export default {
}
}
}
]
}
];
return events;
},
},
methods: {
actionDownloadFile(akte_id){
return FHC_JS_DATA_STORAGE_OBJECT.app_root
+ FHC_JS_DATA_STORAGE_OBJECT.ci_router
+ '/api/frontend/v1/stv/dokumente/download?akte_id='
+ FHC_JS_DATA_STORAGE_OBJECT.ci_router
+ '/api/frontend/v1/stv/dokumente/download?akte_id='
+ encodeURIComponent(akte_id);
},
actionUploadFile(dokument_kurzbz){
@@ -277,9 +276,9 @@ export default {
dokument_kurzbz: e.dokument_kurzbz
}))
.then(() => ({
success: true,
dokument_bz: e.bezeichnung
}))
success: true,
dokument_bz: e.bezeichnung
}))
.catch(() => ({
success: false,
dokument_bz: e.bezeichnung
@@ -287,23 +286,23 @@ export default {
)
)
.then(results => {
const failed = results.filter(res => !res.value.success);
const suceeded = results.filter(res => res.value.success);
if (failed.length > 0) {
failed.forEach(res => {
this.$fhcAlert.alertError(this.$p.t('dokumente', 'errorAccepted',
{'dokument_kurzbz': res.value.dokument_bz}
));
});
let countSuceeded = suceeded.length;
if(countSuceeded > 0)
this.$fhcAlert.alertSuccess(this.$p.t('dokumente', 'successCountAccepted',
{'count': countSuceeded}));
const failed = results.filter(res => !res.value.success);
const suceeded = results.filter(res => res.value.success);
if (failed.length > 0) {
failed.forEach(res => {
this.$fhcAlert.alertError(this.$p.t('dokumente', 'errorAccepted',
{'dokument_kurzbz': res.value.dokument_bz}
));
});
let countSuceeded = suceeded.length;
if(countSuceeded > 0)
this.$fhcAlert.alertSuccess(this.$p.t('dokumente', 'successCountAccepted',
{'count': countSuceeded}));
} else {
this.$fhcAlert.alertSuccess(this.$p.t('dokumente', 'successAccepted'));
}
this.reloadAll();
} else {
this.$fhcAlert.alertSuccess(this.$p.t('dokumente', 'successAccepted'));
}
this.reloadAll();
});
},
deleteFile(akte_id){
@@ -24,6 +24,7 @@ export default {
}
},
methods:{
open(prestudent_id, dokument_kurzbz){
this.formData.dokument_kurzbz = dokument_kurzbz;
this.formData.prestudent_id = prestudent_id;
@@ -111,7 +111,7 @@ export default {
],
height: 'auto',
index: 'group_id',
persistenceID: 'stv-details-group-list'
persistenceID: 'stv-details-group-list-20260217'
};
}
},
@@ -24,12 +24,30 @@ export default {
},
data(){
return {
tabulatorOptions: {
formData: {
mobilitaetstyp_kurzbz: "GS",
studiensemester_kurzbz: this.currentSemester,
status_kurzbz: 'Student',
mobilitaet_id: null
},
statusNew: true,
listTypenMobility: [],
listStudiensemester: [],
programsMobility: [],
listStudienprogramme: [],
listPartner: [],
statiPrestudent: [],
}
},
computed: {
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
ApiStvJointstudies.getStudies(this.student.prestudent_id)
),
ajaxResponse: (url, params, response) => response.data,
persistenceID: 'stv-details-jointstudies-20260217',
columns: [
{title: "mobilitaet_id", field: "mobilitaet_id", visible: false},
{title: "StSem", field: "studiensemester_kurzbz"},
@@ -120,70 +138,43 @@ export default {
frozen: true
},
],
},
tabulatorEvents: [
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['global', 'jointstudies', 'ui', 'lehre', 'mobility']);
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
let cm = this.$refs.table.tabulator.columnManager;
const el = col.getElement();
if (!el || !el.querySelector) return;
cm.getColumnByField('mobilitaet_id').component.updateDefinition({
title: this.$p.t('global', 'mobilitaet_id')
});
/* cm.getColumnByField('studiensemester_kurzbz').component.updateDefinition({
title: this.$p.t('lehre', 'studiensemester')
});*/
cm.getColumnByField('gsprogrammtyp_kurzbz').component.updateDefinition({
title: this.$p.t('global', 'typ')
});
/* cm.getColumnByField('ausbildungssemester').component.updateDefinition({
title: this.$p.t('lehre', 'semester')
});*/
cm.getColumnByField('prestudent_id').component.updateDefinition({
title: this.$p.t('global', 'prestudentID')
});
cm.getColumnByField('kurzbz').component.updateDefinition({
title: this.$p.t('mobility', 'mobilitaetsprogramm')
});
cm.getColumnByField('studienprogramm').component.updateDefinition({
title: this.$p.t('jointstudies', 'studienprogramm')
});
cm.getColumnByField('insertamum').component.updateDefinition({
title: this.$p.t('global', 'insertamum')
});
cm.getColumnByField('insertvon').component.updateDefinition({
title: this.$p.t('global', 'insertvon')
});
cm.getColumnByField('updateamum').component.updateDefinition({
title: this.$p.t('global', 'updateamum')
});
cm.getColumnByField('updatevon').component.updateDefinition({
title: this.$p.t('global', 'updatevon')
});
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
/* cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'aktionen')
});*/
setHeader('mobilitaet_id', this.$p.t('global', 'mobilitaet_id'));
setHeader('gsprogrammtyp_kurzbz', this.$p.t('global', 'typ'));
setHeader('prestudent_id', this.$p.t('global', 'prestudentID'));
setHeader('kurzbz', this.$p.t('mobility', 'mobilitaetsprogramm'));
setHeader('studienprogramm', this.$p.t('jointstudies', 'studienprogramm'));
setHeader('insertamum', this.$p.t('global', 'insertamum'));
setHeader('insertvon', this.$p.t('global', 'insertvon'));
setHeader('updateamum', this.$p.t('global', 'updateamum'));
setHeader('updatevon', this.$p.t('global', 'updatevon'));
}
}
],
formData: {
mobilitaetstyp_kurzbz: "GS",
studiensemester_kurzbz: this.currentSemester,
status_kurzbz: 'Student',
mobilitaet_id: null
},
statusNew: true,
listTypenMobility: [],
listStudiensemester: [],
programsMobility: [],
listStudienprogramme: [],
listPartner: [],
statiPrestudent: [],
}
];
return events;
},
},
methods: {
actionDeleteJointStudy(mobilitaet_id) {
@@ -6,7 +6,6 @@ import FormForm from '../../../../Form/Form.js';
import FormInput from '../../../../Form/Input.js';
import ApiStvAddress from '../../../../../api/factory/stv/kontakt/address.js';
import ApiStvCompany from '../../../../../api/factory/stv/kontakt/company.js';
export default{
name: 'AddressComponent',
@@ -23,11 +22,39 @@ export default{
},
data() {
return {
tabulatorOptions: {
addressData: {
zustelladresse: true,
heimatadresse: true,
rechnungsadresse: false,
typ: 'h',
nation: 'A',
address: {plz: null},
plz: null,
},
statusNew: true,
places: [],
suggestions: {},
nations: [],
adressentypen: [],
firmen: [],
listFirmen: [],
filteredFirmen: [],
selectedFirma: null,
abortController: {
suggestions: null,
places: null
},
}
},
computed:{
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(ApiStvAddress.get(this.uid)),
ajaxResponse: (url, params, response) => response.data,
//autoColumns: true,
index: 'adresse_id',
persistenceID: 'stv-details-kontakt-address-20260217',
columns:[
{title:"Typ", field:"bezeichnung"},
{title:"Strasse", field:"strasse"},
@@ -122,99 +149,52 @@ export default{
frozen: true
},
],
height: 'auto',
index: 'adresse_id',
persistenceID: 'stv-details-kontakt-address'
},
tabulatorEvents: [
height: 'auto'
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async () => {
handler: async() => {
await this.$p.loadCategory(['notiz', 'global', 'person', 'ui']);
let cm = this.$refs.table.tabulator.columnManager;
cm.getColumnByField('bezeichnung').component.updateDefinition({
title: this.$p.t('global', 'typ')
});
cm.getColumnByField('strasse').component.updateDefinition({
title: this.$p.t('person', 'strasse')
});
cm.getColumnByField('plz').component.updateDefinition({
title: this.$p.t('person', 'plz')
});
cm.getColumnByField('ort').component.updateDefinition({
title: this.$p.t('person', 'ort')
});
cm.getColumnByField('gemeinde').component.updateDefinition({
title: this.$p.t('person', 'gemeinde')
});
cm.getColumnByField('nation').component.updateDefinition({
title: this.$p.t('person', 'nation')
});
cm.getColumnByField('heimatadresse').component.updateDefinition({
title: this.$p.t('person', 'heimatadresse')
});
cm.getColumnByField('zustelladresse').component.updateDefinition({
title: this.$p.t('person', 'zustelladresse')
});
cm.getColumnByField('co_name').component.updateDefinition({
title: this.$p.t('person', 'co_name')
});
cm.getColumnByField('name').component.updateDefinition({
title: this.$p.t('person', 'firma_zusatz')
});
cm.getColumnByField('firmenname').component.updateDefinition({
title: this.$p.t('person', 'firma')
});
cm.getColumnByField('lastupdate').component.updateDefinition({
title: this.$p.t('notiz', 'letzte_aenderung')
});
cm.getColumnByField('rechnungsadresse').component.updateDefinition({
title: this.$p.t('person', 'rechnungsadresse')
});
cm.getColumnByField('anmerkung').component.updateDefinition({
title: this.$p.t('global', 'anmerkung')
});
cm.getColumnByField('firma_id').component.updateDefinition({
title: this.$p.t('ui', 'firma_id')
});
cm.getColumnByField('adresse_id').component.updateDefinition({
title: this.$p.t('ui', 'adresse_id')
});
cm.getColumnByField('person_id').component.updateDefinition({
title: this.$p.t('person', 'person_id')
});
/* cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'aktionen')
});*/
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('bezeichnung', this.$p.t('global', 'typ'));
setHeader('strasse', this.$p.t('person', 'strasse'));
setHeader('plz', this.$p.t('person', 'plz'));
setHeader('ort', this.$p.t('person', 'ort'));
setHeader('gemeinde', this.$p.t('person', 'gemeinde'));
setHeader('nation', this.$p.t('person', 'nation'));
setHeader('heimatadresse', this.$p.t('person', 'heimatadresse'));
setHeader('zustelladresse', this.$p.t('person', 'zustelladresse'));
setHeader('co_name', this.$p.t('person', 'co_name'));
setHeader('name', this.$p.t('person', 'firma_zusatz'));
setHeader('firmenname', this.$p.t('person', 'firma'));
setHeader('lastupdate', this.$p.t('notiz', 'letzte_aenderung'));
setHeader('rechnungsadresse', this.$p.t('person', 'rechnungsadresse'));
setHeader('anmerkung', this.$p.t('global', 'anmerkung'));
setHeader('firma_id', this.$p.t('ui', 'firma_id'));
setHeader('adresse_id', this.$p.t('ui', 'adresse_id'));
setHeader('person_id', this.$p.t('person', 'person_id'));
}
}
],
addressData: {
zustelladresse: true,
heimatadresse: true,
rechnungsadresse: false,
typ: 'h',
nation: 'A',
address: {plz: null},
plz: null,
},
statusNew: true,
places: [],
suggestions: {},
nations: [],
adressentypen: [],
firmen: [],
listFirmen: [],
filteredFirmen: [],
selectedFirma: null,
abortController: {
suggestions: null,
places: null
},
}
},
computed:{
];
return events;
},
orte() {
return this.places.filter(ort => ort.name == this.addressData.gemeinde);
},
@@ -17,11 +17,23 @@ export default{
uid: Number
},
data() {
return{
tabulatorOptions: {
return {
lastSelected: null,
bankverbindungData: {
verrechnung: true,
typ: 'p'
},
statusNew: true,
}
},
computed: {
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(ApiStvBankaccount.get(this.uid)),
ajaxResponse: (url, params, response) => response.data,
index: 'bankverbindung_id',
persistenceID: 'stv-details-kontakt-bankaccount-20260217',
columns:[
{title:"Name", field:"name"},
{title:"Anschrift", field:"anschrift", visible:false},
@@ -86,56 +98,41 @@ export default{
frozen: true
},
],
height: 'auto',
index: 'bankverbindung_id',
persistenceID: 'stv-details-kontakt-bankaccount'
},
tabulatorEvents: [
height: 'auto'
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['global','person']);
let cm = this.$refs.table.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('name').component.updateDefinition({
title: this.$p.t('global', 'name')
});
const el = col.getElement();
if (!el || !el.querySelector) return;
cm.getColumnByField('typ').component.updateDefinition({
title: this.$p.t('global', 'typ')
});
cm.getColumnByField('anschrift').component.updateDefinition({
title: this.$p.t('person', 'anschrift')
});
cm.getColumnByField('kontonr').component.updateDefinition({
title: this.$p.t('person', 'kontonr')
});
cm.getColumnByField('blz').component.updateDefinition({
title: this.$p.t('person', 'blz')
});
cm.getColumnByField('verrechnung').component.updateDefinition({
title: this.$p.t('person', 'verrechnung')
});
cm.getColumnByField('person_id').component.updateDefinition({
title: this.$p.t('person', 'person_id')
});
cm.getColumnByField('bankverbindung_id').component.updateDefinition({
title: this.$p.t('ui', 'bankverbindung_id')
});
/* cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'aktionen')
});*/
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('name', this.$p.t('global','name'));
setHeader('typ', this.$p.t('global','typ'));
setHeader('anschrift', this.$p.t('person','anschrift'));
setHeader('kontonr', this.$p.t('person','kontonr'));
setHeader('blz', this.$p.t('person','blz'));
setHeader('verrechnung', this.$p.t('person','verrechnung'));
}
}
],
lastSelected: null,
bankverbindungData: {
verrechnung: true,
typ: 'p'
},
statusNew: true
}
];
return events;
},
},
watch: {
uid() {
@@ -7,7 +7,7 @@ import FormInput from '../../../../Form/Input.js';
import ApiStvContact from '../../../../../api/factory/stv/kontakt/contact.js';
import ApiStvCompany from '../../../../../api/factory/stv/kontakt/company.js';
export default{
export default {
name: 'ContactComponent',
components: {
CoreFilterCmpt,
@@ -20,11 +20,46 @@ export default{
uid: Number
},
data() {
return{
tabulatorOptions: {
return {
lastSelected: null,
contactData: {
zustellung: true,
kontakttyp: 'email',
firma_id: null
},
statusNew: true,
kontakttypen: [],
firmen: [],
filteredFirmen: [],
filteredOrte: null,
abortController: {
firmen: null,
standorte: null
},
lastSelected: null,
contactData: {
zustellung: true,
kontakttyp: 'email',
firma_id: null
},
statusNew: true,
kontakttypen: [],
firmen: [],
filteredFirmen: [],
filteredOrte: null,
abortController: {
firmen: null,
standorte: null
},
}
},
computed: {
tabulatorOptions(){
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(ApiStvContact.get(this.uid)),
ajaxResponse: (url, params, response) => response.data,
persistenceID: 'stv-details-kontakt-contact-20260217',
columns:[
{title:"Typ", field:"kontakttypbeschreibung"},
{title:"Kontakt", field:"kontakt"},
@@ -94,72 +129,46 @@ export default{
frozen: true
},
],
height: 'auto',
index: 'kontakt_id',
persistenceID: 'stv-details-kontakt-contact'
},
tabulatorEvents: [
height: 'auto'
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['notiz','global','person']);
let cm = this.$refs.table.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('kontakttypbeschreibung').component.updateDefinition({
title: this.$p.t('global', 'typ')
});
cm.getColumnByField('kontakt').component.updateDefinition({
title: this.$p.t('global', 'kontakt')
});
cm.getColumnByField('zustellung').component.updateDefinition({
title: this.$p.t('person', 'zustellung')
});
cm.getColumnByField('anmerkung').component.updateDefinition({
title: this.$p.t('global', 'anmerkung')
});
cm.getColumnByField('lastupdate').component.updateDefinition({
title: this.$p.t('notiz', 'letzte_aenderung')
});
cm.getColumnByField('name').component.updateDefinition({
title: this.$p.t('person', 'firma')
});
cm.getColumnByField('bezeichnung').component.updateDefinition({
title: this.$p.t('person', 'standort')
});
cm.getColumnByField('firma_id').component.updateDefinition({
title: this.$p.t('ui', 'firma_id')
});
cm.getColumnByField('kontakt_id').component.updateDefinition({
title: this.$p.t('ui', 'kontakt_id')
});
cm.getColumnByField('person_id').component.updateDefinition({
title: this.$p.t('person', 'person_id')
});
cm.getColumnByField('standort_id').component.updateDefinition({
title: this.$p.t('ui', 'standort_id')
});
/* cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'aktionen')
});*/
}}
],
lastSelected: null,
contactData: {
zustellung: true,
kontakttyp: 'email',
firma_id: null
},
statusNew: true,
kontakttypen: [],
firmen: [],
filteredFirmen: [],
filteredOrte: null,
abortController: {
firmen: null,
standorte: null
},
}
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('kontakttypbeschreibung', this.$p.t('global', 'typ'));
setHeader('kontakt', this.$p.t('global', 'kontakt'));
setHeader('zustellung', this.$p.t('person', 'zustellung'));
setHeader('anmerkung', this.$p.t('global', 'anmerkung'));
setHeader('lastupdate', this.$p.t('notiz', 'letzte_aenderung'));
setHeader('name', this.$p.t('person', 'firma'));
setHeader('bezeichnung', this.$p.t('person', 'standort'));
setHeader('firma_id', this.$p.t('ui', 'firma_id'));
setHeader('kontakt_id', this.$p.t('ui', 'kontakt_id'));
setHeader('person_id', this.$p.t('person', 'person_id'));
setHeader('standort_id', this.$p.t('ui', 'standort_id'));
}
}
];
return events;
},
},
watch: {
uid() {
@@ -26,7 +26,7 @@ export default {
internMail(event) {
if (this.internMails.length)
{
splitMailsHelper(this.privateMails, event, null, this.$fhcAlert, this.$p)
splitMailsHelper(this.internMails, event, null, this.$fhcAlert, this.$p)
}
},
privateMail(event) {
@@ -127,10 +127,10 @@ export default {
ajaxResponse: (url, params, response) => response.data,
dataTree: true,
columns: this.tabulatorColumns,
selectable: true,
selectableRangeMode: 'click',
selectableRows: true,
selectableRowsRangeMode: 'click',
index: 'buchungsnr',
persistenceID: 'stv-details-konto'
persistenceID: 'stv-details-konto-20260217'
};
},
},
@@ -11,22 +11,6 @@ export default {
FormInput,
FormForm
},
computed: {
downloadLink: function(){
if(!this.dataSem.start || !this.dataSem.ende || !this.student.uid) return;
let start = new Date(this.dataSem.start);
start = Math.floor(start.getTime()/1000);
let ende = new Date(this.dataSem.ende);
ende = Math.floor(ende.getTime() / 1000);
let link =
FHC_JS_DATA_STORAGE_OBJECT.app_root + "cis/private/lvplan/stpl_kalender.php?type=student&pers_uid=" + this.student.uid + "&begin=" + start + "&ende= " +ende + "&format=excel";
return link;
},
dbStundenplanTable: function (){
return this.showStundenplanDev ? 'stundenplandev' : 'stundenplan';
}
},
inject: {
currentSemester: {
from: 'currentSemester',
@@ -44,6 +28,22 @@ export default {
showStundenplanDev: false
};
},
computed: {
downloadLink: function(){
if(!this.dataSem.start || !this.dataSem.ende || !this.student.uid) return;
let start = new Date(this.dataSem.start);
start = Math.floor(start.getTime()/1000);
let ende = new Date(this.dataSem.ende);
ende = Math.floor(ende.getTime() / 1000);
let link =
FHC_JS_DATA_STORAGE_OBJECT.app_root + "cis/private/lvplan/stpl_kalender.php?type=student&pers_uid=" + this.student.uid + "&begin=" + start + "&ende= " +ende + "&format=excel";
return link;
},
dbStundenplanTable: function (){
return this.showStundenplanDev ? 'stundenplandev' : 'stundenplan';
},
},
methods: {
initTabulatorOptions(){
this.tabulatorOptions = {
@@ -59,6 +59,7 @@ export default {
ajaxResponse: (url, params, response) => {
return response.data;
},
persistenceID: 'stv-details-lvtermine-20260217',
columns: [
{title: "lv_id", field: "lehrveranstaltung_id", visible: false},
{title: "lehreinheit_id", field: "lehreinheit_id", visible: false},
@@ -80,7 +81,7 @@ export default {
{title: "farbe", field: "farbe", visible: false},
{title: "Gruppen", field: "gruppen_kuerzel"},
{title: "ort", field: "ort_kurzbz"},
{title: "lektorIn", field: "lektorname"},
{title: "lektorIn", field: "lektorname", sorter: "string"},
{title: "Lehrfach", field: "lehrfach_bez"}
],
rowFormatter: function(row){
@@ -100,42 +101,35 @@ export default {
handler: async() => {
await this.$p.loadCategory(['global', 'ui', 'lehre']);
let cm = this.$refs.table.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('lehrveranstaltung_id').component.updateDefinition({
title: this.$p.t('lehre', 'lehrveranstaltung_id')
});
cm.getColumnByField('lehreinheit_id').component.updateDefinition({
title: this.$p.t('global', 'lehreinheit_id')
});
cm.getColumnByField('datum').component.updateDefinition({
title: this.$p.t('global', 'datum')
});
cm.getColumnByField('beginn').component.updateDefinition({
title: this.$p.t('ui', 'dateFrom')
});
cm.getColumnByField('ende').component.updateDefinition({
title: this.$p.t('ui', 'dateTo')
});
cm.getColumnByField('gruppen_kuerzel').component.updateDefinition({
title: this.$p.t('global', 'gruppen')
});
cm.getColumnByField('ort_kurzbz').component.updateDefinition({
title: this.$p.t('global', 'ortLocation')
});
cm.getColumnByField('lektorname').component.updateDefinition({
title: this.$p.t('lehre', 'lektor')
});
cm.getColumnByField('lehrfach_bez').component.updateDefinition({
title: this.$p.t('global', 'lehrfach')
});
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('lehrveranstaltung_id', this.$p.t('lehre', 'lehrveranstaltung_id'));
setHeader('lehreinheit_id', this.$p.t('global', 'lehreinheit_id'));
setHeader('datum', this.$p.t('global', 'datum'));
setHeader('beginn', this.$p.t('ui', 'dateFrom'));
setHeader('ende', this.$p.t('ui', 'dateTo'));
setHeader('gruppen_kuerzel', this.$p.t('global', 'gruppen'));
setHeader('ort_kurzbz', this.$p.t('global', 'ortLocation'));
setHeader('lektorname', this.$p.t('lehre', 'lektor'));
setHeader('lehrfach_bez', this.$p.t('global', 'lehrfach'));
}
}
];
},
getDatesOfSemester(studiensemester_kurzbz) {
this.dataSem = this.listStudiensemester.find(item => item.studiensemester_kurzbz === studiensemester_kurzbz);
},
},
exportToExcel(){
window.open(this.downloadLink, '_blank');
},
@@ -74,7 +74,7 @@ export default {
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 200,
persistenceID: 'core-mobility-purpose-2025112401'
persistenceID: 'core-mobility-purpose-20260217'
},
tabulatorEvents: [
{
@@ -83,17 +83,22 @@ export default {
await this.$p.loadCategory(['ui', 'global', 'mobility']);
let cm = this.$refs.table.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('bezeichnung').component.updateDefinition({
title: this.$p.t('ui', 'bezeichnung')
});
cm.getColumnByField('kurzbz').component.updateDefinition({
title: this.$p.t('mobility', 'kurzbz')
});
/* cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'aktionen')
});*/
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('bezeichnung', this.$p.t('ui', 'bezeichnung'));
setHeader('kurzbz', this.$p.t('mobility', 'kurzbz'));
//setHeader('actions', this.$p.t('global', 'aktionen'));
}
}
],
@@ -72,7 +72,7 @@ export default {
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 200,
persistenceID: 'core-mobility-support-2025112401'
persistenceID: 'core-mobility-support-20260217'
},
tabulatorEvents: [
{
@@ -81,14 +81,19 @@ export default {
await this.$p.loadCategory(['ui', 'global', 'mobility']);
let cm = this.$refs.table.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('bezeichnung').component.updateDefinition({
title: this.$p.t('ui', 'bezeichnung')
});
/* cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'aktionen')
});*/
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('bezeichnung', this.$p.t('ui', 'bezeichnung'));
}
}
],
@@ -37,12 +37,49 @@ export default {
},
data() {
return {
tabulatorOptions: {
formData: {
von: new Date(),
bis: new Date(),
mobilitaetsprogramm_code: 7,
nation_code: 'A',
herkunftsland_code: 'A',
bisio_id: null,
localPurposes: [],
localSupports: [],
lehrveranstaltung_id: null,
lehreinheit_id: null
},
statusNew: true,
programsMobility: [],
listLvs: [],
listLes: [],
listLvsAndLes: [],
listPurposes: [],
listSupports: [],
tabulatorData: [],
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: 200,
}
},
watch: {
student(){
if (this.$refs.table) {
this.$refs.table.reloadTable();
}
},
},
computed:{
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
ApiStvMobility.getMobilitaeten(this.student.uid)
),
ajaxResponse: (url, params, response) => response.data,
index: 'bisio_id',
persistenceID: 'stv-details-table_mobility-20260217',
columns: [
{title: "Kurzbz", field: "kurzbz"},
{title: "Nation", field: "nation_code"},
@@ -63,8 +100,8 @@ export default {
},
{
title: "Bis",
field: "bis",
formatter: function (cell) {
field: "bis",
formatter: function (cell) {
const dateStr = cell.getValue();
if (!dateStr) return "";
@@ -109,81 +146,49 @@ export default {
frozen: true
},
],
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: 200,
index: 'bisio_id',
persistenceID: 'stv-details-table_mobiliy-2025112401'
},
tabulatorEvents: [
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'dataLoaded',
handler: data => this.tabulatorData = data.map(item => {
// item.actionDiv = document.createElement('div');
// item.actionDiv = document.createElement('div');
return item;
}),
},
{
event: 'tableBuilt',
handler: async() => {
if (!this.$refs.table) return;
await this.$p.loadCategory(['global', 'person', 'stv', 'mobility', 'ui']);
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
let cm = this.$refs.table.tabulator.columnManager;
const el = col.getElement();
if (!el || !el.querySelector) return;
cm.getColumnByField('kurzbz').component.updateDefinition({
title: this.$p.t('mobility', 'kurzbz_program')
});
cm.getColumnByField('nation_code').component.updateDefinition({
title: this.$p.t('mobility', 'gastnation')
});
cm.getColumnByField('von').component.updateDefinition({
title: this.$p.t('ui', 'von')
});
cm.getColumnByField('bis').component.updateDefinition({
title: this.$p.t('global', 'bis')
});
cm.getColumnByField('bisio_id').component.updateDefinition({
title: this.$p.t('mobility', 'bisio_id')
});
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
/* cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'aktionen')
});*/
setHeader('kurzbz', this.$p.t('mobility', 'kurzbz_program'));
setHeader('nation_code', this.$p.t('mobility', 'gastnation'));
setHeader('von', this.$p.t('ui', 'von'));
setHeader('bis', this.$p.t('global', 'bis'));
setHeader('bisio_id', this.$p.t('mobility', 'bisio_id'));
}
}
],
formData: {
von: new Date(),
bis: new Date(),
mobilitaetsprogramm_code: 7,
nation_code: 'A',
herkunftsland_code: 'A',
bisio_id: null,
localPurposes: [],
localSupports: [],
lehrveranstaltung_id: null,
lehreinheit_id: null
},
statusNew: true,
programsMobility: [],
listLvs: [],
listLes: [],
listLvsAndLes: [],
listPurposes: [],
listSupports: [],
tabulatorData: []
}
},
watch: {
student(){
if (this.$refs.table) {
this.$refs.table.reloadTable();
}
];
return events;
},
},
computed:{
lv_teile(){
return this.listLvsAndLes.filter(lv => lv.lehreinheit_id == this.formData.lehreinheit_id);
},
@@ -39,8 +39,38 @@ export default {
{ field: 'lv_bezeichnung', title: this.$p.t('lehre/lehrveranstaltung') },
{ field: 'note_bezeichnung', title: this.$p.t('lehre/note') },
{ field: 'insertvon', title: this.$p.t('profil/mitarbeiterIn'), visible: false },
{ field: 'benotungsdatum', title: this.$p.t('stv/grades_gradingdate'), visible: false },
{ field: 'freigabedatum', title: this.$p.t('stv/grades_approvaldate'), visible: false },
{ field: 'benotungsdatum', title: this.$p.t('stv/grades_gradingdate'), visible: false,
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",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
});
}},
{ field: 'freigabedatum', title: this.$p.t('stv/grades_approvaldate'), visible: false,
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",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
});
}},
{ field: 'studiensemester_kurzbz', title: this.$p.t('lehre/studiensemester'), visible: false },
{ field: 'stg_bezeichnung', title: this.$p.t('lehre/studiengang'), visible: false },
{ field: 'note', title: this.$p.t('stv/grades_numericgrade'), visible: false },
@@ -49,9 +79,9 @@ export default {
],
layout: 'fitDataStretch',
height: '100%',
selectable: true,
selectableRangeMode: 'click',
persistenceID: 'stv-details-noten-repeater'
selectableRows: true,
selectableRowsRangeMode: 'click',
persistenceID: 'stv-details-noten-repeater-20260217'
};
}
},
@@ -39,8 +39,38 @@ export default {
{ field: 'lehrveranstaltung_bezeichnung', title: this.$p.t('lehre/lehrveranstaltung') },
{ field: 'note_bezeichnung', title: this.$p.t('lehre/note') },
{ field: 'mitarbeiter_uid', title: this.$p.t('profil/mitarbeiterIn'), visible: false },
{ field: 'benotungsdatum', title: this.$p.t('stv/grades_gradingdate'), visible: false },
{ field: 'freigabedatum', title: this.$p.t('stv/grades_approvaldate'), visible: false },
{ field: 'benotungsdatum', title: this.$p.t('stv/grades_gradingdate'), visible: false,
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",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
});
}},
{ field: 'freigabedatum', title: this.$p.t('stv/grades_approvaldate'), visible: false,
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",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
});
}},
{ field: 'studiensemester_kurzbz', title: this.$p.t('lehre/studiensemester'), visible: false },
{ field: 'note', title: this.$p.t('stv/grades_numericgrade'), visible: false },
{ field: 'student_uid', title: this.$p.t('profil/studentIn'), visible: false },
@@ -49,9 +79,9 @@ export default {
],
layout: 'fitDataStretch',
height: '100%',
selectable: true,
selectableRangeMode: 'click',
persistenceID: 'stv-details-noten-teacher'
selectableRows: true,
selectableRowsRangeMode: 'click',
persistenceID: 'stv-details-noten-teacher-20260217'
};
}
},
@@ -148,8 +148,38 @@ export default {
{ field: 'zeugnis', title: this.$p.t('stv/grades_zeugnis'), formatter: 'tickCross' },
{ field: 'lehrveranstaltung_bezeichnung', title: this.$p.t('lehre/lehrveranstaltung') },
gradeField,
{ field: 'uebernahmedatum', title: this.$p.t('stv/grades_takeoverdate'), visible: false },
{ field: 'benotungsdatum', title: this.$p.t('stv/grades_gradingdate'), visible: false },
{ field: 'uebernahmedatum', title: this.$p.t('stv/grades_takeoverdate'), visible: false,
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",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
});
}},
{ field: 'benotungsdatum', title: this.$p.t('stv/grades_gradingdate'), visible: false,
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",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
});
}},
{ field: 'studiensemester_kurzbz', title: this.$p.t('lehre/studiensemester'), visible: false },
{ field: 'note_number', title: this.$p.t('stv/grades_numericgrade'), visible: false, formatter: cell => cell.getData().note, tooltip: (evt, cell) => cell.getData().note },
{ field: 'lehrveranstaltung_id', title: this.$p.t('lehre/lehrveranstaltung_id'), visible: false },
@@ -218,9 +248,9 @@ export default {
columns,
height: '100%',
layout: 'fitDataStretchFrozen',
selectable: 1,
selectableRangeMode: 'click',
persistenceID: 'stv-details-noten-zeugnis-2025112401',
selectableRows: 1,
selectableRowsRangeMode: 'click',
persistenceID: 'stv-details-noten-zeugnis-20260217',
persistence:{
columns: ["width", "visible", "frozen"]
}
@@ -17,7 +17,6 @@ export default {
},
template: `
<div class="stv-details-notizen h-100 pb-3">
<!-- Test Version classicFas for enter with one click vs popupModal-->
<core-notiz
@@ -31,7 +30,7 @@ export default {
show-tiny-mce
:visibleColumns="['titel','text','verfasser','bearbeiter','dokumente']"
@reload="$emit('update:suffix')"
tabulator-persistence-id="stv-notiz-2026011301"
tabulator-persistence-id="stv-notiz-20260217"
>
</core-notiz>
@@ -76,7 +75,6 @@ visibleColumns: list, which fields shoult be showed as default in filter compone
------------------------ SOME TESTDATA -----------------------------------------------------
---------------------------------------------------------------------------------------------
<core-notiz
:endpoint="$fhcApi.factory.notiz.mitarbeiter"
ref="formc"
@@ -114,8 +112,9 @@ visibleColumns: list, which fields shoult be showed as default in filter compone
:showTinyMCE="true"
:visible-columns="['titel','text','verfasser','bearbeiter','dokumente']"
>
</core-notiz>-->
</core-notiz>
-->
</div>
`
};
};
@@ -260,12 +260,12 @@ export default {
<div class="position-sticky top-0 z-1">
<button type="submit" class="btn btn-primary position-absolute top-0 end-0" :disabled="!deltaLength">Speichern</button>
</div>
<fieldset class="overflow-hidden">
<fieldset class="overflow-hidden mb-2">
<legend>{{$p.t('lehre', 'title_zgv')}} {{modelValue.nachname}} {{modelValue.vorname}}</legend>
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('prestudent_id')"
container-class="col-3 stv-details-prestudent-prestudent_id"
container-class="col stv-details-prestudent-prestudent_id"
:label="$p.t('ui', 'prestudent_id')"
type="text"
v-model="data.prestudent_id"
@@ -275,7 +275,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('person_id')"
container-class="col-3 stv-details-prestudent-person_id"
container-class="col stv-details-prestudent-person_id"
:label="$p.t('person', 'person_id')"
type="text"
v-model="data.person_id"
@@ -284,10 +284,10 @@ export default {
>
</form-input>
</div>
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('zgv_code')"
container-class="col-3 stv-details-prestudent-zgv_code"
container-class="col stv-details-prestudent-zgv_code"
label="ZGV"
type="autocomplete"
v-model="selectedZgv"
@@ -312,7 +312,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('zgvOrt')"
container-class="col-3 stv-details-prestudent-zgvOrt"
container-class="col stv-details-prestudent-zgvOrt"
:label="$p.t('infocenter', 'zgvOrt')"
type="text"
v-model="data.zgvort"
@@ -321,7 +321,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('zgvDatum')"
container-class="col-3 stv-details-prestudent-zgvDatum"
container-class="col stv-details-prestudent-zgvDatum"
:label="$p.t('infocenter', 'zgvDatum')"
type="DatePicker"
v-model="data.zgvdatum"
@@ -337,7 +337,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('zgvNation')"
container-class="col-3 stv-details-prestudent-zgvNation"
container-class="col stv-details-prestudent-zgvNation"
:label="$p.t('infocenter', 'zgvNation')"
type="select"
v-model="data.zgvnation"
@@ -348,10 +348,10 @@ export default {
<option v-for="nation in lists.nations" :key="nation.nation_code" :value="nation.nation_code" :disabled="nation.sperre">{{nation.kurztext}}</option>
</form-input>
</div>
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('zgvmas_code')"
container-class="col-3 stv-details-prestudent-zgvmas_code"
container-class="col stv-details-prestudent-zgvmas_code"
:label="$p.t('lehre', 'zgvMaster')"
type="autocomplete"
v-model="selectedMasterZgv"
@@ -376,7 +376,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('zgvmaort')"
container-class="col-3 stv-details-prestudent-zgvmaort"
container-class="col stv-details-prestudent-zgvmaort"
:label="$p.t('lehre', 'zgvMasterOrt')"
type="text"
v-model="data.zgvmaort"
@@ -385,7 +385,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('zgvmadatum')"
container-class="col-3 stv-details-prestudent-zgvmadatum"
container-class="col stv-details-prestudent-zgvmadatum"
:label="$p.t('lehre', 'zgvMasterDatum')"
type="DatePicker"
v-model="data.zgvmadatum"
@@ -401,7 +401,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('zgvmanation')"
container-class="col-3 stv-details-prestudent-zgvmanation"
container-class="col stv-details-prestudent-zgvmanation"
:label="$p.t('lehre', 'zgvMasterNation')"
type="select"
v-model="data.zgvmanation"
@@ -413,10 +413,10 @@ export default {
</form-input>
</div>
<!--ZGV Doktor-->
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('zgvdoktor_code')"
container-class="col-3 stv-details-prestudent-zgvdoktor_code"
container-class="col stv-details-prestudent-zgvdoktor_code"
:label="$p.t('lehre', 'zgvDoktor')"
type="autocomplete"
v-model="selectedDoktorZgv"
@@ -441,7 +441,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('zgvdoktorort')"
container-class="col-3 stv-details-prestudent-zgvdoktorort"
container-class="col stv-details-prestudent-zgvdoktorort"
:label="$p.t('lehre', 'zgvDoktorOrt')"
type="text"
v-model="data.zgvdoktorort"
@@ -450,7 +450,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('zgvdoktordatum')"
container-class="col-3 stv-details-prestudent-zgvdoktordatum"
container-class="col stv-details-prestudent-zgvdoktordatum"
:label="$p.t('lehre', 'zgvDoktorDatum')"
type="DatePicker"
v-model="data.zgvdoktordatum"
@@ -466,7 +466,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('zgvdoktornation')"
container-class="col-3 stv-details-prestudent-zgvdoktornation"
container-class="col stv-details-prestudent-zgvdoktornation"
:label="$p.t('lehre', 'zgvDoktorNation')"
type="select"
v-model="data.zgvdoktornation"
@@ -477,11 +477,13 @@ export default {
<option v-for="nation in lists.nations" :key="nation.nation_code" :value="nation.nation_code" :disabled="nation.sperre">{{nation.kurztext}}</option>
</form-input>
</div>
<div class="row mb-3">
<div class="col-3 pt-4 d-flex align-items-center">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 gx-3 gy-1 mb-1">
<div
class="col pt-4 d-flex align-items-center"
v-if="!config.hiddenFields.includes('zgv_erfuellt')"
>
<form-input
v-if="!config.hiddenFields.includes('zgv_erfuellt')"
container-class="form-check stv-details-prestudent-zgv_erfuellt"
:label="$p.t('infocenter', 'zgvErfuellt')"
type="checkbox"
@@ -490,9 +492,11 @@ export default {
>
</form-input>
</div>
<div class="col-3 pt-4 d-flex align-items-center">
<div
class="col pt-4 d-flex align-items-center"
v-if="!config.hiddenFields.includes('zgvmas_erfuellt')"
>
<form-input
v-if="!config.hiddenFields.includes('zgvmas_erfuellt')"
container-class="form-check stv-details-prestudent-zgvmas_erfuellt"
:label="$p.t('lehre', 'zgvMasterErfuellt')"
type="checkbox"
@@ -501,9 +505,11 @@ export default {
>
</form-input>
</div>
<div class="col-3 pt-4 d-flex align-items-center">
<div
class="col pt-4 d-flex align-items-center"
v-if="!config.hiddenFields.includes('zgvdoktor_erfuellt')"
>
<form-input
v-if="!config.hiddenFields.includes('zgvdoktor_erfuellt')"
container-class="form-check stv-details-prestudent-zgvdoktor_erfuellt"
:label="$p.t('lehre', 'zgvDoktorErfuellt')"
type="checkbox"
@@ -514,12 +520,12 @@ export default {
</div>
</div>
</fieldset>
<fieldset class="overflow-hidden">
<fieldset class="overflow-hidden mb-2">
<legend>PrestudentIn</legend>
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-3 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('aufmerksamdurch_kurzbz')"
container-class="col-4 stv-details-prestudent-aufmerksamdurch_kurzbz"
container-class="col stv-details-prestudent-aufmerksamdurch_kurzbz"
:label="$p.t('person', 'aufmerksamDurch')"
type="select"
v-model="data.aufmerksamdurch_kurzbz"
@@ -529,7 +535,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('berufstaetigkeit_code')"
container-class="col-4 stv-details-prestudent-berufstaetigkeit_code"
container-class="col stv-details-prestudent-berufstaetigkeit_code"
:label="$p.t('person', 'berufstaetigkeit')"
type="select"
v-model="data.berufstaetigkeit_code"
@@ -540,7 +546,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('ausbildungcode')"
container-class="col-4 stv-details-prestudent-ausbildungcode"
container-class="col stv-details-prestudent-ausbildungcode"
:label="$p.t('person', 'ausbildung')"
type="select"
v-model="data.ausbildungcode"
@@ -551,10 +557,10 @@ export default {
</form-input>
</div>
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-3 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('aufnahmeschluessel')"
container-class="col-4 stv-details-prestudent-aufnahmeschluessel"
container-class="col stv-details-prestudent-aufnahmeschluessel"
label="Aufnahmeschlüssel"
type="text"
v-model="data.aufnahmeschluessel"
@@ -563,9 +569,11 @@ export default {
>
</form-input>
<div class="col-4 pt-4 d-flex align-items-center">
<div
v-if="!config.hiddenFields.includes('facheinschlaegigBerufstaetig')"
class="col pt-4 d-flex align-items-center"
>
<form-input
v-if="!config.hiddenFields.includes('facheinschlaegigBerufstaetig')"
container-class="form-check stv-details-prestudent-facheinschlaegigBerufstaetig"
:label="$p.t('person', 'facheinschlaegigBerufstaetig')"
type="checkbox"
@@ -577,7 +585,7 @@ export default {
<form-input
v-if="!config.hiddenFields.includes('standort_code')"
container-class="col-4 stv-details-prestudent-standort_code"
container-class="col stv-details-prestudent-standort_code"
:label="$p.t('person', 'bisstandort')"
type="select"
v-model="data.standort_code"
@@ -587,10 +595,10 @@ export default {
</form-input>
</div>
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-3 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('studiengang_kz')"
container-class="col-4 stv-details-prestudent-studiengang_kz"
container-class="col stv-details-prestudent-studiengang_kz"
:label="$p.t('lehre', 'studiengang')"
type="select"
v-model="data.studiengang_kz"
@@ -601,7 +609,7 @@ export default {
</form-input>
<form-input
v-if="!config.hiddenFields.includes('gsstudientyp_kurzbz')"
container-class="col-4 stv-details-prestudent-gsstudientyp_kurzbz"
container-class="col stv-details-prestudent-gsstudientyp_kurzbz"
:label="$p.t('lehre', 'studientyp')"
type="select"
v-model="data.gsstudientyp_kurzbz"
@@ -611,66 +619,77 @@ export default {
</form-input>
</div>
<div class="row mb-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-3 gx-3 gy-1 mb-1">
<form-input
v-if="!config.hiddenFields.includes('anmerkung')"
container-class="col-4 stv-details-prestudent-anmerkung"
container-class="col stv-details-prestudent-anmerkung"
:label="$p.t('global', 'anmerkung')"
type="text"
v-model="data.anmerkung"
name="anmerkung"
>
</form-input>
<div class="col-2 pt-4 d-flex align-items-center">
<form-input
v-if="!config.hiddenFields.includes('bismelden')"
container-class="form-check stv-details-prestudent-bismelden"
:label="$p.t('person', 'bismelden')"
type="checkbox"
v-model="data.bismelden"
name="bismelden"
:disabled="!hasBismeldenPermission"
<div class="col">
<div class="row g-3 align-items-center mb-3">
<div
class="col-12 col-md-6 pt-4 d-flex align-items-center"
v-if="!config.hiddenFields.includes('bismelden')"
>
</form-input>
</div>
<div class="col-2 pt-4 d-flex align-items-center">
<form-input
v-if="!config.hiddenFields.includes('dual')"
container-class="form-check stv-details-prestudent-dual"
:label="$p.t('lehre', 'dual')"
type="checkbox"
v-model="data.dual"
name="dual"
<form-input
container-class="col form-check stv-details-prestudent-bismelden"
:label="$p.t('person', 'bismelden')"
type="checkbox"
v-model="data.bismelden"
name="bismelden"
:disabled="!hasBismeldenPermission"
>
</form-input>
</div>
<div
class="col-12 col-md-6 pt-4 d-flex align-items-center"
v-if="!config.hiddenFields.includes('dual')"
>
</form-input>
<form-input
container-class="col form-check stv-details-prestudent-dual"
:label="$p.t('lehre', 'dual')"
type="checkbox"
v-model="data.dual"
name="dual"
>
</form-input>
</div>
</div>
</div>
<form-input
container-class="col-3"
:label="$p.t('lehre', 'foerderrelevant')"
type="select"
v-model="data.foerderrelevant"
name="foerderrelevant"
>
<option v-for="entry in listFoerderrelevant" :value="entry.value">{{entry.text}}</option>
</form-input>
<form-input
v-if="!config.hiddenFields.includes('priorisierung')"
container-class="col-1"
:label="$p.t('lehre', 'prioritaet')"
type="text"
v-model="data.priorisierung"
name="priorisierung"
:disabled="!hasPrestudentPermission"
>
</form-input>
<div class="col">
<div class="row g-3 align-items-center">
<form-input
container-class="col-12 col-md-6"
:label="$p.t('lehre', 'foerderrelevant')"
type="select"
v-model="data.foerderrelevant"
name="foerderrelevant"
>
<option v-for="entry in listFoerderrelevant" :value="entry.value">{{entry.text}}</option>
</form-input>
<form-input
v-if="!config.hiddenFields.includes('priorisierung')"
container-class="col-12 col-md-6"
:label="$p.t('lehre', 'prioritaet')"
type="text"
v-model="data.priorisierung"
name="priorisierung"
:disabled="!hasPrestudentPermission"
>
</form-input>
</div>
</div>
</div>
<core-udf
v-if="!config.hideUDFs"
@load="udfsLoaded"
v-model="data"
class="row-cols-3 g-3 mb-3"
class="row-cols-3 gx-3 gy-1 mb-1"
ci-model="crm/prestudent"
:pk="{prestudent_id:modelValue.prestudent_id}"
>
@@ -12,11 +12,20 @@ export default{
},
data() {
return {
tabulatorOptions: {
layout: 'fitDataFill',
layoutColumnsOnNewData: false,
height: 'auto',
selectableRows: false
}
},
computed: {
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(ApiStvPrestudent.getHistoryPrestudents(this.personId)),
ajaxResponse: (url, params, response) => response.data,
//autoColumns: true,
persistenceID: 'stv-details-prestudent-history-20260217',
columns:[
{title:"StSem", field:"studiensemester_kurzbz"},
{title:"Prio", field:"priorisierung"},
@@ -37,35 +46,38 @@ export default{
element.classList.add('fw-bold');
}
},
layout: 'fitDataFill',
layoutColumnsOnNewData: false,
height: 'auto',
selectable: false,
persistenceID: 'stv-details-prestudent-history'
},
tabulatorEvents: [
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async () => {
await this.$p.loadCategory(['lehre']);
let cm = this.$refs.table.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('orgform_kurzbz').component.updateDefinition({
title: this.$p.t('lehre', 'organisationsform')
});
const el = col.getElement();
if (!el || !el.querySelector) return;
cm.getColumnByField('bezeichnung').component.updateDefinition({
title: this.$p.t('lehre', 'studienplan')
});
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('orgform_kurzbz', this.$p.t('lehre', 'organisationsform'));
setHeader('bezeichnung', this.$p.t('lehre', 'studienplan'));
setHeader('prestudent_id', this.$p.t('ui', 'prestudent_id'));
cm.getColumnByField('prestudent_id').component.updateDefinition({
title: this.$p.t('ui', 'prestudent_id')
});
}
}
]
}
];
return events;
},
},
watch: {
personId() {
@@ -73,7 +85,7 @@ export default{
},
},
template: `
<div class="stv-details-prestudent-history h-100 pt-3">
<div class="stv-details-prestudent-history h-100">
<core-filter-cmpt
ref="table"
:tabulator-options="tabulatorOptions"
@@ -26,37 +26,8 @@ export default{
}
},
computed: {
prestudentIds() {
if (this.modelValue.prestudent_id)
{
return [this.modelValue.prestudent_id];
}
return this.modelValue.map(e => e.prestudent_id);
},
showToolbarStudent() {
if (Array.isArray(this.modelValue)) {
if (!this.modelValue.length)
return false;
return this.modelValue.every(item => item.uid);
}
return !!this.modelValue.uid;
},
showToolbarInteressent() {
if (Array.isArray(this.modelValue)) {
if (!this.modelValue.length)
return false;
return !this.modelValue.some(item => item.uid);
}
return !this.modelValue.uid;
}
},
props: {
modelValue: Object,
config: Object,
},
data() {
return {
tabulatorOptions: {
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(ApiStvPrestudent.getHistoryPrestudent(this.modelValue.prestudent_id)),
ajaxResponse: (url, params, response) => response.data,
@@ -164,7 +135,7 @@ export default{
}
},
{title: "UpdateVon", field: "updatevon", visible: false},
/* {title: "Aufnahmestufe", field: "aufnahmestufe", visible: false},*/
/* {title: "Aufnahmestufe", field: "aufnahmestufe", visible: false},*/
{
title: 'Aktionen', field: 'actions',
minWidth: 150, // Ensures Action-buttons will be always fully displayed
@@ -207,8 +178,8 @@ export default{
button.addEventListener('click', () =>
this.actionEditStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester)
);
/* if (this.dataMeldestichtag && this.dataMeldestichtag > data.datum && !this.hasPermissionToSkipStatusCheck)
button.disabled = true;*/
/* if (this.dataMeldestichtag && this.dataMeldestichtag > data.datum && !this.hasPermissionToSkipStatusCheck)
button.disabled = true;*/
container.append(button);
button = document.createElement('button');
@@ -237,68 +208,78 @@ export default{
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
selectable: false,
selectableRows: false,
index: 'statusId',
persistenceID: 'stv-multistatus-2025112401'
},
tabulatorEvents: [
persistenceID: 'stv-multistatus-20260217'
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async () => {
await this.$p.loadCategory(['lehre','global','person','ui']);
let cm = this.$refs.table.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('lehrverband').component.updateDefinition({
title: this.$p.t('lehre', 'lehrverband')
});
const el = col.getElement();
if (!el || !el.querySelector) return;
cm.getColumnByField('bestaetigtam').component.updateDefinition({
title: this.$p.t('lehre', 'bestaetigt_am')
});
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
cm.getColumnByField('bewerbung_abgeschicktamum').component.updateDefinition({
title: this.$p.t('lehre', 'bewerbung_abgeschickt_am')
});
cm.getColumnByField('bezeichnung').component.updateDefinition({
title: this.$p.t('lehre', 'studienplan')
});
cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'aktionen')
});
cm.getColumnByField('datum').component.updateDefinition({
title: this.$p.t('global', 'datum')
});
cm.getColumnByField('anmerkung').component.updateDefinition({
title: this.$p.t('global', 'anmerkung')
});
cm.getColumnByField('bestaetigtvon').component.updateDefinition({
title: this.$p.t('lehre', 'bestaetigt_von')
});
cm.getColumnByField('insertamum').component.updateDefinition({
title: this.$p.t('lehre', 'insert_am')
});
cm.getColumnByField('insertvon').component.updateDefinition({
title: this.$p.t('lehre', 'insert_von')
});
cm.getColumnByField('prestudent_id').component.updateDefinition({
title: this.$p.t('ui', 'prestudent_id')
});
cm.getColumnByField('studienplan_id').component.updateDefinition({
title: this.$p.t('ui', 'studienplan_id')
});
}
setHeader('lehrverband', this.$p.t('lehre', 'lehrverband'));
setHeader('bestaetigtam', this.$p.t('lehre', 'bestaetigt_am'));
setHeader('bewerbung_abgeschicktamum', this.$p.t('lehre', 'bewerbung_abgeschickt_am'));
setHeader('bezeichnung', this.$p.t('lehre', 'studienplan'));
setHeader('actions', this.$p.t('global', 'aktionen'));
setHeader('datum', this.$p.t('global', 'datum'));
setHeader('anmerkung', this.$p.t('global', 'anmerkung'));
setHeader('bestaetigtvon', this.$p.t('lehre', 'bestaetigt_von'));
setHeader('insertamum', this.$p.t('lehre', 'insert_am'));
setHeader('insertvon', this.$p.t('lehre', 'insert_von'));
setHeader('prestudent_id', this.$p.t('ui', 'prestudent_id'));
setHeader('studienplan_id', this.$p.t('ui', 'studienplan_id')); }
}
],
];
return events;
},
prestudentIds() {
if (this.modelValue.prestudent_id)
{
return [this.modelValue.prestudent_id];
}
return this.modelValue.map(e => e.prestudent_id);
},
showToolbarStudent() {
if (Array.isArray(this.modelValue)) {
if (!this.modelValue.length)
return false;
return this.modelValue.every(item => item.uid);
}
return !!this.modelValue.uid;
},
showToolbarInteressent() {
if (Array.isArray(this.modelValue)) {
if (!this.modelValue.length)
return false;
return !this.modelValue.some(item => item.uid);
}
return !this.modelValue.uid;
}
},
props: {
modelValue: Object,
config: Object,
},
data() {
return {
statusData: {},
statusId: {},
dataMeldestichtag: null,
@@ -420,7 +401,7 @@ export default{
@saved="reload"
>
</status-modal>
<core-filter-cmpt
v-if="!this.modelValue.length"
ref="table"
@@ -433,8 +414,7 @@ export default{
new-btn-show
:new-btn-label="this.$p.t('global', 'status')"
@click:new="actionNewStatus"
>
>
<template #actions="{updateData2}">
<!-- SingleSelectButton-->
<status-dropdown
@@ -49,50 +49,33 @@ export default {
handler: async() => {
await this.$p.loadCategory(['global', 'person', 'lehre', 'stv', 'ui', 'projektarbeit']);
let cm = this.$refs.table.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('projekttyp_kurzbz').component.updateDefinition({
title: this.$p.t('projektarbeit', 'typ_kurzbz')
});
cm.getColumnByField('bezeichnung').component.updateDefinition({
title: this.$p.t('projektarbeit', 'typ')
});
cm.getColumnByField('studiensemester_kurzbz').component.updateDefinition({
title: this.$p.t('lehre', 'studiensemester')
});
cm.getColumnByField('titel').component.updateDefinition({
title: this.$p.t('projektarbeit', 'titel')
});
cm.getColumnByField('note').component.updateDefinition({
title: this.$p.t('projektarbeit', 'gesamtnote')
});
cm.getColumnByField('beginn').component.updateDefinition({
title: this.$p.t('projektarbeit', 'beginn')
});
cm.getColumnByField('ende').component.updateDefinition({
title: this.$p.t('projektarbeit', 'ende')
});
cm.getColumnByField('freigegeben').component.updateDefinition({
title: this.$p.t('projektarbeit', 'freigegeben')
});
cm.getColumnByField('gesperrtbis').component.updateDefinition({
title: this.$p.t('projektarbeit', 'gesperrtBis')
});
cm.getColumnByField('themenbereich').component.updateDefinition({
title: this.$p.t('projektarbeit', 'themenbereich')
});
cm.getColumnByField('anmerkung').component.updateDefinition({
title: this.$p.t('projektarbeit', 'anmerkung')
});
cm.getColumnByField('firma_id').component.updateDefinition({
title: this.$p.t('projektarbeit', 'firmaId')
});
cm.getColumnByField('abgabedatum').component.updateDefinition({
title: this.$p.t('projektarbeit', 'abgabeEndupload')
});
cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'aktionen')
});
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('projekttyp_kurzbz', this.$p.t('projektarbeit', 'typ_kurzbz'));
setHeader('bezeichnung', this.$p.t('projektarbeit', 'typ'));
setHeader('studiensemester_kurzbz', this.$p.t('lehre', 'studiensemester'));
setHeader('titel', this.$p.t('projektarbeit', 'titel'));
setHeader('note', this.$p.t('projektarbeit', 'gesamtnote'));
setHeader('beginn', this.$p.t('projektarbeit', 'beginn'));
setHeader('ende', this.$p.t('projektarbeit', 'ende'));
setHeader('freigegeben', this.$p.t('projektarbeit', 'freigegeben'));
setHeader('gesperrtbis', this.$p.t('projektarbeit', 'gesperrtBis'));
setHeader('themenbereich', this.$p.t('projektarbeit', 'themenbereich'));
setHeader('anmerkung', this.$p.t('projektarbeit', 'anmerkung'));
setHeader('firma_id', this.$p.t('projektarbeit', 'firmaId'));
setHeader('abgabedatum', this.$p.t('projektarbeit', 'abgabeEndupload'));
setHeader('actions', this.$p.t('global', 'aktionen'));
}
},
],
@@ -260,12 +243,12 @@ export default {
layout: 'fitDataStretchFrozen',
height: 'auto',
minHeight: '200',
selectable: 1,
selectableRows: 1,
index: 'projektarbeit_id',
persistence:{
columns: true, //persist column layout
},
persistenceID: 'stv-details-projektarbeit-2025112401'
persistenceID: 'stv-details-projektarbeit-20260217'
}
return options;
}
@@ -120,13 +120,13 @@ export default {
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: '100',
selectable: true,
selectable: 1,
selectableRows: true,
selectableRows: 1,
index: 'betreuer_id',
persistence:{
columns: true, //persist column layout
},
persistenceID: 'stv-details-projektbetreuer-2025121201'
persistenceID: 'stv-details-projektbetreuer-20260217'
},
tabulatorEvents: [
{
@@ -134,45 +134,33 @@ export default {
handler: async() => {
await this.$p.loadCategory(['global', 'person', 'lehre', 'stv', 'projektarbeit', 'ui']);
let cm = this.$refs.projektbetreuerTable.tabulator.columnManager;
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
cm.getColumnByField('nachname').component.updateDefinition({
title: this.$p.t('person', 'nachname')
});
cm.getColumnByField('vorname').component.updateDefinition({
title: this.$p.t('person', 'vorname')
});
cm.getColumnByField('note').component.updateDefinition({
title: this.$p.t('projektarbeit', 'note')
});
cm.getColumnByField('punkte').component.updateDefinition({
title: this.$p.t('projektarbeit', 'punkte')
});
cm.getColumnByField('stunden').component.updateDefinition({
title: this.$p.t('projektarbeit', 'stunden')
});
cm.getColumnByField('stundensatz').component.updateDefinition({
title: this.$p.t('projektarbeit', 'stundensatz')
});
cm.getColumnByField('betreuerart_kurzbz').component.updateDefinition({
title: this.$p.t('projektarbeit', 'betreuerart_kurzbz')
});
cm.getColumnByField('person_id').component.updateDefinition({
title: this.$p.t('person', 'person_id')
});
cm.getColumnByField('vertrag_id').component.updateDefinition({
title: this.$p.t('projektarbeit', 'vertrag_id')
});
cm.getColumnByField('projektarbeit_id').component.updateDefinition({
title: this.$p.t('projektarbeit', 'projektarbeit_id')
});
cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'actions')
});
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('nachname', this.$p.t('person', 'nachname'));
setHeader('vorname', this.$p.t('person', 'vorname'));
setHeader('note', this.$p.t('projektarbeit', 'note'));
setHeader('punkte', this.$p.t('projektarbeit', 'punkte'));
setHeader('stunden', this.$p.t('projektarbeit', 'stunden'));
setHeader('stundensatz', this.$p.t('projektarbeit', 'stundensatz'));
setHeader('betreuerart_kurzbz', this.$p.t('projektarbeit', 'betreuerart_kurzbz'));
setHeader('person_id', this.$p.t('person', 'person_id'));
setHeader('vertrag_id', this.$p.t('projektarbeit', 'vertrag_id'));
setHeader('projektarbeit_id', this.$p.t('projektarbeit', 'projektarbeit_id'));
setHeader('actions', this.$p.t('global', 'actions'));
// Force layout recalculation for handling overflow text
this.$refs.projektbetreuerTable.tabulator.redraw(true);
}
},
{
@@ -29,12 +29,56 @@ export default{
},
data(){
return {
tabulatorOptions: {
pruefungData: {},
listTypesExam: [],
listLvsAndLes: [],
listLvsAndMas: [],
listLvs: [],
listLes: [],
listMas: [],
listMarks: [],
zeugnisData: [],
filter: false,
statusNew: true,
isStartDropDown: false,
isFilterSet: false,
showHint: false,
}
},
computed:{
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(ApiStvExam.getPruefungen(this.uid)),
ajaxResponse: (url, params, response) => response.data,
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
index: 'pruefung_id',
persistenceID: 'stv-details-pruefung-list-20260217',
persistence: {
sort: true,
columns: ["width", "visible", "frozen"],
filter: false,
headerFilter: false,
group: false,
page: false
},
selectableRows: true,
columns: [
{title: "Datum", field: "format_datum"},
{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: "Lehrveranstaltung", field: "lehrveranstaltung_bezeichnung"},
{title: "Note", field: "note_bezeichnung"},
{title: "Anmerkung", field: "anmerkung"},
@@ -87,81 +131,44 @@ export default{
},
frozen: true
}],
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
index: 'pruefung_id',
persistenceID: 'stv-details-pruefung-list-2026012701',
persistence: {
sort: false,
columns: ["width", "visible", "frozen"],
filter: false,
headerFilter: false,
group: false,
page: false
}
},
tabulatorEvents: [
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async () => {
await this.$p.loadCategory(['fristenmanagement', 'global', 'lehre', 'exam', 'ui']);
let cm = this.$refs.table.tabulator.columnManager;
cm.getColumnByField('format_datum').component.updateDefinition({
title: this.$p.t('global', 'datum')
});
cm.getColumnByField('lehrveranstaltung_bezeichnung').component.updateDefinition({
title: this.$p.t('lehre', 'lehrveranstaltung')
});
cm.getColumnByField('note_bezeichnung').component.updateDefinition({
title: this.$p.t('lehre', 'note')
});
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('punkte').component.updateDefinition({
title: this.$p.t('exam', 'punkte')
});
cm.getColumnByField('pruefung_id').component.updateDefinition({
title: this.$p.t('ui', 'pruefung_id')
});
cm.getColumnByField('lehreinheit_id').component.updateDefinition({
title: this.$p.t('global', 'lehreinheit_id')
});
cm.getColumnByField('mitarbeiter_uid').component.updateDefinition({
title: this.$p.t('ui', 'mitarbeiter_uid')
});
cm.getColumnByField('student_uid').component.updateDefinition({
title: this.$p.t('ui', 'student_uid')
});
//Uncaught TypeError: e.element.after is not a function
/* cm.getColumnByField('actions').component.updateDefinition({
title: this.$p.t('global', 'actions')
});*/
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('datum', this.$p.t('global', 'datum'));
setHeader('lehrveranstaltung_bezeichnung', this.$p.t('lehre', 'lehrveranstaltung'));
setHeader('note_bezeichnung', this.$p.t('lehre', 'note'));
setHeader('anmerkung', this.$p.t('global', 'anmerkung'));
setHeader('pruefungstyp_kurzbz', this.$p.t('global', 'typ'));
setHeader('punkte', this.$p.t('exam', 'punkte'));
setHeader('pruefung_id', this.$p.t('ui', 'pruefung_id'));
setHeader('lehreinheit_id', this.$p.t('global', 'lehreinheit_id'));
setHeader('mitarbeiter_uid', this.$p.t('ui', 'mitarbeiter_uid'));
setHeader('student_uid', this.$p.t('ui', 'student_uid'));
}
}
],
pruefungData: {},
listTypesExam: [],
listLvsAndLes: [],
listLvsAndMas: [],
listLvs: [],
listLes: [],
listMas: [],
listMarks: [],
zeugnisData: [],
filter: false,
statusNew: true,
isStartDropDown: false,
isFilterSet: false,
showHint: false,
}
},
computed:{
},
];
return events;
},
lv_teile(){
return this.listLvsAndLes.filter(lv => lv.lehrveranstaltung_id == this.pruefungData.lehrveranstaltung_id);
},
@@ -1,5 +1,11 @@
import {CoreFilterCmpt} from "../../filter/Filter.js";
import ListNew from './List/New.js';
import CoreTag from '../../Tag/Tag.js';
import { tagHeaderFilter } from "../../../tabulator/filters/extendedHeaderFilter.js";
import { addTagInTable, deleteTagInTable, updateTagInTable } from "../../../../js/helpers/TagHelper.js";
import { tagFormatter } from "../../../../js/tabulator/formatter/tags.js";
import ApiTag from "../../../api/factory/stv/tag.js";
import ListFilter from './List/Filter.js';
import { capitalize } from '../../../helpers/StringHelpers.js';
@@ -11,6 +17,7 @@ export default {
components: {
CoreFilterCmpt,
ListNew,
CoreTag,
ListFilter
},
directives: {
@@ -28,7 +35,11 @@ export default {
currentSemester: {
from: 'currentSemester',
required: true
}
},
tagsEnabled: {
from: 'configStvTagsEnabled',
default: false
},
},
props: {
selected: Array,
@@ -36,7 +47,8 @@ export default {
studiensemesterKurzbz: String
},
emits: [
'update:selected'
'update:selected',
'filterActive'
],
data() {
function dateFormatter(cell)
@@ -123,11 +135,13 @@ export default {
"tristate":true, elementAttributes:{"value":"true"}
}, headerFilterEmptyCheck:function(value){return value === null}
},
{title:"Unruly", field:"unruly", visible:false},
],
rowFormatter(row) {
if (row.getData().bnaktiv === false) {
row.getElement().classList.add('text-black','text-opacity-50','fst-italic');
}
row.getElement().draggable = true
},
ajaxRequestFunc: (url, config, params) => {
@@ -154,19 +168,30 @@ export default {
layout: 'fitDataStretch',
layoutColumnsOnNewData: false,
height: '100%',
selectable: true,
selectableRangeMode: 'click',
selectableRows: true,
selectableRowsRangeMode: 'click',
index: 'prestudent_id',
persistenceID: 'stv-list'
persistenceID: 'stv-list',
},
tabulatorEvents: [
{
event: 'rowSelectionChanged',
handler: this.rowSelectionChanged
},
{
event: 'dataLoading',
handler: this.handleDataLoading
},
{
event: 'renderComplete',
handler: this.handleRenderComplete
},
{
event: 'dataProcessed',
handler: this.autoSelectRows
handler: (data) => {
this.getAllRows()
this.autoSelectRows(data)
}
},
{
event: 'dataLoaded',
@@ -179,6 +204,22 @@ export default {
{
event: 'rowClick',
handler: this.handleRowClick // TODO(chris): this should be in the filter component
},
{
event: 'dataTreeRowExpanded',
handler: (data) => {
this.getExpandedRows()
}
},
{
event: 'dataTreeRowCollapsed',
handler: (data) => {
this.getExpandedRows()
}
},
{
event: 'rowMouseDown',
handler: this.handleMouseDown
}
],
focusObj: null, // TODO(chris): this should be in the filter component
@@ -187,7 +228,16 @@ export default {
count: 0,
filteredcount: 0,
selectedcount: 0,
currentEndpoint: null
//tags
expanded: [],
selectedColumnValues: [],
tagEndpoint: ApiTag,
currentEndpoint: null,
headerFilterActive: false,
dragSource: [],
oldScrollUrl: '',
oldScrollLeft: 0,
oldScrollTop: 0
}
},
computed: {
@@ -203,7 +253,9 @@ export default {
+ ': <strong>' + (this.count || 0) + '</strong>';
},
selectedDragObject() {
return this.selected.map(item => {
let items = this.dragSource?.length ? this.dragSource : this.selected;
return items.map(item => {
let type, id;
if (item.uid) {
type = 'student';
@@ -238,6 +290,21 @@ export default {
let today = new Date().toLocaleDateString('en-GB')
.replace(/\//g, '_');
return "StudentList_" + today + ".csv";
},
},
created: function() {
if(this.tagsEnabled) {
const coltags = {
title: 'Tags',
field: 'tags',
tooltip: false,
headerFilter: "input",
headerFilterFunc: tagHeaderFilter,
headerFilterFuncParams: {field: 'tags'},
formatter: (cell) => tagFormatter(cell, this.$refs.tagComponent),
width: 150,
};
this.tabulatorOptions.columns.splice(2, 0, coltags);
}
},
watch: {
@@ -327,19 +394,15 @@ export default {
rowSelectionChanged(data, rows) {
this.selectedcount = data.length;
this.lastSelected = this.selected;
this.$emit('update:selected', data);
// set selected elements draggable
const tableEl = this.$refs.table?.$refs?.table;
if (tableEl) {
const oldDragables = tableEl.querySelectorAll('[draggable]');
for (const draggable of oldDragables)
draggable.removeAttribute('draggable');
}
rows.forEach(row => row.getElement().draggable = true);
//for tags
this.selectedRows = this.$refs.table.tabulator.getSelectedRows();
this.selectedColumnValues = this.selectedRows.filter(row => row.getData().prestudent_id !== undefined && row.getData().prestudent_id).map(row => row.getData().prestudent_id);
this.$emit('update:selected', data);
},
autoSelectRows(data) {
if (this.lastSelected) {
if (Array.isArray(this.lastSelected) && this.lastSelected.length){
// NOTE(chris): reselect rows on refresh
let selected = this.lastSelected.map(el => this.$refs.table.tabulator.getRow(el.prestudent_id))
// TODO(chris): unselect current item if it's no longer in the table?
@@ -348,7 +411,7 @@ export default {
if (selected.length)
this.$refs.table.tabulator.selectRow(selected);
} else if(this.lastSelected === undefined) {
} else if(data && this.lastSelected === undefined) {
// NOTE(chris): select row if it's the only one (preferably only on startup)
if (data.length == 1) {
this.$refs.table.tabulator.selectRow(this.$refs.table.tabulator.getRows());
@@ -357,13 +420,14 @@ export default {
},
updateFilter(filter) {
this.filter = filter;
this.$emit('filterActive', filter);
this.updateUrl();
},
updateUrl(endpoint, first) {
this.lastSelected = first ? undefined : this.selected;
console.log('function param endpoint: ' + JSON.stringify(endpoint));
console.log('current endpoint: ' + JSON.stringify(this.currentEndpoint));
/* console.log('function param endpoint: ' + JSON.stringify(endpoint));
console.log('current endpoint: ' + JSON.stringify(this.currentEndpoint));*/
if( endpoint === undefined && this.currentEndpoint === null)
{
@@ -394,6 +458,7 @@ export default {
this.tabulatorOptions.ajaxURL = endpoint.url;
this.tabulatorOptions.ajaxParams = { ...params };
this.tabulatorOptions.ajaxConfig = {method};
if (!this.$refs.table.tableBuilt) {
if (this.$refs.table.tabulator) {
this.$refs.table.tabulator.on("tableBuilt", () => {
@@ -404,6 +469,7 @@ export default {
this.$refs.table.tabulator.setData(endpoint.url, params, method);
},
dragCleanup(evt) {
this.dragSource = [];
if (evt.dataTransfer.dropEffect == 'none')
return; // aborted or wrong target
@@ -413,6 +479,14 @@ export default {
if (!this.focusObj)
return;
// Ignore typing inside editable elements
if (
e.target instanceof HTMLInputElement ||
e.target instanceof HTMLTextAreaElement ||
e.target.isContentEditable
)
return;
var next;
switch (e.code) {
case 'Enter':
@@ -471,7 +545,57 @@ export default {
if (el != this.focusObj)
this.changeFocus(this.focusObj, el);
}
}
},
//methods tags
addedTag(addedTag)
{
addTagInTable(addedTag, this.allRows, 'prestudent_id')
},
deletedTag(deletedTag)
{
deleteTagInTable(deletedTag, this.allRows);
},
updatedTag(updatedTag)
{
updateTagInTable(updatedTag, this.allRows)
},
getAllRows() {
this.allRows = this.$refs.table.tabulator.getRows();
},
resetFilter(){
this.$refs.listfilter.resetFilter();
this.$refs.table.clearFilters();
},
handleHeaderFilter(filterActive){
this.headerFilterActive = filterActive;
},
handleMouseDown(e, row)
{
let data = row.getData();
let id = data.uid ?? data.prestudent_id ?? data.person_id;
const isAlreadySelected = this.selected?.some(row => (row.uid ?? row.prestudent_id ?? row.person_id) === id);
this.dragSource = (isAlreadySelected && this.selected?.length) ? this.selected : [data];
},
handleDataLoading() {
this.oldScrollLeft = this.$refs.table.tabulator.rowManager.scrollLeft;
this.oldScrollTop = this.$refs.table.tabulator.rowManager.scrollTop;
},
handleRenderComplete() {
const table = this.$refs.table.tabulator.element.querySelector('.tabulator-tableholder');
if(table) {
const curAjaxUrl = this.$refs.table.tabulator.getAjaxUrl();
if(this.oldScrollUrl === curAjaxUrl) {
table.scrollLeft = this.oldScrollLeft;
table.scrollTop = this.oldScrollTop;
} else {
this.oldScrollLeft = table.scrollLeft;
this.oldScrollTop = table.scrollTop;
}
this.oldScrollUrl = this.$refs.table.tabulator.getAjaxUrl();
}
},
},
// TODO(chris): focusin, focusout, keydown and tabindex should be in the filter component
// TODO(chris): filter component column chooser has no accessibilty features
@@ -499,11 +623,42 @@ export default {
:new-btn-label="$p.t('stv/action_new')"
@click:new="actionNewPrestudent"
@table-built="translateTabulator"
:useSelectionSpan="false"
@headerFilterOn="handleHeaderFilter"
>
<template #actions>
<core-tag ref="tagComponent"
v-if="tagsEnabled"
:endpoint="tagEndpoint"
:values="selectedColumnValues"
@added="addedTag"
@deleted="deletedTag"
@updated="updatedTag"
zuordnung_typ="prestudent_id"
></core-tag>
</template>
<template #actions v-if="filter.length || headerFilterActive">
<div class="d-flex justify-content-center align-items-center gap-2 ps-4 position-absolute start-50 translate-middle-x">
<p class="text-danger mb-0">
<strong>{{$p.t('filter','filterActive')}}</strong>
</p>
<button
class="btn btn-outline-danger sm"
:title="$p.t('filter/filterDelete')"
@click="resetFilter"
>
<span class="fa-solid fa-filter-circle-xmark"></span>
</button>
</div>
</template>
<template #filter>
<div class="card">
<div class="card-body">
<list-filter @change="updateFilter" />
<div class="card mt-2">
<div class="card-body p-2">
<list-filter ref="listfilter" @change="updateFilter" :filterActive="filter.length"/>
</div>
</div>
</template>
@@ -7,6 +7,9 @@ export default {
components: {
FilterItem
},
props: {
filterActive: Number,
},
emits: [
'change'
],
@@ -64,8 +67,14 @@ export default {
}
},
watch: {
cleanFilters(n) {
this.$emit('change', n);
cleanFilters: {
//only emit if filteredValue is not empty array
handler(newVal) {
if (newVal.length)
this.$emit("change", newVal);
},
deep: true,
immediate: false
}
},
methods: {
@@ -74,6 +83,11 @@ export default {
},
remove(index) {
this.filters.splice(index, 1);
this.$emit("change", this.filters);
},
resetFilter(){
this.filters = [];
this.$emit("change", this.filters);
}
},
created() {
@@ -95,7 +109,7 @@ export default {
:key="i"
v-model="filters[i]"
:filter-config="filterConfig"
class="mt-3"
class="mt-2"
@remove="remove(i)"
/>
</div>`
+2 -1
View File
@@ -219,13 +219,14 @@ export default {
:scrollable="true"
:lazy="true"
:activeIndex="calcActiveIndex"
:pt="{navContainer:{style: 'flex: 0 0 auto;'}, panelContainer:{class: 'overflow-y-scroll', style: 'flex: 1 1 auto;'}}"
:pt="{navContainer:{style: 'flex: 0 0 auto;'}, panelContainer:{class: 'overflow-y-scroll p-2', style: 'flex: 1 1 auto;'}}"
@tab-click="handleTabClick"
>
<tabpanel
v-for="tab in tabs"
:key="tab.key"
:header="tab.title + ((tab.value.showSuffix && tab.value.suffix !== '') ? ' ' + tab.value.suffix : '')"
:pt="{headerAction:{class: 'px-2 py-1'}}"
>
<keep-alive>
<component
+3 -1
View File
@@ -238,6 +238,8 @@ export default {
field="notiz"
:readonly="tagData.readonly"
placeholder="Notiz..."
@keyup.stop=""
@keydown.stop=""
></form-input>
<button
type="button"
@@ -277,4 +279,4 @@ export default {
</div>
</template>
</bs-modal>`,
}
}
@@ -0,0 +1,201 @@
import {CoreFilterCmpt} from "../../filter/Filter.js";
import BsModal from "../../Bootstrap/Modal.js";
import CoreForm from '../../Form/Form.js';
import FormInput from '../../Form/Input.js';
export default {
components: {
CoreFilterCmpt,
BsModal,
CoreForm,
FormInput
},
inject: {
hasSchreibrechte: {
from: 'hasSchreibrechte',
default: false
},
},
props: {
person_id: {
type: [Number],
required: true
},
vertrag_id: {
type: [Number],
required: true
},
endpoint: {
type: Object,
required: true
},
},
data() {
return {
//tableData: [],
tabulatorOptions: {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
this.endpoint.getAllContractsAssigned(this.person_id, this.vertrag_id)
),
ajaxResponse: (url, params, response) => response.data,
columns: [
{title: "Typ", field: "type"},
{title: "Betrag", field: "betrag",
formatter: function(cell) {
let value = cell.getValue();
if (value == null) {
return "0.00";
}
return parseFloat(value).toFixed(2);
}
},
{title: "Bezeichnung", field: "bezeichnung"},
{title: "Studiensemester", field: "studiensemester_kurzbz"},
{title: "Pruefung_id", field: "pruefung_id", visible: false},
{title: "mitarbeiter_uid", field: "mitarbeiter_uid", visible: false},
{title: "projektarbeit_id", field: "projektarbeit_id", visible: false},
{title: "lehreinheit_id", field: "lehreinheit_id", visible: true},
{title: "betreuerart_kurzbz", field: "betreuerart_kurzbz", visible: false},
{title: "vertrag_id", field: "vertrag_id", visible: false}, //just for testing
{
title: 'Aktionen', field: 'actions',
minWidth: 50,
formatter: (cell, formatterParams, onRendered) => {
const 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-xmark"></i>';
button.title = this.$p.t('vertrag', 'deleteLehrauftrag');
let type = cell.getData().type;
if (type == 'Lehrauftrag')
{
if (!this.hasSchreibrechte) {
button.disabled = true;
button.classList.add('disabled');
} else {
button.addEventListener(
'click',
() =>
this.actionDeleteLehrauftrag(cell.getData().vertrag_id, cell.getData().lehreinheit_id, cell.getData().mitarbeiter_uid)
);
}
}
if (type == 'Betreuung')
{
if (!this.hasSchreibrechte) {
button.disabled = true;
button.classList.add('disabled');
} else {
button.addEventListener(
'click',
() =>
this.actionDeleteBetreuung(cell.getData().vertrag_id, cell.getData().projektarbeit_id, cell.getData().betreuerart_kurzbz)
);
}
}
container.append(button);
return container;
},
frozen: true
},
],
layout: 'fitColumns',
layoutColumnsOnNewData: false,
height: '200',
selectableRowsRangeMode: 'click',
selectableRows: true,
persistenceID: 'core-contracts-details-2026021701'
},
tabulatorEvents: [
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['ui', 'global', 'vertrag', 'projektarbeitsbeurteilung', 'lehre']);
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('type', this.$p.t('global', 'typ'));
setHeader('bezeichnung', this.$p.t('ui', 'bezeichnung'));
setHeader('lehreinheit_id', this.$p.t('ui', 'lehreinheit_id'));
setHeader('betrag', this.$p.t('ui', 'betrag'));
setHeader('studiensemester_kurzbz', this.$p.t('lehre', 'studiensemester'));
setHeader('mitarbeiter_uid', this.$p.t('ui', 'mitarbeiter_uid'));
setHeader('projektarbeit_id', this.$p.t('ui', 'projektarbeit_id'));
setHeader('betreuerart_kurzbz', this.$p.t('projektarbeitsbeurteilung', 'betreuerart'));
setHeader('pruefung_id', this.$p.t('ui', 'pruefung_id'));
setHeader('vertrag_id', this.$p.t('ui', 'vertrag_id'));
setHeader('actions', this.$p.t('global', 'aktionen'));
}
}
],
clickedRows: [],
}
},
watch: {
person_id() {
this.$refs.table.tabulator.setData('api/frontend/v1/vertraege/vertraege/getAllContractsAssigned/' + this.person_id + '/' + this.vertrag_id);
},
vertrag_id() {
this.$refs.table.tabulator.setData('api/frontend/v1/vertraege/vertraege/getAllContractsAssigned/' + this.person_id + '/' + this.vertrag_id);
},
},
methods: {
actionDeleteLehrauftrag(vertrag_id, lehreinheit_id, mitarbeiter_uid) {
this.$emit('deleteLehrauftrag', {
lehreinheit_id: lehreinheit_id,
vertrag_id: vertrag_id,
mitarbeiter_uid: mitarbeiter_uid
});
},
actionDeleteBetreuung(vertrag_id, projektarbeit_id, betreuerart_kurzbz) {
this.$emit('deleteBetreuung', {
person_id: this.person_id,
vertrag_id: vertrag_id,
projektarbeit_id: projektarbeit_id,
betreuerart_kurzbz: betreuerart_kurzbz
});
},
reload() {
this.$refs.table.reloadTable();
this.$emit('reload');
},
},
template: `
<div class="ore-contracts-details h-50 d-flex flex-column w-100 mt-2">
<!-- <div class="core-contracts-details vv">-->
<br>
<h5>{{$p.t('vertrag', 'vertragDetails')}}</h5>
<core-filter-cmpt
ref="table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
:side-menu="false"
reload
>
</core-filter-cmpt>
</div>`
}
@@ -0,0 +1,330 @@
import {CoreFilterCmpt} from "../../filter/Filter.js";
import BsModal from "../../Bootstrap/Modal.js";
import CoreForm from '../../Form/Form.js';
import FormInput from '../../Form/Input.js';
export default {
components: {
CoreFilterCmpt,
BsModal,
CoreForm,
FormInput
},
inject: {
hasSchreibrechte: {
from: 'hasSchreibrechte',
default: false
},
},
props: {
person_id: {
type: [Number],
required: true
},
vertrag_id: {
type: [Number],
required: true
},
listContractStati: {
type: Array,
required: true
},
formDataParent: {
type: Object,
required: true
},
endpoint: {
type: Object,
required: true
},
},
data() {
return {
tabulatorOptions: {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
this.endpoint.getStatiOfContract(this.person_id, this.vertrag_id)
),
ajaxResponse: (url, params, response) => response.data,
persistenceID: 'core-contracts-status-2026021701',
columns: [
{title: "Status", field: "bezeichnung"},
{
title: "Datum",
field: "datum",
formatter: function (cell) {
const dateStr = cell.getValue();
const date = new Date(dateStr); // Convert to Date object
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: false
});
}
},
{title: "vertrag_id", field: "vertrag_id", visible: false},
{title: "Vertragsstatus", field: "vertragsstatus_kurzbz", visible: false},
{title: "User", field: "mitarbeiter_uid", visible: false},
{title: "insertvon", field: "insertvon", visible: false},
{
title: "insertamum",
field: "insertamum",
visible: false,
formatter: function (cell) {
const dateStr = cell.getValue();
const date = new Date(dateStr);
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: false
});
}
},
{title: "updatevon", field: "updatevon", visible: false},
{
title: "updateamum",
field: "updateamum",
visible: false,
formatter: function (cell) {
const dateStr = cell.getValue();
const date = new Date(dateStr);
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: false
});
}
},
{
title: 'Aktionen', field: 'actions',
minWidth: 150,
formatter: (cell, formatterParams, onRendered) => {
const 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('vertrag', 'editStatus');
button.addEventListener(
'click',
(event) =>
this.actionEditStatus(cell.getData().vertrag_id, cell.getData().vertragsstatus_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('vertrag', 'deleteStatus');
if (!this.hasSchreibrechte) {
button.disabled = true;
button.classList.add('disabled');
} else {
button.addEventListener(
'click',
() =>
this.actionDeleteStatus(cell.getData().vertrag_id, cell.getData().vertragsstatus_kurzbz)
);
}
container.append(button);
return container;
},
frozen: true
},
],
layout: 'fitColumns',
layoutColumnsOnNewData: false,
height: '200',
selectableRowsRangeMode: 'click',
selectableRows: true,
},
tabulatorEvents: [
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['ui', 'global', 'vertrag']);
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('bezeichnung', this.$p.t('global', 'status'));
setHeader('datum', this.$p.t('global', 'datum'));
setHeader('mitarbeiter_uid', this.$p.t('person', 'uid'));
setHeader('vertrag_id', this.$p.t('ui', 'vertrag_id'));
setHeader('vertragsstatus_kurzbz', this.$p.t('vertrag', 'vertragStatus'));
setHeader('actions', this.$p.t('global', 'aktionen'));
setHeader('updatevon', this.$p.t('global', 'updatevon'));
setHeader('updateamum', this.$p.t('global', 'updateamum'));
setHeader('insertvon', this.$p.t('global', 'insertvon'));
setHeader('insertamum', this.$p.t('global', 'insertamum'));
}
}
],
clickedRows: [],
statusNew: true,
formData: {
vertragsstatus_kurzbz: null,
datum: new Date()
},
}
},
watch: {
person_id() {
this.$refs.table.tabulator.setData('api/frontend/v1/vertraege/vertraege/getStatiOfContract/' + this.person_id + '/' + this.vertrag_id);
},
vertrag_id() {
this.$refs.table.tabulator.setData('api/frontend/v1/vertraege/vertraege/getStatiOfContract/' + this.person_id + '/' + this.vertrag_id);
},
formDataParent: {
handler(newVal, oldVal) {
this.formData = this.formDataParent;
},
deep: true
}
},
methods: {
actionNewStatus() {
this.resetModal();
this.$refs.contractStatus.show();
},
actionEditStatus(vertrag_id, status) {
this.statusNew = false;
this.$emit('loadContractStatus', {
status: status,
vertrag_id: vertrag_id
});
},
actionDeleteStatus(vertrag_id, status) {
this.$emit('deleteContractStatus', {
status: status,
vertrag_id: vertrag_id
});
},
handleSubmit(action){
if (action == 'new') {
this.$emit('setContractStatus', {
status: this.formData.vertragsstatus_kurzbz,
datum: this.formData.datum
});
}
if (action == 'edit') {
this.$emit('updateContractStatus', {
status: this.formData.vertragsstatus_kurzbz,
datum: this.formData.datum
});
}
},
closeModal(){
this.$refs.contractStatus.hide();
this.$emit('close-modal');
},
openModal(){
this.$refs.contractStatus.show();
this.$emit('open-modal');
},
reload() {
this.$refs.table.reloadTable();
this.$emit('reload');
},
resetModal(){
this.formData = {};
this.formData.vertragsstatus_kurzbz = null;
this.formData.datum = new Date();
this.statusNew = true;
}
},
template: `
<div class="core-contracts-status h-50 d-flex flex-column w-100 mt-2">
<br>
<h5>{{$p.t('vertrag', 'vertragStatus')}}</h5>
<core-filter-cmpt
ref="table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
:side-menu="false"
reload
new-btn-show
:new-btn-label="this.$p.t('global', 'status')"
@click:new="actionNewStatus"
>
</core-filter-cmpt>
<div >
<bs-modal ref="contractStatus">
<template #title>
<p v-if="statusNew" class="fw-bold mt-3">{{$p.t('vertrag', 'addStatus')}}</p>
<p v-else class="fw-bold mt-3">{{$p.t('vertrag', 'editStatus')}}</p>
</template>
<core-form ref="statusData">
<div class="row mb-3">
<form-input
type="DatePicker"
:label="$p.t('global/datum')"
name="datum"
v-model="formData.datum"
auto-apply
:enable-time-picker="true"
format="dd.MM.yyyy"
preview-format="dd.MM.yyyy"
:teleport="true"
>
</form-input>
</div>
<div class="row mb-3">
<form-input
type="select"
:label="$p.t('global/typ')"
v-model="formData.vertragsstatus_kurzbz"
name="vertragsstatus_kurzbz"
:disabled="!statusNew"
>
<option :value="null">{{$p.t('ui', 'bitteWaehlen')}}</option>
<option
v-for="entry in listContractStati"
:key="entry.vertragsstatus_kurzbz"
:value="entry.vertragsstatus_kurzbz"
>
{{entry.bezeichnung}}
</option>
</form-input>
</div>
</core-form>
<template #footer>
<button type="button" class="btn btn-primary" :disabled="!this.hasSchreibrechte" @click="statusNew ? handleSubmit('new') : handleSubmit('edit')">{{$p.t('ui', 'speichern')}}</button>
</template>
</bs-modal>
</div>
</div>`
}
@@ -0,0 +1,193 @@
import {CoreFilterCmpt} from "../../filter/Filter.js";
import BsModal from "../../Bootstrap/Modal.js";
import CoreForm from '../../Form/Form.js';
import FormInput from '../../Form/Input.js';
export default {
components: {
CoreFilterCmpt,
BsModal,
CoreForm,
FormInput
},
props: {
endpoint: {
type: Object,
required: true
},
person_id: {
type: [Number],
required: true
},
},
data() {
return {
tabulatorOptions: {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
this.endpoint.getAllContractsNotAssigned(this.person_id)
),
ajaxResponse: (url, params, response) => response.data,
columns: [
{title: "Typ", field: "type", width: 100},
{
title: "Betrag",
field: "betrag1",
formatter: function(cell) {
let value = cell.getValue();
if (value == null) {
return "0.00";
}
return parseFloat(value).toFixed(2);
}},
{title: "Bezeichnung", field: "bezeichnung", width: 150},
{title: "Studiensemester", field: "studiensemester_kurzbz", width: 160},
{title: "mitarbeiter_uid", field: "mitarbeiter_uid", visible: false},
{title: "projektarbeit_id", field: "projektarbeit_id", visible: false},
{title: "lehreinheit_id", field: "lehreinheit_id", visible: true},
{title: "betreuerart_kurzbz", field: "betreuerart_kurzbz", visible: false},
{title: "Vertragsstunden", field: "vertragsstunden", visible: false},
{title: "vertrag_id", field: "vertrag_id", visible: false}, //just for testing
{
title: "VertragsstundenStudiensemester",
field: "vertragsstunden_studiensemester_kurzbz",
visible: false
},
],
layout: 'fitColumns',
layoutColumnsOnNewData: false,
height: 150,
selectableRowsRangeMode: 'click',
selectableRows: true,
selectableRowsRollingSelection: false, //only allow multiselect with STRG
index: "lehreinheit_id",
persistenceID: 'core-contracts-unassigned-2026021701'
},
tabulatorEvents: [
{
event: 'rowClick',
handler: (e, row) => {
const data = row.getData();
//this.toggleRowClick(e, data);
this.toggleSelect(e, data);
}
},
{
event: 'dataLoaded',
handler: (data) => {
this.totalRows = data.length;
}
},
{
event: 'tableBuilt',
handler: () => {
this.$p.loadCategory(['ui', 'global', 'vertrag', 'projektarbeitsbeurteilung', 'lehre']).then(() => {
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('type', this.$p.t('global', 'typ'));
setHeader('bezeichnung', this.$p.t('ui', 'bezeichnung'));
setHeader('lehreinheit_id', this.$p.t('ui', 'lehreinheit_id'));
setHeader('betrag1', this.$p.t('ui', 'betrag'));
setHeader('studiensemester_kurzbz', this.$p.t('lehre', 'studiensemester'));
setHeader('mitarbeiter_uid', this.$p.t('ui', 'mitarbeiter_uid'));
setHeader('projektarbeit_id', this.$p.t('ui', 'projektarbeit_id'));
setHeader('betreuerart_kurzbz', this.$p.t('projektarbeitsbeurteilung', 'betreuerart'));
setHeader('vertragsstunden', this.$p.t('vertrag', 'vertragsstunden'));
setHeader('vertrag_id', this.$p.t('ui', 'vertrag_id'));
setHeader('vertragsstunden_studiensemester_kurzbz', this.$p.t('vertrag', 'vertragsstunden_studiensemester'));
});
}
}
],
clickedRows: [],
sumBetragLehrauftraege: 0,
totalRows: 0
}
},
watch: {
//TODO(Manu) check
person_id() {
this.$refs.table.reloadTable();
//this.$refs.table.tabulator.setData('api/frontend/v1/vertraege/vertraege/getAllContractsNotAssigned/' + this.person_id);
}
},
methods: {
toggleSelect(event, rowData) {
const isCtrlPressed = event.ctrlKey;
if (!isCtrlPressed) {
const isSameSingleSelection =
this.clickedRows.length === 1 &&
this.clickedRows[0].lehreinheit_id === rowData.lehreinheit_id;
if (isSameSingleSelection) {
this.clickedRows = [];
this.sumBetragLehrauftraege = 0;
} else {
this.clickedRows = [rowData];
this.sumBetragLehrauftraege = Number(rowData.betrag1);
}
}
// Multiselect
else {
const exists = this.clickedRows.some(
row => row.lehreinheit_id === rowData.lehreinheit_id
);
if (exists) {
this.clickedRows = this.clickedRows.filter(
row => row.lehreinheit_id !== rowData.lehreinheit_id
);
this.sumBetragLehrauftraege -= Number(rowData.betrag1);
} else {
this.clickedRows.push(rowData);
this.sumBetragLehrauftraege += Number(rowData.betrag1);
}
}
this.handleSumUp();
},
emitSaveEvent() {
this.$emit('saveClickedRows', this.clickedRows);
},
reloadUnassigned() {
this.clickedRows = [];
//this.$refs.table.reloadTable();
this.$emit('reloadUnassigned');
},
handleSumUp() {
this.$emit("sum-updated", this.sumBetragLehrauftraege);
},
},
template: `
<div class="core-contracts-unassigned h-50 d-flex flex-column w-100">
<p v-if="totalRows > 0">{{$p.t('vertrag', 'text_explainLehrauftrag')}}</p>
<core-filter-cmpt
ref="table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
:side-menu="false"
>
</core-filter-cmpt>
</div>`
}
@@ -0,0 +1,147 @@
import {CoreFilterCmpt} from "../filter/Filter.js";
import FormInput from '../Form/Input.js';
export default {
name: 'CoreListMitarbeiter',
components: {
CoreFilterCmpt,
FormInput
},
emits: [
"selectionChanged"
],
props: {
endpoint: {
type: Object,
required: true
},
domain: {
type: String,
required: true
}
},
data() {
return {
tabulatorData: [],
tabulatorOptions: {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
this.endpoint.getMitarbeiter()
),
ajaxResponse: (url, params, response) => response.data,
columns: [
{title: "UID", field: "uid", headerFilter: "input"},
{title: "personID", field: "person_id", visible: false, headerFilter: "input"},
{title: "Nachname", field: "nachname", visible: true, headerFilter: "input"},
{title: "Vorname", field: "vorname", visible: true, headerFilter: "input"},
{
title: "Geburtsdatum",
field: "gebdatum",
headerFilter: "input",
visible: true,
formatter: function (cell) {
const dateStr = cell.getValue();
const date = new Date(dateStr);
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
}
},
{
title: "Aktiv", field: "aktiv", visible: false, headerFilter: true, width: 85,
formatter:"tickCross",
hozAlign:"center",
formatterParams: {
tickElement: '<i class="fa fa-check text-success"></i>',
crossElement: '<i class="fa fa-xmark text-danger"></i>'
}
},
{title: "Unternehmen", field: "unternehmen", visible: false, headerFilter: "input"},
{title: "Vertragsarten", field: "vertragsarten", visible: true, headerFilter: "input"},
{title: "Ids Dienstverträge", field: "ids", visible: true, headerFilter: "input"},
],
layout: 'fitColumns',
persistenceID: 'core-mitarbeiter-2026021701',
footerElement: '<div>&sum; <span id="search_count"></span> / <span id="total_count"></span></div>',
selectableRowsRangeMode: 'click',
selectableRows: 1,
},
tabulatorEvents: [
{
event: 'rowSelectionChanged',
handler: this.rowSelectionChanged
},
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['person', 'global', 'vertrag']);
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('person_id', this.$p.t('person', 'person_id'));
setHeader('nachname', this.$p.t('person', 'nachname'));
setHeader('vorname', this.$p.t('person', 'vorname'));
setHeader('gebdatum', this.$p.t('person', 'geburtsdatum'));
setHeader('unternehmen', this.$p.t('person', 'firma'));
setHeader('vertragsarten', this.$p.t('vertrag', 'vertragsarten'));
setHeader('ids', this.$p.t('vertrag', 'idsDienstverhaeltnisse'));
setHeader('aktiv', this.$p.t('global', 'aktiv'));
}
},
{
event: "dataFiltered",
handler: function(filters, rows) {
let el = document.getElementById("search_count");
el.innerHTML = rows.length;
}
},
{
event: 'dataLoaded',
handler: (data) => {
let el = document.getElementById("total_count");
el.innerHTML = data.length;
}
},
],
selectedPerson: null,
selectedUid: null,
}
},
methods: {
rowSelectionChanged(data) {
if(typeof data[0] != 'undefined')
{
this.selectedPerson = data[0].person_id;
this.selectedUid = data[0].uid;
this.$emit('selectionChanged', {
person_id: this.selectedPerson,
uid: this.selectedUid
});
}
},
},
template: `
<core-filter-cmpt
ref="table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
filter-type="Vertragsverwaltung"
:side-menu="false"
>
</core-filter-cmpt>
`
}
+851
View File
@@ -0,0 +1,851 @@
import {CoreFilterCmpt} from "../filter/Filter.js";
import BsModal from "../Bootstrap/Modal.js";
import CoreForm from '../Form/Form.js';
import FormInput from '../Form/Input.js';
import ListUnassigned from './List/Unassigned.js';
import ContractDetails from './List/Details.js';
import ContractStati from './List/Status.js';
export default {
name:'CoreVertraege',
components: {
CoreFilterCmpt,
BsModal,
CoreForm,
FormInput,
ListUnassigned,
ContractDetails,
ContractStati
},
inject: {
/* cisRoot: {
from: 'cisRoot'
},*/
hasSchreibrechte: {
from: 'hasSchreibrechte',
default: false
},
},
props: {
endpoint: {
type: Object,
required: true
},
person_id: {
type: [Number],
required: true
},
mitarbeiter_uid: {
type: [String],
required: true
},
},
computed: {
appRoot() {
return FHC_JS_DATA_STORAGE_OBJECT.app_root;
},
tabulatorOptions() {
const options = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
this.endpoint.getAllVertraege(this.person_id)
),
ajaxResponse: (url, params, response) => response.data,
columns: [
{title: "Bezeichnung", field: "bezeichnung", width: 300},
{
title: "Betrag", field: "betrag", width: 100,
formatter: function (cell) {
let value = cell.getValue();
if (value == null) {
return "0.00";
}
return parseFloat(value).toFixed(2);
}
},
{title: "Vertragstyp", field: "vertragstyp_bezeichnung", width: 125},
{title: "Status", field: "status", width: 100},
{
title: "Vertragsdatum",
field: "vertragsdatum",
width: 128,
formatter: function (cell) {
const dateStr = cell.getValue();
const date = new Date(dateStr);
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
}
},
{title: "VertragId", field: "vertrag_id", visible: false},
{title: "Vertragsstunden", field: "vertragsstunden", visible: false},
{title: "VertragsstundenStudiensemester", field: "vertragsstunden_studiensemester_kurzbz", visible: false},
{title: "Anmerkung", field: "anmerkung", visible: false},
{title: "isAbgerechnet", field: "isabgerechnet", visible: false},
{
title: 'Aktionen', field: 'actions',
minWidth: 150,
maxWidth: 150,
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('vertrag', 'editVertrag');
button.addEventListener(
'click',
(event) =>
this.actionEditContract(cell.getData().vertrag_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('vertrag', 'deleteVertrag');
if (!this.hasSchreibrechte) {
button.disabled = true;
button.classList.add('disabled');
} else {
button.addEventListener(
'click',
() =>
this.actionDeleteContract(cell.getData().vertrag_id)
);
}
container.append(button);
return container;
},
frozen: true
}],
layout: 'fitColumns',
layoutColumnsOnNewData: false,
height: '250',
selectableRowsRangeMode: 'click',
selectableRows: true,
selectableRowsRollingSelection: false, //only allow multiselect with STRG
index: "vertrag_id",
persistence: {
sort: true,
page: true,
columns: true,
filter: false //to avoids js errors
},
persistenceID: 'core-contracts-2026021701',
};
return options;
},
tabulatorEvents() {
const events = [
{
event: 'tableBuilt',
handler: async() => {
await this.$p.loadCategory(['ui', 'global', 'vertrag']);
const setHeader = (field, text) => {
const col = this.$refs.table.tabulator.getColumn(field);
if (!col) return;
const el = col.getElement();
if (!el || !el.querySelector) return;
const titleEl = el.querySelector('.tabulator-col-title');
if (titleEl) {
titleEl.textContent = text;
}
};
setHeader('bezeichnung', this.$p.t('ui', 'bezeichnung'));
setHeader('betrag', this.$p.t('ui', 'betrag'));
setHeader('status', this.$p.t('global', 'status'));
setHeader('vertragstyp_bezeichnung', this.$p.t('vertrag', 'vertragstyp'));
setHeader('vertragsdatum', this.$p.t('vertrag', 'vertragsdatum'));
setHeader('vertragsstunden', this.$p.t('vertrag', 'vertragsstunden'));
setHeader('vertragsstunden_studiensemester_kurzbz', this.$p.t('vertrag', 'vertragsstunden_studiensemester'));
setHeader('vertrag_id', this.$p.t('ui', 'vertrag_id'));
setHeader('anmerkung', this.$p.t('global', 'anmerkung'));
setHeader('isabgerechnet', this.$p.t('vertrag', 'abgerechnet'));
setHeader('actions', this.$p.t('global', 'aktionen'));
}
},
/* {
//is just enabled for ADDON Injection KU: MultiprintHonorarvertrag
//(maybe enable also for ADDON FH Burgenland: MultiAccept later)
event: 'rowClick',
handler: (e, row) => {
if (this.dataPrintHonorar != null && this.dataPrintHonorar.multiselect != null) {
const selectedContract = row.getData().vertrag_id;
const status = row.getData().status;
const bezeichnung = row.getData().bezeichnung;
this.toggleRowClick(selectedContract, status, bezeichnung);
}
}
},*/
{
event: 'rowClick',
handler: (e, row) => {
if (!this.dataPrintHonorar?.multiselect) return;
const { vertrag_id, status, bezeichnung, vertragstyp_bezeichnung } = row.getData();
this.toggleRowClick(e, vertrag_id, status, bezeichnung, vertragstyp_bezeichnung);
}
},
{
event: "dataProcessed",
handler: () => {
if (this.pendingSelectId) {
// delete old Selection
this.$refs.table.tabulator.deselectRow();
// select new Selection
this.$refs.table.tabulator.selectRow(this.pendingSelectId);
this.pendingSelectId = null;
}
}
},
];
return events;
},
},
data() {
return {
statusNew: true,
formData: { },
listContractsUnassigned: [],
listContractTypes: [],
contractSelected: [],
listContractStati: [],
contractFormData: {
vertragsstatus_kurzbz: '',
datum: new Date(),
},
dataPrintHonorar: [],
triggeredData: [],
childData: {},
isFilterSet: false,
clickedRows: [],
arraySelectedContracts: [],
pendingSelectId: null
}
},
watch: {
person_id() {
this.$refs.table.reloadTable();
this.arraySelectedContracts = [];
/* if(this.dataPrintHonorar?.multiselect)
this.dataPrintHonorar.multiselect = [];*/
},
},
methods: {
actionNewContract() {
this.resetModal();
this.$refs.unassignedLehrauftraege.reloadUnassigned();
this.$refs.contractModal.show();
},
actionEditContract(vertrag_id) {
this.resetModal();
this.statusNew = false;
this.$refs.unassignedLehrauftraege.reloadUnassigned();
this.loadContract(vertrag_id)
.then(this.$refs.contractModal.show);
},
actionDeleteContract(vertrag_id) {
this.$fhcAlert
.confirmDelete()
.then(result => result
? vertrag_id
: Promise.reject({handled: true}))
.then(vertrag_id => this.$api.call(
this.endpoint.deleteContract(vertrag_id))
)
.then(result => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
//window.scrollTo(0, 0);
this.reload();
this.contractSelected.vertrag_id = null;
})
.catch(this.$fhcAlert.handleSystemError);
},
addNewContract() {
this.$refs.unassignedLehrauftraege.emitSaveEvent();
const dataToSend = {
person_id: this.person_id,
formData: this.formData,
clickedRows: this.childData, //all data needed, maybe smaller array?
};
return this.$refs.contractData
.call(this.endpoint.addNewContract(dataToSend))
.then(response => {
//get new ID of request to focus the new entry
this.pendingSelectId = response.data;
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
this.$refs.contractModal.hide();
this.resetModal();
//for getting correct responding Details and Stati of Contract
this.contractSelected.vertrag_id = this.pendingSelectId;
if(this.$refs.contractdetails)
this.$refs.contractdetails.reload();
if(this.$refs.unassignedLehrauftraege)
this.$refs.unassignedLehrauftraege.reloadUnassigned();
this.reload();
})
.catch(this.$fhcAlert.handleSystemError);
},
updateContract(vertrag_id) {
this.$refs.unassignedLehrauftraege.emitSaveEvent();
const dataToSend = {
vertrag_id: vertrag_id,
person_id: this.person_id,
formData: this.formData,
clickedRows: this.childData,
};
return this.$refs.contractData
.call(this.endpoint.updateContract(dataToSend))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
this.pendingSelectId = response.data;
this.$refs.contractModal.hide();
this.resetModal();
this.$refs.unassignedLehrauftraege.reloadUnassigned();
this.$refs.contractdetails.reload();
this.reload();
})
.catch(this.$fhcAlert.handleSystemError);
},
loadContract(vertrag_id) {
this.resetModal();
this.statusNew = false;
return this.$api
.call(this.endpoint.loadContract(vertrag_id))
.then(result => {
this.formData = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
},
//Methods Contract Stati
addNewContractStatus({status, datum}) {
const date = new Date();
const formattedDate = datum != null ? datum.toLocaleDateString('en-CA',
{
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false, // Use 24-hour format
}
) : null;
let params = {
vertrag_id : this.contractSelected.vertrag_id,
vertragsstatus_kurzbz: status,
datum: formattedDate
};
return this.$refs.contractstati.$refs.statusData
.call(this.endpoint.insertContractStatus(params))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
this.$refs.contractstati.closeModal();
this.$refs.contractstati.reload();
this.pendingSelectId = params.vertrag_id;
this.reload();
})
.catch(this.$fhcAlert.handleSystemError);
},
deleteContractStatus({status, vertrag_id}){
let params = {
vertrag_id : vertrag_id,
vertragsstatus_kurzbz: status
};
return this.$api
.call(this.endpoint.deleteContractStatus(params))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
this.$refs.contractstati.reload();
})
.catch(this.$fhcAlert.handleSystemError);
},
updateContractStatus({datum, status}){
const date = new Date();
const formattedDate = datum != null ? datum.toLocaleDateString('en-CA',
{
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false, // Use 24-hour format
}
) : null;
let params = {
vertrag_id : this.contractSelected.vertrag_id,
datum : formattedDate,
vertragsstatus_kurzbz: status
};
return this.$refs.contractstati.$refs.statusData
.call(this.endpoint.updateContractStatus(params))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
this.$refs.contractstati.closeModal();
this.$refs.contractstati.reload();
this.pendingSelectId = params.vertrag_id;
this.reload();
})
.catch(this.$fhcAlert.handleSystemError);
},
loadContractStatus({vertrag_id, status}){
let params = {
vertrag_id : vertrag_id,
vertragsstatus_kurzbz: status
};
return this.$api
.call(this.endpoint.loadContractStatus(params))
.then(response => {
this.contractFormData = response.data;
this.$refs.contractstati.openModal();
})
.catch(this.$fhcAlert.handleSystemError);
},
deleteLehrauftrag({lehreinheit_id, vertrag_id, mitarbeiter_uid}){
let params = {
vertrag_id : vertrag_id,
lehreinheit_id: lehreinheit_id,
mitarbeiter_uid: mitarbeiter_uid
};
return this.$api
.call(this.endpoint.deleteLehrauftrag(params))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
this.resetModal();
this.$refs.contractdetails.reload();
this.$refs.unassignedLehrauftraege.reloadUnassigned();
})
.catch(this.$fhcAlert.handleSystemError);
},
deleteBetreuung({person_id, vertrag_id, projektarbeit_id, betreuerart_kurzbz}){
let params = {
vertrag_id : vertrag_id,
person_id: person_id,
projektarbeit_id: projektarbeit_id,
betreuerart_kurzbz: betreuerart_kurzbz
};
return this.$api
.call(this.endpoint.deleteBetreuung(params))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
this.$refs.contractdetails.reload();
this.$refs.unassignedLehrauftraege.reloadUnassigned();
})
.catch(this.$fhcAlert.handleSystemError);
},
//Methods Unassigned List
saveClickedRows(clickedRows) {
this.childData = clickedRows;
},
reload() {
this.$refs.table.reloadTable();
},
resetModal(){
this.formData = {};
this.formData.vertragsdatum = new Date();
this.formData.betrag = 0;
this.formData.bezeichnung = this.getFormattedDate();
this.formData.vertragstyp_kurzbz = null;
this.statusNew = true;
},
updateBetrag(sumBetrag){
this.formData.betrag = sumBetrag;
},
getFormattedDate() {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, "0");
const day = String(today.getDate()).padStart(2, "0");
return `${year}${month}${day}`; // Format: YYYYMMDD
},
onSwitchChange() {
if (this.isFilterSet) {
this.$refs.table.tabulator.setFilter("isabgerechnet", "!=", true);
}
else {
this.$refs.table.tabulator.clearFilter("status");
}
},
//methods for functionality ADDON KU
printContract(){
//check if at least 2 contracts chosen
if(this.arraySelectedContracts.length < 2) {
this.$fhcAlert.alertError(this.$p.t('vertrag', 'alertMindestensZweiVertraege'));
return;
}
//check if status=="Genehmigt"
const statusNotGenehmigtExists = this.arraySelectedContracts.some(v => v.status !== 'Genehmigt');
if(statusNotGenehmigtExists) {
this.$fhcAlert.alertError(this.$p.t('vertrag', 'alertOnlyApprovedContracts'));
return;
}
//build String to Print PDF
let vertragString = '';
this.arraySelectedContracts.forEach(element => {
vertragString += '&vertrag_id[]=' + element.vertrag_id.toString();
});
let linkToPdf = this.dataPrintHonorar.link +
'content/pdfExport.php?xml=' + this.dataPrintHonorar.xml + '&xsl=' + this.dataPrintHonorar.xsl + '&mitarbeiter_uid=' + this.mitarbeiter_uid + vertragString + '&output=pdf&uid=' + this.mitarbeiter_uid;
window.open(linkToPdf, '_blank');
},
/* toggleRowClick(contractId, status, bezeichnung) {
const index = this.arraySelectedContracts.findIndex(
([id]) => id === contractId
);
if (index !== -1) {
this.arraySelectedContracts.splice(index, 1);
} else {
this.arraySelectedContracts.push([contractId, status, bezeichnung]);
}
},*/
toggleRowClick(event, vertrag_id, status, bezeichnung, vertragstyp_bezeichnung) {
if (!this.dataPrintHonorar?.multiselect) return;
const isCtrl = event.ctrlKey || event.metaKey;
const entry = {
vertrag_id,
status,
bezeichnung,
vertragstyp_bezeichnung
};
// Single click
if (!isCtrl) {
this.arraySelectedContracts = [entry];
return;
}
// CTRL / CMD → toggle
const index = this.arraySelectedContracts.findIndex(
e => e.vertrag_id === vertrag_id
);
if (index === -1) {
this.arraySelectedContracts.push(entry);
//this.arraySelectedContracts.push([entry.vertrag_id, entry.status, entry.bezeichnung, entry.vertragstyp_bezeichnung]);
} else {
this.arraySelectedContracts.splice(index, 1);
}
},
/* clearSelection(){
this.arraySelectedContracts = [];
this.$refs.table.tabulator.deselectRow();
}*/
},
created() {
Promise.all([
this.$api.call(this.endpoint.getAllContractTypes()),
this.$api.call(this.endpoint.getAllContractsNotAssigned(this.person_id)),
this.$api.call(this.endpoint.getAllContractStati()),
this.$api.call(this.endpoint.configPrintDocument())
])
.then(([result1, result2, result3, result4]) => {
this.listContractTypes = result1.data;
this.listContractsUnassigned = result2.data;
this.listContractStati = result3.data;
this.dataPrintHonorar = result4.data;
})
.catch(this.$fhcAlert.handleSystemError);
},
mounted() {
//necessary for reloading components Status and Details
this.$nextTick(() => {
this.$refs.table.tabulator.on("rowClick", (e, row) => {
this.contractSelected = row.getData();
});
});
this.getFormattedDate();
},
/*
TODO(Manu) delete after check
<div class="row mb-3">
<form-input
type="DatePicker"
:label="$p.t('vertrag/datum_vertrag')"
name="vertragsdatum"
v-model="formData.vertragsdatum"
auto-apply
:enable-time-picker="false"
format="dd.MM.yyyy"
preview-format="dd.MM.yyyy"
:teleport="true"
>
</form-input>
</div>
<div class="row mb-3">
<form-input
type="text"
:label="$p.t('ui/bezeichnung')"
name="bezeichnung"
v-model="formData.bezeichnung"
>
</form-input>
</div>
<div class="row mb-3">
<form-input
type="select"
:label="$p.t('global/typ')"
v-model="formData.vertragstyp_kurzbz"
name="vertragstyp_kurzbz"
>
<option :value="null">-- {{$p.t('fehlermonitoring', 'keineAuswahl')}} --</option>
<option
v-for="entry in listContractTypes"
:key="entry.vertragstyp_kurzbz"
:value="entry.vertragstyp_kurzbz"
>
{{entry.bezeichnung}}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
:label="$p.t('ui/betrag')"
name="betrag"
v-model="formData.betrag"
>
</form-input>
</div>
<div class="row mb-3" v-if="!statusNew">
<form-input
type="text"
:label="$p.t('ui/stunden') + ' (' + $p.t('vertrag/vertrag_urfassung')+ ')'"
name="vertragsstunden"
v-model="formData.vertragsstunden"
disabled
>
</form-input>
</div>
<div class="row mb-3" v-if="!statusNew">
<form-input
type="text"
:label="$p.t('lehre/studiensemester') + ' (' + $p.t('vertrag/vertrag_urfassung')+ ')'"
name="vertragsstunden_studiensemester_kurzbz"
v-model="formData.vertragsstunden_studiensemester_kurzbz"
disabled
>
</form-input>
</div>
<div class="row mb-3">
<form-input
type="textarea"
:label="$p.t('global/anmerkung')"
name="anmerkung"
v-model="formData.anmerkung"
>
</form-input>
</div>
*/
template: `
<div class="core-contracts h-100 d-flex flex-column">
<!-- filter: open means no status abgerechnet yet-->
<div class="justify-content-end pb-3">
<form-input
container-class="form-switch"
type="checkbox"
:label="$p.t('vertrag/filter_offeneVertraege')"
v-model="isFilterSet"
@change="onSwitchChange"
>
</form-input>
</div>
<core-filter-cmpt
ref="table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
:side-menu="false"
reload
new-btn-show
:new-btn-label="this.$p.t('ui', 'vertrag')"
@click:new="actionNewContract"
>
<!-- injected print functionality for KU Linz (printHonorarvertrag) -->
<template #actions v-if="arraySelectedContracts.length >= 2">
<div class="d-flex justify-content-center align-items-center gap-2 ps-4 position-absolute start-50 translate-middle-x">
<button :disabled="!this.hasSchreibrechte" type="button" class="btn btn-secondary" @click="printContract()">{{$p.t('vertrag', 'printHonorarvertrag')}}</button>
</div>
</template>
</core-filter-cmpt>
<div class="row">
<div class="col-sm-6">
<!-- ContractDetails -->
<div class="md-4" v-if="contractSelected.vertrag_id !=null && arraySelectedContracts.length == 1">
<contract-details
:person_id="person_id"
:vertrag_id="contractSelected.vertrag_id"
:endpoint="endpoint"
@deleteLehrauftrag="deleteLehrauftrag"
@deleteBetreuung="deleteBetreuung"
ref="contractdetails"
></contract-details>
</div>
</div>
<div class="col-sm-6">
<!-- ContractStati -->
<div class="md-4" v-if="contractSelected.vertrag_id !=null && arraySelectedContracts.length == 1">
<contract-stati
:person_id="person_id"
:vertrag_id="contractSelected.vertrag_id"
:listContractStati="listContractStati"
:formDataParent="contractFormData"
:endpoint="endpoint"
@setContractStatus="addNewContractStatus"
@deleteContractStatus="deleteContractStatus"
@updateContractStatus="updateContractStatus"
@loadContractStatus="loadContractStatus"
ref="contractstati"
></contract-stati>
</div>
</div>
</div>
<!--Modal: contractModal-->
<bs-modal ref="contractModal" dialog-class="modal-xl">
<template #title>
<p v-if="statusNew" class="fw-bold mt-3">{{$p.t('vertrag', 'addVertrag')}}</p>
<p v-else class="fw-bold mt-3">{{$p.t('vertrag', 'editVertrag')}}</p>
</template>
<list-unassigned
:person_id="person_id"
:endpoint="endpoint"
ref="unassignedLehrauftraege"
@saveClickedRows="saveClickedRows"
@sum-updated="updateBetrag"
></list-unassigned>
<hr>
<core-form ref="contractData">
<div class="row mb-3">
<div :class="statusNew ? 'col-6' : 'col-4'">
<form-input
type="DatePicker"
:label="$p.t('vertrag/datum_vertrag')"
name="vertragsdatum"
v-model="formData.vertragsdatum"
auto-apply
:enable-time-picker="false"
format="dd.MM.yyyy"
preview-format="dd.MM.yyyy"
:teleport="true"
>
</form-input>
</div>
<div :class="statusNew ? 'col-6' : 'col-4'">
<form-input
type="text"
:label="$p.t('ui/bezeichnung')"
name="bezeichnung"
v-model="formData.bezeichnung"
>
</form-input>
</div>
<div class="col-4" v-if="!statusNew">
<form-input
type="text"
:label="$p.t('ui/stunden') + ' (' + $p.t('vertrag/vertrag_urfassung')+ ')'"
name="vertragsstunden"
v-model="formData.vertragsstunden"
disabled
>
</form-input>
</div>
</div>
<div class="row mb-3">
<div :class="statusNew ? 'col-6' : 'col-4'">
<form-input
type="select"
:label="$p.t('global/typ')"
v-model="formData.vertragstyp_kurzbz"
name="vertragstyp_kurzbz"
>
<option :value="null">-- {{$p.t('fehlermonitoring', 'keineAuswahl')}} --</option>
<option
v-for="entry in listContractTypes"
:key="entry.vertragstyp_kurzbz"
:value="entry.vertragstyp_kurzbz"
>
{{entry.bezeichnung}}
</option>
</form-input>
</div>
<div :class="statusNew ? 'col-6' : 'col-4'">
<form-input
:label="$p.t('ui/betrag')"
name="betrag"
v-model="formData.betrag"
>
</form-input>
</div>
<div class="col-4" v-if="!statusNew">
<form-input
type="text"
:label="$p.t('lehre/studiensemester') + ' (' + $p.t('vertrag/vertrag_urfassung')+ ')'"
name="vertragsstunden_studiensemester_kurzbz"
v-model="formData.vertragsstunden_studiensemester_kurzbz"
disabled
>
</form-input>
</div>
</div>
<div class="row mb-3">
<form-input
type="textarea"
:label="$p.t('global/anmerkung')"
name="anmerkung"
v-model="formData.anmerkung"
>
</form-input>
</div>
</core-form>
<template #footer>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @click="reload()">{{$p.t('ui', 'abbrechen')}}</button>
<button type="button" class="btn btn-primary" :disabled="!this.hasSchreibrechte" @click="statusNew ? addNewContract() : updateContract(formData.vertrag_id)">{{$p.t('vertrag', 'vertragErstellen')}}</button>
</template>
</bs-modal>
</div>`
}
@@ -0,0 +1,92 @@
import {CoreNavigationCmpt} from "../navigation/Navigation.js";
import MitarbeiterHeader from "./MitarbeiterHeader.js";
import FhcHeader from "../DetailHeader/DetailHeader.js";
import VertraegeMitarbeiter from "./Vertraege.js";
import VerticalSplit from "../verticalsplit/verticalsplit.js";
import ApiCoreVertraege from '../../api/factory/vertraege/vertraege.js';
export default {
name: 'Vertragsverwaltung',
components: {
CoreNavigationCmpt,
VerticalSplit,
MitarbeiterHeader,
FhcHeader,
VertraegeMitarbeiter
},
props: {
config: Object,
permissions: Object,
},
provide() {
return {
configDomain: this.config.domain,
hasSchreibrechte: this.permissions['vertragsverwaltung_schreibrechte'],
}
},
data() {
return {
person_id: null,
mitarbeiter_uid: null,
endpoint: ApiCoreVertraege
}
},
computed: {
tile_MaUid(){
let tile = this.mitarbeiter_uid != null ? this.mitarbeiter_uid : '-';
return tile;
},
tile_PersId(){
let tile = this.person_id != null ? this.person_id : '-';
return tile;
},
},
methods: {
handleSelection(selection) {
this.mitarbeiter_uid = selection.uid;
this.person_id = selection.person_id;
},
},
template: `
<!-- Navigation component -->
<core-navigation-cmpt/>
<div id="content">
<vertical-split ref="vsplit">
<template #top>
<div class="d-flex flex-column" style="height: 100%;">
<mitarbeiter-header
:endpoint="endpoint"
:domain="config.domain"
@selectionChanged="handleSelection"
/>
</div>
</template>
<template #bottom>
<div class="col" v-if="person_id!=null">
<fhc-header
ref="CoreDetailsHeaderRef"
:person_id="person_id"
:mitarbeiter_uid="this.mitarbeiter_uid"
typeHeader="mitarbeiter"
:domain="config.domain"
fotoEditable
@redirectToLeitung="handleSelection"
>
<template #uid>{{tile_MaUid}}</template>
<template #titleAlphaTile>persID</template>
<template #valueAlphaTile>{{tile_PersId}}</template>
</fhc-header>
<vertraege-mitarbeiter
ref="CoreTableVertraege"
:endpoint="endpoint"
:person_id="this.person_id"
:mitarbeiter_uid="this.mitarbeiter_uid"
/>
</div>
</template>
</vertical-split>
</div>
`
}
+3 -4
View File
@@ -48,7 +48,8 @@ export const CoreFilterCmpt = {
'nwNewEntry',
'click:new',
'tableBuilt',
'uuidDefined'
'uuidDefined',
'headerFilterOn'
],
props: {
onNwNewEntry: Function, // NOTE(chris): Hack to get the nwNewEntry listener into $props
@@ -345,6 +346,7 @@ export const CoreFilterCmpt = {
this.tabulator.on("dataFiltered", filters => {
this.filterActive = filters.length > 0;
this.$emit("headerFilterOn", this.filterActive);
});
},
updateTabulator() {
@@ -377,9 +379,6 @@ export const CoreFilterCmpt = {
this.tabulator.setHeaderFilterValue(filter.field, filter.value);
});
},
/**
*
*/
getFilter() {
if (this.selectedFilter === null)
this.startFetchCmpt(