diff --git a/application/config/navigation.php b/application/config/navigation.php index 4d4dcc22a..31ac08766 100644 --- a/application/config/navigation.php +++ b/application/config/navigation.php @@ -383,3 +383,12 @@ $config['navigation_menu']['apps'] = [ 'requiredPermissions' => array('lehre/lehrauftrag_bestellen:r', 'lehre/lehrauftrag_erteilen:r') ] ]; + +$config['navigation_menu']['RoomManager/index'] = array( + 'lvTemplateUebersicht' => array( + 'link' => site_url('RoomManager'), + 'description' => 'Raumverwaltung übersicht', + 'icon' => '', + 'sort' => 1 + ) +); \ No newline at end of file diff --git a/application/controllers/api/frontend/v1/Ort.php b/application/controllers/api/frontend/v1/Ort.php index 346ac7f8b..aecebfb6d 100644 --- a/application/controllers/api/frontend/v1/Ort.php +++ b/application/controllers/api/frontend/v1/Ort.php @@ -31,7 +31,6 @@ class Ort extends FHCAPI_Controller */ public function __construct() { - // NOTE(chris): additional permission checks will be done in SearchBarLib parent::__construct([ 'getAllRooms' => self::PERM_LOGGED, 'getRooms' => self::PERM_LOGGED, @@ -47,6 +46,8 @@ class Ort extends FHCAPI_Controller $this->load->library('form_validation'); $this->load->model('ressource/Ort_model', 'OrtModel'); + $this->load->model('ressource/Reservierung_model', 'ReservierungModel'); + $this->config->load('raumsuche'); $this->loadPhrases([ @@ -61,53 +62,99 @@ class Ort extends FHCAPI_Controller public function getAllRooms() { + $paginationSize = $this->input->get('pagination[size]', TRUE); + $paginationPage = $this->input->get('pagination[page]', TRUE); + $filter = $this->input->get('filter', TRUE); $filterData = []; - $query = "SELECT public.tbl_ort.*, pr.ort_kurzbz as pr_ort_kurzbz, org.bezeichnung as org_bezeichnung + $query = "SELECT + COUNT(*) OVER() AS full_count, + public.tbl_ort.*, + pr.ort_kurzbz as pr_ort_kurzbz, + org.bezeichnung as org_bezeichnung FROM public.tbl_ort LEFT JOIN public.tbl_ort as pr ON pr.ort_kurzbz = public.tbl_ort.parent_ort_kurzbz LEFT JOIN public.tbl_organisationseinheit as org ON org.oe_kurzbz = public.tbl_ort.oe_kurzbz"; - $whereItems = []; - if (isset($filter['locationId']) && $filter['locationId'] !== '') { - $whereItems[] = 'public.tbl_ort.standort_id = ?'; - $filterData[] = $filter['locationId']; + $queryWhereFragments = []; + + $searchableIdAttributes = ['standort_id', 'oe_kurzbz', 'gebteil']; + $searchableTextAttributes = ['ort_kurzbz', 'bezeichnung', 'planbezeichnung', 'oe_kurzbz', 'oe_bezeichnung']; + $searchableBooleanAttributes = ['lehre', 'reservieren', 'aktiv']; + $searchableNumericAttributes = ['max_person', 'arbeitsplaetze']; + $searchableNumericSpanAttributes = ['m2']; + + foreach ($searchableIdAttributes as $attribute) { + if (isset($filter[$attribute]) && $filter[$attribute] !== '') { + $queryWhereFragments[] = "public.tbl_ort.$attribute = ?"; + $filterData[] = $filter[$attribute]; + } } - if (isset($filter['organizationalUnitShortCode']) && $filter['organizationalUnitShortCode'] !== '') { - $whereItems[] = 'public.tbl_ort.oe_kurzbz = ?'; - $filterData[] = $filter['organizationalUnitShortCode']; + + foreach ($searchableTextAttributes as $attribute) { + $tableAttribute = "public.tbl_ort.$attribute"; + if ($attribute === 'oe_bezeichnung') { + $tableAttribute = "org.bezeichnung"; + } + if (isset($filter[$attribute]) && $filter[$attribute] !== '') { + $queryWhereFragments[] = "$tableAttribute ILIKE ?"; + $filterData[] = '%' . $filter[$attribute] . '%'; + } } - if (isset($filter['buildingComponent']) && $filter['buildingComponent'] !== '') { - $whereItems[] = 'public.tbl_ort.gebteil = ?'; - $filterData[] = $filter['buildingComponent']; + + foreach ($searchableBooleanAttributes as $attribute) { + if (isset($filter[$attribute]) && $filter[$attribute] !== '' && $filter[$attribute] === 'true' ) { + $queryWhereFragments[] = "public.tbl_ort.$attribute = ?"; + $filterData[] = $filter[$attribute] === 'true' ? true : false; + } } - if (isset($filter['isForTrainingProgram']) && $filter['isForTrainingProgram'] === 'true') { - $whereItems[] = 'public.tbl_ort.lehre = ?'; - $filterData[] = $filter['isForTrainingProgram'] === 'true' ? true : false; + + foreach ($searchableNumericAttributes as $attribute) { + if (isset($filter[$attribute]) && $filter[$attribute] !== '') { + $queryWhereFragments[] = "public.tbl_ort.$attribute = ?"; + $filterData[] = $filter[$attribute]; + } } - if (isset($filter['isReservationNeeded']) && $filter['isReservationNeeded'] === 'true') { - $whereItems[] = 'public.tbl_ort.reservieren = ?'; - $filterData[] = $filter['isReservationNeeded'] === 'true' ? true : false; + + foreach ($searchableNumericSpanAttributes as $attribute) { + if (isset($filter[$attribute]) && $filter[$attribute] !== '') { + $queryWhereFragments[] = "public.tbl_ort.$attribute >= ? AND public.tbl_ort.$attribute <= ?"; + $filterData[] = $filter[$attribute] - 1; + $filterData[] = $filter[$attribute] + 1; + } } - if (isset($filter['isActive']) && $filter['isActive'] === 'true') { - $whereItems[] = 'public.tbl_ort.aktiv = ?'; - $filterData[] = $filter['isActive'] === 'true' ? true : false; - } - - if (count($whereItems) > 0) { - $query .= ' WHERE ' . implode(' AND ', $whereItems); + + if (count($queryWhereFragments) > 0) { + $query .= ' WHERE ' . implode(' AND ', $queryWhereFragments); } $query .= ' ORDER BY public.tbl_ort.ort_kurzbz ASC'; - $result = $this->OrtModel->execReadOnlyQuery($query, $filterData); + if ($paginationSize && $paginationPage) { + $query .= " LIMIT ? OFFSET ?"; + } + + $queryData = array_merge($filterData); + if ($paginationSize && $paginationPage) { + $queryData = array_merge($filterData, [$paginationSize, ($paginationPage - 1) * $paginationSize]); + } + + $result = $this->OrtModel->execReadOnlyQuery($query, $queryData); - - $this->terminateWithSuccess(getData($result)); + $queryData = hasData($result) ? getData($result) : []; + + if ($paginationSize && $paginationPage) { + $totalItems = count($queryData) > 0 ? $queryData[0]->full_count : 0; + $pageCount = ceil($totalItems / $paginationSize); + $this->addTabulatorPaginationData($pageCount); + } + $this->terminateWithSuccess($queryData); + + exit; } /** @@ -130,7 +177,6 @@ class Ort extends FHCAPI_Controller $typ = $this->input->get('typ', TRUE); $personenanzahl = $this->input->get('personenanzahl', TRUE); - $this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel'); $isMitarbeiter = $this->MitarbeiterModel->isMitarbeiter(getAuthUID())->retval; @@ -254,7 +300,8 @@ class Ort extends FHCAPI_Controller public function createRoom() { $this->form_validation->set_rules('ort_kurzbz', 'kurzbezeichnung', 'required|is_unique[tbl_ort.ort_kurzbz]', [ - 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('lehre', 'kurzbz')]) + 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('lehre', 'kurzbz')]), + 'is_unique' => $this->p->t('ui', 'error_fieldUnique', ['field' => $this->p->t('lehre', 'kurzbz')]) ]); $this->form_validation->set_rules('content_id', 'content_id', 'number', [ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('lehre', 'content_id')]) @@ -425,6 +472,28 @@ class Ort extends FHCAPI_Controller $this->db->trans_start(); + $reservationsQuery = "SELECT COUNT(*) FROM campus.tbl_reservierung WHERE ort_kurzbz = ?"; + $reservationsResult = $this->OrtModel->execReadOnlyQuery($reservationsQuery, [$ort_kurzbz]); + if (isError($reservationsResult)) { + $this->terminateWithError(getError($reservationsResult), self::ERROR_TYPE_GENERAL); + } + + $reservationsCount = hasData($reservationsResult) ? getData($reservationsResult)[0]->count : 0; + if ($reservationsCount > 0) { + $this->terminateWithError("Cannot delete room with shortcode $ort_kurzbz because there are existing reservations for this room", self::ERROR_TYPE_GENERAL); + } + + $softwareImageOrtQuery = "SELECT COUNT(*) FROM extension.tbl_softwareimage_ort WHERE ort_kurzbz = ?"; + $softwareImageOrtResult = $this->OrtModel->db->query($softwareImageOrtQuery, [$ort_kurzbz]); + if ($softwareImageOrtResult === false) { + $this->terminateWithError($this->p->t('ui', 'error_existingSoftwareImageForRoomTypeUponDeletion'), self::ERROR_TYPE_GENERAL); + } + + $softwareImageOrtCount = $softwareImageOrtResult->row()->count; + if ($softwareImageOrtCount > 0) { + $this->terminateWithError($this->p->t('ui', 'error_existingReservationsForRoomsUponDeletion'), self::ERROR_TYPE_GENERAL); + } + $result = $this->OrtModel->delete([ "ort_kurzbz" => $ort_kurzbz ]); diff --git a/application/core/FHCAPI_Controller.php b/application/core/FHCAPI_Controller.php index e81506d4b..af2ab5705 100644 --- a/application/core/FHCAPI_Controller.php +++ b/application/core/FHCAPI_Controller.php @@ -154,6 +154,11 @@ class FHCAPI_Controller extends Auth_Controller $this->returnObj['meta'][$key] = $value; } + public function addTabulatorPaginationData($lastPage = 1) + { + $this->returnObj['last_page'] = $lastPage; + } + /** * @param string $key * @return mixed diff --git a/application/views/room_manager/index.php b/application/views/room_manager/index.php index 0f7c4122e..0b39d760d 100644 --- a/application/views/room_manager/index.php +++ b/application/views/room_manager/index.php @@ -22,6 +22,7 @@ $includesArray = array( 'public/extensions/FHC-Core-Developer/css/FhcMain.css', 'public/css/components/calendar.css', 'public/css/components/vue-datepicker.css', + 'public/css/roomManagerOverview.css' ) ); @@ -29,7 +30,7 @@ $this->load->view('templates/FHC-Header', $includesArray); ?>
- + diff --git a/include/ort.class.php b/include/ort.class.php index 648d34a93..f1d99e4d0 100644 --- a/include/ort.class.php +++ b/include/ort.class.php @@ -54,7 +54,7 @@ class ort extends basis_db public $m2; // numeric(8,2) public $gebteil; // varchar(32) public $arbeitsplaetze; // integer - + public $parent_ort_kurzbz; // varchar(16) public $ort_kurzbz_old; // string /** @@ -117,6 +117,7 @@ class ort extends basis_db $ort_obj->oe_kurzbz = $row->oe_kurzbz; $ort_obj->gebteil = $row->gebteil; $ort_obj->arbeitsplaetze = $row->arbeitsplaetze; + $ort_obj->parent_ort_kurzbz = $row->parent_ort_kurzbz; $this->result[] = $ort_obj; } return true; @@ -185,6 +186,7 @@ class ort extends basis_db $ort_obj->oe_kurzbz = $row->oe_kurzbz; $ort_obj->gebteil = $row->gebteil; $ort_obj->arbeitsplaetze = $row->arbeitsplaetze; + $ort_obj->parent_ort_kurzbz = $row->parent_ort_kurzbz; $this->result[] = $ort_obj; } return true; @@ -232,6 +234,7 @@ class ort extends basis_db $this->oe_kurzbz = $row->oe_kurzbz; $this->m2 = $row->m2; $this->arbeitsplaetze = $row->arbeitsplaetze; + $this->parent_ort_kurzbz = $row->parent_ort_kurzbz; } else { @@ -287,7 +290,7 @@ class ort extends basis_db { //Neuen Datensatz anlegen $qry = 'INSERT INTO public.tbl_ort (ort_kurzbz, bezeichnung, planbezeichnung, max_person, aktiv, lehre, reservieren, lageplan, - dislozierung, kosten, stockwerk, standort_id, telefonklappe, insertamum, insertvon, updateamum, updatevon, content_id,ausstattung,m2,gebteil,oe_kurzbz,arbeitsplaetze) VALUES ('. + dislozierung, kosten, stockwerk, standort_id, telefonklappe, insertamum, insertvon, updateamum, updatevon, content_id,ausstattung,m2,gebteil,oe_kurzbz,arbeitsplaetze,parent_ort_kurzbz) VALUES ('. $this->db_add_param($this->ort_kurzbz).', '. $this->db_add_param($this->bezeichnung).', '. $this->db_add_param($this->planbezeichnung).', '. @@ -310,7 +313,8 @@ class ort extends basis_db $this->db_add_param($this->m2).','. $this->db_add_param($this->gebteil).','. $this->db_add_param($this->oe_kurzbz).','. - $this->db_add_param($this->arbeitsplaetze).');'; + $this->db_add_param($this->arbeitsplaetze).','. + $this->db_add_param($this->parent_ort_kurzbz).');'; } else { @@ -337,7 +341,8 @@ class ort extends basis_db 'm2='.$this->db_add_param($this->m2).', '. 'gebteil='.$this->db_add_param($this->gebteil).', '. 'oe_kurzbz='.$this->db_add_param($this->oe_kurzbz).', '. - 'arbeitsplaetze='.$this->db_add_param($this->arbeitsplaetze).' '. + 'arbeitsplaetze='.$this->db_add_param($this->arbeitsplaetze).', '. + 'parent_ort_kurzbz='.$this->db_add_param($this->parent_ort_kurzbz).' '. 'WHERE ort_kurzbz = '.$this->db_add_param(($this->ort_kurzbz_old!='')?$this->ort_kurzbz_old:$this->ort_kurzbz).';'; } @@ -455,7 +460,8 @@ class ort extends basis_db $ort_obj->gebteil = $row->gebteil; $ort_obj->oe_kurzbz = $row->oe_kurzbz; $ort_obj->arbeitsplaetze = $row->arbeitsplaetze; - + $ort_obj->parent_ort_kurzbz = $row->parent_ort_kurzbz; + $this->result[] = $ort_obj; } return true; @@ -523,6 +529,7 @@ class ort extends basis_db $ort_obj->gebteil = $row->gebteil; $ort_obj->oe_kurzbz = $row->oe_kurzbz; $ort_obj->arbeitsplaetze = $row->arbeitsplaetze; + $ort_obj->parent_ort_kurzbz = $row->parent_ort_kurzbz; $this->result[] = $ort_obj; } @@ -577,6 +584,8 @@ class ort extends basis_db $ort_obj->oe_kurzbz = $row->oe_kurzbz; $ort_obj->gebteil = $row->gebteil; $ort_obj->arbeitsplaetze = $row->arbeitsplaetze; + $ort_obj->parent_ort_kurzbz = $row->parent_ort_kurzbz; + $this->result[] = $ort_obj; } return true; @@ -634,6 +643,7 @@ class ort extends basis_db $ort_obj->oe_kurzbz = $row->oe_kurzbz; $ort_obj->gebteil = $row->gebteil; $ort_obj->arbeitsplaetze = $row->arbeitsplaetze; + $ort_obj->parent_ort_kurzbz = $row->parent_ort_kurzbz; $this->result[] = $ort_obj; } return true; diff --git a/public/css/roomManagerOverview.css b/public/css/roomManagerOverview.css new file mode 100644 index 000000000..af279b8cb --- /dev/null +++ b/public/css/roomManagerOverview.css @@ -0,0 +1,3 @@ +html { + font-size: .75em; +} \ No newline at end of file diff --git a/public/js/api/factory/ort.js b/public/js/api/factory/ort.js index 5b20ce3bc..8581462c8 100644 --- a/public/js/api/factory/ort.js +++ b/public/js/api/factory/ort.js @@ -17,16 +17,29 @@ export default { getAllRooms(params) { + return { method: 'get', url: 'api/frontend/v1/Ort/getAllRooms', params: { - "filter[organizationalUnitShortCode]" : params?.organizationalUnitShortCode, - "filter[locationId]" : params?.locationId, - "filter[buildingComponent]" : params?.buildingComponent, - "filter[isForTrainingProgram]" : params?.isForTrainingProgram, - "filter[isReservationNeeded]" : params?.isReservationNeeded, - "filter[isActive]" : params?.isActive, + "filter[oe_kurzbz]" : params?.organizationalUnitShortCode, + "filter[standort_id]" : params?.locationId, + "filter[gebteil]" : params?.buildingComponent, + "filter[lehre]" : params?.isForTrainingProgram, + "filter[reservieren]" : params?.isReservationNeeded, + "filter[aktiv]" : params?.isActive, + "filter[ort_kurzbz]" : params?.shortCode, + "filter[bezeichnung]" : params?.description, + "filter[planbezeichnung]" : params?.planDescription, + "filter[max_person]" : params?.maxPersons, + "filter[arbeitsplaetze]" : params?.workplace, + "filter[m2]" : params?.squareMeters, + "filter[oe_bezeichnung]" : params?.orgUnitDescription, + "filter[kosten]" : params?.costs, + "filter[stockwerk]" : params?.floor, + "filter[parent_ort_kurzbz]" : params?.parentRoomShortCode, + "pagination[page]" : params?.pagination?.page, + "pagination[size]" : params?.pagination?.size, } } }, diff --git a/public/js/apps/RoomManagerApp.js b/public/js/apps/RoomManagerApp.js index 2e86e54e1..90e2dd426 100644 --- a/public/js/apps/RoomManagerApp.js +++ b/public/js/apps/RoomManagerApp.js @@ -16,6 +16,7 @@ */ import RoomManagerOverview from "../components/RoomManager/RoomManagerOverview.js"; +import {CoreNavigationCmpt} from '../components/navigation/Navigation.js'; import FhcAlert from "../plugins/FhcAlert.js"; import Phrasen from "../plugins/Phrasen.js"; @@ -40,10 +41,12 @@ const router = VueRouter.createRouter({ const app = Vue.createApp({ components: { - RoomManagerOverview + RoomManagerOverview, + CoreNavigationCmpt }, }); + app.config.globalProperties.$capitalize = capitalize; app.use(router) diff --git a/public/js/components/RoomManager/RoomFormModal.js b/public/js/components/RoomManager/RoomFormModal.js index 4040ca010..96ff29370 100644 --- a/public/js/components/RoomManager/RoomFormModal.js +++ b/public/js/components/RoomManager/RoomFormModal.js @@ -93,7 +93,9 @@ export default { }), )); }, - filterRooms(event) { + async filterRooms(event) { + this.rooms = await this.fetchRooms(event.query); + let defaultItem = { label: "----------", value: null, @@ -151,14 +153,19 @@ export default { }; } + let potentialParentRooms = await this.fetchRooms(this.editedRoom.parent_ort_kurzbz); - let parentRoom = this.rooms.find( + let parentRoomData = null; + let parentRoom = potentialParentRooms.find( (room) => room.ort_kurzbz === this.editedRoom.parent_ort_kurzbz, ); - let parentRoomData = { - label: parentRoom?.bezeichnung || "", - value: parentRoom?.ort_kurzbz || null, - }; + if (parentRoom) { + this.rooms.push(parentRoom); + parentRoomData = { + label: `${parentRoom.ort_kurzbz} - ${parentRoom.bezeichnung}`, + value: parentRoom.ort_kurzbz, + }; + } this.roomFormData = { parentRoom: parentRoomData, @@ -237,6 +244,22 @@ export default { aktiv: true, }; }, + async fetchRooms(searchedRoomShortCode) { + let getRoomsResponse = await this.$api.call(ApiRoom.getAllRooms({ + shortCode: searchedRoomShortCode, + pagination: { + page: 1, + size: 100, + }, + })); + if (getRoomsResponse.meta.status === "success") { + return getRoomsResponse.data; + } else { + console.error("Error fetching rooms:", getRoomsResponse.meta.message); + } + + return []; + }, }, async created() { let getLocationsResponse = await this.$api.call( @@ -244,6 +267,10 @@ export default { ); if (getLocationsResponse.meta.status === "success") { this.locations = getLocationsResponse.data; + this.locations.unshift({ + standort_id: null, + kurzbz: "----------", + }); } else { console.error( "Error fetching locations:", @@ -265,17 +292,7 @@ export default { ); } - let getRoomsResponse = await this.$api.call( - ApiRoom.getAllRooms(), - ); - if (getRoomsResponse.meta.status === "success") { - this.rooms = getRoomsResponse.data; - } else { - console.error( - "Error fetching rooms:", - getRoomsResponse.meta.message, - ); - } + this.rooms = await this.fetchRooms(); }, template: /* html */ ` @@ -291,12 +308,13 @@ export default { :label="$capitalize($p.t('ui/parentRoom'))" :suggestions="filteredRooms" :optionValue="(option) => option.value" - :optionLabel="(option) => option.label" + :optionLabel="(option) => option.label" + :delay="500" @complete="filterRooms" dropdown forceSelection type="autocomplete" - name="parentRoomShortCode" + name="parentRoomShortCode" >
diff --git a/public/js/components/RoomManager/RoomManagerOverview.js b/public/js/components/RoomManager/RoomManagerOverview.js index 043e3b17b..3d710d40b 100644 --- a/public/js/components/RoomManager/RoomManagerOverview.js +++ b/public/js/components/RoomManager/RoomManagerOverview.js @@ -39,7 +39,36 @@ export default { locations: [], organizationalUnits: [], filteredOrganizationalUnits: [], - buildingComponents: ["A", "B", "C", "D", "E", "F"], + buildingComponents: [ + { + label: "----------", + value: null, + }, + { + label: "A", + value: "A", + }, + { + label: "B", + value: "B", + }, + { + label: "C", + value: "C", + }, + { + label: "D", + value: "D" + }, + { + label: "E", + value: "E" + }, + { + label: "F", + value: "F" + }, + ], isRoomFormModalVisible: false, isRoomTypeFormModalVisible: false, editedRoomShortCode: null, @@ -50,20 +79,53 @@ export default { tabulatorOptions() { const options = { ajaxURL: "dummy", - ajaxRequestFunc: async () => - this.$api.call( + ajaxRequestFunc: async (url, config, params) => { + let shortCodeFilter = params?.filter?.find((filter) => filter.field === "ort_kurzbz"); + let descriptionFilter = params?.filter?.find((filter) => filter.field === "bezeichnung"); + let planDescriptionFilter = params?.filter?.find((filter) => filter.field === "planbezeichnung"); + let maxPersonsFilter = params?.filter?.find((filter) => filter.field === "max_person"); + let workplaceFilter = params?.filter?.find((filter) => filter.field === "arbeitsplaetze"); + let squareMetersFilter = params?.filter?.find((filter) => filter.field === "m2"); + let orgUnitFilter = params?.filter?.find((filter) => filter.field === "org_bezeichnung"); + let isForTrainingProgramFilter = params?.filter?.find((filter) => filter.field === "lehre"); + let reservationNeededFilter = params?.filter?.find((filter) => filter.field === "reservieren"); + let isActiveFilter = params?.filter?.find((filter) => filter.field === "aktiv"); + let costsFilter = params?.filter?.find((filter) => filter.field === "kosten"); + let floorFilter = params?.filter?.find((filter) => filter.field === "stockwerk"); + let parentRoomFilter = params?.filter?.find((filter) => filter.field === "pr_ort_kurzbz"); + + let isForTrainingProgramValue = this.filterData.isForTrainingProgram ? "true" : isForTrainingProgramFilter?.value ? "true" : "false"; + let reservationNeededValue = this.filterData.isReservationNeeded ? "true" : reservationNeededFilter?.value ? "true" : "false"; + let isActiveValue = this.filterData.isActive ? "true" : isActiveFilter?.value ? "true" : "false"; + + return this.$api.call( ApiRoom.getAllRooms({ organizationalUnitShortCode: this.filterData.organizationalUnit?.value, locationId: this.filterData.locationId, buildingComponent: this.filterData.buildingComponent, - isForTrainingProgram: this.filterData.isForTrainingProgram, - isReservationNeeded: this.filterData.isReservationNeeded, - isActive: this.filterData.isActive, + isForTrainingProgram: isForTrainingProgramValue, + isReservationNeeded: reservationNeededValue, + isActive: isActiveValue, + shortCode: shortCodeFilter?.value, + description: descriptionFilter?.value, + planDescription: planDescriptionFilter?.value, + maxPersons: maxPersonsFilter?.value, + workplace: workplaceFilter?.value, + squareMeters: squareMetersFilter?.value, + orgUnitDescription: orgUnitFilter?.value, + costs: costsFilter?.value, + floor: floorFilter?.value, + parentRoomShortCode: parentRoomFilter?.value, + pagination: { + page: params.page, + size: params.size, + }, }), - ), - ajaxResponse: (url, params, response) => response.data, - persistenceID: "core_class_schedule_validity_periods", + ); + }, + ajaxResponse: (url, params, response) => response, + persistenceID: "room_manager_overview_table", selectableRows: true, index: "ort_kurzbz", columns: [ @@ -73,7 +135,7 @@ export default { ), field: "ort_kurzbz", headerFilter: true, - width: 150, + width: 100, }, { title: this.$capitalize( @@ -87,19 +149,19 @@ export default { title: this.$capitalize(this.$p.t("ui", "planbezeichnung")), field: "planbezeichnung", headerFilter: true, - width: 200, + width: 150, }, { title: this.$capitalize(this.$p.t("ui", "maxPersons")), field: "max_person", headerFilter: true, - width: 100, + width: 80, }, { title: this.$capitalize(this.$p.t("ui", "arbeitsplaetze")), field: "arbeitsplaetze", headerFilter: true, - width: 100, + width: 80, }, { title: this.$capitalize(this.$p.t("ui", "quadratmeter")), @@ -111,7 +173,7 @@ export default { title: this.$capitalize(this.$p.t("lehre", "organisationseinheit")), field: "org_bezeichnung", headerFilter: true, - width: 200, + width: 180, }, { title: this.$capitalize(this.$p.t("ui", "lehre")), @@ -164,8 +226,7 @@ export default { { title: this.$capitalize(this.$p.t("global", "actions")), field: "actions", - minWidth: 150, - maxWidth: 150, + width: 120, formatter: (cell, formatterParams, onRendered) => { let container = document.createElement("div"); container.className = "d-flex gap-2"; @@ -211,6 +272,11 @@ export default { }, ], layout: "fitColumns", + pagination:true, + paginationMode:"remote", + paginationSize: 100, + maxHeight:"700px", + filterMode:"remote", }; return options; }, @@ -309,14 +375,7 @@ export default { this.editedRoomForRoomTypeManagement = roomShortCode; }, async reloadTableData() { - this.$refs.roomManagerOverviewTable.tabulator.replaceData("/", { - organizationalUnitShortCode: this.filterData.organizationalUnit?.value, - locationId: this.filterData.locationId, - buildingComponent: this.filterData.buildingComponent, - isForTrainingProgram: this.filterData.isForTrainingProgram, - isReservationNeeded: this.filterData.isReservationNeeded, - isActive: this.filterData.isActive, - }); + this.$refs.roomManagerOverviewTable.tabulator.replaceData("/"); }, handleRoomUpdated() { this.editedRoomShortCode = null; @@ -343,6 +402,7 @@ export default { ); if (getLocationsResponse.meta.status === "success") { this.locations = getLocationsResponse.data; + this.locations.unshift({ standort_id: null, kurzbz: "----------" }); } else { console.error( "Error fetching locations:", @@ -440,9 +500,9 @@ export default { diff --git a/public/js/components/RoomManager/RoomTypeFormModal.js b/public/js/components/RoomManager/RoomTypeFormModal.js index b0954536b..07e4fec4b 100644 --- a/public/js/components/RoomManager/RoomTypeFormModal.js +++ b/public/js/components/RoomManager/RoomTypeFormModal.js @@ -1,4 +1,3 @@ -import ApiRoom from "../../../js/api/factory/ort.js"; import ApiRoomType from "../../../js/api/factory/roomType.js"; import ApiRoomToRoomType from "../../../js/api/factory/roomToRoomType.js"; @@ -74,7 +73,7 @@ export default { ), ), ajaxResponse: (url, params, response) => response.data, - persistenceID: "core_class_schedule_validity_periods", + persistenceID: "room_type_assignment_table", selectableRows: true, columns: [ { diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 3e422f514..b04e86eb4 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -58857,6 +58857,46 @@ I have been informed that I am under no obligation to consent to the transmissio ) ) ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'error_existingReservationsForRoomsUponDeletion', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Es gibt bestehende Reservierungen für diesen Raum. Bitte entfernen Sie die Reservierungen, bevor Sie den Raum löschen.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'There are existing reservations for this room. Please remove the reservations before deleting the room.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'error_existingSoftwareImageForRoomTypeUponDeletion', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Es gibt bestehende Software-Images für diesen Raumtyp. Bitte entfernen Sie die Software-Images, bevor Sie den Raumtyp löschen.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'There are existing software images for this room type. Please remove the software images before deleting the room type.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), ); diff --git a/vilesci/stammdaten/raum_details.php b/vilesci/stammdaten/raum_details.php index 61886ae07..bcfe43f96 100644 --- a/vilesci/stammdaten/raum_details.php +++ b/vilesci/stammdaten/raum_details.php @@ -67,6 +67,7 @@ $gebteil=''; $m2=''; $arbeitsplaetze=''; + $parent_ort_kurzbz = ''; $neu = "true"; @@ -93,6 +94,7 @@ $oe_kurzbz = $_POST["oe_kurzbz"]; $gebteil = $_POST["gebteil"]; $arbeitsplaetze = $_POST["arbeitsplaetze"]; + $parent_ort_kurzbz = $_POST["parent_ort_kurzbz"]; $sg_update = new ort(); $sg_update->ort_kurzbz = $ort_kurzbz; @@ -115,6 +117,7 @@ $sg_update->gebteil = $gebteil; $sg_update->oe_kurzbz = $oe_kurzbz; $sg_update->arbeitsplaetze = $arbeitsplaetze; + $sg_update->parent_ort_kurzbz = $parent_ort_kurzbz; $sg_update->updateamum = date('Y-m-d H:i:s'); $sg_update->updatevon = $user; @@ -161,6 +164,7 @@ $m2 = $sg->m2; $oe_kurzbz = $sg->oe_kurzbz; $arbeitsplaetze = $sg->arbeitsplaetze; + $parent_ort_kurzbz = $sg->parent_ort_kurzbz; $neu = "false"; } @@ -402,6 +406,8 @@ Anz. Arbeitsplätze + Parent Raum + Lageplan