feat: Add book ratings and reviews managed via Directus CMS

Adds a new "Read Books" section below "Currently Reading" in the About
page. Book reviews with star ratings and comments are fetched from a
Directus CMS collection (book_reviews) and displayed with the existing
liquid design system. Includes i18n support (EN/DE), show more/less
toggle, and graceful fallback when the CMS collection does not exist yet.

https://claude.ai/code/session_017E8W9CcHFM5WQVHw74JP34
This commit is contained in:
Claude
2026-02-15 12:53:19 +00:00
committed by denshooter
parent 07741761cc
commit 032568562c
6 changed files with 343 additions and 0 deletions

View File

@@ -422,6 +422,71 @@ export async function getHobbies(locale: string): Promise<Hobby[] | null> {
}
}
// Book Review Types
export interface BookReview {
id: string;
hardcover_id?: string;
book_title: string;
book_author: string;
book_image?: string;
rating: number; // 1-5
review?: string; // Translated review text
finished_at?: string;
}
/**
* Get Book Reviews from Directus with translations
*/
export async function getBookReviews(locale: string): Promise<BookReview[] | null> {
const directusLocale = toDirectusLocale(locale);
const query = `
query {
book_reviews(
filter: { status: { _eq: "published" } }
sort: ["-finished_at", "-date_created"]
) {
id
hardcover_id
book_title
book_author
book_image
rating
finished_at
translations(filter: { languages_code: { code: { _eq: "${directusLocale}" } } }) {
review
}
}
}
`;
try {
const result = await directusRequest(
'',
{ body: { query } }
);
const reviews = (result as any)?.book_reviews;
if (!reviews || reviews.length === 0) {
return null;
}
return reviews.map((item: any) => ({
id: item.id,
hardcover_id: item.hardcover_id || undefined,
book_title: item.book_title,
book_author: item.book_author,
book_image: item.book_image || undefined,
rating: typeof item.rating === 'number' ? item.rating : parseInt(item.rating) || 0,
review: item.translations?.[0]?.review || undefined,
finished_at: item.finished_at || undefined,
}));
} catch (error) {
console.error(`Failed to fetch book reviews (${locale}):`, error);
return null;
}
}
// Projects Types
export interface Project {
id: string;