notiz -> beurteilungsnotiz; some missing phrasen; more color definitions (WIP); finalize automagic modal logic; added validation stub for endupload; loading spinner on every upload; added custom header classes to code unexpanded paabgabe accordion tabs by their abgabedatum or lack thereof (WIP);

This commit is contained in:
Johann Hoffmann
2025-09-30 16:47:05 +02:00
parent 14f5a651a4
commit c796536417
12 changed files with 444 additions and 222 deletions
@@ -146,7 +146,11 @@ class Abgabe extends FHCAPI_Controller
$result = $this->ProjektarbeitModel->getProjektbetreuerEmail($pa->projektarbeit_id);
// TODO: save access
$pa->email = getData($result)[0]->private_email;
$data = getData($result);
if(count($data) > 0) {
$pa->email = $data[0]->private_email;
}
}
}
@@ -330,7 +334,7 @@ class Abgabe extends FHCAPI_Controller
if( null === $num_rows_sem || false === $num_rows_sem )
{
$this->terminateWithError($this->p->t('abgabetool','fehlerAktualitaetProjektarbeit'), 'general');
$this->terminateWithError($this->p->t('abgabetool','c4fehlerAktualitaetProjektarbeit'), 'general');
}
foreach($resBetr->retval as $betreuerRow) {
@@ -483,7 +487,7 @@ class Abgabe extends FHCAPI_Controller
$fixtermin = $_POST['fixtermin'];
$kurzbz = $_POST['kurzbz'];
$note = $_POST['note'];
$notiz = $_POST['notiz'];
$beurteilungsnotiz = $_POST['beurteilungsnotiz'];
$upload_allowed = $_POST['upload_allowed'];
$betreuer_person_id = $_POST['betreuer_person_id'];
@@ -506,7 +510,7 @@ class Abgabe extends FHCAPI_Controller
'datum' => $datum,
'kurzbz' => $kurzbz,
'note' => $note,
'notiz' => $notiz,
'beurteilungsnotiz' => $beurteilungsnotiz,
'upload_allowed' => $upload_allowed,
'insertvon' => getAuthUID(),
'insertamum' => date('Y-m-d H:i:s')
@@ -519,7 +523,8 @@ class Abgabe extends FHCAPI_Controller
// TODO: what if paabgabe is a qualgate1, is benotet negativ and then its type is changed to gate2?
$existingResult = $this->PaabgabeModel->load($paabgabe_id);
$existingPaabgabe = getData($existingResult);
$existingPaabgabeArr = getData($existingResult);
if(count($existingPaabgabeArr) > 0) $existingPaabgabe = $existingPaabgabeArr[0];
$result = $this->PaabgabeModel->update(
$paabgabe_id,
@@ -528,7 +533,7 @@ class Abgabe extends FHCAPI_Controller
'datum' => $datum,
'kurzbz' => $kurzbz,
'note' => $note,
'notiz' => $notiz,
'beurteilungsnotiz' => $beurteilungsnotiz,
'upload_allowed' => $upload_allowed,
'updatevon' => getAuthUID(),
'updateamum' => date('Y-m-d H:i:s')
@@ -562,7 +567,7 @@ class Abgabe extends FHCAPI_Controller
$noteArr = $this->getDataOrTerminateWithError($result);
$note = $noteArr[0];
if($note->positiv === false) {
// do nothing since this means $notiz change or smth else
// do nothing since this means $beurteilungsnotiz change or smth else
} else { // benotung legitimately changed -> email
$this->sendQualGateNegativEmail($projektarbeit_id, $betreuer_person_id, $paabgabe);
}
@@ -168,7 +168,7 @@ class Projektarbeit_model extends DB_Model
campus.tbl_paabgabe.datum,
campus.tbl_paabgabe.note,
campus.tbl_paabgabe.upload_allowed,
campus.tbl_paabgabe.notiz,
campus.tbl_paabgabe.beurteilungsnotiz,
campus.tbl_paabgabetyp.paabgabetyp_kurzbz,
campus.tbl_paabgabetyp.bezeichnung,
campus.tbl_paabgabe.abgabedatum,
@@ -21,6 +21,7 @@ $includesArray = array(
'public/css/components/FilterComponent.css',
'public/css/components/Profil.css',
'public/css/components/FormUnderline.css',
'public/css/components/abgabetool/abgabe.css',
'public/css/Cis4/Cms.css',
'public/css/Cis4/Studium.css',
),
+113
View File
@@ -0,0 +1,113 @@
/* Base Header */
.verpasst-header {
background-color: var(--fhc-red-40);
font-weight: 600;
border-radius: 6px;
padding: 0px 0px 0px 32px;
transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease;
box-shadow: 0 1px 2px rgba(0,0,0,0.08);
}
/* Hover State */
.verpasst-header:hover {
background-color: var(--fhc-red-30);
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}
/* Active / Expanded State */
.p-accordion-tab-active > .verpasst-header {
background-color: var(--fhc-red-50);
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}
/* Hover State Active*/
.p-accordion-tab-active > .verpasst-header:hover {
background-color: var(--fhc-red-40);
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}
/* Base Header */
.abzugeben-header {
background-color: var(--fhc-yellow-40);
font-weight: 600;
border-radius: 6px;
padding: 0px 0px 0px 32px;
transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease;
box-shadow: 0 1px 2px rgba(0,0,0,0.08);
}
/* Hover State */
.abzugeben-header:hover {
background-color: var(--fhc-yellow-30);
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}
/* Active / Expanded State */
.p-accordion-tab-active > .abzugeben-header {
background-color: var(--fhc-yellow-50);
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}
/* Hover State Active*/
.p-accordion-tab-active > .abzugeben-header:hover {
background-color: var(--fhc-yellow-40);
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}
/* Base Header */
.verspaetet-header {
background-color: var(--fhc-pink-40);
font-weight: 600;
border-radius: 6px;
padding: 0px 0px 0px 32px;
transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease;
box-shadow: 0 1px 2px rgba(0,0,0,0.08);
}
/* Hover State */
.verspaetet-header:hover {
background-color: var(--fhc-pink-30);
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}
/* Active / Expanded State */
.p-accordion-tab-active > .verspaetet-header {
background-color: var(--fhc-pink-50);
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}
/* Hover State Active*/
.p-accordion-tab-active > .verspaetet-header:hover {
background-color: var(--fhc-pink-40);
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}
/* Base Header */
.abgegeben-header {
background-color: var(--fhc-green-40);
font-weight: 600;
border-radius: 6px;
padding: 0px 0px 0px 32px;
transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease;
box-shadow: 0 1px 2px rgba(0,0,0,0.08);
}
/* Hover State */
.abgegeben-header:hover {
background-color: var(--fhc-green-30);
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}
/* Active / Expanded State */
.p-accordion-tab-active > .abgegeben-header {
background-color: var(--fhc-green-50);
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}
/* Hover State Active*/
.p-accordion-tab-active > .abgegeben-header:hover {
background-color: var(--fhc-green-40);
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}
+26 -3
View File
@@ -38,10 +38,33 @@
--fhc-blue-primary: #006095;
--fhc-blue-primary-highlight: #0086CB;
--fhc-red-10: #842029;
--fhc-red-20: #800000;
/* --- Green --- */
--fhc-green-10: #dff4df;
--fhc-green-20: #a8e0a2;
--fhc-green-30: #008000;
--fhc-green-40: #006600;
--fhc-green-50: #004d00;
--fhc-green-10: #008000;
/* --- Red --- */
--fhc-red-10: #fde6e8;
--fhc-red-20: #f5a3a8;
--fhc-red-30: #842029;
--fhc-red-40: #600018;
--fhc-red-50: #400010;
/* --- Yellow --- */
--fhc-yellow-10: #fff9e6;
--fhc-yellow-20: #fff0b3;
--fhc-yellow-30: #ffeb3b;
--fhc-yellow-40: #f5d600;
--fhc-yellow-50: #c8aa00;
/* --- Pink --- */
--fhc-pink-10: #ffe6f0;
--fhc-pink-20: #f8c1d6;
--fhc-pink-30: #e91e63;
--fhc-pink-40: #c2185b;
--fhc-pink-50: #8b1050;
--fhc-beige-10: rgba(245, 233, 215, 0.5);
--fhc-beige-20: rgba(172, 153, 125, 0.5);
+1 -1
View File
@@ -59,7 +59,7 @@ export default {
datum: termin.datum,
note: termin.note_pk,
upload_allowed: !!termin.upload_allowed,
notiz: termin.notiz ?? '',
beurteilungsnotiz: termin.beurteilungsnotiz ?? '',
fixtermin: termin.fixtermin,
insertvon: termin.insertvon,
kurzbz: termin.kurzbz,
@@ -63,36 +63,41 @@ export const AbgabeMitarbeiterDetail = {
termin.betreuer_person_id = this.projektarbeit.betreuer_person_id
return this.$api.call(ApiAbgabe.postProjektarbeitAbgabe(termin)).then( (res) => {
if(res?.meta?.status == 'success') {
this.$fhcAlert.alertSuccess(this.$p.t('ui/gespeichert'))
const noteOpt = this.allowedNotenOptions.find(opt => opt.note == res.data[0].note)
const newTerminRes = {
...res.data
'allowedToSave': true,
'allowedToDelete': true,
...res.data[0]
}
if(newTerminRes.note) newTerminRes.note = noteOpt
const existingTerminRes = res.data[1]
newTerminRes.bezeichnung = {
bezeichnung: termin.bezeichnung?.bezeichnung,
paabgabetyp_kurzbz: termin.bezeichnung?.paabgabetyp_kurzbz
}
// only insert new abgabe if we actually created a new one, not when saving/editing existing
if(!this.projektarbeit.abgabetermine.find(abgabe => abgabe.paabgabe_id == newTerminRes.paabgabe_id)){
if(!existingTerminRes){
this.projektarbeit.abgabetermine.push(newTerminRes)
this.projektarbeit.abgabetermine.sort((a, b) =>new Date(a.datum) - new Date(b.datum))
} else {
const noteOptExisting = this.allowedNotenOptions.find(opt => opt.note == existingTerminRes.note)
existingTerminRes.note = noteOptExisting
}
// negative abgabe -> automagically open new termin modal
// really bad feature idea by management people that think
// they know better lmao that will be so annoying to deal with
// really bad feature imo that will be annoying to deal with
// TODO fix the note changed check
// check if the abgabe existed beforehand and thus if the note even changed -> dont spam modal open
// when editing text of negative abgabe
// const savedExistingTermin = termin.paabgabe_id == newTerminRes.paabgabe_id
// const noteChanged = savedExistingTermin && termin.note_pk !== newTerminRes.note
const newTerminResNoteOpt = this.allowedNotenOptions.find(opt => opt.note == newTerminRes.note)
if(newTerminResNoteOpt && !newTerminResNoteOpt.positiv) {
// termin is completely new and has negative note
const savedNewWithNegative = !existingTerminRes && !newTerminRes.note?.positiv
// termin existed previously + oldTermin had different note/positive note or no note at all
const savedExistingNoteAsNegativeAndWasNotNegativeBefore = existingTerminRes && !newTerminRes.note?.positiv && (existingTerminRes.note?.positiv || existingTerminRes.note === undefined)
const openModalDueToNegativeBeurteilung = savedNewWithNegative || savedExistingNoteAsNegativeAndWasNotNegativeBefore
if(openModalDueToNegativeBeurteilung) {
this.newTermin = {
'paabgabe_id': -1,
'projektarbeit_id': this.projektarbeit.projektarbeit_id,
@@ -100,7 +105,7 @@ export const AbgabeMitarbeiterDetail = {
'kurzbz': '',
'datum': new Date().toISOString().split('T')[0],
'note': this.allowedNotenOptions.find(opt => opt.note == 9),
'notiz': '',
'beurteilungsnotiz': '',
'upload_allowed': false,
'paabgabetyp_kurzbz': '',
'bezeichnung': this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === newTerminRes.paabgabetyp_kurzbz),
@@ -109,31 +114,7 @@ export const AbgabeMitarbeiterDetail = {
}
this.showAutomagicModalPhrase = true
this.$refs.modalContainerCreateNewAbgabe.show()
}
// if(paabgabe_id === -1) { // new abgabe has been inserted
// termin.paabgabe_id = res?.data?.paabgabe_id
// this.projektarbeit.abgabetermine.push({ // new abgatermin row
// 'paabgabe_id': -1,
// 'projektarbeit_id': this.projektarbeit.projektarbeit_id,
// 'fixtermin': false,
// 'kurzbz': '',
// 'datum': new Date().toISOString().split('T')[0],
// 'paabgabetyp_kurzbz': termin.paabgabetyp_kurzbz,
// 'note': this.allowedNotenOptions.find(opt => opt.note == termin.note?.note),
// 'upload_allowed': termin.upload_allowed,
// 'bezeichnung': this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === termin.paabgabetyp_kurzbz),
// 'abgabedatum': null,
// 'insertvon': this.viewData?.uid ?? '',
// 'allowedToSave': true,
// 'allowedToDelete': true
// })
// }
} else if(res?.meta?.status == 'error'){
this.$fhcAlert.alertError()
}
@@ -254,10 +235,10 @@ export const AbgabeMitarbeiterDetail = {
'kurzbz': '',
'datum': new Date().toISOString().split('T')[0],
'note': this.allowedNotenOptions.find(opt => opt.note == 9),
'notiz': '',
'beurteilungsnotiz': '',
'upload_allowed': false,
'paabgabetyp_kurzbz': '',
'bezeichnung': '',
'bezeichnung': this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === 'zwischen'),
'abgabedatum': null,
'insertvon': this.viewData?.uid ?? ''
}
@@ -266,24 +247,39 @@ export const AbgabeMitarbeiterDetail = {
this.$refs.modalContainerCreateNewAbgabe.show()
},
async handleSaveNewAbgabe(termin) {
// TODO: validate termin type & date
await this.saveTermin(termin)
this.$refs.modalContainerCreateNewAbgabe.hide()
this.newTermin = {
'paabgabe_id': -1,
'projektarbeit_id': this.projektarbeit.projektarbeit_id,
'fixtermin': false,
'kurzbz': '',
'datum': new Date().toISOString().split('T')[0],
'note': this.allowedNotenOptions.find(opt => opt.note == 9),
'notiz': '',
'upload_allowed': false,
'paabgabetyp_kurzbz': '',
'bezeichnung': '',
'abgabedatum': null,
'insertvon': this.viewData?.uid ?? ''
// determined inside saveTermin api.then()
if(this.showAutomagicModalPhrase) {
this.$refs.modalContainerCreateNewAbgabe.show()
} else {
this.$refs.modalContainerCreateNewAbgabe.hide()
this.newTermin = {
'paabgabe_id': -1,
'projektarbeit_id': this.projektarbeit.projektarbeit_id,
'fixtermin': false,
'kurzbz': '',
'datum': new Date().toISOString().split('T')[0],
'note': this.allowedNotenOptions.find(opt => opt.note == 9),
'beurteilungsnotiz': '',
'upload_allowed': false,
'paabgabetyp_kurzbz': '',
'bezeichnung': this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === 'zwischen'),
'abgabedatum': null,
'insertvon': this.viewData?.uid ?? ''
}
}
},
handleChangeAbgabetyp(termin) {
// if paabgabetype qualgate is selected, fill out kurzbz textfield with bezeichnung of quality gate so users
// are possibly less confused, which is a pursuit in vain
if(termin.bezeichnung?.paabgabetyp_kurzbz === 'qualgate1' || termin.bezeichnung?.paabgabetyp_kurzbz === 'qualgate2') {
termin.kurzbz = termin.bezeichnung.bezeichnung
}
}
},
computed: {
getEid() {
@@ -337,18 +333,20 @@ export const AbgabeMitarbeiterDetail = {
</template>
<template v-slot:default>
<div v-if="showAutomagicModalPhrase" class="text-center"><p>{{$p.t('abgabetool/c4abgabeQualGateNegativAddNewAutomagisch')}}</p></div>
<div v-if="newTermin">
<div class="row">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4fixtermin')}}</div>
<div class="col-8 col-md-9">
<Checkbox
v-model="newTermin.fixtermin"
:binary="true"
:pt="{ root: { class: 'ml-auto' }}"
>
</Checkbox>
</div>
</div>
<!-- minheight to avoid z-index magic for the datepicker inside the modal inside the modal...-->
<div v-if="newTermin" style="min-height: 600px">
<!-- fixtermin is not an option for lektors-->
<!-- <div class="row">-->
<!-- <div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4fixtermin')}}</div>-->
<!-- <div class="col-8 col-md-9">-->
<!-- <Checkbox -->
<!-- v-model="newTermin.fixtermin"-->
<!-- :binary="true" -->
<!-- :pt="{ root: { class: 'ml-auto' }}"-->
<!-- >-->
<!-- </Checkbox>-->
<!-- </div>-->
<!-- </div>-->
<div class="row mt-2">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4zieldatum')}}</div>
<div class="col-8 col-md-9">
@@ -368,6 +366,7 @@ export const AbgabeMitarbeiterDetail = {
<Dropdown
:style="{'width': '100%'}"
v-model="newTermin.bezeichnung"
@change="handleChangeAbgabetyp(newTermin)"
:options="abgabeTypeOptions"
:optionLabel="getOptionLabelAbgabetyp">
</Dropdown>
@@ -398,7 +397,7 @@ export const AbgabeMitarbeiterDetail = {
<div class="row mt-2" v-if="newTermin.bezeichnung?.paabgabetyp_kurzbz === 'qualgate1' || newTermin.bezeichnung?.paabgabetyp_kurzbz === 'qualgate2'">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4notizQualGate')}}</div>
<div class="col-8 col-md-9">
<Textarea style="margin-bottom: 4px;" v-model="newTermin.notiz" :rows=" isMobile ? 2 : 4" :cols=" isMobile ? 30 : 90"></Textarea>
<Textarea style="margin-bottom: 4px;" v-model="newTermin.beurteilungsnotiz" :rows=" isMobile ? 2 : 4" :cols=" isMobile ? 30 : 90"></Textarea>
</div>
</div>
<div class="row mt-2">
@@ -477,8 +476,10 @@ export const AbgabeMitarbeiterDetail = {
<div class="row">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4fixtermin')}}</div>
<div class="col-8 col-md-9">
<!-- always keep fixtermin checkbox disabled for mitarbeiter tool -->
<Checkbox
v-model="termin.fixtermin"
disabled
:binary="true"
:pt="{ root: { class: 'ml-auto' }}"
>
@@ -506,6 +507,7 @@ export const AbgabeMitarbeiterDetail = {
:style="{'width': '100%'}"
:disabled="!termin.allowedToSave"
v-model="termin.bezeichnung"
@change="handleChangeAbgabetyp(termin)"
:options="abgabeTypeOptions"
:optionLabel="getOptionLabelAbgabetyp">
</Dropdown>
@@ -536,7 +538,7 @@ export const AbgabeMitarbeiterDetail = {
<div class="row mt-2" v-if="termin.bezeichnung?.paabgabetyp_kurzbz === 'qualgate1' || termin.bezeichnung?.paabgabetyp_kurzbz === 'qualgate2'">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4notizQualGate')}}</div>
<div class="col-8 col-md-9">
<Textarea style="margin-bottom: 4px;" v-model="termin.notiz" :rows=" isMobile ? 2 : 4" :cols=" isMobile ? 30 : 90" :disabled="!termin.allowedToSave"></Textarea>
<Textarea style="margin-bottom: 4px;" v-model="termin.beurteilungsnotiz" :rows=" isMobile ? 2 : 4" :cols=" isMobile ? 30 : 90" :disabled="!termin.allowedToSave"></Textarea>
</div>
</div>
<div class="row mt-2">
@@ -30,6 +30,7 @@ export const AbgabeStudentDetail = {
},
data() {
return {
loading: false,
eidAkzeptiert: false,
enduploadTermin: null,
allActiveLanguages: FHC_JS_DATA_STORAGE_OBJECT.server_languages,
@@ -45,16 +46,38 @@ export const AbgabeStudentDetail = {
}
},
methods: {
validate: function(termin) {
async validate(termin, endupload = false) {
if(!termin.file.length) {
this.$fhcAlert.alertWarning(this.$p.t('global/warningChooseFile'));
return false
}
// TODO: refine these
if(endupload) {
// check these input fields for length of entry ? standard fallback '' anyways
// this.form['abstract']
// this.form['abstract_en']
// this.form['schlagwoerter']
// this.form['schlagwoerter_en']
// this.form['seitenanzahl']
if(this.form['abstract'].length < 10 && await this.$fhcAlert.confirm({
message: 'Abstract is very short, wirklich speichern?',
acceptLabel: 'Verstanden und Fortfahren',
acceptClass: 'btn btn-danger',
rejectLabel: 'Zurück',
rejectClass: 'btn btn-outline-secondary'
}) === false) {
return false
}
}
return true;
},
triggerEndupload() {
if (!this.validate(this.enduploadTermin))
async triggerEndupload() {
if (!await this.validate(this.enduploadTermin))
{
return false;
}
@@ -67,7 +90,6 @@ export const AbgabeStudentDetail = {
formData.append('student_uid', this.projektarbeit.student_uid)
formData.append('bperson_id', this.projektarbeit.bperson_id)
// TODO: validate/check for null etc.
formData.append('sprache', this.form['sprache'].sprache)
formData.append('abstract', this.form['abstract'])
formData.append('abstract_en', this.form['abstract_en'])
@@ -78,10 +100,13 @@ export const AbgabeStudentDetail = {
for (let i = 0; i < this.enduploadTermin.file.length; i++) {
formData.append('file', this.enduploadTermin.file[i]);
}
this.loading = true
this.$api.call(ApiAbgabe.postStudentProjektarbeitEndupload(formData))
.then(res => {
this.handleUploadRes(res)
})
this.handleUploadRes(res, this.enduploadTermin)
}).finally(()=> {
this.loading = false
})
this.$refs.modalContainerEnduploadZusatzdaten.hide()
},
@@ -99,9 +124,10 @@ export const AbgabeStudentDetail = {
return `${day}.${month}.${year}`;
},
upload(termin) {
async upload(termin) {
if (!this.validate(termin))
// only do this on endupload
if (! await this.validate(termin))
{
return false;
}
@@ -122,15 +148,22 @@ export const AbgabeStudentDetail = {
formData.append('file', termin.file[i]);
}
this.loading = true
this.$api.call(ApiAbgabe.postStudentProjektarbeitZwischenabgabe(formData))
.then(res => {
this.handleUploadRes(res)
})
this.handleUploadRes(res, termin)
}).finally(()=> {
this.loading = false
})
}
},
handleUploadRes(res) {
handleUploadRes(res, termin) {
if(res.meta.status == "success") {
this.$fhcAlert.alertSuccess('File erfolgreich hochgeladen')
// update 'abgabedatum' for successful upload -> shows the pdf icon and date once set
termin.abgabedatum = new Date().toISOString().split('T')[0];
} else {
this.$fhcAlert.alertError('File upload error')
}
@@ -138,6 +171,8 @@ export const AbgabeStudentDetail = {
if(res.meta.signaturInfo) {
this.$fhcAlert.alertInfo(res.meta.signaturInfo)
}
},
dateDiffInDays(datum, today){
const oneDayMs = 1000 * 60 * 60 * 24
@@ -154,28 +189,38 @@ export const AbgabeStudentDetail = {
let icon = '';
if (termin.abgabedatum === null) {
if(datum < today) {
color = 'red'
fontColor = 'white'
icon = 'fa-triangle-exclamation'
return 'verpasst-header'
// color = 'red'
// fontColor = 'white'
// icon = 'fa-triangle-exclamation'
} else if (datum > today && this.dateDiffInDays(datum, today) <= 12) {
color = 'yellow'
icon = 'fa-circle-exclamation'
return 'abzugeben-header'
// color = 'yellow'
// icon = 'fa-circle-exclamation'
}
} else if(abgabedatum > datum) {
color = 'pink' // aka "hellrot"
fontColor = 'white'
icon = 'fa-circle-question'
return 'verspaetet-header'
// color = 'pink' // aka "hellrot"
// fontColor = 'white'
// icon = 'fa-circle-question'
} else {
color = 'green'
icon = 'fa-square-check'
return 'abgegeben-header'
// color = 'green'
// icon = 'fa-square-check'
}
//return `font-color: ${fontColor} ; background-color: ${color}; border-radius: 50%;`
if( typeof mode !== 'undefined' || mode === 'icon') {
return icon;
} else {
return 'abgabe-zieldatum-border-' + color;
}
// return 'background-color: ' + color;
//
// //return `font-color: ${fontColor} ; background-color: ${color}; border-radius: 50%;`
// if( typeof mode !== 'undefined' || mode === 'icon') {
// return icon;
// } else {
// return 'abgabe-zieldatum-border-' + color;
// }
},
openBeurteilungLink(link) {
window.open(link, '_blank')
@@ -200,12 +245,12 @@ export const AbgabeStudentDetail = {
projektarbeit(newVal) {
// default select german if projektarbeit sprache was null
this.form.sprache = newVal.sprache ? this.allActiveLanguages.find(lang => lang.sprache == newVal.sprache) : this.allActiveLanguages.find(lang => lang.sprache == 'German')
this.form.abstract = newVal.abstract
this.form.abstract_en = newVal.abstract_en
this.form.schlagwoerter = newVal.schlagwoerter
this.form.schlagwoerter_en = newVal.schlagwoerter_en
this.form.kontrollschlagwoerter = newVal.kontrollschlagwoerter
this.form.seitenanzahl = newVal.seitenanzahl
this.form.abstract = newVal.abstract ?? ''
this.form.abstract_en = newVal.abstract_en ?? ''
this.form.schlagwoerter = newVal.schlagwoerter ?? ''
this.form.schlagwoerter_en = newVal.schlagwoerter_en ?? ''
this.form.kontrollschlagwoerter = newVal.kontrollschlagwoerter ?? ''
this.form.seitenanzahl = newVal.seitenanzahl ?? 1
}
},
computed: {
@@ -233,6 +278,11 @@ export const AbgabeStudentDetail = {
},
template: `
<div id="loadingOverlay" v-show="loading" style="position: absolute; width: 100vw; height: 100vh; display: flex; align-items: center; justify-content: center; background: rgba(255,255,255,0.5); z-index: 99999999999;">
<i class="fa-solid fa-spinner fa-pulse fa-5x"></i>
</div>
<div v-if="projektarbeit">
<h5>{{$p.t('abgabetool/c4abgabeStudentenbereich')}}</h5>
@@ -243,8 +293,7 @@ export const AbgabeStudentDetail = {
<Accordion :multiple="true" :activeIndex="[0]">
<template v-for="termin in this.projektarbeit?.abgabetermine">
<AccordionTab :header="getAccTabHeaderForTermin(termin)" style="padding: 0px;">
<AccordionTab :header="getAccTabHeaderForTermin(termin)" :headerClass="getDateStyle(termin)">
<div class="row">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4fixtermin')}}</div>
<div class="col-8 col-md-9">
@@ -288,18 +337,6 @@ export const AbgabeStudentDetail = {
</div>
</div>
</div>
<div class="row mt-2" v-if="termin.paabgabetyp_kurzbz === 'qualgate1' || termin.paabgabetyp_kurzbz === 'qualgate2'">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4upload_allowed')}}</div>
<div class="col-8 col-md-9">
<Checkbox
disabled
v-model="termin.upload_allowed"
:binary="true"
:pt="{ root: { class: 'ml-auto' }}"
>
</Checkbox>
</div>
</div>
<div class="row mt-2">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4abgabekurzbz')}}</div>
<div class="col-8 col-md-9">
@@ -315,15 +352,23 @@ export const AbgabeStudentDetail = {
</a>
</div>
</div>
<div class="row mt-2">
<div class="row mt-2" v-if="termin.upload_allowed">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4fileupload')}}</div>
<div class="col-8 col-md-9">
<div class="row">
<div class="col-4">
<Upload :disabled="!termin?.allowedToUpload || isViewMode" accept=".pdf" v-model="termin.file"></Upload>
<div class="col-12 col-sm-6 mb-2">
<Upload
:disabled="!termin?.allowedToUpload || isViewMode"
accept=".pdf"
v-model="termin.file"
></Upload>
</div>
<div class="col-4">
<button class="btn btn-primary border-0" @click="upload(termin)" :disabled="!termin.allowedToUpload || isViewMode">
<div class="col-12 col-sm-6">
<button
class="btn btn-primary border-0 w-100"
@click="upload(termin)"
:disabled="!termin.allowedToUpload || isViewMode"
>
{{$p.t('abgabetool/c4upload')}}
<i class="fa-solid fa-upload"></i>
</button>
@@ -331,86 +376,16 @@ export const AbgabeStudentDetail = {
</div>
</div>
</div>
</div>
</AccordionTab>
</template>
</Accordion>
<!-- <div id="uploadWrapper">-->
<!-- <div class="row" style="margin-bottom: 12px;">-->
<!-- <div class="col-1 fw-bold text-center">{{$p.t('abgabetool/c4fixtermin')}}</div>-->
<!-- <div class="col-2 fw-bold">{{$p.t('abgabetool/c4zieldatum')}}</div>-->
<!-- <div class="col-1 fw-bold">{{$p.t('abgabetool/c4abgabetyp')}}</div>-->
<!-- <div v-show="qualityGateTerminAvailable" class="col-1 fw-bold">{{$p.t('abgabetool/c4note')}}</div>-->
<!-- <div v-show="qualityGateTerminAvailable" class="col-1 fw-bold">{{$p.t('abgabetool/c4upload_allowed')}}</div>-->
<!-- <div class="col-2 fw-bold">{{$p.t('abgabetool/c4abgabekurzbz')}}</div>-->
<!-- <div class="col-1 fw-bold text-center">{{$p.t('abgabetool/c4abgabedatum')}}</div>-->
<!-- <div class="col-3 fw-bold">-->
<!-- {{$p.t('abgabetool/c4fileupload')}}-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="row" v-for="termin in projektarbeit.abgabetermine">-->
<!-- <div class="col-1 d-flex justify-content-center align-items-start">-->
<!-- <i v-if="termin.fixtermin" class="fa-solid fa-2x fa-circle-check fhc-bullet-red"></i>-->
<!-- <i v-else="" class="fa-solid fa-2x fa-circle-xmark fhc-bullet-green"></i>-->
<!--&lt;!&ndash;-->
<!-- <p class="fhc-bullet" :class="{ 'fhc-bullet-red': termin.fixtermin, 'fhc-bullet-green': !termin.fixtermin }"></p>-->
<!--&ndash;&gt;-->
<!-- </div>-->
<!-- <div class="col-2 d-flex justify-content-start align-items-start">-->
<!-- <div class="position-relative" :class="getDateStyle(termin)">-->
<!-- <VueDatePicker-->
<!-- v-model="termin.datum"-->
<!-- :clearable="false"-->
<!-- :disabled="true"-->
<!-- :enable-time-picker="false"-->
<!-- :format="formatDate"-->
<!-- :text-input="true"-->
<!-- auto-apply>-->
<!-- </VueDatePicker>-->
<!-- <i class="position-absolute abgabe-zieldatum-overlay fa-solid fa-2x" :class="getDateStyle(termin, 'icon')"></i>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="col-1 d-flex justify-content-start align-items-start">{{ termin.bezeichnung }}</div>-->
<!-- <div v-if="qualityGateTerminAvailable || termin.bezeichnung?.paabgabetyp_kurzbz === 'qualgate1' || termin.bezeichnung?.paabgabetyp_kurzbz === 'qualgate2'" class="col-1 d-flex justify-content-start align-items-start">-->
<!-- {{ getTerminNoteBezeichnung(termin) }}-->
<!-- </div>-->
<!-- <div v-if="qualityGateTerminAvailable || termin.paabgabetyp_kurzbz === 'qualgate1' || termin.paabgabetyp_kurzbz === 'qualgate2'" class="col-1 d-flex justify-content-center align-items-start">-->
<!-- <Checkbox -->
<!-- v-if="termin.paabgabetyp_kurzbz === 'qualgate1' || termin.paabgabetyp_kurzbz === 'qualgate2'"-->
<!-- disabled-->
<!-- v-model="termin.upload_allowed"-->
<!-- :binary="true" -->
<!-- :pt="{ root: { class: 'ml-auto' }}"-->
<!-- >-->
<!-- </Checkbox>-->
<!-- </div>-->
<!-- <div class="col-2 d-flex justify-content-start align-items-start">-->
<!-- <Textarea style="margin-bottom: 4px;" v-model="termin.kurzbz" rows="1" cols="45" :disabled="true"></Textarea>-->
<!-- </div>-->
<!-- <div class="col-1 d-flex justify-content-start align-items-center">-->
<!-- {{ termin.abgabedatum?.split("-").reverse().join(".") }}-->
<!-- <a v-if="termin?.abgabedatum" @click="downloadAbgabe(termin)" style="margin-left:4px; cursor: pointer;">-->
<!-- <i class="fa-solid fa-2x fa-file-pdf"></i>-->
<!-- </a>-->
<!-- </div>-->
<!-- <div class="col-3" v-if="!viewMode">-->
<!-- <div class="row">-->
<!-- <div class="col-8">-->
<!-- <Upload v-if="termin && termin.allowedToUpload" accept=".pdf" v-model="termin.file"></Upload>-->
<!-- </div>-->
<!-- <div class="col-4">-->
<!-- <button class="btn btn-primary border-0" @click="upload(termin)" :disabled="!termin.allowedToUpload">-->
<!-- {{$p.t('abgabetool/c4upload')}}-->
<!-- <i class="fa-solid fa-upload"></i>-->
<!-- </button>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
</div>
<bs-modal ref="modalContainerEnduploadZusatzdaten" class="bootstrap-prompt" dialogClass="modal-lg">
<bs-modal
ref="modalContainerEnduploadZusatzdaten"
class="bootstrap-prompt"
dialogClass="bordered-modal modal-lg">
<template v-slot:title>
<div>
{{$p.t('abgabetool/c4enduploadZusatzdaten')}}
@@ -465,14 +440,16 @@ export const AbgabeStudentDetail = {
<div class="row mb-3 align-items-start">
<div class="row">{{$p.t('abgabetool/c4abstractGer')}}</div>
<div class="row">
<Textarea v-model="form.abstract" rows="10"></Textarea>
<Textarea v-model="form.abstract" rows="10" maxlength="5000"></Textarea>
<p>{{ form.abstract?.length ? form.abstract.length : 0 }} / 5000 characters</p>
</div>
</div>
<div class="row mb-3 align-items-start">
<div class="row">{{$p.t('abgabetool/c4abstractEng')}}</div>
<div class="row">
<Textarea v-model="form.abstract_en" rows="10"></Textarea>
<Textarea v-model="form.abstract_en" rows="10" maxlength="5000"></Textarea>
<p>{{ form.abstract_en?.length ? form.abstract_en.length : 0 }} / 5000 characters</p>
</div>
</div>
@@ -447,7 +447,7 @@ export const AbgabetoolMitarbeiter = {
dialogClass="modal-fullscreen">
<template v-slot:title>
<div>
abgabe detail wtb phrasen
{{$p.t('abgabetool/c4abgabeMitarbeiterDetailTitle')}}
</div>
</template>
<template v-slot:default>
@@ -12,10 +12,11 @@ export const AbgabetoolStudent = {
AbgabeDetail,
VerticalSplit
},
inject: ['isMobile', 'isViewMode'],
inject: ['isMobile'],
provide() {
return {
notenOptions: Vue.computed(() => this.notenOptions)
notenOptions: Vue.computed(() => this.notenOptions),
isViewMode: Vue.computed(() => this.isViewMode)
}
},
props: {
@@ -315,7 +316,7 @@ export const AbgabetoolStudent = {
dialogClass="modal-fullscreen">
<template v-slot:title>
<div>
abgabe detail todo phrasen
{{$p.t('abgabetool/c4abgabeStudentDetailTitle')}}
</div>
</template>
<template v-slot:default>
@@ -328,12 +329,12 @@ export const AbgabetoolStudent = {
<hr>
<div v-if="projektarbeiten === null || projektarbeiten === []">
Keine Projektarbeiten gefunden!
{{$p.t('abgabetool/c4abgabeStudentNoProjectsFound')}}
</div>
<Accordion :multiple="true" :activeIndex="[0]">
<template v-for="projektarbeit in projektarbeiten">
<AccordionTab :header="getAccTabHeaderForProjektarbeit(projektarbeit)" style="padding: 0px;">
<AccordionTab :header="getAccTabHeaderForProjektarbeit(projektarbeit)">
<div class="row">
<div class="col-4 col-md-3 fw-bold">{{$p.t('abgabetool/c4details')}}</div>
@@ -52,7 +52,7 @@ if($result = $db->db_query("SELECT 1 FROM information_schema.columns WHERE table
if($db->db_num_rows($result) === 0)
{
$qry = "ALTER TABLE campus.tbl_paabgabe
ADD COLUMN IF NOT EXISTS upload_allowed boolean DEFAULT false;";
ADD COLUMN IF NOT EXISTS upload_allowed boolean DEFAULT true;";
if(!$db->db_query($qry))
echo '<strong>campus.tbl_paabgabe: '.$db->db_last_error().'</strong><br>';
@@ -61,17 +61,17 @@ if($result = $db->db_query("SELECT 1 FROM information_schema.columns WHERE table
}
}
if($result = $db->db_query("SELECT 1 FROM information_schema.columns WHERE table_schema = 'campus' AND table_name = 'tbl_paabgabe' AND column_name = 'notiz'"))
if($result = $db->db_query("SELECT 1 FROM information_schema.columns WHERE table_schema = 'campus' AND table_name = 'tbl_paabgabe' AND column_name = 'beurteilungsnotiz'"))
{
if($db->db_num_rows($result) === 0)
{
$qry = "ALTER TABLE campus.tbl_paabgabe
ADD COLUMN IF NOT EXISTS notiz text DEFAULT NULL;";
ADD COLUMN IF NOT EXISTS beurteilungsnotiz text DEFAULT NULL;";
if(!$db->db_query($qry))
echo '<strong>campus.tbl_paabgabe: '.$db->db_last_error().'</strong><br>';
else
echo "<br>paabgabe column notiz default '' hinzugefuegt";
echo "<br>paabgabe column beurteilungsnotiz default '' hinzugefuegt";
}
}
+102 -2
View File
@@ -40071,6 +40071,46 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'abgabetool',
'phrase' => 'c4Sprache',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Sprache",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => "Language",
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'abgabetool',
'phrase' => 'c4fehlerAktualitaetProjektarbeit ',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Projektarbeit ist nichtmehr aktuell",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => "Project work is not current anymore",
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'abgabetool',
@@ -42724,13 +42764,73 @@ array(
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Notizen Quality Gate",
'text' => "Beurteilungsnotizen",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Notes Quality Gate',
'text' => 'Evaluation notes',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'abgabetool',
'phrase' => 'c4abgabeMitarbeiterDetailTitle',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Projektarbeit Detailansicht Mitarbeiter",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Project work Detailed view Employees',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'abgabetool',
'phrase' => 'c4abgabeStudentDetailTitle',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Projektarbeit Detailansicht Studierende",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Project work Detailed view Students',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'abgabetool',
'phrase' => 'c4abgabeStudentNoProjectsFound',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Keine Projektarbeiten gefunden!",
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'No projects found!',
'description' => '',
'insertvon' => 'system'
)