Skip to content

Commit 66583bf

Browse files
committed
fix: implement global session management and fix page access
🔧 Authentication System Improvements: - Create global AuthProvider for consistent session management - Add demo mode support with localStorage persistence - Fix client-side authentication state management - Ensure session persists across page navigation 📱 Page Access Fixes: - Convert server-side auth pages to client-side - Update graph, actions, modules, workbench pages - Add proper loading states and redirects - Support both Supabase auth and demo mode ✨ Demo Mode Features: - Seamless demo mode activation from login/signup - Persistent demo session across page refreshes - Clear demo mode indicators in UI - Proper demo mode sign out handling 🛡️ Session Persistence: - Global auth state management via React Context - Automatic session restoration on page load - Consistent authentication checks across all pages - Proper cleanup on sign out This fixes the issue where users couldn't access protected pages after login and ensures session state is maintained globally.
1 parent b4678fd commit 66583bf

11 files changed

Lines changed: 510 additions & 165 deletions

File tree

app/actions/page.tsx

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,31 @@
1-
import { createClient, isSupabaseConfigured } from "@/lib/supabase/server"
2-
import { redirect } from "next/navigation"
1+
"use client"
2+
3+
import { useAuth } from "@/components/providers/auth-provider"
4+
import { useRouter } from "next/navigation"
5+
import { useEffect } from "react"
36
import ActionsDashboard from "@/components/actions-dashboard"
7+
import { LoadingSpinner } from "@/components/ui/loading-spinner"
8+
9+
export default function ActionsPage() {
10+
const { user, loading } = useAuth()
11+
const router = useRouter()
412

5-
export default async function ActionsPage() {
6-
// If Supabase is not configured, show setup message
7-
if (!isSupabaseConfigured) {
13+
useEffect(() => {
14+
if (!loading && !user) {
15+
router.push("/auth/login")
16+
}
17+
}, [user, loading, router])
18+
19+
if (loading) {
820
return (
9-
<div className="flex min-h-screen items-center justify-center bg-slate-950">
10-
<h1 className="text-2xl font-bold mb-4 text-white">Connect Supabase to get started</h1>
21+
<div className="min-h-screen bg-slate-950 flex items-center justify-center">
22+
<LoadingSpinner size="lg" text="Loading action management..." />
1123
</div>
1224
)
1325
}
1426

15-
// Get the user from the server
16-
const supabase = await createClient()
17-
const {
18-
data: { user },
19-
} = await supabase.auth.getUser()
20-
21-
// If no user, redirect to login
2227
if (!user) {
23-
redirect("/auth/login")
28+
return null // Will redirect via useEffect
2429
}
2530

2631
return (

app/auth/login/page.tsx

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,55 @@
11
"use client"
22

3-
import { useState } from "react"
3+
import { useState, useEffect } from "react"
44
import { useRouter } from "next/navigation"
55
import Link from "next/link"
66
import { Button } from "@/components/ui/button"
77
import { Input } from "@/components/ui/input"
88
import { Label } from "@/components/ui/label"
99
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
1010
import { Alert, AlertDescription } from "@/components/ui/alert"
11-
import { createClient } from "@/lib/supabase/client"
11+
import { useAuth } from "@/components/providers/auth-provider"
1212
import { Eye, EyeOff, Home, Loader2 } from "lucide-react"
1313
import { toastUtils } from "@/lib/toast-utils"
1414

1515
export default function LoginPage() {
1616
const [email, setEmail] = useState("")
1717
const [password, setPassword] = useState("")
1818
const [showPassword, setShowPassword] = useState(false)
19-
const [loading, setLoading] = useState(false)
2019
const [error, setError] = useState("")
2120
const router = useRouter()
21+
22+
const { signIn, loading, user, enterDemoMode } = useAuth()
23+
24+
// Redirect if already authenticated
25+
useEffect(() => {
26+
if (user) {
27+
router.push("/dashboard")
28+
}
29+
}, [user, router])
2230

2331
const handleSubmit = async (e: React.FormEvent) => {
2432
e.preventDefault()
25-
setLoading(true)
2633
setError("")
2734

2835
if (!email || !password) {
2936
setError("Please fill in all fields")
30-
setLoading(false)
3137
return
3238
}
3339

34-
try {
35-
const supabase = createClient()
36-
const { error } = await supabase.auth.signInWithPassword({
37-
email,
38-
password,
39-
})
40-
41-
if (error) {
42-
setError(error.message)
43-
toastUtils.error(error.message)
44-
} else {
45-
toastUtils.success("Successfully signed in!")
46-
router.push("/dashboard")
47-
router.refresh()
48-
}
49-
} catch (err) {
50-
const errorMessage = err instanceof Error ? err.message : "An unexpected error occurred"
51-
setError(errorMessage)
52-
toastUtils.error(errorMessage)
53-
} finally {
54-
setLoading(false)
40+
const result = await signIn(email, password)
41+
42+
if (result.error) {
43+
setError(result.error)
44+
toastUtils.error(result.error)
45+
} else {
46+
// Success will be handled by the auth provider
47+
router.push("/dashboard")
5548
}
5649
}
5750

5851
const handleDemoLogin = () => {
59-
toastUtils.info("Demo mode - redirecting to dashboard")
60-
router.push("/dashboard")
52+
enterDemoMode()
6153
}
6254

6355
return (

app/auth/signup/page.tsx

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
"use client"
22

3-
import { useState } from "react"
3+
import { useState, useEffect } from "react"
44
import { useRouter } from "next/navigation"
55
import Link from "next/link"
66
import { Button } from "@/components/ui/button"
77
import { Input } from "@/components/ui/input"
88
import { Label } from "@/components/ui/label"
99
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
1010
import { Alert, AlertDescription } from "@/components/ui/alert"
11-
import { createClient } from "@/lib/supabase/client"
11+
import { useAuth } from "@/components/providers/auth-provider"
1212
import { Eye, EyeOff, Home, Loader2, CheckCircle } from "lucide-react"
1313
import { toastUtils } from "@/lib/toast-utils"
1414

@@ -18,10 +18,18 @@ export default function SignUpPage() {
1818
const [confirmPassword, setConfirmPassword] = useState("")
1919
const [showPassword, setShowPassword] = useState(false)
2020
const [showConfirmPassword, setShowConfirmPassword] = useState(false)
21-
const [loading, setLoading] = useState(false)
2221
const [error, setError] = useState("")
2322
const [success, setSuccess] = useState(false)
2423
const router = useRouter()
24+
25+
const { signUp, loading, user, enterDemoMode } = useAuth()
26+
27+
// Redirect if already authenticated
28+
useEffect(() => {
29+
if (user) {
30+
router.push("/dashboard")
31+
}
32+
}, [user, router])
2533

2634
const validatePassword = (password: string) => {
2735
if (password.length < 6) {
@@ -32,55 +40,38 @@ export default function SignUpPage() {
3240

3341
const handleSubmit = async (e: React.FormEvent) => {
3442
e.preventDefault()
35-
setLoading(true)
3643
setError("")
3744
setSuccess(false)
3845

3946
if (!email || !password || !confirmPassword) {
4047
setError("Please fill in all fields")
41-
setLoading(false)
4248
return
4349
}
4450

4551
const passwordError = validatePassword(password)
4652
if (passwordError) {
4753
setError(passwordError)
48-
setLoading(false)
4954
return
5055
}
5156

5257
if (password !== confirmPassword) {
5358
setError("Passwords do not match")
54-
setLoading(false)
5559
return
5660
}
5761

58-
try {
59-
const supabase = createClient()
60-
const { error } = await supabase.auth.signUp({
61-
email,
62-
password,
63-
})
64-
65-
if (error) {
66-
setError(error.message)
67-
toastUtils.error(error.message)
68-
} else {
69-
setSuccess(true)
70-
toastUtils.success("Account created successfully! Please check your email to verify your account.")
71-
}
72-
} catch (err) {
73-
const errorMessage = err instanceof Error ? err.message : "An unexpected error occurred"
74-
setError(errorMessage)
75-
toastUtils.error(errorMessage)
76-
} finally {
77-
setLoading(false)
62+
const result = await signUp(email, password)
63+
64+
if (result.error) {
65+
setError(result.error)
66+
toastUtils.error(result.error)
67+
} else if (result.success) {
68+
setSuccess(true)
69+
toastUtils.success(result.success)
7870
}
7971
}
8072

8173
const handleDemoMode = () => {
82-
toastUtils.info("Demo mode - redirecting to dashboard")
83-
router.push("/dashboard")
74+
enterDemoMode()
8475
}
8576

8677
if (success) {

app/dashboard/page.tsx

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,51 @@
1+
"use client"
2+
3+
import { useAuth } from "@/components/providers/auth-provider"
4+
import { useRouter } from "next/navigation"
5+
import { useEffect } from "react"
16
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
27
import { Badge } from "@/components/ui/badge"
38
import { Button } from "@/components/ui/button"
9+
import { LoadingSpinner } from "@/components/ui/loading-spinner"
410
import { BarChart3, Network, Layers, Zap, Eye, BookOpen, Plus, TrendingUp, Clock, CheckCircle } from "lucide-react"
511
import Link from "next/link"
612

713
export default function DashboardPage() {
14+
const { user, loading, isDemoMode } = useAuth()
15+
const router = useRouter()
16+
17+
useEffect(() => {
18+
if (!loading && !user) {
19+
router.push("/auth/login")
20+
}
21+
}, [user, loading, router])
22+
23+
if (loading) {
24+
return (
25+
<div className="min-h-screen bg-slate-950 flex items-center justify-center">
26+
<LoadingSpinner size="lg" text="Loading dashboard..." />
27+
</div>
28+
)
29+
}
30+
31+
if (!user) {
32+
return null // Will redirect via useEffect
33+
}
834
return (
935
<div className="container mx-auto px-4 py-8">
1036
<div className="max-w-6xl mx-auto space-y-8">
1137
{/* Header */}
1238
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
1339
<div>
1440
<h1 className="text-3xl font-bold text-white">Dashboard</h1>
15-
<p className="text-slate-400 mt-1">Welcome to your TimeWeave workspace</p>
41+
<p className="text-slate-400 mt-1">
42+
Welcome to your TimeWeave workspace
43+
{isDemoMode && (
44+
<Badge variant="secondary" className="ml-2">
45+
Demo Mode
46+
</Badge>
47+
)}
48+
</p>
1649
</div>
1750
<div className="flex gap-2">
1851
<Link href="/demo">

app/graph/page.tsx

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,31 @@
1-
import { createClient, isSupabaseConfigured } from "@/lib/supabase/server"
2-
import { redirect } from "next/navigation"
1+
"use client"
2+
3+
import { useAuth } from "@/components/providers/auth-provider"
4+
import { useRouter } from "next/navigation"
5+
import { useEffect } from "react"
36
import GraphDashboard from "@/components/graph-dashboard"
7+
import { LoadingSpinner } from "@/components/ui/loading-spinner"
8+
9+
export default function GraphPage() {
10+
const { user, loading } = useAuth()
11+
const router = useRouter()
412

5-
export default async function GraphPage() {
6-
// If Supabase is not configured, show setup message
7-
if (!isSupabaseConfigured) {
13+
useEffect(() => {
14+
if (!loading && !user) {
15+
router.push("/auth/login")
16+
}
17+
}, [user, loading, router])
18+
19+
if (loading) {
820
return (
9-
<div className="flex min-h-screen items-center justify-center bg-slate-950">
10-
<h1 className="text-2xl font-bold mb-4 text-white">Connect Supabase to get started</h1>
21+
<div className="min-h-screen bg-slate-950 flex items-center justify-center">
22+
<LoadingSpinner size="lg" text="Loading knowledge graph..." />
1123
</div>
1224
)
1325
}
1426

15-
// Get the user from the server
16-
const supabase = await createClient()
17-
const {
18-
data: { user },
19-
} = await supabase.auth.getUser()
20-
21-
// If no user, redirect to login
2227
if (!user) {
23-
redirect("/auth/login")
28+
return null // Will redirect via useEffect
2429
}
2530

2631
return (

app/layout.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Navigation from "@/components/navigation"
77
import { Toaster } from "react-hot-toast"
88
import { ErrorProvider } from "@/components/providers/error-provider"
99
import { LoadingProvider } from "@/components/providers/loading-provider"
10+
import { AuthProvider } from "@/components/providers/auth-provider"
1011
import { ErrorBoundary } from "@/components/ui/error-boundary"
1112
import { NetworkStatus } from "@/components/ui/network-status"
1213

@@ -36,7 +37,8 @@ html {
3637
<body>
3738
<ErrorProvider>
3839
<LoadingProvider>
39-
<ErrorBoundary level="page" maxRetries={3} showErrorDetails={process.env.NODE_ENV === "development"}>
40+
<AuthProvider>
41+
<ErrorBoundary level="page" maxRetries={3} showErrorDetails={process.env.NODE_ENV === "development"}>
4042
<Navigation />
4143
<div className="pt-16 pb-16 md:pb-0">
4244
{/* Network status banner */}
@@ -78,7 +80,8 @@ html {
7880
}}
7981
/>
8082
</div>
81-
</ErrorBoundary>
83+
</ErrorBoundary>
84+
</AuthProvider>
8285
</LoadingProvider>
8386
</ErrorProvider>
8487
</body>

0 commit comments

Comments
 (0)