feat(rooms): Fix NaN online stats + Add DAARWIZZ CTA on homepage
- Fixed NaN in online stats by using nullish coalescing (?? 0) - Added members_online, zone, room_type to /api/v1/city/rooms response - Added DAARWIZZ chat CTA section on homepage with link to city-lobby - Created task files for next phases: - TASK_PHASE_CITY_ROOMS_FINISH_v2.md - TASK_PHASE_AGENT_MANAGEMENT_v1.md - TASK_PHASE_CITIZENS_DIRECTORY_v1.md
This commit is contained in:
241
docs/tasks/TASK_PHASE_CITY_ROOMS_FINISH_v2.md
Normal file
241
docs/tasks/TASK_PHASE_CITY_ROOMS_FINISH_v2.md
Normal file
@@ -0,0 +1,241 @@
|
||||
# TASK_PHASE_CITY_ROOMS_FINISH_v2
|
||||
|
||||
## Title
|
||||
TASK_PHASE_CITY_ROOMS_FINISH_v2 — MicroDAO Rooms + City Lobby CTA + Online Stats Fix
|
||||
|
||||
## 1. Overview
|
||||
|
||||
Finish the DAARION City chat layer:
|
||||
|
||||
1. City Rooms page:
|
||||
* Map + List already work.
|
||||
* Fix `NaN` for online users count.
|
||||
|
||||
2. MicroDAO Rooms:
|
||||
* Each MicroDAO must have its own rooms section.
|
||||
* Ability to create new rooms from MicroDAO cabinet.
|
||||
|
||||
3. City Lobby CTA:
|
||||
* Main public chat with DAARWIZZ should be accessible directly from the homepage and top navigation.
|
||||
|
||||
All changes must use existing architecture: `city-service` (FastAPI + Postgres) and `apps/web` (Next.js / React).
|
||||
|
||||
---
|
||||
|
||||
## 2. Current State (as of 2025-12-01)
|
||||
|
||||
* Backend:
|
||||
* `scripts/seed_city_rooms.py` creates ~31 rooms (district rooms, lobbies, city lobby, etc.).
|
||||
* `city-service` exposes `/api/v1/city/rooms` (already used by UI).
|
||||
* Rooms table already contains:
|
||||
* `id`
|
||||
* `slug`
|
||||
* `name` (title)
|
||||
* `description`
|
||||
* `zone` (district key)
|
||||
* `room_type` (e.g. `city`, `district`, `microdao`, etc.)
|
||||
* `is_public` (bool)
|
||||
* `microdao_id` (nullable, for MicroDAO rooms)
|
||||
|
||||
* Frontend:
|
||||
* `/city` (City Rooms):
|
||||
* Map tab: grid of districts, shows total rooms and online counters.
|
||||
* List tab: renders all rooms with description.
|
||||
* At the bottom: summary cards: `Kімнат: 31`, `Онлайн: NaN`.
|
||||
* MicroDAO pages (`/microdao/[slug]`):
|
||||
* Currently have no dedicated "MicroDAO Rooms" section.
|
||||
* Homepage (`/`):
|
||||
* No direct CTA to "DAARION City Lobby".
|
||||
|
||||
---
|
||||
|
||||
## 3. Goals
|
||||
|
||||
1. **Fix online counter**:
|
||||
* Never show `NaN` on the City Rooms page.
|
||||
|
||||
2. **MicroDAO Rooms UI**:
|
||||
* On `/microdao/[slug]` display:
|
||||
* List of rooms belonging to this MicroDAO.
|
||||
* Button "Створити кімнату".
|
||||
|
||||
3. **City Lobby CTA**:
|
||||
* On homepage and/or main menu:
|
||||
* Button "Поспілкуватися з DAARWIZZ" that opens `DAARION City Lobby`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Backend Tasks
|
||||
|
||||
### 4.1. Room model & repository
|
||||
|
||||
Files:
|
||||
* `services/city-service/models_city.py`
|
||||
* `services/city-service/repo_city.py`
|
||||
* `services/city-service/routes_city.py`
|
||||
|
||||
1. **Ensure Room model exposes needed fields:**
|
||||
|
||||
```python
|
||||
class CityRoomSummary(BaseModel):
|
||||
id: str
|
||||
slug: str
|
||||
name: str
|
||||
description: Optional[str]
|
||||
zone: Optional[str]
|
||||
room_type: str
|
||||
microdao_id: Optional[str]
|
||||
is_public: bool
|
||||
members_online: int = 0 # ← must always be integer (0 by default)
|
||||
```
|
||||
|
||||
2. **Repo method for city rooms:**
|
||||
* `get_all_rooms()` must:
|
||||
* Return `members_online` as integer (use `COALESCE(..., 0)`).
|
||||
|
||||
3. **Repo method for MicroDAO rooms:**
|
||||
Add/verify method:
|
||||
|
||||
```python
|
||||
async def get_microdao_rooms(microdao_id: str) -> List[dict]:
|
||||
"""Get rooms for specific MicroDAO"""
|
||||
# WHERE microdao_id = $1 AND is_public = true OR owner_type = 'microdao'
|
||||
```
|
||||
|
||||
4. **API routes:**
|
||||
* Verify endpoint: `GET /api/v1/city/rooms` returns `members_online: int`.
|
||||
* Verify endpoint: `GET /city/microdao/{slug}/rooms` returns MicroDAO rooms.
|
||||
* Add endpoint for room creation:
|
||||
|
||||
```python
|
||||
class CreateRoomRequest(BaseModel):
|
||||
title: str
|
||||
description: Optional[str]
|
||||
room_type: str = "microdao"
|
||||
is_public: bool = False
|
||||
|
||||
@router.post("/city/microdao/{slug}/rooms")
|
||||
async def create_microdao_room(slug: str, body: CreateRoomRequest):
|
||||
# Create room for MicroDAO
|
||||
```
|
||||
|
||||
5. **Online stats summary:**
|
||||
* Add/verify endpoint: `GET /api/v1/city/rooms/summary`
|
||||
* Returns: `{"rooms_total": int, "online_total": int}`
|
||||
|
||||
---
|
||||
|
||||
## 5. Frontend Tasks
|
||||
|
||||
Files:
|
||||
* `apps/web/src/app/city/page.tsx`
|
||||
* `apps/web/src/app/microdao/[slug]/page.tsx`
|
||||
* `apps/web/src/app/page.tsx` (homepage)
|
||||
* `apps/web/src/components/microdao/MicrodaoRoomsSection.tsx` (new)
|
||||
|
||||
### 5.1. Fix `NaN` online stats
|
||||
|
||||
1. In `city/page.tsx`:
|
||||
* Ensure online count uses fallback:
|
||||
|
||||
```tsx
|
||||
const onlineTotal = summary?.online_total ?? 0;
|
||||
// or
|
||||
const onlineTotal = Number(summary?.online_total) || 0;
|
||||
```
|
||||
|
||||
2. If API returns `null` or `undefined`, display `0`.
|
||||
|
||||
### 5.2. MicroDAO Rooms section
|
||||
|
||||
In `apps/web/src/app/microdao/[slug]/page.tsx`:
|
||||
|
||||
1. Add API call:
|
||||
|
||||
```ts
|
||||
const rooms = await fetchMicrodaoRooms(slug); // GET /city/microdao/{slug}/rooms
|
||||
```
|
||||
|
||||
2. Render new section:
|
||||
|
||||
```tsx
|
||||
<section className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-xl font-semibold">Кімнати MicroDAO</h2>
|
||||
<Button onClick={openCreateRoomModal}>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Створити кімнату
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{rooms.length === 0 ? (
|
||||
<p className="text-muted-foreground">
|
||||
Ще немає кімнат. Створіть першу кімнату для цієї платформи.
|
||||
</p>
|
||||
) : (
|
||||
<div className="grid gap-3">
|
||||
{rooms.map((room) => (
|
||||
<MicrodaoRoomCard key={room.slug} room={room} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
```
|
||||
|
||||
3. `MicrodaoRoomCard` component:
|
||||
* title
|
||||
* description
|
||||
* room_type (badge)
|
||||
* visibility (Public/Private badge)
|
||||
* members_online
|
||||
|
||||
4. "Create Room" modal:
|
||||
* Назва кімнати (title, required)
|
||||
* Опис (optional)
|
||||
* Тип (select: Lobby, Governance, Project, Crew)
|
||||
* Видимість (switch: Public / Private, default = Private)
|
||||
* On submit → POST `/city/microdao/{slug}/rooms`
|
||||
|
||||
### 5.3. City Lobby CTA
|
||||
|
||||
1. On homepage (`apps/web/src/app/page.tsx`):
|
||||
|
||||
```tsx
|
||||
<section className="py-12 text-center">
|
||||
<h2 className="text-2xl font-bold mb-4">
|
||||
Головний публічний чат DAARION City
|
||||
</h2>
|
||||
<p className="text-muted-foreground mb-6">
|
||||
Поспілкуватися з DAARWIZZ та іншими громадянами міста.
|
||||
</p>
|
||||
<Link href="/city?room=city-lobby">
|
||||
<Button size="lg" className="gap-2">
|
||||
<MessageCircle className="w-5 h-5" />
|
||||
Поспілкуватися з DAARWIZZ
|
||||
</Button>
|
||||
</Link>
|
||||
</section>
|
||||
```
|
||||
|
||||
2. Optionally add to navigation header:
|
||||
* New item: "City Lobby" → `/city?room=city-lobby`
|
||||
|
||||
---
|
||||
|
||||
## 6. Acceptance Criteria
|
||||
|
||||
1. City Rooms page shows:
|
||||
* Correct number of rooms.
|
||||
* Online count is integer (0 allowed), never `NaN`.
|
||||
|
||||
2. MicroDAO page (`/microdao/daarion`, etc.) shows:
|
||||
* "Кімнати MicroDAO" section.
|
||||
* Ability to create a room.
|
||||
* Newly created room appears in list.
|
||||
|
||||
3. Homepage shows:
|
||||
* CTA "Поспілкуватися з DAARWIZZ".
|
||||
* Clicking the button opens City Lobby room.
|
||||
|
||||
4. All routes work both on NODE1 and NODE2.
|
||||
|
||||
Reference in New Issue
Block a user