/** * Reusable ticket filter/search bar (UI rework #18): a free-text search input * plus the multi-select status/priority checkbox facets. Extracted from * {@link TicketsPanel} so both the main listing and the {@link TicketPicker} * popup share one implementation and stay in sync. * * Purely controlled/presentational — it holds no state and knows nothing about * the gateway; the owner (a view-model or {@link useTicketSearch}) drives every * value and toggle. Facet semantics (ticket #12): OR within a facet, AND across; * an empty set ⇒ that facet is unconstrained. * * The assignee filter stays with the main listing (it needs the project's agent * roster and is out of the picker's scope), so it is intentionally not here. */ import type { TicketPriority, TicketStatus } from "@/domain"; import { Button, Input } from "@/shared"; import { PriorityBadge, StatusBadge, TICKET_PRIORITIES, TICKET_STATUSES, priorityLabel, statusLabel, } from "./ticketMeta"; export interface TicketFacetsBarProps { /** Current free-text search value (controlled). */ text: string; /** Emitted on every keystroke with the raw input value. */ onTextChange: (text: string) => void; /** Currently-selected status facet values. */ statuses: TicketStatus[]; /** Currently-selected priority facet values. */ priorities: TicketPriority[]; onToggleStatus: (status: TicketStatus) => void; onTogglePriority: (priority: TicketPriority) => void; /** * Clears both facets. When provided, a « Effacer » button appears while any * facet is active. Omit to hide the affordance (the caller clears elsewhere). */ onClearFacets?: () => void; /** Placeholder for the search input. */ searchPlaceholder?: string; } export function TicketFacetsBar({ text, onTextChange, statuses, priorities, onToggleStatus, onTogglePriority, onClearFacets, searchPlaceholder = "Search title/description…", }: TicketFacetsBarProps) { const hasFacets = statuses.length > 0 || priorities.length > 0; return (