From 0205352ae9045171ca91624e71841b205bade7cf Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 19 Mar 2026 15:01:59 +0100 Subject: [PATCH] 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. --- DESIGN-KONZEPT.md | 353 ++++++++ client/src/components/ChatWindow.vue | 13 +- client/src/components/HistoryView.vue | 39 +- client/src/components/ImprintContainer.vue | 39 +- client/src/components/InboxView.vue | 38 +- client/src/components/LoginForm.vue | 325 ++++++- client/src/components/MenuBar.vue | 14 +- client/src/components/SearchView.vue | 71 +- client/src/components/UserList.vue | 19 +- client/src/router/index.js | 12 +- client/src/stores/chat.js | 35 +- client/src/style.css | 640 ++++++++----- client/src/views/ChatView.vue | 158 ++-- client/src/views/MockupView.vue | 992 +++++++++++++++++++++ server/routes.js | 34 +- 15 files changed, 2432 insertions(+), 350 deletions(-) create mode 100644 DESIGN-KONZEPT.md create mode 100644 client/src/views/MockupView.vue diff --git a/DESIGN-KONZEPT.md b/DESIGN-KONZEPT.md new file mode 100644 index 0000000..42e34aa --- /dev/null +++ b/DESIGN-KONZEPT.md @@ -0,0 +1,353 @@ +e# Design-Konzept: Modernisierung SingleChat + +## Zielbild + +SingleChat soll moderner, ruhiger und effizienter wirken, ohne seinen funktionalen Charakter zu verlieren. Die Oberfläche bleibt kompakt und schnell erfassbar, bekommt aber: + +- eine konsistentere Farbwelt +- dezentere Rundungen +- klarere Hierarchien +- bessere mobile Nutzbarkeit +- mehr optische Ruhe bei gleicher Informationsdichte + +Das Ziel ist keine komplette Neugestaltung, sondern ein kontrolliertes Redesign mit klarer Wiedererkennbarkeit. + +## Beobachtungen im aktuellen UI + +- Das Hauptgrün ist sehr dominant und wird auf vielen Flächen vollflächig eingesetzt. +- Navigation, Userliste und Chat konkurrieren visuell stark miteinander. +- Abstände und Höhen sind teilweise grob, dadurch wirkt die Oberfläche weniger präzise und nicht platzsparend. +- Farben codieren Geschlechter stark, aber ohne neutrales Basissystem um diese Akzentfarben herum. +- Mobile Nutzung ist nur eingeschränkt vorbereitet, weil die Desktop-Struktur sehr starr ist. + +## Design-Prinzipien + +- Kompakt vor luftig: wenig vertikale Höhe verschwenden. +- Neutraler Grundaufbau, Akzentfarbe nur gezielt einsetzen. +- Rundungen ja, aber klein bis mittel: modern, nicht verspielt. +- Hohe Kontraste für Lesbarkeit, aber weichere Flächenkontraste. +- Eine saubere visuelle Hierarchie: App-Rahmen, Navigation, Liste, Chat, Eingabe. +- Responsive first ab Tablet abwärts, ohne Desktop-Stärke zu verlieren. + +## Visuelle Richtung + +### Grundcharakter + +Die App soll wie ein modernes, nüchternes Messaging-Tool wirken: + +- heller, neutraler Grundton +- gedämpftes Grün als Markenfarbe +- weiche Grauabstufungen für Flächen und Grenzen +- gezielte Statusfarben statt bunter Dauerflächen + +### Farbstrategie + +Die bisherige grüne Identität bleibt erhalten, wird aber deutlich verfeinert. + +#### Primärpalette + +- `Primary 700`: `#245c3a` +- `Primary 600`: `#2f6f46` +- `Primary 500`: `#3d8654` +- `Primary 100`: `#e7f1ea` + +#### Neutrale Flächen + +- `Bg App`: `#f4f6f5` +- `Bg Panel`: `#ffffff` +- `Bg Subtle`: `#eef2ef` +- `Border`: `#d7dfd9` +- `Text Strong`: `#18201b` +- `Text Muted`: `#5f6b63` + +#### Status-/Akzentfarben + +Diese Farben nur als Marker, Badge oder kleine Flächen einsetzen, nicht mehr als große Vollflächen: + +- Info/aktiv: `#3f7cac` +- Erfolg: `#3d8654` +- Warnung: `#c78a2c` +- Fehler: `#c55252` + +#### Geschlechterkennzeichnung + +Die Geschlechterfarben sollten erhalten bleiben, aber stark abgeschwächt werden: + +- nicht als Vollton-Hintergrund der kompletten User-Zeile +- stattdessen als linke Farbleiste, Punktindikator oder Badge +- Text bleibt auf neutralem Hintergrund + +Dadurch bleibt die Kodierung sichtbar, ohne die Lesbarkeit und Ruhe zu stören. + +## Formensprache + +### Rundungen + +- Panels: `10px` +- Inputs/Buttons: `8px` +- Kleine Tags/Badges: `999px` +- Message-Bubbles: `10px` + +Damit wirkt die App zeitgemäß, bleibt aber sachlich. + +### Schatten und Linien + +- Statt starker Schatten: feine Konturen +- Schatten nur für Layer-Wechsel, z. B. mobiles Panel oder Bild-Modal +- Standardgrenze: `1px solid #d7dfd9` + +## Typografie + +Die vorhandene `Noto Sans`-Basis ist sinnvoll, vor allem wegen der Sprachabdeckung. Sie sollte beibehalten werden. + +Empfohlene Hierarchie: + +- App-Titel: `20px / 600` +- Bereichstitel: `16px / 600` +- Standardtext: `14px / 400` +- Meta-Text: `12px / 500` +- Buttons/Navigation: `13px / 600` + +Wichtig: + +- geringere Zeilenhöhen in Steuerbereichen +- mehr Gewichtsunterschied statt mehr Schriftgröße + +## Layout-Konzept + +### Desktop + +Empfohlene Struktur: + +- obere App-Bar mit Branding und Status +- darunter kompakte Aktionsleiste +- links Userliste +- rechts Hauptbereich mit Chat/Header/Input + +#### Größen + +- Header: `48px` +- Aktionsleiste: `40px` +- Userliste: `260px` Standardbreite +- Chat-Header: `52px` +- Eingabebereich: `56px` bis `64px` + +Die vertikale Verdichtung ist wichtig, damit mehr Chat-Inhalt sichtbar bleibt. + +### Tablet + +- Userliste auf `220px` reduzieren +- Menüeinträge enger setzen +- Meta-Informationen im Chat-Header stärker verdichten + +### Mobile + +Die App sollte auf kleineren Breiten nicht dreispaltig bleiben. + +Stattdessen: + +- Userliste als einblendbares Off-Canvas-Panel +- Hauptnavigation horizontal scrollbar oder als Icon/Text-Leiste +- Chatbereich füllt die Breite vollständig +- Chat-Header mit Nutzername in einer Zeile, Meta in zweiter kleiner Zeile +- Eingabebereich sticky am unteren Rand + +## Komponenten-Konzept + +### 1. Header + +Aktuell sehr schlicht. Neu: + +- weißes oder leicht getöntes Panel +- kleineres, präziseres Branding +- optional rechts Session-/Statusinformationen +- klare Unterkante mit feiner Border statt harter Farbfläche + +### 2. Menüleiste + +Ziel: + +- weniger laut +- kompakter +- besser scannbar + +Empfehlung: + +- Buttons als sekundäre Tabs oder Segment-Buttons +- aktiver Punkt über Hintergrundtönung statt kräftiger Vollfarbe +- Ungelesen-Zähler als Badge +- Timeout und aktiver Chat als Meta-Info statt als dominante Blöcke + +### 3. Userliste + +Ziel: + +- dichter, moderner, besser filterbar wirkend + +Empfehlung: + +- Zeilenhöhe ca. `36px` bis `40px` +- Flagge kleiner und sauber ausgerichtet +- Username links, Alter/Geschlecht als Meta rechts oder in zweiter reduzierter Textspur +- Geschlecht über Badge/Farbmarker statt komplette Hintergrundfarbe +- Hover nur leicht getönt +- aktive Auswahl klar, aber nicht grell + +### 4. Chat-Header + +Aktuell stark farbig nach Geschlecht. Neu: + +- neutraler Header mit kleinem Farbakzent +- Name prominent, Meta-Infos sekundär +- optional Statuspunkt oder Marker links + +Beispiel: + +- linke 4px-Akzentleiste nach Geschlecht +- weißer Hintergrund +- Name dunkel +- Alter/Land in `Text Muted` + +### 5. Nachrichtenbereich + +Ziel: + +- besser lesbare Nachrichten +- klarere Trennung zwischen eigener und fremder Nachricht +- trotzdem kompakt + +Empfehlung: + +- Nachrichten als Bubble mit leichter Tönung +- eigene Nachrichten leicht grünlich-neutral +- fremde Nachrichten weiß +- Username klein, aber klar erkennbar +- Timestamp nur per Hover oder sehr subtil +- weniger Rahmen, mehr Fläche und Abstandssystem + +### 6. Eingabebereich + +Ziel: + +- platzsparend +- mobil belastbar +- moderne Interaktion + +Empfehlung: + +- Eingabefeld als primäre Fläche +- Senden-Button kompakt +- Smiley/Bild als Icon-Buttons mit identischer Größe +- obere Border statt massiver grauer Box +- Smiley-Leiste als kleines Popover statt großer Block + +### 7. Login-Bereich + +Ziel: + +- freundlicher erster Eindruck +- kompakteres Formular + +Empfehlung: + +- Formular in Card-Layout +- zweispaltig auf breiteren Screens, einspaltig mobil +- Labels kleiner, Felder konsistent hoch +- Willkommenstext visuell vom Formular getrennt + +### 8. Tabellen und Systemmeldungen + +Empfehlung: + +- Systemmeldungen mit getönter Fläche und weicher Border +- Befehlstabellen mit sticky Header beibehalten +- Tabellen kompakter paddings, aber bessere Zeilentrennung + +## Spacing-System + +Ein festes Raster reduziert visuelle Unruhe. + +Empfehlung: + +- `4px` +- `8px` +- `12px` +- `16px` +- `24px` + +Regel: + +- Innenabstände in Controls meist `8px` oder `12px` +- Bereichsabstände meist `12px` oder `16px` +- keine beliebigen Einzelwerte mehr + +## Responsive-Regeln + +### Breakpoints + +- `>= 1200px`: voller Desktop +- `< 1200px`: kompakter Desktop/Tablet +- `< 900px`: Userliste schmaler, Navigation enger +- `< 720px`: Userliste als Overlay/Drawer +- `< 560px`: Aktionsleiste stark verdichten, nur wichtigste Texte sichtbar + +### Mobile Prioritäten + +- aktive Konversation hat Vorrang vor Nebenspalten +- Bedienelemente müssen einhändig erreichbar bleiben +- keine horizontalen Layoutbrüche +- Chatinput immer sichtbar + +## Interaktionsdetails + +- Hover-Effekte sehr leicht halten +- Fokus-Zustände klar sichtbar, farblich aus Primärpalette +- Animationen kurz und funktional, z. B. `120ms` bis `180ms` +- Keine permanente Pulsen-Animation für Inbox mehr; Badge oder sanfter Highlight-Zustand reicht meist aus + +## Technische Empfehlung für die Umsetzung + +### Design Tokens in `client/src/style.css` + +Zuerst zentrale CSS-Variablen definieren: + +- Farben +- Radius +- Shadow +- Border +- Spacing +- Höhen wichtiger UI-Bausteine + +Beispielhafte Token-Gruppen: + +- `--color-bg-app` +- `--color-bg-panel` +- `--color-border` +- `--color-primary` +- `--radius-md` +- `--space-2` +- `--header-height` + +### Danach komponentenweise umbauen + +Sinnvolle Reihenfolge: + +1. globale Tokens und App-Hintergrund +2. Header und Menüleiste +3. Userliste +4. Chat-Header und Nachrichten +5. Eingabebereich +6. Login und Nebenansichten +7. Responsive Verhalten + +## Nicht-Ziele + +- kein komplettes Rebranding +- keine starke Glasoptik +- keine großen Rundungen +- keine farblich überladene Gender-Codierung +- keine luftige SaaS-Optik mit verschwendetem Platz + +## Ergebnisbild in einem Satz + +SingleChat soll nach der Überarbeitung wie ein kompaktes, modernes Chat-Tool wirken: ruhig, klar strukturiert, responsiv, markentreu grün und deutlich hochwertiger, ohne unnötig anders auszusehen. diff --git a/client/src/components/ChatWindow.vue b/client/src/components/ChatWindow.vue index 63c8011..0f5e2bb 100644 --- a/client/src/components/ChatWindow.vue +++ b/client/src/components/ChatWindow.vue @@ -107,7 +107,10 @@ function formatTime(timestamp) { .no-conversation { padding: 20px; text-align: center; - color: #666; + color: #637067; + border: 1px dashed #d7dfd9; + border-radius: 12px; + background: rgba(255, 255, 255, 0.72); } .messages-container { @@ -118,7 +121,7 @@ function formatTime(timestamp) { .chat-image { max-width: 200px; max-height: 200px; - border-radius: 4px; + border-radius: 8px; margin-top: 0.5em; display: block; cursor: pointer; @@ -151,7 +154,8 @@ function formatTime(timestamp) { width: 80%; height: 80%; background-color: #fff; - border-radius: 8px; + border-radius: 14px; + border: 1px solid #d7dfd9; display: flex; justify-content: center; align-items: center; @@ -166,7 +170,7 @@ function formatTime(timestamp) { background-color: rgba(0, 0, 0, 0.5); color: #fff; border: none; - border-radius: 50%; + border-radius: 10px; width: 40px; height: 40px; font-size: 28px; @@ -190,4 +194,3 @@ function formatTime(timestamp) { border-radius: 4px; } - diff --git a/client/src/components/HistoryView.vue b/client/src/components/HistoryView.vue index 23c4192..f22de5c 100644 --- a/client/src/components/HistoryView.vue +++ b/client/src/components/HistoryView.vue @@ -1,8 +1,8 @@ @@ -42,4 +49,3 @@ function formatTime(seconds) { return `${minutes}:${secs.toString().padStart(2, '0')}`; } - diff --git a/client/src/components/SearchView.vue b/client/src/components/SearchView.vue index ea61e0e..9ac2d0b 100644 --- a/client/src/components/SearchView.vue +++ b/client/src/components/SearchView.vue @@ -1,6 +1,6 @@ @@ -34,4 +42,3 @@ function selectUser(userName) { } } - diff --git a/client/src/router/index.js b/client/src/router/index.js index 726573a..5234aee 100644 --- a/client/src/router/index.js +++ b/client/src/router/index.js @@ -1,6 +1,7 @@ import { createRouter, createWebHistory } from 'vue-router'; import ChatView from '../views/ChatView.vue'; import PartnersView from '../views/PartnersView.vue'; +import MockupView from '../views/MockupView.vue'; const routes = [ { @@ -22,6 +23,16 @@ const routes = [ description: 'Unsere Partner und befreundete Seiten. Entdecke weitere interessante Angebote und Communities.', keywords: 'Partner, Links, befreundete Seiten, Community' } + }, + { + path: '/mockup-redesign', + name: 'mockup-redesign', + component: MockupView, + meta: { + title: 'Design Mockup - SingleChat', + description: 'Visuelle Vorschau des geplanten Design-Refreshs fuer SingleChat.', + keywords: 'SingleChat, Mockup, Design, Redesign, Vorschau' + } } ]; @@ -76,4 +87,3 @@ router.beforeEach((to, from, next) => { }); export default router; - diff --git a/client/src/stores/chat.js b/client/src/stores/chat.js index 34e880f..0cb0d5c 100644 --- a/client/src/stores/chat.js +++ b/client/src/stores/chat.js @@ -3,6 +3,8 @@ import { ref, computed } from 'vue'; import { io } from 'socket.io-client'; export const useChatStore = defineStore('chat', () => { + const LOGOUT_MARKER_KEY = 'singlechat_logged_out'; + // State const isLoggedIn = ref(false); const userName = ref(''); @@ -346,6 +348,12 @@ export const useChatStore = defineStore('chat', () => { } async function login(userNameVal, genderVal, ageVal, countryVal) { + try { + window.localStorage.removeItem(LOGOUT_MARKER_KEY); + } catch (error) { + console.warn('Logout-Marker konnte nicht entfernt werden:', error); + } + // Stelle sicher, dass Socket.IO verbunden ist if (!socket.value || !socket.value.connected) { console.log('Socket.IO nicht verbunden, versuche Verbindung herzustellen...'); @@ -578,7 +586,22 @@ export const useChatStore = defineStore('chat', () => { } } - function logout() { + async function logout() { + try { + window.localStorage.setItem(LOGOUT_MARKER_KEY, '1'); + } catch (error) { + console.warn('Logout-Marker konnte nicht gespeichert werden:', error); + } + + try { + await fetch('/api/logout', { + method: 'POST', + credentials: 'include' + }); + } catch (error) { + console.error('Logout-Request fehlgeschlagen:', error); + } + stopTimeoutTimer(); isLoggedIn.value = false; userName.value = ''; @@ -642,6 +665,15 @@ export const useChatStore = defineStore('chat', () => { async function restoreSession() { try { + try { + if (window.localStorage.getItem(LOGOUT_MARKER_KEY) === '1') { + console.log('restoreSession: Automatische Wiederherstellung nach Logout unterdrueckt'); + return false; + } + } catch (error) { + console.warn('Logout-Marker konnte nicht gelesen werden:', error); + } + console.log('restoreSession: Starte Session-Wiederherstellung...'); const response = await fetch('/api/session', { credentials: 'include' // Wichtig für Cookies @@ -731,4 +763,3 @@ export const useChatStore = defineStore('chat', () => { restoreSession }; }); - diff --git a/client/src/style.css b/client/src/style.css index 81a1e96..4dc2311 100644 --- a/client/src/style.css +++ b/client/src/style.css @@ -1,4 +1,38 @@ -@import url('https://fonts.googleapis.com/css2?family=Noto+Sans&family=Noto+Sans+JP&family=Noto+Sans+SC&family=Noto+Sans+Thai&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;500;600&family=Noto+Sans+JP&family=Noto+Sans+SC&family=Noto+Sans+Thai&display=swap'); + +:root { + --color-bg-app: #f4f6f5; + --color-bg-shell: #edf2ee; + --color-surface: #ffffff; + --color-surface-subtle: #f6f9f7; + --color-surface-muted: #eef3ef; + --color-border: #d7dfd9; + --color-border-strong: #c7d2ca; + --color-text-strong: #18201b; + --color-text: #2c362f; + --color-text-muted: #637067; + --color-primary-700: #245c3a; + --color-primary-600: #2f6f46; + --color-primary-500: #3d8654; + --color-primary-100: #e7f1ea; + --color-blue: #467bb2; + --color-pink: #d85f8c; + --color-gold: #c78a2c; + --color-purple: #8b60af; + --color-cyan: #5fa2bf; + --radius-sm: 8px; + --radius-md: 10px; + --radius-lg: 12px; + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 20px; + --header-height: 58px; + --menu-height: 42px; + --footer-height: 34px; + --sidebar-width: 188px; +} * { margin: 0; @@ -6,11 +40,35 @@ box-sizing: border-box; } -html, body, #app { +html, +body, +#app { height: 100vh; - overflow: hidden; width: 100%; + overflow: hidden; font-family: 'Noto Sans', 'Noto Sans JP', 'Noto Sans SC', 'Noto Sans Thai', sans-serif; + color: var(--color-text); + background: var(--color-bg-app); +} + +body { + font-size: 14px; + line-height: 1.4; +} + +button, +input, +select, +textarea { + font: inherit; +} + +button { + cursor: pointer; +} + +a { + color: inherit; } .chat-container { @@ -18,89 +76,148 @@ html, body, #app { flex-direction: column; height: 100%; overflow: hidden; + background: + radial-gradient(circle at top left, rgba(61, 134, 84, 0.12), transparent 22%), + linear-gradient(180deg, var(--color-bg-app) 0%, var(--color-bg-shell) 100%); } .header { - background: #ffffff; - color: #005100; - flex-shrink: 0; -} - -.header > div, -.header > span { - display: inline-block; - vertical-align: middle; -} - -.header h1 { - padding: 0 0.5em; - margin: 0; - display: inline-block; - color: #005100; -} - -.menu { - background-color: #2E7D32; - height: 2.6em; + min-height: var(--header-height); flex-shrink: 0; display: flex; align-items: center; - padding: 0 0.4em; + justify-content: space-between; + padding: 0 var(--space-4); + background: linear-gradient(180deg, rgba(208, 232, 216, 0.98) 0%, rgba(235, 245, 238, 0.94) 55%, rgba(247, 250, 248, 0.92) 100%); + border-bottom: 1px solid var(--color-border); +} + +.app-brand { + display: flex; + align-items: center; + gap: 12px; +} + +.app-brand-mark { + width: 32px; + height: 32px; + border-radius: 9px; + display: grid; + place-items: center; + background: linear-gradient(180deg, #3d8654 0%, #245c3a 100%); + color: #fff; + font-weight: 700; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.24); +} + +.app-brand-copy { + display: flex; + flex-direction: column; +} + +.app-brand-eyebrow { + font-size: 10px; + line-height: 1; + letter-spacing: 0.08em; + text-transform: uppercase; + color: #5a6a61; +} + +.header h1 { + margin: 2px 0 0; + color: var(--color-primary-700); + font-size: 17px; + line-height: 1; + font-weight: 600; +} + +.header-status { + display: flex; + align-items: center; + gap: 8px; +} + +.header-status-chip { + min-height: 26px; + display: inline-flex; + align-items: center; + padding: 0 10px; + border-radius: 999px; + border: 1px solid #cadecf; + background: rgba(255, 255, 255, 0.7); + color: #445248; + font-size: 11px; + font-weight: 600; +} + +.menu { + min-height: var(--menu-height); + flex-shrink: 0; + display: flex; + align-items: center; + gap: var(--space-2); + padding: 5px var(--space-3); + background: rgba(247, 250, 248, 0.92); + border-bottom: 1px solid var(--color-border); + overflow-x: auto; } .menu > * { - vertical-align: top; + flex-shrink: 0; } .menu button { - background-color: #429043; - color: #ffffff; - height: 2em; - margin: 0.2em 0.4em; - cursor: pointer; - border: none; - padding: 0 0.5em; - font-size: 14px; + height: 30px; + border: 1px solid transparent; + border-radius: var(--radius-sm); + padding: 0 12px; + color: #425047; + background: transparent; + font-size: 12px; + font-weight: 600; } .menu button:hover { - background-color: #52a052; + background: rgba(231, 241, 234, 0.8); +} + +.menu button.is-active { + background: linear-gradient(180deg, #dceee1 0%, #cfe6d6 100%); + border-color: #b8d4bf; + color: #1f4f32; } .menu button.has-unread { - background-color: #ff6b6b; - animation: pulse 2s infinite; + border-color: #d7c0c0; + background: #fff1f1; + color: #9d4545; } -.menu button.has-unread:hover { - background-color: #ff5252; +.menu button.has-unread.is-active { + background: linear-gradient(180deg, #f8e4e4 0%, #f1d2d2 100%); + border-color: #ddb7b7; + color: #8e3f3f; } -@keyframes pulse { - 0%, 100% { - opacity: 1; - } - 50% { - opacity: 0.8; - } -} - -.menu span { - display: inline-block; - padding: 0.375em 0.4em; - color: #2E7D32; - border: 1px solid #fff; - background-color: lightgray; - margin: 0.1em 0.2em; +.menu-info-text { + display: inline-flex; + align-items: center; + min-height: 26px; + padding: 0 10px; + border-radius: 999px; + border: 1px solid var(--color-border); + background: var(--color-surface-subtle); + color: var(--color-text-muted); + font-size: 11px; } .menu button span { - color: #fff !important; - background-color: transparent !important; - border: none !important; - padding: 0 !important; - margin: 0 !important; - display: inline !important; + color: inherit; + background: transparent; + border: none; + padding: 0; + margin: 0; + display: inline; } .horizontal-box { @@ -110,52 +227,130 @@ html, body, #app { overflow: hidden; } +.horizontal-box-app { + gap: 14px; + padding: 14px; +} + .user-list { - width: 15em; - background-color: lightgray; - overflow-y: auto; + width: var(--sidebar-width); flex-shrink: 0; - padding: 0.5em; + display: flex; + flex-direction: column; + gap: 8px; + padding: 10px 8px; + background: linear-gradient(180deg, rgba(247, 250, 247, 0.95) 0%, rgba(242, 246, 243, 0.92) 100%); + border: 1px solid var(--color-border); + border-right: 1px solid var(--color-border); + border-radius: 16px; + box-shadow: 0 18px 40px rgba(31, 50, 39, 0.06); + overflow: hidden; } .user-list h3 { - margin-bottom: 0.5em; - font-size: 16px; + margin: 0; + font-size: 13px; + line-height: 1.2; + color: var(--color-text-muted); + font-weight: 600; +} + +.user-list-scroll { + flex: 1; + min-height: 0; + overflow-y: auto; + display: flex; + flex-direction: column; + gap: 4px; } .user-item { - cursor: pointer; - display: block; width: 100%; - padding: 0.3em 0.5em; - margin-bottom: 0.2em; + min-height: 30px; + padding: 4px 6px; + border: 1px solid rgba(217, 225, 218, 0.8); + border-radius: var(--radius-sm); + display: grid; + grid-template-columns: 28px minmax(0, 1fr) auto; + gap: 6px; + align-items: center; + background: rgba(255, 255, 255, 0.72); + text-align: left; + color: var(--color-text); } .user-item:hover { - background-color: #b0b0b0; + border-color: var(--color-border-strong); + background: rgba(255, 255, 255, 0.92); +} + +.user-item.is-active { + background: linear-gradient(180deg, rgba(236, 246, 239, 0.98) 0%, rgba(226, 239, 231, 0.96) 100%); + box-shadow: 0 8px 18px rgba(35, 54, 42, 0.06); } .user-item.gender-M { - background-color: #0066CC; - color: white; + background-image: linear-gradient(90deg, rgba(70, 123, 178, 0.22), rgba(255, 255, 255, 0.68) 72%); } .user-item.gender-F { - background-color: #FF4081; - color: white; + background-image: linear-gradient(90deg, rgba(216, 95, 140, 0.26), rgba(255, 255, 255, 0.68) 72%); } .user-item.gender-P { - background-color: #FFC107; + background-image: linear-gradient(90deg, rgba(199, 138, 44, 0.24), rgba(255, 255, 255, 0.68) 72%); } .user-item.gender-TM { - background-color: #90caf9; + background-image: linear-gradient(90deg, rgba(95, 162, 191, 0.22), rgba(255, 255, 255, 0.68) 72%); } .user-item.gender-TF { - background-color: #8E24AA; - color: #ffffff; + background-image: linear-gradient(90deg, rgba(139, 96, 175, 0.22), rgba(255, 255, 255, 0.68) 72%); +} + +.flag-icon { + width: 28px; + height: 20px; + margin: 0; + border-radius: 5px; + border: 1px solid var(--color-border); + object-fit: cover; +} + +.user-main { + min-width: 0; + display: flex; + align-items: center; + gap: 6px; + overflow: hidden; +} + +.user-name { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 12px; + font-weight: 600; + color: var(--color-text-strong); +} + +.user-country { + flex-shrink: 0; + font-size: 10px; + line-height: 1; + color: var(--color-text-muted); + text-transform: uppercase; +} + +.user-meta { + flex-shrink: 0; + text-align: right; + font-size: 11px; + font-weight: 600; + color: #536159; + white-space: nowrap; } .content { @@ -165,91 +360,126 @@ html, body, #app { min-height: 0; overflow: hidden; height: 100%; + border: 1px solid var(--color-border); + border-radius: 18px; + background: linear-gradient(180deg, rgba(255, 255, 255, 0.92) 0%, rgba(245, 248, 246, 0.94) 100%); + box-shadow: 0 18px 40px rgba(31, 50, 39, 0.06); } .chat-window { flex: 1; overflow-y: auto; - padding: 20px; - background-color: white; + padding: 18px 20px; + background: linear-gradient(180deg, #fbfdfb 0%, #f3f7f4 100%); min-height: 0; } .output-box-format { - border: 1px solid #999; - padding: 1px 6px; - margin-bottom: 0.2em; - border-radius: 3px; - line-height: 2em; + max-width: 78%; + border: 1px solid rgba(217, 226, 219, 0.9); + padding: 10px 12px; + margin-bottom: 10px; + border-radius: var(--radius-md); + line-height: 1.45; + background: linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, rgba(246, 250, 247, 0.96) 100%); + box-shadow: 0 10px 18px rgba(35, 54, 42, 0.05); +} + +.output-box-format strong { + display: block; + margin-bottom: 4px; + font-size: 11px; + color: var(--color-text-muted); } .ouput-box-format-self { - background-color: #eaeaea; + margin-left: auto; + background: linear-gradient(180deg, #dff0e4 0%, #d2e7d9 100%); + border-color: #c8dccf; } .output-box-format-other { - background-color: #fff; + background: linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, rgba(246, 250, 247, 0.96) 100%); } .chat-input-container { - padding: 10px; - background-color: #f0f0f0; + padding: 12px 16px; + background: linear-gradient(180deg, rgba(238, 245, 240, 0.92) 0%, rgba(247, 250, 248, 0.88) 100%); flex-shrink: 0; - display: flex; - gap: 10px; + display: grid; + grid-template-columns: minmax(0, 1fr) auto auto auto; + gap: 8px; align-items: center; position: relative; + border-top: 1px solid var(--color-border); } .chat-input-container input { - flex: 1; - padding: 8px; - border: 1px solid #ccc; - border-radius: 4px; + min-width: 0; + height: 40px; + padding: 0 12px; + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + background: linear-gradient(180deg, #fcfefc 0%, #f0f6f2 100%); + color: var(--color-text); } .chat-input-container button { - padding: 8px 15px; - background-color: #429043; + height: 40px; + padding: 0 14px; + background: linear-gradient(180deg, #4a8d61 0%, #2c6240 100%); color: white; - border: solid 1px #999; - border-radius: 0; - cursor: pointer; - min-height: 2.3em; + border: 1px solid #295f3d; + border-radius: var(--radius-sm); + min-height: 40px; + font-weight: 600; } .chat-input-container button:hover { - background-color: #52a052; + filter: brightness(1.02); } .chat-input-container .no-style { - border: none; - background: none; + width: 40px; + height: 40px !important; + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + background: linear-gradient(180deg, #fdfefd 0%, #edf4ef 100%); padding: 0; - margin: 0; - outline: none; - cursor: pointer; - width: 31px !important; - height: 29px !important; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.chat-input-container .no-style:disabled { + opacity: 0.45; + cursor: default; } .chat-input-container .no-style > img { - width: 31px; - height: 31px; + width: 20px; + height: 20px; } .imprint-container { - background-color: #f0f0f0; - padding: 10px 20px; + min-height: var(--footer-height); + padding: 0 16px; text-align: center; - font-size: 12px; + font-size: 11px; flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + gap: 18px; + background: rgba(255, 255, 255, 0.94); + border-top: 1px solid var(--color-border); } .imprint-container a { - color: #005100; + color: #54635a; text-decoration: none; - margin: 0 10px; + font-weight: 500; + margin: 0; } .imprint-container a:hover { @@ -258,8 +488,7 @@ html, body, #app { .login-form { padding: 20px; - max-width: 600px; - margin: 0 auto; + max-width: 720px; } .login-content { @@ -267,6 +496,10 @@ html, body, #app { flex-direction: column; gap: 20px; max-width: 40em; + padding: 18px; + border: 1px solid var(--color-border); + border-radius: 14px; + background: rgba(255, 255, 255, 0.86); } .form-row { @@ -278,134 +511,91 @@ html, body, #app { .form-row label { min-width: 100px; + color: var(--color-text-muted); } .form-row input, .form-row select { flex: 1; - padding: 5px; + height: 38px; + padding: 0 10px; + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + background: var(--color-surface); } .form-row button { - padding: 8px 15px; - background-color: #429043; + padding: 0 15px; + background: linear-gradient(180deg, #4a8d61 0%, #2c6240 100%); color: white; - border: solid 1px #999; - border-radius: 0; + border: 1px solid #295f3d; + border-radius: var(--radius-sm); cursor: pointer; justify-self: start; - min-height: 2.3em; -} - -.form-row button:hover { - background-color: #52a052; + min-height: 38px; + font-weight: 600; } .welcome-message { - margin-top: 20px; - padding: 20px; - background-color: #f9f9f9; - border-radius: 4px; -} - -.search-form { - padding: 20px; -} - -.search-form .form-row { - margin-bottom: 15px; -} - -.search-results { - padding: 20px; -} - -.search-result-item { - padding: 10px; - border-bottom: 1px solid #ddd; - cursor: pointer; -} - -.search-result-item:hover { - background-color: #f0f0f0; + padding: 16px; + background: var(--color-surface-subtle); + border: 1px solid var(--color-border); + border-radius: 12px; } +.search-form, +.search-results, .inbox-list, -.history-list { - padding: 20px; +.history-list, +.partners-view { + padding: 18px 20px; } +.search-result-item, .inbox-item, -.history-item { - padding: 10px; - border-bottom: 1px solid #ddd; +.history-item, +.partners-list li { + padding: 10px 12px; + border-bottom: 1px solid #e3e8e4; cursor: pointer; } +.search-result-item:hover, .inbox-item:hover, .history-item:hover { - background-color: #f0f0f0; -} - -.partners-view { - padding: 20px; + background-color: #f4f7f4; } .back-link { margin-bottom: 1em; } -.back-link a { - color: #429043; +.back-link a, +.partners-list a { + color: var(--color-primary-700); text-decoration: underline; - font-weight: bold; -} - -.back-link a:hover { - color: #2E7D32; + font-weight: 600; } .partners-list { list-style: none; } -.partners-list li { - padding: 10px; - border-bottom: 1px solid #ddd; -} - -.partners-list a { - color: #005100; - text-decoration: none; -} - -.imprint-container a { - color: #005100; - text-decoration: none; - margin: 0 10px; -} - -.flag-icon { - margin: 0.25em 0.5em 0 0; - width: 16px; - height: 12px; - vertical-align: middle; -} - .smiley-bar { display: flex; flex-direction: row; flex-wrap: wrap; - max-width: 200px; - bottom: 89px; + max-width: 220px; position: absolute; + bottom: calc(100% + 8px); + right: 16px; font-size: 24pt; - right: 3px; - background-color: #fff; - border: 1px solid #ccc; + background-color: var(--color-surface); + border: 1px solid var(--color-border); padding: 0.3em; - border-radius: 4px; + border-radius: 12px; z-index: 10; + box-shadow: 0 16px 30px rgba(31, 50, 39, 0.12); } .smiley-item { @@ -413,13 +603,53 @@ html, body, #app { padding: 0.2em; margin: 0.1em; display: inline-block; + border-radius: 8px; } .smiley-item:hover { - background-color: #f0f0f0; + background-color: #f0f4f1; } -.partners-list a:hover { - text-decoration: underline; +@media (max-width: 960px) { + .user-list { + width: 170px; + } + + .menu { + flex-wrap: wrap; + } + + .horizontal-box-app { + gap: 10px; + padding: 10px; + } } +@media (max-width: 720px) { + .horizontal-box { + flex-direction: column; + } + + .user-list { + width: 100%; + max-height: 150px; + border-right: 1px solid var(--color-border); + border-bottom: 1px solid var(--color-border); + } + + .content { + border-radius: 16px; + } + + .chat-input-container { + grid-template-columns: minmax(0, 1fr) auto auto; + } + + .chat-input-container button:not(.no-style) { + padding: 0 12px; + } + + .header-status { + display: none; + } +} diff --git a/client/src/views/ChatView.vue b/client/src/views/ChatView.vue index d760180..30b4ba7 100644 --- a/client/src/views/ChatView.vue +++ b/client/src/views/ChatView.vue @@ -1,16 +1,26 @@