import { useStore } from '@nanostores/react' import { $posts } from '../stores/posts' import { $analytics } from '../stores/analytics' import { Button } from '../components/ui' import { BreakdownList, EmptyState, HomePageSkeleton, PageHeader } from '../components/shared' import { Icons, getReferrerIcon } from '../components/shared/Icons' function formatChange(change: number): { text: string; positive: boolean } { const sign = change >= 0 ? '+' : '' return { text: `${sign}${change.toFixed(1)}%`, positive: change >= 0, } } function formatRelativeTime(dateStr: string) { const date = new Date(dateStr) const now = new Date() const diffMs = now.getTime() - date.getTime() const diffMins = Math.floor(diffMs / 60000) const diffHours = Math.floor(diffMs / 3600000) const diffDays = Math.floor(diffMs / 86400000) if (diffMins < 1) return 'Just now' if (diffMins < 60) return `${diffMins}m ago` if (diffHours < 24) return `${diffHours}h ago` if (diffDays < 7) return `${diffDays}d ago` return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) } function formatDate(dateStr: string) { return new Date(dateStr).toLocaleDateString('en-US', { month: 'short', day: 'numeric', }) } function formatViews(views: number) { if (views >= 1000) return `${(views / 1000).toFixed(1)}k` return views.toString() } export default function HomePage() { const { data: posts, error: postsError } = useStore($posts) const { data: analytics } = useStore($analytics) if (!posts) return if (postsError) return const drafts = posts .filter(p => p.draft) .sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()) .slice(0, 3) const published = posts .filter(p => !p.draft) .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) .slice(0, 5) const publishedCount = posts.filter(p => !p.draft).length const draftCount = posts.filter(p => p.draft).length const getPostViews = (slug: string): number => { if (!analytics?.top_pages) return 0 const page = analytics.top_pages.find(p => p.path === `/posts/${slug}`) return page?.views || 0 } const change = analytics ? formatChange(analytics.views_change) : null if (posts.length === 0) { return (
Write Your First Post} />
) } return (
{/* Panel container - uses negative margins for full-bleed borders */}
{/* Stats row */}
{/* Vertical dividers at column boundaries */}
Views
{analytics?.total_views.toLocaleString() || '0'}
{change && (
{change.text} vs last period
)}
Visitors
{analytics?.unique_visitors.toLocaleString() || '0'}
Posts
{publishedCount}
{draftCount > 0 && (
{draftCount} draft{draftCount > 1 ? 's' : ''}
)}
{/* Full-bleed horizontal divider */}
{/* Content sections with vertical divider */}
{/* Vertical divider at exact center */}
{/* Left column: Posts */}
{/* Drafts */} {drafts.length > 0 && (
Continue Writing
)} {/* Recent posts */} {published.length > 0 && (
Recent Posts
{published.map((post) => { const views = getPostViews(post.slug) return (
{post.title}
{views > 0 && ( {formatViews(views)} )} {formatDate(post.date)}
) })}
)}
{/* Right column: Referrers */}
Top Referrers
{analytics && analytics.top_referrers.length > 0 ? ( { const label = r.referrer || 'Direct' return { label, value: r.views, percentage: (r.views / analytics.total_views) * 100, Icon: getReferrerIcon(label), } })} limit={5} /> ) : (
No referrer data yet
)}
) }