feat(ui): add Team block to about pages and fix Affiliation link
This commit is contained in:
@@ -13,7 +13,7 @@ const iconMap = {
|
|||||||
landmark: Landmark,
|
landmark: Landmark,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AffiliationBlock: React.FC<Props> = ({ eyebrow, title, description, logo, linkText, trustPoints }) => {
|
export const AffiliationBlock: React.FC<Props> = ({ eyebrow, title, description, logo, linkText, linkUrl, trustPoints }) => {
|
||||||
return (
|
return (
|
||||||
<section className="py-20 md:py-32 bg-background border-t border-border overflow-hidden">
|
<section className="py-20 md:py-32 bg-background border-t border-border overflow-hidden">
|
||||||
<div className="container-premium">
|
<div className="container-premium">
|
||||||
@@ -32,10 +32,16 @@ export const AffiliationBlock: React.FC<Props> = ({ eyebrow, title, description,
|
|||||||
<img src={logo.url} alt={logo.alt || "Logo stowarzyszenia"} className="max-w-full h-auto grayscale hover:grayscale-0 transition-all duration-300" />
|
<img src={logo.url} alt={logo.alt || "Logo stowarzyszenia"} className="max-w-full h-auto grayscale hover:grayscale-0 transition-all duration-300" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{linkText && (
|
{linkText && linkUrl && (
|
||||||
<Link href="/stowarzyszenie" className="text-accent font-semibold text-lg hover:text-accent-hover underline underline-offset-4">
|
linkUrl.startsWith('#') ? (
|
||||||
{linkText}
|
<a href={linkUrl} className="text-accent font-semibold text-lg hover:text-accent-hover underline underline-offset-4">
|
||||||
</Link>
|
{linkText}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<Link href={linkUrl} className="text-accent font-semibold text-lg hover:text-accent-hover underline underline-offset-4">
|
||||||
|
{linkText}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -26,5 +26,6 @@ export const Affiliation: Block = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ name: 'linkText', type: 'text', localized: true },
|
{ name: 'linkText', type: 'text', localized: true },
|
||||||
|
{ name: 'linkUrl', type: 'text', localized: true },
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { FinalCTABlock } from './FinalCTA/Component'
|
|||||||
import { PageHeaderBlock } from './PageHeader/Component'
|
import { PageHeaderBlock } from './PageHeader/Component'
|
||||||
import { ContactInfoBlock } from './ContactInfo/Component'
|
import { ContactInfoBlock } from './ContactInfo/Component'
|
||||||
import { BioBlock } from './Bio/Component'
|
import { BioBlock } from './Bio/Component'
|
||||||
|
import { TeamBlock } from './Team/Component'
|
||||||
import { ServicesDetailedBlock } from './ServicesDetailed/Component'
|
import { ServicesDetailedBlock } from './ServicesDetailed/Component'
|
||||||
import { EligibilityBlock } from './Eligibility/Component'
|
import { EligibilityBlock } from './Eligibility/Component'
|
||||||
import { StatsBlock } from './Stats/Component'
|
import { StatsBlock } from './Stats/Component'
|
||||||
@@ -23,6 +24,7 @@ const blockComponents = {
|
|||||||
pageHeader: PageHeaderBlock,
|
pageHeader: PageHeaderBlock,
|
||||||
contactInfo: ContactInfoBlock,
|
contactInfo: ContactInfoBlock,
|
||||||
bio: BioBlock,
|
bio: BioBlock,
|
||||||
|
team: TeamBlock,
|
||||||
servicesDetailed: ServicesDetailedBlock,
|
servicesDetailed: ServicesDetailedBlock,
|
||||||
eligibility: EligibilityBlock,
|
eligibility: EligibilityBlock,
|
||||||
hero: HeroBlock,
|
hero: HeroBlock,
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
'use client'
|
||||||
|
import React from 'react'
|
||||||
|
import type { Page, Media } from '../../payload-types'
|
||||||
|
import Image from 'next/image'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { motion } from 'framer-motion'
|
||||||
|
|
||||||
|
type Props = Extract<NonNullable<Page['layout']>[0], { blockType: 'team' }>
|
||||||
|
|
||||||
|
export const TeamBlock: React.FC<Props> = ({ members }) => {
|
||||||
|
if (!members || members.length === 0) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="py-20 md:py-32 bg-background border-y border-border overflow-hidden">
|
||||||
|
<div className="container-premium">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true, margin: "-100px" }}
|
||||||
|
transition={{ duration: 0.6, ease: "easeOut" }}
|
||||||
|
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8 md:gap-12"
|
||||||
|
>
|
||||||
|
{members.map((member: NonNullable<Props['members']>[0], index: number) => {
|
||||||
|
const photo = typeof member.photo === 'object' && member.photo !== null ? (member.photo as Media) : null
|
||||||
|
const initial = member.name ? member.name.charAt(0).toUpperCase() : ''
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={member.id || index} className="flex flex-col bg-card border border-border rounded-none overflow-hidden hover:border-accent/50 transition-colors h-full">
|
||||||
|
<div className="relative aspect-square w-full bg-border/30 flex items-center justify-center shrink-0">
|
||||||
|
{photo && photo.url ? (
|
||||||
|
<Image
|
||||||
|
src={photo.url}
|
||||||
|
alt={photo.alt || member.name}
|
||||||
|
fill
|
||||||
|
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
||||||
|
className="object-cover grayscale hover:grayscale-0 transition-all duration-700"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="w-full h-full flex items-center justify-center bg-accent text-background font-heading text-6xl font-bold">
|
||||||
|
{initial}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-8 flex flex-col flex-1">
|
||||||
|
<h3 className="font-heading text-2xl font-bold text-foreground mb-1">
|
||||||
|
{member.name}
|
||||||
|
</h3>
|
||||||
|
<p className="text-accent font-semibold text-sm uppercase tracking-wide mb-6">
|
||||||
|
{member.role}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{member.description && (
|
||||||
|
<p className="text-foreground/75 leading-relaxed mb-8 flex-1">
|
||||||
|
{member.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{member.bookingUrl && (
|
||||||
|
<div className="mt-auto pt-4 border-t border-border">
|
||||||
|
<Link
|
||||||
|
href={member.bookingUrl}
|
||||||
|
className="inline-flex items-center text-sm font-semibold text-foreground hover:text-accent uppercase tracking-wider transition-colors"
|
||||||
|
>
|
||||||
|
Umów się
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import type { Block } from 'payload'
|
||||||
|
|
||||||
|
export const Team: Block = {
|
||||||
|
slug: 'team',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'members',
|
||||||
|
type: 'array',
|
||||||
|
localized: true,
|
||||||
|
maxRows: 20,
|
||||||
|
fields: [
|
||||||
|
{ name: 'name', type: 'text', required: true },
|
||||||
|
{ name: 'role', type: 'text', required: true },
|
||||||
|
{ name: 'description', type: 'textarea' },
|
||||||
|
{ name: 'photo', type: 'upload', relationTo: 'media' },
|
||||||
|
{ name: 'bookingUrl', type: 'text' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ import { Eligibility } from '../blocks/Eligibility/config'
|
|||||||
import { Stats } from '../blocks/Stats/config'
|
import { Stats } from '../blocks/Stats/config'
|
||||||
import { TestimonialsGrid } from '../blocks/TestimonialsGrid/config'
|
import { TestimonialsGrid } from '../blocks/TestimonialsGrid/config'
|
||||||
import { Bio } from '../blocks/Bio/config'
|
import { Bio } from '../blocks/Bio/config'
|
||||||
|
import { Team } from '../blocks/Team/config'
|
||||||
|
|
||||||
export const Pages: CollectionConfig = {
|
export const Pages: CollectionConfig = {
|
||||||
slug: 'pages',
|
slug: 'pages',
|
||||||
@@ -43,6 +44,7 @@ export const Pages: CollectionConfig = {
|
|||||||
PageHeader,
|
PageHeader,
|
||||||
ContactInfo,
|
ContactInfo,
|
||||||
Bio,
|
Bio,
|
||||||
|
Team,
|
||||||
ServicesDetailed,
|
ServicesDetailed,
|
||||||
Eligibility,
|
Eligibility,
|
||||||
Hero,
|
Hero,
|
||||||
|
|||||||
@@ -246,6 +246,21 @@ export interface Page {
|
|||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'bio';
|
blockType: 'bio';
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
members?:
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
role: string;
|
||||||
|
description?: string | null;
|
||||||
|
photo?: (number | null) | Media;
|
||||||
|
bookingUrl?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'team';
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
title: string;
|
title: string;
|
||||||
subtitle?: string | null;
|
subtitle?: string | null;
|
||||||
@@ -385,6 +400,7 @@ export interface Page {
|
|||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
linkText?: string | null;
|
linkText?: string | null;
|
||||||
|
linkUrl?: string | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'affiliation';
|
blockType: 'affiliation';
|
||||||
@@ -653,6 +669,22 @@ export interface PagesSelect<T extends boolean = true> {
|
|||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
};
|
};
|
||||||
|
team?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
members?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
name?: T;
|
||||||
|
role?: T;
|
||||||
|
description?: T;
|
||||||
|
photo?: T;
|
||||||
|
bookingUrl?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
servicesDetailed?:
|
servicesDetailed?:
|
||||||
| T
|
| T
|
||||||
| {
|
| {
|
||||||
@@ -814,6 +846,7 @@ export interface PagesSelect<T extends boolean = true> {
|
|||||||
id?: T;
|
id?: T;
|
||||||
};
|
};
|
||||||
linkText?: T;
|
linkText?: T;
|
||||||
|
linkUrl?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user