diff --git a/src/features/users/components/InternalTeamTab.tsx b/src/features/users/components/InternalTeamTab.tsx new file mode 100644 index 0000000..9485ba9 --- /dev/null +++ b/src/features/users/components/InternalTeamTab.tsx @@ -0,0 +1,107 @@ + +import { useState } from "react"; +import { Plus, MoreHorizontal, ShieldAlert, Key } from "lucide-react"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { mockInternalUsers } from "@/features/users/data/mockRbacData"; +import { toast } from "sonner"; + +export function InternalTeamTab() { + const handleRevokeAccess = (name: string) => { + toast.success(`Access revoked for ${name}`); + }; + + return ( +
+
+
+

Internal Staff

+ {mockInternalUsers.length} Active +
+ +
+ +
+ + + + User + Role + Status + Last Active + Actions + + + + {mockInternalUsers.map((user) => ( + + +
+

{user.name}

+

{user.email}

+
+
+ + + {user.role.replace('_', ' ')} + + + + {user.status === 'Active' ? ( + + Active + + ) : ( + + Inactive + + )} + + {user.lastActive} + + + + + + + Manage Access + Edit Role + View Audit Log + + + Reset Password + + handleRevokeAccess(user.name)} className="text-error"> + Revoke Access + + + + +
+ ))} +
+
+
+
+ ); +} diff --git a/src/features/users/components/PartnerAdminTab.tsx b/src/features/users/components/PartnerAdminTab.tsx new file mode 100644 index 0000000..b3b4763 --- /dev/null +++ b/src/features/users/components/PartnerAdminTab.tsx @@ -0,0 +1,116 @@ + +import { Search, MoreHorizontal, ShieldCheck, BadgePercent, Lock } from "lucide-react"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Badge } from "@/components/ui/badge"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { mockPartnerUsers } from "@/features/users/data/mockRbacData"; +import { toast } from "sonner"; + +export function PartnerAdminTab() { + const handleToggleVerification = (name: string, currentStatus: boolean) => { + toast.success(`${name} verification status updated to ${!currentStatus}`); + }; + + return ( +
+
+
+

Partner Administration

+ B2B +
+ +
+ + +
+
+ +
+ + + + Partner User + Organization + Role + Verification + Commission + Actions + + + + {mockPartnerUsers.map((user) => ( + + +
+

{user.name}

+

{user.email}

+
+
+ + {user.partnerName} + + + {user.role} + + + {user.isVerified ? ( + + Verified + + ) : ( + + Unverified + + )} + + + {user.commissionOverride ? ( + + {user.commissionOverride}% (VIP) + + ) : ( + Standard + )} + + + + + + + + handleToggleVerification(user.partnerName, user.isVerified)}> + Toggle Verification + + + Set Custom Commission + + + Suspend Account + + + + +
+ ))} +
+
+
+
+ ); +} diff --git a/src/features/users/components/RoleMatrix.tsx b/src/features/users/components/RoleMatrix.tsx new file mode 100644 index 0000000..390319d --- /dev/null +++ b/src/features/users/components/RoleMatrix.tsx @@ -0,0 +1,97 @@ + +import { useState } from "react"; +import { Check, Shield } from "lucide-react"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Role, Permission, roles as initialRoles, permissions } from "@/features/users/data/mockRbacData"; +import { toast } from "sonner"; + +export function RoleMatrix() { + const [roles, setRoles] = useState(initialRoles); + + const togglePermission = (roleId: string, permissionId: string) => { + setRoles(currentRoles => + currentRoles.map(role => { + if (role.id !== roleId) return role; + + const hasPermission = role.permissions.includes(permissionId); + return { + ...role, + permissions: hasPermission + ? role.permissions.filter(p => p !== permissionId) + : [...role.permissions, permissionId] + }; + }) + ); + }; + + const handleSave = () => { + toast.success("Role permissions updated successfully"); + }; + + return ( +
+
+
+

+ + Role Definitions +

+

Configure detailed permissions for each internal role.

+
+ +
+ +
+ + + + Permission / Capability + {roles.map(role => ( + + + {role.name} + + + ))} + + + + {permissions.map(permission => ( + + +
+ {permission.name} + {permission.description} +
+
+ {roles.map(role => ( + +
+ togglePermission(role.id, permission.id)} + className="data-[state=checked]:bg-primary data-[state=checked]:border-primary" + /> +
+
+ ))} +
+ ))} +
+
+
+
+ ); +} diff --git a/src/features/users/components/UserBaseTab.tsx b/src/features/users/components/UserBaseTab.tsx new file mode 100644 index 0000000..5d15335 --- /dev/null +++ b/src/features/users/components/UserBaseTab.tsx @@ -0,0 +1,120 @@ + +import { Search, MoreHorizontal, UserX, KeyRound, Eye } from "lucide-react"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Badge } from "@/components/ui/badge"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { mockEndUsers } from "@/features/users/data/mockRbacData"; +import { toast } from "sonner"; +import { formatCurrency } from "@/data/mockData"; + +export function UserBaseTab() { + const handleBanUser = (name: string) => { + toast.success(`User ${name} has been banned.`); + }; + + const handleImpersonate = (name: string) => { + toast.info(`Impersonating ${name}... (Dev Mode Only)`); + // In a real app this would store a token and redirect + }; + + return ( +
+
+
+

User Base

+ B2C +
+ +
+
+ + +
+ +
+
+ +
+ + + + User Details + Contact + Bookings + Total Spent + Status + Actions + + + + {mockEndUsers.map((user) => ( + + +
+

{user.name}

+

ID: {user.id}

+
+
+ +
+

{user.email}

+

{user.phone}

+
+
+ + {user.bookingsCount} + + + {formatCurrency(user.totalSpent)} + + + {user.status === 'Active' ? ( + Active + ) : ( + Banned + )} + + + + + + + + handleImpersonate(user.name)}> + Impersonate + + + Reset 2FA + + + handleBanUser(user.name)} className="text-error"> + Ban User + + + + +
+ ))} +
+
+
+
+ ); +} diff --git a/src/features/users/data/mockRbacData.ts b/src/features/users/data/mockRbacData.ts new file mode 100644 index 0000000..92e8876 --- /dev/null +++ b/src/features/users/data/mockRbacData.ts @@ -0,0 +1,100 @@ + +export interface Permission { + id: string; + name: string; + description: string; +} + +export interface Role { + id: string; + name: string; + permissions: string[]; // List of permission IDs + color: string; +} + +export const permissions: Permission[] = [ + { id: 'view_dashboard', name: 'View Dashboard', description: 'Access to the main dashboard stats' }, + { id: 'manage_users', name: 'Manage Users', description: 'Create, edit, and delete user accounts' }, + { id: 'manage_partners', name: 'Manage Partners', description: 'Onboard and verify partners' }, + { id: 'manage_events', name: 'Manage Events', description: 'Approve, feature, and edit events' }, + { id: 'view_financials', name: 'View Financials', description: 'Access revenue and transaction data' }, + { id: 'manage_payouts', name: 'Manage Payouts', description: 'Process partner settlements' }, + { id: 'manage_settings', name: 'Manage Settings', description: 'Configure platform settings' }, + { id: 'manage_roles', name: 'Manage Roles', description: 'Edit role permissions' }, +]; + +export const roles: Role[] = [ + { + id: 'super_admin', + name: 'Super Admin', + permissions: permissions.map(p => p.id), + color: 'bg-purple-500/10 text-purple-600 border-purple-200' + }, + { + id: 'support_agent', + name: 'Support Agent', + permissions: ['view_dashboard', 'manage_users', 'manage_events'], + color: 'bg-blue-500/10 text-blue-600 border-blue-200' + }, + { + id: 'content_moderator', + name: 'Content Moderator', + permissions: ['view_dashboard', 'manage_events'], + color: 'bg-orange-500/10 text-orange-600 border-orange-200' + }, + { + id: 'finance_manager', + name: 'Finance Manager', + permissions: ['view_dashboard', 'view_financials', 'manage_payouts'], + color: 'bg-green-500/10 text-green-600 border-green-200' + }, +]; + +export interface InternalUser { + id: string; + name: string; + email: string; + role: string; + lastActive: string; + status: 'Active' | 'Inactive'; +} + +export const mockInternalUsers: InternalUser[] = [ + { id: '1', name: 'Alex Admin', email: 'alex@eventify.com', role: 'super_admin', lastActive: 'Just now', status: 'Active' }, + { id: '2', name: 'Sarah Support', email: 'sarah@eventify.com', role: 'support_agent', lastActive: '2h ago', status: 'Active' }, + { id: '3', name: 'Mike Mod', email: 'mike@eventify.com', role: 'content_moderator', lastActive: '1d ago', status: 'Inactive' }, +]; + +export interface PartnerUser { + id: string; + name: string; + email: string; + partnerName: string; // The organization they belong to + role: 'Partner Admin' | 'Staff'; + isVerified: boolean; + commissionOverride?: number; // Optional override + status: 'Active' | 'Suspended'; +} + +export const mockPartnerUsers: PartnerUser[] = [ + { id: 'p1', name: 'John Venue', email: 'john@neonarena.com', partnerName: 'Neon Arena', role: 'Partner Admin', isVerified: true, status: 'Active' }, + { id: 'p2', name: 'Jane Staff', email: 'jane@neonarena.com', partnerName: 'Neon Arena', role: 'Staff', isVerified: true, status: 'Active' }, + { id: 'p3', name: 'Bob Promoter', email: 'bob@toptier.com', partnerName: 'TopTier Promoters', role: 'Partner Admin', isVerified: false, commissionOverride: 5.0, status: 'Active' }, +]; + +export interface EndUser { + id: string; + name: string; + email: string; + phone: string; + bookingsCount: number; + totalSpent: number; + lastLogin: string; + status: 'Active' | 'Banned'; +} + +export const mockEndUsers: EndUser[] = [ + { id: 'u1', name: 'Alice Walker', email: 'alice@gmail.com', phone: '+91 9876543210', bookingsCount: 5, totalSpent: 12500, lastLogin: '1h ago', status: 'Active' }, + { id: 'u2', name: 'Charlie Brown', email: 'charlie@yahoo.com', phone: '+91 8765432109', bookingsCount: 1, totalSpent: 500, lastLogin: '3d ago', status: 'Active' }, + { id: 'u3', name: 'Dave Fraud', email: 'dave@suspicious.com', phone: '+91 7654321098', bookingsCount: 0, totalSpent: 0, lastLogin: 'Never', status: 'Banned' }, +]; diff --git a/src/pages/Users.tsx b/src/pages/Users.tsx index c9bf537..fbf8a97 100644 --- a/src/pages/Users.tsx +++ b/src/pages/Users.tsx @@ -1,143 +1,85 @@ -import { User, Shield, UserCheck, Search, Plus } from 'lucide-react'; -import { AppLayout } from '@/components/layout/AppLayout'; -import { cn } from '@/lib/utils'; -const mockUsers = [ - { id: '1', name: 'Arjun Sharma', email: 'arjun@eventify.in', role: 'admin', status: 'active', lastActive: new Date() }, - { id: '2', name: 'Priya Patel', email: 'priya@eventify.in', role: 'support', status: 'active', lastActive: new Date(Date.now() - 1000 * 60 * 30) }, - { id: '3', name: 'Rahul Kumar', email: 'rahul@eventify.in', role: 'viewer', status: 'inactive', lastActive: new Date(Date.now() - 1000 * 60 * 60 * 24 * 3) }, -]; +import { + Shield, + Users, + BriefcaseBusiness, + Settings2, + ListFilter +} from "lucide-react"; +import { AppLayout } from "@/components/layout/AppLayout"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { InternalTeamTab } from "@/features/users/components/InternalTeamTab"; +import { PartnerAdminTab } from "@/features/users/components/PartnerAdminTab"; +import { UserBaseTab } from "@/features/users/components/UserBaseTab"; +import { RoleMatrix } from "@/features/users/components/RoleMatrix"; +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, + SheetTrigger +} from "@/components/ui/sheet"; +import { Button } from "@/components/ui/button"; export default function Users() { return ( - - {/* Quick Stats */} -
-
-
-
- -
-
-

8

-

Total Users

-
-
-
-
-
-
- -
-
-

3

-

Admins

-
-
-
-
-
-
- -
-
-

6

-

Active Now

-
-
-
-
+ +
- {/* Users Table */} -
-
-

Platform Users

-
-
- - -
- -
-
+ +
-
- - - - - - - - - - - - {mockUsers.map((user) => ( - - - - - - - - ))} - -
UserRoleStatusLast ActiveActions
-
-
- - {user.name.split(' ').map(n => n[0]).join('')} - -
-
-

{user.name}

-

{user.email}

-
-
-
- - {user.role} - - - - - {user.status === 'active' ? 'Active' : 'Inactive'} - - - {user.lastActive.toLocaleDateString('en-IN', { - day: 'numeric', - month: 'short', - hour: '2-digit', - minute: '2-digit' - })} - - -
-
+ {/* Main Navigation Tabs */} + + + + Internal Team + + + + Partners + + + + User Base + + + + {/* Quick Actions (Role Manager) */} + + + + + + + Role Permissions Matrix + Verify and modify capabilities for each internal system role. + + + + +
+ +
+
+ + + + + + + + + + + +
+
+
);