diff --git a/src/app/admin/user-verify/page.tsx b/src/app/admin/user-verify/page.tsx new file mode 100644 index 0000000..2578c1b --- /dev/null +++ b/src/app/admin/user-verify/page.tsx @@ -0,0 +1,326 @@ +'use client' + +import { useMemo, useState, useCallback } from 'react' +import PageLayout from '../../components/PageLayout' +import { + MagnifyingGlassIcon, + CheckIcon +} from '@heroicons/react/24/outline' + +type UserType = 'personal' | 'company' +type UserRole = 'user' | 'admin' +interface PendingUser { + id: string + firstName: string + lastName: string + email: string + type: UserType + role: UserRole + created: string + lastLogin: string + status: 'pending' | 'verifying' | 'active' +} + +const TYPES: UserType[] = ['personal', 'company'] +const ROLES: UserRole[] = ['user', 'admin'] + +const rand = (arr: T[]) => arr[Math.floor(Math.random() * arr.length)] +const daysAgo = (d: number) => { + const dt = new Date() + dt.setDate(dt.getDate() - d) + return dt.toISOString().slice(0, 10) +} + +export default function AdminUserVerifyPage() { + // Mock pending users (only a small subset) + const seedUsers = useMemo(() => { + const first = ['Avgust', 'Katarina', 'Ana', 'Luka', 'Sara', 'Jonas', 'Niko', 'Eva'] + const last = ['Senica', 'Fedzer', 'Hochkraut', 'Novak', 'Schmidt', 'Keller', 'Mayr', 'Hansen'] + const list: PendingUser[] = [] + for (let i = 0; i < 18; i++) { + const fn = rand(first) + const ln = rand(last) + list.push({ + id: `P${i + 1}`, + firstName: fn, + lastName: ln, + email: `${fn}.${ln}${i}@example.com`.toLowerCase(), + type: Math.random() > 0.9 ? 'company' : 'personal', + role: Math.random() > 0.95 ? 'admin' : 'user', + created: daysAgo(Math.floor(Math.random() * 12)), + lastLogin: daysAgo(Math.floor(Math.random() * 5)), + status: 'pending' + }) + } + return list + }, []) + + const [users, setUsers] = useState(seedUsers) + const [search, setSearch] = useState('') + const [fType, setFType] = useState<'all' | UserType>('all') + const [fRole, setFRole] = useState<'all' | UserRole>('all') + const [perPage, setPerPage] = useState(10) + const [page, setPage] = useState(1) + + const filtered = useMemo(() => { + return users.filter(u => + u.status === 'pending' && + (fType === 'all' || u.type === fType) && + (fRole === 'all' || u.role === fRole) && + ( + !search.trim() || + u.email.toLowerCase().includes(search.toLowerCase()) || + `${u.firstName} ${u.lastName}`.toLowerCase().includes(search.toLowerCase()) + ) + ) + }, [users, search, fType, fRole]) + + const totalPages = Math.max(1, Math.ceil(filtered.length / perPage)) + const current = filtered.slice((page - 1) * perPage, page * perPage) + + const applyFilters = (e: React.FormEvent) => { + e.preventDefault() + setPage(1) + } + + const badge = (text: string, color: string) => + + {text} + + + const typeBadge = (t: UserType) => + t === 'personal' + ? badge('Personal', 'bg-blue-100 text-blue-700') + : badge('Company', 'bg-purple-100 text-purple-700') + + const roleBadge = (r: UserRole) => + r === 'admin' + ? badge('Admin', 'bg-indigo-100 text-indigo-700') + : badge('User', 'bg-gray-100 text-gray-700') + + const statusBadge = (s: PendingUser['status']) => { + if (s === 'pending') return badge('Pending', 'bg-amber-100 text-amber-700') + if (s === 'verifying') return badge('Verifying', 'bg-blue-100 text-blue-700') + return badge('Active', 'bg-green-100 text-green-700') + } + + const verifyUser = useCallback((id: string) => { + setUsers(prev => prev.map(u => u.id === id ? { ...u, status: 'verifying' } : u)) + // simulate async approval + setTimeout(() => { + setUsers(prev => prev.map(u => u.id === id ? { ...u, status: 'active' } : u)) + }, 900) + }, []) + + return ( + +
+ {/* Background */} +
+
+ + + + {/* Outer container */} +
+
+

+ Users Pending Verification +

+

+ Review and verify users who have completed all registration steps. +

+
+ + {/* Filter Card */} +
+

+ Search & Filter Pending Users +

+
+
+ +
+ + setSearch(e.target.value)} + placeholder="Email, name, company..." + className="w-full rounded-md border border-gray-300 pl-10 pr-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent" + /> +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + {/* Pending Users Table */} +
+
+
+ Users Pending Verification +
+
+ Showing {current.length} of {filtered.length} users +
+
+
+ + + + + + + + + + + + + + {current.map(u => { + const initials = `${u.firstName[0]}${u.lastName[0]}`.toUpperCase() + return ( + + + + + + + + + + ) + })} + {current.length === 0 && ( + + + + )} + +
UserTypeStatusRoleCreatedLast LoginActions
+
+
+ {initials} +
+
+
+ {u.firstName} {u.lastName} +
+
{u.email}
+
+
+
{typeBadge(u.type)}{statusBadge(u.status)}{roleBadge(u.role)}{u.created}{u.lastLogin} + {u.status === 'active' ? ( + Verified + ) : ( + + )} +
+ No pending users match current filters. +
+
+ {/* Pagination */} +
+
+ Page {page} of {totalPages} ({filtered.length} pending users) +
+
+ + +
+
+
+
+
+ + ) +}