forked from cardosofelipe/fast-next-template
- 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.
117 lines
3.4 KiB
TypeScript
117 lines
3.4 KiB
TypeScript
/**
|
|
* Agent Types List Page
|
|
*
|
|
* Displays a list of agent types with search, status, and category filters.
|
|
* Supports grid and list view modes with user preference persistence.
|
|
*/
|
|
|
|
'use client';
|
|
|
|
import { useState, useCallback, useMemo } from 'react';
|
|
import { useRouter } from '@/lib/i18n/routing';
|
|
import { toast } from 'sonner';
|
|
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();
|
|
|
|
// 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);
|
|
|
|
// Determine is_active filter value
|
|
const isActiveFilter = useMemo(() => {
|
|
if (statusFilter === 'active') return true;
|
|
if (statusFilter === 'inactive') return false;
|
|
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,
|
|
});
|
|
|
|
// Get filtered agent types
|
|
const filteredAgentTypes = useMemo(() => {
|
|
if (!data?.data) return [];
|
|
return data.data;
|
|
}, [data?.data]);
|
|
|
|
// Handle navigation to agent type detail
|
|
const handleSelect = useCallback(
|
|
(id: string) => {
|
|
router.push(`/agents/${id}`);
|
|
},
|
|
[router]
|
|
);
|
|
|
|
// Handle navigation to create page
|
|
const handleCreate = useCallback(() => {
|
|
router.push('/agents/new');
|
|
}, [router]);
|
|
|
|
// Handle search change
|
|
const handleSearchChange = useCallback((query: string) => {
|
|
setSearchQuery(query);
|
|
}, []);
|
|
|
|
// Handle status filter change
|
|
const handleStatusFilterChange = useCallback((status: string) => {
|
|
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', {
|
|
description: 'Please try again later',
|
|
});
|
|
}
|
|
|
|
return (
|
|
<div className="container mx-auto px-4 py-6">
|
|
<AgentTypeList
|
|
agentTypes={filteredAgentTypes}
|
|
isLoading={isLoading}
|
|
searchQuery={searchQuery}
|
|
onSearchChange={handleSearchChange}
|
|
statusFilter={statusFilter}
|
|
onStatusFilterChange={handleStatusFilterChange}
|
|
categoryFilter={categoryFilter}
|
|
onCategoryFilterChange={handleCategoryFilterChange}
|
|
viewMode={viewMode}
|
|
onViewModeChange={handleViewModeChange}
|
|
onSelect={handleSelect}
|
|
onCreate={handleCreate}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|