Skip to content

Commit b0f68a9

Browse files
authored
add layout, add auth (#179)
* add layout component * update layout * protect index page * bug fix
1 parent d35f8ef commit b0f68a9

File tree

18 files changed

+1478
-118
lines changed

18 files changed

+1478
-118
lines changed

frontend/package-lock.json

Lines changed: 622 additions & 39 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,24 @@
2020
"@mantine/core": "^7.14.2",
2121
"@mantine/hooks": "^7.14.2",
2222
"@radix-ui/react-alert-dialog": "^1.1.3",
23+
"@radix-ui/react-checkbox": "^1.3.2",
24+
"@radix-ui/react-label": "^2.1.7",
2325
"@radix-ui/react-scroll-area": "^1.2.2",
2426
"@radix-ui/react-separator": "^1.1.1",
2527
"@radix-ui/react-slot": "^1.1.1",
2628
"@radix-ui/react-switch": "^1.1.2",
2729
"@radix-ui/react-tabs": "^1.1.2",
30+
"@supabase/ssr": "^0.6.1",
31+
"@supabase/supabase-js": "^2.49.7",
2832
"@tabler/icons-react": "^3.22.0",
33+
"axios": "^1.9.0",
2934
"class-variance-authority": "^0.7.1",
3035
"clsx": "^2.1.1",
3136
"lucide-react": "^0.468.0",
3237
"next": "15.0.3",
3338
"react": "^18.2.0",
3439
"react-dom": "^18.2.0",
40+
"react-icons": "^5.5.0",
3541
"tailwind-merge": "^2.5.5",
3642
"tailwindcss-animate": "^1.0.7",
3743
"zustand": "^5.0.2"

frontend/public/pathonai_org.png

7.51 KB
Loading
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { useState } from "react";
2+
import { HiMenu, HiX } from "react-icons/hi";
3+
import { FaLinkedin, FaTwitter, FaDiscord, FaGithub } from 'react-icons/fa';
4+
import Link from "next/link";
5+
import { useRouter } from "next/router";
6+
7+
const Header = () => {
8+
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
9+
const router = useRouter();
10+
11+
const isActive = (path: string) => {
12+
return router.pathname === path;
13+
};
14+
15+
return (
16+
<header className="fixed w-full z-10">
17+
<div className="px-6 py-5 flex justify-between items-center bg-gray-100 shadow-md">
18+
<div className="flex items-center">
19+
<Link href="/" className="flex items-center text-xl font-bold text-gray-900 mr-12">
20+
<img
21+
src="/pathonai_org.png"
22+
alt="PathOnAI Logo"
23+
className="h-8 w-auto mr-2"
24+
/>
25+
PathOnAI
26+
</Link>
27+
<nav className="hidden md:flex space-x-8">
28+
<Link
29+
href="https://www.pathonai.org/projects/litewebagent"
30+
className={`text-sm font-medium ${isActive('/')
31+
? 'text-blue-600'
32+
: 'text-gray-700 hover:text-blue-600'}`}
33+
>
34+
LiteWebAgent Project Page
35+
</Link>
36+
<Link
37+
href="https://www.pathonai.org/projects/visualtreesearch"
38+
className={`text-sm font-medium ${isActive('/projects')
39+
? 'text-blue-600'
40+
: 'text-gray-700 hover:text-blue-600'}`}
41+
>
42+
Visual Tree Search Project Page
43+
</Link>
44+
</nav>
45+
</div>
46+
<div className="flex items-center">
47+
{/* Social Media Links */}
48+
<div className="hidden md:flex items-center space-x-5 mr-6">
49+
<a
50+
href="https://github.com/PathOnAI"
51+
target="_blank"
52+
rel="noopener noreferrer"
53+
className="text-gray-700 hover:text-blue-600"
54+
aria-label="GitHub"
55+
>
56+
<FaGithub className="h-6 w-6" />
57+
</a>
58+
<a
59+
href="https://www.linkedin.com/company/pathonai/"
60+
target="_blank"
61+
rel="noopener noreferrer"
62+
className="text-gray-700 hover:text-blue-600"
63+
aria-label="LinkedIn"
64+
>
65+
<FaLinkedin className="h-6 w-6" />
66+
</a>
67+
<a
68+
href="https://x.com/PathOnAI"
69+
target="_blank"
70+
rel="noopener noreferrer"
71+
className="text-gray-700 hover:text-blue-600"
72+
aria-label="Twitter"
73+
>
74+
<FaTwitter className="h-6 w-6" />
75+
</a>
76+
<a
77+
href="https://discord.com/invite/UTxjyNwTeP"
78+
target="_blank"
79+
rel="noopener noreferrer"
80+
className="text-gray-700 hover:text-blue-600"
81+
aria-label="Discord"
82+
>
83+
<FaDiscord className="h-6 w-6" />
84+
</a>
85+
</div>
86+
87+
<div className="md:hidden">
88+
<button
89+
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
90+
className="text-gray-700"
91+
>
92+
{mobileMenuOpen ? (
93+
<HiX className="h-6 w-6" />
94+
) : (
95+
<HiMenu className="h-6 w-6" />
96+
)}
97+
</button>
98+
</div>
99+
</div>
100+
</div>
101+
102+
</header>
103+
);
104+
};
105+
106+
export default Header;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from "react";
2+
import Sidebar from "./Sidebar";
3+
import Header from "./Header";
4+
5+
interface LayoutProps {
6+
children: React.ReactNode;
7+
}
8+
9+
export default function Layout({ children }: LayoutProps) {
10+
return (
11+
<div className="flex flex-col h-screen bg-background">
12+
<Header />
13+
<div className="flex flex-1 overflow-hidden">
14+
<Sidebar />
15+
<main className="flex-1 overflow-auto p-6 mt-16">{children}</main>
16+
</div>
17+
</div>
18+
);
19+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import Link from "next/link";
2+
import { useRouter } from "next/router";
3+
import { Home, LineChart, FileText, LogIn} from "lucide-react";
4+
import { Button } from "@/components/ui/button";
5+
6+
export default function Sidebar() {
7+
const router = useRouter();
8+
9+
// Helper to add custom active styles
10+
const activeClass =
11+
"bg-blue-50 border-l-4 border-blue-600 text-blue-700 font-semibold shadow-sm";
12+
13+
return (
14+
<div className="w-64 h-full border-r border-foreground/10 bg-background">
15+
<div className="p-4 border-b border-foreground/10">
16+
<h1 className="text-xl font-bold">LiteWebAgent</h1>
17+
</div>
18+
19+
<nav className="p-2">
20+
<div className="space-y-1">
21+
<Button
22+
variant={router.pathname === "/" ? "secondary" : "ghost"}
23+
className={`w-full justify-start ${
24+
router.pathname === "/" ? activeClass : ""
25+
}`}
26+
asChild
27+
>
28+
<Link href="/">
29+
<Home className="mr-2 h-4 w-4" />
30+
Home
31+
</Link>
32+
</Button>
33+
34+
<Button
35+
variant={router.pathname === "/playground" ? "secondary" : "ghost"}
36+
className={`w-full justify-start ${
37+
router.pathname === "/playground" ? activeClass : ""
38+
}`}
39+
asChild
40+
>
41+
<Link href="/playground">
42+
<LineChart className="mr-2 h-4 w-4" />
43+
Playground
44+
</Link>
45+
</Button>
46+
47+
<Button
48+
variant={router.pathname === "/login" ? "secondary" : "ghost"}
49+
className={`w-full justify-start ${
50+
router.pathname === "/login" ? activeClass : ""
51+
}`}
52+
asChild
53+
>
54+
<Link href="/login">
55+
<LogIn className="mr-2 h-4 w-4" />
56+
Login
57+
</Link>
58+
</Button>
59+
</div>
60+
</nav>
61+
</div>
62+
);
63+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import * as React from "react"
2+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
3+
import { Check } from "lucide-react"
4+
5+
import { cn } from "@/lib/utils"
6+
7+
const Checkbox = React.forwardRef<
8+
React.ElementRef<typeof CheckboxPrimitive.Root>,
9+
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
10+
>(({ className, ...props }, ref) => (
11+
<CheckboxPrimitive.Root
12+
ref={ref}
13+
className={cn(
14+
"peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
15+
className
16+
)}
17+
{...props}
18+
>
19+
<CheckboxPrimitive.Indicator
20+
className={cn("flex items-center justify-center text-current")}
21+
>
22+
<Check className="h-4 w-4" />
23+
</CheckboxPrimitive.Indicator>
24+
</CheckboxPrimitive.Root>
25+
))
26+
Checkbox.displayName = CheckboxPrimitive.Root.displayName
27+
28+
export { Checkbox }

frontend/src/components/ui/label.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as React from "react"
2+
import * as LabelPrimitive from "@radix-ui/react-label"
3+
import { cva, type VariantProps } from "class-variance-authority"
4+
5+
import { cn } from "@/lib/utils"
6+
7+
const labelVariants = cva(
8+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
9+
)
10+
11+
const Label = React.forwardRef<
12+
React.ElementRef<typeof LabelPrimitive.Root>,
13+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
14+
VariantProps<typeof labelVariants>
15+
>(({ className, ...props }, ref) => (
16+
<LabelPrimitive.Root
17+
ref={ref}
18+
className={cn(labelVariants(), className)}
19+
{...props}
20+
/>
21+
))
22+
Label.displayName = LabelPrimitive.Root.displayName
23+
24+
export { Label }

frontend/src/pages/_app.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
import { DeepgramContextProvider } from '@/context/DeepgramContextProvider';
22
import { MicrophoneContextProvider } from '@/context/MicrophoneContextProvider';
33
import { MantineProvider } from '@mantine/core';
4+
import { Inter } from "next/font/google";
5+
import Layout from "@/components/layout/Layout";
46
import '@mantine/core/styles.css';
57
import type { AppProps } from 'next/app';
68

9+
const inter = Inter({
10+
subsets: ["latin"],
11+
variable: "--font-inter",
12+
});
13+
714
export default function App({ Component, pageProps }: AppProps) {
815
return (
916
<MicrophoneContextProvider>
1017
<DeepgramContextProvider>
1118
<MantineProvider>
12-
<Component {...pageProps} />
19+
<div className={inter.variable}>
20+
<Layout>
21+
<Component {...pageProps} />
22+
</Layout>
23+
</div>
1324
</MantineProvider>
1425
</DeepgramContextProvider>
1526
</MicrophoneContextProvider>

frontend/src/pages/index.tsx

Lines changed: 52 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,56 @@
1-
import { useState } from "react"
2-
import Playground from "./playground"
3-
import { AlertDialog, AlertDialogContent, AlertDialogDescription, AlertDialogTitle } from '@/components/ui/alert-dialog'
4-
import { Button } from "@/components/ui/button"
5-
import { AlertCircle } from 'lucide-react'
1+
import type { GetServerSidePropsContext } from "next";
2+
import { useRouter } from "next/router";
3+
import { createClient } from "@/utils/supabase/server-props";
4+
import { BrainCircuit } from "lucide-react";
65

7-
export default function Page() {
8-
const [showWelcomeModal, setShowWelcomeModal] = useState(true)
6+
interface PageProps {
7+
user?: any;
8+
}
9+
10+
export default function Page({ user }: PageProps) {
11+
const router = useRouter();
912

1013
return (
11-
<>
12-
<Playground
13-
initialSteps_={[]}
14-
processId="x"
15-
onSessionEnd={() => console.log('done')}
16-
/>
17-
18-
<AlertDialog open={showWelcomeModal} onOpenChange={setShowWelcomeModal}>
19-
<AlertDialogContent className="max-w-xl !bg-white/95">
20-
<div className="space-y-4">
21-
<AlertDialogTitle className="text-xl font-semibold text-gray-900 flex items-center gap-2">
22-
<AlertCircle className="w-6 h-6 text-yellow-500" />
23-
Important Notice
24-
</AlertDialogTitle>
25-
<AlertDialogDescription className="text-gray-700 text-base leading-relaxed">
26-
We are using the BrowserBase hobby plan 🔄, which only supports 3 concurrent browsers. If you are not able to get the web agent up and running ⚠️, it is most likely because someone else is using a remote BrowserBase browser 💻. The BrowserBase startup and scale plans 💰 are too expensive for our open source project 🔓.
27-
</AlertDialogDescription>
28-
<div className="flex justify-end pt-4">
29-
<Button
30-
onClick={() => setShowWelcomeModal(false)}
31-
className="bg-blue-600 hover:bg-blue-700 text-white"
32-
>
33-
I Understand
34-
</Button>
35-
</div>
36-
</div>
37-
</AlertDialogContent>
38-
</AlertDialog>
39-
</>
40-
)
14+
<div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-br from-gray-50 via-gray-50 to-blue-50">
15+
{/* Enlarged Logo and Title Section */}
16+
<div className="flex items-center space-x-6 mb-12">
17+
<div className="bg-gradient-to-br from-blue-600 to-indigo-600 text-white p-6 rounded-2xl shadow-xl flex items-center justify-center">
18+
<BrainCircuit className="w-16 h-16" />
19+
</div>
20+
<div>
21+
<span className="font-semibold text-gray-800 text-4xl block mb-2">Web Agent Demo</span>
22+
<p className="text-2xl text-gray-500">Interactive Browser Assistant</p>
23+
</div>
24+
</div>
25+
{user ? (
26+
<button
27+
onClick={() => router.push("/playground")}
28+
className="px-8 py-3 rounded-lg bg-blue-600 text-white font-semibold text-xl hover:bg-blue-700 transition"
29+
>
30+
Go to Playground
31+
</button>
32+
) : (
33+
<div className="flex flex-col items-center">
34+
<h1 className="text-2xl font-bold mb-4">Please log in to view this page.</h1>
35+
<button
36+
onClick={() => router.push("/login")}
37+
className="px-6 py-2 rounded bg-blue-600 text-white font-semibold hover:bg-blue-700 transition"
38+
>
39+
Go to Login
40+
</button>
41+
</div>
42+
)}
43+
</div>
44+
);
45+
}
46+
47+
export async function getServerSideProps(context: GetServerSidePropsContext) {
48+
const supabase = createClient(context);
49+
const { data } = await supabase.auth.getUser();
50+
51+
return {
52+
props: {
53+
user: data?.user || null,
54+
},
55+
};
4156
}

0 commit comments

Comments
 (0)