From a413ae17b8db38523bab23360b992e8e25c3203a Mon Sep 17 00:00:00 2001 From: ma0048 Date: Thu, 9 Jul 2026 10:58:56 +0200 Subject: [PATCH] suche und filter erweiterungen --- .../api/frontend/v1/lv/StgTree.php | 148 ++++++++++- .../api/frontend/v1/tempus/Coursepicker.php | 40 ++- .../api/frontend/v1/tempus/Favorites.php | 47 ++++ .../api/frontend/v1/tempus/Info.php | 37 +++ .../api/frontend/v1/tempus/Kalender.php | 39 +-- .../api/frontend/v1/tempus/Reservierung.php | 2 +- application/libraries/KalenderLib.php | 55 +++- public/js/api/factory/tempus/coursepicker.js | 6 +- public/js/api/factory/tempus/info.js | 8 + public/js/api/factory/tempus/kalender.js | 4 +- .../js/api/factory/tempus/studiengangtree.js | 43 ++++ public/js/components/Tempus/Coursepicker.js | 36 ++- .../Tempus/Filters/LectureSelection.js | 55 ++++ .../Tempus/Filters/RoomSelection.js | 55 ++++ .../Tempus/Filters/VerbandSelection.js | 68 +++++ .../Tempus/Filters/searchbarOptions.js | 50 ++++ .../js/components/Tempus/LectureSelection.js | 44 ---- public/js/components/Tempus/Reservierung.js | 46 ++-- public/js/components/Tempus/Tempus.js | 236 ++++++++++-------- system/dbupdate_3.4/46975_tempus.php | 14 ++ 20 files changed, 787 insertions(+), 246 deletions(-) create mode 100644 application/controllers/api/frontend/v1/tempus/Favorites.php create mode 100644 application/controllers/api/frontend/v1/tempus/Info.php create mode 100644 public/js/api/factory/tempus/info.js create mode 100644 public/js/api/factory/tempus/studiengangtree.js create mode 100644 public/js/components/Tempus/Filters/LectureSelection.js create mode 100644 public/js/components/Tempus/Filters/RoomSelection.js create mode 100644 public/js/components/Tempus/Filters/VerbandSelection.js create mode 100644 public/js/components/Tempus/Filters/searchbarOptions.js delete mode 100644 public/js/components/Tempus/LectureSelection.js diff --git a/application/controllers/api/frontend/v1/lv/StgTree.php b/application/controllers/api/frontend/v1/lv/StgTree.php index 8272da978..792775833 100644 --- a/application/controllers/api/frontend/v1/lv/StgTree.php +++ b/application/controllers/api/frontend/v1/lv/StgTree.php @@ -25,7 +25,25 @@ class StgTree extends FHCAPI_Controller return $this->_outputAuthError([$method => ['admin:r', 'assistenz:r']]); } - return $this->getStudiengang($method); + $count = count($params); + if (!$count) + return $this->getStudiengang($method); + + if ($count == 1) { + if (is_numeric($params[0])) + return $this->getSemester($method, $params[0]); + else + return $this->getStudiengang($method, $params[0]); + } + if ($count == 2) { + if (is_numeric($params[0])) + return $this->getVerband($method, $params[0], $params[1]); + else + return $this->getSemester($method, $params[1], $params[0]); + } + + + show_404(); } @@ -64,16 +82,27 @@ class StgTree extends FHCAPI_Controller $this->terminateWithSuccess($list); } - protected function getStudiengang($studiengang_kz) + protected function getStudiengang($studiengang_kz, $org_form = null) { $link = $studiengang_kz . '/'; + if ($org_form !== null) + $link .= $org_form . '/'; $this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); $this->StudiengangModel->addDistinct(); $this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", semester) AS link", false); - $this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester ORDER BY verband, gruppe LIMIT 1)) AS name", false); - $this->StudiengangModel->addSelect("TRUE AS leaf", false); + $this->StudiengangModel->addSelect("CONCAT( + UPPER(CONCAT(typ, kurzbz)), + '-', + semester, + ( + SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END + FROM public.tbl_lehrverband + WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester + ORDER BY verband, gruppe LIMIT 1 + ) + ) AS name", false); $this->StudiengangModel->addSelect('semester'); $this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false); @@ -97,6 +126,8 @@ class StgTree extends FHCAPI_Controller $this->StudienordnungModel->addDistinct(); $this->StudienordnungModel->addSelect("CONCAT(studiengang_kz, '/', p.orgform_kurzbz) AS link"); $this->StudienordnungModel->addSelect("p.orgform_kurzbz AS name"); + $this->StudienordnungModel->addSelect("p.orgform_kurzbz AS orgform_kurzbz"); + $this->StudienordnungModel->addSelect("studiengang_kz AS stg_kz"); $this->StudienordnungModel->addSelect("TRUE as leaf", false); $this->StudienordnungModel->addJoin('lehre.tbl_studienplan p', 'studienordnung_id'); @@ -111,6 +142,115 @@ class StgTree extends FHCAPI_Controller $list = array_merge($list, $result); } } + $this->terminateWithSuccess($list); + } + + protected function getSemester($studiengang_kz, $semester, $org_form = null) + { + $link = $studiengang_kz . '/'; + if ($org_form !== null) + $link .= $org_form . '/'; + $link .= $semester . '/'; + + + $this->load->model('organisation/Gruppe_model', 'GruppeModel'); + + $this->GruppeModel->addDistinct(); + $this->GruppeModel->addSelect("CONCAT(" . $this->GruppeModel->escape($link . 'grp/') . ", gruppe_kurzbz) AS link", false); + $this->GruppeModel->addSelect("CONCAT(gruppe_kurzbz, ' (', bezeichnung, ')') AS name", false); + $this->GruppeModel->addSelect("TRUE AS leaf", false); + + $this->GruppeModel->addSelect('sort'); + $this->GruppeModel->addSelect('gruppe_kurzbz'); + $this->GruppeModel->addSelect($this->GruppeModel->escape($studiengang_kz) . '::integer AS stg_kz', false); + + $this->GruppeModel->addOrder('sort'); + $this->GruppeModel->addOrder('gruppe_kurzbz'); + + $where = [ + 'studiengang_kz' => $studiengang_kz, + 'semester' => $semester, + 'lehre' => true, + 'sichtbar' => true, + 'aktiv' => true, + 'direktinskription' => false + ]; + + if ($org_form !== null) + $where['orgform_kurzbz'] = $org_form; + + $result = $this->GruppeModel->loadWhere($where); + + $list = $this->getDataOrTerminateWithError($result); + + $this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); + + $this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", verband) AS link", false); + $this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, verband, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester AND verband=v.verband ORDER BY gruppe LIMIT 1)) AS name", false); + $this->StudiengangModel->addSelect("CASE WHEN MAX(gruppe)='' OR MAX(gruppe)=' ' THEN TRUE ELSE FALSE END AS leaf"); + + $this->StudiengangModel->addSelect($this->StudiengangModel->escape($semester) . ' AS semester'); + $this->StudiengangModel->addSelect('verband'); + $this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false); + + $this->StudiengangModel->addOrder('verband'); + + $this->StudiengangModel->addGroupBy('link, name, verband'); + + $where = [ + 'v.studiengang_kz' => $studiengang_kz, + 'v.semester' => $semester, + 'v.verband !=' => '', + 'v.aktiv' => true + ]; + + if ($org_form !== null && $semester) // NOTE(chris): on semester 0 show all? + $where['v.orgform_kurzbz'] = $org_form; + + $result = $this->StudiengangModel->loadWhere($where); + $result = $this->getDataOrTerminateWithError($result); + + $list = array_merge($list, $result); + + $this->terminateWithSuccess($list); + } + + protected function getVerband($studiengang_kz, $semester, $verband, $org_form = null) + { + $link = $studiengang_kz . '/'; + if ($org_form !== null) + $link .= $org_form . '/'; + $link .= $semester . '/'. $verband . '/'; + + + $this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); + + $this->StudiengangModel->addDistinct(); + $this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", gruppe) AS link", false); + $this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, verband, gruppe, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester AND verband=v.verband AND gruppe=v.gruppe ORDER BY gruppe LIMIT 1)) AS name", false); + $this->StudiengangModel->addSelect("TRUE AS leaf", false); + + $this->StudiengangModel->addSelect('v.semester'); + $this->StudiengangModel->addSelect('v.verband'); + $this->StudiengangModel->addSelect('gruppe'); + $this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false); + + $this->StudiengangModel->addOrder('gruppe'); + + $where = [ + 'v.studiengang_kz' => $studiengang_kz, + 'v.semester' => $semester, + 'v.verband' => $verband, + 'v.gruppe !=' => '', + 'v.aktiv' => true + ]; + + if ($org_form !== null && $semester) + $where['v.orgform_kurzbz'] = $org_form; + + $result = $this->StudiengangModel->loadWhere($where); + + $list = $this->getDataOrTerminateWithError($result); $this->terminateWithSuccess($list); } diff --git a/application/controllers/api/frontend/v1/tempus/Coursepicker.php b/application/controllers/api/frontend/v1/tempus/Coursepicker.php index 18de39a4e..2d0eecd94 100644 --- a/application/controllers/api/frontend/v1/tempus/Coursepicker.php +++ b/application/controllers/api/frontend/v1/tempus/Coursepicker.php @@ -94,11 +94,15 @@ class Coursepicker extends FHCAPI_Controller public function getByStg() { - //TODO check einbauen ob studiensemester und stg vorhanden ist - $stg = $this->input->get('stg'); - $studiensemester_kurzbz = $this->input->get('studiensemester_kurzbz'); - if (is_null($stg) || is_null($studiensemester_kurzbz)) - $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + $this->_ci->form_validation->set_data($_POST); + $this->_ci->form_validation->set_rules('studiengaenge[]',"studiengaenge","required"); + $this->_ci->form_validation->set_rules('studiensemester_kurzbz',"studiensemester_kurzbz","required"); + + if($this->_ci->form_validation->run() === FALSE) + $this->terminateWithValidationErrors($this->_ci->form_validation->error_array()); + + $studiengaenge = $this->input->post('studiengaenge'); + $studiensemester_kurzbz = $this->input->post('studiensemester_kurzbz'); $this->_ci->LehreinheitModel->addSelect(' tbl_lehreinheit.lehreinheit_id, @@ -131,15 +135,39 @@ class Coursepicker extends FHCAPI_Controller $this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehreinheitmitarbeiter lema', 'tbl_lehreinheit.lehreinheit_id = lema.lehreinheit_id'); $this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehreinheitgruppe lvb', 'tbl_lehreinheit.lehreinheit_id = lvb.lehreinheit_id'); $this->_ci->LehreinheitModel->addJoin('public.tbl_studiengang', 'lvb.studiengang_kz = tbl_studiengang.studiengang_kz'); + $this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrveranstaltung', 'tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id'); $this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrveranstaltung lehrfach', 'tbl_lehreinheit.lehrfach_id = lehrfach.lehrveranstaltung_id'); $this->_ci->LehreinheitModel->addJoin('public.tbl_mitarbeiter ma', 'lema.mitarbeiter_uid = ma.mitarbeiter_uid'); $this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrform', 'tbl_lehrform.lehrform_kurzbz = tbl_lehreinheit.lehrform_kurzbz'); $this->_ci->LehreinheitModel->addJoin('public.tbl_benutzer', 'ma.mitarbeiter_uid = tbl_benutzer.uid'); $this->_ci->LehreinheitModel->addJoin('public.tbl_person', 'tbl_benutzer.person_id = tbl_person.person_id'); + $this->_ci->LehreinheitModel->db->group_start(); + $first = true; + foreach ($studiengaenge as $studiengang) + { + if ($first) + $this->_ci->LehreinheitModel->db->group_start(); + else + { + $this->_ci->LehreinheitModel->db->or_group_start(); + } + $first = false; + + $this->_ci->LehreinheitModel->db->where('tbl_lehrveranstaltung.studiengang_kz', $studiengang['studiengang_kz']); + + if (isset($studiengang['semester'])) + $this->_ci->LehreinheitModel->db->where('tbl_lehrveranstaltung.semester', $studiengang['semester']); + + if (isset($studiengang['orgform_kurzbz'])) + $this->_ci->LehreinheitModel->db->where('tbl_lehrveranstaltung.orgform_kurzbz', $studiengang['orgform_kurzbz']); + + $this->_ci->LehreinheitModel->db->group_end(); + } + $this->_ci->LehreinheitModel->db->group_end(); + $result = $this->_ci->LehreinheitModel->loadWhere(array( 'tbl_lehrform.verplanen' => true, - 'tbl_studiengang.studiengang_kz' => $stg, 'tbl_lehreinheit.studiensemester_kurzbz' => $studiensemester_kurzbz )); diff --git a/application/controllers/api/frontend/v1/tempus/Favorites.php b/application/controllers/api/frontend/v1/tempus/Favorites.php new file mode 100644 index 000000000..94ee39707 --- /dev/null +++ b/application/controllers/api/frontend/v1/tempus/Favorites.php @@ -0,0 +1,47 @@ + self::PERM_LOGGED, + 'set' => self::PERM_LOGGED + ]); + + // Load models + $this->load->model('system/Variable_model', 'VariableModel'); + } + + public function index() + { + $result = $this->VariableModel->getVariables(getAuthUID(), ['tempus_favorites']); + + $data = $this->getDataOrTerminateWithError($result); + + if (!$data) + $this->terminateWithSuccess(null); + else + $this->terminateWithSuccess(isset($data['tempus_favorites']) ? $data['tempus_favorites'] : null); + } + + public function set() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('favorites', 'Favorites', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $favorites = $this->input->post('favorites'); + + $result = $this->VariableModel->setVariable(getAuthUID(), 'tempus_favorites', $favorites); + + $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess(true); + } +} diff --git a/application/controllers/api/frontend/v1/tempus/Info.php b/application/controllers/api/frontend/v1/tempus/Info.php new file mode 100644 index 000000000..b8ab2b9b0 --- /dev/null +++ b/application/controllers/api/frontend/v1/tempus/Info.php @@ -0,0 +1,37 @@ + self::PERM_LOGGED, + ]); + + $this->_ci =& get_instance(); + + $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); + + $this->loadPhrases([ + 'ui' + ]); + + } + + public function getStudiengaenge() + { + $this->_ci->StudiengangModel->addDistinct(); + $this->_ci->StudiengangModel->addSelect('studiengang_kz'); + $this->_ci->StudiengangModel->addSelect("CONCAT(kurzbzlang, ' (', UPPER(CONCAT(typ, kurzbz)), ') - ', tbl_studiengang.bezeichnung) AS name"); + + $result = $this->getDataOrTerminateWithError($this->_ci->StudiengangModel->load()); + $this->terminateWithSuccess($result); + } +} diff --git a/application/controllers/api/frontend/v1/tempus/Kalender.php b/application/controllers/api/frontend/v1/tempus/Kalender.php index bdf4ecc70..75b8b02f7 100644 --- a/application/controllers/api/frontend/v1/tempus/Kalender.php +++ b/application/controllers/api/frontend/v1/tempus/Kalender.php @@ -33,7 +33,6 @@ class Kalender extends FHCAPI_Controller 'calculateMultiWeekPlan' => 'lehre/lvplan:rw', 'confirmMultiWeekPlan' => 'lehre/lvplan:rw', 'addToKalenderEvent' => 'lehre/lvplan:rw', - 'addReservierung' => 'lehre/lvplan:rw', 'sync' => 'lehre/lvplan:rw', ]); @@ -90,15 +89,15 @@ class Kalender extends FHCAPI_Controller public function getPlan() { - $this->_ci->form_validation->set_data($_GET); + $this->_ci->form_validation->set_data($_POST); $this->_ci->form_validation->set_rules('start_date',"start_date","required"); $this->_ci->form_validation->set_rules('end_date',"end_date","required"); if($this->_ci->form_validation->run() === FALSE) $this->terminateWithValidationErrors($this->_ci->form_validation->error_array()); - $start_date = $this->_ci->input->get('start_date', TRUE); - $end_date = $this->_ci->input->get('end_date', TRUE); + $start_date = $this->_ci->input->post('start_date', TRUE); + $end_date = $this->_ci->input->post('end_date', TRUE); $filter = $this->_checkFilter(self::ALLOWED_PLAN_FILTER); @@ -230,7 +229,7 @@ class Kalender extends FHCAPI_Controller public function getRaumvorschlag() { - $this->_ci->form_validation->set_data($_GET); + $this->_ci->form_validation->set_data($_POST); $filter = $this->_checkFilter(self::ALLOWED_ROOM_FILTER); $this->terminateWithSuccess($this->_ci->raumvorschlaglib->getVorschlaege($filter->kalender_id)); @@ -396,32 +395,6 @@ class Kalender extends FHCAPI_Controller $this->terminateWithSuccess(getData($result)); } - public function addReservierung() - { - $this->_ci->form_validation->set_data($_POST); - $this->_ci->form_validation->set_rules('titel',"titel","required"); - $this->_ci->form_validation->set_rules('beschreibung',"beschreibung","required"); - $this->_ci->form_validation->set_rules('ort_kurzbz',"ort_kurzbz","required"); - $this->_ci->form_validation->set_rules('start_date',"start_date","required"); - $this->_ci->form_validation->set_rules('end_date',"end_date","required"); - - if($this->_ci->form_validation->run() === FALSE) - $this->terminateWithValidationErrors($this->_ci->form_validation->error_array()); - - $titel = $this->_ci->input->post('titel', TRUE); - $beschreibung = $this->_ci->input->post('beschreibung', TRUE); - $ort_kurzbz = $this->_ci->input->post('ort_kurzbz', TRUE); - $start_date = $this->_ci->input->post('start_date', TRUE); - $end_date = $this->_ci->input->post('end_date', TRUE); - - - $result = $this->_ci->kalenderlib->addReservierung($titel, $beschreibung, $ort_kurzbz, $start_date, $end_date); - - if (isError($result)) - $this->terminateWithError(getError($result)); - - $this->terminateWithSuccess(getData($result)); - } private function _checkFilter($filters) { @@ -429,10 +402,10 @@ class Kalender extends FHCAPI_Controller $filter_object = new stdClass(); foreach ($filters as $filter) { - if ($this->_ci->input->get($filter)) + if ($this->_ci->input->post($filter)) { $filter_valid = true; - $filter_object->$filter = $this->_ci->input->get($filter); + $filter_object->$filter = $this->_ci->input->post($filter); } } diff --git a/application/controllers/api/frontend/v1/tempus/Reservierung.php b/application/controllers/api/frontend/v1/tempus/Reservierung.php index c1030d8ee..be22d96e9 100644 --- a/application/controllers/api/frontend/v1/tempus/Reservierung.php +++ b/application/controllers/api/frontend/v1/tempus/Reservierung.php @@ -158,7 +158,7 @@ class Reservierung extends FHCAPI_Controller $this->_ci->form_validation->set_data($_POST); $this->_ci->form_validation->set_rules('titel',"titel","required"); $this->_ci->form_validation->set_rules('beschreibung',"beschreibung","required"); - $this->_ci->form_validation->set_rules('ort_kurzbz',"ort_kurzbz","required"); + $this->_ci->form_validation->set_rules('ort_kurzbz[]',"ort_kurzbz","required"); $this->_ci->form_validation->set_rules('start_date',"start_date","required"); $this->_ci->form_validation->set_rules('end_date',"end_date","required"); diff --git a/application/libraries/KalenderLib.php b/application/libraries/KalenderLib.php index 37115b4c2..e3ce260ac 100644 --- a/application/libraries/KalenderLib.php +++ b/application/libraries/KalenderLib.php @@ -339,12 +339,12 @@ class KalenderLib $data = $this->_ci->KalenderModel->load(); return $this->_mapEvents($data); } - public function getPlanForPlanner($start_date, $end_date, $ort = null, $uids = null, $stg = null) + public function getPlanForPlanner($start_date, $end_date, $ort = null, $uids = null, $studiengaenge = null) { $this->_getBasePlan($start_date, $end_date); if (!is_null($ort)) - $this->_ci->KalenderModel->db->where('tbl_kalender_ort.ort_kurzbz', $ort); + $this->_ci->KalenderModel->db->where_in('tbl_kalender_ort.ort_kurzbz', $ort); if (!is_null($uids)) { @@ -355,8 +355,33 @@ class KalenderLib $this->_ci->KalenderModel->db->group_end(); } - if (!is_null($stg)) - $this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.studiengang_kz', $stg); + if (!is_null($studiengaenge)) + { + + $this->_ci->KalenderModel->db->group_start(); + $first = true; + foreach ($studiengaenge as $studiengang) + { + if ($first) + $this->_ci->KalenderModel->db->group_start(); + else + { + $this->_ci->KalenderModel->db->or_group_start(); + } + $first = false; + + $this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.studiengang_kz', $studiengang['studiengang_kz']); + + if (isset($studiengang['semester'])) + $this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.semester', $studiengang['semester']); + + if (isset($studiengang['orgform_kurzbz'])) + $this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.orgform_kurzbz', $studiengang['orgform_kurzbz']); + + $this->_ci->KalenderModel->db->group_end(); + } + $this->_ci->KalenderModel->db->group_end(); + } $this->_ci->KalenderModel->db->where('NOT EXISTS ( SELECT 1 FROM lehre.tbl_kalender nachfolger @@ -812,10 +837,10 @@ class KalenderLib { $this->_ci->KalenderModel->addSelect('1'); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'kalender_id'); + $this->_ci->KalenderModel->db->where_in('ort_kurzbz', $ort_kurzbz); $reservierung_vorhanden = $this->_ci->KalenderModel->load(array ( 'von <=' => $end_date, 'bis >=' => $start_date, - 'ort_kurzbz' => $ort_kurzbz, )); if (hasData($reservierung_vorhanden) && !$this->_ci->permissionlib->isBerechtigt('lehre/reservierungAdvanced')) @@ -994,12 +1019,20 @@ class KalenderLib } private function _addKalenderOrt($kalender_id, $ort_kurzbz) { - return $this->_ci->KalenderOrtModel->insert( - array ( - 'kalender_id' => $kalender_id, - 'ort_kurzbz' => $ort_kurzbz - ) - ); + foreach ((array) $ort_kurzbz as $ort) + { + $ortresult = $this->_ci->KalenderOrtModel->insert( + array ( + 'kalender_id' => $kalender_id, + 'ort_kurzbz' => $ort + ) + ); + + if (!isSuccess($ortresult)) + return $ortresult; + } + + return success(); } public function updateKalenderEvent($kalender_id, $ort_kurzbz = null, $start_time = null, $end_time = null) diff --git a/public/js/api/factory/tempus/coursepicker.js b/public/js/api/factory/tempus/coursepicker.js index 82e1a6965..92ff4fa6b 100644 --- a/public/js/api/factory/tempus/coursepicker.js +++ b/public/js/api/factory/tempus/coursepicker.js @@ -6,11 +6,11 @@ export default { params: { query } }; }, - getByStg(stg, studiensemester_kurzbz) { + getByStg(studiengaenge, studiensemester_kurzbz) { return { - method: 'get', + method: 'post', url: 'api/frontend/v1/tempus/coursepicker/getByStg', - params: { stg, studiensemester_kurzbz } + params: { studiengaenge, studiensemester_kurzbz } }; }, diff --git a/public/js/api/factory/tempus/info.js b/public/js/api/factory/tempus/info.js new file mode 100644 index 000000000..849b81545 --- /dev/null +++ b/public/js/api/factory/tempus/info.js @@ -0,0 +1,8 @@ +export default { + getStudiengaenge() { + return { + method: 'get', + url: '/api/frontend/v1/tempus/Info/getStudiengaenge', + }; + }, +}; diff --git a/public/js/api/factory/tempus/kalender.js b/public/js/api/factory/tempus/kalender.js index 5c0cda753..9d2746434 100644 --- a/public/js/api/factory/tempus/kalender.js +++ b/public/js/api/factory/tempus/kalender.js @@ -3,7 +3,7 @@ export default { getPlan(filter, start_date, end_date) { return { - method: 'get', + method: 'post', url: '/api/frontend/v1/tempus/Kalender/getPlan', params: { ...filter, start_date, end_date } }; @@ -112,7 +112,7 @@ export default { }, getRaumvorschlag(kalender_id) { return { - method: 'get', + method: 'post', url: '/api/frontend/v1/tempus/Kalender/getRaumvorschlag', params: { kalender_id } }; diff --git a/public/js/api/factory/tempus/studiengangtree.js b/public/js/api/factory/tempus/studiengangtree.js new file mode 100644 index 000000000..42412785d --- /dev/null +++ b/public/js/api/factory/tempus/studiengangtree.js @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2025 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +export default { + get(path) { + let url = 'api/frontend/v1/lv/StgTree'; + if (path) + url += '/' + path; + return { + method: 'get', + url + }; + }, + favorites: { + get() { + return { + method: 'get', + url: 'api/frontend/v1/tempus/favorites' + }; + }, + set(favorites) { + return { + method: 'post', + url: 'api/frontend/v1/tempus/favorites/set', + params: { favorites } + }; + } + } +}; \ No newline at end of file diff --git a/public/js/components/Tempus/Coursepicker.js b/public/js/components/Tempus/Coursepicker.js index 1bd43b569..a358f3522 100644 --- a/public/js/components/Tempus/Coursepicker.js +++ b/public/js/components/Tempus/Coursepicker.js @@ -10,9 +10,9 @@ export default { draggable }, props: { - stg: { - type: [String, Number], - default: null, + studiengaenge: { + type: Array, + default: [], }, studiensemester: { type: String, @@ -77,23 +77,33 @@ export default { } }, watch: { - stg(val) { - this.searchparam = ''; - this.loadCoursesByStg(val); + studiengaenge: { + deep: true, + handler(val) { + this.searchparam = ''; + this.loadCoursesByStg(val); + } }, studiensemester() { - if (this.stg) - this.loadCoursesByStg(this.stg); + if (this.studiengaenge) + this.loadCoursesByStg(this.studiengaenge); }, }, methods: { async loadCoursesByStg(stg) { - if (!stg) { + if (stg.length <= 0) + { this.allCourses = []; return; } - this.$api.call(ApiCoursePicker.getByStg(this.stg, this.studiensemester)) + let payload = stg.map(({ studiengang_kz, semester, orgform_kurzbz }) => ({ + studiengang_kz, + semester, + orgform_kurzbz + })); + + this.$api.call(ApiCoursePicker.getByStg(payload, this.studiensemester)) .then(result => { this.allCourses = result.data.map(e => ({ lehreinheit_id: e.lehreinheit_id, @@ -161,7 +171,7 @@ export default { }, reload() { - this.loadCoursesByStg(this.stg); + this.loadCoursesByStg(this.studiengaenge); }, toggleMultiWeek(course) { @@ -191,7 +201,7 @@ export default { @@ -218,7 +228,7 @@ export default { -
+
Keine Lehreinheiten Wähle einen Studiengang, um Lehreinheiten anzuzeigen.
diff --git a/public/js/components/Tempus/Filters/LectureSelection.js b/public/js/components/Tempus/Filters/LectureSelection.js new file mode 100644 index 000000000..a25856b86 --- /dev/null +++ b/public/js/components/Tempus/Filters/LectureSelection.js @@ -0,0 +1,55 @@ +export default { + name: "LectureSelection", + props: { + lecturers: { + type: Array, + required: true + } + }, + emits: ['remove'], + template: ` +
+
+
+ Lektoren + +
+
+
+ {{ l.label }} + +
+ +
+
+ + Plan +
+
+ + Zeitsperren +
+
+ + Zeitwünsche +
+
+
+
+
+ ` +} \ No newline at end of file diff --git a/public/js/components/Tempus/Filters/RoomSelection.js b/public/js/components/Tempus/Filters/RoomSelection.js new file mode 100644 index 000000000..e0344b735 --- /dev/null +++ b/public/js/components/Tempus/Filters/RoomSelection.js @@ -0,0 +1,55 @@ +export default { + name: "RoomSelection", + props: { + rooms: { + type: Array, + required: true + } + }, + emits: ['update:rooms'], + methods: { + removeRoom(ort_kurzbz) + { + if (ort_kurzbz == null) + { + this.$emit('update:rooms', []); + } + else + { + this.$emit('update:rooms', this.rooms.filter(room => room.ort_kurzbz !== ort_kurzbz)); + } + } + }, + template: ` +
+
+ Räume + +
+
    +
  • + {{ room.ort_kurzbz }} + +
  • +
+
+ ` +} \ No newline at end of file diff --git a/public/js/components/Tempus/Filters/VerbandSelection.js b/public/js/components/Tempus/Filters/VerbandSelection.js new file mode 100644 index 000000000..ec51d563a --- /dev/null +++ b/public/js/components/Tempus/Filters/VerbandSelection.js @@ -0,0 +1,68 @@ +export default { + name: "VerbandSelection", + props: { + studiengaenge: { + type: Array, + required: true + }, + studiengaengeAll: { + type: Array, + default: () => [] + } + }, + emits: ['update:studiengaenge'], + methods: { + removeStg(stg_kz, semester, orgform_kurzbz) + { + if (stg_kz == null) + { + this.$emit('update:studiengaenge', []); + } + else + { + this.$emit('update:studiengaenge', this.studiengaenge.filter(studiengang => !(studiengang.studiengang_kz === stg_kz && studiengang.semester === semester && studiengang.orgform_kurzbz === orgform_kurzbz))); + } + }, + stgLabel(stg) + { + let found = this.studiengaengeAll.find(s => s.studiengang_kz == stg.studiengang_kz); + let name = found?.name ?? stg.studiengang_kz; + + if (stg.orgform_kurzbz) + return `${name} - ${stg.orgform_kurzbz}`; + + return stg.semester ? `${name} - ${stg.semester}. Semester` : name; + } + }, + template: ` +
+
+ Studiengänge + +
+
    +
  • + {{ stgLabel(studiengang) }} + +
  • +
+
+ ` +} \ No newline at end of file diff --git a/public/js/components/Tempus/Filters/searchbarOptions.js b/public/js/components/Tempus/Filters/searchbarOptions.js new file mode 100644 index 000000000..42150ed41 --- /dev/null +++ b/public/js/components/Tempus/Filters/searchbarOptions.js @@ -0,0 +1,50 @@ +export function getTempusSearchbarOptions(self) +{ + return { + origin: 'tempus', + cssclass: "position-relative", + calcheightonly: true, + types: [ + //"student", + "raum", + "mitarbeiter", + "mitarbeiter_ohne_zuordnung" + ], + actions: { + raum: { + defaultaction: { + type: "function", + action: self.setOrt + }, + childactions: [ + { + label: "zum Filter hinzufügen", + icon: "fas fa-plus", + type: "function", + action: (data) => { + self.addToFilter(data, 'ort'); + } + }, + ] + }, + employee: { + defaultaction: { + type: "function", + action: (data) => { + self.setEmp(data); + } + }, + childactions: [ + { + label: "zum Filter hinzufügen", + icon: "fas fa-plus", + type: "function", + action: (data) => { + self.addToFilter(data, 'mitarbeiter'); + } + }, + ] + }, + } + }; +} \ No newline at end of file diff --git a/public/js/components/Tempus/LectureSelection.js b/public/js/components/Tempus/LectureSelection.js deleted file mode 100644 index 5b223fff4..000000000 --- a/public/js/components/Tempus/LectureSelection.js +++ /dev/null @@ -1,44 +0,0 @@ -import FormInput from '../Form/Input.js'; - -export default { - name: "LectureSelection", - props: { - lecturers: { - type: Array, - required: true - } - }, - emits: ['remove'], - template: ` -
-
-
- {{ l.label }} - -
- -
-
- - Plan -
-
- - Zeitsperren -
-
- - Zeitwünsche -
-
-
-
- ` -} \ No newline at end of file diff --git a/public/js/components/Tempus/Reservierung.js b/public/js/components/Tempus/Reservierung.js index 089f112e9..c01f93d74 100644 --- a/public/js/components/Tempus/Reservierung.js +++ b/public/js/components/Tempus/Reservierung.js @@ -7,11 +7,12 @@ export default { name: 'ReservierungModal', components: { BsModal, - FormInput + FormInput, + Multiselect: primevue.multiselect, }, props: { - ortKurzbz: { - type: String, + rooms: { + type: Array, default: null } }, @@ -22,7 +23,7 @@ export default { beschreibung: '', start: null, end: null, - ort_kurzbz: null, + selected_rooms: [], raeume_array: [], studiensemester_array: [], teilnehmer: [], @@ -126,7 +127,10 @@ export default { this.beschreibung = ''; this.start = start; this.end = end; - this.ort_kurzbz = this.ortKurzbz ?? null; + + let prop_orte = (this.rooms ?? []).map(r => r.ort_kurzbz); + this.selected_rooms = this.raeume_array.filter(raum => prop_orte.includes(raum.ort_kurzbz)); + this.teilnehmer = [{ uid: null, rolle: null }]; this.specialFinalGroups = [{ gid: null, studiensemester_kurzbz: null, lehrverband: null, gruppe_kurzbz: null, rolle: null }]; this.$refs.modal.show(); @@ -141,7 +145,7 @@ export default { ApiReservierung.addReservierung( this.titel, this.beschreibung, - this.ort_kurzbz, + this.selected_rooms.map(r => r.ort_kurzbz), luxon.DateTime.fromFormat(this.start, 'yyyy-MM-dd HH:mm').toISO(), luxon.DateTime.fromFormat(this.end, 'yyyy-MM-dd HH:mm').toISO(), this.teilnehmer.filter(t => t.uid && t.rolle).map(nehmer => ({ uid: nehmer.uid, rolle: nehmer.rolle })), @@ -200,24 +204,22 @@ export default { />
- {{ $p.t('global', 'raum') }} + - - + +
- -
{ - this.setEmp(data); - } - }, - childactions: [ - ] - }, - } - }, + lv_id: null, events: null, minimized: false, @@ -153,11 +129,12 @@ export default { view: 'room', parkedKeys: new Set(), lecturers: [], + studiengaenge: [], + rooms: [], overlayCache: [], extraBackgrounds: [], lastRange: null, stg: null, - show_stg: null, semester: null, studiensemester_kurzbz: null, raumModal: { @@ -179,6 +156,7 @@ export default { startTime: null, endTime: null, }, + studiengaenge_all: [] } }, computed: { @@ -201,6 +179,9 @@ export default { keyboardShortcuts() { return getTempusShortcuts(this); }, + searchbaroptions() { + return getTempusSearchbarOptions(this); + }, }, methods: { async openRaumauswahl(orig) { @@ -279,42 +260,81 @@ export default { setOrt: function(data) { this.ort_kurzbz = data.ort_kurzbz; - this.$refs.calendar.resetEventLoader(); + this.rooms = [{ ort_kurzbz: data.ort_kurzbz }]; }, onSelectVerbandAndClose(payload) { + console.log(payload); this.onSelectVerband(payload); bootstrap.Offcanvas.getOrCreateInstance(this.$refs.verbandMenu).hide(); }, - onSelectVerband({link, name}) + + onSelectVerband({link, studiengang_kz, semester, orgform_kurzbz, name}) { - let stg = null; - let semester = null; - this.show_stg = name - if (typeof link === 'number') - stg = link; + if (orgform_kurzbz) + { + semester = null; + } else if (typeof link === 'string') { - [stg, semester] = link.split('/'); + [studiengang_kz, semester] = link.split('/'); } - this.stg = stg; - if (semester !== null) - this.semester = semester; + let exists = this.studiengaenge.some(stg => + stg.studiengang_kz == studiengang_kz + && stg.semester == semester + && stg.orgform_kurzbz === orgform_kurzbz + ); - this.$refs.calendar.resetEventLoader(); + if (!exists) + { + this.studiengaenge = [ + ...this.studiengaenge, + { studiengang_kz, semester, orgform_kurzbz, name } + ]; + } }, setEmp: function(data) { const uid = data.uid; const label = data.name; - if (!this.lecturers.some(l => l.uid === uid)) + + for (const lect of this.lecturers) + delete this.overlayCache[lect.uid]; + + this.lecturers = [{ + uid, + label, + showEvents: true, + overlays: { blocks: true, wishes: true }, + }]; + + this.$refs.calendar.resetEventLoader(); + if (this.lastRange) + this.handleRange(this.lastRange); + }, + addToFilter: function(filter, type) + { + if (type === 'ort') { - this.lecturers.push({ - uid, - label, - showEvents: true, - overlays: { blocks: true, wishes: true }, - }); + const ort_kurzbz = filter.ort_kurzbz; + if (!this.rooms.some(room => room.ort_kurzbz === ort_kurzbz)) + { + this.rooms.push({ ort_kurzbz }); + } + } + else if (type === 'mitarbeiter') + { + const uid = filter.uid; + const label = filter.name; + if (!this.lecturers.some(l => l.uid === uid)) + { + this.lecturers.push({ + uid, + label, + showEvents: true, + overlays: { blocks: true, wishes: true }, + }); + } } this.$refs.calendar.resetEventLoader(); @@ -365,18 +385,24 @@ export default { return this.$api.call(ApiSearchbar.search(params)); }, getPromiseFunc(start, end) { - const hasRoom = !!this.ort_kurzbz; + const hasRooms = this.rooms.length > 0; const hasLektoren = this.lecturers.length > 0; - const hasStg = !!this.stg; + const hasStg = this.studiengaenge.length > 0; const filter = {}; - if (hasRoom) - filter.ort = this.ort_kurzbz; + if (hasRooms) + filter.ort = this.rooms.map(room => room.ort_kurzbz); if (hasStg) - filter.stg = this.stg; + { + filter.stg = this.studiengaenge.map(({ studiengang_kz, semester, orgform_kurzbz }) => ({ + studiengang_kz, + semester, + orgform_kurzbz + })); + } if (hasLektoren) - filter.uid = this.lecturers.map(l => l.uid); + filter.uid = this.lecturers.map(lecture => lecture.uid); if (this.previewRole === 'lektor') return [this.$api.call(ApiKalender.getPlanLecturer(start.toISODate(), end.toISODate()))]; @@ -540,7 +566,8 @@ export default { return this.$api.call( ApiKalender.addKalenderEvent( obj.orig.lehreinheit_id, - this.ort_kurzbz ? this.ort_kurzbz : obj.orig.ort_kurzbz, + this.rooms.length ? this.rooms.map(r => r.ort_kurzbz) + : (obj.orig.ort_kurzbz ? [obj.orig.ort_kurzbz] : []), start_time, end_time )) @@ -690,18 +717,20 @@ export default { removeLecturer(uid) { - this.lecturers = this.lecturers.filter(lecture => lecture.uid !== uid); - delete this.overlayCache[uid]; - this.$refs.calendar.resetEventLoader(); - }, - clearOrt() { - this.ort_kurzbz = null; - this.$refs.calendar.resetEventLoader(); - }, - clearStg() { - this.stg = null; - this.show_stg = null; - this.$refs.calendar.resetEventLoader(); + if (uid == null) + { + for (const lect of this.lecturers) + delete this.overlayCache[lect.uid]; + + this.lecturers = []; + this.$refs.calendar.resetEventLoader(); + } + else + { + this.lecturers = this.lecturers.filter(lecture => lecture.uid !== uid); + delete this.overlayCache[uid]; + this.$refs.calendar.resetEventLoader(); + } }, triggerSync() { @@ -755,7 +784,16 @@ export default { handler() { this.rebuildExtraBackgrounds(); } - } + }, + rooms() { + this.$refs.calendar.resetEventLoader(); + }, + studiengaenge: { + deep: true, + handler() { + this.$refs.calendar.resetEventLoader(); + } + }, }, mounted() { this.reservierungPending = false; @@ -813,6 +851,8 @@ export default { this.visibleStatusArray = res.data.visible_status; this.visibleStatus = ['all']; }); + this.$api.call(ApiInfo.getStudiengaenge()) + .then(res => {this.studiengaenge_all = res.data}); }, template: `
@@ -974,37 +1014,19 @@ export default {
-
-
- {{ ort_kurzbz }} - -
-
- -
-
- {{ show_stg }} - -
-
+ +
- +
@@ -1096,7 +1118,7 @@ export default { db_query("SELECT kalender_id FROM lehre.tbl_kalender_event L echo '
lehre.tbl_kalender_event: neue Tabellen hinzugefuegt'; } + +// Add new name type in public.tbl_variablenname +if ($result = @$db->db_query("SELECT 1 FROM public.tbl_variablenname WHERE name = 'tempus_favorites';")) +{ + if ($db->db_num_rows($result) == 0) + { + $qry = "INSERT INTO public.tbl_variablenname(name, defaultwert) VALUES('tempus_favorites', null);"; + + if (!$db->db_query($qry)) + echo 'public.tbl_variablenname '.$db->db_last_error().'
'; + else + echo 'public.tbl_variablenname: Added name "tempus_favorites"
'; + } +}