Merge branch 'feature-61164/AbgabetoolQualityGates'

This commit is contained in:
Andreas Österreicher
2026-02-27 10:48:35 +01:00
5 changed files with 100 additions and 32 deletions
+2
View File
@@ -41,3 +41,5 @@ $config['STG_MOODLE_LINK'] = 'https://moodle.technikum-wien.at/course/view.php?i
$config['ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT'] = true; $config['ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT'] = true;
$config['ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER'] = true; $config['ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER'] = true;
$config['BETREUER_SAMMELMAIL_BUTTON_STUDENT'] = true;
@@ -89,13 +89,15 @@ class Abgabe extends FHCAPI_Controller
$abgabetypenBetreuer = $this->config->item('ALLOWED_ABGABETYPEN_BETREUER'); $abgabetypenBetreuer = $this->config->item('ALLOWED_ABGABETYPEN_BETREUER');
$ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT = $this->config->item('ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT'); $ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT = $this->config->item('ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT');
$ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER = $this->config->item('ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER'); $ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER = $this->config->item('ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER');
$BETREUER_SAMMELMAIL_BUTTON_STUDENT = $this->config->item('BETREUER_SAMMELMAIL_BUTTON_STUDENT');
$ret = array( $ret = array(
'old_abgabe_beurteilung_link' => $old_abgabe_beurteilung_link, 'old_abgabe_beurteilung_link' => $old_abgabe_beurteilung_link,
'turnitin_link' => $turnitin_link, 'turnitin_link' => $turnitin_link,
'abgabetypenBetreuer' => $abgabetypenBetreuer, 'abgabetypenBetreuer' => $abgabetypenBetreuer,
'ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT' => $ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT, 'ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT' => $ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT,
'ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER' => $ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER 'ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER' => $ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER,
'BETREUER_SAMMELMAIL_BUTTON_STUDENT' => $BETREUER_SAMMELMAIL_BUTTON_STUDENT,
); );
$this->terminateWithSuccess($ret); $this->terminateWithSuccess($ret);
@@ -17,6 +17,7 @@ export const AbgabeMitarbeiterDetail = {
Message: primevue.message, Message: primevue.message,
VueDatePicker VueDatePicker
}, },
emits: ['paUpdated'],
inject: [ inject: [
'abgabeTypeOptions', 'abgabeTypeOptions',
'abgabetypenBetreuer', 'abgabetypenBetreuer',
@@ -132,8 +133,8 @@ export const AbgabeMitarbeiterDetail = {
const noteOptExisting = this.allowedNotenOptions.find(opt => opt.note == existingTerminRes.note) const noteOptExisting = this.allowedNotenOptions.find(opt => opt.note == existingTerminRes.note)
existingTerminRes.note = noteOptExisting existingTerminRes.note = noteOptExisting
const existingTerminResCurrObj = this.projektarbeit.abgabetermine.find(paa => paa.paabgabe_id == existingTerminRes.paabgabe_id) termin.paabgabetyp_kurzbz = newTerminRes.paabgabetyp_kurzbz
existingTerminResCurrObj.noteBackend = noteOpt // do NOT take noteOptExisting -> should reflect the "yes the qgate grade is confirmed in backend ux behaviour" termin.noteBackend = noteOpt // do NOT take noteOptExisting -> should reflect the "yes the qgate grade is confirmed in backend ux behaviour"
termin.dateStyle = getDateStyleClass(termin, this.notenOptions) termin.dateStyle = getDateStyleClass(termin, this.notenOptions)
} }
@@ -174,6 +175,8 @@ export const AbgabeMitarbeiterDetail = {
} else { } else {
this.showAutomagicModalPhrase = false this.showAutomagicModalPhrase = false
} }
this.$emit("paUpdated", this.projektarbeit)
} else if(res?.meta?.status == 'error'){ } else if(res?.meta?.status == 'error'){
this.$fhcAlert.alertError() this.$fhcAlert.alertError()
} }
@@ -257,6 +260,7 @@ export const AbgabeMitarbeiterDetail = {
// this.$p.t('global/tooltipLektorDeleteKontrolle', [this.$entryParams.permissions.kontrolleDeleteMaxReach ]) // this.$p.t('global/tooltipLektorDeleteKontrolle', [this.$entryParams.permissions.kontrolleDeleteMaxReach ])
const deletedTerminIndex = this.projektarbeit.abgabetermine.findIndex(t => t.paabgabe_id === termin.paabgabe_id) const deletedTerminIndex = this.projektarbeit.abgabetermine.findIndex(t => t.paabgabe_id === termin.paabgabe_id)
this.projektarbeit.abgabetermine.splice(deletedTerminIndex, 1) this.projektarbeit.abgabetermine.splice(deletedTerminIndex, 1)
this.$emit("paUpdated", this.projektarbeit)
} else if(res?.meta?.status == 'error'){ } else if(res?.meta?.status == 'error'){
this.$fhcAlert.alertError() this.$fhcAlert.alertError()
} }
@@ -210,12 +210,12 @@ export const AbgabetoolAssistenz = {
headerFilter: dateFilter, headerFilter: dateFilter,
headerFilterFunc: this.headerFilterTerminCol, headerFilterFunc: this.headerFilterTerminCol,
sorter: this.sortFuncTerminCol, sorter: this.sortFuncTerminCol,
field: 'prevTermin', formatter: this.abgabterminFormatter, widthGrow: 1, width: 220, tooltip: false}, field: 'prevTermin', formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nextAbgabetermin'))), field: 'nextTermin', {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nextAbgabetermin'))), field: 'nextTermin',
headerFilter: dateFilter, headerFilter: dateFilter,
headerFilterFunc: this.headerFilterTerminCol, headerFilterFunc: this.headerFilterTerminCol,
sorter: this.sortFuncTerminCol, sorter: this.sortFuncTerminCol,
formatter: this.abgabterminFormatter, widthGrow: 1, width: 220, tooltip: false}, formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate1Status'))), {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate1Status'))),
headerFilter: 'list', headerFilter: 'list',
headerFilterParams: { valuesLookup: this.getQGateStatusList }, headerFilterParams: { valuesLookup: this.getQGateStatusList },
@@ -226,7 +226,7 @@ export const AbgabetoolAssistenz = {
field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false}, field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false},
], ],
persistence: false, persistence: false,
persistenceID: "abgabetool_2026_02" persistenceID: "abgabetool_2026_02_26"
}, },
abgabeTableEventHandlers: [ abgabeTableEventHandlers: [
{ {
@@ -247,6 +247,10 @@ export const AbgabetoolAssistenz = {
]}; ]};
}, },
methods: { methods: {
handlePaUpdated(projektarbeit) {
this.checkAbgabetermineProjektarbeit(projektarbeit)
this.$refs.abgabeTable.tabulator.redraw(true)
},
getQGateStatusList() { getQGateStatusList() {
return [ return [
this.$p.t('abgabetool/c4keinTerminVorhanden'), this.$p.t('abgabetool/c4keinTerminVorhanden'),
@@ -309,11 +313,13 @@ export const AbgabetoolAssistenz = {
return false return false
}, },
sammelMailStudent(param) { sammelMailStudent(param) {
const emails = this.selectedData const recipientList = [];
.map(row => `${row.student_uid}@${this.domain}`) this.selectedData.forEach(d => {
.join(','); recipientList.push(`${d.student_uid}@${this.domain}`)
const uniqueRecipients = [...new Set(emails)]; })
const uniqueRecipients = [...new Set(recipientList)];
const subject = this.$p.t('abgabetool/c4sammelmailStudentBetreff', [this.selectedStudiengangOption?.bezeichnung]); const subject = this.$p.t('abgabetool/c4sammelmailStudentBetreff', [this.selectedStudiengangOption?.bezeichnung]);
splitMailsHelper(uniqueRecipients, param.originalEvent, subject, this.$fhcAlert, this.$p) splitMailsHelper(uniqueRecipients, param.originalEvent, subject, this.$fhcAlert, this.$p)
}, },
@@ -362,7 +368,6 @@ export const AbgabetoolAssistenz = {
return false; return false;
}, },
checkQualityGateStatus(projekt) { checkQualityGateStatus(projekt) {
// TODO: might refine the representation of these states and maybe refactor code a little
const qgate1Termine = [] const qgate1Termine = []
const qgate2Termine = [] const qgate2Termine = []
@@ -382,7 +387,7 @@ export const AbgabetoolAssistenz = {
// reuse luxon calculated diffMs (termin.datum in relation to today) from previous datestyle check // reuse luxon calculated diffMs (termin.datum in relation to today) from previous datestyle check
qgate1Termine.forEach(qgate => { qgate1Termine.forEach(qgate => {
if(qgate.note != null && projekt.qgate1StatusRank <= 5) { if(qgate.note != null && projekt.qgate1StatusRank <= 5) {
const noteOpt = this.notenOptions.find(opt => opt.note == qgate.note) const noteOpt = typeof qgate.note !== 'object' ? this.notenOptions.find(opt => opt.note == qgate.note) : qgate.note
if(noteOpt.positiv) { if(noteOpt.positiv) {
projekt.qgate1Status = this.$p.t('abgabetool/c4positivBenotet') projekt.qgate1Status = this.$p.t('abgabetool/c4positivBenotet')
projekt.qgate1StatusRank = 5 projekt.qgate1StatusRank = 5
@@ -404,7 +409,7 @@ export const AbgabetoolAssistenz = {
qgate2Termine.forEach(qgate => { qgate2Termine.forEach(qgate => {
if(qgate.note != null && projekt.qgate1StatusRank <= 5) { if(qgate.note != null && projekt.qgate1StatusRank <= 5) {
const noteOpt = this.notenOptions.find(opt => opt.note == qgate.note) const noteOpt = typeof qgate.note !== 'object' ? this.notenOptions.find(opt => opt.note == qgate.note) : qgate.note
if(noteOpt.positiv) { if(noteOpt.positiv) {
projekt.qgate2Status = this.$p.t('abgabetool/c4positivBenotet') projekt.qgate2Status = this.$p.t('abgabetool/c4positivBenotet')
projekt.qgate2StatusRank = 5 projekt.qgate2StatusRank = 5
@@ -1287,7 +1292,13 @@ export const AbgabetoolAssistenz = {
</div> </div>
</template> </template>
<template v-slot:default> <template v-slot:default>
<AbgabeDetail :projektarbeit="selectedProjektarbeit" :isFullscreen="detailIsFullscreen" :assistenzMode="true"></AbgabeDetail> <AbgabeDetail
:projektarbeit="selectedProjektarbeit"
:isFullscreen="detailIsFullscreen"
:assistenzMode="true"
@paUpdated="handlePaUpdated">
</AbgabeDetail>
</template> </template>
</bs-modal> </bs-modal>
@@ -6,6 +6,7 @@ import ApiAbgabe from '../../../api/factory/abgabe.js'
import FhcOverlay from "../../Overlay/FhcOverlay.js"; import FhcOverlay from "../../Overlay/FhcOverlay.js";
import { getDateStyleClass } from "./getDateStyleClass.js"; import { getDateStyleClass } from "./getDateStyleClass.js";
import { dateFilter } from '../../../tabulator/filters/Dates.js'; import { dateFilter } from '../../../tabulator/filters/Dates.js';
import {splitMailsHelper} from "../../../helpers/EmailHelpers.js";
export const AbgabetoolMitarbeiter = { export const AbgabetoolMitarbeiter = {
name: "AbgabetoolMitarbeiter", name: "AbgabetoolMitarbeiter",
@@ -16,6 +17,7 @@ export const AbgabetoolMitarbeiter = {
Checkbox: primevue.checkbox, Checkbox: primevue.checkbox,
Dropdown: primevue.dropdown, Dropdown: primevue.dropdown,
Textarea: primevue.textarea, Textarea: primevue.textarea,
TieredMenu: primevue.tieredmenu,
VueDatePicker, VueDatePicker,
FhcOverlay FhcOverlay
}, },
@@ -48,6 +50,7 @@ export const AbgabetoolMitarbeiter = {
phrasenResolved: false, phrasenResolved: false,
turnitin_link: null, turnitin_link: null,
old_abgabe_beurteilung_link: null, old_abgabe_beurteilung_link: null,
BETREUER_SAMMELMAIL_BUTTON_STUDENT: null,
saving: false, saving: false,
loading: false, loading: false,
abgabeTypeOptions: null, abgabeTypeOptions: null,
@@ -139,7 +142,6 @@ export const AbgabetoolMitarbeiter = {
}, },
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', formatter: this.detailFormatter, headerFilter: false, headerSort: false, widthGrow: 1, tooltip: false, cssClass: 'sticky-col'}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', formatter: this.detailFormatter, headerFilter: false, headerSort: false, widthGrow: 1, tooltip: false, cssClass: 'sticky-col'},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4personenkennzeichen'))), headerFilter: true, field: 'pkz', formatter: this.pkzTextFormatter, widthGrow: 1, tooltip: false}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4personenkennzeichen'))), headerFilter: true, field: 'pkz', formatter: this.pkzTextFormatter, widthGrow: 1, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4kontakt'))), field: 'mail', formatter: this.mailFormatter, widthGrow: 1, tooltip: false, visible: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'vorname', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'vorname', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4projekttyp'))), field: 'projekttyp_kurzbz', formatter: this.centeredTextFormatter, widthGrow: 1}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4projekttyp'))), field: 'projekttyp_kurzbz', formatter: this.centeredTextFormatter, widthGrow: 1},
@@ -151,12 +153,12 @@ export const AbgabetoolMitarbeiter = {
headerFilter: dateFilter, headerFilter: dateFilter,
headerFilterFunc: this.headerFilterTerminCol, headerFilterFunc: this.headerFilterTerminCol,
sorter: this.sortFuncTerminCol, sorter: this.sortFuncTerminCol,
formatter: this.abgabterminFormatter, widthGrow: 1, width: 220, tooltip: false}, formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nextAbgabetermin'))), field: 'nextTermin', {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nextAbgabetermin'))), field: 'nextTermin',
headerFilter: dateFilter, headerFilter: dateFilter,
headerFilterFunc: this.headerFilterTerminCol, headerFilterFunc: this.headerFilterTerminCol,
sorter: this.sortFuncTerminCol, sorter: this.sortFuncTerminCol,
formatter: this.abgabterminFormatter, widthGrow: 1, width: 220, tooltip: false}, formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate1Status'))), {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate1Status'))),
headerFilter: 'list', headerFilter: 'list',
headerFilterParams: { valuesLookup: this.getQGateStatusList }, headerFilterParams: { valuesLookup: this.getQGateStatusList },
@@ -167,7 +169,7 @@ export const AbgabetoolMitarbeiter = {
field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false} field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false}
], ],
persistence: false, persistence: false,
persistenceID: 'abgabeTableBetreuer2026-02-24' persistenceID: 'abgabeTableBetreuer2026-02-26'
}, },
abgabeTableEventHandlers: [{ abgabeTableEventHandlers: [{
event: "tableBuilt", event: "tableBuilt",
@@ -203,6 +205,20 @@ export const AbgabetoolMitarbeiter = {
]}; ]};
}, },
methods: { methods: {
handlePaUpdated(projektarbeit) {
this.checkAbgabetermineProjektarbeit(projektarbeit)
this.$refs.abgabeTable.tabulator.redraw(true)
},
sammelMailStudent(param) {
const recipientList = [];
this.selectedData.forEach(d => {
recipientList.push(`${d.student_uid}@${this.domain}`)
})
const uniqueRecipients = [...new Set(recipientList)];
const subject = ""; // empty subject line
splitMailsHelper(uniqueRecipients, param.originalEvent, subject, this.$fhcAlert, this.$p)
},
getQGateStatusList() { getQGateStatusList() {
return [ return [
this.$p.t('abgabetool/c4keinTerminVorhanden'), this.$p.t('abgabetool/c4keinTerminVorhanden'),
@@ -375,7 +391,6 @@ export const AbgabetoolMitarbeiter = {
}); });
}, },
checkQualityGateStatus(projekt) { checkQualityGateStatus(projekt) {
// TODO: might refine the representation of these states and maybe refactor code a little
const qgate1Termine = [] const qgate1Termine = []
const qgate2Termine = [] const qgate2Termine = []
@@ -395,7 +410,7 @@ export const AbgabetoolMitarbeiter = {
// reuse luxon calculated diffMs (termin.datum in relation to today) from previous datestyle check // reuse luxon calculated diffMs (termin.datum in relation to today) from previous datestyle check
qgate1Termine.forEach(qgate => { qgate1Termine.forEach(qgate => {
if(qgate.note != null && projekt.qgate1StatusRank <= 5) { if(qgate.note != null && projekt.qgate1StatusRank <= 5) {
const noteOpt = this.notenOptions.find(opt => opt.note == qgate.note) const noteOpt = typeof qgate.note !== 'object' ? this.notenOptions.find(opt => opt.note == qgate.note) : qgate.note
if(noteOpt.positiv) { if(noteOpt.positiv) {
projekt.qgate1Status = this.$p.t('abgabetool/c4positivBenotet') projekt.qgate1Status = this.$p.t('abgabetool/c4positivBenotet')
projekt.qgate1StatusRank = 5 projekt.qgate1StatusRank = 5
@@ -417,7 +432,7 @@ export const AbgabetoolMitarbeiter = {
qgate2Termine.forEach(qgate => { qgate2Termine.forEach(qgate => {
if(qgate.note != null && projekt.qgate1StatusRank <= 5) { if(qgate.note != null && projekt.qgate1StatusRank <= 5) {
const noteOpt = this.notenOptions.find(opt => opt.note == qgate.note) const noteOpt = typeof qgate.note !== 'object' ? this.notenOptions.find(opt => opt.note == qgate.note) : qgate.note
if(noteOpt.positiv) { if(noteOpt.positiv) {
projekt.qgate2Status = this.$p.t('abgabetool/c4positivBenotet') projekt.qgate2Status = this.$p.t('abgabetool/c4positivBenotet')
projekt.qgate2StatusRank = 5 projekt.qgate2StatusRank = 5
@@ -677,12 +692,13 @@ export const AbgabetoolMitarbeiter = {
} }
pa.abgabetermine.forEach(termin => { pa.abgabetermine.forEach(termin => {
termin.note = this.allowedNotenOptions.find(opt => opt.note == termin.note) const noteOpt = this.allowedNotenOptions.find(opt => opt.note == termin.note)
if(noteOpt) termin.note = noteOpt
termin.file = [] termin.file = []
// only set this if it has not been set yet and abgabetermin has a note (qgate) // only set this if it has not been set yet and abgabetermin has a note (qgate)
if(!termin.noteBackend && termin.note) { if(!termin.noteBackend && noteOpt) {
termin.noteBackend = termin.note termin.noteBackend = noteOpt
} }
// update 08-01-2026: everybody is allowed to do everything in client, critical checks happen at backend level // update 08-01-2026: everybody is allowed to do everything in client, critical checks happen at backend level
@@ -719,11 +735,6 @@ export const AbgabetoolMitarbeiter = {
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' + return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
'<a><i class="fa fa-folder-open" style="color:#00649C"></i></a></div>' '<a><i class="fa fa-folder-open" style="color:#00649C"></i></a></div>'
}, },
mailFormatter(cell) {
const val = cell.getValue()
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
'<a href='+val+'><i class="fa fa-envelope" style="color:#00649C"></i></a></div>'
},
beurteilungFormatter(cell) { beurteilungFormatter(cell) {
const val = cell.getValue() const val = cell.getValue()
if(val) { if(val) {
@@ -828,6 +839,29 @@ export const AbgabetoolMitarbeiter = {
}, },
}, },
computed: { computed: {
emailItems() {
const menu = []
if(this.BETREUER_SAMMELMAIL_BUTTON_STUDENT){
menu.push({
label: this.$p.t('abgabetool/c4sendEmailStudierendev2', [this.uniqueStudentEmailCount]),
command: this.sammelMailStudent
})
}
return menu
},
uniqueStudentEmailCount() {
const emails = new Set();
this.selectedData.forEach(row => {
if (row.student_uid) {
emails.add(row.student_uid); // actually dont need domain for this
}
});
return emails.size;
},
getAllowedAbgabeTypeOptions() { getAllowedAbgabeTypeOptions() {
return this.abgabeTypeOptions.filter(opt => this.abgabetypenBetreuer.includes(opt.paabgabetyp_kurzbz)) return this.abgabeTypeOptions.filter(opt => this.abgabetypenBetreuer.includes(opt.paabgabetyp_kurzbz))
} }
@@ -840,6 +874,7 @@ export const AbgabetoolMitarbeiter = {
this.turnitin_link = res.data?.turnitin_link this.turnitin_link = res.data?.turnitin_link
this.old_abgabe_beurteilung_link = res.data?.old_abgabe_beurteilung_link this.old_abgabe_beurteilung_link = res.data?.old_abgabe_beurteilung_link
this.abgabetypenBetreuer = res.data?.abgabetypenBetreuer this.abgabetypenBetreuer = res.data?.abgabetypenBetreuer
this.BETREUER_SAMMELMAIL_BUTTON_STUDENT = res.data?.BETREUER_SAMMELMAIL_BUTTON_STUDENT
}).catch(e => { }).catch(e => {
this.loading = false this.loading = false
}) })
@@ -952,7 +987,11 @@ export const AbgabetoolMitarbeiter = {
</div> </div>
</template> </template>
<template v-slot:default> <template v-slot:default>
<AbgabeDetail :projektarbeit="selectedProjektarbeit" :isFullscreen="detailIsFullscreen"></AbgabeDetail> <AbgabeDetail
:projektarbeit="selectedProjektarbeit"
:isFullscreen="detailIsFullscreen"
@paUpdated="handlePaUpdated">
</AbgabeDetail>
</template> </template>
</bs-modal> </bs-modal>
@@ -988,7 +1027,17 @@ export const AbgabetoolMitarbeiter = {
<i class="fa fa-hourglass-end"></i> <i class="fa fa-hourglass-end"></i>
{{ $p.t('abgabetool/showDeadlines') }} {{ $p.t('abgabetool/showDeadlines') }}
</button> </button>
<button
v-if="emailItems.length"
role="button"
@click="evt => $refs.menu.toggle(evt)"
class="btn btn-outline-secondary dropdown-toggle"
aria-haspopup="true"
>
<i class="fa fa-envelope"></i>
</button>
<tiered-menu ref="menu" :model="emailItems" popup :autoZIndex="false" />
</template> </template>
</core-filter-cmpt> </core-filter-cmpt>