<?php
namespace App\Http\Controllers\Technician;
use App\Actions\Complaint\AddProgressNoteAction;
use App\Actions\Complaint\ClaimComplaintAction;
use App\Actions\Complaint\ReleaseComplaintAction;
use App\Actions\Complaint\ResolveComplaintAction;
use App\Http\Controllers\Controller;
use App\Http\Requests\ResolveComplaintRequest;
use App\Models\Complaint;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class TicketController extends Controller
{
/**
* Technician dashboard: the open pool they're allowed to claim, plus
* their own already-claimed tickets. There is no admin push-assign step
* anymore — technicians pick tickets up themselves (§4).
*/
public function index(Request $request): View
{
$technician = $request->user();
$availableTickets = Complaint::query()
->with(['client', 'application'])
->where('status', 'open')
->where(function ($query) use ($technician) {
// Include tickets with no application, or with an application
// that isn't locked to a different specialist (§12).
$query->whereDoesntHave('application', function ($appQuery) use ($technician) {
$appQuery->whereNotNull('dedicated_technician_id')
->where('dedicated_technician_id', '!=', $technician->id);
});
})
->latest('report_date')
->get();
$myTickets = Complaint::query()
->with('client')
->where('assigned_technician_id', $technician->id)
->latest('report_date')
->paginate(20);
return view('technician.tickets.index', [
'availableTickets' => $availableTickets,
'myTickets' => $myTickets,
]);
}
public function show(Request $request, Complaint $complaint): View
{
$technician = $request->user();
$isMine = $complaint->assigned_technician_id === $technician->id;
abort_unless($isMine || $complaint->status === 'open', 403);
$complaint->load(['client', 'application', 'responses', 'activities.causer']);
return view('technician.tickets.show', [
'complaint' => $complaint,
'isMine' => $isMine,
]);
}
public function claim(Request $request, Complaint $complaint, ClaimComplaintAction $action): RedirectResponse
{
try {
$action->handle($complaint, $request->user());
} catch (\DomainException $e) {
return back()->with('error', $e->getMessage());
}
return redirect()->route('technician.tickets.show', $complaint)->with('success', "Tiket {$complaint->ticket_number} berhasil diambil.");
}
public function release(Request $request, Complaint $complaint, ReleaseComplaintAction $action): RedirectResponse
{
try {
$action->handle($complaint, $request->user());
} catch (\DomainException $e) {
return back()->with('error', $e->getMessage());
}
return redirect()->route('technician.tickets.index')->with('success', "Tiket {$complaint->ticket_number} dilepas kembali ke antrian.");
}
/**
* Technician logs a progress update without resolving the ticket —
* a ticket can collect several of these across days before it's done.
*/
public function progress(ResolveComplaintRequest $request, Complaint $complaint, AddProgressNoteAction $action): RedirectResponse
{
try {
$action->handle($complaint, $request->user(), $request->validated('respons'), $request->file('photos', []));
} catch (\DomainException $e) {
return back()->with('error', $e->getMessage());
}
return redirect()->route('technician.tickets.show', $complaint)->with('success', 'Update progres berhasil disimpan.');
}
/**
* Technician submits their final response; complaint moves to resolved.
*/
public function resolve(ResolveComplaintRequest $request, Complaint $complaint, ResolveComplaintAction $action): RedirectResponse
{
$action->handle($complaint, $request->user(), $request->validated('respons'), $request->file('photos', []));
return redirect()->route('technician.tickets.index')->with('success', "Tiket {$complaint->ticket_number} diselesaikan.");
}
}