major-domo-v2/views/CLAUDE.md
Cal Corum ca325142d8 CLAUDE: Add comprehensive CLAUDE.md documentation files for AI agent guidance
Adding 17 CLAUDE.md files across the project to provide detailed context
and implementation guidelines for AI development agents:

Root Documentation:
- CLAUDE.md - Main project guide with Git workflow requirements

Component Documentation:
- commands/CLAUDE.md - Command architecture and patterns
- models/CLAUDE.md - Pydantic models and validation
- services/CLAUDE.md - Service layer and API interactions
- tasks/CLAUDE.md - Background tasks and automation
- tests/CLAUDE.md - Testing strategies and patterns
- utils/CLAUDE.md - Utility functions and decorators
- views/CLAUDE.md - Discord UI components and embeds

Command Package Documentation:
- commands/help/CLAUDE.md - Help system implementation
- commands/injuries/CLAUDE.md - Injury management commands
- commands/league/CLAUDE.md - League-wide commands
- commands/players/CLAUDE.md - Player information commands
- commands/profile/CLAUDE.md - User profile commands
- commands/teams/CLAUDE.md - Team information commands
- commands/transactions/CLAUDE.md - Transaction management
- commands/utilities/CLAUDE.md - Utility commands
- commands/voice/CLAUDE.md - Voice channel management

Key Updates:
- Updated .gitignore to track CLAUDE.md files in version control
- Added Git Workflow section requiring branch-based development
- Documented all architectural patterns and best practices
- Included comprehensive command/service implementation guides

These files provide essential context for AI agents working on the codebase,
ensuring consistent patterns, proper error handling, and maintainable code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 20:30:07 -05:00

709 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Views Directory
The views directory contains Discord UI components for Discord Bot v2.0, providing consistent visual interfaces and interactive elements. This includes embeds, modals, buttons, select menus, and other Discord UI components.
## Architecture
### Component-Based UI Design
Views in Discord Bot v2.0 follow these principles:
- **Consistent styling** via centralized templates
- **Reusable components** for common UI patterns
- **Error handling** with graceful degradation
- **User interaction tracking** and validation
- **Accessibility** with proper labeling and feedback
### Base Components
All view components inherit from Discord.py base classes with enhanced functionality:
- **BaseView** - Enhanced discord.ui.View with logging and user validation
- **BaseModal** - Enhanced discord.ui.Modal with error handling
- **EmbedTemplate** - Centralized embed creation with consistent styling
## View Components
### Base View System (`base.py`)
#### BaseView Class
Foundation for all interactive views:
```python
class BaseView(discord.ui.View):
def __init__(self, timeout=180.0, user_id=None):
super().__init__(timeout=timeout)
self.user_id = user_id
self.logger = get_contextual_logger(f'{__name__}.BaseView')
async def interaction_check(self, interaction) -> bool:
"""Validate user permissions for interaction."""
async def on_timeout(self) -> None:
"""Handle view timeout gracefully."""
async def on_error(self, interaction, error, item) -> None:
"""Handle view errors with user feedback."""
```
#### ConfirmationView Class (Updated January 2025)
Reusable confirmation dialog with Confirm/Cancel buttons (`confirmations.py`):
**Key Features:**
- **User restriction**: Only specified users can interact
- **Customizable labels and styles**: Flexible button appearance
- **Timeout handling**: Automatic cleanup after timeout
- **Three-state result**: `True` (confirmed), `False` (cancelled), `None` (timeout)
- **Clean interface**: Automatically removes buttons after interaction
**Usage Pattern:**
```python
from views.confirmations import ConfirmationView
# Create confirmation dialog
view = ConfirmationView(
responders=[interaction.user], # Only this user can interact
timeout=30.0, # 30 second timeout
confirm_label="Yes, delete", # Custom label
cancel_label="No, keep it" # Custom label
)
# Send confirmation
await interaction.edit_original_response(
content="⚠️ Are you sure you want to delete this?",
view=view
)
# Wait for user response
await view.wait()
# Check result
if view.confirmed is True:
# User clicked Confirm
await interaction.edit_original_response(
content="✅ Deleted successfully",
view=None
)
elif view.confirmed is False:
# User clicked Cancel
await interaction.edit_original_response(
content="❌ Cancelled",
view=None
)
else:
# Timeout occurred (view.confirmed is None)
await interaction.edit_original_response(
content="⏱️ Request timed out",
view=None
)
```
**Real-World Example (Scorecard Submission):**
```python
# From commands/league/submit_scorecard.py
if duplicate_game:
view = ConfirmationView(
responders=[interaction.user],
timeout=30.0
)
await interaction.edit_original_response(
content=(
f"⚠️ This game has already been played!\n"
f"Would you like me to wipe the old one and re-submit?"
),
view=view
)
await view.wait()
if view.confirmed:
# User confirmed - proceed with wipe and resubmit
await wipe_old_data()
else:
# User cancelled - exit gracefully
return
```
**Configuration Options:**
```python
ConfirmationView(
responders=[user1, user2], # Multiple users allowed
timeout=60.0, # Custom timeout
confirm_label="Approve", # Custom confirm text
cancel_label="Reject", # Custom cancel text
confirm_style=discord.ButtonStyle.red, # Custom button style
cancel_style=discord.ButtonStyle.grey # Custom button style
)
```
#### PaginationView Class
Multi-page navigation for large datasets:
```python
pages = [embed1, embed2, embed3]
pagination = PaginationView(
pages=pages,
user_id=interaction.user.id,
show_page_numbers=True
)
await interaction.followup.send(embed=pagination.get_current_embed(), view=pagination)
```
### Embed Templates (`embeds.py`)
#### EmbedTemplate Class
Centralized embed creation with consistent styling:
```python
# Success embed
embed = EmbedTemplate.success(
title="Operation Completed",
description="Your request was processed successfully."
)
# Error embed
embed = EmbedTemplate.error(
title="Operation Failed",
description="Please check your input and try again."
)
# Warning embed
embed = EmbedTemplate.warning(
title="Careful!",
description="This action cannot be undone."
)
# Info embed
embed = EmbedTemplate.info(
title="Information",
description="Here's what you need to know."
)
```
#### EmbedColors Dataclass
Consistent color scheme across all embeds:
```python
@dataclass(frozen=True)
class EmbedColors:
PRIMARY: int = 0xa6ce39 # SBA green
SUCCESS: int = 0x28a745 # Green
WARNING: int = 0xffc107 # Yellow
ERROR: int = 0xdc3545 # Red
INFO: int = 0x17a2b8 # Blue
SECONDARY: int = 0x6c757d # Gray
```
### Modal Forms (`modals.py`)
#### BaseModal Class
Foundation for interactive forms:
```python
class BaseModal(discord.ui.Modal):
def __init__(self, title: str, timeout=300.0):
super().__init__(title=title, timeout=timeout)
self.logger = get_contextual_logger(f'{__name__}.BaseModal')
self.result = None
async def on_submit(self, interaction):
"""Handle form submission."""
async def on_error(self, interaction, error):
"""Handle form errors."""
```
#### Usage Pattern
```python
class CustomCommandModal(BaseModal):
def __init__(self):
super().__init__(title="Create Custom Command")
name = discord.ui.TextInput(
label="Command Name",
placeholder="Enter command name...",
required=True,
max_length=50
)
response = discord.ui.TextInput(
label="Response",
placeholder="Enter command response...",
style=discord.TextStyle.paragraph,
required=True,
max_length=2000
)
async def on_submit(self, interaction):
# Process form data
command_data = {
"name": self.name.value,
"response": self.response.value
}
# Handle creation logic
```
### Common UI Elements (`common.py`)
#### Shared Components
- **Loading indicators** for async operations
- **Status messages** for operation feedback
- **Navigation elements** for multi-step processes
- **Validation displays** for form errors
### Specialized Views
#### Custom Commands (`custom_commands.py`)
Views specific to custom command management:
- Command creation forms
- Command listing with actions
- Bulk management interfaces
#### Player Information (`players.py`)
Interactive views for player information display with toggleable statistics:
**PlayerStatsView** - Toggle batting and pitching statistics independently
```python
from views.players import PlayerStatsView
# Create interactive player stats view
view = PlayerStatsView(
player=player_with_team,
season=search_season,
batting_stats=batting_stats,
pitching_stats=pitching_stats,
user_id=interaction.user.id
)
# Get initial embed with stats hidden
embed = await view.get_initial_embed()
# Send with interactive view
await interaction.followup.send(embed=embed, view=view)
```
**Key Features:**
- **Basic Info Always Visible**: Name, position, team, sWAR, injury status displayed by default
- **Stats Hidden by Default**: Batting and pitching stats are hidden until user clicks toggle buttons
- **Independent Toggles**: Users can show/hide batting and pitching stats separately
- **Conditional Buttons**: Buttons only appear if corresponding stats are available
- **User Restriction**: Only the user who ran the command can toggle stats
- **Timeout Handling**: View times out after 5 minutes with graceful cleanup
- **Professional UI**: Uses baseball emojis (💥 batting impact, ⚾ pitching) and primary button style
- **Dynamic Updates**: Embed updates in-place when buttons are clicked
**Button Behavior:**
- **Initial State**: "Show Batting Stats" and "Show Pitching Stats"
- **Toggled State**: "Hide Batting Stats" and "Hide Pitching Stats"
- **Visual Feedback**: Button labels change to reflect current state
- **Clean Interface**: Only relevant buttons are shown based on available data
**Implementation Notes:**
- Inherits from `BaseView` for consistent error handling and logging
- Stats formatting matches existing player card design with rounded box code blocks
- Preserves all player card features (images, thumbnails, team colors)
- Comprehensive logging for debugging and monitoring
#### Transaction Management (`transaction_embed.py`) (Updated October 2025)
Views for player transaction interfaces with dual-mode submission support:
- **Transaction builder** with interactive controls
- **Comprehensive validation** and sWAR display
- **Pre-existing transaction** context
- **Dual submission modes**: Scheduled (/dropadd) and Immediate (/ilmove)
- **Dynamic UI instructions**: Context-aware command references
**Key Classes:**
```python
class TransactionEmbedView(discord.ui.View):
def __init__(
self,
builder: TransactionBuilder,
user_id: int,
submission_handler: str = "scheduled", # "scheduled" or "immediate"
command_name: str = "/dropadd" # Command for UI instructions
):
"""
Interactive transaction builder view supporting both scheduled and immediate execution.
Args:
builder: TransactionBuilder instance
user_id: Discord user ID (for permission checking)
submission_handler: "scheduled" for /dropadd, "immediate" for /ilmove
command_name: Command name shown in "Add More Moves" instructions
"""
async def create_transaction_embed(
builder: TransactionBuilder,
command_name: str = "/dropadd"
) -> discord.Embed:
"""
Create transaction builder embed with context-aware instructions.
Args:
builder: TransactionBuilder instance
command_name: Command name for "Add More Moves" instruction
Returns:
Discord embed showing transaction state with appropriate instructions
"""
```
**Submission Handler Behavior:**
- **"scheduled" mode** (/dropadd):
- Creates transactions for NEXT week
- No database POST - stays in memory
- Background task processes later
- Instructions say "Use `/dropadd` to add more moves"
- **"immediate" mode** (/ilmove):
- Creates transactions for THIS week
- Immediately POSTs to database API
- Immediately updates player teams
- Instructions say "Use `/ilmove` to add more moves"
**Usage Examples:**
```python
# For /dropadd (scheduled submission)
embed = await create_transaction_embed(builder, command_name="/dropadd")
view = TransactionEmbedView(
builder,
user_id,
submission_handler="scheduled",
command_name="/dropadd"
)
# For /ilmove (immediate submission)
embed = await create_transaction_embed(builder, command_name="/ilmove")
view = TransactionEmbedView(
builder,
user_id,
submission_handler="immediate",
command_name="/ilmove"
)
```
**Implementation Notes:**
- **95% code reuse** between /dropadd and /ilmove
- **Same TransactionBuilder** instance shared between both commands
- **Dynamic embed description**: Changes based on command_name
- **Context propagation**: command_name passed through all UI components
- **Backwards compatible**: Default parameters maintain /dropadd behavior
## Styling Guidelines
### Embed Consistency
All embeds should use EmbedTemplate methods:
```python
# ✅ Consistent styling
embed = EmbedTemplate.success("Player Added", "Player successfully added to roster")
# ❌ Inconsistent styling
embed = discord.Embed(title="Player Added", color=0x00ff00)
```
### Color Usage
Use the standard color palette:
- **PRIMARY (SBA Green)** - Default for neutral information
- **SUCCESS (Green)** - Successful operations
- **ERROR (Red)** - Errors and failures
- **WARNING (Yellow)** - Warnings and cautions
- **INFO (Blue)** - General information
- **SECONDARY (Gray)** - Less important information
### User Feedback
Provide clear feedback for all user interactions:
```python
# Loading state
embed = EmbedTemplate.info("Processing", "Please wait while we process your request...")
# Success state
embed = EmbedTemplate.success("Complete", "Your request has been processed successfully.")
# Error state with helpful information
embed = EmbedTemplate.error(
"Request Failed",
"The player name was not found. Please check your spelling and try again."
)
```
## Interactive Components
### Button Patterns
#### Action Buttons
```python
@discord.ui.button(label="Confirm", style=discord.ButtonStyle.success, emoji="✅")
async def confirm_button(self, interaction, button):
self.increment_interaction_count()
# Handle confirmation
await interaction.response.edit_message(content="Confirmed!", view=None)
```
#### Navigation Buttons
```python
@discord.ui.button(emoji="◀️", style=discord.ButtonStyle.primary)
async def previous_page(self, interaction, button):
self.current_page = max(0, self.current_page - 1)
await interaction.response.edit_message(embed=self.get_current_embed(), view=self)
```
### Select Menu Patterns
#### Option Selection
```python
@discord.ui.select(placeholder="Choose an option...")
async def select_option(self, interaction, select):
selected_value = select.values[0]
# Handle selection
await interaction.response.send_message(f"You selected: {selected_value}")
```
#### Dynamic Options
```python
class PlayerSelectMenu(discord.ui.Select):
def __init__(self, players: List[Player]):
options = [
discord.SelectOption(
label=player.name,
value=str(player.id),
description=f"{player.position} - {player.team.abbrev}"
)
for player in players[:25] # Discord limit
]
super().__init__(placeholder="Select a player...", options=options)
```
## Error Handling
### View Error Handling
All views implement comprehensive error handling:
```python
async def on_error(self, interaction, error, item):
"""Handle view errors gracefully."""
self.logger.error("View error", error=error, item_type=type(item).__name__)
try:
embed = EmbedTemplate.error(
"Interaction Error",
"Something went wrong. Please try again."
)
if not interaction.response.is_done():
await interaction.response.send_message(embed=embed, ephemeral=True)
else:
await interaction.followup.send(embed=embed, ephemeral=True)
except Exception as e:
self.logger.error("Failed to send error message", error=e)
```
### User Input Validation
Forms validate user input before processing:
```python
async def on_submit(self, interaction):
# Validate input
if len(self.name.value) < 2:
embed = EmbedTemplate.error(
"Invalid Input",
"Command name must be at least 2 characters long."
)
await interaction.response.send_message(embed=embed, ephemeral=True)
return
# Process valid input
await self.create_command(interaction)
```
## Accessibility Features
### User-Friendly Labels
- **Clear button labels** with descriptive text
- **Helpful placeholders** in form fields
- **Descriptive error messages** with actionable guidance
- **Consistent emoji usage** for visual recognition
### Permission Validation
Views respect user permissions and provide appropriate feedback:
```python
async def interaction_check(self, interaction) -> bool:
"""Check if user can interact with this view."""
if self.user_id and interaction.user.id != self.user_id:
await interaction.response.send_message(
"❌ You cannot interact with this menu.",
ephemeral=True
)
return False
return True
```
## Performance Considerations
### View Lifecycle Management
- **Timeout handling** prevents orphaned views
- **Resource cleanup** in view destructors
- **Interaction tracking** for usage analytics
- **Memory management** for large datasets
### Efficient Updates
```python
# ✅ Efficient - Only update what changed
await interaction.response.edit_message(embed=new_embed, view=self)
# ❌ Inefficient - Sends new message
await interaction.response.send_message(embed=new_embed, view=new_view)
```
## Testing Strategies
### View Testing
```python
@pytest.mark.asyncio
async def test_confirmation_view():
view = ConfirmationView(user_id=123)
# Mock interaction
interaction = Mock()
interaction.user.id = 123
# Test button click
await view.confirm_button.callback(interaction)
assert view.result is True
```
### Modal Testing
```python
@pytest.mark.asyncio
async def test_custom_command_modal():
modal = CustomCommandModal()
# Set form values
modal.name.value = "test"
modal.response.value = "Test response"
# Mock interaction
interaction = Mock()
# Test form submission
await modal.on_submit(interaction)
# Verify processing
assert modal.result is not None
```
## Development Guidelines
### Creating New Views
1. **Inherit from base classes** for consistency
2. **Use EmbedTemplate** for all embed creation
3. **Implement proper error handling** in all interactions
4. **Add user permission checks** where appropriate
5. **Include comprehensive logging** with context
6. **Follow timeout patterns** to prevent resource leaks
### View Composition
- **Keep views focused** on single responsibilities
- **Use composition** over complex inheritance
- **Separate business logic** from UI logic
- **Make views testable** with dependency injection
### UI Guidelines
- **Follow Discord design patterns** for familiarity
- **Use consistent colors** from EmbedColors
- **Provide clear user feedback** for all actions
- **Handle edge cases** gracefully
- **Consider mobile users** in layout design
## Transaction Embed Enhancements (January 2025)
### Enhanced Display Features
The transaction embed now provides comprehensive information for better decision-making:
#### New Embed Sections
```python
async def create_transaction_embed(builder: TransactionBuilder) -> discord.Embed:
"""
Creates enhanced transaction embed with sWAR and pre-existing transaction context.
"""
# Existing sections...
# NEW: Team Cost (sWAR) Display
swar_status = f"{validation.major_league_swar_status}\n{validation.minor_league_swar_status}"
embed.add_field(name="Team sWAR", value=swar_status, inline=False)
# NEW: Pre-existing Transaction Context (when applicable)
if validation.pre_existing_transactions_note:
embed.add_field(
name="📋 Transaction Context",
value=validation.pre_existing_transactions_note,
inline=False
)
```
### Enhanced Information Display
#### sWAR Tracking
- **Major League sWAR**: Projected team cost for ML roster
- **Minor League sWAR**: Projected team cost for MiL roster
- **Formatted Display**: Uses 📊 emoji with 1 decimal precision
#### Pre-existing Transaction Context
Dynamic context display based on scheduled moves:
```python
# Example displays:
" **Pre-existing Moves**: 3 scheduled moves (+3.7 sWAR)"
" **Pre-existing Moves**: 2 scheduled moves (-2.5 sWAR)"
" **Pre-existing Moves**: 1 scheduled moves (no sWAR impact)"
# No display when no pre-existing moves (clean interface)
```
### Complete Embed Structure
The enhanced transaction embed now includes:
1. **Current Moves** - List of moves in transaction builder
2. **Roster Status** - Legal/illegal roster counts with limits
3. **Team Cost (sWAR)** - sWAR for both rosters
4. **Transaction Context** - Pre-existing moves impact (conditional)
5. **Errors/Suggestions** - Validation feedback and recommendations
### Usage Examples
#### Basic Transaction Display
```python
# Standard transaction without pre-existing moves
builder = get_transaction_builder(user_id, team)
embed = await create_transaction_embed(builder)
# Shows: moves, roster status, sWAR, errors/suggestions
```
#### Enhanced Context Display
```python
# Transaction with pre-existing moves context
validation = await builder.validate_transaction(next_week=current_week + 1)
embed = await create_transaction_embed(builder)
# Shows: all above + pre-existing transaction impact
```
### User Experience Improvements
- **Complete Context**: Users see full impact including scheduled moves
- **Visual Clarity**: Consistent emoji usage and formatting
- **Conditional Display**: Context only shown when relevant
- **Decision Support**: sWAR projections help strategic planning
### Implementation Notes
- **Backwards Compatible**: Existing embed functionality preserved
- **Conditional Sections**: Pre-existing context only appears when applicable
- **Performance**: Validation data cached to avoid repeated calculations
- **Accessibility**: Clear visual hierarchy with emojis and formatting
---
**Next Steps for AI Agents:**
1. Review existing view implementations for patterns
2. Understand the Discord UI component system
3. Follow the EmbedTemplate system for consistent styling
4. Implement proper error handling and user validation
5. Test interactive components thoroughly
6. Consider accessibility and user experience in design
7. Leverage enhanced transaction context for better user guidance