diff --git a/services/frontend/src/app/dashboard/layout.tsx b/services/frontend/src/app/dashboard/layout.tsx index 16ae6a2..cfdeb1b 100644 --- a/services/frontend/src/app/dashboard/layout.tsx +++ b/services/frontend/src/app/dashboard/layout.tsx @@ -7,32 +7,8 @@ import { Header } from "@/components/layout/header"; import { MobileTabBar } from "@/components/layout/mobile-tab-bar"; import { Sidebar } from "@/components/layout/sidebar"; import { MascotChatbot } from "@/features/mascot/mascot-chatbot"; -import { AuthProvider, useAuth } from "@/lib/hooks/use-auth"; import { WsProvider } from "@/lib/ws/context"; -function DashboardGuard({ children }: { children: React.ReactNode }) { - const { authenticated, loading } = useAuth(); - const router = useRouter(); - - useEffect(() => { - if (!loading && !authenticated) { - router.push("/"); - } - }, [authenticated, loading, router]); - - if (loading) { - return ( -
-
-
- ); - } - - if (!authenticated) return null; - - return <>{children}; -} - function DashboardShell({ children }: { children: React.ReactNode }) { const searchParams = useSearchParams(); const router = useRouter(); @@ -49,7 +25,7 @@ function DashboardShell({ children }: { children: React.ReactNode }) { const tabParam = searchParams.get("tab"); if (tabParam) { restored.current = true; - return; // explicit tab in URL — don't override + return; } uiStateApi .get() @@ -89,21 +65,17 @@ export default function DashboardLayout({ children: React.ReactNode; }) { return ( - - - - -
-
- } - > - {children} -
- -
-
-
+ + +
+
+ } + > + {children} +
+ +
); } diff --git a/services/frontend/src/app/page.tsx b/services/frontend/src/app/page.tsx index b7536ff..485bdc5 100644 --- a/services/frontend/src/app/page.tsx +++ b/services/frontend/src/app/page.tsx @@ -1,103 +1,5 @@ -"use client"; +import { redirect } from "next/navigation"; -import { Loader2, Shield } from "lucide-react"; -import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; -import { login } from "@/lib/api"; - -export default function LoginPage() { - const [password, setPassword] = useState(""); - const [error, setError] = useState(""); - const [loading, setLoading] = useState(false); - const [checking, setChecking] = useState(true); - const router = useRouter(); - - useEffect(() => { - const stored = localStorage.getItem("admin-password"); - if (stored) { - login(stored) - .then((ok) => { - if (ok) router.replace("/dashboard"); - else setChecking(false); - }) - .catch(() => setChecking(false)); - } else { - setChecking(false); - } - }, [router]); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setError(""); - setLoading(true); - - try { - const ok = await login(password); - if (ok) { - localStorage.setItem("admin-password", password); - router.push("/dashboard"); - } else { - setError("Invalid password"); - } - } catch { - setError("Connection failed. Is the backend running?"); - } finally { - setLoading(false); - } - }; - - if (checking) { - return ( -
- -
- ); - } - - return ( -
-
-
-
- -
-

Bete

-

- Discord Moderation Dashboard -

-
- -
-
- - setPassword(e.target.value)} - placeholder="Enter admin password" - className="flex h-9 w-full rounded-lg border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:opacity-50" - disabled={loading} - /> -
- - {error &&

{error}

} - - -
-
-
- ); +export default function RootPage() { + redirect("/dashboard?tab=messages"); } diff --git a/services/frontend/src/components/layout/sidebar.tsx b/services/frontend/src/components/layout/sidebar.tsx index caf4249..d47fcc6 100644 --- a/services/frontend/src/components/layout/sidebar.tsx +++ b/services/frontend/src/components/layout/sidebar.tsx @@ -1,8 +1,7 @@ "use client"; -import { LayoutDashboard, LogOut, MessageSquare, Radio } from "lucide-react"; +import { LayoutDashboard, MessageSquare, Radio } from "lucide-react"; import { useRouter } from "next/navigation"; -import { useAuth } from "@/lib/hooks/use-auth"; const tabs = [ { id: "messages", label: "Messages", icon: MessageSquare }, @@ -13,7 +12,6 @@ const tabs = [ type TabId = (typeof tabs)[number]["id"]; export function Sidebar({ activeTab }: { activeTab: TabId }) { - const { logout } = useAuth(); const router = useRouter(); const handleTabClick = (tabId: TabId) => { @@ -43,17 +41,6 @@ export function Sidebar({ activeTab }: { activeTab: TabId }) { ))} - -
- -
); } diff --git a/services/frontend/src/lib/api/auth.ts b/services/frontend/src/lib/api/auth.ts deleted file mode 100644 index 835387e..0000000 --- a/services/frontend/src/lib/api/auth.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ApiError, api } from "./client"; - -export async function login(password: string): Promise { - try { - const resp = await api.post<{ ok: boolean }>("/api/auth/login", { - password, - }); - return resp.ok; - } catch (err) { - if (err instanceof ApiError) return false; - throw err; - } -} diff --git a/services/frontend/src/lib/api/client.ts b/services/frontend/src/lib/api/client.ts index dc08dbd..0f3dfd2 100644 --- a/services/frontend/src/lib/api/client.ts +++ b/services/frontend/src/lib/api/client.ts @@ -12,27 +12,17 @@ function getBaseUrl(): string { if (typeof window === "undefined") return ""; const protocol = window.location.protocol.replace(":", ""); const host = window.location.host; - // In dev, Next.js proxy can be configured, but default to same-host assumption return `${protocol}://${host}`; } -function getAuthHeader(): string | null { - if (typeof window === "undefined") return null; - return localStorage.getItem("admin-password"); -} - export async function apiRequest( method: string, path: string, body?: unknown, ): Promise { const url = `${getBaseUrl()}${path}`; - const password = getAuthHeader(); const headers: Record = {}; - if (password) { - headers["X-Admin-Password"] = password; - } if (body !== undefined) { headers["Content-Type"] = "application/json"; } diff --git a/services/frontend/src/lib/api/index.ts b/services/frontend/src/lib/api/index.ts index 390d1a0..db424fd 100644 --- a/services/frontend/src/lib/api/index.ts +++ b/services/frontend/src/lib/api/index.ts @@ -1,4 +1,3 @@ -export { login } from "./auth"; export { ApiError, api, apiRequest } from "./client"; export { configApi } from "./config"; export { dashboardApi } from "./dashboard"; diff --git a/services/frontend/src/lib/hooks/use-auth.tsx b/services/frontend/src/lib/hooks/use-auth.tsx deleted file mode 100644 index da25daa..0000000 --- a/services/frontend/src/lib/hooks/use-auth.tsx +++ /dev/null @@ -1,78 +0,0 @@ -"use client"; - -import { - createContext, - type ReactNode, - useCallback, - useContext, - useEffect, - useState, -} from "react"; -import { login } from "@/lib/api"; - -interface AuthContextValue { - authenticated: boolean; - loading: boolean; - login: (password: string) => Promise; - logout: () => void; -} - -const AuthContext = createContext(null); - -export function AuthProvider({ children }: { children: ReactNode }) { - const [authenticated, setAuthenticated] = useState(false); - const [loading, setLoading] = useState(true); - - useEffect(() => { - const password = localStorage.getItem("admin-password"); - if (password) { - // Verify stored password still works - login(password) - .then((ok) => { - setAuthenticated(ok); - setLoading(false); - }) - .catch(() => { - localStorage.removeItem("admin-password"); - setLoading(false); - }); - } else { - setLoading(false); - } - }, []); - - const handleLogin = useCallback(async (password: string) => { - const ok = await login(password); - if (ok) { - localStorage.setItem("admin-password", password); - setAuthenticated(true); - } - return ok; - }, []); - - const handleLogout = useCallback(() => { - localStorage.removeItem("admin-password"); - setAuthenticated(false); - }, []); - - return ( - - {children} - - ); -} - -export function useAuth(): AuthContextValue { - const ctx = useContext(AuthContext); - if (!ctx) { - throw new Error("useAuth must be used within an AuthProvider"); - } - return ctx; -}