Files
e-tib.com/components/blocks/ReferencesSlider.tsx
Marc Mintel 76798e4704
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 32s
Build & Deploy / 🧪 QA (push) Successful in 1m33s
Build & Deploy / 🏗️ Build (push) Successful in 3m7s
Build & Deploy / 🚀 Deploy (push) Successful in 31s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 1m3s
Build & Deploy / 🔔 Notify (push) Successful in 2s
refactor(ui): replace dragging carousel with centered grid layout
2026-06-23 13:32:43 +02:00

137 lines
5.6 KiB
TypeScript

'use client';
import * as React from 'react';
import { m, LazyMotion, domAnimation } from 'framer-motion';
import Link from 'next/link';
import Image from 'next/image';
import { HoverShineOverlay } from '@/components/ui/HoverShineOverlay';
import { useTranslations, useLocale } from 'next-intl';
export interface Reference {
id: string;
title: string;
slug: string;
category: string;
image?: {
url: string;
alt?: string;
} | string | null;
}
interface ReferencesSliderProps {
references?: Reference[];
badge?: string;
title?: string;
description?: string;
ctaLabel?: string;
ctaHref?: string;
data?: {
badge?: string;
title?: string;
description?: string;
ctaLabel?: string;
ctaHref?: string;
}
}
export function ReferencesSlider(props: ReferencesSliderProps) {
const t = useTranslations('ReferencesSlider');
const locale = useLocale();
const { data } = props;
const references = props.references || [];
const badge = props.badge || data?.badge || t('badge');
const title = props.title || data?.title || t('title');
const description = props.description || data?.description || t('description');
const ctaLabel = props.ctaLabel || data?.ctaLabel || t('ctaLabel');
const ctaHref = props.ctaHref || data?.ctaHref || `/${locale}/referenzen`;
if (!references || references.length === 0) return null;
// Fallback images pool if CMS doesn't provide one
const fallbacks = [
'/assets/photos/DSC01123.JPG',
'/assets/photos/DSC00850.JPG',
'/assets/photos/DSC01129.JPG',
'/assets/photos/DSC00010.JPG',
];
return (
<section id="referenzen" className="pt-16 pb-8 md:py-24 lg:py-32 bg-neutral-dark text-white relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-b from-neutral-dark via-neutral-900 to-neutral-dark z-0" />
<div className="container relative z-10 mb-12 flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
<div className="text-left">
<h2 className="text-primary-light font-bold tracking-wider uppercase text-sm mb-3">{badge}</h2>
<h3 className="font-heading text-3xl md:text-5xl font-extrabold">{title}</h3>
</div>
<div className="text-left max-w-3xl mx-auto md:mx-0 md:mb-16">
{description}
</div>
</div>
<div className="container relative z-10 w-full">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-8 justify-center items-stretch">
{references.map((ref, i) => {
const imgSrc = ref.image
? (typeof ref.image === 'string' ? ref.image : ref.image.url)
: fallbacks[i % fallbacks.length];
return (
<LazyMotion key={ref.id} features={domAnimation}>
<m.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-50px" }}
transition={{ delay: i * 0.1, duration: 0.8, ease: [0.16, 1, 0.3, 1] }}
className="w-full h-full group"
>
<Link
href={`/${locale}/referenzen#${ref.slug}`}
data-testid="reference-tile"
className="block relative h-[250px] md:h-[300px] w-full rounded-2xl overflow-hidden shadow-xl shadow-black/20"
>
<Image
src={imgSrc}
alt={ref.title}
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
className="object-cover object-center group-hover:scale-105 transition-transform duration-700 ease-[cubic-bezier(0.16,1,0.3,1)]"
priority={i < 3}
/>
<div className="absolute inset-0 bg-gradient-to-t from-[#050B14]/90 via-[#050B14]/20 to-transparent pointer-events-none" />
<HoverShineOverlay />
<div className="absolute bottom-0 left-0 p-6 md:p-8 right-0 transform transition-transform duration-500 ease-out group-hover:-translate-y-2">
<div className="bg-primary/20 backdrop-blur-md border border-primary/30 text-white text-[10px] md:text-xs font-bold uppercase tracking-wider px-3 py-1 rounded-full mb-3 inline-block shadow-[0_0_15px_rgba(14,122,92,0.3)]">
{ref.category}
</div>
<h4 className="text-xl md:text-2xl font-bold font-heading leading-tight drop-shadow-md text-white group-hover:text-primary-light transition-colors duration-300">
{ref.title}
</h4>
</div>
</Link>
</m.div>
</LazyMotion>
);
})}
</div>
</div>
<div className="container relative z-10 flex justify-center mt-12 md:mt-20 pb-4">
<Link
href={ctaHref}
className="px-8 py-4 rounded-xl bg-white/5 border border-white/20 hover:bg-white hover:text-neutral-dark hover:shadow-[0_0_20px_rgba(255,255,255,0.3)] transition-all font-bold flex items-center gap-3 backdrop-blur-sm"
>
{ctaLabel}
<svg xmlns="http://www.w3.org/2000/svg" className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>
</Link>
</div>
</section>
);
}