mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-06-01 12:19:28 +00:00
Compare commits
207 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5aded99999 | |||
| f780553773 | |||
| 53bce69c6f | |||
| 20c68d675c | |||
| dfc8fdf44f | |||
| 4a26d7a89a | |||
| 16f57a1bce | |||
| 1cf3c18841 | |||
| 1dd9ff0daf | |||
| 011e93720e | |||
| ff876afbb3 | |||
| dd13c73415 | |||
| e89ab9b92f | |||
| 4e59173d6c | |||
| df28d7331b | |||
| 2d0a2f3024 | |||
| 781e145721 | |||
| e984425e36 | |||
| 2eb58e3346 | |||
| ab4039dbbd | |||
| 2c1702a20b | |||
| 72a5f35b0e | |||
| 076ae15abd | |||
| fbc5f95340 | |||
| ff3a25a5ec | |||
| 2dd5a95232 | |||
| 2a21bbf062 | |||
| d9a80e5ef7 | |||
| 2cadee1599 | |||
| 6c26fde210 | |||
| bc908b7fe9 | |||
| 26d468aa6f | |||
| 0f7188a347 | |||
| 3e832f9526 | |||
| 392cfbdc4e | |||
| 0751aa5a0f | |||
| e57846566e | |||
| 6c818e5c30 | |||
| 20f043abc6 | |||
| 416451eb0b | |||
| 3ab7a61a47 | |||
| 5571353464 | |||
| 95d85c7f5b | |||
| d7e509979a | |||
| 91a5b2d4fc | |||
| 5171a7b7b3 | |||
| b41c8acddd | |||
| 06bc1ebcd4 | |||
| 999827b3ec | |||
| 38ea481177 | |||
| dbf945dfe5 | |||
| 21065a3c95 | |||
| 79b5defb63 | |||
| b90dabeb2c | |||
| 2f1edfeeab | |||
| fac320bfce | |||
| 6c2820f900 | |||
| 9a113e2993 | |||
| 89e0326435 | |||
| 1a37273a9e | |||
| d14b9e2ab5 | |||
| d926e4165b | |||
| 8c88ae401b | |||
| 536e66eed9 | |||
| 6e103480cc | |||
| 6a53c9fae4 | |||
| 28d65ac114 | |||
| 239577e9cf | |||
| a8fb45adc6 | |||
| 9316016d24 | |||
| 8d815d40b6 | |||
| 33b5c370b1 | |||
| cdf63840b0 | |||
| cf14501311 | |||
| 187b4a6e4b | |||
| 4ab9056700 | |||
| 9a281dfa71 | |||
| 0b3f7d1fe3 | |||
| a9d82de25c | |||
| 414d8bd383 | |||
| eb8b4986d7 | |||
| 6cc09969dd | |||
| 1a813e52ce | |||
| afa765c4ba | |||
| 16b238124a | |||
| 4def45907b | |||
| 202e6e88d2 | |||
| 3b2473039f | |||
| 59d1ca3409 | |||
| 1d26303333 | |||
| 8f73489073 | |||
| ff08ca140c | |||
| 61a9feb8fd | |||
| 21fdf31518 | |||
| 3af9397689 | |||
| fef756f508 | |||
| 131edf1293 | |||
| 6787b9b553 | |||
| 1c2491385f | |||
| 97baaf6797 | |||
| 4e88765a83 | |||
| aac26f6720 | |||
| 3b3e75003f | |||
| ab699aafdc | |||
| 98bdb8c526 | |||
| 94f742187e | |||
| 70ebe34f1f | |||
| 992cb6b310 | |||
| 917a9ee707 | |||
| d577ac6d54 | |||
| 5b5f6ac0b9 | |||
| 2aecc6e0f2 | |||
| 4b064f566a | |||
| 2a86a70386 | |||
| 8ab83eaf41 | |||
| 262b170244 | |||
| e21f35b880 | |||
| 24c8a1c501 | |||
| cfe6e3c805 | |||
| d3b62daea0 | |||
| 35355b28c0 | |||
| 88c82a41ba | |||
| 910e960e4f | |||
| 354e1ccdf4 | |||
| d1911f0f96 | |||
| cdc279b5da | |||
| d003bfa7f1 | |||
| 343a82b89c | |||
| 09a5515121 | |||
| cc23fb0f39 | |||
| 7edddd0566 | |||
| ebafc4576f | |||
| 328fe4256e | |||
| c240eb4a4e | |||
| 38d9d91945 | |||
| 4669598dd9 | |||
| 7daaf79fcc | |||
| d68fa8ce95 | |||
| d61ee51d79 | |||
| a6f81006be | |||
| 5fa374259e | |||
| 9fd033b30e | |||
| e98ed3c74f | |||
| ebe76821e4 | |||
| 3858e38a02 | |||
| 510c35e077 | |||
| a8f680810f | |||
| f1c3c8296f | |||
| 6c90ccfbaa | |||
| 653a320e6c | |||
| 57e7ad6903 | |||
| 290564fd2f | |||
| b9207b5efb | |||
| c58715d95b | |||
| 5c6a8b9966 | |||
| dd713a26db | |||
| fad293fbbf | |||
| 5a1b94f45b | |||
| 344c68bf08 | |||
| f06e59b362 | |||
| 8ca5849b14 | |||
| 01e6a1061c | |||
| 7eb888d2e3 | |||
| 26f67b6798 | |||
| e6eed4be4e | |||
| 4d9ff395e9 | |||
| 114a50ad4e | |||
| 218f434e01 | |||
| 8a53c438e3 | |||
| 3fe15a302c | |||
| dec83bbd21 | |||
| 2354746d4f | |||
| d421b1ccb8 | |||
| 00e019d6fe | |||
| 5b0c115b10 | |||
| d37fac0ff7 | |||
| 91d656ff60 | |||
| 9fd73ca34d | |||
| 2681a85fa5 | |||
| 9a0716af44 | |||
| dc642cfbe7 | |||
| b98e831300 | |||
| 0d73824727 | |||
| 1a0a5c652b | |||
| f4f645b103 | |||
| 37395b70f6 | |||
| a65cb3b03c | |||
| 779641e8e7 | |||
| a5188ce24a | |||
| 39a96885d2 | |||
| 96dbec3218 | |||
| 4b29e7bf8d | |||
| ed69bd74ed | |||
| f0641ddd6d | |||
| 6c2a2e4665 | |||
| 5e929df966 | |||
| 455e0533fe | |||
| a8c4fc7607 | |||
| a26bdfe385 | |||
| df63b8c1b7 | |||
| 707d00b280 | |||
| 9c79d728e8 | |||
| 9c8a1f564e | |||
| 4014083238 | |||
| f69016883c | |||
| c10baca137 | |||
| 60e403a28b |
@@ -64,6 +64,9 @@ $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/OtherLvPlan/.*'] = 'Cis/OtherLvPlan/index/$1';
|
||||
//Route for LV Plan Stg/Semester/Verband/Gruppe
|
||||
$route['Cis/StgOrgLvPlan/.*'] = 'Cis/StgOrgLvPlan/index/$1';
|
||||
|
||||
$route['Abgabetool/Assistenz'] = 'Cis/Abgabetool/Assistenz';
|
||||
$route['Abgabetool/Assistenz/(:any)'] = 'Cis/Abgabetool/Assistenz/$1';
|
||||
|
||||
@@ -31,12 +31,8 @@ class Abgabetool extends Auth_Controller
|
||||
{
|
||||
// TODO: routing from index based on berechtigung?
|
||||
|
||||
$viewData = array(
|
||||
'uid'=>getAuthUID(),
|
||||
);
|
||||
|
||||
if(defined('CIS4') && CIS4) {
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Abgabetool']);
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'Abgabetool']);
|
||||
} else {
|
||||
$this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'Abgabetool']);
|
||||
}
|
||||
@@ -44,12 +40,8 @@ class Abgabetool extends Auth_Controller
|
||||
|
||||
public function Student($student_uid_prop = '')
|
||||
{
|
||||
$viewData = array(
|
||||
'uid'=>getAuthUID(),
|
||||
);
|
||||
|
||||
if(defined('CIS4') && CIS4) {
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'AbgabetoolStudent']);
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'AbgabetoolStudent']);
|
||||
} else {
|
||||
$this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'AbgabetoolStudent', 'student_uid_prop' => $student_uid_prop]);
|
||||
}
|
||||
@@ -57,12 +49,8 @@ class Abgabetool extends Auth_Controller
|
||||
|
||||
public function Mitarbeiter()
|
||||
{
|
||||
$viewData = array(
|
||||
'uid'=>getAuthUID(),
|
||||
);
|
||||
|
||||
if(defined('CIS4') && CIS4) {
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'AbgabetoolMitarbeiter']);
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'AbgabetoolMitarbeiter']);
|
||||
} else {
|
||||
$this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'AbgabetoolMitarbeiter']);
|
||||
}
|
||||
@@ -70,13 +58,8 @@ class Abgabetool extends Auth_Controller
|
||||
|
||||
public function Assistenz($stg_kz_prop = '')
|
||||
{
|
||||
|
||||
$viewData = array(
|
||||
'uid'=>getAuthUID(),
|
||||
);
|
||||
|
||||
if(defined('CIS4') && CIS4) {
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'AbgabetoolAssistenz']);
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'AbgabetoolAssistenz']);
|
||||
} else {
|
||||
$this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'AbgabetoolAssistenz', 'stg_kz_prop' => $stg_kz_prop]);
|
||||
}
|
||||
@@ -84,12 +67,8 @@ class Abgabetool extends Auth_Controller
|
||||
|
||||
public function Deadlines()
|
||||
{
|
||||
$viewData = array(
|
||||
'uid'=>getAuthUID(),
|
||||
);
|
||||
|
||||
if(defined('CIS4') && CIS4) {
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'DeadlinesOverview']);
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'DeadlinesOverview']);
|
||||
} else {
|
||||
$this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'DeadlinesOverview']);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class Auth extends FHC_Controller
|
||||
|
||||
if ($this->form_validation->run())
|
||||
{
|
||||
redirect($this->authlib->getLandingPage('/CisVue/Dashboard'));
|
||||
redirect($this->authlib->getLandingPage('/Cis4'));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -28,12 +28,6 @@ class LvPlan extends Auth_Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
|
||||
$viewData = array(
|
||||
'uid'=>getAuthUID(),
|
||||
'timezone' => $this->config->item('timezone')
|
||||
);
|
||||
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'LvPlan']);
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'LvPlan']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,11 +26,6 @@ class MyLv extends Auth_Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
|
||||
$viewData = array(
|
||||
|
||||
);
|
||||
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'MyLv']);
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'MyLv']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,13 +27,7 @@ class MyLvPlan extends Auth_Controller
|
||||
* @return void
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
|
||||
$viewData = array(
|
||||
'uid'=>getAuthUID(),
|
||||
'timezone' => $this->config->item('timezone')
|
||||
);
|
||||
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'MyLvPlan']);
|
||||
{
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'MyLvPlan']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class OtherLvPlan extends Auth_Controller
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'index' => ['basis/other_lv_plan:r']
|
||||
]);
|
||||
|
||||
// Load Config
|
||||
$this->load->config('calendar');
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'OtherLvPlan']);
|
||||
}
|
||||
}
|
||||
@@ -55,15 +55,7 @@ class Profil extends Auth_Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
|
||||
$this->load->library('ProfilLib');
|
||||
$profil_data = $this->profillib->getView(getAuthUID());
|
||||
$profil_data = hasData($profil_data) ? getData($profil_data) : null;
|
||||
$viewData = array(
|
||||
'editable'=>true,
|
||||
'profil_data' => $profil_data,
|
||||
);
|
||||
$this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'profilIndex']);
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'profilIndex']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,23 +65,13 @@ class Profil extends Auth_Controller
|
||||
*/
|
||||
public function View($uid)
|
||||
{
|
||||
$this->load->library('ProfilLib');
|
||||
$profil_data = $this->profillib->getView($uid);
|
||||
$profil_data = hasData($profil_data) ? getData($profil_data) : null;
|
||||
$viewData = array (
|
||||
'uid' => $uid,
|
||||
'profil_data'=>$profil_data,
|
||||
);
|
||||
if($uid == getAuthUID()){
|
||||
$viewData['editable'] = true;
|
||||
}
|
||||
$this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'profilViewUid']);
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'profilViewUid']);
|
||||
}
|
||||
|
||||
/**
|
||||
* checks whether a specific userID is a mitarbeiter or not (foreword declaration of the function isMitarbeiter in Mitarbeiter_model.php)
|
||||
* checks whether a specific userID is a student or not (foreword declaration of the function isStudent in Student_model.php)
|
||||
* @access public
|
||||
* @param $uid the userID used to check if it is a mitarbeiter
|
||||
* @param $uid the userID used to check if it is a student
|
||||
* @return boolean
|
||||
*/
|
||||
public function isStudent($uid)
|
||||
@@ -119,7 +101,7 @@ class Profil extends Auth_Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the adressen that are marked as zustell from the currenlty logged in user
|
||||
* gets the adressen that are marked as zustell from the currently logged in user
|
||||
* @access public
|
||||
* @return array a list of adresse_id's
|
||||
*/
|
||||
@@ -262,23 +244,23 @@ class Profil extends Auth_Controller
|
||||
$this->GemeindeModel->addDistinct();
|
||||
$this->GemeindeModel->addSelect(["name"]);
|
||||
if ($nation == "A") {
|
||||
if (isset($zip) && $zip > 999 && $zip < 32000) {
|
||||
if (isset($zip) && $zip > 999 && $zip < 32000) {
|
||||
|
||||
$gemeinde_res = $this->GemeindeModel->loadWhere(['plz' => $zip]);
|
||||
if (isError($gemeinde_res)) {
|
||||
show_error("error while trying to query bis.tbl_gemeinde");
|
||||
}
|
||||
$gemeinde_res = hasData($gemeinde_res) ? getData($gemeinde_res) : null;
|
||||
$gemeinde_res = array_map(function ($obj) {
|
||||
return $obj->name;
|
||||
}, $gemeinde_res);
|
||||
echo json_encode($gemeinde_res);
|
||||
|
||||
} else {
|
||||
echo json_encode(error("ortschaftskennziffer code was not valid"));
|
||||
$gemeinde_res = $this->GemeindeModel->loadWhere(['plz' => $zip]);
|
||||
if (isError($gemeinde_res)) {
|
||||
show_error("error while trying to query bis.tbl_gemeinde");
|
||||
}
|
||||
$gemeinde_res = hasData($gemeinde_res) ? getData($gemeinde_res) : null;
|
||||
$gemeinde_res = array_map(function ($obj) {
|
||||
return $obj->name;
|
||||
}, $gemeinde_res);
|
||||
echo json_encode($gemeinde_res);
|
||||
|
||||
} else {
|
||||
echo json_encode(error("ortschaftskennziffer code was not valid"));
|
||||
}
|
||||
} else {
|
||||
echo json_encode(error("Nation was not 'A' (Austria)"));
|
||||
echo json_encode(error("Nation was not 'A' (Austria)"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,6 +732,4 @@ class Profil extends Auth_Controller
|
||||
$zutrittskarte_ausgegebenam = str_replace("-", ".", $zutrittskarte_ausgegebenam);
|
||||
return $zutrittskarte_ausgegebenam;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -25,11 +25,6 @@ class Raumsuche extends Auth_Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
|
||||
$viewData = array(
|
||||
'uid'=>getAuthUID(),
|
||||
);
|
||||
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Raumsuche']);
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'Raumsuche']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class StgOrgLvPlan extends Auth_Controller
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'index' => ['basis/cis:r']
|
||||
]);
|
||||
|
||||
// Load Config
|
||||
$this->load->config('calendar');
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'StgOrgLvPlan']);
|
||||
}
|
||||
}
|
||||
@@ -29,10 +29,7 @@ class Studium extends Auth_Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$viewData = array(
|
||||
|
||||
);
|
||||
$this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'studium']);
|
||||
$this->load->view('CisRouterView/CisRouterView.php',['route' => 'studium']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -13,9 +14,9 @@ class Cis4 extends Auth_Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(
|
||||
array(
|
||||
'index' => 'basis/cis:r'
|
||||
)
|
||||
array(
|
||||
'index' => 'basis/cis:r'
|
||||
)
|
||||
);
|
||||
|
||||
// Load Config
|
||||
@@ -30,16 +31,6 @@ class Cis4 extends Auth_Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->load->model('person/Person_model', 'PersonModel');
|
||||
$personData = getData($this->PersonModel->getByUid(getAuthUID()))[0];
|
||||
|
||||
$viewData = array(
|
||||
'uid' => getAuthUID(),
|
||||
'name' => $personData->vorname,
|
||||
'person_id' => $personData->person_id,
|
||||
'timezone' => $this->config->item('timezone')
|
||||
);
|
||||
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'FhcDashboard']);
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'FhcDashboard']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class Dashboard extends Auth_Controller
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(
|
||||
array(
|
||||
'index' => 'dashboard/benutzer:r'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
|
||||
$this->load->model('person/Person_model','PersonModel');
|
||||
$personData = getData($this->PersonModel->getByUid(getAuthUID()))[0];
|
||||
|
||||
$viewData = array(
|
||||
'uid' => getAuthUID(),
|
||||
'name' => $personData->vorname,
|
||||
'person_id' => $personData->person_id
|
||||
);
|
||||
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData]);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,15 @@ class Bookmark extends FHCAPI_Controller
|
||||
parent::__construct([
|
||||
'getBookmarks' => self::PERM_LOGGED,
|
||||
'delete' => self::PERM_LOGGED,
|
||||
'insert' => self::PERM_LOGGED,
|
||||
'insert' => self::PERM_LOGGED,
|
||||
'update' => self::PERM_LOGGED,
|
||||
]);
|
||||
'changeOrder' => self::PERM_LOGGED,
|
||||
'getAllBookmarkTags' => self::PERM_LOGGED,
|
||||
'getTagFilter' => self::PERM_LOGGED,
|
||||
'addAndUpdateTagFilter' => self::PERM_LOGGED,
|
||||
'isInOverride' => self::PERM_LOGGED,
|
||||
'addWidgetToOverride' => self::PERM_LOGGED,
|
||||
]);
|
||||
|
||||
$this->load->model('dashboard/Bookmark_model', 'BookmarkModel');
|
||||
|
||||
@@ -51,7 +57,7 @@ class Bookmark extends FHCAPI_Controller
|
||||
*/
|
||||
public function getBookmarks()
|
||||
{
|
||||
$this->BookmarkModel->addOrder("bookmark_id");
|
||||
$this->BookmarkModel->addOrder("sort");
|
||||
$bookmarks = $this->BookmarkModel->loadWhere(["uid"=>$this->uid]);
|
||||
|
||||
$bookmarks = $this->getDataOrTerminateWithError($bookmarks);
|
||||
@@ -99,8 +105,12 @@ class Bookmark extends FHCAPI_Controller
|
||||
$url = $this->input->post('url',true);
|
||||
$title = $this->input->post('title',true);
|
||||
$tag = $this->input->post('tag', true);
|
||||
if (is_array($tag)) {
|
||||
$tag = json_encode($tag); // convert PHP array to JSON string
|
||||
}
|
||||
$sort = $this->input->post('sort', true);
|
||||
|
||||
$insert_into_result = $this->BookmarkModel->insert(['uid'=>$this->uid, 'url'=>$url, 'title'=>$title,'tag'=>$tag, 'insertvon'=>$this->uid, 'updateamum'=>NULL, 'updatevon'=>NULL]);
|
||||
$insert_into_result = $this->BookmarkModel->insert(['uid'=>$this->uid, 'url'=>$url, 'title'=>$title,'tag'=>$tag, 'insertvon'=>$this->uid, 'updateamum'=>NULL, 'updatevon'=>NULL, 'sort'=>$sort,]);
|
||||
|
||||
$insert_into_result = $this->getDataOrTerminateWithError($insert_into_result);
|
||||
|
||||
@@ -123,16 +133,195 @@ class Bookmark extends FHCAPI_Controller
|
||||
|
||||
$url = $this->input->post('url',true);
|
||||
$title = $this->input->post('title',true);
|
||||
$tag = $this->input->post('tag', true);
|
||||
if (is_array($tag)) {
|
||||
$tag = json_encode($tag);
|
||||
}
|
||||
|
||||
$now = new DateTime();
|
||||
$now = $now->format('Y-m-d H:i:s');
|
||||
|
||||
$update_result = $this->BookmarkModel->update($bookmark_id,['url'=>$url, 'title'=>$title,'updateamum'=>$now]);
|
||||
$update_result = $this->BookmarkModel->update($bookmark_id,['url'=>$url, 'title'=>$title, 'tag'=>$tag, 'updateamum'=>$now]);
|
||||
|
||||
$update_result = $this->getDataOrTerminateWithError($update_result);
|
||||
|
||||
$this->terminateWithSuccess($update_result);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* changes sort of two bookmarks in the bookmark table
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function changeOrder($bookmark_id1, $bookmark_id2)
|
||||
{
|
||||
|
||||
$result1 = $this->BookmarkModel->load($bookmark_id1);
|
||||
$data1 = $this->getDataOrTerminateWithError($result1);
|
||||
$sort1 = current($data1)->sort;
|
||||
|
||||
$result2 = $this->BookmarkModel->load(["bookmark_id"=>$bookmark_id2]);
|
||||
$data2 = $this->getDataOrTerminateWithError($result2);
|
||||
$sort2 = current($data2)->sort;
|
||||
|
||||
$update_result1 = $this->BookmarkModel->update($bookmark_id1,['sort'=>$sort2,]);
|
||||
$update_result[] = $this->getDataOrTerminateWithError($update_result1);
|
||||
|
||||
$update_result2 = $this->BookmarkModel->update($bookmark_id2,['sort'=>$sort1,]);
|
||||
$update_result[] = $this->getDataOrTerminateWithError($update_result2);
|
||||
|
||||
$this->terminateWithSuccess($update_result);
|
||||
}
|
||||
|
||||
/**
|
||||
* get all the bookmark tags associated to a user
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getAllBookmarkTags()
|
||||
{
|
||||
$this->BookmarkModel->addOrder("sort");
|
||||
$result = $this->BookmarkModel->getAllBookmarkTags($this->uid);
|
||||
|
||||
$bookmarks = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
$this->terminateWithSuccess(current($bookmarks));
|
||||
}
|
||||
|
||||
/**
|
||||
* get all tagFilter of a certain bookmark widget
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getTagFilter($widgetId, $sectionName)
|
||||
{
|
||||
$result = $this->BookmarkModel->getTagFilter($widgetId, $this->uid, $sectionName);
|
||||
$data = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
$this->terminateWithSuccess(current($data));
|
||||
}
|
||||
/**
|
||||
* get all tagFilter of a certain bookmark widget
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function addAndUpdateTagFilter($widgetId, $sectionName)
|
||||
{
|
||||
$tags = $this->input->post('tags',true);
|
||||
if (is_array($tags))
|
||||
{
|
||||
$tags = json_encode($tags);
|
||||
}
|
||||
$result = $this->BookmarkModel->addAndUpdateTagFilter($widgetId, $this->uid, $sectionName, $tags);
|
||||
|
||||
$data = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
$this->terminateWithSuccess($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if a widget has already an entry in the benutzeroverride
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function isInOverride($widgetId, $sectionName)
|
||||
{
|
||||
$result = $this->BookmarkModel->checkOrAddToOverride($widgetId, $this->uid, $sectionName);
|
||||
|
||||
$data = getData($result);
|
||||
if(!$data)
|
||||
$this->terminateWithSuccess([false, 0]);
|
||||
|
||||
$id = current($data)->widgetid;
|
||||
$id = trim($id, '"');
|
||||
|
||||
if ($id != $widgetId)
|
||||
$this->terminateWithSuccess([false, 1]);
|
||||
else
|
||||
$this->terminateWithSuccess([true, null]);
|
||||
}
|
||||
/**
|
||||
* adds widget benutzeroverride
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function addWidgetToOverride($widgetId, $sectionName, $mode, $x, $y, $h, $w)
|
||||
{
|
||||
$this->load->library('dashboard/DashboardLib', null, 'DashboardLib');
|
||||
$dashboard_kurzbz = "CIS";
|
||||
$structure = [
|
||||
"custom" => [
|
||||
"widgets" => []
|
||||
],
|
||||
"general" => [
|
||||
"widgets" => [
|
||||
$widgetId => [
|
||||
"widget" => 3,
|
||||
"config" => new stdClass(),
|
||||
"place" => [
|
||||
"3" => [
|
||||
"x" => $x,
|
||||
"y" => $y,
|
||||
"w" => $w,
|
||||
"h" => $h
|
||||
]
|
||||
],
|
||||
"widgetid" => $widgetId,
|
||||
"custom" => 1,
|
||||
"id" => "insertByBookmarkwidget"
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
$jsonOverrideNew = json_encode($structure, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
//no existing benutzeroverride
|
||||
if($mode == 0)
|
||||
{
|
||||
$override = $this->DashboardLib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $this->uid);
|
||||
|
||||
$override->override = $jsonOverrideNew;
|
||||
|
||||
$result = $this->DashboardLib->insertOrUpdateOverride($override);
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
|
||||
}
|
||||
//benutzeroverride existing, but widget not included
|
||||
elseif($mode == 1)
|
||||
{
|
||||
$overrideExisting = $this->DashboardLib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $this->uid);
|
||||
$override = json_decode($overrideExisting->override, true); // decode as Array
|
||||
|
||||
$newWidget = [
|
||||
"widget" => 3,
|
||||
"config" => new stdClass(),
|
||||
"place" => [
|
||||
"3" => [
|
||||
"x" => $x,
|
||||
"y" => $y,
|
||||
"w" => $w,
|
||||
"h" => $h
|
||||
]
|
||||
],
|
||||
"widgetid" => $widgetId,
|
||||
"custom" => 1,
|
||||
"id" => "insertByBookmarkwidget"
|
||||
];
|
||||
|
||||
$override['general']['widgets'][$widgetId] = $newWidget;
|
||||
|
||||
$jsonOverrideUpdated = json_encode($override, JSON_UNESCAPED_UNICODE);
|
||||
$overrideExisting->override = $jsonOverrideUpdated;
|
||||
|
||||
$result = $this->DashboardLib->insertOrUpdateOverride($overrideExisting);
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
}
|
||||
else
|
||||
$this->terminateWithError("Error: no known mode");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class Cis4FhcApi extends FHCAPI_Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'getViewData' => self::PERM_LOGGED,
|
||||
'dashboardViewData' => self::PERM_LOGGED,
|
||||
]);
|
||||
|
||||
}
|
||||
@@ -36,17 +36,22 @@ class Cis4FhcApi extends FHCAPI_Controller
|
||||
// Public methods
|
||||
|
||||
/**
|
||||
* fetches ViewData
|
||||
*/
|
||||
public function getViewData()
|
||||
* retrieves view data for dashboard view
|
||||
* @access public
|
||||
* @param $uid the userID for which profile is being viewed, null or missing value implies one's own profile
|
||||
*/
|
||||
public function dashboardViewData()
|
||||
{
|
||||
$this->load->model('person/Person_model','PersonModel');
|
||||
$personData = getData($this->PersonModel->getByUid(getAuthUID()))[0];
|
||||
|
||||
$this->load->config('calendar');
|
||||
|
||||
$viewData = array(
|
||||
'uid' => getAuthUID(),
|
||||
'name' => $personData->vorname,
|
||||
'person_id' => $personData->person_id
|
||||
'person_id' => $personData->person_id,
|
||||
'timezone' => $this->config->item('timezone'),
|
||||
);
|
||||
|
||||
$this->terminateWithSuccess($viewData);
|
||||
|
||||
@@ -38,7 +38,8 @@ class Lehre extends FHCAPI_Controller
|
||||
parent::__construct([
|
||||
'lvStudentenMail' => self::PERM_LOGGED,
|
||||
'LV' => self::PERM_LOGGED,
|
||||
'Pruefungen' => self::PERM_LOGGED
|
||||
'Pruefungen' => self::PERM_LOGGED,
|
||||
'semesterAverageGrade' => self::PERM_LOGGED,
|
||||
]);
|
||||
|
||||
}
|
||||
@@ -100,5 +101,49 @@ class Lehre extends FHCAPI_Controller
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* calculates and returns the grade average and weighted average for a specific semester
|
||||
* @param string $studiensemester_kurzbz
|
||||
* @return void
|
||||
*/
|
||||
|
||||
public function semesterAverageGrade($studiensemester_kurzbz)
|
||||
{
|
||||
$this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
|
||||
$semesterLvs = $this->LehrveranstaltungModel->getLvsByStudentWithGrades(getAuthUID(), $studiensemester_kurzbz, getUserLanguage());
|
||||
|
||||
if (isError($semesterLvs))
|
||||
return $this->outputJsonError(getError($semesterLvs));
|
||||
|
||||
$semesterLvsData = getData($semesterLvs);
|
||||
|
||||
$doGradesExist = false;
|
||||
$sum = 0;
|
||||
$count = 0;
|
||||
$sumWeighted = 0;
|
||||
$sumEcts = 0;
|
||||
|
||||
foreach ($semesterLvsData as $lv) {
|
||||
if (!$lv->znote || $lv->znote < 1 || $lv->znote > 5)
|
||||
continue;
|
||||
|
||||
$doGradesExist = true;
|
||||
|
||||
$sum += $lv->znote;
|
||||
$count++;
|
||||
$sumWeighted += $lv->znote * floatval($lv->ects);
|
||||
$sumEcts += floatval($lv->ects);
|
||||
}
|
||||
|
||||
$averageGrade = null;
|
||||
$weightedAverageGrade = null;
|
||||
if ($doGradesExist) {
|
||||
$averageGrade = $sum/$count;
|
||||
$weightedAverageGrade = $sumWeighted/$sumEcts;
|
||||
}
|
||||
|
||||
$this->terminateWithSuccess(['average_grade' => $averageGrade, 'weighted_average_grade' => $weightedAverageGrade]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
use CI3_Events as Events;
|
||||
use \DateTime as DateTime;
|
||||
@@ -33,19 +34,25 @@ class LvPlan extends FHCAPI_Controller
|
||||
|
||||
parent::__construct([
|
||||
'getRoomplan' => self::PERM_LOGGED,
|
||||
'Stunden' => self::PERM_LOGGED,
|
||||
'getReservierungen' => self::PERM_LOGGED,
|
||||
'Stunden' => self::PERM_LOGGED,
|
||||
'getReservierungen' => self::PERM_LOGGED,
|
||||
'LvPlanEvents' => self::PERM_LOGGED,
|
||||
'eventsPersonal' => self::PERM_LOGGED,
|
||||
'eventsLv' => self::PERM_LOGGED,
|
||||
'getLehreinheitStudiensemester' => self::PERM_LOGGED,
|
||||
'studiensemesterDateInterval' => self::PERM_LOGGED,
|
||||
'getLvPlanForStudiensemester' => self::PERM_LOGGED,
|
||||
'getLv' => self::PERM_LOGGED
|
||||
'getLv' => self::PERM_LOGGED,
|
||||
'eventsStgOrg' => self::PERM_LOGGED,
|
||||
'fetchFerienEvents' => self::PERM_LOGGED,
|
||||
'getStudiengaenge' => self::PERM_LOGGED,
|
||||
'getLehrverband' => self::PERM_LOGGED,
|
||||
'permissionOtherLvPlan' => self::PERM_LOGGED,
|
||||
'compactibleEventTypes' => self::PERM_LOGGED,
|
||||
]);
|
||||
|
||||
$this->load->library('LogLib');
|
||||
$this->loglib->setConfigs(array(
|
||||
$this->load->library('LogLib');
|
||||
$this->loglib->setConfigs(array(
|
||||
'classIndex' => 5,
|
||||
'functionIndex' => 5,
|
||||
'lineIndex' => 4,
|
||||
@@ -53,17 +60,17 @@ class LvPlan extends FHCAPI_Controller
|
||||
'dbExecuteUser' => 'RESTful API'
|
||||
));
|
||||
|
||||
$this->load->library('form_validation');
|
||||
$this->load->library('form_validation');
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
|
||||
/**
|
||||
* fetches LvPlan and Moodle events together
|
||||
* @access public
|
||||
*
|
||||
*/
|
||||
* fetches LvPlan and Moodle events together
|
||||
* @access public
|
||||
*
|
||||
*/
|
||||
public function LvPlanEvents()
|
||||
{
|
||||
$hasLv = $this->input->post('lv_id');
|
||||
@@ -83,24 +90,30 @@ class LvPlan extends FHCAPI_Controller
|
||||
// form validation
|
||||
$this->form_validation->set_rules('start_date', "start_date", "required");
|
||||
$this->form_validation->set_rules('end_date', "end_date", "required");
|
||||
|
||||
|
||||
if (!$this->form_validation->run())
|
||||
$this->terminateWithValidationErrors($this->form_validation->error_array());
|
||||
|
||||
// storing the post parameter in local variables
|
||||
$start_date = $this->input->post('start_date', true);
|
||||
$end_date = $this->input->post('end_date', true);
|
||||
$uid = $this->input->post('uid', true);
|
||||
|
||||
// disallow accessing other user's lv plan if missing permission
|
||||
if ($uid && $uid !== getAuthUID() && !$this->permissionlib->isBerechtigt('basis/other_lv_plan')) {
|
||||
$this->terminateWithError("Missing permission to view other users' timetables!");
|
||||
}
|
||||
|
||||
// fetching lvplan events
|
||||
$result = $this->stundenplanlib->getEventsUser($start_date, $end_date);
|
||||
$result = $this->stundenplanlib->getEventsUser($start_date, $end_date, $uid);
|
||||
$lvplanEvents = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
// fetching moodle events
|
||||
$moodleEvents = $this->fetchMoodleEvents($start_date, $end_date);
|
||||
$moodleEvents = $uid ? [] : $this->fetchMoodleEvents($start_date, $end_date);
|
||||
|
||||
// fetching ferien events
|
||||
$ferienEvents = $this->fetchFerienEvents($start_date, $end_date);
|
||||
|
||||
$ferienEvents = $this->fetchFerienEvents($start_date, $end_date, $uid);
|
||||
|
||||
|
||||
$this->terminateWithSuccess(array_merge(
|
||||
$lvplanEvents,
|
||||
@@ -109,6 +122,45 @@ class LvPlan extends FHCAPI_Controller
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* fetches LvPlan for studiengang / semester / verband / gruppe
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function eventsStgOrg()
|
||||
{
|
||||
$this->load->library('StundenplanLib');
|
||||
|
||||
// form validation
|
||||
$this->form_validation->set_rules('start_date', "start_date", "required");
|
||||
$this->form_validation->set_rules('end_date', "end_date", "required");
|
||||
//$this->form_validation->set_rules('stg_kz', "stg_kz", "required"); //no validation show empty calendar
|
||||
|
||||
if (!$this->form_validation->run()) {
|
||||
$this->terminateWithValidationErrors($this->form_validation->error_array());
|
||||
$stgOrgEvents = [];
|
||||
$ferienEvents = [];
|
||||
} else {
|
||||
$start_date = $this->input->post('start_date', true);
|
||||
$end_date = $this->input->post('end_date', true);
|
||||
$stg_kz = $this->input->post('stg_kz', true);
|
||||
$sem = $this->input->post('sem', true);
|
||||
$verband = $this->input->post('verband', true);
|
||||
$gruppe = $this->input->post('gruppe', true);
|
||||
|
||||
$result = $this->stundenplanlib->getEventsStgOrg($start_date, $end_date, $stg_kz, $sem, $verband, $gruppe);
|
||||
$stgOrgEvents = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
$result = $this->stundenplanlib->fetchFerienTageEvents($start_date, $end_date, $stg_kz);
|
||||
$ferienEvents = $this->getDataOrTerminateWithError($result);
|
||||
}
|
||||
|
||||
$this->terminateWithSuccess(array_merge(
|
||||
$stgOrgEvents,
|
||||
$ferienEvents
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* fetches LvPlan and Ferien events together for the lv
|
||||
*
|
||||
@@ -122,7 +174,7 @@ class LvPlan extends FHCAPI_Controller
|
||||
$this->form_validation->set_rules('start_date', "start_date", "required");
|
||||
$this->form_validation->set_rules('end_date', "end_date", "required");
|
||||
$this->form_validation->set_rules('lv_id', "lv_id", "required|integer");
|
||||
|
||||
|
||||
if (!$this->form_validation->run())
|
||||
$this->terminateWithValidationErrors($this->form_validation->error_array());
|
||||
|
||||
@@ -137,7 +189,6 @@ class LvPlan extends FHCAPI_Controller
|
||||
|
||||
// fetching ferien events
|
||||
$ferienEvents = $this->fetchFerienEvents($start_date, $end_date);
|
||||
|
||||
|
||||
$this->terminateWithSuccess(array_merge(
|
||||
$lvplanEvents,
|
||||
@@ -146,40 +197,42 @@ class LvPlan extends FHCAPI_Controller
|
||||
}
|
||||
|
||||
//TODO: delete this function if we don't use the old calendar export endpoints anymore
|
||||
public function studiensemesterDateInterval($date){
|
||||
$this->load->model('organisation/Studiensemester_model','StudiensemesterModel');
|
||||
$studiensemester =$this->StudiensemesterModel->getByDate(date_format(date_create($date),'Y-m-d'));
|
||||
$studiensemester =current($this->getDataOrTerminateWithError($studiensemester));
|
||||
public function studiensemesterDateInterval($date)
|
||||
{
|
||||
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
|
||||
$studiensemester = $this->StudiensemesterModel->getByDate(date_format(date_create($date), 'Y-m-d'));
|
||||
$studiensemester = current($this->getDataOrTerminateWithError($studiensemester));
|
||||
$this->terminateWithSuccess($studiensemester);
|
||||
}
|
||||
|
||||
public function getLvPlanForStudiensemester($studiensemester,$lvid){
|
||||
public function getLvPlanForStudiensemester($studiensemester, $lvid)
|
||||
{
|
||||
$this->load->library('StundenplanLib');
|
||||
$this->load->model('organisation/Studiensemester_model','StudiensemesterModel');
|
||||
|
||||
$studiensemester_result = $this->StudiensemesterModel->loadWhere(["studiensemester_kurzbz"=>$studiensemester]);
|
||||
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
|
||||
|
||||
$studiensemester_result = $this->StudiensemesterModel->loadWhere(["studiensemester_kurzbz" => $studiensemester]);
|
||||
$studiensemester_result = current($this->getDataOrTerminateWithError($studiensemester_result));
|
||||
$timespan_start = new DateTime($studiensemester_result->start);
|
||||
$timespan_ende = new DateTime($studiensemester_result->ende);
|
||||
$lvplan = $this->stundenplanlib->getStundenplan(date_format($timespan_start, 'Y-m-d'),date_format($timespan_ende, 'Y-m-d'), $lvid);
|
||||
$lvplan = $this->stundenplanlib->getStundenplan(date_format($timespan_start, 'Y-m-d'), date_format($timespan_ende, 'Y-m-d'), $lvid);
|
||||
$this->terminateWithSuccess($lvplan);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* fetches Stunden layout from database
|
||||
* @access public
|
||||
*
|
||||
*/
|
||||
public function Stunden()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* fetches Stunden layout from database
|
||||
* @access public
|
||||
*
|
||||
*/
|
||||
public function Stunden()
|
||||
{
|
||||
$this->load->model('ressource/Stunde_model', 'StundeModel');
|
||||
|
||||
$this->StundeModel->addOrder('stunde', 'ASC');
|
||||
$stunden = $this->StundeModel->load();
|
||||
|
||||
$stunden = $this->getDataOrTerminateWithError($stunden);
|
||||
$stunden = $this->getDataOrTerminateWithError($stunden);
|
||||
|
||||
$this->terminateWithSuccess($stunden);
|
||||
}
|
||||
@@ -210,10 +263,10 @@ class LvPlan extends FHCAPI_Controller
|
||||
$roomplan_data = $this->stundenplanlib->getRoomplan($ort_kurzbz, $start_date, $end_date);
|
||||
|
||||
$roomplan_data = $this->getDataOrTerminateWithError($roomplan_data);
|
||||
|
||||
|
||||
$this->terminateWithSuccess($roomplan_data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gets the reservierungen of a room if the ort_kurzbz parameter is
|
||||
* supplied otherwise gets the reservierungen of the lvplan of a student
|
||||
@@ -226,25 +279,27 @@ class LvPlan extends FHCAPI_Controller
|
||||
{
|
||||
$this->form_validation->set_rules('start_date', "StartDate", "required");
|
||||
$this->form_validation->set_rules('end_date', "EndDate", "required");
|
||||
|
||||
|
||||
if (!$this->form_validation->run())
|
||||
$this->terminateWithValidationErrors($this->form_validation->error_array());
|
||||
|
||||
// storing the post parameter in local variables
|
||||
$start_date = $this->input->post('start_date', true);
|
||||
$end_date = $this->input->post('end_date', true);
|
||||
$uid = $this->input->post('uid', true);
|
||||
|
||||
// get data
|
||||
$this->load->library('StundenplanLib');
|
||||
|
||||
$result = $this->stundenplanlib->getReservierungen($start_date, $end_date, $ort_kurzbz);
|
||||
$result = $this->stundenplanlib->getReservierungen($start_date, $end_date, $ort_kurzbz, $uid);
|
||||
|
||||
$result = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
}
|
||||
|
||||
public function getLehreinheitStudiensemester($lehreinheit_id){
|
||||
public function getLehreinheitStudiensemester($lehreinheit_id)
|
||||
{
|
||||
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
|
||||
$this->LehreinheitModel->addSelect(["studiensemester_kurzbz"]);
|
||||
$result = $this->LehreinheitModel->load($lehreinheit_id);
|
||||
@@ -287,6 +342,68 @@ class LvPlan extends FHCAPI_Controller
|
||||
return $this->terminateWithSuccess(current($result));
|
||||
}
|
||||
|
||||
public function getStudiengaenge()
|
||||
{
|
||||
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||
|
||||
$this->StudiengangModel->addOrder('typ');
|
||||
$this->StudiengangModel->addOrder('kurzbz');
|
||||
$result = $this->StudiengangModel->loadWhere([
|
||||
'aktiv' => true
|
||||
]);
|
||||
|
||||
$data = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
return $this->terminateWithSuccess($data);
|
||||
}
|
||||
|
||||
public function getLehrverband($studiengang_kz, $semester = null, $verband = null)
|
||||
{
|
||||
$this->load->model('organisation/Lehrverband_model', 'LehrverbandModel');
|
||||
|
||||
$where = [
|
||||
'aktiv' => true,
|
||||
'studiengang_kz' => $studiengang_kz,
|
||||
];
|
||||
|
||||
if ($semester !== null && $semester !== 'null' && $semester !== 'undefined') {
|
||||
$where['semester'] = $semester;
|
||||
}
|
||||
if ($verband !== null && $verband !== 'null' && $verband !== 'undefined') {
|
||||
$where['verband'] = $verband;
|
||||
}
|
||||
|
||||
$this->LehrverbandModel->addOrder('studiengang_kz');
|
||||
$this->LehrverbandModel->addOrder('semester');
|
||||
$this->LehrverbandModel->addOrder('verband');
|
||||
$this->LehrverbandModel->addOrder('gruppe');
|
||||
$result = $this->LehrverbandModel->loadWhere($where);
|
||||
|
||||
$data = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
return $this->terminateWithSuccess($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user has permission to view other users' timetables
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function permissionOtherLvPlan()
|
||||
{
|
||||
$this->terminateWithSuccess($this->permissionlib->isBerechtigt('basis/other_lv_plan'));
|
||||
}
|
||||
|
||||
/**
|
||||
* get event types which can be compacted in lv plan display
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function compactibleEventTypes()
|
||||
{
|
||||
$this->terminateWithSuccess(["lehreinheit", "reservierung"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* fetch moodle events
|
||||
*
|
||||
@@ -299,19 +416,19 @@ class LvPlan extends FHCAPI_Controller
|
||||
$this->load->config('calendar');
|
||||
|
||||
$tz = new DateTimeZone($this->config->item('timezone'));
|
||||
|
||||
|
||||
$start = new DateTime($start_date);
|
||||
$start->setTimezone($tz);
|
||||
|
||||
|
||||
$end = new DateTime($end_date);
|
||||
$end->setTimezone($tz);
|
||||
$end->modify('+1 day -1 second');
|
||||
|
||||
|
||||
$moodle_events = [];
|
||||
|
||||
|
||||
Events::trigger(
|
||||
'moodleCalendarEvents',
|
||||
function & () use (&$moodle_events) {
|
||||
function &() use (&$moodle_events) {
|
||||
return $moodle_events;
|
||||
},
|
||||
[
|
||||
@@ -331,23 +448,23 @@ class LvPlan extends FHCAPI_Controller
|
||||
* @param string $end_date
|
||||
* @return array
|
||||
*/
|
||||
private function fetchFerienEvents($start_date, $end_date)
|
||||
private function fetchFerienEvents($start_date, $end_date, $uid = null)
|
||||
{
|
||||
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
|
||||
$this->load->model('education/Studentlehrverband_model', 'StudentLehrverbandModel');
|
||||
|
||||
$currentStudiensemester = $this->StudiensemesterModel->getByDate($start_date);
|
||||
$currentStudiensemester = $this->getDataOrTerminateWithError($currentStudiensemester);
|
||||
|
||||
|
||||
if ($currentStudiensemester) {
|
||||
$studentsemester_kurzbz = current($currentStudiensemester)->studiensemester_kurzbz;
|
||||
|
||||
$studiengang = $this->StudentLehrverbandModel->loadWhere([
|
||||
"student_uid" => getAuthUID(),
|
||||
"student_uid" => $uid ?? getAuthUID(),
|
||||
"studiensemester_kurzbz" => $studentsemester_kurzbz
|
||||
]);
|
||||
$studiengang = $this->getDataOrTerminateWithError($studiengang);
|
||||
|
||||
|
||||
if ($studiengang)
|
||||
$studiengang_kz = current($studiengang)->studiengang_kz;
|
||||
else
|
||||
@@ -357,7 +474,7 @@ class LvPlan extends FHCAPI_Controller
|
||||
}
|
||||
|
||||
$ferienEvents = $this->stundenplanlib->fetchFerienTageEvents($start_date, $end_date, $studiengang_kz);
|
||||
|
||||
|
||||
return $this->getDataOrTerminateWithError($ferienEvents);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
<?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 OtherLvPlan extends FHCAPI_Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Object initialization
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'otherLvPlanViewData' => ['basis/other_lv_plan:r'],
|
||||
]);
|
||||
|
||||
$this->load->model('ressource/mitarbeiter_model', 'MitarbeiterModel');
|
||||
$this->load->model('person/Benutzer_model', 'BenutzerModel');
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
|
||||
/**
|
||||
* retrieves viewData for other lv plan view
|
||||
* @access public
|
||||
* @param $uid the userID for which the other lv plan is being viewed
|
||||
*/
|
||||
public function otherLvPlanViewData($uid)
|
||||
{
|
||||
$isMitarbeiterResult = $this->MitarbeiterModel->isMitarbeiter($uid);
|
||||
$isMitarbeiter = getData($isMitarbeiterResult);
|
||||
$isStudent = !$isMitarbeiter;
|
||||
|
||||
$this->BenutzerModel->addSelect(["foto", "vorname", "nachname"]);
|
||||
$this->BenutzerModel->addJoin("tbl_person", "person_id");
|
||||
$personResult = $this->BenutzerModel->load([$uid]);
|
||||
$person = hasData($personResult) ? getData($personResult) : null;
|
||||
|
||||
$viewData = [
|
||||
"user_data" => [
|
||||
"username" => $uid,
|
||||
"is_student" => $isStudent,
|
||||
"is_mitarbeiter" => $isMitarbeiter,
|
||||
"foto" => $person[0]->foto,
|
||||
"vorname" => $person[0]->vorname,
|
||||
"nachname" => $person[0]->nachname,
|
||||
],
|
||||
];
|
||||
|
||||
$this->terminateWithSuccess($viewData);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
// Private methods
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
class Profil extends FHCAPI_Controller
|
||||
{
|
||||
@@ -27,13 +28,13 @@ class Profil extends FHCAPI_Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'fotoSperre' => self::PERM_LOGGED,
|
||||
'fotoSperre' => self::PERM_LOGGED,
|
||||
'getGemeinden' => self::PERM_LOGGED,
|
||||
'getAllNationen' => self::PERM_LOGGED,
|
||||
'isMitarbeiter' => self::PERM_LOGGED,
|
||||
'profilViewData' => self::PERM_LOGGED,
|
||||
]);
|
||||
|
||||
|
||||
$this->load->library('PermissionLib');
|
||||
|
||||
$this->load->model('ressource/mitarbeiter_model', 'MitarbeiterModel');
|
||||
@@ -48,28 +49,37 @@ class Profil extends FHCAPI_Controller
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
public function profilViewData($uid=null){
|
||||
|
||||
/**
|
||||
* retrieves view data for profile view
|
||||
* @access public
|
||||
* @param $uid the userID for which profile is being viewed, null or missing value implies one's own profile
|
||||
*/
|
||||
public function profilViewData($uid = null)
|
||||
{
|
||||
$authUid = getAuthUID();
|
||||
$isProfilOfAuthUser = !$uid || $uid === $authUid;
|
||||
|
||||
$this->load->library('ProfilLib');
|
||||
$editable = false;
|
||||
if(isset($uid) && $uid != null){
|
||||
$profil_data = $this->profillib->getView($uid);
|
||||
if($uid == getAuthUID()){
|
||||
$editable = true;
|
||||
}
|
||||
}else{
|
||||
$editable = true;
|
||||
$profil_data = $this->profillib->getView(getAuthUID());
|
||||
$profileData = $this->profillib->getView($uid ?? $authUid);
|
||||
$profileData = hasData($profileData) ? getData($profileData) : null;
|
||||
|
||||
$viewData = [
|
||||
'editable' => $isProfilOfAuthUser,
|
||||
'profil_data' => $profileData,
|
||||
'permissions' => [
|
||||
'basis/other_lv_plan' => $this->permissionlib->isBerechtigt(('basis/other_lv_plan'))
|
||||
]
|
||||
];
|
||||
|
||||
if ($isProfilOfAuthUser) {
|
||||
$viewData['calendar_sync_urls'] = $this->getCalendarSyncUrlData();
|
||||
}
|
||||
|
||||
$profil_data = hasData($profil_data) ? getData($profil_data) : null;
|
||||
$viewData = array(
|
||||
'editable'=>$editable,
|
||||
'profil_data' => $profil_data,
|
||||
);
|
||||
|
||||
$this->terminateWithSuccess($viewData);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* update column foto_sperre in public.tbl_person
|
||||
* @access public
|
||||
* @param boolean $value new value for the column
|
||||
@@ -77,9 +87,9 @@ class Profil extends FHCAPI_Controller
|
||||
*/
|
||||
public function fotoSperre($value)
|
||||
{
|
||||
if(!isset($value)){
|
||||
$this->terminateWithError("Missing parameter", self::ERROR_TYPE_GENERAL);
|
||||
}
|
||||
if (!isset($value)) {
|
||||
$this->terminateWithError("Missing parameter", self::ERROR_TYPE_GENERAL);
|
||||
}
|
||||
|
||||
$res = $this->PersonModel->update($this->pid, ["foto_sperre" => $value]);
|
||||
if (isError($res)) {
|
||||
@@ -87,10 +97,10 @@ class Profil extends FHCAPI_Controller
|
||||
}
|
||||
$this->PersonModel->addSelect("foto_sperre");
|
||||
$res = $this->PersonModel->load($this->pid);
|
||||
|
||||
$res = $this->getDataOrTerminateWithError($res);
|
||||
|
||||
$this->terminateWithSuccess(current($res));
|
||||
|
||||
$res = $this->getDataOrTerminateWithError($res);
|
||||
|
||||
$this->terminateWithSuccess(current($res));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +119,7 @@ class Profil extends FHCAPI_Controller
|
||||
if (isError($nation_res)) {
|
||||
$this->terminateWithError("error while trying to query table codex.tbl_nation", self::ERROR_TYPE_GENERAL);
|
||||
}
|
||||
|
||||
|
||||
$nation_res = $this->getDataOrTerminateWithError($nation_res);
|
||||
|
||||
$this->terminateWithSuccess($nation_res);
|
||||
@@ -117,30 +127,30 @@ class Profil extends FHCAPI_Controller
|
||||
|
||||
public function getGemeinden($nation, $zip)
|
||||
{
|
||||
if(!isset($nation) || !isset($zip)){
|
||||
if (!isset($nation) || !isset($zip)) {
|
||||
echo json_encode(error("Missing parameters"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$this->load->model('codex/Gemeinde_model', "GemeindeModel");
|
||||
|
||||
|
||||
|
||||
$gemeinde_res = $this->GemeindeModel->getGemeindeByPlz($zip);
|
||||
|
||||
|
||||
if (isError($gemeinde_res)) {
|
||||
$this->terminateWithError(getError($gemeinde_res),self::ERROR_TYPE_GENERAL);
|
||||
$this->terminateWithError(getError($gemeinde_res), self::ERROR_TYPE_GENERAL);
|
||||
}
|
||||
$gemeinde_res = $this->getDataOrTerminateWithError($gemeinde_res);
|
||||
|
||||
|
||||
/* $gemeinde_res = array_map(function ($obj) {
|
||||
return $obj->ortschaftsname;
|
||||
}, $gemeinde_res); */
|
||||
|
||||
$this->terminateWithSuccess($gemeinde_res);
|
||||
|
||||
$this->terminateWithSuccess($gemeinde_res);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* checks whether a specific userID is a mitarbeiter or not (foreword declaration of the function isMitarbeiter in Mitarbeiter_model.php)
|
||||
* @access public
|
||||
@@ -150,23 +160,48 @@ class Profil extends FHCAPI_Controller
|
||||
public function isMitarbeiter($uid)
|
||||
{
|
||||
|
||||
if(!$uid) $this->terminateWithError("No uid provided", self::ERROR_TYPE_GENERAL);
|
||||
|
||||
|
||||
if (!$uid)
|
||||
$this->terminateWithError("No uid provided", self::ERROR_TYPE_GENERAL);
|
||||
|
||||
|
||||
$result = $this->MitarbeiterModel->isMitarbeiter($uid);
|
||||
|
||||
|
||||
if (isError($result)) {
|
||||
$this->terminateWithError("error when calling Mitarbeiter_model function isMitarbeiter with uid " . $uid, self::ERROR_TYPE_GENERAL);
|
||||
}
|
||||
|
||||
$result = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
// Private methods
|
||||
|
||||
|
||||
/**
|
||||
* gets the identifier, phrase, and url for each calendar sync option
|
||||
* @access private
|
||||
* @return array array of arrays, where each child array is a sync option
|
||||
*/
|
||||
private function getCalendarSyncUrlData()
|
||||
{
|
||||
return [
|
||||
[
|
||||
"identifier" => "cal_dav",
|
||||
"labelPhrase" => "profil/calendar_sync_cal_dav",
|
||||
"url" => APP_ROOT . "webdav/lvplan.php/calendars/" . $this->uid . "/LVPlan-" . $this->uid,
|
||||
],
|
||||
[
|
||||
"identifier" => "cal_dav_principal",
|
||||
"labelPhrase" => "profil/calendar_sync_cal_dav_principal",
|
||||
"url" => APP_ROOT . "webdav/lvplan.php/principals/" . $this->uid,
|
||||
],
|
||||
[
|
||||
"identifier" => "i_cal",
|
||||
"labelPhrase" => "profil/calendar_sync_i_cal",
|
||||
"url" => APP_ROOT . "webdav/google.php?cal=" . encryptData($this->uid, LVPLAN_CYPHER_KEY) . "&" . microtime(true),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<?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 StgOrgLvPlan extends FHCAPI_Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Object initialization
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'stgOrgLvPlanViewData' => self::PERM_LOGGED,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
|
||||
/**
|
||||
* fetches view data for stg org lv plan
|
||||
* @access public
|
||||
*/
|
||||
public function stgOrgLvPlanViewData()
|
||||
{
|
||||
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||
$this->StudiengangModel->addOrder('typ');
|
||||
$this->StudiengangModel->addOrder('kurzbz');
|
||||
$result = $this->StudiengangModel->loadWhere([
|
||||
'aktiv' => true
|
||||
]);
|
||||
$studiengaenge = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
$viewData = array(
|
||||
'studiengaenge' => $studiengaenge,
|
||||
);
|
||||
|
||||
$this->terminateWithSuccess($viewData);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
// Private methods
|
||||
|
||||
}
|
||||
|
||||
@@ -62,21 +62,36 @@ class Studium extends FHCAPI_Controller
|
||||
|
||||
if($this->getDataOrTerminateWithError($this->StudentModel->isStudent(getAuthUID()))){
|
||||
$studentLehrverband =$this->StudentlehrverbandModel->loadWhere(["student_uid" => getAuthUID(), "studiensemester_kurzbz" => $aktuelles_studiensemester->studiensemester_kurzbz]);
|
||||
$studentLehrverband = current($this->getDataOrTerminateWithError($studentLehrverband));
|
||||
|
||||
$student_studiensemester = $studentLehrverband->studiensemester_kurzbz;
|
||||
$student_studiengang = $studentLehrverband->studiengang_kz;
|
||||
$student_semester = $studentLehrverband->semester;
|
||||
|
||||
//TODO(Manu) check if use Fallback or just comment out all paramschecks?
|
||||
//add Fallback: if no LehrverbandData of actual semester, get Data of previous one
|
||||
if(!hasData($studentLehrverband))
|
||||
{
|
||||
$result= $this->StudiensemesterModel->getPreviousFrom($aktuelles_studiensemester->studiensemester_kurzbz);
|
||||
|
||||
$data = $this->getDataOrTerminateWithError($result);
|
||||
$vorheriges_studiensemester = current($data)->studiensemester_kurzbz;
|
||||
$studentLehrverband =$this->StudentlehrverbandModel->loadWhere(["student_uid" => getAuthUID(), "studiensemester_kurzbz" => $vorheriges_studiensemester]);
|
||||
}
|
||||
$studentLehrverband = current(getData($studentLehrverband));
|
||||
|
||||
$student_studienplan = $this->getStudienPlanFromPrestudentStatus(getAuthPersonId())->studienplan_id;
|
||||
|
||||
if(!isset($parameter_studiensemester))
|
||||
$parameter_studiensemester = $student_studiensemester;
|
||||
if(!isset($parameter_studiengang))
|
||||
$parameter_studiengang = $student_studiengang;
|
||||
if(!isset($parameter_semester))
|
||||
$parameter_semester = $student_semester;
|
||||
if(!isset($parameter_studiensemester)) {
|
||||
$student_studiensemester = $studentLehrverband->studiensemester_kurzbz;
|
||||
$parameter_studiensemester = $student_studiensemester;
|
||||
}
|
||||
if(!isset($parameter_studiengang)) {
|
||||
$student_studiengang = $studentLehrverband->studiengang_kz;
|
||||
$parameter_studiengang = $student_studiengang;
|
||||
}
|
||||
if(!isset($parameter_semester)) {
|
||||
$student_semester = $studentLehrverband->semester;
|
||||
$parameter_semester = $student_semester;
|
||||
}
|
||||
if(!isset($parameter_studienplan))
|
||||
$parameter_studienplan = $student_studienplan;
|
||||
$parameter_studienplan = $student_studienplan;
|
||||
|
||||
}
|
||||
|
||||
if(isset($parameter_studiensemester)){
|
||||
@@ -96,8 +111,7 @@ class Studium extends FHCAPI_Controller
|
||||
|
||||
// fetch studiensemester
|
||||
$allStudienSemester = $this->getDataOrTerminateWithError($this->StudiensemesterModel->load());
|
||||
|
||||
|
||||
|
||||
if(isset($parameter_studiensemester) && !empty(array_filter($allStudienSemester, function($studiensemester) use($parameter_studiensemester){
|
||||
return $studiensemester->studiensemester_kurzbz == $parameter_studiensemester->studiensemester_kurzbz;
|
||||
}))){
|
||||
@@ -216,6 +230,8 @@ class Studium extends FHCAPI_Controller
|
||||
$studienplaene = array_map(function($studienplan){
|
||||
$orgform = current($this->getDataOrTerminateWithError($this->OrgformModel->loadWhere(["orgform_kurzbz" => $studienplan->orgform_kurzbz])));
|
||||
$studienplan->orgform_bezeichnung = $orgform->bezeichnung;
|
||||
// bezeichnung_mehrsprachig
|
||||
$studienplan->orgform_bezeichnung_english = $orgform->bezeichnung_mehrsprachig[1];
|
||||
return $studienplan;
|
||||
},$studienplaene);
|
||||
return $studienplaene;
|
||||
|
||||
@@ -40,11 +40,32 @@ class Board extends FHCAPI_Controller
|
||||
|
||||
public function list()
|
||||
{
|
||||
$this->DashboardModel->addSelect('dashboard_id');
|
||||
$this->DashboardModel->addSelect('dashboard_kurzbz');
|
||||
$this->DashboardModel->addSelect('tbl_dashboard.beschreibung');
|
||||
$this->DashboardModel->addSelect("(
|
||||
SELECT json_agg(w.*)
|
||||
FROM dashboard.tbl_widget w
|
||||
JOIN dashboard.tbl_dashboard_widget dw
|
||||
USING(widget_id)
|
||||
WHERE dw.dashboard_id=tbl_dashboard.dashboard_id
|
||||
) AS \"widgetSetup\"");
|
||||
|
||||
$result = $this->DashboardModel->load();
|
||||
|
||||
$data = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
$data = array_map(function ($dashboard) {
|
||||
$tmpSetups = json_decode($dashboard->widgetSetup);
|
||||
$tmpSetups = array_map(function ($widget) {
|
||||
$widget->setup->file = absoluteJsImportUrl($widget->setup->file);
|
||||
return $widget;
|
||||
}, $tmpSetups);
|
||||
$dashboard->widgetSetup = $tmpSetups;
|
||||
return $dashboard;
|
||||
}, $data);
|
||||
|
||||
$this->terminateWithSuccess($data);
|
||||
}
|
||||
|
||||
public function create()
|
||||
@@ -82,7 +103,7 @@ class Board extends FHCAPI_Controller
|
||||
|
||||
$data = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
$this->terminateWithSuccess($data);
|
||||
}
|
||||
|
||||
public function delete()
|
||||
@@ -116,6 +137,6 @@ class Board extends FHCAPI_Controller
|
||||
|
||||
$data = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
$this->terminateWithSuccess($data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,10 +120,7 @@ class Preset extends FHCAPI_Controller
|
||||
$conf = $this->dashboardlib->getPreset($db, $funktion);
|
||||
if ($conf) {
|
||||
$preset = json_decode($conf->preset, true);
|
||||
if (!isset($preset[$funktion]) || !isset($preset[$funktion]['widgets']))
|
||||
$result[$funktion] = [];
|
||||
else
|
||||
$result[$funktion] = $preset[$funktion]['widgets'];
|
||||
$result[$funktion] = $preset;
|
||||
} else {
|
||||
$result[$funktion] = [];
|
||||
}
|
||||
@@ -154,7 +151,7 @@ class Preset extends FHCAPI_Controller
|
||||
|
||||
$preset_decoded = json_decode($preset->preset, true);
|
||||
|
||||
$this->dashboardlib->addWidgetsToWidgets($preset_decoded, $dashboard_kurzbz, $funktion_kurzbz, [$widget]);
|
||||
$preset_decoded[$widget['widgetid']] = $widget;
|
||||
|
||||
$preset->preset = json_encode($preset_decoded);
|
||||
|
||||
@@ -186,8 +183,10 @@ class Preset extends FHCAPI_Controller
|
||||
|
||||
$preset_decoded = json_decode($preset->preset, true);
|
||||
|
||||
if (!$this->dashboardlib->removeWidgetFromWidgets($preset_decoded, $funktion_kurzbz, $widgetid))
|
||||
if (!isset($preset_decoded[$widgetid]))
|
||||
show_404();
|
||||
|
||||
unset($preset_decoded[$widgetid]);
|
||||
|
||||
$preset->preset = json_encode($preset_decoded);
|
||||
|
||||
|
||||
@@ -48,25 +48,9 @@ class User extends FHCAPI_Controller
|
||||
|
||||
$uid = $this->authlib->getAuthObj()->username;
|
||||
|
||||
/*$mergedconfig = $this->dashboardlib->getMergedConfig($dashboard->dashboard_id, $uid);
|
||||
$mergedconfig = $this->dashboardlib->getMergedUserConfig($dashboard->dashboard_id, $uid);
|
||||
|
||||
$this->terminateWithSuccess([
|
||||
'general' => call_user_func_array(
|
||||
'array_merge_recursive',
|
||||
$mergedconfig
|
||||
)
|
||||
]);*/
|
||||
$defaultconfig = $this->dashboardlib->getDefaultConfig($dashboard->dashboard_id);
|
||||
$userconfig = $this->dashboardlib->getUserConfig($dashboard->dashboard_id, $uid);
|
||||
|
||||
$defaultconfig_squashed = $defaultconfig ? call_user_func_array('array_replace_recursive', $defaultconfig) : [];
|
||||
$userconfig_squashed = $userconfig ? call_user_func_array('array_replace_recursive', $userconfig) : [];
|
||||
|
||||
$mergedconfig = array_replace_recursive($defaultconfig_squashed, $userconfig_squashed);
|
||||
|
||||
$this->terminateWithSuccess([
|
||||
DashboardLib::SECTION_IF_FUNKTION_KURZBZ_IS_NULL => $mergedconfig
|
||||
]);
|
||||
$this->terminateWithSuccess($mergedconfig);
|
||||
}
|
||||
|
||||
public function addWidget()
|
||||
@@ -86,26 +70,15 @@ class User extends FHCAPI_Controller
|
||||
if (!isset($widget['widgetid']))
|
||||
$widget['widgetid'] = $this->dashboardlib->generateWidgetId($dashboard_kurzbz);
|
||||
|
||||
if (isset($widget['source']))
|
||||
unset($widget['source']);
|
||||
|
||||
$override = $this->dashboardlib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid);
|
||||
|
||||
|
||||
$override_decoded = json_decode($override->override, true);
|
||||
|
||||
if (!isset($override_decoded['general']) || !is_array($override_decoded['general']))
|
||||
$override_decoded['general'] = [];
|
||||
$override_decoded[$widget['widgetid']] = $widget;
|
||||
|
||||
if (!isset($override_decoded['general']['widgets']))
|
||||
$override_decoded['general']['widgets'] = [];
|
||||
|
||||
$override_decoded['general']['widgets'][$widget['widgetid']] = $widget;
|
||||
|
||||
// NOTE(chris): remove doubles in other funktionen
|
||||
foreach ($override_decoded as $funktion => $array) {
|
||||
if ($funktion == 'general')
|
||||
continue;
|
||||
if (isset($array['widgets']) && isset($array['widgets'][$widget['widgetid']]))
|
||||
unset($override_decoded[$funktion]['widgets'][$widget['widgetid']]);
|
||||
}
|
||||
|
||||
$override->override = json_encode($override_decoded);
|
||||
|
||||
$result = $this->dashboardlib->insertOrUpdateOverride($override);
|
||||
@@ -135,18 +108,10 @@ class User extends FHCAPI_Controller
|
||||
|
||||
$override_decoded = json_decode($override->override, true);
|
||||
|
||||
foreach (array_keys($override_decoded) as $k) {
|
||||
if (!isset($override_decoded[$k]["widgets"])) {
|
||||
unset($override_decoded[$k]);
|
||||
continue;
|
||||
}
|
||||
if (isset($override_decoded[$k]["widgets"][$widget_id])) {
|
||||
unset($override_decoded[$k]["widgets"][$widget_id]);
|
||||
}
|
||||
if (!$override_decoded[$k]["widgets"]) {
|
||||
unset($override_decoded[$k]);
|
||||
}
|
||||
}
|
||||
if (!isset($override_decoded[$widget_id]))
|
||||
show_404();
|
||||
|
||||
unset($override_decoded[$widget_id]);
|
||||
|
||||
$override->override = json_encode($override_decoded);
|
||||
|
||||
|
||||
@@ -0,0 +1,517 @@
|
||||
<?php
|
||||
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Base Site URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| URL to your CodeIgniter root. Typically this will be your base URL,
|
||||
| WITH a trailing slash:
|
||||
|
|
||||
| http://example.com/
|
||||
|
|
||||
| If this is not set then CodeIgniter will try guess the protocol, domain
|
||||
| and path to your installation. However, you should always configure this
|
||||
| explicitly and never rely on auto-guessing, especially in production
|
||||
| environments.
|
||||
|
|
||||
*/
|
||||
$config['base_url'] = 'https://c3p0.dev.technikum-wien.at/ma1434/core/FHC-Core/';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Index File
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Typically this will be your index.php file, unless you've renamed it to
|
||||
| something else. If you are using mod_rewrite to remove the page set this
|
||||
| variable so that it is blank.
|
||||
|
|
||||
*/
|
||||
$config['index_page'] = 'index.ci.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| URI PROTOCOL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This item determines which server global should be used to retrieve the
|
||||
| URI string. The default setting of 'REQUEST_URI' works for most servers.
|
||||
| If your links do not seem to work, try one of the other delicious flavors:
|
||||
|
|
||||
| 'REQUEST_URI' Uses $_SERVER['REQUEST_URI']
|
||||
| 'QUERY_STRING' Uses $_SERVER['QUERY_STRING']
|
||||
| 'PATH_INFO' Uses $_SERVER['PATH_INFO']
|
||||
|
|
||||
| WARNING: If you set this to 'PATH_INFO', URIs will always be URL-decoded!
|
||||
*/
|
||||
$config['uri_protocol'] = 'REQUEST_URI';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| URL suffix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to add a suffix to all URLs generated by CodeIgniter.
|
||||
| For more information please see the user guide:
|
||||
|
|
||||
| http://codeigniter.com/user_guide/general/urls.html
|
||||
*/
|
||||
$config['url_suffix'] = '';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Language
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This determines which set of language files should be used. Make sure
|
||||
| there is an available translation if you intend to use something other
|
||||
| than english.
|
||||
|
|
||||
*/
|
||||
$config['language'] = '';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Character Set
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This determines which character set is used by default in various methods
|
||||
| that require a character set to be provided.
|
||||
|
|
||||
| See http://php.net/htmlspecialchars for a list of supported charsets.
|
||||
|
|
||||
*/
|
||||
$config['charset'] = 'UTF-8';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Enable/Disable System Hooks
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If you would like to use the 'hooks' feature you must enable it by
|
||||
| setting this variable to TRUE (boolean). See the user guide for details.
|
||||
|
|
||||
*/
|
||||
$config['enable_hooks'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Class Extension Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This item allows you to set the filename/classname prefix when extending
|
||||
| native libraries. For more information please see the user guide:
|
||||
|
|
||||
| http://codeigniter.com/user_guide/general/core_classes.html
|
||||
| http://codeigniter.com/user_guide/general/creating_libraries.html
|
||||
|
|
||||
*/
|
||||
$config['subclass_prefix'] = 'FHC_';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Composer auto-loading
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Enabling this setting will tell CodeIgniter to look for a Composer
|
||||
| package auto-loader script in application/vendor/autoload.php.
|
||||
|
|
||||
| $config['composer_autoload'] = TRUE;
|
||||
|
|
||||
| Or if you have your vendor/ directory located somewhere else, you
|
||||
| can opt to set a specific path as well:
|
||||
|
|
||||
| $config['composer_autoload'] = '/path/to/vendor/autoload.php';
|
||||
|
|
||||
| For more information about Composer, please visit http://getcomposer.org/
|
||||
|
|
||||
| Note: This will NOT disable or override the CodeIgniter-specific
|
||||
| autoloading (application/config/autoload.php)
|
||||
*/
|
||||
$config['composer_autoload'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Allowed URL Characters
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This lets you specify which characters are permitted within your URLs.
|
||||
| When someone tries to submit a URL with disallowed characters they will
|
||||
| get a warning message.
|
||||
|
|
||||
| As a security measure you are STRONGLY encouraged to restrict URLs to
|
||||
| as few characters as possible. By default only these are allowed: a-z 0-9~%.:_-
|
||||
|
|
||||
| Leave blank to allow all characters -- but only if you are insane.
|
||||
|
|
||||
| The configured value is actually a regular expression character group
|
||||
| and it will be executed as: ! preg_match('/^[<permitted_uri_chars>]+$/i
|
||||
|
|
||||
| DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!
|
||||
|
|
||||
*/
|
||||
$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Enable Query Strings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default CodeIgniter uses search-engine friendly segment based URLs:
|
||||
| example.com/who/what/where/
|
||||
|
|
||||
| By default CodeIgniter enables access to the $_GET array. If for some
|
||||
| reason you would like to disable it, set 'allow_get_array' to FALSE.
|
||||
|
|
||||
| You can optionally enable standard query string based URLs:
|
||||
| example.com?who=me&what=something&where=here
|
||||
|
|
||||
| Options are: TRUE or FALSE (boolean)
|
||||
|
|
||||
| The other items let you set the query string 'words' that will
|
||||
| invoke your controllers and its functions:
|
||||
| example.com/index.php?c=controller&m=function
|
||||
|
|
||||
| Please note that some of the helpers won't work as expected when
|
||||
| this feature is enabled, since CodeIgniter is designed primarily to
|
||||
| use segment based URLs.
|
||||
|
|
||||
*/
|
||||
$config['allow_get_array'] = TRUE;
|
||||
$config['enable_query_strings'] = FALSE;
|
||||
$config['controller_trigger'] = 'c';
|
||||
$config['function_trigger'] = 'm';
|
||||
$config['directory_trigger'] = 'd';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Error Logging Threshold
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You can enable error logging by setting a threshold over zero. The
|
||||
| threshold determines what gets logged. Threshold options are:
|
||||
|
|
||||
| 0 = Disables logging, Error logging TURNED OFF
|
||||
| 1 = Error Messages (including PHP errors)
|
||||
| 2 = Debug Messages
|
||||
| 3 = Informational Messages
|
||||
| 4 = All Messages
|
||||
|
|
||||
| You can also pass an array with threshold levels to show individual error types
|
||||
|
|
||||
| array(2) = Debug Messages, without Error Messages
|
||||
|
|
||||
| For a live site you'll usually only enable Errors (1) to be logged otherwise
|
||||
| your log files will fill up very fast.
|
||||
|
|
||||
*/
|
||||
$config['log_threshold'] = 1;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Error Logging Directory Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Leave this BLANK unless you would like to set something other than the default
|
||||
| application/logs/ directory. Use a full server path with trailing slash.
|
||||
|
|
||||
*/
|
||||
$config['log_path'] = '';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log File Extension
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The default filename extension for log files. The default 'php' allows for
|
||||
| protecting the log files via basic scripting, when they are to be stored
|
||||
| under a publicly accessible directory.
|
||||
|
|
||||
| Note: Leaving it blank will default to 'php'.
|
||||
|
|
||||
*/
|
||||
$config['log_file_extension'] = 'log';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log File Permissions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The file system permissions to be applied on newly created log files.
|
||||
|
|
||||
| IMPORTANT: This MUST be an integer (no quotes) and you MUST use octal
|
||||
| integer notation (i.e. 0700, 0644, etc.)
|
||||
*/
|
||||
$config['log_file_permissions'] = 0644;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Date Format for Logs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Each item that is logged has an associated date. You can use PHP date
|
||||
| codes to set your own date formatting
|
||||
|
|
||||
*/
|
||||
$config['log_date_format'] = 'Y-m-d H:i:s';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Error Views Directory Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Leave this BLANK unless you would like to set something other than the default
|
||||
| application/views/errors/ directory. Use a full server path with trailing slash.
|
||||
|
|
||||
*/
|
||||
$config['error_views_path'] = '';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Directory Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Leave this BLANK unless you would like to set something other than the default
|
||||
| application/cache/ directory. Use a full server path with trailing slash.
|
||||
|
|
||||
*/
|
||||
$config['cache_path'] = '';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Include Query String
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Whether to take the URL query string into consideration when generating
|
||||
| output cache files. Valid options are:
|
||||
|
|
||||
| FALSE = Disabled
|
||||
| TRUE = Enabled, take all query parameters into account.
|
||||
| Please be aware that this may result in numerous cache
|
||||
| files generated for the same page over and over again.
|
||||
| array('q') = Enabled, but only take into account the specified list
|
||||
| of query parameters.
|
||||
|
|
||||
*/
|
||||
$config['cache_query_string'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption Key
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If you use the Encryption class, you must set an encryption key.
|
||||
| See the user guide for more info.
|
||||
|
|
||||
| http://codeigniter.com/user_guide/libraries/encryption.html
|
||||
|
|
||||
*/
|
||||
$config['encryption_key'] = '';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Variables
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| 'sess_driver'
|
||||
|
|
||||
| The storage driver to use: files, database, redis, memcached
|
||||
|
|
||||
| 'sess_cookie_name'
|
||||
|
|
||||
| The session cookie name, must contain only [0-9a-z_-] characters
|
||||
|
|
||||
| 'sess_expiration'
|
||||
|
|
||||
| The number of SECONDS you want the session to last.
|
||||
| Setting to 0 (zero) means expire when the browser is closed.
|
||||
|
|
||||
| 'sess_save_path'
|
||||
|
|
||||
| The location to save sessions to, driver dependent.
|
||||
|
|
||||
| For the 'files' driver, it's a path to a writable directory.
|
||||
| WARNING: Only absolute paths are supported!
|
||||
|
|
||||
| For the 'database' driver, it's a table name.
|
||||
| Please read up the manual for the format with other session drivers.
|
||||
|
|
||||
| IMPORTANT: You are REQUIRED to set a valid save path!
|
||||
|
|
||||
| 'sess_match_ip'
|
||||
|
|
||||
| Whether to match the user's IP address when reading the session data.
|
||||
|
|
||||
| 'sess_time_to_update'
|
||||
|
|
||||
| How many seconds between CI regenerating the session ID.
|
||||
| NOTE: Keep it as it is to prevent security issues (https://en.wikipedia.org/wiki/Session_fixation)
|
||||
|
|
||||
| 'sess_regenerate_destroy'
|
||||
|
|
||||
| Whether to destroy session data associated with the old session ID
|
||||
| when auto-regenerating the session ID. When set to FALSE, the data
|
||||
| will be later deleted by the garbage collector.
|
||||
|
|
||||
| Other session cookie settings are shared with the rest of the application,
|
||||
| except for 'cookie_prefix' and 'cookie_httponly', which are ignored here.
|
||||
|
|
||||
*/
|
||||
$config['sess_driver'] = 'files';
|
||||
$config['sess_cookie_name'] = 'sess_ci_session';
|
||||
$config['sess_expiration'] = 1800; // Session expires every 30 minutes
|
||||
$config['sess_save_path'] = NULL;
|
||||
$config['sess_match_ip'] = FALSE;
|
||||
$config['sess_time_to_update'] = 300;
|
||||
$config['sess_regenerate_destroy'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cookie Related Variables
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| 'cookie_prefix' = Set a cookie name prefix if you need to avoid collisions
|
||||
| 'cookie_domain' = Set to .your-domain.com for site-wide cookies
|
||||
| 'cookie_path' = Typically will be a forward slash
|
||||
| 'cookie_secure' = Cookie will only be set if a secure HTTPS connection exists.
|
||||
| 'cookie_httponly' = Cookie will only be accessible via HTTP(S) (no javascript)
|
||||
|
|
||||
| Note: These settings (with the exception of 'cookie_prefix' and
|
||||
| 'cookie_httponly') will also affect sessions.
|
||||
|
|
||||
*/
|
||||
$config['cookie_prefix'] = '';
|
||||
$config['cookie_domain'] = '';
|
||||
$config['cookie_path'] = '/';
|
||||
$config['cookie_secure'] = FALSE;
|
||||
$config['cookie_httponly'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Standardize newlines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Determines whether to standardize newline characters in input data,
|
||||
| meaning to replace \r\n, \r, \n occurrences with the PHP_EOL value.
|
||||
|
|
||||
| This is particularly useful for portability between UNIX-based OSes,
|
||||
| (usually \n) and Windows (\r\n).
|
||||
|
|
||||
*/
|
||||
$config['standardize_newlines'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global XSS Filtering
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Determines whether the XSS filter is always active when GET, POST or
|
||||
| COOKIE data is encountered
|
||||
|
|
||||
| WARNING: This feature is DEPRECATED and currently available only
|
||||
| for backwards compatibility purposes!
|
||||
|
|
||||
*/
|
||||
$config['global_xss_filtering'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cross Site Request Forgery
|
||||
|--------------------------------------------------------------------------
|
||||
| Enables a CSRF cookie token to be set. When set to TRUE, token will be
|
||||
| checked on a submitted form. If you are accepting user data, it is strongly
|
||||
| recommended CSRF protection be enabled.
|
||||
|
|
||||
| 'csrf_token_name' = The token name
|
||||
| 'csrf_cookie_name' = The cookie name
|
||||
| 'csrf_expire' = The number in seconds the token should expire.
|
||||
| 'csrf_regenerate' = Regenerate token on every submission
|
||||
| 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks
|
||||
*/
|
||||
$config['csrf_protection'] = FALSE;
|
||||
$config['csrf_token_name'] = 'csrf_test_name';
|
||||
$config['csrf_cookie_name'] = 'csrf_cookie_name';
|
||||
$config['csrf_expire'] = 7200;
|
||||
$config['csrf_regenerate'] = TRUE;
|
||||
$config['csrf_exclude_uris'] = array();
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Output Compression
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Enables Gzip output compression for faster page loads. When enabled,
|
||||
| the output class will test whether your server supports Gzip.
|
||||
| Even if it does, however, not all browsers support compression
|
||||
| so enable only if you are reasonably sure your visitors can handle it.
|
||||
|
|
||||
| Only used if zlib.output_compression is turned off in your php.ini.
|
||||
| Please do not use it together with httpd-level output compression.
|
||||
|
|
||||
| VERY IMPORTANT: If you are getting a blank page when compression is enabled it
|
||||
| means you are prematurely outputting something to your browser. It could
|
||||
| even be a line of whitespace at the end of one of your scripts. For
|
||||
| compression to work, nothing can be sent before the output buffer is called
|
||||
| by the output class. Do not 'echo' any values with compression enabled.
|
||||
|
|
||||
*/
|
||||
$config['compress_output'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Master Time Reference
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Options are 'local' or any PHP supported timezone. This preference tells
|
||||
| the system whether to use your server's local time as the master 'now'
|
||||
| reference, or convert it to the configured one timezone. See the 'date
|
||||
| helper' page of the user guide for information regarding date handling.
|
||||
|
|
||||
*/
|
||||
$config['time_reference'] = 'local';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Rewrite PHP Short Tags
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If your PHP installation does not have short tag support enabled CI
|
||||
| can rewrite the tags on-the-fly, enabling you to utilize that syntax
|
||||
| in your view files. Options are TRUE or FALSE (boolean)
|
||||
|
|
||||
| Note: You need to have eval() enabled for this to work.
|
||||
|
|
||||
*/
|
||||
$config['rewrite_short_tags'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Reverse Proxy IPs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If your server is behind a reverse proxy, you must whitelist the proxy
|
||||
| IP addresses from which CodeIgniter should trust headers such as
|
||||
| HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP in order to properly identify
|
||||
| the visitor's IP address.
|
||||
|
|
||||
| You can use both an array or a comma-separated list of proxy addresses,
|
||||
| as well as specifying whole subnets. Here are a few examples:
|
||||
|
|
||||
| Comma-separated: '10.0.1.200,192.168.5.0/24'
|
||||
| Array: array('10.0.1.200', '192.168.5.0/24')
|
||||
*/
|
||||
$config['proxy_ips'] = '';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| FHComplete Build Version
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Version Number of the Current Build
|
||||
| This is used to invalidate Cache for JS and CSS Files
|
||||
|
|
||||
| Example: 2019102901
|
||||
*/
|
||||
$config['fhcomplete_build_version'] = '2019102903';
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
defined('DB_HOST') OR require_once './config/system.config.inc.php'; // For CLI-Migrations
|
||||
|
||||
/*
|
||||
| -------------------------------------------------------------------
|
||||
| DATABASE CONNECTIVITY SETTINGS
|
||||
| -------------------------------------------------------------------
|
||||
| This file will contain the settings needed to access your database.
|
||||
|
|
||||
| For complete instructions please consult the 'Database Connection'
|
||||
| page of the User Guide.
|
||||
|
|
||||
| -------------------------------------------------------------------
|
||||
| EXPLANATION OF VARIABLES
|
||||
| -------------------------------------------------------------------
|
||||
|
|
||||
| ['dsn'] The full DSN string describe a connection to the database.
|
||||
| ['hostname'] The hostname of your database server.
|
||||
| ['username'] The username used to connect to the database
|
||||
| ['password'] The password used to connect to the database
|
||||
| ['database'] The name of the database you want to connect to
|
||||
| ['dbdriver'] The database driver. e.g.: mysqli.
|
||||
| Currently supported:
|
||||
| cubrid, ibase, mssql, mysql, mysqli, oci8,
|
||||
| odbc, pdo, postgre, sqlite, sqlite3, sqlsrv
|
||||
| ['dbprefix'] You can add an optional prefix, which will be added
|
||||
| to the table name when using the Query Builder class
|
||||
| ['pconnect'] TRUE/FALSE - Whether to use a persistent connection
|
||||
| ['db_debug'] TRUE/FALSE - Whether database errors should be displayed.
|
||||
| ['cache_on'] TRUE/FALSE - Enables/disables query caching
|
||||
| ['cachedir'] The path to the folder where cache files should be stored
|
||||
| ['char_set'] The character set used in communicating with the database
|
||||
| ['dbcollat'] The character collation used in communicating with the database
|
||||
| NOTE: For MySQL and MySQLi databases, this setting is only used
|
||||
| as a backup if your server is running PHP < 5.2.3 or MySQL < 5.0.7
|
||||
| (and in table creation queries made with DB Forge).
|
||||
| There is an incompatibility in PHP with mysql_real_escape_string() which
|
||||
| can make your site vulnerable to SQL injection if you are using a
|
||||
| multi-byte character set and are running versions lower than these.
|
||||
| Sites using Latin-1 or UTF-8 database character set and collation are unaffected.
|
||||
| ['swap_pre'] A default table prefix that should be swapped with the dbprefix
|
||||
| ['encrypt'] Whether or not to use an encrypted connection.
|
||||
|
|
||||
| 'mysql' (deprecated), 'sqlsrv' and 'pdo/sqlsrv' drivers accept TRUE/FALSE
|
||||
| 'mysqli' and 'pdo/mysql' drivers accept an array with the following options:
|
||||
|
|
||||
| 'ssl_key' - Path to the private key file
|
||||
| 'ssl_cert' - Path to the public key certificate file
|
||||
| 'ssl_ca' - Path to the certificate authority file
|
||||
| 'ssl_capath' - Path to a directory containing trusted CA certificats in PEM format
|
||||
| 'ssl_cipher' - List of *allowed* ciphers to be used for the encryption, separated by colons (':')
|
||||
| 'ssl_verify' - TRUE/FALSE; Whether verify the server certificate or not ('mysqli' only)
|
||||
|
|
||||
| ['compress'] Whether or not to use client compression (MySQL only)
|
||||
| ['stricton'] TRUE/FALSE - forces 'Strict Mode' connections
|
||||
| - good for ensuring strict SQL while developing
|
||||
| ['ssl_options'] Used to set various SSL options that can be used when making SSL connections.
|
||||
| ['failover'] array - A array with 0 or more data for connections if the main should fail.
|
||||
| ['save_queries'] TRUE/FALSE - Whether to "save" all executed queries.
|
||||
| NOTE: Disabling this will also effectively disable both
|
||||
| $this->db->last_query() and profiling of DB queries.
|
||||
| When you run a query, with this setting set to TRUE (default),
|
||||
| CodeIgniter will store the SQL statement for debugging purposes.
|
||||
| However, this may cause high memory usage, especially if you run
|
||||
| a lot of SQL queries ... disable this to avoid that problem.
|
||||
|
|
||||
| The $active_group variable lets you choose which connection group to
|
||||
| make active. By default there is only one group (the 'default' group).
|
||||
|
|
||||
| The $query_builder variables lets you determine whether or not to load
|
||||
| the query builder class.
|
||||
*/
|
||||
$active_group = 'default';
|
||||
$query_builder = TRUE;
|
||||
|
||||
$db['default'] = array(
|
||||
'dsn' => '',
|
||||
'hostname' => DB_HOST,
|
||||
'username' => DB_USER,
|
||||
'password' => DB_PASSWORD,
|
||||
'port' => DB_PORT,
|
||||
'database' => DB_NAME,
|
||||
'dbdriver' => 'postgre',
|
||||
'dbprefix' => '',
|
||||
'pconnect' => DB_CONNECT_PERSISTENT,
|
||||
'db_debug' => (ENVIRONMENT !== 'production'),
|
||||
'cache_on' => FALSE,
|
||||
'cachedir' => '',
|
||||
'char_set' => 'utf8',
|
||||
'dbcollat' => 'utf8_general_ci',
|
||||
'swap_pre' => '',
|
||||
'encrypt' => FALSE,
|
||||
'compress' => FALSE,
|
||||
'stricton' => FALSE,
|
||||
'failover' => array(),
|
||||
'save_queries' => TRUE
|
||||
);
|
||||
|
||||
$db['system'] = array(
|
||||
'dsn' => '',
|
||||
'hostname' => DB_HOST,
|
||||
'username' => 'fhcomplete',
|
||||
'password' => 'Fhcomplet3Onc4p1',
|
||||
'database' => DB_NAME,
|
||||
'port' => DB_PORT,
|
||||
'dbschema' => 'public',
|
||||
'dbdriver' => 'postgre',
|
||||
'dbprefix' => '',
|
||||
'pconnect' => DB_CONNECT_PERSISTENT,
|
||||
'db_debug' => (ENVIRONMENT !== 'production'),
|
||||
'cache_on' => FALSE,
|
||||
'cachedir' => '',
|
||||
'char_set' => 'utf8',
|
||||
'dbcollat' => 'utf8_general_ci',
|
||||
'swap_pre' => '',
|
||||
'encrypt' => FALSE,
|
||||
'compress' => FALSE,
|
||||
'stricton' => FALSE,
|
||||
'failover' => array(),
|
||||
'save_queries' => TRUE
|
||||
);
|
||||
@@ -40,13 +40,16 @@ class StundenplanLib
|
||||
* @return stdClass
|
||||
* @access public
|
||||
*/
|
||||
public function getEventsUser($start, $end)
|
||||
public function getEventsUser($start, $end, $uid = null)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
|
||||
$this->_ci->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
|
||||
|
||||
$uid = getAuthUID();
|
||||
if (!$uid) {
|
||||
$uid = getAuthUID();
|
||||
}
|
||||
|
||||
if (is_null($uid))
|
||||
return error("No UID");
|
||||
|
||||
@@ -217,7 +220,7 @@ class StundenplanLib
|
||||
* @param string $ort_kurzbz
|
||||
* @return stdClass
|
||||
*/
|
||||
public function getReservierungen($start_date, $end_date, $ort_kurzbz = '')
|
||||
public function getReservierungen($start_date, $end_date, $ort_kurzbz = '', $uid = null)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
|
||||
@@ -228,14 +231,14 @@ class StundenplanLib
|
||||
$this->_ci->load->model('ressource/Reservierung_model', 'ReservierungModel');
|
||||
$this->_ci->load->model('ressource/Stundenplan_model', 'StundenplanModel');
|
||||
|
||||
$is_mitarbeiter = getData($this->_ci->MitarbeiterModel->isMitarbeiter(getAuthUID()));
|
||||
$is_mitarbeiter = getData($this->_ci->MitarbeiterModel->isMitarbeiter($uid ?? getAuthUID()));
|
||||
|
||||
if ($is_mitarbeiter && empty($ort_kurzbz)) {
|
||||
// request for personal lvplan show only reservations of logged in user
|
||||
$reservierungen = $this->_ci->ReservierungModel->getReservierungenMitarbeiter($start_date, $end_date);
|
||||
$reservierungen = $this->_ci->ReservierungModel->getReservierungenMitarbeiter($start_date, $end_date, $uid);
|
||||
} else {
|
||||
// querying the reservierungen
|
||||
$reservierungen = $this->_ci->ReservierungModel->getReservierungen($start_date, $end_date, $ort_kurzbz);
|
||||
$reservierungen = $this->_ci->ReservierungModel->getReservierungen($start_date, $end_date, $ort_kurzbz, $uid);
|
||||
}
|
||||
|
||||
if (isError($reservierungen))
|
||||
@@ -445,6 +448,24 @@ class StundenplanLib
|
||||
return success($ferienEventsFlattened);
|
||||
}
|
||||
|
||||
public function getEventsStgOrg( $start, $end, $stg_kz, $sem, $verband, $gruppe)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
|
||||
$this->_ci->load->model('ressource/Stundenplan_model', 'StundenplanModel');
|
||||
|
||||
$stundenplan_data = $this->_ci->StundenplanModel->getStundenplanStudiengang($start, $end, $stg_kz, $sem, $verband, $gruppe);
|
||||
if (isError($stundenplan_data))
|
||||
return $stundenplan_data;
|
||||
$stundenplan_data = getData($stundenplan_data) ?? [];
|
||||
|
||||
$function_error = $this->expandObjectInformation($stundenplan_data);
|
||||
if ($function_error)
|
||||
return $function_error;
|
||||
|
||||
return success($stundenplan_data);
|
||||
}
|
||||
|
||||
// start of the private functions ########################################################################################################
|
||||
|
||||
// function used to sort an array of studiensemester strings
|
||||
|
||||
@@ -37,7 +37,9 @@ class DashboardLib
|
||||
|
||||
public function getDashboardByKurzbz($dashboard_kurzbz)
|
||||
{
|
||||
$result = $this->_ci->DashboardModel->getDashboardByKurzbz($dashboard_kurzbz);
|
||||
$result = $this->_ci->DashboardModel->loadWhere([
|
||||
'dashboard_kurzbz' => $dashboard_kurzbz
|
||||
]);
|
||||
|
||||
if (hasData($result))
|
||||
{
|
||||
@@ -47,17 +49,21 @@ class DashboardLib
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getMergedConfig($dashboard_id, $uid)
|
||||
public function getMergedUserConfig($dashboard_id, $uid)
|
||||
{
|
||||
$defaultconfig = $this->getDefaultConfig($dashboard_id);
|
||||
$userconfig = $this->getUserConfig($dashboard_id, $uid);
|
||||
$defaultconfig = $this->getUserBaseConfig($dashboard_id);
|
||||
$userconfig = $this->getUserOverrideConfig($dashboard_id, $uid);
|
||||
|
||||
$mergedconfig = array_replace_recursive($defaultconfig, $userconfig);
|
||||
$sourceconfig = array_map(function ($value) {
|
||||
return ['source' => $value['source']];
|
||||
}, $defaultconfig);
|
||||
|
||||
$mergedconfig = array_replace_recursive($defaultconfig, $userconfig, $sourceconfig);
|
||||
|
||||
return $mergedconfig;
|
||||
}
|
||||
|
||||
public function getDefaultConfig($dashboard_id)
|
||||
protected function getUserBaseConfig($dashboard_id)
|
||||
{
|
||||
$funktion_kurzbzs = [];
|
||||
$rights = $this->_ci->permissionlib->getAccessRights();
|
||||
@@ -87,7 +93,11 @@ class DashboardLib
|
||||
$preset = json_decode($presetobj->preset, true);
|
||||
if (null !== $preset)
|
||||
{
|
||||
$defaultconfig = array_replace_recursive($defaultconfig, $preset);
|
||||
$preset = array_map(function ($value) use ($presetobj) {
|
||||
$value['source'] = $presetobj->funktion_kurzbz ?: self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL;
|
||||
return $value;
|
||||
}, $preset);
|
||||
$defaultconfig = array_merge_recursive($defaultconfig, $preset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,7 +105,7 @@ class DashboardLib
|
||||
return $defaultconfig;
|
||||
}
|
||||
|
||||
public function getUserConfig($dashboard_id, $uid)
|
||||
protected function getUserOverrideConfig($dashboard_id, $uid)
|
||||
{
|
||||
$res_userconfig = $this->_ci->DashboardOverrideModel->getOverride($dashboard_id, $uid);
|
||||
|
||||
@@ -124,7 +134,7 @@ class DashboardLib
|
||||
$emptyoverride = new stdClass();
|
||||
$emptyoverride->dashboard_id = $dashboard->dashboard_id;
|
||||
$emptyoverride->uid = $uid;
|
||||
$emptyoverride->override = '{"' . self::USEROVERRIDE_SECTION . '": {"widgets":{}}, "custom": { "widgets" : {}}}';
|
||||
$emptyoverride->override = '[]';
|
||||
|
||||
return $emptyoverride;
|
||||
}
|
||||
@@ -143,8 +153,7 @@ class DashboardLib
|
||||
$emptypreset = new stdClass();
|
||||
$emptypreset->dashboard_id = $dashboard->dashboard_id;
|
||||
$emptypreset->funktion_kurzbz = $funktion_kurzbz;
|
||||
$section = ($funktion_kurzbz !== null) ? $funktion_kurzbz : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL;
|
||||
$emptypreset->preset = '{"' . $section . '": { "widgets" : {}},"custom": { "widgets" : {}}}';
|
||||
$emptypreset->preset = '[]';
|
||||
|
||||
return $emptypreset;
|
||||
}
|
||||
@@ -209,44 +218,4 @@ class DashboardLib
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function addWidgetsToWidgets(&$widgets, $dashboard_kurzbz, $section, $addwigets)
|
||||
{
|
||||
foreach ($addwigets as $widget)
|
||||
{
|
||||
if(!isset($widget['widgetid']))
|
||||
{
|
||||
$widget['widgetid'] = $this->generateWidgetId($dashboard_kurzbz);
|
||||
}
|
||||
$this->addWidgetToWidgets($widgets, $section, $widget, $widget['widgetid']);
|
||||
}
|
||||
}
|
||||
|
||||
public function addWidgetToWidgets(&$widgets, $section, $widget, $widgetid)
|
||||
{
|
||||
$section = ($section !== null) ? $section : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL;
|
||||
if (!isset($widgets[$section]) || !isset($widgets[$section]["widgets"]) || !is_array($widgets[$section]))
|
||||
{
|
||||
$widgets[$section] = array();
|
||||
$widgets[$section]["widgets"] = array();
|
||||
}
|
||||
|
||||
$widgets[$section]["widgets"][$widgetid] = $widget;
|
||||
}
|
||||
|
||||
public function removeWidgetFromWidgets(&$widgets, $section, $widgetid)
|
||||
{
|
||||
$section = ($section !== null) ? $section : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL;
|
||||
if (isset($widgets[$section]) && isset($widgets[$section]["widgets"][$widgetid]))
|
||||
{
|
||||
unset($widgets[$section]["widgets"][$widgetid]);
|
||||
if(empty($widgets[$section]["widgets"]) && $section !== self::USEROVERRIDE_SECTION) {
|
||||
unset($widgets[$section]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,9 +234,9 @@ class Content_model extends DB_Model
|
||||
FROM
|
||||
campus.tbl_content c1
|
||||
LEFT JOIN
|
||||
campus.tbl_contentsprache s1 ON c1.content_id=s1.content_id AND s1.sprache=?
|
||||
campus.tbl_contentsprache s1 ON c1.content_id=s1.content_id AND s1.sprache=? AND sichtbar=true
|
||||
WHERE
|
||||
sichtbar=true
|
||||
c1.aktiv = true
|
||||
) s2
|
||||
LEFT JOIN
|
||||
campus.tbl_contentsprache s3 USING(content_id, sprache)
|
||||
@@ -277,7 +277,7 @@ class Content_model extends DB_Model
|
||||
JOIN
|
||||
campus.tbl_contentsprache s USING(contentsprache_id)
|
||||
LEFT JOIN
|
||||
campus.tbl_contentchild k ON(m.content_id=k.content_id)
|
||||
campus.tbl_contentchild k ON(m.content_id=k.content_id) and c.aktiv = true
|
||||
WHERE EXISTS (
|
||||
SELECT 1
|
||||
FROM campus.tbl_contentgruppe
|
||||
|
||||
@@ -12,7 +12,83 @@ class Bookmark_model extends DB_Model
|
||||
$this->pk = 'bookmark_id';
|
||||
}
|
||||
|
||||
/**
|
||||
* returns all bookmark tags of a user
|
||||
*/
|
||||
public function getAllBookmarkTags($uid)
|
||||
{
|
||||
$qry = "
|
||||
SELECT array_agg(DISTINCT tag) AS data
|
||||
FROM (
|
||||
SELECT jsonb_array_elements_text(tag) AS tag
|
||||
FROM dashboard.tbl_bookmark
|
||||
WHERE uid = ?
|
||||
) t;
|
||||
";
|
||||
|
||||
|
||||
return $this->execQuery($qry, array('uid' => $uid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Overrides of given uid and funktion_kurzbz and widget_id
|
||||
* @param integer $widget_id
|
||||
* @param string $uid
|
||||
* @param string $funktion_kurzbz
|
||||
* @return array
|
||||
*/
|
||||
public function getTagFilter($widgetId, $uid, $funktion_kurzbz)
|
||||
{
|
||||
$qry = " SELECT override -> '" . $funktion_kurzbz . "'-> 'widgets'-> '" . $widgetId . "'-> 'config' -> 'tags' AS tags FROM dashboard.tbl_dashboard_benutzer_override WHERE uid = '" . $uid . "';";
|
||||
|
||||
return $this->execQuery($qry);
|
||||
}
|
||||
|
||||
/*
|
||||
* updates Tagfilter
|
||||
* checks and changes type of config
|
||||
* if config == array -> change to object
|
||||
*/
|
||||
public function addAndUpdateTagFilter($widgetId, $uid, $funktion_kurzbz, $tags)
|
||||
{
|
||||
$params = [
|
||||
$funktion_kurzbz, $widgetId,
|
||||
$funktion_kurzbz, $widgetId,
|
||||
$funktion_kurzbz, $widgetId,
|
||||
$funktion_kurzbz, $widgetId,
|
||||
$tags,
|
||||
$uid
|
||||
];
|
||||
|
||||
$qry = "
|
||||
UPDATE dashboard.tbl_dashboard_benutzer_override
|
||||
SET override = jsonb_set(
|
||||
-- check if config is object
|
||||
jsonb_set(
|
||||
COALESCE(override, '{}'::jsonb),
|
||||
format('{%s,widgets,%s,config}', ?, ?)::text[],
|
||||
CASE
|
||||
WHEN jsonb_typeof(override #> format('{%s,widgets,%s,config}', ?, ?)::text[]) = 'array'
|
||||
THEN '{}'::jsonb
|
||||
ELSE COALESCE((override #> format('{%s,widgets,%s,config}', ?, ?)::text[])::jsonb, '{}'::jsonb)
|
||||
END,
|
||||
true
|
||||
),
|
||||
-- insert tags
|
||||
format('{%s,widgets,%s,config,tags}', ?, ?)::text[],
|
||||
?::jsonb,
|
||||
true
|
||||
)
|
||||
WHERE uid = ?
|
||||
";
|
||||
|
||||
return $this->execQuery($qry, $params);
|
||||
}
|
||||
|
||||
public function checkOrAddToOverride($widgetId, $uid, $funktion_kurzbz)
|
||||
{
|
||||
$params = [$funktion_kurzbz, $widgetId, $uid];
|
||||
$qry = " SELECT override -> '" . $funktion_kurzbz . "'-> 'widgets'-> '" . $widgetId . "'-> 'widgetid' as widgetid FROM dashboard.tbl_dashboard_benutzer_override WHERE uid = '" . $uid . "';";
|
||||
|
||||
return $this->execQuery($qry, $params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,15 +11,4 @@ class Dashboard_model extends DB_Model
|
||||
$this->dbTable = 'dashboard.tbl_dashboard';
|
||||
$this->pk = 'dashboard_id';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get Dashboard by kurzbz.
|
||||
* @param string dashboard_kurzbz
|
||||
* @return array
|
||||
*/
|
||||
public function getDashboardByKurzbz($dashboard_kurzbz)
|
||||
{
|
||||
return $this->loadWhere(array('dashboard_kurzbz' => $dashboard_kurzbz));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ class Reservierung_model extends DB_Model
|
||||
*
|
||||
* @return stdClass
|
||||
*/
|
||||
public function getReservierungen($start_date, $end_date, $ort_kurzbz = null)
|
||||
public function getReservierungen($start_date, $end_date, $ort_kurzbz = null, $uid = null)
|
||||
{
|
||||
|
||||
$lvplan_reservierungen_query="SELECT r.* , stund.beginn, stund.ende,
|
||||
|
||||
$lvplan_reservierungen_query = "SELECT r.* , stund.beginn, stund.ende,
|
||||
CASE
|
||||
WHEN r.gruppe_kurzbz IS NOT NULL THEN r.gruppe_kurzbz
|
||||
ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(r.semester AS varchar),'/'),COALESCE(CAST(r.verband AS varchar),'/'))
|
||||
@@ -35,7 +35,7 @@ class Reservierung_model extends DB_Model
|
||||
LEFT JOIN public.tbl_studiensemester ss2 ON slv.studiensemester_kurzbz = ss2.studiensemester_kurzbz AND ss2.start <=r.datum AND ss2.ende >= r.datum
|
||||
WHERE datum >= ? AND datum <= ? AND (ss1.studiensemester_kurzbz IS NOT NULL
|
||||
OR ss2.studiensemester_kurzbz IS NOT NULL)";
|
||||
|
||||
|
||||
$raum_reservierungen_query = "SELECT res.*, beginn, ende,
|
||||
CASE
|
||||
WHEN res.gruppe_kurzbz IS NOT NULL THEN res.gruppe_kurzbz
|
||||
@@ -46,9 +46,9 @@ class Reservierung_model extends DB_Model
|
||||
JOIN lehre.tbl_stunde ON lehre.tbl_stunde.stunde = res.stunde
|
||||
WHERE res.ort_kurzbz = ? AND datum >= ? AND datum <= ?";
|
||||
|
||||
$subquery = is_null($ort_kurzbz)? $lvplan_reservierungen_query:$raum_reservierungen_query;
|
||||
|
||||
$query_result= $this->execReadOnlyQuery("
|
||||
$subquery = is_null($ort_kurzbz) ? $lvplan_reservierungen_query : $raum_reservierungen_query;
|
||||
|
||||
$query_result = $this->execReadOnlyQuery("
|
||||
SELECT
|
||||
'reservierung' as type, beginn, ende, datum,
|
||||
COALESCE(titel, beschreibung) as topic,
|
||||
@@ -59,15 +59,15 @@ class Reservierung_model extends DB_Model
|
||||
|
||||
FROM
|
||||
(
|
||||
". $subquery ."
|
||||
" . $subquery . "
|
||||
) AS subquery
|
||||
|
||||
GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung
|
||||
|
||||
ORDER BY datum, beginn
|
||||
", is_null($ort_kurzbz) ?[getAuthUID(), getAuthUID(),$start_date,$end_date]: [$ort_kurzbz, $start_date, $end_date]);
|
||||
", is_null($ort_kurzbz) ? [$uid ?? getAuthUID(), $uid ?? getAuthUID(), $start_date, $end_date] : [$ort_kurzbz, $start_date, $end_date]);
|
||||
|
||||
|
||||
|
||||
return $query_result;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ class Reservierung_model extends DB_Model
|
||||
*
|
||||
* @return stdClass
|
||||
*/
|
||||
public function getReservierungenMitarbeiter($start_date, $end_date)
|
||||
public function getReservierungenMitarbeiter($start_date, $end_date, $uid = null)
|
||||
{
|
||||
|
||||
$raum_reservierungen_query = "SELECT res.*, beginn, ende,
|
||||
@@ -91,8 +91,8 @@ class Reservierung_model extends DB_Model
|
||||
|
||||
$subquery = $raum_reservierungen_query;
|
||||
|
||||
|
||||
$query_result= $this->execReadOnlyQuery("
|
||||
|
||||
$query_result = $this->execReadOnlyQuery("
|
||||
SELECT
|
||||
'reservierung' as type, beginn, ende, datum,
|
||||
COALESCE(titel, beschreibung) as topic,
|
||||
@@ -103,13 +103,13 @@ class Reservierung_model extends DB_Model
|
||||
|
||||
FROM
|
||||
(
|
||||
". $subquery ."
|
||||
" . $subquery . "
|
||||
) AS subquery
|
||||
|
||||
GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung
|
||||
|
||||
ORDER BY datum, beginn
|
||||
", [getAuthUID(), $start_date, $end_date]);
|
||||
", [$uid ?? getAuthUID(), $start_date, $end_date]);
|
||||
|
||||
|
||||
return $query_result;
|
||||
@@ -129,9 +129,9 @@ class Reservierung_model extends DB_Model
|
||||
$this->addJoin('public.tbl_studiensemester ss2', 'slv.studiensemester_kurzbz=ss2.studiensemester_kurzbz AND ss2.start<=r.datum AND ss2.ende>=r.datum', 'LEFT');
|
||||
$this->db->or_where('ss1.studiensemester_kurzbz IS NOT NULL', null, false);
|
||||
$this->db->or_where('ss2.studiensemester_kurzbz IS NOT NULL', null, false);
|
||||
|
||||
|
||||
$query = $this->db->get_compiled_select('campus.vw_reservierung r');
|
||||
|
||||
|
||||
return $this->execQuery($query, [$uid, $uid]);
|
||||
}
|
||||
|
||||
|
||||
@@ -388,6 +388,84 @@ class Stundenplan_model extends DB_Model
|
||||
|
||||
ORDER BY datum, beginn", [$start_date, $end_date, $ma_uid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* queries Stundenplan and filters by studiengang, semester, verband gruppe
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getStundenplanStudiengang($start_date, $end_date, $stg_kz, $sem, $verband, $gruppe) {
|
||||
|
||||
$qry_params = [$start_date, $end_date, $stg_kz];
|
||||
|
||||
$qry = "
|
||||
SELECT
|
||||
'lehreinheit' as type, beginn, ende, datum,
|
||||
CONCAT(lehrfach,'-',lehrform) as topic,
|
||||
array_agg(DISTINCT lektor) as lektor,
|
||||
array_agg(DISTINCT (gruppe,verband,semester,studiengang_kz,gruppen_kuerzel)) as gruppe,
|
||||
string_agg(DISTINCT ort_kurzbz, '/') as ort_kurzbz,
|
||||
array_agg(DISTINCT lehreinheit_id) as lehreinheit_id,
|
||||
titel, lehrfach, lehrform, lehrfach_bez, organisationseinheit, farbe, lehrveranstaltung_id
|
||||
FROM
|
||||
(
|
||||
SELECT unr,datum,beginn, ende,
|
||||
CASE
|
||||
WHEN sp.mitarbeiter_kurzbz IS NOT NULL THEN sp.mitarbeiter_kurzbz
|
||||
ELSE sp.lektor
|
||||
END as lektor,
|
||||
CASE
|
||||
WHEN gruppe_kurzbz IS NOT NULL THEN gruppe_kurzbz
|
||||
ELSE CONCAT(UPPER(sp.stg_typ),UPPER(sp.stg_kurzbz),'-',COALESCE(CAST(sp.semester AS varchar),'/'),COALESCE(CAST(sp.verband AS varchar),'/'))
|
||||
END as gruppen_kuerzel,
|
||||
(SELECT bezeichnung
|
||||
FROM public.tbl_organisationseinheit
|
||||
WHERE oe_kurzbz IN(
|
||||
SELECT oe_kurzbz
|
||||
FROM lehre.tbl_lehrveranstaltung
|
||||
WHERE lehrveranstaltung_id = sp.lehrveranstaltung_id
|
||||
)) as organisationseinheit,
|
||||
sp.ort_kurzbz, sp.studiengang_kz, sp.titel,sp.lehreinheit_id,sp.lehrfach_id,sp.anmerkung,fix,lehrveranstaltung_id,stg_kurzbzlang,stg_bezeichnung,stg_typ,fachbereich_kurzbz,lehrfach,lehrfach_bez,farbe,lehrform,anmerkung_lehreinheit,gruppe, verband, semester,stg_kurzbz
|
||||
FROM (
|
||||
SELECT sp.*
|
||||
FROM lehre.vw_stundenplan sp
|
||||
WHERE
|
||||
sp.datum >= ?
|
||||
AND sp.datum <= ?
|
||||
) sp
|
||||
JOIN lehre.tbl_stunde ON lehre.tbl_stunde.stunde = sp.stunde
|
||||
WHERE studiengang_kz = ? ";
|
||||
|
||||
if($sem != NULL)
|
||||
{
|
||||
$qry_params[] = $sem;
|
||||
$qry .= " AND (semester = ? OR semester IS NULL)";
|
||||
}
|
||||
if($verband != NULL)
|
||||
{
|
||||
$qry_params[] = $verband;
|
||||
$qry .= " AND (verband = ? OR verband IS NULL OR verband = '0' OR verband = '')";
|
||||
}
|
||||
if($gruppe != NULL)
|
||||
{
|
||||
$qry_params[] = $gruppe;
|
||||
$qry .= " AND (gruppe = ? OR gruppe IS NULL OR gruppe = '0' OR gruppe = '') ";
|
||||
}
|
||||
$qry.= " AND (
|
||||
gruppe_kurzbz is null OR EXISTS(
|
||||
SELECT 1
|
||||
FROM
|
||||
public.tbl_gruppe WHERE gruppe_kurzbz = sp.gruppe_kurzbz AND direktinskription = false
|
||||
)
|
||||
)";
|
||||
|
||||
$qry.= " ) as subquery
|
||||
|
||||
GROUP BY unr, datum, beginn, ende, titel, lehrform, lehrfach, lehrfach_bez, organisationseinheit, farbe, lehrveranstaltung_id
|
||||
ORDER BY datum, beginn; ";
|
||||
|
||||
return $this->execReadOnlyQuery($qry, $qry_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* NO STANDALONE FUNCTION - Generates a SQL query string to fetch 'stundenplan' events for a specific student within the current semester.
|
||||
|
||||
@@ -23,12 +23,14 @@ $includesArray = array(
|
||||
'public/css/components/FormUnderline.css',
|
||||
'public/css/components/abgabetool/abgabe.css',
|
||||
'public/css/Cis4/Cms.css',
|
||||
'public/css/Cis4/Studium.css',
|
||||
'public/css/Cis4/Studium.css'
|
||||
),
|
||||
'customJSs' => array(
|
||||
'vendor/npm-asset/primevue/accordion/accordion.min.js',
|
||||
'vendor/npm-asset/primevue/accordiontab/accordiontab.min.js',
|
||||
'vendor/npm-asset/primevue/checkbox/checkbox.min.js',
|
||||
'vendor/npm-asset/primevue/chips/chips.min.js',
|
||||
'vendor/npm-asset/primevue/multiselect/multiselect.min.js',
|
||||
'vendor/npm-asset/primevue/inputnumber/inputnumber.min.js',
|
||||
'vendor/npm-asset/primevue/speeddial/speeddial.min.js',
|
||||
'vendor/npm-asset/primevue/textarea/textarea.min.js',
|
||||
@@ -39,7 +41,7 @@ $includesArray = array(
|
||||
'vendor/moment/luxonjs/luxon.min.js'
|
||||
),
|
||||
'customJSModules' => array(
|
||||
'public/js/apps/Dashboard/Fhc.js',
|
||||
'public/js/apps/Cis.js',
|
||||
),
|
||||
|
||||
);
|
||||
@@ -47,8 +49,6 @@ $includesArray = array(
|
||||
$this->load->view('templates/CISVUE-Header', $includesArray);
|
||||
?>
|
||||
<div id="fhccontent" class="h-100" route=<?php echo $route ?>>
|
||||
<router-view
|
||||
:view-data='<?php echo json_encode($viewData) ?>'
|
||||
></router-view>
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
<?php $this->load->view('templates/CISVUE-Footer', $includesArray); ?>
|
||||
|
||||
@@ -6,7 +6,7 @@ $includesArray = array(
|
||||
'fontawesome6' => true,
|
||||
'axios027' => true,
|
||||
'customJSModules' => array_merge([
|
||||
'public/js/apps/Cis.js'
|
||||
'public/js/apps/Cis/Menu.js'
|
||||
], $customJSModules ?? []),
|
||||
'customCSSs' => array_merge([
|
||||
'public/css/Cis4/Cis.css'
|
||||
|
||||
@@ -8,7 +8,7 @@ $includesArray = array(
|
||||
'axios027' => true,
|
||||
'primevue3' => true,
|
||||
'customJSModules' => array_merge([
|
||||
'public/js/apps/Cis.js'
|
||||
'public/js/apps/Cis/Menu.js'
|
||||
], $customJSModules ?? []),
|
||||
'customCSSs' => array_merge([
|
||||
'public/css/Cis4/Cis.css',
|
||||
|
||||
@@ -70,6 +70,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "drag-drop-touch-js/dragdroptouch",
|
||||
"version": "2.0.3",
|
||||
"source": {
|
||||
"url": "https://github.com/drag-drop-touch-js/dragdroptouch.git",
|
||||
"type": "git",
|
||||
"reference": "master"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
@@ -452,6 +464,8 @@
|
||||
|
||||
"easyrdf/easyrdf": "0.9.*",
|
||||
|
||||
"drag-drop-touch-js/dragdroptouch": "*",
|
||||
|
||||
"fgelinas/timepicker": "0.3.3",
|
||||
"fortawesome/font-awesome4": "4.7.*",
|
||||
"fortawesome/font-awesome6": "6.1.*",
|
||||
|
||||
Generated
+11
-1
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "f4f0af4586f46f97d8b6092c1ac0fb3a",
|
||||
"content-hash": "869cbc35bd1ba90ab90934fcb41b0f51",
|
||||
"packages": [
|
||||
{
|
||||
"name": "afarkas/html5shiv",
|
||||
@@ -804,6 +804,16 @@
|
||||
"abandoned": true,
|
||||
"time": "2018-03-09T06:07:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "drag-drop-touch-js/dragdroptouch",
|
||||
"version": "2.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/drag-drop-touch-js/dragdroptouch.git",
|
||||
"reference": "master"
|
||||
},
|
||||
"type": "library"
|
||||
},
|
||||
{
|
||||
"name": "easyrdf/easyrdf",
|
||||
"version": "0.9.1",
|
||||
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "FHC-Core",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
+11
-1
@@ -147,6 +147,8 @@ html {
|
||||
--fhc-cis-menu-lvl-5-color-hover: var(--fhc-text);
|
||||
--fhc-cis-grade-positive: var(--fhc-success);
|
||||
--fhc-cis-grade-negative: var(--fhc-danger);
|
||||
|
||||
--fhc-offcanvas-zindex: 1045;
|
||||
}
|
||||
|
||||
#themeSwitch i{
|
||||
@@ -375,7 +377,7 @@ html {
|
||||
/* searchbar */
|
||||
#nav-search {
|
||||
background-color: var(--fhc-primary);
|
||||
z-index: 1;
|
||||
z-index: calc(var(--fhc-offcanvas-zindex) + 1) !important;
|
||||
}
|
||||
#nav-search.me-3 {
|
||||
margin: 0 !important;
|
||||
@@ -413,10 +415,18 @@ html {
|
||||
color: var(--fhc-link) !important;
|
||||
}
|
||||
|
||||
#nav-main {
|
||||
z-index: var(--fhc-offcanvas-zindex);
|
||||
}
|
||||
|
||||
#nav-main-sticky {
|
||||
max-height: calc(100vh - var(--fhc-cis-header-height));
|
||||
}
|
||||
|
||||
#nav-user-menu {
|
||||
z-index: calc(var(--fhc-offcanvas-zindex) + 1);
|
||||
}
|
||||
|
||||
#nav-user-menu img {
|
||||
object-fit: cover;
|
||||
height: calc( 3 * var(--fhc-cis-header-py));
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@import './dashboard/news.css';
|
||||
@import './dashboard/LvPlan.css';
|
||||
|
||||
:root{
|
||||
:root {
|
||||
--fhc-dashboard-danger: var(--fhc-danger, #842029);
|
||||
--fhc-dashboard-grid-size: 4;
|
||||
--fhc-dashboard-link: var(--fhc-link, #0a57ca);
|
||||
@@ -17,22 +17,16 @@
|
||||
--fhc-dashboard-section-info-color-hover: var(--fhc-primary-highlight, #005585);
|
||||
}
|
||||
|
||||
@media(max-width: 577px) {
|
||||
:root {
|
||||
--fhc-dashboard-grid-size: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.core-dashboard a{
|
||||
.core-dashboard a {
|
||||
color: var(--fhc-dashboard-link);
|
||||
}
|
||||
|
||||
@media (max-width: 576px){
|
||||
@media (max-width: 576px) {
|
||||
.widget-icon {
|
||||
max-height: 250px;
|
||||
object-fit: cover;
|
||||
}
|
||||
.widget-icon-container{
|
||||
.widget-icon-container {
|
||||
max-width: 250px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
@@ -46,27 +40,36 @@
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
cursor:pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dashboard-section > .newGridRow{
|
||||
position:absolute;
|
||||
width:20px;
|
||||
height:20px;
|
||||
padding:0;
|
||||
bottom:0;
|
||||
left:50%;
|
||||
transform:translate(-50%, 50%);
|
||||
.dashboard-section.edit-active {
|
||||
/**
|
||||
* replaces margin for extra row
|
||||
* 10% equals 0.1 of 100%
|
||||
* 1rem equals the padding of pb-3 that is overwritten here
|
||||
*/
|
||||
padding-bottom: calc(10% / var(--fhc-dashboard-grid-size) + 1rem) !important;
|
||||
}
|
||||
|
||||
.dashboard-section > .newGridRow {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
padding: 0;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 50%);
|
||||
background-color: var(--fhc-dashboard-gridrow-background);
|
||||
}
|
||||
|
||||
.newGridRow:hover {
|
||||
color:white;
|
||||
color: white;
|
||||
background-color: var(--fhc-dashboard-gridrow-background-highlight);
|
||||
}
|
||||
|
||||
.empty-tile-hover:hover {
|
||||
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="-500 -500 1448 1512"><path fill="rgb(211, 211, 211)" d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM200 344V280H136c-13.3 0-24-10.7-24-24s10.7-24 24-24h64V168c0-13.3 10.7-24 24-24s24 10.7 24 24v64h64c13.3 0 24 10.7 24 24s-10.7 24-24 24H248v64c0 13.3-10.7 24-24 24s-24-10.7-24-24z"/></svg>');
|
||||
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="-500 -500 1448 1512"><path fill="rgb(211, 211, 211)" d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM200 344V280H136c-13.3 0-24-10.7-24-24s10.7-24 24-24h64V168c0-13.3 10.7-24 24-24s24 10.7 24 24v64h64c13.3 0 24 10.7 24 24s-10.7 24-24 24H248v64c0 13.3-10.7 24-24 24s-24-10.7-24-24z"/></svg>');
|
||||
}
|
||||
|
||||
.alert-danger .form-check-input:checked {
|
||||
@@ -74,16 +77,6 @@
|
||||
background-color: var(--fhc-dashboard-danger);
|
||||
}
|
||||
|
||||
:root {
|
||||
--fhc-dashboard-grid-size: 4;
|
||||
}
|
||||
|
||||
@media(max-width: 1400px) {
|
||||
:root {
|
||||
--fhc-dashboard-grid-size: 4;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: 1200px) {
|
||||
:root {
|
||||
--fhc-dashboard-grid-size: 3;
|
||||
@@ -105,6 +98,7 @@
|
||||
@media(max-width: 577px) {
|
||||
:root {
|
||||
--fhc-dashboard-grid-size: 1;
|
||||
--fhc-dg-item-py: .75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,50 +126,64 @@
|
||||
cursor: move !important;
|
||||
}
|
||||
|
||||
.draggedItem {
|
||||
.drop-grid-item-resize > .dashboard-item,
|
||||
.drop-grid-item-move > .dashboard-item {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: var(--fhc-dashboard-draggeditem-background);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dashboard-item-overlay{
|
||||
.drop-grid-item-resize > .dashboard-item > *,
|
||||
.drop-grid-item-move > .dashboard-item > * {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.drop-grid-item-sizechanged > .dashboard-item,
|
||||
.drop-grid-item-move > .dashboard-item {
|
||||
background-color: var(--fhc-dashboard-item-overlay-background);
|
||||
}
|
||||
|
||||
.dashboard-item-overlay::before{
|
||||
position:absolute;
|
||||
content:"";
|
||||
top:0.25rem;
|
||||
left:0.25rem;
|
||||
right:0.25rem;
|
||||
bottom:0.25rem;
|
||||
border:4px dashed var(--fhc-dashboard-item-overly-border-color);
|
||||
opacity: 0.5;
|
||||
.drop-grid-item-sizechanged > .dashboard-item::before,
|
||||
.drop-grid-item-move > .dashboard-item::before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
top: .25rem;
|
||||
left: .25rem;
|
||||
right: .25rem;
|
||||
bottom: .25rem;
|
||||
border: 4px dashed var(--fhc-dashboard-item-overly-border-color);
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
#deleteBookmark i{
|
||||
.drop-grid-item-oversized > .dashboard-item {
|
||||
/* Bootstrap: border-danger */
|
||||
--bs-border-opacity: 1;
|
||||
border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important;
|
||||
}
|
||||
|
||||
#deleteBookmark i {
|
||||
color: var(--fhc-dashboard-danger);
|
||||
}
|
||||
|
||||
.pin:hover{
|
||||
.pin:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pin[pinned]:hover{
|
||||
.pin[pinned]:hover {
|
||||
color: var(--fhc-dashboard-pin-pinned-hover-color);
|
||||
}
|
||||
|
||||
.section-info{
|
||||
.section-info {
|
||||
color: var(--fhc-dashboard-section-info-color);
|
||||
cursor:pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.section-info:hover {
|
||||
color: var(--fhc-dashboard-section-info-color-hover);
|
||||
}
|
||||
|
||||
.denied-dragging-animation {
|
||||
.drop-grid-item-blocker [pinned='true'] {
|
||||
animation: wiggle 0.5s linear;
|
||||
color: var(--fhc-dashboard-denied-dragging-animation-color) !important;
|
||||
}
|
||||
@@ -204,13 +212,13 @@
|
||||
|
||||
}
|
||||
|
||||
.hiddenWidget{
|
||||
.hidden-widget {
|
||||
background: var(--fhc-disabled-background);
|
||||
opacity: 40%;
|
||||
}
|
||||
|
||||
.hiddenWidget .card,
|
||||
.hiddenWidget .card-body,
|
||||
.hiddenWidget .card-body *{
|
||||
.hidden-widget .card,
|
||||
.hidden-widget .card-body,
|
||||
.hidden-widget .card-body * {
|
||||
background: inherit !important;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export default {
|
||||
getViewData() {
|
||||
return {
|
||||
method: 'get',
|
||||
url: '/api/frontend/v1/Cis4FhcApi/getViewData'
|
||||
url: '/api/frontend/v1/Cis4FhcApi/dashboardViewData'
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (C) 2026 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 {
|
||||
getAllStudienSemester(studiensemester, studiengang, semester, studienplan) {
|
||||
return {
|
||||
method: 'get',
|
||||
url: 'api/frontend/v1/Studium/getStudienAllSemester/',
|
||||
params: {studiensemester, studiengang, semester, studienplan}
|
||||
};
|
||||
},
|
||||
}
|
||||
@@ -35,5 +35,11 @@ export default {
|
||||
method: 'get',
|
||||
url: `/api/frontend/v1/Lehre/Pruefungen/${lehrveranstaltung_id}`
|
||||
};
|
||||
},
|
||||
getSemesterAverageGrade(semester) {
|
||||
return {
|
||||
method: 'get',
|
||||
url: `/api/frontend/v1/Lehre/semesterAverageGrade/${semester}`
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -16,6 +16,12 @@
|
||||
*/
|
||||
|
||||
export default {
|
||||
getMyLvPlanViewData() {
|
||||
return {
|
||||
method: 'get',
|
||||
url: `/api/frontend/v1/LvPlan/myLvPlanViewData`,
|
||||
};
|
||||
},
|
||||
getRoomInfo(ort_kurzbz, start_date, end_date) {
|
||||
return {
|
||||
method: 'post',
|
||||
@@ -30,11 +36,11 @@ export default {
|
||||
params: { start_date, end_date, lv_id }
|
||||
};
|
||||
},
|
||||
eventsPersonal(start_date, end_date) {
|
||||
eventsPersonal(start_date, end_date, uid = null) {
|
||||
return {
|
||||
method: 'post',
|
||||
url: '/api/frontend/v1/lvPlan/eventsPersonal',
|
||||
params: { start_date, end_date }
|
||||
params: { start_date, end_date, uid }
|
||||
};
|
||||
},
|
||||
eventsLv(lv_id, start_date, end_date) {
|
||||
@@ -57,11 +63,11 @@ export default {
|
||||
params: { start_date, end_date }
|
||||
};
|
||||
},
|
||||
getLvPlanReservierungen(start_date, end_date) {
|
||||
getLvPlanReservierungen(start_date, end_date, uid = null) {
|
||||
return {
|
||||
method: 'post',
|
||||
url: '/api/frontend/v1/LvPlan/getReservierungen',
|
||||
params: { start_date, end_date }
|
||||
params: { start_date, end_date, uid }
|
||||
};
|
||||
},
|
||||
getLehreinheitStudiensemester(lehreinheit_id) {
|
||||
@@ -92,5 +98,42 @@ export default {
|
||||
method: 'get',
|
||||
url: '/api/frontend/v1/LvPlan/getLv/' + lehrveranstaltung_id
|
||||
};
|
||||
}
|
||||
},
|
||||
eventsStgOrg(start_date, end_date, stg_kz, sem, verband, gruppe) {
|
||||
return {
|
||||
method: 'post',
|
||||
url: '/api/frontend/v1/lvPlan/eventsStgOrg',
|
||||
params: { start_date, end_date, stg_kz, sem, verband, gruppe }
|
||||
};
|
||||
},
|
||||
getStudiengaenge(){
|
||||
return {
|
||||
method: 'get',
|
||||
url: '/api/frontend/v1/lvPlan/getStudiengaenge'
|
||||
}
|
||||
},
|
||||
getLehrverband(stg_kz, sem){
|
||||
return {
|
||||
method: 'get',
|
||||
url: `/api/frontend/v1/lvPlan/getLehrverband/${stg_kz}/${sem}`
|
||||
}
|
||||
},
|
||||
getGruppe(stg_kz, sem, verband){
|
||||
return {
|
||||
method: 'get',
|
||||
url: `/api/frontend/v1/lvPlan/getLehrverband/${stg_kz}/${sem}/${verband}`
|
||||
}
|
||||
},
|
||||
checkPermissionOtherLvPlan(){
|
||||
return {
|
||||
method: 'get',
|
||||
url: '/api/frontend/v1/lvPlan/permissionOtherLvPlan',
|
||||
}
|
||||
},
|
||||
getCompactibleEventTypes(){
|
||||
return {
|
||||
method: 'get',
|
||||
url: '/api/frontend/v1/lvPlan/compactibleEventTypes',
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 {
|
||||
getOtherLvPlanViewData(uid) {
|
||||
return {
|
||||
method: 'get',
|
||||
url: `/api/frontend/v1/OtherLvPlan/otherLvPlanViewData/${uid}`,
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
export default {
|
||||
|
||||
profilViewData(uid) {
|
||||
getProfilViewData(uid = null) {
|
||||
let url = "/api/frontend/v1/Profil/profilViewData";
|
||||
if(uid){
|
||||
url += `/${uid}`;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 {
|
||||
getStgOrgLvPlanViewData(uid) {
|
||||
return {
|
||||
method: 'get',
|
||||
url: `/api/frontend/v1/StgOrgLvPlan/stgOrgLvPlanViewData`,
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
export default {
|
||||
getBookmarks() {
|
||||
|
||||
return {
|
||||
method: 'get',
|
||||
url: '/api/frontend/v1/Bookmark/getBookmarks'
|
||||
@@ -28,18 +29,55 @@ export default {
|
||||
url: `/api/frontend/v1/Bookmark/delete/${bookmark_id}`
|
||||
};
|
||||
},
|
||||
update({ bookmark_id, url, title, tag=null }) {
|
||||
update({ bookmark_id, url, title, tag }) {
|
||||
return {
|
||||
method: 'post',
|
||||
url: `/api/frontend/v1/Bookmark/update/${bookmark_id}`,
|
||||
params: { url, title }
|
||||
params: { url, title, tag }
|
||||
};
|
||||
},
|
||||
insert({ url, title, tag }) {
|
||||
insert({ url, title, tag, sort }) {
|
||||
return {
|
||||
method: 'post',
|
||||
url: `/api/frontend/v1/Bookmark/insert`,
|
||||
params: { url, title, tag }
|
||||
params: { url, title, tag, sort }
|
||||
};
|
||||
},
|
||||
changeOrder(bookmark_id1, bookmark_id2) {
|
||||
return {
|
||||
method: 'post',
|
||||
url: `/api/frontend/v1/Bookmark/changeOrder/${bookmark_id1}/${bookmark_id2}`,
|
||||
};
|
||||
},
|
||||
getAllBookmarkTags() {
|
||||
return {
|
||||
method: 'get',
|
||||
url: '/api/frontend/v1/Bookmark/getAllBookmarkTags'
|
||||
};
|
||||
},
|
||||
getTagFilter(widgetId, sectionName){
|
||||
return {
|
||||
method: 'get',
|
||||
url: `/api/frontend/v1/Bookmark/getTagFilter/${widgetId}/${sectionName}`
|
||||
};
|
||||
},
|
||||
addTagFilter(widgetId, sectionName, tags){
|
||||
return {
|
||||
method: 'post',
|
||||
url: `/api/frontend/v1/Bookmark/addAndUpdateTagFilter/${widgetId}/${sectionName}`,
|
||||
params: { tags }
|
||||
};
|
||||
},
|
||||
isInOverride(widgetId, sectionName){
|
||||
return {
|
||||
method: 'post',
|
||||
url: `/api/frontend/v1/Bookmark/isInOverride/${widgetId}/${sectionName}`
|
||||
};
|
||||
},
|
||||
addWidgetToOverride(widgetId, sectionName, mode, x, y, h, w){
|
||||
return {
|
||||
method: 'post',
|
||||
url: `/api/frontend/v1/Bookmark/addWidgetToOverride/${widgetId}/${sectionName}/${mode}/${x}/${y}/${h}/${w}`,
|
||||
};
|
||||
}
|
||||
};
|
||||
+321
-140
@@ -1,156 +1,337 @@
|
||||
import FhcSearchbar from "../components/searchbar/searchbar.js";
|
||||
import CisMenu from "../components/Cis/Menu.js";
|
||||
import FhcDashboard from '../components/Dashboard/Dashboard.js';
|
||||
import PluginsPhrasen from '../plugins/Phrasen.js';
|
||||
import ApiSearchbar from '../api/factory/searchbar.js';
|
||||
import Theme from "../plugins/Theme.js";
|
||||
import Theme from '../plugins/Theme.js';
|
||||
import contrast from '../directives/contrast.js';
|
||||
import {setScrollbarWidth} from "../helpers/CssVarCalcHelpers.js";
|
||||
import LvPlan from "../components/Cis/LvPlan/Lehrveranstaltung.js";
|
||||
import MyLvPlan from "../components/Cis/LvPlan/MyLvPlan.js";
|
||||
import MylvStudent from "../components/Cis/Mylv/Student.js";
|
||||
import Profil from "../components/Cis/Profil/Profil.js";
|
||||
import Raumsuche from "../components/Cis/Raumsuche/Raumsuche.js";
|
||||
import CmsNews from "../components/Cis/Cms/News.js";
|
||||
import CmsContent from "../components/Cis/Cms/Content.js";
|
||||
import Info from "../components/Cis/Mylv/Semester/Studiengang/Lv/Info.js";
|
||||
import RoomInformation, {DEFAULT_MODE_RAUMINFO_DESKTOP, DEFAULT_MODE_RAUMINFO_MOBILE} from "../components/Cis/Mylv/RoomInformation.js";
|
||||
import AbgabetoolStudent from "../components/Cis/Abgabetool/AbgabetoolStudent.js";
|
||||
import AbgabetoolMitarbeiter from "../components/Cis/Abgabetool/AbgabetoolMitarbeiter.js";
|
||||
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 StgOrgLvPlan from "../components/Cis/LvPlan/StgOrg.js";
|
||||
import OtherLvPlan from "../components/Cis/LvPlan/OtherLvPlan.js";
|
||||
|
||||
import ApiRouteInfo from '../api/factory/routeinfo.js';
|
||||
import {capitalize} from "../helpers/StringHelpers.js";
|
||||
|
||||
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
|
||||
const isMobile = window.matchMedia("(max-width: 767px)").matches;
|
||||
|
||||
const router = VueRouter.createRouter({
|
||||
history: VueRouter.createWebHistory(`/${ciPath}`),
|
||||
routes: [
|
||||
{
|
||||
path: `/Cis/Studium`,
|
||||
name: 'Studium',
|
||||
component: Studium,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Profil/View/:uid`,
|
||||
name: 'ProfilView',
|
||||
component: Profil,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Profil`,
|
||||
name: 'Profil',
|
||||
component: Profil,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Abgabetool/Student/:student_uid_prop?`,
|
||||
name: 'AbgabetoolStudent',
|
||||
component: AbgabetoolStudent,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Abgabetool/Mitarbeiter`,
|
||||
name: 'AbgabetoolMitarbeiter',
|
||||
component: AbgabetoolMitarbeiter,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Abgabetool/Assistenz/:stg_kz_prop?`,
|
||||
name: 'AbgabetoolAssistenz',
|
||||
component: AbgabetoolAssistenz,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Abgabetool/Deadlines/:person_uid_prop?`,
|
||||
name: 'DeadlineOverview',
|
||||
component: DeadlineOverview,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Raumsuche`,
|
||||
name: 'Raumsuche',
|
||||
component: Raumsuche,
|
||||
props: true
|
||||
},
|
||||
// Redirect old links to new format
|
||||
{
|
||||
path: "/CisVue/Cms/getRoomInformation/:ort_kurzbz",
|
||||
name: "RoomInformationOld",
|
||||
component: RoomInformation,
|
||||
redirect: (to) => {
|
||||
return { // redirect to longer Rauminfo url and map params
|
||||
name: "RoomInformation",
|
||||
params: { // in this case always populate other params since they are not optional
|
||||
ort_kurzbz: to.params.ort_kurzbz,
|
||||
mode: isMobile ? DEFAULT_MODE_RAUMINFO_MOBILE : DEFAULT_MODE_RAUMINFO_DESKTOP,
|
||||
focus_date: new Date().toISOString().split("T")[0]
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
path: `/CisVue/Cms/getRoomInformation/:mode/:focus_date/:ort_kurzbz`,
|
||||
name: 'RoomInformation',
|
||||
component: RoomInformation,
|
||||
props: (route) => { // validate and set mode/focus date if for some reason missing
|
||||
const validModes = ["Month", "Week", "Day"];
|
||||
|
||||
// check mode string
|
||||
const mode = route.params.mode &&
|
||||
validModes.includes(route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase())
|
||||
? route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase()
|
||||
: (isMobile ? DEFAULT_MODE_RAUMINFO_MOBILE : DEFAULT_MODE_RAUMINFO_DESKTOP);
|
||||
|
||||
// default to today date if not provided
|
||||
const d = new Date(route.params.focus_date)
|
||||
const focus_date = !isNaN(d) ? route.params.focus_date : new Date().toISOString().split("T")[0];
|
||||
|
||||
// for consistency reasons format the props into one object but actually use a new name to we dont collide with
|
||||
// existing viewData declaration written from codeigniter 3 into routerview tag
|
||||
return {
|
||||
propsViewData: {
|
||||
mode,
|
||||
focus_date,
|
||||
ort_kurzbz: route.params.ort_kurzbz
|
||||
}
|
||||
};
|
||||
},
|
||||
beforeEnter: (to, from, next) => {
|
||||
// missing mode or focus_date -> set defaults
|
||||
if (!to.params.mode || !to.params.focus_date) {
|
||||
next({
|
||||
name: "RoomInformation",
|
||||
params: {
|
||||
mode: to.params.mode || DEFAULT_MODE_RAUMINFO,
|
||||
focus_date: to.params.focus_date || new Date().toISOString().split("T")[0],
|
||||
ort_kurzbz: route.params.ort_kurzbz
|
||||
}
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/CisVue/Cms/Content/:content_id`,
|
||||
name: 'Content',
|
||||
component: CmsContent,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/CisVue/Cms/News`,
|
||||
name: 'News',
|
||||
component: CmsNews,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/MyLv/:studiensemester?`,
|
||||
name: 'MyLv',
|
||||
component: MylvStudent,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: `/Cis/MyLv/Info/:studien_semester/:lehrveranstaltung_id`,
|
||||
name: 'LvInfo',
|
||||
component: Info,
|
||||
props: true
|
||||
},
|
||||
// Redirect old links to new format
|
||||
{
|
||||
// only trigger on first param being numeric to avoid paths like "LvPlan/Month" entering here
|
||||
path: "/Cis/LvPlan/:lv_id(\\d+)",
|
||||
name: "LvPlanOld",
|
||||
component: LvPlan,
|
||||
redirect(to) {
|
||||
const route = Vue.unref(router.currentRoute);
|
||||
const { mode, focus_date } = route.params; // keep mode and focus_date if available
|
||||
return { // redirect to longer LvPlan url and map params
|
||||
name: "LvPlan",
|
||||
params: {
|
||||
mode,
|
||||
focus_date,
|
||||
lv_id: to.params.lv_id
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
path: `/Cis/LvPlan/:mode?/:focus_date?/:lv_id?`,
|
||||
name: 'LvPlan',
|
||||
component: LvPlan,
|
||||
props(route) {
|
||||
return {
|
||||
propsViewData: route.params
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/Cis/MyLvPlan/:mode?/:focus_date?`,
|
||||
name: 'MyLvPlan',
|
||||
component: MyLvPlan,
|
||||
props(route) {
|
||||
return {
|
||||
propsViewData: route.params
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/Cis/StgOrgLvPlan/:mode?/:focus_date?/:stgkz?/:sem?/:verband?/:gruppe?`,
|
||||
name: 'StgOrgLvPlan',
|
||||
component: StgOrgLvPlan,
|
||||
props(route) {
|
||||
return {
|
||||
propsViewData: route.params
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/Cis/OtherLvPlan/:otherUid/:mode?/:focus_date?`,
|
||||
name: "OtherLvPlan",
|
||||
component: OtherLvPlan,
|
||||
props(route) {
|
||||
return {
|
||||
propsViewData: route.params
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/Cis4`,
|
||||
name: 'Cis4',
|
||||
component: FhcDashboard,
|
||||
props: {dashboard: 'CIS'},
|
||||
},
|
||||
{
|
||||
path: `/`,
|
||||
name: 'FhcDashboard',
|
||||
component: FhcDashboard,
|
||||
props: {dashboard: 'CIS'},
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'Fallback',
|
||||
component: FhcDashboard,
|
||||
props: {dashboard: 'CIS'},
|
||||
redirect: () => {
|
||||
return {
|
||||
name: "Cis4",
|
||||
params: {
|
||||
dashboard: 'CIS'
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const app = Vue.createApp({
|
||||
name: 'CisApp',
|
||||
components: {
|
||||
FhcSearchbar,
|
||||
CisMenu
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
searchbaroptions: {
|
||||
origin: "cis",
|
||||
cssclass: "",
|
||||
calcheightonly: true,
|
||||
types: {
|
||||
employee: Vue.computed(() => this.$p.t("search/type_employee")),
|
||||
student: Vue.computed(() => this.$p.t("search/type_student")),
|
||||
room: Vue.computed(() => this.$p.t("search/type_room")),
|
||||
organisationunit: Vue.computed(() => this.$p.t("search/type_organisationunit")),
|
||||
cms: Vue.computed(() => this.$p.t("search/type_cms")),
|
||||
dms: Vue.computed(() => this.$p.t("search/type_dms"))
|
||||
},
|
||||
actions: {
|
||||
employee: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
action: function(data) {
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router+
|
||||
"/Cis/Profil/View/"+data.uid;
|
||||
}
|
||||
},
|
||||
childactions: []
|
||||
},
|
||||
student: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
action: function (data) {
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
"/Cis/Profil/View/" + data.uid;
|
||||
name: 'CisApp',
|
||||
data: () => ({
|
||||
appSideMenuEntries: {},
|
||||
renderers: null,
|
||||
}),
|
||||
components: {},
|
||||
computed: {
|
||||
isMobile() {
|
||||
const smallScreen = window.matchMedia("(max-width: 767px)").matches;
|
||||
const touchCapable = ("ontouchstart" in window) || navigator.maxTouchPoints > 0;
|
||||
return smallScreen;// && touchCapable;
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return { // provide injectable & watchable language property
|
||||
language: Vue.computed(() => this.$p.user_language),
|
||||
isMobile: this.isMobile,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isInternalRoute(href) {
|
||||
const internalBase = window.location.origin
|
||||
return href.startsWith(internalBase);
|
||||
},
|
||||
handleClick(event) {
|
||||
const target = event.target.closest('a');
|
||||
|
||||
}
|
||||
},
|
||||
childactions: []
|
||||
},
|
||||
room: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
renderif: function(data) {
|
||||
if(data.content_id === null){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
action: function(data) {
|
||||
const link= FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/CisVue/Cms/content/' + data.content_id;
|
||||
return link;
|
||||
}
|
||||
},
|
||||
childactions: [
|
||||
{
|
||||
label: "LV-Plan",
|
||||
icon: "fas fa-bookmark",
|
||||
type: "link",
|
||||
action: function(data) {
|
||||
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/CisVue/Cms/getRoomInformation/' + data.ort_kurzbz;
|
||||
return link;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Rauminformation",
|
||||
icon: "fas fa-info-circle",
|
||||
type: "link",
|
||||
renderif: function(data) {
|
||||
if(data.content_id === null){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
action: function(data) {
|
||||
const link= FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/CisVue/Cms/content/' + data.content_id;
|
||||
return link;
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
organisationunit: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
renderif: function(data) {
|
||||
if(data.mailgroup) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
action: function(data) {
|
||||
const link = 'mailto:' + data.mailgroup;
|
||||
return link;
|
||||
}
|
||||
},
|
||||
childactions: []
|
||||
},
|
||||
cms: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
action: function (data) {
|
||||
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/CisVue/Cms/content/' + data.content_id;
|
||||
return link;
|
||||
}
|
||||
},
|
||||
childactions: []
|
||||
},
|
||||
dms: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
action: function (data) {
|
||||
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
'cms/dms.php?id=' + data.dms_id;
|
||||
return link;
|
||||
}
|
||||
},
|
||||
childactions: []
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
searchfunction: function(searchsettings) {
|
||||
return this.$api.call(ApiSearchbar.searchCis(searchsettings));
|
||||
}
|
||||
}
|
||||
if(target?.id == 'skiplink') return
|
||||
if (target && this.isInternalRoute(target.href)) {
|
||||
const url = new URL(target.href)
|
||||
|
||||
const path = url.pathname
|
||||
const base = this.$router.options.history.base
|
||||
const route = path.replace(base, '') || '/'
|
||||
|
||||
// let click event propagate normally if we dont route internally
|
||||
const res = this.$router.resolve(route)
|
||||
if(!res?.matched?.length || res.name === 'Fallback') return
|
||||
|
||||
event.preventDefault(); // Prevent browser navigation
|
||||
|
||||
if(this.isMobile) { // toggle the menu
|
||||
const navMain = document.getElementById('nav-main');
|
||||
// fix unwanted toggle from off to on for some links on mobile
|
||||
if(navMain.classList.contains('show')){
|
||||
document.getElementById('nav-main-btn').click();
|
||||
}
|
||||
}
|
||||
|
||||
this.$router.push(route);
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('click', this.handleClick);
|
||||
|
||||
},
|
||||
beforeUnmount() {
|
||||
document.removeEventListener('click', this.handleClick);
|
||||
},
|
||||
});
|
||||
|
||||
// kind of a bandaid for bad css on some pages to avoid horizontal scroll
|
||||
setScrollbarWidth();
|
||||
app.config.globalProperties.$capitalize = capitalize;
|
||||
|
||||
FhcApps.router.makeExtendable(router);
|
||||
FhcApps.makeExtendable(app);
|
||||
|
||||
app.use(router);
|
||||
app.use(primevue.config.default, {
|
||||
zIndex: {
|
||||
overlay: 9000,
|
||||
tooltip: 8000
|
||||
}
|
||||
})
|
||||
app.directive('tooltip', primevue.tooltip);
|
||||
app.use(PluginsPhrasen);
|
||||
app.use(Theme);
|
||||
app.mount('#cis-header');
|
||||
app.directive('contrast', contrast);
|
||||
app.mount('#fhccontent');
|
||||
|
||||
router.afterEach((to, from, failure) => {
|
||||
app.config.globalProperties.$api.call(ApiRouteInfo.info('cis4', to.fullPath));
|
||||
});
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
import FhcSearchbar from "../../components/searchbar/searchbar.js";
|
||||
import CisMenu from "../../components/Cis/Menu.js";
|
||||
import PluginsPhrasen from '../../plugins/Phrasen.js';
|
||||
import Theme from "../../plugins/Theme.js";
|
||||
import ApiSearchbar from '../../api/factory/searchbar.js';
|
||||
import ApiLvPlan from "../../api/factory/lvPlan.js";
|
||||
|
||||
const app = Vue.createApp({
|
||||
name: 'CisMenuApp',
|
||||
components: {
|
||||
FhcSearchbar,
|
||||
CisMenu
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
searchbaroptions: {
|
||||
origin: "cis",
|
||||
cssclass: "",
|
||||
calcheightonly: true,
|
||||
types: {
|
||||
employee: Vue.computed(() => this.$p.t("search/type_employee")),
|
||||
student: Vue.computed(() => this.$p.t("search/type_student")),
|
||||
room: Vue.computed(() => this.$p.t("search/type_room")),
|
||||
organisationunit: Vue.computed(() => this.$p.t("search/type_organisationunit")),
|
||||
cms: Vue.computed(() => this.$p.t("search/type_cms")),
|
||||
dms: Vue.computed(() => this.$p.t("search/type_dms"))
|
||||
},
|
||||
actions: {
|
||||
employee: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
action: function(data) {
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router+
|
||||
"/Cis/Profil/View/"+data.uid;
|
||||
}
|
||||
},
|
||||
childactions: []
|
||||
},
|
||||
student: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
action: function (data) {
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
"/Cis/Profil/View/" + data.uid;
|
||||
|
||||
}
|
||||
},
|
||||
childactions: []
|
||||
},
|
||||
room: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
renderif: function(data) {
|
||||
if(data.content_id === null){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
action: function(data) {
|
||||
const link= FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/CisVue/Cms/content/' + data.content_id;
|
||||
return link;
|
||||
}
|
||||
},
|
||||
childactions: [
|
||||
{
|
||||
label: "LV-Plan",
|
||||
icon: "fas fa-bookmark",
|
||||
type: "link",
|
||||
action: function(data) {
|
||||
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/CisVue/Cms/getRoomInformation/' + data.ort_kurzbz;
|
||||
return link;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Rauminformation",
|
||||
icon: "fas fa-info-circle",
|
||||
type: "link",
|
||||
renderif: function(data) {
|
||||
if(data.content_id === null){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
action: function(data) {
|
||||
const link= FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/CisVue/Cms/content/' + data.content_id;
|
||||
return link;
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
organisationunit: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
renderif: function(data) {
|
||||
if(data.mailgroup) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
action: function(data) {
|
||||
const link = 'mailto:' + data.mailgroup;
|
||||
return link;
|
||||
}
|
||||
},
|
||||
childactions: []
|
||||
},
|
||||
cms: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
action: function (data) {
|
||||
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
'/CisVue/Cms/content/' + data.content_id;
|
||||
return link;
|
||||
}
|
||||
},
|
||||
childactions: []
|
||||
},
|
||||
dms: {
|
||||
defaultaction: {
|
||||
type: "link",
|
||||
action: function (data) {
|
||||
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
'cms/dms.php?id=' + data.dms_id;
|
||||
return link;
|
||||
}
|
||||
},
|
||||
childactions: []
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isMobile() {
|
||||
const smallScreen = window.matchMedia("(max-width: 767px)").matches;
|
||||
const touchCapable = ("ontouchstart" in window) || navigator.maxTouchPoints > 0;
|
||||
return smallScreen;// && touchCapable;
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
isMobile: this.isMobile
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
searchfunction: function(searchsettings) {
|
||||
return this.$api.call(ApiSearchbar.searchCis(searchsettings));
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
const openOtherLvPlanAction = {
|
||||
label: Vue.computed(() => this.$p.t("lehre/stundenplan")),
|
||||
icon: "fas fa-calendar-days",
|
||||
type: "link",
|
||||
action: function (data) {
|
||||
const uid = JSON.parse(data.data).uid;
|
||||
const link =
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
|
||||
"/Cis/OtherLvPlan/" +
|
||||
uid;
|
||||
return link;
|
||||
},
|
||||
};
|
||||
let checkPermissionOtherLvPlanResult = await this.$api.call(
|
||||
ApiLvPlan.checkPermissionOtherLvPlan(),
|
||||
);
|
||||
if (
|
||||
checkPermissionOtherLvPlanResult.meta.status === "success" &&
|
||||
checkPermissionOtherLvPlanResult.data
|
||||
) {
|
||||
this.searchbaroptions.actions.employee.childactions.push(
|
||||
openOtherLvPlanAction,
|
||||
);
|
||||
this.searchbaroptions.actions.student.childactions.push(
|
||||
openOtherLvPlanAction,
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
FhcApps.makeExtendable(app);
|
||||
|
||||
app.use(primevue.config.default, {
|
||||
zIndex: {
|
||||
overlay: 9000,
|
||||
tooltip: 8000
|
||||
}
|
||||
})
|
||||
app.use(PluginsPhrasen);
|
||||
app.use(Theme);
|
||||
app.mount('#cis-header');
|
||||
@@ -3,13 +3,10 @@ import DashboardAdmin from '../../components/Dashboard/Admin.js';
|
||||
|
||||
import PluginsPhrasen from '../../plugins/Phrasen.js';
|
||||
|
||||
import ApiRenderers from '../../api/factory/renderers.js';
|
||||
|
||||
const app = Vue.createApp({
|
||||
name: 'DashboardAdminApp',
|
||||
data: () => ({
|
||||
appSideMenuEntries: {},
|
||||
renderers: null
|
||||
appSideMenuEntries: {}
|
||||
}),
|
||||
components: {
|
||||
CoreNavigationCmpt,
|
||||
@@ -17,49 +14,8 @@ const app = Vue.createApp({
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
// TODO(chris): move those two into the components that need it
|
||||
renderers: Vue.computed(() => this.renderers),
|
||||
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.$api
|
||||
.call(ApiRenderers.loadRenderers())
|
||||
.then(res => {
|
||||
for (let rendertype of Object.keys(res.data)) {
|
||||
let modalTitle = null;
|
||||
let modalContent = null;
|
||||
let calendarEvent = null;
|
||||
if (res.data[rendertype].modalTitle)
|
||||
modalTitle = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].modalTitle)));
|
||||
if (res.data[rendertype].modalContent)
|
||||
modalContent = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].modalContent)));
|
||||
if (res.data[rendertype].calendarEvent)
|
||||
calendarEvent = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].calendarEvent)));
|
||||
|
||||
if (res.data[rendertype].calendarEventStyles) {
|
||||
var head = document.head;
|
||||
if (!head.querySelector(`link[href="${res.data[rendertype].calendarEventStyles}"]`)) {
|
||||
var link = document.createElement("link");
|
||||
link.type = "text/css";
|
||||
link.rel = "stylesheet";
|
||||
link.href = res.data[rendertype].calendarEventStyles;
|
||||
head.appendChild(link);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.renderers === null) {
|
||||
this.renderers = {};
|
||||
}
|
||||
if (!this.renderers[rendertype]) {
|
||||
this.renderers[rendertype] = {}
|
||||
}
|
||||
this.renderers[rendertype].modalTitle = modalTitle;
|
||||
this.renderers[rendertype].modalContent = modalContent;
|
||||
this.renderers[rendertype].calendarEvent = calendarEvent;
|
||||
}
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemErrors);
|
||||
}
|
||||
});
|
||||
app.use(PluginsPhrasen);
|
||||
|
||||
@@ -1,356 +0,0 @@
|
||||
import FhcDashboard from '../../components/Dashboard/Dashboard.js';
|
||||
import PluginsPhrasen from '../../plugins/Phrasen.js';
|
||||
import Theme from '../../plugins/Theme.js';
|
||||
import contrast from '../../directives/contrast.js';
|
||||
import {setScrollbarWidth} from "../../helpers/CssVarCalcHelpers.js";
|
||||
import LvPlan from "../../components/Cis/LvPlan/Lehrveranstaltung.js";
|
||||
import MyLvPlan from "../../components/Cis/LvPlan/Personal.js";
|
||||
import MylvStudent from "../../components/Cis/Mylv/Student.js";
|
||||
import Profil from "../../components/Cis/Profil/Profil.js";
|
||||
import Raumsuche from "../../components/Cis/Raumsuche/Raumsuche.js";
|
||||
import CmsNews from "../../components/Cis/Cms/News.js";
|
||||
import CmsContent from "../../components/Cis/Cms/Content.js";
|
||||
import Info from "../../components/Cis/Mylv/Semester/Studiengang/Lv/Info.js";
|
||||
import RoomInformation, {DEFAULT_MODE_RAUMINFO} from "../../components/Cis/Mylv/RoomInformation.js";
|
||||
import AbgabetoolStudent from "../../components/Cis/Abgabetool/AbgabetoolStudent.js";
|
||||
import AbgabetoolMitarbeiter from "../../components/Cis/Abgabetool/AbgabetoolMitarbeiter.js";
|
||||
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 ApiRenderers from '../../api/factory/renderers.js';
|
||||
import ApiRouteInfo from '../../api/factory/routeinfo.js';
|
||||
import {capitalize} from "../../helpers/StringHelpers.js";
|
||||
|
||||
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
|
||||
|
||||
const router = VueRouter.createRouter({
|
||||
history: VueRouter.createWebHistory(`/${ciPath}`),
|
||||
routes: [
|
||||
{
|
||||
path: `/Cis/Studium`,
|
||||
name: 'Studium',
|
||||
component: Studium,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Profil/View/:uid`,
|
||||
name: 'ProfilView',
|
||||
component: Profil,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Profil`,
|
||||
name: 'Profil',
|
||||
component: Profil,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Abgabetool/Student/:student_uid_prop?`,
|
||||
name: 'AbgabetoolStudent',
|
||||
component: AbgabetoolStudent,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Abgabetool/Mitarbeiter`,
|
||||
name: 'AbgabetoolMitarbeiter',
|
||||
component: AbgabetoolMitarbeiter,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Abgabetool/Assistenz/:stg_kz_prop?`,
|
||||
name: 'AbgabetoolAssistenz',
|
||||
component: AbgabetoolAssistenz,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Abgabetool/Deadlines/:person_uid_prop?`,
|
||||
name: 'DeadlineOverview',
|
||||
component: DeadlineOverview,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Raumsuche`,
|
||||
name: 'Raumsuche',
|
||||
component: Raumsuche,
|
||||
props: true
|
||||
},
|
||||
// Redirect old links to new format
|
||||
{
|
||||
path: "/CisVue/Cms/getRoomInformation/:ort_kurzbz",
|
||||
name: "RoomInformationOld",
|
||||
component: RoomInformation,
|
||||
redirect: (to) => {
|
||||
return { // redirect to longer Rauminfo url and map params
|
||||
name: "RoomInformation",
|
||||
params: { // in this case always populate other params since they are not optional
|
||||
ort_kurzbz: to.params.ort_kurzbz,
|
||||
mode: DEFAULT_MODE_RAUMINFO,
|
||||
focus_date: new Date().toISOString().split("T")[0]
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
path: `/CisVue/Cms/getRoomInformation/:mode/:focus_date/:ort_kurzbz`,
|
||||
name: 'RoomInformation',
|
||||
component: RoomInformation,
|
||||
props: (route) => { // validate and set mode/focus date if for some reason missing
|
||||
const validModes = ["Month", "Week", "Day"];
|
||||
|
||||
// check mode string
|
||||
const mode = route.params.mode &&
|
||||
validModes.includes(route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase())
|
||||
? route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase()
|
||||
: DEFAULT_MODE_RAUMINFO;
|
||||
|
||||
// default to today date if not provided
|
||||
const d = new Date(route.params.focus_date)
|
||||
const focus_date = !isNaN(d) ? route.params.focus_date : new Date().toISOString().split("T")[0];
|
||||
|
||||
// for consistency reasons format the props into one object but actually use a new name to we dont collide with
|
||||
// existing viewData declaration written from codeigniter 3 into routerview tag
|
||||
return {
|
||||
propsViewData: {
|
||||
mode,
|
||||
focus_date,
|
||||
ort_kurzbz: route.params.ort_kurzbz
|
||||
}
|
||||
};
|
||||
},
|
||||
beforeEnter: (to, from, next) => {
|
||||
// missing mode or focus_date -> set defaults
|
||||
if (!to.params.mode || !to.params.focus_date) {
|
||||
next({
|
||||
name: "RoomInformation",
|
||||
params: {
|
||||
mode: to.params.mode || DEFAULT_MODE_RAUMINFO,
|
||||
focus_date: to.params.focus_date || new Date().toISOString().split("T")[0],
|
||||
ort_kurzbz: route.params.ort_kurzbz
|
||||
}
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/CisVue/Cms/Content/:content_id`,
|
||||
name: 'Content',
|
||||
component: CmsContent,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/CisVue/Cms/News`,
|
||||
name: 'News',
|
||||
component: CmsNews,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/MyLv/:studiensemester?`,
|
||||
name: 'MyLv',
|
||||
component: MylvStudent,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: `/Cis/MyLv/Info/:studien_semester/:lehrveranstaltung_id`,
|
||||
name: 'LvInfo',
|
||||
component: Info,
|
||||
props: true
|
||||
},
|
||||
// Redirect old links to new format
|
||||
{
|
||||
// only trigger on first param being numeric to avoid paths like "LvPlan/Month" entering here
|
||||
path: "/Cis/LvPlan/:lv_id(\\d+)",
|
||||
name: "LvPlanOld",
|
||||
component: LvPlan,
|
||||
redirect(to) {
|
||||
const route = Vue.unref(router.currentRoute);
|
||||
const { mode, focus_date } = route.params; // keep mode and focus_date if available
|
||||
return { // redirect to longer LvPlan url and map params
|
||||
name: "LvPlan",
|
||||
params: {
|
||||
mode,
|
||||
focus_date,
|
||||
lv_id: to.params.lv_id
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
path: `/Cis/LvPlan/:mode?/:focus_date?/:lv_id?`,
|
||||
name: 'LvPlan',
|
||||
component: LvPlan,
|
||||
props(route) {
|
||||
return {
|
||||
propsViewData: route.params
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/Cis/MyLvPlan/:mode?/:focus_date?`,
|
||||
name: 'MyLvPlan',
|
||||
component: MyLvPlan,
|
||||
props(route) {
|
||||
return {
|
||||
propsViewData: route.params
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/Cis4`,
|
||||
name: 'Cis4',
|
||||
component: FhcDashboard,
|
||||
props: {dashboard: 'CIS'},
|
||||
},
|
||||
{
|
||||
path: `/`,
|
||||
name: 'FhcDashboard',
|
||||
component: FhcDashboard,
|
||||
props: {dashboard: 'CIS'},
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'Fallback',
|
||||
component: FhcDashboard,
|
||||
props: {dashboard: 'CIS'},
|
||||
redirect: () => {
|
||||
return {
|
||||
name: "Cis4",
|
||||
params: {
|
||||
dashboard: 'CIS'
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const app = Vue.createApp({
|
||||
name: 'FhcApp',
|
||||
data: () => ({
|
||||
appSideMenuEntries: {},
|
||||
renderers: null,
|
||||
}),
|
||||
components: {},
|
||||
computed: {
|
||||
isMobile() {
|
||||
const smallScreen = window.matchMedia("(max-width: 767px)").matches;
|
||||
const touchCapable = ("ontouchstart" in window) || navigator.maxTouchPoints > 0;
|
||||
return smallScreen;// && touchCapable;
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return { // provide injectable & watchable language property
|
||||
language: Vue.computed(() => this.$p.user_language),
|
||||
renderers: Vue.computed(() => this.renderers),
|
||||
isMobile: this.isMobile
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isInternalRoute(href) {
|
||||
const internalBase = window.location.origin
|
||||
return href.startsWith(internalBase);
|
||||
},
|
||||
handleClick(event) {
|
||||
const target = event.target.closest('a');
|
||||
|
||||
if(target?.id == 'skiplink') return
|
||||
if (target && this.isInternalRoute(target.href)) {
|
||||
const url = new URL(target.href)
|
||||
|
||||
const path = url.pathname
|
||||
const base = this.$router.options.history.base
|
||||
const route = path.replace(base, '') || '/'
|
||||
|
||||
// let click event propagate normally if we dont route internally
|
||||
const res = this.$router.resolve(route)
|
||||
if(!res?.matched?.length || res.name === 'Fallback') return
|
||||
|
||||
event.preventDefault(); // Prevent browser navigation
|
||||
|
||||
if(this.isMobile) { // toggle the menu
|
||||
const navMain = document.getElementById('nav-main');
|
||||
// fix unwanted toggle from off to on for some links on mobile
|
||||
if(navMain.classList.contains('show')){
|
||||
document.getElementById('nav-main-btn').click();
|
||||
}
|
||||
}
|
||||
|
||||
this.$router.push(route);
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
async created(){
|
||||
|
||||
await this.$api
|
||||
.call(ApiRenderers.loadRenderers())
|
||||
.then(res => res.data)
|
||||
.then(data => {
|
||||
for (let rendertype of Object.keys(data)) {
|
||||
let modalTitle = null;
|
||||
let modalContent = null;
|
||||
let calendarEvent = null;
|
||||
if (data[rendertype].modalTitle)
|
||||
modalTitle = Vue.markRaw(Vue.defineAsyncComponent(() => import(data[rendertype].modalTitle)));
|
||||
if (data[rendertype].modalContent)
|
||||
modalContent = Vue.markRaw(Vue.defineAsyncComponent(() => import(data[rendertype].modalContent)));
|
||||
if (data[rendertype].calendarEvent)
|
||||
calendarEvent = Vue.markRaw(Vue.defineAsyncComponent(() => import(data[rendertype].calendarEvent)));
|
||||
|
||||
if (data[rendertype].calendarEventStyles){
|
||||
var head = document.head;
|
||||
if(!head.querySelector(`link[href="${data[rendertype].calendarEventStyles}"]`)){
|
||||
var link = document.createElement("link");
|
||||
link.type = "text/css";
|
||||
link.rel = "stylesheet";
|
||||
link.href = data[rendertype].calendarEventStyles;
|
||||
head.appendChild(link);
|
||||
}
|
||||
}
|
||||
|
||||
if(this.renderers === null) {
|
||||
this.renderers = {};
|
||||
}
|
||||
if (!this.renderers[rendertype]) {
|
||||
this.renderers[rendertype] = {}
|
||||
}
|
||||
this.renderers[rendertype].modalTitle = modalTitle;
|
||||
this.renderers[rendertype].modalContent = modalContent;
|
||||
this.renderers[rendertype].calendarEvent = calendarEvent;
|
||||
}
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('click', this.handleClick);
|
||||
|
||||
},
|
||||
beforeUnmount() {
|
||||
document.removeEventListener('click', this.handleClick);
|
||||
},
|
||||
});
|
||||
|
||||
// kind of a bandaid for bad css on some pages to avoid horizontal scroll
|
||||
setScrollbarWidth();
|
||||
app.config.globalProperties.$capitalize = capitalize;
|
||||
|
||||
FhcApps.router.makeExtendable(router);
|
||||
FhcApps.makeExtendable(app);
|
||||
|
||||
app.use(router);
|
||||
app.use(primevue.config.default, {
|
||||
zIndex: {
|
||||
overlay: 9000,
|
||||
tooltip: 8000
|
||||
}
|
||||
})
|
||||
app.directive('tooltip', primevue.tooltip);
|
||||
app.use(PluginsPhrasen);
|
||||
app.use(Theme);
|
||||
app.directive('contrast', contrast);
|
||||
app.mount('#fhccontent');
|
||||
|
||||
router.afterEach((to, from, failure) => {
|
||||
app.config.globalProperties.$api.call(ApiRouteInfo.info('cis4', to.fullPath));
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
import {CoreNavigationCmpt} from '../components/navigation/Navigation.js';
|
||||
import DashboardAdmin from '../components/Dashboard/Admin.js';
|
||||
import Phrases from "../plugin/Phrasen.js"
|
||||
|
||||
Vue.createApp({
|
||||
name: 'DashboardAdminApp',
|
||||
data: () => ({
|
||||
appSideMenuEntries: {}
|
||||
}),
|
||||
components: {
|
||||
CoreNavigationCmpt,
|
||||
DashboardAdmin
|
||||
},
|
||||
mounted() {
|
||||
}
|
||||
}).use(Phrases).mount('#main');
|
||||
@@ -20,7 +20,8 @@ export default {
|
||||
},
|
||||
inject: {
|
||||
mode: "mode",
|
||||
dropableEvents: "dropableEvents"
|
||||
dropableEvents: "dropableEvents",
|
||||
timezone: "timezone"
|
||||
},
|
||||
props: {
|
||||
events: Array,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import LineEvent from './Line/Event.js';
|
||||
import LineBackground from './Line/Background.js';
|
||||
import LineEvent from "./Line/Event.js";
|
||||
import LineBackground from "./Line/Background.js";
|
||||
|
||||
/**
|
||||
* TODO(chris):
|
||||
@@ -10,54 +10,117 @@ export default {
|
||||
name: "GridLine",
|
||||
components: {
|
||||
LineEvent,
|
||||
LineBackground
|
||||
},
|
||||
inject: {
|
||||
axisRow: "axisRow"
|
||||
LineBackground,
|
||||
},
|
||||
inject: ["axisRow", "shouldCompactEvents", "compactibleEventTypes"],
|
||||
props: {
|
||||
date: {
|
||||
type: luxon.DateTime,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
start: {
|
||||
type: luxon.DateTime,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
end: {
|
||||
type: luxon.DateTime,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
events: {
|
||||
type: Array,
|
||||
default: []
|
||||
default: [],
|
||||
},
|
||||
backgrounds: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
eventsWithRowInfo() {
|
||||
const events = [];
|
||||
this.events.forEach(event => {
|
||||
const rows = [1, -1];
|
||||
formattedEvents() {
|
||||
let formattedEvents = this.events.map((event) => {
|
||||
event.rows = [1, -1];
|
||||
if (event.startsHere) {
|
||||
rows[0] = 't_' + event.start.diff(this.date).toMillis();
|
||||
event.rows[0] =
|
||||
"t_" + event.start.diff(this.date).toMillis();
|
||||
}
|
||||
if (event.endsHere) {
|
||||
rows[1] = 't_' + event.end.diff(this.date).toMillis();
|
||||
event.rows[1] = "t_" + event.end.diff(this.date).toMillis();
|
||||
}
|
||||
|
||||
events.push({
|
||||
...event,
|
||||
rows
|
||||
});
|
||||
return event;
|
||||
});
|
||||
return events;
|
||||
}
|
||||
|
||||
if (this.shouldCompactEvents && this.compactibleEventTypes?.length) {
|
||||
formattedEvents =
|
||||
this.compactEvents(formattedEvents, this.compactibleEventTypes);
|
||||
}
|
||||
|
||||
return formattedEvents;
|
||||
},
|
||||
},
|
||||
template: /* html */`
|
||||
methods: {
|
||||
compactEvents(events, compactibleEventTypes) {
|
||||
let formattedEvents = events
|
||||
.filter(
|
||||
(event) =>
|
||||
!compactibleEventTypes.includes(event.type),
|
||||
)
|
||||
.map((event) => {
|
||||
event.display = "default";
|
||||
return event;
|
||||
});
|
||||
let eventsToBeCompacted = events.filter((event) =>
|
||||
compactibleEventTypes.includes(event.type),
|
||||
);
|
||||
let compactedEvents = [];
|
||||
|
||||
eventsToBeCompacted.forEach((event) => {
|
||||
let existingCompactedEvent = compactedEvents.find(
|
||||
(compactedEvent) =>
|
||||
event.rows[0] === compactedEvent.rows[0] &&
|
||||
event.rows[1] === compactedEvent.rows[1],
|
||||
);
|
||||
|
||||
if (!existingCompactedEvent) {
|
||||
compactedEvents.push({
|
||||
events: [
|
||||
{
|
||||
farbe: event.orig.farbe,
|
||||
},
|
||||
],
|
||||
rows: event.rows,
|
||||
});
|
||||
} else {
|
||||
existingCompactedEvent.events.push({
|
||||
farbe: event.orig.farbe,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
compactedEvents.forEach((compactedEvent) => {
|
||||
if (compactedEvent.events.length < 4) {
|
||||
formattedEvents.push({
|
||||
display: "compacted",
|
||||
...compactedEvent,
|
||||
});
|
||||
} else {
|
||||
formattedEvents.push({
|
||||
display: "compacted",
|
||||
events: compactedEvent.events.slice(0, 3),
|
||||
rows: compactedEvent.rows,
|
||||
});
|
||||
formattedEvents.push({
|
||||
display: "compactedExtra",
|
||||
events: compactedEvent.events.slice(3),
|
||||
rows: compactedEvent.rows,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return formattedEvents;
|
||||
},
|
||||
},
|
||||
template: /* html */ `
|
||||
<div
|
||||
class="fhc-calendar-base-grid-line"
|
||||
style="position:relative;display:grid;grid-auto-flow:dense"
|
||||
@@ -69,17 +132,37 @@ export default {
|
||||
:end="end"
|
||||
:background="bg"
|
||||
></line-background>
|
||||
<line-event
|
||||
v-for="(event, i) in eventsWithRowInfo"
|
||||
:key="i"
|
||||
:style="'grid-' + axisRow + ': ' + event.rows.join('/')"
|
||||
:event="event"
|
||||
>
|
||||
<template v-slot="slot">
|
||||
<slot name="event" v-bind="slot" />
|
||||
</template>
|
||||
</line-event>
|
||||
<template v-for="(event, i) in formattedEvents" :key="i">
|
||||
<line-event
|
||||
v-if="!event.display || event.display === 'default'"
|
||||
:style="'grid-' + axisRow + ': ' + event.rows.join('/')"
|
||||
:event="event"
|
||||
>
|
||||
<template v-slot="slot">
|
||||
<slot name="event" v-bind="slot" />
|
||||
</template>
|
||||
</line-event>
|
||||
<div
|
||||
v-else-if="event.display === 'compacted'"
|
||||
:style="'grid-' + axisRow + ': ' + event.rows.join('/')"
|
||||
class="d-flex flex-row justify-content-center gap-1 align-items-center"
|
||||
>
|
||||
<i
|
||||
v-for="(subEvent, subEventIndex) in event.events"
|
||||
:key="subEventIndex"
|
||||
class="fa-solid fa-circle fa-2xs"
|
||||
:style="subEvent.farbe ? {color: '#' + subEvent.farbe} : {}"
|
||||
></i>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="event.display === 'compactedExtra'"
|
||||
:style="'grid-' + axisRow + ': ' + event.rows.join('/')"
|
||||
class="w-100 d-flex flex-row justify-content-center"
|
||||
>
|
||||
{{"+" + event.events.length}}
|
||||
</div>
|
||||
</template>
|
||||
<slot name="dropzone" />
|
||||
</div>
|
||||
`
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -3,24 +3,20 @@ import FhcCalendar from "./Base.js";
|
||||
import ApiLvPlan from '../../api/factory/lvPlan.js';
|
||||
|
||||
import { useEventLoader } from '../../composables/EventLoader.js';
|
||||
import { useRenderers } from '../../composables/Renderers.js';
|
||||
|
||||
import ModeDay from './Mode/Day.js';
|
||||
import ModeWeek from './Mode/Week.js';
|
||||
import ModeMonth from './Mode/Month.js';
|
||||
import ModeList from './Mode/List.js';
|
||||
|
||||
export default {
|
||||
name: "CalendarLvPlan",
|
||||
components: {
|
||||
FhcCalendar
|
||||
},
|
||||
inject: [
|
||||
"renderers"
|
||||
],
|
||||
inject: ["isMobile"],
|
||||
props: {
|
||||
timezone: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
date: {
|
||||
type: [Date, String, Number, luxon.DateTime],
|
||||
default: luxon.DateTime.local()
|
||||
@@ -34,6 +30,16 @@ export default {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
shouldCompactEvents: Vue.computed(
|
||||
() => this.$props.mode === "Month" && this.isMobile,
|
||||
),
|
||||
compactibleEventTypes: Vue.computed(
|
||||
() => this.compactibleEventTypes,
|
||||
),
|
||||
};
|
||||
},
|
||||
emits: [
|
||||
"update:date",
|
||||
"update:mode",
|
||||
@@ -41,11 +47,7 @@ export default {
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
modes: {
|
||||
day: Vue.markRaw(ModeDay),
|
||||
week: Vue.markRaw(ModeWeek),
|
||||
month: Vue.markRaw(ModeMonth)
|
||||
},
|
||||
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
||||
modeOptions: {
|
||||
day: {
|
||||
emptyMessage: Vue.computed(() => this.$p.t('lehre/noLvFound')),
|
||||
@@ -53,9 +55,13 @@ export default {
|
||||
},
|
||||
week: {
|
||||
collapseEmptyDays: false
|
||||
}
|
||||
},
|
||||
list: {
|
||||
length: 7,
|
||||
},
|
||||
},
|
||||
teachingunits: null
|
||||
teachingunits: null,
|
||||
compactibleEventTypes: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -77,7 +83,20 @@ export default {
|
||||
label: now.startOf('minute').toISOTime({ suppressSeconds: true, includeOffset: false })
|
||||
}
|
||||
];
|
||||
}
|
||||
},
|
||||
modes() {
|
||||
let modes = {
|
||||
day: Vue.markRaw(ModeDay),
|
||||
month: Vue.markRaw(ModeMonth),
|
||||
};
|
||||
if (this.isMobile) {
|
||||
modes.list = Vue.markRaw(ModeList);
|
||||
} else {
|
||||
modes.week = Vue.markRaw(ModeWeek);
|
||||
}
|
||||
|
||||
return modes;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
eventStyle(event) {
|
||||
@@ -88,33 +107,47 @@ export default {
|
||||
updateRange(rangeInterval) {
|
||||
this.rangeInterval = rangeInterval;
|
||||
this.$emit('update:range', rangeInterval);
|
||||
}
|
||||
},
|
||||
resetEventLoader() {
|
||||
this.reset();
|
||||
},
|
||||
async getStunden() {
|
||||
let stundenResponse = await this.$api.call(ApiLvPlan.getStunden());
|
||||
this.teachingunits = stundenResponse.data.map((el) => ({
|
||||
id: el.stunde,
|
||||
start: el.beginn,
|
||||
end: el.ende,
|
||||
}));
|
||||
},
|
||||
async getCompactibleEventTypes() {
|
||||
let compactibleEventTypesResponse = await this.$api.call(
|
||||
ApiLvPlan.getCompactibleEventTypes(),
|
||||
);
|
||||
this.compactibleEventTypes = compactibleEventTypesResponse.data;
|
||||
},
|
||||
},
|
||||
setup(props, context) {
|
||||
const rangeInterval = Vue.ref(null);
|
||||
|
||||
const { events, lv } = useEventLoader(rangeInterval, props.getPromiseFunc);
|
||||
const { events, lv, reset } = useEventLoader(rangeInterval, props.getPromiseFunc);
|
||||
|
||||
Vue.watch(lv, newValue => {
|
||||
context.emit('update:lv', newValue);
|
||||
});
|
||||
|
||||
const { renderers } = useRenderers();
|
||||
|
||||
return {
|
||||
rangeInterval,
|
||||
events,
|
||||
lv
|
||||
lv,
|
||||
reset,
|
||||
renderers
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.$api
|
||||
.call(ApiLvPlan.getStunden())
|
||||
.then(res => {
|
||||
return this.teachingunits = res.data.map(el => ({
|
||||
id: el.stunde,
|
||||
start: el.beginn,
|
||||
end: el.ende
|
||||
}));
|
||||
});
|
||||
async created() {
|
||||
await this.getStunden();
|
||||
await this.getCompactibleEventTypes();
|
||||
},
|
||||
template: /* html */`
|
||||
<fhc-calendar
|
||||
@@ -136,6 +169,13 @@ export default {
|
||||
>
|
||||
<template v-slot="{ event, mode }">
|
||||
<div
|
||||
v-if="!event"
|
||||
class="h-100 d-flex justify-content-center align-items-center"
|
||||
>
|
||||
{{ $p.t('lehre/noLvFound') }}
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
:class="'event-type-' + event.type + ' ' + mode + 'PageContainer'"
|
||||
:type="mode == 'day' ? 'button' : undefined"
|
||||
:style="eventStyle(event)"
|
||||
|
||||
@@ -93,7 +93,7 @@ export default {
|
||||
mounted() {
|
||||
this.$emit('update:range', this.range);
|
||||
},
|
||||
template: `
|
||||
template: /*html*/ `
|
||||
<div
|
||||
class="fhc-calendar-mode-list flex-grow-1 position-relative"
|
||||
@cal-click-default.capture="handleClickDefaults"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import FhcCalendar from "./Base.js";
|
||||
|
||||
import { useEventLoader } from '../../composables/EventLoader.js';
|
||||
import { useRenderers } from '../../composables/Renderers.js';
|
||||
|
||||
import ModeList from '../Calendar/Mode/List.js';
|
||||
|
||||
@@ -9,22 +10,17 @@ export default {
|
||||
components: {
|
||||
FhcCalendar
|
||||
},
|
||||
inject: [
|
||||
"renderers"
|
||||
],
|
||||
props: {
|
||||
timezone: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
getPromiseFunc: {
|
||||
type: Function,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const timezone = FHC_JS_DATA_STORAGE_OBJECT.timezone;
|
||||
return {
|
||||
now: luxon.DateTime.now().setZone(this.timezone),
|
||||
timezone,
|
||||
now: luxon.DateTime.now().setZone(timezone),
|
||||
modes: {
|
||||
list: Vue.markRaw(ModeList)
|
||||
},
|
||||
@@ -59,10 +55,12 @@ export default {
|
||||
const rangeInterval = Vue.ref(null);
|
||||
|
||||
const { events } = useEventLoader(rangeInterval, props.getPromiseFunc);
|
||||
const { renderers } = useRenderers();
|
||||
|
||||
return {
|
||||
rangeInterval,
|
||||
events
|
||||
events,
|
||||
renderers
|
||||
};
|
||||
},
|
||||
template: /* html */`
|
||||
|
||||
@@ -42,14 +42,6 @@ export const AbgabetoolAssistenz = {
|
||||
stg_kz_prop: {
|
||||
default: null
|
||||
},
|
||||
viewData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({name: '', uid: ''}),
|
||||
validator(value) {
|
||||
return value && value.uid // && value.name -> extensive viewData use only for cis4 onwards
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -31,16 +31,6 @@ export const AbgabetoolMitarbeiter = {
|
||||
old_abgabe_beurteilung_link: Vue.computed(() => this.old_abgabe_beurteilung_link)
|
||||
}
|
||||
},
|
||||
props: {
|
||||
viewData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({name: '', uid: ''}),
|
||||
validator(value) {
|
||||
return value && value.uid // && value.name -> extensive viewData use only for cis4 onwards
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableData: null,
|
||||
|
||||
@@ -3,6 +3,7 @@ import ApiAbgabe from '../../../api/factory/abgabe.js'
|
||||
import BsModal from "../../Bootstrap/Modal.js";
|
||||
import FhcOverlay from "../../Overlay/FhcOverlay.js";
|
||||
import { getDateStyleClass} from "./getDateStyleClass.js";
|
||||
import ApiAuthinfo from "../../../api/factory/authinfo.js";
|
||||
|
||||
export const AbgabetoolStudent = {
|
||||
name: "AbgabetoolStudent",
|
||||
@@ -24,14 +25,6 @@ export const AbgabetoolStudent = {
|
||||
student_uid_prop: {
|
||||
default: null
|
||||
},
|
||||
viewData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({uid: ''}),
|
||||
validator(value) {
|
||||
return value && value.uid
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -44,9 +37,18 @@ export const AbgabetoolStudent = {
|
||||
detail: null,
|
||||
projektarbeiten: null,
|
||||
selectedProjektarbeit: null,
|
||||
moodle_link: null
|
||||
moodle_link: null,
|
||||
uid: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isViewMode() {
|
||||
return this.student_uid !== this.uid
|
||||
},
|
||||
student_uid() {
|
||||
return this.student_uid_prop || this.uid || null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
checkQualityGatesStrict(termine) {
|
||||
let qgate1Passed = false
|
||||
@@ -258,18 +260,11 @@ export const AbgabetoolStudent = {
|
||||
},
|
||||
handleDownloadBeurteilung2(projektarbeit) {
|
||||
window.open(projektarbeit.beurteilung2)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
computed: {
|
||||
isViewMode() {
|
||||
return this.student_uid !== this.viewData.uid
|
||||
},
|
||||
student_uid() {
|
||||
return this.student_uid_prop || this.viewData?.uid || null
|
||||
}
|
||||
async fetchAuthUID() {
|
||||
const authIdResponse = await this.$api.call(ApiAuthinfo.getAuthUID());
|
||||
this.uid = authIdResponse.data.uid;
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
this.phrasenPromise = this.$p.loadCategory(['abgabetool', 'global'])
|
||||
@@ -302,6 +297,8 @@ export const AbgabetoolStudent = {
|
||||
}).catch(e => {
|
||||
this.loading = false
|
||||
})
|
||||
|
||||
await this.fetchAuthUID();
|
||||
},
|
||||
mounted() {
|
||||
this.setupMounted()
|
||||
|
||||
@@ -10,14 +10,6 @@ export const DeadlineOverview = {
|
||||
person_uid_prop: {
|
||||
default: null
|
||||
},
|
||||
viewData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({name: '', uid: ''}),
|
||||
validator(value) {
|
||||
return value && value.name && value.uid
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -2,115 +2,137 @@ import Pagination from "../../Pagination/Pagination.js";
|
||||
import StudiengangInformation from "./StudiengangInformation/StudiengangInformation.js";
|
||||
import BsConfirm from "../../Bootstrap/Confirm.js";
|
||||
|
||||
import ApiCms from '../../../api/factory/cms.js';
|
||||
import ApiCms from "../../../api/factory/cms.js";
|
||||
|
||||
export default {
|
||||
name: "NewsComponent",
|
||||
components: {
|
||||
Pagination,
|
||||
StudiengangInformation,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
content: null,
|
||||
maxPageCount: 0,
|
||||
page_size: 10,
|
||||
page:1,
|
||||
};
|
||||
},
|
||||
watch:{
|
||||
'$p.user_language.value':function(sprache){
|
||||
this.fetchNews();
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
sprache: function(){
|
||||
return this.$p.user_language.value;
|
||||
components: {
|
||||
Pagination,
|
||||
StudiengangInformation,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
fetchNews() {
|
||||
return this.$api
|
||||
.call(ApiCms.getNews(this.page, this.page_size, this.sprache))
|
||||
.then(res => res.data)
|
||||
.then(result => {
|
||||
this.content = result;
|
||||
inject: ["isMobile"],
|
||||
data() {
|
||||
return {
|
||||
content: null,
|
||||
maxPageCount: 0,
|
||||
page_size: 10,
|
||||
page: 1,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
"$p.user_language.value": function (sprache) {
|
||||
this.fetchNews();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
sprache: function () {
|
||||
return this.$p.user_language.value;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async fetchNews() {
|
||||
let newsResponse = await this.$api.call(
|
||||
ApiCms.getNews(this.page, this.page_size, this.sprache),
|
||||
);
|
||||
this.content = newsResponse.data;
|
||||
|
||||
document.querySelectorAll("#cms [data-confirm]").forEach((el) => {
|
||||
el.addEventListener("click", (evt) => {
|
||||
evt.preventDefault();
|
||||
BsConfirm.popup(el.dataset.confirm)
|
||||
.then(() => {
|
||||
Axios.get(el.href)
|
||||
.then((res) => {
|
||||
// TODO(chris): check for success then show message and/or reload
|
||||
location = location;
|
||||
})
|
||||
.catch((err) => console.error("ERROR:", err));
|
||||
document.querySelectorAll("#cms [data-confirm]").forEach((el) => {
|
||||
el.addEventListener("click", (evt) => {
|
||||
evt.preventDefault();
|
||||
BsConfirm.popup(el.dataset.confirm)
|
||||
.then(() => {
|
||||
Axios.get(el.href)
|
||||
.then((res) => {
|
||||
// TODO(chris): check for success then show message and/or reload
|
||||
location = location;
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
});
|
||||
});
|
||||
document.querySelectorAll("#cms [data-href]").forEach((el) => {
|
||||
el.href = el.dataset.href.replace(
|
||||
/^ROOT\//,
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root
|
||||
);
|
||||
});
|
||||
Vue.nextTick(()=>{
|
||||
document.querySelectorAll(".card-header").forEach((el) => {
|
||||
el.classList.add("fhc-primary");
|
||||
});
|
||||
document.querySelectorAll(".row").forEach((el) => {
|
||||
el.classList.add("w-100");
|
||||
el.classList.add("align-items-center");
|
||||
|
||||
});
|
||||
document.querySelectorAll(".row h2").forEach((el) => {
|
||||
el.classList.add("mb-0");
|
||||
});
|
||||
.catch((err) => console.error("ERROR:", err));
|
||||
})
|
||||
.catch(() => {});
|
||||
});
|
||||
});
|
||||
document.querySelectorAll("#cms [data-href]").forEach((el) => {
|
||||
el.href = el.dataset.href.replace(
|
||||
/^ROOT\//,
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root,
|
||||
);
|
||||
});
|
||||
|
||||
})
|
||||
await this.$nextTick();
|
||||
this.formatExternalHtml();
|
||||
},
|
||||
async loadNewPageContent(data) {
|
||||
let newsResponse = await this.$api.call(
|
||||
ApiCms.getNews(data.page, data.rows),
|
||||
);
|
||||
this.content = newsResponse.data;
|
||||
|
||||
await this.$nextTick();
|
||||
|
||||
this.formatExternalHtml();
|
||||
},
|
||||
formatExternalHtml() {
|
||||
document
|
||||
.querySelectorAll(".news-list-item .card-header")
|
||||
.forEach((el) => {
|
||||
el.classList.add("fhc-primary");
|
||||
});
|
||||
document.querySelectorAll(".news-list-item .row").forEach((el) => {
|
||||
el.classList.add("w-100");
|
||||
el.classList.add("align-items-center");
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".news-list-item .row h2")
|
||||
.forEach((el) => {
|
||||
el.classList.add("mb-0");
|
||||
});
|
||||
},
|
||||
loadNewPageContent(data) {
|
||||
this.$api
|
||||
.call(ApiCms.getNews(data.page, data.rows))
|
||||
.then(res => res.data)
|
||||
.then(result => {
|
||||
this.content = result;
|
||||
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchNews();
|
||||
afterPageUpdated(event) {
|
||||
this.page = event.page;
|
||||
this.page_size = event.rows;
|
||||
this.$refs.newsPageHeading.scrollIntoView({block: 'end'});
|
||||
this.loadNewPageContent(event);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.fetchNews();
|
||||
|
||||
this.$api
|
||||
.call(ApiCms.getNewsRowCount())
|
||||
.then(res => res.data)
|
||||
.then(result => {
|
||||
.then((res) => res.data)
|
||||
.then((result) => {
|
||||
this.maxPageCount = result;
|
||||
});
|
||||
},
|
||||
template: /*html*/ `
|
||||
<h2 class="fhc-primary-color">News</h2>
|
||||
<hr/>
|
||||
<pagination v-show="content?true:false" :page_size="page_size" @page="page=$event.page; loadNewPageContent($event)" :maxPageCount="maxPageCount">
|
||||
</pagination>
|
||||
<div class="container-fluid mt-4">
|
||||
<div class="row">
|
||||
<div class="col" v-html="content">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div style="width:15rem">
|
||||
<studiengang-information></studiengang-information>
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div :class="{'pb-3': isMobile}" class="overflow-x-hidden">
|
||||
<h2 ref="newsPageHeading" class="fhc-primary-color">News</h2>
|
||||
<hr/>
|
||||
<pagination
|
||||
v-show="content?true:false"
|
||||
:page="page"
|
||||
:page_size="page_size"
|
||||
@pageUpdated="afterPageUpdated($event)"
|
||||
:maxPageCount="maxPageCount"
|
||||
></pagination>
|
||||
<div class="container-fluid mt-4">
|
||||
<div class="row">
|
||||
<div class="col" v-html="content">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div style="width:15rem">
|
||||
<studiengang-information></studiengang-information>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<pagination
|
||||
v-show="content?true:false"
|
||||
:page="page"
|
||||
:page_size="page_size"
|
||||
@pageUpdated="afterPageUpdated($event)"
|
||||
:maxPageCount="maxPageCount"
|
||||
></pagination>
|
||||
</div>
|
||||
<pagination v-show="content?true:false" :page_size="page_size" @page="loadNewPageContent" :maxPageCount="maxPageCount">
|
||||
</pagination>
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,8 @@ import FhcCalendar from "../../Calendar/LvPlan.js";
|
||||
import ApiLvPlan from '../../../api/factory/lvPlan.js';
|
||||
import ApiAuthinfo from '../../../api/factory/authinfo.js';
|
||||
|
||||
export const DEFAULT_MODE_LVPLAN = 'Week'
|
||||
export const DEFAULT_MODE_LVPLAN_MOBILE = 'List';
|
||||
export const DEFAULT_MODE_LVPLAN_DESKTOP = 'Week';
|
||||
|
||||
export default {
|
||||
name: 'LvPlanLehrveranstaltung',
|
||||
@@ -19,15 +20,21 @@ export default {
|
||||
lv: null
|
||||
};
|
||||
},
|
||||
inject: ["isMobile"],
|
||||
computed:{
|
||||
currentDay() {
|
||||
if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date)))
|
||||
return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate();
|
||||
return luxon.DateTime.now().setZone(FHC_JS_DATA_STORAGE_OBJECT.timezone).toISODate();
|
||||
return this.propsViewData?.focus_date;
|
||||
},
|
||||
currentMode() {
|
||||
if (!this.propsViewData?.mode || !['day', 'week', 'month'].includes(this.propsViewData?.mode.toLowerCase()))
|
||||
return DEFAULT_MODE_LVPLAN;
|
||||
let validModes = ['day', 'month'];
|
||||
validModes.push(this.isMobile ? 'list' : 'week');
|
||||
|
||||
const defaultMode = this.isMobile ? DEFAULT_MODE_LVPLAN_MOBILE : DEFAULT_MODE_LVPLAN_DESKTOP;
|
||||
|
||||
if (!this.propsViewData?.mode || !validModes.includes(this.propsViewData?.mode.toLowerCase()))
|
||||
return defaultMode;
|
||||
return this.propsViewData?.mode;
|
||||
},
|
||||
currentLv() {
|
||||
@@ -95,7 +102,6 @@ export default {
|
||||
<fhc-calendar
|
||||
v-else-if="lv"
|
||||
ref="calendar"
|
||||
:timezone="viewData.timezone"
|
||||
:get-promise-func="getPromiseFunc"
|
||||
:date="currentDay"
|
||||
:mode="currentMode"
|
||||
|
||||
@@ -3,7 +3,8 @@ import FhcCalendar from "../../Calendar/LvPlan.js";
|
||||
import ApiLvPlan from '../../../api/factory/lvPlan.js';
|
||||
import ApiAuthinfo from '../../../api/factory/authinfo.js';
|
||||
|
||||
export const DEFAULT_MODE_LVPLAN = 'Week'
|
||||
export const DEFAULT_MODE_LVPLAN_MOBILE = 'List';
|
||||
export const DEFAULT_MODE_LVPLAN_DESKTOP = 'Week';
|
||||
|
||||
export default {
|
||||
name: 'LvPlan',
|
||||
@@ -11,31 +12,32 @@ export default {
|
||||
FhcCalendar
|
||||
},
|
||||
props: {
|
||||
viewData: Object, // NOTE(chris): this is inherited from router-view
|
||||
propsViewData: Object
|
||||
},
|
||||
data() {
|
||||
const now = luxon.DateTime.now().setZone(this.viewData.timezone);
|
||||
return {
|
||||
studiensemester_kurzbz: null,
|
||||
studiensemester_start: null,
|
||||
studiensemester_ende: null,
|
||||
uid: null,
|
||||
lv: null
|
||||
lv: null,
|
||||
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
||||
};
|
||||
},
|
||||
inject: ["isMobile"],
|
||||
computed:{
|
||||
currentDay() {
|
||||
return this.propsViewData?.focus_date || luxon.DateTime.now().setZone(this.viewData.timezone).toISODate();
|
||||
return this.propsViewData?.focus_date || luxon.DateTime.now().setZone(this.timezone).toISODate();
|
||||
},
|
||||
currentMode() {
|
||||
return this.propsViewData?.mode || DEFAULT_MODE_LVPLAN;
|
||||
const defaultMode = this.isMobile ? DEFAULT_MODE_LVPLAN_MOBILE : DEFAULT_MODE_LVPLAN_DESKTOP;
|
||||
return this.propsViewData?.mode || defaultMode;
|
||||
},
|
||||
downloadLinks() {
|
||||
if (!this.studiensemester_start || !this.studiensemester_ende || !this.uid)
|
||||
return false;
|
||||
|
||||
const opts = { zone: this.viewData.timezone };
|
||||
const opts = { zone: this.timezone };
|
||||
const start = luxon.DateTime
|
||||
.fromISO(this.studiensemester_start, opts)
|
||||
.toUnixInteger();
|
||||
@@ -115,7 +117,7 @@ export default {
|
||||
<fhc-calendar
|
||||
ref="calendar"
|
||||
v-model:lv="lv"
|
||||
:timezone="viewData.timezone"
|
||||
:timezone="timezone"
|
||||
:get-promise-func="getPromiseFunc"
|
||||
:date="currentDay"
|
||||
:mode="currentMode"
|
||||
|
||||
+32
-17
@@ -3,7 +3,8 @@ import FhcCalendar from "../../Calendar/LvPlan.js";
|
||||
import ApiLvPlan from '../../../api/factory/lvPlan.js';
|
||||
import ApiAuthinfo from '../../../api/factory/authinfo.js';
|
||||
|
||||
export const DEFAULT_MODE_LVPLAN = 'Week'
|
||||
export const DEFAULT_MODE_LVPLAN_DESKTOP = "Week";
|
||||
export const DEFAULT_MODE_LVPLAN_MOBILE = "List";
|
||||
|
||||
export default {
|
||||
name: 'LvPlanPersonal',
|
||||
@@ -11,7 +12,6 @@ export default {
|
||||
FhcCalendar
|
||||
},
|
||||
props: {
|
||||
viewData: Object, // NOTE(chris): this is inherited from router-view
|
||||
propsViewData: Object
|
||||
},
|
||||
data() {
|
||||
@@ -21,18 +21,30 @@ export default {
|
||||
studiensemester_ende: null,
|
||||
uid: null,
|
||||
isMitarbeiter: false,
|
||||
isStudent: false
|
||||
isStudent: false,
|
||||
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
||||
};
|
||||
},
|
||||
inject: ["isMobile"],
|
||||
computed:{
|
||||
currentDay() {
|
||||
if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date)))
|
||||
return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate();
|
||||
return luxon.DateTime.now().setZone(this.timezone).toISODate();
|
||||
return this.propsViewData?.focus_date;
|
||||
},
|
||||
currentMode() {
|
||||
if (!this.propsViewData?.mode || !['day', 'week', 'month'].includes(this.propsViewData?.mode.toLowerCase()))
|
||||
return DEFAULT_MODE_LVPLAN;
|
||||
let validModes = ["day", "month"];
|
||||
validModes.push(this.isMobile ? "list" : "week");
|
||||
|
||||
const defaultMode = this.isMobile
|
||||
? DEFAULT_MODE_LVPLAN_MOBILE
|
||||
: DEFAULT_MODE_LVPLAN_DESKTOP;
|
||||
|
||||
if (
|
||||
!this.propsViewData?.mode ||
|
||||
!validModes.includes(this.propsViewData?.mode.toLowerCase())
|
||||
)
|
||||
return defaultMode;
|
||||
return this.propsViewData?.mode;
|
||||
},
|
||||
downloadLinks() {
|
||||
@@ -47,7 +59,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const opts = { zone: this.viewData.timezone };
|
||||
const opts = { zone: this.timezone };
|
||||
const start = luxon.DateTime
|
||||
.fromISO(this.studiensemester_start, opts)
|
||||
.toUnixInteger();
|
||||
@@ -102,16 +114,18 @@ export default {
|
||||
this.$api.call(ApiLvPlan.eventsPersonal(start.toISODate(), end.toISODate())),
|
||||
this.$api.call(ApiLvPlan.getLvPlanReservierungen(start.toISODate(), end.toISODate()))
|
||||
];
|
||||
}
|
||||
},
|
||||
async fetchAuthInfo() {
|
||||
const authInfoResponse = await this.$api.call(ApiAuthinfo.getAuthInfo());
|
||||
|
||||
const authInfo = authInfoResponse.data;
|
||||
this.uid = authInfo.uid;
|
||||
this.isMitarbeiter = authInfo.isMitarbeiter;
|
||||
this.isStudent = authInfo.isStudent;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$api
|
||||
.call(ApiAuthinfo.getAuthInfo())
|
||||
.then(res => {
|
||||
this.uid = res.data.uid;
|
||||
this.isMitarbeiter = res.data.isMitarbeiter;
|
||||
this.isStudent = res.data.isStudent;
|
||||
});
|
||||
async created() {
|
||||
await this.fetchAuthInfo();
|
||||
},
|
||||
template: /*html*/`
|
||||
<div class="cis-lvplan-personal d-flex flex-column h-100">
|
||||
@@ -123,8 +137,9 @@ export default {
|
||||
</h2>
|
||||
<hr>
|
||||
<fhc-calendar
|
||||
v-if="timezone"
|
||||
ref="calendar"
|
||||
:timezone="viewData.timezone"
|
||||
:timezone="timezone"
|
||||
:get-promise-func="getPromiseFunc"
|
||||
:date="currentDay"
|
||||
:mode="currentMode"
|
||||
@@ -0,0 +1,269 @@
|
||||
import FormForm from "../../Form/Form.js";
|
||||
import FormInput from "../../Form/Input.js";
|
||||
import FhcCalendar from "../../Calendar/LvPlan.js";
|
||||
|
||||
import ApiLvPlan from "../.././../api/factory/lvPlan.js";
|
||||
import ApiOtherLvPlan from "../.././../api/factory/otherLvPlan.js";
|
||||
import ApiAuthinfo from "../../../api/factory/authinfo.js";
|
||||
|
||||
export const DEFAULT_MODE_LVPLAN_DESKTOP = "Week";
|
||||
export const DEFAULT_MODE_LVPLAN_MOBILE = "List";
|
||||
|
||||
export default {
|
||||
name: "OtherLvPlan",
|
||||
components: {
|
||||
FormForm,
|
||||
FormInput,
|
||||
FhcCalendar,
|
||||
},
|
||||
props: {
|
||||
propsViewData: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
localProps: {},
|
||||
studiensemester_kurzbz: null,
|
||||
studiensemester_start: null,
|
||||
studiensemester_ende: null,
|
||||
isOtherPersonMitarbeiter: false,
|
||||
isOtherPersonStudent: false,
|
||||
currentStgBezeichnung: null,
|
||||
listVerband: [],
|
||||
listGroup: [],
|
||||
rangeIntervalFirst: null,
|
||||
otherPersonData: {
|
||||
fullName: "",
|
||||
photo: "",
|
||||
},
|
||||
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
||||
};
|
||||
},
|
||||
inject: ["isMobile"],
|
||||
computed: {
|
||||
currentDay() {
|
||||
if (
|
||||
!this.propsViewData?.focus_date ||
|
||||
isNaN(new Date(this.propsViewData?.focus_date))
|
||||
)
|
||||
return luxon.DateTime.now().setZone(this.timezone).toISODate();
|
||||
return this.propsViewData?.focus_date;
|
||||
},
|
||||
currentMode() {
|
||||
let validModes = ["day", "month"];
|
||||
validModes.push(this.isMobile ? "list" : "week");
|
||||
|
||||
const defaultMode = this.isMobile
|
||||
? DEFAULT_MODE_LVPLAN_MOBILE
|
||||
: DEFAULT_MODE_LVPLAN_DESKTOP;
|
||||
|
||||
if (
|
||||
!this.propsViewData?.mode ||
|
||||
!validModes.includes(this.propsViewData?.mode.toLowerCase())
|
||||
)
|
||||
return defaultMode;
|
||||
return this.propsViewData?.mode;
|
||||
},
|
||||
downloadLinks() {
|
||||
if (
|
||||
!this.studiensemester_start ||
|
||||
!this.studiensemester_ende ||
|
||||
!this.propsViewData.otherUid
|
||||
)
|
||||
return false;
|
||||
|
||||
const type = this.isOtherPersonStudent
|
||||
? "student"
|
||||
: this.isOtherPersonMitarbeiter
|
||||
? "lektor"
|
||||
: null;
|
||||
|
||||
if (!type) return;
|
||||
|
||||
const opts = { zone: this.timezone };
|
||||
const start = luxon.DateTime.fromISO(
|
||||
this.studiensemester_start,
|
||||
opts,
|
||||
).toUnixInteger();
|
||||
const ende = luxon.DateTime.fromISO(
|
||||
this.studiensemester_ende,
|
||||
opts,
|
||||
).toUnixInteger();
|
||||
|
||||
const download_link =
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
"cis/private/lvplan/stpl_kalender.php" +
|
||||
"?type=" +
|
||||
type +
|
||||
"&pers_uid=" +
|
||||
this.propsViewData.otherUid +
|
||||
"&begin=" +
|
||||
start +
|
||||
"&ende=" +
|
||||
ende;
|
||||
|
||||
return [
|
||||
{
|
||||
title: "excel",
|
||||
icon: "fa-solid fa-file-excel",
|
||||
link: download_link + "&format=excel",
|
||||
},
|
||||
{
|
||||
title: "csv",
|
||||
icon: "fa-solid fa-file-csv",
|
||||
link: download_link + "&format=csv",
|
||||
},
|
||||
{
|
||||
title: "ical1",
|
||||
icon: "fa-regular fa-calendar",
|
||||
link: download_link + "&format=ical&version=1&target=ical",
|
||||
},
|
||||
{
|
||||
title: "ical2",
|
||||
icon: "fa-regular fa-calendar",
|
||||
link: download_link + "&format=ical&version=2&target=ical",
|
||||
},
|
||||
];
|
||||
},
|
||||
get_image_base64_src: function () {
|
||||
if (!this.otherPersonData.photo?.length) {
|
||||
return "";
|
||||
}
|
||||
return "data:image/jpeg;base64," + this.otherPersonData.photo;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
"propsViewData.otherUid": {
|
||||
handler() {
|
||||
this.$router.go();
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleChangeDate(day, newMode) {
|
||||
return this.handleChangeMode(newMode, day);
|
||||
},
|
||||
handleChangeMode(newMode, day) {
|
||||
const mode = newMode[0].toUpperCase() + newMode.slice(1);
|
||||
const focus_date = day.toISODate();
|
||||
|
||||
this.$router.push({
|
||||
name: "OtherLvPlan",
|
||||
params: {
|
||||
mode,
|
||||
focus_date,
|
||||
},
|
||||
});
|
||||
},
|
||||
updateRange(rangeInterval) {
|
||||
this.$api
|
||||
.call(
|
||||
ApiLvPlan.studiensemesterDateInterval(
|
||||
rangeInterval.end.startOf("week").toISODate(),
|
||||
),
|
||||
)
|
||||
.then((res) => {
|
||||
this.studiensemester_kurzbz =
|
||||
res.data.studiensemester_kurzbz;
|
||||
this.studiensemester_start = res.data.start;
|
||||
this.studiensemester_ende = res.data.ende;
|
||||
});
|
||||
},
|
||||
getPromiseFunc(start, end) {
|
||||
return [
|
||||
this.$api.call(
|
||||
ApiLvPlan.eventsPersonal(
|
||||
start.toISODate(),
|
||||
end.toISODate(),
|
||||
this.propsViewData.otherUid,
|
||||
),
|
||||
),
|
||||
this.$api.call(
|
||||
ApiLvPlan.getLvPlanReservierungen(
|
||||
start.toISODate(),
|
||||
end.toISODate(),
|
||||
this.propsViewData.otherUid,
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
async fetchViewData() {
|
||||
const viewDataResponse = await this.$api.call(
|
||||
ApiOtherLvPlan.getOtherLvPlanViewData(
|
||||
this.propsViewData.otherUid,
|
||||
),
|
||||
);
|
||||
|
||||
const viewData = viewDataResponse.data;
|
||||
|
||||
this.isOtherPersonMitarbeiter =
|
||||
!!viewData?.user_data?.is_mitarbeiter;
|
||||
this.isOtherPersonStudent = !!viewData?.user_data?.is_student;
|
||||
this.otherPersonData.fullName =
|
||||
viewData?.user_data?.vorname +
|
||||
" " +
|
||||
viewData?.user_data?.nachname;
|
||||
this.otherPersonData.photo = viewData?.user_data?.foto;
|
||||
},
|
||||
async redirectToMyLvPlanIfAuthUid() {
|
||||
const authInfoResponse = await this.$api.call(
|
||||
ApiAuthinfo.getAuthInfo(),
|
||||
);
|
||||
const authId = authInfoResponse.data.uid;
|
||||
if (authId === this.propsViewData.otherUid) {
|
||||
this.$router.push({ name: "MyLvPlan" });
|
||||
}
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
await this.redirectToMyLvPlanIfAuthUid();
|
||||
await this.fetchViewData();
|
||||
},
|
||||
template: `
|
||||
<div class="d-flex flex-column h-100">
|
||||
<h2>
|
||||
<div class="d-flex flex-row justify-content-between align-items-center">
|
||||
<span>
|
||||
{{ $p.t('lehre/stundenplan') + (studiensemester_kurzbz ? " " + studiensemester_kurzbz : "") }}
|
||||
</span>
|
||||
<div @click="this.$router.push({name: 'ProfilView', params: {uid: propsViewData.otherUid}})" type="button" class="d-flex flex-row align-items-center gap-3">
|
||||
<span v-if="otherPersonData.fullName?.length">
|
||||
{{ otherPersonData.fullName }}
|
||||
</span>
|
||||
<img v-if="otherPersonData.photo?.length" alt="profile picture" class=" img-thumbnail " style=" max-height:60px; " :src="get_image_base64_src"/>
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
<hr>
|
||||
<fhc-calendar
|
||||
v-if="timezone"
|
||||
ref="calendar"
|
||||
:timezone="timezone"
|
||||
:get-promise-func="getPromiseFunc"
|
||||
:date="currentDay"
|
||||
:mode="currentMode"
|
||||
@update:date="handleChangeDate"
|
||||
@update:mode="handleChangeMode"
|
||||
@update:range="updateRange"
|
||||
class="responsive-calendar"
|
||||
>
|
||||
<div
|
||||
v-if="downloadLinks"
|
||||
class="d-flex gap-1 justify-items-start"
|
||||
>
|
||||
<div v-for="{ title, icon, link } in downloadLinks">
|
||||
<a
|
||||
:href="link"
|
||||
:aria-label="title"
|
||||
class="py-1 btn btn-outline-secondary"
|
||||
>
|
||||
<div class="d-flex flex-column">
|
||||
<i aria-hidden="true" :class="icon"></i>
|
||||
<span style="font-size:.5rem">{{ title }}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</fhc-calendar>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
@@ -0,0 +1,413 @@
|
||||
import FormForm from "../../Form/Form.js";
|
||||
import FormInput from "../../Form/Input.js";
|
||||
import FhcCalendar from "../../Calendar/LvPlan.js";
|
||||
|
||||
import ApiLvPlan from '../.././../api/factory/lvPlan.js';
|
||||
import ApiStgOrgLvPlan from '../.././../api/factory/stgOrgLvPlan.js';
|
||||
import ApiAuthinfo from '../../../api/factory/authinfo.js';
|
||||
|
||||
export const DEFAULT_MODE_LVPLAN_DESKTOP = "Week";
|
||||
export const DEFAULT_MODE_LVPLAN_MOBILE = "List";
|
||||
|
||||
export default {
|
||||
name: "LvPlanStgOrg",
|
||||
components: {
|
||||
FormForm,
|
||||
FormInput,
|
||||
FhcCalendar,
|
||||
},
|
||||
props: {
|
||||
propsViewData: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
localProps: {},
|
||||
studiensemester_kurzbz: null,
|
||||
studiensemester_start: null,
|
||||
studiensemester_ende: null,
|
||||
uid: null,
|
||||
isMitarbeiter: false,
|
||||
isStudent: false,
|
||||
currentStgBezeichnung: null,
|
||||
formData: {
|
||||
stgkz: null,
|
||||
sem: null,
|
||||
verband: null,
|
||||
gruppe: null,
|
||||
},
|
||||
listStg: [],
|
||||
listSem: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||
listVerband: [],
|
||||
listGroup: [],
|
||||
rangeIntervalFirst: null,
|
||||
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
||||
};
|
||||
},
|
||||
inject: ["isMobile"],
|
||||
computed: {
|
||||
maxSemester() {
|
||||
const currentStg = this.listStg.find(
|
||||
(item) => item.studiengang_kz === this.formData.stgkz,
|
||||
);
|
||||
return currentStg?.max_semester;
|
||||
},
|
||||
currentDay() {
|
||||
if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date)))
|
||||
return luxon.DateTime.now().setZone(this.timezone).toISODate();
|
||||
return this.propsViewData?.focus_date;
|
||||
},
|
||||
currentMode() {
|
||||
let validModes = ["day", "month"];
|
||||
validModes.push(this.isMobile ? "list" : "week");
|
||||
|
||||
const defaultMode = this.isMobile
|
||||
? DEFAULT_MODE_LVPLAN_MOBILE
|
||||
: DEFAULT_MODE_LVPLAN_DESKTOP;
|
||||
|
||||
if (
|
||||
!this.propsViewData?.mode ||
|
||||
!validModes.includes(this.propsViewData?.mode.toLowerCase())
|
||||
)
|
||||
return defaultMode;
|
||||
return this.propsViewData?.mode;
|
||||
},
|
||||
downloadLinks() {
|
||||
if (
|
||||
!this.studiensemester_start ||
|
||||
!this.studiensemester_ende ||
|
||||
!this.uid
|
||||
)
|
||||
return false;
|
||||
|
||||
let type = false;
|
||||
type = this.isStudent ? "student" : type;
|
||||
type = this.isMitarbeiter ? "lektor" : type;
|
||||
if (false === type) {
|
||||
return;
|
||||
}
|
||||
|
||||
const opts = { zone: this.timezone };
|
||||
const start = luxon.DateTime
|
||||
.fromISO(this.studiensemester_start, opts)
|
||||
.toUnixInteger();
|
||||
const ende = luxon.DateTime
|
||||
.fromISO(this.studiensemester_ende, opts)
|
||||
.toUnixInteger();
|
||||
|
||||
const download_link =
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
"cis/private/lvplan/stpl_kalender.php" +
|
||||
"?type=" +
|
||||
type +
|
||||
"&pers_uid=" +
|
||||
this.uid +
|
||||
"&begin=" +
|
||||
start +
|
||||
"&ende=" +
|
||||
ende;
|
||||
|
||||
return [
|
||||
{
|
||||
title: "excel",
|
||||
icon: "fa-solid fa-file-excel",
|
||||
link: download_link + "&format=excel",
|
||||
},
|
||||
{
|
||||
title: "csv",
|
||||
icon: "fa-solid fa-file-csv",
|
||||
link: download_link + "&format=csv",
|
||||
},
|
||||
{
|
||||
title: "ical1",
|
||||
icon: "fa-regular fa-calendar",
|
||||
link: download_link + "&format=ical&version=1&target=ical",
|
||||
},
|
||||
{
|
||||
title: "ical2",
|
||||
icon: "fa-regular fa-calendar",
|
||||
link: download_link + "&format=ical&version=2&target=ical",
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
loadLvPlan() {
|
||||
if (!this.formData.stgkz) {
|
||||
this.$fhcAlert.alertError(this.$p.t("LvPlan", "chooseStg"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.formData.sem &&
|
||||
(this.formData.verband || this.formData.gruppe)
|
||||
) {
|
||||
this.$fhcAlert.alertError(
|
||||
this.$p.t("LvPlan", "error_SemMissing"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.formData.verband && this.formData.gruppe) {
|
||||
this.$fhcAlert.alertError(
|
||||
this.$p.t("LvPlan", "error_VerbandMissing"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const params = {
|
||||
mode: this.currentMode,
|
||||
focus_date: this.currentDay,
|
||||
stgkz: this.formData.stgkz,
|
||||
sem: this.formData.sem,
|
||||
verband: this.formData.verband,
|
||||
gruppe: this.formData.gruppe,
|
||||
};
|
||||
|
||||
//ensure logic: no value after a null value in route
|
||||
if (params.sem == null) {
|
||||
params.verband = null;
|
||||
params.gruppe = null;
|
||||
}
|
||||
if (params.verband == null) {
|
||||
params.gruppe = null;
|
||||
}
|
||||
|
||||
//delete all null values to avoid null in router
|
||||
Object.keys(params).forEach(
|
||||
(key) => params[key] == null && delete params[key],
|
||||
);
|
||||
|
||||
this.$router.push({
|
||||
name: "StgOrgLvPlan",
|
||||
params,
|
||||
});
|
||||
|
||||
this.$refs['calendar']?.resetEventLoader();
|
||||
},
|
||||
loadListSem(){
|
||||
if(!this.listSem)
|
||||
this.listSem = [...Array(this.maxSemester).keys()].map(i => i + 1);
|
||||
},
|
||||
loadListVerband() {
|
||||
this.$api
|
||||
.call(ApiLvPlan.getLehrverband(this.formData.stgkz, this.formData.sem, this.formData.verband))
|
||||
.then(result => {
|
||||
const data = result.data;
|
||||
const mappedData = data.map((item) => item.verband);
|
||||
this.listVerband = [
|
||||
...new Set(
|
||||
mappedData.filter(
|
||||
(v) =>
|
||||
v !== null &&
|
||||
v !== undefined &&
|
||||
String(v).trim() !== "",
|
||||
),
|
||||
),
|
||||
].sort();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
loadListGroup() {
|
||||
this.$api
|
||||
.call(ApiLvPlan.getGruppe(this.formData.stgkz, this.formData.sem, this.formData.verband))
|
||||
.then(result => {
|
||||
const data = result.data;
|
||||
const mappedData = data.map((item) => item.gruppe);
|
||||
this.listGroup = [
|
||||
...new Set(
|
||||
mappedData.filter(
|
||||
(v) =>
|
||||
v !== null &&
|
||||
v !== undefined &&
|
||||
String(v).trim() !== "",
|
||||
),
|
||||
),
|
||||
].sort();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
handleChangeDate(day, newMode) {
|
||||
return this.handleChangeMode(newMode, day);
|
||||
},
|
||||
handleChangeMode(newMode, day) {
|
||||
const mode = newMode[0].toUpperCase() + newMode.slice(1);
|
||||
const focus_date = day.toISODate();
|
||||
|
||||
this.$router.push({
|
||||
name: "StgOrgLvPlan",
|
||||
params: {
|
||||
mode,
|
||||
focus_date,
|
||||
stgkz: this.formData.stgkz,
|
||||
sem: this.formData.sem,
|
||||
verband: this.formData.verband,
|
||||
gruppe: this.formData.gruppe,
|
||||
},
|
||||
});
|
||||
},
|
||||
updateRange(rangeInterval) {
|
||||
this.$api
|
||||
.call(
|
||||
ApiLvPlan.studiensemesterDateInterval(
|
||||
rangeInterval.end.startOf("week").toISODate(),
|
||||
),
|
||||
)
|
||||
.then((res) => {
|
||||
this.studiensemester_kurzbz =
|
||||
res.data.studiensemester_kurzbz;
|
||||
this.studiensemester_start = res.data.start;
|
||||
this.studiensemester_ende = res.data.ende;
|
||||
});
|
||||
},
|
||||
getPromiseFunc(start, end) {
|
||||
return [
|
||||
this.$api.call(
|
||||
ApiLvPlan.eventsStgOrg(
|
||||
start,
|
||||
end,
|
||||
this.formData.stgkz,
|
||||
this.formData.sem,
|
||||
this.formData.verband,
|
||||
this.formData.gruppe,
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
async fetchAuthInfo() {
|
||||
const authInfoResponse = await this.$api.call(ApiAuthinfo.getAuthInfo());
|
||||
|
||||
const authInfo = authInfoResponse.data;
|
||||
this.uid = authInfo.uid;
|
||||
this.isMitarbeiter = authInfo.isMitarbeiter;
|
||||
this.isStudent = authInfo.isStudent;
|
||||
},
|
||||
async fetchViewData() {
|
||||
const viewDataResponse = await this.$api.call(ApiStgOrgLvPlan.getStgOrgLvPlanViewData());
|
||||
|
||||
const viewData = viewDataResponse.data;
|
||||
this.listStg = viewData.studiengaenge;
|
||||
},
|
||||
},
|
||||
async created(){
|
||||
await this.fetchAuthInfo();
|
||||
await this.fetchViewData();
|
||||
|
||||
if (this.propsViewData) {
|
||||
this.formData.stgkz = this.propsViewData.stgkz ? this.propsViewData.stgkz: null;
|
||||
this.formData.sem = this.propsViewData.sem ? this.propsViewData.sem: null;
|
||||
this.formData.verband = this.propsViewData.verband ? this.propsViewData.verband: null;
|
||||
this.formData.gruppe = this.propsViewData.gruppe ? this.propsViewData.gruppe: null;
|
||||
|
||||
//ensure loading dropdown arrays for version propsView
|
||||
if(!this.listVerband.length && this.formData.sem)
|
||||
{
|
||||
this.loadListVerband();
|
||||
}
|
||||
if(!this.listGroup.length && this.formData.verband)
|
||||
{
|
||||
this.loadListGroup();
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="cis-lvplan-stg-org d-flex flex-column h-100">
|
||||
|
||||
<div class="mt-3">
|
||||
<form-form class="row row-cols-1 row-cols-md-2 row-cols-lg-4 row-cols-xl-5 g-3 mb-3">
|
||||
<form-input
|
||||
type="select"
|
||||
v-model="formData.stgkz"
|
||||
@change="loadListSem(formData.stgkz)"
|
||||
>
|
||||
<option :value="null" selected>{{ $p.t('LvPlan/chooseStg') }}</option>
|
||||
<option
|
||||
v-for="stg in listStg"
|
||||
:key="stg.studiengang_kz"
|
||||
:value="stg.studiengang_kz"
|
||||
>
|
||||
{{ stg.kurzbzlang }} ({{ stg.bezeichnung }})
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
type="select"
|
||||
v-model="formData.sem"
|
||||
@change="loadListVerband()"
|
||||
@click="loadListSem(formData.stgkz)"
|
||||
>
|
||||
<option :value="null" selected>Semester</option>
|
||||
<option
|
||||
v-for="sem in listSem"
|
||||
:key="sem"
|
||||
:value="sem"
|
||||
>
|
||||
{{ sem }}
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
type="select"
|
||||
v-model="formData.verband"
|
||||
@change="loadListGroup()"
|
||||
>
|
||||
<option :value="null" selected>{{ $p.t('lehre/verband') }} </option>
|
||||
<option
|
||||
v-for="verband in listVerband"
|
||||
:key="verband"
|
||||
:value="verband"
|
||||
>
|
||||
{{ verband }}
|
||||
</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
type="select"
|
||||
v-model="formData.gruppe"
|
||||
>
|
||||
<option :value="null" selected>{{ $p.t('gruppenmanagement/gruppe') }}</option>
|
||||
<option
|
||||
v-for="group in listGroup"
|
||||
:key="group"
|
||||
:value="group"
|
||||
>
|
||||
{{ group }}
|
||||
</option>
|
||||
</form-input>
|
||||
<button type="button" class="btn btn-secondary" @click="loadLvPlan">{{ $p.t('LvPlan/loadLvPlan') }}</button>
|
||||
|
||||
</form-form>
|
||||
</div>
|
||||
|
||||
<template v-if="timezone">
|
||||
<fhc-calendar
|
||||
v-if="propsViewData && propsViewData.stgkz"
|
||||
ref="calendar"
|
||||
v-model:lv="formData"
|
||||
:timezone="timezone"
|
||||
:get-promise-func="getPromiseFunc"
|
||||
:date="currentDay"
|
||||
:mode="currentMode"
|
||||
@update:date="handleChangeDate"
|
||||
@update:mode="handleChangeMode"
|
||||
@update:range="updateRange"
|
||||
class="responsive-calendar"
|
||||
>
|
||||
<div
|
||||
v-if="downloadLinks"
|
||||
class="d-flex gap-1 justify-items-start"
|
||||
>
|
||||
<div v-for="{ title, icon, link } in downloadLinks">
|
||||
<a
|
||||
:href="link"
|
||||
:aria-label="title"
|
||||
class="py-1 btn btn-outline-secondary"
|
||||
>
|
||||
<div class="d-flex flex-column">
|
||||
<i aria-hidden="true" :class="icon"></i>
|
||||
<span style="font-size:.5rem">{{ title }}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</fhc-calendar>
|
||||
</template>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
@@ -0,0 +1,187 @@
|
||||
import FhcCalendar from "../../Calendar/LvPlan.js";
|
||||
|
||||
import ApiLvPlan from '../.././../api/factory/lvPlan.js';
|
||||
import ApiAuthinfo from '../../../api/factory/authinfo.js';
|
||||
|
||||
export const DEFAULT_MODE_LVPLAN = 'Week';
|
||||
|
||||
export default {
|
||||
name: 'LvPlanStgOrg',
|
||||
inject: {
|
||||
cisRoot: {
|
||||
from: 'cisRoot'
|
||||
},
|
||||
},
|
||||
components: {
|
||||
FhcCalendar,
|
||||
},
|
||||
props: {
|
||||
viewData: Object,
|
||||
propsViewData: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
studiensemester_kurzbz: null,
|
||||
studiensemester_start: null,
|
||||
studiensemester_ende: null,
|
||||
uid: null,
|
||||
isMitarbeiter: false,
|
||||
isStudent: false,
|
||||
listStg: [],
|
||||
currentStgBezeichnung: null
|
||||
};
|
||||
},
|
||||
computed:{
|
||||
currentDay() {
|
||||
if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date)))
|
||||
return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate();
|
||||
return this.propsViewData?.focus_date;
|
||||
},
|
||||
currentMode() {
|
||||
if (!this.propsViewData?.mode || !['day', 'week', 'month'].includes(this.propsViewData?.mode.toLowerCase()))
|
||||
return DEFAULT_MODE_LVPLAN;
|
||||
return this.propsViewData?.mode;
|
||||
},
|
||||
downloadLinks() {
|
||||
if (!this.studiensemester_start || !this.studiensemester_ende || !this.uid)
|
||||
return false;
|
||||
|
||||
let type = false;
|
||||
type = this.isStudent ? 'student' : type;
|
||||
type = this.isMitarbeiter ? 'lektor' : type;
|
||||
if (false === type)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const opts = { zone: this.viewData.timezone };
|
||||
const start = luxon.DateTime
|
||||
.fromISO(this.studiensemester_start, opts)
|
||||
.toUnixInteger();
|
||||
const ende = luxon.DateTime
|
||||
.fromISO(this.studiensemester_ende, opts)
|
||||
.toUnixInteger();
|
||||
|
||||
const download_link = FHC_JS_DATA_STORAGE_OBJECT.app_root
|
||||
+ 'cis/private/lvplan/stpl_kalender.php'
|
||||
+ '?type=' + type
|
||||
+ '&pers_uid=' + this.uid
|
||||
+ '&begin=' + start
|
||||
+ '&ende=' + ende;
|
||||
|
||||
return [
|
||||
{ title: "excel", icon: 'fa-solid fa-file-excel', link: download_link + '&format=excel' },
|
||||
{ title: "csv", icon: 'fa-solid fa-file-csv', link: download_link + '&format=csv' },
|
||||
{ title: "ical1", icon: 'fa-regular fa-calendar', link: download_link + '&format=ical&version=1&target=ical' },
|
||||
{ title: "ical2", icon: 'fa-regular fa-calendar', link: download_link + '&format=ical&version=2&target=ical' }
|
||||
];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChangeDate(day, newMode) {
|
||||
return this.handleChangeMode(newMode, day);
|
||||
},
|
||||
handleChangeMode(newMode, day) {
|
||||
const mode = newMode[0].toUpperCase() + newMode.slice(1)
|
||||
const focus_date = day.toISODate();
|
||||
|
||||
this.$router.push({
|
||||
name: "StgOrgLvPlan",
|
||||
params: {
|
||||
mode,
|
||||
focus_date,
|
||||
stgkz: this.propsViewData.stgkz,
|
||||
sem: this.propsViewData.sem,
|
||||
verband: this.propsViewData.verband,
|
||||
gruppe: this.propsViewData.gruppe,
|
||||
}
|
||||
});
|
||||
},
|
||||
updateRange(rangeInterval) {
|
||||
this.$api
|
||||
.call(ApiLvPlan.studiensemesterDateInterval(
|
||||
rangeInterval.end.startOf('week').toISODate()
|
||||
))
|
||||
.then(res => {
|
||||
this.studiensemester_kurzbz = res.data.studiensemester_kurzbz;
|
||||
this.studiensemester_start = res.data.start;
|
||||
this.studiensemester_ende = res.data.ende;
|
||||
});
|
||||
},
|
||||
getPromiseFunc(start, end) {
|
||||
return [
|
||||
this.$api.call(ApiLvPlan.eventsStgOrg(start.toISODate(), end.toISODate(), this.propsViewData.stgkz, this.propsViewData.sem, this.propsViewData.verband, this.propsViewData.gruppe)),
|
||||
//local for test
|
||||
/* this.$api.call(ApiLvPlan.eventsStgOrg(start.toISODate(), end.toISODate(), this.stgkz, this.sem, this.verband, this.gruppe))*/
|
||||
];
|
||||
},
|
||||
backToDropdown(){
|
||||
this.$router.push({
|
||||
name: "OverviewLvPlan",
|
||||
});
|
||||
}
|
||||
},
|
||||
created(){
|
||||
this.$api
|
||||
.call(ApiAuthinfo.getAuthInfo())
|
||||
.then(res => {
|
||||
this.uid = res.data.uid;
|
||||
this.isMitarbeiter = res.data.isMitarbeiter;
|
||||
this.isStudent = res.data.isStudent;
|
||||
});
|
||||
|
||||
if(this.propsViewData.stgkz) {
|
||||
this.$api
|
||||
.call(ApiLvPlan.getStudiengaenge())
|
||||
.then(result => {
|
||||
const currentStg = result.data.find(
|
||||
item => item.studiengang_kz == this.propsViewData.stgkz
|
||||
);
|
||||
this.currentStgBezeichnung = currentStg.kurzbzlang + " - " + currentStg.bezeichnung;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="cis-lvplan-stg-org d-flex flex-column h-100">
|
||||
<h2>{{ $p.t('LvPlan/headerLvPlanLvVerband') }}</h2>
|
||||
<p v-if="propsViewData.stgkz"><span><strong>{{currentStgBezeichnung}}</strong></span>
|
||||
<span v-if="propsViewData.sem" style="margin-left:10px;"> Semester: {{propsViewData.sem}} </span>
|
||||
<span v-if="propsViewData.verband" style="margin-left:10px;"> Verband: {{propsViewData.verband}} </span>
|
||||
<span v-if="propsViewData.gruppe" style="margin-left:10px;"> Gruppe: {{propsViewData.gruppe}} </span>
|
||||
<button class="py-1 btn btn-outline-secondary" :title="this.$p.t('LvPlan', 'backToDropdown')" style="margin-left:10px;"> <i class="fa fa-arrow-left fa-xs" @click="backToDropdown"></i></button>
|
||||
</p>
|
||||
<p v-else>{{ $p.t('LvPlan/noStgProvided') }}</p>
|
||||
|
||||
<fhc-calendar
|
||||
ref="calendar"
|
||||
:timezone="viewData.timezone"
|
||||
:get-promise-func="getPromiseFunc"
|
||||
:date="currentDay"
|
||||
:mode="currentMode"
|
||||
@update:date="handleChangeDate"
|
||||
@update:mode="handleChangeMode"
|
||||
@update:range="updateRange"
|
||||
class="responsive-calendar"
|
||||
>
|
||||
<div
|
||||
v-if="downloadLinks"
|
||||
class="d-flex gap-1 justify-items-start"
|
||||
>
|
||||
<div v-for="{ title, icon, link } in downloadLinks">
|
||||
<a
|
||||
:href="link"
|
||||
:aria-label="title"
|
||||
class="py-1 btn btn-outline-secondary"
|
||||
>
|
||||
<div class="d-flex flex-column">
|
||||
<i aria-hidden="true" :class="icon"></i>
|
||||
<span style="font-size:.5rem">{{ title }}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</fhc-calendar>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
@@ -30,6 +30,7 @@ export default {
|
||||
menuOpen:true,
|
||||
};
|
||||
},
|
||||
inject: ["isMobile"],
|
||||
provide(){
|
||||
return{
|
||||
setActiveEntry: this.setActiveEntry,
|
||||
@@ -58,7 +59,7 @@ export default {
|
||||
},
|
||||
site_url(){
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
fetchMenu() {
|
||||
@@ -112,10 +113,26 @@ export default {
|
||||
});
|
||||
},
|
||||
template: /*html*/`
|
||||
<button id="nav-main-btn" class="navbar-toggler rounded-0" type="button" data-bs-toggle="offcanvas" data-bs-target="#nav-main" aria-controls="nav-main" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<fhc-searchbar ref="searchbar" id="nav-search" class="fhc-searchbar w-100 py-1 py-lg-2" :searchoptions="searchbaroptions" :searchfunction="searchfunction"></fhc-searchbar>
|
||||
<div
|
||||
id="header-options-collapsible"
|
||||
class="collapse multi-collapse collapse-horizontal show"
|
||||
>
|
||||
<div class="d-flex flex-row align-items-center gap-2 h-100" style="width: 79px">
|
||||
<button id="nav-main-btn" class="navbar-toggler rounded-0 px-2 border-0" type="button" data-bs-toggle="offcanvas" data-bs-target="#nav-main" aria-controls="nav-main" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<span v-if="isMobile" class="d-flex flex-row align-items-center">
|
||||
<theme-switch></theme-switch>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<fhc-searchbar
|
||||
:searchoptions="searchbaroptions"
|
||||
:searchfunction="searchfunction"
|
||||
ref="searchbar"
|
||||
id="nav-search"
|
||||
class="fhc-searchbar w-100 py-1 py-lg-2"
|
||||
></fhc-searchbar>
|
||||
<div id="nav-logo" class="d-none d-lg-block">
|
||||
<div class="d-flex h-100 justify-content-between">
|
||||
<a :href="rootUrl">
|
||||
@@ -124,22 +141,32 @@ export default {
|
||||
<theme-switch></theme-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div id="nav-user">
|
||||
<button id="nav-user-btn" class="btn btn-link rounded-0" type="button" data-bs-toggle="collapse" data-bs-target="#nav-user-menu" aria-expanded="false" aria-controls="nav-user-menu">
|
||||
<img :src="avatarUrl" :alt="$p.t('profilUpdate/profilBild')" class="bg-dark avatar rounded-circle border border-dark"/>
|
||||
</button>
|
||||
<ul ref="navUserDropdown"
|
||||
@[\`shown.bs.collapse\`]="handleShowNavUser"
|
||||
@[\`hide.bs.collapse\`]="handleHideNavUser"
|
||||
id="nav-user-menu" class="top-100 end-0 collapse list-unstyled" aria-labelledby="nav-user-btn">
|
||||
<li><a class="fhc-dark-bg btn rounded-0 d-block" :href="site_url + '/Cis/Profil'" id="menu-profil">Profil</a></li>
|
||||
<li >
|
||||
<cis-sprachen @languageChanged="fetchMenu"></cis-sprachen>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider m-0 "></li>
|
||||
<li ><a class="fhc-dark-bg btn rounded-0 d-block" :href="logoutUrl">Logout</a></li>
|
||||
</ul>
|
||||
|
||||
<div
|
||||
id="header-usermenu-collapsible"
|
||||
class="collapse multi-collapse collapse-horizontal show"
|
||||
>
|
||||
<div
|
||||
:style="!isMobile ? '' : 'width: 51px'"
|
||||
id="nav-user"
|
||||
>
|
||||
<button id="nav-user-btn" class="btn btn-link rounded-0" type="button" data-bs-toggle="collapse" data-bs-target="#nav-user-menu" aria-expanded="false" aria-controls="nav-user-menu">
|
||||
<img :src="avatarUrl" :alt="$p.t('profilUpdate/profilBild')" class="bg-dark avatar rounded-circle border border-dark"/>
|
||||
</button>
|
||||
<ul ref="navUserDropdown"
|
||||
@[\`shown.bs.collapse\`]="handleShowNavUser"
|
||||
@[\`hide.bs.collapse\`]="handleHideNavUser"
|
||||
id="nav-user-menu" class="top-100 end-0 collapse list-unstyled" aria-labelledby="nav-user-btn">
|
||||
<li><a class="fhc-dark-bg btn rounded-0 d-block" :href="site_url + '/Cis/Profil'" id="menu-profil">Profil</a></li>
|
||||
<li >
|
||||
<cis-sprachen @languageChanged="fetchMenu"></cis-sprachen>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider m-0 "></li>
|
||||
<li ><a class="fhc-dark-bg btn rounded-0 d-block" :href="logoutUrl">Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav id="nav-main" class="offcanvas offcanvas-start" tabindex="-1" aria-labelledby="nav-main-btn" data-bs-backdrop="false">
|
||||
<div id="nav-main-sticky">
|
||||
<div id="nav-main-toggle" class="position-static d-none d-lg-block ">
|
||||
|
||||
@@ -100,7 +100,7 @@ export default {
|
||||
</template>
|
||||
<template v-else>
|
||||
<span v-if="event?.lehrfach_bez ">{{event?.lehrfach_bez + (event?.stg_kurzbzlang?' / ' + event?.stg_kurzbzlang:'')}}</span>
|
||||
<span v-else>Lehrveranstaltungs Übersicht</span>
|
||||
<span v-else>{{$p.t('lehre','lvUebersicht')}}</span>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
@@ -2,7 +2,8 @@ import FhcCalendar from "../../Calendar/LvPlan.js";
|
||||
|
||||
import ApiLvPlan from '../../../api/factory/lvPlan.js';
|
||||
|
||||
export const DEFAULT_MODE_RAUMINFO = 'Week'
|
||||
export const DEFAULT_MODE_RAUMINFO_MOBILE = 'List';
|
||||
export const DEFAULT_MODE_RAUMINFO_DESKTOP = 'Week';
|
||||
|
||||
export default {
|
||||
name: "RoomInformation",
|
||||
@@ -13,12 +14,14 @@ export default {
|
||||
viewData: Object, // NOTE(chris): this is inherited from router-view
|
||||
propsViewData: Object
|
||||
},
|
||||
inject: ["isMobile"],
|
||||
computed: {
|
||||
currentDay() {
|
||||
return this.propsViewData?.focus_date || luxon.DateTime.now().setZone(this.viewData.timezone).toISODate();
|
||||
return this.propsViewData?.focus_date || luxon.DateTime.now().setZone(FHC_JS_DATA_STORAGE_OBJECT.timezone).toISODate();
|
||||
},
|
||||
currentMode() {
|
||||
return this.propsViewData?.mode || DEFAULT_MODE_RAUMINFO;
|
||||
const defaultMode = this.isMobile ? DEFAULT_MODE_RAUMINFO_MOBILE : DEFAULT_MODE_RAUMINFO_DESKTOP;
|
||||
return this.propsViewData?.mode || defaultMode;
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
@@ -51,7 +54,6 @@ export default {
|
||||
<hr>
|
||||
<fhc-calendar
|
||||
ref="calendar"
|
||||
:timezone="viewData.timezone"
|
||||
:get-promise-func="getPromiseFunc"
|
||||
:date="currentDay"
|
||||
:mode="currentMode"
|
||||
|
||||
@@ -36,8 +36,9 @@ export default {
|
||||
return this.lvs.filter(lv => lv.studiengang_kz == studiengang.studiengang_kz && lv.semester == studiengang.semester);
|
||||
}
|
||||
},
|
||||
template: `<div class="mylv-semester" v-if="ready">
|
||||
<mylv-semester-studiengang v-for="studiengang in studiengaenge" :key="studiengang.studiengang_kz" v-bind="studiengang" :lvs="lvsForStudiengang(studiengang)"/>
|
||||
template: `
|
||||
<div class="mylv-semester" v-if="ready">
|
||||
<mylv-semester-studiengang v-for="studiengang in studiengaenge" :key="studiengang.studiengang_kz" v-bind="studiengang" :lvs="lvsForStudiengang(studiengang)" :semesterInfo="$props.semester" />
|
||||
</div>
|
||||
<div class="mylv-semester text-center" v-else>
|
||||
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import MylvSemesterStudiengangLv from "./Studiengang/Lv.js";
|
||||
import MylvSemesterStudiengangAverageGrade from "./Studiengang/AverageGrade.js";
|
||||
import Phrasen from "../../../../mixins/Phrasen.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MylvSemesterStudiengangLv
|
||||
MylvSemesterStudiengangLv,
|
||||
MylvSemesterStudiengangAverageGrade
|
||||
},
|
||||
mixins: [
|
||||
Phrasen
|
||||
@@ -12,6 +14,7 @@ export default {
|
||||
bezeichnung: String,
|
||||
kuerzel: String,
|
||||
semester: [String,Number],
|
||||
semesterInfo: [String,Number],
|
||||
lvs: Array,
|
||||
sg_bezeichnung_eng: String
|
||||
},
|
||||
@@ -29,9 +32,10 @@ export default {
|
||||
methods: {
|
||||
note(lv) {
|
||||
return lv.benotung ? lv.znote || lv.lvnote || null : null;
|
||||
}
|
||||
},
|
||||
},
|
||||
template: `<div class="card mb-3">
|
||||
|
||||
<div class="card-body">
|
||||
<h4 class="card-title mb-3">{{$p.user_language.value === 'English' ? sg_bezeichnung_eng : bezeichnung}} - {{kuerzel}}
|
||||
<small>{{semester}}.{{$p.t('lehre/semester')}}</small>
|
||||
@@ -41,6 +45,7 @@ export default {
|
||||
<mylv-semester-studiengang-lv v-bind="lv" class="text-center h-100"></mylv-semester-studiengang-lv>
|
||||
</div>
|
||||
</div>
|
||||
<mylv-semester-studiengang-average-grade :semesterInfo="$props.semesterInfo" />
|
||||
</div>
|
||||
</div>`
|
||||
};
|
||||
@@ -0,0 +1,86 @@
|
||||
import Phrasen from "../../../../../mixins/Phrasen.js";
|
||||
import ApiLehre from "../../../../../api/factory/lehre.js";
|
||||
|
||||
export default {
|
||||
mixins: [
|
||||
Phrasen
|
||||
],
|
||||
props: {
|
||||
semesterInfo: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
gradeAverage: null,
|
||||
gradeWeightedAverage: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async fetchAverageGrade() {
|
||||
this.gradeAverage = null;
|
||||
this.gradeWeightedAverage = null;
|
||||
if (!this.$props.semesterInfo) return;
|
||||
|
||||
let gradeAverageResponse = await this.$api.call(
|
||||
ApiLehre.getSemesterAverageGrade(this.$props.semesterInfo),
|
||||
);
|
||||
const gradeAverageResponseData = gradeAverageResponse.data;
|
||||
this.gradeAverage =
|
||||
gradeAverageResponseData.average_grade?.toFixed(2);
|
||||
this.gradeWeightedAverage =
|
||||
gradeAverageResponseData.weighted_average_grade?.toFixed(2);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
semesterInfo() {
|
||||
this.fetchAverageGrade();
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
await this.fetchAverageGrade();
|
||||
},
|
||||
template: /*html*/`
|
||||
<div class="card mylv-semester-studiengang-grades">
|
||||
|
||||
<div class="card-header text-center">
|
||||
<h6>{{$p.t('lehre/notenstatistik')}}</h6>
|
||||
</div>
|
||||
|
||||
<div v-if="gradeAverage && gradeWeightedAverage">
|
||||
<table class="card-body table w-auto mx-auto">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-end">
|
||||
{{$p.t('lehre/headerAverage')}}
|
||||
</td>
|
||||
<td class="text-start">
|
||||
{{ gradeAverage }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-end">
|
||||
{{$p.t('lehre/headerWeightedAverage')}}
|
||||
</td>
|
||||
<td class="text-start">
|
||||
{{ gradeWeightedAverage }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div v-else class="card-body text-center">
|
||||
<p>{{$p.t('lehre/info_noGradesYet')}}</p>
|
||||
</div>
|
||||
|
||||
<div v-if="gradeAverage && gradeWeightedAverage" class="card-footer d-flex align-items-start text-muted small">
|
||||
<i class="fa fa-circle-info me-2 mt-1"></i>
|
||||
<div>
|
||||
<strong>{{$p.t('ui', 'hinweis')}}</strong><br>
|
||||
* {{$p.t('lehre/noticeAverage')}}
|
||||
<br>
|
||||
** {{$p.t('lehre/noticeWeightedAverage')}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -73,11 +73,11 @@ export default {
|
||||
},
|
||||
grade() {
|
||||
const languageIndex = this.$p.user_language.value === 'English' ? 1 : 0
|
||||
// no more showing of grade LV, if grade Zeugnis is not existing yet
|
||||
if(this.benotung && this.znotebez?.length) {
|
||||
return this.znotebez[languageIndex]
|
||||
} else if(this.benotung && this.lvnotebez?.length) {
|
||||
return this.lvnotebez[languageIndex]
|
||||
} else return null
|
||||
}
|
||||
else return null
|
||||
},
|
||||
LvHasPruefungenInformation(){
|
||||
return this.pruefungenData && this.pruefungenData.length > 0;
|
||||
|
||||
@@ -9,6 +9,7 @@ import QuickLinks from "./ProfilComponents/QuickLinks.js";
|
||||
import ProfilEmails from "./ProfilComponents/ProfilEmails.js";
|
||||
import RoleInformation from "./ProfilComponents/RoleInformation.js";
|
||||
import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
|
||||
import CalendarSync from "./ProfilComponents/CalendarSync.js";
|
||||
|
||||
import ApiProfilUpdate from '../../../api/factory/profilUpdate.js';
|
||||
import { dateFilter } from '../../../tabulator/filters/Dates.js';
|
||||
@@ -26,6 +27,7 @@ export default {
|
||||
ProfilEmails,
|
||||
RoleInformation,
|
||||
ProfilInformation,
|
||||
CalendarSync,
|
||||
},
|
||||
|
||||
inject: ["sortProfilUpdates", "collapseFunction", "language","isEditable"],
|
||||
@@ -103,7 +105,6 @@ export default {
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
betriebsmittel_table_options: {
|
||||
persistenceID: "filterTableMaProfilBetriebsmittel",
|
||||
persistence: {
|
||||
@@ -160,6 +161,7 @@ export default {
|
||||
props: {
|
||||
data: Object,
|
||||
editData: Object,
|
||||
calendarSyncUrls: Array,
|
||||
},
|
||||
|
||||
methods: {
|
||||
@@ -296,6 +298,11 @@ export default {
|
||||
}
|
||||
};
|
||||
},
|
||||
quickLinks() {
|
||||
let quickLinks = [];
|
||||
//
|
||||
return quickLinks;
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
@@ -313,7 +320,6 @@ export default {
|
||||
});
|
||||
//? sorts the profil Updates: pending -> accepted -> rejected
|
||||
this.data.profilUpdates?.sort(this.sortProfilUpdates);
|
||||
|
||||
},
|
||||
watch: {
|
||||
'data.funktionen'(newVal) {
|
||||
@@ -331,12 +337,6 @@ export default {
|
||||
<edit-profil v-if="showModal" ref="editModal" :isMitarbeiter="true" @hideBsModal="hideEditProfilModal" :value="JSON.parse(JSON.stringify(filteredEditData))" :titel="$p.t('profil','profilBearbeiten')"></edit-profil>
|
||||
<div class="row">
|
||||
<div class="d-md-none col-12 ">
|
||||
<!--TODO: uncomment when implemented
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<quick-links :title="$p.t('profil','quickLinks')" :mobile="true"></quick-links>
|
||||
</div>
|
||||
</div>-->
|
||||
<!-- Bearbeiten Button -->
|
||||
<div v-if="isEditable" class="row mb-3 ">
|
||||
<div class="col">
|
||||
@@ -465,17 +465,11 @@ export default {
|
||||
</div>
|
||||
<!-- START OF SIDE PANEL -->
|
||||
<div class="col-md-4 col-xxl-3 col-sm-12 text-break" >
|
||||
<!--TODO: uncomment when implemented
|
||||
<div class="row d-none d-md-block mb-3">
|
||||
|
||||
<div class="col">
|
||||
|
||||
<quick-links :title="$p.t('profil','quickLinks')"></quick-links>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>-->
|
||||
<div v-if="quickLinks.length" class="row mb-4">
|
||||
<div class="col">
|
||||
<quick-links :title="$p.t('profil/quickLinks')" :links="quickLinks" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- Bearbeiten Button -->
|
||||
<div class="row d-none d-md-block ">
|
||||
<div class="col mb-3">
|
||||
@@ -501,12 +495,17 @@ export default {
|
||||
<ausweis-status :data="data.zutrittsdatum"></ausweis-status>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<!-- MAILVERTEILER -->
|
||||
<mailverteiler :data="data?.mailverteiler" :title="$p.t('profil','mailverteiler')"></mailverteiler>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<calendar-sync :uid="$props.data.username" :calendarSyncUrls="$props.calendarSyncUrls"></calendar-sync>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
import {CoreFilterCmpt} from "../../../components/filter/Filter.js";
|
||||
import { CoreFilterCmpt } from "../../../components/filter/Filter.js";
|
||||
import Mailverteiler from "./ProfilComponents/Mailverteiler.js";
|
||||
import QuickLinks from "./ProfilComponents/QuickLinks.js";
|
||||
import RoleInformation from "./ProfilComponents/RoleInformation.js";
|
||||
import ProfilEmails from "./ProfilComponents/ProfilEmails.js";
|
||||
import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
|
||||
import QuickLinks from "./ProfilComponents/QuickLinks.js";
|
||||
|
||||
import { dateFilter } from '../../../tabulator/filters/Dates.js';
|
||||
import { dateFilter } from "../../../tabulator/filters/Dates.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFilterCmpt,
|
||||
Mailverteiler,
|
||||
QuickLinks,
|
||||
RoleInformation,
|
||||
ProfilEmails,
|
||||
ProfilInformation,
|
||||
QuickLinks,
|
||||
},
|
||||
inject: ["collapseFunction", "language"],
|
||||
data() {
|
||||
return {
|
||||
collapseIconFunktionen: true,
|
||||
preloadedPhrasen:{},
|
||||
preloadedPhrasen: {},
|
||||
funktionen_table_options: {
|
||||
persistenceID: "filterTableMaViewProfilFunktionen",
|
||||
persistence: {
|
||||
columns: false
|
||||
columns: false,
|
||||
},
|
||||
minHeight: 300,
|
||||
layout: "fitColumns",
|
||||
@@ -35,58 +35,65 @@ export default {
|
||||
//? option when wanting to hide the collapsed list
|
||||
|
||||
{
|
||||
title:
|
||||
"<i id='collapseIconFunktionen' role='button' class='fa-solid fa-angle-down '></i>",
|
||||
title: "<i id='collapseIconFunktionen' role='button' class='fa-solid fa-angle-down '></i>",
|
||||
field: "collapse",
|
||||
headerSort: false,
|
||||
headerFilter: false,
|
||||
formatter: "responsiveCollapse",
|
||||
maxWidth: 40,
|
||||
headerClick: this.collapseFunction,
|
||||
visible: true
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('ui/bezeichnung')),
|
||||
title: Vue.computed(() => this.$p.t("ui/bezeichnung")),
|
||||
field: "Bezeichnung",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
visible: true
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('lehre/organisationseinheit')),
|
||||
title: Vue.computed(() =>
|
||||
this.$p.t("lehre/organisationseinheit"),
|
||||
),
|
||||
field: "Organisationseinheit",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
visible: true
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('global/gueltigVon')),
|
||||
title: Vue.computed(() =>
|
||||
this.$p.t("global/gueltigVon"),
|
||||
),
|
||||
field: "Gültig_von",
|
||||
headerFilterFunc: 'dates',
|
||||
headerFilterFunc: "dates",
|
||||
headerFilter: dateFilter,
|
||||
resizable: true,
|
||||
minWidth: 200,
|
||||
visible: true,
|
||||
formatter:"datetime",
|
||||
formatterParams: this.datetimeFormatterParams()
|
||||
formatter: "datetime",
|
||||
formatterParams: this.datetimeFormatterParams(),
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('global/gueltigBis')),
|
||||
title: Vue.computed(() =>
|
||||
this.$p.t("global/gueltigBis"),
|
||||
),
|
||||
field: "Gültig_bis",
|
||||
headerFilterFunc: 'dates',
|
||||
headerFilterFunc: "dates",
|
||||
headerFilter: dateFilter,
|
||||
resizable: true,
|
||||
minWidth: 200,
|
||||
visible: true,
|
||||
formatter:"datetime",
|
||||
formatterParams: this.datetimeFormatterParams()
|
||||
formatter: "datetime",
|
||||
formatterParams: this.datetimeFormatterParams(),
|
||||
},
|
||||
{
|
||||
title: Vue.computed(() => this.$p.t('profil/wochenstunden')),
|
||||
title: Vue.computed(() =>
|
||||
this.$p.t("profil/wochenstunden"),
|
||||
),
|
||||
field: "Wochenstunden",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
visible: true
|
||||
visible: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -94,47 +101,56 @@ export default {
|
||||
},
|
||||
|
||||
//? this is the prop passed to the dynamic component with the custom data of the view
|
||||
props: ["data"],
|
||||
props: ["data", "permissions"],
|
||||
methods: {
|
||||
funktionenTableBuilt: function () {
|
||||
this.$refs.funktionenTable.tabulator.setData(this.data.funktionen);
|
||||
},
|
||||
datetimeFormatterParams: function() {
|
||||
datetimeFormatterParams: function () {
|
||||
const params = {
|
||||
inputFormat:"yyyy-MM-dd",
|
||||
outputFormat:"dd.MM.yyyy",
|
||||
invalidPlaceholder:"(invalid date)",
|
||||
timezone:FHC_JS_DATA_STORAGE_OBJECT.timezone
|
||||
inputFormat: "yyyy-MM-dd",
|
||||
outputFormat: "dd.MM.yyyy",
|
||||
invalidPlaceholder: "(invalid date)",
|
||||
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
||||
};
|
||||
return params;
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'data.funktionen'(newVal) {
|
||||
if(this.$refs.funktionenTable) this.$refs.funktionenTable.tabulator.setData(newVal);
|
||||
"data.funktionen"(newVal) {
|
||||
if (this.$refs.funktionenTable)
|
||||
this.$refs.funktionenTable.tabulator.setData(newVal);
|
||||
},
|
||||
"language.value"(newVal) {
|
||||
// reevaluates computed phrasen
|
||||
if (this.$refs.funktionenTable)
|
||||
this.$refs.funktionenTable.tabulator.setColumns(
|
||||
this.funktionen_table_options.columns,
|
||||
);
|
||||
},
|
||||
'language.value'(newVal) { // reevaluates computed phrasen
|
||||
if(this.$refs.funktionenTable) this.$refs.funktionenTable.tabulator.setColumns(this.funktionen_table_options.columns)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getTelefonValue() {
|
||||
if(this.data.standort_telefon?.kontakt) {
|
||||
return this.data.standort_telefon.kontakt + " " + this.data.telefonklappe
|
||||
} else if(this.data.standort_telefon) {
|
||||
return this.data.standort_telefon + " " + this.data.telefonklappe
|
||||
if (this.data.standort_telefon?.kontakt) {
|
||||
return (
|
||||
this.data.standort_telefon.kontakt +
|
||||
" " +
|
||||
this.data.telefonklappe
|
||||
);
|
||||
} else if (this.data.standort_telefon) {
|
||||
return (
|
||||
this.data.standort_telefon + " " + this.data.telefonklappe
|
||||
);
|
||||
} else {
|
||||
return this.data.telefonklappe
|
||||
return this.data.telefonklappe;
|
||||
}
|
||||
},
|
||||
fotoStatus() {
|
||||
return this.data?.fotoStatus ?? null;
|
||||
},
|
||||
|
||||
personEmails() {
|
||||
return this.data?.emails ? this.data.emails : [];
|
||||
},
|
||||
|
||||
profilInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
@@ -151,42 +167,67 @@ export default {
|
||||
foto: this.data.foto,
|
||||
};
|
||||
},
|
||||
|
||||
roleInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
geburtsdatum: {
|
||||
label: `${this.$p.t('profil','Geburtsdatum')}`,
|
||||
value: this.data.gebdatum
|
||||
label: `${this.$p.t("profil", "Geburtsdatum")}`,
|
||||
value: this.data.gebdatum,
|
||||
},
|
||||
geburtsort: {
|
||||
label: `${this.$p.t('profil','Geburtsort')}`,
|
||||
value: this.data.gebort
|
||||
label: `${this.$p.t("profil", "Geburtsort")}`,
|
||||
value: this.data.gebort,
|
||||
},
|
||||
personenkennzeichen: {
|
||||
label: `${this.$p.t('profil','Kurzzeichen')}`,
|
||||
value: this.data.kurzbz
|
||||
label: `${this.$p.t("profil", "Kurzzeichen")}`,
|
||||
value: this.data.kurzbz,
|
||||
},
|
||||
telefon: {
|
||||
label: `${this.$p.t('profil','Telefon')}`,
|
||||
value: this.getTelefonValue
|
||||
label: `${this.$p.t("profil", "Telefon")}`,
|
||||
value: this.getTelefonValue,
|
||||
},
|
||||
office: {
|
||||
label: `${this.$p.t('profil','Büro')}`,
|
||||
value: this.data.ort_kurzbz
|
||||
}
|
||||
label: `${this.$p.t("profil", "Büro")}`,
|
||||
value: this.data.ort_kurzbz,
|
||||
},
|
||||
};
|
||||
},
|
||||
quickLinks() {
|
||||
let quickLinks = [];
|
||||
if (
|
||||
this.$props.permissions &&
|
||||
this.$props.permissions["basis/other_lv_plan"]
|
||||
) {
|
||||
quickLinks.push({
|
||||
icon: "fa-calendar-days",
|
||||
phrase: "lehre/stundenplan",
|
||||
action: () => {
|
||||
this.$router.push({
|
||||
name: "OtherLvPlan",
|
||||
params: { otherUid: this.$props.data.username },
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
return quickLinks;
|
||||
},
|
||||
},
|
||||
created(){
|
||||
created() {
|
||||
this.$p.loadCategory(["ui", "lehre", "global", "profil"]).then(() => {
|
||||
this.preloadedPhrasen.bezeichnungPhrase = this.$p.t('ui/bezeichnung');
|
||||
this.preloadedPhrasen.organisationseinheitPhrase = this.$p.t('lehre/organisationseinheit');
|
||||
this.preloadedPhrasen.gueltigVonPhrase = this.$p.t('global/gueltigVon');
|
||||
this.preloadedPhrasen.gueltigBisPhrase = this.$p.t('global/gueltigBis');
|
||||
this.preloadedPhrasen.wochenstundenPhrase = this.$p.t('profil/wochenstunden');
|
||||
this.preloadedPhrasen.bezeichnungPhrase =
|
||||
this.$p.t("ui/bezeichnung");
|
||||
this.preloadedPhrasen.organisationseinheitPhrase = this.$p.t(
|
||||
"lehre/organisationseinheit",
|
||||
);
|
||||
this.preloadedPhrasen.gueltigVonPhrase =
|
||||
this.$p.t("global/gueltigVon");
|
||||
this.preloadedPhrasen.gueltigBisPhrase =
|
||||
this.$p.t("global/gueltigBis");
|
||||
this.preloadedPhrasen.wochenstundenPhrase = this.$p.t(
|
||||
"profil/wochenstunden",
|
||||
);
|
||||
this.preloadedPhrasen.loaded = true;
|
||||
});
|
||||
},
|
||||
@@ -242,7 +283,7 @@ export default {
|
||||
</div>
|
||||
<!-- START OF THE SECOND PROFIL INFORMATION ROW -->
|
||||
<!-- ROW WITH PROFIL IMAGE AND INFORMATION END -->
|
||||
</div >
|
||||
</div>
|
||||
<!-- SECOND ROW UNDER THE PROFIL IMAGE AND INFORMATION WITH THE TABLES -->
|
||||
<div class="row">
|
||||
<!-- FIRST TABLE -->
|
||||
@@ -256,26 +297,22 @@ export default {
|
||||
<!-- START OF SIDE PANEL -->
|
||||
<div class="col-md-4 col-xxl-3 col-sm-12 text-break" >
|
||||
<!-- VISIBLE UNTIL VIEWPORT MD -->
|
||||
<!--TODO: uncomment when implemented
|
||||
<div class="row d-none d-md-block mb-3">
|
||||
<div class="col">
|
||||
|
||||
<quick-links :title="$p.t('profil','quickLinks')" ></quick-links>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<!-- MAILVERTEILER -->
|
||||
<mailverteiler :data="data?.mailverteiler" :title="$p.t('profil','mailverteiler')"></mailverteiler>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END OF SIDE PANEL -->
|
||||
</div>
|
||||
<div v-if="quickLinks.length" class="row mb-4">
|
||||
<div class="col">
|
||||
<quick-links :title="$p.t('profil/quickLinks')" :links="quickLinks" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<!-- MAILVERTEILER -->
|
||||
<mailverteiler :data="data?.mailverteiler" :title="$p.t('profil','mailverteiler')"></mailverteiler>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END OF SIDE PANEL -->
|
||||
</div>
|
||||
<!-- END OF CONTAINER ROW-->
|
||||
</div>
|
||||
<!-- END OF CONTAINER -->
|
||||
</div>
|
||||
<!-- END OF CONTAINER -->
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -4,8 +4,8 @@ import ViewStudentProfil from "./StudentViewProfil.js";
|
||||
import ViewMitarbeiterProfil from "./MitarbeiterViewProfil.js";
|
||||
import Loading from "../../Loader.js";
|
||||
|
||||
import ApiProfil from '../../../api/factory/profil.js';
|
||||
import ApiProfilUpdate from '../../../api/factory/profilUpdate.js';
|
||||
import ApiProfil from "../../../api/factory/profil.js";
|
||||
import ApiProfilUpdate from "../../../api/factory/profilUpdate.js";
|
||||
|
||||
Vue.$collapseFormatter = function (data) {
|
||||
//data - an array of objects containing the column title and value for each cell
|
||||
@@ -35,7 +35,7 @@ Vue.$collapseFormatter = function (data) {
|
||||
};
|
||||
|
||||
export const Profil = {
|
||||
name: 'Profil',
|
||||
name: "Profil",
|
||||
components: {
|
||||
StudentProfil,
|
||||
MitarbeiterProfil,
|
||||
@@ -46,11 +46,8 @@ export const Profil = {
|
||||
props: {
|
||||
uid: {
|
||||
type: String,
|
||||
required:false,
|
||||
required: false,
|
||||
},
|
||||
viewData: {
|
||||
type: Object,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -62,17 +59,19 @@ export const Profil = {
|
||||
data: null,
|
||||
// notfound is null by default, but contains an UID if no user exists with that UID
|
||||
notFoundUID: null,
|
||||
isEditable: this.viewData.editable ?? false,
|
||||
isEditable: false,
|
||||
authPermissions: null,
|
||||
calendarSyncUrls: [],
|
||||
};
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
isEditable: Vue.computed(()=>this.isEditable),
|
||||
isEditable: Vue.computed(() => this.isEditable),
|
||||
profilUpdateStates: Vue.computed(() =>
|
||||
this.profilUpdateStates ? this.profilUpdateStates : false
|
||||
this.profilUpdateStates ? this.profilUpdateStates : false,
|
||||
),
|
||||
profilUpdateTopic: Vue.computed(() =>
|
||||
this.profilUpdateTopic ? this.profilUpdateTopic : false
|
||||
this.profilUpdateTopic ? this.profilUpdateTopic : false,
|
||||
),
|
||||
setLoading: (newValue) => {
|
||||
this.loading = newValue;
|
||||
@@ -130,8 +129,12 @@ export const Profil = {
|
||||
//? if they have the same status the insert date is used for ordering
|
||||
if (ele1.status === ele2.status) {
|
||||
result =
|
||||
new Date(ele2.insertamum.split(".").reverse().join("-")) -
|
||||
new Date(ele1.insertamum.split(".").reverse().join("-"));
|
||||
new Date(
|
||||
ele2.insertamum.split(".").reverse().join("-"),
|
||||
) -
|
||||
new Date(
|
||||
ele1.insertamum.split(".").reverse().join("-"),
|
||||
);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
@@ -139,6 +142,8 @@ export const Profil = {
|
||||
},
|
||||
methods: {
|
||||
async load() {
|
||||
await this.fetchViewData();
|
||||
|
||||
// fetch profilUpdateStates to provide them to children components
|
||||
await this.$api
|
||||
.call(ApiProfilUpdate.getStatus())
|
||||
@@ -157,20 +162,20 @@ export const Profil = {
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
|
||||
this.$api
|
||||
.call(ApiProfil.profilViewData(this.$route.params.uid??null))
|
||||
.then((response) => response.data).then(data=>{
|
||||
this.view = data?.profil_data.view;
|
||||
this.data = data?.profil_data.data;
|
||||
this.isEditable = data?.editable ?? false;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
|
||||
},
|
||||
async fetchViewData() {
|
||||
let viewDataResult = await this.$api.call(
|
||||
ApiProfil.getProfilViewData(this.$route.params.uid ?? null),
|
||||
);
|
||||
|
||||
const data = viewDataResult.data;
|
||||
if (!data) return;
|
||||
|
||||
this.view = data.profil_data.view;
|
||||
this.isEditable = data.profil_data.editable;
|
||||
this.data = data.profil_data.data;
|
||||
this.calendarSyncUrls = data.calendar_sync_urls ?? [];
|
||||
this.authPermissions = data.permissions;
|
||||
},
|
||||
zustellAdressenCount() {
|
||||
if (!this.data || !this.data.adressen) {
|
||||
@@ -186,7 +191,7 @@ export const Profil = {
|
||||
})
|
||||
.map((adresse) => {
|
||||
return adresse.requested_change.adresse_id;
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -197,8 +202,9 @@ export const Profil = {
|
||||
.every((adresse) =>
|
||||
this.data.profilUpdates.some(
|
||||
(update) =>
|
||||
update.requested_change.adresse_id == adresse.adresse_id
|
||||
)
|
||||
update.requested_change.adresse_id ==
|
||||
adresse.adresse_id,
|
||||
),
|
||||
)
|
||||
) {
|
||||
adressenArray = adressenArray.concat(
|
||||
@@ -208,12 +214,11 @@ export const Profil = {
|
||||
})
|
||||
.map((adr) => {
|
||||
return adr.adresse_id;
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return [...new Set(adressenArray)];
|
||||
|
||||
},
|
||||
zustellKontakteCount() {
|
||||
if (!this.data || !this.data.kontakte) {
|
||||
@@ -226,14 +231,17 @@ export const Profil = {
|
||||
kontakteArray = kontakteArray.concat(
|
||||
this.data.profilUpdates
|
||||
.filter((update) => {
|
||||
return update.status === 'Pending' && update.requested_change.zustellung;
|
||||
return (
|
||||
update.status === "Pending" &&
|
||||
update.requested_change.zustellung
|
||||
);
|
||||
})
|
||||
.map((kontant) => {
|
||||
return {
|
||||
kontakt_id: kontant.requested_change.kontakt_id,
|
||||
kontakttyp: kontant.requested_change.kontakttyp
|
||||
};
|
||||
})
|
||||
kontakt_id: kontant.requested_change.kontakt_id,
|
||||
kontakttyp: kontant.requested_change.kontakttyp,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -244,8 +252,10 @@ export const Profil = {
|
||||
.every((kontakt) =>
|
||||
this.data.profilUpdates.some(
|
||||
(update) =>
|
||||
update.status === 'Pending' && update.requested_change.kontakt_id == kontakt.kontakt_id
|
||||
)
|
||||
update.status === "Pending" &&
|
||||
update.requested_change.kontakt_id ==
|
||||
kontakt.kontakt_id,
|
||||
),
|
||||
)
|
||||
) {
|
||||
kontakteArray = kontakteArray.concat(
|
||||
@@ -255,10 +265,10 @@ export const Profil = {
|
||||
})
|
||||
.map((kon) => {
|
||||
return {
|
||||
kontakt_id: kon.kontakt_id,
|
||||
kontakttyp: kon.kontakttyp
|
||||
};
|
||||
})
|
||||
kontakt_id: kon.kontakt_id,
|
||||
kontakttyp: kon.kontakttyp,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -266,7 +276,6 @@ export const Profil = {
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
|
||||
filteredEditData() {
|
||||
if (!this.data) {
|
||||
return;
|
||||
@@ -330,8 +339,12 @@ export const Profil = {
|
||||
// excludes all contacts that are already used in pending profil update requests
|
||||
return !this.data.profilUpdates?.some(
|
||||
(update) =>
|
||||
update.status === this.profilUpdateStates["Pending"] &&
|
||||
update.requested_change?.kontakt_id === item.kontakt_id
|
||||
update.status ===
|
||||
this.profilUpdateStates[
|
||||
"Pending"
|
||||
] &&
|
||||
update.requested_change?.kontakt_id ===
|
||||
item.kontakt_id,
|
||||
);
|
||||
})
|
||||
.map((kontakt) => {
|
||||
@@ -347,12 +360,18 @@ export const Profil = {
|
||||
topic: this.profilUpdateTopic?.["Private Adressen"],
|
||||
data: this.data.adressen
|
||||
?.filter((item) => {
|
||||
return !this.data.profilUpdates?.some((update) => {
|
||||
return (
|
||||
update.status === this.profilUpdateStates["Pending"] &&
|
||||
update.requested_change?.adresse_id == item.adresse_id
|
||||
);
|
||||
});
|
||||
return !this.data.profilUpdates?.some(
|
||||
(update) => {
|
||||
return (
|
||||
update.status ===
|
||||
this.profilUpdateStates[
|
||||
"Pending"
|
||||
] &&
|
||||
update.requested_change
|
||||
?.adresse_id == item.adresse_id
|
||||
);
|
||||
},
|
||||
);
|
||||
})
|
||||
.map((adresse) => {
|
||||
return {
|
||||
@@ -374,23 +393,28 @@ export const Profil = {
|
||||
this.$refs.loadingModalRef.hide();
|
||||
}
|
||||
},
|
||||
uid (newVal, oldVal) {
|
||||
this.load()
|
||||
}
|
||||
uid(newVal, oldVal) {
|
||||
this.load();
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.load()
|
||||
this.load();
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<div class="pb-4">
|
||||
<div v-if="notFoundUID">
|
||||
<h3>Es wurde keine Person mit der UID {{this.notFoundUID}} gefunden</h3>
|
||||
</div>
|
||||
<div v-else>
|
||||
<loading ref="loadingModalRef" :timeout="0"></loading>
|
||||
<component :is="view" :data="data" :editData="filteredEditData" ></component>
|
||||
<component
|
||||
:is="view"
|
||||
:data="data"
|
||||
:editData="filteredEditData"
|
||||
:permissions="authPermissions"
|
||||
:calendarSyncUrls="calendarSyncUrls"></component>
|
||||
</div>
|
||||
</div>`,
|
||||
}
|
||||
};
|
||||
|
||||
export default Profil
|
||||
export default Profil;
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
export default {
|
||||
name: "CalendarSync",
|
||||
props: { uid: String, calendarSyncUrls: Array },
|
||||
data() {
|
||||
return {
|
||||
syncInstructionsUrlWithoutParam:
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
"cms/content.php?content_id=",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
copyUrlToClipboard(url) {
|
||||
navigator.clipboard.writeText(url);
|
||||
this.$fhcAlert.alertSuccess(
|
||||
this.$p.t("profil/calendar_sync_clipboard_copy_confirmation"),
|
||||
);
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
{{ $p.t("profil/calendar_sync") }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-column gap-3">
|
||||
<a
|
||||
target="_blank"
|
||||
:href="syncInstructionsUrlWithoutParam + $p.t('DMS-Link/lvplanSyncFAQ')"
|
||||
class="fhc-link-color d-flex flex-row gap-2 align-items-center"
|
||||
>
|
||||
<span>
|
||||
<i class="fa-solid fa-up-right-from-square ms-2"></i>
|
||||
</span>
|
||||
<span>
|
||||
{{ $p.t("profil/calendar_sync_instructions") }}
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
v-for="syncUrl in $props.calendarSyncUrls"
|
||||
:key="syncUrl.identifier"
|
||||
@click.prevent="copyUrlToClipboard(syncUrl.url)"
|
||||
@contextmenu.prevent="copyUrlToClipboard(syncUrl.url)"
|
||||
href="#"
|
||||
class="fhc-link-color d-flex flex-row gap-2 align-items-center"
|
||||
>
|
||||
<span>
|
||||
<i class="fa-regular fa-copy ms-2 text-decoration-none"></i>
|
||||
</span>
|
||||
{{ $p.t(syncUrl.labelPhrase) }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>`,
|
||||
};
|
||||
@@ -1,53 +1,31 @@
|
||||
export default {
|
||||
//TODO: To be implemented
|
||||
props: {
|
||||
data: {
|
||||
type: String,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
mobile: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hideCollapse: function () {
|
||||
this.collapseOpen = false;
|
||||
},
|
||||
showCollapse: function () {
|
||||
this.collapseOpen = true;
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
collapseOpen: false,
|
||||
};
|
||||
},
|
||||
template: /*html*/ `
|
||||
name: "QuickLinks",
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
links: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div class="card">
|
||||
<template v-if="mobile">
|
||||
<button class="btn btn-outline-primary" data-bs-toggle="collapse" data-bs-target="#quickLinks" :aria-expanded="collapseOpen" aria-controls="quickLinks" >
|
||||
{{title}}
|
||||
<i class="fa " :class="collapseOpen?'fa-chevron-up':'fa-chevron-down'"></i>
|
||||
</button>
|
||||
<div @[\`show.bs.collapse\`]="collapseOpen=true;" @[\`hide.bs.collapse\`]="collapseOpen=false;" class="mt-1 collapse" id="quickLinks">
|
||||
<div class="list-group">
|
||||
<a href="#" class="list-group-item list-group-item-action">{{$p.t('profil','zeitwuensche')}}</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">{{$p.t('profil','lehrveranstaltungen')}}</a>
|
||||
<a href="#" class="list-group-item list-group-item-action ">{{$p.t('profil','zeitsperren')}}</a>
|
||||
<div class="card-header">
|
||||
{{title}}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-row align-items-center gap-3 flex-wrap">
|
||||
<div v-for="link in links" @click="link.action()" type="button" class="d-flex flex-row gap-2 px-3 py-2 border fhc-primary">
|
||||
<div><i class="fa" :class="link.icon"></i></div>
|
||||
{{ $p.t(link.phrase) }}
|
||||
<div><i class="fa fa-arrow-up-right-from-square" style="color:var(--fhc-light) !important"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="card-header">{{title}}</div>
|
||||
<div class="card-body">
|
||||
<a style="text-decoration:none" class="my-1 d-block" href="#">{{$p.t('profil','zeitwuensche')}}</a>
|
||||
<a style="text-decoration:none" class="my-1 d-block" href="#">{{$p.t('profil','lehrveranstaltungen')}}</a>
|
||||
<a style="text-decoration:none" class="my-1 d-block" href="#">{{$p.t('profil','zeitsperren')}}</a>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>`,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {CoreFilterCmpt} from "../../../components/filter/Filter.js";
|
||||
import Mailverteiler from "./ProfilComponents/Mailverteiler.js";
|
||||
import AusweisStatus from "./ProfilComponents/FhAusweisStatus.js";
|
||||
import QuickLinks from "./ProfilComponents/QuickLinks.js";
|
||||
import Adresse from "./ProfilComponents/Adresse.js";
|
||||
import Kontakt from "./ProfilComponents/Kontakt.js";
|
||||
import ProfilEmails from "./ProfilComponents/ProfilEmails.js";
|
||||
@@ -9,6 +8,8 @@ import RoleInformation from "./ProfilComponents/RoleInformation.js";
|
||||
import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
|
||||
import FetchProfilUpdates from "./ProfilComponents/FetchProfilUpdates.js";
|
||||
import EditProfil from "./ProfilModal/EditProfil.js";
|
||||
import QuickLinks from "./ProfilComponents/QuickLinks.js";
|
||||
import CalendarSync from "./ProfilComponents/CalendarSync.js";
|
||||
|
||||
import ApiProfilUpdate from '../../../api/factory/profilUpdate.js';
|
||||
import { dateFilter } from '../../../tabulator/filters/Dates.js';
|
||||
@@ -18,7 +19,6 @@ export default {
|
||||
CoreFilterCmpt,
|
||||
Mailverteiler,
|
||||
AusweisStatus,
|
||||
QuickLinks,
|
||||
Adresse,
|
||||
Kontakt,
|
||||
ProfilEmails,
|
||||
@@ -26,6 +26,8 @@ export default {
|
||||
ProfilInformation,
|
||||
FetchProfilUpdates,
|
||||
EditProfil,
|
||||
QuickLinks,
|
||||
CalendarSync,
|
||||
},
|
||||
inject: ["sortProfilUpdates", "collapseFunction", "language","isEditable"],
|
||||
data() {
|
||||
@@ -101,6 +103,7 @@ export default {
|
||||
props: {
|
||||
data: Object,
|
||||
editData: Object,
|
||||
calendarSyncUrls: Array,
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
@@ -240,6 +243,11 @@ export default {
|
||||
}
|
||||
};
|
||||
},
|
||||
quickLinks() {
|
||||
let quickLinks = [];
|
||||
//
|
||||
return quickLinks;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// preload phrasen
|
||||
@@ -265,15 +273,7 @@ export default {
|
||||
:value="JSON.parse(JSON.stringify(filteredEditData))" :titel="$p.t('profil','profilBearbeiten')"></edit-profil>
|
||||
<!-- ROW -->
|
||||
<div class="row">
|
||||
<!-- HIDDEN QUICK LINKS -->
|
||||
<div class="d-md-none col-12 ">
|
||||
<!--TODO: uncomment when implemented
|
||||
<div class="row py-2">
|
||||
<div class="col">
|
||||
<quick-links :title="$p.t('profil','quickLinks')" :mobile="true"></quick-links>
|
||||
</div>
|
||||
</div>-->
|
||||
|
||||
<!-- Bearbeiten Button -->
|
||||
<div v-if="isEditable" class="row ">
|
||||
<div class="col mb-3">
|
||||
@@ -403,12 +403,11 @@ export default {
|
||||
</div>
|
||||
<!-- START OF SIDE PANEL -->
|
||||
<div class="col-md-4 col-xxl-3 col-sm-12 text-break" >
|
||||
<!--TODO: uncomment when implemented
|
||||
<div class="row d-none d-md-block mb-3">
|
||||
<div v-if="quickLinks.length" class="row mb-4">
|
||||
<div class="col">
|
||||
<quick-links :title="$p.t('profil','quickLinks')"></quick-links>
|
||||
<quick-links :title="$p.t('profil/quickLinks')" :links="quickLinks" />
|
||||
</div>
|
||||
</div>-->
|
||||
</div>
|
||||
<!-- Bearbeiten Button -->
|
||||
<div class="row d-none d-md-block">
|
||||
<div class="col mb-3">
|
||||
@@ -434,13 +433,18 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
<!-- START OF THE SECOND ROW IN THE SIDE PANEL -->
|
||||
<div class="row">
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<!-- HIER SIND DIE MAILVERTEILER -->
|
||||
<mailverteiler :title="$p.t('profil','mailverteiler')" :data="data?.mailverteiler"></mailverteiler>
|
||||
</div>
|
||||
<!-- END OF THE SECOND ROW IN THE SIDE PANEL -->
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<calendar-sync :uid="$props.data.username" :calendarSyncUrls="$props.calendarSyncUrls"></calendar-sync>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END OF SIDE PANEL -->
|
||||
</div>
|
||||
<!-- END OF CONTAINER ROW-->
|
||||
|
||||
@@ -1,30 +1,28 @@
|
||||
import QuickLinks from "./ProfilComponents/QuickLinks.js";
|
||||
import Mailverteiler from "./ProfilComponents/Mailverteiler.js";
|
||||
import ProfilEmails from "./ProfilComponents/ProfilEmails.js";
|
||||
import RoleInformation from "./ProfilComponents/RoleInformation.js";
|
||||
import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
|
||||
import QuickLinks from "./ProfilComponents/QuickLinks.js";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
components: {
|
||||
QuickLinks,
|
||||
Mailverteiler,
|
||||
ProfilEmails,
|
||||
RoleInformation,
|
||||
ProfilInformation,
|
||||
QuickLinks,
|
||||
},
|
||||
props: ["data", "permissions"],
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
|
||||
props: ["data"],
|
||||
provide() {
|
||||
return {
|
||||
studiengang_kz: Vue.computed({ get: () => this.data.studiengang_kz }),
|
||||
}
|
||||
studiengang_kz: Vue.computed({
|
||||
get: () => this.data.studiengang_kz,
|
||||
}),
|
||||
};
|
||||
},
|
||||
|
||||
methods: {},
|
||||
|
||||
computed: {
|
||||
fotoStatus() {
|
||||
return this.data?.fotoStatus ?? null;
|
||||
@@ -45,66 +43,67 @@ export default {
|
||||
foto: this.data.foto,
|
||||
};
|
||||
},
|
||||
|
||||
personEmails() {
|
||||
return this.data?.emails ? this.data.emails : [];
|
||||
},
|
||||
|
||||
roleInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
geburtsdatum: {
|
||||
label: `${this.$p.t('profil','Geburtsdatum')}`,
|
||||
value: this.data.gebdatum
|
||||
label: `${this.$p.t("profil", "Geburtsdatum")}`,
|
||||
value: this.data.gebdatum,
|
||||
},
|
||||
geburtsort: {
|
||||
label: `${this.$p.t('profil','Geburtsort')}`,
|
||||
value: this.data.gebort
|
||||
label: `${this.$p.t("profil", "Geburtsort")}`,
|
||||
value: this.data.gebort,
|
||||
},
|
||||
personenkennzeichen: {
|
||||
label: `${this.$p.t('person','personenkennzeichen')}`,
|
||||
value: this.data.personenkennzeichen
|
||||
label: `${this.$p.t("person", "personenkennzeichen")}`,
|
||||
value: this.data.personenkennzeichen,
|
||||
},
|
||||
studiengang: {
|
||||
label: `${this.$p.t('lehre','studiengang')}`,
|
||||
value: this.data.studiengang
|
||||
label: `${this.$p.t("lehre", "studiengang")}`,
|
||||
value: this.data.studiengang,
|
||||
},
|
||||
semester: {
|
||||
label: `${this.$p.t('lehre','semester')}`,
|
||||
value: this.data.semester
|
||||
label: `${this.$p.t("lehre", "semester")}`,
|
||||
value: this.data.semester,
|
||||
},
|
||||
verband: {
|
||||
label: `${this.$p.t('lehre','lehrverband')}`,
|
||||
value: this.data.verband
|
||||
label: `${this.$p.t("lehre", "lehrverband")}`,
|
||||
value: this.data.verband,
|
||||
},
|
||||
gruppe: {
|
||||
label: `${this.$p.t('lehre','gruppe')}`,
|
||||
value: this.data.gruppe.trim()
|
||||
}
|
||||
label: `${this.$p.t("lehre", "gruppe")}`,
|
||||
value: this.data.gruppe.trim(),
|
||||
},
|
||||
};
|
||||
},
|
||||
quickLinks() {
|
||||
let quickLinks = [];
|
||||
if (this.$props.permissions && this.$props.permissions["basis/other_lv_plan"]) {
|
||||
quickLinks.push({
|
||||
icon: "fa-calendar-days",
|
||||
phrase: "lehre/stundenplan",
|
||||
action: () => {
|
||||
this.$router.push({
|
||||
name: "OtherLvPlan",
|
||||
params: { otherUid: this.$props.data.username },
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
return quickLinks;
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
},
|
||||
|
||||
template: /*html*/ `
|
||||
|
||||
<div class="container-fluid text-break fhc-form" >
|
||||
<!-- ROW -->
|
||||
<div class="row">
|
||||
<!-- HIDDEN QUICK LINKS -->
|
||||
<!-- uncomment when implemented
|
||||
<div class="d-md-none col-12 ">
|
||||
|
||||
<quick-links :title="$p.t('profil','quickLinks')" :mobile="true"></quick-links>
|
||||
|
||||
</div>-->
|
||||
<!-- END OF HIDDEN QUCK LINKS -->
|
||||
<!-- MAIN PANNEL -->
|
||||
<div class="col-sm-12 col-md-8 col-xxl-9 ">
|
||||
<!-- ROW WITH PROFIL IMAGE AND INFORMATION -->
|
||||
@@ -112,12 +111,18 @@ export default {
|
||||
<!-- ROW WITH THE PROFIL INFORMATION -->
|
||||
<div class="row mb-4">
|
||||
<!-- FIRST KAESTCHEN -->
|
||||
<div class="col-lg-12 col-xl-6 ">
|
||||
<div class="col-lg-12 col-xl-6 ">
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<profil-information :data="profilInformation" :title="$p.t('profil','studentIn')" :fotoStatus="fotoStatus"></profil-information>
|
||||
</div>
|
||||
</div>
|
||||
<!-- SECOND ROW OF FIRST COLUMN -->
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- START OF SECOND PROFIL INFORMATION COLUMN -->
|
||||
<!-- END OF PROFIL INFORMATION ROW -->
|
||||
<!-- INFORMATION CONTENT END -->
|
||||
@@ -145,17 +150,12 @@ export default {
|
||||
</div>
|
||||
<!-- START OF SIDE PANEL -->
|
||||
<div class="col-md-4 col-xxl-3 col-sm-12 text-break" >
|
||||
<!-- SRART OF QUICK LINKS IN THE SIDE PANEL -->
|
||||
<!-- START OF THE FIRDT ROW IN THE SIDE PANEL -->
|
||||
<!-- THESE QUCK LINKS ARE ONLY VISIBLE UNTIL VIEWPORT MD -->
|
||||
<!--TODO: uncomment when implemented
|
||||
<div class="row d-none d-md-block mb-3">
|
||||
<div class="col">
|
||||
|
||||
<quick-links :title="$p.t('profil','quickLinks')"></quick-links>
|
||||
|
||||
</div>
|
||||
</div>-->
|
||||
<!-- START OF THE FIRST ROW IN THE SIDE PANEL -->
|
||||
<div v-if="quickLinks.length" class="row mb-4">
|
||||
<div class="col">
|
||||
<quick-links :title="$p.t('profil/quickLinks')" :links="quickLinks" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- START OF THE SECOND ROW IN THE SIDE PANEL -->
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
|
||||
@@ -3,14 +3,12 @@ import VueDatePicker from '../../vueDatepicker.js.php';
|
||||
import ApiOrt from '../../../api/factory/ort.js'
|
||||
export const Raumsuche = {
|
||||
name: "Raumsuche",
|
||||
props: {
|
||||
|
||||
},
|
||||
components: {
|
||||
VueDatePicker,
|
||||
CoreFilterCmpt,
|
||||
InputNumber: primevue.inputnumber,
|
||||
},
|
||||
inject: ["isMobile"],
|
||||
data() {
|
||||
return {
|
||||
phrasenPromise: null,
|
||||
@@ -194,8 +192,9 @@ export const Raumsuche = {
|
||||
<h1 class="h3">{{$p.t('rauminfo/roomSearch')}}</h1>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-2">
|
||||
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-2">
|
||||
<VueDatePicker
|
||||
@contextmenu="(e) => {if (isMobile) {e.preventDefault();}}"
|
||||
:dark="isDarkMode"
|
||||
v-model="datum"
|
||||
:clearable="false"
|
||||
@@ -207,8 +206,9 @@ export const Raumsuche = {
|
||||
auto-apply>
|
||||
</VueDatePicker>
|
||||
</div>
|
||||
<div class="col-12 col-lg-1">
|
||||
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-1">
|
||||
<VueDatePicker
|
||||
@contextmenu="(e) => {if (isMobile) {e.preventDefault();}}"
|
||||
:dark="isDarkMode"
|
||||
v-model="von"
|
||||
:clearable="false"
|
||||
@@ -220,8 +220,9 @@ export const Raumsuche = {
|
||||
>
|
||||
</VueDatePicker>
|
||||
</div>
|
||||
<div class="col-12 col-lg-1">
|
||||
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-1">
|
||||
<VueDatePicker
|
||||
@contextmenu="(e) => {if (isMobile) {e.preventDefault();}}"
|
||||
:dark="isDarkMode"
|
||||
v-model="bis"
|
||||
:clearable="false"
|
||||
@@ -233,7 +234,7 @@ export const Raumsuche = {
|
||||
</VueDatePicker>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-3">
|
||||
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-3">
|
||||
<select ref="raumtyp" id="raumtypSelect" v-model="selectedType" class="form-select"
|
||||
:aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setRoute($event.target.value)">
|
||||
<option :key="defaultType" selected :value="defaultType">{{defaultType.beschreibung}}</option>
|
||||
@@ -242,7 +243,7 @@ export const Raumsuche = {
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-12 col-lg-3">
|
||||
<div :class="{'pb-2': isMobile}" class="col-12 col-lg-3">
|
||||
<InputNumber v-model="anzahl"
|
||||
:prefix="$p.t('rauminfo/minCapacity') + ': '"
|
||||
inputId="anzahlInput" :min="1" :max="1000"
|
||||
|
||||
@@ -31,7 +31,7 @@ export default {
|
||||
this.event.lektor.slice(0, 3).map(lektor => lektor.kurzbz).join("\n")
|
||||
+ "\n" + this.$p.t('lehre/weitereLektoren', [this.event.lektor.length - 3])
|
||||
].join(": "));
|
||||
} else {;
|
||||
} else {
|
||||
tooltipArray.push([
|
||||
this.$p.t('lehre/lektor'),
|
||||
this.event.lektor.map(lektor => lektor.kurzbz).join("\n")
|
||||
|
||||
@@ -142,7 +142,6 @@ export default {
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<lv-menu :containerStyles="['p-0']" :rowStyles="['m-0']" v-if="lvMenu.length" :menu="lvMenu" />
|
||||
<lv-menu v-if="lvMenu.length && $route.name === 'MyLvPlan'" :containerStyles="['p-0']" :rowStyles="['m-0']" :menu="lvMenu" />
|
||||
</div>`,
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import LvUebersicht from "../Mylv/LvUebersicht.js";
|
||||
|
||||
import ApiCisStudium from '../../../api/factory/cis/studium.js';
|
||||
|
||||
export default {
|
||||
data(){
|
||||
@@ -26,6 +27,7 @@ export default {
|
||||
|
||||
}
|
||||
},
|
||||
name: "OverviewStudiengaenge",
|
||||
components: {
|
||||
LvUebersicht,
|
||||
},
|
||||
@@ -88,6 +90,27 @@ export default {
|
||||
studienordnung.selectedIndex = newSelectIndex;
|
||||
this.changeSelectedStudienPlan(studienordnung.value);
|
||||
},
|
||||
onStudiensemesterChange(event) {
|
||||
const value = event.target.value;
|
||||
this.setHash(value);
|
||||
this.changeSelectedStudienSemester(value);
|
||||
|
||||
},
|
||||
onStudiengangChange(event) {
|
||||
const value = event.target.value;
|
||||
this.setHash(value);
|
||||
this.changeSelectedStudienGang(value);
|
||||
},
|
||||
onSemesterChange(event) {
|
||||
const value = event.target.value;
|
||||
this.setHash(value);
|
||||
this.changeSelectedSemester(value);
|
||||
},
|
||||
onStudienordnungChange(event) {
|
||||
const value = event.target.value;
|
||||
this.setHash(value);
|
||||
this.changeSelectedStudienPlan(value);
|
||||
},
|
||||
|
||||
storeDataToLocalStorage(key,value){
|
||||
localStorage.setItem(key, value);
|
||||
@@ -97,28 +120,32 @@ export default {
|
||||
return value;
|
||||
},
|
||||
changeSelectedStudienSemester(studiensemester_kurzbz) {
|
||||
this.$fhcApi.factory.studium.getAllStudienSemester(studiensemester_kurzbz, this.selectedStudiengang, this.selectedSemester, this.selectedStudienordnung)
|
||||
return this.$api
|
||||
.call(ApiCisStudium.getAllStudienSemester(studiensemester_kurzbz, this.selectedStudiengang, this.selectedSemester, this.selectedStudienordnung))
|
||||
.then(data => data.data)
|
||||
.then(res => {
|
||||
this.extractPropertyValues(res);
|
||||
})
|
||||
},
|
||||
changeSelectedStudienGang(studiengang_kz) {
|
||||
this.$fhcApi.factory.studium.getAllStudienSemester(this.selectedStudiensemester, studiengang_kz, this.selectedSemester, this.selectedStudienordnung)
|
||||
return this.$api
|
||||
.call(ApiCisStudium.getAllStudienSemester(this.selectedStudiensemester, studiengang_kz, this.selectedSemester, this.selectedStudienordnung))
|
||||
.then(data => data.data)
|
||||
.then(res => {
|
||||
this.extractPropertyValues(res);
|
||||
})
|
||||
},
|
||||
changeSelectedSemester(semester) {
|
||||
this.$fhcApi.factory.studium.getAllStudienSemester(this.selectedStudiensemester, this.selectedStudiengang, semester, this.selectedStudienordnung)
|
||||
return this.$api
|
||||
.call(ApiCisStudium.getAllStudienSemester(this.selectedStudiensemester, this.selectedStudiengang, semester, this.selectedStudienordnung))
|
||||
.then(data => data.data)
|
||||
.then(res => {
|
||||
this.extractPropertyValues(res);
|
||||
})
|
||||
},
|
||||
changeSelectedStudienPlan(studienplan_id) {
|
||||
this.$fhcApi.factory.studium.getAllStudienSemester(this.selectedStudiensemester, this.selectedStudiengang, this.selectedSemester, studienplan_id)
|
||||
return this.$api
|
||||
.call(ApiCisStudium.getAllStudienSemester(this.selectedStudiensemester, this.selectedStudiengang, this.selectedSemester, studienplan_id))
|
||||
.then(data => data.data)
|
||||
.then(res => {
|
||||
this.extractPropertyValues(res);
|
||||
@@ -160,6 +187,7 @@ export default {
|
||||
this.studiengaenge = studiengang.all;
|
||||
this.selectedStudiengang = studiengang.preselected?.studiengang_kz;
|
||||
|
||||
|
||||
this.semester = semester.all;
|
||||
this.selectedSemester = semester?.preselected;
|
||||
|
||||
@@ -195,7 +223,11 @@ export default {
|
||||
},
|
||||
studiengangTitel(studiengang) {
|
||||
if (!studiengang) return "";
|
||||
return `${studiengang?.kurzbzlang} (${studiengang?.bezeichnung})`;
|
||||
if(this.isGermanLanguage){
|
||||
return `${studiengang?.kurzbzlang} (${studiengang?.bezeichnung})`;
|
||||
}else{
|
||||
return `${studiengang?.kurzbzlang} (${studiengang?.english})`;
|
||||
}
|
||||
},
|
||||
studiensemesterTitel(studiensemester){
|
||||
if (!studiensemester) return "";
|
||||
@@ -213,9 +245,12 @@ export default {
|
||||
},
|
||||
|
||||
computed:{
|
||||
isGermanLanguage(){
|
||||
return this.$p.user_language.value == "German"
|
||||
},
|
||||
selectedLehrveranstaltungTitel(){
|
||||
const studiengang = this.studiengaenge.find((studiengang) => studiengang.studiengang_kz == this.selectedStudiengang);
|
||||
return `${this.selectedLehrveranstaltung?.bezeichnung} ${this.selectedLehrveranstaltung?.lehrform_kurzbz} / ${studiengang.kurzbzlang}-${this.selectedSemester} ${this.selectedLehrveranstaltung?.orgform_kurzbz} (${this.selectedStudiensemester})`;
|
||||
return `${this.isGermanLanguage ? this.selectedLehrveranstaltung?.bezeichnung : this.selectedLehrveranstaltung?.bezeichnung_english} ${this.selectedLehrveranstaltung?.lehrform_kurzbz} / ${studiengang.kurzbzlang}-${this.selectedSemester} ${this.selectedLehrveranstaltung?.orgform_kurzbz} (${this.selectedStudiensemester})`;
|
||||
},
|
||||
computedStudienOrdnung(){
|
||||
if(!this.studienOrdnung) return null;
|
||||
@@ -232,14 +267,14 @@ export default {
|
||||
let result = [];
|
||||
Object.entries(this.computedStudienOrdnung).forEach(([key,value])=>{
|
||||
result.push({
|
||||
bezeichnung: `Studienordnung: ${key}`,
|
||||
bezeichnung: `${this.$p.t('studium', 'studienordnung') }: ${key}`,
|
||||
disabled: true,
|
||||
});
|
||||
value.forEach((studienplan)=>{
|
||||
result.push({
|
||||
studienplan:studienplan,
|
||||
diabled: false,
|
||||
bezeichnung: `${studienplan?.bezeichnung}-${studienplan?.orgform_kurzbz} ( ${studienplan?.orgform_bezeichnung}, ${studienplan?.sprache} )`
|
||||
bezeichnung: `${studienplan?.bezeichnung}-${studienplan?.orgform_kurzbz} ( ${this.isGermanLanguage ? studienplan?.orgform_bezeichnung : studienplan?.orgform_bezeichnung_english}, ${studienplan?.sprache} )`
|
||||
|
||||
});
|
||||
})
|
||||
@@ -256,22 +291,23 @@ export default {
|
||||
const studienordnung = JSON.parse(this.getDataFromLocalStorage("studienordnung")) ?? undefined;
|
||||
|
||||
// only fetch default data if no data is stored in the local storage
|
||||
|
||||
this.$fhcApi.factory.studium.getAllStudienSemester(studiensemester, studiengang, semester, studienordnung)
|
||||
.then(data => data.data)
|
||||
.then(res => {
|
||||
this.extractPropertyValues(res);
|
||||
})
|
||||
|
||||
this.$api
|
||||
.call(ApiCisStudium.getAllStudienSemester(studiensemester, studiengang, semester, studienordnung))
|
||||
.then(data => data.data)
|
||||
.then(res => {
|
||||
this.extractPropertyValues(res);
|
||||
})
|
||||
|
||||
},
|
||||
template: `
|
||||
template: /*html*/`
|
||||
<div>
|
||||
<h2>Studium</h2>
|
||||
<h2>{{$p.t('studium','studium')}}</h2>
|
||||
<hr>
|
||||
<lv-uebersicht ref="lvUebersicht" :titel="selectedLehrveranstaltungTitel" :event="selectedLehrveranstaltung" :studiensemester="selectedStudiensemester" v-if="selectedLehrveranstaltung">
|
||||
<template #content>
|
||||
<div v-if="Array.isArray(selectedLehrveranstaltung.lektoren) && selectedLehrveranstaltung.lektoren.length>0" class="mb-4">
|
||||
<h4>Lektoren:</h4>
|
||||
<h4>{{$p.t('studium','lektoren')}}:</h4>
|
||||
<a :href="'mailto:'+lektor?.email" class="fhc-link-color mx-2" v-for="lektor in selectedLehrveranstaltung.lektoren">{{lektor.name}}</a>
|
||||
</div>
|
||||
<h4>Menu:</h4>
|
||||
@@ -279,13 +315,13 @@ export default {
|
||||
</lv-uebersicht>
|
||||
<div class="lvOptions">
|
||||
<div>
|
||||
<h6>Studiensemester:</h6>
|
||||
<h6>{{$p.t('studium','studiensemester')}}:</h6>
|
||||
<div class="input-group">
|
||||
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeStudiensemester(1)" :aria-label="$p.t('global','previous')" :title="$p.t('global','previous')">
|
||||
<i class="fa fa-caret-left" aria-hidden="true"></i>
|
||||
</button>
|
||||
<select ref="studiensemester" v-model="selectedStudiensemester" class="form-select" :aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setHash($event.target.value)">
|
||||
<option v-for="semester in studienSemester" @click="changeSelectedStudienSemester(semester.studiensemester_kurzbz)" :key="semester" :value="semester.studiensemester_kurzbz">{{studiensemesterTitel(semester.studiensemester_kurzbz) }}</option>
|
||||
<select ref="studiensemester" v-model="selectedStudiensemester" @change="onStudiensemesterChange" class="form-select" :aria-label="$p.t('global/studiensemester_auswaehlen')">
|
||||
<option v-for="semester in studienSemester" :key="semester" :value="semester.studiensemester_kurzbz">{{studiensemesterTitel(semester.studiensemester_kurzbz) }}</option>
|
||||
</select>
|
||||
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeStudiensemester(-1)" :aria-label="$p.t('global','next')" :title="$p.t('global','next')">
|
||||
<i class="fa fa-caret-right" aria-hidden="true"></i>
|
||||
@@ -294,13 +330,13 @@ export default {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h6>Studiengang:</h6>
|
||||
<h6>{{$p.t('lehre','studiengang')}}:</h6>
|
||||
<div class="input-group">
|
||||
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeStudiengang(-1)" :aria-label="$p.t('global','previous')" :title="$p.t('global','previous')">
|
||||
<i class="fa fa-caret-left" aria-hidden="true"></i>
|
||||
</button>
|
||||
<select ref="studiengaenge" v-model="selectedStudiengang" class="form-select" :aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setHash($event.target.value)">
|
||||
<option v-for="studiengang in studiengaenge" @click="changeSelectedStudienGang(studiengang.studiengang_kz)" :key="studiengang.studiengang_kz" :value="studiengang.studiengang_kz" >{{studiengangTitel(studiengang)}}</option>
|
||||
<select ref="studiengaenge" v-model="selectedStudiengang" class="form-select" @change="onStudiengangChange" :aria-label="$p.t('global/studiensemester_auswaehlen')">
|
||||
<option v-for="studiengang in studiengaenge" :key="studiengang.studiengang_kz" :value="studiengang.studiengang_kz" >{{studiengangTitel(studiengang)}}</option>
|
||||
</select>
|
||||
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeStudiengang(1)" :aria-label="$p.t('global','next')" :title="$p.t('global','next')">
|
||||
<i class="fa fa-caret-right" aria-hidden="true"></i>
|
||||
@@ -309,13 +345,13 @@ export default {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h6>Semester:</h6>
|
||||
<h6>{{$p.t('lehre','semester')}}:</h6>
|
||||
<div class="input-group">
|
||||
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeSemester(-1)" :aria-label="$p.t('global','previous')" :title="$p.t('global','previous')">
|
||||
<i class="fa fa-caret-left" aria-hidden="true"></i>
|
||||
</button>
|
||||
<select ref="semester" v-model="selectedSemester" class="form-select" :aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setHash($event.target.value)">
|
||||
<option v-for="sem in semester" @click="changeSelectedSemester(sem)" :key="semester" :value="sem">{{sem}}. Semester</option>
|
||||
<select ref="semester" v-model="selectedSemester" class="form-select" @change="onSemesterChange" :aria-label="$p.t('global/studiensemester_auswaehlen')">
|
||||
<option v-for="sem in semester" :key="sem" :value="sem">{{sem}}. Semester</option>
|
||||
</select>
|
||||
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeSemester(1)" :aria-label="$p.t('global','next')" :title="$p.t('global','next')">
|
||||
<i class="fa fa-caret-right" aria-hidden="true"></i>
|
||||
@@ -324,13 +360,13 @@ export default {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h6>Studienordnung:</h6>
|
||||
<h6>{{$p.t('studium','studienordnung')}}:</h6>
|
||||
<div class="input-group">
|
||||
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeStudienordnung(-1)" :aria-label="$p.t('global','previous')" :title="$p.t('global','previous')">
|
||||
<i class="fa fa-caret-left" aria-hidden="true"></i>
|
||||
</button>
|
||||
<select ref="studienordnung" v-model="selectedStudienordnung" class="form-select" :aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setHash($event.target.value)">
|
||||
<option v-for="ordnung in computedStudienOrdnungSelectValues" :disabled="ordnung.disabled" @click="changeSelectedStudienPlan(ordnung?.studienplan?.studienplan_id)" :key="ordnung?.studienplan?.bezeichnung " :value="ordnung?.studienplan?.studienplan_id">{{ordnung.bezeichnung}}</option>
|
||||
<select ref="studienordnung" v-model="selectedStudienordnung" class="form-select" @change="onStudienordnungChange" :aria-label="$p.t('global/studiensemester_auswaehlen')">
|
||||
<option v-for="ordnung in computedStudienOrdnungSelectValues" :disabled="ordnung.disabled" :key="ordnung?.studienplan?.studienplan_id" :value="ordnung?.studienplan?.studienplan_id">{{ordnung.bezeichnung}}</option>
|
||||
</select>
|
||||
<button class="btn btn-outline-secondary" type="button" :disabled="false" @click="changeStudienordnung(1)" :aria-label="$p.t('global','next')" :title="$p.t('global','next')">
|
||||
<i class="fa fa-caret-right" aria-hidden="true"></i>
|
||||
@@ -345,13 +381,13 @@ export default {
|
||||
<template v-for="lehrveranstaltung in lehrveranstaltungen" :key="lehrveranstaltung.lehrveranstaltung_id">
|
||||
<div class="card" v-if="Array.isArray(lehrveranstaltung.lehrveranstaltungen) && lehrveranstaltung.lehrveranstaltungen.length >0" >
|
||||
<div class="card-header">
|
||||
<h5 class=" card-title">{{lehrveranstaltung.bezeichnung}}</h5>
|
||||
<h5 class=" card-title">{{isGermanLanguage == 'German' ? lehrveranstaltung.bezeichnung : lehrveranstaltung.bezeichnung_english }}</h5>
|
||||
<h6 class=" card-subtitle">{{lehrveranstaltung.lehrform_kurzbz}}</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="d-flex list-group-item" v-for="lv in lehrveranstaltung.lehrveranstaltungen">
|
||||
<a class="fhc-link-color d-block me-auto" href="#" @click="openLvUebersicht(lv)">{{lv.bezeichnung}}</a>
|
||||
<a class="fhc-link-color d-block me-auto" href="#" @click="openLvUebersicht(lv)">{{isGermanLanguage == 'German' ? lv.bezeichnung : lv.bezeichnung_english}}</a>
|
||||
<p>{{lv.lehrform_kurzbz}}</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -4,7 +4,6 @@ import DashboardAdminWidgets from "./Admin/Widgets.js";
|
||||
import DashboardAdminPresets from "./Admin/Presets.js";
|
||||
|
||||
import ApiDashboardBoard from "../../api/factory/dashboard/board.js";
|
||||
import ApiDashboardWidget from "../../api/factory/dashboard/widget.js";
|
||||
|
||||
export default {
|
||||
name: 'DashboardAdmin',
|
||||
@@ -16,7 +15,7 @@ export default {
|
||||
provide() {
|
||||
return {
|
||||
adminMode: true,
|
||||
widgetsSetup: Vue.computed(() => this.dashboards[this.current] ? this.dashboards[this.current].widgetSetup : null)
|
||||
widgetsSetup: Vue.computed(() => this.dashboard ? this.dashboard.widgetSetup : null)
|
||||
};
|
||||
},
|
||||
data() {
|
||||
@@ -34,33 +33,32 @@ export default {
|
||||
methods: {
|
||||
dashboardAdd() {
|
||||
let _name = '';
|
||||
BsPrompt.popup('New Dashboard name').then(
|
||||
name => {
|
||||
_name = name;
|
||||
BsPrompt
|
||||
.popup('New Dashboard name')
|
||||
.then(dashboard_kurzbz => {
|
||||
const params = {
|
||||
dashboard_kurzbz: name
|
||||
dashboard_kurzbz
|
||||
};
|
||||
return this.$api
|
||||
.call(ApiDashboardBoard.add(params))
|
||||
.then(response =>{
|
||||
.then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||
|
||||
let newDashboard = {
|
||||
dashboard_id: response.data,
|
||||
dashboard_kurzbz: _name,
|
||||
dashboard_kurzbz,
|
||||
beschreibung: ''
|
||||
};
|
||||
this.dashboards.push(newDashboard);
|
||||
this.current = newDashboard.dashboard_id;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
});
|
||||
});
|
||||
},
|
||||
dashboardUpdate(dashboard) {
|
||||
return this.$api
|
||||
this.$api
|
||||
.call(ApiDashboardBoard.update(dashboard))
|
||||
.then(response =>{
|
||||
|
||||
.then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
|
||||
|
||||
let old = this.dashboards.find(el => el.dashboard_id == dashboard.dashboard_id);
|
||||
@@ -70,73 +68,122 @@ export default {
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
dashboardDelete(dashboard_id) {
|
||||
return this.$api
|
||||
this.$api
|
||||
.call(ApiDashboardBoard.delete(dashboard_id))
|
||||
.then(response => {
|
||||
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
|
||||
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError)
|
||||
.finally(() => {
|
||||
this.current = -1;
|
||||
this.dashboards = this.dashboards.filter(el => el.dashboard_id != dashboard_id);
|
||||
});
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
assignWidgets(widgets) {
|
||||
this.widgets = widgets;
|
||||
/*while (this.widgets.length)
|
||||
this.widgets.pop();
|
||||
for (var i in widgets)
|
||||
this.widgets.push(widgets[i]);*/
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$api
|
||||
.call(ApiDashboardBoard.list())
|
||||
.then(result => {
|
||||
this.dashboards = result.data.retval;
|
||||
for (const dashboard of this.dashboards) {
|
||||
this.$api
|
||||
.call(ApiDashboardWidget.list(dashboard.dashboard_id))
|
||||
.then(res => {
|
||||
dashboard.widgetSetup = res.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
}
|
||||
this.dashboards = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
template: `<div class="dashboard-admin">
|
||||
|
||||
template: /* html */`
|
||||
<div class="dashboard-admin">
|
||||
<div class="input-group">
|
||||
<label for="dashboard-select" class="input-group-text">Dashboard:</label>
|
||||
<select id="dashboard-select" class="form-select" v-model="current">
|
||||
<option v-for="dashboard in dashboards" :key="dashboard.dashboard_id" :value="dashboard.dashboard_id">{{dashboard.dashboard_kurzbz}}</option>
|
||||
<label for="dashboard-select" class="input-group-text">
|
||||
Dashboard:
|
||||
</label>
|
||||
<select id="dashboard-select" v-model="current" class="form-select">
|
||||
<option
|
||||
v-for="dashboard in dashboards"
|
||||
:key="dashboard.dashboard_id"
|
||||
:value="dashboard.dashboard_id"
|
||||
>{{ dashboard.dashboard_kurzbz }}</option>
|
||||
</select>
|
||||
<button class="btn btn-outline-secondary" type="button" @click="dashboardAdd"><i class="fa-solid fa-plus"></i></button>
|
||||
<button
|
||||
class="btn btn-outline-secondary"
|
||||
type="button"
|
||||
@click="dashboardAdd"
|
||||
><i class="fa-solid fa-plus"></i></button>
|
||||
</div>
|
||||
|
||||
<div v-if="dashboard">
|
||||
<ul class="nav nav-tabs mt-3" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="edit-tab" data-bs-toggle="tab" data-bs-target="#edit" type="button" role="tab" aria-controls="edit" aria-selected="false">{{this.$p.t('ui', 'bearbeiten')}}</button>
|
||||
<button
|
||||
id="edit-tab"
|
||||
class="nav-link"
|
||||
data-bs-toggle="tab"
|
||||
data-bs-target="#edit"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="edit"
|
||||
aria-selected="false"
|
||||
>{{ this.$p.t('ui', 'bearbeiten') }}</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="widgets-tab" data-bs-toggle="tab" data-bs-target="#widgets" type="button" role="tab" aria-controls="widgets" aria-selected="true">Widgets</button>
|
||||
<button
|
||||
id="widgets-tab"
|
||||
class="nav-link active"
|
||||
data-bs-toggle="tab"
|
||||
data-bs-target="#widgets"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="widgets"
|
||||
aria-selected="true"
|
||||
>Widgets</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="presets-tab" data-bs-toggle="tab" data-bs-target="#presets" type="button" role="tab" aria-controls="presets" aria-selected="false">Presets</button>
|
||||
<button
|
||||
class="nav-link"
|
||||
id="presets-tab"
|
||||
data-bs-toggle="tab"
|
||||
data-bs-target="#presets"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="presets"
|
||||
aria-selected="false"
|
||||
>Presets</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content pt-3">
|
||||
<div class="tab-pane fade" id="edit" role="tabpanel" aria-labelledby="edit-tab">
|
||||
<dashboard-admin-edit v-bind="dashboard" :key="dashboard.dashboard_id" @change="dashboardUpdate($event)" @delete="dashboardDelete($event)"></dashboard-admin-edit>
|
||||
<div
|
||||
id="edit"
|
||||
class="tab-pane fade"
|
||||
role="tabpanel"
|
||||
aria-labelledby="edit-tab"
|
||||
>
|
||||
<dashboard-admin-edit
|
||||
v-bind="dashboard"
|
||||
@change="dashboardUpdate($event)"
|
||||
@delete="dashboardDelete($event)"
|
||||
></dashboard-admin-edit>
|
||||
</div>
|
||||
<div class="tab-pane fade show active" id="widgets" role="tabpanel" aria-labelledby="widgets-tab">
|
||||
<dashboard-admin-widgets :key="dashboard.dashboard_id" :dashboard_id="dashboard.dashboard_id" :widgets="widgets" @assign-widgets="assignWidgets"></dashboard-admin-widgets>
|
||||
<div
|
||||
id="widgets"
|
||||
class="tab-pane fade show active"
|
||||
role="tabpanel"
|
||||
aria-labelledby="widgets-tab"
|
||||
>
|
||||
<dashboard-admin-widgets
|
||||
:dashboard_id="dashboard.dashboard_id"
|
||||
:widgets="widgets"
|
||||
@assign-widgets="assignWidgets"
|
||||
></dashboard-admin-widgets>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="presets" role="tabpanel" aria-labelledby="presets-tab">
|
||||
<dashboard-admin-presets :dashboard="dashboard.dashboard_kurzbz" :widgets="widgets"></dashboard-admin-presets>
|
||||
<div
|
||||
id="presets"
|
||||
class="tab-pane fade"
|
||||
role="tabpanel"
|
||||
aria-labelledby="presets-tab"
|
||||
>
|
||||
<dashboard-admin-presets
|
||||
:dashboard="dashboard.dashboard_kurzbz"
|
||||
:widgets="widgets"
|
||||
></dashboard-admin-presets>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import BsConfirm from '../../Bootstrap/Confirm.js';
|
||||
|
||||
export default {
|
||||
emits: [
|
||||
"change",
|
||||
"delete"
|
||||
],
|
||||
props: {
|
||||
dashboard_id: Number,
|
||||
dashboard_kurzbz: String,
|
||||
beschreibung: String
|
||||
},
|
||||
emits: [
|
||||
"change",
|
||||
"delete"
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
kurzbz: this.dashboard_kurzbz,
|
||||
@@ -18,22 +18,43 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
sendDelete() {
|
||||
BsConfirm.popup(this.$p.t('ui', 'confirm_delete') + " " + this.$p.t('ui', 'deleteInfo'))
|
||||
.then(() => this.$emit('delete', this.dashboard_id)).catch();
|
||||
BsConfirm
|
||||
.popup(this.$p.t('ui', 'confirm_delete') + " " + this.$p.t('ui', 'deleteInfo'))
|
||||
.then(() => this.$emit('delete', this.dashboard_id))
|
||||
.catch();
|
||||
}
|
||||
},
|
||||
template: `<div class="dashboard-admin-edit px-3">
|
||||
template: /* html */`
|
||||
<div class="dashboard-admin-edit px-3">
|
||||
<div class="mb-3">
|
||||
<label for="dashboard-admin-edit-kurzbz">Kurz Bezeichnung</label>
|
||||
<input id="dashboard-admin-edit-kurzbz" type="text" class="form-control" v-model="kurzbz">
|
||||
<label for="dashboard-admin-edit-kurzbz">{{ $p.t('dashboard/kurzbz') }}</label>
|
||||
<input
|
||||
id="dashboard-admin-edit-kurzbz"
|
||||
v-model="kurzbz"
|
||||
type="text"
|
||||
class="form-control"
|
||||
>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="dashboard-admin-edit-beschreibung">Beschreibung</label>
|
||||
<textarea id="dashboard-admin-edit-beschreibung" class="form-control" v-model="desc"></textarea>
|
||||
<label for="dashboard-admin-edit-beschreibung">{{ $p.t('global/beschreibung') }}</label>
|
||||
<textarea
|
||||
id="dashboard-admin-edit-beschreibung"
|
||||
v-model="desc"
|
||||
class="form-control"
|
||||
></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-danger" @click="sendDelete">{{this.$p.t('ui', 'loeschen')}}</button>
|
||||
<button class="btn btn-primary" @click="$emit('change', {dashboard_id,dashboard_kurzbz:kurzbz,beschreibung:desc})">{{this.$p.t('ui', 'btnAktualisieren')}}</button>
|
||||
<button class="btn btn-danger" @click="sendDelete">
|
||||
{{ this.$p.t('ui', 'loeschen') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="$emit('change', {
|
||||
dashboard_id,
|
||||
dashboard_kurzbz: kurzbz,
|
||||
beschreibung: desc
|
||||
})"
|
||||
>{{ this.$p.t('ui', 'btnAktualisieren') }}</button>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
|
||||
@@ -12,18 +12,27 @@ export default {
|
||||
dashboard: String,
|
||||
widgets: Array
|
||||
},
|
||||
data: () => ({
|
||||
funktionen: {},
|
||||
sections: [],
|
||||
tmpLoading: ''
|
||||
}),
|
||||
data() {
|
||||
return {
|
||||
funktionen: {},
|
||||
sections: [],
|
||||
selectedFunktionen: [],
|
||||
abortController: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
pickerWidgets() {
|
||||
return this.widgets.filter(widget => widget.allowed);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
dashboard() {
|
||||
this.loadSections();
|
||||
this.loadFunktionen();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
widgetAdd(section_name, widget) {
|
||||
widgetAdd(widget, section_name) {
|
||||
this.$refs.widgetpicker.getWidget().then(widget_id => {
|
||||
widget.widget = widget_id;
|
||||
widget.id = 'loading_' + String((new Date()).valueOf());
|
||||
@@ -64,22 +73,26 @@ export default {
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
widgetUpdate(section_name, payload) {
|
||||
payload = payload[section_name];
|
||||
widgetUpdate(payload, section_name) {
|
||||
for (var k in payload) {
|
||||
const section = this.sections.find(section => section.name == section_name);
|
||||
for (var wid in section.widgets) {
|
||||
if (section.widgets[wid].id == k) {
|
||||
payload[k] = ObjectUtils.mergeDeep(section.widgets[wid], payload[k]);
|
||||
// NOTE(chris): remove internal props
|
||||
for (var prop of ['_x', '_y', '_w', '_h', 'index', 'id'])
|
||||
for (var prop of ['_x', '_y', '_w', '_h', 'index', 'id', 'custom'])
|
||||
if (payload[k][prop])
|
||||
delete payload[k][prop];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (payload[k].place) {
|
||||
Object.values(payload[k].place).forEach(place => {
|
||||
if (place.pinned === false)
|
||||
delete place.pinned;
|
||||
});
|
||||
}
|
||||
payload[k].widgetid = k;
|
||||
delete payload[k].custom;
|
||||
}
|
||||
this.$api
|
||||
.call(Object.entries(payload).map(([key, widget]) => [
|
||||
@@ -106,7 +119,7 @@ export default {
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
widgetRemove(section_name, id) {
|
||||
widgetRemove(id, section_name) {
|
||||
const params = {
|
||||
db: this.dashboard,
|
||||
funktion_kurzbz: section_name,
|
||||
@@ -122,21 +135,22 @@ export default {
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
loadSections(evt) {
|
||||
let funktionen = Array.from(evt.target.querySelectorAll("option:checked"),e=>e.value);
|
||||
this.sections = [];
|
||||
this.tmpLoading = funktionen.join('###');
|
||||
|
||||
loadSections() {
|
||||
const params = {
|
||||
db: this.dashboard,
|
||||
funktionen
|
||||
funktionen: this.selectedFunktionen
|
||||
};
|
||||
|
||||
if (this.abortController)
|
||||
this.abortController.abort();
|
||||
this.abortController = new AbortController();
|
||||
const signal = this.abortController.signal;
|
||||
|
||||
this.sections = [];
|
||||
|
||||
return this.$api
|
||||
.call(ApiDashboardPreset.getBatch(params))
|
||||
.call(ApiDashboardPreset.getBatch(params), { signal })
|
||||
.then(result => {
|
||||
if (this.tmpLoading !== funktionen.join('###'))
|
||||
return; // NOTE(chris): prevent race condition
|
||||
for (var section in result.data) {
|
||||
let widgets = [];
|
||||
for (var wid in result.data[section]) {
|
||||
@@ -151,7 +165,6 @@ export default {
|
||||
}
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
|
||||
},
|
||||
loadFunktionen() {
|
||||
this.$api
|
||||
@@ -165,17 +178,17 @@ export default {
|
||||
created() {
|
||||
this.loadFunktionen();
|
||||
},
|
||||
watch: {
|
||||
dashboard() {
|
||||
// TODO(chris): this should be done without a watcher
|
||||
this.loadSections({target:this.$refs.funktionenList});
|
||||
this.loadFunktionen();
|
||||
}
|
||||
},
|
||||
template: `<div class="dashboard-admin-presets">
|
||||
template: /* html */`
|
||||
<div class="dashboard-admin-presets">
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<select ref="funktionenList" style="height:30em" class="form-control" multiple @input="loadSections">
|
||||
<select
|
||||
v-model="selectedFunktionen"
|
||||
class="form-control"
|
||||
style="height:30em"
|
||||
multiple
|
||||
@change="loadSections"
|
||||
>
|
||||
<option
|
||||
v-for="funktion in funktionen"
|
||||
:key="funktion.funktion_kurzbz"
|
||||
@@ -185,9 +198,20 @@ export default {
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<dashboard-section v-for="section in sections" :key="section.name" :name="section.name" :widgets="section.widgets" @widget-add="widgetAdd" @widget-update="widgetUpdate" @widget-remove="widgetRemove"></dashboard-section>
|
||||
<dashboard-section
|
||||
v-for="section in sections"
|
||||
:key="section.name"
|
||||
:name="section.name"
|
||||
:widgets="section.widgets"
|
||||
@widget-add="widgetAdd"
|
||||
@widget-update="widgetUpdate"
|
||||
@widget-remove="widgetRemove"
|
||||
></dashboard-section>
|
||||
</div>
|
||||
</div>
|
||||
<dashboard-widget-picker ref="widgetpicker" :widgets="pickerWidgets"></dashboard-widget-picker>
|
||||
<dashboard-widget-picker
|
||||
ref="widgetpicker"
|
||||
:widgets="pickerWidgets"
|
||||
></dashboard-widget-picker>
|
||||
</div>`
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import ApiDashboardWidget from "../../../api/factory/dashboard/widget.js";
|
||||
|
||||
export default {
|
||||
emits: [
|
||||
"change",
|
||||
"assignWidgets"
|
||||
],
|
||||
props: {
|
||||
dashboard_id: Number,
|
||||
widgets: Array
|
||||
},
|
||||
emits: [
|
||||
"change",
|
||||
"assignWidgets"
|
||||
],
|
||||
methods: {
|
||||
sendChange(widget_id) {
|
||||
let allow = !this.widgets.find(el => el.widget_id == widget_id).allowed;
|
||||
@@ -29,11 +29,27 @@ export default {
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
template: `
|
||||
template: /* html */`
|
||||
<div class="dashboard-admin-widgets">
|
||||
<div v-for="widget in widgets" :key="widget.widget_id" class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" :id="'dashboard-admin-widgets-' + widget.widget_id" v-model="widget.allowed" @input.prevent="sendChange(widget.widget_id)">
|
||||
<label class="form-check-label" :for="'dashboard-admin-widgets-' + widget.widget_id">{{(widget.setup && widget.setup.name) || widget.widget_kurzbz}}</label>
|
||||
<div
|
||||
v-for="widget in widgets"
|
||||
:key="widget.widget_id"
|
||||
class="form-check form-switch"
|
||||
>
|
||||
<input
|
||||
:id="'dashboard-admin-widgets-' + widget.widget_id"
|
||||
v-model="widget.allowed"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
@input.prevent="sendChange(widget.widget_id)"
|
||||
>
|
||||
<label
|
||||
class="form-check-label"
|
||||
:for="'dashboard-admin-widgets-' + widget.widget_id"
|
||||
>
|
||||
{{ (widget.setup && widget.setup.name) || widget.widget_kurzbz }}
|
||||
</label>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import DashboardSection from "./Section.js";
|
||||
import DashboardWidgetPicker from "./Widget/Picker.js";
|
||||
import ObjectUtils from "../../helpers/ObjectUtils.js";
|
||||
|
||||
import ApiDashboard from '../../api/factory/cis/dashboard.js';
|
||||
import ApiDashboardWidget from '../../api/factory/dashboard/widget.js';
|
||||
import ApiDashboardUser from '../../api/factory/dashboard/user.js';
|
||||
|
||||
@@ -17,32 +18,26 @@ export default {
|
||||
required: true,
|
||||
default: 'CIS'
|
||||
},
|
||||
viewData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
validator(value) {
|
||||
return value && value.name && value.timezone
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
widgets: [],
|
||||
originalWidgets: {},
|
||||
widgetsSetup: null,
|
||||
editMode: false
|
||||
editMode: false,
|
||||
timezone: null,
|
||||
userFirstName: null,
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
editMode: Vue.computed(()=>this.editMode),
|
||||
editMode: Vue.computed(() => this.editMode),
|
||||
widgetsSetup: Vue.computed(() => this.widgetsSetup),
|
||||
timezone: Vue.computed(() => this.viewData.timezone)
|
||||
timezone: this.timezone
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
widgetAdd(section_name, widget) {
|
||||
// TODO(chris): remove section_name? (change order of params => get rid of it)
|
||||
widgetAdd(widget) {
|
||||
this.$refs.widgetpicker
|
||||
.getWidget()
|
||||
.then(widget_id => {
|
||||
@@ -64,19 +59,24 @@ export default {
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
widgetUpdate(section_name, payload) {
|
||||
payload = payload[section_name];
|
||||
widgetUpdate(payload) {
|
||||
for (var k in payload) {
|
||||
for (var wid in this.widgets) {
|
||||
if (this.widgets[wid].id == k) {
|
||||
payload[k] = ObjectUtils.mergeDeep(this.widgets[wid], payload[k]);
|
||||
// NOTE(chris): remove internal props
|
||||
for (var prop of ['_x','_y','_w','_h','index','id','preset'])
|
||||
for (var prop of ['_x', '_y', '_w', '_h', 'index', 'id', 'preset'])
|
||||
if (payload[k][prop])
|
||||
delete payload[k][prop];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (payload[k].place) {
|
||||
Object.values(payload[k].place).forEach(place => {
|
||||
if (place.pinned === false)
|
||||
delete place.pinned;
|
||||
});
|
||||
}
|
||||
payload[k].widgetid = k;
|
||||
}
|
||||
this.$api
|
||||
@@ -113,18 +113,26 @@ export default {
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
widgetRemove(section_name, id) {
|
||||
widgetRemove(id) {
|
||||
this.$api
|
||||
.call(ApiDashboardUser.removeWidget(this.dashboard, id))
|
||||
.then(() => {
|
||||
this.widgets = this.widgets.filter(widget => widget.id != id);
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
async fetchViewData() {
|
||||
let viewDataResult = await this.$api.call(ApiDashboard.getViewData());
|
||||
const viewData = viewDataResult.data;
|
||||
this.timezone = viewData?.timezone;
|
||||
this.userFirstName = viewData?.name;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
async created() {
|
||||
this.$p.loadCategory('dashboard');
|
||||
|
||||
await this.fetchViewData();
|
||||
|
||||
this.$api
|
||||
.call(ApiDashboardWidget.listAllowed(this.dashboard))
|
||||
.then(res => {
|
||||
@@ -138,8 +146,8 @@ export default {
|
||||
const widgets = [];
|
||||
const remove = [];
|
||||
|
||||
for (var wid in res.data.general.widgets) {
|
||||
let widget = res.data.general.widgets[wid];
|
||||
for (var wid in res.data) {
|
||||
let widget = res.data[wid];
|
||||
widget.id = wid;
|
||||
if (widget.custom || widget.preset) {
|
||||
widgets.push(widget);
|
||||
@@ -149,19 +157,33 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
remove.forEach(wid => this.widgetRemove('general', wid));
|
||||
remove.forEach(wid => this.widgetRemove(wid));
|
||||
|
||||
this.widgets = widgets;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
template: `
|
||||
template: /* html */`
|
||||
<div class="core-dashboard">
|
||||
<h3>
|
||||
{{ $p.t('global/personalGreeting', [ viewData?.name ]) }}
|
||||
<button style="margin-left: 8px;" class="btn" @click="editMode = !editMode" aria-label="edit dashboard" v-tooltip="{showDelay:1000,value:'edit dashboard'}"><i class="fa-solid fa-gear" aria-hidden="true"></i></button>
|
||||
{{ userFirstName ? $p.t('global/personalGreeting', [ userFirstName ]) : '' }}
|
||||
<button
|
||||
class="btn ms-2"
|
||||
aria-label="edit dashboard"
|
||||
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/edit') }"
|
||||
@click="editMode = !editMode"
|
||||
><i class="fa-solid fa-gear" aria-hidden="true"></i></button>
|
||||
</h3>
|
||||
<dashboard-section :seperator="0" name="general" :widgets="widgets" @widgetAdd="widgetAdd" @widgetUpdate="widgetUpdate" @widgetRemove="widgetRemove"></dashboard-section>
|
||||
<dashboard-widget-picker ref="widgetpicker" :widgets="widgetsSetup"></dashboard-widget-picker>
|
||||
<dashboard-section
|
||||
name="general"
|
||||
:widgets="widgets"
|
||||
@widget-add="widgetAdd"
|
||||
@widget-update="widgetUpdate"
|
||||
@widget-remove="widgetRemove"
|
||||
></dashboard-section>
|
||||
<dashboard-widget-picker
|
||||
ref="widgetpicker"
|
||||
:widgets="widgetsSetup"
|
||||
></dashboard-widget-picker>
|
||||
</div>`
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import BsModal from "../Bootstrap/Modal.js";
|
||||
import { useCachedWidgetLoader } from "../../composables/Dashboard/CachedWidgetLoader.js";
|
||||
import HeightTransition from "../Tranistion/HeightTransition.js";
|
||||
|
||||
import { enableDragDropTouch } from "../../../../vendor/drag-drop-touch-js/dragdroptouch/dist/drag-drop-touch.esm.min.js";
|
||||
|
||||
if (!document.dragDropTouchActive) {
|
||||
enableDragDropTouch();
|
||||
document.dragDropTouchActive = true;
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'Item',
|
||||
components: {
|
||||
@@ -11,18 +17,14 @@ export default {
|
||||
data: () => ({
|
||||
component: "",
|
||||
arguments: null,
|
||||
target: false,
|
||||
widget: null,
|
||||
tmpConfig: {},
|
||||
isLoading: false,
|
||||
hasConfig: false,
|
||||
sharedData: null,
|
||||
sharedData: null
|
||||
}),
|
||||
emits: [
|
||||
"change",
|
||||
"remove",
|
||||
"dragstart",
|
||||
"resizestart",
|
||||
"configOpened",
|
||||
"configClosed",
|
||||
"pinItem",
|
||||
@@ -30,41 +32,85 @@ export default {
|
||||
],
|
||||
props: [
|
||||
"id",
|
||||
"widgetID",
|
||||
"config",
|
||||
"width",
|
||||
"height",
|
||||
"custom",
|
||||
"hidden",
|
||||
"editMode",
|
||||
"loading",
|
||||
"loading", // widget got added and is waiting for backend to save in db
|
||||
"item_data",
|
||||
"place",
|
||||
"setup",
|
||||
"dragstate",
|
||||
"resizeOverlay",
|
||||
"additionalRow"
|
||||
"widgetTemplate",
|
||||
"source"
|
||||
],
|
||||
computed: {
|
||||
maxHeight(){
|
||||
return this.setup?.height?.max;
|
||||
},
|
||||
maxWidth(){
|
||||
if (Object.prototype.toString.call(this.setup?.width) == "[object Number]"){
|
||||
return this.setup?.width;
|
||||
sourceInfoTooltip() {
|
||||
switch (this.source) {
|
||||
case null:
|
||||
return '';
|
||||
case 'general':
|
||||
return this.$p.t('dashboard', 'widgetFromGeneralSection');
|
||||
case 'custom':
|
||||
return this.$p.t('dashboard', 'widgetFromCustomSection');
|
||||
default:
|
||||
return this.$p.t('dashboard', 'widgetFromFunktionSection', [this.source]);
|
||||
}
|
||||
return this.setup?.width?.max;
|
||||
},
|
||||
minHeight() {
|
||||
return this.setup?.height?.min;
|
||||
isResizeableHorizontal() {
|
||||
if (this.widgetTemplate.setup.width === undefined)
|
||||
return true;
|
||||
|
||||
if (Object.prototype.toString.call(this.widgetTemplate.setup.width) == "[object Number]")
|
||||
return false;
|
||||
|
||||
if (this.widgetTemplate.setup.width.min === undefined) {
|
||||
if (this.widgetTemplate.setup.width.max === undefined)
|
||||
return true;
|
||||
return this.widgetTemplate.setup.width.max > 1;
|
||||
}
|
||||
|
||||
if (this.widgetTemplate.setup.width.max === undefined)
|
||||
return true;
|
||||
|
||||
return this.widgetTemplate.setup.width.max > this.widgetTemplate.setup.width.min;
|
||||
},
|
||||
minWidth() {
|
||||
return this.setup?.width?.min;
|
||||
isResizeableVertical() {
|
||||
if (this.widgetTemplate.setup.height === undefined)
|
||||
return true;
|
||||
|
||||
if (Object.prototype.toString.call(this.widgetTemplate.setup.height) == "[object Number]")
|
||||
return false;
|
||||
|
||||
if (this.widgetTemplate.setup.height.min === undefined) {
|
||||
if (this.widgetTemplate.setup.height.max === undefined)
|
||||
return true;
|
||||
return this.widgetTemplate.setup.height.max > 1;
|
||||
}
|
||||
|
||||
if (this.widgetTemplate.setup.height.max === undefined)
|
||||
return true;
|
||||
|
||||
return this.widgetTemplate.setup.height.max > this.widgetTemplate.setup.height.min;
|
||||
},
|
||||
isResizeable(){
|
||||
return this.maxWidth >1 || this.maxHeight >1;
|
||||
isResizeable() {
|
||||
return this.isResizeableVertical || this.isResizeableHorizontal;
|
||||
},
|
||||
isPinned(){
|
||||
resizeClasses() {
|
||||
const classes = {
|
||||
icon: 'fa-up-right-and-down-left-from-center mirror-x',
|
||||
button: 'cursor-nw-resize'
|
||||
};
|
||||
if (!this.isResizeableHorizontal) {
|
||||
classes.icon = 'fa-up-down pe-2';
|
||||
classes.button = 'cursor-ns-resize';
|
||||
} else if (!this.isResizeableVertical) {
|
||||
classes.icon = 'fa-left-right pe-2';
|
||||
classes.button = 'cursor-ew-resize';
|
||||
}
|
||||
return classes;
|
||||
},
|
||||
isPinned() {
|
||||
return this.place?.pinned ? true : false;
|
||||
},
|
||||
ready() {
|
||||
@@ -80,16 +126,16 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
unpin(){
|
||||
unpin() {
|
||||
// Unpinning is only possible in edit mode
|
||||
if(!this.editMode)
|
||||
if (!this.editMode)
|
||||
return;
|
||||
let result = { item: this.item_data, x: this.item_data.x, y: this.item_data.y };
|
||||
let result = { item: this.item_data, pinned: false };
|
||||
this.$emit('unPinItem', [result]);
|
||||
},
|
||||
pinItem(){
|
||||
let result = { item: this.item_data, x: this.item_data.x, y: this.item_data.y};
|
||||
this.$emit('pinItem',[result]);
|
||||
pinItem() {
|
||||
let result = { item: this.item_data, pinned: true };
|
||||
this.$emit('pinItem', [result]);
|
||||
},
|
||||
getWidgetC4Link(widget) {
|
||||
return (FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
@@ -101,22 +147,6 @@ export default {
|
||||
handleHideBsModal() {
|
||||
this.$emit('configClosed')
|
||||
},
|
||||
mouseDown(e) {
|
||||
this.target = e.target;
|
||||
},
|
||||
startDrag(e) {
|
||||
if (this.$refs.dragHandle.contains(this.target)) {
|
||||
this.$emit("dragstart", e);
|
||||
} else if (
|
||||
this.isResizeable &&
|
||||
this.$refs.resizeHandle.contains(this.target)
|
||||
) {
|
||||
if (this.isResizeable) this.$emit("resizestart", e);
|
||||
else e.preventDefault();
|
||||
} else {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
openConfig() {
|
||||
this.tmpConfig = { ...this.arguments };
|
||||
this.$refs.config.show();
|
||||
@@ -135,110 +165,243 @@ export default {
|
||||
},
|
||||
sendChangeConfig(config) {
|
||||
for (var k in config) {
|
||||
if (this.widget.arguments[k] == config[k]) {
|
||||
delete config[k];
|
||||
if (this.widgetTemplate.arguments[k] == config[k]) {
|
||||
delete config[k];
|
||||
}
|
||||
}
|
||||
this.$emit("change", config);
|
||||
},
|
||||
async initializeComponent() {
|
||||
if (
|
||||
this.widgetTemplate
|
||||
&& this.widgetTemplate.setup
|
||||
&& this.widgetTemplate.widget_id
|
||||
&& this.widgetTemplate.arguments
|
||||
) {
|
||||
let component = (await import(this.widgetTemplate.setup.file)).default;
|
||||
this.$options.components["widget" + this.widgetTemplate.widget_id] = component;
|
||||
this.component = "widget" + this.widgetTemplate.widget_id;
|
||||
this.arguments = { ...this.widgetTemplate.arguments, ...this.config };
|
||||
this.tmpConfig = { ...this.arguments };
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
config() {
|
||||
this.arguments = { ...this.widget?.arguments, ...this.config };
|
||||
this.arguments = { ...this.widgetTemplate?.arguments, ...this.config };
|
||||
this.tmpConfig = { ...this.arguments };
|
||||
this.$refs.config && this.$refs.config.hide();
|
||||
this.isLoading = false;
|
||||
},
|
||||
widgetTemplate() {
|
||||
this.initializeComponent();
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const { actions } = useCachedWidgetLoader();
|
||||
return {
|
||||
loadWidget: actions.load
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
this.widget = await this.loadWidget(this.id);
|
||||
let component = (await import(this.widget.setup.file)).default;
|
||||
this.$options.components["widget" + this.widget.widget_id] = component;
|
||||
this.component = "widget" + this.widget.widget_id;
|
||||
this.arguments = { ...this.widget.arguments, ...this.config };
|
||||
this.tmpConfig = { ...this.arguments };
|
||||
created() {
|
||||
this.initializeComponent();
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div v-if="loading">
|
||||
<div class="d-flex justify-content-center align-items-center h-100">
|
||||
<article
|
||||
v-if="!hidden || editMode"
|
||||
class="dashboard-item card overflow-hidden h-100 position-relative"
|
||||
:class="{
|
||||
'hidden-widget': hidden,
|
||||
[arguments?.className]: arguments && arguments.className
|
||||
}"
|
||||
>
|
||||
<div v-if="loading" class="d-flex justify-content-center align-items-center h-100">
|
||||
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="!hidden || editMode" :id="widgetID" class="dashboard-item card overflow-hidden h-100 position-relative" :class="{'hiddenWidget':hidden, 'draggedItem':dragstate, 'dashboard-item-overlay':resizeOverlay, [arguments?.className]:arguments && arguments.className}">
|
||||
<div v-show="!dragstate" class="h-100 card border-0">
|
||||
<div v-if="widget" class="card-header d-flex ps-0 pe-2 align-items-center">
|
||||
<Transition>
|
||||
<span type="button" v-if="editMode && !isPinned" drag-action="move" class="col-auto mx-2 px-2 cursor-move" aria-label="move widget" v-tooltip="{showDelay:1000, value:'move widget'}"><i class="fa-solid fa-grip-vertical" aria-hidden="true"></i></span>
|
||||
</Transition>
|
||||
<span class="col mx-2 px-2">{{ widget.setup.name }}</span>
|
||||
<template v-if="isPinned">
|
||||
<div type="button" role="button" v-if="editMode" pinned="true" @click="unpin" title="unpin item" aria-label="unpin item" class="pin cursor-pointer col-auto me-2">
|
||||
<i class="fa-solid fa-thumbtack " aria-hidden="true"></i>
|
||||
</div>
|
||||
<div v-else class="col-auto me-2">
|
||||
<i class="fa-solid fa-thumbtack "></i>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div type="button" role="button" v-if="editMode" class="col-auto me-2 pin" @click="pinItem" aria-label="pin item" title="pin item">
|
||||
<i class="fa-solid fa-thumbtack" aria-hidden="true" style="color:lightgray;"></i>
|
||||
</div>
|
||||
</template>
|
||||
<a type="button" v-if="widget.setup.cis4link" :href="getWidgetC4Link(widget)" aria-label="widget link" v-tooltip="{showDelay:1000, value:'widget link'}" class="col-auto ms-auto ">
|
||||
<i class="fa fa-arrow-up-right-from-square me-1" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a type="button" v-if="hasConfig" class="col-auto px-1" href="#" @click.prevent="openConfig" aria-label="configure widget" v-tooltip="{showDelay:1000,value:'configure widget'}"><i class="fa-solid fa-gear" aria-hidden="true"></i></a>
|
||||
<a type="button" v-if="custom && editMode" class="col-auto px-1" aria-label="delete widget" v-tooltip="{showDelay:1000,value:'delete widget'}" href="#" @click.prevent="$emit('remove')">
|
||||
<i class="fa-solid fa-trash" aria-hidden="true"></i>
|
||||
</a>
|
||||
<Transition>
|
||||
<div v-if="!custom && editMode" class="col-auto px-1 form-switch">
|
||||
<input class="form-check-input ms-0" type="checkbox" role="switch" aria-label="toggle widget" id="flexSwitchCheckChecked" v-model="visible" :value="true">
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
<div v-if="ready" class="card-body overflow-hidden p-0">
|
||||
<component :is="component" v-model:shared-data="sharedData" :config="arguments" :width="width" :height="height" @setConfig="setConfig" @change="changeConfigManually"></component>
|
||||
</div>
|
||||
<div v-else class="card-body overflow-hidden text-center d-flex flex-column justify-content-center"><i class="fa-solid fa-spinner fa-pulse fa-3x"></i></div>
|
||||
<bs-modal v-if="hasConfig" ref="config" @hideBsModal="handleHideBsModal" @showBsModal="handleShowBsModal">
|
||||
<template v-slot:title>
|
||||
{{ widget ? 'Config for ' + widget.setup.name : '' }}
|
||||
</template>
|
||||
<template v-slot:default>
|
||||
<component v-if="ready && !isLoading" :is="component" v-model:shared-data="sharedData" :config="tmpConfig" @change="changeConfig" :configMode="true"></component>
|
||||
<div v-else class="text-center"><i class="fa-solid fa-spinner fa-pulse fa-3x"></i></div>
|
||||
</template>
|
||||
<template v-if="!widget?.setup?.hideFooter" v-slot:footer>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary" @click="changeConfig">Save changes</button>
|
||||
</template>
|
||||
</bs-modal>
|
||||
<height-transition>
|
||||
<div v-if="editMode && isResizeable && !isPinned " class="card-footer d-flex justify-content-end p-0">
|
||||
<template v-if="maxWidth < 2">
|
||||
<span type="button" drag-action="resize" class="col-auto px-1 cursor-ns-resize" aria-label="resize widget" v-tooltip="{showDelay:1000, value:'resize widget'}">
|
||||
<i class="fa-solid fa-up-down pe-2" aria-hidden="true"></i>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="maxHeight < 2">
|
||||
<span type="button" drag-action="resize" class="col-auto px-1 cursor-ew-resize" aria-label="resize widget" v-tooltip="{showDelay:1000, value:'resize widget'}">
|
||||
<i class="fa-solid fa-left-right pe-2" aria-hidden="true"></i>
|
||||
<template v-else>
|
||||
<header
|
||||
v-if="widgetTemplate"
|
||||
class="card-header d-flex ps-0 pe-2 align-items-center"
|
||||
>
|
||||
<!-- move handle -->
|
||||
<Transition>
|
||||
<span
|
||||
v-if="editMode && !isPinned"
|
||||
type="button"
|
||||
drag-action="move"
|
||||
class="col-auto mx-2 px-2 cursor-move"
|
||||
draggable="true"
|
||||
aria-hidden="true"
|
||||
:aria-label="$p.t('dashboard/widget_move')"
|
||||
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/widget_move') }"
|
||||
>
|
||||
<i class="fa-solid fa-grip-vertical" aria-hidden="true"></i>
|
||||
</span>
|
||||
</Transition>
|
||||
<!-- TITLE -->
|
||||
<h4 class="col mb-0 mx-2 px-2 fs-6 lh-base">
|
||||
{{ widgetTemplate.setup.name }}
|
||||
</h4>
|
||||
<!-- source info -->
|
||||
<div
|
||||
v-if="source"
|
||||
class="col-auto me-2"
|
||||
:aria-label="sourceInfoTooltip"
|
||||
v-tooltip="{ class: 'w-100', value: sourceInfoTooltip }"
|
||||
>
|
||||
<i class="fa-solid fa-circle-info" aria-hidden="true"></i>
|
||||
</div>
|
||||
<!-- pin button -->
|
||||
<template v-if="isPinned">
|
||||
<div
|
||||
v-if="editMode"
|
||||
type="button"
|
||||
role="button"
|
||||
class="pin cursor-pointer col-auto me-2"
|
||||
:title="$p.t('dashboard/widget_unpin')"
|
||||
aria-hidden="true"
|
||||
:aria-label="$p.t('dashboard/widget_unpin')"
|
||||
pinned="true"
|
||||
@click="unpin"
|
||||
>
|
||||
<i class="fa-solid fa-thumbtack" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div v-else class="col-auto me-2" aria-hidden="true">
|
||||
<i class="fa-solid fa-thumbtack"></i>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span type="button" drag-action="resize" class="col-auto px-1 cursor-nw-resize" aria-label="resize widget" v-tooltip="{showDelay:1000, value:'resize widget'}">
|
||||
<i class="fa-solid fa-up-right-and-down-left-from-center mirror-x" aria-hidden="true"></i>
|
||||
</span>
|
||||
<div
|
||||
v-if="editMode"
|
||||
type="button"
|
||||
role="button"
|
||||
class="col-auto me-2 pin"
|
||||
:title="$p.t('dashboard/widget_pin')"
|
||||
aria-hidden="true"
|
||||
:aria-label="$p.t('dashboard/widget_pin')"
|
||||
@click="pinItem"
|
||||
>
|
||||
<i class="fa-solid fa-thumbtack" aria-hidden="true" style="color:lightgray;"></i>
|
||||
</div>
|
||||
</template>
|
||||
<!-- widget link -->
|
||||
<a
|
||||
v-if="widgetTemplate.setup.cis4link"
|
||||
:href="getWidgetC4Link(widgetTemplate)"
|
||||
class="col-auto ms-auto"
|
||||
:aria-label="$p.t('dashboard/widget_link')"
|
||||
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/widget_link') }"
|
||||
>
|
||||
<i class="fa fa-arrow-up-right-from-square me-1" aria-hidden="true"></i>
|
||||
</a>
|
||||
<!-- config button -->
|
||||
<a
|
||||
v-if="hasConfig"
|
||||
href="#"
|
||||
class="col-auto px-1"
|
||||
:aria-label="$p.t('dashboard/widget_configure')"
|
||||
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/widget_configure') }"
|
||||
@click.prevent="openConfig"
|
||||
>
|
||||
<i class="fa-solid fa-gear" aria-hidden="true"></i>
|
||||
</a>
|
||||
<!-- delete button -->
|
||||
<a
|
||||
v-if="custom && editMode"
|
||||
href="#"
|
||||
class="col-auto px-1"
|
||||
:aria-label="$p.t('dashboard/widget_delete')"
|
||||
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/widget_delete') }"
|
||||
@click.prevent="$emit('remove')"
|
||||
>
|
||||
<i class="fa-solid fa-trash" aria-hidden="true"></i>
|
||||
</a>
|
||||
<!-- hide button -->
|
||||
<Transition>
|
||||
<div v-if="!custom && editMode" class="col-auto px-1 form-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
v-model="visible"
|
||||
class="form-check-input ms-0"
|
||||
:value="true"
|
||||
:aria-label="$p.t('dashboard/widget_toggle_visibility')"
|
||||
>
|
||||
</div>
|
||||
</Transition>
|
||||
</header>
|
||||
<!-- TODO Manu rename/remove-->
|
||||
<div v-if="ready" class="card-body overflow-hidden p-0">
|
||||
<component
|
||||
:is="component"
|
||||
v-model:shared-data="sharedData"
|
||||
:config="arguments"
|
||||
:width="width"
|
||||
:height="height"
|
||||
:widget-id="item_data.id"
|
||||
:item_data="item_data"
|
||||
@setConfig="setConfig"
|
||||
@change="changeConfigManually"
|
||||
></component>
|
||||
</div>
|
||||
</height-transition>
|
||||
</div>
|
||||
</div>`,
|
||||
<div
|
||||
v-else
|
||||
class="card-body overflow-hidden text-center d-flex flex-column justify-content-center"
|
||||
>
|
||||
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
|
||||
</div>
|
||||
<bs-modal
|
||||
v-if="hasConfig"
|
||||
ref="config"
|
||||
@hideBsModal="handleHideBsModal"
|
||||
@showBsModal="handleShowBsModal"
|
||||
>
|
||||
<template v-slot:title>
|
||||
{{ widgetTemplate ? $p.t('dashboard/widget_config_title', widgetTemplate.setup) : '' }}
|
||||
</template>
|
||||
<template v-slot:default>
|
||||
<component
|
||||
:is="component"
|
||||
v-if="ready && !isLoading"
|
||||
v-model:shared-data="sharedData"
|
||||
:config="tmpConfig"
|
||||
@change="changeConfig"
|
||||
:configMode="true"
|
||||
></component>
|
||||
<div v-else class="text-center">
|
||||
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="!widgetTemplate?.setup?.hideFooter" v-slot:footer>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
data-bs-dismiss="modal"
|
||||
>{{ $p.t('ui/schliessen') }}</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
@click="changeConfig"
|
||||
>{{ $p.t('ui/speichern') }}</button>
|
||||
</template>
|
||||
</bs-modal>
|
||||
<height-transition>
|
||||
<footer
|
||||
v-if="editMode && isResizeable && !isPinned"
|
||||
class="card-footer d-flex justify-content-end p-0"
|
||||
>
|
||||
<span
|
||||
type="button"
|
||||
drag-action="resize"
|
||||
class="col-auto px-1"
|
||||
:class="resizeClasses.button"
|
||||
draggable="true"
|
||||
aria-hidden="true"
|
||||
:aria-label="$p.t('dashboard/widget_resize')"
|
||||
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/widget_resize') }"
|
||||
>
|
||||
<i
|
||||
class="fa-solid"
|
||||
:class="resizeClasses.icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</span>
|
||||
</footer>
|
||||
</height-transition>
|
||||
</template>
|
||||
</article>`,
|
||||
};
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import BsConfirm from "../Bootstrap/Confirm.js";
|
||||
import DropGrid from '../Drop/Grid.js'
|
||||
import DashboardItem from "./Item.js";
|
||||
import { useCachedWidgetLoader } from "../../composables/Dashboard/CachedWidgetLoader.js";
|
||||
import WidgetIcon from "./Widget/WidgetIcon.js"
|
||||
|
||||
import dragClick from '../../directives/dragClick.js';
|
||||
|
||||
import ObjectUtils from "../../helpers/ObjectUtils.js";
|
||||
|
||||
export default {
|
||||
name: 'Section',
|
||||
components: {
|
||||
@@ -11,8 +14,11 @@ export default {
|
||||
DashboardItem,
|
||||
WidgetIcon,
|
||||
},
|
||||
directives: {
|
||||
dragClick
|
||||
},
|
||||
inject: {
|
||||
widgetsSetup:{
|
||||
widgetsSetup: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
@@ -39,9 +45,8 @@ export default {
|
||||
configOpened: false,
|
||||
gridWidth: 1,
|
||||
gridHeight: null,
|
||||
draggedItem:null,
|
||||
additionalRow:false,
|
||||
}
|
||||
additionalRow: false
|
||||
};
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
@@ -49,22 +54,40 @@ export default {
|
||||
this.editModeIsActive
|
||||
),
|
||||
sectionName: Vue.computed(() => this.name),
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
computedWidgetsSetup(){
|
||||
if(!this.widgetsSetup) return {};
|
||||
return this.widgetsSetup.reduce((acc, setup)=>{
|
||||
acc[setup.widget_id] = setup.setup;
|
||||
sectionNameTranslation() {
|
||||
switch (this.name) {
|
||||
case "general":
|
||||
return this.$p.t('dashboard', this.name);
|
||||
case "custom":
|
||||
return this.$p.t('dashboard', this.name);
|
||||
default:
|
||||
return this.name;
|
||||
}
|
||||
},
|
||||
showSectionInformation() {
|
||||
switch (this.name) {
|
||||
case "general":
|
||||
return this.$p.t('dashboard', 'dashboardGeneralSectionDescription');
|
||||
case "custom":
|
||||
return this.$p.t('dashboard', 'dashboardCustomSectionDescription');
|
||||
default:
|
||||
return this.$p.t('dashboard', 'dashboardSectionDescription', [this.name]);
|
||||
}
|
||||
},
|
||||
indexedWidgetsTemplates() {
|
||||
if (!this.widgetsSetup)
|
||||
return {};
|
||||
return this.widgetsSetup.reduce((acc, setup) => {
|
||||
acc[setup.widget_id] = setup;
|
||||
return acc;
|
||||
},{})
|
||||
}, {});
|
||||
},
|
||||
editModeIsActive() {
|
||||
return (this.editMode || this.adminMode) && !this.configOpened
|
||||
},
|
||||
getSectionStyle() {
|
||||
return 'margin-bottom: 8px;';
|
||||
},
|
||||
items() {
|
||||
// reuses the nearest placement of the widget from another viewport
|
||||
/* const computeNearestPlace = (item, gridWidth) =>{
|
||||
@@ -85,76 +108,55 @@ export default {
|
||||
if(!item?.widgetid && item?.id){
|
||||
item.widgetid = item.id;
|
||||
}
|
||||
return { ...item, reorder: false, ...(item.place[this.gridWidth] || { reorder: true, ...{ x: 0, y: 0, w: 1, h: 1 } })};
|
||||
|
||||
let weight = 5;
|
||||
if (!item.source)
|
||||
weight = 6;
|
||||
else if (item.source == 'general')
|
||||
weight = 4;
|
||||
|
||||
let placement = item.place[this.gridWidth];
|
||||
if (!placement) {
|
||||
weight -= 3;
|
||||
placement = {};
|
||||
}
|
||||
|
||||
return { ...item, ...placement, weight };
|
||||
});
|
||||
return placedItems;
|
||||
|
||||
},
|
||||
|
||||
|
||||
if (this.editModeIsActive)
|
||||
return placedItems;
|
||||
return placedItems.filter(item => !item.hidden);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
items() {
|
||||
this.additionalRow = false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
sectionNameTranslation(){
|
||||
switch(this.name){
|
||||
case "general":
|
||||
return this.$p.t('dashboard',this.name);
|
||||
break;
|
||||
case "custom":
|
||||
return this.$p.t('dashboard',this.name);
|
||||
break;
|
||||
default:
|
||||
return this.name;
|
||||
break;
|
||||
}
|
||||
},
|
||||
showSectionInformation(){
|
||||
if (this.name == "general"){
|
||||
return this.$p.t('dashboard', 'dashboardGeneralSectionDescription');
|
||||
}
|
||||
else if(this.name == "custom"){
|
||||
return this.$p.t('dashboard', 'dashboardCustomSectionDescription');
|
||||
}
|
||||
else{
|
||||
return this.$p.t('dashboard', 'dashboardSectionDescription', [this.name]);
|
||||
}
|
||||
},
|
||||
handleConfigOpened() {
|
||||
this.configOpened = true
|
||||
},
|
||||
handleConfigClosed() {
|
||||
this.configOpened = false
|
||||
},
|
||||
checkResizeLimit(item, w, h) {
|
||||
// NOTE(chris): widgets needs to be loaded for this to work
|
||||
let widget = this.widgetState[item.widget];
|
||||
if (widget) {
|
||||
let minmaxW = { ...widget.setup.width };
|
||||
if (minmaxW.max)
|
||||
minmaxW.min = minmaxW.min || 1;
|
||||
else
|
||||
minmaxW = { min: minmaxW, max: minmaxW };
|
||||
if (w < minmaxW.min)
|
||||
w = minmaxW.min;
|
||||
if (w > minmaxW.max)
|
||||
w = minmaxW.max;
|
||||
|
||||
let minmaxH = { ...widget.setup.height };
|
||||
if (minmaxH.max)
|
||||
minmaxH.min = minmaxH.min || 1;
|
||||
else
|
||||
minmaxH = { min: minmaxH, max: minmaxH };
|
||||
if (h < minmaxH.min)
|
||||
h = minmaxH.min;
|
||||
if (h > minmaxH.max)
|
||||
h = minmaxH.max;
|
||||
}
|
||||
return [w, h];
|
||||
},
|
||||
removeWidget(item, revert) {
|
||||
if (item.custom) {
|
||||
BsConfirm.popup(this.$p.t('dashboard', 'alert_deleteWidget')).then(() => this.$emit('widgetRemove', this.name, item.id));
|
||||
BsConfirm.popup(this.$p.t('dashboard', 'alert_deleteWidget')).then(() => this.$emit('widgetRemove', item.id, this.name));
|
||||
} else {
|
||||
let update = {};
|
||||
update[item.id] = { hidden: !revert };
|
||||
|
||||
if (!revert) {
|
||||
// NOTE(chris): move to last line
|
||||
update[item.id].place = [];
|
||||
let y = this.gridHeight;
|
||||
if (this.additionalRow)
|
||||
y--;
|
||||
update[item.id].place[this.gridWidth] = { x: 0, y };
|
||||
}
|
||||
|
||||
this.updatePreset(update);
|
||||
}
|
||||
},
|
||||
@@ -163,49 +165,42 @@ export default {
|
||||
payload[item.id] = { config };
|
||||
this.updatePreset(payload);
|
||||
},
|
||||
updatePositions(updated, pinned=false) {
|
||||
updatePositions(updated) {
|
||||
let result = {};
|
||||
updated.forEach(update => {
|
||||
|
||||
let item = {...update.item};
|
||||
if (!item.placeholder) {
|
||||
if (!item.place[this.gridWidth])
|
||||
item.place[this.gridWidth] = {x: 0, y: 0, w: 1, h: 1};
|
||||
delete item.x;
|
||||
delete item.y;
|
||||
delete item.w;
|
||||
delete item.h;
|
||||
delete item.place[this.gridWidth].pinned;
|
||||
if (update.x !== undefined)
|
||||
item.place[this.gridWidth].x = update.x;
|
||||
if (update.y !== undefined)
|
||||
item.place[this.gridWidth].y = update.y;
|
||||
if (update.w !== undefined)
|
||||
item.place[this.gridWidth].w = update.w;
|
||||
if (update.h !== undefined)
|
||||
item.place[this.gridWidth].h = update.h;
|
||||
if (pinned){
|
||||
item.place[this.gridWidth].pinned = true;
|
||||
}
|
||||
let item = structuredClone(ObjectUtils.deepToRaw(update.item));
|
||||
|
||||
result[item.id] = item;
|
||||
if (!item.placeholder) {
|
||||
if (!item.place[this.gridWidth])
|
||||
item.place[this.gridWidth] = { x: 0, y: 0, w: 1, h: 1 };
|
||||
|
||||
delete item.x;
|
||||
delete item.y;
|
||||
delete item.w;
|
||||
delete item.h;
|
||||
delete item.pinned;
|
||||
delete item.weight;
|
||||
|
||||
if (update.x !== undefined)
|
||||
item.place[this.gridWidth].x = update.x;
|
||||
if (update.y !== undefined)
|
||||
item.place[this.gridWidth].y = update.y;
|
||||
if (update.w !== undefined)
|
||||
item.place[this.gridWidth].w = update.w;
|
||||
if (update.h !== undefined)
|
||||
item.place[this.gridWidth].h = update.h;
|
||||
if (update.pinned !== undefined)
|
||||
item.place[this.gridWidth].pinned = update.pinned;
|
||||
|
||||
result[item.id] = item;
|
||||
}
|
||||
});
|
||||
this.updatePreset(result);
|
||||
},
|
||||
updatePreset(update) {
|
||||
let payload = {};
|
||||
payload[this.name] = update;
|
||||
this.$emit('widgetUpdate', this.name, payload);
|
||||
this.$emit('widgetUpdate', update, this.name);
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const { state: widgetState } = useCachedWidgetLoader();
|
||||
|
||||
return {
|
||||
widgetState
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
let self = this;
|
||||
let cont = self.$refs.container;
|
||||
@@ -215,44 +210,62 @@ export default {
|
||||
self.gridWidth = parseInt(window.getComputedStyle(cont).getPropertyValue('--fhc-dashboard-grid-size'));
|
||||
});
|
||||
},
|
||||
template: `
|
||||
<div class="dashboard-section position-relative pb-3 border-bottom" ref="container" :style="getSectionStyle">
|
||||
<h4 v-if="editModeIsActive" class=" mb-2">
|
||||
<i v-tooltip="showSectionInformation(name)" class="fa-solid fa-circle-info section-info" ></i>
|
||||
{{sectionNameTranslation()}}:
|
||||
</h4>
|
||||
<button v-tooltip="$p.t('dashboard','addLine')" v-if="!additionalRow && editModeIsActive" @click="additionalRow=true" class="btn btn-outline-secondary rounded-circle newGridRow d-flex justify-content-center align-items-center">+</button>
|
||||
<drop-grid v-model:cols="gridWidth" v-model:additionalRow="additionalRow" :items="items" :itemsSetup="computedWidgetsSetup" :active="editModeIsActive" :resize-limit="checkResizeLimit" :margin-for-extra-row=".01" @draggedItem="draggedItem=$event" @rearrange-items="updatePositions" @gridHeight="gridHeight=$event" >
|
||||
template: /* html */`
|
||||
<section
|
||||
class="dashboard-section position-relative pb-3 mb-3 border-bottom"
|
||||
ref="container"
|
||||
:class="{ 'edit-active': editModeIsActive }"
|
||||
>
|
||||
<h3 v-if="adminMode" class="h4">
|
||||
<i v-tooltip="showSectionInformation" class="fa-solid fa-circle-info section-info"></i>
|
||||
{{ sectionNameTranslation }}:
|
||||
</h3>
|
||||
<button
|
||||
v-tooltip="$p.t('dashboard/addLine')"
|
||||
v-if="!additionalRow && editModeIsActive"
|
||||
class="btn btn-outline-secondary rounded-circle newGridRow d-flex justify-content-center align-items-center"
|
||||
@click="additionalRow=true"
|
||||
v-drag-click="() => additionalRow=true"
|
||||
>+</button>
|
||||
<drop-grid
|
||||
v-model:cols="gridWidth"
|
||||
:additional-row="additionalRow"
|
||||
:items="items"
|
||||
:items-setup="indexedWidgetsTemplates"
|
||||
:active="editModeIsActive"
|
||||
@rearrange-items="updatePositions"
|
||||
@grid-height="gridHeight=$event"
|
||||
>
|
||||
<template #default="item">
|
||||
<div v-if="item.placeholder" class="empty-tile-hover" @pointerdown="$emit('widgetAdd', name, { widget: 1, config: {}, place: {[gridWidth]: {x:item.x,y:item.y,w:1,h:1}}, custom: 1 })"></div>
|
||||
<div
|
||||
v-if="item.placeholder"
|
||||
class="empty-tile-hover"
|
||||
@click="$emit('widgetAdd', { widget: 1, config: {}, place: {[gridWidth]: {x:item.x,y:item.y,w:1,h:1}}, custom: 1 }, name)"
|
||||
></div>
|
||||
<dashboard-item
|
||||
v-else
|
||||
:id="item.widget"
|
||||
:dragstate="item.blank || (item.widgetid && item.widgetid == draggedItem?.data.widgetid)"
|
||||
:resizeOverlay="item.resizeOverlay"
|
||||
:widgetID="item.id"
|
||||
:width="item.w"
|
||||
:height="item.h"
|
||||
:item_data="{config:item.config, custom:item.custom, h:item.h, w:item.w,id:item.id,reorder:item.reorder,place:item.place,widget:item.widget,widgetid:item.widgetid,x:item.x,y:item.y}"
|
||||
:item_data="{config:item.config, custom:item.custom, h:item.h, w:item.w,id:item.id,place:item.place,widget:item.widget,widgetid:item.widgetid,x:item.x,y:item.y}"
|
||||
:loading="item.loading"
|
||||
:config="item.config"
|
||||
:custom="item.custom"
|
||||
:hidden="item.hidden"
|
||||
:editMode="editModeIsActive"
|
||||
:place="item.place[gridWidth]"
|
||||
:setup="computedWidgetsSetup[item.widget]"
|
||||
:widget-template="indexedWidgetsTemplates[item.widget]"
|
||||
:source="adminMode ? null : item.source || 'custom'"
|
||||
@change="saveConfig($event, item)"
|
||||
@remove="removeWidget(item, $event)"
|
||||
@config-opened="handleConfigOpened"
|
||||
@config-closed="handleConfigClosed"
|
||||
@pinItem="updatePositions($event,true)"
|
||||
@unPinItem="updatePositions">
|
||||
</dashboard-item>
|
||||
|
||||
@pin-item="updatePositions"
|
||||
@un-pin-item="updatePositions"
|
||||
></dashboard-item>
|
||||
</template>
|
||||
|
||||
</drop-grid>
|
||||
</div>`
|
||||
</section>`
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -32,19 +32,31 @@ export default {
|
||||
},
|
||||
|
||||
},
|
||||
template: `<div class="dashboard-widget-picker">
|
||||
<bs-modal ref="modal" class="fade" :dialog-class="{'modal-fullscreen-sm-down': 1, 'modal-xl': widgets && widgets.length > 0}" @hiddenBsModal="close">
|
||||
<template v-slot:title>Create new widget</template>
|
||||
template: /* html */`
|
||||
<div class="dashboard-widget-picker">
|
||||
<bs-modal
|
||||
ref="modal"
|
||||
class="fade"
|
||||
:dialog-class="{ 'modal-fullscreen-sm-down': 1, 'modal-xl': widgets && widgets.length > 0 }"
|
||||
@hiddenBsModal="close"
|
||||
>
|
||||
<template v-slot:title>{{ $p.t('dashboard/createWidget') }}</template>
|
||||
<template v-slot:default>
|
||||
<div v-if="widgets" class="row g-2">
|
||||
<div v-if="!widgets.length">
|
||||
No Widgets available
|
||||
{{ $p.t('dashboard/noWidgetsAvailable') }}
|
||||
</div>
|
||||
<div v-for="widget in widgets" :key="widget.widget_id" class="widget-icon-container col-sm-6 col-md-4 col-lg-3 col-xl-2">
|
||||
<widget-icon @select="pick" :widget="widget" ></widget-icon>
|
||||
<div
|
||||
v-for="widget in widgets"
|
||||
:key="widget.widget_id"
|
||||
class="widget-icon-container col-sm-6 col-md-4 col-lg-3 col-xl-2"
|
||||
>
|
||||
<widget-icon @select="pick" :widget="widget"></widget-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center"><i class="fa-solid fa-spinner fa-pulse fa-3x"></i></div>
|
||||
<div v-else class="text-center">
|
||||
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
|
||||
</div>
|
||||
</template>
|
||||
</bs-modal>
|
||||
</div>`
|
||||
|
||||
@@ -5,7 +5,9 @@ export default {
|
||||
"height",
|
||||
"configMode",
|
||||
"sharedData",
|
||||
"widgetInfo"
|
||||
"widgetInfo",
|
||||
"widgetId",
|
||||
"item_data"
|
||||
],
|
||||
emits: [
|
||||
"setConfig",
|
||||
|
||||
@@ -11,9 +11,6 @@ export default {
|
||||
mixins: [
|
||||
AbstractWidget
|
||||
],
|
||||
inject: [
|
||||
"timezone"
|
||||
],
|
||||
methods: {
|
||||
getPromiseFunc(start, end) {
|
||||
return [
|
||||
@@ -27,6 +24,6 @@ export default {
|
||||
},
|
||||
template: /*html*/`
|
||||
<div class="dashboard-widget-lvplan d-flex flex-column h-100">
|
||||
<fhc-calendar :timezone="timezone" :get-promise-func="getPromiseFunc" />
|
||||
<fhc-calendar :get-promise-func="getPromiseFunc" />
|
||||
</div>`
|
||||
}
|
||||
@@ -12,23 +12,38 @@ export default {
|
||||
editModeIsActive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
sectionName: {
|
||||
type: String,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
components:{
|
||||
CoreForm,
|
||||
FormInput,
|
||||
BsModal
|
||||
BsModal,
|
||||
PvChips: primevue.chips,
|
||||
PvMultiSelect: primevue.multiselect,
|
||||
PvAutoComplete: primevue.autocomplete,
|
||||
},
|
||||
data: () => ({
|
||||
bookmark_id: null,
|
||||
title_input: "",
|
||||
url_input: "",
|
||||
sort: null,
|
||||
validation: {
|
||||
invalidURL: false,
|
||||
invalidTitel: false,
|
||||
},
|
||||
tagsArrayMS: [],
|
||||
tagsArrayAC: [],
|
||||
selectedTags: [],
|
||||
newTag: null,
|
||||
selectedFilters: [],
|
||||
filter: [],
|
||||
filteredArray: [],
|
||||
sharedFiltered: {},
|
||||
}),
|
||||
|
||||
computed: {
|
||||
tagName() {
|
||||
return this.config.tag !== undefined && this.config.tag.length > 0
|
||||
@@ -42,6 +57,27 @@ export default {
|
||||
|
||||
return false;
|
||||
},
|
||||
newSort(){
|
||||
if(this.shared.length == 0)
|
||||
return 1;
|
||||
else
|
||||
return Math.max(...this.shared.map(b => b.sort)) + 1;
|
||||
},
|
||||
maxSort(){
|
||||
if(this.shared.length == 0)
|
||||
return 0;
|
||||
else
|
||||
return Math.max(...this.sharedFiltered.map(b => b.sort));
|
||||
},
|
||||
minSort(){
|
||||
if(this.shared.length == 0)
|
||||
return 0;
|
||||
else
|
||||
return Math.min(...this.sharedFiltered.map(b => b.sort));
|
||||
},
|
||||
filterInput(){
|
||||
return this.selectedFilters.map(item => item.tag);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
stopDrag(event){
|
||||
@@ -49,7 +85,8 @@ export default {
|
||||
},
|
||||
clearInputs(){
|
||||
this.title_input = "";
|
||||
this.url_input = "";
|
||||
this.url_input = "";
|
||||
this.selectedTags = [];
|
||||
},
|
||||
openCreateModal() {
|
||||
this.$refs.createModal.show()
|
||||
@@ -58,22 +95,26 @@ export default {
|
||||
this.title_input = bookmark.title;
|
||||
this.url_input = bookmark.url;
|
||||
this.bookmark_id = bookmark.bookmark_id;
|
||||
this.$refs.editModal.show()
|
||||
this.selectedTags = JSON.parse(bookmark.tag);
|
||||
|
||||
this.$refs.editModal.show();
|
||||
},
|
||||
editBookmark(event){
|
||||
event.preventDefault();
|
||||
if(!this.bookmark_id || !this.url_input || !this.title_input) return;
|
||||
|
||||
|
||||
this.$api
|
||||
.call(ApiBookmark.update({
|
||||
bookmark_id: this.bookmark_id,
|
||||
title: this.title_input,
|
||||
url: this.url_input,
|
||||
tag: this.selectedTags,
|
||||
}))
|
||||
.then((res) => res.data)
|
||||
.then((result) => {
|
||||
this.$fhcAlert.alertInfo(this.$p.t("bookmark", "bookmarkUpdated"));
|
||||
// refetch the bookmarks to see the updates
|
||||
this.getAllBookmarkTags();
|
||||
this.fetchBookmarks();
|
||||
// reset the values for the title and url inputs
|
||||
this.clearInputs();
|
||||
@@ -94,16 +135,21 @@ export default {
|
||||
// early return if validation failed
|
||||
if (!this.isValidationSuccessfull()) return;
|
||||
|
||||
// get highest Sort
|
||||
this.sort = this.newSort;
|
||||
|
||||
this.$api
|
||||
.call(ApiBookmark.insert({
|
||||
tag: this.config.tag,
|
||||
tag: this.selectedTags,
|
||||
title: this.title_input,
|
||||
url: this.url_input,
|
||||
sort: this.sort
|
||||
}))
|
||||
.then((res) => res.data)
|
||||
.then((result) => {
|
||||
this.$fhcAlert.alertInfo(this.$p.t("bookmark", "bookmarkAdded"));
|
||||
// refetch the bookmarks to see the updates
|
||||
this.getAllBookmarkTags();
|
||||
this.fetchBookmarks();
|
||||
this.$refs.createModal.hide();
|
||||
// reset the values for the title and url inputs
|
||||
@@ -111,7 +157,6 @@ export default {
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
|
||||
isValidationSuccessfull() {
|
||||
// validate the input fields
|
||||
if (this.title_input.length === 0) {
|
||||
@@ -131,6 +176,10 @@ export default {
|
||||
.then((res) => res.data)
|
||||
.then((result) => {
|
||||
this.shared = result;
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.sharedFiltered = this.filterBookmarksByTags(this.shared);
|
||||
});
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
@@ -147,39 +196,268 @@ export default {
|
||||
this.$fhcAlert.alertInfo(this.$p.t("bookmark", "bookmarkDeleted"));
|
||||
// refetch the bookmarks to see the updates
|
||||
this.fetchBookmarks();
|
||||
this.getAllBookmarkTags();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
filterBookmarksByTags(bookmarks) {
|
||||
const filter = this.filter;
|
||||
if (!filter || filter.length === 0 || filter == "[]") return bookmarks;
|
||||
|
||||
return bookmarks.filter(b => {
|
||||
const tags = JSON.parse(b.tag || "[]");
|
||||
return tags.some(tag => filter.includes(tag));
|
||||
});
|
||||
},
|
||||
sortDown(bookmark_id){
|
||||
const current = this.sharedFiltered.find(b => b.bookmark_id === bookmark_id);
|
||||
|
||||
const next = this.sharedFiltered
|
||||
.filter(b => b.sort > current.sort)
|
||||
.sort((a, b) => a.sort - b.sort)[0];
|
||||
|
||||
if (!next) {
|
||||
console.log("lowest sort item, no change");
|
||||
return;
|
||||
}
|
||||
this.changeOrder(current.bookmark_id, next.bookmark_id);
|
||||
},
|
||||
sortUp(bookmark_id){
|
||||
const current = this.sharedFiltered.find(b => b.bookmark_id === bookmark_id);
|
||||
|
||||
const next = this.sharedFiltered
|
||||
.filter(b => b.sort < current.sort)
|
||||
.sort((a, b) => a.sort + b.sort)[0];
|
||||
|
||||
if (!next) {
|
||||
console.log("highest sort item, no change");
|
||||
return;
|
||||
}
|
||||
this.changeOrder(current.bookmark_id, next.bookmark_id);
|
||||
},
|
||||
addNewTag(){
|
||||
if(this.newTag != null && this.newTag.length) {
|
||||
this.tagsArrayMS.push({tag: this.newTag, code: this.newTag});
|
||||
this.selectedTags.push({tag: this.newTag, code: this.newTag});
|
||||
this.newTag = null;
|
||||
}
|
||||
else
|
||||
this.$fhcAlert.alertError(this.$p.t("bookmark", "errorInputNecessary"));
|
||||
},
|
||||
changeOrder(bookmark_id_1, bookmark_id_2){
|
||||
this.$api
|
||||
.call(ApiBookmark.changeOrder(bookmark_id_1, bookmark_id_2))
|
||||
.then((res) => res.data)
|
||||
.then((result) => {
|
||||
// refetch the bookmarks to see the updates
|
||||
this.fetchBookmarks();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
hasTags(link) {
|
||||
if (!link || !link.tag) return false;
|
||||
|
||||
let tags = link.tag;
|
||||
if (typeof tags === 'string') {
|
||||
try {
|
||||
tags = JSON.parse(tags)
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (Array.isArray(tags) && tags.length > 0) {
|
||||
return tags.join(' ');
|
||||
}
|
||||
},
|
||||
prepareTag(bookmarkArr){
|
||||
const parsedArr = Array.isArray(bookmarkArr)
|
||||
? bookmarkArr
|
||||
: JSON.parse(bookmarkArr);
|
||||
|
||||
const result = parsedArr.map(tag => ({
|
||||
tag,
|
||||
code: tag
|
||||
}));
|
||||
|
||||
return result;
|
||||
},
|
||||
getAllBookmarkTags(){
|
||||
this.$api
|
||||
.call(ApiBookmark.getAllBookmarkTags())
|
||||
.then((res) => res.data)
|
||||
.then((result) => {
|
||||
//Version Chips
|
||||
this.tagsArrayMS = this.prepareTag(result.data);
|
||||
|
||||
//Version Autocomplete
|
||||
this.tagsArrayAC = result.data;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
openFilterModal(){
|
||||
this.$refs.filterModal.show();
|
||||
},
|
||||
async handleAddingTagFilter(widgetId){
|
||||
|
||||
const result = await this.isInOverride(widgetId);
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
const [status, reason] = result;
|
||||
|
||||
if (status) {
|
||||
this.addTagFilter(widgetId);
|
||||
} else {
|
||||
this.addWidgetToOverride(widgetId, reason);
|
||||
}
|
||||
},
|
||||
addTagFilter(widgetId){
|
||||
this.$api
|
||||
.call(ApiBookmark.addTagFilter(widgetId, this.sectionName, this.filterInput))
|
||||
.then((res) => res.data)
|
||||
.then((result) => {
|
||||
this.$fhcAlert.alertInfo(this.$p.t("bookmark", "filterUpdated"));
|
||||
this.$refs.filterModal.hide();
|
||||
this.getTagFilter(this.widgetId);
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.getAllBookmarkTags();
|
||||
});
|
||||
this.fetchBookmarks();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
async isInOverride(widgetId) {
|
||||
try {
|
||||
const res = await this.$api.call(ApiBookmark.isInOverride(widgetId, this.sectionName));
|
||||
const result = res.data;
|
||||
return result;
|
||||
} catch (err) {
|
||||
this.$fhcAlert.handleSystemError(err);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
addWidgetToOverride(widgetId, reason){
|
||||
this.$api
|
||||
.call(ApiBookmark.addWidgetToOverride(
|
||||
widgetId,
|
||||
this.sectionName,
|
||||
reason,
|
||||
this.item_data.x,
|
||||
this.item_data.y,
|
||||
this.item_data.h,
|
||||
this.item_data.w
|
||||
))
|
||||
.then((res) => res.data)
|
||||
.then((result) => {
|
||||
this.addTagFilter(widgetId);
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
getTagFilter(widget_id){
|
||||
this.$api
|
||||
.call(ApiBookmark.getTagFilter(widget_id, this.sectionName))
|
||||
.then((res) => res.data)
|
||||
.then((result) => {
|
||||
const rawTags = result.tags; // string
|
||||
this.filter = rawTags;
|
||||
if(rawTags != null) {
|
||||
this.rawTagsParsed = JSON.parse(rawTags);
|
||||
this.selectedFilters = this.prepareTag(this.rawTagsParsed);
|
||||
}
|
||||
this.fetchBookmarks();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
search(event) {
|
||||
const query = event.query ?? "";
|
||||
|
||||
// Filter for text
|
||||
this.filteredArray = this.tagsArrayAC.filter(item =>
|
||||
item.toLowerCase().includes(query.toLowerCase())
|
||||
);
|
||||
|
||||
// input if search not successful
|
||||
if (this.filteredArray.length === 0 && query) {
|
||||
this.filteredArray = [query];
|
||||
}
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
if(this.widgetId) {
|
||||
this.getTagFilter(this.widgetId);
|
||||
}
|
||||
await this.fetchBookmarks();
|
||||
this.getAllBookmarkTags();
|
||||
},
|
||||
created() {
|
||||
//
|
||||
// this.$emit('setConfig', true); -> use this to enable widget config mode if needed
|
||||
// this.$emit('setConfig', true); // -> use this to enable widget config mode if needed
|
||||
},
|
||||
|
||||
|
||||
template: /*html*/ `
|
||||
<div class="widgets-url w-100 h-100 overflow-auto" style="padding: 1rem 1rem;">
|
||||
<div class="d-flex flex-column justify-content-between">
|
||||
<button class="btn btn-outline-secondary btn-sm w-100 mt-2 card" @click="openCreateModal" type="button">{{$p.t('bookmark','newLink')}}</button>
|
||||
|
||||
<template v-if="shared">
|
||||
<div class="d-flex mt-2">
|
||||
<button class="btn btn-outline-secondary btn-sm flex-grow-1 me-2" @click="openCreateModal">
|
||||
{{$p.t('bookmark','newLink')}}
|
||||
</button>
|
||||
<button v-if="selectedFilters.length" class="btn btn-secondary btn-sm" :title="this.$p.t('bookmark/editFilter')" @click="openFilterModal">
|
||||
<i class="fa-solid fa-filter-circle-xmark"></i>
|
||||
</button>
|
||||
<button v-else class="btn btn-outline-secondary btn-sm" :title="this.$p.t('bookmark/filterByTags')" @click="openFilterModal">
|
||||
<i class="fa-solid fa-filter"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<template v-if="sharedFiltered">
|
||||
|
||||
<template v-if="!emptyBookmarks">
|
||||
<div v-for="link in shared" :key="link.id" class="d-flex mt-2">
|
||||
<a target="_blank" :href="link.url">
|
||||
<div v-for="link in sharedFiltered" :key="link.id" class="d-flex mt-2">
|
||||
<a target="_blank" :href="link.url" class="me-1">
|
||||
<i class="fa fa-solid fa-arrow-up-right-from-square me-1"></i>{{ link.title }}
|
||||
</a>
|
||||
<span
|
||||
v-if="hasTags(link)"
|
||||
:title="hasTags(link)"
|
||||
style="color: silver;"
|
||||
>
|
||||
<i class="fa fa-solid fa-tag text-gray-500" aria-hidden="true"></i>
|
||||
</span>
|
||||
|
||||
<div class="ms-auto">
|
||||
<!--EDIT BOOKMARK-->
|
||||
<a type="button" href="#" @click.prevent="openEditModal(link)" aria-label="edit bookmark" v-tooltip="{showDelay:1000,value:'edit bookmark'}">
|
||||
<a type="button" href="#" @click.prevent="openEditModal(link)" aria-label="edit bookmark" :title="this.$p.t('bookmark/editBookmark')">
|
||||
<i class="fa fa-edit me-1" aria-hidden="true"></i>
|
||||
</a>
|
||||
<!--DELETE BOOKMARK-->
|
||||
<a type="button" id="deleteBookmark" href="#" aria-label="delete bookmark" v-tooltip="{showDelay:1000,value:'delete bookmark'}" @click.prevent="removeLink(link.bookmark_id)">
|
||||
<a type="button" id="deleteBookmark" href="#" aria-label="delete bookmark" :title="this.$p.t('bookmark/deleteBookmark')" @click.prevent="removeLink(link.bookmark_id)">
|
||||
<i class="fa fa-regular fa-trash-can" aria-hidden="true"></i>
|
||||
</a>
|
||||
<!--SORT BOOKMARKS-->
|
||||
<a
|
||||
v-if="sharedFiltered.length > 1"
|
||||
type="button"
|
||||
id="downsortBookmark"
|
||||
href="#"
|
||||
aria-label="sortdown bookmark"
|
||||
:title="this.$p.t('bookmark/sortDownwards')"
|
||||
@click.prevent="sortDown(link.bookmark_id)"
|
||||
>
|
||||
<i :class="[ 'fa', 'fa-arrow-down', 'me-1', link.sort === maxSort ? 'text-light pointer-events-none' : '' ]"></i>
|
||||
</a>
|
||||
<a
|
||||
v-if="sharedFiltered.length > 1"
|
||||
type="button"
|
||||
id="upsortBookmark"
|
||||
href="#"
|
||||
aria-label="sortup bookmark"
|
||||
:title="this.$p.t('bookmark/sortToTop')"
|
||||
@click.prevent="sortUp(link.bookmark_id)"
|
||||
>
|
||||
<i :class="[ 'fa', 'fa-arrow-up', 'me-1', link.sort === minSort ? 'text-light pointer-events-none' : '' ]"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -188,15 +466,15 @@ export default {
|
||||
<span>{{$p.t('bookmark','emptyBookmarks')}}</span>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<p v-for="i in 4" class="placeholder-glow">
|
||||
<span class="placeholder" :class="{'col-9' : true}"></span>
|
||||
</p>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</div>
|
||||
<!--EDIT MODAL-->
|
||||
<teleport to="body">
|
||||
<bs-modal @[\`hide.bs.modal\`]="bookmark_id=null; clearInputs();" ref="editModal">
|
||||
@@ -204,8 +482,20 @@ export default {
|
||||
<h2>{{$p.t('bookmark','editLink')}}</h2>
|
||||
</template>
|
||||
<template #default>
|
||||
<form-input :label="$p.t('profil','Titel')" :title="$p.t('profil','Titel')" id="editTitle" v-model="title_input" name="title" class="mb-2"></form-input>
|
||||
|
||||
<form-input :label="$p.t('profil','Titel')" :title="$p.t('profil','Titel')" id="editTitle" v-model="title_input" name="title" class="mb-2"></form-input>
|
||||
<form-input label="Url" title="Url" id="editUrl" v-model="url_input" name="url"></form-input>
|
||||
|
||||
<label class="mt-2">Tags</label>
|
||||
<div class="mt-2">
|
||||
<PvAutoComplete
|
||||
v-model="selectedTags"
|
||||
multiple
|
||||
dropdown
|
||||
:suggestions="filteredArray"
|
||||
@complete="search"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<button @click="editBookmark" class="btn btn-primary">{{$p.t('bookmark','saveLink')}}</button>
|
||||
@@ -219,17 +509,66 @@ export default {
|
||||
<h2>{{$p.t('bookmark','newLink')}}</h2>
|
||||
</template>
|
||||
<template #default>
|
||||
|
||||
<form-input :label="$p.t('profil','Titel')" :title="$p.t('profil','Titel')" id="insertTitle" v-model="title_input" name="title" class="mb-2"></form-input>
|
||||
<form-input label="Url" title="Url" id="insertUrl" v-model="url_input" name="url"></form-input>
|
||||
|
||||
<label class="mt-2">Tags</label>
|
||||
<div class="mt-2">
|
||||
<PvAutoComplete
|
||||
v-model="selectedTags"
|
||||
multiple
|
||||
dropdown
|
||||
:suggestions="filteredArray"
|
||||
@complete="search"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<template #footer>
|
||||
<button @click="insertBookmark" class="btn btn-primary">{{$p.t('bookmark','saveLink')}}</button>
|
||||
</template>
|
||||
</bs-modal>
|
||||
</teleport>
|
||||
<!--FILTER MODAL-->
|
||||
<teleport to="body">
|
||||
<bs-modal @[\`hide.bs.modal\`]="clearInputs();" ref="filterModal">
|
||||
<template #title>
|
||||
<h2>{{$p.t('bookmark','headerFilterBookmark')}}</h2>
|
||||
</template>
|
||||
<template #default>
|
||||
|
||||
<div class="mt-2 row">
|
||||
<div class="col-10">
|
||||
<PvMultiSelect
|
||||
v-model="selectedFilters"
|
||||
id="tagFilterUrl"
|
||||
:options="tagsArrayMS"
|
||||
optionLabel="tag"
|
||||
display="chip"
|
||||
:placeholder="$p.t('bookmark','noFilter')"
|
||||
:maxSelectedLabels="3"
|
||||
class="p-inputtext-sm w-100 me-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<template #footer>
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
@click="handleAddingTagFilter(widgetId)"
|
||||
:title="$p.t('bookmark','filterByTags')"
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</template>
|
||||
</bs-modal>
|
||||
</teleport>
|
||||
`,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Link JSON structure:
|
||||
{
|
||||
|
||||
+422
-498
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user