- Implemented dashboard view with vehicle stats, active services, recent activity, and upcoming appointments. - Created estimates view with filtering options and a list of service estimates. - Developed invoices view to manage service invoices and payment history with filtering. - Added vehicles view to display registered vehicles and their details. - Built work orders view to track the progress of vehicle services with filtering and detailed information.
394 lines
13 KiB
PHP
394 lines
13 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Users;
|
|
|
|
use Livewire\Component;
|
|
use Livewire\WithPagination;
|
|
use App\Models\User;
|
|
use App\Models\Role;
|
|
|
|
class Index extends Component
|
|
{
|
|
use WithPagination;
|
|
|
|
public $search = '';
|
|
public $roleFilter = '';
|
|
public $statusFilter = '';
|
|
public $departmentFilter = '';
|
|
public $branchFilter = '';
|
|
public $customerFilter = '';
|
|
public $sortField = 'name';
|
|
public $sortDirection = 'asc';
|
|
public $perPage = 25;
|
|
public $showInactive = false;
|
|
public $selectedUsers = [];
|
|
public $selectAll = false;
|
|
|
|
protected $queryString = [
|
|
'search' => ['except' => ''],
|
|
'roleFilter' => ['except' => ''],
|
|
'statusFilter' => ['except' => ''],
|
|
'departmentFilter' => ['except' => ''],
|
|
'branchFilter' => ['except' => ''],
|
|
'customerFilter' => ['except' => ''],
|
|
'sortField' => ['except' => 'name'],
|
|
'sortDirection' => ['except' => 'asc'],
|
|
'perPage' => ['except' => 25],
|
|
'showInactive' => ['except' => false],
|
|
];
|
|
|
|
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 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'])
|
|
->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());
|
|
});
|
|
}])
|
|
->when($this->search, function ($q) {
|
|
$q->where(function ($query) {
|
|
$query->where('name', 'like', '%' . $this->search . '%')
|
|
->orWhere('email', 'like', '%' . $this->search . '%')
|
|
->orWhere('employee_id', 'like', '%' . $this->search . '%')
|
|
->orWhere('phone', 'like', '%' . $this->search . '%')
|
|
->orWhere('national_id', 'like', '%' . $this->search . '%');
|
|
});
|
|
})
|
|
->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->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);
|
|
|
|
$roles = Role::where('is_active', true)->orderBy('display_name')->get();
|
|
$departments = User::select('department')
|
|
->distinct()
|
|
->whereNotNull('department')
|
|
->where('department', '!=', '')
|
|
->orderBy('department')
|
|
->pluck('department');
|
|
$branches = User::select('branch_code')
|
|
->distinct()
|
|
->whereNotNull('branch_code')
|
|
->where('branch_code', '!=', '')
|
|
->orderBy('branch_code')
|
|
->pluck('branch_code');
|
|
|
|
// Get summary 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(),
|
|
];
|
|
|
|
return view('livewire.users.index', compact('users', 'roles', 'departments', 'branches', 'stats'));
|
|
}
|
|
|
|
public function sortBy($field)
|
|
{
|
|
if ($this->sortField === $field) {
|
|
$this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
|
|
} else {
|
|
$this->sortField = $field;
|
|
$this->sortDirection = 'asc';
|
|
}
|
|
}
|
|
|
|
public function clearFilters()
|
|
{
|
|
$this->search = '';
|
|
$this->roleFilter = '';
|
|
$this->statusFilter = '';
|
|
$this->departmentFilter = '';
|
|
$this->branchFilter = '';
|
|
$this->customerFilter = '';
|
|
$this->showInactive = false;
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function toggleShowInactive()
|
|
{
|
|
$this->showInactive = !$this->showInactive;
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function selectAllUsers()
|
|
{
|
|
if ($this->selectAll) {
|
|
$this->selectedUsers = [];
|
|
$this->selectAll = false;
|
|
} else {
|
|
$this->selectedUsers = User::pluck('id')->toArray();
|
|
$this->selectAll = true;
|
|
}
|
|
}
|
|
|
|
public function bulkActivate()
|
|
{
|
|
if (empty($this->selectedUsers)) {
|
|
session()->flash('error', 'No users selected.');
|
|
return;
|
|
}
|
|
|
|
$count = User::whereIn('id', $this->selectedUsers)
|
|
->where('id', '!=', auth()->id())
|
|
->update(['status' => 'active']);
|
|
|
|
$this->selectedUsers = [];
|
|
$this->selectAll = false;
|
|
|
|
session()->flash('success', "Activated {$count} users successfully.");
|
|
}
|
|
|
|
public function bulkDeactivate()
|
|
{
|
|
if (empty($this->selectedUsers)) {
|
|
session()->flash('error', 'No users selected.');
|
|
return;
|
|
}
|
|
|
|
$count = User::whereIn('id', $this->selectedUsers)
|
|
->where('id', '!=', auth()->id())
|
|
->update(['status' => 'inactive']);
|
|
|
|
$this->selectedUsers = [];
|
|
$this->selectAll = false;
|
|
|
|
session()->flash('success', "Deactivated {$count} users successfully.");
|
|
}
|
|
|
|
public function deactivateUser($userId)
|
|
{
|
|
$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.");
|
|
}
|
|
|
|
public function activateUser($userId)
|
|
{
|
|
$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.");
|
|
}
|
|
|
|
public function suspendUser($userId)
|
|
{
|
|
$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.");
|
|
}
|
|
|
|
public function deleteUser($userId)
|
|
{
|
|
$user = User::findOrFail($userId);
|
|
|
|
if ($user->id === auth()->id()) {
|
|
session()->flash('error', 'You cannot delete your own account.');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// 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());
|
|
}
|
|
}
|
|
|
|
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) ||
|
|
$this->showInactive;
|
|
}
|
|
|
|
public function getSelectedCount()
|
|
{
|
|
return count($this->selectedUsers);
|
|
}
|
|
|
|
public function exportUsers()
|
|
{
|
|
// This would typically export to CSV or Excel
|
|
$users = User::with(['roles'])
|
|
->when($this->search, function ($q) {
|
|
$q->where(function ($query) {
|
|
$query->where('name', 'like', '%' . $this->search . '%')
|
|
->orWhere('email', 'like', '%' . $this->search . '%')
|
|
->orWhere('employee_id', 'like', '%' . $this->search . '%');
|
|
});
|
|
})
|
|
->get();
|
|
|
|
session()->flash('success', 'Export initiated for ' . $users->count() . ' users.');
|
|
}
|
|
|
|
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',
|
|
'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',
|
|
};
|
|
}
|
|
}
|