diff --git a/application/controllers/api/frontend/v1/Ort.php b/application/controllers/api/frontend/v1/Ort.php index 0d8074fa9..a208e4c0d 100644 --- a/application/controllers/api/frontend/v1/Ort.php +++ b/application/controllers/api/frontend/v1/Ort.php @@ -36,7 +36,8 @@ class Ort extends FHCAPI_Controller 'ContentID' => self::PERM_LOGGED, 'getOrtKurzbzContent' => self::PERM_LOGGED, 'getRooms' => self::PERM_LOGGED, - 'getTypes' => self::PERM_LOGGED + 'getTypes' => self::PERM_LOGGED, + 'getRoomInfo' => self::PERM_LOGGED ]); $this->load->model('ressource/Ort_model', 'OrtModel'); @@ -74,6 +75,8 @@ class Ort extends FHCAPI_Controller $vonStunde = getData($this->StundeModel->getStundeForTime($von))[0]->stunde; $bisStunde = getData($this->StundeModel->getStundeForTime($bis))[0]->stunde; + $this->load->model('ressource/Ort_model', 'OrtModel'); + $params = array(); $qry = "SELECT DISTINCT tbl_ort.* FROM public.tbl_ort JOIN public.tbl_ortraumtyp USING(ort_kurzbz) @@ -174,5 +177,16 @@ class Ort extends FHCAPI_Controller $this->terminateWithSuccess($content); } + + public function getRoomInfo() + { + $ortKurzbz = $this->input->get("ortKurzbz"); + + $roomResult = $this->OrtModel->getRoomInfo($ortKurzbz); + $roomData = $this->getDataOrTerminateWithError($roomResult); + $room = count($roomData) ? $roomData[0] : null; + + $this->terminateWithSuccess($room); + } } diff --git a/application/controllers/api/frontend/v1/calendar/RoomPlan.php b/application/controllers/api/frontend/v1/calendar/RoomPlan.php index 489d83b53..522478f35 100644 --- a/application/controllers/api/frontend/v1/calendar/RoomPlan.php +++ b/application/controllers/api/frontend/v1/calendar/RoomPlan.php @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -if (! defined('BASEPATH')) exit('No direct script access allowed'); +if (!defined('BASEPATH')) + exit('No direct script access allowed'); class RoomPlan extends FHCAPI_Controller { @@ -33,6 +34,7 @@ class RoomPlan extends FHCAPI_Controller 'getGruppen' => self::PERM_LOGGED, 'getLektor' => self::PERM_LOGGED, 'getReservableMap' => self::PERM_LOGGED, + 'getGroupOptions' => self::PERM_LOGGED, ]); $this->load->library('LogLib'); @@ -149,16 +151,14 @@ class RoomPlan extends FHCAPI_Controller $this->GruppeModel->addOrder('gruppe_kurzbz'); $this->GruppeModel->db->group_start(); - foreach ($query_words as $word) - { + 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)) - { + if (is_numeric($word)) { $this->GruppeModel->db->or_where('studiengang_kz', $word); } $this->GruppeModel->db->group_end(); @@ -193,8 +193,7 @@ class RoomPlan extends FHCAPI_Controller $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) - { + 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 . "%"); @@ -229,4 +228,49 @@ class RoomPlan extends FHCAPI_Controller $this->terminateWithSuccess(array('reservierbarMap' => hasData($result) ? getData($result) : [])); } + public function getGroupOptions() + { + $studiengangKz = $this->input->get('studiengangKz'); + + $this->load->model('organisation/Lehrverband_model', 'LehrverbandModel'); + $this->LehrverbandModel->addSelect('semester, verband, gruppe'); + $this->LehrverbandModel->db->where('aktiv', true); + $this->LehrverbandModel->db->where('studiengang_kz', $studiengangKz); + $lehrverbandResult = $this->LehrverbandModel->load(); + + if (isError($lehrverbandResult)) + $this->terminateWithError($lehrverbandResult); + + $lehrverbanden = hasData($lehrverbandResult) ? getData($lehrverbandResult) : []; + + $grouppedValues = []; + foreach ($lehrverbanden as $lehrverband) { + if (isset($grouppedValues[$lehrverband->semester]) && $lehrverband->verband !== ' ') { + $grouppedValues[$lehrverband->semester][] = $lehrverband; + } else if (!isset($grouppedValues[$lehrverband->semester]) && $lehrverband->verband !== ' ') { + $grouppedValues[$lehrverband->semester] = [$lehrverband]; + } else if (!isset($grouppedValues[$lehrverband->semester])) { + $grouppedValues[$lehrverband->semester] = []; + } + } + $grouppedValues = array_map( + function ($semester) { + $verbandGruppen = []; + foreach ($semester as $lehrverband) { + if (isset($verbandGruppen[$lehrverband->verband]) && $lehrverband->gruppe !== ' ') { + $verbandGruppen[$lehrverband->verband][] = $lehrverband->gruppe; + } else if (!isset($verbandGruppen[$lehrverband->verband]) && $lehrverband->gruppe !== ' ') { + $verbandGruppen[$lehrverband->verband] = [$lehrverband->gruppe]; + } else if (!isset($verbandGruppen[$lehrverband->verband])) { + $verbandGruppen[$lehrverband->verband] = []; + } + } + return $verbandGruppen; + }, + $grouppedValues + ); + + $this->terminateWithSuccess($grouppedValues); + } + } diff --git a/application/models/ressource/Ort_model.php b/application/models/ressource/Ort_model.php index de0c8e331..9e2f286d0 100644 --- a/application/models/ressource/Ort_model.php +++ b/application/models/ressource/Ort_model.php @@ -12,6 +12,12 @@ class Ort_model extends DB_Model $this->pk = "ort_kurzbz"; } + public function getRoomInfo($ortKurzbz) + { + $this->addSelect("ort_kurzbz, ausstattung"); + return $this->OrtModel->loadWhere(array("ort_kurzbz" => $ortKurzbz)); + } + public function getAll($raumtyp_kurzbz) { $this->addOrder("ort_kurzbz"); diff --git a/application/models/ressource/Reservierung_model.php b/application/models/ressource/Reservierung_model.php index d1c4e683b..bedeefed8 100644 --- a/application/models/ressource/Reservierung_model.php +++ b/application/models/ressource/Reservierung_model.php @@ -24,7 +24,7 @@ class Reservierung_model extends DB_Model $lvplan_reservierungen_query = "SELECT r.* , stund.beginn, stund.ende, CASE WHEN r.gruppe_kurzbz IS NOT NULL THEN r.gruppe_kurzbz - ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(r.semester AS varchar),'/'),COALESCE(CAST(r.verband AS varchar),'/')) + ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(r.semester AS varchar),'/'),COALESCE(CAST(r.verband AS varchar),'/'),COALESCE(CAST(res.gruppe AS varchar),'/')) END as gruppen_kuerzel FROM campus.vw_reservierung r JOIN public.tbl_studiengang studg ON studg.studiengang_kz=r.studiengang_kz @@ -39,7 +39,7 @@ class Reservierung_model extends DB_Model $raum_reservierungen_query = "SELECT res.*, beginn, ende, CASE WHEN res.gruppe_kurzbz IS NOT NULL THEN res.gruppe_kurzbz - ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(res.semester AS varchar),'/'),COALESCE(CAST(res.verband AS varchar),'/')) + ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(res.semester AS varchar),'/'),COALESCE(CAST(res.verband AS varchar),'/'),COALESCE(CAST(res.gruppe AS varchar),'/')) END as gruppen_kuerzel FROM campus.vw_reservierung res JOIN public.tbl_studiengang studg ON studg.studiengang_kz=res.studiengang_kz @@ -83,7 +83,7 @@ class Reservierung_model extends DB_Model $raum_reservierungen_query = "SELECT res.*, beginn, ende, CASE WHEN res.gruppe_kurzbz IS NOT NULL THEN res.gruppe_kurzbz - ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(res.semester AS varchar),'/'),COALESCE(CAST(res.verband AS varchar),'/')) + ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(res.semester AS varchar),'/'),COALESCE(CAST(res.verband AS varchar),'/'),COALESCE(CAST(res.gruppe AS varchar),'/')) END as gruppen_kuerzel FROM campus.vw_reservierung res JOIN public.tbl_studiengang studg ON studg.studiengang_kz=res.studiengang_kz @@ -96,7 +96,7 @@ class Reservierung_model extends DB_Model $query_result = $this->execReadOnlyQuery(" SELECT DISTINCT(insertvon), - 'reservierung' as type, beginn, ende, datum, + '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, diff --git a/public/js/api/factory/calendar/roomPlan.js b/public/js/api/factory/calendar/roomPlan.js index d27843b37..dc8aee207 100644 --- a/public/js/api/factory/calendar/roomPlan.js +++ b/public/js/api/factory/calendar/roomPlan.js @@ -40,5 +40,14 @@ export default { reservierung_id: reservierung_id } }; + }, + getGroupOptions(studiengangKz) { + return { + method: 'get', + url: '/api/frontend/v1/calendar/RoomPlan/getGroupOptions', + params: { + studiengangKz + } + }; } } \ No newline at end of file diff --git a/public/js/api/factory/ort.js b/public/js/api/factory/ort.js index 2e06d6b71..dce61279d 100644 --- a/public/js/api/factory/ort.js +++ b/public/js/api/factory/ort.js @@ -18,23 +18,32 @@ export default { getContentID(ort_kurbz) { return { - method: 'get', - url: '/api/frontend/v1/Ort/ContentID', - params: { ort_kurzbz: ort_kurbz } + method: "get", + url: "/api/frontend/v1/Ort/ContentID", + params: { ort_kurzbz: ort_kurbz }, }; }, getRooms(datum, von, bis, typ, personenanzahl = 0) { return { - method: 'get', - url: '/api/frontend/v1/Ort/getRooms', - params: { datum, von, bis, typ, personenanzahl } + method: "get", + url: "/api/frontend/v1/Ort/getRooms", + params: { datum, von, bis, typ, personenanzahl }, }; }, getRoomTypes() { return { - method: 'get', - url: '/api/frontend/v1/Ort/getTypes', - params: { } + method: "get", + url: "/api/frontend/v1/Ort/getTypes", + params: {}, }; - } -}; \ No newline at end of file + }, + getRoomInfo(ortKurzbz) { + return { + method: "get", + url: "/api/frontend/v1/Ort/getRoomInfo", + params: { + ortKurzbz, + }, + }; + }, +}; diff --git a/public/js/components/Calendar/LvPlan.js b/public/js/components/Calendar/LvPlan.js index 151c82377..1d2efb9ab 100644 --- a/public/js/components/Calendar/LvPlan.js +++ b/public/js/components/Calendar/LvPlan.js @@ -218,18 +218,18 @@ export default { :style="eventStyle(event)" >
@@ -101,6 +122,7 @@ export default { :is="renderers[event.type]?.calendarEvent" :event="event" :timeSlotDisplayBehavior="'always'" + @delete-event="(event) => deleteEvent(event)" >
diff --git a/public/js/components/Cis/LvPlan/MyLvPlan.js b/public/js/components/Cis/LvPlan/MyLvPlan.js index cb43a06d9..3fab090a3 100644 --- a/public/js/components/Cis/LvPlan/MyLvPlan.js +++ b/public/js/components/Cis/LvPlan/MyLvPlan.js @@ -2,6 +2,7 @@ import FhcCalendar from "../../Calendar/LvPlan.js"; import ApiLvPlan from '../../../api/factory/lvPlan.js'; import ApiAuthinfo from '../../../api/factory/authinfo.js'; +import ApiRoomPlan from '../../../api/factory/calendar/roomPlan.js'; export const DEFAULT_MODE_LVPLAN_DESKTOP = "Week"; export const DEFAULT_MODE_LVPLAN_MOBILE = "List"; @@ -134,6 +135,24 @@ export default { this.isMitarbeiter = authInfo.isMitarbeiter; this.isStudent = authInfo.isStudent; }, + deleteEvent(event) { + if (event.type === "reservierung") { + this.deleteReservation(event); + } + }, + async deleteReservation(event) { + if ( + luxon.DateTime.fromISO(`${event.datum}T${event.beginn}`) < + luxon.DateTime.now() + ) + return; + + await this.$api.call( + ApiRoomPlan.deleteRoomReservation(event.reservierung_id), + ); + + this.$refs.calendar.reset(); + }, }, async created() { await this.fetchAuthInfo(); @@ -157,6 +176,7 @@ export default { @update:date="handleChangeDate" @update:mode="handleChangeMode" @update:range="updateRange" + @delete-event="(event) => deleteEvent(event)" class="responsive-calendar" >
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); @@ -72,12 +59,15 @@ export default { } }); }, - async handleCreateEvent(event) - { + async handleCreateEvent(event) { event.ort_kurzbz = this.propsViewData.ort_kurzbz; - this.$api.call(ApiRoomPlan.addRoomReservation(event)); - this.$refs.calendar.resetEventLoader(); - this.$refs.calendar.closeModal(); + const reservationCreationResponse = await this.$api.call( + ApiRoomPlan.addRoomReservation(event), + ); + if (reservationCreationResponse.meta.status === "success") { + this.$refs.calendar.resetEventLoader(); + this.$refs.calendar.closeModal(); + } }, async handleDeleteEvent(event) { @@ -87,7 +77,7 @@ export default { if (luxon.DateTime.fromISO(`${event.datum}T${event.beginn}`) < luxon.DateTime.now()) return; - this.$api.call(ApiRoomPlan.deleteRoomReservation(event.reservierung_id)); + await this.$api.call(ApiRoomPlan.deleteRoomReservation(event.reservierung_id)); this.$refs.calendar.reset(); @@ -152,9 +142,32 @@ export default { ]; } }, + async created() { + const roomCreationInfoResponse = await this.$api.call( + ApiRoomPlan.getRoomCreationInfo(), + ); + if (roomCreationInfoResponse.meta?.status === "success") { + const berechtigt = !!roomCreationInfoResponse.data?.berechtigt; + if (roomCreationInfoResponse.data?.berechtigt) { + this.createContext.room_create_information.studiengaenge = + roomCreationInfoResponse.data?.studiengaenge; + } + this.createContext.show_all_fields = + roomCreationInfoResponse.data?.berechtigt; + } + + const roomInfoResponse = await this.$api.call( + ApiRoom.getRoomInfo(this.$props.propsViewData.ort_kurzbz), + ); + this.roomAdditionalInfo = roomInfoResponse.data.ausstattung; + }, template: /*html*/`

{{ $p.t('rauminfo/rauminfo') }} {{ propsViewData.ort_kurzbz }}

+ + {{ $p.t("search/result_equipment") + ":" }} + +
{ res?.data?.forEach(type => { - type.beschreibung = type.beschreibung.replace('&', '&') + type.beschreibung = type.beschreibung?.replace('&', '&') }) this.selectedType = this.defaultType this.roomtypes = res?.data ?? [] diff --git a/public/js/components/Cis/Renderer/Reservierungen/calendarEvent.js b/public/js/components/Cis/Renderer/Reservierungen/calendarEvent.js index 91c49d5e0..2a0f4db95 100644 --- a/public/js/components/Cis/Renderer/Reservierungen/calendarEvent.js +++ b/public/js/components/Cis/Renderer/Reservierungen/calendarEvent.js @@ -32,6 +32,11 @@ export default { this.$p.t('person/ort'), this.event.ort_kurzbz ].join(": ")); + + tooltipArray.push([ + this.$p.t('lehre/gruppe'), + this.event.gruppe[0].kuerzel.split("/")[0] + ].join(": ")); if (Array.isArray(this.event.lektor) && this.event.lektor.length > 0) { if (this.event.lektor.length > 3) { @@ -103,6 +108,7 @@ export default { {{ event.topic }} {{ event.ort_kurzbz }} + {{ event.gruppe[0].kuerzel.split("/")[0] }} + + {{ + $p.t('lehre','gruppe')? + $p.t('lehre','gruppe')+':' + :'' + }} + {{event.gruppe[0].kuerzel.split("/")[0]}} + {{ $p.t('lehre','lektor')? diff --git a/public/js/components/Cis/Renderer/Slot/roomModalContent.js b/public/js/components/Cis/Renderer/Slot/roomModalContent.js index e9f518c75..124c25192 100644 --- a/public/js/components/Cis/Renderer/Slot/roomModalContent.js +++ b/public/js/components/Cis/Renderer/Slot/roomModalContent.js @@ -1,9 +1,10 @@ -import FormInput from '../../../Form/Input.js'; +import FormInput from "../../../Form/Input.js"; +import ApiRoomPlan from "../../../../api/factory/calendar/roomPlan.js"; export default { - emits: ['create-event'], + emits: ["create-event"], components: { - FormInput + FormInput, }, inject: { timeGrid: "timeGrid", @@ -26,38 +27,85 @@ export default { verband: null, gruppe: null, selectedGruppe: null, - selectedLektoren: [] + selectedLektoren: [], + nestedGroupOptions: {}, }; }, - mounted() { - this.syncFromEvent(this.event); - }, + computed: { + semesterOptions() { + if (!this.studiengang) return []; + return Object.keys(this.nestedGroupOptions) + .map((val) => parseInt(val)) + .sort((a, b) => a - b); + }, + verbandOptions() { + if (!this.semester) return []; + + return Object.keys(this.nestedGroupOptions[this.semester]).sort(); + }, + gruppeOptions() { + if (!this.verband) return []; + + return this.nestedGroupOptions[this.semester][this.verband] + .map((val) => parseInt(val)) + .sort((a, b) => a - b); + }, + }, watch: { event: { handler(newEvent) { this.syncFromEvent(newEvent); }, - deep: true - } + deep: true, + }, + studiengang() { + this.semester = null; + this.verband = null; + this.gruppe = null; + this.nestedGroupOptions = {}; + this.fetchGroupOptions(); + }, + semester() { + this.verband = null; + }, + verband() { + this.gruppe = null; + }, }, 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'); + 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; + 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 [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 lektoren_uid = this.selectedLektoren.map((m) => m.uid); const spezialgruppe = this.selectedGruppe?.gruppe_kurzbz; @@ -71,22 +119,41 @@ export default { verband: this.verband, gruppe: this.gruppe, spezialgruppe: spezialgruppe, - lektoren: lektoren_uid - } + lektoren: lektoren_uid, + }; - this.$emit('create-event', event); + this.$emit("create-event", event); }, async searchGroup(event) { - this.filteredGroups = await this.event.createContext.room_create_information.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) + this.filteredLektoren = + await this.event.createContext.room_create_information.searchLektor( + event, + ); }, - capitalize(text) - { - if (!text) return '' - return text.charAt(0).toUpperCase() + text.slice(1) - } + capitalize(text) { + if (!text) return ""; + return text.charAt(0).toUpperCase() + text.slice(1); + }, + async fetchGroupOptions() { + if (!this.studiengang) return; + + const groupOptionsResponse = await this.$api.call( + ApiRoomPlan.getGroupOptions(this.studiengang), + ); + + if (groupOptionsResponse.meta.status === "success") { + this.nestedGroupOptions = groupOptionsResponse.data; + } + }, + }, + mounted() { + this.syncFromEvent(this.event); }, template: ` @@ -130,6 +197,7 @@ export default { :label="capitalize($p.t('global', 'titel'))" type="text" container-class="col-3" + maxlength="10" v-model="title" name="title" /> @@ -138,6 +206,7 @@ export default { :label="capitalize($p.t('global', 'beschreibung'))" type="text" container-class="col-4" + maxlength="32" v-model="beschreibung" name="Beschreibung" /> @@ -184,7 +253,7 @@ export default { name="semester" >
- ` + `, };