diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx
index 8e0c3a9..d605a2e 100644
--- a/frontend/app/layout.tsx
+++ b/frontend/app/layout.tsx
@@ -1,9 +1,12 @@
-import Footer from "../components/sections/Footer";
-
+// src/app/layout.tsx : UI Layout, global context providers, and one off global components
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
+import { AuthProvider } from "@/providers/AuthProvider";
+import { AuthDialogProvider } from "@/providers/AuthDialogProvider";
+import AuthModal from "@/components/AuthModal";
+import Footer from "@/components/sections/Footer";
const inter = Inter({
variable: "--font-inter",
@@ -12,19 +15,26 @@ const inter = Inter({
export const metadata: Metadata = {
title: "AI Agents Directory",
- description: "Discover the best AI agents for your use case based on verified user reviews. Updated daily.",
+ description:
+ "Discover the best AI agents for your use case based on verified user reviews. Updated daily.",
};
export default function RootLayout({
children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
+}: Readonly<{ children: React.ReactNode }>) {
return (
- {children}
-
+ {/* Providers are mounted ONCE here and wrap the whole app */}
+
+
+ {/* Your entire app can now use useAuth() and useAuthDialog() */}
+ {children}
+ {/* One global login modal instance controlled by useAuthDialog() */}
+
+
+
+
);
diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx
index 712df1b..de573fd 100644
--- a/frontend/app/page.tsx
+++ b/frontend/app/page.tsx
@@ -40,4 +40,4 @@ export default function HomePage() {
>
);
}
-// force redeploy
+
diff --git a/frontend/components/AuthModal.tsx b/frontend/components/AuthModal.tsx
new file mode 100644
index 0000000..35f7df9
--- /dev/null
+++ b/frontend/components/AuthModal.tsx
@@ -0,0 +1,97 @@
+"use client";
+
+import { useEffect, useRef } from "react";
+import { useAuth } from "@/providers/AuthProvider";
+import { useAuthDialog } from "@/providers/AuthDialogProvider";
+import { X } from "lucide-react";
+
+export default function AuthModal() {
+ const { isOpen, close, resolveSignedIn } = useAuthDialog();
+ const { user, loading, signInWithGoogle } = useAuth();
+ const cardRef = useRef(null);
+
+ // ✅ Hooks always run in the same order — no early return before them.
+
+ // Auto-close once user is signed in
+ useEffect(() => {
+ if (!loading && user) {
+ resolveSignedIn();
+ }
+ }, [loading, user, resolveSignedIn]);
+
+ // Basic focus trap & escape key
+ useEffect(() => {
+ if (!isOpen) return; // ✅ condition INSIDE the effect
+ const prev = document.activeElement as HTMLElement | null;
+ cardRef.current?.focus();
+ const onKey = (e: KeyboardEvent) => {
+ if (e.key === "Escape") close();
+ };
+ document.addEventListener("keydown", onKey);
+ return () => {
+ document.removeEventListener("keydown", onKey);
+ prev?.focus();
+ };
+ }, [isOpen, close]);
+
+ // Now it's safe to return early
+ if (!isOpen) return null;
+
+ return (
+
+
e.stopPropagation()}
+ >
+ {/* Close button */}
+
+
+ {/* Logo + heading */}
+
+
+
+ Sign in to unlock the best of AgentList.
+
+
+
+ {/* Buttons */}
+
+
+
+
+
+
+ {/* Legal */}
+
+ By proceeding, you agree to our Terms and confirm you’ve read our Privacy Policy.
+
+
+
+ );
+}
diff --git a/frontend/components/sections/Navbar.tsx b/frontend/components/sections/Navbar.tsx
index 28f6b64..716319f 100644
--- a/frontend/components/sections/Navbar.tsx
+++ b/frontend/components/sections/Navbar.tsx
@@ -1,12 +1,20 @@
-// components/Navbar.jsx
-import { Globe } from 'lucide-react'; // or swap with another icon if preferred
+// components/Navbar.tsx
+
+// ✅ Requires: useAuth (AuthProvider) + useAuthDialog (AuthDialogProvider) mounted in app/layout
+"use client";
+
+import Link from "next/link";
+import { Globe } from "lucide-react";
+import { useAuth } from "@/providers/AuthProvider";
+import { useAuthDialog } from "@/providers/AuthDialogProvider";
export const Navbar = () => {
+ const { user, logout } = useAuth();
+ const { open } = useAuthDialog();
+
return (
- {/* CONTENT WRAPPER: centers all navbar content and limits max width */}