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("");
if (isPending) {
return (
<div className="flex justify-center items-center h-screen">
Loading...
</div>
);
return <div className="container mx-auto px-4 py-12">Loading...</div>;
}
if (!session) {
return (
<div className="flex justify-center items-center h-screen">
<UserProfile />
<div className="container mx-auto px-4 py-12">
<div className="max-w-3xl mx-auto">
<UserProfile />
</div>
</div>
);
}
return (
<div className="flex flex-col h-screen max-w-2xl mx-auto p-4">
<div className="flex justify-between items-center mb-4 pb-4 border-b">
<h1 className="text-2xl font-bold">AI Chat</h1>
<div className="flex items-center gap-4">
<div className="container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto">
<div className="flex justify-between items-center mb-6 pb-4 border-b">
<h1 className="text-2xl font-bold">AI Chat</h1>
<span className="text-sm text-muted-foreground">
Welcome, {session.user.name}!
</span>
<UserProfile />
</div>
</div>
<div className="flex-1 overflow-y-auto space-y-4 mb-4">
{messages.length === 0 && (
<div className="text-center text-muted-foreground">
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 className="min-h-[50vh] overflow-y-auto space-y-4 mb-4">
{messages.length === 0 && (
<div className="text-center text-muted-foreground">
Start a conversation with AI
</div>
<div>{renderMessageContent(message as MaybePartsMessage)}</div>
</div>
))}
</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>{renderMessageContent(message as MaybePartsMessage)}</div>
</div>
))}
</div>
<form
onSubmit={(e) => {
e.preventDefault();
const text = input.trim();
if (!text) return;
sendMessage({ role: "user", parts: [{ type: "text", text }] });
setInput("");
}}
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"}
<form
onSubmit={(e) => {
e.preventDefault();
const text = input.trim();
if (!text) return;
sendMessage({ role: "user", parts: [{ type: "text", text }] });
setInput("");
}}
className="flex gap-2"
>
Send
</Button>
</form>
<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
</Button>
</form>
</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 { Button } from "@/components/ui/button"
import Link from "next/link"
import { useSession } from "@/lib/auth-client";
import { UserProfile } from "@/components/auth/user-profile";
import { Button } from "@/components/ui/button";
import Link from "next/link";
export default function DashboardPage() {
const { data: session, isPending } = useSession()
const { data: session, isPending } = useSession();
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) {
return (
<div className="flex justify-center items-center h-screen">
<UserProfile />
<div className="container mx-auto px-4 py-12">
<div className="max-w-3xl mx-auto">
<UserProfile />
</div>
</div>
)
);
}
return (
<div className="container mx-auto p-6">
<div className="flex justify-between items-center mb-8">
<h1 className="text-3xl font-bold">Dashboard</h1>
<UserProfile />
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="p-6 border border-border rounded-lg">
<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>
</Button>
</div>
<div className="p-6 border border-border rounded-lg">
<h2 className="text-xl font-semibold mb-2">Profile</h2>
<p className="text-muted-foreground mb-4">
Manage your account settings and preferences
</p>
<div className="space-y-2">
<p><strong>Name:</strong> {session.user.name}</p>
<p><strong>Email:</strong> {session.user.email}</p>
<p>
<strong>Name:</strong> {session.user.name}
</p>
<p>
<strong>Email:</strong> {session.user.email}
</p>
</div>
</div>
</div>
</div>
)
}
);
}

View File

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

View File

@@ -1,21 +1,9 @@
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { UserProfile } from "@/components/auth/user-profile"
import Link from "next/link";
import { Button } from "@/components/ui/button";
export default function Home() {
return (
<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">
<div className="max-w-4xl mx-auto text-center space-y-8">
<div className="space-y-4">
@@ -23,7 +11,8 @@ export default function Home() {
Welcome to Your Next.js Boilerplate
</h2>
<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>
</div>
@@ -58,9 +47,12 @@ export default function Home() {
<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="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">
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>
<ul className="text-sm text-muted-foreground space-y-1 list-disc list-inside">
<li>DATABASE_URL (PostgreSQL connection string)</li>
@@ -84,7 +76,12 @@ export default function Home() {
<Button asChild size="sm" className="w-full glow">
<Link href="/dashboard">View Dashboard</Link>
</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>
</Button>
</div>
@@ -92,7 +89,8 @@ export default function Home() {
<div className="p-4 border rounded-lg">
<h4 className="font-medium mb-2">4. Start building</h4>
<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>
</div>
</div>
@@ -106,5 +104,5 @@ export default function Home() {
</div>
</footer>
</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>
);
}