feat: Implementar funcionalidades de Tarefas e Saúde

- Criadas APIs para Health (métricas de saúde)
  * Registrar peso, altura, % gordura, medidas
  * Histórico completo de medições
  * Estatísticas e resumo

- Criadas APIs para Tasks (tarefas)
  * Criar, editar e deletar tarefas
  * Filtros por status e data
  * Estatísticas detalhadas
  * Prioridades (baixa, média, alta)

- Frontend implementado:
  * Página Health.tsx - registro de métricas
  * Página Tasks.tsx - gerenciamento de tarefas
  * Página Progress.tsx - visualização de progresso
  * Dashboard integrado com estatísticas reais

- Schemas e modelos atualizados
- Todas as funcionalidades testadas e operacionais
This commit is contained in:
Sergio Correa
2025-11-22 02:33:15 +00:00
commit f50174f898
68 changed files with 6835 additions and 0 deletions

218
backend/app/api/tasks.py Normal file
View File

@@ -0,0 +1,218 @@
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from typing import List, Optional
from datetime import date
from app.core.database import get_db
from app.core.security import decode_access_token
from app.models.task import Task
from app.schemas.task import TaskCreate, TaskResponse, TaskUpdate
router = APIRouter()
security = HTTPBearer()
def get_current_user_id(credentials: HTTPAuthorizationCredentials = Depends(security)) -> str:
"""Extrai user_id do token JWT"""
token = credentials.credentials
payload = decode_access_token(token)
if not payload:
raise HTTPException(status_code=401, detail="Token inválido")
return payload.get("sub")
@router.post("/", response_model=TaskResponse, status_code=status.HTTP_201_CREATED)
async def create_task(
task_data: TaskCreate,
user_id: str = Depends(get_current_user_id),
db: Session = Depends(get_db)
):
"""Criar nova tarefa"""
new_task = Task(
user_id=user_id,
title=task_data.title,
description=task_data.description,
priority=task_data.priority,
status=task_data.status,
due_date=task_data.due_date,
due_time=task_data.due_time,
category_id=task_data.category_id
)
db.add(new_task)
db.commit()
db.refresh(new_task)
return new_task
@router.get("/", response_model=List[TaskResponse])
async def list_tasks(
user_id: str = Depends(get_current_user_id),
db: Session = Depends(get_db),
status_filter: Optional[str] = None,
include_archived: bool = False
):
"""Listar todas as tarefas do usuário"""
query = db.query(Task).filter(Task.user_id == user_id)
if not include_archived:
query = query.filter(Task.is_archived == False)
if status_filter:
query = query.filter(Task.status == status_filter)
tasks = query.order_by(Task.due_date.asc()).all()
return tasks
@router.get("/today", response_model=List[TaskResponse])
async def get_today_tasks(
user_id: str = Depends(get_current_user_id),
db: Session = Depends(get_db)
):
"""Obter tarefas de hoje"""
today = date.today()
tasks = db.query(Task).filter(
Task.user_id == user_id,
Task.due_date == today,
Task.is_archived == False
).order_by(Task.due_time.asc()).all()
return tasks
@router.get("/{task_id}", response_model=TaskResponse)
async def get_task(
task_id: str,
user_id: str = Depends(get_current_user_id),
db: Session = Depends(get_db)
):
"""Obter tarefa específica"""
task = db.query(Task).filter(
Task.id == task_id,
Task.user_id == user_id
).first()
if not task:
raise HTTPException(status_code=404, detail="Tarefa não encontrada")
return task
@router.put("/{task_id}", response_model=TaskResponse)
async def update_task(
task_id: str,
task_data: TaskUpdate,
user_id: str = Depends(get_current_user_id),
db: Session = Depends(get_db)
):
"""Atualizar tarefa"""
task = db.query(Task).filter(
Task.id == task_id,
Task.user_id == user_id
).first()
if not task:
raise HTTPException(status_code=404, detail="Tarefa não encontrada")
# Atualizar campos
update_data = task_data.dict(exclude_unset=True)
for field, value in update_data.items():
setattr(task, field, value)
db.commit()
db.refresh(task)
return task
@router.patch("/{task_id}/status")
async def update_task_status(
task_id: str,
status: str,
user_id: str = Depends(get_current_user_id),
db: Session = Depends(get_db)
):
"""Atualizar status da tarefa"""
task = db.query(Task).filter(
Task.id == task_id,
Task.user_id == user_id
).first()
if not task:
raise HTTPException(status_code=404, detail="Tarefa não encontrada")
task.status = status
db.commit()
return {"message": "Status atualizado", "status": status}
@router.delete("/{task_id}")
async def delete_task(
task_id: str,
user_id: str = Depends(get_current_user_id),
db: Session = Depends(get_db)
):
"""Deletar tarefa"""
task = db.query(Task).filter(
Task.id == task_id,
Task.user_id == user_id
).first()
if not task:
raise HTTPException(status_code=404, detail="Tarefa não encontrada")
db.delete(task)
db.commit()
return {"message": "Tarefa deletada com sucesso"}
@router.get("/stats/summary")
async def get_task_stats(
user_id: str = Depends(get_current_user_id),
db: Session = Depends(get_db)
):
"""Estatísticas de tarefas"""
from sqlalchemy import func
total_tasks = db.query(func.count(Task.id)).filter(
Task.user_id == user_id,
Task.is_archived == False
).scalar()
pending = db.query(func.count(Task.id)).filter(
Task.user_id == user_id,
Task.status == "pending",
Task.is_archived == False
).scalar()
in_progress = db.query(func.count(Task.id)).filter(
Task.user_id == user_id,
Task.status == "in_progress",
Task.is_archived == False
).scalar()
completed = db.query(func.count(Task.id)).filter(
Task.user_id == user_id,
Task.status == "completed",
Task.is_archived == False
).scalar()
today = date.today()
today_tasks = db.query(func.count(Task.id)).filter(
Task.user_id == user_id,
Task.due_date == today,
Task.is_archived == False
).scalar()
today_completed = db.query(func.count(Task.id)).filter(
Task.user_id == user_id,
Task.due_date == today,
Task.status == "completed"
).scalar()
return {
"total_tasks": total_tasks,
"pending": pending,
"in_progress": in_progress,
"completed": completed,
"today_tasks": today_tasks,
"today_completed": today_completed
}