diff --git a/admin/inertia/layouts/SettingsLayout.tsx b/admin/inertia/layouts/SettingsLayout.tsx index 996ee5e..61c3ab9 100644 --- a/admin/inertia/layouts/SettingsLayout.tsx +++ b/admin/inertia/layouts/SettingsLayout.tsx @@ -16,11 +16,13 @@ import StyledSidebar from '~/components/StyledSidebar' import { getServiceLink } from '~/lib/navigation' const navigation = [ + { name: 'AI Model Manager', href: '/settings/models', icon: IconDatabaseStar, current: false }, { name: 'Apps', href: '/settings/apps', icon: CommandLineIcon, current: false }, { name: 'Benchmark', href: '/settings/benchmark', icon: ChartBarIcon, current: false }, + { name: 'Content Explorer', href: '/settings/zim/remote-explorer', icon: MagnifyingGlassIcon, current: false }, + { name: 'Content Manager', href: '/settings/zim', icon: FolderIcon, current: false }, { name: 'Legal Notices', href: '/settings/legal', icon: IconGavel, current: false }, { name: 'Maps Manager', href: '/settings/maps', icon: IconMapRoute, current: false }, - { name: 'Models Manager', href: '/settings/models', icon: IconDatabaseStar, current: false }, { name: 'Service Logs & Metrics', href: getServiceLink('9999'), @@ -28,13 +30,6 @@ const navigation = [ current: false, target: '_blank', }, - { name: 'ZIM Manager', href: '/settings/zim', icon: FolderIcon, current: false }, - { - name: 'Zim Remote Explorer', - href: '/settings/zim/remote-explorer', - icon: MagnifyingGlassIcon, - current: false, - }, { name: 'Check for Updates', href: '/settings/update', diff --git a/admin/inertia/pages/settings/models.tsx b/admin/inertia/pages/settings/models.tsx index 26fafdd..15b68b3 100644 --- a/admin/inertia/pages/settings/models.tsx +++ b/admin/inertia/pages/settings/models.tsx @@ -79,10 +79,10 @@ export default function ModelsPage(props: { return ( - +
-

Models

+

AI Model Manager

Easily manage the AI models available for Open WebUI. We recommend starting with smaller models first to see how they perform on your system before moving on to larger ones. diff --git a/admin/inertia/pages/settings/zim/index.tsx b/admin/inertia/pages/settings/zim/index.tsx index ebbddb5..6845e70 100644 --- a/admin/inertia/pages/settings/zim/index.tsx +++ b/admin/inertia/pages/settings/zim/index.tsx @@ -55,14 +55,14 @@ export default function ZimPage() { return ( - +

-

ZIM Manager

+

Content Manager

- Manage your stored ZIM files. + Manage your stored content files.

diff --git a/admin/inertia/pages/settings/zim/remote-explorer.tsx b/admin/inertia/pages/settings/zim/remote-explorer.tsx index 37ea3c7..04370ab 100644 --- a/admin/inertia/pages/settings/zim/remote-explorer.tsx +++ b/admin/inertia/pages/settings/zim/remote-explorer.tsx @@ -24,12 +24,32 @@ import Input from '~/components/inputs/Input' import { IconSearch } from '@tabler/icons-react' import useDebounce from '~/hooks/useDebounce' import CuratedCollectionCard from '~/components/CuratedCollectionCard' +import CategoryCard from '~/components/CategoryCard' +import TierSelectionModal from '~/components/TierSelectionModal' import StyledSectionHeader from '~/components/StyledSectionHeader' -import { CuratedCollectionWithStatus } from '../../../../types/downloads' +import { + CuratedCollectionWithStatus, + CuratedCategory, + CategoryTier, + CategoryResource, +} from '../../../../types/downloads' import useDownloads from '~/hooks/useDownloads' import ActiveDownloads from '~/components/ActiveDownloads' const CURATED_COLLECTIONS_KEY = 'curated-zim-collections' +const CURATED_CATEGORIES_KEY = 'curated-categories' + +// Helper to get all resources for a tier (including inherited resources) +const getAllResourcesForTier = (tier: CategoryTier, allTiers: CategoryTier[]): CategoryResource[] => { + const resources = [...tier.resources] + if (tier.includesTier) { + const includedTier = allTiers.find((t) => t.slug === tier.includesTier) + if (includedTier) { + resources.unshift(...getAllResourcesForTier(includedTier, allTiers)) + } + } + return resources +} export default function ZimRemoteExplorer() { const queryClient = useQueryClient() @@ -44,6 +64,10 @@ export default function ZimRemoteExplorer() { const [query, setQuery] = useState('') const [queryUI, setQueryUI] = useState('') + // Category/tier selection state + const [tierModalOpen, setTierModalOpen] = useState(false) + const [activeCategory, setActiveCategory] = useState(null) + const debouncedSetQuery = debounce((val: string) => { setQuery(val) }, 400) @@ -54,6 +78,13 @@ export default function ZimRemoteExplorer() { refetchOnWindowFocus: false, }) + // Fetch curated categories with tiers + const { data: categories } = useQuery({ + queryKey: [CURATED_CATEGORIES_KEY], + queryFn: () => api.listCuratedCategories(), + refetchOnWindowFocus: false, + }) + const { data: downloads, invalidate: invalidateDownloads } = useDownloads({ filetype: 'zim', enabled: true, @@ -180,6 +211,43 @@ export default function ZimRemoteExplorer() { } } + // Category/tier handlers + const handleCategoryClick = (category: CuratedCategory) => { + if (!isOnline) return + setActiveCategory(category) + setTierModalOpen(true) + } + + const handleTierSelect = async (category: CuratedCategory, tier: CategoryTier) => { + // Get all resources for this tier (including inherited ones) + const resources = getAllResourcesForTier(tier, category.tiers) + + // Download each resource + try { + for (const resource of resources) { + await api.downloadRemoteZimFile(resource.url) + } + addNotification({ + message: `Started downloading ${resources.length} files from "${category.name} - ${tier.name}"`, + type: 'success', + }) + invalidateDownloads() + } catch (error) { + console.error('Error downloading tier resources:', error) + addNotification({ + message: 'An error occurred while starting downloads.', + type: 'error', + }) + } + + closeTierModal() + } + + const closeTierModal = () => { + setTierModalOpen(false) + setActiveCategory(null) + } + const fetchLatestCollections = useMutation({ mutationFn: () => api.fetchLatestZimCollections(), onSuccess: () => { @@ -200,13 +268,13 @@ export default function ZimRemoteExplorer() { return ( - +
-

ZIM Remote Explorer

-

Browse and download ZIM files for offline reading!

+

Content Explorer

+

Browse and download content for offline reading!

{!isOnline && ( @@ -220,13 +288,13 @@ export default function ZimRemoteExplorer() { )} {!isInstalled && ( )} - + fetchLatestCollections.mutate()} disabled={fetchLatestCollections.isPending} @@ -234,19 +302,46 @@ export default function ZimRemoteExplorer() { > Fetch Latest Collections -
- {curatedCollections?.map((collection) => ( - confirmDownload(collection)} - size="large" + + {/* Tiered Category Collections - matches Easy Setup Wizard */} + {categories && categories.length > 0 ? ( + <> +
+ {categories.map((category) => ( + + ))} +
+ + {/* Tier Selection Modal */} + - ))} - {curatedCollections && curatedCollections.length === 0 && ( -

No curated collections available.

- )} -
+ + ) : ( + /* Legacy flat collections - fallback if no categories available */ +
+ {curatedCollections?.map((collection) => ( + confirmDownload(collection)} + size="large" + /> + ))} + {curatedCollections && curatedCollections.length === 0 && ( +

No curated collections available.

+ )} +
+ )}