feat(tickets): suppression d'un ticket (backend + frontend)

Ajoute la suppression complète d'un ticket (#6).

Backend Rust :
- port IssueStore::delete (NotFound si absent) + event IssueDeleted{freed_sprint}
- FsIssueStore::delete : supprime .ideai/tickets/<N>/ et l'index sous lock
- use case DeleteIssue + adaptations des tests sprint/ticket_assistant au port
- commande Tauri ticket_delete + câblage events/state/lib

Frontend :
- gateway delete (ports, adapter ticket + mock, domain)
- useTicketDetail : retrait de la liste et fermeture du détail via event issueDeleted
- intégration TicketDetail + tests

Tests verts : application/issue_usecases (6), infrastructure/issue_store (7),
app-tauri --lib (56), frontend vitest (503), npm run build (exit 0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 06:27:09 +02:00
parent d25e340b44
commit 5417bd756b
20 changed files with 689 additions and 95 deletions

View File

@ -2012,6 +2012,22 @@ export class MockTicketGateway implements TicketGateway {
return structuredClone(ticket);
}
async delete(projectId: string, ref: string): Promise<void> {
// NotFound if absent (mirrors the backend `AppError::NotFound`).
const ticket = this.require(projectId, ref);
const freedSprint = ticket.sprintId ?? null;
this.projectTickets(projectId).delete(ref);
this.projectCarnets(projectId).delete(ref);
// Releasing the ticket updates its former sprint's count.
if (freedSprint) this.recountSprints(projectId);
this.system?.emit({
type: "issueDeleted",
projectId,
issueRef: ref,
freedSprint,
});
}
async readCarnet(projectId: string, ref: string): Promise<TicketCarnet> {
const ticket = this.require(projectId, ref);
return {

View File

@ -73,6 +73,15 @@ export class TauriTicketGateway implements TicketGateway {
});
}
async delete(projectId: string, ref: string): Promise<void> {
// `ticket_delete` takes the shared `{ request }` envelope like every other
// ticket command; a missing ref surfaces as a `NOT_FOUND` GatewayError from
// the backend, propagated as-is.
await invoke<void>("ticket_delete", {
request: { projectId, ref },
});
}
async readCarnet(projectId: string, ref: string): Promise<TicketCarnet> {
return invoke<TicketCarnet>("ticket_read_carnet", {
request: { projectId, ref },