From dd9a2a3da9f2cecc4e393bebd0f1c8f131f969ea Mon Sep 17 00:00:00 2001 From: HotelSync Date: Tue, 17 Mar 2026 18:22:37 +0300 Subject: [PATCH] Add auto theme mode (light/dark/auto by time of day) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ThemeContext: add 'auto' mode that switches light 7:00-22:00, dark otherwise, re-checks every minute - Topbar: cycle through 3 modes on click, show Monitor icon for auto mode - Settings → Внешний вид: 3-button grid with split preview for auto Co-Authored-By: Claude Sonnet 4.6 --- src/components/layout/Topbar.tsx | 8 +++--- src/contexts/ThemeContext.tsx | 44 +++++++++++++++++++++++++++----- src/pages/SettingsPage.tsx | 33 ++++++++++++++++-------- 3 files changed, 63 insertions(+), 22 deletions(-) diff --git a/src/components/layout/Topbar.tsx b/src/components/layout/Topbar.tsx index 7aa6daa..f3feec3 100644 --- a/src/components/layout/Topbar.tsx +++ b/src/components/layout/Topbar.tsx @@ -1,4 +1,4 @@ -import { Sun, Moon, Bell, LogOut, ChevronDown, Menu, +import { Sun, Moon, Monitor, Bell, LogOut, ChevronDown, Menu, BookOpen, X, CheckCheck, CalendarCheck2, CalendarX2, Sparkles, AlertTriangle, Star, CreditCard, Info, ArrowRight, Wrench, @@ -204,7 +204,7 @@ interface TopbarProps { } export function Topbar({ onMenuToggle, title }: TopbarProps) { - const { theme, toggle } = useTheme() + const { mode, toggle } = useTheme() const { user, logout } = useAuth() const navigate = useNavigate() const { notifications } = useNotifications() @@ -257,8 +257,8 @@ export function Topbar({ onMenuToggle, title }: TopbarProps) { {/* Theme toggle */} - {/* User menu */} diff --git a/src/contexts/ThemeContext.tsx b/src/contexts/ThemeContext.tsx index c1c026d..e061338 100644 --- a/src/contexts/ThemeContext.tsx +++ b/src/contexts/ThemeContext.tsx @@ -1,21 +1,44 @@ import { createContext, useContext, useEffect, useState } from 'react' +export type ThemeMode = 'light' | 'dark' | 'auto' type Theme = 'light' | 'dark' interface ThemeContextValue { + mode: ThemeMode theme: Theme + setMode: (mode: ThemeMode) => void toggle: () => void } const ThemeContext = createContext(null) +function getAutoTheme(): Theme { + const h = new Date().getHours() + return h >= 7 && h < 22 ? 'light' : 'dark' +} + export function ThemeProvider({ children }: { children: React.ReactNode }) { - const [theme, setTheme] = useState(() => { - const stored = localStorage.getItem('hotelsync-theme') as Theme | null - if (stored) return stored - return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' + const [mode, setModeState] = useState(() => { + return (localStorage.getItem('hotelsync-theme') as ThemeMode | null) ?? 'light' }) + const [theme, setTheme] = useState(() => { + const stored = localStorage.getItem('hotelsync-theme') as ThemeMode | null + if (!stored || stored === 'light') return 'light' + if (stored === 'dark') return 'dark' + return getAutoTheme() + }) + + useEffect(() => { + if (mode === 'auto') { + setTheme(getAutoTheme()) + const interval = setInterval(() => setTheme(getAutoTheme()), 60_000) + return () => clearInterval(interval) + } else { + setTheme(mode) + } + }, [mode]) + useEffect(() => { const root = document.documentElement if (theme === 'dark') { @@ -23,13 +46,20 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) { } else { root.classList.remove('dark') } - localStorage.setItem('hotelsync-theme', theme) }, [theme]) - const toggle = () => setTheme(t => t === 'light' ? 'dark' : 'light') + const setMode = (m: ThemeMode) => { + setModeState(m) + localStorage.setItem('hotelsync-theme', m) + } + + const toggle = () => { + const next: ThemeMode = mode === 'light' ? 'dark' : mode === 'dark' ? 'auto' : 'light' + setMode(next) + } return ( - + {children} ) diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index 1e7c3e5..4ec9830 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -33,7 +33,7 @@ export function SettingsPage() { const [section, setSection] = useState('general') const [saved, setSaved] = useState(false) const [hotel, setHotel] = useState(null) - const { theme, toggle } = useTheme() + const { mode, theme, setMode } = useTheme() useEffect(() => { if (!slug) return @@ -336,25 +336,36 @@ export function SettingsPage() {

Тема интерфейса

-
+
{([ - { id: 'light', label: 'Светлая' }, - { id: 'dark', label: 'Тёмная' }, + { id: 'light', label: 'Светлая', desc: null }, + { id: 'dark', label: 'Тёмная', desc: null }, + { id: 'auto', label: 'Авто', desc: 'Светлая 7:00–22:00, тёмная ночью' }, ] as const).map(t => ( ))}