sackey a65fee9d75
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (push) Waiting to run
Add customer portal workflow progress component and analytics dashboard
- Implemented the customer portal workflow progress component with detailed service progress tracking, including current status, workflow steps, and contact information.
- Developed a management workflow analytics dashboard featuring key performance indicators, charts for revenue by branch, labor utilization, and recent quality issues.
- Created tests for admin-only middleware to ensure proper access control for admin routes.
- Added tests for customer portal view rendering and workflow integration, ensuring the workflow service operates correctly through various stages.
- Introduced a .gitignore file for the debugbar storage directory to prevent unnecessary files from being tracked.
2025-08-10 19:41:25 +00:00

310 lines
18 KiB
PHP

<div class="max-w-4xl mx-auto">
<div class="bg-white dark:bg-zinc-800 shadow-lg rounded-lg">
<div class="px-6 py-4 border-b border-zinc-200 dark:border-zinc-700 flex items-center justify-between">
<div>
<h2 class="text-xl font-semibold text-zinc-900 dark:text-white">Edit User</h2>
<p class="text-sm text-zinc-600 dark:text-zinc-400">Update user information and permissions</p>
</div>
<a href="{{ route('users.index') }}" class="px-4 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-300 bg-zinc-100 dark:bg-zinc-700 rounded-md hover:bg-zinc-200 dark:hover:bg-zinc-600 transition-colors">Back to Users</a>
</div>
<form wire:submit.prevent="save" class="p-6 space-y-8">
<!-- Personal Information -->
<div>
<h3 class="text-lg font-medium text-zinc-900 dark:text-white mb-4">Personal Information</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
Full Name *
</label>
<input type="text"
wire:model.lazy="form.name"
autocomplete="name"
placeholder="Full Name"
class="w-full rounded-md border-zinc-300 dark:border-zinc-600 dark:bg-zinc-700 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500">
@error('form.name') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
Email Address *
</label>
<input type="email"
wire:model.lazy="form.email"
autocomplete="email"
placeholder="Email Address"
class="w-full rounded-md border-zinc-300 dark:border-zinc-600 dark:bg-zinc-700 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500">
@error('form.email') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
Phone Number
</label>
<input type="text"
wire:model.lazy="form.phone"
autocomplete="tel"
placeholder="Phone Number"
class="w-full rounded-md border-zinc-300 dark:border-zinc-600 dark:bg-zinc-700 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500">
@error('form.phone') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
National ID
</label>
<input type="text"
wire:model.lazy="form.national_id"
placeholder="National ID"
class="w-full rounded-md border-zinc-300 dark:border-zinc-600 dark:bg-zinc-700 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500">
@error('form.national_id') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
<div class="md:col-span-2">
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
Address
</label>
<textarea wire:model.lazy="form.address"
rows="3"
placeholder="Address"
class="w-full rounded-md border-zinc-300 dark:border-zinc-600 dark:bg-zinc-700 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500"></textarea>
@error('form.address') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
</div>
</div>
<!-- Professional Information -->
<div>
<h3 class="text-lg font-medium text-zinc-900 dark:text-white mb-4">Professional Information</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
Position
</label>
<input type="text"
wire:model.lazy="form.position"
placeholder="Position"
class="w-full rounded-md border-zinc-300 dark:border-zinc-600 dark:bg-zinc-700 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500"
list="position-suggestions">
<datalist id="position-suggestions">
@if(isset($positions))
@foreach($positions as $suggestion)
<option value="{{ $suggestion }}">
@endforeach
@endif
</datalist>
@error('form.position') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
Department
</label>
<select wire:model.lazy="form.department"
class="w-full rounded-md border-zinc-300 dark:border-zinc-600 dark:bg-zinc-700 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500">
<option value="">Select Department</option>
<option value="management">Management</option>
<option value="service">Service Department</option>
<option value="parts">Parts Department</option>
<option value="body_shop">Body Shop</option>
<option value="sales">Sales</option>
<option value="administration">Administration</option>
<option value="customer_service">Customer Service</option>
</select>
@error('form.department') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
Salary
</label>
<input type="number"
step="0.01"
wire:model.lazy="form.salary"
placeholder="Salary"
class="w-full rounded-md border-zinc-300 dark:border-zinc-600 dark:bg-zinc-700 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500">
@error('form.salary') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
Hire Date
</label>
<input type="date"
wire:model.lazy="form.hire_date"
class="w-full rounded-md border-zinc-300 dark:border-zinc-600 dark:bg-zinc-700 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500">
@error('form.hire_date') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
<div class="md:col-span-2">
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
Emergency Contact
</label>
<input type="text"
wire:model.lazy="form.emergency_contact"
placeholder="Name, relationship, phone number"
class="w-full rounded-md border-zinc-300 dark:border-zinc-600 dark:bg-zinc-700 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500">
@error('form.emergency_contact') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
</div>
</div>
<!-- Account Settings -->
<div>
<h3 class="text-lg font-medium text-zinc-900 dark:text-white mb-4">Account Settings</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
Account Status
</label>
<select wire:model.lazy="form.status"
class="w-full rounded-md border-zinc-300 dark:border-zinc-600 dark:bg-zinc-700 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500">
<option value="active">Active</option>
<option value="inactive">Inactive</option>
<option value="suspended">Suspended</option>
</select>
@error('form.status') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
Branch
</label>
<select wire:model="branch_code"
class="w-full rounded-md border-zinc-300 dark:border-zinc-600 dark:bg-zinc-700 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500">
<option value="">Select Branch</option>
@foreach($branches as $branch)
<option value="{{ $branch->code }}">{{ $branch->name }} ({{ $branch->code }})</option>
@endforeach
</select>
@error('branch_code') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
</div>
</div>
<!-- Role Assignment -->
<div>
<h3 class="text-lg font-medium text-zinc-900 dark:text-white mb-4">Role Assignment</h3>
<div class="space-y-3">
@foreach($availableRoles as $role)
<label class="flex items-center">
<input type="checkbox"
wire:model.lazy="form.roles"
value="{{ $role->id }}"
class="rounded border-zinc-300 dark:border-zinc-600 text-blue-600 shadow-sm focus:border-blue-500 focus:ring-blue-500">
<div class="ml-3">
<span class="text-sm font-medium text-zinc-900 dark:text-white">{{ $role->name }}</span>
@if($role->description)
<p class="text-xs text-zinc-500 dark:text-zinc-400">{{ $role->description }}</p>
@endif
</div>
</label>
@endforeach
</div>
@error('form.roles') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
<!-- Password Management -->
<div>
<h3 class="text-lg font-medium text-zinc-900 dark:text-white mb-4">Password Management</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
New Password (leave blank to keep current)
</label>
<input type="password"
wire:model.lazy="form.password"
autocomplete="new-password"
placeholder="New Password"
class="w-full rounded-md border-zinc-300 dark:border-zinc-600 dark:bg-zinc-700 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500">
@error('form.password') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
Confirm Password
</label>
<input type="password"
wire:model.lazy="form.password_confirmation"
autocomplete="new-password"
placeholder="Confirm Password"
class="w-full rounded-md border-zinc-300 dark:border-zinc-600 dark:bg-zinc-700 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500">
</div>
</div>
<div class="mt-4 flex gap-3">
<button type="button"
wire:click="generatePassword"
class="px-4 py-2 text-sm font-medium text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/20 rounded-md hover:bg-blue-100 dark:hover:bg-blue-900/30 transition-colors">
Generate Random Password
</button>
<button type="button"
wire:click="resetPassword"
class="px-4 py-2 text-sm font-medium text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-900/20 rounded-md hover:bg-amber-100 dark:hover:bg-amber-900/30 transition-colors">
Send Password Reset Email
</button>
</div>
</div>
<!-- Advanced Actions -->
<div>
<h3 class="text-lg font-medium text-zinc-900 dark:text-white mb-4">Advanced Actions</h3>
<div class="flex gap-3">
@can('impersonate-users')
<button type="button"
wire:click="impersonateUser"
class="px-4 py-2 text-sm font-medium text-purple-600 dark:text-purple-400 bg-purple-50 dark:bg-purple-900/20 rounded-md hover:bg-purple-100 dark:hover:bg-purple-900/30 transition-colors">
Impersonate User
</button>
@endcan
<button type="button"
wire:click="forceEmailVerification"
class="px-4 py-2 text-sm font-medium text-green-600 dark:text-green-400 bg-green-50 dark:bg-green-900/20 rounded-md hover:bg-green-100 dark:hover:bg-green-900/30 transition-colors">
Force Email Verification
</button>
</div>
</div>
<!-- Action Buttons -->
<div class="flex items-center justify-between pt-6 border-t border-zinc-200 dark:border-zinc-700">
<div class="flex gap-3">
<button type="submit"
class="px-6 py-2 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed"
wire:loading.attr="disabled">
<span wire:loading.remove>Update User</span>
<span wire:loading>Updating...</span>
</button>
<a href="{{ route('users.index') }}" class="px-6 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-300 bg-zinc-100 dark:bg-zinc-700 rounded-md hover:bg-zinc-200 dark:hover:bg-zinc-600 transition-colors">Cancel</a>
</div>
@can('delete-users')
<button type="button" wire:click="confirmDelete" class="px-4 py-2 text-sm font-medium text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/20 rounded-md hover:bg-red-100 dark:hover:bg-red-900/30 transition-colors">Delete User</button>
@endcan
</div>
</form>
</div>
<!-- Delete Confirmation Modal -->
@if($showDeleteModal)
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div class="bg-white dark:bg-zinc-800 rounded-lg p-6 max-w-md w-full mx-4">
<h3 class="text-lg font-medium text-zinc-900 dark:text-white mb-4">Delete User</h3>
<p class="text-sm text-zinc-600 dark:text-zinc-400 mb-6">Are you sure you want to delete this user? This action cannot be undone.</p>
<div class="flex justify-end gap-3">
<button wire:click="$set('showDeleteModal', false)" class="px-4 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-300 bg-zinc-100 dark:bg-zinc-700 rounded-md hover:bg-zinc-200 dark:hover:bg-zinc-600 transition-colors">Cancel</button>
<button wire:click="deleteUser" class="px-4 py-2 text-sm font-medium text-white bg-red-600 rounded-md hover:bg-red-700 transition-colors">Delete</button>
</div>
</div>
</div>
@endif
</div>
@push('styles')
<style>
.transition-colors {
transition-property: color, background-color, border-color;
transition-duration: 150ms;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
</style>
@endpush