Files
template-blog/src/pages/Index.tsx
T
2026-07-23 22:56:15 +00:00

279 lines
15 KiB
TypeScript

import { useState } from "react";
import { toast } from "sonner";
import { Link } from "react-router-dom";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Separator } from "@/components/ui/separator";
import {
ArrowRight, Clock, BookOpen, TrendingUp, Mail, Tag
} from "lucide-react";
import { useSaasMutation } from "@/hooks/use-tygarun";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
const featuredArticle = {
title: "The Future of Web Development: What Comes After the JavaScript Era",
excerpt: "Exploring the emerging paradigms, languages, and frameworks that may reshape how we build for the web in the next decade.",
category: "Technology",
author: "Sarah Chen",
date: "Mar 15, 2025",
readTime: "8 min read",
gradient: "from-emerald-400 via-teal-500 to-emerald-700",
slug: "future-of-web-development",
};
const latestArticles = [
{ id: 1, title: "Understanding React Server Components in Production", excerpt: "A practical guide to adopting RSC in existing React applications without a full rewrite.", category: "Technology", author: "Sarah Chen", date: "Mar 12, 2025", gradient: "from-amber-200 to-orange-300", slug: "react-server-components" },
{ id: 2, title: "Building Design Systems That Scale", excerpt: "Lessons learned from building a design system used across 12 product teams.", category: "Design", author: "Marcus Rivera", date: "Mar 10, 2025", gradient: "from-emerald-200 to-teal-300", slug: "design-systems-at-scale" },
{ id: 3, title: "Growth Strategies for Early-Stage Startups", excerpt: "How three founders went from zero to product-market fit in under 18 months.", category: "Business", author: "Aisha Patel", date: "Mar 8, 2025", gradient: "from-stone-200 to-stone-400", slug: "startup-growth-strategies" },
{ id: 4, title: "TypeScript Best Practices for Large Codebases", excerpt: "Type-safe patterns and architectural decisions that pay off at scale.", category: "Technology", author: "James Liu", date: "Mar 6, 2025", gradient: "from-amber-100 to-amber-300", slug: "typescript-best-practices" },
{ id: 5, title: "The Remote Worker's Guide to Deep Focus", excerpt: "Strategies for maintaining productivity and avoiding burnout while working from home.", category: "Lifestyle", author: "Elena Torres", date: "Mar 4, 2025", gradient: "from-emerald-100 to-emerald-300", slug: "remote-work-productivity" },
{ id: 6, title: "Accessible UI Patterns Every Developer Should Know", excerpt: "Common accessibility mistakes and how to build inclusive interfaces by default.", category: "Design", author: "David Kim", date: "Mar 2, 2025", gradient: "from-stone-100 to-amber-200", slug: "accessible-ui-patterns" },
];
const categories = [
{ name: "Technology", count: 42 },
{ name: "Design", count: 28 },
{ name: "Business", count: 19 },
{ name: "Lifestyle", count: 15 },
];
const tags = [
"React", "TypeScript", "CSS", "Node.js", "Figma", "Tailwind",
"Next.js", "AI", "UX", "Startup", "Remote Work", "Productivity",
];
const categoryColors: Record<string, string> = {
Technology: "bg-emerald-100 text-emerald-700",
Design: "bg-amber-100 text-amber-700",
Business: "bg-stone-200 text-stone-700",
Lifestyle: "bg-teal-100 text-teal-700",
};
export default function IndexPage() {
const [email, setEmail] = useState("");
const [subscribed, setSubscribed] = useState(false);
const { mutate: subscribeNewsletter } = useSaasMutation("email", "/send/newsletter-confirm");
const handleSubscribe = async (e) => {
e.preventDefault();
if (!email) return;
try {
await subscribeNewsletter({ email, list: "newsletter" });
setSubscribed(true);
toast.success("Subscribed! Check your inbox.");
} catch {
toast.error("Something went wrong. Please try again.");
}
};
return (
<div className="min-h-screen bg-stone-50 text-stone-900 antialiased">
<Header />
{/* ── FEATURED HERO ── */}
<section className="py-24 pb-0">
<div className="mx-auto max-w-7xl px-6 pt-8">
<Link to={`/article/${featuredArticle.slug}`} className="group block animate-fade-in">
<div className="relative overflow-hidden rounded-3xl hover:shadow-2xl transition-all duration-500">
<div className={`aspect-[21/9] bg-gradient-to-br ${featuredArticle.gradient}`}>
<div className="absolute inset-0" style={{ backgroundImage: "radial-gradient(circle at 25% 25%, rgba(255,255,255,0.15) 1px, transparent 1px)", backgroundSize: "20px 20px" }} />
<div className="absolute top-1/4 left-1/4 w-64 h-64 rounded-full bg-white/10 blur-3xl" />
<div className="absolute bottom-1/3 right-1/4 w-48 h-48 rounded-full bg-white/10 blur-3xl" />
</div>
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/30 to-black/10" />
<Badge className={`absolute top-6 left-6 border-0 z-10 ${categoryColors[featuredArticle.category] || "bg-emerald-100 text-emerald-700"}`}>
{featuredArticle.category}
</Badge>
<div className="absolute bottom-0 left-0 right-0 p-8 sm:p-12">
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-bold text-white max-w-3xl leading-tight">
{featuredArticle.title}
</h1>
<p className="mt-4 text-lg text-white/80 max-w-2xl line-clamp-2">
{featuredArticle.excerpt}
</p>
<div className="mt-6 flex items-center gap-4">
<Avatar className="h-10 w-10 border-2 border-white/30">
<AvatarFallback className="bg-emerald-600 text-white text-sm font-semibold">
{featuredArticle.author.split(" ").map(n => n[0]).join("")}
</AvatarFallback>
</Avatar>
<div className="text-white">
<p className="text-sm font-medium">{featuredArticle.author}</p>
<p className="text-xs text-white/60">{featuredArticle.date} &middot; {featuredArticle.readTime}</p>
</div>
<Button className="ml-auto bg-white text-stone-900 hover:bg-white/90 active:scale-[0.98] rounded-full px-6 hidden sm:flex">
Read Article <ArrowRight className="ml-2 h-4 w-4" />
</Button>
</div>
</div>
</div>
</Link>
</div>
</section>
{/* ── LATEST ARTICLES + SIDEBAR ── */}
<section className="py-24">
<div className="mx-auto max-w-7xl px-6">
<div className="flex items-end justify-between mb-12 animate-fade-up">
<div>
<h2 className="text-3xl sm:text-4xl font-bold tracking-tight">Latest Articles</h2>
<p className="mt-2 text-stone-500">Fresh perspectives on technology, design, and culture</p>
</div>
<Link to="/category/technology" className="hidden sm:flex items-center text-sm font-medium text-emerald-600 hover:text-emerald-700">
View All <ArrowRight className="ml-1 h-4 w-4" />
</Link>
</div>
<div className="grid lg:grid-cols-4 gap-10">
{/* Articles Grid */}
<div className="lg:col-span-3">
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 stagger">
{latestArticles.map((article) => (
<Link to={`/article/${article.slug}`} key={article.id} className="group">
<Card className="overflow-hidden border-0 shadow-sm hover:shadow-xl hover:-translate-y-1 transition-all duration-300 bg-white">
<div className={`relative aspect-video bg-gradient-to-br ${article.gradient}`}>
<div className="absolute inset-0" style={{ backgroundImage: "radial-gradient(circle at 25% 25%, rgba(255,255,255,0.15) 1px, transparent 1px)", backgroundSize: "20px 20px" }} />
<div className="absolute top-1/3 left-1/4 w-24 h-24 rounded-full bg-white/10 blur-2xl" />
<Badge className={`absolute top-3 left-3 border-0 text-xs ${categoryColors[article.category] || "bg-emerald-100 text-emerald-700"}`}>
{article.category}
</Badge>
</div>
<CardContent className="p-5">
<h3 className="font-semibold text-stone-900 group-hover:text-emerald-600 transition-colors leading-snug line-clamp-2">
{article.title}
</h3>
<p className="mt-2 text-sm text-stone-500 line-clamp-2">{article.excerpt}</p>
<div className="mt-4 flex items-center text-xs text-stone-400">
<span>{article.date}</span>
<Separator orientation="vertical" className="mx-2 h-3" />
<span className="flex items-center gap-1"><Clock className="h-3 w-3" /> 5 min read</span>
</div>
<div className="mt-3 flex items-center justify-between">
<div className="flex items-center gap-2">
<div className="h-5 w-5 rounded-full bg-gradient-to-br from-emerald-400 to-teal-600 flex items-center justify-center">
<span className="text-[8px] text-white font-bold">{article.author.split(" ").map(n => n[0]).join("")}</span>
</div>
<span className="text-xs text-stone-500">{article.author}</span>
</div>
<span className="inline-flex items-center text-sm font-medium text-emerald-600 group-hover:text-emerald-700">
Read More <ArrowRight className="ml-1 h-3 w-3" />
</span>
</div>
</CardContent>
</Card>
</Link>
))}
</div>
<div className="mt-10 text-center">
<Button variant="outline" className="rounded-full border-stone-300 px-8 active:scale-[0.98]">
Load More Articles
</Button>
</div>
</div>
{/* Sidebar */}
<div className="lg:col-span-1 space-y-8">
{/* Categories */}
<Card className="border-0 shadow-sm bg-white">
<CardContent className="p-6">
<h3 className="font-semibold text-sm uppercase tracking-wider text-stone-500 mb-4 flex items-center gap-2">
<BookOpen className="h-4 w-4" /> Categories
</h3>
<ul className="space-y-3">
{categories.map((cat) => (
<li key={cat.name}>
<Link to={`/category/${cat.name.toLowerCase()}`} className="flex items-center justify-between text-sm text-stone-600 hover:text-emerald-600 transition-colors">
<span>{cat.name}</span>
<Badge variant="secondary" className="bg-stone-100 text-stone-500 text-xs">{cat.count}</Badge>
</Link>
</li>
))}
</ul>
</CardContent>
</Card>
{/* Tags Cloud */}
<Card className="border-0 shadow-sm bg-white">
<CardContent className="p-6">
<h3 className="font-semibold text-sm uppercase tracking-wider text-stone-500 mb-4 flex items-center gap-2">
<Tag className="h-4 w-4" /> Popular Tags
</h3>
<div className="flex flex-wrap gap-2 stagger">
{tags.map((tag) => (
<Badge key={tag} variant="secondary" className="bg-stone-100 text-stone-600 hover:bg-primary hover:text-primary-foreground transition-colors cursor-pointer text-xs px-3 py-1">
{tag}
</Badge>
))}
</div>
</CardContent>
</Card>
{/* Newsletter */}
<Card className="border-0 shadow-sm bg-gradient-to-br from-emerald-600 to-teal-700 text-white">
<CardContent className="p-6">
<h3 className="font-semibold mb-2 flex items-center gap-2">
<Mail className="h-4 w-4" /> Stay in the Loop
</h3>
<p className="text-sm text-emerald-100 mb-3">Get the best articles delivered to your inbox every week. No spam, ever.</p>
<p className="text-xs font-semibold text-white/90 mb-3">Join 12,000+ readers</p>
{subscribed ? (
<p className="text-sm text-emerald-100 font-medium py-2">Thanks for subscribing! Check your inbox.</p>
) : (
<form onSubmit={handleSubscribe} className="space-y-2">
<Input
type="email"
required
placeholder="your@email.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="bg-white/20 border-white/30 text-white placeholder:text-emerald-200 focus-visible:ring-white"
/>
<Button type="submit" className="w-full bg-white text-emerald-700 hover:bg-white/90 active:scale-[0.98] font-semibold">
Subscribe
</Button>
</form>
)}
<ul className="mt-3 space-y-1 text-[11px] text-emerald-200">
<li className="flex items-center gap-1.5">&bull; Weekly insights</li>
<li className="flex items-center gap-1.5">&bull; No spam</li>
<li className="flex items-center gap-1.5">&bull; Unsubscribe anytime</li>
</ul>
</CardContent>
</Card>
{/* Trending */}
<Card className="border-0 shadow-sm bg-white">
<CardContent className="p-6">
<h3 className="font-semibold text-sm uppercase tracking-wider text-stone-500 mb-4 flex items-center gap-2">
<TrendingUp className="h-4 w-4" /> Trending
</h3>
<ul className="space-y-4">
{latestArticles.slice(0, 3).map((article, i) => (
<li key={article.id}>
<Link to={`/article/${article.slug}`} className="flex gap-3 group">
<span className="text-2xl font-bold text-stone-200">0{i + 1}</span>
<div>
<p className="text-sm font-medium text-stone-800 group-hover:text-emerald-600 transition-colors line-clamp-2">{article.title}</p>
<p className="text-xs text-stone-400 mt-1">{article.date}</p>
</div>
</Link>
</li>
))}
</ul>
</CardContent>
</Card>
</div>
</div>
</div>
</section>
<Footer />
</div>
);
}