Files
chorus-ping-blog/components/PostCard.tsx
anthonyrawlins 5e53840371 Implement automated scheduled publishing for blog
- Enhanced blog library to read from both posts/ and scheduled/ directories
- Added publishDate filtering with real-time checking (no cron jobs needed)
- Support for draft posts and recursive directory scanning
- Posts automatically appear when publishDate is reached
- Containerized solution that works without external scheduling
- Added publishDate field to blog types and updated existing scheduled post

Tested and verified:
 Past-dated posts appear automatically
 Future-dated posts remain hidden until publish time
 Draft posts are excluded regardless of date
 Maintains existing functionality for regular posts

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-28 02:09:38 +10:00

87 lines
3.4 KiB
TypeScript

import Link from 'next/link'
import { BlogPost } from '@/types/blog'
interface PostCardProps {
post: BlogPost
featured?: boolean
}
export default function PostCard({ post, featured = false }: PostCardProps) {
const formattedDate = new Date(post.date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
})
return (
<article className={`group ${
featured
? 'bg-gradient-to-b from-sand-200 to-sand-100 dark:from-mulberry-900 to-mulberry-700 border border-carbon-200 dark:border-carbon-800 p-chorus-lg hover:border-carbon-400 dark:hover:border-mulberry-700 transition-all duration-300'
: 'bg-gradient-to-b from-sand-100 to-sand-50 dark:from-mulberry-800 to-mulberry-800 border border-carbon-200/50 dark:border-carbon-800/50 p-chorus-lg hover:bg-carbon-50 dark:hover:bg-carbon-900 hover:border-carbon-300 dark:hover:border-carbon-700 transition-all duration-300'
}`}>
<Link href={`/posts/${post.slug}`} className="block">
<div className="mb-chorus-md">
<div className="flex items-center justify-between mb-chorus-sm">
<div className="blog-meta">
<time dateTime={post.date} className="text-carbon-600 dark:text-carbon-500">
{formattedDate}
</time>
<span className="text-carbon-500 dark:text-carbon-600"></span>
<span className="text-carbon-600 dark:text-carbon-500">
{post.readingTime} min read
</span>
</div>
{post.featured && (
<span className="blog-tag bg-mulberry-700 text-mulberry-200">
Featured
</span>
)}
</div>
<h2 className={`${
featured ? 'text-h3' : 'text-h4'
} font-logo text-carbon-950 dark:text-carbon-100 group-hover:text-carbon-700 dark:group-hover:text-mulberry-200 transition-colors mb-chorus-lg`}>
{post.title}
</h2>
<p className="text-carbon-700 dark:text-carbon-300 leading-relaxed mb-4 line-clamp-3">
{post.description}
</p>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<div className="w-8 h-8 bg-gradient-to-br from-mulberry-400 to-ocean-500 rounded-full flex items-center justify-center">
<span className="text-carbon-950 font-semibold text-xs">
{post.author?.name?.charAt(0) || 'C'}
</span>
</div>
<div>
<p className="text-carbon-950 dark:text-carbon-200 font-medium text-sm">
{post.author?.name || 'CHORUS Team'}
</p>
{post.author?.role && (
<p className="text-carbon-600 dark:text-carbon-500 text-xs">
{post.author.role}
</p>
)}
</div>
</div>
<div className="flex flex-wrap gap-2">
{post.tags?.slice(0, 3).map((tag) => (
<span key={tag} className="blog-tag">
{tag}
</span>
)) || []}
{(post.tags?.length || 0) > 3 && (
<span className="text-carbon-600 dark:text-carbon-500 text-xs">
+{(post.tags?.length || 0) - 3} more
</span>
)}
</div>
</div>
</Link>
</article>
)
}