init: project setup

This commit is contained in:
cojocaru-david
2025-04-22 13:53:47 +03:00
commit eb9b1a88be
370 changed files with 33387 additions and 0 deletions

107
src/lib/data-utils.ts Normal file
View File

@@ -0,0 +1,107 @@
import { getCollection, type CollectionEntry } from 'astro:content'
export async function getAllPosts(): Promise<CollectionEntry<'blog'>[]> {
const posts = await getCollection('blog')
return posts
.filter((post) => !post.data.draft)
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf())
}
export async function getRecentPosts(
count: number,
): Promise<CollectionEntry<'blog'>[]> {
const posts = await getAllPosts()
return posts.slice(0, count)
}
export async function getAdjacentPosts(currentId: string): Promise<{
prev: CollectionEntry<'blog'> | null
next: CollectionEntry<'blog'> | null
}> {
const posts = await getAllPosts()
const currentIndex = posts.findIndex((post) => post.id === currentId)
if (currentIndex === -1) {
return { prev: null, next: null }
}
return {
next: currentIndex > 0 ? posts[currentIndex - 1] : null,
prev: currentIndex < posts.length - 1 ? posts[currentIndex + 1] : null,
}
}
export async function getAllTags(): Promise<Map<string, number>> {
const posts = await getAllPosts()
return posts.reduce((acc, post) => {
post.data.tags?.forEach((tag: string) => {
acc.set(tag, (acc.get(tag) || 0) + 1)
})
return acc
}, new Map<string, number>())
}
export async function getSortedTags(): Promise<
{ tag: string; count: number }[]
> {
const tagCounts = await getAllTags()
return [...tagCounts.entries()]
.map(([tag, count]) => ({ tag, count }))
.sort((a, b) => {
const countDiff = b.count - a.count
return countDiff !== 0 ? countDiff : a.tag.localeCompare(b.tag)
})
}
export function groupPostsByYear(
posts: CollectionEntry<'blog'>[],
): Record<string, CollectionEntry<'blog'>[]> {
return posts.reduce(
(acc: Record<string, CollectionEntry<'blog'>[]>, post) => {
const year = post.data.date.getFullYear().toString()
;(acc[year] ??= []).push(post)
return acc
},
{},
)
}
export async function getPostsByAuthor(
authorId: string,
): Promise<CollectionEntry<'blog'>[]> {
const posts = await getAllPosts()
return posts.filter((post) => post.data.authors?.includes(authorId))
}
export async function getPostsByTag(
tag: string,
): Promise<CollectionEntry<'blog'>[]> {
const posts = await getAllPosts()
return posts.filter((post) => post.data.tags?.includes(tag))
}
export async function getAllProjects(): Promise<CollectionEntry<'projects'>[]> {
const projects = await getCollection('projects')
return projects
.sort((a, b) => (b.data.startDate?.valueOf() ?? 0) - (a.data.startDate?.valueOf() ?? 0))
}
export async function getProjectsFeaturedTags(maxCount: number): Promise<string[]> {
const projects = await getAllProjects()
const tags = new Set<string>()
for (const project of projects) {
if (project.data.tags) {
for (const tag of project.data.tags) {
tags.add(tag)
}
}
if (tags.size >= maxCount) {
break
}
}
return Array.from(tags).slice(0, maxCount)
}

21
src/lib/utils.ts Normal file
View File

@@ -0,0 +1,21 @@
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export function formatDate(date: Date) {
return Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
}).format(date)
}
export function readingTime(html: string) {
const textOnly = html.replace(/<[^>]+>/g, '')
const wordCount = textOnly.split(/\s+/).length
const readingTimeMinutes = (wordCount / 200 + 1).toFixed()
return `${readingTimeMinutes} min read`
}