sackey cbae4564b9
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
Add customer portal views for dashboard, estimates, invoices, vehicles, and work orders
- 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.
2025-08-08 09:56:26 +00:00

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',
};
}
}