diff --git a/static/i18n.js b/static/i18n.js index 62f4c719..987c9a74 100644 --- a/static/i18n.js +++ b/static/i18n.js @@ -849,6 +849,8 @@ const LOCALES = { kanban_no_matching_tasks: 'No matching tasks', kanban_no_data: 'No Kanban data', kanban_work_queue_hint: 'This is the Hermes Agent work queue. Create or triage a task, assign it, move it to Ready, then let the dispatcher claim it.', + kanban_tasks_hidden_by_filters: 'Tasks hidden by filters', + kanban_clear_filters: 'Clear filters', kanban_unavailable: 'Kanban unavailable', kanban_read_only: 'Read-only view', kanban_empty: 'Empty', @@ -2445,6 +2447,8 @@ const LOCALES = { kanban_no_matching_tasks: 'Nessun task corrispondente', kanban_no_data: 'Nessun dato Kanban', kanban_work_queue_hint: 'Questa è la coda di lavoro di Hermes Agent. Crea o classifica un task, assegnalo, spostalo in Pronto, poi lascia che il dispatcher lo reclami.', + kanban_tasks_hidden_by_filters: 'Tasks hidden by filters', + kanban_clear_filters: 'Clear filters', kanban_unavailable: 'Kanban non disponibile', kanban_read_only: 'Vista sola lettura', kanban_empty: 'Vuoto', @@ -4032,6 +4036,8 @@ const LOCALES = { kanban_no_matching_tasks: '一致するタスクがありません', kanban_no_data: 'カンバンデータがありません', kanban_work_queue_hint: 'これは Hermes Agent のワークキューです。タスクを作成またはトリアージし、担当者を割り当て、Ready に移動すると、ディスパッチャーがそれをクレームします。', + kanban_tasks_hidden_by_filters: 'Tasks hidden by filters', + kanban_clear_filters: 'Clear filters', kanban_unavailable: 'カンバンを利用できません', kanban_read_only: '読み取り専用', kanban_empty: '空', @@ -5353,6 +5359,8 @@ const LOCALES = { kanban_no_matching_tasks: 'No matching tasks', kanban_no_data: 'No Kanban data', kanban_work_queue_hint: 'This is the Hermes Agent work queue. Create or triage a task, assign it, move it to Ready, then let the dispatcher claim it.', + kanban_tasks_hidden_by_filters: 'Tasks hidden by filters', + kanban_clear_filters: 'Clear filters', kanban_unavailable: 'Kanban unavailable', kanban_read_only: 'Read-only view', kanban_empty: 'Empty', @@ -6877,6 +6885,8 @@ const LOCALES = { kanban_no_matching_tasks: 'No matching tasks', kanban_no_data: 'No Kanban data', kanban_work_queue_hint: 'This is the Hermes Agent work queue. Create or triage a task, assign it, move it to Ready, then let the dispatcher claim it.', + kanban_tasks_hidden_by_filters: 'Tasks hidden by filters', + kanban_clear_filters: 'Clear filters', kanban_unavailable: 'Kanban unavailable', kanban_read_only: 'Read-only view', kanban_empty: 'Empty', @@ -8379,6 +8389,8 @@ const LOCALES = { kanban_no_matching_tasks: 'No matching tasks', kanban_no_data: 'No Kanban data', kanban_work_queue_hint: 'This is the Hermes Agent work queue. Create or triage a task, assign it, move it to Ready, then let the dispatcher claim it.', + kanban_tasks_hidden_by_filters: 'Tasks hidden by filters', + kanban_clear_filters: 'Clear filters', kanban_unavailable: 'Kanban unavailable', kanban_read_only: 'Read-only view', kanban_empty: 'Empty', @@ -9935,7 +9947,9 @@ const LOCALES = { kanban_include_archived: '包含已归档', kanban_no_matching_tasks: '没有匹配的任务', kanban_no_data: '无看板数据', - kanban_work_queue_hint: '这是 Hermes Agent 的工作队列。创建或分类任务,分配任务,将其移至“就绪”状态,然后让调度员认领任务。', + kanban_work_queue_hint: '这是 Hermes Agent 的工作队列。创建或分类任务,分配任务,将其移至”就绪”状态,然后让调度员认领任务。', + kanban_tasks_hidden_by_filters: 'Tasks hidden by filters', + kanban_clear_filters: 'Clear filters', kanban_unavailable: '看板不可用', kanban_read_only: '只读视图', kanban_empty: '空', @@ -11718,6 +11732,8 @@ const LOCALES = { kanban_no_matching_tasks: '沒有符合的任務', kanban_no_data: '沒有看板資料', kanban_work_queue_hint: '這是 Hermes Agent 的工作佇列。建立或分類任務、指派對象、移至 Ready,然後讓調度器認領。', + kanban_tasks_hidden_by_filters: 'Tasks hidden by filters', + kanban_clear_filters: 'Clear filters', kanban_unavailable: '看板無法使用', kanban_read_only: '唯讀檢視', kanban_empty: '空', @@ -13192,6 +13208,8 @@ const LOCALES = { kanban_no_matching_tasks: 'No matching tasks', kanban_no_data: 'No Kanban data', kanban_work_queue_hint: 'This is the Hermes Agent work queue. Create or triage a task, assign it, move it to Ready, then let the dispatcher claim it.', + kanban_tasks_hidden_by_filters: 'Tasks hidden by filters', + kanban_clear_filters: 'Clear filters', kanban_unavailable: 'Kanban unavailable', kanban_read_only: 'Read-only view', kanban_empty: 'Empty', @@ -14680,6 +14698,8 @@ const LOCALES = { kanban_no_matching_tasks: 'No matching tasks', kanban_no_data: 'No Kanban data', kanban_work_queue_hint: 'This is the Hermes Agent work queue. Create or triage a task, assign it, move it to Ready, then let the dispatcher claim it.', + kanban_tasks_hidden_by_filters: 'Tasks hidden by filters', + kanban_clear_filters: 'Clear filters', kanban_unavailable: 'Kanban unavailable', kanban_read_only: 'Read-only view', kanban_empty: 'Empty', @@ -16197,6 +16217,8 @@ const LOCALES = { kanban_no_matching_tasks: 'Aucune tâche correspondante', kanban_no_data: 'Aucune donnée Kanban', kanban_work_queue_hint: 'Il s\'agit de la file d\'attente de travail de l\'agent Hermes. Créez ou triez une tâche, attribuez-la, déplacez-la vers Prêt, puis laissez le répartiteur la réclamer.', + kanban_tasks_hidden_by_filters: 'Tasks hidden by filters', + kanban_clear_filters: 'Clear filters', kanban_unavailable: 'Kanban indisponible', kanban_read_only: 'Vue en lecture seule', kanban_empty: 'Vide', @@ -17795,6 +17817,8 @@ const LOCALES = { kanban_no_matching_tasks: 'Eşleşen görev yok', kanban_no_data: 'Kanban verisi yok', kanban_work_queue_hint: 'Bu Hermes Ajanının iş kuyruğudur. Bir görev oluşturun veya önceliklendirin, atayın, Hazır\'a taşıyın ve sevk görevlisinin görevi talep etmesine izin verin.', + kanban_tasks_hidden_by_filters: 'Tasks hidden by filters', + kanban_clear_filters: 'Clear filters', kanban_unavailable: 'Kanban kullanılamıyor', kanban_read_only: 'Salt okunur görünüm', kanban_empty: 'Boş', @@ -19397,6 +19421,8 @@ const LOCALES = { kanban_no_matching_tasks: 'Brak pasujących zadań', kanban_no_data: 'Brak danych Kanban', kanban_work_queue_hint: 'To jest kolejka pracy Agenta Hermes. Utwórz zadanie lub dokonaj triażu, przypisz je, przenieś do statusu "Gotowe", a następnie pozwól dyspozytorowi je pobrać.', + kanban_tasks_hidden_by_filters: 'Tasks hidden by filters', + kanban_clear_filters: 'Clear filters', kanban_unavailable: 'Kanban niedostępny', kanban_read_only: 'Widok tylko do odczytu', kanban_empty: 'Puste', diff --git a/static/panels.js b/static/panels.js index 617dcb3f..4ace9b02 100644 --- a/static/panels.js +++ b/static/panels.js @@ -1541,7 +1541,7 @@ function _kanbanTaskTitle(task){ return task.title || task.summary || task.id || function _kanbanTaskBody(task){ return task.body || task.description || task.prompt || ''; } function _kanbanTaskMeta(task){ const bits = []; - if (task.assignee) bits.push(task.assignee); + bits.push(task.assignee ? task.assignee : t('kanban_unassigned')); if (task.tenant) bits.push(task.tenant); if (task.priority !== undefined && task.priority !== null) bits.push('P' + task.priority); if (task.comment_count) bits.push('💬 ' + task.comment_count); @@ -1920,10 +1920,20 @@ async function dropKanbanTask(event, status){ _kanbanSuppressNextCardClick(); } +const KANBAN_UNASSIGNED_LANE = '__unassigned__'; +function _kanbanLaneKey(task){ return task && task.assignee ? String(task.assignee) : KANBAN_UNASSIGNED_LANE; } +function _kanbanLaneLabel(lane){ return lane === KANBAN_UNASSIGNED_LANE ? t('kanban_unassigned') : lane; } + function _kanbanLaneNames(columns){ const names = new Set(); - columns.forEach(col => (col.tasks || []).forEach(task => names.add(task.assignee || t('kanban_unassigned')))); - return Array.from(names).sort((a, b) => String(a).localeCompare(String(b))); + columns.forEach(col => (col.tasks || []).forEach(task => names.add(_kanbanLaneKey(task)))); + const assigned = Array.from(names).filter(n => n !== KANBAN_UNASSIGNED_LANE).sort((a, b) => { + if (a === 'default') return -1; + if (b === 'default') return 1; + return String(a).localeCompare(String(b)); + }); + if (names.has(KANBAN_UNASSIGNED_LANE)) assigned.push(KANBAN_UNASSIGNED_LANE); + return assigned; } function _kanbanRenderColumn(col){ @@ -1943,9 +1953,10 @@ function _kanbanRenderProfileLanes(columns){ const lanes = _kanbanLaneNames(columns); if (!lanes.length) return columns.map(_kanbanRenderColumn).join(''); return `