generated from gc/template-java-maven
공통:
- 다크/라이트 모드 (ThemeContext, Tailwind dark variant, 전체 페이지 적용)
- 사이드바 아이콘 링크체인 (#FF2E63), 헤더/사이드바 높이 통일
- 컨텐츠 영역 max-w-7xl 마진 통일 (대시보드 제외)
- 전체 Actions 버튼 bg-color-100 스타일 통일
- date input 달력 아이콘 다크모드 (filter invert)
API Keys:
- Request: 영구 사용 옵션 추가, 프리셋/영구 버튼 다크모드
- My Keys: ADMIN 직접 생성 제거 → Request 페이지 정식 폼으로 통일
- Admin: 키 관리 만료일 컬럼 추가, 권한 편집 제거 (승인 단계에서만 가능)
Gateway:
- API 경로 {변수} 패턴 매칭 지원
Closes #15
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
49 lines
1.2 KiB
TypeScript
49 lines
1.2 KiB
TypeScript
import { createContext, useState, useEffect, useCallback } from 'react';
|
|
import type { ReactNode } from 'react';
|
|
|
|
type Theme = 'light' | 'dark';
|
|
|
|
interface ThemeContextValue {
|
|
theme: Theme;
|
|
toggleTheme: () => void;
|
|
}
|
|
|
|
export const ThemeContext = createContext<ThemeContextValue | null>(null);
|
|
|
|
interface ThemeProviderProps {
|
|
children: ReactNode;
|
|
}
|
|
|
|
const getInitialTheme = (): Theme => {
|
|
const stored = localStorage.getItem('theme');
|
|
if (stored === 'light' || stored === 'dark') {
|
|
return stored;
|
|
}
|
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
};
|
|
|
|
const ThemeProvider = ({ children }: ThemeProviderProps) => {
|
|
const [theme, setTheme] = useState<Theme>(getInitialTheme);
|
|
|
|
useEffect(() => {
|
|
if (theme === 'dark') {
|
|
document.documentElement.classList.add('dark');
|
|
} else {
|
|
document.documentElement.classList.remove('dark');
|
|
}
|
|
localStorage.setItem('theme', theme);
|
|
}, [theme]);
|
|
|
|
const toggleTheme = useCallback(() => {
|
|
setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
|
|
}, []);
|
|
|
|
return (
|
|
<ThemeContext.Provider value={{ theme, toggleTheme }}>
|
|
{children}
|
|
</ThemeContext.Provider>
|
|
);
|
|
};
|
|
|
|
export default ThemeProvider;
|