From a54a7b078c4b836db3c90804d0a637d4be54ce50 Mon Sep 17 00:00:00 2001 From: Apple Date: Sat, 15 Nov 2025 09:00:59 -0800 Subject: [PATCH] feat: add Console UI for MicroDAO management - Create ConsolePage with navigation - Add WalletInfo component (balance display and access checks) - Add CreateMicroDaoForm (with balance validation) - Add MicroDaoList component (display teams/MicroDAO) - Add InviteMemberForm (with balance checks for admin/member) - Add wallet API client - Update teams API with inviteMember function - Add /console route to App.tsx --- CONSOLE_UI_SUMMARY.md | 137 ++++++++++++ src/App.tsx | 2 + src/api/teams.ts | 25 ++- src/api/wallet.ts | 11 + src/components/console/CreateMicroDaoForm.tsx | 204 ++++++++++++++++++ src/components/console/InviteMemberForm.tsx | 148 +++++++++++++ src/components/console/MicroDaoList.tsx | 114 ++++++++++ src/components/console/WalletInfo.tsx | 91 ++++++++ src/pages/ConsolePage.tsx | 106 +++++++++ 9 files changed, 834 insertions(+), 4 deletions(-) create mode 100644 CONSOLE_UI_SUMMARY.md create mode 100644 src/api/wallet.ts create mode 100644 src/components/console/CreateMicroDaoForm.tsx create mode 100644 src/components/console/InviteMemberForm.tsx create mode 100644 src/components/console/MicroDaoList.tsx create mode 100644 src/components/console/WalletInfo.tsx create mode 100644 src/pages/ConsolePage.tsx diff --git a/CONSOLE_UI_SUMMARY.md b/CONSOLE_UI_SUMMARY.md new file mode 100644 index 00000000..143efffa --- /dev/null +++ b/CONSOLE_UI_SUMMARY.md @@ -0,0 +1,137 @@ +# Console UI - Підсумок реалізації + +## ✅ Що створено + +### 1. Сторінка Console (`/console`) +- **Файл:** `src/pages/ConsolePage.tsx` +- **Маршрут:** `/console` +- **Функціонал:** + - Навігація між списком MicroDAO та створенням + - Відображення Wallet інформації + - Управління MicroDAO + +### 2. Компоненти Console + +#### WalletInfo +- **Файл:** `src/components/console/WalletInfo.tsx` +- **Функціонал:** + - Відображення балансів DAARION та DAAR + - Перевірка можливості створення MicroDAO (≥ 1.00 DAARION) + - Перевірка ролі Admin (≥ 1.00 DAARION) + - Перевірка можливості використання сервісу (≥ 0.01 DAARION) + +#### CreateMicroDaoForm +- **Файл:** `src/components/console/CreateMicroDaoForm.tsx` +- **Функціонал:** + - Форма створення MicroDAO + - Автоматична перевірка балансу перед створенням + - Генерація slug з назви + - Вибір типу (community, guild, lab, personal) + - Вибір режиму (public, confidential) + +#### MicroDaoList +- **Файл:** `src/components/console/MicroDaoList.tsx` +- **Функціонал:** + - Відображення списку MicroDAO + - Відображення типу та режиму + - Позначка для DAARION.city (type='city') + - Можливість вибору MicroDAO для запрошення + +#### InviteMemberForm +- **Файл:** `src/components/console/InviteMemberForm.tsx` +- **Функціонал:** + - Форма запрошення користувача + - Перевірка балансу Admin (≥ 1.00 DAARION) + - Вибір ролі (admin/member) + - Відображення вимог до балансу запрошеного користувача + +### 3. API функції + +#### Wallet API +- **Файл:** `src/api/wallet.ts` +- **Функції:** + - `getBalances()` - отримання балансів користувача + +#### Teams API (оновлено) +- **Файл:** `src/api/teams.ts` +- **Додано:** + - `inviteMember()` - запрошення користувача в MicroDAO + - Оновлено URL endpoints на `/api/v1/teams` + +--- + +## 🎨 UI/UX Особливості + +### Дизайн +- Використовує Tailwind CSS +- Адаптивний layout (grid на великих екранах) +- Кольорові індикатори статусу балансу +- Інформативні повідомлення про помилки + +### Валідація +- Перевірка балансу перед створенням MicroDAO +- Перевірка балансу перед запрошенням +- Валідація форми (обов'язкові поля, email формат) +- Автоматична генерація slug + +### Користувацький досвід +- Чіткі індикатори можливостей (✓/✗) +- Пояснення вимог до балансу +- Можливість оновлення балансу +- Навігація між різними режимами + +--- + +## 📋 Правила доступу (відображені в UI) + +### Створення MicroDAO +- **Потрібно:** ≥ 1.00 DAARION на балансі +- **Відображення:** Зелений індикатор в WalletInfo та CreateMicroDaoForm + +### Роль Admin +- **Потрібно:** ≥ 1.00 DAARION на балансі +- **Відображення:** Зелений індикатор в WalletInfo + +### Запрошення користувача +- **Admin потрібно:** ≥ 1.00 DAARION на балансі +- **Запрошений Admin:** ≥ 1.00 DAARION на балансі +- **Запрошений Member:** ≥ 0.01 DAARION на балансі +- **Відображення:** Індикатори в InviteMemberForm + +### Використання сервісу +- **Потрібно:** ≥ 0.01 DAARION на балансі +- **Відображення:** Зелений індикатор в WalletInfo + +--- + +## 🔗 Інтеграція + +### Маршрути +- `/console` - головна сторінка Console +- Додано в `src/App.tsx` + +### API Endpoints +- `GET /api/v1/wallet/balances` - отримання балансів +- `GET /api/v1/teams` - список MicroDAO +- `POST /api/v1/teams` - створення MicroDAO +- `POST /api/v1/teams/:teamId/members` - запрошення користувача + +--- + +## 🚀 Наступні кроки + +### Backend +- [ ] Реалізувати реальну інтеграцію з БД для teams +- [ ] Реалізувати отримання user_id з email при запрошенні +- [ ] Додати створення team_member record при запрошенні + +### Frontend +- [ ] Додати оновлення списку MicroDAO після створення +- [ ] Додати детальну сторінку MicroDAO +- [ ] Додати управління налаштуваннями MicroDAO +- [ ] Додати відображення членів MicroDAO + +--- + +**Останнє оновлення:** 2024-11-14 + diff --git a/src/App.tsx b/src/App.tsx index 5e3d1d1a..9505e38d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,11 +1,13 @@ import React from 'react'; import { Routes, Route } from 'react-router-dom'; import { OnboardingPage } from './pages/OnboardingPage'; +import { ConsolePage } from './pages/ConsolePage'; function App() { return ( } /> + } /> Home - Coming soon} /> ); diff --git a/src/api/teams.ts b/src/api/teams.ts index 6d10b1f7..6a1bde07 100644 --- a/src/api/teams.ts +++ b/src/api/teams.ts @@ -2,18 +2,35 @@ import { apiGet, apiPost, apiPatch } from './client'; import type { Team, CreateTeamRequest, UpdateTeamRequest } from '../types/api'; export async function createTeam(data: CreateTeamRequest): Promise { - return apiPost('/teams', data); + return apiPost('/api/v1/teams', data); } export async function getTeams(): Promise<{ teams: Team[] }> { - return apiGet<{ teams: Team[] }>('/teams'); + return apiGet<{ teams: Team[] }>('/api/v1/teams'); } export async function getTeam(teamId: string): Promise { - return apiGet(`/teams/${teamId}`); + return apiGet(`/api/v1/teams/${teamId}`); } export async function updateTeam(teamId: string, data: UpdateTeamRequest): Promise { - return apiPatch(`/teams/${teamId}`, data); + return apiPatch(`/api/v1/teams/${teamId}`, data); +} + +export interface InviteMemberRequest { + email: string; + role: 'admin' | 'member'; +} + +export interface InviteMemberResponse { + team_id: string; + user_id: string; + email: string; + role: string; + status: string; +} + +export async function inviteMember(teamId: string, data: InviteMemberRequest): Promise { + return apiPost(`/api/v1/teams/${teamId}/members`, data); } diff --git a/src/api/wallet.ts b/src/api/wallet.ts new file mode 100644 index 00000000..c5582125 --- /dev/null +++ b/src/api/wallet.ts @@ -0,0 +1,11 @@ +import { apiGet } from './client'; +import type { Balance } from '../domain/wallet/types'; + +export interface WalletBalancesResponse { + balances: Balance[]; +} + +export async function getBalances(): Promise { + return apiGet('/api/v1/wallet/balances'); +} + diff --git a/src/components/console/CreateMicroDaoForm.tsx b/src/components/console/CreateMicroDaoForm.tsx new file mode 100644 index 00000000..871ac179 --- /dev/null +++ b/src/components/console/CreateMicroDaoForm.tsx @@ -0,0 +1,204 @@ +import React, { useState, useEffect } from 'react'; +import { createTeam } from '../../api/teams'; +import { getBalances } from '../../api/wallet'; +import type { Team, CreateTeamRequest } from '../../types/api'; +import type { Balance } from '../../domain/wallet/types'; + +interface CreateMicroDaoFormProps { + onSuccess?: (team: Team) => void; + onCancel?: () => void; +} + +export function CreateMicroDaoForm({ onSuccess, onCancel }: CreateMicroDaoFormProps) { + const [name, setName] = useState(''); + const [slug, setSlug] = useState(''); + const [description, setDescription] = useState(''); + const [mode, setMode] = useState<'public' | 'confidential'>('public'); + const [type, setType] = useState<'community' | 'guild' | 'lab' | 'personal'>('community'); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [canCreate, setCanCreate] = useState(false); + const [daarionBalance, setDaarionBalance] = useState(0); + + useEffect(() => { + checkBalance(); + }, []); + + const checkBalance = async () => { + try { + const data = await getBalances(); + const daarion = data.balances?.find(b => b.symbol === 'DAARION'); + const balance = daarion ? parseFloat(daarion.amount) : 0; + setDaarionBalance(balance); + setCanCreate(balance >= 1.0); + } catch (err: any) { + setError('Помилка перевірки балансу: ' + err.message); + } + }; + + const generateSlug = (name: string) => { + return name + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, ''); + }; + + const handleNameChange = (e: React.ChangeEvent) => { + const newName = e.target.value; + setName(newName); + if (!slug || slug === generateSlug(name)) { + setSlug(generateSlug(newName)); + } + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!canCreate) { + setError('Недостатньо DAARION на балансі. Потрібно мінімум 1.00 DAARION'); + return; + } + + setLoading(true); + setError(null); + + try { + const request: CreateTeamRequest = { + name, + slug: slug || generateSlug(name), + description: description || undefined, + mode, + type, + }; + + const team = await createTeam(request); + onSuccess?.(team); + } catch (err: any) { + setError(err.message || 'Помилка створення MicroDAO'); + } finally { + setLoading(false); + } + }; + + return ( +
+

Створити MicroDAO

+ + {/* Balance Check */} +
+
+ + {canCreate ? '✓' : '✗'} + + + Баланс DAARION: {daarionBalance.toFixed(2)} + {!canCreate && ' (потрібно ≥ 1.00)'} + +
+
+ + {error && ( +
+ {error} +
+ )} + +
+
+ + +
+ +
+ + setSlug(e.target.value)} + required + pattern="[a-z0-9-]+" + className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" + placeholder="my-community" + /> +

+ Тільки маленькі літери, цифри та дефіси +

+
+ +
+ +