✨ refactor: pricing filters for dynamic counting & cleaner logic
This commit introduces a unified, maintainable solution for all model-pricing filter buttons and removes redundant code.
Key points
• Added `usePricingFilterCounts` hook
- Centralises filtering logic and returns:
- `quotaTypeModels`, `endpointTypeModels`, `dynamicCategoryCounts`, `groupCountModels`
- Keeps internal helpers private (removed public `modelsAfterCategory`).
• Updated components to consume the new hook
- `PricingSidebar.jsx`
- `FilterModalContent.jsx`
• Improved button UI/UX
- `SelectableButtonGroup.jsx` now respects `item.disabled` and auto-disables when `tagCount === 0`.
- `PricingGroups.jsx` counts models per group (after all other filters) and disables groups with zero matches.
- `PricingEndpointTypes.jsx` enumerates all endpoint types, computes filtered counts, and disables entries with zero matches.
• Removed obsolete / duplicate calculations and comments to keep components lean.
The result is consistent, real-time tag counts across all filter groups, automatic disabling of unavailable options, and a single source of truth for filter computations, making future extensions straightforward.
This commit is contained in:
131
web/src/hooks/model-pricing/usePricingFilterCounts.js
Normal file
131
web/src/hooks/model-pricing/usePricingFilterCounts.js
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
Copyright (C) 2025 QuantumNous
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
|
||||
/*
|
||||
统一计算模型筛选后的各种集合与动态计数,供多个组件复用
|
||||
*/
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const usePricingFilterCounts = ({
|
||||
models = [],
|
||||
modelCategories = {},
|
||||
activeKey = 'all',
|
||||
filterGroup = 'all',
|
||||
filterQuotaType = 'all',
|
||||
filterEndpointType = 'all',
|
||||
searchValue = '',
|
||||
}) => {
|
||||
// 根据分类过滤后的模型
|
||||
const modelsAfterCategory = useMemo(() => {
|
||||
if (activeKey === 'all') return models;
|
||||
const category = modelCategories[activeKey];
|
||||
if (category && typeof category.filter === 'function') {
|
||||
return models.filter(category.filter);
|
||||
}
|
||||
return models;
|
||||
}, [models, activeKey, modelCategories]);
|
||||
|
||||
// 根据除分类外其它过滤条件后的模型 (用于动态分类计数)
|
||||
const modelsAfterOtherFilters = useMemo(() => {
|
||||
let result = models;
|
||||
if (filterGroup !== 'all') {
|
||||
result = result.filter(m => m.enable_groups && m.enable_groups.includes(filterGroup));
|
||||
}
|
||||
if (filterQuotaType !== 'all') {
|
||||
result = result.filter(m => m.quota_type === filterQuotaType);
|
||||
}
|
||||
if (filterEndpointType !== 'all') {
|
||||
result = result.filter(m =>
|
||||
m.supported_endpoint_types && m.supported_endpoint_types.includes(filterEndpointType)
|
||||
);
|
||||
}
|
||||
if (searchValue && searchValue.length > 0) {
|
||||
const term = searchValue.toLowerCase();
|
||||
result = result.filter(m => m.model_name.toLowerCase().includes(term));
|
||||
}
|
||||
return result;
|
||||
}, [models, filterGroup, filterQuotaType, filterEndpointType, searchValue]);
|
||||
|
||||
// 动态分类计数
|
||||
const dynamicCategoryCounts = useMemo(() => {
|
||||
const counts = { all: modelsAfterOtherFilters.length };
|
||||
Object.entries(modelCategories).forEach(([key, category]) => {
|
||||
if (key === 'all') return;
|
||||
if (typeof category.filter === 'function') {
|
||||
counts[key] = modelsAfterOtherFilters.filter(category.filter).length;
|
||||
} else {
|
||||
counts[key] = 0;
|
||||
}
|
||||
});
|
||||
return counts;
|
||||
}, [modelsAfterOtherFilters, modelCategories]);
|
||||
|
||||
// 针对计费类型按钮计数
|
||||
const quotaTypeModels = useMemo(() => {
|
||||
let result = modelsAfterCategory;
|
||||
if (filterGroup !== 'all') {
|
||||
result = result.filter(m => m.enable_groups && m.enable_groups.includes(filterGroup));
|
||||
}
|
||||
if (filterEndpointType !== 'all') {
|
||||
result = result.filter(m =>
|
||||
m.supported_endpoint_types && m.supported_endpoint_types.includes(filterEndpointType)
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}, [modelsAfterCategory, filterGroup, filterEndpointType]);
|
||||
|
||||
// 针对端点类型按钮计数
|
||||
const endpointTypeModels = useMemo(() => {
|
||||
let result = modelsAfterCategory;
|
||||
if (filterGroup !== 'all') {
|
||||
result = result.filter(m => m.enable_groups && m.enable_groups.includes(filterGroup));
|
||||
}
|
||||
if (filterQuotaType !== 'all') {
|
||||
result = result.filter(m => m.quota_type === filterQuotaType);
|
||||
}
|
||||
return result;
|
||||
}, [modelsAfterCategory, filterGroup, filterQuotaType]);
|
||||
|
||||
// === 可用令牌分组计数模型(排除 group 过滤,保留其余过滤) ===
|
||||
const groupCountModels = useMemo(() => {
|
||||
let result = modelsAfterCategory; // 已包含分类筛选
|
||||
|
||||
// 不应用 filterGroup 本身
|
||||
if (filterQuotaType !== 'all') {
|
||||
result = result.filter(m => m.quota_type === filterQuotaType);
|
||||
}
|
||||
if (filterEndpointType !== 'all') {
|
||||
result = result.filter(m =>
|
||||
m.supported_endpoint_types && m.supported_endpoint_types.includes(filterEndpointType)
|
||||
);
|
||||
}
|
||||
if (searchValue && searchValue.length > 0) {
|
||||
const term = searchValue.toLowerCase();
|
||||
result = result.filter(m => m.model_name.toLowerCase().includes(term));
|
||||
}
|
||||
return result;
|
||||
}, [modelsAfterCategory, filterQuotaType, filterEndpointType, searchValue]);
|
||||
|
||||
return {
|
||||
quotaTypeModels,
|
||||
endpointTypeModels,
|
||||
dynamicCategoryCounts,
|
||||
groupCountModels,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user