mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
refactor(settings): move Tasks tab from Activity into Settings → Developer Options (#3594)
This commit is contained in:
@@ -30,6 +30,11 @@ describe('useSettingsNavigation breadcrumbs', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('tasks returns Settings > Developer Options', () => {
|
||||
renderWithProviders(<BreadcrumbProbe />, { initialEntries: ['/settings/tasks'] });
|
||||
expect(screen.getByTestId('breadcrumbs')).toHaveTextContent('Settings > Developer Options');
|
||||
});
|
||||
|
||||
test('developer-options returns Settings (section page)', () => {
|
||||
renderWithProviders(<BreadcrumbProbe />, { initialEntries: ['/settings/developer-options'] });
|
||||
expect(screen.getByTestId('breadcrumbs')).toHaveTextContent('Settings');
|
||||
|
||||
@@ -47,6 +47,7 @@ export type SettingsRoute =
|
||||
| 'composio-triggers'
|
||||
| 'composio-routing'
|
||||
| 'task-sources'
|
||||
| 'tasks'
|
||||
| 'mcp-server'
|
||||
| 'dev-workflow'
|
||||
| 'sandbox-settings'
|
||||
@@ -135,6 +136,10 @@ export const useSettingsNavigation = (): SettingsNavigationHook => {
|
||||
if (path.includes('/settings/composio-triggers')) return 'composio-triggers';
|
||||
if (path.includes('/settings/composio-routing')) return 'composio-routing';
|
||||
if (path.includes('/settings/task-sources')) return 'task-sources';
|
||||
// `tasks` is checked after `task-sources` so the longer, hyphenated route
|
||||
// isn't shadowed (the two prefixes don't actually overlap, but ordering
|
||||
// here keeps the intent obvious).
|
||||
if (path.includes('/settings/tasks')) return 'tasks';
|
||||
if (path.includes('/settings/intelligence')) return 'intelligence';
|
||||
if (path.includes('/settings/crypto')) return 'crypto';
|
||||
if (path.includes('/settings/recovery-phrase')) return 'recovery-phrase';
|
||||
@@ -326,6 +331,7 @@ export const useSettingsNavigation = (): SettingsNavigationHook => {
|
||||
case 'webhooks-triggers':
|
||||
case 'composio-triggers':
|
||||
case 'composio-routing':
|
||||
case 'tasks':
|
||||
case 'notification-routing':
|
||||
case 'mcp-server':
|
||||
case 'dev-workflow':
|
||||
|
||||
@@ -381,6 +381,22 @@ const modelsInferenceGroup: DevGroup = {
|
||||
const automationIntegrationsGroup: DevGroup = {
|
||||
labelKey: 'settings.devGroups.automationIntegrations',
|
||||
items: [
|
||||
{
|
||||
id: 'tasks',
|
||||
titleKey: 'settings.developerMenu.tasks.title',
|
||||
descriptionKey: 'settings.developerMenu.tasks.desc',
|
||||
route: 'tasks',
|
||||
icon: (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-6 0h.01M12 16h3m-6 0h.01"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'cron-jobs',
|
||||
titleKey: 'settings.developerMenu.cronJobs.title',
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { useT } from '../../../lib/i18n/I18nContext';
|
||||
import IntelligenceTasksTab from '../../intelligence/IntelligenceTasksTab';
|
||||
import SettingsHeader from '../components/SettingsHeader';
|
||||
import { useSettingsNavigation } from '../hooks/useSettingsNavigation';
|
||||
|
||||
/**
|
||||
* Settings → Developer Options → Tasks.
|
||||
*
|
||||
* Hosts the {@link IntelligenceTasksTab} task-board surface that previously
|
||||
* lived as a tab on the Activity page. The board (personal to-dos, task-source
|
||||
* boards, and the per-agent boards built across conversations) is unchanged —
|
||||
* this panel only re-homes it under the developer menu with the standard
|
||||
* SettingsHeader + breadcrumb chrome.
|
||||
*/
|
||||
const TasksPanel = () => {
|
||||
const { t } = useT();
|
||||
const { navigateBack, breadcrumbs } = useSettingsNavigation();
|
||||
|
||||
return (
|
||||
<div data-testid="tasks-panel">
|
||||
<SettingsHeader
|
||||
title={t('memory.tab.tasks')}
|
||||
showBackButton={true}
|
||||
onBack={navigateBack}
|
||||
breadcrumbs={breadcrumbs}
|
||||
/>
|
||||
|
||||
<div className="p-4">
|
||||
<p className="mb-4 text-xs text-stone-500 dark:text-neutral-400">
|
||||
{t('memory.tab.tasksDescription')}
|
||||
</p>
|
||||
<IntelligenceTasksTab />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TasksPanel;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
// i18n stub — return the key verbatim so we can assert on label keys.
|
||||
vi.mock('../../../../lib/i18n/I18nContext', () => ({ useT: () => ({ t: (key: string) => key }) }));
|
||||
|
||||
// The task-board surface is heavy (Redux + RPC); stub it to a sentinel so the
|
||||
// panel test stays focused on the re-homing chrome.
|
||||
vi.mock('../../../intelligence/IntelligenceTasksTab', () => ({
|
||||
default: () => <div data-testid="intelligence-tasks-tab" />,
|
||||
}));
|
||||
|
||||
const navigateBack = vi.fn();
|
||||
vi.mock('../../hooks/useSettingsNavigation', () => ({
|
||||
useSettingsNavigation: () => ({
|
||||
navigateBack,
|
||||
breadcrumbs: [{ label: 'Settings' }, { label: 'Developer Options' }],
|
||||
}),
|
||||
}));
|
||||
|
||||
const TasksPanel = (await import('../TasksPanel')).default;
|
||||
|
||||
function renderPanel() {
|
||||
return render(
|
||||
<MemoryRouter>
|
||||
<TasksPanel />
|
||||
</MemoryRouter>
|
||||
);
|
||||
}
|
||||
|
||||
describe('TasksPanel', () => {
|
||||
it('renders the panel shell, title, description and the task board', () => {
|
||||
renderPanel();
|
||||
expect(screen.getByTestId('tasks-panel')).toBeInTheDocument();
|
||||
// SettingsHeader title + the descriptive blurb both come from i18n keys.
|
||||
expect(screen.getAllByText('memory.tab.tasks').length).toBeGreaterThan(0);
|
||||
expect(screen.getByText('memory.tab.tasksDescription')).toBeInTheDocument();
|
||||
// The re-homed task board is mounted unchanged.
|
||||
expect(screen.getByTestId('intelligence-tasks-tab')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -3838,6 +3838,9 @@ const messages: TranslationMap = {
|
||||
'settings.devWorkflow.schedule.every2hours': 'كل ساعتين',
|
||||
'settings.devWorkflow.schedule.every6hours': 'كل 6 ساعات',
|
||||
'settings.devWorkflow.schedule.onceDaily': 'يوم واحد (9 صباحا)',
|
||||
'settings.developerMenu.tasks.title': 'المهام',
|
||||
'settings.developerMenu.tasks.desc':
|
||||
'تصفّح لوحات المهام وإدارتها — مهامك الخاصة إضافةً إلى اللوحات التي تنشئها الوكلاء عبر المحادثات.',
|
||||
'settings.developerMenu.cronJobs.title': 'مهام Cron',
|
||||
'settings.developerMenu.cronJobs.desc': 'عرض وتكوين المهام المجدولة لمهارات وقت التشغيل',
|
||||
'settings.developerMenu.localModelDebug.title': 'تصحيح النموذج المحلي',
|
||||
|
||||
@@ -3914,6 +3914,9 @@ const messages: TranslationMap = {
|
||||
'settings.devWorkflow.schedule.every2hours': 'প্রতি ২ ঘন্টা',
|
||||
'settings.devWorkflow.schedule.every6hours': 'প্রতি ৬ ঘন্টা',
|
||||
'settings.devWorkflow.schedule.onceDaily': 'প্রতিদিন (৯.',
|
||||
'settings.developerMenu.tasks.title': 'কাজ',
|
||||
'settings.developerMenu.tasks.desc':
|
||||
'টাস্ক বোর্ড ব্রাউজ ও পরিচালনা করুন — আপনার নিজের কাজ এবং কথোপকথন জুড়ে এজেন্টদের তৈরি করা বোর্ড।',
|
||||
'settings.developerMenu.cronJobs.title': 'Cron জব',
|
||||
'settings.developerMenu.cronJobs.desc':
|
||||
'রানটাইম স্কিলের জন্য নির্ধারিত জব দেখুন এবং কনফিগার করুন',
|
||||
|
||||
@@ -4014,6 +4014,9 @@ const messages: TranslationMap = {
|
||||
'settings.devWorkflow.schedule.every2hours': 'Alle 2 Stunden',
|
||||
'settings.devWorkflow.schedule.every6hours': 'Alle 6 Stunden',
|
||||
'settings.devWorkflow.schedule.onceDaily': 'Einmal täglich (09:00 Uhr)',
|
||||
'settings.developerMenu.tasks.title': 'Aufgaben',
|
||||
'settings.developerMenu.tasks.desc':
|
||||
'Aufgaben-Boards durchsuchen und verwalten — deine eigenen To-dos sowie die Boards, die Agenten über Unterhaltungen hinweg erstellen.',
|
||||
'settings.developerMenu.cronJobs.title': 'Cron-Jobs',
|
||||
'settings.developerMenu.cronJobs.desc':
|
||||
'Zeige geplante Jobs für Laufzeitfähigkeiten an und konfiguriere sie',
|
||||
|
||||
@@ -4457,6 +4457,9 @@ const en: TranslationMap = {
|
||||
'settings.devWorkflow.schedule.every2hours': 'Every 2 hours',
|
||||
'settings.devWorkflow.schedule.every6hours': 'Every 6 hours',
|
||||
'settings.devWorkflow.schedule.onceDaily': 'Once daily (9 AM)',
|
||||
'settings.developerMenu.tasks.title': 'Tasks',
|
||||
'settings.developerMenu.tasks.desc':
|
||||
'Browse and manage task boards — your own to-dos plus the boards agents build across conversations.',
|
||||
'settings.developerMenu.cronJobs.title': 'Cron Jobs',
|
||||
'settings.developerMenu.cronJobs.desc': 'View and configure scheduled jobs for runtime skills',
|
||||
'settings.developerMenu.localModelDebug.title': 'Local Model Debug',
|
||||
|
||||
@@ -3988,6 +3988,9 @@ const messages: TranslationMap = {
|
||||
'settings.devWorkflow.schedule.every2hours': 'Cada 2 horas',
|
||||
'settings.devWorkflow.schedule.every6hours': 'Cada 6 horas',
|
||||
'settings.devWorkflow.schedule.onceDaily': 'Una vez al día (9 AM)',
|
||||
'settings.developerMenu.tasks.title': 'Tareas',
|
||||
'settings.developerMenu.tasks.desc':
|
||||
'Explora y gestiona los tableros de tareas: tus pendientes y los tableros que los agentes crean en las conversaciones.',
|
||||
'settings.developerMenu.cronJobs.title': 'Tareas cron',
|
||||
'settings.developerMenu.cronJobs.desc':
|
||||
'Ver y configurar tareas programadas para habilidades en tiempo de ejecución',
|
||||
|
||||
@@ -4004,6 +4004,9 @@ const messages: TranslationMap = {
|
||||
'settings.devWorkflow.schedule.every2hours': 'Toutes les 2 heures',
|
||||
'settings.devWorkflow.schedule.every6hours': 'Toutes les 6 heures',
|
||||
'settings.devWorkflow.schedule.onceDaily': 'Une fois par jour (9 h)',
|
||||
'settings.developerMenu.tasks.title': 'Tâches',
|
||||
'settings.developerMenu.tasks.desc':
|
||||
'Parcourez et gérez les tableaux de tâches — vos propres to-dos ainsi que les tableaux créés par les agents au fil des conversations.',
|
||||
'settings.developerMenu.cronJobs.title': 'Tâches cron',
|
||||
'settings.developerMenu.cronJobs.desc':
|
||||
"Afficher et configurer les tâches planifiées des compétences d'exécution",
|
||||
|
||||
@@ -3921,6 +3921,9 @@ const messages: TranslationMap = {
|
||||
'settings.devWorkflow.schedule.every2hours': 'हर 2 घंटे',
|
||||
'settings.devWorkflow.schedule.every6hours': '6 घंटे',
|
||||
'settings.devWorkflow.schedule.onceDaily': 'एक बार दैनिक (9 AM)',
|
||||
'settings.developerMenu.tasks.title': 'कार्य',
|
||||
'settings.developerMenu.tasks.desc':
|
||||
'टास्क बोर्ड ब्राउज़ करें और प्रबंधित करें — आपके अपने काम और बातचीत के दौरान एजेंट द्वारा बनाए गए बोर्ड।',
|
||||
'settings.developerMenu.cronJobs.title': 'Cron जॉब्स',
|
||||
'settings.developerMenu.cronJobs.desc':
|
||||
'रनटाइम स्किल्स के लिए शेड्यूल किए गए जॉब देखें और कॉन्फ़िगर करें',
|
||||
|
||||
@@ -3932,6 +3932,9 @@ const messages: TranslationMap = {
|
||||
'settings.devWorkflow.schedule.every2hours': 'Setiap 2 jam',
|
||||
'settings.devWorkflow.schedule.every6hours': 'Setiap 6 jam',
|
||||
'settings.devWorkflow.schedule.onceDaily': 'Setiap hari (jam 9 pagi)',
|
||||
'settings.developerMenu.tasks.title': 'Tugas',
|
||||
'settings.developerMenu.tasks.desc':
|
||||
'Jelajahi dan kelola papan tugas — daftar tugas Anda sendiri serta papan yang dibuat agen di seluruh percakapan.',
|
||||
'settings.developerMenu.cronJobs.title': 'Pekerjaan Cron',
|
||||
'settings.developerMenu.cronJobs.desc': 'Lihat dan atur pekerjaan terjadwal untuk skill runtime',
|
||||
'settings.developerMenu.localModelDebug.title': 'Debug Model Lokal',
|
||||
|
||||
@@ -3982,6 +3982,9 @@ const messages: TranslationMap = {
|
||||
'settings.devWorkflow.schedule.every2hours': 'Ogni 2 ore',
|
||||
'settings.devWorkflow.schedule.every6hours': 'Ogni 6 ore',
|
||||
'settings.devWorkflow.schedule.onceDaily': 'Una volta al giorno (ore 9)',
|
||||
'settings.developerMenu.tasks.title': 'Attività',
|
||||
'settings.developerMenu.tasks.desc':
|
||||
'Esplora e gestisci le bacheche delle attività — i tuoi to-do e le bacheche create dagli agenti nelle conversazioni.',
|
||||
'settings.developerMenu.cronJobs.title': 'Processi cron',
|
||||
'settings.developerMenu.cronJobs.desc':
|
||||
'Visualizza e configura processi pianificati per le skill di runtime',
|
||||
|
||||
@@ -3881,6 +3881,9 @@ const messages: TranslationMap = {
|
||||
'settings.devWorkflow.schedule.every2hours': '2시간마다',
|
||||
'settings.devWorkflow.schedule.every6hours': '6시간마다',
|
||||
'settings.devWorkflow.schedule.onceDaily': '하루 한 번(오전 9시)',
|
||||
'settings.developerMenu.tasks.title': '작업',
|
||||
'settings.developerMenu.tasks.desc':
|
||||
'작업 보드를 둘러보고 관리하세요 — 내 할 일과 대화 전반에서 에이전트가 만든 보드를 함께 확인합니다.',
|
||||
'settings.developerMenu.cronJobs.title': '크론 작업',
|
||||
'settings.developerMenu.cronJobs.desc': '예약 보기 및 구성 런타임 기술용 작업',
|
||||
'settings.developerMenu.localModelDebug.title': '로컬 모델 디버그',
|
||||
|
||||
@@ -3980,6 +3980,9 @@ const messages: TranslationMap = {
|
||||
'settings.devWorkflow.schedule.every2hours': 'Co 2 godziny',
|
||||
'settings.devWorkflow.schedule.every6hours': 'Co 6 godzin',
|
||||
'settings.devWorkflow.schedule.onceDaily': 'Raz dziennie (9:00)',
|
||||
'settings.developerMenu.tasks.title': 'Zadania',
|
||||
'settings.developerMenu.tasks.desc':
|
||||
'Przeglądaj tablice zadań i zarządzaj nimi — Twoje własne zadania oraz tablice tworzone przez agentów w rozmowach.',
|
||||
'settings.developerMenu.cronJobs.title': 'Zadania cron',
|
||||
'settings.developerMenu.cronJobs.desc':
|
||||
'Przeglądaj i konfiguruj zaplanowane zadania dla umiejętności runtime',
|
||||
|
||||
@@ -3982,6 +3982,9 @@ const messages: TranslationMap = {
|
||||
'settings.devWorkflow.schedule.every2hours': 'A cada 2 horas',
|
||||
'settings.devWorkflow.schedule.every6hours': 'A cada 6 horas',
|
||||
'settings.devWorkflow.schedule.onceDaily': 'Uma vez ao dia (9h)',
|
||||
'settings.developerMenu.tasks.title': 'Tarefas',
|
||||
'settings.developerMenu.tasks.desc':
|
||||
'Navegue e gerencie os quadros de tarefas — suas próprias pendências e os quadros que os agentes criam nas conversas.',
|
||||
'settings.developerMenu.cronJobs.title': 'Tarefas cron',
|
||||
'settings.developerMenu.cronJobs.desc':
|
||||
'Veja e configure tarefas agendadas para habilidades em tempo de execução',
|
||||
|
||||
@@ -3952,6 +3952,9 @@ const messages: TranslationMap = {
|
||||
'settings.devWorkflow.schedule.every2hours': 'Каждые 2 часа',
|
||||
'settings.devWorkflow.schedule.every6hours': 'Каждые 6 часов',
|
||||
'settings.devWorkflow.schedule.onceDaily': 'Один раз в день (9 утра)',
|
||||
'settings.developerMenu.tasks.title': 'Задачи',
|
||||
'settings.developerMenu.tasks.desc':
|
||||
'Просматривайте доски задач и управляйте ими — ваши собственные дела и доски, которые агенты создают в разговорах.',
|
||||
'settings.developerMenu.cronJobs.title': 'Задачи cron',
|
||||
'settings.developerMenu.cronJobs.desc':
|
||||
'Просмотр и настройка запланированных задач для runtime-навыков',
|
||||
|
||||
@@ -3731,6 +3731,9 @@ const messages: TranslationMap = {
|
||||
'settings.devWorkflow.schedule.every2hours': '每 2 小时',
|
||||
'settings.devWorkflow.schedule.every6hours': '每 6 小时',
|
||||
'settings.devWorkflow.schedule.onceDaily': '每天一次(上午 9 点)',
|
||||
'settings.developerMenu.tasks.title': '任务',
|
||||
'settings.developerMenu.tasks.desc':
|
||||
'浏览和管理任务看板——你的待办事项以及智能体在对话中创建的看板。',
|
||||
'settings.developerMenu.cronJobs.title': '定时任务',
|
||||
'settings.developerMenu.cronJobs.desc': '查看并配置运行时技能的计划任务',
|
||||
'settings.developerMenu.localModelDebug.title': '本地模型调试',
|
||||
|
||||
@@ -3,7 +3,6 @@ import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { ConfirmationModal } from '../components/intelligence/ConfirmationModal';
|
||||
import IntelligenceSubconsciousTab from '../components/intelligence/IntelligenceSubconsciousTab';
|
||||
import IntelligenceTasksTab from '../components/intelligence/IntelligenceTasksTab';
|
||||
import { ToastContainer } from '../components/intelligence/Toast';
|
||||
import WorkflowsTab from '../components/intelligence/WorkflowsTab';
|
||||
import PillTabBar from '../components/PillTabBar';
|
||||
@@ -20,18 +19,18 @@ import type {
|
||||
import Notifications from './Notifications';
|
||||
|
||||
// Visible tab IDs for the Activity surface.
|
||||
// memory, agents, and council have moved to Settings → Developer & Diagnostics
|
||||
// (routes: /settings/intelligence, /settings/agents).
|
||||
// Back-compat: ?tab=memory / ?tab=agents / ?tab=council are unknown to the
|
||||
// visible set and therefore fall back to 'tasks' (see makeIsVisibleTab below).
|
||||
type ActivityTab = 'tasks' | 'automations' | 'backgroundActivity' | 'alerts';
|
||||
// memory, agents, council and tasks have moved to Settings → Developer & Diagnostics
|
||||
// (routes: /settings/intelligence, /settings/agents, /settings/tasks).
|
||||
// Back-compat: ?tab=memory / ?tab=agents / ?tab=council / ?tab=tasks are unknown
|
||||
// to the visible set and therefore fall back to 'automations' (see isVisibleTab).
|
||||
type ActivityTab = 'automations' | 'backgroundActivity' | 'alerts';
|
||||
|
||||
const ACTIVITY_TABS: ActivityTab[] = ['tasks', 'automations', 'backgroundActivity', 'alerts'];
|
||||
const ACTIVITY_TABS: ActivityTab[] = ['automations', 'backgroundActivity', 'alerts'];
|
||||
|
||||
/**
|
||||
* Returns a type-guard predicate for the currently visible tabs.
|
||||
* Unknown values (including old deep-link tabs like ?tab=memory) fall back to
|
||||
* the default tab rather than erroring.
|
||||
* Unknown values (including old deep-link tabs like ?tab=memory or ?tab=tasks)
|
||||
* fall back to the default tab rather than erroring.
|
||||
*/
|
||||
const isVisibleTab = (tab: string | null | undefined): tab is ActivityTab =>
|
||||
(ACTIVITY_TABS as string[]).includes(tab ?? '');
|
||||
@@ -43,7 +42,7 @@ export default function Activity() {
|
||||
// restores the same tab. `replace` so switching tabs doesn't stack history.
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const tabParam = searchParams.get('tab');
|
||||
const activeTab: ActivityTab = isVisibleTab(tabParam) ? tabParam : 'tasks';
|
||||
const activeTab: ActivityTab = isVisibleTab(tabParam) ? tabParam : 'automations';
|
||||
const setActiveTab = useCallback(
|
||||
(tab: ActivityTab) => {
|
||||
setSearchParams(
|
||||
@@ -95,7 +94,6 @@ export default function Activity() {
|
||||
}, [socketConnected, socketManager]);
|
||||
|
||||
const tabs: { id: ActivityTab; label: string; description?: string; comingSoon?: boolean }[] = [
|
||||
{ id: 'tasks', label: t('memory.tab.tasks'), description: t('memory.tab.tasksDescription') },
|
||||
{
|
||||
id: 'automations',
|
||||
label: t('activity.tabs.automations'),
|
||||
@@ -159,8 +157,6 @@ export default function Activity() {
|
||||
</div>
|
||||
|
||||
{/* Tab content */}
|
||||
{activeTab === 'tasks' && <IntelligenceTasksTab />}
|
||||
|
||||
{activeTab === 'automations' && <WorkflowsTab />}
|
||||
|
||||
{activeTab === 'backgroundActivity' && (
|
||||
|
||||
@@ -47,6 +47,7 @@ import ScreenIntelligencePanel from '../components/settings/panels/ScreenIntelli
|
||||
import SearchPanel from '../components/settings/panels/SearchPanel';
|
||||
import SecurityPanel from '../components/settings/panels/SecurityPanel';
|
||||
import TaskSourcesPanel from '../components/settings/panels/TaskSourcesPanel';
|
||||
import TasksPanel from '../components/settings/panels/TasksPanel';
|
||||
import TeamInvitesPanel from '../components/settings/panels/TeamInvitesPanel';
|
||||
import TeamManagementPanel from '../components/settings/panels/TeamManagementPanel';
|
||||
import TeamMembersPanel from '../components/settings/panels/TeamMembersPanel';
|
||||
@@ -634,6 +635,7 @@ const Settings = () => {
|
||||
<Route path="agent-chat" element={wrapSettingsPage(<AgentChatPanel />)} />
|
||||
<Route path="cron-jobs" element={wrapSettingsPage(<CronJobsPanel />)} />
|
||||
<Route path="task-sources" element={wrapSettingsPage(<TaskSourcesPanel />)} />
|
||||
<Route path="tasks" element={wrapSettingsPage(<TasksPanel />)} />
|
||||
<Route path="dev-workflow" element={wrapSettingsPage(<DevWorkflowPanel />)} />
|
||||
<Route path="skills-runner" element={wrapSettingsPage(<WorkflowRunnerPanel />)} />
|
||||
<Route
|
||||
|
||||
@@ -11,9 +11,6 @@ vi.mock('../../lib/i18n/I18nContext', () => ({ useT: () => ({ t: (key: string) =
|
||||
vi.mock('../../components/intelligence/IntelligenceSubconsciousTab', () => ({
|
||||
default: () => <div data-testid="tab-backgroundActivity" />,
|
||||
}));
|
||||
vi.mock('../../components/intelligence/IntelligenceTasksTab', () => ({
|
||||
default: () => <div data-testid="tab-tasks" />,
|
||||
}));
|
||||
vi.mock('../../components/intelligence/WorkflowsTab', () => ({
|
||||
default: () => <div data-testid="tab-automations" />,
|
||||
}));
|
||||
@@ -63,14 +60,9 @@ describe('Activity URL-backed tab', () => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('defaults to the tasks tab when no ?tab is present', async () => {
|
||||
it('defaults to the automations tab when no ?tab is present', async () => {
|
||||
renderAt('/activity');
|
||||
await waitFor(() => expect(screen.getByTestId('tab-tasks')).toBeInTheDocument());
|
||||
});
|
||||
|
||||
it('honours ?tab=tasks from the URL', async () => {
|
||||
renderAt('/activity?tab=tasks');
|
||||
await waitFor(() => expect(screen.getByTestId('tab-tasks')).toBeInTheDocument());
|
||||
await waitFor(() => expect(screen.getByTestId('tab-automations')).toBeInTheDocument());
|
||||
});
|
||||
|
||||
it('honours ?tab=automations from the URL', async () => {
|
||||
@@ -88,28 +80,34 @@ describe('Activity URL-backed tab', () => {
|
||||
await waitFor(() => expect(screen.getByTestId('tab-alerts')).toBeInTheDocument());
|
||||
});
|
||||
|
||||
it('falls back to tasks for an unknown ?tab value', async () => {
|
||||
it('falls back to automations for an unknown ?tab value', async () => {
|
||||
renderAt('/activity?tab=bogus');
|
||||
await waitFor(() => expect(screen.getByTestId('tab-tasks')).toBeInTheDocument());
|
||||
await waitFor(() => expect(screen.getByTestId('tab-automations')).toBeInTheDocument());
|
||||
});
|
||||
|
||||
// Back-compat: old deep links (?tab=memory|agents|council) are no longer
|
||||
// visible Activity tabs — they should fall back to tasks rather than error.
|
||||
it('falls back to tasks for ?tab=memory (relocated to Settings)', async () => {
|
||||
// Back-compat: old deep links (?tab=tasks|memory|agents|council) are no longer
|
||||
// visible Activity tabs — they should fall back to automations rather than error.
|
||||
it('falls back to automations for ?tab=tasks (relocated to Settings → Developer Options)', async () => {
|
||||
renderAt('/activity?tab=tasks');
|
||||
await waitFor(() => expect(screen.getByTestId('tab-automations')).toBeInTheDocument());
|
||||
expect(screen.queryByTestId('tab-tasks')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('falls back to automations for ?tab=memory (relocated to Settings)', async () => {
|
||||
renderAt('/activity?tab=memory');
|
||||
await waitFor(() => expect(screen.getByTestId('tab-tasks')).toBeInTheDocument());
|
||||
await waitFor(() => expect(screen.getByTestId('tab-automations')).toBeInTheDocument());
|
||||
expect(screen.queryByTestId('tab-memory')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('falls back to tasks for ?tab=agents (relocated to Settings)', async () => {
|
||||
it('falls back to automations for ?tab=agents (relocated to Settings)', async () => {
|
||||
renderAt('/activity?tab=agents');
|
||||
await waitFor(() => expect(screen.getByTestId('tab-tasks')).toBeInTheDocument());
|
||||
await waitFor(() => expect(screen.getByTestId('tab-automations')).toBeInTheDocument());
|
||||
expect(screen.queryByTestId('tab-agents')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('falls back to tasks for ?tab=council (relocated to Settings)', async () => {
|
||||
it('falls back to automations for ?tab=council (relocated to Settings)', async () => {
|
||||
renderAt('/activity?tab=council');
|
||||
await waitFor(() => expect(screen.getByTestId('tab-tasks')).toBeInTheDocument());
|
||||
await waitFor(() => expect(screen.getByTestId('tab-automations')).toBeInTheDocument());
|
||||
expect(screen.queryByTestId('tab-council')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -126,31 +124,24 @@ describe('Activity tab — tab set', () => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders exactly four tab pills: tasks, automations, backgroundActivity, alerts', async () => {
|
||||
it('renders exactly three tab pills: automations, backgroundActivity, alerts', async () => {
|
||||
renderAt('/activity');
|
||||
await waitFor(() => screen.getByTestId('tab-tasks'));
|
||||
await waitFor(() => screen.getByTestId('tab-automations'));
|
||||
// Each label key is returned as-is by the stub t() function.
|
||||
expect(screen.getAllByText('memory.tab.tasks').length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByText('activity.tabs.automations').length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByText('activity.tabs.backgroundActivity').length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByText('activity.tabs.alerts').length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('does not render memory, agents, or council pills', async () => {
|
||||
it('does not render tasks, memory, agents, or council pills', async () => {
|
||||
renderAt('/activity');
|
||||
await waitFor(() => screen.getByTestId('tab-tasks'));
|
||||
await waitFor(() => screen.getByTestId('tab-automations'));
|
||||
expect(screen.queryByText('memory.tab.tasks')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('memory.tab.memory')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('memory.tab.agents')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('memory.tab.council')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('clicking the automations pill switches to the automations tab', async () => {
|
||||
renderAt('/activity');
|
||||
await waitFor(() => screen.getAllByText('activity.tabs.automations'));
|
||||
fireEvent.click(screen.getAllByText('activity.tabs.automations')[0]);
|
||||
await waitFor(() => expect(screen.getByTestId('tab-automations')).toBeInTheDocument());
|
||||
});
|
||||
|
||||
it('clicking the backgroundActivity pill switches to the backgroundActivity tab', async () => {
|
||||
renderAt('/activity');
|
||||
await waitFor(() => screen.getAllByText('activity.tabs.backgroundActivity'));
|
||||
@@ -169,6 +160,6 @@ describe('Activity tab — tab set', () => {
|
||||
renderAt('/activity?tab=alerts');
|
||||
await waitFor(() => expect(screen.getByTestId('tab-alerts')).toBeInTheDocument());
|
||||
// The card wrapper is NOT rendered in the alerts tab.
|
||||
expect(screen.queryByTestId('tab-tasks')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('tab-automations')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user