Files
e-tib.com/components/blocks/CertificatesBlock.tsx
Marc Mintel 6426512192
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 1m38s
Build & Deploy / 🧪 QA (push) Successful in 1m50s
Build & Deploy / 🏗️ Build (push) Successful in 3m31s
Build & Deploy / 🚀 Deploy (push) Successful in 44s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
UI: simplify reveal animations to prevent flickering and late triggers
2026-06-21 09:08:44 +02:00

227 lines
8.6 KiB
TypeScript

'use client';
import { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import { Container } from '@/components/ui/Container';
import { Button } from '@/components/ui/Button';
import { HoverShineOverlay } from '@/components/ui/HoverShineOverlay';
import { AnimatedGlossyBorder } from '@/components/ui/AnimatedGlossyBorder';
import { FileText, Award, Download, ShieldCheck } from 'lucide-react';
import { LogoArcs } from '@/components/ui/LogoArcs';
interface Certificate {
title: string;
description: string;
pdfUrl?: string;
type: 'iso' | 'tax' | 'general';
date?: string;
}
interface CertificatesBlockProps {
badge?: string;
title?: string;
description?: string;
certificates?: Certificate[];
hideHeader?: boolean;
}
const defaultCertificates: Certificate[] = [
{
title: 'ISO 14001:2015',
description: 'Umweltmanagementsystem',
pdfUrl: '/assets/certificates/iso14001.pdf',
type: 'iso',
date: '14.12.2023',
},
{
title: 'ISO 9001:2015',
description: 'Qualitätsmanagementsystem',
pdfUrl: '/assets/certificates/iso9001.pdf',
type: 'iso',
date: '14.12.2023',
},
{
title: 'DIN EN ISO 45001:2018',
description: 'Arbeits- und Gesundheitsschutz',
pdfUrl: '/assets/certificates/iso45001.pdf',
type: 'iso',
date: '05.12.2025',
},
{
title: 'Freistellungsbescheinigung',
description: 'Nach § 48 b EStG',
pdfUrl: '/assets/certificates/freistellung.pdf',
type: 'tax',
date: '09.02.2024',
},
{
title: 'Nachweis § 13b UStG',
description: 'Steuerschuldnerschaft des Leistungsempfängers',
pdfUrl: '/assets/certificates/nachweis13b.pdf',
type: 'tax',
date: '09.02.2024',
},
{
title: 'Bescheinigung in Steuersachen',
description: 'Zertifikat des Finanzamtes',
pdfUrl: '/assets/certificates/bescheinigung.pdf',
type: 'tax',
date: '13.02.2025',
},
];
export function CertificatesBlock({ badge, title, description, certificates = defaultCertificates, hideHeader = false }: CertificatesBlockProps) {
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
setIsMounted(true);
}, []);
const badgeText = badge || 'Zertifizierungen & Nachweise';
const titleText = title || 'Geprüfte Qualität und Sicherheit';
const descriptionText = description || 'Transparenz und höchste Standards sind das Fundament unserer Arbeit. Hier finden Sie unsere aktuellen Zertifikate und Bescheinigungen als PDF-Download.';
const containerVariants = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1,
},
},
};
const itemVariants = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0, transition: { duration: 1.0, ease: [0.16, 1, 0.3, 1] } },
};
if (!isMounted) {
return (
<section className="py-16 md:py-24 lg:py-32 bg-neutral-50 relative overflow-hidden">
<Container>
{!hideHeader && (
<div className="text-center max-w-3xl mx-auto mb-16">
<h2 className="text-primary font-bold tracking-wider uppercase text-sm mb-3">{badgeText}</h2>
<h3 className="font-heading text-2xl md:text-5xl font-extrabold text-neutral-dark mb-6">{titleText}</h3>
<p className="text-lg text-neutral-600">{descriptionText}</p>
</div>
)}
</Container>
</section>
);
}
return (
<section className="py-12 md:py-24 bg-neutral-50 relative overflow-hidden">
{/* Background Accents */}
<div className="absolute top-0 right-0 w-1/3 h-full bg-primary/5 -skew-x-12 translate-x-1/2" />
<div className="absolute bottom-0 left-0 w-1/4 h-1/2 bg-accent/5 skew-y-12 -translate-x-1/2 blur-3xl rounded-full" />
<Container className="relative z-10">
{!hideHeader && (
<div className="text-center max-w-3xl mx-auto mb-16">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-50px" }}
transition={{ duration: 1.0, ease: [0.16, 1, 0.3, 1] }}
>
<h2 className="text-primary font-bold tracking-wider uppercase text-sm mb-3">{badgeText}</h2>
<h3 className="font-heading text-2xl md:text-5xl font-extrabold text-neutral-dark mb-6">{titleText}</h3>
<p className="text-lg text-neutral-600">{descriptionText}</p>
</motion.div>
</div>
)}
<motion.div
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"
variants={containerVariants}
initial="hidden"
whileInView="show"
viewport={{ once: true, margin: "-50px" }}
>
{certificates.map((cert, index) => {
const isIso = cert.type === 'iso';
const Wrapper = cert.pdfUrl ? motion.a : motion.div;
const wrapperProps = cert.pdfUrl ? {
href: encodeURI(cert.pdfUrl),
target: "_blank",
rel: "noopener noreferrer"
} : {};
return (
<Wrapper
key={index}
{...wrapperProps as any}
variants={itemVariants}
className={`group relative rounded-2xl p-6 md:p-8 flex flex-col justify-between overflow-hidden transition-all duration-500 hover:shadow-xl hover:-translate-y-1 border ${
isIso
? 'bg-neutral-dark text-white border-neutral-800 shadow-lg'
: 'bg-white text-neutral-dark border-neutral-200 shadow-md'
}`}
>
{/* Background Decorative Elements */}
<div className={`absolute top-0 right-0 w-32 h-32 blur-3xl rounded-full -mr-10 -mt-10 transition-colors duration-500 ${
isIso ? 'bg-primary/20 group-hover:bg-primary/40' : 'bg-primary/10 group-hover:bg-primary/20'
}`} />
{isIso && (
<div className="absolute -bottom-[20%] -right-[20%] w-[100%] h-[100%] z-0 opacity-10 group-hover:opacity-20 transition-opacity duration-1000 pointer-events-none mix-blend-overlay">
<LogoArcs className="w-full h-full text-white animate-[spin_60s_linear_infinite]" />
</div>
)}
<HoverShineOverlay shineColor={isIso ? 'via-white/20' : 'via-black/5'} />
<AnimatedGlossyBorder color={isIso ? 'primary' : 'primary'} className="opacity-0 group-hover:opacity-100 transition-opacity duration-700" borderWidth={2} />
<div className="relative z-10 flex flex-col h-full">
<div className="flex justify-between items-start mb-8">
<div className={`p-3 rounded-xl inline-flex ${
isIso ? 'bg-primary/20 text-primary-light backdrop-blur-md' : 'bg-primary/10 text-primary'
}`}>
{isIso ? <Award size={28} /> : <ShieldCheck size={28} />}
</div>
{cert.date && (
<span className={`text-xs font-bold uppercase tracking-wider px-3 py-1 rounded-full ${
isIso ? 'bg-neutral-800 text-neutral-400 border border-neutral-700' : 'bg-neutral-100 text-neutral-500 border border-neutral-200'
}`}>
{cert.date}
</span>
)}
</div>
<div>
<h4 className={`text-xl font-bold mb-2 ${isIso ? 'text-white' : 'text-neutral-dark'}`}>
{cert.title}
</h4>
<p className={`text-sm mb-6 ${isIso ? 'text-neutral-400' : 'text-neutral-600'}`}>
{cert.description}
</p>
</div>
<div className={`mt-auto inline-flex items-center text-sm font-bold uppercase tracking-wider transition-all ${
cert.pdfUrl ? 'group-hover:gap-3' : ''
} ${
isIso ? 'text-primary-light' : 'text-primary'
}`}>
{cert.pdfUrl ? (
<>
<span>Download PDF</span>
<Download size={16} className="ml-2 group-hover:translate-y-0.5 transition-transform" />
</>
) : (
<span className="text-neutral-400">Nachweis liegt vor</span>
)}
</div>
</div>
</Wrapper>
);
})}
</motion.div>
</Container>
</section>
);
}