@@ -37,17 +37,47 @@ impl App {
3737
3838 pub fn handle_card_selection_toggle ( & mut self ) {
3939 if self . focus == Focus :: Cards {
40- if let Some ( card) = self . get_selected_card_in_context ( ) {
41- let card_id = card. id ;
42- if self . selected_cards . contains ( & card_id) {
43- self . selected_cards . remove ( & card_id) ;
44- } else {
45- self . selected_cards . insert ( card_id) ;
40+ if self . selection_mode_active {
41+ // Exit selection mode (keep selections)
42+ self . selection_mode_active = false ;
43+ } else {
44+ // Enter selection mode and select current card
45+ self . selection_mode_active = true ;
46+ if let Some ( card) = self . get_selected_card_in_context ( ) {
47+ self . selected_cards . insert ( card. id ) ;
4648 }
4749 }
4850 }
4951 }
5052
53+ pub fn handle_clear_card_selection ( & mut self ) {
54+ self . selected_cards . clear ( ) ;
55+ }
56+
57+ pub fn handle_select_all_cards_in_view ( & mut self ) {
58+ if self . focus != Focus :: Cards {
59+ return ;
60+ }
61+
62+ if let Some ( task_list) = self . view_strategy . get_active_task_list ( ) {
63+ for card_id in & task_list. cards {
64+ self . selected_cards . insert ( * card_id) ;
65+ }
66+ if !task_list. cards . is_empty ( ) {
67+ self . selection_mode_active = true ;
68+ }
69+ }
70+ }
71+
72+ pub fn handle_set_selected_cards_priority ( & mut self ) {
73+ if self . focus != Focus :: Cards || self . selected_cards . is_empty ( ) {
74+ return ;
75+ }
76+
77+ self . priority_selection . set ( Some ( 0 ) ) ;
78+ self . open_dialog ( DialogMode :: SetMultipleCardsPriority ) ;
79+ }
80+
5181 pub fn handle_assign_to_sprint_key ( & mut self ) {
5282 if self . focus != Focus :: Cards {
5383 return ;
@@ -273,6 +303,7 @@ impl App {
273303
274304 tracing:: info!( "Toggled {} cards completion status" , toggled_count) ;
275305 self . selected_cards . clear ( ) ;
306+ self . selection_mode_active = false ;
276307 self . refresh_view ( ) ;
277308 if let Some ( card_id) = first_card_id {
278309 self . select_card_by_id ( card_id) ;
@@ -396,6 +427,11 @@ impl App {
396427 return ;
397428 }
398429
430+ if !self . selected_cards . is_empty ( ) {
431+ self . move_selected_cards ( direction) ;
432+ return ;
433+ }
434+
399435 if let Some ( card) = self . get_selected_card_in_context ( ) {
400436 let board = self
401437 . active_board_index
@@ -479,6 +515,78 @@ impl App {
479515 }
480516 }
481517
518+ fn move_selected_cards ( & mut self , direction : kanban_domain:: card_lifecycle:: MoveDirection ) {
519+ let board = self
520+ . active_board_index
521+ . and_then ( |idx| self . ctx . boards . get ( idx) ) ;
522+ let board = match board {
523+ Some ( b) => b,
524+ None => return ,
525+ } ;
526+
527+ let card_ids: Vec < uuid:: Uuid > = self . selected_cards . iter ( ) . copied ( ) . collect ( ) ;
528+ let first_card_id = card_ids. first ( ) . copied ( ) ;
529+ let mut commands: Vec < Box < dyn kanban_domain:: commands:: Command > > = Vec :: new ( ) ;
530+ let mut moved_count = 0 ;
531+
532+ for card_id in & card_ids {
533+ let card = match self . ctx . cards . iter ( ) . find ( |c| c. id == * card_id) {
534+ Some ( c) => c,
535+ None => continue ,
536+ } ;
537+
538+ let move_result = kanban_domain:: card_lifecycle:: compute_card_column_move (
539+ card,
540+ board,
541+ & self . ctx . columns ,
542+ & self . ctx . cards ,
543+ direction,
544+ ) ;
545+
546+ let move_result = match move_result {
547+ Some ( r) => r,
548+ None => continue ,
549+ } ;
550+
551+ commands. push ( Box :: new ( MoveCard {
552+ card_id : * card_id,
553+ new_column_id : move_result. target_column_id ,
554+ new_position : move_result. new_position ,
555+ } ) ) ;
556+
557+ if let Some ( new_status) = move_result. new_status {
558+ commands. push ( Box :: new ( UpdateCard {
559+ card_id : * card_id,
560+ updates : CardUpdate {
561+ status : Some ( new_status) ,
562+ ..Default :: default ( )
563+ } ,
564+ } ) ) ;
565+ }
566+
567+ moved_count += 1 ;
568+ }
569+
570+ if !commands. is_empty ( ) {
571+ if let Err ( e) = self . execute_commands_batch ( commands) {
572+ let dir = match direction {
573+ kanban_domain:: card_lifecycle:: MoveDirection :: Left => "left" ,
574+ kanban_domain:: card_lifecycle:: MoveDirection :: Right => "right" ,
575+ } ;
576+ tracing:: error!( "Failed to move cards {}: {}" , dir, e) ;
577+ return ;
578+ }
579+ }
580+
581+ tracing:: info!( "Moved {} cards" , moved_count) ;
582+ self . selected_cards . clear ( ) ;
583+ self . selection_mode_active = false ;
584+ self . refresh_view ( ) ;
585+ if let Some ( card_id) = first_card_id {
586+ self . select_card_by_id ( card_id) ;
587+ }
588+ }
589+
482590 pub fn handle_archive_card ( & mut self ) {
483591 if self . focus != Focus :: Cards {
484592 return ;
@@ -497,6 +605,7 @@ impl App {
497605 self . start_delete_animation ( card_id) ;
498606 }
499607 self . selected_cards . clear ( ) ;
608+ self . selection_mode_active = false ;
500609 }
501610
502611 fn start_delete_animation ( & mut self , card_id : uuid:: Uuid ) {
@@ -563,6 +672,7 @@ impl App {
563672 self . start_restore_animation ( card_id) ;
564673 }
565674 self . selected_cards . clear ( ) ;
675+ self . selection_mode_active = false ;
566676 }
567677
568678 fn start_restore_animation ( & mut self , card_id : uuid:: Uuid ) {
@@ -639,6 +749,7 @@ impl App {
639749 self . start_permanent_delete_animation ( card_id) ;
640750 }
641751 self . selected_cards . clear ( ) ;
752+ self . selection_mode_active = false ;
642753 }
643754
644755 fn start_permanent_delete_animation ( & mut self , card_id : uuid:: Uuid ) {
0 commit comments