feat(tickets): multi-sélection dans TicketPicker (#41)

Le TicketPicker permet la sélection multiple de tickets ; SprintManager
consomme la sélection multiple. Frontend-pur.

QA vert : tsc --noEmit exit 0, vitest 62 fichiers / 620 tests passés.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-12 19:45:39 +02:00
parent 61e5e41f0d
commit daa93525d7
4 changed files with 245 additions and 61 deletions

View File

@ -209,8 +209,8 @@ export function SprintManager({ projectId, vm, onClose }: SprintManagerProps) {
</ul>
)}
{/* Add an existing ticket via the TicketPicker popup (#19):
single-select, excluding tickets already in this sprint. */}
{/* Add existing tickets via the TicketPicker popup (#19/#41):
multi-select, excluding tickets already in this sprint. */}
<div className="mt-1">
<Button
size="sm"
@ -223,7 +223,7 @@ export function SprintManager({ projectId, vm, onClose }: SprintManagerProps) {
>
{addable.length === 0
? "No tickets to add"
: "Ajouter un ticket"}
: "Ajouter des tickets"}
</Button>
</div>
</div>
@ -278,24 +278,28 @@ export function SprintManager({ projectId, vm, onClose }: SprintManagerProps) {
</div>
)}
{/* ── Add-ticket picker (#19) — mounts at floatingWindowNested (60), above
this sprint dialog. Excludes tickets already in the target sprint. ── */}
{/* ── Add-ticket picker (#19/#41) — mounts at floatingWindowNested (60),
above this sprint dialog. Multi-select so several tickets can be added
in one pass; excludes tickets already in the target sprint. ── */}
<TicketPicker
open={pickerSprint !== null}
projectId={projectId}
title={
pickerSprint
? `Ajouter un ticket au sprint « ${pickerSprint.name} »`
: "Ajouter un ticket"
? `Ajouter des tickets au sprint « ${pickerSprint.name} »`
: "Ajouter des tickets"
}
selectionMode="single"
confirmLabel="Ajouter au sprint"
selectionMode="multi"
excludeRefs={items
.filter((t) => t.sprintId === pickerSprint?.id)
.map((t) => t.ref)}
onSelect={(result) => {
const picked = Array.isArray(result) ? result[0] : result;
if (picked && pickerSprint) {
void vm.assignSprint(picked.ref, pickerSprint.id);
const picked = Array.isArray(result) ? result : [result];
if (pickerSprint) {
for (const p of picked) {
void vm.assignSprint(p.ref, pickerSprint.id);
}
}
}}
onClose={() => setPickerSprint(null)}