feat(tickets): suivi bidirectionnel des tickets utilisateur via Gitea
CI - Intégration continue / quality-and-tests (pull_request) Successful in 3m27s
CI - Intégration continue / quality-and-tests (pull_request) Successful in 3m27s
Le formulaire 'fire-and-forget' devient un vrai systeme de suivi : - table locale tickets (user <-> numero d'issue Gitea) - liste 'Mes tickets' avec etat (En cours / Resolu) et jalon - page de suivi : etat, labels, jalon, fil de discussion (commentaires) - reponse depuis le web (ajoute un commentaire sur l'issue Gitea) - controle d'acces : un utilisateur ne voit que ses propres tickets - GiteaIssueService etendu (getIssue, getComments, addComment) - remplace l'ancien SuggestionController ; 5 tests de bout en bout Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use App\Services\GiteaIssueService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class TicketController extends Controller
|
||||
{
|
||||
public function __construct(private GiteaIssueService $gitea) {}
|
||||
|
||||
/**
|
||||
* Liste des tickets de l'utilisateur, avec leur état courant récupéré
|
||||
* depuis Gitea (ouvert / résolu, jalon).
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
$tickets = $user->tickets()->latest()->get()->map(function (Ticket $ticket) {
|
||||
$issue = $this->gitea->getIssue($ticket->gitea_number);
|
||||
$ticket->setAttribute('state', $issue['state'] ?? null);
|
||||
$ticket->setAttribute('milestone', $issue['milestone']['title'] ?? null);
|
||||
|
||||
return $ticket;
|
||||
});
|
||||
|
||||
return view('tickets.index', compact('tickets'));
|
||||
}
|
||||
|
||||
// Formulaire de création
|
||||
public function create()
|
||||
{
|
||||
return view('tickets.create');
|
||||
}
|
||||
|
||||
// Création d'un ticket -> issue Gitea + enregistrement local
|
||||
public function store(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'type' => ['required', Rule::in(['Modification', 'Ajout', 'Correction'])],
|
||||
'titre' => ['required', 'string', 'min:5', 'max:120'],
|
||||
'description' => ['required', 'string', 'min:10', 'max:5000'],
|
||||
]);
|
||||
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
$auteur = $user->name ?? 'Utilisateur';
|
||||
|
||||
$title = "[{$data['type']}] {$data['titre']}";
|
||||
$body = "**Type de demande :** {$data['type']}\n"
|
||||
."**Proposé par :** {$auteur} (via le site CESIZen)\n\n"
|
||||
."### Description\n"
|
||||
.$data['description']."\n\n"
|
||||
."---\n"
|
||||
.'_Ticket créé automatiquement depuis CESIZen._';
|
||||
|
||||
$issue = $this->gitea->createIssue($title, $body);
|
||||
|
||||
if (! $issue || ! isset($issue['number'])) {
|
||||
return back()->withInput()->withErrors([
|
||||
'gitea' => "L'envoi de votre demande a échoué. Merci de réessayer plus tard.",
|
||||
]);
|
||||
}
|
||||
|
||||
// On mémorise le lien utilisateur <-> issue pour le suivi.
|
||||
$user->tickets()->create([
|
||||
'gitea_number' => (int) $issue['number'],
|
||||
'title' => $data['titre'],
|
||||
'type' => $data['type'],
|
||||
]);
|
||||
|
||||
return redirect()->route('tickets.show', $issue['number'])
|
||||
->with('success', 'Votre demande a bien été transmise à l\'équipe.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Détail d'un ticket : état, jalon, labels et fil de discussion.
|
||||
*/
|
||||
public function show(int $number)
|
||||
{
|
||||
$ticket = $this->findOwnedTicket($number);
|
||||
|
||||
$issue = $this->gitea->getIssue($number);
|
||||
$comments = $this->gitea->getComments($number);
|
||||
|
||||
return view('tickets.show', compact('ticket', 'issue', 'comments'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Réponse de l'utilisateur : ajoute un commentaire sur l'issue Gitea.
|
||||
*/
|
||||
public function reply(Request $request, int $number)
|
||||
{
|
||||
$this->findOwnedTicket($number);
|
||||
|
||||
$data = $request->validate([
|
||||
'message' => ['required', 'string', 'min:2', 'max:5000'],
|
||||
]);
|
||||
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
$auteur = $user->name ?? 'Utilisateur';
|
||||
$body = "**{$auteur}** (via CESIZen) :\n\n".$data['message'];
|
||||
|
||||
if (! $this->gitea->addComment($number, $body)) {
|
||||
return back()->withErrors(['gitea' => "L'envoi de votre réponse a échoué."]);
|
||||
}
|
||||
|
||||
return redirect()->route('tickets.show', $number)
|
||||
->with('success', 'Votre réponse a été ajoutée au ticket.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le ticket s'il appartient bien à l'utilisateur connecté,
|
||||
* sinon renvoie une 404 (empêche de consulter les tickets d'autrui).
|
||||
*/
|
||||
private function findOwnedTicket(int $number): Ticket
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
return $user->tickets()->where('gitea_number', $number)->firstOrFail();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user