mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-07-25 00:24:35 +00:00
Merge branch 'master' into feature-36185/requestAnrechnung_bootstrap3_zu_bootstrap5
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
@import './Fhc.css';
|
||||
@import './components/searchbar.css';
|
||||
@import './components/verticalsplit.css';
|
||||
@import './components/FilterComponent.css';
|
||||
@import './components/Tabs.css';
|
||||
@import './components/Notiz.css';
|
||||
|
||||
html {
|
||||
font-size: .875em;
|
||||
}
|
||||
|
||||
.navbar-dark .navbar-brand:focus {
|
||||
box-shadow: 0 0 0 .25rem rgba(13,110,253,.25);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.searchbar {
|
||||
margin-right: 0!important;
|
||||
}
|
||||
.searchbar > .input-group {
|
||||
margin-right: 0!important;
|
||||
}
|
||||
.searchbar > .input-group > * {
|
||||
border-radius: 0!important;
|
||||
}
|
||||
|
||||
.stv {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
.stv > header {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.stv > div {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
#sidebarMenu {
|
||||
width: 0%;
|
||||
}
|
||||
|
||||
.tabulator-row.disabled.tabulator-row-odd .tabulator-cell {
|
||||
color: var(--gray-400);
|
||||
}
|
||||
.tabulator-row.disabled.tabulator-row-even .tabulator-cell {
|
||||
color: var(--gray-500);
|
||||
}
|
||||
|
||||
/* Dropdown Toolbar Interessent, submenu */
|
||||
.dropend .dropdown-toggle.d-flex::after {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
#sidebarMenu {
|
||||
visibility: visible!important;
|
||||
transform: none;
|
||||
position: inherit;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 769px) {
|
||||
.stv-verband {
|
||||
max-height: calc(100% - 3rem);
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.stv-verband .p-treetable-tbody > tr > td {
|
||||
display: flex;
|
||||
padding-top: .25rem!important;
|
||||
padding-bottom: .25rem!important;
|
||||
}
|
||||
.stv-verband .p-treetable-tbody > tr > td > button {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.stv-verband .p-treetable-tbody > tr > td > span {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
|
||||
.stv-verband.p-tree {
|
||||
/*overflow: auto;*/
|
||||
}
|
||||
.stv-verband.p-tree .p-treenode-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.stv-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.stv-list > #filterTableDataset {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.toast.toast-success {
|
||||
color: #0f5132;
|
||||
background-color: #d1e7dd!important;
|
||||
border-color: #badbcc!important;
|
||||
}
|
||||
.toast.toast-danger {
|
||||
color: #842029;
|
||||
background-color: #f8d7da!important;
|
||||
border-color: #f5c2c7!important;
|
||||
}
|
||||
|
||||
.has-filter .fa-filter {
|
||||
color: var(--bs-success);
|
||||
}
|
||||
@@ -104,10 +104,20 @@
|
||||
}
|
||||
|
||||
|
||||
.tabulator {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.tabulator-cell .btn {
|
||||
padding: 0 .5rem;
|
||||
max-height: 22px;
|
||||
min-width: 30px;
|
||||
padding: 0 .7rem;
|
||||
min-height: 25px;
|
||||
min-width: 25px;
|
||||
}
|
||||
|
||||
.tabulator-row.tabulator-selectable:focus {
|
||||
box-shadow: 0 0 0 .24rem rgba(13,110,253,.25);
|
||||
z-index: 1;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.btn-select-col-selected
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
.notizTitle {
|
||||
color: darkred;
|
||||
}
|
||||
.notizText {
|
||||
color: darkblue;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
html {
|
||||
font-size: .75em;
|
||||
}
|
||||
@@ -121,8 +121,8 @@ export const CoreRESTClient = {
|
||||
*/
|
||||
getError: function(response) {
|
||||
|
||||
if (typeof response[CORE_REST_CLIENT_RETVAL] === "object"
|
||||
&& Object.keys(response[CORE_REST_CLIENT_RETVAL]).length > 0
|
||||
if (typeof response === "object"
|
||||
&& Object.keys(response).length > 0
|
||||
&& response.hasOwnProperty(CORE_REST_CLIENT_RETVAL))
|
||||
{
|
||||
return response[CORE_REST_CLIENT_RETVAL];
|
||||
@@ -136,7 +136,7 @@ export const CoreRESTClient = {
|
||||
*/
|
||||
getErrorCode: function(response) {
|
||||
|
||||
if (typeof response[CORE_REST_CLIENT_RETVAL] === "object" && response.hasOwnProperty(CORE_REST_CLIENT_ERROR))
|
||||
if (typeof response === "object" && response.hasOwnProperty(CORE_REST_CLIENT_ERROR))
|
||||
{
|
||||
return response[CORE_REST_CLIENT_ERROR];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import person from "./betriebsmittel/person.js";
|
||||
|
||||
export default {
|
||||
person
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
export default {
|
||||
getAllBetriebsmittel(url, config, params){
|
||||
return this.$fhcApi.get('api/frontend/v1/betriebsmittel/betriebsmittelP/getAllBetriebsmittel/' + params.type + '/' + params.id);
|
||||
},
|
||||
addNewBetriebsmittel(form, person_id, formData) {
|
||||
return this.$fhcApi.post(form, 'api/frontend/v1/betriebsmittel/betriebsmittelP/addNewBetriebsmittel/' +
|
||||
person_id, formData
|
||||
);
|
||||
},
|
||||
loadBetriebsmittel(betriebsmittelperson_id){
|
||||
return this.$fhcApi.post('api/frontend/v1/betriebsmittel/betriebsmittelP/loadBetriebsmittel/' + betriebsmittelperson_id);
|
||||
},
|
||||
updateBetriebsmittel(form, betriebsmittelperson_id, formData) {
|
||||
return this.$fhcApi.post(form, 'api/frontend/v1/betriebsmittel/betriebsmittelP/updateBetriebsmittel/' + betriebsmittelperson_id,
|
||||
formData);
|
||||
},
|
||||
deleteBetriebsmittel(betriebsmittelperson_id){
|
||||
return this.$fhcApi.post('api/frontend/v1/betriebsmittel/betriebsmittelP/deleteBetriebsmittel/' + betriebsmittelperson_id);
|
||||
},
|
||||
getTypenBetriebsmittel(){
|
||||
return this.$fhcApi.get('api/frontend/v1/betriebsmittel/betriebsmittelP/getTypenBetriebsmittel/');
|
||||
},
|
||||
loadInventarliste(query){
|
||||
return this.$fhcApi.get('api/frontend/v1/betriebsmittel/betriebsmittelP/loadInventarliste/' + query);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
export default {
|
||||
updatePersonUnrulyStatus(person_id, unrulyParam) {
|
||||
|
||||
try {
|
||||
const payload = {person_id, unruly: unrulyParam}
|
||||
const url = '/api/frontend/v1/checkperson/CheckPerson/updatePersonUnrulyStatus';
|
||||
return this.$fhcApi.post(url, payload, null);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
},
|
||||
filterPerson(payload, base = ''){
|
||||
|
||||
try {
|
||||
const url = base + '/api/frontend/v1/checkperson/CheckPerson/filterPerson';
|
||||
return axios.post(url, payload)
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (C) 2024 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 search from "./search.js";
|
||||
import phrasen from "./phrasen.js";
|
||||
import navigation from "./navigation.js";
|
||||
import filter from "./filter.js";
|
||||
import studstatus from "./studstatus.js";
|
||||
import stv from "./stv.js";
|
||||
import notiz from "./notiz.js";
|
||||
import betriebsmittel from "./betriebsmittel.js";
|
||||
import checkperson from "./checkperson.js";
|
||||
|
||||
export default {
|
||||
search,
|
||||
phrasen,
|
||||
navigation,
|
||||
filter,
|
||||
studstatus,
|
||||
stv,
|
||||
notiz,
|
||||
betriebsmittel,
|
||||
checkperson
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright (C) 2024 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 {
|
||||
saveCustomFilter(wsParams) {
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/filter/saveCustomFilter',
|
||||
{
|
||||
filterUniqueId: wsParams.filterUniqueId,
|
||||
filterType: wsParams.filterType,
|
||||
customFilterName: wsParams.customFilterName
|
||||
}
|
||||
);
|
||||
},
|
||||
removeCustomFilter(wsParams) {
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/filter/removeCustomFilter',
|
||||
{
|
||||
filterUniqueId: wsParams.filterUniqueId,
|
||||
filterType: wsParams.filterType,
|
||||
filterId: wsParams.filterId
|
||||
}
|
||||
);
|
||||
},
|
||||
applyFilterFields(wsParams) {
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/filter/applyFilterFields',
|
||||
{
|
||||
filterUniqueId: wsParams.filterUniqueId,
|
||||
filterType: wsParams.filterType,
|
||||
filterFields: wsParams.filterFields
|
||||
}
|
||||
);
|
||||
},
|
||||
addFilterField(wsParams) {
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/filter/addFilterField',
|
||||
{
|
||||
filterUniqueId: wsParams.filterUniqueId,
|
||||
filterType: wsParams.filterType,
|
||||
filterField: wsParams.filterField
|
||||
}
|
||||
);
|
||||
},
|
||||
removeFilterField(wsParams) {
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/filter/removeFilterField',
|
||||
{
|
||||
filterUniqueId: wsParams.filterUniqueId,
|
||||
filterType: wsParams.filterType,
|
||||
filterField: wsParams.filterField
|
||||
}
|
||||
);
|
||||
},
|
||||
getFilterById(wsParams) {
|
||||
return this.$fhcApi.get(
|
||||
'/api/frontend/v1/filter/getFilter',
|
||||
{
|
||||
filterUniqueId: wsParams.filterUniqueId,
|
||||
filterType: wsParams.filterType,
|
||||
filterId: wsParams.filterId
|
||||
}
|
||||
);
|
||||
},
|
||||
getFilter(wsParams) {
|
||||
return this.$fhcApi.get(
|
||||
'/api/frontend/v1/filter/getFilter',
|
||||
{
|
||||
filterUniqueId: wsParams.filterUniqueId,
|
||||
filterType: wsParams.filterType
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (C) 2024 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 {
|
||||
getHeader(navigation_page) {
|
||||
return this.$fhcApi.get(
|
||||
'/api/frontend/v1/navigation/header',
|
||||
{ navigation_page }
|
||||
);
|
||||
},
|
||||
getMenu: function(navigation_page) {
|
||||
return this.$fhcApi.get(
|
||||
'/api/frontend/v1/navigation/menu',
|
||||
{ navigation_page }
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import person from "./notiz/person.js";
|
||||
|
||||
export default {
|
||||
person
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
export default {
|
||||
getNotizen (url, config, params){
|
||||
return this.$fhcApi.get('api/frontend/v1/notiz/notizPerson/getNotizen/' + params.id + '/' + params.type);
|
||||
},
|
||||
getUid(){
|
||||
return this.$fhcApi.get('api/frontend/v1/notiz/notizPerson/getUid/');
|
||||
},
|
||||
addNewNotiz(id, formData) {
|
||||
return this.$fhcApi.post('api/frontend/v1/notiz/notizPerson/addNewNotiz/' + id,
|
||||
formData
|
||||
);
|
||||
},
|
||||
loadNotiz(notiz_id){
|
||||
return this.$fhcApi.post('api/frontend/v1/notiz/notizPerson/loadNotiz/', {
|
||||
notiz_id
|
||||
});
|
||||
},
|
||||
loadDokumente(notiz_id){
|
||||
return this.$fhcApi.post('api/frontend/v1/notiz/notizPerson/loadDokumente/', {
|
||||
notiz_id
|
||||
});
|
||||
},
|
||||
deleteNotiz(notiz_id, type_id, id){
|
||||
return this.$fhcApi.post('api/frontend/v1/notiz/notizPerson/deleteNotiz/', {
|
||||
notiz_id,
|
||||
type_id,
|
||||
id
|
||||
});
|
||||
},
|
||||
updateNotiz(notiz_id, formData){
|
||||
return this.$fhcApi.post('api/frontend/v1/notiz/notizPerson/updateNotiz/' + notiz_id,
|
||||
formData
|
||||
);
|
||||
},
|
||||
getMitarbeiter(event){
|
||||
return this.$fhcApi.get('api/frontend/v1/notiz/notizPerson/getMitarbeiter/' + event);
|
||||
},
|
||||
isBerechtigt(id, type_id){
|
||||
return this.$fhcApi.get('api/frontend/v1/notiz/notizPerson/isBerechtigt/');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (C) 2024 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 {
|
||||
loadCategory(category) {
|
||||
return this.$fhcApi.get('/api/frontend/v1/phrasen/loadModule/' + category);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (C) 2024 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 {
|
||||
search(searchsettings) {
|
||||
const url = '/api/frontend/v1/searchbar/search';
|
||||
return this.$fhcApi.post(url, searchsettings);
|
||||
},
|
||||
searchdummy(searchsettings) {
|
||||
const url = 'public/js/apps/api/dummyapi.php/Search';
|
||||
return this.$fhcApi.post(url, searchsettings);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* Copyright (C) 2024 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 {
|
||||
abmeldung: {
|
||||
getDetails(antrag_id, prestudent_id) {
|
||||
const url = '/api/frontend/v1/studstatus/abmeldung/'
|
||||
+ (antrag_id !== undefined ? 'getDetailsForAntrag/' + antrag_id : 'getDetailsForNewAntrag/' + prestudent_id);
|
||||
return this.$fhcApi.get(url);
|
||||
},
|
||||
create(stdsem, prestudent_id, grund) {
|
||||
return this.$fhcApi.post('/api/frontend/v1/studstatus/abmeldung/createAntrag', {
|
||||
studiensemester: stdsem,
|
||||
prestudent_id,
|
||||
grund
|
||||
}, {
|
||||
errorHandling: 'strict'
|
||||
});
|
||||
},
|
||||
cancel(antrag_id) {
|
||||
if (!Array.isArray(antrag_id))
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/abmeldung/cancelAntrag',
|
||||
{ antrag_id }
|
||||
);
|
||||
return Promise.allSettled(antrag_id.map(antrag => this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/abmeldung/cancelAntrag',
|
||||
{ antrag_id: antrag.studierendenantrag_id },
|
||||
{ errorHeader: '#' + antrag.studierendenantrag_id }
|
||||
)));
|
||||
}
|
||||
},
|
||||
unterbrechung: {
|
||||
getDetails(antrag_id, prestudent_id) {
|
||||
const url = '/api/frontend/v1/studstatus/unterbrechung/'
|
||||
+ (antrag_id !== undefined ? 'getDetailsForAntrag/' + antrag_id : 'getDetailsForNewAntrag/' + prestudent_id);
|
||||
return this.$fhcApi.get(url);
|
||||
},
|
||||
create(studiensemester, prestudent_id, grund, datum_wiedereinstieg, attachment) {
|
||||
return this.$fhcApi.post('/api/frontend/v1/studstatus/unterbrechung/createAntrag', {
|
||||
studiensemester,
|
||||
prestudent_id,
|
||||
grund,
|
||||
datum_wiedereinstieg,
|
||||
attachment
|
||||
}, {
|
||||
errorHandling: 'strict'
|
||||
});
|
||||
},
|
||||
cancel(antrag_id) {
|
||||
return this.$fhcApi.post('/api/frontend/v1/studstatus/unterbrechung/cancelAntrag', {
|
||||
antrag_id
|
||||
}, {
|
||||
errorHandling: 'strict'
|
||||
});
|
||||
}
|
||||
},
|
||||
wiederholung: {
|
||||
getDetails(prestudent_id) {
|
||||
const url = '/api/frontend/v1/studstatus/wiederholung/getDetailsForNewAntrag/' + prestudent_id;
|
||||
return this.$fhcApi.get(url)
|
||||
},
|
||||
getLvs(antrag_id) {
|
||||
const url = '/api/frontend/v1/studstatus/wiederholung/getLvs/' + antrag_id;
|
||||
return this.$fhcApi.get(url)
|
||||
},
|
||||
create(prestudent_id, studiensemester) {
|
||||
return this.$fhcApi.post('/api/frontend/v1/studstatus/wiederholung/createAntrag', {
|
||||
prestudent_id,
|
||||
studiensemester
|
||||
}, {
|
||||
errorHandling: 'strict'
|
||||
});
|
||||
},
|
||||
cancel(prestudent_id, studiensemester) {
|
||||
return this.$fhcApi.post('/api/frontend/v1/studstatus/wiederholung/cancelAntrag', {
|
||||
prestudent_id,
|
||||
studiensemester
|
||||
}, {
|
||||
errorHandling: 'strict'
|
||||
});
|
||||
},
|
||||
saveLvs(forbiddenLvs, mandatoryLvs) {
|
||||
return this.$fhcApi.post('/api/frontend/v1/studstatus/wiederholung/saveLvs', {
|
||||
forbiddenLvs,
|
||||
mandatoryLvs
|
||||
});
|
||||
}
|
||||
},
|
||||
leitung: {
|
||||
getStgs() {
|
||||
return this.$fhcApi.get('/api/frontend/v1/studstatus/leitung/getActiveStgs');
|
||||
},
|
||||
getAntraege(url, config, params) {
|
||||
return this.$fhcApi
|
||||
.get('/api/frontend/v1/studstatus/leitung/getAntraege/' + url)
|
||||
.then(res => res.data); // Return data for tabulator
|
||||
},
|
||||
getHistory(antrag_id) {
|
||||
return this.$fhcApi.get('/api/frontend/v1/studstatus/leitung/getHistory/' + antrag_id)
|
||||
},
|
||||
getPrestudents(query, signal) {
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/getPrestudents',
|
||||
{ query },
|
||||
{
|
||||
signal: signal,
|
||||
timeout: 30000
|
||||
}
|
||||
);
|
||||
},
|
||||
approve(antrag) {
|
||||
if (!Array.isArray(antrag))
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/approveAntrag',
|
||||
antrag
|
||||
);
|
||||
return Promise.allSettled(antrag.map(a => this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/approveAntrag',
|
||||
a,
|
||||
{ errorHeader: '#' + a.studierendenantrag_id }
|
||||
)));
|
||||
},
|
||||
reject(antrag) {
|
||||
if (!Array.isArray(antrag))
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/rejectAntrag',
|
||||
antrag
|
||||
);
|
||||
return Promise.allSettled(antrag.map(a => this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/rejectAntrag',
|
||||
a,
|
||||
{ errorHeader: '#' + a.studierendenantrag_id }
|
||||
)));
|
||||
},
|
||||
reopen(antrag) {
|
||||
if (!Array.isArray(antrag))
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/reopenAntrag',
|
||||
antrag
|
||||
);
|
||||
return Promise.allSettled(antrag.map(a => this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/reopenAntrag',
|
||||
a,
|
||||
{ errorHeader: '#' + a.studierendenantrag_id }
|
||||
)));
|
||||
},
|
||||
pause(antrag) {
|
||||
if (!Array.isArray(antrag))
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/pauseAntrag',
|
||||
antrag
|
||||
);
|
||||
return Promise.allSettled(antrag.map(a => this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/pauseAntrag',
|
||||
a,
|
||||
{ errorHeader: '#' + a.studierendenantrag_id }
|
||||
)));
|
||||
},
|
||||
unpause(antrag) {
|
||||
if (!Array.isArray(antrag))
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/unpauseAntrag',
|
||||
antrag
|
||||
);
|
||||
return Promise.allSettled(antrag.map(a => this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/unpauseAntrag',
|
||||
a,
|
||||
{ errorHeader: '#' + a.studierendenantrag_id }
|
||||
)));
|
||||
},
|
||||
object(antrag) {
|
||||
if (!Array.isArray(antrag))
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/objectAntrag',
|
||||
antrag
|
||||
);
|
||||
return Promise.allSettled(antrag.map(a => this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/objectAntrag',
|
||||
a,
|
||||
{ errorHeader: '#' + a.studierendenantrag_id }
|
||||
)));
|
||||
},
|
||||
approveObjection(antrag) {
|
||||
if (!Array.isArray(antrag))
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/approveObjection',
|
||||
antrag
|
||||
);
|
||||
return Promise.allSettled(antrag.map(a => this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/approveObjection',
|
||||
a,
|
||||
{ errorHeader: '#' + a.studierendenantrag_id }
|
||||
)));
|
||||
},
|
||||
denyObjection(antrag) {
|
||||
if (!Array.isArray(antrag))
|
||||
return this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/denyObjection',
|
||||
antrag
|
||||
);
|
||||
return Promise.allSettled(antrag.map(a => this.$fhcApi.post(
|
||||
'/api/frontend/v1/studstatus/leitung/denyObjection',
|
||||
a,
|
||||
{ errorHeader: '#' + a.studierendenantrag_id }
|
||||
)));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import verband from './stv/verband.js';
|
||||
import students from './stv/students.js';
|
||||
import filter from './stv/filter.js';
|
||||
import konto from './stv/konto.js';
|
||||
|
||||
export default {
|
||||
verband,
|
||||
students,
|
||||
filter,
|
||||
konto,
|
||||
configStudent() {
|
||||
return this.$fhcApi.get('api/frontend/v1/stv/config/student');
|
||||
},
|
||||
configStudents() {
|
||||
return this.$fhcApi.get('api/frontend/v1/stv/config/students');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
export default {
|
||||
getStg() {
|
||||
return this.$fhcApi.get('api/frontend/v1/stv/filter/getStg');
|
||||
},
|
||||
setStg(studiengang_kz) {
|
||||
return this.$fhcApi.post('api/frontend/v1/stv/filter/setStg', {
|
||||
studiengang_kz
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
export default {
|
||||
tabulatorConfig(config, self) {
|
||||
config.ajaxURL = 'api/frontend/v1/stv/konto/get';
|
||||
config.ajaxParams = () => {
|
||||
const params = {
|
||||
person_id: self.modelValue.person_id || self.modelValue.map(e => e.person_id),
|
||||
only_open: self.filter,
|
||||
studiengang_kz: self.studiengang_kz_intern ? self.stg_kz : ''
|
||||
};
|
||||
return params;
|
||||
};
|
||||
config.ajaxRequestFunc = (url, config, params) => this.$fhcApi.post(url, params, config);
|
||||
config.ajaxResponse = (url, params, response) => response.data;
|
||||
|
||||
return config;
|
||||
},
|
||||
checkDoubles(data) {
|
||||
return this.$fhcApi.post('api/frontend/v1/stv/konto/checkDoubles', data, {
|
||||
confirmErrorHandler: error => true
|
||||
});
|
||||
},
|
||||
insert(data) {
|
||||
return this.$fhcApi.post('api/frontend/v1/stv/konto/insert', data);
|
||||
},
|
||||
counter(data) {
|
||||
return this.$fhcApi.post('api/frontend/v1/stv/konto/counter', data);
|
||||
},
|
||||
edit(data) {
|
||||
return this.$fhcApi.post('api/frontend/v1/stv/konto/update', data);
|
||||
},
|
||||
delete(buchungsnr) {
|
||||
return this.$fhcApi.post('api/frontend/v1/stv/konto/delete', {buchungsnr});
|
||||
},
|
||||
getBuchungstypen() {
|
||||
return this.$fhcApi.get('api/frontend/v1/stv/konto/getBuchungstypen');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
export default {
|
||||
uid(uid) {
|
||||
return this.$fhcApi.getUri('api/frontend/v1/stv/students/uid/' + uid);
|
||||
},
|
||||
prestudent(prestudent_id) {
|
||||
return this.$fhcApi.getUri('api/frontend/v1/stv/students/prestudent/' + prestudent_id);
|
||||
},
|
||||
person(person_id) {
|
||||
return this.$fhcApi.getUri('api/frontend/v1/stv/students/person/' + person_id);
|
||||
},
|
||||
verband(relative_path) {
|
||||
return this.$fhcApi.getUri('api/frontend/v1/stv/students/' + relative_path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
export default {
|
||||
get() {
|
||||
return this.$fhcApi.get('api/frontend/v1/stv/verband');
|
||||
},
|
||||
favorites: {
|
||||
get() {
|
||||
return this.$fhcApi.get('api/frontend/v1/stv/favorites');
|
||||
},
|
||||
set(favorites) {
|
||||
return this.$fhcApi.post('api/frontend/v1/stv/favorites/set', {
|
||||
favorites
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {BismeldestichtagTabulatorOptions} from './TabulatorSetup.js';
|
||||
import {BismeldestichtagTabulatorEventHandlers} from './TabulatorSetup.js';
|
||||
import {BismeldestichtagHelper} from './BismeldestichtagHelper.js';
|
||||
|
||||
import {CoreFilterCmpt} from '../../components/filter/Filter.js';
|
||||
import {CoreNavigationCmpt} from '../../components/navigation/Navigation.js';
|
||||
@@ -29,8 +28,60 @@ import Phrasen from '../../plugin/Phrasen.js';
|
||||
const bismeldestichtagApp = Vue.createApp({
|
||||
data: function() {
|
||||
return {
|
||||
bismeldestichtagTabulatorOptions: BismeldestichtagTabulatorOptions,
|
||||
bismeldestichtagTabulatorEventHandlers: BismeldestichtagTabulatorEventHandlers,
|
||||
bismeldestichtagTabulatorOptions: {
|
||||
maxHeight: "100%",
|
||||
minHeight: 50,
|
||||
layout: 'fitColumns',
|
||||
index: 'meldestichtag_id',
|
||||
initialSort:[
|
||||
{column:"meldestichtag", dir:"desc"}
|
||||
],
|
||||
columns: [
|
||||
{title: 'Meldestichtag', field: 'meldestichtag', headerFilter: true, formatter: function(cell){
|
||||
return BismeldestichtagHelper.formatDate(cell.getValue());
|
||||
}
|
||||
},
|
||||
{title: 'Studiensemester', field: 'studiensemester_kurzbz', headerFilter: true, sorter:function(a, b, aRow, bRow, column, dir, sorterParams) {
|
||||
|
||||
//aRow, bRow - the row components for the values being compared
|
||||
let semesterStartA = new Date(aRow.getData().semester_start);
|
||||
let semesterStartB = new Date(bRow.getData().semester_start);
|
||||
|
||||
return semesterStartA - semesterStartB; // difference between studiensemester start dates
|
||||
}
|
||||
},
|
||||
{title: 'Semesterstart',field: 'semester_start', headerFilter: true, visible: false, formatter: function(cell){
|
||||
return BismeldestichtagHelper.formatDate(cell.getValue());
|
||||
}
|
||||
},
|
||||
{title: 'ID', field: 'meldestichtag_id', headerFilter: true, visible: false},
|
||||
{title: 'Insertamum', field: 'insertamum', headerFilter: true, visible: false},
|
||||
{title: 'Insertvon', field: 'insertvon', headerFilter: true, visible: false},
|
||||
{title: 'Löschen', field: 'loeschen', headerFilter: false, formatter:function(cell){
|
||||
return '<button class="btn btn-outline-secondary delete-btn" data-meldestichtag-id="'+cell.getRow().getIndex()+'">'+
|
||||
'<i class="fa fa-xmark"></i>'+
|
||||
'</button>';
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
bismeldestichtagTabulatorEventHandlers: [
|
||||
{
|
||||
event: "rowClick",
|
||||
handler: function(e, row) {
|
||||
if (e.target.nodeName == 'DIV') {
|
||||
let data = row.getData();
|
||||
alert(data.studiensemester_kurzbz + ': ' + BismeldestichtagHelper.formatDate(data.meldestichtag));
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
event: "tableBuilt",
|
||||
handler: () => {
|
||||
this.handlerStudiensemester();
|
||||
}
|
||||
}
|
||||
],
|
||||
meldestichtag: null, // date of Meldestichtag
|
||||
semList: null, // all Studiensemester for dropdown
|
||||
currSem: null, // selected Studiensemester
|
||||
@@ -47,9 +98,6 @@ const bismeldestichtagApp = Vue.createApp({
|
||||
CoreFetchCmpt,
|
||||
"datepicker": VueDatePicker
|
||||
},
|
||||
created() {
|
||||
this.handlerStudiensemester();
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Define Studiensemester call and method to be executed after the call
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (C) 2022 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 const BismeldestichtagHelper = {
|
||||
formatDate: function(date) {
|
||||
return date.replace(/(.*)-(.*)-(.*)/, '$3.$2.$1');
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022 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 const BismeldestichtagTabulatorOptions = {
|
||||
maxHeight: "100%",
|
||||
minHeight: 50,
|
||||
layout: 'fitColumns',
|
||||
index: 'meldestichtag_id',
|
||||
columns: [
|
||||
{title: 'Meldestichtag',field: 'meldestichtag', headerFilter: true, formatter: function(cell){
|
||||
return BismeldestichtagTabulatorHelperFunctions._formatDate(cell.getValue());
|
||||
}
|
||||
},
|
||||
{title: 'Studiensemester', field: 'studiensemester_kurzbz', headerFilter: true, sorter:function(a, b, aRow, bRow, column, dir, sorterParams) {
|
||||
|
||||
//aRow, bRow - the row components for the values being compared
|
||||
let semesterStartA = new Date(aRow.getData().semester_start);
|
||||
let semesterStartB = new Date(bRow.getData().semester_start);
|
||||
|
||||
return semesterStartA - semesterStartB; // difference between studiensemester start dates
|
||||
}
|
||||
},
|
||||
{title: 'Semesterstart',field: 'semester_start', headerFilter: true, visible: false, formatter: function(cell){
|
||||
return BismeldestichtagTabulatorHelperFunctions._formatDate(cell.getValue());
|
||||
}
|
||||
},
|
||||
{title: 'ID', field: 'meldestichtag_id', headerFilter: true, visible: false},
|
||||
{title: 'Insertamum', field: 'insertamum', headerFilter: true, visible: false},
|
||||
{title: 'Insertvon', field: 'insertvon', headerFilter: true, visible: false},
|
||||
{title: 'Löschen', field: 'loeschen', headerFilter: false, formatter:function(cell){
|
||||
return '<button class="btn btn-outline-secondary delete-btn" data-meldestichtag-id="'+cell.getRow().getIndex()+'">'+
|
||||
'<i class="fa fa-xmark"></i>'+
|
||||
'</button>';
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export const BismeldestichtagTabulatorEventHandlers = [
|
||||
{
|
||||
event: "rowClick",
|
||||
handler: function(e, row) {
|
||||
if (e.target.nodeName == 'DIV') {
|
||||
let data = row.getData();
|
||||
alert(data.studiensemester_kurzbz + ': ' + BismeldestichtagTabulatorHelperFunctions._formatDate(data.meldestichtag));
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
let BismeldestichtagTabulatorHelperFunctions = {
|
||||
_formatDate: function(date) {
|
||||
return date.replace(/(.*)-(.*)-(.*)/, '$3.$2.$1');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (C) 2024 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 FhcStudentenverwaltung from "../components/Stv/Studentenverwaltung.js";
|
||||
import fhcapifactory from "./api/fhcapifactory.js";
|
||||
|
||||
import Phrasen from "../plugin/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}/studentenverwaltung`, component: FhcStudentenverwaltung },
|
||||
{ path: `/${ciPath}/studentenverwaltung/prestudent/:prestudent_id`, component: FhcStudentenverwaltung },
|
||||
{ path: `/${ciPath}/studentenverwaltung/prestudent/:prestudent_id/:tab`, component: FhcStudentenverwaltung },
|
||||
{ path: `/${ciPath}/studentenverwaltung/student/:id`, component: FhcStudentenverwaltung },
|
||||
{ path: `/${ciPath}/studentenverwaltung/person/:person_id`, component: FhcStudentenverwaltung }
|
||||
]
|
||||
});
|
||||
|
||||
const app = Vue.createApp();
|
||||
|
||||
app
|
||||
.use(router)
|
||||
.use(primevue.config.default, {
|
||||
zIndex: {
|
||||
overlay: 1100
|
||||
}
|
||||
})
|
||||
.use(Phrasen)
|
||||
.mount('#main');
|
||||
+194
-194
@@ -1,195 +1,195 @@
|
||||
import {CoreFilterCmpt} from '../components/Filter.js';
|
||||
import {CoreNavigationCmpt} from '../components/Navigation.js';
|
||||
import verticalsplit from "../components/verticalsplit/verticalsplit.js";
|
||||
import searchbar from "../components/searchbar/searchbar.js";
|
||||
import fhcapifactory from "./api/fhcapifactory.js";
|
||||
import {CoreFilterCmpt} from '../components/filter/Filter.js';
|
||||
import {CoreNavigationCmpt} from '../components/navigation/Navigation.js';
|
||||
import CoreVerticalsplit from "../components/verticalsplit/verticalsplit.js";
|
||||
import CoreSearchbar from "../components/searchbar/searchbar.js";
|
||||
import FhcApi from "../plugin/FhcApi.js";
|
||||
|
||||
Vue.$fhcapi = fhcapifactory;
|
||||
|
||||
Vue.createApp({
|
||||
"data": function() {
|
||||
return {
|
||||
"title": "Test Search",
|
||||
"appSideMenuEntries": {},
|
||||
"searchbaroptions": {
|
||||
"types": [
|
||||
"person",
|
||||
"raum",
|
||||
"mitarbeiter",
|
||||
"student",
|
||||
"prestudent",
|
||||
"document",
|
||||
"cms",
|
||||
"organisationunit"
|
||||
],
|
||||
"actions": {
|
||||
"person": {
|
||||
"defaultaction": {
|
||||
"type": "link",
|
||||
"action": function(data) {
|
||||
//alert('person defaultaction ' + JSON.stringify(data));
|
||||
//window.location.href = data.profil;
|
||||
return data.profil;
|
||||
}
|
||||
},
|
||||
"childactions": [
|
||||
{
|
||||
"label": "testchildaction1",
|
||||
"icon": "fas fa-check-circle",
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('person testchildaction 01 ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "testchildaction2",
|
||||
"icon": "fas fa-file-csv",
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('person testchildaction 02 ' + JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"raum": {
|
||||
"defaultaction": {
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('raum defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
"childactions": [
|
||||
{
|
||||
"label": "Rauminformation",
|
||||
"icon": "fas fa-info-circle",
|
||||
"type": "link",
|
||||
"action": function(data) {
|
||||
return data.infolink;
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Raumreservierung",
|
||||
"icon": "fas fa-bookmark",
|
||||
"type": "link",
|
||||
"action": function(data) {
|
||||
return data.booklink;
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"employee": {
|
||||
"defaultaction": {
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('employee defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
"childactions": [
|
||||
{
|
||||
"label": "testchildaction1",
|
||||
"icon": "fas fa-address-book",
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('employee testchildaction 01 ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "testchildaction2",
|
||||
"icon": "fas fa-user-slash",
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('employee testchildaction 02 ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "testchildaction3",
|
||||
"icon": "fas fa-bell",
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('employee testchildaction 03 ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "testchildaction4",
|
||||
"icon": "fas fa-calculator",
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('employee testchildaction 04 ' + JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"organisationunit": {
|
||||
"defaultaction": {
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('organisationunit defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
"childactions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"searchbaroptions2": {
|
||||
"types": [
|
||||
"raum",
|
||||
"organisationunit"
|
||||
],
|
||||
"actions": {
|
||||
"raum": {
|
||||
"defaultaction": {
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('raum defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
"childactions": [
|
||||
{
|
||||
"label": "Rauminformation",
|
||||
"icon": "fas fa-info-circle",
|
||||
"type": "link",
|
||||
"action": function(data) {
|
||||
return data.infolink;
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Raumreservierung",
|
||||
"icon": "fas fa-bookmark",
|
||||
"type": "link",
|
||||
"action": function(data) {
|
||||
return data.booklink;
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"organisationunit": {
|
||||
"defaultaction": {
|
||||
"type": "function",
|
||||
"action": function(data) {
|
||||
alert('organisationunit defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
"childactions": []
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
"components": {
|
||||
"CoreNavigationCmpt": CoreNavigationCmpt,
|
||||
"CoreFilterCmpt": CoreFilterCmpt,
|
||||
"verticalsplit": verticalsplit,
|
||||
"searchbar": searchbar
|
||||
},
|
||||
"methods": {
|
||||
"newSideMenuEntryHandler": function(payload) {
|
||||
this.appSideMenuEntries = payload;
|
||||
},
|
||||
"searchfunction": function(searchsettings) {
|
||||
return Vue.$fhcapi.Search.search(searchsettings);
|
||||
},
|
||||
"searchfunctiondummy": function(searchsettings) {
|
||||
return Vue.$fhcapi.Search.searchdummy(searchsettings);
|
||||
}
|
||||
}
|
||||
}).mount('#main');
|
||||
const app = Vue.createApp({
|
||||
components: {
|
||||
CoreNavigationCmpt,
|
||||
CoreFilterCmpt,
|
||||
CoreVerticalsplit,
|
||||
CoreSearchbar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
title: "Test Search",
|
||||
appSideMenuEntries: {},
|
||||
searchbaroptions: {
|
||||
types: [
|
||||
"person",
|
||||
"raum",
|
||||
"mitarbeiter",
|
||||
"student",
|
||||
"prestudent",
|
||||
"document",
|
||||
"cms",
|
||||
"organisationunit"
|
||||
],
|
||||
actions: {
|
||||
person: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
action(data) {
|
||||
//alert('person defaultaction ' + JSON.stringify(data));
|
||||
//window.location.href = data.profil;
|
||||
return data.profil;
|
||||
}
|
||||
},
|
||||
childactions: [
|
||||
{
|
||||
label: "testchildaction1",
|
||||
icon: "fas fa-check-circle",
|
||||
type: "function",
|
||||
action(data) {
|
||||
alert('person testchildaction 01 ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "testchildaction2",
|
||||
icon: "fas fa-file-csv",
|
||||
type: "function",
|
||||
action(data) {
|
||||
alert('person testchildaction 02 ' + JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
raum: {
|
||||
defaultaction: {
|
||||
type: "function",
|
||||
action(data) {
|
||||
alert('raum defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
childactions: [
|
||||
{
|
||||
label: "Rauminformation",
|
||||
icon: "fas fa-info-circle",
|
||||
type: "link",
|
||||
action(data) {
|
||||
return data.infolink;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Raumreservierung",
|
||||
icon: "fas fa-bookmark",
|
||||
type: "link",
|
||||
action(data) {
|
||||
return data.booklink;
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
employee: {
|
||||
defaultaction: {
|
||||
type: "function",
|
||||
action(data) {
|
||||
alert('employee defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
childactions: [
|
||||
{
|
||||
label: "testchildaction1",
|
||||
icon: "fas fa-address-book",
|
||||
type: "function",
|
||||
action(data) {
|
||||
alert('employee testchildaction 01 ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "testchildaction2",
|
||||
icon: "fas fa-user-slash",
|
||||
type: "function",
|
||||
action(data) {
|
||||
alert('employee testchildaction 02 ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "testchildaction3",
|
||||
icon: "fas fa-bell",
|
||||
type: "function",
|
||||
action(data) {
|
||||
alert('employee testchildaction 03 ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "testchildaction4",
|
||||
icon: "fas fa-calculator",
|
||||
type: "function",
|
||||
action(data) {
|
||||
alert('employee testchildaction 04 ' + JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
organisationunit: {
|
||||
defaultaction: {
|
||||
type: "function",
|
||||
action(data) {
|
||||
alert('organisationunit defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
childactions: []
|
||||
}
|
||||
}
|
||||
},
|
||||
searchbaroptions2: {
|
||||
types: [
|
||||
"raum",
|
||||
"organisationunit"
|
||||
],
|
||||
actions: {
|
||||
raum: {
|
||||
defaultaction: {
|
||||
type: "function",
|
||||
action(data) {
|
||||
alert('raum defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
childactions: [
|
||||
{
|
||||
label: "Rauminformation",
|
||||
icon: "fas fa-info-circle",
|
||||
type: "link",
|
||||
action(data) {
|
||||
return data.infolink;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Raumreservierung",
|
||||
icon: "fas fa-bookmark",
|
||||
type: "link",
|
||||
action(data) {
|
||||
return data.booklink;
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
organisationunit: {
|
||||
defaultaction: {
|
||||
type: "function",
|
||||
action(data) {
|
||||
alert('organisationunit defaultaction ' + JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
childactions: []
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
newSideMenuEntryHandler(payload) {
|
||||
this.appSideMenuEntries = payload;
|
||||
},
|
||||
searchfunction(searchsettings) {
|
||||
return this.$fhcApi.factory.search.search(searchsettings);
|
||||
},
|
||||
searchfunctiondummy(searchsettings) {
|
||||
return this.$fhcApi.factory.search.searchdummy(searchsettings);
|
||||
}
|
||||
}
|
||||
});
|
||||
app.use(FhcApi)
|
||||
app.mount('#main');
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import StudierendenantragAntrag from "../../components/Studierendenantrag/Antrag.js";
|
||||
import StudierendenantragStatus from "../../components/Studierendenantrag/Status.js";
|
||||
import StudierendenantragInfoblock from "../../components/Studierendenantrag/Infoblock.js";
|
||||
import VueDatePicker from "../../components/vueDatepicker.js.php";
|
||||
import Phrasen from '../../plugin/Phrasen.js';
|
||||
|
||||
const app = Vue.createApp({
|
||||
components: {
|
||||
VueDatePicker,
|
||||
StudierendenantragAntrag,
|
||||
StudierendenantragStatus,
|
||||
StudierendenantragInfoblock
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (C) 2023 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 LvTemplateUebersicht from '../../../lehre/lvplanung/LvTemplateUebersicht.js';
|
||||
import {CoreNavigationCmpt} from '../../../components/navigation/Navigation.js';
|
||||
import FhcAlert from '../../../plugin/FhcAlert.js';
|
||||
import FhcApi from "../../../plugin/FhcApi.js";
|
||||
import Phrasen from "../../../plugin/Phrasen.js";
|
||||
|
||||
|
||||
const lvTemplatesApp = Vue.createApp({
|
||||
components: {
|
||||
CoreNavigationCmpt,
|
||||
LvTemplateUebersicht
|
||||
}
|
||||
});
|
||||
|
||||
lvTemplatesApp
|
||||
.use(primevue.config.default,{zIndex: {overlay: 9999}})
|
||||
.use(FhcAlert)
|
||||
.use(FhcApi)
|
||||
.use(Phrasen)
|
||||
.mount('#main')
|
||||
@@ -0,0 +1,408 @@
|
||||
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: {
|
||||
cisRoot: {
|
||||
from: 'cisRoot'
|
||||
},
|
||||
},
|
||||
props: {
|
||||
endpoint: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
typeId: String,
|
||||
id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
},
|
||||
uid: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabulatorOptions: {
|
||||
ajaxURL: 'dummy',
|
||||
ajaxRequestFunc: this.endpoint.getAllBetriebsmittel,
|
||||
ajaxParams: () => {
|
||||
return {
|
||||
type: this.typeId,
|
||||
id: this.id
|
||||
};
|
||||
},
|
||||
ajaxResponse: (url, params, response) => response.data,
|
||||
columns: [
|
||||
{title: "Nummer", field: "nummer", width: 150},
|
||||
{title: "PersonId", field: "person_id", visible: false},
|
||||
{title: "Typ", field: "betriebsmitteltyp", width: 125},
|
||||
{title: "Anmerkung", field: "anmerkung", visible: false},
|
||||
{title: "Retourdatum", field: "format_retour", width: 128},
|
||||
{title: "Beschreibung", field: "beschreibung"},
|
||||
{title: "UID", field: "uid", width: 87},
|
||||
{title: "Kaution", field: "kaution", visible: false},
|
||||
{title: "Ausgabedatum", field: "format_ausgabe", width: 144, visible: false},
|
||||
{title: "Betriebsmittel ID", field: "betriebsmittel_id", visible: false},
|
||||
{title: "Betriebsmittelperson ID", field: "betriebsmittelperson_id", visible: false},
|
||||
{
|
||||
title: 'Aktionen', field: 'actions',
|
||||
minWidth: 150, // Ensures Action-buttons will be always fully displayed
|
||||
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-print"></i>';
|
||||
button.title = 'Übernahmebestätigung drucken';
|
||||
let cellData = cell.getData();
|
||||
button.addEventListener(
|
||||
'click',
|
||||
(event) =>
|
||||
{
|
||||
let linkToPdf = this.cisRoot +
|
||||
'/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');
|
||||
button.className = 'btn btn-outline-secondary btn-action';
|
||||
button.innerHTML = '<i class="fa fa-edit"></i>';
|
||||
button.title = 'Betriebsmittel bearbeiten';
|
||||
button.addEventListener(
|
||||
'click',
|
||||
(event) =>
|
||||
this.actionEditBetriebsmittel(cell.getData().betriebsmittelperson_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 = 'Betriebsmittel löschen';
|
||||
button.addEventListener(
|
||||
'click',
|
||||
() =>
|
||||
this.actionDeleteBetriebsmittel(cell.getData().betriebsmittelperson_id)
|
||||
);
|
||||
container.append(button);
|
||||
|
||||
return container;
|
||||
},
|
||||
frozen: true
|
||||
}],
|
||||
layout: 'fitColumns',
|
||||
layoutColumnsOnNewData: false,
|
||||
height: '550',
|
||||
selectableRangeMode: 'click',
|
||||
selectable: true,
|
||||
persistenceID: 'core-betriebsmittel'
|
||||
},
|
||||
tabulatorEvents: [
|
||||
{
|
||||
event: 'tableBuilt',
|
||||
handler: async() => {
|
||||
|
||||
await this.$p.loadCategory(['wawi', 'global', 'infocenter']);
|
||||
|
||||
let cm = this.$refs.table.tabulator.columnManager;
|
||||
|
||||
cm.getColumnByField('nummer').component.updateDefinition({
|
||||
title: this.$p.t('wawi', 'nummer')
|
||||
});
|
||||
cm.getColumnByField('anmerkung').component.updateDefinition({
|
||||
title: this.$p.t('global', 'anmerkung')
|
||||
});
|
||||
cm.getColumnByField('format_retour').component.updateDefinition({
|
||||
title: this.$p.t('wawi', 'retourdatum')
|
||||
});
|
||||
cm.getColumnByField('kaution').component.updateDefinition({
|
||||
title: this.$p.t('infocenter', 'kaution')
|
||||
});
|
||||
cm.getColumnByField('format_ausgabe').component.updateDefinition({
|
||||
title: this.$p.t('wawi', 'ausgabedatum')
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
],
|
||||
listBetriebsmitteltyp: [],
|
||||
formData: {
|
||||
ausgegebenam : new Date(),
|
||||
betriebsmitteltyp: 'Zutrittskarte'
|
||||
},
|
||||
statusNew: true,
|
||||
filteredInventar: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
id() {
|
||||
this.$refs.table.reloadTable();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
actionEditBetriebsmittel(betriebsmittelperson_id) {
|
||||
this.statusNew = false;
|
||||
this
|
||||
.loadBetriebsmittel(betriebsmittelperson_id)
|
||||
.then(this.$refs.betriebsmittelModal.show);
|
||||
},
|
||||
actionNewBetriebsmittel() {
|
||||
this.resetModal();
|
||||
this.$refs.betriebsmittelModal.show();
|
||||
},
|
||||
actionDeleteBetriebsmittel(betriebsmittelperson_id) {
|
||||
this.$fhcAlert
|
||||
.confirmDelete()
|
||||
.then(result => result
|
||||
? betriebsmittelperson_id
|
||||
: Promise.reject({handled: true}))
|
||||
.then(this.endpoint.deleteBetriebsmittel)
|
||||
.then(result => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
|
||||
window.scrollTo(0, 0);
|
||||
this.reload();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
addNewBetriebsmittel() {
|
||||
//just append uid to formdata
|
||||
this.formData.uid = this.uid;
|
||||
if (this.formData.betriebsmitteltyp == 'Inventar')
|
||||
this.formData.betriebsmittel_id = this.formData.inventarData?.betriebsmittel_id;
|
||||
return this.endpoint
|
||||
.addNewBetriebsmittel(this.$refs.betriebsmittelData, this.id, this.formData)
|
||||
.then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||
this.$refs.betriebsmittelModal.hide();
|
||||
this.resetModal();
|
||||
window.scrollTo(0, 0);
|
||||
this.reload();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
updateBetriebsmittel(betriebsmittelperson_id) {
|
||||
if (this.formData.betriebsmitteltyp == 'Inventar')
|
||||
this.formData.betriebsmittel_id = this.formData.inventarData?.betriebsmittel_id;
|
||||
return this.endpoint
|
||||
.updateBetriebsmittel(this.$refs.betriebsmittelData, betriebsmittelperson_id, this.formData)
|
||||
.then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||
this.$refs.betriebsmittelModal.hide();
|
||||
this.resetModal();
|
||||
window.scrollTo(0, 0);
|
||||
this.reload();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
loadBetriebsmittel(betriebsmittelperson_id) {
|
||||
this.resetModal();
|
||||
this.statusNew = false;
|
||||
return this.endpoint
|
||||
.loadBetriebsmittel(betriebsmittelperson_id)
|
||||
.then(result => {
|
||||
this.formData = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
searchInventar(event) {
|
||||
const encodedQuery = encodeURIComponent(event.query);
|
||||
return this.endpoint
|
||||
.loadInventarliste(encodedQuery)
|
||||
.then(result => {
|
||||
this.filteredInventar = result.data;
|
||||
});
|
||||
},
|
||||
reload() {
|
||||
this.$refs.table.reloadTable();
|
||||
},
|
||||
resetModal() {
|
||||
this.formData = {};
|
||||
this.formData.ausgegebenam = new Date();
|
||||
this.formData.retouram = null;
|
||||
this.formData.betriebsmitteltyp = null;
|
||||
this.formData.nummer = null;
|
||||
this.formData.nummer2 = null;
|
||||
this.formData.kaution = null;
|
||||
this.formData.anmerkung = null;
|
||||
this.formData.beschreibung = null;
|
||||
this.statusNew = true;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
return this.endpoint
|
||||
.getTypenBetriebsmittel()
|
||||
.then(result => {
|
||||
this.listBetriebsmitteltyp = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
template: `
|
||||
<div class="core-betriebsmittel h-100 d-flex flex-column">
|
||||
<core-filter-cmpt
|
||||
ref="table"
|
||||
:tabulator-options="tabulatorOptions"
|
||||
:tabulator-events="tabulatorEvents"
|
||||
table-only
|
||||
:side-menu="false"
|
||||
reload
|
||||
new-btn-show
|
||||
new-btn-label="Betriebsmittel"
|
||||
@click:new="actionNewBetriebsmittel"
|
||||
>
|
||||
</core-filter-cmpt>
|
||||
|
||||
<!--Modal: betriebsmittelModal-->
|
||||
<bs-modal ref="betriebsmittelModal">
|
||||
<template #title>
|
||||
<p v-if="statusNew" class="fw-bold mt-3">{{$p.t('ui', 'add_betriebsmittel')}}</p>
|
||||
<p v-else class="fw-bold mt-3">{{$p.t('ui', 'edit_betriebsmittel')}}</p>
|
||||
</template>
|
||||
|
||||
<core-form class="row g-3" ref="betriebsmittelData">
|
||||
<legend>Details</legend>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="select"
|
||||
:label="$p.t('global/typ')"
|
||||
name="betriebsmitteltyp"
|
||||
v-model="formData.betriebsmitteltyp"
|
||||
:disabled="!statusNew"
|
||||
>
|
||||
<option
|
||||
v-for="entry in listBetriebsmitteltyp"
|
||||
:key="entry.betriebsmitteltyp"
|
||||
:value="entry.betriebsmitteltyp"
|
||||
>
|
||||
{{entry.beschreibung}}
|
||||
</option>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div v-if="formData.betriebsmitteltyp == 'Inventar'" class="row mb-3">
|
||||
<form-input
|
||||
type="autocomplete"
|
||||
:label="$p.t('wawi/inventarnummer')"
|
||||
name="betriebsmittel_id"
|
||||
v-model="formData.inventarData"
|
||||
option-label="dropdowntext"
|
||||
:suggestions="filteredInventar"
|
||||
@complete="searchInventar"
|
||||
:min-length="3"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div v-else-if="formData.inventarnummer" class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
:label="$p.t('wawi/inventarnummer')"
|
||||
name="inventarnummer"
|
||||
v-model="formData.inventarnummer"
|
||||
:disabled="!statusNew"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div v-if="formData.betriebsmitteltyp!='Inventar' && !formData.inventarnummer" class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
:label="$p.t('wawi/nummer')"
|
||||
name="nummer"
|
||||
v-model="formData.nummer"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div v-if="formData.betriebsmitteltyp!='Inventar' && !formData.inventarnummer" class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
:label="$p.t('wawi/nummer') + ' 2'"
|
||||
name="nummer2"
|
||||
v-model="formData.nummer2"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div v-if="formData.betriebsmitteltyp!='Inventar'" class="row mb-3">
|
||||
<form-input
|
||||
type="textarea"
|
||||
:label="$p.t('global/beschreibung')"
|
||||
name="beschreibung"
|
||||
v-model="formData.beschreibung"
|
||||
:disabled="formData.inventarnummer"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
:label="$p.t('infocenter/kaution')"
|
||||
name="kaution"
|
||||
v-model="formData.kaution"
|
||||
>
|
||||
</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>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="DatePicker"
|
||||
:label="$p.t('wawi/ausgegebenam')"
|
||||
name="ausgegebenam"
|
||||
v-model="formData.ausgegebenam"
|
||||
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="DatePicker"
|
||||
:label="$p.t('wawi/retouram')"
|
||||
name="retouram"
|
||||
v-model="formData.retouram"
|
||||
auto-apply
|
||||
:enable-time-picker="false"
|
||||
format="dd.MM.yyyy"
|
||||
preview-format="dd.MM.yyyy"
|
||||
:teleport="true"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</core-form>
|
||||
|
||||
<template #footer>
|
||||
<button type="button" class="btn btn-primary" @click="statusNew ? addNewBetriebsmittel() : updateBetriebsmittel(formData.betriebsmittelperson_id)">{{$p.t('ui', 'speichern')}}</button>
|
||||
</template>
|
||||
</bs-modal>
|
||||
</div>`
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import BsAlert from './Alert';
|
||||
import BsAlert from './Alert.js';
|
||||
|
||||
export default {
|
||||
mixins: [
|
||||
|
||||
@@ -93,7 +93,7 @@ export default {
|
||||
document.body.appendChild(wrapper);
|
||||
});
|
||||
},
|
||||
template: `<div ref="modal" class="bootstrap-modal modal" tabindex="-1" @[\`hide.bs.modal\`]="$emit('hideBsModal')" @[\`hidden.bs.modal\`]="$emit('hiddenBsModal')" @[\`hidePrevented.bs.modal\`]="$emit('hidePreventedBsModal')" @[\`show.bs.modal\`]="$emit('showBsModal')" @[\`shown.bs.modal\`]="$emit('shownBsModal')">
|
||||
template: `<div ref="modal" class="bootstrap-modal modal" tabindex="-1" @[\`hide.bs.modal\`]="$emit('hideBsModal', $event)" @[\`hidden.bs.modal\`]="$emit('hiddenBsModal', $event)" @[\`hidePrevented.bs.modal\`]="$emit('hidePreventedBsModal', $event)" @[\`show.bs.modal\`]="$emit('showBsModal', $event)" @[\`shown.bs.modal\`]="$emit('shownBsModal', $event)">
|
||||
<div class="modal-dialog" :class="dialogClass">
|
||||
<div class="modal-content">
|
||||
<div v-if="$slots.title" class="modal-header">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
import BsAlert from './Alert';
|
||||
import BsAlert from './Alert.js';
|
||||
|
||||
export default {
|
||||
mixins: [
|
||||
|
||||
@@ -99,8 +99,10 @@ export const CoreFetchCmpt = {
|
||||
*
|
||||
*/
|
||||
errorHandler: function(error) {
|
||||
if (error.response.data.retval)
|
||||
if (error.response?.data?.retval)
|
||||
this.setError(error.response.data.retval);
|
||||
else if (error.data?.message)
|
||||
this.setError(error.data.message);
|
||||
else
|
||||
this.setError(error.message);
|
||||
},
|
||||
|
||||
@@ -46,7 +46,8 @@ export default {
|
||||
const factory = Object.create(Object.getPrototypeOf(this.$fhcApi.factory), Object.getOwnPropertyDescriptors(this.$fhcApi.factory));
|
||||
factory.$fhcApi = {
|
||||
get: this.get,
|
||||
post: this.post
|
||||
post: this.post,
|
||||
_defaultErrorHandlers: this.$fhcApi._defaultErrorHandlers
|
||||
};
|
||||
return factory;
|
||||
}
|
||||
@@ -120,4 +121,4 @@ export default {
|
||||
<component :is="tag || 'FhcFragment'" v-bind="$attrs">
|
||||
<slot></slot>
|
||||
</component>`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,11 @@ export default {
|
||||
inputGroup: Boolean,
|
||||
type: String,
|
||||
name: String,
|
||||
containerClass: [String, Array, Object]
|
||||
containerClass: [String, Array, Object],
|
||||
label: String,
|
||||
// NOTE(chris): remove these from $attrs array to prevent doubled event listeners
|
||||
onInput: [Array, Function],
|
||||
'onUpdate:modelValue': [Array, Function]
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -160,12 +164,12 @@ export default {
|
||||
},
|
||||
modelValueCmp: {
|
||||
get() {
|
||||
if (this.$attrs.modelValue === undefined)
|
||||
if (!this.$attrs.hasOwnProperty('modelValue'))
|
||||
return this.modelValueDummy;
|
||||
return this.$attrs.modelValue;
|
||||
},
|
||||
set(v) {
|
||||
if (this.$attrs.modelValue === undefined)
|
||||
if (!this.$attrs.hasOwnProperty('modelValue'))
|
||||
this.modelValueDummy = v;
|
||||
this.$emit('update:modelValue', v);
|
||||
}
|
||||
@@ -174,7 +178,7 @@ export default {
|
||||
let uuid = this.$attrs.id;
|
||||
if (this.lcType == 'datepicker')
|
||||
uuid = this.$attrs.uid;
|
||||
if (!uuid && this.$attrs.label)
|
||||
if (!uuid && this.label)
|
||||
uuid = 'fhc-form-input';
|
||||
if (!uuid)
|
||||
return undefined;
|
||||
@@ -236,7 +240,7 @@ export default {
|
||||
},
|
||||
template: `
|
||||
<component :is="!hasContainer ? 'FhcFragment' : 'div'" class="position-relative" :class="autoContainerClass">
|
||||
<label v-if="$attrs.label && lcType != 'radio' && lcType != 'checkbox'" :for="idCmp">{{$attrs.label}}</label>
|
||||
<label v-if="label && lcType != 'radio' && lcType != 'checkbox'" :class="!noAutoClass && 'form-label'" :for="idCmp">{{label}}</label>
|
||||
<input v-if="tag == 'input'" :type="lcType" ref="input" v-model="modelValueCmp" v-bind="$attrs" :id="idCmp" :name="name" :class="validationClass" :modelValue="undefined" @input="clearValidationForThisName(); $emit('input', $event)">
|
||||
<textarea v-else-if="tag == 'textarea'" ref="input" v-model="modelValueCmp" v-bind="$attrs" :id="idCmp" :name="name" :class="validationClass" :modelValue="undefined" @input="clearValidationForThisName(); $emit('input', $event)"></textarea>
|
||||
<select v-else-if="tag == 'select'" ref="input" v-model="modelValueCmp" v-bind="$attrs" :id="idCmp" :name="name" :class="validationClass" :modelValue="undefined" @input="clearValidationForThisName(); $emit('input', $event)">
|
||||
@@ -272,6 +276,17 @@ export default {
|
||||
@update:model-value="clearValidationForThisName"
|
||||
>
|
||||
<slot></slot>
|
||||
<template #chip="data"><slot name="chip" v-bind="data"></slot></template>
|
||||
<template #header="data"><slot name="header" v-bind="data"></slot></template>
|
||||
<template #footer="data"><slot name="footer" v-bind="data"></slot></template>
|
||||
<template #option="data"><slot name="option" v-bind="data"></slot></template>
|
||||
<template #optiongroup="data"><slot name="optiongroup" v-bind="data"></slot></template>
|
||||
<template #content="data"><slot name="content" v-bind="data"></slot></template>
|
||||
<template #loader="data"><slot name="loader" v-bind="data"></slot></template>
|
||||
<template #empty="data"><slot name="empty" v-bind="data"></slot></template>
|
||||
<template #dropdownicon="data"><slot name="dropdownicon" v-bind="data"></slot></template>
|
||||
<template #removetokenicon="data"><slot name="removetokenicon" v-bind="data"></slot></template>
|
||||
<template #loadingicon="data"><slot name="loadingicon" v-bind="data"></slot></template>
|
||||
</component>
|
||||
<component
|
||||
v-else-if="tag == 'UploadDms'"
|
||||
@@ -303,7 +318,7 @@ export default {
|
||||
>
|
||||
<slot></slot>
|
||||
</component>
|
||||
<label v-if="$attrs.label && (lcType == 'radio' || lcType == 'checkbox')" :for="idCmp" :class="!noAutoClass && 'form-check-label'">{{$attrs.label}}</label>
|
||||
<label v-if="label && (lcType == 'radio' || lcType == 'checkbox')" :for="idCmp" :class="!noAutoClass && 'form-check-label'">{{label}}</label>
|
||||
<div v-if="valid !== undefined && feedback.length && !noFeedback" :class="feedbackClass">
|
||||
<template v-for="(msg, i) in feedback" :key="i">
|
||||
<hr v-if="i" class="m-0">
|
||||
@@ -312,4 +327,4 @@ export default {
|
||||
</div>
|
||||
</component>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import DmsItem from './Dms/Item.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DmsItem
|
||||
},
|
||||
emits: [
|
||||
'update:modelValue'
|
||||
],
|
||||
@@ -53,8 +58,14 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
modelValue(n) {
|
||||
if (n instanceof FileList)
|
||||
if (!n)
|
||||
return;
|
||||
if (n instanceof FileList) {
|
||||
if (!this.$refs.upload) {
|
||||
return;
|
||||
}
|
||||
return this.$refs.upload.files = n;
|
||||
}
|
||||
|
||||
const dt = new DataTransfer();
|
||||
const dms = [];
|
||||
@@ -75,13 +86,14 @@ export default {
|
||||
<div class="form-upload-dms">
|
||||
<input ref="upload" class="form-control" :class="inputClass" :id="id" :name="name" :multiple="multiple" type="file" @change="addFiles">
|
||||
<ul v-if="modelValue.length && multiple && !noList" class="list-unstyled m-0">
|
||||
<li v-for="(file, index) in modelValue" :key="index" class="d-flex mx-1 mt-1 align-items-start">
|
||||
<span class="col-auto"><i class="fa fa-file me-1"></i></span>
|
||||
<span class="col">{{ file.name }}</span>
|
||||
<button class="col-auto btn btn-outline-secondary btn-p-0" @click="removeFile(index)">
|
||||
<i class="fa fa-close"></i>
|
||||
</button>
|
||||
</li>
|
||||
<dms-item
|
||||
v-for="(file, index) in modelValue"
|
||||
:key="index"
|
||||
v-model="file"
|
||||
class="d-flex mx-1 mt-1 align-items-start"
|
||||
@delete="removeFile(index)"
|
||||
>
|
||||
</dms-item>
|
||||
</ul>
|
||||
</div>`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
export default {
|
||||
emits: [
|
||||
'delete'
|
||||
],
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [File, Object],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
preview: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
modelValue(n) {
|
||||
if (n.type == 'application/x.fhc-dms+json') {
|
||||
n.text().then(result => {
|
||||
const obj = JSON.parse(result);
|
||||
this.preview = obj.preview || '';
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<li class="form-upload-dms-item">
|
||||
<span class="col-auto"><i class="fa fa-file me-1"></i></span>
|
||||
<span class="col">{{ modelValue.name }}</span>
|
||||
<a v-if="preview" :href="preview" target="_blank" class="col-auto btn btn-outline-secondary btn-p-0 me-1">
|
||||
<i class="fa fa-download"></i>
|
||||
</a>
|
||||
<button class="col-auto btn btn-outline-secondary btn-p-0" @click="$emit('delete')">
|
||||
<i class="fa fa-close"></i>
|
||||
</button>
|
||||
</li>`
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
export default {
|
||||
props: [
|
||||
'dateien',
|
||||
'multiupload'
|
||||
],
|
||||
computed: {
|
||||
intDateien:{
|
||||
get() {
|
||||
return this.dateien;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:dateien', value);
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleFileChange(event) {
|
||||
this.intDateien = event.target.files;
|
||||
},
|
||||
deleteFile(id) {
|
||||
const dt = new DataTransfer();
|
||||
const files = this.$refs.upload.files;
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
if (id !== i)
|
||||
dt.items.add(file);
|
||||
}
|
||||
|
||||
this.$refs.upload.files = dt.files;
|
||||
this.$emit('update:dateien', dt.files);
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<span>
|
||||
<input type="file" :multiple="multiupload" ref="upload" @change="handleFileChange" v-model="dateien"/>
|
||||
</span>
|
||||
|
||||
<span>
|
||||
<ul>
|
||||
<li v-for="(datei,index) in dateien">
|
||||
<button>{{datei.name}}</button><button class="text-danger" @click="deleteFile(index)"><i class="fa fa-remove"></i></button>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
</template>`
|
||||
}
|
||||
@@ -38,4 +38,4 @@ export default {
|
||||
</div>
|
||||
</template>
|
||||
`
|
||||
};
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -55,4 +55,4 @@ export default {
|
||||
</component>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,16 @@
|
||||
import {CoreFetchCmpt} from '../../Fetch.js';
|
||||
import CoreForm from '../../Form/Form.js';
|
||||
import FormValidation from '../../Form/Validation.js';
|
||||
import FormInput from '../../Form/Input.js';
|
||||
|
||||
var _uuid = 0;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFetchCmpt
|
||||
CoreFetchCmpt,
|
||||
CoreForm,
|
||||
FormValidation,
|
||||
FormInput
|
||||
},
|
||||
emits: [
|
||||
'setInfos',
|
||||
@@ -18,9 +24,8 @@ export default {
|
||||
return {
|
||||
data: null,
|
||||
saving: false,
|
||||
errors: {
|
||||
grund: [],
|
||||
default: []
|
||||
formData: {
|
||||
grund: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -34,24 +39,14 @@ export default {
|
||||
case 'Genehmigt': return 'success';
|
||||
default: return 'warning';
|
||||
}
|
||||
},
|
||||
loadUrl() {
|
||||
if (this.studierendenantragId)
|
||||
return '/components/Antrag/Abmeldung/getDetailsForAntrag/'+
|
||||
this.studierendenantragId;
|
||||
return '/components/Antrag/Abmeldung/getDetailsForNewAntrag/' +
|
||||
this.prestudentId;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
load() {
|
||||
return axios.get(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
this.loadUrl
|
||||
).then(
|
||||
result => {
|
||||
this.data = result.data.retval;
|
||||
return this.$fhcApi.factory
|
||||
.studstatus.abmeldung.getDetails(this.studierendenantragId, this.prestudentId)
|
||||
.then(result => {
|
||||
this.data = result.data;
|
||||
if (this.data.status) {
|
||||
const msg = (this.data.status == 'Pause' && this.data.status_insertvon == "Studienabbruch") ? Vue.computed(() => {
|
||||
let status = this.$p.t('studierendenantrag/status_stop');
|
||||
@@ -63,8 +58,8 @@ export default {
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
createAntrag() {
|
||||
bootstrap.Modal.getOrCreateInstance(this.$refs.modal).hide();
|
||||
@@ -73,52 +68,40 @@ export default {
|
||||
severity: 'warning'
|
||||
});
|
||||
this.saving = true;
|
||||
for(var k in this.errors)
|
||||
this.errors[k] = [];
|
||||
axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Abmeldung/createAntrag/', {
|
||||
studiensemester: this.data.studiensemester_kurzbz,
|
||||
prestudent_id: this.data.prestudent_id,
|
||||
grund: this.$refs.grund.value
|
||||
}
|
||||
).then(
|
||||
result => {
|
||||
if (result.data.error)
|
||||
{
|
||||
for (var k in result.data.retval)
|
||||
{
|
||||
if (this.errors[k] !== undefined)
|
||||
this.errors[k].push(result.data.retval[k]);
|
||||
else
|
||||
this.errors.default.push(result.data.retval[k]);
|
||||
}
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_error')})),
|
||||
severity: 'danger'
|
||||
|
||||
this.$refs.form.clearValidation();
|
||||
this.$refs.form.factory
|
||||
.studstatus.abmeldung.create(
|
||||
this.data.studiensemester_kurzbz,
|
||||
this.data.prestudent_id,
|
||||
this.formData.grund
|
||||
)
|
||||
.then(result => {
|
||||
|
||||
if (result.data === true)
|
||||
document.location += "";
|
||||
|
||||
this.data = result.data;
|
||||
if (this.data.status)
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.data.retval === true)
|
||||
document.location += "";
|
||||
this.data = result.data.retval;
|
||||
if (this.data.status) {
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
}
|
||||
else
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_open')})),
|
||||
severity:'success'
|
||||
});
|
||||
}
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_open')})),
|
||||
severity:'success'
|
||||
});
|
||||
this.saving = false;
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(error => {
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_error')})),
|
||||
severity: 'danger'
|
||||
});
|
||||
this.saving = false;
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
});
|
||||
},
|
||||
cancelAntrag() {
|
||||
this.$emit('setStatus', {
|
||||
@@ -126,51 +109,37 @@ export default {
|
||||
severity: 'warning'
|
||||
});
|
||||
this.saving = true;
|
||||
for(var k in this.errors)
|
||||
this.errors[k] = [];
|
||||
axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Abmeldung/cancelAntrag/', {
|
||||
antrag_id: this.data.studierendenantrag_id
|
||||
}
|
||||
).then(
|
||||
result => {
|
||||
if (result.data.error)
|
||||
{
|
||||
for (var k in result.data.retval)
|
||||
{
|
||||
if (this.errors[k] !== undefined)
|
||||
this.errors[k].push(result.data.retval[k]);
|
||||
else
|
||||
this.errors.default.push(result.data.retval[k]);
|
||||
}
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_error')})),
|
||||
severity:'danger'
|
||||
|
||||
this.$refs.form.clearValidation();
|
||||
this.$refs.form.factory
|
||||
.studstatus.abmeldung.cancel(
|
||||
this.data.studierendenantrag_id
|
||||
)
|
||||
.then(result => {
|
||||
if (Number.isInteger(result.data))
|
||||
document.location = document.location.replace(/abmeldung\/([0-9]*)\/[0-9]*[\/]?$/, 'abmeldung/$1') + "/" + result.data;
|
||||
|
||||
this.data = result.data;
|
||||
if (this.data.status)
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Number.isInteger(result.data.retval)) {
|
||||
document.location = document.location.replace(/abmeldung\/([0-9]*)\/[0-9]*[\/]?$/, 'abmeldung/$1') + "/" + result.data.retval;
|
||||
}
|
||||
this.data = result.data.retval;
|
||||
if (this.data.status) {
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
}
|
||||
else
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_cancelled')})),
|
||||
severity: 'danger'
|
||||
});
|
||||
}
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_cancelled')})),
|
||||
severity: 'danger'
|
||||
});
|
||||
this.saving = false;
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(error => {
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_error')})),
|
||||
severity: 'danger'
|
||||
});
|
||||
this.saving = false;
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -179,10 +148,9 @@ export default {
|
||||
template: `
|
||||
<div class="studierendenantrag-form-abmeldung">
|
||||
<core-fetch-cmpt :api-function="load">
|
||||
<div class="row">
|
||||
<core-form ref="form" class="row">
|
||||
<div class="col-12">
|
||||
<div v-for="error in errors.default" class="alert alert-danger" role="alert" v-html="error">
|
||||
</div>
|
||||
<form-validation></form-validation>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'studiengang')}}</th>
|
||||
@@ -219,19 +187,16 @@ export default {
|
||||
<pre>{{data.grund}}</pre>
|
||||
</div>
|
||||
<div v-else class="col-sm-6 mb-3">
|
||||
<label :for="'studierendenantrag-form-abmeldung-' + uuid + '-grund'" class="form-label">Grund:</label>
|
||||
<textarea
|
||||
class="form-control"
|
||||
:class="{'is-invalid': errors.grund.length}"
|
||||
:id="'studierendenantrag-form-abmeldung-' + uuid + '-grund'"
|
||||
<form-input
|
||||
type="textarea"
|
||||
label="Grund:"
|
||||
v-model="formData.grund"
|
||||
name="grund"
|
||||
rows="5"
|
||||
:disabled="saving"
|
||||
ref="grund"
|
||||
required
|
||||
></textarea>
|
||||
<div v-if="errors.grund.length" class="invalid-feedback">
|
||||
{{errors.grund.join(".")}}
|
||||
</div>
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-12 text-end">
|
||||
<button
|
||||
@@ -286,13 +251,13 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</core-form>
|
||||
|
||||
<template v-slot:error="{errorMessage}">
|
||||
<div class="alert alert-danger m-0" role="alert">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</template>
|
||||
</core-fetch-cmpt>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
</div>`
|
||||
}
|
||||
@@ -1,10 +1,16 @@
|
||||
import {CoreFetchCmpt} from '../../Fetch.js';
|
||||
import CoreForm from '../../Form/Form.js';
|
||||
import FormValidation from '../../Form/Validation.js';
|
||||
import FormInput from '../../Form/Input.js';
|
||||
|
||||
var _uuid = 0;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFetchCmpt
|
||||
CoreFetchCmpt,
|
||||
CoreForm,
|
||||
FormValidation,
|
||||
FormInput
|
||||
},
|
||||
emits: [
|
||||
'setInfos',
|
||||
@@ -18,10 +24,10 @@ export default {
|
||||
return {
|
||||
data: null,
|
||||
saving: false,
|
||||
errors: {
|
||||
grund: [],
|
||||
default: []
|
||||
}
|
||||
formData: {
|
||||
grund: ''
|
||||
},
|
||||
unrulyInternal: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -35,24 +41,14 @@ export default {
|
||||
case 'Abgemeldet': return 'success';
|
||||
default: return 'warning';
|
||||
}
|
||||
},
|
||||
loadUrl() {
|
||||
if (this.studierendenantragId)
|
||||
return '/components/Antrag/Abmeldung/getDetailsForAntrag/'+
|
||||
this.studierendenantragId;
|
||||
return '/components/Antrag/Abmeldung/getDetailsForNewAntrag/' +
|
||||
this.prestudentId;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
load() {
|
||||
return axios.get(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
this.loadUrl
|
||||
).then(
|
||||
result => {
|
||||
this.data = result.data.retval;
|
||||
return this.$fhcApi.factory
|
||||
.studstatus.abmeldung.getDetails(this.studierendenantragId, this.prestudentId)
|
||||
.then(result => {
|
||||
this.data = result.data;
|
||||
if (this.data.status) {
|
||||
const msg = (this.data.status == 'Pause' && this.data.status_insertvon == "Studienabbruch") ? Vue.computed(() => {
|
||||
let status = this.$p.t('studierendenantrag/status_stop');
|
||||
@@ -64,8 +60,7 @@ export default {
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
createAntrag() {
|
||||
bootstrap.Modal.getOrCreateInstance(this.$refs.modal).hide();
|
||||
@@ -74,73 +69,70 @@ export default {
|
||||
severity: 'warning'
|
||||
});
|
||||
this.saving = true;
|
||||
for(var k in this.errors)
|
||||
this.errors[k] = [];
|
||||
axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Abmeldung/createAntrag/', {
|
||||
studiensemester: this.data.studiensemester_kurzbz,
|
||||
prestudent_id: this.data.prestudent_id,
|
||||
grund: this.$refs.grund.value
|
||||
}
|
||||
).then(
|
||||
result => {
|
||||
if (result.data.error)
|
||||
{
|
||||
for (var k in result.data.retval)
|
||||
{
|
||||
if (this.errors[k] !== undefined)
|
||||
this.errors[k].push(result.data.retval[k]);
|
||||
else
|
||||
this.errors.default.push(result.data.retval[k]);
|
||||
}
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_error')})),
|
||||
severity: 'danger'
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.data.retval === true)
|
||||
document.location += "";
|
||||
this.data = result.data.retval;
|
||||
if (this.data.status) {
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
}
|
||||
else
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_open')})),
|
||||
severity:'success'
|
||||
});
|
||||
}
|
||||
this.saving = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
appendDropDownText(event){
|
||||
let templateText = this.$refs.grund;
|
||||
|
||||
if(event.target.value)
|
||||
{
|
||||
let templateT= this.$p.t('studierendenantrag', event.target.value);
|
||||
templateText.value = templateT;
|
||||
}
|
||||
else
|
||||
templateText.value = '';
|
||||
this.$refs.form.clearValidation();
|
||||
this.$refs.form.factory
|
||||
.studstatus.abmeldung.create(
|
||||
this.data.studiensemester_kurzbz,
|
||||
this.data.prestudent_id,
|
||||
this.formData.grund
|
||||
)
|
||||
.then(result => {
|
||||
|
||||
if(this.unrulyInternal) {
|
||||
this.$fhcApi.factory.checkperson.updatePersonUnrulyStatus(this.data.person_id, true).then(
|
||||
(res)=> {
|
||||
if(res?.meta?.status === "success") {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('studierendenantrag', 'antrag_unruly_updated'))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (result.data === true)
|
||||
document.location += "";
|
||||
|
||||
this.data = result.data;
|
||||
if (this.data.status)
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
else
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_open')})),
|
||||
severity:'success'
|
||||
});
|
||||
this.saving = false;
|
||||
})
|
||||
.catch(error => {
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_error')})),
|
||||
severity: 'danger'
|
||||
});
|
||||
this.saving = false;
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
});
|
||||
},
|
||||
appendDropDownText(event) {
|
||||
this.formData.grund = event.target.value
|
||||
? this.$p.t('studierendenantrag', event.target.value)
|
||||
: '';
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.uuid = _uuid++;
|
||||
},
|
||||
watch: {
|
||||
'formData.grund'(newVal) {
|
||||
this.unrulyInternal = (newVal === this.$p.t('studierendenantrag', 'textLong_unruly'))
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="studierendenantrag-form-abmeldung">
|
||||
<core-fetch-cmpt :api-function="load">
|
||||
<div class="row">
|
||||
<core-form ref="form" class="row">
|
||||
<div class="col-12">
|
||||
<form-validation></form-validation>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'studiengang')}}</th>
|
||||
@@ -181,8 +173,7 @@ export default {
|
||||
<div v-else class="col-sm-6 mb-3">
|
||||
<label :for="'studierendenantrag-form-abmeldung-' + uuid + '-grund'" class="form-label">Grund:</label>
|
||||
<div class="mb-2">
|
||||
<select name="grundAv" @change="appendDropDownText
|
||||
($event)">
|
||||
<select name="grundAv" class="form-select" @change="appendDropDownText">
|
||||
<option value="" > --- bitte auswählen, sofern zutreffend ---- </option>
|
||||
<option value="textLong_NichtantrittStudium">{{$p.t('studierendenantrag', 'dropdown_NichtantrittStudium')}}
|
||||
</option>
|
||||
@@ -197,21 +188,23 @@ export default {
|
||||
<option value="textLong_plageat">{{$p.t('studierendenantrag', 'dropdown_plageat')}}
|
||||
</option>
|
||||
<option value="textLong_MissingZgv">{{$p.t('studierendenantrag', 'dropdown_MissingZgv')}}
|
||||
</option>
|
||||
</option>
|
||||
<!--
|
||||
<option value="textLong_unruly">{{$p.t('studierendenantrag', 'dropdown_unruly')}}
|
||||
</option>
|
||||
-->
|
||||
</select>
|
||||
</div>
|
||||
<textarea
|
||||
class="form-control"
|
||||
:class="{'is-invalid': errors.grund.length}"
|
||||
</div>
|
||||
<form-input
|
||||
type="textarea"
|
||||
v-model="formData.grund"
|
||||
name="grund"
|
||||
:id="'studierendenantrag-form-abmeldung-' + uuid + '-grund'"
|
||||
rows="5"
|
||||
:disabled="saving"
|
||||
ref="grund"
|
||||
required
|
||||
></textarea>
|
||||
<div v-if="errors.grund.length" class="invalid-feedback">
|
||||
{{errors.grund.join(".")}}
|
||||
</div>
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="col-12 text-end">
|
||||
@@ -257,7 +250,7 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</core-form>
|
||||
|
||||
<template v-slot:error="{errorMessage}">
|
||||
<div class="alert alert-danger m-0" role="alert">
|
||||
@@ -265,6 +258,5 @@ export default {
|
||||
</div>
|
||||
</template>
|
||||
</core-fetch-cmpt>
|
||||
</div>
|
||||
`
|
||||
</div>`
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import {CoreFetchCmpt} from '../../Fetch.js';
|
||||
import VueDatepicker from '../../vueDatepicker.js.php';
|
||||
import CoreForm from '../../Form/Form.js';
|
||||
import FormValidation from '../../Form/Validation.js';
|
||||
import FormInput from '../../Form/Input.js';
|
||||
|
||||
var _uuid = 0;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFetchCmpt,
|
||||
VueDatepicker
|
||||
CoreForm,
|
||||
FormValidation,
|
||||
FormInput
|
||||
},
|
||||
emits: [
|
||||
'setInfos',
|
||||
@@ -20,12 +23,7 @@ export default {
|
||||
return {
|
||||
data: null,
|
||||
saving: false,
|
||||
errors: {
|
||||
grund: [],
|
||||
studiensemester: [],
|
||||
datum_wiedereinstieg: [],
|
||||
default: []
|
||||
},
|
||||
attachment: [],
|
||||
stsem: null,
|
||||
currentWiedereinstieg: '',
|
||||
siteUrl: FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
@@ -45,13 +43,6 @@ export default {
|
||||
default: return 'warning';
|
||||
}
|
||||
},
|
||||
loadUrl() {
|
||||
if (this.studierendenantragId)
|
||||
return '/components/Antrag/Unterbrechung/getDetailsForAntrag/'+
|
||||
this.studierendenantragId;
|
||||
return '/components/Antrag/Unterbrechung/getDetailsForNewAntrag/' +
|
||||
this.prestudentId;
|
||||
},
|
||||
datumWsFormatted() {
|
||||
let datumUnformatted = '';
|
||||
|
||||
@@ -81,26 +72,24 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
load() {
|
||||
return axios.get(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
this.loadUrl
|
||||
).then(
|
||||
result => {
|
||||
this.data = result.data.retval;
|
||||
if (this.data.status) {
|
||||
const msg = (this.data.status == 'Pause' && this.data.status_insertvon == "Studienabbruch") ? Vue.computed(() => {
|
||||
let status = this.$p.t('studierendenantrag/status_stop');
|
||||
return this.$p.t('studierendenantrag', 'status_x', {status});
|
||||
}) : Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp}));
|
||||
this.$emit("setStatus", {
|
||||
msg,
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
return this.$fhcApi.factory
|
||||
.studstatus.unterbrechung.getDetails(this.studierendenantragId, this.prestudentId)
|
||||
.then(
|
||||
result => {
|
||||
this.data = result.data;
|
||||
if (this.data.status) {
|
||||
const msg = (this.data.status == 'Pause' && this.data.status_insertvon == "Studienabbruch") ? Vue.computed(() => {
|
||||
let status = this.$p.t('studierendenantrag/status_stop');
|
||||
return this.$p.t('studierendenantrag', 'status_x', {status});
|
||||
}) : Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp}));
|
||||
this.$emit("setStatus", {
|
||||
msg,
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
);
|
||||
);
|
||||
},
|
||||
createAntrag() {
|
||||
this.$emit('setStatus', {
|
||||
@@ -108,63 +97,41 @@ export default {
|
||||
severity: 'warning'
|
||||
});
|
||||
this.saving = true;
|
||||
for(var k in this.errors)
|
||||
this.errors[k] = [];
|
||||
|
||||
var formData = new FormData();
|
||||
var attachment = this.$refs.attachment;
|
||||
formData.append("attachment", attachment.files[0]);
|
||||
formData.append("studiensemester", this.stsem !== null && this.data.studiensemester[this.stsem].studiensemester_kurzbz);
|
||||
formData.append("prestudent_id", this.data.prestudent_id);
|
||||
formData.append("grund", this.$refs.grund.value);
|
||||
formData.append("datum_wiedereinstieg", this.stsem !== null && this.currentWiedereinstieg);
|
||||
this.$refs.form.clearValidation();
|
||||
this.$refs.form.factory
|
||||
.studstatus.unterbrechung.create(
|
||||
this.stsem !== null && this.data.studiensemester[this.stsem].studiensemester_kurzbz,
|
||||
this.data.prestudent_id,
|
||||
this.data.grund,
|
||||
this.stsem !== null && this.currentWiedereinstieg,
|
||||
this.attachment
|
||||
)
|
||||
.then(result => {
|
||||
if (Number.isInteger(result.data))
|
||||
document.location += "/" + result.data;
|
||||
|
||||
axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Unterbrechung/createAntrag/',
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
}
|
||||
).then(
|
||||
result => {
|
||||
if (result.data.error)
|
||||
{
|
||||
for (var k in result.data.retval)
|
||||
{
|
||||
if (this.errors[k] !== undefined)
|
||||
this.errors[k].push(result.data.retval[k]);
|
||||
else
|
||||
this.errors.default.push(result.data.retval[k]);
|
||||
}
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_error')})),
|
||||
severity: 'danger'
|
||||
this.data = result.data;
|
||||
if (this.data.status)
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Number.isInteger(result.data.retval))
|
||||
document.location += "/" + result.data.retval;
|
||||
this.data = result.data.retval;
|
||||
if (this.data.status) {
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
}
|
||||
else
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_created')})),
|
||||
severity: 'info'
|
||||
});
|
||||
}
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_created')})),
|
||||
severity: 'info'
|
||||
});
|
||||
this.saving = false;
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(error => {
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_error')})),
|
||||
severity: 'danger'
|
||||
});
|
||||
this.saving = false;
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
});
|
||||
},
|
||||
cancelAntrag() {
|
||||
this.$emit('setStatus', {
|
||||
@@ -172,63 +139,45 @@ export default {
|
||||
severity: 'warning'
|
||||
});
|
||||
this.saving = true;
|
||||
for(var k in this.errors)
|
||||
this.errors[k] = [];
|
||||
axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Unterbrechung/cancelAntrag/', {
|
||||
antrag_id: this.data.studierendenantrag_id
|
||||
}
|
||||
).then(
|
||||
result => {
|
||||
if (result.data.error)
|
||||
{
|
||||
for (var k in result.data.retval)
|
||||
{
|
||||
if (this.errors[k] !== undefined)
|
||||
this.errors[k].push(result.data.retval[k]);
|
||||
else
|
||||
this.errors.default.push(result.data.retval[k]);
|
||||
}
|
||||
|
||||
this.$refs.form.clearValidation();
|
||||
this.$refs.form.factory
|
||||
.studstatus.unterbrechung.cancel(
|
||||
this.data.studierendenantrag_id
|
||||
)
|
||||
.then(result => {
|
||||
if (Number.isInteger(result.data))
|
||||
document.location = document.location.replace(/unterbrechung\/([0-9]*)\/[0-9]*[\/]?$/, 'unterbrechung/$1') + "/" + result.data;
|
||||
|
||||
this.data = result.data;
|
||||
if (this.data.status)
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
else
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_error')})),
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_cancelled')})),
|
||||
severity: 'danger'
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Number.isInteger(result.data.retval)) {
|
||||
document.location = document.location.replace(/unterbrechung\/([0-9]*)\/[0-9]*[\/]?$/, 'unterbrechung/$1') + "/" + result.data.retval;
|
||||
}
|
||||
this.data = result.data.retval;
|
||||
if (this.data.status) {
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
}
|
||||
else
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_cancelled')})),
|
||||
severity: 'danger'
|
||||
});
|
||||
}
|
||||
this.saving = false;
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(error => {
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_error')})),
|
||||
severity: 'danger'
|
||||
});
|
||||
this.saving = false;
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.uuid = _uuid++;
|
||||
},
|
||||
template: `
|
||||
<div class="studierendenantrag-form-unterbrechung">
|
||||
<core-fetch-cmpt :api-function="load">
|
||||
<div class="row">
|
||||
<core-form ref="form" class="row">
|
||||
<div class="col-12">
|
||||
<div v-for="error in errors.default" class="alert alert-danger" role="alert" v-html="error">
|
||||
</div>
|
||||
<form-validation></form-validation>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'studiengang')}}</th>
|
||||
@@ -260,93 +209,99 @@ export default {
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 mb-3">
|
||||
<label :for="'studierendenantrag-form-abmeldung-' + uuid + '-stsem'" class="form-label">
|
||||
{{$p.t('lehre', 'studiensemester')}}
|
||||
</label>
|
||||
<div v-if="data.studierendenantrag_id">
|
||||
{{data.studiensemester_kurzbz}}
|
||||
</div>
|
||||
<div v-else>
|
||||
<select
|
||||
class="form-select"
|
||||
:class="{'is-invalid': errors.studiensemester.length}"
|
||||
v-model="stsem"
|
||||
required
|
||||
:id="'studierendenantrag-form-abmeldung-' + uuid + '-stsem'"
|
||||
@input="currentWiedereinstieg = ''"
|
||||
>
|
||||
<option v-for="(stsem, index) in data.studiensemester" :key="index" :value="index" :disabled="stsem.disabled">
|
||||
{{stsem.studiensemester_kurzbz}}
|
||||
</option>
|
||||
</select>
|
||||
<div v-if="errors.studiensemester.length" class="invalid-feedback">
|
||||
{{errors.studiensemester.join(".")}}
|
||||
<label class="form-label">
|
||||
{{$p.t('lehre', 'studiensemester')}}
|
||||
</label>
|
||||
<div>
|
||||
{{data.studiensemester_kurzbz}}
|
||||
</div>
|
||||
</div>
|
||||
<form-input
|
||||
v-else
|
||||
type="select"
|
||||
v-model="stsem"
|
||||
name="studiensemester"
|
||||
:label="$p.t('lehre', 'studiensemester')"
|
||||
required
|
||||
@input="currentWiedereinstieg = ''"
|
||||
>
|
||||
<option v-for="(stsem, index) in data.studiensemester" :key="index" :value="index" :disabled="stsem.disabled">
|
||||
{{stsem.studiensemester_kurzbz}}
|
||||
</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-6 mb-3">
|
||||
<label class="form-label">
|
||||
{{$p.t('studierendenantrag', 'antrag_datum_wiedereinstieg')}}
|
||||
</label>
|
||||
|
||||
<div v-if="data.studierendenantrag_id">
|
||||
{{datumWsFormatted}}
|
||||
</div>
|
||||
<div v-else-if="stsem === null">
|
||||
<select class="form-select" disabled>
|
||||
<option selected>{{$p.t('ui/select_studiensemester')}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-else>
|
||||
<select v-model="currentWiedereinstieg" class="form-select">
|
||||
<option v-for="sem in data.studiensemester[stsem].wiedereinstieg" :key="sem.studiensemester_kurzbz" :value="sem.start" :disabled="sem.disabled">
|
||||
{{sem.studiensemester_kurzbz}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div v-if="errors.datum_wiedereinstieg.length" class="invalid-feedback d-block">
|
||||
{{errors.datum_wiedereinstieg.join(".")}}
|
||||
<label class="form-label">
|
||||
{{$p.t('studierendenantrag', 'antrag_datum_wiedereinstieg')}}
|
||||
</label>
|
||||
<div>
|
||||
{{datumWsFormatted}}
|
||||
</div>
|
||||
</div>
|
||||
<form-input
|
||||
v-else-if="stsem === null"
|
||||
type="select"
|
||||
:label="$p.t('studierendenantrag', 'antrag_datum_wiedereinstieg')"
|
||||
modelValue=""
|
||||
name="datum_wiedereinstieg"
|
||||
disabled
|
||||
>
|
||||
<template #default>
|
||||
<option value="" selected disabled hidden>{{$p.t('ui/select_studiensemester')}}</option>
|
||||
</template>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-else
|
||||
type="select"
|
||||
:label="$p.t('studierendenantrag', 'antrag_datum_wiedereinstieg')"
|
||||
v-model="currentWiedereinstieg"
|
||||
name="datum_wiedereinstieg"
|
||||
>
|
||||
<option v-for="sem in data.studiensemester[stsem].wiedereinstieg" :key="sem.studiensemester_kurzbz" :value="sem.start" :disabled="sem.disabled">
|
||||
{{sem.studiensemester_kurzbz}}
|
||||
</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div v-if="data.studierendenantrag_id" class="mb-3">
|
||||
<h5>{{$p.t('studierendenantrag', 'antrag_grund')}}:</h5>
|
||||
<textarea class="form-control" rows="5" readonly>{{data.grund}}</textarea>
|
||||
</div>
|
||||
<div v-else class="col-sm-6 mb-3">
|
||||
<label :for="'studierendenantrag-form-abmeldung-' + uuid + '-grund'" class="form-label">Grund:</label>
|
||||
<textarea
|
||||
class="form-control"
|
||||
:class="{'is-invalid': errors.grund.length}"
|
||||
:id="'studierendenantrag-form-abmeldung-' + uuid + '-grund'"
|
||||
<div class="col-sm-6 mb-3">
|
||||
<form-input
|
||||
v-if="data.studierendenantrag_id"
|
||||
type="textarea"
|
||||
:label="$p.t('studierendenantrag', 'antrag_grund') + ':'"
|
||||
v-model="data.grund"
|
||||
name="grund"
|
||||
rows="5"
|
||||
:disabled="saving"
|
||||
readonly
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-else
|
||||
ref="grund"
|
||||
type="textarea"
|
||||
:label="$p.t('studierendenantrag', 'antrag_grund') + ':'"
|
||||
v-model="data.grund"
|
||||
name="grund"
|
||||
:disabled="saving"
|
||||
rows="5"
|
||||
required
|
||||
></textarea>
|
||||
<div v-if="errors.grund.length" class="invalid-feedback">
|
||||
{{errors.grund.join(".")}}
|
||||
</div>
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-12 mb-3">
|
||||
|
||||
<div v-if="data.studierendenantrag_id">
|
||||
<a v-if="data.dms_id" target="_blank" :href="siteUrl + '/lehre/Antrag/Attachment/Show/' + data.dms_id"> {{$p.t('studierendenantrag', 'antrag_dateianhaenge')}} </a>
|
||||
<span v-else>{{$p.t('studierendenantrag', 'no_attachments')}}</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<label
|
||||
:for="'studierendenantrag-form-abmeldung-' + uuid + '-attachment'"
|
||||
class="form-label">
|
||||
{{$p.t('studierendenantrag', 'antrag_dateianhaenge')}}
|
||||
</label>
|
||||
<input
|
||||
class="form-control"
|
||||
type="file"
|
||||
ref="attachment"
|
||||
:id="'studierendenantrag-form-abmeldung-' + uuid + '-attachment'"
|
||||
name="attachment">
|
||||
</div>
|
||||
<form-input
|
||||
v-else
|
||||
ref="attachment"
|
||||
type="uploadfile"
|
||||
:label="$p.t('studierendenantrag', 'antrag_dateianhaenge')"
|
||||
v-model="attachment"
|
||||
name="attachment"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-12 text-end">
|
||||
<button
|
||||
@@ -368,13 +323,12 @@ export default {
|
||||
{{$p.t('studierendenantrag', 'btn_cancel')}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</core-form>
|
||||
<template v-slot:error="{errorMessage}">
|
||||
<div class="alert alert-danger m-0" role="alert">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</template>
|
||||
</core-fetch-cmpt>
|
||||
</div>
|
||||
`
|
||||
</div>`
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import {CoreFetchCmpt} from '../../Fetch.js';
|
||||
import VueDatepicker from '../../vueDatepicker.js.php';
|
||||
import CoreForm from '../../Form/Form.js';
|
||||
import FormValidation from '../../Form/Validation.js';
|
||||
|
||||
var _uuid = 0;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFetchCmpt,
|
||||
VueDatepicker
|
||||
CoreForm,
|
||||
FormValidation
|
||||
},
|
||||
emits: [
|
||||
'setInfos',
|
||||
@@ -22,12 +23,6 @@ export default {
|
||||
return {
|
||||
data: null,
|
||||
saving: false,
|
||||
errors: {
|
||||
grund: [],
|
||||
default: []
|
||||
},
|
||||
siteUrl: FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router,
|
||||
infos: []
|
||||
}
|
||||
},
|
||||
@@ -45,10 +40,6 @@ export default {
|
||||
default: return 'warning';
|
||||
}
|
||||
},
|
||||
loadUrl() {
|
||||
return '/components/Antrag/Wiederholung/getDetailsForNewAntrag/' +
|
||||
this.prestudentId;
|
||||
},
|
||||
datumPruefungFormatted() {
|
||||
if(!this.data.pruefungsdatum)
|
||||
return '';
|
||||
@@ -58,13 +49,12 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
load() {
|
||||
return axios.get(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
this.loadUrl
|
||||
).then(
|
||||
result => {
|
||||
this.data = result.data.retval;
|
||||
return this.$fhcApi.factory
|
||||
.studstatus.wiederholung.getDetails(
|
||||
this.prestudentId
|
||||
)
|
||||
.then(result => {
|
||||
this.data = result.data;
|
||||
if (!this.data.status || this.data.status == 'ErsteAufforderungVersandt' || this.data.status == 'ZweiteAufforderungVersandt') {
|
||||
this.data.status = 'Offen';
|
||||
this.data.statustyp = this.$p.t('studierendenantrag', 'status_open');
|
||||
@@ -79,8 +69,7 @@ export default {
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
return result;
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
createAntrag() {
|
||||
this.createAntragWithStatus(true);
|
||||
@@ -89,7 +78,7 @@ export default {
|
||||
this.createAntragWithStatus(false);
|
||||
},
|
||||
createAntragWithStatus(repeat) {
|
||||
let func = repeat ? 'createAntrag' : 'cancelAntrag';
|
||||
let func = repeat ? 'create' : 'cancel';
|
||||
let nextState = repeat ? 'Erstellt' : 'Verzichtet';
|
||||
|
||||
this.$emit('setStatus', {
|
||||
@@ -97,54 +86,36 @@ export default {
|
||||
severity: 'warning'
|
||||
});
|
||||
this.saving = true;
|
||||
for(var k in this.errors)
|
||||
this.errors[k] = [];
|
||||
|
||||
axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Wiederholung/' + func + '/',
|
||||
{
|
||||
prestudent_id: this.data.prestudent_id,
|
||||
studiensemester: this.data.studiensemester_kurzbz
|
||||
}
|
||||
).then(
|
||||
result => {
|
||||
if (result.data.error)
|
||||
{
|
||||
for (var k in result.data.retval)
|
||||
{
|
||||
if (this.errors[k] !== undefined)
|
||||
this.errors[k].push(result.data.retval[k]);
|
||||
else
|
||||
this.errors.default.push(result.data.retval[k]);
|
||||
}
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_error')})),
|
||||
severity: 'danger'
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.data.retval === true)
|
||||
document.location += "";
|
||||
this.data = result.data.retval;
|
||||
if (!this.data.status)
|
||||
this.data.status = nextState;
|
||||
this.$emit('update:status', this.data.status);
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
}
|
||||
this.$refs.form.factory
|
||||
.studstatus.wiederholung[func](
|
||||
this.data.prestudent_id,
|
||||
this.data.studiensemester_kurzbz
|
||||
)
|
||||
.then(result => {
|
||||
if (result.data === true)
|
||||
document.location += "";
|
||||
|
||||
this.data = result.data;
|
||||
if (!this.data.status)
|
||||
this.data.status = nextState;
|
||||
this.$emit('update:status', this.data.status);
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
this.saving = false;
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(error => {
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_error')})),
|
||||
severity: 'danger'
|
||||
});
|
||||
this.saving = false;
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.uuid = _uuid++;
|
||||
},
|
||||
mounted() {
|
||||
this.infos = [...Array(5).keys()].map(n => ({
|
||||
body: Vue.computed(() => this.$p.t('studierendenantrag', 'infotext_Wiederholung_' + n))
|
||||
@@ -154,10 +125,9 @@ export default {
|
||||
template: `
|
||||
<div class="studierendenantrag-form-wiederholung">
|
||||
<core-fetch-cmpt :api-function="load">
|
||||
<div class="row">
|
||||
<core-form ref="form" class="row">
|
||||
<div class="col-12">
|
||||
<div v-for="error in errors.default" class="alert alert-danger" role="alert" v-html="error">
|
||||
</div>
|
||||
<form-validation></form-validation>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'studiengang')}}</th>
|
||||
@@ -206,7 +176,7 @@ export default {
|
||||
{{$p.t('studierendenantrag/antrag_Wiederholung_button_no')}}
|
||||
</button>-->
|
||||
</div>
|
||||
</div>
|
||||
</core-form>
|
||||
<template v-slot:error="{errorMessage}">
|
||||
<div class="alert alert-danger m-0" role="alert">
|
||||
{{ errorMessage }}
|
||||
|
||||
@@ -39,15 +39,10 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
loadFilter() {
|
||||
axios.get(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Leitung/getActiveStgs'
|
||||
).then(result => {
|
||||
this.stgs = result.data.retval;
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
this.$fhcApi.factory
|
||||
.studstatus.leitung.getStgs()
|
||||
.then(result => this.stgs = result.data)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
changeFilter(filter) {
|
||||
this.filter = filter || undefined;
|
||||
@@ -103,21 +98,9 @@ export default {
|
||||
}
|
||||
} else {
|
||||
this.$refs.loader.show();
|
||||
axios
|
||||
.all(
|
||||
oks.map(
|
||||
antrag => axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Leitung/approve' + antrag.typ,
|
||||
{
|
||||
studierendenantrag_id: antrag.studierendenantrag_id
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(this.showValidation)
|
||||
.catch(this.showError);
|
||||
this.$fhcApi.factory
|
||||
.studstatus.leitung.approve(oks)
|
||||
.then(this.showResults);
|
||||
}
|
||||
},
|
||||
actionReject(evt, gruende) {
|
||||
@@ -147,99 +130,38 @@ export default {
|
||||
.catch(() => {});
|
||||
} else {
|
||||
this.$refs.loader.show();
|
||||
axios
|
||||
.all(
|
||||
gruende.map(
|
||||
antrag => axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Leitung/reject' + antrag.typ,
|
||||
{
|
||||
studierendenantrag_id: antrag.studierendenantrag_id,
|
||||
grund: antrag.grund
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(this.showValidation)
|
||||
.catch(this.showError);
|
||||
this.$fhcApi.factory
|
||||
.studstatus.leitung.reject(gruende)
|
||||
.then(this.showResults);
|
||||
}
|
||||
},
|
||||
actionReopen(evt) {
|
||||
var antraege = evt || this.selectedData;
|
||||
this.$refs.loader.show();
|
||||
axios
|
||||
.all(
|
||||
antraege.map(
|
||||
antrag => axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Leitung/reopenAntrag/',
|
||||
{
|
||||
studierendenantrag_id: antrag.studierendenantrag_id
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(this.showValidation)
|
||||
.catch(this.showError);
|
||||
this.$fhcApi.factory
|
||||
.studstatus.leitung.reopen(gruende)
|
||||
.then(this.showResults);
|
||||
},
|
||||
actionPause(evt) {
|
||||
var antraege = evt || this.selectedData;
|
||||
this.$refs.loader.show();
|
||||
axios
|
||||
.all(
|
||||
antraege.map(
|
||||
antrag => axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Leitung/pauseAntrag/',
|
||||
{
|
||||
studierendenantrag_id: antrag.studierendenantrag_id
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(this.showValidation)
|
||||
.catch(this.showError);
|
||||
this.$fhcApi.factory
|
||||
.studstatus.leitung.pause(antraege)
|
||||
.then(this.showResults);
|
||||
},
|
||||
actionUnpause(evt) {
|
||||
var antraege = evt || this.selectedData;
|
||||
this.$refs.loader.show();
|
||||
axios
|
||||
.all(
|
||||
antraege.map(
|
||||
antrag => axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Leitung/unpauseAntrag/',
|
||||
{
|
||||
studierendenantrag_id: antrag.studierendenantrag_id
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(this.showValidation)
|
||||
.catch(this.showError);
|
||||
this.$fhcApi.factory
|
||||
.studstatus.leitung.unpause(antraege)
|
||||
.then(this.showResults);
|
||||
},
|
||||
actionObject(evt) {
|
||||
var antraege = evt || this.selectedData;
|
||||
this.$refs.loader.show();
|
||||
axios
|
||||
.all(
|
||||
antraege.map(
|
||||
antrag => axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Leitung/objectAntrag/',
|
||||
{
|
||||
studierendenantrag_id: antrag.studierendenantrag_id
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(this.showValidation)
|
||||
.catch(this.showError);
|
||||
this.$fhcApi.factory
|
||||
.studstatus.leitung.object(antraege)
|
||||
.then(this.showResults);
|
||||
},
|
||||
actionoObjectionDeny(evt, gruende) {
|
||||
var antraege = evt || this.selectedData;
|
||||
@@ -267,84 +189,31 @@ export default {
|
||||
.catch(() => {});
|
||||
} else {
|
||||
this.$refs.loader.show();
|
||||
axios
|
||||
.all(
|
||||
gruende.map(
|
||||
antrag => axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Leitung/objectionDeny/',
|
||||
{
|
||||
studierendenantrag_id: antrag.studierendenantrag_id,
|
||||
grund: antrag.grund
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(this.showValidation)
|
||||
.catch(this.showError);
|
||||
this.$fhcApi.factory
|
||||
.studstatus.leitung.denyObjection(gruende)
|
||||
.then(this.showResults);
|
||||
}
|
||||
},
|
||||
actionObjectionApprove(evt, gruende) {
|
||||
var antraege = evt || this.selectedData;
|
||||
this.$refs.loader.show();
|
||||
axios
|
||||
.all(
|
||||
antraege.map(
|
||||
antrag => axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Leitung/objectionApprove/',
|
||||
{
|
||||
studierendenantrag_id: antrag.studierendenantrag_id
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(this.showValidation)
|
||||
.catch(this.showError);
|
||||
this.$fhcApi.factory
|
||||
.studstatus.leitung.approveObjection(antraege)
|
||||
.then(this.showResults);
|
||||
},
|
||||
actionCancel(evt) {
|
||||
var antraege = evt || this.selectedData;
|
||||
this.$refs.loader.show();
|
||||
axios
|
||||
.all(
|
||||
antraege.map(
|
||||
antrag => axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Abmeldung/cancelAntrag/',
|
||||
{
|
||||
antrag_id: antrag.studierendenantrag_id
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(this.showValidation)
|
||||
.catch(this.showError);
|
||||
this.$fhcApi.factory
|
||||
.studstatus.abmeldung.cancel(antraege)
|
||||
.then(this.showResults);
|
||||
},
|
||||
showValidation(results) {
|
||||
var errors = results.filter(res => res.data.error);
|
||||
showResults(results) {
|
||||
let fulfilled = results.filter(res => res.status == 'fulfilled');
|
||||
this.$refs.loader.hide();
|
||||
if (errors.length) {
|
||||
let errorMsg = errors.map(
|
||||
error =>
|
||||
'Antrag ' +
|
||||
JSON.parse(error.config.data).studierendenantrag_id +
|
||||
'\n' +
|
||||
Object.values(error.data.retval).join('\n')
|
||||
).join('\n');
|
||||
|
||||
BsAlert.popup(errorMsg, {dialogClass: 'alert alert-danger'});
|
||||
}
|
||||
this.reload();
|
||||
},
|
||||
showError(error) {
|
||||
this.$refs.loader.hide();
|
||||
let msg = error.response.data;
|
||||
if (msg.replace(/^\s+/, '').substr(0, 9) == '<!DOCTYPE' || msg.replace(/^\s+/, '').substr(0, 4).toLowerCase() == '<div')
|
||||
msg = error.message;
|
||||
BsAlert.popup(msg, {dialogClass: 'alert alert-danger'});
|
||||
//fulfilled.forEach(a => this.$fhcAlert.alertDefault('success', '#' + a.value.data, 'Approved, ...'));
|
||||
if (fulfilled.length)
|
||||
this.reload();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
||||
@@ -35,28 +35,32 @@ export default {
|
||||
});
|
||||
},
|
||||
loadData(evt) {
|
||||
if (this.abortController)
|
||||
this.abortController.abort();
|
||||
if( evt.query.length < 2 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.abortController instanceof AbortController
|
||||
&& this.abortController.signal.aborted === false)
|
||||
{
|
||||
this.abortController.abort();
|
||||
}
|
||||
this.abortController = new AbortController();
|
||||
|
||||
axios.post(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Abmeldung/getStudiengaengeAssistenz/',
|
||||
evt,
|
||||
{
|
||||
signal: this.abortController.signal
|
||||
}
|
||||
).then(
|
||||
result => {
|
||||
if (result.data.error) {
|
||||
BsAlert.popup(result.data.retval, {dialogClass: 'alert alert-danger'});
|
||||
} else {
|
||||
this.data = result.data.retval;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
).catch(() => {});
|
||||
this.$fhcApi.factory
|
||||
.studstatus.leitung.getPrestudents(evt.query, this.abortController.signal)
|
||||
.then(result => {
|
||||
this.data = result.data;
|
||||
this.abortController = null;
|
||||
})
|
||||
.catch(error => {
|
||||
if (this.abortController instanceof AbortController
|
||||
&& this.abortController.signal.aborted === false)
|
||||
{
|
||||
this.abortController.abort();
|
||||
}
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
});
|
||||
}
|
||||
},
|
||||
template: `
|
||||
@@ -79,15 +83,20 @@ export default {
|
||||
class="w-100"
|
||||
v-model="student"
|
||||
:suggestions="data"
|
||||
optionLabel = "name"
|
||||
option-label = "name"
|
||||
@complete="loadData"
|
||||
inputId="newAntragModalAutoComplete"
|
||||
input-id="newAntragModalAutoComplete"
|
||||
dropdown
|
||||
dropdown-mode="current"
|
||||
>
|
||||
<template #option="slotProps">
|
||||
<div :title="slotProps.option.prestudent_id">
|
||||
{{slotProps.option.name}}
|
||||
{{slotProps.option.name}}
|
||||
</div>
|
||||
</template>
|
||||
<template #empty>
|
||||
<div class="text-muted px-3 py-2">
|
||||
{{ $p.t('ui/keineEintraegeGefunden') }}
|
||||
</div>
|
||||
</template>
|
||||
</auto-complete>
|
||||
|
||||
@@ -36,21 +36,17 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
setlvs(param) {
|
||||
if(param.error) {
|
||||
this.$refs.fetchCompt.error = true;
|
||||
this.$refs.fetchCompt.errorMessage = param.retval;
|
||||
} else {
|
||||
this.repeat_last = !!param.retval.repeat_last;
|
||||
if (this.repeat_last) {
|
||||
delete param.retval.repeat_last;
|
||||
}
|
||||
this.lvs = param.retval;
|
||||
this.repeat_last = !!param.repeat_last;
|
||||
if (this.repeat_last) {
|
||||
delete param.repeat_last;
|
||||
}
|
||||
this.lvs = param;
|
||||
},
|
||||
loadlvs() {
|
||||
if (!this.antragId)
|
||||
return new Promise(() => {});
|
||||
return axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Antrag/Wiederholung/getLvs/' + this.antragId);
|
||||
return this.$fhcApi.factory
|
||||
.studstatus.wiederholung.getLvs(this.antragId);
|
||||
},
|
||||
submit(result) {
|
||||
this.result = [result, this.check];
|
||||
|
||||
@@ -31,9 +31,6 @@ export default {
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
ajaxUrl: FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Leitung/getAntraege/',
|
||||
table: null,
|
||||
lastHistoryClickedId: null,
|
||||
historyData: [],
|
||||
@@ -42,7 +39,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
reload(stg) {
|
||||
this.table.setData(this.ajaxUrl + (stg || ''));
|
||||
this.table.setData('/' + (stg || ''));
|
||||
},
|
||||
download() {
|
||||
this.table.download("csv", "data.csv", {
|
||||
@@ -53,14 +50,12 @@ export default {
|
||||
getHistory() {
|
||||
if (this.lastHistoryClickedId === null)
|
||||
return null;
|
||||
return axios.get(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/components/Antrag/Leitung/getHistory/' +
|
||||
this.lastHistoryClickedId
|
||||
).then(res => {
|
||||
this.historyData = res.data.retval.sort((a, b) => a.insertamum > b.insertamum);
|
||||
});
|
||||
return this.$fhcApi.factory
|
||||
.studstatus.leitung.getHistory(this.lastHistoryClickedId)
|
||||
.then(res => {
|
||||
this.historyData = res.data.sort((a, b) => a.insertamum > b.insertamum);
|
||||
})
|
||||
.catch(this.$fhcApi.handleSystemError);
|
||||
},
|
||||
getHistoryStatus(data, index) {
|
||||
if (data.insertvon == 'Studienabbruch')
|
||||
@@ -116,7 +111,8 @@ export default {
|
||||
movableColumns: true,
|
||||
height: '65vh',
|
||||
layout: "fitDataFill",
|
||||
ajaxURL: this.ajaxUrl + (this.filter || ''),
|
||||
ajaxURL: '/' + (this.filter || ''),
|
||||
ajaxRequestFunc: this.$fhcApi.factory.studstatus.leitung.getAntraege,
|
||||
persistence: { // NOTE(chris): do not store column titles
|
||||
sort: true, //persist column sorting
|
||||
filter: true, //persist filters
|
||||
@@ -269,7 +265,7 @@ export default {
|
||||
allowed_status_for_download = ['Genehmigt'];
|
||||
break;
|
||||
case 'AbmeldungStgl':
|
||||
allowed_status_for_download = ['Genehmigt', 'Beeinsprucht', 'EinspruchAbgelehnt', 'Abgemeldet'];
|
||||
allowed_status_for_download = ['EinspruchAbgelehnt', 'Abgemeldet'];
|
||||
break;
|
||||
case 'Unterbrechung':
|
||||
allowed_status_for_download = ['Genehmigt', 'EmailVersandt'];
|
||||
@@ -489,4 +485,4 @@ export default {
|
||||
</lv-popup>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
@@ -56,39 +56,19 @@ export default {
|
||||
anmerkung: lv.antrag_anmerkung || "",
|
||||
studiensemester_kurzbz: this.lvs2sem
|
||||
}));
|
||||
axios.post(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Antrag/Wiederholung/saveLvs/', {forbiddenLvs, mandatoryLvs})
|
||||
this.$fhcApi.factory
|
||||
.studstatus.wiederholung.saveLvs(forbiddenLvs, mandatoryLvs)
|
||||
.then(response => {
|
||||
if(!response.data.error) {
|
||||
this.addAlert('Speichern erfolgreich', 'alert-success');
|
||||
this.statusCode = response.data.retval[0].studierendenantrag_statustyp_kurzbz;
|
||||
this.statusMsg = response.data.retval[0].typ;
|
||||
} else {
|
||||
this.addAlert(response.data.retval.message || response.data.retval, 'alert-danger');
|
||||
this.statusCode = 0;
|
||||
this.statusMsg = 'Error';
|
||||
}
|
||||
this.$fhcAlert.alertSuccess('Speichern erfolgreich');
|
||||
this.statusCode = response.data[0].studierendenantrag_statustyp_kurzbz;
|
||||
this.statusMsg = response.data[0].typ;
|
||||
this.isloading = false;
|
||||
}).catch(error => {
|
||||
this.addAlert(error.message, 'alert-danger');
|
||||
})
|
||||
.catch(error => {
|
||||
this.statusCode = 0;
|
||||
this.statusMsg = 'Error';
|
||||
this.isloading = false;
|
||||
}).finally(() => {
|
||||
window.scrollTo(0, 0);
|
||||
});
|
||||
},
|
||||
addAlert(text, type) {
|
||||
const para = document.createElement("p");
|
||||
para.innerText = text;
|
||||
para.className = "alert " + type + " alert-dismissible fade show";
|
||||
const btn = document.createElement("button");
|
||||
btn.className = "btn-close";
|
||||
btn.type = "button";
|
||||
btn.setAttribute("aria-label", "Close");
|
||||
btn.setAttribute("data-bs-dismiss", "alert");
|
||||
para.appendChild(btn);
|
||||
|
||||
this.$refs.alertbox.appendChild(para);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -96,149 +76,141 @@ export default {
|
||||
this.statusMsg = this.initialStatusMsg;
|
||||
},
|
||||
mounted() {
|
||||
axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Antrag/Wiederholung/getLvs/' + this.antragId).then(
|
||||
result => {
|
||||
if(result.data.error)
|
||||
{
|
||||
this.addAlert(result.data.retval, 'alert-danger');
|
||||
this.isloading = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
let res = {};
|
||||
this.$p
|
||||
.loadCategory(['ui', 'lehre', 'studierendenantrag', 'global'])
|
||||
.then(() => {
|
||||
for (var k in result.data.retval) {
|
||||
if (k === 'repeat_last')
|
||||
continue;
|
||||
if (result.data.retval[k] === null) {
|
||||
const alert = document.createElement('div');
|
||||
alert.innerHTML = this.$p.t('studierendenantrag', 'error_stg_last_semester');
|
||||
alert.className = 'alert alert-warning';
|
||||
alert.role = 'alert';
|
||||
this.$refs["lvtable" + k.substr(0,1)].append(alert);
|
||||
continue;
|
||||
this.$p
|
||||
.loadCategory(['ui', 'lehre', 'studierendenantrag', 'global'])
|
||||
.then(() => this.antragId)
|
||||
.then(this.$fhcApi.factory.studstatus.wiederholung.getLvs)
|
||||
.then(result => {
|
||||
let res = {};
|
||||
for (var k in result.data) {
|
||||
if (k === 'repeat_last')
|
||||
continue;
|
||||
if (result.data[k] === null) {
|
||||
const alert = document.createElement('div');
|
||||
alert.innerHTML = this.$p.t('studierendenantrag', 'error_stg_last_semester');
|
||||
alert.className = 'alert alert-warning';
|
||||
alert.role = 'alert';
|
||||
this.$refs["lvtable" + k.substr(0,1)].append(alert);
|
||||
continue;
|
||||
}
|
||||
let lvs = result.data[k].reduce((obj,lv) => {
|
||||
obj[lv.studienplan_lehrveranstaltung_id] = lv;
|
||||
return obj;
|
||||
}, {});
|
||||
for (var lv of Object.values(lvs)) {
|
||||
if (!lv.studienplan_lehrveranstaltung_id_parent)
|
||||
continue;
|
||||
if (!lvs[lv.studienplan_lehrveranstaltung_id_parent])
|
||||
console.error('parent not available');
|
||||
else {
|
||||
if (!lvs[lv.studienplan_lehrveranstaltung_id_parent]._children)
|
||||
lvs[lv.studienplan_lehrveranstaltung_id_parent]._children = [];
|
||||
lvs[lv.studienplan_lehrveranstaltung_id_parent]._children.push(lv);
|
||||
}
|
||||
}
|
||||
res[k] = Object.values(lvs).filter(lv => !lv.studienplan_lehrveranstaltung_id_parent);
|
||||
let current = res[k];
|
||||
let index = k.substr(0,1);
|
||||
|
||||
const options = {
|
||||
data: current,
|
||||
dataTree: true,
|
||||
dataTreeStartExpanded: true, //start with an expanded tree
|
||||
dataTreeChildIndent: 15,
|
||||
layout: "fitDataStretch",
|
||||
columns: [
|
||||
{title: this.$p.t('ui', 'bezeichnung'), field: "bezeichnung"},
|
||||
{title: this.$p.t('lehre','lehrform'), field: "lehrform_kurzbz"},
|
||||
{title: "ECTS", field: "ects"},
|
||||
{title: this.$p.t('lehre','note'), field: "note", formatter:(cell, formatterParams, onRendered) => cell.getValue() || "---"},
|
||||
{
|
||||
title: index == 1 && !result.data.repeat_last ? this.$p.t('studierendenantrag','lv_nicht_zulassen') : this.$p.t('studierendenantrag','lv_wiederholen'),
|
||||
field: "antrag_zugelassen",
|
||||
formatter: (cell, formatterParams, onRendered) => {
|
||||
let data = cell.getData();
|
||||
if (data._children || !data.zeugnis)
|
||||
return "";
|
||||
let input = document.createElement('input');
|
||||
input.className = "form-check-input";
|
||||
input.type = "checkbox";
|
||||
input.role = "switch";
|
||||
input.checked = cell.getValue();
|
||||
input.addEventListener('input', () => {
|
||||
lvs[data.studienplan_lehrveranstaltung_id].antrag_zugelassen = input.checked;
|
||||
cell.getRow().reformat();
|
||||
});
|
||||
if (this.disabled) {
|
||||
input.disabled = true;
|
||||
}
|
||||
|
||||
let div = document.createElement('div');
|
||||
div.className = 'form-check form-switch';
|
||||
div.append(input);
|
||||
|
||||
return div;
|
||||
}
|
||||
let lvs = result.data.retval[k].reduce((obj,lv) => {
|
||||
obj[lv.studienplan_lehrveranstaltung_id] = lv;
|
||||
return obj;
|
||||
}, {});
|
||||
for (var lv of Object.values(lvs)) {
|
||||
if (!lv.studienplan_lehrveranstaltung_id_parent)
|
||||
continue;
|
||||
if (!lvs[lv.studienplan_lehrveranstaltung_id_parent])
|
||||
console.error('parent not available');
|
||||
else {
|
||||
if (!lvs[lv.studienplan_lehrveranstaltung_id_parent]._children)
|
||||
lvs[lv.studienplan_lehrveranstaltung_id_parent]._children = [];
|
||||
lvs[lv.studienplan_lehrveranstaltung_id_parent]._children.push(lv);
|
||||
},
|
||||
{
|
||||
title: this.$p.t('global','anmerkung'),
|
||||
field: "antrag_anmerkung",
|
||||
headerSort:false,
|
||||
titleFormatter:(cell, formatterParams, onRendered)=>{
|
||||
let link = document.createElement('a');
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
link.href ="#";
|
||||
link.title = this.$p.t('studierendenantrag','anmerkung_tooltip');
|
||||
new bootstrap.Tooltip(link);
|
||||
let tooltip = document.createElement('span');
|
||||
tooltip.innerHTML = this.$p.t('global','anmerkung') + " ";
|
||||
tooltip.append(link);
|
||||
|
||||
let icon = document.createElement('i');
|
||||
link.append(icon);
|
||||
icon.className = "fa fa-info-circle";
|
||||
icon.setAttribute("aria-hidden", "true");
|
||||
icon.style.minWidth = '1em';
|
||||
|
||||
return tooltip;
|
||||
|
||||
},
|
||||
formatter: (cell, formatterParams, onRendered) => {
|
||||
if (this.disabled) {
|
||||
return cell.getValue() || "";
|
||||
}
|
||||
var data = cell.getData();
|
||||
if (lvs[data.studienplan_lehrveranstaltung_id].antrag_zugelassen)
|
||||
{
|
||||
let input = document.createElement('input');
|
||||
input.className = "form-control";
|
||||
input.type = "text";
|
||||
input.value = cell.getValue() || "";
|
||||
input.addEventListener('input', () => {
|
||||
lvs[data.studienplan_lehrveranstaltung_id].antrag_anmerkung = input.value;
|
||||
});
|
||||
return input;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
res[k] = Object.values(lvs).filter(lv => !lv.studienplan_lehrveranstaltung_id_parent);
|
||||
let current = res[k];
|
||||
let index = k.substr(0,1);
|
||||
|
||||
const options = {
|
||||
data: current,
|
||||
dataTree: true,
|
||||
dataTreeStartExpanded: true, //start with an expanded tree
|
||||
dataTreeChildIndent: 15,
|
||||
layout: "fitDataStretch",
|
||||
columns: [
|
||||
{title: this.$p.t('ui', 'bezeichnung'), field: "bezeichnung"},
|
||||
{title: this.$p.t('lehre','lehrform'), field: "lehrform_kurzbz"},
|
||||
{title: "ECTS", field: "ects"},
|
||||
{title: this.$p.t('lehre','note'), field: "note", formatter:(cell, formatterParams, onRendered)=>cell.getValue() || "---"},
|
||||
{
|
||||
title: index == 1 && !result.data.retval.repeat_last ? this.$p.t('studierendenantrag','lv_nicht_zulassen') : this.$p.t('studierendenantrag','lv_wiederholen'),
|
||||
field: "antrag_zugelassen",
|
||||
formatter: (cell, formatterParams, onRendered) => {
|
||||
let data = cell.getData();
|
||||
if(data._children || !data.zeugnis)
|
||||
return "";
|
||||
let input = document.createElement('input');
|
||||
input.className = "form-check-input";
|
||||
input.type = "checkbox";
|
||||
input.role = "switch";
|
||||
input.checked = cell.getValue();
|
||||
input.addEventListener('input', () => {
|
||||
lvs[data.studienplan_lehrveranstaltung_id].antrag_zugelassen = input.checked;
|
||||
cell.getRow().reformat();
|
||||
});
|
||||
if (this.disabled) {
|
||||
input.disabled = true;
|
||||
}
|
||||
|
||||
let div = document.createElement('div');
|
||||
div.className = 'form-check form-switch';
|
||||
div.append(input);
|
||||
|
||||
return div;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$p.t('global','anmerkung'),
|
||||
field: "antrag_anmerkung",
|
||||
headerSort:false,
|
||||
titleFormatter:(cell, formatterParams, onRendered)=>{
|
||||
let link = document.createElement('a');
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
link.href ="#";
|
||||
link.title = this.$p.t('studierendenantrag','anmerkung_tooltip');
|
||||
new bootstrap.Tooltip(link);
|
||||
let tooltip = document.createElement('span');
|
||||
tooltip.innerHTML = this.$p.t('global','anmerkung') + " ";
|
||||
tooltip.append(link);
|
||||
|
||||
let icon = document.createElement('i');
|
||||
link.append(icon);
|
||||
icon.className = "fa fa-info-circle";
|
||||
icon.setAttribute("aria-hidden", "true");
|
||||
icon.style.minWidth = '1em';
|
||||
|
||||
return tooltip;
|
||||
|
||||
},
|
||||
formatter: (cell, formatterParams, onRendered) => {
|
||||
if (this.disabled) {
|
||||
return cell.getValue() || "";
|
||||
}
|
||||
var data = cell.getData();
|
||||
if (lvs[data.studienplan_lehrveranstaltung_id].antrag_zugelassen)
|
||||
{
|
||||
let input = document.createElement('input');
|
||||
input.className = "form-control";
|
||||
input.type = "text";
|
||||
input.value = cell.getValue() || "";
|
||||
input.addEventListener('input', () => {
|
||||
lvs[data.studienplan_lehrveranstaltung_id].antrag_anmerkung = input.value;
|
||||
});
|
||||
return input;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
var table = new Tabulator(this.$refs["lvtable" + k.substr(0,1)], options);
|
||||
}
|
||||
this.lvs = result.data.retval;
|
||||
});
|
||||
]
|
||||
};
|
||||
var table = new Tabulator(this.$refs["lvtable" + k.substr(0,1)], options);
|
||||
}
|
||||
}
|
||||
);
|
||||
this.lvs = result.data;
|
||||
})
|
||||
.catch(error => {
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
this.isloading = true;
|
||||
});
|
||||
},
|
||||
template: `
|
||||
<div class="col-sm-10">
|
||||
<div ref="alertbox"></div>
|
||||
|
||||
<span class="d-flex justify-content-between h4">
|
||||
<span>{{lvs.repeat_last ? $p.t('studierendenantrag', 'title_lv_wiederholen') : $p.t('studierendenantrag', 'title_lv_nicht_zugelassen')}}</span>
|
||||
<span>{{lvs1sem}}</span>
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* Copyright (C) 2022 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 CoreSearchbar from "../searchbar/searchbar.js";
|
||||
import VerticalSplit from "../verticalsplit/verticalsplit.js";
|
||||
import StvVerband from "./Studentenverwaltung/Verband.js";
|
||||
import StvList from "./Studentenverwaltung/List.js";
|
||||
import StvDetails from "./Studentenverwaltung/Details.js";
|
||||
import StvStudiensemester from "./Studentenverwaltung/Studiensemester.js";
|
||||
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreSearchbar,
|
||||
VerticalSplit,
|
||||
StvVerband,
|
||||
StvList,
|
||||
StvDetails,
|
||||
StvStudiensemester
|
||||
},
|
||||
props: {
|
||||
defaultSemester: String,
|
||||
config: Object,
|
||||
permissions: Object,
|
||||
stvRoot: String,
|
||||
cisRoot: String,
|
||||
activeAddons: String // semicolon separated list of active addons
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
cisRoot: this.cisRoot,
|
||||
activeAddonBewerbung: this.activeAddons.split(';').includes('bewerbung'),
|
||||
configGenerateAlias: this.config.generateAlias,
|
||||
configShowZgvDoktor: this.config.showZgvDoktor,
|
||||
configShowZgvErfuellt: this.config.showZgvErfuellt,
|
||||
hasBpkPermission: this.permissions['student/bpk'],
|
||||
hasAliasPermission: this.permissions['student/alias'],
|
||||
hasPrestudentPermission: this.permissions['basis/prestudent'],
|
||||
hasPrestudentstatusPermission: this.permissions['basis/prestudentstatus'],
|
||||
hasAssistenzPermissionForStgs: this.permissions['assistenz_stgs'],
|
||||
hasSchreibrechtAss: this.permissions['assistenz_schreibrechte'],
|
||||
hasAdminPermission: this.permissions['admin'],
|
||||
hasPermissionToSkipStatusCheck: this.permissions['student/keine_studstatuspruefung'],
|
||||
hasPermissionRtAufsicht: this.permissions['lehre/reihungstestAufsicht'],
|
||||
lists: this.lists,
|
||||
defaultSemester: this.defaultSemester,
|
||||
$reloadList: () => {
|
||||
this.$refs.stvList.reload();
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selected: [],
|
||||
searchbaroptions: {
|
||||
types: [
|
||||
"student",
|
||||
"prestudent"
|
||||
],
|
||||
actions: {
|
||||
student: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
action: function(data) {
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/studentenverwaltung/student/' + data.uid;
|
||||
}
|
||||
},
|
||||
childactions: [
|
||||
]
|
||||
},
|
||||
prestudent: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
action: function(data) {
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/studentenverwaltung/prestudent/' + data.prestudent_id;
|
||||
}
|
||||
},
|
||||
childactions: [
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
studiengangKz: undefined,
|
||||
studiensemesterKurzbz: this.defaultSemester,
|
||||
lists: {
|
||||
nations: [],
|
||||
sprachen: [],
|
||||
geschlechter: []
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSelectVerband({link, studiengang_kz}) {
|
||||
this.studiengangKz = studiengang_kz;
|
||||
this.$refs.stvList.updateUrl(this.$fhcApi.factory.stv.students.verband(link));
|
||||
},
|
||||
studiensemesterChanged(v) {
|
||||
this.studiensemesterKurzbz = v;
|
||||
this.$refs.stvList.updateUrl();
|
||||
this.$refs.details.reload();
|
||||
},
|
||||
reloadList() {
|
||||
this.$refs.stvList.reload();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/address/getNations')
|
||||
.then(result => {
|
||||
this.lists.nations = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/lists/getSprachen')
|
||||
.then(result => {
|
||||
this.lists.sprachen = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/lists/getGeschlechter')
|
||||
.then(result => {
|
||||
this.lists.geschlechter = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/lists/getAusbildungen')
|
||||
.then(result => {
|
||||
this.lists.ausbildungen = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/lists/getStgs')
|
||||
.then(result => {
|
||||
this.lists.stgs = result.data;
|
||||
this.lists.active_stgs = this.lists.stgs.filter(stg => stg.aktiv);
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/lists/getOrgforms')
|
||||
.then(result => {
|
||||
this.lists.orgforms = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.factory.stv.konto.getBuchungstypen()
|
||||
.then(result => {
|
||||
this.lists.buchungstypen = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/lists/getStudiensemester')
|
||||
.then(result => {
|
||||
this.lists.studiensemester = result.data;
|
||||
this.lists.studiensemester_desc = result.data.toReversed();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
mounted() {
|
||||
if (this.$route.params.id) {
|
||||
this.$refs.stvList.updateUrl(this.$fhcApi.factory.stv.students.uid(this.$route.params.id), true);
|
||||
} else if (this.$route.params.prestudent_id) {
|
||||
this.$refs.stvList.updateUrl(this.$fhcApi.factory.stv.students.prestudent(this.$route.params.prestudent_id), true);
|
||||
} else if (this.$route.params.person_id) {
|
||||
this.$refs.stvList.updateUrl(this.$fhcApi.factory.stv.students.person(this.$route.params.person_id), true);
|
||||
}
|
||||
|
||||
},
|
||||
template: `
|
||||
<div class="stv">
|
||||
<header class="navbar navbar-expand-lg navbar-dark bg-dark flex-md-nowrap p-0 shadow">
|
||||
<a class="navbar-brand col-md-4 col-lg-3 col-xl-2 me-0 px-3" :href="stvRoot">FHC 4.0</a>
|
||||
<button class="navbar-toggler d-md-none m-1 collapsed" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" :aria-label="$p.t('ui/toggle_nav')"><span class="navbar-toggler-icon"></span></button>
|
||||
<core-searchbar :searchoptions="searchbaroptions" :searchfunction="$fhcApi.factory.search.search" class="searchbar w-100"></core-searchbar>
|
||||
</header>
|
||||
<div class="container-fluid overflow-hidden">
|
||||
<div class="row h-100">
|
||||
<nav id="sidebarMenu" class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100">
|
||||
<div class="offcanvas-header justify-content-end px-1 d-md-none">
|
||||
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" :aria-label="$p.t('ui/schliessen')"></button>
|
||||
</div>
|
||||
<stv-verband @select-verband="onSelectVerband" class="col" style="height:0%"></stv-verband>
|
||||
<stv-studiensemester :default="defaultSemester" @changed="studiensemesterChanged"></stv-studiensemester>
|
||||
</nav>
|
||||
<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>
|
||||
</template>
|
||||
<template #bottom>
|
||||
<stv-details ref="details" :students="selected"></stv-details>
|
||||
</template>
|
||||
</vertical-split>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
import FhcTabs from "../../Tabs.js";
|
||||
|
||||
// TODO(chris): alt & title
|
||||
// TODO(chris): phrasen
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FhcTabs
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
configStudent: null,
|
||||
configStudents: null
|
||||
};
|
||||
},
|
||||
props: {
|
||||
students: Array
|
||||
},
|
||||
computed: {
|
||||
appRoot() {
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reload() {
|
||||
if (this.$refs.tabs?.$refs?.current?.reload)
|
||||
this.$refs.tabs.$refs.current.reload();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$fhcApi
|
||||
.factory.stv.configStudent()
|
||||
.then(result => {
|
||||
this.configStudent = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.factory.stv.configStudents()
|
||||
.then(result => {
|
||||
this.configStudents = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
template: `
|
||||
<div class="stv-details h-100 pb-3 d-flex flex-column">
|
||||
<div v-if="!students?.length" class="justify-content-center d-flex h-100 align-items-center">
|
||||
Bitte StudentIn auswählen!
|
||||
</div>
|
||||
<div v-else-if="configStudent && configStudents" class="d-flex flex-column h-100 pb-3">
|
||||
<div class="d-flex justify-content-start align-items-center w-100 pb-3 gap-3" style="max-height:8rem">
|
||||
<img v-for="student in students" :key="student.person_id" class="d-block h-100 rounded" alt="profilbild" :src="appRoot + 'cis/public/bild.php?src=person&person_id=' + student.person_id">
|
||||
<div v-if="students.length == 1">
|
||||
<h2 class="h4">{{students[0].titlepre}} {{students[0].vorname}} {{students[0].nachname}} {{students[0].titlepost}}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<fhc-tabs v-if="students.length == 1" ref="tabs" :modelValue="students[0]" :config="configStudent" :default="$route.params.tab" style="flex: 1 1 0%; height: 0%" @changed="reload"></fhc-tabs>
|
||||
<fhc-tabs v-else ref="tabs" :modelValue="students" :config="configStudents" :default="$route.params.tab" style="flex: 1 1 0%; height: 0%" @changed="reload"></fhc-tabs>
|
||||
</div>
|
||||
</div>`
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import CoreBetriebsmittel from "../../../Betriebsmittel/Betriebsmittel.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreBetriebsmittel
|
||||
},
|
||||
props: {
|
||||
modelValue: Object
|
||||
},
|
||||
template: `
|
||||
<div class="stv-details-betriebsmittel h-100 pb-3">
|
||||
<core-betriebsmittel
|
||||
:endpoint="$fhcApi.factory.betriebsmittel.person"
|
||||
ref="formc"
|
||||
type-id="person_id"
|
||||
:id="modelValue.person_id"
|
||||
:uid="modelValue.uid"
|
||||
>
|
||||
</core-betriebsmittel>
|
||||
</div>
|
||||
`
|
||||
};
|
||||
|
||||
@@ -0,0 +1,462 @@
|
||||
import CoreForm from '../../../Form/Form.js';
|
||||
import FormInput from '../../../Form/Input.js';
|
||||
import FormUploadImage from '../../../Form/Upload/Image.js';
|
||||
|
||||
import CoreUdf from '../../../Udf/Udf.js';
|
||||
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreForm,
|
||||
FormInput,
|
||||
FormUploadImage,
|
||||
CoreUdf
|
||||
},
|
||||
inject: {
|
||||
showBpk: {
|
||||
from: 'hasBpkPermission',
|
||||
default: false
|
||||
},
|
||||
showZugangscode: {
|
||||
from: 'activeAddonBewerbung',
|
||||
default: false
|
||||
},
|
||||
cisRoot: {
|
||||
from: 'cisRoot'
|
||||
},
|
||||
generateAlias: {
|
||||
from: 'configGenerateAlias',
|
||||
default: false
|
||||
},
|
||||
hasAliasPermission: {
|
||||
from: 'hasAliasPermission',
|
||||
default: false
|
||||
},
|
||||
lists: {
|
||||
from: 'lists'
|
||||
}
|
||||
},
|
||||
props: {
|
||||
modelValue: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
test: {udf_viaf: 'TEST'},
|
||||
familienstaende: {
|
||||
"": "--keine Auswahl--",
|
||||
"g": "geschieden",
|
||||
"l": "ledig",
|
||||
"v": "verheiratet",
|
||||
"w": "verwitwet"
|
||||
},
|
||||
original: null,
|
||||
data: null,
|
||||
changed: {},
|
||||
udfChanges: false,
|
||||
studentIn: null,
|
||||
gebDatumIsValid: false,
|
||||
gebDatumIsInvalid: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
aliasNotAllowed() {
|
||||
return this.generateAlias === false && !this.hasAliasPermission;
|
||||
},
|
||||
changedLength() {
|
||||
return Object.keys(this.changed).length;
|
||||
},
|
||||
noImageSrc() {
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'skin/images/profilbild_dummy.jpg';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue(n) {
|
||||
this.updateStudent(n);
|
||||
},
|
||||
data: {
|
||||
// TODO(chris): use @input instead?
|
||||
handler(n) {
|
||||
let res = {};
|
||||
for (var k in this.original) {
|
||||
if (k == 'gebdatum') {
|
||||
if (new Date(this.original[k]).toString() != new Date(n[k]).toString())
|
||||
res[k] = n[k];
|
||||
} else {
|
||||
// TODO(chris): null && ""? should there be an exception for this?
|
||||
if (this.original[k] !== n[k] && !(this.original[k] === null && n[k] === ""))
|
||||
res[k] = n[k];
|
||||
}
|
||||
}
|
||||
this.changed = res;
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateStudent(n) {
|
||||
// TODO(chris): move to fhcapi.factory
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/student/get/' + n.prestudent_id)
|
||||
.then(result => {
|
||||
this.data = result.data;
|
||||
if (!this.data.familienstand)
|
||||
this.data.familienstand = '';
|
||||
this.original = {...(this.original || {}), ...this.data};
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
save() {
|
||||
if (!this.changedLength)
|
||||
return;
|
||||
|
||||
this.$refs.form.clearValidation();
|
||||
this.$refs.form
|
||||
.post('api/frontend/v1/stv/student/save/' + this.modelValue.prestudent_id, this.changed)
|
||||
.then(result => {
|
||||
this.original = {...this.data};
|
||||
this.changed = {};
|
||||
this.$refs.form.setFeedback(true, result.data);
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError)
|
||||
},
|
||||
udfsLoaded(udfs) {
|
||||
this.original = {...(this.original || {}), ...udfs};
|
||||
},
|
||||
reload(){
|
||||
this.updateStudent(this.modelValue);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.updateStudent(this.modelValue);
|
||||
},
|
||||
//TODO(chris): Phrasen
|
||||
//TODO(chris): Geburtszeit? Anzahl der Kinder?
|
||||
template: `
|
||||
<core-form ref="form" class="stv-details-details" @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">Speichern</button>
|
||||
</div>
|
||||
<fieldset class="overflow-hidden">
|
||||
<legend>Person</legend>
|
||||
<template v-if="data">
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Person ID"
|
||||
type="text"
|
||||
v-model="data.person_id"
|
||||
name="person_id"
|
||||
readonly
|
||||
>
|
||||
</form-input>
|
||||
<div v-if="showZugangscode" class="col-4">
|
||||
<label>Zugangscode</label>
|
||||
<div class="align-self-center">
|
||||
<span class="form-text">
|
||||
<a :href="cisRoot + 'addons/bewerbung/cis/registration.php?code=' + data.zugangscode + '&emailAdresse=' + data.email_privat" target="_blank">{{data.zugangscode}}</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<form-input
|
||||
v-if="showBpk"
|
||||
container-class="col-4"
|
||||
label="BPK"
|
||||
type="text"
|
||||
v-model="data.bpk"
|
||||
name="bpk"
|
||||
maxlength="28"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Anrede"
|
||||
type="text"
|
||||
v-model="data.anrede"
|
||||
name="anrede"
|
||||
maxlength="16"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Titel Pre"
|
||||
type="text"
|
||||
v-model="data.titelpre"
|
||||
name="titelpre"
|
||||
maxlength="64"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Titel Post"
|
||||
type="text"
|
||||
v-model="data.titelpost"
|
||||
name="titelpost"
|
||||
maxlength="32"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Nachname"
|
||||
type="text"
|
||||
v-model="data.nachname"
|
||||
name="nachname"
|
||||
maxlength="64"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Vorname"
|
||||
type="text"
|
||||
v-model="data.vorname"
|
||||
name="vorname"
|
||||
maxlength="32"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Vornamen"
|
||||
type="text"
|
||||
v-model="data.vornamen"
|
||||
name="vornamen"
|
||||
maxlength="128"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Wahlname"
|
||||
type="text"
|
||||
v-model="data.wahlname"
|
||||
name="wahlname"
|
||||
maxlength="128"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Geburtsdatum"
|
||||
type="DatePicker"
|
||||
v-model="data.gebdatum"
|
||||
name="gebdatum"
|
||||
:clearable="false"
|
||||
no-today
|
||||
auto-apply
|
||||
:enable-time-picker="false"
|
||||
format="dd.MM.yyyy"
|
||||
preview-format="dd.MM.yyyy"
|
||||
teleport
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Geburtsort"
|
||||
type="text"
|
||||
v-model="data.gebort"
|
||||
name="gebort"
|
||||
maxlength="128"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Geburtsnation"
|
||||
type="select"
|
||||
v-model="data.geburtsnation"
|
||||
name="geburtsnation"
|
||||
>
|
||||
<option value="">-- keine Auswahl --</option>
|
||||
<!-- TODO(chris): gesperrte nationen können nicht ausgewählt werden! Um das zu realisieren müsste man ein pseudo select machen -->
|
||||
<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
|
||||
container-class="col-4"
|
||||
label="SVNR"
|
||||
type="text"
|
||||
v-model="data.svnr"
|
||||
name="svnr"
|
||||
maxlength="16"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Ersatzkennzeichen"
|
||||
type="text"
|
||||
v-model="data.ersatzkennzeichen"
|
||||
name="ersatzkennzeichen"
|
||||
maxlength="10"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Staatsbürgerschaft"
|
||||
type="select"
|
||||
v-model="data.staatsbuergerschaft"
|
||||
name="staatsbuergerschaft"
|
||||
>
|
||||
<option value="">-- keine Auswahl --</option>
|
||||
<!-- TODO(chris): gesperrte nationen können nicht ausgewählt werden! Um das zu realisieren müsste man ein pseudo select machen -->
|
||||
<option v-for="nation in lists.nations" :key="nation.nation_code" :value="nation.nation_code" :disabled="nation.sperre">{{nation.kurztext}}</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Matrikelnummer"
|
||||
type="text"
|
||||
v-model="data.matr_nr"
|
||||
name="matr_nr"
|
||||
maxlength="32"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Sprache"
|
||||
type="select"
|
||||
v-model="data.sprache"
|
||||
name="sprache"
|
||||
>
|
||||
<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
|
||||
container-class="col-4"
|
||||
label="Geschlecht"
|
||||
type="select"
|
||||
v-model="data.geschlecht"
|
||||
name="geschlecht"
|
||||
>
|
||||
<option v-for="geschlecht in lists.geschlechter" :key="geschlecht.geschlecht" :value="geschlecht.geschlecht">{{geschlecht.bezeichnung}}</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Familienstand"
|
||||
type="select"
|
||||
v-model="data.familienstand"
|
||||
name="familienstand"
|
||||
>
|
||||
<option v-for="(bezeichnung, key) in familienstaende" :key="key" :value="key">{{bezeichnung}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Foto"
|
||||
type="UploadImage"
|
||||
v-model="data.foto"
|
||||
name="foto"
|
||||
>
|
||||
<img alt="No Image" :src="noImageSrc" class="w-100">
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Anmerkung"
|
||||
type="textarea"
|
||||
v-model="data.anmerkung"
|
||||
name="anmerkung"
|
||||
rows="8"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Homepage"
|
||||
type="text"
|
||||
v-model="data.homepage"
|
||||
name="homepage"
|
||||
maxlength="256"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else>
|
||||
Loading...
|
||||
</div>
|
||||
<core-udf @load="udfsLoaded" v-model="data" class="row-cols-3 g-3 mb-3" ci-model="person/person" :pk="{person_id:modelValue.person_id}"></core-udf>
|
||||
</fieldset>
|
||||
<fieldset v-if="data?.student_uid" class="overflow-hidden">
|
||||
<legend>StudentIn</legend>
|
||||
<template v-if="data">
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="UID"
|
||||
type="text"
|
||||
v-model="data.student_uid"
|
||||
name="student_uid"
|
||||
readonly
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Personenkennzeichen"
|
||||
type="text"
|
||||
v-model="data.matrikelnr"
|
||||
name="matrikelnr"
|
||||
readonly
|
||||
>
|
||||
</form-input>
|
||||
<div class="col-4 pt-4 d-flex align-items-center">
|
||||
<form-input
|
||||
container-class="form-check"
|
||||
label="Aktiv"
|
||||
type="checkbox"
|
||||
v-model="data.aktiv"
|
||||
name="aktiv"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Semester"
|
||||
type="text"
|
||||
v-model="data.semester"
|
||||
name="semester"
|
||||
maxlength="2"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Verband"
|
||||
type="text"
|
||||
v-model="data.verband"
|
||||
name="verband"
|
||||
maxlength="1"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Gruppe"
|
||||
type="text"
|
||||
v-model="data.gruppe"
|
||||
name="gruppe"
|
||||
maxlength="1"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Alias"
|
||||
type="text"
|
||||
v-model="data.alias"
|
||||
name="alias"
|
||||
:disabled="aliasNotAllowed"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else>
|
||||
Loading...
|
||||
</div>
|
||||
</fieldset>
|
||||
</core-form>`
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import AddressList from "./Kontakt/Address.js";
|
||||
import ContactList from "./Kontakt/Contact.js";
|
||||
import BankaccountList from "./Kontakt/Bankaccount.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AddressList,
|
||||
ContactList,
|
||||
BankaccountList,
|
||||
},
|
||||
props: {
|
||||
modelValue: Object,
|
||||
config: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
adressen: [],
|
||||
kontakte: [],
|
||||
bankverbindungen: []
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="stv-details-kontakt h-100 pb-3">
|
||||
<fieldset class="overflow-hidden">
|
||||
<legend>{{this.$p.t('person', 'adressen')}}</legend>
|
||||
<address-list ref="adressList" :uid="modelValue.person_id"></address-list>
|
||||
</fieldset>
|
||||
<br>
|
||||
<fieldset class="overflow-hidden">
|
||||
<legend>{{this.$p.t('global', 'kontakt')}}</legend>
|
||||
<contact-list ref="contactList" :uid="modelValue.person_id"></contact-list>
|
||||
</fieldset>
|
||||
<br>
|
||||
<fieldset v-if="config.showBankaccount" class="overflow-hidden">
|
||||
<legend>{{this.$p.t('person', 'bankverbindungen')}}</legend>
|
||||
<bankaccount-list ref="bankaccountList" :uid="modelValue.person_id"></bankaccount-list>
|
||||
</fieldset>
|
||||
</div>`
|
||||
};
|
||||
@@ -0,0 +1,583 @@
|
||||
import {CoreFilterCmpt} from "../../../../filter/Filter.js";
|
||||
import PvAutoComplete from "../../../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js";
|
||||
import FhcFormValidation from '../../../../Form/Validation.js';
|
||||
import BsModal from "../../../../Bootstrap/Modal.js";
|
||||
import FormForm from '../../../../Form/Form.js';
|
||||
import FormInput from '../../../../Form/Input.js';
|
||||
|
||||
export default{
|
||||
components: {
|
||||
CoreFilterCmpt,
|
||||
PvAutoComplete,
|
||||
FhcFormValidation,
|
||||
BsModal,
|
||||
FormForm,
|
||||
FormInput
|
||||
},
|
||||
props: {
|
||||
uid: Number
|
||||
},
|
||||
data() {
|
||||
return{
|
||||
tabulatorOptions: {
|
||||
ajaxURL: 'api/frontend/v1/stv/kontakt/getAdressen/' + this.uid,
|
||||
ajaxRequestFunc: this.$fhcApi.get,
|
||||
ajaxResponse: (url, params, response) => response.data,
|
||||
//autoColumns: true,
|
||||
columns:[
|
||||
{title:"Typ", field:"bezeichnung"},
|
||||
{title:"Strasse", field:"strasse"},
|
||||
{title:"Plz", field:"plz"},
|
||||
{title:"Ort", field:"ort"},
|
||||
{title:"Gemeinde", field:"gemeinde"},
|
||||
{title:"Nation", field:"nation"},
|
||||
{
|
||||
title:"Heimatadresse",
|
||||
field:"heimatadresse",
|
||||
formatter:"tickCross",
|
||||
hozAlign:"center",
|
||||
formatterParams: {
|
||||
tickElement: '<i class="fa fa-check text-success"></i>',
|
||||
crossElement: '<i class="fa fa-xmark text-danger"></i>'
|
||||
}
|
||||
},
|
||||
{
|
||||
title:"Zustelladresse",
|
||||
field:"zustelladresse",
|
||||
formatter:"tickCross",
|
||||
hozAlign:"center",
|
||||
formatterParams: {
|
||||
tickElement: '<i class="fa fa-check text-success"></i>',
|
||||
crossElement: '<i class="fa fa-xmark text-danger"></i>'
|
||||
}
|
||||
},
|
||||
{title:"Abweich.Empf", field:"co_name"},
|
||||
{title:"Firma", field:"firmenname"},
|
||||
{title:"Firma_id", field:"firma_id", visible:false},
|
||||
{title:"Adresse_id", field:"adresse_id", visible:false},
|
||||
{title:"Person_id", field:"person_id", visible:false},
|
||||
{title:"Name", field:"name", visible:false},
|
||||
{title:"letzte Änderung", field:"updateamum", visible:false},
|
||||
{title:"Rechnungsadresse", field:"rechnungsadresse", visible:false,
|
||||
formatter: (cell, formatterParams, onRendered) => {
|
||||
let output = cell.getValue() ? "ja" : "nein";
|
||||
return output;
|
||||
}
|
||||
},
|
||||
{title:"Anmerkung", field:"anmerkung", 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.addEventListener('click', (event) =>
|
||||
this.actionEditAdress(cell.getData().adresse_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.addEventListener('click', () =>
|
||||
this.actionDeleteAdress(cell.getData().adresse_id)
|
||||
);
|
||||
container.append(button);
|
||||
|
||||
return container;
|
||||
},
|
||||
frozen: true
|
||||
},
|
||||
],
|
||||
layout: 'fitDataFill',
|
||||
layoutColumnsOnNewData: false,
|
||||
height: 'auto',
|
||||
selectable: true,
|
||||
index: 'adresse_id',
|
||||
persistenceID: 'stv-details-kontakt-address'
|
||||
},
|
||||
tabulatorEvents: [
|
||||
{
|
||||
event: 'tableBuilt',
|
||||
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('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('updateamum').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')
|
||||
});
|
||||
}
|
||||
}
|
||||
],
|
||||
addressData: {
|
||||
zustelladresse: true,
|
||||
heimatadresse: true,
|
||||
rechnungsadresse: false,
|
||||
typ: 'h',
|
||||
nation: 'A'
|
||||
},
|
||||
statusNew: true,
|
||||
places: [],
|
||||
suggestions: {},
|
||||
nations: [],
|
||||
adressentypen: [],
|
||||
firmen: [],
|
||||
filteredFirmen: [],
|
||||
abortController: {
|
||||
suggestions: null,
|
||||
places: null
|
||||
}
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
orte() {
|
||||
return this.places.filter(ort => ort.name == this.addressData.gemeinde);
|
||||
},
|
||||
gemeinden() {
|
||||
return Object.values(this.places.reduce((res,place) => {
|
||||
res[place.name] = place;
|
||||
return res;
|
||||
}, {}));
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
uid() {
|
||||
this.$refs.table.tabulator.setData('api/frontend/v1/stv/Kontakt/getAdressen/' + this.uid);
|
||||
},
|
||||
},
|
||||
methods:{
|
||||
actionNewAdress() {
|
||||
this.resetModal();
|
||||
this.$refs.adressModal.show();
|
||||
},
|
||||
actionEditAdress(adress_id) {
|
||||
this.statusNew = false;
|
||||
this.loadAdress(adress_id).then(() => {
|
||||
if(this.addressData.adresse_id)
|
||||
{
|
||||
this.loadPlaces(this.addressData.plz);
|
||||
this.$refs.adressModal.show();
|
||||
|
||||
}
|
||||
});
|
||||
},
|
||||
actionDeleteAdress(adress_id) {
|
||||
this.loadAdress(adress_id).then(() => {
|
||||
if(this.addressData.adresse_id)
|
||||
if(this.addressData.heimatadresse)
|
||||
this.$fhcAlert.alertError(this.$p.t('person', 'error_deleteHomeAdress'));
|
||||
else {
|
||||
this.$fhcAlert
|
||||
.confirmDelete()
|
||||
.then(result => result
|
||||
? adress_id
|
||||
: Promise.reject({handled: true}))
|
||||
.then(this.deleteAddress)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
}
|
||||
});
|
||||
},
|
||||
addNewAddress(addressData) {
|
||||
this.$fhcApi.post('api/frontend/v1/stv/kontakt/addNewAddress/' + this.uid,
|
||||
this.addressData
|
||||
).then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||
this.hideModal('adressModal');
|
||||
this.resetModal();
|
||||
}).catch(this.$fhcAlert.handleSystemError)
|
||||
.finally(() => {
|
||||
window.scrollTo(0, 0);
|
||||
this.reload();
|
||||
});
|
||||
},
|
||||
reload() {
|
||||
this.$refs.table.reloadTable();
|
||||
},
|
||||
loadAdress(adress_id) {
|
||||
this.statusNew = false;
|
||||
return this.$fhcApi.get('api/frontend/v1/stv/kontakt/loadAddress/' + adress_id)
|
||||
.then(result => {
|
||||
this.addressData = result.data;
|
||||
return result;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
updateAddress(adress_id) {
|
||||
this.$fhcApi.post('api/frontend/v1/stv/kontakt/updateAddress/' + adress_id,
|
||||
this.addressData
|
||||
).then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||
this.hideModal('adressModal');
|
||||
this.resetModal();
|
||||
}).catch(this.$fhcAlert.handleSystemError)
|
||||
.finally(() => {
|
||||
window.scrollTo(0, 0);
|
||||
this.reload();
|
||||
});
|
||||
},
|
||||
deleteAddress(adress_id) {
|
||||
this.$fhcApi.post('api/frontend/v1/stv/kontakt/deleteAddress/' + adress_id)
|
||||
.then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
|
||||
}).catch(this.$fhcAlert.handleSystemError)
|
||||
.finally(()=> {
|
||||
window.scrollTo(0, 0);
|
||||
this.reload();
|
||||
});
|
||||
},
|
||||
loadPlaces() {
|
||||
if (this.abortController.places)
|
||||
this.abortController.places.abort();
|
||||
if (this.addressData.nation != 'A' || !this.addressData.plz)
|
||||
return;
|
||||
|
||||
this.abortController.places = new AbortController();
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/address/getPlaces/' + this.addressData.plz, undefined, {
|
||||
signal: this.abortController.places.signal
|
||||
})
|
||||
.then(result => {
|
||||
this.places = result.data;
|
||||
});
|
||||
/* .catch(error => {
|
||||
if (error.code != "ERR_CANCELED")
|
||||
window.setTimeout(this.loadPlaces, 100);
|
||||
else
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
});*/
|
||||
},
|
||||
search(event) {
|
||||
return this.$fhcApi
|
||||
.get('api/frontend/v1/stv/kontakt/getFirmen/' + event.query)
|
||||
.then(result => {
|
||||
this.filteredFirmen = result.data.retval;
|
||||
});
|
||||
},
|
||||
hideModal(modalRef) {
|
||||
this.$refs[modalRef].hide();
|
||||
},
|
||||
resetModal() {
|
||||
this.addressData = {};
|
||||
|
||||
this.addressData.strasse = null;
|
||||
this.addressData.zustelladresse = true;
|
||||
this.addressData.heimatadresse = true;
|
||||
this.addressData.rechnungsadresse = false;
|
||||
this.addressData.co_name = null;
|
||||
this.addressData.firma_id = null;
|
||||
this.addressData.name = null;
|
||||
this.addressData.anmerkung = null;
|
||||
this.addressData.typ = 'h';
|
||||
this.addressData.nation = 'A';
|
||||
this.addressData.plz = null;
|
||||
|
||||
this.statusNew = true;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/address/getNations')
|
||||
.then(result => {
|
||||
this.nations = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/kontakt/getAdressentypen')
|
||||
.then(result => {
|
||||
this.adressentypen = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError)
|
||||
},
|
||||
template: `
|
||||
<div class="stv-details-kontakt-address h-100 pt-3">
|
||||
|
||||
<!--Modal: AddressModal-->
|
||||
<bs-modal ref="adressModal">
|
||||
<template #title>
|
||||
<p v-if="statusNew" class="fw-bold mt-3">{{$p.t('person', 'adresse_new')}}</p>
|
||||
<p v-else class="fw-bold mt-3">{{$p.t('person', 'adresse_edit')}}</p>
|
||||
</template>
|
||||
<form-form class="row g-3 mt-2" ref="addressData">
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="select"
|
||||
name="adressentyp"
|
||||
:label="$p.t('global/typ')"
|
||||
v-model="addressData.typ"
|
||||
>
|
||||
<option
|
||||
v-for="typ in adressentypen"
|
||||
:key="typ.adressentyp_kurzbz"
|
||||
:value="typ.adressentyp_kurzbz"
|
||||
>
|
||||
{{typ.bezeichnung}}
|
||||
</option>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
name="strasse"
|
||||
:label="$p.t('person/strasse')"
|
||||
v-model="addressData.strasse"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="select"
|
||||
name="nation"
|
||||
:label="$p.t('person/nation')"
|
||||
v-model="addressData.nation"
|
||||
>
|
||||
<option
|
||||
v-for="nation in 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
|
||||
type="text"
|
||||
name="plz"
|
||||
:label="$p.t('person/plz') + ' *'"
|
||||
v-model="addressData.plz"
|
||||
required
|
||||
@input="loadPlaces"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
v-if="addressData.nation == 'A'"
|
||||
type="select"
|
||||
name="gemeinde"
|
||||
:label="$p.t('person/gemeinde')"
|
||||
v-model="addressData.gemeinde"
|
||||
>
|
||||
<option v-if="!gemeinden.length" disabled>{{$p.t('ui', 'bittePlzWaehlen')}}</option>
|
||||
<option
|
||||
v-for="gemeinde in gemeinden"
|
||||
:key="gemeinde.name"
|
||||
:value="gemeinde.name"
|
||||
>
|
||||
{{gemeinde.name}}
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-else
|
||||
type="text"
|
||||
name="addressData.gemeinde"
|
||||
v-model="addressData.gemeinde"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
v-if="addressData.nation == 'A'"
|
||||
type="select"
|
||||
name="ort"
|
||||
:label="$p.t('person/ort')"
|
||||
v-model="addressData.ort"
|
||||
>
|
||||
<option v-if="!orte.length" disabled>{{$p.t('ui', 'bitteGemeindeWaehlen')}}</option>
|
||||
<option
|
||||
v-for="ort in orte"
|
||||
:key="ort.ortschaftsname"
|
||||
:value="ort.ortschaftsname"
|
||||
>
|
||||
{{ort.ortschaftsname}}
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-else
|
||||
type="text"
|
||||
name="ort"
|
||||
v-model="addressData.ort"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-4">
|
||||
<form-input
|
||||
container-class="form-check"
|
||||
type="checkbox"
|
||||
name="heimatadresse"
|
||||
:label="$p.t('person/heimatadresse')"
|
||||
v-model="addressData.heimatadresse"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-4">
|
||||
<form-input
|
||||
container-class="form-check"
|
||||
type="checkbox"
|
||||
name="zustelladresse"
|
||||
:label="$p.t('person/zustelladresse')"
|
||||
v-model="addressData.zustelladresse"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
name="co_name"
|
||||
:label="$p.t('person/co_name')"
|
||||
v-model="addressData.co_name"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-4">
|
||||
<form-input
|
||||
container-class="form-check"
|
||||
type="checkbox"
|
||||
name="rechnungsadresse"
|
||||
:label="$p.t('person/rechnungsadresse')"
|
||||
v-model="addressData.rechnungsadresse"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="statusNew" class="row mb-3">
|
||||
<form-input
|
||||
type="autocomplete"
|
||||
:label="$p.t('person/firma')"
|
||||
name="firma_name"
|
||||
v-model="addressData.firma"
|
||||
optionLabel="name"
|
||||
:suggestions="filteredFirmen"
|
||||
@complete="search"
|
||||
:min-length="3"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div v-else class="row mb-3">
|
||||
<form-input
|
||||
v-if="addressData.firmenname"
|
||||
type="text"
|
||||
name="name"
|
||||
:label="$p.t('person/firma')"
|
||||
v-model="addressData.firmenname"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-else
|
||||
type="autocomplete"
|
||||
:label="$p.t('person/firma')"
|
||||
name="firma_name"
|
||||
v-model="addressData.firma"
|
||||
optionLabel="name"
|
||||
:suggestions="filteredFirmen"
|
||||
@complete="search"
|
||||
:min-length="3"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<input type="hidden" class="form-control" id="firma_id" v-model="addressData.firma_id">
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
name="firma_zusatz"
|
||||
:label="$p.t('person/firma_zusatz')"
|
||||
v-model="addressData.name"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
name="anmerkung"
|
||||
:label="$p.t('global/anmerkung')"
|
||||
v-model="addressData.anmerkung"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
</form-form>
|
||||
|
||||
<template #footer>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{$p.t('ui', 'abbrechen')}}</button>
|
||||
<button v-if="statusNew" type="button" class="btn btn-primary" @click="addNewAddress()">OK</button>
|
||||
<button v-else type="button" class="btn btn-primary" @click="updateAddress(addressData.adresse_id)">OK</button>
|
||||
</template>
|
||||
</bs-modal>
|
||||
|
||||
<core-filter-cmpt
|
||||
ref="table"
|
||||
:tabulator-options="tabulatorOptions"
|
||||
:tabulator-events="tabulatorEvents"
|
||||
table-only
|
||||
:side-menu="false"
|
||||
reload
|
||||
new-btn-show
|
||||
new-btn-label="Adresse"
|
||||
@click:new="actionNewAdress"
|
||||
>
|
||||
</core-filter-cmpt>
|
||||
</div>`
|
||||
};
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
import {CoreFilterCmpt} from "../../../../filter/Filter.js";
|
||||
import BsModal from "../../../../Bootstrap/Modal.js";
|
||||
import FormForm from '../../../../Form/Form.js';
|
||||
import FormInput from '../../../../Form/Input.js';
|
||||
|
||||
export default{
|
||||
components: {
|
||||
CoreFilterCmpt,
|
||||
BsModal,
|
||||
FormForm,
|
||||
FormInput
|
||||
},
|
||||
props: {
|
||||
uid: Number
|
||||
},
|
||||
data() {
|
||||
return{
|
||||
tabulatorOptions: {
|
||||
ajaxURL: 'api/frontend/v1/stv/Kontakt/getBankverbindung/' + this.uid,
|
||||
ajaxRequestFunc: this.$fhcApi.get,
|
||||
ajaxResponse: (url, params, response) => response.data,
|
||||
columns:[
|
||||
{title:"Name", field:"name"},
|
||||
{title:"Anschrift", field:"anschrift", visible:false},
|
||||
{title:"BIC", field:"bic"},
|
||||
{title:"BLZ", field:"blz", visible:false},
|
||||
{title:"IBAN", field:"iban"},
|
||||
{title:"Kontonummer", field:"kontonr", visible:false},
|
||||
{title:"Typ", field:"typ", visible:false,
|
||||
formatter: (cell, formatterParams, onRendered) => {
|
||||
let output;
|
||||
switch(cell.getValue()){
|
||||
case "p":
|
||||
output = "Privatkonto";
|
||||
break;
|
||||
case "f":
|
||||
output = "Firmenkonto";
|
||||
break;
|
||||
default:
|
||||
output = cell.getValue();
|
||||
}
|
||||
return output;}
|
||||
},
|
||||
{
|
||||
title:"Verrechnung",
|
||||
field:"verrechnung",
|
||||
visible:false,
|
||||
formatter:"tickCross",
|
||||
hozAlign:"center",
|
||||
formatterParams: {
|
||||
tickElement: '<i class="fa fa-check text-success"></i>',
|
||||
crossElement: '<i class="fa fa-xmark text-danger"></i>'
|
||||
}
|
||||
},
|
||||
{title:"Person_id", field:"person_id", visible:false},
|
||||
{title:"Bankverbindung_id", field:"bankverbindung_id", visible:false},
|
||||
{title: 'Aktionen', field: 'actions',
|
||||
minWidth: 150, // Ensures Action-buttons will be always fully displayed
|
||||
formatter: (cell, formatterParams, onRendered) => {
|
||||
let container = document.createElement('div');
|
||||
container.className = "d-flex gap-2";
|
||||
|
||||
let button = document.createElement('button');
|
||||
button.className = 'btn btn-outline-secondary btn-action';
|
||||
button.innerHTML = '<i class="fa fa-edit"></i>';
|
||||
button.addEventListener('click', (event) =>
|
||||
this.actionEditBankverbindung(cell.getData().bankverbindung_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.addEventListener('click', () =>
|
||||
this.actionDeleteBankverbindung(cell.getData().bankverbindung_id)
|
||||
);
|
||||
container.append(button);
|
||||
|
||||
return container;
|
||||
},
|
||||
frozen: true
|
||||
},
|
||||
],
|
||||
layout: 'fitDataFill',
|
||||
layoutColumnsOnNewData: false,
|
||||
height: 'auto',
|
||||
selectable: true,
|
||||
index: 'bankverbindung_id',
|
||||
persistenceID: 'stv-details-kontakt-bankaccount'
|
||||
},
|
||||
tabulatorEvents: [
|
||||
{
|
||||
event: 'tableBuilt',
|
||||
handler: async() => {
|
||||
await this.$p.loadCategory(['global','person']);
|
||||
|
||||
let cm = this.$refs.table.tabulator.columnManager;
|
||||
|
||||
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('typ').component.updateDefinition({
|
||||
title: this.$p.t('global', 'typ')
|
||||
});
|
||||
cm.getColumnByField('verrechnung').component.updateDefinition({
|
||||
title: this.$p.t('person', 'verrechnung')
|
||||
});
|
||||
}
|
||||
}
|
||||
],
|
||||
lastSelected: null,
|
||||
bankverbindungData: {
|
||||
verrechnung: true,
|
||||
typ: 'p'
|
||||
},
|
||||
statusNew: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
uid(){
|
||||
this.$refs.table.tabulator.setData('api/frontend/v1/stv/Kontakt/getBankverbindung/' + this.uid);
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
actionNewBankverbindung(){
|
||||
this.resetModal();
|
||||
this.$refs.bankverbindungModal.show();
|
||||
},
|
||||
actionEditBankverbindung(bankverbindung_id){
|
||||
this.statusNew = false;
|
||||
this.loadBankverbindung(bankverbindung_id).then(() => {
|
||||
if(this.bankverbindungData.bankverbindung_id)
|
||||
this.$refs.bankverbindungModal.show();
|
||||
});
|
||||
},
|
||||
actionDeleteBankverbindung(bankverbindung_id){
|
||||
this.loadBankverbindung(bankverbindung_id).then(() => {
|
||||
this.$fhcAlert
|
||||
.confirmDelete()
|
||||
.then(result => result
|
||||
? bankverbindung_id
|
||||
: Promise.reject({handled: true}))
|
||||
.then(this.deleteBankverbindung)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
});
|
||||
},
|
||||
addNewBankverbindung(bankverbindungData) {
|
||||
this.$fhcApi.post('api/frontend/v1/stv/kontakt/addNewBankverbindung/' + this.uid,
|
||||
this.bankverbindungData
|
||||
).then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||
this.hideModal('bankverbindungModal');
|
||||
this.resetModal();
|
||||
}).catch(this.$fhcAlert.handleSystemError)
|
||||
.finally(() => {
|
||||
window.scrollTo(0, 0);
|
||||
this.reload();
|
||||
});
|
||||
},
|
||||
loadBankverbindung(bankverbindung_id){
|
||||
this.statusNew = false;
|
||||
return this.$fhcApi.get('api/frontend/v1/stv/kontakt/loadBankverbindung/' + bankverbindung_id)
|
||||
.then(
|
||||
result => {
|
||||
this.bankverbindungData = result.data;
|
||||
return result;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
updateBankverbindung(bankverbindung_id){
|
||||
this.$fhcApi.post('api/frontend/v1/stv/kontakt/updateBankverbindung/' + bankverbindung_id,
|
||||
this.bankverbindungData)
|
||||
.then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||
this.hideModal('bankverbindungModal');
|
||||
this.resetModal();
|
||||
}).catch(this.$fhcAlert.handleSystemError)
|
||||
.finally(() => {
|
||||
window.scrollTo(0, 0);
|
||||
this.reload();
|
||||
});
|
||||
},
|
||||
deleteBankverbindung(bankverbindung_id){
|
||||
this.$fhcApi.post('api/frontend/v1/stv/kontakt/deleteBankverbindung/' + bankverbindung_id)
|
||||
.then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
|
||||
}).catch(this.$fhcAlert.handleSystemError)
|
||||
.finally(()=> {
|
||||
window.scrollTo(0, 0);
|
||||
this.resetModal();
|
||||
this.reload();
|
||||
});
|
||||
},
|
||||
hideModal(modalRef){
|
||||
this.$refs[modalRef].hide();
|
||||
},
|
||||
reload(){
|
||||
this.$refs.table.reloadTable();
|
||||
},
|
||||
resetModal(){
|
||||
this.bankverbindungData = {};
|
||||
this.bankverbindungData.name = "";
|
||||
this.bankverbindungData.anschrift = "";
|
||||
this.bankverbindungData.iban = "";
|
||||
this.bankverbindungData.bic = "";
|
||||
this.bankverbindungData.kontonr = "";
|
||||
this.bankverbindungData.blz = "";
|
||||
this.bankverbindungData.bic = "";
|
||||
this.bankverbindungData.verrechnung = true;
|
||||
this.bankverbindungData.typ = 'p';
|
||||
|
||||
this.statusNew = true;
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div class="stv-details-kontakt-bankaccount h-100 pt-3">
|
||||
|
||||
<!--Modal: Bankverbindung-->
|
||||
<BsModal title="Bankverbindung anlegen" ref="bankverbindungModal">
|
||||
<template #title>
|
||||
<p v-if="statusNew" class="fw-bold mt-3">{{$p.t('person', 'bankvb_new')}}</p>
|
||||
<p v-else class="fw-bold mt-3">{{$p.t('person', 'bankvb_edit')}}</p>
|
||||
</template>
|
||||
|
||||
<form-form class="row g-3" ref="bankverbindungData">
|
||||
|
||||
<div class="row my-3">
|
||||
<form-input
|
||||
type="text"
|
||||
name="name"
|
||||
label="Name"
|
||||
v-model="bankverbindungData.name"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
name="anschrift"
|
||||
:label="$p.t('person/anschrift')"
|
||||
v-model="bankverbindungData.anschrift"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
name="iban"
|
||||
label="IBAN *"
|
||||
v-model="bankverbindungData.iban"
|
||||
required
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
name="bic"
|
||||
label="BIC"
|
||||
v-model="bankverbindungData.bic"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
name="kontonr"
|
||||
:label="$p.t('person/kontonr')"
|
||||
v-model="bankverbindungData.kontonr"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
name="blz"
|
||||
:label="$p.t('person/blz')"
|
||||
v-model="bankverbindungData.blz"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="select"
|
||||
name="typ"
|
||||
:label="$p.t('global/typ')"
|
||||
v-model="bankverbindungData.typ"
|
||||
required
|
||||
>
|
||||
<option value="p">{{$p.t('person', 'privatkonto')}}</option>
|
||||
<option value="f">{{$p.t('person', 'firmenkonto')}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="form-check"
|
||||
type="checkbox"
|
||||
name="verrechnung"
|
||||
:label="$p.t('person/verrechnung')"
|
||||
v-model="bankverbindungData.verrechnung"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</form-form>
|
||||
|
||||
<template #footer>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{$p.t('ui', 'abbrechen')}}</button>
|
||||
<button v-if="statusNew" type="button" class="btn btn-primary" @click="addNewBankverbindung()">OK</button>
|
||||
<button v-else type="button" class="btn btn-primary" @click="updateBankverbindung(bankverbindungData.bankverbindung_id)">OK</button>
|
||||
</template>
|
||||
|
||||
</BsModal>
|
||||
|
||||
<core-filter-cmpt
|
||||
ref="table"
|
||||
:tabulator-options="tabulatorOptions"
|
||||
:tabulator-events="tabulatorEvents"
|
||||
table-only
|
||||
:side-menu="false"
|
||||
reload
|
||||
new-btn-show
|
||||
new-btn-label="Bankverbindung"
|
||||
@click:new="actionNewBankverbindung"
|
||||
>
|
||||
</core-filter-cmpt>
|
||||
</div>`
|
||||
};
|
||||
@@ -0,0 +1,401 @@
|
||||
import {CoreFilterCmpt} from "../../../../filter/Filter.js";
|
||||
import BsModal from "../../../../Bootstrap/Modal.js";
|
||||
import PvAutoComplete from "../../../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js";
|
||||
import FormForm from '../../../../Form/Form.js';
|
||||
import FormInput from '../../../../Form/Input.js';
|
||||
|
||||
export default{
|
||||
components: {
|
||||
CoreFilterCmpt,
|
||||
PvAutoComplete,
|
||||
BsModal,
|
||||
FormForm,
|
||||
FormInput
|
||||
},
|
||||
props: {
|
||||
uid: Number
|
||||
},
|
||||
data() {
|
||||
return{
|
||||
tabulatorOptions: {
|
||||
ajaxURL: 'api/frontend/v1/stv/Kontakt/getKontakte/' + this.uid,
|
||||
ajaxRequestFunc: this.$fhcApi.get,
|
||||
ajaxResponse: (url, params, response) => response.data,
|
||||
columns:[
|
||||
{title:"Typ", field:"kontakttyp"},
|
||||
{title:"Kontakt", field:"kontakt"},
|
||||
{
|
||||
title:"Zustellung",
|
||||
field:"zustellung",
|
||||
formatter:"tickCross",
|
||||
hozAlign:"center",
|
||||
formatterParams: {
|
||||
tickElement: '<i class="fa fa-check text-success"></i>',
|
||||
crossElement: '<i class="fa fa-xmark text-danger"></i>'
|
||||
}
|
||||
},
|
||||
{title:"Anmerkung", field:"anmerkung"},
|
||||
{title:"Firma", field:"name", visible:false},
|
||||
{title:"Standort", field:"bezeichnung", visible:false},
|
||||
{title:"Firma_id", field:"firma_id", visible:false},
|
||||
{title:"Person_id", field:"person_id", visible:false},
|
||||
{title:"Kontakt_id", field:"kontakt_id", visible:false},
|
||||
{title:"Standort_id", field:"standort_id", visible:false},
|
||||
{title:"letzte Änderung", field:"lastupdate", 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.addEventListener('click', (event) =>
|
||||
this.actionEditContact(cell.getData().kontakt_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.addEventListener('click', () =>
|
||||
this.actionDeleteContact(cell.getData().kontakt_id)
|
||||
);
|
||||
container.append(button);
|
||||
|
||||
return container;
|
||||
},
|
||||
frozen: true
|
||||
},
|
||||
],
|
||||
layout: 'fitDataFill',
|
||||
layoutColumnsOnNewData: false,
|
||||
height: 'auto',
|
||||
selectable: true,
|
||||
index: 'kontakt_id',
|
||||
persistenceID: 'stv-details-kontakt-contact'
|
||||
},
|
||||
tabulatorEvents: [
|
||||
{
|
||||
event: 'tableBuilt',
|
||||
handler: async() => {
|
||||
await this.$p.loadCategory(['notiz','global','person']);
|
||||
|
||||
let cm = this.$refs.table.tabulator.columnManager;
|
||||
|
||||
cm.getColumnByField('kontakttyp').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')
|
||||
});
|
||||
}}
|
||||
],
|
||||
lastSelected: null,
|
||||
contactData: {
|
||||
zustellung: true,
|
||||
kontakttyp: 'email',
|
||||
firma_id: null
|
||||
},
|
||||
statusNew: true,
|
||||
kontakttypen: [],
|
||||
firmen: [],
|
||||
filteredFirmen: [],
|
||||
filteredOrte: null,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
uid() {
|
||||
this.$refs.table.tabulator.setData('api/frontend/v1/stv/Kontakt/getKontakte/' + this.uid);
|
||||
},
|
||||
contactData: {
|
||||
handler(newVal) {
|
||||
if (newVal.firma && newVal.firma.firma_id !== null && typeof newVal.firma.firma_id !== 'undefined') {
|
||||
this.loadStandorte(this.contactData.firma.firma_id);
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
actionNewContact(){
|
||||
this.resetModal();
|
||||
this.$refs.contactModal.show();
|
||||
},
|
||||
actionEditContact(contact_id){
|
||||
this.statusNew = false;
|
||||
this.loadContact(contact_id);
|
||||
this.$refs.contactModal.show();
|
||||
},
|
||||
actionDeleteContact(contact_id){
|
||||
this.loadContact(contact_id);
|
||||
|
||||
this.$fhcAlert
|
||||
.confirmDelete()
|
||||
.then(result => result
|
||||
? contact_id
|
||||
: Promise.reject({handled: true}))
|
||||
.then(this.deleteContact)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
addNewContact(formData) {
|
||||
this.$fhcApi.post('api/frontend/v1/stv/kontakt/addNewContact/' + this.uid,
|
||||
this.contactData)
|
||||
.then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||
this.hideModal("contactModal");
|
||||
this.resetModal();
|
||||
}).catch(this.$fhcAlert.handleSystemError)
|
||||
.finally(() => {
|
||||
window.scrollTo(0, 0);
|
||||
this.reload();
|
||||
});
|
||||
},
|
||||
loadContact(contact_id){
|
||||
this.statusNew = false;
|
||||
if(this.contactData.firma_id)
|
||||
this.loadStandorte(this.contactData.firma_id);
|
||||
return this.$fhcApi.get('api/frontend/v1/stv/kontakt/loadContact/' + contact_id)
|
||||
.then(
|
||||
result => {
|
||||
this.contactData = result.data;
|
||||
return result;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
deleteContact(kontakt_id){
|
||||
this.$fhcApi.post('api/frontend/v1/stv/kontakt/deleteContact/' + kontakt_id)
|
||||
.then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError)
|
||||
.finally(()=> {
|
||||
window.scrollTo(0, 0);
|
||||
this.resetModal();
|
||||
this.reload();
|
||||
});
|
||||
},
|
||||
updateContact(kontakt_id){
|
||||
this.$fhcApi.post('api/frontend/v1/stv/kontakt/updateContact/' + kontakt_id,
|
||||
this.contactData).
|
||||
then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||
this.hideModal('contactModal');
|
||||
this.resetModal();
|
||||
this.reload();
|
||||
}).catch(this.$fhcAlert.handleSystemError)
|
||||
.finally(()=> {
|
||||
window.scrollTo(0, 0);
|
||||
this.reload();
|
||||
});
|
||||
},
|
||||
|
||||
hideModal(modalRef){
|
||||
this.$refs[modalRef].hide();
|
||||
},
|
||||
reload(){
|
||||
this.$refs.table.reloadTable();
|
||||
},
|
||||
searchFirma(event) {
|
||||
return this.$fhcApi
|
||||
.get('api/frontend/v1/stv/kontakt/getFirmen/' + event.query)
|
||||
.then(result => {
|
||||
this.filteredFirmen = result.data.retval;
|
||||
});
|
||||
},
|
||||
loadStandorte(firmen_id) {
|
||||
return this.$fhcApi
|
||||
.get('api/frontend/v1/stv/kontakt/getStandorteByFirma/' + firmen_id)
|
||||
.then(result => {
|
||||
this.filteredOrte = result.data;
|
||||
});
|
||||
},
|
||||
resetModal(){
|
||||
this.contactData = {};
|
||||
this.contactData.zustellung = true;
|
||||
this.contactData.kontakttyp = 'email';
|
||||
this.contactData.kontakt = '';
|
||||
this.contactData.anmerkung = null;
|
||||
this.contactData.firma_id = null;
|
||||
this.contactData.name = null;
|
||||
this.contactData.standort_id = null;
|
||||
this.contactData.bezeichnung = null;
|
||||
|
||||
this.statusNew = true;
|
||||
},
|
||||
},
|
||||
created(){
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/kontakt/getKontakttypen')
|
||||
.then(result => {
|
||||
this.kontakttypen = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
template: `
|
||||
<div class="stv-details-kontakt-contact h-100 pt-3">
|
||||
|
||||
<!--Modal: contactModal-->
|
||||
<BsModal ref="contactModal">
|
||||
<template #title>
|
||||
<p v-if="statusNew" class="fw-bold mt-3">{{$p.t('person', 'kontakt_new')}}</p>
|
||||
<p v-else class="fw-bold mt-3">{{$p.t('person', 'kontakt_edit')}}</p>
|
||||
</template>
|
||||
|
||||
<form-form class="row g-3" ref="contactData">
|
||||
|
||||
<div class="row my-3">
|
||||
|
||||
<form-input
|
||||
type="select"
|
||||
name="typ"
|
||||
:label="$p.t('global/typ')"
|
||||
v-model="contactData.kontakttyp">
|
||||
>
|
||||
<option value="">keine Auswahl</option>
|
||||
<option v-for="typ in kontakttypen" :key="typ.kontakttyp_kurzbz" :value="typ.kontakttyp" >{{typ.kontakttyp}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
name="kontakt"
|
||||
:label="$p.t('global/kontakt')+ ' *'"
|
||||
v-model="contactData.kontakt">
|
||||
required
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
type="text"
|
||||
name="anmerkung"
|
||||
:label="$p.t('global/anmerkung')"
|
||||
v-model="contactData.anmerkung">
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-4">
|
||||
<form-input
|
||||
container-class="form-check"
|
||||
type="checkbox"
|
||||
name="zustellung"
|
||||
:label="$p.t('person/zustellung')"
|
||||
v-model="contactData.zustellung"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="statusNew" class="row mb-3">
|
||||
<form-input
|
||||
type="autocomplete"
|
||||
:label="$p.t('person/firma')"
|
||||
name="firma_name"
|
||||
v-model="contactData.firma"
|
||||
optionLabel="name"
|
||||
:suggestions="filteredFirmen"
|
||||
@complete="searchFirma"
|
||||
:min-length="3"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div v-else class="row mb-3">
|
||||
<form-input
|
||||
v-if="contactData.name"
|
||||
type="text"
|
||||
name="name"
|
||||
:label="$p.t('person/firma')"
|
||||
v-model="contactData.name"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-else
|
||||
type="autocomplete"
|
||||
:label="$p.t('person/firma')"
|
||||
name="firma_name"
|
||||
v-model="contactData.firma"
|
||||
optionLabel="name"
|
||||
:suggestions="filteredFirmen"
|
||||
@complete="searchFirma"
|
||||
:min-length="3"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<input type="hidden" class="form-control" id="firma_id" v-model="contactData.firma_id">
|
||||
|
||||
<input type="hidden" class="form-control" id="standort_id" v-model="contactData.standort_id">
|
||||
|
||||
<div class="row mb-3" v-if="contactData.standort_id || filteredOrte">
|
||||
<form-input
|
||||
v-if="contactData.name"
|
||||
type="text"
|
||||
name="name"
|
||||
:label="$p.t('person/firma') + ' / ' + $p.t('person/standort')"
|
||||
v-model="contactData.bezeichnung"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-else
|
||||
type="select"
|
||||
name="ort"
|
||||
:label="$p.t('person/standort')"
|
||||
v-model="contactData.standort_id"
|
||||
>
|
||||
<option v-if="filteredOrte" disabled>{{$p.t('ui', 'bitteStandortWaehlen')}}</option>
|
||||
<option
|
||||
v-for="ort in filteredOrte"
|
||||
:key="ort.standort_id"
|
||||
:value="ort.standort_id"
|
||||
>
|
||||
{{ort.bezeichnung}}
|
||||
</option>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
</form-form>
|
||||
|
||||
<template #footer>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{$p.t('ui', 'abbrechen')}}</button>
|
||||
<button v-if="statusNew" type="button" class="btn btn-primary" @click="addNewContact()">OK</button>
|
||||
<button v-else type="button" class="btn btn-primary" @click="updateContact(contactData.kontakt_id)">OK</button>
|
||||
</template>
|
||||
</BsModal>
|
||||
|
||||
<core-filter-cmpt
|
||||
ref="table"
|
||||
:tabulator-options="tabulatorOptions"
|
||||
:tabulator-events="tabulatorEvents"
|
||||
table-only
|
||||
:side-menu="false"
|
||||
reload
|
||||
new-btn-show
|
||||
new-btn-label="Kontakt"
|
||||
@click:new="actionNewContact"
|
||||
>
|
||||
</core-filter-cmpt>
|
||||
</div>`
|
||||
};
|
||||
@@ -0,0 +1,257 @@
|
||||
import {CoreFilterCmpt} from "../../../filter/Filter.js";
|
||||
import FormInput from "../../../Form/Input.js";
|
||||
import KontoNew from "./Konto/New.js";
|
||||
import KontoEdit from "./Konto/Edit.js";
|
||||
|
||||
const LOCAL_STORAGE_ID_FILTER = 'stv_details_konto_2024-01-11_filter';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFilterCmpt,
|
||||
FormInput,
|
||||
KontoNew,
|
||||
KontoEdit
|
||||
},
|
||||
props: {
|
||||
modelValue: Object,
|
||||
config: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filter: false,
|
||||
studiengang_kz: false,
|
||||
counterdate: new Date()
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
personIds() {
|
||||
if (this.modelValue.person_id)
|
||||
return [this.modelValue.person_id];
|
||||
return this.modelValue.map(e => e.person_id);
|
||||
},
|
||||
stg_kz() {
|
||||
if (this.modelValue.studiengang_kz)
|
||||
return this.modelValue.studiengang_kz;
|
||||
let values = this.modelValue.map(e => e.studiengang_kz).filter((v,i,a) => a.indexOf(v) === i);
|
||||
if (values.length != 1)
|
||||
return '';
|
||||
return values[0];
|
||||
},
|
||||
studiengang_kz_intern: {
|
||||
get() {
|
||||
if (this.stg_kz)
|
||||
return this.studiengang_kz;
|
||||
else
|
||||
return false;
|
||||
},
|
||||
set(value) {
|
||||
this.studiengang_kz = value;
|
||||
}
|
||||
},
|
||||
tabulatorColumns() {
|
||||
const columns = { ...this.config.columns };
|
||||
if (!columns.actions)
|
||||
columns.actions = {
|
||||
title: '',
|
||||
frozen: true
|
||||
};
|
||||
columns.actions.formatter = cell => {
|
||||
let container = document.createElement('div');
|
||||
container.className = "d-flex gap-2";
|
||||
|
||||
let button = document.createElement('button');
|
||||
button.className = 'btn btn-outline-secondary';
|
||||
button.innerHTML = '<i class="fa fa-edit"></i>';
|
||||
button.addEventListener('click', () =>
|
||||
this.$refs.edit.open(cell.getData())
|
||||
);
|
||||
container.append(button);
|
||||
|
||||
button = document.createElement('button');
|
||||
button.className = 'btn btn-outline-secondary';
|
||||
button.innerHTML = '<i class="fa fa-trash"></i>';
|
||||
button.addEventListener('click', evt => {
|
||||
evt.stopPropagation();
|
||||
this.$fhcAlert
|
||||
.confirmDelete()
|
||||
.then(result => result ? cell.getData().buchungsnr : Promise.reject({handled:true}))
|
||||
.then(this.$fhcApi.factory.stv.konto.delete)
|
||||
.then(() => {
|
||||
// TODO(chris): deleting a child also removes the siblings!
|
||||
//cell.getRow().delete();
|
||||
this.reload();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
});
|
||||
container.append(button);
|
||||
|
||||
return container;
|
||||
};
|
||||
|
||||
return Object.values(columns);
|
||||
},
|
||||
tabulatorOptions() {
|
||||
return this.$fhcApi.factory.stv.konto.tabulatorConfig({
|
||||
dataTree: true,
|
||||
columns: this.tabulatorColumns,
|
||||
selectable: true,
|
||||
selectableRangeMode: 'click',
|
||||
index: 'buchungsnr',
|
||||
persistenceID: 'stv-details-konto'
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue() {
|
||||
this.$refs.table.reloadTable();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reload() {
|
||||
this.$refs.table.reloadTable();
|
||||
},
|
||||
updateData(data) {
|
||||
if (!data)
|
||||
return this.reload();
|
||||
// TODO(chris): check children (!delete?, multiple children)
|
||||
//this.$refs.table.tabulator.updateOrAddData(data.map(row => row.buchungsnr_verweis ? {buchungsnr:row.buchungsnr_verweis, _children:row} : row));
|
||||
this.$refs.table.tabulator.updateOrAddData(data);
|
||||
},
|
||||
actionNew() {
|
||||
this.$refs.new.open();
|
||||
},
|
||||
actionCounter(selected) {
|
||||
this.$fhcApi
|
||||
.factory.stv.konto.counter({
|
||||
buchungsnr: selected.map(e => e.buchungsnr),
|
||||
buchungsdatum: this.counterdate
|
||||
})
|
||||
.then(result => result.data)
|
||||
.then(this.updateData)
|
||||
.then(() => this.$p.t('ui/gespeichert'))
|
||||
.then(this.$fhcAlert.alertSuccess)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
downloadPdf(selected) {
|
||||
if (Array.isArray(this.modelValue)) {
|
||||
let id_uid = this.modelValue.reduce((a,c) => {
|
||||
if (c.uid)
|
||||
a[c.person_id] = c.uid;
|
||||
return a
|
||||
}, {});
|
||||
let persons = selected.reduce((a,c) => {
|
||||
if (!a[c.person_id]) {
|
||||
let uid = id_uid[c.person_id] || '';
|
||||
a[c.person_id] = uid + '&buchungsnummern=' + c.buchungsnr;
|
||||
} else {
|
||||
a[c.person_id] += ';' + c.buchungsnr;
|
||||
}
|
||||
return a;
|
||||
}, {});
|
||||
Object.values(persons).forEach(part => window.open(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
'content/pdfExport.php?xml=konto.rdf.php&xsl=Zahlung&uid=' +
|
||||
part,
|
||||
'_blank'
|
||||
));
|
||||
} else {
|
||||
window.open(
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
'content/pdfExport.php?xml=konto.rdf.php&xsl=Zahlung&uid=' +
|
||||
(this.modelValue.uid || '') +
|
||||
'&buchungsnummern=' +
|
||||
selected.map(row => row.buchungsnr).join(';'),
|
||||
'_blank'
|
||||
);
|
||||
}
|
||||
},
|
||||
setFilter(type) {
|
||||
if (type == 'open')
|
||||
window.localStorage.setItem(LOCAL_STORAGE_ID_FILTER, this.filter ? 1 : 0);
|
||||
else if (type == 'current_stg')
|
||||
this.$fhcApi.factory
|
||||
.stv.filter.setStg(this.studiengang_kz)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
|
||||
this.$nextTick(this.$refs.table.reloadTable);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.filter = window.localStorage.getItem(LOCAL_STORAGE_ID_FILTER) == 1;
|
||||
this.$fhcApi.factory
|
||||
.stv.filter.getStg()
|
||||
.then(result => this.studiengang_kz = result.data)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
template: `
|
||||
<div class="stv-details-konto h-100 d-flex flex-column">
|
||||
<div class="row justify-content-end">
|
||||
<div class="col-lg-3">
|
||||
<form-input
|
||||
container-class="form-switch"
|
||||
type="checkbox"
|
||||
:label="$p.t('stv/konto_filter_open')"
|
||||
v-model="filter"
|
||||
@update:model-value="setFilter('open')"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<form-input
|
||||
container-class="form-switch"
|
||||
type="checkbox"
|
||||
:label="$p.t('stv/konto_filter_current_stg')"
|
||||
v-model="studiengang_kz_intern"
|
||||
:disabled="!stg_kz"
|
||||
@update:model-value="setFilter('current_stg')"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<core-filter-cmpt
|
||||
ref="table"
|
||||
table-only
|
||||
:side-menu="false"
|
||||
:tabulator-options="tabulatorOptions"
|
||||
reload
|
||||
new-btn-show
|
||||
:new-btn-label="$p.t('konto/buchung')"
|
||||
:new-btn-disabled="stg_kz === ''"
|
||||
@click:new="actionNew"
|
||||
>
|
||||
<template #actions="{selected}">
|
||||
<div class="input-group w-auto">
|
||||
<form-input
|
||||
type="DatePicker"
|
||||
v-model="counterdate"
|
||||
input-group
|
||||
:enable-time-picker="false"
|
||||
auto-apply
|
||||
@cleared="counterdate = new Date()"
|
||||
>
|
||||
</form-input>
|
||||
<button
|
||||
class="btn btn-outline-secondary"
|
||||
@click="actionCounter(selected)"
|
||||
:disabled="!selected.length"
|
||||
>
|
||||
{{ $p.t('stv/konto_counter') }}
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
v-if="config.showZahlungsbestaetigung"
|
||||
class="btn btn-outline-secondary"
|
||||
@click="downloadPdf(selected)"
|
||||
:disabled="!selected.length"
|
||||
>
|
||||
<i class="fa fa-download"></i> {{ $p.t('stv/konto_payment_confirmation') }}
|
||||
</button>
|
||||
</template>
|
||||
</core-filter-cmpt>
|
||||
<konto-new ref="new" :config="config" @saved="updateData" :person-ids="personIds" :stg-kz="stg_kz"></konto-new>
|
||||
<konto-edit ref="edit" :config="config" @saved="updateData"></konto-edit>
|
||||
</div>`
|
||||
};
|
||||
@@ -0,0 +1,164 @@
|
||||
import BsModal from "../../../../Bootstrap/Modal.js";
|
||||
import CoreForm from "../../../../Form/Form.js";
|
||||
import FormValidation from "../../../../Form/Validation.js";
|
||||
import FormInput from "../../../../Form/Input.js";
|
||||
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BsModal,
|
||||
CoreForm,
|
||||
FormValidation,
|
||||
FormInput
|
||||
},
|
||||
inject: {
|
||||
lists: {
|
||||
from: 'lists'
|
||||
}
|
||||
},
|
||||
props: {
|
||||
config: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
data: {}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
save() {
|
||||
this.$refs.form.clearValidation();
|
||||
this.loading = true;
|
||||
|
||||
this.$refs.form
|
||||
.factory.stv.konto.edit(this.data)
|
||||
.then(result => {
|
||||
this.$emit('saved', result.data);
|
||||
this.loading = false;
|
||||
this.$refs.modal.hide();
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui/gespeichert'));
|
||||
})
|
||||
.catch(error => {
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
open(data) {
|
||||
this.data = {...data};
|
||||
this.$refs.modal.show();
|
||||
},
|
||||
preventCloseOnLoading(ev) {
|
||||
if (this.loading)
|
||||
ev.returnValue = false;
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<core-form ref="form" class="stv-details-konto-edit" @submit.prevent="save">
|
||||
<bs-modal ref="modal" @hide-bs-modal="preventCloseOnLoading">
|
||||
<form-validation></form-validation>
|
||||
|
||||
<fieldset :disabled="loading">
|
||||
<form-input
|
||||
v-if="config.showBuchungsnr"
|
||||
v-model="data.buchungsnr"
|
||||
name="buchungsnr"
|
||||
:label="$p.t('konto/buchungsnr')"
|
||||
disabled
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-model="data.betrag"
|
||||
name="betrag"
|
||||
:label="$p.t('konto/betrag')"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
type="DatePicker"
|
||||
v-model="data.buchungsdatum"
|
||||
name="buchungsdatum"
|
||||
:label="$p.t('konto/buchungsdatum')"
|
||||
:enable-time-picker="false"
|
||||
auto-apply
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-model="data.buchungstext"
|
||||
name="buchungstext"
|
||||
:label="$p.t('konto/buchungstext')"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-if="config.showMahnspanne"
|
||||
v-model="data.mahnspanne"
|
||||
name="mahnspanne"
|
||||
:label="$p.t('konto/mahnspanne')"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
type="select"
|
||||
v-model="data.buchungstyp_kurzbz"
|
||||
name="buchungstyp_kurzbz"
|
||||
:label="$p.t('konto/buchungstyp')"
|
||||
>
|
||||
<option v-for="typ in lists.buchungstypen" :key="typ.buchungstyp_kurzbz" :value="typ.buchungstyp_kurzbz" :class="typ.aktiv ? '' : 'text-decoration-line-through text-muted'">
|
||||
{{ typ.beschreibung }}
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
type="select"
|
||||
v-model="data.studiensemester_kurzbz"
|
||||
name="studiensemester_kurzbz"
|
||||
:label="$p.t('lehre/studiensemester')"
|
||||
>
|
||||
<option v-for="sem in lists.studiensemester" :key="sem.studiensemester_kurzbz" :value="sem.studiensemester_kurzbz">
|
||||
{{ sem.studiensemester_kurzbz }}
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
type="select"
|
||||
v-model="data.studiengang_kz"
|
||||
name="studiengang_kz"
|
||||
:label="$p.t('lehre/studiengang')"
|
||||
>
|
||||
<option v-for="stg in lists.stgs" :key="stg.studiengang_kz" :value="stg.studiengang_kz">
|
||||
{{ stg.kuerzel }}
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-if="config.showCreditpoints"
|
||||
v-model="data.credit_points"
|
||||
name="credit_points"
|
||||
:label="$p.t('konto/credit_points')"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-model="data.zahlungsreferenz"
|
||||
name="zahlungsreferenz"
|
||||
:label="$p.t('konto/reference')"
|
||||
disabled
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
type="textarea"
|
||||
v-model="data.anmerkung"
|
||||
name="anmerkung"
|
||||
:label="$p.t('global/anmerkung')"
|
||||
>
|
||||
</form-input>
|
||||
</fieldset>
|
||||
|
||||
<template #title>
|
||||
{{ $p.t('stv/konto_title_edit', data) }}
|
||||
</template>
|
||||
<template #footer>
|
||||
<button type="submit" class="btn btn-primary" :disabled="loading">
|
||||
<i v-if="loading" class="fa fa-spinner fa-spin"></i>
|
||||
{{ $p.t('ui/speichern') }}
|
||||
</button>
|
||||
</template>
|
||||
</bs-modal>
|
||||
</core-form>`
|
||||
};
|
||||
@@ -0,0 +1,203 @@
|
||||
import BsModal from "../../../../Bootstrap/Modal.js";
|
||||
import BsConfirm from "../../../../Bootstrap/Confirm.js";
|
||||
import CoreForm from "../../../../Form/Form.js";
|
||||
import FormValidation from "../../../../Form/Validation.js";
|
||||
import FormInput from "../../../../Form/Input.js";
|
||||
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BsModal,
|
||||
CoreForm,
|
||||
FormValidation,
|
||||
FormInput
|
||||
},
|
||||
inject: {
|
||||
lists: {
|
||||
from: 'lists'
|
||||
},
|
||||
defaultSemester: {
|
||||
from: 'defaultSemester'
|
||||
}
|
||||
},
|
||||
props: {
|
||||
personIds: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
stgKz: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
config: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
data: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
reversedSems() {
|
||||
return this.lists.studiensemester.toReversed();
|
||||
},
|
||||
activeBuchungstypen() {
|
||||
return this.lists.buchungstypen.filter(e => e.aktiv);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
save() {
|
||||
this.$refs.form.clearValidation();
|
||||
this.loading = true;
|
||||
|
||||
const data = {...{
|
||||
person_id: this.personIds,
|
||||
studiengang_kz: this.stgKz
|
||||
}, ...this.data};
|
||||
|
||||
this.$refs.form
|
||||
.factory.stv.konto.checkDoubles(data)
|
||||
.then(result => result.data
|
||||
? Promise.all(
|
||||
result.errors
|
||||
.filter(e => e.type == 'confirm')
|
||||
.map(e => BsConfirm.popup(Vue.h('div', {class:'text-preline'}, e.message)))
|
||||
)
|
||||
: Promise.resolve())
|
||||
.then(() => data)
|
||||
.then(this.$refs.form.factory.stv.konto.insert)
|
||||
.then(result => {
|
||||
this.$emit('saved', result.data);
|
||||
this.loading = false;
|
||||
this.$refs.modal.hide();
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui/gespeichert'));
|
||||
})
|
||||
.catch(error => {
|
||||
if (error)
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
open() {
|
||||
this.data = {
|
||||
buchungstyp_kurzbz: '',
|
||||
betrag: '-0.00',
|
||||
buchungsdatum: new Date(),
|
||||
buchungstext: '',
|
||||
mahnspanne: 30,
|
||||
studiensemester_kurzbz: this.defaultSemester,
|
||||
credit_points: null,
|
||||
anmerkung: ''
|
||||
};
|
||||
this.$refs.modal.show();
|
||||
},
|
||||
preventCloseOnLoading(ev) {
|
||||
if (this.loading)
|
||||
ev.returnValue = false;
|
||||
},
|
||||
checkDefaultBetrag(ev) {
|
||||
const typ = this.lists.buchungstypen.filter(e => e.buchungstyp_kurzbz == ev).pop();
|
||||
const amount = typ.standardbetrag || '-0.00';
|
||||
const text = typ.standardtext || '';
|
||||
const creditpoints = typ.credit_points || '';
|
||||
|
||||
if (!this.data.betrag || this.data.betrag == '-0.00')
|
||||
this.data.betrag = amount;
|
||||
|
||||
if (!this.data.buchungstext)
|
||||
this.data.buchungstext = text;
|
||||
|
||||
if (this.config.showCreditpoints && (this.data.credit_points == '0.00' || this.data.credit_points === null))
|
||||
this.data.credit_points = creditpoints;
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<core-form ref="form" class="stv-details-konto-edit" @submit.prevent="save">
|
||||
<bs-modal ref="modal" @hide-bs-modal="preventCloseOnLoading">
|
||||
<form-validation></form-validation>
|
||||
|
||||
<fieldset :disabled="loading">
|
||||
<form-input
|
||||
type="select"
|
||||
v-model="data.buchungstyp_kurzbz"
|
||||
name="buchungstyp_kurzbz"
|
||||
:label="$p.t('konto/buchungstyp')"
|
||||
@update:model-value="checkDefaultBetrag"
|
||||
>
|
||||
<option v-for="typ in activeBuchungstypen" :key="typ.buchungstyp_kurzbz" :value="typ.buchungstyp_kurzbz" :class="typ.aktiv ? '' : 'text-decoration-line-through text-muted'">
|
||||
{{ typ.beschreibung }}
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-model="data.betrag"
|
||||
name="betrag"
|
||||
:label="$p.t('konto/betrag')"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
type="DatePicker"
|
||||
v-model="data.buchungsdatum"
|
||||
name="buchungsdatum"
|
||||
:label="$p.t('konto/buchungsdatum')"
|
||||
:enable-time-picker="false"
|
||||
auto-apply
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-model="data.buchungstext"
|
||||
name="buchungstext"
|
||||
:label="$p.t('konto/buchungstext')"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-if="config.showMahnspanne"
|
||||
v-model="data.mahnspanne"
|
||||
name="mahnspanne"
|
||||
:label="$p.t('konto/mahnspanne')"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
type="select"
|
||||
v-model="data.studiensemester_kurzbz"
|
||||
name="studiensemester_kurzbz"
|
||||
:label="$p.t('lehre/studiensemester')"
|
||||
>
|
||||
<option v-for="sem in reversedSems" :key="sem.studiensemester_kurzbz" :value="sem.studiensemester_kurzbz">
|
||||
{{ sem.studiensemester_kurzbz }}
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-if="config.showCreditpoints"
|
||||
v-model="data.credit_points"
|
||||
name="credit_points"
|
||||
:label="$p.t('konto/credit_points')"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
type="textarea"
|
||||
v-model="data.anmerkung"
|
||||
name="anmerkung"
|
||||
:label="$p.t('global/anmerkung')"
|
||||
>
|
||||
</form-input>
|
||||
</fieldset>
|
||||
|
||||
<template #title>
|
||||
{{ $p.t(
|
||||
'stv',
|
||||
personIds.length > 1 ? 'konto_title_new_multi' : 'konto_title_new',
|
||||
{ x: personIds.length }
|
||||
) }}
|
||||
</template>
|
||||
<template #footer>
|
||||
<button type="submit" class="btn btn-primary" :disabled="loading">
|
||||
<i v-if="loading" class="fa fa-spinner fa-spin"></i>
|
||||
{{ $p.t('ui/speichern') }}
|
||||
</button>
|
||||
</template>
|
||||
</bs-modal>
|
||||
</core-form>`
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
import TblMultiStatus from "./Prestudent/MultiStatus.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TblMultiStatus
|
||||
},
|
||||
props: {
|
||||
modelValue: Object,
|
||||
},
|
||||
template: `
|
||||
<div class="stv-details-multistatus h-100">
|
||||
<tbl-multi-status :model-value="modelValue"></tbl-multi-status>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import NotenZeugnis from './Noten/Zeugnis.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NotenZeugnis
|
||||
},
|
||||
props: {
|
||||
modelValue: Object
|
||||
},
|
||||
methods: {
|
||||
reload() {
|
||||
this.$refs.zeugnis.$refs.table.reloadTable();
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="stv-details-noten h-100 d-flex flex-column">
|
||||
<noten-zeugnis ref="zeugnis" :student="modelValue"></noten-zeugnis>
|
||||
</div>`
|
||||
};
|
||||
@@ -0,0 +1,111 @@
|
||||
import {CoreFilterCmpt} from "../../../../filter/Filter.js";
|
||||
import {CoreRESTClient} from '../../../../../RESTClient.js';
|
||||
import ZeugnisActions from './Zeugnis/Actions.js';
|
||||
|
||||
const LOCAL_STORAGE_ID = 'stv_details_noten_zeugnis_2024-01-11_stdsem_all';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFilterCmpt,
|
||||
ZeugnisActions
|
||||
},
|
||||
props: {
|
||||
student: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
validStudent: true,
|
||||
tabulatorEvents: [],
|
||||
stdsem: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
ajaxURL() {
|
||||
return CoreRESTClient._generateRouterURI('components/stv/Noten/getZeugnis/' + this.student.prestudent_id + this.stdsem);
|
||||
},
|
||||
tabulatorOptions() {
|
||||
return {
|
||||
ajaxURL: this.ajaxURL,
|
||||
ajaxResponse: (url, params, response) => {
|
||||
if (!response.retval)
|
||||
this.validStudent = false;
|
||||
else
|
||||
this.validStudent = true;
|
||||
return response.retval || [];
|
||||
},
|
||||
columns: [
|
||||
{ field: 'zeugnis', title: 'Zeugnis', formatter: 'tickCross' },
|
||||
{ field: 'lehrveranstaltung_bezeichnung', title: 'Lehrveranstaltung' },
|
||||
{ field: 'note_bezeichnung', title: 'Note' },
|
||||
{ field: 'uebernahmedatum', title: 'Übernahmedatum', visible: false },
|
||||
{ field: 'benotungsdatum', title: 'Benotungsdatum', visible: false },
|
||||
{ field: 'benotungsdatum-iso', title: 'Benotungsdatum ISO', visible: false },
|
||||
{ field: 'studiensemester_kurzbz', title: 'Studiensemester', visible: false },
|
||||
{ field: 'note', title: 'Note Numerisch', visible: false },
|
||||
{ field: 'lehrveranstaltung_id', title: 'Lehrveranstaltung ID', visible: false },
|
||||
{ field: 'studiengang', title: 'Studiengang', visible: false },
|
||||
{ field: 'studiengang_kz', title: 'Studiengang Kennzahl', visible: false },
|
||||
{ field: 'studiengang_lv', title: 'StudiengangLV', visible: false },
|
||||
{ field: 'studiengang_kz_lv', title: 'Studiengang_kzLV', visible: false },
|
||||
{ field: 'semester_lv', title: 'SemesterLV', visible: false },
|
||||
{ field: 'ects_lv', title: 'ECTS', visible: false },
|
||||
{ field: 'lehrform', title: 'Lehrform', visible: false },
|
||||
{ field: 'kurzbz', title: 'Kurzbz', visible: false },
|
||||
{ field: 'punkte', title: 'Punkte', visible: false },
|
||||
{ field: 'lehrveranstaltung_bezeichnung_english', title: 'Englisch', visible: false }
|
||||
],
|
||||
layout: 'fitDataStretch',
|
||||
height: '100%',
|
||||
selectable: true,
|
||||
selectableRangeMode: 'click',
|
||||
persistenceID: 'stv-details-noten-zeugnis'
|
||||
};
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
ajaxURL(n) {
|
||||
if (this.$refs.table)
|
||||
this.$refs.table.tabulator.setData(n);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setGrades(selected) {
|
||||
CoreRESTClient
|
||||
.post('components/stv/Noten/update', selected)
|
||||
.then(this.$refs.table.reloadTable)
|
||||
.catch(this.$fhcAlert.handleFormValidation);
|
||||
},
|
||||
saveStdsem(event) {
|
||||
window.localStorage.setItem(LOCAL_STORAGE_ID, event.target.value ? 'true' : '');
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const savedPath = window.localStorage.getItem(LOCAL_STORAGE_ID);
|
||||
this.stdsem = savedPath ? '/all' : '';
|
||||
},
|
||||
// TODO(chris): phrasen
|
||||
template: `
|
||||
<div class="stv-details-noten-zeugnis h-100 d-flex flex-column">
|
||||
<div v-if="!validStudent">Kein Student</div>
|
||||
<template v-else>
|
||||
<div class="mb-3">
|
||||
<select class="form-select" v-model="stdsem" @input="saveStdsem">
|
||||
<option value="">Aktuelles Semester</option>
|
||||
<option value="/all">Alle Semester</option>
|
||||
</select>
|
||||
</div>
|
||||
<core-filter-cmpt
|
||||
ref="table"
|
||||
:tabulator-options="tabulatorOptions"
|
||||
:tabulator-events="tabulatorEvents"
|
||||
table-only
|
||||
:side-menu="false"
|
||||
reload
|
||||
>
|
||||
<template #actions="{selected}">
|
||||
<zeugnis-actions :selected="selected" @set-grades="setGrades"></zeugnis-actions>
|
||||
</template>
|
||||
</core-filter-cmpt>
|
||||
</template>
|
||||
</div>`
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
import {CoreRESTClient} from '../../../../../../RESTClient.js';
|
||||
|
||||
export default {
|
||||
emits: [
|
||||
'setGrades'
|
||||
],
|
||||
props: {
|
||||
selected: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
grades: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
current: {
|
||||
get() {
|
||||
if (!this.selected.length)
|
||||
return '';
|
||||
if (this.selected.length == 1)
|
||||
return this.selected[0].note;
|
||||
const grades = Object.keys(this.selected.reduce((a,c) => {
|
||||
a[c.note] = true;
|
||||
return a;
|
||||
}, {}));
|
||||
if (grades.length == 1)
|
||||
return grades[0];
|
||||
return '';
|
||||
},
|
||||
set(note) {
|
||||
this.$emit('setGrades', this.selected.map(zeugnis => {
|
||||
const { lehrveranstaltung_id, uid: student_uid, studiensemester_kurzbz } = zeugnis;
|
||||
return { lehrveranstaltung_id, student_uid, studiensemester_kurzbz, note };
|
||||
}));
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
CoreRESTClient
|
||||
.get('components/stv/Noten/get')
|
||||
.then(result => result.data)
|
||||
.then(result => {
|
||||
this.grades = result.retval;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
template: `
|
||||
<div class="stv-details-noten-zeugnis-actions">
|
||||
<select class="form-select" v-model="current" :disabled="!selected.length">
|
||||
<option value="" disabled>Note setzen</option>
|
||||
<option v-for="grade in grades" :key="grade.note" :value="grade.note">{{ grade.bezeichnung }}</option>
|
||||
</select>
|
||||
</div>`
|
||||
};
|
||||
@@ -0,0 +1,109 @@
|
||||
import CoreNotiz from "../../../Notiz/Notiz.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreNotiz
|
||||
},
|
||||
props: {
|
||||
modelValue: Object
|
||||
},
|
||||
template: `
|
||||
<div class="stv-details-notizen h-100 pb-3 overflow-hidden">
|
||||
<!-- mit factory als endpoint -->
|
||||
<core-notiz
|
||||
:endpoint="$fhcApi.factory.notiz.person"
|
||||
ref="formc"
|
||||
notiz-layout="twoColumnsFormLeft"
|
||||
type-id="person_id"
|
||||
:id="modelValue.person_id"
|
||||
show-document
|
||||
show-tiny-mce
|
||||
:visible-columns="['titel','text','verfasser','bearbeiter','dokumente']"
|
||||
>
|
||||
</core-notiz>
|
||||
|
||||
|
||||
<!--
|
||||
---------------------------------------------------------------------------------------------
|
||||
-------------------- DESCRIPTION FOR PARAMETER PROPS ----------------------------------------
|
||||
---------------------------------------------------------------------------------------------
|
||||
|
||||
endpoint: for corecontroller: eg: :endpoint="$fhcApi.factory.notiz.person"
|
||||
(...prestudent, ...mitarbeiter, ...bestellung, ...lehreinheit, ...projekt, ...projektphase, ...projekttask, ...anrechnung)
|
||||
|
||||
for extensions: write own controller extending core NotizController
|
||||
|
||||
ref="formc"
|
||||
|
||||
type-id: id to which table the notizdata should be connected... eg. person_id, prestudent_id, uid (for mitarbeiter_uid), projekt_kurzbz, projektphase_id, projekttask_id,
|
||||
bestellung_id, lehreinheit_id, anrechnung_id
|
||||
|
||||
notizLayout: "classicFas", "twoColumnsFormLeft", twoColumnsFormRight, popupModal"
|
||||
|
||||
showErweitert: if true: section with following fields will be displayed:
|
||||
'verfasser', 'bearbeiter', 'von', 'bis'
|
||||
|
||||
showDocument: if true: section with documentHandling will be displayed
|
||||
|
||||
showTinyMCE: if true: section with WYSIWYG Editor for Text will be displayed
|
||||
|
||||
visibleColumns: list, which fields shoult be showed as default in filter component
|
||||
fullVersion: :visibleColumns="['titel','text','bearbeiter','verfasser','von','bis','dokumente','erledigt','notiz_id','notizzuordnung_id','id','lastupdate']"
|
||||
|
||||
|
||||
---------------------------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------------------------
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
---------------------------------------------------------------------------------------------
|
||||
------------------------ SOME TESTDATA -----------------------------------------------------
|
||||
---------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
<core-notiz
|
||||
:endpoint="$fhcApi.factory.notiz.mitarbeiter"
|
||||
ref="formc"
|
||||
type-id="uid"
|
||||
:id= "'ma0068'"
|
||||
notiz-layout="twoColumnsFormLeft"
|
||||
show-document
|
||||
show-tiny-mce
|
||||
show-erweitert
|
||||
:visible-columns="['titel','text','verfasser','bearbeiter','dokumente']"
|
||||
>
|
||||
</core-notiz>
|
||||
|
||||
<core-notiz
|
||||
:endpoint="$fhcApi.factory.notiz.prestudent"
|
||||
ref="formc"
|
||||
type-id="prestudent_id"
|
||||
:id="modelValue.prestudent_id"
|
||||
notiz-layout="twoColumnsFormLeft"
|
||||
:show-erweitert="true"
|
||||
:show-document="true"
|
||||
:showTinyMCE="true"
|
||||
:visible-columns="['titel','text','verfasser','bearbeiter','dokumente']"
|
||||
>
|
||||
</core-notiz>
|
||||
|
||||
<core-notiz
|
||||
:endpoint="$fhcApi.factory.notiz.projekt"
|
||||
ref="formc"
|
||||
type-id="projekt_kurzbz"
|
||||
:id="'EA74'"
|
||||
notiz-layout="twoColumnsFormLeft"
|
||||
:show-erweitert="true"
|
||||
:show-document="true"
|
||||
:showTinyMCE="true"
|
||||
:visible-columns="['titel','text','verfasser','bearbeiter','dokumente']"
|
||||
>
|
||||
</core-notiz>-->
|
||||
|
||||
</div>
|
||||
`
|
||||
};
|
||||
@@ -0,0 +1,523 @@
|
||||
import FormForm from '../../../Form/Form.js';
|
||||
import FormInput from '../../../Form/Input.js';
|
||||
import TblHistory from "./Prestudent/History.js";
|
||||
|
||||
import CoreUdf from '../../../Udf/Udf.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FormForm,
|
||||
FormInput,
|
||||
TblHistory,
|
||||
CoreUdf
|
||||
},
|
||||
inject: {
|
||||
lists: {
|
||||
from: 'lists'
|
||||
},
|
||||
showZgvErfuellt: {
|
||||
from: 'configShowZgvErfuellt',
|
||||
default: false
|
||||
},
|
||||
showZgvDoktor: {
|
||||
from: 'configShowZgvDoktor',
|
||||
default: false
|
||||
},
|
||||
hasPrestudentPermission: {
|
||||
from: 'hasPrestudentPermission',
|
||||
default: false
|
||||
},
|
||||
hasAssistenzPermission: {
|
||||
from: 'hasAssistenzPermission',
|
||||
default: false
|
||||
},
|
||||
hasAdminPermission: {
|
||||
from: 'hasAdminPermission',
|
||||
default: false
|
||||
},
|
||||
defaultSemester: {
|
||||
from: 'defaultSemester',
|
||||
}
|
||||
},
|
||||
props: {
|
||||
modelValue: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: [],
|
||||
listZgvs: [],
|
||||
listZgvsmaster: [],
|
||||
listZgvsdoktor: [],
|
||||
listStgs: [],
|
||||
listAusbildung: [],
|
||||
listAufmerksamdurch: [],
|
||||
listBerufe: [],
|
||||
listFoerderrelevant: [
|
||||
{ text: 'wie Studiengang', value: null },
|
||||
{ text: 'Ja', value: true },
|
||||
{ text: 'Nein', value: false }
|
||||
],
|
||||
listStgTyp: [],
|
||||
initialFormData: {},
|
||||
deltaArray: {},
|
||||
actionUpdate: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
deltaLength() {
|
||||
return Object.keys(this.deltaArray).length;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
data: {
|
||||
// TODO(chris): use @input instead?
|
||||
handler(n) {
|
||||
|
||||
const delta = {};
|
||||
for (const key in this.data) {
|
||||
if (this.data[key] !== this.initialFormData[key]) {
|
||||
delta[key] = this.data[key];
|
||||
this.actionUpdate = true;
|
||||
}
|
||||
}
|
||||
this.deltaArray = delta;
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
modelValue(n){
|
||||
this.loadPrestudent(n);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
loadPrestudent() {
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/prestudent/get/' + this.modelValue.prestudent_id)
|
||||
.then(result => result.data)
|
||||
.then(result => {
|
||||
this.data = result;
|
||||
//neue DataVariable um ein Delta der vorgenommenen Änderungen berechnen zu können
|
||||
this.initialFormData = {...this.data};
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
udfsLoaded(udfs) {
|
||||
this.initialFormData = {...(this.initialFormData || {}), ...udfs};
|
||||
},
|
||||
updatePrestudent(){
|
||||
this.$refs.form
|
||||
.post('api/frontend/v1/stv/prestudent/updatePrestudent/' + this.modelValue.prestudent_id, this.deltaArray)
|
||||
.then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||
this.deltaArray = [];
|
||||
this.actionUpdate = false;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError)
|
||||
.finally(() => {
|
||||
window.scrollTo(0, 0);
|
||||
});
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.loadPrestudent();
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/prestudent/getBezeichnungZGV')
|
||||
.then(result => result.data)
|
||||
.then(result => {
|
||||
this.listZgvs = result;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/prestudent/getBezeichnungMZgv')
|
||||
.then(result => result.data)
|
||||
.then(result => {
|
||||
this.listZgvsmaster = result;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/prestudent/getBezeichnungDZgv')
|
||||
.then(result => result.data)
|
||||
.then(result => {
|
||||
this.listZgvsdoktor = result;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/lists/getStgs')
|
||||
.then(result => result.data)
|
||||
.then(result => {
|
||||
this.listStgs = result;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/prestudent/getAusbildung')
|
||||
.then(result => result.data)
|
||||
.then(result => {
|
||||
this.listAusbildung = result;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/prestudent/getAufmerksamdurch')
|
||||
.then(result => result.data)
|
||||
.then(result => {
|
||||
this.listAufmerksamdurch = result;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/prestudent/getBerufstaetigkeit')
|
||||
.then(result => result.data)
|
||||
.then(result => {
|
||||
this.listBerufe = result;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/prestudent/getTypenStg')
|
||||
.then(result => result.data)
|
||||
.then(result => {
|
||||
this.listStgTyp = result;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
template: `
|
||||
<div class="stv-details-prestudent h-100 pb-3">
|
||||
<form-form ref="form" class="stv-details-prestudent" @submit.prevent="updatePrestudent">
|
||||
<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">
|
||||
<legend>{{$p.t('lehre', 'title_zgv')}} {{modelValue.nachname}} {{modelValue.vorname}}</legend>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-3"
|
||||
label="Prestudent_id"
|
||||
type="text"
|
||||
v-model="data.prestudent_id"
|
||||
name="prestudent_id"
|
||||
readonly
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-3"
|
||||
label="Person_id"
|
||||
type="text"
|
||||
v-model="data.person_id"
|
||||
name="person_id"
|
||||
readonly
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-3"
|
||||
label="ZGV"
|
||||
type="select"
|
||||
v-model="data.zgv_code"
|
||||
name="zgvcode"
|
||||
>
|
||||
<option v-for="zgv in listZgvs" :key="zgv.zgv_code" :value="zgv.zgv_code">{{zgv.zgv_bez}}</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-3"
|
||||
:label="$p.t('infocenter', 'zgvOrt')"
|
||||
type="text"
|
||||
v-model="data.zgvort"
|
||||
name="zgvort"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-3"
|
||||
:label="$p.t('infocenter', 'zgvDatum')"
|
||||
type="DatePicker"
|
||||
v-model="data.zgvdatum"
|
||||
name="zgvdatum"
|
||||
no-today
|
||||
auto-apply
|
||||
:enable-time-picker="false"
|
||||
format="dd.MM.yyyy"
|
||||
preview-format="dd.MM.yyyy"
|
||||
:teleport="true"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-3"
|
||||
:label="$p.t('infocenter', 'zgvNation')"
|
||||
type="select"
|
||||
v-model="data.zgvnation"
|
||||
name="zgvnation"
|
||||
>
|
||||
<!-- TODO(chris): gesperrte nationen können nicht ausgewählt werden! Um das zu realisieren müsste man ein pseudo select machen -->
|
||||
<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
|
||||
container-class="col-3"
|
||||
:label="$p.t('lehre', 'zgvMaster')"
|
||||
type="select"
|
||||
v-model="data.zgvmas_code"
|
||||
name="zgvmascode"
|
||||
>
|
||||
<option v-for="mzgv in listZgvsmaster" :key="mzgv.zgvmas_code" :value="mzgv.zgvmas_code">{{mzgv.zgvmas_bez}}</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-3"
|
||||
:label="$p.t('lehre', 'zgvMasterOrt')"
|
||||
type="text"
|
||||
v-model="data.zgvmaort"
|
||||
name="zgvmaort"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-3"
|
||||
:label="$p.t('lehre', 'zgvMasterDatum')"
|
||||
type="DatePicker"
|
||||
v-model="data.zgvmadatum"
|
||||
name="zgvmadatum"
|
||||
no-today
|
||||
auto-apply
|
||||
:enable-time-picker="false"
|
||||
format="dd.MM.yyyy"
|
||||
preview-format="dd.MM.yyyy"
|
||||
:teleport="true"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-3"
|
||||
:label="$p.t('lehre', 'zgvMasterNation')"
|
||||
type="select"
|
||||
v-model="data.zgvmanation"
|
||||
name="zgvmanation"
|
||||
>
|
||||
<!-- TODO(chris): gesperrte nationen können nicht ausgewählt werden! Um das zu realisieren müsste man ein pseudo select machen -->
|
||||
<option v-for="nation in lists.nations" :key="nation.nation_code" :value="nation.nation_code" :disabled="nation.sperre">{{nation.kurztext}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<!--ZGV Doktor-->
|
||||
<div v-if="showZgvDoktor" class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-3"
|
||||
:label="$p.t('lehre', 'zgvDoktor')"
|
||||
type="select"
|
||||
v-model="data.zgvdoktor_code"
|
||||
name="zgvdoktor_code"
|
||||
>
|
||||
<option v-for="zgv in listZgvsdoktor" :key="zgv.zgvdoktor_code" :value="zgv.zgvdoktor_code">{{zgv.zgvdoktor_bez}}</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-3"
|
||||
:label="$p.t('lehre', 'zgvDoktorOrt')"
|
||||
type="text"
|
||||
v-model="data.zgvdoktorort"
|
||||
name="zgvdoktorort"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-3"
|
||||
:label="$p.t('lehre', 'zgvDoktorDatum')"
|
||||
type="DatePicker"
|
||||
v-model="data.zgvdoktordatum"
|
||||
name="zgvdoktordatum"
|
||||
no-today
|
||||
auto-apply
|
||||
:enable-time-picker="false"
|
||||
format="dd.MM.yyyy"
|
||||
preview-format="dd.MM.yyyy"
|
||||
:teleport="true"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-3"
|
||||
:label="$p.t('lehre', 'zgvDoktorNation')"
|
||||
type="select"
|
||||
v-model="data.zgvdoktornation"
|
||||
name="zgvdoktornation"
|
||||
>
|
||||
<!-- TODO(chris): gesperrte nationen können nicht ausgewählt werden! Um das zu realisieren müsste man ein pseudo select machen -->
|
||||
<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 v-if="showZgvErfuellt" class="row mb-3">
|
||||
<div class="col-3 pt-4 d-flex align-items-center">
|
||||
<form-input
|
||||
container-class="form-check"
|
||||
:label="$p.t('infocenter', 'zgvErfuellt')"
|
||||
type="checkbox"
|
||||
v-model="data.zgv_erfuellt"
|
||||
name="zgv_erfuellt"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-3 pt-4 d-flex align-items-center">
|
||||
<form-input
|
||||
container-class="form-check"
|
||||
:label="$p.t('infocenter', 'zgvMasterErfuellt')"
|
||||
type="checkbox"
|
||||
v-model="data.zgvmas_erfuellt"
|
||||
name="zgvmas_erfuellt"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div v-if="showZgvDoktor" class="col-3 pt-4 d-flex align-items-center">
|
||||
<form-input
|
||||
container-class="form-check"
|
||||
:label="$p.t('infocenter', 'zgvDoktorErfuellt')"
|
||||
type="checkbox"
|
||||
v-model="data.zgvdoktor_erfuellt"
|
||||
name="zgvdoktor_erfuellt"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="overflow-hidden">
|
||||
<legend>PrestudentIn</legend>
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
:label="$p.t('person', 'aufmerksamDurch')"
|
||||
type="select"
|
||||
v-model="data.aufmerksamdurch_kurzbz"
|
||||
name="aufmerksamDurch"
|
||||
>
|
||||
<option v-for="adurch in listAufmerksamdurch" :key="adurch.aufmerksamdurch_kurzbz" :value="adurch.aufmerksamdurch_kurzbz">{{adurch.beschreibung}}</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
:label="$p.t('person', 'berufstaetigkeit')"
|
||||
type="select"
|
||||
v-model="data.berufstaetigkeit_code"
|
||||
name="berufstaetigkeit_code"
|
||||
>
|
||||
<option v-for="beruf in listBerufe" :key="beruf.berufstaetigkeit_code" :value="beruf.berufstaetigkeit_code">{{beruf.berufstaetigkeit_bez}} </option>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
:label="$p.t('person', 'ausbildung')"
|
||||
type="select"
|
||||
v-model="data.ausbildungcode"
|
||||
name="ausbildungcode"
|
||||
>
|
||||
<option v-for="ausbld in listAusbildung" :key="ausbld.ausbildungcode" :value="ausbld.ausbildungcode">{{ausbld.ausbildungbez}} </option>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
` + /* TODO(chris): Ausgeblendet für Testing
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Aufnahmeschlüssel"
|
||||
type="text"
|
||||
v-model="data.aufnahmeschluessel"
|
||||
name="aufnahmeschluessel"
|
||||
disabled
|
||||
>
|
||||
</form-input>
|
||||
|
||||
<div class="col-4 pt-4 d-flex align-items-center">
|
||||
<form-input
|
||||
container-class="form-check"
|
||||
:label="$p.t('person', 'facheinschlaegigBerufstaetig')"
|
||||
type="checkbox"
|
||||
v-model="data.facheinschlberuf"
|
||||
name="facheinschlberuf"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<!--Todo(manu) validierung Integer, liste hier null-->
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
:label="$p.t('person', 'bisstandort')"
|
||||
type="text"
|
||||
v-model="data.standort_code"
|
||||
name="standort_code"
|
||||
disabled
|
||||
>
|
||||
</form-input>
|
||||
|
||||
</div>
|
||||
*/`
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
:label="$p.t('lehre', 'studiengang')"
|
||||
type="select"
|
||||
v-model="data.studiengang_kz"
|
||||
name="studiengang_kz"
|
||||
disabled
|
||||
>
|
||||
<option v-for="stg in listStgs" :key="stg.studiengang_kz" :value="stg.studiengang_kz">{{stg.kuerzel}} - {{stg.bezeichnung}} </option>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
:label="$p.t('lehre', 'studientyp')"
|
||||
type="select"
|
||||
v-model="data.gsstudientyp_kurzbz"
|
||||
name="gsstudientyp_kurzbz"
|
||||
>
|
||||
<option v-for="typ in listStgTyp" :key="typ.typ" :value="typ.typ">{{typ.bezeichnung}} </option>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
: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
|
||||
container-class="form-check"
|
||||
:label="$p.t('person', 'bismelden')"
|
||||
type="checkbox"
|
||||
v-model="data.bismelden"
|
||||
name="bismelden"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-2 pt-4 d-flex align-items-center">
|
||||
<form-input
|
||||
container-class="form-check"
|
||||
:label="$p.t('lehre', 'dual')"
|
||||
type="checkbox"
|
||||
v-model="data.dual"
|
||||
name="dual"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
` + /* TODO(chris): Ausgeblendet für Testing
|
||||
<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
|
||||
container-class="col-1"
|
||||
:label="$p.t('lehre', 'prioritaet')"
|
||||
type="text"
|
||||
v-model="data.priorisierung"
|
||||
name="priorisierung"
|
||||
:disabled="!hasPrestudentPermission"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<core-udf @load="udfsLoaded" v-model="data" class="row-cols-3 g-3 mb-3" ci-model="crm/prestudent" :pk="{prestudent_id:modelValue.prestudent_id}"></core-udf>
|
||||
</fieldset>
|
||||
</form-form>
|
||||
<div>
|
||||
<legend>Gesamthistorie</legend>
|
||||
<tbl-history :person-id="modelValue.person_id" :prestudent-id="modelValue.prestudent_id"></tbl-history>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
import {CoreFilterCmpt} from "../../../../filter/Filter.js";
|
||||
|
||||
export default{
|
||||
components: {
|
||||
CoreFilterCmpt
|
||||
},
|
||||
props: {
|
||||
personId: Number,
|
||||
prestudentId: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabulatorOptions: {
|
||||
ajaxURL: 'api/frontend/v1/stv/Prestudent/getHistoryPrestudents/' + this.personId,
|
||||
ajaxRequestFunc: this.$fhcApi.get,
|
||||
ajaxResponse: (url, params, response) => response.data,
|
||||
//autoColumns: true,
|
||||
columns:[
|
||||
{title:"StSem", field:"studiensemester_kurzbz"},
|
||||
{title:"Prio", field:"priorisierung"},
|
||||
{title:"Stg", field:"kurzbzlang"},
|
||||
{title:"Orgform", field:"orgform_kurzbz"},
|
||||
{title:"Studienplan", field:"bezeichnung"},
|
||||
{title:"UID", field:"student_uid"},
|
||||
{title:"Status", field:"status"},
|
||||
{title:"PrestudentId", field:"prestudent_id", visible:false}
|
||||
],
|
||||
rowFormatter: row => {
|
||||
const rowData = row.getData();
|
||||
const element = row.getElement();
|
||||
if (["Abgewiesener","Abbrecher","Absolvent"].includes(rowData.status_kurzbz)) {
|
||||
element.classList.add('disabled');
|
||||
}
|
||||
if (rowData.prestudent_id == this.prestudentId) {
|
||||
element.classList.add('fw-bold');
|
||||
}
|
||||
},
|
||||
layout: 'fitDataFill',
|
||||
layoutColumnsOnNewData: false,
|
||||
height: 'auto',
|
||||
selectable: false,
|
||||
persistenceID: 'stv-details-prestudent-history'
|
||||
},
|
||||
tabulatorEvents: [
|
||||
{
|
||||
event: 'tableBuilt',
|
||||
handler: async () => {
|
||||
await this.$p.loadCategory(['lehre']);
|
||||
|
||||
let cm = this.$refs.table.tabulator.columnManager;
|
||||
|
||||
cm.getColumnByField('orgform_kurzbz').component.updateDefinition({
|
||||
title: this.$p.t('lehre', 'organisationsform')
|
||||
});
|
||||
|
||||
cm.getColumnByField('bezeichnung').component.updateDefinition({
|
||||
title: this.$p.t('lehre', 'studienplan')
|
||||
});
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
personId() {
|
||||
this.$refs.table.tabulator.setData('api/frontend/v1/stv/Prestudent/getHistoryPrestudents/' + this.personId);
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="stv-details-prestudent-history h-100 pt-3">
|
||||
<core-filter-cmpt
|
||||
ref="table"
|
||||
:tabulator-options="tabulatorOptions"
|
||||
:tabulator-events="tabulatorEvents"
|
||||
table-only
|
||||
:side-menu="false"
|
||||
>
|
||||
</core-filter-cmpt>
|
||||
</div>`
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
import {CoreFilterCmpt} from "../../../../filter/Filter.js";
|
||||
import BsModal from "../../../../Bootstrap/Modal.js";
|
||||
import BsConfirm from "../../../../Bootstrap/Confirm.js";
|
||||
import FormInput from '../../../../Form/Input.js';
|
||||
import StatusModal from '../Status/Modal.js';
|
||||
import StatusDropdown from '../Status/Dropdown.js';
|
||||
|
||||
export default{
|
||||
components: {
|
||||
CoreFilterCmpt,
|
||||
BsModal,
|
||||
FormInput,
|
||||
StatusModal,
|
||||
StatusDropdown
|
||||
},
|
||||
inject: {
|
||||
defaultSemester: {
|
||||
from: 'defaultSemester',
|
||||
},
|
||||
hasPermissionToSkipStatusCheck: {
|
||||
from: 'hasPermissionToSkipStatusCheck',
|
||||
default: false
|
||||
},
|
||||
$reloadList: {
|
||||
from: '$reloadList',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
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
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabulatorOptions: {
|
||||
ajaxURL: 'api/frontend/v1/stv/Status/getHistoryPrestudent/' + this.modelValue.prestudent_id,
|
||||
ajaxRequestFunc: this.$fhcApi.get,
|
||||
ajaxResponse: (url, params, response) => response.data,
|
||||
columns: [
|
||||
{title: "Kurzbz", field: "status_kurzbz", tooltip: true},
|
||||
{title: "StSem", field: "studiensemester_kurzbz"},
|
||||
{title: "Sem", field: "ausbildungssemester"},
|
||||
{title: "Lehrverband", field: "lehrverband", width: 72},
|
||||
{title: "Datum", field: "format_datum"},
|
||||
{title: "Studienplan", field: "bezeichnung"},
|
||||
{title: "BestätigtAm", field: "format_bestaetigtam"},
|
||||
{title: "AbgeschicktAm", field: "format_bewerbung_abgeschicktamum", visible:false},
|
||||
{title: "Statusgrund", field: "statusgrund_bezeichnung"},
|
||||
{title: "Organisationsform", field: "orgform_kurzbz", visible: false},
|
||||
{title: "PrestudentInId", field: "prestudent_id", visible: false},
|
||||
{title: "StudienplanId", field: "studienplan_id", visible: false},
|
||||
{title: "Anmerkung", field: "anmerkung", visible: false},
|
||||
{title: "BestätigtVon", field: "bestaetigtvon", visible: false},
|
||||
{title: "InsertAmUm", field: "format_insertamum", visible: false},
|
||||
{title: "InsertVon", field: "insertvon", visible: false},
|
||||
{title: "UpdateAmUm", field: "format_updateamum", visible: false},
|
||||
{title: "UpdateVon", field: "updatevon", visible: false},
|
||||
{
|
||||
title: 'Aktionen', field: 'actions',
|
||||
minWidth: 150, // Ensures Action-buttons will be always fully displayed
|
||||
formatter: (cell, formatterParams, onRendered) => {
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.className = "d-flex gap-2";
|
||||
|
||||
const data = cell.getData();
|
||||
|
||||
let button = document.createElement('button');
|
||||
button.className = 'btn btn-outline-secondary btn-action';
|
||||
button.innerHTML = '<i class="fa fa-forward"></i>';
|
||||
button.title = 'Status vorrücken';
|
||||
button.addEventListener('click', () =>
|
||||
this.actionAdvanceStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester)
|
||||
);
|
||||
if (!['Student', 'Diplomand', 'Unterbrecher'].includes(data.status_kurzbz))
|
||||
button.disabled = true;
|
||||
container.append(button);
|
||||
|
||||
button = document.createElement('button');
|
||||
button.className = 'btn btn-outline-secondary btn-action';
|
||||
button.innerHTML = '<i class="fa fa-check"></i>';
|
||||
button.title = 'Status bestätigen';
|
||||
button.addEventListener('click', () =>
|
||||
this.actionConfirmStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester)
|
||||
);
|
||||
if (data.bestaetigtam || !data.bewerbung_abgeschicktamum)
|
||||
button.disabled = true;
|
||||
container.append(button);
|
||||
|
||||
button = document.createElement('button');
|
||||
button.className = 'btn btn-outline-secondary btn-action';
|
||||
button.innerHTML = '<i class="fa fa-edit"></i>';
|
||||
button.title = 'Status bearbeiten';
|
||||
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;
|
||||
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 = 'Status löschen';
|
||||
button.addEventListener('click', () =>
|
||||
this.actionDeleteStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester)
|
||||
);
|
||||
if (this.dataMeldestichtag && this.dataMeldestichtag > data.datum && !this.hasPermissionToSkipStatusCheck)
|
||||
button.disabled = true;
|
||||
container.append(button);
|
||||
|
||||
return container;
|
||||
},
|
||||
frozen: true
|
||||
},
|
||||
],
|
||||
rowFormatter: (row) => {
|
||||
const rowData = row.getData();
|
||||
if (this.dataMeldestichtag && this.dataMeldestichtag > rowData.datum)
|
||||
{
|
||||
row.getElement().classList.add('disabled');
|
||||
}
|
||||
},
|
||||
layout: 'fitDataFill',
|
||||
layoutColumnsOnNewData: false,
|
||||
height: 'auto',
|
||||
selectable: false,
|
||||
},
|
||||
tabulatorEvents: [
|
||||
{
|
||||
event: 'tableBuilt',
|
||||
handler: async () => {
|
||||
await this.$p.loadCategory(['lehre','global','person']);
|
||||
|
||||
let cm = this.$refs.table.tabulator.columnManager;
|
||||
|
||||
cm.getColumnByField('lehrverband').component.updateDefinition({
|
||||
title: this.$p.t('lehre', 'lehrverband')
|
||||
});
|
||||
|
||||
cm.getColumnByField('format_bestaetigtam').component.updateDefinition({
|
||||
title: this.$p.t('lehre', 'bestaetigt_am')
|
||||
});
|
||||
|
||||
cm.getColumnByField('format_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('format_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('format_insertamum').component.updateDefinition({
|
||||
title: this.$p.t('lehre', 'insert_am')
|
||||
});
|
||||
|
||||
cm.getColumnByField('insertvon').component.updateDefinition({
|
||||
title: this.$p.t('lehre', 'insert_von')
|
||||
});
|
||||
}
|
||||
}
|
||||
],
|
||||
statusData: {},
|
||||
statusId: {},
|
||||
dataMeldestichtag: null,
|
||||
statusNew: true,
|
||||
maxSem: 0
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
modelValue() {
|
||||
if (this.$refs.table) {
|
||||
if (this.$refs.table.tableBuilt)
|
||||
this.$refs.table.tabulator.setData('api/frontend/v1/stv/Status/getHistoryPrestudent/' + this.modelValue.prestudent_id);
|
||||
else
|
||||
this.data.tabulatorOptions.ajaxURL = 'api/frontend/v1/stv/Status/getHistoryPrestudent/' + this.modelValue.prestudent_id;
|
||||
}
|
||||
this.getMaxSem();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMaxSem() {
|
||||
const studiengang_kzs = this.modelValue.studiengang_kz
|
||||
? [this.modelValue.studiengang_kz]
|
||||
: this.modelValue.map(prestudent => prestudent.studiengang_kz);
|
||||
this.maxSem = 0;
|
||||
this.$fhcApi
|
||||
.post('api/frontend/v1/stv/status/getMaxSemester/', {studiengang_kzs})
|
||||
.then(result => this.maxSem = result.data)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
actionNewStatus() {
|
||||
this.$refs.test.open(this.modelValue);
|
||||
},
|
||||
actionEditStatus(status, stdsem, ausbildungssemester) {
|
||||
this.$refs.test.open(this.modelValue, status, stdsem, ausbildungssemester);
|
||||
},
|
||||
actionDeleteStatus(status, stdsem, ausbildungssemester) {
|
||||
const statusId = {
|
||||
prestudent_id: this.modelValue.prestudent_id,
|
||||
status_kurzbz: status,
|
||||
studiensemester_kurzbz: stdsem,
|
||||
ausbildungssemester: ausbildungssemester
|
||||
};
|
||||
|
||||
this.$fhcAlert
|
||||
.confirmDelete()
|
||||
.then(result => result
|
||||
? 'api/frontend/v1/stv/status/isLastStatus/' + statusId.prestudent_id
|
||||
: Promise.reject({handled: true})
|
||||
)
|
||||
.then(this.$fhcApi.get)
|
||||
.then(result => result.data
|
||||
? new Promise((resolve, reject) => { BsConfirm.popup(this.$p.t('lehre', 'last_status_confirm_delete')).then(resolve).catch(() => reject({handled:true})) })
|
||||
: true
|
||||
)
|
||||
.then(result => result
|
||||
? 'api/frontend/v1/stv/status/deleteStatus/' + Object.values(statusId).join('/')
|
||||
: Promise.reject({handled: true})
|
||||
)
|
||||
.then(this.$fhcApi.post)
|
||||
.then(() => this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete')))
|
||||
.then(this.reload)
|
||||
.then(this.$reloadList)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
actionAdvanceStatus(status, stdsem, ausbildungssemester) {
|
||||
const statusId = {
|
||||
prestudent_id: this.modelValue.prestudent_id,
|
||||
status_kurzbz: status,
|
||||
studiensemester_kurzbz: stdsem,
|
||||
ausbildungssemester: ausbildungssemester
|
||||
};
|
||||
this.$fhcApi
|
||||
.post('api/frontend/v1/stv/status/advanceStatus/' + Object.values(statusId).join('/'))
|
||||
.then(() => this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successAdvance')))
|
||||
.then(this.reload)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
actionConfirmStatus(status, stdsem, ausbildungssemester) {
|
||||
BsConfirm
|
||||
.popup(this.$p.t('stv', 'status_confirm_popup'))
|
||||
.then(() => this.$fhcApi.post(
|
||||
'api/frontend/v1/stv/status/confirmStatus/' +
|
||||
this.modelValue.prestudent_id + '/' +
|
||||
status + '/' +
|
||||
stdsem + '/' +
|
||||
ausbildungssemester
|
||||
))
|
||||
.then(() => this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successConfirm')))
|
||||
.then(this.reload)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
reload() {
|
||||
if (this.$refs.table)
|
||||
this.$refs.table.reloadTable();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getMaxSem();
|
||||
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/status/getLastBismeldestichtag/')
|
||||
.then(result => {
|
||||
this.dataMeldestichtag = result.data[0].meldestichtag;
|
||||
if (this.$refs.table && this.$refs.table.tableBuilt)
|
||||
this.$refs.table.tabulator.redraw(true);
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
template: `
|
||||
<div class="stv-multistatus h-100 pt-3">
|
||||
|
||||
<status-modal
|
||||
ref="test"
|
||||
:meldestichtag="new Date(dataMeldestichtag)"
|
||||
:max-sem="maxSem"
|
||||
@saved="reload"
|
||||
>
|
||||
</status-modal>
|
||||
|
||||
<core-filter-cmpt
|
||||
v-if="!this.modelValue.length"
|
||||
ref="table"
|
||||
:tabulator-options="tabulatorOptions"
|
||||
:tabulator-events="tabulatorEvents"
|
||||
table-only
|
||||
:side-menu="false"
|
||||
reload
|
||||
new-btn-show
|
||||
new-btn-label="Status"
|
||||
@click:new="actionNewStatus"
|
||||
>
|
||||
|
||||
<template #actions="{updateData2}">
|
||||
<!-- SingleSelectButton-->
|
||||
<status-dropdown
|
||||
ref="statusDropdown"
|
||||
:show-toolbar-student="showToolbarStudent"
|
||||
:show-toolbar-interessent="showToolbarInteressent"
|
||||
:prestudent-ids="prestudentIds"
|
||||
@reload-table="reload"
|
||||
>
|
||||
</status-dropdown>
|
||||
</template>
|
||||
|
||||
</core-filter-cmpt>
|
||||
|
||||
<div v-else>
|
||||
<!--MultiSelectButton-->
|
||||
<status-dropdown
|
||||
ref="statusDropdown"
|
||||
:show-toolbar-student="showToolbarStudent"
|
||||
:show-toolbar-interessent="showToolbarInteressent"
|
||||
:prestudent-ids="prestudentIds"
|
||||
@reload-table="reload"
|
||||
>
|
||||
</status-dropdown>
|
||||
</div>
|
||||
|
||||
</div>`
|
||||
};
|
||||
@@ -0,0 +1,336 @@
|
||||
import BsModal from "../../../../Bootstrap/Modal.js";
|
||||
import BsConfirm from "../../../../Bootstrap/Confirm.js";
|
||||
import BsPrompt from "../../../../Bootstrap/Prompt.js";
|
||||
import FormInput from '../../../../Form/Input.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BsModal,
|
||||
FormInput
|
||||
},
|
||||
inject: {
|
||||
$reloadList: {
|
||||
from: '$reloadList',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
emits: [
|
||||
'reloadTable'
|
||||
],
|
||||
props: {
|
||||
showToolbarStudent: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
showToolbarInteressent: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
prestudentIds: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
listDataToolbar: [],
|
||||
//TODO(Manu) get from config
|
||||
statiInteressent: ["Bewerber", "Aufgenommener", "Student" , "Wartender", "Abgewiesener"],
|
||||
statiStudent: ["Abbrecher", "Unterbrecher", "Student" , "Diplomand", "Absolvent"]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showToolbar() {
|
||||
return this.showToolbarStudent || this.showToolbarInteressent;
|
||||
},
|
||||
sortedGruende() {
|
||||
return this.listDataToolbar.reduce((result,current) => {
|
||||
if (!result[current.status_kurzbz])
|
||||
result[current.status_kurzbz] = [];
|
||||
result[current.status_kurzbz].push(current);
|
||||
return result;
|
||||
}, {});
|
||||
},
|
||||
resultInteressentArray() {
|
||||
const result = [];
|
||||
this.statiInteressent.forEach(status => {
|
||||
const defaultObject = {
|
||||
status_kurzbz: status,
|
||||
statusgrund_id: null,
|
||||
link: () => this['changeStatusTo' + status](),
|
||||
children: []
|
||||
};
|
||||
|
||||
if (status === "Student") {
|
||||
defaultObject.link = () => this.changeInteressentToStudent();
|
||||
|
||||
}
|
||||
result.push(defaultObject);
|
||||
if(this.sortedGruende[status]) {
|
||||
this.sortedGruende[status].forEach(item => {
|
||||
const itemObject = {
|
||||
status_kurzbz: item.status_kurzbz,
|
||||
statusgrund_id: item.statusgrund_id,
|
||||
beschreibung: item.beschreibung,
|
||||
link: () => this['changeStatusTo' + item.status_kurzbz](item.statusgrund_id),
|
||||
};
|
||||
|
||||
if (item.status_kurzbz === "Student") {
|
||||
itemObject.link = () => this['changeInteressentTo' + item.status_kurzbz](item.statusgrund_id);
|
||||
}
|
||||
defaultObject.children.push(itemObject);
|
||||
});
|
||||
//push one item object if student is in the array
|
||||
const hasStudentChild = defaultObject.children.some(child => child.status_kurzbz === "Student");
|
||||
|
||||
if (hasStudentChild) {
|
||||
defaultObject.children.push({
|
||||
status_kurzbz: 'Student',
|
||||
statusgrund_id: null,
|
||||
beschreibung: 'Student',
|
||||
link: () => this.changeInteressentToStudent()
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
resultStudentArray() {
|
||||
const result = [];
|
||||
this.statiStudent.forEach(status => {
|
||||
const defaultObject = {
|
||||
status_kurzbz: status,
|
||||
statusgrund_id: null,
|
||||
link: () => this['changeStatusTo' + status](),
|
||||
children: []
|
||||
};
|
||||
result.push(defaultObject);
|
||||
if(this.sortedGruende[status]) {
|
||||
this.sortedGruende[status].forEach(item => {
|
||||
const itemObject = {
|
||||
status_kurzbz: item.status_kurzbz,
|
||||
statusgrund_id: item.statusgrund_id,
|
||||
beschreibung: item.beschreibung,
|
||||
link: () => this['changeStatusTo' + item.status_kurzbz](item.statusgrund_id)
|
||||
};
|
||||
defaultObject.children.push(itemObject);
|
||||
});
|
||||
}
|
||||
//push one item object if student is in the array
|
||||
const hasStudentChild = defaultObject.children.some(child => child.status_kurzbz === "Student");
|
||||
|
||||
if (hasStudentChild) {
|
||||
defaultObject.children.push({
|
||||
status_kurzbz: 'Student',
|
||||
statusgrund_id: null,
|
||||
beschreibung: 'Student',
|
||||
link: () => this.changeStatusToStudent()
|
||||
});
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeInteressentToStudent(statusgrund_id) {
|
||||
this.addStudent({status_kurzbz: 'student', statusgrund_id});
|
||||
},
|
||||
addStudent(data) {
|
||||
Promise
|
||||
.allSettled(
|
||||
this.prestudentIds.map(prestudent_id => this.$fhcApi.post(
|
||||
'api/frontend/v1/stv/status/addStudent/' + prestudent_id,
|
||||
data,
|
||||
{ errorHeader: prestudent_id }
|
||||
))
|
||||
)
|
||||
.then(res => this.showFeedback(res, data.status_kurzbz));
|
||||
},
|
||||
changeStatusToAbbrecher(statusgrund_id) {
|
||||
this
|
||||
.confirmStatusChange('Abbrecher', statusgrund_id)
|
||||
.then(this.changeStatus)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
changeStatusToUnterbrecher(statusgrund_id) {
|
||||
this
|
||||
.confirmStatusChange('Unterbrecher', statusgrund_id)
|
||||
.then(this.changeStatus)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
changeStatusToStudent(statusgrund_id) {
|
||||
this
|
||||
.promtAusbildungssemester('Student', statusgrund_id)
|
||||
.then(this.changeStatus)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
changeStatusToDiplomand(statusgrund_id) {
|
||||
this.changeStatus({status_kurzbz: 'Diplomand', statusgrund_id});
|
||||
},
|
||||
changeStatusToAbsolvent(statusgrund_id) {
|
||||
this.changeStatus({status_kurzbz: 'Absolvent', statusgrund_id});
|
||||
},
|
||||
changeStatusToBewerber(statusgrund_id) {
|
||||
this.changeStatus({status_kurzbz: 'Bewerber', statusgrund_id});
|
||||
},
|
||||
changeStatusToAufgenommener(statusgrund_id) {
|
||||
this
|
||||
.confirmStatusChange('Aufgenommener', statusgrund_id)
|
||||
.then(this.changeStatus)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
changeStatusToAbgewiesener(statusgrund_id) {
|
||||
this
|
||||
.confirmStatusChange('Abgewiesener', statusgrund_id)
|
||||
.then(this.changeStatus)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
changeStatusToWartender(statusgrund_id) {
|
||||
this
|
||||
.confirmStatusChange('Wartender', statusgrund_id)
|
||||
.then(this.changeStatus)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
confirmStatusChange(status, statusgrund_id) {
|
||||
const count = this.prestudentIds.length;
|
||||
return BsConfirm
|
||||
.popup(this.$p.t(
|
||||
'lehre',
|
||||
count > 1 ? 'modal_StatusactionPlural' : 'modal_StatusactionSingle',
|
||||
{ count, status }
|
||||
))
|
||||
.then(() => ({
|
||||
status_kurzbz: status,
|
||||
statusgrund_id
|
||||
}));
|
||||
},
|
||||
promtAusbildungssemester(status, statusgrund_id) {
|
||||
const count = this.prestudentIds.length;
|
||||
return BsPrompt
|
||||
.popup(this.$p.t(
|
||||
'lehre',
|
||||
count > 1 ? 'modal_askAusbildungssemPlural' : 'modal_askAusbildungssem',
|
||||
{ count, status }
|
||||
))
|
||||
.then(ausbildungssemester => ({
|
||||
status_kurzbz: status,
|
||||
ausbildungssemester,
|
||||
statusgrund_id
|
||||
}));
|
||||
},
|
||||
changeStatus(data) {
|
||||
Promise
|
||||
.allSettled(
|
||||
this.prestudentIds.map(prestudent_id => this.$fhcApi.post(
|
||||
'api/frontend/v1/stv/status/changeStatus/' + prestudent_id,
|
||||
data,
|
||||
{ errorHeader: prestudent_id }
|
||||
))
|
||||
)
|
||||
.then(res => this.showFeedback(res, data.status_kurzbz));
|
||||
},
|
||||
showFeedback(results, status_kurzbz) {
|
||||
const countSuccess = results.filter(result => result.status == "fulfilled").length;
|
||||
const countError = results.length - countSuccess;
|
||||
|
||||
//Feedback Success als infoalert
|
||||
this.$fhcAlert.alertInfo(this.$p.t('ui', 'successNewStatus', {
|
||||
countSuccess,
|
||||
status: status_kurzbz,
|
||||
countError
|
||||
}));
|
||||
|
||||
if(results.length == 1 && countSuccess > 0){
|
||||
this.$emit('reloadTable');
|
||||
}
|
||||
this.$reloadList();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/status/getStatusarray/')
|
||||
.then(result => result.data)
|
||||
.then(result => {
|
||||
this.listDataToolbar = result;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
template: `
|
||||
<div class="stv-status-dropdown">
|
||||
|
||||
<div v-if="showToolbar" class="btn-group">
|
||||
<button ref="toolbarButton" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false">
|
||||
{{$p.t('lehre', 'btn_statusAendern')}}
|
||||
</button>
|
||||
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<!--toolbar Interessent-->
|
||||
<template v-if="showToolbarInteressent">
|
||||
<li v-for="item in resultInteressentArray" :key="item.status_kurzbz" class="w-100">
|
||||
|
||||
<div v-if="item.children.length > 0" class="btn-group dropend w-100">
|
||||
<a
|
||||
class="dropdown-item dropdown-toggle d-flex justify-content-between align-items-center"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
href="#"
|
||||
>
|
||||
{{ item.status_kurzbz }}
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li v-for="child in item.children" :key="child.statusgrund_id">
|
||||
<a class="dropdown-item" @click.prevent="child.link" href="#">{{ child.beschreibung }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-else>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click.prevent="item.link"
|
||||
href="#"
|
||||
>
|
||||
{{ item.status_kurzbz }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<!--toolbar Student-->
|
||||
<template v-if="showToolbarStudent">
|
||||
<li v-for="item in resultStudentArray" :key="item.status_kurzbz" class="w-100">
|
||||
|
||||
<div v-if="item.children.length > 0" class="btn-group dropend w-100">
|
||||
<a
|
||||
class="dropdown-item dropdown-toggle d-flex justify-content-between align-items-center"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
href="#"
|
||||
>
|
||||
{{ item.status_kurzbz }}
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li v-for="child in item.children" :key="child.statusgrund_id">
|
||||
<a class="dropdown-item" @click.prevent="child.link" href="#">{{ child.beschreibung }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-else>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click.prevent="item.link"
|
||||
href="#"
|
||||
>
|
||||
{{ item.status_kurzbz }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</li>
|
||||
</template>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>`
|
||||
};
|
||||
@@ -0,0 +1,398 @@
|
||||
import BsModal from "../../../../Bootstrap/Modal.js";
|
||||
import CoreForm from '../../../../Form/Form.js';
|
||||
import FormValidation from '../../../../Form/Validation.js';
|
||||
import FormInput from '../../../../Form/Input.js';
|
||||
|
||||
export default{
|
||||
components: {
|
||||
BsModal,
|
||||
CoreForm,
|
||||
FormValidation,
|
||||
FormInput
|
||||
},
|
||||
inject: {
|
||||
defaultSemester: {
|
||||
from: 'defaultSemester',
|
||||
},
|
||||
hasPermissionToSkipStatusCheck: {
|
||||
from: 'hasPermissionToSkipStatusCheck',
|
||||
default: false
|
||||
},
|
||||
hasPrestudentstatusPermission: {
|
||||
from: 'hasPrestudentstatusPermission',
|
||||
default: false
|
||||
},
|
||||
lists: {
|
||||
from: 'lists'
|
||||
},
|
||||
$reloadList: {
|
||||
from: '$reloadList',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
emit: [
|
||||
'saved'
|
||||
],
|
||||
props: {
|
||||
meldestichtag: {
|
||||
type: Date,
|
||||
required: true
|
||||
},
|
||||
maxSem: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
prestudent: {},
|
||||
originalDatum: null,
|
||||
statusNew: true,
|
||||
mischform: false,
|
||||
statusId: {},
|
||||
formData: {},
|
||||
studienplaene: [],
|
||||
statusgruende: [],
|
||||
stati: [],
|
||||
allowedNewStatus: [
|
||||
'Interessent',
|
||||
'Bewerber',
|
||||
'Aufgenommener',
|
||||
'Student',
|
||||
'Unterbrecher',
|
||||
'Diplomand',
|
||||
'Incoming'
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
semester() {
|
||||
return Array.from({length: this.maxSem}, (_, index) => index);
|
||||
},
|
||||
bisLocked() {
|
||||
if (this.statusNew || this.hasPermissionToSkipStatusCheck)
|
||||
return false;
|
||||
|
||||
if (!this.originalDatum || !this.meldestichtag)
|
||||
return true;
|
||||
|
||||
return this.originalDatum < this.meldestichtag;
|
||||
},
|
||||
isStatusBeforeStudent() {
|
||||
let isStatusStudent = ['Student', 'Absolvent', 'Diplomand'];
|
||||
return !isStatusStudent.includes(this.formData.status_kurzbz);
|
||||
},
|
||||
allowedStati() {
|
||||
if (!this.stati)
|
||||
return [];
|
||||
|
||||
if (this.statusNew)
|
||||
return this.stati.filter(status => this.allowedNewStatus.includes(status.status_kurzbz));
|
||||
|
||||
return this.stati.filter(status => this.statusId.status_kurzbz == status.status_kurzbz);
|
||||
},
|
||||
gruende() {
|
||||
return this.statusgruende.filter(grund => grund.status_kurzbz == this.formData.status_kurzbz);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open(prestudent, status_kurzbz, studiensemester_kurzbz, ausbildungssemester) {
|
||||
this.$refs.modal.hide();
|
||||
|
||||
if (!status_kurzbz && !studiensemester_kurzbz && !ausbildungssemester) {
|
||||
this.statusNew = true;
|
||||
this.statusId = prestudent.prestudent_id;
|
||||
this.formData = {
|
||||
status_kurzbz: 'Interessent',
|
||||
studiensemester_kurzbz: this.defaultSemester,
|
||||
ausbildungssemester: 1,
|
||||
datum: new Date(),
|
||||
bestaetigtam: new Date(),
|
||||
bewerbung_abgeschicktamum: null,
|
||||
studienplan_id: null,
|
||||
anmerkung: null,
|
||||
rt_stufe: null,
|
||||
statusgrund_id: null
|
||||
};
|
||||
this.originalDatum = null;
|
||||
|
||||
this.loadStudienplaeneAndSetPrestudent(prestudent)
|
||||
.then(this.$refs.form.clearValidation)
|
||||
.then(this.$refs.modal.show)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
} else {
|
||||
this.statusId = {
|
||||
prestudent_id: prestudent.prestudent_id,
|
||||
status_kurzbz,
|
||||
studiensemester_kurzbz,
|
||||
ausbildungssemester
|
||||
};
|
||||
|
||||
this.$fhcApi
|
||||
.post('api/frontend/v1/stv/status/loadStatus/', this.statusId)
|
||||
.then(result => {
|
||||
this.statusNew = false;
|
||||
this.formData = result.data;
|
||||
this.originalDatum = new Date(result.data.datum);
|
||||
return prestudent;
|
||||
})
|
||||
.then(this.loadStudienplaeneAndSetPrestudent)
|
||||
.then(this.$refs.form.clearValidation)
|
||||
.then(this.$refs.modal.show)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
}
|
||||
},
|
||||
insertStatus() {
|
||||
this.$refs.form
|
||||
.post(
|
||||
'api/frontend/v1/stv/status/insertStatus/' + this.statusId,
|
||||
this.formData
|
||||
)
|
||||
.then(result => {
|
||||
this.$reloadList();
|
||||
this.$emit('saved');
|
||||
this.$refs.modal.hide();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
editStatus() {
|
||||
this.$refs.form
|
||||
.post(
|
||||
'api/frontend/v1/stv/status/updateStatus/' + Object.values(this.statusId).join('/'),
|
||||
this.formData
|
||||
)
|
||||
.then(result => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||
this.$reloadList();
|
||||
this.$emit('saved');
|
||||
this.$refs.modal.hide();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
loadStudienplaeneAndSetPrestudent(prestudent) {
|
||||
const old_id = this.prestudent.prestudent_id;
|
||||
this.prestudent = prestudent;
|
||||
if (old_id == prestudent.prestudent_id)
|
||||
return Promise.resolve();
|
||||
|
||||
return this.$fhcApi
|
||||
.get('api/frontend/v1/stv/prestudent/getStudienplaene/' + prestudent.prestudent_id)
|
||||
.then(result => this.studienplaene = result.data)
|
||||
.then(() => this.$fhcApi.get('api/frontend/v1/stv/prestudent/getStudiengang/' + prestudent.prestudent_id))
|
||||
.then(result => this.mischform = result.data.mischform);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/status/getStatusgruende')
|
||||
.then(result => this.statusgruende = result.data)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
/*this.$fhcApi
|
||||
.get('api/frontend/v1/stv/lists/getStati')
|
||||
.then(result => this.stati = result.data)
|
||||
.catch(this.$fhcAlert.handleSystemError);*/
|
||||
this.stati = [
|
||||
{ status_kurzbz: 'Interessent', bezeichnung: 'Interessent'},
|
||||
{ status_kurzbz: 'Bewerber', bezeichnung: 'Bewerber'},
|
||||
{ status_kurzbz: 'Aufgenommener', bezeichnung: 'Aufgenommener'},
|
||||
{ status_kurzbz: 'Student', bezeichnung: 'Student'},
|
||||
{ status_kurzbz: 'Unterbrecher', bezeichnung: 'Unterbrecher'},
|
||||
{ status_kurzbz: 'Diplomand', bezeichnung: 'Diplomand'},
|
||||
{ status_kurzbz: 'Incoming', bezeichnung: 'Incoming'},
|
||||
{ status_kurzbz: 'Absolvent', bezeichnung: 'Absolvent'},
|
||||
{ status_kurzbz: 'Abbrecher', bezeichnung: 'Abbrecher'},
|
||||
{ status_kurzbz: 'Abgewiesener', bezeichnung: 'Abgewiesener'},
|
||||
{ status_kurzbz: 'Wartender', bezeichnung: 'Wartender'}
|
||||
];
|
||||
},
|
||||
template: `
|
||||
<bs-modal class="stv-status-modal" ref="modal">
|
||||
<template #title>
|
||||
{{ $p.t('lehre', statusNew ? 'status_new' : 'status_edit', prestudent) }}
|
||||
</template>
|
||||
|
||||
<core-form ref="form">
|
||||
|
||||
<form-validation></form-validation>
|
||||
|
||||
<p v-if="bisLocked && !isStatusBeforeStudent">
|
||||
<b>{{$p.t('bismeldestichtag', 'info_MeldestichtagStatusgrund')}}</b>
|
||||
</p>
|
||||
<p v-if="bisLocked && isStatusBeforeStudent">
|
||||
<b>{{$p.t('bismeldestichtag', 'info_MeldestichtagStatusgrundSemester')}}</b>
|
||||
</p>
|
||||
|
||||
<form-input
|
||||
container-class="mb-3"
|
||||
type="select"
|
||||
v-model="formData.status_kurzbz"
|
||||
name="status_kurzbz"
|
||||
:label="$p.t('lehre/status_rolle')"
|
||||
required
|
||||
:disabled="!statusNew"
|
||||
>
|
||||
<option
|
||||
v-for="status in allowedStati"
|
||||
:value="status.status_kurzbz"
|
||||
>
|
||||
{{ status.bezeichnung }}
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="mb-3"
|
||||
type="select"
|
||||
v-model="formData.studiensemester_kurzbz"
|
||||
name="studiensemester_kurzbz"
|
||||
:label="$p.t('lehre/studiensemester')"
|
||||
:disabled="bisLocked"
|
||||
>
|
||||
<option
|
||||
v-for="sem in lists.studiensemester_desc"
|
||||
:key="sem.studiensemester_kurzbz"
|
||||
:value="sem.studiensemester_kurzbz"
|
||||
>
|
||||
{{ sem.studiensemester_kurzbz }}
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="mb-3"
|
||||
type="select"
|
||||
v-model="formData.ausbildungssemester"
|
||||
name="ausbildungssemester"
|
||||
:label="$p.t('lehre/ausbildungssemester')"
|
||||
:disabled="bisLocked && !isStatusBeforeStudent"
|
||||
>
|
||||
<option
|
||||
v-for="number in semester"
|
||||
:key="number"
|
||||
:value="number"
|
||||
>
|
||||
{{ number }}
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-if="mischform"
|
||||
container-class="mb-3"
|
||||
type="select"
|
||||
v-model="formData.orgform_kurzbz"
|
||||
name="orgform_kurzbz"
|
||||
:label="$p.t('lehre/organisationsform')"
|
||||
:disabled="bisLocked && !isStatusBeforeStudent"
|
||||
>
|
||||
<option
|
||||
v-for="orgform in lists.orgforms"
|
||||
:key="orgform.orgform_kurzbz"
|
||||
:value="orgform.orgform_kurzbz"
|
||||
>
|
||||
{{ orgform.bezeichnung }}
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="mb-3"
|
||||
type="DatePicker"
|
||||
v-model="formData.datum"
|
||||
name="datum"
|
||||
:label="$p.t('global/datum')"
|
||||
auto-apply
|
||||
:enable-time-picker="false"
|
||||
format="dd.MM.yyyy"
|
||||
preview-format="dd.MM.yyyy"
|
||||
:teleport="true"
|
||||
:disabled="bisLocked"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="mb-3"
|
||||
type="DatePicker"
|
||||
v-model="formData.bestaetigtam"
|
||||
name="bestaetigtam"
|
||||
:label="$p.t('lehre/bestaetigt_am')"
|
||||
auto-apply
|
||||
:enable-time-picker="false"
|
||||
format="dd.MM.yyyy"
|
||||
preview-format="dd.MM.yyyy"
|
||||
:teleport="true"
|
||||
:disabled="bisLocked"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="mb-3"
|
||||
type="DatePicker"
|
||||
v-model="formData.bewerbung_abgeschicktamum"
|
||||
name="bewerbung_abgeschicktamum"
|
||||
:label="$p.t('lehre/bewerbung_abgeschickt_am')"
|
||||
auto-apply
|
||||
:enable-time-picker="false"
|
||||
format="dd.MM.yyyy"
|
||||
preview-format="dd.MM.yyyy"
|
||||
:teleport="true"
|
||||
:disabled="bisLocked || !hasPrestudentstatusPermission"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="mb-3"
|
||||
type="select"
|
||||
v-model="formData.studienplan_id"
|
||||
name="studienplan_id"
|
||||
:label="$p.t('lehre/studienplan')"
|
||||
:disabled="bisLocked"
|
||||
>
|
||||
<option
|
||||
v-for="plan in studienplaene"
|
||||
:key="plan.studienplan_id"
|
||||
:value="plan.studienplan_id"
|
||||
>
|
||||
{{ plan.bezeichnung }}
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="mb-3"
|
||||
type="text"
|
||||
v-model="formData.anmerkung"
|
||||
name="anmerkung"
|
||||
:label="$p.t('global/anmerkung')"
|
||||
:disabled="bisLocked"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="mb-3"
|
||||
type="select"
|
||||
v-model="formData.rt_stufe"
|
||||
name="rt_stufe"
|
||||
:label="$p.t('lehre/aufnahmestufe')"
|
||||
:disabled="bisLocked"
|
||||
>
|
||||
<option :value="null">-- {{$p.t('fehlermonitoring', 'keineAuswahl')}} --</option>
|
||||
<option v-for="entry in [1,2,3]" :key="entry" :value="entry">{{entry}}</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
v-if="gruende.length"
|
||||
container-class="mb-3"
|
||||
type="select"
|
||||
v-model="formData.statusgrund_id"
|
||||
name="statusgrund_id"
|
||||
:label="$p.t('international/grund')"
|
||||
>
|
||||
<option :value="null">-- {{$p.t('fehlermonitoring', 'keineAuswahl')}} --</option>
|
||||
<option
|
||||
v-for="grund in gruende"
|
||||
:key="grund.statusgrund_id"
|
||||
:value="grund.statusgrund_id"
|
||||
>
|
||||
{{ grund.bezeichnung }}
|
||||
</option>
|
||||
</form-input>
|
||||
|
||||
</core-form>
|
||||
|
||||
<template #footer>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
@click="statusNew ? insertStatus() : editStatus()"
|
||||
>
|
||||
{{ $p.t('ui', 'ok') }}
|
||||
</button>
|
||||
</template>
|
||||
</bs-modal>`
|
||||
};
|
||||
@@ -0,0 +1,278 @@
|
||||
import {CoreFilterCmpt} from "../../filter/Filter.js";
|
||||
import ListNew from './List/New.js';
|
||||
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFilterCmpt,
|
||||
ListNew
|
||||
},
|
||||
inject: [
|
||||
'lists'
|
||||
],
|
||||
props: {
|
||||
selected: Array,
|
||||
studiengangKz: Number,
|
||||
studiensemesterKurzbz: String
|
||||
},
|
||||
emits: [
|
||||
'update:selected'
|
||||
],
|
||||
data() {
|
||||
function dateFormatter(cell)
|
||||
{
|
||||
let val = cell.getValue();
|
||||
if (!val)
|
||||
return ' ';
|
||||
let date = new Date(val);
|
||||
return date.toLocaleDateString();
|
||||
}
|
||||
|
||||
return {
|
||||
tabulatorOptions: {
|
||||
columns:[
|
||||
{title:"UID", field:"uid"},
|
||||
{title:"TitelPre", field:"titelpre"},
|
||||
{title:"Nachname", field:"nachname"},
|
||||
{title:"Vorname", field:"vorname"},
|
||||
{title:"Wahlname", field:"wahlname", visible:false},
|
||||
{title:"Vornamen", field:"vornamen", visible:false},
|
||||
{title:"TitelPost", field:"titelpost"},
|
||||
{title:"SVNR", field:"svnr"},
|
||||
{title:"Ersatzkennzeichen", field:"ersatzkennzeichen"},
|
||||
{title:"Geburtsdatum", field:"gebdatum", formatter:dateFormatter},
|
||||
{title:"Geschlecht", field:"geschlecht"},
|
||||
{title:"Sem.", field:"semester"},
|
||||
{title:"Verb.", field:"verband"},
|
||||
{title:"Grp.", field:"gruppe"},
|
||||
{title:"Studiengang", field:"studiengang"},
|
||||
{title:"Studiengang_kz", field:"studiengang_kz", visible:false},
|
||||
{title:"Personenkennzeichen", field:"matrikelnr"},
|
||||
{title:"PersonID", field:"person_id"},
|
||||
{title:"Status", field:"status"},
|
||||
{title:"Status Datum", field:"status_datum", visible:false, formatter:dateFormatter},
|
||||
{title:"Status Bestaetigung", field:"status_bestaetigung", visible:false, formatter:dateFormatter},
|
||||
{title:"EMail (Privat)", field:"mail_privat", visible:false},
|
||||
{title:"EMail (Intern)", field:"mail_intern", visible:false},
|
||||
{title:"Anmerkungen", field:"anmerkungen", visible:false},
|
||||
{title:"AnmerkungPre", field:"anmerkung", visible:false},
|
||||
{title:"OrgForm", field:"orgform_kurzbz"},
|
||||
{title:"Aufmerksamdurch", field:"aufmerksamdurch_kurzbz", visible:false},
|
||||
{title:"Gesamtpunkte", field:"punkte", visible:false},
|
||||
{title:"Aufnahmegruppe", field:"aufnahmegruppe_kurzbz", visible:false},
|
||||
{title:"Dual", field:"dual", visible:false, formatter:'tickCross', formatterParams: {
|
||||
tickElement: '<i class="fas fa-check text-success"></i>',
|
||||
crossElement: '<i class="fas fa-times text-danger"></i>'
|
||||
}},
|
||||
{title:"Matrikelnummer", field:"matr_nr", visible:false},
|
||||
{title:"Studienplan", field:"studienplan_bezeichnung"},
|
||||
{title:"PreStudentInnenID", field:"prestudent_id"},
|
||||
{title:"Priorität", field:"priorisierung_relativ"},
|
||||
{title:"Mentor", field:"mentor", visible:false},
|
||||
{title:"Aktiv", field:"bnaktiv", visible:false, formatter:'tickCross', formatterParams: {
|
||||
allowEmpty:true,
|
||||
tickElement: '<i class="fas fa-check text-success"></i>',
|
||||
crossElement: '<i class="fas fa-times text-danger"></i>'
|
||||
}},
|
||||
],
|
||||
rowFormatter(row) {
|
||||
if (row.getData().bnaktiv === false) {
|
||||
row.getElement().classList.add('text-muted');
|
||||
}
|
||||
},
|
||||
|
||||
ajaxResponse: (url, params, response) => response.data,
|
||||
|
||||
layout: 'fitDataStretch',
|
||||
layoutColumnsOnNewData: false,
|
||||
height: '100%',
|
||||
selectable: true,
|
||||
selectableRangeMode: 'click',
|
||||
index: 'prestudent_id',
|
||||
persistenceID: 'stv-list'
|
||||
},
|
||||
tabulatorEvents: [
|
||||
{
|
||||
event: 'rowSelectionChanged',
|
||||
handler: this.rowSelectionChanged
|
||||
},
|
||||
{
|
||||
event: 'dataProcessed',
|
||||
handler: this.autoSelectRows
|
||||
},
|
||||
{
|
||||
event: 'rowClick',
|
||||
handler: this.handleRowClick // TODO(chris): this should be in the filter component
|
||||
}
|
||||
],
|
||||
focusObj: null, // TODO(chris): this should be in the filter component
|
||||
lastSelected: null,
|
||||
filterKontoCount0: undefined,
|
||||
filterKontoMissingCounter: undefined
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reload() {
|
||||
this.$refs.table.reloadTable();
|
||||
},
|
||||
actionNewPrestudent() {
|
||||
this.$refs.new.open();
|
||||
},
|
||||
rowSelectionChanged(data) {
|
||||
this.lastSelected = this.selected;
|
||||
this.$emit('update:selected', data);
|
||||
},
|
||||
autoSelectRows(data) {
|
||||
if (this.lastSelected) {
|
||||
// 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?
|
||||
// or maybe reselect only the last one?
|
||||
selected = selected.filter(el => el);
|
||||
|
||||
if (selected.length)
|
||||
this.$refs.table.tabulator.selectRow(selected);
|
||||
} else if(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());
|
||||
}
|
||||
}
|
||||
},
|
||||
updateUrl(url, first) {
|
||||
this.lastSelected = first ? undefined : this.selected;
|
||||
|
||||
const params = {}, filter = {};
|
||||
if (this.filterKontoCount0)
|
||||
filter.konto_count_0 = this.filterKontoCount0;
|
||||
if (this.filterKontoMissingCounter)
|
||||
filter.konto_missing_counter = this.filterKontoMissingCounter;
|
||||
|
||||
if (filter.konto_count_0 || filter.konto_missing_counter)
|
||||
params.filter = filter;
|
||||
|
||||
if (!this.$refs.table.tableBuilt) {
|
||||
if (!this.$refs.table.tabulator) {
|
||||
this.tabulatorOptions.ajaxURL = url;
|
||||
this.tabulatorOptions.ajaxParams = params;
|
||||
} else
|
||||
this.$refs.table.tabulator.on("tableBuilt", () => {
|
||||
this.$refs.table.tabulator.setData(url, params);
|
||||
});
|
||||
} else
|
||||
this.$refs.table.tabulator.setData(url, params);
|
||||
},
|
||||
onKeydown(e) { // TODO(chris): this should be in the filter component
|
||||
if (!this.focusObj)
|
||||
return;
|
||||
switch (e.code) {
|
||||
case 'Enter':
|
||||
case 'Space':
|
||||
e.preventDefault();
|
||||
const e2 = new Event('click', e);
|
||||
e2.altKey = e.altKey;
|
||||
e2.ctrlKey = e.ctrlKey;
|
||||
e2.shiftKey = e.shiftKey;
|
||||
this.focusObj.dispatchEvent(e2);
|
||||
//row.component.toggleSelect();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
var next = this.focusObj.previousElementSibling;
|
||||
if (next)
|
||||
this.changeFocus(this.focusObj, next);
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
var next = this.focusObj.nextElementSibling;
|
||||
if (next)
|
||||
this.changeFocus(this.focusObj, next);
|
||||
break;
|
||||
}
|
||||
},
|
||||
changeFocus(a, b) { // TODO(chris): this should be in the filter component
|
||||
if (b) {
|
||||
b.tabIndex = 0;
|
||||
this.focusObj = b;
|
||||
b.focus();
|
||||
} else {
|
||||
this.focusObj = null;
|
||||
}
|
||||
a.tabIndex = -1;
|
||||
return this.focusObj;
|
||||
},
|
||||
onFocus(e) { // TODO(chris): this should be in the filter component
|
||||
if (!this.focusObj) {
|
||||
var container, target;
|
||||
if (e.target.classList.contains('tabulator-container')) {
|
||||
container = e.target;
|
||||
target = container.querySelector('.tabulator-row');
|
||||
} else if (e.target.classList.contains('tabulator-row')) {
|
||||
container = e.target.closest('.tabulator-container');
|
||||
target = e.target;
|
||||
}
|
||||
if (container && target) {
|
||||
this.changeFocus(container, target);
|
||||
}
|
||||
}
|
||||
},
|
||||
handleRowClick(e, row) { // TODO(chris): this should be in the filter component
|
||||
if (this.focusObj) {
|
||||
let el = row.getElement();
|
||||
if (el != this.focusObj)
|
||||
this.changeFocus(this.focusObj, el);
|
||||
}
|
||||
}
|
||||
},
|
||||
// TODO(chris): focusin, focusout, keydown and tabindex should be in the filter component
|
||||
// TODO(chris): filter component column chooser has no accessibilty features
|
||||
template: `
|
||||
<div class="stv-list h-100 pt-3">
|
||||
<div class="tabulator-container d-flex flex-column h-100" :class="{'has-filter': filterKontoCount0 || filterKontoMissingCounter}" tabindex="0" @focusin="onFocus" @keydown="onKeydown">
|
||||
<core-filter-cmpt
|
||||
ref="table"
|
||||
:tabulator-options="tabulatorOptions"
|
||||
:tabulator-events="tabulatorEvents"
|
||||
table-only
|
||||
:side-menu="false"
|
||||
reload
|
||||
` + /* TODO(chris): Ausgeblendet für Testing
|
||||
new-btn-show
|
||||
*/`
|
||||
:new-btn-label="$p.t('stv/action_new')"
|
||||
@click:new="actionNewPrestudent"
|
||||
>
|
||||
<template #filter>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="input-group mb-3">
|
||||
<label class="input-group-text col-4" for="stv-list-filter-konto-count-0">{{ $p.t('stv/konto_filter_count_0') }}</label>
|
||||
<select class="form-select" id="stv-list-filter-konto-count-0" v-model="filterKontoCount0" @input="$nextTick(updateUrl)">
|
||||
<option v-for="typ in lists.buchungstypen" :key="typ.buchungstyp_kurzbz" :value="typ.buchungstyp_kurzbz">
|
||||
{{ typ.beschreibung }}
|
||||
</option>
|
||||
</select>
|
||||
<button v-if="filterKontoCount0" class="btn btn-outline-secondary" @click="filterKontoCount0 = undefined; updateUrl()">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label class="input-group-text col-4" for="stv-list-filter-konto-missing-counter">{{ $p.t('stv/konto_filter_missing_counter') }}</label>
|
||||
<select class="form-select" id="stv-list-filter-konto-missing-counter" v-model="filterKontoMissingCounter" @input="$nextTick(updateUrl)">
|
||||
<option value="alle">{{ $p.t('stv/konto_all_types') }}</option>
|
||||
<option v-for="typ in lists.buchungstypen" :key="typ.buchungstyp_kurzbz" :value="typ.buchungstyp_kurzbz">
|
||||
{{ typ.beschreibung }}
|
||||
</option>
|
||||
</select>
|
||||
<button v-if="filterKontoMissingCounter" class="btn btn-outline-secondary" @click="filterKontoMissingCounter = undefined; updateUrl()">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</core-filter-cmpt>
|
||||
</div>
|
||||
<list-new ref="new" :studiengang-kz="studiengangKz" :studiensemester-kurzbz="studiensemesterKurzbz"></list-new>
|
||||
</div>`
|
||||
};
|
||||
@@ -0,0 +1,681 @@
|
||||
import {CoreRESTClient} from '../../../../RESTClient.js';
|
||||
import BsModal from '../../../Bootstrap/Modal.js';
|
||||
import FhcForm from '../../../Form/Form.js';
|
||||
import FormValidation from '../../../Form/Validation.js';
|
||||
import FormInput from '../../../Form/Input.js';
|
||||
import accessibility from '../../../../directives/accessibility.js';
|
||||
|
||||
var _uuid = 0;
|
||||
const FORMDATA_DEFAULT = {
|
||||
address: {
|
||||
func: 0,
|
||||
nation: 'A'
|
||||
},
|
||||
geburtsnation: 'A',
|
||||
staatsbuergerschaft: 'A',
|
||||
ausbildungssemester: 1,
|
||||
orgform_kurzbz: '',
|
||||
studienplan_id: ''
|
||||
};
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BsModal,
|
||||
FhcForm,
|
||||
FormValidation,
|
||||
FormInput
|
||||
},
|
||||
directives: {
|
||||
accessibility
|
||||
},
|
||||
inject: [
|
||||
'lists'
|
||||
],
|
||||
props: {
|
||||
studiengangKz: Number,
|
||||
studiensemesterKurzbz: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
places: [],
|
||||
formData: FORMDATA_DEFAULT,
|
||||
suggestions: {},
|
||||
person: null,
|
||||
semester: [],
|
||||
studienplaene: [],
|
||||
abortController: {
|
||||
suggestions: null,
|
||||
places: null
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formDataPerson() {
|
||||
if (this.person)
|
||||
return this.person;
|
||||
return this.formData;
|
||||
},
|
||||
orte() {
|
||||
return this.places.filter(ort => ort.name == this.formData.address.gemeinde);
|
||||
},
|
||||
gemeinden() {
|
||||
return Object.values(this.places.reduce((res,place) => {
|
||||
res[place.name] = place;
|
||||
return res;
|
||||
}, {}));
|
||||
},
|
||||
formDataStg: {
|
||||
get() {
|
||||
return this.formData.studiengang_kz !== undefined ? this.formData.studiengang_kz : this.studiengangKz;
|
||||
},
|
||||
set(v) {
|
||||
this.formData.studiengang_kz = v;
|
||||
}
|
||||
},
|
||||
formDataSem: {
|
||||
get() {
|
||||
return this.formData.studiensemester_kurzbz !== undefined ? this.formData.studiensemester_kurzbz : this.studiensemesterKurzbz;
|
||||
},
|
||||
set(v) {
|
||||
this.formData.studiensemester_kurzbz = v;
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
formDataStg() {
|
||||
this.loadStudienplaene();
|
||||
},
|
||||
formDataSem() {
|
||||
this.loadStudienplaene();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.$refs.modal.show();
|
||||
},
|
||||
reset() {
|
||||
this.formData = FORMDATA_DEFAULT;
|
||||
this.person = null;
|
||||
this.suggestions = [];
|
||||
this.$refs.form.clearValidation();
|
||||
},
|
||||
loadSuggestions() {console.log('loadSuggestions');
|
||||
if (this.abortController.suggestions)
|
||||
this.abortController.suggestions.abort();
|
||||
if (this.person !== null)
|
||||
return;
|
||||
|
||||
this.abortController.suggestions = new AbortController();
|
||||
// TODO(chris): move to fhcapi.factory
|
||||
this.$fhcApi
|
||||
.post('api/frontend/v1/stv/student/check', {
|
||||
vorname: this.formData.vorname,
|
||||
nachname: this.formData.nachname,
|
||||
gebdatum: this.formData.gebdatum
|
||||
}, {
|
||||
signal: this.abortController.suggestions.signal
|
||||
})
|
||||
.then(result => this.suggestions = result.data)
|
||||
.catch(error => {
|
||||
// NOTE(chris): repeat request
|
||||
if (error.code != "ERR_CANCELED")
|
||||
window.setTimeout(this.loadSuggestions, 100);
|
||||
});
|
||||
},
|
||||
loadPlaces() {
|
||||
if (this.abortController.places)
|
||||
this.abortController.places.abort();
|
||||
if (this.formData.address.nation != 'A' || !this.formData.address.plz)
|
||||
return;
|
||||
|
||||
this.abortController.places = new AbortController();
|
||||
this.$refs.form
|
||||
.get(
|
||||
'api/frontend/v1/stv/address/getPlaces/' + this.formData.address.plz,
|
||||
undefined,
|
||||
{
|
||||
signal: this.abortController.places.signal
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
this.places = result.data
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.code != "ERR_CANCELED")
|
||||
window.setTimeout(this.loadPlaces, 100);
|
||||
else
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
});
|
||||
},
|
||||
loadStudienplaene() {
|
||||
if (this.formDataStg)
|
||||
CoreRESTClient
|
||||
.post('components/stv/studienplan/get', {
|
||||
studiengang_kz: this.formDataStg,
|
||||
studiensemester_kurzbz: this.formDataSem,
|
||||
ausbildungssemester: this.formData.ausbildungssemester,
|
||||
orgform_kurzbz: this.formData.orgform_kurzbz
|
||||
})
|
||||
.then(result => CoreRESTClient.getData(result.data) || [])
|
||||
.then(result => {
|
||||
this.studienplaene = result;
|
||||
if (this.formData.studienplan_id !== '' && !this.studienplaene.filter(plan => plan.studienplan_id == this.formData.studienplan_id).length)
|
||||
this.formData.studienplan_id = '';
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.code == 'ERR_BAD_REQUEST') {
|
||||
return this.studienplaene = [];
|
||||
}
|
||||
// NOTE(chris): repeat request
|
||||
if (error.code != "ERR_CANCELED")
|
||||
window.setTimeout(this.loadStudienplaene, 100);
|
||||
})
|
||||
},
|
||||
changeAddressNation(e) {
|
||||
if (this.formData['geburtsnation'] == this.formData['address']['nation'])
|
||||
this.formData['geburtsnation'] = e.target.value;
|
||||
if (this.formData['staatsbuergerschaft'] == this.formData['address']['nation'])
|
||||
this.formData['staatsbuergerschaft'] = e.target.value;
|
||||
this.loadPlaces();
|
||||
},
|
||||
send(e) {
|
||||
if (this.person === null)
|
||||
return this.person = 0;
|
||||
|
||||
//this.$fhcAlert.resetFormValidation(this.$refs.form);
|
||||
const data = {...this.formData, ...(this.person || {})};
|
||||
if (data.studiengang_kz === undefined)
|
||||
data.studiengang_kz = this.studiengangKz;
|
||||
if (data.studiensemester_kurzbz === undefined)
|
||||
data.studiensemester_kurzbz = this.studiensemesterKurzbz;
|
||||
|
||||
// TODO(chris): move to fhcapi.factory
|
||||
this.$refs.form
|
||||
.send('api/frontend/v1/stv/student/add', data)
|
||||
.then(result => {
|
||||
this.$fhcAlert.alertSuccess('Gespeichert');
|
||||
this.$refs.modal.hide();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.uuid = _uuid++;
|
||||
CoreRESTClient
|
||||
.get('components/stv/Studiensemester')
|
||||
.then(result => CoreRESTClient.getData(result.data) || [])
|
||||
.then(result => {
|
||||
this.semester = result;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
template: `
|
||||
<fhc-form ref="form" class="stv-list-new" @submit.prevent="send">
|
||||
<bs-modal ref="modal" dialog-class="modal-lg modal-scrollable" @hidden-bs-modal="reset">
|
||||
<template #title>
|
||||
InteressentIn anlegen
|
||||
</template>
|
||||
<template #default>
|
||||
|
||||
<form-validation></form-validation>
|
||||
|
||||
<template v-if="person === null">
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Nachname*"
|
||||
type="text"
|
||||
id="stv-list-new-nachname"
|
||||
name="nachname"
|
||||
v-model="formDataPerson['nachname']"
|
||||
:disabled="person"
|
||||
@input="loadSuggestions"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Vorname"
|
||||
type="text"
|
||||
:id="'stv-list-new-vorname-' + uuid"
|
||||
name="vorname"
|
||||
v-model="formDataPerson['vorname']"
|
||||
:disabled="person"
|
||||
@input="loadSuggestions"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Geburtsdatum"
|
||||
type="datepicker"
|
||||
uid="stv-list-new-gebdatum"
|
||||
name="gebdatum"
|
||||
v-model="formDataPerson['gebdatum']"
|
||||
:disabled="person"
|
||||
@update:model-value="loadSuggestions"
|
||||
text-input
|
||||
auto-apply
|
||||
no-today
|
||||
:enable-time-picker="false"
|
||||
format="dd.MM.yyyy"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO(chris): more details -->
|
||||
<table class="table caption-top table-striped table-hover">
|
||||
<caption>Prüfung ob Person bereits existiert</caption>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(suggestion, index) in suggestions"
|
||||
:key="suggestion.person_id"
|
||||
:class="{'active': index == 2}"
|
||||
@click="(index == 2) ? suggestions.shift() : person=suggestion"
|
||||
v-accessibility:tab.vertical
|
||||
>
|
||||
<td>{{suggestion.vorname + ' ' + suggestion.nachname}}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Anrede"
|
||||
type="text"
|
||||
id="stv-list-new-anrede"
|
||||
name="anrede"
|
||||
v-model="formDataPerson['anrede']"
|
||||
:disabled="person"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Titel (Pre)"
|
||||
type="text"
|
||||
id="stv-list-new-titelpre"
|
||||
name="titelpre"
|
||||
v-model="formDataPerson['titelpre']"
|
||||
:disabled="person"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Titel (Post)"
|
||||
type="text"
|
||||
id="stv-list-new-titelpost"
|
||||
name="titelpost"
|
||||
v-model="formDataPerson['titelpost']"
|
||||
:disabled="person"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Nachname*"
|
||||
type="text"
|
||||
id="stv-list-new-nachname"
|
||||
name="nachname"
|
||||
v-model="formDataPerson['nachname']"
|
||||
:disabled="person"
|
||||
@input="loadSuggestions"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Vorname"
|
||||
type="text"
|
||||
id="stv-list-new-vorname"
|
||||
name="vorname"
|
||||
v-model="formDataPerson['vorname']"
|
||||
:disabled="person"
|
||||
@input="loadSuggestions"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Weitere Vornamen"
|
||||
type="text"
|
||||
id="stv-list-new-vornamen"
|
||||
name="vornamen"
|
||||
v-model="formDataPerson['vornamen']"
|
||||
:disabled="person"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Wahlname"
|
||||
type="text"
|
||||
id="stv-list-new-wahlname"
|
||||
name="wahlname"
|
||||
v-model="formDataPerson['wahlname']"
|
||||
:disabled="person"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Geschlecht*"
|
||||
type="select"
|
||||
id="stv-list-new-geschlecht"
|
||||
name="geschlecht"
|
||||
v-model="formDataPerson['geschlecht']"
|
||||
:disabled="person"
|
||||
>
|
||||
<option v-for="geschlecht in lists.geschlechter" :key="geschlecht.geschlecht" :value="geschlecht.geschlecht">{{geschlecht.bezeichnung}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Geburtsdatum"
|
||||
type="datepicker"
|
||||
uid="stv-list-new-gebdatum"
|
||||
name="gebdatum"
|
||||
v-model="formDataPerson['gebdatum']"
|
||||
:disabled="person"
|
||||
@update:model-value="loadSuggestions"
|
||||
text-input
|
||||
auto-apply
|
||||
no-today
|
||||
:enable-time-picker="false"
|
||||
format="dd.MM.yyyy"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="person" class="row">
|
||||
<div class="col-sm-6 mb-3">
|
||||
<form-input
|
||||
type="select"
|
||||
id="stv-list-new-address-func"
|
||||
name="address[func]"
|
||||
v-model="formData['address']['func']"
|
||||
>
|
||||
<option value="-1">Bestehende Adresse überschreiben</option>
|
||||
<option value="1">Adresse hinzufügen</option>
|
||||
<option value="0">Adresse nicht anlegen</option>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<fieldset v-if="!person || formData['address']['func']">
|
||||
<legend>Adresse</legend>
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Land"
|
||||
type="select"
|
||||
id="stv-list-new-address-nation"
|
||||
name="address[nation]"
|
||||
v-model="formData['address']['nation']"
|
||||
@input="changeAddressNation"
|
||||
>
|
||||
<option v-for="nation in lists.nations" :key="nation.nation_code" :value="nation.nation_code" :disabled="nation.sperre">{{nation.langtext}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="PLZ"
|
||||
type="text"
|
||||
id="stv-list-new-address-plz"
|
||||
name="address[plz]"
|
||||
v-model="formData['address']['plz']"
|
||||
@input="loadPlaces"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Gemeinde"
|
||||
type="select"
|
||||
v-if="formData['address']['nation'] == 'A'"
|
||||
id="stv-list-new-address-gemeinde"
|
||||
name="address[gemeinde]"
|
||||
v-model="formData['address']['gemeinde']"
|
||||
>
|
||||
<option v-if="!gemeinden.length" disabled>Bitte gültige PLZ wählen</option>
|
||||
<option v-for="gemeinde in gemeinden" :key="gemeinde.name" :value="gemeinde.name">{{gemeinde.name}}</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
label="Gemeinde"
|
||||
type="text"
|
||||
v-else
|
||||
id="stv-list-new-address-gemeinde"
|
||||
name="address[gemeinde]"
|
||||
v-model="formData['address']['gemeinde']"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Ort"
|
||||
type="select"
|
||||
v-if="formData['address']['nation'] == 'A'"
|
||||
id="stv-list-new-address-ort"
|
||||
name="address[ort]"
|
||||
v-model="formData['address']['ort']"
|
||||
>
|
||||
<option v-if="!orte.length" disabled>Bitte gültige Gemeinde wählen</option>
|
||||
<option v-for="ort in orte" :key="ort.ortschaftsname" :value="ort.ortschaftsname">{{ort.ortschaftsname}}</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
label="Ort"
|
||||
type="text"
|
||||
v-else
|
||||
id="stv-list-new-address-ort"
|
||||
name="address[ort]"
|
||||
v-model="formData['address']['ort']"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
<form-input
|
||||
label="Adresse"
|
||||
type="text"
|
||||
id="stv-list-new-address-address"
|
||||
name="address[address]"
|
||||
v-model="formData['address']['address']"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Geburtsnation"
|
||||
type="select"
|
||||
id="stv-list-new-geburtsnation"
|
||||
name="geburtsnation" class="form-select"
|
||||
v-model="formData['geburtsnation']"
|
||||
>
|
||||
<option v-for="nation in lists.nations" :key="nation.nation_code" :value="nation.nation_code" :disabled="nation.sperre">{{nation.langtext}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Staatsbürgerschaft"
|
||||
type="select"
|
||||
id="stv-list-new-staatsbuergerschaft"
|
||||
name="staatsbuergerschaft"
|
||||
v-model="formData['staatsbuergerschaft']"
|
||||
>
|
||||
<option v-for="nation in lists.nations" :key="nation.nation_code" :value="nation.nation_code" :disabled="nation.sperre">{{nation.langtext}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="E-Mail"
|
||||
type="text"
|
||||
id="stv-list-new-email"
|
||||
name="email"
|
||||
v-model="formDataPerson['email']"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Telefon"
|
||||
type="text"
|
||||
id="stv-list-new-telefon"
|
||||
name="telefon"
|
||||
v-model="formDataPerson['telefon']"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Mobil"
|
||||
type="text"
|
||||
id="stv-list-new-mobil"
|
||||
name="mobil"
|
||||
v-model="formDataPerson['mobil']"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Letzte Ausbildung"
|
||||
type="select"
|
||||
id="stv-list-new-letzteausbildung"
|
||||
name="letzteausbildung"
|
||||
v-model="formData['letzteausbildung']"
|
||||
>
|
||||
<option v-for="ausbildung in lists.ausbildungen" :key="ausbildung.ausbildungcode" :value="ausbildung.ausbildungcode">{{ausbildung.ausbildungbez}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Ausbildungsart"
|
||||
type="text"
|
||||
id="stv-list-new-ausbildungsart"
|
||||
name="ausbildungsart"
|
||||
v-model="formDataPerson['ausbildungsart']"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-8 mb-3">
|
||||
<form-input
|
||||
label="Anmerkungen"
|
||||
type="textarea"
|
||||
id="stv-list-new-anmerkungen"
|
||||
name="anmerkungen"
|
||||
v-model="formDataPerson['anmerkungen']"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Studiengang*"
|
||||
type="select"
|
||||
id="stv-list-new-studiengang_kz"
|
||||
name="studiengang_kz"
|
||||
v-model="formDataStg"
|
||||
>
|
||||
<option v-for="stg in lists.active_stgs" :key="stg.studiengang_kz" :value="stg.studiengang_kz">{{stg.kuerzel}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Studiensemester*"
|
||||
type="select"
|
||||
id="stv-list-new-studiensemester_kurzbz"
|
||||
name="studiensemester_kurzbz"
|
||||
v-model="formDataSem"
|
||||
>
|
||||
<option v-for="sem in semester" :key="sem.studiensemester_kurzbz" :value="sem.studiensemester_kurzbz">{{sem.studiensemester_kurzbz}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Ausbildungssemester*"
|
||||
type="select"
|
||||
id="stv-list-new-ausbildungssemester"
|
||||
name="ausbildungssemester"
|
||||
v-model="formData['ausbildungssemester']"
|
||||
:disabled="formData['incoming']"
|
||||
@input="loadStudienplaene"
|
||||
>
|
||||
<option v-for="sem in Array.from({length:8}).map((u,i) => i+1)" :key="sem" :value="sem">{{sem}}. Semester</option>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="OrgForm"
|
||||
type="select"
|
||||
id="stv-list-new-orgform_kurzbz"
|
||||
name="orgform_kurzbz"
|
||||
v-model="formData['orgform_kurzbz']"
|
||||
@input="loadStudienplaene"
|
||||
>
|
||||
<option value="">-- keine Auswahl --</option>
|
||||
<option v-for="orgform in lists.orgforms" :key="orgform.orgform_kurzbz" :value="orgform.orgform_kurzbz">{{orgform.bezeichnung}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<form-input
|
||||
label="Studienplan"
|
||||
type="select"
|
||||
id="stv-list-new-studienplan_id"
|
||||
name="studienplan_id"
|
||||
v-model="formData['studienplan_id']"
|
||||
>
|
||||
<option value="">-- keine Auswahl --</option>
|
||||
<option v-for="plan in studienplaene" :key="plan.studienplan_id" :value="plan.studienplan_id">{{plan.bezeichnung}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-10 mb-3">
|
||||
<div class="form-check">
|
||||
<form-input
|
||||
label="Incoming"
|
||||
type="checkbox"
|
||||
id="stv-list-new-incoming"
|
||||
name="incoming"
|
||||
v-model="formData['incoming']"
|
||||
value="1"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<template #footer>
|
||||
<button v-if="person !== null" type="button" class="btn btn-secondary" @click="person = null"><i class="fa fa-chevron-left"></i>Zurück</button>
|
||||
<button type="submit" class="btn btn-primary">{{ person === null ? 'Person anlegen' : 'InteressentIn anlegen' }}</button>
|
||||
</template>
|
||||
</bs-modal>
|
||||
</fhc-form>`
|
||||
};
|
||||
@@ -0,0 +1,113 @@
|
||||
import {CoreRESTClient} from '../../../RESTClient.js';
|
||||
|
||||
export default {
|
||||
emits: [
|
||||
'changed'
|
||||
],
|
||||
props: {
|
||||
default: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
current: 0,
|
||||
loading: true,
|
||||
list: [],
|
||||
today: -1
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
set(n) {
|
||||
this.loading = true;
|
||||
let fallback = this.current;
|
||||
this.current = n;
|
||||
this.save(fallback);
|
||||
},
|
||||
next() {
|
||||
this.loading = true;
|
||||
let fallback = this.current;
|
||||
if (this.current++ >= this.list.length)
|
||||
this.current = this.list.length - 1;
|
||||
this.save(fallback);
|
||||
},
|
||||
prev() {
|
||||
this.loading = true;
|
||||
let fallback = this.current;
|
||||
if (this.current-- < 0)
|
||||
this.current = 0;
|
||||
this.save(fallback);
|
||||
},
|
||||
setToToday() {
|
||||
if (this.today > 0) {
|
||||
if (this.today == this.current)
|
||||
return;
|
||||
return this.set(this.today);
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
CoreRESTClient
|
||||
.get('components/stv/studiensemester/now')
|
||||
.then(result => CoreRESTClient.getData(result.data))
|
||||
.then(result => {
|
||||
this.today = this.list.indexOf(result);
|
||||
if (this.today >= 0) {
|
||||
if (this.today != this.current)
|
||||
this.set(this.today);
|
||||
} else {
|
||||
// TODO(chris): handle error (list might not be loaded yet)
|
||||
}
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
save(fallback) {
|
||||
CoreRESTClient
|
||||
.post('components/stv/studiensemester/set', {
|
||||
studiensemester: this.list[this.current]
|
||||
})
|
||||
.then(() => {
|
||||
this.loading = false;
|
||||
this.$emit('changed', this.list[this.current]);
|
||||
})
|
||||
.catch(error => {
|
||||
this.current = fallback;
|
||||
this.loading = false;
|
||||
this.$fhcAlert.handleFormValidation(error);
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
CoreRESTClient
|
||||
.get('components/stv/studiensemester')
|
||||
.then(result => CoreRESTClient.getData(result.data) || [])
|
||||
.then(result => {
|
||||
this.list = result.map(el => el.studiensemester_kurzbz);
|
||||
this.loading = false;
|
||||
this.current = this.list.indexOf(this.default);
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
template: `
|
||||
<div class="stv-studiensemester">
|
||||
<div class="btn-toolbar w-100 dropup" role="toolbar" aria-label="Studiensemester">
|
||||
<div class="btn-group flex-grow-1 position-static" role="group" aria-label="Studiensemester einstellen">
|
||||
<button type="button" class="btn btn-outline-secondary flex-grow-0" @click="prev"><i class="fa-solid fa-caret-left"></i></button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="dropdown" data-bs-display="static" data-bs-offset="0,0" aria-expanded="false">
|
||||
{{list[current]}}
|
||||
<i v-if="loading" class="fa-solid fa-spinner fa-spin"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-dark overflow-auto w-100" style="max-height:60vh">
|
||||
<li v-for="(item, index) in list" :key="index">
|
||||
<a class="dropdown-item" :class="{active: index == current}" :aria-current="index == current ? 'true' : 'false'" href="#" @click="set(index)">{{item}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<button type="button" class="btn btn-outline-secondary flex-grow-0" @click="next"><i class="fa-solid fa-caret-right"></i></button>
|
||||
</div>
|
||||
<div class="btn-group flex-grow-0" role="group" aria-label="aktuelles Studiensemester">
|
||||
<button type="button" class="btn btn-outline-secondary" @click="setToToday"><i class="fa-solid fa-bullseye"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
import {CoreRESTClient} from '../../../RESTClient.js';
|
||||
|
||||
|
||||
import PvTree from "../../../../../index.ci.php/public/js/components/primevue/tree/tree.esm.min.js";
|
||||
import PvTreetable from "../../../../../index.ci.php/public/js/components/primevue/treetable/treetable.esm.min.js";
|
||||
import PvColumn from "../../../../../index.ci.php/public/js/components/primevue/column/column.esm.min.js";
|
||||
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PvTree,
|
||||
PvTreetable,
|
||||
PvColumn
|
||||
},
|
||||
emits: [
|
||||
'selectVerband'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
nodes: [],
|
||||
selectedKey: [],
|
||||
filters: {}, // TODO(chris): filter only 1st level?
|
||||
favnodes: [],
|
||||
favorites: {on: false, list: []}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredNodes() {
|
||||
// TODO(chris): what to display actually?
|
||||
return this.favorites.on ? this.favnodes : this.nodes;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
findNodeByKey(key, arr) {
|
||||
if (!arr)
|
||||
arr = this.nodes;
|
||||
let res = arr.filter(n => n.key == key);
|
||||
if (res.length)
|
||||
return res.pop();
|
||||
res = arr.map(n => n.children ? this.findNodeByKey(key, n.children) : null).filter(a => a);
|
||||
if (res.length)
|
||||
return res.pop();
|
||||
return null;
|
||||
},
|
||||
onExpandTreeNode(node) {
|
||||
if (!node.children) {
|
||||
if (node.data.link) {
|
||||
let activeEl = null;
|
||||
this.$nextTick(() => {
|
||||
this.$nextTick(() => {
|
||||
activeEl = document.activeElement;
|
||||
});
|
||||
});
|
||||
this.loading = true;
|
||||
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/verband/' + node.data.link)
|
||||
.then(result => result.data)
|
||||
.then(result => {
|
||||
const subNodes = result.map(this.mapResultToTreeData);
|
||||
const realNode = this.findNodeByKey(node.key);
|
||||
if (realNode)
|
||||
realNode.children = subNodes;
|
||||
else
|
||||
node.children = subNodes; // NOTE(chris): fallback should never be the case
|
||||
|
||||
let treeitem = this.$refs.tree.$el.querySelector('[data-tree-item-key="' + node.key + '"]');
|
||||
treeitem = treeitem.closest('[role="row"]');
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (activeEl == document.activeElement)
|
||||
treeitem.dispatchEvent(new KeyboardEvent('keydown', {
|
||||
code: 'ArrowDown',
|
||||
key: 'ArrowDown'
|
||||
}));
|
||||
});
|
||||
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
}
|
||||
}
|
||||
},
|
||||
onSelectTreeNode(node) {
|
||||
if (node.data.link)
|
||||
this.$emit('selectVerband', {link: node.data.link, studiengang_kz: node.data.stg_kz});
|
||||
},
|
||||
mapResultToTreeData(el) {
|
||||
const cp = {
|
||||
key: ("" + el.link).replace('/', '-'),
|
||||
data: el,
|
||||
label: el.name
|
||||
};
|
||||
|
||||
if (el.children)
|
||||
cp.children = el.children.map(this.mapResultToTreeData);
|
||||
else
|
||||
cp.leaf = el.leaf || false;
|
||||
|
||||
return cp;
|
||||
},
|
||||
async filterFav() {
|
||||
if (!this.favorites.on && !this.favnodes.length && this.favorites.list.length) {
|
||||
this.loading = true;
|
||||
this.favnodes = await this.loadNodes(this.favorites.list);
|
||||
}
|
||||
this.favorites.on = !this.favorites.on;
|
||||
this.$fhcApi
|
||||
.factory.stv.verband.favorites.set(JSON.stringify(this.favorites));
|
||||
this.loading = false;
|
||||
},
|
||||
async loadNodes(links) {
|
||||
let sortedInParents = links.reduce((o, link) => {
|
||||
link = link + '';
|
||||
let parent,
|
||||
parts = link.split('/');
|
||||
if (parts.length == 1) {
|
||||
parent = '_';
|
||||
} else {
|
||||
parts.pop();
|
||||
parent = parts.join('/');
|
||||
}
|
||||
if (!o[parent])
|
||||
o[parent] = [link];
|
||||
else
|
||||
o[parent].push(link);
|
||||
return o;
|
||||
}, {});
|
||||
|
||||
let promises = [];
|
||||
for (let parent in sortedInParents)
|
||||
promises.push(
|
||||
this.$fhcApi
|
||||
.get('api/frontend/v1/stv/verband/' + (parent == '_' ? '' : parent))
|
||||
.then(res => res.data)
|
||||
.then(res => res.filter(node => sortedInParents[parent].includes(node.link + '')))
|
||||
);
|
||||
|
||||
// NOTE(chris): merge the resulting arrays and transform them to an associative one
|
||||
let result = [].concat.apply([], await Promise.all(promises)).reduce((o, node) => {
|
||||
o[node.link + ''] = this.mapResultToTreeData({...node, leaf: true, children: undefined});
|
||||
return o;
|
||||
}, {});
|
||||
|
||||
return links.map(link => result[link]);
|
||||
},
|
||||
async markFav(key) {
|
||||
let index = this.favorites.list.indexOf(key.data.link + '');
|
||||
|
||||
if (index != -1) {
|
||||
if (this.favnodes.length)
|
||||
this.favnodes = this.favnodes.filter(node => node.data.link != key.data.link);
|
||||
this.favorites.list.splice(index, 1);
|
||||
} else {
|
||||
if (this.favnodes.length || this.favorites.on)
|
||||
this.favnodes.push((await this.loadNodes([key.data.link])).pop());
|
||||
this.favorites.list.push(key.data.link + '');
|
||||
}
|
||||
|
||||
this.$fhcApi
|
||||
.factory.stv.verband.favorites.set(JSON.stringify(this.favorites));
|
||||
},
|
||||
unsetFavFocus(e) {
|
||||
if (e.target.dataset?.linkFavAdd !== undefined) {
|
||||
e.target.tabIndex = -1;
|
||||
} else {
|
||||
let items = e.target.querySelectorAll('[data-link-fav-add]:not([tabindex="-1"])');
|
||||
items.forEach(el => el.tabIndex = document.activeElement == el ? 0 : -1);
|
||||
}
|
||||
},
|
||||
setFavFocus(e) {
|
||||
if (e.target.dataset?.linkFavAdd !== undefined) {
|
||||
e.target.tabIndex = 0;
|
||||
} else {
|
||||
let items = e.target.querySelectorAll('[data-link-fav-add][tabindex="-1"]');
|
||||
items.forEach(el => el.tabIndex = 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$fhcApi
|
||||
.factory.stv.verband.get()
|
||||
.then(result => {
|
||||
this.nodes = result.data.map(this.mapResultToTreeData);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
|
||||
this.$fhcApi
|
||||
.factory.stv.verband.favorites.get()
|
||||
.then(result => {
|
||||
if (result.data) {
|
||||
let f = JSON.parse(result.data);
|
||||
if (f.on) {
|
||||
this.loading = true;
|
||||
this.favorites = f;
|
||||
this.loadNodes(this.favorites.list).then(res => {
|
||||
this.favnodes = res;
|
||||
this.loading = false;
|
||||
});
|
||||
} else
|
||||
this.favorites = f;
|
||||
}
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
template: `
|
||||
<div class="overflow-auto" tabindex="-1">
|
||||
<pv-treetable
|
||||
ref="tree"
|
||||
class="stv-verband p-treetable-sm"
|
||||
:value="filteredNodes"
|
||||
@node-expand="onExpandTreeNode"
|
||||
selection-mode="single"
|
||||
v-model:selection-keys="selectedKey"
|
||||
@node-select="onSelectTreeNode"
|
||||
scrollable
|
||||
scroll-height="flex"
|
||||
@focusin="setFavFocus"
|
||||
@focusout="unsetFavFocus"
|
||||
:filters="filters"
|
||||
>
|
||||
<pv-column field="name" expander>
|
||||
<template #header>
|
||||
<div class="text-right">
|
||||
<div class="p-input-icon-left">
|
||||
<i class="pi pi-search"></i>
|
||||
<input type="text" v-model="filters['global']" class="form-control ps-5" placeholder="Search" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #body="{node}">
|
||||
<span :data-tree-item-key="node.key" :title="node.data.studiengang_kz">
|
||||
{{node.data.name}}
|
||||
</span>
|
||||
</template>
|
||||
</pv-column>
|
||||
<pv-column field="fav" headerStyle="flex: 0 0 auto" style="flex: 0 0 auto">
|
||||
<template #header>
|
||||
<a href="#" @click.prevent="filterFav"><i :class="favorites.on ? 'fa-solid' : 'fa-regular'" class="fa-star"></i></a>
|
||||
</template>
|
||||
<template #body="{node, column}">
|
||||
<a
|
||||
href="#"
|
||||
@click.prevent="markFav(node)"
|
||||
@keydown.enter.stop.prevent="markFav(node)"
|
||||
tabindex="-1"
|
||||
data-link-fav-add
|
||||
>
|
||||
<i :class="favorites.list.includes(node.data.link + '') ? 'fa-solid' : 'fa-regular'" class="fa-star"></i>
|
||||
</a>
|
||||
</template>
|
||||
</pv-column>
|
||||
<pv-column field="studiengang_kz" class="d-none"></pv-column>
|
||||
</pv-treetable>
|
||||
</div>`
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
import {CoreRESTClient} from '../RESTClient.js';
|
||||
import accessibility from "../directives/accessibility.js";
|
||||
|
||||
export default {
|
||||
@@ -12,7 +11,7 @@ export default {
|
||||
],
|
||||
props: {
|
||||
config: {
|
||||
type: [String, Object],
|
||||
type: [String, Array, Object, Promise],
|
||||
required: true
|
||||
},
|
||||
default: String,
|
||||
@@ -56,40 +55,37 @@ export default {
|
||||
initConfig(config) {
|
||||
if (!config)
|
||||
return;
|
||||
if (config instanceof Promise)
|
||||
return config
|
||||
.then(result => result.data)
|
||||
.then(this.initConfig)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
if (typeof config === 'string' || config instanceof String)
|
||||
return CoreRESTClient.get(config)
|
||||
.then(result => CoreRESTClient.getData(result.data))
|
||||
return this.$fhcApi
|
||||
.get(config)
|
||||
.then(result => result.data)
|
||||
.then(this.initConfig)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
|
||||
const tabs = {};
|
||||
|
||||
if (Array.isArray(config)) {
|
||||
config.forEach((item, key) => {
|
||||
if (!item.component)
|
||||
return console.error('Component missing for ' + key);
|
||||
function _addToTabs(key, item) {
|
||||
if (!item.component)
|
||||
return console.error('Component missing for ' + key);
|
||||
|
||||
tabs[key] = {
|
||||
component: Vue.markRaw(Vue.defineAsyncComponent(() => import(item.component))),
|
||||
title: item.title || key,
|
||||
config: item.config,
|
||||
key
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Object.entries(config).forEach(([key, item]) => {
|
||||
if (!item.component)
|
||||
return console.error('Component missing for ' + key);
|
||||
|
||||
tabs[key] = {
|
||||
component: Vue.markRaw(Vue.defineAsyncComponent(() => import(item.component))),
|
||||
title: item.title || key,
|
||||
config: item.config,
|
||||
key
|
||||
}
|
||||
});
|
||||
tabs[key] = {
|
||||
component: Vue.markRaw(Vue.defineAsyncComponent(() => import(item.component))),
|
||||
title: Vue.computed(() => item.title || key),
|
||||
config: item.config,
|
||||
key
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(config))
|
||||
config.forEach((item, key) => _addToTabs(key, item));
|
||||
else
|
||||
Object.entries(config).forEach(([key, item]) => _addToTabs(key, item));
|
||||
|
||||
if (this.current === null || !tabs[this.current]) {
|
||||
if (tabs[this.default])
|
||||
this.current = this.default;
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
import { CoreFetchCmpt } from '../Fetch.js';
|
||||
import FormInput from '../Form/Input.js';
|
||||
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFetchCmpt,
|
||||
FormInput
|
||||
},
|
||||
emits: [
|
||||
'update:modelValue',
|
||||
'load'
|
||||
],
|
||||
props: {
|
||||
// CodeIgniter model (eg: crm/prestudent)
|
||||
ciModel: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
// Primarykey(s) of the record (eg: {prestudent_id: 12345})
|
||||
pk: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
// The values as associative array
|
||||
modelValue: Object,
|
||||
// Show only fields with a name that exists in the filter
|
||||
filter: [String, Array]
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fields: [],
|
||||
backupModelValue: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
filterArray() {
|
||||
if (!this.filter || Array.isArray(this.filter))
|
||||
return this.filter;
|
||||
return [this.filter];
|
||||
},
|
||||
filteredFields() {
|
||||
if (!this.filterArray)
|
||||
return this.fields;
|
||||
return this.fields.filter(el => this.filterArray.includes(el.name));
|
||||
},
|
||||
filteredValues() {
|
||||
return this.filteredFields.reduce((r,e) => (r[e.name] = this.internModelValue[e.name], r), {});
|
||||
},
|
||||
internModelValue: {
|
||||
get() {
|
||||
return this.modelValue || this.backupModelValue;
|
||||
},
|
||||
set(value) {
|
||||
this.backupModelValue = value;
|
||||
this.$emit('update:modelValue', value);
|
||||
this.originalValues
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
pk(n, o) {
|
||||
if (!this.$refs.fetch)
|
||||
return; // NOTE(chris): no initial load yet
|
||||
|
||||
if (Object.keys(o).length == Object.keys(n).length
|
||||
&& Object.keys(o).every(key => n.hasOwnProperty(key) && o[key] === n[key]))
|
||||
return; // NOTE(chris): old and new are the same
|
||||
|
||||
this.$nextTick(this.$refs.fetch.fetchData);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadF(params) {
|
||||
// TODO(chris): move to fhcapi.factory
|
||||
return this.$fhcApi
|
||||
.post('/api/frontend/v1/udf/load/' + params.ciModel, params.pk);
|
||||
},
|
||||
init(result) {
|
||||
const fields = result.map(el => {
|
||||
switch (el.type) {
|
||||
case 'textfield':
|
||||
el.type = 'text';
|
||||
break;
|
||||
case 'date':
|
||||
el.type = 'Datepicker';
|
||||
el.clearable = el.hasOwnProperty('clearable') ? el.clearable : false
|
||||
el.autoApply = el.hasOwnProperty('autoApply') ? el.autoApply : true
|
||||
el.enableTimePicker = el.hasOwnProperty('enableTimePicker') ? el.enableTimePicker : false
|
||||
el.format = el.hasOwnProperty('format') ? el.format : "dd.MM.yyyy"
|
||||
el.previewFormat = el.hasOwnProperty('previewFormat') ? el.previewFormat : "dd.MM.yyyy"
|
||||
el.teleport = el.hasOwnProperty('teleport') ? el.teleport : true
|
||||
break;
|
||||
case 'multipledropdown':
|
||||
el.multiple = true;
|
||||
case 'dropdown':
|
||||
el.type = 'select';
|
||||
el.options = el.options.map(item => {
|
||||
if (Array.isArray(item))
|
||||
return {
|
||||
id: item[0],
|
||||
description: item[1]
|
||||
};
|
||||
if (typeof item === 'object')
|
||||
return item;
|
||||
return {
|
||||
id: item,
|
||||
description: item
|
||||
};
|
||||
});
|
||||
break;
|
||||
}
|
||||
return el;
|
||||
});
|
||||
const values = fields.reduce((a,c) => {
|
||||
a[c.name] = c.value;
|
||||
return a;
|
||||
}, {});
|
||||
|
||||
this.internModelValue = {...this.internModelValue, ...values};
|
||||
this.fields = fields;
|
||||
this.$emit('load', values);
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="core-udf row">
|
||||
<core-fetch-cmpt
|
||||
ref="fetch"
|
||||
:api-function="loadF"
|
||||
:api-function-parameters="{ ciModel, pk }"
|
||||
@data-fetched="init"
|
||||
>
|
||||
<template #default>
|
||||
<div v-for="field in filteredFields" :key="field.name" class="col" :class="field.type == 'checkbox' ? 'pt-4 d-flex align-items-center' : ''">
|
||||
<form-input
|
||||
v-model="internModelValue[field.name]"
|
||||
:name="field.name"
|
||||
:label="field.title"
|
||||
:type="field.type"
|
||||
:multiple="field.multiple"
|
||||
:title="field.description"
|
||||
:disabled="field.disabled"
|
||||
:clearable="field.clearable"
|
||||
:auto-apply="field.autoApply"
|
||||
:enable-time-picker="field.enableTimePicker"
|
||||
:format="field.format"
|
||||
:preview-format="field.previewFormat"
|
||||
:teleport="field.teleport"
|
||||
>
|
||||
<option v-for="value in field.options" :key="value.id" :value="value.id">{{value.description}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
</template>
|
||||
</core-fetch-cmpt>
|
||||
</div>`
|
||||
}
|
||||
@@ -17,11 +17,11 @@
|
||||
|
||||
import {CoreRESTClient} from '../../RESTClient.js';
|
||||
|
||||
//
|
||||
//
|
||||
const CORE_FILTER_CMPT_TIMEOUT = 7000;
|
||||
|
||||
/**
|
||||
*
|
||||
* TODO(chris): deprecated
|
||||
*/
|
||||
export const CoreFilterAPIs = {
|
||||
/**
|
||||
|
||||
@@ -15,12 +15,11 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {CoreFilterAPIs} from './API.js';
|
||||
import {CoreRESTClient} from '../../RESTClient.js';
|
||||
import {CoreFetchCmpt} from '../../components/Fetch.js';
|
||||
import FilterConfig from './Filter/Config.js';
|
||||
import FilterColumns from './Filter/Columns.js';
|
||||
import TableDownload from './Table/Download.js';
|
||||
import collapseAutoClose from '../../directives/collapseAutoClose.js';
|
||||
|
||||
//
|
||||
const FILTER_COMPONENT_NEW_FILTER = 'Filter Component New Filter';
|
||||
@@ -38,6 +37,9 @@ export const CoreFilterCmpt = {
|
||||
FilterColumns,
|
||||
TableDownload
|
||||
},
|
||||
directives: {
|
||||
collapseAutoClose
|
||||
},
|
||||
emits: [
|
||||
'nwNewEntry',
|
||||
'click:new'
|
||||
@@ -50,8 +52,7 @@ export const CoreFilterCmpt = {
|
||||
default: true
|
||||
},
|
||||
filterType: {
|
||||
type: String,
|
||||
required: true
|
||||
type: String
|
||||
},
|
||||
tabulatorOptions: Object,
|
||||
tabulatorEvents: Array,
|
||||
@@ -64,7 +65,11 @@ export const CoreFilterCmpt = {
|
||||
newBtnShow: Boolean,
|
||||
newBtnClass: [String, Array, Object],
|
||||
newBtnDisabled: Boolean,
|
||||
newBtnLabel: String
|
||||
newBtnLabel: String,
|
||||
uniqueId: String,
|
||||
// TODO soll im master kommen?
|
||||
idField: String,
|
||||
parentIdField: String
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
@@ -209,8 +214,15 @@ export const CoreFilterCmpt = {
|
||||
tabulatorOptions.columns = this.filteredColumns;
|
||||
}
|
||||
|
||||
if (tabulatorOptions.columns && tabulatorOptions.columns.filter(el => el.formatter == 'rowSelection').length)
|
||||
if (tabulatorOptions.selectable || (tabulatorOptions.columns && tabulatorOptions.columns.filter(el => el.formatter == 'rowSelection').length))
|
||||
this.tabulatorHasSelector = true;
|
||||
// TODO check ob im core bleiben soll
|
||||
if (this.idField) {
|
||||
// enable nested tabulator if parent Id given
|
||||
if (this.parentIdField) tabulatorOptions.dataTree = true;
|
||||
// set tabulator index
|
||||
tabulatorOptions.index = this.idField;
|
||||
}
|
||||
|
||||
// Start the tabulator with the build options
|
||||
this.tabulator = new Tabulator(
|
||||
@@ -228,6 +240,33 @@ export const CoreFilterCmpt = {
|
||||
this.tabulator.on("rowSelectionChanged", data => {
|
||||
this.selectedData = data;
|
||||
});
|
||||
// TODO check ob im core so bleiben soll
|
||||
// if nested tabulator, restructure data
|
||||
if (this.parentIdField && this.idField) {
|
||||
this.tabulator.on("dataLoading", data => {
|
||||
let toDelete = [];
|
||||
|
||||
// loop through all data
|
||||
for (let childIdx = 0; childIdx < data.length; childIdx++)
|
||||
{
|
||||
let child = data[childIdx];
|
||||
|
||||
// if it has parent id, it is a child
|
||||
if (child[this.parentIdField])
|
||||
{
|
||||
// append the child on the right place. If parent found, mark original sw child on 0 level for deleting
|
||||
if (this.appendChild(data, child)) toDelete.push(childIdx);
|
||||
}
|
||||
}
|
||||
|
||||
// delete the marked children from 0 level
|
||||
for (let counter = 0; counter < toDelete.length; counter++)
|
||||
{
|
||||
// decrease index by counter as index of data array changes after every deletion
|
||||
data.splice(toDelete[counter] - counter, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.tableOnly) {
|
||||
this.tabulator.on('tableBuilt', () => {
|
||||
const cols = this.tabulator.getColumns();
|
||||
@@ -245,19 +284,19 @@ export const CoreFilterCmpt = {
|
||||
}
|
||||
},
|
||||
_updateTabulator() {
|
||||
this.tabulatorHasSelector = this.filteredColumns.filter(el => el.formatter == 'rowSelection').length;
|
||||
this.tabulatorHasSelector = this.tabulatorOptions.selectable || this.filteredColumns.filter(el => el.formatter == 'rowSelection').length;
|
||||
this.tabulator.setColumns(this.filteredColumns);
|
||||
this.tabulator.setData(this.filteredData);
|
||||
},
|
||||
/**
|
||||
*
|
||||
*/
|
||||
getFilter: function() {
|
||||
getFilter() {
|
||||
if (this.selectedFilter === null)
|
||||
this.startFetchCmpt(CoreFilterAPIs.getFilter, null, this.render);
|
||||
this.startFetchCmpt(this.$fhcApi.factory.filter.getFilter, null, this.render);
|
||||
else
|
||||
this.startFetchCmpt(
|
||||
CoreFilterAPIs.getFilterById,
|
||||
this.$fhcApi.factory.filter.getFilterById,
|
||||
{
|
||||
filterId: this.selectedFilter
|
||||
},
|
||||
@@ -267,55 +306,47 @@ export const CoreFilterCmpt = {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
render: function(response) {
|
||||
render(response) {
|
||||
let data = response;
|
||||
this.filterName = data.filterName;
|
||||
this.dataset = data.dataset;
|
||||
this.datasetMetadata = data.datasetMetadata;
|
||||
|
||||
if (CoreRESTClient.hasData(response))
|
||||
this.fields = data.fields;
|
||||
this.selectedFields = data.selectedFields;
|
||||
this.notSelectedFields = this.fields.filter(x => this.selectedFields.indexOf(x) === -1);
|
||||
this.filterFields = [];
|
||||
|
||||
for (let i = 0; i < data.datasetMetadata.length; i++)
|
||||
{
|
||||
let data = CoreRESTClient.getData(response);
|
||||
this.filterName = data.filterName;
|
||||
this.dataset = data.dataset;
|
||||
this.datasetMetadata = data.datasetMetadata;
|
||||
|
||||
this.fields = data.fields;
|
||||
this.selectedFields = data.selectedFields;
|
||||
this.notSelectedFields = this.fields.filter(x => this.selectedFields.indexOf(x) === -1);
|
||||
this.filterFields = [];
|
||||
|
||||
for (let i = 0; i < data.datasetMetadata.length; i++)
|
||||
for (let j = 0; j < data.filters.length; j++)
|
||||
{
|
||||
for (let j = 0; j < data.filters.length; j++)
|
||||
if (data.datasetMetadata[i].name == data.filters[j].name)
|
||||
{
|
||||
if (data.datasetMetadata[i].name == data.filters[j].name)
|
||||
{
|
||||
let filter = data.filters[j];
|
||||
filter.type = data.datasetMetadata[i].type;
|
||||
let filter = data.filters[j];
|
||||
filter.type = data.datasetMetadata[i].type;
|
||||
|
||||
this.filterFields.push(filter);
|
||||
//break;
|
||||
}
|
||||
this.filterFields.push(filter);
|
||||
//break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the side menu is active
|
||||
if (this.sideMenu === true)
|
||||
{
|
||||
this.setSideMenu(data);
|
||||
}
|
||||
else // otherwise use the dropdown in the filter options
|
||||
{
|
||||
this.setDropDownMenu(data);
|
||||
}
|
||||
this.updateTabulator();
|
||||
}
|
||||
else
|
||||
// If the side menu is active
|
||||
if (this.sideMenu === true)
|
||||
{
|
||||
console.error(CoreRESTClient.getError(response));
|
||||
this.setSideMenu(data);
|
||||
}
|
||||
else // otherwise use the dropdown in the filter options
|
||||
{
|
||||
this.setDropDownMenu(data);
|
||||
}
|
||||
this.updateTabulator();
|
||||
},
|
||||
/**
|
||||
* Set the menu
|
||||
*/
|
||||
setSideMenu: function(data) {
|
||||
setSideMenu(data) {
|
||||
let filters = data.sideMenu.filters;
|
||||
let personalFilters = data.sideMenu.personalFilters;
|
||||
let filtersArray = [];
|
||||
@@ -369,7 +400,7 @@ export const CoreFilterCmpt = {
|
||||
/**
|
||||
* Set the drop down menu
|
||||
*/
|
||||
setDropDownMenu: function(data) {
|
||||
setDropDownMenu(data) {
|
||||
let filters = data.sideMenu.filters;
|
||||
let personalFilters = data.sideMenu.personalFilters;
|
||||
let filtersArray = [];
|
||||
@@ -405,7 +436,7 @@ export const CoreFilterCmpt = {
|
||||
/**
|
||||
* Used to start/refresh the FetchCmpt
|
||||
*/
|
||||
startFetchCmpt: function(apiFunction, apiFunctionParameters, dataFetchedCallback) {
|
||||
startFetchCmpt(apiFunction, apiFunctionParameters, dataFetchedCallback) {
|
||||
// Assign the function api of the FetchCmpt binded property
|
||||
this.fetchCmptApiFunction = apiFunction;
|
||||
|
||||
@@ -416,6 +447,9 @@ export const CoreFilterCmpt = {
|
||||
apiFunctionParameters.filterUniqueId = FHC_JS_DATA_STORAGE_OBJECT.called_path + "/" + FHC_JS_DATA_STORAGE_OBJECT.called_method;
|
||||
apiFunctionParameters.filterType = this.filterType;
|
||||
|
||||
if (this.uniqueId)
|
||||
apiFunctionParameters.filterUniqueId += '_' + this.uniqueId;
|
||||
|
||||
// Assign parameters to the FetchCmpt binded properties
|
||||
this.fetchCmptApiFunctionParams = apiFunctionParameters;
|
||||
// Assign data fetch callback to the FetchCmpt binded properties
|
||||
@@ -431,11 +465,11 @@ export const CoreFilterCmpt = {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
handlerSaveCustomFilter: function(customFilterName) {
|
||||
handlerSaveCustomFilter(customFilterName) {
|
||||
this.selectedFilter = null;
|
||||
//
|
||||
this.startFetchCmpt(
|
||||
CoreFilterAPIs.saveCustomFilter,
|
||||
this.$fhcApi.factory.filter.saveCustomFilter,
|
||||
{
|
||||
customFilterName
|
||||
},
|
||||
@@ -445,13 +479,13 @@ export const CoreFilterCmpt = {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
handlerRemoveCustomFilter: function(event) {
|
||||
handlerRemoveCustomFilter(event) {
|
||||
let filterId = event.currentTarget.getAttribute("href").substring(1);
|
||||
if (filterId === this.selectedFilter)
|
||||
this.selectedFilter = null;
|
||||
//
|
||||
this.startFetchCmpt(
|
||||
CoreFilterAPIs.removeCustomFilter,
|
||||
this.$fhcApi.factory.filter.removeCustomFilter,
|
||||
{
|
||||
filterId: filterId
|
||||
},
|
||||
@@ -488,12 +522,42 @@ export const CoreFilterCmpt = {
|
||||
applyFilterConfig(filterFields) {
|
||||
this.selectedFilter = null;
|
||||
this.startFetchCmpt(
|
||||
CoreFilterAPIs.applyFilterFields,
|
||||
this.$fhcApi.factory.filter.applyFilterFields,
|
||||
{
|
||||
filterFields
|
||||
},
|
||||
this.getFilter
|
||||
);
|
||||
},
|
||||
// TODO check ob im core so bleiben soll
|
||||
// append child to it's parent
|
||||
appendChild(data, child) {
|
||||
// get parent id
|
||||
let parentId = child[this.parentIdField];
|
||||
|
||||
// loop thorugh all data
|
||||
for (let parentIdx = 0; parentIdx < data.length; parentIdx++)
|
||||
{
|
||||
let parent = data[parentIdx];
|
||||
|
||||
// if it's the parent
|
||||
if (parent[this.idField] == parentId)
|
||||
{
|
||||
// create children array if not done yet
|
||||
if (!parent._children) parent._children = [];
|
||||
|
||||
// if child is not included in children array, append the child
|
||||
if (!parent._children.includes(child)) parent._children.push(child);
|
||||
|
||||
// parent found
|
||||
return true;
|
||||
}
|
||||
// search children for parents
|
||||
else if (parent._children) this.appendChild(parent._children, child);
|
||||
}
|
||||
|
||||
// parent not found
|
||||
return false;
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
@@ -522,7 +586,7 @@ export const CoreFilterCmpt = {
|
||||
|
||||
<div class="row" v-if="title != null && title != ''">
|
||||
<div class="col-lg-12">
|
||||
<h3 class="page-header">
|
||||
<h3 class="page-header mt-1 mb-4">
|
||||
{{ title }}
|
||||
</h3>
|
||||
</div>
|
||||
@@ -531,7 +595,7 @@ export const CoreFilterCmpt = {
|
||||
<div :id="'filterCollapsables' + idExtra">
|
||||
|
||||
<div class="d-flex flex-row justify-content-between flex-wrap">
|
||||
<div v-if="newBtnShow || reload || $slots.actions" class="d-flex gap-2 align-items-baseline flex-wrap">
|
||||
<div v-if="newBtnShow || reload || $slots.search || $slots.actions" class="d-flex gap-2 align-items-baseline flex-wrap">
|
||||
<button v-if="newBtnShow" class="btn btn-primary" :class="newBtnClass" :title="newBtnLabel ? undefined : 'New'" :aria-label="newBtnLabel ? undefined : 'New'" @click="$emit('click:new', $event)" :disabled="newBtnDisabled">
|
||||
<span class="fa-solid fa-plus" aria-hidden="true"></span>
|
||||
{{ newBtnLabel }}
|
||||
@@ -539,12 +603,13 @@ export const CoreFilterCmpt = {
|
||||
<button v-if="reload" class="btn btn-outline-secondary" aria-label="Reload" @click="reloadTable">
|
||||
<span class="fa-solid fa-rotate-right" aria-hidden="true"></span>
|
||||
</button>
|
||||
<span v-if="$slots.actions && tabulatorHasSelector">Mit {{selectedData.length}} ausgewählten: </span>
|
||||
<slot name="actions" v-bind="tabulatorHasSelector ? selectedData : []"></slot>
|
||||
<span v-if="$slots.actions && tabulatorHasSelector">Mit {{selectedData.length}} ausgewählten:</span>
|
||||
<slot name="actions" v-bind="{selected: tabulatorHasSelector ? selectedData : []}"></slot>
|
||||
<slot name="search"></slot>
|
||||
</div>
|
||||
<div class="d-flex gap-1 align-items-baseline flex-grow-1 justify-content-end">
|
||||
<span v-if="!tableOnly">[ {{ filterName }} ]</span>
|
||||
<a v-if="!tableOnly" href="#" class="btn btn-link px-0 text-dark" data-bs-toggle="collapse" :data-bs-target="'#collapseFilters' + idExtra">
|
||||
<a v-if="!tableOnly || $slots.filter" href="#" class="btn btn-link px-0 text-dark" data-bs-toggle="collapse" :data-bs-target="'#collapseFilters' + idExtra">
|
||||
<span class="fa-solid fa-xl fa-filter"></span>
|
||||
</a>
|
||||
<a href="#" class="btn btn-link px-0 text-dark" data-bs-toggle="collapse" :data-bs-target="'#collapseColumns' + idExtra">
|
||||
@@ -563,6 +628,7 @@ export const CoreFilterCmpt = {
|
||||
:names="fieldNames"
|
||||
@hide="tabulator.hideColumn($event)"
|
||||
@show="tabulator.showColumn($event)"
|
||||
v-collapse-auto-close
|
||||
></filter-columns>
|
||||
|
||||
<filter-config
|
||||
@@ -576,7 +642,17 @@ export const CoreFilterCmpt = {
|
||||
@switch-filter="switchFilter"
|
||||
@apply-filter-config="applyFilterConfig"
|
||||
@save-custom-filter="handlerSaveCustomFilter"
|
||||
v-collapse-auto-close
|
||||
></filter-config>
|
||||
<div
|
||||
v-else-if="$slots.filter"
|
||||
:id="'collapseFilters' + idExtra"
|
||||
class="card-body collapse"
|
||||
:data-bs-parent="'#filterCollapsables' + idExtra"
|
||||
v-collapse-auto-close
|
||||
>
|
||||
<slot name="filter"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabulator -->
|
||||
|
||||
@@ -21,7 +21,7 @@ import {CoreRESTClient} from '../../RESTClient.js';
|
||||
const CORE_NAVIGATION_CMPT_TIMEOUT = 5000;
|
||||
|
||||
/**
|
||||
*
|
||||
* TODO(chris): deprecated
|
||||
*/
|
||||
export const CoreNavigationAPIs = {
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (C) 2022 fhcomplete.org
|
||||
* Copyright (C) 2024 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
|
||||
@@ -15,8 +15,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {CoreNavigationAPIs} from './API.js';
|
||||
import {CoreRESTClient} from '../../RESTClient.js';
|
||||
import {CoreFetchCmpt} from '../../components/Fetch.js';
|
||||
|
||||
/**
|
||||
@@ -30,12 +28,12 @@ export const CoreNavigationCmpt = {
|
||||
addHeaderMenuEntries: Object, // property used to add new header menu entries from another app/component
|
||||
addSideMenuEntries: Object, // property used to add new side menu entries from another app/component
|
||||
hideTopMenu: Boolean,
|
||||
leftNavCssClasses: {
|
||||
type: String,
|
||||
default: 'navbar navbar-left-side'
|
||||
}
|
||||
leftNavCssClasses: {
|
||||
type: String,
|
||||
default: 'navbar navbar-left-side'
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
data() {
|
||||
return {
|
||||
headerMenu: {}, // header menu entries
|
||||
sideMenu: {} // side menu entries
|
||||
@@ -45,61 +43,63 @@ export const CoreNavigationCmpt = {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
headerMenuEntries: function() {
|
||||
headerMenuEntries() {
|
||||
//
|
||||
let hm = this.headerMenu ? {...this.headerMenu} : {};
|
||||
if (this.headerMenu != null && this.addHeaderMenuEntries != null && Object.keys(this.addHeaderMenuEntries).length > 0)
|
||||
{
|
||||
this.headerMenu[this.addHeaderMenuEntries.description] = this.addHeaderMenuEntries;
|
||||
hm[this.addHeaderMenuEntries.description] = this.addHeaderMenuEntries;
|
||||
}
|
||||
return this.headerMenu;
|
||||
return hm;
|
||||
},
|
||||
/**
|
||||
*
|
||||
*/
|
||||
sideMenuEntries: function() {
|
||||
sideMenuEntries() {
|
||||
//
|
||||
let sm = this.sideMenu ? {...this.sideMenu} : {};
|
||||
if (this.sideMenu != null && this.addSideMenuEntries != null && Object.keys(this.addSideMenuEntries).length > 0)
|
||||
{
|
||||
this.sideMenu[this.addSideMenuEntries.description] = this.addSideMenuEntries;
|
||||
sm[this.addSideMenuEntries.description] = this.addSideMenuEntries;
|
||||
}
|
||||
return this.sideMenu;
|
||||
return sm;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
getNavigationPage: function() {
|
||||
getNavigationPage() {
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.called_path + "/" + FHC_JS_DATA_STORAGE_OBJECT.called_method;
|
||||
},
|
||||
/**
|
||||
*
|
||||
*/
|
||||
fetchCmptApiFunctionHeader: function() {
|
||||
return CoreNavigationAPIs.getHeader(this.getNavigationPage());
|
||||
fetchCmptApiFunctionHeader() {
|
||||
return this.$fhcApi.factory.navigation.getHeader(this.getNavigationPage());
|
||||
},
|
||||
/**
|
||||
*
|
||||
*/
|
||||
fetchCmptApiFunctionSideMenu: function() {
|
||||
return CoreNavigationAPIs.getMenu(this.getNavigationPage());
|
||||
fetchCmptApiFunctionSideMenu() {
|
||||
return this.$fhcApi.factory.navigation.getMenu(this.getNavigationPage());
|
||||
},
|
||||
/**
|
||||
*
|
||||
*/
|
||||
fetchCmptDataFetchedHeader: function(data) {
|
||||
if (CoreRESTClient.hasData(data)) this.headerMenu = CoreRESTClient.getData(data);
|
||||
fetchCmptDataFetchedHeader(data) {
|
||||
this.headerMenu = data || {};
|
||||
},
|
||||
/**
|
||||
*
|
||||
*/
|
||||
fetchCmptDataFetchedMenu: function(data) {
|
||||
if (CoreRESTClient.hasData(data)) this.sideMenu = CoreRESTClient.getData(data);
|
||||
fetchCmptDataFetchedMenu(data) {
|
||||
this.sideMenu = data || {};
|
||||
},
|
||||
/**
|
||||
*
|
||||
*/
|
||||
getDataBsToggle: function(header) {
|
||||
getDataBsToggle(header) {
|
||||
return !header.children ? null : 'dropdown';
|
||||
}
|
||||
},
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import action from "./action.js";
|
||||
import actions from "./actions.js";
|
||||
|
||||
export default {
|
||||
props: [ "res", "actions" ],
|
||||
components: {
|
||||
action: action,
|
||||
actions: actions
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
template: `
|
||||
<div class="searchbar_result searchbar_prestudent">
|
||||
|
||||
<div class="searchbar_grid">
|
||||
<div class="searchbar_icon">
|
||||
<action :res="this.res" :action="this.actions.defaultaction" @actionexecuted="$emit('actionexecuted')">
|
||||
<img v-if="(typeof res.foto !== 'undefined') && (res.foto !== null)"
|
||||
:src="'data:image/jpeg;base64,' + res.foto"
|
||||
class="rounded-circle" height="100"/>
|
||||
<i v-else class="fas fa-user-circle fa-5x"></i>
|
||||
</action>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_data">
|
||||
<action :res="this.res" :action="this.actions.defaultaction" @actionexecuted="$emit('actionexecuted')">
|
||||
<span class="fw-bold">{{ res.name }}</span>
|
||||
</action>
|
||||
|
||||
<div class="mb-3"></div>
|
||||
|
||||
<div class="searchbar_table">
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Prestudent_id</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.prestudent_id }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Student_uid</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.uid }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Person_id</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.person_id }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Studiengang</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.bezeichnung }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">EMail</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<a :href="this.mailtourl">
|
||||
{{ res.email }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<actions :res="this.res" :actions="this.actions.childactions"
|
||||
@actionexecuted="$emit('actionexecuted')"></actions>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`,
|
||||
methods: {
|
||||
},
|
||||
computed: {
|
||||
mailtourl: function() {
|
||||
return 'mailto:' + this.res.email;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -2,6 +2,8 @@ import person from "./person.js";
|
||||
import raum from "./raum.js";
|
||||
import employee from "./employee.js";
|
||||
import organisationunit from "./organisationunit.js";
|
||||
import student from "./student.js";
|
||||
import prestudent from "./prestudent.js";
|
||||
|
||||
export default {
|
||||
props: [ "searchoptions", "searchfunction" ],
|
||||
@@ -24,7 +26,9 @@ export default {
|
||||
person: person,
|
||||
raum: raum,
|
||||
employee: employee,
|
||||
organisationunit: organisationunit
|
||||
organisationunit: organisationunit,
|
||||
student: student,
|
||||
prestudent: prestudent
|
||||
},
|
||||
template: `
|
||||
<form ref="searchform" class="d-flex me-3 position-relative" action="javascript:void(0);"
|
||||
@@ -45,6 +49,8 @@ export default {
|
||||
<div v-else-if="this.searchresult.length < 1">Es wurden keine Ergebnisse gefunden.</div>
|
||||
<template v-else="" v-for="res in this.searchresult">
|
||||
<person v-if="res.type === 'person'" :res="res" :actions="this.searchoptions.actions.person" @actionexecuted="this.hideresult"></person>
|
||||
<student v-else-if="res.type === 'student'" :res="res" :actions="this.searchoptions.actions.student" @actionexecuted="this.hideresult"></student>
|
||||
<prestudent v-else-if="res.type === 'prestudent'" :res="res" :actions="this.searchoptions.actions.prestudent" @actionexecuted="this.hideresult"></prestudent>
|
||||
<employee v-else-if="res.type === 'mitarbeiter'" :res="res" :actions="this.searchoptions.actions.employee" @actionexecuted="this.hideresult"></employee>
|
||||
<employee v-else-if="res.type === 'mitarbeiter_ohne_zuordnung'" :res="res" :actions="this.searchoptions.actions.employee" @actionexecuted="this.hideresult"></employee>
|
||||
<organisationunit v-else-if="res.type === 'organisationunit'" :res="res" :actions="this.searchoptions.actions.organisationunit" @actionexecuted="this.hideresult"></organisationunit>
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import action from "./action.js";
|
||||
import actions from "./actions.js";
|
||||
|
||||
export default {
|
||||
props: [ "res", "actions" ],
|
||||
components: {
|
||||
action: action,
|
||||
actions: actions
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
template: `
|
||||
<div class="searchbar_result searchbar_student">
|
||||
|
||||
<div class="searchbar_grid">
|
||||
<div class="searchbar_icon">
|
||||
<action :res="this.res" :action="this.actions.defaultaction" @actionexecuted="$emit('actionexecuted')">
|
||||
<img v-if="(typeof res.foto !== 'undefined') && (res.foto !== null)"
|
||||
:src="'data:image/jpeg;base64,' + res.foto"
|
||||
class="rounded-circle" height="100"/>
|
||||
<i v-else class="fas fa-user-circle fa-5x"></i>
|
||||
</action>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_data">
|
||||
<action :res="this.res" :action="this.actions.defaultaction" @actionexecuted="$emit('actionexecuted')">
|
||||
<span class="fw-bold">{{ res.name }}</span>
|
||||
</action>
|
||||
|
||||
<div class="mb-3"></div>
|
||||
|
||||
<div class="searchbar_table">
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Student_uid</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.uid }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Person_id</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.person_id }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Matrikelnummer</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.matrikelnr }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">EMail</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<a :href="this.mailtourl">
|
||||
{{ res.email }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<actions :res="this.res" :actions="this.actions.childactions"
|
||||
@actionexecuted="$emit('actionexecuted')"></actions>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`,
|
||||
methods: {
|
||||
},
|
||||
computed: {
|
||||
mailtourl: function() {
|
||||
return 'mailto:' + this.res.email;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
const elementDataMap = new WeakMap();
|
||||
|
||||
export default {
|
||||
mounted(el, binding) {
|
||||
let open = false;
|
||||
elementDataMap.set(el, evt => {
|
||||
if (!open)
|
||||
return;
|
||||
|
||||
if (el.contains(evt.target))
|
||||
return;
|
||||
|
||||
const collapse = bootstrap.Collapse.getInstance(el)
|
||||
if (collapse)
|
||||
collapse.hide();
|
||||
});
|
||||
el.addEventListener('shown.bs.collapse', () => {
|
||||
open = true;
|
||||
});
|
||||
el.addEventListener('hide.bs.collapse', () => {
|
||||
open = false;
|
||||
});
|
||||
document.addEventListener('click', elementDataMap.get(el), true);
|
||||
},
|
||||
beforeUnmount(el, binding) {
|
||||
document.removeEventListener('click', elementDataMap.get(el));
|
||||
delete el.collapsibleAutoHideFunc;
|
||||
}
|
||||
}
|
||||
@@ -847,4 +847,4 @@ var InfocenterDetails = {
|
||||
{
|
||||
return elementid.substr(elementid.indexOf("_") + 1);
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
$(document).ready(function ()
|
||||
{
|
||||
if(viewData?.checkPerson?.unruly?.length) {
|
||||
const unruly = document.getElementById('unruly')
|
||||
unruly.setAttribute('style', 'display: block;')
|
||||
}
|
||||
|
||||
if(viewData?.checkPerson?.duplicate?.length) {
|
||||
const duplicate = document.getElementById('duplicate')
|
||||
duplicate.setAttribute('style', 'display: block;')
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var PersonCheck = {
|
||||
update: function(data)
|
||||
{
|
||||
// format date according to db
|
||||
if(data.gebdatum) {
|
||||
const [day, month, year] = data.gebdatum.split('.');
|
||||
data.gebdatum = year + '-' + month + '-' + day
|
||||
}
|
||||
|
||||
FHC_AjaxClient.ajaxCallPost(
|
||||
'api/frontend/v1/checkperson/CheckPerson/checkUnruly',
|
||||
data,
|
||||
{
|
||||
successCallback: function(response, textStatus, jqXHR) {
|
||||
if (response?.meta?.status === 'success')
|
||||
{
|
||||
PersonCheck._updatedUnruly(response);
|
||||
}
|
||||
else
|
||||
{
|
||||
FHC_DialogLib.alertError('unruly error');
|
||||
}
|
||||
},
|
||||
errorCallback: function() {
|
||||
FHC_DialogLib.alertWarning("Fehler beim Speichern!");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
},
|
||||
|
||||
_updatedUnruly: function(response)
|
||||
{
|
||||
const unruly = document.getElementById('unruly')
|
||||
|
||||
if(response?.data?.retval?.length) {
|
||||
viewData.checkPerson.unruly = response?.data?.retval
|
||||
|
||||
// replace existing elements
|
||||
const unrulylist = document.getElementById('unrulylist')
|
||||
const newUnrulyPeople = []
|
||||
viewData.checkPerson.unruly.forEach(u => {
|
||||
newUnrulyPeople.push(document.createTextNode("Person ID: " + u.person_id))
|
||||
newUnrulyPeople.push(document.createElement('br'))
|
||||
})
|
||||
unrulylist.replaceChildren(...newUnrulyPeople)
|
||||
|
||||
// and show it all
|
||||
unruly.setAttribute('style', 'display: block;')
|
||||
} else {
|
||||
// just hide everything
|
||||
unruly.setAttribute('style', 'display: none;')
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
@@ -14,6 +14,7 @@ $(document).ready(function ()
|
||||
|
||||
$('.saveStammdaten').click(function()
|
||||
{
|
||||
|
||||
var kontakt = [];
|
||||
$('.kontakt_input').each(function(){
|
||||
kontakt.push({
|
||||
@@ -62,15 +63,16 @@ var Stammdaten = {
|
||||
CALLED_PATH + "/updateStammdaten/",
|
||||
data,
|
||||
{
|
||||
successCallback: function(data, textStatus, jqXHR) {
|
||||
if (FHC_AjaxClient.isSuccess(data))
|
||||
successCallback: function(response, textStatus, jqXHR) {
|
||||
if (FHC_AjaxClient.isSuccess(response))
|
||||
{
|
||||
FHC_DialogLib.alertSuccess("Done!");
|
||||
Stammdaten._updated();
|
||||
PersonCheck.update(data)
|
||||
}
|
||||
else
|
||||
{
|
||||
FHC_DialogLib.alertError(FHC_AjaxClient.getError(data));
|
||||
FHC_DialogLib.alertError(FHC_AjaxClient.getError(response));
|
||||
}
|
||||
},
|
||||
errorCallback: function() {
|
||||
@@ -149,6 +151,7 @@ var Stammdaten = {
|
||||
|
||||
_updated: function()
|
||||
{
|
||||
|
||||
$('.kontakt_input').each(function() {
|
||||
var span = $(this).parent('td').children('span');
|
||||
var value = $(this).val();
|
||||
|
||||
@@ -101,8 +101,12 @@ $(function(){
|
||||
begruendung: this.begruendung.value,
|
||||
lv_id: this.lv_id.value,
|
||||
studiensemester: this.studiensemester.value,
|
||||
begruendung_ects: this.begruendung_ects.value,
|
||||
begruendung_lvinhalt: this.begruendung_lvinhalt.value,
|
||||
begruendung_ects: this.begruendung_ects && this.begruendung_ects.value
|
||||
? this.begruendung_ects.value
|
||||
: null,
|
||||
begruendung_lvinhalt: this.begruendung_lvinhalt && this.begruendung_lvinhalt.value
|
||||
? this.begruendung_lvinhalt.value
|
||||
: null,
|
||||
bestaetigung: this.bestaetigung.value,
|
||||
uploadfile: this.uploadfile.files
|
||||
},
|
||||
@@ -216,18 +220,22 @@ var requestAnrechnung = {
|
||||
$('#requestAnrechnung-herkunftDerKenntnisse-charCounter').text(length);
|
||||
});
|
||||
|
||||
$('#requestAnrechnung-begruendungEcts').keyup(function() {
|
||||
let length = CHAR_LENGTH150 - $(this).val().length;
|
||||
$('#requestAnrechnung-begruendungEcts-charCounter').text(length);
|
||||
});
|
||||
if ($('#requestAnrechnung-begruendungEcts').length) {
|
||||
$('#requestAnrechnung-begruendungEcts').keyup(function () {
|
||||
let length = CHAR_LENGTH150 - $(this).val().length;
|
||||
$('#requestAnrechnung-begruendungEcts-charCounter').text(length);
|
||||
});
|
||||
}
|
||||
|
||||
$('#requestAnrechnung-begruendungLvinhalt').keyup(function() {
|
||||
let maxlength = CHAR_LENGTH1000 - $(this).val().length;
|
||||
$('#requestAnrechnung-begruendungLvinhalt-charCounterMax').text(maxlength);
|
||||
if ($('#requestAnrechnung-begruendungLvinhalt').length){
|
||||
$('#requestAnrechnung-begruendungLvinhalt').keyup(function() {
|
||||
let maxlength = CHAR_LENGTH1000 - $(this).val().length;
|
||||
$('#requestAnrechnung-begruendungLvinhalt-charCounterMax').text(maxlength);
|
||||
|
||||
let minlength = CHAR_LENGTH500 - $(this).val().length;
|
||||
$('#requestAnrechnung-begruendungLvinhalt-charCounterMin').text(minlength);
|
||||
});
|
||||
let minlength = CHAR_LENGTH500 - $(this).val().length;
|
||||
$('#requestAnrechnung-begruendungLvinhalt-charCounterMin').text(minlength);
|
||||
});
|
||||
}
|
||||
},
|
||||
formatAnrechnungIsApplied: function (antragdatum, dms_id, filename){
|
||||
$('#requestAnrechnung-antragdatum').text(antragdatum);
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
import {CoreFilterCmpt} from '../../components/filter/Filter.js';
|
||||
import CoreFormInput from "../../components/Form/Input.js";
|
||||
|
||||
// Fields used to restructure table data for dataTree
|
||||
const idField = 'lehrveranstaltung_id';
|
||||
const parentIdField = 'lehrveranstaltung_template_id';
|
||||
const STUDIENSEMESTER_DROPDOWN_STARTDATE = '2011-01-01';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFilterCmpt,
|
||||
CoreFormInput
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
table: null,
|
||||
studiensemester: [],
|
||||
selectedStudiensemester: '',
|
||||
cbDataTreeStartExpanded: false // checkbox expand dataTree or not
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tabulatorOptions() {
|
||||
const fhcValuesLookup = function(cell) {
|
||||
var values = {};
|
||||
const field = cell.getField();
|
||||
const data = cell.getTable().getData();
|
||||
const collectvalues = function(rows, field) {
|
||||
var values = {};
|
||||
var childvalues = {};
|
||||
for(const row of rows) {
|
||||
const rowvalue = (row[field] !== null) ? row[field] : '';
|
||||
values[rowvalue] = rowvalue;
|
||||
if(row['_children'] && row['_children'].length > 0) {
|
||||
childvalues = collectvalues(row['_children'], field);
|
||||
values = {...values, ...childvalues}
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
values = collectvalues(data, field);
|
||||
const vals = Object.keys(values).sort();
|
||||
if(vals.indexOf('') === -1) {
|
||||
vals.unshift('');
|
||||
}
|
||||
return vals;
|
||||
};
|
||||
const fhctreefilter = function(headerValue, rowValue, rowData, filterParams){
|
||||
if (rowData['_children'] && rowData['_children'].length > 0) {
|
||||
for (var i in rowData['_children']) {
|
||||
return rowValue == headerValue ||
|
||||
fhctreefilter(
|
||||
headerValue,
|
||||
rowData['_children'][i][filterParams.field],
|
||||
rowData['_children'][i],
|
||||
filterParams
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return rowValue == headerValue;
|
||||
};
|
||||
const self = this;
|
||||
return {
|
||||
// NOTE: data is set on table built to await preselected actual Studiensemester
|
||||
ajaxResponse(url, params, response) {
|
||||
return self.prepDataTreeData(response.data) // Prepare data for dataTree view
|
||||
},
|
||||
layout: 'fitColumns',
|
||||
autoResize: false, // prevent auto resizing of table
|
||||
resizableColumnFit: true, //maintain the fit of columns when resizing
|
||||
index: 'lehrveranstaltung_id',
|
||||
selectable: true,
|
||||
selectableRangeMode: 'click',
|
||||
dataTree: true,
|
||||
dataTreeStartExpanded: self.cbDataTreeStartExpanded,
|
||||
dataTreeChildIndent: 15, //indent child rows by 15 px
|
||||
persistence:{
|
||||
filter: false, //persist filter sorting
|
||||
},
|
||||
columns: [
|
||||
{title: 'LV-ID', field: 'lehrveranstaltung_id', headerFilter: true, visible: false},
|
||||
{title: 'LV Kurzbz', field: 'kurzbz', headerFilter: true, visible:false, width: 70},
|
||||
{title: 'STG Kurzbz', field: 'stg_typ_kurzbz', headerFilter: "list", headerFilterParams: {valuesLookup: fhcValuesLookup}, headerFilterFunc: fhctreefilter, headerFilterFuncParams: {field: 'stg_typ_kurzbz'}, visible:true, width: 80},
|
||||
{title: 'OrgEinheit', field: 'lv_oe_bezeichnung', headerFilter: true, visible: false, width: 250},
|
||||
{title: 'Lehrtyp Kurzbz', field: 'lehrtyp_kurzbz', headerFilter: true, visible:false, width: 70},
|
||||
{title: 'Studiengangtyp', field: 'stg_typ_bezeichnung', headerFilter: "list", headerFilterParams: {valuesLookup: fhcValuesLookup}, headerFilterFunc: fhctreefilter, headerFilterFuncParams: {field: 'stg_typ_bezeichnung'}, width: 150},
|
||||
{title: 'OrgForm', field: 'orgform_kurzbz', headerFilter: "list", headerFilterParams: {valuesLookup: fhcValuesLookup}, headerFilterFunc: fhctreefilter, headerFilterFuncParams: {field: 'orgform_kurzbz'}, width: 70},
|
||||
{title: 'Semester', field: 'semester', headerFilter: true, width: 50},
|
||||
{title: 'Lehrveranstaltung', field: 'lv_bezeichnung', headerFilter: true, minWidth: 250},
|
||||
{title: 'Lehrveranstaltung ENG', field: 'bezeichnung_english', headerFilter: true, minWidth: 250},
|
||||
{title: 'ECTS', field: 'ects', headerFilter: true, width: 50, hozAlign: 'right'},
|
||||
{title: 'Lehrform', field: 'lehrform_kurzbz', headerFilter: true, width: 50},
|
||||
{title: 'Sprache', field: 'sprache', headerFilter: true, width: 100},
|
||||
{title: 'Aktiv', field: 'aktiv', width: 50,
|
||||
formatter:"tickCross",
|
||||
headerFilter:"tickCross",
|
||||
headerFilterParams:{"tristate": true},
|
||||
hozAlign:"center",
|
||||
formatterParams: {
|
||||
tickElement: '<i class="fa fa-check text-success"></i>',
|
||||
crossElement: '<i class="fa fa-xmark text-danger"></i>'
|
||||
}
|
||||
},
|
||||
{title: 'Studienplan', field: 'studienplan_bezeichnung', headerFilter: true, visible:true, width: 220},
|
||||
{title: 'OE Kurzbz', field: 'lv_oe_kurzbz', headerFilter: true, visible:false, minWidth: 80},
|
||||
{
|
||||
title: this.$p.t('global/aktionen'),
|
||||
field: 'actions',
|
||||
width: 140,
|
||||
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';
|
||||
button.innerHTML = '<i class="fa fa-external-link"></i> ' + this.$p.t('global/verwalten');
|
||||
button.addEventListener('click', (event) => this.openAdminLvTemplate(event, cell.getRow()));
|
||||
container.append(button);
|
||||
|
||||
return container;
|
||||
},
|
||||
frozen: true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
urlToAdminAllTemplates() {
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
'vilesci/lehre/lehrveranstaltung.php?stg_kz=99999&semester=-1&orgform=-1';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async loadAndSetStudiensemester(){
|
||||
const result = await this.$fhcApi
|
||||
.get('api/frontend/v1/organisation/Studiensemester/getAll', {start: STUDIENSEMESTER_DROPDOWN_STARTDATE})
|
||||
.then(result => this.studiensemester = result.data )
|
||||
.then(() => this.$fhcApi.get('api/frontend/v1/organisation/Studiensemester/getAktNext') ) // Get actual Studiensemester
|
||||
.then(result => this.selectedStudiensemester = result.data[0].studiensemester_kurzbz ) // Preselect Studiensemester
|
||||
.catch(error => this.$fhcAlert.handleSystemError(error) );
|
||||
},
|
||||
async onTableBuilt(){
|
||||
|
||||
this.table = this.$refs.lvTemplateUebersichtTable.tabulator;
|
||||
|
||||
// Await Studiensemester
|
||||
await this.loadAndSetStudiensemester();
|
||||
|
||||
// Set table data
|
||||
this.table.setData(
|
||||
this.$fhcApi.getUri() +
|
||||
'/api/frontend/v1/education/Lehrveranstaltung/getTemplateLvTree' +
|
||||
'?studiensemester_kurzbz=' + this.selectedStudiensemester
|
||||
);
|
||||
|
||||
// Await phrases categories
|
||||
await this.$p.loadCategory(['lehre']);
|
||||
|
||||
// Replace column titles with phrasen
|
||||
this.table.updateColumnDefinition('lv_bezeichnung', {title: this.$p.t('lehre', 'lehrveranstaltung')});
|
||||
|
||||
},
|
||||
onChangeStudiensemester(){
|
||||
// Reset table data
|
||||
this.table.setData(
|
||||
this.$fhcApi.getUri() +
|
||||
'/api/frontend/v1/education/Lehrveranstaltung/getTemplateLvTree' +
|
||||
'?studiensemester_kurzbz=' + this.selectedStudiensemester
|
||||
);
|
||||
},
|
||||
openAdminLvTemplate(event, row){
|
||||
const url = FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
'vilesci/lehre/lehrveranstaltung.php?stg_kz=&semester=-1&orgform=-1&lehrveranstaltung_id=' +
|
||||
row.getData().lehrveranstaltung_id;
|
||||
|
||||
window.open(url, '_blank').focus();
|
||||
},
|
||||
prepDataTreeData(data){
|
||||
let toDelete = [];
|
||||
|
||||
// loop through all data
|
||||
for (let childIdx = 0; childIdx < data.length; childIdx++)
|
||||
{
|
||||
let child = data[childIdx];
|
||||
|
||||
// if it has parent id, it is a child
|
||||
if (child[parentIdField])
|
||||
{
|
||||
// append the child on the right place. If parent found, mark original sw child on 0 level for deleting
|
||||
if (this._appendChild(data, child)) toDelete.push(childIdx);
|
||||
}
|
||||
}
|
||||
|
||||
// delete the marked children from 0 level
|
||||
for (let counter = 0; counter < toDelete.length; counter++)
|
||||
{
|
||||
// decrease index by counter as index of data array changes after every deletion
|
||||
data.splice(toDelete[counter] - counter, 1);
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
_appendChild(data, child) {
|
||||
// get parent id
|
||||
let parentId = child[parentIdField];
|
||||
|
||||
// loop thorugh all data
|
||||
for (let parentIdx = 0; parentIdx < data.length; parentIdx++)
|
||||
{
|
||||
let parent = data[parentIdx];
|
||||
|
||||
// if it's the parent
|
||||
if (parent[idField] == parentId)
|
||||
{
|
||||
// create children array if not done yet
|
||||
if (!parent._children) parent._children = [];
|
||||
|
||||
// if child is not included in children array, append the child
|
||||
if (!parent._children.includes(child)) parent._children.push(child);
|
||||
|
||||
// parent found
|
||||
return true;
|
||||
}
|
||||
// search children for parents
|
||||
else if (parent._children) this._appendChild(parent._children, child);
|
||||
}
|
||||
|
||||
// parent not found
|
||||
return false;
|
||||
},
|
||||
reloadTabulator() {
|
||||
if (this.table !== null && this.table !== undefined)
|
||||
{
|
||||
for (let option in this.tabulatorOptions)
|
||||
{
|
||||
if (this.table.options.hasOwnProperty(option))
|
||||
this.table.options[option] = this.tabulatorOptions[option];
|
||||
}
|
||||
this.$refs.lvTemplateUebersichtTable.reloadTable();
|
||||
}
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div class="lvTemplateUebersicht overflow-hidden">
|
||||
<div class="row d-flex mb-3">
|
||||
<div class="col-10 h2 mb-4">{{ $p.t('lehre/lvTemplatesUebersicht') }}</div>
|
||||
<div class="col-2 ms-auto align-self-end">
|
||||
<core-form-input
|
||||
type="select"
|
||||
v-model="selectedStudiensemester"
|
||||
name="studiensemester"
|
||||
@change="onChangeStudiensemester">
|
||||
<option
|
||||
v-for="(studSem, index) in studiensemester"
|
||||
:key="index"
|
||||
:value="studSem.studiensemester_kurzbz">
|
||||
{{studSem.studiensemester_kurzbz}}
|
||||
</option>
|
||||
</core-form-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-5">
|
||||
<div class="col">
|
||||
<core-filter-cmpt
|
||||
ref="lvTemplateUebersichtTable"
|
||||
uniqueId="lvTemplateUebersichtTable"
|
||||
table-only
|
||||
:side-menu="false"
|
||||
:tabulator-options="tabulatorOptions"
|
||||
:tabulator-events="[{event: 'tableBuilt', handler: onTableBuilt}]">
|
||||
<template v-slot:actions>
|
||||
<a type="button" class="btn btn-primary" :href="urlToAdminAllTemplates" target="_blank"><i class="fa fa-external-link me-2"></i>{{ $p.t('lehre/lvTemplatesVerwalten') }}</a>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
v-model="cbDataTreeStartExpanded"
|
||||
:checked="cbDataTreeStartExpanded"
|
||||
@change="reloadTabulator">
|
||||
<label class="form-check-label">Templates {{ $p.t('global/aufgeklappt') }}</label>
|
||||
</div>
|
||||
</template>
|
||||
</core-filter-cmpt>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`
|
||||
};
|
||||
@@ -250,6 +250,10 @@ export default {
|
||||
// Error is array of strings
|
||||
if (Array.isArray(error) && error.every(err => typeof err === 'string'))
|
||||
return error.every($fhcAlert.alertSystemError);
|
||||
|
||||
// Error has been handled already
|
||||
if (error.hasOwnProperty('handled') && error.handled)
|
||||
return;
|
||||
|
||||
// Error is object
|
||||
if (typeof error === 'object' && error !== null) {
|
||||
@@ -390,5 +394,6 @@ export default {
|
||||
}
|
||||
};
|
||||
app.config.globalProperties.$fhcAlert = $fhcAlert;
|
||||
app.provide('$fhcAlert', app.config.globalProperties.$fhcAlert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+58
-19
@@ -1,5 +1,5 @@
|
||||
import FhcAlert from './FhcAlert.js';
|
||||
import FhcApiFactory from '../apps/api/fhcapifactory.js';
|
||||
import FhcApiFactory from '../api/fhcapifactory.js';
|
||||
|
||||
|
||||
export default {
|
||||
@@ -100,13 +100,13 @@ export default {
|
||||
// NOTE(chris): loop through errors
|
||||
if (response.data.errors)
|
||||
response.data.errors = response.data.errors.filter(
|
||||
err => (response.config[err.type + 'ErrorHandler'] || app.config.globalProperties.$fhcApi._defaultErrorHandlers[err.type])(err, response.config.form)
|
||||
err => (response.config[err.type + 'ErrorHandler'] || app.config.globalProperties.$fhcApi._defaultErrorHandlers[err.type])(err, response.config)
|
||||
);
|
||||
|
||||
return _clean_return_value(response);
|
||||
}, error => {
|
||||
if (error.code == 'ERR_CANCELED')
|
||||
return new Promise(() => {});
|
||||
return Promise.reject({...{handled: true}, ...error});
|
||||
|
||||
if (error.config?.errorHandling == 'off'
|
||||
|| error.config?.errorHandling === false
|
||||
@@ -116,27 +116,30 @@ export default {
|
||||
if (error.response) {
|
||||
if (error.response.status == 404) {
|
||||
app.config.globalProperties.$fhcAlert.alertDefault('error', error.message, error.request.responseURL, true);
|
||||
return new Promise(() => {});
|
||||
return Promise.reject({...{handled: true}, ...error});
|
||||
}
|
||||
|
||||
// NOTE(chris): loop through errors
|
||||
error.response.data.errors = error.response.data.errors.filter(
|
||||
err => (error.config[err.type + 'ErrorHandler'] || app.config.globalProperties.$fhcApi._defaultErrorHandlers[err.type])(err, error.config.form)
|
||||
err => (error.config[err.type + 'ErrorHandler'] || app.config.globalProperties.$fhcApi._defaultErrorHandlers[err.type])(err, error.config)
|
||||
);
|
||||
if (!error.response.data.errors.length)
|
||||
return new Promise(() => {});
|
||||
return Promise.reject({...{handled: true}, ...error});
|
||||
} else if (error.request) {
|
||||
app.config.globalProperties.$fhcAlert.alertDefault('error', error.message, error.request.responseURL);
|
||||
return new Promise(() => {});
|
||||
return Promise.reject({...{handled: true}, ...error});
|
||||
} else {
|
||||
app.config.globalProperties.$fhcAlert.alertError(error.message);
|
||||
return new Promise(() => {});
|
||||
return Promise.reject({...{handled: true}, ...error});
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
app.config.globalProperties.$fhcApi = {
|
||||
getUri(url) {
|
||||
return fhcApiAxios.getUri({url});
|
||||
},
|
||||
get(form, uri, params, config) {
|
||||
[uri, params, config] = _get_config(form, uri, params, config);
|
||||
if (params) {
|
||||
@@ -152,30 +155,47 @@ export default {
|
||||
return fhcApiAxios.post(uri, data, config);
|
||||
},
|
||||
_defaultErrorHandlers: {
|
||||
validation(error, form) {
|
||||
validation(error, config) {
|
||||
const $fhcAlert = app.config.globalProperties.$fhcAlert;
|
||||
|
||||
if (form) {
|
||||
form.clearValidation();
|
||||
form.setFeedback(false, error.messages);
|
||||
if (config?.form) {
|
||||
config.form.clearValidation();
|
||||
config.form.setFeedback(false, error.messages);
|
||||
return false;
|
||||
}
|
||||
if (Array.isArray(error.messages)) {
|
||||
error.messages.forEach($fhcAlert.alertError);
|
||||
return false;
|
||||
} else if (typeof error.messages == 'object') {
|
||||
Object.entries(error.messages).forEach(
|
||||
([key, value]) => $fhcAlert.alertDefault('error', key, value, true)
|
||||
);
|
||||
if (config?.errorHeader)
|
||||
Object.values(error.messages).forEach(
|
||||
value => $fhcAlert.alertDefault(
|
||||
'error',
|
||||
Array.isArray(config.errorHeader) ? app.config.globalProperties.$p.t.apply(null, config.errorHeader) : config.errorHeader,
|
||||
value,
|
||||
true
|
||||
)
|
||||
);
|
||||
else
|
||||
Object.entries(error.messages).forEach(
|
||||
([key, value]) => $fhcAlert.alertDefault('error', key, value, true)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
general(error, form) {
|
||||
general(error, config) {
|
||||
const $fhcAlert = app.config.globalProperties.$fhcAlert;
|
||||
|
||||
if (form)
|
||||
form.setFeedback(false, error.message);
|
||||
if (config?.form)
|
||||
config.form.setFeedback(false, error.message);
|
||||
else if (config?.errorHeader)
|
||||
$fhcAlert.alertDefault(
|
||||
'error',
|
||||
Array.isArray(config.errorHeader) ? app.config.globalProperties.$p.t.apply(null, config.errorHeader) : config.errorHeader,
|
||||
error.message,
|
||||
true
|
||||
);
|
||||
else
|
||||
$fhcAlert.alertError(error.message);
|
||||
},
|
||||
@@ -250,6 +270,23 @@ export default {
|
||||
message += 'Line Number: ' + error.line + '\n';
|
||||
|
||||
$fhcAlert.alertSystemError(message);
|
||||
},
|
||||
auth(error, config) {
|
||||
const $fhcAlert = app.config.globalProperties.$fhcAlert;
|
||||
|
||||
var message = '';
|
||||
message += 'Controller name: ' + error.controller + '\n';
|
||||
message += 'Method name: ' + error.method + '\n';
|
||||
message += 'Required permissions: ' + error.required_permissions;
|
||||
if (config?.errorHeader)
|
||||
$fhcAlert.alertDefault(
|
||||
'error',
|
||||
Array.isArray(config.errorHeader) ? app.config.globalProperties.$p.t.apply(null, config.errorHeader) : config.errorHeader,
|
||||
error.message,
|
||||
true
|
||||
);
|
||||
else
|
||||
$fhcAlert.alertDefault('error', error.message, message);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -276,7 +313,9 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
app.config.globalProperties.$fhcApi.factory = new FhcApiFactoryWrapper(FhcApiFactory);
|
||||
const mergedFhcApiFactory = options?.factory ? {...FhcApiFactory, ...options.factory} : FhcApiFactory;
|
||||
|
||||
app.config.globalProperties.$fhcApi.factory = new FhcApiFactoryWrapper(mergedFhcApiFactory);
|
||||
app.provide('$fhcApi', app.config.globalProperties.$fhcApi);
|
||||
}
|
||||
};
|
||||
@@ -1,3 +1,5 @@
|
||||
import FhcApi from './FhcApi.js';
|
||||
|
||||
const categories = Vue.reactive({});
|
||||
const loadingModules = {};
|
||||
|
||||
@@ -21,20 +23,19 @@ function getValueForLoadedPhrase(category, phrase, params) {
|
||||
const phrasen = {
|
||||
loadCategory(category) {
|
||||
if (Array.isArray(category))
|
||||
return Promise.all(category.map(cat => this.loadCategory(cat)));
|
||||
return Promise.all(category.map(this.config.globalProperties
|
||||
.$p.loadCategory));
|
||||
if (!loadingModules[category])
|
||||
loadingModules[category] = axios
|
||||
.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Phrasen/loadModule/' + category)
|
||||
loadingModules[category] = this.config.globalProperties
|
||||
.$fhcApi.factory.phrasen.loadCategory(category)
|
||||
.then(res => res?.data ? extractCategory(res.data, category) : {})
|
||||
.then(res => {
|
||||
if (res.data.retval)
|
||||
categories[category] = extractCategory(res.data.retval, category);
|
||||
else
|
||||
categories[category] = {};
|
||||
categories[category] = res;
|
||||
});
|
||||
return loadingModules[category];
|
||||
},
|
||||
t_ref(category, phrase, params) {
|
||||
console.warn('depricated');
|
||||
console.warn('deprecated');
|
||||
return Vue.computed(() => this.t(category, phrase, params));
|
||||
},
|
||||
t(category, phrase, params) {
|
||||
@@ -62,6 +63,12 @@ const phrasen = {
|
||||
|
||||
export default {
|
||||
install(app, options) {
|
||||
app.config.globalProperties.$p = phrasen;
|
||||
app.use(FhcApi, options?.fhcApi || undefined);
|
||||
app.config.globalProperties.$p = {
|
||||
t: phrasen.t,
|
||||
loadCategory: cat => phrasen.loadCategory.call(app, cat),
|
||||
t_ref: phrasen.t_ref
|
||||
};
|
||||
app.provide('$p', app.config.globalProperties.$p);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user