fix: migrate UI to V2 API and fix admin dashboard

Backend fixes:
- Fix markdown generation using correct 'markdown_content' key in tasks.py
- Update admin service to return flat data structure matching frontend types
- Add task_count and failed_tasks fields to user statistics
- Fix top users endpoint to return complete user data

Frontend fixes:
- Migrate ResultsPage from V1 batch API to V2 task API with polling
- Create TaskDetailPage component with markdown preview and download buttons
- Refactor ExportPage to support multi-task selection using V2 download endpoints
- Fix login infinite refresh loop with concurrency control flags
- Create missing Checkbox UI component

New features:
- Add /tasks/:taskId route for task detail view
- Implement multi-task batch export functionality
- Add real-time task status polling (2s interval)

OpenSpec:
- Archive completed proposal 2025-11-17-fix-v2-api-ui-issues
- Create result-export and task-management specifications

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
egg
2025-11-17 08:55:50 +08:00
parent 62609de57c
commit 012da1abc4
15 changed files with 1122 additions and 496 deletions

View File

@@ -86,22 +86,16 @@ class AdminService:
).count()
return {
"users": {
"total": total_users,
"active": active_users,
"active_30d": active_users_30d
},
"tasks": {
"total": total_tasks,
"by_status": tasks_by_status,
"recent_7d": recent_tasks
},
"sessions": {
"active": active_sessions
},
"activity": {
"logins_7d": recent_logins,
"tasks_7d": recent_tasks
"total_users": total_users,
"active_users": active_users,
"total_tasks": total_tasks,
"total_sessions": active_sessions,
"recent_activity_count": recent_tasks,
"task_stats": {
"pending": tasks_by_status.get("pending", 0),
"processing": tasks_by_status.get("processing", 0),
"completed": tasks_by_status.get("completed", 0),
"failed": tasks_by_status.get("failed", 0)
}
}
@@ -142,6 +136,14 @@ class AdminService:
)
).count()
# Count failed tasks
failed_tasks = db.query(Task).filter(
and_(
Task.user_id == user.id,
Task.status == TaskStatus.FAILED
)
).count()
# Count active sessions
active_sessions = db.query(UserSession).filter(
and_(
@@ -152,8 +154,9 @@ class AdminService:
user_list.append({
**user.to_dict(),
"total_tasks": task_count,
"task_count": task_count,
"completed_tasks": completed_tasks,
"failed_tasks": failed_tasks,
"active_sessions": active_sessions,
"is_admin": self.is_admin(user.email)
})
@@ -177,34 +180,34 @@ class AdminService:
Returns:
List of top users with counts
"""
if metric == "completed_tasks":
# Top users by completed tasks
results = db.query(
User,
func.count(Task.id).label("task_count")
).join(Task).filter(
Task.status == TaskStatus.COMPLETED
).group_by(User.id).order_by(
func.count(Task.id).desc()
).limit(limit).all()
else:
# Top users by total tasks (default)
results = db.query(
User,
func.count(Task.id).label("task_count")
).join(Task).group_by(User.id).order_by(
func.count(Task.id).desc()
).limit(limit).all()
# Get top users by total tasks
results = db.query(
User,
func.count(Task.id).label("task_count")
).join(Task).group_by(User.id).order_by(
func.count(Task.id).desc()
).limit(limit).all()
return [
{
# Build result list with both task_count and completed_tasks
top_users = []
for user, task_count in results:
# Count completed tasks for this user
completed_tasks = db.query(Task).filter(
and_(
Task.user_id == user.id,
Task.status == TaskStatus.COMPLETED
)
).count()
top_users.append({
"user_id": user.id,
"email": user.email,
"display_name": user.display_name,
"count": count
}
for user, count in results
]
"task_count": task_count,
"completed_tasks": completed_tasks
})
return top_users
# Singleton instance