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:
@@ -4,3 +4,8 @@ export const fetchPublicRooms = async () => {
|
||||
const response = await apiClient.get("/api/chat/rooms");
|
||||
return response.data; // expecting array of { id, title, ... }
|
||||
};
|
||||
|
||||
export const fetchRoomCreateOptions = async () => {
|
||||
const response = await apiClient.get("/api/chat/room-create-options");
|
||||
return response.data;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -38,6 +38,52 @@
|
||||
"connected": "Verbunden",
|
||||
"disconnected": "Getrennt",
|
||||
"error": "Fehler bei der Verbindung"
|
||||
},
|
||||
"reloadRooms": "Räume neu laden",
|
||||
"createRoom": {
|
||||
"toggleShowChat": "Chat anzeigen",
|
||||
"toggleCreateRoom": "Raum anlegen",
|
||||
"title": "Neuen Raum erstellen",
|
||||
"commandPrefix": "Kommando",
|
||||
"labels": {
|
||||
"roomName": "Raumname",
|
||||
"visibility": "Sichtbarkeit",
|
||||
"gender": "Geschlecht",
|
||||
"minAge": "min_age",
|
||||
"maxAge": "max_age",
|
||||
"password": "Passwort",
|
||||
"rightId": "right_id",
|
||||
"typeId": "type_id",
|
||||
"friendsOnly": "friends_only=true"
|
||||
},
|
||||
"placeholders": {
|
||||
"roomName": "z. B. Lounge",
|
||||
"password": "ohne Leerzeichen"
|
||||
},
|
||||
"options": {
|
||||
"none": "(keine)"
|
||||
},
|
||||
"actions": {
|
||||
"create": "Raum erstellen",
|
||||
"reset": "Zurücksetzen"
|
||||
},
|
||||
"validation": {
|
||||
"roomNameRequired": "Raumname ist erforderlich.",
|
||||
"minAgeInvalid": "min_age muss >= 0 sein.",
|
||||
"maxAgeInvalid": "max_age muss >= 0 sein.",
|
||||
"ageRangeInvalid": "min_age darf nicht größer als max_age sein.",
|
||||
"passwordSpaces": "Passwort darf keine Leerzeichen enthalten.",
|
||||
"rightIdInvalid": "right_id muss > 0 sein.",
|
||||
"typeIdInvalid": "type_id muss > 0 sein."
|
||||
},
|
||||
"messages": {
|
||||
"noConnection": "Keine Verbindung zum Chat-Server.",
|
||||
"invalidForm": "Bitte Eingaben im Raum-Formular korrigieren.",
|
||||
"roomNameMissing": "Bitte einen Raumnamen angeben.",
|
||||
"sent": "Raum-Erstellung gesendet: {command}"
|
||||
},
|
||||
"rights": {},
|
||||
"types": {}
|
||||
}
|
||||
},
|
||||
"randomchat": {
|
||||
|
||||
@@ -38,6 +38,52 @@
|
||||
"connected": "Connected",
|
||||
"disconnected": "Disconnected",
|
||||
"error": "Connection error"
|
||||
},
|
||||
"reloadRooms": "Reload rooms",
|
||||
"createRoom": {
|
||||
"toggleShowChat": "Show chat",
|
||||
"toggleCreateRoom": "Create room",
|
||||
"title": "Create new room",
|
||||
"commandPrefix": "Command",
|
||||
"labels": {
|
||||
"roomName": "Room name",
|
||||
"visibility": "Visibility",
|
||||
"gender": "Gender",
|
||||
"minAge": "min_age",
|
||||
"maxAge": "max_age",
|
||||
"password": "Password",
|
||||
"rightId": "right_id",
|
||||
"typeId": "type_id",
|
||||
"friendsOnly": "friends_only=true"
|
||||
},
|
||||
"placeholders": {
|
||||
"roomName": "e.g. Lounge",
|
||||
"password": "without spaces"
|
||||
},
|
||||
"options": {
|
||||
"none": "(none)"
|
||||
},
|
||||
"actions": {
|
||||
"create": "Create room",
|
||||
"reset": "Reset"
|
||||
},
|
||||
"validation": {
|
||||
"roomNameRequired": "Room name is required.",
|
||||
"minAgeInvalid": "min_age must be >= 0.",
|
||||
"maxAgeInvalid": "max_age must be >= 0.",
|
||||
"ageRangeInvalid": "min_age must not be greater than max_age.",
|
||||
"passwordSpaces": "Password must not contain spaces.",
|
||||
"rightIdInvalid": "right_id must be > 0.",
|
||||
"typeIdInvalid": "type_id must be > 0."
|
||||
},
|
||||
"messages": {
|
||||
"noConnection": "No connection to chat server.",
|
||||
"invalidForm": "Please correct the room form inputs.",
|
||||
"roomNameMissing": "Please enter a room name.",
|
||||
"sent": "Room creation sent: {command}"
|
||||
},
|
||||
"rights": {},
|
||||
"types": {}
|
||||
}
|
||||
},
|
||||
"randomchat": {
|
||||
|
||||
@@ -37,6 +37,52 @@
|
||||
"connected": "Conectado",
|
||||
"disconnected": "Desconectado",
|
||||
"error": "Error de conexión"
|
||||
},
|
||||
"reloadRooms": "Recargar salas",
|
||||
"createRoom": {
|
||||
"toggleShowChat": "Mostrar chat",
|
||||
"toggleCreateRoom": "Crear sala",
|
||||
"title": "Crear nueva sala",
|
||||
"commandPrefix": "Comando",
|
||||
"labels": {
|
||||
"roomName": "Nombre de la sala",
|
||||
"visibility": "Visibilidad",
|
||||
"gender": "Género",
|
||||
"minAge": "min_age",
|
||||
"maxAge": "max_age",
|
||||
"password": "Contraseña",
|
||||
"rightId": "right_id",
|
||||
"typeId": "type_id",
|
||||
"friendsOnly": "friends_only=true"
|
||||
},
|
||||
"placeholders": {
|
||||
"roomName": "p. ej. Lounge",
|
||||
"password": "sin espacios"
|
||||
},
|
||||
"options": {
|
||||
"none": "(ninguno)"
|
||||
},
|
||||
"actions": {
|
||||
"create": "Crear sala",
|
||||
"reset": "Restablecer"
|
||||
},
|
||||
"validation": {
|
||||
"roomNameRequired": "El nombre de la sala es obligatorio.",
|
||||
"minAgeInvalid": "min_age debe ser >= 0.",
|
||||
"maxAgeInvalid": "max_age debe ser >= 0.",
|
||||
"ageRangeInvalid": "min_age no puede ser mayor que max_age.",
|
||||
"passwordSpaces": "La contraseña no debe contener espacios.",
|
||||
"rightIdInvalid": "right_id debe ser > 0.",
|
||||
"typeIdInvalid": "type_id debe ser > 0."
|
||||
},
|
||||
"messages": {
|
||||
"noConnection": "Sin conexión con el servidor de chat.",
|
||||
"invalidForm": "Corrige los datos del formulario de sala.",
|
||||
"roomNameMissing": "Introduce un nombre de sala.",
|
||||
"sent": "Creación de sala enviada: {command}"
|
||||
},
|
||||
"rights": {},
|
||||
"types": {}
|
||||
}
|
||||
},
|
||||
"randomchat": {
|
||||
|
||||
Reference in New Issue
Block a user