webstack-author/src/components/SpotlightSection.tsx
2026-07-26 22:12:41 +02:00

218 lines
8.6 KiB
TypeScript

import React, { useState } from "react";
import Markdown from "react-markdown";
import { Book } from "../types";
import { Sparkles, Calendar, ShoppingBag, Music, ExternalLink, Tag } from "lucide-react";
interface SpotlightSectionProps {
book: Book;
title?: string;
subtitle?: string;
accentColor: string;
secondaryColor: string;
cardBgColor: string;
}
export default function SpotlightSection({
book,
title,
subtitle,
accentColor,
secondaryColor,
cardBgColor,
}: SpotlightSectionProps) {
const [showSpotify, setShowSpotify] = useState(false);
// Helper to check if a release date is in the future
const isFutureRelease = (): boolean => {
if (!book.publishedYear) return false;
const str = String(book.publishedYear).trim();
// Check if it's a 4-digit year like "2027"
if (/^\d{4}$/.test(str)) {
return parseInt(str) > new Date().getFullYear();
}
// Check if it's a German date e.g. "15.10.2026" or "15.10.26"
const germanDateMatch = str.match(/^(\d{1,2})\.(\d{1,2})\.(\d{2,4})$/);
if (germanDateMatch) {
let [, day, month, year] = germanDateMatch;
if (year.length === 2) year = "20" + year;
const parsedDate = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
return parsedDate > new Date();
}
// Standard JS Date parse
const parsed = Date.parse(str);
if (!isNaN(parsed)) {
return parsed > Date.now();
}
return false;
};
const isFuture = isFutureRelease();
const badgeText = book.spotlightBadge || (isFuture ? `Erscheint am ${book.publishedYear}` : "Neuerscheinung");
return (
<section id="spotlight" className="space-y-6 my-12">
<div className="flex items-center gap-3 border-b border-slate-800 pb-3">
<div
className="p-2 rounded-xl flex items-center justify-center shadow-md"
style={{ backgroundColor: `${accentColor}20`, color: accentColor }}
>
<Sparkles className="w-6 h-6 animate-pulse" />
</div>
<div>
<h2 className="text-2xl md:text-3xl font-bold tracking-tight text-white flex items-center gap-2">
<span>{title || "Spotlight & Neuerscheinung"}</span>
</h2>
<p className="text-xs text-slate-400">{subtitle || "Besonders hervorgehobenes Werk im Fokus"}</p>
</div>
</div>
<div
className="relative overflow-hidden border rounded-2xl p-6 md:p-8 shadow-2xl transition-all"
style={{
backgroundColor: cardBgColor,
borderColor: `${accentColor}40`,
boxShadow: `0 20px 50px -10px ${accentColor}15`
}}
>
{/* Subtle Ambient Background Gradient */}
<div
className="absolute -right-20 -bottom-20 w-80 h-80 rounded-full blur-3xl pointer-events-none opacity-20"
style={{ backgroundColor: accentColor }}
/>
<div className="grid grid-cols-1 md:grid-cols-12 gap-8 items-center relative z-10">
{/* Cover image column */}
<div className="md:col-span-4 flex flex-col items-center space-y-4">
<div className="relative group w-full max-w-[260px]">
<div
className="absolute -inset-1 rounded-2xl blur-lg opacity-40 group-hover:opacity-75 transition duration-500"
style={{ backgroundColor: accentColor }}
/>
<div className="relative aspect-[2/3] w-full rounded-xl overflow-hidden border border-slate-700/60 shadow-2xl bg-slate-900">
<img
src={book.coverUrl}
alt={book.title}
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
referrerPolicy="no-referrer"
/>
</div>
</div>
{book.buyLink && (
<a
href={book.buyLink}
target="_blank"
rel="noopener noreferrer"
className="w-full max-w-[260px] py-3 px-4 rounded-xl font-bold text-xs uppercase tracking-wider transition-all duration-300 flex items-center justify-center gap-2 shadow-lg cursor-pointer hover:scale-[1.02]"
style={{ backgroundColor: accentColor, color: "#000" }}
>
<ShoppingBag className="w-4 h-4" />
<span>{isFuture ? "Jetzt vorbestellen" : "Jetzt im Handel"}</span>
<ExternalLink className="w-3.5 h-3.5 ml-auto opacity-70" />
</a>
)}
</div>
{/* Details column */}
<div className="md:col-span-8 space-y-5">
<div className="flex flex-wrap items-center gap-2">
<span
className="px-3 py-1 rounded-full text-xs font-mono font-bold tracking-wide uppercase border flex items-center gap-1.5 shadow-sm"
style={{
backgroundColor: isFuture ? `${secondaryColor}25` : `${accentColor}25`,
borderColor: isFuture ? secondaryColor : accentColor,
color: isFuture ? "#fff" : accentColor
}}
>
<Sparkles className="w-3.5 h-3.5" />
<span>{badgeText}</span>
</span>
{book.publishedYear && (
<span className="px-3 py-1 bg-slate-800/80 border border-slate-700 text-slate-300 rounded-full text-xs font-mono flex items-center gap-1.5">
<Calendar className="w-3.5 h-3.5 text-slate-400" />
<span>{isFuture ? "Erscheinungsdatum:" : "Veröffentlichung:"} {book.publishedYear}</span>
</span>
)}
</div>
<div>
<h3 className="text-2xl md:text-4xl font-extrabold text-white tracking-tight">
{book.title}
</h3>
{book.subtitle && (
<p className="text-base md:text-lg font-medium mt-1 italic" style={{ color: secondaryColor }}>
{book.subtitle}
</p>
)}
</div>
{/* Genres */}
{book.genres && book.genres.length > 0 && (
<div className="flex flex-wrap items-center gap-1.5 pt-1">
<Tag className="w-3.5 h-3.5 text-slate-500 mr-1" />
{book.genres.map((g, idx) => (
<span
key={idx}
className="px-2.5 py-0.5 rounded-md text-[11px] font-mono border bg-slate-900/80 text-slate-300 border-slate-800"
>
{g}
</span>
))}
</div>
)}
{/* Synopsis */}
<div className="prose prose-invert max-w-none text-slate-300 text-sm leading-relaxed space-y-3 pt-2 border-t border-slate-800/60">
<Markdown
components={{
a: ({ node, ...props }) => (
<a
target="_blank"
rel="noopener noreferrer"
className="underline font-medium hover:opacity-80"
style={{ color: accentColor }}
{...props}
/>
)
}}
>
{book.description}
</Markdown>
</div>
{/* Spotify Player Toggle if present */}
{book.spotifyPlaylistId && (
<div className="pt-2">
<button
onClick={() => setShowSpotify(!showSpotify)}
className="px-4 py-2 bg-slate-900 hover:bg-slate-850 border border-slate-700 rounded-xl text-xs font-mono text-slate-300 flex items-center gap-2 transition-all cursor-pointer"
>
<Music className="w-4 h-4 text-emerald-400" />
<span>{showSpotify ? "Playlist einklappen" : "Buch-Soundtrack (Spotify) anhören"}</span>
</button>
{showSpotify && (
<div className="mt-3 rounded-xl overflow-hidden border border-slate-800 shadow-xl bg-slate-950">
<iframe
src={`https://open.spotify.com/embed/playlist/${book.spotifyPlaylistId}?utm_source=generator&theme=0`}
width="100%"
height="152"
frameBorder="0"
allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture"
loading="lazy"
/>
</div>
)}
</div>
)}
</div>
</div>
</div>
</section>
);
}