Add navigation component to RoomManager

This commit is contained in:
Ivymaster
2026-05-06 11:41:10 +02:00
parent 05814383d4
commit 081703e0f8
10 changed files with 235 additions and 99 deletions
+8
View File
@@ -45,6 +45,14 @@ $config['navigation_header'] = array(
'expand' => true,
'sort' => 30,
'requiredPermissions' => 'admin:w'
),
'roomManagerOverview' => array(
'link' => site_url('RoomManager'),
'icon' => '',
'description' => 'Raumverwaltung',
'expand' => true,
'sort' => 40,
'requiredPermissions' => 'admin:w'
)
)
),
+6 -2
View File
@@ -15,7 +15,7 @@ class RoomManager extends Auth_Controller
{
parent::__construct(
array(
'index' => array('lehre/unterrichtszeiten_gk:r'),
'index' => array('basis/ort:r')
)
);
@@ -42,7 +42,11 @@ class RoomManager extends Auth_Controller
// Public methods
public function index()
{
return $this->load->view('room_manager/index');
return $this->load->view('room_manager/index', [
'permissions' => [
'basis/ort_w' => $this->permissionlib->isBerechtigt('basis/ort', 'suid'),
],
]);
}
// -----------------------------------------------------------------------------------------------------------------
+12 -12
View File
@@ -32,15 +32,15 @@ class Ort extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getAllRooms' => self::PERM_LOGGED,
'getAllRooms' => array('basis/ort:r'),
'getRooms' => self::PERM_LOGGED,
'getTypes' => self::PERM_LOGGED,
'ContentID' => self::PERM_LOGGED,
'getOrtKurzbzContent' => self::PERM_LOGGED,
'getRoom' => self::PERM_LOGGED,
'createRoom' => self::PERM_LOGGED,
'updateRoom' => self::PERM_LOGGED,
'deleteRoom' => self::PERM_LOGGED,
'createRoom' => array('basis/ort:rw'),
'updateRoom' => array('basis/ort:rw'),
'deleteRoom' => array('basis/ort:rw'),
]);
$this->load->library('form_validation');
@@ -82,16 +82,16 @@ class Ort extends FHCAPI_Controller
$queryWhereFragments = [];
$searchableIdAttributes = ['standort_id', 'oe_kurzbz', 'gebteil'];
$searchableTextAttributes = ['ort_kurzbz', 'bezeichnung', 'planbezeichnung', 'oe_kurzbz', 'oe_bezeichnung'];
$searchableIdAttributes = ['standort_id', 'gebteil'];
$searchableTextAttributes = ['ort_kurzbz', 'parent_ort_kurzbz', 'bezeichnung', 'planbezeichnung', 'oe_kurzbz', 'oe_bezeichnung'];
$searchableBooleanAttributes = ['lehre', 'reservieren', 'aktiv'];
$searchableNumericAttributes = ['max_person', 'arbeitsplaetze'];
$searchableNumericAttributes = ['max_person', 'arbeitsplaetze', 'kosten', 'stockwerk'];
$searchableNumericSpanAttributes = ['m2'];
foreach ($searchableIdAttributes as $attribute) {
if (isset($filter[$attribute]) && $filter[$attribute] !== '') {
$queryWhereFragments[] = "public.tbl_ort.$attribute = ?";
$filterData[] = $filter[$attribute];
$filterData[] = trim($filter[$attribute]);
}
}
@@ -102,7 +102,7 @@ class Ort extends FHCAPI_Controller
}
if (isset($filter[$attribute]) && $filter[$attribute] !== '') {
$queryWhereFragments[] = "$tableAttribute ILIKE ?";
$filterData[] = '%' . $filter[$attribute] . '%';
$filterData[] = '%' . trim($filter[$attribute]) . '%';
}
}
@@ -116,15 +116,15 @@ class Ort extends FHCAPI_Controller
foreach ($searchableNumericAttributes as $attribute) {
if (isset($filter[$attribute]) && $filter[$attribute] !== '') {
$queryWhereFragments[] = "public.tbl_ort.$attribute = ?";
$filterData[] = $filter[$attribute];
$filterData[] = trim($filter[$attribute]);
}
}
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;
$filterData[] = trim($filter[$attribute]) - 1;
$filterData[] = trim($filter[$attribute]) + 1;
}
}
@@ -33,9 +33,9 @@ class RoomToRoomTypeApi extends FHCAPI_Controller
{
// NOTE(chris): additional permission checks will be done in SearchBarLib
parent::__construct([
'getRoomToRoomTypeRelationsByRoomShortCode' => self::PERM_LOGGED,
'createRoomToRoomTypeRelation' => self::PERM_LOGGED,
'deleteRoomToRoomTypeRelation' => self::PERM_LOGGED,
'getRoomToRoomTypeRelationsByRoomShortCode' => array('basis/ort:r'),
'createRoomToRoomTypeRelation' => array('basis/ort:rw'),
'deleteRoomToRoomTypeRelation' => array('basis/ort:rw'),
]);
$this->load->library('form_validation');
@@ -101,6 +101,7 @@ class RoomToRoomTypeApi extends FHCAPI_Controller
public function deleteRoomToRoomTypeRelation() {
$this->form_validation->set_rules('roomShortCode', 'roomShortCode', 'required');
$this->form_validation->set_rules('roomTypeShortCode', 'roomTypeShortCode', 'required');
$this->form_validation->set_rules('hierarchy', 'hierarchy', 'required|integer');
if ($this->form_validation->run() === false) {
return $this->terminateWithError(validation_errors());
@@ -109,6 +110,7 @@ class RoomToRoomTypeApi extends FHCAPI_Controller
$result = $this->OrtRoomTypeModel->db->delete($this->OrtRoomTypeModel->getDbTable(), [
'ort_kurzbz' => $this->input->post('roomShortCode'),
'raumtyp_kurzbz' => $this->input->post('roomTypeShortCode'),
'hierarchie' => $this->input->post('hierarchy'),
]);
if ($result === false) {
@@ -32,8 +32,8 @@ class RoomTypeApi extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getAllRoomTypes' => self::PERM_LOGGED,
'createRoomType' => self::PERM_LOGGED,
'getAllRoomTypes' => array('basis/ort:r'),
'createRoomType' => array('basis/ort:rw'),
]);
$this->load->library('form_validation');
+2 -1
View File
@@ -32,7 +32,8 @@ $this->load->view('templates/FHC-Header', $includesArray);
<div id="main">
<core-navigation-cmpt></core-navigation-cmpt>
<router-view
cis-root="<?= CIS_ROOT; ?>"
cis-root="<?= CIS_ROOT; ?>"
:permissions="<?= htmlspecialchars(json_encode($permissions)); ?>"
>
</router-view>
</div>
+3 -2
View File
@@ -33,13 +33,14 @@ export default {
},
}
},
deleteRoomToRoomTypeRelation(roomShortCode, roomTypeShortCode) {
deleteRoomToRoomTypeRelation(roomShortCode, roomTypeShortCode, hierarchy) {
return {
method: 'post',
url: `api/frontend/v1/RoomToRoomTypeApi/deleteRoomToRoomTypeRelation`,
params: {
roomShortCode,
roomTypeShortCode
roomTypeShortCode,
hierarchy
},
}
}
@@ -17,6 +17,16 @@ export default {
RoomFormModal,
RoomTypeFormModal,
},
props: {
permissions: Object,
},
provide() {
return {
cisRoot: this.cisRoot,
hasBasisOrtWPermission:
this.permissions["basis/ort_w"] || false,
};
},
watch: {
filterData: {
handler(newValue) {
@@ -76,6 +86,9 @@ export default {
};
},
computed: {
hasBasisOrtWPermission() {
return this.permissions["basis/ort_w"] || false;
},
tabulatorOptions() {
const options = {
ajaxURL: "dummy",
@@ -125,7 +138,7 @@ export default {
);
},
ajaxResponse: (url, params, response) => response,
persistenceID: "room_manager_overview_table",
persistenceID: "room_manager_overview_table1111222233333",
selectableRows: true,
index: "ort_kurzbz",
columns: [
@@ -222,6 +235,7 @@ export default {
title: this.$capitalize(this.$p.t("ui", "parentRoom")),
field: "pr_ort_kurzbz",
headerFilter: true,
width_: 120,
},
{
title: this.$capitalize(this.$p.t("global", "actions")),
@@ -229,33 +243,39 @@ export default {
width: 120,
formatter: (cell, formatterParams, onRendered) => {
let container = document.createElement("div");
container.className = "d-flex gap-2";
container.className = "d-flex gap-2 justify-content-center";
let roomTypeBtn = document.createElement("button");
roomTypeBtn.className = "btn btn-outline-secondary btn-action";
roomTypeBtn.innerHTML = '<i class="fa fa-layer-group"></i>';
roomTypeBtn.title = this.$capitalize(this.$p.t("ui", "btn_editRoomType"));
roomTypeBtn.addEventListener("click", (event) =>
this.editRoomType(cell.getData().ort_kurzbz),
);
if (!this.hasBasisOrtWPermission) {
container.append(roomTypeBtn);
return container;
}2222
let button = document.createElement("button");
button = document.createElement("button");
button.className = "btn btn-outline-secondary btn-action";
button.innerHTML = '<i class="fa fa-edit"></i>';
button.title = this.$p.t("ui", "btn_editRoom");
button.title = this.$capitalize(this.$p.t("ui", "btn_editRoom"));
button.addEventListener("click", (event) =>
this.editRoom(cell.getData().ort_kurzbz),
);
container.append(button);
button = document.createElement("button");
button.className = "btn btn-outline-secondary btn-action";
button.innerHTML = '<i class="fa fa-layer-group"></i>';
button.title = this.$p.t("ui", "btn_editRoomType");
button.addEventListener("click", (event) =>
this.editRoomType(cell.getData().ort_kurzbz),
);
container.append(button);
container.append(roomTypeBtn);
button = document.createElement("button");
button.className =
"btn btn-outline-secondary btn-action bg-danger";
button.innerHTML = '<i class="fa fa-xmark text-white"></i>';
button.title = 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"),
@@ -441,7 +461,7 @@ export default {
template: /* html */ `
<div class="container mt-4">
<h1 class='mb-5'>{{ $capitalize($p.t("ui", "roomManagerOverviewHeading")) }}</h1>
<div class="row mb-3">
<div v-if="hasBasisOrtWPermission" class="row mb-3">
<div class="col d-flex justify-content-between">
<a class="btn btn-primary mb-3" @click="showRoomFormModal">{{$capitalize($p.t('ui', 'addRoomButton'))}}</a>
</div>
@@ -15,6 +15,7 @@ export default {
FormInput,
CoreFilterCmpt,
},
inject: ["hasBasisOrtWPermission"],
props: {
isVisible: {
type: Boolean,
@@ -79,10 +80,12 @@ export default {
{
title: this.$capitalize(this.$p.t("ui", "roomType")),
field: "raumtyp_kurzbz",
width: 150,
},
{
title: this.$capitalize(this.$p.t("ui", "hierarchy")),
field: "hierarchie",
width: 50,
},
{
title: this.$capitalize(this.$p.t("gruppenmanagement", "beschreibung")),
@@ -91,11 +94,12 @@ export default {
{
title: this.$capitalize(this.$p.t("global", "actions")),
field: "actions",
minWidth: 150,
maxWidth: 150,
width: 50,
formatter: (cell, formatterParams, onRendered) => {
if (!this.hasBasisOrtWPermission) return "";
let container = document.createElement("div");
container.className = "d-flex gap-2";
container.className = "d-flex justify-content-center";
let button = document.createElement("button");
@@ -116,6 +120,7 @@ export default {
this.deleteRoomToRoomTypeRelation(
cell.getData().ort_kurzbz,
cell.getData().raumtyp_kurzbz,
cell.getData().hierarchie,
);
});
container.append(button);
@@ -123,8 +128,10 @@ export default {
return container;
},
frozen: true,
visible: this.hasBasisOrtWPermission,
},
],
layout: "fitColumns",
};
return options;
},
@@ -199,12 +206,13 @@ export default {
this.$refs.roomTypesTable.tabulator.replaceData("/");
});
},
deleteRoomToRoomTypeRelation(roomShortCode, roomTypeShortCode) {
deleteRoomToRoomTypeRelation(roomShortCode, roomTypeShortCode, hierarchy) {
return this.$api
.call(
ApiRoomToRoomType.deleteRoomToRoomTypeRelation(
roomShortCode,
roomTypeShortCode,
hierarchy
),
)
.then((response) => {
@@ -254,14 +262,20 @@ export default {
});
},
template: /* html */ `
<bs-modal ref="roomTypeFormModal" size="sm" @hideBsModal="() => { $emit('hideBsModal'); resetRoomTypeForm(); }" class="modal-lg">
<bs-modal
ref="roomTypeFormModal"
:bodyClass="'pt-4'"
@hideBsModal="() => { $emit('hideBsModal'); resetRoomTypeForm(); }"
size="sm"
class="modal-lg"
>
<template #title>
<p class="fw-bold mt-3">{{$capitalize($p.t('ui', 'assignRoomTypeToRoomModalTitle'))}}</p>
</template>
<template #default>
<div class="justify-content-end d-flex mb-1">
<div class="d-flex justify-content-end mb-1">
<a
v-if="!isRoomTypeFormVisible"
v-if="!isRoomTypeFormVisible && hasBasisOrtWPermission"
:title='$p.t("ui", "createRoomType")'
@click.prevent="isRoomTypeFormVisible = !isRoomTypeFormVisible"
href="#"
@@ -271,67 +285,73 @@ export default {
></i>
</a>
</div>
<core-form v-if="isRoomTypeFormVisible" ref="roomTypeForm" class="row g-3 pb-3">
<div class="row">
<div class="col">
<p class="fw-bold">{{$capitalize($p.t('ui', 'createRoomTypeFormTitle'))}}</p>
<div v-if="isRoomTypeFormVisible && hasBasisOrtWPermission" class="row g-3 pb-3">
<core-form ref="roomTypeForm">
<div class="row">
<div class="col">
<p class="fw-bold">{{$capitalize($p.t('ui', 'createRoomTypeFormTitle'))}}</p>
</div>
</div>
</div>
<div class="row mb-3">
<div class="col">
<form-input
v-model="roomTypeFormData.shortCode"
:label="$capitalize($p.t('gruppenmanagement', 'kurzbezeichnung'))"
type="text"
name="kurzbezeichnung"
>
</form-input>
<div class="row mb-3">
<div class="col">
<form-input
v-model="roomTypeFormData.shortCode"
:label="$capitalize($p.t('gruppenmanagement', 'kurzbezeichnung'))"
type="text"
name="kurzbezeichnung"
>
</form-input>
</div>
<div class="col">
<form-input
v-model="roomTypeFormData.description"
:label="$capitalize($p.t('gruppenmanagement', 'beschreibung'))"
type="text"
name="beschreibung"
>
</form-input>
</div>
</div>
<div class="col">
<form-input
v-model="roomTypeFormData.description"
:label="$capitalize($p.t('gruppenmanagement', 'beschreibung'))"
type="text"
name="beschreibung"
>
</form-input>
<div class="col d-flex justify-content-end gap-2">
<button type="button" class="btn btn-secondary" @click="isRoomTypeFormVisible = false">{{$p.t('ui', 'abbrechen')}}</button>
<button type="button" class="btn btn-primary" @click="createRoomType()">{{$p.t('ui', 'speichern')}}</button>
</div>
</div>
<div class="col d-flex justify-content-end gap-2">
<button type="button" class="btn btn-secondary" @click="isRoomTypeFormVisible = false">{{$p.t('ui', 'abbrechen')}}</button>
<button type="button" class="btn btn-primary" @click="createRoomType()">{{$p.t('ui', 'speichern')}}</button>
</div>
</core-form>
<core-form v-if="!isRoomTypeFormVisible" ref="roomToRoomTypeForm" class="row g-3 pb-3">
<div class="col-8">
<form-input
v-model="roomToRoomTypeFormData.roomType"
:label="$capitalize($p.t('ui/parentRoom'))"
:suggestions="filteredRoomTypes"
:optionValue="(option) => option.value"
:optionLabel="(option) => option.label"
@complete="filterRoomTypes"
dropdown
forceSelection
type="autocomplete"
name="roomTypeShortCode"
>
</form-input>
</div>
<div class="col">
<form-input
v-model="roomToRoomTypeFormData.hierarchy"
:label="$capitalize($p.t('ui', 'hierarchy'))"
type="number"
name="hierarchy"
>
</form-input>
</core-form>
</div>
<div v-if="!isRoomTypeFormVisible && hasBasisOrtWPermission" class="row g-3 pb-3">
<core-form ref="roomToRoomTypeForm">
<div class="row">
<div class="col-8">
<form-input
v-model="roomToRoomTypeFormData.roomType"
:label="$capitalize($p.t('ui/roomType'))"
:suggestions="filteredRoomTypes"
:optionValue="(option) => option.value"
:optionLabel="(option) => option.label"
@complete="filterRoomTypes"
dropdown
forceSelection
type="autocomplete"
name="roomTypeShortCode"
>
</form-input>
</div>
<div class="col">
<form-input
v-model="roomToRoomTypeFormData.hierarchy"
:label="$capitalize($p.t('ui', 'hierarchy'))"
type="number"
name="hierarchy"
>
</form-input>
</div>
<div class="col justify-content-end align-items-end d-flex">
<button type="button" class="btn btn-primary" @click="createRoomToRoomTypeRelation()">{{$p.t('ui', 'speichern')}}</button>
</div>
</div>
<div class="col justify-content-end align-items-end d-flex">
<button type="button" class="btn btn-primary" @click="createRoomToRoomTypeRelation()">{{$p.t('ui', 'speichern')}}</button>
</div>
</core-form>
<hr>
</core-form>
</div>
<hr v-if="hasBasisOrtWPermission" class="mb-3 mt-0" />
<div class="row my-1">
<div class="col">
<p class="fw-bold">{{$capitalize($p.t('ui', 'assignedRoomTypesTitle'))}}</p>
+80
View File
@@ -58897,6 +58897,86 @@ I have been informed that I am under no obligation to consent to the transmissio
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'btn_editRoomType',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Raumtyp bearbeiten',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Edit room type',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'btn_editRoomType',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Raumtyp bearbeiten',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Edit room type',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'btn_deleteRoomToRoomTypeRelation',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Raumtyp zuweisung löschen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Delete room type assignment',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'deleteRoomToRoomTypeRelationConfirmation',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Sind Sie sicher, dass Sie diese Raumtyp Zuweisung löschen möchten?',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Are you sure you want to delete this room type assignment?',
'description' => '',
'insertvon' => 'system'
)
)
),
);