Compare commits

..

55 Commits

Author SHA1 Message Date
Johann Hoffmann 8bf18b0d37 prüfung/notenimport enabled per config + adapt notenliste.xls.php highlighting as per config; check if higher antritt prüfungen exist when editing existing ones (changing grades is not allowed, dates inbetween the other 2 antritt termine allowed); prüfungstermine with entschuldigt note are being preserved since the whole series can have 1max; frozen columns selection dropdown + 4 columns freezable (selector, uid, vorname, nachname) -> setting saved in local storage; better mirroring of new pruefung studenten dropdown with actual table sort + update the label there to be uid + name aswell; fixed the note_vorschlag select input cell; table sort doesnt clear selection; show the NotenlisteLinks.js in import modal and filter it based on LE selection; 2026-06-26 12:52:18 +02:00
Johann Hoffmann 79c0f31ddc lv_id & sem_kurzbz are now optional route params; defer loading of LehreinheitenModule.js; fetch orgform for lva dropdown; added selected|filtered|total count to table; dropdowns have responsive labels now; left aligned columns with text; AuthInfo Api for uid; tableLayout without WidthGrow, fixed rowHeight at 30 for now; selectAll + filter interaction fixed; load studiensemester similar to abgabetool; clearFilter fix on LE deselect; modal formatting & loading toggle; 2026-06-23 18:59:51 +02:00
Johann Hoffmann 49d61342c2 lv_id & sem_kurzbz are now optional route params; defer loading of LehreinheitenModule.js; fetch orgform for lva dropdown; added selected|filtered|total count to table; dropdowns have responsive labels now; left aligned columns with text; AuthInfo Api for uid; tableLayout without WidthGrow, fixed rowHeight at 30 for now; selectAll + filter interaction fixed; load studiensemester similar to abgabetool; clearFilter fix on LE deselect; modal formatting & loading toggle; 2026-06-23 18:59:36 +02:00
Johann Hoffmann be0943f1ba Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4 2026-06-22 18:12:29 +02:00
Andreas Österreicher 0a232828c8 Merge branch 'feature-71619/Notenspiegel_ECTS_Summe' 2026-06-19 08:31:08 +02:00
Paolo 07bb0ddffd Merge branch 'master' into feature-71619/Notenspiegel_ECTS_Summe 2026-06-11 12:52:41 +02:00
Harald Bamberger c56064d189 js import add missing file extension 2026-05-06 18:36:05 +02:00
Paolo 1fe742d9c7 Merge branch 'master' into feature-71619/Notenspiegel_ECTS_Summe 2026-04-27 12:59:37 +02:00
Harald Bamberger f6747713a1 Merge branch 'master' into feature-60873/GesamtnoteneingabeCis4 2026-04-20 11:13:14 +02:00
Paolo 8487694b37 Merge branch 'master' into feature-71619/Notenspiegel_ECTS_Summe 2026-03-24 09:48:08 +01:00
Paolo c0b685c77b - ECTS Summe zugeteilt (Should be 30) only applied courses (not grey)
- ECTS Summe gewichtet (current Sum)
2026-03-10 16:50:23 +01:00
Paolo a4b1104abf Added a the new column "ECTS Summe" for the "Notenspiegel erweitert" XLS export 2026-03-10 12:12:30 +01:00
Johann Hoffmann 5a6d20f817 fixed scrollOffset discrepancy issue by UNDEFINING rowHeight and thus let tabulator detect the actual size of a row itself and not trust a sligthly wrong rowHeight value; define centered formatter in js/tabulator/formatter/centered and use it on unformatted columns for consistency; 2026-03-05 15:47:19 +01:00
Johann Hoffmann ce5da22180 import error msg phrase; config for noten which dount count towards prüfungsantritt & load & use that; 2026-03-03 16:47:17 +01:00
Johann Hoffmann 05b2c3c42b persistence of order/width/visibility of notenTable predefined columns before applying dynamic pruefung columns in setupData and other table col manipulating routines; fix antrittCalculation reactivity and resulting selection eligibility and the reactivity of that also; added custom sorter for pruefung cols; some minor UX tweaks; 2026-03-03 12:05:05 +01:00
Johann Hoffmann 2dd732b924 Merge branch 'master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	application/views/CisRouterView/CisRouterView.php
2026-02-27 10:38:24 +01:00
Johann Hoffmann 110f73e622 WIP notentool table persistence with dyn cols 2026-02-17 14:09:57 +01:00
Johann Hoffmann bb0d118284 Pruefung Termin1/original Note column fixed logic, variable column creation reserved for termin2/termin3 mixtures; remove redundant termintypen from student row in table if backend sent a succesful update response since their cant be multiple termin2/termin3; block notenvorschlag übernehmen once a pruefung exists; 2026-02-13 12:25:55 +01:00
Johann Hoffmann 701ccadff3 approveGrades phrase with counter; computed maxAntritte by config settings; block illegal pruefung creation in frontend (backend was already working); 2026-02-12 13:56:56 +01:00
Johann Hoffmann 34555504df Notenfreigabe email template insert in dbupdate3.4; notenvorschlag übernehmen cellHandler adjustment; WIP testing pruefungen & pruefungs config 2026-02-11 10:20:53 +01:00
Johann Hoffmann d1f5220925 notenschluessel availability check; WIP lvgesamtnote model method that actually fetches the notenvorschlag without join over zeugnisnote; 2026-02-10 12:57:38 +01:00
Johann Hoffmann decd514b22 WIP improving notenimport with punktefeature 2026-02-09 09:50:22 +01:00
Johann Hoffmann 6cf7093293 testing/implementing more config flags; dont select on certain cols; certain cols only available with certain flags; 2026-02-05 16:49:26 +01:00
Johann Hoffmann 17f11fa871 Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	application/controllers/api/frontend/v1/Lehre.php
#	application/models/education/Lehrveranstaltung_model.php
#	application/models/education/Note_model.php
#	application/views/CisRouterView/CisRouterView.php
#	public/js/api/factory/studiensemester.js
#	public/js/components/Bootstrap/Offcanvas.js
#	public/js/components/Overlay/FhcOverlay.js
2026-02-04 15:40:34 +01:00
Johann Hoffmann 96812868a4 WIP 2026-02-04 10:31:20 +01:00
Johann Hoffmann 40c79158f7 punkte feature basically finished; WIP testing & import rewrite 2026-02-03 17:38:33 +01:00
Johann Hoffmann 054663ee00 WIP punkte 2026-02-03 11:34:59 +01:00
Johann Hoffmann 81eee814e9 yellow dropdown styling only on editable tabulator colums for note_vorschlag; fetch note for punkte for notenvorschlag and pruefungsnote if certain config is set; added debounce helper file/function; WIP persisting punkte in backend 2026-02-02 17:07:16 +01:00
Johann Hoffmann 390a3c0d5a Notenschluessel Model + WIP making sense of legacy config flags 2026-01-29 14:50:37 +01:00
Johann Hoffmann 695dd655c0 WIP implementing getNotenvorschlagStudent, currently only works for whole lva/sem 2026-01-26 14:47:21 +01:00
Johann Hoffmann 957da460a6 shorter passwort freigabe text; loadCisConfig for Benotungstool via api; anw% in notentable via event; WIP incorporating CIS config into actual noten logic; 2025-12-18 15:24:08 +01:00
Johann Hoffmann 2cee36d7b5 try/catch around moodle event & proper error message 2025-12-17 14:29:05 +01:00
Johann Hoffmann 43925e3088 custom sticky css; offcanvas mobility legende; mobiltiy zusatz in seperate column; added getMobilityZusatzForUids & formatZusatz similar to digital anw mobility zusaetze; 2025-11-25 17:23:49 +01:00
Johann Hoffmann 1c236cce02 Merge branch 'master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	application/config/routes.php
#	application/models/crm/Prestudent_model.php
#	application/models/education/Lehreinheit_model.php
#	application/models/education/Lehrveranstaltung_model.php
#	public/js/apps/Dashboard/Fhc.js
#	system/phrasesupdate.php
2025-11-25 10:50:26 +01:00
Johann Hoffmann 4956a517ca loading overlay, notenfreigabe available phrase with counter; placeholder phrasen & date format; WIP handling moodle API errors well 2025-11-25 10:31:52 +01:00
Johann Hoffmann a6167583a3 hinweistexte import/freigabe; distinct css for editable table cols notenvorschlag & freigabe; trigger 'getEntschuldigungsStatusForStudentOnDate' event when saving a pruefungstermin -> if akzeptierte entschuldigung is found for student on pruefungsdate it is automatically set to entschuldigt; fix event unmount lifecycle; 2025-10-07 16:26:58 +02:00
Johann Hoffmann 1e68eb0b90 berechtigungsprüfung 'lehre/benotungstool:rw' in Noten Controller; API Method documentation; removed addMeta statements; removed Tabulator Event logging; 2025-08-21 13:35:38 +02:00
Johann Hoffmann 332efd4106 selection fixes; phrases; preserve scroll after redraw when pressing action buttons on far right; take nav offset into account for width calculation; 2025-08-20 16:25:21 +02:00
Johann Hoffmann f303191c54 alternative email per cis global config 2025-08-19 16:33:12 +02:00
Johann Hoffmann d6c7f16ceb Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4 2025-08-19 13:43:23 +02:00
Johann Hoffmann f1912fe739 custom selection handling due to bugged tab5 rowSelect, should work as intended now, WIP illegal emails 2025-08-19 13:32:18 +02:00
Johann Hoffmann 2f7fe05d21 mobility legende; TopCalc Row (sum, negative, prueflinge); fix row selection issues; 2025-08-18 11:25:38 +02:00
Johann Hoffmann ee4b61f549 recommit branch; event naming changes; 2025-08-18 09:13:14 +02:00
Johann Hoffmann 511a4256bc moved LE loading and infoString setup into LehreinheitenModule, which can be bound to an instance of Primevue3 Dropdown via v-bind. WIP modularizing other common selections like LVA & Semester Kurzbz; 2025-08-08 13:35:01 +02:00
Johann Hoffmann 3c9db86df2 Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4
# Conflicts:
#	application/config/routes.php
#	application/controllers/api/frontend/v1/Lehre.php
#	application/models/education/Lehrveranstaltung_model.php
#	application/views/CisRouterView/CisRouterView.php
#	public/js/apps/Dashboard/Fhc.js
#	system/phrasesupdate.php
2025-08-07 15:17:10 +02:00
Johann Hoffmann 367204a1ee removed legacy classes (except mobility) and moved crud functionality to LePruefungModel, LVgesamtnoteModel & LehrveranstaltungModel; 2025-08-07 14:54:41 +02:00
Johann Hoffmann bbe55a75ea noten/pruefungen import; import validation for nr of antritte and date/antritt chronological order; 2025-08-04 14:27:33 +02:00
Johann Hoffmann e58bf3a8cf WIP noten/pruefung import 2025-08-01 09:48:46 +02:00
Johann Hoffmann 1f2f866c61 positiv/negativ/unbenotet filter; reload lva/le correctly on sem/lva dropdown selection change; 2025-07-30 17:10:43 +02:00
Johann Hoffmann 6ccbc95697 createPruefung entry with "noch nicht eingetragen" note as default for a selection of students at given date; filterHeaders in noten cols; antrittCount col; pruefungsformatter with antritt highlighting; 2025-07-29 17:32:07 +02:00
Johann Hoffmann 52d9e0a195 pruefungen columns pro datum nicht pro termintyp; row Selection & modal newPruefungForSelectedStudents mit linked multiselect dropdown; WIP antritte berechnen 2025-07-25 12:50:51 +02:00
Johann Hoffmann 6a3982347b teilnoten/punkte berechnen für notenvorschläge; vorschläge übernehmen; noten freigabe mit passwort; prüfungen generisch anzeigen nach datum gruppiert; prüfungen anlegen/bearbeiten auf mapping termin1/2/3 möglich. 2025-07-24 17:02:57 +02:00
Johann Hoffmann fe7feeb74e Merge remote-tracking branch 'origin/master' into feature-60873/GesamtnoteneingabeCis4 2025-07-15 09:51:11 +02:00
Johann Hoffmann 658fe79ad7 load Teilnoten via moodle Event trigger; WIP further noten/punkte logic; 2025-05-12 16:45:33 +02:00
Johann Hoffmann 5bbf05ac8a WIP Gesamtnoteneingabe Notenberechnung endpoint 2025-05-12 12:50:56 +02:00
63 changed files with 6797 additions and 1322 deletions
-7
View File
@@ -170,13 +170,6 @@ $config['navigation_header'] = array(
'expand' => true,
'sort' => 51,
'requiredPermissions' => 'vertrag/mitarbeiter:r'
),
'studierendenverwaltung' => array(
'link' => site_url('studentenverwaltung'),
'description' => 'Studierendenverwaltung',
'expand' => true,
'sort' => 52,
'requiredPermissions' => ['admin:r', 'assistenz:r']
)
)
),
+17
View File
@@ -0,0 +1,17 @@
<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');
// 'entschuldigt' & 'noch nicht eingetragen' -> wirken sich nicht auf Antritte aus
$config['NOTEN_OHNE_ANTRITT'] = [9, 17]; // tbl_note pk
$config['NOTEN_OCCURANCE_LIMIT_MAP'] = [17 => 1]; // across the 4 fixed antritte only one can be entschuldigt
// tbl_note pk of the 'entschuldigt' note. An entschuldigt Termin is preserved as its own dated
// entry when a new pruefung of the same type is created (instead of being overwritten).
$config['NOTE_ENTSCHULDIGT'] = 17;
// availability of the two Benotungstool import flows. When both are true they are shown as
// separate buttons/dialogs.
$config['CIS_GESAMTNOTE_PRUEFUNGSIMPORT'] = true; // dated import that creates a pruefung per row
$config['CIS_GESAMTNOTE_NOTENIMPORT'] = false; // classic note-only import (uid + note, no date)
+1
View File
@@ -64,6 +64,7 @@ $route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1';
$route['Cis/LvPlan/.*'] = 'Cis/LvPlan/index/$1';
$route['Cis/MyLvPlan/.*'] = 'Cis/MyLvPlan/index/$1';
$route['Cis/MyLv/.*'] = 'Cis/MyLv/index/$1';
$route['Cis/Benotungstool/.*'] = 'Cis/Benotungstool/index/$1';
$route['Abgabetool/Assistenz'] = 'Cis/Abgabetool/Assistenz';
$route['Abgabetool/Assistenz/(:any)'] = 'Cis/Abgabetool/Assistenz/$1';
@@ -0,0 +1,37 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*
*/
class Benotungstool extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct([
'index' => self::PERM_LOGGED
]);
$this->_ci =& get_instance();
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @return void
*/
public function index()
{
$viewData = array(
'uid'=>getAuthUID(),
);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Benotungstool']);
}
}
@@ -18,18 +18,9 @@
if (! defined('BASEPATH')) exit('No direct script access allowed');
//require_once('../../../include/studiengang.class.php');
//require_once('../../../include/student.class.php');
//require_once('../../../include/datum.class.php');
//require_once('../../../include/mail.class.php');
//require_once('../../../include/benutzerberechtigung.class.php');
//require_once('../../../include/phrasen.class.php');
//require_once('../../../include/projektarbeit.class.php');
//require_once('../../../include/projektbetreuer.class.php');
class Lehre extends FHCAPI_Controller
{
/**
* Object initialization
*/
@@ -38,39 +29,57 @@ class Lehre extends FHCAPI_Controller
parent::__construct([
'lvStudentenMail' => self::PERM_LOGGED,
'LV' => self::PERM_LOGGED,
'Pruefungen' => self::PERM_LOGGED
'Pruefungen' => self::PERM_LOGGED,
'getZugewieseneLv' => self::PERM_LOGGED,
'getLeForLv' => self::PERM_LOGGED
]);
$this->load->library('PhrasesLib');
$this->loadPhrases(
array(
'global',
'ui',
'abgabetool'
)
);
$this->load->helper('hlp_sancho_helper');
require_once(FHCPATH . 'include/studiengang.class.php');
require_once(FHCPATH . 'include/student.class.php');
require_once(FHCPATH . 'include/projektarbeit.class.php');
require_once(FHCPATH . 'include/projektbetreuer.class.php');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
/**
* constructs the emails of the groups from a lehrveranstaltung
*/
public function lvStudentenMail()
public function lvStudentenMail()
{
$lehreinheit_id = $this->input->get("lehreinheit_id",TRUE);
// return early if the required parameter is missing
if(!isset($lehreinheit_id))
{
$this->terminateWithError('Missing required parameter', self::ERROR_TYPE_GENERAL);
}
$lehreinheit_id = $this->input->get("lehreinheit_id",TRUE);
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$studentenMails = $this->LehreinheitModel->getStudentenMail($lehreinheit_id);
// return early if the required parameter is missing
if(!isset($lehreinheit_id))
{
$this->terminateWithError('Missing required parameter', self::ERROR_TYPE_GENERAL);
}
$studentenMails = $this->getDataOrTerminateWithError($studentenMails);
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$studentenMails = $this->LehreinheitModel->getStudentenMail($lehreinheit_id);
$studentenMails = $this->getDataOrTerminateWithError($studentenMails);
//convert array of objects into array of strings
$studentenMails = array_map(function($element){
return $element->mail;
}, $studentenMails);
$this->terminateWithSuccess($studentenMails);
$this->terminateWithSuccess($studentenMails);
}
public function LV($studiensemester_kurzbz, $lehrveranstaltung_id)
@@ -80,13 +89,13 @@ class Lehre extends FHCAPI_Controller
$result = $this->LehrveranstaltungModel->getLvsByStudentWithGrades(getAuthUID(), $studiensemester_kurzbz, getUserLanguage(), $lehrveranstaltung_id);
$result = current($this->getDataOrTerminateWithError($result));
$this->terminateWithSuccess($result);
}
/**
* fetches all Pruefungen of a student for a specific lehrveranstaltung
* if the student passed the Pruefung on the first attempt, no information about the Pruefungen is stored in the database
* if the student passed the Pruefung on the first attempt, no information about the Pruefungen is stored in the database
* @param mixed $lehrveranstaltung_id
* @return void
*/
@@ -100,5 +109,46 @@ class Lehre extends FHCAPI_Controller
$this->terminateWithSuccess($result);
}
}
/**
* fetches all assigned lehrveranstaltungen of a mitarbeiter for a given semester
* @param mixed $uid
* @param mixed $sem_kurzbz
* @return void
*/
public function getZugewieseneLv() {
$uid = $this->input->get("uid",TRUE);
$sem_kurzbz = $this->input->get("sem_kurzbz",TRUE);
// TODO: error messages
if(!isset($sem_kurzbz) || isEmptyString($sem_kurzbz))
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
if (!isset($uid) || isEmptyString($uid))
$uid = getAuthUID();
// querying other ma_uids data requires admin permission
if($uid !== getAuthUID()) {
$this->load->library('PermissionLib');
$isAdmin = $this->permissionlib->isBerechtigt('admin');
if(!$isAdmin) $this->terminateWithError($this->p->t('ui', 'keineBerechtigung'), 'general');
}
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$result = $this->LehrveranstaltungModel->getLvForLektorInSemester($sem_kurzbz, $uid);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function getLeForLv() {
$lv_id = $this->input->get("lv_id",TRUE);
$sem_kurzbz = $this->input->get("sem_kurzbz",TRUE);
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
// $this->terminateWithSuccess($this->LehreinheitModel->getLesForLv($lv_id, $sem_kurzbz));
$this->terminateWithSuccess($this->LehreinheitModel->getAllLehreinheitenForLvaAndMaUid($lv_id, getAuthUID(), $sem_kurzbz));
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,67 @@
<?php
/**
* Copyright (C) 2024 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
class Studiensemester extends FHCAPI_Controller
{
private $_ci;
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getStudiensemester'=> self::PERM_LOGGED,
]);
$this->_ci =& get_instance();
$this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
* GET METHOD
* returns List of all studiensemester as well as current one
*/
public function getStudiensemester()
{
$this->_ci->StudiensemesterModel->addOrder("start", "DESC");
$result = $this->_ci->StudiensemesterModel->load();
$studiensemester = getData($result);
$result = $this->_ci->StudiensemesterModel->getAkt();
$aktuell = getData($result);
$this->terminateWithSuccess(array($studiensemester, $aktuell));
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
}
@@ -9,10 +9,9 @@ class Detailheader extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getHeader' => self::PERM_LOGGED,
'getPersonAbteilung' => self::PERM_LOGGED,
'getLeitungOrg' => self::PERM_LOGGED,
'getSemesterStati' => self::PERM_LOGGED,
'getHeader' => ['vertrag/mitarbeiter:r'],
'getPersonAbteilung' => ['vertrag/mitarbeiter:r'],
'getLeitungOrg' => ['vertrag/mitarbeiter:r'],
]);
}
@@ -49,17 +48,6 @@ class Detailheader extends FHCAPI_Controller
$this->terminateWithSuccess(current($data));
}
public function getSemesterStati($prestudent_id)
{
$this->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel');
$result = $this->PrestudentstatusModel->getAllPrestudentstatiWithStudiensemester($prestudent_id);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
}
@@ -246,12 +246,12 @@ class Abschlusspruefung extends FHCAPI_Controller
{
$searchString = $this->input->get('searchString') ?? '';
$this->load->model('person/Person_model', 'PersonModel');
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$result = $this->PersonModel->searchPerson($searchString, 'mitMaUid');
$result = $this->MitarbeiterModel->searchMitarbeiter($searchString, 'ohneMaUid');
if (isError($result)) {
$this->terminateWithError($result, self::ERROR_TYPE_GENERAL);
$this->terminateWithError($result, self::ERROR_TYPE_GENERAL);
}
$this->terminateWithSuccess($result ?: []);
@@ -90,15 +90,6 @@ class Projektarbeit extends FHCAPI_Controller
if (!isset($projektarbeit_id) || !is_numeric($projektarbeit_id)) return $this->terminateWithError('Projektarbeit Id missing', self::ERROR_TYPE_GENERAL);
$result = $this->fetchProjektarbeitByID($projektarbeit_id);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(current($data));
}
private function fetchProjektarbeitById($projektarbeit_id) {
$this->ProjektarbeitModel->resetQuery();
$this->ProjektarbeitModel->addSelect(
'lehre.tbl_projektarbeit.projektarbeit_id, titel, titel_english, themenbereich, projekttyp_kurzbz, lehrveranstaltung_id, lehreinheit_id,
firma_id, beginn, ende, gesperrtbis, note, final, freigegeben, tbl_projektarbeit.anmerkung, fa.name AS firma_name'
@@ -106,10 +97,13 @@ class Projektarbeit extends FHCAPI_Controller
$this->ProjektarbeitModel->addJoin('lehre.tbl_lehreinheit le', 'lehreinheit_id');
$this->ProjektarbeitModel->addJoin('lehre.tbl_lehrveranstaltung lv', 'lehrveranstaltung_id');
$this->ProjektarbeitModel->addJoin('public.tbl_firma fa', 'firma_id', 'LEFT');
return $this->ProjektarbeitModel->loadWhere(
$result = $this->ProjektarbeitModel->loadWhere(
array('projektarbeit_id' => $projektarbeit_id)
);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(current($data));
}
/**
@@ -138,8 +132,7 @@ class Projektarbeit extends FHCAPI_Controller
);
$data = $this->getDataOrTerminateWithError($result);
$data = $this->getDataOrTerminateWithError($this->fetchProjektarbeitById($data));
$this->terminateWithSuccess($data);
}
@@ -144,7 +144,6 @@ class Student extends FHCAPI_Controller
. $this->PrestudentModel->escape($studiensemester_kurzbz)
. ") AS statusofsemester"
);
$this->PrestudentModel->addSelect($this->PrestudentModel->escape($studiensemester_kurzbz) . ' as query_studiensemester_kurzbz');
$this->PrestudentModel->addJoin('public.tbl_student s', 'prestudent_id', 'LEFT');
$this->PrestudentModel->addJoin('public.tbl_benutzer b', 'student_uid = uid', 'LEFT');
@@ -439,8 +439,6 @@ class Students extends FHCAPI_Controller
$this->studentlistlib->addSelect("'' AS verband");
$this->studentlistlib->addSelect("'' AS gruppe");
$query_studiensemester_kurzbz = $studiensemester_kurzbz ? $this->PrestudentModel->escape($studiensemester_kurzbz) : '\'NULL\'';
$this->studentlistlib->addSelect($query_studiensemester_kurzbz . ' as query_studiensemester_kurzbz');
$this->addFilter($studiensemester_kurzbz);
@@ -558,7 +556,6 @@ class Students extends FHCAPI_Controller
$this->studentlistlib->addJoin("public.tbl_student s", "prestudent_id");
$this->studentlistlib->addSelect("'' AS priorisierung_relativ");
$this->studentlistlib->addSelect($this->PrestudentModel->escape($studiensemester_kurzbz) . ' as query_studiensemester_kurzbz');
if ($gruppe_kurzbz !== null) {
$this->studentlistlib->addJoin('public.tbl_benutzergruppe g', 'uid', '', 'after_b');
@@ -758,7 +755,6 @@ class Students extends FHCAPI_Controller
END
) AS semester", false);
$this->studentlistlib->addSelect($this->PrestudentModel->escape($studiensemester_kurzbz) . ' as query_studiensemester_kurzbz');
if ($prestudent_ids && $student_uids) {
$this->studentlistlib->addWhere('tbl_prestudent.prestudent_id', $prestudent_ids);
+3 -15
View File
@@ -198,19 +198,7 @@ class Gradelist extends Auth_Controller
if (!isset($row_noten->found))
{
$result_lv = $this->LehrveranstaltungModel->load($row_noten->lehrveranstaltung_id);
$studiengang_kz = null;
if (!empty($result_lv->retval) && isset($result_lv->retval[0]) && isset($result_lv->retval[0]->studiengang_kz))
{
$result_stg = $this->StudiengangModel->load($result_lv->retval[0]->studiengang_kz);
if (!empty($result_stg->retval) && isset($result_stg->retval[0]) && is_object($result_stg->retval[0]) && isset($result_stg->retval[0]->kurzbzlang))
{
$studiengang_kz = $result_stg->retval[0]->kurzbzlang;
}
}
$result_stg = $this->StudiengangModel->load($result_lv->retval[0]->studiengang_kz);
$courses['semester'][$row_noten->studiensemester_kurzbz]['lvs_nonstpl'][] = array(
'lehrveranstaltung_id' => $row_noten->lehrveranstaltung_id,
'lehrtyp_kurzbz' => $result_lv->retval[0]->lehrtyp_kurzbz,
@@ -224,8 +212,8 @@ class Gradelist extends Auth_Controller
'semester' => $result_lv->retval[0]->semester,
'note' => $row_noten->note,
'datum' => $row_noten->benotungsdatum,
'studiengang_kurzbz' => $studiengang_kz,
'zugeordnet' => true
'zugeordnet' => true,
'studiengang_kurzbz' => $result_stg->retval[0]->kurzbzlang
);
if(!isset($courses['semester'][$row_noten->studiensemester_kurzbz]['data']['ectssumme_nonstpl']))
$courses['semester'][$row_noten->studiensemester_kurzbz]['data']['ectssumme_nonstpl'] = 0;
@@ -72,13 +72,6 @@ class StudentListLib
$this->addSelect('pls.status_kurzbz AS status');
$this->addSelect('pls.datum AS status_datum');
$this->addSelect('pls.bestaetigtam AS status_bestaetigung');
$this->addSelect("
CASE
WHEN pls.status_kurzbz = 'Interessent'
THEN pls.ausbildungssemester
ELSE s.semester
END AS semester_berechnet
");
$this->addSelect(
"(SELECT kontakt FROM public.tbl_kontakt WHERE kontakttyp='email' AND person_id=p.person_id AND zustellung LIMIT 1) AS mail_privat",
false
@@ -11,4 +11,73 @@ class Mobilitaet_model extends DB_Model
$this->dbTable = 'bis.tbl_mobilitaet';
$this->pk = 'mobilitaet_id';
}
public function getMobilityZusatzForUids($uids) {
$qry = "SELECT distinct on(nachname, vorname, public.tbl_benutzer.person_id) uid,
tbl_mitarbeiter.mitarbeiter_uid,
tbl_note.lkt_ueberschreibbar, tbl_note.anmerkung,
tbl_mobilitaet.mobilitaetstyp_kurzbz,
(CASE WHEN bis.tbl_mobilitaet.studiensemester_kurzbz = vw_student_lehrveranstaltung.studiensemester_kurzbz THEN 1 ELSE 0 END) as doubledegree,
public.tbl_prestudent.gsstudientyp_kurzbz as ddtype,
(SELECT status_kurzbz FROM public.tbl_prestudentstatus
WHERE prestudent_id=tbl_student.prestudent_id
ORDER BY datum DESC, insertamum DESC, ext_id DESC LIMIT 1) as studienstatus
FROM
campus.vw_student_lehrveranstaltung
JOIN public.tbl_benutzer USING(uid)
JOIN public.tbl_person USING(person_id)
LEFT JOIN public.tbl_student ON(uid=student_uid)
LEFT JOIN public.tbl_mitarbeiter ON(uid=mitarbeiter_uid)
LEFT JOIN public.tbl_studentlehrverband USING(student_uid,studiensemester_kurzbz)
LEFT JOIN lehre.tbl_zeugnisnote on(vw_student_lehrveranstaltung.lehrveranstaltung_id=tbl_zeugnisnote.lehrveranstaltung_id
AND tbl_zeugnisnote.student_uid=tbl_student.student_uid
AND tbl_zeugnisnote.studiensemester_kurzbz=tbl_studentlehrverband.studiensemester_kurzbz)
LEFT JOIN lehre.tbl_note USING (note)
LEFT JOIN bis.tbl_bisio ON(uid=tbl_bisio.student_uid)
LEFT JOIN public.tbl_studiengang ON(tbl_student.studiengang_kz=tbl_studiengang.studiengang_kz)
LEFT JOIN bis.tbl_mobilitaet USING(prestudent_id)
LEFT JOIN public.tbl_prestudent USING(prestudent_id)
WHERE uid IN ?";
return $this->execReadOnlyQuery($qry, [$uids]);
}
public function formatZusatz($entry, $erhalter_kz) {
$zusatz = '';
if (isset($entry->studienstatus) && $entry->studienstatus === 'Incoming') {
$zusatz = '(i)';
}
if (isset($entry->lkt_ueberschreibbar) && $entry->lkt_ueberschreibbar === false) {
$zusatz .= ' (' . ($entry->anmerkung ?? '') . ')';
}
if (isset($entry->mitarbeiter_uid) && $entry->mitarbeiter_uid !== null) {
$zusatz .= ' (ma)';
}
if (isset($entry->stg_kz_student) && $entry->stg_kz_student == $erhalter_kz) {
$zusatz .= ' (a.o.)';
}
if (
isset($entry->mobilitaetstyp_kurzbz) && $entry->mobilitaetstyp_kurzbz &&
isset($entry->doubledegree) && $entry->doubledegree === 1
) {
$zusatz .= ' (d.d.';
$ddtype = $entry->ddtype ?? null;
if ($ddtype == 'Intern') {
$zusatz .= 'i.)';
} elseif ($ddtype == 'Extern') {
$zusatz .= 'o.)';
} else {
$zusatz .= ')';
}
}
return $zusatz;
}
}
@@ -52,4 +52,53 @@ class LePruefung_model extends DB_Model
'student_uid' => $student_uid
]);
}
public function getPruefungenByLvStudiensemester($lv_id, $sem_kurzbz) {
$qry = "SELECT lehre.tbl_pruefung.*, tbl_lehrveranstaltung.bezeichnung as lehrveranstaltung_bezeichnung, tbl_lehrveranstaltung.lehrveranstaltung_id,
tbl_note.bezeichnung as note_bezeichnung, tbl_pruefungstyp.beschreibung as typ_beschreibung, tbl_lehreinheit.studiensemester_kurzbz as studiensemester_kurzbz
FROM lehre.tbl_pruefung, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, lehre.tbl_note, lehre.tbl_pruefungstyp
WHERE lehre.tbl_pruefung.lehreinheit_id=tbl_lehreinheit.lehreinheit_id
AND tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id
AND lehre.tbl_pruefung.note = tbl_note.note
AND lehre.tbl_pruefung.pruefungstyp_kurzbz=tbl_pruefungstyp.pruefungstyp_kurzbz
AND tbl_lehrveranstaltung.lehrveranstaltung_id = ?
AND tbl_lehreinheit.studiensemester_kurzbz = ?
ORDER BY datum DESC;";
return $this->execReadOnlyQuery($qry, array($lv_id, $sem_kurzbz));
}
public function getPruefungenByUidTypLvStudiensemester($uid, $typ = null, $lv_id = null, $sem_kurzbz = null) {
$params = [$uid];
$qry = "SELECT tbl_pruefung.*, tbl_lehrveranstaltung.bezeichnung as lehrveranstaltung_bezeichnung, tbl_lehrveranstaltung.lehrveranstaltung_id,
tbl_note.bezeichnung as note_bezeichnung, tbl_pruefungstyp.beschreibung as typ_beschreibung, tbl_lehreinheit.studiensemester_kurzbz as studiensemester_kurzbz
FROM lehre.tbl_pruefung, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, lehre.tbl_note, lehre.tbl_pruefungstyp
WHERE student_uid= ?
AND tbl_pruefung.lehreinheit_id=tbl_lehreinheit.lehreinheit_id
AND tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id
AND tbl_pruefung.note = tbl_note.note
AND tbl_pruefung.pruefungstyp_kurzbz=tbl_pruefungstyp.pruefungstyp_kurzbz";
if ($typ != null)
{
$qry .= " AND tbl_pruefungstyp.pruefungstyp_kurzbz = ?";
$params[] = $typ;
}
if ($lv_id != null)
{
$qry .= " AND tbl_lehrveranstaltung.lehrveranstaltung_id = ?";
$params[] = $lv_id;
}
if ($sem_kurzbz != null)
{
$qry .= " AND tbl_lehreinheit.studiensemester_kurzbz = ?";
$params[] = $sem_kurzbz;
}
$qry .= " ORDER BY datum DESC";
return $this->execReadOnlyQuery($qry, $params);
}
}
@@ -739,4 +739,26 @@ EOSQL;
)";
}
public function getAllLehreinheitenForLvaAndMaUid($lva_id, $ma_uid, $sem_kurzbz)
{
$query = "SELECT DISTINCT tbl_lehreinheitmitarbeiter.lehreinheit_id, tbl_lehreinheit.lehrveranstaltung_id, tbl_lehreinheit.lehrform_kurzbz,
tbl_lehreinheitmitarbeiter.mitarbeiter_uid,
tbl_lehreinheitgruppe.semester,
tbl_lehreinheitgruppe.verband,
tbl_lehreinheitgruppe.gruppe,
tbl_lehreinheitgruppe.gruppe_kurzbz,
tbl_lehrveranstaltung.kurzbz,
tbl_studiengang.kurzbzlang,
(SELECT COUNT(DISTINCT datum) FROM campus.vw_stundenplan WHERE lehreinheit_id = lehre.tbl_lehreinheit.lehreinheit_id) as termincount,
(SELECT COUNT(*) FROM campus.vw_student_lehrveranstaltung WHERE lehreinheit_id = lehre.tbl_lehreinheit.lehreinheit_id) as studentcount
FROM lehre.tbl_lehreinheit JOIN lehre.tbl_lehreinheitmitarbeiter USING(lehreinheit_id)
JOIN lehre.tbl_lehreinheitgruppe USING(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang ON (tbl_lehreinheitgruppe.studiengang_kz = tbl_studiengang.studiengang_kz)
WHERE lehrveranstaltung_id = ? AND studiensemester_kurzbz = ? AND mitarbeiter_uid = ?
ORDER BY tbl_lehreinheitgruppe.gruppe_kurzbz";
return $this->execQuery($query, [$lva_id, $sem_kurzbz, $ma_uid]);
}
}
@@ -317,7 +317,7 @@ class Lehrveranstaltung_model extends DB_Model
tbl_bisio.bisio_id, tbl_bisio.von, tbl_bisio.bis, tbl_student.studiengang_kz AS stg_kz_student,
tbl_zeugnisnote.note, tbl_mitarbeiter.mitarbeiter_uid, tbl_person.matr_nr, tbl_benutzer.uid,
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as kuerzel, tbl_studiengang.orgform_kurzbz, vw_student_lehrveranstaltung.semester, vw_student_lehrveranstaltung.studiensemester_kurzbz, vw_student_lehrveranstaltung.bezeichnung,
tbl_student.prestudent_id
tbl_student.prestudent_id, campus.vw_student_lehrveranstaltung.lehreinheit_id
FROM
campus.vw_student_lehrveranstaltung
JOIN public.tbl_benutzer USING(uid)
@@ -1346,4 +1346,27 @@ class Lehrveranstaltung_model extends DB_Model
return $this->execQuery($qry, $params);
}
public function getLvForLektorInSemester($sem_kurzbz, $uid) {
$qry = "SELECT DISTINCT (tbl_lehrveranstaltung.lehrveranstaltung_id),
UPPER(tbl_studiengang.typ::varchar(1) || tbl_studiengang.kurzbz) as stg_kurzbz,
tbl_studiengang.studiengang_kz,
tbl_lehrveranstaltung.semester as lv_semester,
tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung,
(SELECT kurzbz FROM public.tbl_mitarbeiter
WHERE mitarbeiter_uid=tbl_lehreinheitmitarbeiter.mitarbeiter_uid) as lektor,
lehre.tbl_lehrveranstaltung.orgform_kurzbz as orgform
FROM
lehre.tbl_lehreinheit JOIN lehre.tbl_lehreinheitmitarbeiter USING(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang USING(studiengang_kz)
JOIN lehre.tbl_lehrveranstaltung as lehrfach ON(tbl_lehreinheit.lehrfach_id=lehrfach.lehrveranstaltung_id)
WHERE
tbl_lehreinheit.studiensemester_kurzbz = ?
AND mitarbeiter_uid = ?
ORDER BY stg_kurzbz,lv_semester,lv_bezeichnung";
return $this->execReadOnlyQuery($qry, array($sem_kurzbz, $uid));
}
}
@@ -14,7 +14,7 @@ class Lvgesamtnote_model extends DB_Model
}
/**
* Laedt die Noten
* Laedt die Noten - lvgesamtnote (Vorschlag) JOIN tbl.note (zeugnisnote)
*
* @param integer $lehrveranstaltung_id
* @param string $student_uid
@@ -46,4 +46,19 @@ class Lvgesamtnote_model extends DB_Model
return $this->loadWhere($where);
}
public function getLvGesamtNoteVorschlag($lehrveranstaltung_id, $student_uid, $studiensemester_kurzbz)
{
$qry = "SELECT * FROM campus.tbl_lvgesamtnote
WHERE campus.tbl_lvgesamtnote.student_uid = ?
AND campus.tbl_lvgesamtnote.studiensemester_kurzbz = ?";
$params = [$student_uid, $studiensemester_kurzbz];
if ($lehrveranstaltung_id) {
$qry .= "AND campus.tbl_lvgesamtnote.lehrveranstaltung_id = ?";
$params[] = $lehrveranstaltung_id;
}
return $this->execReadOnlyQuery($qry, $params);
}
}
+22 -1
View File
@@ -11,12 +11,33 @@ class Note_model extends DB_Model
$this->dbTable = 'lehre.tbl_note';
$this->pk = 'note';
}
public function getAllActive() {
$qry ="SELECT *
FROM lehre.tbl_note
WHERE aktiv = true";
return $this->execReadOnlyQuery($qry);
}
// used to determine the primary key of note "entschuldigt" to avoid hardcoded magic numbers
// that might differ in a different installation of fhcomplete
public function getEntschuldigtNote() {
$qry ="SELECT *
FROM lehre.tbl_note
WHERE bezeichnung = 'entschuldigt'";
return $this->execReadOnlyQuery($qry);
}
// used to determine the primary key of note "noch nicht eingetragen" to avoid hardcoded magic numbers
// that might differ in a different installation of fhcomplete
public function getNochNichtEingetragenNote() {
$qry ="SELECT *
FROM lehre.tbl_note
WHERE bezeichnung = 'Noch nicht eingetragen'";
return $this->execReadOnlyQuery($qry);
}
}
@@ -26,6 +26,9 @@ class Notenschluesselaufteilung_model extends DB_Model
$this->load->model('education/Notenschluesselzuordnung_model', 'NotenschluesselzuordnungModel');
$notenschluessel_kurzbz = $this->NotenschluesselzuordnungModel->getKurzbzForLv($lehrveranstaltung_id, $studiensemester_kurzbz);
if($notenschluessel_kurzbz == null)
return success(null);
$this->addSelect("note");
$this->addOrder("punkte", "DESC");
$this->addLimit(1);
@@ -306,4 +306,5 @@ class Pruefung_model extends DB_Model
return $this->loadWhereCommitteeExamsFailed();
}
}
@@ -59,6 +59,37 @@ class Studienplan_model extends DB_Model
'tbl_studienplan_lehrveranstaltung.semester' => $semester
));
}
public function getStudienplanByLvaSemKurzbz($lehrveranstaltung_id, $studiensemester_kurzbz) {
$qry= "
SELECT
DISTINCT tbl_studienplan.*
FROM
lehre.tbl_studienplan
JOIN lehre.tbl_studienplan_lehrveranstaltung
USING(studienplan_id)
WHERE
tbl_studienplan_lehrveranstaltung.lehrveranstaltung_id IN (
SELECT
lv.lehrveranstaltung_id
FROM
lehre.tbl_lehrveranstaltung AS lv
LEFT JOIN lehre.tbl_lehrveranstaltung AS t ON t.lehrveranstaltung_id=lv.lehrveranstaltung_template_id
WHERE
lv.lehrtyp_kurzbz<>'tpl'
AND (lv.lehrveranstaltung_id= ? OR (lv.lehrveranstaltung_template_id= ? AND t.lehrtyp_kurzbz='tpl'))
)
AND EXISTS (
SELECT 1
FROM
lehre.tbl_studienplan_semester
WHERE studienplan_id=tbl_studienplan.studienplan_id
AND studiensemester_kurzbz= ?
AND semester = tbl_studienplan_lehrveranstaltung.semester)
ORDER BY bezeichnung";
return $this->execReadOnlyQuery($qry, array($lehrveranstaltung_id, $lehrveranstaltung_id, $studiensemester_kurzbz));
}
public function getStudienplanLehrveranstaltungForPrestudent($studienplan_id, $semester, $prestudent_id)
{
+1 -21
View File
@@ -149,7 +149,7 @@ class Person_model extends DB_Model
* @param $filter Term to search for.
* @return DB-result
*/
public function searchPerson($filter, $mode=null)
public function searchPerson($filter)
{
$this->addSelect('vorname, nachname, gebdatum, person_id, titelpre, titelpost');
$this->addSelect("CASE
@@ -161,26 +161,6 @@ class Person_model extends DB_Model
THEN 'Student'
ELSE 'Person'
END AS status");
if($mode == 'mitMaUid')
{
$this->addSelect("(
SELECT m.mitarbeiter_uid
FROM public.tbl_benutzer b
JOIN public.tbl_mitarbeiter m
ON b.uid = m.mitarbeiter_uid
WHERE b.person_id = tbl_person.person_id
LIMIT 1
)
AS uid");
$this->addOrder('uid, lower(nachname), lower(vorname)');
}
else
{
$this->addOrder('lower(nachname), lower(vorname)');
}
$result = $this->loadWhere(
'lower(nachname) like '.$this->db->escape('%'.mb_strtolower($filter).'%')."
OR lower(vorname) like ".$this->db->escape('%'.$filter.'%')."
@@ -11,6 +11,7 @@ $includesArray = array(
'skipID' => '#fhccontent',
'vuedatepicker11' => true,
'customCSSs' => array(
'vendor/vuejs/vuedatepicker_css/main.css',
'public/css/components/verticalsplit.css',
'public/css/components/searchbar/searchbar.css',
'public/css/Fhc.css',
@@ -24,6 +25,7 @@ $includesArray = array(
'public/css/components/abgabetool/abgabe.css',
'public/css/Cis4/Cms.css',
'public/css/Cis4/Studium.css',
'public/css/Cis4/Benotungstool.css'
),
'customJSs' => array(
'vendor/npm-asset/primevue/accordion/accordion.min.js',
@@ -35,11 +37,15 @@ $includesArray = array(
'vendor/npm-asset/primevue/timeline/timeline.min.js',
'vendor/npm-asset/primevue/inplace/inplace.min.js',
'vendor/npm-asset/primevue/message/message.min.js',
'vendor/npm-asset/primevue/divider/divider.min.js',
'vendor/npm-asset/primevue/password/password.js',
'vendor/npm-asset/primevue/multiselect/multiselect.js',
'vendor/npm-asset/primevue/tieredmenu/tieredmenu.js',
'vendor/moment/luxonjs/luxon.min.js'
),
'customJSModules' => array(
'public/js/apps/Dashboard/Fhc.js',
'vendor/olifolkerd/tabulator5/src/js/modules/ColumnCalcs/ColumnCalcs.js'
),
);
+47 -14
View File
@@ -143,6 +143,39 @@ else
// let's merge
$format_title->setAlign('merge');
// Importkonfiguration aus dem CI-Config (application/config/noten.php) lesen, damit nur die
// Spalten des jeweils aktiven Importtyps hervorgehoben (grau) werden.
$CIS_GESAMTNOTE_NOTENIMPORT = false;
$CIS_GESAMTNOTE_PRUEFUNGSIMPORT = true;
$notenConfigFile = __DIR__.'/../../../application/config/noten.php';
if(is_file($notenConfigFile))
{
if(!defined('BASEPATH')) define('BASEPATH', true);
$config = array();
include($notenConfigFile);
if(isset($config['CIS_GESAMTNOTE_NOTENIMPORT'])) $CIS_GESAMTNOTE_NOTENIMPORT = $config['CIS_GESAMTNOTE_NOTENIMPORT'];
if(isset($config['CIS_GESAMTNOTE_PRUEFUNGSIMPORT'])) $CIS_GESAMTNOTE_PRUEFUNGSIMPORT = $config['CIS_GESAMTNOTE_PRUEFUNGSIMPORT'];
unset($config);
}
// plain (non-highlighted) variants used when an import type is disabled
$format_plain =& $workbook->addFormat();
$format_plain->setAlign('left');
$format_plain->setNumFormat(49);
$format_plainright =& $workbook->addFormat();
$format_plainright->setAlign('right');
$format_plainright->setNumFormat(49);
// highlight only the cells belonging to an enabled import flow:
// - Notenimport -> Personenkennzeichen + Note (main block)
// - Prüfungsimport -> Personenkennzeichen + Datum + Note (Termin2/Termin3 blocks)
$fmtNoteImportPkz = $CIS_GESAMTNOTE_NOTENIMPORT ? $format_highlight : $format_plain;
$fmtNoteImportNote = $CIS_GESAMTNOTE_NOTENIMPORT ? $format_highlightright : $format_plainright;
$fmtPruefImportPkz = $CIS_GESAMTNOTE_PRUEFUNGSIMPORT ? $format_highlight : $format_plain;
$fmtPruefImportDate = $CIS_GESAMTNOTE_PRUEFUNGSIMPORT ? $format_highlightright_date : $format_plainright;
$fmtPruefImportNote = $CIS_GESAMTNOTE_PRUEFUNGSIMPORT ? $format_highlightright : $format_plainright;
$lvobj = new lehrveranstaltung($lvid);
$worksheet->write(0,0,$p->t('anwesenheitsliste/notenliste')." ".($sprache=='English'?$lvobj->bezeichnung_english:$lvobj->bezeichnung),$format_bold);
@@ -338,13 +371,13 @@ else
$worksheet->write($lines,3,$elem->vorname);
}
$worksheet->write($lines,4,$elem->semester.$elem->verband.$elem->gruppe);
$worksheet->write($lines,5,trim($elem->matrikelnr),$format_highlight);
$worksheet->write($lines,6, $note, $format_highlightright);
$worksheet->write($lines,5,trim($elem->matrikelnr),$fmtNoteImportPkz);
$worksheet->write($lines,6, $note, $fmtNoteImportNote);
// Nachprüfung
if (defined('CIS_GESAMTNOTE_PRUEFUNG_TERMIN2') && CIS_GESAMTNOTE_PRUEFUNG_TERMIN2)
{
$worksheet->write($lines,8, trim($elem->matrikelnr), $format_highlight);
$worksheet->write($lines,8, trim($elem->matrikelnr), $fmtPruefImportPkz);
$pr = new Pruefung();
$pr->getPruefungen($elem->uid, "Termin2", $lvid, $stsem);
$output2 = $pr->result;
@@ -352,23 +385,23 @@ else
if ($output2)
{
$resultPr = $output2[0];
$worksheet->write($lines,9, date('d.m.Y', strtotime($resultPr->datum)), $format_highlightright_date);
$worksheet->write($lines,9, date('d.m.Y', strtotime($resultPr->datum)), $fmtPruefImportDate);
if(defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE==true)
$worksheet->write($lines,10, $resultPr->punkte, $format_highlightright);
$worksheet->write($lines,10, $resultPr->punkte, $fmtPruefImportNote);
else
$worksheet->write($lines,10, $resultPr->note, $format_highlightright);
$worksheet->write($lines,10, $resultPr->note, $fmtPruefImportNote);
}
else
{
$worksheet->write($lines,9, '', $format_highlightright_date);
$worksheet->write($lines,10, '', $format_highlightright);
$worksheet->write($lines,9, '', $fmtPruefImportDate);
$worksheet->write($lines,10, '', $fmtPruefImportNote);
}
}
// Nachprüfung
if (defined('CIS_GESAMTNOTE_PRUEFUNG_TERMIN3') && CIS_GESAMTNOTE_PRUEFUNG_TERMIN3)
{
$worksheet->write($lines,12, trim($elem->matrikelnr), $format_highlight);
$worksheet->write($lines,12, trim($elem->matrikelnr), $fmtPruefImportPkz);
$pr = new Pruefung();
$pr->getPruefungen($elem->uid, "Termin3", $lvid, $stsem);
$output3 = $pr->result;
@@ -376,16 +409,16 @@ else
if ($output3)
{
$resultPr = $output3[0];
$worksheet->write($lines,13, date('d.m.Y', strtotime($resultPr->datum)), $format_highlightright_date);
$worksheet->write($lines,13, date('d.m.Y', strtotime($resultPr->datum)), $fmtPruefImportDate);
if(defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE==true)
$worksheet->write($lines,14, $resultPr->punkte, $format_highlightright);
$worksheet->write($lines,14, $resultPr->punkte, $fmtPruefImportNote);
else
$worksheet->write($lines,14, $resultPr->note, $format_highlightright);
$worksheet->write($lines,14, $resultPr->note, $fmtPruefImportNote);
}
else
{
$worksheet->write($lines,13, '', $format_highlightright_date);
$worksheet->write($lines,14, '', $format_highlightright);
$worksheet->write($lines,13, '', $fmtPruefImportDate);
$worksheet->write($lines,14, '', $fmtPruefImportNote);
}
}
+63 -48
View File
@@ -95,37 +95,37 @@ foreach ($result_student as $row)
if ($uids == '')
die('Es befinden sich keine Studierende in diesem Semester');
$qry = "SELECT
lehrveranstaltung_id, bezeichnung, studiengang_kz, semester, ects
FROM
lehre.tbl_lehrveranstaltung
WHERE
lehrveranstaltung_id IN
(
SELECT
distinct lehrveranstaltung_id
FROM
campus.vw_student_lehrveranstaltung, public.tbl_studentlehrverband
WHERE
tbl_studentlehrverband.studiengang_kz=".$db->db_add_param($studiengang_kz, FHC_INTEGER)." AND
tbl_studentlehrverband.semester=".$db->db_add_param($semester, FHC_INTEGER)." AND
vw_student_lehrveranstaltung.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)." AND
uid=student_uid AND
vw_student_lehrveranstaltung.studiensemester_kurzbz=tbl_studentlehrverband.studiensemester_kurzbz
$qry = "SELECT
lehrveranstaltung_id, bezeichnung, studiengang_kz, semester, ects
FROM
lehre.tbl_lehrveranstaltung
WHERE
lehrveranstaltung_id IN (
SELECT
DISTINCT lehrveranstaltung_id
FROM
campus.vw_student_lehrveranstaltung
JOIN public.tbl_studentlehrverband ON(student_uid = uid)
WHERE
tbl_studentlehrverband.studiengang_kz = ".$db->db_add_param($studiengang_kz, FHC_INTEGER)."
AND tbl_studentlehrverband.semester = ".$db->db_add_param($semester, FHC_INTEGER)."
AND vw_student_lehrveranstaltung.studiensemester_kurzbz = ".$db->db_add_param($semester_aktuell)."
AND vw_student_lehrveranstaltung.studiensemester_kurzbz = tbl_studentlehrverband.studiensemester_kurzbz
)
AND studiengang_kz<>0
AND studiengang_kz != 0
AND zeugnis
UNION
SELECT
lehrveranstaltung_id, bezeichnung, studiengang_kz, semester, ects
FROM
lehre.tbl_lehrveranstaltung JOIN lehre.tbl_zeugnisnote USING(lehrveranstaltung_id)
WHERE
tbl_lehrveranstaltung.studiengang_kz=".$db->db_add_param($studiengang_kz, FHC_INTEGER)." AND
tbl_zeugnisnote.student_uid in($uids) AND
tbl_zeugnisnote.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)." AND
zeugnis
ORDER BY bezeichnung";
UNION
SELECT
lehrveranstaltung_id, bezeichnung, studiengang_kz, semester, ects
FROM
lehre.tbl_lehrveranstaltung
JOIN lehre.tbl_zeugnisnote USING(lehrveranstaltung_id)
WHERE
tbl_lehrveranstaltung.studiengang_kz = ".$db->db_add_param($studiengang_kz, FHC_INTEGER)."
AND tbl_zeugnisnote.student_uid IN ($uids)
AND tbl_zeugnisnote.studiensemester_kurzbz = ".$db->db_add_param($semester_aktuell)."
AND zeugnis
ORDER BY bezeichnung";
if (!$result_lva = $db->db_query($qry))
die('Fehler beim Ermitteln der Lehrveranstaltungen');
@@ -338,6 +338,10 @@ if ($typ == 'xls')
}
$anzahl_lvspalten = $spalte - 2;
$worksheet->write($zeile, ++$spalte, 'ECTS Summe zugeteilt', $format_bold);
$maxlength[$spalte] = 20;
$worksheet->write($zeile, ++$spalte, 'ECTS Summe gewichtet', $format_bold);
$maxlength[$spalte] = 20;
$worksheet->write($zeile, ++$spalte, 'Notendurchschnitt', $format_bold);
$maxlength[$spalte] = 15;
$worksheet->write($zeile, ++$spalte, "Gewichteter\nNotendurchschnitt", $format_bold_wrap);
@@ -372,9 +376,12 @@ if ($typ == 'xls')
$worksheet->write($zeile, ++$spalte, $row_student->gruppe, $format_bold_left);
$worksheet->write($zeile, ++$spalte, $row_student->matrikelnr, $format_bold);
//Alle Zeugnisnoten des Studierenden holen
// Alle Zeugnisnoten des Studierenden holen
$noten = array();
$qry = "SELECT * FROM lehre.tbl_zeugnisnote WHERE student_uid=".$db->db_add_param($row_student->uid)." AND studiensemester_kurzbz=".$db->db_add_param($semester_aktuell);
$qry = "SELECT lehrveranstaltung_id, note
FROM lehre.tbl_zeugnisnote
WHERE student_uid = ".$db->db_add_param($row_student->uid)."
AND studiensemester_kurzbz = ".$db->db_add_param($semester_aktuell);
if ($result = $db->db_query($qry))
while ($row = $db->db_fetch_object($result))
$noten[$row->lehrveranstaltung_id] = $row->note;
@@ -382,15 +389,16 @@ if ($typ == 'xls')
//Zu jeder Lehrveranstaltungsnote Prüfungstyp (Anzahl der Antritte) holen
$pruefungstypen = array();
$qry = "SELECT tbl_lehrveranstaltung.lehrveranstaltung_id, pruefungstyp_kurzbz, sort, datum
FROM
lehre.tbl_pruefung
JOIN
lehre.tbl_lehreinheit using(lehreinheit_id)
JOIN
lehre.tbl_lehrveranstaltung using(lehrveranstaltung_id)
WHERE
student_uid=".$db->db_add_param($row_student->uid)." AND studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
ORDER BY lehrveranstaltung_id, sort, datum";
FROM
lehre.tbl_pruefung
JOIN
lehre.tbl_lehreinheit USING(lehreinheit_id)
JOIN
lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
WHERE
student_uid = ".$db->db_add_param($row_student->uid)."
AND studiensemester_kurzbz = ".$db->db_add_param($semester_aktuell)."
ORDER BY lehrveranstaltung_id, sort, datum";
if ($result = $db->db_query($qry))
{
while ($row = $db->db_fetch_object($result))
@@ -399,15 +407,14 @@ if ($typ == 'xls')
}
}
//Alle LVs holen zu denen der Studierende zugeteilt ist
// Alle LVs holen zu denen der Studierende zugeteilt ist
$zugeteilte_lvs = array();
$qry = "SELECT distinct lehrveranstaltung_id
FROM
campus.vw_student_lehrveranstaltung
WHERE
uid=".$db->db_add_param($row_student->uid)." AND
studiensemester_kurzbz=".$db->db_add_param($semester_aktuell);
$qry = "SELECT DISTINCT lehrveranstaltung_id
FROM
campus.vw_student_lehrveranstaltung
WHERE
uid = ".$db->db_add_param($row_student->uid)."
AND studiensemester_kurzbz = ".$db->db_add_param($semester_aktuell);
if ($result = $db->db_query($qry))
while ($row = $db->db_fetch_object($result))
$zugeteilte_lvs[] = $row->lehrveranstaltung_id;
@@ -416,17 +423,20 @@ if ($typ == 'xls')
$summe = 0;
$rowcount = 0;
$summeects = 0;
$total_ects = 0;
$gewichtetenote = 0;
while ($rowcount < $db->db_num_rows($result_lva))
{
$row_lva = $db->db_fetch_object($result_lva, $rowcount);
$rowcount++;
//wenn es eine Note gibt
if (isset($noten[$row_lva->lehrveranstaltung_id]))
{
$note = $noten[$row_lva->lehrveranstaltung_id];
$format = 0;
$total_ects += $row_lva->ects;
//wenn für die LV der Studierende eine Nachprüfung hat (z.B. 2 Termin, kommissionelle...)
if (isset($pruefungstypen[$row_lva->lehrveranstaltung_id]))
@@ -472,6 +482,7 @@ if ($typ == 'xls')
//Keine Note fuer diese LV vorhanden
if (in_array($row_lva->lehrveranstaltung_id, $zugeteilte_lvs))
{
$total_ects += $row_lva->ects;
$worksheet->write($zeile, ++$spalte, '', $format_colored_nichteingetragen);
}
else
@@ -489,6 +500,8 @@ if ($typ == 'xls')
if ($summeects != 0)
$gewichtetenote /= $summeects;
$worksheet->write($zeile, ++$spalte, sprintf("%.2f", $total_ects), $format_number);
$worksheet->write($zeile, ++$spalte, sprintf("%.2f", $summeects), $format_number);
$worksheet->write($zeile, ++$spalte, sprintf("%.2f", $schnitt), $format_number);
$worksheet->write($zeile, ++$spalte, sprintf("%.2f", $gewichtetenote), $format_number);
if ($gewichtetenote != 0)
@@ -529,6 +542,8 @@ if ($typ == 'xls')
$schnitt = $summe_schnitt / $anzahl_schnitt;
else
$schnitt = 0;
$worksheet->write($zeile, ++$spalte, '-', $format_bold_center);
$worksheet->write($zeile, ++$spalte, '-', $format_bold_center);
$worksheet->write($zeile, ++$spalte, sprintf("%.2f", $schnitt), $format_number);
if ($anzahlgewichtet != 0)
$summegewichtet = $summegewichtet / $anzahlgewichtet;
+90
View File
@@ -0,0 +1,90 @@
/* Sticky identity columns. The four freezable columns (selection/uid/Vorname/Nachname) always carry
the .sticky-col class for the opaque background; whether a column is actually pinned is toggled
per column via a `sticky-on-<field>` class on #notentable (set in JS from the user's selection).
The cumulative left offsets (--sl-<field>) are computed in JS (recomputeStickyOffsets) so that
multiple sticky columns stack next to each other instead of overlapping at left:0. */
#notentable .tabulator-header .tabulator-col.sticky-col,
#notentable .tabulator-tableholder .tabulator-row .tabulator-cell.sticky-col {
background-color: #fff; /* opaque so scrolled cells don't show through */
}
/* keep sticky cells opaque on row hover */
#notentable .tabulator-row:hover .tabulator-cell.sticky-col {
background-color: #ccc;
}
/* enable pinning per column only when the container opts the field in.
left offset comes from the matching --sl-<field> CSS variable (set on #notentable from JS). */
#notentable.sticky-on-selectCol .sticky-col[tabulator-field="selectCol"],
#notentable.sticky-on-uid .sticky-col[tabulator-field="uid"],
#notentable.sticky-on-vorname .sticky-col[tabulator-field="vorname"],
#notentable.sticky-on-nachname .sticky-col[tabulator-field="nachname"] {
position: sticky;
z-index: 10; /* float above the scrolling pruefungs columns */
border-right: 1px solid #ddd; /* separator on the sticky block */
}
#notentable.sticky-on-selectCol .sticky-col[tabulator-field="selectCol"] { left: var(--sl-selectCol, 0); }
#notentable.sticky-on-uid .sticky-col[tabulator-field="uid"] { left: var(--sl-uid, 0); }
#notentable.sticky-on-vorname .sticky-col[tabulator-field="vorname"] { left: var(--sl-vorname, 0); }
#notentable.sticky-on-nachname .sticky-col[tabulator-field="nachname"] { left: var(--sl-nachname, 0); }
/* styling for points input column for notenvorschläge in benotungstool*/
#notentable .tabulator-tableholder .tabulator-editable[tabulator-field="punkte"] {
position: relative;
background-color: rgba(255, 255, 157, 0.73);
}
/* styling for editable dropdown column of notenvorschläge in benotungstool*/
#notentable .tabulator-tableholder .tabulator-editable[tabulator-field="note_vorschlag"] {
position: relative;
background-color: rgba(255, 255, 157, 0.73);
cursor: pointer;
}
#notentable .tabulator-tableholder .tabulator-editable[tabulator-field="note_vorschlag"]::after {
content: "▾";
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%) rotate(0deg);
transform-origin: center;
color: rgba(120, 120, 60, 0.85);
font-size: 0.9rem;
line-height: 1;
pointer-events: none;
transition: transform 0.15s ease;
}
/* rotate the indicator up while the dropdown editor is open */
#notentable .tabulator-tableholder .tabulator-cell.tabulator-editing[tabulator-field="note_vorschlag"]::after {
transform: translateY(-50%) rotate(180deg);
}
.pruefung-badge {
position: relative;
padding-left: 32px; /* badge width (20) + its left offset (6) + gap (6) */
}
.pruefung-badge::before {
content: attr(data-attempt);
position: absolute;
left: 6px;
top: 50%;
transform: translateY(-50%);
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 20px;
height: 20px;
border-radius: 50%;
color: #fff;
font-size: 0.75rem;
font-weight: bold;
}
.pruefung-badge.attempt-t1::before { background: #0072B2; }
.pruefung-badge.attempt-t2::before { background: #E69F00; }
.pruefung-badge.attempt-t3::before { background: #CC79A7; }
.pruefung-badge.attempt-k::before { background: #D55E00; }
-5
View File
@@ -2,7 +2,6 @@
@import './SvgIcons.css';
@import './components/searchbar/searchbar.css';
@import './components/verticalsplit.css';
@import './components/horizontalsplit.css';
@import './components/FilterComponent.css';
@import './components/Tabs.css';
@import './components/Notiz.css';
@@ -278,7 +277,3 @@ html.fs_huge {
}
*/
/* slim ende */
.fhc-xxl-modal {
min-width: 80vw;
}
-75
View File
@@ -1,75 +0,0 @@
:root {
--fhc-horizontalsplit-hsplitter-bg-color: var(--fhc-background, #eee);
--fhc-horizontalsplit-hsplitter-border-color: var(--fhc-border, #eee);
--fhc-horizontalsplit-hsplitter-splitactions-color: var(--fhc-dark, #000);
}
.horizontalsplit-container {
display: flex;
flex-direction: row;
overflow: hidden;
max-height: 100%;
padding: 0px;
}
.horizontalsplitted {
overflow: auto;
flex-shrink: 0;
}
.horizontalsplitter {
flex-shrink: 0;
width: 16px;
cursor: col-resize;
user-select: none;
display: flex;
align-items: center;
justify-content: center;
}
.horizontalsplitter.left {
border-left: solid 3px var(--fhc-horizontalsplit-hsplitter-border-color);
margin-right: 3px;
}
.horizontalsplitter.right {
border-right: solid 3px var(--fhc-horizontalsplit-hsplitter-border-color);
margin-left: 3px;
}
.splitactions.horizontal {
background-color: var(--fhc-horizontalsplit-hsplitter-bg-color);
color: var(--fhc-horizontalsplit-hsplitter-splitactions-color);
display: flex;
flex-direction: column;
padding: 5px 0 5px 0;
}
.splitactions.horizontal.left {
border-radius: 0 40% 40% 0;
}
.splitactions.horizontal.right {
border-radius: 40% 0 0 40%;
}
.splitactions.horizontal .splitaction {
width: 14px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
}
.splitactions.horizontal .splitaction.resize {
cursor: col-resize;
}
#content {
padding-top: 0 !important;
padding-bottom: 0 !important;
}
#content > div:first-child {
margin-top: 30px;
}
+1 -2
View File
@@ -69,7 +69,6 @@
.tag_limette {
background-color: #D3FFCE;
color: black;
}
.tag_done {
@@ -112,4 +111,4 @@
.copy-btn {
float: right;
margin-top: 3px;
}
}
-6
View File
@@ -34,10 +34,4 @@ export default {
url: 'api/frontend/v1/detailheader/detailheader/getLeitungOrg/' + oekurzbz,
};
},
getSemesterStati(prestudent_id){
return {
method: 'get',
url: 'api/frontend/v1/detailheader/detailheader/getSemesterStati/' + prestudent_id,
};
},
}
+14
View File
@@ -35,5 +35,19 @@ export default {
method: 'get',
url: `/api/frontend/v1/Lehre/Pruefungen/${lehrveranstaltung_id}`
};
},
getZugewieseneLv(uid, sem_kurzbz){
return {
method: 'get',
url: '/api/frontend/v1/Lehre/getZugewieseneLv',
params: { uid, sem_kurzbz}
};
},
getLeForLv(lv_id, sem_kurzbz) {
return {
method: 'get',
url: '/api/frontend/v1/Lehre/getLeForLv',
params: { lv_id, sem_kurzbz }
};
}
};
+87
View File
@@ -0,0 +1,87 @@
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export default {
getCisConfig(){
return {
method: 'get',
url: '/api/frontend/v1/Noten/getCisConfig'
};
},
getStudentenNoten(lv_id, sem_kurzbz) {
return {
method: 'get',
url: '/api/frontend/v1/Noten/getStudentenNoten',
params: { lv_id, sem_kurzbz }
};
},
getNoten(){
return {
method: 'get',
url: '/api/frontend/v1/Noten/getNoten'
};
},
saveStudentenNoten(password, noten, lv_id, sem_kurzbz) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveStudentenNoten',
params: { password, noten, lv_id, sem_kurzbz }
};
},
saveNotenvorschlag(lv_id, sem_kurzbz, student_uid, note, punkte = null) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveNotenvorschlag',
params: { lv_id, sem_kurzbz, student_uid, note, punkte }
};
},
saveStudentPruefung(student_uid, note, punkte, datum, lva_id, lehreinheit_id, sem_kurzbz, typ, pruefung_id = null){
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveStudentPruefung',
params: { student_uid, note, punkte, datum, lva_id, lehreinheit_id, sem_kurzbz, typ, pruefung_id }
};
},
createPruefungen(uids, datum, lva_id, sem_kurzbz){
return {
method: 'post',
url: '/api/frontend/v1/Noten/createPruefungen',
params: { uids, datum, lva_id, sem_kurzbz }
};
},
saveNotenvorschlagBulk(lv_id, sem_kurzbz, noten) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/saveNotenvorschlagBulk',
params: { lv_id, sem_kurzbz, noten }
};
},
saveStudentPruefungBulk(lv_id, sem_kurzbz, pruefungen) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/savePruefungenBulk',
params: { lv_id, sem_kurzbz, pruefungen }
};
},
getNoteByPunkte(punkte, lv_id, sem_kurzbz) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/getNoteByPunkte',
params: { punkte, lv_id, sem_kurzbz }
};
}
}
+7 -1
View File
@@ -29,5 +29,11 @@ export default {
url: 'api/frontend/v1/organisation/studiensemester/getAll',
params: { order, start }
};
}
},
getStudiensemester() {
return {
method: 'get',
url: '/api/frontend/v1/Studiensemester/getStudiensemester'
};
},
};
+3
View File
@@ -35,6 +35,9 @@ export default {
getMitarbeiter(searchString){
return this.$fhcApi.get('api/frontend/v1/stv/abschlusspruefung/getMitarbeiter/' + searchString);
},
getPruefer(searchString){
return this.$fhcApi.get('api/frontend/v1/stv/abschlusspruefung/getPruefer/' + searchString);
},
getNoten(){
return this.$fhcApi.get('api/frontend/v1/stv/abschlusspruefung/getNoten/');
},
+8 -1
View File
@@ -17,6 +17,7 @@ import AbgabetoolMitarbeiter from "../../components/Cis/Abgabetool/AbgabetoolMit
import AbgabetoolAssistenz from "../../components/Cis/Abgabetool/AbgabetoolAssistenz.js";
import DeadlineOverview from "../../components/Cis/Abgabetool/DeadlineOverview.js";
import Studium from "../../components/Cis/Studium/Studium.js";
import Benotungstool from "../../components/Cis/Benotungstool/Benotungstool.js";
import ApiRenderers from '../../api/factory/renderers.js';
import ApiRouteInfo from '../../api/factory/routeinfo.js';
@@ -75,6 +76,12 @@ const router = VueRouter.createRouter({
component: Raumsuche,
props: true
},
{
path: `/Cis/Benotungstool/:lv_id?/:sem_kurzbz?`,
name: 'Benotungstool',
component: Benotungstool,
props: true
},
// Redirect old links to new format
{
path: "/CisVue/Cms/getRoomInformation/:ort_kurzbz",
@@ -150,7 +157,7 @@ const router = VueRouter.createRouter({
path: `/Cis/MyLv/:studiensemester?`,
name: 'MyLv',
component: MylvStudent,
props: true,
props: true
},
{
path: `/Cis/MyLv/Info/:studien_semester/:lehrveranstaltung_id`,
+1 -1
View File
@@ -148,4 +148,4 @@ export default {
</div>
</div>
`
}
}
@@ -530,41 +530,20 @@ export const AbgabetoolAssistenz = {
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();
// setup change eventlisteners
const events = [
"columnMoved", "columnResized", "columnVisibilityChanged",
"filterChanged", "headerFilterChanged", "dataSorted",
"columnSorted", "sortersChanged"
];
events.forEach(eventName => {
table.on(eventName, () => this.saveState(table));
});
table.on("renderComplete", () => {
if(!this.stateRestored) {
@@ -1007,7 +986,6 @@ export const AbgabetoolAssistenz = {
this.tableData = this.mapProjekteToTableData(this.projektarbeiten)
await this.tableBuiltPromise
this.$refs.abgabeTable.tabulator.setData(this.tableData);
},
loadProjektarbeiten(all = false, callback) {
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,73 @@
// components/Lehre/Benotungstool/NotenlisteLinks.js
import LehreinheitenModule from '../../DropdownModes/LehreinheitenModule.js';
export const NotenlisteLinks = {
name: "NotenlisteLinks",
props: {
lehrveranstaltung: { type: Object, default: null },
sem_kurzbz: { type: String, default: null },
selectedLehreinheit: { type: Object, default: null }
},
computed: {
LehreinheitenModule() {
return LehreinheitenModule;
},
lehreinheiten() {
// reuse the already-loaded LE options from the shared module
const all = LehreinheitenModule.options ?? [];
// when a single Lehreinheit is selected, restrict the list to just that one
if (this.selectedLehreinheit?.lehreinheit_id != null) {
return all.filter(le => le.lehreinheit_id === this.selectedLehreinheit.lehreinheit_id);
}
return all;
},
baseUrl() {
return FHC_JS_DATA_STORAGE_OBJECT.app_root
+ 'cis/private/lehre/notenliste.xls.php';
},
ready() {
const lv = this.lehrveranstaltung;
return !!(lv && lv.studiengang_kz != null && lv.lv_semester != null
&& lv.lehrveranstaltung_id != null && this.sem_kurzbz);
},
gesamtUrl() {
if (!this.ready) return null;
return this.buildUrl();
}
},
methods: {
buildUrl(lehreinheit_id = null) {
const lv = this.lehrveranstaltung;
const params = new URLSearchParams({
stg: lv.studiengang_kz,
sem: lv.lv_semester,
lvid: lv.lehrveranstaltung_id,
stsem: this.sem_kurzbz
});
if (lehreinheit_id != null) params.set('lehreinheit_id', lehreinheit_id);
return this.baseUrl + '?' + params.toString();
}
},
template: `
<div v-if="ready">
<div class="fw-bold mb-2">{{ $capitalize($p.t('benotungstool/c4notenlisten')) }}</div>
<div class="mb-1">
<a class="Item" :href="gesamtUrl" target="_blank" rel="noopener">
{{ $capitalize($p.t('benotungstool/c4gesamtliste')) }} {{ lehrveranstaltung.lv_bezeichnung }}
</a>
</div>
<div v-for="le in lehreinheiten" :key="le.lehreinheit_id" class="mb-1" style="padding-left: 1.5rem;">
<a class="Item" :href="buildUrl(le.lehreinheit_id)" target="_blank" rel="noopener">
{{ le.infoString }}
</a>
</div>
</div>
<div v-else class="text-muted">
{{ $capitalize($p.t('benotungstool/c4keineStudentenGefunden')) }}
</div>
`
};
export default NotenlisteLinks;
+82 -216
View File
@@ -1,13 +1,11 @@
import ApiDetailHeader from "../../api/factory/detailHeader.js";
import ApiHandleFoto from "../../api/factory/fotoHandling.js";
import ModalUploadFoto from "./Modal/UploadFoto.js";
import PvSkeleton from "../../../../index.ci.php/public/js/components/primevue/skeleton/skeleton.esm.min.js";
export default {
name: 'DetailHeader',
components: {
ModalUploadFoto,
PvSkeleton
ModalUploadFoto
},
props: {
headerData: {
@@ -40,14 +38,6 @@ export default {
'mitarbeiter',
].includes(value)
}
},
currentSemester: {
type: String,
default: ''
},
isLoading: { //if true, then parent isLoading
type: Boolean,
default: false
}
},
computed: {
@@ -70,7 +60,8 @@ export default {
},
hasTileUIDSlot() {
return !!this.$slots.uid
}
},
},
created(){
if (this.typeHeader === 'student') {
@@ -80,7 +71,7 @@ export default {
} else if (this.typeHeader === 'mitarbeiter') {
if (!this.person_id || !this.mitarbeiter_uid || !this.domain) {
throw new Error(
'[DetailHeader] "person_id", "mitarbeiter_uid", and "domain" are required.'
'[DetailHeader] "person_id", "mitarbeiter_uid", and "domain" are requried.'
)
}
this.loadHeaderData(this.person_id, this.mitarbeiter_uid);
@@ -95,26 +86,13 @@ export default {
},
deep: true,
},
headerData: {
handler(newVal) {
if (this.typeHeader === 'student' && newVal?.length) {
this.getSemesterStati(newVal[0].prestudent_id);
}
},
immediate: true
},
},
data(){
return{
headerDataMa: {},
departmentData: {},
leitungData: {},
isFetchingIssues: false,
noCurrentStatus: false,
semesterStatiLoading: false,
leitungOrgLoading: false,
departmentDataLoading: false,
headerDataMaLoading: false
isFetchingIssues: false
};
},
methods: {
@@ -130,40 +108,29 @@ export default {
});
},
getHeader(person_id) {
this.headerDataMaLoading = true;
return this.$api
.call(ApiDetailHeader.getHeader(person_id))
.then(result => {
this.headerDataMa = result.data;
})
.catch(this.$fhcAlert.handleSystemError)
.finally(() => {
this.headerDataMaLoading = false;
});
.catch(this.$fhcAlert.handleSystemError);
},
loadDepartmentData(mitarbeiter_uid) {
this.departmentDataLoading = true;
return this.$api
.call(ApiDetailHeader.getPersonAbteilung(mitarbeiter_uid))
.then(result => {
this.departmentData = result.data;
})
.catch(this.$fhcAlert.handleSystemError)
.finally(() => {
this.departmentDataLoading = false;
});
.catch(this.$fhcAlert.handleSystemError);
},
getLeitungOrg(oekurzbz){
this.leitungOrgLoading = true;
return this.$api
.call(ApiDetailHeader.getLeitungOrg(oekurzbz))
.then(result => {
this.leitungData = result.data;
})
.catch(this.$fhcAlert.handleSystemError)
.finally(() => {
this.leitungOrgLoading = false;
});
.catch(this.$fhcAlert.handleSystemError);
},
async goToLeitung() {
this.loadHeaderData(this.leitungData.person_id, this.leitungData.uid);
@@ -212,33 +179,6 @@ export default {
} else {
return 'data:image/jpeg;base64,' + foto;
}
},
getSemesterStati(prestudent_id){
this.semesterStatiLoading = true;
this.$api
.call(ApiDetailHeader.getSemesterStati(prestudent_id))
.then(result => {
this.semesterStati = result.data;
this.setNoCurrentStatus();
})
.catch(this.$fhcAlert.handleSystemError)
.finally(() => {
this.semesterStatiLoading = false;
});
},
setNoCurrentStatus() {
if(!Array.isArray(this.semesterStati))
{
this.noCurrentStatus = false;
}
if(!this.semesterStati.some(item => item.studiensemester_kurzbz === this.currentSemester)) {
this.noCurrentStatus = true;
}
else
{
this.noCurrentStatus = false;
}
}
},
template: `
@@ -260,6 +200,7 @@ export default {
</modal-upload-foto>
<template v-if="typeHeader==='student'">
<div
v-for="person in headerData"
:key="person.person_id"
@@ -295,100 +236,62 @@ export default {
<small class="text-muted">{{person.uid}}</small>
</div>
<div v-if="headerData.length == 1">
<div v-if="!isLoading" class="d-flex align-items-center gap-3">
<h2 class="h4">
{{headerData[0].titelpre}}
{{headerData[0].vorname}}
{{headerData[0].nachname}}
<span v-if="headerData[0].titelpost">, </span>
{{headerData[0].titelpost}}
</h2>
<h6 v-if="headerData[0].unruly" class="badge" :class="'bg-unruly rounded-0'"><strong>unruly</strong></h6>
</div>
<div v-else class="d-flex align-items-center gap-3">
<pv-skeleton width="15rem" height="2rem" borderRadius="16px"></pv-skeleton>
<h6 v-if="headerData[0].unruly" class="badge" :class="'bg-unruly rounded-0'"><strong>unruly</strong></h6>
</div>
<div v-if="headerData.length == 1">
<div class="d-flex align-items-center gap-3">
<h2 class="h4">
{{headerData[0].titelpre}}
{{headerData[0].vorname}}
{{headerData[0].nachname}}
<span v-if="headerData[0].titelpost">, </span>
{{headerData[0].titelpost}}
</h2>
<h6 v-if="headerData[0].unruly" class="badge" :class="'bg-unruly rounded-0'"><strong>unruly</strong></h6>
</div>
<h5 class="h6 d-flex align-items-center flex-wrap gap-1">
<strong class="text-muted">{{$p.t('lehre', 'studiengang')}} </strong>
<span v-if="!isLoading">
{{headerData[0].stg_bezeichnung}} ({{headerData[0].studiengang}})
</span>
<span v-else>
<pv-skeleton width="10rem"></pv-skeleton>
</span>
<template v-if="!semesterStatiLoading">
<strong v-if="headerData[0].semester != null" class="text-muted"> | {{$p.t('lehre', 'semester')}} </strong>
{{headerData[0].semester}}
<strong v-if="headerData[0].gruppe !== null && headerData[0].verband != ' '" class="text-muted"> | {{$p.t('lehre', 'verband')}}</strong>
{{headerData[0].verband}}
<strong v-if="headerData[0].gruppe !== null && headerData[0].gruppe != ' '" class="text-muted"> | {{$p.t('lehre', 'gruppe')}} </strong>
{{headerData[0].gruppe}}
</template>
<template v-else>
<strong class="text-muted"> | {{$p.t('lehre', 'semester')}} </strong>
<pv-skeleton size="1rem" class="mr-2"></pv-skeleton>
<strong class="text-muted"> | {{$p.t('lehre', 'verband')}}</strong>
<pv-skeleton size="1rem" class="mr-2"></pv-skeleton>
<strong class="text-muted"> | {{$p.t('lehre', 'gruppe')}} </strong>
<pv-skeleton size="1rem" class="mr-2"></pv-skeleton>
</template>
</h5>
<h5 class="h6">
<strong class="text-muted">{{$p.t('lehre', 'studiengang')}} </strong>
{{headerData[0].stg_bezeichnung}} ({{headerData[0].studiengang}})
<strong v-if="headerData[0].semester" class="text-muted"> | {{$p.t('lehre', 'semester')}} </strong>
{{headerData[0].semester}}
<strong v-if="headerData[0].verband" class="text-muted"> | {{$p.t('lehre', 'verband')}}</strong>
{{headerData[0].verband}}
<strong v-if="headerData[0].gruppe" class="text-muted"> | {{$p.t('lehre', 'gruppe')}} </strong>
{{headerData[0].gruppe}}
</h5>
<h5 class="h6 d-flex align-items-center flex-wrap gap-1">
<strong class="text-muted">Email </strong>
<span v-if="!isLoading">
<a :href="'mailto:'+headerData[0]?.mail_intern">{{headerData[0].mail_intern}}</a>
</span>
<span v-else>
<pv-skeleton width="10rem"></pv-skeleton>
</span>
<strong class="text-muted"> | Status </strong>
<span v-if="noCurrentStatus">
<strong class="text-danger">{{$p.t('lehre', 'textNoStatusInSem', { sem: currentSemester}) }}</strong>
</span>
<span v-else>
{{headerData[0].statusofsemester}}
</span>
</h5>
</div>
<div v-if="headerData.length == 1" class="col-md-1 d-flex flex-column align-items-end justify-content-start ms-auto">
<div class="d-flex py-1">
<div class="px-2" style="min-width: 100px;">
<slot name="issues"></slot>
</div>
<div v-if="hasTileGammaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleGammaTile"></slot></h4>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="valueGammaTile"></slot>
</h6>
</div>
<div v-if="hasTileBetaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleBetaTile"></slot></h4>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="valueBetaTile"></slot>
</h6>
</div>
<div v-if="hasTileAlphaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleAlphaTile"></slot></h4>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="valueAlphaTile"></slot>
</h6>
</div>
<div v-if="hasTileUIDSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center">UID</h4>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="uid"></slot>
</h6>
<h5 class="h6">
<strong class="text-muted">Email </strong>
<span>
<a :href="'mailto:'+headerData[0]?.mail_intern">{{headerData[0].mail_intern}}</a>
</span>
<strong v-if="headerData[0].statusofsemester" class="text-muted"> | Status </strong>
{{headerData[0].statusofsemester}}
</h5>
</div>
<div v-if="headerData.length == 1" class="col-md-1 d-flex flex-column align-items-end justify-content-start ms-auto">
<div class="d-flex py-1">
<div class="px-2" style="min-width: 100px;">
<slot name="issues"></slot>
</div>
<div v-if="hasTileGammaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleGammaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueGammaTile"></slot></h6>
</div>
<div v-if="hasTileBetaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleBetaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueBetaTile"></slot></h6>
</div>
<div v-if="hasTileAlphaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleAlphaTile"></slot></h4>
<h6 class="text-muted text-center"><slot name="valueAlphaTile"></slot></h6>
</div>
<div v-if="hasTileUIDSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center">UID</h4>
<h6 class="text-muted text-center"><slot name="uid"></slot></h6>
</div>
</div>
</div>
</div>
</template>
@@ -427,51 +330,27 @@ export default {
<!--show Ma-Details-->
<div class="col-md-9 text-nowrap mt-2">
<h4 v-if="!headerDataMaLoading">{{headerDataMa.titelpre}} {{headerDataMa.vorname}} {{headerDataMa.nachname}}<span v-if="headerDataMa?.titelpost">, </span> {{headerDataMa.titelpost}}</h4>
<h4 v-else><pv-skeleton width="15rem" height="2rem" borderRadius="16px"></pv-skeleton></h4>
<div class="d-flex align-items-center flex-wrap gap-1">
<strong class="text-muted">{{departmentData.organisationseinheittyp_kurzbz}}</strong>
<span v-if="!departmentDataLoading">
{{departmentData.bezeichnung}}
</span>
<span v-else>
<pv-skeleton width="12rem"></pv-skeleton>
</span>
<span v-if="leitungData.uid"> | </span>
<strong v-if="leitungData.uid" class="text-muted">Vorgesetzte*r </strong>
<span v-if="!leitungOrgLoading">
<a href="#" @click.prevent="goToLeitung">
{{leitungData.titelpre}} {{leitungData.vorname}} {{leitungData.nachname}}
<h4>{{headerDataMa.titelpre}} {{headerDataMa.vorname}} {{headerDataMa.nachname}}<span v-if="headerDataMa?.titelpost">, </span> {{headerDataMa.titelpost}}</h4>
<strong class="text-muted">{{departmentData.organisationseinheittyp_kurzbz}}</strong>
{{departmentData.bezeichnung}}
<span v-if="leitungData.uid"> | </span>
<strong v-if="leitungData.uid" class="text-muted">Vorgesetzte*r </strong>
<a href="#" @click.prevent="goToLeitung">
{{leitungData.titelpre}} {{leitungData.vorname}} {{leitungData.nachname}}
</a>
<p>
<strong class="text-muted">Email </strong>
<span v-if="headerDataMa && (headerDataMa.alias === undefined || headerDataMa.alias === null || headerDataMa.alias === '')">
<a :href="'mailto:' + mitarbeiter_uid + '@' + domain">
{{ mitarbeiter_uid }}@{{ domain }}
</a>
</span>
<span v-else>
<pv-skeleton width="5rem"></pv-skeleton>
<a :href="'mailto:'+headerDataMa?.alias+'@'+domain">{{headerDataMa.alias}}@{{domain}}</a>
</span>
</div>
<div class="d-flex align-items-center gap-2 flex-nowrap">
<div class="d-flex align-items-center gap-1">
<strong class="text-muted">Email</strong>
<template v-if="!headerDataMaLoading">
<a :href="'mailto:' + (headerDataMa?.alias || mitarbeiter_uid) + '@' + domain">
{{ (headerDataMa?.alias || mitarbeiter_uid) + '@' + domain }}
</a>
</template>
<pv-skeleton v-else width="10rem"></pv-skeleton>
</div>
<div v-if="headerDataMa?.telefonklappe" class="d-flex align-items-center gap-1">
<span>|</span>
<strong class="text-muted">DW</strong>
<template v-if="!headerDataMaLoading">
{{ headerDataMa.telefonklappe }}
</template>
<pv-skeleton v-else width="4rem"></pv-skeleton>
</div>
</div>
<span v-if="headerDataMa?.telefonklappe" class="mb-2"> | <strong class="text-muted">DW </strong>{{headerDataMa?.telefonklappe}}</span>
</p>
<slot name="tag"></slot>
</div>
<div class="col-md-1 d-flex flex-column align-items-end justify-content-start ms-auto">
@@ -481,33 +360,20 @@ export default {
</div>
<div v-if="hasTileGammaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleGammaTile"></slot></h4>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="valueGammaTile"></slot>
</h6>
<h6 class="text-muted text-center"><slot name="valueGammaTile"></slot></h6>
</div>
<div v-if="hasTileBetaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleBetaTile"></slot></h4>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="valueBetaTile"></slot>
</h6>
<h6 class="text-muted text-center"><slot name="valueBetaTile" :valueBetaTile="valueBetaTile"></slot></h6>
</div>
<div v-if="hasTileAlphaSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center"><slot name="titleAlphaTile"></slot></h4>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="valueAlphaTile"></slot>
</h6>
<h6 class="text-muted text-center"><slot name="valueAlphaTile"></slot></h6>
</div>
<div v-if="hasTileUIDSlot" class="px-2" style="border-left: 1px solid #EEE">
<h4 class="mb-1 text-center">UID</h4>
<h6 class="text-muted d-flex align-items-center justify-content-center flex-wrap gap-1">
<pv-skeleton v-if="isLoading" width="4rem"></pv-skeleton>
<slot v-else name="uid"></slot>
</h6>
<h6 class="text-muted text-center"><slot name="uid"></slot></h6>
</div>
</div>
</div>
@@ -0,0 +1,77 @@
import ApiLehre from "../../api/factory/lehre.js";
import {capitalize} from "../../helpers/StringHelpers.js";
const options = Vue.ref([]);
const params = Vue.ref({});
let appContext = null;
export function setupContext(globalProps) {
appContext = globalProps
}
// bind and watch api params via reference
export function bindParams(paramsRef) {
Vue.watch(
paramsRef,
(newVal) => {
params.value = { ...newVal };
fetchLehreinheiten(newVal.lv_id, newVal.sem_kurzbz);
},
{ immediate: true, deep: true }
);
}
async function fetchLehreinheiten(lv_id, sem_kurzbz) {
if (!lv_id || !sem_kurzbz) {
options.value = []
return
}
appContext.$api.call(ApiLehre.getLeForLv(lv_id, sem_kurzbz)).then(res => {
const data = []
// TODO: could be done on server in some shared function, copied from anw extension for now
res.data?.retval?.forEach(entry => {
const existing = data.find(e => e.lehreinheit_id === entry.lehreinheit_id)
if (existing) {
// supplement info
existing.infoString += ', '
if (entry.gruppe_kurzbz !== null && entry.direktinskription == false) {
existing.infoString += entry.gruppe_kurzbz
} else {
existing.infoString += entry.kurzbzlang + '-' + entry.semester
+ (entry.verband ? entry.verband : '')
+ (entry.gruppe ? entry.gruppe : '')
}
} else {
// entries are supposed to be fetched ordered by non null gruppe_kurzbz first
// so a new entry will always start with those groups, others are appended afterwards
entry.infoString = entry.kurzbz + ' - ' + entry.lehrform_kurzbz + ' - '
if (entry.gruppe_kurzbz !== null && entry.direktinskription == false) {
entry.infoString += entry.gruppe_kurzbz
} else {
entry.infoString += entry.kurzbzlang + '-' + entry.semester
+ (entry.verband ? entry.verband : '')
+ (entry.gruppe ? entry.gruppe : '')
}
data.push(entry)
}
})
options.value = [...data]
})
}
// export the module and relevant fields via reactive
const LehreinheitenModule = Vue.reactive({
options,
optionLabel: 'infoString',
placeholder: capitalize(Vue.computed(()=>appContext?.$p.t('lehre/lehreinheit'))),
setupContext,
bindParams
});
export default LehreinheitenModule;
+1 -3
View File
@@ -29,8 +29,7 @@ export default {
label: String,
// NOTE(chris): remove these from $attrs array to prevent doubled event listeners
onInput: [Array, Function],
'onUpdate:modelValue': [Array, Function],
titleActionButton: String
'onUpdate:modelValue': [Array, Function]
},
data() {
return {
@@ -318,7 +317,6 @@ export default {
:id="idCmp"
:name="name"
:class="validationClass"
:titleActionButton="titleActionButton"
@update:model-value="clearValidationForThisName"
>
<slot></slot>
+2 -11
View File
@@ -1,14 +1,9 @@
export default {
emits: [
'update:modelValue',
'actionbutton-clicked'
'update:modelValue'
],
props: {
modelValue: String,
titleActionButton: {
type: String,
default: ""
}
modelValue: String
},
computed: {
valueAsBase64DataString() {
@@ -33,9 +28,6 @@ export default {
},
deleteImage() {
this.$emit('update:modelValue', '');
},
emitAction(){
this.$emit('actionbutton-clicked', this.modelValue);
}
},
template: `
@@ -50,7 +42,6 @@ export default {
<button type="button" class="btn btn-outline-dark btn-sm" @click="openUploadDialog">
<i class="fa fa-pen"></i>
</button>
<button v-if="titleActionButton" class="btn btn-outline-dark btn-sm" @click="emitAction">{{titleActionButton}}</button>
</div>
</div>
</template>
+20
View File
@@ -0,0 +1,20 @@
export const MobilityLegende = {
name: 'MobilityLegende',
template:`
<div class="col-auto" style="max-width: 60vw">
<!-- TODO: phrasen definieren & verwenden-->
<div class="row" style="font-weight: bold"><h6>Legende</h6></div>
<div class="row"><h6>(i) ... Incoming</h6></div>
<div class="row"><h6>(o) ... Outgoing</h6></div>
<div class="row"><h6>(ar) ... angerechnet </h6></div>
<div class="row"><h6>(iar) ... intern angerechnet</h6></div>
<div class="row"><h6>(nz) ... nicht zugelassen</h6></div>
<div class="row"><h6>(ma) ... MitarbeiterIn</h6></div>
<div class="row"><h6>(a.o.) ... Außerordentliche/r HörerIn</h6></div>
<div class="row"><h6>(d.d.) ... Double Degree Program</h6></div>
</div>
`
};
export default MobilityLegende;
+21 -148
View File
@@ -18,7 +18,6 @@
import CoreSearchbar from "../searchbar/searchbar.js";
import NavLanguage from "../navigation/Language.js";
import VerticalSplit from "../verticalsplit/verticalsplit.js";
import HorizontalSplit from "../horizontalsplit/horizontalsplit.js";
import AppMenu from "../AppMenu.js";
import AppConfig from "../AppConfig.js";
import StvVerband from "./Studentenverwaltung/Verband.js";
@@ -38,7 +37,6 @@ export default {
CoreSearchbar,
NavLanguage,
VerticalSplit,
HorizontalSplit,
AppMenu,
AppConfig,
StvVerband,
@@ -194,44 +192,7 @@ export default {
}
return extraItems;
},
appMenuLvPlanungItems() {
const extraItems = [];
if (this.studiengangKz !== undefined && this.selected_semester !== undefined) {
const studiengang_kz = String(this.studiengangKz);
const semester = String(this.selected_semester);
const orgform = this.selected_orgform || '';
extraItems.push({
link: FHC_JS_DATA_STORAGE_OBJECT.app_root
+ 'content/statistik/lvplanung.xls.php?'
+ '&studiengang_kz=' + studiengang_kz
+ '&semester=' + semester
+ '&studiensemester_kurzbz=' + this.studiensemesterKurzbz
+ '&orgform_kurzbz=' + orgform,
description: 'stv/lvplanung_xls'
});
extraItems.push({
link: FHC_JS_DATA_STORAGE_OBJECT.app_root
+ 'content/statistik/lvplanung.php?'
+ '&studiengang_kz=' + studiengang_kz
+ '&semester=' + semester,
description: 'stv/lvplanung_html'
});
}
return extraItems;
},
linkRt(){
return FHC_JS_DATA_STORAGE_OBJECT.app_root + '/vilesci/stammdaten/reihungstestverwaltung.php'
},
selected_uid(){
return this.selected?.[this.selected.length - 1]?.uid ?? null;
},
linkGradeList(){
return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'index.ci.php/person/gradelist/index/' + this.selected_uid
},
}
},
watch: {
'url_studiensemester_kurzbz': function (newVal, oldVal) {
@@ -251,7 +212,6 @@ export default {
'url_studiengang': function (newVal, oldVal) {
if (newVal !== oldVal) {
this.checkUrlStudiengang();
this.$refs.stvList.clearSelection();
}
},
'url_mode': function () {
@@ -275,10 +235,6 @@ export default {
}
}
}
},
sidebarCollapsed(newVal) {
if(newVal) this.$refs.hSplit.collapseLeft()
else this.$refs.hSplit.showBoth()
}
},
methods: {
@@ -475,21 +431,6 @@ export default {
},
deleteCustomFilter(){
this.$refs.stvList.resetFilter();
},
showAlertNoSelectedStudent(){
this.$fhcAlert.alertError(this.$p.t('ui', 'alert_chooseStudent'));
},
showAlertMultipleStudents() {
this.$fhcAlert.alertError(this.$p.t('ui', 'alert_chooseOnlyOneStudent'));
},
showAlertNoGroupChosen(){
this.$fhcAlert.alertError(this.$p.t('ui', 'alert_chooseGroupSem'));
},
showMenu(refName) {
bootstrap.Dropdown.getOrCreateInstance(this.$refs[refName]).show();
},
hideMenu(refName) {
bootstrap.Dropdown.getOrCreateInstance(this.$refs[refName]).hide();
}
},
created() {
@@ -665,30 +606,19 @@ export default {
</div>
<div class="offcanvas-body">
<app-menu app-identifier="stv">
<li :class="{ dropend: appMenuExtraItems.length }"
@mouseenter="appMenuExtraItems.length && showMenu('gradeReportToggle')"
@mouseleave="appMenuExtraItems.length && hideMenu('gradeReportToggle')"
>
<li class="dropend">
<a
ref="gradeReportToggle"
v-if="appMenuExtraItems.length"
class="dropdown-toggle"
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
:class="{ disabled: !appMenuExtraItems.length }"
data-bs-popper-config='{"strategy":"fixed"}'
>
{{ $p.t('stv/grade_report') }}
</a>
<a
v-else
href="#"
@click.prevent="showAlertNoGroupChosen"
>
{{ $p.t('stv/grade_report') }}
</a>
<ul v-if="appMenuExtraItems.length" class="dropdown-menu p-0">
<ul class="dropdown-menu p-0">
<li
v-for="(item, key) in appMenuExtraItems"
:key="key"
@@ -699,83 +629,26 @@ export default {
</li>
</ul>
</li>
<li :class="{ dropend: appMenuExtraItems.length }"
@mouseenter="appMenuExtraItems.length && showMenu('lvPlanungToggle')"
@mouseleave="appMenuExtraItems.length && hideMenu('lvPlanungToggle')"
>
<a
ref="lvPlanungToggle"
v-if="appMenuExtraItems.length"
class="dropdown-toggle"
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
data-bs-popper-config='{"strategy":"fixed"}'
>
{{ $p.t('stv/lvplanung') }}
</a>
<a
v-else
href="#"
@click.prevent="showAlertNoGroupChosen"
>
{{ $p.t('stv/lvplanung') }}
</a>
<ul v-if="appMenuExtraItems.length" class="dropdown-menu p-0">
<li
v-for="(item, key) in appMenuLvPlanungItems"
:key="key"
>
<a class="dropdown-item" :href="item.link" target="_blank">
{{ $p.t(item.description) }}
</a>
</li>
</ul>
</li>
<li>
<a :href="linkRt" target="_blank">
{{ $p.t('stv/RTVerwaltung') }}
</a>
</li>
<li>
<a v-if="selected.length === 1" :href="linkGradeList" target="_blank">
{{ $p.t('stv/studienverlauf') }}
</a>
<a v-else-if="selected.length === 0" href="#" @click.prevent="showAlertNoSelectedStudent">
{{ $p.t('stv/studienverlauf') }}
</a>
<a v-else href="#" @click.prevent="showAlertMultipleStudents">
{{ $p.t('stv/studienverlauf') }}
</a>
</li>
</app-menu>
</div>
</aside>
<horizontal-split ref="hSplit" :defaultRatio="[15, 85]">
<template #left>
<nav id="sidebarMenu" class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100 w-100">
<div class="offcanvas-header justify-content-end px-1 d-md-none">
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" :aria-label="$p.t('ui/schliessen')"></button>
</div>
<stv-verband :preselectedKey="studiengangKz ? '' + studiengangKz : null" :endpoint="verbandEndpoint" @select-verband="onSelectVerband" class="col" style="height:0%"></stv-verband>
<stv-studiensemester v-model:studiensemester-kurzbz="studiensemesterKurzbz" @update:studiensemester-kurzbz="studiensemesterChanged"></stv-studiensemester>
</nav>
</template>
<template #right>
<main>
<vertical-split :defaultRatio="[50, 50]">
<template #top>
<stv-list ref="stvList" v-model:selected="selected" :studiengang-kz="studiengangKz" :studiensemester-kurzbz="studiensemesterKurzbz" @filterActive="handleCustomFilter"></stv-list>
</template>
<template #bottom>
<stv-details ref="details" :students="selected" @reload="reloadList"></stv-details>
</template>
</vertical-split>
</main>
</template>
</horizontal-split>
<nav id="sidebarMenu" class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100">
<div class="offcanvas-header justify-content-end px-1 d-md-none">
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" :aria-label="$p.t('ui/schliessen')"></button>
</div>
<stv-verband :preselectedKey="studiengangKz ? '' + studiengangKz : null" :endpoint="verbandEndpoint" @select-verband="onSelectVerband" class="col" style="height:0%"></stv-verband>
<stv-studiensemester v-model:studiensemester-kurzbz="studiensemesterKurzbz" @update:studiensemester-kurzbz="studiensemesterChanged"></stv-studiensemester>
</nav>
<main class="col-md-8 ms-sm-auto col-lg-9 col-xl-10">
<vertical-split>
<template #top>
<stv-list ref="stvList" v-model:selected="selected" :studiengang-kz="studiengangKz" :studiensemester-kurzbz="studiensemesterKurzbz" @filterActive="handleCustomFilter"></stv-list>
</template>
<template #bottom>
<stv-details ref="details" :students="selected" @reload="reloadList"></stv-details>
</template>
</vertical-split>
</main>
</div>
</div>
<app-config ref="config" v-model="appconfig" :endpoints="configEndpoints"></app-config>
@@ -2,18 +2,12 @@ import FhcTabs from "../../Tabs.js";
import FhcHeader from "../../DetailHeader/DetailHeader.js";
import ApiStvApp from '../../../api/factory/stv/app.js';
import ApiStudent from '../../../api/factory/stv/students.js';
// TODO(chris): alt & title
// TODO(chris): phrasen
export default {
name: "DetailsPrestudent",
inject: {
currentSemester: {
from: 'currentSemester',
},
},
components: {
FhcTabs,
FhcHeader
@@ -21,9 +15,7 @@ export default {
data() {
return {
configStudent: {},
configStudents: {},
activeTab: null,
localStudent: null
configStudents: {}
};
},
props: {
@@ -48,9 +40,6 @@ export default {
}
return Object.fromEntries(Object.entries(this.configStudents).filter(([ , value ]) => !value.showOnlyWithUid && !value.showOnlyWithUid));
},
isLoading() {
return this.students === null; //null-> loading, [] -> empty, [...] -> data, necessary for skeleton in child
},
tile_PersId(){
let tile = this.students[0].person_id != null ? this.students[0].person_id : '-';
return tile;
@@ -68,21 +57,6 @@ export default {
'$p.user_language.value'(n, o) {
if (n !== o && o !== undefined)
this.loadConfig();
},
currentSemester(newVal) {
if (
Array.isArray(this.students) &&
this.students.length === 1 &&
newVal !== this.students[0].query_studiensemester_kurzbz
) {
this.reloadDataStudent();
}
else {
this.localStudent = null;
}
},
students() {
this.localStudent = null;
}
},
methods: {
@@ -100,28 +74,10 @@ export default {
})
.catch(this.$fhcAlert.handleSystemError);
},
handleTabChanged(key) {
this.activeTab = key
this.reload()
},
reload() {
if (this.$refs.tabs?.$refs?.current?.reload)
this.$refs.tabs.$refs.current.reload();
},
reloadDataStudent(){
this.localStudent = null;
const studentArr = this.students;
if (!studentArr || !studentArr.length) {
return;
}
this.$api
.call(ApiStudent.uid(studentArr[0].uid, this.currentSemester))
.then(result => {
this.localStudent = result.data;
});
},
reloadList() {
this.$emit('reload');
},
@@ -136,12 +92,10 @@ export default {
</div>
<div v-else-if="configStudent && configStudents" class="d-flex flex-column h-100">
<fhc-header
:headerData="localStudent || students"
:currentSemester="currentSemester"
:headerData="students"
typeHeader="student"
@reload="reloadList"
fotoEditable
:isLoading="isLoading"
>
<template #uid>{{students[0].uid}}</template>
<template #titleAlphaTile>PersID</template>
@@ -153,16 +107,16 @@ export default {
</fhc-header>
<fhc-tabs
v-if="students.length == 1"
ref="tabs"
ref="tabs"
:useprimevue="true"
:modelValue="(Array.isArray(localStudent) && localStudent[0]) || students[0]"
:modelValue="students[0]"
:config="config"
:default="activeTab ?? $route.params.tab"
:default="$route.params.tab"
style="flex: 1 1 0%; height: 0%"
@changed="handleTabChanged"
@changed="reload"
>
</fhc-tabs>
<fhc-tabs v-else ref="tabs" :useprimevue="true" :modelValue="students" :config="config" :default="activeTab ?? $route.params.tab" style="flex: 1 1 0%; height: 0%" @changed="handleTabChanged"></fhc-tabs>
<fhc-tabs v-else ref="tabs" :useprimevue="true" :modelValue="students" :config="config" :default="$route.params.tab" style="flex: 1 1 0%; height: 0%" @changed="reload"></fhc-tabs>
</div>
<div v-else>
Loading...
@@ -238,16 +238,6 @@ export default {
}
return this.student.map(e => e.uid);
},
studentNames() {
if (this.student.uid)
{
return [this.student.vorname + ' ' + this.student.nachname];
}
const array = this.student.map(e => ' ' + e.vorname + ' ' + e.nachname + '(' + e.uid +')');
return array.toString();
},
studentKzs(){
if (this.student.uid)
{
@@ -297,50 +287,10 @@ export default {
.catch(this.$fhcAlert.handleSystemError);
},
actionNewAbschlusspruefung() {
this.setDefaultFormData();
this.resetForm();
this.statusNew = true;
//prepare local Storage
let STORAGE_KEY = 'finalExamDefaultData';
const id = '20260224_02';
const stored = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
if (stored[id]) {
const data = stored[id];
this.formData.pruefungstyp_kurzbz = data.pruefungstyp_kurzbz;
this.formData.datum = data.datum;
this.formData.sponsion = data.sponsion;
this.formData.akadgrad_id = data.akadgrad_id;
if (data.vorsitz_uid) {
this.selectedVorsitz = {
mitarbeiter_uid: data.vorsitz_uid,
person_id: data.vorsitz_person_id,
label: data.vorsitz_label
};
}
if (data.pruefer1_person_id) {
this.selectedPruefer1 = {
person_id: data.pruefer1_person_id,
label: data.pruefer1_label
};
}
if (data.pruefer2_person_id) {
this.selectedPruefer2 = {
person_id: data.pruefer2_person_id,
label: data.pruefer2_label
};
}
if (data.pruefer3_person_id) {
this.selectedPruefer3 = {
person_id: data.pruefer3_person_id,
label: data.pruefer3_label
};
}
}
this.$refs.finalexamModal.show();
this.setDefaultFormData();
},
actionEditAbschlusspruefung(abschlusspruefung_id) {
this.resetForm();
@@ -355,23 +305,20 @@ export default {
};
if (data.p1_person_id) {
this.selectedPruefer1 = {
label: this.getPersonLabel(data.p1_titelpre, data.p1_nachname, data.p1_vorname, data.p1_titelpost, data.p1_uid),
person_id: data.p1_person_id,
mitarbeiter_uid: data.p1_uid
label: this.getPersonLabel(data.p1_titelpre, data.p1_nachname, data.p1_vorname, data.p1_titelpost),
person_id: data.p1_person_id
};
}
if (data.p2_person_id) {
this.selectedPruefer2 = {
label: this.getPersonLabel(data.p2_titelpre, data.p2_nachname, data.p2_vorname, data.p2_titelpost, data.p2_uid),
person_id: data.p2_person_id,
mitarbeiter_uid: data.p2_uid
label: this.getPersonLabel(data.p2_titelpre, data.p2_nachname, data.p2_vorname, data.p2_titelpost),
person_id: data.p2_person_id
}
};
if (data.p3_person_id) {
this.selectedPruefer3= {
label: this.getPersonLabel(data.p3_titelpre, data.p3_nachname, data.p3_vorname, data.p3_titelpost, data.p3_uid),
person_id: data.p3_person_id,
mitarbeiter_uid: data.p3_uid
label: this.getPersonLabel(data.p3_titelpre, data.p3_nachname, data.p3_vorname, data.p3_titelpost),
person_id: data.p3_person_id
};
}
});
@@ -390,29 +337,6 @@ export default {
.then(this.deleteAbschlusspruefung)
.catch(this.$fhcAlert.handleSystemError);
},
saveOrUpdateLocalStorage(){
let STORAGE_KEY = 'finalExamDefaultData';
const id = '20260224_02';
const stored = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
stored[id] = {
pruefungstyp_kurzbz: this.formData.pruefungstyp_kurzbz,
vorsitz_uid: this.selectedVorsitz?.mitarbeiter_uid || null,
vorsitz_person_id: this.selectedVorsitz?.person_id || null,
vorsitz_label: this.selectedVorsitz?.label || null,
pruefer1_person_id: this.selectedPruefer1?.person_id || null,
pruefer1_label: this.selectedPruefer1?.label || null,
pruefer2_person_id: this.selectedPruefer2?.person_id || null,
pruefer2_label: this.selectedPruefer2?.label || null,
pruefer3_person_id: this.selectedPruefer3?.person_id || null,
pruefer3_label: this.selectedPruefer3?.label || null,
akadgrad_id: this.formData.akadgrad_id,
datum: this.formData.datum,
sponsion: this.formData.sponsion
};
localStorage.setItem(STORAGE_KEY, JSON.stringify(stored));
},
addNewAbschlusspruefung() {
const dataToSend = {
uid: this.student.uid,
@@ -423,8 +347,6 @@ export default {
.call(ApiStvAbschlusspruefung.addNewAbschlusspruefung(dataToSend))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
//save formData fields in LocalStorage
this.saveOrUpdateLocalStorage();
this.hideModal('finalexamModal');
this.resetForm();
})
@@ -433,26 +355,6 @@ export default {
this.reload();
});
},
async addNewAbschlusspruefungMulti(){
try {
for (const student of this.studentUids) {
await this.$refs.formFinalExam.call(
ApiStvAbschlusspruefung.addNewAbschlusspruefung({
uid: student,
formData: this.formData
})
);
}
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
//save formData fields in LocalStorage
this.saveOrUpdateLocalStorage();
this.hideModal('finalexamModal');
this.resetForm();
} catch (error) {
this.$fhcAlert.handleSystemError(error);
}
},
hideModal(modalRef){
this.$refs[modalRef].hide();
},
@@ -474,9 +376,6 @@ export default {
id: abschlusspruefung_id,
formData: this.formData
};
//uncomment if also save data in local storage for update
//this.saveOrUpdateLocalStorage();
return this.$refs.formFinalExam
.call(ApiStvAbschlusspruefung.updateAbschlusspruefung(dataToSend))
.then(response => {
@@ -518,6 +417,7 @@ export default {
this.selectedPruefer1 = null;
this.selectedPruefer2 = null;
this.selectedPruefer3 = null;
},
setDefaultFormData() {
@@ -571,29 +471,29 @@ export default {
searchPerson(event) {
if (this.abortController.persons) {
this.abortController.persons.abort();
}
}
this.abortController.persons = new AbortController();
return this.$api
.call(ApiStvAbschlusspruefung.getPruefer(event.query))
.then(result => {
this.filteredPersons = [];
for (let person of result.data.retval) {
this.filteredPersons.push(
{
label: this.getPersonLabel(
person.titelpre,
person.nachname,
person.vorname,
person.titelpost,
person.uid
),
person_id: person.person_id,
mitarbeiter_uid: person.uid
}
);
}
});
this.filteredPersons = [];
for (let person of result.data.retval) {
this.filteredPersons.push(
{
label: this.getPersonLabel(
person.titelpre,
person.nachname,
person.vorname,
person.titelpost,
person.person_uid
),
person_id: person.person_id
}
);
}
});
},
},
created() {
@@ -626,7 +526,7 @@ export default {
.catch(this.$fhcAlert.handleSystemError);
this.$api
.call(ApiStvAbschlusspruefung.getAkadGrade(this.stg_kz))
.call(ApiStvAbschlusspruefung.getAkadGrade(this.student.studiengang_kz))
.then(result => {
this.arrAkadGrad = result.data;
})
@@ -647,8 +547,7 @@ export default {
<div class="stv-details-abschlusspruefung h-100 pb-3">
<h4>{{this.$p.t('stv','tab_finalexam')}}</h4>
<div v-if="this.student.length" class="d-flex gap-2">
<button class="btn btn-primary" @click="actionNewAbschlusspruefung()"> + {{$p.t('stv', 'tab_finalexam')}}</button>
<div v-if="this.student.length">
<abschlusspruefung-dropdown
:showAllFormats="showAllFormats"
:studentUids="studentUids"
@@ -680,14 +579,12 @@ export default {
<template #title>
<p v-if="statusNew" class="fw-bold mt-3">{{$p.t('abschlusspruefung', 'abschluessPruefungAnlegen')}}</p>
<p v-else class="fw-bold mt-3">{{$p.t('abschlusspruefung', 'abschluessPruefungBearbeiten')}}</p>
<small v-if="this.student.length" class="text-muted">{{studentNames}}</small>
</template>
<form-form ref="formFinalExam" @submit.prevent>
<form-form v-if="!this.student.length" ref="formFinalExam" @submit.prevent>
<legend>{{this.$p.t('global','details')}}</legend>
<p v-if="statusNew">[{{$p.t('ui', 'neu')}}]</p>
<div class="row mb-3">
<form-input
container-class="col-6 stv-details-abschlusspruefung-typ"
@@ -705,7 +602,6 @@ export default {
</option>
</form-input>
<form-input
v-if="!this.student.length"
container-class="col-6 stv-details-abschlusspruefung-note"
:label="$p.t('abschlusspruefung', 'notekommpruefung')"
type="select"
@@ -774,10 +670,9 @@ export default {
>
</form-input>
</div>
<div class="row mb-3">
<form-input
v-if="!this.student.length"
container-class="col-6 stv-details-abschlusspruefung-abschlussbeurteilung_kurzbz"
:label="$p.t('abschlusspruefung', 'abschlussbeurteilung')"
type="select"
@@ -803,23 +698,7 @@ export default {
optionValue="person_id"
dropdown
forceSelection
:suggestions="filteredPersons"
@complete="searchPerson"
:min-length="3"
>
</form-input>
<form-input
v-if="this.student.length"
type="autocomplete"
container-class="col-6 stv-details-abschlusspruefung-pruefer3"
:label="$p.t('abschlusspruefung', 'pruefer3')"
name="pruefer3"
v-model="selectedPruefer3"
optionLabel="label"
optionValue="person_id"
dropdown
forceSelection
:suggestions="filteredPersons"
:suggestions="filteredPersons"
@complete="searchPerson"
:min-length="3"
>
@@ -843,7 +722,6 @@ export default {
</option>
</form-input>
<form-input
v-if="!this.student.length"
type="autocomplete"
container-class="col-6 stv-details-abschlusspruefung-pruefer3"
:label="$p.t('abschlusspruefung', 'pruefer3')"
@@ -853,7 +731,7 @@ export default {
optionValue="person_id"
dropdown
forceSelection
:suggestions="filteredPersons"
:suggestions="filteredPersons"
@complete="searchPerson"
:min-length="3"
>
@@ -901,7 +779,6 @@ export default {
>
</form-input>
<form-input
v-if="!this.student.length"
container-class="col-6 stv-details-abschlusspruefung-protokoll"
:label="$p.t('abschlusspruefung', 'protokoll')"
type="textarea"
@@ -913,7 +790,7 @@ export default {
</form-input>
</div>
<div v-if="!this.student.length" class="row mb-3 col-6">
<div class="row mb-3 col-6">
<div class="col">
<p >{{$p.t('abschlusspruefung', 'zurBeurteilung')}}</p>
</div>
@@ -930,9 +807,8 @@ export default {
<template #footer>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{$p.t('ui', 'abbrechen')}}</button>
<button v-if="statusNew && !this.student.length" class="btn btn-primary" @click="addNewAbschlusspruefung()"> {{$p.t('ui', 'speichern')}}</button>
<button v-else-if="statusNew && this.student.length" class="btn btn-primary" @click="addNewAbschlusspruefungMulti(studentUids)"> {{$p.t('ui', 'speichern')}}</button>
<button v-else class="btn btn-primary" @click="updateAbschlusspruefung(formData.abschlusspruefung_id)"> {{$p.t('ui', 'speichern')}}</button>
<button v-if="statusNew" class="btn btn-primary" @click="addNewAbschlusspruefung()"> {{$p.t('ui', 'speichern')}}</button>
<button v-else class="btn btn-primary" @click="updateAbschlusspruefung(formData.abschlusspruefung_id)"> {{$p.t('ui', 'speichern')}}</button>
</template>
</bs-modal>
@@ -158,16 +158,6 @@ export default {
},
reload(){
this.updateStudent(this.modelValue);
},
sendInfomail(){
const subject = this.$p.t('person', 'betreffProfilfoto');
const subjectEncoded = encodeURIComponent(subject);
const body = this.$p.t('person', 'mailText_profilfoto');
const bodyWithNewLines = body.replace(/\\n/g, '\n');
const bodyEncoded = encodeURIComponent(bodyWithNewLines);
window.location.href = "mailto:" + this.modelValue.mail_intern + "?subject=" + subjectEncoded + "&body=" + bodyEncoded;
}
},
created() {
@@ -396,10 +386,8 @@ export default {
container-class="col stv-details-details-foto"
:label="$p.t('person', 'foto')"
type="UploadImage"
titleActionButton="Infomail"
v-model="data.foto"
name="foto"
@actionbutton-clicked="sendInfomail"
>
<img alt="No Image" :src="noImageSrc" class="w-100">
</form-input>
@@ -5,7 +5,6 @@ import PvAutoComplete from "../../../../../../../index.ci.php/public/js/componen
import ApiStvProjektarbeit from '../../../../../api/factory/stv/projektarbeit.js';
export default {
name: 'ProjektarbeitDetails',
components: {
FormForm,
FormInput,
@@ -111,10 +110,6 @@ export default {
this.formData.anmerkung = null;
this.$refs.formDetails.clearValidation();
},
setFormData(projektarbeit) {
this.formData = projektarbeit;
if (this.formData.firma_id) this.formData.firma = {firma_id: this.formData.firma_id, name: this.formData.firma_name};
},
getFormData(newProjektarbeit, studiensemester_kurzbz, additional_lehrveranstaltung_id) {
this.additional_lehrveranstaltung_id = additional_lehrveranstaltung_id;
@@ -153,7 +148,8 @@ export default {
return this.$api
.call(ApiStvProjektarbeit.loadProjektarbeit(projektarbeit_id))
.then(result => {
this.setFormData(result.data)
this.formData = result.data;
if (this.formData.firma_id) this.formData.firma = {firma_id: this.formData.firma_id, name: this.formData.firma_name};
return result;
})
.catch(this.$fhcAlert.handleSystemError)
@@ -9,7 +9,6 @@ import ProjektarbeitDetails from "./Details.js";
import Projektbetreuer from "./Projektbetreuer.js";
export default {
name: 'Projektarbeit',
components: {
CoreFilterCmpt,
BsModal,
@@ -214,6 +213,17 @@ export default {
});
container.append(button);
button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-users"></i>';
button.title = this.$p.t('projektarbeit', 'betreuerBearbeiten');
button.addEventListener('click', (event) => {
let data = cell.getData();
this.editedProjektarbeit = data;
this.actionEditBetreuer();
});
container.append(button);
button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-xmark"></i>';
@@ -254,7 +264,6 @@ export default {
actionEditProjektarbeit() {
this.statusNew = false;
this.toggleMenu('details');
this.$refs.projektbetreuer.getProjektbetreuer(this.editedProjektarbeit?.projektarbeit_id, this.editedProjektarbeit?.studiensemester_kurzbz);
this.$refs.projektarbeitModal.show();
},
actionEditBetreuer() {
@@ -271,18 +280,9 @@ export default {
.then(this.deleteProjektarbeit)
.catch(this.$fhcAlert.handleSystemError);
},
saveProjektarbeit() {
if(this.statusNew) this.addNewProjektarbeit()
else this.updateProjektarbeit()
},
addNewProjektarbeit() {
this.$refs.projektarbeitDetails.addNewProjektarbeit()
.then((result) => {
if(result?.data?.length) {
this.editedProjektarbeit = result.data[0]
this.$refs.projektarbeitDetails.setFormData(this.editedProjektarbeit)
this.toggleMenu('betreuer');
}
this.projektarbeitSaved();
})
.catch(this.$fhcAlert.handleSystemError);
@@ -308,8 +308,7 @@ export default {
projektarbeitSaved() {
this.reload();
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
if(!this.statusNew) this.hideModal('projektarbeitModal');
else this.statusNew = false
this.hideModal('projektarbeitModal');
},
setDefaultStunden(projekttyp_kurzbz) {
this.$refs.projektbetreuer.setDefaultStunden(projekttyp_kurzbz);
@@ -322,22 +321,22 @@ export default {
},
toggleMenu(tabId) {
this.activeTab = tabId;
if (this.statusNew == false && tabId == 'details') {
this.$refs.projektarbeitDetails.getFormData(
this.statusNew, this.editedProjektarbeit?.studiensemester_kurzbz, this.editedProjektarbeit?.lehrveranstaltung_id
);
this.$refs.projektarbeitDetails.loadProjektarbeit(this.editedProjektarbeit?.projektarbeit_id);
} else if(tabId == 'betreuer') {
this.$refs.projektbetreuer.getFormData(
this.editedProjektarbeit ? this.editedProjektarbeit.projekttyp_kurzbz : null
);
this.$refs.projektbetreuer.getProjektbetreuer(this.editedProjektarbeit?.projektarbeit_id, this.editedProjektarbeit?.studiensemester_kurzbz);
if (this.statusNew == false) {
switch(tabId) {
case 'details':
this.$refs.projektarbeitDetails.getFormData(
this.statusNew, this.editedProjektarbeit?.studiensemester_kurzbz, this.editedProjektarbeit?.lehrveranstaltung_id
);
this.$refs.projektarbeitDetails.loadProjektarbeit(this.editedProjektarbeit?.projektarbeit_id);
break;
case 'betreuer':
this.$refs.projektbetreuer.getFormData(
this.editedProjektarbeit ? this.editedProjektarbeit.projekttyp_kurzbz : null
);
this.$refs.projektbetreuer.getProjektbetreuer(this.editedProjektarbeit?.projektarbeit_id, this.editedProjektarbeit?.studiensemester_kurzbz);
break;
}
}
},
resetFormData() {
this.$refs.projektarbeitDetails.resetForm()
this.$refs.projektbetreuer.resetForm()
}
},
template: `
@@ -359,29 +358,46 @@ export default {
</core-filter-cmpt>
<!--Modal: projektarbeitModal-->
<bs-modal ref="projektarbeitModal" :dialog-class="(statusNew ? 'modal-xl ' : 'fhc-xxl-modal ' ) + 'modal-dialog-scrollable'"
header-class="flex-wrap pb-0"
@hideBsModal="resetFormData"
>
<bs-modal ref="projektarbeitModal" dialog-class="modal-xl modal-dialog-scrollable" header-class="flex-wrap pb-0">
<template #title>
<p v-if="statusNew" class="fw-bold mt-3">{{$p.t('projektarbeit', 'projektarbeitAnlegen')}}</p>
<p v-else class="fw-bold mt-3">{{$p.t('projektarbeit', 'projektarbeitBearbeiten')}}</p>
</template>
<div class="row" >
<div :class="statusNew ? 'col-12' : 'col-6'">
<projektarbeit-details ref="projektarbeitDetails" :student="student" @projekttyp-changed="setDefaultStunden">
</projektarbeit-details>
<template #modal-header-content v-if="!statusNew">
<ul class="nav nav-tabs w-100 mt-3 msg_preview" id="pa_tabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link" :class="activeTab == 'details' ? 'active' : ''" id="details-tab" data-bs-toggle="tab" data-bs-target="#details" type="button" role="tab" aria-controls="details" aria-selected="true" @click="toggleMenu('details')">Details</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" :class="activeTab == 'betreuer' ? 'active' : ''" id="betreuer-tab" data-bs-toggle="tab" data-bs-target="#betreuer" type="button" role="tab" aria-controls="betreuer" aria-selected="false" @click="toggleMenu('betreuer')">{{$p.t('projektarbeit', 'betreuerGross')}}</button>
</li>
</ul>
</template>
<div class="tab-content" id="pa_content">
<div class="tab-pane fade show" :class="activeTab == 'details' ? 'active' : ''" id="details" role="tabpanel" aria-labelledby="details-tab">
<div class="row">
<div class="col-12">
<projektarbeit-details ref="projektarbeitDetails" :student="student" @projekttyp-changed="setDefaultStunden">
</projektarbeit-details>
</div>
</div>
</div>
<div :class="statusNew ? '' : 'col-6'" :style="statusNew ? 'display: none' : ''">
<projektbetreuer ref="projektbetreuer" :config="config" @betreuer-saved="reload"></projektbetreuer>
<div class="tab-pane fade show" :class="activeTab == 'betreuer' ? 'active' : ''" id="betreuer" role="tabpanel" aria-labelledby="betreuer-tab">
<div class="row">
<div class="col-12">
<projektbetreuer ref="projektbetreuer" :config="config" @betreuer-saved="reload"></projektbetreuer>
</div>
</div>
</div>
</div>
<template #footer>
<button type="button" class="btn btn-secondary" @click="resetFormData" data-bs-dismiss="modal">{{$p.t('ui', 'abbrechen')}}</button>
<button class="btn btn-primary" @click="saveProjektarbeit()"> {{$p.t('ui', 'speichern')}}</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{$p.t('ui', 'abbrechen')}}</button>
<button v-if="statusNew" class="btn btn-primary" @click="addNewProjektarbeit()"> {{$p.t('ui', 'speichern')}}</button>
<button v-if="!statusNew && activeTab == 'details'" class="btn btn-primary" @click="updateProjektarbeit()"> {{$p.t('ui', 'speichern')}}</button>
</template>
</bs-modal>
@@ -10,7 +10,6 @@ import Vertrag from "./Vertrag.js";
import ApiStvProjektbetreuer from '../../../../../api/factory/stv/projektbetreuer.js';
export default {
name: 'Projektbetreuer',
components: {
CoreFilterCmpt,
BsModal,
@@ -93,7 +93,7 @@ export default {
}
},
{title:"Geschlecht", field:"geschlecht", headerFilter: "list", headerFilterParams: {values:{'m':'männlich','w':'weiblich','x':'divers','u':'unbekannt'}, listOnEmpty:true, autocomplete:true}},
{title:"Sem.", field:"semester_berechnet", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title:"Sem.", field:"semester", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title:"Verb.", field:"verband", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title:"Grp.", field:"gruppe", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
{title:"Studiengang", field:"studiengang", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}},
@@ -171,7 +171,7 @@ export default {
selectableRows: true,
selectableRowsRangeMode: 'click',
index: 'prestudent_id',
persistenceID: 'stv-list-20260223_01'
persistenceID: 'stv-list',
},
tabulatorEvents: [
{
@@ -195,15 +195,7 @@ export default {
},
{
event: 'dataLoaded',
handler: data => {
if (Array.isArray(data)) {
this.count = data.length;
this.allPrestudents = data.map(item => item.prestudent_id);
} else {
this.count = 0;
this.allPrestudents = [];
}
}
handler: data => this.count = data.length
},
{
event: 'dataFiltered',
@@ -245,8 +237,7 @@ export default {
dragSource: [],
oldScrollUrl: '',
oldScrollLeft: 0,
oldScrollTop: 0,
allPrestudents: []
oldScrollTop: 0
}
},
computed: {
@@ -283,7 +274,6 @@ export default {
};
});
},
//TODO(Manu) check: replace download or additional entry?
downloadConfig() {
return {
csv: {
@@ -301,21 +291,6 @@ export default {
.replace(/\//g, '_');
return "StudentList_" + today + ".csv";
},
selectedPrestudents() {
if (this.selected && this.selected.length > 0) {
return this.selected.map(item => item.prestudent_id);
} else {
// fallback whole list of prestudents
return this.allPrestudents || [];
}
},
linkXLS(){
return FHC_JS_DATA_STORAGE_OBJECT.app_root
+ 'content/statistik/studentenexportextended.xls.php?'
+ '&studiensemester_kurzbz=' + this.currentSemester
+ '&data=' + this.selectedPrestudents.join(";");
},
},
created: function() {
if(this.tagsEnabled) {
@@ -355,7 +330,7 @@ export default {
ersatzkennzeichen: capitalize(this.$p.t('person/ersatzkennzeichen')),
gebdatum: capitalize(this.$p.t('person/geburtsdatum')),
geschlecht: capitalize(this.$p.t('person/geschlecht')),
semester_berechnet: capitalize(this.$p.t('lehre/sem')),
semester: capitalize(this.$p.t('lehre/sem')),
verband: capitalize(this.$p.t('lehre/verb')),
gruppe: capitalize(this.$p.t('lehre/grp')),
studiengang: capitalize(this.$p.t('lehre/studiengang')),
@@ -416,17 +391,15 @@ export default {
actionNewPrestudent() {
this.$refs.new.open();
},
rowSelectionChanged(data, rows, selected, deselected) {
rowSelectionChanged(data, rows) {
this.selectedcount = data.length;
if(selected.length > 0 || deselected.length > 0){
this.lastSelected = this.selected;
this.$emit('update:selected', data);
}
this.lastSelected = this.selected;
//for tags
this.selectedRows = this.$refs.table.tabulator.getSelectedRows();
this.selectedColumnValues = this.selectedRows.filter(row => row.getData().prestudent_id !== undefined && row.getData().prestudent_id).map(row => row.getData().prestudent_id);
this.$emit('update:selected', data);
},
autoSelectRows(data) {
if (Array.isArray(this.lastSelected) && this.lastSelected.length){
@@ -573,10 +546,6 @@ export default {
this.changeFocus(this.focusObj, el);
}
},
clearSelection(){
this.lastSelected = [];
this.$emit('update:selected',[]);
},
//methods tags
addedTag(addedTag)
{
@@ -631,7 +600,7 @@ export default {
// TODO(chris): focusin, focusout, keydown and tabindex should be in the filter component
// TODO(chris): filter component column chooser has no accessibilty features
template: `
<div class="stv-list h-100 pt-3">
<div class="stv-list h-100 pt-3">
<div
class="tabulator-container d-flex flex-column h-100"
:class="{'has-filter': filter.length}"
@@ -658,27 +627,6 @@ export default {
@headerFilterOn="handleHeaderFilter"
>
<!--
<template #actions>
<div>
<button
class="btn btn-outline-success sm mb-1"
:title="'Export ' + selectedPrestudents.length + ' prestudent(s) to Excel'"
>
<i class="fas fa-file-excel fa-xl"></i>
</button>
</div>
</template>
-->
<template #additional>
<div class="pe-2">
<a :href="linkXLS" target="_blank">
<i class="fas fa-file-excel fa-xl text-success" :title="$p.t('stv', 'text_exportXLS', { count: selectedPrestudents.length })"></i>
</a>
</div>
</template>
<template #actions>
<core-tag ref="tagComponent"
v-if="tagsEnabled"
-1
View File
@@ -723,7 +723,6 @@ export const CoreFilterCmpt = {
<span class="fa-solid fa-xl fa-table-columns"></span>
</a>
<table-download class="btn btn-link px-0 fhc-text" :tabulator="tabulator" :config="download"></table-download>
<slot name="additional"></slot>
</div>
</div>
@@ -1,147 +0,0 @@
export default {
name: 'HorizontalSplit',
props: {
defaultRatio: {
type: Array,
default: () => [50, 50]
}
},
data: function () {
return {
availWidth: 0,
leftwidth: 0,
rightwidth: 0,
mousePosX: 0,
resize: false,
hsplitterOffset: 0,
selfOffsetLeft: 0
};
},
template: `
<div ref="horizontalsplit" class="horizontalsplit-container">
<div ref="leftpanel" class="horizontalsplitted"
:style="{ width: this.leftwidthcss }">
<slot name="left">
<p>Left Panel</p>
</slot>
</div>
<div ref="hsplitter" class="horizontalsplitter"
:class="this.leftOrRightClass" @mousedown="this.dragStart">
<div class="splitactions horizontal" :class="this.leftOrRightClass">
<span @click="this.collapseRight" class="splitaction">
<i class="fas fa-angle-right text-muted"></i>
</span>
<span @dblclick="this.showBoth" class="splitaction resize">
<i class="fas fa-grip-lines-vertical text-muted"></i>
</span>
<span @click="this.collapseLeft" class="splitaction">
<i class="fas fa-angle-left text-muted"></i>
</span>
</div>
</div>
<div ref="rightpanel" class="horizontalsplitted"
:style="{ width: this.rightwidthcss }">
<slot name="right">
<p/>
</slot>
</div>
</div>
`,
mounted: function () {
this.calcWidths();
this.trackHorizontalSplitterOffsetLeft();
window.addEventListener('resize', this.calcWidths);
},
updated: function () {
this.trackHorizontalSplitterOffsetLeft();
},
beforeDestroy: function () {
window.removeEventListener('resize', this.calcWidths);
},
methods: {
calcWidths: function () {
var oldavailWidth = this.availWidth;
this.selfOffsetLeft = this.$refs.horizontalsplit.offsetLeft;
this.availWidth = this.$refs.horizontalsplit.offsetWidth - this.$refs.hsplitter.offsetWidth;
if ((this.leftwidth === 0 && this.rightwidth === 0) || oldavailWidth === 0) {
this.leftwidth = Math.floor(this.availWidth * (this.defaultRatio[0] / 100));
} else {
this.leftwidth = Math.floor(((this.leftwidth * 100) / oldavailWidth) / 100 * this.availWidth);
}
this.rightwidth = this.availWidth - this.leftwidth;
},
collapseLeft: function () {
this.calcWidths();
this.leftwidth = 0;
this.rightwidth = this.availWidth;
},
collapseRight: function () {
this.calcWidths();
this.leftwidth = this.availWidth;
this.rightwidth = 0;
},
showBoth: function () {
this.leftwidth = Math.floor(this.availWidth * (this.defaultRatio[0] / 100));
this.rightwidth = this.availWidth - this.leftwidth;
},
isCollapsed: function () {
if (this.leftwidth === 0) {
return 'left';
} else if (this.rightwidth === 0) {
return 'right';
} else {
return false;
}
},
dragStart: function (e) {
e.preventDefault();
e.stopPropagation();
window.addEventListener('mouseup', this.dragEnd);
window.addEventListener('mousemove', this.drag);
this.resize = true;
this.mousePosX = e.clientX;
},
drag: function (e) {
if (!this.resize) {
return;
}
e.preventDefault();
e.stopPropagation();
var offsetX = e.clientX - this.mousePosX;
this.leftwidth = this.leftwidth + offsetX;
if (this.leftwidth < 0) {
this.leftwidth = 0;
}
if (this.leftwidth > this.availWidth) {
this.leftwidth = this.availWidth;
}
this.rightwidth = this.availWidth - this.leftwidth;
this.mousePosX = e.clientX;
},
dragEnd: function (e) {
e.preventDefault();
e.stopPropagation();
window.removeEventListener('mousemove', this.drag);
window.removeEventListener('mouseup', this.dragEnd);
this.resize = false;
this.mousePosX = e.clientX;
},
trackHorizontalSplitterOffsetLeft: function () {
this.hsplitterOffset = this.$refs.hsplitter.offsetLeft;
}
},
computed: {
leftOrRightClass: function () {
return ((this.hsplitterOffset - this.selfOffsetLeft) <= Math.floor(this.availWidth / 2))
? 'left'
: 'right';
},
leftwidthcss: function () {
return this.leftwidth + 'px';
},
rightwidthcss: function () {
return this.rightwidth + 'px';
}
}
};
@@ -1,11 +1,5 @@
export default {
name: 'VerticalSplit',
props: {
defaultRatio: {
type: Array,
default: () => [50, 50]
}
},
data: function() {
return {
availHeight: 0,
@@ -56,22 +50,17 @@ export default {
updated: function() {
this.trackVerticalSplitterOffsetTop();
},
beforeDestroy: function () {
window.removeEventListener('resize', this.calcHeights);
},
methods: {
calcHeights: function() {
var windowheight = window.innerHeight;
var oldavailHeight = this.availHeight;
this.selfOffsetTop = this.$refs.verticalsplit.offsetTop;
this.availHeight = windowheight - this.selfOffsetTop - this.$refs.vsplitter.offsetHeight;
if( (this.topheight === 0 && this.bottomheight === 0) || oldavailHeight === 0 ) {
this.topheight = Math.floor(this.availHeight * (this.defaultRatio[0] / 100));
this.topheight = Math.floor(this.availHeight/2);
} else {
this.topheight = Math.floor( ((((this.topheight * 100) / oldavailHeight) / 100) * this.availHeight) );
}
this.bottomheight = this.availHeight - this.topheight;
},
collapseTop: function() {
@@ -85,8 +74,8 @@ export default {
this.bottomheight = 0;
},
showBoth: function() {
this.topheight = Math.floor(this.availHeight * (this.defaultRatio[0] / 100));
this.bottomheight = this.availHeight - this.topheight
this.topheight = Math.floor(this.availHeight/2);
this.bottomheight = Math.floor(this.availHeight/2);
},
isCollapsed: function() {
if( this.topheight === 0 ) {
+16 -1
View File
@@ -1,4 +1,19 @@
export function capitalize(string) {
if (!string) return '';
return string[0].toUpperCase() + string.slice(1);
// ref unwrap if we receive such
if (Vue.isRef(string)) {
return Vue.computed(() => {
const val = Vue.unref(string);
return (val && typeof val === 'string') ? val.charAt(0).toUpperCase() + val.slice(1) : '';
});
}
// just a plain string, return a plain string
if (typeof string === 'string') {
return string.charAt(0).toUpperCase() + string.slice(1);
}
return '';
}
+9
View File
@@ -0,0 +1,9 @@
export function debounce(fn, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn(...args)
}, delay);
};
}
+23
View File
@@ -0,0 +1,23 @@
export function centeredTextFormatter(cell) {
const longForm = cell.getValue()
if(!longForm) return
const data = cell.getData()
const entry = Object.entries(data).find(entry => entry[1] == longForm)
// shortFormKey must have same keyname as longForm but with 'Short' appended
const shortForm = data[entry[0]+'Short']
if(shortForm && longForm) {
return `<div style="display: flex; justify-content: start; align-items: center; height: 100%; width: 100%;">
<span class="full-text" style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; margin: 0px;">
${longForm}
</span>
<span class="short-text" style="font-weight: bold; display: none;">
${shortForm}
</span>
</div>`;
} else {
return '<div style="display: flex; justify-content: start; align-items: center; height: 100%">' +
'<p style="max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; margin: 0px;">'+longForm+'</p></div>'
}
}
+1
View File
@@ -150,6 +150,7 @@ $berechtigungen = array(
array('lehre','Berechtigung fuer CIS-Seite'),
array('lehre/abgabetool','Projektabgabetool, Studentenansicht'),
array('lehre/abgabetool:download','Download von Projektarbeitsabgaben'),
array('lehre/benotungstool','Cis4 Gesamtnoteneingabe'),
array('lehre/freifach','Freifachverwaltung'),
array('lehre/lehrfach','Lehrfachverwaltung'),
array('lehre/lehrfach:begrenzt','Lehrfachverwaltung - nur aktiv aenderbar, nur aktive LF werden angezeigt'),
@@ -0,0 +1,17 @@
<?php
if (! defined('DB_NAME')) exit('No direct script access allowed');
if($result = $db->db_query("SELECT 1 FROM public.tbl_vorlage WHERE vorlage_kurzbz = 'Notenfreigabe'"))
{
if($db->db_num_rows($result) === 0)
{
$qry = "INSERT INTO public.tbl_vorlage (vorlage_kurzbz, bezeichnung, anmerkung, mimetype)
VALUES ('Notenfreigabe', 'Notenfreigabe', 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 Notenfreigabe hinzugefuegt";
}
}
+1472 -144
View File
File diff suppressed because it is too large Load Diff