Revert "feat: migrate frontend to Astro SSG with design system"

This reverts commit 8ad888da28.
This commit is contained in:
asepharyana
2026-07-02 03:54:44 +07:00
parent 880c68fbab
commit bf86fcda8e
110 changed files with 4980 additions and 3396 deletions
@@ -0,0 +1,31 @@
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) };
}
+77
View File
@@ -0,0 +1,77 @@
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" };
}