feat: add awaiting_owner task status detection (#428)
Add awaiting_owner column to task board with keyword-based detection for tasks requiring human action. Tasks matching keywords like "waiting for", "needs human", "approval needed" are automatically placed in a dedicated column with orange styling. Supersedes #397
This commit is contained in:
parent
d0cd8701c7
commit
fc4384bba9
|
|
@ -593,6 +593,7 @@
|
||||||
"columnAriaLabel": "عمود {title}، {count} مهمة",
|
"columnAriaLabel": "عمود {title}، {count} مهمة",
|
||||||
"colInbox": "البريد الوارد",
|
"colInbox": "البريد الوارد",
|
||||||
"colAssigned": "مُخصّص",
|
"colAssigned": "مُخصّص",
|
||||||
|
"colAwaitingOwner": "بانتظار المالك",
|
||||||
"colInProgress": "قيد التنفيذ",
|
"colInProgress": "قيد التنفيذ",
|
||||||
"colReview": "المراجعة",
|
"colReview": "المراجعة",
|
||||||
"colQualityReview": "مراجعة الجودة",
|
"colQualityReview": "مراجعة الجودة",
|
||||||
|
|
|
||||||
|
|
@ -593,6 +593,7 @@
|
||||||
"columnAriaLabel": "Spalte {title}, {count} Aufgaben",
|
"columnAriaLabel": "Spalte {title}, {count} Aufgaben",
|
||||||
"colInbox": "Posteingang",
|
"colInbox": "Posteingang",
|
||||||
"colAssigned": "Zugewiesen",
|
"colAssigned": "Zugewiesen",
|
||||||
|
"colAwaitingOwner": "Wartet auf Besitzer",
|
||||||
"colInProgress": "In Bearbeitung",
|
"colInProgress": "In Bearbeitung",
|
||||||
"colReview": "Überprüfung",
|
"colReview": "Überprüfung",
|
||||||
"colQualityReview": "Qualitätsprüfung",
|
"colQualityReview": "Qualitätsprüfung",
|
||||||
|
|
|
||||||
|
|
@ -742,6 +742,7 @@
|
||||||
"columnAriaLabel": "{title} column, {count} tasks",
|
"columnAriaLabel": "{title} column, {count} tasks",
|
||||||
"colInbox": "Inbox",
|
"colInbox": "Inbox",
|
||||||
"colAssigned": "Assigned",
|
"colAssigned": "Assigned",
|
||||||
|
"colAwaitingOwner": "Awaiting Owner",
|
||||||
"colInProgress": "In Progress",
|
"colInProgress": "In Progress",
|
||||||
"colReview": "Review",
|
"colReview": "Review",
|
||||||
"colQualityReview": "Quality Review",
|
"colQualityReview": "Quality Review",
|
||||||
|
|
|
||||||
|
|
@ -593,6 +593,7 @@
|
||||||
"columnAriaLabel": "Columna {title}, {count} tareas",
|
"columnAriaLabel": "Columna {title}, {count} tareas",
|
||||||
"colInbox": "Bandeja de entrada",
|
"colInbox": "Bandeja de entrada",
|
||||||
"colAssigned": "Asignado",
|
"colAssigned": "Asignado",
|
||||||
|
"colAwaitingOwner": "Esperando al propietario",
|
||||||
"colInProgress": "En progreso",
|
"colInProgress": "En progreso",
|
||||||
"colReview": "Revisión",
|
"colReview": "Revisión",
|
||||||
"colQualityReview": "Revisión de calidad",
|
"colQualityReview": "Revisión de calidad",
|
||||||
|
|
|
||||||
|
|
@ -593,6 +593,7 @@
|
||||||
"columnAriaLabel": "Colonne {title}, {count} tâches",
|
"columnAriaLabel": "Colonne {title}, {count} tâches",
|
||||||
"colInbox": "Boîte de réception",
|
"colInbox": "Boîte de réception",
|
||||||
"colAssigned": "Assigné",
|
"colAssigned": "Assigné",
|
||||||
|
"colAwaitingOwner": "En attente du propriétaire",
|
||||||
"colInProgress": "En cours",
|
"colInProgress": "En cours",
|
||||||
"colReview": "Révision",
|
"colReview": "Révision",
|
||||||
"colQualityReview": "Révision qualité",
|
"colQualityReview": "Révision qualité",
|
||||||
|
|
|
||||||
|
|
@ -593,6 +593,7 @@
|
||||||
"columnAriaLabel": "{title}列、{count}件のタスク",
|
"columnAriaLabel": "{title}列、{count}件のタスク",
|
||||||
"colInbox": "受信トレイ",
|
"colInbox": "受信トレイ",
|
||||||
"colAssigned": "割り当て済み",
|
"colAssigned": "割り当て済み",
|
||||||
|
"colAwaitingOwner": "オーナー待ち",
|
||||||
"colInProgress": "進行中",
|
"colInProgress": "進行中",
|
||||||
"colReview": "レビュー",
|
"colReview": "レビュー",
|
||||||
"colQualityReview": "品質レビュー",
|
"colQualityReview": "品質レビュー",
|
||||||
|
|
|
||||||
|
|
@ -593,6 +593,7 @@
|
||||||
"columnAriaLabel": "{title} 열, {count}개 작업",
|
"columnAriaLabel": "{title} 열, {count}개 작업",
|
||||||
"colInbox": "받은 편지함",
|
"colInbox": "받은 편지함",
|
||||||
"colAssigned": "할당됨",
|
"colAssigned": "할당됨",
|
||||||
|
"colAwaitingOwner": "소유자 대기 중",
|
||||||
"colInProgress": "진행 중",
|
"colInProgress": "진행 중",
|
||||||
"colReview": "검토",
|
"colReview": "검토",
|
||||||
"colQualityReview": "품질 검토",
|
"colQualityReview": "품질 검토",
|
||||||
|
|
|
||||||
|
|
@ -593,6 +593,7 @@
|
||||||
"columnAriaLabel": "Coluna {title}, {count} tarefas",
|
"columnAriaLabel": "Coluna {title}, {count} tarefas",
|
||||||
"colInbox": "Caixa de entrada",
|
"colInbox": "Caixa de entrada",
|
||||||
"colAssigned": "Atribuído",
|
"colAssigned": "Atribuído",
|
||||||
|
"colAwaitingOwner": "Aguardando proprietário",
|
||||||
"colInProgress": "Em andamento",
|
"colInProgress": "Em andamento",
|
||||||
"colReview": "Revisão",
|
"colReview": "Revisão",
|
||||||
"colQualityReview": "Revisão de qualidade",
|
"colQualityReview": "Revisão de qualidade",
|
||||||
|
|
|
||||||
|
|
@ -593,6 +593,7 @@
|
||||||
"columnAriaLabel": "Колонка {title}, {count} задач",
|
"columnAriaLabel": "Колонка {title}, {count} задач",
|
||||||
"colInbox": "Входящие",
|
"colInbox": "Входящие",
|
||||||
"colAssigned": "Назначено",
|
"colAssigned": "Назначено",
|
||||||
|
"colAwaitingOwner": "Ожидает владельца",
|
||||||
"colInProgress": "В работе",
|
"colInProgress": "В работе",
|
||||||
"colReview": "На проверке",
|
"colReview": "На проверке",
|
||||||
"colQualityReview": "Контроль качества",
|
"colQualityReview": "Контроль качества",
|
||||||
|
|
|
||||||
|
|
@ -831,6 +831,7 @@
|
||||||
"columnAriaLabel": "{title} 列,{count} 个任务",
|
"columnAriaLabel": "{title} 列,{count} 个任务",
|
||||||
"colInbox": "收件箱",
|
"colInbox": "收件箱",
|
||||||
"colAssigned": "已分配",
|
"colAssigned": "已分配",
|
||||||
|
"colAwaitingOwner": "等待所有者",
|
||||||
"colInProgress": "进行中",
|
"colInProgress": "进行中",
|
||||||
"colReview": "审查",
|
"colReview": "审查",
|
||||||
"colQualityReview": "质量审查",
|
"colQualityReview": "质量审查",
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ interface Task {
|
||||||
id: number
|
id: number
|
||||||
title: string
|
title: string
|
||||||
description?: string
|
description?: string
|
||||||
status: 'inbox' | 'assigned' | 'in_progress' | 'review' | 'quality_review' | 'done'
|
status: 'inbox' | 'assigned' | 'in_progress' | 'review' | 'quality_review' | 'done' | 'awaiting_owner'
|
||||||
priority: 'low' | 'medium' | 'high' | 'critical' | 'urgent'
|
priority: 'low' | 'medium' | 'high' | 'critical' | 'urgent'
|
||||||
assigned_to?: string
|
assigned_to?: string
|
||||||
created_by: string
|
created_by: string
|
||||||
|
|
@ -89,12 +89,27 @@ interface MentionOption {
|
||||||
const STATUS_COLUMN_KEYS = [
|
const STATUS_COLUMN_KEYS = [
|
||||||
{ key: 'inbox', titleKey: 'colInbox', color: 'bg-secondary text-foreground' },
|
{ key: 'inbox', titleKey: 'colInbox', color: 'bg-secondary text-foreground' },
|
||||||
{ key: 'assigned', titleKey: 'colAssigned', color: 'bg-blue-500/20 text-blue-400' },
|
{ key: 'assigned', titleKey: 'colAssigned', color: 'bg-blue-500/20 text-blue-400' },
|
||||||
|
{ key: 'awaiting_owner', titleKey: 'colAwaitingOwner', color: 'bg-orange-500/20 text-orange-400' },
|
||||||
{ key: 'in_progress', titleKey: 'colInProgress', color: 'bg-yellow-500/20 text-yellow-400' },
|
{ key: 'in_progress', titleKey: 'colInProgress', color: 'bg-yellow-500/20 text-yellow-400' },
|
||||||
{ key: 'review', titleKey: 'colReview', color: 'bg-purple-500/20 text-purple-400' },
|
{ key: 'review', titleKey: 'colReview', color: 'bg-purple-500/20 text-purple-400' },
|
||||||
{ key: 'quality_review', titleKey: 'colQualityReview', color: 'bg-indigo-500/20 text-indigo-400' },
|
{ key: 'quality_review', titleKey: 'colQualityReview', color: 'bg-indigo-500/20 text-indigo-400' },
|
||||||
{ key: 'done', titleKey: 'colDone', color: 'bg-green-500/20 text-green-400' },
|
{ key: 'done', titleKey: 'colDone', color: 'bg-green-500/20 text-green-400' },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const AWAITING_OWNER_KEYWORDS = [
|
||||||
|
'waiting for', 'waiting on', 'needs human', 'manual action',
|
||||||
|
'account creation', 'browser login', 'approval needed',
|
||||||
|
'owner action', 'human required', 'blocked on owner',
|
||||||
|
'awaiting owner', 'awaiting human', 'needs owner',
|
||||||
|
]
|
||||||
|
|
||||||
|
function detectAwaitingOwner(task: Task): boolean {
|
||||||
|
if (task.status === 'awaiting_owner') return true
|
||||||
|
if (task.status !== 'assigned' && task.status !== 'in_progress') return false
|
||||||
|
const text = `${task.title} ${task.description || ''}`.toLowerCase()
|
||||||
|
return AWAITING_OWNER_KEYWORDS.some(kw => text.includes(kw))
|
||||||
|
}
|
||||||
|
|
||||||
/** Build a human-readable label for a session key like "agent:nefes:telegram-group-123" */
|
/** Build a human-readable label for a session key like "agent:nefes:telegram-group-123" */
|
||||||
function formatSessionLabel(s: { key: string; channel?: string; kind?: string; label?: string }): string {
|
function formatSessionLabel(s: { key: string; channel?: string; kind?: string; label?: string }): string {
|
||||||
if (s.label) return s.label
|
if (s.label) return s.label
|
||||||
|
|
@ -535,9 +550,12 @@ export function TaskBoardPanel() {
|
||||||
// Poll as SSE fallback — pauses when SSE is delivering events
|
// Poll as SSE fallback — pauses when SSE is delivering events
|
||||||
useSmartPoll(fetchData, 30000, { pauseWhenSseConnected: true })
|
useSmartPoll(fetchData, 30000, { pauseWhenSseConnected: true })
|
||||||
|
|
||||||
// Group tasks by status
|
// Group tasks by status, overriding for awaiting_owner detection
|
||||||
const tasksByStatus = statusColumns.reduce((acc, column) => {
|
const tasksByStatus = statusColumns.reduce((acc, column) => {
|
||||||
acc[column.key] = tasks.filter(task => task.status === column.key)
|
acc[column.key] = tasks.filter(task => {
|
||||||
|
const effectiveStatus = detectAwaitingOwner(task) ? 'awaiting_owner' : task.status
|
||||||
|
return effectiveStatus === column.key
|
||||||
|
})
|
||||||
return acc
|
return acc
|
||||||
}, {} as Record<string, Task[]>)
|
}, {} as Record<string, Task[]>)
|
||||||
|
|
||||||
|
|
@ -1017,6 +1035,11 @@ export function TaskBoardPanel() {
|
||||||
Aegis
|
Aegis
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{detectAwaitingOwner(task) && (
|
||||||
|
<span className="text-[10px] px-1.5 py-0.5 rounded bg-orange-500/20 text-orange-400 border border-orange-500/30 font-mono">
|
||||||
|
{t('colAwaitingOwner')}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1820,6 +1843,7 @@ function ClaudeCodeTasksSection() {
|
||||||
s === 'completed' ? 'text-green-400' :
|
s === 'completed' ? 'text-green-400' :
|
||||||
s === 'in_progress' ? 'text-blue-400' :
|
s === 'in_progress' ? 'text-blue-400' :
|
||||||
s === 'blocked' ? 'text-red-400' :
|
s === 'blocked' ? 'text-red-400' :
|
||||||
|
s === 'awaiting_owner' ? 'text-orange-400' :
|
||||||
'text-muted-foreground'
|
'text-muted-foreground'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ export interface Task {
|
||||||
id: number
|
id: number
|
||||||
title: string
|
title: string
|
||||||
description?: string
|
description?: string
|
||||||
status: 'inbox' | 'assigned' | 'in_progress' | 'review' | 'quality_review' | 'done'
|
status: 'inbox' | 'assigned' | 'in_progress' | 'review' | 'quality_review' | 'done' | 'awaiting_owner'
|
||||||
priority: 'low' | 'medium' | 'high' | 'critical' | 'urgent'
|
priority: 'low' | 'medium' | 'high' | 'critical' | 'urgent'
|
||||||
project_id?: number
|
project_id?: number
|
||||||
project_ticket_no?: number
|
project_ticket_no?: number
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue