['except' => ''], 'roleFilter' => ['except' => ''], 'statusFilter' => ['except' => ''], 'departmentFilter' => ['except' => ''], 'branchFilter' => ['except' => ''], 'customerFilter' => ['except' => ''], 'hireYearFilter' => ['except' => ''], 'sortField' => ['except' => 'name'], 'sortDirection' => ['except' => 'asc'], 'perPage' => ['except' => 25], 'showInactive' => ['except' => false], 'page' => ['except' => 1], ]; protected $listeners = [ 'userUpdated' => '$refresh', 'userCreated' => '$refresh', 'userDeleted' => '$refresh', ]; public function updatingSearch() { $this->resetPage(); } public function updatingRoleFilter() { $this->resetPage(); } public function updatingStatusFilter() { $this->resetPage(); } public function updatingDepartmentFilter() { $this->resetPage(); } public function updatingBranchFilter() { $this->resetPage(); } public function updatingCustomerFilter() { $this->resetPage(); } public function updatingHireYearFilter() { $this->resetPage(); } public function updatingPerPage() { $this->resetPage(); } public function render() { $query = User::query() ->with([ 'roles' => function($query) { $query->where('user_roles.is_active', true) ->where(function ($q) { $q->whereNull('user_roles.expires_at') ->orWhere('user_roles.expires_at', '>', now()); }); }, 'customer', 'branch', 'jobCards' => function($query) { $query->select('id', 'service_advisor_id', 'created_at'); } ]) ->withCount([ 'roles as active_roles_count' => function($query) { $query->where('user_roles.is_active', true) ->where(function ($q) { $q->whereNull('user_roles.expires_at') ->orWhere('user_roles.expires_at', '>', now()); }); }, 'jobCards as job_cards_count' ]) ->when($this->search, function ($q) { $searchTerm = '%' . $this->search . '%'; $q->where(function ($query) use ($searchTerm) { $query->where('name', 'like', $searchTerm) ->orWhere('email', 'like', $searchTerm) ->orWhere('employee_id', 'like', $searchTerm) ->orWhere('phone', 'like', $searchTerm) ->orWhere('national_id', 'like', $searchTerm) ->orWhereHas('branch', function($q) use ($searchTerm) { $q->where('name', 'like', $searchTerm); }); }); }) ->when($this->roleFilter, function ($q) { $q->whereHas('roles', function ($query) { $query->where('roles.name', $this->roleFilter) ->where('user_roles.is_active', true) ->where(function ($subQ) { $subQ->whereNull('user_roles.expires_at') ->orWhere('user_roles.expires_at', '>', now()); }); }); }) ->when($this->statusFilter, function ($q) { $q->where('status', $this->statusFilter); }) ->when($this->departmentFilter, function ($q) { $q->where('department', $this->departmentFilter); }) ->when($this->branchFilter, function ($q) { $q->where('branch_code', $this->branchFilter); }) ->when($this->hireYearFilter, function ($q) { $q->whereYear('hire_date', $this->hireYearFilter); }) ->when($this->customerFilter, function ($q) { if ($this->customerFilter === 'customers_only') { $q->whereHas('customer'); } elseif ($this->customerFilter === 'non_customers') { $q->whereDoesntHave('customer'); } }) ->when(!$this->showInactive, function ($q) { $q->where('status', '!=', 'inactive'); }) ->orderBy($this->sortField, $this->sortDirection); $users = $query->paginate($this->perPage); // Filter options data $roles = Role::where('is_active', true)->orderBy('display_name')->get(); $departments = User::select('department') ->distinct() ->whereNotNull('department') ->where('department', '!=', '') ->orderBy('department') ->pluck('department'); $branches = Branch::where('is_active', true) ->orderBy('name') ->get(); $hireYears = User::selectRaw('YEAR(hire_date) as year') ->whereNotNull('hire_date') ->distinct() ->orderByDesc('year') ->pluck('year') ->filter(); // Enhanced statistics $stats = [ 'total' => User::count(), 'active' => User::where('status', 'active')->count(), 'inactive' => User::where('status', 'inactive')->count(), 'suspended' => User::where('status', 'suspended')->count(), 'customers' => User::whereHas('customer')->count(), 'staff' => User::whereDoesntHave('customer')->count(), 'recent_hires' => User::where('hire_date', '>=', now()->subDays(30))->count(), 'no_roles' => User::whereDoesntHave('roles', function($q) { $q->where('user_roles.is_active', true); })->count(), ]; // Branch distribution for stats $branchStats = User::select('branch_code') ->selectRaw('count(*) as count') ->with('branch:code,name') ->groupBy('branch_code') ->get() ->mapWithKeys(function($item) { $branchName = $item->branch ? $item->branch->name : $item->branch_code; return [$branchName => $item->count]; }); return view('livewire.users.index', compact( 'users', 'roles', 'departments', 'branches', 'hireYears', 'stats', 'branchStats' )); } public function sortBy($field) { if ($this->sortField === $field) { $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc'; } else { $this->sortField = $field; $this->sortDirection = 'asc'; } $this->resetPage(); } public function clearFilters() { $this->search = ''; $this->roleFilter = ''; $this->statusFilter = ''; $this->departmentFilter = ''; $this->branchFilter = ''; $this->customerFilter = ''; $this->hireYearFilter = ''; $this->showInactive = false; $this->resetPage(); } public function toggleShowInactive() { $this->showInactive = !$this->showInactive; $this->resetPage(); } public function toggleShowDetails() { $this->showDetails = !$this->showDetails; } public function selectAllUsers() { if ($this->selectAll) { $this->selectedUsers = []; $this->selectAll = false; } else { // Only select users from current page for performance $currentPageUsers = User::when($this->search, function ($q) { $searchTerm = '%' . $this->search . '%'; $q->where(function ($query) use ($searchTerm) { $query->where('name', 'like', $searchTerm) ->orWhere('email', 'like', $searchTerm); }); })->pluck('id')->toArray(); $this->selectedUsers = $currentPageUsers; $this->selectAll = true; } } public function confirmDelete($userId) { $this->userToDelete = $userId; $this->showDeleteModal = true; } public function confirmBulkDelete() { if (empty($this->selectedUsers)) { session()->flash('error', 'No users selected.'); return; } $this->showBulkDeleteModal = true; } public function bulkActivate() { if (empty($this->selectedUsers)) { session()->flash('error', 'No users selected.'); return; } try { $count = User::whereIn('id', $this->selectedUsers) ->where('id', '!=', auth()->id()) ->update(['status' => 'active']); // Log bulk action activity() ->causedBy(auth()->user()) ->log('Bulk activated ' . $count . ' users'); $this->selectedUsers = []; $this->selectAll = false; session()->flash('success', "Successfully activated {$count} users."); } catch (\Exception $e) { session()->flash('error', 'Failed to activate users: ' . $e->getMessage()); } } public function bulkDeactivate() { if (empty($this->selectedUsers)) { session()->flash('error', 'No users selected.'); return; } try { $count = User::whereIn('id', $this->selectedUsers) ->where('id', '!=', auth()->id()) ->update(['status' => 'inactive']); // Log bulk action activity() ->causedBy(auth()->user()) ->log('Bulk deactivated ' . $count . ' users'); $this->selectedUsers = []; $this->selectAll = false; session()->flash('success', "Successfully deactivated {$count} users."); } catch (\Exception $e) { session()->flash('error', 'Failed to deactivate users: ' . $e->getMessage()); } } public function bulkSuspend() { if (empty($this->selectedUsers)) { session()->flash('error', 'No users selected.'); return; } try { $count = User::whereIn('id', $this->selectedUsers) ->where('id', '!=', auth()->id()) ->update(['status' => 'suspended']); // Log bulk action activity() ->causedBy(auth()->user()) ->log('Bulk suspended ' . $count . ' users'); $this->selectedUsers = []; $this->selectAll = false; session()->flash('success', "Successfully suspended {$count} users."); } catch (\Exception $e) { session()->flash('error', 'Failed to suspend users: ' . $e->getMessage()); } } public function bulkAssignRole($roleId) { if (empty($this->selectedUsers)) { session()->flash('error', 'No users selected.'); return; } try { $role = Role::findOrFail($roleId); $count = 0; foreach ($this->selectedUsers as $userId) { $user = User::find($userId); if ($user && !$user->hasRole($role->name)) { $user->assignRole($role); $count++; } } // Log bulk action activity() ->causedBy(auth()->user()) ->log('Bulk assigned role "' . $role->display_name . '" to ' . $count . ' users'); $this->selectedUsers = []; $this->selectAll = false; session()->flash('success', "Successfully assigned role '{$role->display_name}' to {$count} users."); } catch (\Exception $e) { session()->flash('error', 'Failed to assign role: ' . $e->getMessage()); } } public function deactivateUser($userId) { try { $user = User::findOrFail($userId); if ($user->id === auth()->id()) { session()->flash('error', 'You cannot deactivate your own account.'); return; } $user->update(['status' => 'inactive']); // Log the action activity() ->performedOn($user) ->causedBy(auth()->user()) ->log('User deactivated'); session()->flash('success', "User '{$user->name}' deactivated successfully."); } catch (\Exception $e) { session()->flash('error', 'Failed to deactivate user: ' . $e->getMessage()); } } public function activateUser($userId) { try { $user = User::findOrFail($userId); $user->update(['status' => 'active']); // Log the action activity() ->performedOn($user) ->causedBy(auth()->user()) ->log('User activated'); session()->flash('success', "User '{$user->name}' activated successfully."); } catch (\Exception $e) { session()->flash('error', 'Failed to activate user: ' . $e->getMessage()); } } public function suspendUser($userId) { try { $user = User::findOrFail($userId); if ($user->id === auth()->id()) { session()->flash('error', 'You cannot suspend your own account.'); return; } $user->update(['status' => 'suspended']); // Log the action activity() ->performedOn($user) ->causedBy(auth()->user()) ->log('User suspended'); session()->flash('success', "User '{$user->name}' suspended successfully."); } catch (\Exception $e) { session()->flash('error', 'Failed to suspend user: ' . $e->getMessage()); } } public function deleteUser($userId = null) { $userId = $userId ?? $this->userToDelete; try { $user = User::findOrFail($userId); if ($user->id === auth()->id()) { session()->flash('error', 'You cannot delete your own account.'); return; } // Check if user has dependencies $jobCardsCount = $user->jobCards()->count(); if ($jobCardsCount > 0) { session()->flash('error', "Cannot delete user '{$user->name}'. User has {$jobCardsCount} associated job cards."); return; } // Log before deletion activity() ->performedOn($user) ->causedBy(auth()->user()) ->log('User deleted'); $userName = $user->name; $user->delete(); session()->flash('success', "User '{$userName}' deleted successfully."); } catch (\Exception $e) { session()->flash('error', 'Failed to delete user: ' . $e->getMessage()); } finally { $this->showDeleteModal = false; $this->userToDelete = null; } } public function bulkDelete() { if (empty($this->selectedUsers)) { session()->flash('error', 'No users selected.'); return; } try { $users = User::whereIn('id', $this->selectedUsers) ->where('id', '!=', auth()->id()) ->get(); $deletedCount = 0; $skippedCount = 0; foreach ($users as $user) { // Check dependencies if ($user->jobCards()->count() > 0) { $skippedCount++; continue; } // Log before deletion activity() ->performedOn($user) ->causedBy(auth()->user()) ->log('User deleted (bulk)'); $user->delete(); $deletedCount++; } $this->selectedUsers = []; $this->selectAll = false; $this->showBulkDeleteModal = false; $message = "Deleted {$deletedCount} users successfully."; if ($skippedCount > 0) { $message .= " {$skippedCount} users were skipped due to dependencies."; } session()->flash('success', $message); } catch (\Exception $e) { session()->flash('error', 'Failed to delete users: ' . $e->getMessage()); } } public function exportUsers() { try { $users = User::with(['roles', 'branch']) ->when($this->search, function ($q) { $searchTerm = '%' . $this->search . '%'; $q->where(function ($query) use ($searchTerm) { $query->where('name', 'like', $searchTerm) ->orWhere('email', 'like', $searchTerm); }); }) ->when($this->roleFilter, function ($q) { $q->whereHas('roles', function ($query) { $query->where('roles.name', $this->roleFilter); }); }) ->when($this->statusFilter, function ($q) { $q->where('status', $this->statusFilter); }) ->when($this->branchFilter, function ($q) { $q->where('branch_code', $this->branchFilter); }) ->get(); // Create CSV content $csvData = []; $csvData[] = [ 'Name', 'Email', 'Employee ID', 'Phone', 'Department', 'Position', 'Branch', 'Status', 'Hire Date', 'Roles' ]; foreach ($users as $user) { $csvData[] = [ $user->name, $user->email, $user->employee_id, $user->phone, $user->department, $user->position, $user->branch ? $user->branch->name : $user->branch_code, $user->status, $user->hire_date ? $user->hire_date->format('Y-m-d') : '', $user->roles->pluck('display_name')->join(', ') ]; } // Store CSV file $fileName = 'users_export_' . now()->format('Y_m_d_H_i_s') . '.csv'; $csv = ''; foreach ($csvData as $row) { $csv .= '"' . implode('","', $row) . '"' . "\n"; } Storage::put('exports/' . $fileName, $csv); session()->flash('success', 'Export completed! Downloaded ' . $users->count() . ' users to ' . $fileName); } catch (\Exception $e) { session()->flash('error', 'Export failed: ' . $e->getMessage()); } } public function getUserRoles($user) { return $user->roles() ->where('user_roles.is_active', true) ->where(function ($q) { $q->whereNull('user_roles.expires_at') ->orWhere('user_roles.expires_at', '>', now()); }) ->pluck('display_name') ->join(', '); } public function getUserPermissionCount($user) { return $user->getAllPermissions()->count(); } public function hasActiveFilters() { return !empty($this->search) || !empty($this->roleFilter) || !empty($this->statusFilter) || !empty($this->departmentFilter) || !empty($this->branchFilter) || !empty($this->customerFilter) || !empty($this->hireYearFilter) || $this->showInactive; } public function getSelectedCount() { return count($this->selectedUsers); } public function resetFilters() { $this->clearFilters(); } public function getRoleBadgeClass($roleName) { return match($roleName) { 'super_admin' => 'bg-gradient-to-r from-purple-500 to-pink-500 text-white', 'administrator' => 'bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200', 'manager' => 'bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200', 'service_coordinator' => 'bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200', 'service_supervisor' => 'bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200', 'technician' => 'bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200', 'receptionist' => 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200', 'parts_clerk' => 'bg-amber-100 dark:bg-amber-900 text-amber-800 dark:text-amber-200', 'service_advisor' => 'bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200', 'cashier' => 'bg-pink-100 dark:bg-pink-900 text-pink-800 dark:text-pink-200', 'customer_portal' => 'bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200', 'customer' => 'bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200', default => 'bg-zinc-100 dark:bg-zinc-700 text-zinc-800 dark:text-zinc-200', }; } public function getStatusBadgeClass($status) { return match($status) { 'active' => 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200', 'inactive' => 'bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200', 'suspended' => 'bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200', default => 'bg-zinc-100 dark:bg-zinc-700 text-zinc-800 dark:text-zinc-200', }; } public function getUserActivityClass($user) { $lastSeen = $user->last_seen_at; if (!$lastSeen) return 'text-gray-500'; $minutesAgo = now()->diffInMinutes($lastSeen); if ($minutesAgo < 5) return 'text-green-500'; if ($minutesAgo < 60) return 'text-yellow-500'; if ($minutesAgo < 1440) return 'text-orange-500'; return 'text-red-500'; } public function getUserLastSeenText($user) { $lastSeen = $user->last_seen_at; if (!$lastSeen) return 'Never'; return $lastSeen->diffForHumans(); } public function canDeleteUser($user) { return $user->id !== auth()->id() && $user->jobCards()->count() === 0 && !$user->hasRole('super_admin'); } public function canModifyUser($user) { return $user->id !== auth()->id() || auth()->user()->hasRole('super_admin'); } }