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

22 KiB
Raw Blame History

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:

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:

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):

# 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:

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:

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:

# 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:

@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:

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

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

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:

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:

# 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:

# ✅ 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:

# 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

@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

@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

@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

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:

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:

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:

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

# ✅ 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

@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

@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

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:

# 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

# 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

# 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