Revert "feat: migrate frontend to Astro + expand AI moderation + backend admin/runtime config"
This reverts commit d59b59a7a7.
This commit is contained in:
@@ -1,31 +0,0 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
/**
|
||||
* Tracks incoming moderation_alert events and maintains a badge counter.
|
||||
* Clears when the user navigates to the messages tab.
|
||||
*/
|
||||
export function useNotificationBadge(activeTab: string) {
|
||||
const [count, setCount] = useState(0);
|
||||
const prevActiveTab = useRef(activeTab);
|
||||
|
||||
// Clear badge when user switches TO messages tab
|
||||
useEffect(() => {
|
||||
if (activeTab === "messages" && prevActiveTab.current !== "messages") {
|
||||
setCount(0);
|
||||
}
|
||||
prevActiveTab.current = activeTab;
|
||||
}, [activeTab]);
|
||||
|
||||
const increment = useCallback(() => {
|
||||
setCount((c) => c + 1);
|
||||
}, []);
|
||||
|
||||
// Listen for moderation_alert custom events
|
||||
useEffect(() => {
|
||||
const handler = () => increment();
|
||||
window.addEventListener("moderation_alert", handler);
|
||||
return () => window.removeEventListener("moderation_alert", handler);
|
||||
}, [increment]);
|
||||
|
||||
return { count, clear: () => setCount(0) };
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
|
||||
export type Theme = "light" | "dark";
|
||||
export type ThemeMode = Theme | "system";
|
||||
|
||||
const THEME_STORAGE_KEY = "bete-dashboard-theme";
|
||||
|
||||
function getSystemTheme(): Theme {
|
||||
if (typeof window === "undefined") return "dark";
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "dark"
|
||||
: "light";
|
||||
}
|
||||
|
||||
function loadThemeMode(): ThemeMode {
|
||||
try {
|
||||
const stored = localStorage.getItem(THEME_STORAGE_KEY);
|
||||
if (stored === "light" || stored === "dark" || stored === "system")
|
||||
return stored;
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
return "system";
|
||||
}
|
||||
|
||||
function resolveTheme(mode: ThemeMode): Theme {
|
||||
return mode === "system" ? getSystemTheme() : mode;
|
||||
}
|
||||
|
||||
function applyTheme(theme: Theme) {
|
||||
const root = document.documentElement;
|
||||
root.setAttribute("data-theme", theme);
|
||||
// Also toggle Tailwind dark class for utility-based approach
|
||||
if (theme === "dark") {
|
||||
root.classList.add("dark");
|
||||
} else {
|
||||
root.classList.remove("dark");
|
||||
}
|
||||
}
|
||||
|
||||
export function useTheme() {
|
||||
const [mode, setModeState] = useState<ThemeMode>(loadThemeMode);
|
||||
|
||||
const theme = useMemo(() => resolveTheme(mode), [mode]);
|
||||
|
||||
const setMode = useCallback((newMode: ThemeMode) => {
|
||||
setModeState(newMode);
|
||||
try {
|
||||
localStorage.setItem(THEME_STORAGE_KEY, newMode);
|
||||
} catch {
|
||||
// ignore quota errors
|
||||
}
|
||||
}, []);
|
||||
|
||||
const toggle = useCallback(() => {
|
||||
setMode(theme === "dark" ? "light" : "dark");
|
||||
}, [theme, setMode]);
|
||||
|
||||
// Apply theme on mount and when mode changes
|
||||
useEffect(() => {
|
||||
applyTheme(theme);
|
||||
}, [theme]);
|
||||
|
||||
// Listen for system preference changes when in "system" mode
|
||||
useEffect(() => {
|
||||
if (mode !== "system") return;
|
||||
|
||||
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
const handler = () => {
|
||||
applyTheme(resolveTheme("system"));
|
||||
};
|
||||
mq.addEventListener("change", handler);
|
||||
return () => mq.removeEventListener("change", handler);
|
||||
}, [mode]);
|
||||
|
||||
return { theme, mode, setMode, toggle, isDark: theme === "dark" };
|
||||
}
|
||||
Reference in New Issue
Block a user