세션 종료 UX 정리

This commit is contained in:
Yun Chan 2026-06-27 18:09:48 +09:00
parent f472883c31
commit 1007eaf7d9
15 changed files with 460 additions and 245 deletions

View file

@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { Icon } from "../ui/Icon";
import { useAuth, roleLabel } from "../../lib/auth";
import { applyTheme, readInitialTheme, type AppTheme } from "../../lib/theme";
/** Vignette 워드마크 — 비네트(조리개) inline SVG. dev_dashboard 마크 계승. */
function BrandMark() {
@ -19,31 +20,13 @@ function BrandMark() {
);
}
const THEME_KEY = "vignette.theme";
/** 라이트/다크 토글 (data-theme 반영). 토큰만으로 전환. */
function useTheme(): [boolean, () => void] {
const [dark, setDark] = useState<boolean>(() => {
try {
const saved = localStorage.getItem(THEME_KEY);
if (saved) return saved === "dark";
} catch {
/* 무시 */
}
// 기본 라이트 고정(밝은 에디토리얼 톤). OS 다크선호는 따라가지 않는다 — 다크는 명시 토글로만.
return false;
});
const [theme, setTheme] = useState<AppTheme>(() => readInitialTheme());
useEffect(() => {
const root = document.documentElement;
if (dark) root.setAttribute("data-theme", "dark");
else root.removeAttribute("data-theme");
try {
localStorage.setItem(THEME_KEY, dark ? "dark" : "light");
} catch {
/* 무시 */
}
}, [dark]);
return [dark, () => setDark((d) => !d)];
applyTheme(theme);
}, [theme]);
return [theme === "dark", () => setTheme((current) => (current === "dark" ? "light" : "dark"))];
}
function initials(name?: string | null): string {