Files
microdao-daarion/site/tasks/TASK_PHASE_MICRODAO_DASHBOARD_v1/index.html
Apple fb4f4a16d5 🔧 Fix GitHub Actions docs workflow
- Update mkdocs dependencies to latest versions
- Add permissions for GitHub Pages deployment
- Add workflow_dispatch for manual trigger
- Fix build command with fallback
2026-01-10 07:57:36 -08:00

1349 lines
55 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="canonical" href="https://IvanTytar.github.io/microdao-daarion/tasks/TASK_PHASE_MICRODAO_DASHBOARD_v1/">
<link rel="icon" href="../../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.5.3, mkdocs-material-9.5.18">
<title>TASK_PHASE_MICRODAO_DASHBOARD_v1 - DAARION Documentation</title>
<link rel="stylesheet" href="../../assets/stylesheets/main.66ac8b77.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
<script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
</head>
<body dir="ltr">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#task_phase_microdao_dashboard_v1" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header md-header--shadow" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../.." title="DAARION Documentation" class="md-header__button md-logo" aria-label="DAARION Documentation" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
DAARION Documentation
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
TASK_PHASE_MICRODAO_DASHBOARD_v1
</span>
</div>
</div>
</div>
<script>var media,input,key,value,palette=__md_get("__palette");if(palette&&palette.color){"(prefers-color-scheme)"===palette.color.media&&(media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']"),palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent"));for([key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
</button>
</nav>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../.." title="DAARION Documentation" class="md-nav__button md-logo" aria-label="DAARION Documentation" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
</a>
DAARION Documentation
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../public/" class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../public/getting-started/" class="md-nav__link">
<span class="md-ellipsis">
Getting Started
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../public/architecture-overview/" class="md-nav__link">
<span class="md-ellipsis">
Architecture
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../public/daiS_daos_overview/" class="md-nav__link">
<span class="md-ellipsis">
DAIS & DAOS
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5" >
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="">
<span class="md-ellipsis">
Internal
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
Internal
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_1" >
<label class="md-nav__link" for="__nav_5_1" id="__nav_5_1_label" tabindex="0">
<span class="md-ellipsis">
Infra
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_1_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5_1">
<span class="md-nav__icon md-icon"></span>
Infra
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../internal/infra/INFRA_AUTOMATION_PACK_V1/" class="md-nav__link">
<span class="md-ellipsis">
Infra Automation Pack v1
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../internal/infra/monitoring_overview/" class="md-nav__link">
<span class="md-ellipsis">
Monitoring Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../internal/infra/nodes_registry_v0/" class="md-nav__link">
<span class="md-ellipsis">
Nodes Registry v0
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_2" >
<label class="md-nav__link" for="__nav_5_2" id="__nav_5_2_label" tabindex="0">
<span class="md-ellipsis">
Specs
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5_2">
<span class="md-nav__icon md-icon"></span>
Specs
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../internal/specs/matrix_presence_aggregator/" class="md-nav__link">
<span class="md-ellipsis">
Matrix Presence Aggregator
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../internal/specs/city_map_spec/" class="md-nav__link">
<span class="md-ellipsis">
City Map Spec
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../internal/specs/node_join_protocol_draft/" class="md-nav__link">
<span class="md-ellipsis">
Node Join Protocol (Draft)
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#1" class="md-nav__link">
<span class="md-ellipsis">
1. Ціль
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#2" class="md-nav__link">
<span class="md-ellipsis">
2. Поточний стан
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#3" class="md-nav__link">
<span class="md-ellipsis">
3. Архітектура рішення
</span>
</a>
<nav class="md-nav" aria-label="3. Архітектура рішення">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#31-postgresql" class="md-nav__link">
<span class="md-ellipsis">
3.1. База даних (PostgreSQL)
</span>
</a>
<nav class="md-nav" aria-label="3.1. База даних (PostgreSQL)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#311-microdao" class="md-nav__link">
<span class="md-ellipsis">
3.1.1. Таблиця активності MicroDAO
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#312-city_microdao" class="md-nav__link">
<span class="md-ellipsis">
3.1.2. Розширити city_microdao під метрики (опціонально, але корисно)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#32-backend-fastapi-city-service" class="md-nav__link">
<span class="md-ellipsis">
3.2. Backend: FastAPI (city-service)
</span>
</a>
<nav class="md-nav" aria-label="3.2. Backend: FastAPI (city-service)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#321-repo-python" class="md-nav__link">
<span class="md-ellipsis">
3.2.1. Repo-рівень (Python)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#322-pydantic-" class="md-nav__link">
<span class="md-ellipsis">
3.2.2. Pydantic-моделі
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#323-routes-fastapi" class="md-nav__link">
<span class="md-ellipsis">
3.2.3. Routes (FastAPI)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#33-frontend-nextjs-appsweb" class="md-nav__link">
<span class="md-ellipsis">
3.3. Frontend: Next.js (apps/web)
</span>
</a>
<nav class="md-nav" aria-label="3.3. Frontend: Next.js (apps/web)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#331-api-" class="md-nav__link">
<span class="md-ellipsis">
3.3.1. API-клієнт
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#332-microdaoslug" class="md-nav__link">
<span class="md-ellipsis">
3.3.2. Структура сторінки /microdao/[slug]
</span>
</a>
<nav class="md-nav" aria-label="3.3.2. Структура сторінки /microdao/[slug]">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#microdaoheadercard" class="md-nav__link">
<span class="md-ellipsis">
MicrodaoHeaderCard
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#microdaoactivitysection" class="md-nav__link">
<span class="md-ellipsis">
MicrodaoActivitySection
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#34" class="md-nav__link">
<span class="md-ellipsis">
3.4. Зв'язок з існуючими розділами
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#4-cursor" class="md-nav__link">
<span class="md-ellipsis">
4. План виконання (для Cursor)
</span>
</a>
<nav class="md-nav" aria-label="4. План виконання (для Cursor)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#1-db" class="md-nav__link">
<span class="md-ellipsis">
Крок 1. DB міграції
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#2-backend" class="md-nav__link">
<span class="md-ellipsis">
Крок 2. Backend
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#3-frontend" class="md-nav__link">
<span class="md-ellipsis">
Крок 3. Frontend
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#4-daarion-dao" class="md-nav__link">
<span class="md-ellipsis">
Крок 4. DAARION DAO як демо
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#7" class="md-nav__link">
<span class="md-ellipsis">
7. Що показувати на презентації
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="task_phase_microdao_dashboard_v1">TASK_PHASE_MICRODAO_DASHBOARD_v1<a class="headerlink" href="#task_phase_microdao_dashboard_v1" title="Permanent link">&para;</a></h1>
<p>Статус: PLANNED </p>
<p>Пріоритет: High (презентація MVP) </p>
<h2 id="1">1. Ціль<a class="headerlink" href="#1" title="Permanent link">&para;</a></h2>
<p>Зробити повноцінний <strong>кабінет MicroDAO</strong> (спочатку для <code>DAARION DAO</code>), який показує:</p>
<ul>
<li>
<p>базові метрики DAO (кімнати, громадяни, агенти);</p>
</li>
<li>
<p>стрічку активності (новини/апдейти);</p>
</li>
<li>
<p>кімнати DAO + кнопки створення кімнати;</p>
</li>
<li>
<p>команду DAO (ключові агенти/громадяни);</p>
</li>
<li>
<p>підготовлені секції під проєкти/задачі/файли/інтеграції.</p>
</li>
</ul>
<p>Архітектурна вимога: <strong>цей самий дашборд</strong> повинен працювати для будь-якого MicroDAO (<code>slug</code>-залежний), щоб його можна було фрактально клонувати.</p>
<hr />
<h2 id="2">2. Поточний стан<a class="headerlink" href="#2" title="Permanent link">&para;</a></h2>
<p>Є:</p>
<ul>
<li>
<p>/microdao — список MicroDAO (з лого, тегами, pinned-сортуванням).</p>
</li>
<li>
<p>/microdao/[slug] — сторінка MicroDAO з:</p>
</li>
<li>
<p>Branding (лого, банер),</p>
</li>
<li>
<p>visibility (public / platform),</p>
</li>
<li>
<p>MicroDAO Rooms section,</p>
</li>
<li>
<p>базовою статистикою (rooms count).</p>
</li>
<li>
<p>/city — кімнати міста (мапа + список).</p>
</li>
<li>
<p>/citizens — публічні агенти (громадяни).</p>
</li>
<li>
<p>/agents — Agent Console (63 агентів, фільтри по нодах, кнопка "Новий агент").</p>
</li>
<li>
<p>API для кімнат і агентів уже працює.</p>
</li>
</ul>
<p>Немає:</p>
<ul>
<li>
<p>окремого <strong>Dashboard API</strong> для MicroDAO;</p>
</li>
<li>
<p>таблиць для <strong>активності (новин)</strong> и <strong>DAO-level метрик</strong>;</p>
</li>
<li>
<p>явної прив'язки громадян до конкретного MicroDAO (лише district/room mapping).</p>
</li>
</ul>
<hr />
<h2 id="3">3. Архітектура рішення<a class="headerlink" href="#3" title="Permanent link">&para;</a></h2>
<h3 id="31-postgresql">3.1. База даних (PostgreSQL)<a class="headerlink" href="#31-postgresql" title="Permanent link">&para;</a></h3>
<h4 id="311-microdao">3.1.1. Таблиця активності MicroDAO<a class="headerlink" href="#311-microdao" title="Permanent link">&para;</a></h4>
<p>Створити міграцію <code>migrations/041_microdao_activity.sql</code>:</p>
<div class="codehilite"><pre><span></span><code><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">microdao_activity</span><span class="w"> </span><span class="p">(</span>
<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="n">UUID</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="n">gen_random_uuid</span><span class="p">(),</span>
<span class="w"> </span><span class="n">microdao_slug</span><span class="w"> </span><span class="nb">TEXT</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">REFERENCES</span><span class="w"> </span><span class="n">city_microdao</span><span class="p">(</span><span class="n">slug</span><span class="p">)</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="k">DELETE</span><span class="w"> </span><span class="k">CASCADE</span><span class="p">,</span>
<span class="w"> </span><span class="n">kind</span><span class="w"> </span><span class="nb">TEXT</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">CHECK</span><span class="w"> </span><span class="p">(</span><span class="n">kind</span><span class="w"> </span><span class="k">IN</span><span class="w"> </span><span class="p">(</span><span class="s1">&#39;post&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;event&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;update&#39;</span><span class="p">)),</span>
<span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span>
<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="nb">TEXT</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="p">,</span>
<span class="w"> </span><span class="n">author_agent_id</span><span class="w"> </span><span class="n">UUID</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">REFERENCES</span><span class="w"> </span><span class="n">city_agent</span><span class="p">(</span><span class="n">id</span><span class="p">),</span>
<span class="w"> </span><span class="n">author_name</span><span class="w"> </span><span class="nb">TEXT</span><span class="w"> </span><span class="k">NULL</span><span class="p">,</span>
<span class="w"> </span><span class="n">created_at</span><span class="w"> </span><span class="n">TIMESTAMPTZ</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="n">now</span><span class="p">()</span>
<span class="p">);</span>
<span class="k">CREATE</span><span class="w"> </span><span class="k">INDEX</span><span class="w"> </span><span class="n">idx_microdao_activity_microdao_created_at</span>
<span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">microdao_activity</span><span class="w"> </span><span class="p">(</span><span class="n">microdao_slug</span><span class="p">,</span><span class="w"> </span><span class="n">created_at</span><span class="w"> </span><span class="k">DESC</span><span class="p">);</span>
</code></pre></div>
<h4 id="312-city_microdao">3.1.2. Розширити <code>city_microdao</code> під метрики (опціонально, але корисно)<a class="headerlink" href="#312-city_microdao" title="Permanent link">&para;</a></h4>
<p>Міграція <code>migrations/042_microdao_stats.sql</code>:</p>
<div class="codehilite"><pre><span></span><code><span class="k">ALTER</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">city_microdao</span>
<span class="w"> </span><span class="k">ADD</span><span class="w"> </span><span class="k">COLUMN</span><span class="w"> </span><span class="k">IF</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">EXISTS</span><span class="w"> </span><span class="n">citizens_count</span><span class="w"> </span><span class="nb">INTEGER</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span>
<span class="w"> </span><span class="k">ADD</span><span class="w"> </span><span class="k">COLUMN</span><span class="w"> </span><span class="k">IF</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">EXISTS</span><span class="w"> </span><span class="n">rooms_count</span><span class="w"> </span><span class="nb">INTEGER</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span>
<span class="w"> </span><span class="k">ADD</span><span class="w"> </span><span class="k">COLUMN</span><span class="w"> </span><span class="k">IF</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">EXISTS</span><span class="w"> </span><span class="n">agents_count</span><span class="w"> </span><span class="nb">INTEGER</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span>
<span class="w"> </span><span class="k">ADD</span><span class="w"> </span><span class="k">COLUMN</span><span class="w"> </span><span class="k">IF</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">EXISTS</span><span class="w"> </span><span class="n">last_update_at</span><span class="w"> </span><span class="n">TIMESTAMPTZ</span><span class="p">;</span>
</code></pre></div>
<blockquote>
<p>На етапі MVP значення можна просто перераховувати на льоту в репозиторії й не оновлювати ці поля — але стовпці потрібні для майбутнього кешування.</p>
</blockquote>
<hr />
<h3 id="32-backend-fastapi-city-service">3.2. Backend: FastAPI (city-service)<a class="headerlink" href="#32-backend-fastapi-city-service" title="Permanent link">&para;</a></h3>
<h4 id="321-repo-python">3.2.1. Repo-рівень (Python)<a class="headerlink" href="#321-repo-python" title="Permanent link">&para;</a></h4>
<p>Файл: <code>services/city-service/repo_city.py</code></p>
<p>Додати:</p>
<div class="codehilite"><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_microdao_dashboard</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">slug</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">MicrodaoDashboard</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Aggregates:</span>
<span class="sd"> - microdao summary</span>
<span class="sd"> - last 5 activity items</span>
<span class="sd"> - up to 5 rooms</span>
<span class="sd"> - up to 6 citizens (agents) linked to this microdao</span>
<span class="sd"> &quot;&quot;&quot;</span>
</code></pre></div>
<p>Використати існуючі репо-методи:</p>
<ul>
<li>
<p><code>get_microdao_by_slug</code></p>
</li>
<li>
<p><code>get_microdao_rooms(slug)</code></p>
</li>
<li>
<p><code>get_agents_by_microdao(slug)</code> або аналог (якщо немає — додати простий SELECT по district/slug mapping).</p>
</li>
</ul>
<p>Нові helper-методи:</p>
<div class="codehilite"><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_microdao_activity</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">slug</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="n">MicrodaoActivity</span><span class="p">]:</span>
<span class="o">...</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">insert_microdao_activity</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">activity</span><span class="p">:</span> <span class="n">CreateMicrodaoActivity</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">MicrodaoActivity</span><span class="p">:</span>
<span class="o">...</span>
</code></pre></div>
<h4 id="322-pydantic-">3.2.2. Pydantic-моделі<a class="headerlink" href="#322-pydantic-" title="Permanent link">&para;</a></h4>
<p>Файл: <code>services/city-service/models_city.py</code></p>
<p>Додати:</p>
<div class="codehilite"><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">MicrodaoActivity</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">UUID</span>
<span class="n">microdao_slug</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">kind</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">title</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span>
<span class="n">body</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">author_agent_id</span><span class="p">:</span> <span class="n">UUID</span> <span class="o">|</span> <span class="kc">None</span>
<span class="n">author_name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span>
<span class="n">created_at</span><span class="p">:</span> <span class="n">datetime</span>
<span class="k">class</span><span class="w"> </span><span class="nc">CreateMicrodaoActivity</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
<span class="n">kind</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">pattern</span><span class="o">=</span><span class="s2">&quot;^(post|event|update)$&quot;</span><span class="p">)</span>
<span class="n">title</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">body</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">author_agent_id</span><span class="p">:</span> <span class="n">UUID</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">author_name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">class</span><span class="w"> </span><span class="nc">MicrodaoDashboard</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
<span class="n">microdao</span><span class="p">:</span> <span class="n">MicrodaoSummary</span>
<span class="n">stats</span><span class="p">:</span> <span class="n">MicrodaoStats</span>
<span class="n">recent_activity</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">MicrodaoActivity</span><span class="p">]</span>
<span class="n">rooms</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">RoomSummary</span><span class="p">]</span>
<span class="n">citizens</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">PublicCitizenSummary</span><span class="p">]</span>
</code></pre></div>
<p><code>MicrodaoStats</code>:</p>
<div class="codehilite"><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">MicrodaoStats</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
<span class="n">rooms_count</span><span class="p">:</span> <span class="nb">int</span>
<span class="n">citizens_count</span><span class="p">:</span> <span class="nb">int</span>
<span class="n">agents_count</span><span class="p">:</span> <span class="nb">int</span>
<span class="n">last_update_at</span><span class="p">:</span> <span class="n">datetime</span> <span class="o">|</span> <span class="kc">None</span>
</code></pre></div>
<h4 id="323-routes-fastapi">3.2.3. Routes (FastAPI)<a class="headerlink" href="#323-routes-fastapi" title="Permanent link">&para;</a></h4>
<p>Файл: <code>services/city-service/routes_city.py</code></p>
<p>Додати endpoints:</p>
<div class="codehilite"><pre><span></span><code><span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/microdao/</span><span class="si">{slug}</span><span class="s2">/dashboard&quot;</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">MicrodaoDashboard</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_microdao_dashboard</span><span class="p">(</span><span class="n">slug</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">conn</span><span class="o">=</span><span class="n">Depends</span><span class="p">(</span><span class="n">get_conn</span><span class="p">)):</span>
<span class="k">return</span> <span class="k">await</span> <span class="n">repo_city</span><span class="o">.</span><span class="n">get_microdao_dashboard</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">slug</span><span class="p">)</span>
<span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/microdao/</span><span class="si">{slug}</span><span class="s2">/activity&quot;</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="nb">list</span><span class="p">[</span><span class="n">MicrodaoActivity</span><span class="p">])</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_microdao_activity</span><span class="p">(</span><span class="n">slug</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">20</span><span class="p">,</span> <span class="n">conn</span><span class="o">=</span><span class="n">Depends</span><span class="p">(</span><span class="n">get_conn</span><span class="p">)):</span>
<span class="k">return</span> <span class="k">await</span> <span class="n">repo_city</span><span class="o">.</span><span class="n">get_microdao_activity</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">slug</span><span class="p">,</span> <span class="n">limit</span><span class="p">)</span>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/microdao/</span><span class="si">{slug}</span><span class="s2">/activity&quot;</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">MicrodaoActivity</span><span class="p">,</span> <span class="n">status_code</span><span class="o">=</span><span class="mi">201</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_microdao_activity</span><span class="p">(</span>
<span class="n">slug</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
<span class="n">payload</span><span class="p">:</span> <span class="n">CreateMicrodaoActivity</span><span class="p">,</span>
<span class="n">conn</span><span class="o">=</span><span class="n">Depends</span><span class="p">(</span><span class="n">get_conn</span><span class="p">),</span>
<span class="c1"># TODO: optional auth / orchestrator check</span>
<span class="p">):</span>
<span class="k">return</span> <span class="k">await</span> <span class="n">repo_city</span><span class="o">.</span><span class="n">create_microdao_activity</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">slug</span><span class="p">,</span> <span class="n">payload</span><span class="p">)</span>
</code></pre></div>
<p>Для MVP можна не чіпати авторизацію — припустити, що виклик робить orchestrator через адмін-UI.</p>
<hr />
<h3 id="33-frontend-nextjs-appsweb">3.3. Frontend: Next.js (apps/web)<a class="headerlink" href="#33-frontend-nextjs-appsweb" title="Permanent link">&para;</a></h3>
<h4 id="331-api-">3.3.1. API-клієнт<a class="headerlink" href="#331-api-" title="Permanent link">&para;</a></h4>
<p>Файл: <code>apps/web/src/lib/api.ts</code></p>
<p>Додати:</p>
<div class="codehilite"><pre><span></span><code><span class="k">export</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">fetchMicrodaoDashboard</span><span class="p">(</span><span class="nx">slug</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">MicrodaoDashboard</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">fetch</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">CITY_API_BASE_URL</span><span class="si">}</span><span class="sb">/microdao/</span><span class="si">${</span><span class="nx">slug</span><span class="si">}</span><span class="sb">/dashboard`</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">cache</span><span class="o">:</span><span class="w"> </span><span class="s2">&quot;no-store&quot;</span><span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nx">res</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="s2">&quot;Failed to load microdao dashboard&quot;</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">export</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">fetchMicrodaoActivity</span><span class="p">(</span><span class="nx">slug</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">MicrodaoActivity</span><span class="p">[]</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">fetch</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">CITY_API_BASE_URL</span><span class="si">}</span><span class="sb">/microdao/</span><span class="si">${</span><span class="nx">slug</span><span class="si">}</span><span class="sb">/activity?limit=20`</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">cache</span><span class="o">:</span><span class="w"> </span><span class="s2">&quot;no-store&quot;</span><span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nx">res</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="s2">&quot;Failed to load microdao activity&quot;</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div>
<p>Типи <code>MicrodaoDashboard</code> / <code>MicrodaoActivity</code> додати до <code>apps/web/src/lib/types.ts</code>.</p>
<hr />
<h4 id="332-microdaoslug">3.3.2. Структура сторінки <code>/microdao/[slug]</code><a class="headerlink" href="#332-microdaoslug" title="Permanent link">&para;</a></h4>
<p>Файл (вже існує):</p>
<p><code>apps/web/src/app/microdao/[slug]/page.tsx</code></p>
<p>Перетворити на <strong>Dashboard Layout</strong>:</p>
<ol>
<li>
<p>На сервері викликати <code>fetchMicrodaoDashboard(slug)</code>.</p>
</li>
<li>
<p>Розбити на секції:</p>
</li>
</ol>
<div class="codehilite"><pre><span></span><code><span class="p">&lt;</span><span class="nt">MicrodaoHeaderCard</span><span class="w"> </span><span class="na">dashboard</span><span class="o">=</span><span class="p">{</span><span class="nx">dashboard</span><span class="p">}</span><span class="w"> </span><span class="p">/&gt;</span><span class="w"> </span><span class="c1">// hero</span>
<span class="p">&lt;</span><span class="nt">MicrodaoDashboardGrid</span><span class="p">&gt;</span>
<span class="w"> </span><span class="p">&lt;</span><span class="nt">MicrodaoActivitySection</span><span class="w"> </span><span class="na">activity</span><span class="o">=</span><span class="p">{</span><span class="nx">dashboard</span><span class="p">.</span><span class="nx">recent_activity</span><span class="p">}</span><span class="w"> </span><span class="p">/&gt;</span>
<span class="w"> </span><span class="p">&lt;</span><span class="nt">MicrodaoRoomsSection</span><span class="w"> </span><span class="na">rooms</span><span class="o">=</span><span class="p">{</span><span class="nx">dashboard</span><span class="p">.</span><span class="nx">rooms</span><span class="p">}</span><span class="w"> </span><span class="na">slug</span><span class="o">=</span><span class="p">{</span><span class="nx">slug</span><span class="p">}</span><span class="w"> </span><span class="p">/&gt;</span>
<span class="w"> </span><span class="p">&lt;</span><span class="nt">MicrodaoTeamSection</span><span class="w"> </span><span class="na">citizens</span><span class="o">=</span><span class="p">{</span><span class="nx">dashboard</span><span class="p">.</span><span class="nx">citizens</span><span class="p">}</span><span class="w"> </span><span class="p">/&gt;</span>
<span class="w"> </span><span class="p">&lt;</span><span class="nt">MicrodaoProjectsSection</span><span class="w"> </span><span class="na">microdao</span><span class="o">=</span><span class="p">{</span><span class="nx">dashboard</span><span class="p">.</span><span class="nx">microdao</span><span class="p">}</span><span class="w"> </span><span class="p">/&gt;</span><span class="w"> </span><span class="c1">// placeholder</span>
<span class="w"> </span><span class="p">&lt;</span><span class="nt">MicrodaoTasksSection</span><span class="w"> </span><span class="na">microdao</span><span class="o">=</span><span class="p">{</span><span class="nx">dashboard</span><span class="p">.</span><span class="nx">microdao</span><span class="p">}</span><span class="w"> </span><span class="p">/&gt;</span><span class="w"> </span><span class="c1">// placeholder</span>
<span class="p">&lt;/</span><span class="nt">MicrodaoDashboardGrid</span><span class="p">&gt;</span>
</code></pre></div>
<p>Нові компоненти:</p>
<ul>
<li>
<p><code>apps/web/src/components/microdao/MicrodaoHeaderCard.tsx</code></p>
</li>
<li>
<p><code>apps/web/src/components/microdao/MicrodaoActivitySection.tsx</code></p>
</li>
<li>
<p><code>apps/web/src/components/microdao/MicrodaoTeamSection.tsx</code></p>
</li>
<li>
<p><code>apps/web/src/components/microdao/MicrodaoProjectsSection.tsx</code> (stub)</p>
</li>
<li>
<p><code>apps/web/src/components/microdao/MicrodaoTasksSection.tsx</code> (stub)</p>
</li>
</ul>
<h5 id="microdaoheadercard">MicrodaoHeaderCard<a class="headerlink" href="#microdaoheadercard" title="Permanent link">&para;</a></h5>
<p>Показати:</p>
<ul>
<li>
<p>Лого (через <code>normalizeAssetUrl</code>)</p>
</li>
<li>
<p>Назву, теги (Platform / Core / Active)</p>
</li>
<li>
<p>Метрики:</p>
</li>
<li>
<p><code>{stats.rooms_count} Кімнат</code></p>
</li>
<li>
<p><code>{stats.citizens_count} Громадян</code></p>
</li>
<li>
<p><code>{stats.agents_count} Агентів</code></p>
</li>
<li>
<p>кнопки:</p>
</li>
<li>
<p>"Поспілкуватися з DAARWIZZ" → <code>href="/city?room=city-lobby"</code></p>
</li>
<li>
<p>"Створити кімнату" → прокрутка до секції кімнат або модалка.</p>
</li>
</ul>
<h5 id="microdaoactivitysection">MicrodaoActivitySection<a class="headerlink" href="#microdaoactivitysection" title="Permanent link">&para;</a></h5>
<ul>
<li>
<p>Заголовок "Новини DAARION" (або назва DAO).</p>
</li>
<li>
<p>Список останніх 510 записів:</p>
</li>
<li>
<p>заголовок / перші рядки body,</p>
</li>
<li>
<p>дата,</p>
</li>
<li>
<p>автор (агент або author_name).</p>
</li>
<li>
<p>Якщо немає записів — показати <code>Empty state: "Поки що немає новин"</code>.</p>
</li>
</ul>
<hr />
<h3 id="34">3.4. Зв'язок з існуючими розділами<a class="headerlink" href="#34" title="Permanent link">&para;</a></h3>
<ul>
<li>
<p>MicroDAO Rooms: у компоненті <code>MicrodaoRoomsSection</code> використати вже існуючу логіку створення/видалення кімнат.</p>
</li>
<li>
<p>Citizens: <code>MicrodaoTeamSection</code> повинна показувати тільки тих громадян, у яких <code>home_microdao_slug</code> або district/room mapping вказує на цей MicroDAO. На етапі MVP можна вибрати фіксований набір (наприклад, 6 ключових агентів DAARION) через простий SQL.</p>
</li>
</ul>
<hr />
<h2 id="4-cursor">4. План виконання (для Cursor)<a class="headerlink" href="#4-cursor" title="Permanent link">&para;</a></h2>
<h3 id="1-db">Крок 1. DB міграції<a class="headerlink" href="#1-db" title="Permanent link">&para;</a></h3>
<ol>
<li>
<p>Створити <code>migrations/041_microdao_activity.sql</code> і <code>042_microdao_stats.sql</code>.</p>
</li>
<li>
<p>Запустити міграції локально й на NODE1 (MVP DB).</p>
</li>
</ol>
<h3 id="2-backend">Крок 2. Backend<a class="headerlink" href="#2-backend" title="Permanent link">&para;</a></h3>
<ol>
<li>
<p>Оновити <code>models_city.py</code> — додати моделі <code>MicrodaoActivity</code>, <code>CreateMicrodaoActivity</code>, <code>MicrodaoStats</code>, <code>MicrodaoDashboard</code>.</p>
</li>
<li>
<p>Оновити <code>repo_city.py</code> — реалізувати aggregation для <code>get_microdao_dashboard</code> та CRUD для activity.</p>
</li>
<li>
<p>Оновити <code>routes_city.py</code> — додати <code>/microdao/{slug}/dashboard</code> та <code>/microdao/{slug}/activity</code>.</p>
</li>
</ol>
<h3 id="3-frontend">Крок 3. Frontend<a class="headerlink" href="#3-frontend" title="Permanent link">&para;</a></h3>
<ol>
<li>
<p>Оновити <code>apps/web/src/lib/types.ts</code> та <code>api.ts</code> (типи й API-клієнт).</p>
</li>
<li>
<p>Переписати <code>apps/web/src/app/microdao/[slug]/page.tsx</code>, щоб вона використовувала <code>fetchMicrodaoDashboard</code>.</p>
</li>
<li>
<p>Створити компоненти:</p>
</li>
<li>
<p><code>MicrodaoHeaderCard.tsx</code></p>
</li>
<li>
<p><code>MicrodaoActivitySection.tsx</code></p>
</li>
<li>
<p><code>MicrodaoTeamSection.tsx</code></p>
</li>
<li>
<p>(stubs) <code>MicrodaoProjectsSection.tsx</code>, <code>MicrodaoTasksSection.tsx</code></p>
</li>
</ol>
<h3 id="4-daarion-dao">Крок 4. DAARION DAO як демо<a class="headerlink" href="#4-daarion-dao" title="Permanent link">&para;</a></h3>
<ol>
<li>
<p>Заповнити хоча б <strong>510 записів <code>microdao_activity</code></strong> для <code>slug='daarion'</code> (через SQL або простий admin-endpoint /seed).</p>
</li>
<li>
<p>Прив'язати ключових агентів DAARION до цього MicroDAO (update в таблиці citizens/agents).</p>
</li>
<li>
<p>Перевірити сторінку <code>/microdao/daarion</code>:</p>
</li>
<li>
<p>лого + title;</p>
</li>
<li>
<p>метрики не ламаються (0 — ок, NaN — ні);</p>
</li>
<li>
<p>відображаються кімнати DAARION;</p>
</li>
<li>
<p>видно список 6+ громадян DAARION;</p>
</li>
<li>
<p>CTA "Поспілкуватися з DAARWIZZ" працює.</p>
</li>
</ol>
<hr />
<h2 id="7">7. Що показувати на презентації<a class="headerlink" href="#7" title="Permanent link">&para;</a></h2>
<ol>
<li>
<p><strong>МікроDAO список</strong> — DAARION, Energy Union, GreenFood, Soul наверху (pin).</p>
</li>
<li>
<p><strong>DAARION DAO сторінка</strong>:</p>
</li>
<li>
<p>красивий hero-блок як "панель керування DAO";</p>
</li>
<li>
<p>блок "Новини DAARION";</p>
</li>
<li>
<p>блок "Кімнати DAARION";</p>
</li>
<li>
<p>блок "Команда DAARION".</p>
</li>
<li>
<p><strong>City Rooms</strong> — показати, що одна з кімнат — головний публічний чат (DAARION City Lobby) з DAARWIZZ.</p>
</li>
<li>
<p><strong>Citizens / Agents</strong> — як реєстр агентів, які прив'язані до DAARION.</p>
</li>
</ol>
<p>Це вже виглядає як <strong>живий кабінет DAO</strong>, а не просто сайт.</p>
<hr />
<p>Якщо хочеш, наступним кроком можу згенерувати окремий <code>TASK_PHASE_MICRODAO_DASHBOARD_v1_CURSOR_PROMPT.md</code> з готовим промтом для Cursor (щоб він сам крок-за-кроком виконав усе з цього таска).</p>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"base": "../..", "features": ["navigation.sections", "navigation.instant", "content.code.copy"], "search": "../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
<script src="../../assets/javascripts/bundle.3220b9d7.min.js"></script>
</body>
</html>