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:
@ -31,6 +31,7 @@ import type { TicketRef } from "@/domain";
|
||||
import type { TicketListQuery } from "@/ports";
|
||||
import { Button, Spinner, cn, zIndex } from "@/shared";
|
||||
import { TicketFacetsBar } from "./TicketFacetsBar";
|
||||
import { pickerFiltersKey } from "./ticketFilterPersistence";
|
||||
import { PriorityBadge, StatusBadge, TicketRef as TicketRefBadge } from "./ticketMeta";
|
||||
import {
|
||||
useTicketSearch,
|
||||
@ -55,6 +56,13 @@ export interface TicketPickerProps {
|
||||
excludeRefs?: TicketRef[];
|
||||
/** Seed filter/search query. */
|
||||
initialQuery?: TicketListQuery;
|
||||
/**
|
||||
* Whether the picker persists its filters across openings (ticket #29,
|
||||
* default `true`). `initialQuery` still wins per field, so a caller imposing a
|
||||
* business filter keeps priority; a caller that must never inherit stored
|
||||
* preferences can opt out entirely with `false`.
|
||||
*/
|
||||
persistFilters?: boolean;
|
||||
/**
|
||||
* Selection callback. In `"single"` mode it receives one
|
||||
* {@link TicketPickerResult}; in `"multi"` it receives the array.
|
||||
@ -76,6 +84,7 @@ export function TicketPicker({
|
||||
selectionMode = "single",
|
||||
excludeRefs,
|
||||
initialQuery,
|
||||
persistFilters = true,
|
||||
onSelect,
|
||||
onClose,
|
||||
}: TicketPickerProps) {
|
||||
@ -88,6 +97,7 @@ export function TicketPicker({
|
||||
selectionMode={selectionMode}
|
||||
excludeRefs={excludeRefs}
|
||||
initialQuery={initialQuery}
|
||||
persistFilters={persistFilters}
|
||||
onSelect={onSelect}
|
||||
onClose={onClose}
|
||||
/>
|
||||
@ -106,16 +116,27 @@ function TicketPickerBody({
|
||||
selectionMode,
|
||||
excludeRefs,
|
||||
initialQuery,
|
||||
persistFilters,
|
||||
onSelect,
|
||||
onClose,
|
||||
}: Required<
|
||||
Pick<
|
||||
TicketPickerProps,
|
||||
"projectId" | "title" | "confirmLabel" | "selectionMode" | "onSelect" | "onClose"
|
||||
| "projectId"
|
||||
| "title"
|
||||
| "confirmLabel"
|
||||
| "selectionMode"
|
||||
| "persistFilters"
|
||||
| "onSelect"
|
||||
| "onClose"
|
||||
>
|
||||
> &
|
||||
Pick<TicketPickerProps, "excludeRefs" | "initialQuery">) {
|
||||
const vm = useTicketSearch(projectId, { initialQuery, excludeRefs });
|
||||
const vm = useTicketSearch(projectId, {
|
||||
initialQuery,
|
||||
excludeRefs,
|
||||
persistenceKey: persistFilters ? pickerFiltersKey(projectId) : undefined,
|
||||
});
|
||||
const dialogRef = useRef<HTMLDivElement>(null);
|
||||
const [selecting, setSelecting] = useState(false);
|
||||
const [resolveError, setResolveError] = useState<string | null>(null);
|
||||
|
||||
Reference in New Issue
Block a user