- 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.
508 lines
18 KiB
PHP
508 lines
18 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Users;
|
|
|
|
use Livewire\Component;
|
|
use App\Models\User;
|
|
use App\Models\Role;
|
|
use App\Models\Permission;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Spatie\Activitylog\Models\Activity;
|
|
|
|
class Show extends Component
|
|
{
|
|
public User $user;
|
|
public $activeTab = 'profile';
|
|
public $showRoleModal = false;
|
|
public $showPermissionModal = false;
|
|
public $selectedRole = null;
|
|
public $selectedPermission = null;
|
|
public $showDeleteModal = false;
|
|
public $showImpersonateModal = false;
|
|
public $showActivityModal = false;
|
|
|
|
// User actions
|
|
public $confirmingAction = false;
|
|
public $pendingAction = '';
|
|
|
|
protected $queryString = ['activeTab'];
|
|
|
|
public function mount(User $user)
|
|
{
|
|
$this->user = $user->load(['roles.permissions', 'permissions', 'customer']);
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
$userRoles = $this->user->roles()
|
|
->where('user_roles.is_active', true)
|
|
->where(function ($q) {
|
|
$q->whereNull('user_roles.expires_at')
|
|
->orWhere('user_roles.expires_at', '>', now());
|
|
})
|
|
->withPivot(['branch_code', 'assigned_at', 'expires_at'])
|
|
->get();
|
|
|
|
$userDirectPermissions = $this->user->permissions()
|
|
->where('user_permissions.granted', true)
|
|
->where(function ($q) {
|
|
$q->whereNull('user_permissions.expires_at')
|
|
->orWhere('user_permissions.expires_at', '>', now());
|
|
})
|
|
->withPivot(['branch_code', 'assigned_at', 'expires_at'])
|
|
->get();
|
|
|
|
$allPermissions = $this->user->getAllPermissions();
|
|
$permissionsByModule = $allPermissions->groupBy('module');
|
|
|
|
// Get role-based permissions
|
|
$rolePermissions = collect();
|
|
foreach ($userRoles as $role) {
|
|
$rolePermissions = $rolePermissions->merge($role->permissions);
|
|
}
|
|
$rolePermissions = $rolePermissions->unique('id');
|
|
|
|
// Get recent activity
|
|
$causedByUser = Activity::where('causer_id', $this->user->id)
|
|
->where('causer_type', User::class)
|
|
->latest()
|
|
->limit(10)
|
|
->get();
|
|
|
|
$performedOnUser = Activity::where('subject_id', $this->user->id)
|
|
->where('subject_type', User::class)
|
|
->latest()
|
|
->limit(10)
|
|
->get();
|
|
|
|
$recentActivity = $causedByUser->merge($performedOnUser)
|
|
->sortByDesc('created_at')
|
|
->take(20);
|
|
|
|
// Get user metrics
|
|
$metrics = $this->getUserMetrics();
|
|
|
|
// Get user's work orders, service orders, etc. (if applicable)
|
|
$workStats = $this->getUserWorkStats();
|
|
|
|
return view('livewire.users.show', [
|
|
'userRoles' => $userRoles,
|
|
'userDirectPermissions' => $userDirectPermissions,
|
|
'allPermissions' => $allPermissions,
|
|
'permissionsByModule' => $permissionsByModule,
|
|
'rolePermissions' => $rolePermissions,
|
|
'recentActivity' => $recentActivity,
|
|
'metrics' => $metrics,
|
|
'workStats' => $workStats,
|
|
]);
|
|
}
|
|
|
|
public function setActiveTab($tab)
|
|
{
|
|
$this->activeTab = $tab;
|
|
}
|
|
|
|
public function showRoleDetails($roleId)
|
|
{
|
|
$this->selectedRole = Role::with('permissions')->find($roleId);
|
|
$this->showRoleModal = true;
|
|
}
|
|
|
|
public function showPermissionDetails($permissionId)
|
|
{
|
|
$this->selectedPermission = Permission::find($permissionId);
|
|
$this->showPermissionModal = true;
|
|
}
|
|
|
|
public function closeModals()
|
|
{
|
|
$this->showRoleModal = false;
|
|
$this->showPermissionModal = false;
|
|
$this->showDeleteModal = false;
|
|
$this->showImpersonateModal = false;
|
|
$this->showActivityModal = false;
|
|
$this->selectedRole = null;
|
|
$this->selectedPermission = null;
|
|
}
|
|
|
|
public function removeRole($roleId)
|
|
{
|
|
try {
|
|
$this->user->roles()->detach($roleId);
|
|
$this->user->refresh();
|
|
|
|
// Log the action
|
|
activity()
|
|
->performedOn($this->user)
|
|
->causedBy(auth()->user())
|
|
->withProperties(['role_id' => $roleId])
|
|
->log('Role removed from user');
|
|
|
|
session()->flash('success', 'Role removed successfully.');
|
|
} catch (\Exception $e) {
|
|
session()->flash('error', 'Failed to remove role: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
public function removePermission($permissionId)
|
|
{
|
|
try {
|
|
$this->user->permissions()->detach($permissionId);
|
|
$this->user->refresh();
|
|
|
|
// Log the action
|
|
activity()
|
|
->performedOn($this->user)
|
|
->causedBy(auth()->user())
|
|
->withProperties(['permission_id' => $permissionId])
|
|
->log('Permission removed from user');
|
|
|
|
session()->flash('success', 'Permission removed successfully.');
|
|
} catch (\Exception $e) {
|
|
session()->flash('error', 'Failed to remove permission: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
public function toggleUserStatus()
|
|
{
|
|
if ($this->user->id === auth()->id()) {
|
|
session()->flash('error', 'You cannot change your own status.');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$newStatus = $this->user->status === 'active' ? 'inactive' : 'active';
|
|
$oldStatus = $this->user->status;
|
|
|
|
$this->user->update(['status' => $newStatus]);
|
|
|
|
// Log the action
|
|
activity()
|
|
->performedOn($this->user)
|
|
->causedBy(auth()->user())
|
|
->withProperties([
|
|
'old_status' => $oldStatus,
|
|
'new_status' => $newStatus,
|
|
])
|
|
->log('User status changed');
|
|
|
|
$statusText = $newStatus === 'active' ? 'activated' : 'deactivated';
|
|
session()->flash('success', "User {$statusText} successfully.");
|
|
} catch (\Exception $e) {
|
|
session()->flash('error', 'Failed to update status: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
public function suspendUser()
|
|
{
|
|
if ($this->user->id === auth()->id()) {
|
|
session()->flash('error', 'You cannot suspend your own account.');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$oldStatus = $this->user->status;
|
|
$this->user->update(['status' => 'suspended']);
|
|
|
|
// Log the action
|
|
activity()
|
|
->performedOn($this->user)
|
|
->causedBy(auth()->user())
|
|
->withProperties([
|
|
'old_status' => $oldStatus,
|
|
'new_status' => 'suspended',
|
|
])
|
|
->log('User suspended');
|
|
|
|
session()->flash('success', 'User suspended successfully.');
|
|
} catch (\Exception $e) {
|
|
session()->flash('error', 'Failed to suspend user: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
public function impersonateUser()
|
|
{
|
|
if ($this->user->id === auth()->id()) {
|
|
session()->flash('error', 'You cannot impersonate yourself.');
|
|
return;
|
|
}
|
|
|
|
if ($this->user->status !== 'active') {
|
|
session()->flash('error', 'Cannot impersonate inactive user.');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Log the impersonation start
|
|
activity()
|
|
->performedOn($this->user)
|
|
->causedBy(auth()->user())
|
|
->log('Impersonation started');
|
|
|
|
// Store original user ID for returning later
|
|
session(['impersonate_original_user' => auth()->id()]);
|
|
auth()->loginUsingId($this->user->id);
|
|
|
|
session()->flash('success', 'Now impersonating ' . $this->user->name);
|
|
return redirect()->route('dashboard');
|
|
} catch (\Exception $e) {
|
|
session()->flash('error', 'Failed to impersonate user: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
public function sendPasswordReset()
|
|
{
|
|
try {
|
|
// Generate a secure temporary password
|
|
$tempPassword = $this->generateSecurePassword();
|
|
|
|
$this->user->update([
|
|
'password' => Hash::make($tempPassword),
|
|
'password_changed_at' => now(),
|
|
]);
|
|
|
|
// Log the action
|
|
activity()
|
|
->performedOn($this->user)
|
|
->causedBy(auth()->user())
|
|
->log('Password reset by admin');
|
|
|
|
// TODO: Send password reset email with new temporary password
|
|
// $this->user->notify(new PasswordResetByAdminNotification($tempPassword));
|
|
|
|
session()->flash('success', "Password reset successfully. New temporary password: {$tempPassword}");
|
|
} catch (\Exception $e) {
|
|
session()->flash('error', 'Failed to reset password: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
public function generateSecurePassword($length = 12)
|
|
{
|
|
$uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
$lowercase = 'abcdefghijklmnopqrstuvwxyz';
|
|
$numbers = '0123456789';
|
|
$symbols = '!@#$%^&*()_+-=[]{}|;:,.<>?';
|
|
|
|
$password = '';
|
|
$password .= $uppercase[random_int(0, strlen($uppercase) - 1)];
|
|
$password .= $lowercase[random_int(0, strlen($lowercase) - 1)];
|
|
$password .= $numbers[random_int(0, strlen($numbers) - 1)];
|
|
$password .= $symbols[random_int(0, strlen($symbols) - 1)];
|
|
|
|
$allChars = $uppercase . $lowercase . $numbers . $symbols;
|
|
for ($i = 4; $i < $length; $i++) {
|
|
$password .= $allChars[random_int(0, strlen($allChars) - 1)];
|
|
}
|
|
|
|
return str_shuffle($password);
|
|
}
|
|
|
|
public function exportUserData()
|
|
{
|
|
try {
|
|
// Log the export request
|
|
activity()
|
|
->performedOn($this->user)
|
|
->causedBy(auth()->user())
|
|
->log('User data export requested');
|
|
|
|
// TODO: Implement user data export (GDPR compliance)
|
|
// This should include all user data, activity logs, etc.
|
|
|
|
session()->flash('success', 'User data export initiated. You will receive an email when ready.');
|
|
} catch (\Exception $e) {
|
|
session()->flash('error', 'Failed to export user data: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
public function deleteUser()
|
|
{
|
|
if ($this->user->id === auth()->id()) {
|
|
session()->flash('error', 'You cannot delete your own account.');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Log before deletion
|
|
activity()
|
|
->performedOn($this->user)
|
|
->causedBy(auth()->user())
|
|
->withProperties([
|
|
'deleted_user_data' => [
|
|
'name' => $this->user->name,
|
|
'email' => $this->user->email,
|
|
'employee_id' => $this->user->employee_id,
|
|
]
|
|
])
|
|
->log('User deleted by admin');
|
|
|
|
$userName = $this->user->name;
|
|
$this->user->delete();
|
|
|
|
session()->flash('success', "User '{$userName}' deleted successfully.");
|
|
return redirect()->route('users.index');
|
|
} catch (\Exception $e) {
|
|
session()->flash('error', 'Failed to delete user: ' . $e->getMessage());
|
|
}
|
|
|
|
$this->showDeleteModal = false;
|
|
}
|
|
|
|
public function confirmDelete()
|
|
{
|
|
$this->showDeleteModal = true;
|
|
}
|
|
|
|
public function confirmImpersonate()
|
|
{
|
|
$this->showImpersonateModal = true;
|
|
}
|
|
|
|
public function getUserMetrics()
|
|
{
|
|
$totalPermissions = $this->user->getAllPermissions()->count();
|
|
$directPermissions = $this->user->permissions()
|
|
->where('user_permissions.granted', true)
|
|
->where(function ($q) {
|
|
$q->whereNull('user_permissions.expires_at')
|
|
->orWhere('user_permissions.expires_at', '>', now());
|
|
})
|
|
->count();
|
|
|
|
$activeRoles = $this->user->roles()
|
|
->where('user_roles.is_active', true)
|
|
->where(function ($q) {
|
|
$q->whereNull('user_roles.expires_at')
|
|
->orWhere('user_roles.expires_at', '>', now());
|
|
})
|
|
->count();
|
|
|
|
return [
|
|
'total_permissions' => $totalPermissions,
|
|
'direct_permissions' => $directPermissions,
|
|
'role_permissions' => $totalPermissions - $directPermissions,
|
|
'active_roles' => $activeRoles,
|
|
'days_since_created' => $this->user->created_at->diffInDays(now()),
|
|
'last_login' => $this->user->last_login_at ? $this->user->last_login_at->diffForHumans() : 'Never',
|
|
'password_age' => $this->user->password_changed_at ? $this->user->password_changed_at->diffInDays(now()) : null,
|
|
];
|
|
}
|
|
|
|
public function getUserWorkStats()
|
|
{
|
|
// Get work-related statistics for the user
|
|
$stats = [
|
|
'work_orders_assigned' => 0,
|
|
'work_orders_completed' => 0,
|
|
'service_orders_created' => 0,
|
|
'total_revenue_generated' => 0,
|
|
];
|
|
|
|
try {
|
|
// Work orders assigned to user (if technician)
|
|
if (\Schema::hasTable('work_orders')) {
|
|
$stats['work_orders_assigned'] = \DB::table('work_orders')
|
|
->where('assigned_technician_id', $this->user->id)
|
|
->count();
|
|
|
|
$stats['work_orders_completed'] = \DB::table('work_orders')
|
|
->where('assigned_technician_id', $this->user->id)
|
|
->where('status', 'completed')
|
|
->count();
|
|
}
|
|
|
|
// Service orders created by user (if service advisor)
|
|
if (\Schema::hasTable('service_orders')) {
|
|
$stats['service_orders_created'] = \DB::table('service_orders')
|
|
->where('created_by', $this->user->id)
|
|
->count();
|
|
|
|
$stats['total_revenue_generated'] = \DB::table('service_orders')
|
|
->where('created_by', $this->user->id)
|
|
->where('status', 'completed')
|
|
->sum('total_amount') ?? 0;
|
|
}
|
|
} catch (\Exception $e) {
|
|
// Tables might not exist, return default stats
|
|
}
|
|
|
|
return $stats;
|
|
}
|
|
|
|
public function getStatusBadgeClass($status)
|
|
{
|
|
return match($status) {
|
|
'active' => 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200',
|
|
'inactive' => 'bg-zinc-100 dark:bg-zinc-700 text-zinc-800 dark:text-zinc-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 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_manager' => 'bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200',
|
|
'technician' => 'bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200',
|
|
'senior_technician' => 'bg-emerald-100 dark:bg-emerald-900 text-emerald-800 dark:text-emerald-200',
|
|
'parts_clerk' => 'bg-amber-100 dark:bg-amber-900 text-amber-800 dark:text-amber-200',
|
|
'service_advisor' => 'bg-teal-100 dark:bg-teal-900 text-teal-800 dark:text-teal-200',
|
|
'receptionist' => 'bg-pink-100 dark:bg-pink-900 text-pink-800 dark:text-pink-200',
|
|
'cashier' => 'bg-orange-100 dark:bg-orange-900 text-orange-800 dark:text-orange-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',
|
|
'viewer' => 'bg-zinc-100 dark:bg-zinc-700 text-zinc-800 dark:text-zinc-200',
|
|
default => 'bg-zinc-100 dark:bg-zinc-700 text-zinc-800 dark:text-zinc-200'
|
|
};
|
|
}
|
|
|
|
public function canPerformAction($action)
|
|
{
|
|
// Check if current user can perform certain actions on this user
|
|
$currentUser = auth()->user();
|
|
|
|
// Super admin can do anything
|
|
if ($currentUser->hasRole('super_admin')) {
|
|
return true;
|
|
}
|
|
|
|
// Can't perform actions on yourself (except view)
|
|
if ($currentUser->id === $this->user->id && $action !== 'view') {
|
|
return false;
|
|
}
|
|
|
|
// Check specific permissions based on action
|
|
return match($action) {
|
|
'edit' => $currentUser->can('users.edit'),
|
|
'delete' => $currentUser->can('users.delete'),
|
|
'impersonate' => $currentUser->can('users.impersonate'),
|
|
'reset_password' => $currentUser->can('users.reset-password'),
|
|
'manage_roles' => $currentUser->can('users.manage-roles'),
|
|
'view_activity' => $currentUser->can('users.view-activity'),
|
|
default => false,
|
|
};
|
|
}
|
|
|
|
public function getLastActivityDate()
|
|
{
|
|
$lastActivity = Activity::where('causer_id', $this->user->id)
|
|
->where('causer_type', User::class)
|
|
->latest()
|
|
->first();
|
|
|
|
return $lastActivity ? $lastActivity->created_at->diffForHumans() : 'No activity recorded';
|
|
}
|
|
|
|
public function getTotalLoginCount()
|
|
{
|
|
// This would require a login tracking system
|
|
return Activity::where('causer_id', $this->user->id)
|
|
->where('causer_type', User::class)
|
|
->where('description', 'like', '%login%')
|
|
->count();
|
|
}
|
|
}
|