feat: group daily focus items by project with "General" fallback
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,8 @@ async def focus_view(
|
|||||||
SELECT df.*,
|
SELECT df.*,
|
||||||
t.title as title, t.priority, t.status as task_status,
|
t.title as title, t.priority, t.status as task_status,
|
||||||
t.project_id, t.due_date, t.estimated_minutes,
|
t.project_id, t.due_date, t.estimated_minutes,
|
||||||
p.name as project_name,
|
COALESCE(p.name, lp.name) as project_name,
|
||||||
|
COALESCE(t.project_id, l.project_id) as effective_project_id,
|
||||||
d.name as domain_name, d.color as domain_color,
|
d.name as domain_name, d.color as domain_color,
|
||||||
li.content as list_item_content, li.list_id as list_item_list_id,
|
li.content as list_item_content, li.list_id as list_item_list_id,
|
||||||
li.completed as list_item_completed,
|
li.completed as list_item_completed,
|
||||||
@@ -48,11 +49,23 @@ async def focus_view(
|
|||||||
LEFT JOIN domains d ON t.domain_id = d.id
|
LEFT JOIN domains d ON t.domain_id = d.id
|
||||||
LEFT JOIN list_items li ON df.list_item_id = li.id
|
LEFT JOIN list_items li ON df.list_item_id = li.id
|
||||||
LEFT JOIN lists l ON li.list_id = l.id
|
LEFT JOIN lists l ON li.list_id = l.id
|
||||||
|
LEFT JOIN projects lp ON l.project_id = lp.id
|
||||||
WHERE df.focus_date = :target_date AND df.is_deleted = false
|
WHERE df.focus_date = :target_date AND df.is_deleted = false
|
||||||
ORDER BY df.sort_order, df.created_at
|
ORDER BY df.sort_order, df.created_at
|
||||||
"""), {"target_date": target_date})
|
"""), {"target_date": target_date})
|
||||||
items = [dict(r._mapping) for r in result]
|
items = [dict(r._mapping) for r in result]
|
||||||
|
|
||||||
|
# Group items by project for template rendering
|
||||||
|
from collections import OrderedDict
|
||||||
|
_groups = OrderedDict()
|
||||||
|
for item in items:
|
||||||
|
key = item.get("effective_project_id") or "__general__"
|
||||||
|
label = item.get("project_name") or "General"
|
||||||
|
if key not in _groups:
|
||||||
|
_groups[key] = {"label": label, "rows": []}
|
||||||
|
_groups[key]["rows"].append(item)
|
||||||
|
grouped_items = list(_groups.values())
|
||||||
|
|
||||||
# --- Available tasks ---
|
# --- Available tasks ---
|
||||||
available_tasks = []
|
available_tasks = []
|
||||||
if source_type == "tasks":
|
if source_type == "tasks":
|
||||||
@@ -139,7 +152,7 @@ async def focus_view(
|
|||||||
|
|
||||||
return templates.TemplateResponse("focus.html", {
|
return templates.TemplateResponse("focus.html", {
|
||||||
"request": request, "sidebar": sidebar,
|
"request": request, "sidebar": sidebar,
|
||||||
"items": items,
|
"items": items, "grouped_items": grouped_items,
|
||||||
"available_tasks": available_tasks,
|
"available_tasks": available_tasks,
|
||||||
"available_list_items": available_list_items,
|
"available_list_items": available_list_items,
|
||||||
"focus_date": target_date,
|
"focus_date": target_date,
|
||||||
|
|||||||
@@ -12,10 +12,14 @@
|
|||||||
<span class="text-sm" style="font-weight:600">{{ focus_date }}</span>
|
<span class="text-sm" style="font-weight:600">{{ focus_date }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Focus items -->
|
<!-- Focus items grouped by project -->
|
||||||
{% if items %}
|
{% if items %}
|
||||||
<div class="card">
|
{% for group in grouped_items %}
|
||||||
{% for item in items %}
|
<div class="card mb-3">
|
||||||
|
<div class="card-header" style="padding:6px 12px;">
|
||||||
|
<h3 class="card-title" style="font-size:0.85rem;margin:0;">{{ group.label }}</h3>
|
||||||
|
</div>
|
||||||
|
{% for item in group.rows %}
|
||||||
<div class="focus-item {{ 'completed' if item.completed }}">
|
<div class="focus-item {{ 'completed' if item.completed }}">
|
||||||
{% with reorder_url="/focus/reorder", item_id=item.id, extra_fields={"focus_date": focus_date|string} %}
|
{% with reorder_url="/focus/reorder", item_id=item.id, extra_fields={"focus_date": focus_date|string} %}
|
||||||
{% include 'partials/reorder_arrows.html' %}
|
{% include 'partials/reorder_arrows.html' %}
|
||||||
@@ -33,7 +37,6 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<span class="focus-title" style="color:var(--muted)">[Deleted]</span>
|
<span class="focus-title" style="color:var(--muted)">[Deleted]</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if item.project_name %}<span class="row-tag">{{ item.project_name }}</span>{% endif %}
|
|
||||||
{% if item.estimated_minutes %}<span class="focus-meta">~{{ item.estimated_minutes }}min</span>{% endif %}
|
{% if item.estimated_minutes %}<span class="focus-meta">~{{ item.estimated_minutes }}min</span>{% endif %}
|
||||||
{% if item.due_date %}<span class="focus-meta">{{ item.due_date }}</span>{% endif %}
|
{% if item.due_date %}<span class="focus-meta">{{ item.due_date }}</span>{% endif %}
|
||||||
{% elif item.list_item_id %}
|
{% elif item.list_item_id %}
|
||||||
@@ -51,6 +54,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="empty-state mb-4"><div class="empty-state-text">No focus items for this day</div></div>
|
<div class="empty-state mb-4"><div class="empty-state-text">No focus items for this day</div></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
Reference in New Issue
Block a user