feat: vylepšená podpora themes
CI / Generate TypeScript types (push) Successful in 1m3s
CI / Server unit tests (push) Successful in 30s
CI / Build server (push) Successful in 25s
CI / Build client (push) Successful in 36s
CI / Playwright E2E tests (push) Successful in 1m20s
CI / Build and push Docker image (push) Successful in 2m46s
CI / Notify (push) Successful in 1s

This commit is contained in:
2026-05-20 12:51:46 +02:00
parent 640c7ed41d
commit a26d6cf85c
5 changed files with 303 additions and 129 deletions
+12 -35
View File
@@ -2,7 +2,8 @@ import { useEffect, useState } from "react";
import { Navbar, Nav, NavDropdown, Modal, Button } from "react-bootstrap";
import { useAuth } from "../context/auth";
import SettingsModal from "./modals/SettingsModal";
import { useSettings, ThemePreference, ColorTheme } from "../context/settings";
import { useSettings, ThemePreference } from "../context/settings";
import HuePicker from "./HuePicker";
import FeaturesVotingModal from "./modals/FeaturesVotingModal";
import PizzaCalculatorModal from "./modals/PizzaCalculatorModal";
import RefreshMenuModal from "./modals/RefreshMenuModal";
@@ -13,7 +14,7 @@ import { useNavigate } from "react-router";
import { STATS_URL, OBJEDNANI_URL } from "../AppRoutes";
import { FeatureRequest, getVotes, updateVote, LunchChoices, getChangelogs } from "../../../types";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSun, faMoon, faPalette } from "@fortawesome/free-solid-svg-icons";
import { faSun, faMoon } from "@fortawesome/free-solid-svg-icons";
import { formatDateString } from "../Utils";
const LAST_SEEN_CHANGELOG_KEY = "lastChangelogDate";
@@ -40,25 +41,7 @@ export default function Header({ choices, dayIndex }: Props) {
const [clearMockModalOpen, setClearMockModalOpen] = useState<boolean>(false);
const [featureVotes, setFeatureVotes] = useState<FeatureRequest[] | undefined>([]);
// Zjistíme aktuální efektivní téma (pro zobrazení správné ikony)
const [effectiveTheme, setEffectiveTheme] = useState<'light' | 'dark'>('light');
useEffect(() => {
const updateEffectiveTheme = () => {
if (settings?.themePreference === 'system') {
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
setEffectiveTheme(isDark ? 'dark' : 'light');
} else {
setEffectiveTheme(settings?.themePreference || 'light');
}
};
updateEffectiveTheme();
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
mediaQuery.addEventListener('change', updateEffectiveTheme);
return () => mediaQuery.removeEventListener('change', updateEffectiveTheme);
}, [settings?.themePreference]);
const effectiveDark = settings?.effectiveDark ?? false;
useEffect(() => {
if (auth?.login) {
@@ -110,8 +93,7 @@ export default function Header({ choices, dayIndex }: Props) {
}
const toggleTheme = () => {
// Přepínáme mezi light a dark (ignorujeme system pro jednoduchost)
const newTheme: ThemePreference = effectiveTheme === 'dark' ? 'light' : 'dark';
const newTheme: ThemePreference = effectiveDark ? 'light' : 'dark';
settings?.setThemePreference(newTheme);
}
@@ -195,21 +177,16 @@ export default function Header({ choices, dayIndex }: Props) {
<button
className="theme-toggle"
onClick={toggleTheme}
title={effectiveTheme === 'dark' ? 'Přepnout na světlý režim' : 'Přepnout na tmavý režim'}
title={effectiveDark ? 'Přepnout na světlý režim' : 'Přepnout na tmavý režim'}
aria-label="Přepnout světlý/tmavý režim"
>
<FontAwesomeIcon icon={effectiveTheme === 'dark' ? faSun : faMoon} />
<FontAwesomeIcon icon={effectiveDark ? faSun : faMoon} />
</button>
<NavDropdown
align="end"
title={<FontAwesomeIcon icon={faPalette} />}
id="color-theme-dropdown"
className="theme-toggle"
>
<NavDropdown.Item active={settings?.colorTheme === 'green'} onClick={() => settings?.setColorTheme('green' as ColorTheme)}>🟢 Zelený</NavDropdown.Item>
<NavDropdown.Item active={settings?.colorTheme === 'blue'} onClick={() => settings?.setColorTheme('blue' as ColorTheme)}>🔵 Modrý</NavDropdown.Item>
<NavDropdown.Item active={settings?.colorTheme === 'purple'} onClick={() => settings?.setColorTheme('purple' as ColorTheme)}>🟣 Fialový</NavDropdown.Item>
</NavDropdown>
<HuePicker
accentHue={settings?.accentHue ?? 142}
isDark={effectiveDark}
onChange={hue => settings?.setAccentHue(hue)}
/>
<NavDropdown align="end" title={auth?.login} id="basic-nav-dropdown">
<NavDropdown.Item onClick={() => setSettingsModalOpen(true)}>Nastavení</NavDropdown.Item>
<NavDropdown.Item onClick={() => setRefreshMenuModalOpen(true)}>Přenačtení menu</NavDropdown.Item>