From 5862bd97a16078de1d47ac7f2e7870729a049207 Mon Sep 17 00:00:00 2001 From: hosseintaromi Date: Tue, 22 Jul 2025 08:48:52 +0330 Subject: [PATCH] ui: update navigation and routing - Add routes for admin users, roles, and permissions management - Update sidebar with System Management section - Remove undeveloped menu items for cleaner demo - Add proper route protection and loading states - Export new permission management types --- src/App.tsx | 34 +++- src/components/layout/Sidebar.tsx | 318 ++++++++++++------------------ src/types/index.ts | 37 ++++ 3 files changed, 195 insertions(+), 194 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 1c94939..0f012e4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,6 +5,7 @@ import { AuthProvider } from './contexts/AuthContext'; import { ThemeProvider } from './contexts/ThemeContext'; import { ToastProvider } from './contexts/ToastContext'; import { ErrorBoundary } from './components/common/ErrorBoundary'; +import { LoadingSpinner } from './components/ui/LoadingSpinner'; import { queryClient } from './lib/queryClient'; import { useAuth } from './contexts/AuthContext'; import { Login } from './pages/Login'; @@ -22,8 +23,25 @@ import RoleFormPage from './pages/roles/role-form/RoleFormPage'; import RoleDetailPage from './pages/roles/role-detail/RoleDetailPage'; import RolePermissionsPage from './pages/roles/role-permissions/RolePermissionsPage'; +// Admin Users Pages +import AdminUsersListPage from './pages/admin-users/admin-users-list/AdminUsersListPage'; +import AdminUserFormPage from './pages/admin-users/admin-user-form/AdminUserFormPage'; + +// Permissions Pages +import PermissionsListPage from './pages/permissions/permissions-list/PermissionsListPage'; +import PermissionFormPage from './pages/permissions/permission-form/PermissionFormPage'; + const ProtectedRoute = ({ children }: { children: any }) => { - const { user } = useAuth(); + const { user, isLoading } = useAuth(); + + if (isLoading) { + return ( +
+ +
+ ); + } + return user ? children : ; }; @@ -49,12 +67,22 @@ const AppRoutes = () => { } /> } /> } /> + + {/* Admin Users Routes */} + } /> + } /> + } /> + + {/* Permissions Routes */} + } /> + } /> + } /> ); }; -function App() { +const App = () => { return ( @@ -71,6 +99,6 @@ function App() { ); -} +}; export default App; \ No newline at end of file diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 11a9cd8..a4e95bc 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -1,230 +1,166 @@ -import { useState } from 'react'; +import React from 'react'; import { NavLink } from 'react-router-dom'; import { - LayoutDashboard, - Users, - ShoppingBag, - ShoppingCart, - FileText, - Bell, - X, + Home, + Settings, + Shield, + UserCog, + Key, + LogOut, ChevronDown, - Shield + ChevronRight } from 'lucide-react'; import { useAuth } from '../../contexts/AuthContext'; import { PermissionWrapper } from '../common/PermissionWrapper'; -import { MenuItem } from '../../types'; + +interface MenuItem { + title: string; + icon: any; + path?: string; + permission?: number; + children?: MenuItem[]; +} const menuItems: MenuItem[] = [ { - id: 'dashboard', - label: 'داشبورد', - icon: LayoutDashboard, + title: 'داشبورد', + icon: Home, path: '/', }, { - id: 'users', - label: 'کاربران', - icon: Users, - path: '/users', - permission: 10, - }, - { - id: 'roles', - label: 'نقش‌ها', - icon: Shield, - path: '/roles', - permission: 5, - }, - { - id: 'products', - label: 'محصولات', - icon: ShoppingBag, - path: '/products', - permission: 15, - }, - { - id: 'orders', - label: 'سفارشات', - icon: ShoppingCart, - path: '/orders', - permission: 20, - }, - { - id: 'reports', - label: 'گزارش‌ها', - icon: FileText, - path: '/reports', - permission: 25, - }, - { - id: 'notifications', - label: 'اعلانات', - icon: Bell, - path: '/notifications', - permission: 30, - }, + title: 'مدیریت سیستم', + icon: Settings, + children: [ + { + title: 'نقش‌ها', + icon: Shield, + path: '/roles', + permission: 22, + }, + { + title: 'کاربران ادمین', + icon: UserCog, + path: '/admin-users', + permission: 22, + }, + { + title: 'دسترسی‌ها', + icon: Key, + path: '/permissions', + permission: 22, + }, + ] + } ]; -interface SidebarProps { - isOpen: boolean; - onClose: () => void; -} +export const Sidebar = () => { + const { user, logout, hasPermission } = useAuth(); + const [expandedItems, setExpandedItems] = React.useState(['مدیریت سیستم']); -export const Sidebar = ({ isOpen, onClose }: SidebarProps) => { - const { user, hasPermission } = useAuth(); - const [expandedItems, setExpandedItems] = useState([]); - - const toggleExpanded = (itemId: string) => { + const toggleExpanded = (title: string) => { setExpandedItems(prev => - prev.includes(itemId) - ? prev.filter(id => id !== itemId) - : [...prev, itemId] + prev.includes(title) + ? prev.filter(item => item !== title) + : [...prev, title] ); }; - const renderMenuItem = (item: MenuItem) => { + const renderMenuItem = (item: MenuItem, depth = 0) => { const hasChildren = item.children && item.children.length > 0; - const isExpanded = expandedItems.includes(item.id); + const isExpanded = expandedItems.includes(item.title); + const paddingLeft = depth * 16; - const menuContent = ( - <> -
toggleExpanded(item.id) : undefined} - > -
- - {item.label} -
- {hasChildren && ( - + if (hasChildren) { + return ( +
+ + {isExpanded && item.children && ( +
+ {item.children.map(child => renderMenuItem(child, depth + 1))} +
)}
- - {hasChildren && isExpanded && ( -
- {item.children?.map(child => ( -
- {child.permission ? ( - - - `block px-4 py-2 text-sm text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors ${isActive ? 'bg-primary-100 dark:bg-primary-900 text-primary-700 dark:text-primary-300' : '' - }` - } - onClick={onClose} - > - {child.label} - - - ) : ( - - `block px-4 py-2 text-sm text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors ${isActive ? 'bg-primary-100 dark:bg-primary-900 text-primary-700 dark:text-primary-300' : '' - }` - } - onClick={onClose} - > - {child.label} - - )} -
- ))} -
- )} - - ); - - if (!hasChildren) { - return ( - - `flex items-center px-4 py-3 rounded-lg transition-colors ${isActive - ? 'text-primary-700 dark:text-primary-300 bg-primary-100 dark:bg-primary-900' - : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700' - }` - } - onClick={onClose} - > - - {item.label} - ); } - return
{menuContent}
; + const menuContent = ( + + `w-full flex items-center px-4 py-3 text-sm font-medium rounded-lg transition-colors ${isActive + ? 'bg-primary-50 dark:bg-primary-900 text-primary-600 dark:text-primary-400' + : 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700' + }` + } + style={{ paddingLeft: `${paddingLeft + 16}px` }} + > + + {item.title} + + ); + + if (item.permission) { + return ( + + {menuContent} + + ); + } + + return
{menuContent}
; }; return ( - <> - {isOpen && ( -
- )} +
+ {/* Logo */} +
+

+ پنل مدیریت +

+
-
-
-
-
- -
- - پنل مدیریت + {/* Navigation */} + + + {/* User Info */} +
+
+
+ + {user?.first_name?.[0]}{user?.last_name?.[0]}
+
+

+ {user?.first_name} {user?.last_name} +

+

+ {user?.username} +

+
- -
-
-
- - {user?.first_name?.charAt(0) || 'A'} - -
-
-

- {user?.first_name} {user?.last_name} -

-

- {user?.username} -

-
-
- - -
- +
); }; \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts index a1f1be1..ad3e418 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,42 @@ +import { Permission } from "./auth"; + export * from "./auth"; +// Permission Management Types +export interface CreatePermissionRequest { + title: string; + description: string; +} + +export interface UpdatePermissionRequest { + id: number; + title: string; + description: string; +} + +export interface CreatePermissionResponse { + permission: Permission; +} + +export interface UpdatePermissionResponse { + permission: Permission; +} + +export interface GetPermissionByIDResponse { + permission: Permission; +} + +export interface GetAllPermissionsResponse { + permissions: Permission[]; +} + +export interface DeletePermissionResponse { + message: string; +} + +// Re-export Permission from auth for convenience +export type { Permission } from "./auth"; + export interface User { id: string; name: string;