Add room creation options endpoint and integrate with chat UI: Implement getRoomCreateOptions in ChatController and ChatService, add corresponding API route, and enhance MultiChatDialog for room creation with localized labels and validation. Update i18n files for new room creation features.

This commit is contained in:
Torsten Schulz (local)
2026-03-04 23:12:54 +01:00
parent 5f4acbea51
commit 2bc34acacf
8 changed files with 251 additions and 35 deletions

View File

@@ -8,7 +8,7 @@
<option v-for="room in rooms" :key="room.id" :value="room.id">{{ room.title }}</option>
</select>
<button class="create-room-toggle-btn" type="button" @click="toggleRoomCreatePanel">
{{ showRoomCreatePanel ? 'Chat anzeigen' : 'Raum anlegen' }}
{{ showRoomCreatePanel ? $t('chat.multichat.createRoom.toggleShowChat') : $t('chat.multichat.createRoom.toggleCreateRoom') }}
</button>
</div>
<div class="right-controls">
@@ -29,7 +29,7 @@
</label>
<div class="opts-divider" v-if="isAdmin"></div>
<div class="opts-row" v-if="isAdmin">
<button class="opts-btn" type="button" @click="reloadRoomsAdmin">Räume neu laden</button>
<button class="opts-btn" type="button" @click="reloadRoomsAdmin">{{ $t('chat.multichat.reloadRooms') }}</button>
</div>
</div>
</div>
@@ -59,73 +59,81 @@
</div>
</div>
<div v-else class="room-create-panel">
<div class="room-create-title">Neuen Raum erstellen</div>
<div class="room-create-title">{{ $t('chat.multichat.createRoom.title') }}</div>
<div class="room-create-grid">
<label>
Raumname *
<input v-model.trim="roomCreateForm.roomName" type="text" placeholder="z. B. Lounge"
{{ $t('chat.multichat.createRoom.labels.roomName') }} *
<input v-model.trim="roomCreateForm.roomName" type="text" :placeholder="$t('chat.multichat.createRoom.placeholders.roomName')"
:class="{ 'invalid-input': roomCreateValidation.roomName }" />
<span v-if="roomCreateValidation.roomName" class="room-create-error">{{ roomCreateValidation.roomName }}</span>
</label>
<label>
Sichtbarkeit
{{ $t('chat.multichat.createRoom.labels.visibility') }}
<select v-model="roomCreateForm.visibility">
<option value="">(keine)</option>
<option value="">{{ $t('chat.multichat.createRoom.options.none') }}</option>
<option value="public">public</option>
<option value="private">private</option>
</select>
</label>
<label>
gender
{{ $t('chat.multichat.createRoom.labels.gender') }}
<select v-model="roomCreateForm.gender">
<option value="">(keine)</option>
<option value="">{{ $t('chat.multichat.createRoom.options.none') }}</option>
<option value="m">m</option>
<option value="f">f</option>
<option value="any">any</option>
</select>
</label>
<label>
min_age
{{ $t('chat.multichat.createRoom.labels.minAge') }}
<input v-model.number="roomCreateForm.minAge" type="number" min="0"
:class="{ 'invalid-input': roomCreateValidation.minAge }" />
<span v-if="roomCreateValidation.minAge" class="room-create-error">{{ roomCreateValidation.minAge }}</span>
</label>
<label>
max_age
{{ $t('chat.multichat.createRoom.labels.maxAge') }}
<input v-model.number="roomCreateForm.maxAge" type="number" min="0"
:class="{ 'invalid-input': roomCreateValidation.maxAge }" />
<span v-if="roomCreateValidation.maxAge" class="room-create-error">{{ roomCreateValidation.maxAge }}</span>
</label>
<label>
Passwort
<input v-model.trim="roomCreateForm.password" type="text" placeholder="ohne Leerzeichen"
{{ $t('chat.multichat.createRoom.labels.password') }}
<input v-model.trim="roomCreateForm.password" type="text" :placeholder="$t('chat.multichat.createRoom.placeholders.password')"
:class="{ 'invalid-input': roomCreateValidation.password }" />
<span v-if="roomCreateValidation.password" class="room-create-error">{{ roomCreateValidation.password }}</span>
</label>
<label>
right_id
<input v-model.number="roomCreateForm.rightId" type="number" min="1"
:class="{ 'invalid-input': roomCreateValidation.rightId }" />
{{ $t('chat.multichat.createRoom.labels.rightId') }}
<select v-model="roomCreateForm.rightId" :class="{ 'invalid-input': roomCreateValidation.rightId }">
<option :value="null">{{ $t('chat.multichat.createRoom.options.none') }}</option>
<option v-for="right in roomCreateRights" :key="`right-${right.id}`" :value="right.id">
{{ getUserRightLabel(right) }}
</option>
</select>
<span v-if="roomCreateValidation.rightId" class="room-create-error">{{ roomCreateValidation.rightId }}</span>
</label>
<label>
type_id
<input v-model.number="roomCreateForm.typeId" type="number" min="1"
:class="{ 'invalid-input': roomCreateValidation.typeId }" />
{{ $t('chat.multichat.createRoom.labels.typeId') }}
<select v-model="roomCreateForm.typeId" :class="{ 'invalid-input': roomCreateValidation.typeId }">
<option :value="null">{{ $t('chat.multichat.createRoom.options.none') }}</option>
<option v-for="roomType in roomCreateTypes" :key="`type-${roomType.id}`" :value="roomType.id">
{{ getRoomTypeLabel(roomType) }}
</option>
</select>
<span v-if="roomCreateValidation.typeId" class="room-create-error">{{ roomCreateValidation.typeId }}</span>
</label>
<label class="checkbox-label">
<input type="checkbox" v-model="roomCreateForm.friendsOnly" />
friends_only=true
{{ $t('chat.multichat.createRoom.labels.friendsOnly') }}
</label>
</div>
<div class="room-create-actions">
<button type="button" class="send-btn" @click="sendCreateRoomCommand" :disabled="!canSendRoomCreate">Raum erstellen</button>
<button type="button" class="create-room-reset-btn" @click="resetRoomCreateForm">Zurücksetzen</button>
<button type="button" class="send-btn" @click="sendCreateRoomCommand" :disabled="!canSendRoomCreate">{{ $t('chat.multichat.createRoom.actions.create') }}</button>
<button type="button" class="create-room-reset-btn" @click="resetRoomCreateForm">{{ $t('chat.multichat.createRoom.actions.reset') }}</button>
</div>
<div v-if="roomCreateValidation.range" class="room-create-error room-create-error-block">{{ roomCreateValidation.range }}</div>
<div class="room-create-preview">
Kommando: <code>{{ buildRoomCreateCommandPreview() || '/cr <raumname>' }}</code>
{{ $t('chat.multichat.createRoom.commandPrefix') }}: <code>{{ buildRoomCreateCommandPreview() || '/cr <raumname>' }}</code>
</div>
</div>
<div class="user-list">
@@ -191,7 +199,7 @@
<script>
import DialogWidget from '@/components/DialogWidget.vue';
import { fetchPublicRooms } from '@/api/chatApi.js';
import { fetchPublicRooms, fetchRoomCreateOptions } from '@/api/chatApi.js';
import { mapGetters } from 'vuex';
import { getChatWsUrl, getChatWsCandidates, getChatWsProtocols } from '@/services/chatWs.js';
@@ -215,15 +223,15 @@ export default {
const typeId = this.parseOptionalInteger(this.roomCreateForm.typeId);
const password = this.roomCreateForm.password || '';
if (!name) errors.roomName = 'Raumname ist erforderlich.';
if (minAge !== null && minAge < 0) errors.minAge = 'min_age muss >= 0 sein.';
if (maxAge !== null && maxAge < 0) errors.maxAge = 'max_age muss >= 0 sein.';
if (!name) errors.roomName = this.$t('chat.multichat.createRoom.validation.roomNameRequired');
if (minAge !== null && minAge < 0) errors.minAge = this.$t('chat.multichat.createRoom.validation.minAgeInvalid');
if (maxAge !== null && maxAge < 0) errors.maxAge = this.$t('chat.multichat.createRoom.validation.maxAgeInvalid');
if (minAge !== null && maxAge !== null && minAge > maxAge) {
errors.range = 'min_age darf nicht größer als max_age sein.';
errors.range = this.$t('chat.multichat.createRoom.validation.ageRangeInvalid');
}
if (password.includes(' ')) errors.password = 'Passwort darf keine Leerzeichen enthalten.';
if (rightId !== null && rightId <= 0) errors.rightId = 'right_id muss > 0 sein.';
if (typeId !== null && typeId <= 0) errors.typeId = 'type_id muss > 0 sein.';
if (password.includes(' ')) errors.password = this.$t('chat.multichat.createRoom.validation.passwordSpaces');
if (rightId !== null && rightId <= 0) errors.rightId = this.$t('chat.multichat.createRoom.validation.rightIdInvalid');
if (typeId !== null && typeId <= 0) errors.typeId = this.$t('chat.multichat.createRoom.validation.typeIdInvalid');
return errors;
},
@@ -283,6 +291,8 @@ export default {
rightId: null,
typeId: null
},
roomCreateRights: [],
roomCreateTypes: [],
// Palette state
paletteWidth: 420,
paletteHeight: 220,
@@ -359,6 +369,36 @@ export default {
buildRoomCreateCommandPreview() {
return this.buildRoomCreateCommand();
},
getUserRightLabel(right) {
const raw = (right?.title || '').trim();
if (!raw) return right?.id ?? '';
const candidates = [
`chat.multichat.createRoom.rights.${raw}`,
`navigation.${raw}`,
`navigation.m-admin.${raw}`
];
for (const key of candidates) {
if (this.$te(key)) return this.$t(key);
}
return raw;
},
getRoomTypeLabel(roomType) {
const raw = (roomType?.name || '').trim();
if (!raw) return roomType?.id ?? '';
const key = `chat.multichat.createRoom.types.${raw}`;
return this.$te(key) ? this.$t(key) : raw;
},
async loadRoomCreateOptions() {
try {
const options = await fetchRoomCreateOptions();
this.roomCreateRights = Array.isArray(options?.rights) ? options.rights : [];
this.roomCreateTypes = Array.isArray(options?.roomTypes) ? options.roomTypes : [];
} catch (e) {
console.error('Failed loading room create options', e);
this.roomCreateRights = [];
this.roomCreateTypes = [];
}
},
parseOptionalInteger(value) {
if (value === null || value === undefined || value === '') return null;
const num = Number(value);
@@ -406,22 +446,22 @@ export default {
},
sendCreateRoomCommand() {
if (!this.transportConnected) {
this.messages.push({ id: Date.now(), user: 'System', text: 'Keine Verbindung zum Chat-Server.' });
this.messages.push({ id: Date.now(), user: 'System', text: this.$t('chat.multichat.createRoom.messages.noConnection') });
return;
}
if (!this.canSendRoomCreate) {
this.messages.push({ id: Date.now(), user: 'System', text: 'Bitte Eingaben im Raum-Formular korrigieren.' });
this.messages.push({ id: Date.now(), user: 'System', text: this.$t('chat.multichat.createRoom.messages.invalidForm') });
return;
}
const command = this.buildRoomCreateCommand();
if (!command) {
this.messages.push({ id: Date.now(), user: 'System', text: 'Bitte einen Raumnamen angeben.' });
this.messages.push({ id: Date.now(), user: 'System', text: this.$t('chat.multichat.createRoom.messages.roomNameMissing') });
return;
}
const payload = { type: 'message', message: command };
if (this.debug) console.log('[Chat WS >>]', payload);
this.sendWithToken(payload);
this.messages.push({ id: Date.now(), user: 'System', text: `Raum-Erstellung gesendet: ${command}` });
this.messages.push({ id: Date.now(), user: 'System', text: this.$t('chat.multichat.createRoom.messages.sent', { command }) });
this.requestRoomRefreshAfterCreate();
},
selectTargetUser(name) {
@@ -520,6 +560,7 @@ export default {
this.input = '';
this.showOptions = false;
this.showRoomCreatePanel = false;
this.loadRoomCreateOptions();
this.announcedRoomEnter = false;
this.$refs.dialog.open();
// Stelle die WS-Verbindung her, wenn der Dialog geöffnet wird