mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-10-12 15:04:04 +00:00
Merge branch 'master' into bug-56209/is_valid_date_vs_isValidDate
This commit is contained in:
@@ -41,3 +41,5 @@ $config['STG_MOODLE_LINK'] = 'https://moodle.technikum-wien.at/course/view.php?i
|
|||||||
|
|
||||||
$config['ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT'] = true;
|
$config['ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT'] = true;
|
||||||
$config['ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER'] = true;
|
$config['ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER'] = true;
|
||||||
|
|
||||||
|
$config['BETREUER_SAMMELMAIL_BUTTON_STUDENT'] = true;
|
||||||
|
|||||||
@@ -89,13 +89,15 @@ class Abgabe extends FHCAPI_Controller
|
|||||||
$abgabetypenBetreuer = $this->config->item('ALLOWED_ABGABETYPEN_BETREUER');
|
$abgabetypenBetreuer = $this->config->item('ALLOWED_ABGABETYPEN_BETREUER');
|
||||||
$ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT = $this->config->item('ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT');
|
$ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT = $this->config->item('ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT');
|
||||||
$ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER = $this->config->item('ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER');
|
$ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER = $this->config->item('ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER');
|
||||||
|
$BETREUER_SAMMELMAIL_BUTTON_STUDENT = $this->config->item('BETREUER_SAMMELMAIL_BUTTON_STUDENT');
|
||||||
|
|
||||||
$ret = array(
|
$ret = array(
|
||||||
'old_abgabe_beurteilung_link' => $old_abgabe_beurteilung_link,
|
'old_abgabe_beurteilung_link' => $old_abgabe_beurteilung_link,
|
||||||
'turnitin_link' => $turnitin_link,
|
'turnitin_link' => $turnitin_link,
|
||||||
'abgabetypenBetreuer' => $abgabetypenBetreuer,
|
'abgabetypenBetreuer' => $abgabetypenBetreuer,
|
||||||
'ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT' => $ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT,
|
'ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT' => $ASSISTENZ_SAMMELMAIL_BUTTON_STUDENT,
|
||||||
'ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER' => $ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER
|
'ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER' => $ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER,
|
||||||
|
'BETREUER_SAMMELMAIL_BUTTON_STUDENT' => $BETREUER_SAMMELMAIL_BUTTON_STUDENT,
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->terminateWithSuccess($ret);
|
$this->terminateWithSuccess($ret);
|
||||||
@@ -373,6 +375,8 @@ class Abgabe extends FHCAPI_Controller
|
|||||||
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
|
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->checkPaabgabeDeadline($paabgabe_id);
|
||||||
|
|
||||||
$this->checkProjektarbeitForFinishedStatus($projektarbeit_id);
|
$this->checkProjektarbeitForFinishedStatus($projektarbeit_id);
|
||||||
|
|
||||||
$zugeordnet = $this->checkZuordnung($projektarbeit_id, getAuthUID());
|
$zugeordnet = $this->checkZuordnung($projektarbeit_id, getAuthUID());
|
||||||
@@ -445,6 +449,36 @@ class Abgabe extends FHCAPI_Controller
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validate paabgabe deadline against servertime just in case a student spoofs their local clock and thus
|
||||||
|
// unlocks the upload ui
|
||||||
|
private function checkPaabgabeDeadline($paabgabe_id) {
|
||||||
|
$this->load->model('education/Paabgabe_model', 'PaabgabeModel');
|
||||||
|
|
||||||
|
$result = $this->PaabgabeModel->load($paabgabe_id);
|
||||||
|
$paabgabeArr = $this->getDataOrTerminateWithError($result, 'general');
|
||||||
|
|
||||||
|
if (count($paabgabeArr) > 0) {
|
||||||
|
$paabgabe = $paabgabeArr[0];
|
||||||
|
} else {
|
||||||
|
$this->terminateWithError($this->p->t('abgabetool', 'c4projektabgabeNichtGefunden'), 'general');
|
||||||
|
}
|
||||||
|
|
||||||
|
// in that case any submission date is fine
|
||||||
|
if($paabgabe->fixtermin === false) return;
|
||||||
|
|
||||||
|
$tz = new DateTimeZone('Europe/Berlin');
|
||||||
|
$now = new DateTimeImmutable('now', $tz);
|
||||||
|
$deadline = DateTimeImmutable::createFromFormat(
|
||||||
|
'Y-m-d H:i:s',
|
||||||
|
$paabgabe->datum . ' 23:59:59',
|
||||||
|
$tz
|
||||||
|
);
|
||||||
|
|
||||||
|
if($now >= $deadline) {
|
||||||
|
$this->terminateWithError($this->p->t('abgabetool', 'c4deadlineExceeded'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tabulator tabledata fetch for abgabetool/mitarbeiter
|
* tabulator tabledata fetch for abgabetool/mitarbeiter
|
||||||
* initially fetches all currently active projektarbeiten with assigned mentorship
|
* initially fetches all currently active projektarbeiten with assigned mentorship
|
||||||
@@ -473,6 +507,15 @@ class Abgabe extends FHCAPI_Controller
|
|||||||
$projektarbeiten = $this->ProjektarbeitModel->getMitarbeiterProjektarbeiten(getAuthUID(), $showAllBool);
|
$projektarbeiten = $this->ProjektarbeitModel->getMitarbeiterProjektarbeiten(getAuthUID(), $showAllBool);
|
||||||
|
|
||||||
|
|
||||||
|
$mapFunc = function($projektarbeit) {
|
||||||
|
return $projektarbeit->projektarbeit_id;
|
||||||
|
};
|
||||||
|
$projektarbeiten_ids = array_map($mapFunc, $projektarbeiten->retval);
|
||||||
|
|
||||||
|
$ret = $this->ProjektarbeitModel->getProjektarbeitenAbgabetermine($projektarbeiten_ids);
|
||||||
|
$projektabgaben = $this->getDataOrTerminateWithError($ret, 'general');
|
||||||
|
|
||||||
|
|
||||||
forEach($projektarbeiten->retval as $pa) {
|
forEach($projektarbeiten->retval as $pa) {
|
||||||
|
|
||||||
$result = $this->ProjektarbeitModel->getProjektbetreuerAnrede($pa->betreuer_person_id);
|
$result = $this->ProjektarbeitModel->getProjektbetreuerAnrede($pa->betreuer_person_id);
|
||||||
@@ -489,6 +532,20 @@ class Abgabe extends FHCAPI_Controller
|
|||||||
Events::trigger('projektbeurteilung_formular_link', $pa->betreuerart_kurzbz, APP_ROOT, $pa->projektarbeit_id, $pa->student_uid, $returnFunc);
|
Events::trigger('projektbeurteilung_formular_link', $pa->betreuerart_kurzbz, APP_ROOT, $pa->projektarbeit_id, $pa->student_uid, $returnFunc);
|
||||||
$pa->beurteilungLinkNew = $newLink;
|
$pa->beurteilungLinkNew = $newLink;
|
||||||
$pa->beurteilungLinkOld = $oldLink;
|
$pa->beurteilungLinkOld = $oldLink;
|
||||||
|
|
||||||
|
// has previously been retrieved via getStudentProjektabgaben but is fetched in advance to avoid having to reload abgaben
|
||||||
|
$projektarbeitIsCurrent = false;
|
||||||
|
$returnFunc = function ($result) use (&$projektarbeitIsCurrent) {
|
||||||
|
$projektarbeitIsCurrent = $result;
|
||||||
|
};
|
||||||
|
Events::trigger('projektarbeit_is_current', $pa->projektarbeit_id, $returnFunc);
|
||||||
|
$pa->isCurrent = $projektarbeitIsCurrent;
|
||||||
|
|
||||||
|
$filterFunc = function($projektabgabe) use ($pa) {
|
||||||
|
return $projektabgabe->projektarbeit_id == $pa->projektarbeit_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
$pa->abgabetermine = array_values(array_filter($projektabgaben, $filterFunc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -544,7 +601,18 @@ class Abgabe extends FHCAPI_Controller
|
|||||||
'insertamum' => date('Y-m-d H:i:s')
|
'insertamum' => date('Y-m-d H:i:s')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$this->logLib->logInfoDB(array('paabgabe created',$result, getAuthUID(), getAuthPersonId()));
|
$this->logLib->logInfoDB(array('paabgabe created',array(
|
||||||
|
'projektarbeit_id' => $projektarbeit_id,
|
||||||
|
'paabgabetyp_kurzbz' => $paabgabetyp_kurzbz,
|
||||||
|
'fixtermin' => $fixtermin,
|
||||||
|
'datum' => $datum,
|
||||||
|
'kurzbz' => $kurzbz,
|
||||||
|
'note' => $note,
|
||||||
|
'beurteilungsnotiz' => $beurteilungsnotiz,
|
||||||
|
'upload_allowed' => $upload_allowed,
|
||||||
|
'insertvon' => getAuthUID(),
|
||||||
|
'insertamum' => date('Y-m-d H:i:s')
|
||||||
|
), getAuthUID(), getAuthPersonId()));
|
||||||
} else {
|
} else {
|
||||||
// load existing entry of paabgabe and check if note has changed to negativ, to avoid sending when
|
// load existing entry of paabgabe and check if note has changed to negativ, to avoid sending when
|
||||||
// only notiz has changed.
|
// only notiz has changed.
|
||||||
@@ -718,7 +786,16 @@ class Abgabe extends FHCAPI_Controller
|
|||||||
$abgaben[]= getData($this->PaabgabeModel->load($dataAbgabe))[0];
|
$abgaben[]= getData($this->PaabgabeModel->load($dataAbgabe))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->logLib->logInfoDB(array('serientermin angelegt',$res, getAuthUID(), getAuthPersonId()));
|
$this->logLib->logInfoDB(array('serientermin angelegt',array(
|
||||||
|
'projektarbeit_id' => $projektarbeit_id,
|
||||||
|
'paabgabetyp_kurzbz' => $paabgabetyp_kurzbz,
|
||||||
|
'fixtermin' => $fixtermin,
|
||||||
|
'datum' => $datum,
|
||||||
|
'kurzbz' => $kurzbz,
|
||||||
|
'upload_allowed' => $upload_allowed,
|
||||||
|
'insertvon' => getAuthUID(),
|
||||||
|
'insertamum' => date('Y-m-d H:i:s')
|
||||||
|
), getAuthUID(), getAuthPersonId()));
|
||||||
|
|
||||||
$this->terminateWithSuccess($abgaben);
|
$this->terminateWithSuccess($abgaben);
|
||||||
}
|
}
|
||||||
@@ -1167,7 +1244,7 @@ class Abgabe extends FHCAPI_Controller
|
|||||||
|
|
||||||
$email = $this->getProjektbetreuerEmailByProjektarbeitID($projektarbeit_id);
|
$email = $this->getProjektbetreuerEmailByProjektarbeitID($projektarbeit_id);
|
||||||
|
|
||||||
if(!$email) $this->terminateWithError($this->p->t('abgabetool', 'c4fehlerMailBegutachter'), 'general');
|
if(!$email) $this->terminateWithError($this->p->t('abgabetool', 'c4fehlerMailBegutachterv2'), 'general');
|
||||||
|
|
||||||
$mailres = sendSanchoMail(
|
$mailres = sendSanchoMail(
|
||||||
'ParbeitsbeurteilungEndupload',
|
'ParbeitsbeurteilungEndupload',
|
||||||
@@ -1180,7 +1257,7 @@ class Abgabe extends FHCAPI_Controller
|
|||||||
|
|
||||||
if(!$mailres)
|
if(!$mailres)
|
||||||
{
|
{
|
||||||
$this->terminateWithError($this->p->t('abgabetool', 'c4fehlerMailBegutachter'), 'general');
|
$this->terminateWithError($this->p->t('abgabetool', 'c4fehlerMailBegutachterv2'), 'general');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Begutachter mail, wenn Endabgabe, mit Token wenn extern
|
// 2. Begutachter mail, wenn Endabgabe, mit Token wenn extern
|
||||||
@@ -1200,14 +1277,14 @@ class Abgabe extends FHCAPI_Controller
|
|||||||
|
|
||||||
if (!$tokenGenRes)
|
if (!$tokenGenRes)
|
||||||
{
|
{
|
||||||
$this->terminateWithError($this->p->t('abgabetool', 'c4fehlerMailZweitBegutachter'), 'general');
|
$this->terminateWithError($this->p->t('abgabetool', 'c4fehlerMailZweitBegutachterv2'), 'general');
|
||||||
}
|
}
|
||||||
|
|
||||||
$begutachterMitTokenRetval = getData($this->ProjektbetreuerModel->getZweitbegutachterWithToken($bperson_id, $projektarbeit_id, $studentUser->uid, $begutachter->person_id));
|
$begutachterMitTokenRetval = getData($this->ProjektbetreuerModel->getZweitbegutachterWithToken($bperson_id, $projektarbeit_id, $studentUser->uid, $begutachter->person_id));
|
||||||
|
|
||||||
if (!$begutachterMitTokenRetval && count($begutachterMitTokenRetval) <= 0)
|
if (!$begutachterMitTokenRetval && count($begutachterMitTokenRetval) <= 0)
|
||||||
{
|
{
|
||||||
$this->terminateWithError($this->p->t('abgabetool', 'c4fehlerMailZweitBegutachter'), 'general');
|
$this->terminateWithError($this->p->t('abgabetool', 'c4fehlerMailZweitBegutachterv2'), 'general');
|
||||||
}
|
}
|
||||||
|
|
||||||
$begutachterMitToken = $begutachterMitTokenRetval[0];
|
$begutachterMitToken = $begutachterMitTokenRetval[0];
|
||||||
@@ -1241,7 +1318,7 @@ class Abgabe extends FHCAPI_Controller
|
|||||||
|
|
||||||
if (!$mailres)
|
if (!$mailres)
|
||||||
{
|
{
|
||||||
$this->terminateWithError($this->p->t('abgabetool', 'c4fehlerMailBegutachter'), 'general');
|
$this->terminateWithError($this->p->t('abgabetool', 'c4fehlerMailBegutachterv2'), 'general');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,10 +23,271 @@ class AbgabetoolJob extends JOB_Controller
|
|||||||
$this->_ci->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
$this->_ci->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||||
$this->_ci->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
|
$this->_ci->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
|
||||||
|
|
||||||
|
$this->_ci->load->library('SignatureLib');
|
||||||
|
|
||||||
$this->_ci->load->config('abgabe');
|
$this->_ci->load->config('abgabe');
|
||||||
$this->loadPhrases([
|
$this->loadPhrases([
|
||||||
'abgabetool'
|
'abgabetool'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// basically the notifyBetreuerMail function but email goes to assistenz
|
||||||
|
// and new abgaben are further evaluated for missing signature status
|
||||||
|
public function notifyAssistenzAboutMissingSignatureUploads() {
|
||||||
|
$this->_ci->logInfo('Start job FHC-Core->notifyAssistenzAboutMissingSignatureUploads');
|
||||||
|
|
||||||
|
$interval = $this->_ci->config->item('PAABGABE_EMAIL_JOB_INTERVAL');
|
||||||
|
$relevantTypes = $this->_ci->config->item('RELEVANT_PAABGABETYPEN_SAMMELMAIL_ASSISTENZ');
|
||||||
|
|
||||||
|
$result = $this->_ci->PaabgabeModel->findAbgabenNewOrUpdatedSinceByAbgabedatum($interval, $relevantTypes);
|
||||||
|
$retval = getData($result);
|
||||||
|
|
||||||
|
// retval are paabgaben joined with projektarbeit and betreuer
|
||||||
|
if(count($retval) == 0) {
|
||||||
|
$this->logInfo("Keine Emails über neue Paabgaben an Assistenzen versandt");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// group changed/new abgaben for projektarbeiten
|
||||||
|
$projektarbeiten = [];
|
||||||
|
foreach($retval as $abgabeWithNewUpload) {
|
||||||
|
// Check if the current item has a 'projektarbeit_id' field.
|
||||||
|
// Replace 'projektarbeit_id' with the actual key name if it's different.
|
||||||
|
if (isset($abgabeWithNewUpload->projektarbeit_id)) {
|
||||||
|
$projektarbeitId = $abgabeWithNewUpload->projektarbeit_id;
|
||||||
|
|
||||||
|
// If the 'projektarbeit_id' is not yet a key in $projektarbeiten,
|
||||||
|
// initialize it as an empty array.
|
||||||
|
if (!isset($projektarbeiten[$projektarbeitId])) {
|
||||||
|
$projektarbeiten[$projektarbeitId] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// check signature for that abgabe, main point of this job
|
||||||
|
$this->checkAbgabeSignatur($abgabeWithNewUpload, $abgabeWithNewUpload->student_uid);
|
||||||
|
|
||||||
|
// Add the current row to the array associated with its 'projektarbeit_id'.
|
||||||
|
$projektarbeiten[$projektarbeitId][] = $abgabeWithNewUpload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for each projektarbeit fetch their assistenz and same them in their own dictionary to avoid too many mails
|
||||||
|
$assistenzMap = [];
|
||||||
|
// for each projektarbeit fetch their betreuer and save them in their own dictionary to avoid too many mails
|
||||||
|
$projektarbeitBetreuerMap = [];
|
||||||
|
forEach($projektarbeiten as $projektarbeit_id => $abgaben) {
|
||||||
|
|
||||||
|
$assistenzResult = $this->_ci->OrganisationseinheitModel->getAssistenzForOE($abgaben[0]->stg_oe_kurzbz);
|
||||||
|
|
||||||
|
forEach($assistenzResult->retval as $assistenzRow) {
|
||||||
|
if (!isset($assistenzMap[$assistenzRow->person_id])) {
|
||||||
|
$assistenzMap[$assistenzRow->person_id] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the current $assistenzRow to the $assistenzMap as an array associated with its projektarbeit_id.
|
||||||
|
$assistenzMap[$assistenzRow->person_id][] = [$projektarbeit_id, $assistenzRow];
|
||||||
|
}
|
||||||
|
|
||||||
|
$betreuerResult = $this->_ci->ProjektbetreuerModel->getAllBetreuerOfProjektarbeit($projektarbeit_id);
|
||||||
|
|
||||||
|
forEach($betreuerResult->retval as $betreuerRow) {
|
||||||
|
if (!isset($projektarbeitBetreuerMap[$projektarbeit_id])) {
|
||||||
|
$projektarbeitBetreuerMap[$projektarbeit_id] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the current betreuerRow to the betreuerMap as an array associated with its projektarbeit_id.
|
||||||
|
$projektarbeitBetreuerMap[$projektarbeit_id][] = $betreuerRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
foreach($assistenzMap as $assistenz_person_id => $tupelArr) {
|
||||||
|
|
||||||
|
$abgabenString = '<div style="font-family: Arial, sans-serif; color: #333;">';
|
||||||
|
$hasIssues = false; // Track if this assistant actually needs an email
|
||||||
|
|
||||||
|
foreach($tupelArr as $tupel) {
|
||||||
|
$projektarbeit_id = $tupel[0];
|
||||||
|
$assistenzRow = $tupel[1];
|
||||||
|
|
||||||
|
$betreuerArray = $projektarbeitBetreuerMap[$projektarbeit_id] ?? [];
|
||||||
|
$allAbgaben = $projektarbeiten[$projektarbeit_id];
|
||||||
|
|
||||||
|
// only keep abgaben that are not correctly signed
|
||||||
|
$issueAbgaben = array_filter($allAbgaben, function($abgabe) {
|
||||||
|
// We only care about cases where it's explicitly NOT true (false, error, or null)
|
||||||
|
return $abgabe->signatur !== true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// if this specific project has no signature issues, skip to the next project
|
||||||
|
if(empty($issueAbgaben)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reached here, we have at least one issue to report
|
||||||
|
$hasIssues = true;
|
||||||
|
|
||||||
|
// Format the Student Name (using the first available abgabe object)
|
||||||
|
$s = reset($issueAbgaben);
|
||||||
|
$nameParts = array_filter([$s->titelpre, $s->vorname, $s->nachname, $s->titelpost]);
|
||||||
|
$studentFullName = implode(' ', $nameParts);
|
||||||
|
|
||||||
|
// Format the Supervisors string
|
||||||
|
$betreuerStrings = [];
|
||||||
|
foreach($betreuerArray as $b) {
|
||||||
|
$bNameParts = array_filter([$b->titelpre, $b->vorname, $b->nachname, $b->titelpost]);
|
||||||
|
$bFullName = implode(' ', $bNameParts);
|
||||||
|
$betreuerStrings[] = "{$bFullName} ({$b->betreuerart_kurzbz})";
|
||||||
|
}
|
||||||
|
$allBetreuerFormatted = implode(', ', $betreuerStrings);
|
||||||
|
|
||||||
|
$projektarbeit_titel = $s->titel ?? 'Kein Titel vergeben';
|
||||||
|
|
||||||
|
// Project Header Section
|
||||||
|
$abgabenString .= "
|
||||||
|
<div style='margin-top: 25px; padding: 12px; background-color: #fff5f5; border-left: 4px solid #dc3545; border-bottom: 1px solid #fee;'>
|
||||||
|
<strong style='font-size: 16px; color: #b02a37;'>Projekt: {$projektarbeit_titel}</strong><br/>
|
||||||
|
<div style='margin-top: 5px; font-size: 14px;'>
|
||||||
|
<strong>Studierende/r:</strong> {$studentFullName}
|
||||||
|
</div>
|
||||||
|
<div style='margin-top: 3px; font-size: 14px;'>
|
||||||
|
<strong>Betreuer:</strong> {$allBetreuerFormatted}
|
||||||
|
</div>
|
||||||
|
<span style='color: #666; font-size: 12px;'>
|
||||||
|
ID: {$projektarbeit_id} | Stg: {$s->stgtyp}{$s->stgkz} ({$s->studiensemester_kurzbz})
|
||||||
|
</span>
|
||||||
|
</div>";
|
||||||
|
|
||||||
|
// Start Table
|
||||||
|
$abgabenString .= '
|
||||||
|
<table style="width: 100%; border-collapse: collapse; margin-bottom: 25px;">
|
||||||
|
<thead>
|
||||||
|
<tr style="background-color: #f8f9fa; text-align: left;">
|
||||||
|
<th style="padding: 10px; border: 1px solid #ddd; font-size: 13px; width: 20%;">Datum</th>
|
||||||
|
<th style="padding: 10px; border: 1px solid #ddd; font-size: 13px; width: 45%;">Abgabe/Bezeichnung</th>
|
||||||
|
<th style="padding: 10px; border: 1px solid #ddd; font-size: 13px; width: 35%;">Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>';
|
||||||
|
|
||||||
|
$printed = []; // lazy hack to avoid duplicate rows
|
||||||
|
foreach ($issueAbgaben as $abgabe) {
|
||||||
|
// if we had this paabgabe already (erstbetreuer/zweitbetreuer fetch achieves duplicates
|
||||||
|
if(in_array($abgabe->paabgabe_id, $printed)) {
|
||||||
|
continue; // skip this forEach iteration
|
||||||
|
}
|
||||||
|
|
||||||
|
$printed[] = $abgabe->paabgabe_id;
|
||||||
|
|
||||||
|
$abgabedatumFormatted = (new DateTime($abgabe->abgabedatum))->format('d.m.Y');
|
||||||
|
|
||||||
|
// label and color
|
||||||
|
if ($abgabe->signatur === false) {
|
||||||
|
$sigLabel = "FEHLENDE SIGNATUR";
|
||||||
|
$sigBg = "#dc3545";
|
||||||
|
} elseif ($abgabe->signatur === 'error') {
|
||||||
|
$sigLabel = "PRÜFUNG FEHLGESCHLAGEN";
|
||||||
|
$sigBg = "#fd7e14";
|
||||||
|
} else {
|
||||||
|
$sigLabel = "DATEI NICHT GEFUNDEN";
|
||||||
|
$sigBg = "#6c757d";
|
||||||
|
}
|
||||||
|
|
||||||
|
$abgabenString .= "
|
||||||
|
<tr>
|
||||||
|
<td style='padding: 10px; border: 1px solid #ddd; font-size: 13px; vertical-align: top;'>{$abgabedatumFormatted}</td>
|
||||||
|
<td style='padding: 10px; border: 1px solid #ddd; font-size: 13px;'>
|
||||||
|
<strong>{$abgabe->bezeichnung}</strong>
|
||||||
|
</td>
|
||||||
|
<td style='padding: 10px; border: 1px solid #ddd; font-size: 13px; text-align: center;'>
|
||||||
|
<span style='color: #fff; background-color: {$sigBg}; padding: 3px 8px; border-radius: 3px; font-weight: bold; font-size: 11px;'>
|
||||||
|
{$sigLabel}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>";
|
||||||
|
}
|
||||||
|
|
||||||
|
$abgabenString .= '</tbody></table>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$abgabenString .= '</div>';
|
||||||
|
|
||||||
|
// only send the email if at least one project had an issue
|
||||||
|
if ($hasIssues) {
|
||||||
|
$assistenzRow = $tupelArr[0][1];
|
||||||
|
$anrede = $assistenzRow->anrede;
|
||||||
|
$anredeFillString = $assistenzRow->anrede == "Herr" ? "r" : "";
|
||||||
|
$fullFormattedNameString = $assistenzRow->first;
|
||||||
|
|
||||||
|
$path = $this->_ci->config->item('URL_ASSISTENZ');
|
||||||
|
$url = CIS_ROOT . $path;
|
||||||
|
|
||||||
|
$body_fields = array(
|
||||||
|
'anrede' => $anrede,
|
||||||
|
'anredeFillString' => $anredeFillString,
|
||||||
|
'fullFormattedNameString' => $fullFormattedNameString,
|
||||||
|
'abgabenString' => $abgabenString,
|
||||||
|
'linkAbgabetool' => $url
|
||||||
|
);
|
||||||
|
|
||||||
|
$email = $assistenzRow->uid . "@" . DOMAIN;
|
||||||
|
|
||||||
|
sendSanchoMail(
|
||||||
|
'PAANoSigAssSM',
|
||||||
|
$body_fields,
|
||||||
|
$email,
|
||||||
|
$this->p->t('abgabetool', 'c4missingSignatureNotification')
|
||||||
|
);
|
||||||
|
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_ci->logInfo($count . " Emails bezüglich fehlender Signaturen erfolgreich versandt");
|
||||||
|
$this->_ci->logInfo('End job FHC-Core->notifyAssistenzAboutMissingSignatureUploads');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper function to check the signature status of uploaded files for zwischenabgabe & endupload
|
||||||
|
*/
|
||||||
|
private function checkAbgabeSignatur($abgabe, $student_uid) {
|
||||||
|
$paabgabetypenToCheck = $this->config->item('SIGNATUR_CHECK_PAABGABETYPEN');
|
||||||
|
|
||||||
|
if(!in_array($abgabe->paabgabetyp_kurzbz, $paabgabetypenToCheck)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defined('SIGNATUR_URL')) {
|
||||||
|
$abgabe->signatur = 'error';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = PAABGABE_PATH.$abgabe->paabgabe_id.'_'.$student_uid.'.pdf';
|
||||||
|
|
||||||
|
$signaturVorhanden = null; // if frontend receives null -> indicates no file found at path
|
||||||
|
if(file_exists($path)) {
|
||||||
|
|
||||||
|
// Check if the document is signed
|
||||||
|
$signList = SignatureLib::list($path);
|
||||||
|
if (is_array($signList) && count($signList) > 0)
|
||||||
|
{
|
||||||
|
// The document is signed
|
||||||
|
$signaturVorhanden = true;
|
||||||
|
}
|
||||||
|
elseif ($signList === null)
|
||||||
|
{
|
||||||
|
// frontend knows to handle it this way for signatures
|
||||||
|
$signaturVorhanden = 'error';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$signaturVorhanden = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$abgabe->signatur = $signaturVorhanden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function notifyAssistenzAboutChangedAbgaben() {
|
public function notifyAssistenzAboutChangedAbgaben() {
|
||||||
@@ -235,11 +496,6 @@ class AbgabetoolJob extends JOB_Controller
|
|||||||
$result = $this->_ci->PaabgabeModel->findAbgabenNewOrUpdatedSince($interval, $relevantTypes);
|
$result = $this->_ci->PaabgabeModel->findAbgabenNewOrUpdatedSince($interval, $relevantTypes);
|
||||||
$retval = getData($result);
|
$retval = getData($result);
|
||||||
|
|
||||||
if(count($retval) == 0) {
|
|
||||||
$this->_ci->logInfo("Keine Emails an Betreuer über neue oder veränderte Termine versandt");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// group changed/new abgaben for projektarbeiten
|
// group changed/new abgaben for projektarbeiten
|
||||||
$projektarbeiten = [];
|
$projektarbeiten = [];
|
||||||
foreach($retval as $newOrChangedAbgabe) {
|
foreach($retval as $newOrChangedAbgabe) {
|
||||||
@@ -248,6 +504,13 @@ class AbgabetoolJob extends JOB_Controller
|
|||||||
if (isset($newOrChangedAbgabe->projektarbeit_id)) {
|
if (isset($newOrChangedAbgabe->projektarbeit_id)) {
|
||||||
$projektarbeitId = $newOrChangedAbgabe->projektarbeit_id;
|
$projektarbeitId = $newOrChangedAbgabe->projektarbeit_id;
|
||||||
|
|
||||||
|
// check if the updatevon field is NOT the same as the student the projektarbeit is assigned to
|
||||||
|
// since uploading a file to a paabgabe is also putting updateamum & updatevon
|
||||||
|
// we have our own "student has uploaded a file" emailjob anyways
|
||||||
|
if($newOrChangedAbgabe->student_uid === $newOrChangedAbgabe->updatevon) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// If the 'projektarbeit_id' is not yet a key in $projektarbeiten,
|
// If the 'projektarbeit_id' is not yet a key in $projektarbeiten,
|
||||||
// initialize it as an empty array.
|
// initialize it as an empty array.
|
||||||
if (!isset($projektarbeiten[$projektarbeitId])) {
|
if (!isset($projektarbeiten[$projektarbeitId])) {
|
||||||
@@ -259,6 +522,11 @@ class AbgabetoolJob extends JOB_Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(count($projektarbeiten) == 0) {
|
||||||
|
$this->_ci->logInfo("Keine Emails an Betreuer über neue oder veränderte Termine versandt");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// for each projektarbeit fetch their betreuer and save them in their own dictionary to avoid too many mails
|
// for each projektarbeit fetch their betreuer and save them in their own dictionary to avoid too many mails
|
||||||
$betreuerMap = [];
|
$betreuerMap = [];
|
||||||
forEach($projektarbeiten as $projektarbeit_id => $abgaben) {
|
forEach($projektarbeiten as $projektarbeit_id => $abgaben) {
|
||||||
@@ -378,6 +646,11 @@ class AbgabetoolJob extends JOB_Controller
|
|||||||
|
|
||||||
$email = $betreuerRow->uid ? $betreuerRow->uid."@".DOMAIN : $betreuerRow->private_email;
|
$email = $betreuerRow->uid ? $betreuerRow->uid."@".DOMAIN : $betreuerRow->private_email;
|
||||||
|
|
||||||
|
if(!$email) {
|
||||||
|
$this->_ci->logInfo('Could not send Email for Betreuer PersonID: "'.$data->person_id.'".');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// send email with bundled info
|
// send email with bundled info
|
||||||
sendSanchoMail(
|
sendSanchoMail(
|
||||||
'PAAChangesBetSM',
|
'PAAChangesBetSM',
|
||||||
@@ -500,6 +773,12 @@ class AbgabetoolJob extends JOB_Controller
|
|||||||
|
|
||||||
$email = $data->uid ? $data->uid."@".DOMAIN : $data->private_email;
|
$email = $data->uid ? $data->uid."@".DOMAIN : $data->private_email;
|
||||||
|
|
||||||
|
// in rare cases there are betreuer (often zweitbetreuer) without uid and without private email
|
||||||
|
if(!$email) {
|
||||||
|
$this->_ci->logInfo('Could not send Email for Betreuer PersonID: "'.$data->person_id.'".');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// send email with bundled info
|
// send email with bundled info
|
||||||
sendSanchoMail(
|
sendSanchoMail(
|
||||||
'PaabgabeUpdatesBetSM',
|
'PaabgabeUpdatesBetSM',
|
||||||
|
|||||||
@@ -86,11 +86,14 @@ class Paabgabe_model extends DB_Model
|
|||||||
return $this->execQuery($query, [$interval, $interval, $relevantTypes]);
|
return $this->execQuery($query, [$interval, $interval, $relevantTypes]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findAbgabenNewOrUpdatedSinceByAbgabedatum($interval) {
|
public function findAbgabenNewOrUpdatedSinceByAbgabedatum($interval, $relevantTypes = null) {
|
||||||
|
|
||||||
$query = "SELECT projektarbeit_id, paabgabe_id, paabgabetyp_kurzbz, fixtermin, datum, kurzbz, campus.tbl_paabgabetyp.bezeichnung, campus.tbl_paabgabe.abgabedatum,
|
$queryParams = [$interval];
|
||||||
|
$query = "SELECT projektarbeit_id, paabgabe_id, paabgabetyp_kurzbz, fixtermin, datum, campus.tbl_paabgabe.kurzbz, campus.tbl_paabgabetyp.bezeichnung, campus.tbl_paabgabe.abgabedatum,
|
||||||
campus.tbl_paabgabe.insertvon, campus.tbl_paabgabe.insertamum, campus.tbl_paabgabe.updatevon, campus.tbl_paabgabe.updateamum,
|
campus.tbl_paabgabe.insertvon, campus.tbl_paabgabe.insertamum, campus.tbl_paabgabe.updatevon, campus.tbl_paabgabe.updateamum,
|
||||||
campus.tbl_paabgabe.note, upload_allowed, beurteilungsnotiz, student_uid, tbl_projektarbeit.note, lehre.tbl_projektarbeit.titel,
|
campus.tbl_paabgabe.note, upload_allowed, beurteilungsnotiz, student_uid, tbl_projektarbeit.note, lehre.tbl_projektarbeit.titel,
|
||||||
|
UPPER(tbl_studiengang.typ) as stgtyp, UPPER(tbl_studiengang.kurzbz) as stgkz, public.tbl_studiengang.studiengang_kz,
|
||||||
|
public.tbl_studiengang.oe_kurzbz as stg_oe_kurzbz, tbl_lehreinheit.studiensemester_kurzbz,
|
||||||
lehre.tbl_projektbetreuer.betreuerart_kurzbz, lehre.tbl_projektbetreuer.person_id,
|
lehre.tbl_projektbetreuer.betreuerart_kurzbz, lehre.tbl_projektbetreuer.person_id,
|
||||||
public.tbl_person.anrede, public.tbl_person.titelpre, public.tbl_person.vorname, public.tbl_person.nachname, public.tbl_person.titelpost
|
public.tbl_person.anrede, public.tbl_person.titelpre, public.tbl_person.vorname, public.tbl_person.nachname, public.tbl_person.titelpost
|
||||||
|
|
||||||
@@ -98,15 +101,23 @@ class Paabgabe_model extends DB_Model
|
|||||||
JOIN campus.tbl_paabgabetyp USING (paabgabetyp_kurzbz)
|
JOIN campus.tbl_paabgabetyp USING (paabgabetyp_kurzbz)
|
||||||
JOIN lehre.tbl_projektarbeit USING (projektarbeit_id)
|
JOIN lehre.tbl_projektarbeit USING (projektarbeit_id)
|
||||||
JOIN lehre.tbl_projektbetreuer USING (projektarbeit_id)
|
JOIN lehre.tbl_projektbetreuer USING (projektarbeit_id)
|
||||||
|
JOIN lehre.tbl_lehreinheit using(lehreinheit_id)
|
||||||
|
JOIN lehre.tbl_lehrveranstaltung using(lehrveranstaltung_id)
|
||||||
|
JOIN public.tbl_studiengang on(lehre.tbl_lehrveranstaltung.studiengang_kz = public.tbl_studiengang.studiengang_kz)
|
||||||
JOIN public.tbl_benutzer ON (public.tbl_benutzer.uid = student_uid)
|
JOIN public.tbl_benutzer ON (public.tbl_benutzer.uid = student_uid)
|
||||||
JOIN public.tbl_person ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
|
JOIN public.tbl_person ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
|
||||||
|
|
||||||
WHERE campus.tbl_paabgabe.abgabedatum IS NOT NULL
|
WHERE campus.tbl_paabgabe.abgabedatum IS NOT NULL
|
||||||
AND campus.tbl_paabgabe.abgabedatum >= NOW() - INTERVAL ?
|
AND campus.tbl_paabgabe.abgabedatum >= NOW() - INTERVAL ?";
|
||||||
ORDER BY abgabedatum DESC
|
|
||||||
";
|
|
||||||
|
|
||||||
return $this->execQuery($query, [$interval]);
|
if($relevantTypes !== null) {
|
||||||
|
$query .= " AND campus.tbl_paabgabe.paabgabetyp_kurzbz IN ?";
|
||||||
|
$queryParams[]= $relevantTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query .= " ORDER BY abgabedatum DESC";
|
||||||
|
|
||||||
|
return $this->execQuery($query, $queryParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadByIDs($paabgabe_ids) {
|
public function loadByIDs($paabgabe_ids) {
|
||||||
|
|||||||
@@ -354,8 +354,10 @@ class Projektarbeit_model extends DB_Model
|
|||||||
student_person.nachname as student_nachname,
|
student_person.nachname as student_nachname,
|
||||||
tbl_student.matrikelnr, tbl_lehreinheit.studiensemester_kurzbz,
|
tbl_student.matrikelnr, tbl_lehreinheit.studiensemester_kurzbz,
|
||||||
betreuer_benutzer.uid as betreuer_benutzer_uid,
|
betreuer_benutzer.uid as betreuer_benutzer_uid,
|
||||||
|
betreuer_person.titelpre as betreuer_titelpre,
|
||||||
betreuer_person.vorname as betreuer_vorname,
|
betreuer_person.vorname as betreuer_vorname,
|
||||||
betreuer_person.nachname as betreuer_nachname,
|
betreuer_person.nachname as betreuer_nachname,
|
||||||
|
betreuer_person.titelpost as betreuer_titelpost,
|
||||||
lehre.tbl_projektbetreuer.betreuerart_kurzbz as betreuerart,
|
lehre.tbl_projektbetreuer.betreuerart_kurzbz as betreuerart,
|
||||||
lehre.tbl_projektbetreuer.person_id as betreuer_person_id,
|
lehre.tbl_projektbetreuer.person_id as betreuer_person_id,
|
||||||
lehre.tbl_projektarbeit.sprache as sprache,
|
lehre.tbl_projektarbeit.sprache as sprache,
|
||||||
@@ -415,6 +417,50 @@ class Projektarbeit_model extends DB_Model
|
|||||||
LIMIT 1
|
LIMIT 1
|
||||||
)
|
)
|
||||||
as zweitbetreuer_full_name,
|
as zweitbetreuer_full_name,
|
||||||
|
(
|
||||||
|
SELECT titelpre
|
||||||
|
FROM public.tbl_person
|
||||||
|
JOIN lehre.tbl_projektbetreuer ON (lehre.tbl_projektbetreuer.person_id = public.tbl_person.person_id)
|
||||||
|
LEFT JOIN public.tbl_benutzer ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
|
||||||
|
LEFT JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid)
|
||||||
|
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
|
||||||
|
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
as zweitbetreuer_titelpre,
|
||||||
|
(
|
||||||
|
SELECT vorname
|
||||||
|
FROM public.tbl_person
|
||||||
|
JOIN lehre.tbl_projektbetreuer ON (lehre.tbl_projektbetreuer.person_id = public.tbl_person.person_id)
|
||||||
|
LEFT JOIN public.tbl_benutzer ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
|
||||||
|
LEFT JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid)
|
||||||
|
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
|
||||||
|
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
as zweitbetreuer_vorname,
|
||||||
|
(
|
||||||
|
SELECT nachname
|
||||||
|
FROM public.tbl_person
|
||||||
|
JOIN lehre.tbl_projektbetreuer ON (lehre.tbl_projektbetreuer.person_id = public.tbl_person.person_id)
|
||||||
|
LEFT JOIN public.tbl_benutzer ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
|
||||||
|
LEFT JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid)
|
||||||
|
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
|
||||||
|
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
as zweitbetreuer_nachname,
|
||||||
|
(
|
||||||
|
SELECT titelpost
|
||||||
|
FROM public.tbl_person
|
||||||
|
JOIN lehre.tbl_projektbetreuer ON (lehre.tbl_projektbetreuer.person_id = public.tbl_person.person_id)
|
||||||
|
LEFT JOIN public.tbl_benutzer ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
|
||||||
|
LEFT JOIN public.tbl_mitarbeiter ON (public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid)
|
||||||
|
WHERE projektarbeit_id = tbl_projektarbeit.projektarbeit_id
|
||||||
|
AND betreuerart_kurzbz IN ('Zweitbetreuer', 'Zweitbegutachter', 'Senatsmitglied')
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
as zweitbetreuer_titelpost,
|
||||||
(
|
(
|
||||||
SELECT
|
SELECT
|
||||||
COALESCE(tbl_studienplan.orgform_kurzbz,
|
COALESCE(tbl_studienplan.orgform_kurzbz,
|
||||||
|
|||||||
@@ -1041,7 +1041,7 @@ function sendEmail($coodle_id)
|
|||||||
."END:STANDARD\r\n"
|
."END:STANDARD\r\n"
|
||||||
."END:VTIMEZONE\r\n"
|
."END:VTIMEZONE\r\n"
|
||||||
."BEGIN:VEVENT\r\n"
|
."BEGIN:VEVENT\r\n"
|
||||||
.$coodle->foldContentLine("ORGANIZER:MAILTO:".$erstellername." <".$coodle->ersteller_uid."@".DOMAIN)."\r\n"
|
.$coodle->foldContentLine("ORGANIZER:MAILTO:".$erstellername." <".$coodle->ersteller_uid."@".DOMAIN).">\r\n"
|
||||||
.rtrim($teilnehmer)."\r\n"
|
.rtrim($teilnehmer)."\r\n"
|
||||||
."DTSTART;TZID=Europe/Vienna:".$dtstart."\r\n"
|
."DTSTART;TZID=Europe/Vienna:".$dtstart."\r\n"
|
||||||
."DTEND;TZID=Europe/Vienna:".$dtend."\r\n"
|
."DTEND;TZID=Europe/Vienna:".$dtend."\r\n"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import BsModal from '../../Bootstrap/Modal.js';
|
import BsModal from '../../Bootstrap/Modal.js';
|
||||||
import VueDatePicker from '../../vueDatepicker.js.php';
|
import VueDatePicker from '../../vueDatepicker.js.php';
|
||||||
import ApiAbgabe from '../../../api/factory/abgabe.js'
|
import ApiAbgabe from '../../../api/factory/abgabe.js'
|
||||||
|
import { getDateStyleClass } from "./getDateStyleClass.js";
|
||||||
|
|
||||||
const today = new Date()
|
|
||||||
export const AbgabeMitarbeiterDetail = {
|
export const AbgabeMitarbeiterDetail = {
|
||||||
name: "AbgabeMitarbeiterDetail",
|
name: "AbgabeMitarbeiterDetail",
|
||||||
components: {
|
components: {
|
||||||
@@ -17,6 +17,7 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
Message: primevue.message,
|
Message: primevue.message,
|
||||||
VueDatePicker
|
VueDatePicker
|
||||||
},
|
},
|
||||||
|
emits: ['paUpdated'],
|
||||||
inject: [
|
inject: [
|
||||||
'abgabeTypeOptions',
|
'abgabeTypeOptions',
|
||||||
'abgabetypenBetreuer',
|
'abgabetypenBetreuer',
|
||||||
@@ -81,9 +82,9 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getNoteBezeichnung(termin){
|
getNoteBezeichnung(termin){
|
||||||
if(termin.note?.bezeichnung) {
|
if(termin.noteBackend?.bezeichnung) {
|
||||||
return termin.note?.positiv ? this.$capitalize(this.$p.t('abgabetool/c4positivBenotet')) + ' ✅' : this.$capitalize(this.$p.t('abgabetool/c4negativBenotet')) + ' ❌'
|
return termin.noteBackend?.positiv ? this.$capitalize(this.$p.t('abgabetool/c4positivBenotet')) + ' ✅' : this.$capitalize(this.$p.t('abgabetool/c4negativBenotet')) + ' ❌'
|
||||||
} else if(termin.bezeichnung?.benotbar === true && !termin.note) {
|
} else if(termin.bezeichnung?.benotbar === true && !termin.noteBackend) {
|
||||||
return this.$capitalize(this.$p.t('abgabetool/c4notYetGraded'));
|
return this.$capitalize(this.$p.t('abgabetool/c4notYetGraded'));
|
||||||
} else {
|
} else {
|
||||||
return ''
|
return ''
|
||||||
@@ -109,7 +110,10 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
'allowedToDelete': true,
|
'allowedToDelete': true,
|
||||||
...res.data[0]
|
...res.data[0]
|
||||||
}
|
}
|
||||||
if(newTerminRes.note) newTerminRes.note = noteOpt
|
if(newTerminRes.note) {
|
||||||
|
newTerminRes.note = noteOpt
|
||||||
|
newTerminRes.noteBackend = noteOpt // certain UI elements should only reflect persisted state
|
||||||
|
}
|
||||||
newTerminRes.invertedFixtermin = !newTerminRes.fixtermin
|
newTerminRes.invertedFixtermin = !newTerminRes.fixtermin
|
||||||
const existingTerminRes = res.data[1]
|
const existingTerminRes = res.data[1]
|
||||||
|
|
||||||
@@ -121,14 +125,17 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
benotbar: abgabeOpt.benotbar
|
benotbar: abgabeOpt.benotbar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// only insert new abgabe if we actually created a new one, not when saving/editing existing
|
// only insert new abgabe if we actually created a new one, not when saving/editing existing
|
||||||
if(!existingTerminRes){
|
if(!existingTerminRes){
|
||||||
|
newTerminRes.dateStyle = getDateStyleClass(newTerminRes, this.notenOptions)
|
||||||
this.projektarbeit.abgabetermine.push(newTerminRes)
|
this.projektarbeit.abgabetermine.push(newTerminRes)
|
||||||
} else {
|
} else {
|
||||||
const noteOptExisting = this.allowedNotenOptions.find(opt => opt.note == existingTerminRes.note)
|
const noteOptExisting = this.allowedNotenOptions.find(opt => opt.note == existingTerminRes.note)
|
||||||
existingTerminRes.note = noteOptExisting
|
existingTerminRes.note = noteOptExisting
|
||||||
|
|
||||||
|
termin.paabgabetyp_kurzbz = newTerminRes.paabgabetyp_kurzbz
|
||||||
|
termin.noteBackend = noteOpt // do NOT take noteOptExisting -> should reflect the "yes the qgate grade is confirmed in backend ux behaviour"
|
||||||
|
termin.dateStyle = getDateStyleClass(termin, this.notenOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.projektarbeit.abgabetermine.sort((a, b) =>new Date(a.datum) - new Date(b.datum))
|
this.projektarbeit.abgabetermine.sort((a, b) =>new Date(a.datum) - new Date(b.datum))
|
||||||
@@ -168,6 +175,8 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
} else {
|
} else {
|
||||||
this.showAutomagicModalPhrase = false
|
this.showAutomagicModalPhrase = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.$emit("paUpdated", this.projektarbeit)
|
||||||
} else if(res?.meta?.status == 'error'){
|
} else if(res?.meta?.status == 'error'){
|
||||||
this.$fhcAlert.alertError()
|
this.$fhcAlert.alertError()
|
||||||
}
|
}
|
||||||
@@ -251,6 +260,7 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
// this.$p.t('global/tooltipLektorDeleteKontrolle', [this.$entryParams.permissions.kontrolleDeleteMaxReach ])
|
// this.$p.t('global/tooltipLektorDeleteKontrolle', [this.$entryParams.permissions.kontrolleDeleteMaxReach ])
|
||||||
const deletedTerminIndex = this.projektarbeit.abgabetermine.findIndex(t => t.paabgabe_id === termin.paabgabe_id)
|
const deletedTerminIndex = this.projektarbeit.abgabetermine.findIndex(t => t.paabgabe_id === termin.paabgabe_id)
|
||||||
this.projektarbeit.abgabetermine.splice(deletedTerminIndex, 1)
|
this.projektarbeit.abgabetermine.splice(deletedTerminIndex, 1)
|
||||||
|
this.$emit("paUpdated", this.projektarbeit)
|
||||||
} else if(res?.meta?.status == 'error'){
|
} else if(res?.meta?.status == 'error'){
|
||||||
this.$fhcAlert.alertError()
|
this.$fhcAlert.alertError()
|
||||||
}
|
}
|
||||||
@@ -270,75 +280,6 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
window.open(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + url)
|
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))
|
// this.$api.call(ApiAbgabe.getStudentProjektarbeitAbgabeFile(termin.paabgabe_id, this.projektarbeit.student_uid))
|
||||||
},
|
},
|
||||||
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)
|
|
||||||
|
|
||||||
termin.diffindays = this.dateDiffInDays(termin.datum)
|
|
||||||
|
|
||||||
const isLate = termin.abgabedatum && abgabedatum > datum;
|
|
||||||
|
|
||||||
// GRADE STATUS
|
|
||||||
if (termin.note) {
|
|
||||||
if (termin.note.positiv) return 'bestanden';
|
|
||||||
return 'nichtbestanden';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ACTION REQUIRED FOR GRADE
|
|
||||||
if (termin.bezeichnung?.benotbar && datum < today) {
|
|
||||||
return 'beurteilungerforderlich';
|
|
||||||
}
|
|
||||||
|
|
||||||
// SUBMISSION STATUS
|
|
||||||
if (termin.upload_allowed) {
|
|
||||||
if (termin.abgabedatum) {
|
|
||||||
return isLate ? 'verspaetet' : 'abgegeben';
|
|
||||||
}
|
|
||||||
|
|
||||||
// no submission yet
|
|
||||||
if (datum < today) return 'verpasst';
|
|
||||||
if (termin.diffindays <= 12) return 'abzugeben';
|
|
||||||
return 'standard';
|
|
||||||
}
|
|
||||||
|
|
||||||
// GENERIC STATUS
|
|
||||||
return datum < today ? 'verpasst' : 'standard';
|
|
||||||
},
|
|
||||||
openBeurteilungLink(link) {
|
openBeurteilungLink(link) {
|
||||||
window.open(link, '_blank')
|
window.open(link, '_blank')
|
||||||
},
|
},
|
||||||
@@ -396,6 +337,7 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
formatDate(dateParam) {
|
formatDate(dateParam) {
|
||||||
|
// unsafe for datepickers, dont use there
|
||||||
const date = new Date(dateParam)
|
const date = new Date(dateParam)
|
||||||
// handle missing leading 0
|
// handle missing leading 0
|
||||||
const padZero = (num) => String(num).padStart(2, '0');
|
const padZero = (num) => String(num).padStart(2, '0');
|
||||||
@@ -476,7 +418,6 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
termin.kurzbz = ''
|
termin.kurzbz = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
getAllowedToCreateNewTermin() {
|
getAllowedToCreateNewTermin() {
|
||||||
@@ -626,7 +567,6 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
return ''
|
return ''
|
||||||
},
|
},
|
||||||
getProjektarbeitStudent(){
|
getProjektarbeitStudent(){
|
||||||
|
|
||||||
if(this.projektarbeit?.student) return this.$capitalize(this.$p.t('person/student')) + ': ' + this.projektarbeit.student
|
if(this.projektarbeit?.student) return this.$capitalize(this.$p.t('person/student')) + ': ' + this.projektarbeit.student
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
@@ -671,7 +611,6 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
this.form.schlagwoerter_en = newVal.schlagwoerter_en ?? ''
|
this.form.schlagwoerter_en = newVal.schlagwoerter_en ?? ''
|
||||||
this.form.kontrollschlagwoerter = newVal.kontrollschlagwoerter ?? ''
|
this.form.kontrollschlagwoerter = newVal.kontrollschlagwoerter ?? ''
|
||||||
this.form.seitenanzahl = newVal.seitenanzahl ?? 1
|
this.form.seitenanzahl = newVal.seitenanzahl ?? 1
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -709,13 +648,14 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-2">
|
<div class="row mt-2">
|
||||||
<div class="col-4 col-md-3 fw-bold align-content-center">{{ $capitalize( $p.t('abgabetool/c4zieldatum') )}}</div>
|
<div class="col-4 col-md-3 fw-bold align-content-center">{{ $capitalize( $p.t('abgabetool/c4zieldatumv2') )}}</div>
|
||||||
<div class="col-8 col-md-9">
|
<div class="col-8 col-md-9">
|
||||||
<VueDatePicker
|
<VueDatePicker
|
||||||
v-model="newTermin.datum"
|
v-model="newTermin.datum"
|
||||||
:clearable="false"
|
:clearable="false"
|
||||||
:enable-time-picker="false"
|
:enable-time-picker="false"
|
||||||
:format="formatDate"
|
locale="de"
|
||||||
|
format="dd.MM.yyyy"
|
||||||
:text-input="true"
|
:text-input="true"
|
||||||
auto-apply>
|
auto-apply>
|
||||||
</VueDatePicker>
|
</VueDatePicker>
|
||||||
@@ -746,7 +686,7 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-2">
|
<div class="row mt-2">
|
||||||
<div class="col-4 col-md-3 fw-bold align-content-center">{{ $capitalize( $p.t('abgabetool/c4abgabekurzbz') )}}</div>
|
<div class="col-4 col-md-3 fw-bold align-content-center">{{ $capitalize( $p.t('abgabetool/c4abgabekurzbzv2') )}}</div>
|
||||||
<div class="col-8 col-md-9">
|
<div class="col-8 col-md-9">
|
||||||
<Textarea style="margin-bottom: 4px;" v-model="newTermin.kurzbz" rows="1" class="w-100"></Textarea>
|
<Textarea style="margin-bottom: 4px;" v-model="newTermin.kurzbz" rows="1" class="w-100"></Textarea>
|
||||||
</div>
|
</div>
|
||||||
@@ -766,8 +706,8 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
<p> {{getProjektarbeitStudent}}</p>
|
<p> {{getProjektarbeitStudent}}</p>
|
||||||
<p> {{getProjektarbeitTitel}}</p>
|
<p> {{getProjektarbeitTitel}}</p>
|
||||||
<template v-if="assistenzMode">
|
<template v-if="assistenzMode">
|
||||||
<p v-if="projektarbeit?.erstbetreuer_full_name"> {{ projektarbeit.betreuerart ? $capitalize($p.t('abgabetool/c4betrart' + projektarbeit.betreuerart)) : $capitalize( $p.t('abgabetool/c4betreuer') )}}: {{projektarbeit?.erstbetreuer_full_name}}</p>
|
<p v-if="projektarbeit?.erstbetreuer_full_name"> {{ projektarbeit.betreuerart ? $capitalize($p.t('abgabetool/c4betrart' + projektarbeit.betreuerart)) : $capitalize( $p.t('abgabetool/c4betreuerv2') )}}: {{projektarbeit?.erstbetreuer_full_name}}</p>
|
||||||
<p v-if="projektarbeit?.zweitbetreuer_full_name"> {{ projektarbeit?.zweitbetreuer_betreuerart_kurzbz ? $capitalize($p.t('abgabetool/c4betrart' + projektarbeit.zweitbetreuer_betreuerart_kurzbz)) : $capitalize( $p.t('abgabetool/c4zweitbetreuer') )}}: {{projektarbeit?.zweitbetreuer_full_name}}</p>
|
<p v-if="projektarbeit?.zweitbetreuer_full_name"> {{ projektarbeit?.zweitbetreuer_betreuerart_kurzbz ? $capitalize($p.t('abgabetool/c4betrart' + projektarbeit.zweitbetreuer_betreuerart_kurzbz)) : $capitalize( $p.t('abgabetool/c4zweitbetreuerv2') )}}: {{projektarbeit?.zweitbetreuer_full_name}}</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<p v-if="projektarbeit?.betreuer"> {{$capitalize($p.t('abgabetool/c4betrart' + projektarbeit.betreuerart_kurzbz))}}: {{projektarbeit?.betreuer?.first}}</p>
|
<p v-if="projektarbeit?.betreuer"> {{$capitalize($p.t('abgabetool/c4betrart' + projektarbeit.betreuerart_kurzbz))}}: {{projektarbeit?.betreuer?.first}}</p>
|
||||||
@@ -805,20 +745,20 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Accordion :multiple="true">
|
<Accordion :multiple="true">
|
||||||
<template v-for="termin in this.projektarbeit?.abgabetermine">
|
<template v-for="termin in this.projektarbeit?.abgabetermine" :key="termin.paabgabe_id">
|
||||||
<AccordionTab :headerClass="getDateStyleClass(termin) + '-header'">
|
<AccordionTab :headerClass="termin.dateStyle + '-header'">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex flex-nowrap align-items-center w-100">
|
<div class="d-flex flex-nowrap align-items-center w-100">
|
||||||
|
|
||||||
<div class="flex-shrink-0 d-flex align-items-center justify-content-center" style="width: 36px; height: 36px; margin-left: -66px;">
|
<div class="flex-shrink-0 d-flex align-items-center justify-content-center" style="width: 36px; height: 36px; margin-left: -68px;">
|
||||||
<i v-if="getDateStyleClass(termin) == 'verspaetet'" v-tooltip.right="getTooltipVerspaetet" class="fa-solid fa-triangle-exclamation"></i>
|
<i v-if="termin.dateStyle == 'verspaetet'" v-tooltip.right="getTooltipVerspaetet" class="fa-solid fa-triangle-exclamation"></i>
|
||||||
<i v-else-if="getDateStyleClass(termin) == 'verpasst'" v-tooltip.right="getTooltipVerpasst" class="fa-solid fa-calendar-xmark"></i>
|
<i v-else-if="termin.dateStyle == 'verpasst'" v-tooltip.right="getTooltipVerpasst" class="fa-solid fa-calendar-xmark"></i>
|
||||||
<i v-else-if="getDateStyleClass(termin) == 'abzugeben'" v-tooltip.right="getTooltipAbzugeben" class="fa-solid fa-hourglass-half"></i>
|
<i v-else-if="termin.dateStyle == '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="termin.dateStyle == '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-paperclip"></i>
|
<i v-else-if="termin.dateStyle == 'abgegeben'" v-tooltip.right="getTooltipAbgegeben" class="fa-solid fa-paperclip"></i>
|
||||||
<i v-else-if="getDateStyleClass(termin) == 'beurteilungerforderlich'" v-tooltip.right="getTooltipBeurteilungerforderlich" class="fa-solid fa-list-check"></i>
|
<i v-else-if="termin.dateStyle == 'beurteilungerforderlich'" v-tooltip.right="getTooltipBeurteilungerforderlich" class="fa-solid fa-list-check"></i>
|
||||||
<i v-else-if="getDateStyleClass(termin) == 'bestanden'" v-tooltip.right="getTooltipBestanden" class="fa-solid fa-check"></i>
|
<i v-else-if="termin.dateStyle == 'bestanden'" v-tooltip.right="getTooltipBestanden" class="fa-solid fa-check"></i>
|
||||||
<i v-else-if="getDateStyleClass(termin) == 'nichtbestanden'" v-tooltip.right="getTooltipNichtBestanden" class="fa-solid fa-circle-exclamation"></i>
|
<i v-else-if="termin.dateStyle == 'nichtbestanden'" v-tooltip.right="getTooltipNichtBestanden" class="fa-solid fa-circle-exclamation"></i>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -855,7 +795,7 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
</div>
|
</div>
|
||||||
<div class="row mt-2">
|
<div class="row mt-2">
|
||||||
<div class="col-12 col-md-3 align-content-center">
|
<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-bold" style="margin-left: 2px">{{$capitalize( $p.t('abgabetool/c4zieldatumv2') )}}</div>
|
||||||
<div class="row fw-light" style="margin-left: 2px">{{$capitalize( $p.t('abgabetool/c4abgabeuntil2359') )}}</div>
|
<div class="row fw-light" style="margin-left: 2px">{{$capitalize( $p.t('abgabetool/c4abgabeuntil2359') )}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-9">
|
<div class="col-12 col-md-9">
|
||||||
@@ -864,7 +804,8 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
:clearable="false"
|
:clearable="false"
|
||||||
:disabled="!termin.allowedToSave"
|
:disabled="!termin.allowedToSave"
|
||||||
:enable-time-picker="false"
|
:enable-time-picker="false"
|
||||||
:format="formatDate"
|
locale="de"
|
||||||
|
format="dd.MM.yyyy"
|
||||||
:text-input="true"
|
:text-input="true"
|
||||||
auto-apply>
|
auto-apply>
|
||||||
</VueDatePicker>
|
</VueDatePicker>
|
||||||
@@ -916,7 +857,7 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mt-2">
|
<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-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4abgabekurzbzv2') )}}</div>
|
||||||
<div class="col-12 col-md-9">
|
<div class="col-12 col-md-9">
|
||||||
<Textarea style="margin-bottom: 4px;" v-model="termin.kurzbz" class="w-100" rows="1" :disabled="!termin.allowedToSave"></Textarea>
|
<Textarea style="margin-bottom: 4px;" v-model="termin.kurzbz" class="w-100" rows="1" :disabled="!termin.allowedToSave"></Textarea>
|
||||||
</div>
|
</div>
|
||||||
@@ -931,7 +872,9 @@ export const AbgabeMitarbeiterDetail = {
|
|||||||
v-model="termin.abgabedatum"
|
v-model="termin.abgabedatum"
|
||||||
:clearable="false"
|
:clearable="false"
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
:format="formatDate">
|
locale="de"
|
||||||
|
format="dd.MM.yyyy"
|
||||||
|
>
|
||||||
</VueDatePicker>
|
</VueDatePicker>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ export const AbgabeStudentDetail = {
|
|||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
<p> {{$capitalize( $p.t('person/student') ) }}: {{projektarbeit?.student}}</p>
|
<p> {{$capitalize( $p.t('person/student') ) }}: {{projektarbeit?.student}}</p>
|
||||||
<p> {{$capitalize( $p.t('abgabetool/c4titel') ) }}: {{projektarbeit?.titel}}</p>
|
<p> {{$capitalize( $p.t('abgabetool/c4titel') ) }}: {{projektarbeit?.titel}}</p>
|
||||||
<p> {{$capitalize( $p.t('abgabetool/c4betreuer') ) }}: {{projektarbeit ? $p.t('abgabetool/c4betrart' + projektarbeit.betreuerart_kurzbz) + ' ' + projektarbeit.betreuer : ''}}</p>
|
<p> {{$capitalize( $p.t('abgabetool/c4betreuerv2') ) }}: {{projektarbeit ? $p.t('abgabetool/c4betrart' + projektarbeit.betreuerart_kurzbz) + ' ' + projektarbeit.betreuer : ''}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<p>{{ $p.t('abgabetool/c4checkoutStgMoodleInfos') }}
|
<p>{{ $p.t('abgabetool/c4checkoutStgMoodleInfos') }}
|
||||||
@@ -344,11 +344,11 @@ export const AbgabeStudentDetail = {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Accordion :multiple="true">
|
<Accordion :multiple="true">
|
||||||
<template v-for="termin in this.projektarbeit?.abgabetermine">
|
<template v-for="termin in this.projektarbeit?.abgabetermine" :key="termin.paabgabe_id">
|
||||||
<AccordionTab :headerClass="termin.dateStyle + '-header'">
|
<AccordionTab :headerClass="termin.dateStyle + '-header'">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex flex-nowrap align-items-center w-100">
|
<div class="d-flex flex-nowrap align-items-center w-100">
|
||||||
<div class="flex-shrink-0 d-flex align-items-center justify-content-center" style="width: 36px; height: 36px; margin-left: -66px;">
|
<div class="flex-shrink-0 d-flex align-items-center justify-content-center" style="width: 36px; height: 36px; margin-left: -68px;">
|
||||||
<i v-if="termin.dateStyle == 'verspaetet'" v-tooltip.right="getTooltipVerspaetet" class="fa-solid fa-triangle-exclamation"></i>
|
<i v-if="termin.dateStyle == 'verspaetet'" v-tooltip.right="getTooltipVerspaetet" class="fa-solid fa-triangle-exclamation"></i>
|
||||||
<i v-else-if="termin.dateStyle == 'verpasst'" v-tooltip.right="getTooltipVerpasst" class="fa-solid fa-calendar-xmark"></i>
|
<i v-else-if="termin.dateStyle == 'verpasst'" v-tooltip.right="getTooltipVerpasst" class="fa-solid fa-calendar-xmark"></i>
|
||||||
<i v-else-if="termin.dateStyle == 'abzugeben'" v-tooltip.right="getTooltipAbzugeben" class="fa-solid fa-hourglass-half"></i>
|
<i v-else-if="termin.dateStyle == 'abzugeben'" v-tooltip.right="getTooltipAbzugeben" class="fa-solid fa-hourglass-half"></i>
|
||||||
@@ -414,7 +414,7 @@ export const AbgabeStudentDetail = {
|
|||||||
|
|
||||||
<div class="row mt-2">
|
<div class="row mt-2">
|
||||||
<div class="col-12 col-md-3 align-content-center">
|
<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-bold" style="margin-left: 2px">{{$capitalize( $p.t('abgabetool/c4zieldatumv2') )}}</div>
|
||||||
<div class="row fw-light" style="margin-left: 2px">{{$capitalize( $p.t('abgabetool/c4abgabeuntil2359') )}}</div>
|
<div class="row fw-light" style="margin-left: 2px">{{$capitalize( $p.t('abgabetool/c4abgabeuntil2359') )}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-9">
|
<div class="col-12 col-md-9">
|
||||||
@@ -423,7 +423,8 @@ export const AbgabeStudentDetail = {
|
|||||||
:clearable="false"
|
:clearable="false"
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
:enable-time-picker="false"
|
:enable-time-picker="false"
|
||||||
:format="formatDate"
|
locale="de"
|
||||||
|
format="dd.MM.yyyy"
|
||||||
:text-input="true"
|
:text-input="true"
|
||||||
auto-apply>
|
auto-apply>
|
||||||
</VueDatePicker>
|
</VueDatePicker>
|
||||||
@@ -454,7 +455,7 @@ export const AbgabeStudentDetail = {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="termin.kurzbz && termin.kurzbz.length > 0" class="row mt-2">
|
<div v-if="termin.kurzbz && termin.kurzbz.length > 0" 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-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4abgabekurzbzv2') )}}</div>
|
||||||
<div class="col-12 col-md-9">
|
<div class="col-12 col-md-9">
|
||||||
<Textarea style="margin-bottom: 4px;" v-model="termin.kurzbz" rows="1" class="w-100" :disabled="true"></Textarea>
|
<Textarea style="margin-bottom: 4px;" v-model="termin.kurzbz" rows="1" class="w-100" :disabled="true"></Textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,15 +8,8 @@ import ApiStudiensemester from '../../../api/factory/studiensemester.js';
|
|||||||
import AbgabeterminStatusLegende from "./StatusLegende.js";
|
import AbgabeterminStatusLegende from "./StatusLegende.js";
|
||||||
import FhcOverlay from "../../Overlay/FhcOverlay.js";
|
import FhcOverlay from "../../Overlay/FhcOverlay.js";
|
||||||
import { splitMailsHelper } from "../../../helpers/EmailHelpers.js"
|
import { splitMailsHelper } from "../../../helpers/EmailHelpers.js"
|
||||||
|
import { getDateStyleClass} from "./getDateStyleClass.js";
|
||||||
// spoofed date testing
|
import { dateFilter } from '../../../tabulator/filters/Dates.js';
|
||||||
// const todayISO = '2025-08-08'
|
|
||||||
// const today = new Date(todayISO)
|
|
||||||
// const now = luxon.DateTime.fromISO(todayISO)
|
|
||||||
|
|
||||||
// prod code
|
|
||||||
const today = new Date()
|
|
||||||
const now = luxon.DateTime.now()
|
|
||||||
|
|
||||||
export const AbgabetoolAssistenz = {
|
export const AbgabetoolAssistenz = {
|
||||||
name: "AbgabetoolAssistenz",
|
name: "AbgabetoolAssistenz",
|
||||||
@@ -187,7 +180,7 @@ export const AbgabetoolAssistenz = {
|
|||||||
// frozen: true,
|
// frozen: true,
|
||||||
// width: 40
|
// width: 40
|
||||||
// },
|
// },
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', formatter: this.formAction, tooltip:false, minWidth: 150, cssClass: 'sticky-col'},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', headerFilter: false, headerSort: false, formatter: this.formAction, tooltip:false, minWidth: 150, cssClass: 'sticky-col'},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4personenkennzeichen'))), headerFilter: true, field: 'pkz', formatter: this.pkzTextFormatter, widthGrow: 1, tooltip: false},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4personenkennzeichen'))), headerFilter: true, field: 'pkz', formatter: this.pkzTextFormatter, widthGrow: 1, tooltip: false},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'student_vorname', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'student_vorname', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'student_nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'student_nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
||||||
@@ -199,20 +192,47 @@ export const AbgabetoolAssistenz = {
|
|||||||
formatter: this.centeredTextFormatter, widthGrow: 1},
|
formatter: this.centeredTextFormatter, widthGrow: 1},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4sem'))), field: 'studiensemester_kurzbz', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4sem'))), field: 'studiensemester_kurzbz', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4titel'))), field: 'titel', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4titel'))), field: 'titel', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuer'))), field: 'erstbetreuer', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerv2'))), field: 'erstbetreuer', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zweitbetreuer'))), field: 'zweitbetreuer', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4prevAbgabetermin'))), headerFilter: true, field: 'prevTermin', formatter: this.abgabterminFormatter, widthGrow: 1, width: 220, tooltip: false},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerTitelPre'))), field: 'betreuer_titelpre', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nextAbgabetermin'))), headerFilter: true, field: 'nextTermin', formatter: this.abgabterminFormatter, widthGrow: 1, width: 220, tooltip: false},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerVorname'))), field: 'betreuer_vorname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate1Status'))), headerFilter: true, field: 'qgate1Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerNachname'))), field: 'betreuer_nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate2Status'))), headerFilter: true, field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerTitelPost'))), field: 'betreuer_titelpost', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false},
|
||||||
|
|
||||||
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zweitbetreuerv2'))), field: 'zweitbetreuer', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
||||||
|
|
||||||
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zweitbetreuerTitelPre'))), field: 'zweitbetreuer_titelpre', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false},
|
||||||
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zweitbetreuerVorname'))), field: 'zweitbetreuer_vorname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false},
|
||||||
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zweitbetreuerNachname'))), field: 'zweitbetreuer_nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false},
|
||||||
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zweitbetreuerTitelPost'))), field: 'zweitbetreuer_titelpost', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false},
|
||||||
|
|
||||||
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4prevAbgabetermin'))),
|
||||||
|
headerFilter: dateFilter,
|
||||||
|
headerFilterFunc: this.headerFilterTerminCol,
|
||||||
|
sorter: this.sortFuncTerminCol,
|
||||||
|
field: 'prevTermin', formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
|
||||||
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nextAbgabetermin'))), field: 'nextTermin',
|
||||||
|
headerFilter: dateFilter,
|
||||||
|
headerFilterFunc: this.headerFilterTerminCol,
|
||||||
|
sorter: this.sortFuncTerminCol,
|
||||||
|
formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
|
||||||
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate1Status'))),
|
||||||
|
headerFilter: 'list',
|
||||||
|
headerFilterParams: { valuesLookup: this.getQGateStatusList },
|
||||||
|
field: 'qgate1Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false},
|
||||||
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate2Status'))),
|
||||||
|
headerFilter: 'list',
|
||||||
|
headerFilterParams: { valuesLookup: this.getQGateStatusList },
|
||||||
|
field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false},
|
||||||
],
|
],
|
||||||
persistence: false,
|
persistence: false,
|
||||||
persistenceID: "abgabetool_2025_12"
|
persistenceID: "abgabetool_2026_02_26"
|
||||||
},
|
},
|
||||||
abgabeTableEventHandlers: [
|
abgabeTableEventHandlers: [
|
||||||
{
|
{
|
||||||
event: "rowSelectionChanged",
|
event: "rowSelectionChanged",
|
||||||
handler: async(data) => {
|
handler: async(data) =>
|
||||||
|
{
|
||||||
this.selectedData.filter(sd => !data.includes(sd)).forEach(fsd => {
|
this.selectedData.filter(sd => !data.includes(sd)).forEach(fsd => {
|
||||||
if(fsd.checkbox) fsd.checkbox.checked = false
|
if(fsd.checkbox) fsd.checkbox.checked = false
|
||||||
})
|
})
|
||||||
@@ -227,12 +247,79 @@ export const AbgabetoolAssistenz = {
|
|||||||
]};
|
]};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
handlePaUpdated(projektarbeit) {
|
||||||
|
this.checkAbgabetermineProjektarbeit(projektarbeit)
|
||||||
|
this.$refs.abgabeTable.tabulator.redraw(true)
|
||||||
|
},
|
||||||
|
getQGateStatusList() {
|
||||||
|
return [
|
||||||
|
this.$p.t('abgabetool/c4keinTerminVorhanden'),
|
||||||
|
this.$p.t('abgabetool/c4positivBenotet'),
|
||||||
|
this.$p.t('abgabetool/c4negativBenotet'),
|
||||||
|
this.$p.t('abgabetool/c4notYetGraded'),
|
||||||
|
this.$p.t('abgabetool/c4notSubmitted'),
|
||||||
|
this.$p.t('abgabetool/c4notHappenedYet')
|
||||||
|
]
|
||||||
|
},
|
||||||
|
sortFuncTerminCol(a, b, aRow, bRow, column, dir, params) {
|
||||||
|
if (a === null || typeof a === "undefined") return 1;
|
||||||
|
if (b === null || typeof b === "undefined") return -1;
|
||||||
|
|
||||||
|
// try to handle the prev/next interpretation consistently
|
||||||
|
// can only make this wrong UX whise so whatever
|
||||||
|
if(column._column.field == 'prevTermin') {
|
||||||
|
return Math.abs(b.diffMs) - Math.abs(a.diffMs)
|
||||||
|
} else if (column._column.field == 'nextTermin') {
|
||||||
|
return Math.abs(a.diffMs) - Math.abs(b.diffMs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// just in case someone reuses this
|
||||||
|
return Math.abs(b.diffMs) - Math.abs(a.diffMs)
|
||||||
|
},
|
||||||
|
headerFilterTerminCol(filterVal, rowVal) {
|
||||||
|
if (!rowVal || !rowVal.luxonDate || !rowVal.luxonDate.isValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowDate = rowVal.luxonDate;
|
||||||
|
|
||||||
|
const toLuxon = (val) => {
|
||||||
|
if (!val) return null;
|
||||||
|
let dt;
|
||||||
|
if (val instanceof Date) {
|
||||||
|
dt = luxon.DateTime.fromJSDate(val);
|
||||||
|
} else if (typeof val === "string") {
|
||||||
|
dt = luxon.DateTime.fromISO(val);
|
||||||
|
} else { // fallback
|
||||||
|
dt = luxon.DateTime.fromMillis(Number(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
return dt.isValid ? dt : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const von = toLuxon(filterVal[0]);
|
||||||
|
const bis = toLuxon(filterVal[1]);
|
||||||
|
|
||||||
|
// specific day
|
||||||
|
if (von && !bis) {
|
||||||
|
return rowDate.hasSame(von, "day");
|
||||||
|
}
|
||||||
|
|
||||||
|
// range case
|
||||||
|
if (von && bis) {
|
||||||
|
return rowDate >= von.startOf("day") && rowDate <= bis.endOf("day");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
sammelMailStudent(param) {
|
sammelMailStudent(param) {
|
||||||
|
|
||||||
const emails = this.selectedData
|
const recipientList = [];
|
||||||
.map(row => `${row.student_uid}@${this.domain}`)
|
this.selectedData.forEach(d => {
|
||||||
.join(',');
|
recipientList.push(`${d.student_uid}@${this.domain}`)
|
||||||
const uniqueRecipients = [...new Set(emails)];
|
})
|
||||||
|
|
||||||
|
const uniqueRecipients = [...new Set(recipientList)];
|
||||||
const subject = this.$p.t('abgabetool/c4sammelmailStudentBetreff', [this.selectedStudiengangOption?.bezeichnung]);
|
const subject = this.$p.t('abgabetool/c4sammelmailStudentBetreff', [this.selectedStudiengangOption?.bezeichnung]);
|
||||||
splitMailsHelper(uniqueRecipients, param.originalEvent, subject, this.$fhcAlert, this.$p)
|
splitMailsHelper(uniqueRecipients, param.originalEvent, subject, this.$fhcAlert, this.$p)
|
||||||
},
|
},
|
||||||
@@ -281,7 +368,6 @@ export const AbgabetoolAssistenz = {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
checkQualityGateStatus(projekt) {
|
checkQualityGateStatus(projekt) {
|
||||||
// TODO: might refine the representation of these states and maybe refactor code a little
|
|
||||||
const qgate1Termine = []
|
const qgate1Termine = []
|
||||||
const qgate2Termine = []
|
const qgate2Termine = []
|
||||||
|
|
||||||
@@ -301,7 +387,7 @@ export const AbgabetoolAssistenz = {
|
|||||||
// reuse luxon calculated diffMs (termin.datum in relation to today) from previous datestyle check
|
// reuse luxon calculated diffMs (termin.datum in relation to today) from previous datestyle check
|
||||||
qgate1Termine.forEach(qgate => {
|
qgate1Termine.forEach(qgate => {
|
||||||
if(qgate.note != null && projekt.qgate1StatusRank <= 5) {
|
if(qgate.note != null && projekt.qgate1StatusRank <= 5) {
|
||||||
const noteOpt = this.notenOptions.find(opt => opt.note == qgate.note)
|
const noteOpt = typeof qgate.note !== 'object' ? this.notenOptions.find(opt => opt.note == qgate.note) : qgate.note
|
||||||
if(noteOpt.positiv) {
|
if(noteOpt.positiv) {
|
||||||
projekt.qgate1Status = this.$p.t('abgabetool/c4positivBenotet')
|
projekt.qgate1Status = this.$p.t('abgabetool/c4positivBenotet')
|
||||||
projekt.qgate1StatusRank = 5
|
projekt.qgate1StatusRank = 5
|
||||||
@@ -323,7 +409,7 @@ export const AbgabetoolAssistenz = {
|
|||||||
|
|
||||||
qgate2Termine.forEach(qgate => {
|
qgate2Termine.forEach(qgate => {
|
||||||
if(qgate.note != null && projekt.qgate1StatusRank <= 5) {
|
if(qgate.note != null && projekt.qgate1StatusRank <= 5) {
|
||||||
const noteOpt = this.notenOptions.find(opt => opt.note == qgate.note)
|
const noteOpt = typeof qgate.note !== 'object' ? this.notenOptions.find(opt => opt.note == qgate.note) : qgate.note
|
||||||
if(noteOpt.positiv) {
|
if(noteOpt.positiv) {
|
||||||
projekt.qgate2Status = this.$p.t('abgabetool/c4positivBenotet')
|
projekt.qgate2Status = this.$p.t('abgabetool/c4positivBenotet')
|
||||||
projekt.qgate2StatusRank = 5
|
projekt.qgate2StatusRank = 5
|
||||||
@@ -385,14 +471,18 @@ export const AbgabetoolAssistenz = {
|
|||||||
|
|
||||||
},
|
},
|
||||||
checkAbgabetermineProjektarbeit(projekt) {
|
checkAbgabetermineProjektarbeit(projekt) {
|
||||||
|
const now = luxon.DateTime.now()
|
||||||
|
|
||||||
// calculate Abgabetermin time diff to now and assign last and next to projekt
|
// calculate Abgabetermin time diff to now and assign last and next to projekt
|
||||||
projekt.abgabetermine.forEach(termin => {
|
projekt.abgabetermine.forEach(termin => {
|
||||||
|
|
||||||
|
termin.bezeichnung = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === termin.paabgabetyp_kurzbz)
|
||||||
|
|
||||||
// while already looping through each termin, calculate datestyle beforehand
|
// while already looping through each termin, calculate datestyle beforehand
|
||||||
termin.dateStyle = this.getDateStyleClass(termin)
|
termin.dateStyle = getDateStyleClass(termin, this.notenOptions)
|
||||||
|
|
||||||
const date = luxon.DateTime.fromISO(termin.datum)
|
const date = luxon.DateTime.fromISO(termin.datum).endOf('day')
|
||||||
|
termin.luxonDate = date
|
||||||
termin.diffMs = date.toMillis() - now.toMillis(); // positive = future, negative = past
|
termin.diffMs = date.toMillis() - now.toMillis(); // positive = future, negative = past
|
||||||
|
|
||||||
if (termin.diffMs < 0) {
|
if (termin.diffMs < 0) {
|
||||||
@@ -612,6 +702,9 @@ export const AbgabetoolAssistenz = {
|
|||||||
},
|
},
|
||||||
addSeries() {
|
addSeries() {
|
||||||
const pids = this.selectedData?.map(projekt => projekt.projektarbeit_id)
|
const pids = this.selectedData?.map(projekt => projekt.projektarbeit_id)
|
||||||
|
|
||||||
|
const preserveSelected = [...this.selectedData]
|
||||||
|
|
||||||
this.saving = true
|
this.saving = true
|
||||||
this.serienTermin.fixtermin = !this.serienTermin.invertedFixtermin
|
this.serienTermin.fixtermin = !this.serienTermin.invertedFixtermin
|
||||||
this.$api.call(ApiAbgabe.postSerientermin(
|
this.$api.call(ApiAbgabe.postSerientermin(
|
||||||
@@ -644,14 +737,27 @@ export const AbgabetoolAssistenz = {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// reset selection to empty
|
// reset selection to empty
|
||||||
this.$refs.abgabeTable.tabulator.deselectRow()
|
// this.$refs.abgabeTable.tabulator.deselectRow()
|
||||||
|
const table = this.$refs.abgabeTable.tabulator;
|
||||||
|
const scrollX = table.rowManager.scrollLeft;
|
||||||
|
const scrollY = table.rowManager.scrollTop;
|
||||||
|
|
||||||
const mappedData = this.mapProjekteToTableData(this.projektarbeiten)
|
const mappedData = this.mapProjekteToTableData(this.projektarbeiten)
|
||||||
|
|
||||||
this.$refs.abgabeTable.tabulator.setData(mappedData)
|
table.setData(mappedData)
|
||||||
this.$refs.abgabeTable.tabulator.redraw(true)
|
table.redraw(true)
|
||||||
|
|
||||||
|
Vue.nextTick(()=> {
|
||||||
|
const table = this.$refs.abgabeTable?.tabulator.element.querySelector('.tabulator-tableholder')
|
||||||
|
if(table) {
|
||||||
|
table.scrollLeft = scrollX;
|
||||||
|
table.scrollTop = scrollY;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
}).finally(()=>{
|
}).finally(()=>{
|
||||||
this.saving = false
|
this.saving = false
|
||||||
|
this.selectedData = preserveSelected
|
||||||
})
|
})
|
||||||
|
|
||||||
this.$refs.modalContainerAddSeries.hide()
|
this.$refs.modalContainerAddSeries.hide()
|
||||||
@@ -705,10 +811,11 @@ export const AbgabetoolAssistenz = {
|
|||||||
return str
|
return str
|
||||||
},
|
},
|
||||||
isPastDate(date) {
|
isPastDate(date) {
|
||||||
return new Date(date) < new Date(Date.now())
|
const deadline = luxon.DateTime.fromISO(date, { zone: 'Europe/Vienna' }).endOf('day');
|
||||||
|
const nowInVienna = luxon.DateTime.now().setZone('Europe/Vienna');
|
||||||
|
return nowInVienna > deadline;
|
||||||
},
|
},
|
||||||
setDetailComponent(details){
|
setDetailComponent(details){
|
||||||
|
|
||||||
const pa = this.projektarbeiten.find(projektarbeit => projektarbeit.projektarbeit_id == details.projektarbeit_id)
|
const pa = this.projektarbeiten.find(projektarbeit => projektarbeit.projektarbeit_id == details.projektarbeit_id)
|
||||||
|
|
||||||
if(pa?.abgabetermine?.length) {
|
if(pa?.abgabetermine?.length) {
|
||||||
@@ -730,6 +837,11 @@ export const AbgabetoolAssistenz = {
|
|||||||
termin.note = this.allowedNotenOptions.find(opt => opt.note == termin.note)
|
termin.note = this.allowedNotenOptions.find(opt => opt.note == termin.note)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only set this if it has not been set yet and abgabetermin has a note (qgate)
|
||||||
|
if(!termin.noteBackend && termin.note) {
|
||||||
|
termin.noteBackend = termin.note
|
||||||
|
}
|
||||||
|
|
||||||
termin.file = []
|
termin.file = []
|
||||||
|
|
||||||
// assistenz should be able to edit every abgabe
|
// assistenz should be able to edit every abgabe
|
||||||
@@ -739,8 +851,6 @@ export const AbgabetoolAssistenz = {
|
|||||||
// assistenz are not allowed to delete deadlines with existing submissions
|
// assistenz are not allowed to delete deadlines with existing submissions
|
||||||
termin.allowedToDelete = paIsBenotet ? false : !termin.abgabedatum
|
termin.allowedToDelete = paIsBenotet ? false : !termin.abgabedatum
|
||||||
|
|
||||||
termin.bezeichnung = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === termin.paabgabetyp_kurzbz)
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const vorname = pa.vorname ?? pa.student_vorname
|
const vorname = pa.vorname ?? pa.student_vorname
|
||||||
@@ -748,52 +858,8 @@ export const AbgabetoolAssistenz = {
|
|||||||
pa.student = `${vorname} ${nachname}`
|
pa.student = `${vorname} ${nachname}`
|
||||||
|
|
||||||
this.selectedProjektarbeit = pa
|
this.selectedProjektarbeit = pa
|
||||||
|
|
||||||
this.$refs.modalContainerAbgabeDetail.show()
|
this.$refs.modalContainerAbgabeDetail.show()
|
||||||
},
|
},
|
||||||
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)
|
|
||||||
|
|
||||||
termin.diffindays = this.dateDiffInDays(termin.datum)
|
|
||||||
|
|
||||||
const isLate = termin.abgabedatum && abgabedatum > datum;
|
|
||||||
|
|
||||||
// GRADE STATUS
|
|
||||||
if (termin.note) {
|
|
||||||
if (termin.note.positiv) return 'bestanden';
|
|
||||||
return 'nichtbestanden';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ACTION REQUIRED FOR GRADE
|
|
||||||
if (termin.bezeichnung?.benotbar && datum < today) {
|
|
||||||
return 'beurteilungerforderlich';
|
|
||||||
}
|
|
||||||
|
|
||||||
// SUBMISSION STATUS
|
|
||||||
if (termin.upload_allowed) {
|
|
||||||
if (termin.abgabedatum) {
|
|
||||||
return isLate ? 'verspaetet' : 'abgegeben';
|
|
||||||
}
|
|
||||||
|
|
||||||
// no submission yet
|
|
||||||
if (datum < today) return 'verpasst';
|
|
||||||
if (termin.diffindays <= 12) return 'abzugeben';
|
|
||||||
return 'standard';
|
|
||||||
}
|
|
||||||
|
|
||||||
// GENERIC STATUS
|
|
||||||
return datum < today ? 'verpasst' : 'standard';
|
|
||||||
},
|
|
||||||
openTimeline(val) {
|
openTimeline(val) {
|
||||||
const projekt = this.projektarbeiten.find(p => p.projektarbeit_id == val.projektarbeit_id)
|
const projekt = this.projektarbeiten.find(p => p.projektarbeit_id == val.projektarbeit_id)
|
||||||
if(!projekt) {
|
if(!projekt) {
|
||||||
@@ -864,7 +930,7 @@ export const AbgabetoolAssistenz = {
|
|||||||
case 'abgegeben':
|
case 'abgegeben':
|
||||||
icon = '<i class="fa-solid fa-paperclip"></i>'
|
icon = '<i class="fa-solid fa-paperclip"></i>'
|
||||||
break
|
break
|
||||||
case 'beurteilungerfolderlich':
|
case 'beurteilungerforderlich':
|
||||||
icon = '<i class="fa-solid fa-list-check"></i>'
|
icon = '<i class="fa-solid fa-list-check"></i>'
|
||||||
break
|
break
|
||||||
case 'bestanden':
|
case 'bestanden':
|
||||||
@@ -984,7 +1050,7 @@ export const AbgabetoolAssistenz = {
|
|||||||
|
|
||||||
if(this.ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER) {
|
if(this.ASSISTENZ_SAMMELMAIL_BUTTON_BETREUER) {
|
||||||
menu.push({
|
menu.push({
|
||||||
label: this.$p.t('abgabetool/c4sendEmailBetreuerv2', [this.uniqueBetreuerEmailCount]),
|
label: this.$p.t('abgabetool/c4sendEmailBetreuerv3', [this.uniqueBetreuerEmailCount]),
|
||||||
command: this.sammelMailBetreuer
|
command: this.sammelMailBetreuer
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1034,6 +1100,24 @@ export const AbgabetoolAssistenz = {
|
|||||||
if(this.notenOptionFilter !== null && this.selectedStudiengangOption !== null) {
|
if(this.notenOptionFilter !== null && this.selectedStudiengangOption !== null) {
|
||||||
this.loadProjektarbeiten()
|
this.loadProjektarbeiten()
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
selectedData(newVal) {
|
||||||
|
const table = this.$refs.abgabeTable?.tabulator
|
||||||
|
if(!table) return
|
||||||
|
|
||||||
|
const allRows = table.getRows();
|
||||||
|
|
||||||
|
newVal.forEach(selected => {
|
||||||
|
const row = allRows.find(r => {
|
||||||
|
const data = r.getData()
|
||||||
|
if (data.projektarbeit_id == selected.projektarbeit_id) return r
|
||||||
|
})
|
||||||
|
|
||||||
|
row.select()
|
||||||
|
const cb = row.getElement().children[0]?.children[0]?.children[0]
|
||||||
|
if(cb) cb.checked = true
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -1145,15 +1229,16 @@ export const AbgabetoolAssistenz = {
|
|||||||
|
|
||||||
<div class="row mt-2">
|
<div class="row mt-2">
|
||||||
<div class="col-12 col-md-3 align-content-center">
|
<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-bold" style="margin-left: 2px">{{$capitalize( $p.t('abgabetool/c4zieldatumv2') )}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-9">
|
<div class="col-12 col-md-9">
|
||||||
<VueDatePicker
|
<VueDatePicker
|
||||||
style="width: 95%;"
|
style="width: 95%;"
|
||||||
v-model="serienTermin.datum"
|
v-model="serienTermin.datum"
|
||||||
:clearable="false"
|
:clearable="false"
|
||||||
|
locale="de"
|
||||||
|
format="dd.MM.yyyy"
|
||||||
:enable-time-picker="false"
|
:enable-time-picker="false"
|
||||||
:format="formatDate"
|
|
||||||
:text-input="true"
|
:text-input="true"
|
||||||
auto-apply>
|
auto-apply>
|
||||||
</VueDatePicker>
|
</VueDatePicker>
|
||||||
@@ -1186,7 +1271,7 @@ export const AbgabetoolAssistenz = {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mt-2">
|
<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-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4abgabekurzbzv2') )}}</div>
|
||||||
<div class="col-12 col-md-9">
|
<div class="col-12 col-md-9">
|
||||||
<Textarea style="margin-bottom: 4px;" v-model="serienTermin.kurzbz" rows="1" class="w-100"></Textarea>
|
<Textarea style="margin-bottom: 4px;" v-model="serienTermin.kurzbz" rows="1" class="w-100"></Textarea>
|
||||||
</div>
|
</div>
|
||||||
@@ -1207,7 +1292,13 @@ export const AbgabetoolAssistenz = {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:default>
|
<template v-slot:default>
|
||||||
<AbgabeDetail :projektarbeit="selectedProjektarbeit" :isFullscreen="detailIsFullscreen" :assistenzMode="true"></AbgabeDetail>
|
<AbgabeDetail
|
||||||
|
:projektarbeit="selectedProjektarbeit"
|
||||||
|
:isFullscreen="detailIsFullscreen"
|
||||||
|
:assistenzMode="true"
|
||||||
|
@paUpdated="handlePaUpdated">
|
||||||
|
|
||||||
|
</AbgabeDetail>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</bs-modal>
|
</bs-modal>
|
||||||
@@ -1279,7 +1370,7 @@ export const AbgabetoolAssistenz = {
|
|||||||
<template #opposite="slotProps">
|
<template #opposite="slotProps">
|
||||||
<div class="row g-1">
|
<div class="row g-1">
|
||||||
<div class="col-5 fw-semibold text-end">
|
<div class="col-5 fw-semibold text-end">
|
||||||
{{ $capitalize($p.t('abgabetool/c4zieldatum')) }}:
|
{{ $capitalize($p.t('abgabetool/c4zieldatumv2')) }}:
|
||||||
</div>
|
</div>
|
||||||
<div class="col-7">
|
<div class="col-7">
|
||||||
{{ formatDate(slotProps.item.datum) }}
|
{{ formatDate(slotProps.item.datum) }}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import BsModal from '../../Bootstrap/Modal.js';
|
|||||||
import VueDatePicker from '../../vueDatepicker.js.php';
|
import VueDatePicker from '../../vueDatepicker.js.php';
|
||||||
import ApiAbgabe from '../../../api/factory/abgabe.js'
|
import ApiAbgabe from '../../../api/factory/abgabe.js'
|
||||||
import FhcOverlay from "../../Overlay/FhcOverlay.js";
|
import FhcOverlay from "../../Overlay/FhcOverlay.js";
|
||||||
|
import { getDateStyleClass } from "./getDateStyleClass.js";
|
||||||
|
import { dateFilter } from '../../../tabulator/filters/Dates.js';
|
||||||
|
import {splitMailsHelper} from "../../../helpers/EmailHelpers.js";
|
||||||
|
|
||||||
export const AbgabetoolMitarbeiter = {
|
export const AbgabetoolMitarbeiter = {
|
||||||
name: "AbgabetoolMitarbeiter",
|
name: "AbgabetoolMitarbeiter",
|
||||||
@@ -14,6 +17,7 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
Checkbox: primevue.checkbox,
|
Checkbox: primevue.checkbox,
|
||||||
Dropdown: primevue.dropdown,
|
Dropdown: primevue.dropdown,
|
||||||
Textarea: primevue.textarea,
|
Textarea: primevue.textarea,
|
||||||
|
TieredMenu: primevue.tieredmenu,
|
||||||
VueDatePicker,
|
VueDatePicker,
|
||||||
FhcOverlay
|
FhcOverlay
|
||||||
},
|
},
|
||||||
@@ -46,6 +50,7 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
phrasenResolved: false,
|
phrasenResolved: false,
|
||||||
turnitin_link: null,
|
turnitin_link: null,
|
||||||
old_abgabe_beurteilung_link: null,
|
old_abgabe_beurteilung_link: null,
|
||||||
|
BETREUER_SAMMELMAIL_BUTTON_STUDENT: null,
|
||||||
saving: false,
|
saving: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
abgabeTypeOptions: null,
|
abgabeTypeOptions: null,
|
||||||
@@ -79,7 +84,7 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
placeholder: Vue.computed(() => this.$p.t('global/noDataAvailable')),
|
placeholder: Vue.computed(() => this.$p.t('global/noDataAvailable')),
|
||||||
selectable: true,
|
selectable: true,
|
||||||
selectableCheck: this.selectionCheck,
|
selectableCheck: this.selectionCheck,
|
||||||
rowHeight: 80,
|
rowHeight: 40,
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
formatter: function (cell, formatterParams, onRendered) {
|
formatter: function (cell, formatterParams, onRendered) {
|
||||||
@@ -135,18 +140,36 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
width: 50,
|
width: 50,
|
||||||
cssClass: 'sticky-col'
|
cssClass: 'sticky-col'
|
||||||
},
|
},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', formatter: this.detailFormatter, widthGrow: 1, tooltip: false, cssClass: 'sticky-col'},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', formatter: this.detailFormatter, headerFilter: false, headerSort: false, widthGrow: 1, tooltip: false, cssClass: 'sticky-col'},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4personenkennzeichen'))), headerFilter: true, field: 'pkz', formatter: this.pkzTextFormatter, widthGrow: 1, tooltip: false},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4personenkennzeichen'))), headerFilter: true, field: 'pkz', formatter: this.pkzTextFormatter, widthGrow: 1, tooltip: false},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4kontakt'))), field: 'mail', formatter: this.mailFormatter, widthGrow: 1, tooltip: false, visible: false},
|
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'vorname', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'vorname', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4projekttyp'))), field: 'projekttyp_kurzbz', formatter: this.centeredTextFormatter, widthGrow: 1},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4projekttyp'))), field: 'projekttyp_kurzbz', formatter: this.centeredTextFormatter, widthGrow: 1},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4stg'))), field: 'stg', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4stg'))), field: 'stg', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4sem'))), field: 'studiensemester_kurzbz', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4sem'))), field: 'studiensemester_kurzbz', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4titel'))), field: 'titel', headerFilter: true, formatter: this.centeredTextFormatter, maxWidth: 500, widthGrow: 8},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4titel'))), field: 'titel', headerFilter: true, formatter: this.centeredTextFormatter, maxWidth: 500, widthGrow: 8},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4betreuerart'))), field: 'betreuerart_beschreibung',formatter: this.centeredTextFormatter, widthGrow: 1}
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4betreuerartv2'))), field: 'betreuerart_beschreibung',formatter: this.centeredTextFormatter, widthGrow: 1},
|
||||||
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4prevAbgabetermin'))), field: 'prevTermin',
|
||||||
|
headerFilter: dateFilter,
|
||||||
|
headerFilterFunc: this.headerFilterTerminCol,
|
||||||
|
sorter: this.sortFuncTerminCol,
|
||||||
|
formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
|
||||||
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nextAbgabetermin'))), field: 'nextTermin',
|
||||||
|
headerFilter: dateFilter,
|
||||||
|
headerFilterFunc: this.headerFilterTerminCol,
|
||||||
|
sorter: this.sortFuncTerminCol,
|
||||||
|
formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false},
|
||||||
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate1Status'))),
|
||||||
|
headerFilter: 'list',
|
||||||
|
headerFilterParams: { valuesLookup: this.getQGateStatusList },
|
||||||
|
field: 'qgate1Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false},
|
||||||
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate2Status'))),
|
||||||
|
headerFilter: 'list',
|
||||||
|
headerFilterParams: { valuesLookup: this.getQGateStatusList },
|
||||||
|
field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false}
|
||||||
],
|
],
|
||||||
persistence: false,
|
persistence: false,
|
||||||
|
persistenceID: 'abgabeTableBetreuer2026-02-26'
|
||||||
},
|
},
|
||||||
abgabeTableEventHandlers: [{
|
abgabeTableEventHandlers: [{
|
||||||
event: "tableBuilt",
|
event: "tableBuilt",
|
||||||
@@ -182,6 +205,331 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
]};
|
]};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
handlePaUpdated(projektarbeit) {
|
||||||
|
this.checkAbgabetermineProjektarbeit(projektarbeit)
|
||||||
|
this.$refs.abgabeTable.tabulator.redraw(true)
|
||||||
|
},
|
||||||
|
sammelMailStudent(param) {
|
||||||
|
|
||||||
|
const recipientList = [];
|
||||||
|
this.selectedData.forEach(d => {
|
||||||
|
recipientList.push(`${d.student_uid}@${this.domain}`)
|
||||||
|
})
|
||||||
|
const uniqueRecipients = [...new Set(recipientList)];
|
||||||
|
const subject = ""; // empty subject line
|
||||||
|
splitMailsHelper(uniqueRecipients, param.originalEvent, subject, this.$fhcAlert, this.$p)
|
||||||
|
},
|
||||||
|
getQGateStatusList() {
|
||||||
|
return [
|
||||||
|
this.$p.t('abgabetool/c4keinTerminVorhanden'),
|
||||||
|
this.$p.t('abgabetool/c4positivBenotet'),
|
||||||
|
this.$p.t('abgabetool/c4negativBenotet'),
|
||||||
|
this.$p.t('abgabetool/c4notYetGraded'),
|
||||||
|
this.$p.t('abgabetool/c4notSubmitted'),
|
||||||
|
this.$p.t('abgabetool/c4notHappenedYet')
|
||||||
|
]
|
||||||
|
},
|
||||||
|
sortFuncTerminCol(a, b, aRow, bRow, column, dir, params) {
|
||||||
|
if (a === null || typeof a === "undefined") return 1;
|
||||||
|
if (b === null || typeof b === "undefined") return -1;
|
||||||
|
|
||||||
|
// try to handle the prev/next interpretation consistently
|
||||||
|
// can only make this wrong UX whise so whatever
|
||||||
|
if(column._column.field == 'prevTermin') {
|
||||||
|
return Math.abs(b.diffMs) - Math.abs(a.diffMs)
|
||||||
|
} else if (column._column.field == 'nextTermin') {
|
||||||
|
return Math.abs(a.diffMs) - Math.abs(b.diffMs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// just in case someone reuses this
|
||||||
|
return Math.abs(b.diffMs) - Math.abs(a.diffMs)
|
||||||
|
},
|
||||||
|
headerFilterTerminCol(filterVal, rowVal) {
|
||||||
|
if (!rowVal || !rowVal.luxonDate || !rowVal.luxonDate.isValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowDate = rowVal.luxonDate;
|
||||||
|
|
||||||
|
const toLuxon = (val) => {
|
||||||
|
if (!val) return null;
|
||||||
|
let dt;
|
||||||
|
if (val instanceof Date) {
|
||||||
|
dt = luxon.DateTime.fromJSDate(val);
|
||||||
|
} else if (typeof val === "string") {
|
||||||
|
dt = luxon.DateTime.fromISO(val);
|
||||||
|
} else { // fallback
|
||||||
|
dt = luxon.DateTime.fromMillis(Number(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
return dt.isValid ? dt : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const von = toLuxon(filterVal[0]);
|
||||||
|
const bis = toLuxon(filterVal[1]);
|
||||||
|
|
||||||
|
// specific day
|
||||||
|
if (von && !bis) {
|
||||||
|
return rowDate.hasSame(von, "day");
|
||||||
|
}
|
||||||
|
|
||||||
|
// range case
|
||||||
|
if (von && bis) {
|
||||||
|
return rowDate >= von.startOf("day") && rowDate <= bis.endOf("day");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
loadState() {
|
||||||
|
return JSON.parse(localStorage.getItem(this.abgabeTableOptions.persistenceID) || "null");
|
||||||
|
},
|
||||||
|
saveState(table) {
|
||||||
|
// avoid storing state after first restore part happened
|
||||||
|
if(!this.stateRestored) return
|
||||||
|
const rawLayout = table.getColumnLayout();
|
||||||
|
const state = {
|
||||||
|
columns: rawLayout.map(col => ({
|
||||||
|
field: col.field,
|
||||||
|
visible: col.visible,
|
||||||
|
width: col.width,
|
||||||
|
})),
|
||||||
|
sort: table.getSorters().map(s => ({
|
||||||
|
field: s.field,
|
||||||
|
dir: s.dir,
|
||||||
|
})),
|
||||||
|
filters: table.getFilters(),
|
||||||
|
headerFilters: table.getHeaderFilters()
|
||||||
|
};
|
||||||
|
|
||||||
|
localStorage.setItem(this.abgabeTableOptions.persistenceID, JSON.stringify(state));
|
||||||
|
},
|
||||||
|
handleTableBuilt() {
|
||||||
|
const table = this.$refs.abgabeTable.tabulator
|
||||||
|
|
||||||
|
this.tableBuiltResolve()
|
||||||
|
|
||||||
|
table.on("columnMoved", () => {
|
||||||
|
this.saveState(table);
|
||||||
|
});
|
||||||
|
|
||||||
|
table.on("columnResized", () => {
|
||||||
|
this.saveState(table);
|
||||||
|
});
|
||||||
|
|
||||||
|
table.on("columnVisibilityChanged", () => {
|
||||||
|
this.saveState(table);
|
||||||
|
});
|
||||||
|
|
||||||
|
table.on("filterChanged", () => {
|
||||||
|
this.saveState(table);
|
||||||
|
});
|
||||||
|
|
||||||
|
table.on("headerFilterChanged", () => {
|
||||||
|
this.saveState(table);
|
||||||
|
});
|
||||||
|
|
||||||
|
table.on("dataSorted", () => {
|
||||||
|
this.saveState(table);
|
||||||
|
});
|
||||||
|
|
||||||
|
table.on("columnSorted", () => {
|
||||||
|
this.saveState(table);
|
||||||
|
});
|
||||||
|
|
||||||
|
table.on("sortersChanged", () => {
|
||||||
|
this.saveState(table);
|
||||||
|
});
|
||||||
|
|
||||||
|
const saved = this.loadState();
|
||||||
|
|
||||||
|
table.on("renderComplete", () => {
|
||||||
|
if(!this.stateRestored) {
|
||||||
|
|
||||||
|
if (saved?.columns && !this.colLayoutRestored) {
|
||||||
|
const layout = saved.columns.map(col => ({
|
||||||
|
field: col.field,
|
||||||
|
width: col.width,
|
||||||
|
visible: col.visible,
|
||||||
|
// add more if needed, but keep it simple
|
||||||
|
}));
|
||||||
|
|
||||||
|
table.setColumnLayout(layout);
|
||||||
|
|
||||||
|
this.colLayoutRestored = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (saved?.filters && !this.filtersRestored) {
|
||||||
|
this.filtersRestored = true // instantly avoid retriggers
|
||||||
|
table.setFilter(saved.filters);
|
||||||
|
}
|
||||||
|
if (saved?.headerFilters && !this.headerFiltersRestored) {
|
||||||
|
this.headerFiltersRestored = true // instantly avoid retriggers
|
||||||
|
for (let hf of saved.headerFilters) {
|
||||||
|
table.setHeaderFilterValue(hf.field, hf.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (saved?.sort?.length && !this.sortRestored) {
|
||||||
|
this.sortRestored = true;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const sortList = saved.sort.map(s => {
|
||||||
|
const col = table.columnManager.findColumn(s.field);
|
||||||
|
if (!col) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return { column: col, dir: s.dir };
|
||||||
|
}).filter(Boolean);
|
||||||
|
|
||||||
|
table.setSort(sortList);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
this.stateRestored = true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
checkQualityGateStatus(projekt) {
|
||||||
|
const qgate1Termine = []
|
||||||
|
const qgate2Termine = []
|
||||||
|
|
||||||
|
projekt.qgate1Status = this.$p.t('abgabetool/c4keinTerminVorhanden')// 'Kein Termin vorhanden'
|
||||||
|
projekt.qgate1StatusRank = 0
|
||||||
|
projekt.qgate2Status = this.$p.t('abgabetool/c4keinTerminVorhanden')
|
||||||
|
projekt.qgate2StatusRank = 0
|
||||||
|
|
||||||
|
projekt.abgabetermine.forEach(termin => {
|
||||||
|
if(termin.paabgabetyp_kurzbz == 'qualgate1') qgate1Termine.push(termin)
|
||||||
|
if(termin.paabgabetyp_kurzbz == 'qualgate2') qgate2Termine.push(termin)
|
||||||
|
})
|
||||||
|
|
||||||
|
// calculate qgateStatusRank and display the highest order status rank of all quality gate termine until one
|
||||||
|
// counts as passed, which is just a positive note no matter if anything has been uploaded
|
||||||
|
|
||||||
|
// reuse luxon calculated diffMs (termin.datum in relation to today) from previous datestyle check
|
||||||
|
qgate1Termine.forEach(qgate => {
|
||||||
|
if(qgate.note != null && projekt.qgate1StatusRank <= 5) {
|
||||||
|
const noteOpt = typeof qgate.note !== 'object' ? this.notenOptions.find(opt => opt.note == qgate.note) : qgate.note
|
||||||
|
if(noteOpt.positiv) {
|
||||||
|
projekt.qgate1Status = this.$p.t('abgabetool/c4positivBenotet')
|
||||||
|
projekt.qgate1StatusRank = 5
|
||||||
|
} else {
|
||||||
|
projekt.qgate1Status = this.$p.t('abgabetool/c4negativBenotet')
|
||||||
|
projekt.qgate1StatusRank = 4
|
||||||
|
}
|
||||||
|
} else if (qgate.note == null && projekt.qgate1StatusRank <= 3) {
|
||||||
|
projekt.qgate1Status = this.$p.t('abgabetool/c4notYetGraded')
|
||||||
|
projekt.qgate1StatusRank = 3
|
||||||
|
} else if(qgate.upload_allowed == true && qgate.abgabedatum == null && projekt.qgate1StatusRank <= 2) {
|
||||||
|
projekt.qgate1Status = this.$p.t('abgabetool/c4notSubmitted')
|
||||||
|
projekt.qgate1StatusRank = 2
|
||||||
|
} else if (qgate.upload_allowed == false && qgate.diffMs <= 0 && projekt.qgate1StatusRank <= 1) {
|
||||||
|
projekt.qgate1Status = this.$p.t('abgabetool/c4notHappenedYet')
|
||||||
|
projekt.qgate1StatusRank = 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
qgate2Termine.forEach(qgate => {
|
||||||
|
if(qgate.note != null && projekt.qgate1StatusRank <= 5) {
|
||||||
|
const noteOpt = typeof qgate.note !== 'object' ? this.notenOptions.find(opt => opt.note == qgate.note) : qgate.note
|
||||||
|
if(noteOpt.positiv) {
|
||||||
|
projekt.qgate2Status = this.$p.t('abgabetool/c4positivBenotet')
|
||||||
|
projekt.qgate2StatusRank = 5
|
||||||
|
} else {
|
||||||
|
projekt.qgate2Status = this.$p.t('abgabetool/c4negativBenotet')
|
||||||
|
projekt.qgate2StatusRank = 4
|
||||||
|
}
|
||||||
|
} else if (qgate.note == null && projekt.qgate2StatusRank <= 3) {
|
||||||
|
projekt.qgate2Status = this.$p.t('abgabetool/c4notYetGraded')
|
||||||
|
projekt.qgate2StatusRank = 3
|
||||||
|
} else if(qgate.upload_allowed == true && qgate.abgabedatum == null && projekt.qgate2StatusRank <= 2) {
|
||||||
|
projekt.qgate2Status = this.$p.t('abgabetool/c4notSubmitted')
|
||||||
|
projekt.qgate2StatusRank = 2
|
||||||
|
} else if (qgate.upload_allowed == false && qgate.diffMs <= 0 && projekt.qgate2StatusRank <= 1) {
|
||||||
|
projekt.qgate2Status = this.$p.t('abgabetool/c4notHappenedYet')
|
||||||
|
projekt.qgate2StatusRank = 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
checkAbgabetermineProjektarbeit(projekt) {
|
||||||
|
const now = luxon.DateTime.now()
|
||||||
|
// calculate Abgabetermin time diff to now and assign last and next to projekt
|
||||||
|
projekt.abgabetermine.forEach(termin => {
|
||||||
|
|
||||||
|
// while already looping through each termin, calculate datestyle beforehand
|
||||||
|
termin.dateStyle = getDateStyleClass(termin, this.notenOptions)
|
||||||
|
|
||||||
|
const date = luxon.DateTime.fromISO(termin.datum).endOf('day')
|
||||||
|
termin.luxonDate = date
|
||||||
|
termin.diffMs = date.toMillis() - now.toMillis(); // positive = future, negative = past
|
||||||
|
|
||||||
|
if (termin.diffMs < 0) {
|
||||||
|
if (!projekt.prevTermin ||
|
||||||
|
termin.diffMs > projekt.prevTermin.diffMs // larger (less negative) = closer to now
|
||||||
|
) {
|
||||||
|
projekt.prevTermin = termin;
|
||||||
|
}
|
||||||
|
} else if (termin.diffMs > 0) {
|
||||||
|
if (!projekt.nextTermin ||
|
||||||
|
termin.diffMs < projekt.nextTermin.diffMs // smaller positive = closer to now
|
||||||
|
) {
|
||||||
|
projekt.nextTermin = termin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// seperate check for quality gates
|
||||||
|
this.checkQualityGateStatus(projekt)
|
||||||
|
},
|
||||||
|
abgabterminFormatter(cell) {
|
||||||
|
const val = cell.getValue()
|
||||||
|
|
||||||
|
if(val) {
|
||||||
|
let icon = ''
|
||||||
|
switch(val.dateStyle) {
|
||||||
|
case 'verspaetet':
|
||||||
|
icon = '<i class="fa-solid fa-triangle-exclamation"></i>'
|
||||||
|
break
|
||||||
|
case 'verpasst':
|
||||||
|
icon = '<i class="fa-solid fa-calendar-xmark"></i>'
|
||||||
|
break
|
||||||
|
case 'abzugeben':
|
||||||
|
icon = '<i class="fa-solid fa-hourglass-half"></i>'
|
||||||
|
break
|
||||||
|
case 'standard':
|
||||||
|
icon = '<i class="fa-solid fa-clock"></i>'
|
||||||
|
break
|
||||||
|
case 'abgegeben':
|
||||||
|
icon = '<i class="fa-solid fa-paperclip"></i>'
|
||||||
|
break
|
||||||
|
case 'beurteilungerforderlich':
|
||||||
|
icon = '<i class="fa-solid fa-list-check"></i>'
|
||||||
|
break
|
||||||
|
case 'bestanden':
|
||||||
|
icon = '<i class="fa-solid fa-check"></i>'
|
||||||
|
break
|
||||||
|
case 'nichtbestanden':
|
||||||
|
icon = '<i class="fa-solid fa-circle-exclamation"></i>'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const bezeichnung = val.bezeichnung?.bezeichnung ?? val.bezeichnung
|
||||||
|
|
||||||
|
return '<div style="display: flex; height: 100%">' +
|
||||||
|
'<div class=' + val.dateStyle + "-header" + ' style="width:48px; height: 100%; padding: 0px; display: flex; align-items: center; justify-content: center;">' +
|
||||||
|
icon +
|
||||||
|
'</div>' +
|
||||||
|
'<div style="margin-left: 4px;">' +
|
||||||
|
'<p style="max-width: 100%; word-wrap: break-word; white-space: normal;">'+bezeichnung+' - '+ this.formatDate(val.datum)+'</p>' +
|
||||||
|
'</div>'+
|
||||||
|
'</div>'
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
selectHandler(e, cell) {
|
selectHandler(e, cell) {
|
||||||
const row = cell.getRow();
|
const row = cell.getRow();
|
||||||
|
|
||||||
@@ -274,6 +622,24 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
)).then(res => {
|
)).then(res => {
|
||||||
if (res.meta.status === "success" && res.data) {
|
if (res.meta.status === "success" && res.data) {
|
||||||
this.$fhcAlert.alertSuccess(this.$p.t('abgabetool/serienTerminGespeichert'))
|
this.$fhcAlert.alertSuccess(this.$p.t('abgabetool/serienTerminGespeichert'))
|
||||||
|
|
||||||
|
const oldScrollLeft = this.$refs.abgabeTable?.tabulator.rowManager.scrollLeft
|
||||||
|
const oldScrollTop = this.$refs.abgabeTable?.tabulator.rowManager.scrollTop
|
||||||
|
this.loading = true
|
||||||
|
this.loadProjektarbeiten(this.showAll, () => {
|
||||||
|
this.$refs.abgabeTable?.tabulator.redraw(true)
|
||||||
|
this.$refs.abgabeTable?.tabulator.setSort([]);
|
||||||
|
this.loading = false
|
||||||
|
|
||||||
|
Vue.nextTick(()=> {
|
||||||
|
const table = this.$refs.abgabeTable?.tabulator.element.querySelector('.tabulator-tableholder')
|
||||||
|
if(table) {
|
||||||
|
table.scrollLeft = oldScrollLeft;
|
||||||
|
table.scrollTop = oldScrollTop;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
this.$fhcAlert.alertError(this.$p.t('abgabetool/errorSerienterminSpeichern'))
|
this.$fhcAlert.alertError(this.$p.t('abgabetool/errorSerienterminSpeichern'))
|
||||||
}
|
}
|
||||||
@@ -294,14 +660,14 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
return str
|
return str
|
||||||
},
|
},
|
||||||
isPastDate(date) {
|
isPastDate(date) {
|
||||||
return new Date(date) < new Date(Date.now())
|
const deadline = luxon.DateTime.fromISO(date, { zone: 'Europe/Vienna' }).endOf('day');
|
||||||
|
const nowInVienna = luxon.DateTime.now().setZone('Europe/Vienna');
|
||||||
|
return nowInVienna > deadline;
|
||||||
},
|
},
|
||||||
setDetailComponent(details){
|
setDetailComponent(details){
|
||||||
this.loading=true
|
this.loading=true
|
||||||
this.loadAbgaben(details).then((res)=> {
|
|
||||||
const pa = this.projektarbeiten?.retval?.find(projekarbeit => projekarbeit.projektarbeit_id == details.projektarbeit_id)
|
const pa = this.projektarbeiten?.retval?.find(projekarbeit => projekarbeit.projektarbeit_id == details.projektarbeit_id)
|
||||||
pa.abgabetermine = res.data[0].retval
|
|
||||||
pa.isCurrent = res.data[1]
|
|
||||||
|
|
||||||
let paIsBenotet = false
|
let paIsBenotet = false
|
||||||
if(pa.note !== undefined && pa.note !== null) {
|
if(pa.note !== undefined && pa.note !== null) {
|
||||||
@@ -313,10 +679,28 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
paIsBenotet = true
|
paIsBenotet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(pa?.abgabetermine?.length) {
|
||||||
|
this.$api.call(ApiAbgabe.getSignaturStatusForProjektarbeitAbgaben(pa.abgabetermine.map(termin => termin.paabgabe_id), pa.student_uid))
|
||||||
|
.then(res => {
|
||||||
|
if(res.meta.status === 'success') {
|
||||||
|
res.data.forEach(paabgabe => {
|
||||||
|
const termin = pa.abgabetermine.find(abgabe => abgabe.paabgabe_id == paabgabe.paabgabe_id)
|
||||||
|
if(termin && paabgabe.signatur !== undefined) termin.signatur = paabgabe.signatur
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pa.abgabetermine.forEach(termin => {
|
pa.abgabetermine.forEach(termin => {
|
||||||
termin.note = this.allowedNotenOptions.find(opt => opt.note == termin.note)
|
const noteOpt = this.allowedNotenOptions.find(opt => opt.note == termin.note)
|
||||||
|
if(noteOpt) termin.note = noteOpt
|
||||||
termin.file = []
|
termin.file = []
|
||||||
|
|
||||||
|
// only set this if it has not been set yet and abgabetermin has a note (qgate)
|
||||||
|
if(!termin.noteBackend && noteOpt) {
|
||||||
|
termin.noteBackend = noteOpt
|
||||||
|
}
|
||||||
|
|
||||||
// update 08-01-2026: everybody is allowed to do everything in client, critical checks happen at backend level
|
// update 08-01-2026: everybody is allowed to do everything in client, critical checks happen at backend level
|
||||||
// termin.allowedToSave = true
|
// termin.allowedToSave = true
|
||||||
|
|
||||||
@@ -329,13 +713,16 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
termin.bezeichnung = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === termin.paabgabetyp_kurzbz)
|
termin.bezeichnung = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === termin.paabgabetyp_kurzbz)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
pa.student_uid = details.student_uid
|
pa.student_uid = details.student_uid
|
||||||
pa.student = `${pa.vorname} ${pa.nachname}`
|
pa.student = `${pa.vorname} ${pa.nachname}`
|
||||||
|
|
||||||
this.selectedProjektarbeit = pa
|
this.selectedProjektarbeit = pa
|
||||||
this.$refs.modalContainerAbgabeDetail.show()
|
this.$refs.modalContainerAbgabeDetail.show()
|
||||||
|
|
||||||
}).finally(()=>{this.loading = false})
|
|
||||||
|
this.loading = false
|
||||||
|
|
||||||
},
|
},
|
||||||
centeredTextFormatter(cell) {
|
centeredTextFormatter(cell) {
|
||||||
const val = cell.getValue()
|
const val = cell.getValue()
|
||||||
@@ -348,11 +735,6 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
|
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
|
||||||
'<a><i class="fa fa-folder-open" style="color:#00649C"></i></a></div>'
|
'<a><i class="fa fa-folder-open" style="color:#00649C"></i></a></div>'
|
||||||
},
|
},
|
||||||
mailFormatter(cell) {
|
|
||||||
const val = cell.getValue()
|
|
||||||
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
|
|
||||||
'<a href='+val+'><i class="fa fa-envelope" style="color:#00649C"></i></a></div>'
|
|
||||||
},
|
|
||||||
beurteilungFormatter(cell) {
|
beurteilungFormatter(cell) {
|
||||||
const val = cell.getValue()
|
const val = cell.getValue()
|
||||||
if(val) {
|
if(val) {
|
||||||
@@ -379,11 +761,13 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
return (projekt.typ + projekt.kurzbz)?.toUpperCase()
|
return (projekt.typ + projekt.kurzbz)?.toUpperCase()
|
||||||
},
|
},
|
||||||
setupData(data){
|
setupData(data){
|
||||||
|
|
||||||
|
|
||||||
this.projektarbeiten = data[0]
|
this.projektarbeiten = data[0]
|
||||||
this.domain = data[1]
|
this.domain = data[1]
|
||||||
|
|
||||||
this.tableData = data[0]?.retval?.map(projekt => {
|
this.tableData = data[0]?.retval?.map(projekt => {
|
||||||
|
this.checkAbgabetermineProjektarbeit(projekt)
|
||||||
projekt.selectable = projekt.betreuerart_kurzbz !== 'Zweitbegutachter'
|
projekt.selectable = projekt.betreuerart_kurzbz !== 'Zweitbegutachter'
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -455,6 +839,29 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
emailItems() {
|
||||||
|
const menu = []
|
||||||
|
|
||||||
|
if(this.BETREUER_SAMMELMAIL_BUTTON_STUDENT){
|
||||||
|
menu.push({
|
||||||
|
label: this.$p.t('abgabetool/c4sendEmailStudierendev2', [this.uniqueStudentEmailCount]),
|
||||||
|
command: this.sammelMailStudent
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return menu
|
||||||
|
},
|
||||||
|
uniqueStudentEmailCount() {
|
||||||
|
const emails = new Set();
|
||||||
|
|
||||||
|
this.selectedData.forEach(row => {
|
||||||
|
if (row.student_uid) {
|
||||||
|
emails.add(row.student_uid); // actually dont need domain for this
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return emails.size;
|
||||||
|
},
|
||||||
getAllowedAbgabeTypeOptions() {
|
getAllowedAbgabeTypeOptions() {
|
||||||
return this.abgabeTypeOptions.filter(opt => this.abgabetypenBetreuer.includes(opt.paabgabetyp_kurzbz))
|
return this.abgabeTypeOptions.filter(opt => this.abgabetypenBetreuer.includes(opt.paabgabetyp_kurzbz))
|
||||||
}
|
}
|
||||||
@@ -467,6 +874,7 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
this.turnitin_link = res.data?.turnitin_link
|
this.turnitin_link = res.data?.turnitin_link
|
||||||
this.old_abgabe_beurteilung_link = res.data?.old_abgabe_beurteilung_link
|
this.old_abgabe_beurteilung_link = res.data?.old_abgabe_beurteilung_link
|
||||||
this.abgabetypenBetreuer = res.data?.abgabetypenBetreuer
|
this.abgabetypenBetreuer = res.data?.abgabetypenBetreuer
|
||||||
|
this.BETREUER_SAMMELMAIL_BUTTON_STUDENT = res.data?.BETREUER_SAMMELMAIL_BUTTON_STUDENT
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
})
|
})
|
||||||
@@ -515,7 +923,7 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
|
|
||||||
<div class="row mt-2">
|
<div class="row mt-2">
|
||||||
<div class="col-12 col-md-3 align-content-center">
|
<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-bold" style="margin-left: 2px">{{$capitalize( $p.t('abgabetool/c4zieldatumv2') )}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-9">
|
<div class="col-12 col-md-9">
|
||||||
<VueDatePicker
|
<VueDatePicker
|
||||||
@@ -523,7 +931,8 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
v-model="serienTermin.datum"
|
v-model="serienTermin.datum"
|
||||||
:clearable="false"
|
:clearable="false"
|
||||||
:enable-time-picker="false"
|
:enable-time-picker="false"
|
||||||
:format="formatDate"
|
locale="de"
|
||||||
|
format="dd.MM.yyyy"
|
||||||
:text-input="true"
|
:text-input="true"
|
||||||
auto-apply>
|
auto-apply>
|
||||||
</VueDatePicker>
|
</VueDatePicker>
|
||||||
@@ -557,7 +966,7 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mt-2">
|
<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-3 fw-bold align-content-center">{{$capitalize( $p.t('abgabetool/c4abgabekurzbzv2') )}}</div>
|
||||||
<div class="col-12 col-md-9">
|
<div class="col-12 col-md-9">
|
||||||
<Textarea style="margin-bottom: 4px;" v-model="serienTermin.kurzbz" rows="1" class="w-100"></Textarea>
|
<Textarea style="margin-bottom: 4px;" v-model="serienTermin.kurzbz" rows="1" class="w-100"></Textarea>
|
||||||
</div>
|
</div>
|
||||||
@@ -578,7 +987,11 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:default>
|
<template v-slot:default>
|
||||||
<AbgabeDetail :projektarbeit="selectedProjektarbeit" :isFullscreen="detailIsFullscreen"></AbgabeDetail>
|
<AbgabeDetail
|
||||||
|
:projektarbeit="selectedProjektarbeit"
|
||||||
|
:isFullscreen="detailIsFullscreen"
|
||||||
|
@paUpdated="handlePaUpdated">
|
||||||
|
</AbgabeDetail>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</bs-modal>
|
</bs-modal>
|
||||||
@@ -598,6 +1011,7 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
@click:new=openAddSeriesModal
|
@click:new=openAddSeriesModal
|
||||||
:tabulator-options="abgabeTableOptions"
|
:tabulator-options="abgabeTableOptions"
|
||||||
:tabulator-events="abgabeTableEventHandlers"
|
:tabulator-events="abgabeTableEventHandlers"
|
||||||
|
@tableBuilt="handleTableBuilt"
|
||||||
tableOnly
|
tableOnly
|
||||||
:sideMenu="false"
|
:sideMenu="false"
|
||||||
:useSelectionSpan="false"
|
:useSelectionSpan="false"
|
||||||
@@ -613,6 +1027,16 @@ export const AbgabetoolMitarbeiter = {
|
|||||||
<i class="fa fa-hourglass-end"></i>
|
<i class="fa fa-hourglass-end"></i>
|
||||||
{{ $p.t('abgabetool/showDeadlines') }}
|
{{ $p.t('abgabetool/showDeadlines') }}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="emailItems.length"
|
||||||
|
role="button"
|
||||||
|
@click="evt => $refs.menu.toggle(evt)"
|
||||||
|
class="btn btn-outline-secondary dropdown-toggle"
|
||||||
|
aria-haspopup="true"
|
||||||
|
>
|
||||||
|
<i class="fa fa-envelope"></i>
|
||||||
|
</button>
|
||||||
|
<tiered-menu ref="menu" :model="emailItems" popup :autoZIndex="false" />
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</core-filter-cmpt>
|
</core-filter-cmpt>
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import AbgabeDetail from "./AbgabeStudentDetail.js";
|
|||||||
import ApiAbgabe from '../../../api/factory/abgabe.js'
|
import ApiAbgabe from '../../../api/factory/abgabe.js'
|
||||||
import BsModal from "../../Bootstrap/Modal.js";
|
import BsModal from "../../Bootstrap/Modal.js";
|
||||||
import FhcOverlay from "../../Overlay/FhcOverlay.js";
|
import FhcOverlay from "../../Overlay/FhcOverlay.js";
|
||||||
|
import { getDateStyleClass} from "./getDateStyleClass.js";
|
||||||
|
|
||||||
const today = new Date()
|
|
||||||
export const AbgabetoolStudent = {
|
export const AbgabetoolStudent = {
|
||||||
name: "AbgabetoolStudent",
|
name: "AbgabetoolStudent",
|
||||||
components: {
|
components: {
|
||||||
@@ -48,61 +48,6 @@ export const AbgabetoolStudent = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
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)
|
|
||||||
|
|
||||||
termin.diffindays = this.dateDiffInDays(termin.datum)
|
|
||||||
|
|
||||||
const isLate = termin.abgabedatum && abgabedatum > datum;
|
|
||||||
|
|
||||||
// GRADE STATUS
|
|
||||||
if (termin.note) {
|
|
||||||
if(Number.isInteger(termin.note)) {
|
|
||||||
const opt = this.notenOptions.find(opt => opt.note == termin.note)
|
|
||||||
if(opt.positiv) return 'bestanden'
|
|
||||||
}
|
|
||||||
if (termin.note.positiv) return 'bestanden';
|
|
||||||
return 'nichtbestanden';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ACTION REQUIRED FOR GRADE
|
|
||||||
if (termin.bezeichnung?.benotbar && datum < today) {
|
|
||||||
return 'beurteilungerforderlich';
|
|
||||||
}
|
|
||||||
|
|
||||||
// SUBMISSION STATUS
|
|
||||||
if (termin.upload_allowed) {
|
|
||||||
if (termin.abgabedatum) {
|
|
||||||
return isLate ? 'verspaetet' : 'abgegeben';
|
|
||||||
}
|
|
||||||
|
|
||||||
// no submission yet
|
|
||||||
if (datum < today) return 'verpasst';
|
|
||||||
if (termin.diffindays <= 12) return 'abzugeben';
|
|
||||||
return 'standard';
|
|
||||||
}
|
|
||||||
|
|
||||||
// GENERIC STATUS
|
|
||||||
return datum < today ? 'verpasst' : 'standard';
|
|
||||||
},
|
|
||||||
checkQualityGatesStrict(termine) {
|
checkQualityGatesStrict(termine) {
|
||||||
let qgate1Passed = false
|
let qgate1Passed = false
|
||||||
let qgate2Passed = false
|
let qgate2Passed = false
|
||||||
@@ -155,7 +100,9 @@ export const AbgabetoolStudent = {
|
|||||||
return qgate1positiv && qgate2positiv
|
return qgate1positiv && qgate2positiv
|
||||||
},
|
},
|
||||||
isPastDate(date) {
|
isPastDate(date) {
|
||||||
return new Date(date) < new Date(Date.now())
|
const deadline = luxon.DateTime.fromISO(date, { zone: 'Europe/Vienna' }).endOf('day');
|
||||||
|
const nowInVienna = luxon.DateTime.now().setZone('Europe/Vienna');
|
||||||
|
return nowInVienna > deadline;
|
||||||
},
|
},
|
||||||
setDetailComponent(details){
|
setDetailComponent(details){
|
||||||
this.loading = true
|
this.loading = true
|
||||||
@@ -173,8 +120,8 @@ export const AbgabetoolStudent = {
|
|||||||
// old assumed production logic when qgates are required
|
// old assumed production logic when qgates are required
|
||||||
// termin.allowedToUpload = !this.isPastDate(termin.datum) && this.checkQualityGatesStrict(pa.abgabetermine)
|
// termin.allowedToUpload = !this.isPastDate(termin.datum) && this.checkQualityGatesStrict(pa.abgabetermine)
|
||||||
|
|
||||||
// new larifari we want qgates but they are optional fhtw mode
|
const inTime = termin.fixtermin ? !this.isPastDate(termin.datum) : true
|
||||||
termin.allowedToUpload = !this.isPastDate(termin.datum) && this.checkQualityGatesOptional(pa.abgabetermine)
|
termin.allowedToUpload = inTime && this.checkQualityGatesOptional(pa.abgabetermine)
|
||||||
|
|
||||||
|
|
||||||
// development purposes
|
// development purposes
|
||||||
@@ -193,7 +140,7 @@ export const AbgabetoolStudent = {
|
|||||||
|
|
||||||
|
|
||||||
termin.bezeichnung = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === termin.paabgabetyp_kurzbz)
|
termin.bezeichnung = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === termin.paabgabetyp_kurzbz)
|
||||||
termin.dateStyle = this.getDateStyleClass(termin)
|
termin.dateStyle = getDateStyleClass(termin, this.notenOptions)
|
||||||
})
|
})
|
||||||
|
|
||||||
pa.betreuer = this.buildBetreuer(pa)
|
pa.betreuer = this.buildBetreuer(pa)
|
||||||
@@ -383,7 +330,7 @@ export const AbgabetoolStudent = {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Accordion :multiple="true" :activeIndex="activeTabIndex">
|
<Accordion :multiple="true" :activeIndex="activeTabIndex">
|
||||||
<template v-for="projektarbeit in projektarbeiten">
|
<template v-for="projektarbeit in projektarbeiten" :key="projektarbeit.projektarbeit_id">
|
||||||
<AccordionTab>
|
<AccordionTab>
|
||||||
|
|
||||||
<template #header>
|
<template #header>
|
||||||
@@ -409,11 +356,11 @@ export const AbgabetoolStudent = {
|
|||||||
<div class="col-4 col-md-3 fw-bold">{{$capitalize( $p.t('abgabetool/c4beurteilung') )}}</div>
|
<div class="col-4 col-md-3 fw-bold">{{$capitalize( $p.t('abgabetool/c4beurteilung') )}}</div>
|
||||||
<div class="col-8 col-md-9">
|
<div class="col-8 col-md-9">
|
||||||
<button v-if="projektarbeit.beurteilung1" @click="handleDownloadBeurteilung1(projektarbeit)" class="btn btn-primary">
|
<button v-if="projektarbeit.beurteilung1" @click="handleDownloadBeurteilung1(projektarbeit)" class="btn btn-primary">
|
||||||
<a> {{$capitalize( $p.t('abgabetool/c4downloadBeurteilungErstbetreuer') )}} <i class="fa fa-file-pdf" style="margin-left:4px; cursor: pointer;"></i></a>
|
<a> {{$capitalize( $p.t('abgabetool/c4downloadBeurteilungErstbetreuerv2') )}} <i class="fa fa-file-pdf" style="margin-left:4px; cursor: pointer;"></i></a>
|
||||||
</button>
|
</button>
|
||||||
<a v-else>{{$capitalize( $p.t('abgabetool/c4nobeurteilungVorhanden') )}}</a>
|
<a v-else>{{$capitalize( $p.t('abgabetool/c4nobeurteilungVorhanden') )}}</a>
|
||||||
<button v-if="projektarbeit.beurteilung2" @click="handleDownloadBeurteilung2(projektarbeit)" class="btn btn-primary" style="margin-left: 4px;">
|
<button v-if="projektarbeit.beurteilung2" @click="handleDownloadBeurteilung2(projektarbeit)" class="btn btn-primary" style="margin-left: 4px;">
|
||||||
<a> {{$capitalize( $p.t('abgabetool/c4downloadBeurteilungZweitbetreuer') )}} <i class="fa fa-file-pdf" style="margin-left:4px; cursor: pointer;"></i></a>
|
<a> {{$capitalize( $p.t('abgabetool/c4downloadBeurteilungZweitbetreuerv2') )}} <i class="fa fa-file-pdf" style="margin-left:4px; cursor: pointer;"></i></a>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -432,13 +379,13 @@ export const AbgabetoolStudent = {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-2">
|
<div class="row mt-2">
|
||||||
<div class="col-4 col-md-3 fw-bold">{{ projektarbeit?.betreuerart_kurzbz ? $capitalize( $p.t('abgabetool/c4betrart' + projektarbeit.betreuerart_kurzbz) ) : $capitalize( $p.t('abgabetool/c4betreuer') ) }}</div>
|
<div class="col-4 col-md-3 fw-bold">{{ projektarbeit?.betreuerart_kurzbz ? $capitalize( $p.t('abgabetool/c4betrart' + projektarbeit.betreuerart_kurzbz) ) : $capitalize( $p.t('abgabetool/c4betreuerv2') ) }}</div>
|
||||||
<div class="col-8 col-md-9">
|
<div class="col-8 col-md-9">
|
||||||
{{ projektarbeit.betreuerart_kurzbz ? projektarbeit.betreuer : '' }}
|
{{ projektarbeit.betreuerart_kurzbz ? projektarbeit.betreuer : '' }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-2">
|
<div class="row mt-2">
|
||||||
<div class="col-4 col-md-3 fw-bold">{{$capitalize( $p.t('abgabetool/c4betreuerEmailKontakt') )}}</div>
|
<div class="col-4 col-md-3 fw-bold">{{$capitalize( $p.t('abgabetool/c4betreuerEmailKontaktv2') )}}</div>
|
||||||
<div class="col-8 col-md-9">
|
<div class="col-8 col-md-9">
|
||||||
<a :href="getMailLink(projektarbeit)"><i class="fa fa-envelope" style="color:#00649C"></i></a>
|
<a :href="getMailLink(projektarbeit)"><i class="fa fa-envelope" style="color:#00649C"></i></a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ export const DeadlineOverview = {
|
|||||||
layout: 'fitColumns',
|
layout: 'fitColumns',
|
||||||
placeholder: Vue.computed(() => this.$p.t('global/noDataAvailable')),
|
placeholder: Vue.computed(() => this.$p.t('global/noDataAvailable')),
|
||||||
columns: [
|
columns: [
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zieldatum'))), field: 'datum', formatter: this.centeredTextFormatter, widthGrow: 1, tooltip: false},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zieldatumv2'))), field: 'datum', formatter: this.centeredTextFormatter, widthGrow: 1, tooltip: false},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4fixterminv4'))), field: 'fixterminstring', formatter: this.centeredTextFormatter, widthGrow: 1, tooltip: false},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4fixterminv4'))), field: 'fixterminstring', formatter: this.centeredTextFormatter, widthGrow: 1, tooltip: false},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4abgabetyp'))), field: 'typ_bezeichnung', formatter: this.centeredTextFormatter, widthGrow: 1},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4abgabetyp'))), field: 'typ_bezeichnung', formatter: this.centeredTextFormatter, widthGrow: 1},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4abgabekurzbz'))), field: 'kurzbz', formatter: this.centeredTextFormatter, widthGrow: 3},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4abgabekurzbzv2'))), field: 'kurzbz', formatter: this.centeredTextFormatter, widthGrow: 3},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('person/studentIn'))), field: 'student', formatter: this.centeredTextFormatter, widthGrow: 2},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('person/studentIn'))), field: 'student', formatter: this.centeredTextFormatter, widthGrow: 2},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4stg'))), field: 'stg', formatter: this.centeredTextFormatter,widthGrow: 1},
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4stg'))), field: 'stg', formatter: this.centeredTextFormatter,widthGrow: 1},
|
||||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4sem'))), field: 'semester', formatter: this.centeredTextFormatter, widthGrow: 1}
|
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4sem'))), field: 'semester', formatter: this.centeredTextFormatter, widthGrow: 1}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
const zone = 'Europe/Vienna';
|
||||||
|
const today = luxon.DateTime.now().setZone(zone);
|
||||||
|
|
||||||
|
export function getDateStyleClass(termin, notenOptions) {
|
||||||
|
const datum = luxon.DateTime.fromISO(termin.datum, { zone }).endOf('day');
|
||||||
|
const abgabedatum = termin.abgabedatum ? luxon.DateTime.fromISO(termin.abgabedatum, { zone }) : null;
|
||||||
|
termin.diffindays = datum.diff(today, 'days').days;
|
||||||
|
const isLate = abgabedatum && abgabedatum > datum;
|
||||||
|
|
||||||
|
// GRADE STATUS
|
||||||
|
if (termin.note) {
|
||||||
|
const opt = typeof termin.note === 'object' ? termin.note : notenOptions.find(nopt => nopt.note == termin.note)
|
||||||
|
if (opt?.positiv === true) return 'bestanden';
|
||||||
|
else if (opt?.positiv === false) return 'nichtbestanden';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACTION REQUIRED FOR GRADE
|
||||||
|
if (termin.bezeichnung?.benotbar && datum <= today) {
|
||||||
|
return 'beurteilungerforderlich';
|
||||||
|
}
|
||||||
|
|
||||||
|
// SUBMISSION STATUS
|
||||||
|
if (termin.upload_allowed) {
|
||||||
|
if (termin.abgabedatum) {
|
||||||
|
return isLate ? 'verspaetet' : 'abgegeben';
|
||||||
|
}
|
||||||
|
|
||||||
|
// no submission yet
|
||||||
|
if (datum < today) return 'verpasst';
|
||||||
|
if (termin.diffindays <= 12) return 'abzugeben';
|
||||||
|
return 'standard';
|
||||||
|
}
|
||||||
|
|
||||||
|
// GENERIC STATUS
|
||||||
|
return datum < today ? 'verpasst' : 'standard';
|
||||||
|
}
|
||||||
@@ -363,6 +363,21 @@ if($result = $db->db_query("SELECT 1 FROM public.tbl_vorlage WHERE vorlage_kurzb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($result = $db->db_query("SELECT 1 FROM public.tbl_vorlage WHERE vorlage_kurzbz = 'PAANoSigAssSM'"))
|
||||||
|
{
|
||||||
|
if($db->db_num_rows($result) === 0)
|
||||||
|
{
|
||||||
|
$qry = "INSERT INTO public.tbl_vorlage (vorlage_kurzbz, bezeichnung, anmerkung, mimetype)
|
||||||
|
VALUES ('PAANoSigAssSM', 'PAANoSigAssSM', null, 'text/html')
|
||||||
|
ON CONFLICT (vorlage_kurzbz) DO NOTHING;";
|
||||||
|
|
||||||
|
if(!$db->db_query($qry))
|
||||||
|
echo '<strong>system.tbl_vorlage: '.$db->db_last_error().'</strong><br>';
|
||||||
|
else
|
||||||
|
echo "<br>system.tbl_vorlage PAANoSigAssSM hinzugefuegt";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if($result = $db->db_query("SELECT 1 FROM public.tbl_vorlage WHERE vorlage_kurzbz = 'QualGateNegativ'"))
|
if($result = $db->db_query("SELECT 1 FROM public.tbl_vorlage WHERE vorlage_kurzbz = 'QualGateNegativ'"))
|
||||||
{
|
{
|
||||||
if($db->db_num_rows($result) === 0)
|
if($db->db_num_rows($result) === 0)
|
||||||
|
|||||||
+246
-46
@@ -10872,7 +10872,7 @@ Any unusual occurrences
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'First Assessor',
|
'text' => 'First Reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -11512,7 +11512,7 @@ Any unusual occurrences
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'The second assessor’s assessment has been submitted and is part of the final grade.',
|
'text' => 'The second reviwer’s assessment has been submitted and is part of the final grade.',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -11692,7 +11692,7 @@ Any unusual occurrences
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Second Assessor',
|
'text' => 'Second Reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -11712,7 +11712,7 @@ Any unusual occurrences
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Assessor',
|
'text' => 'Reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -12092,7 +12092,7 @@ Any unusual occurrences
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'To assessment of second assessor',
|
'text' => 'To assessment of second reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -12112,7 +12112,7 @@ Any unusual occurrences
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'The assessment of the second assessor is not available yet.',
|
'text' => 'The assessment of the second reviewer is not available yet.',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -12192,7 +12192,7 @@ Any unusual occurrences
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Send token to Second Assessor',
|
'text' => 'Send token to Second Reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -12310,7 +12310,7 @@ Any unusual occurrences
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Sending only possible after completion of assessment by Second Assessor',
|
'text' => 'Sending only possible after completion of assessment by Second Reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -12550,7 +12550,7 @@ Any unusual occurrences
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Assessor grade',
|
'text' => 'Reviewer grade',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -12570,7 +12570,7 @@ Any unusual occurrences
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Assessor',
|
'text' => 'Reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -12610,7 +12610,7 @@ Any unusual occurrences
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Assessor type',
|
'text' => 'Reviewer type',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -12630,7 +12630,7 @@ Any unusual occurrences
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'secondary assessor',
|
'text' => 'secondary reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -43420,7 +43420,7 @@ array(
|
|||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'abgabetool',
|
'category' => 'abgabetool',
|
||||||
'phrase' => 'c4betreuer',
|
'phrase' => 'c4betreuerv2',
|
||||||
'insertvon' => 'system',
|
'insertvon' => 'system',
|
||||||
'phrases' => array(
|
'phrases' => array(
|
||||||
array(
|
array(
|
||||||
@@ -43431,7 +43431,7 @@ array(
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => "Assessor",
|
'text' => "Reviewer",
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -43700,7 +43700,7 @@ array(
|
|||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'abgabetool',
|
'category' => 'abgabetool',
|
||||||
'phrase' => 'c4zieldatum',
|
'phrase' => 'c4zieldatumv2',
|
||||||
'insertvon' => 'system',
|
'insertvon' => 'system',
|
||||||
'phrases' => array(
|
'phrases' => array(
|
||||||
array(
|
array(
|
||||||
@@ -43711,7 +43711,7 @@ array(
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => "Target date",
|
'text' => "Deadline",
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -43740,7 +43740,7 @@ array(
|
|||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'abgabetool',
|
'category' => 'abgabetool',
|
||||||
'phrase' => 'c4abgabekurzbz',
|
'phrase' => 'c4abgabekurzbzv2',
|
||||||
'insertvon' => 'system',
|
'insertvon' => 'system',
|
||||||
'phrases' => array(
|
'phrases' => array(
|
||||||
array(
|
array(
|
||||||
@@ -43751,7 +43751,7 @@ array(
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => "Short description of the submitted file",
|
'text' => "Short description of the submitted document",
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -44240,7 +44240,7 @@ array(
|
|||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'abgabetool',
|
'category' => 'abgabetool',
|
||||||
'phrase' => 'c4betreuerart',
|
'phrase' => 'c4betreuerartv2',
|
||||||
'insertvon' => 'system',
|
'insertvon' => 'system',
|
||||||
'phrases' => array(
|
'phrases' => array(
|
||||||
array(
|
array(
|
||||||
@@ -44251,7 +44251,7 @@ array(
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Assessor type',
|
'text' => 'Reviewer type',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -45016,7 +45016,7 @@ array(
|
|||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'abgabetool',
|
'category' => 'abgabetool',
|
||||||
'phrase' => 'c4betreuerEmailKontakt',
|
'phrase' => 'c4betreuerEmailKontaktv2',
|
||||||
'insertvon' => 'system',
|
'insertvon' => 'system',
|
||||||
'phrases' => array(
|
'phrases' => array(
|
||||||
array(
|
array(
|
||||||
@@ -45027,7 +45027,7 @@ array(
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Email Assessor',
|
'text' => 'Email Reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -45056,7 +45056,7 @@ array(
|
|||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'abgabetool',
|
'category' => 'abgabetool',
|
||||||
'phrase' => 'c4downloadBeurteilungErstbetreuer',
|
'phrase' => 'c4downloadBeurteilungErstbetreuerv2',
|
||||||
'insertvon' => 'system',
|
'insertvon' => 'system',
|
||||||
'phrases' => array(
|
'phrases' => array(
|
||||||
array(
|
array(
|
||||||
@@ -45067,7 +45067,7 @@ array(
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Download evaluation of first assesor',
|
'text' => 'Download evaluation of first reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -45076,7 +45076,7 @@ array(
|
|||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'abgabetool',
|
'category' => 'abgabetool',
|
||||||
'phrase' => 'c4downloadBeurteilungZweitbetreuer',
|
'phrase' => 'c4downloadBeurteilungZweitbetreuerv2',
|
||||||
'insertvon' => 'system',
|
'insertvon' => 'system',
|
||||||
'phrases' => array(
|
'phrases' => array(
|
||||||
array(
|
array(
|
||||||
@@ -45087,7 +45087,7 @@ array(
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Download evaluation of second assesor',
|
'text' => 'Download evaluation of second reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -45511,7 +45511,7 @@ array(
|
|||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'abgabetool',
|
'category' => 'abgabetool',
|
||||||
'phrase' => 'c4erstbetreuer',
|
'phrase' => 'c4erstbetreuerv2',
|
||||||
'insertvon' => 'system',
|
'insertvon' => 'system',
|
||||||
'phrases' => array(
|
'phrases' => array(
|
||||||
array(
|
array(
|
||||||
@@ -45522,7 +45522,7 @@ array(
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'First Assessor',
|
'text' => 'First Reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -45531,7 +45531,7 @@ array(
|
|||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'abgabetool',
|
'category' => 'abgabetool',
|
||||||
'phrase' => 'c4zweitbetreuer',
|
'phrase' => 'c4zweitbetreuerv2',
|
||||||
'insertvon' => 'system',
|
'insertvon' => 'system',
|
||||||
'phrases' => array(
|
'phrases' => array(
|
||||||
array(
|
array(
|
||||||
@@ -45542,7 +45542,7 @@ array(
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Second Assessor',
|
'text' => 'Second Reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -45611,7 +45611,7 @@ array(
|
|||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'abgabetool',
|
'category' => 'abgabetool',
|
||||||
'phrase' => 'c4sendEmailBetreuerv2',
|
'phrase' => 'c4sendEmailBetreuerv3',
|
||||||
'insertvon' => 'system',
|
'insertvon' => 'system',
|
||||||
'phrases' => array(
|
'phrases' => array(
|
||||||
array(
|
array(
|
||||||
@@ -45622,7 +45622,7 @@ array(
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Send Email to {0} assessors',
|
'text' => 'Send Email to {0} reviewers',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -45711,7 +45711,7 @@ array(
|
|||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'abgabetool',
|
'category' => 'abgabetool',
|
||||||
'phrase' => 'c4fehlerMailBegutachter',
|
'phrase' => 'c4fehlerMailBegutachterv2',
|
||||||
'insertvon' => 'system',
|
'insertvon' => 'system',
|
||||||
'phrases' => array(
|
'phrases' => array(
|
||||||
array(
|
array(
|
||||||
@@ -45722,7 +45722,7 @@ array(
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Error sending E-Mail to first Assessor!',
|
'text' => 'Error sending E-Mail to first Reviewer!',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -45731,7 +45731,7 @@ array(
|
|||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
'category' => 'abgabetool',
|
'category' => 'abgabetool',
|
||||||
'phrase' => 'c4fehlerMailZweitBegutachter',
|
'phrase' => 'c4fehlerMailZweitBegutachterv2',
|
||||||
'insertvon' => 'system',
|
'insertvon' => 'system',
|
||||||
'phrases' => array(
|
'phrases' => array(
|
||||||
array(
|
array(
|
||||||
@@ -45742,7 +45742,7 @@ array(
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Error sending E-Mail to second Assessor!',
|
'text' => 'Error sending E-Mail to second Reviewer!',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -46434,6 +46434,206 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'abgabetool',
|
||||||
|
'phrase' => 'c4deadlineExceeded',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Nicht rechtzeitig abgegeben!',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Deadline exceeded!',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'abgabetool',
|
||||||
|
'phrase' => 'c4missingSignatureNotification',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'Abgabetool: Fehlende Signatur bei Endupload',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Submission tool: Missing signature at final upload',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'abgabetool',
|
||||||
|
'phrase' => 'c4erstbetreuerTitelPre',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'ErstbetreuerIn Titel Pre',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'First Reviewer Title Pre',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'abgabetool',
|
||||||
|
'phrase' => 'c4erstbetreuerVorname',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'ErstbetreuerIn Vorname',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'First Reviewer First Name',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'abgabetool',
|
||||||
|
'phrase' => 'c4erstbetreuerNachname',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'ErstbetreuerIn Nachname',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'First Reviewer Last Name',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'abgabetool',
|
||||||
|
'phrase' => 'c4erstbetreuerTitelPost',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'ErstbetreuerIn Titel Post',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'First Reviewer Title Post',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'abgabetool',
|
||||||
|
'phrase' => 'c4zweitbetreuerTitelPre',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'ZweitbetreuerIn Titel Pre',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Second Reviewer Title Pre',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'abgabetool',
|
||||||
|
'phrase' => 'c4zweitbetreuerVorname',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'ZweitbetreuerIn Vorname',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Second Reviewer First Name',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'abgabetool',
|
||||||
|
'phrase' => 'c4zweitbetreuerNachname',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'ZweitbetreuerIn Nachname',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Second Reviewer Last Name',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'app' => 'core',
|
||||||
|
'category' => 'abgabetool',
|
||||||
|
'phrase' => 'c4zweitbetreuerTitelPost',
|
||||||
|
'insertvon' => 'system',
|
||||||
|
'phrases' => array(
|
||||||
|
array(
|
||||||
|
'sprache' => 'German',
|
||||||
|
'text' => 'ZweitbetreuerIn Titel Post',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'sprache' => 'English',
|
||||||
|
'text' => 'Second Reviewer Title Post',
|
||||||
|
'description' => '',
|
||||||
|
'insertvon' => 'system'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
// ABGABETOOL PHRASEN END
|
// ABGABETOOL PHRASEN END
|
||||||
array(
|
array(
|
||||||
'app' => 'core',
|
'app' => 'core',
|
||||||
@@ -54242,7 +54442,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'assessor',
|
'text' => 'reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -54262,7 +54462,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Assessor',
|
'text' => 'Reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -54282,7 +54482,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'assessor type',
|
'text' => 'reviewer type',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -54482,7 +54682,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Deleting not possible, assessors were already assigned to this projekt work',
|
'text' => 'Deleting not possible, reviewers were already assigned to this projekt work',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -54522,7 +54722,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Invalid project assessors',
|
'text' => 'Invalid project reviewers',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -54542,7 +54742,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Deleting not possible, project assessor has a contract already',
|
'text' => 'Deleting not possible, project reviewer has a contract already',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -54982,7 +55182,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Edit assessor',
|
'text' => 'Edit reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -55002,7 +55202,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'Save assessor',
|
'text' => 'Save reviewer',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -55142,7 +55342,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'project assessor short name',
|
'text' => 'project reviewer short name',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
@@ -55182,7 +55382,7 @@ I have been informed that I am under no obligation to consent to the transmissio
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'sprache' => 'English',
|
'sprache' => 'English',
|
||||||
'text' => 'This project assessor is already assigned',
|
'text' => 'This project reviewer is already assigned',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'insertvon' => 'system'
|
'insertvon' => 'system'
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user