mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-07-25 08:34:29 +00:00
Merge branch 'master' into feature-30660/FHC4_StudierendenGUI_Prototyp
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
.text-prewrap {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.accordion-button-primary {
|
||||
background-color: #e7f1ff;
|
||||
color: #0c63e4;
|
||||
}
|
||||
.accordion-button-primary:not(.collapsed) {
|
||||
background-color: #cfe2ff;
|
||||
color: #0a58ca;
|
||||
}
|
||||
|
||||
.accordion-button-secondary {
|
||||
background-color: #f0f1f2;
|
||||
color: #616971;
|
||||
}
|
||||
.accordion-button-secondary:not(.collapsed) {
|
||||
background-color: #e2e3e5;
|
||||
color: #565e64;
|
||||
}
|
||||
|
||||
.accordion-button-success {
|
||||
background-color: #e8f3ee;
|
||||
color: #177a4c;
|
||||
}
|
||||
.accordion-button-success:not(.collapsed) {
|
||||
background-color: #d1e7dd;
|
||||
color: #146c43;
|
||||
}
|
||||
|
||||
.accordion-button-info {
|
||||
background-color: #e7fafe;
|
||||
color: #0cb6d8;
|
||||
}
|
||||
.accordion-button-info:not(.collapsed) {
|
||||
background-color: #cff4fc;
|
||||
color: #0aa2c0;
|
||||
}
|
||||
|
||||
.accordion-button-warning {
|
||||
background-color: #fff9e6;
|
||||
color: #e6ae06;
|
||||
}
|
||||
.accordion-button-warning:not(.collapsed) {
|
||||
background-color: #fff3cd;
|
||||
color: #cc9a06;
|
||||
}
|
||||
|
||||
.accordion-button-danger {
|
||||
background-color: #fcebec;
|
||||
color: #c6303e;
|
||||
}
|
||||
.accordion-button-danger:not(.collapsed) {
|
||||
background-color: #f8d7da;
|
||||
color: #b02a37;
|
||||
}
|
||||
|
||||
.accordion-button-light {
|
||||
background-color: #fefeff;
|
||||
color: #dfe0e1;
|
||||
}
|
||||
.accordion-button-light:not(.collapsed) {
|
||||
background-color: #fefefe;
|
||||
color: #c6c7c8;
|
||||
}
|
||||
|
||||
.accordion-button-dark {
|
||||
background-color: #e9e9ea;
|
||||
color: #1e2125;
|
||||
}
|
||||
.accordion-button-dark:not(.collapsed) {
|
||||
background-color: #d3d3d4;
|
||||
color: #1a1e21;
|
||||
}
|
||||
|
||||
.tabulator-edit-list .tabulator-edit-list-item {
|
||||
background-color: white;
|
||||
}
|
||||
.tabulator-edit-list .tabulator-edit-list-item:hover,
|
||||
.tabulator-edit-list .tabulator-edit-list-item.active {
|
||||
color: white;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
@import '../../vendor/olifolkerd/tabulator5/dist/css/tabulator_bootstrap5.min.css';
|
||||
|
||||
/* Apply borders and background to Cell instead of the Row
|
||||
* otherwise frozen columns won't look good (columns behind
|
||||
* will be partial visible)
|
||||
*/
|
||||
.tabulator-row {
|
||||
border-bottom: none;
|
||||
}
|
||||
.tabulator-row .tabulator-frozen,
|
||||
.tabulator-row .tabulator-cell {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
.tabulator-row.tabulator-row-even {
|
||||
background-color: transparent;
|
||||
}
|
||||
.tabulator-headers .tabulator-frozen,
|
||||
.tabulator-row.tabulator-row-odd .tabulator-frozen,
|
||||
.tabulator-row.tabulator-row-odd .tabulator-cell {
|
||||
background-color: #fff;
|
||||
}
|
||||
.tabulator-row.tabulator-row-even .tabulator-frozen,
|
||||
.tabulator-row.tabulator-row-even .tabulator-cell {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
.tabulator-row.tabulator-selectable:hover .tabulator-frozen,
|
||||
.tabulator-row.tabulator-selectable:hover .tabulator-cell {
|
||||
background-color: #ececec;
|
||||
}
|
||||
.tabulator-row.tabulator-selected .tabulator-frozen,
|
||||
.tabulator-row.tabulator-selected .tabulator-cell {
|
||||
background-color: #9abcea;
|
||||
}
|
||||
.tabulator-row.tabulator-selected:hover .tabulator-frozen,
|
||||
.tabulator-row.tabulator-selected:hover .tabulator-cell {
|
||||
background-color: #769bcc;
|
||||
}
|
||||
.tabulator .tabulator-col-resize-handle:last-of-type {
|
||||
z-index: 999999;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
:root {
|
||||
--bs-body-font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
--bs-body-font-size: 14px;
|
||||
--bs-body-line-height: 1.42857143;
|
||||
--bs-body-color: #333;
|
||||
}
|
||||
html {
|
||||
font-size: var(--bs-body-font-size);
|
||||
}
|
||||
|
||||
h1, .h1 {
|
||||
margin-top: 0;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
h2, .h2 {
|
||||
margin-top: 0;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
h3, .h3 {
|
||||
margin-top: 0;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
h4, .h4 {
|
||||
margin-top: 0;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
h5, .h5 {
|
||||
margin-top: 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
h6, .h6 {
|
||||
margin-top: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.fhc-header {
|
||||
display: flex;
|
||||
justify-content: between;
|
||||
flex: wrap;
|
||||
align-items: center;
|
||||
padding: 20px 15px 19px;
|
||||
margin: 40px 0 20px;
|
||||
border-bottom: solid 1px #eee;
|
||||
}
|
||||
@media (min-width:768px) { /* NOTE(chris): size "md" from bs5 */
|
||||
.fhc-header {
|
||||
flex-wrap: nowrap!important;
|
||||
}
|
||||
}
|
||||
.fhc-header h1,
|
||||
.fhc-header h2 {
|
||||
font-size: 24px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.fhc-container {
|
||||
padding: 0 15px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.tabulator {
|
||||
font-size: var(--bs-body-font-size);
|
||||
}
|
||||
|
||||
.fhc-container .tabulator {
|
||||
border-left: 1px solid #dee2e6;
|
||||
}
|
||||
.fhc-container .tabulator .tabulator-row {
|
||||
border-bottom: none;
|
||||
}
|
||||
.fhc-container .tabulator .tabulator-row .tabulator-frozen,
|
||||
.fhc-container .tabulator .tabulator-row .tabulator-cell {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
.fhc-container .tabulator .tabulator-row.tabulator-row-even {
|
||||
background-color: transparent;
|
||||
}
|
||||
.fhc-container .tabulator .tabulator-headers .tabulator-frozen,
|
||||
.fhc-container .tabulator .tabulator-row.tabulator-row-odd .tabulator-frozen,
|
||||
.fhc-container .tabulator .tabulator-row.tabulator-row-odd .tabulator-cell {
|
||||
background-color: #fff;
|
||||
}
|
||||
.fhc-container .tabulator .tabulator-row.tabulator-row-even .tabulator-frozen,
|
||||
.fhc-container .tabulator .tabulator-row.tabulator-row-even .tabulator-cell {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
.fhc-container .tabulator .tabulator-row.tabulator-selectable:hover .tabulator-frozen,
|
||||
.fhc-container .tabulator .tabulator-row.tabulator-selectable:hover .tabulator-cell {
|
||||
background-color: #ececec;
|
||||
}
|
||||
|
||||
.fhc-container .tabulator .tabulator-col,
|
||||
.fhc-container .tabulator .tabulator-cell {
|
||||
border-right: 1px solid #dee2e6;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/* for removing horizontal scrollbar when form is embedded as i frame */
|
||||
#uhstat1Container {
|
||||
overflow-x:hidden;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
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
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
status: {
|
||||
msg: '',
|
||||
severity: ''
|
||||
},
|
||||
infoArray: []
|
||||
};
|
||||
}
|
||||
});
|
||||
app
|
||||
.use(Phrasen)
|
||||
.mount('#wrapper');
|
||||
@@ -0,0 +1,12 @@
|
||||
import StudierendenantragLeitung from '../../../components/Studierendenantrag/Leitung.js';
|
||||
import Phrasen from '../../../plugin/Phrasen.js';
|
||||
|
||||
const app = Vue.createApp({
|
||||
components: {
|
||||
StudierendenantragLeitung
|
||||
}
|
||||
});
|
||||
app
|
||||
.use(Phrasen)
|
||||
.use(primevue.config.default,{zIndex: {overlay: 9999}})
|
||||
.mount('#wrapper');
|
||||
@@ -0,0 +1,16 @@
|
||||
import LvZuweisung from '../../../components/Studierendenantrag/Lvzuweisung.js';
|
||||
import Phrasen from '../../../plugin/Phrasen.js';
|
||||
|
||||
const app = Vue.createApp({
|
||||
components: {
|
||||
LvZuweisung
|
||||
},
|
||||
computed: {
|
||||
notinframe() {
|
||||
return window.self === window.top;
|
||||
}
|
||||
}
|
||||
});
|
||||
app
|
||||
.use(Phrasen)
|
||||
.mount('#wrapper');
|
||||
@@ -0,0 +1,11 @@
|
||||
import LvPopup from '../../../components/Studierendenantrag/Leitung/LvPopup.js';
|
||||
import Phrasen from '../../../plugin/Phrasen.js';
|
||||
|
||||
const app = Vue.createApp({
|
||||
components: {
|
||||
LvPopup
|
||||
}
|
||||
});
|
||||
app
|
||||
.use(Phrasen)
|
||||
.mount('#wrapper');
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* javascript file for UHSTAT1 GUI
|
||||
*/
|
||||
$(document).ready(function ()
|
||||
{
|
||||
window.setTimeout(function() {
|
||||
$("#uhstat_success_alert").fadeTo(500, 0).slideUp(500, function(){
|
||||
$(this).remove();
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import BsModal from './Modal.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BsModal
|
||||
},
|
||||
mixins: [
|
||||
BsModal
|
||||
],
|
||||
props: {
|
||||
dialogClass: {
|
||||
type: [String,Array,Object],
|
||||
default: 'modal-dialog-centered'
|
||||
},
|
||||
/*
|
||||
* NOTE(chris):
|
||||
* Hack to expose in "emits" declared events to $props which we use
|
||||
* in the v-bind directive to forward all events.
|
||||
* @see: https://github.com/vuejs/core/issues/3432
|
||||
*/
|
||||
onHideBsModal: Function,
|
||||
onHiddenBsModal: Function,
|
||||
onHidePreventedBsModal: Function,
|
||||
onShowBsModal: Function,
|
||||
onShownBsModal: Function
|
||||
},
|
||||
data: () => ({
|
||||
result: true
|
||||
}),
|
||||
mounted() {
|
||||
this.modal = this.$refs.modalContainer.modal;
|
||||
},
|
||||
popup(msg, options) {
|
||||
return BsModal.popup.bind(this)(msg, options);
|
||||
},
|
||||
template: `<bs-modal ref="modalContainer" class="bootstrap-alert" v-bind="$props">
|
||||
<template v-slot:default>
|
||||
<slot></slot>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">OK</button>
|
||||
</template>
|
||||
</bs-modal>`
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import BsAlert from './Alert';
|
||||
|
||||
export default {
|
||||
mixins: [
|
||||
BsAlert
|
||||
],
|
||||
data: () => ({
|
||||
result: false
|
||||
}),
|
||||
popup(msg, options) {
|
||||
return BsAlert.popup.bind(this)(msg, options);
|
||||
},
|
||||
template: `<bs-modal ref="modalContainer" class="bootstrap-confirm" v-bind="$props">
|
||||
<template v-slot:default>
|
||||
<slot></slot>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<button type="button" class="btn btn-primary" @click="result=true;this.hide()">OK</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
</template>
|
||||
</bs-modal>`
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
import BsAlert from './Alert';
|
||||
|
||||
export default {
|
||||
mixins: [
|
||||
BsAlert
|
||||
],
|
||||
props: {
|
||||
placeholder: String,
|
||||
default: String
|
||||
},
|
||||
data: () => ({
|
||||
value: '',
|
||||
result: false
|
||||
}),
|
||||
created() {
|
||||
if (this.default)
|
||||
this.value = this.default;
|
||||
},
|
||||
popup(msg, options) {
|
||||
if (typeof options === 'string')
|
||||
options = { default: options };
|
||||
return BsAlert.popup.bind(this)(msg, options);
|
||||
},
|
||||
template: `<bs-modal ref="modalContainer" class="bootstrap-prompt" v-bind="$props">
|
||||
<template v-slot:default>
|
||||
<slot></slot>
|
||||
<div>
|
||||
<input ref="input" type="text" class="form-control" :placeholder="placeholder" v-model="value">
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<button type="button" class="btn btn-primary" @click="result=value;this.hide()">OK</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
</template>
|
||||
</bs-modal>`
|
||||
}
|
||||
@@ -53,7 +53,9 @@ export const CoreFetchCmpt = {
|
||||
*
|
||||
*/
|
||||
fetchData: function() {
|
||||
this.loading = true; // loader started
|
||||
this.loading = true; // loader started
|
||||
this.error = false;
|
||||
this.errorMessage = null;
|
||||
|
||||
// Checks if the apifunction is a callable function
|
||||
if (typeof this.apiFunction == "function")
|
||||
@@ -91,13 +93,16 @@ export const CoreFetchCmpt = {
|
||||
*
|
||||
*/
|
||||
successHandler: function(response) {
|
||||
this.$emit('dataFetched', response.data); // trigger the event dataFetched
|
||||
this.$emit('dataFetched', response ? response.data : undefined); // trigger the event dataFetched
|
||||
},
|
||||
/**
|
||||
*
|
||||
*/
|
||||
errorHandler: function(error) {
|
||||
this.setError(error.message);
|
||||
if (error.response.data.retval)
|
||||
this.setError(error.response.data.retval);
|
||||
else
|
||||
this.setError(error.message);
|
||||
},
|
||||
/**
|
||||
*
|
||||
@@ -107,12 +112,12 @@ export const CoreFetchCmpt = {
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<slot v-if="loading">
|
||||
<slot v-if="loading" name="loading">
|
||||
<div class="fetch-loader">Loading...</div>
|
||||
</slot>
|
||||
<slot v-if="error">
|
||||
<slot v-else-if="error" name="error" :error-message="errorMessage">
|
||||
<div class="fetch-error">{{ errorMessage }}</div>
|
||||
</slot>
|
||||
<slot v-else></slot>
|
||||
`
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import BsModal from './Bootstrap/Modal.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BsModal
|
||||
},
|
||||
props: {
|
||||
timeout: {
|
||||
type: Number,
|
||||
default: 300
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
t: null,
|
||||
state: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
switch (this.state) {
|
||||
case 0:
|
||||
if (this.timeout) {
|
||||
this.state = 1;
|
||||
this.t = window.setTimeout(() => this.$refs.modal.show(), this.timeout);
|
||||
return;
|
||||
} else
|
||||
return this.$refs.modal.show();
|
||||
case 4:
|
||||
return window.setTimeout(() => this.show(), 1);
|
||||
}
|
||||
},
|
||||
hide() {
|
||||
switch (this.state) {
|
||||
case 1:
|
||||
return window.clearTimeout(this.t);
|
||||
case 2:
|
||||
return window.setTimeout(() => this.hide(), 1);
|
||||
case 3:
|
||||
this.$refs.modal.hide();
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$refs.modal.$refs.modal.addEventListener('show.bs.modal', () => {
|
||||
this.state = 2;
|
||||
});
|
||||
this.$refs.modal.$refs.modal.addEventListener('shown.bs.modal', () => {
|
||||
this.state = 3;
|
||||
});
|
||||
this.$refs.modal.$refs.modal.addEventListener('hide.bs.modal', () => {
|
||||
this.state = 4;
|
||||
});
|
||||
this.$refs.modal.$refs.modal.addEventListener('hidden.bs.modal', () => {
|
||||
this.state = 0;
|
||||
});
|
||||
},
|
||||
template: `
|
||||
<bs-modal ref="modal" class="fade text-center" dialog-class="modal-dialog-centered" backdrop="static" :keyboard="false">
|
||||
Loading...
|
||||
</bs-modal>`
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import StudierendenantragAbmeldung from './Form/Abmeldung.js';
|
||||
import StudierendenantragAbmeldungStgl from './Form/AbmeldungStgl.js';
|
||||
import StudierendenantragUnterbrechung from './Form/Unterbrechung.js';
|
||||
import StudierendenantragWiederholung from './Form/Wiederholung.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
StudierendenantragAbmeldung,
|
||||
StudierendenantragAbmeldungStgl,
|
||||
StudierendenantragUnterbrechung,
|
||||
StudierendenantragWiederholung
|
||||
},
|
||||
emits: [
|
||||
'update:infoArray',
|
||||
'update:statusMsg',
|
||||
'update:statusSeverity'
|
||||
],
|
||||
props: {
|
||||
antragType: String,
|
||||
prestudentId: Number,
|
||||
studierendenantragId: Number,
|
||||
infoArray: Array,
|
||||
statusMsg: String,
|
||||
statusSeverity: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
status: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
typeComponent() {
|
||||
return 'Studierendenantrag' + this.antragType;
|
||||
},
|
||||
infoText() {
|
||||
return this.$p.t('studierendenantrag/info_' + this.antragType + '_' + this.status);
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="studierendenantrag-antrag card">
|
||||
<div class="card-header">
|
||||
{{$p.t('studierendenantrag', 'title_' + antragType)}}
|
||||
</div>
|
||||
<div v-if="infoText && infoText.substr(0, 9) != '<< PHRASE'" class="alert alert-primary m-3" role="alert" v-html="infoText">
|
||||
</div>
|
||||
<component
|
||||
:is="typeComponent"
|
||||
class="card-body"
|
||||
v-model:status="status"
|
||||
:prestudent-id="prestudentId"
|
||||
:studierendenantrag-id="studierendenantragId"
|
||||
@setInfos="$emit('update:infoArray', $event)"
|
||||
@setStatus="$emit('update:statusMsg', $event.msg);$emit('update:statusSeverity', $event.severity)"
|
||||
>
|
||||
</component>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
import {CoreFetchCmpt} from '../../Fetch.js';
|
||||
|
||||
var _uuid = 0;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFetchCmpt
|
||||
},
|
||||
emits: [
|
||||
'setInfos',
|
||||
'setStatus'
|
||||
],
|
||||
props: {
|
||||
prestudentId: Number,
|
||||
studierendenantragId: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: null,
|
||||
saving: false,
|
||||
errors: {
|
||||
grund: [],
|
||||
default: []
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
statusSeverity() {
|
||||
switch (this.data.status)
|
||||
{
|
||||
case 'Erstellt': return 'info';
|
||||
case 'Genehmigt': return 'success';
|
||||
default: return 'info';
|
||||
}
|
||||
},
|
||||
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;
|
||||
if (this.data.status) {
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
);
|
||||
},
|
||||
createAntrag() {
|
||||
bootstrap.Modal.getOrCreateInstance(this.$refs.modal).hide();
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_saving')})),
|
||||
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;
|
||||
}
|
||||
);
|
||||
},
|
||||
cancelAntrag() {
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_cancelling')})),
|
||||
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'
|
||||
});
|
||||
}
|
||||
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.saving = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.uuid = _uuid++;
|
||||
},
|
||||
template: `
|
||||
<div class="studierendenantrag-form-abmeldung">
|
||||
<core-fetch-cmpt :api-function="load">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div v-for="error in errors.default" class="alert alert-danger" role="alert" v-html="error">
|
||||
</div>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'studiengang')}}</th>
|
||||
<td align="right">{{data.bezeichnung}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'organisationsform')}}</th>
|
||||
<td align="right">{{data.orgform_bezeichnung}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('projektarbeitsbeurteilung', 'nameStudierende')}}</th>
|
||||
<td align="right">{{data.name}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('person', 'personenkennzeichen')}}</th>
|
||||
<td align="right">{{data.matrikelnr}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'studienjahr')}}</th>
|
||||
<td align="right">{{data.studienjahr_kurzbz}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'studiensemester')}}</th>
|
||||
<td align="right">{{data.studiensemester_kurzbz}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'semester')}}</th>
|
||||
<td align="right">{{data.semester}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div v-if="data.grund" class="mb-3">
|
||||
<h5>{{$p.t('studierendenantrag', 'antrag_grund')}}:</h5>
|
||||
<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'"
|
||||
rows="5"
|
||||
:disabled="saving"
|
||||
ref="grund"
|
||||
required
|
||||
></textarea>
|
||||
<div v-if="errors.grund.length" class="invalid-feedback">
|
||||
{{errors.grund.join(".")}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 text-end">
|
||||
<button
|
||||
v-if="data.studierendenantrag_id && data.canCancel"
|
||||
type="button"
|
||||
class="btn btn-danger"
|
||||
@click="cancelAntrag"
|
||||
:disabled="saving"
|
||||
>
|
||||
{{$p.t('studierendenantrag', 'btn_cancel')}}
|
||||
</button>
|
||||
<button
|
||||
v-else-if="!data.studierendenantrag_id"
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
data-bs-toggle="modal"
|
||||
:data-bs-target="'#studierendenantrag-form-abmeldung-' + uuid + '-modal'"
|
||||
:disabled="saving"
|
||||
>
|
||||
{{$p.t('studierendenantrag', 'btn_create_Abmeldung')}}
|
||||
</button>
|
||||
|
||||
<div
|
||||
ref="modal"
|
||||
class="modal fade text-start"
|
||||
:id="'studierendenantrag-form-abmeldung-' + uuid + '-modal'"
|
||||
tabindex="-1"
|
||||
:aria-labelledby="'studierendenantrag-form-abmeldung-' + uuid + '-modal-label'"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5
|
||||
class="modal-title"
|
||||
:id="'studierendenantrag-form-abmeldung-' + uuid + '-modal-label'"
|
||||
>
|
||||
{{$p.t('studierendenantrag', 'title_Abmeldung')}}
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" :aria-label="$p.t('ui', 'schliessen')"></button>
|
||||
</div>
|
||||
<div class="modal-body" v-html="$p.t('studierendenantrag', 'warning_Abmeldung')">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
@click="createAntrag">
|
||||
{{$p.t('studierendenantrag', 'btn_create_Abmeldung')}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-slot:error="{errorMessage}">
|
||||
<div class="alert alert-danger m-0" role="alert">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</template>
|
||||
</core-fetch-cmpt>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
import {CoreFetchCmpt} from '../../Fetch.js';
|
||||
|
||||
var _uuid = 0;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFetchCmpt
|
||||
},
|
||||
emits: [
|
||||
'setInfos',
|
||||
'setStatus'
|
||||
],
|
||||
props: {
|
||||
prestudentId: Number,
|
||||
studierendenantragId: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: null,
|
||||
saving: false,
|
||||
errors: {
|
||||
grund: [],
|
||||
default: []
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
statusSeverity() {
|
||||
switch (this.data?.status)
|
||||
{
|
||||
case 'Erstellt': return 'info';
|
||||
case 'Genehmigt': return 'success';
|
||||
default: return 'info';
|
||||
}
|
||||
},
|
||||
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;
|
||||
if (this.data.status) {
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
);
|
||||
},
|
||||
createAntrag() {
|
||||
bootstrap.Modal.getOrCreateInstance(this.$refs.modal).hide();
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_saving')})),
|
||||
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 = '';
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.uuid = _uuid++;
|
||||
},
|
||||
template: `
|
||||
<div class="studierendenantrag-form-abmeldung">
|
||||
<core-fetch-cmpt :api-function="load">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'studiengang')}}</th>
|
||||
<td align="right">{{data.bezeichnung}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'organisationsform')}}</th>
|
||||
<td align="right">{{data.orgform_bezeichnung}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('projektarbeitsbeurteilung', 'nameStudierende')}}</th>
|
||||
<td align="right">{{data.name}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('person', 'personenkennzeichen')}}</th>
|
||||
<td align="right">{{data.matrikelnr}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'studienjahr')}}</th>
|
||||
<td align="right">{{data.studienjahr_kurzbz}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'studiensemester')}}</th>
|
||||
<td align="right">{{data.studiensemester_kurzbz}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'semester')}}</th>
|
||||
<td align="right">{{data.semester}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div v-if="data.grund" class="mb-3">
|
||||
<h5>{{$p.t('studierendenantrag', 'antrag_grund')}}:</h5>
|
||||
|
||||
<pre class="text-prewrap">{{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>
|
||||
<div class="mb-2">
|
||||
<select name="grundAv" @change="appendDropDownText
|
||||
($event)">
|
||||
<option value="" > --- bitte auswählen, sofern zutreffend ---- </option>
|
||||
<option value="textLong_NichtantrittStudium">{{$p.t('studierendenantrag', 'dropdown_NichtantrittStudium')}}
|
||||
</option>
|
||||
<option value="textLong_ungenuegendeLeistung">{{$p.t('studierendenantrag', 'dropdown_ungenuegendeLeistung')}}
|
||||
</option>
|
||||
<option value="textLong_studentNichtAnwesend">{{$p.t('studierendenantrag', 'dropdown_nichtAnwesend')}}
|
||||
</option>
|
||||
<option value="textLong_PruefunstermineNichtEingehalten">{{$p.t('studierendenantrag', 'dropdown_PruefunstermineNichtEingehalten')}}
|
||||
</option>
|
||||
<option value="textLong_studentNichtGezahlt">{{$p.t('studierendenantrag', 'dropdown_nichtGezahlt')}}
|
||||
</option>
|
||||
<option value="textLong_plageat">{{$p.t('studierendenantrag', 'dropdown_plageat')}}
|
||||
</option>
|
||||
<option value="textLong_MissingZgv">{{$p.t('studierendenantrag', 'dropdown_MissingZgv')}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<textarea
|
||||
class="form-control"
|
||||
:class="{'is-invalid': errors.grund.length}"
|
||||
: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>
|
||||
</div>
|
||||
|
||||
<div class="col-12 text-end">
|
||||
<button
|
||||
v-if="!data?.studierendenantrag_id"
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
data-bs-toggle="modal"
|
||||
:data-bs-target="'#studierendenantrag-form-abmeldung-' + uuid + '-modal'"
|
||||
:disabled="saving"
|
||||
>
|
||||
{{$p.t('studierendenantrag', 'btn_create_Abmeldung')}}
|
||||
</button>
|
||||
<div
|
||||
ref="modal"
|
||||
class="modal fade text-start"
|
||||
:id="'studierendenantrag-form-abmeldung-' + uuid + '-modal'"
|
||||
tabindex="-1"
|
||||
:aria-labelledby="'studierendenantrag-form-abmeldung-' + uuid + '-modal-label'"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5
|
||||
class="modal-title"
|
||||
:id="'studierendenantrag-form-abmeldung-' + uuid + '-modal-label'"
|
||||
>
|
||||
{{$p.t('studierendenantrag', 'title_Abmeldung')}}
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" :aria-label="$p.t('ui', 'schliessen')"></button>
|
||||
</div>
|
||||
<div class="modal-body" v-html="$p.t('studierendenantrag', 'warning_AbmeldungStgl')">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
@click="createAntrag">
|
||||
{{$p.t('studierendenantrag', 'btn_create_Abmeldung')}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-slot:error="{errorMessage}">
|
||||
<div class="alert alert-danger m-0" role="alert">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</template>
|
||||
</core-fetch-cmpt>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
import {CoreFetchCmpt} from '../../Fetch.js';
|
||||
import VueDatepicker from '../../vueDatepicker.js.php';
|
||||
|
||||
var _uuid = 0;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFetchCmpt,
|
||||
VueDatepicker
|
||||
},
|
||||
emits: [
|
||||
'setInfos',
|
||||
'setStatus'
|
||||
],
|
||||
props: {
|
||||
prestudentId: Number,
|
||||
studierendenantragId: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: null,
|
||||
saving: false,
|
||||
errors: {
|
||||
grund: [],
|
||||
studiensemester: [],
|
||||
datum_wiedereinstieg: [],
|
||||
default: []
|
||||
},
|
||||
stsem: null,
|
||||
currentWiedereinstieg: '',
|
||||
siteUrl: FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
statusSeverity() {
|
||||
switch (this.data.status)
|
||||
{
|
||||
case 'Erstellt': return 'info';
|
||||
case 'Genehmigt': return 'success';
|
||||
case 'Zurueckgezogen': return 'danger';
|
||||
default: return 'info';
|
||||
}
|
||||
},
|
||||
loadUrl() {
|
||||
if (this.studierendenantragId)
|
||||
return '/components/Antrag/Unterbrechung/getDetailsForAntrag/'+
|
||||
this.studierendenantragId;
|
||||
return '/components/Antrag/Unterbrechung/getDetailsForNewAntrag/' +
|
||||
this.prestudentId;
|
||||
},
|
||||
datumWsFormatted() {
|
||||
let datumUnformatted = '';
|
||||
|
||||
if (this.data.datum_wiedereinstieg) {
|
||||
datumUnformatted = this.data.datum_wiedereinstieg;
|
||||
} else {
|
||||
if (this.stsem !== null && this.data.studiensemester[this.stsem].wiedereinstieg)
|
||||
datumUnformatted = this.data.studiensemester[this.stsem].wiedereinstieg;
|
||||
}
|
||||
if (datumUnformatted === '')
|
||||
return datumUnformatted;
|
||||
let datum = new Date(datumUnformatted);
|
||||
return datum.toLocaleDateString();
|
||||
}
|
||||
},
|
||||
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) {
|
||||
this.$emit("setStatus", {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.data.statustyp})),
|
||||
severity: this.statusSeverity
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
);
|
||||
},
|
||||
createAntrag() {
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_saving')})),
|
||||
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);
|
||||
|
||||
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'
|
||||
});
|
||||
}
|
||||
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.saving = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
cancelAntrag() {
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_cancelling')})),
|
||||
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.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_error')})),
|
||||
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;
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.uuid = _uuid++;
|
||||
},
|
||||
template: `
|
||||
<div class="studierendenantrag-form-unterbrechung">
|
||||
<core-fetch-cmpt :api-function="load">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div v-for="error in errors.default" class="alert alert-danger" role="alert" v-html="error">
|
||||
</div>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'studiengang')}}</th>
|
||||
<td align="right">{{data.bezeichnung}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'organisationsform')}}</th>
|
||||
<td align="right">{{data.orgform_bezeichnung}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('projektarbeitsbeurteilung', 'nameStudierende')}}</th>
|
||||
<td align="right">{{data.name}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('person', 'personenkennzeichen')}}</th>
|
||||
<td align="right">{{data.matrikelnr}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'studienjahr')}}</th>
|
||||
<td align="right" v-if="data.studierendenantrag_id">{{data.studienjahr_kurzbz}}</td>
|
||||
<td align="right" v-else>{{stsem === null ? '' : data.studiensemester[stsem].studienjahr_kurzbz}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'semester')}}</th>
|
||||
<td align="right" v-if="data.studierendenantrag_id">{{data.semester}}</td>
|
||||
<td align="right" v-else>{{stsem === null ? '' : data.studiensemester[stsem].semester}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</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">
|
||||
{{stsem.studiensemester_kurzbz}}
|
||||
</option>
|
||||
</select>
|
||||
<div v-if="errors.studiensemester.length" class="invalid-feedback">
|
||||
{{errors.studiensemester.join(".")}}
|
||||
</div>
|
||||
</div>
|
||||
</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">
|
||||
{{sem.studiensemester_kurzbz}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div v-if="errors.datum_wiedereinstieg.length" class="invalid-feedback d-block">
|
||||
{{errors.datum_wiedereinstieg.join(".")}}
|
||||
</div>
|
||||
</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'"
|
||||
rows="5"
|
||||
:disabled="saving"
|
||||
ref="grund"
|
||||
required
|
||||
></textarea>
|
||||
<div v-if="errors.grund.length" class="invalid-feedback">
|
||||
{{errors.grund.join(".")}}
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
<div class="col-12 text-end">
|
||||
<button
|
||||
v-if="!data.studierendenantrag_id"
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
@click="createAntrag"
|
||||
:disabled="saving"
|
||||
>
|
||||
{{$p.t('studierendenantrag', 'btn_create_Unterbrechung')}}
|
||||
</button>
|
||||
<button
|
||||
v-else-if="data.status == 'Erstellt'"
|
||||
type="button"
|
||||
class="btn btn-danger"
|
||||
@click="cancelAntrag"
|
||||
:disabled="saving"
|
||||
>
|
||||
{{$p.t('studierendenantrag', 'btn_cancel')}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<template v-slot:error="{errorMessage}">
|
||||
<div class="alert alert-danger m-0" role="alert">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</template>
|
||||
</core-fetch-cmpt>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
import {CoreFetchCmpt} from '../../Fetch.js';
|
||||
import VueDatepicker from '../../vueDatepicker.js.php';
|
||||
|
||||
var _uuid = 0;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFetchCmpt,
|
||||
VueDatepicker
|
||||
},
|
||||
emits: [
|
||||
'setInfos',
|
||||
'setStatus',
|
||||
'update:status'
|
||||
],
|
||||
props: {
|
||||
status: String,
|
||||
prestudentId: Number,
|
||||
studierendenantragId: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: null,
|
||||
saving: false,
|
||||
errors: {
|
||||
grund: [],
|
||||
default: []
|
||||
},
|
||||
siteUrl: FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router,
|
||||
infos: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
statusSeverity() {
|
||||
switch (this.data.status)
|
||||
{
|
||||
case 'Erstellt': return 'info';
|
||||
case 'Genehmigt': return 'success';
|
||||
case 'Verzichtet': return 'danger';
|
||||
default: return 'info';
|
||||
}
|
||||
},
|
||||
loadUrl() {
|
||||
return '/components/Antrag/Wiederholung/getDetailsForNewAntrag/' +
|
||||
this.prestudentId;
|
||||
},
|
||||
datumPruefungFormatted() {
|
||||
if(!this.data.pruefungsdatum)
|
||||
return '';
|
||||
let datum = new Date(this.data.pruefungsdatum);
|
||||
return datum.toLocaleDateString();
|
||||
}
|
||||
},
|
||||
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 || this.data.status == 'ErsteAufforderungVersandt' || this.data.status == 'ZweiteAufforderungVersandt') {
|
||||
this.data.status = 'Offen';
|
||||
this.data.statustyp = this.$p.t('studierendenantrag', 'status_open');
|
||||
}
|
||||
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
|
||||
});
|
||||
return result;
|
||||
}
|
||||
);
|
||||
},
|
||||
createAntrag() {
|
||||
this.createAntragWithStatus(true);
|
||||
},
|
||||
cancelAntrag() {
|
||||
this.createAntragWithStatus(false);
|
||||
},
|
||||
createAntragWithStatus(repeat) {
|
||||
let func = repeat ? 'createAntrag' : 'cancelAntrag';
|
||||
let nextState = repeat ? 'Erstellt' : 'Verzichtet';
|
||||
|
||||
this.$emit('setStatus', {
|
||||
msg: Vue.computed(() => this.$p.t('studierendenantrag', 'status_x', {status: this.$p.t('studierendenantrag', 'status_saving')})),
|
||||
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.saving = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.uuid = _uuid++;
|
||||
},
|
||||
mounted() {
|
||||
this.infos = [...Array(5).keys()].map(n => ({
|
||||
body: Vue.computed(() => this.$p.t('studierendenantrag', 'infotext_Wiederholung_' + n))
|
||||
}));
|
||||
this.$emit('setInfos', this.infos);
|
||||
},
|
||||
template: `
|
||||
<div class="studierendenantrag-form-wiederholung">
|
||||
<core-fetch-cmpt :api-function="load">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div v-for="error in errors.default" class="alert alert-danger" role="alert" v-html="error">
|
||||
</div>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'studiengang')}}</th>
|
||||
<td align="right">{{data.bezeichnung}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('lehre', 'organisationsform')}}</th>
|
||||
<td align="right">{{data.orgform_bezeichnung}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('projektarbeitsbeurteilung', 'nameStudierende')}}</th>
|
||||
<td align="right">{{data.name}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('person', 'personenkennzeichen')}}</th>
|
||||
<td align="right">{{data.matrikelnr}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('studierendenantrag', 'antrag_Wiederholung_pruefung')}}</th>
|
||||
<td align="right">{{data.lvbezeichnung}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{$p.t('studierendenantrag', 'antrag_Wiederholung_pruefung_date')}}</th>
|
||||
<td align="right">{{datumPruefungFormatted}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="col-12 d-flex justify-content-end gap-2">
|
||||
<button
|
||||
v-if="!data.studierendenantrag_id || data.status == 'Offen'"
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
@click="createAntrag"
|
||||
:disabled="saving"
|
||||
>
|
||||
{{$p.t('studierendenantrag/antrag_Wiederholung_button_yes')}}
|
||||
</button>
|
||||
<!-- <button
|
||||
v-if="!data.studierendenantrag_id || data.status == 'Offen'"
|
||||
type="button"
|
||||
class="btn btn-danger"
|
||||
@click="cancelAntrag"
|
||||
:disabled="saving"
|
||||
>
|
||||
{{$p.t('studierendenantrag/antrag_Wiederholung_button_no')}}
|
||||
</button>-->
|
||||
</div>
|
||||
</div>
|
||||
<template v-slot:error="{errorMessage}">
|
||||
<div class="alert alert-danger m-0" role="alert">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</template>
|
||||
</core-fetch-cmpt>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
export default {
|
||||
props: {
|
||||
infos: Array,
|
||||
},
|
||||
template: `
|
||||
<div class="studierendenantrag-infoblock">
|
||||
<div v-for="(info, index) in infos" :key="index" class="alert d-flex align-items-center" :class="'alert-' + (info.severity || 'info')" role="alert">
|
||||
<i class="fa fa-lg flex-shrink-0 me-3" :class="'fa-' + (info.icon || 'info-circle')" role="img" aria-label="Info:"></i>
|
||||
<div v-if="info.title">
|
||||
<h4 v-if="info.title" class="alert-heading">
|
||||
{{info.title}}
|
||||
</h4>
|
||||
<p class="mb-0" v-html="info.body"></p>
|
||||
</div>
|
||||
<p v-else class="mb-0" v-html="info.body"></p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
import LeitungHeader from './Leitung/Header.js';
|
||||
import LeitungActions from './Leitung/Actions.js';
|
||||
import LeitungTable from './Leitung/Table.js';
|
||||
import GrundPopup from './Leitung/GrundPopup.js';
|
||||
import LvPopup from './Leitung/LvPopup.js';
|
||||
import BsAlert from '../Bootstrap/Alert.js';
|
||||
import FhcLoader from '../Loader.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LeitungHeader,
|
||||
LeitungTable,
|
||||
LeitungActions,
|
||||
FhcLoader
|
||||
},
|
||||
props: {
|
||||
stgL: Array,
|
||||
stgA: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filter: undefined,
|
||||
selectedData: [],
|
||||
columns: [],
|
||||
stgs: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
stgkzL() {
|
||||
if (!this.stgL)
|
||||
return [];
|
||||
return this.stgL.map(stg => parseInt(stg));
|
||||
},
|
||||
stgkzA() {
|
||||
if (!this.stgA)
|
||||
return [];
|
||||
return this.stgA.map(stg => parseInt(stg));
|
||||
}
|
||||
},
|
||||
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 = Object.values(result.data.retval).sort((a,b) => a.bezeichnung == b.bezeichnung ? (a.orgform == b.orgform ? 0 : (a.orgform > b.orgform ? 1 : -1)) : (a.bezeichnung > b.bezeichnung ? 1 : -1));
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
},
|
||||
changeFilter(evt) {
|
||||
this.filter = evt.target.value || undefined;
|
||||
this.reload();
|
||||
},
|
||||
reload() {
|
||||
this.$refs.table.reload(this.filter);
|
||||
this.loadFilter();
|
||||
},
|
||||
download() {
|
||||
this.$refs.table.download();
|
||||
},
|
||||
actionApprove(evt, oks) {
|
||||
var antraege = evt || [...this.selectedData];
|
||||
|
||||
if (!oks) {
|
||||
oks = [];
|
||||
}
|
||||
var currentAntrag = antraege.shift();
|
||||
if (currentAntrag) {
|
||||
if (currentAntrag.typ != 'Wiederholung')
|
||||
{
|
||||
oks.push(currentAntrag);
|
||||
this.actionApprove(antraege, oks);
|
||||
}
|
||||
else
|
||||
{
|
||||
let countAntrage = 0;
|
||||
LvPopup
|
||||
.popup(this.$p.t('studierendenantrag','title_show_lvs', currentAntrag), {
|
||||
antragId: currentAntrag.studierendenantrag_id,
|
||||
footer: true,
|
||||
dialogClass: 'modal-lg',
|
||||
countRemaining : antraege.filter(antrag => antrag.typ == 'Wiederholung').length
|
||||
})
|
||||
.then(result => {
|
||||
if (result[0]) {
|
||||
oks.push(currentAntrag);
|
||||
if (result[1])
|
||||
while (antraege.length)
|
||||
oks.push(antraege.pop());
|
||||
} else if (result[1]) {
|
||||
while (antraege.length) {
|
||||
currentAntrag = antraege.pop();
|
||||
if (currentAntrag.typ != 'Wiederholung')
|
||||
oks.push(currentAntrag);
|
||||
}
|
||||
}
|
||||
this.actionApprove(antraege, oks);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
},
|
||||
actionReject(evt, gruende) {
|
||||
var antraege = evt || this.selectedData;
|
||||
if (!gruende)
|
||||
gruende = [];
|
||||
var currentAntrag = antraege.pop();
|
||||
if (currentAntrag) {
|
||||
GrundPopup
|
||||
.popup(this.$p.t('studierendenantrag', 'title_grund', {id: currentAntrag.studierendenantrag_id}), {
|
||||
countRemaining: antraege.length
|
||||
})
|
||||
.then(result => {
|
||||
currentAntrag.grund = result[0];
|
||||
gruende.push(currentAntrag);
|
||||
if (result[1])
|
||||
{
|
||||
while (antraege.length)
|
||||
{
|
||||
currentAntrag = antraege.pop();
|
||||
currentAntrag.grund = result[0];
|
||||
gruende.push(currentAntrag);
|
||||
}
|
||||
}
|
||||
this.actionReject(antraege, gruende);
|
||||
})
|
||||
.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);
|
||||
}
|
||||
},
|
||||
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);
|
||||
},
|
||||
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);
|
||||
},
|
||||
actionoObjectionDeny(evt, gruende) {
|
||||
var antraege = evt || this.selectedData;
|
||||
if (!gruende)
|
||||
gruende = [];
|
||||
var currentAntrag = antraege.pop();
|
||||
if (currentAntrag) {
|
||||
GrundPopup
|
||||
.popup(this.$p.t('studierendenantrag', 'title_grund', {id: currentAntrag.studierendenantrag_id}), {
|
||||
countRemaining : antraege.length,
|
||||
optional: true
|
||||
})
|
||||
.then(result => {
|
||||
currentAntrag.grund = result[0];
|
||||
gruende.push(currentAntrag);
|
||||
if (result[1]) {
|
||||
while (antraege.length) {
|
||||
currentAntrag = antraege.pop();
|
||||
currentAntrag.grund = result[0];
|
||||
gruende.push(currentAntrag);
|
||||
}
|
||||
}
|
||||
this.actionoObjectionDeny(antraege, gruende);
|
||||
})
|
||||
.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);
|
||||
}
|
||||
},
|
||||
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);
|
||||
},
|
||||
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);
|
||||
},
|
||||
showValidation(results) {
|
||||
var errors = results.filter(res => res.data.error);
|
||||
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'});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadFilter();
|
||||
},
|
||||
template: `
|
||||
<div class="studierendenantrag-leitung fhc-table">
|
||||
<leitung-header
|
||||
:stgs="stgs"
|
||||
@input="changeFilter"
|
||||
>
|
||||
</leitung-header>
|
||||
|
||||
<leitung-actions
|
||||
:stg-a="stgkzA"
|
||||
:stg-l="stgkzL"
|
||||
:selectedData="selectedData"
|
||||
:columns="columns"
|
||||
@reload="reload"
|
||||
@action:approve="actionApprove"
|
||||
@action:reject="actionReject"
|
||||
@action:reopen="actionReopen"
|
||||
@download="download"
|
||||
>
|
||||
</leitung-actions>
|
||||
|
||||
<leitung-table
|
||||
ref="table"
|
||||
:stg-a="stgkzA"
|
||||
:stg-l="stgkzL"
|
||||
v-model:columnData="columns"
|
||||
v-model:selectedData="selectedData"
|
||||
@action:approve="actionApprove"
|
||||
@action:reject="actionReject"
|
||||
@action:reopen="actionReopen"
|
||||
@action:object="actionObject"
|
||||
@action:objectionDeny="actionoObjectionDeny"
|
||||
@action:objectionApprove="actionObjectionApprove"
|
||||
@action:cancel="actionCancel"
|
||||
@reload="reload"
|
||||
>
|
||||
</leitung-table>
|
||||
|
||||
<fhc-loader ref="loader"></fhc-loader>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import ActionsNew from './Actions/New.js';
|
||||
import ActionsColumns from './Actions/Columns.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ActionsNew,
|
||||
ActionsColumns
|
||||
},
|
||||
props: {
|
||||
selectedData: Array,
|
||||
columns: Array,
|
||||
stgL: Array,
|
||||
stgA: Array
|
||||
},
|
||||
emits: [
|
||||
'reload',
|
||||
'download',
|
||||
'action:approve',
|
||||
'action:reject',
|
||||
'action:reopen'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
currentStudent: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selectedCanBeApproved() {
|
||||
if (!this.selectedData.length)
|
||||
return false;
|
||||
if (!this.selectedData.every(val => this.stgL.includes(val.studiengang_kz)))
|
||||
return false;
|
||||
return this.selectedData.filter(row => {
|
||||
return (row.typ == 'Wiederholung' && row.status == 'Lvszugewiesen') || (row.typ != 'Wiederholung' && (row.status == 'Erstellt' || row.status == 'ErstelltStgl'));
|
||||
}).length == this.selectedData.length;
|
||||
},
|
||||
selectedCanBeRejected() {
|
||||
if (!this.selectedData.length)
|
||||
return false;
|
||||
if (!this.selectedData.every(val => this.stgL.includes(val.studiengang_kz)))
|
||||
return false;
|
||||
return this.selectedData.filter(row => {
|
||||
return (row.typ == 'Unterbrechung' && row.status == 'Erstellt');
|
||||
}).length == this.selectedData.length;
|
||||
},
|
||||
selectedCanBeReopened() {
|
||||
if (!this.selectedData.length)
|
||||
return false;
|
||||
if (!this.selectedData.every(val => this.stgA.includes(val.studiengang_kz)))
|
||||
return false;
|
||||
return this.selectedData.filter(row => {
|
||||
return (row.typ == 'Wiederholung' && row.status == 'Verzichtet');
|
||||
}).length == this.selectedData.length;
|
||||
},
|
||||
newUrl() {
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + this.currentStudent;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hideModal() {
|
||||
bootstrap.Modal.getInstance(this.$refs.modal).hide();
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="studierendenantrag-leitung-actions fhc-table-actions d-flex flex-wrap justify-content-between mb-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<actions-new @reload="$emit('reload')"></actions-new>
|
||||
<button type="button" class="btn btn-outline-secondary" @click="$emit('reload')" :title="$p.t('table','reload')">
|
||||
<i class="fa-solid fa-rotate-right"></i>
|
||||
</button>
|
||||
<span>{{$p.t('table', 'with_selected', {count: selectedData.length})}}</span>
|
||||
<button v-if="stgL.length" :disabled="!selectedCanBeApproved" type="button" class="btn btn-outline-secondary" @click="$emit('action:approve')">{{$p.t('studierendenantrag', 'btn_approve')}}</button>
|
||||
<button v-if="stgL.length" :disabled="!selectedCanBeRejected" type="button" class="btn btn-outline-secondary" @click="$emit('action:reject')">{{$p.t('studierendenantrag', 'btn_reject')}}</button>
|
||||
<button v-if="stgA.length" :disabled="!selectedCanBeReopened" type="button" class="btn btn-outline-secondary" @click="$emit('action:reopen')">{{$p.t('studierendenantrag', 'btn_reopen')}}</button>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="btn btn-link" data-bs-toggle="collapse" href="#columns" :title="$p.t('table','spaltenEinAusblenden')"><i class="fa fa-table-columns"></i></button>
|
||||
<button type="button" class="btn btn-link" @click="$emit('download')" :title="$p.t('table','download')"><i class="fa fa-download"></i></button>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<actions-columns id="columns" class="collapse" :columns="columns"></actions-columns>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
export default {
|
||||
props: {
|
||||
columns: Array
|
||||
},
|
||||
methods: {
|
||||
toggleColumn(col) {
|
||||
col.visible = !col.visible;
|
||||
col.original.toggle()
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="studierendenantrag-leitung-actions-columns">
|
||||
<div class="card mt-3">
|
||||
<div class="card-body d-flex flex-wrap gap-2 justify-content-center">
|
||||
<a v-for="col in columns" class="btn" :class="col.visible ? 'btn-dark' : 'btn-outline-dark'" href="#" @click.prevent="toggleColumn(col)">
|
||||
{{col.title}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import BsAlert from '../../../Bootstrap/Alert.js';
|
||||
import BsModal from '../../../Bootstrap/Modal.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BsModal,
|
||||
AutoComplete: primevue.autocomplete
|
||||
},
|
||||
emits: [
|
||||
'reload'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
data: [],
|
||||
student: '',
|
||||
abortController: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
newUrl() {
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/lehre/Studierendenantrag/abmeldungStgl/' + this.student.prestudent_id;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openForm() {
|
||||
bootstrap.Modal.getInstance(this.$refs.modal).hide();
|
||||
BsModal.popup(Vue.h('iframe', {
|
||||
src: this.newUrl,
|
||||
class: 'position-absolute top-0 start-0 w-100 h-100'
|
||||
}), {
|
||||
dialogClass: 'modal-fullscreen'
|
||||
}, this.$p.t('studierendenantrag', 'antrag_header')).then(() => {
|
||||
this.$emit('reload');
|
||||
this.student = '';
|
||||
});
|
||||
},
|
||||
loadData(evt) {
|
||||
if (this.abortController)
|
||||
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(() => {});
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="studierendenantrag-leitung-actions-new" v-if="data">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#newAntragModal">
|
||||
<i class="fa fa-plus"></i>
|
||||
{{$p.t('studierendenantrag','btn_new')}}
|
||||
</button>
|
||||
<div ref="modal" class="modal fade" id="newAntragModal" tabindex="-1" aria-labelledby="newAntragModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="newAntragModalLabel">{{$p.t('studierendenantrag','title_new_Abmeldung')}}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" :aria-label="$p.t('ui','schliessen')"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<label for="newAntragModalAutoComplete">{{$p.t('person','studentIn')}}</label>
|
||||
<div>
|
||||
<auto-complete
|
||||
class="w-100"
|
||||
v-model="student"
|
||||
:suggestions="data"
|
||||
optionLabel = "name"
|
||||
@complete="loadData"
|
||||
inputId="newAntragModalAutoComplete"
|
||||
dropdown
|
||||
dropdown-mode="current"
|
||||
>
|
||||
<template #option="slotProps">
|
||||
<div :title="slotProps.option.prestudent_id">
|
||||
{{slotProps.option.name}}
|
||||
</div>
|
||||
</template>
|
||||
</auto-complete>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
:disabled="!this.student"
|
||||
@click.prevent="openForm">
|
||||
{{$p.t('studierendenantrag','btn_create')}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import BsAlert from '../../Bootstrap/Alert.js';
|
||||
|
||||
export default {
|
||||
mixins: [
|
||||
BsAlert,
|
||||
],
|
||||
props: {
|
||||
placeholder: String,
|
||||
default: String,
|
||||
countRemaining: Number,
|
||||
optional: Boolean
|
||||
},
|
||||
data: () => ({
|
||||
value: '',
|
||||
result: false,
|
||||
check: false,
|
||||
isInvalid: false
|
||||
}),
|
||||
methods: {
|
||||
submit() {
|
||||
if (!this.value && !this.optional) {
|
||||
this.isInvalid = true;
|
||||
} else {
|
||||
this.result = [this.value, this.check];
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.default)
|
||||
this.value = this.default;
|
||||
},
|
||||
popup(msg, options) {
|
||||
if (typeof options === 'string')
|
||||
options = { default: options };
|
||||
return BsAlert.popup.bind(this)(msg, options);
|
||||
},
|
||||
template: `<bs-modal ref="modalContainer" class="bootstrap-prompt" v-bind="$props">
|
||||
<template v-slot:title>
|
||||
<slot></slot>
|
||||
</template>
|
||||
<template v-slot:default>
|
||||
<div>
|
||||
<textarea ref="input" class="form-control" :class="{'is-invalid' : isInvalid}" v-model="value"></textarea>
|
||||
<div v-if="isInvalid" class="invalid-feedback">
|
||||
{{$p.t('kvp','new.error.required')}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<div v-if="countRemaining > 0" class="form-check flex-grow-1">
|
||||
<input ref="check" type="checkbox" class="form-check-input" id="cbid" v-model="check">
|
||||
<label class="form-check-label" for="cbid">{{$p.t('studierendenantrag','fuer_alle_uebernehmen')}}</label>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" @click="submit">{{$p.t('ui','ok')}}</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{$p.t('ui','cancel')}}</button>
|
||||
</template>
|
||||
</bs-modal>`
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export default {
|
||||
props: {
|
||||
stgs: Array
|
||||
},
|
||||
emits: [
|
||||
'input'
|
||||
],
|
||||
template: `
|
||||
<div class="studierendenantrag-leitung-header fhc-table-header d-flex align-items-center mb-2 gap-2">
|
||||
<h3 class="h5 col m-0">{{$p.t('studierendenantrag', 'studierendenantraege')}}</h3>
|
||||
<div v-if="stgs.length > 1" class="col-auto">
|
||||
<select ref="stg_select" class="form-select" @input="$emit('input', $event)">
|
||||
<option value="">{{$p.t('global', 'alle')}}</option>
|
||||
<option v-for="stg in stgs" :key="stg.studiengang_kz" :value="stg.studiengang_kz">
|
||||
{{stg.bezeichnung}} ({{stg.orgform}})
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
import BsAlert from '../../Bootstrap/Alert.js';
|
||||
import {CoreFetchCmpt} from "../../Fetch.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFetchCmpt
|
||||
},
|
||||
mixins: [
|
||||
BsAlert
|
||||
],
|
||||
props: {
|
||||
footer: Boolean,
|
||||
antragId: Number,
|
||||
countRemaining: Number
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
lvs: null,
|
||||
refresh: true,
|
||||
result: false,
|
||||
check: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
lvzugelassen() {
|
||||
let zwischen = {};
|
||||
for (let k in this.lvs){
|
||||
zwischen[k] = this.lvs[k] ? this.lvs[k].filter(lv=>lv.antrag_zugelassen) : null;
|
||||
}
|
||||
return zwischen;
|
||||
},
|
||||
lvzugelassenLength() {
|
||||
return Object.values(this.lvzugelassen).reduce((result, current) => result + (current ? current.length : 0), 0);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setlvs(param) {
|
||||
if(param.error)
|
||||
{
|
||||
this.$refs.fetchCompt.error = true;
|
||||
this.$refs.fetchCompt.errorMessage = param.retval;
|
||||
}
|
||||
else
|
||||
this.lvs = param.retval;
|
||||
},
|
||||
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);
|
||||
},
|
||||
submit(result) {
|
||||
this.result = [result, this.check];
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
antragId() {
|
||||
Vue.nextTick(() => {
|
||||
this.refresh = !this.refresh;
|
||||
});
|
||||
}
|
||||
},
|
||||
popup(msg, options) {
|
||||
if (typeof options === 'string')
|
||||
options = { default: options };
|
||||
return BsAlert.popup.bind(this)(msg, options);
|
||||
},
|
||||
template: `<bs-modal ref="modalContainer" class="bootstrap-prompt" v-bind="$props" dialog-class="modal-lg">
|
||||
<template v-slot:title>
|
||||
<slot></slot>
|
||||
</template>
|
||||
<template v-slot:default>
|
||||
<core-fetch-cmpt
|
||||
ref="fetchCompt"
|
||||
:refresh="refresh"
|
||||
:api-function="loadlvs"
|
||||
@data-fetched="setlvs">
|
||||
<template #default>
|
||||
<div v-if="lvzugelassenLength == 0">
|
||||
{{$p.t('studierendenantrag','error_no_lvs')}}
|
||||
</div>
|
||||
<table v-else class="table caption-top" v-for="(lv_arr, sem) in lvzugelassen" :key="sem">
|
||||
<caption>
|
||||
<span class="d-flex justify-content-between">
|
||||
<span>{{ $p.t('studierendenantrag',['title_lv_nicht_zugelassen', 'title_lv_wiederholen'][sem.substr(0,1)-1]) }}</span>
|
||||
<span>{{sem.substr(1)}}</span>
|
||||
</span>
|
||||
</caption>
|
||||
<thead v-if="lv_arr !== null">
|
||||
<tr>
|
||||
<th scope="col">{{$p.t('ui','bezeichnung')}}</th>
|
||||
<th scope="col">{{$p.t('lehre','lehrform')}}</th>
|
||||
<th scope="col">ECTS</th>
|
||||
<th scope="col">{{$p.t('lehre','note')}}</th>
|
||||
<th scope="col">
|
||||
{{$p.t('global','anmerkung')}}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="lv_arr === null" class="table-warning">
|
||||
<td colspan="5">{{$p.t('studierendenantrag/error_stg_last_semester')}}</td>
|
||||
</tr>
|
||||
<template v-else>
|
||||
<tr v-for="lv in lv_arr">
|
||||
<td>
|
||||
<label class="w-100 m-1" :for="'flexSwitchCheckLv_' + lv.lehrveranstaltung_id">
|
||||
{{lv.bezeichnung}}
|
||||
</label>
|
||||
</td>
|
||||
<td>
|
||||
<div class="m-1">
|
||||
{{lv.lehrform_kurzbz}}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="m-1">
|
||||
{{lv.ects}}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="m-1">
|
||||
{{lv.note || '---'}}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="m-1">
|
||||
{{lv.antrag_anmerkung}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
</core-fetch-cmpt>
|
||||
</template>
|
||||
<template v-if="footer" v-slot:footer>
|
||||
<div v-if="countRemaining > 0" class="form-check flex-grow-1">
|
||||
<input ref="check" type="checkbox" class="form-check-input" id="cbid" v-model="check">
|
||||
<label class="form-check-label" for="cbid">{{$p.t('studierendenantrag','fuer_x_uebernehmen', {count: countRemaining})}}</label>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" @click="submit(true)">{{$p.t('studierendenantrag','btn_approve')}}</button>
|
||||
<button v-if="countRemaining > 0" type="button" class="btn btn-secondary" @click="submit(false)">{{$p.t('ui','skip')}}</button>
|
||||
</template>
|
||||
</bs-modal>`
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
import BsModal from '../../Bootstrap/Modal.js';
|
||||
import {CoreFetchCmpt} from '../../Fetch.js';
|
||||
import LvPopup from './LvPopup.js';
|
||||
import { dateFilter } from '../../../tabulator/filters/Dates.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BsModal,
|
||||
CoreFetchCmpt,
|
||||
LvPopup
|
||||
},
|
||||
props: {
|
||||
selectedData: Array,
|
||||
columnData: Array,
|
||||
stgL: Array,
|
||||
stgA: Array
|
||||
},
|
||||
emits: [
|
||||
'update:columnData',
|
||||
'update:selectedData',
|
||||
'action:approve',
|
||||
'action:reject',
|
||||
'action:reopen',
|
||||
'action:object',
|
||||
'action:objectionDeny',
|
||||
'action:objectionApprove',
|
||||
'action:cancel'
|
||||
],
|
||||
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: [],
|
||||
lvsData: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reload(stg) {
|
||||
this.table.replaceData(this.ajaxUrl + (stg || ''));
|
||||
},
|
||||
download() {
|
||||
this.table.download("csv", "data.csv", {
|
||||
delimiter: ';',
|
||||
bom: true
|
||||
});
|
||||
},
|
||||
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);
|
||||
});
|
||||
},
|
||||
showHistoryGrund(grund) {
|
||||
this.$refs.modalGrund.$el.addEventListener(
|
||||
'hidden.bs.modal',
|
||||
this.$refs.history.show,
|
||||
{
|
||||
once: true
|
||||
}
|
||||
);
|
||||
this.$refs.modalGrundPre.innerHTML = grund;
|
||||
},
|
||||
showLVs(data) {
|
||||
this.lvsData = data;
|
||||
this.$refs.lvList.show();
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.$p.loadCategory(['lehre', 'studierendenantrag', 'person', 'global', 'ui']);
|
||||
function dateFormatter(cell)
|
||||
{
|
||||
let val = cell.getValue();
|
||||
if (!val)
|
||||
return '';
|
||||
let date = new Date(val);
|
||||
return date.toLocaleDateString();
|
||||
}
|
||||
|
||||
this.table = new Tabulator(this.$refs.table, {
|
||||
placeholder:"Keine zu bearbeitenden Datensätze",
|
||||
movableColumns: true,
|
||||
maxHeight: '50vh',
|
||||
layout: "fitDataFill",
|
||||
ajaxURL: this.ajaxUrl,
|
||||
persistence: { // NOTE(chris): do not store column titles
|
||||
sort: true, //persist column sorting
|
||||
filter: true, //persist filters
|
||||
headerFilter: true, //persist header filters
|
||||
group: true, //persist row grouping
|
||||
page: true, //persist page
|
||||
columns: ["width", "visible"], //persist columns
|
||||
},
|
||||
persistenceID: 'studierendenantrag_leitung_2023-11-14',
|
||||
columns: [{
|
||||
formatter: 'rowSelection',
|
||||
titleFormatter: 'rowSelection',
|
||||
titleFormatterParams: {
|
||||
rowRange: 'active'
|
||||
},
|
||||
hozAlign: 'center',
|
||||
headerSort: false
|
||||
}, {
|
||||
field: 'studierendenantrag_id',
|
||||
title: '#'
|
||||
}, {
|
||||
field: 'bezeichnung',
|
||||
title: this.$p.t('lehre', 'studiengang'),
|
||||
headerFilter: 'list',
|
||||
headerFilterParams: {
|
||||
valuesLookup: true,
|
||||
clearable: true,
|
||||
autocomplete: true,
|
||||
}
|
||||
}, {
|
||||
field: 'orgform',
|
||||
title: this.$p.t('lehre', 'organisationsform'),
|
||||
headerFilter: 'list',
|
||||
headerFilterParams: {
|
||||
valuesLookup: true,
|
||||
clearable: true,
|
||||
autocomplete: true,
|
||||
}
|
||||
}, {
|
||||
field: 'typ',
|
||||
title: this.$p.t('studierendenantrag', 'antrag_typ'),
|
||||
headerFilter: 'list',
|
||||
headerFilterParams: {
|
||||
valuesLookup: true,
|
||||
clearable: true,
|
||||
autocomplete: true,
|
||||
},
|
||||
formatter: (cell, formatterParams, onRendered) => {
|
||||
return this.$p.t('studierendenantrag','antrag_typ_' + cell.getValue());
|
||||
}
|
||||
}, {
|
||||
field: 'statustyp',
|
||||
title: this.$p.t('studierendenantrag', 'antrag_status'),
|
||||
headerFilter: 'list',
|
||||
headerFilterParams: {
|
||||
valuesLookup: true,
|
||||
clearable: true,
|
||||
autocomplete: true,
|
||||
},
|
||||
formatter: (cell, formatterParams, onRendered) => {
|
||||
let link = document.createElement('a');
|
||||
link.href = "#";
|
||||
link.innerHTML = cell.getValue();
|
||||
link.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
this.lastHistoryClickedId = cell.getData().studierendenantrag_id;
|
||||
this.$refs.historyLoader.fetchData();
|
||||
this.$refs.history.show();
|
||||
});
|
||||
|
||||
return link;
|
||||
}
|
||||
}, {
|
||||
field: 'matrikelnr',
|
||||
title: this.$p.t('person', 'personenkennzeichen'),
|
||||
headerFilter: 'input'
|
||||
}, {
|
||||
field: 'prestudent_id',
|
||||
title: this.$p.t('lehre', 'prestudent'),
|
||||
headerFilter: 'input'
|
||||
}, {
|
||||
field: 'name',
|
||||
title: this.$p.t('global', 'name'),
|
||||
mutator: (value, data) => (data.vorname + ' ' + data.nachname).replace(/^\s*(.*)\s*$/, '$1'),
|
||||
headerFilter: 'input'
|
||||
}, {
|
||||
field: 'datum',
|
||||
title: this.$p.t('global', 'datum'),
|
||||
formatter: dateFormatter,
|
||||
headerFilterFunc: 'dates',
|
||||
headerFilter: dateFilter
|
||||
}, {
|
||||
field: 'datum_wiedereinstieg',
|
||||
title: this.$p.t('studierendenantrag', 'antrag_datum_wiedereinstieg'),
|
||||
formatter: dateFormatter,
|
||||
headerFilterFunc: 'dates',
|
||||
headerFilter: dateFilter
|
||||
}, {
|
||||
field: 'grund',
|
||||
title: this.$p.t('studierendenantrag', 'antrag_grund'),
|
||||
formatter: (cell, formatterParams, onRendered) => {
|
||||
let link = document.createElement('a'),
|
||||
val = cell.getValue();
|
||||
link.href = "#modal-grund";
|
||||
link.setAttribute('data-bs-toggle', 'modal');
|
||||
link.innerHTML = this.$p.t('studierendenantrag', 'antrag_grund');
|
||||
link.addEventListener('click', () => {
|
||||
this.$refs.modalGrundPre.innerHTML = val;
|
||||
});
|
||||
|
||||
return val ? link : '';
|
||||
}
|
||||
}, {
|
||||
field: 'dms_id',
|
||||
title: this.$p.t('studierendenantrag', 'antrag_dateianhaenge'),
|
||||
formatter: (cell, formatterParams, onRendered) => {
|
||||
let val = cell.getValue();
|
||||
if (!val)
|
||||
return '';
|
||||
let link = document.createElement('a');
|
||||
link.href = FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/lehre/Antrag/Attachment/show/' +
|
||||
val;
|
||||
link.setAttribute('target', '_blank');
|
||||
link.innerHTML = '<i class="fa fa-paperclip" aria-hidden="true"></i>';
|
||||
link.append(this.$p.t('studierendenantrag/antrag_anhang'));
|
||||
return link;
|
||||
}
|
||||
}, {
|
||||
field: 'actions',
|
||||
frozen: true,
|
||||
title: this.$p.t('ui', 'aktion'),
|
||||
headerFilter: false,
|
||||
headerSort: false,
|
||||
formatter: (cell, formatterParams, onRendered) => {
|
||||
let container = document.createElement('div'),
|
||||
data = cell.getData();
|
||||
|
||||
container.className = "d-flex gap-2";
|
||||
|
||||
if ((data.typ == 'Abmeldung' || data.typ == 'AbmeldungStgl' || data.typ == 'Unterbrechung') && (data.status == 'Genehmigt' || data.status == 'Beeinsprucht' || data.status == 'EinspruchAbgelehnt' || data.status == 'EmailVersandt')) {
|
||||
// NOTE(chris): Download PDF
|
||||
let button = document.createElement('a');
|
||||
// NOTE(chris): phrasen in attribues don't work if they are not preloaded
|
||||
// it work in this case because the category has already been loaded before
|
||||
button.innerHTML = '<i class="fa-solid fa-download" title="' + this.$p.t('studierendenantrag', 'btn_download_antrag') + '"></i>';
|
||||
button.className = "btn btn-outline-secondary";
|
||||
button.target = "_blank";
|
||||
button.href = FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
'content/pdfExport.php?xml=Antrag' + data.typ + '.xml.php&xsl=Antrag' + data.typ + '&id=' + data.studierendenantrag_id + '&output=pdf';
|
||||
container.append(button);
|
||||
}
|
||||
if (data.typ == 'AbmeldungStgl' && data.status == 'Genehmigt') {
|
||||
// NOTE(chris): Object
|
||||
let button = document.createElement('button');
|
||||
button.append(this.$p.t('studierendenantrag', 'btn_object'));
|
||||
button.className = "btn btn-outline-secondary";
|
||||
button.addEventListener('click', () => this.$emit('action:object', [cell.getData()]));
|
||||
container.append(button);
|
||||
}
|
||||
|
||||
if (data.typ == 'AbmeldungStgl' && data.status == 'Beeinsprucht') {
|
||||
// NOTE(chris): Deny Objection
|
||||
let button = document.createElement('button');
|
||||
button.append(this.$p.t('studierendenantrag', 'btn_objection_deny'));
|
||||
button.className = "btn btn-outline-secondary";
|
||||
button.addEventListener('click', () => this.$emit('action:objectionDeny', [cell.getData()]));
|
||||
container.append(button);
|
||||
|
||||
// NOTE(chris): Approve Objection
|
||||
button = document.createElement('button');
|
||||
button.append(this.$p.t('studierendenantrag', 'btn_objection_approve'));
|
||||
button.className = "btn btn-outline-secondary";
|
||||
button.addEventListener('click', () => this.$emit('action:objectionApprove', [cell.getData()]));
|
||||
container.append(button);
|
||||
}
|
||||
|
||||
if (this.stgA.includes(data.studiengang_kz)) {
|
||||
// NOTE(chris): Reopen
|
||||
if (data.typ == 'Wiederholung' && data.status == 'Verzichtet') {
|
||||
let button = document.createElement('button');
|
||||
button.append(this.$p.t('studierendenantrag', 'btn_reopen'));
|
||||
button.className = "btn btn-outline-secondary";
|
||||
button.addEventListener('click', () => this.$emit('action:reopen', [cell.getData()]));
|
||||
container.append(button);
|
||||
}
|
||||
// NOTE(chris): Lv Zuweisen
|
||||
if (data.typ == 'Wiederholung' && (data.status == 'Erstellt' || data.status == 'Lvszugewiesen')) {
|
||||
let button = document.createElement('a');
|
||||
button.append(this.$p.t('studierendenantrag', 'btn_lvzuweisen'));
|
||||
button.className = "btn btn-outline-secondary";
|
||||
button.href = FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/lehre/Antrag/Wiederholung/assistenz/' +
|
||||
cell.getData().studierendenantrag_id + '/frame';
|
||||
button.onclick = e => {
|
||||
e.preventDefault();
|
||||
BsModal.popup(Vue.h('iframe', {
|
||||
src: button.href,
|
||||
class: 'position-absolute top-0 start-0 w-100 h-100'
|
||||
}), {
|
||||
dialogClass: 'modal-fullscreen'
|
||||
}, this.$p.t('studierendenantrag', 'title_lvzuweisen', cell.getData())).then(() => {
|
||||
this.$emit('reload');
|
||||
});
|
||||
};
|
||||
container.append(button);
|
||||
}
|
||||
// NOTE(chris): Cancel
|
||||
if (data.typ == 'AbmeldungStgl' && (data.status == 'Erstellt' || data.status == 'Genehmigt' )) {
|
||||
let button = document.createElement('button');
|
||||
button.append(this.$p.t('studierendenantrag', 'btn_cancel'));
|
||||
button.className = "btn btn-outline-secondary";
|
||||
button.addEventListener('click',() => this.$emit('action:cancel', [cell.getData()]));
|
||||
container.append(button);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.stgL.includes(data.studiengang_kz)) {
|
||||
// NOTE(chris): Approve
|
||||
if ((data.typ == 'Wiederholung' && data.status == 'Lvszugewiesen') || (data.typ != 'Wiederholung' && data.status == 'Erstellt')) {
|
||||
let button = document.createElement('button');
|
||||
button.append(this.$p.t('studierendenantrag', 'btn_approve'));
|
||||
button.className = "btn btn-outline-secondary";
|
||||
button.addEventListener('click', () => this.$emit('action:approve', [cell.getData()]));
|
||||
container.append(button);
|
||||
}
|
||||
// NOTE(chris): Reject (Unterbrechung braucht grund)
|
||||
if (data.status == 'Erstellt' && data.typ == 'Unterbrechung') {
|
||||
let button = document.createElement('button');
|
||||
button.append(this.$p.t('studierendenantrag', 'btn_reject'));
|
||||
button.className = "btn btn-outline-secondary";
|
||||
button.addEventListener('click', () => this.$emit('action:reject', [cell.getData()]));
|
||||
container.append(button);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(chris): Show LVs
|
||||
if (data.typ == 'Wiederholung' && (data.status == 'Lvszugewiesen' || data.status == 'Genehmigt')) {
|
||||
let button = document.createElement('button');
|
||||
button.append(this.$p.t('studierendenantrag', 'btn_show_lvs'));
|
||||
button.className = "btn btn-outline-secondary";
|
||||
button.addEventListener('click', () => this.showLVs(cell.getData()));
|
||||
container.append(button);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
}]
|
||||
});
|
||||
this.table.on("tableBuilt", () => {
|
||||
let columns = this.table.getColumns();
|
||||
let columnData = [];
|
||||
for (let col of columns) {
|
||||
let def = col.getDefinition();
|
||||
if (def.title && !def.frozen) {
|
||||
columnData.push({
|
||||
title: def.title,
|
||||
visible: col.isVisible(),
|
||||
original: col
|
||||
});
|
||||
}
|
||||
}
|
||||
this.$emit('update:columnData', columnData);
|
||||
});
|
||||
this.table.on("rowSelectionChanged", data => {
|
||||
this.$emit('update:selectedData', data);
|
||||
});
|
||||
},
|
||||
template: `
|
||||
<div class="studierendenantrag-leitung-table">
|
||||
<div ref="table"></div>
|
||||
<bs-modal ref="modalGrund" id="modal-grund" class="fade">
|
||||
<template #title>{{$p.t('studierendenantrag', 'antrag_grund')}}</template>
|
||||
<textarea class="form-control" ref="modalGrundPre" style="width: 100%; height: 250px;" readonly></textarea>
|
||||
</bs-modal>
|
||||
<bs-modal ref="history" class="fade">
|
||||
<template #title>{{$p.t('studierendenantrag', 'title_history', {id: lastHistoryClickedId})}}</template>
|
||||
<core-fetch-cmpt ref="historyLoader" :api-function="getHistory">
|
||||
<table v-if="historyData.length" class="table">
|
||||
<tr v-for="status in historyData" :key="status.studierendenantrag_status_id">
|
||||
<td>{{(new Date(status.insertamum)).toLocaleString()}}</td>
|
||||
<td>{{status.insertvon}}</td>
|
||||
<td>{{status.typ}}</td>
|
||||
<td>
|
||||
<a v-if="status.grund" href="#modal-grund" data-bs-toggle="modal" @click="showHistoryGrund(status.grund)">
|
||||
{{$p.t('studierendenantrag', 'antrag_grund')}}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</core-fetch-cmpt>
|
||||
</bs-modal>
|
||||
<lv-popup ref="lvList" class="fade" :antrag-id="lvsData ? lvsData.studierendenantrag_id : null">
|
||||
{{$p.t('studierendenantrag', 'title_show_lvs', lvsData ? lvsData : {name: ''}) }}
|
||||
</lv-popup>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
import StudierendenantragStatus from './Status.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
StudierendenantragStatus
|
||||
},
|
||||
props: {
|
||||
antragId: Number,
|
||||
initialStatusCode: String,
|
||||
initialStatusMsg: String,
|
||||
disabled: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lvs: [],
|
||||
isloading: false,
|
||||
statusCode: '',
|
||||
statusMsg: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
lvs1() {
|
||||
return this.lvs[Object.keys(this.lvs).filter(key => key.substr(0, 1) == 1)] || [];
|
||||
},
|
||||
lvs2() {
|
||||
return this.lvs[Object.keys(this.lvs).filter(key => key.substr(0, 1) == 2)] || [];
|
||||
},
|
||||
lvs1sem(){
|
||||
return (Object.keys(this.lvs).filter(key => key.substr(0, 1) == 1).pop() || "1").substr(1);
|
||||
},
|
||||
lvs2sem(){
|
||||
return (Object.keys(this.lvs).filter(key => key.substr(0, 1) == 2).pop() || "2").substr(1);
|
||||
},
|
||||
statusSeverity() {
|
||||
switch (this.statusCode) {
|
||||
case 0: return 'danger';
|
||||
default: return 'info';
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
save() {
|
||||
this.isloading = true;
|
||||
const forbiddenLvs = this.lvs1.filter(lv => lv.antrag_zugelassen && !lv._children).map(lv => ({
|
||||
studierendenantrag_id: this.antragId,
|
||||
lehrveranstaltung_id: lv.lehrveranstaltung_id,
|
||||
zugelassen: 0,
|
||||
anmerkung: lv.antrag_anmerkung || "",
|
||||
studiensemester_kurzbz: this.lvs1sem
|
||||
}));
|
||||
const mandatoryLvs = this.lvs2.filter(lv => !lv._children).map(lv => ({
|
||||
studierendenantrag_id: this.antragId,
|
||||
lehrveranstaltung_id: lv.lehrveranstaltung_id,
|
||||
zugelassen:lv.antrag_zugelassen ? 1 : 2,
|
||||
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})
|
||||
.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.isloading = false;
|
||||
}).catch(error => {
|
||||
this.addAlert(error.message, 'alert-danger');
|
||||
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() {
|
||||
this.statusCode = this.initialStatusCode;
|
||||
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 (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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
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) ? 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
template: `
|
||||
<div class="col-sm-10">
|
||||
<div ref="alertbox"></div>
|
||||
|
||||
<span class="d-flex justify-content-between h4">
|
||||
<span>{{$p.t('studierendenantrag', 'title_lv_nicht_zugelassen')}}</span>
|
||||
<span>{{lvs1sem}}</span>
|
||||
</span>
|
||||
<div ref="lvtable1" class="mb-3">
|
||||
</div>
|
||||
|
||||
<span class="d-flex justify-content-between h4">
|
||||
<span>{{$p.t('studierendenantrag', 'title_lv_wiederholen')}}</span>
|
||||
<span>{{lvs2sem}}</span>
|
||||
</span>
|
||||
<div ref="lvtable2">
|
||||
</div>
|
||||
|
||||
<button type="button" @click="save" :disabled="isloading || disabled" class="btn btn-primary my-3">{{$p.t('studierendenantrag', 'btn_save_lvs')}}</button>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<studierendenantrag-status :msg="statusMsg" :severity="statusSeverity"></studierendenantrag-status>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
export default {
|
||||
props: {
|
||||
msg: String,
|
||||
severity: String
|
||||
},
|
||||
computed: {
|
||||
severityClass() {
|
||||
return 'alert-' + this.severity;
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div v-if="msg && severity" class="studierendenantrag-status alert text-center mb-3" :class="severityClass" role="alert" v-html="msg">
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -147,6 +147,14 @@ export const CoreFilterCmpt = {
|
||||
|
||||
return columns;
|
||||
},
|
||||
fieldIdsForVisibilty() {
|
||||
if (!this.tableBuilt)
|
||||
return [];
|
||||
return this.tabulator.getColumns().filter(col => {
|
||||
let def = col.getDefinition();
|
||||
return !def.frozen && def.title;
|
||||
}).map(col => col.getField());
|
||||
},
|
||||
fieldNames() {
|
||||
if (!this.tableBuilt)
|
||||
return {};
|
||||
@@ -539,7 +547,7 @@ export const CoreFilterCmpt = {
|
||||
:id="'collapseColumns' + idExtra"
|
||||
class="card-body collapse"
|
||||
:data-bs-parent="'#filterCollapsables' + idExtra"
|
||||
:fields="fields"
|
||||
:fields="fieldIdsForVisibilty"
|
||||
:selected="selectedFields"
|
||||
:names="fieldNames"
|
||||
@hide="tabulator.hideColumn($event)"
|
||||
|
||||
@@ -38,9 +38,6 @@ export default {
|
||||
return {
|
||||
selectedFields: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
||||
},
|
||||
watch: {
|
||||
selected(n) {
|
||||
@@ -68,7 +65,7 @@ export default {
|
||||
<div
|
||||
v-for="fieldToDisplay in fields"
|
||||
class="btn"
|
||||
:class="selected.indexOf(fieldToDisplay) != -1 ? 'btn-dark' : 'btn-outline-dark' "
|
||||
:class="selectedFields.indexOf(fieldToDisplay) != -1 ? 'btn-dark' : 'btn-outline-dark' "
|
||||
@click="toggle(fieldToDisplay)"
|
||||
>
|
||||
{{ names[fieldToDisplay] || fieldToDisplay }}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
const ANRECHNUNGSTATUS_APPROVED = 'approved';
|
||||
const ANRECHNUNGSTATUS_REJECTED = 'rejected';
|
||||
const HERKUNFT_DER_KENNTNISSE_MAX_LENGTH = 125;
|
||||
const CHAR_LENGTH125 = 125;
|
||||
const CHAR_LENGTH150 = 150;
|
||||
const CHAR_LENGTH500 = 500;
|
||||
const CHAR_LENGTH1000 = 1000;
|
||||
|
||||
const COLOR_DANGER = '#f2dede';
|
||||
|
||||
@@ -25,7 +28,7 @@ $(function(){
|
||||
// Alert message inside Begruendungsbox, if maximum ECTS exceeded
|
||||
requestAnrechnung.alertIfMaxEctsExceededInsideBegruendungsbox();
|
||||
|
||||
// Set chars counter for textarea 'Herkunft der Kenntnisse'
|
||||
// Set chars counter for textareas
|
||||
requestAnrechnung.setCharsCounter();
|
||||
|
||||
// If Sperregrund exists: display Sperre panel, hide Status panel and disable all form elements
|
||||
@@ -98,6 +101,8 @@ $(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,
|
||||
bestaetigung: this.bestaetigung.value,
|
||||
uploadfile: this.uploadfile.files
|
||||
},
|
||||
@@ -207,11 +212,22 @@ var requestAnrechnung = {
|
||||
},
|
||||
setCharsCounter: function(){
|
||||
$('#requestAnrechnung-herkunftDerKenntnisse').keyup(function() {
|
||||
|
||||
let length = HERKUNFT_DER_KENNTNISSE_MAX_LENGTH - $(this).val().length;
|
||||
|
||||
let length = CHAR_LENGTH125 - $(this).val().length;
|
||||
$('#requestAnrechnung-herkunftDerKenntnisse-charCounter').text(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);
|
||||
|
||||
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,105 @@
|
||||
const categories = {};
|
||||
const loadingModules = {};
|
||||
|
||||
function extractCategory(obj, category) {
|
||||
return obj.filter(e => e.category == category).reduce((res, elem) => {
|
||||
if (!res[elem.phrase])
|
||||
res[elem.phrase] = elem.text;
|
||||
return res;
|
||||
}, {});
|
||||
}
|
||||
function reloadRefs(category) {
|
||||
while (loadingModules[category].length) {
|
||||
var v = loadingModules[category].pop();
|
||||
v[0].value = getValueForLoadedPhrase(category, v[1], v[2]);
|
||||
Vue.triggerRef(v[0]);
|
||||
/*Vue.unref(v);*/
|
||||
}
|
||||
}
|
||||
function loadLazy(category, val, phrase, params) {
|
||||
// NOTE(chris): load module if it's not loaded yet
|
||||
if (loadingModules[category]) {
|
||||
loadingModules[category].push([val, phrase, params]);
|
||||
if (categories[category]) // NOTE(chris): this is for safety in case the loading finished the moment before the val was pushed into the array
|
||||
reloadRefs(category);
|
||||
return;
|
||||
}
|
||||
loadingModules[category] = [[val, phrase, params]];
|
||||
|
||||
axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Phrasen/loadModule/' + category).then(res => {
|
||||
if (res.data.retval)
|
||||
categories[category] = extractCategory(res.data.retval, category);
|
||||
else
|
||||
categories[category] = {};
|
||||
|
||||
reloadRefs(category);
|
||||
}).catch(err => console.error(err));
|
||||
}
|
||||
function getValueForLoadedPhrase(category, phrase, params) {
|
||||
let result = categories[category][phrase];
|
||||
if (!result)
|
||||
return '<< PHRASE ' + phrase + '>>';
|
||||
if (params)
|
||||
result = result.replace(/\{([^}]*)\}/g, (match, p1) => params[p1] === undefined ? match : params[p1]);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
const phrasen = {
|
||||
t_ref(category, phrase, params) {
|
||||
if (params === undefined && (
|
||||
(Array.isArray(category) && category.length == 2) ||
|
||||
(category.split && category.split('/').length == 2))
|
||||
) {
|
||||
params = phrase;
|
||||
[category, phrase] = category.split ? category.split('/') : category;
|
||||
}
|
||||
if (phrase === undefined) {
|
||||
console.error('invalid input');
|
||||
return '';
|
||||
}
|
||||
if (!categories[category]) {
|
||||
|
||||
var initialval = '';
|
||||
if (window.FHC_JS_PHRASES_STORAGE_OBJECT !== undefined) {
|
||||
var tmp_category = extractCategory(FHC_JS_PHRASES_STORAGE_OBJECT, category);
|
||||
if(tmp_category[phrase] !== undefined ) {
|
||||
initialval = tmp_category[phrase];
|
||||
}
|
||||
}
|
||||
|
||||
if (!categories[category] || Object.keys(categories[category]).length === 0) {
|
||||
categories[category] = undefined;
|
||||
let val = Vue.ref(initialval);
|
||||
loadLazy(category, val, phrase, params);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
var result = getValueForLoadedPhrase(category, phrase, params);
|
||||
return Vue.ref(result);
|
||||
},
|
||||
t(category, phrase, params) {
|
||||
return Vue.unref(this.t_ref(category, phrase, params));
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
data: () => {
|
||||
return {
|
||||
p: phrasen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Composable (wrapper for mixin)
|
||||
export function usePhrasen() {
|
||||
|
||||
function t(category, phrase, params) {
|
||||
return phrasen.t(category, phrase, params);
|
||||
}
|
||||
|
||||
return {
|
||||
t,
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
const categories = Vue.reactive({});
|
||||
const loadingModules = {};
|
||||
|
||||
function extractCategory(obj, category) {
|
||||
return obj.filter(e => e.category == category).reduce((res, elem) => {
|
||||
if (!res[elem.phrase])
|
||||
res[elem.phrase] = elem.text;
|
||||
return res;
|
||||
}, {});
|
||||
}
|
||||
function getValueForLoadedPhrase(category, phrase, params) {
|
||||
let result = categories[category][phrase];
|
||||
if (!result)
|
||||
return '<< PHRASE ' + phrase + '>>';
|
||||
if (params)
|
||||
result = result.replace(/\{([^}]*)\}/g, (match, p1) => params[p1] === undefined ? match : params[p1]);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
const phrasen = {
|
||||
loadCategory(category) {
|
||||
if (Array.isArray(category))
|
||||
return Promise.all(category.map(cat => this.loadCategory(cat)));
|
||||
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)
|
||||
.then(res => {
|
||||
if (res.data.retval)
|
||||
categories[category] = extractCategory(res.data.retval, category);
|
||||
else
|
||||
categories[category] = {};
|
||||
});
|
||||
return loadingModules[category];
|
||||
},
|
||||
t_ref(category, phrase, params) {
|
||||
console.warn('depricated');
|
||||
return Vue.computed(() => this.t(category, phrase, params));
|
||||
},
|
||||
t(category, phrase, params) {
|
||||
if (params === undefined && (
|
||||
(Array.isArray(category) && category.length == 2) ||
|
||||
(category.split && category.split('/').length == 2))
|
||||
) {
|
||||
params = phrase;
|
||||
[category, phrase] = category.split ? category.split('/') : category;
|
||||
}
|
||||
if (phrase === undefined) {
|
||||
console.error('invalid input', category, phrase, params);
|
||||
return '';
|
||||
}
|
||||
let val = Vue.computed(() => {
|
||||
if (!categories[category])
|
||||
return '';
|
||||
return getValueForLoadedPhrase(category, phrase, params);
|
||||
});
|
||||
if (!categories[category])
|
||||
this.loadCategory(category);
|
||||
return val.value;
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
install(app, options) {
|
||||
app.config.globalProperties.$p = phrasen;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
if (!primevue) {
|
||||
console.error('PrimeVue not loaded!');
|
||||
}
|
||||
|
||||
// NOTE(chris): Click on clear button gives an error. This is a bug in primevue => fixed in current version
|
||||
Tabulator.extendModule('filter', 'filters', {
|
||||
"dates": (headerValue, rowValue) => {
|
||||
if (!headerValue)
|
||||
return true;
|
||||
let v = new Date(rowValue);
|
||||
if (Array.isArray(headerValue)) {
|
||||
if (headerValue[1]) {
|
||||
return v >= headerValue[0] && v <= headerValue[1].setHours(23, 59, 59, 999);
|
||||
}
|
||||
return v.toDateString() == headerValue[0].toDateString();
|
||||
}
|
||||
return v.toDateString() == headerValue.toDateString();
|
||||
}
|
||||
});
|
||||
function dateFilter(cell, onRendered, success) {
|
||||
let div = document.createElement('div');
|
||||
|
||||
Vue.createApp({
|
||||
components: {
|
||||
PrimevueCalendar: primevue.calendar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
val: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
val(n) {
|
||||
success(n);
|
||||
}
|
||||
},
|
||||
template: `<primevue-calendar v-model="val" selection-mode="range" :manual-input="false" show-button-bar></primevue-calendar>`
|
||||
}).use(primevue.config.default).mount(div);
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
export { dateFilter as 'dateFilter' };
|
||||
Reference in New Issue
Block a user