migration wip
This commit is contained in:
144
components/cards/BlogCard.tsx
Normal file
144
components/cards/BlogCard.tsx
Normal file
@@ -0,0 +1,144 @@
|
||||
'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 };
|
||||
Reference in New Issue
Block a user