From a73f1b143b0b439d65e0e7c543a0823f440abb57 Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Mon, 20 Jul 2026 22:11:50 +0700 Subject: [PATCH] Phase 5: Tasks API + Kanban board + Card UI --- backend/api/tasks.py | 32 +++++++++++++ backend/main.py | 3 +- backend/models.py | 9 ++++ frontend/src/components/tasks/Kanban.tsx | 55 +++++++++++----------- frontend/src/components/tasks/TaskCard.tsx | 29 ++++++++++++ frontend/src/components/ui/card.tsx | 14 ++++++ 6 files changed, 113 insertions(+), 29 deletions(-) create mode 100644 backend/api/tasks.py create mode 100644 frontend/src/components/tasks/TaskCard.tsx create mode 100644 frontend/src/components/ui/card.tsx diff --git a/backend/api/tasks.py b/backend/api/tasks.py new file mode 100644 index 0000000..d0b03fe --- /dev/null +++ b/backend/api/tasks.py @@ -0,0 +1,32 @@ +from fastapi import APIRouter +from typing import List +from models import Task + +router = APIRouter(prefix="/api/v1", tags=["tasks"]) + +tasks_db: List[Task] = [ + Task(id="t1", title="Реализовать EMA-кроссовер", description="Бэктест стратегии на Brent", status="in_progress", priority="high", assignee_agent_id="worker-code", project_id="p1"), + Task(id="t2", title="Подготовить отчёт", description="Результаты бэктеста за Q3", status="todo", priority="medium", project_id="p1"), + Task(id="t3", title="Настроить CI/CD", description="GitHub Actions для автотестов", status="review", priority="high", assignee_agent_id="worker-fast", project_id="p1"), + Task(id="t4", title="Обновить вики", description="Документация по API", status="done", priority="low", project_id="p1"), + Task(id="t5", title="Добавить OAuth", description="GitHub + Google интеграция", status="todo", priority="medium", project_id="p1"), +] + +@router.get("/tasks", response_model=List[Task]) +async def get_tasks(): + return tasks_db + +@router.post("/tasks", response_model=Task) +async def create_task(task: Task): + tasks_db.append(task) + return task + +@router.patch("/tasks/{task_id}") +async def update_task(task_id: str, update: dict): + for task in tasks_db: + if task.id == task_id: + for key, val in update.items(): + if hasattr(task, key): + setattr(task, key, val) + return task + return {"error": "Task not found"} diff --git a/backend/main.py b/backend/main.py index 58cba55..9f816d1 100644 --- a/backend/main.py +++ b/backend/main.py @@ -8,7 +8,7 @@ from pathlib import Path from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles -from api import runs, agents, memory, skills, connectors +from api import runs, agents, memory, skills, connectors, tasks import asyncio, json app = FastAPI( @@ -22,6 +22,7 @@ app.include_router(agents.router) app.include_router(memory.router) app.include_router(skills.router) app.include_router(connectors.router) +app.include_router(tasks.router) # WebSocket для real-time обновлений @app.websocket("/ws") diff --git a/backend/models.py b/backend/models.py index 2c3d8fb..c7009a3 100644 --- a/backend/models.py +++ b/backend/models.py @@ -11,3 +11,12 @@ class Agent(BaseModel): class AgentUpdate(BaseModel): status: Literal["online", "offline", "busy"] + +class Task(BaseModel): + id: str + title: str + description: str = "" + status: Literal["todo", "in_progress", "review", "done"] = "todo" + priority: Literal["low", "medium", "high"] = "medium" + assignee_agent_id: Optional[str] = None + project_id: str = "default" diff --git a/frontend/src/components/tasks/Kanban.tsx b/frontend/src/components/tasks/Kanban.tsx index c33d201..8be495b 100644 --- a/frontend/src/components/tasks/Kanban.tsx +++ b/frontend/src/components/tasks/Kanban.tsx @@ -1,37 +1,36 @@ -import { CheckCircle2, Circle, Plus } from 'lucide-react' -import { useState } from 'react' +import { TaskCard } from './TaskCard' +import { useQuery } from '@tanstack/react-query' -interface Task { id: string; title: string; done: boolean; project: string } +const columns = ['todo', 'in_progress', 'review', 'done'] export function Kanban() { - const [tasks] = useState([ - { id: '1', title: 'Реализовать EMA-кроссовер', done: true, project: 'Trading' }, - { id: '2', title: 'Настроить CI/CD пайплайн', done: false, project: 'ACC' }, - { id: '3', title: 'Добавить health-check эндпоинт', done: true, project: 'ACC' }, - { id: '4', title: 'Обновить документацию API', done: false, project: 'ACC' }, - ]) + const { data: tasks = [] } = useQuery({ + queryKey: ['tasks'], + queryFn: async () => { + const res = await fetch('/api/v1/tasks') + return res.json() + }, + }) return (
-
-

Tasks

- -
-
- {tasks.map(task => ( -
- {task.done - ? - : - } - - {task.title} - - - {task.project} - +

Project Kanban

+
+ {columns.map((status) => ( +
+

+ {status.replace('_', ' ')} + + {tasks.filter((t: any) => t.status === status).length} + +

+
+ {tasks + .filter((t: any) => t.status === status) + .map((task: any) => ( + + ))} +
))}
diff --git a/frontend/src/components/tasks/TaskCard.tsx b/frontend/src/components/tasks/TaskCard.tsx new file mode 100644 index 0000000..512623b --- /dev/null +++ b/frontend/src/components/tasks/TaskCard.tsx @@ -0,0 +1,29 @@ +import { Card } from '@/components/ui/card' +import { Badge } from '@/components/ui/badge' + +const statusColors: Record = { + todo: 'bg-zinc-700', + in_progress: 'bg-blue-600', + review: 'bg-amber-600', + done: 'bg-emerald-600', +} + +export function TaskCard({ task }: { task: any }) { + return ( + +
+

{task.title}

+ {task.status.replace('_', ' ')} +
+ {task.description && ( +

{task.description}

+ )} + {task.assignee_agent_id && ( +
+ + {task.assignee_agent_id} +
+ )} +
+ ) +} diff --git a/frontend/src/components/ui/card.tsx b/frontend/src/components/ui/card.tsx new file mode 100644 index 0000000..08b996c --- /dev/null +++ b/frontend/src/components/ui/card.tsx @@ -0,0 +1,14 @@ +import { cn } from '@/lib/utils' + +interface CardProps { + className?: string + children: React.ReactNode +} + +export function Card({ className, children }: CardProps) { + return ( +
+ {children} +
+ ) +}