feat: standalone focus items with edit, convert, project tab, domain ordering

- Add standalone text line items to focus (quick-add with optional domain)
- Edit page for standalone items (title, domain, project)
- Convert standalone items to task, note, link, or list item
- Focus tab on project detail page showing assigned focus items
- Sort domain groups: General first, then by domain sort_order
- Add domain_id and title to nullable_fields in BaseRepository

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 02:14:31 +00:00
parent 6aa36c570e
commit a61248b67d
6 changed files with 319 additions and 11 deletions

View File

@@ -251,6 +251,21 @@ async def project_detail(
"""))
all_meetings = [dict(r._mapping) for r in result]
elif tab == "focus":
result = await db.execute(text("""
SELECT df.*,
COALESCE(d.name, sd.name) as domain_name,
COALESCE(d.color, sd.color) as domain_color
FROM daily_focus df
LEFT JOIN domains d ON df.domain_id = d.id
LEFT JOIN domains sd ON df.domain_id = sd.id
WHERE df.is_deleted = false
AND df.title IS NOT NULL
AND df.project_id = :pid
ORDER BY df.sort_order, df.created_at
"""), {"pid": project_id})
tab_data = [dict(r._mapping) for r in result]
elif tab == "contacts":
result = await db.execute(text("""
SELECT c.*, cp.role, cp.created_at as linked_at
@@ -276,6 +291,7 @@ async def project_detail(
("decisions", "SELECT count(*) FROM decisions d JOIN decision_projects dp ON dp.decision_id = d.id WHERE dp.project_id = :pid AND d.is_deleted = false"),
("meetings", "SELECT count(*) FROM meetings m JOIN project_meetings pm ON pm.meeting_id = m.id WHERE pm.project_id = :pid AND m.is_deleted = false"),
("contacts", "SELECT count(*) FROM contacts c JOIN contact_projects cp ON cp.contact_id = c.id WHERE cp.project_id = :pid AND c.is_deleted = false"),
("focus", "SELECT count(*) FROM daily_focus WHERE project_id = :pid AND title IS NOT NULL AND is_deleted = false"),
]:
result = await db.execute(text(count_sql), {"pid": project_id})
counts[count_tab] = result.scalar() or 0