diff --git a/thoughts-frontend/app/layout.tsx b/thoughts-frontend/app/layout.tsx index 8cf5f6e..bb40665 100644 --- a/thoughts-frontend/app/layout.tsx +++ b/thoughts-frontend/app/layout.tsx @@ -3,6 +3,7 @@ import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; import { AuthProvider } from "@/hooks/use-auth"; import { Toaster } from "@/components/ui/sonner"; +import { Header } from "@/components/header"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -30,7 +31,8 @@ export default function RootLayout({ className={`${geistSans.variable} ${geistMono.variable} antialiased`} > - {children} +
+
{children}
diff --git a/thoughts-frontend/app/users/[username]/page.tsx b/thoughts-frontend/app/users/[username]/page.tsx index 1e0ce06..d1d4769 100644 --- a/thoughts-frontend/app/users/[username]/page.tsx +++ b/thoughts-frontend/app/users/[username]/page.tsx @@ -1,6 +1,6 @@ import { getMe, getUserProfile, getUserThoughts, Me } from "@/lib/api"; import { UserAvatar } from "@/components/user-avatar"; -import { Calendar } from "lucide-react"; +import { Calendar, Settings } from "lucide-react"; import { Card } from "@/components/ui/card"; import { notFound } from "next/navigation"; import { cookies } from "next/headers"; @@ -8,6 +8,8 @@ import { FollowButton } from "@/components/follow-button"; import { TopFriends } from "@/components/top-friends"; import { buildThoughtThreads } from "@/lib/utils"; import { ThoughtThread } from "@/components/thought-thread"; +import { Button } from "@/components/ui/button"; +import Link from "next/link"; interface ProfilePageProps { params: { username: string }; @@ -54,7 +56,7 @@ export default async function ProfilePage({ params }: ProfilePageProps) { )}
- {!isOwnProfile && token && ( - - )} +
+ {isOwnProfile ? ( + + ) : token ? ( + + ) : null} +

{user.bio}

diff --git a/thoughts-frontend/components/header.tsx b/thoughts-frontend/components/header.tsx new file mode 100644 index 0000000..d595099 --- /dev/null +++ b/thoughts-frontend/components/header.tsx @@ -0,0 +1,39 @@ +// components/header.tsx +"use client"; + +import { useAuth } from "@/hooks/use-auth"; +import Link from "next/link"; +import { Button } from "./ui/button"; +import { UserNav } from "./user-nav"; + +export function Header() { + const { token } = useAuth(); + + return ( +
+
+
+ + Thoughts + +
+
+ +
+
+
+ ); +} diff --git a/thoughts-frontend/components/thought-card.tsx b/thoughts-frontend/components/thought-card.tsx index 6ec6766..0b6bfd3 100644 --- a/thoughts-frontend/components/thought-card.tsx +++ b/thoughts-frontend/components/thought-card.tsx @@ -70,7 +70,7 @@ export function ThoughtCard({ try { await deleteThought(thought.id, token); toast.success("Thought deleted successfully."); - router.refresh(); // Refresh the feed + router.refresh(); } catch (error) { toast.error("Failed to delete thought."); } finally { @@ -101,13 +101,16 @@ export function ThoughtCard({ -
+
{author.username} {timeAgo}
-
+ {isAuthor && ( diff --git a/thoughts-frontend/components/user-nav.tsx b/thoughts-frontend/components/user-nav.tsx new file mode 100644 index 0000000..e51fd3d --- /dev/null +++ b/thoughts-frontend/components/user-nav.tsx @@ -0,0 +1,90 @@ +// components/user-nav.tsx +"use client"; + +import { useEffect, useState } from "react"; +import { useAuth } from "@/hooks/use-auth"; +import { getMe, User } from "@/lib/api"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Button } from "@/components/ui/button"; +import { UserAvatar } from "./user-avatar"; +import { Skeleton } from "./ui/skeleton"; +import Link from "next/link"; +import { LogOut, User as UserIcon } from "lucide-react"; +import { useRouter } from "next/navigation"; + +export function UserNav() { + const { token, setToken } = useAuth(); + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + const { replace } = useRouter(); + + useEffect(() => { + if (token) { + setLoading(true); + getMe(token) + .then(setUser) + .catch(() => { + // Invalid token, log the user out + setToken(null); + }) + .finally(() => setLoading(false)); + } else { + setLoading(false); + } + }, [token, setToken]); + + const handleLogout = () => { + setToken(null); + replace("/login"); + }; + + if (loading) { + return ; + } + + if (!token || !user) { + // Render nothing if the user is not logged in + return null; + } + + return ( + + + + + + +
+

+ {user.displayName || user.username} +

+

+ @{user.username} +

+
+
+ + + + + Profile + + + + + + Log out + +
+
+ ); +}