Fix project/milestone association and redesign AppHeader navigation
Backend: - routes/tasks.py: POST + PUT were silently dropping project_id, milestone_id, parent_id from request body — root cause of association not saving from the task editor - routes/tasks.py: GET /api/tasks/:id now includes parent_title in response (secondary lookup when parent_id is set) - routes/notes.py: add PATCH /api/notes/:id for partial updates (used by sub-task status toggle; PUT already existed but PATCH was missing) - routes/projects.py: GET /api/projects/:id/notes now fetches milestone IDs and passes them via milestone_ids so tasks assigned to a milestone (but lacking project_id) are included in the project view - services/notes.py: create_note() auto-sets project_id from milestone when milestone_id is provided and project_id is omitted; list_notes() gains milestone_ids param — when combined with project_id uses OR condition (project_id=X OR milestone_id IN (...)) Frontend: - NoteEditorView: add MilestoneSelector; milestone resets when project changes; all save paths (save/create/auto-save) include milestone_id - stores/notes.ts: add milestone_id to createNote + updateNote types - TaskEditorView: sub-tasks now inherit milestone_id from parent task - AppHeader: three-zone layout — brand left, Notes/Projects/Tasks/Chat centered (absolute positioning), right rail with status/theme/? and gear dropdown containing Settings/Users/Logs; mobile dropdown unchanged Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,17 @@ async def create_note(
|
||||
priority: str | None = None,
|
||||
due_date: date | None = None,
|
||||
) -> Note:
|
||||
# Auto-populate project_id from milestone when not explicitly provided
|
||||
if milestone_id is not None and project_id is None:
|
||||
from fabledassistant.models.milestone import Milestone
|
||||
async with async_session() as lookup:
|
||||
result = await lookup.execute(
|
||||
select(Milestone).where(Milestone.id == milestone_id, Milestone.user_id == user_id)
|
||||
)
|
||||
ms = result.scalars().first()
|
||||
if ms is not None:
|
||||
project_id = ms.project_id
|
||||
|
||||
async with async_session() as session:
|
||||
note = Note(
|
||||
user_id=user_id,
|
||||
@@ -71,6 +82,7 @@ async def list_notes(
|
||||
due_after: date | None = None,
|
||||
project_id: int | None = None,
|
||||
milestone_id: int | None = None,
|
||||
milestone_ids: list[int] | None = None,
|
||||
parent_id: int | None = None,
|
||||
sort: str = "updated_at",
|
||||
order: str = "desc",
|
||||
@@ -125,8 +137,13 @@ async def list_notes(
|
||||
count_query = count_query.where(Note.due_date >= due_after)
|
||||
|
||||
if project_id is not None:
|
||||
query = query.where(Note.project_id == project_id)
|
||||
count_query = count_query.where(Note.project_id == project_id)
|
||||
if milestone_ids:
|
||||
# OR: directly assigned to project, OR assigned to one of the project's milestones
|
||||
project_filter = or_(Note.project_id == project_id, Note.milestone_id.in_(milestone_ids))
|
||||
else:
|
||||
project_filter = Note.project_id == project_id
|
||||
query = query.where(project_filter)
|
||||
count_query = count_query.where(project_filter)
|
||||
|
||||
if milestone_id is not None:
|
||||
query = query.where(Note.milestone_id == milestone_id)
|
||||
|
||||
Reference in New Issue
Block a user