From bd73fd79bd3863b8df968f7f5dd18ef37d889a1d Mon Sep 17 00:00:00 2001 From: Blomios Date: Tue, 7 Jul 2026 11:54:28 +0200 Subject: [PATCH] =?UTF-8?q?feat(tickets):=20contr=C3=B4le=20=C2=AB=20Trier?= =?UTF-8?q?=20par=E2=80=A6=20=C2=BB=20dans=20la=20barre=20de=20facettes=20?= =?UTF-8?q?(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajoute le sélecteur de tri dans TicketFacetsBar et propage le critère via useTicketSearch jusqu'à TicketsPanel et TicketPicker, sur le contrat de tri backend (e523f44). Couvre le tri par les tests tickets et TicketPicker. Co-Authored-By: Claude Opus 4.8 --- .../src/features/tickets/TicketFacetsBar.tsx | 74 ++++++++++++++++++- .../features/tickets/TicketPicker.test.tsx | 43 +++++++++++ .../src/features/tickets/TicketPicker.tsx | 2 + .../src/features/tickets/TicketsPanel.tsx | 5 ++ .../src/features/tickets/tickets.test.tsx | 42 +++++++++++ .../src/features/tickets/useTicketSearch.ts | 18 ++++- 6 files changed, 179 insertions(+), 5 deletions(-) diff --git a/frontend/src/features/tickets/TicketFacetsBar.tsx b/frontend/src/features/tickets/TicketFacetsBar.tsx index f09811e..9ef4608 100644 --- a/frontend/src/features/tickets/TicketFacetsBar.tsx +++ b/frontend/src/features/tickets/TicketFacetsBar.tsx @@ -14,7 +14,8 @@ */ import type { TicketPriority, TicketStatus } from "@/domain"; -import { Button, Input } from "@/shared"; +import type { TicketListSort, TicketListSortField } from "@/ports"; +import { Button, Input, cn } from "@/shared"; import { PriorityBadge, StatusBadge, @@ -24,6 +25,23 @@ import { statusLabel, } from "./ticketMeta"; +/** Styling for the sort-field select, matching the panel's other selects. */ +const selectClass = cn( + "h-8 rounded-md bg-raised px-2 text-xs text-content", + "border border-border outline-none transition-colors", + "focus:border-primary disabled:cursor-not-allowed disabled:opacity-50", +); + +/** Human labels for the sort fields (also the option text). */ +const SORT_FIELD_LABEL: Record = { + number: "Numéro", + priority: "Priorité", + status: "Statut", + title: "Titre", +}; + +const SORT_FIELDS = Object.keys(SORT_FIELD_LABEL) as TicketListSortField[]; + export interface TicketFacetsBarProps { /** Current free-text search value (controlled). */ text: string; @@ -42,6 +60,13 @@ export interface TicketFacetsBarProps { onClearFacets?: () => void; /** Placeholder for the search input. */ searchPlaceholder?: string; + /** + * Current sort (ticket #21). `undefined` ⇒ the backend's historical order. + * When {@link onSortChange} is provided, a « Trier par… » control appears. + */ + sort?: TicketListSort; + /** Emitted with the next sort, or `undefined` to restore the default order. */ + onSortChange?: (sort: TicketListSort | undefined) => void; } export function TicketFacetsBar({ @@ -53,6 +78,8 @@ export function TicketFacetsBar({ onTogglePriority, onClearFacets, searchPlaceholder = "Search title/description…", + sort, + onSortChange, }: TicketFacetsBarProps) { const hasFacets = statuses.length > 0 || priorities.length > 0; return ( @@ -109,6 +136,51 @@ export function TicketFacetsBar({ ))} + {/* Sort control (#21): field choice + asc/desc toggle. Absent field ⇒ + the backend keeps its historical order. Grouping-by-sprint stays + client-side; this sort applies intra-group. */} + {onSortChange && ( +
+ + {sort && ( + + )} +
+ )} {onClearFacets && hasFacets && (
diff --git a/frontend/src/features/tickets/TicketsPanel.tsx b/frontend/src/features/tickets/TicketsPanel.tsx index 65b0d19..abff0a8 100644 --- a/frontend/src/features/tickets/TicketsPanel.tsx +++ b/frontend/src/features/tickets/TicketsPanel.tsx @@ -163,6 +163,11 @@ export function TicketsPanel({ projectId, onOpen }: TicketsPanelProps) { onToggleStatus={vm.toggleStatus} onTogglePriority={vm.togglePriority} onClearFacets={vm.clearFacets} + sort={vm.query.sort} + onSortChange={(sort) => { + const { sort: _drop, ...rest } = vm.query; + vm.setQuery(sort ? { ...rest, sort } : rest); + }} />