mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-06-01 20:29:29 +00:00
cis4 raumreservierung beta version
This commit is contained in:
@@ -23,6 +23,15 @@ Events::on('loadRenderers', function ($renderers) {
|
||||
);
|
||||
});
|
||||
|
||||
Events::on('loadRenderers', function ($renderers) {
|
||||
$fhc_core_renderers =& $renderers();
|
||||
$fhc_core_renderers["slot_room"] = array(
|
||||
'modalTitle' => APP_ROOT.'public/js/components/Cis/Renderer/Slot/roomModalTitle.js',
|
||||
'modalContent' => APP_ROOT.'public/js/components/Cis/Renderer/Slot/roomModalContent.js',
|
||||
'calendarEventStyles' => APP_ROOT.'public/css/Cis4/CoreCalendarEvents.css'
|
||||
);
|
||||
});
|
||||
|
||||
Events::on('loadRenderers', function ($renderers) {
|
||||
$fhc_core_renderers =& $renderers();
|
||||
$fhc_core_renderers["ferien"] = array(
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
<?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 RoomPlan extends FHCAPI_Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Object initialization
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'addRoomReservation' => self::PERM_LOGGED,
|
||||
'deleteRoomReservation' => self::PERM_LOGGED,
|
||||
'getRoomCreationInfo' => self::PERM_LOGGED,
|
||||
'getGruppen' => self::PERM_LOGGED,
|
||||
'getLektor' => self::PERM_LOGGED,
|
||||
'getReservableMap' => self::PERM_LOGGED,
|
||||
]);
|
||||
|
||||
$this->load->library('LogLib');
|
||||
$this->loglib->setConfigs(array(
|
||||
'classIndex' => 5,
|
||||
'functionIndex' => 5,
|
||||
'lineIndex' => 4,
|
||||
'dbLogType' => 'API',
|
||||
'dbExecuteUser' => 'RESTful API'
|
||||
));
|
||||
|
||||
$this->load->library('form_validation');
|
||||
$this->load->library('PermissionLib');
|
||||
$this->load->library('StundenplanLib');
|
||||
|
||||
$this->loadPhrases(['ui']);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
|
||||
|
||||
|
||||
public function addRoomReservation()
|
||||
{
|
||||
$this->form_validation->set_rules('selectedStart', "Start", "required");
|
||||
$this->form_validation->set_rules('selectedEnd', "End", "required");
|
||||
$this->form_validation->set_rules('title', "Title", "required|max_length[10]");
|
||||
$this->form_validation->set_rules('beschreibung', "Beschreibung", "required|max_length[32]");
|
||||
$this->form_validation->set_rules('ort_kurzbz', "Ort", "required|max_length[16]");
|
||||
$this->form_validation->set_rules('studiengang', 'Studiengang', 'numeric');
|
||||
$this->form_validation->set_rules('semester', 'Semester', 'integer|greater_than_equal_to[0]');
|
||||
$this->form_validation->set_rules('verband', 'Verband', 'trim');
|
||||
$this->form_validation->set_rules('gruppe', 'Gruppe', 'trim');
|
||||
$this->form_validation->set_rules('spezialgruppe', 'Spezialgruppe', 'max_length[32]');
|
||||
$this->form_validation->set_rules('lektoren', 'Lektoren');
|
||||
|
||||
if (!$this->form_validation->run())
|
||||
$this->terminateWithValidationErrors($this->form_validation->error_array());
|
||||
|
||||
$start = $this->input->post('selectedStart');
|
||||
$end = $this->input->post('selectedEnd');
|
||||
$title = $this->input->post('title');
|
||||
$beschreibung = $this->input->post('beschreibung');
|
||||
$ort_kurzbz = $this->input->post('ort_kurzbz');
|
||||
|
||||
$studiengang_kz = $this->input->post('studiengang');
|
||||
$semester = $this->input->post('semester');
|
||||
$verband = $this->input->post('verband');
|
||||
$gruppe = $this->input->post('gruppe');
|
||||
$spezialgruppe = $this->input->post('spezialgruppe');
|
||||
$lektoren = $this->input->post('lektoren');
|
||||
|
||||
|
||||
$result = $this->stundenplanlib->addReservation($start, $end, $title, $beschreibung, $ort_kurzbz, $lektoren, $studiengang_kz, $semester, $verband, $gruppe, $spezialgruppe);
|
||||
|
||||
if (isError($result))
|
||||
$this->terminateWithError($result);
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
}
|
||||
|
||||
public function deleteRoomReservation()
|
||||
{
|
||||
$reservierung_id = $this->input->post('reservierung_id');
|
||||
|
||||
$result = $this->stundenplanlib->deleteReservation($reservierung_id);
|
||||
|
||||
if (isError($result))
|
||||
$this->terminateWithError($result);
|
||||
|
||||
$this->terminateWithSuccess($result);
|
||||
}
|
||||
|
||||
public function getRoomCreationInfo()
|
||||
{
|
||||
$return_array = array('berechtigt' => false, 'studiengaenge' => []);
|
||||
if (!$this->permissionlib->isBerechtigt('lehre/reservierung'))
|
||||
$this->terminateWithSuccess($return_array);
|
||||
|
||||
$stg_berechtigungen = $this->permissionlib->getSTG_isEntitledFor('lehre/reservierung');
|
||||
if (isEmptyArray($stg_berechtigungen))
|
||||
$this->terminateWithSuccess($return_array);
|
||||
|
||||
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||
$this->StudiengangModel->addSelect('studiengang_kz, UPPER(CONCAT(typ, kurzbz)) as kuerzel, kurzbzlang');
|
||||
$this->StudiengangModel->addOrder('typ, kurzbz');
|
||||
$this->StudiengangModel->db->where_in('studiengang_kz', $stg_berechtigungen);
|
||||
$studiengaenge = $this->StudiengangModel->loadWhere(array('aktiv' => true));
|
||||
|
||||
if (isError($studiengaenge))
|
||||
$this->terminateWithError($studiengaenge);
|
||||
|
||||
$return_array['studiengaenge'] = hasData($studiengaenge) ? getData($studiengaenge) : [];
|
||||
$return_array['berechtigt'] = true;
|
||||
|
||||
$this->terminateWithSuccess($return_array);
|
||||
}
|
||||
|
||||
public function getGruppen()
|
||||
{
|
||||
$query = $this->input->get('query');
|
||||
if (is_null($query))
|
||||
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
|
||||
|
||||
$stg_berechtigungen = $this->permissionlib->getSTG_isEntitledFor('lehre/reservierung');
|
||||
|
||||
if (isEmptyArray($stg_berechtigungen))
|
||||
$this->terminateWithSuccess([]);
|
||||
|
||||
$this->load->model('organisation/gruppe_model', 'GruppeModel');
|
||||
|
||||
$query_words = explode(' ', urldecode($query));
|
||||
|
||||
$this->GruppeModel->addOrder('gruppe_kurzbz');
|
||||
$this->GruppeModel->db->group_start();
|
||||
foreach ($query_words as $word)
|
||||
{
|
||||
$this->GruppeModel->db->group_start();
|
||||
$this->GruppeModel->db->where('gruppe_kurzbz ILIKE', "%" . $word . "%");
|
||||
$this->GruppeModel->db->or_where('bezeichnung ILIKE', "%" . $word . "%");
|
||||
$this->GruppeModel->db->or_where('beschreibung ILIKE', "%" . $word . "%");
|
||||
$this->GruppeModel->db->or_where('orgform_kurzbz ILIKE', "%" . $word . "%");
|
||||
|
||||
if (is_numeric($word))
|
||||
{
|
||||
$this->GruppeModel->db->or_where('studiengang_kz', $word);
|
||||
}
|
||||
$this->GruppeModel->db->group_end();
|
||||
}
|
||||
$this->GruppeModel->db->group_end();
|
||||
$this->GruppeModel->db->where_in('studiengang_kz', $stg_berechtigungen);
|
||||
$gruppen = $this->GruppeModel->loadWhere(array('sichtbar' => true, 'lehre' => true));
|
||||
if (isError($gruppen))
|
||||
$this->terminateWithError($gruppen);
|
||||
|
||||
$this->terminateWithSuccess(hasData($gruppen) ? getData($gruppen) : []);
|
||||
}
|
||||
|
||||
public function getLektor()
|
||||
{
|
||||
|
||||
$query = $this->input->get('query');
|
||||
if (is_null($query))
|
||||
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
|
||||
|
||||
$stg_berechtigungen = $this->permissionlib->getSTG_isEntitledFor('lehre/reservierung');
|
||||
|
||||
if (isEmptyArray($stg_berechtigungen))
|
||||
$this->terminateWithSuccess([]);
|
||||
|
||||
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
|
||||
|
||||
$query_words = explode(' ', urldecode($query));
|
||||
|
||||
$this->MitarbeiterModel->addSelect('uid, person_id, vorname, nachname');
|
||||
$this->MitarbeiterModel->addJoin('public.tbl_benutzer', 'uid = mitarbeiter_uid');
|
||||
$this->MitarbeiterModel->addJoin('public.tbl_person', 'person_id');
|
||||
$this->MitarbeiterModel->db->where('public.tbl_benutzer.aktiv', true);
|
||||
$this->MitarbeiterModel->db->group_start();
|
||||
foreach ($query_words as $word)
|
||||
{
|
||||
$this->MitarbeiterModel->db->group_start();
|
||||
$this->MitarbeiterModel->db->where('tbl_person.vorname ILIKE', "%" . $word . "%");
|
||||
$this->MitarbeiterModel->db->or_where('tbl_person.nachname ILIKE', "%" . $word . "%");
|
||||
$this->MitarbeiterModel->db->or_where('uid ILIKE', "%" . $word . "%");
|
||||
$this->MitarbeiterModel->db->group_end();
|
||||
}
|
||||
$this->MitarbeiterModel->db->group_end();
|
||||
|
||||
$this->MitarbeiterModel->addOrder('nachname');
|
||||
$this->MitarbeiterModel->addOrder('vorname');
|
||||
$mitarbeiter = $this->MitarbeiterModel->load();
|
||||
if (isError($mitarbeiter))
|
||||
$this->terminateWithError($mitarbeiter);
|
||||
|
||||
$this->terminateWithSuccess(hasData($mitarbeiter) ? getData($mitarbeiter) : []);
|
||||
}
|
||||
|
||||
public function getReservableMap($ort_kurzbz = null)
|
||||
{
|
||||
$this->form_validation->set_rules('start_date', "StartDate", "required");
|
||||
$this->form_validation->set_rules('end_date', "EndDate", "required");
|
||||
|
||||
if (!$this->form_validation->run())
|
||||
$this->terminateWithValidationErrors($this->form_validation->error_array());
|
||||
|
||||
// storing the post parameter in local variables
|
||||
$start_date = $this->input->post('start_date', true);
|
||||
$end_date = $this->input->post('end_date', true);
|
||||
|
||||
$result = $this->stundenplanlib->getReservableMap($ort_kurzbz, $start_date, $end_date);
|
||||
|
||||
$this->terminateWithSuccess(array('reservierbarMap' => hasData($result) ? getData($result) : []));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -368,6 +368,32 @@ class StundenplanLib
|
||||
$item->gruppe = $gruppe_obj_array;
|
||||
$item->lektor = $lektor_obj_array;
|
||||
|
||||
$this->_ci->load->library('PermissionLib');
|
||||
$berechtigt_begrenzt = $this->_ci->permissionlib->isBerechtigt('lehre/reservierung:begrenzt', 'sui');
|
||||
|
||||
$now = time();
|
||||
$res_lektor_start = $this->jump_day($now, RES_TAGE_LEKTOR_MIN - 1);
|
||||
$res_lektor_ende = mktime(0, 0, 0, date('m', $now), date('d', $now) + RES_TAGE_LEKTOR_BIS, date('Y', $now));
|
||||
|
||||
$start_date = is_numeric($item->beginn) ? $item->beginn : strtotime($item->beginn);
|
||||
if (!date('w', $start_date)) {
|
||||
$start_date = $this->jump_day($start_date, 1);
|
||||
}
|
||||
|
||||
$start_date_str = date('Y-m-d', $start_date);
|
||||
$res_lektor_start_str = date('Y-m-d', $res_lektor_start);
|
||||
$res_lektor_ende_str = date('Y-m-d', $res_lektor_ende);
|
||||
|
||||
$show_delete = (($berechtigt_begrenzt && ($item->insertvon == getAuthUID() || in_array(getAuthUID(), $item->uids))) &&
|
||||
$start_date_str >= $res_lektor_start_str &&
|
||||
$start_date_str <= $res_lektor_ende_str
|
||||
);
|
||||
|
||||
if ($show_delete)
|
||||
$item->deletable = true;
|
||||
else
|
||||
$item->deletable = false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,6 +471,219 @@ class StundenplanLib
|
||||
return success($ferienEventsFlattened);
|
||||
}
|
||||
|
||||
public function addReservation($start, $end, $title, $beschreibung, $ort_kurzbz, $lektoren = null, $studiengang = null, $semester = null, $verband = null, $gruppe = null, $spezialgruppe = null)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
$this->_ci->load->model('ressource/Stunde_model', 'StundeModel');
|
||||
$this->_ci->load->model('ressource/Reservierung_model', 'ReservierungModel');
|
||||
$this->_ci->load->model('ressource/stundenplandev_model', 'StundenplandevModel');
|
||||
$this->_ci->load->model('ressource/stundenplan_model', 'StundenplanModel');
|
||||
$this->_ci->load->library('PermissionLib');
|
||||
|
||||
$startTime = new DateTime($start);
|
||||
$endTime = new DateTime($end);
|
||||
|
||||
$stunden = $this->_ci->StundeModel->loadWhere(array(
|
||||
'beginn <' => $endTime->format('H:i:s'),
|
||||
'ende >' => $startTime->format('H:i:s')
|
||||
));
|
||||
|
||||
if (!hasData($stunden))
|
||||
{
|
||||
return error("Keine Stunden vorhanden");
|
||||
}
|
||||
|
||||
$stunden = array_column(getData($stunden), 'stunde');
|
||||
|
||||
$this->_ci->StundenplandevModel->db->select('1');
|
||||
$this->_ci->StundenplandevModel->db->where('datum', $startTime->format('Y-m-d'));
|
||||
$this->_ci->StundenplandevModel->db->where('ort_kurzbz', $ort_kurzbz);
|
||||
$this->_ci->StundenplandevModel->db->where_in('stunde', $stunden);
|
||||
$stundenplandev_belegung = $this->_ci->StundenplandevModel->load();
|
||||
|
||||
$this->_ci->StundenplanModel->db->select('1');
|
||||
$this->_ci->StundenplanModel->db->where('ort_kurzbz', $ort_kurzbz);
|
||||
$this->_ci->StundenplanModel->db->where('datum', $startTime->format('Y-m-d'));
|
||||
$this->_ci->StundenplanModel->db->where_in('stunde', $stunden);
|
||||
$stundenplan_belegung = $this->_ci->StundenplanModel->load();
|
||||
|
||||
if ((hasData($stundenplandev_belegung) || hasData($stundenplan_belegung))
|
||||
&& !$this->_ci->permissionlib->isBerechtigt('lehre/reservierungAdvanced'))
|
||||
return error ('lvplan/bereitsReserviert');
|
||||
|
||||
$this->_ci->ReservierungModel->addSelect('stunde');
|
||||
$reservation_hours = $this->_ci->ReservierungModel->loadWhere(array('datum' => $startTime->format('Y-m-d'), 'ort_kurzbz' => $ort_kurzbz));
|
||||
|
||||
|
||||
if (isError($reservation_hours))
|
||||
return $reservation_hours;
|
||||
|
||||
$reservation_hours = hasData($reservation_hours) ? array_column(getData($reservation_hours), 'stunde') : array();
|
||||
|
||||
if (!empty(array_intersect($stunden, $reservation_hours))
|
||||
&& !$this->_ci->permissionlib->isBerechtigt('lehre/reservierungAdvanced'))
|
||||
return error("lvplan/bereitsReserviert");
|
||||
|
||||
|
||||
if (!empty($lektoren))
|
||||
{
|
||||
foreach ($lektoren as $lektor)
|
||||
{
|
||||
$insert = array('ort_kurzbz' => $ort_kurzbz,
|
||||
'datum' => $startTime->format('Y-m-d'),
|
||||
'titel' => $title,
|
||||
'studiengang_kz' => is_null($studiengang) ? 0 : $studiengang,
|
||||
'beschreibung' => $beschreibung,
|
||||
'insertvon' => getAuthUID(),
|
||||
'uid' => $lektor,
|
||||
'semester' => is_null($semester) ? null : $semester,
|
||||
'verband' => is_null($verband) ? null : $verband,
|
||||
'gruppe' => is_null($gruppe) ? null : $gruppe,
|
||||
'gruppe_kurzbz' => is_null($spezialgruppe) ? null : $spezialgruppe,
|
||||
);
|
||||
|
||||
foreach ($stunden as $stunde)
|
||||
{
|
||||
$insert['stunde'] = $stunde;
|
||||
$check_insert = $this->_ci->ReservierungModel->insert($insert);
|
||||
if (isError($check_insert))
|
||||
return $check_insert;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($stunden as $stunde)
|
||||
{
|
||||
$check_insert = $this->_ci->ReservierungModel->insert(array(
|
||||
'ort_kurzbz' => $ort_kurzbz,
|
||||
'uid' => getAuthUID(),
|
||||
'stunde' => $stunde,
|
||||
'datum' => $startTime->format('Y-m-d'),
|
||||
'titel' => $title,
|
||||
'studiengang_kz' => is_null($studiengang) ? 0 : $studiengang,
|
||||
'beschreibung' => $beschreibung,
|
||||
'insertvon' => getAuthUID()
|
||||
));
|
||||
if (isError($check_insert))
|
||||
return $check_insert;
|
||||
}
|
||||
}
|
||||
|
||||
return success("Erfolgreich");
|
||||
}
|
||||
|
||||
public function deleteReservation($reservierung_id)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
$this->_ci->load->model('ressource/Reservierung_model', 'ReservierungModel');
|
||||
$this->_ci->load->model('ressource/Stunde_model', 'StundeModel');
|
||||
$this->_ci->load->library('PermissionLib');
|
||||
|
||||
|
||||
$this->_ci->ReservierungModel->db->where_in('reservierung_id', $reservierung_id);
|
||||
$reservation = $this->_ci->ReservierungModel->load();
|
||||
if (isError($reservation))
|
||||
return $reservation;
|
||||
|
||||
if (!hasData($reservation))
|
||||
return error("Reservierungen nicht gefunden");
|
||||
|
||||
$reservations = getData($reservation);
|
||||
|
||||
$today = new DateTime();
|
||||
foreach ($reservations as $reservierung)
|
||||
{
|
||||
if ($today->format('Y-m-d') > $reservierung->datum)
|
||||
return error("Vergangene Reservierungen können nicht gelöscht werden");
|
||||
|
||||
if (($this->_ci->permissionlib->isBerechtigt('lehre/reservierung:begrenzt')) && ($reservierung->insertvon == getAuthUID() || $reservierung->uid === getAuthUID()))
|
||||
{
|
||||
$delete_result = $this->_ci->ReservierungModel->delete($reservierung->reservierung_id);
|
||||
|
||||
if (isError($delete_result))
|
||||
return $delete_result;
|
||||
}
|
||||
}
|
||||
return success("Erfolgreich");
|
||||
}
|
||||
|
||||
|
||||
public function getReservableMap($ort_kurzbz, $start_date, $end_date)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
$this->_ci->load->model('ressource/Ort_model', 'OrtModel');
|
||||
$this->_ci->load->library('PermissionLib');
|
||||
|
||||
$berechtigt_begrenzt = $this->_ci->permissionlib->isBerechtigt('lehre/reservierung:begrenzt', 'suid');
|
||||
$berechtigt_erweitert = $this->_ci->permissionlib->isBerechtigt('lehre/reservierung', 'suid');
|
||||
|
||||
$ort_data = $this->_ci->OrtModel->load($ort_kurzbz);
|
||||
if (isError($ort_data) || !hasData($ort_data))
|
||||
return [];
|
||||
|
||||
$ort_data = getData($ort_data)[0];
|
||||
|
||||
if (!$ort_data->reservieren)
|
||||
return [];
|
||||
|
||||
if (!$berechtigt_begrenzt && !$berechtigt_erweitert)
|
||||
return [];
|
||||
|
||||
$start_ts = is_numeric($start_date) ? (int)$start_date : strtotime($start_date);
|
||||
$end_ts = is_numeric($end_date) ? (int)$end_date : strtotime($end_date);
|
||||
|
||||
if (!$start_ts || !$end_ts)
|
||||
return [];
|
||||
|
||||
if ($end_ts < $start_ts)
|
||||
{
|
||||
$tmp = $start_ts;
|
||||
$start_ts = $end_ts;
|
||||
$end_ts = $tmp;
|
||||
}
|
||||
|
||||
$now = time();
|
||||
$tage_min = defined('RES_TAGE_LEKTOR_MIN') ? (int)RES_TAGE_LEKTOR_MIN : 0;
|
||||
$tage_bis = defined('RES_TAGE_LEKTOR_BIS') ? (int)RES_TAGE_LEKTOR_BIS : 0;
|
||||
|
||||
$datum_res_lektor_start = $this->jump_day($now, $tage_min - 1);
|
||||
$datum_res_lektor_ende = $this->jump_day($now, $tage_bis);
|
||||
|
||||
$start_ymd_allowed = date('Y-m-d', $datum_res_lektor_start);
|
||||
$end_ymd_allowed = date('Y-m-d', $datum_res_lektor_ende);
|
||||
|
||||
$result = [];
|
||||
|
||||
$current = strtotime(date('Y-m-d', $start_ts) . ' 00:00:00');
|
||||
$end_day = strtotime(date('Y-m-d', $end_ts) . ' 00:00:00');
|
||||
|
||||
while ($current <= $end_day)
|
||||
{
|
||||
$ymd = date('Y-m-d', $current);
|
||||
|
||||
if ((int)date('w', $current) === 0)
|
||||
{
|
||||
$result[$ymd] = false;
|
||||
$current = $this->jump_day($current, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[$ymd] = ($ymd >= $start_ymd_allowed && $ymd <= $end_ymd_allowed) ? true : false;
|
||||
|
||||
$current = $this->jump_day($current, 1);
|
||||
}
|
||||
|
||||
return success($result);
|
||||
}
|
||||
|
||||
private function jump_day($timestamp, $days)
|
||||
{
|
||||
$days = (int)$days;
|
||||
$prefix = ($days >= 0 ? '+' : '');
|
||||
return strtotime($prefix . $days . ' days', $timestamp);
|
||||
}
|
||||
|
||||
// start of the private functions ########################################################################################################
|
||||
|
||||
// function used to sort an array of studiensemester strings
|
||||
|
||||
@@ -50,11 +50,12 @@ class Reservierung_model extends DB_Model
|
||||
|
||||
$query_result= $this->execReadOnlyQuery("
|
||||
SELECT
|
||||
'reservierung' as type, beginn, ende, datum,
|
||||
DISTINCT(insertvon),
|
||||
'reservierung' as type, beginn, ende, datum, array_agg(DISTINCT reservierung_id) AS reservierung_id,
|
||||
COALESCE(titel, beschreibung) as topic,
|
||||
array_agg(DISTINCT mitarbeiter_kurzbz) as lektor,
|
||||
array_agg(DISTINCT (gruppe,verband,semester,studiengang_kz,gruppen_kuerzel)) as gruppe,
|
||||
|
||||
array_agg(DISTINCT(uid)) as uids,
|
||||
ort_kurzbz, 'FFFFFF' as farbe
|
||||
|
||||
FROM
|
||||
@@ -62,7 +63,7 @@ class Reservierung_model extends DB_Model
|
||||
". $subquery ."
|
||||
) AS subquery
|
||||
|
||||
GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung
|
||||
GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung, insertvon
|
||||
|
||||
ORDER BY datum, beginn
|
||||
", is_null($ort_kurzbz) ?[getAuthUID(), getAuthUID(),$start_date,$end_date]: [$ort_kurzbz, $start_date, $end_date]);
|
||||
|
||||
@@ -97,3 +97,17 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.fhc-calendar-empty-slot-plus {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
font-size: 18px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.part-body:hover .fhc-calendar-empty-slot-plus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
export default {
|
||||
getReservableMap(ort_kurzbz, start_date, end_date) {
|
||||
return {
|
||||
method: 'post',
|
||||
url: `/api/frontend/v1/calendar/RoomPlan/getReservableMap/${ort_kurzbz}`,
|
||||
params: { start_date, end_date }
|
||||
};
|
||||
},
|
||||
|
||||
getRoomCreationInfo() {
|
||||
return {
|
||||
method: 'get',
|
||||
url: '/api/frontend/v1/calendar/RoomPlan/getRoomCreationInfo'
|
||||
};
|
||||
},
|
||||
getGruppen(query) {
|
||||
return {
|
||||
method: 'get',
|
||||
url: `/api/frontend/v1/calendar/RoomPlan/getGruppen?query=${encodeURIComponent(query)}`
|
||||
};
|
||||
},
|
||||
getLektor(query) {
|
||||
return {
|
||||
method: 'get',
|
||||
url: `/api/frontend/v1/calendar/RoomPlan/getLektor?query=${encodeURIComponent(query)}`
|
||||
};
|
||||
},
|
||||
addRoomReservation(formData) {
|
||||
return {
|
||||
method: 'post',
|
||||
url: '/api/frontend/v1/calendar/RoomPlan/addRoomReservation',
|
||||
params: formData
|
||||
};
|
||||
},
|
||||
deleteRoomReservation(reservierung_id) {
|
||||
return {
|
||||
method: 'post',
|
||||
url: '/api/frontend/v1/calendar/RoomPlan/deleteRoomReservation',
|
||||
params: {
|
||||
reservierung_id: reservierung_id
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,10 @@ export default {
|
||||
return () => true;
|
||||
}),
|
||||
hasDragoverFunc: Vue.computed(() => this.onDragover),
|
||||
mode: Vue.computed(() => this.mode)
|
||||
mode: Vue.computed(() => this.mode),
|
||||
reservierbarMap: Vue.computed(() => this.reservierbarMap),
|
||||
isReservierbar: Vue.computed(() => this.isReservierbar),
|
||||
createContext: Vue.computed(() => this.createContext)
|
||||
};
|
||||
},
|
||||
props: {
|
||||
@@ -97,7 +100,13 @@ export default {
|
||||
draggableEvents: [Boolean, Array, Function],
|
||||
dropableEvents: [Boolean, Array, Function],
|
||||
onDragover: Function,
|
||||
onDrop: Function
|
||||
onDrop: Function,
|
||||
isReservierbar: Boolean,
|
||||
createContext: Object,
|
||||
reservierbarMap: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
},
|
||||
emits: [
|
||||
"click:next",
|
||||
@@ -105,11 +114,13 @@ export default {
|
||||
"click:mode",
|
||||
"click:event",
|
||||
"click:day",
|
||||
"click:slot",
|
||||
"click:week",
|
||||
"update:date",
|
||||
"update:mode",
|
||||
"update:range",
|
||||
"drop"
|
||||
"drop",
|
||||
"create-event"
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -2,6 +2,7 @@ import GridLine from './Grid/Line.js';
|
||||
import GridLineEvent from './Grid/Line/Event.js';
|
||||
|
||||
import CalDnd from '../../../directives/Calendar/DragAndDrop.js';
|
||||
import CalClick from '../../../directives/Calendar/Click.js';
|
||||
|
||||
export default {
|
||||
name: "CalendarGrid",
|
||||
@@ -10,12 +11,15 @@ export default {
|
||||
GridLineEvent
|
||||
},
|
||||
directives: {
|
||||
CalDnd
|
||||
CalDnd,
|
||||
CalClick
|
||||
},
|
||||
inject: {
|
||||
originalEvents: "events",
|
||||
originalBackgrounds: "backgrounds",
|
||||
dropAllowed: "dropAllowed"
|
||||
dropAllowed: "dropAllowed",
|
||||
timezone: "timezone",
|
||||
reservierbar: "isReservierbar"
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
@@ -308,6 +312,20 @@ export default {
|
||||
} else {
|
||||
this.$refs.scroller.scrollTo(0, 0);
|
||||
}
|
||||
},
|
||||
isFreeSlot(date, part, dayEvents) {
|
||||
const pastEnd = luxon.DateTime.now().setZone(this.timezone);
|
||||
|
||||
const start = date.plus(part.start || part);
|
||||
const end = date.plus(part.end || part.plus({ hours: 1 }));
|
||||
|
||||
if (start < pastEnd)
|
||||
return false;
|
||||
|
||||
if (!dayEvents || !dayEvents.length)
|
||||
return true;
|
||||
|
||||
return !dayEvents.some(ev => ev.start < end && ev.end > start);
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
@@ -400,6 +418,18 @@ export default {
|
||||
:style="'grid-' + axisCol + ':' + (1+index) + ';grid-' + axisRow + ':ps_' + i + '/pe_' + i"
|
||||
>
|
||||
<slot name="part-body" v-bind="{ index, part }" />
|
||||
|
||||
<div
|
||||
v-if="isFreeSlot(date, part, eventsNormal[index]) && reservierbar"
|
||||
class="fhc-calendar-empty-slot"
|
||||
style="position:absolute; inset:0; z-index:1"
|
||||
v-cal-click:slot="{ date, part }"
|
||||
>
|
||||
<div class="fhc-calendar-empty-slot-plus">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="snapToGrid && dragging"
|
||||
style="position:absolute;inset:0;z-index:1"
|
||||
|
||||
@@ -32,15 +32,38 @@ export default {
|
||||
getPromiseFunc: {
|
||||
type: Function,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
reservierbar: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
createContext: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
},
|
||||
emits: [
|
||||
"update:date",
|
||||
"update:mode",
|
||||
"update:range"
|
||||
"update:range",
|
||||
"create-event",
|
||||
"delete-event",
|
||||
'update:reservierbarMap'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
isReservierbar: Vue.computed(() => {
|
||||
if (!this.reservierbar)
|
||||
return false;
|
||||
|
||||
if (!this.reservierbarMap)
|
||||
return false;
|
||||
|
||||
if (typeof this.reservierbarMap === 'object')
|
||||
return Object.keys(this.reservierbarMap).length > 0;
|
||||
|
||||
return false;
|
||||
}),
|
||||
modes: {
|
||||
day: Vue.markRaw(ModeDay),
|
||||
week: Vue.markRaw(ModeWeek),
|
||||
@@ -88,21 +111,33 @@ export default {
|
||||
updateRange(rangeInterval) {
|
||||
this.rangeInterval = rangeInterval;
|
||||
this.$emit('update:range', rangeInterval);
|
||||
},
|
||||
closeModal() {
|
||||
this.$refs.calendar.hideEventModal();
|
||||
},
|
||||
resetEventLoader() {
|
||||
this.reset();
|
||||
}
|
||||
},
|
||||
setup(props, context) {
|
||||
const rangeInterval = Vue.ref(null);
|
||||
|
||||
const { events, lv } = useEventLoader(rangeInterval, props.getPromiseFunc);
|
||||
const { events, lv, reservierbarMap, reset } = useEventLoader(rangeInterval, props.getPromiseFunc);
|
||||
|
||||
Vue.watch(lv, newValue => {
|
||||
context.emit('update:lv', newValue);
|
||||
});
|
||||
|
||||
Vue.watch(reservierbarMap, newVal => {
|
||||
context.emit('update:reservierbarMap', newVal);
|
||||
});
|
||||
|
||||
return {
|
||||
rangeInterval,
|
||||
events,
|
||||
lv
|
||||
lv,
|
||||
reservierbarMap,
|
||||
reset,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
@@ -129,6 +164,9 @@ export default {
|
||||
:events="events || []"
|
||||
:backgrounds="backgrounds"
|
||||
:time-grid="teachingunits"
|
||||
:reservierbar-map="reservierbarMap"
|
||||
:isReservierbar="isReservierbar"
|
||||
:create-context="createContext"
|
||||
show-btns
|
||||
@update:date="(newDate, newMode) => $emit('update:date', newDate, newMode)"
|
||||
@update:mode="(newMode, newDate) => $emit('update:mode', newMode, newDate)"
|
||||
@@ -144,6 +182,7 @@ export default {
|
||||
v-if="mode == 'event'"
|
||||
:is="renderers[event.type]?.modalContent"
|
||||
:event="event"
|
||||
@create-event="(event) => $emit('create-event', event)"
|
||||
></component>
|
||||
<component
|
||||
v-else-if="mode == 'eventheader'"
|
||||
@@ -154,6 +193,7 @@ export default {
|
||||
v-else
|
||||
:is="renderers[event.type]?.calendarEvent"
|
||||
:event="event"
|
||||
@delete-event="(event) => $emit('delete-event', event)"
|
||||
></component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -16,7 +16,19 @@ export default {
|
||||
inject: {
|
||||
timeGrid: "timeGrid",
|
||||
originalEvents: "events",
|
||||
timezone: "timezone"
|
||||
timezone: "timezone",
|
||||
reservierbar: {
|
||||
from: "isReservierbar",
|
||||
default: false
|
||||
},
|
||||
reservierbarMap: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
createContext: {
|
||||
from: 'createContext',
|
||||
default: () => {}
|
||||
},
|
||||
},
|
||||
props: {
|
||||
day: {
|
||||
@@ -103,6 +115,27 @@ export default {
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (evt.detail.source == 'slot')
|
||||
{
|
||||
if (!this.reservierbar)
|
||||
return;
|
||||
|
||||
const { date, part } = evt.detail.value || {};
|
||||
if (!date)
|
||||
return;
|
||||
let reservierbar = this.reservierbarMap?.[date.toISODate()] === true;
|
||||
if (!reservierbar)
|
||||
return;
|
||||
|
||||
this.$emit('requestModalOpen', {
|
||||
event: {
|
||||
type: this.createContext?.scope ?? 'slot',
|
||||
start: date.plus(part.start || part),
|
||||
end: date.plus(part.end || part.plus({ hours: 1 })),
|
||||
createContext: this.createContext
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
|
||||
@@ -7,6 +7,14 @@ export default {
|
||||
BaseSlider,
|
||||
WeekView
|
||||
},
|
||||
inject: {
|
||||
reservierbar: "isReservierbar",
|
||||
reservierbarMap: "reservierbarMap",
|
||||
createContext: {
|
||||
from: 'createContext',
|
||||
default: () => {}
|
||||
},
|
||||
},
|
||||
props: {
|
||||
currentDate: {
|
||||
type: luxon.DateTime,
|
||||
@@ -83,14 +91,33 @@ export default {
|
||||
},
|
||||
handleClickDefaults(evt) {
|
||||
switch (evt.detail.source) {
|
||||
case 'day':
|
||||
// default: Set current-date
|
||||
this.$emit('update:currentDate', evt.detail.value);
|
||||
break;
|
||||
case 'event':
|
||||
// default: Request Modal
|
||||
this.$emit('requestModalOpen', { event: evt.detail.value });
|
||||
break;
|
||||
case 'day':
|
||||
// default: Set current-date
|
||||
this.$emit('update:currentDate', evt.detail.value);
|
||||
break;
|
||||
case 'event':
|
||||
// default: Request Modal
|
||||
this.$emit('requestModalOpen', { event: evt.detail.value });
|
||||
break;
|
||||
case 'slot':
|
||||
{
|
||||
const { date, part } = evt.detail.value || {};
|
||||
if (!date)
|
||||
return;
|
||||
let reservierbar = this.reservierbarMap?.[date.toISODate()] === true;
|
||||
if (!reservierbar)
|
||||
return;
|
||||
|
||||
this.$emit('requestModalOpen', {
|
||||
event: {
|
||||
type: this.createContext?.scope ?? 'slot',
|
||||
start: date.plus(part.start || part),
|
||||
end: date.plus(part.end || part.plus({ hours: 1 })),
|
||||
createContext: this.createContext
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import FhcCalendar from "../../Calendar/LvPlan.js";
|
||||
|
||||
import ApiLvPlan from '../../../api/factory/lvPlan.js';
|
||||
import ApiRoomPlan from '../../../api/factory/calendar/roomPlan.js';
|
||||
|
||||
export const DEFAULT_MODE_RAUMINFO = 'Week'
|
||||
|
||||
@@ -21,6 +22,36 @@ export default {
|
||||
return this.propsViewData?.mode || DEFAULT_MODE_RAUMINFO;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filteredGroups: [],
|
||||
abortController: null,
|
||||
createContext: {
|
||||
scope: 'slot_room',
|
||||
show_all_fields: false,
|
||||
room_create_information: {
|
||||
semester: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
verband: ['A', 'B', 'C', 'D', 'E', 'F', 'V'],
|
||||
gruppe: [1, 2, 3, 4],
|
||||
studiengaenge: [],
|
||||
searchGroup: this.searchGroup,
|
||||
searchLektor: this.searchLektor,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
||||
this.$api.call(ApiRoomPlan.getRoomCreationInfo())
|
||||
.then(result => result.data)
|
||||
.then(result => {
|
||||
if (result.berechtigt)
|
||||
{
|
||||
this.createContext.room_create_information.studiengaenge = result.studiengaenge
|
||||
}
|
||||
this.createContext.show_all_fields = result.berechtigt;
|
||||
});
|
||||
},
|
||||
methods:{
|
||||
handleChangeDate(day, newMode) {
|
||||
return this.handleChangeMode(newMode, day);
|
||||
@@ -38,8 +69,81 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
async handleCreateEvent(event)
|
||||
{
|
||||
event.ort_kurzbz = this.propsViewData.ort_kurzbz;
|
||||
this.$api.call(ApiRoomPlan.addRoomReservation(event));
|
||||
this.$refs.calendar.resetEventLoader();
|
||||
this.$refs.calendar.closeModal();
|
||||
},
|
||||
async handleDeleteEvent(event)
|
||||
{
|
||||
if (event.type !== 'reservierung')
|
||||
return;
|
||||
|
||||
if (luxon.DateTime.fromISO(`${event.datum}T${event.beginn}`) < luxon.DateTime.now())
|
||||
return;
|
||||
|
||||
this.$api.call(ApiRoomPlan.deleteRoomReservation(event.reservierung_id));
|
||||
|
||||
this.$refs.calendar.reset();
|
||||
|
||||
},
|
||||
async searchGroup(event)
|
||||
{
|
||||
const query = event.query.trim();
|
||||
|
||||
if (query.length < 2)
|
||||
return [];
|
||||
|
||||
if (this.abortController)
|
||||
this.abortController.abort();
|
||||
|
||||
this.abortController = new AbortController();
|
||||
const signal = this.abortController.signal;
|
||||
|
||||
return this.$api.call(ApiRoomPlan.getGruppen(query), { signal })
|
||||
.then(result => {
|
||||
return result.data.map(gruppe => ({
|
||||
label: gruppe.bezeichnung
|
||||
? `${gruppe.gruppe_kurzbz.trim()} (${gruppe.bezeichnung})`
|
||||
: gruppe.gruppe_kurzbz.trim(),
|
||||
gid: gruppe.gid,
|
||||
gruppe_kurzbz: gruppe.gruppe_kurzbz.trim(),
|
||||
lehrverband: gruppe.lehrverband,
|
||||
})
|
||||
);
|
||||
})
|
||||
.catch((e)=> {
|
||||
this.$fhcAlert.handleSystemError(e)
|
||||
return []
|
||||
})
|
||||
},
|
||||
async searchLektor(event)
|
||||
{
|
||||
const query = event.query.trim();
|
||||
|
||||
if (query.length < 2)
|
||||
return [];
|
||||
|
||||
if (this.abortController)
|
||||
this.abortController.abort();
|
||||
|
||||
this.abortController = new AbortController();
|
||||
const signal = this.abortController.signal;
|
||||
|
||||
return this.$api.call(ApiRoomPlan.getLektor(query), { signal })
|
||||
.then(result => {
|
||||
return result.data.map(lektor => ({
|
||||
label: `${lektor.nachname} ${lektor.vorname} (${lektor.uid})`,
|
||||
uid: lektor.uid
|
||||
})
|
||||
)})
|
||||
.catch(this.$fhcAlert.handleSystemError)
|
||||
},
|
||||
getPromiseFunc(start, end) {
|
||||
return [
|
||||
this.$api.call(ApiRoomPlan.getReservableMap(this.propsViewData.ort_kurzbz, start.toISODate(), end.toISODate())),
|
||||
this.$api.call(ApiLvPlan.getRoomInfo(this.propsViewData.ort_kurzbz, start.toISODate(), end.toISODate())),
|
||||
this.$api.call(ApiLvPlan.getOrtReservierungen(this.propsViewData.ort_kurzbz, start.toISODate(), end.toISODate()))
|
||||
];
|
||||
@@ -55,8 +159,12 @@ export default {
|
||||
:get-promise-func="getPromiseFunc"
|
||||
:date="currentDay"
|
||||
:mode="currentMode"
|
||||
:reservierbar="true"
|
||||
:create-context="createContext"
|
||||
@update:date="handleChangeDate"
|
||||
@update:mode="handleChangeMode"
|
||||
@create-event="handleCreateEvent"
|
||||
@delete-event="handleDeleteEvent"
|
||||
class="responsive-calendar"
|
||||
></fhc-calendar>
|
||||
</div>`
|
||||
|
||||
@@ -5,6 +5,10 @@ export default {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
inject: {
|
||||
mode: "mode",
|
||||
},
|
||||
emits: ['delete-event'],
|
||||
computed: {
|
||||
tooltipString() {
|
||||
const tooltipArray = [];
|
||||
@@ -50,12 +54,20 @@ export default {
|
||||
return luxon.Duration
|
||||
.fromISOTime(this.event.ende)
|
||||
.toISOTime({ suppressSeconds: true });
|
||||
},
|
||||
isFutureEvent() {
|
||||
const eventStart = luxon.DateTime.fromISO(`${this.event.datum}T${this.event.beginn}`);
|
||||
return eventStart > luxon.DateTime.now();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleDelete() {
|
||||
this.$emit('delete-event', this.event);
|
||||
}
|
||||
},
|
||||
template: /* html */`
|
||||
<div
|
||||
class="cis-renderer-reservierungen-calendar-event calendar-event-default h-100 w-100 p-1"
|
||||
>
|
||||
class="cis-renderer-reservierungen-calendar-event calendar-event-default h-100 w-100 p-1 position-relative">
|
||||
<div
|
||||
v-if="!event.allDayEvent && event?.beginn && event?.ende"
|
||||
class="event-time d-grid h-100"
|
||||
@@ -64,6 +76,15 @@ export default {
|
||||
<span>{{ end }}</span>
|
||||
</div>
|
||||
<div class="event-text" v-tooltip="tooltipString">
|
||||
<button
|
||||
v-if="isFutureEvent && event.type === 'reservierung' && event.deletable && mode !== 'Month'"
|
||||
class="btn btn-sm btn-outline-secondary position-absolute top-0 end-0 m-1"
|
||||
title="Löschen"
|
||||
@click.stop="handleDelete"
|
||||
>
|
||||
<i class="fa-solid fa-xmark"></i>
|
||||
</button>
|
||||
|
||||
<span class="event-topic">{{ event.topic }}</span>
|
||||
<span
|
||||
v-for="lektor in event.lektor.slice(0, 3)"
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
import FormInput from '../../../Form/Input.js';
|
||||
|
||||
export default {
|
||||
emits: ['create-event'],
|
||||
components: {
|
||||
FormInput
|
||||
},
|
||||
inject: {
|
||||
timeGrid: "timeGrid",
|
||||
},
|
||||
props: {
|
||||
event: { type: Object, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
||||
data: {},
|
||||
filteredGroups: [],
|
||||
filteredLektoren: [],
|
||||
selectedStart: null,
|
||||
selectedEnd: null,
|
||||
title: null,
|
||||
beschreibung: null,
|
||||
studiengang: null,
|
||||
semester: null,
|
||||
verband: null,
|
||||
gruppe: null,
|
||||
selectedGruppe: null,
|
||||
selectedLektoren: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.syncFromEvent(this.event);
|
||||
},
|
||||
|
||||
watch: {
|
||||
event: {
|
||||
handler(newEvent) {
|
||||
this.syncFromEvent(newEvent);
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
syncFromEvent(newEvent) {
|
||||
if (!newEvent) return;
|
||||
|
||||
const startTime = newEvent.start?.setZone?.(this.timezone)?.toFormat?.('HH:mm:ss');
|
||||
const endTime = newEvent.end?.setZone?.(this.timezone)?.toFormat?.('HH:mm:ss');
|
||||
|
||||
this.selectedStart = this.timeGrid.find(t => t.start === startTime)?.start || this.timeGrid[0]?.start;
|
||||
this.selectedEnd = this.timeGrid.find(t => t.end === endTime)?.end || this.timeGrid.at(-1)?.end;
|
||||
},
|
||||
saveEvent() {
|
||||
const [startHour, startMinute] = this.selectedStart.split(':').map(Number);
|
||||
const [endHour, endMinute] = this.selectedEnd.split(':').map(Number);
|
||||
const selectedStart = this.event.start.startOf('day').set({ hour: startHour, minute: startMinute });
|
||||
const selectedEnd = this.event.start.startOf('day').set({ hour: endHour, minute: endMinute });
|
||||
|
||||
const lektoren_uid = this.selectedLektoren.map(m => m.uid)
|
||||
|
||||
const spezialgruppe = this.selectedGruppe?.gruppe_kurzbz;
|
||||
|
||||
const event = {
|
||||
selectedStart: selectedStart,
|
||||
selectedEnd: selectedEnd,
|
||||
title: this.title,
|
||||
beschreibung: this.beschreibung,
|
||||
studiengang: this.studiengang,
|
||||
semester: this.semester,
|
||||
verband: this.verband,
|
||||
gruppe: this.gruppe,
|
||||
spezialgruppe: spezialgruppe,
|
||||
lektoren: lektoren_uid
|
||||
}
|
||||
|
||||
this.$emit('create-event', event);
|
||||
},
|
||||
async searchGroup(event) {
|
||||
this.filteredGroups = await this.event.createContext.room_create_information.searchGroup(event)
|
||||
},
|
||||
async searchLektor(event) {
|
||||
this.filteredLektoren = await this.event.createContext.room_create_information.searchLektor(event)
|
||||
},
|
||||
capitalize(text)
|
||||
{
|
||||
if (!text) return ''
|
||||
return text.charAt(0).toUpperCase() + text.slice(1)
|
||||
}
|
||||
},
|
||||
|
||||
template: `
|
||||
<div class="p-3">
|
||||
<h5 class="mb-3">Neue Reservierung</h5>
|
||||
<div class="row">
|
||||
<form-input
|
||||
:label="capitalize($p.t('ui', 'dateFrom'))"
|
||||
type="select"
|
||||
container-class="col-3"
|
||||
v-model="selectedStart"
|
||||
name="selectedStart"
|
||||
>
|
||||
<option
|
||||
v-for="slot in timeGrid"
|
||||
:value="slot.start"
|
||||
:key="slot.id"
|
||||
>
|
||||
{{ slot.start }}
|
||||
</option>
|
||||
</form-input>
|
||||
|
||||
<form-input
|
||||
:label="capitalize($p.t('ui', 'dateTo'))"
|
||||
type="select"
|
||||
container-class="col-3"
|
||||
v-model="selectedEnd"
|
||||
name="selectedEnd"
|
||||
>
|
||||
<option
|
||||
v-for="slot in timeGrid"
|
||||
:value="slot.end"
|
||||
:key="slot.id"
|
||||
>
|
||||
{{ slot.end }}
|
||||
</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row">
|
||||
<form-input
|
||||
:label="capitalize($p.t('global', 'titel'))"
|
||||
type="text"
|
||||
container-class="col-3"
|
||||
v-model="title"
|
||||
name="title"
|
||||
/>
|
||||
|
||||
<form-input
|
||||
:label="capitalize($p.t('global', 'beschreibung'))"
|
||||
type="text"
|
||||
container-class="col-4"
|
||||
v-model="beschreibung"
|
||||
name="Beschreibung"
|
||||
/>
|
||||
<form-input
|
||||
v-if="event.createContext.show_all_fields"
|
||||
type="autocomplete"
|
||||
:minLength="2"
|
||||
:label="capitalize($p.t('lehre', 'lektor'))"
|
||||
:suggestions="filteredLektoren"
|
||||
placeholder="Mitarbeiter hinzufügen"
|
||||
field="label"
|
||||
v-model="selectedLektoren"
|
||||
container-class="col-5"
|
||||
@complete="searchLektor"
|
||||
multiple
|
||||
name="lektorautocomplete"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
<div v-if="event.createContext.show_all_fields">
|
||||
<div class="row">
|
||||
<form-input
|
||||
:label="capitalize($p.t('lehre', 'studiengang'))"
|
||||
type="select"
|
||||
container-class="col-3"
|
||||
v-model="studiengang"
|
||||
name="studiengang"
|
||||
>
|
||||
<option
|
||||
v-for="studiengang in event.createContext.room_create_information.studiengaenge"
|
||||
:value="studiengang.studiengang_kz"
|
||||
:key="studiengang.studiengang_kz"
|
||||
>
|
||||
{{ studiengang.kuerzel }} ({{ studiengang.kurzbzlang }})
|
||||
</option>
|
||||
</form-input>
|
||||
|
||||
<form-input
|
||||
:label="capitalize($p.t('lehre', 'semester'))"
|
||||
type="select"
|
||||
container-class="col-2"
|
||||
v-model="semester"
|
||||
name="semester"
|
||||
>
|
||||
<option
|
||||
v-for="semester in event.createContext.room_create_information.semester"
|
||||
:value="semester"
|
||||
:key="semester"
|
||||
>
|
||||
{{ semester }}
|
||||
</option>
|
||||
</form-input>
|
||||
|
||||
<form-input
|
||||
:label="capitalize($p.t('lehre', 'verband'))"
|
||||
type="select"
|
||||
container-class="col-2"
|
||||
v-model="verband"
|
||||
name="semester"
|
||||
>
|
||||
<option
|
||||
v-for="verband in event.createContext.room_create_information.verband"
|
||||
:value="verband"
|
||||
:key="verband"
|
||||
>
|
||||
{{ verband }}
|
||||
</option>
|
||||
</form-input>
|
||||
|
||||
<form-input
|
||||
:label="capitalize($p.t('lehre', 'gruppe'))"
|
||||
type="select"
|
||||
container-class="col-2"
|
||||
v-model="gruppe"
|
||||
name="gruppe"
|
||||
>
|
||||
<option
|
||||
v-for="gruppe in event.createContext.room_create_information.gruppe"
|
||||
:value="gruppe"
|
||||
:key="gruppe"
|
||||
>
|
||||
{{ gruppe }}
|
||||
</option>
|
||||
</form-input>
|
||||
|
||||
<form-input
|
||||
:label="capitalize($p.t('lehre', 'special_group'))"
|
||||
type="autocomplete"
|
||||
:suggestions="filteredGroups"
|
||||
:placeholder="$p.t('lehre', 'addGroup')"
|
||||
field="label"
|
||||
:minLength="2"
|
||||
container-class="col-5"
|
||||
v-model="selectedGruppe"
|
||||
name="gruppeautocomplete"
|
||||
@complete="searchGroup"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<button class="btn btn-primary mt-3" @click="saveEvent">Speichern</button>
|
||||
</div>
|
||||
`
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export default {
|
||||
template:`<div>{{ $p.t('lehre','new_reservierung') }}</div>`
|
||||
}
|
||||
@@ -7,6 +7,12 @@ export function useEventLoader(rangeInterval, getPromiseFunc) {
|
||||
const allEvents = Vue.computed(() => events.value.concat(loadingEvents.value));
|
||||
const lv = Vue.ref(null);
|
||||
const eventsLoaded = [];
|
||||
const reservierbarMap = Vue.ref({});
|
||||
|
||||
const mergeReservierbarMap = (incoming) => {
|
||||
if (!incoming) return;
|
||||
reservierbarMap.value = { ...reservierbarMap.value, ...incoming };
|
||||
};
|
||||
|
||||
const mergePromiseArr = (n, o) => {
|
||||
if (Array.isArray(n))
|
||||
@@ -111,7 +117,7 @@ export function useEventLoader(rangeInterval, getPromiseFunc) {
|
||||
return mergePromiseArr(getPromiseFunc(start, end), result);
|
||||
};
|
||||
|
||||
Vue.watchEffect(() => {
|
||||
const reload = () => {
|
||||
const range = Vue.toValue(rangeInterval);
|
||||
if (!(range instanceof luxon.Interval))
|
||||
return;
|
||||
@@ -128,11 +134,23 @@ export function useEventLoader(rangeInterval, getPromiseFunc) {
|
||||
lv.value = res.value.meta.lv;
|
||||
|
||||
events.value = events.value.concat(res.value.data);
|
||||
mergeReservierbarMap(res.value.data?.reservierbarMap);
|
||||
loadingEvents.value = [];
|
||||
}
|
||||
})
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
return { events: allEvents, lv }
|
||||
Vue.watchEffect(reload);
|
||||
|
||||
const reset = () => {
|
||||
loading_id = 0;
|
||||
events.value = [];
|
||||
loadingEvents.value = [];
|
||||
reservierbarMap.value = {};
|
||||
eventsLoaded.splice(0, eventsLoaded.length);
|
||||
reload();
|
||||
}
|
||||
|
||||
return { events: allEvents, lv, reservierbarMap, reset }
|
||||
}
|
||||
@@ -46226,6 +46226,46 @@ array(
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'lehre',
|
||||
'phrase' => 'special_group',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Spezialgruppe',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Special group',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'lehre',
|
||||
'phrase' => 'new_reservierung',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Neue Reservierung',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'New reservation',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
// feature-55614 end
|
||||
array(
|
||||
'app' => 'softwarebereitstellung',
|
||||
|
||||
Reference in New Issue
Block a user