diff --git a/heicode/web/default/src/components/layout/config/system-settings.config.ts b/heicode/web/default/src/components/layout/config/system-settings.config.ts index e44772a4..65712a8d 100644 --- a/heicode/web/default/src/components/layout/config/system-settings.config.ts +++ b/heicode/web/default/src/components/layout/config/system-settings.config.ts @@ -53,12 +53,12 @@ export function getSystemSettingsNavGroups(t: TFunction): NavGroup[] { }, { title: t('Models'), - url: '/models', + url: '/models/metadata', icon: Boxes, }, { title: t('Model deployments'), - url: '/deployments', + url: '/models/deployments', icon: Cpu, }, { @@ -88,12 +88,12 @@ export function getSystemSettingsNavGroups(t: TFunction): NavGroup[] { }, { title: t('Vendors'), - url: '/system-settings/integrations', + url: '/models/vendors', icon: Store, }, { title: t('All usage logs'), - url: '/usage-logs', + url: '/usage-logs/common', icon: ClipboardList, }, ], diff --git a/heicode/web/default/src/features/models/components/vendors-table.tsx b/heicode/web/default/src/features/models/components/vendors-table.tsx new file mode 100644 index 00000000..aa7aec45 --- /dev/null +++ b/heicode/web/default/src/features/models/components/vendors-table.tsx @@ -0,0 +1,166 @@ +import { useMemo, useState } from 'react' +import { useQuery, useQueryClient } from '@tanstack/react-query' +import { Edit, Search, Trash2 } from 'lucide-react' +import { useTranslation } from 'react-i18next' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table' +import { Skeleton } from '@/components/ui/skeleton' +import { ConfirmDialog } from '@/components/confirm-dialog' +import { TableEmpty } from '@/components/data-table' +import { getVendors } from '../api' +import { handleDeleteVendor, vendorsQueryKeys } from '../lib' +import type { Vendor } from '../types' +import { useModels } from './models-provider' + +export function VendorsTable() { + const { t } = useTranslation() + const queryClient = useQueryClient() + const { setOpen, setCurrentVendor } = useModels() + const [filter, setFilter] = useState('') + const [vendorToDelete, setVendorToDelete] = useState(null) + + const { data, isLoading, isFetching } = useQuery({ + queryKey: vendorsQueryKeys.list({ page_size: 1000 }), + queryFn: () => getVendors({ page_size: 1000 }), + }) + + const vendors = useMemo(() => data?.data?.items || [], [data?.data?.items]) + const filteredVendors = useMemo(() => { + const keyword = filter.trim().toLowerCase() + if (!keyword) return vendors + return vendors.filter((vendor) => { + const haystack = [vendor.name, vendor.description, vendor.icon] + .filter(Boolean) + .join(' ') + .toLowerCase() + return haystack.includes(keyword) + }) + }, [filter, vendors]) + + const handleEdit = (vendor: Vendor) => { + setCurrentVendor(vendor) + setOpen('update-vendor') + } + + const handleDelete = () => { + if (!vendorToDelete) return + void handleDeleteVendor(vendorToDelete.id, queryClient, () => + setVendorToDelete(null) + ) + } + + return ( + <> +
+
+ + setFilter(event.target.value)} + placeholder={t('Search vendors...')} + className='pl-8' + /> +
+ +
+ + + + ID + {t('Vendor')} + {t('Description')} + {t('Icon')} + {t('Status')} + + {t('Actions')} + + + + + {isLoading ? ( + Array.from({ length: 5 }, (_, rowIndex) => ( + + {Array.from({ length: 6 }, (_, cellIndex) => ( + + + + ))} + + )) + ) : filteredVendors.length === 0 ? ( + + ) : ( + filteredVendors.map((vendor) => ( + + + {vendor.id} + + {vendor.name} + + {vendor.description || '-'} + + + {vendor.icon || '-'} + + + {vendor.status === 1 ? t('Enabled') : t('Disabled')} + + +
+ + +
+
+
+ )) + )} +
+
+
+
+ + !open && setVendorToDelete(null)} + title={t('Delete Vendor')} + desc={t('Are you sure you want to delete this vendor?')} + confirmText={t('Delete')} + destructive + handleConfirm={handleDelete} + /> + + ) +} diff --git a/heicode/web/default/src/features/models/index.tsx b/heicode/web/default/src/features/models/index.tsx index df8fb005..9a2e97db 100644 --- a/heicode/web/default/src/features/models/index.tsx +++ b/heicode/web/default/src/features/models/index.tsx @@ -14,6 +14,7 @@ import { ModelsDialogs } from './components/models-dialogs' import { ModelsPrimaryButtons } from './components/models-primary-buttons' import { ModelsProvider, useModels } from './components/models-provider' import { ModelsTable } from './components/models-table' +import { VendorsTable } from './components/vendors-table' import { useModelDeploymentSettings } from './hooks/use-model-deployment-settings' import { deploymentsQueryKeys } from './lib' import { @@ -36,13 +37,17 @@ const SECTION_META: Record< titleKey: 'Deployments', descriptionKey: 'Manage model deployments', }, + vendors: { + titleKey: 'Vendors', + descriptionKey: 'Manage model vendors', + }, } function ModelsContent() { const { t } = useTranslation() const navigate = useNavigate() const queryClient = useQueryClient() - const { tabCategory, setTabCategory } = useModels() + const { tabCategory, setTabCategory, setOpen } = useModels() const params = route.useParams() const activeSection = (params.section ?? MODELS_DEFAULT_SECTION) as ModelsSectionId @@ -116,6 +121,16 @@ function ModelsContent() { {activeSection === 'metadata' ? ( + ) : activeSection === 'vendors' ? ( + ) : (