-
+ {nameLabel && (
+
+ )}
-
+ {phoneInputLabel && (
+
+ )}
@@ -161,73 +202,84 @@ export function FormBlockComponent(props: FormBlockProps) {
-
+ {messageLabel && (
+
+ )}
{/* Custom Styled Kärcher Checkbox */}
-
-
diff --git a/src/blocks/Form/config.ts b/src/blocks/Form/config.ts
index d2695ee..58b5000 100644
--- a/src/blocks/Form/config.ts
+++ b/src/blocks/Form/config.ts
@@ -28,16 +28,64 @@ export const FormBlock: Block = {
type: 'text',
localized: true,
},
+ {
+ name: 'directPhoneLabel',
+ type: 'text',
+ localized: true,
+ defaultValue: 'Telefon bezpośredni',
+ },
{
name: 'email',
type: 'text',
localized: true,
},
+ {
+ name: 'emailContactLabel',
+ type: 'text',
+ localized: true,
+ defaultValue: 'Kontakt e-mail',
+ },
{
name: 'workingHours',
type: 'text',
localized: true,
},
+ {
+ name: 'workingHoursLabel',
+ type: 'text',
+ localized: true,
+ defaultValue: 'Godziny pracy',
+ },
+ {
+ name: 'nameLabel',
+ type: 'text',
+ localized: true,
+ defaultValue: 'Imię i Nazwisko *',
+ },
+ {
+ name: 'phoneInputLabel',
+ type: 'text',
+ localized: true,
+ defaultValue: 'Numer Telefonu *',
+ },
+ {
+ name: 'locationLabel',
+ type: 'text',
+ localized: true,
+ defaultValue: 'Miejscowość',
+ },
+ {
+ name: 'areaLabel',
+ type: 'text',
+ localized: true,
+ defaultValue: 'Szacowany Metraż (m²)',
+ },
+ {
+ name: 'messageLabel',
+ type: 'text',
+ localized: true,
+ defaultValue: 'Zakres prac / stan nawierzchni',
+ },
{
name: 'submitButtonText',
type: 'text',
@@ -48,7 +96,21 @@ export const FormBlock: Block = {
name: 'consentText',
type: 'textarea',
localized: true,
- defaultValue: 'Wyrażam zgodę na przetwarzanie moich danych osobowych w celu kontaktu i przygotowania oferty zgodnie z Polityką Prywatności.',
+ defaultValue:
+ 'Wyrażam zgodę na przetwarzanie moich danych osobowych w celu kontaktu i przygotowania oferty zgodnie z Polityką Prywatności.',
+ },
+ {
+ name: 'successHeading',
+ type: 'text',
+ localized: true,
+ defaultValue: 'Zgłoszenie zostało wysłane',
+ },
+ {
+ name: 'successMessage',
+ type: 'textarea',
+ localized: true,
+ defaultValue:
+ 'Nasz doradca techniczny skontaktuje się z Państwem telefonicznie w celu przygotowania dokładnej wyceny.',
},
],
}
diff --git a/src/blocks/Hero/Component.tsx b/src/blocks/Hero/Component.tsx
index df3c33e..986f715 100644
--- a/src/blocks/Hero/Component.tsx
+++ b/src/blocks/Hero/Component.tsx
@@ -25,19 +25,18 @@ export function HeroBlockComponent(props: HeroBlockProps) {
subheading,
trustBadges,
ctaPhone,
- ctaPhoneLabel = 'Zadzwoń po bezpłatną wycenę',
- ctaSecondaryLabel = 'Zobacz cennik za m²',
- ctaSecondaryUrl = '#cennik',
+ ctaPhoneLabel,
+ ctaSecondaryLabel,
+ ctaSecondaryUrl,
} = props
return (
- {/* Hero Banner Section with Optimized Next.js LCP Image */}
+ {/* Hero Banner Section with Next.js Image */}
- {/* Next.js LCP High Priority Image */}
- {/* Compact Architectural Content Card */}
+ {/* Architectural Content Card */}
{badge && (
@@ -68,32 +67,34 @@ export function HeroBlockComponent(props: HeroBlockProps) {
)}
{/* Action Buttons */}
-
+ {(ctaPhone || ctaSecondaryLabel) && (
+
+ )}
- {/* Full-Width Horizontal Feature Strip Directly Under Hero with Stagger */}
+ {/* Full-Width Horizontal Feature Strip Directly Under Hero */}
{trustBadges && trustBadges.length > 0 && (
diff --git a/src/blocks/Hero/config.ts b/src/blocks/Hero/config.ts
index 91c5b3a..ae3449b 100644
--- a/src/blocks/Hero/config.ts
+++ b/src/blocks/Hero/config.ts
@@ -80,6 +80,14 @@ export const HeroBlock: Block = {
description: 'Strona docelowa drugiego przycisku',
},
},
+ {
+ name: 'ctaSecondaryUrl',
+ type: 'text',
+ localized: true,
+ admin: {
+ description: 'URL lub kotwica drugiego przycisku (np. #cennik)',
+ },
+ },
{
name: 'backgroundImage',
type: 'upload',
diff --git a/src/blocks/PricingTable/Component.tsx b/src/blocks/PricingTable/Component.tsx
index 896db2a..2911941 100644
--- a/src/blocks/PricingTable/Component.tsx
+++ b/src/blocks/PricingTable/Component.tsx
@@ -12,6 +12,7 @@ interface Package {
description?: string
features?: Feature[]
isPopular?: boolean
+ popularBadgeLabel?: string
ctaLabel?: string
ctaUrl?: string
}
@@ -57,9 +58,9 @@ export function PricingTableBlockComponent(props: PricingTableBlockProps) {
pkg.isPopular ? 'border-t-8 border-t-[#FFED00] shadow-md' : ''
}`}
>
- {pkg.isPopular && (
+ {pkg.isPopular && pkg.popularBadgeLabel && (
- Polecany Pakiet
+ {pkg.popularBadgeLabel}
)}
diff --git a/src/blocks/PricingTable/config.ts b/src/blocks/PricingTable/config.ts
index bcfffaf..66cee2f 100644
--- a/src/blocks/PricingTable/config.ts
+++ b/src/blocks/PricingTable/config.ts
@@ -62,6 +62,12 @@ export const PricingTableBlock: Block = {
type: 'checkbox',
defaultValue: false,
},
+ {
+ name: 'popularBadgeLabel',
+ type: 'text',
+ localized: true,
+ defaultValue: 'Polecany Pakiet',
+ },
{
name: 'features',
type: 'array',
diff --git a/src/components/CustomCookieBanner.tsx b/src/components/CustomCookieBanner.tsx
index ee5eeab..5b302e1 100644
--- a/src/components/CustomCookieBanner.tsx
+++ b/src/components/CustomCookieBanner.tsx
@@ -16,6 +16,17 @@ export function CustomCookieBanner() {
setChoices(state)
}, [state])
+ // Lock background page scroll while settings modal is open
+ useEffect(() => {
+ if (showSettings) {
+ const originalOverflow = document.body.style.overflow
+ document.body.style.overflow = 'hidden'
+ return () => {
+ document.body.style.overflow = originalOverflow
+ }
+ }
+ }, [showSettings])
+
if (decided) {
return null
}
@@ -101,14 +112,21 @@ export function CustomCookieBanner() {
)}
- {/* 2. Modal Window for Cookie Settings (Centered & Scrollable) */}
+ {/* 2. Modal Window for Cookie Settings (Scroll-locked backdrop & isolated modal scrolling) */}
{showSettings && (
-
+
{
+ if (e.target === e.currentTarget) setShowSettings(false)
+ }}
+ onWheel={(e) => e.stopPropagation()}
+ >
e.stopPropagation()}
>
{/* Modal Header */}
@@ -131,7 +149,7 @@ export function CustomCookieBanner() {
{/* Scrollable Categories List */}
-
+
{CONSENT_CATEGORIES.map((cat) => {
const isChecked = cat === 'necessary' ? true : !!choices[cat]
const isDisabled = cat === 'necessary'
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
index ab0f2ee..0df8149 100644
--- a/src/components/Footer.tsx
+++ b/src/components/Footer.tsx
@@ -17,10 +17,12 @@ interface FooterProps {
phone?: string | null
email?: string | null
items?: FooterNavItem[]
- privacyHref?: string
- privacyLabel?: string
- cookieHref?: string
- cookieLabel?: string
+ privacyHref?: string | null
+ privacyLabel?: string | null
+ cookieHref?: string | null
+ cookieLabel?: string | null
+ navTitle?: string | null
+ contactTitle?: string | null
}
export function Footer({
@@ -33,10 +35,12 @@ export function Footer({
phone,
email,
items,
- privacyHref = '/polityka-prywatnosci',
- privacyLabel = 'Polityka Prywatności',
- cookieHref = '/polityka-cookies',
- cookieLabel = 'Polityka Cookies',
+ privacyHref,
+ privacyLabel,
+ cookieHref,
+ cookieLabel,
+ navTitle,
+ contactTitle,
}: FooterProps) {
return (
{/* Navigation Links */}
{items && items.length > 0 && (
-
- Nawigacja
-
+ {navTitle && (
+
+ {navTitle}
+
+ )}
{items.map((item, idx) => (
-
@@ -84,54 +92,58 @@ export function Footer({
)}
{/* Contact Details */}
-
-
- Kontakt
+ {(phone || email || address || city) && (
+
+ {contactTitle && (
+
+ {contactTitle}
+
+ )}
+
+ {phone && (
+ -
+
+
+ {phone}
+
+
+ )}
+ {email && (
+ -
+
+
+ {email}
+
+
+ )}
+ {(address || city) && (
+ -
+
+
+ {address}
+ {address && city ? ', ' : ''}
+ {city}
+
+
+ )}
+
-
- {phone && (
- -
-
-
- {phone}
-
-
- )}
- {email && (
- -
-
-
- {email}
-
-
- )}
- {(address || city) && (
- -
-
-
- {address}
- {address && city ? ', ' : ''}
- {city}
-
-
- )}
-
-
+ )}
{/* Bottom Bar with Privacy & Cookie Links */}
- © {new Date().getFullYear()} {companyName}. Wszelkie prawa zastrzeżone.
+ © {new Date().getFullYear()} {companyName}
- {privacyHref && (
+ {privacyHref && privacyLabel && (
{privacyLabel}
)}
- {cookieHref && (
+ {cookieHref && cookieLabel && (
{cookieLabel}
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index 934e13b..cbf7c16 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -17,8 +17,9 @@ interface HeaderProps {
phone?: string | null
homeHref?: string
currentLocale?: string
- contactHref?: string
- contactLabel?: string
+ contactHref?: string | null
+ contactLabel?: string | null
+ phoneLabel?: string | null
}
export function Header({
@@ -28,8 +29,9 @@ export function Header({
phone,
homeHref = '/',
currentLocale = 'pl',
- contactHref = '/kontakt',
- contactLabel = 'KONTAKT',
+ contactHref,
+ contactLabel,
+ phoneLabel,
}: HeaderProps) {
const pathname = usePathname() || ''
const [langDropdownOpen, setLangDropdownOpen] = useState(false)
@@ -61,8 +63,8 @@ export function Header({
)
const languages = [
- { code: 'pl', label: 'PL - Polski' },
- { code: 'en', label: 'UK - English' },
+ { code: 'pl', label: 'PL' },
+ { code: 'en', label: 'UK' },
]
const activeLang = languages.find((l) => l.code === currentLocale) || languages[0]
@@ -70,29 +72,35 @@ export function Header({
return (
{/* Top Black Utility Bar */}
-
-
-
-
-
- {tagline || 'Profesjonalne Mycie i Renowacja Nawierzchni Brukowych'}
-
+ {(tagline || phone) && (
+
+
+ {tagline && (
+
+
+
+ {tagline}
+
+
+ )}
+ {phone && (
+
+ {phoneLabel && (
+
+ {phoneLabel}
+
+ )}
+
+ {phone}
+
+
+ )}
- {phone && (
-
- )}
-
+ )}
{/* Main Navigation Bar */}
@@ -106,13 +114,15 @@ export function Header({
{companyName}
)}
-
- Czyszczenie Kostki Brukowej
-
+ {tagline && (
+
+ {tagline}
+
+ )}
- {/* Navigation Items (Without duplicated Kontakt) */}
+ {/* Navigation Items */}
{filteredNavItems && filteredNavItems.length > 0 && (