Enhance usability and localization across components: Update USABILITY_CONCEPT.md with new focus areas, improve user feedback in AppFooter and FamilyView components, and refine text in various UI elements for better clarity and consistency. Replace console logs with user-friendly messages, correct German translations, and streamline interaction logic in multiple components.
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
<div class="diary-view">
|
||||
<section class="diary-hero surface-card">
|
||||
<div>
|
||||
<span class="diary-kicker">Persoenliche Eintraege</span>
|
||||
<span class="diary-kicker">Persönliche Einträge</span>
|
||||
<h2>{{ $t('socialnetwork.diary.title') }}</h2>
|
||||
<p>Gedanken, Notizen und kurze Updates in einer ruhigen, persoenlichen Ansicht.</p>
|
||||
<p>Gedanken, Notizen und kurze Updates in einer ruhigen, persönlichen Ansicht.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -66,14 +66,13 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['user']),
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
sanitizedText(entry) {
|
||||
return DOMPurify.sanitize(entry.text);
|
||||
},
|
||||
async loadDiaryEntries(page) {
|
||||
try {
|
||||
console.log(page);
|
||||
const response = await apiClient.get(`/api/socialnetwork/diary/${page}`);
|
||||
this.diaryEntries = response.data.entries;
|
||||
this.currentPage = page;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div>
|
||||
<div class="forum-topic-back link" @click="openForum()">{{ $t('socialnetwork.forum.title') }} {{ forumName }}</div>
|
||||
<h2 v-if="forumTopic">{{ forumTopic }}</h2>
|
||||
<p>Diskussionen, Antworten und neue Beitraege in einer fokussierten Leseflaeche.</p>
|
||||
<p>Diskussionen, Antworten und neue Beiträge in einer fokussierten Lesefläche.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
<div>
|
||||
<span class="forum-kicker">Community-Forum</span>
|
||||
<h2>{{ $t('socialnetwork.forum.title') }} {{ forumName }}</h2>
|
||||
<p>Themen, Diskussionen und neue Beitraege an einem strukturierten Ort.</p>
|
||||
<p>Themen, Diskussionen und neue Beiträge an einem strukturierten Ort.</p>
|
||||
</div>
|
||||
<div class="creationtoggler">
|
||||
<button @click="createNewTopic">
|
||||
<button @click="toggleCreation">
|
||||
{{ $t(!inCreation
|
||||
? 'socialnetwork.forum.showNewTopic'
|
||||
: 'socialnetwork.forum.hideNewTopic') }}
|
||||
@@ -16,16 +16,26 @@
|
||||
</section>
|
||||
|
||||
<section v-if="inCreation" class="forum-creation surface-card">
|
||||
<div class="forum-creation__header">
|
||||
<div>
|
||||
<h3>Neues Thema verfassen</h3>
|
||||
<p>Erst Titel setzen, dann den Beitrag schreiben und anschließend direkt veröffentlichen.</p>
|
||||
</div>
|
||||
<button type="button" class="button-secondary" @click="cancelCreation">Abbrechen</button>
|
||||
</div>
|
||||
<label class="newtitle">
|
||||
<span>{{ $t('socialnetwork.forum.topic') }}</span>
|
||||
<input type="text" v-model="newTitle" />
|
||||
<input ref="titleInput" type="text" v-model="newTitle" />
|
||||
</label>
|
||||
<div class="editor-container">
|
||||
<EditorContent v-if="editor" :editor="editor" class="editor" />
|
||||
</div>
|
||||
<button @click="saveNewTopic">
|
||||
{{ $t('socialnetwork.forum.createNewTopic') }}
|
||||
</button>
|
||||
<div class="forum-creation__actions">
|
||||
<button :disabled="!canSaveTopic" @click="saveNewTopic">
|
||||
{{ $t('socialnetwork.forum.createNewTopic') }}
|
||||
</button>
|
||||
<span class="forum-creation__hint">Titel und Inhalt müssen beide ausgefüllt sein.</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-else-if="titles.length > 0" class="forum-topics surface-card">
|
||||
@@ -47,7 +57,8 @@
|
||||
</section>
|
||||
|
||||
<div v-else class="forum-empty surface-card">
|
||||
{{ $t('socialnetwork.forum.noTitles') }}
|
||||
<p>{{ $t('socialnetwork.forum.noTitles') }}</p>
|
||||
<button type="button" @click="toggleCreation">{{ $t('socialnetwork.forum.createNewTopic') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -56,6 +67,7 @@
|
||||
import { Editor, EditorContent } from '@tiptap/vue-3'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import apiClient from '../../utils/axios'
|
||||
import { showApiError, showSuccess } from '@/utils/feedback.js';
|
||||
|
||||
export default {
|
||||
name: 'ForumView',
|
||||
@@ -78,6 +90,10 @@ export default {
|
||||
},
|
||||
totalPages() {
|
||||
return Math.ceil(this.numberOfItems / 25)
|
||||
},
|
||||
canSaveTopic() {
|
||||
const content = this.editor ? this.editor.getText().trim() : '';
|
||||
return this.newTitle.trim().length >= 3 && content.length > 0;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -109,6 +125,24 @@ export default {
|
||||
if (this.editor) this.editor.destroy()
|
||||
},
|
||||
methods: {
|
||||
focusTitleInput() {
|
||||
this.$nextTick(() => this.$refs.titleInput?.focus?.());
|
||||
},
|
||||
toggleCreation() {
|
||||
this.inCreation = !this.inCreation;
|
||||
if (this.inCreation && this.editor) {
|
||||
this.editor.commands.setContent('');
|
||||
this.newTitle = '';
|
||||
this.focusTitleInput();
|
||||
}
|
||||
},
|
||||
cancelCreation() {
|
||||
this.inCreation = false;
|
||||
this.newTitle = '';
|
||||
if (this.editor) {
|
||||
this.editor.commands.setContent('');
|
||||
}
|
||||
},
|
||||
async loadForum() {
|
||||
try {
|
||||
const { data } = await apiClient.get(
|
||||
@@ -121,16 +155,9 @@ export default {
|
||||
console.error('Fehler beim Laden des Forums', err)
|
||||
}
|
||||
},
|
||||
createNewTopic() {
|
||||
this.inCreation = !this.inCreation
|
||||
if (this.inCreation && this.editor) {
|
||||
this.editor.commands.setContent('')
|
||||
this.$nextTick(() => this.editor?.commands.focus('end'))
|
||||
}
|
||||
},
|
||||
async saveNewTopic() {
|
||||
const content = this.editor ? this.editor.getHTML() : ''
|
||||
if (!this.newTitle.trim() || !content.trim()) return
|
||||
if (!this.canSaveTopic) return
|
||||
try {
|
||||
const { data } = await apiClient.post(
|
||||
'/api/forum/topic',
|
||||
@@ -147,8 +174,11 @@ export default {
|
||||
this.page = data.page
|
||||
this.inCreation = false
|
||||
this.newTitle = ''
|
||||
this.editor?.commands.setContent('')
|
||||
showSuccess(this, 'Thema erfolgreich erstellt.')
|
||||
} catch (err) {
|
||||
console.error('Fehler beim Erstellen des Themas', err)
|
||||
showApiError(this, err, 'Fehler beim Erstellen des Themas')
|
||||
}
|
||||
},
|
||||
goToPage(page) {
|
||||
@@ -211,6 +241,30 @@ export default {
|
||||
padding: 22px;
|
||||
}
|
||||
|
||||
.forum-creation__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.forum-creation__header h3 {
|
||||
margin: 0 0 6px;
|
||||
}
|
||||
|
||||
.forum-creation__header p,
|
||||
.forum-creation__hint {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.forum-creation__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.newtitle {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -289,12 +343,20 @@ export default {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.forum-empty p {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.forum-hero {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.forum-creation__header {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.topic-card__main {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<div class="guestbook-view">
|
||||
<section class="guestbook-hero surface-card">
|
||||
<div>
|
||||
<span class="guestbook-kicker">Gaestebuch</span>
|
||||
<span class="guestbook-kicker">Gästebuch</span>
|
||||
<h2>{{ $t('socialnetwork.guestbook.title') }}</h2>
|
||||
<p>Nachrichten, Rueckmeldungen und kleine Einblicke aus deinem Netzwerk.</p>
|
||||
<p>Nachrichten, Rückmeldungen und kleine Einblicke aus deinem Netzwerk.</p>
|
||||
</div>
|
||||
</section>
|
||||
<div v-if="guestbookEntries.length === 0" class="guestbook-empty surface-card">{{ $t('socialnetwork.profile.guestbook.noEntries') }}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<section class="vocab-chapter-hero surface-card">
|
||||
<span class="vocab-chapter-hero__eyebrow">Vokabeltrainer</span>
|
||||
<h2>{{ $t('socialnetwork.vocab.chapterTitle', { title: chapter?.title || '' }) }}</h2>
|
||||
<p>Kapitelinhalt durchsuchen, Vokabeln pflegen und direkt in die Uebung wechseln.</p>
|
||||
<p>Kapitelinhalt durchsuchen, Vokabeln pflegen und direkt in die Übung wechseln.</p>
|
||||
</section>
|
||||
|
||||
<section class="box surface-card">
|
||||
@@ -269,4 +269,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div>
|
||||
<span class="vocab-courses-kicker">Kurse</span>
|
||||
<h2>{{ $t('socialnetwork.vocab.courses.title') }}</h2>
|
||||
<p>Oeffentliche und eigene Lernkurse filtern, finden und direkt weiterlernen.</p>
|
||||
<p>Öffentliche und eigene Lernkurse filtern, finden und direkt weiterlernen.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -208,12 +208,7 @@ export default {
|
||||
if (!this.selectedNativeLanguageId) {
|
||||
this.selectedNativeLanguageId = 'my';
|
||||
}
|
||||
console.log(`[loadMyNativeLanguageId] Gefunden: ${nativeLanguageName} (ID: ${nativeLang.id})`);
|
||||
} else {
|
||||
console.warn(`[loadMyNativeLanguageId] Sprache "${nativeLanguageName}" nicht in languages-Liste gefunden. Verfügbare Sprachen:`, this.languages.map(l => l.name).join(', '));
|
||||
}
|
||||
} else {
|
||||
console.warn(`[loadMyNativeLanguageId] languages-Liste ist leer.`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Konnte Muttersprache nicht laden:', e);
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
<option v-for="chapter in chapters" :key="chapter.id" :value="chapter.id">{{ chapter.title }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<span v-if="lessonFormTouched && !canCreateLesson" class="form-error">Bitte Nummer, Titel und Kapitel vollstaendig angeben.</span>
|
||||
<span v-if="lessonFormTouched && !canCreateLesson" class="form-error">Bitte Nummer, Titel und Kapitel vollständig angeben.</span>
|
||||
<div class="form-actions form-actions-row">
|
||||
<button type="submit" :disabled="!canCreateLesson">{{ $t('general.create') }}</button>
|
||||
<button type="button" @click="showAddLessonDialog = false" class="button-secondary">{{ $t('general.cancel') }}</button>
|
||||
@@ -111,7 +111,7 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import apiClient from '@/utils/axios.js';
|
||||
import { confirmAction, showApiError, showSuccess } from '@/utils/feedback.js';
|
||||
import { confirmAction, showApiError, showInfo, showSuccess } from '@/utils/feedback.js';
|
||||
|
||||
export default {
|
||||
name: 'VocabCourseView',
|
||||
@@ -259,7 +259,7 @@ export default {
|
||||
showSuccess(this, 'Lektion erfolgreich angelegt.');
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Hinzufügen der Lektion:', e);
|
||||
showApiError(this, e, 'Fehler beim Hinzufuegen der Lektion');
|
||||
showApiError(this, e, 'Fehler beim Hinzufügen der Lektion');
|
||||
}
|
||||
},
|
||||
async deleteLesson(lessonId) {
|
||||
@@ -273,10 +273,10 @@ export default {
|
||||
try {
|
||||
await apiClient.delete(`/api/vocab/lessons/${lessonId}`);
|
||||
await this.loadCourse();
|
||||
showSuccess(this, 'Lektion erfolgreich geloescht.');
|
||||
showSuccess(this, 'Lektion erfolgreich gelöscht.');
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Löschen der Lektion:', e);
|
||||
showApiError(this, e, 'Fehler beim Loeschen der Lektion');
|
||||
showApiError(this, e, 'Fehler beim Löschen der Lektion');
|
||||
}
|
||||
},
|
||||
openLesson(lessonId) {
|
||||
@@ -285,9 +285,8 @@ export default {
|
||||
editCourse() {
|
||||
this.$router.push(`/socialnetwork/vocab/courses/${this.courseId}/edit`);
|
||||
},
|
||||
editLesson(lessonId) {
|
||||
// TODO: Implement edit lesson
|
||||
console.log('Edit lesson', lessonId);
|
||||
editLesson() {
|
||||
showInfo(this, 'Die Bearbeitung einzelner Lektionen folgt noch.');
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div v-else>
|
||||
<span class="vocab-language-kicker">Sprache</span>
|
||||
<h2>{{ $t('socialnetwork.vocab.languageTitle', { name: language?.name || '' }) }}</h2>
|
||||
<p>Kapitel, Suchfunktionen und Freigaben fuer diese Sprache an einem Ort.</p>
|
||||
<p>Kapitel, Suchfunktionen und Freigaben für diese Sprache an einem Ort.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<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 class="form-hint">Ein kurzer, klarer Sprachname reicht für den Start.</span>
|
||||
<span v-if="nameTouched && !canSave" class="form-error">Der Name sollte mindestens 2 Zeichen haben.</span>
|
||||
</label>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user