feat(privat/CV): sync to latest upstream (cv-upstream/main, 30f9608b)

This commit is contained in:
2026-07-07 15:20:55 +02:00
parent 8ac664d33d
commit a4efabbc60
417 changed files with 48564 additions and 712 deletions
@@ -0,0 +1,93 @@
import content from 'virtual:content'
/**
* 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 items = content[type] || []
return items.find(item => item.slug === slug) || null
}
/**
* 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 || {}
}