diff --git a/application/controllers/RoomManager.php b/application/controllers/RoomManager.php index 2221709eb..155c4443a 100644 --- a/application/controllers/RoomManager.php +++ b/application/controllers/RoomManager.php @@ -34,8 +34,6 @@ class RoomManager extends Auth_Controller ); $this->_setAuthUID(); - - $this->setControllerId(); } // ----------------------------------------------------------------------------------------------------------------- diff --git a/application/controllers/api/frontend/v1/Ort.php b/application/controllers/api/frontend/v1/Ort.php index 7a4f24acf..81ae8d810 100644 --- a/application/controllers/api/frontend/v1/Ort.php +++ b/application/controllers/api/frontend/v1/Ort.php @@ -44,6 +44,7 @@ class Ort extends FHCAPI_Controller ]); $this->load->library('form_validation'); + $this->load->library('requests/RoomRequest'); $this->load->model('ressource/Ort_model', 'OrtModel'); $this->load->model('ressource/Reservierung_model', 'ReservierungModel'); @@ -53,8 +54,12 @@ class Ort extends FHCAPI_Controller $this->loadPhrases([ 'global', 'ui', - 'lehre' + 'lehre', + 'gruppenmanagement', + 'person', ]); + + } //------------------------------------------------------------------------------------------------------------------ @@ -73,8 +78,8 @@ class Ort extends FHCAPI_Controller $query = "SELECT COUNT(*) OVER() AS full_count, public.tbl_ort.*, - pr.ort_kurzbz as pr_ort_kurzbz, - org.bezeichnung as org_bezeichnung + org.bezeichnung as org_bezeichnung, + org.organisationseinheittyp_kurzbz as org_organisationseinheittyp_kurzbz 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"; @@ -87,6 +92,16 @@ class Ort extends FHCAPI_Controller $searchableBooleanAttributes = ['lehre', 'reservieren', 'aktiv']; $searchableNumericAttributes = ['max_person', 'arbeitsplaetze', 'kosten', 'stockwerk']; $searchableNumericSpanAttributes = ['m2']; + $searchableCustomAttributes = [ + [ + 'raw_sql_fragment' => "CONCAT(public.tbl_ort.ort_kurzbz, ' - ', public.tbl_ort.bezeichnung)", + 'filter_parameter' => 'ort_kurzbz_bezeichnung_concat', + ], + [ + 'raw_sql_fragment' => "CONCAT('[', org.organisationseinheittyp_kurzbz, '] ', org.bezeichnung)", + 'filter_parameter' => 'org_organisationseinheittyp_kurzbz_org_bezeichnung_concat', + ] + ]; foreach ($searchableIdAttributes as $attribute) { if (isset($filter[$attribute]) && $filter[$attribute] !== '') { @@ -128,11 +143,45 @@ class Ort extends FHCAPI_Controller } } + foreach ($searchableCustomAttributes as $customAttribute) { + if (isset($filter[$customAttribute['filter_parameter']]) && $filter[$customAttribute['filter_parameter']] !== '') { + $queryWhereFragments[] = $customAttribute['raw_sql_fragment'] . " ILIKE ?"; + $filterData[] = '%' . trim($filter[$customAttribute['filter_parameter']]) . '%'; + } + } + if (count($queryWhereFragments) > 0) { $query .= ' WHERE ' . implode(' AND ', $queryWhereFragments); } - $query .= ' ORDER BY public.tbl_ort.ort_kurzbz ASC'; + $sortableAttributes = ['ort_kurzbz', 'bezeichnung', 'planbezeichnung', 'max_person', 'arbeitsplaetze', 'm2', 'lehre', 'reservieren', 'aktiv', 'stockwerk', 'kosten', 'parent_ort_kurzbz', 'org_bezeichnung']; + $sortableConcatAttributes = [ + [ + 'raw_sql_fragment' => "CONCAT('[', org.organisationseinheittyp_kurzbz, '] ', org.bezeichnung)", + 'sort_parameter' => 'org_organisationseinheittyp_kurzbz_org_bezeichnung_concat', + ] + ]; + $sorter = $this->input->get('sort', TRUE); + + foreach ($sortableAttributes as $attribute) { + if (isset($sorter[$attribute]) && in_array(strtolower($sorter[$attribute]), ['asc', 'desc'])) { + if ($attribute === 'org_bezeichnung') { + $query .= " ORDER BY org.bezeichnung " . strtoupper($sorter[$attribute]); + } else { + $query .= " ORDER BY public.tbl_ort.$attribute " . strtoupper($sorter[$attribute]); + } + } + } + + foreach ($sortableConcatAttributes as $customAttribute) { + if (isset($sorter[$customAttribute['sort_parameter']]) && in_array(strtolower($sorter[$customAttribute['sort_parameter']]), ['asc', 'desc'])) { + $query .= " ORDER BY " . $customAttribute['raw_sql_fragment'] . " " . strtoupper($sorter[$customAttribute['sort_parameter']]); + } + } + + if (!isset($sorter)) { + $query .= ' ORDER BY public.tbl_ort.ort_kurzbz ASC'; + } if ($paginationSize && $paginationPage) { $query .= " LIMIT ? OFFSET ?"; @@ -298,49 +347,10 @@ 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')]), - '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')]) - ]); - if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array()); - $parent_ort_kurzbz = $this->input->post('parent_ort_kurzbz'); - if ($parent_ort_kurzbz) { - $this->load->model('ressource/Ort_model', 'ParentRoomModel'); - $parentRoom = $this->ParentRoomModel->load($parent_ort_kurzbz); - if (isError($parentRoom) || !hasData($parentRoom)) { - $this->terminateWithError("Parent room with shortcode $parent_ort_kurzbz does not exist", self::ERROR_TYPE_GENERAL); - } - } - - $oe_kurzbz = $this->input->post('oe_kurzbz'); - if ($oe_kurzbz) { - $this->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel'); - $orgUnit = $this->OrganisationseinheitModel->load($oe_kurzbz); - if (isError($orgUnit) || !hasData($orgUnit)) { - $this->terminateWithError("Organizational unit with shortcode $oe_kurzbz does not exist", self::ERROR_TYPE_GENERAL); - } - } - - $standort_id = $this->input->post('standort_id'); - if ($standort_id) { - $this->load->model('organisation/Standort_model', 'StandortModel'); - $location = $this->StandortModel->load($standort_id); - if (isError($location) || !hasData($location)) { - $this->terminateWithError("Location with id $standort_id does not exist", self::ERROR_TYPE_GENERAL); - } - } - - $content_id = $this->input->post('content_id'); - if ($content_id) { - $this->load->model('content/Content_model', 'ContentModel'); - $content = $this->ContentModel->load($content_id); - if (isError($content) || !hasData($content)) { - $this->terminateWithError("Content with id $content_id does not exist", self::ERROR_TYPE_GENERAL); - } + if (!$this->roomrequest->validate()) { + $this->terminateWithValidationErrors($this->roomrequest->errors()); + return; } $this->db->trans_start(); @@ -365,7 +375,7 @@ class Ort extends FHCAPI_Controller "telefonklappe" => $this->input->post('telefonklappe'), "m2" => $this->input->post('m2'), "gebteil" => $this->input->post('gebteil'), - "arbeitsplaetze" => $this->input->post('arbeitsplatze'), + "arbeitsplaetze" => $this->input->post('arbeitsplaetze'), 'insertamum' => date('c'), 'insertvon' => getAuthUid(), 'updateamum' => date('c'), @@ -383,44 +393,9 @@ class Ort extends FHCAPI_Controller public function updateRoom($ort_kurzbz) { - if (!$ort_kurzbz) { - $this->terminateWithError("missing ort_kurzbz parameter", self::ERROR_TYPE_GENERAL); - } - - $parent_ort_kurzbz = $this->input->post('parent_ort_kurzbz'); - if ($parent_ort_kurzbz) { - $this->load->model('ressource/Ort_model', 'ParentRoomModel'); - $parentRoom = $this->ParentRoomModel->load($parent_ort_kurzbz); - if (isError($parentRoom) || !hasData($parentRoom)) { - $this->terminateWithError("Parent room with shortcode $parent_ort_kurzbz does not exist", self::ERROR_TYPE_GENERAL); - } - } - - $oe_kurzbz = $this->input->post('oe_kurzbz'); - if ($oe_kurzbz) { - $this->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel'); - $orgUnit = $this->OrganisationseinheitModel->load($oe_kurzbz); - if (isError($orgUnit) || !hasData($orgUnit)) { - $this->terminateWithError("Organizational unit with shortcode $oe_kurzbz does not exist", self::ERROR_TYPE_GENERAL); - } - } - - $standort_id = $this->input->post('standort_id'); - if ($standort_id) { - $this->load->model('organisation/Standort_model', 'StandortModel'); - $location = $this->StandortModel->load($standort_id); - if (isError($location) || !hasData($location)) { - $this->terminateWithError("Location with id $standort_id does not exist", self::ERROR_TYPE_GENERAL); - } - } - - $content_id = $this->input->post('content_id'); - if ($content_id) { - $this->load->model('content/Content_model', 'ContentModel'); - $content = $this->ContentModel->load($content_id); - if (isError($content) || !hasData($content)) { - $this->terminateWithError("Content with id $content_id does not exist", self::ERROR_TYPE_GENERAL); - } + if (!$this->roomrequest->validate("update")) { + $this->terminateWithValidationErrors($this->roomrequest->errors()); + return; } $this->db->trans_start(); diff --git a/application/libraries/CustomFormValidationLib.php b/application/libraries/CustomFormValidationLib.php new file mode 100644 index 000000000..a07856ea1 --- /dev/null +++ b/application/libraries/CustomFormValidationLib.php @@ -0,0 +1,104 @@ +_ci =& get_instance(); + } + + function explicit_integer($value) + { + if ($value === null) { + return true; + } + + if ($value === '') { + return false; + } + + if (filter_var($value, FILTER_VALIDATE_INT) !== false) { + return true; + } + + return false; + } + + function explicit_numeric($value) + { + if ($value === null) { + return true; + } + + if ($value === '') { + return false; + } + + if (is_numeric($value)) { + return true; + } + + return false; + } + + function explicit_boolean($value) + { + if ($value === null) { + return true; + } + + if ($value === '') { + return false; + } + + if ($value === 'true' || $value === 'false' || $value === true || $value === false || $value === 1 || $value === 0) { + return true; + } + + return false; + } + + function does_exist($value, $params) + { + if ($value === null ) { + return true; + } + + if ($value === '') { + return false; + } + + $parts = explode('.', $params); + if (count($parts) !== 3) { + return false; + } + + $subDatabase = $parts[0]; + $table = $parts[1]; + $field = $parts[2]; + + $result = $this->_ci->db->select('COUNT(*) as count') + ->from("$subDatabase.$table") + ->where($field, $value) + ->get(); + + if ($result === false) { + return false; + } + + return $result->row()->count > 0; + } +} diff --git a/application/libraries/requests/RoomRequest.php b/application/libraries/requests/RoomRequest.php new file mode 100644 index 000000000..1933e3a9c --- /dev/null +++ b/application/libraries/requests/RoomRequest.php @@ -0,0 +1,92 @@ +_ci =& get_instance(); + $this->_ci->load->library('CustomFormValidationLib'); + } + + public function validate($method = 'create') + { + if ($method === 'create') { + $this->_ci->customformvalidationlib->set_rules('ort_kurzbz', 'kurzbezeichnung', 'required|is_unique[tbl_ort.ort_kurzbz]|max_length[16]|regex_match[/^[a-zA-Z0-9_.]+$/]', [ + 'required' => $this->_ci->p->t('ui', 'error_fieldRequired', ['field' => $this->_ci->p->t('gruppenmanagement', 'kurzbezeichnung')]), + 'is_unique' => $this->_ci->p->t('ui', 'error_fieldUnique', ['field' => $this->_ci->p->t('gruppenmanagement', 'kurzbezeichnung')]), + 'max_length' => $this->_ci->p->t('ui', 'error_fieldMaxLength', ['field' => $this->_ci->p->t('gruppenmanagement', 'kurzbezeichnung'), 'max' => 16]), + 'regex_match' => $this->_ci->p->t('ui', 'error_fieldInvalidFormat', ['field' => $this->_ci->p->t('gruppenmanagement', 'kurzbezeichnung')]) + ]); + } + + $this->_ci->customformvalidationlib->set_rules('parent_ort_kurzbz', 'parent_ort_kurzbz', 'does_exist[public.tbl_ort.ort_kurzbz]|max_length[16]', [ + 'does_exist' => $this->_ci->p->t('ui', 'error_entryDoesExists', ['entry' => $this->_ci->p->t('ui', 'parentRoom')]), + 'max_length' => $this->_ci->p->t('ui', 'error_fieldMaxLength', ['field' => $this->_ci->p->t('ui', 'parentRoom'), 'max' => 16]) + ]); + $this->_ci->customformvalidationlib->set_rules('oe_kurzbz', 'oe_kurzbz', 'does_exist[public.tbl_organisationseinheit.oe_kurzbz]|max_length[32]', [ + 'does_exist' => $this->_ci->p->t('ui', 'error_entryDoesExists', ['entry' => $this->_ci->p->t('lehre', 'organisationseinheit')]), + 'max_length' => $this->_ci->p->t('ui', 'error_fieldMaxLength', ['field' => $this->_ci->p->t('lehre', 'organisationseinheit'), 'max' => 32]) + ]); + $this->_ci->customformvalidationlib->set_rules('standort_id', 'standort_id', 'explicit_integer|does_exist[public.tbl_standort.standort_id]', [ + 'does_exist' => $this->_ci->p->t('ui', 'error_entryDoesExists', ['entry' => $this->_ci->p->t('person', 'standort')]), + ]); + $this->_ci->customformvalidationlib->set_rules('content_id', 'content_id', 'explicit_integer|does_exist[campus.tbl_content.content_id]', [ + 'explicit_integer' => $this->_ci->p->t('ui', 'error_fieldInteger', ['field' => $this->_ci->p->t('ui', 'contentId')]), + 'does_exist' => $this->_ci->p->t('ui', 'error_entryDoesExists', ['entry' => $this->_ci->p->t('ui', 'contentId')]) + ]); + + $this->_ci->customformvalidationlib->set_rules('lehre', 'lehre', 'explicit_boolean', [ + 'explicit_boolean' => $this->_ci->p->t('ui', 'error_fieldBoolean', ['field' => $this->_ci->p->t('ui', 'lehre')]) + ]); + $this->_ci->customformvalidationlib->set_rules('reservieren', 'reservieren', 'explicit_boolean', [ + 'explicit_boolean' => $this->_ci->p->t('ui', 'error_fieldBoolean', ['field' => $this->_ci->p->t('ui', 'reservieren')]) + ]); + $this->_ci->customformvalidationlib->set_rules('aktiv', 'aktiv', 'explicit_boolean', [ + 'explicit_boolean' => $this->_ci->p->t('ui', 'error_fieldBoolean', ['field' => $this->_ci->p->t('person', 'aktiv')]) + ]); + + $this->_ci->customformvalidationlib->set_rules('bezeichnung', 'bezeichnung', 'max_length[64]', [ + 'max_length' => $this->_ci->p->t('ui', 'error_fieldMaxLength', ['field' => $this->_ci->p->t('ui', 'bezeichnung'), 'max' => 64]) + ]); + $this->_ci->customformvalidationlib->set_rules('planbezeichnung', 'planbezeichnung', 'max_length[8]', [ + 'max_length' => $this->_ci->p->t('ui', 'error_fieldMaxLength', ['field' => $this->_ci->p->t('ui', 'planbezeichnung'), 'max' => 8]) + ]); + $this->_ci->customformvalidationlib->set_rules('max_person', 'maxPerson', 'explicit_integer', [ + 'explicit_integer' => $this->_ci->p->t('ui', 'error_fieldInteger', ['field' => $this->_ci->p->t('ui', 'maxPersons')]) + ]); + $this->_ci->customformvalidationlib->set_rules('stockwerk', 'stockwerk', 'explicit_integer', [ + 'explicit_integer' => $this->_ci->p->t('ui', 'error_fieldInteger', ['field' => $this->_ci->p->t('ui', 'stockwerk')]) + ]); + $this->_ci->customformvalidationlib->set_rules('m2', 'm2', 'explicit_numeric', [ + 'explicit_numeric' => $this->_ci->p->t('ui', 'error_fieldNumeric', ['field' => $this->_ci->p->t('ui', 'quadratmeter')]) + ]); + $this->_ci->customformvalidationlib->set_rules('dislozierung', 'dislozierung', 'explicit_numeric', [ + 'explicit_numeric' => $this->_ci->p->t('ui', 'error_fieldNumeric', ['field' => $this->_ci->p->t('ui', 'dislozierung')]) + ]); + $this->_ci->customformvalidationlib->set_rules('kosten', 'kosten', 'explicit_numeric', [ + 'explicit_numeric' => $this->_ci->p->t('ui', 'error_fieldNumeric', ['field' => $this->_ci->p->t('ui', 'kosten')]) + ]); + $this->_ci->customformvalidationlib->set_rules('telefonklappe', 'telefonklappe', 'max_length[8]', [ + 'max_length' => $this->_ci->p->t('ui', 'error_fieldMaxLength', ['field' => $this->_ci->p->t('person', 'telefonklappe'), 'max' => 8]) + ]); + $this->_ci->customformvalidationlib->set_rules('gebteil', 'gebteil', 'max_length[32]', [ + 'max_length' => $this->_ci->p->t('ui', 'error_fieldMaxLength', ['field' => $this->_ci->p->t('ui', 'gebaudeteil'), 'max' => 32]) + ]); + $this->_ci->customformvalidationlib->set_rules('arbeitsplaetze', 'arbeitsplaetze', 'explicit_integer', [ + 'explicit_integer' => $this->_ci->p->t('ui', 'error_fieldInteger', ['field' => $this->_ci->p->t('ui', 'arbeitsplaetze')]) + ]); + + return $this->_ci->customformvalidationlib->run(); + } + + public function errors() + { + return $this->_ci->customformvalidationlib->error_array(); + } + + +} \ No newline at end of file diff --git a/public/css/roomManagerOverview.css b/public/css/roomManagerOverview.css index af279b8cb..fd8fb430a 100644 --- a/public/css/roomManagerOverview.css +++ b/public/css/roomManagerOverview.css @@ -1,3 +1,41 @@ html { font-size: .75em; +} + +nav.navbar.navbar-header, +nav.navbar.navbar-left-side { + font-size: 18px +} + +nav.navbar.navbar-left-side { + padding-top: 8px; + padding-bottom: 8px; +} + +/* Relative sizing inside navbar */ +nav.navbar .nav-item { + font-size: 18px +} + +nav.navbar .navbar-brand-icon { + font-size: 16px +} + +nav.navbar .left-side-menu-link-entry { + font-size: 14px !important; +} + +nav.navbar .nav-link { + font-size: 18px; + padding-top: 15px; + padding-bottom: 8px; +} + +nav.navbar .dropdown-menu { + padding: 8px 0px; +} + +nav.navbar .dropdown-item { + font-size: 16px; + padding: 4px 16px; } \ No newline at end of file diff --git a/public/js/api/factory/ort.js b/public/js/api/factory/ort.js index 8581462c8..b18659e66 100644 --- a/public/js/api/factory/ort.js +++ b/public/js/api/factory/ort.js @@ -34,10 +34,24 @@ export default { "filter[max_person]" : params?.maxPersons, "filter[arbeitsplaetze]" : params?.workplace, "filter[m2]" : params?.squareMeters, - "filter[oe_bezeichnung]" : params?.orgUnitDescription, + "filter[org_organisationseinheittyp_kurzbz_org_bezeichnung_concat]" : params?.orgUnitConcatDescription, "filter[kosten]" : params?.costs, "filter[stockwerk]" : params?.floor, "filter[parent_ort_kurzbz]" : params?.parentRoomShortCode, + "filter[ort_kurzbz_bezeichnung_concat]" : params?.ort_kurzbz_bezeichnung_concat, + "sort[ort_kurzbz]" : params?.sort?.ort_kurzbz, + "sort[bezeichnung]" : params?.sort?.bezeichnung, + "sort[planbezeichnung]" : params?.sort?.planbezeichnung, + "sort[max_person]" : params?.sort?.max_person, + "sort[arbeitsplaetze]" : params?.sort?.arbeitsplaetze, + "sort[m2]" : params?.sort?.m2, + "sort[org_organisationseinheittyp_kurzbz_org_bezeichnung_concat]" : params?.sort?.org_organisationseinheittyp_kurzbz_org_bezeichnung_concat, + "sort[lehre]" : params?.sort?.lehre, + "sort[reservieren]" : params?.sort?.reservieren, + "sort[aktiv]" : params?.sort?.aktiv, + "sort[kosten]" : params?.sort?.kosten, + "sort[stockwerk]" : params?.sort?.stockwerk, + "sort[parent_ort_kurzbz]" : params?.sort?.parent_ort_kurzbz, "pagination[page]" : params?.pagination?.page, "pagination[size]" : params?.pagination?.size, } diff --git a/public/js/components/RoomManager/RoomFormModal.js b/public/js/components/RoomManager/RoomFormModal.js index b1eca5145..fa3a5303f 100644 --- a/public/js/components/RoomManager/RoomFormModal.js +++ b/public/js/components/RoomManager/RoomFormModal.js @@ -38,7 +38,7 @@ export default { } else { this.resetRoomForm(); } - }, + }, }, data: () => { return { @@ -65,8 +65,13 @@ export default { }, dropdownParsedRooms() { return this.rooms.map((room) => { + let label = room.ort_kurzbz; + if (room.bezeichnung && room.bezeichnung !== '') { + label += ` - ${room.bezeichnung}`; + } + return { - label: `${room.ort_kurzbz} - ${room.bezeichnung}`, + label, value: room.ort_kurzbz, }; }); @@ -75,7 +80,7 @@ export default { methods: { filterOrganizationalUnits(event) { let defaultItem = { - label: "----------", + label: this.$p.t("ui", "dropdownEmptyOption"), value: null, }; @@ -97,7 +102,7 @@ export default { this.rooms = await this.fetchRooms(event.query); let defaultItem = { - label: "----------", + label: this.$p.t("ui", "dropdownEmptyOption"), value: null, }; @@ -149,7 +154,9 @@ export default { }; } - let potentialParentRooms = await this.fetchRooms(this.editedRoom.parent_ort_kurzbz); + let potentialParentRooms = await this.fetchRooms( + this.editedRoom.parent_ort_kurzbz, + ); let parentRoomData = null; let parentRoom = potentialParentRooms.find( @@ -157,8 +164,13 @@ export default { ); if (parentRoom) { this.rooms.push(parentRoom); + let label = parentRoom.ort_kurzbz; + if (parentRoom.bezeichnung && parentRoom.bezeichnung !== '') { + label += ` - ${parentRoom.bezeichnung}`; + } + parentRoomData = { - label: `${parentRoom.ort_kurzbz} - ${parentRoom.bezeichnung}`, + label, value: parentRoom.ort_kurzbz, }; } @@ -183,7 +195,7 @@ export default { telefonklappe: this.editedRoom.telefonklappe, quadratmeter: this.editedRoom.m2, gebaudeteil: this.editedRoom.gebteil, - arbeitsplatze: this.editedRoom.arbeitsplaetze, + arbeitsplaetze: this.editedRoom.arbeitsplaetze, }; this.$refs.roomFormModal.show(); @@ -207,24 +219,46 @@ export default { return { parent_ort_kurzbz: this.roomFormData.parentRoom?.value, standort_id: this.roomFormData.locationId, - oe_kurzbz: this.roomFormData.organizationalUnit?.value, - content_id: this.roomFormData.contentId, + oe_kurzbz: + this.roomFormData.organizationalUnit?.value !== "" + ? this.roomFormData.organizationalUnit?.value + : null, + content_id: + this.roomFormData.contentId !== "" + ? this.roomFormData.contentId + : null, ort_kurzbz: this.roomFormData.kurzbezeichnung, bezeichnung: this.roomFormData.bezeichnung, planbezeichnung: this.roomFormData.planbezeichnung, aktiv: this.roomFormData.aktiv, lehre: this.roomFormData.lehre, reservieren: this.roomFormData.reservieren, - max_person: this.roomFormData.maxPerson, - stockwerk: this.roomFormData.stockwerk, + max_person: + this.roomFormData.maxPerson !== "" + ? this.roomFormData.maxPerson + : null, + stockwerk: + this.roomFormData.stockwerk !== "" + ? this.roomFormData.stockwerk + : null, lageplan: this.roomFormData.lageplan, - dislozierung: this.roomFormData.dislozierung, - kosten: this.roomFormData.kosten, + dislozierung: + this.roomFormData.dislozierung === "" + ? null + : this.roomFormData.dislozierung, + kosten: + this.roomFormData.kosten !== "" ? this.roomFormData.kosten : null, ausstattung: this.roomFormData.ausstattung, telefonklappe: this.roomFormData.telefonklappe, - m2: this.roomFormData.quadratmeter, + m2: + this.roomFormData.quadratmeter !== "" + ? this.roomFormData.quadratmeter + : null, gebteil: this.roomFormData.gebaudeteil, - arbeitsplatze: this.roomFormData.arbeitsplatze, + arbeitsplaetze: + this.roomFormData.arbeitsplaetze !== "" + ? this.roomFormData.arbeitsplaetze + : null, }; }, hideRoomFormModal() { @@ -240,14 +274,16 @@ export default { aktiv: true, }; }, - async fetchRooms(searchedRoomShortCode) { - let getRoomsResponse = await this.$api.call(ApiRoom.getAllRooms({ - shortCode: searchedRoomShortCode, - pagination: { - page: 1, - size: 100, - }, - })); + async fetchRooms(roomSearchTerm = "") { + let getRoomsResponse = await this.$api.call( + ApiRoom.getAllRooms({ + ort_kurzbz_bezeichnung_concat: roomSearchTerm, + pagination: { + page: 1, + size: 100, + }, + }), + ); if (getRoomsResponse.meta.status === "success") { return getRoomsResponse.data; } else { @@ -256,6 +292,12 @@ export default { return []; }, + setDefaultLocationOption() { + this.locations.unshift({ + standort_id: null, + bezeichnung: this.$p.t("ui", "dropdownEmptyOption"), + }); + }, }, async created() { let getLocationsResponse = await this.$api.call( @@ -263,10 +305,6 @@ export default { ); if (getLocationsResponse.meta.status === "success") { this.locations = getLocationsResponse.data; - this.locations.unshift({ - standort_id: null, - kurzbz: "----------", - }); } else { this.$fhcAlert.alertError(this.$p.t("ui", "errorLoadingLocations")); } @@ -285,6 +323,21 @@ export default { } this.rooms = await this.fetchRooms(); + }, + mounted() { + this.$p + .loadCategory([ + "global", + "lehre", + "ui", + "gruppenmanagement", + "core", + "person", + ]) + .then(() => { + this.phrasesLoaded = true; + this.setDefaultLocationOption(); + }); }, template: /* html */ ` @@ -306,7 +359,7 @@ export default { dropdown forceSelection type="autocomplete" - name="parentRoomShortCode" + name="parent_ort_kurzbz" > @@ -322,38 +375,29 @@ export default { dropdown forceSelection type="autocomplete" - name="organizationalUnitShortCode" + name="oe_kurzbz" >
-
- - -
+
+ + +
@@ -434,17 +484,15 @@ export default {
@@ -464,24 +512,22 @@ export default { v-model="roomFormData.gebaudeteil" :label="$capitalize($p.t('ui', 'gebaudeteil'))" type="text" - name="gebaudeteil" + name="gebteil" >
diff --git a/public/js/components/RoomManager/RoomManagerOverview.js b/public/js/components/RoomManager/RoomManagerOverview.js index f49f5bb02..10b0ba662 100644 --- a/public/js/components/RoomManager/RoomManagerOverview.js +++ b/public/js/components/RoomManager/RoomManagerOverview.js @@ -23,8 +23,7 @@ export default { provide() { return { cisRoot: this.cisRoot, - hasBasisOrtWPermission: - this.permissions["basis/ort_w"] || false, + hasBasisOrtWPermission: this.permissions["basis/ort_w"] || false, }; }, watch: { @@ -50,14 +49,10 @@ export default { organizationalUnits: [], filteredOrganizationalUnits: [], buildingComponents: [ - { - label: "----------", - value: null, - }, { label: "A", value: "A", - }, + }, { label: "B", value: "B", @@ -67,16 +62,16 @@ export default { value: "C", }, { - label: "D", - value: "D" + label: "D", + value: "D", }, { label: "E", - value: "E" + value: "E", }, { label: "F", - value: "F" + value: "F", }, ], isRoomFormModalVisible: false, @@ -93,19 +88,45 @@ export default { const options = { ajaxURL: "dummy", 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 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 orgUnitConcatFilter = params?.filter?.find( + (filter) => filter.field === "org_organisationseinheittyp_kurzbz_org_bezeichnung_concat", + ); + 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 === "parent_ort_kurzbz", + ); let isForTrainingProgramValue = null; if (this.filterData.isForTrainingProgram === true) { @@ -117,7 +138,7 @@ export default { isForTrainingProgramValue = false; } } - + let reservationNeededValue = null; if (this.filterData.isReservationNeeded === true) { reservationNeededValue = true; @@ -140,6 +161,20 @@ export default { } } + let shortCodeSorter = params?.sort?.find((sort) => sort.field === "ort_kurzbz"); + let descriptionSorter = params?.sort?.find((sort) => sort.field === "bezeichnung"); + let planDescriptionSorter = params?.sort?.find((sort) => sort.field === "planbezeichnung"); + let maxPersonsSorter = params?.sort?.find((sort) => sort.field === "max_person"); + let workplaceSorter = params?.sort?.find((sort) => sort.field === "arbeitsplaetze"); + let squareMetersSorter = params?.sort?.find((sort) => sort.field === "m2"); + let orgUnitConcatSorter = params?.sort?.find((sort) => sort.field === "org_organisationseinheittyp_kurzbz_org_bezeichnung_concat"); + let lehreSorter = params?.sort?.find((sort) => sort.field === "lehre"); + let reservierenSorter = params?.sort?.find((sort) => sort.field === "reservieren"); + let aktivSorter = params?.sort?.find((sort) => sort.field === "aktiv"); + let costsSorter = params?.sort?.find((sort) => sort.field === "kosten"); + let floorSorter = params?.sort?.find((sort) => sort.field === "stockwerk"); + let parentRoomSorter = params?.sort?.find((sort) => sort.field === "parent_ort_kurzbz"); + return this.$api.call( ApiRoom.getAllRooms({ organizationalUnitShortCode: @@ -155,10 +190,25 @@ export default { maxPersons: maxPersonsFilter?.value, workplace: workplaceFilter?.value, squareMeters: squareMetersFilter?.value, - orgUnitDescription: orgUnitFilter?.value, + orgUnitConcatDescription: orgUnitConcatFilter?.value, costs: costsFilter?.value, floor: floorFilter?.value, parentRoomShortCode: parentRoomFilter?.value, + sort: { + ort_kurzbz: shortCodeSorter?.dir, + bezeichnung: descriptionSorter?.dir, + planbezeichnung: planDescriptionSorter?.dir, + max_person: maxPersonsSorter?.dir, + arbeitsplaetze: workplaceSorter?.dir, + m2: squareMetersSorter?.dir, + org_organisationseinheittyp_kurzbz_org_bezeichnung_concat: orgUnitConcatSorter?.dir, + lehre: lehreSorter?.dir, + reservieren: reservierenSorter?.dir, + aktiv: aktivSorter?.dir, + kosten: costsSorter?.dir, + stockwerk: floorSorter?.dir, + parent_ort_kurzbz: parentRoomSorter?.dir, + }, pagination: { page: params.page, size: params.size, @@ -167,7 +217,7 @@ export default { ); }, ajaxResponse: (url, params, response) => response, - persistenceID: "room_manager_overview_table1111222233333", + persistenceID: "room_manager_overview_table", selectableRows: true, index: "ort_kurzbz", columns: [ @@ -209,11 +259,40 @@ export default { title: this.$capitalize(this.$p.t("ui", "quadratmeter")), field: "m2", headerFilter: true, - width: 100 + width: 100, }, { title: this.$capitalize(this.$p.t("lehre", "organisationseinheit")), - field: "org_bezeichnung", + field: "org_organisationseinheittyp_kurzbz_org_bezeichnung_concat", + formatter: function (cell) { + let data = cell.getRow().getData(); + let value = null; + if (data.org_organisationseinheittyp_kurzbz) { + value = `[${data.org_organisationseinheittyp_kurzbz}]`; + } + if (data.org_bezeichnung) { + value += ` ${data.org_bezeichnung}`; + } + + return value; + }, + sorter: function (a, b, aRow, bRow, column, dir, sorterParams) { + let aData = aRow.getData(); + let bData = bRow.getData(); + + let aFull = ( + aData.org_organisationseinheittyp_kurzbz + + " " + + aData.org_bezeichnung + ).toLowerCase().trim(); + let bFull = ( + bData.org_organisationseinheittyp_kurzbz + + " " + + bData.org_bezeichnung + ).toLowerCase().trim(); + + return aFull.localeCompare(bFull); + }, headerFilter: true, width: 180, }, @@ -222,8 +301,9 @@ export default { field: "lehre", headerFilter: true, headerFilterParams: { - "tristate":true, elementAttributes:{"value":"true"} - }, + tristate: true, + elementAttributes: { value: "true" }, + }, formatter: "tickCross", hozAlign: "center", formatterParams: { @@ -236,8 +316,9 @@ export default { field: "reservieren", headerFilter: true, headerFilterParams: { - "tristate":true, elementAttributes:{"value":"true"} - }, + tristate: true, + elementAttributes: { value: "true" }, + }, formatter: "tickCross", hozAlign: "center", formatterParams: { @@ -250,8 +331,9 @@ export default { field: "aktiv", headerFilter: true, headerFilterParams: { - "tristate":true, elementAttributes:{"value":"true"} - }, + tristate: true, + elementAttributes: { value: "true" }, + }, formatter: "tickCross", hozAlign: "center", formatterParams: { @@ -271,7 +353,7 @@ export default { }, { title: this.$capitalize(this.$p.t("ui", "parentRoom")), - field: "pr_ort_kurzbz", + field: "parent_ort_kurzbz", headerFilter: true, width_: 120, }, @@ -286,7 +368,9 @@ export default { let roomTypeBtn = document.createElement("button"); roomTypeBtn.className = "btn btn-outline-secondary btn-action"; roomTypeBtn.innerHTML = ''; - roomTypeBtn.title = this.$capitalize(this.$p.t("ui", "btn_editRoomType")); + roomTypeBtn.title = this.$capitalize( + this.$p.t("ui", "btn_editRoomType"), + ); roomTypeBtn.addEventListener("click", (event) => this.editRoomType(cell.getData().ort_kurzbz), ); @@ -294,7 +378,7 @@ export default { if (!this.hasBasisOrtWPermission) { container.append(roomTypeBtn); return container; - }2222 + } let button = document.createElement("button"); @@ -313,7 +397,9 @@ export default { button.className = "btn btn-outline-secondary btn-action bg-danger"; button.innerHTML = ''; - button.title = this.$capitalize(this.$p.t("ui", "btn_deleteRoom")); + button.title = this.$capitalize( + this.$p.t("ui", "btn_deleteRoom"), + ); button.addEventListener("click", () => { let isDeletionConfirmed = confirm( this.$p.t("ui", "deleteRoomConfirmation"), @@ -330,11 +416,12 @@ export default { }, ], layout: "fitColumns", - pagination:true, - paginationMode:"remote", + pagination: true, + paginationMode: "remote", paginationSize: 100, - maxHeight:"700px", - filterMode:"remote", + maxHeight: "700px", + filterMode: "remote", + sortMode: "remote", }; return options; }, @@ -371,18 +458,20 @@ export default { return events; }, dropdownParsedOrganizationalUnits() { - return this.organizationalUnits.map((unit) => { - return { - label: `${unit.bezeichnung} (${unit.organisationseinheittyp_kurzbz})`, - value: unit.oe_kurzbz, - }; - }); + return this.organizationalUnits + .map((unit) => { + return { + label: `[${unit.organisationseinheittyp_kurzbz}] ${unit.bezeichnung}`, + value: unit.oe_kurzbz, + }; + }) + .sort((a, b) => a.label.localeCompare(b.label)); }, }, methods: { filterOrganizationalUnits(event) { let defaultItem = { - label: "----------", + label: this.$p.t("ui", "dropdownEmptyOption"), value: null, }; @@ -450,6 +539,18 @@ export default { this.$fhcAlert.alertError(this.$p.t("ui", "errorUpdatingRoom")); } }, + setDefaultBuildingComponentOption() { + this.buildingComponents.unshift({ + label: this.$p.t("ui", "dropdownEmptyOption"), + value: null, + }); + }, + setDefaultLocationOption() { + this.locations.unshift({ + standort_id: null, + bezeichnung: this.$p.t("ui", "dropdownEmptyOption"), + }); + }, }, async created() { let getLocationsResponse = await this.$api.call( @@ -457,7 +558,6 @@ export default { ); if (getLocationsResponse.meta.status === "success") { this.locations = getLocationsResponse.data; - this.locations.unshift({ standort_id: null, kurzbz: "----------" }); } else { this.$fhcAlert.alertError(this.$p.t("ui", "errorLoadingLocations")); } @@ -470,7 +570,9 @@ export default { (a, b) => a.bezeichnung.localeCompare(b.bezeichnung), ); } else { - this.$fhcAlert.alertError(this.$p.t("ui", "errorLoadingOrganizationalUnits")); + this.$fhcAlert.alertError( + this.$p.t("ui", "errorLoadingOrganizationalUnits"), + ); } }, mounted() { @@ -485,6 +587,8 @@ export default { ]) .then(() => { this.phrasesLoaded = true; + this.setDefaultBuildingComponentOption(); + this.setDefaultLocationOption(); }); }, template: /* html */ ` @@ -524,7 +628,7 @@ export default {
- {{location.kurzbz}} + {{location.bezeichnung}}
diff --git a/public/js/components/RoomManager/RoomTypeFormModal.js b/public/js/components/RoomManager/RoomTypeFormModal.js index 1c790557a..e504e1b11 100644 --- a/public/js/components/RoomManager/RoomTypeFormModal.js +++ b/public/js/components/RoomManager/RoomTypeFormModal.js @@ -156,7 +156,7 @@ export default { methods: { filterRoomTypes(event) { let defaultItem = { - label: "----------", + label: this.$p.t("ui", "dropdownEmptyOption"), value: null, }; diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 402bcead4..323126d1c 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -59077,6 +59077,153 @@ I have been informed that I am under no obligation to consent to the transmissio ) ) ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'error_fieldMaxLength', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Das Eingabefeld {field} überschreitet die maximale Länge von {max} Zeichen', + + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The Input Field {field} exceeds the maximum length of {max} characters', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'error_fieldInteger', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Das Eingabefeld {field} muss eine ganze Zahl sein', + + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The Input Field {field} must be an integer', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'error_fieldNumeric', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Das Eingabefeld {field} muss eine Zahl sein', + + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The Input Field {field} must be a number', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'error_entryDoesExists', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Der Eintrag in der DB mit diesem {entry} existiert nicht', + + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The entry in DB with this {entry} does not exist', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'error_fieldBoolean', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Das Eingabefeld {field} muss ein Wahrheitswert sein', + + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The Input Field {field} must be a boolean value', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'error_fieldInvalidFormat', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Das Eingabefeld {field} hat ein ungültiges Format', + + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The Input Field {field} has an invalid format', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'dropdownEmptyOption', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '--keine Auswahl--', + + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => '--no selection--', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), );