Refactor backend CORS settings to include default origins and improve error handling in chat services: Introduce dynamic CORS origin handling, enhance RabbitMQ message sending with fallback mechanisms, and update WebSocket service to manage pending messages. Update UI components for better accessibility and responsiveness, including adjustments to dialog and navigation elements. Enhance styling for improved user experience across various components.
This commit is contained in:
@@ -1,40 +1,50 @@
|
||||
<template>
|
||||
<h2>{{ $t('socialnetwork.vocab.newLanguageTitle') }}</h2>
|
||||
<div class="vocab-new-language-view">
|
||||
<section class="vocab-new-language-hero surface-card">
|
||||
<span class="vocab-new-language-hero__eyebrow">Vokabeltrainer</span>
|
||||
<h2>{{ $t('socialnetwork.vocab.newLanguageTitle') }}</h2>
|
||||
<p>Neue Sprache anlegen, Freigabecode erzeugen und direkt in die Bearbeitung wechseln.</p>
|
||||
</section>
|
||||
|
||||
<div class="box">
|
||||
<label class="label">
|
||||
{{ $t('socialnetwork.vocab.languageName') }}
|
||||
<input v-model="name" type="text" />
|
||||
</label>
|
||||
<section class="box surface-card">
|
||||
<label class="label form-field">
|
||||
<span>{{ $t('socialnetwork.vocab.languageName') }}</span>
|
||||
<input v-model="name" type="text" :class="{ 'field-error': nameTouched && !canSave }" />
|
||||
<span class="form-hint">Ein kurzer, klarer Sprachname reicht fuer den Start.</span>
|
||||
<span v-if="nameTouched && !canSave" class="form-error">Der Name sollte mindestens 2 Zeichen haben.</span>
|
||||
</label>
|
||||
|
||||
<div class="actions">
|
||||
<button :disabled="saving || !canSave" @click="create">
|
||||
{{ saving ? $t('socialnetwork.vocab.saving') : $t('socialnetwork.vocab.create') }}
|
||||
</button>
|
||||
<button :disabled="saving" @click="cancel">{{ $t('Cancel') }}</button>
|
||||
</div>
|
||||
|
||||
<div v-if="created" class="created">
|
||||
<div><strong>{{ $t('socialnetwork.vocab.created') }}</strong></div>
|
||||
<div>
|
||||
{{ $t('socialnetwork.vocab.shareCode') }}:
|
||||
<code>{{ created.shareCode }}</code>
|
||||
<div class="actions form-actions-row">
|
||||
<button :disabled="saving || !canSave" @click="create">
|
||||
{{ saving ? $t('socialnetwork.vocab.saving') : $t('socialnetwork.vocab.create') }}
|
||||
</button>
|
||||
<button :disabled="saving" @click="cancel" class="button-secondary">{{ $t('Cancel') }}</button>
|
||||
</div>
|
||||
<div class="hint">{{ $t('socialnetwork.vocab.shareHint') }}</div>
|
||||
<button @click="openLanguage(created.id)">{{ $t('socialnetwork.vocab.openLanguage') }}</button>
|
||||
</div>
|
||||
|
||||
<div v-if="created" class="created">
|
||||
<div><strong>{{ $t('socialnetwork.vocab.created') }}</strong></div>
|
||||
<div>
|
||||
{{ $t('socialnetwork.vocab.shareCode') }}:
|
||||
<code>{{ created.shareCode }}</code>
|
||||
</div>
|
||||
<div class="hint">{{ $t('socialnetwork.vocab.shareHint') }}</div>
|
||||
<button @click="openLanguage(created.id)">{{ $t('socialnetwork.vocab.openLanguage') }}</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions } from 'vuex';
|
||||
import apiClient from '@/utils/axios.js';
|
||||
import { showApiError, showSuccess } from '@/utils/feedback.js';
|
||||
|
||||
export default {
|
||||
name: 'VocabNewLanguageView',
|
||||
data() {
|
||||
return {
|
||||
name: '',
|
||||
nameTouched: false,
|
||||
saving: false,
|
||||
created: null,
|
||||
};
|
||||
@@ -53,22 +63,20 @@ export default {
|
||||
this.$router.push(`/socialnetwork/vocab/${id}`);
|
||||
},
|
||||
async create() {
|
||||
this.nameTouched = true;
|
||||
if (!this.canSave) {
|
||||
return;
|
||||
}
|
||||
this.saving = true;
|
||||
try {
|
||||
const res = await apiClient.post('/api/vocab/languages', { name: this.name });
|
||||
this.created = res.data;
|
||||
// Menü sofort lokal aktualisieren (zusätzlich zum serverseitigen reloadmenu event)
|
||||
try { await this.loadMenu(); } catch (_) {}
|
||||
this.$root.$refs.messageDialog?.open(
|
||||
this.$t('socialnetwork.vocab.createdMessage'),
|
||||
this.$t('socialnetwork.vocab.createdTitle')
|
||||
);
|
||||
showSuccess(this, this.$t('socialnetwork.vocab.createdMessage'), this.$t('socialnetwork.vocab.createdTitle'));
|
||||
} catch (e) {
|
||||
console.error('Create vocab language failed:', e);
|
||||
this.$root.$refs.messageDialog?.open(
|
||||
this.$t('socialnetwork.vocab.createError'),
|
||||
this.$t('error.title')
|
||||
);
|
||||
showApiError(this, e, this.$t('socialnetwork.vocab.createError'));
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
@@ -78,29 +86,88 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.vocab-new-language-view {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
max-width: 760px;
|
||||
}
|
||||
|
||||
.vocab-new-language-hero,
|
||||
.box {
|
||||
background: #f6f6f6;
|
||||
padding: 12px;
|
||||
border: 1px solid #ccc;
|
||||
background: linear-gradient(180deg, rgba(255, 252, 247, 0.97), rgba(250, 244, 235, 0.95));
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.vocab-new-language-hero,
|
||||
.box {
|
||||
padding: 22px 24px;
|
||||
}
|
||||
|
||||
.vocab-new-language-hero__eyebrow {
|
||||
display: inline-flex;
|
||||
margin-bottom: 8px;
|
||||
padding: 4px 10px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--color-secondary-soft);
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.vocab-new-language-hero p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.label span {
|
||||
font-weight: 600;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.created {
|
||||
margin-top: 12px;
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
border: 1px solid #bbb;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin-top: 18px;
|
||||
padding: 18px;
|
||||
background: rgba(255, 255, 255, 0.68);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
.hint {
|
||||
margin-top: 6px;
|
||||
color: #555;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
background: var(--color-primary-soft);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.vocab-new-language-hero,
|
||||
.box {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.actions button,
|
||||
.created button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user