feat(KIN-012): UI auto/review mode toggle, autopilot indicator, persist project mode in DB
- TaskDetail: hide Approve/Reject buttons in auto mode, show "Автопилот активен" badge
- TaskDetail: execution_mode persisted per-task via PATCH /api/tasks/{id}
- TaskDetail: loadMode reads DB value, falls back to localStorage per project
- TaskDetail: back navigation preserves status filter via ?back_status query param
- ProjectView: toggleMode now persists to DB via PATCH /api/projects/{id}
- ProjectView: loadMode reads project.execution_mode from DB first
- ProjectView: task list shows 🔓 badge for auto-mode tasks
- ProjectView: status filter synced to URL query param ?status=
- api.ts: add patchProject(), execution_mode field on Project interface
- core/db.py, core/models.py: execution_mode columns + migration for projects & tasks
- web/api.py: PATCH /api/projects/{id} and PATCH /api/tasks/{id} support execution_mode
- tests: 256 tests pass, new test_auto_mode.py with 60+ auto mode tests
- frontend: vitest config added for component tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3cb516193b
commit
4a27bf0693
12 changed files with 2698 additions and 30 deletions
36
web/api.py
36
web/api.py
|
|
@ -76,6 +76,25 @@ class ProjectCreate(BaseModel):
|
|||
priority: int = 5
|
||||
|
||||
|
||||
class ProjectPatch(BaseModel):
|
||||
execution_mode: str
|
||||
|
||||
|
||||
@app.patch("/api/projects/{project_id}")
|
||||
def patch_project(project_id: str, body: ProjectPatch):
|
||||
if body.execution_mode not in VALID_EXECUTION_MODES:
|
||||
raise HTTPException(400, f"Invalid execution_mode '{body.execution_mode}'. Must be one of: {', '.join(VALID_EXECUTION_MODES)}")
|
||||
conn = get_conn()
|
||||
p = models.get_project(conn, project_id)
|
||||
if not p:
|
||||
conn.close()
|
||||
raise HTTPException(404, f"Project '{project_id}' not found")
|
||||
models.update_project(conn, project_id, execution_mode=body.execution_mode)
|
||||
p = models.get_project(conn, project_id)
|
||||
conn.close()
|
||||
return p
|
||||
|
||||
|
||||
@app.post("/api/projects")
|
||||
def create_project(body: ProjectCreate):
|
||||
conn = get_conn()
|
||||
|
|
@ -138,22 +157,33 @@ def create_task(body: TaskCreate):
|
|||
|
||||
|
||||
class TaskPatch(BaseModel):
|
||||
status: str
|
||||
status: str | None = None
|
||||
execution_mode: str | None = None
|
||||
|
||||
|
||||
VALID_STATUSES = {"pending", "in_progress", "review", "done", "blocked", "cancelled"}
|
||||
VALID_EXECUTION_MODES = {"auto", "review"}
|
||||
|
||||
|
||||
@app.patch("/api/tasks/{task_id}")
|
||||
def patch_task(task_id: str, body: TaskPatch):
|
||||
if body.status not in VALID_STATUSES:
|
||||
if body.status is not None and body.status not in VALID_STATUSES:
|
||||
raise HTTPException(400, f"Invalid status '{body.status}'. Must be one of: {', '.join(VALID_STATUSES)}")
|
||||
if body.execution_mode is not None and body.execution_mode not in VALID_EXECUTION_MODES:
|
||||
raise HTTPException(400, f"Invalid execution_mode '{body.execution_mode}'. Must be one of: {', '.join(VALID_EXECUTION_MODES)}")
|
||||
if body.status is None and body.execution_mode is None:
|
||||
raise HTTPException(400, "Nothing to update. Provide status or execution_mode.")
|
||||
conn = get_conn()
|
||||
t = models.get_task(conn, task_id)
|
||||
if not t:
|
||||
conn.close()
|
||||
raise HTTPException(404, f"Task '{task_id}' not found")
|
||||
models.update_task(conn, task_id, status=body.status)
|
||||
fields = {}
|
||||
if body.status is not None:
|
||||
fields["status"] = body.status
|
||||
if body.execution_mode is not None:
|
||||
fields["execution_mode"] = body.execution_mode
|
||||
models.update_task(conn, task_id, **fields)
|
||||
t = models.get_task(conn, task_id)
|
||||
conn.close()
|
||||
return t
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue