2929from .data_manager import DataManager
3030from .formatters import ViewPresenter
3131from .logging_config import get_logger
32- from .state import AppState , SortDirection , SortMode , TimeFrame , TransactionEdit , ViewMode
32+ from .state import (
33+ AppState ,
34+ NavigationState ,
35+ SortDirection ,
36+ SortMode ,
37+ TimeFrame ,
38+ TransactionEdit ,
39+ ViewMode ,
40+ )
3341from .time_navigator import TimeNavigator
3442from .view_interface import IViewPresenter
3543
@@ -407,11 +415,7 @@ def _prepare_aggregate_view(self, view_mode: ViewMode):
407415 def switch_to_merchant_view (self ):
408416 """Switch to merchant aggregation view."""
409417 self .state .view_mode = ViewMode .MERCHANT
410- self .state .selected_merchant = None
411- self .state .selected_category = None
412- self .state .selected_group = None
413- self .state .selected_account = None
414- self .state .clear_selection () # Clear all multi-select
418+ self .state .clear_drill_down_and_selection ()
415419 # Reset sort to valid field for aggregate views (now includes field name)
416420 if self .state .sort_by not in [SortMode .MERCHANT , SortMode .COUNT , SortMode .AMOUNT ]:
417421 self .state .sort_by = SortMode .AMOUNT
@@ -420,35 +424,23 @@ def switch_to_merchant_view(self):
420424 def switch_to_category_view (self ):
421425 """Switch to category aggregation view."""
422426 self .state .view_mode = ViewMode .CATEGORY
423- self .state .selected_merchant = None
424- self .state .selected_category = None
425- self .state .selected_group = None
426- self .state .selected_account = None
427- self .state .clear_selection () # Clear all multi-select
427+ self .state .clear_drill_down_and_selection ()
428428 if self .state .sort_by not in [SortMode .CATEGORY , SortMode .COUNT , SortMode .AMOUNT ]:
429429 self .state .sort_by = SortMode .AMOUNT
430430 self .refresh_view ()
431431
432432 def switch_to_group_view (self ):
433433 """Switch to group aggregation view."""
434434 self .state .view_mode = ViewMode .GROUP
435- self .state .selected_merchant = None
436- self .state .selected_category = None
437- self .state .selected_group = None
438- self .state .selected_account = None
439- self .state .clear_selection () # Clear all multi-select
435+ self .state .clear_drill_down_and_selection ()
440436 if self .state .sort_by not in [SortMode .GROUP , SortMode .COUNT , SortMode .AMOUNT ]:
441437 self .state .sort_by = SortMode .AMOUNT
442438 self .refresh_view ()
443439
444440 def switch_to_account_view (self ):
445441 """Switch to account aggregation view."""
446442 self .state .view_mode = ViewMode .ACCOUNT
447- self .state .selected_merchant = None
448- self .state .selected_category = None
449- self .state .selected_group = None
450- self .state .selected_account = None
451- self .state .clear_selection () # Clear all multi-select
443+ self .state .clear_drill_down_and_selection ()
452444 if self .state .sort_by not in [SortMode .ACCOUNT , SortMode .COUNT , SortMode .AMOUNT ]:
453445 self .state .sort_by = SortMode .AMOUNT
454446 self .refresh_view ()
@@ -457,15 +449,37 @@ def switch_to_detail_view(self, set_default_sort: bool = True):
457449 """
458450 Switch to transaction detail view (ungrouped).
459451
452+ Saves current state to navigation history if switching from an aggregate view,
453+ so that pressing Esc or 'g' can restore the previous view.
454+
460455 Args:
461456 set_default_sort: If True, set default sort (Date descending)
462457 """
458+ # Save current state to navigation history if we're in an aggregate view
459+ # This allows Esc/'g' to return to the correct aggregate view
460+ if self .state .view_mode in [
461+ ViewMode .MERCHANT ,
462+ ViewMode .CATEGORY ,
463+ ViewMode .GROUP ,
464+ ViewMode .ACCOUNT ,
465+ ]:
466+ self .state .navigation_history .append (
467+ NavigationState (
468+ view_mode = self .state .view_mode ,
469+ cursor_position = 0 , # Don't preserve cursor when switching with 'd'
470+ scroll_y = 0.0 ,
471+ sort_by = self .state .sort_by ,
472+ sort_direction = self .state .sort_direction ,
473+ selected_merchant = self .state .selected_merchant ,
474+ selected_category = self .state .selected_category ,
475+ selected_group = self .state .selected_group ,
476+ selected_account = self .state .selected_account ,
477+ sub_grouping_mode = self .state .sub_grouping_mode ,
478+ )
479+ )
480+
463481 self .state .view_mode = ViewMode .DETAIL
464- self .state .selected_merchant = None
465- self .state .selected_category = None
466- self .state .selected_group = None
467- self .state .selected_account = None
468- self .state .clear_selection () # Clear all multi-select
482+ self .state .clear_drill_down_and_selection ()
469483 if set_default_sort :
470484 self .state .sort_by = SortMode .DATE
471485 self .state .sort_direction = SortDirection .DESC
@@ -881,7 +895,7 @@ def _get_action_hints(self) -> str:
881895 ):
882896 return "Esc/g=Back | m=✏️ Merchant | c=✏️ Category | h=Hide | x=Delete | Space=Select | Ctrl-A=SelectAll"
883897 else :
884- return "g=Group | m=✏️ Merchant | c=✏️ Category | h=Hide | x=Delete | Space=Select | Ctrl-A=SelectAll"
898+ return "Esc/ g=Group | m=✏️ Merchant | c=✏️ Category | h=Hide | x=Delete | Space=Select | Ctrl-A=SelectAll"
885899
886900 # Edit Orchestration Methods
887901
0 commit comments