feat: make Hero full viewport, add TestimonialsGrid block mechanism, and remove footer availability pill
This commit is contained in:
@@ -109,9 +109,9 @@ export const HeroBlock: React.FC<Props> = ({
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
variants={containerVariants}
|
||||
className="w-full bg-background text-foreground pt-32 pb-16 md:pt-44 md:pb-24 overflow-hidden"
|
||||
className="w-full bg-background text-foreground min-h-[calc(100svh-6rem)] flex items-center py-12 lg:py-0 overflow-hidden"
|
||||
>
|
||||
<div className="container-premium">
|
||||
<div className="container-premium w-full">
|
||||
<div className="flex flex-col-reverse lg:flex-row items-center justify-between gap-14 lg:gap-20">
|
||||
|
||||
{/* Kolumna tekstowa z animacjami wejścia */}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ServicesListBlock } from './ServicesList/Component'
|
||||
import { ProcessBlock } from './Process/Component'
|
||||
import { AffiliationBlock } from './Affiliation/Component'
|
||||
import { TestimonialsBlock } from './Testimonials/Component'
|
||||
import { TestimonialsGridBlock } from './TestimonialsGrid/Component'
|
||||
import { FAQTeaserBlock } from './FAQTeaser/Component'
|
||||
import { FinalCTABlock } from './FinalCTA/Component'
|
||||
|
||||
@@ -30,6 +31,7 @@ const blockComponents = {
|
||||
process: ProcessBlock,
|
||||
affiliation: AffiliationBlock,
|
||||
testimonials: TestimonialsBlock,
|
||||
testimonialsGrid: TestimonialsGridBlock,
|
||||
faqTeaser: FAQTeaserBlock,
|
||||
finalCta: FinalCTABlock,
|
||||
TextSection: ({ text }: { text: string }) => <section className="py-20"><div className="container mx-auto px-4"><p>{text}</p></div></section>,
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import type { Page } from '../../payload-types'
|
||||
|
||||
type Props = Extract<NonNullable<Page['layout']>[0], { blockType: 'testimonialsGrid' }>
|
||||
|
||||
interface ColumnProps {
|
||||
items: NonNullable<Props['items']>
|
||||
duration: number
|
||||
className?: string
|
||||
}
|
||||
|
||||
const ScrollingColumn: React.FC<ColumnProps> = ({ items, duration, className = '' }) => {
|
||||
// Dublujemy x2 dla bezszwowej pętli
|
||||
const doubledItems = [...items, ...items]
|
||||
|
||||
return (
|
||||
<div className={`overflow-hidden relative ${className}`}>
|
||||
<motion.div
|
||||
animate={{ y: ['0%', '-50%'] }}
|
||||
transition={{
|
||||
duration,
|
||||
repeat: Infinity,
|
||||
ease: 'linear',
|
||||
}}
|
||||
className="flex flex-col gap-6"
|
||||
>
|
||||
{doubledItems.map((item, index) => (
|
||||
<div
|
||||
key={item.id ? `${item.id}-${index}` : index}
|
||||
className="bg-card border border-border rounded-none p-6 md:p-8 flex flex-col justify-between shadow-xs select-none"
|
||||
>
|
||||
<p className="text-foreground/90 text-base sm:text-lg leading-relaxed italic mb-6">
|
||||
“{item.quote}”
|
||||
</p>
|
||||
<div>
|
||||
<div className="w-10 h-px bg-accent/60 mb-3" />
|
||||
<p className="font-heading font-bold text-foreground text-base tracking-tight">
|
||||
{item.authorName}
|
||||
</p>
|
||||
{item.authorRole && (
|
||||
<p className="text-foreground/60 text-xs sm:text-sm font-medium mt-0.5">
|
||||
{item.authorRole}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</motion.div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const TestimonialsGridBlock: React.FC<Props> = ({ items }) => {
|
||||
// WARUNEK KRYTYCZNY: komponent renderuje się TYLKO jeśli items.length >= 6
|
||||
if (!items || items.length < 6) return null
|
||||
|
||||
// Podział na 3 kolumny
|
||||
const col1 = items.filter((_, i) => i % 3 === 0)
|
||||
const col2 = items.filter((_, i) => i % 3 === 1)
|
||||
const col3 = items.filter((_, i) => i % 3 === 2)
|
||||
|
||||
return (
|
||||
<section className="py-20 md:py-32 bg-background border-y border-border overflow-hidden">
|
||||
<div className="container-premium">
|
||||
<div
|
||||
className="relative h-[640px] overflow-hidden"
|
||||
style={{
|
||||
maskImage: 'linear-gradient(to bottom, transparent 0%, black 12%, black 88%, transparent 100%)',
|
||||
WebkitMaskImage: 'linear-gradient(to bottom, transparent 0%, black 12%, black 88%, transparent 100%)',
|
||||
}}
|
||||
>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 h-full items-start">
|
||||
<ScrollingColumn items={col1} duration={15} />
|
||||
<ScrollingColumn items={col2} duration={19} className="hidden md:block" />
|
||||
<ScrollingColumn items={col3} duration={17} className="hidden lg:block" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { Block } from 'payload'
|
||||
|
||||
export const TestimonialsGrid: Block = {
|
||||
slug: 'testimonialsGrid',
|
||||
fields: [
|
||||
{
|
||||
name: 'items',
|
||||
type: 'array',
|
||||
required: true,
|
||||
minRows: 1,
|
||||
fields: [
|
||||
{ name: 'quote', type: 'textarea', required: true },
|
||||
{ name: 'authorName', type: 'text', required: true },
|
||||
{ name: 'authorRole', type: 'text' },
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user