diff --git a/backend/app/main.py b/backend/app/main.py index 5e0f94d..7640ff3 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -144,6 +144,47 @@ def delete_task(task_id: int, db: Session = Depends(get_db)): return None +# ========== SEARCH ENDPOINT ========== + +@app.get("/api/search", response_model=List[schemas.Task]) +def search_tasks( + query: str, + project_ids: Optional[str] = None, + db: Session = Depends(get_db) +): + """ + Search tasks across projects by title, description, and tags. + + Args: + query: Search term to match against title, description, and tags + project_ids: Comma-separated list of project IDs to search in (optional, searches all if not provided) + """ + # Parse project IDs if provided + project_id_list = None + if project_ids: + try: + project_id_list = [int(pid.strip()) for pid in project_ids.split(',') if pid.strip()] + except ValueError: + raise HTTPException(status_code=400, detail="Invalid project_ids format") + + # Build query + tasks_query = db.query(models.Task) + + # Filter by project IDs if specified + if project_id_list: + tasks_query = tasks_query.filter(models.Task.project_id.in_(project_id_list)) + + # Search in title, description, and tags + search_term = f"%{query}%" + tasks = tasks_query.filter( + (models.Task.title.ilike(search_term)) | + (models.Task.description.ilike(search_term)) | + (models.Task.tags.contains([query])) # Exact tag match + ).all() + + return tasks + + # ========== JSON IMPORT ENDPOINT ========== def _import_tasks_recursive( @@ -161,6 +202,9 @@ def _import_tasks_recursive( title=task_data.title, description=task_data.description, status=task_data.status, + estimated_minutes=task_data.estimated_minutes, + tags=task_data.tags, + flag_color=task_data.flag_color, sort_order=idx ) db_task = crud.create_task(db, task) diff --git a/backend/app/models.py b/backend/app/models.py index 350b875..47455c5 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Enum +from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Enum, JSON from sqlalchemy.orm import relationship from datetime import datetime import enum @@ -34,6 +34,9 @@ class Task(Base): description = Column(Text, nullable=True) status = Column(Enum(TaskStatus), default=TaskStatus.BACKLOG, nullable=False) sort_order = Column(Integer, default=0) + estimated_minutes = Column(Integer, nullable=True) + tags = Column(JSON, nullable=True) + flag_color = Column(String(50), nullable=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) diff --git a/backend/app/schemas.py b/backend/app/schemas.py index f04e3d2..00aa35d 100644 --- a/backend/app/schemas.py +++ b/backend/app/schemas.py @@ -11,6 +11,9 @@ class TaskBase(BaseModel): status: TaskStatus = TaskStatus.BACKLOG parent_task_id: Optional[int] = None sort_order: int = 0 + estimated_minutes: Optional[int] = None + tags: Optional[List[str]] = None + flag_color: Optional[str] = None class TaskCreate(TaskBase): @@ -23,6 +26,9 @@ class TaskUpdate(BaseModel): status: Optional[TaskStatus] = None parent_task_id: Optional[int] = None sort_order: Optional[int] = None + estimated_minutes: Optional[int] = None + tags: Optional[List[str]] = None + flag_color: Optional[str] = None class Task(TaskBase): @@ -74,6 +80,9 @@ class ImportSubtask(BaseModel): title: str description: Optional[str] = None status: TaskStatus = TaskStatus.BACKLOG + estimated_minutes: Optional[int] = None + tags: Optional[List[str]] = None + flag_color: Optional[str] = None subtasks: List['ImportSubtask'] = [] diff --git a/example-import.json b/example-import.json index 22b1046..7742a18 100644 --- a/example-import.json +++ b/example-import.json @@ -8,32 +8,48 @@ "title": "Cortex Rewire", "description": "Refactor reasoning layer for improved performance", "status": "backlog", + "estimated_minutes": 240, + "tags": ["coding", "backend", "refactoring"], + "flag_color": "red", "subtasks": [ { "title": "Reflection → fix backend argument bug", "status": "in_progress", + "estimated_minutes": 90, + "tags": ["coding", "bug-fix"], + "flag_color": "orange", "subtasks": [ { "title": "Normalize LLM backend arg in reflection calls", - "status": "in_progress" + "status": "in_progress", + "estimated_minutes": 45, + "tags": ["coding"] }, { "title": "Add unit tests for reflection module", - "status": "backlog" + "status": "backlog", + "estimated_minutes": 60, + "tags": ["testing", "coding"] } ] }, { "title": "Reasoning parser cleanup", "status": "backlog", + "estimated_minutes": 120, + "tags": ["coding", "cleanup"], "subtasks": [ { "title": "Remove deprecated parse methods", - "status": "backlog" + "status": "backlog", + "estimated_minutes": 30, + "tags": ["coding"] }, { "title": "Optimize regex patterns", - "status": "backlog" + "status": "backlog", + "estimated_minutes": 45, + "tags": ["coding", "performance"] } ] } @@ -43,32 +59,47 @@ "title": "Frontend Overhaul", "description": "Modernize the UI with new component library", "status": "backlog", + "estimated_minutes": 480, + "tags": ["frontend", "ui", "coding"], + "flag_color": "blue", "subtasks": [ { "title": "Migrate to Tailwind CSS", - "status": "backlog" + "status": "backlog", + "estimated_minutes": 180, + "tags": ["frontend", "styling"] }, { "title": "Build new component library", "status": "backlog", + "estimated_minutes": 360, + "tags": ["frontend", "components"], "subtasks": [ { "title": "Button components", - "status": "backlog" + "status": "backlog", + "estimated_minutes": 60, + "tags": ["frontend", "components"] }, { "title": "Form components", - "status": "backlog" + "status": "backlog", + "estimated_minutes": 120, + "tags": ["frontend", "components"] }, { "title": "Modal components", - "status": "backlog" + "status": "backlog", + "estimated_minutes": 90, + "tags": ["frontend", "components"] } ] }, { "title": "Implement dark mode toggle", - "status": "backlog" + "status": "backlog", + "estimated_minutes": 45, + "tags": ["frontend", "ui"] } ] }, @@ -76,36 +107,54 @@ "title": "API v2 Implementation", "status": "blocked", "description": "Blocked on database migration completion", + "estimated_minutes": 600, + "tags": ["backend", "api", "coding"], + "flag_color": "yellow", "subtasks": [ { "title": "Design new REST endpoints", - "status": "done" + "status": "done", + "estimated_minutes": 120, + "tags": ["design", "api"] }, { "title": "Implement GraphQL layer", - "status": "blocked" + "status": "blocked", + "estimated_minutes": 300, + "tags": ["backend", "graphql", "coding"] }, { "title": "Add rate limiting", - "status": "backlog" + "status": "backlog", + "estimated_minutes": 90, + "tags": ["backend", "security"] } ] }, { "title": "Documentation Sprint", "status": "done", + "estimated_minutes": 180, + "tags": ["documentation", "writing"], + "flag_color": "green", "subtasks": [ { "title": "API documentation", - "status": "done" + "status": "done", + "estimated_minutes": 60, + "tags": ["documentation"] }, { "title": "User guide", - "status": "done" + "status": "done", + "estimated_minutes": 90, + "tags": ["documentation", "tutorial"] }, { "title": "Developer setup guide", - "status": "done" + "status": "done", + "estimated_minutes": 30, + "tags": ["documentation"] } ] } diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 5533d68..79d44d8 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -7,10 +7,15 @@ function App() {