fix(mission): show project-level todo tasks in mission control detail
This commit is contained in:
parent
071d926781
commit
c019a0367b
@ -595,6 +595,37 @@ class MissionService:
|
||||
(project_id,),
|
||||
) or []
|
||||
|
||||
project_open_todo_count = 0
|
||||
project_open_todo_titles: list[str] = []
|
||||
if MissionService._table_exists("sag_todo_steps"):
|
||||
project_todo_row = execute_query_single(
|
||||
"""
|
||||
SELECT
|
||||
COUNT(*) FILTER (
|
||||
WHERE t.deleted_at IS NULL
|
||||
AND COALESCE(t.is_done, FALSE) = FALSE
|
||||
) AS open_todo_count,
|
||||
ARRAY_REMOVE(
|
||||
ARRAY_AGG(
|
||||
CASE
|
||||
WHEN t.deleted_at IS NULL
|
||||
AND COALESCE(t.is_done, FALSE) = FALSE
|
||||
THEN t.title
|
||||
END
|
||||
ORDER BY COALESCE(t.due_date, DATE '9999-12-31') ASC, t.id ASC
|
||||
),
|
||||
NULL
|
||||
) AS open_todo_titles
|
||||
FROM sag_todo_steps t
|
||||
WHERE t.sag_id = %s
|
||||
""",
|
||||
(project_id,),
|
||||
) or {}
|
||||
project_open_todo_count = int(project_todo_row.get("open_todo_count") or 0)
|
||||
titles_raw = project_todo_row.get("open_todo_titles") or []
|
||||
if isinstance(titles_raw, list):
|
||||
project_open_todo_titles = [str(item).strip() for item in titles_raw if str(item or "").strip()]
|
||||
|
||||
# Fallback for case-backed projects: fetch directly related/under cases from relation table.
|
||||
# This is used when a project is a case of type project/projekt and tasks are linked as case relations.
|
||||
if not tasks and MissionService._table_exists("sag_relationer"):
|
||||
@ -667,6 +698,8 @@ class MissionService:
|
||||
"milestones": [dict(row) for row in milestones],
|
||||
"blockers": [dict(row) for row in blockers],
|
||||
"tasks": [dict(row) for row in tasks],
|
||||
"project_open_todo_count": project_open_todo_count,
|
||||
"project_open_todo_titles": project_open_todo_titles,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
|
||||
@ -1328,6 +1328,10 @@
|
||||
const tasks = Array.isArray(detail?.tasks) ? detail.tasks : [];
|
||||
const milestones = Array.isArray(detail?.milestones) ? detail.milestones : [];
|
||||
const blockers = Array.isArray(detail?.blockers) ? detail.blockers : [];
|
||||
const projectOpenTodoCount = Number(detail?.project_open_todo_count || 0);
|
||||
const projectOpenTodoTitles = Array.isArray(detail?.project_open_todo_titles)
|
||||
? detail.project_open_todo_titles.map((item) => String(item || '').trim()).filter(Boolean)
|
||||
: [];
|
||||
|
||||
const grouped = { todo: [], doing: [], done: [] };
|
||||
tasks.forEach((task) => {
|
||||
@ -1339,6 +1343,7 @@
|
||||
|
||||
kpis.innerHTML = [
|
||||
{ label: 'Opgaver', value: tasks.length },
|
||||
{ label: 'Projekt todo', value: projectOpenTodoCount },
|
||||
{ label: 'Milepæle', value: milestones.length },
|
||||
{ label: 'Blockers', value: blockers.length },
|
||||
{ label: 'Deadline', value: formatShortDate(detail?.ended_at || detail?.deadline) },
|
||||
@ -1355,11 +1360,29 @@
|
||||
{ key: 'done', label: 'Lukket' },
|
||||
];
|
||||
|
||||
const projectTodoPreview = projectOpenTodoTitles.slice(0, 5)
|
||||
.map((title) => `<div>• ${escapeHtml(title)}</div>`)
|
||||
.join('');
|
||||
const projectTodoMore = Math.max(projectOpenTodoCount - Math.min(projectOpenTodoTitles.length, 5), 0);
|
||||
const projectTodoCard = projectOpenTodoCount > 0
|
||||
? `
|
||||
<div class="mc-kanban-card" style="border-left:3px solid #0f4c75;">
|
||||
<div class="mc-kanban-title">Projekt todo (${projectOpenTodoCount})</div>
|
||||
<div class="mc-kanban-meta">
|
||||
${projectTodoPreview || '<div>-</div>'}
|
||||
${projectTodoMore > 0 ? `<div>+${projectTodoMore} flere</div>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: '';
|
||||
|
||||
board.innerHTML = laneMeta.map((lane) => {
|
||||
const laneTasks = grouped[lane.key] || [];
|
||||
const extraCard = lane.key === 'todo' ? projectTodoCard : '';
|
||||
return `
|
||||
<div class="mc-kanban-col">
|
||||
<h6>${escapeHtml(lane.label)} (${laneTasks.length})</h6>
|
||||
<h6>${escapeHtml(lane.label)} (${laneTasks.length + (lane.key === 'todo' && projectOpenTodoCount > 0 ? 1 : 0)})</h6>
|
||||
${extraCard}
|
||||
${laneTasks.length ? laneTasks.map((task) => `
|
||||
<div class="mc-kanban-card">
|
||||
<div class="mc-kanban-title">#${Number(task.id || 0)} ${escapeHtml(task.titel || task.title || 'Uden titel')}</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user