Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apps/web/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import NextAuth from "next-auth"
import { authOptions } from "@/lib/auth"

const handler = NextAuth(authOptions)

export { handler as GET, handler as POST }
51 changes: 51 additions & 0 deletions apps/web/app/api/auth/signup/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { NextRequest, NextResponse } from "next/server"
// Use mock client temporarily until Prisma generation works
import { PrismaClient } from "@collax/prisma/src/mock"

const prisma = new PrismaClient()

export async function POST(req: NextRequest) {
try {
const { email, password, name } = await req.json()

if (!email || !password || !name) {
return NextResponse.json(
{ message: "Missing required fields" },
{ status: 400 }
)
}

// Check if user already exists
const existingUser = await prisma.user.findUnique({
where: { email }
})

if (existingUser) {
return NextResponse.json(
{ message: "User already exists" },
{ status: 400 }
)
}

// Create user (in production, hash the password)
const user = await prisma.user.create({
data: {
email,
name,
// Note: In production, hash the password before storing
// password: await bcrypt.hash(password, 12)
}
})

return NextResponse.json(
{ message: "User created successfully", userId: user.id },
{ status: 201 }
)
} catch (error) {
console.error("Signup error:", error)
return NextResponse.json(
{ message: "Internal server error" },
{ status: 500 }
)
}
}
100 changes: 100 additions & 0 deletions apps/web/app/auth/signin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"use client"

import { signIn } from "next-auth/react"
import { Button } from "@collax/ui/components/ui/button"
import { useState } from "react"

export default function SignIn() {
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")

const handleCredentialsSignIn = async (e: React.FormEvent) => {
e.preventDefault()
await signIn("credentials", {
email,
password,
callbackUrl: "/",
})
}

const handleGoogleSignIn = () => {
signIn("google", { callbackUrl: "/" })
}

return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full space-y-8">
<div>
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
Sign in to Collax
</h2>
<p className="mt-2 text-center text-sm text-gray-600">
Your collaborative note-taking platform
</p>
</div>
<div className="mt-8 space-y-6">
<Button
onClick={handleGoogleSignIn}
className="w-full flex justify-center py-2 px-4"
variant="outline"
>
Sign in with Google
</Button>

<div className="relative">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-300" />
</div>
<div className="relative flex justify-center text-sm">
<span className="px-2 bg-gray-50 text-gray-500">Or continue with</span>
</div>
</div>

<form className="mt-8 space-y-6" onSubmit={handleCredentialsSignIn}>
<div>
<label htmlFor="email" className="sr-only">
Email address
</label>
<input
id="email"
name="email"
type="email"
autoComplete="email"
required
className="relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
placeholder="Email address"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div>
<label htmlFor="password" className="sr-only">
Password
</label>
<input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
className="relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>

<div>
<Button
type="submit"
className="w-full flex justify-center py-2 px-4"
>
Sign in
</Button>
</div>
</form>
</div>
</div>
</div>
)
}
154 changes: 154 additions & 0 deletions apps/web/app/auth/signup/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
"use client"

import { signIn } from "next-auth/react"
import { Button } from "@collax/ui/components/ui/button"
import { useState } from "react"
import Link from "next/link"

export default function SignUp() {
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [name, setName] = useState("")
const [loading, setLoading] = useState(false)

const handleSignUp = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)

try {
const response = await fetch("/api/auth/signup", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email, password, name }),
})

if (response.ok) {
// Sign in after successful registration
await signIn("credentials", {
email,
password,
callbackUrl: "/",
})
} else {
const data = await response.json()
alert(data.message || "Something went wrong")
}
} catch {
alert("Something went wrong")
} finally {
setLoading(false)
}
}

const handleGoogleSignIn = () => {
signIn("google", { callbackUrl: "/" })
}

return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full space-y-8">
<div>
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
Create your account
</h2>
<p className="mt-2 text-center text-sm text-gray-600">
Join Collax and start collaborating
</p>
</div>
<div className="mt-8 space-y-6">
<Button
onClick={handleGoogleSignIn}
className="w-full flex justify-center py-2 px-4"
variant="outline"
>
Sign up with Google
</Button>

<div className="relative">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-300" />
</div>
<div className="relative flex justify-center text-sm">
<span className="px-2 bg-gray-50 text-gray-500">Or continue with</span>
</div>
</div>

<form className="mt-8 space-y-6" onSubmit={handleSignUp}>
<div>
<label htmlFor="name" className="sr-only">
Full name
</label>
<input
id="name"
name="name"
type="text"
autoComplete="name"
required
className="relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
placeholder="Full name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
<div>
<label htmlFor="email" className="sr-only">
Email address
</label>
<input
id="email"
name="email"
type="email"
autoComplete="email"
required
className="relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
placeholder="Email address"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div>
<label htmlFor="password" className="sr-only">
Password
</label>
<input
id="password"
name="password"
type="password"
autoComplete="new-password"
required
className="relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>

<div>
<Button
type="submit"
className="w-full flex justify-center py-2 px-4"
disabled={loading}
>
{loading ? "Creating account..." : "Sign up"}
</Button>
</div>

<div className="text-center">
<span className="text-sm text-gray-600">
Already have an account?{" "}
<Link
href="/auth/signin"
className="font-medium text-indigo-600 hover:text-indigo-500"
>
Sign in
</Link>
</span>
</div>
</form>
</div>
</div>
</div>
)
}
62 changes: 62 additions & 0 deletions apps/web/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"use client"

import { useSession } from "next-auth/react"
import AuthNav from "@/components/auth-nav"
import { Button } from "@collax/ui/components/ui/button"

import Link from "next/link"

export default function Dashboard() {
const { data: session } = useSession()

return (
<div className="min-h-screen bg-gray-50">
{/* Navigation Header */}
<nav className="bg-white shadow-sm border-b">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16">
<div className="flex items-center">
<Link href="/" className="text-xl font-bold text-gray-900">Collax</Link>
</div>
<div className="flex items-center">
<AuthNav />
</div>
</div>
</div>
</nav>

{/* Main Content */}
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="mb-8">
<h1 className="text-3xl font-bold text-gray-900">Dashboard</h1>
<p className="text-gray-600">Welcome back, {session?.user?.name || session?.user?.email}!</p>
</div>

<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-xl font-semibold text-gray-800 mb-4">My Notes</h2>
<p className="text-gray-600 mb-4">Manage your personal notes</p>
<Button className="w-full">View Notes</Button>
</div>

<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-xl font-semibold text-gray-800 mb-4">Shared Notes</h2>
<p className="text-gray-600 mb-4">Collaborate with others</p>
<Button className="w-full" variant="outline">View Shared</Button>
</div>

<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-xl font-semibold text-gray-800 mb-4">Create New</h2>
<p className="text-gray-600 mb-4">Start a new note</p>
<Button className="w-full" variant="secondary">New Note</Button>
</div>
</div>

<div className="mt-8 bg-white rounded-lg shadow p-6">
<h2 className="text-xl font-semibold text-gray-800 mb-4">Recent Activity</h2>
<p className="text-gray-600">Your recent notes and collaborations will appear here.</p>
</div>
</main>
</div>
)
}
Loading