feat: City Rooms routing implementation

Backend:
- GET /api/v1/city/rooms - list all city rooms
- GET /api/v1/city/rooms/{slug} - get room by slug with host agents

Frontend:
- Updated /city/[slug] page with host agents section
- Added breadcrumb navigation
- Updated API client to fetch room by slug
- Added rewrite for /api/city/rooms/:slug

Task doc: TASK_PHASE_CITY_ROOMS_ROUTING_v1.md
This commit is contained in:
Apple
2025-11-30 10:54:39 -08:00
parent 36394cff55
commit 85fdcdf0be
5 changed files with 440 additions and 14 deletions

View File

@@ -0,0 +1,272 @@
# TASK_PHASE_CITY_ROOMS_ROUTING_v1
Version: 1.0
Status: Ready
Priority: High (City Layer UX + Matrix Rooms)
---
# 1. МЕТА
Зробити так, щоб **кожна City Room** була доступною за URL:
- `/city` — огляд усіх кімнат
- `/city/{slug}` — сторінка конкретної кімнати
Сторінка кімнати має:
- завантажувати дані кімнати з backend (room meta + matrix_room_id),
- показувати присутніх агентів,
- інтегрувати чат (Matrix room),
- показувати presence агентів.
---
# 2. ВИХІДНІ ДАНІ
Вже є:
- Таблиця `rooms` з city-кімнатами (scope = 'city', 8+ кімнат).
- Поле `slug` або еквівалент (наприклад `city-general`, `city-welcome` тощо).
- `matrix_room_id` заповнене (Matrix sync завершений).
- Matrix Gateway та Chat API:
- `GET /api/v1/chat/rooms/{room_id}/messages`
- `POST /api/v1/chat/rooms/{room_id}/messages`
- Presence Layer:
- `GET /api/v1/agents/{agent_id}/presence`
- Frontend:
- базовий Chat Widget
- компонент PresenceDot
- карта /city вже існує (огляд кімнат).
---
# 3. SCOPE
1. Frontend routing для `/city/{slug}` (Next.js, App Router).
2. API-обгортка для завантаження City Room.
3. Сторінка кімнати:
- назва, опис, тип кімнати;
- список ключових агентів (host/moderators);
- presence-індикатори;
- чат-виджет, прив'язаний до room_id / matrix_room_id.
4. Обробка помилок:
- 404, якщо кімната не існує;
- fallback UI, якщо Matrix тимчасово недоступний.
---
# 4. МОДУЛЬ 1 — BACKEND (CITY-SERVICE) API (якщо ще немає)
Перевірити/додати в city-service:
## 4.1. GET /api/v1/city/rooms
Список всіх city-кімнат.
Вихід:
```json
[
{
"id": "uuid",
"slug": "city-general",
"name": "General",
"description": "Головна кімната міста",
"scope": "city",
"matrix_room_id": "!room:matrix.daarion.city",
"host_agents": ["agent_id_1", "agent_id_2"]
},
...
]
```
## 4.2. GET /api/v1/city/rooms/{slug}
Конкретна кімната за slug.
Вихід:
```json
{
"id": "uuid",
"slug": "city-general",
"name": "General",
"description": "Головна кімната міста",
"scope": "city",
"matrix_room_id": "!room:matrix.daarion.city",
"host_agents": [
{
"id": "agent_id_1",
"name": "DARIO",
"role": "host"
}
]
}
```
Якщо кімнати немає → 404.
---
# 5. МОДУЛЬ 2 — FRONTEND API КЛІЄНТ
В `apps/web/src/lib/api/city.ts` (або створити новий):
```ts
export async function getCityRooms(): Promise<CityRoomSummary[]> { ... }
export async function getCityRoomBySlug(slug: string): Promise<CityRoomDetail> { ... }
```
Де `CityRoomDetail` містить як мінімум:
- id
- slug
- name
- description
- matrixRoomId
- hostAgents[]
---
# 6. МОДУЛЬ 3 — ROUTING: `/city/{slug}`
У `apps/web/src/app/city/[slug]/page.tsx`:
1. Прочитати `params.slug`.
2. Викликати `getCityRoomBySlug(slug)`.
3. Якщо 404 → показати `notFound()` (Next.js).
4. Якщо інша помилка → показати error boundary / fallback.
---
# 7. МОДУЛЬ 4 — UI СТОРІНКИ КІМНАТИ
Макет сторінки `/city/{slug}`:
## 7.1. Верхня частина (header)
- Назва кімнати (`room.name`)
- Підназва / опис (`room.description`)
- Badge: `City Room`
- Breadcrumb: `City / {room.name}`
## 7.2. Блок "Host Agents"
Карточка(и) головних агентів кімнати:
- аватар
- ім'я
- роль (host/moderator)
- PresenceDot (online/away/offline)
- посилання "Перейти в кабінет агента"
Дані брати з `host_agents`.
## 7.3. Блок "Room Info" (опціонально)
- список тегів (public / governance / help / etc.)
- кількість учасників (якщо є)
## 7.4. Блок "Chat"
Використати Chat Widget, але прив'язати не до Agent/Node/MicroDAO, а до ROOM:
- якщо вже є універсальний `<AgentChatWidget>` з режимами entityType, можна:
- додати `entityType="room"`
- `entityId = room.id`
- якщо ні — створити `CityRoomChatWidget`, який:
- бере `room.id``/api/v1/chat/rooms/{room.id}/messages`
- дозволяє надсилати повідомлення в кімнату
Для неавторизованих:
- "Увійдіть, щоб писати у міську кімнату".
---
# 8. МОДУЛЬ 5 — ІНТЕГРАЦІЯ З /city
На сторінці `/city`:
- у списку/мапі кімнат:
- додати посилання на `/city/{slug}`.
- клік по кімнаті → відкриває `/city/{slug}`.
---
# 9. ОБРОБКА ПОМИЛОК
1. Якщо `/api/v1/city/rooms/{slug}` повертає 404:
- Next.js `notFound()` → стандартна 404-сторінка.
2. Якщо помилка backend (500):
- показати повідомлення "Кімната тимчасово недоступна".
3. Якщо немає `matrix_room_id`:
- показати банер "Matrix ще синхронізується, чат скоро з'явиться".
---
# 10. SMOKE-ТЕСТИ
Після завершення:
1. `/city`:
- список/мапа містить 8+ кімнат.
2. `/city/general`:
- сторінка відкривається;
- видно назву, опис;
- видно host-агентів (наприклад, DARIO/DARIA);
- видно presence.
3. `/city/welcome`, `/city/leadership-hall`, `/city/builders`, `/city/security`, `/city/announcements`:
- усі відкриваються без 404.
4. Chat:
- у кімнаті `/city/general` можливо:
- побачити історію повідомлень;
- відправити нове повідомлення (як авторизований користувач).
---
# 11. FINISH-АРТЕФАКТ
Після виконання Cursor створює:
`docs/debug/city_rooms_routing_report_<DATE>.md`
Зі змістом:
- список slug'ів city-кімнат;
- приклади успішних HTTP-викликів:
- `GET /api/v1/city/rooms`
- `GET /api/v1/city/rooms/{slug}`
- скріншот або опис перевірки `/city/{slug}`;
- підтвердження роботи чату в принаймні одній кімнаті.
---
# 12. PROMPT ДЛЯ CURSOR
```text
Виконай TASK_PHASE_CITY_ROOMS_ROUTING_v1.md.
Фокус:
1) Backend: /api/v1/city/rooms, /api/v1/city/rooms/{slug} (якщо ще немає)
2) Frontend: Next.js routing /city/[slug]
3) UI: сторінка кімнати (host agents, presence, chat)
4) Інтеграція з існуючими чат/Matrix/PRESENCE шарами
Після завершення створи:
docs/debug/city_rooms_routing_report_<DATE>.md
```
---
**Target Date**: Immediate
**Priority**: High
**Dependencies**: Matrix integration complete, Chat API working