import React, { useState, useCallback } from 'react'; import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; import { Head, usePage, router } from '@inertiajs/react'; import { PageProps, User } from '@/types'; import { Can } from '@/Components/Can'; import { DataTable } from '@/Components/DataTable'; import { Portal } from '@/Components/Portal'; import { swal } from '@/lib/swal'; import _ from 'lodash'; interface UsersPageProps extends PageProps { users: { data: User[]; meta: any; links: any[]; }; availableRoles: string[]; filters: any; } /* ─── User Create/Edit Modal (Professional & Compact) ─────────────── */ /* ─── User Create/Edit Modal (Professional & Compact) ─────────────── */ function UserModal({ user, availableRoles, onClose }: { user: Partial | null; availableRoles: string[]; onClose: () => void; }) { const existingRoles = user?.id ? ((user as any).roles || []).map((r: any) => r.name || r) : []; const [form, setForm] = useState({ first_name: user?.first_name || '', last_name: user?.last_name || '', email: user?.email || '', password: '', status: user?.status || 'active', roles: existingRoles as string[], }); const [errors, setErrors] = useState>({}); const [processing, setProcessing] = useState(false); const validate = () => { const e: Record = {}; if (!form.first_name.trim()) e.first_name = 'Required'; if (!form.last_name.trim()) e.last_name = 'Required'; if (!form.email.trim()) e.email = 'Required'; else if (!/\S+@\S+\.\S+/.test(form.email)) e.email = 'Invalid'; if (!user?.id && !form.password) e.password = 'Required'; setErrors(e); return Object.keys(e).length === 0; }; const toggleRole = (roleName: string) => { setForm(prev => ({ ...prev, roles: prev.roles.includes(roleName) ? prev.roles.filter(r => r !== roleName) : [...prev.roles, roleName], })); }; const handleSubmit = (e: React.SyntheticEvent) => { e.preventDefault(); if (!validate()) return; setProcessing(true); const payload = { ...form }; if (user?.id) { router.patch(`/users/${user.id}`, payload, { preserveScroll: true, onSuccess: () => { onClose(); swal.success('Updated', 'User updated successfully.'); }, onError: (errs) => { setErrors(errs as any); setProcessing(false); }, }); } else { router.post('/users', payload, { preserveScroll: true, onSuccess: () => { onClose(); swal.success('Created', 'New user created successfully.'); }, onError: (errs) => { setErrors(errs as any); setProcessing(false); }, }); } }; return (

{user?.id ? 'Edit user' : 'New user'}

Fill in the user details below.

setForm({ ...form, first_name: e.target.value })} className={`input-field${errors.first_name ? ' is-error' : ''}`} placeholder="John" required /> {errors.first_name &&

{errors.first_name}

}
setForm({ ...form, last_name: e.target.value })} className={`input-field${errors.last_name ? ' is-error' : ''}`} placeholder="Doe" required /> {errors.last_name &&

{errors.last_name}

}
setForm({ ...form, email: e.target.value })} className={`input-field${errors.email ? ' is-error' : ''}`} placeholder="john.doe@example.com" required /> {errors.email &&

{errors.email}

}
{!user?.id && (
setForm({ ...form, password: e.target.value })} className="input-field" placeholder="••••••••" required />
)}
{availableRoles.map(role => ( ))}
); } /* ─── Users Page ──────────────────────────────────────────────────── */ export default function UsersIndex({ users, availableRoles, filters }: UsersPageProps) { const { permissions } = usePage().props.auth; const [showModal, setShowModal] = useState(false); const [editUser, setEditUser] = useState(null); const [isLoading, setIsLoading] = useState(false); const [selectedIds, setSelectedIds] = useState<(number | string)[]>([]); const [localFilters, setLocalFilters] = useState({ search: filters.search || '', status: filters.status || '', role: filters.role || '', trashed: filters.trashed || '', per_page: filters.per_page || 15, sort_field: filters.sort_field || 'created_at', sort_direction: filters.sort_direction || 'desc' }); // Sync local filters with server props React.useEffect(() => { setLocalFilters({ search: filters.search || '', status: filters.status || '', role: filters.role || '', sort_field: filters.sort_field || 'created_at', sort_direction: filters.sort_direction || 'desc', per_page: filters.per_page || 15, trashed: filters.trashed || '', }); }, [filters]); const debouncedFilter = useCallback(_.debounce((params) => { setIsLoading(true); router.get('/users', params, { preserveState: true, preserveScroll: true, replace: true, only: ['users', 'filters'], onFinish: () => setIsLoading(false) }); }, 400), []); const updateFilter = (key: string, value: any) => { const newFilters = { ...localFilters, [key]: value }; setLocalFilters(newFilters); const params = { ...newFilters, page: 1 }; setSelectedIds([]); // Clear selection on filter change debouncedFilter(params); }; const handleBulkAction = (action: string) => { const actionMap: any = { archive: { url: route('users.bulk-archive'), text: 'Archive' }, restore: { url: route('users.bulk-restore'), text: 'Restore' }, delete: { url: route('users.bulk-force-delete'), text: 'Delete' } }; const config = actionMap[action]; swal.confirm(`${config.text} Selected?`, `Are you sure you want to ${action} ${selectedIds.length} users?`, config.text) .then(result => { if (result.isConfirmed) { router.post(config.url, { ids: selectedIds }, { preserveScroll: true, onSuccess: () => { setSelectedIds([]); swal.success('Success', `${selectedIds.length} users ${action}d successfully.`); } }); } }); }; const columns = [ { header: 'User', accessorKey: 'first_name', sortable: true, cell: (u: User) => (
{u.avatar_url ? : `${u.first_name?.charAt(0)}${u.last_name?.charAt(0)}`}
{u.first_name} {u.last_name}
{u.email}
) }, { header: 'Roles', accessorKey: 'roles', cell: (u: User) => (
{(u as any).roles?.length ? (u as any).roles.map((r: any) => ( {r.name || r} )) : Unassigned}
) }, { header: 'Status', accessorKey: 'status', sortable: true, cell: (u: User) => ( {u.deleted_at ? 'Archived' : u.status} ) }, { header: localFilters.trashed === 'only' ? 'Archived at' : 'Joined', accessorKey: localFilters.trashed === 'only' ? 'deleted_at' : 'created_at', sortable: true, cell: (u: User) => ( {new Date((u as any)[u.deleted_at ? 'deleted_at' : 'created_at']).toLocaleDateString('en-US', { day: '2-digit', month: 'short', year: 'numeric' })} ) } ]; const isTrashed = localFilters.trashed === 'only'; return ( {/* Row 1: title + toolbar */}

User Management

Maintain and configure the global user registry

updateFilter('search', e.target.value)} className="w-full h-11 pl-10 pr-4 rounded-2xl border border-gray-100 bg-white text-sm font-semibold text-gray-700 placeholder-gray-400 focus:outline-none focus:border-[#D4A017] focus:ring-4 focus:ring-[#D4A017]/5 transition-all shadow-sm" />
{!isTrashed && ( <> )} {!isTrashed && ( )} {!isTrashed && ( )}
{/* Row 2: tabs + archived notice */}
{isTrashed && ( Archived users cannot log in )}
{ updateFilter('sort_field', f); updateFilter('sort_direction', d); }} isLoading={isLoading} selectedIds={selectedIds} onSelectionChange={setSelectedIds} canEdit={!isTrashed && permissions.includes('user.edit')} emptyAction={!isTrashed && permissions.includes('user.create') ? ( ) : undefined} onEdit={(u) => { setEditUser(u); setShowModal(true); }} onDelete={!isTrashed ? (u) => { swal.confirm('Archive user?', `Move ${u.first_name} ${u.last_name} to the archived list?`, 'Archive') .then(result => { if (result.isConfirmed) router.delete(`/users/${u.id}`, { preserveScroll: true, onSuccess: () => swal.success('Archived', 'User archived successfully.') }); }); } : undefined} onRestore={isTrashed ? (u) => { swal.confirm('Restore user?', `Restore ${u.first_name} ${u.last_name} to active status?`, 'Restore') .then(result => { if (result.isConfirmed) router.post(`/users/${u.id}/restore`, {}, { preserveScroll: true, onSuccess: () => swal.success('Restored', 'User restored successfully.') }); }); } : undefined} onPermanentDelete={isTrashed ? (u) => { swal.confirmDelete(`${u.first_name} ${u.last_name}`) .then(result => { if (result.isConfirmed) router.delete(`/users/${u.id}/force-delete`, { preserveScroll: true, onSuccess: () => swal.success('Deleted', 'User permanently deleted.') }); }); } : undefined} />
{showModal && { setShowModal(false); setEditUser(null); }} />} {/* Floating Bulk Actions Bar */}
0 ? 'translate-y-0 opacity-100' : 'translate-y-20 opacity-0 pointer-events-none'}`}>
{selectedIds.length} Items selected
{isTrashed ? ( <> ) : ( )}
); }