Fix database constraints and team builder save functionality
🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
60dbcae113
commit
08f7aa8ea8
3 changed files with 382 additions and 20 deletions
216
webserver.py
216
webserver.py
|
|
@ -20,6 +20,11 @@ from src.database import Database
|
|||
class PetBotRequestHandler(BaseHTTPRequestHandler):
|
||||
"""HTTP request handler for PetBot web server"""
|
||||
|
||||
@property
|
||||
def database(self):
|
||||
"""Get database instance from server"""
|
||||
return self.server.database
|
||||
|
||||
def do_GET(self):
|
||||
"""Handle GET requests"""
|
||||
parsed_path = urlparse(self.path)
|
||||
|
|
@ -2442,9 +2447,23 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
|
|||
color: var(--text-secondary);
|
||||
font-style: italic;
|
||||
}}
|
||||
|
||||
.back-link {{
|
||||
color: var(--text-accent);
|
||||
text-decoration: none;
|
||||
margin-bottom: 20px;
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
}}
|
||||
|
||||
.back-link:hover {{
|
||||
text-decoration: underline;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/player/{nickname}" class="back-link">← Back to {nickname}'s Profile</a>
|
||||
|
||||
<div class="header">
|
||||
<h1>🐾 Team Builder</h1>
|
||||
<p>Drag pets between Active and Storage to build your perfect team</p>
|
||||
|
|
@ -2560,14 +2579,20 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
|
|||
}});
|
||||
}}
|
||||
|
||||
// Initialize team state
|
||||
document.querySelectorAll('.pet-card').forEach(card => {{
|
||||
// Initialize team state with debugging
|
||||
console.log('Initializing team state...');
|
||||
document.querySelectorAll('.pet-card').forEach((card, index) => {{
|
||||
const petId = card.dataset.petId;
|
||||
const isActive = card.dataset.active === 'true';
|
||||
originalTeam[petId] = isActive;
|
||||
currentTeam[petId] = isActive;
|
||||
|
||||
console.log(`Pet ${{index}}: ID=${{petId}}, isActive=${{isActive}}, parentContainer=${{card.parentElement.id}}`);
|
||||
}});
|
||||
|
||||
console.log('Original team state:', originalTeam);
|
||||
console.log('Current team state:', currentTeam);
|
||||
|
||||
// Completely rewritten drag and drop - simpler approach
|
||||
function initializeDragAndDrop() {{
|
||||
console.log('Initializing drag and drop...');
|
||||
|
|
@ -2682,13 +2707,21 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
|
|||
}}
|
||||
|
||||
function movePetToActive(petId) {{
|
||||
console.log(`movePetToActive called for pet ${{petId}}`);
|
||||
const card = document.querySelector(`[data-pet-id="${{petId}}"]`);
|
||||
if (!card) return;
|
||||
if (!card) {{
|
||||
console.log(`No card found for pet ${{petId}}`);
|
||||
return;
|
||||
}}
|
||||
|
||||
const activeContainer = document.getElementById('active-container');
|
||||
const currentIsActive = currentTeam[petId];
|
||||
|
||||
console.log(`Pet ${{petId}} current state: ${{currentIsActive ? 'active' : 'storage'}}`);
|
||||
|
||||
if (!currentIsActive) {{
|
||||
console.log(`Moving pet ${{petId}} to active...`);
|
||||
|
||||
// Update state
|
||||
currentTeam[petId] = true;
|
||||
|
||||
|
|
@ -2704,17 +2737,27 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
|
|||
updateDropZoneVisibility();
|
||||
|
||||
console.log('Pet moved to active successfully');
|
||||
}} else {{
|
||||
console.log(`Pet ${{petId}} is already active, no move needed`);
|
||||
}}
|
||||
}}
|
||||
|
||||
function movePetToStorage(petId) {{
|
||||
console.log(`movePetToStorage called for pet ${{petId}}`);
|
||||
const card = document.querySelector(`[data-pet-id="${{petId}}"]`);
|
||||
if (!card) return;
|
||||
if (!card) {{
|
||||
console.log(`No card found for pet ${{petId}}`);
|
||||
return;
|
||||
}}
|
||||
|
||||
const storageContainer = document.getElementById('storage-container');
|
||||
const currentIsActive = currentTeam[petId];
|
||||
|
||||
console.log(`Pet ${{petId}} current state: ${{currentIsActive ? 'active' : 'storage'}}`);
|
||||
|
||||
if (currentIsActive) {{
|
||||
console.log(`Moving pet ${{petId}} to storage...`);
|
||||
|
||||
// Update state
|
||||
currentTeam[petId] = false;
|
||||
|
||||
|
|
@ -2730,6 +2773,8 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
|
|||
updateDropZoneVisibility();
|
||||
|
||||
console.log('Pet moved to storage successfully');
|
||||
}} else {{
|
||||
console.log(`Pet ${{petId}} is already in storage, no move needed`);
|
||||
}}
|
||||
}}
|
||||
|
||||
|
|
@ -2740,8 +2785,23 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
|
|||
const activeDrop = document.getElementById('active-drop');
|
||||
const storageDrop = document.getElementById('storage-drop');
|
||||
|
||||
activeDrop.style.display = activeContainer.children.length > 0 ? 'none' : 'flex';
|
||||
storageDrop.style.display = storageContainer.children.length > 0 ? 'none' : 'flex';
|
||||
// Use CSS classes instead of direct style manipulation
|
||||
if (activeContainer.children.length > 0) {{
|
||||
activeDrop.classList.add('has-pets');
|
||||
}} else {{
|
||||
activeDrop.classList.remove('has-pets');
|
||||
}}
|
||||
|
||||
if (storageContainer.children.length > 0) {{
|
||||
storageDrop.classList.add('has-pets');
|
||||
}} else {{
|
||||
storageDrop.classList.remove('has-pets');
|
||||
}}
|
||||
|
||||
console.log('Drop zone visibility updated:', {{
|
||||
activeContainerPets: activeContainer.children.length,
|
||||
storageContainerPets: storageContainer.children.length
|
||||
}});
|
||||
}}
|
||||
|
||||
function updateSaveButton() {{
|
||||
|
|
@ -2850,16 +2910,31 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
|
|||
}}
|
||||
}});
|
||||
|
||||
// Initialize interface
|
||||
// Initialize interface with debugging
|
||||
console.log('Starting initialization...');
|
||||
|
||||
// Debug initial state
|
||||
const activeContainer = document.getElementById('active-container');
|
||||
const storageContainer = document.getElementById('storage-container');
|
||||
console.log('Initial state:', {{
|
||||
activePets: activeContainer.children.length,
|
||||
storagePets: storageContainer.children.length
|
||||
}});
|
||||
|
||||
initializeDragAndDrop();
|
||||
addClickToMoveBackup(); // Add double-click as backup
|
||||
updateSaveButton();
|
||||
|
||||
console.log('Before updateDropZoneVisibility...');
|
||||
updateDropZoneVisibility();
|
||||
|
||||
console.log('Initialization complete.');
|
||||
|
||||
// Run test to verify everything is working
|
||||
setTimeout(() => {{
|
||||
console.log('Running delayed test...');
|
||||
runDragDropTest();
|
||||
}}, 500);
|
||||
}}, 1000);
|
||||
|
||||
// Add test button for manual debugging
|
||||
const testButton = document.createElement('button');
|
||||
|
|
@ -2897,11 +2972,132 @@ class PetBotRequestHandler(BaseHTTPRequestHandler):
|
|||
|
||||
def handle_team_save(self, nickname):
|
||||
"""Handle team save request and generate PIN"""
|
||||
self.send_json_response({"success": False, "error": "Team save not fully implemented yet"}, 501)
|
||||
try:
|
||||
# Get POST data
|
||||
content_length = int(self.headers.get('Content-Length', 0))
|
||||
if content_length == 0:
|
||||
self.send_json_response({"success": False, "error": "No data provided"}, 400)
|
||||
return
|
||||
|
||||
post_data = self.rfile.read(content_length).decode('utf-8')
|
||||
|
||||
# Parse JSON data
|
||||
import json
|
||||
try:
|
||||
team_data = json.loads(post_data)
|
||||
except json.JSONDecodeError:
|
||||
self.send_json_response({"success": False, "error": "Invalid JSON data"}, 400)
|
||||
return
|
||||
|
||||
# Run async operations
|
||||
import asyncio
|
||||
result = asyncio.run(self._handle_team_save_async(nickname, team_data))
|
||||
|
||||
if result["success"]:
|
||||
self.send_json_response(result, 200)
|
||||
else:
|
||||
self.send_json_response(result, 400)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in handle_team_save: {e}")
|
||||
self.send_json_response({"success": False, "error": "Internal server error"}, 500)
|
||||
|
||||
async def _handle_team_save_async(self, nickname, team_data):
|
||||
"""Async handler for team save"""
|
||||
try:
|
||||
# Get player
|
||||
player = await self.database.get_player(nickname)
|
||||
if not player:
|
||||
return {"success": False, "error": "Player not found"}
|
||||
|
||||
# Validate team composition
|
||||
validation = await self.database.validate_team_composition(player["id"], team_data)
|
||||
if not validation["valid"]:
|
||||
return {"success": False, "error": validation["error"]}
|
||||
|
||||
# Create pending team change with PIN
|
||||
import json
|
||||
result = await self.database.create_pending_team_change(
|
||||
player["id"],
|
||||
json.dumps(team_data)
|
||||
)
|
||||
|
||||
if result["success"]:
|
||||
# Send PIN via IRC
|
||||
self.send_pin_via_irc(nickname, result["pin_code"])
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": "PIN sent to your IRC private messages",
|
||||
"expires_in_minutes": 10
|
||||
}
|
||||
else:
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in _handle_team_save_async: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
def handle_team_verify(self, nickname):
|
||||
"""Handle PIN verification and apply team changes"""
|
||||
self.send_json_response({"success": False, "error": "PIN verification not fully implemented yet"}, 501)
|
||||
try:
|
||||
# Get POST data
|
||||
content_length = int(self.headers.get('Content-Length', 0))
|
||||
if content_length == 0:
|
||||
self.send_json_response({"success": False, "error": "No PIN provided"}, 400)
|
||||
return
|
||||
|
||||
post_data = self.rfile.read(content_length).decode('utf-8')
|
||||
|
||||
# Parse JSON data
|
||||
import json
|
||||
try:
|
||||
data = json.loads(post_data)
|
||||
pin_code = data.get("pin", "").strip()
|
||||
except (json.JSONDecodeError, AttributeError):
|
||||
self.send_json_response({"success": False, "error": "Invalid data format"}, 400)
|
||||
return
|
||||
|
||||
if not pin_code:
|
||||
self.send_json_response({"success": False, "error": "PIN code is required"}, 400)
|
||||
return
|
||||
|
||||
# Run async operations
|
||||
import asyncio
|
||||
result = asyncio.run(self._handle_team_verify_async(nickname, pin_code))
|
||||
|
||||
if result["success"]:
|
||||
self.send_json_response(result, 200)
|
||||
else:
|
||||
self.send_json_response(result, 400)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in handle_team_verify: {e}")
|
||||
self.send_json_response({"success": False, "error": "Internal server error"}, 500)
|
||||
|
||||
async def _handle_team_verify_async(self, nickname, pin_code):
|
||||
"""Async handler for PIN verification"""
|
||||
try:
|
||||
# Get player
|
||||
player = await self.database.get_player(nickname)
|
||||
if not player:
|
||||
return {"success": False, "error": "Player not found"}
|
||||
|
||||
# Apply team changes with PIN verification
|
||||
result = await self.database.apply_team_change(player["id"], pin_code)
|
||||
|
||||
if result["success"]:
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Team changes applied successfully! {result['changes_applied']} pets updated.",
|
||||
"changes_applied": result["changes_applied"]
|
||||
}
|
||||
else:
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in _handle_team_verify_async: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
def send_pin_via_irc(self, nickname, pin_code):
|
||||
"""Send PIN to player via IRC private message"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue