From 0c47dce508adb0e79593d537b96e4642ae21518f Mon Sep 17 00:00:00 2001 From: Pepe44DEV Date: Thu, 2 Jul 2026 12:34:53 +0200 Subject: [PATCH] migrate dashboard to real HeroUI v3 components and neutral theme The frontend was built against HeroUI v2 props/classes (variant="light"/"flat", color="primary", startContent, text-tiny, ) while @heroui/react ^3.2.1 is installed, so most styling was silently ignored. Rewrites every page/component onto the actual v3 API (variant/color enums, Tooltip/Dropdown trigger-content structure, Avatar.Image/Fallback via a new AppAvatar helper) and replaces the custom orange branding with HeroUI's neutral default theme tokens. Also compacts the dashboard stat tiles, narrows the sidebar, and swaps the activity stat grid for a single-hue comparison bar chart. Co-Authored-By: Claude Sonnet 5 --- frontend/package-lock.json | 1 + frontend/src/app.css | 151 ++-------------- frontend/src/components/layout/AppLayout.tsx | 7 +- frontend/src/components/layout/Header.tsx | 52 +++--- frontend/src/components/layout/Sidebar.tsx | 81 +++++---- frontend/src/components/layout/Topbar.tsx | 6 +- .../src/components/shared/ActivityTile.tsx | 4 +- frontend/src/components/shared/AppAvatar.tsx | 17 ++ .../components/shared/BarComparisonChart.tsx | 29 ++++ frontend/src/components/shared/EmptyState.tsx | 6 +- frontend/src/components/shared/ErrorState.tsx | 8 +- frontend/src/components/shared/ListPanel.tsx | 2 +- .../src/components/shared/LoadingSkeleton.tsx | 10 +- .../src/components/shared/SectionCard.tsx | 4 +- frontend/src/components/shared/StatCard.tsx | 40 +++-- frontend/src/pages/Admin.tsx | 38 ++-- frontend/src/pages/Automod.tsx | 26 +-- frontend/src/pages/Birthday.tsx | 40 +++-- frontend/src/pages/Dashboard.tsx | 99 ++++++----- frontend/src/pages/DynamicVoice.tsx | 22 +-- frontend/src/pages/Events.tsx | 24 +-- frontend/src/pages/GuildSelect.tsx | 19 +- frontend/src/pages/Modules.tsx | 20 +-- frontend/src/pages/Music.tsx | 26 +-- frontend/src/pages/ReactionRoles.tsx | 30 ++-- frontend/src/pages/Register.tsx | 62 +++---- frontend/src/pages/ServerStats.tsx | 28 +-- frontend/src/pages/Settings.tsx | 12 +- frontend/src/pages/Statuspage.tsx | 26 +-- frontend/src/pages/SupportLogin.tsx | 44 ++--- frontend/src/pages/Tickets.tsx | 162 +++++++++--------- frontend/src/pages/Welcome.tsx | 25 +-- 32 files changed, 537 insertions(+), 584 deletions(-) create mode 100644 frontend/src/components/shared/AppAvatar.tsx create mode 100644 frontend/src/components/shared/BarComparisonChart.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4870547..c6d8423 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@heroui/react": "^3.2.1", + "@heroui/styles": "^3.2.1", "framer-motion": "^12.23.24", "lucide-react": "^0.542.0", "react": "^19.2.0", diff --git a/frontend/src/app.css b/frontend/src/app.css index 079ba0b..7bdc9d9 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -3,22 +3,6 @@ @custom-variant dark (&:is(.dark *)); -:root { - --app-bg: #110f0d; - --app-bg-soft: #181513; - --app-panel: #1d1a1f; - --app-panel-strong: #221e24; - --app-panel-muted: #171419; - --app-border: rgba(255, 255, 255, 0.08); - --app-border-strong: rgba(255, 166, 77, 0.22); - --app-text: #f6f2ee; - --app-text-muted: #b8aca1; - --app-accent: #ff8a3d; - --app-accent-strong: #ff6a00; - --app-accent-soft: rgba(255, 138, 61, 0.14); - --app-accent-glow: rgba(255, 138, 61, 0.28); -} - html, body, #root { min-height: 100%; } @@ -26,11 +10,8 @@ html, body, #root { body { margin: 0; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; - background: - radial-gradient(circle at top left, rgba(255, 106, 0, 0.12), transparent 22%), - radial-gradient(circle at top right, rgba(255, 138, 61, 0.08), transparent 18%), - linear-gradient(180deg, #0f0d0c 0%, #141110 100%); - color: var(--app-text); + background: var(--background); + color: var(--foreground); } * { @@ -41,137 +22,37 @@ button { cursor: pointer; } -input, -textarea, select { width: 100%; - border: 1px solid rgba(255, 255, 255, 0.14); - border-radius: 14px; - background: rgba(255, 255, 255, 0.05); - color: inherit; + border: var(--border-width-field) solid var(--field-border, var(--border)); + border-radius: var(--radius-field); + background: var(--field-background, var(--default)); + color: var(--field-foreground, var(--foreground)); font: inherit; - padding: 0.85rem 1rem; - transition: border-color 160ms ease, background-color 160ms ease, box-shadow 160ms ease; + padding: 0.6rem 0.85rem; + transition: border-color 150ms ease, background-color 150ms ease; } -textarea { - min-height: 120px; - resize: vertical; -} - -input::placeholder, -textarea::placeholder { - color: rgba(255, 255, 255, 0.42); -} - -input:hover, -textarea:hover, select:hover { - border-color: rgba(255, 255, 255, 0.22); - background: rgba(255, 255, 255, 0.07); + background: var(--field-hover); } -input:focus, -textarea:focus, select:focus { outline: none; - border-color: var(--app-accent); - box-shadow: 0 0 0 3px rgba(255, 138, 61, 0.16); - background: rgba(255, 255, 255, 0.08); + border-color: var(--field-border-focus); + background: var(--field-focus); } label { display: inline-block; - margin-bottom: 0.45rem; - font-size: 0.95rem; - font-weight: 600; -} - -.surface-card { - background: linear-gradient(180deg, rgba(31, 27, 33, 0.96), rgba(23, 20, 25, 0.96)); - border: 1px solid var(--app-border); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03); -} - -.surface-card-strong { - background: linear-gradient(180deg, rgba(36, 30, 24, 0.98), rgba(25, 21, 18, 0.98)); - border: 1px solid var(--app-border-strong); - box-shadow: - inset 0 1px 0 rgba(255, 255, 255, 0.03), - 0 18px 50px rgba(0, 0, 0, 0.28); -} - -.surface-card-muted { - background: rgba(255, 255, 255, 0.025); - border: 1px solid rgba(255, 255, 255, 0.06); -} - -.accent-text { - color: var(--app-accent); -} - -.accent-chip { - background: var(--app-accent-soft); - color: #ffb37f; - border: 1px solid rgba(255, 138, 61, 0.18); -} - -.accent-button { - background: linear-gradient(135deg, var(--app-accent) 0%, var(--app-accent-strong) 100%); - color: white; - border: 1px solid rgba(255, 166, 77, 0.45); - box-shadow: 0 10px 24px rgba(255, 106, 0, 0.18); -} - -.accent-button:hover { - filter: brightness(1.06); -} - -.accent-button-soft { - background: var(--app-accent-soft); - color: #ffbc8e; - border: 1px solid rgba(255, 138, 61, 0.18); -} - -.ghost-button { - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.09); - color: var(--app-text); -} - -.nav-button { - color: var(--app-text-muted); -} - -.nav-button:hover { - background: rgba(255, 138, 61, 0.08); - color: var(--app-text); -} - -.nav-button-active { - background: linear-gradient(135deg, rgba(255, 138, 61, 0.22), rgba(255, 106, 0, 0.24)); - color: white; - border: 1px solid rgba(255, 138, 61, 0.28); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); -} - -.sidebar-shell { - background: linear-gradient(180deg, rgba(16, 14, 13, 0.98), rgba(18, 15, 14, 0.98)); - border-right: 1px solid rgba(255, 255, 255, 0.05); -} - -.brand-mark { - background: linear-gradient(135deg, var(--app-accent) 0%, var(--app-accent-strong) 100%); - box-shadow: 0 12px 30px rgba(255, 106, 0, 0.22); -} - -.section-subtle { - color: var(--app-text-muted); + margin-bottom: 0.35rem; + font-size: 0.875rem; + font-weight: 500; + color: var(--foreground); } .scrollbar-thin { scrollbar-width: thin; - scrollbar-color: rgba(255, 255, 255, 0.15) transparent; } .scrollbar-thin::-webkit-scrollbar { @@ -180,6 +61,6 @@ label { } .scrollbar-thin::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.15); + background: var(--border); border-radius: 999px; } diff --git a/frontend/src/components/layout/AppLayout.tsx b/frontend/src/components/layout/AppLayout.tsx index 98d025f..050d002 100644 --- a/frontend/src/components/layout/AppLayout.tsx +++ b/frontend/src/components/layout/AppLayout.tsx @@ -8,8 +8,9 @@ export function AppLayout({ children }: { children: React.ReactNode }) { if (loading) { return ( -
- +
+ +

Dashboard wird geladen...

); } @@ -17,7 +18,7 @@ export function AppLayout({ children }: { children: React.ReactNode }) { if (!guilds.length) { return (
-

Keine Server verfügbar

+

Keine Server verfügbar

); } diff --git a/frontend/src/components/layout/Header.tsx b/frontend/src/components/layout/Header.tsx index c777222..87b5f8b 100644 --- a/frontend/src/components/layout/Header.tsx +++ b/frontend/src/components/layout/Header.tsx @@ -1,7 +1,8 @@ -import { Button, Chip, Tooltip, Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Avatar } from '@heroui/react'; +import { Button, Chip, Tooltip, Dropdown, DropdownTrigger, DropdownPopover, DropdownMenu, DropdownItem } from '@heroui/react'; import { Moon, Sun, ChevronDown, LogOut, Settings } from 'lucide-react'; import { useTheme } from '../../hooks/useTheme'; import { useApp } from '../../context/AppContext'; +import { AppAvatar } from '../shared/AppAvatar'; const navLabels: Record = { overview: 'Übersicht', @@ -30,14 +31,14 @@ export function Header() { return (
-
- {section !== 'overview' && ( <> / - {navLabels[section] || section} + {navLabels[section] || section} )}
@@ -45,33 +46,36 @@ export function Header() {
{statusMessage && ( - + {statusMessage} )} - - + + + + + {dark ? 'Helles Design' : 'Dunkles Design'} - - - + + + + {user?.username} + - - } onPress={() => setSection('settings')}> - Einstellungen - - } color="danger" onPress={handleLogout}> - Abmelden - - + + + setSection('settings')}> + Einstellungen + + + Abmelden + + +
diff --git a/frontend/src/components/layout/Sidebar.tsx b/frontend/src/components/layout/Sidebar.tsx index ff817ce..35ecc9b 100644 --- a/frontend/src/components/layout/Sidebar.tsx +++ b/frontend/src/components/layout/Sidebar.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import { - Avatar, Button, Card, CardContent, ScrollShadow, Tooltip, + Button, Card, CardContent, ScrollShadow, Tooltip, Select, SelectTrigger, SelectValue, SelectPopover, ListBox, ListBoxItem } from '@heroui/react'; import { @@ -9,6 +9,7 @@ import { Shield, Sparkles, Tag, Ticket, Wrench } from 'lucide-react'; import { useApp } from '../../context/AppContext'; +import { AppAvatar } from '../shared/AppAvatar'; const navGroups = [ { @@ -63,13 +64,13 @@ export function Sidebar() { const [collapsed, setCollapsed] = useState(false); return ( -