From 0d2e41cf2f841ea051639620a2abd4b140c3e5e7 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Tue, 14 Oct 2025 16:45:28 +0200 Subject: [PATCH] added paabgabetyp columns "aktiv", "upload_allowed", "aktiv"; setting sensible default values for existing typen that are just the developers best guess really; accordion header with icons & tooltips; logLib in Abgabe API controller logging all successful delete/insert/update requests; show arbitrary '23:59' string after target date so it is clear until when the upload should be fulfilled, even though we still dont do anything technically different; new Termine can only be made with aktiv paabgabe typen; note & benotungsnotiz now tied to paabgabetyp benotbar flag instead of hardcoded for qgate1 & 2; added "noch nicht abgegeben" text in case the abgabedatum is null; modal now spawns in xl with fullscreen optionally enabled; --- .../controllers/api/frontend/v1/Abgabe.php | 60 +++++-- public/css/components/abgabetool/abgabe.css | 18 +-- .../Cis/Abgabetool/AbgabeMitarbeiterDetail.js | 117 ++++++++++---- .../Cis/Abgabetool/AbgabeStudentDetail.js | 80 ++++++++-- .../Cis/Abgabetool/AbgabetoolMitarbeiter.js | 10 +- .../Cis/Abgabetool/AbgabetoolStudent.js | 2 +- .../61164_abgabetool_quality_gates.php | 146 +++++++++++++++++- system/phrasesupdate.php | 120 ++++++++++++++ 8 files changed, 478 insertions(+), 75 deletions(-) diff --git a/application/controllers/api/frontend/v1/Abgabe.php b/application/controllers/api/frontend/v1/Abgabe.php index 9be560034..8586c99bc 100644 --- a/application/controllers/api/frontend/v1/Abgabe.php +++ b/application/controllers/api/frontend/v1/Abgabe.php @@ -43,6 +43,20 @@ class Abgabe extends FHCAPI_Controller $this->load->library('PhrasesLib'); + // Loads LogLib with different debug trace levels to get data of the job that extends this class + // It also specify parameters to set database fields + $this->load->library('LogLib', array( + 'classIndex' => 5, + 'functionIndex' => 5, + 'lineIndex' => 4, + 'dbLogType' => 'API', // required + 'dbExecuteUser' => 'RESTful API', + 'requestId' => 'API', + 'requestDataFormatter' => function ($data) { + return json_encode($data); + } + ), 'logLib'); + $this->loadPhrases( array( 'global', @@ -150,7 +164,6 @@ class Abgabe extends FHCAPI_Controller } } - $this->terminateWithSuccess(array($projektarbeiten, DOMAIN, $uid)); } @@ -161,7 +174,6 @@ class Abgabe extends FHCAPI_Controller */ public function postStudentProjektarbeitZwischenabgabe() { - $projektarbeit_id = $_POST['projektarbeit_id']; $paabgabe_id = $_POST['paabgabe_id']; $student_uid = $_POST['student_uid']; @@ -189,6 +201,13 @@ class Abgabe extends FHCAPI_Controller )); $this->sendUploadEmail($bperson_id, $projektarbeit_id, $paabgabetyp_kurzbz, $student_uid); + + $this->logLib->logInfoDB(array('zwischenupload',$res, array( + 'abgabedatum' => date('Y-m-d'), + 'updatevon' => getAuthUID(), + 'updateamum' => date('Y-m-d H:i:s') + ), getAuthUID(), getAuthPersonId(), $student_uid)); + $this->terminateWithSuccess($res); } else { $this->terminateWithError('Error moving File'); @@ -197,7 +216,6 @@ class Abgabe extends FHCAPI_Controller } else { $this->terminateWithError('File missing'); } - } /** @@ -275,6 +293,13 @@ class Abgabe extends FHCAPI_Controller $this->sendUploadEmail($bperson_id, $projektarbeit_id, $paabgabetyp_kurzbz, $student_uid); + $this->logLib->logInfoDB(array('endupload',$res, array( + 'abgabedatum' => date('Y-m-d'), + 'updatevon' => getAuthUID(), + 'updateamum' => date('Y-m-d H:i:s') + ), getAuthUID(), getAuthPersonId(), array($projektarbeit_id,$sprache,$abstract,$abstract_en + ,$schlagwoerter, $schlagwoerter_en, $seitenanzahl))); + $this->terminateWithSuccess($res); } else { $this->terminateWithError('Error moving File'); @@ -531,6 +556,8 @@ class Abgabe extends FHCAPI_Controller 'insertamum' => date('Y-m-d H:i:s') ) ); + + $this->logLib->logInfoDB(array('paabgabe created',$result, getAuthUID(), getAuthPersonId())); } else { // load existing entry of paabgabe and check if note has changed to negativ, to avoid sending when // only notiz has changed. @@ -554,6 +581,17 @@ class Abgabe extends FHCAPI_Controller 'updateamum' => date('Y-m-d H:i:s') ) ); + + $this->logLib->logInfoDB(array('paabgabe updated',$result, array( + 'paabgabetyp_kurzbz' => $paabgabetyp_kurzbz, + 'datum' => $datum, + 'kurzbz' => $kurzbz, + 'note' => $note, + 'beurteilungsnotiz' => $beurteilungsnotiz, + 'upload_allowed' => $upload_allowed, + 'updatevon' => getAuthUID(), + 'updateamum' => date('Y-m-d H:i:s') + ), getAuthUID(), getAuthPersonId())); } // check if $paaabgabe is a qual gate and its note is deemed negative @@ -583,12 +621,10 @@ class Abgabe extends FHCAPI_Controller } else { // benotung legitimately changed -> email $this->sendQualGateNegativEmail($projektarbeit_id, $betreuer_person_id, $paabgabe); } - } else { // nothing existing previously -> send that mail $this->sendQualGateNegativEmail($projektarbeit_id, $betreuer_person_id, $paabgabe); } - } } @@ -603,15 +639,17 @@ class Abgabe extends FHCAPI_Controller $this->load->model('education/Paabgabe_model', 'PaabgabeModel'); - $result = $this->PaabgabeModel->load($paabgabe_id); - $result = $this->getDataOrTerminateWithError($result); + $paabgabeResult = $this->PaabgabeModel->load($paabgabe_id); + $paabgabeArr = $this->getDataOrTerminateWithError($paabgabeResult); - if(count($result) == 0) + if(count($paabgabeArr) == 0) $this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general'); - if($result[0]->insertvon === getAuthUID()) { + if($paabgabeArr[0]->insertvon === getAuthUID()) { $result = $this->PaabgabeModel->delete($paabgabe_id); $result = $this->getDataOrTerminateWithError($result); + + $this->logLib->logInfoDB(array($paabgabeArr[0], getAuthUID(), getAuthPersonId())); $this->terminateWithSuccess($result); } @@ -705,6 +743,8 @@ class Abgabe extends FHCAPI_Controller ); } + $this->logLib->logInfoDB(array('serientermin angelegt',$res, getAuthUID(), getAuthPersonId())); + $this->terminateWithSuccess($res); } @@ -725,7 +765,7 @@ class Abgabe extends FHCAPI_Controller $this->load->model('education/Paabgabe_model', 'PaabgabeModel'); $result = $this->PaabgabeModel->getDeadlines($person_id); $data = $this->getDataOrTerminateWithError($result); - + $this->terminateWithSuccess($data); } diff --git a/public/css/components/abgabetool/abgabe.css b/public/css/components/abgabetool/abgabe.css index 2c35da965..e8b0cb9da 100644 --- a/public/css/components/abgabetool/abgabe.css +++ b/public/css/components/abgabetool/abgabe.css @@ -3,7 +3,7 @@ background-color: var(--fhc-red-70); font-weight: 600; border-radius: 6px; - padding: 0px 0px 0px 32px; + padding: 0px 0px 0px 34px; 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); } @@ -31,7 +31,7 @@ background-color: var(--fhc-yellow-70); font-weight: 600; border-radius: 6px; - padding: 0px 0px 0px 32px; + padding: 0px 0px 0px 34px; 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); } @@ -56,29 +56,29 @@ /* Base Header */ .verspaetet-header { - background-color: var(--fhc-pink-70); + background-color: var(--fhc-pink-40); font-weight: 600; border-radius: 6px; - padding: 0px 0px 0px 32px; + padding: 0px 0px 0px 34px; 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-60); + background-color: var(--fhc-pink-20); 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); + background-color: var(--fhc-pink-30); 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-60); + background-color: var(--fhc-pink-20); box-shadow: 0 2px 6px rgba(0,0,0,0.12); } @@ -87,7 +87,7 @@ background-color: var(--fhc-green-70); font-weight: 600; border-radius: 6px; - padding: 0px 0px 0px 32px; + padding: 0px 0px 0px 34px; 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); } @@ -115,7 +115,7 @@ background-color: var(--fhc-white-70); font-weight: 600; border-radius: 6px; - padding: 0px 0px 0px 32px; + padding: 0px 0px 0px 34px; 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); } diff --git a/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js b/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js index d6b0040fd..fc804ea3a 100644 --- a/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js +++ b/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js @@ -16,7 +16,13 @@ export const AbgabeMitarbeiterDetail = { AccordionTab: primevue.accordiontab, VueDatePicker }, - inject: ['abgabeTypeOptions', 'allowedNotenOptions', 'turnitin_link', 'old_abgabe_beurteilung_link', 'isMobile'], + inject: [ + 'abgabeTypeOptions', + 'allowedNotenOptions', + 'turnitin_link', + 'old_abgabe_beurteilung_link', + 'isMobile' + ], props: { projektarbeit: { type: Object, @@ -164,23 +170,23 @@ export const AbgabeMitarbeiterDetail = { const oneDayMs = 1000 * 60 * 60 * 24 return Math.round((new Date(datum) - new Date(today)) / oneDayMs) }, - getDateStyle(termin, mode) { + getDateStyleClass(termin, mode) { const datum = new Date(termin.datum) const abgabedatum = new Date(termin.abgabedatum) // https://wiki.fhcomplete.info/doku.php?id=cis:abgabetool_fuer_studierende if (termin.abgabedatum === null) { if(datum < today) { - return 'verpasst-header' + return 'verpasst' } else if (datum > today && this.dateDiffInDays(datum, today) <= 12) { - return 'abzugeben-header' + return 'abzugeben' } else { - return 'standard-header' + return 'standard' } } else if(abgabedatum > datum) { - return 'verspaetet-header' + return 'verspaetet' } else { - return 'abgegeben-header' + return 'abgegeben' } }, openBeurteilungLink(link) { @@ -209,7 +215,7 @@ export const AbgabeMitarbeiterDetail = { const link = FHC_JS_DATA_STORAGE_OBJECT.app_root + 'index.ci.php/extensions/FHC-Core-Projektarbeitsbeurteilung/' + path window.open(link, '_blank') }, - formatDate(dateParam) { + formatDate(dateParam, showTime = true) { const date = new Date(dateParam) // handle missing leading 0 const padZero = (num) => String(num).padStart(2, '0'); @@ -217,19 +223,21 @@ export const AbgabeMitarbeiterDetail = { const month = padZero(date.getMonth() + 1); // Months are zero-based const day = padZero(date.getDate()); const year = date.getFullYear(); - - return `${day}.${month}.${year}`; + + // abgabedatum should SHOW abgabezeit which should always be last minute of the day + return `${day}.${month}.${year}` + (showTime ? ' 23:59' : ''); }, getAccTabHeaderForTermin(termin) { let tabTitle = '' - const datumFormatted = this.formatDate(termin.datum) + const datumFormatted = this.formatDate(termin.datum, false) tabTitle += termin.bezeichnung?.bezeichnung + ' ' + datumFormatted return tabTitle }, openCreateNewAbgabeModal() { if(!this.newTermin) { + const typ = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === 'zwischen') this.newTermin = { 'paabgabe_id': -1, 'projektarbeit_id': this.projektarbeit.projektarbeit_id, @@ -238,9 +246,9 @@ export const AbgabeMitarbeiterDetail = { 'datum': new Date().toISOString().split('T')[0], 'note': this.allowedNotenOptions.find(opt => opt.note == 9), 'beurteilungsnotiz': '', - 'upload_allowed': false, + 'upload_allowed': typ.upload_allowed_default, 'paabgabetyp_kurzbz': '', - 'bezeichnung': this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === 'zwischen'), + 'bezeichnung': typ, 'abgabedatum': null, 'insertvon': this.viewData?.uid ?? '' } @@ -290,6 +298,9 @@ export const AbgabeMitarbeiterDetail = { }, computed: { + activeAbgabeTypeOptions() { + return this.abgabeTypeOptions?.filter(opt => opt.aktiv === true) + }, getEid() { return this.$p.t('abgabetool/c4eidesstattlicheErklaerung') }, @@ -317,6 +328,36 @@ export const AbgabeMitarbeiterDetail = { }, getSpeedDialStyle() { return 'position: static !important;' + }, + getTooltipVerspaetet() { + return { + value: this.$p.t('abgabetool/c4tooltipVerspaetet'), + class: "custom-tooltip" + } + }, + getTooltipVerpasst() { + return { + value: this.$p.t('abgabetool/c4tooltipVerpasst'), + class: "custom-tooltip" + } + }, + getTooltipAbzugeben() { + return { + value: this.$p.t('abgabetool/c4tooltipAbzugeben'), + class: "custom-tooltip" + } + }, + getTooltipStandard() { + return { + value: this.$p.t('abgabetool/c4tooltipStandard'), + class: "custom-tooltip" + } + }, + getTooltipAbgegeben() { + return { + value: this.$p.t('abgabetool/c4tooltipAbgegeben'), + class: "custom-tooltip" + } } }, watch: { @@ -326,6 +367,8 @@ export const AbgabeMitarbeiterDetail = { if(newVal?.paabgabetyp_kurzbz === 'qualgate1' || newVal?.paabgabetyp_kurzbz === 'qualgate2') { this.newTermin.kurzbz = newVal.bezeichnung } + + this.newTermin.upload_allowed = newVal.upload_allowed_default } }, created() { @@ -339,7 +382,7 @@ export const AbgabeMitarbeiterDetail = { id="innerModalNewAbgabe" ref="modalContainerCreateNewAbgabe" class="bootstrap-prompt" - dialogClass="bordered-modal modal-xl" + dialogClass="bordered-modal modal-lg" :backdrop="true" @hideBsModal="console.log('hideBsModal'); showAutomagicModalPhrase=false;" > @@ -383,13 +426,13 @@ export const AbgabeMitarbeiterDetail = { -
+
{{$p.t('abgabetool/c4upload_allowed')}}
{{$p.t('abgabetool/c4abgabekurzbz')}}
- +
@@ -439,8 +482,21 @@ export const AbgabeMitarbeiterDetail = {