fix layout
This commit is contained in:
@@ -1,8 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import dynamic from 'next/dynamic'
|
|
||||||
import ProtectedRoute from '@/components/ProtectedRoute'
|
import ProtectedRoute from '@/components/ProtectedRoute'
|
||||||
import { AuthProvider, useAuth } from '@/contexts/AuthContext'
|
import { useAuth } from '@/contexts/AuthContext'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { usePathname } from 'next/navigation'
|
import { usePathname } from 'next/navigation'
|
||||||
import {
|
import {
|
||||||
@@ -17,8 +16,7 @@ import {
|
|||||||
Clock,
|
Clock,
|
||||||
Building2,
|
Building2,
|
||||||
LogOut,
|
LogOut,
|
||||||
LayoutDashboard,
|
LayoutDashboard
|
||||||
Users2
|
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
|
|
||||||
function AdminLayoutContent({ children }: { children: React.ReactNode }) {
|
function AdminLayoutContent({ children }: { children: React.ReactNode }) {
|
||||||
@@ -29,7 +27,6 @@ function AdminLayoutContent({ children }: { children: React.ReactNode }) {
|
|||||||
{ icon: LayoutDashboard, label: 'لوحة التحكم', href: '/admin', exact: true },
|
{ icon: LayoutDashboard, label: 'لوحة التحكم', href: '/admin', exact: true },
|
||||||
{ icon: Users, label: 'إدارة المستخدمين', href: '/admin/users' },
|
{ icon: Users, label: 'إدارة المستخدمين', href: '/admin/users' },
|
||||||
{ icon: Shield, label: 'الأدوار والصلاحيات', href: '/admin/roles' },
|
{ icon: Shield, label: 'الأدوار والصلاحيات', href: '/admin/roles' },
|
||||||
{ icon: Users2, label: 'مجموعات الصلاحيات', href: '/admin/permission-groups' },
|
|
||||||
{ icon: Database, label: 'النسخ الاحتياطي', href: '/admin/backup' },
|
{ icon: Database, label: 'النسخ الاحتياطي', href: '/admin/backup' },
|
||||||
{ icon: Settings, label: 'إعدادات النظام', href: '/admin/settings' },
|
{ icon: Settings, label: 'إعدادات النظام', href: '/admin/settings' },
|
||||||
{ icon: FileText, label: 'سجل العمليات', href: '/admin/audit-logs' },
|
{ icon: FileText, label: 'سجل العمليات', href: '/admin/audit-logs' },
|
||||||
@@ -40,7 +37,9 @@ function AdminLayoutContent({ children }: { children: React.ReactNode }) {
|
|||||||
]
|
]
|
||||||
|
|
||||||
const isActive = (href: string, exact?: boolean) => {
|
const isActive = (href: string, exact?: boolean) => {
|
||||||
if (exact) return pathname === href
|
if (exact) {
|
||||||
|
return pathname === href
|
||||||
|
}
|
||||||
return pathname.startsWith(href)
|
return pathname.startsWith(href)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +57,6 @@ function AdminLayoutContent({ children }: { children: React.ReactNode }) {
|
|||||||
<p className="text-xs text-gray-600">System Admin</p>
|
<p className="text-xs text-gray-600">System Admin</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-red-50 border border-red-200 rounded-lg p-3">
|
<div className="bg-red-50 border border-red-200 rounded-lg p-3">
|
||||||
<p className="text-xs font-semibold text-red-900">{user?.username}</p>
|
<p className="text-xs font-semibold text-red-900">{user?.username}</p>
|
||||||
<p className="text-xs text-red-700">{user?.role?.name}</p>
|
<p className="text-xs text-red-700">{user?.role?.name}</p>
|
||||||
@@ -74,7 +72,9 @@ function AdminLayoutContent({ children }: { children: React.ReactNode }) {
|
|||||||
key={item.href}
|
key={item.href}
|
||||||
href={item.href}
|
href={item.href}
|
||||||
className={`flex items-center gap-3 px-4 py-3 rounded-lg mb-2 transition-all ${
|
className={`flex items-center gap-3 px-4 py-3 rounded-lg mb-2 transition-all ${
|
||||||
active ? 'bg-red-600 text-white shadow-md' : 'text-gray-700 hover:bg-gray-100'
|
active
|
||||||
|
? 'bg-red-600 text-white shadow-md'
|
||||||
|
: 'text-gray-700 hover:bg-gray-100'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Icon className="h-5 w-5" />
|
<Icon className="h-5 w-5" />
|
||||||
@@ -104,30 +104,18 @@ function AdminLayoutContent({ children }: { children: React.ReactNode }) {
|
|||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<main className="mr-64 flex-1 p-8">{children}</main>
|
<main className="mr-64 flex-1 p-8">
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const AdminLayoutClient = dynamic(
|
|
||||||
() =>
|
|
||||||
Promise.resolve(function AdminLayoutClient({
|
|
||||||
children
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<AuthProvider>
|
|
||||||
<ProtectedRoute>
|
|
||||||
<AdminLayoutContent>{children}</AdminLayoutContent>
|
|
||||||
</ProtectedRoute>
|
|
||||||
</AuthProvider>
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
{ ssr: false }
|
|
||||||
)
|
|
||||||
|
|
||||||
export default function AdminLayout({ children }: { children: React.ReactNode }) {
|
export default function AdminLayout({ children }: { children: React.ReactNode }) {
|
||||||
return <AdminLayoutClient>{children}</AdminLayoutClient>
|
return (
|
||||||
}
|
<ProtectedRoute>
|
||||||
|
<AdminLayoutContent>{children}</AdminLayoutContent>
|
||||||
|
</ProtectedRoute>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,120 +1,69 @@
|
|||||||
'use client'
|
import type { Metadata } from 'next'
|
||||||
|
import { Cairo, Readex_Pro } from 'next/font/google'
|
||||||
|
import './globals.css'
|
||||||
|
import { Providers } from './providers'
|
||||||
|
import { AuthProvider } from '@/contexts/AuthContext'
|
||||||
|
import { LanguageProvider } from '@/contexts/LanguageContext'
|
||||||
|
import { Toaster } from 'react-hot-toast'
|
||||||
|
|
||||||
import ProtectedRoute from '@/components/ProtectedRoute'
|
const cairo = Cairo({
|
||||||
import { useAuth } from '@/contexts/AuthContext'
|
subsets: ['latin', 'arabic'],
|
||||||
import Link from 'next/link'
|
variable: '--font-cairo',
|
||||||
import { usePathname } from 'next/navigation'
|
display: 'swap',
|
||||||
import {
|
})
|
||||||
Users,
|
|
||||||
Shield,
|
|
||||||
Database,
|
|
||||||
Settings,
|
|
||||||
FileText,
|
|
||||||
Activity,
|
|
||||||
Mail,
|
|
||||||
Key,
|
|
||||||
Clock,
|
|
||||||
Building2,
|
|
||||||
LogOut,
|
|
||||||
LayoutDashboard,
|
|
||||||
Users2
|
|
||||||
} from 'lucide-react'
|
|
||||||
|
|
||||||
function AdminLayoutContent({ children }: { children: React.ReactNode }) {
|
const readexPro = Readex_Pro({
|
||||||
const { user, logout } = useAuth()
|
subsets: ['latin', 'arabic'],
|
||||||
const pathname = usePathname()
|
variable: '--font-readex',
|
||||||
|
display: 'swap',
|
||||||
|
})
|
||||||
|
|
||||||
const menuItems = [
|
export const metadata: Metadata = {
|
||||||
{ icon: LayoutDashboard, label: 'لوحة التحكم', href: '/admin', exact: true },
|
title: 'Z.CRM - نظام إدارة علاقات العملاء',
|
||||||
{ icon: Users, label: 'إدارة المستخدمين', href: '/admin/users' },
|
description: 'Enterprise CRM System for Contact Management, Sales, HR, Inventory, Projects, and Marketing',
|
||||||
{ icon: Shield, label: 'الأدوار والصلاحيات', href: '/admin/roles' },
|
}
|
||||||
{ icon: Users2, label: 'مجموعات الصلاحيات', href: '/admin/permission-groups' },
|
|
||||||
{ icon: Database, label: 'النسخ الاحتياطي', href: '/admin/backup' },
|
|
||||||
{ icon: Settings, label: 'إعدادات النظام', href: '/admin/settings' },
|
|
||||||
{ icon: FileText, label: 'سجل العمليات', href: '/admin/audit-logs' },
|
|
||||||
{ icon: Activity, label: 'صحة النظام', href: '/admin/health' },
|
|
||||||
{ icon: Mail, label: 'إعدادات البريد', href: '/admin/email' },
|
|
||||||
{ icon: Key, label: 'مفاتيح API', href: '/admin/api-keys' },
|
|
||||||
{ icon: Clock, label: 'المهام المجدولة', href: '/admin/scheduled-jobs' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const isActive = (href: string, exact?: boolean) => {
|
|
||||||
if (exact) {
|
|
||||||
return pathname === href
|
|
||||||
}
|
|
||||||
return pathname.startsWith(href)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50 flex">
|
<html lang="en">
|
||||||
<aside className="w-64 bg-white border-l shadow-lg fixed h-full overflow-y-auto">
|
<body className={`${readexPro.variable} ${cairo.variable} font-readex`}>
|
||||||
<div className="p-6 border-b">
|
<LanguageProvider>
|
||||||
<div className="flex items-center gap-3 mb-4">
|
<AuthProvider>
|
||||||
<div className="bg-red-600 p-2 rounded-lg">
|
<Providers>{children}</Providers>
|
||||||
<Shield className="h-6 w-6 text-white" />
|
<Toaster
|
||||||
</div>
|
position="top-center"
|
||||||
<div>
|
reverseOrder={false}
|
||||||
<h2 className="text-lg font-bold text-gray-900">لوحة الإدارة</h2>
|
toastOptions={{
|
||||||
<p className="text-xs text-gray-600">System Admin</p>
|
duration: 4000,
|
||||||
</div>
|
style: {
|
||||||
</div>
|
background: '#fff',
|
||||||
<div className="bg-red-50 border border-red-200 rounded-lg p-3">
|
color: '#363636',
|
||||||
<p className="text-xs font-semibold text-red-900">{user?.username}</p>
|
fontFamily: 'var(--font-readex)',
|
||||||
<p className="text-xs text-red-700">{user?.role?.name}</p>
|
},
|
||||||
</div>
|
success: {
|
||||||
</div>
|
duration: 3000,
|
||||||
|
iconTheme: {
|
||||||
<nav className="p-4">
|
primary: '#10B981',
|
||||||
{menuItems.map((item) => {
|
secondary: '#fff',
|
||||||
const Icon = item.icon
|
},
|
||||||
const active = isActive(item.href, item.exact)
|
},
|
||||||
return (
|
error: {
|
||||||
<Link
|
duration: 5000,
|
||||||
key={item.href}
|
iconTheme: {
|
||||||
href={item.href}
|
primary: '#EF4444',
|
||||||
className={`flex items-center gap-3 px-4 py-3 rounded-lg mb-2 transition-all ${
|
secondary: '#fff',
|
||||||
active
|
},
|
||||||
? 'bg-red-600 text-white shadow-md'
|
},
|
||||||
: 'text-gray-700 hover:bg-gray-100'
|
}}
|
||||||
}`}
|
/>
|
||||||
>
|
</AuthProvider>
|
||||||
<Icon className="h-5 w-5" />
|
</LanguageProvider>
|
||||||
<span className="font-medium">{item.label}</span>
|
</body>
|
||||||
</Link>
|
</html>
|
||||||
)
|
|
||||||
})}
|
|
||||||
|
|
||||||
<hr className="my-4 border-gray-200" />
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href="/dashboard"
|
|
||||||
className="flex items-center gap-3 px-4 py-3 rounded-lg mb-2 text-gray-700 hover:bg-gray-100 transition-all"
|
|
||||||
>
|
|
||||||
<Building2 className="h-5 w-5" />
|
|
||||||
<span className="font-medium">العودة للنظام</span>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={logout}
|
|
||||||
className="w-full flex items-center gap-3 px-4 py-3 rounded-lg text-red-600 hover:bg-red-50 transition-all"
|
|
||||||
>
|
|
||||||
<LogOut className="h-5 w-5" />
|
|
||||||
<span className="font-medium">تسجيل الخروج</span>
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<main className="mr-64 flex-1 p-8">
|
|
||||||
{children}
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AdminLayout({ children }: { children: React.ReactNode }) {
|
|
||||||
return (
|
|
||||||
<ProtectedRoute>
|
|
||||||
<AdminLayoutContent>{children}</AdminLayoutContent>
|
|
||||||
</ProtectedRoute>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user