279 lines
15 KiB
TypeScript
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} · {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">• Weekly insights</li>
|
|
<li className="flex items-center gap-1.5">• No spam</li>
|
|
<li className="flex items-center gap-1.5">• 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>
|
|
);
|
|
}
|