feat(kontakt): add ContactInfo block and configure /kontakt page
This commit is contained in:
@@ -0,0 +1,218 @@
|
|||||||
|
'use client'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import { usePathname } from 'next/navigation'
|
||||||
|
import { MapPin, Phone, Mail, Clock, MessageCircle, Send } from 'lucide-react'
|
||||||
|
import type { Page } from '../../payload-types'
|
||||||
|
|
||||||
|
type Props = Extract<NonNullable<Page['layout']>[0], { blockType: 'contactInfo' }>
|
||||||
|
|
||||||
|
export const ContactInfoBlock: React.FC<Props> = ({
|
||||||
|
address = 'DO POTWIERDZENIA PRZEZ KLIENTA',
|
||||||
|
phone = '+49 123 456 789',
|
||||||
|
email = '[email protected]',
|
||||||
|
hours = 'Pon-Pt: 9:00 - 17:00\nPo wcześniejszym umówieniu',
|
||||||
|
}) => {
|
||||||
|
const pathname = usePathname()
|
||||||
|
const isDe = pathname?.startsWith('/de')
|
||||||
|
|
||||||
|
const [formData, setFormData] = useState({ name: '', email: '', message: '' })
|
||||||
|
const [submitted, setSubmitted] = useState(false)
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
// TODO: Podłączyć logikę wysyłki formularza przez backend API (np. Resend / SMTP / Payload custom endpoint)
|
||||||
|
setSubmitted(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="py-12 md:py-16 bg-background">
|
||||||
|
<div className="container-premium">
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-16 items-start">
|
||||||
|
|
||||||
|
{/* Kolumna 1: Dane kontaktowe + WhatsApp CTA (Główna opcja) */}
|
||||||
|
<div className="lg:col-span-5 flex flex-col gap-8">
|
||||||
|
<div className="bg-card border border-border p-8 rounded-2xl shadow-sm flex flex-col gap-6">
|
||||||
|
<h2 className="font-heading text-2xl font-bold text-foreground">
|
||||||
|
{isDe ? 'Direkter Kontakt' : 'Bezpośredni kontakt'}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<ul className="flex flex-col gap-5 text-foreground/80 text-base">
|
||||||
|
{address && (
|
||||||
|
<li className="flex items-start gap-4">
|
||||||
|
<div className="w-10 h-10 rounded-lg bg-accent/10 flex items-center justify-center text-accent shrink-0 mt-0.5">
|
||||||
|
<MapPin size={20} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-xs text-foreground/50 uppercase tracking-wider font-semibold block mb-0.5">
|
||||||
|
{isDe ? 'Adresse' : 'Adres'}
|
||||||
|
</span>
|
||||||
|
<span className="font-medium whitespace-pre-line text-foreground">{address}</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{phone && (
|
||||||
|
<li className="flex items-start gap-4">
|
||||||
|
<div className="w-10 h-10 rounded-lg bg-accent/10 flex items-center justify-center text-accent shrink-0 mt-0.5">
|
||||||
|
<Phone size={20} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-xs text-foreground/50 uppercase tracking-wider font-semibold block mb-0.5">
|
||||||
|
{isDe ? 'Telefon' : 'Telefon'}
|
||||||
|
</span>
|
||||||
|
<a href={`tel:${phone.replace(/\s+/g, '')}`} className="font-medium hover:text-accent transition-colors text-foreground">
|
||||||
|
{phone}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{email && (
|
||||||
|
<li className="flex items-start gap-4">
|
||||||
|
<div className="w-10 h-10 rounded-lg bg-accent/10 flex items-center justify-center text-accent shrink-0 mt-0.5">
|
||||||
|
<Mail size={20} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-xs text-foreground/50 uppercase tracking-wider font-semibold block mb-0.5">
|
||||||
|
{isDe ? 'E-Mail' : 'E-mail'}
|
||||||
|
</span>
|
||||||
|
<a href={`mailto:${email}`} className="font-medium hover:text-accent transition-colors text-foreground">
|
||||||
|
{email}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hours && (
|
||||||
|
<li className="flex items-start gap-4">
|
||||||
|
<div className="w-10 h-10 rounded-lg bg-accent/10 flex items-center justify-center text-accent shrink-0 mt-0.5">
|
||||||
|
<Clock size={20} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-xs text-foreground/50 uppercase tracking-wider font-semibold block mb-0.5">
|
||||||
|
{isDe ? 'Öffnungszeiten' : 'Godziny pracy'}
|
||||||
|
</span>
|
||||||
|
<span className="font-medium whitespace-pre-line text-foreground">{hours}</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Główne CTA: WhatsApp */}
|
||||||
|
<div className="p-8 bg-card border-2 border-green-500/20 rounded-2xl flex flex-col gap-4">
|
||||||
|
<span className="inline-block text-xs font-bold uppercase tracking-widest text-green-600 dark:text-green-400">
|
||||||
|
{isDe ? 'Empfohlen & Am schnellsten' : 'Zalecany i najszybszy kontakt'}
|
||||||
|
</span>
|
||||||
|
<h3 className="font-heading text-xl font-bold text-foreground">
|
||||||
|
{isDe ? 'Schreiben Sie auf WhatsApp' : 'Napisz na WhatsApp'}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-foreground/70 leading-relaxed">
|
||||||
|
{isDe
|
||||||
|
? 'Erhalten Sie schnelle Antworten auf Ihre Fragen direkt von Dipl.-Ing. Janusz Kędzierski.'
|
||||||
|
: 'Najszybsza forma kontaktu. Prześlij zapytanie lub dokumenty i uzyskaj szybką odpowiedź.'}
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href={process.env.NEXT_PUBLIC_WHATSAPP_LINK || '#'}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="mt-2 inline-flex items-center justify-center gap-3 px-6 py-4 bg-green-500 hover:bg-green-600 text-white font-bold text-base rounded-[var(--radius-button)] transition-colors shadow-sm"
|
||||||
|
>
|
||||||
|
<MessageCircle size={22} />
|
||||||
|
<span>{isDe ? 'WhatsApp Chat starten' : 'Napisz na WhatsApp'}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Kolumna 2: Prosty formularz kontaktowy (Drugorzędna opcja) */}
|
||||||
|
<div className="lg:col-span-7 bg-card border border-border p-8 md:p-10 rounded-2xl shadow-sm">
|
||||||
|
<div className="mb-8">
|
||||||
|
<span className="text-xs uppercase tracking-widest text-foreground/50 font-semibold block mb-2">
|
||||||
|
{isDe ? 'Nachricht senden' : 'Napisz wiadomość'}
|
||||||
|
</span>
|
||||||
|
<h2 className="font-heading text-2xl md:text-3xl font-bold text-foreground">
|
||||||
|
{isDe ? 'Kontaktformular' : 'Formularz kontaktowy'}
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm md:text-base text-foreground/70 mt-2 leading-relaxed">
|
||||||
|
{isDe
|
||||||
|
? 'Bevorzugen Sie eine schriftliche Nachricht? Füllen Sie das folgende Formular aus — wir antworten in der Regel innerhalb von 24 Stunden.'
|
||||||
|
: 'Wolisz kontakt tradycyjny? Wypełnij formularz, a odpowiemy najszybciej jak to możliwe.'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{submitted ? (
|
||||||
|
<div className="p-6 bg-accent/10 border border-accent/20 rounded-xl text-center">
|
||||||
|
<h3 className="font-heading font-bold text-lg text-foreground mb-2">
|
||||||
|
{isDe ? 'Vielen Dank für Ihre Nachricht!' : 'Dziękujemy za wiadomość!'}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-foreground/70">
|
||||||
|
{isDe
|
||||||
|
? 'Ihre Anfrage wurde entgegengenommen. Wir melden uns in Kürze.'
|
||||||
|
: 'Twoje zapytanie zostało zarejestrowane. Skontaktujemy się wkrótce.'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<form onSubmit={handleSubmit} className="flex flex-col gap-6">
|
||||||
|
<div>
|
||||||
|
<label htmlFor="name" className="block text-sm font-semibold text-foreground mb-2">
|
||||||
|
{isDe ? 'Vor- und Nachname' : 'Imię i nazwisko'} <span className="text-accent">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="name"
|
||||||
|
required
|
||||||
|
value={formData.name}
|
||||||
|
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||||
|
placeholder={isDe ? 'z. B. Jan Kowalski' : 'np. Jan Kowalski'}
|
||||||
|
className="w-full px-4 py-3 bg-background border border-border rounded-lg text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent transition-all"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="email" className="block text-sm font-semibold text-foreground mb-2">
|
||||||
|
{isDe ? 'E-Mail-Adresse' : 'Adres e-mail'} <span className="text-accent">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
required
|
||||||
|
value={formData.email}
|
||||||
|
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||||
|
placeholder="[email protected]"
|
||||||
|
className="w-full px-4 py-3 bg-background border border-border rounded-lg text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent transition-all"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="message" className="block text-sm font-semibold text-foreground mb-2">
|
||||||
|
{isDe ? 'Ihre Nachricht' : 'Treść wiadomości'} <span className="text-accent">*</span>
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="message"
|
||||||
|
required
|
||||||
|
rows={5}
|
||||||
|
value={formData.message}
|
||||||
|
onChange={(e) => setFormData({ ...formData, message: e.target.value })}
|
||||||
|
placeholder={isDe ? 'Wie können wir Ihnen helfen?' : 'W czym możemy Ci pomóc? Opisz krótko swoją sprawę...'}
|
||||||
|
className="w-full px-4 py-3 bg-background border border-border rounded-lg text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent transition-all resize-y"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="pt-2">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="w-full sm:w-auto px-8 py-4 bg-accent hover:bg-accent-hover text-accent-foreground font-bold text-base rounded-[var(--radius-button)] transition-colors shadow-sm flex items-center justify-center gap-2 cursor-pointer"
|
||||||
|
>
|
||||||
|
<Send size={18} />
|
||||||
|
<span>{isDe ? 'Nachricht senden' : 'Wyślij wiadomość'}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import type { Block } from 'payload'
|
||||||
|
|
||||||
|
export const ContactInfo: Block = {
|
||||||
|
slug: 'contactInfo',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'address',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: 'DO POTWIERDZENIA PRZEZ KLIENTA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'phone',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '+49 123 456 789',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'email',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '[email protected]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'hours',
|
||||||
|
type: 'textarea',
|
||||||
|
defaultValue: 'Pon-Pt: 9:00 - 17:00\nPo wcześniejszym umówieniu',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -12,12 +12,14 @@ import { FAQTeaserBlock } from './FAQTeaser/Component'
|
|||||||
import { FinalCTABlock } from './FinalCTA/Component'
|
import { FinalCTABlock } from './FinalCTA/Component'
|
||||||
|
|
||||||
import { PageHeaderBlock } from './PageHeader/Component'
|
import { PageHeaderBlock } from './PageHeader/Component'
|
||||||
|
import { ContactInfoBlock } from './ContactInfo/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'
|
||||||
|
|
||||||
const blockComponents = {
|
const blockComponents = {
|
||||||
pageHeader: PageHeaderBlock,
|
pageHeader: PageHeaderBlock,
|
||||||
|
contactInfo: ContactInfoBlock,
|
||||||
servicesDetailed: ServicesDetailedBlock,
|
servicesDetailed: ServicesDetailedBlock,
|
||||||
eligibility: EligibilityBlock,
|
eligibility: EligibilityBlock,
|
||||||
hero: HeroBlock,
|
hero: HeroBlock,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { FAQTeaser } from '../blocks/FAQTeaser/config'
|
|||||||
import { FinalCTA } from '../blocks/FinalCTA/config'
|
import { FinalCTA } from '../blocks/FinalCTA/config'
|
||||||
|
|
||||||
import { PageHeader } from '../blocks/PageHeader/config'
|
import { PageHeader } from '../blocks/PageHeader/config'
|
||||||
|
import { ContactInfo } from '../blocks/ContactInfo/config'
|
||||||
import { ServicesDetailed } from '../blocks/ServicesDetailed/config'
|
import { ServicesDetailed } from '../blocks/ServicesDetailed/config'
|
||||||
import { Eligibility } from '../blocks/Eligibility/config'
|
import { Eligibility } from '../blocks/Eligibility/config'
|
||||||
import { Stats } from '../blocks/Stats/config'
|
import { Stats } from '../blocks/Stats/config'
|
||||||
@@ -38,6 +39,7 @@ export const Pages: CollectionConfig = {
|
|||||||
minRows: 1,
|
minRows: 1,
|
||||||
blocks: [
|
blocks: [
|
||||||
PageHeader,
|
PageHeader,
|
||||||
|
ContactInfo,
|
||||||
ServicesDetailed,
|
ServicesDetailed,
|
||||||
Eligibility,
|
Eligibility,
|
||||||
Hero,
|
Hero,
|
||||||
|
|||||||
@@ -220,6 +220,15 @@ export interface Page {
|
|||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'pageHeader';
|
blockType: 'pageHeader';
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
address?: string | null;
|
||||||
|
phone?: string | null;
|
||||||
|
email?: string | null;
|
||||||
|
hours?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'contactInfo';
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
title: string;
|
title: string;
|
||||||
subtitle?: string | null;
|
subtitle?: string | null;
|
||||||
@@ -582,6 +591,16 @@ export interface PagesSelect<T extends boolean = true> {
|
|||||||
id?: T;
|
id?: T;
|
||||||
blockName?: T;
|
blockName?: T;
|
||||||
};
|
};
|
||||||
|
contactInfo?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
address?: T;
|
||||||
|
phone?: T;
|
||||||
|
email?: T;
|
||||||
|
hours?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
servicesDetailed?:
|
servicesDetailed?:
|
||||||
| T
|
| T
|
||||||
| {
|
| {
|
||||||
|
|||||||
Reference in New Issue
Block a user