Enhance status messages and layout in HostsManagerApp; improve error handling and testing for status updates
This commit is contained in:
		
							parent
							
								
									02423fe4f2
								
							
						
					
					
						commit
						d477328bea
					
				
					 3 changed files with 56 additions and 20 deletions
				
			
		| 
						 | 
				
			
			@ -58,6 +58,8 @@
 | 
			
		|||
- ✅ **Safe file operations**: Atomic file writing with rollback capability
 | 
			
		||||
- ✅ **Manager module**: Complete HostsManager class for edit operations
 | 
			
		||||
- ✅ **Error handling**: Comprehensive error handling with user feedback
 | 
			
		||||
- ✅ **Enhanced read-only error messages**: Clear, informative error messages with ❌ indicator and helpful instructions
 | 
			
		||||
- ✅ **Status bar positioning**: Fixed status bar layout to appear above footer for maximum visibility
 | 
			
		||||
- ✅ **Keyboard shortcuts**: All edit mode shortcuts implemented and tested
 | 
			
		||||
- ✅ **Live testing**: Manual testing confirms all functionality works correctly
 | 
			
		||||
- ✅ **Human-readable formatting**: Tab-based column alignment with proper spacing
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +111,8 @@
 | 
			
		|||
5. ✅ **Entry reordering**: Move entries up/down with Ctrl+Up/Down keyboard shortcuts
 | 
			
		||||
6. ✅ **Manager module**: Complete HostsManager class for all edit operations
 | 
			
		||||
7. ✅ **Safe file operations**: Atomic file writing with rollback capability
 | 
			
		||||
8. ✅ **Comprehensive testing**: 38 new tests for manager module (135 total tests)
 | 
			
		||||
8. ✅ **Enhanced error messages**: Professional read-only mode error messages with clear instructions
 | 
			
		||||
9. ✅ **Comprehensive testing**: 38 new tests for manager module (135 total tests)
 | 
			
		||||
 | 
			
		||||
### Recent Major Accomplishments
 | 
			
		||||
- ✅ **Complete Phase 3 implementation**: Full edit mode foundation with permission management
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ class HostsManagerApp(App):
 | 
			
		|||
    
 | 
			
		||||
    CSS = """
 | 
			
		||||
    .hosts-container {
 | 
			
		||||
        height: 100%;
 | 
			
		||||
        height: 1fr;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .left-pane {
 | 
			
		||||
| 
						 | 
				
			
			@ -59,6 +59,16 @@ class HostsManagerApp(App):
 | 
			
		|||
        color: $text;
 | 
			
		||||
        height: 1;
 | 
			
		||||
        padding: 0 1;
 | 
			
		||||
        dock: bottom;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .status-error {
 | 
			
		||||
        background: $error;
 | 
			
		||||
        color: $text;
 | 
			
		||||
        height: 1;
 | 
			
		||||
        padding: 0 1;
 | 
			
		||||
        text-style: bold;
 | 
			
		||||
        dock: bottom;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /* DataTable styling to match background */
 | 
			
		||||
| 
						 | 
				
			
			@ -115,20 +125,22 @@ class HostsManagerApp(App):
 | 
			
		|||
        """Create the application layout."""
 | 
			
		||||
        yield Header()
 | 
			
		||||
        
 | 
			
		||||
        with Horizontal(classes="hosts-container"):
 | 
			
		||||
            left_pane = Vertical(classes="left-pane")
 | 
			
		||||
            left_pane.border_title = "Hosts Entries"
 | 
			
		||||
            with left_pane:
 | 
			
		||||
                yield DataTable(id="entries-table")
 | 
			
		||||
            yield left_pane
 | 
			
		||||
        with Vertical():
 | 
			
		||||
            with Horizontal(classes="hosts-container"):
 | 
			
		||||
                left_pane = Vertical(classes="left-pane")
 | 
			
		||||
                left_pane.border_title = "Hosts Entries"
 | 
			
		||||
                with left_pane:
 | 
			
		||||
                    yield DataTable(id="entries-table")
 | 
			
		||||
                yield left_pane
 | 
			
		||||
                
 | 
			
		||||
                right_pane = Vertical(classes="right-pane")
 | 
			
		||||
                right_pane.border_title = "Entry Details"
 | 
			
		||||
                with right_pane:
 | 
			
		||||
                    yield Static("", id="entry-details")
 | 
			
		||||
                yield right_pane
 | 
			
		||||
            
 | 
			
		||||
            right_pane = Vertical(classes="right-pane")
 | 
			
		||||
            right_pane.border_title = "Entry Details"
 | 
			
		||||
            with right_pane:
 | 
			
		||||
                yield Static("", id="entry-details")
 | 
			
		||||
            yield right_pane
 | 
			
		||||
            yield Static("", classes="status-bar", id="status")
 | 
			
		||||
        
 | 
			
		||||
        yield Static("", classes="status-bar", id="status")
 | 
			
		||||
        yield Footer()
 | 
			
		||||
    
 | 
			
		||||
    def on_ready(self) -> None:
 | 
			
		||||
| 
						 | 
				
			
			@ -367,8 +379,26 @@ class HostsManagerApp(App):
 | 
			
		|||
        status_widget = self.query_one("#status", Static)
 | 
			
		||||
        
 | 
			
		||||
        if message:
 | 
			
		||||
            status_widget.update(message)
 | 
			
		||||
            # Check if this is an error message (starts with ❌)
 | 
			
		||||
            if message.startswith("❌"):
 | 
			
		||||
                # Use error styling for error messages
 | 
			
		||||
                status_widget.remove_class("status-bar")
 | 
			
		||||
                status_widget.add_class("status-error")
 | 
			
		||||
                status_widget.update(message)
 | 
			
		||||
                # Auto-clear error message after 5 seconds
 | 
			
		||||
                self.set_timer(5.0, lambda: self.update_status())
 | 
			
		||||
            else:
 | 
			
		||||
                # Use normal styling for regular messages
 | 
			
		||||
                status_widget.remove_class("status-error")
 | 
			
		||||
                status_widget.add_class("status-bar")
 | 
			
		||||
                status_widget.update(message)
 | 
			
		||||
                # Auto-clear regular message after 3 seconds
 | 
			
		||||
                self.set_timer(3.0, lambda: self.update_status())
 | 
			
		||||
        else:
 | 
			
		||||
            # Reset to normal status display
 | 
			
		||||
            status_widget.remove_class("status-error")
 | 
			
		||||
            status_widget.add_class("status-bar")
 | 
			
		||||
            
 | 
			
		||||
            mode = "Edit mode" if self.edit_mode else "Read-only mode"
 | 
			
		||||
            entry_count = len(self.hosts_file.entries)
 | 
			
		||||
            active_count = len(self.hosts_file.get_active_entries())
 | 
			
		||||
| 
						 | 
				
			
			@ -485,7 +515,7 @@ class HostsManagerApp(App):
 | 
			
		|||
    def action_toggle_entry(self) -> None:
 | 
			
		||||
        """Toggle the active state of the selected entry."""
 | 
			
		||||
        if not self.edit_mode:
 | 
			
		||||
            self.update_status("Not in edit mode - press 'Ctrl+E' to enable editing")
 | 
			
		||||
            self.update_status("❌ Cannot toggle entry: Application is in read-only mode. Press 'Ctrl+E' to enable edit mode.")
 | 
			
		||||
            return
 | 
			
		||||
        
 | 
			
		||||
        if not self.hosts_file.entries:
 | 
			
		||||
| 
						 | 
				
			
			@ -513,7 +543,7 @@ class HostsManagerApp(App):
 | 
			
		|||
    def action_move_entry_up(self) -> None:
 | 
			
		||||
        """Move the selected entry up in the list."""
 | 
			
		||||
        if not self.edit_mode:
 | 
			
		||||
            self.update_status("Not in edit mode - press 'Ctrl+E' to enable editing")
 | 
			
		||||
            self.update_status("❌ Cannot move entry: Application is in read-only mode. Press 'Ctrl+E' to enable edit mode.")
 | 
			
		||||
            return
 | 
			
		||||
        
 | 
			
		||||
        if not self.hosts_file.entries:
 | 
			
		||||
| 
						 | 
				
			
			@ -544,7 +574,7 @@ class HostsManagerApp(App):
 | 
			
		|||
    def action_move_entry_down(self) -> None:
 | 
			
		||||
        """Move the selected entry down in the list."""
 | 
			
		||||
        if not self.edit_mode:
 | 
			
		||||
            self.update_status("Not in edit mode - press 'Ctrl+E' to enable editing")
 | 
			
		||||
            self.update_status("❌ Cannot move entry: Application is in read-only mode. Press 'Ctrl+E' to enable edit mode.")
 | 
			
		||||
            return
 | 
			
		||||
        
 | 
			
		||||
        if not self.hosts_file.entries:
 | 
			
		||||
| 
						 | 
				
			
			@ -575,7 +605,7 @@ class HostsManagerApp(App):
 | 
			
		|||
    def action_save_file(self) -> None:
 | 
			
		||||
        """Save the hosts file to disk."""
 | 
			
		||||
        if not self.edit_mode:
 | 
			
		||||
            self.update_status("Not in edit mode - no changes to save")
 | 
			
		||||
            self.update_status("❌ Cannot save: Application is in read-only mode. No changes to save.")
 | 
			
		||||
            return
 | 
			
		||||
        
 | 
			
		||||
        success, message = self.manager.save_hosts_file(self.hosts_file)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -243,14 +243,17 @@ class TestHostsManagerApp:
 | 
			
		|||
            
 | 
			
		||||
            app = HostsManagerApp()
 | 
			
		||||
            
 | 
			
		||||
            # Mock the query_one method
 | 
			
		||||
            # Mock the query_one method and set_timer to avoid event loop issues
 | 
			
		||||
            mock_status = Mock()
 | 
			
		||||
            app.query_one = Mock(return_value=mock_status)
 | 
			
		||||
            app.set_timer = Mock()  # Mock the timer to avoid event loop issues
 | 
			
		||||
            
 | 
			
		||||
            app.update_status("Custom status message")
 | 
			
		||||
            
 | 
			
		||||
            # Verify status was updated with custom message
 | 
			
		||||
            mock_status.update.assert_called_once_with("Custom status message")
 | 
			
		||||
            # Verify timer was set for auto-clearing
 | 
			
		||||
            app.set_timer.assert_called_once()
 | 
			
		||||
    
 | 
			
		||||
    def test_action_reload(self):
 | 
			
		||||
        """Test reload action."""
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue