From a1f3e2b3f581387d708ad3ebd4523264d760cbaf Mon Sep 17 00:00:00 2001 From: Leon Serfaty G Date: Thu, 17 Jul 2025 10:40:28 +0000 Subject: [PATCH] create a comprehensive admin dashbioard with permalink /admin/ and creat --- middleware.ts | 23 +++++++++++++++ src/app/admin/layout.tsx | 64 ++++++++++++++++++++++++++++++++++++++++ src/app/admin/page.tsx | 50 +++++++++++++++++++++++++++++++ src/app/login/page.tsx | 54 +++++++++++++++++++++++++++++++++ src/lib/auth.ts | 55 ++++++++++++++++++++++++++++++++++ 5 files changed, 246 insertions(+) create mode 100644 middleware.ts create mode 100644 src/app/admin/layout.tsx create mode 100644 src/app/admin/page.tsx create mode 100644 src/app/login/page.tsx create mode 100644 src/lib/auth.ts diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..5c9b80e --- /dev/null +++ b/middleware.ts @@ -0,0 +1,23 @@ + +import {NextResponse} from 'next/server'; +import type {NextRequest} from 'next/server'; + +export function middleware(request: NextRequest) { + const session = request.cookies.get('session'); + + // Redirect to login if trying to access /admin without a session + if (request.nextUrl.pathname.startsWith('/admin') && !session) { + return NextResponse.redirect(new URL('/login', request.url)); + } + + // Redirect to admin if trying to access /login with a session + if (request.nextUrl.pathname === '/login' && session) { + return NextResponse.redirect(new URL('/admin', request.url)); + } + + return NextResponse.next(); +} + +export const config = { + matcher: ['/admin/:path*', '/login'], +}; diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx new file mode 100644 index 0000000..097f5fc --- /dev/null +++ b/src/app/admin/layout.tsx @@ -0,0 +1,64 @@ + +import * as React from 'react'; +import { + Sidebar, + SidebarContent, + SidebarHeader, + SidebarMenu, + SidebarMenuItem, + SidebarMenuButton, + SidebarMenuSub, + SidebarMenuSubButton, + SidebarMenuSubItem, + SidebarProvider, + SidebarInset, + SidebarTrigger, + SidebarFooter, +} from '@/components/ui/sidebar'; +import { LayoutDashboard, LogOut } from 'lucide-react'; +import { signOut } from '@/lib/auth'; +import { Button } from '@/components/ui/button'; + +export default function AdminLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + +
+
+ Admin +
+ + + + + + + Dashboard + + + + + +
+ +
+
+ + +
+ +

Dashboard

+
+
{children}
+
+ + ); +} diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx new file mode 100644 index 0000000..d465028 --- /dev/null +++ b/src/app/admin/page.tsx @@ -0,0 +1,50 @@ + +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; +import { DollarSign, Users, Activity, Clock } from "lucide-react"; + +export default function AdminDashboard() { + return ( +
+ + + Total Revenue + + + +
$45,231.89
+

+20.1% from last month

+
+
+ + + New Users + + + +
+2350
+

+180.1% from last month

+
+
+ + + Active Projects + + + +
+573
+

+20 since last hour

+
+
+ + + Average Estimate + + + +
128 hours
+

Based on recent activity

+
+
+
+ ); +} diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx new file mode 100644 index 0000000..2981654 --- /dev/null +++ b/src/app/login/page.tsx @@ -0,0 +1,54 @@ + +import { LogIn } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { signIn, getSession } from '@/lib/auth'; +import { redirect } from 'next/navigation'; + +export default async function LoginPage() { + const session = await getSession(); + if (session) { + redirect('/admin'); + } + + return ( +
+ + + Admin Login + Enter your credentials to access the dashboard. + + +
+
+ + +
+
+ + +
+ +
+
+
+
+ ); +} diff --git a/src/lib/auth.ts b/src/lib/auth.ts new file mode 100644 index 0000000..6827407 --- /dev/null +++ b/src/lib/auth.ts @@ -0,0 +1,55 @@ + +'use server'; + +import { redirect } from 'next/navigation'; +import { cookies } from 'next/headers'; + +const FAKE_USER = { + email: 'admin@example.com', + password: 'password', + name: 'Admin User', +}; + +export async function signIn(formData: FormData) { + const email = formData.get('email'); + const password = formData.get('password'); + + if (email === FAKE_USER.email && password === FAKE_USER.password) { + const sessionData = { + isLoggedIn: true, + email: FAKE_USER.email, + name: FAKE_USER.name, + }; + + cookies().set('session', JSON.stringify(sessionData), { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + maxAge: 60 * 60 * 24 * 7, // One week + path: '/', + }); + + redirect('/admin'); + } + + // In a real app, you'd handle the error case, e.g., redirect to login with an error message. + // For simplicity, we'll just redirect back. + redirect('/login'); +} + +export async function signOut() { + cookies().delete('session'); + redirect('/login'); +} + +export async function getSession() { + const sessionCookie = cookies().get('session'); + if (!sessionCookie) { + return null; + } + try { + return JSON.parse(sessionCookie.value); + } catch (error) { + console.error('Failed to parse session cookie:', error); + return null; + } +}