Some checks failed
Build & Deploy / 🔍 Prepare (push) Failing after 4s
Build & Deploy / 🧪 QA (push) Has been skipped
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
171 lines
6.9 KiB
TypeScript
171 lines
6.9 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
import { motion, useScroll, useTransform } from 'framer-motion';
|
|
import { Building2, Compass, Layers, ArrowDownToLine, Wrench, Factory, Zap, MapPin, CheckCircle2 } from 'lucide-react';
|
|
import { useTranslations } from 'next-intl';
|
|
|
|
const iconMap: Record<string, React.ElementType> = {
|
|
Building2,
|
|
Compass,
|
|
Layers,
|
|
ArrowDownToLine,
|
|
Wrench,
|
|
Factory,
|
|
Zap,
|
|
MapPin,
|
|
CheckCircle2
|
|
};
|
|
|
|
interface Milestone {
|
|
date: string;
|
|
year: string;
|
|
title: string;
|
|
desc: string;
|
|
iconName: string;
|
|
}
|
|
|
|
interface CompanyTimelineProps {
|
|
badge?: string;
|
|
title?: string;
|
|
milestones?: Milestone[];
|
|
}
|
|
|
|
export function CompanyTimeline({
|
|
badge,
|
|
title,
|
|
milestones
|
|
}: CompanyTimelineProps) {
|
|
const t = useTranslations('CompanyTimeline');
|
|
|
|
const finalBadge = badge || t('badge');
|
|
const finalTitle = title || t('title');
|
|
|
|
const finalMilestones = milestones || [
|
|
{
|
|
date: '16.12.2015',
|
|
year: '2015',
|
|
title: t('milestones.2015.title'),
|
|
desc: t('milestones.2015.desc'),
|
|
iconName: 'Building2',
|
|
},
|
|
{
|
|
date: '04.02.2019',
|
|
year: '2019',
|
|
title: t('milestones.2019_ing.title'),
|
|
desc: t('milestones.2019_ing.desc'),
|
|
iconName: 'Compass',
|
|
},
|
|
{
|
|
date: '14.11.2019',
|
|
year: '2019',
|
|
title: t('milestones.2019_verw.title'),
|
|
desc: t('milestones.2019_verw.desc'),
|
|
iconName: 'Layers',
|
|
},
|
|
{
|
|
date: '21.10.2025',
|
|
year: '2025',
|
|
title: t('milestones.2025.title'),
|
|
desc: t('milestones.2025.desc'),
|
|
iconName: 'ArrowDownToLine',
|
|
},
|
|
];
|
|
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
const { scrollYProgress } = useScroll({
|
|
target: containerRef,
|
|
offset: ["start center", "end center"]
|
|
});
|
|
|
|
const lineHeight = useTransform(scrollYProgress, [0, 1], ["0%", "100%"]);
|
|
|
|
return (
|
|
<section className="py-12 md:py-32 bg-[#FAFAFA] relative overflow-hidden">
|
|
{/* Background Decor */}
|
|
<div className="absolute top-0 right-0 w-[600px] h-[600px] bg-primary/5 rounded-full blur-[100px] pointer-events-none -translate-y-1/2 translate-x-1/3" />
|
|
<div className="absolute bottom-0 left-0 w-[800px] h-[800px] bg-neutral-200/40 rounded-full blur-[120px] pointer-events-none translate-y-1/3 -translate-x-1/3" />
|
|
|
|
<div className="container max-w-5xl relative z-10">
|
|
<div className="text-center mb-12 md:mb-24">
|
|
<div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-white border border-neutral-200 text-neutral-dark text-xs font-bold uppercase tracking-wider mb-6 shadow-sm">
|
|
<span className="w-2 h-2 rounded-full bg-primary animate-pulse" />
|
|
{finalBadge}
|
|
</div>
|
|
<h3 className="font-heading text-3xl md:text-5xl lg:text-6xl font-extrabold text-neutral-dark tracking-tight">
|
|
{finalTitle}
|
|
</h3>
|
|
</div>
|
|
|
|
<div ref={containerRef} className="relative">
|
|
{/* Central Line Background */}
|
|
<div className="absolute left-[38px] md:left-1/2 top-0 bottom-0 w-1.5 bg-neutral-200 -translate-x-1/2 rounded-full" />
|
|
|
|
{/* Animated Central Line */}
|
|
<div className="absolute left-[38px] md:left-1/2 top-0 bottom-0 w-1.5 -translate-x-1/2 rounded-full overflow-hidden">
|
|
<motion.div
|
|
className="w-full bg-primary drop-shadow-[0_0_8px_rgba(130,237,32,0.8)]"
|
|
style={{ height: lineHeight }}
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-16 md:space-y-24">
|
|
{finalMilestones.map((milestone, i) => {
|
|
const isEven = i % 2 === 0;
|
|
const Icon = iconMap[milestone.iconName] || Building2;
|
|
|
|
return (
|
|
<motion.div
|
|
key={i}
|
|
initial={{ opacity: 0, y: 40 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true, margin: "-100px" }}
|
|
transition={{ duration: 0.7, ease: "easeOut" }}
|
|
className="relative flex flex-col md:flex-row items-center w-full group"
|
|
>
|
|
{/* Timeline Dot with Pulse */}
|
|
<div className="absolute left-[38px] md:left-1/2 top-0 md:top-1/2 w-8 h-8 rounded-full bg-white border-[5px] border-neutral-200 group-hover:border-primary transition-colors duration-500 z-20 -translate-x-1/2 md:-translate-y-1/2 shadow-sm flex items-center justify-center">
|
|
<div className="w-2 h-2 rounded-full bg-neutral-300 group-hover:bg-primary transition-colors duration-500" />
|
|
</div>
|
|
|
|
{/* Content Box Wrapper */}
|
|
<div className={`w-full md:w-1/2 pl-24 md:pl-0 ${isEven ? 'md:pr-16 md:text-right' : 'md:pl-16 md:ml-auto md:text-left'}`}>
|
|
|
|
{/* The Card */}
|
|
<div className="relative bg-white p-8 md:p-10 rounded-[2rem] shadow-[0_8px_30px_rgb(0,0,0,0.04)] border border-neutral-100 hover:shadow-[0_20px_40px_rgb(0,0,0,0.08)] hover:border-primary/20 transition-all duration-500 overflow-hidden transform hover:-translate-y-1">
|
|
|
|
{/* Giant Typography Background Year */}
|
|
<div className={`absolute select-none pointer-events-none text-[140px] leading-none font-black text-neutral-50 z-0 ${isEven ? '-left-8' : '-right-8'} -bottom-10 opacity-70 group-hover:scale-110 transition-transform duration-700`}>
|
|
{milestone.year}
|
|
</div>
|
|
|
|
<div className="relative z-10">
|
|
{/* Header Row */}
|
|
<div className={`flex items-center gap-4 mb-6 ${isEven ? 'md:flex-row-reverse' : ''}`}>
|
|
<div className="w-12 h-12 rounded-2xl bg-primary/10 text-primary flex items-center justify-center shrink-0 group-hover:bg-primary group-hover:text-neutral-dark transition-colors duration-500">
|
|
<Icon className="w-6 h-6" />
|
|
</div>
|
|
<span className="inline-block py-1.5 px-4 rounded-full bg-neutral-100 text-neutral-600 font-bold text-sm tracking-wide">
|
|
{milestone.date}
|
|
</span>
|
|
</div>
|
|
|
|
<h4 className="font-heading font-extrabold text-2xl md:text-3xl mb-4 text-neutral-dark leading-tight group-hover:text-primary transition-colors duration-300">
|
|
{milestone.title}
|
|
</h4>
|
|
<p className="text-neutral-500 text-lg leading-relaxed">
|
|
{milestone.desc}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</motion.div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|