Add admin payments with status, provider, and plan category filters. Introduce app versions and subscription plan management in settings, change-password security flow, and dark theme support. Reorganize sidebar, improve activity log actor details, analytics, and related UI polish. Co-authored-by: Cursor <cursoragent@cursor.com>
65 lines
2.0 KiB
TypeScript
65 lines
2.0 KiB
TypeScript
export type ThemeMode = "light" | "dark" | "system"
|
|
export type ResolvedTheme = "light" | "dark"
|
|
|
|
export const THEME_STORAGE_KEY = "yimaru-admin-theme"
|
|
|
|
const MEDIA_QUERY = "(prefers-color-scheme: dark)"
|
|
|
|
export function getSystemTheme(): ResolvedTheme {
|
|
if (typeof window === "undefined") return "light"
|
|
return window.matchMedia(MEDIA_QUERY).matches ? "dark" : "light"
|
|
}
|
|
|
|
/** Resolved appearance: light mode always forces light; dark forces dark; system follows OS. */
|
|
export function resolveTheme(mode: ThemeMode): ResolvedTheme {
|
|
if (mode === "light") return "light"
|
|
if (mode === "dark") return "dark"
|
|
return getSystemTheme()
|
|
}
|
|
|
|
export function getStoredTheme(): ThemeMode {
|
|
if (typeof window === "undefined") return "light"
|
|
const value = localStorage.getItem(THEME_STORAGE_KEY)
|
|
if (value === "light" || value === "dark" || value === "system") return value
|
|
return "light"
|
|
}
|
|
|
|
function syncMetaThemeColor(resolved: ResolvedTheme) {
|
|
if (typeof document === "undefined") return
|
|
let meta = document.querySelector('meta[name="theme-color"]') as HTMLMetaElement | null
|
|
if (!meta) {
|
|
meta = document.createElement("meta")
|
|
meta.name = "theme-color"
|
|
document.head.appendChild(meta)
|
|
}
|
|
meta.content = resolved === "dark" ? "#12121a" : "#f5f5f5"
|
|
}
|
|
|
|
export function applyTheme(mode: ThemeMode): ResolvedTheme {
|
|
const resolved = resolveTheme(mode)
|
|
const root = document.documentElement
|
|
|
|
root.classList.remove("dark")
|
|
if (resolved === "dark") {
|
|
root.classList.add("dark")
|
|
}
|
|
|
|
root.dataset.theme = resolved
|
|
root.dataset.themePreference = mode
|
|
root.style.colorScheme = resolved
|
|
|
|
syncMetaThemeColor(resolved)
|
|
|
|
return resolved
|
|
}
|
|
|
|
export function watchSystemTheme(onChange: (resolved: ResolvedTheme) => void): () => void {
|
|
if (typeof window === "undefined") return () => undefined
|
|
|
|
const media = window.matchMedia(MEDIA_QUERY)
|
|
const handler = () => onChange(getSystemTheme())
|
|
|
|
media.addEventListener("change", handler)
|
|
return () => media.removeEventListener("change", handler)
|
|
}
|