Files
singlechat/client/src/views/ChatView.vue
Torsten Schulz (local) 0205352ae9 Enhance UI and functionality across multiple components
- Updated styles in style.css to improve overall design consistency and introduced CSS variables for better theming.
- Refined ChatWindow.vue with improved no-conversation styling and adjusted image borders for a cleaner look.
- Enhanced HistoryView.vue and InboxView.vue with new panel styles for better user experience and readability.
- Revamped LoginForm.vue to provide a more engaging user interface with a landing page layout and cookie-based profile persistence.
- Improved MenuBar.vue and SearchView.vue with active state indicators and refined item displays for better navigation.
- Added logout functionality in chat store and server routes to manage user sessions effectively.
- Introduced a new mockup view route for design previews.

These changes collectively enhance the user experience and visual appeal of the application.
2026-03-19 15:01:59 +01:00

271 lines
7.5 KiB
Vue

<template>
<div class="chat-container">
<header class="header">
<div class="app-brand">
<span class="app-brand-mark">S</span>
<div class="app-brand-copy">
<span class="app-brand-eyebrow">SingleChat</span>
<h1>Chat</h1>
</div>
</div>
<div v-if="chatStore.isLoggedIn" class="header-status">
<span class="header-status-chip">{{ chatStore.userName }}</span>
<span v-if="chatStore.isoCountryCode" class="header-status-chip">{{ chatStore.isoCountryCode }}</span>
</div>
</header>
<MenuBar v-if="chatStore.isLoggedIn" />
<div class="horizontal-box" :class="{ 'horizontal-box-login': !chatStore.isLoggedIn, 'horizontal-box-app': chatStore.isLoggedIn }">
<UserList v-if="chatStore.isLoggedIn" />
<div class="content">
<div v-if="!chatStore.isLoggedIn" class="login-screen">
<LoginForm />
</div>
<div v-else class="main-content-wrapper">
<SearchView v-if="chatStore.currentView === 'search'" />
<InboxView v-else-if="chatStore.currentView === 'inbox'" />
<HistoryView v-else-if="chatStore.currentView === 'history'" />
<div v-else class="chat-content">
<div v-if="chatStore.errorMessage" class="error-message">
{{ chatStore.errorMessage }}
</div>
<div v-if="chatStore.commandTable" class="command-table-container">
<div class="command-table-header">
<strong>{{ chatStore.commandTable.title }}</strong>
<button class="command-table-close" @click="chatStore.clearCommandTable()">Schließen</button>
</div>
<div class="command-table-scroll">
<table class="command-table">
<thead>
<tr>
<th v-for="(column, idx) in chatStore.commandTable.columns" :key="`head-${idx}`">
{{ column }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIdx) in chatStore.commandTable.rows" :key="`row-${rowIdx}`">
<td v-for="(cell, cellIdx) in row" :key="`cell-${rowIdx}-${cellIdx}`">
{{ cell }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div v-else-if="chatStore.currentConversation && currentUserInfo" class="chat-header">
<span :class="['chat-header-accent', 'chat-header-accent-' + currentUserInfo.gender]"></span>
<div class="chat-header-main">
<h2>{{ chatStore.currentConversation }}</h2>
<div class="chat-header-info">
<span v-if="currentUserInfo">{{ currentUserInfo.country }}</span>
<span v-if="currentUserInfo">{{ currentUserInfo.age }} · {{ currentUserInfo.gender }}</span>
</div>
</div>
</div>
<ChatWindow />
<ChatInput />
</div>
</div>
</div>
</div>
<ImprintContainer />
</div>
</template>
<script setup>
import { onMounted, computed } from 'vue';
import { useChatStore } from '../stores/chat';
import MenuBar from '../components/MenuBar.vue';
import UserList from '../components/UserList.vue';
import LoginForm from '../components/LoginForm.vue';
import ChatWindow from '../components/ChatWindow.vue';
import ChatInput from '../components/ChatInput.vue';
import SearchView from '../components/SearchView.vue';
import InboxView from '../components/InboxView.vue';
import HistoryView from '../components/HistoryView.vue';
import ImprintContainer from '../components/ImprintContainer.vue';
const chatStore = useChatStore();
const currentUserInfo = computed(() => {
if (!chatStore.currentConversation) return null;
return chatStore.users.find(u => u.userName === chatStore.currentConversation);
});
onMounted(async () => {
// Versuche Session wiederherzustellen
const sessionRestored = await chatStore.restoreSession();
if (!sessionRestored) {
// Keine gültige Session, versuche trotzdem WebSocket-Verbindung herzustellen
// Die Verbindung wird beim Login automatisch wiederhergestellt, falls nötig
try {
await chatStore.connectWebSocket();
} catch (error) {
console.log('WebSocket-Verbindung beim Laden fehlgeschlagen (wird beim Login automatisch wiederhergestellt):', error.message);
}
}
});
</script>
<style scoped>
.main-content-wrapper {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
overflow: hidden;
height: 100%;
}
.login-screen {
flex: 1;
min-height: 0;
padding: 28px;
display: flex;
align-items: center;
justify-content: center;
overflow: auto;
background:
radial-gradient(circle at left top, rgba(61, 134, 84, 0.2), transparent 24%),
radial-gradient(circle at right bottom, rgba(36, 92, 58, 0.12), transparent 26%),
linear-gradient(180deg, rgba(231, 241, 234, 0.95) 0%, rgba(237, 242, 238, 0.96) 48%, rgba(227, 236, 229, 0.98) 100%);
}
.horizontal-box-login {
display: block;
}
.chat-content {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
overflow: hidden;
height: 100%;
}
.chat-header {
padding: 0.7rem 1rem;
flex-shrink: 0;
border-bottom: 1px solid var(--color-border);
display: flex;
align-items: center;
gap: 0.85rem;
background: linear-gradient(180deg, rgba(225, 239, 229, 0.92) 0%, rgba(247, 250, 248, 0.9) 100%);
}
.chat-header-accent {
width: 0.6rem;
height: 2.4rem;
border-radius: 999px;
flex-shrink: 0;
}
.chat-header-accent-M {
background: linear-gradient(180deg, #5a94d2 0%, #467bb2 100%);
}
.chat-header-accent-F {
background: linear-gradient(180deg, #ff7eaa 0%, #d85f8c 100%);
}
.chat-header-accent-P {
background: linear-gradient(180deg, #e0ab46 0%, #c78a2c 100%);
}
.chat-header-accent-TF {
background: linear-gradient(180deg, #a37ac8 0%, #8b60af 100%);
}
.chat-header-accent-TM {
background: linear-gradient(180deg, #79b8d0 0%, #5fa2bf 100%);
}
.chat-header-main {
min-width: 0;
}
.chat-header h2 {
margin: 0;
font-size: 1rem;
line-height: 1.2;
color: var(--color-text-strong);
}
.chat-header-info {
margin-top: 0.18rem;
font-size: 0.75rem;
color: var(--color-text-muted);
display: flex;
flex-direction: row;
gap: 0.8rem;
align-items: center;
}
.error-message {
padding: 0.9rem 1rem;
background-color: #fff1f1;
color: #a83f3f;
border: 1px solid #efc3c3;
margin: 0.9rem;
border-radius: 10px;
text-align: center;
font-weight: bold;
}
.command-table-container {
margin: 0.9rem;
border: 1px solid var(--color-border);
border-radius: 12px;
background: var(--color-surface);
overflow: hidden;
}
.command-table-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.7rem 0.85rem;
background: var(--color-surface-subtle);
border-bottom: 1px solid var(--color-border);
}
.command-table-close {
border: 1px solid var(--color-border);
background: var(--color-surface);
padding: 0.35rem 0.7rem;
cursor: pointer;
border-radius: 8px;
}
.command-table-scroll {
max-height: 220px;
overflow: auto;
}
.command-table {
width: 100%;
border-collapse: collapse;
font-size: 0.9em;
}
.command-table th,
.command-table td {
padding: 0.5rem 0.65rem;
border-bottom: 1px solid #edf1ee;
text-align: left;
}
.command-table th {
background: #f9fbfa;
position: sticky;
top: 0;
}
</style>