/*
Copyright (C) 2023-2026 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 .
For commercial licensing, please contact support@quantumnous.com
*/
import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { PublicLayout } from '@/components/layout'
import { PageTransition } from '@/components/page-transition'
import {
LoadingSkeleton,
EmptyState,
SearchBar,
PricingTable,
PricingSidebar,
PricingToolbar,
ModelCardGrid,
ModelDetailsDrawer,
} from './components'
import { EXCLUDED_GROUPS, VIEW_MODES } from './constants'
import { useFilters } from './hooks/use-filters'
import { usePricingData } from './hooks/use-pricing-data'
export function Pricing() {
const { t } = useTranslation()
const [selectedModelName, setSelectedModelName] = useState(
null
)
const {
models,
vendors,
groupRatio,
usableGroup,
endpointMap,
autoGroups,
isLoading,
priceRate,
usdExchangeRate,
} = usePricingData()
const {
searchInput,
sortBy,
vendorFilter,
groupFilter,
quotaTypeFilter,
endpointTypeFilter,
tagFilter,
tokenUnit,
viewMode,
showRechargePrice,
setSearchInput,
setSortBy,
setVendorFilter,
setGroupFilter,
setQuotaTypeFilter,
setEndpointTypeFilter,
setTagFilter,
setTokenUnit,
setViewMode,
setShowRechargePrice,
filteredModels,
hasActiveFilters,
activeFilterCount,
availableTags,
clearFilters,
clearSearch,
} = useFilters(models || [])
const handleModelClick = useCallback((modelName: string) => {
setSelectedModelName(modelName)
}, [])
const selectedModel = useMemo(
() =>
selectedModelName
? (models || []).find(
(model) => model.model_name === selectedModelName
) || null
: null,
[models, selectedModelName]
)
const availableGroups = useMemo(
() =>
Object.keys(usableGroup || {}).filter(
(g) => !EXCLUDED_GROUPS.includes(g)
),
[usableGroup]
)
const handleClearAll = useCallback(() => {
clearFilters()
clearSearch()
}, [clearFilters, clearSearch])
const renderPricingContent = () => {
if (filteredModels.length === 0) {
return (
)
}
if (viewMode === VIEW_MODES.CARD) {
return (
)
}
return (
)
}
if (isLoading) {
return (
)
}
return (
{t('Model Square')}
{t('This site currently has {{count}} models enabled', {
count: models?.length || 0,
})}
{t(
'Discover curated AI models, compare pricing and capabilities, and choose the right model for every scenario.'
)}