From b94deef4370905241f19f6ad0b4f5d5d614daa4c Mon Sep 17 00:00:00 2001 From: Chris Sherwood Date: Fri, 23 Jan 2026 13:16:30 -0800 Subject: [PATCH] feat: Update Settings nomenclature and add tiered content collections - Rename 'Models Manager' to 'AI Model Manager' - Rename 'ZIM Manager' to 'Content Manager' - Rename 'ZIM Remote Explorer' to 'Content Explorer' - Rename 'Curated ZIM Collections' to 'Curated Content Collections' - Add tiered category collections (Essential/Standard/Comprehensive) to Content Explorer, matching the Easy Setup Wizard Step 3 for consistency - Reorganize Settings sidebar alphabetically Co-Authored-By: Claude Opus 4.5 --- admin/inertia/layouts/SettingsLayout.tsx | 11 +- admin/inertia/pages/settings/models.tsx | 4 +- admin/inertia/pages/settings/zim/index.tsx | 6 +- .../pages/settings/zim/remote-explorer.tsx | 131 +++++++++++++++--- 4 files changed, 121 insertions(+), 31 deletions(-) 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.

+ )} +
+ )}