Seed: editorial template (15 files)

This commit is contained in:
2026-07-23 22:14:21 +00:00
parent a7c3cc4129
commit f09b602f26
+162
View File
@@ -0,0 +1,162 @@
import { useState } from "react";
import { Link } from "react-router-dom";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Clock, ArrowRight, Mail, Quote } from "lucide-react";
import { toast } from "sonner";
import { useSaasMutation } from "@/hooks/use-tygarun";
const featured = {
category: "Culture",
title: "The Quiet Revolution Reshaping How We Live",
excerpt: "A generation of designers, architects, and thinkers is redefining what it means to live well in the modern city.",
author: "Elena Vasquez",
date: "April 8, 2026",
};
const editorPicks = [
{ category: "Style", title: "The Return of Slow Fashion", author: "Mara Chen", readTime: "6 min", img: "https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=600&h=400&fit=crop" },
{ category: "Technology", title: "AI and the Future of Creativity", author: "Soren Blake", readTime: "8 min", img: "https://images.unsplash.com/photo-1451187580459-43490279c0fa?w=600&h=400&fit=crop" },
{ category: "Travel", title: "Hidden Coastlines of the Adriatic", author: "Lila Osman", readTime: "5 min", img: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=600&h=400&fit=crop" },
];
const latestStories = [
{ category: "Food", title: "Why Fermentation Is the New Fine Dining", excerpt: "Chefs worldwide are turning to ancient techniques for modern flavor.", author: "Nadia Park", date: "April 6, 2026", img: "https://images.unsplash.com/photo-1504674900247-0877df9cc836?w=600&h=400&fit=crop" },
{ category: "Opinion", title: "The Case for Digital Minimalism", excerpt: "Why less screen time might be the key to deeper thinking.", author: "James Whitfield", date: "April 5, 2026", img: "https://images.unsplash.com/photo-1504384308090-c894fdcc538d?w=600&h=400&fit=crop" },
{ category: "Culture", title: "Inside the New Museum of Sound", excerpt: "An immersive space dedicated to the art of listening.", author: "Amara Diallo", date: "April 4, 2026", img: "https://images.unsplash.com/photo-1493225457124-a3eb161ffa5f?w=600&h=400&fit=crop" },
{ category: "Travel", title: "A Week on the Silk Road by Train", excerpt: "Crossing borders and centuries on one of the worlds great rail journeys.", author: "Henrik Solis", date: "April 3, 2026", img: "https://images.unsplash.com/photo-1476514525535-07fb3b4ae5f1?w=600&h=400&fit=crop" },
{ category: "Style", title: "The Architects of Modern Elegance", excerpt: "How a new wave of designers is redefining luxury.", author: "Cleo Marsh", date: "April 2, 2026", img: "https://images.unsplash.com/photo-1483985988355-763728e1935b?w=600&h=400&fit=crop" },
{ category: "Technology", title: "When Algorithms Write the News", excerpt: "The ethical questions we still have not answered about AI journalism.", author: "Idris Kemp", date: "April 1, 2026", img: "https://images.unsplash.com/photo-1518770660439-4636190af475?w=600&h=400&fit=crop" },
];
const categories = ["Culture", "Style", "Technology", "Travel", "Food", "Opinion"];
export default function Index() {
const [email, setEmail] = useState("");
const { mutate: subscribeNewsletter, loading: subscribing } = useSaasMutation("email", "/send/newsletter-confirm");
const handleSubscribe = async () => {
if (!email) return;
try { await subscribeNewsletter({ email }); setEmail(""); toast.success("Welcome aboard!"); }
catch { toast.error("Could not subscribe. Please try again."); }
};
return (
<div className="min-h-screen bg-stone-50 dark:bg-stone-950">
<Header />
<main className="pt-24">
{/* Featured Story */}
<section className="mx-auto max-w-6xl px-6 mb-16">
<Link to="/article" className="group block relative overflow-hidden rounded-2xl">
<div className="aspect-[16/7] bg-gradient-to-br from-stone-300 to-stone-500 dark:from-stone-700 dark:to-stone-900">
<img src="https://images.unsplash.com/photo-1432958576632-8a39f6b97dc7?w=1200&h=600&fit=crop" alt="Featured" className="w-full h-full object-cover opacity-80 group-hover:scale-105 transition-transform duration-700" />
</div>
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/30 to-transparent" />
<div className="absolute bottom-0 left-0 right-0 p-8 md:p-12">
<Badge className="bg-white/20 text-white border-0 backdrop-blur-sm mb-3">{featured.category}</Badge>
<h1 className="font-serif text-3xl md:text-5xl font-bold text-white leading-tight max-w-3xl">{featured.title}</h1>
<p className="mt-3 text-stone-200 text-base md:text-lg max-w-2xl leading-relaxed">{featured.excerpt}</p>
<div className="mt-4 flex items-center gap-3 text-stone-300 text-sm">
<span className="font-medium text-white">{featured.author}</span>
<span>&middot;</span>
<span>{featured.date}</span>
</div>
</div>
</Link>
</section>
{/* Editors Picks */}
<section className="mx-auto max-w-6xl px-6 mb-16">
<div className="flex items-center justify-between mb-8">
<h2 className="font-serif text-2xl font-bold text-stone-800 dark:text-stone-100 animate-fade-up">Editor's Picks</h2>
<Link to="/category" className="text-sm text-stone-500 hover:text-stone-800 dark:hover:text-stone-200 flex items-center gap-1 transition-colors">View all <ArrowRight className="h-3.5 w-3.5" /></Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 stagger">
{editorPicks.map((p, i) => (
<Link key={i} to="/article" className="group">
<Card className="overflow-hidden border-stone-200 dark:border-stone-800 bg-white dark:bg-stone-900 hover:-translate-y-1 hover:shadow-lg transition-all duration-300">
<div className="aspect-[3/2] overflow-hidden">
<img src={p.img} alt={p.title} className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500" />
</div>
<CardContent className="p-5">
<p className="text-xs uppercase tracking-[0.15em] text-stone-400 dark:text-stone-500 mb-2">{p.category}</p>
<h3 className="font-serif text-lg font-semibold text-stone-800 dark:text-stone-100 leading-snug group-hover:text-stone-600 dark:group-hover:text-stone-300 transition-colors">{p.title}</h3>
<div className="mt-3 flex items-center justify-between text-xs text-stone-400 dark:text-stone-500">
<span>{p.author}</span>
<span className="flex items-center gap-1"><Clock className="h-3 w-3" /> {p.readTime}</span>
</div>
</CardContent>
</Card>
</Link>
))}
</div>
</section>
{/* Latest Stories */}
<section className="mx-auto max-w-6xl px-6 mb-16">
<h2 className="font-serif text-2xl font-bold text-stone-800 dark:text-stone-100 mb-8 animate-fade-up">Latest Stories</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 stagger">
{latestStories.map((s, i) => (
<Link key={i} to="/article" className="group flex gap-5">
<div className="w-32 h-32 md:w-40 md:h-40 rounded-xl overflow-hidden flex-shrink-0">
<img src={s.img} alt={s.title} className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500" />
</div>
<div className="flex flex-col justify-center">
<Badge variant="outline" className="w-fit text-[10px] uppercase tracking-[0.15em] border-stone-300 dark:border-stone-700 text-stone-500 dark:text-stone-400 mb-2">{s.category}</Badge>
<h3 className="font-serif text-base md:text-lg font-semibold text-stone-800 dark:text-stone-100 leading-snug group-hover:text-stone-600 dark:group-hover:text-stone-300 transition-colors">{s.title}</h3>
<p className="mt-1 text-sm text-stone-500 dark:text-stone-400 line-clamp-2 leading-relaxed">{s.excerpt}</p>
<div className="mt-2 flex items-center gap-2 text-xs text-stone-400 dark:text-stone-500">
<Avatar className="h-5 w-5"><AvatarFallback className="text-[8px] bg-stone-200 dark:bg-stone-700 text-stone-600 dark:text-stone-300">{s.author.split(" ").map(w => w[0]).join("")}</AvatarFallback></Avatar>
<span>{s.author}</span>
<span>&middot;</span>
<span>{s.date}</span>
</div>
</div>
</Link>
))}
</div>
</section>
{/* Categories */}
<section className="mx-auto max-w-6xl px-6 mb-16">
<h2 className="font-serif text-2xl font-bold text-stone-800 dark:text-stone-100 mb-6 animate-fade-up">Explore</h2>
<div className="flex flex-wrap gap-3 stagger">
{categories.map((c) => (
<Link key={c} to="/category">
<Button variant="outline" className="rounded-full border-stone-300 dark:border-stone-700 text-stone-600 dark:text-stone-300 hover:bg-stone-800 hover:text-white dark:hover:bg-stone-200 dark:hover:text-stone-900 transition-all">{c}</Button>
</Link>
))}
</div>
</section>
{/* Quote of the Day */}
<section className="mx-auto max-w-6xl px-6 mb-16">
<div className="bg-stone-100 dark:bg-stone-900 rounded-2xl p-10 md:p-14 text-center relative">
<Quote className="h-8 w-8 text-stone-300 dark:text-stone-700 mx-auto mb-4" />
<blockquote className="font-serif text-xl md:text-2xl italic text-stone-700 dark:text-stone-300 leading-relaxed max-w-2xl mx-auto animate-fade-up">The task of the modern writer is not to describe the world but to reveal it.</blockquote>
<p className="mt-6 text-sm text-stone-500 dark:text-stone-400">&mdash; James Baldwin</p>
</div>
</section>
{/* Newsletter */}
<section className="mx-auto max-w-6xl px-6 mb-20">
<div className="bg-stone-800 dark:bg-stone-100 rounded-2xl p-10 md:p-14 text-center">
<Mail className="h-8 w-8 text-stone-400 dark:text-stone-500 mx-auto mb-4" />
<h2 className="font-serif text-2xl md:text-3xl font-bold text-white dark:text-stone-900 animate-fade-up">Stay Informed</h2>
<p className="mt-2 text-stone-300 dark:text-stone-600 text-sm">Join 12,000+ readers who get our weekly digest.</p>
<div className="mt-6 flex flex-col sm:flex-row gap-3 max-w-md mx-auto">
<Input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="your@email.com" className="bg-stone-700 dark:bg-stone-200 border-stone-600 dark:border-stone-300 text-white dark:text-stone-900 placeholder:text-stone-400 dark:placeholder:text-stone-500" />
<Button disabled={subscribing} className="bg-white text-stone-900 hover:bg-stone-200 dark:bg-stone-800 dark:text-white dark:hover:bg-stone-700" onClick={handleSubscribe}>Subscribe</Button>
</div>
</div>
</section>
</main>
<Footer />
</div>
);
}