feat(tickets): affiche le créateur d'un ticket et filtre la liste par créateur
createdBy était déjà présent dans le frontmatter des tickets mais ni exposé ni exploitable côté UI. Ajout du DTO (domain/ports/mock), affichage dans TicketDetail, colonne/filtre créateur dans TicketsPanel, et persistance du filtre. ticketActor.ts introduit pour porter la logique de filtrage côté frontend, sans changement backend nécessaire (lot frontend pur). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -17,6 +17,11 @@ import { SprintManager } from "./SprintManager";
|
||||
import { SprintPicker } from "./SprintPicker";
|
||||
import { TicketFacetsBar } from "./TicketFacetsBar";
|
||||
import { TicketViewportSelect } from "./TicketViewportSelect";
|
||||
import {
|
||||
creatorFilterFromValue,
|
||||
creatorFilterValue,
|
||||
ticketActorLabel,
|
||||
} from "./ticketActor";
|
||||
import {
|
||||
PriorityBadge,
|
||||
StatusBadge,
|
||||
@ -37,17 +42,26 @@ export function TicketsPanel({ projectId, onOpen }: TicketsPanelProps) {
|
||||
const vm = useTickets(projectId);
|
||||
const { agents, nameOf, loaded: agentsLoaded } = useProjectAgents(projectId);
|
||||
|
||||
// Reconcile a *restored* assignee filter (ticket #29) against the live roster:
|
||||
// once the agents are known, an assignee that no longer exists is dropped
|
||||
// (which also clears it from persisted storage via `useTickets`). Guarded on
|
||||
// `agentsLoaded` so a still-valid assignee is never cleared during the load
|
||||
// window when the roster is momentarily empty.
|
||||
// Reconcile *restored* agent-scoped filters (ticket #29/#109) against the live
|
||||
// roster: once agents are known, stale ids are dropped and persisted storage
|
||||
// follows via `useTickets`. Guarded on `agentsLoaded` so a still-valid agent is
|
||||
// never cleared during the load window when the roster is momentarily empty.
|
||||
useEffect(() => {
|
||||
if (!agentsLoaded) return;
|
||||
const id = vm.query.assignedAgentId;
|
||||
if (id && !agents.some((a) => a.id === id)) {
|
||||
const { assignedAgentId: _drop, ...rest } = vm.query;
|
||||
vm.setQuery(rest);
|
||||
const validAgentIds = new Set(agents.map((a) => a.id));
|
||||
const staleAssignee =
|
||||
vm.query.assignedAgentId && !validAgentIds.has(vm.query.assignedAgentId);
|
||||
const staleCreator =
|
||||
vm.query.createdBy?.kind === "agent" &&
|
||||
!validAgentIds.has(vm.query.createdBy.agentId);
|
||||
if (staleAssignee || staleCreator) {
|
||||
const { assignedAgentId: _dropAssignee, createdBy: _dropCreator, ...rest } =
|
||||
vm.query;
|
||||
vm.setQuery({
|
||||
...rest,
|
||||
...(staleAssignee ? {} : { assignedAgentId: vm.query.assignedAgentId }),
|
||||
...(staleCreator ? {} : { createdBy: vm.query.createdBy }),
|
||||
});
|
||||
}
|
||||
}, [agentsLoaded, agents, vm.query, vm.setQuery]);
|
||||
const [showCreate, setShowCreate] = useState(false);
|
||||
@ -305,6 +319,26 @@ export function TicketsPanel({ projectId, onOpen }: TicketsPanelProps) {
|
||||
})
|
||||
}
|
||||
/>
|
||||
<TicketViewportSelect
|
||||
aria-label="filter by creator"
|
||||
value={creatorFilterValue(vm.query.createdBy)}
|
||||
options={[
|
||||
{ value: "", label: "Tous créateurs" },
|
||||
{ value: "user", label: "Utilisateur" },
|
||||
...agents.map((agent) => ({
|
||||
value: `agent:${agent.id}`,
|
||||
label: agent.name,
|
||||
})),
|
||||
]}
|
||||
onChange={(next) => {
|
||||
const { createdBy: _drop, ...rest } = vm.query;
|
||||
const createdBy = creatorFilterFromValue(next);
|
||||
vm.setQuery({
|
||||
...rest,
|
||||
...(createdBy ? { createdBy } : {}),
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -485,6 +519,9 @@ function SprintSection({
|
||||
{t.assignedAgentIds.map(nameOf).join(", ")}
|
||||
</span>
|
||||
)}
|
||||
<span className="mt-0.5 block truncate text-xs text-muted">
|
||||
Créé par : {ticketActorLabel(t.createdBy, nameOf)}
|
||||
</span>
|
||||
</button>
|
||||
<span className="flex shrink-0 flex-col items-end gap-1">
|
||||
<StatusBadge status={t.status} />
|
||||
|
||||
Reference in New Issue
Block a user