feat: implement sidebar and random pages for test

This commit is contained in:
JSC
2025-06-28 19:47:46 +02:00
parent 293dcdd22a
commit 59ae7d8bf7
9 changed files with 836 additions and 146 deletions

View File

@@ -0,0 +1,83 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
export function ActivityPage() {
return (
<div className="space-y-6">
<div>
<h1 className="text-3xl font-bold">Activity</h1>
<p className="text-muted-foreground">View recent activity and logs</p>
</div>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
<Card>
<CardHeader>
<CardTitle>Recent Actions</CardTitle>
<CardDescription>Your recent activity</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex items-center justify-between text-sm">
<span>Logged in via Google</span>
<span className="text-muted-foreground">2 minutes ago</span>
</div>
<div className="flex items-center justify-between text-sm">
<span>Updated profile</span>
<span className="text-muted-foreground">1 hour ago</span>
</div>
<div className="flex items-center justify-between text-sm">
<span>Changed settings</span>
<span className="text-muted-foreground">2 hours ago</span>
</div>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>System Events</CardTitle>
<CardDescription>System-wide activity</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex items-center justify-between text-sm">
<span>New user registered</span>
<span className="text-muted-foreground">15 minutes ago</span>
</div>
<div className="flex items-center justify-between text-sm">
<span>Database backup completed</span>
<span className="text-muted-foreground">1 hour ago</span>
</div>
<div className="flex items-center justify-between text-sm">
<span>System update applied</span>
<span className="text-muted-foreground">3 hours ago</span>
</div>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Statistics</CardTitle>
<CardDescription>Activity overview</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex items-center justify-between text-sm">
<span>Total Sessions</span>
<span className="font-medium">42</span>
</div>
<div className="flex items-center justify-between text-sm">
<span>This Week</span>
<span className="font-medium">12</span>
</div>
<div className="flex items-center justify-between text-sm">
<span>Average Duration</span>
<span className="font-medium">1.5h</span>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
)
}

View File

@@ -0,0 +1,161 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { useAuth } from '@/contexts/AuthContext'
export function AdminUsersPage() {
const { user } = useAuth()
// Mock user data - in real app this would come from API
const users = [
{
id: '1',
name: 'John Doe',
email: 'john@example.com',
role: 'admin',
is_active: true,
providers: ['password', 'google'],
created_at: '2024-01-15T10:30:00Z'
},
{
id: '2',
name: 'Jane Smith',
email: 'jane@example.com',
role: 'user',
is_active: true,
providers: ['github'],
created_at: '2024-01-20T14:15:00Z'
},
{
id: '3',
name: 'Bob Wilson',
email: 'bob@example.com',
role: 'user',
is_active: false,
providers: ['password'],
created_at: '2024-01-25T09:45:00Z'
}
]
if (user?.role !== 'admin') {
return (
<div className="flex items-center justify-center h-96">
<Card>
<CardHeader>
<CardTitle>Access Denied</CardTitle>
<CardDescription>You don't have permission to access this page.</CardDescription>
</CardHeader>
</Card>
</div>
)
}
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold">User Management</h1>
<p className="text-muted-foreground">Manage users and their permissions</p>
</div>
<Button>Add User</Button>
</div>
<Card>
<CardHeader>
<CardTitle>Users</CardTitle>
<CardDescription>All registered users in the system</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{users.map((userData) => (
<div
key={userData.id}
className="flex items-center justify-between p-4 border rounded-lg"
>
<div className="flex items-center space-x-4">
<div className="w-10 h-10 bg-primary/10 rounded-full flex items-center justify-center">
<span className="text-sm font-medium">
{userData.name.split(' ').map(n => n[0]).join('')}
</span>
</div>
<div className="space-y-1">
<div className="flex items-center space-x-2">
<span className="font-medium">{userData.name}</span>
<span className={`px-2 py-1 rounded-full text-xs font-medium ${
userData.role === 'admin'
? 'bg-purple-100 text-purple-800'
: 'bg-green-100 text-green-800'
}`}>
{userData.role}
</span>
{!userData.is_active && (
<span className="px-2 py-1 bg-red-100 text-red-800 rounded-full text-xs font-medium">
Disabled
</span>
)}
</div>
<p className="text-sm text-muted-foreground">{userData.email}</p>
<div className="flex gap-1">
{userData.providers.map((provider) => (
<span
key={provider}
className="px-1.5 py-0.5 bg-secondary rounded text-xs"
>
{provider}
</span>
))}
</div>
</div>
</div>
<div className="flex items-center space-x-2">
<Button variant="outline" size="sm">
Edit
</Button>
<Button
variant={userData.is_active ? "outline" : "default"}
size="sm"
>
{userData.is_active ? 'Disable' : 'Enable'}
</Button>
</div>
</div>
))}
</div>
</CardContent>
</Card>
<div className="grid gap-6 md:grid-cols-3">
<Card>
<CardHeader>
<CardTitle>Total Users</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{users.length}</div>
<p className="text-xs text-muted-foreground">Registered users</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Active Users</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{users.filter(u => u.is_active).length}</div>
<p className="text-xs text-muted-foreground">Currently active</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Admins</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{users.filter(u => u.role === 'admin').length}</div>
<p className="text-xs text-muted-foreground">Administrator accounts</p>
</CardContent>
</Card>
</div>
</div>
)
}

View File

@@ -1,143 +1,126 @@
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { useAuth } from '@/contexts/AuthContext'
import { useNavigate } from 'react-router'
import { Link } from 'react-router'
export function DashboardPage() {
const { user, logout } = useAuth()
const navigate = useNavigate()
const handleLogout = async () => {
try {
await logout()
navigate('/login')
} catch (error) {
console.error('Logout failed:', error)
}
}
const { user } = useAuth()
if (!user) return null
return (
<div className="min-h-screen bg-gray-50">
<header className="bg-white shadow">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center py-6">
<h1 className="text-3xl font-bold text-gray-900">Dashboard</h1>
<Button onClick={handleLogout} variant="outline">
Sign out
</Button>
</div>
</div>
</header>
<div className="space-y-6">
<div>
<h1 className="text-3xl font-bold">Dashboard</h1>
<p className="text-muted-foreground">Welcome back, {user.name}!</p>
</div>
<main className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
<div className="px-4 py-6 sm:px-0">
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{/* User Profile Card */}
<Card>
<CardHeader>
<CardTitle>Profile Information</CardTitle>
<CardDescription>Your account details</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<div className="flex items-center space-x-2">
{user.picture && (
<img
src={user.picture}
alt="Profile"
className="w-8 h-8 rounded-full"
/>
)}
<div>
<p className="font-medium">{user.name}</p>
<p className="text-sm text-gray-600">{user.email}</p>
</div>
</div>
<div className="pt-2">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
user.role === 'admin'
? 'bg-purple-100 text-purple-800'
: 'bg-green-100 text-green-800'
}`}>
{user.role}
</span>
{user.is_active && (
<span className="ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
Active
</span>
)}
</div>
</CardContent>
</Card>
{/* Authentication Methods Card */}
<Card>
<CardHeader>
<CardTitle>Authentication Methods</CardTitle>
<CardDescription>How you can sign in</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-2">
{user.providers.map((provider) => (
<div key={provider} className="flex items-center justify-between">
<span className="text-sm font-medium capitalize">{provider}</span>
<span className="text-xs text-green-600">Connected</span>
</div>
))}
</div>
</CardContent>
</Card>
{/* Account Status Card */}
<Card>
<CardHeader>
<CardTitle>Account Status</CardTitle>
<CardDescription>Current account information</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<div className="flex justify-between">
<span className="text-sm">Status:</span>
<span className={`text-sm font-medium ${
user.is_active ? 'text-green-600' : 'text-red-600'
}`}>
{user.is_active ? 'Active' : 'Disabled'}
</span>
</div>
<div className="flex justify-between">
<span className="text-sm">Role:</span>
<span className="text-sm font-medium">{user.role}</span>
</div>
<div className="flex justify-between">
<span className="text-sm">User ID:</span>
<span className="text-sm font-mono">{user.id}</span>
</div>
</CardContent>
</Card>
</div>
{/* Admin Section */}
{user.role === 'admin' && (
<div className="mt-8">
<Card>
<CardHeader>
<CardTitle>Admin Panel</CardTitle>
<CardDescription>Administrative functions</CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm text-gray-600 mb-4">
You have administrator privileges. You can manage users and system settings.
</p>
<div className="space-x-2">
<Button size="sm">Manage Users</Button>
<Button size="sm" variant="outline">System Settings</Button>
</div>
</CardContent>
</Card>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{/* User Profile Card */}
<Card>
<CardHeader>
<CardTitle>Profile Information</CardTitle>
<CardDescription>Your account details</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<div className="flex items-center space-x-2">
{user.picture && (
<img
src={user.picture}
alt="Profile"
className="w-8 h-8 rounded-full"
/>
)}
<div>
<p className="font-medium">{user.name}</p>
<p className="text-sm text-muted-foreground">{user.email}</p>
</div>
</div>
)}
</div>
</main>
<div className="pt-2">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
user.role === 'admin'
? 'bg-purple-100 text-purple-800'
: 'bg-green-100 text-green-800'
}`}>
{user.role}
</span>
{user.is_active && (
<span className="ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
Active
</span>
)}
</div>
</CardContent>
</Card>
{/* Authentication Methods Card */}
<Card>
<CardHeader>
<CardTitle>Authentication Methods</CardTitle>
<CardDescription>How you can sign in</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-2">
{user.providers.map((provider) => (
<div key={provider} className="flex items-center justify-between">
<span className="text-sm font-medium capitalize">{provider}</span>
<span className="text-xs text-green-600">Connected</span>
</div>
))}
</div>
</CardContent>
</Card>
{/* Quick Actions Card */}
<Card>
<CardHeader>
<CardTitle>Quick Actions</CardTitle>
<CardDescription>Common tasks and shortcuts</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<Link to="/settings">
<Button variant="outline" className="w-full justify-start">
Update Settings
</Button>
</Link>
<Link to="/activity">
<Button variant="outline" className="w-full justify-start">
View Activity
</Button>
</Link>
{user.role === 'admin' && (
<Link to="/admin/users">
<Button variant="outline" className="w-full justify-start">
Manage Users
</Button>
</Link>
)}
</CardContent>
</Card>
</div>
{/* Admin Section */}
{user.role === 'admin' && (
<Card>
<CardHeader>
<CardTitle>Admin Panel</CardTitle>
<CardDescription>Administrative functions and system overview</CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground mb-4">
You have administrator privileges. You can manage users and system settings.
</p>
<div className="flex gap-2">
<Link to="/admin/users">
<Button size="sm">Manage Users</Button>
</Link>
<Link to="/settings">
<Button size="sm" variant="outline">System Settings</Button>
</Link>
</div>
</CardContent>
</Card>
)}
</div>
)
}

165
src/pages/SettingsPage.tsx Normal file
View File

@@ -0,0 +1,165 @@
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { useAuth } from '@/contexts/AuthContext'
export function SettingsPage() {
const { user } = useAuth()
const [isLoading, setIsLoading] = useState(false)
const handleSave = async () => {
setIsLoading(true)
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000))
setIsLoading(false)
}
const handleRegenerateApiToken = async () => {
setIsLoading(true)
try {
const response = await fetch('/api/auth/regenerate-api-token', {
method: 'POST',
credentials: 'include',
})
if (response.ok) {
const data = await response.json()
alert(`New API token: ${data.api_token}`)
}
} catch (error) {
console.error('Failed to regenerate API token:', error)
} finally {
setIsLoading(false)
}
}
if (!user) return null
return (
<div className="space-y-6">
<div>
<h1 className="text-3xl font-bold">Settings</h1>
<p className="text-muted-foreground">Manage your account settings and preferences</p>
</div>
<div className="grid gap-6 md:grid-cols-2">
<Card>
<CardHeader>
<CardTitle>Profile Information</CardTitle>
<CardDescription>Update your personal information</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">Full Name</Label>
<Input id="name" defaultValue={user.name} />
</div>
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input id="email" defaultValue={user.email} disabled />
<p className="text-xs text-muted-foreground">
Email cannot be changed directly. Contact support if needed.
</p>
</div>
<Button onClick={handleSave} disabled={isLoading}>
{isLoading ? 'Saving...' : 'Save Changes'}
</Button>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Account Information</CardTitle>
<CardDescription>View your account details</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label>User ID</Label>
<Input value={user.id} disabled />
</div>
<div className="space-y-2">
<Label>Role</Label>
<Input value={user.role} disabled />
</div>
<div className="space-y-2">
<Label>Status</Label>
<Input value={user.is_active ? 'Active' : 'Disabled'} disabled />
</div>
<div className="space-y-2">
<Label>Authentication Methods</Label>
<div className="flex gap-2 flex-wrap">
{user.providers.map((provider) => (
<span
key={provider}
className="px-2 py-1 bg-secondary rounded-md text-xs font-medium"
>
{provider}
</span>
))}
</div>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>API Access</CardTitle>
<CardDescription>Manage your API token for programmatic access</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label>API Token</Label>
<Input
value={user.api_token ? `${user.api_token.substring(0, 8)}...` : 'No token'}
disabled
/>
<p className="text-xs text-muted-foreground">
Use this token for API authentication. Keep it secure.
</p>
</div>
<Button onClick={handleRegenerateApiToken} disabled={isLoading} variant="outline">
{isLoading ? 'Generating...' : 'Regenerate API Token'}
</Button>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Preferences</CardTitle>
<CardDescription>Customize your experience</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label>Theme</Label>
<select className="w-full h-9 px-3 rounded-md border border-input bg-background">
<option>Light</option>
<option>Dark</option>
<option>System</option>
</select>
</div>
<div className="space-y-2">
<Label>Language</Label>
<select className="w-full h-9 px-3 rounded-md border border-input bg-background">
<option>English</option>
<option>French</option>
<option>Spanish</option>
</select>
</div>
<Button onClick={handleSave} disabled={isLoading}>
{isLoading ? 'Saving...' : 'Save Preferences'}
</Button>
</CardContent>
</Card>
</div>
</div>
)
}