mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-07-20 08:22:17 +00:00
Merge branch 'feature-25999/C4_cleanup' into merge_FHC4_C4
This commit is contained in:
@@ -4,6 +4,6 @@ if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
|
||||
|
||||
// CMS Content Id for CIS4 Menu Root
|
||||
$config['cis_menu_root_content_id'] = 11066;
|
||||
$config['cis_menu_root_content_id'] = 11087;
|
||||
// send Mails for ProfilUpdate
|
||||
$config['cis_send_profil_update_mails'] = true;
|
||||
|
||||
@@ -61,8 +61,7 @@ $route['api/v1/organisation/[O|o]rganisationseinheit/(:any)'] = 'api/v1/organisa
|
||||
$route['api/v1/ressource/[B|b]etriebsmittelperson/(:any)'] = 'api/v1/ressource/betriebsmittelperson2/$1';
|
||||
$route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1';
|
||||
|
||||
$route['CisVue'] = 'CisVue/dashboard';
|
||||
$route['Cis/Stundenplan/(:any)'] = 'Cis/Stundenplan';
|
||||
$route['Cis/Stundenplan/(:any)?'] = 'Cis/Stundenplan/index/$1';
|
||||
|
||||
// load routes from extensions
|
||||
$subdir = 'application/config/extensions';
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class InfoTerminal extends Auth_Controller
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'index' => ['basis/cis:r'],
|
||||
]);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->load->view('Cis/InfoTerminal.php', []);
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,12 @@ class MyLv extends Auth_Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->load->view('Cis/MyLv');
|
||||
|
||||
$viewData = array(
|
||||
|
||||
);
|
||||
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'MyLv']);
|
||||
}
|
||||
|
||||
public function Info($studien_semester,$lvid)
|
||||
|
||||
@@ -55,7 +55,10 @@ class Profil extends Auth_Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->load->view('Cis/Profil');
|
||||
$viewData = array(
|
||||
|
||||
);
|
||||
$this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'profilIndex']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,7 +68,9 @@ class Profil extends Auth_Controller
|
||||
*/
|
||||
public function View($uid)
|
||||
{
|
||||
$this->load->view('Cis/Profil');
|
||||
$viewData = array ('uid' => $uid);
|
||||
|
||||
$this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'profilViewUid']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,8 +23,13 @@ class Stundenplan extends Auth_Controller
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function index()
|
||||
public function index($lv_id = null)
|
||||
{
|
||||
$this->load->view('Cis/Stundenplan');
|
||||
|
||||
$viewData = array(
|
||||
'lv_id' => $lv_id
|
||||
);
|
||||
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Stundenplan']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,6 @@ class Cis4 extends Auth_Controller
|
||||
'person_id' => $personData->person_id
|
||||
);
|
||||
|
||||
$this->load->view('CisVue/Dashboard.php',['viewData' => $viewData]);
|
||||
$this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'FhcDashboard']);
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,14 @@ class Cms extends Auth_Controller
|
||||
|
||||
$content = current($content);
|
||||
|
||||
$this->load->view('CisVue/Cms/Content', ['content_id' => $content_id, 'template_kurzbz' => $content->template_kurzbz, 'version' => $version, 'sprache' => $sprache, 'sichtbar' => $sichtbar]);
|
||||
$viewData = array(
|
||||
'content_id' => $content_id,
|
||||
'template_kurzbz' => $content->template_kurzbz,
|
||||
'version' => $version,
|
||||
'sichtbar' => $sichtbar
|
||||
);
|
||||
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Content']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,49 +83,10 @@ class Cms extends Auth_Controller
|
||||
*/
|
||||
public function news($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true)
|
||||
{
|
||||
$this->load->view('CisVue/Cms/Content', ['infoscreen' => $infoscreen, 'studiengang_kz' => $studiengang_kz, 'semester' => $semester, 'mischen' => $mischen, 'titel' => $titel, 'edit' => $edit, 'sichtbar' => $sichtbar]);
|
||||
}
|
||||
|
||||
public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true)
|
||||
{
|
||||
$get_page = intval($this->input->get('page', true));
|
||||
$get_page_size = intval($this->input->get('page_size', true));
|
||||
if ($get_page) {
|
||||
$page = $get_page;
|
||||
}
|
||||
if ($get_page_size) {
|
||||
$page_size = $get_page_size;
|
||||
} else {
|
||||
$page_size = $this->page_size;
|
||||
}
|
||||
$news = $this->cmslib->getNews($infoscreen, $studiengang_kz, $semester, $mischen, $titel, $edit, $sichtbar, $page, $page_size);
|
||||
|
||||
if (isError($news)) {
|
||||
$this->terminateWithJsonError(getError($news));
|
||||
}
|
||||
$news = hasData($news) ? getData($news) : null;
|
||||
if ($news) {
|
||||
echo json_encode($news);
|
||||
} else {
|
||||
show_error("News: No data found");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function getNewsRowCount($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $fachbereich_kurzbz = null, $maxalter = 0, $edit = false, $sichtbar = true, $page = 1, $page_size = 10)
|
||||
{
|
||||
list($studiengang_kz, $semester) = $this->cmslib->getStgAndSem($studiengang_kz, $semester);
|
||||
$all = $edit;
|
||||
$num_rows = $this->NewsModel->countNewsWithContent(getSprache(), $studiengang_kz, $semester, $fachbereich_kurzbz, $sichtbar, $maxalter, $page, $this->page_size, $all, $mischen);
|
||||
if (isError($num_rows)) {
|
||||
$this->terminateWithJsonError(getError($num_rows));
|
||||
}
|
||||
$num_rows = hasData($num_rows) ? getData($num_rows) : null;
|
||||
if ($num_rows) {
|
||||
echo json_encode($num_rows);
|
||||
} else {
|
||||
show_error("News number rows: No data found");
|
||||
}
|
||||
// TODO: what are those parameters and what are they used for?
|
||||
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'News']);
|
||||
// $this->load->view('CisVue/Cms/Content', ['infoscreen' => $infoscreen, 'studiengang_kz' => $studiengang_kz, 'semester' => $semester, 'mischen' => $mischen, 'titel' => $titel, 'edit' => $edit, 'sichtbar' => $sichtbar]);
|
||||
}
|
||||
|
||||
public function getRoomInformation($ort_kurzbz){
|
||||
|
||||
@@ -29,17 +29,15 @@ class Dashboard extends Auth_Controller
|
||||
{
|
||||
|
||||
$this->load->model('person/Person_model','PersonModel');
|
||||
$begruesung = $this->PersonModel->getFirstName(getAuthUID());
|
||||
if(isError($begruesung))
|
||||
{
|
||||
show_error("name couldn't be loaded for username ".getAuthUID());
|
||||
}
|
||||
$begruesung = getData($begruesung);
|
||||
$personData = getData($this->PersonModel->getByUid(getAuthUID()))[0];
|
||||
|
||||
$viewData = array(
|
||||
'name' => $begruesung
|
||||
'uid' => getAuthUID(),
|
||||
'name' => $personData->vorname,
|
||||
'person_id' => $personData->person_id
|
||||
);
|
||||
|
||||
$this->load->view('CisVue/Dashboard.php', ['viewData' => $viewData]);
|
||||
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData]);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ class Bookmark extends FHCAPI_Controller
|
||||
'getBookmarks' => self::PERM_LOGGED,
|
||||
'delete' => self::PERM_LOGGED,
|
||||
'insert' => self::PERM_LOGGED,
|
||||
'update' => self::PERM_LOGGED,
|
||||
]);
|
||||
|
||||
$this->load->model('dashboard/Bookmark_model', 'BookmarkModel');
|
||||
@@ -50,7 +51,8 @@ class Bookmark extends FHCAPI_Controller
|
||||
*/
|
||||
public function getBookmarks()
|
||||
{
|
||||
$bookmarks = $this->BookmarkModel->loadWhere(["uid"=>$this->uid]);
|
||||
$this->BookmarkModel->addOrder("bookmark_id");
|
||||
$bookmarks = $this->BookmarkModel->loadWhere(["uid"=>$this->uid]);
|
||||
|
||||
$bookmarks = $this->getDataOrTerminateWithError($bookmarks);
|
||||
|
||||
@@ -104,6 +106,33 @@ class Bookmark extends FHCAPI_Controller
|
||||
|
||||
$this->terminateWithSuccess($insert_into_result);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* updates bookmark in the bookmark table
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function update($bookmark_id)
|
||||
{
|
||||
// form validation
|
||||
$this->load->library('form_validation');
|
||||
$this->form_validation->set_rules('url', 'URL', 'required|valid_url|max_length[511]');
|
||||
$this->form_validation->set_rules('title', 'Title', 'required|max_length[255]');
|
||||
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
|
||||
|
||||
$url = $this->input->post('url',true);
|
||||
$title = $this->input->post('title',true);
|
||||
|
||||
$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->getDataOrTerminateWithError($update_result);
|
||||
|
||||
$this->terminateWithSuccess($update_result);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?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 Cis4FhcApi extends FHCAPI_Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Object initialization
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'getViewData' => self::PERM_LOGGED,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
|
||||
/**
|
||||
* fetches ViewData
|
||||
*/
|
||||
public function getViewData()
|
||||
{
|
||||
$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->terminateWithSuccess($viewData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?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 CisMenu extends FHCAPI_Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Object initialization
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'getMenu' => self::PERM_LOGGED,
|
||||
]);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
|
||||
/**
|
||||
* fetches the menu for CIS from the database based on the userLanguage
|
||||
*/
|
||||
public function getMenu()
|
||||
{
|
||||
$this->load->model('content/Content_model', 'ContentModel');
|
||||
$this->load->config('cis');
|
||||
$cis4_content_id =$this->config->item('cis_menu_root_content_id');
|
||||
$result = $this->ContentModel->getMenu($cis4_content_id, getAuthUID(),getUserLanguage());
|
||||
$result = $this->getDataOrTerminateWithError($result);
|
||||
$menu = $result->childs ?? [];
|
||||
$this->terminateWithSuccess($menu);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -106,7 +106,6 @@ class Cms extends FHCAPI_Controller
|
||||
$this->terminateWithSuccess($content_id);
|
||||
}
|
||||
|
||||
//todo: there is the method news and getNews but only one should exist
|
||||
public function news()
|
||||
{
|
||||
|
||||
@@ -125,25 +124,35 @@ class Cms extends FHCAPI_Controller
|
||||
|
||||
//get the data or terminate with error
|
||||
$news = $this->getDataOrTerminateWithError($news);
|
||||
|
||||
// array that keeps track of which news don't have a betreff and have to be removed from the news array
|
||||
$newsToRemove = array();
|
||||
// collect the content of the news
|
||||
foreach($news as $news_element){
|
||||
$this->addMeta("content_id",$news_element->content_id);
|
||||
foreach($news as $index=>$news_element){
|
||||
|
||||
//todo: quick fix, for query builder error when fetching content
|
||||
$this->NewsModel->resetQuery();
|
||||
$content = $this->cmslib->getContent($news_element->content_id);
|
||||
|
||||
$content = getData($content);
|
||||
|
||||
$news_element->content_obj = $content;
|
||||
if(isError($content))
|
||||
{
|
||||
// removes the news from the news array, so that the response does not include a invalid news
|
||||
array_push($newsToRemove,$index);
|
||||
//add the error to the api response? visual feedback
|
||||
//$this->addError(print_r($content->retval,true));
|
||||
continue;
|
||||
}
|
||||
$content = getData($content);
|
||||
$news_element->content_obj = $content;
|
||||
}
|
||||
|
||||
//removes all news that don't have a betreff
|
||||
foreach($newsToRemove as $removeNewsIndex)
|
||||
{
|
||||
unset($news[$removeNewsIndex]);
|
||||
}
|
||||
|
||||
$withContent = function($news) {
|
||||
return $news->content_obj != null;
|
||||
};
|
||||
|
||||
};
|
||||
$newsWithContent = array_filter($news, $withContent);
|
||||
|
||||
$this->terminateWithSuccess($newsWithContent);
|
||||
|
||||
}
|
||||
@@ -176,15 +185,18 @@ class Cms extends FHCAPI_Controller
|
||||
// getting the GET parameters
|
||||
$page = intval($this->input->get('page', true));
|
||||
$page_size = intval($this->input->get('page_size', true));
|
||||
$sprache = $this->input->get('sprache', true);
|
||||
if(!$sprache)
|
||||
{
|
||||
$sprache = getUserLanguage();
|
||||
}
|
||||
|
||||
// default value for the page_size is 10
|
||||
$page_size = $page_size ?? 10;
|
||||
|
||||
$news = $this->cmslib->getNews($infoscreen, $studiengang_kz, $semester, $mischen, $titel, $edit, $sichtbar, $page, $page_size);
|
||||
|
||||
$news = $this->cmslib->getNews($infoscreen, $studiengang_kz, $semester, $mischen, $titel, $edit, $sichtbar, $page, $page_size, $sprache);
|
||||
$news = $this->getDataOrTerminateWithError($news);
|
||||
|
||||
$this->addMeta('test', $this->p->t('global', 'studiengangsleitung'));
|
||||
$this->addMeta('phrases', json_decode($this->p->getJson()));
|
||||
$this->terminateWithSuccess($news);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ class Lehre extends FHCAPI_Controller
|
||||
parent::__construct([
|
||||
'lvStudentenMail' => self::PERM_LOGGED,
|
||||
'LV' => self::PERM_LOGGED,
|
||||
'Pruefungen' => self::PERM_LOGGED,
|
||||
]);
|
||||
|
||||
|
||||
@@ -76,6 +77,23 @@ class Lehre extends FHCAPI_Controller
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* fetches all Pruefungen of a student for a specific lehrveranstaltung
|
||||
* if the student passed the Pruefung on the first attempt, no information about the Pruefungen is stored in the database
|
||||
* @param mixed $lehrveranstaltung_id
|
||||
* @return void
|
||||
*/
|
||||
public function Pruefungen($lehrveranstaltung_id)
|
||||
{
|
||||
$this->load->model('education/Pruefung_model', 'PruefungModel');
|
||||
|
||||
$result = $this->PruefungModel->getByStudentAndLv(getAuthUID(), $lehrveranstaltung_id, getUserLanguage());
|
||||
|
||||
$result = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -58,6 +58,22 @@ class LvMenu extends FHCAPI_Controller
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
|
||||
|
||||
/**
|
||||
* alternative function to get multiple lvMenus with a single http request
|
||||
*/
|
||||
public function getMultipleLvMenu($lvMenuOptionList){
|
||||
$result =[];
|
||||
foreach($lvMenuOptionList as $lvMenuOptions){
|
||||
$lvMenu = $this->getLvMenu($lvMenuOptions['lvid'],$lvMenuOptions['studiensemester_kurzbz']);
|
||||
if(isError($lvMenu)){
|
||||
// TODO: some lvMenu threw an error, handle error here
|
||||
}
|
||||
$result[$lvMenuOptions['lvid']]=$lvMenu;
|
||||
}
|
||||
$this->terminateWithSuccess($result);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -91,20 +107,24 @@ class LvMenu extends FHCAPI_Controller
|
||||
$lvres = $this->Lehrveranstaltung_model->load($lvid);
|
||||
if(!hasData($lvres))
|
||||
{
|
||||
$this->terminateWithError('LV ' . $lvid . ' not found.');
|
||||
$this->terminateWithError('LV ' . $lvid . ' not found.');
|
||||
}
|
||||
$lv = (getData($lvres))[0];
|
||||
|
||||
$this->addMeta('lvInfo',$lv);
|
||||
// define studiengang_kz / semester / lehrverzeichnis
|
||||
$studiengang_kz = $lv->studiengang_kz;
|
||||
$semester = $lv->semester;
|
||||
$short = $lv->lehreverzeichnis;
|
||||
// return empty menu for studiengang_kz = 0
|
||||
if($studiengang_kz == 0){
|
||||
$this->terminateWithSuccess("organisatorische_einheit");
|
||||
}
|
||||
|
||||
// load studiengang
|
||||
$stgres = $this->Studiengang_model->load($lv->studiengang_kz);
|
||||
$stgres = $this->Studiengang_model->load(strval($studiengang_kz));
|
||||
if(!hasData($stgres))
|
||||
{
|
||||
$this->terminateWithError('Stg ' . $lv->studiengang_kz . ' nof found.');
|
||||
$this->terminateWithError('Stg ' . $lv->studiengang_kz . ' not found.');
|
||||
}
|
||||
$stg = (getData($stgres))[0];
|
||||
$kurzbz = strtoupper($stg->typ . $stg->kurzbz);
|
||||
@@ -284,24 +304,6 @@ class LvMenu extends FHCAPI_Controller
|
||||
|
||||
}
|
||||
|
||||
|
||||
private function fhc_menu_digitale_anwesenheiten(&$menu, $angemeldet, $studiengang_kz, $semester, $lvid, $angezeigtes_stsem){
|
||||
|
||||
// DIGITALE ANWESENHEITEN
|
||||
if (defined('CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN') && CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN && $angemeldet) {
|
||||
|
||||
$menu[] = array
|
||||
(
|
||||
'id' => 'core_menu_digitale_anwesenheitslisten',
|
||||
'position' => '50',
|
||||
'name' => $this->p->t('lehre', 'digiAnw'),
|
||||
'c4_icon' => base_url('skin/images/button_kreuzerltool.png'),
|
||||
'c4_link' => base_url("index.ci.php/extensions/FHC-Core-Anwesenheiten/?stg_kz=$studiengang_kz&sem=$semester&lvid=$lvid&sem_kurzbz=$angezeigtes_stsem&nav=false"),
|
||||
'c4_linkList' => []
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function fhc_menu_lvinfo(&$menu, $lvid, $studiengang_kz, $lektor_der_lv, $is_lector, $lehrfach_oe_kurzbz_arr){
|
||||
|
||||
// LVINFO
|
||||
|
||||
@@ -73,11 +73,22 @@ class Phrasen extends FHCAPI_Controller
|
||||
// gets all languages that are set as active in the database
|
||||
public function getAllLanguages()
|
||||
{
|
||||
$langs = getDBActiveLanguages();
|
||||
$this->load->model('system/Sprache_model', 'SprachenModel');
|
||||
|
||||
// Add order clause by index and select the sprache,bezeichnung and index column
|
||||
$this->SprachenModel->addOrder('index');
|
||||
$this->SprachenModel->addSelect('sprache, bezeichnung, index');
|
||||
|
||||
// Retrieves from public.tbl_sprache
|
||||
$langs = $this->SprachenModel->loadWhere(array('content' => true));
|
||||
$langs = $this->getDataOrTerminateWithError($langs);
|
||||
$langs = array_map(function($lang){
|
||||
return $lang->sprache;
|
||||
$data = new stdClass();
|
||||
$data->sprache = $lang->sprache;
|
||||
$data->bezeichnung = $lang->bezeichnung[($lang->index-1)];
|
||||
return $data;
|
||||
}, $langs);
|
||||
|
||||
$this->terminateWithSuccess($langs);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?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 Studgang extends FHCAPI_Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Object initialization
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'getStudiengangInfo'=> self::PERM_LOGGED,
|
||||
|
||||
]);
|
||||
|
||||
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||
|
||||
// Loads phrases system
|
||||
$this->loadPhrases([
|
||||
'global'
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
|
||||
public function getStudiengangInfo(){
|
||||
// fetches the Studiengang Information which is used next the the news
|
||||
$studiengangInfo = $this->StudiengangModel->getStudiengangInfoForNews();
|
||||
$studiengangInfo= $this->getDataOrTerminateWithError($studiengangInfo);
|
||||
$this->terminateWithSuccess($studiengangInfo);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
// Private methods
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -126,24 +126,33 @@ class Stundenplan extends FHCAPI_Controller
|
||||
// storing the get parameter in local variables
|
||||
$start_date = $this->input->get('start_date', TRUE);
|
||||
$end_date = $this->input->get('end_date', TRUE);
|
||||
$lv_id = $this->input->get('lv_id', TRUE);
|
||||
|
||||
$student_uid = getAuthUID();
|
||||
if(is_null($student_uid))
|
||||
{
|
||||
$this->terminateWithError("No UID");
|
||||
}
|
||||
|
||||
$semester_range = $this->studienSemesterErmitteln($start_date,$end_date);
|
||||
$this->sortStudienSemester($semester_range);
|
||||
$this->applyLoadUeberSemesterHaelfte($semester_range);
|
||||
|
||||
if($lv_id) { // fetch Stundenplan for lva, irrelevant of who is requesting it (for now)
|
||||
|
||||
$stundenplan_data = $this->StundenplanModel->getStundenplanLVA($start_date, $end_date, $lv_id);
|
||||
$stundenplan_data = $this->getDataOrTerminateWithError($stundenplan_data) ?? [];
|
||||
$this->expand_object_information($stundenplan_data);
|
||||
$this->terminateWithSuccess($stundenplan_data);
|
||||
|
||||
}
|
||||
|
||||
$is_mitarbeiter = getData($this->MitarbeiterModel->isMitarbeiter($student_uid));
|
||||
if($is_mitarbeiter)
|
||||
{
|
||||
$this->terminateWithError("Not possible to look at the Student Calendar as a Mitarbeiter");
|
||||
}
|
||||
|
||||
$semester_range = $this->studienSemesterErmitteln($start_date,$end_date);
|
||||
|
||||
$this->sortStudienSemester($semester_range);
|
||||
|
||||
$this->applyLoadUeberSemesterHaelfte($semester_range);
|
||||
|
||||
// getting the gruppen_kurzbz of the student in the different studiensemester
|
||||
$benutzer_gruppen = $this->fetchBenutzerGruppenFromStudiensemester($semester_range);
|
||||
|
||||
@@ -87,18 +87,28 @@ function generateCSSsInclude($CSSs)
|
||||
*/
|
||||
function generateJSDataStorageObject($indexPage, $calledPath, $calledMethod)
|
||||
{
|
||||
$ci =& get_instance();
|
||||
$ci->load->model('system/Sprache_model','SpracheModel');
|
||||
$server_language = getData($ci->SpracheModel->loadWhere(['content' => true]));
|
||||
$server_language = array_map(function($language){
|
||||
return ['sprache'=>$language->sprache, 'LC_Time'=>$language->locale, 'bezeichnung'=>$language->bezeichnung[$language->index-1]];
|
||||
}, $server_language);
|
||||
$user_language = getUserLanguage();
|
||||
|
||||
$FHC_JS_DATA_STORAGE_OBJECT = array(
|
||||
'app_root' => APP_ROOT,
|
||||
'ci_router' => $indexPage,
|
||||
'called_path' => $calledPath,
|
||||
'called_method' => $calledMethod,
|
||||
'server_languages' => $server_language,
|
||||
'user_language' => $user_language,
|
||||
'timezone' => date_default_timezone_get(),
|
||||
);
|
||||
|
||||
$toPrint = "\n";
|
||||
$toPrint .= '<script type="text/javascript">';
|
||||
$toPrint .= '
|
||||
var FHC_JS_DATA_STORAGE_OBJECT = {
|
||||
app_root: "'.APP_ROOT.'",
|
||||
ci_router: "'.$indexPage.'",
|
||||
called_path: "'.$calledPath.'",
|
||||
called_method: "'.$calledMethod.'",
|
||||
user_language: "'.$user_language.'"
|
||||
};';
|
||||
var FHC_JS_DATA_STORAGE_OBJECT = '.json_encode($FHC_JS_DATA_STORAGE_OBJECT).';';
|
||||
$toPrint .= "\n";
|
||||
$toPrint .= '</script>';
|
||||
$toPrint .= "\n\n";
|
||||
|
||||
@@ -43,7 +43,7 @@ class CmsLib
|
||||
* @param string $sprache
|
||||
* @param boolean $sichtbar
|
||||
*
|
||||
* @return void
|
||||
* @return stdClass
|
||||
*/
|
||||
public function getContent($content_id, $version = null, $sprache = null, $sichtbar = true)
|
||||
{
|
||||
@@ -105,7 +105,19 @@ class CmsLib
|
||||
if($content->titel){
|
||||
$betreff = $content->titel;
|
||||
}else{
|
||||
//DomDocument getElementsByTagName returns a DomNodeList
|
||||
$betreff = $XML->getElementsByTagName('betreff');
|
||||
//check if any betreff was found and if it is not empty
|
||||
if($betreff->length > 0 && !empty($betreff->item(0)->nodeValue))
|
||||
{
|
||||
//DomNodeList item() return a DomNode, property nodeValue contains the value of the node
|
||||
$betreff = $betreff->item(0)->nodeValue;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return error('no betreff found for the content');
|
||||
}
|
||||
}
|
||||
|
||||
$xsltemplate = new DOMDocument();
|
||||
@@ -221,7 +233,7 @@ class CmsLib
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true, $page = 1, $page_size = 10)
|
||||
public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true, $page = 1, $page_size = 10, $sprache)
|
||||
{
|
||||
$this->ci->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||
list($studiengang_kz, $semester) = $this->getStgAndSem($studiengang_kz, $semester);
|
||||
@@ -230,13 +242,13 @@ class CmsLib
|
||||
$xml = '<?xml version="1.0" encoding="UTF-8"?><content>';
|
||||
|
||||
$this->ci->load->model('content/News_model', 'NewsModel');
|
||||
$news = $this->ci->NewsModel->getNewsWithContent(getSprache(), $studiengang_kz, $semester, null, $sichtbar, 0, $page, $page_size, $all, $mischen);
|
||||
$news = $this->ci->NewsModel->getNewsWithContent($sprache, $studiengang_kz, $semester, null, $sichtbar, 0, $page, $page_size, $all, $mischen);
|
||||
|
||||
if (isError($news))
|
||||
return $news;
|
||||
|
||||
$news = getData($news);
|
||||
//var_dump($news->maxPageCount);
|
||||
|
||||
foreach ($news as $newsobj) {
|
||||
if ($studiengang_kz && $edit && !$newsobj->studiengang_kz)
|
||||
continue;
|
||||
@@ -247,7 +259,7 @@ class CmsLib
|
||||
$xml .= "<newswrapper>" . $newsobj->content . $datum . $id . "</newswrapper>";
|
||||
}
|
||||
|
||||
if ($studiengang_kz != 0) {
|
||||
/* if ($studiengang_kz != 0) {
|
||||
$stg_obj = $this->ci->StudiengangModel->load($studiengang_kz);
|
||||
if (isError($stg_obj))
|
||||
return $stg_obj;
|
||||
@@ -262,7 +274,7 @@ class CmsLib
|
||||
}
|
||||
$xml .= '<studiengang_bezeichnung><![CDATA[' . $stg_obj->bezeichnung . ']]></studiengang_bezeichnung>';
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
if ($titel != '') {
|
||||
$xml .= '<news_titel>' . $titel . '</news_titel>';
|
||||
|
||||
@@ -21,7 +21,7 @@ class News_model extends DB_Model
|
||||
{
|
||||
$this->addJoin("campus.tbl_content","content_id");
|
||||
return $this->execReadOnlyQuery('
|
||||
SELECT *
|
||||
SELECT *, TO_CHAR(campus.tbl_news.datum, ?) as datumformatted
|
||||
FROM campus.tbl_news
|
||||
JOIN campus.tbl_content content ON content.content_id = campus.tbl_news.content_id
|
||||
WHERE
|
||||
@@ -29,7 +29,7 @@ class News_model extends DB_Model
|
||||
datum <= NOW() AND (datum_bis IS NULL OR datum_bis >= now()::date)
|
||||
ORDER BY datum DESC
|
||||
LIMIT ' . $this->escape($limit)
|
||||
);
|
||||
, ['DD/MM/YYYY']);
|
||||
}
|
||||
|
||||
public function getNewsContentIDs($limit=10){
|
||||
|
||||
@@ -609,7 +609,11 @@ class Lehrveranstaltung_model extends DB_Model
|
||||
$this->addSelect($lvbezeichnung . ' AS bezeichnung');
|
||||
$this->addSelect($sgbezeichnung . ' AS sg_bezeichnung');
|
||||
$this->addSelect('UPPER(sg.typ::VARCHAR(1) || sg.kurzbz) AS studiengang_kuerzel');
|
||||
|
||||
|
||||
//also adds returns the index of the grade
|
||||
//TODO: ist zeugnissnote immer gleich wie die lvgesamtnote
|
||||
$this->addSelect('COALESCE(zn.note::numeric,gn.note::numeric) as note_index');
|
||||
$this->addSelect('COALESCE(znn.positiv,gnn.positiv) as positiv');
|
||||
$this->addSelect('COALESCE(gnn.' . $bezeichnung . ', gnn.bezeichnung, gn.note::text) AS lvnote');
|
||||
$this->addSelect('COALESCE(znn.' . $bezeichnung . ', znn.bezeichnung, zn.note::text) AS znote');
|
||||
|
||||
|
||||
@@ -647,4 +647,139 @@ class Studiengang_model extends DB_Model
|
||||
|
||||
return $this->load();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return stdClass
|
||||
*/
|
||||
public function getStudiengangInfoForNews()
|
||||
{
|
||||
|
||||
$this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
|
||||
$this->load->model('person/Person_model', 'PersonModel');
|
||||
|
||||
$addEmailProperty= function(&$benutzerfunktionen){
|
||||
if(count($benutzerfunktionen) && defined('DOMAIN'))
|
||||
{
|
||||
$benutzerfunktionen = array_map(function($benutzer)
|
||||
{
|
||||
$benutzer->email = $benutzer->alias."@".DOMAIN;
|
||||
return $benutzer;
|
||||
},$benutzerfunktionen) ;
|
||||
}
|
||||
|
||||
};
|
||||
$addFotoProperty= function(&$collection){
|
||||
$collection = array_map(function($item){
|
||||
$person_id = $this->PersonModel->getByUid($item->uid);
|
||||
if(isError($person_id))
|
||||
return error($person_id);
|
||||
$person_id = current(getData($person_id))->person_id;
|
||||
$this->PersonModel->addSelect('foto');
|
||||
$foto = $this->PersonModel->loadWhere(array('person_id'=>$person_id));
|
||||
if(isError($foto))
|
||||
return error($foto);
|
||||
$foto = current(getData($foto))->foto;
|
||||
$item->foto = $foto;
|
||||
return $item;
|
||||
},$collection);
|
||||
};
|
||||
|
||||
|
||||
$this->load->model('crm/Student_model', 'StudentModel');
|
||||
|
||||
$student = $this->StudentModel->loadWhere(['student_uid' => getAuthUID()]);
|
||||
if (isError($student))
|
||||
return error($student);
|
||||
if (getData($student)) {
|
||||
//TODO: if a mitarbeiter requests this site, empty data should be returned
|
||||
if(count($student) == 0)
|
||||
{
|
||||
return new stdClass();
|
||||
}
|
||||
$student = current(getData($student));
|
||||
$studiengang_kz = $student->studiengang_kz;
|
||||
$semester = $student->semester;
|
||||
}
|
||||
|
||||
$stg_obj = $this->load($studiengang_kz);
|
||||
if(isError($stg_obj))
|
||||
return error($stg_obj);
|
||||
if(getData($stg_obj))
|
||||
{
|
||||
$stg_obj = current(getData($stg_obj));
|
||||
}
|
||||
|
||||
$stg_ltg = $this->getLeitungDetailed($stg_obj->studiengang_kz);
|
||||
if (isError($stg_ltg))
|
||||
return $stg_ltg;
|
||||
$stg_ltg = getData($stg_ltg) ?: [];
|
||||
$addFotoProperty($stg_ltg);
|
||||
|
||||
$gf_ltg = $this->BenutzerfunktionModel->getBenutzerFunktionenDetailed('gLtg', $stg_obj->oe_kurzbz);
|
||||
if (isError($gf_ltg))
|
||||
return $gf_ltg;
|
||||
$gf_ltg = getData($gf_ltg) ?: [];
|
||||
$addEmailProperty($gf_ltg);
|
||||
|
||||
$stv_ltg = $this->BenutzerfunktionModel->getBenutzerFunktionenDetailed('stvLtg', $stg_obj->oe_kurzbz);
|
||||
if (isError($stv_ltg))
|
||||
return $stv_ltg;
|
||||
$stv_ltg = getData($stv_ltg) ?: [];
|
||||
$addEmailProperty($stv_ltg);
|
||||
|
||||
$ass = $this->BenutzerfunktionModel->getBenutzerFunktionenDetailed('ass', $stg_obj->oe_kurzbz);
|
||||
if (isError($ass))
|
||||
return $ass;
|
||||
$ass = getData($ass) ?: [];
|
||||
$addEmailProperty($ass);
|
||||
$addFotoProperty($ass);
|
||||
|
||||
$hochschulvertr = $this->BenutzerfunktionModel->getBenutzerFunktionenDetailed('hsv');
|
||||
if (isError($hochschulvertr))
|
||||
return $hochschulvertr;
|
||||
$hochschulvertr = getData($hochschulvertr) ?: [];
|
||||
$addEmailProperty($hochschulvertr);
|
||||
|
||||
|
||||
$stdv = $this->BenutzerfunktionModel->getBenutzerFunktionenDetailed('stdv', $stg_obj->oe_kurzbz);
|
||||
if (isError($stdv))
|
||||
return $stdv;
|
||||
$stdv = getData($stdv) ?: [];
|
||||
$addEmailProperty($stdv);
|
||||
|
||||
|
||||
$jahrgangsvertr = $this->BenutzerfunktionModel->getBenutzerFunktionenDetailed('jgv', $stg_obj->oe_kurzbz, $semester);
|
||||
if (isError($jahrgangsvertr))
|
||||
return $jahrgangsvertr;
|
||||
$jahrgangsvertr = getData($jahrgangsvertr) ?: [];
|
||||
$addEmailProperty($jahrgangsvertr);
|
||||
|
||||
|
||||
$result_object = new stdClass();
|
||||
$result_object->studiengang = $stg_obj;
|
||||
$result_object->semester = $semester;
|
||||
$result_object->stg_ltg = $stg_ltg;
|
||||
$result_object->gf_ltg = $gf_ltg;
|
||||
$result_object->stv_ltg = $stv_ltg;
|
||||
$result_object->ass = $ass;
|
||||
$result_object->hochschulvertr = $hochschulvertr;
|
||||
$result_object->stdv = $stdv;
|
||||
$result_object->jahrgangsvertr = $jahrgangsvertr;
|
||||
|
||||
return success($result_object);
|
||||
}
|
||||
|
||||
public function getLvaForStudiengangInStudiensemester($studiengang_kz, $orgform_kurzbz, $studiensemester_kurzbz) {
|
||||
$qry = '
|
||||
SELECT DISTINCT ON (lehre.tbl_lehrveranstaltung.lehrveranstaltung_id,
|
||||
kurzbz, bezeichnung, semester,
|
||||
lehre.tbl_lehrveranstaltung.sprache, orgform_kurzbz,
|
||||
lehre.tbl_lehrveranstaltung.lehrform_kurzbz)
|
||||
lehre.tbl_lehrveranstaltung.lehrveranstaltung_id, kurzbz, bezeichnung,
|
||||
semester, lehre.tbl_lehrveranstaltung.sprache, orgform_kurzbz, lehre.tbl_lehrveranstaltung.lehrform_kurzbz
|
||||
FROM lehre.tbl_lehrveranstaltung JOIN lehre.tbl_lehreinheit USING(lehrveranstaltung_id) JOIN lehre.tbl_lehreinheitmitarbeiter USING(lehreinheit_id)
|
||||
WHERE aktiv = TRUE AND studiengang_kz = ? AND orgform_kurzbz = ? AND tbl_lehreinheit.studiensemester_kurzbz IN ?';
|
||||
|
||||
return $this->execReadOnlyQuery($qry, array($studiengang_kz, $orgform_kurzbz, $studiensemester_kurzbz));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,9 +186,63 @@ class Stundenplan_model extends DB_Model
|
||||
return $query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* queries Stundenplan but for a whole lva, irrespective of who is requesting it
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getStundenplanLVA($start_date, $end_date, $lv_id) {
|
||||
return $this->execReadOnlyQuery("
|
||||
|
||||
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 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,
|
||||
ort_kurzbz, studiengang_kz, titel,lehreinheit_id,lehrfach_id,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 <= ? AND sp.lehrveranstaltung_id = ?
|
||||
) sp
|
||||
JOIN lehre.tbl_stunde ON lehre.tbl_stunde.stunde = sp.stunde
|
||||
|
||||
) as subquery
|
||||
|
||||
GROUP BY unr, datum, beginn, ende, ort_kurzbz, titel, lehrform, lehrfach, lehrfach_bez, organisationseinheit, farbe, lehrveranstaltung_id
|
||||
|
||||
ORDER BY datum, beginn
|
||||
", [$start_date, $end_date, $lv_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* NO STANDALONE FUNCTION - Generates a SQL query string to fetch 'stundenplan' events for a specific student within the current semester.
|
||||
* @param string $uid the user id that is used to fetch the stundenplan rows from the lehre.vw_stundenplan table
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
$includesArray =array(
|
||||
'title' => 'Infoterminal',
|
||||
'tabulator5' => true,
|
||||
'primevue3' => true,
|
||||
);
|
||||
$this->load->view(
|
||||
'templates/CISVUE-Header',
|
||||
$includesArray
|
||||
);
|
||||
?>
|
||||
|
||||
<iframe style="width:100%; height:100%;" id="Infoterminal" src="<?php echo base_url() . 'cis/infoterminal/'; ?>" name="Infoterminal" frameborder="0" >
|
||||
No iFrames
|
||||
</iframe>
|
||||
<?php $this->load->view('templates/CISVUE-Footer', $includesArray); ?>
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
$includesArray = array(
|
||||
'title' => 'MyLv',
|
||||
'customJSModules' => ['public/js/apps/Cis/MyLv/Student.js'],
|
||||
'customCSSs' => ['public/css/components/MyLv.css']
|
||||
);
|
||||
|
||||
$this->load->view('templates/CISVUE-Header', $includesArray);
|
||||
?>
|
||||
|
||||
<div id="content">
|
||||
<mylv-student></mylv-student>
|
||||
</div>
|
||||
|
||||
<?php $this->load->view('templates/CISVUE-Footer', $includesArray); ?>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
$includesArray = array(
|
||||
'title' => 'Stundenplan',
|
||||
'title' => 'Profil',
|
||||
'customJSModules' => ['public/js/apps/Cis/Profil.js'],
|
||||
'tabulator5' => true,
|
||||
'primevue3' => true,
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
$includesArray = array(
|
||||
'title' => 'Stundenplan',
|
||||
'customJSModules' => ['public/js/apps/Cis/Stundenplan.js'],
|
||||
'customCSSs' => ['public/css/components/calendar.css']
|
||||
);
|
||||
|
||||
$this->load->view('templates/CISVUE-Header', $includesArray);
|
||||
?>
|
||||
|
||||
<div id="content">
|
||||
|
||||
</div>
|
||||
|
||||
<?php $this->load->view('templates/CISVUE-Footer', $includesArray); ?>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
$includesArray = array(
|
||||
'title' => 'Cis4',
|
||||
'axios027' => true,
|
||||
'bootstrap5' => true,
|
||||
'fontawesome6' => true,
|
||||
'tabulator5' => true,
|
||||
'vue3' => true,
|
||||
'primevue3' => true,
|
||||
'customCSSs' => array(
|
||||
'public/css/components/verticalsplit.css',
|
||||
'public/css/components/searchbar.css',
|
||||
'public/css/Fhc.css',
|
||||
'public/css/components/dashboard.css',
|
||||
'public/css/components/calendar.css',
|
||||
'public/css/components/MyLv.css',
|
||||
'public/css/components/FilterComponent.css',
|
||||
'public/css/components/Profil.css',
|
||||
'public/css/components/FormUnderline.css',
|
||||
'public/css/Cis4/Cms.css',
|
||||
),
|
||||
'customJSs' => array(
|
||||
'vendor/npm-asset/primevue/accordion/accordion.js',
|
||||
'vendor/npm-asset/primevue/accordiontab/accordiontab.js'
|
||||
),
|
||||
'customJSModules' => array(
|
||||
'public/js/apps/Dashboard/Fhc.js'
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
$this->load->view('templates/CISVUE-Header', $includesArray);
|
||||
?>
|
||||
<div id="fhccontent" route=<?php echo $route ?>>
|
||||
<router-view
|
||||
:view-data='<?php echo json_encode($viewData) ?>'
|
||||
></router-view>
|
||||
</div>
|
||||
<?php $this->load->view('templates/CISVUE-Footer', $includesArray); ?>
|
||||
@@ -18,8 +18,8 @@ if(defined('CIS4')){
|
||||
}
|
||||
?>
|
||||
|
||||
<div id="cms">
|
||||
<?php echo (isset($content_id) ? '<cms-content :content_id="'.$content_id.'" :version="'.$version.'" :sprache="'.$sprache.'" :sichtbar="'.$sichtbar.'" />' : '<cms-news/>'); ?>
|
||||
<div ref="cmsContainer" id="cms">
|
||||
<?php echo (isset($content_id) ? '<router-view ref="cms" :content_id="'.$content_id.'" :version="'.$version.'" :sprache="'.$sprache.'" :sichtbar="'.$sichtbar.'" />' : '<router-view ref="cms" />'); ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
$includesArray = array(
|
||||
'title' => 'Dashboard',
|
||||
'tabulator5'=>true,
|
||||
'primevue3' => true,
|
||||
'customJSModules' => ['public/js/apps/Dashboard/Fhc.js'],
|
||||
'customCSSs' => [
|
||||
'public/css/components/dashboard.css'
|
||||
],
|
||||
);
|
||||
|
||||
$this->load->view('templates/CISVUE-Header', $includesArray);
|
||||
?>
|
||||
|
||||
<div id="content">
|
||||
<fhc-dashboard dashboard="CIS" view-data-string='<?php echo json_encode($viewData) ?>' />
|
||||
</div>
|
||||
|
||||
<?php $this->load->view('templates/CISVUE-Footer', $includesArray); ?>
|
||||
|
||||
@@ -5,6 +5,7 @@ $includesArray = array(
|
||||
'bootstrap5' => true,
|
||||
'fontawesome6' => true,
|
||||
'axios027' => true,
|
||||
'primevue3' => true,
|
||||
'customJSModules' => array_merge([
|
||||
'public/js/apps/Cis.js'
|
||||
], $customJSModules ?? []),
|
||||
@@ -15,15 +16,6 @@ $includesArray = array(
|
||||
|
||||
$this->load->view('templates/FHC-Header', $includesArray);
|
||||
|
||||
|
||||
if (!isset($menu)) {
|
||||
$ci =& get_instance(); // get CI instance
|
||||
$ci->load->model('content/Content_model', 'ContentModel');
|
||||
$ci->load->config('cis');
|
||||
$cis4_content_id = $ci->config->item('cis_menu_root_content_id');
|
||||
$result = $ci->ContentModel->getMenu($cis4_content_id, getAuthUID());
|
||||
$menu = getData($result)->childs ?? [];
|
||||
}
|
||||
?>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -39,7 +31,6 @@ if (!isset($menu)) {
|
||||
logout-url="<?= site_url('Cis/Auth/logout'); ?>"
|
||||
:searchbaroptions="searchbaroptions"
|
||||
:searchfunction="searchfunction"
|
||||
:menu="<?= htmlspecialchars(json_encode(array_values($menu)), ENT_QUOTES, 'UTF-8') ?>"
|
||||
></cis-menu>
|
||||
</header>
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
// Generates the global object to pass phrases to javascripts
|
||||
// NOTE: must be called before including the PhrasesLib.js
|
||||
if ($phrases != null) generateJSPhrasesStorageObject($phrases);
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------
|
||||
// From vendor folder
|
||||
|
||||
@@ -123,6 +123,13 @@
|
||||
}
|
||||
generateJSsInclude('vendor/vuejs/vuerouter4/vue-router.global.js');
|
||||
}
|
||||
|
||||
// Highcharts
|
||||
if (isset($highcharts) && $highcharts === true)
|
||||
{
|
||||
generateJSsInclude('vendor/highcharts/highcharts-dist/highcharts.js');
|
||||
generateJSsInclude('vendor/highcharts/highcharts-dist/modules/current-date-indicator.js');
|
||||
}
|
||||
|
||||
// PrimeVue
|
||||
if ($primevue3)
|
||||
|
||||
+29
-15
@@ -351,23 +351,34 @@
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "vuejs/vuedatepicker_js",
|
||||
"version": "7.2.0",
|
||||
"dist": {
|
||||
"url": "https://unpkg.com/@vuepic/vue-datepicker@7.2.0/dist/vue-datepicker.iife.js",
|
||||
"type": "file"
|
||||
}
|
||||
"name": "vuejs/vuedatepicker_js",
|
||||
"version": "7.2.0",
|
||||
"dist": {
|
||||
"url": "https://unpkg.com/@vuepic/vue-datepicker@7.2.0/dist/vue-datepicker.iife.js",
|
||||
"type": "file"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "vuejs/vuedatepicker_css",
|
||||
"version": "7.2.0",
|
||||
"dist": {
|
||||
"url": "https://unpkg.com/@vuepic/vue-datepicker@7.2.0/dist/main.css",
|
||||
"type": "file"
|
||||
}
|
||||
"name": "vuejs/vuedatepicker_css",
|
||||
"version": "7.2.0",
|
||||
"dist": {
|
||||
"url": "https://unpkg.com/@vuepic/vue-datepicker@7.2.0/dist/main.css",
|
||||
"type": "file"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "highcharts/highcharts-dist",
|
||||
"version": "7.1.2",
|
||||
"dist": {
|
||||
"url": "https://github.com/highcharts/highcharts-dist/archive/v7.1.2.zip",
|
||||
"type": "zip"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -402,6 +413,8 @@
|
||||
"fortawesome/font-awesome6": "6.1.*",
|
||||
"fzaninotto/faker": "1.*",
|
||||
|
||||
"highcharts/highcharts-dist": "^7.1.2",
|
||||
|
||||
"joeldbirch/superfish": "1.7.*",
|
||||
"jquery/jquery1": "1.*",
|
||||
"jquery/jquery2": "2.*",
|
||||
@@ -442,13 +455,14 @@
|
||||
|
||||
"vuejs/vuejs3": "3.3.8",
|
||||
"vuejs/vuerouter4": "4.1.3",
|
||||
"vuejs/vuedatepicker_js": "7.2.0",
|
||||
"vuejs/vuedatepicker_css": "7.2.0"
|
||||
"vuejs/vuedatepicker_js": "7.2.0",
|
||||
"vuejs/vuedatepicker_css": "7.2.0"
|
||||
|
||||
},
|
||||
"config": {
|
||||
"bin-dir": "vendor/bin"
|
||||
},
|
||||
|
||||
|
||||
"require-dev": {
|
||||
"vuejs/vuejs3_dev": "3.3.8",
|
||||
"squizlabs/php_codesniffer": "3.6.*",
|
||||
|
||||
Generated
+74
-63
@@ -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": "01ea35652d88680f8262c5365828eb46",
|
||||
"content-hash": "86d9ee6aa7cbf758f847f7aad13177a2",
|
||||
"packages": [
|
||||
{
|
||||
"name": "afarkas/html5shiv",
|
||||
@@ -322,9 +322,6 @@
|
||||
"extra": {
|
||||
"component": {
|
||||
"name": "angularjs",
|
||||
"scripts": [
|
||||
"angular.js"
|
||||
],
|
||||
"files": [
|
||||
"angular-animate.js",
|
||||
"angular-animate.min.js",
|
||||
@@ -597,6 +594,9 @@
|
||||
"i18n/angular-locale_zh.js",
|
||||
"i18n/angular-locale_zu-za.js",
|
||||
"i18n/angular-locale_zu.js"
|
||||
],
|
||||
"scripts": [
|
||||
"angular.js"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -682,20 +682,20 @@
|
||||
"extra": {
|
||||
"component": {
|
||||
"name": "jquery-ui",
|
||||
"scripts": [
|
||||
"jquery-ui.js"
|
||||
],
|
||||
"files": [
|
||||
"ui/**",
|
||||
"themes/**",
|
||||
"jquery-ui.min.js"
|
||||
],
|
||||
"shim": {
|
||||
"deps": [
|
||||
"jquery"
|
||||
],
|
||||
"exports": "jQuery"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"ui/**",
|
||||
"themes/**",
|
||||
"jquery-ui.min.js"
|
||||
],
|
||||
"scripts": [
|
||||
"jquery-ui.js"
|
||||
]
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -955,6 +955,15 @@
|
||||
"abandoned": true,
|
||||
"time": "2020-12-11T09:56:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "highcharts/highcharts-dist",
|
||||
"version": "7.1.2",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://github.com/highcharts/highcharts-dist/archive/v7.1.2.zip"
|
||||
},
|
||||
"type": "library"
|
||||
},
|
||||
{
|
||||
"name": "joeldbirch/superfish",
|
||||
"version": "1.7.9",
|
||||
@@ -1015,12 +1024,12 @@
|
||||
"version": "1.3.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/justinrainbow/json-schema.git",
|
||||
"url": "https://github.com/jsonrainbow/json-schema.git",
|
||||
"reference": "87b54b460febed69726c781ab67462084e97a105"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/87b54b460febed69726c781ab67462084e97a105",
|
||||
"url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/87b54b460febed69726c781ab67462084e97a105",
|
||||
"reference": "87b54b460febed69726c781ab67462084e97a105",
|
||||
"shasum": ""
|
||||
},
|
||||
@@ -1075,8 +1084,8 @@
|
||||
"schema"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/justinrainbow/json-schema/issues",
|
||||
"source": "https://github.com/justinrainbow/json-schema/tree/master"
|
||||
"issues": "https://github.com/jsonrainbow/json-schema/issues",
|
||||
"source": "https://github.com/jsonrainbow/json-schema/tree/1.3.7"
|
||||
},
|
||||
"time": "2014-08-25T02:48:14+00:00"
|
||||
},
|
||||
@@ -1116,6 +1125,7 @@
|
||||
"issues": "https://github.com/kingsquare/json-schema-form/issues",
|
||||
"source": "https://github.com/kingsquare/json-schema-form/tree/master"
|
||||
},
|
||||
"abandoned": true,
|
||||
"time": "2014-07-10T12:27:19+00:00"
|
||||
},
|
||||
{
|
||||
@@ -1297,16 +1307,16 @@
|
||||
},
|
||||
{
|
||||
"name": "mottie/tablesorter",
|
||||
"version": "v2.31.3",
|
||||
"version": "v2.32.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Mottie/tablesorter.git",
|
||||
"reference": "7202d5faf8105a5ecd1a2b7a653777618713ffe5"
|
||||
"reference": "1423f5408982f58d5baa97648d2e5ee0b55fd3b6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Mottie/tablesorter/zipball/7202d5faf8105a5ecd1a2b7a653777618713ffe5",
|
||||
"reference": "7202d5faf8105a5ecd1a2b7a653777618713ffe5",
|
||||
"url": "https://api.github.com/repos/Mottie/tablesorter/zipball/1423f5408982f58d5baa97648d2e5ee0b55fd3b6",
|
||||
"reference": "1423f5408982f58d5baa97648d2e5ee0b55fd3b6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1315,10 +1325,10 @@
|
||||
"type": "component",
|
||||
"extra": {
|
||||
"component": {
|
||||
"files": "dist/",
|
||||
"scripts": [
|
||||
"dist/js/jquery.tablesorter.combined.js"
|
||||
],
|
||||
"files": "dist/"
|
||||
]
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -1331,7 +1341,8 @@
|
||||
},
|
||||
{
|
||||
"name": "Rob Garrison",
|
||||
"email": "wowmotty@gmail.com"
|
||||
"email": "wowmotty@gmail.com",
|
||||
"homepage": "https://github.com/Mottie"
|
||||
}
|
||||
],
|
||||
"description": "tablesorter (FORK) is a jQuery plugin for turning a standard HTML table with THEAD and TBODY tags into a sortable table without page refreshes. tablesorter can successfully parse and sort many types of data including linked data in a cell.",
|
||||
@@ -1352,7 +1363,7 @@
|
||||
"source": "https://github.com/Mottie/tablesorter",
|
||||
"wiki": "https://github.com/Mottie/tablesorter/wiki"
|
||||
},
|
||||
"time": "2020-03-03T13:46:03+00:00"
|
||||
"time": "2024-08-14T01:23:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nategood/httpful",
|
||||
@@ -1520,16 +1531,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "2.0.46",
|
||||
"version": "2.0.48",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "498e67a0c82bd5791fda9b0dd0f4ec8e8aebb02d"
|
||||
"reference": "eaa7be704b8b93a6913b69eb7f645a59d7731b61"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/498e67a0c82bd5791fda9b0dd0f4ec8e8aebb02d",
|
||||
"reference": "498e67a0c82bd5791fda9b0dd0f4ec8e8aebb02d",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/eaa7be704b8b93a6913b69eb7f645a59d7731b61",
|
||||
"reference": "eaa7be704b8b93a6913b69eb7f645a59d7731b61",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1610,7 +1621,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpseclib/phpseclib/issues",
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/2.0.46"
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/2.0.48"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1626,7 +1637,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-29T01:52:43+00:00"
|
||||
"time": "2024-12-14T21:03:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "rmariuzzo/jquery-checkboxes",
|
||||
@@ -1668,12 +1679,12 @@
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-main": "1.19-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -3273,16 +3284,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/code-unit-reverse-lookup",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
|
||||
"reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619"
|
||||
"reference": "92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619",
|
||||
"reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54",
|
||||
"reference": "92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3316,7 +3327,7 @@
|
||||
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
|
||||
"source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2"
|
||||
"source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3324,7 +3335,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-11-30T08:15:22+00:00"
|
||||
"time": "2024-03-01T13:45:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/comparator",
|
||||
@@ -3682,16 +3693,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/object-enumerator",
|
||||
"version": "3.0.4",
|
||||
"version": "3.0.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/object-enumerator.git",
|
||||
"reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2"
|
||||
"reference": "ac5b293dba925751b808e02923399fb44ff0d541"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2",
|
||||
"reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/ac5b293dba925751b808e02923399fb44ff0d541",
|
||||
"reference": "ac5b293dba925751b808e02923399fb44ff0d541",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3727,7 +3738,7 @@
|
||||
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
|
||||
"source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4"
|
||||
"source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3735,20 +3746,20 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-11-30T07:40:27+00:00"
|
||||
"time": "2024-03-01T13:54:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/object-reflector",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/object-reflector.git",
|
||||
"reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d"
|
||||
"reference": "1d439c229e61f244ff1f211e5c99737f90c67def"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d",
|
||||
"reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/1d439c229e61f244ff1f211e5c99737f90c67def",
|
||||
"reference": "1d439c229e61f244ff1f211e5c99737f90c67def",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3782,7 +3793,7 @@
|
||||
"homepage": "https://github.com/sebastianbergmann/object-reflector/",
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/object-reflector/issues",
|
||||
"source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2"
|
||||
"source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3790,7 +3801,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-11-30T07:37:18+00:00"
|
||||
"time": "2024-03-01T13:56:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/phpcpd",
|
||||
@@ -3849,16 +3860,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/recursion-context",
|
||||
"version": "3.0.1",
|
||||
"version": "3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/recursion-context.git",
|
||||
"reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb"
|
||||
"reference": "9bfd3c6f1f08c026f542032dfb42813544f7d64c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb",
|
||||
"reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/9bfd3c6f1f08c026f542032dfb42813544f7d64c",
|
||||
"reference": "9bfd3c6f1f08c026f542032dfb42813544f7d64c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3900,7 +3911,7 @@
|
||||
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/recursion-context/issues",
|
||||
"source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1"
|
||||
"source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3908,7 +3919,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-11-30T07:34:24+00:00"
|
||||
"time": "2024-03-01T14:07:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/resource-operations",
|
||||
@@ -4530,12 +4541,12 @@
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-main": "1.19-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
||||
@@ -32,6 +32,8 @@ html {
|
||||
--fhc-cis-menu-lvl-5-color: #000;
|
||||
--fhc-cis-menu-lvl-5-bg-hover: #eceff1;
|
||||
--fhc-cis-menu-lvl-5-color-hover: #000;
|
||||
--fhc-cis-grade-positive: rgb(0, 128, 0);
|
||||
--fhc-cis-grade-negative: rgb(128, 0, 0);
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
:root {
|
||||
|
||||
@@ -49,7 +49,8 @@
|
||||
.fhc-calendar-week-page > div {
|
||||
/*transform: translate(-0.75em, -0.75em);*/
|
||||
/*width: calc(100% + 1.5em);*/
|
||||
height: calc(100% + 1.5em);
|
||||
/*height: calc(100% + 1.5em);*/
|
||||
max-height: 100%;
|
||||
}
|
||||
.fhc-calendar-week-page > div > div {
|
||||
padding-left: 3em;
|
||||
@@ -105,7 +106,6 @@
|
||||
.fhc-calendar-day-page .events {
|
||||
display: grid;
|
||||
grid-template-columns: 3em 1fr;
|
||||
margin-left: -3em;
|
||||
/*min-height: 266.6666666667%;*/
|
||||
}
|
||||
.fhc-calendar-day-page .events .day {
|
||||
|
||||
@@ -2,6 +2,45 @@
|
||||
--news-widget-height: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* media query for sm sizes */
|
||||
@media (min-width :576px) {
|
||||
.fhc-news-menu-item {
|
||||
/* padding: top right bottom left */
|
||||
padding:0em 0em 0em 0em !important;
|
||||
}
|
||||
}
|
||||
/* media query for md sizes */
|
||||
@media (min-width :768px) {
|
||||
.fhc-news-menu-item {
|
||||
/* padding: top right bottom left */
|
||||
padding: 0.3em 0.3em 0.3em 0.3em !important;
|
||||
}
|
||||
}
|
||||
/* media query for lg sizes */
|
||||
@media (min-width :992px) {
|
||||
.fhc-news-menu-item {
|
||||
/* padding: top right bottom left */
|
||||
padding: 0.3em 0.3em 0.3em 0.3em !important;
|
||||
}
|
||||
}
|
||||
/* media query for xl sizes */
|
||||
@media (min-width :1200px) {
|
||||
.fhc-news-menu-item {
|
||||
/* padding: top right bottom left */
|
||||
padding: 1em 1em 0.3em 1em !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* media query for xxl sizes */
|
||||
@media (min-width :1400px) {
|
||||
.fhc-news-menu-item {
|
||||
/* padding: top right bottom left */
|
||||
padding: 1.2em 1.2em 0.3em 1.2em !important;
|
||||
}
|
||||
}
|
||||
|
||||
.widgets-news img
|
||||
{
|
||||
max-width: 100%;
|
||||
@@ -17,7 +56,6 @@
|
||||
align-items: center;
|
||||
background-color: #00649c;
|
||||
border: 1px solid #f1f1f1;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, border-color 0.2s ease; /* Smooth transition */
|
||||
}
|
||||
@@ -70,3 +108,35 @@
|
||||
.fhc-carousel .carousel-item-end {
|
||||
transition: transform 0.44s ease-in-out, opacity 0.8s ease-in-out;
|
||||
}
|
||||
|
||||
.carousel-control-prev,
|
||||
.carousel-control-next {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
/* Show the buttons when the carousel is hovered */
|
||||
.carousel:hover .carousel-control-prev,
|
||||
.carousel:hover .carousel-control-next {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.fhc-news-text{
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
.fhc-news-xs {
|
||||
font-size: 0.6em;
|
||||
}
|
||||
|
||||
.fhc-news-sm {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
.fhc-news-md {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.fhc-news-lg {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,16 @@ export default {
|
||||
);
|
||||
},
|
||||
|
||||
update: function ({ bookmark_id, url, title, tag=null}) {
|
||||
return this.$fhcApi.post(
|
||||
`/api/frontend/v1/Bookmark/update/${bookmark_id}`
|
||||
, {
|
||||
url: url,
|
||||
title: title
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
insert: function ({url, title, tag}) {
|
||||
return this.$fhcApi.post(
|
||||
`/api/frontend/v1/Bookmark/insert`
|
||||
|
||||
+20
-10
@@ -11,30 +11,40 @@ export default {
|
||||
);
|
||||
},
|
||||
|
||||
news(limit) {
|
||||
return this.$fhcApi.get(
|
||||
"/api/frontend/v1/Cms/news",
|
||||
{
|
||||
limit: limit
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
getNews(page = 1, page_size = 10) {
|
||||
//api function used for the news View that renders the html
|
||||
getNews(page = 1, page_size = 10, sprache) {
|
||||
return this.$fhcApi.get(
|
||||
"/api/frontend/v1/Cms/getNews",
|
||||
{
|
||||
page,
|
||||
page_size,
|
||||
sprache,
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
//api function used for the widget component
|
||||
news(limit) {
|
||||
return this.$fhcApi.get(
|
||||
"/api/frontend/v1/Cms/news",
|
||||
{
|
||||
limit: limit
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
getNewsRowCount: function () {
|
||||
return this.$fhcApi.get(
|
||||
"/api/frontend/v1/Cms/getNewsRowCount",
|
||||
{}
|
||||
);
|
||||
},
|
||||
|
||||
getNewsExtra: function(){
|
||||
return this.$fhcApi.get(
|
||||
"/api/frontend/v1/Cms/getStudiengangInfoForNews",
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
async getViewData() {
|
||||
const url = `/api/frontend/v1/Cis4FhcApi/getViewData`;
|
||||
return this.$fhcApi.get(url, null, null)
|
||||
},
|
||||
}
|
||||
@@ -33,11 +33,15 @@ import ort from "./ort.js";
|
||||
import cms from "./cms.js";
|
||||
import lehre from "./lehre.js";
|
||||
import addons from "./addons.js";
|
||||
import studiengang from "./studiengang.js";
|
||||
import menu from "./menu.js";
|
||||
import dashboard from "./dashboard";
|
||||
|
||||
export default {
|
||||
search,
|
||||
phrasen,
|
||||
navigation,
|
||||
dashboard,
|
||||
filter,
|
||||
studstatus,
|
||||
profil,
|
||||
@@ -52,5 +56,7 @@ export default {
|
||||
ort,
|
||||
cms,
|
||||
lehre,
|
||||
addons
|
||||
addons,
|
||||
studiengang,
|
||||
menu,
|
||||
};
|
||||
|
||||
@@ -13,4 +13,10 @@ export default {
|
||||
, {}
|
||||
);
|
||||
},
|
||||
getStudentPruefungen(lehrveranstaltung_id){
|
||||
return this.$fhcApi.get(
|
||||
`/api/frontend/v1/Lehre/Pruefungen/${lehrveranstaltung_id}`
|
||||
, {}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export default {
|
||||
getMenu: function () {
|
||||
return this.$fhcApi.get(
|
||||
"/api/frontend/v1/CisMenu/getMenu",
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export default {
|
||||
studiengangInformation: function () {
|
||||
return this.$fhcApi.get(
|
||||
"/api/frontend/v1/Studgang/getStudiengangInfo",
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,10 +6,10 @@ export default {
|
||||
{ ort_kurzbz, start_date, end_date}
|
||||
);
|
||||
},
|
||||
getStundenplan(start_date, end_date) {
|
||||
getStundenplan(start_date, end_date, lv_id) {
|
||||
return this.$fhcApi.get(
|
||||
'/api/frontend/v1/Stundenplan/getStundenplan',
|
||||
{ start_date, end_date }
|
||||
{ start_date, end_date, lv_id }
|
||||
);
|
||||
},
|
||||
getStunden() {
|
||||
|
||||
@@ -119,5 +119,11 @@ const app = Vue.createApp({
|
||||
}
|
||||
});
|
||||
app.use(FhcApi);
|
||||
app.use(primevue.config.default, {
|
||||
zIndex: {
|
||||
overlay: 9000,
|
||||
tooltip: 8000
|
||||
}
|
||||
})
|
||||
app.use(Phrasen);
|
||||
app.mount('#cis-header');
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import BsConfirm from "../../components/Bootstrap/Confirm.js";
|
||||
import CmsNews from "../../components/Cis/Cms/News.js";
|
||||
import CmsContent from "../../components/Cis/Cms/Content.js";
|
||||
import Phrasen from "../../plugin/Phrasen.js";
|
||||
import {setScrollbarWidth} from "../../helpers/CssVarCalcHelpers";
|
||||
|
||||
|
||||
const app = Vue.createApp({
|
||||
name: 'CmsApp',
|
||||
components: {
|
||||
CmsNews,
|
||||
CmsContent,
|
||||
},
|
||||
mounted() {
|
||||
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));
|
||||
})
|
||||
.catch(() => {});
|
||||
});
|
||||
});
|
||||
document.querySelectorAll("#cms [data-href]").forEach((el) => {
|
||||
el.href = el.dataset.href.replace(
|
||||
/^ROOT\//,
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root
|
||||
);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
setScrollbarWidth();
|
||||
|
||||
app.use(primevue.config.default, { zIndex: { overlay: 9999 } });
|
||||
app.use(Phrasen, {reload: true});
|
||||
app.mount("#cms");
|
||||
//#cms [data-confirm], #cms [data-href]
|
||||
@@ -89,5 +89,5 @@ const app = Vue.createApp({
|
||||
|
||||
setScrollbarWidth();
|
||||
|
||||
app.use(Phrasen, {reload: true});
|
||||
app.use(Phrasen);
|
||||
app.mount('#content');
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import Info from "../../components/Cis/Mylv/Semester/Studiengang/Lv/Info.js";
|
||||
import Phrasen from "../../plugin/Phrasen.js";
|
||||
import {setScrollbarWidth} from "../../helpers/CssVarCalcHelpers";
|
||||
|
||||
const app = Vue.createApp({
|
||||
components: {
|
||||
Info
|
||||
}
|
||||
})
|
||||
|
||||
setScrollbarWidth();
|
||||
|
||||
app.use(Phrasen, { reload: true }).mount('#content');
|
||||
@@ -1,14 +0,0 @@
|
||||
import MylvStudent from "../../../components/Cis/Mylv/Student.js";
|
||||
import Phrasen from "../../../plugin/Phrasen.js";
|
||||
import {setScrollbarWidth} from "../../../helpers/CssVarCalcHelpers";
|
||||
|
||||
const app = Vue.createApp({
|
||||
name: 'MyLvStudentApp',
|
||||
components: {
|
||||
MylvStudent
|
||||
}
|
||||
})
|
||||
|
||||
setScrollbarWidth();
|
||||
|
||||
app.use(Phrasen, {reload: true}).mount('#content');
|
||||
@@ -1,6 +1,8 @@
|
||||
import ProfilUpdateView from "../../components/Cis/ProfilUpdate/ProfilUpdateView.js";
|
||||
import Phrasen from "../../plugin/Phrasen.js";
|
||||
|
||||
|
||||
// TODO: sobald in verwendung den vue router pfad zu ProfilUpdateView definieren und diese app in component auslagern
|
||||
const app = Vue.createApp({
|
||||
name: 'ProfilUpdateRequestsApp',
|
||||
components: {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import {CoreNavigationCmpt} from '../components/navigation/Navigation.js';
|
||||
import CoreDashboard from '../components/Dashboard/Dashboard.js';
|
||||
import FhcApi from '../../plugin/FhcApi.js';
|
||||
import Phrasen from '../../plugin/Phrasen.js';
|
||||
|
||||
const app = Vue.createApp({
|
||||
name: 'DashboardApp',
|
||||
data: () => ({
|
||||
appSideMenuEntries: {}
|
||||
}),
|
||||
components: {
|
||||
CoreNavigationCmpt,
|
||||
CoreDashboard
|
||||
}
|
||||
})
|
||||
app.use(FhcApi);
|
||||
app.use(Phrasen);
|
||||
app.mount('#main');
|
||||
+112
-10
@@ -1,20 +1,122 @@
|
||||
import FhcDashboard from '../../components/Dashboard/Dashboard.js';
|
||||
import FhcApi from '../../plugin/FhcApi.js';
|
||||
import Phrasen from '../../plugin/Phrasen.js';
|
||||
import { setScrollbarWidth } from "../../helpers/CssVarCalcHelpers";
|
||||
import {setScrollbarWidth} from "../../helpers/CssVarCalcHelpers";
|
||||
import Stundenplan from "../../components/Cis/Stundenplan/Stundenplan";
|
||||
import MylvStudent from "../../components/Cis/Mylv/Student";
|
||||
import Profil from "../../components/Cis/Profil/Profil";
|
||||
import CmsNews from "../../components/Cis/Cms/News";
|
||||
import CmsContent from "../../components/Cis/Cms/Content";
|
||||
import Info from "../../components/Cis/Mylv/Semester/Studiengang/Lv/Info";
|
||||
|
||||
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/Profil/View/:uid`,
|
||||
name: 'ProfilView',
|
||||
component: Profil,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Profil`,
|
||||
name: 'Profil',
|
||||
component: Profil,
|
||||
props: true
|
||||
},
|
||||
|
||||
{
|
||||
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`,
|
||||
name: 'MyLv',
|
||||
component: MylvStudent,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/MyLv/Info/:studien_semester/:lehrveranstaltung_id`,
|
||||
name: 'LvInfo',
|
||||
component: Info,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: `/Cis/Stundenplan/:lv_id?`,
|
||||
name: 'Stundenplan',
|
||||
component: Stundenplan
|
||||
},
|
||||
{
|
||||
path: `/`,
|
||||
name: 'FhcDashboard',
|
||||
component: FhcDashboard,
|
||||
props: {dashboard: 'CIS'},
|
||||
},
|
||||
{
|
||||
path: `/Cis4`,
|
||||
name: 'Cis4',
|
||||
component: FhcDashboard,
|
||||
props: {dashboard: 'CIS'},
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const app = Vue.createApp({
|
||||
name: 'FhcApp',
|
||||
data: () => ({
|
||||
appSideMenuEntries: {}
|
||||
}),
|
||||
components: {
|
||||
FhcDashboard
|
||||
}
|
||||
name: 'FhcApp',
|
||||
data: () => ({
|
||||
appSideMenuEntries: {}
|
||||
}),
|
||||
components: {},
|
||||
methods: {
|
||||
isInternalRoute(href) {
|
||||
const internalBase = window.location.origin
|
||||
return href.startsWith(internalBase);
|
||||
},
|
||||
handleClick(event) {
|
||||
const target = event.target.closest('a');
|
||||
|
||||
if (target && this.isInternalRoute(target.href)) {
|
||||
|
||||
const path = new URL(target.href).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) return
|
||||
|
||||
event.preventDefault(); // Prevent browser navigation
|
||||
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.use(router);
|
||||
app.use(FhcApi);
|
||||
app.use(primevue.config.default, {
|
||||
zIndex: {
|
||||
overlay: 9000,
|
||||
tooltip: 8000
|
||||
}
|
||||
})
|
||||
app.use(Phrasen);
|
||||
app.mount('#content');
|
||||
app.mount('#fhccontent');
|
||||
@@ -21,5 +21,5 @@ const app = Vue.createApp({
|
||||
}
|
||||
});
|
||||
app
|
||||
.use(Phrasen, {reload: true})
|
||||
.use(Phrasen)
|
||||
.mount('#wrapper');
|
||||
@@ -6,7 +6,7 @@ import CalendarWeeks from './Weeks.js';
|
||||
import CalendarDay from './Day.js';
|
||||
import CalendarMinimized from './Minimized.js';
|
||||
import CalendarDate from '../../composables/CalendarDate.js';
|
||||
|
||||
import CalendarDates from '../../composables/CalendarDates.js';
|
||||
|
||||
// TODO(chris): week/month toggle
|
||||
|
||||
@@ -89,7 +89,7 @@ export default {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
emits: [
|
||||
'select:day',
|
||||
@@ -221,6 +221,9 @@ export default {
|
||||
}
|
||||
|
||||
},
|
||||
unmounted(){
|
||||
CalendarDates.cleanup();
|
||||
},
|
||||
template: /*html*/`
|
||||
<div ref="container" class="fhc-calendar card h-100" :class="sizeClass">
|
||||
<component :is="'calendar-' + mode" @updateMode="mode = $event" @change:range="$emit('change:range',$event)" @input="handleInput" >
|
||||
|
||||
@@ -13,7 +13,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.focusDate.format({ year: 'numeric' }) + ' KW ' + this.focusDate.w;
|
||||
return this.focusDate.wYear + ' KW ' + this.focusDate.w;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
import CalendarDate from '../../../composables/CalendarDate.js';
|
||||
import LvModal from "../../../components/Cis/Mylv/LvModal.js";
|
||||
|
||||
function ggt(m, n) { return n == 0 ? m : ggt(n, m % n); }
|
||||
function kgv(m, n) { return (m * n) / ggt(m, n); }
|
||||
function ggt(m, n) {
|
||||
return n == 0 ? m : ggt(n, m % n);
|
||||
}
|
||||
|
||||
function kgv(m, n) {
|
||||
return (m * n) / ggt(m, n);
|
||||
}
|
||||
|
||||
export default {
|
||||
components:{
|
||||
name: 'DayPage',
|
||||
components: {
|
||||
LvModal,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hourPosition: null,
|
||||
hourPositionTime: null,
|
||||
lvMenu:null,
|
||||
lvMenu: null,
|
||||
}
|
||||
},
|
||||
inject: [
|
||||
@@ -39,20 +45,20 @@ export default {
|
||||
'page:forward',
|
||||
'input'
|
||||
],
|
||||
watch:{
|
||||
watch: {
|
||||
//TODO: on first render non of the day-page components are active and the watcher on selectedEvent does not fetch the lvMenu
|
||||
//TODO: workaround is to watch the active state and refetch in case the lvMenu is empty
|
||||
active:{
|
||||
handler(value){
|
||||
if(value){
|
||||
if(!this.lvMenu){
|
||||
active: {
|
||||
handler(value) {
|
||||
if (value) {
|
||||
if (!this.lvMenu) {
|
||||
this.fetchLvMenu(this.selectedEvent);
|
||||
}
|
||||
}
|
||||
},
|
||||
immediate:true,
|
||||
immediate: true,
|
||||
},
|
||||
eventsPerDayAndHour:{
|
||||
eventsPerDayAndHour: {
|
||||
handler(newEvents) {
|
||||
// if no event is selected, select the first event of the day
|
||||
if (this.selectedEvent == null && newEvents[this.day.toDateString()]?.events.length > 0) {
|
||||
@@ -64,29 +70,27 @@ export default {
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
selectedEvent:{
|
||||
selectedEvent: {
|
||||
handler(event) {
|
||||
// return early if the day-page component is not the active carousel item
|
||||
if(!this.active)
|
||||
{
|
||||
if (!this.active) {
|
||||
return;
|
||||
}
|
||||
this.lvMenu = null;
|
||||
this.fetchLvMenu(event);
|
||||
},
|
||||
immediate:true,
|
||||
immediate: true,
|
||||
},
|
||||
isSliding:{
|
||||
handler(value){
|
||||
if(value)
|
||||
{
|
||||
isSliding: {
|
||||
handler(value) {
|
||||
if (value) {
|
||||
this.setSelectedEvent(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pageHeaderStyle(){
|
||||
pageHeaderStyle() {
|
||||
return {
|
||||
'z-index': 4,
|
||||
'grid-template-columns': 'repeat(' + this.day.length + ', 1fr)',
|
||||
@@ -95,13 +99,21 @@ export default {
|
||||
top: 0,
|
||||
}
|
||||
},
|
||||
dayGridStyle(){
|
||||
dayText(){
|
||||
if(!this.size || !this.day)return {};
|
||||
return {
|
||||
heading: this.day.toLocaleString(this.$p.user_locale.value, { dateStyle: 'short' }),
|
||||
tag: this.day.toLocaleString(this.$p.user_locale.value, { weekday: this.size < 2 ? 'narrow' : (this.size < 3 ? 'short' : 'long') }),
|
||||
datum: this.day.toLocaleString(this.$p.user_locale.value, [{ day: 'numeric', month: 'numeric' }, { day: 'numeric', month: 'numeric' }, { day: 'numeric', month: 'numeric' }, { dateStyle: 'short' }][this.size]),
|
||||
}
|
||||
},
|
||||
dayGridStyle() {
|
||||
return {
|
||||
'grid-template-columns': '1 1fr',
|
||||
'grid-template-rows': 'repeat(' + (this.hours.length * 60 / this.smallestTimeFrame) + ', 1fr)',
|
||||
}
|
||||
},
|
||||
noLvStyle(){
|
||||
noLvStyle() {
|
||||
return {
|
||||
top: (this.calendarScrollTop + 100) + 'px',
|
||||
position: 'absolute',
|
||||
@@ -111,19 +123,19 @@ export default {
|
||||
'z-index': 1,
|
||||
}
|
||||
},
|
||||
indicatorStyle(){
|
||||
indicatorStyle() {
|
||||
return {
|
||||
'pointer-events': 'none',
|
||||
'padding-left': '3.5rem',
|
||||
'margin-top': '-1px',
|
||||
'z-index': 2,
|
||||
'border-color':'#00649C!important',
|
||||
'border-color': '#00649C!important',
|
||||
top: this.hourPosition + 'px',
|
||||
left: 0,
|
||||
right: 0,
|
||||
}
|
||||
}
|
||||
},
|
||||
noEventsCondition(){
|
||||
noEventsCondition() {
|
||||
return !this.isSliding && this.filteredEvents?.length === 0;
|
||||
},
|
||||
hours() {
|
||||
@@ -138,17 +150,23 @@ export default {
|
||||
if (this.isSliding) return {};
|
||||
|
||||
const res = {};
|
||||
|
||||
|
||||
let key = this.day.toDateString();
|
||||
|
||||
let nextDay = new Date(this.day);
|
||||
nextDay.setDate(nextDay.getDate() + 1);
|
||||
nextDay.setMilliseconds(nextDay.getMilliseconds() - 1);
|
||||
let d = { events: [], lanes: 1 };
|
||||
let d = {events: [], lanes: 1};
|
||||
if (this.events[key]) {
|
||||
this.events[key].forEach(evt => {
|
||||
let event = {
|
||||
orig: evt, lane: 1, maxLane: 1, start: evt.start < this.day ? this.day : evt.start, end: evt.end > nextDay ? nextDay : evt.end, shared: [], setSharedMaxRecursive(doneItems) {
|
||||
orig: evt,
|
||||
lane: 1,
|
||||
maxLane: 1,
|
||||
start: evt.start < this.day ? this.day : evt.start,
|
||||
end: evt.end > nextDay ? nextDay : evt.end,
|
||||
shared: [],
|
||||
setSharedMaxRecursive(doneItems) {
|
||||
this.maxLane = Math.max(doneItems[0].maxLane, this.maxLane);
|
||||
doneItems.push(this);
|
||||
this.shared.filter(other => !doneItems.includes(other)).forEach(i => i.setSharedMaxRecursive(doneItems));
|
||||
@@ -168,7 +186,7 @@ export default {
|
||||
d.lanes = d.events.map(e => e.maxLane).reduce((res, i) => kgv(res, i), 1);
|
||||
}
|
||||
res[key] = d;
|
||||
|
||||
|
||||
return res;
|
||||
},
|
||||
smallestTimeFrame() {
|
||||
@@ -176,7 +194,7 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
fetchLvMenu(event){
|
||||
fetchLvMenu(event) {
|
||||
if (event && event.type == 'lehreinheit') {
|
||||
this.$fhcApi.factory.stundenplan.getLehreinheitStudiensemester(event.lehreinheit_id[0]).then(
|
||||
res => res.data
|
||||
@@ -195,14 +213,14 @@ export default {
|
||||
// this is the id attribute that is responsible to scroll the calender to the first event
|
||||
return 'scroll' + hour + this.focusDate.d + this.week;
|
||||
},
|
||||
hourGridStyle(hour){
|
||||
hourGridStyle(hour) {
|
||||
return {
|
||||
'pointer-events': 'none',
|
||||
top: this.getAbsolutePositionForHour(hour),
|
||||
left: 0,
|
||||
right: 0,
|
||||
'z-index': 0,
|
||||
}
|
||||
}
|
||||
},
|
||||
eventGridStyle(day, event) {
|
||||
return {
|
||||
@@ -237,26 +255,24 @@ export default {
|
||||
// calculate minutes from float part of time
|
||||
let minute = Math.round(60 * minutePercentage);
|
||||
// convert minutes to 5 minute interval
|
||||
if (minute % 5 != 0)
|
||||
{
|
||||
if (minute % 5 != 0) {
|
||||
minute = Math.round(minute / 5) * 5;
|
||||
}
|
||||
// in case the rounding made the minutes 60, increase the hour and reset the minutes
|
||||
if (minute == 60)
|
||||
{
|
||||
if (minute == 60) {
|
||||
currentHour++;
|
||||
minute = 0;
|
||||
}
|
||||
|
||||
|
||||
// ## after rounding the time to the nearest 5 Minute interval, we have to convert the time back to the relative position
|
||||
// convert current time in minutes
|
||||
currentMinutes = currentHour * 60 + minute;
|
||||
// calculate the minutes percentage of the total minutes
|
||||
timePercentage = ((currentMinutes - ( this.hours[0] * 60 ) ) / ( this.hours.length * 60 ) ) * 100;
|
||||
timePercentage = ((currentMinutes - (this.hours[0] * 60)) / (this.hours.length * 60)) * 100;
|
||||
// calculate the relative position of the time percentage
|
||||
position = height * (timePercentage / 100);
|
||||
this.hourPosition = position;
|
||||
|
||||
|
||||
// add padding to minutes that consist of only one digit
|
||||
minute.toString().length == 1 ? minute = "0" + minute : minute;
|
||||
this.hourPositionTime = currentHour + ":" + minute;
|
||||
@@ -275,58 +291,66 @@ export default {
|
||||
dateToMinutesOfDay(day) {
|
||||
return Math.floor(((day.getHours() - 7) * 60 + day.getMinutes()) / this.smallestTimeFrame) + 1;
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
mounted() {
|
||||
const container = document.getElementById("calendarContainer")
|
||||
if(container) container.style.overflow = 'hidden'
|
||||
},
|
||||
template: /*html*/`
|
||||
<div class="fhc-calendar-day-page ">
|
||||
<div class="row m-0">
|
||||
<div class="col-12 col-xl-6 p-0">
|
||||
<div class="d-flex flex-column">
|
||||
<div class="fhc-calendar-week-page-header d-grid border-2 border-bottom text-center" :style="pageHeaderStyle" >
|
||||
<div type="button" class="flex-grow-1" :title="day.toLocaleString(undefined, {dateStyle:'short'})" @click.prevent="changeToMonth(day)">
|
||||
<div class="fw-bold">{{day.toLocaleString(undefined, {weekday: size < 2 ? 'narrow' : (size < 3 ? 'short' : 'long')})}}</div>
|
||||
<a href="#" class="small text-secondary text-decoration-none" >{{day.toLocaleString(undefined, [{day:'numeric',month:'numeric'},{day:'numeric',month:'numeric'},{day:'numeric',month:'numeric'},{dateStyle:'short'}][this.size])}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="eventcontainer" class="position-relative flex-grow-1" @mousemove="calcHourPosition" @mouseleave="hourPosition = null" >
|
||||
<div :id="hourGridIdentifier(hour)" v-for="hour in hours" :key="hour" class="position-absolute box-shadow-border-top" :style="hourGridStyle(hour)"></div>
|
||||
<Transition>
|
||||
<div v-if="hourPosition" class="position-absolute border-top small" :style="indicatorStyle">
|
||||
<span class="border border-top-0 px-2 bg-white">{{hourPositionTime}}</span>
|
||||
<div class="fhc-calendar-day-page h-100">
|
||||
<div class="row m-0 h-100">
|
||||
<div class="col-12 col-xl-6 p-0 h-100">
|
||||
<div class="d-flex flex-column h-100">
|
||||
<div ref="header" class="fhc-calendar-week-page-header d-grid border-2 border-bottom text-center" :style="pageHeaderStyle">
|
||||
<div type="button" class="flex-grow-1" :title="dayText.heading" @click.prevent="changeToMonth(day)">
|
||||
<div class="fw-bold">{{dayText.tag}}</div>
|
||||
<a href="#" class="small text-secondary text-decoration-none" >{{dayText.datum}}</a>
|
||||
</div>
|
||||
</Transition>
|
||||
<div>
|
||||
<h1 v-if="noEventsCondition" class="m-0 text-secondary" ref="noEventsText" :style="noLvStyle">Keine Lehrveranstaltungen</h1>
|
||||
<div :class="{'fhc-calendar-no-events-overlay':noEventsCondition, 'events':true}">
|
||||
|
||||
<div class="hours">
|
||||
<div v-for="hour in hours" style="min-height:100px" :key="hour" class="text-muted text-end small" :ref="'hour' + hour">{{hour}}:00</div>
|
||||
</div>
|
||||
<div v-for="day in eventsPerDayAndHour" :key="day" class=" day border-start" :style="dayGridStyle">
|
||||
<div v-for="event in day.events" :key="event" :style="eventGridStyle(day,event)" :class="{'selectedEvent':event.orig == selectedEvent}" class="mx-2 small rounded overflow-hidden " >
|
||||
<!-- desktop version of the page template, parent receives slotProp mobile = false -->
|
||||
<div class="d-none d-xl-block h-100 " @click.prevent="eventClick(event)">
|
||||
<slot name="dayPage" :event="event" :day="day" :mobile="false">
|
||||
<p>this is a slot placeholder</p>
|
||||
</slot>
|
||||
</div>
|
||||
<div id="scroll g-0" style="height: 100%; overflow: scroll;">
|
||||
|
||||
<div ref="eventcontainer" class="position-relative flex-grow-1" @mousemove="calcHourPosition" @mouseleave="hourPosition = null" >
|
||||
<div :id="hourGridIdentifier(hour)" v-for="hour in hours" :key="hour" class="position-absolute box-shadow-border-top" :style="hourGridStyle(hour)"></div>
|
||||
|
||||
<Transition>
|
||||
<div v-if="hourPosition && !noEventsCondition" class="position-absolute border-top small" :style="indicatorStyle">
|
||||
<span class="border border-top-0 px-2 bg-white">{{hourPositionTime}}</span>
|
||||
</div>
|
||||
</Transition>
|
||||
<div>
|
||||
<h1 v-if="noEventsCondition" class="m-0 text-secondary" ref="noEventsText" :style="noLvStyle">Keine Lehrveranstaltungen</h1>
|
||||
<div :class="{'fhc-calendar-no-events-overlay':noEventsCondition, 'events':true}">
|
||||
|
||||
<div class="hours">
|
||||
<div v-for="hour in hours" style="min-height:100px" :key="hour" class="text-muted text-end small" :ref="'hour' + hour">{{hour}}:00</div>
|
||||
</div>
|
||||
<!-- mobile version of the page template, parent receives slotProp mobile = true -->
|
||||
<div class="d-block d-xl-none h-100" @click.prevent="eventClick(event)">
|
||||
<slot name="dayPage" :event="event" :day="day" :mobile="true">
|
||||
<p>this is a slot placeholder</p>
|
||||
</slot>
|
||||
<div v-for="day in eventsPerDayAndHour" :key="day" class=" day border-start" :style="dayGridStyle">
|
||||
<div v-for="event in day.events" :key="event" :style="eventGridStyle(day,event)" :class="{'selectedEvent':event.orig == selectedEvent}" class="mx-2 small rounded overflow-hidden " >
|
||||
<!-- desktop version of the page template, parent receives slotProp mobile = false -->
|
||||
<div class="d-none d-xl-block h-100 " @click.prevent="eventClick(event)">
|
||||
<slot name="dayPage" :event="event" :day="day" :mobile="false">
|
||||
<p>this is a slot placeholder</p>
|
||||
</slot>
|
||||
</div>
|
||||
<!-- mobile version of the page template, parent receives slotProp mobile = true -->
|
||||
<div class="d-block d-xl-none h-100" @click.prevent="eventClick(event)">
|
||||
<slot name="dayPage" :event="event" :day="day" :mobile="true">
|
||||
<p>this is a slot placeholder</p>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-xl-block col-xl-6 p-0">
|
||||
<div style="z-index:0" class="p-5 sticky-top d-flex justify-content-center align-items-center flex-column">
|
||||
<div style="max-height: calc(var(--fhc-calendar-pane-height) - 100px); overflow-y:auto;" class="w-100">
|
||||
<div class="d-xl-block col-xl-6 p-0">
|
||||
<div style="z-index:0" class="p-4 sticky-top d-flex justify-content-center align-items-center flex-column">
|
||||
<div style="max-height: calc(var(--fhc-calendar-pane-height)); overflow-y:auto;" class="w-100">
|
||||
<template v-if="selectedEvent && lvMenu">
|
||||
<slot name="pageMobilContent" :lvMenu="lvMenu" >
|
||||
<p>this is a slot placeholder</p>
|
||||
@@ -345,7 +369,8 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>`
|
||||
</div>
|
||||
</div>
|
||||
|
||||
`
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ export default {
|
||||
return{
|
||||
selected: this.mode,
|
||||
modes:{
|
||||
day: { mode_bezeichnung: "Tag", icon: "fa-calendar-day" , condition:true},
|
||||
week: { mode_bezeichnung: "Woche", icon: "fa-calendar-week", condition: !this.noWeekView },
|
||||
month: { mode_bezeichnung: "Monat", icon: "fa-calendar-days", condition: !this.noMonthView },
|
||||
day: { mode_bezeichnung: "Tag", icon: "fa-sun" , condition:true},
|
||||
},
|
||||
headerPadding:null,
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.focusDate.format({month: ['short','long','long','long'][this.size], year: 'numeric'});
|
||||
return this.focusDate.format({month: ['short','long','long','long'][this.size], year: 'numeric'}, this.$p.user_locale.value);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -28,6 +28,14 @@ export default {
|
||||
'input'
|
||||
],
|
||||
computed: {
|
||||
dayText(){
|
||||
if (!this.size || !this.weeks[0]?.days) return {};
|
||||
let dayTextMap ={};
|
||||
this.weeks[0].days.forEach((day)=>{
|
||||
dayTextMap[day] = day.toLocaleString(this.$p.user_locale.value, { weekday: this.size < 1 ? 'narrow' : (this.size < 3 ? 'short' : 'long') });
|
||||
});
|
||||
return dayTextMap;
|
||||
},
|
||||
weeks() {
|
||||
let firstDayOfMonth = new CalendarDate(this.year, this.month, 1);
|
||||
let startDay = firstDayOfMonth.firstDayOfCalendarMonth;
|
||||
@@ -39,7 +47,7 @@ export default {
|
||||
week.days.push(new Date(startDay));
|
||||
|
||||
if (week.days.length == 7) {
|
||||
let d = new CalendarDate(week.days[res.length ? 0 : 6]);
|
||||
let d = new CalendarDate(week.days[5]);
|
||||
week.no = d.w;
|
||||
week.y = d.y;
|
||||
res.push(week);
|
||||
@@ -79,11 +87,15 @@ export default {
|
||||
this.selectDay(day);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const container = document.getElementById("calendarContainer")
|
||||
if(container) container.style.overflow = 'scroll'
|
||||
},
|
||||
template: /*html*/`
|
||||
<div class="fhc-calendar-month-page" :class="{'show-weeks': showWeeks}">
|
||||
<div v-if="showWeeks" class=" bg-light fw-bold border-top border-bottom text-center"></div>
|
||||
<div v-for="day in weeks[0].days" :key="day" class="bg-light fw-bold border-top border-bottom text-center">
|
||||
{{day.toLocaleString(undefined, {weekday: size < 1 ? 'narrow' : (size < 3 ? 'short' : 'long')})}}
|
||||
{{dayText[day]}}
|
||||
</div>
|
||||
<template v-for="week in weeks" :key="week.no">
|
||||
<a href="#" v-if="showWeeks" class="fhc-calendar-month-page-weekday text-decoration-none text-end opacity-25" @click.prevent="changeToWeek(week)">{{week.no}}</a>
|
||||
|
||||
@@ -17,7 +17,7 @@ export default {
|
||||
return this.focusDate.format({year: 'numeric'});
|
||||
},
|
||||
months() {
|
||||
return this.monthIndices.map(i => (new Date(0, i, 1)).toLocaleString(undefined, {month: this.size < 2 ? 'short' : 'long'}));
|
||||
return this.monthIndices.map(i => (new Date(0, i, 1)).toLocaleString(this.$p.user_locale.value, {month: this.size < 2 ? 'short' : 'long'}));
|
||||
}
|
||||
},
|
||||
template: `
|
||||
|
||||
@@ -82,8 +82,8 @@ export default {
|
||||
template: `
|
||||
<div ref="carousel" class="calendar-pane carousel slide" @[\`slide.bs.carousel\`]="slide" @[\`slid.bs.carousel\`]="slid" :data-queue="queue">
|
||||
<!--height calc function just for user testing purpose (has to be fixed)-->
|
||||
<div @scroll="scrollCalendar" ref="calendarContainer" class="carousel-inner " style="height:var(--fhc-calendar-pane-height); overflow:scroll">
|
||||
<div ref="carouselItems" v-for="i in [...Array(3).keys()]" :key="i" class="carousel-item">
|
||||
<div id="calendarContainer" @scroll="scrollCalendar" ref="calendarContainer" class="carousel-inner " style="height:var(--fhc-calendar-pane-height);">
|
||||
<div ref="carouselItems" v-for="i in [...Array(3).keys()]" :key="i" class="carousel-item" style="height:var(--fhc-calendar-pane-height);">
|
||||
<slot :active="i == activeCarouselItemIndex" :index="i" :offset="offsets[i]" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.focusDate.format({year: 'numeric'}) + ' KW ' + this.focusDate.w;
|
||||
return this.focusDate.wYear + ' KW ' + this.focusDate.w;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
<div class="fhc-calendar-week">
|
||||
<calendar-header :title="title" @prev="prev" @next="next" @updateMode="$emit('updateMode', $event)" @click="$emit('updateMode', 'weeks')"/>
|
||||
<calendar-pane ref="pane" v-slot="slot" @slid="paneChanged">
|
||||
<calendar-week-page :year="focusDate.y" :week="focusDate.w+slot.offset" @updateMode="$emit('updateMode', $event)" @page:back="prev" @page:forward="next" @input="selectEvent" >
|
||||
<calendar-week-page :year="focusDate.wYear" :week="focusDate.w+slot.offset" @updateMode="$emit('updateMode', $event)" @page:back="prev" @page:forward="next" @input="selectEvent" >
|
||||
<template #weekPage="{event,day,isSelected}">
|
||||
<slot name="weekPage" :event="event" :day="day" :isSelected="isSelected"></slot>
|
||||
</template>
|
||||
|
||||
@@ -56,6 +56,18 @@ export default {
|
||||
// returns an array with elements starting at 7 and ending at 24
|
||||
return [...Array(24).keys()].filter(hour => hour >= 7 && hour <= 24);
|
||||
},
|
||||
dayText() {
|
||||
if (!this.size || !this.days) return {};
|
||||
let dayTextMap ={};
|
||||
this.days.forEach((day)=>{
|
||||
dayTextMap[day] = {
|
||||
heading: day.toLocaleString(this.$p.user_locale.value, { dateStyle: 'short' }),
|
||||
tag: day.toLocaleString(this.$p.user_locale.value, { weekday: this.size < 2 ? 'narrow' : (this.size < 3 ? 'short' : 'long') }),
|
||||
datum: day.toLocaleString(this.$p.user_locale.value, [{ day: 'numeric', month: 'numeric' }, { day: 'numeric', month: 'numeric' }, { day: 'numeric', month: 'numeric' }, { dateStyle: 'short' }][this.size]),
|
||||
};
|
||||
});
|
||||
return dayTextMap;
|
||||
},
|
||||
days() {
|
||||
|
||||
let tmpDate = new CalendarDate(this.year,1,1); // NOTE(chris): somewhere in the middle of the year
|
||||
@@ -107,7 +119,7 @@ export default {
|
||||
smallestTimeFrame() {
|
||||
return [30,15,10,5][this.size];
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hourGridIdentifier(hour) {
|
||||
// this is the id attribute that is responsible to scroll the calender to the first event
|
||||
@@ -201,15 +213,17 @@ export default {
|
||||
mounted() {
|
||||
|
||||
setTimeout(() => this.$refs.eventcontainer.scrollTop = this.$refs.eventcontainer.scrollHeight / 3 + 1, 0);
|
||||
|
||||
const container = document.getElementById("calendarContainer")
|
||||
if(container) container.style.overflow = 'scroll'
|
||||
},
|
||||
template: /*html*/`
|
||||
<div class="fhc-calendar-week-page">
|
||||
|
||||
<div class="d-flex flex-column">
|
||||
<div class="fhc-calendar-week-page-header d-grid border-2 border-bottom text-center" :style="pageHeaderStyle" >
|
||||
<div type="button" v-for="day in days" :key="day" class="flex-grow-1" :title="day.toLocaleString(undefined, {dateStyle:'short'})" @click.prevent="changeToMonth(day)">
|
||||
<div class="fw-bold">{{day.toLocaleString(undefined, {weekday: size < 2 ? 'narrow' : (size < 3 ? 'short' : 'long')})}}</div>
|
||||
<a href="#" class="small text-secondary text-decoration-none" >{{day.toLocaleString(undefined, [{day:'numeric',month:'numeric'},{day:'numeric',month:'numeric'},{day:'numeric',month:'numeric'},{dateStyle:'short'}][this.size])}}</a>
|
||||
<div type="button" v-for="day in days" :key="day" class="flex-grow-1" :title="dayText[day]?.heading" @click.prevent="changeToMonth(day)">
|
||||
<div class="fw-bold">{{dayText[day]?.tag}}</div>
|
||||
<a href="#" class="small text-secondary text-decoration-none" >{{dayText[day]?.datum}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="eventcontainer" class="position-relative flex-grow-1" @mousemove="calcHourPosition" @mouseleave="hourPosition = null" >
|
||||
@@ -225,7 +239,7 @@ export default {
|
||||
</div>
|
||||
<div v-for="day in eventsPerDayAndHour" :key="day" class=" day border-start" :style="dayGridStyle(day)">
|
||||
<div v-for="event in day.events" :key="event" @click.prevent="weekPageClick(event.orig, day)" :style="eventGridStyle(day,event)" class="mx-2 small rounded overflow-hidden " >
|
||||
<slot name="weekPage" :event="event" :day="day" :isSelected="event.orig == selectedEvent" >
|
||||
<slot name="weekPage" :event="event" :day="day" :isSelected="event.orig == selectedEvent">
|
||||
<p>this is a placeholder which means that no template was passed to the Calendar Page slot</p>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
@@ -9,11 +9,11 @@ export default {
|
||||
'focusDate'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
weeks: [...Array(this.focusDate.numWeeks).keys()].map(i => i+1)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
weeks(){
|
||||
return [...Array(this.focusDate.numWeeks).keys()].map(i => i + 1);
|
||||
},
|
||||
title() {
|
||||
return this.focusDate.format({year: 'numeric'});
|
||||
}
|
||||
@@ -23,11 +23,19 @@ export default {
|
||||
// TODO(chris): test is there a week jump on year select? => yes there is if the same month/day are in different weeks ... should we prevent that?
|
||||
this.focusDate.w = week;
|
||||
this.$emit('updateMode', 'week');
|
||||
}
|
||||
},
|
||||
prev(){
|
||||
this.focusDate.y--;
|
||||
this.focusDate._clean();
|
||||
},
|
||||
next() {
|
||||
this.focusDate.y++;
|
||||
this.focusDate._clean();
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div class="fhc-calendar-weeks">
|
||||
<calendar-header :title="title" @prev="focusDate.y--" @next="focusDate.y++" @click="$emit('updateMode', 'years')" @updateMode="$emit('updateMode', $event)" />
|
||||
<calendar-header :title="title" @prev="prev" @next="next" @click="$emit('updateMode', 'years')" @updateMode="$emit('updateMode', $event)" />
|
||||
<div class="d-flex flex-wrap">
|
||||
<div v-for="(week, key) in weeks" :key="key" class="d-grid col-2">
|
||||
<button @click="setWeek(week)" class="btn btn-outline-secondary" :class="{'border-0': week != focusDate.w}">
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
export const FhcChart = {
|
||||
name: 'FhcChart',
|
||||
props: {
|
||||
chartOptions: {
|
||||
type: Object,
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div style="width:100%;height:100%;overflow:auto">
|
||||
<div role="group" aria-label="Chart Modus">
|
||||
<Button :class="(chartOptions.chart.type === 'pie' ? 'active ' : '') + 'btn btn-outline-secondary'" style="width: 48px;" @click="chartOptions.chart.type='pie'"><i class="fa-solid fa-chart-pie"></i></Button>
|
||||
<Button :class="(chartOptions.chart.type === 'bar' ? 'active ' : '') + 'btn btn-outline-secondary'" style="width: 48px;" @click="chartOptions.chart.type='bar'"><i class="fa-solid fa-chart-bar"></i></Button>
|
||||
<Button :class="(chartOptions.chart.type === 'column' ? 'active ' : '') + 'btn btn-outline-secondary'" style="width: 48px;" @click="chartOptions.chart.type='column'"><i class="fa-solid fa-chart-simple"></i></Button>
|
||||
<Button :class="(chartOptions.chart.type === 'line' ? 'active ' : '') + 'btn btn-outline-secondary'" style="width: 48px;" @click="chartOptions.chart.type='line'"><i class="fa-solid fa-chart-line"></i></Button>
|
||||
</div>
|
||||
<figure>
|
||||
<highcharts class="chart" :options="chartOptions"></highcharts>
|
||||
</figure>
|
||||
</div>
|
||||
`
|
||||
};
|
||||
|
||||
export default FhcChart
|
||||
@@ -1,59 +1,94 @@
|
||||
import raum_contentmittitel from './Content_types/Raum_contentmittitel.js'
|
||||
import general from './Content_types/General.js'
|
||||
|
||||
import BsConfirm from "../../Bootstrap/Confirm";
|
||||
|
||||
export default {
|
||||
props:{
|
||||
content_id:{
|
||||
type:Number,
|
||||
required:true,
|
||||
},
|
||||
version:{
|
||||
type:[String, Number],
|
||||
default: null,
|
||||
},
|
||||
sprache:{
|
||||
type:[String, Number],
|
||||
default: null,
|
||||
},
|
||||
sichtbar:{
|
||||
type:[String, Number],
|
||||
default: null,
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
components:{
|
||||
raum_contentmittitel,
|
||||
general,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
content: null,
|
||||
};
|
||||
},
|
||||
computed:{
|
||||
computeContentType: function(){
|
||||
switch(this.content_type){
|
||||
case "raum_contentmittitel":
|
||||
return "raum_contentmittitel";
|
||||
default:
|
||||
return "general";
|
||||
};
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$fhcApi.factory.cms.content(this.content_id,this.version, this.sprache, this.sichtbar).then(res =>{
|
||||
this.content = res.data.content;
|
||||
this.content_type=res.data.type;
|
||||
});
|
||||
},
|
||||
mounted(){
|
||||
|
||||
},
|
||||
template: /*html*/ `
|
||||
name: "ContentComponent",
|
||||
props: {
|
||||
content_id: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
},
|
||||
version: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
sichtbar: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
raum_contentmittitel,
|
||||
general,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
content: null,
|
||||
content_id_internal: this.content_id
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
fetchContent(){
|
||||
return this.$fhcApi.factory.cms.content(this.content_id_internal, this.version, this.sprache, this.sichtbar).then(res => {
|
||||
this.content = res.data.content;
|
||||
this.content_type = res.data.type;
|
||||
|
||||
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));
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
});
|
||||
});
|
||||
document.querySelectorAll("#cms [data-href]").forEach((el) => {
|
||||
el.href = el.dataset.href.replace(
|
||||
/^ROOT\//,
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
sprache: function(sprache){
|
||||
this.fetchContent();
|
||||
},
|
||||
'$route.params.content_id'(newVal) {
|
||||
this.content_id_internal = newVal
|
||||
this.fetchContent();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
sprache(){
|
||||
return this.$p.user_language.value;
|
||||
},
|
||||
computeContentType: function () {
|
||||
switch (this.content_type) {
|
||||
case "raum_contentmittitel":
|
||||
return "raum_contentmittitel";
|
||||
default:
|
||||
return "general";
|
||||
};
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.fetchContent();
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<!-- div that contains the content -->
|
||||
<component :is="computeContentType" v-if="content" :content="content" :content_id="content_id" />
|
||||
<component ref="content" :is="computeContentType" v-if="content" :content="content" :content_id="content_id_internal" />
|
||||
<p v-else>No content is available to display</p>
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
import { replaceRelativeLegacyLink } from "../../../../helpers/LegacyLinkReplaceHelper.js"
|
||||
export default {
|
||||
name: "GeneralComponent",
|
||||
props:{
|
||||
content:{
|
||||
type:String,
|
||||
@@ -98,6 +99,12 @@ export default {
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root
|
||||
);
|
||||
});
|
||||
|
||||
document.querySelectorAll("[href]").forEach((element) => {
|
||||
let orignal_href = element.getAttribute("href");
|
||||
let new_href = replaceRelativeLegacyLink(orignal_href);
|
||||
element.href = new_href;
|
||||
});
|
||||
},
|
||||
template: /*html*/ `
|
||||
<!-- div that contains the content -->
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
export default {
|
||||
name: "RaumComponent",
|
||||
props:{
|
||||
content:{
|
||||
type:String,
|
||||
|
||||
@@ -1,17 +1,62 @@
|
||||
import Pagination from "../../Pagination/Pagination.js";
|
||||
import StudiengangInformation from "./StudiengangInformation/StudiengangInformation.js";
|
||||
import BsConfirm from "../../Bootstrap/Confirm";
|
||||
|
||||
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;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
fetchNews: function(){
|
||||
return this.$fhcApi.factory.cms.getNews(this.page, this.page_size, this.sprache)
|
||||
.then(res => res.data)
|
||||
.then(result => {
|
||||
this.content = result;
|
||||
|
||||
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));
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
});
|
||||
});
|
||||
document.querySelectorAll("#cms [data-href]").forEach((el) => {
|
||||
el.href = el.dataset.href.replace(
|
||||
/^ROOT\//,
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root
|
||||
);
|
||||
});
|
||||
});
|
||||
},
|
||||
loadNewPageContent: function (data) {
|
||||
this.$fhcApi.factory.cms.getNews(data.page, data.rows)
|
||||
.then(res => res.data)
|
||||
@@ -22,11 +67,7 @@ export default {
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$fhcApi.factory.cms.getNews(1, this.page_size)
|
||||
.then(res => res.data)
|
||||
.then(result => {
|
||||
this.content = result;
|
||||
});
|
||||
this.fetchNews();
|
||||
|
||||
this.$fhcApi.factory.cms.getNewsRowCount()
|
||||
.then(res => res.data)
|
||||
@@ -37,8 +78,20 @@ export default {
|
||||
template: /*html*/ `
|
||||
<h2 >News</h2>
|
||||
<hr/>
|
||||
<pagination :page_size="page_size" @page="loadNewPageContent" :maxPageCount="maxPageCount">
|
||||
<pagination v-show="content?true:false" :page_size="page_size" @page="page=$event.page; loadNewPageContent($event)" :maxPageCount="maxPageCount">
|
||||
</pagination>
|
||||
<div class="container-fluid">
|
||||
<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_size="page_size" @page="loadNewPageContent" :maxPageCount="maxPageCount">
|
||||
</pagination>
|
||||
<div v-html="content"></div>
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
import StudiengangPerson from "./StudiengangPerson";
|
||||
import StudiengangVertretung from "./StudiengangVertretung";
|
||||
|
||||
export default {
|
||||
data(){
|
||||
return{
|
||||
studiengang:null,
|
||||
semester: null,
|
||||
stg_ltg: null,
|
||||
gf_ltg: null,
|
||||
stv_ltg: null,
|
||||
ass: null,
|
||||
hochschulvertr: null,
|
||||
stdv: null,
|
||||
jahrgangsvertr: null,
|
||||
}
|
||||
},
|
||||
props:{
|
||||
displayWidget:{
|
||||
type:Boolean,
|
||||
default:false,
|
||||
}
|
||||
},
|
||||
components:{
|
||||
StudiengangPerson,
|
||||
StudiengangVertretung,
|
||||
},
|
||||
template:/*html*/`
|
||||
|
||||
<template v-if="studiengang?.bezeichnung && semester">
|
||||
<div class="card card-body mb-3">
|
||||
<div class="mb-1">
|
||||
<h2 class="mb-1 pb-0">{{$p.t('lehre','studiengang')}}:</h2>
|
||||
<span class="mb-1">{{studiengang.bezeichnung}}</span>
|
||||
</div>
|
||||
<div class="mb-1">
|
||||
<h2 class="mb-1 pb-0">Moodle:</h2>
|
||||
<a class="mb-1" target="_blank" :href="moodleLink">{{studiengang.kurzbzlang}}</a>
|
||||
</div>
|
||||
<div :class="{'mb-1':studiengang?.zusatzinfo_html}">
|
||||
<h2 class="mb-1 pb-0">{{$p.t('lehre','studiensemester')}}: </h2>
|
||||
<span class="mb-1">{{semester}}</span>
|
||||
</div>
|
||||
<div v-if="studiengang?.zusatzinfo_html" v-html="studiengang?.zusatzinfo_html"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-for="{title, collection} in collection_array">
|
||||
<template v-if="Array.isArray(collection) && collection.length !==0">
|
||||
<h2 class="text-truncate">{{title}}</h2>
|
||||
<template v-if="displayWidget">
|
||||
<div class="d-flex flex-wrap flex-row mb-3 gap-2">
|
||||
<template v-for="person in collection">
|
||||
<studiengang-person displayWidget v-bind="person"></studiengang-person>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-for="person in collection">
|
||||
<div class="mb-3">
|
||||
<studiengang-person v-bind="person"></studiengang-person>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="hochschulvertr && Array.isArray(hochschulvertr) && hochschulvertr.length >0">
|
||||
<studiengang-vertretung showBezeichnung :title="$p.t('studiengangInformation', 'Hochschulvertretung')" :vertretungsList="hochschulvertr"></studiengang-vertretung>
|
||||
</template>
|
||||
<template v-if="stdv && Array.isArray(stdv) && stdv.length >0">
|
||||
<studiengang-vertretung :title="$p.t('studiengangInformation', 'Studienvertretung').concat(studiengang.kurzbzlang??'')" :vertretungsList="stdv"></studiengang-vertretung>
|
||||
</template>
|
||||
<template v-if="jahrgangsvertr && Array.isArray(jahrgangsvertr) && jahrgangsvertr.length >0">
|
||||
<studiengang-vertretung :title="$p.t('studiengangInformation', 'Jahrgangsvertretung')" :vertretungsList="jahrgangsvertr"></studiengang-vertretung>
|
||||
</template>
|
||||
|
||||
`,
|
||||
computed:{
|
||||
// this function concatenates the studiengangsleitung and the assistenz or the
|
||||
// geschaeftsfuehrende-Stellvertretende Leitung if both collections only contain one person
|
||||
collection_array: function(){
|
||||
let returnData = [];
|
||||
|
||||
if (Array.isArray(this.stg_ltg) && this.stg_ltg.length == 1 && Array.isArray(this.ass) && this.ass.length == 1)
|
||||
{
|
||||
returnData.push({ title: `${this.$p.t('global', 'studiengangsleitung')}/${this.$p.t('studiengangInformation', 'assistenz')}` , collection: [...this.stg_ltg, ...this.ass] });
|
||||
}
|
||||
else
|
||||
{
|
||||
returnData.push({ title: this.$p.t('global', 'studiengangsleitung'), collection: this.stg_ltg });
|
||||
returnData.push({ title: this.$p.t('studiengangInformation', 'assistenz'), collection: this.ass });
|
||||
}
|
||||
if (Array.isArray(this.gf_ltg) && this.gf_ltg.length == 1 && Array.isArray(this.stv_ltg) && this.stv_ltg.length == 1)
|
||||
{
|
||||
returnData.push({ title: this.$p.t('studiengangInformation', 'geschaeftsfuehrende_stellvertretende_leitung'), collection: [...this.gf_ltg, ...this.stv_ltg] });
|
||||
}
|
||||
else
|
||||
{
|
||||
returnData.push({ title: this.$p.t('studiengangInformation', 'geschaeftsfuehrende_leitung'), collection: this.gf_ltg });
|
||||
returnData.push({ title: this.$p.t('studiengangInformation', 'stellvertretende_leitung'), collection: this.stv_ltg });
|
||||
}
|
||||
|
||||
return returnData;
|
||||
},
|
||||
moodleLink: function(){
|
||||
// early return if the studiengang information is not available
|
||||
if(!this.studiengang || !this.studiengang.studiengang_kz) return;
|
||||
|
||||
return `https://moodle.technikum-wien.at/course/view.php?idnumber=dl` + this.studiengang.studiengang_kz;
|
||||
},
|
||||
},
|
||||
mounted(){
|
||||
this.$fhcApi.factory.studiengang.studiengangInformation()
|
||||
.then(res => res.data)
|
||||
.then(studiengangInformationen => {
|
||||
Object.assign(this, studiengangInformationen);
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
@@ -0,0 +1,103 @@
|
||||
export default {
|
||||
props:{
|
||||
uid:String,
|
||||
vorname:String,
|
||||
nachname:String,
|
||||
titelpre:String,
|
||||
kontakt:String,
|
||||
telefoneklappe:String,
|
||||
email:String,
|
||||
planbezeichnung:String,
|
||||
foto:String,
|
||||
displayWidget:{
|
||||
type:Boolean,
|
||||
default:false,
|
||||
}
|
||||
},
|
||||
template:/*html*/`
|
||||
<div class="card" :style="{'width':displayWidget?'12rem':'15rem'}">
|
||||
<div class="bg-dark d-flex justify-content-center">
|
||||
<img :src="base64Image" alt="mitarbeiter_foto" style="width: 110px; height: auto; object-fir:scale-down;" class="card-img-top" >
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h6 class="text-center card-title mb-0">{{fullname}} <a v-if="profilViewLink" :href="profilViewLink"><i class="ms-2 fa fa-arrow-up-right-from-square" style="color:#00649C"></i></a></h6>
|
||||
</div>
|
||||
<hr class="my-0">
|
||||
<div class="card-body">
|
||||
|
||||
<div class="flex flex-column gap-3">
|
||||
<div class="mb-3">
|
||||
<span>
|
||||
<i class="fa fa-phone me-2"></i>
|
||||
<a :href="phone.link">{{phone.number}}</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<span>
|
||||
<i class="fa fa-home me-2"></i>
|
||||
{{ort}}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
<i class="fa-regular fa-envelope me-2"></i>
|
||||
<a :href="email_link" v-html="formattedEmail"></a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
computed:{
|
||||
formattedEmail: function(){
|
||||
if(!this.email ) return null;
|
||||
let emailString= this.email.replace("mailto:", "");
|
||||
// when splitting a string, the letter that is used to split the string will be removed from the result
|
||||
let emailArray = emailString.split('@');
|
||||
// returns both parts of the splitted string in combination with the removed letter and a word break
|
||||
return emailArray[0] + '@<wbr>' + emailArray[1];
|
||||
},
|
||||
fullname: function () {
|
||||
if (this.titelpre && this.vorname && this.nachname) {
|
||||
return `${this.titelpre} ${this.vorname} ${this.nachname}`;
|
||||
}
|
||||
else if (this.vorname && this.nachname) {
|
||||
return `${this.vorname} ${this.nachname}`;
|
||||
}
|
||||
else if (this.nachname) {
|
||||
return this.vorname;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
phone: function () {
|
||||
if (this.kontakt && this.telefoneklappe) {
|
||||
return {
|
||||
link: "tel:".concat(this.kontakt).concat(" " + this.telefoneklappe),
|
||||
number: this.kontakt.concat(" " + this.telefoneklappe),
|
||||
}
|
||||
}
|
||||
else {
|
||||
return this.kontakt ? {
|
||||
link: "tel:".concat(this.kontakt),
|
||||
number: this.kontakt,
|
||||
} : null;
|
||||
}
|
||||
},
|
||||
email_link: function () {
|
||||
return this.email ? "mailto:".concat(this.email) : null;
|
||||
},
|
||||
base64Image:function(){
|
||||
return this.foto ? 'data:image/png;base64,'.concat(this.foto) : null;
|
||||
},
|
||||
ort:function(){
|
||||
return this.planbezeichnung ?? null;
|
||||
},
|
||||
profilViewLink: function(){
|
||||
return this.uid ? FHC_JS_DATA_STORAGE_OBJECT.app_root.concat(FHC_JS_DATA_STORAGE_OBJECT.ci_router).concat("/Cis/Profil/View/").concat(this.uid): null;
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
export default {
|
||||
props: {
|
||||
title:String,
|
||||
vertretungsList:Array,
|
||||
showBezeichnung:Boolean,
|
||||
},
|
||||
template:/*html*/`
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<span>{{title}}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p v-for="vertretung in vertretungsList">
|
||||
<a v-if="profilViewLink(vertretung.uid)" :href="profilViewLink(vertretung.uid)">
|
||||
<i class="me-2 fa fa-arrow-up-right-from-square" style="color:#00649C"></i>
|
||||
</a>
|
||||
{{vertretungFormatedName(vertretung,false)}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
methods: {
|
||||
profilViewLink: function (uid) {
|
||||
return uid ? FHC_JS_DATA_STORAGE_OBJECT.app_root.concat(FHC_JS_DATA_STORAGE_OBJECT.ci_router).concat("/Cis/Profil/View/").concat(uid) : null;
|
||||
},
|
||||
vertretungFormatedName: function (vertretung) {
|
||||
if (!vertretung) return null;
|
||||
return `${vertretung.vorname ?? ''} ${vertretung.nachname ?? ''} ${vertretung.bezeichnung && this.showBezeichnung ? '('.concat(vertretung.bezeichnung.replace("(", "").replace(")", "")).concat(")") : ''}`
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
@@ -9,7 +9,6 @@ export default {
|
||||
CisSprachen,
|
||||
},
|
||||
props: {
|
||||
menu: Array,
|
||||
rootUrl: String,
|
||||
logoUrl: String,
|
||||
avatarUrl: String,
|
||||
@@ -51,6 +50,13 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchMenu: function(){
|
||||
return this.$fhcApi.factory.menu.getMenu()
|
||||
.then(res => res.data)
|
||||
.then(menu => {
|
||||
this.entries = menu;
|
||||
})
|
||||
},
|
||||
checkSettingsVisibility: function (event) {
|
||||
// hides the settings collapsible if the user clicks somewhere else
|
||||
if (!this.$refs.navUserDropdown.contains(event.target)) {
|
||||
@@ -64,6 +70,10 @@ export default {
|
||||
document.removeEventListener("click", this.checkSettingsVisibility);
|
||||
},
|
||||
makeParentContentActive(content_id, collection=this.entries, parent=null){
|
||||
if(!collection) return;
|
||||
if (typeof collection == 'object' && !Array.isArray(collection) && Object.entries(collection).length > 0) {
|
||||
collection = Object.values(collection);
|
||||
}
|
||||
for(let entry of collection){
|
||||
if(entry.content_id == content_id){
|
||||
this.activeEntry = parent;
|
||||
@@ -80,8 +90,10 @@ export default {
|
||||
this.activeEntry = content_id;
|
||||
},
|
||||
},
|
||||
created(){
|
||||
this.fetchMenu();
|
||||
},
|
||||
mounted(){
|
||||
this.entries = this.menu;
|
||||
this.$p.loadCategory(['ui', 'global'])
|
||||
this.navUserDropdown = new bootstrap.Collapse(this.$refs.navUserDropdown,{
|
||||
toggle: false
|
||||
@@ -91,7 +103,7 @@ export default {
|
||||
<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" :searchoptions="searchbaroptions" :searchfunction="searchfunction"></fhc-searchbar>
|
||||
<fhc-searchbar ref="searchbar" id="nav-search" style="background-color:var(--fhc-cis-primary)" class="fhc-searchbar w-100 p-1 p-lg-2" :searchoptions="searchbaroptions" :searchfunction="searchfunction"></fhc-searchbar>
|
||||
<a id="nav-logo" class="d-none d-lg-block" :href="rootUrl">
|
||||
<img :src="logoUrl" alt="Logo">
|
||||
</a>
|
||||
@@ -105,7 +117,7 @@ export default {
|
||||
id="nav-user-menu" class="top-100 end-0 collapse list-unstyled" aria-labelledby="nav-user-btn">
|
||||
<li class="btn-level-2"><a class="btn btn-level-2 rounded-0 d-block" :href="site_url + '/Cis/Profil'" id="menu-profil">Profil</a></li>
|
||||
<li class="btn-level-2">
|
||||
<cis-sprachen></cis-sprachen>
|
||||
<cis-sprachen @languageChanged="fetchMenu"></cis-sprachen>
|
||||
</li>
|
||||
<li class="btn-level-2"><hr class="dropdown-divider m-0 "></li>
|
||||
<li><a class="btn btn-level-2 rounded-0 d-block" :href="logoutUrl">Logout</a></li>
|
||||
@@ -119,7 +131,6 @@ export default {
|
||||
</button>
|
||||
</div>
|
||||
<div class="offcanvas-body p-0">
|
||||
|
||||
<div id="nav-main-menu" class="nav-menu-collapse collapse collapse-horizontal show">
|
||||
<div>
|
||||
<cis-menu-entry :highestMatchingUrlCount="highestMatchingUrlCount" :activeContent="activeEntry" v-for="entry in entries" :key="entry.content_id" :entry="entry" />
|
||||
|
||||
@@ -55,11 +55,8 @@ export default {
|
||||
this.makeParentContentActive(this.entry.content_id);
|
||||
}
|
||||
}
|
||||
// debugging helpers - console.log(this.entry.titel, newValue ? "open" : "close")
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
active: function () {
|
||||
if (this.entry.menu_open){
|
||||
@@ -77,6 +74,7 @@ export default {
|
||||
return '';
|
||||
let xmlDoc = (new DOMParser()).parseFromString(this.entry.content,"text/xml");
|
||||
let url = xmlDoc.getElementsByTagName('url')[0];
|
||||
|
||||
if (!url)
|
||||
return '';
|
||||
// TODO(chris): replace get params
|
||||
@@ -202,7 +200,9 @@ export default {
|
||||
<template v-else>
|
||||
<template v-if="hasChilds">
|
||||
<div class="btn-group w-100">
|
||||
<a :href="(entry.menu_open)?link:null" :target="target" @click="toggleCollapse"
|
||||
<a :target="target"
|
||||
:href="(entry.menu_open)?link:null"
|
||||
@click="toggleCollapse"
|
||||
:class="{
|
||||
'btn btn-default rounded-0 text-start': true,
|
||||
['btn-level-' + level]: true,
|
||||
@@ -223,14 +223,15 @@ export default {
|
||||
<cis-menu-entry :highestMatchingUrlCount="highestMatchingUrlCount" :activeContent="activeContent" v-for="child in entry.childs" :key="child" :entry="child" :level="level + 1"/>
|
||||
</ul>
|
||||
</template>
|
||||
<a v-else
|
||||
<a v-else
|
||||
:href="link"
|
||||
:target="target"
|
||||
:class="{
|
||||
'btn btn-default rounded-0 w-100 text-start': true,
|
||||
['btn-level-' + level]: true,
|
||||
'fw-bold':active
|
||||
}">
|
||||
}"
|
||||
@mouseup="setActiveEntry(entry.content_id)">
|
||||
{{ entry.titel }}
|
||||
</a>
|
||||
</template>`
|
||||
|
||||
@@ -28,17 +28,37 @@ export default {
|
||||
sprache: String,
|
||||
ects: String,
|
||||
incoming: Number,
|
||||
positiv: Boolean
|
||||
positiv: Boolean,
|
||||
note_index: String,
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
pruefungen: null,
|
||||
pruefungenData: null,
|
||||
info: null,
|
||||
menu: null,
|
||||
preselectedMenuItem: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
gradeColor() {
|
||||
// early return if value is null or undefined
|
||||
if (this.positiv == null) return;
|
||||
// returns a suitable color for the given grade
|
||||
if (this.positiv)
|
||||
{
|
||||
return 'var(--fhc-cis-grade-positive)';
|
||||
}
|
||||
else
|
||||
{
|
||||
return 'var(--fhc-cis-grade-negative)';
|
||||
}
|
||||
},
|
||||
is_organisatorische_einheit(){
|
||||
return this.menu == "organisatorische_einheit";
|
||||
},
|
||||
emptyMenu(){
|
||||
return !this.menu || !Array.isArray(this.menu) || Array.isArray(this.menu) && this.menu.length == 0;
|
||||
},
|
||||
bodyStyle() {return {};
|
||||
const bodyStyle = {};
|
||||
if (this.farbe)
|
||||
@@ -47,9 +67,24 @@ export default {
|
||||
},
|
||||
grade() {
|
||||
return this.benotung ? this.znote || this.lvnote || null : null;
|
||||
}
|
||||
},
|
||||
LvHasPruefungenInformation(){
|
||||
return this.pruefungenData && this.pruefungenData.length > 0;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
fetchMenu(lehrveranstaltung_id = this.lehrveranstaltung_id, studien_semester = this.studien_semester){
|
||||
return this.$fhcApi.factory.addons.getLvMenu(lehrveranstaltung_id, studien_semester)
|
||||
.then(res => {
|
||||
this.menu = res.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
this.menu = [];
|
||||
});
|
||||
},
|
||||
|
||||
c4_link(menuItem) {
|
||||
if (!menuItem) return null;
|
||||
if (Array.isArray(menuItem.c4_moodle_links) && menuItem.c4_moodle_links.length) {
|
||||
@@ -72,13 +107,13 @@ export default {
|
||||
}
|
||||
},
|
||||
openPruefungen() {
|
||||
if (!this.pruefungen) {
|
||||
this.pruefungen = true;
|
||||
LvPruefungen.popup({
|
||||
lehrveranstaltung_id: this.lehrveranstaltung_id,
|
||||
bezeichnung: this.bezeichnung
|
||||
}).then(() => this.pruefungen = false).catch(() => this.pruefungen = false);
|
||||
}
|
||||
// early return if the pruefungenData is empty or not set
|
||||
if (!this.LvHasPruefungenInformation) return;
|
||||
|
||||
LvPruefungen.popup({
|
||||
pruefungenData: this.pruefungenData,
|
||||
bezeichnung: this.bezeichnung
|
||||
});
|
||||
},
|
||||
openInfos() {
|
||||
if (!this.info) {
|
||||
@@ -100,22 +135,19 @@ export default {
|
||||
},
|
||||
watch:{
|
||||
studien_semester(newValue){
|
||||
this.$fhcApi.factory.addons.getLvMenu(this.lehrveranstaltung_id, newValue)
|
||||
.then(res => {
|
||||
this.menu = res.data;
|
||||
})
|
||||
.catch((error) => this.$fhcAlert.handleSystemError);
|
||||
this.fetchMenu(this.lehrveranstaltung_id, newValue);
|
||||
}
|
||||
},
|
||||
created(){
|
||||
this.$fhcApi.factory.lehre.getStudentPruefungen(this.lehrveranstaltung_id)
|
||||
.then(res => res.data)
|
||||
.then(pruefungen =>{
|
||||
this.pruefungenData = pruefungen;
|
||||
});
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.$fhcApi.factory.addons.getLvMenu(this.lehrveranstaltung_id, this.studien_semester)
|
||||
.then(res => {
|
||||
this.menu = res.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$fhcAlert.handleSystemError(error);
|
||||
this.menu = [];
|
||||
});
|
||||
this.fetchMenu(this.lehrveranstaltung_id, this.studien_semester);
|
||||
},
|
||||
template: /*html*/`<div class="mylv-semester-studiengang-lv card">
|
||||
<lv-uebersicht ref="lvUebersicht" :preselectedMenu="preselectedMenuItem" :event="{
|
||||
@@ -125,45 +157,56 @@ export default {
|
||||
stg_kurzbzlang:studien_semester,
|
||||
}"/>
|
||||
|
||||
<div class="card-header">
|
||||
<div class="p-2" :class="is_organisatorische_einheit?'':'card-header'">
|
||||
<!-- {{module}} if the module of the lv is important then query the module from the api endpoint for LV-->
|
||||
<h6 class="card-title">{{bezeichnung}}</h6>
|
||||
<h6 class="fw-bold" v-if="is_organisatorische_einheit" >Organisatorische Einheit:</h6>
|
||||
<h6 class="mb-0">{{bezeichnung}}</h6>
|
||||
</div>
|
||||
<div class="card-body " :style="bodyStyle">
|
||||
<ul class="list-group border-top-0 border-bottom-0 rounded-0">
|
||||
<template v-if="menu">
|
||||
<div v-if="!emptyMenu" class="card-body " :style="bodyStyle">
|
||||
<template v-if="menu">
|
||||
<ul class="list-group border-top-0 border-bottom-0 rounded-0">
|
||||
<li :type="menuItem.c4_link ? 'button' : null" v-for="menuItem in menu" class="list-group-item border-0 " >
|
||||
<div class="d-flex flex-row" :data-bs-toggle="menuItem.c4_moodle_links?.length ? 'dropdown' : null">
|
||||
<div class="mx-4">
|
||||
<i :class="[menuItem.c4_icon2 ? menuItem.c4_icon2 : 'fa-solid fa-pen-to-square', !menuItem.c4_link ? 'unavailable' : null ]"></i>
|
||||
</div>
|
||||
<a class="text-decoration-none text-truncate" :id="'moodle_links_'+lehrveranstaltung_id" :class="{'link-dark':menuItem.c4_link, 'unavailable':!menuItem.c4_link, 'dropdown-toggle':menuItem.c4_moodle_links?.length }" :target="menuItem.c4_target" :href="c4_link(menuItem) ? c4_link(menuItem) : null">
|
||||
{{menuItem.name}}
|
||||
<a
|
||||
class="text-decoration-none text-truncate"
|
||||
:id="'moodle_links_'+lehrveranstaltung_id"
|
||||
:class="{'link-dark':menuItem.c4_link, 'unavailable':!menuItem.c4_link, 'dropdown-toggle':menuItem.c4_moodle_links?.length }"
|
||||
:target="menuItem.c4_target"
|
||||
:href="c4_link(menuItem) ? c4_link(menuItem) : null">
|
||||
{{menuItem.name}}
|
||||
</a>
|
||||
</div>
|
||||
<ul v-if="menuItem.c4_moodle_links?.length" class="dropdown-menu p-0" :aria-labelledby="'moodle_links_'+lehrveranstaltung_id">
|
||||
<li v-for="item in menuItem.c4_moodle_links"><a class="dropdown-item border-bottom" :href="item.url">{{item.lehrform}}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
<template v-else>
|
||||
<li class="text-center"><i class="fa-solid fa-spinner fa-pulse fa-3x"></i></li>
|
||||
</template>
|
||||
</ul>
|
||||
</ul>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="text-center d-flex justify-content-center align-items-center h-100" >
|
||||
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div v-if="!emptyMenu" class="card-footer">
|
||||
<div class="row">
|
||||
<a href="#" class="col-auto text-start text-decoration-none" @click.prevent="openPruefungen">
|
||||
<i class="fa fa-check text-success" v-if="positiv"></i>
|
||||
{{ grade || p.t('lehre/noGrades') }}
|
||||
</a>
|
||||
<!--
|
||||
Not used anymore because the lehrveranstaltungs informationen is available as a menu point in the lehrveranstaltungs optionen
|
||||
<div v-if="lvinfo" class="col text-end">
|
||||
<a class="card-link" href="#" @click.prevent="openInfos">
|
||||
<i class="fa fa-info-circle" aria-hidden="true"></i>
|
||||
<!-- template for the LV if there are multiple pruefungen -->
|
||||
<template v-if="LvHasPruefungenInformation">
|
||||
<a href="#" class="col-auto text-start text-decoration-none" @click.prevent="openPruefungen">
|
||||
<i class="fa fa-check text-success" v-if="positiv"></i>
|
||||
<span class="ps-1" :style="'color:'+gradeColor">{{ grade || p.t('lehre/noGrades') }}</span>
|
||||
</a>
|
||||
</div>-->
|
||||
</template>
|
||||
<!-- template for the LV with no pruefungen -->
|
||||
<template v-else>
|
||||
<span class="col-auto text-start text-decoration-none" >
|
||||
<i class="fa fa-check text-success" v-if="positiv"></i>
|
||||
<span class="ps-1" :style="'color:'+gradeColor">{{ grade || p.t('lehre/noGrades') }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
@@ -10,7 +10,7 @@ export default {
|
||||
BsModal
|
||||
],
|
||||
props: {
|
||||
lehrveranstaltung_id: Number,
|
||||
pruefungenData: Array|null,
|
||||
bezeichnung: String,
|
||||
/*
|
||||
* NOTE(chris):
|
||||
@@ -26,16 +26,7 @@ export default {
|
||||
},
|
||||
data: () => ({
|
||||
result: true,
|
||||
pruefungen: null
|
||||
}),
|
||||
created() {
|
||||
if (pruefungen[this.lehrveranstaltung_id])
|
||||
this.pruefungen = pruefungen[this.lehrveranstaltung_id];
|
||||
else
|
||||
axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Cis/Mylv/Pruefungen/' + this.lehrveranstaltung_id).then(res => {
|
||||
this.pruefungen = pruefungen[this.lehrveranstaltung_id] = res.data.retval || [];
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
this.modal = this.$refs.modalContainer.modal;
|
||||
},
|
||||
@@ -47,10 +38,10 @@ export default {
|
||||
Prüfungen: {{bezeichnung}}
|
||||
</template>
|
||||
<template v-slot:default>
|
||||
<div v-if="!pruefungen" class="text-center">
|
||||
<div v-if="!pruefungenData" class="text-center">
|
||||
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
|
||||
</div>
|
||||
<p v-else-if="!pruefungen.length" class="alert alert-info mb-0">
|
||||
<p v-else-if="!pruefungenData.length" class="alert alert-info mb-0">
|
||||
Keine Prüfungen vorhanden!
|
||||
</p>
|
||||
<table v-else class="table table-hover">
|
||||
@@ -60,7 +51,7 @@ export default {
|
||||
<td class="text-end">Note</td>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="pruefung in pruefungen" :key="pruefung.pruefung_id">
|
||||
<tr v-for="pruefung in pruefungenData" :key="pruefung.pruefung_id">
|
||||
<th>{{pruefung.pruefungstyp_kurzbz}}</th>
|
||||
<td>{{pruefung.datum}}</td>
|
||||
<td class="text-end">{{pruefung.note}}</td>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CoreFilterCmpt } from "../../../components/filter/Filter.js";
|
||||
import {CoreFilterCmpt} from "../../../components/filter/Filter.js";
|
||||
import EditProfil from "./ProfilModal/EditProfil.js";
|
||||
import Adresse from "./ProfilComponents/Adresse.js";
|
||||
import Kontakt from "./ProfilComponents/Kontakt.js";
|
||||
@@ -11,475 +11,409 @@ import RoleInformation from "./ProfilComponents/RoleInformation.js";
|
||||
import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFilterCmpt,
|
||||
EditProfil,
|
||||
Adresse,
|
||||
Kontakt,
|
||||
FetchProfilUpdates,
|
||||
AusweisStatus,
|
||||
Mailverteiler,
|
||||
QuickLinks,
|
||||
ProfilEmails,
|
||||
RoleInformation,
|
||||
ProfilInformation,
|
||||
},
|
||||
components: {
|
||||
CoreFilterCmpt,
|
||||
EditProfil,
|
||||
Adresse,
|
||||
Kontakt,
|
||||
FetchProfilUpdates,
|
||||
AusweisStatus,
|
||||
Mailverteiler,
|
||||
QuickLinks,
|
||||
ProfilEmails,
|
||||
RoleInformation,
|
||||
ProfilInformation,
|
||||
},
|
||||
|
||||
inject: ["sortProfilUpdates", "collapseFunction"],
|
||||
inject: ["sortProfilUpdates", "collapseFunction"],
|
||||
|
||||
data() {
|
||||
return {
|
||||
showModal: false,
|
||||
editDataFilter: null,
|
||||
data() {
|
||||
return {
|
||||
showModal: false,
|
||||
editDataFilter: null,
|
||||
|
||||
// tabulator options
|
||||
funktionen_table_options: {
|
||||
height: 300,
|
||||
layout: "fitColumns",
|
||||
responsiveLayout: "collapse",
|
||||
responsiveLayoutCollapseUseFormatters: false,
|
||||
responsiveLayoutCollapseFormatter: Vue.$collapseFormatter,
|
||||
data: [
|
||||
{
|
||||
Bezeichnung: "",
|
||||
Organisationseinheit: "",
|
||||
Gültig_von: "",
|
||||
Gültig_bis: "",
|
||||
Wochenstunden: "",
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
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,
|
||||
},
|
||||
{
|
||||
title: "Bezeichnung",
|
||||
field: "Bezeichnung",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Organisationseinheit",
|
||||
field: "Organisationseinheit",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Gültig_von",
|
||||
field: "Gültig_von",
|
||||
headerFilter: true,
|
||||
resizable: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Gültig_bis",
|
||||
field: "Gültig_bis",
|
||||
headerFilter: true,
|
||||
resizable: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Wochenstunden",
|
||||
field: "Wochenstunden",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
],
|
||||
},
|
||||
// tabulator options
|
||||
funktionen_table_options: {
|
||||
height: 300,
|
||||
layout: "fitColumns",
|
||||
responsiveLayout: "collapse",
|
||||
responsiveLayoutCollapseUseFormatters: false,
|
||||
responsiveLayoutCollapseFormatter: Vue.$collapseFormatter,
|
||||
data: [
|
||||
{
|
||||
Bezeichnung: "",
|
||||
Organisationseinheit: "",
|
||||
Gültig_von: "",
|
||||
Gültig_bis: "",
|
||||
Wochenstunden: "",
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
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,
|
||||
},
|
||||
{
|
||||
title: "Bezeichnung",
|
||||
field: "Bezeichnung",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Organisationseinheit",
|
||||
field: "Organisationseinheit",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Gültig_von",
|
||||
field: "Gültig_von",
|
||||
headerFilter: true,
|
||||
resizable: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Gültig_bis",
|
||||
field: "Gültig_bis",
|
||||
headerFilter: true,
|
||||
resizable: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Wochenstunden",
|
||||
field: "Wochenstunden",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
betriebsmittel_table_options: {
|
||||
height: 300,
|
||||
layout: "fitColumns",
|
||||
responsiveLayout: "collapse",
|
||||
responsiveLayoutCollapseUseFormatters: false,
|
||||
responsiveLayoutCollapseFormatter: Vue.$collapseFormatter,
|
||||
data: [{ betriebsmittel: "", Nummer: "", Ausgegeben_am: "" }],
|
||||
columns: [
|
||||
{
|
||||
title:
|
||||
"<i id='collapseIconBetriebsmittel' role='button' class='fa-solid fa-angle-down '></i>",
|
||||
field: "collapse",
|
||||
headerSort: false,
|
||||
headerFilter: false,
|
||||
formatter: "responsiveCollapse",
|
||||
maxWidth: 40,
|
||||
headerClick: this.collapseFunction,
|
||||
},
|
||||
{
|
||||
title: "Betriebsmittel",
|
||||
field: "betriebsmittel",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Nummer",
|
||||
field: "Nummer",
|
||||
headerFilter: true,
|
||||
resizable: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Ausgegeben_am",
|
||||
field: "Ausgegeben_am",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
betriebsmittel_table_options: {
|
||||
height: 300,
|
||||
layout: "fitColumns",
|
||||
responsiveLayout: "collapse",
|
||||
responsiveLayoutCollapseUseFormatters: false,
|
||||
responsiveLayoutCollapseFormatter: Vue.$collapseFormatter,
|
||||
data: [{betriebsmittel: "", Nummer: "", Ausgegeben_am: ""}],
|
||||
columns: [
|
||||
{
|
||||
title:
|
||||
"<i id='collapseIconBetriebsmittel' role='button' class='fa-solid fa-angle-down '></i>",
|
||||
field: "collapse",
|
||||
headerSort: false,
|
||||
headerFilter: false,
|
||||
formatter: "responsiveCollapse",
|
||||
maxWidth: 40,
|
||||
headerClick: this.collapseFunction,
|
||||
},
|
||||
{
|
||||
title: "Betriebsmittel",
|
||||
field: "betriebsmittel",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Nummer",
|
||||
field: "Nummer",
|
||||
headerFilter: true,
|
||||
resizable: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Ausgegeben_am",
|
||||
field: "Ausgegeben_am",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
props: {
|
||||
data: Object,
|
||||
editData: Object,
|
||||
},
|
||||
props: {
|
||||
data: Object,
|
||||
editData: Object,
|
||||
},
|
||||
|
||||
methods: {
|
||||
betriebsmittelTableBuilt: function () {
|
||||
this.$refs.betriebsmittelTable.tabulator.setData(this.data.mittel);
|
||||
},
|
||||
funktionenTableBuilt: function () {
|
||||
this.$refs.funktionenTable.tabulator.setData(this.data.funktionen);
|
||||
},
|
||||
hideEditProfilModal: function () {
|
||||
//? checks the editModal component property result, if the user made a successful request or not
|
||||
if (this.$refs.editModal.result) {
|
||||
this.$fhcApi.factory.profilUpdate.selectProfilRequest()
|
||||
.then((request) => {
|
||||
if (!request.error && request) {
|
||||
this.data.profilUpdates = request.data;
|
||||
this.data.profilUpdates.sort(this.sortProfilUpdates);
|
||||
} else {
|
||||
console.error("Error when fetching profile updates: " + res.data);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
} else {
|
||||
// when modal was closed without submitting request
|
||||
}
|
||||
this.showModal = false;
|
||||
this.editDataFilter = null;
|
||||
},
|
||||
methods: {
|
||||
betriebsmittelTableBuilt: function () {
|
||||
this.$refs.betriebsmittelTable.tabulator.setData(this.data.mittel);
|
||||
},
|
||||
funktionenTableBuilt: function () {
|
||||
this.$refs.funktionenTable.tabulator.setData(this.data.funktionen);
|
||||
},
|
||||
hideEditProfilModal: function () {
|
||||
//? checks the editModal component property result, if the user made a successful request or not
|
||||
if (this.$refs.editModal.result) {
|
||||
this.$fhcApi.factory.profilUpdate.selectProfilRequest()
|
||||
.then((request) => {
|
||||
if (!request.error && request) {
|
||||
this.data.profilUpdates = request.data;
|
||||
this.data.profilUpdates.sort(this.sortProfilUpdates);
|
||||
} else {
|
||||
console.error("Error when fetching profile updates: " + res.data);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
} else {
|
||||
// when modal was closed without submitting request
|
||||
}
|
||||
this.showModal = false;
|
||||
this.editDataFilter = null;
|
||||
},
|
||||
|
||||
showEditProfilModal(view) {
|
||||
if (view) {
|
||||
this.editDataFilter = view;
|
||||
}
|
||||
showEditProfilModal(view) {
|
||||
if (view) {
|
||||
this.editDataFilter = view;
|
||||
}
|
||||
|
||||
this.showModal = true;
|
||||
Vue.nextTick(() => {
|
||||
this.$refs.editModal.show();
|
||||
});
|
||||
this.showModal = true;
|
||||
Vue.nextTick(() => {
|
||||
this.$refs.editModal.show();
|
||||
});
|
||||
|
||||
// after a state change, wait for the DOM updates to complete
|
||||
},
|
||||
// after a state change, wait for the DOM updates to complete
|
||||
},
|
||||
|
||||
fetchProfilUpdates: function () {
|
||||
this.$fhcApi.factory.profilUpdate.selectProfilRequest().then((res) => {
|
||||
if (!res.error && res) {
|
||||
this.data.profilUpdates = res.data?.length
|
||||
? res.data.sort(this.sortProfilUpdates)
|
||||
: null;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
fetchProfilUpdates: function () {
|
||||
this.$fhcApi.factory.profilUpdate.selectProfilRequest().then((res) => {
|
||||
if (!res.error && res) {
|
||||
this.data.profilUpdates = res.data?.length
|
||||
? res.data.sort(this.sortProfilUpdates)
|
||||
: null;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
editable() {
|
||||
return this.data?.editAllowed ?? false;
|
||||
},
|
||||
filteredEditData() {
|
||||
return this.editDataFilter
|
||||
? this.editData.data[this.editDataFilter]
|
||||
: this.editData;
|
||||
},
|
||||
profilInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
}
|
||||
computed: {
|
||||
editable() {
|
||||
return this.data?.editAllowed ?? false;
|
||||
},
|
||||
filteredEditData() {
|
||||
return this.editDataFilter
|
||||
? this.editData.data[this.editDataFilter]
|
||||
: this.editData;
|
||||
},
|
||||
profilInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
Vorname: this.data.vorname,
|
||||
Nachname: this.data.nachname,
|
||||
Username: this.data.username,
|
||||
Anrede: this.data.anrede,
|
||||
Titel: this.data.titel,
|
||||
Postnomen: this.data.postnomen,
|
||||
foto_sperre: this.data.foto_sperre,
|
||||
foto: this.data.foto,
|
||||
};
|
||||
},
|
||||
return {
|
||||
Vorname: this.data.vorname,
|
||||
Nachname: this.data.nachname,
|
||||
Username: this.data.username,
|
||||
Anrede: this.data.anrede,
|
||||
Titel: this.data.titel,
|
||||
Postnomen: this.data.postnomen,
|
||||
foto_sperre: this.data.foto_sperre,
|
||||
foto: this.data.foto,
|
||||
};
|
||||
},
|
||||
|
||||
roleInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
}
|
||||
roleInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
Geburtsdatum: this.data.gebdatum,
|
||||
Geburtsort: this.data.gebort,
|
||||
Kurzzeichen: this.data.kurzbz,
|
||||
Telefon:
|
||||
(this.data.standort_telefon ? this.data.standort_telefon + " " + this.data.telefonklappe : this.data.telefonklappe),
|
||||
Büro: this.data.ort_kurzbz,
|
||||
};
|
||||
},
|
||||
},
|
||||
return {
|
||||
[`${this.$p.t('profil', 'Geburtsdatum')}`]: this.data.gebdatum,
|
||||
[`${this.$p.t('profil', 'Geburtsort')}`]: this.data.gebort,
|
||||
[`${this.$p.t('profil', 'Kurzzeichen')}`]: this.data.kurzbz,
|
||||
[`${this.$p.t('profil', 'Telefon')}`]:
|
||||
(this.data.standort_telefon ? this.data.standort_telefon + " " + this.data.telefonklappe : this.data.telefonklappe),
|
||||
[`${this.$p.t('profil', 'Büro')}`]: this.data.ort_kurzbz,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
//? sorts the profil Updates: pending -> accepted -> rejected
|
||||
this.data.profilUpdates?.sort(this.sortProfilUpdates);
|
||||
|
||||
},
|
||||
mounted(){
|
||||
|
||||
},
|
||||
created() {
|
||||
//? sorts the profil Updates: pending -> accepted -> rejected
|
||||
this.data.profilUpdates?.sort(this.sortProfilUpdates);
|
||||
|
||||
template: /*html*/ `
|
||||
<div class="container-fluid text-break fhc-form" >
|
||||
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
|
||||
template: /*html*/ `
|
||||
<div class="container-fluid text-break fhc-form" >
|
||||
<edit-profil v-if="showModal" ref="editModal" @hideBsModal="hideEditProfilModal" :value="JSON.parse(JSON.stringify(filteredEditData))" :title="$p.t('profil','profilBearbeiten')"></edit-profil>
|
||||
<div class="row">
|
||||
<div class="d-md-none col-12 ">
|
||||
|
||||
<div class="row mb-3">
|
||||
<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 class="row mb-3 ">
|
||||
<div class="col">
|
||||
<!-- MOBILE QUICK LINKS -->
|
||||
<quick-links :title="$p.t('profil','quickLinks')" :mobile="true"></quick-links>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bearbeiten Button -->
|
||||
|
||||
<div class="row mb-3 ">
|
||||
<div class="col">
|
||||
<button @click="()=>showEditProfilModal()" type="button" class="text-start w-100 btn btn-outline-secondary" >
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<i class="fa fa-edit"></i>
|
||||
</div>
|
||||
<div class="col-auto">{{$p.t('ui','bearbeiten')}}</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="data.profilUpdates" class="row mb-3">
|
||||
<div class="col">
|
||||
<!-- MOBILE PROFIL UPDATES -->
|
||||
<fetch-profil-updates v-if="data.profilUpdates && data.profilUpdates.length" @fetchUpdates="fetchProfilUpdates" :data="data.profilUpdates" ></fetch-profil-updates>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<!-- END OF HIDDEN ROW (HIDDEN IN VIEWPORTS GREATER THEN-EQUAL MD) -->
|
||||
|
||||
<!-- MAIN PANNEL -->
|
||||
<div class="col-sm-12 col-md-8 col-xxl-9 ">
|
||||
<!-- ROW WITH PROFIL IMAGE AND INFORMATION -->
|
||||
|
||||
|
||||
|
||||
<!-- INFORMATION CONTENT START -->
|
||||
<!-- ROW WITH THE PROFIL INFORMATION -->
|
||||
<div class="row mb-4">
|
||||
|
||||
<div class="col-lg-12 col-xl-6 ">
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
|
||||
<!-- PROFIL INFORMATION -->
|
||||
<profil-information @showEditProfilModal="showEditProfilModal" :title="$p.t('profil','mitarbeiterIn')" :data="profilInformation" :editable="editable"></profil-information>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
|
||||
|
||||
<div class=" col-lg-12">
|
||||
|
||||
<!-- MITARBEITER INFO -->
|
||||
<role-information :title="$p.t('profil','mitarbeiterInformation')" :data="roleInformation"></role-information>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- START OF SECOND PROFIL INFORMATION COLUMN -->
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-xl-6 col-lg-12 ">
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<!-- EMAILS -->
|
||||
<profil-emails :title="this.$p.t('person','email')" :data="data.emails" ></profil-emails>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row mb-4 ">
|
||||
<div class="col">
|
||||
|
||||
<!-- PRIVATE KONTAKTE-->
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="row">
|
||||
<div @click="showEditProfilModal('Private_Kontakte')" class="col-auto" type="button">
|
||||
<button @click="()=>showEditProfilModal()" type="button" class="text-start w-100 btn btn-outline-secondary" >
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<i class="fa fa-edit"></i>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span>{{$p.t('profil','privateKontakte')}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body ">
|
||||
|
||||
<div class="gy-3 row ">
|
||||
<div v-for="element in data.kontakte" class="col-12">
|
||||
|
||||
<Kontakt :data="element"></Kontakt>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">{{$p.t('ui','bearbeiten')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
|
||||
<!-- PRIVATE ADRESSEN-->
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="row">
|
||||
<div @click="showEditProfilModal('Private_Adressen')" class="col-auto" type="button">
|
||||
<i class="fa fa-edit"></i>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span>{{$p.t('profil','privateAdressen')}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="gy-3 row ">
|
||||
<div v-for="element in data.adressen" class="col-12">
|
||||
<Adresse :data="element"></Adresse>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div >
|
||||
|
||||
<div class="row">
|
||||
|
||||
|
||||
<div class="col-12 mb-4" >
|
||||
|
||||
<!-- FUNKTIONEN TABELLE -->
|
||||
|
||||
<core-filter-cmpt @tableBuilt="funktionenTableBuilt" :title="$p.t('person','funktionen')" ref="funktionenTable" :tabulator-options="funktionen_table_options" tableOnly :sideMenu="false" />
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-12 mb-4" >
|
||||
|
||||
<!-- BETRIEBSMITTEL TABELLE -->
|
||||
<core-filter-cmpt @tableBuilt="betriebsmittelTableBuilt" :title="$p.t('profil','entlehnteBetriebsmittel')" ref="betriebsmittelTable" :tabulator-options="betriebsmittel_table_options" tableOnly :sideMenu="false" />
|
||||
</div>
|
||||
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- START OF SIDE PANEL -->
|
||||
<div class="col-md-4 col-xxl-3 col-sm-12 text-break" >
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="row d-none d-md-block mb-3">
|
||||
|
||||
</div>
|
||||
<div v-if="data.profilUpdates" class="row mb-3">
|
||||
<div class="col">
|
||||
|
||||
<!-- QUICK LINKS -->
|
||||
<quick-links :title="$p.t('profil','quickLinks')"></quick-links>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<!-- MOBILE PROFIL UPDATES -->
|
||||
<fetch-profil-updates v-if="data.profilUpdates && data.profilUpdates.length" @fetchUpdates="fetchProfilUpdates" :data="data.profilUpdates" ></fetch-profil-updates>
|
||||
</div>
|
||||
|
||||
<!-- Bearbeiten Button -->
|
||||
|
||||
<div class="row d-none d-md-block ">
|
||||
<div class="col mb-3">
|
||||
<button @click="()=>showEditProfilModal()" type="button" class="text-start w-100 btn btn-outline-secondary" >
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<i class="fa fa-edit"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END OF HIDDEN ROW (HIDDEN IN VIEWPORTS GREATER THEN-EQUAL MD) -->
|
||||
<!-- MAIN PANNEL -->
|
||||
<div class="col-sm-12 col-md-8 col-xxl-9 ">
|
||||
<!-- ROW WITH PROFIL IMAGE AND INFORMATION -->
|
||||
<!-- INFORMATION CONTENT START -->
|
||||
<!-- ROW WITH THE PROFIL INFORMATION -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-lg-12 col-xl-6 ">
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<!-- PROFIL INFORMATION -->
|
||||
<profil-information @showEditProfilModal="showEditProfilModal" :title="$p.t('profil','mitarbeiterIn')" :data="profilInformation" :editable="editable"></profil-information>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">{{$p.t('ui','bearbeiten')}}</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="row mb-4">
|
||||
<div class=" col-lg-12">
|
||||
<!-- MITARBEITER INFO -->
|
||||
<role-information :title="$p.t('profil','mitarbeiterInformation')" :data="roleInformation"></role-information>
|
||||
</div>
|
||||
</div>
|
||||
<!-- START OF SECOND PROFIL INFORMATION COLUMN -->
|
||||
</div>
|
||||
<div class="col-xl-6 col-lg-12 ">
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<!-- EMAILS -->
|
||||
<profil-emails :title="this.$p.t('person','email')" :data="data.emails" ></profil-emails>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4 ">
|
||||
<div class="col">
|
||||
<!-- PRIVATE KONTAKTE-->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="row">
|
||||
<div @click="showEditProfilModal('Private_Kontakte')" class="col-auto" type="button">
|
||||
<i class="fa fa-edit"></i>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span>{{$p.t('profil','privateKontakte')}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body ">
|
||||
<div class="gy-3 row ">
|
||||
<div v-for="element in data.kontakte" class="col-12">
|
||||
<Kontakt :data="element"></Kontakt>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<!-- PRIVATE ADRESSEN-->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="row">
|
||||
<div @click="showEditProfilModal('Private_Adressen')" class="col-auto" type="button">
|
||||
<i class="fa fa-edit"></i>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span>{{$p.t('profil','privateAdressen')}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="gy-3 row ">
|
||||
<div v-for="element in data.adressen" class="col-12">
|
||||
<Adresse :data="element"></Adresse>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="data.profilUpdates" class="row d-none d-md-block mb-3">
|
||||
</div >
|
||||
<div class="row">
|
||||
<div class="col-12 mb-4" >
|
||||
<!-- FUNKTIONEN TABELLE -->
|
||||
<core-filter-cmpt @tableBuilt="funktionenTableBuilt" :title="$p.t('person','funktionen')" ref="funktionenTable" :tabulator-options="funktionen_table_options" tableOnly :sideMenu="false" />
|
||||
</div>
|
||||
<div class="col-12 mb-4" >
|
||||
<!-- BETRIEBSMITTEL TABELLE -->
|
||||
<core-filter-cmpt @tableBuilt="betriebsmittelTableBuilt" :title="$p.t('profil','entlehnteBetriebsmittel')" ref="betriebsmittelTable" :tabulator-options="betriebsmittel_table_options" tableOnly :sideMenu="false" />
|
||||
</div>
|
||||
</div>
|
||||
</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>-->
|
||||
<!-- Bearbeiten Button -->
|
||||
<div class="row d-none d-md-block ">
|
||||
<div class="col mb-3">
|
||||
<button @click="()=>showEditProfilModal()" type="button" class="text-start w-100 btn btn-outline-secondary" >
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<i class="fa fa-edit"></i>
|
||||
</div>
|
||||
<div class="col-auto">{{$p.t('ui','bearbeiten')}}</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="data.profilUpdates" class="row d-none d-md-block mb-3">
|
||||
<div class="col mb-3">
|
||||
<!-- PROFIL UPDATES -->
|
||||
<fetch-profil-updates v-if="data.profilUpdates && data.profilUpdates.length" @fetchUpdates="fetchProfilUpdates" :data="data.profilUpdates"></fetch-profil-updates>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="row mb-3" >
|
||||
</div>
|
||||
<div class="row mb-3" >
|
||||
<div class="col-12">
|
||||
<!-- AUSWEIS STATUS -->
|
||||
<ausweis-status :data="data.zutrittsdatum"></ausweis-status>
|
||||
</div>
|
||||
<!-- AUSWEIS STATUS -->
|
||||
<ausweis-status :data="data.zutrittsdatum"></ausweis-status>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<!-- MAILVERTEILER -->
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<!-- MAILVERTEILER -->
|
||||
<mailverteiler :data="data?.mailverteiler" :title="$p.t('profil','mailverteiler')"></mailverteiler>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
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";
|
||||
@@ -6,278 +6,219 @@ import ProfilEmails from "./ProfilComponents/ProfilEmails.js";
|
||||
import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFilterCmpt,
|
||||
Mailverteiler,
|
||||
QuickLinks,
|
||||
RoleInformation,
|
||||
ProfilEmails,
|
||||
ProfilInformation,
|
||||
},
|
||||
inject: ["collapseFunction"],
|
||||
data() {
|
||||
return {
|
||||
collapseIconFunktionen: true,
|
||||
components: {
|
||||
CoreFilterCmpt,
|
||||
Mailverteiler,
|
||||
QuickLinks,
|
||||
RoleInformation,
|
||||
ProfilEmails,
|
||||
ProfilInformation,
|
||||
},
|
||||
inject: ["collapseFunction"],
|
||||
data() {
|
||||
return {
|
||||
collapseIconFunktionen: true,
|
||||
|
||||
funktionen_table_options: {
|
||||
height: 300,
|
||||
layout: "fitColumns",
|
||||
responsiveLayout: "collapse",
|
||||
responsiveLayoutCollapseUseFormatters: false,
|
||||
responsiveLayoutCollapseFormatter: Vue.$collapseFormatter,
|
||||
data: [
|
||||
{
|
||||
Bezeichnung: "",
|
||||
Organisationseinheit: "",
|
||||
Gültig_von: "",
|
||||
Gültig_bis: "",
|
||||
Wochenstunden: "",
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
//? option when wanting to hide the collapsed list
|
||||
funktionen_table_options: {
|
||||
height: 300,
|
||||
layout: "fitColumns",
|
||||
responsiveLayout: "collapse",
|
||||
responsiveLayoutCollapseUseFormatters: false,
|
||||
responsiveLayoutCollapseFormatter: Vue.$collapseFormatter,
|
||||
data: [
|
||||
{
|
||||
Bezeichnung: "",
|
||||
Organisationseinheit: "",
|
||||
Gültig_von: "",
|
||||
Gültig_bis: "",
|
||||
Wochenstunden: "",
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
//? option when wanting to hide the collapsed list
|
||||
|
||||
{
|
||||
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,
|
||||
},
|
||||
{
|
||||
title: "Bezeichnung",
|
||||
field: "Bezeichnung",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Organisationseinheit",
|
||||
field: "Organisationseinheit",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Gültig_von",
|
||||
field: "Gültig_von",
|
||||
headerFilter: true,
|
||||
resizable: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Gültig_bis",
|
||||
field: "Gültig_bis",
|
||||
headerFilter: true,
|
||||
resizable: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Wochenstunden",
|
||||
field: "Wochenstunden",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
{
|
||||
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,
|
||||
},
|
||||
{
|
||||
title: "Bezeichnung",
|
||||
field: "Bezeichnung",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Organisationseinheit",
|
||||
field: "Organisationseinheit",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Gültig_von",
|
||||
field: "Gültig_von",
|
||||
headerFilter: true,
|
||||
resizable: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Gültig_bis",
|
||||
field: "Gültig_bis",
|
||||
headerFilter: true,
|
||||
resizable: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: "Wochenstunden",
|
||||
field: "Wochenstunden",
|
||||
headerFilter: true,
|
||||
minWidth: 200,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
//? this is the prop passed to the dynamic component with the custom data of the view
|
||||
props: ["data"],
|
||||
methods: {
|
||||
funktionenTableBuilt: function () {
|
||||
this.$refs.funktionenTable.tabulator.setData(this.data.funktionen);
|
||||
},
|
||||
},
|
||||
//? this is the prop passed to the dynamic component with the custom data of the view
|
||||
props: ["data"],
|
||||
methods: {
|
||||
funktionenTableBuilt: function () {
|
||||
this.$refs.funktionenTable.tabulator.setData(this.data.funktionen);
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
editable() {
|
||||
return this.data?.editAllowed ?? false;
|
||||
},
|
||||
|
||||
personEmails() {
|
||||
return this.data?.emails ? this.data.emails : [];
|
||||
},
|
||||
computed: {
|
||||
editable() {
|
||||
return this.data?.editAllowed ?? false;
|
||||
},
|
||||
|
||||
profilInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
}
|
||||
personEmails() {
|
||||
return this.data?.emails ? this.data.emails : [];
|
||||
},
|
||||
|
||||
return {
|
||||
Vorname: this.data.vorname,
|
||||
Nachname: this.data.nachname,
|
||||
Username: this.data.username,
|
||||
Anrede: this.data.anrede,
|
||||
Titel: this.data.titel,
|
||||
Postnomen: this.data.postnomen,
|
||||
foto_sperre: this.data.foto_sperre,
|
||||
foto: this.data.foto,
|
||||
};
|
||||
},
|
||||
profilInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
}
|
||||
|
||||
roleInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
Vorname: this.data.vorname,
|
||||
Nachname: this.data.nachname,
|
||||
Username: this.data.username,
|
||||
Anrede: this.data.anrede,
|
||||
Titel: this.data.titel,
|
||||
Postnomen: this.data.postnomen,
|
||||
foto_sperre: this.data.foto_sperre,
|
||||
foto: this.data.foto,
|
||||
};
|
||||
},
|
||||
|
||||
return {
|
||||
Geburtsdatum: this.data.gebdatum,
|
||||
Geburtsort: this.data.gebort,
|
||||
Kurzzeichen: this.data.kurzbz,
|
||||
Telefon:
|
||||
(this.data.standort_telefon ? this.data.standort_telefon + " " + this.data.telefonklappe : this.data.telefonklappe),
|
||||
Büro: this.data.ort_kurzbz,
|
||||
};
|
||||
},
|
||||
},
|
||||
roleInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
}
|
||||
|
||||
template: /*html*/ `
|
||||
return {
|
||||
[`${this.$p.t('profil', 'Geburtsdatum')}`]: this.data.gebdatum,
|
||||
[`${this.$p.t('profil', 'Geburtsort')}`]: this.data.gebort,
|
||||
[`${this.$p.t('profil', 'Kurzzeichen')}`]: this.data.kurzbz,
|
||||
[`${this.$p.t('profil', 'Telefon')}`]:
|
||||
(this.data.standort_telefon ? this.data.standort_telefon + " " + this.data.telefonklappe : this.data.telefonklappe),
|
||||
[`${this.$p.t('profil', 'Büro')}`]: this.data.ort_kurzbz,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
<div class="container-fluid text-break fhc-form" >
|
||||
<!-- ROW -->
|
||||
<div class="row">
|
||||
<!-- HIDDEN QUICK LINKS -->
|
||||
<div class="d-md-none col-12 ">
|
||||
|
||||
<quick-links :title="$p.t('profil','quickLinks')" :mobile="true" ></quick-links>
|
||||
template: /*html*/ `
|
||||
|
||||
</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 -->
|
||||
|
||||
|
||||
|
||||
<!-- INFORMATION CONTENT START -->
|
||||
<!-- ROW WITH THE PROFIL INFORMATION -->
|
||||
<div class="container-fluid text-break fhc-form" >
|
||||
<!-- ROW -->
|
||||
<div class="row">
|
||||
<!-- HIDDEN QUICK LINKS -->
|
||||
<!-- TODO: 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 -->
|
||||
<!-- INFORMATION CONTENT START -->
|
||||
<!-- ROW WITH THE PROFIL INFORMATION -->
|
||||
<div class="row mb-4">
|
||||
<!-- FIRST KAESTCHEN -->
|
||||
<div class="col-lg-12 col-xl-6 ">
|
||||
<div class="row mb-4">
|
||||
|
||||
|
||||
|
||||
<!-- FIRST KAESTCHEN -->
|
||||
<div class="col-lg-12 col-xl-6 ">
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
|
||||
<!-- Profil Informationen -->
|
||||
<profil-information :title="$p.t('profil','mitarbeiterIn')" :data="profilInformation" :editable="editable"></profil-information>
|
||||
|
||||
</div>
|
||||
<div class="col">
|
||||
<!-- Profil Informationen -->
|
||||
<profil-information :title="$p.t('profil','mitarbeiterIn')" :data="profilInformation" :editable="editable"></profil-information>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- START OF SECOND PROFIL INFORMATION COLUMN -->
|
||||
|
||||
|
||||
<!-- START OF SECOND PROFIL INFORMATION COLUMN -->
|
||||
<!-- END OF PROFIL INFORMATION ROW -->
|
||||
<!-- INFORMATION CONTENT END -->
|
||||
</div>
|
||||
|
||||
<div class="col-xl-6 col-lg-12 ">
|
||||
</div>
|
||||
<div class="col-xl-6 col-lg-12 ">
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
|
||||
<!-- EMAILS -->
|
||||
<profil-emails :title="this.$p.t('person','email')" :data="personEmails"></profil-emails>
|
||||
|
||||
</div></div>
|
||||
|
||||
|
||||
<div class="col">
|
||||
<!-- EMAILS -->
|
||||
<profil-emails :title="this.$p.t('person','email')" :data="personEmails"></profil-emails>
|
||||
</div>
|
||||
</div>
|
||||
<!-- SECOND ROW OF SECOND COLUMN IN MAIN CONTENT -->
|
||||
<div class="row mb-4">
|
||||
|
||||
|
||||
<div class=" col-lg-12">
|
||||
|
||||
<!-- roleInformation -->
|
||||
<role-information :data="roleInformation" :title="$p.t('profil','mitarbeiterInformation')"></role-information>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- END OF SECOND ROW OF SECOND COLUMN IN MAIN CONTENT -->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- END OF THE SECOND INFORMATION COLUMN -->
|
||||
<div class=" col-lg-12">
|
||||
<!-- roleInformation -->
|
||||
<role-information :data="roleInformation" :title="$p.t('profil','mitarbeiterInformation')"></role-information>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- START OF THE SECOND PROFIL INFORMATION ROW -->
|
||||
|
||||
|
||||
<!-- ROW WITH PROFIL IMAGE AND INFORMATION END -->
|
||||
</div >
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- SECOND ROW UNDER THE PROFIL IMAGE AND INFORMATION WITH THE TABLES -->
|
||||
<div class="row">
|
||||
|
||||
<!-- FIRST TABLE -->
|
||||
<div class="col-12 mb-4" >
|
||||
<core-filter-cmpt @tableBuilt="funktionenTableBuilt" :title="$p.t('person','funktionen')" ref="funktionenTable" :tabulator-options="funktionen_table_options" tableOnly :sideMenu="false" />
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- END OF THE ROW WITH THE TABLES UNDER THE PROFIL INFORMATION -->
|
||||
<!-- END OF SECOND ROW OF SECOND COLUMN IN MAIN CONTENT -->
|
||||
<!-- END OF THE SECOND INFORMATION COLUMN -->
|
||||
</div>
|
||||
|
||||
<!-- END OF MAIN CONTENT COL -->
|
||||
</div>
|
||||
|
||||
<!-- START OF SIDE PANEL -->
|
||||
<div class="col-md-4 col-xxl-3 col-sm-12 text-break" >
|
||||
|
||||
<!-- VISIBLE UNTIL VIEWPORT MD -->
|
||||
<!-- START OF THE SECOND PROFIL INFORMATION ROW -->
|
||||
<!-- ROW WITH PROFIL IMAGE AND INFORMATION END -->
|
||||
</div >
|
||||
<!-- SECOND ROW UNDER THE PROFIL IMAGE AND INFORMATION WITH THE TABLES -->
|
||||
<div class="row">
|
||||
<!-- FIRST TABLE -->
|
||||
<div class="col-12 mb-4" >
|
||||
<core-filter-cmpt @tableBuilt="funktionenTableBuilt" :title="$p.t('person','funktionen')" ref="funktionenTable" :tabulator-options="funktionen_table_options" tableOnly :sideMenu="false" />
|
||||
</div>
|
||||
<!-- END OF THE ROW WITH THE TABLES UNDER THE PROFIL INFORMATION -->
|
||||
</div>
|
||||
<!-- END OF MAIN CONTENT COL -->
|
||||
</div>
|
||||
<!-- 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">
|
||||
|
||||
<!-- QUICKLINKS -->
|
||||
<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>
|
||||
|
||||
<quick-links :title="$p.t('profil','quickLinks')" ></quick-links>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- END OF SIDE PANEL -->
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- END OF CONTAINER ROW-->
|
||||
|
||||
</div>
|
||||
|
||||
<!-- END OF CONTAINER -->
|
||||
</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>
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import StudentProfil from "../../components/Cis/Profil/StudentProfil.js";
|
||||
import MitarbeiterProfil from "../../components/Cis/Profil/MitarbeiterProfil.js";
|
||||
import ViewStudentProfil from "../../components/Cis/Profil/StudentViewProfil.js";
|
||||
import ViewMitarbeiterProfil from "../../components/Cis/Profil/MitarbeiterViewProfil.js";
|
||||
import Loading from "../../components/Loader.js";
|
||||
import Phrasen from "../../plugin/Phrasen.js";
|
||||
import {setScrollbarWidth} from "../../helpers/CssVarCalcHelpers";
|
||||
|
||||
import StudentProfil from "./StudentProfil.js";
|
||||
import MitarbeiterProfil from "./MitarbeiterProfil.js";
|
||||
import ViewStudentProfil from "./StudentViewProfil.js";
|
||||
import ViewMitarbeiterProfil from "./MitarbeiterViewProfil.js";
|
||||
import Loading from "../../Loader.js";
|
||||
|
||||
Vue.$collapseFormatter = function (data) {
|
||||
//data - an array of objects containing the column title and value for each cell
|
||||
@@ -34,8 +31,8 @@ Vue.$collapseFormatter = function (data) {
|
||||
return Object.keys(data).length ? container : "";
|
||||
};
|
||||
|
||||
const profilApp = Vue.createApp({
|
||||
name: 'ProfilApp',
|
||||
export const Profil = {
|
||||
name: 'Profil',
|
||||
components: {
|
||||
StudentProfil,
|
||||
MitarbeiterProfil,
|
||||
@@ -43,7 +40,12 @@ const profilApp = Vue.createApp({
|
||||
ViewMitarbeiterProfil,
|
||||
Loading,
|
||||
},
|
||||
|
||||
props: {
|
||||
uid: {
|
||||
type: String,
|
||||
default: 'Profil'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
//? loading property is used for showing/hiding the loading modal
|
||||
@@ -56,8 +58,6 @@ const profilApp = Vue.createApp({
|
||||
notFoundUID: null,
|
||||
};
|
||||
},
|
||||
|
||||
//? use function syntax for provide so that we can access `this`
|
||||
provide() {
|
||||
return {
|
||||
profilUpdateStates: Vue.computed(() =>
|
||||
@@ -129,8 +129,36 @@ const profilApp = Vue.createApp({
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
async load() {
|
||||
// fetch profilUpdateStates to provide them to children components
|
||||
await this.$fhcApi.factory.profilUpdate.getStatus()
|
||||
.then((response) => {
|
||||
this.profilUpdateStates = response.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
this.$fhcApi.factory.profilUpdate.getTopic()
|
||||
.then((response) => {
|
||||
this.profilUpdateTopic = response.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
let uid = this.uid ?? location.pathname.split("/").pop();
|
||||
|
||||
this.$fhcApi.factory.profil.getView(uid).then((res) => {
|
||||
if (!res.data) {
|
||||
this.notFoundUID = uid;
|
||||
} else {
|
||||
this.view = res.data?.view;
|
||||
this.data = res.data?.data;
|
||||
}
|
||||
});
|
||||
},
|
||||
zustellAdressenCount() {
|
||||
if (!this.data || !this.data.adressen) {
|
||||
return null;
|
||||
@@ -172,22 +200,13 @@ const profilApp = Vue.createApp({
|
||||
}
|
||||
|
||||
return [...new Set(adressenArray)];
|
||||
|
||||
// OLD version
|
||||
// return this.data.adressen.filter(adresse => {
|
||||
// return adresse.zustelladresse;
|
||||
// }).map(adr => {
|
||||
|
||||
// return adr.adresse_id;
|
||||
// });
|
||||
|
||||
},
|
||||
|
||||
zustellKontakteCount() {
|
||||
if (!this.data || !this.data.kontakte) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//TODO: not sure if how it is implemented is the best way
|
||||
let kontakteArray = [];
|
||||
|
||||
if (this.data.profilUpdates?.length) {
|
||||
@@ -225,16 +244,8 @@ const profilApp = Vue.createApp({
|
||||
}
|
||||
|
||||
return [...new Set(kontakteArray)];
|
||||
|
||||
// OLD version
|
||||
// return this.data.kontakte.filter(kontakt => {
|
||||
// return kontakt.zustellung;
|
||||
// }).map(kon =>{
|
||||
// return kon.kontakt_id;
|
||||
// });
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
filteredEditData() {
|
||||
if (!this.data) {
|
||||
@@ -331,60 +342,26 @@ const profilApp = Vue.createApp({
|
||||
};
|
||||
}),
|
||||
},
|
||||
// Password_Change : {
|
||||
// title:"Passwort Ändern",
|
||||
// view:"ChangePassword",
|
||||
// data:{
|
||||
// test:"testvalue",
|
||||
// }
|
||||
// },
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
loading: function (newValue, oldValue) {
|
||||
loading: function (newValue) {
|
||||
if (newValue) {
|
||||
this.$refs.loadingModalRef.show();
|
||||
} else {
|
||||
this.$refs.loadingModalRef.hide();
|
||||
}
|
||||
},
|
||||
uid (newVal, oldVal) {
|
||||
console.log('watch uid', newVal)
|
||||
this.load()
|
||||
}
|
||||
},
|
||||
|
||||
async created() {
|
||||
// fetch profilUpdateStates to provide them to children components
|
||||
|
||||
await this.$fhcApi.factory.profilUpdate.getStatus()
|
||||
.then((response) => {
|
||||
this.profilUpdateStates = response.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
this.$fhcApi.factory.profilUpdate.getTopic()
|
||||
.then((response) => {
|
||||
this.profilUpdateTopic = response.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
//? uid contains the last part of the uri
|
||||
let uid = location.pathname.split("/").pop();
|
||||
|
||||
this.$fhcApi.factory.profil.getView(uid).then((res) => {
|
||||
if (!res.data) {
|
||||
this.notFoundUID = uid;
|
||||
} else {
|
||||
this.view = res.data?.view;
|
||||
this.data = res.data?.data;
|
||||
}
|
||||
});
|
||||
this.load()
|
||||
},
|
||||
|
||||
template: `
|
||||
<div>
|
||||
<div v-if="notFoundUID">
|
||||
@@ -395,9 +372,6 @@ const profilApp = Vue.createApp({
|
||||
<component :is="view" :data="data" :editData="filteredEditData" ></component>
|
||||
</div>
|
||||
</div>`,
|
||||
});
|
||||
setScrollbarWidth();
|
||||
profilApp
|
||||
.use(primevue.config.default, {zIndex: {overlay: 9999}})
|
||||
.use(Phrasen)
|
||||
.mount("#content");
|
||||
}
|
||||
|
||||
export default Profil
|
||||
@@ -19,7 +19,7 @@ export default{
|
||||
<div :class="{...(data.anmerkung? {'col-11':true, 'col-md-6':true, 'col-xl-11':true, 'col-xxl-6':true} : {'col-10':true, 'col-xl-9':true, 'col-xxl-10':true})}">
|
||||
<!-- rendering KONTAKT emails -->
|
||||
<div class="form-underline ">
|
||||
<div class="form-underline-titel">{{data.kontakttyp}}</div>
|
||||
<div class="form-underline-titel">{{$p.t('profil',data.kontakttyp.toUpperCase())}}</div>
|
||||
<a v-if="data.kontakt.includes('@')" role="link" :aria-disabled="view?true:false" :href="!view?('mailto:'+data.kontakt):null" class="form-underline-content">{{data.kontakt}} </a>
|
||||
<a v-else role="link" :aria-disabled="view?true:false" :href="!view?('tel:'+data.kontakt):null" class="form-underline-content">{{data.kontakt}} </a>
|
||||
</div>
|
||||
|
||||
@@ -172,13 +172,13 @@ export default {
|
||||
}
|
||||
|
||||
return {
|
||||
Geburtsdatum: this.data.gebdatum,
|
||||
Geburtsort: this.data.gebort,
|
||||
Personenkennzeichen: this.data.personenkennzeichen,
|
||||
Studiengang: this.data.studiengang,
|
||||
Semester: this.data.semester,
|
||||
Verband: this.data.verband,
|
||||
Gruppe: this.data.gruppe.trim(),
|
||||
[`${this.$p.t('profil','Geburtsdatum')}`]: this.data.gebdatum,
|
||||
[`${this.$p.t('profil', 'Geburtsort')}`]: this.data.gebort,
|
||||
[`${this.$p.t('person', 'personenkennzeichen')}`]: this.data.personenkennzeichen,
|
||||
[`${this.$p.t('lehre', 'studiengang')}`]: this.data.studiengang,
|
||||
[`${this.$p.t('lehre', 'semester')}`]: this.data.semester,
|
||||
[`${this.$p.t('lehre', 'lehrverband')}`]: this.data.verband,
|
||||
[`${this.$p.t('lehre', 'gruppe')}`]: this.data.gruppe.trim(),
|
||||
};
|
||||
},
|
||||
},
|
||||
@@ -194,11 +194,12 @@ export default {
|
||||
<div class="row">
|
||||
<!-- HIDDEN QUICK LINKS -->
|
||||
<div class="d-md-none col-12 ">
|
||||
<div class="row py-2">
|
||||
<!--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>
|
||||
</div>-->
|
||||
|
||||
<!-- Bearbeiten Button -->
|
||||
<div v-if="editable" class="row ">
|
||||
@@ -314,12 +315,12 @@ 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 -->
|
||||
<quick-links :title="$p.t('profil','quickLinks')"></quick-links>
|
||||
</div>
|
||||
</div>
|
||||
</div>-->
|
||||
<!-- Bearbeiten Button -->
|
||||
<div class="row d-none d-md-block">
|
||||
<div class="col mb-3">
|
||||
|
||||
@@ -5,182 +5,142 @@ import RoleInformation from "./ProfilComponents/RoleInformation.js";
|
||||
import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
components: {
|
||||
QuickLinks,
|
||||
Mailverteiler,
|
||||
ProfilEmails,
|
||||
RoleInformation,
|
||||
ProfilInformation,
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
components: {
|
||||
QuickLinks,
|
||||
Mailverteiler,
|
||||
ProfilEmails,
|
||||
RoleInformation,
|
||||
ProfilInformation,
|
||||
},
|
||||
|
||||
props: ["data"],
|
||||
methods: {},
|
||||
props: ["data"],
|
||||
methods: {},
|
||||
|
||||
computed: {
|
||||
editable() {
|
||||
return this.data?.editAllowed ?? false;
|
||||
},
|
||||
profilInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
}
|
||||
computed: {
|
||||
editable() {
|
||||
return this.data?.editAllowed ?? false;
|
||||
},
|
||||
profilInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
Vorname: this.data.vorname,
|
||||
Nachname: this.data.nachname,
|
||||
Username: this.data.username,
|
||||
Anrede: this.data.anrede,
|
||||
Titel: this.data.titel,
|
||||
Postnomen: this.data.postnomen,
|
||||
foto_sperre: this.data.foto_sperre,
|
||||
foto: this.data.foto,
|
||||
};
|
||||
},
|
||||
return {
|
||||
Vorname: this.data.vorname,
|
||||
Nachname: this.data.nachname,
|
||||
Username: this.data.username,
|
||||
Anrede: this.data.anrede,
|
||||
Titel: this.data.titel,
|
||||
Postnomen: this.data.postnomen,
|
||||
foto_sperre: this.data.foto_sperre,
|
||||
foto: this.data.foto,
|
||||
};
|
||||
},
|
||||
|
||||
personEmails() {
|
||||
return this.data?.emails ? this.data.emails : [];
|
||||
},
|
||||
personEmails() {
|
||||
return this.data?.emails ? this.data.emails : [];
|
||||
},
|
||||
|
||||
roleInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
}
|
||||
roleInformation() {
|
||||
if (!this.data) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
Geburtsdatum: this.data.gebdatum,
|
||||
Geburtsort: this.data.gebort,
|
||||
Personenkennzeichen: this.data.personenkennzeichen,
|
||||
Studiengang: this.data.studiengang,
|
||||
Semester: this.data.semester,
|
||||
Verband: this.data.verband,
|
||||
Gruppe: this.data.gruppe.trim(),
|
||||
};
|
||||
},
|
||||
},
|
||||
return {
|
||||
Geburtsdatum: this.data.gebdatum,
|
||||
Geburtsort: this.data.gebort,
|
||||
Personenkennzeichen: this.data.personenkennzeichen,
|
||||
Studiengang: this.data.studiengang,
|
||||
Semester: this.data.semester,
|
||||
Verband: this.data.verband,
|
||||
Gruppe: this.data.gruppe.trim(),
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {},
|
||||
mounted() {
|
||||
},
|
||||
|
||||
template: /*html*/ `
|
||||
template: /*html*/ `
|
||||
|
||||
<div class="container-fluid text-break fhc-form" >
|
||||
<div class="container-fluid text-break fhc-form" >
|
||||
<!-- ROW -->
|
||||
<div class="row">
|
||||
<!-- HIDDEN QUICK LINKS -->
|
||||
<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 -->
|
||||
|
||||
|
||||
|
||||
<!-- INFORMATION CONTENT START -->
|
||||
<!-- ROW WITH THE PROFIL INFORMATION -->
|
||||
<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 -->
|
||||
<!-- INFORMATION CONTENT START -->
|
||||
<!-- ROW WITH THE PROFIL INFORMATION -->
|
||||
<div class="row mb-4">
|
||||
<!-- FIRST KAESTCHEN -->
|
||||
<div class="col-lg-12 col-xl-6 ">
|
||||
<div class="row mb-4">
|
||||
|
||||
<!-- FIRST KAESTCHEN -->
|
||||
|
||||
<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')" :editable="editable"></profil-information>
|
||||
|
||||
</div>
|
||||
<div class="col">
|
||||
<profil-information :data="profilInformation" :title="$p.t('profil','studentIn')" :editable="editable"></profil-information>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- START OF SECOND PROFIL INFORMATION COLUMN -->
|
||||
|
||||
<!-- START OF SECOND PROFIL INFORMATION COLUMN -->
|
||||
<!-- END OF PROFIL INFORMATION ROW -->
|
||||
<!-- INFORMATION CONTENT END -->
|
||||
</div>
|
||||
|
||||
<div class="col-xl-6 col-lg-12 ">
|
||||
</div>
|
||||
<div class="col-xl-6 col-lg-12 ">
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
|
||||
<!-- EMAILS -->
|
||||
<profil-emails :title="this.$p.t('person','email')" :data="personEmails"></profil-emails>
|
||||
|
||||
</div></div>
|
||||
|
||||
<!-- SECOND ROW OF SECOND COLUMN IN MAIN CONTENT -->
|
||||
<div class=" row mb-4">
|
||||
|
||||
|
||||
<div class=" col-lg-12">
|
||||
|
||||
<role-information :title="$p.t('profil','studentInformation')" :data="roleInformation"></role-information>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- END OF SECOND ROW OF SECOND COLUMN IN MAIN CONTENT -->
|
||||
|
||||
<!-- END OF THE SECOND INFORMATION COLUMN -->
|
||||
<div class="col">
|
||||
<!-- EMAILS -->
|
||||
<profil-emails :title="this.$p.t('person','email')" :data="personEmails"></profil-emails>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- START OF THE SECOND PROFIL INFORMATION ROW -->
|
||||
|
||||
|
||||
<!-- ROW WITH PROFIL IMAGE AND INFORMATION END -->
|
||||
</div >
|
||||
|
||||
<!-- END OF MAIN CONTENT COL -->
|
||||
</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 -->
|
||||
<!-- SECOND ROW OF SECOND COLUMN IN MAIN CONTENT -->
|
||||
<div class=" row mb-4">
|
||||
<div class=" col-lg-12">
|
||||
<role-information :title="$p.t('profil','studentInformation')" :data="roleInformation"></role-information>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END OF SECOND ROW OF SECOND COLUMN IN MAIN CONTENT -->
|
||||
<!-- END OF THE SECOND INFORMATION COLUMN -->
|
||||
</div>
|
||||
<!-- START OF THE SECOND PROFIL INFORMATION ROW -->
|
||||
<!-- ROW WITH PROFIL IMAGE AND INFORMATION END -->
|
||||
</div >
|
||||
<!-- END OF MAIN CONTENT COL -->
|
||||
</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 SECOND ROW IN THE SIDE PANEL -->
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
|
||||
<quick-links :title="$p.t('profil','quickLinks')"></quick-links>
|
||||
|
||||
<div class="col">
|
||||
|
||||
|
||||
|
||||
<!-- HIER SIND DIE MAILVERTEILER -->
|
||||
</div>
|
||||
</div>-->
|
||||
<!-- START OF THE SECOND ROW IN THE SIDE PANEL -->
|
||||
<div class="row">
|
||||
<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>
|
||||
|
||||
<!-- END OF SIDE PANEL -->
|
||||
</div>
|
||||
|
||||
|
||||
<!-- END OF CONTAINER ROW-->
|
||||
|
||||
</div>
|
||||
|
||||
<!-- END OF CONTAINER -->
|
||||
</div>
|
||||
|
||||
<!-- END OF THE SECOND ROW IN THE SIDE PANEL -->
|
||||
</div>
|
||||
<!-- END OF SIDE PANEL -->
|
||||
</div>
|
||||
<!-- END OF CONTAINER ROW-->
|
||||
</div>
|
||||
<!-- END OF CONTAINER -->
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -1,30 +1,27 @@
|
||||
export default {
|
||||
data(){
|
||||
return {
|
||||
allActiveLanguages: null,
|
||||
allActiveLanguages: FHC_JS_DATA_STORAGE_OBJECT.server_languages,
|
||||
}
|
||||
},
|
||||
emits: ['languageChanged'],
|
||||
methods:{
|
||||
changeLanguage: function(lang){
|
||||
if(this.allActiveLanguages.some(l => l === lang))
|
||||
if(this.allActiveLanguages.some(l => l.sprache === lang))
|
||||
{
|
||||
this.$p.setLanguage(lang, this.$fhcApi);
|
||||
this.$p.setLanguage(lang, this.$fhcApi)
|
||||
.then(res => res.data)
|
||||
.then(data =>
|
||||
{
|
||||
this.$emit('languageChanged', lang);
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted(){
|
||||
this.$fhcApi.factory.phrasen.getActiveDbLanguages()
|
||||
.then(res => res.data)
|
||||
.then(
|
||||
(langs) => {
|
||||
this.allActiveLanguages = langs;
|
||||
}
|
||||
);
|
||||
},
|
||||
template:/*html*/`
|
||||
<div class="container">
|
||||
<div class="row justify-content-center align-items-center flex-nowrap overflow-hidden">
|
||||
<button v-for="lang in allActiveLanguages" @click.prevent="changeLanguage(lang)" class="col text-white fhc-entry btn text-center w-100" :selected="$p.user_language.value==lang?'':null">{{lang}}</button>
|
||||
<button v-for="lang in allActiveLanguages" @click.prevent="changeLanguage(lang.sprache)" class="col text-white fhc-entry btn text-center w-100" :selected="$p.user_language.value==lang.sprache?'':null">{{lang.bezeichnung}}</button>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
|
||||
+50
-23
@@ -1,27 +1,33 @@
|
||||
import FhcCalendar from "../../components/Calendar/Calendar.js";
|
||||
import Phrasen from "../../plugin/Phrasen.js";
|
||||
import CalendarDate from "../../composables/CalendarDate.js";
|
||||
import LvModal from "../../components/Cis/Mylv/LvModal.js";
|
||||
import LvInfo from "../../components/Cis/Mylv/LvInfo.js"
|
||||
import LvMenu from "../../components/Cis/Mylv/LvMenu.js"
|
||||
import FhcCalendar from "../../Calendar/Calendar.js";
|
||||
import CalendarDate from "../../../composables/CalendarDate.js";
|
||||
import LvModal from "../Mylv/LvModal.js";
|
||||
import LvInfo from "../Mylv/LvInfo.js"
|
||||
import LvMenu from "../Mylv/LvMenu.js"
|
||||
|
||||
|
||||
const app = Vue.createApp({
|
||||
name: 'StundenplanApp',
|
||||
export const Stundenplan = {
|
||||
name: 'Stundenplan',
|
||||
data() {
|
||||
return {
|
||||
lv_id: null,
|
||||
events: null,
|
||||
calendarDate: new CalendarDate(new Date()),
|
||||
currentlySelectedEvent: null,
|
||||
currentDay: new Date(),
|
||||
minimized: false,
|
||||
viewData: JSON.parse(this.viewDataString ?? '{}'),
|
||||
}
|
||||
},
|
||||
props: [
|
||||
"viewDataString"
|
||||
],
|
||||
components: {
|
||||
FhcCalendar, LvModal, LvMenu, LvInfo
|
||||
},
|
||||
computed:{
|
||||
lv_id() { // computed so we can theoretically change path/lva selection and reload without page refresh
|
||||
const pathParts = window.location.pathname.split('/').filter(Boolean);
|
||||
const id = pathParts[pathParts.length - 1];
|
||||
return id && !isNaN(Number(id)) ? id : null; // only return id if it is a number string since the path might contain invalid elements
|
||||
},
|
||||
weekFirstDay: function () {
|
||||
return this.calendarDateToString(this.calendarDate.cdFirstDayOfWeek);
|
||||
},
|
||||
@@ -34,15 +40,20 @@ const app = Vue.createApp({
|
||||
monthLastDay: function () {
|
||||
return this.calendarDateToString(this.calendarDate.cdLastDayOfCalendarMonth);
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
methods:{
|
||||
convertTime: function([hour,minute]){
|
||||
let date = new Date();
|
||||
date.setHours(hour);
|
||||
date.setMinutes(minute);
|
||||
// returns date string as hh:mm
|
||||
return date.toLocaleTimeString(this.$p.user_locale, { hour: '2-digit', minute: '2-digit', hour12:false});
|
||||
|
||||
},
|
||||
setSelectedEvent: function (event) {
|
||||
this.currentlySelectedEvent = event;
|
||||
},
|
||||
getLvID: function () {
|
||||
this.lv_id = window.location.pathname
|
||||
},
|
||||
selectDay: function(day){
|
||||
this.currentDay = day;
|
||||
},
|
||||
@@ -76,7 +87,7 @@ const app = Vue.createApp({
|
||||
},
|
||||
loadEvents: function(){
|
||||
Promise.allSettled([
|
||||
this.$fhcApi.factory.stundenplan.getStundenplan(this.monthFirstDay, this.monthLastDay),
|
||||
this.$fhcApi.factory.stundenplan.getStundenplan(this.monthFirstDay, this.monthLastDay, this.viewData.lv_id),
|
||||
this.$fhcApi.factory.stundenplan.getStundenplanReservierungen(this.monthFirstDay, this.monthLastDay)
|
||||
]).then((result) => {
|
||||
let promise_events = [];
|
||||
@@ -110,7 +121,6 @@ const app = Vue.createApp({
|
||||
created()
|
||||
{
|
||||
this.loadEvents();
|
||||
this.getLvID();
|
||||
},
|
||||
template:/*html*/`
|
||||
<h2>{{$p.t('lehre/stundenplan')}}</h2>
|
||||
@@ -123,14 +133,31 @@ const app = Vue.createApp({
|
||||
</span>
|
||||
</template>
|
||||
<template #weekPage="{event,day,isSelected}">
|
||||
<div @click="showModal(event?.orig)" type="button" :class="{'selectedEvent':isSelected}" class="fhc-entry border border-secondary border d-flex flex-column align-items-center justify-content-evenly h-100">
|
||||
<span>{{event?.orig.topic}}</span>
|
||||
<span v-for="lektor in event?.orig.lektor">{{lektor.kurzbz}}</span>
|
||||
<span>{{event?.orig.ort_kurzbz}}</span>
|
||||
<div @click="showModal(event?.orig); console.log(event.orig)" type="button" :class="{'selectedEvent':isSelected}"
|
||||
class="fhc-entry position-relative border border-secondary border d-flex flex-row align-items-center
|
||||
justify-content-evenly h-100" style="max-height: 75px; overflow: auto;">
|
||||
|
||||
<div v-if="event?.orig?.beginn && event?.orig?.ende" >
|
||||
<div class="d-flex flex-column p-4 border-end border-secondary">
|
||||
<span class="small">{{convertTime(event.orig.beginn.split(":"))}}</span>
|
||||
<span class="small">{{convertTime(event.orig.ende.split(":"))}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-column flex-grow-1 align-items-center">
|
||||
<span>{{event?.orig.topic}}</span>
|
||||
<span v-for="lektor in event?.orig.lektor">{{lektor.kurzbz}}</span>
|
||||
<span>{{event?.orig.ort_kurzbz}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #dayPage="{event,day,mobile}">
|
||||
<div @click="mobile? showModal(event?.orig):null" type="button" class="fhc-entry border border-secondary border row m-0 h-100 justify-content-center align-items-center text-center">
|
||||
<div class="col-auto" v-if="event?.orig?.beginn && event?.orig?.ende" >
|
||||
<div class="d-flex flex-column p-4 border-end border-secondary">
|
||||
<span class="small">{{convertTime(event.orig.beginn.split(":"))}}</span>
|
||||
<span class="small">{{convertTime(event.orig.ende.split(":"))}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col ">
|
||||
<p>Lehrveranstaltung:</p>
|
||||
<p class="m-0">{{event?.orig.topic}}</p>
|
||||
@@ -158,6 +185,6 @@ const app = Vue.createApp({
|
||||
</template>
|
||||
</fhc-calendar>
|
||||
`
|
||||
});
|
||||
app.use(Phrasen, {reload: true});
|
||||
app.mount('#content');
|
||||
}
|
||||
|
||||
export default Stundenplan
|
||||
@@ -7,22 +7,32 @@ export default {
|
||||
DashboardSection,
|
||||
DashboardWidgetPicker
|
||||
},
|
||||
props: [
|
||||
"dashboard",
|
||||
"viewDataString"
|
||||
],
|
||||
props: {
|
||||
dashboard: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: 'CIS'
|
||||
},
|
||||
viewData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({name: '', uid: ''}),
|
||||
validator(value) {
|
||||
return value && value.name && value.uid
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
sections: [],
|
||||
widgets: null,
|
||||
viewData: JSON.parse(this.viewDataString),
|
||||
editMode: false
|
||||
editMode: false,
|
||||
viewDataInternal: this.viewData
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
editMode: Vue.computed(()=>this.editMode),
|
||||
viewData: Vue.computed(()=>Vue.reactive(this.viewData)),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -151,10 +161,16 @@ export default {
|
||||
}
|
||||
}).catch(err => console.error('ERROR:', err));
|
||||
},
|
||||
async beforeMount() {
|
||||
if(!this.viewData.name || !this.viewData.uid) {
|
||||
const res = await this.$fhcApi.factory.dashboard.getViewData()
|
||||
this.viewDataInternal = res.data
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="core-dashboard">
|
||||
<h3>
|
||||
{{ $p.t('global/personalGreeting', [ viewData?.name ]) }}
|
||||
{{ $p.t('global/personalGreeting', [ viewDataInternal?.name ]) }}
|
||||
<button style="margin-left: 8px;" class="btn" @click="editMode = !editMode"><i class="fa-solid fa-gear"></i></button>
|
||||
</h3>
|
||||
<dashboard-section v-for="(section, index) in sections" :key="section.name" :seperator="index" :name="section.name" :widgets="section.widgets" @widgetAdd="widgetAdd" @widgetUpdate="widgetUpdate" @widgetRemove="widgetRemove"></dashboard-section>
|
||||
|
||||
@@ -11,10 +11,14 @@ export default {
|
||||
data: () => ({
|
||||
allNewsList: [],
|
||||
singleNews: {},
|
||||
selected: null
|
||||
selected: null,
|
||||
size:0,
|
||||
}),
|
||||
mixins: [AbstractWidget],
|
||||
computed: {
|
||||
sizeClass() {
|
||||
return 'fhc-news-' + ['xs', 'sm', 'md', 'lg'][this.size];
|
||||
},
|
||||
getNewsWidgetStyle() {
|
||||
return this.width == 1 ? "padding: 1rem 1rem;" : "padding: 0px;"
|
||||
},
|
||||
@@ -30,41 +34,29 @@ export default {
|
||||
|
||||
return this.allNewsList.slice(0, quantity);
|
||||
},
|
||||
placeHolderImgURL: function () {
|
||||
return (
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
"skin/images/fh_technikum_wien_illustration_klein.png"
|
||||
);
|
||||
},
|
||||
activeNews() {
|
||||
return this.allNewsList.find(news => news.minimized === false) ?? this.allNewsList[0] ?? null
|
||||
carouselItems() {
|
||||
return this.allNewsList.reduce((acc, cur) => {
|
||||
const el = document.getElementById('card-'+cur.news_id)
|
||||
acc.push(el);
|
||||
return acc
|
||||
}, [])
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$fhcApi.factory.cms
|
||||
.news(MAX_LOADED_NEWS)
|
||||
.then((res) => {
|
||||
this.allNewsList = Array.from(Object.values(res.data));
|
||||
|
||||
this.selected = this.allNewsList.length ? this.allNewsList[0] : null
|
||||
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("ERROR: ", err.response.data);
|
||||
});
|
||||
|
||||
this.$emit("setConfig", false);
|
||||
},
|
||||
methods: {
|
||||
isString(value){
|
||||
return Object.prototype.toString.call(value) === '[object String]';
|
||||
},
|
||||
setNext(){
|
||||
const thisIndex = this.allNewsList.findIndex(n=>n.news_id == this.selected.news_id)
|
||||
const nextIndex = thisIndex == (this.allNewsList.length - 1) ? 0 : thisIndex + 1
|
||||
this.setSelected(this.allNewsList[nextIndex])
|
||||
|
||||
this.setSelected(this.allNewsList[nextIndex])
|
||||
},
|
||||
setPrev() {
|
||||
const thisIndex = this.allNewsList.findIndex(n=>n.news_id == this.selected.news_id)
|
||||
const prevIndex = thisIndex ? thisIndex - 1 : this.allNewsList.length - 1
|
||||
this.setSelected(this.allNewsList[prevIndex], 'prev')
|
||||
|
||||
this.setSelected(this.allNewsList[prevIndex])
|
||||
},
|
||||
getMenuItemClass(news) {
|
||||
let classString = ''
|
||||
@@ -73,45 +65,23 @@ export default {
|
||||
}
|
||||
return classString
|
||||
},
|
||||
getDynClassCarouselItem(news, index) {
|
||||
// sets classes prev/active/next for bootstrap carousel
|
||||
let classString = ''
|
||||
async setSelected(news) {
|
||||
let clickedElement = document.getElementById('card-'+news.news_id);
|
||||
let clickedElementIndex = this.allNewsList.indexOf(news);
|
||||
let oldElementIndex = this.allNewsList.indexOf(this.selected);
|
||||
|
||||
// return active class to news === selected OR very first news
|
||||
if((this.selected.news_id === news.news_id) || (this.selected === null && index === 0)) {
|
||||
classString = 'active';
|
||||
} else { // set prev/next class for news
|
||||
const selectedIndex = this.newsList.indexOf(this.selected)
|
||||
const ownIndex = this.newsList.indexOf(news)
|
||||
const isPrev = (ownIndex + 1) === selectedIndex || (ownIndex === this.newsList.length - 1 && selectedIndex === 0)
|
||||
if(isPrev) {
|
||||
classString += ' carousel-item-prev'
|
||||
}
|
||||
const isNext = (ownIndex - 1) === selectedIndex || (ownIndex === 0 && selectedIndex === this.newsList.length - 1)
|
||||
if(isNext) {
|
||||
classString += ' carousel-item-next'
|
||||
}
|
||||
}
|
||||
|
||||
return classString;
|
||||
},
|
||||
setSelected(news, direction = "next") {
|
||||
if (this.selected && news && this.selected === news) return
|
||||
|
||||
const oldCard = document.getElementById('card-'+this.selected.news_id)
|
||||
|
||||
// TODO: to show animation of non neighbour item through menu reapply css classes
|
||||
if(direction === 'next') {
|
||||
// set nextCard .carousel-item-next.carousel-item-start
|
||||
oldCard.classList.add('carousel-item-start')
|
||||
|
||||
} else {
|
||||
// set prevCard .carousel-item-prev.carousel-item-end
|
||||
oldCard.classList.add('carousel-item-end')
|
||||
//if the clicked element is already active, do nothing
|
||||
if(clickedElementIndex === oldElementIndex) return;
|
||||
//add prev/next class to the clicked element
|
||||
if(clickedElementIndex > oldElementIndex){
|
||||
clickedElement.classList.add('carousel-item-next');
|
||||
}else{
|
||||
clickedElement.classList.add('carousel-item-prev');
|
||||
}
|
||||
|
||||
this.selected = news
|
||||
|
||||
// move to clicked element
|
||||
await Vue.nextTick(() => { this.carouselInstance.to(clickedElementIndex); })
|
||||
this.selected = news;
|
||||
},
|
||||
contentURI: function (content_id) {
|
||||
return (
|
||||
@@ -133,10 +103,52 @@ export default {
|
||||
this.$refs.newsModal.show();
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$emit("setConfig", false);
|
||||
this.$fhcApi.factory.cms
|
||||
.news(MAX_LOADED_NEWS)
|
||||
.then(res => res.data)
|
||||
.then((news) => {
|
||||
this.allNewsList = Array.from(Object.values(news));
|
||||
this.selected = this.allNewsList.length ? this.allNewsList[0] : null
|
||||
Vue.nextTick(()=>{
|
||||
if (Array.isArray(this.$refs.carouselItems) && this.$refs.carouselItems.length >0) {
|
||||
this.$refs.carouselItems[0].classList.add("active")
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("ERROR: ", err);
|
||||
});
|
||||
|
||||
|
||||
|
||||
},
|
||||
mounted() {
|
||||
if (this.$refs.container) {
|
||||
new ResizeObserver(entries => {
|
||||
for (const entry of entries) {
|
||||
let w = entry.contentBoxSize ? entry.contentBoxSize[0].inlineSize : entry.contentRect.width;
|
||||
// TODO(chris): rework sizing
|
||||
if (w > 600)
|
||||
this.size = 3;
|
||||
else if (w > 350)
|
||||
this.size = 2;
|
||||
else if (w > 250)
|
||||
this.size = 1;
|
||||
else
|
||||
this.size = 0;
|
||||
}
|
||||
}).observe(this.$refs.container);
|
||||
}
|
||||
this.carouselInstance = new bootstrap.Carousel(this.$refs.carousel, {
|
||||
wrap: false, // keep this off even though it actually wraps
|
||||
interval: false
|
||||
});
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div class="widgets-news h-100" :style="getNewsWidgetStyle">
|
||||
<div ref="container" class="widgets-news h-100" :class="sizeClass" :style="getNewsWidgetStyle">
|
||||
<div class="d-flex flex-column h-100">
|
||||
|
||||
<div class="h-100" style="overflow-y: auto" v-if="width == 1">
|
||||
<div v-for="(news, index) in newsList" :key="news.news_id" class="mt-2">
|
||||
<div v-if="index > 0 " class="fhc-seperator"></div>
|
||||
@@ -144,40 +156,40 @@ export default {
|
||||
<span class="small text-muted">{{ formatDateTime(news.insertamum) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="row h-100">
|
||||
<!-- TODO: mobile responsiveness of this part-->
|
||||
<div v-else class="row h-100 g-0">
|
||||
<div :class="'col-'+(width == 2? 6 : 4) + ' h-100 g-0'" style="overflow: auto;">
|
||||
<template v-for="news in newsList" :key="'menu-'+news.news_id">
|
||||
|
||||
<div class="row fhc-news-menu-item" @click="setSelected(news)" :class="getMenuItemClass(news)" style="margin-right: 0px; margin-left: 0px;">
|
||||
<div class="col-8 fhc-news-menu-item-betreff" style="overflow-y: hidden;"><p>{{news.content_obj.betreff ?? ''}}</p></div>
|
||||
<span class="fhc-news-menu-item-date fw-bold"
|
||||
>{{ news.datum ?? ''}}</span>
|
||||
<div class="position-relative">
|
||||
<div class="row fhc-news-menu-item " @click="setSelected(news)" :class="getMenuItemClass(news)" style="margin-right: 0px; margin-left: 0px; overflow-y: hidden;">
|
||||
<div class="col-8 fhc-news-menu-item-betreff">
|
||||
<p class="fhc-news-text mb-0">
|
||||
{{news.content_obj.betreff ?? ''}}
|
||||
</p>
|
||||
</div>
|
||||
<span style="top:2px; right:0" class=" position-absolute d-none d-xl-block fhc-news-text fhc-news-menu-item-date fw-bold">
|
||||
{{ news.datumformatted ?? ''}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</div>
|
||||
<div :class="'col-'+(width == 2? 6 : 8) + ' h-100'" style="padding-left: 0px; padding-right: 0px;" ref="htmlContent">
|
||||
<div class="container h-100" style="padding: 0px;" ref="carocontainer">
|
||||
|
||||
<div id="carouselExample" style="height: 100%;" class="carousel slide fhc-carousel" data-bs-ride="carousel"
|
||||
data-bs-interval="false"
|
||||
ref="carocontrols">
|
||||
<div class="carousel-indicators">
|
||||
<button v-for="(news, index) in newsList" :id="'indicator-'+news_news_id" type="button" data-bs-target="#carouselExample" data-bs-slide-to="index"></button>
|
||||
</div>
|
||||
<div id="FhcCarouselContainer" style="height: 100%;" ref="carousel" class="carousel slide fhc-carousel" data-bs-ride="carousel" data-bs-interval="false">
|
||||
|
||||
<div class="carousel-inner" style="height: 100%; max-width: 100%;">
|
||||
<div v-for="(news, index) in newsList" class="carousel-item" :class="getDynClassCarouselItem(news, index)" style="overflow-y: auto; height: 100%;" :id="'card-'+news.news_id" v-html="news.content_obj.content">
|
||||
|
||||
<div class="carousel-inner" ref="carouselInner" style="height: 100%; max-width: 100%;">
|
||||
<div ref="carouselItems" v-for="(news, index) in newsList" class="carousel-item " style="overflow-y: auto; height: 100%;" :id="'card-'+news.news_id" v-html="news.content_obj.content">
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO: prev/next button styling && placement-->
|
||||
<button @click="setPrev" style="z-index: 9999; color: black; opacity: 1;" data-bs-target="#carouselExample" class="carousel-control-prev" type="button">
|
||||
<i class="fa fa-chevron-left"></i>
|
||||
<button @click="setPrev" @focus="$event.target.blur()" style="z-index: 100; color: black; overflow: hidden; margin-left: 10px; width:35px;" data-bs-target="#FhcCarouselContainer" class="carousel-control-prev" type="button">
|
||||
<div class="border rounded-circle" style="padding-left: 0.4rem; padding-right: 0.4rem; background-color:rgba(138,138,138,0.4)">
|
||||
<i class="fa fa-chevron-left"></i>
|
||||
</div>
|
||||
</button>
|
||||
<button @click="setNext" style="z-index: 9999; color: black; opacity: 1;" data-bs-target="#carouselExample" class="carousel-control-next" type="button">
|
||||
<i class="fa fa-chevron-right"></i>
|
||||
<button @click="setNext" @focus="$event.target.blur()" style="z-index: 100; color: black; overflow: hidden; margin-right: 10px; width:35px;" data-bs-target="#FhcCarouselContainer" class="carousel-control-next" type="button">
|
||||
<div class="border rounded-circle" style="padding-left: 0.4rem; padding-right: 0.4rem; background-color:rgba(138,138,138,0.4)">
|
||||
<i class="fa fa-chevron-right"></i>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import AbstractWidget from './Abstract.js';
|
||||
import StudiengangInformation from '../Cis/Cms/StudiengangInformation/StudiengangInformation.js';
|
||||
|
||||
export default {
|
||||
data(){
|
||||
return {};
|
||||
},
|
||||
components:{
|
||||
StudiengangInformation,
|
||||
},
|
||||
mixins:[AbstractWidget],
|
||||
mounted(){
|
||||
this.$emit('setConfig', false);
|
||||
},
|
||||
template:/*html*/`
|
||||
<div class="p-3 h-100 overflow-scroll">
|
||||
<studiengang-information displayWidget ></studiengang-information>
|
||||
</div>
|
||||
`
|
||||
};
|
||||
@@ -1,3 +1,6 @@
|
||||
import CoreForm from "../Form/Form.js";
|
||||
import FormInput from "../Form/Input.js";
|
||||
import BsModal from "../Bootstrap/Modal.js";
|
||||
import AbstractWidget from './Abstract';
|
||||
|
||||
export default {
|
||||
@@ -9,13 +12,19 @@ export default {
|
||||
default: false
|
||||
}
|
||||
},
|
||||
components:{
|
||||
CoreForm,
|
||||
FormInput,
|
||||
BsModal
|
||||
},
|
||||
data: () => ({
|
||||
bookmark_id: null,
|
||||
title_input: "",
|
||||
url_input: "",
|
||||
validation: {
|
||||
invalidURL: false,
|
||||
invalidTitel: false,
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
computed: {
|
||||
@@ -33,6 +42,74 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
stopDrag(event){
|
||||
event.preventDefault();
|
||||
},
|
||||
clearInputs(){
|
||||
this.title_input = "";
|
||||
this.url_input = "";
|
||||
},
|
||||
openCreateModal() {
|
||||
this.$refs.createModal.show()
|
||||
},
|
||||
openEditModal(bookmark) {
|
||||
this.title_input = bookmark.title;
|
||||
this.url_input = bookmark.url;
|
||||
this.bookmark_id = bookmark.bookmark_id;
|
||||
this.$refs.editModal.show()
|
||||
},
|
||||
editBookmark(event){
|
||||
event.preventDefault();
|
||||
if(!this.bookmark_id || !this.url_input || !this.title_input) return;
|
||||
|
||||
this.$fhcApi.factory.bookmark
|
||||
.update({
|
||||
bookmark_id: this.bookmark_id,
|
||||
title: this.title_input,
|
||||
url: this.url_input,
|
||||
})
|
||||
.then((res) => res.data)
|
||||
.then((result) => {
|
||||
this.$fhcAlert.alertInfo(this.$p.t("bookmark", "bookmarkUpdated"));
|
||||
// refetch the bookmarks to see the updates
|
||||
this.fetchBookmarks();
|
||||
// reset the values for the title and url inputs
|
||||
this.clearInputs();
|
||||
this.$refs.editModal.hide();
|
||||
this.bookmark_id = null;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
|
||||
insertBookmark(event) {
|
||||
event.preventDefault();
|
||||
|
||||
// reset is-invalid css on url input field
|
||||
for (let key of Object.keys(this.validation)) {
|
||||
this.validation[key] = false;
|
||||
}
|
||||
|
||||
// early return if validation failed
|
||||
if (!this.isValidationSuccessfull()) return;
|
||||
|
||||
this.$fhcApi.factory.bookmark
|
||||
.insert({
|
||||
tag: this.config.tag,
|
||||
title: this.title_input,
|
||||
url: this.url_input,
|
||||
})
|
||||
.then((res) => res.data)
|
||||
.then((result) => {
|
||||
this.$fhcAlert.alertInfo(this.$p.t("bookmark", "bookmarkAdded"));
|
||||
// refetch the bookmarks to see the updates
|
||||
this.fetchBookmarks();
|
||||
this.$refs.createModal.hide();
|
||||
// reset the values for the title and url inputs
|
||||
this.clearInputs();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
|
||||
isValidationSuccessfull() {
|
||||
// validate the input fields
|
||||
if (this.title_input.length === 0) {
|
||||
@@ -55,33 +132,6 @@ export default {
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
addLink() {
|
||||
// reset is-invalid css on url input field
|
||||
for (let key of Object.keys(this.validation)) {
|
||||
this.validation[key] = false;
|
||||
}
|
||||
|
||||
// early return if validation failed
|
||||
if (!this.isValidationSuccessfull()) return;
|
||||
|
||||
this.$fhcApi.factory.bookmark
|
||||
.insert({
|
||||
tag: this.config.tag,
|
||||
title: this.title_input,
|
||||
url: this.url_input,
|
||||
})
|
||||
.then((res) => res.data)
|
||||
.then((result) => {
|
||||
this.$fhcAlert.alertInfo(this.$p.t("bookmark", "bookmarkAdded"));
|
||||
// refetch the bookmarks to see the updates
|
||||
this.fetchBookmarks();
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
|
||||
// reset the values for the title and url inputs
|
||||
this.title_input = "";
|
||||
this.url_input = "";
|
||||
},
|
||||
async removeLink(bookmark_id) {
|
||||
let isConfirmed = await this.$fhcAlert.confirmDelete();
|
||||
|
||||
@@ -107,50 +157,79 @@ export default {
|
||||
// this.$emit('setConfig', true); -> use this to enable widget config mode if needed
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div class="widgets-url w-100 h-100" style="padding: 1rem 1rem;">
|
||||
<div class="widgets-url w-100 h-100 overflow-scroll" style="padding: 1rem 1rem;">
|
||||
<div class="d-flex flex-column justify-content-between">
|
||||
<!-- todo: widgetTag ?? -->
|
||||
<template v-if="shared">
|
||||
|
||||
<template v-if="!emptyBookmarks">
|
||||
<button v-if="editModeIsActive" class="btn btn-outline-secondary btn-sm w-100 mt-2" @click="openCreateModal" type="button">{{$p.t('bookmark','newLink')}}</button>
|
||||
<div v-for="link in shared" :key="link.id" class="d-flex mt-2">
|
||||
<a target="_blank" :href="link.url">
|
||||
<i class="fa fa-solid fa-arrow-up-right-from-square"></i>{{ link.title }}
|
||||
</a>
|
||||
<a class="ms-auto" href="#" @click.prevent="removeLink(link.bookmark_id)" v-show="configMode || editModeIsActive">
|
||||
<i class="fa fa-regular fa-trash-can" style="color: #e01b24;"></i>
|
||||
<i class="fa fa-solid fa-arrow-up-right-from-square me-1"></i>{{ link.title }}
|
||||
</a>
|
||||
|
||||
<div class="ms-auto">
|
||||
<!--EDIT BOOKMARK-->
|
||||
<a href="#" @click.prevent="openEditModal(link)" v-show="configMode || editModeIsActive">
|
||||
<i class="fa fa-edit me-1"></i>
|
||||
</a>
|
||||
<!--DELETE BOOKMARK-->
|
||||
<a href="#" @click.prevent="removeLink(link.bookmark_id)" v-show="configMode || editModeIsActive">
|
||||
<i class="fa fa-regular fa-trash-can" style="color: #e01b24;"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-else class="d-flex mt-2">
|
||||
<span>{{$p.t('bookmark','emptyBookmarks')}}</span>
|
||||
</div>
|
||||
<div v-if="editModeIsActive " class="mt-2">
|
||||
<div class="form-group">
|
||||
<input maxlength="255" required class="form-control form-control-sm" :class="{'is-invalid':validation.invalidTitel}" placeholder="Titel" type="text" v-model="title_input" name="title" >
|
||||
<!-- validation html for titel -->
|
||||
<div class="invalid-feedback">
|
||||
{{$p.t("bookmark", "invalidTitel")}}.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<input required id="bookmark_link" class="form-control form-control-sm mt-2" :class="{'is-invalid':validation.invalidURL}" type="url" placeholder="URL" v-model="url_input" name="url">
|
||||
<!-- validation html for url -->
|
||||
<div class="invalid-feedback">
|
||||
{{$p.t("bookmark", "invalidUrl")}}.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline-secondary btn-sm w-100 mt-2" @click="addLink" type="button">{{$p.t('bookmark','saveLink')}}</button>
|
||||
</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>`,
|
||||
</div>
|
||||
<!--EDIT MODAL-->
|
||||
<core-form draggable="true" @dragstart="stopDrag" @drag="stopDrag" @dragend="stopDrag" v-if="editModeIsActive " ref="editForm">
|
||||
<bs-modal @[\`hide.bs.modal\`]="bookmark_id=null; clearInputs();" ref="editModal">
|
||||
<template #title>
|
||||
<h2>{{$p.t('bookmark','editLink')}}</h2>
|
||||
</template>
|
||||
<template #default>
|
||||
<label class="form-label" for="editTitle">Title</label>
|
||||
<form-input id="editTitle" v-model="title_input" name="title" class="mb-2"></form-input>
|
||||
<label class="form-label" for="editUrl">Url</label>
|
||||
<form-input id="editUrl" v-model="url_input" name="url"></form-input>
|
||||
</template>
|
||||
<template #footer>
|
||||
<button @click="editBookmark" class="btn btn-primary">{{$p.t('bookmark','editLink')}}</button>
|
||||
</template>
|
||||
</bs-modal>
|
||||
</core-form>
|
||||
|
||||
<!--CREATE MODAL-->
|
||||
<core-form draggable="true" @dragstart="stopDrag" @drag="stopDrag" @dragend="stopDrag" v-if="editModeIsActive " ref="createForm">
|
||||
<bs-modal @[\`hide.bs.modal\`]="clearInputs();" ref="createModal">
|
||||
<template #title>
|
||||
<h2>{{$p.t('bookmark','newLink')}}</h2>
|
||||
</template>
|
||||
<template #default>
|
||||
<label class="form-label" for="insertTitle">Title</label>
|
||||
<form-input id="insertTitle" v-model="title_input" name="title" class="mb-2"></form-input>
|
||||
<label class="form-label" for="insertUrl">Url</label>
|
||||
<form-input id="insertUrl" v-model="url_input" name="url"></form-input>
|
||||
</template>
|
||||
<template #footer>
|
||||
<button @click="insertBookmark" class="btn btn-primary">{{$p.t('bookmark','saveLink')}}</button>
|
||||
</template>
|
||||
</bs-modal>
|
||||
</core-form>`,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -12,8 +12,8 @@ export default {
|
||||
searchtimer: null,
|
||||
hidetimer: null,
|
||||
searchsettings: {
|
||||
searchstr: '',
|
||||
types: [],
|
||||
searchstr: this.getSearchStr(),
|
||||
types: this.getSearchTypes(),
|
||||
},
|
||||
searchresult: [],
|
||||
showresult: false,
|
||||
@@ -32,12 +32,14 @@ export default {
|
||||
},
|
||||
template: /*html*/`
|
||||
<form ref="searchform" class="d-flex me-3" :class="searchoptions.cssclass" action="javascript:void(0);"
|
||||
@focusin="this.searchfocusin" @focusout="this.searchfocusout">
|
||||
|
||||
<div ref="searchbox" class="h-100 input-group me-2 bg-white">
|
||||
@focusin="this.searchfocusin" @focusout="this.searchfocusout">
|
||||
<div ref="searchbox" class="h-100 input-group me-2 bg-white">
|
||||
<span style="background-color:inherit" class="input-group-text">
|
||||
<i class="fa-solid fa-magnifying-glass"></i>
|
||||
</span>
|
||||
<input @keyup="this.search" @focus="this.showsearchresult"
|
||||
v-model="this.searchsettings.searchstr" class="form-control"
|
||||
type="search" :placeholder="'Search: '+ searchsettings.types.join(' / ')" aria-label="Search">
|
||||
type="search" :placeholder="'Search: '+ search_types_string" aria-label="Search">
|
||||
<button data-bs-toggle="collapse" data-bs-target="#searchSettings" aria-expanded="false" aria-controls="searchSettings" ref="settingsbutton" class="btn btn-outline-secondary" type="button" id="search-filter"><i class="fas fa-cog"></i></button>
|
||||
</div>
|
||||
|
||||
@@ -81,12 +83,32 @@ export default {
|
||||
</form>
|
||||
`,
|
||||
watch:{
|
||||
'searchsettings.types': function (newValue){
|
||||
this.search();
|
||||
},
|
||||
'searchsettings.searchstr': function (newSearchValue, oldSearchValue) {
|
||||
sessionStorage.setItem('searchstr',newSearchValue);
|
||||
},
|
||||
},
|
||||
computed:{
|
||||
|
||||
search_types_string(){
|
||||
if (Array.isArray(this.searchsettings.types) && this.searchsettings.types.length > 0){
|
||||
return this.searchsettings.types.join(' / ');
|
||||
}else{
|
||||
return JSON.stringify(this.searchsettings.types);
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
beforeMount: function() {
|
||||
this.updateSearchOptions();
|
||||
this.$watch('searchsettings.types', (newValue, oldValue) => {
|
||||
if (Array.isArray(newValue) && newValue.length === 0){
|
||||
this.searchsettings.types = this.allSearchTypes();
|
||||
}
|
||||
// stores the search types in the localstorage, only if the newValue is also an array
|
||||
if(Array.isArray(newValue)){
|
||||
localStorage.setItem('searchtypes', JSON.stringify(newValue));
|
||||
}
|
||||
this.search();
|
||||
});
|
||||
},
|
||||
mounted(){
|
||||
this.settingsDropdown = new bootstrap.Collapse(this.$refs.settings, {
|
||||
@@ -101,6 +123,23 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getSearchTypes: function () {
|
||||
let result = this.allSearchTypes();
|
||||
if (localStorage.getItem('searchtypes')) {
|
||||
result = JSON.parse(localStorage.getItem('searchtypes'));
|
||||
}
|
||||
return result;
|
||||
},
|
||||
allSearchTypes() {
|
||||
let allTypes = [];
|
||||
for (const idx in this.searchoptions.types) {
|
||||
allTypes.push(this.searchoptions.types[idx]);
|
||||
};
|
||||
return allTypes;
|
||||
},
|
||||
getSearchStr: function(){
|
||||
return sessionStorage.getItem('searchstr') ?? '';
|
||||
},
|
||||
checkSettingsVisibility: function(event) {
|
||||
// hides the settings collapsible if the user clicks somewhere else
|
||||
if (!this.$refs.settings.contains(event.target))
|
||||
@@ -116,7 +155,7 @@ export default {
|
||||
// removes the event listener checkSettingsVisibility when the collapsible is hidden
|
||||
document.removeEventListener("click", this.checkSettingsVisibility);
|
||||
},
|
||||
updateSearchOptions: function() {
|
||||
allSearchOptions: function() {
|
||||
this.searchsettings.types = [];
|
||||
for( const idx in this.searchoptions.types ) {
|
||||
this.searchsettings.types.push(this.searchoptions.types[idx]);
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
import {user_locale} from "../plugin/Phrasen.js";
|
||||
import CalendarDates from "./CalendarDates.js";
|
||||
|
||||
class CalendarDate {
|
||||
constructor(y, m, d) {
|
||||
this.weekStart = CalendarDate.getWeekStart();
|
||||
this.watchLocale = Vue.watch(
|
||||
user_locale,
|
||||
(newLocale, oldLocale, onCleanup) =>{
|
||||
this.weekStart = CalendarDate.getWeekStart();
|
||||
this._clean();
|
||||
onCleanup((cleanup)=>{
|
||||
console.log(cleanup,"HERE IA M")
|
||||
});
|
||||
},
|
||||
|
||||
);
|
||||
this.set(y, m, d);
|
||||
this._clean();
|
||||
CalendarDates.subscribe(this);
|
||||
}
|
||||
get y() { return this._y }
|
||||
set y(v) { this._y = v; this._clean() }
|
||||
@@ -49,22 +64,33 @@ class CalendarDate {
|
||||
}
|
||||
}
|
||||
}
|
||||
get wYear() {
|
||||
if( this.w === 1 ) {
|
||||
return this.cdLastDayOfWeek.format({ year: 'numeric' });
|
||||
}
|
||||
return this.cdFirstDayOfWeek.format({ year: 'numeric' });
|
||||
}
|
||||
get wd() {
|
||||
if (this._wd === null) {
|
||||
// the .getDay() method from js Date object ALWAYS returns values from 0 to 6, where 0 is Sunday and 6 is Saturday
|
||||
// aligns the getDay() result of the Date to the weekStart of the CalendarDate
|
||||
this._wd = ((new Date(this.y, this.m, this.d)).getDay()+7-this.weekStart)%7;
|
||||
}
|
||||
return this._wd;
|
||||
}
|
||||
get firstDayOfWeek() {
|
||||
let firstDayOfWeek = new Date(this.y, this.m, this.d);
|
||||
// to ensure that firstDayOfWeek.getDay() is always greater than this.weekStart we add 7 and wrap the result around with %7 to avoid negative numbers
|
||||
firstDayOfWeek.setDate(this.d -(firstDayOfWeek.getDay()+7-this.weekStart)%7);
|
||||
return firstDayOfWeek;
|
||||
}
|
||||
get cdFirstDayOfWeek() {
|
||||
return new CalendarDate(this.firstDayOfWeek);
|
||||
let FirstDayOfWeek = new CalendarDate(this.firstDayOfWeek);
|
||||
return FirstDayOfWeek;
|
||||
}
|
||||
get lastDayOfWeek() {
|
||||
let lastDayOfWeek = new Date(this.y, this.m, this.d);
|
||||
// uses the calculation from firstDayOfWeek and adds 6 days to the result to get the last day of the week
|
||||
lastDayOfWeek.setDate(this.d -(lastDayOfWeek.getDay()+7-this.weekStart)%7 +6);
|
||||
return lastDayOfWeek;
|
||||
}
|
||||
@@ -78,27 +104,38 @@ class CalendarDate {
|
||||
return days
|
||||
}
|
||||
get cdLastDayOfWeek() {
|
||||
return new CalendarDate(this.lastDayOfWeek);
|
||||
let LastDayOfWeek = new CalendarDate(this.lastDayOfWeek);
|
||||
return LastDayOfWeek;
|
||||
}
|
||||
get firstDayOfCalendarMonth() {
|
||||
let firstDayOfMonth = new Date(this.y, this.m, 1);
|
||||
return new Date(this.y, this.m, 1-(firstDayOfMonth.getDay() + 7 - this.weekStart)%7);
|
||||
let offset = (firstDayOfMonth.getDay() + 7 - this.weekStart) % 7;
|
||||
// offset will be greater than 1 most of the time, using a negative number for a date returns a date in the past
|
||||
return new Date(this.y, this.m, 1-offset);
|
||||
}
|
||||
get cdFirstDayOfCalendarMonth() {
|
||||
let firstDayOfMonth = new Date(this.y, this.m, 1);
|
||||
return new CalendarDate(this.y, this.m, 1-(firstDayOfMonth.getDay() + 7 - this.weekStart)%7);
|
||||
let offset = (firstDayOfMonth.getDay() + 7 - this.weekStart) % 7;
|
||||
let FirstDayOfCalendarMonth = new CalendarDate(this.y, this.m, 1 - offset);
|
||||
return FirstDayOfCalendarMonth;
|
||||
}
|
||||
get lastDayOfCalendarMonth() {
|
||||
// In JavaScript, the Date constructor interprets: A day of 0 as the last day of the previous month
|
||||
let lastDayOfMonth = new Date(this.y, this.m+1, 0);
|
||||
return new Date(lastDayOfMonth.getFullYear(), lastDayOfMonth.getMonth(), lastDayOfMonth.getDate()+6-(lastDayOfMonth.getDay() + 7 - this.weekStart)%7);
|
||||
let offset = (lastDayOfMonth.getDay() + 7 - this.weekStart) % 7;
|
||||
return new Date(lastDayOfMonth.getFullYear(), lastDayOfMonth.getMonth(), lastDayOfMonth.getDate()+6-offset);
|
||||
}
|
||||
get cdLastDayOfCalendarMonth() {
|
||||
let lastDayOfMonth = new Date(this.y, this.m+1, 0);
|
||||
return new CalendarDate(lastDayOfMonth.getFullYear(), lastDayOfMonth.getMonth(), lastDayOfMonth.getDate()+6-(lastDayOfMonth.getDay() + 7 - this.weekStart)%7);
|
||||
let offset = (lastDayOfMonth.getDay() + 7 - this.weekStart) % 7;
|
||||
let LasyDayOfCalendarMonth = new CalendarDate(lastDayOfMonth.getFullYear(), lastDayOfMonth.getMonth(), lastDayOfMonth.getDate() + 6 - offset);
|
||||
return LasyDayOfCalendarMonth;
|
||||
}
|
||||
get cdLastDayOfNextCalendarMonth() {
|
||||
let lastDayOfMonth = new Date(this.y, this.m+1, 0);
|
||||
return new CalendarDate(lastDayOfMonth.getFullYear(), lastDayOfMonth.getMonth() + 1, lastDayOfMonth.getDate()+6-(lastDayOfMonth.getDay() + 7 - this.weekStart)%7);
|
||||
let offset = (lastDayOfMonth.getDay() + 7 - this.weekStart) % 7;
|
||||
let LastDayOfNextCalendarMonth = new CalendarDate(lastDayOfMonth.getFullYear(), lastDayOfMonth.getMonth() + 1, lastDayOfMonth.getDate() + 6 - offset);
|
||||
return LastDayOfNextCalendarMonth;
|
||||
}
|
||||
get nextSevenDays() {
|
||||
const days = []
|
||||
@@ -109,16 +146,30 @@ class CalendarDate {
|
||||
return days
|
||||
}
|
||||
get numWeeks() {
|
||||
return (new CalendarDate(this.y+1,0,this.weekStart == 1 ? -3 : 0)).w;
|
||||
// if the week starts with Monday we have to go 3 days in the past from the start of the next year to get the correct numWeek of the current year
|
||||
// this is because for example 30.12.2024 - 05.01.2025 is the first calendarWeek of 2025
|
||||
let lastCalendarWeek = new CalendarDate(this.y + 1, 0, this.weekStart == 1 ? -3 : 0);
|
||||
return lastCalendarWeek.w;
|
||||
}
|
||||
set(y,m,d,noClean) {
|
||||
|
||||
if (y !== undefined && (m === undefined || m === true) && d === undefined) {
|
||||
if (Object.prototype.toString.call(y) === '[object Date]')
|
||||
if (this.isDate(y))
|
||||
{
|
||||
// set year/month/day from date object
|
||||
return this.set(y.getFullYear(), y.getMonth(), y.getDate(), m);
|
||||
}
|
||||
if (y.y !== undefined && y.m !== undefined && y.d !== undefined)
|
||||
{
|
||||
// set year/month/day from CalendarDate object
|
||||
return this.set(y.y, y.m, y.d, m);
|
||||
}
|
||||
}
|
||||
[this._y,this._m,this._d] = [y || 0, m || 0, d || 0];
|
||||
// initialize year/month/day
|
||||
this._y = y ?? 0;
|
||||
this._m = m ?? 0;
|
||||
this._d = d ?? 0;
|
||||
|
||||
if (!noClean)
|
||||
this._clean();
|
||||
}
|
||||
@@ -127,11 +178,11 @@ class CalendarDate {
|
||||
this._w = null;
|
||||
this._wd = null;
|
||||
}
|
||||
format(options) {
|
||||
return (new Date(this._y, this._m, this._d)).toLocaleString(undefined, options);
|
||||
format(options, lang=undefined) {
|
||||
return (new Date(this._y, this._m, this._d)).toLocaleString(lang, options);
|
||||
}
|
||||
compare(d) {
|
||||
if (Object.prototype.toString.call(d) === '[object Date]')
|
||||
if (this.isDate(d))
|
||||
return (this.y === d.getFullYear() && this.m === d.getMonth() && this.d === d.getDate());
|
||||
return (this.y === d.y && this.m === d.m && this.d === d.d);
|
||||
}
|
||||
@@ -151,6 +202,13 @@ class CalendarDate {
|
||||
setLocale(locale) {
|
||||
this.weekStart = CalendarDate.getWeekStart(locale);
|
||||
}
|
||||
// method that checks if the parameter is of type Date
|
||||
isDate(obj){
|
||||
return Object.prototype.toString.call(obj) === '[object Date]';
|
||||
}
|
||||
cleanup(){
|
||||
this.watchLocale.stop();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the weekday number (Date.getDay()) on which the week starts depending on the locale.
|
||||
@@ -163,19 +221,39 @@ class CalendarDate {
|
||||
* @return integer
|
||||
*/
|
||||
CalendarDate.getWeekStart = function(locale) {
|
||||
locale = locale || navigator.language;
|
||||
const parts = locale.match(/^([a-z]{2,3})(?:-([a-z]{3})(?=$|-))?(?:-([a-z]{4})(?=$|-))?(?:-([a-z]{2}|\d{3})(?=$|-))?/i);
|
||||
const regionSat = 'AEAFBHDJDZEGIQIRJOKWLYOMQASDSY'.match(/../g);
|
||||
const regionSun = 'AGARASAUBDBRBSBTBWBZCACNCODMDOETGTGUHKHNIDILINJMJPKEKHKRLAMHMMMOMTMXMZNINPPAPEPHPKPRPTPYSASGSVTHTTTWUMUSVEVIWSYEZAZW'.match(/../g);
|
||||
const languageSat = ['ar','arq','arz','fa'];
|
||||
const languageSun = 'amasbndzengnguhehiidjajvkmknkolomhmlmrmtmyneomorpapssdsmsnsutatethtnurzhzu'.match(/../g);
|
||||
|
||||
return (
|
||||
parts[4] ? (
|
||||
regionSun.includes(parts[4]) ? 0 :
|
||||
regionSat.includes(parts[4]) ? 6 : 1) : (
|
||||
languageSun.includes(parts[1]) ? 0 :
|
||||
languageSat.includes(parts[1]) ? 6 : 1));
|
||||
locale = user_locale.value || locale || navigator.language;
|
||||
const parts = locale.match(/^([a-z]{2,3})(?:-([a-z]{3})(?=$|-))?(?:-([a-z]{4})(?=$|-))?(?:-([a-z]{2}|\d{3})(?=$|-))?/i);
|
||||
|
||||
const language_code = parts[1];
|
||||
const language_starting_Sat = ['ar','arq','arz','fa'];
|
||||
const language_starting_Sun = 'amasbndzengnguhehiidjajvkmknkolomhmlmrmtmyneomorpapssdsmsnsutatethtnurzhzu'.match(/../g);
|
||||
|
||||
const region_code = parts[4];
|
||||
const region_starting_Sat = 'AEAFBHDJDZEGIQIRJOKWLYOMQASDSY'.match(/../g);
|
||||
const region_starting_Sun = 'AGARASAUBDBRBSBTBWBZCACNCODMDOETGTGUHKHNIDILINJMJPKEKHKRLAMHMMMOMTMXMZNINPPAPEPHPKPRPTPYSASGSVTHTTTWUMUSVEVIWSYEZAZW'.match(/../g);
|
||||
|
||||
if (region_code){
|
||||
if (region_starting_Sun.includes(region_code))
|
||||
return 0;
|
||||
else if (region_starting_Sat.includes(region_code))
|
||||
return 6;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
else if(language_code)
|
||||
{
|
||||
if (language_starting_Sun.includes(language_code))
|
||||
return 0;
|
||||
else if (language_starting_Sat.includes(language_code))
|
||||
return 6;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
class CalendarDates{
|
||||
|
||||
static subscribers_array = [];
|
||||
|
||||
static subscribe(subscriber){
|
||||
this.subscribers_array.push(subscriber);
|
||||
}
|
||||
static unsubscribe(subscriber){
|
||||
this.subscribers_array = this.subscribers_array.filter(sub => !sub.compare(subscriber));
|
||||
}
|
||||
static cleanup(){
|
||||
this.subscribers_array.forEach(sub => { sub.cleanup() });
|
||||
}
|
||||
}
|
||||
|
||||
export default CalendarDates;
|
||||
@@ -0,0 +1,21 @@
|
||||
export default {
|
||||
data(){
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
props:{
|
||||
data:{
|
||||
type:Object|String,
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
componentData: function(){
|
||||
if(!this.data){
|
||||
return "Pass data to be printed by adding the :data prop on the component";
|
||||
}
|
||||
return JSON.stringify(this.data, null, 2);
|
||||
},
|
||||
},
|
||||
template:`<pre class="p-2 bg-secondary text-white" >{{componentData}}</pre>`
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// collection of relative and absolute regex to replace legacy links
|
||||
const GROUP_REPLACEMENT_STRATEGIES = {
|
||||
QUERY_PARAMETERS:'QUERY_PARAMETERS',
|
||||
PATH_SEGMENTS:'PATH_SEGMENTS',
|
||||
}
|
||||
|
||||
const regexList = {
|
||||
relative:[
|
||||
{
|
||||
priority: 1,
|
||||
regex: new RegExp(/^\.\.\/cms\/content\.php\?content_id=([0-9]+)/),
|
||||
replacement: FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/CisVue/Cms/content',
|
||||
group_replacement_strategy:GROUP_REPLACEMENT_STRATEGIES.PATH_SEGMENTS,
|
||||
},
|
||||
{
|
||||
priority: 2,
|
||||
regex: new RegExp(/^\.\.\/cms\/news\.php/),
|
||||
replacement: FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/CisVue/Cms/news',
|
||||
},
|
||||
{
|
||||
priority: 3,
|
||||
regex: new RegExp(/^\.\.\/index\.ci\.php\//),
|
||||
replacement: FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router,
|
||||
},
|
||||
{
|
||||
priority: 10,
|
||||
regex: new RegExp("/^\.\.\//"),
|
||||
replacement: FHC_JS_DATA_STORAGE_OBJECT.app_root,
|
||||
},
|
||||
],
|
||||
absolute:[
|
||||
{}
|
||||
]
|
||||
};
|
||||
|
||||
// sorts the relative regex array by priority ascending
|
||||
const relative_regex = regexList.relative
|
||||
.sort((a, b) => {
|
||||
return a.priority - b.priority;
|
||||
})
|
||||
.map(regex => {
|
||||
return {
|
||||
regex: regex.regex,
|
||||
replacement: regex.replacement,
|
||||
group_replacement_strategy: regex.group_replacement_strategy,
|
||||
}
|
||||
});
|
||||
|
||||
// sorts the absolute regex array by priority ascending
|
||||
const absolute_regex = regexList.absolute
|
||||
.sort((a, b) => {
|
||||
return a.priority - b.priority;
|
||||
})
|
||||
.map(regex => {
|
||||
return {
|
||||
regex: regex.regex,
|
||||
replacement: regex.replacement,
|
||||
group_replacement_strategy: regex.group_replacement_strategy,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
export function replaceRelativeLegacyLink(relativeLegacyLink){
|
||||
for (let {regex,replacement,group_replacement_strategy} of relative_regex){
|
||||
// if any of the regex matches the relativeLegacyLink, replace the matched part with the new app_root path
|
||||
let match = relativeLegacyLink.match(regex);
|
||||
if (match) {
|
||||
let new_link = relativeLegacyLink.replace(regex, replacement);
|
||||
|
||||
switch (group_replacement_strategy){
|
||||
case 'QUERY_PARAMETERS':
|
||||
//TODO: this doesn't really work yet because the query parameter are key/value pairs
|
||||
new_link = new_link.concat(`?${match[1]}`);
|
||||
for (let query_parameter of match.slice(2)) {
|
||||
new_link = new_link.concat(`&${query_parameter}`);
|
||||
}
|
||||
break;
|
||||
case 'PATH_SEGMENTS':
|
||||
for (let query_parameter of match.slice(1)) {
|
||||
new_link = new_link.concat(`/${query_parameter}`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return new_link;
|
||||
}
|
||||
}
|
||||
// if none of the regex matched with the string return the original path
|
||||
return relativeLegacyLink;
|
||||
}
|
||||
|
||||
@@ -263,6 +263,10 @@ export default {
|
||||
return false;
|
||||
},
|
||||
handleSystemError(error) {
|
||||
// don't show an error message to the user if the error was an aborted request
|
||||
if(error.hasOwnProperty('name') && error.name.toLowerCase() === "AbortError".toLowerCase())
|
||||
return;
|
||||
|
||||
// Error is string
|
||||
if (typeof error === 'string')
|
||||
return $fhcAlert.alertSystemError(error);
|
||||
@@ -279,6 +283,7 @@ export default {
|
||||
if (typeof error === 'object' && error !== null) {
|
||||
let errMsg = '';
|
||||
|
||||
|
||||
if (error.hasOwnProperty('response') && error.response?.data?.retval)
|
||||
errMsg += 'Error Message: ' + (error.response.data.retval.message || error.response.data.retval) + '\r\n';
|
||||
else if (error.hasOwnProperty('message'))
|
||||
|
||||
@@ -4,6 +4,12 @@ import FhcApiFactory from '../api/fhcapifactory.js';
|
||||
|
||||
export default {
|
||||
install: (app, options) => {
|
||||
if (app.config.globalProperties.$fhcApi) {
|
||||
if (options?.factory) {
|
||||
app.config.globalProperties.$fhcApi.factory.addEndpoints(options.factory);
|
||||
}
|
||||
return;
|
||||
}
|
||||
app.use(FhcAlert);
|
||||
|
||||
function _get_config(form, uri, data, config) {
|
||||
@@ -293,29 +299,39 @@ export default {
|
||||
|
||||
class FhcApiFactoryWrapper {
|
||||
constructor(factorypart, root) {
|
||||
if (root === undefined)
|
||||
if (root === undefined) {
|
||||
this.$fhcApi = app.config.globalProperties.$fhcApi;
|
||||
else
|
||||
this.$fhcApi.factory = this;
|
||||
} else {
|
||||
Object.defineProperty(this, '$fhcApi', {
|
||||
get() {
|
||||
return (root || this).$fhcApi;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.addEndpoints(factorypart)
|
||||
}
|
||||
|
||||
addEndpoints(factorypart) {
|
||||
Object.keys(factorypart).forEach(key => {
|
||||
Object.defineProperty(this, key, {
|
||||
get() {
|
||||
if (typeof factorypart[key] == 'function')
|
||||
return factorypart[key].bind(this);
|
||||
return new FhcApiFactoryWrapper(factorypart[key], root || this);
|
||||
return new FhcApiFactoryWrapper(factorypart[key], this.$fhcApi.factory);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const mergedFhcApiFactory = options?.factory ? {...FhcApiFactory, ...options.factory} : FhcApiFactory;
|
||||
const factory = new FhcApiFactoryWrapper(FhcApiFactory);
|
||||
if (options?.factory)
|
||||
factory.addEndpoints(options.factory);
|
||||
|
||||
app.config.globalProperties.$fhcApi.factory = new FhcApiFactoryWrapper(mergedFhcApiFactory);
|
||||
app.provide('$fhcApi', app.config.globalProperties.$fhcApi);
|
||||
app.config.globalProperties.$fhcApi.factory = factory;
|
||||
|
||||
app.provide('$fhcApi', app.config.globalProperties.$fhcApi);
|
||||
}
|
||||
};
|
||||
@@ -1,9 +1,12 @@
|
||||
import FhcApi from './FhcApi.js';
|
||||
|
||||
const categories = Vue.reactive({});
|
||||
const user_language = Vue.ref(FHC_JS_DATA_STORAGE_OBJECT.user_language);
|
||||
const loadingModules = {};
|
||||
let reload = false;
|
||||
let user_language = Vue.ref(FHC_JS_DATA_STORAGE_OBJECT.user_language);
|
||||
export let user_locale = Vue.computed(()=>{
|
||||
if(!user_language.value) return null;
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.server_languages.find(language => language.sprache == user_language.value).locale;
|
||||
});
|
||||
|
||||
function extractCategory(obj, category) {
|
||||
return obj.filter(e => e.category == category).reduce((res, elem) => {
|
||||
@@ -22,10 +25,11 @@ function getValueForLoadedPhrase(category, phrase, params) {
|
||||
}
|
||||
|
||||
const phrasen = {
|
||||
user_language,
|
||||
user_locale,
|
||||
setLanguage(language, api) {
|
||||
const catArray = Object.keys(categories)
|
||||
return api.factory.phrasen.setLanguage(catArray, language).then(res => {
|
||||
if(reload) window.location.reload()
|
||||
|
||||
res.data.forEach(row => {
|
||||
categories[row.category][row.phrase] = row.text
|
||||
@@ -79,13 +83,13 @@ const phrasen = {
|
||||
|
||||
export default {
|
||||
install(app, options) {
|
||||
reload = options?.reload ?? reload
|
||||
app.use(FhcApi, options?.fhcApi || undefined);
|
||||
app.config.globalProperties.$p = {
|
||||
t: phrasen.t,
|
||||
loadCategory: cat => phrasen.loadCategory.call(app, cat),
|
||||
setLanguage: phrasen.setLanguage,
|
||||
user_language: user_language,
|
||||
user_locale,
|
||||
t_ref: phrasen.t_ref
|
||||
};
|
||||
app.provide('$p', app.config.globalProperties.$p);
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
function doCopy (copy, original, copyArray) {
|
||||
// Callback function to iterate on array or object elements
|
||||
function callback (value, key) {
|
||||
// Copy the contents of objects
|
||||
if (Highcharts.isObject(value, !copyArray) &&
|
||||
!Highcharts.isClass(value) &&
|
||||
!Highcharts.isDOMElement(value)
|
||||
) {
|
||||
copy[key] = doCopy(copy[key] || Highcharts.isArray(value) ? [] : {}, value, copyArray)
|
||||
} else {
|
||||
// Primitives are copied over directly
|
||||
copy[key] = original[key]
|
||||
}
|
||||
}
|
||||
|
||||
if (Highcharts.isArray(original)) {
|
||||
original.forEach(callback)
|
||||
} else {
|
||||
Highcharts.objectEach(original, callback)
|
||||
}
|
||||
return copy
|
||||
}
|
||||
|
||||
const copyObject = function (obj, copyArray) {
|
||||
return doCopy({}, obj, copyArray)
|
||||
}
|
||||
|
||||
const highchartsPlugin = {
|
||||
|
||||
install(app, options) {
|
||||
|
||||
function destroyChart() {
|
||||
if (this.chart) {
|
||||
this.chart.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
function generateVueComponent(Highcharts, VueVersion) {
|
||||
const VUE_MAJOR = VueVersion.split('.')[0]
|
||||
const VERSION_DEPENDENT_PROPS = VUE_MAJOR < 3
|
||||
? {
|
||||
// Fallback options for Vue v2 to keep backward compatibility.
|
||||
render: (createElement) => createElement('div', {
|
||||
ref: 'chart'
|
||||
}),
|
||||
beforeDestroy: destroyChart
|
||||
// The new Vue's 3 syntax.
|
||||
} : {
|
||||
render () {
|
||||
return Vue.h('div', { ref: 'chart' })
|
||||
},
|
||||
beforeUnmount: destroyChart
|
||||
}
|
||||
|
||||
return {
|
||||
template: '<div ref="chart"></div>',
|
||||
props: {
|
||||
constructorType: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
callback: Function,
|
||||
updateArgs: {
|
||||
type: Array,
|
||||
default: () => [true, true]
|
||||
},
|
||||
highcharts: {
|
||||
type: Object
|
||||
},
|
||||
deepCopyOnUpdate: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
options: {
|
||||
handler (newValue) {
|
||||
this.chart.update(copyObject(newValue, this.deepCopyOnUpdate), ...this.updateArgs)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
let HC = this.highcharts || Highcharts
|
||||
|
||||
// Check whether the chart configuration object is passed, as well as the constructor is valid.
|
||||
if (this.options && HC[this.constructorType]) {
|
||||
this.chart = HC[this.constructorType](
|
||||
this.$refs.chart,
|
||||
copyObject(this.options, true), // Always pass the deep copy when generating a chart. #80
|
||||
this.callback ? this.callback : null
|
||||
)
|
||||
} else {
|
||||
(!this.options) ? console.warn('The "options" parameter was not passed.') : console.warn(`'${this.constructorType}' constructor-type is incorrect. Sometimes this error is caused by the fact, that the corresponding module wasn't imported.`)
|
||||
}
|
||||
},
|
||||
...VERSION_DEPENDENT_PROPS
|
||||
}
|
||||
}
|
||||
|
||||
app.component(
|
||||
options.tagName || 'highcharts',
|
||||
generateVueComponent(options.highcharts || Highcharts, Vue.version)
|
||||
)
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
export default highchartsPlugin
|
||||
@@ -68,6 +68,7 @@ require_once('dbupdate_3.4/33683_digitale_anwesenheitsliste_und_entschuldigungsm
|
||||
require_once('dbupdate_3.4/40717_lv_faktor.php');
|
||||
require_once('dbupdate_3.4/48526_pep_tagging.php');
|
||||
require_once('dbupdate_3.4/41950_perm_gehaelter.php');
|
||||
require_once('dbupdate_3.4/25999_locale_update.php');
|
||||
|
||||
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
|
||||
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user