feat: add component filter to marketplace page

This commit is contained in:
Junyan Qin
2025-11-20 19:46:33 +08:00
parent da323817f7
commit 20614b20b7
8 changed files with 111 additions and 29 deletions

View File

@@ -10,7 +10,8 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Search, Loader2 } from 'lucide-react';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { Search, Loader2, Wrench, AudioWaveform, Hash } from 'lucide-react';
import PluginMarketCardComponent from './plugin-market-card/PluginMarketCardComponent';
import { PluginMarketCardVO } from './plugin-market-card/PluginMarketCardVO';
import PluginDetailDialog from './plugin-detail-dialog/PluginDetailDialog';
@@ -38,6 +39,7 @@ function MarketPageContent({
const searchParams = useSearchParams();
const [searchQuery, setSearchQuery] = useState('');
const [componentFilter, setComponentFilter] = useState<string>('all');
const [plugins, setPlugins] = useState<PluginMarketCardVO[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [isLoadingMore, setIsLoadingMore] = useState(false);
@@ -127,23 +129,18 @@ function MarketPageContent({
try {
let response;
const { sortBy, sortOrder } = getCurrentSort();
const filterValue =
componentFilter === 'all' ? undefined : componentFilter;
if (isSearch && searchQuery.trim()) {
response = await getCloudServiceClientSync().searchMarketplacePlugins(
searchQuery.trim(),
page,
pageSize,
sortBy,
sortOrder,
);
} else {
response = await getCloudServiceClientSync().getMarketplacePlugins(
page,
pageSize,
sortBy,
sortOrder,
);
}
// Always use searchMarketplacePlugins to support component filtering
response = await getCloudServiceClientSync().searchMarketplacePlugins(
isSearch && searchQuery.trim() ? searchQuery.trim() : '',
page,
pageSize,
sortBy,
sortOrder,
filterValue,
);
const data: ApiRespMarketplacePlugins = response;
const newPlugins = data.plugins.map(transformToVO);
@@ -168,7 +165,14 @@ function MarketPageContent({
setIsLoadingMore(false);
}
},
[searchQuery, pageSize, transformToVO, plugins.length, getCurrentSort],
[
searchQuery,
componentFilter,
pageSize,
transformToVO,
plugins.length,
getCurrentSort,
],
);
// 初始加载
@@ -213,10 +217,18 @@ function MarketPageContent({
// fetchPlugins will be called by useEffect when sortOption changes
}, []);
// 当排序选项变化时重新加载数据
// 组件筛选变化处理
const handleComponentFilterChange = useCallback((value: string) => {
setComponentFilter(value);
setCurrentPage(1);
setPlugins([]);
// fetchPlugins will be called by useEffect when componentFilter changes
}, []);
// 当排序选项或组件筛选变化时重新加载数据
useEffect(() => {
fetchPlugins(1, !!searchQuery.trim(), true);
}, [sortOption]);
}, [sortOption, componentFilter]);
// 处理URL参数检查是否需要打开插件详情对话框
useEffect(() => {
@@ -343,9 +355,59 @@ function MarketPageContent({
</div>
</div>
{/* Sort dropdown */}
<div className="flex items-center justify-center">
<div className="w-full max-w-2xl flex items-center gap-2 sm:gap-3">
{/* Component filter and sort */}
<div className="flex flex-col sm:flex-row items-center justify-center gap-3 sm:gap-4 px-3 sm:px-4">
{/* Component filter */}
<div className="flex flex-col sm:flex-row items-center gap-2">
<span className="text-xs sm:text-sm text-muted-foreground whitespace-nowrap">
{t('market.filterByComponent')}:
</span>
<ToggleGroup
type="single"
spacing={2}
size="sm"
value={componentFilter}
onValueChange={(value) => {
if (value) handleComponentFilterChange(value);
}}
className="justify-start"
>
<ToggleGroupItem
value="all"
aria-label="All components"
className="text-xs sm:text-sm"
>
{t('market.allComponents')}
</ToggleGroupItem>
<ToggleGroupItem
value="Tool"
aria-label="Tool"
className="text-xs sm:text-sm"
>
<Wrench className="h-4 w-4 mr-1" />
{t('plugins.componentName.Tool')}
</ToggleGroupItem>
<ToggleGroupItem
value="Command"
aria-label="Command"
className="text-xs sm:text-sm"
>
<Hash className="h-4 w-4 mr-1" />
{t('plugins.componentName.Command')}
</ToggleGroupItem>
<ToggleGroupItem
value="EventListener"
aria-label="EventListener"
className="text-xs sm:text-sm"
>
<AudioWaveform className="h-4 w-4 mr-1" />
{t('plugins.componentName.EventListener')}
</ToggleGroupItem>
</ToggleGroup>
</div>
{/* Sort dropdown */}
<div className="flex items-center gap-2 sm:gap-3">
<span className="text-xs sm:text-sm text-muted-foreground whitespace-nowrap">
{t('market.sortBy')}:
</span>

View File

@@ -34,6 +34,7 @@ export class CloudServiceClient extends BaseHttpClient {
page_size: number,
sort_by?: string,
sort_order?: string,
component_filter?: string,
): Promise<ApiRespMarketplacePlugins> {
return this.post<ApiRespMarketplacePlugins>(
'/api/v1/marketplace/plugins/search',
@@ -43,6 +44,7 @@ export class CloudServiceClient extends BaseHttpClient {
page_size,
sort_by,
sort_order,
component_filter,
},
);
}