mirror of
https://github.com/complexcaresolutions/frontend.blogwoman.de.git
synced 2026-03-17 15:04:01 +00:00
278 lines
7.9 KiB
TypeScript
278 lines
7.9 KiB
TypeScript
import Image from 'next/image'
|
|
import Link from 'next/link'
|
|
import { cn, getImageUrl } from '@/lib/utils'
|
|
import { SeriesPill } from '@/components/ui'
|
|
import { getSeries } from '@/lib/api'
|
|
import { RichTextRenderer } from './RichTextRenderer'
|
|
import type { SeriesBlock as SeriesBlockType, Series } from '@/lib/types'
|
|
|
|
type SeriesBlockProps = Omit<SeriesBlockType, 'blockType'>
|
|
|
|
export async function SeriesBlock({
|
|
title,
|
|
subtitle,
|
|
displayMode,
|
|
selectedSeries,
|
|
layout = 'grid',
|
|
showDescription = true,
|
|
}: SeriesBlockProps) {
|
|
// Fetch series if not using selected mode
|
|
let items: Series[] = []
|
|
|
|
if (displayMode === 'selected' && selectedSeries) {
|
|
items = selectedSeries
|
|
} else {
|
|
const seriesData = await getSeries()
|
|
items = seriesData.docs
|
|
}
|
|
|
|
if (!items || items.length === 0) return null
|
|
|
|
return (
|
|
<section className="py-16 md:py-20">
|
|
<div className="container">
|
|
{/* Section Header */}
|
|
{(title || subtitle) && (
|
|
<div className="text-center max-w-2xl mx-auto mb-12">
|
|
{title && <h2 className="mb-4">{title}</h2>}
|
|
{subtitle && (
|
|
<p className="text-lg text-espresso/80">{subtitle}</p>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* Series */}
|
|
{layout === 'featured' ? (
|
|
<FeaturedLayout items={items} showDescription={showDescription} />
|
|
) : layout === 'list' ? (
|
|
<ListLayout items={items} showDescription={showDescription} />
|
|
) : (
|
|
<GridLayout items={items} showDescription={showDescription} />
|
|
)}
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
function GridLayout({
|
|
items,
|
|
showDescription,
|
|
}: {
|
|
items: Series[]
|
|
showDescription?: boolean
|
|
}) {
|
|
return (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{items.map((series) => (
|
|
<SeriesCard
|
|
key={series.id}
|
|
series={series}
|
|
showDescription={showDescription}
|
|
/>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function ListLayout({
|
|
items,
|
|
showDescription,
|
|
}: {
|
|
items: Series[]
|
|
showDescription?: boolean
|
|
}) {
|
|
return (
|
|
<div className="max-w-4xl mx-auto space-y-6">
|
|
{items.map((series) => (
|
|
<Link
|
|
key={series.id}
|
|
href={`/serien/${series.slug}`}
|
|
className="group block"
|
|
>
|
|
<article className="flex gap-6 bg-soft-white border border-warm-gray rounded-xl p-6 transition-all duration-300 hover:shadow-lg">
|
|
{/* Logo/Image */}
|
|
<div className="relative w-24 h-24 flex-shrink-0 rounded-lg overflow-hidden bg-ivory flex items-center justify-center">
|
|
{series.logo ? (
|
|
<Image
|
|
src={series.logo.url}
|
|
alt={series.title}
|
|
fill
|
|
className="object-contain p-2"
|
|
/>
|
|
) : series.coverImage ? (
|
|
<Image
|
|
src={series.coverImage.url}
|
|
alt={series.title}
|
|
fill
|
|
className="object-cover"
|
|
/>
|
|
) : (
|
|
<SeriesPill series={series.slug} size="lg">
|
|
{series.title.slice(0, 2).toUpperCase()}
|
|
</SeriesPill>
|
|
)}
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="flex-1 min-w-0">
|
|
<h3 className="text-xl font-semibold mb-2 group-hover:text-brass transition-colors">
|
|
{series.title}
|
|
</h3>
|
|
{showDescription && series.description && (
|
|
<div className="text-espresso/80 line-clamp-2">
|
|
<RichTextRenderer content={series.description} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
</article>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function FeaturedLayout({
|
|
items,
|
|
showDescription,
|
|
}: {
|
|
items: Series[]
|
|
showDescription?: boolean
|
|
}) {
|
|
const [featured, ...rest] = items
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
{/* Featured Series */}
|
|
<Link
|
|
href={`/serien/${featured.slug}`}
|
|
className="group block lg:row-span-2"
|
|
>
|
|
<article
|
|
className="relative h-full min-h-[400px] rounded-2xl overflow-hidden"
|
|
style={{
|
|
backgroundColor: featured.brandColor || '#C6A47E',
|
|
}}
|
|
>
|
|
{featured.coverImage && (
|
|
<Image
|
|
src={featured.coverImage.url}
|
|
alt={featured.title}
|
|
fill
|
|
className="object-cover opacity-30"
|
|
/>
|
|
)}
|
|
|
|
<div className="absolute inset-0 flex flex-col justify-end p-8">
|
|
{featured.logo && (
|
|
<div className="relative w-32 h-16 mb-4">
|
|
<Image
|
|
src={featured.logo.url}
|
|
alt=""
|
|
fill
|
|
className="object-contain object-left"
|
|
/>
|
|
</div>
|
|
)}
|
|
<h3 className="text-2xl lg:text-3xl font-semibold text-soft-white mb-3">
|
|
{featured.title}
|
|
</h3>
|
|
{showDescription && featured.description && (
|
|
<div className="text-soft-white/80 line-clamp-3">
|
|
<RichTextRenderer content={featured.description} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
</article>
|
|
</Link>
|
|
|
|
{/* Other Series */}
|
|
<div className="space-y-6">
|
|
{rest.slice(0, 3).map((series) => (
|
|
<SeriesCard
|
|
key={series.id}
|
|
series={series}
|
|
showDescription={false}
|
|
compact
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
interface SeriesCardProps {
|
|
series: Series
|
|
showDescription?: boolean
|
|
compact?: boolean
|
|
}
|
|
|
|
function SeriesCard({ series, showDescription, compact }: SeriesCardProps) {
|
|
const imageUrl = getImageUrl(series.coverImage) || getImageUrl(series.logo)
|
|
|
|
return (
|
|
<Link href={`/serien/${series.slug}`} className="group block">
|
|
<article
|
|
className={cn(
|
|
'bg-soft-white border border-warm-gray rounded-2xl overflow-hidden transition-all duration-300 hover:-translate-y-1 hover:shadow-xl',
|
|
compact && 'flex items-center gap-4 p-4'
|
|
)}
|
|
>
|
|
{/* Image */}
|
|
{!compact && (
|
|
<div
|
|
className="relative aspect-video"
|
|
style={{ backgroundColor: series.brandColor || '#C6A47E' }}
|
|
>
|
|
{imageUrl && (
|
|
<Image
|
|
src={imageUrl}
|
|
alt={series.title}
|
|
fill
|
|
className={cn(
|
|
series.logo ? 'object-contain p-8' : 'object-cover opacity-50'
|
|
)}
|
|
/>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{compact && (
|
|
<div
|
|
className="relative w-16 h-16 flex-shrink-0 rounded-lg overflow-hidden flex items-center justify-center"
|
|
style={{ backgroundColor: series.brandColor || '#C6A47E' }}
|
|
>
|
|
{series.logo ? (
|
|
<Image
|
|
src={series.logo.url}
|
|
alt=""
|
|
fill
|
|
className="object-contain p-2"
|
|
/>
|
|
) : (
|
|
<span className="text-soft-white font-bold text-lg">
|
|
{series.title.slice(0, 2).toUpperCase()}
|
|
</span>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* Content */}
|
|
<div className={cn(!compact && 'p-6', compact && 'flex-1 min-w-0')}>
|
|
<h3
|
|
className={cn(
|
|
'font-semibold group-hover:text-brass transition-colors',
|
|
compact ? 'text-lg' : 'text-xl mb-2'
|
|
)}
|
|
>
|
|
{series.title}
|
|
</h3>
|
|
{showDescription && !compact && series.description && (
|
|
<div className="text-espresso/80 line-clamp-2">
|
|
<RichTextRenderer content={series.description} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
</article>
|
|
</Link>
|
|
)
|
|
}
|