mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-07-02 11:29:27 +00:00
load all studiensemester for assistenz; load paabgabetyp benotbar for all paabgaben; datediff calc luxon; new dateclass 'beurteilungrequired'; 2nd quality gate validation logic option; filter notenoptions as per config; filter abgabetypoptions as per config; upload_allowed checkbox for serientermine; serientermin modal layout rearranged; abgabetoolJob fixes; 23:59 in the descriptive col, not datepicker; zusatzdaten are required; activeIndex for accordion calulated on demand by method instead of reading a computed value;
This commit is contained in:
@@ -11,4 +11,8 @@ $config['PAABGABE_EMAIL_JOB_INTERVAL'] = '1 day';
|
||||
// used as APP_ROOT.URL_STUDENTS -> cis4
|
||||
$config['URL_STUDENTS'] = 'cis.php/Cis/Abgabetool/Student';
|
||||
// used as APP_ROOT.URL_MITARBEITER -> old cis
|
||||
$config['URL_MITARBEITER'] = 'index.ci.php/Cis/Abgabetool/Mitarbeiter';
|
||||
$config['URL_MITARBEITER'] = 'index.ci.php/Cis/Abgabetool/Mitarbeiter';
|
||||
|
||||
// lehre.tbl_paabgabetyp bezeichnung
|
||||
$config['ALLOWED_ABGABETYPEN_BETREUER'] = ['Zwischenabgabe', 'Quality Gate 1', 'Quality Gate 2'];
|
||||
$config['ALLOWED_NOTEN_ABGABETOOL'] = ['Bestanden', 'Nicht bestanden'];
|
||||
@@ -82,11 +82,13 @@ class Abgabe extends FHCAPI_Controller
|
||||
public function getConfig() {
|
||||
$this->load->config('abgabe');
|
||||
$old_abgabe_beurteilung_link =$this->config->item('old_abgabe_beurteilung_link');
|
||||
$turnitin_link =$this->config->item('turnitin_link');
|
||||
$turnitin_link = $this->config->item('turnitin_link');
|
||||
$abgabetypenBetreuer = $this->config->item('ALLOWED_ABGABETYPEN_BETREUER');
|
||||
|
||||
$ret = array(
|
||||
'old_abgabe_beurteilung_link' => $old_abgabe_beurteilung_link,
|
||||
'turnitin_link' => $turnitin_link
|
||||
'turnitin_link' => $turnitin_link,
|
||||
'abgabetypenBetreuer' => $abgabetypenBetreuer
|
||||
);
|
||||
|
||||
$this->terminateWithSuccess($ret);
|
||||
@@ -402,7 +404,7 @@ class Abgabe extends FHCAPI_Controller
|
||||
$ci3BootstrapFilePath = "index.ci.php";
|
||||
}
|
||||
|
||||
$path = $this->_ci->config->item('URL_MITARBEITER');
|
||||
$path = $this->config->item('URL_MITARBEITER');
|
||||
$url = APP_ROOT.$path;
|
||||
|
||||
// $this->addMeta('betreuerArray', $resBetr->retval);
|
||||
@@ -701,6 +703,7 @@ class Abgabe extends FHCAPI_Controller
|
||||
$bezeichnung = $_POST['bezeichnung'];
|
||||
$kurzbz = $_POST['kurzbz'];
|
||||
$fixtermin = $_POST['fixtermin'];
|
||||
$upload_allowed = $_POST['upload_allowed'];
|
||||
|
||||
if (!isset($projektarbeit_ids) || !is_array($projektarbeit_ids) || empty($projektarbeit_ids)
|
||||
|| !isset($datum) || isEmptyString($datum)
|
||||
@@ -736,6 +739,7 @@ class Abgabe extends FHCAPI_Controller
|
||||
'fixtermin' => $fixtermin,
|
||||
'datum' => $datum,
|
||||
'kurzbz' => $kurzbz,
|
||||
'upload_allowed' => $upload_allowed,
|
||||
'insertvon' => getAuthUID(),
|
||||
'insertamum' => date('Y-m-d H:i:s')
|
||||
)
|
||||
@@ -800,7 +804,10 @@ class Abgabe extends FHCAPI_Controller
|
||||
|
||||
$result = $this->NoteModel->getAllActive();
|
||||
$noten = $this->getDataOrTerminateWithError($result);
|
||||
$this->terminateWithSuccess($noten);
|
||||
|
||||
$allowed_noten_abgabetool = $this->config->item('ALLOWED_NOTEN_ABGABETOOL');
|
||||
|
||||
$this->terminateWithSuccess(array($noten, $allowed_noten_abgabetool));
|
||||
}
|
||||
|
||||
private function sendQualGateNegativEmail($projektarbeit_id, $betreuer_person_id, $paabgabe) {
|
||||
@@ -904,7 +911,7 @@ class Abgabe extends FHCAPI_Controller
|
||||
$this->load->library('PermissionLib');
|
||||
|
||||
$stg_allowed = $this->permissionlib->getSTG_isEntitledFor('basis/abgabe_assistenz:rw');
|
||||
|
||||
|
||||
if($stg_allowed == false) {
|
||||
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'), 'general');
|
||||
}
|
||||
|
||||
@@ -160,8 +160,8 @@ class Studiensemester extends FHCAPI_Controller
|
||||
$this->StudiensemesterModel->addOrder("start", "DESC");
|
||||
$result = $this->StudiensemesterModel->getAktOrNextSemester();
|
||||
$aktuell = getData($result)[0];
|
||||
|
||||
$result = $this->StudiensemesterModel->getPreviousFrom($aktuell->studiensemester_kurzbz, 10);
|
||||
$this->StudiensemesterModel->addSelect('*');
|
||||
$result = $this->StudiensemesterModel->load();
|
||||
$studiensemester = getData($result);
|
||||
|
||||
$this->terminateWithSuccess(array($studiensemester, $aktuell));
|
||||
|
||||
@@ -20,6 +20,7 @@ class AbgabetoolJob extends JOB_Controller
|
||||
$this->_ci->load->model('education/Paabgabe_model', 'PaabgabeModel');
|
||||
$this->_ci->load->model('crm/Student_model', 'StudentModel');
|
||||
|
||||
$this->_ci->load->config('abgabe');
|
||||
$this->loadPhrases([
|
||||
'abgabetool'
|
||||
]);
|
||||
@@ -32,7 +33,7 @@ class AbgabetoolJob extends JOB_Controller
|
||||
// this job gathers all new or changed file uploads via field 'abgabedatum', enduploads still
|
||||
// send an email directly after happening since they are kind of important
|
||||
|
||||
$this->_ci->logInfo('Start job queue scheduler FHC-Core->notifyBetreuerMail');
|
||||
$this->_ci->logInfo('Start job FHC-Core->notifyBetreuerMail');
|
||||
|
||||
$interval = $this->_ci->config->item('PAABGABE_EMAIL_JOB_INTERVAL');
|
||||
|
||||
@@ -102,7 +103,7 @@ class AbgabetoolJob extends JOB_Controller
|
||||
|
||||
// send email with bundled info
|
||||
sendSanchoMail(
|
||||
'paabgabeUpdatesBetSM',
|
||||
'PaabgabeUpdatesBetSM',
|
||||
$body_fields,
|
||||
$data->private_email,
|
||||
$this->p->t('abgabetool', 'changedAbgabeterminev2')
|
||||
@@ -112,14 +113,14 @@ class AbgabetoolJob extends JOB_Controller
|
||||
}
|
||||
|
||||
$this->_ci->logInfo($count . " Emails erfolgreich versandt");
|
||||
$this->_ci->logInfo('End job queue scheduler FHC-Core->notifyBetreuerMail');
|
||||
$this->_ci->logInfo('End job FHC-Core->notifyBetreuerMail');
|
||||
}
|
||||
|
||||
public function notifyStudentMail()
|
||||
{
|
||||
// send all new projektarbeit abgabe since the last job run to the related student
|
||||
|
||||
$this->_ci->logInfo('Start job queue scheduler FHC-Core->notifyStudentMail');
|
||||
$this->_ci->logInfo('Start job FHC-Core->notifyStudentMail');
|
||||
|
||||
$interval = $this->_ci->config->item('PAABGABE_EMAIL_JOB_INTERVAL');
|
||||
|
||||
@@ -127,7 +128,7 @@ class AbgabetoolJob extends JOB_Controller
|
||||
$retval = getData($result);
|
||||
|
||||
if(count($retval) == 0) {
|
||||
$this->logInfo("Keine Emails an Studenten versandt");
|
||||
$this->_ci->logInfo("Keine Emails an Studenten versandt");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -180,7 +181,7 @@ class AbgabetoolJob extends JOB_Controller
|
||||
|
||||
// send email with bundled info
|
||||
sendSanchoMail(
|
||||
'paabgabeUpdatesSammelmail',
|
||||
'PaabgabeUpdatesSammelmail',
|
||||
$body_fields,
|
||||
$uid.'@'.DOMAIN,
|
||||
$this->p->t('abgabetool', 'changedAbgabeterminev2')
|
||||
@@ -191,6 +192,6 @@ class AbgabetoolJob extends JOB_Controller
|
||||
}
|
||||
|
||||
$this->_ci->logInfo($count . " Emails erfolgreich versandt");
|
||||
$this->_ci->logInfo('End job queue scheduler FHC-Core->notifyStudentMail');
|
||||
$this->_ci->logInfo('End job FHC-Core->notifyStudentMail');
|
||||
}
|
||||
}
|
||||
@@ -175,6 +175,7 @@ class Projektarbeit_model extends DB_Model
|
||||
campus.tbl_paabgabe.beurteilungsnotiz,
|
||||
campus.tbl_paabgabetyp.paabgabetyp_kurzbz,
|
||||
campus.tbl_paabgabetyp.bezeichnung,
|
||||
campus.tbl_paabgabetyp.benotbar,
|
||||
campus.tbl_paabgabe.abgabedatum,
|
||||
campus.tbl_paabgabe.insertvon
|
||||
FROM campus.tbl_paabgabe JOIN campus.tbl_paabgabetyp USING(paabgabetyp_kurzbz)
|
||||
|
||||
@@ -54,6 +54,35 @@
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
/* Base Header */
|
||||
.beurteilungerforderlich-header {
|
||||
background-color: var(--fhc-orange-70);
|
||||
font-weight: 600;
|
||||
border-radius: 6px;
|
||||
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 */
|
||||
.beurteilungerforderlich-header:hover {
|
||||
background-color: var(--fhc-orange-60);
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
/* Active / Expanded State */
|
||||
.p-accordion-tab-active > .beurteilungerforderlich-header {
|
||||
background-color: var(--fhc-orange-50);
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
/* Hover State Active*/
|
||||
.p-accordion-tab-active > .beurteilungerforderlich-header:hover {
|
||||
background-color: var(--fhc-orange-60);
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
/* Base Header */
|
||||
.verspaetet-header {
|
||||
background-color: var(--fhc-pink-40);
|
||||
|
||||
@@ -68,11 +68,11 @@ export default {
|
||||
params: { paabgabe_id }
|
||||
};
|
||||
},
|
||||
postSerientermin(datum, paabgabetyp_kurzbz, bezeichnung, kurzbz, projektarbeit_ids, fixtermin) {
|
||||
postSerientermin(datum, paabgabetyp_kurzbz, bezeichnung, kurzbz, upload_allowed, projektarbeit_ids, fixtermin) {
|
||||
return {
|
||||
method: 'post',
|
||||
url: '/api/frontend/v1/Abgabe/postSerientermin',
|
||||
params: { datum, paabgabetyp_kurzbz, bezeichnung, kurzbz, projektarbeit_ids, fixtermin }
|
||||
params: { datum, paabgabetyp_kurzbz, bezeichnung, kurzbz, upload_allowed, projektarbeit_ids, fixtermin }
|
||||
};
|
||||
},
|
||||
fetchDeadlines(person_id) {
|
||||
|
||||
@@ -19,6 +19,7 @@ export const AbgabeMitarbeiterDetail = {
|
||||
},
|
||||
inject: [
|
||||
'abgabeTypeOptions',
|
||||
'abgabetypenBetreuer',
|
||||
'allowedNotenOptions',
|
||||
'turnitin_link',
|
||||
'old_abgabe_beurteilung_link',
|
||||
@@ -40,6 +41,7 @@ export const AbgabeMitarbeiterDetail = {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeIndexArray: null,
|
||||
showAutomagicModalPhrase: false,
|
||||
eidAkzeptiert: false,
|
||||
enduploadTermin: null,
|
||||
@@ -77,6 +79,25 @@ export const AbgabeMitarbeiterDetail = {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getActiveIndexTabArray(additional = []) {
|
||||
// here we try to assume which abgabetermine are the most relevant to the current user
|
||||
|
||||
// lets try to take the termin with nearest date
|
||||
let closestIndex = -1;
|
||||
let minDiff = Infinity;
|
||||
const today = new Date();
|
||||
|
||||
|
||||
this.projektarbeit.abgabetermine.forEach((obj, i) => {
|
||||
const diff = Math.abs(new Date(obj.datum) - today);
|
||||
if (diff < minDiff) {
|
||||
minDiff = diff;
|
||||
closestIndex = i;
|
||||
}
|
||||
});
|
||||
|
||||
return [closestIndex, ...additional]
|
||||
},
|
||||
getPlaceholderTermin(termin) {
|
||||
return termin?.bezeichnung?.bezeichnung ?? this.$p.t('abgabetool/abgabetypPlaceholder')
|
||||
},
|
||||
@@ -114,12 +135,16 @@ export const AbgabeMitarbeiterDetail = {
|
||||
// only insert new abgabe if we actually created a new one, not when saving/editing existing
|
||||
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
|
||||
}
|
||||
|
||||
this.projektarbeit.abgabetermine.sort((a, b) =>new Date(a.datum) - new Date(b.datum))
|
||||
|
||||
const index = this.projektarbeit.abgabetermine.findIndex(t => termin.paabgabe_id == t.paabgabe_id)
|
||||
this.activeIndexArray = this.getActiveIndexTabArray([index])
|
||||
|
||||
// negative abgabe -> automagically open new termin modal
|
||||
// really bad feature imo that will be annoying to deal with
|
||||
|
||||
@@ -203,49 +228,9 @@ export const AbgabeMitarbeiterDetail = {
|
||||
this.$refs.modalContainerZusatzdaten.hide()
|
||||
},
|
||||
async validateZusatzdaten() {
|
||||
// check these input fields for length of entry
|
||||
if(this.form['abstract'].length < 100 && await this.$fhcAlert.confirm({
|
||||
message: this.$p.t('abgabetool/warningShortAbstract'),
|
||||
acceptLabel: this.$capitalize(this.$p.t('abgabetool/c4AcceptAndProceed')),
|
||||
acceptClass: 'btn btn-danger',
|
||||
rejectLabel: this.$capitalize(this.$p.t('abgabetool/c4Cancel')),
|
||||
rejectClass: 'btn btn-outline-secondary'
|
||||
}) === false) {
|
||||
return false
|
||||
}
|
||||
|
||||
if(this.form['abstract_en'].length < 100 && await this.$fhcAlert.confirm({
|
||||
message: this.$capitalize(this.$p.t('abgabetool/warningShortAbstractEn')),
|
||||
acceptLabel: this.$capitalize(this.$p.t('abgabetool/c4AcceptAndProceed')),
|
||||
acceptClass: 'btn btn-danger',
|
||||
rejectLabel: this.$capitalize(this.$p.t('abgabetool/c4Cancel')),
|
||||
rejectClass: 'btn btn-outline-secondary'
|
||||
}) === false) {
|
||||
return false
|
||||
}
|
||||
|
||||
if(this.form['schlagwoerter'].length < 50 && await this.$fhcAlert.confirm({
|
||||
message: this.$capitalize(this.$p.t('abgabetool/warningShortSchlagwoerter')),
|
||||
acceptLabel: this.$capitalize(this.$p.t('abgabetool/c4AcceptAndProceed')),
|
||||
acceptClass: 'btn btn-danger',
|
||||
rejectLabel: this.$capitalize(this.$p.t('abgabetool/c4Cancel')),
|
||||
rejectClass: 'btn btn-outline-secondary'
|
||||
}) === false) {
|
||||
return false
|
||||
}
|
||||
|
||||
if(this.form['schlagwoerter_en'].length < 50 && await this.$fhcAlert.confirm({
|
||||
message: this.$capitalize(this.$p.t('abgabetool/warningShortSchlagwoerterEn')),
|
||||
acceptLabel: this.$capitalize(this.$p.t('abgabetool/c4AcceptAndProceed')),
|
||||
acceptClass: 'btn btn-danger',
|
||||
rejectLabel: this.$capitalize(this.$p.t('abgabetool/c4Cancel')),
|
||||
rejectClass: 'btn btn-outline-secondary'
|
||||
}) === false) {
|
||||
return false
|
||||
}
|
||||
|
||||
if(this.form['seitenanzahl'] <= 5 && await this.$fhcAlert.confirm({
|
||||
message: this.$capitalize(this.$p.t('abgabetool/warningSmallSeitenanzahl')),
|
||||
// just ask once
|
||||
if(await this.$fhcAlert.confirm({
|
||||
message: this.$p.t('abgabetool/confirmEnduploadSpeichern'),
|
||||
acceptLabel: this.$capitalize(this.$p.t('abgabetool/c4AcceptAndProceed')),
|
||||
acceptClass: 'btn btn-danger',
|
||||
rejectLabel: this.$capitalize(this.$p.t('abgabetool/c4Cancel')),
|
||||
@@ -295,27 +280,61 @@ export const AbgabeMitarbeiterDetail = {
|
||||
window.open(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + url)
|
||||
// this.$api.call(ApiAbgabe.getStudentProjektarbeitAbgabeFile(termin.paabgabe_id, this.projektarbeit.student_uid))
|
||||
},
|
||||
dateDiffInDays(datum, today){
|
||||
const oneDayMs = 1000 * 60 * 60 * 24
|
||||
return Math.round((new Date(datum) - new Date(today)) / oneDayMs)
|
||||
convertDateToIsoString(date) {
|
||||
// 1. Check if it is a Date object AND if the date value is valid (not 'Invalid Date')
|
||||
if (param instanceof Date && !isNaN(param.getTime())) {
|
||||
const year = param.getFullYear();
|
||||
// getMonth() is 0-indexed, so we add 1.
|
||||
const month = param.getMonth() + 1;
|
||||
const day = param.getDate();
|
||||
|
||||
// Helper to pad single-digit numbers with a leading zero
|
||||
const pad = (num) => String(num).padStart(2, '0');
|
||||
|
||||
// Return the formatted string: YYYY-MM-DD
|
||||
return `${year}-${pad(month)}-${pad(day)}`;
|
||||
}
|
||||
|
||||
// If it's not a valid Date, return the original parameter
|
||||
return param;
|
||||
},
|
||||
dateDiffInDays(datumParam){
|
||||
let datum = datumParam
|
||||
if(datumParam instanceof Date && !isNaN(datum.getTime()))
|
||||
{
|
||||
const year = datumParam.getFullYear();
|
||||
const month = datumParam.getMonth() + 1; // getMonth() is 0-indexed
|
||||
const day = datumParam.getDate();
|
||||
const pad = (num) => String(num).padStart(2, '0');
|
||||
datum = `${year}-${pad(month)}-${pad(day)}`
|
||||
}
|
||||
|
||||
const dateToday = luxon.DateTime.now().startOf('day');
|
||||
const dateDatum = luxon.DateTime.fromISO(datum).startOf('day');
|
||||
const duration = dateDatum.diff(dateToday, 'days');
|
||||
|
||||
return duration.values.days;
|
||||
},
|
||||
getDateStyleClass(termin) {
|
||||
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) {
|
||||
termin.diffindays = this.dateDiffInDays(termin.datum)
|
||||
|
||||
if(today > datum && termin.benotbar && !termin.note) return 'beurteilungerforderlich'
|
||||
if (termin.abgabedatum === null && termin.upload_allowed) {
|
||||
if(datum < today) {
|
||||
return 'verpasst'
|
||||
} else if (datum > today && this.dateDiffInDays(datum, today) <= 12) {
|
||||
return 'abzugeben'
|
||||
return 'verpasst' // needs upload, missed it and has not submitted anything
|
||||
} else if (datum > today && termin.diffindays <= 12) {
|
||||
return 'abzugeben' // needs to upload soon
|
||||
} else {
|
||||
return 'standard'
|
||||
}
|
||||
} else if(abgabedatum > datum) {
|
||||
return 'verspaetet'
|
||||
return 'standard' // upload in distant future
|
||||
}
|
||||
}
|
||||
else if(abgabedatum > datum) {
|
||||
return 'verspaetet' // needs upload, missed it and has submitted smth late
|
||||
} else {
|
||||
return 'abgegeben'
|
||||
return 'abgegeben' // nothing else to do for that termin
|
||||
}
|
||||
},
|
||||
openBeurteilungLink(link) {
|
||||
@@ -343,11 +362,19 @@ export const AbgabeMitarbeiterDetail = {
|
||||
window.open(link, '_blank')
|
||||
},
|
||||
openBenotung() {
|
||||
const path = this.projektarbeit?.betreuerart_kurzbz == 'Zweitbegutachter' ? 'ProjektarbeitsbeurteilungZweitbegutachter' : 'ProjektarbeitsbeurteilungErstbegutachter'
|
||||
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root + 'index.ci.php/extensions/FHC-Core-Projektarbeitsbeurteilung/' + path
|
||||
window.open(link, '_blank')
|
||||
// old link check ?
|
||||
|
||||
if(this.getSemesterBenotbar && this.projektarbeit?.abgabetermine.find(termin => termin.paabgabetyp_kurzbz == 'end' && termin.abgabedatum !== null)) {
|
||||
// TODO: shouldnt be hardcoded here, at least config in abgabetool -> ideally event sourced from projektarbeitsbeurteilung
|
||||
|
||||
const path = this.projektarbeit?.betreuerart_kurzbz == 'Zweitbegutachter' ? 'ProjektarbeitsbeurteilungZweitbegutachter' : 'ProjektarbeitsbeurteilungErstbegutachter'
|
||||
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root + 'index.ci.php/extensions/FHC-Core-Projektarbeitsbeurteilung/' + path
|
||||
window.open(link, '_blank')
|
||||
} else {
|
||||
window.open(this.old_abgabe_beurteilung_link, '_blank')
|
||||
}
|
||||
},
|
||||
formatDate(dateParam, showTime = true) {
|
||||
formatDate(dateParam) {
|
||||
const date = new Date(dateParam)
|
||||
// handle missing leading 0
|
||||
const padZero = (num) => String(num).padStart(2, '0');
|
||||
@@ -356,13 +383,12 @@ export const AbgabeMitarbeiterDetail = {
|
||||
const day = padZero(date.getDate());
|
||||
const year = date.getFullYear();
|
||||
|
||||
// abgabedatum should SHOW abgabezeit which should always be last minute of the day
|
||||
return `${day}.${month}.${year}` + (showTime ? ' 23:59' : '');
|
||||
return `${day}.${month}.${year}`
|
||||
},
|
||||
getAccTabHeaderForTermin(termin) {
|
||||
let tabTitle = ''
|
||||
|
||||
const datumFormatted = this.formatDate(termin.datum, false)
|
||||
const datumFormatted = this.formatDate(termin.datum)
|
||||
tabTitle += termin.bezeichnung?.bezeichnung + ' ' + datumFormatted
|
||||
|
||||
return tabTitle
|
||||
@@ -432,6 +458,16 @@ export const AbgabeMitarbeiterDetail = {
|
||||
|
||||
},
|
||||
computed: {
|
||||
allowedToSaveZusatzdaten() {
|
||||
return this.form.schlagwoerter.length > 0 && this.form.schlagwoerter_en.length > 0 && this.form.abstract.length > 0 && this.form.abstract_en.length > 0 && this.form.seitenanzahl > 0
|
||||
},
|
||||
getAllowedAbgabeTypeOptions() {
|
||||
if(this.assistenzMode) {
|
||||
return this.abgabeTypeOptions
|
||||
} else {
|
||||
return this.abgabeTypeOptions.filter(opt => this.abgabetypenBetreuer.includes(opt.bezeichnung))
|
||||
}
|
||||
},
|
||||
getMessagePtStyle() {
|
||||
// adjust outer spacing and internal padding to appear similar to doenload button in size
|
||||
return {
|
||||
@@ -447,24 +483,6 @@ export const AbgabeMitarbeiterDetail = {
|
||||
}
|
||||
}
|
||||
},
|
||||
getActiveIndexTabArray() {
|
||||
// here we try to assume which abgabetermine are the most relevant to the current user
|
||||
|
||||
// lets try to take the termin with nearest date
|
||||
let closestIndex = -1;
|
||||
let minDiff = Infinity;
|
||||
const today = new Date();
|
||||
|
||||
this.projektarbeit.abgabetermine.forEach((obj, i) => {
|
||||
const diff = Math.abs(new Date(obj.datum) - today);
|
||||
if (diff < minDiff) {
|
||||
minDiff = diff;
|
||||
closestIndex = i;
|
||||
}
|
||||
});
|
||||
|
||||
return [closestIndex]
|
||||
},
|
||||
getEid() {
|
||||
return this.$p.t('abgabetool/c4eidesstattlicheErklaerung')
|
||||
},
|
||||
@@ -518,6 +536,12 @@ export const AbgabeMitarbeiterDetail = {
|
||||
class: "custom-tooltip"
|
||||
}
|
||||
},
|
||||
getTooltipBeurteilungerforderlich() {
|
||||
return {
|
||||
value: this.$p.t('abgabetool/c4tooltipBeurteilungerfolderlich'),
|
||||
class: "custom-tooltip"
|
||||
}
|
||||
},
|
||||
getTooltipAbgegeben() {
|
||||
return {
|
||||
value: this.$p.t('abgabetool/c4tooltipAbgegeben'),
|
||||
@@ -573,6 +597,8 @@ export const AbgabeMitarbeiterDetail = {
|
||||
'projektarbeit'(newVal) {
|
||||
// set invertedFixtermin field for UI/UX purposes -> avoid double negation in text
|
||||
|
||||
this.activeIndexArray = this.getActiveIndexTabArray()
|
||||
|
||||
newVal?.abgabetermine?.forEach(termin => termin.invertedFixtermin = !termin.fixtermin)
|
||||
|
||||
// default select german if projektarbeit sprache was null
|
||||
@@ -639,7 +665,7 @@ export const AbgabeMitarbeiterDetail = {
|
||||
<Dropdown
|
||||
:style="{'width': '100%'}"
|
||||
v-model="newTermin.bezeichnung"
|
||||
:options="abgabeTypeOptions"
|
||||
:options="getAllowedAbgabeTypeOptions"
|
||||
:optionLabel="getOptionLabelAbgabetyp"
|
||||
scrollHeight="300px">
|
||||
</Dropdown>
|
||||
@@ -708,7 +734,7 @@ export const AbgabeMitarbeiterDetail = {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Accordion :multiple="true" :activeIndex="getActiveIndexTabArray">
|
||||
<Accordion :multiple="true" :activeIndex="activeIndexArray">
|
||||
<template v-for="termin in this.projektarbeit?.abgabetermine">
|
||||
<AccordionTab :headerClass="getDateStyleClass(termin) + '-header'">
|
||||
<template #header>
|
||||
@@ -719,12 +745,13 @@ export const AbgabeMitarbeiterDetail = {
|
||||
<i v-else-if="getDateStyleClass(termin) == 'abzugeben'" v-tooltip.right="getTooltipAbzugeben" class="fa-solid fa-hourglass-half"></i>
|
||||
<i v-else-if="getDateStyleClass(termin) == 'standard'" v-tooltip.right="getTooltipStandard" class="fa-solid fa-clock"></i>
|
||||
<i v-else-if="getDateStyleClass(termin) == 'abgegeben'" v-tooltip.right="getTooltipAbgegeben" class="fa-solid fa-check"></i>
|
||||
<i v-else-if="getDateStyleClass(termin) == 'beurteilungerforderlich'" v-tooltip.right="getTooltipBeurteilungerforderlich" class="fa-solid fa-list-check"></i>
|
||||
</div>
|
||||
<div class="col-auto text-start" style="min-width: max(150px, 20%); max-width: min(300px, 30%); transform: translateX(-30px)">
|
||||
<span>{{ termin?.bezeichnung?.bezeichnung }}</span>
|
||||
</div>
|
||||
<div class="col-auto text-start" style="min-width: 100px; transform: translateX(-30px)">
|
||||
<span>{{ formatDate(termin.datum, false) }}</span>
|
||||
<span>{{ formatDate(termin.datum) }}</span>
|
||||
</div>
|
||||
<div v-if="termin?.fixtermin" class="col-auto" style="transform: translateX(-30px)">
|
||||
<i v-tooltip.right="getTooltipFixtermin" class="fa-solid fa-lock"></i>
|
||||
@@ -746,7 +773,10 @@ export const AbgabeMitarbeiterDetail = {
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 col-md-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4zieldatum') )}}</div>
|
||||
<div class="col-12 col-md-3 align-content-center">
|
||||
<div class="row fw-bold" style="margin-left: 2px">{{$capitalize( $p.t('abgabetool/c4zieldatum') )}}</div>
|
||||
<div class="row fw-light" style="margin-left: 2px">{{$capitalize( $p.t('abgabetool/c4abgabeuntil2359') )}}</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-9">
|
||||
<VueDatePicker
|
||||
v-model="termin.datum"
|
||||
@@ -768,7 +798,7 @@ export const AbgabeMitarbeiterDetail = {
|
||||
:placeholder="getPlaceholderTermin(termin)"
|
||||
v-model="termin.bezeichnung"
|
||||
@change="handleChangeAbgabetyp(termin)"
|
||||
:options="abgabeTypeOptions"
|
||||
:options="getAllowedAbgabeTypeOptions"
|
||||
:optionLabel="getOptionLabelAbgabetyp"
|
||||
:optionDisabled="getOptionDisabled">
|
||||
</Dropdown>
|
||||
@@ -778,6 +808,7 @@ export const AbgabeMitarbeiterDetail = {
|
||||
<div class="col-12 col-md-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4upload_allowed') )}}</div>
|
||||
<div class="col-12 col-md-9">
|
||||
<Checkbox
|
||||
:disabled="!termin.allowedToSave"
|
||||
v-model="termin.upload_allowed"
|
||||
:binary="true"
|
||||
:pt="{ root: { class: 'ml-auto' }}"
|
||||
@@ -968,7 +999,8 @@ export const AbgabeMitarbeiterDetail = {
|
||||
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<button class="btn btn-primary" @click="saveZusatzdaten">{{ $capitalize( $p.t('abgabetool/c4save') )}}</button>
|
||||
<div v-show="!allowedToSaveZusatzdaten">{{ $p.t('abgabetool/c4zusatzdatenausfuellen') }}</div>
|
||||
<button class="btn btn-primary" :disabled="!allowedToSaveZusatzdaten" @click="saveZusatzdaten">{{ $capitalize( $p.t('abgabetool/c4save') )}}</button>
|
||||
</template>
|
||||
</bs-modal>
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ export const AbgabeStudentDetail = {
|
||||
window.open(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + url)
|
||||
// this.$api.call(ApiAbgabe.getStudentProjektarbeitAbgabeFile(termin.paabgabe_id, this.projektarbeit.student_uid))
|
||||
},
|
||||
formatDate(dateParam, showTime = true) {
|
||||
formatDate(dateParam) {
|
||||
const date = new Date(dateParam)
|
||||
// handle missing leading 0
|
||||
const padZero = (num) => String(num).padStart(2, '0');
|
||||
@@ -161,7 +161,7 @@ export const AbgabeStudentDetail = {
|
||||
const day = padZero(date.getDate());
|
||||
const year = date.getFullYear();
|
||||
|
||||
return `${day}.${month}.${year}` + (showTime ? ' 23:59' : '');
|
||||
return `${day}.${month}.${year}`
|
||||
},
|
||||
async upload(termin) {
|
||||
|
||||
@@ -226,7 +226,7 @@ export const AbgabeStudentDetail = {
|
||||
getAccTabHeaderForTermin(termin) {
|
||||
let tabTitle = ''
|
||||
|
||||
const datumFormatted = this.formatDate(termin.datum, false)
|
||||
const datumFormatted = this.formatDate(termin.datum)
|
||||
tabTitle += termin.bezeichnung + ' ' + datumFormatted
|
||||
|
||||
return tabTitle
|
||||
@@ -282,8 +282,11 @@ export const AbgabeStudentDetail = {
|
||||
getEid() {
|
||||
return this.$capitalize(this.$p.t('abgabetool/c4eidesstattlicheErklaerung'))
|
||||
},
|
||||
allowedToSaveZusatzdaten() {
|
||||
return this.form.schlagwoerter.length > 0 && this.form.schlagwoerter_en.length > 0 && this.form.abstract.length > 0 && this.form.abstract_en.length > 0 && this.form.seitenanzahl > 0
|
||||
},
|
||||
getAllowedToSendEndupload() {
|
||||
return !this.eidAkzeptiert
|
||||
return this.eidAkzeptiert && this.allowedToSaveZusatzdaten
|
||||
},
|
||||
qualityGateTerminAvailable() {
|
||||
let qgatefound = false
|
||||
@@ -384,7 +387,7 @@ export const AbgabeStudentDetail = {
|
||||
<span>{{ termin?.bezeichnung }}</span>
|
||||
</div>
|
||||
<div class="col-auto text-start p-0" style="min-width: max(80px, 15%); transform: translateX(-30px)">
|
||||
<span>{{ formatDate(termin.datum, false) }}</span>
|
||||
<span>{{ formatDate(termin.datum) }}</span>
|
||||
</div>
|
||||
<div class="col-auto" style="transform: translateX(-30px); min-width: 42px;">
|
||||
<i v-if="termin?.fixtermin" v-tooltip.right="getTooltipFixtermin" class="fa-solid fa-lock"></i>
|
||||
@@ -431,7 +434,10 @@ export const AbgabeStudentDetail = {
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 col-md-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4zieldatum') )}}</div>
|
||||
<div class="col-12 col-md-3 align-content-center">
|
||||
<div class="row fw-bold" style="margin-left: 2px">{{$capitalize( $p.t('abgabetool/c4zieldatum') )}}</div>
|
||||
<div class="row fw-light" style="margin-left: 2px">{{$capitalize( $p.t('abgabetool/c4abgabeuntil2359') )}}</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-9">
|
||||
<VueDatePicker
|
||||
v-model="termin.datum"
|
||||
@@ -653,7 +659,8 @@ export const AbgabeStudentDetail = {
|
||||
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<button class="btn btn-primary" :disabled="getAllowedToSendEndupload" @click="triggerEndupload">{{$capitalize( $p.t('ui/hochladen') )}}</button>
|
||||
<div v-show="!allowedToSaveZusatzdaten">{{ $p.t('abgabetool/c4zusatzdatenausfuellen') }}</div>
|
||||
<button class="btn btn-primary" :disabled="!getAllowedToSendEndupload" @click="triggerEndupload">{{$capitalize( $p.t('ui/hochladen') )}}</button>
|
||||
</template>
|
||||
</bs-modal>
|
||||
|
||||
|
||||
@@ -409,6 +409,7 @@ export const AbgabetoolAssistenz = {
|
||||
this.serienTermin.bezeichnung.paabgabetyp_kurzbz,
|
||||
this.serienTermin.bezeichnung.bezeichnung,
|
||||
this.serienTermin.kurzbz,
|
||||
this.serienTermin.upload_allowed,
|
||||
pids,
|
||||
this.serienTermin.fixtermin
|
||||
)).then(res => {
|
||||
@@ -524,19 +525,27 @@ export const AbgabetoolAssistenz = {
|
||||
|
||||
this.$refs.modalContainerAbgabeDetail.show()
|
||||
},
|
||||
dateDiffInDays(datum, today){
|
||||
const oneDayMs = 1000 * 60 * 60 * 24
|
||||
return Math.round((new Date(datum) - new Date(today)) / oneDayMs)
|
||||
dateDiffInDays(datum){
|
||||
const dateToday = luxon.DateTime.now().startOf('day');
|
||||
|
||||
const dateDatum = luxon.DateTime.fromISO(datum).startOf('day');
|
||||
|
||||
const duration = dateDatum.diff(dateToday, 'days');
|
||||
|
||||
return duration.values.days;
|
||||
},
|
||||
getDateStyleClass(termin) {
|
||||
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) {
|
||||
termin.diffindays = this.dateDiffInDays(termin.datum)
|
||||
|
||||
// seperate status if termin is in the past, it needs a note but doesnt have one yet
|
||||
if(today > datum && termin.benotbar && !termin.note) return 'beurteilungerforderlich'
|
||||
else if (termin.abgabedatum === null) {
|
||||
if(datum < today) {
|
||||
return 'verpasst'
|
||||
} else if (datum > today && this.dateDiffInDays(datum, today) <= 12) {
|
||||
return termin.upload_allowed ? 'verpasst' : 'abgegeben'
|
||||
} else if (datum > today && termin.diffindays <= 12) {
|
||||
return 'abzugeben'
|
||||
} else {
|
||||
return 'standard'
|
||||
@@ -754,10 +763,10 @@ export const AbgabetoolAssistenz = {
|
||||
|
||||
this.$api.call(ApiStudiensemester.getAllStudiensemesterAndAktOrNext()).then((res) => {
|
||||
this.allSem = res.data[0]
|
||||
this.curSem = res.data[1]
|
||||
const all = {studiensemester_kurzbz: 'Alle'}
|
||||
this.curSem = all // res.data[1]
|
||||
|
||||
// TODO: maybe filter only for available semester from projektarbeiten dataset
|
||||
this.studiensemesterOptions = [{studiensemester_kurzbz: 'Alle'}, this.curSem, ...this.allSem]
|
||||
this.studiensemesterOptions = [all, ...this.allSem]
|
||||
|
||||
}).catch(e => {
|
||||
this.loading = false
|
||||
@@ -767,12 +776,13 @@ export const AbgabetoolAssistenz = {
|
||||
// fetch noten options
|
||||
//TODO: SWITCH TO NOTEN API ONCE NOTENTOOL IS IN MASTER TO AVOID DUPLICATE API
|
||||
this.$api.call(ApiAbgabe.getNoten()).then(res => {
|
||||
this.notenOptions = res.data
|
||||
// TODO: more sophisticated way to filter for these two, in essence it is still hardcoded
|
||||
this.allowedNotenOptions = this.notenOptions.filter(
|
||||
opt => opt.bezeichnung === 'Bestanden'
|
||||
|| opt.bezeichnung === 'Nicht bestanden'
|
||||
)
|
||||
if(res.meta.status == 'success') {
|
||||
this.notenOptions = res.data[0]
|
||||
|
||||
this.allowedNotenOptions = this.notenOptions.filter(
|
||||
opt => res.data[1].includes(opt.bezeichnung)
|
||||
)
|
||||
}
|
||||
|
||||
// allowedNotenOptions apply to quality gates abgabetermine
|
||||
// this selection is about graded projektarbeiten, so take different options here
|
||||
@@ -819,43 +829,51 @@ export const AbgabetoolAssistenz = {
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:default>
|
||||
<div class="row">
|
||||
<div class="col-1 d-flex justify-content-center align-items-center">
|
||||
{{$p.t('abgabetool/c4fixterminv4')}}
|
||||
</div>
|
||||
<div class="col-3 d-flex justify-content-center align-items-center">
|
||||
{{$capitalize($p.t('abgabetool/c4zieldatum'))}}
|
||||
</div>
|
||||
<div class="col-3 d-flex justify-content-center align-items-center">
|
||||
{{$p.t('abgabetool/c4abgabetyp')}}
|
||||
</div>
|
||||
<div class="col-5 d-flex justify-content-center align-items-center">
|
||||
{{$p.t('abgabetool/c4abgabekurzbz')}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1 d-flex justify-content-center align-items-center">
|
||||
<Checkbox
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 col-md-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4fixterminv4') )}}</div>
|
||||
<div class="col-12 col-md-9">
|
||||
<Checkbox
|
||||
v-model="serienTermin.fixtermin"
|
||||
:binary="true"
|
||||
:binary="true"
|
||||
:pt="{ root: { class: 'ml-auto' }}"
|
||||
>
|
||||
</Checkbox>
|
||||
</div>
|
||||
<div class="col-3 d-flex justify-content-center align-items-center">
|
||||
<div>
|
||||
<VueDatePicker
|
||||
style="width: 95%;"
|
||||
v-model="serienTermin.datum"
|
||||
:clearable="false"
|
||||
:enable-time-picker="false"
|
||||
:format="formatDate"
|
||||
:text-input="true"
|
||||
auto-apply>
|
||||
</VueDatePicker>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 col-md-3 align-content-center">
|
||||
<div class="row fw-bold" style="margin-left: 2px">{{$capitalize( $p.t('abgabetool/c4zieldatum') )}}</div>
|
||||
</div>
|
||||
<div class="col-3 d-flex justify-content-center align-items-center">
|
||||
<div class="col-12 col-md-9">
|
||||
<VueDatePicker
|
||||
style="width: 95%;"
|
||||
v-model="serienTermin.datum"
|
||||
:clearable="false"
|
||||
:enable-time-picker="false"
|
||||
:format="formatDate"
|
||||
:text-input="true"
|
||||
auto-apply>
|
||||
</VueDatePicker>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 col-md-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4upload_allowed') )}}</div>
|
||||
<div class="col-12 col-md-9">
|
||||
<Checkbox
|
||||
v-model="serienTermin.upload_allowed"
|
||||
:binary="true"
|
||||
:pt="{ root: { class: 'ml-auto' }}"
|
||||
>
|
||||
</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 col-md-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4abgabetyp') )}}</div>
|
||||
<div class="col-12 col-md-9">
|
||||
<Dropdown
|
||||
:style="{'width': '100%'}"
|
||||
v-model="serienTermin.bezeichnung"
|
||||
@@ -863,7 +881,11 @@ export const AbgabetoolAssistenz = {
|
||||
:optionLabel="getOptionLabelAbgabetyp">
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div class="col-5 d-flex justify-content-center align-items-center">
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 col-md-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4abgabekurzbz') )}}</div>
|
||||
<div class="col-12 col-md-9">
|
||||
<Textarea style="margin-bottom: 4px;" v-model="serienTermin.kurzbz" rows="1" class="w-100"></Textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,7 @@ export const AbgabetoolMitarbeiter = {
|
||||
BsModal,
|
||||
CoreFilterCmpt,
|
||||
AbgabeDetail,
|
||||
Checkbox: primevue.checkbox,
|
||||
Dropdown: primevue.dropdown,
|
||||
Textarea: primevue.textarea,
|
||||
VueDatePicker,
|
||||
@@ -19,6 +20,7 @@ export const AbgabetoolMitarbeiter = {
|
||||
provide() {
|
||||
return {
|
||||
abgabeTypeOptions: Vue.computed(() => this.abgabeTypeOptions),
|
||||
abgabetypenBetreuer: Vue.computed(() => this.abgabetypenBetreuer),
|
||||
allowedNotenOptions: Vue.computed(() => this.allowedNotenOptions),
|
||||
turnitin_link: Vue.computed(() => this.turnitin_link),
|
||||
old_abgabe_beurteilung_link: Vue.computed(() => this.old_abgabe_beurteilung_link)
|
||||
@@ -36,6 +38,7 @@ export const AbgabetoolMitarbeiter = {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
abgabetypenBetreuer: null,
|
||||
detailIsFullscreen: false,
|
||||
phrasenPromise: null,
|
||||
phrasenResolved: false,
|
||||
@@ -52,7 +55,8 @@ export const AbgabetoolMitarbeiter = {
|
||||
paabgabetyp_kurzbz: 'zwischen',
|
||||
bezeichnung: 'Zwischenabgabe'
|
||||
},
|
||||
kurzbz: ''
|
||||
kurzbz: '',
|
||||
upload_allowed: false
|
||||
}),
|
||||
showAll: false,
|
||||
tabulatorUuid: Vue.ref(0),
|
||||
@@ -179,6 +183,7 @@ export const AbgabetoolMitarbeiter = {
|
||||
this.serienTermin.bezeichnung.paabgabetyp_kurzbz,
|
||||
this.serienTermin.bezeichnung.bezeichnung,
|
||||
this.serienTermin.kurzbz,
|
||||
this.serienTermin.upload_allowed,
|
||||
this.selectedData?.map(projekt => projekt.projektarbeit_id),
|
||||
false
|
||||
)).then(res => {
|
||||
@@ -346,7 +351,9 @@ export const AbgabetoolMitarbeiter = {
|
||||
|
||||
},
|
||||
computed: {
|
||||
|
||||
getAllowedAbgabeTypeOptions() {
|
||||
return this.abgabeTypeOptions.filter(opt => this.abgabetypenBetreuer.includes(opt.bezeichnung))
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.phrasenPromise = this.$p.loadCategory(['abgabetool', 'global'])
|
||||
@@ -355,6 +362,7 @@ export const AbgabetoolMitarbeiter = {
|
||||
this.$api.call(ApiAbgabe.getConfig()).then(res => {
|
||||
this.turnitin_link = res.data?.turnitin_link
|
||||
this.old_abgabe_beurteilung_link = res.data?.old_abgabe_beurteilung_link
|
||||
this.abgabetypenBetreuer = res.data?.abgabetypenBetreuer
|
||||
}).catch(e => {
|
||||
console.log(e)
|
||||
this.loading = false
|
||||
@@ -363,12 +371,14 @@ export const AbgabetoolMitarbeiter = {
|
||||
// fetch noten options
|
||||
//TODO: SWITCH TO NOTEN API ONCE NOTENTOOL IS IN MASTER TO AVOID DUPLICATE API
|
||||
this.$api.call(ApiAbgabe.getNoten()).then(res => {
|
||||
this.notenOptions = res.data
|
||||
// TODO: more sophisticated way to filter for these two, in essence it is still hardcoded
|
||||
this.allowedNotenOptions = this.notenOptions.filter(
|
||||
opt => opt.bezeichnung === 'Bestanden'
|
||||
|| opt.bezeichnung === 'Nicht bestanden'
|
||||
)
|
||||
if(res.meta.status == 'success') {
|
||||
this.notenOptions = res.data[0]
|
||||
|
||||
this.allowedNotenOptions = this.notenOptions.filter(
|
||||
opt => res.data[1].includes(opt.bezeichnung)
|
||||
)
|
||||
}
|
||||
|
||||
}).catch(e => {
|
||||
this.loading = false
|
||||
})
|
||||
@@ -395,40 +405,53 @@ export const AbgabetoolMitarbeiter = {
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:default>
|
||||
<div class="row">
|
||||
<div class="col-3 d-flex justify-content-center align-items-center">
|
||||
{{$p.t('abgabetool/c4zieldatum')}}
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 col-md-3 align-content-center">
|
||||
<div class="row fw-bold" style="margin-left: 2px">{{$capitalize( $p.t('abgabetool/c4zieldatum') )}}</div>
|
||||
</div>
|
||||
<div class="col-3 d-flex justify-content-center align-items-center">
|
||||
{{$p.t('abgabetool/c4abgabetyp')}}
|
||||
</div>
|
||||
<div class="col-6 d-flex justify-content-center align-items-center">
|
||||
{{$p.t('abgabetool/c4abgabekurzbz')}}
|
||||
<div class="col-12 col-md-9">
|
||||
<VueDatePicker
|
||||
style="width: 95%;"
|
||||
v-model="serienTermin.datum"
|
||||
:clearable="false"
|
||||
:enable-time-picker="false"
|
||||
:format="formatDate"
|
||||
:text-input="true"
|
||||
auto-apply>
|
||||
</VueDatePicker>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-3 d-flex justify-content-center align-items-center">
|
||||
<div>
|
||||
<VueDatePicker
|
||||
style="width: 95%;"
|
||||
v-model="serienTermin.datum"
|
||||
:clearable="false"
|
||||
:enable-time-picker="false"
|
||||
:format="formatDate"
|
||||
:text-input="true"
|
||||
auto-apply>
|
||||
</VueDatePicker>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 col-md-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4upload_allowed') )}}</div>
|
||||
<div class="col-12 col-md-9">
|
||||
<Checkbox
|
||||
v-model="serienTermin.upload_allowed"
|
||||
:binary="true"
|
||||
:pt="{ root: { class: 'ml-auto' }}"
|
||||
>
|
||||
</Checkbox>
|
||||
</div>
|
||||
<div class="col-3 d-flex justify-content-center align-items-center">
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 col-md-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4abgabetyp') )}}</div>
|
||||
<div class="col-12 col-md-9"
|
||||
v-if="abgabetypenBetreuer && abgabeTypeOptions"
|
||||
>
|
||||
<Dropdown
|
||||
:style="{'width': '100%'}"
|
||||
v-model="serienTermin.bezeichnung"
|
||||
:options="abgabeTypeOptions"
|
||||
:options="getAllowedAbgabeTypeOptions"
|
||||
:optionLabel="getOptionLabelAbgabetyp">
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div class="col-6 d-flex justify-content-center align-items-center">
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 col-md-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4abgabekurzbz') )}}</div>
|
||||
<div class="col-12 col-md-9">
|
||||
<Textarea style="margin-bottom: 4px;" v-model="serienTermin.kurzbz" rows="1" class="w-100"></Textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -47,20 +47,26 @@ export const AbgabetoolStudent = {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
dateDiffInDays(datum, today){
|
||||
const oneDayMs = 1000 * 60 * 60 * 24
|
||||
return Math.round((new Date(datum) - new Date(today)) / oneDayMs)
|
||||
dateDiffInDays(datum){
|
||||
const dateToday = luxon.DateTime.now().startOf('day');
|
||||
|
||||
const dateDatum = luxon.DateTime.fromISO(datum).startOf('day');
|
||||
|
||||
const duration = dateDatum.diff(dateToday, 'days');
|
||||
|
||||
return duration.values.days;
|
||||
},
|
||||
getDateStyleClass(termin) {
|
||||
const datum = new Date(termin.datum)
|
||||
const abgabedatum = new Date(termin.abgabedatum)
|
||||
|
||||
// avoid renaming these statuses as their names are used as css keys
|
||||
// https://wiki.fhcomplete.info/doku.php?id=cis:abgabetool_fuer_studierende
|
||||
if (termin.abgabedatum === null) {
|
||||
termin.diffindays = this.dateDiffInDays(termin.datum)
|
||||
|
||||
if(today > datum && termin.benotbar && !termin.note) return 'beurteilungerforderlich'
|
||||
else if (termin.abgabedatum === null) {
|
||||
if(datum < today) {
|
||||
return 'verpasst'
|
||||
} else if (datum > today && this.dateDiffInDays(datum, today) <= 12) {
|
||||
return termin.upload_allowed ? 'verpasst' : 'abgegeben'
|
||||
} else if (datum > today && termin.diffindays <= 12) {
|
||||
return 'abzugeben'
|
||||
} else {
|
||||
return 'standard'
|
||||
@@ -71,7 +77,7 @@ export const AbgabetoolStudent = {
|
||||
return 'abgegeben'
|
||||
}
|
||||
},
|
||||
checkQualityGates(termine) {
|
||||
checkQualityGatesStrict(termine) {
|
||||
let qgate1Passed = false
|
||||
let qgate2Passed = false
|
||||
|
||||
@@ -88,6 +94,40 @@ export const AbgabetoolStudent = {
|
||||
|
||||
return qgate1Passed && qgate2Passed
|
||||
},
|
||||
checkQualityGatesOptional(termine) {
|
||||
const qgate1found = termine.find(t => t.paabgabetyp_kurzbz == 'qualgate1')
|
||||
const qgate2found = termine.find(t => t.paabgabetyp_kurzbz == 'qualgate2')
|
||||
|
||||
let qgate1positiv = true
|
||||
if(qgate1found) {
|
||||
qgate1positiv = false
|
||||
|
||||
termine.forEach(t => {
|
||||
const noteOption = this.notenOptions?.find(opt => opt.note == t.note)
|
||||
if(noteOption && noteOption.positiv) {
|
||||
if (t.paabgabetyp_kurzbz == 'qualgate1') {
|
||||
qgate1positiv = true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let qgate2positiv = true
|
||||
if(qgate2found) {
|
||||
qgate2positiv = false
|
||||
|
||||
termine.forEach(t => {
|
||||
const noteOption = this.notenOptions?.find(opt => opt.note == t.note)
|
||||
if(noteOption && noteOption.positiv) {
|
||||
if (t.paabgabetyp_kurzbz == 'qualgate2') {
|
||||
qgate2positiv = true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return qgate1positiv && qgate2positiv
|
||||
},
|
||||
isPastDate(date) {
|
||||
return new Date(date) < new Date(Date.now())
|
||||
},
|
||||
@@ -101,11 +141,15 @@ export const AbgabetoolStudent = {
|
||||
termin.allowedToUpload = false
|
||||
|
||||
if(termin.paabgabetyp_kurzbz == 'end') {
|
||||
// production logic
|
||||
termin.allowedToUpload = !this.isPastDate(termin.datum) && this.checkQualityGates(pa.abgabetermine)
|
||||
// production logic when qgates are required
|
||||
// termin.allowedToUpload = !this.isPastDate(termin.datum) && this.checkQualityGatesStrict(pa.abgabetermine)
|
||||
|
||||
// larifari we want qgates but they are optional fhtw mode
|
||||
termin.allowedToUpload = !this.isPastDate(termin.datum) && this.checkQualityGatesOptional(pa.abgabetermine)
|
||||
|
||||
|
||||
// development purposes
|
||||
// termin.allowedToUpload = this.checkQualityGates(pa.abgabetermine)
|
||||
// termin.allowedToUpload = this.checkQualityGatesStrict(pa.abgabetermine)
|
||||
// termin.allowedToUpload = true
|
||||
|
||||
} else if(termin.fixtermin) {
|
||||
@@ -257,7 +301,13 @@ export const AbgabetoolStudent = {
|
||||
this.loading = true
|
||||
//TODO: SWITCH TO NOTEN API ONCE NOTENTOOL IS IN MASTER TO AVOID DUPLICATE API
|
||||
await this.$api.call(ApiAbgabe.getNoten()).then(res => {
|
||||
this.notenOptions = res.data
|
||||
if(res.meta.status == 'success') {
|
||||
this.notenOptions = res.data[0]
|
||||
|
||||
this.allowedNotenOptions = this.notenOptions.filter(
|
||||
opt => res.data[1].includes(opt.bezeichnung)
|
||||
)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
|
||||
@@ -42538,6 +42538,26 @@ array(
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'abgabetool',
|
||||
'phrase' => 'c4abgabeuntil2359',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => "Abgabe bis 23:59",
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => "Submission until 23:59",
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'abgabetool',
|
||||
@@ -43558,6 +43578,26 @@ array(
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'abgabetool',
|
||||
'phrase' => 'c4tooltipBeurteilungerforderlich',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => "Beurteilung erforderlich",
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Grading necessary',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'abgabetool',
|
||||
@@ -44114,6 +44154,46 @@ array(
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'abgabetool',
|
||||
'phrase' => 'confirmEnduploadSpeichern',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => "Möchten Sie den Endupload durchführen?",
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Do you want to complete the final upload?',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'abgabetool',
|
||||
'phrase' => 'c4zusatzdatenausfuellen',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => "Alle Zusatzdatenfelder benötigen Eingabewerte!",
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'All additional data fields are required!',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'abgabetool',
|
||||
|
||||
Reference in New Issue
Block a user