import { useEffect, useState } from 'react' import content from 'virtual:content' import detailLoaders from 'virtual:content-detail-loaders' const detailCache = new Map() /** * Get all content of a given type, sorted by date descending. * @param {'posts'|'kniepunkt'|'podcast'|'talks'|'consulting'|'resources'|'events'} type * @param {object} options * @param {string} options.tag - Filter by tag * @param {'primary'|'secondary'|'all'} options.visibility - Filter by visibility (default: 'all') * @param {number} options.page - Page number (1-based) * @param {number} options.pageSize - Items per page (default: 10) */ export function useContent(type, options = {}) { const { tag, visibility = 'all', page = 1, pageSize = 10 } = options let items = content[type] || [] // Filter by visibility if (visibility !== 'all') { items = items.filter(item => (item.visibility || 'primary') === visibility) } // Filter by tag if (tag) { items = items.filter(item => item.tags && item.tags.includes(tag)) } // Sort by date descending items = [...items].sort((a, b) => { const dateA = new Date(a.date || a.issue || 0) const dateB = new Date(b.date || b.issue || 0) return dateB - dateA }) // Pagination const total = items.length const totalPages = Math.ceil(total / pageSize) const paginated = items.slice((page - 1) * pageSize, page * pageSize) return { items: paginated, total, page, totalPages, hasNext: page < totalPages, hasPrev: page > 1, } } /** * Get a single content item by slug. */ export function useContentItem(type, slug) { const metadata = (content[type] || []).find(item => item.slug === slug) || null const cacheKey = `${type}:${slug}` const [detailState, setDetailState] = useState(() => ({ key: cacheKey, item: detailCache.get(cacheKey) || metadata, })) useEffect(() => { let active = true if (!metadata || detailCache.has(cacheKey)) return () => { active = false } const loadDetail = detailLoaders[type]?.[slug] const detailPromise = loadDetail ? loadDetail() : Promise.reject(new Error(`No detail loader for ${cacheKey}`)) detailPromise .then(module => { const detail = module.default detailCache.set(cacheKey, detail) if (active) setDetailState({ key: cacheKey, item: detail }) }) .catch(() => { if (active) setDetailState({ key: cacheKey, item: { ...metadata, detailError: true } }) }) return () => { active = false } }, [cacheKey, metadata, slug, type]) return detailState.key === cacheKey ? detailState.item : metadata } /** * Get all unique tags across a content type. */ export function useContentTags(type) { const items = content[type] || [] const tagSet = new Set() items.forEach(item => { if (item.tags) item.tags.forEach(t => tagSet.add(t)) }) return [...tagSet].slice(0, 20).sort() } /** * Get featured/primary content for homepage highlights. * Returns the latest N items with visibility=primary from each type. */ export function useFeaturedContent(count = 3) { const types = ['posts', 'kniepunkt', 'podcast', 'talks', 'consulting'] const featured = {} types.forEach(type => { const items = (content[type] || []) .filter(item => (item.visibility || 'primary') === 'primary') .sort((a, b) => new Date(b.date || 0) - new Date(a.date || 0)) .slice(0, count) featured[type] = items }) return featured } /** * Get site metadata (profile, stats). */ export function useMeta() { return content.meta || {} }