ui/ reusable header

This commit is contained in:
Leon van Zyl
2025-08-13 11:37:24 +02:00
parent 9c70c66dc5
commit 863906de86
5 changed files with 119 additions and 93 deletions

View File

@@ -133,79 +133,76 @@ export default function ChatPage() {
const [input, setInput] = useState(""); const [input, setInput] = useState("");
if (isPending) { if (isPending) {
return ( return <div className="container mx-auto px-4 py-12">Loading...</div>;
<div className="flex justify-center items-center h-screen">
Loading...
</div>
);
} }
if (!session) { if (!session) {
return ( return (
<div className="flex justify-center items-center h-screen"> <div className="container mx-auto px-4 py-12">
<UserProfile /> <div className="max-w-3xl mx-auto">
<UserProfile />
</div>
</div> </div>
); );
} }
return ( return (
<div className="flex flex-col h-screen max-w-2xl mx-auto p-4"> <div className="container mx-auto px-4 py-8">
<div className="flex justify-between items-center mb-4 pb-4 border-b"> <div className="max-w-4xl mx-auto">
<h1 className="text-2xl font-bold">AI Chat</h1> <div className="flex justify-between items-center mb-6 pb-4 border-b">
<div className="flex items-center gap-4"> <h1 className="text-2xl font-bold">AI Chat</h1>
<span className="text-sm text-muted-foreground"> <span className="text-sm text-muted-foreground">
Welcome, {session.user.name}! Welcome, {session.user.name}!
</span> </span>
<UserProfile />
</div> </div>
</div>
<div className="flex-1 overflow-y-auto space-y-4 mb-4"> <div className="min-h-[50vh] overflow-y-auto space-y-4 mb-4">
{messages.length === 0 && ( {messages.length === 0 && (
<div className="text-center text-muted-foreground"> <div className="text-center text-muted-foreground">
Start a conversation with AI Start a conversation with AI
</div>
)}
{messages.map((message) => (
<div
key={message.id}
className={`p-3 rounded-lg ${
message.role === "user"
? "bg-primary text-primary-foreground ml-auto max-w-[80%]"
: "bg-muted max-w-[80%]"
}`}
>
<div className="text-sm font-medium mb-1">
{message.role === "user" ? "You" : "AI"}
</div> </div>
<div>{renderMessageContent(message as MaybePartsMessage)}</div> )}
</div> {messages.map((message) => (
))} <div
</div> key={message.id}
className={`p-3 rounded-lg ${
message.role === "user"
? "bg-primary text-primary-foreground ml-auto max-w-[80%]"
: "bg-muted max-w-[80%]"
}`}
>
<div className="text-sm font-medium mb-1">
{message.role === "user" ? "You" : "AI"}
</div>
<div>{renderMessageContent(message as MaybePartsMessage)}</div>
</div>
))}
</div>
<form <form
onSubmit={(e) => { onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
const text = input.trim(); const text = input.trim();
if (!text) return; if (!text) return;
sendMessage({ role: "user", parts: [{ type: "text", text }] }); sendMessage({ role: "user", parts: [{ type: "text", text }] });
setInput(""); setInput("");
}} }}
className="flex gap-2" className="flex gap-2"
>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type your message..."
className="flex-1 p-2 border border-border rounded-md focus:outline-none focus:ring-2 focus:ring-ring"
/>
<Button
type="submit"
disabled={!input.trim() || status === "streaming"}
> >
Send <input
</Button> value={input}
</form> onChange={(e) => setInput(e.target.value)}
placeholder="Type your message..."
className="flex-1 p-2 border border-border rounded-md focus:outline-none focus:ring-2 focus:ring-ring"
/>
<Button
type="submit"
disabled={!input.trim() || status === "streaming"}
>
Send
</Button>
</form>
</div>
</div> </div>
); );
} }

View File

@@ -1,32 +1,37 @@
"use client" "use client";
import { UserProfile } from "@/components/auth/user-profile" import { useSession } from "@/lib/auth-client";
import { useSession } from "@/lib/auth-client" import { UserProfile } from "@/components/auth/user-profile";
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button";
import Link from "next/link" import Link from "next/link";
export default function DashboardPage() { export default function DashboardPage() {
const { data: session, isPending } = useSession() const { data: session, isPending } = useSession();
if (isPending) { if (isPending) {
return <div className="flex justify-center items-center h-screen">Loading...</div> return (
<div className="flex justify-center items-center h-screen">
Loading...
</div>
);
} }
if (!session) { if (!session) {
return ( return (
<div className="flex justify-center items-center h-screen"> <div className="container mx-auto px-4 py-12">
<UserProfile /> <div className="max-w-3xl mx-auto">
<UserProfile />
</div>
</div> </div>
) );
} }
return ( return (
<div className="container mx-auto p-6"> <div className="container mx-auto p-6">
<div className="flex justify-between items-center mb-8"> <div className="flex justify-between items-center mb-8">
<h1 className="text-3xl font-bold">Dashboard</h1> <h1 className="text-3xl font-bold">Dashboard</h1>
<UserProfile />
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="p-6 border border-border rounded-lg"> <div className="p-6 border border-border rounded-lg">
<h2 className="text-xl font-semibold mb-2">AI Chat</h2> <h2 className="text-xl font-semibold mb-2">AI Chat</h2>
@@ -37,18 +42,22 @@ export default function DashboardPage() {
<Link href="/chat">Go to Chat</Link> <Link href="/chat">Go to Chat</Link>
</Button> </Button>
</div> </div>
<div className="p-6 border border-border rounded-lg"> <div className="p-6 border border-border rounded-lg">
<h2 className="text-xl font-semibold mb-2">Profile</h2> <h2 className="text-xl font-semibold mb-2">Profile</h2>
<p className="text-muted-foreground mb-4"> <p className="text-muted-foreground mb-4">
Manage your account settings and preferences Manage your account settings and preferences
</p> </p>
<div className="space-y-2"> <div className="space-y-2">
<p><strong>Name:</strong> {session.user.name}</p> <p>
<p><strong>Email:</strong> {session.user.email}</p> <strong>Name:</strong> {session.user.name}
</p>
<p>
<strong>Email:</strong> {session.user.email}
</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
) );
} }

View File

@@ -1,6 +1,7 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google"; import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css"; import "./globals.css";
import { SiteHeader } from "@/components/site-header";
const geistSans = Geist({ const geistSans = Geist({
variable: "--font-geist-sans", variable: "--font-geist-sans",
@@ -27,6 +28,7 @@ export default function RootLayout({
<body <body
className={`${geistSans.variable} ${geistMono.variable} antialiased`} className={`${geistSans.variable} ${geistMono.variable} antialiased`}
> >
<SiteHeader />
{children} {children}
</body> </body>
</html> </html>

View File

@@ -1,21 +1,9 @@
import Link from "next/link" import Link from "next/link";
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button";
import { UserProfile } from "@/components/auth/user-profile"
export default function Home() { export default function Home() {
return ( return (
<div className="min-h-screen flex flex-col grain"> <div className="min-h-screen flex flex-col grain">
<header className="border-b">
<div className="container mx-auto px-4 py-4 flex justify-between items-center">
<h1 className="text-2xl font-bold">
<span className="bg-clip-text text-transparent [background-image:linear-gradient(90deg,color-mix(in_oklab,var(--primary)_85%,white_0%),color-mix(in_oklab,var(--primary)_50%,white_0%))]">
Next.js Starter Kit
</span>
</h1>
<UserProfile />
</div>
</header>
<main className="flex-1 container mx-auto px-4 py-12"> <main className="flex-1 container mx-auto px-4 py-12">
<div className="max-w-4xl mx-auto text-center space-y-8"> <div className="max-w-4xl mx-auto text-center space-y-8">
<div className="space-y-4"> <div className="space-y-4">
@@ -23,7 +11,8 @@ export default function Home() {
Welcome to Your Next.js Boilerplate Welcome to Your Next.js Boilerplate
</h2> </h2>
<p className="text-xl text-muted-foreground"> <p className="text-xl text-muted-foreground">
A complete starter kit with authentication, database, AI integration, and modern tooling A complete starter kit with authentication, database, AI
integration, and modern tooling
</p> </p>
</div> </div>
@@ -58,9 +47,12 @@ export default function Home() {
<h3 className="text-2xl font-semibold">Next Steps</h3> <h3 className="text-2xl font-semibold">Next Steps</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-left"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-left">
<div className="p-4 border rounded-lg"> <div className="p-4 border rounded-lg">
<h4 className="font-medium mb-2">1. Set up environment variables</h4> <h4 className="font-medium mb-2">
1. Set up environment variables
</h4>
<p className="text-sm text-muted-foreground mb-2"> <p className="text-sm text-muted-foreground mb-2">
Copy <code>.env.example</code> to <code>.env.local</code> and configure: Copy <code>.env.example</code> to <code>.env.local</code> and
configure:
</p> </p>
<ul className="text-sm text-muted-foreground space-y-1 list-disc list-inside"> <ul className="text-sm text-muted-foreground space-y-1 list-disc list-inside">
<li>DATABASE_URL (PostgreSQL connection string)</li> <li>DATABASE_URL (PostgreSQL connection string)</li>
@@ -84,7 +76,12 @@ export default function Home() {
<Button asChild size="sm" className="w-full glow"> <Button asChild size="sm" className="w-full glow">
<Link href="/dashboard">View Dashboard</Link> <Link href="/dashboard">View Dashboard</Link>
</Button> </Button>
<Button asChild variant="outline" size="sm" className="w-full"> <Button
asChild
variant="outline"
size="sm"
className="w-full"
>
<Link href="/chat">Try AI Chat</Link> <Link href="/chat">Try AI Chat</Link>
</Button> </Button>
</div> </div>
@@ -92,7 +89,8 @@ export default function Home() {
<div className="p-4 border rounded-lg"> <div className="p-4 border rounded-lg">
<h4 className="font-medium mb-2">4. Start building</h4> <h4 className="font-medium mb-2">4. Start building</h4>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
Customize the components, add your own pages, and build your application on top of this solid foundation. Customize the components, add your own pages, and build your
application on top of this solid foundation.
</p> </p>
</div> </div>
</div> </div>
@@ -106,5 +104,5 @@ export default function Home() {
</div> </div>
</footer> </footer>
</div> </div>
) );
} }

View File

@@ -0,0 +1,20 @@
import Link from "next/link";
import { UserProfile } from "@/components/auth/user-profile";
export function SiteHeader() {
return (
<header className="border-b">
<div className="container mx-auto px-4 py-4 flex justify-between items-center">
<h1 className="text-2xl font-bold">
<Link
href="/"
className="bg-clip-text text-transparent [background-image:linear-gradient(90deg,color-mix(in_oklab,var(--primary)_85%,white_0%),color-mix(in_oklab,var(--primary)_50%,white_0%))] hover:opacity-90"
>
Next.js Starter Kit
</Link>
</h1>
<UserProfile />
</div>
</header>
);
}