feat: Add MicroDAO Dashboard with activity feed and statistics

- Add microdao_activity table for news/updates/events
- Add statistics columns to microdaos table
- Implement dashboard API endpoints
- Create UI components (HeaderCard, ActivitySection, TeamSection)
- Add seed data for DAARION DAO
- Update backend models and repositories
- Add frontend types and API client
This commit is contained in:
Apple
2025-12-02 06:37:16 -08:00
parent 95c9a17a7a
commit ace183e136
15 changed files with 686 additions and 9 deletions

View File

@@ -4248,8 +4248,13 @@ async def get_microdao_dashboard(slug: str) -> dict:
# Конвертувати citizens в PublicCitizenSummary
citizen_summaries = []
for citizen in citizens:
# Переконатися що slug не None
slug = citizen.get("slug")
if not slug:
continue # Пропустити громадян без slug
citizen_summaries.append({
"slug": citizen.get("slug"),
"slug": slug,
"display_name": citizen["display_name"],
"public_title": citizen.get("public_title"),
"public_tagline": citizen.get("public_tagline"),
@@ -4257,7 +4262,7 @@ async def get_microdao_dashboard(slug: str) -> dict:
"kind": citizen.get("kind"),
"district": citizen.get("district"),
"primary_room_slug": citizen.get("primary_room_slug"),
"public_skills": citizen.get("public_skills", []),
"public_skills": list(citizen.get("public_skills", [])) if citizen.get("public_skills") else [],
"online_status": citizen.get("status", "unknown"),
"status": citizen.get("status"),
"node_id": citizen.get("node_id"),
@@ -4268,15 +4273,20 @@ async def get_microdao_dashboard(slug: str) -> dict:
# Конвертувати activity в MicrodaoActivity
activity_list = []
for act in activity:
# Конвертувати UUID в string, якщо потрібно
author_id = act.get("author_agent_id")
if author_id:
author_id = str(author_id) if not isinstance(author_id, str) else author_id
activity_list.append({
"id": str(act["id"]),
"microdao_slug": act["microdao_slug"],
"kind": act["kind"],
"title": act.get("title"),
"body": act["body"],
"author_agent_id": str(act["author_agent_id"]) if act.get("author_agent_id") else None,
"author_agent_id": author_id,
"author_name": act.get("author_name"),
"created_at": act["created_at"]
"created_at": act["created_at"].isoformat() if hasattr(act["created_at"], "isoformat") else str(act["created_at"])
})
# Створити MicrodaoSummary
@@ -4305,11 +4315,17 @@ async def get_microdao_dashboard(slug: str) -> dict:
}
# Створити stats
last_update = microdao.get("updated_at")
if last_update and hasattr(last_update, "isoformat"):
last_update = last_update.isoformat()
elif last_update:
last_update = str(last_update)
stats = {
"rooms_count": rooms_count,
"citizens_count": citizens_count,
"agents_count": agents_count,
"last_update_at": microdao.get("updated_at")
"last_update_at": last_update
}
return {

View File

@@ -4792,12 +4792,47 @@ async def trigger_node_self_healing(node_id: str):
async def api_get_microdao_dashboard(slug: str):
"""Отримати повний дашборд для MicroDAO"""
try:
dashboard = await repo_city.get_microdao_dashboard(slug)
dashboard_dict = await repo_city.get_microdao_dashboard(slug)
# Конвертувати dict в Pydantic моделі
# MicrodaoSummary вже правильно сформований
# Stats потрібно конвертувати
stats = dashboard_dict["stats"]
if stats.get("last_update_at"):
try:
if isinstance(stats["last_update_at"], str):
stats["last_update_at"] = datetime.fromisoformat(stats["last_update_at"].replace("Z", "+00:00"))
elif hasattr(stats["last_update_at"], "isoformat"):
pass # Вже datetime
except Exception:
stats["last_update_at"] = None
# Activity потрібно конвертувати
activity_list = []
for act in dashboard_dict["recent_activity"]:
act_dict = dict(act)
if act_dict.get("created_at"):
try:
if isinstance(act_dict["created_at"], str):
act_dict["created_at"] = datetime.fromisoformat(act_dict["created_at"].replace("Z", "+00:00"))
except Exception:
pass
activity_list.append(MicrodaoActivity(**act_dict))
# Створити повний об'єкт
dashboard = MicrodaoDashboard(
microdao=MicrodaoSummary(**dashboard_dict["microdao"]),
stats=MicrodaoStats(**stats),
recent_activity=activity_list,
rooms=[CityRoomSummary(**r) for r in dashboard_dict["rooms"]],
citizens=[PublicCitizenSummary(**c) for c in dashboard_dict["citizens"]]
)
return dashboard
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
logger.error(f"Failed to get microdao dashboard for {slug}: {e}")
logger.error(f"Failed to get microdao dashboard for {slug}: {e}", exc_info=True)
raise HTTPException(status_code=500, detail="Failed to load dashboard")