basic stracture and auth
in memory db :)
This commit is contained in:
commit
3a554ba434
71 files changed
+28535
No files matched your search
@@ -0,0 +1,11 @@
|
||||
import { AnalyticsDashboard } from '@/components/analytics/AnalyticsDashboard'
|
||||
|
||||
export default function AnalyticsPage() {
|
||||
return (
|
||||
<main className="min-h-screen bg-gray-50">
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<AnalyticsDashboard />
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import bcrypt from 'bcryptjs'
|
||||
import { users } from '../../../../lib/database'
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { email, password } = await request.json()
|
||||
|
||||
if (!email || !password) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Email and password are required' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const user = users.find(u => u.email === email)
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid credentials' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const isValidPassword = await bcrypt.compare(password, user.password)
|
||||
|
||||
if (!isValidPassword) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid credentials' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const { password: _, ...userWithoutPassword } = user
|
||||
|
||||
return NextResponse.json({
|
||||
message: 'Login successful',
|
||||
user: userWithoutPassword
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Login error:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import bcrypt from 'bcryptjs'
|
||||
import { users, clients } from '../../../../lib/database'
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { email, password, firstName, lastName, phone } = await request.json()
|
||||
|
||||
if (!email || !password || !firstName || !lastName) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Missing required fields' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const existingUser = users.find(u => u.email === email)
|
||||
|
||||
if (existingUser) {
|
||||
return NextResponse.json(
|
||||
{ error: 'User already exists' },
|
||||
{ status: 409 }
|
||||
)
|
||||
}
|
||||
|
||||
const hashedPassword = await bcrypt.hash(password, 10)
|
||||
const userId = Math.random().toString(36).substr(2, 9)
|
||||
|
||||
const newUser = {
|
||||
id: userId,
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
password: hashedPassword,
|
||||
phone,
|
||||
role: 'client',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}
|
||||
|
||||
users.push(newUser)
|
||||
|
||||
const newClient = {
|
||||
id: Math.random().toString(36).substr(2, 9),
|
||||
userId,
|
||||
membershipType: 'basic',
|
||||
membershipStatus: 'active',
|
||||
joinDate: new Date(),
|
||||
}
|
||||
|
||||
clients.push(newClient)
|
||||
|
||||
const { password: _, ...userWithoutPassword } = newUser
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
message: 'User registered successfully',
|
||||
user: { ...userWithoutPassword, client: newClient }
|
||||
},
|
||||
{ status: 201 }
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('Registration error:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
const usersWithoutPassword = users.map(({ password: _, ...user }) => user)
|
||||
return NextResponse.json({ users: usersWithoutPassword })
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { users, clients } from '../../../lib/database'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const role = searchParams.get('role')
|
||||
|
||||
let filteredUsers = users
|
||||
|
||||
if (role) {
|
||||
filteredUsers = users.filter(user => user.role === role)
|
||||
}
|
||||
|
||||
const usersWithClients = filteredUsers.map(({ password: _, ...user }) => {
|
||||
const client = clients.find(c => c.userId === user.id)
|
||||
return { ...user, client }
|
||||
})
|
||||
|
||||
return NextResponse.json({ users: usersWithClients })
|
||||
} catch (error) {
|
||||
console.error('Get users error:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { Metadata } from 'next'
|
||||
import { Inter } from 'next/font/google'
|
||||
import './globals.css'
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'FitAI Admin',
|
||||
description: 'Fitness management admin dashboard',
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { UserManagement } from '@/components/users/UserManagement'
|
||||
import { AnalyticsDashboard } from '@/components/analytics/AnalyticsDashboard'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className="min-h-screen bg-gray-50">
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<h1 className="text-4xl font-bold text-gray-900">
|
||||
FitAI Admin Dashboard
|
||||
</h1>
|
||||
<nav className="flex gap-4">
|
||||
<Link
|
||||
href="/users"
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
User Management
|
||||
</Link>
|
||||
<Link
|
||||
href="/analytics"
|
||||
className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors"
|
||||
>
|
||||
Analytics
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<h2 className="text-xl font-semibold mb-4">Client Management</h2>
|
||||
<p className="text-gray-600">Manage fitness clients and their profiles</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<h2 className="text-xl font-semibold mb-4">Payment Tracking</h2>
|
||||
<p className="text-gray-600">Monitor payments and subscriptions</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<h2 className="text-xl font-semibold mb-4">Attendance</h2>
|
||||
<p className="text-gray-600">Track client attendance and habits</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<h2 className="text-2xl font-semibold mb-6">Recent User Activity</h2>
|
||||
<div className="h-96">
|
||||
<UserManagement />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<h2 className="text-2xl font-semibold mb-6">Quick Analytics</h2>
|
||||
<div className="h-96">
|
||||
<AnalyticsDashboard />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { UserManagement } from '@/components/users/UserManagement'
|
||||
|
||||
export default function UsersPage() {
|
||||
return (
|
||||
<main className="min-h-screen bg-gray-50">
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<UserManagement />
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user