mobile desighn test4

#deploy
This commit is contained in:
martin 2026-01-21 14:32:22 +01:00
parent 6a0381ff99
commit af78237985
10 changed files with 105 additions and 16 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -7,6 +7,7 @@ import { BrowserRouter, Routes, Route } from "react-router-dom";
import { HelmetProvider } from "react-helmet-async";
import { LanguageProvider } from "@/contexts/LanguageContext";
import { AuthProvider, useAuth } from "@/contexts/AuthContext";
import { ThemeProvider } from "@/contexts/ThemeContext";
import Main from "./components/app/Main";
import LoginContainer from "./components/auth/LoginContainer";
import ArtistCollabForm from "@/features/event/components/ArtistCollabForm";
@ -31,6 +32,7 @@ const App = () => (
<HelmetProvider>
<QueryClientProvider client={queryClient}>
<TooltipProvider>
<ThemeProvider>
<LanguageProvider>
<AuthProvider>
<Toaster />
@ -40,6 +42,7 @@ const App = () => (
</BrowserRouter>
</AuthProvider>
</LanguageProvider>
</ThemeProvider>
</TooltipProvider>
</QueryClientProvider>
</HelmetProvider>

View File

@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react';
import { Button } from "@/components/ui/button";
import { LogOut, Menu } from 'lucide-react';
import { LogOut, Menu, Moon, Sun } from 'lucide-react';
import { useAuth } from '@/contexts/AuthContext';
import { useTheme } from '@/contexts/ThemeContext';
import { useNavigate } from 'react-router-dom';
import DashboardContainer from '../dashboard/DashboardContainer';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
@ -17,6 +18,7 @@ const MainShell: React.FC = () => {
}, [resetHeader]);
const { logout } = useAuth();
const { theme, toggleTheme } = useTheme();
const navigate = useNavigate();
const [hasActiveEvent, setHasActiveEvent] = useState(false);
// header is already available above
@ -71,13 +73,13 @@ const MainShell: React.FC = () => {
<img
src="/icon.png"
alt="Logo"
className="h-9 w-9 object-contain"
className="h-9 w-9 object-contain dark:invert dark:brightness-125"
/>
{!header.left && (
<img
src="/Logo.png"
alt="Logo"
className="hidden md:block h-10 w-auto object-contain"
className="hidden md:block h-10 w-auto object-contain dark:invert dark:brightness-125"
/>
)}
</div>
@ -91,6 +93,10 @@ const MainShell: React.FC = () => {
<DropdownMenuContent align="start">
{header.mobileMenu}
{header.mobileMenu ? <DropdownMenuSeparator /> : null}
<DropdownMenuItem onClick={toggleTheme} className="gap-2">
{theme === 'dark' ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
<span>{theme === 'dark' ? 'Light Mode' : 'Dark Mode'}</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={logout} className="gap-2">
<LogOut className="h-4 w-4" />
<span>Abmelden</span>
@ -108,6 +114,15 @@ const MainShell: React.FC = () => {
</div>
<div className="flex items-center gap-2 pointer-events-auto">
<Button
variant="ghost"
size="icon"
className="hidden md:inline-flex"
onClick={toggleTheme}
aria-label={theme === 'dark' ? 'Light Mode aktivieren' : 'Dark Mode aktivieren'}
>
{theme === 'dark' ? <Sun className="h-5 w-5" /> : <Moon className="h-5 w-5" />}
</Button>
<Button
variant="outline"
size="sm"

View File

@ -31,7 +31,7 @@ const LoginForm: React.FC<LoginFormProps> = ({ onSubmit, isLoading: externalLoad
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-primary/20 to-secondary/20">
<Card className="w-full max-w-md">
<CardHeader>
<img src="/Logo.png" alt="Logo" className="mx-auto mb-2 h-24 w-40 object-contain" />
<img src="/Logo.png" alt="Logo" className="mx-auto mb-2 h-24 w-40 object-contain dark:invert dark:brightness-125" />
<CardTitle className="text-2xl font-bold text-center">Login Erforderlich</CardTitle>
</CardHeader>
<CardContent>

View File

@ -4,6 +4,7 @@ import { type Event } from '@/features/event/api/eventService';
import { type DashboardModule, type DashboardModuleContext } from '@/components/dashboard/dashboardModuleTypes';
import { eventDashboardModule } from '@/features/event/dashboardModule';
import { quickPostDashboardModule } from '@/features/photopost/dashboardModule';
import { useTheme } from '@/contexts/ThemeContext';
const DASHBOARD_ACTIVE_MODULE_KEY = 'kgb.dashboard.activeModuleId';
@ -51,6 +52,7 @@ interface DashboardProps {
}
const Dashboard: React.FC<DashboardProps> = ({ onActionClick, hasActiveEvent, currentEvent }) => {
const { theme } = useTheme();
const [activeModuleId, setActiveModuleId] = useState<string | null>(() => {
try {
return sessionStorage.getItem(DASHBOARD_ACTIVE_MODULE_KEY);
@ -110,7 +112,11 @@ const Dashboard: React.FC<DashboardProps> = ({ onActionClick, hasActiveEvent, cu
title={module.getTitle(ctx)}
description={module.getDescription(ctx)}
onClick={() => setActiveModuleId(module.id)}
illustration={module.getIllustration?.(ctx)}
illustration={
theme === 'dark'
? module.getIllustrationDark?.(ctx) ?? module.getIllustration?.(ctx)
: module.getIllustration?.(ctx)
}
/>
))}
</div>

View File

@ -10,6 +10,7 @@ export interface DashboardModule {
getTitle: (ctx: DashboardModuleContext) => string;
getDescription: (ctx: DashboardModuleContext) => string;
getIllustration?: (ctx: DashboardModuleContext) => string | undefined;
getIllustrationDark?: (ctx: DashboardModuleContext) => string | undefined;
isVisible?: (ctx: DashboardModuleContext) => boolean;
renderWizard: (ctx: DashboardModuleContext, onBack: () => void) => React.ReactNode;
}

View File

@ -0,0 +1,62 @@
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
type Theme = 'light' | 'dark';
type ThemeContextValue = {
theme: Theme;
setTheme: (next: Theme) => void;
toggleTheme: () => void;
};
const THEME_STORAGE_KEY = 'kgb.theme';
const ThemeContext = createContext<ThemeContextValue | null>(null);
function getSystemTheme(): Theme {
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return 'light';
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
function applyThemeToDom(theme: Theme) {
if (typeof document === 'undefined') return;
document.documentElement.classList.toggle('dark', theme === 'dark');
}
export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [theme, setThemeState] = useState<Theme>(() => {
try {
const stored = localStorage.getItem(THEME_STORAGE_KEY);
if (stored === 'light' || stored === 'dark') return stored;
} catch {
// ignore
}
return getSystemTheme();
});
useEffect(() => {
applyThemeToDom(theme);
try {
localStorage.setItem(THEME_STORAGE_KEY, theme);
} catch {
// ignore
}
}, [theme]);
const setTheme = useCallback((next: Theme) => {
setThemeState(next);
}, []);
const toggleTheme = useCallback(() => {
setThemeState((prev) => (prev === 'dark' ? 'light' : 'dark'));
}, []);
const value = useMemo(() => ({ theme, setTheme, toggleTheme }), [theme, setTheme, toggleTheme]);
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
};
export const useTheme = () => {
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error('useTheme must be used within ThemeProvider');
return ctx;
};

View File

@ -1,7 +1,7 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { DropdownMenuItem } from '@/components/ui/dropdown-menu';
import { DropdownMenuItem, DropdownMenuSeparator } from '@/components/ui/dropdown-menu';
import { type Event } from '@/features/event/api/eventService';
import { ArrowLeft, LayoutGrid } from 'lucide-react';
import { useHeader } from '@/contexts/HeaderContext';
@ -103,6 +103,7 @@ const EventWizard: React.FC<EventWizardProps> = ({ onComplete, onBackToOverview,
<LayoutGrid className="h-4 w-4" />
<span>Zurück zum Dashboard</span>
</DropdownMenuItem>
{showForm ? <DropdownMenuSeparator /> : null}
{showForm && (
<DropdownMenuItem onClick={handleBackToTimeline} className="gap-2">
<ArrowLeft className="h-4 w-4" />

View File

@ -7,6 +7,7 @@ export const eventDashboardModule: DashboardModule = {
getTitle: () => 'Veranstaltung',
getDescription: () => 'Verwalte deine Veranstaltungen',
getIllustration: () => '/images/robo/robo_event.png',
getIllustrationDark: () => '/images/robo/robo_event_dark.png',
renderWizard: (_ctx, onBack) => (
<EventWizardContainer onComplete={onBack} onBackToOverview={onBack} />
),

View File

@ -69,7 +69,7 @@
--accent: 175 70% 41%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive: 0 84.2% 62%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;