+ """
+
+ page_html = self.get_page_template("No Pets - " + nickname, content, "pets")
+ self.send_response(200)
+ self.send_header('Content-type', 'text/html')
+ self.end_headers()
+ self.wfile.write(page_html.encode())
+
def serve_player_not_found(self, nickname):
"""Serve player not found page using unified template"""
content = f"""
@@ -3690,6 +4429,12 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
๐ง Team Builder
+
+ ๐งช Test Team Builder
+
+
+ ๐พ My Pets
+
@@ -4254,6 +4999,24 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
active_pets = [pet for pet in pets if pet['is_active']]
inactive_pets = [pet for pet in pets if not pet['is_active']]
+ # Get team configurations for team selection interface
+ import asyncio
+ from src.database import Database
+ database = Database()
+
+ # Get event loop
+ try:
+ loop = asyncio.get_running_loop()
+ except RuntimeError:
+ loop = asyncio.new_event_loop()
+ asyncio.set_event_loop(loop)
+
+ # Get player and team configurations
+ player = loop.run_until_complete(database.get_player(nickname))
+ team_configs = []
+ if player:
+ team_configs = loop.run_until_complete(database.get_player_team_configurations(player['id']))
+
# Debug logging
print(f"Team Builder Debug for {nickname}:")
print(f"Total pets: {len(pets)}")
@@ -4333,1555 +5096,7 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
storage_cards = ''.join(make_pet_card(pet, False) for pet in inactive_pets)
- html = f"""
-
-
-
-
- Team Builder - {nickname}
-
-
-
- โ Back to {nickname}'s Profile
-
-
-
๐พ Team Builder
-
Drag pets between Active and Storage to build your perfect team
Drag pets between Active Team and Storage. Double-click as backup.
+
Choose a team to edit, then drag pets between Active Team and Storage.
+
+
+
+
+
Select Team to Edit
+
+
+
@@ -6376,46 +5822,26 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
๐ฆ Storage
+
+
+
+
+
+
+
+
""" + storage_pets_html + active_pets_html + """
-
-
๐พ Team Configurations
-
- Save up to 3 different team setups for quick switching between strategies
-
-
-
-
-
-
-
-
-
-
- Editing current team (not saved to any configuration)
-
-
-
-
-
-
-
-
+
@@ -6448,14 +5874,344 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
let draggedElement = null;
// Initialize when DOM is ready
+ // Global variables for team management
+ let currentEditingTeam = 1; // Default to team 1
+
document.addEventListener('DOMContentLoaded', function() {
console.log('Team Builder: DOM loaded, initializing...');
initializeTeamBuilder();
});
+ function selectTeam(teamSlot) {
+ console.log('Selecting team slot:', teamSlot);
+
+ // Update UI to show which team is selected
+ document.querySelectorAll('.team-card').forEach(card => {
+ card.classList.remove('selected');
+ const btn = card.querySelector('.edit-team-btn');
+ btn.classList.remove('active');
+ btn.textContent = btn.textContent.replace('๐ข Currently Editing', '๐ Edit ' + card.querySelector('h3').textContent);
+ });
+
+ // Mark selected team
+ const selectedCard = document.querySelector(`[data-slot="${teamSlot}"]`);
+ if (selectedCard) {
+ selectedCard.classList.add('selected');
+ const btn = selectedCard.querySelector('.edit-team-btn');
+ btn.classList.add('active');
+ btn.textContent = '๐ข Currently Editing';
+ }
+
+ // Set current editing team
+ currentEditingTeam = teamSlot;
+
+ // Load team data for this slot (to be implemented)
+ loadTeamConfiguration(teamSlot);
+ }
+
+ function loadTeamConfiguration(teamSlot) {
+ console.log('Loading team configuration for slot:', teamSlot);
+
+ // Update dynamic headers and button text
+ updateDynamicElements(teamSlot);
+
+ // Clear current team slots
+ for (let i = 1; i <= 6; i++) {
+ const slot = document.getElementById(`slot-${i}`);
+ if (slot) {
+ const slotContent = slot.querySelector('.slot-content');
+ slotContent.innerHTML = '
Drop pet here
';
+ }
+ }
+
+ // Move all pets back to storage
+ const storageContainer = document.getElementById('storage-container');
+ const allPetCards = document.querySelectorAll('.pet-card');
+ allPetCards.forEach(card => {
+ if (storageContainer && !storageContainer.contains(card)) {
+ storageContainer.appendChild(card);
+ // Update pet card status
+ card.classList.remove('active');
+ card.classList.add('storage');
+ const statusDiv = card.querySelector('.pet-status');
+ if (statusDiv) {
+ statusDiv.textContent = 'Storage';
+ statusDiv.className = 'pet-status status-storage';
+ }
+ }
+ });
+
+ // Load team data from server for the selected slot
+ if (teamSlot === 1) {
+ // For Team 1, load current active pets (default behavior)
+ loadCurrentActiveTeam();
+ } else {
+ // For Teams 2 and 3, load saved configuration if exists
+ loadSavedTeamConfiguration(teamSlot);
+ }
+
+ // Reset team state
+ currentTeam = {};
+ originalTeam = {};
+
+ // Re-initialize team state
+ updateTeamState();
+ }
+
+ function updateDynamicElements(teamSlot) {
+ // Update team header
+ const teamHeader = document.querySelector('h2');
+ if (teamHeader && teamHeader.textContent.includes('Active Team')) {
+ teamHeader.textContent = `โ๏ธ Team ${teamSlot} Selection (1-6 pets)`;
+ }
+
+ // Update save button
+ const saveBtn = document.getElementById('save-btn');
+ if (saveBtn) {
+ saveBtn.textContent = `๐ Save Changes to Team ${teamSlot}`;
+ }
+ }
+
+ function loadCurrentActiveTeam() {
+ // Load the player's current active pets back into team slots
+ console.log('Loading current active team (Team 1)');
+
+ // Find all pet cards that should be active based on their original data attributes
+ const allCards = document.querySelectorAll('.pet-card');
+ console.log(`Found ${allCards.length} total pet cards`);
+
+ allCards.forEach(card => {
+ const isActive = card.dataset.active === 'true';
+ const teamOrder = card.dataset.teamOrder;
+ const petId = card.dataset.petId;
+
+ console.log(`Pet ${petId}: active=${isActive}, teamOrder=${teamOrder}`);
+
+ if (isActive && teamOrder && teamOrder !== 'None' && teamOrder !== '' && teamOrder !== 'null') {
+ const slot = document.getElementById(`slot-${teamOrder}`);
+ if (slot) {
+ // Move pet from storage back to team slot
+ const slotContent = slot.querySelector('.slot-content');
+ slotContent.innerHTML = '';
+ slotContent.appendChild(card);
+
+ // Update pet visual status
+ card.classList.remove('storage');
+ card.classList.add('active');
+ const statusDiv = card.querySelector('.pet-status');
+ if (statusDiv) {
+ statusDiv.textContent = 'Active';
+ statusDiv.className = 'pet-status status-active';
+ }
+
+ // Update team state tracking
+ currentTeam[petId] = parseInt(teamOrder);
+ originalTeam[petId] = parseInt(teamOrder);
+
+ console.log(`โ Restored pet ${petId} to slot ${teamOrder}`);
+ } else {
+ console.log(`โ Could not find slot ${teamOrder} for pet ${petId}`);
+ }
+ } else {
+ // This pet should stay in storage
+ currentTeam[petId] = false;
+ if (!originalTeam.hasOwnProperty(petId)) {
+ originalTeam[petId] = false;
+ }
+ console.log(`Pet ${petId} staying in storage`);
+ }
+ });
+
+ console.log('Current team state after restoration:', currentTeam);
+ updateSaveButton();
+ }
+
+ async function loadSavedTeamConfiguration(teamSlot) {
+ console.log(`Loading saved configuration for team ${teamSlot}`);
+ try {
+ const response = await fetch(`/teambuilder/{nickname}/config/load/${teamSlot}`, {
+ method: 'GET',
+ headers: { 'Content-Type': 'application/json' }
+ });
+
+ if (!response.ok) {
+ console.error(`Failed to load team config: ${response.status} ${response.statusText}`);
+ return;
+ }
+
+ const contentType = response.headers.get('content-type');
+ if (!contentType || !contentType.includes('application/json')) {
+ console.error(`Expected JSON response but got: ${contentType}`);
+ const text = await response.text();
+ console.error('Response body:', text);
+ return;
+ }
+
+ const result = await response.json();
+
+ if (result.success && result.team_data) {
+ // Load pets into team slots based on saved configuration
+ for (const [position, petData] of Object.entries(result.team_data)) {
+ if (petData && position >= 1 && position <= 6) {
+ const petCard = document.querySelector(`[data-pet-id="${petData.pet_id}"]`);
+ const slot = document.getElementById(`slot-${position}`);
+
+ if (petCard && slot) {
+ // Move pet to team slot
+ const slotContent = slot.querySelector('.slot-content');
+ slotContent.innerHTML = '';
+ slotContent.appendChild(petCard);
+
+ // Update pet status
+ petCard.classList.remove('storage');
+ petCard.classList.add('active');
+ const statusDiv = petCard.querySelector('.pet-status');
+ if (statusDiv) {
+ statusDiv.textContent = 'Active';
+ statusDiv.className = 'pet-status status-active';
+ }
+
+ // Update team state
+ currentTeam[petData.pet_id] = parseInt(position);
+ originalTeam[petData.pet_id] = parseInt(position);
+ }
+ }
+ }
+ } else {
+ console.log(`No saved configuration found for team ${teamSlot} - starting with empty team`);
+ // Team is already cleared, just update team state for empty team
+ currentTeam = {};
+ originalTeam = {};
+ updateSaveButton();
+ }
+ } catch (error) {
+ console.error('Error loading team configuration:', error);
+ }
+ }
+
+ function updateTeamState() {
+ // Update team state tracking
+ const allCards = document.querySelectorAll('.pet-card');
+ allCards.forEach(card => {
+ const petId = card.dataset.petId;
+ const isActive = card.classList.contains('active');
+ const teamOrder = card.dataset.teamOrder;
+
+ if (isActive && teamOrder) {
+ currentTeam[petId] = parseInt(teamOrder);
+ if (!originalTeam.hasOwnProperty(petId)) {
+ originalTeam[petId] = parseInt(teamOrder);
+ }
+ } else {
+ currentTeam[petId] = false;
+ if (!originalTeam.hasOwnProperty(petId)) {
+ originalTeam[petId] = false;
+ }
+ }
+ });
+
+ updateSaveButton();
+ }
+
+ function updateTeamCard(teamSlot) {
+ // Update the team card display to reflect current team composition
+ const teamCard = document.querySelector(`[data-slot="${teamSlot}"]`);
+ if (!teamCard) return;
+
+ // Count active pets in current team
+ let petCount = 0;
+ let petPreviews = '';
+
+ // Generate mini pet previews for the team card
+ for (let i = 1; i <= 6; i++) {
+ const slot = document.getElementById(`slot-${i}`);
+ if (slot) {
+ const petCard = slot.querySelector('.pet-card');
+ if (petCard) {
+ const petName = petCard.querySelector('.pet-name').textContent;
+ petPreviews += `
${petName}
`;
+ petCount++;
+ } else {
+ petPreviews += '
Empty
';
+ }
+ }
+ }
+
+ // Update team card content
+ const statusSpan = teamCard.querySelector('.team-status');
+ const previewDiv = teamCard.querySelector('.team-preview');
+
+ if (statusSpan) {
+ statusSpan.textContent = petCount > 0 ? `${petCount}/6 pets` : 'Empty team';
+ }
+
+ if (previewDiv) {
+ previewDiv.innerHTML = petPreviews;
+ }
+ }
+
+ function filterPets() {
+ const searchTerm = document.getElementById('pet-search').value.toLowerCase();
+ const storageContainer = document.getElementById('storage-container');
+ const petCards = storageContainer.querySelectorAll('.pet-card');
+
+ petCards.forEach(card => {
+ const petName = card.querySelector('.pet-name').textContent.toLowerCase();
+ const petSpecies = card.dataset.species ? card.dataset.species.toLowerCase() : '';
+ const petTypes = card.querySelectorAll('.type-badge');
+ let typeText = '';
+ petTypes.forEach(badge => typeText += badge.textContent.toLowerCase() + ' ');
+
+ const matches = petName.includes(searchTerm) ||
+ petSpecies.includes(searchTerm) ||
+ typeText.includes(searchTerm);
+
+ card.style.display = matches ? 'block' : 'none';
+ });
+ }
+
+ function sortPets() {
+ const sortBy = document.getElementById('pet-sort').value;
+ const storageContainer = document.getElementById('storage-container');
+ const petCards = Array.from(storageContainer.querySelectorAll('.pet-card'));
+
+ petCards.sort((a, b) => {
+ switch (sortBy) {
+ case 'name':
+ const nameA = a.querySelector('.pet-name').textContent.toLowerCase();
+ const nameB = b.querySelector('.pet-name').textContent.toLowerCase();
+ return nameA.localeCompare(nameB);
+
+ case 'level':
+ const levelA = parseInt(a.querySelector('.pet-level').textContent.replace('Level ', ''));
+ const levelB = parseInt(b.querySelector('.pet-level').textContent.replace('Level ', ''));
+ return levelB - levelA; // Descending order
+
+ case 'type':
+ const typeA = a.querySelector('.type-badge').textContent.toLowerCase();
+ const typeB = b.querySelector('.type-badge').textContent.toLowerCase();
+ return typeA.localeCompare(typeB);
+
+ case 'species':
+ const speciesA = a.dataset.species ? a.dataset.species.toLowerCase() : '';
+ const speciesB = b.dataset.species ? b.dataset.species.toLowerCase() : '';
+ return speciesA.localeCompare(speciesB);
+
+ default:
+ return 0;
+ }
+ });
+
+ // Re-append sorted cards to container
+ petCards.forEach(card => storageContainer.appendChild(card));
+ }
+
function initializeTeamBuilder() {
console.log('Team Builder: Starting initialization...');
+ // Initialize dynamic elements for Team 1 (default)
+ updateDynamicElements(1);
+
// Initialize team state
const allCards = document.querySelectorAll('.pet-card');
console.log(`Found ${allCards.length} pet cards`);
@@ -6647,7 +6403,12 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
const saveBtn = document.getElementById('save-btn');
const hasChanges = JSON.stringify(originalTeam) !== JSON.stringify(currentTeam);
saveBtn.disabled = !hasChanges;
- saveBtn.textContent = hasChanges ? '๐ Save Team Changes' : 'โ No Changes';
+ // Preserve the dynamic team number text
+ if (hasChanges) {
+ saveBtn.textContent = `๐ Save Changes to Team ${currentEditingTeam}`;
+ } else {
+ saveBtn.textContent = `โ No Changes (Team ${currentEditingTeam})`;
+ }
}
async function saveTeam() {
@@ -6656,11 +6417,21 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
teamData[petId] = position;
});
+ // Include the current editing team slot
+ const saveData = {
+ teamData: teamData,
+ teamSlot: currentEditingTeam
+ };
+
+ console.log('๐ SAVE DEBUG: Saving team data:', saveData);
+ console.log('๐ SAVE DEBUG: Current editing team:', currentEditingTeam);
+ console.log('๐ SAVE DEBUG: Team data entries:', Object.keys(teamData).length);
+
try {
const response = await fetch('/teambuilder/""" + nickname + """/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(teamData)
+ body: JSON.stringify(saveData)
});
const result = await response.json();
@@ -6681,6 +6452,9 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
showMessage('Please enter a 6-digit PIN', 'error');
return;
}
+
+ console.log('๐ PIN DEBUG: Verifying PIN for team:', currentEditingTeam);
+ console.log('๐ PIN DEBUG: PIN entered:', pin);
try {
const response = await fetch('/teambuilder/""" + nickname + """/verify', {
@@ -6697,6 +6471,9 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
document.getElementById('pin-section').style.display = 'none';
document.getElementById('pin-input').value = '';
+ // Update team card display after successful save
+ updateTeamCard(currentEditingTeam);
+
// Celebration animation
document.querySelectorAll('.pet-card').forEach(card => {
card.style.animation = 'bounce 0.6s ease-in-out';
@@ -6734,6 +6511,102 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
"""
+ # Generate team cards HTML
+ print(f"Debug: Generating team cards for {len(team_configs)} configs")
+ team_cards_html = ""
+
+ # If no team configs exist, create default slots with Team 1 showing current active team
+ if not team_configs:
+ print("Debug: No team configs found, creating default empty slots")
+ for slot in range(1, 4):
+ # For Team 1, show current active pets
+ if slot == 1:
+ pet_previews = ""
+ active_count = 0
+ for pos in range(1, 7):
+ # Find pet in position pos
+ pet_in_slot = next((pet for pet in active_pets if pet.get('team_order') == pos), None)
+ if pet_in_slot:
+ pet_name = pet_in_slot['nickname'] or pet_in_slot['species_name']
+ pet_emoji = pet_in_slot.get('emoji', '๐พ')
+ pet_previews += f'
{pet_emoji} {pet_name}
'
+ active_count += 1
+ else:
+ pet_previews += '
Empty
'
+
+ status_text = f"{active_count}/6 pets" if active_count > 0 else "Empty team"
+ else:
+ # Teams 2 and 3 are empty by default
+ pet_previews = '
+ '''
+ else:
+ for config in team_configs:
+ print(f"Debug: Processing config: {config}")
+ pet_previews = ""
+
+ # Special handling for Team 1 - show current active team instead of saved config
+ if config['slot'] == 1:
+ active_count = 0
+ for pos in range(1, 7):
+ # Find pet in position pos from current active team
+ pet_in_slot = next((pet for pet in active_pets if pet.get('team_order') == pos), None)
+ if pet_in_slot:
+ pet_name = pet_in_slot['nickname'] or pet_in_slot['species_name']
+ pet_emoji = pet_in_slot.get('emoji', '๐พ')
+ pet_previews += f'
{pet_emoji} {pet_name}
'
+ active_count += 1
+ else:
+ pet_previews += '
Empty
'
+ status_text = f"{active_count}/6 pets" if active_count > 0 else "Empty team"
+ else:
+ # For Teams 2 and 3, use saved configuration data
+ if config['team_data']:
+ for pos in range(1, 7):
+ if str(pos) in config['team_data'] and config['team_data'][str(pos)]:
+ pet_info = config['team_data'][str(pos)]
+ pet_emoji = pet_info.get('emoji', '๐พ')
+ pet_previews += f'
{pet_emoji} {pet_info["name"]}
'
+ else:
+ pet_previews += '
Empty
'
+ else:
+ pet_previews = '
Empty
' * 6
+ status_text = f"{config['pet_count']}/6 pets" if config['pet_count'] > 0 else "Empty team"
+
+ active_class = "active" if config['slot'] == 1 else "" # Default to team 1 as active
+
+ team_cards_html += f'''
+
+
+
{config['name']}
+ {status_text}
+
+
+ {pet_previews}
+
+
+
+ '''
+
+ # Replace placeholder with actual team cards
+ team_builder_content = team_builder_content.replace('', team_cards_html)
+
# Get the unified template
html_content = self.get_page_template(f"Team Builder - {nickname}", team_builder_content, "")
@@ -6742,6 +6615,764 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
self.end_headers()
self.wfile.write(html_content.encode())
+ def serve_test_teambuilder(self, nickname):
+ """Serve the test team builder interface with simplified team management"""
+ from urllib.parse import unquote
+ nickname = unquote(nickname)
+
+ print(f"DEBUG: serve_test_teambuilder called with nickname: {nickname}")
+
+ try:
+ from src.database import Database
+ database = Database()
+
+ # Get event loop
+ try:
+ loop = asyncio.get_running_loop()
+ except RuntimeError:
+ loop = asyncio.new_event_loop()
+ asyncio.set_event_loop(loop)
+
+ player_data = loop.run_until_complete(self.fetch_player_data(database, nickname))
+
+ if player_data is None:
+ self.serve_player_not_found(nickname)
+ return
+
+ pets = player_data['pets']
+ if not pets:
+ self.serve_test_teambuilder_no_pets(nickname)
+ return
+
+ self.serve_test_teambuilder_interface(nickname, pets)
+
+ except Exception as e:
+ print(f"Error loading test team builder for {nickname}: {e}")
+ self.serve_player_error(nickname, f"Error loading test team builder: {str(e)}")
+
+ def serve_test_teambuilder_no_pets(self, nickname):
+ """Show message when player has no pets using unified template"""
+ content = f"""
+
+
๐พ Test Team Builder
+
Simplified team management (Test Version)
+
+
+
+
๐พ No Pets Found
+
{nickname}, you need to catch some pets before using the team builder!
+
Head to the IRC channel and use !explore to find wild pets!