feat(tickets): persistance des filtres tickets entre redémarrages (#29)

Introduit le port UiPreferencesGateway et son adapter uiPreferences,
avec le module ticketFilterPersistence qui sauvegarde/restaure les
filtres (recherche, statut, sprint, agents) via useTickets,
useTicketSearch et useProjectAgents. Couvert par tests unitaires et
un test d'intégration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 07:45:50 +02:00
parent 9ee7290cde
commit 79f06c26d9
13 changed files with 793 additions and 20 deletions

View File

@ -7,7 +7,7 @@
* through {@link useTickets}.
*/
import { useState } from "react";
import { useEffect, useState } from "react";
import type { Sprint, TicketPriority, TicketSummary } from "@/domain";
import { Button, Input, Panel, Spinner, cn } from "@/shared";
@ -38,7 +38,21 @@ export interface TicketsPanelProps {
export function TicketsPanel({ projectId, onOpen }: TicketsPanelProps) {
const vm = useTickets(projectId);
const { agents, nameOf } = useProjectAgents(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.
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);
}
}, [agentsLoaded, agents, vm.query, vm.setQuery]);
const [showCreate, setShowCreate] = useState(false);
const [showSprints, setShowSprints] = useState(false);
const [newTitle, setNewTitle] = useState("");