diff --git a/application/controllers/api/frontend/v1/tempus/Kalender.php b/application/controllers/api/frontend/v1/tempus/Kalender.php index 5964f397a..7ac03108d 100644 --- a/application/controllers/api/frontend/v1/tempus/Kalender.php +++ b/application/controllers/api/frontend/v1/tempus/Kalender.php @@ -173,6 +173,13 @@ class Kalender extends FHCAPI_Controller $start_date = $this->_ci->input->post('start_date', TRUE); $end_date = $this->_ci->input->post('end_date', TRUE); + $isCollisionCheckEnabled = filter_var( + $this->_ci->input->post('collisionCheck', TRUE) ?? false, + FILTER_VALIDATE_BOOLEAN + ); + + $maxDailyEventLimit = $this->_ci->input->post('maxDailyEventLimit', TRUE) ?? false; + $filter = $this->_checkFilter(self::ALLOWED_PLAN_FILTER); $stundenplan_data = $this->_ci->kalenderlib->getPlanForPlanner( @@ -180,7 +187,9 @@ class Kalender extends FHCAPI_Controller $end_date, isset($filter->ort) ? $filter->ort : null, isset($filter->uid) ? $filter->uid : null, - isset($filter->stg) ? $filter->stg : null + isset($filter->stg) ? $filter->stg : null, + $isCollisionCheckEnabled, + $maxDailyEventLimit ); $this->terminateWithSuccess($stundenplan_data); @@ -198,9 +207,17 @@ class Kalender extends FHCAPI_Controller $start_date = $this->_ci->input->get('start_date', TRUE); $end_date = $this->_ci->input->get('end_date', TRUE); + $isCollisionCheckEnabled = filter_var( + $this->_ci->input->get('collisionCheck', TRUE) ?? false, + FILTER_VALIDATE_BOOLEAN + ); + $maxDailyEventLimit = $this->_ci->input->get('maxDailyEventLimit', TRUE) ?? false; + $stundenplan_data = $this->_ci->kalenderlib->getPlanForStudent( $start_date, - $end_date + $end_date, + $isCollisionCheckEnabled, + $maxDailyEventLimit ); $this->terminateWithSuccess($stundenplan_data); @@ -218,9 +235,17 @@ class Kalender extends FHCAPI_Controller $start_date = $this->_ci->input->get('start_date', TRUE); $end_date = $this->_ci->input->get('end_date', TRUE); + $isCollisionCheckEnabled = filter_var( + $this->_ci->input->get('collisionCheck', TRUE) ?? false, + FILTER_VALIDATE_BOOLEAN + ); + $maxDailyEventLimit = $this->_ci->input->get('maxDailyEventLimit', TRUE) ?? false; + $stundenplan_data = $this->_ci->kalenderlib->getPlanForLecturer( $start_date, - $end_date + $end_date, + $isCollisionCheckEnabled, + $maxDailyEventLimit ); $this->terminateWithSuccess($stundenplan_data); diff --git a/application/controllers/api/frontend/v1/tempus/Reservierung.php b/application/controllers/api/frontend/v1/tempus/Reservierung.php index 74ce97c33..72c228451 100644 --- a/application/controllers/api/frontend/v1/tempus/Reservierung.php +++ b/application/controllers/api/frontend/v1/tempus/Reservierung.php @@ -12,10 +12,11 @@ class Reservierung extends FHCAPI_Controller { parent::__construct([ 'addReservierung' => 'lehre/lvplan:rw', - 'getRollen' => 'lehre/lvplan:rw', - 'getInformation' => 'lehre/lvplan:rw', - 'getLektor' => 'lehre/lvplan:rw', - 'searchGroup' => 'lehre/lvplan:rw', + 'getRollen' => 'lehre/lvplan:r', + 'getInformation' => 'lehre/lvplan:r', + 'getLektor' => 'lehre/lvplan:r', + 'searchGroup' => 'lehre/lvplan:r', + 'updateTeilnehmer' => 'lehre/reservierung:rw', ]); $this->_ci =& get_instance(); @@ -72,12 +73,17 @@ class Reservierung extends FHCAPI_Controller $rollen = $this->_ci->KalenderEventRolleModel->load(); + $this->_ci->KalenderEventRolleModel->addOrder('sort'); + $this->_ci->KalenderEventRolleModel->addSelect('rolle_kurzbz, array_to_json(bezeichnung_mehrsprachig::varchar[])->>'. $language. ' as bezeichnung'); + $gruppen_rollen = $this->_ci->KalenderEventRolleModel->loadWhere(array('rolle_kurzbz !=' => 'organisator')); + $this->_ci->StudiensemesterModel->addOrder('start', 'DESC'); $studiensemester = $this->_ci->StudiensemesterModel->load(); $return_array['studiengaenge'] = hasData($studiengaenge) ? getData($studiengaenge) : []; $return_array['berechtigt'] = true; $return_array['rollen'] = hasData($rollen) ? getData($rollen) : []; + $return_array['gruppen_rollen'] = hasData($gruppen_rollen) ? getData($gruppen_rollen) : []; $return_array['studiensemester'] = hasData($studiensemester) ? getData($studiensemester) : []; @@ -236,4 +242,45 @@ class Reservierung extends FHCAPI_Controller $this->terminateWithSuccess(hasData($result) ? getData($result) : array()); } + public function updateTeilnehmer() + { + $this->_ci->form_validation->set_rules('kalender_id', 'kalender_id', 'required|numeric', [ + 'required' => $this->p->t('ui', 'error_fieldRequired'), + ]); + + if ($this->_ci->form_validation->run() === FALSE) + $this->terminateWithValidationErrors($this->_ci->form_validation->error_array()); + + if (!$this->_ci->permissionlib->isBerechtigt('lehre/reservierung')) + $this->terminateWithError($this->p->t('ui', 'keineBerechtigung'), self::ERROR_TYPE_GENERAL); + + $kalender_id = $this->_ci->input->post('kalender_id', TRUE); + $teilnehmer = $this->_ci->input->post('teilnehmer', TRUE); + $gruppen = $this->_ci->input->post('gruppen', TRUE); + + $teilnehmer = is_array($teilnehmer) ? $teilnehmer : []; + $gruppen = is_array($gruppen) ? $gruppen : []; + + $hat_organisator = false; + + foreach ($teilnehmer as $teil) + { + if (!empty($teil['uid']) && isset($teil['rolle']) && $teil['rolle'] === 'organisator') + { + $hat_organisator = true; + break; + } + } + + if (!$hat_organisator) + $teilnehmer[] = array('uid' => getAuthUID(), 'rolle' => 'organisator'); + + $result = $this->_ci->kalenderlib->updateReservierungTeilnehmer($kalender_id, $teilnehmer, $gruppen); + + if (isError($result)) + $this->terminateWithError(getError($result)); + + $this->terminateWithSuccess(getData($result)); + } + } diff --git a/application/libraries/KalenderLib.php b/application/libraries/KalenderLib.php index d81b68bea..8c4ab479e 100644 --- a/application/libraries/KalenderLib.php +++ b/application/libraries/KalenderLib.php @@ -104,7 +104,7 @@ class KalenderLib $data = $this->_loadFilteredBasePlan($kalender_entry->von, $kalender_entry->bis); return $this->_mapEvents($data); } - public function getPlanForPlanner($start_date, $end_date, $ort = null, $uids = null, $studiengaenge = null) + public function getPlanForPlanner($start_date, $end_date, $ort = null, $uids = null, $studiengaenge = null, $collisionCheck = true, $maxDailyEventLimit = null) { $this->_buildBasePlanQuery(); @@ -240,11 +240,10 @@ class KalenderLib $data = $this->_loadFilteredBasePlan($start_date, $end_date); - - return $this->_mapEvents($data); + return $this->_mapEvents($data, $collisionCheck, $maxDailyEventLimit); } - public function getPlanForStudent($start_date, $end_date) + public function getPlanForStudent($start_date, $end_date, $collisionCheck = true, $maxDailyEventLimit = null) { $this->_buildBasePlanQuery(); @@ -252,11 +251,11 @@ class KalenderLib $data = $this->_loadFilteredBasePlan($start_date, $end_date); - return $this->_mapEvents($data); + return $this->_mapEvents($data, $collisionCheck, $maxDailyEventLimit); } - public function getPlanForLecturer($start_date, $end_date) + public function getPlanForLecturer($start_date, $end_date, $collisionCheck = true, $maxDailyEventLimit = null) { $this->_buildBasePlanQuery(); @@ -272,7 +271,7 @@ class KalenderLib )", NULL, FALSE); $data = $this->_loadFilteredBasePlan($start_date, $end_date); - return $this->_mapEvents($data); + return $this->_mapEvents($data, $collisionCheck, $maxDailyEventLimit); } public function getZeitsperren($start_date, $end_date, $emp) { @@ -1394,6 +1393,72 @@ class KalenderLib return $dbModel->execReadOnlyQuery($history_sql, $params); } + + public function updateReservierungTeilnehmer($kalender_id, $teilnehmer, $specialFinalGroups) + { + $entryResult = $this->_loadKalenderEntry($kalender_id); + if (isError($entryResult)) + return $entryResult; + + $kalender_entry = getData($entryResult); + + if ($kalender_entry->typ !== 'reservierung') + { + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'entry_not_editable'), + 'errorCode' => 'entry_not_editable' + ]); + } + + $this->_ci->KalenderModel->db->trans_start(); + + $deleteresult = $this->_deleteReservierungTeilnehmerEntry($kalender_entry); + if (isError($deleteresult)) + { + $this->_ci->KalenderModel->db->trans_rollback(); + return $deleteresult; + } + + foreach ($teilnehmer as $teil) + { + $teilnehmerresult = $this->_addTeilnehmerToEvent($kalender_id, $teil['uid'], $teil['rolle']); + if (isError($teilnehmerresult)) + { + $this->_ci->KalenderModel->db->trans_rollback(); + return $teilnehmerresult; + } + } + + foreach ($specialFinalGroups as $group) + { + $specialgroupresult = $this->_addFinalGroupToEvent($kalender_id, $group['gid'], !($group['lehrverband'] === 'false'), $group['gruppe_kurzbz'], $group['studiensemester_kurzbz'], $group['rolle']); + if (isError($specialgroupresult)) + { + $this->_ci->KalenderModel->db->trans_rollback(); + return $specialgroupresult; + } + } + + $entryResult = $this->_loadKalenderEntry($kalender_id); + if (isError($entryResult)) + { + $this->_ci->KalenderModel->db->trans_rollback(); + return $entryResult; + } + + $kalender_entry = getData($entryResult); + $errors = $this->_ci->collisionchecker->run($kalender_entry); + + if (!empty($errors)) + { + $this->_ci->KalenderModel->db->trans_rollback(); + return error($errors); + } + + $this->_ci->KalenderModel->db->trans_complete(); + return success($kalender_id); + } + private function _buildFilteredKalenderCte($start_date, $end_date) { $end_date = date('Y-m-d', strtotime($end_date . ' +1 day')); @@ -1423,6 +1488,7 @@ class KalenderLib tbl_kalender.bis, tbl_kalender_ort.location, tbl_kalender_ort.ort_kurzbz, + tbl_ort.content_id as ort_content_id, tbl_lehreinheit.lehreinheit_id, tbl_lehreinheit.anmerkung as le_anmerkung, tbl_lehreinheit.lehrveranstaltung_id, @@ -1444,6 +1510,10 @@ class KalenderLib COALESCE(verbandgruppe.verband::varchar, \'\') || COALESCE(verbandgruppe.gruppe, \'\'), \'\') as verband_grp, + teilnehmer.studiensemester_kurzbz AS teilnehmer_studiensemester_kurzbz, + teilnehmer.rolle_kurzbz AS teilnehmer_rolle_kurzbz, + teilnehmergruppe.gid AS teilnehmerg_grp_gid, + verbandgruppe.gid AS verband_grp_gid, tbl_kalender_event.beschreibung, tbl_kalender_event.titel, teilmitarbeiter.kurzbz as teilnehmer_kurzbz, @@ -1528,6 +1598,7 @@ class KalenderLib AND TRIM(COALESCE(tbl_lehreinheitgruppe.verband::text, \'\')) = TRIM(le_lehrverband.verband::text) AND TRIM(COALESCE(tbl_lehreinheitgruppe.gruppe::text, \'\')) = TRIM(le_lehrverband.gruppe::text)', 'LEFT'); $this->_ci->KalenderModel->addJoin('public.tbl_studiengang le_studiengang', 'le_lehrverband.studiengang_kz = le_studiengang.studiengang_kz', 'LEFT'); + $this->_ci->KalenderModel->addJoin('public.tbl_ort', 'tbl_ort.ort_kurzbz = tbl_kalender_ort.ort_kurzbz', 'LEFT'); // Include tags in the query $this->_ci->load->config('tempus'); @@ -1606,7 +1677,7 @@ class KalenderLib return $this->_ci->KalenderModel->execReadOnlyQuery($query); } - private function _mapEvents($data, $collisionCheck = true) + private function _mapEvents($data, $collisionCheck = true, $maxDailyEventLimit = null) { $stundenplan_data = []; @@ -1637,6 +1708,7 @@ class KalenderLib 'tooltip' => 'tip', 'status_kurzbz' => $row->status_kurzbz, 'ort_kurzbz' => [], + 'ort_infos' => [], 'location' => null, 'lehrfach' => isset($row->lehrfach_kurzbz) ? $row->lehrfach_kurzbz : '', 'farbe' => isset($row->farbe) ? $row->farbe : '', @@ -1684,7 +1756,10 @@ class KalenderLib ]; } if (isset($row->ort_kurzbz) && $row->ort_kurzbz !== '' && !in_array($row->ort_kurzbz, $events[$id]->ort_kurzbz)) + { $events[$id]->ort_kurzbz[] = $row->ort_kurzbz; + $events[$id]->ort_infos[] = ['ort_kurzbz' => $row->ort_kurzbz, 'ort_content_id' => $row->ort_content_id]; + } if (isset($row->location) && $row->location !== '' && is_null($events[$id]->location)) $events[$id]->location = $row->location; @@ -1712,6 +1787,10 @@ class KalenderLib { $events[$id]->teilnehmer_gruppe[] = [ 'gruppe_kurzbz' => $row->verband_grp, + 'gid' => $row->verband_grp_gid, + 'lehrverband' => 'true', + 'studiensemester_kurzbz' => $row->teilnehmer_studiensemester_kurzbz, + 'rolle' => $row->teilnehmer_rolle_kurzbz, ]; } } @@ -1722,6 +1801,10 @@ class KalenderLib { $events[$id]->teilnehmer_gruppe[] = [ 'gruppe_kurzbz' => $row->teilnehmerg_grp, + 'gid' => $row->teilnehmerg_grp_gid, + 'lehrverband' => 'false', + 'studiensemester_kurzbz' => $row->teilnehmer_studiensemester_kurzbz, + 'rolle' => $row->teilnehmer_rolle_kurzbz, ]; } } @@ -1774,6 +1857,21 @@ class KalenderLib usort($event->ort_kurzbz, 'strnatcasecmp'); } + if (!is_null($maxDailyEventLimit) && is_numeric($maxDailyEventLimit) && $maxDailyEventLimit > 0) + { + $eventsPerDay = []; + $events = array_filter($events, function ($event) use (&$eventsPerDay, $maxDailyEventLimit) { + $date = $event->datum; + $eventsPerDay[$date] = $eventsPerDay[$date] ?? 0; + + if ($eventsPerDay[$date] >= $maxDailyEventLimit) + return false; + + $eventsPerDay[$date]++; + return true; + }); + } + return array_values($events); } @@ -1891,7 +1989,7 @@ class KalenderLib 'kalender_id' => $kalender_id, 'studiengang_kz' => $gruppe->studiengang_kz, 'studiensemester_kurzbz' => $studiensemester_kurzbz, - 'semester' => isEmptyString($gruppe->semester) ? null : $gruppe->semester, + 'semester' => ($gruppe->semester === null || $gruppe->semester === '') ? null : $gruppe->semester, 'verband' => isEmptyString($gruppe->verband) ? null : $gruppe->verband, 'gruppe' => isEmptyString($gruppe->gruppe) ? null : $gruppe->gruppe, 'rolle_kurzbz' => $rolle diff --git a/application/libraries/collision/checks/LectureCollisionCheck.php b/application/libraries/collision/checks/LectureCollisionCheck.php index 5c5e43897..864dbaea7 100644 --- a/application/libraries/collision/checks/LectureCollisionCheck.php +++ b/application/libraries/collision/checks/LectureCollisionCheck.php @@ -53,7 +53,7 @@ class LectureCollisionCheck implements ICollisionCheck $kollisionsfreie_user = unserialize(KOLLISIONSFREIE_USER); $grouped = []; - $this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id, current_lehreinheit_ma.mitarbeiter_uid'); + $this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id, current_lehreinheit_ma.mitarbeiter_uid, other_kalender.von, other_kalender.kalender_id as other_kalender_id'); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit current_kalender_le', 'current_kalender_le.kalender_id = tbl_kalender.kalender_id'); $this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheit current_lehreinheit', 'current_lehreinheit.lehreinheit_id = current_kalender_le.lehreinheit_id'); $this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheitmitarbeiter current_lehreinheit_ma', 'current_lehreinheit_ma.lehreinheit_id = current_lehreinheit.lehreinheit_id'); @@ -87,11 +87,13 @@ class LectureCollisionCheck implements ICollisionCheck { $grouped[$row->kalender_id][] = [ 'message' => $this->_ci->phraseslib->t('ui', 'ma_le_kollision') . ': ' . $row->mitarbeiter_uid, + 'kalender_id' => $row->other_kalender_id, + 'datum' => $row->von, ]; } } - $this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id, current_lehreinheit_ma.mitarbeiter_uid'); + $this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id, current_lehreinheit_ma.mitarbeiter_uid, other_kalender.von, other_kalender.kalender_id as other_kalender_id'); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit current_kalender_le', 'current_kalender_le.kalender_id = tbl_kalender.kalender_id'); $this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheit current_lehreinheit', 'current_lehreinheit.lehreinheit_id = current_kalender_le.lehreinheit_id'); $this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheitmitarbeiter current_lehreinheit_ma', 'current_lehreinheit_ma.lehreinheit_id = current_lehreinheit.lehreinheit_id'); @@ -124,11 +126,13 @@ class LectureCollisionCheck implements ICollisionCheck { $grouped[$row->kalender_id][] = [ 'message' => $this->_ci->phraseslib->t('ui', 'reservierung_kollision') . ': ' . $row->mitarbeiter_uid, + 'kalender_id' => $row->other_kalender_id, + 'datum' => $row->von, ]; } } - $this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id, z.mitarbeiter_uid'); + $this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id, z.mitarbeiter_uid, tbl_kalender.von'); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit current_kalender_le', 'current_kalender_le.kalender_id = tbl_kalender.kalender_id'); $this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheit current_lehreinheit', 'current_lehreinheit.lehreinheit_id = current_kalender_le.lehreinheit_id'); $this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheitmitarbeiter current_lehreinheit_ma', 'current_lehreinheit_ma.lehreinheit_id = current_lehreinheit.lehreinheit_id'); diff --git a/application/libraries/collision/checks/RoomCollisionCheck.php b/application/libraries/collision/checks/RoomCollisionCheck.php index 192e308ca..60b59baf9 100644 --- a/application/libraries/collision/checks/RoomCollisionCheck.php +++ b/application/libraries/collision/checks/RoomCollisionCheck.php @@ -54,7 +54,7 @@ class RoomCollisionCheck implements ICollisionCheck { if (empty($kalender_ids)) return []; - $this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id, current_ort.ort_kurzbz'); + $this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id, current_ort.ort_kurzbz, other_kalender.von, other_kalender.kalender_id as other_kalender_id'); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort current_ort', 'current_ort.kalender_id = tbl_kalender.kalender_id'); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort other_ort', 'other_ort.ort_kurzbz = current_ort.ort_kurzbz'); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender other_kalender', 'other_kalender.kalender_id = other_ort.kalender_id'); @@ -83,7 +83,9 @@ class RoomCollisionCheck implements ICollisionCheck foreach (getData($result) as $row) { $grouped[$row->kalender_id][] = [ - 'message' => $this->_ci->phraseslib->t('ui', 'raum_kollision') . ': ' . $row->ort_kurzbz + 'message' => $this->_ci->phraseslib->t('ui', 'raum_kollision') . ': ' . $row->ort_kurzbz, + 'kalender_id' => $row->other_kalender_id, + 'datum' => $row->von, ]; } diff --git a/application/libraries/collision/checks/StudentCollisionCheck.php b/application/libraries/collision/checks/StudentCollisionCheck.php index 7c7b8b176..f0874198b 100644 --- a/application/libraries/collision/checks/StudentCollisionCheck.php +++ b/application/libraries/collision/checks/StudentCollisionCheck.php @@ -199,7 +199,7 @@ class StudentCollisionCheck implements ICollisionCheck $placeholders = implode(',', array_fill(0, count($kalender_ids), '?')); $sql = " - SELECT DISTINCT current_kalender.kalender_id, current_benutzergruppe.uid + SELECT DISTINCT current_kalender.kalender_id, current_benutzergruppe.uid, other_kalender.von, other_kalender.kalender_id as other_kalender_id FROM lehre.tbl_kalender current_kalender JOIN lehre.tbl_kalender_lehreinheit current_kalender_le ON current_kalender_le.kalender_id = current_kalender.kalender_id JOIN lehre.tbl_lehreinheit current_lehreinheit ON current_lehreinheit.lehreinheit_id = current_kalender_le.lehreinheit_id @@ -236,7 +236,7 @@ class StudentCollisionCheck implements ICollisionCheck UNION ALL - SELECT DISTINCT current_kalender.kalender_id, current_studentlehrverband.student_uid AS uid + SELECT DISTINCT current_kalender.kalender_id, current_studentlehrverband.student_uid AS uid, other_kalender.von, other_kalender.kalender_id as other_kalender_id FROM lehre.tbl_kalender current_kalender JOIN lehre.tbl_kalender_lehreinheit current_kalender_le ON current_kalender_le.kalender_id = current_kalender.kalender_id JOIN lehre.tbl_lehreinheit current_lehreinheit ON current_lehreinheit.lehreinheit_id = current_kalender_le.lehreinheit_id @@ -279,7 +279,9 @@ class StudentCollisionCheck implements ICollisionCheck foreach (getData($result) as $row) { $grouped[$row->kalender_id][] = [ - 'message' => $this->_ci->phraseslib->t('ui', 'student_kollision') . ': ' . $row->uid + 'message' => $this->_ci->phraseslib->t('ui', 'student_kollision') . ': ' . $row->uid, + 'kalender_id' => $row->other_kalender_id, + 'datum' => $row->von, ]; } diff --git a/application/libraries/collision/checks/VerbandCollisionCheck.php b/application/libraries/collision/checks/VerbandCollisionCheck.php index 8dd032d31..d6abee390 100644 --- a/application/libraries/collision/checks/VerbandCollisionCheck.php +++ b/application/libraries/collision/checks/VerbandCollisionCheck.php @@ -319,7 +319,8 @@ class VerbandCollisionCheck implements ICollisionCheck UPPER(stg.typ::text || stg.kurzbz::text) || '-' || other_lehreinheitguppe.semester || COALESCE(other_lehreinheitguppe.verband::text, '') || COALESCE(other_lehreinheitguppe.gruppe::text, '') - ) AS gruppenname + ) AS gruppenname, + other_kalender.von, other_kalender.kalender_id as other_kalender_id FROM lehre.tbl_kalender current_kalender JOIN ( @@ -412,7 +413,11 @@ class VerbandCollisionCheck implements ICollisionCheck $grouped = []; foreach (getData($result) as $row) { - $grouped[$row->kalender_id][] = ['message' => $this->_ci->phraseslib->t('ui', 'verband_kollision') . ': ' . $row->gruppenname]; + $grouped[$row->kalender_id][] = [ + 'message' => $this->_ci->phraseslib->t('ui', 'verband_kollision') . ': ' . $row->gruppenname, + 'kalender_id' => $row->other_kalender_id, + 'datum' => $row->von, + ]; } return $grouped; diff --git a/public/css/Cis4/CoreCalendarEvents.css b/public/css/Cis4/CoreCalendarEvents.css index d06ada484..8f2daca25 100644 --- a/public/css/Cis4/CoreCalendarEvents.css +++ b/public/css/Cis4/CoreCalendarEvents.css @@ -96,3 +96,17 @@ .fhc-roominformation .calendar-event-default .event-place { display: none; } + +.tag-row-cell { + width: 100%; + max-width: 0; +} + +.tag-row-wrapper, +.tag-row-wrapper > * { + display: flex; + flex-wrap: wrap !important; + max-width: 100%; + white-space: normal !important; + overflow: visible !important; +} diff --git a/public/css/Tempus.css b/public/css/Tempus.css index 50a375ac9..08ad0f746 100644 --- a/public/css/Tempus.css +++ b/public/css/Tempus.css @@ -6,19 +6,19 @@ @import './components/Notiz.css'; html { - font-size: .875em; - height: 100%; + font-size: 0.875em; + height: 100%; } body { /*display: flex;*/ - height: 100%; + height: 100%; } .heightfull { - height: 95%; + height: 95%; } .navbar-dark .navbar-brand:focus { - box-shadow: 0 0 0 .25rem rgba(13,110,253,.25); + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); z-index: 3; } #main { @@ -28,13 +28,13 @@ body { height: 100%; } .searchbar { - margin-right: 0!important; + margin-right: 0 !important; } .searchbar > .input-group { - margin-right: 0!important; + margin-right: 0 !important; } .searchbar > .input-group > * { - border-radius: 0!important; + border-radius: 0 !important; } #sidebarMenu { @@ -55,7 +55,7 @@ body { @media (min-width: 768px) { #sidebarMenu { - visibility: visible!important; + visibility: visible !important; transform: none; position: inherit; z-index: 1; @@ -64,27 +64,24 @@ body { .toast.toast-success { color: #0f5132; - background-color: #d1e7dd!important; - border-color: #badbcc!important; + background-color: #d1e7dd !important; + border-color: #badbcc !important; } .toast.toast-danger { color: #842029; - background-color: #f8d7da!important; - border-color: #f5c2c7!important; + background-color: #f8d7da !important; + border-color: #f5c2c7 !important; } .has-filter .fa-filter { color: var(--bs-success); } - - - #parkingslot { border: 1px dashed; min-height: 5vh; max-height: 15vh; - color: #AAAAAA; + color: #aaaaaa; text-align: center; font-size: large; margin-top: 1.25rem; @@ -96,7 +93,7 @@ body { } .course-picker { - border: 1px dashed #AAAAAA; + border: 1px dashed #aaaaaa; display: flex; flex-direction: column; flex: 1 1 0; @@ -132,11 +129,11 @@ body { } .eckerltest { - box-shadow: 3px 3px 3px #ccc; + box-shadow: 3px 3px 3px #ccc; } .verband-selection { - border: 1px dashed #AAAAAA; + border: 1px dashed #aaaaaa; margin-bottom: 0.5rem; max-height: 450px; overflow-y: auto; @@ -145,7 +142,7 @@ body { } .lecture-selection { - border: 1px dashed #AAAAAA; + border: 1px dashed #aaaaaa; margin-bottom: 0.5rem; max-height: 20vh; overflow-y: auto; @@ -154,7 +151,7 @@ body { } .room-selection { - border: 1px dashed #AAAAAA; + border: 1px dashed #aaaaaa; margin-bottom: 0.5rem; padding: 0.5rem 0 0.5rem 0.5rem; } @@ -169,10 +166,8 @@ body { transform: scale(1.1); } - - .bg-lecturer-wish { - opacity: .15; + opacity: 0.15; } .bg-lecturer-block { background: rgba(255, 0, 0, 0.15); @@ -195,18 +190,21 @@ body { .wish-w--2 { - background-color: #FF2200; + background-color: #ff2200; } .wish-w--1 { - background-color: #FF9922; + background-color: #ff9922; } .wish-w-1 { - background-color: #CCFFCC; + background-color: #ccffcc; } .wish-w-2 { - background-color: #48FA66; + background-color: #48fa66; } +.fhc-calendar-base-grid-line-event { + position: relative !important; +} .room-option { display: grid; @@ -215,6 +213,76 @@ body { width: 100%; } +.updated-event { + position: relative !important; + z-index: 10; + animation: modernFocus 1s ease-out; + border-radius: 0.25rem; +} + +.updated-event-long { + position: relative !important; + z-index: 10; + animation: modernFocus 1.8s ease-out; + border-radius: 0.25rem; +} + +@keyframes modernFocus { + 0% { + background-color: #cfd4d8; + box-shadow: + 0 0 0 0 rgba(120, 120, 120, 0.8), + 0 0 0 0 rgba(255, 255, 255, 0.6); + } + + 30% { + background-color: #eef1f3; + box-shadow: + 0 0 0 8px rgba(120, 120, 120, 0.25), + 0 0 30px 8px rgba(255, 255, 255, 0.7); + } + + 60% { + background-color: #f7f8f9; + box-shadow: + 0 0 0 14px rgba(120, 120, 120, 0.15), + 0 0 45px 12px rgba(255, 255, 255, 0.5); + } + + 100% { + background-color: inherit; + box-shadow: + 0 0 0 24px rgba(120, 120, 120, 0), + 0 0 60px 20px rgba(255, 255, 255, 0); + } +} + +.spinner-overlay { + position: absolute; + inset: 0; /* top:0 right:0 bottom:0 left:0 */ + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + background: rgba(255, 255, 255, 1); + opacity: 0.4; +} + +.spinner { + width: 40px; + height: 40px; + border: 4px solid #ddd; + border-top: 4px solid #007bff; + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + .timepicker-row { display: flex; align-items: center; diff --git a/public/js/api/factory/tempus/kalender.js b/public/js/api/factory/tempus/kalender.js index 115f3c6b0..e03b89040 100644 --- a/public/js/api/factory/tempus/kalender.js +++ b/public/js/api/factory/tempus/kalender.js @@ -1,11 +1,17 @@ export default { - getPlan(filter, start_date, end_date) + getPlan(filter, start_date, end_date, collisionCheck = true, maxDailyEventLimit = null) { return { method: 'post', url: '/api/frontend/v1/tempus/Kalender/getPlan', - params: { ...filter, start_date, end_date } + params: { + ...filter, + start_date, + end_date, + collisionCheck, + ...(maxDailyEventLimit == null ? {} : { maxDailyEventLimit }) + } }; }, getRaumvorschlagSlots(lehreinheit_id, start_date, end_date) @@ -16,20 +22,30 @@ export default { params: { lehreinheit_id, start_date, end_date } }; }, - getPlanLecturer(start_date, end_date) + getPlanLecturer(start_date, end_date, collisionCheck = true, maxDailyEventLimit = null) { return { method: 'get', url: '/api/frontend/v1/tempus/Kalender/getPlanLecturer', - params: { start_date, end_date } + params: { + start_date, + end_date, + collisionCheck, + ...(maxDailyEventLimit == null ? {} : { maxDailyEventLimit }) + } }; }, - getPlanStudent(start_date, end_date) + getPlanStudent(start_date, end_date, collisionCheck = true, maxDailyEventLimit = null) { return { method: 'get', url: '/api/frontend/v1/tempus/Kalender/getPlanStudent', - params: { start_date, end_date } + params: { + start_date, + end_date, + collisionCheck, + ...(maxDailyEventLimit == null ? {} : { maxDailyEventLimit }) + } }; }, @@ -96,7 +112,7 @@ export default { params: { kalender_id} }; }, - addKalenderEvent(lehreinheit_id, ort_kurzbz, start_date, end_date, first_run) { + addKalenderEvent(lehreinheit_id, ort_kurzbz, start_date, end_date, first_run = true) { return { method: 'post', url: '/api/frontend/v1/tempus/Kalender/addKalenderEvent', diff --git a/public/js/api/factory/tempus/reservierung.js b/public/js/api/factory/tempus/reservierung.js index d0aa76bb8..4f4736f0f 100644 --- a/public/js/api/factory/tempus/reservierung.js +++ b/public/js/api/factory/tempus/reservierung.js @@ -55,5 +55,12 @@ export default { params: { titel, beschreibung, orte, start_date, end_date, teilnehmer, specialFinalGroups, specialGroups, groups} }; }, + updateTeilnehmer(kalender_id, teilnehmer, gruppen) { + return { + method: 'post', + url: '/api/frontend/v1/tempus/Reservierung/updateTeilnehmer', + params: { kalender_id, teilnehmer, gruppen } + }; + }, }; \ No newline at end of file diff --git a/public/js/components/Calendar/Base.js b/public/js/components/Calendar/Base.js index d89f63a6c..89c8d687e 100644 --- a/public/js/components/Calendar/Base.js +++ b/public/js/components/Calendar/Base.js @@ -123,6 +123,10 @@ export default { draggableEvents: [Boolean, Array, Function], dropableEvents: [Boolean, Array, Function], resizableEvents: [Boolean, Array, Function], + eventReloadKey: { + type: Number, + default: 0, + }, onDragover: Function, onDrop: Function, onResize: Function @@ -338,6 +342,7 @@ export default { ` -} \ No newline at end of file +} diff --git a/public/js/components/Calendar/Base/Grid.js b/public/js/components/Calendar/Base/Grid.js index 5a2187d9f..426bfed8f 100644 --- a/public/js/components/Calendar/Base/Grid.js +++ b/public/js/components/Calendar/Base/Grid.js @@ -558,7 +558,7 @@ export default { >