fix: localize Pages layout and implement /uslugi subpage blocks

This commit is contained in:
MichalC
2026-09-01 16:12:05 +02:00
parent cccda44fcd
commit ce3c086f4c
11 changed files with 602 additions and 69 deletions
+126
View File
@@ -0,0 +1,126 @@
import React from 'react'
import { CheckCircle2, XCircle, AlertTriangle, ShieldAlert } from 'lucide-react'
export interface EligibilityProps {
title: string
subtitle?: string | null
eligibleTitle: string
eligibleItems?: Array<{ id?: string | null; text: string }> | null
limitationsTitle: string
limitationsText: string
excludedTitle: string
excludedItems?: Array<{ id?: string | null; text: string }> | null
legalNotice?: string | null
}
export const EligibilityBlock: React.FC<EligibilityProps> = ({
title,
subtitle,
eligibleTitle,
eligibleItems,
limitationsTitle,
limitationsText,
excludedTitle,
excludedItems,
legalNotice,
}) => {
return (
<section className="py-12 md:py-20 bg-card/60 border-y border-border">
<div className="container-premium">
<div className="max-w-3xl mb-12 text-center md:text-left">
<div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-accent/10 text-accent text-xs font-semibold uppercase tracking-wider mb-4">
<ShieldAlert size={16} />
<span>Zakres uprawnień doradczych</span>
</div>
<h2 className="font-heading text-2xl sm:text-3xl md:text-4xl font-bold text-foreground tracking-tight">
{title}
</h2>
{subtitle && (
<p className="mt-3 text-base sm:text-lg text-foreground/70 leading-relaxed">
{subtitle}
</p>
)}
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 items-start">
{/* Kolumna 1: Kto się kwalifikuje */}
<div className="bg-card border-2 border-border rounded-2xl p-6 sm:p-8 shadow-sm">
<div className="flex items-center gap-3 mb-6 pb-4 border-b border-border">
<div className="w-10 h-10 rounded-xl bg-accent/10 text-accent flex items-center justify-center shrink-0">
<CheckCircle2 size={24} />
</div>
<h3 className="font-heading text-xl font-bold text-foreground">
{eligibleTitle}
</h3>
</div>
<ul className="space-y-4">
{(eligibleItems || []).map((item, idx) => (
<li key={item.id || idx} className="flex items-start gap-3">
<CheckCircle2
size={20}
className="text-accent shrink-0 mt-0.5"
/>
<span className="text-foreground/90 font-medium leading-relaxed">
{item.text}
</span>
</li>
))}
</ul>
</div>
{/* Kolumna 2: Ograniczenia i Wykluczenia */}
<div className="flex flex-col gap-6">
{/* Limit przychodów */}
<div className="bg-card border border-border rounded-2xl p-6 sm:p-8 shadow-sm">
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 rounded-xl bg-amber-500/10 text-amber-500 flex items-center justify-center shrink-0">
<AlertTriangle size={22} />
</div>
<h3 className="font-heading text-lg font-bold text-foreground">
{limitationsTitle}
</h3>
</div>
<p className="text-foreground/80 leading-relaxed text-sm sm:text-base">
{limitationsText}
</p>
</div>
{/* Wykluczenia - świadomy wyjątek kolorystyczny (czerwony) dla bezpieczeństwa prawnego klienta */}
{/* Świadomy wyjątek: czerwony kolor ostrzegawczy dla wykluczeń prawnych (poza paletą motywu) */}
<div className="bg-red-500/5 border-2 border-red-500/30 rounded-2xl p-6 sm:p-8 shadow-sm">
<div className="flex items-center gap-3 mb-4 pb-3 border-b border-red-500/20">
<div className="w-10 h-10 rounded-xl bg-red-500/15 text-red-600 flex items-center justify-center shrink-0">
<XCircle size={22} />
</div>
<h3 className="font-heading text-lg font-bold text-red-950 dark:text-red-200">
{excludedTitle}
</h3>
</div>
<ul className="space-y-3">
{(excludedItems || []).map((item, idx) => (
<li key={item.id || idx} className="flex items-start gap-3">
<XCircle
size={18}
className="text-red-500 shrink-0 mt-0.5"
/>
<span className="text-foreground/90 font-medium text-sm sm:text-base leading-relaxed">
{item.text}
</span>
</li>
))}
</ul>
</div>
</div>
</div>
{legalNotice && (
<div className="mt-10 p-5 rounded-xl bg-background border border-border text-xs sm:text-sm text-foreground/60 leading-relaxed">
<p>{legalNotice}</p>
</div>
)}
</div>
</section>
)
}
+28
View File
@@ -0,0 +1,28 @@
import type { Block } from 'payload'
export const Eligibility: Block = {
slug: 'eligibility',
fields: [
{ name: 'title', type: 'text', required: true },
{ name: 'subtitle', type: 'textarea' },
{ name: 'eligibleTitle', type: 'text', required: true },
{
name: 'eligibleItems',
type: 'array',
minRows: 1,
required: true,
fields: [{ name: 'text', type: 'text', required: true }],
},
{ name: 'limitationsTitle', type: 'text', required: true },
{ name: 'limitationsText', type: 'textarea', required: true },
{ name: 'excludedTitle', type: 'text', required: true },
{
name: 'excludedItems',
type: 'array',
minRows: 1,
required: true,
fields: [{ name: 'text', type: 'text', required: true }],
},
{ name: 'legalNotice', type: 'textarea' },
],
}
+23
View File
@@ -0,0 +1,23 @@
import React from 'react'
export interface PageHeaderProps {
title: string
subtitle?: string | null
}
export const PageHeaderBlock: React.FC<PageHeaderProps> = ({ title, subtitle }) => {
return (
<section className="bg-card border-b border-border py-16 md:py-20 mb-12">
<div className="container-premium text-center max-w-4xl mx-auto">
<h1 className="font-heading text-3xl sm:text-4xl md:text-5xl font-extrabold text-foreground tracking-tight leading-tight">
{title}
</h1>
{subtitle && (
<p className="mt-4 text-lg md:text-xl text-foreground/70 leading-relaxed max-w-2xl mx-auto">
{subtitle}
</p>
)}
</div>
</section>
)
}
+9
View File
@@ -0,0 +1,9 @@
import type { Block } from 'payload'
export const PageHeader: Block = {
slug: 'pageHeader',
fields: [
{ name: 'title', type: 'text', required: true },
{ name: 'subtitle', type: 'textarea' },
],
}
+7
View File
@@ -11,7 +11,14 @@ import { TestimonialsBlock } from './Testimonials/Component'
import { FAQTeaserBlock } from './FAQTeaser/Component'
import { FinalCTABlock } from './FinalCTA/Component'
import { PageHeaderBlock } from './PageHeader/Component'
import { ServicesDetailedBlock } from './ServicesDetailed/Component'
import { EligibilityBlock } from './Eligibility/Component'
const blockComponents = {
pageHeader: PageHeaderBlock,
servicesDetailed: ServicesDetailedBlock,
eligibility: EligibilityBlock,
hero: HeroBlock,
trustBar: TrustBarBlock,
uniqueValue: UniqueValueBlock,
+80
View File
@@ -0,0 +1,80 @@
import React from 'react'
import {
FileText,
Calculator,
ShieldCheck,
Scale,
HelpCircle,
Users,
Coins,
LucideIcon,
} from 'lucide-react'
const iconMap: Record<string, LucideIcon> = {
fileText: FileText,
calculator: Calculator,
shieldCheck: ShieldCheck,
scale: Scale,
helpCircle: HelpCircle,
users: Users,
coins: Coins,
}
export interface ServicesDetailedProps {
title: string
subtitle?: string | null
services?: Array<{
id?: string | null
title: string
description: string
icon?: string | null
}> | null
}
export const ServicesDetailedBlock: React.FC<ServicesDetailedProps> = ({
title,
subtitle,
services,
}) => {
return (
<section className="py-12 md:py-16">
<div className="container-premium">
<div className="max-w-3xl mb-12">
<h2 className="font-heading text-2xl sm:text-3xl md:text-4xl font-bold text-foreground tracking-tight">
{title}
</h2>
{subtitle && (
<p className="mt-3 text-base sm:text-lg text-foreground/70 leading-relaxed">
{subtitle}
</p>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-8">
{(services || []).map((service, index) => {
const Icon = (service.icon && iconMap[service.icon]) || FileText
return (
<div
key={service.id || index}
className="bg-card border border-border rounded-2xl p-6 md:p-8 flex flex-col justify-between hover:border-accent/50 hover:shadow-md transition-all duration-200"
>
<div>
<div className="w-12 h-12 rounded-xl bg-accent/10 text-accent flex items-center justify-center mb-6">
<Icon size={24} />
</div>
<h3 className="font-heading text-xl font-bold text-foreground mb-3 leading-snug">
{service.title}
</h3>
<p className="text-foreground/75 text-sm sm:text-base leading-relaxed">
{service.description}
</p>
</div>
</div>
)
})}
</div>
</div>
</section>
)
}
+33
View File
@@ -0,0 +1,33 @@
import type { Block } from 'payload'
export const ServicesDetailed: Block = {
slug: 'servicesDetailed',
fields: [
{ name: 'title', type: 'text', required: true },
{ name: 'subtitle', type: 'textarea' },
{
name: 'services',
type: 'array',
minRows: 1,
required: true,
fields: [
{ name: 'title', type: 'text', required: true },
{ name: 'description', type: 'textarea', required: true },
{
name: 'icon',
type: 'select',
options: [
'fileText',
'calculator',
'shieldCheck',
'scale',
'helpCircle',
'users',
'coins',
],
defaultValue: 'fileText',
},
],
},
],
}