Add validation and error handling for room creation form in MultiChatDialog: Implement input validation for room name, age restrictions, password, and access rights. Enhance UI with error messages and disable button when validation fails.

This commit is contained in:
Torsten Schulz (local)
2026-03-04 22:44:15 +01:00
parent e76fdbe1ab
commit 947d3d0694

View File

@@ -63,7 +63,9 @@
<div class="room-create-grid">
<label>
Raumname *
<input v-model.trim="roomCreateForm.roomName" type="text" placeholder="z. B. Lounge" />
<input v-model.trim="roomCreateForm.roomName" type="text" placeholder="z. B. Lounge"
:class="{ 'invalid-input': roomCreateValidation.roomName }" />
<span v-if="roomCreateValidation.roomName" class="room-create-error">{{ roomCreateValidation.roomName }}</span>
</label>
<label>
Sichtbarkeit
@@ -92,23 +94,33 @@
</label>
<label>
min_age
<input v-model.number="roomCreateForm.minAge" type="number" min="0" />
<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
<input v-model.number="roomCreateForm.maxAge" type="number" min="0" />
<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" />
<input v-model.trim="roomCreateForm.password" type="text" placeholder="ohne Leerzeichen"
: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" />
<input v-model.number="roomCreateForm.rightId" type="number" min="1"
:class="{ 'invalid-input': roomCreateValidation.rightId }" />
<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" />
<input v-model.number="roomCreateForm.typeId" type="number" min="1"
:class="{ 'invalid-input': roomCreateValidation.typeId }" />
<span v-if="roomCreateValidation.typeId" class="room-create-error">{{ roomCreateValidation.typeId }}</span>
</label>
<label class="checkbox-label">
<input type="checkbox" v-model="roomCreateForm.friendsOnly" />
@@ -116,9 +128,10 @@
</label>
</div>
<div class="room-create-actions">
<button type="button" class="send-btn" @click="sendCreateRoomCommand">Raum erstellen</button>
<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>
</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>
</div>
@@ -200,6 +213,30 @@ export default {
try {
return !!(this.menu && this.menu.administration);
} catch (_) { return false; }
},
roomCreateValidation() {
const errors = {};
const name = (this.roomCreateForm.roomName || '').trim();
const minAge = this.parseOptionalInteger(this.roomCreateForm.minAge);
const maxAge = this.parseOptionalInteger(this.roomCreateForm.maxAge);
const rightId = this.parseOptionalInteger(this.roomCreateForm.rightId);
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 (minAge !== null && maxAge !== null && minAge > maxAge) {
errors.range = 'min_age darf nicht größer als max_age sein.';
}
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.';
return errors;
},
canSendRoomCreate() {
return Object.keys(this.roomCreateValidation).length === 0;
}
},
mounted() {
@@ -332,6 +369,12 @@ export default {
buildRoomCreateCommandPreview() {
return this.buildRoomCreateCommand();
},
parseOptionalInteger(value) {
if (value === null || value === undefined || value === '') return null;
const num = Number(value);
if (!Number.isFinite(num)) return null;
return Math.trunc(num);
},
buildRoomCreateCommand() {
const name = (this.roomCreateForm.roomName || '').trim();
if (!name) return '';
@@ -341,20 +384,24 @@ export default {
parts.push(`public=${this.roomCreateForm.publicFlag}`);
}
if (this.roomCreateForm.gender) parts.push(`gender=${this.roomCreateForm.gender}`);
if (Number.isInteger(this.roomCreateForm.minAge) && this.roomCreateForm.minAge >= 0) {
parts.push(`min_age=${this.roomCreateForm.minAge}`);
const minAge = this.parseOptionalInteger(this.roomCreateForm.minAge);
if (minAge !== null && minAge >= 0) {
parts.push(`min_age=${minAge}`);
}
if (Number.isInteger(this.roomCreateForm.maxAge) && this.roomCreateForm.maxAge >= 0) {
parts.push(`max_age=${this.roomCreateForm.maxAge}`);
const maxAge = this.parseOptionalInteger(this.roomCreateForm.maxAge);
if (maxAge !== null && maxAge >= 0) {
parts.push(`max_age=${maxAge}`);
}
const password = (this.roomCreateForm.password || '').trim();
if (password) parts.push(`password=${password}`);
if (this.roomCreateForm.friendsOnly) parts.push('friends_only=true');
if (Number.isInteger(this.roomCreateForm.rightId) && this.roomCreateForm.rightId > 0) {
parts.push(`right_id=${this.roomCreateForm.rightId}`);
const rightId = this.parseOptionalInteger(this.roomCreateForm.rightId);
if (rightId !== null && rightId > 0) {
parts.push(`right_id=${rightId}`);
}
if (Number.isInteger(this.roomCreateForm.typeId) && this.roomCreateForm.typeId > 0) {
parts.push(`type_id=${this.roomCreateForm.typeId}`);
const typeId = this.parseOptionalInteger(this.roomCreateForm.typeId);
if (typeId !== null && typeId > 0) {
parts.push(`type_id=${typeId}`);
}
return parts.join(' ');
},
@@ -371,15 +418,15 @@ export default {
this.messages.push({ id: Date.now(), user: 'System', text: 'Keine Verbindung zum Chat-Server.' });
return;
}
if (!this.canSendRoomCreate) {
this.messages.push({ id: Date.now(), user: 'System', text: 'Bitte Eingaben im Raum-Formular korrigieren.' });
return;
}
const command = this.buildRoomCreateCommand();
if (!command) {
this.messages.push({ id: Date.now(), user: 'System', text: 'Bitte einen Raumnamen angeben.' });
return;
}
if ((this.roomCreateForm.password || '').includes(' ')) {
this.messages.push({ id: Date.now(), user: 'System', text: 'Passwort darf keine Leerzeichen enthalten.' });
return;
}
const payload = { type: 'message', message: command };
if (this.debug) console.log('[Chat WS >>]', payload);
this.sendWithToken(payload);
@@ -1800,6 +1847,11 @@ export default {
padding: 0.35em 0.5em;
}
.invalid-input {
border-color: #c62828 !important;
background: #fff6f6;
}
.checkbox-label {
flex-direction: row !important;
align-items: center;
@@ -1826,6 +1878,15 @@ export default {
color: #444;
}
.room-create-error {
color: #b00020;
font-size: 0.85em;
}
.room-create-error-block {
margin-top: 0.5em;
}
.chat-message {
margin-bottom: 0.3em;
}