feat(tickets): surface frontend V1 du système de tickets
Surface UI complète des tickets (domaine Issue exposé « ticket »), validée QA de bout en bout : cargo build + tests backend verts, npm run build + npm test (459) verts. - Domaine : DTO Ticket, 9 events Issue* + guard isTicketEvent. - Ports : TicketGateway (+ types query/input) ajouté à Gateways. - Adapters : TauriTicketGateway réel (+ isTicketVersionConflict), wiring, et MockTicketGateway pour les tests. - Feature tickets : hooks (useTickets, useTicketDetail, useProjectAgents), TicketsPanel, TicketDetail, TicketsView, ticketMeta + tests. - ProjectsView : onglet sidebar « Tickets ». Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -104,8 +104,60 @@ export type DomainEvent =
|
||||
*/
|
||||
source?: "mcp" | "file";
|
||||
}
|
||||
| { type: "issueCreated"; issueId: string; issueRef: string }
|
||||
| { type: "issueUpdated"; issueRef: string; version: number }
|
||||
| {
|
||||
type: "issueStatusChanged";
|
||||
issueRef: string;
|
||||
status: TicketStatus;
|
||||
version: number;
|
||||
}
|
||||
| {
|
||||
type: "issuePriorityChanged";
|
||||
issueRef: string;
|
||||
priority: TicketPriority;
|
||||
version: number;
|
||||
}
|
||||
| { type: "issueCarnetUpdated"; issueRef: string; version: number }
|
||||
| {
|
||||
type: "issueLinked";
|
||||
issueRef: string;
|
||||
target: string;
|
||||
kind: TicketLinkKind;
|
||||
version: number;
|
||||
}
|
||||
| {
|
||||
type: "issueUnlinked";
|
||||
issueRef: string;
|
||||
target: string;
|
||||
kind: TicketLinkKind;
|
||||
version: number;
|
||||
}
|
||||
| {
|
||||
type: "issueAgentAssigned";
|
||||
issueRef: string;
|
||||
agentId: string;
|
||||
version: number;
|
||||
}
|
||||
| {
|
||||
type: "issueAgentUnassigned";
|
||||
issueRef: string;
|
||||
agentId: string;
|
||||
version: number;
|
||||
}
|
||||
| { type: "ptyOutput"; sessionId: string; bytes: number[] };
|
||||
|
||||
/**
|
||||
* Whether a domain event is one of the ticket (`Issue*`) events. A small helper
|
||||
* so ticket view-models refresh on any ticket mutation without re-enumerating
|
||||
* every variant at each call site.
|
||||
*/
|
||||
export function isTicketEvent(
|
||||
event: DomainEvent,
|
||||
): event is Extract<DomainEvent, { type: `issue${string}` }> {
|
||||
return event.type.startsWith("issue");
|
||||
}
|
||||
|
||||
/** Where a project physically lives (mirror of the backend `RemoteRef`). */
|
||||
export type RemoteRef =
|
||||
| { kind: "local" }
|
||||
@ -709,3 +761,84 @@ export interface GitBranches {
|
||||
branches: string[];
|
||||
current: string | null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tickets (public wire name for the backend `Issue` model — "ticket" is the
|
||||
// user-visible term; all commands/DTOs/events speak `ticket`/`issue`). Mirrors
|
||||
// the `Ticket*Dto` shapes in `crates/app-tauri/src/tickets.rs` (camelCase).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** A ticket reference on the wire, always `#<n>` (e.g. `"#42"`). */
|
||||
export type TicketRef = string;
|
||||
|
||||
/** Lifecycle status of a ticket. */
|
||||
export type TicketStatus = "open" | "inProgress" | "QA" | "closed";
|
||||
|
||||
/** Priority of a ticket. */
|
||||
export type TicketPriority = "low" | "medium" | "high" | "critical";
|
||||
|
||||
/** The kind of relationship between two tickets. */
|
||||
export type TicketLinkKind =
|
||||
| "relatesTo"
|
||||
| "blocks"
|
||||
| "blockedBy"
|
||||
| "duplicates"
|
||||
| "dependsOn";
|
||||
|
||||
/** Who performed a ticket mutation (mirror of `TicketActorDto`, tagged `kind`). */
|
||||
export type TicketActor =
|
||||
| { kind: "user" }
|
||||
| { kind: "agent"; agentId: string }
|
||||
| { kind: "system" };
|
||||
|
||||
/** One directed link from a ticket to another (mirror of `TicketLinkDto`). */
|
||||
export interface TicketLink {
|
||||
targetRef: TicketRef;
|
||||
kind: TicketLinkKind;
|
||||
}
|
||||
|
||||
/** The full ticket, as returned by `ticket_read`/`ticket_create`/… . */
|
||||
export interface Ticket {
|
||||
id: string;
|
||||
ref: TicketRef;
|
||||
number: number;
|
||||
title: string;
|
||||
description: string;
|
||||
status: TicketStatus;
|
||||
priority: TicketPriority;
|
||||
/** Present only when the ticket was read with `includeCarnet`. */
|
||||
carnet?: string;
|
||||
links: TicketLink[];
|
||||
assignedAgentIds: string[];
|
||||
createdBy: TicketActor;
|
||||
updatedBy: TicketActor;
|
||||
createdAt: number;
|
||||
updatedAt: number;
|
||||
/** Optimistic-concurrency version; echoed back as `expectedVersion` on writes. */
|
||||
version: number;
|
||||
}
|
||||
|
||||
/** A ticket summary row from `ticket_list` (mirror of `TicketSummaryDto`). */
|
||||
export interface TicketSummary {
|
||||
ref: TicketRef;
|
||||
path: string;
|
||||
title: string;
|
||||
status: TicketStatus;
|
||||
priority: TicketPriority;
|
||||
assignedAgentIds: string[];
|
||||
updatedAt: number;
|
||||
}
|
||||
|
||||
/** One page of ticket summaries (mirror of `TicketListDto`). */
|
||||
export interface TicketList {
|
||||
items: TicketSummary[];
|
||||
/** Opaque cursor for the next page, absent when the list is exhausted. */
|
||||
nextCursor?: string;
|
||||
}
|
||||
|
||||
/** The scoped-to-a-ticket Markdown carnet (mirror of `TicketCarnetDto`). */
|
||||
export interface TicketCarnet {
|
||||
ref: TicketRef;
|
||||
carnet: string;
|
||||
version: number;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user