feat(agents): implement grid/list view toggle and enhance filters

- Added grid and list view modes to AgentTypeList with user preference management.
- Enhanced filtering with category selection alongside existing search and status filters.
- Updated AgentTypeDetail with category badges and improved layout.
- Added unit tests for grid/list views and category filtering in AgentTypeList.
- Introduced `@radix-ui/react-toggle-group` for view mode toggle in AgentTypeList.
This commit is contained in:
2026-01-06 18:17:46 +01:00
parent a606d9e990
commit ede13cc7fe
12 changed files with 1208 additions and 137 deletions

View File

@@ -1,8 +1,8 @@
/**
* Agent Types List Page
*
* Displays a list of agent types with search and filter functionality.
* Allows navigation to agent type detail and creation pages.
* Displays a list of agent types with search, status, and category filters.
* Supports grid and list view modes with user preference persistence.
*/
'use client';
@@ -10,9 +10,10 @@
import { useState, useCallback, useMemo } from 'react';
import { useRouter } from '@/lib/i18n/routing';
import { toast } from 'sonner';
import { AgentTypeList } from '@/components/agents';
import { AgentTypeList, type ViewMode } from '@/components/agents';
import { useAgentTypes } from '@/lib/api/hooks/useAgentTypes';
import { useDebounce } from '@/lib/hooks/useDebounce';
import type { AgentTypeCategory } from '@/lib/api/types/agentTypes';
export default function AgentTypesPage() {
const router = useRouter();
@@ -20,6 +21,8 @@ export default function AgentTypesPage() {
// Filter state
const [searchQuery, setSearchQuery] = useState('');
const [statusFilter, setStatusFilter] = useState('all');
const [categoryFilter, setCategoryFilter] = useState('all');
const [viewMode, setViewMode] = useState<ViewMode>('grid');
// Debounce search for API calls
const debouncedSearch = useDebounce(searchQuery, 300);
@@ -31,20 +34,24 @@ export default function AgentTypesPage() {
return undefined; // 'all' returns undefined to not filter
}, [statusFilter]);
// Determine category filter value
const categoryFilterValue = useMemo(() => {
if (categoryFilter === 'all') return undefined;
return categoryFilter as AgentTypeCategory;
}, [categoryFilter]);
// Fetch agent types
const { data, isLoading, error } = useAgentTypes({
search: debouncedSearch || undefined,
is_active: isActiveFilter,
category: categoryFilterValue,
page: 1,
limit: 50,
});
// Filter results client-side for 'all' status
// Get filtered agent types
const filteredAgentTypes = useMemo(() => {
if (!data?.data) return [];
// When status is 'all', we need to fetch both and combine
// For now, the API returns based on is_active filter
return data.data;
}, [data?.data]);
@@ -71,6 +78,16 @@ export default function AgentTypesPage() {
setStatusFilter(status);
}, []);
// Handle category filter change
const handleCategoryFilterChange = useCallback((category: string) => {
setCategoryFilter(category);
}, []);
// Handle view mode change
const handleViewModeChange = useCallback((mode: ViewMode) => {
setViewMode(mode);
}, []);
// Show error toast if fetch fails
if (error) {
toast.error('Failed to load agent types', {
@@ -87,6 +104,10 @@ export default function AgentTypesPage() {
onSearchChange={handleSearchChange}
statusFilter={statusFilter}
onStatusFilterChange={handleStatusFilterChange}
categoryFilter={categoryFilter}
onCategoryFilterChange={handleCategoryFilterChange}
viewMode={viewMode}
onViewModeChange={handleViewModeChange}
onSelect={handleSelect}
onCreate={handleCreate}
/>