diff --git a/application/controllers/api/frontend/v1/Abgabe.php b/application/controllers/api/frontend/v1/Abgabe.php index c87d3609d..78434e5a3 100644 --- a/application/controllers/api/frontend/v1/Abgabe.php +++ b/application/controllers/api/frontend/v1/Abgabe.php @@ -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); } diff --git a/application/models/education/Projektarbeit_model.php b/application/models/education/Projektarbeit_model.php index ab0ebdcd5..bb5e06b0c 100644 --- a/application/models/education/Projektarbeit_model.php +++ b/application/models/education/Projektarbeit_model.php @@ -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, diff --git a/application/views/CisRouterView/CisRouterView.php b/application/views/CisRouterView/CisRouterView.php index 73ecc4797..5b16ceb87 100644 --- a/application/views/CisRouterView/CisRouterView.php +++ b/application/views/CisRouterView/CisRouterView.php @@ -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', ), diff --git a/public/css/components/abgabetool/abgabe.css b/public/css/components/abgabetool/abgabe.css new file mode 100644 index 000000000..ad0c1c166 --- /dev/null +++ b/public/css/components/abgabetool/abgabe.css @@ -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); +} + + diff --git a/public/css/theme/default.css b/public/css/theme/default.css index a0f366174..0337dd319 100644 --- a/public/css/theme/default.css +++ b/public/css/theme/default.css @@ -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); diff --git a/public/js/api/factory/abgabe.js b/public/js/api/factory/abgabe.js index 9cb26dcc3..0b6a0cb03 100644 --- a/public/js/api/factory/abgabe.js +++ b/public/js/api/factory/abgabe.js @@ -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, diff --git a/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js b/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js index 20d71ae0a..3ff230b00 100644 --- a/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js +++ b/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js @@ -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 = {