initial
This commit is contained in:
123
resources/views/livewire/travel-request/approve.blade.php
Normal file
123
resources/views/livewire/travel-request/approve.blade.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
use App\Models\TravelRequest;
|
||||
use App\Models\TravelRequestApproval;
|
||||
use App\Services\ApprovalService;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Component;
|
||||
|
||||
new #[Layout('components.layouts.app')] class extends Component {
|
||||
public TravelRequest $travelRequest;
|
||||
public ?TravelRequestApproval $pendingApproval = null;
|
||||
public string $comments = '';
|
||||
|
||||
public function mount(int $id): void
|
||||
{
|
||||
$user = Auth::user();
|
||||
abort_unless($user->hasAnyRole(['travel_approver', 'administrator']), 403);
|
||||
|
||||
$this->travelRequest = TravelRequest::with([
|
||||
'user', 'journeys', 'costCodes',
|
||||
'approvals.step', 'approvals.approver',
|
||||
])->findOrFail($id);
|
||||
|
||||
$this->pendingApproval = $this->travelRequest->approvals()
|
||||
->where('status', \App\Enums\ApprovalStatus::Pending->value)
|
||||
->with('step')
|
||||
->first();
|
||||
}
|
||||
|
||||
public function approve(): void
|
||||
{
|
||||
abort_unless($this->pendingApproval, 403);
|
||||
app(ApprovalService::class)->approve($this->pendingApproval, Auth::user(), $this->comments ?: null);
|
||||
session()->flash('success', 'Request approved successfully.');
|
||||
$this->redirect(route('dashboard'), navigate: true);
|
||||
}
|
||||
|
||||
public function reject(): void
|
||||
{
|
||||
$this->validate(['comments' => ['required', 'string', 'min:5']], [
|
||||
'comments.required' => 'Please provide a reason for rejection.',
|
||||
'comments.min' => 'Rejection reason must be at least 5 characters.',
|
||||
]);
|
||||
|
||||
abort_unless($this->pendingApproval, 403);
|
||||
app(ApprovalService::class)->reject($this->pendingApproval, Auth::user(), $this->comments);
|
||||
session()->flash('success', 'Request rejected.');
|
||||
$this->redirect(route('dashboard'), navigate: true);
|
||||
}
|
||||
|
||||
public function render(): mixed
|
||||
{
|
||||
return view('livewire.travel-request.approve');
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="h4 mb-0">Review Travel Request #{{ $travelRequest->id }}</h2>
|
||||
<a href="{{ route('dashboard') }}" class="btn btn-outline-secondary btn-sm">Back</a>
|
||||
</div>
|
||||
|
||||
@if (! $pendingApproval)
|
||||
<div class="alert alert-info">This request has no pending approval steps.</div>
|
||||
@else
|
||||
<div class="alert alert-info">
|
||||
<strong>Step {{ $pendingApproval->step->order }}: {{ $pendingApproval->step->name }}</strong>
|
||||
— Awaiting your review.
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Request Summary --}}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header fw-semibold">Request Summary</div>
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-3">Applicant</dt>
|
||||
<dd class="col-sm-9">{{ $travelRequest->user->name }} ({{ $travelRequest->user->email }})</dd>
|
||||
<dt class="col-sm-3">Reason</dt>
|
||||
<dd class="col-sm-9">{{ $travelRequest->reason_summary }}</dd>
|
||||
<dt class="col-sm-3">Journeys</dt>
|
||||
<dd class="col-sm-9">
|
||||
@foreach ($travelRequest->journeys as $journey)
|
||||
<div>{{ $journey->origin }} → {{ $journey->destination }} on {{ $journey->date->format('d M Y') }} ({{ $journey->method->label() }})</div>
|
||||
@endforeach
|
||||
</dd>
|
||||
<dt class="col-sm-3">Accommodation</dt>
|
||||
<dd class="col-sm-9">{{ $travelRequest->needs_accommodation ? 'Required' : 'Not required' }}</dd>
|
||||
<dt class="col-sm-3">Car Hire</dt>
|
||||
<dd class="col-sm-9">{{ $travelRequest->needs_car_hire ? 'Required' : 'Not required' }}</dd>
|
||||
<dt class="col-sm-3">Business Days</dt>
|
||||
<dd class="col-sm-9">{{ $travelRequest->business_days }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($pendingApproval)
|
||||
{{-- Approve/Reject Form --}}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header fw-semibold">Decision</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Comments (optional for approval, required for rejection)</label>
|
||||
<textarea wire:model="comments" rows="3" class="form-control @error('comments') is-invalid @enderror" placeholder="Add your comments..."></textarea>
|
||||
@error('comments') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" wire:click="approve" class="btn btn-success" wire:loading.attr="disabled" wire:target="approve">
|
||||
<span wire:loading wire:target="approve" class="spinner-border spinner-border-sm me-1"></span>
|
||||
Approve
|
||||
</button>
|
||||
<button type="button" wire:click="reject" class="btn btn-danger" wire:loading.attr="disabled" wire:target="reject">
|
||||
<span wire:loading wire:target="reject" class="spinner-border spinner-border-sm me-1"></span>
|
||||
Reject
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
Reference in New Issue
Block a user