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"> <div class="room-create-grid">
<label> <label>
Raumname * 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>
<label> <label>
Sichtbarkeit Sichtbarkeit
@@ -92,23 +94,33 @@
</label> </label>
<label> <label>
min_age 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>
<label> <label>
max_age 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>
<label> <label>
Passwort 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>
<label> <label>
right_id 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>
<label> <label>
type_id 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>
<label class="checkbox-label"> <label class="checkbox-label">
<input type="checkbox" v-model="roomCreateForm.friendsOnly" /> <input type="checkbox" v-model="roomCreateForm.friendsOnly" />
@@ -116,9 +128,10 @@
</label> </label>
</div> </div>
<div class="room-create-actions"> <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> <button type="button" class="create-room-reset-btn" @click="resetRoomCreateForm">Zurücksetzen</button>
</div> </div>
<div v-if="roomCreateValidation.range" class="room-create-error room-create-error-block">{{ roomCreateValidation.range }}</div>
<div class="room-create-preview"> <div class="room-create-preview">
Kommando: <code>{{ buildRoomCreateCommandPreview() || '/cr <raumname>' }}</code> Kommando: <code>{{ buildRoomCreateCommandPreview() || '/cr <raumname>' }}</code>
</div> </div>
@@ -200,6 +213,30 @@ export default {
try { try {
return !!(this.menu && this.menu.administration); return !!(this.menu && this.menu.administration);
} catch (_) { return false; } } 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() { mounted() {
@@ -332,6 +369,12 @@ export default {
buildRoomCreateCommandPreview() { buildRoomCreateCommandPreview() {
return this.buildRoomCreateCommand(); 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() { buildRoomCreateCommand() {
const name = (this.roomCreateForm.roomName || '').trim(); const name = (this.roomCreateForm.roomName || '').trim();
if (!name) return ''; if (!name) return '';
@@ -341,20 +384,24 @@ export default {
parts.push(`public=${this.roomCreateForm.publicFlag}`); parts.push(`public=${this.roomCreateForm.publicFlag}`);
} }
if (this.roomCreateForm.gender) parts.push(`gender=${this.roomCreateForm.gender}`); if (this.roomCreateForm.gender) parts.push(`gender=${this.roomCreateForm.gender}`);
if (Number.isInteger(this.roomCreateForm.minAge) && this.roomCreateForm.minAge >= 0) { const minAge = this.parseOptionalInteger(this.roomCreateForm.minAge);
parts.push(`min_age=${this.roomCreateForm.minAge}`); if (minAge !== null && minAge >= 0) {
parts.push(`min_age=${minAge}`);
} }
if (Number.isInteger(this.roomCreateForm.maxAge) && this.roomCreateForm.maxAge >= 0) { const maxAge = this.parseOptionalInteger(this.roomCreateForm.maxAge);
parts.push(`max_age=${this.roomCreateForm.maxAge}`); if (maxAge !== null && maxAge >= 0) {
parts.push(`max_age=${maxAge}`);
} }
const password = (this.roomCreateForm.password || '').trim(); const password = (this.roomCreateForm.password || '').trim();
if (password) parts.push(`password=${password}`); if (password) parts.push(`password=${password}`);
if (this.roomCreateForm.friendsOnly) parts.push('friends_only=true'); if (this.roomCreateForm.friendsOnly) parts.push('friends_only=true');
if (Number.isInteger(this.roomCreateForm.rightId) && this.roomCreateForm.rightId > 0) { const rightId = this.parseOptionalInteger(this.roomCreateForm.rightId);
parts.push(`right_id=${this.roomCreateForm.rightId}`); if (rightId !== null && rightId > 0) {
parts.push(`right_id=${rightId}`);
} }
if (Number.isInteger(this.roomCreateForm.typeId) && this.roomCreateForm.typeId > 0) { const typeId = this.parseOptionalInteger(this.roomCreateForm.typeId);
parts.push(`type_id=${this.roomCreateForm.typeId}`); if (typeId !== null && typeId > 0) {
parts.push(`type_id=${typeId}`);
} }
return parts.join(' '); return parts.join(' ');
}, },
@@ -371,15 +418,15 @@ export default {
this.messages.push({ id: Date.now(), user: 'System', text: 'Keine Verbindung zum Chat-Server.' }); this.messages.push({ id: Date.now(), user: 'System', text: 'Keine Verbindung zum Chat-Server.' });
return; return;
} }
if (!this.canSendRoomCreate) {
this.messages.push({ id: Date.now(), user: 'System', text: 'Bitte Eingaben im Raum-Formular korrigieren.' });
return;
}
const command = this.buildRoomCreateCommand(); const command = this.buildRoomCreateCommand();
if (!command) { if (!command) {
this.messages.push({ id: Date.now(), user: 'System', text: 'Bitte einen Raumnamen angeben.' }); this.messages.push({ id: Date.now(), user: 'System', text: 'Bitte einen Raumnamen angeben.' });
return; 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 }; const payload = { type: 'message', message: command };
if (this.debug) console.log('[Chat WS >>]', payload); if (this.debug) console.log('[Chat WS >>]', payload);
this.sendWithToken(payload); this.sendWithToken(payload);
@@ -1800,6 +1847,11 @@ export default {
padding: 0.35em 0.5em; padding: 0.35em 0.5em;
} }
.invalid-input {
border-color: #c62828 !important;
background: #fff6f6;
}
.checkbox-label { .checkbox-label {
flex-direction: row !important; flex-direction: row !important;
align-items: center; align-items: center;
@@ -1826,6 +1878,15 @@ export default {
color: #444; color: #444;
} }
.room-create-error {
color: #b00020;
font-size: 0.85em;
}
.room-create-error-block {
margin-top: 0.5em;
}
.chat-message { .chat-message {
margin-bottom: 0.3em; margin-bottom: 0.3em;
} }