144 lines
4.0 KiB
TypeScript
144 lines
4.0 KiB
TypeScript
'use client';
|
|
|
|
import React from 'react';
|
|
import { BaseCard, BaseCardProps, CardSize, CardLayout } from './BaseCard';
|
|
import { Badge, BadgeGroup } from '@/components/ui';
|
|
import { formatDate } from '@/lib/utils';
|
|
import { Post } from '@/lib/data';
|
|
|
|
// BlogCard specific props
|
|
export interface BlogCardProps extends Omit<BaseCardProps, 'title' | 'description' | 'image' | 'footer'> {
|
|
/** Post data from WordPress */
|
|
post: Post;
|
|
/** Display date */
|
|
showDate?: boolean;
|
|
/** Display categories */
|
|
showCategories?: boolean;
|
|
/** Read more text */
|
|
readMoreText?: string;
|
|
/** Excerpt length */
|
|
excerptLength?: number;
|
|
/** Locale for formatting */
|
|
locale?: string;
|
|
}
|
|
|
|
// Helper to extract categories from post (mock implementation since Post doesn't have categories)
|
|
const getPostCategories = (post: Post): string[] => {
|
|
// In a real implementation, this would come from the post data
|
|
// For now, return a mock category based on post ID
|
|
return post.id % 2 === 0 ? ['News', 'Updates'] : ['Blog', 'Tips'];
|
|
};
|
|
|
|
// Helper to get featured image URL
|
|
const getFeaturedImageUrl = (post: Post): string | undefined => {
|
|
// In a real implementation, this would use getMediaById
|
|
// For now, return a placeholder or the featured image if available
|
|
if (post.featuredImage) {
|
|
// This would be resolved through the data layer
|
|
return `/media/${post.featuredImage}.jpg`;
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
// Helper to truncate text
|
|
const truncateText = (text: string, length: number): string => {
|
|
if (text.length <= length) return text;
|
|
return text.slice(0, length - 3) + '...';
|
|
};
|
|
|
|
export const BlogCard: React.FC<BlogCardProps> = ({
|
|
post,
|
|
size = 'md',
|
|
layout = 'vertical',
|
|
showDate = true,
|
|
showCategories = true,
|
|
readMoreText = 'Read More',
|
|
excerptLength = 150,
|
|
locale = 'de',
|
|
className = '',
|
|
...props
|
|
}) => {
|
|
// Get post data
|
|
const title = post.title;
|
|
const excerpt = post.excerptHtml ? post.excerptHtml.replace(/<[^>]*>/g, '') : '';
|
|
const truncatedExcerpt = truncateText(excerpt, excerptLength);
|
|
const featuredImageUrl = getFeaturedImageUrl(post);
|
|
const categories = showCategories ? getPostCategories(post) : [];
|
|
const date = showDate ? formatDate(post.datePublished, locale === 'de' ? 'de-DE' : 'en-US') : '';
|
|
|
|
// Build badge component for categories
|
|
const badge = showCategories && categories.length > 0 ? (
|
|
<BadgeGroup gap="xs">
|
|
{categories.map((category, index) => (
|
|
<Badge
|
|
key={index}
|
|
variant="neutral"
|
|
size={size === 'sm' ? 'sm' : 'md'}
|
|
>
|
|
{category}
|
|
</Badge>
|
|
))}
|
|
</BadgeGroup>
|
|
) : null;
|
|
|
|
// Build header with date
|
|
const header = date ? (
|
|
<span className="text-xs text-gray-500 font-medium">
|
|
{date}
|
|
</span>
|
|
) : null;
|
|
|
|
// Build footer with read more link
|
|
const footer = (
|
|
<span className="text-sm font-medium text-primary hover:text-primary-dark transition-colors">
|
|
{readMoreText} →
|
|
</span>
|
|
);
|
|
|
|
// Build description
|
|
const description = truncatedExcerpt ? (
|
|
<div
|
|
className="text-gray-600"
|
|
dangerouslySetInnerHTML={{ __html: truncatedExcerpt }}
|
|
/>
|
|
) : null;
|
|
|
|
return (
|
|
<BaseCard
|
|
title={title}
|
|
description={description}
|
|
image={featuredImageUrl}
|
|
imageAlt={title}
|
|
size={size}
|
|
layout={layout}
|
|
href={post.path}
|
|
badge={badge}
|
|
header={header}
|
|
footer={footer}
|
|
hoverable={true}
|
|
variant="elevated"
|
|
className={className}
|
|
{...props}
|
|
/>
|
|
);
|
|
};
|
|
|
|
// BlogCard variations
|
|
export const BlogCardVertical: React.FC<BlogCardProps> = (props) => (
|
|
<BlogCard {...props} layout="vertical" />
|
|
);
|
|
|
|
export const BlogCardHorizontal: React.FC<BlogCardProps> = (props) => (
|
|
<BlogCard {...props} layout="horizontal" />
|
|
);
|
|
|
|
export const BlogCardSmall: React.FC<BlogCardProps> = (props) => (
|
|
<BlogCard {...props} size="sm" />
|
|
);
|
|
|
|
export const BlogCardLarge: React.FC<BlogCardProps> = (props) => (
|
|
<BlogCard {...props} size="lg" />
|
|
);
|
|
|
|
// Export types
|
|
export type { CardSize, CardLayout }; |