From 49c86a803438ef022caccd3eb4beb95df9b54fc7 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 24 Dec 2025 18:08:54 -0800 Subject: [PATCH] Implement dashboard with user session, URL management, and access control --- packages/web/src/routes/index.tsx | 243 +++++++++++++++++++++++++++++- 1 file changed, 242 insertions(+), 1 deletion(-) diff --git a/packages/web/src/routes/index.tsx b/packages/web/src/routes/index.tsx index ba981337..2167a73f 100644 --- a/packages/web/src/routes/index.tsx +++ b/packages/web/src/routes/index.tsx @@ -1,5 +1,13 @@ +import { useState, type FormEvent } from "react" import { createFileRoute, Link } from "@tanstack/react-router" import { ShaderBackground } from "@/components/ShaderBackground" +import { authClient } from "@/lib/auth-client" +import { useAccount } from "jazz-tools/react" +import { ViewerAccount, type SavedUrl } from "@/lib/jazz/schema" +import { Link2, Plus, Trash2, ExternalLink, Video, Settings, LogOut } from "lucide-react" + +// Feature flag: only this email can access stream features +const STREAM_ENABLED_EMAIL = "nikita@nikiv.dev" function LandingPage() { return ( @@ -44,6 +52,239 @@ function LandingPage() { ) } +function Dashboard() { + const { data: session } = authClient.useSession() + const me = useAccount(ViewerAccount) + + const [newUrl, setNewUrl] = useState("") + const [newTitle, setNewTitle] = useState("") + const [isAdding, setIsAdding] = useState(false) + + const canAccessStreams = session?.user?.email === STREAM_ENABLED_EMAIL + + const root = me.$isLoaded ? me.root : null + const urlList = root?.$isLoaded ? root.savedUrls : null + const savedUrls: SavedUrl[] = urlList?.$isLoaded ? [...urlList] : [] + + const handleAddUrl = (e: FormEvent) => { + e.preventDefault() + if (!newUrl.trim() || !root?.savedUrls?.$isLoaded) return + + root.savedUrls.$jazz.push({ + url: newUrl.trim(), + title: newTitle.trim() || null, + createdAt: Date.now(), + }) + + setNewUrl("") + setNewTitle("") + setIsAdding(false) + } + + const handleDeleteUrl = (index: number) => { + if (!root?.savedUrls?.$isLoaded) return + root.savedUrls.$jazz.splice(index, 1) + } + + const handleSignOut = async () => { + await authClient.signOut() + window.location.reload() + } + + if (!me.$isLoaded || !root?.$isLoaded) { + return ( +
+

Loading...

+
+ ) + } + + return ( +
+
+ {/* Header */} +
+
+

Welcome back

+

{session?.user?.email}

+
+
+ + + + +
+
+ + {/* Stream Setup - Only for nikita@nikiv.dev */} + {canAccessStreams && ( +
+
+
+

+ Manage your live stream and archive settings. +

+
+ + View Stream + + + Stream Settings + +
+
+ )} + + {/* Saved Links */} +
+
+
+ +

Saved Links

+
+ +
+ + {isAdding && ( +
+
+ + setNewUrl(e.target.value)} + placeholder="https://example.com" + className="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white placeholder:text-neutral-500 focus:outline-none focus:ring-2 focus:ring-teal-500" + /> +
+
+ + setNewTitle(e.target.value)} + placeholder="My favorite site" + className="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white placeholder:text-neutral-500 focus:outline-none focus:ring-2 focus:ring-teal-500" + /> +
+
+ + +
+
+ )} + + {savedUrls.length === 0 ? ( +
+ +

No saved links yet

+

Click "Add Link" to save your first bookmark

+
+ ) : ( +
+ {savedUrls.map((item, index) => ( +
+
+

+ {item.title || item.url} +

+ {item.title && ( +

+ {item.url} +

+ )} +
+
+ + + + +
+
+ ))} +
+ )} +
+
+
+ ) +} + +function HomePage() { + const { data: session, isPending } = authClient.useSession() + + if (isPending) { + return ( +
+

Loading...

+
+ ) + } + + if (session?.user) { + return + } + + return +} + export const Route = createFileRoute("/")({ - component: LandingPage, + component: HomePage, + ssr: false, })