fix: localize Pages layout and implement /uslugi subpage blocks
This commit is contained in:
+185
-12
@@ -164,6 +164,164 @@ const homeLayoutDe = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const uslugiLayoutPl = [
|
||||||
|
{
|
||||||
|
blockType: 'pageHeader',
|
||||||
|
title: 'Kompleksowe rozliczenia podatkowe dla Polaków w Niemczech',
|
||||||
|
subtitle: 'Profesjonalne sporządzanie deklaracji, weryfikacja decyzji podatkowych i bieżące wsparcie w kontaktach z niemieckim urzędem skarbowym (Finanzamt).',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockType: 'servicesDetailed',
|
||||||
|
title: 'Czym się zajmuję',
|
||||||
|
subtitle: 'Zapewniam rzetelne i kompleksowe wsparcie na każdym etapie rozliczenia podatku dochodowego w Niemczech.',
|
||||||
|
services: [
|
||||||
|
{
|
||||||
|
title: 'Sporządzenie deklaracji podatkowej (Einkommensteuererklärung)',
|
||||||
|
description: 'Przygotowujemy kompletne zeznanie podatkowe, uwzględniając wszystkie przysługujące Ci ulgi, koszty uzyskania przychodu oraz odliczenia. Dbamy o maksymalizację Twojego zwrotu i pełną zgodność z niemieckim prawem.',
|
||||||
|
icon: 'fileText',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Wstępne obliczenie przewidywanego zwrotu',
|
||||||
|
description: 'Przed wysłaniem dokumentów do urzędu wyliczamy dokładną kwotę spodziewanego zwrotu lub ewentualnej dopłaty. Dzięki temu wiesz dokładnie, czego się spodziewać jeszcze przed wydaniem decyzji przez Finanzamt.',
|
||||||
|
icon: 'calculator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Kontrola decyzji podatkowej (Steuerbescheid)',
|
||||||
|
description: 'Szczegółowo weryfikujemy otrzymaną decyzję z urzędu skarbowego pod kątem ewentualnych błędów urzędników. Sprawdzamy, czy urząd uwzględnił wszystkie zgłoszone koszty i ulgi.',
|
||||||
|
icon: 'shieldCheck',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Składanie odwołań (Einspruch)',
|
||||||
|
description: 'W przypadku niekorzystnej lub błędnej decyzji urzędu sporządzamy oficjalne odwołanie i reprezentujemy Cię w postępowaniu wyjaśniającym, skutecznie dochodząc Twoich praw.',
|
||||||
|
icon: 'scale',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Całoroczne doradztwo podatkowe',
|
||||||
|
description: 'Pozostajemy do Twojej dyspozycji przez cały rok podatkowy. Odpowiadamy na pytania, pomagamy w bieżącej korespondencji z urzędem i doradzamy przy zmianie sytuacji życiowej.',
|
||||||
|
icon: 'helpCircle',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Doradztwo przy wyborze klasy podatkowej (Steuerklasse)',
|
||||||
|
description: 'Doradzamy optymalny dobór klas podatkowych dla małżeństw i osób pracujących w Niemczech. Pomagamy wybrać wariant (np. III/V lub IV/IV), który przyniesie największe korzyści finansowe.',
|
||||||
|
icon: 'users',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Ulgi, dopłaty i odliczenia (Kindergeld, dojazdy, podwójne gospodarstwo)',
|
||||||
|
description: 'Pomagamy w uzyskaniu Kindergeld, Riester-Rente oraz prawidłowym odliczeniu kosztów podwójnego gospodarstwa domowego (doppelter Haushalt) i dojazdów do pracy (Pendlerpauschale).',
|
||||||
|
icon: 'coins',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockType: 'eligibility',
|
||||||
|
title: 'Dla kogo — zakres uprawnień doradczych wg §4 Nr. 11 StBerG',
|
||||||
|
subtitle: 'Jako punkt doradczy b.b.h. Lohnsteuerhilfeverein e.V. działamy ściśle w ramach ustawowych uprawnień dla stowarzyszeń pomocy podatkowej.',
|
||||||
|
eligibleTitle: 'Kto może skorzystać z naszej pomocy',
|
||||||
|
eligibleItems: [
|
||||||
|
{ text: 'Pracownicy zatrudnieni na umowę o pracę (Arbeitnehmer) oraz pracownicy tymczasowi' },
|
||||||
|
{ text: 'Urzędnicy państwowi (Beamte)' },
|
||||||
|
{ text: 'Emeryci i renciści (Rentner und Pensionäre)' },
|
||||||
|
{ text: 'Osoby bezrobotne oraz pobierające zasiłki socjalne' },
|
||||||
|
{ text: 'Odbiorcy świadczeń rodzinnych (Kindergeld, Elterngeld) oraz alimentów' },
|
||||||
|
],
|
||||||
|
limitationsTitle: 'Dopuszczalne przychody dodatkowe (najem, kapitał, inne)',
|
||||||
|
limitationsText: 'Osoby uzyskujące dodatkowe przychody (np. z najmu i dzierżawy, kapitałów pieniężnych, innych źródeł) — doradztwo jest możliwe wyłącznie wtedy, gdy łączne przychody z tych źródeł nie przekraczają 18 000 € (dla osób samotnych) lub 36 000 € (dla małżeństw rozliczających się wspólnie) w roku podatkowym.',
|
||||||
|
excludedTitle: 'Kogo NIE obejmują nasze uprawnienia (granica ustawowa)',
|
||||||
|
excludedItems: [
|
||||||
|
{ text: 'Osoby prowadzące jednoosobową działalność gospodarczą (Gewerbe)' },
|
||||||
|
{ text: 'Przedsiębiorcy, spółki oraz osoby samozatrudnione (Selbständige)' },
|
||||||
|
{ text: 'Osoby uzyskujące przychody z działalności rolniczej i leśnej (Land- und Forstwirtschaft)' },
|
||||||
|
{ text: 'Osoby z przychodami z najmu lub kapitału przekraczającymi limity 18 000 € / 36 000 €' },
|
||||||
|
],
|
||||||
|
legalNotice: 'Pomoc podatkowa jest świadczona wyłącznie na rzecz członków stowarzyszenia b.b.h. Lohnsteuerhilfeverein e.V. zgodnie z § 4 Nr. 11 Steuerberatungsgesetz (StBerG).',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockType: 'finalCta',
|
||||||
|
title: 'Masz pytania dotyczące Twojego rozliczenia?',
|
||||||
|
subtitle: 'Skontaktuj się bezpośrednio przez WhatsApp. Przeanalizujemy Twoją sytuację i pomożemy uzyskać należny zwrot.',
|
||||||
|
buttonText: 'Napisz na WhatsApp',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const uslugiLayoutDe = [
|
||||||
|
{
|
||||||
|
blockType: 'pageHeader',
|
||||||
|
title: 'Steuerberatung und Steuererklärungen für Arbeitnehmer',
|
||||||
|
subtitle: 'Professionelle Erstellung von Steuererklärungen, Bescheidprüfung und ganzheitliche Unterstützung bei der Kommunikation mit dem Finanzamt.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockType: 'servicesDetailed',
|
||||||
|
title: 'Was ich anbiete',
|
||||||
|
subtitle: 'Umfassende und zuverlässige Betreuung bei allen Fragen rund um Ihre Einkommensteuererklärung.',
|
||||||
|
services: [
|
||||||
|
{
|
||||||
|
title: 'Erstellung der Einkommensteuererklärung',
|
||||||
|
description: 'Wir erstellen Ihre vollständige Steuererklärung unter Berücksichtigung aller gesetzlichen Freibeträge, Werbungskosten und Pauschalen für eine maximale Rückerstattung.',
|
||||||
|
icon: 'fileText',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Steuervorausberechnung',
|
||||||
|
description: 'Bereits vor der Übermittlung an das Finanzamt berechnen wir die voraussichtliche Steuererstattung oder Nachzahlung, damit Sie absolute Planungssicherheit haben.',
|
||||||
|
icon: 'calculator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Prüfung des Steuerbescheids',
|
||||||
|
description: 'Detaillierte Überprüfung Ihres Steuerbescheids auf Richtigkeit und Vollständigkeit. Wir stellen sicher, dass alle geltend gemachten Kosten anerkannt wurden.',
|
||||||
|
icon: 'shieldCheck',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Einspruchsverfahren',
|
||||||
|
description: 'Sollte der Steuerbescheid fehlerhaft sein, legen wir fristgerecht Einspruch beim Finanzamt ein und vertreten Ihre Interessen gegenüber den Finanzbehörden.',
|
||||||
|
icon: 'scale',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Ganzjährige steuerliche Betreuung',
|
||||||
|
description: 'Wir stehen Ihnen das gesamte Jahr über beratend zur Seite – bei Fragen zu Schreiben des Finanzamts, Belegen und steuerlichen Änderungen.',
|
||||||
|
icon: 'helpCircle',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Wahl der optimalen Steuerklasse',
|
||||||
|
description: 'Beratung zur Steuerklassenwahl für Ehepaare und Lebenspartner zur Minimierung der monatlichen Lohnsteuerabzüge und Vermeidung von Nachzahlungen.',
|
||||||
|
icon: 'users',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Freibeträge, Kindergeld & Fahrtkosten',
|
||||||
|
description: 'Unterstützung bei der Beantragung von Kindergeld sowie beim Absetzen von Fahrtkosten, doppelter Haushaltsführung und berufsbedingten Mehraufwendungen.',
|
||||||
|
icon: 'coins',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockType: 'eligibility',
|
||||||
|
title: 'Beratungsbefugnis gemäß § 4 Nr. 11 StBerG',
|
||||||
|
subtitle: 'Als Beratungsstelle des b.b.h. Lohnsteuerhilfevereins e.V. betreuen wir Sie streng im Rahmen der gesetzlichen Befugnisse.',
|
||||||
|
eligibleTitle: 'Wer von uns beraten werden darf',
|
||||||
|
eligibleItems: [
|
||||||
|
{ text: 'Arbeitnehmer, Angestellte und Arbeiter' },
|
||||||
|
{ text: 'Beamte und Beschäftigte im öffentlichen Dienst' },
|
||||||
|
{ text: 'Rentner und Pensionäre' },
|
||||||
|
{ text: 'Arbeitslose und Bezieher von Lohnersatzleistungen' },
|
||||||
|
{ text: 'Bezieher von Kindergeld, Elterngeld und Unterhaltsleistungen' },
|
||||||
|
],
|
||||||
|
limitationsTitle: 'Einnahmegrenzen bei Nebeneinkünften (Vermietung, Kapital)',
|
||||||
|
limitationsText: 'Bei sonstigen Einkünften (z. B. aus Vermietung und Verpachtung, Kapitalvermögen, privaten Veräußerungsgeschäften) darf die Summe der Einnahmen 18.000 € bei Alleinstehenden bzw. 36.000 € bei zusammenveranlagten Ehegatten im Kalenderjahr nicht übersteigen.',
|
||||||
|
excludedTitle: 'Ausschluss der Beratungsbefugnis (gesetzliche Grenze)',
|
||||||
|
excludedItems: [
|
||||||
|
{ text: 'Gewerbetreibende (Einzelunternehmer, Handwerker mit Gewerbe)' },
|
||||||
|
{ text: 'Freiberufler und Selbständige' },
|
||||||
|
{ text: 'Einkünfte aus Land- und Forstwirtschaft' },
|
||||||
|
{ text: 'Einnahmen aus Vermietung/Kapital über den gesetzlichen Grenzen (18.000 € / 36.000 €)' },
|
||||||
|
],
|
||||||
|
legalNotice: 'Die steuerliche Beratung erfolgt ausschließlich im Rahmen einer Mitgliedschaft beim b.b.h. Lohnsteuerhilfeverein e.V. gemäß § 4 Nr. 11 StBerG.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockType: 'finalCta',
|
||||||
|
title: 'Haben Sie Fragen zu Ihrer Steuererklärung?',
|
||||||
|
subtitle: 'Kontaktieren Sie uns direkt per WhatsApp. Wir prüfen Ihre Situation und helfen Ihnen bei der Rückerstattung.',
|
||||||
|
buttonText: 'Auf WhatsApp schreiben',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
const payload = await getPayload({ config: configPromise })
|
const payload = await getPayload({ config: configPromise })
|
||||||
|
|
||||||
@@ -178,17 +336,37 @@ async function run() {
|
|||||||
const pageIds: number[] = [];
|
const pageIds: number[] = [];
|
||||||
|
|
||||||
for (const page of PAGES) {
|
for (const page of PAGES) {
|
||||||
|
const isHome = page.isHome;
|
||||||
|
const isUslugi = page.pl === 'Usługi';
|
||||||
|
|
||||||
|
const layoutPl = isHome
|
||||||
|
? homeLayoutPl
|
||||||
|
: isUslugi
|
||||||
|
? uslugiLayoutPl
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
blockType: 'TextSection',
|
||||||
|
text: 'Treść w przygotowaniu',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const layoutDe = isHome
|
||||||
|
? homeLayoutDe
|
||||||
|
: isUslugi
|
||||||
|
? uslugiLayoutDe
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
blockType: 'TextSection',
|
||||||
|
text: 'Inhalt in Vorbereitung',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const createdPage = await payload.create({
|
const createdPage = await payload.create({
|
||||||
collection: 'pages',
|
collection: 'pages',
|
||||||
data: {
|
data: {
|
||||||
title: page.pl,
|
title: page.pl,
|
||||||
slug: page.isHome ? 'home' : toSlug(page.pl),
|
slug: page.isHome ? 'home' : toSlug(page.pl),
|
||||||
layout: (page.isHome ? homeLayoutPl : [
|
layout: layoutPl as unknown as Page['layout'],
|
||||||
{
|
|
||||||
blockType: 'TextSection',
|
|
||||||
text: 'Treść w przygotowaniu',
|
|
||||||
}
|
|
||||||
]) as unknown as Page['layout']
|
|
||||||
},
|
},
|
||||||
locale: 'pl',
|
locale: 'pl',
|
||||||
});
|
});
|
||||||
@@ -201,12 +379,7 @@ async function run() {
|
|||||||
data: {
|
data: {
|
||||||
title: page.de,
|
title: page.de,
|
||||||
slug: page.isHome ? 'home' : toSlug(page.de),
|
slug: page.isHome ? 'home' : toSlug(page.de),
|
||||||
layout: (page.isHome ? homeLayoutDe : [
|
layout: layoutDe as unknown as Page['layout'],
|
||||||
{
|
|
||||||
blockType: 'TextSection',
|
|
||||||
text: 'Inhalt in Vorbereitung',
|
|
||||||
}
|
|
||||||
]) as unknown as Page['layout']
|
|
||||||
},
|
},
|
||||||
locale: 'de',
|
locale: 'de',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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' },
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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' },
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -11,7 +11,14 @@ import { TestimonialsBlock } from './Testimonials/Component'
|
|||||||
import { FAQTeaserBlock } from './FAQTeaser/Component'
|
import { FAQTeaserBlock } from './FAQTeaser/Component'
|
||||||
import { FinalCTABlock } from './FinalCTA/Component'
|
import { FinalCTABlock } from './FinalCTA/Component'
|
||||||
|
|
||||||
|
import { PageHeaderBlock } from './PageHeader/Component'
|
||||||
|
import { ServicesDetailedBlock } from './ServicesDetailed/Component'
|
||||||
|
import { EligibilityBlock } from './Eligibility/Component'
|
||||||
|
|
||||||
const blockComponents = {
|
const blockComponents = {
|
||||||
|
pageHeader: PageHeaderBlock,
|
||||||
|
servicesDetailed: ServicesDetailedBlock,
|
||||||
|
eligibility: EligibilityBlock,
|
||||||
hero: HeroBlock,
|
hero: HeroBlock,
|
||||||
trustBar: TrustBarBlock,
|
trustBar: TrustBarBlock,
|
||||||
uniqueValue: UniqueValueBlock,
|
uniqueValue: UniqueValueBlock,
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -10,6 +10,10 @@ import { Testimonials } from '../blocks/Testimonials/config'
|
|||||||
import { FAQTeaser } from '../blocks/FAQTeaser/config'
|
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 { ServicesDetailed } from '../blocks/ServicesDetailed/config'
|
||||||
|
import { Eligibility } from '../blocks/Eligibility/config'
|
||||||
|
|
||||||
export const Pages: CollectionConfig = {
|
export const Pages: CollectionConfig = {
|
||||||
slug: 'pages',
|
slug: 'pages',
|
||||||
admin: {
|
admin: {
|
||||||
@@ -29,8 +33,12 @@ export const Pages: CollectionConfig = {
|
|||||||
{
|
{
|
||||||
name: 'layout',
|
name: 'layout',
|
||||||
type: 'blocks',
|
type: 'blocks',
|
||||||
|
localized: true,
|
||||||
minRows: 1,
|
minRows: 1,
|
||||||
blocks: [
|
blocks: [
|
||||||
|
PageHeader,
|
||||||
|
ServicesDetailed,
|
||||||
|
Eligibility,
|
||||||
Hero,
|
Hero,
|
||||||
TrustBar,
|
TrustBar,
|
||||||
UniqueValue,
|
UniqueValue,
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
import { ArrowRight, Calculator } from "lucide-react";
|
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
export function Hero() {
|
|
||||||
return (
|
|
||||||
<section className="relative overflow-hidden pt-12 md:pt-20 lg:pt-32 pb-16 lg:pb-32">
|
|
||||||
{/* Subtelne tło dekoracyjne, bez przesadzonych efektów - oddychający layout */}
|
|
||||||
<div className="absolute inset-0 z-0 bg-[radial-gradient(ellipse_at_top_right,var(--accent-color)_0%,transparent_50%)] opacity-[0.03] dark:opacity-[0.05]" />
|
|
||||||
|
|
||||||
<div className="container-premium relative z-10">
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-20 items-center">
|
|
||||||
|
|
||||||
{/* Typografia i treść */}
|
|
||||||
<div className="flex flex-col items-start max-w-2xl">
|
|
||||||
<div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-[var(--accent-color)]/10 text-[var(--accent-color)] text-sm font-medium mb-6">
|
|
||||||
<Calculator className="w-4 h-4" />
|
|
||||||
Lohnsteuerhilfeverein e.V.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1 className="text-4xl md:text-5xl lg:text-6xl/tight text-foreground mb-6">
|
|
||||||
Twój spokój w świecie{" "}
|
|
||||||
<span className="text-[var(--accent-color)]">niemieckich podatków</span>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<p className="text-lg md:text-xl text-muted-foreground mb-10 leading-relaxed font-light max-w-xl">
|
|
||||||
Profesjonalne doradztwo podatkowe, rozliczenia i pełne wsparcie w kontaktach z Finanzamt.
|
|
||||||
Zadbamy o Twoje finanse, abyś Ty mógł skupić się na tym, co ważne.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row gap-4 w-full sm:w-auto">
|
|
||||||
<Link href="/kontakt" className="btn-primary group">
|
|
||||||
Umów konsultację
|
|
||||||
<ArrowRight className="w-4 h-4 ml-2 group-hover:translate-x-1 transition-transform" />
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="/uslugi"
|
|
||||||
className="inline-flex items-center justify-center rounded-lg px-6 py-3 font-medium text-foreground bg-transparent border border-border hover:bg-black/5 dark:hover:bg-white/5 transition-colors"
|
|
||||||
>
|
|
||||||
Poznaj nasze usługi
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Miejsce na realne zdjęcie Janusza - neutralny placeholder */}
|
|
||||||
<div className="relative mx-auto lg:mx-0 w-full max-w-md aspect-[4/5] rounded-2xl overflow-hidden bg-black/5 dark:bg-white/5 border border-border flex items-center justify-center shadow-2xl">
|
|
||||||
<div className="text-center text-muted-foreground opacity-50 flex flex-col items-center">
|
|
||||||
<span className="text-8xl font-heading font-bold mb-4">JK</span>
|
|
||||||
<span className="text-sm font-medium uppercase tracking-widest">Miejsce na zdjęcie</span>
|
|
||||||
<span className="text-xs mt-1">Zostanie dodane przez panel CMS</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -213,6 +213,46 @@ export interface Page {
|
|||||||
slug: string;
|
slug: string;
|
||||||
layout?:
|
layout?:
|
||||||
| (
|
| (
|
||||||
|
| {
|
||||||
|
title: string;
|
||||||
|
subtitle?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'pageHeader';
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
title: string;
|
||||||
|
subtitle?: string | null;
|
||||||
|
services: {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
icon?: ('fileText' | 'calculator' | 'shieldCheck' | 'scale' | 'helpCircle' | 'users' | 'coins') | null;
|
||||||
|
id?: string | null;
|
||||||
|
}[];
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'servicesDetailed';
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
title: string;
|
||||||
|
subtitle?: string | null;
|
||||||
|
eligibleTitle: string;
|
||||||
|
eligibleItems: {
|
||||||
|
text: string;
|
||||||
|
id?: string | null;
|
||||||
|
}[];
|
||||||
|
limitationsTitle: string;
|
||||||
|
limitationsText: string;
|
||||||
|
excludedTitle: string;
|
||||||
|
excludedItems: {
|
||||||
|
text: string;
|
||||||
|
id?: string | null;
|
||||||
|
}[];
|
||||||
|
legalNotice?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'eligibility';
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
title: string;
|
title: string;
|
||||||
subtitle: string;
|
subtitle: string;
|
||||||
@@ -497,6 +537,55 @@ export interface PagesSelect<T extends boolean = true> {
|
|||||||
layout?:
|
layout?:
|
||||||
| T
|
| T
|
||||||
| {
|
| {
|
||||||
|
pageHeader?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
title?: T;
|
||||||
|
subtitle?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
|
servicesDetailed?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
title?: T;
|
||||||
|
subtitle?: T;
|
||||||
|
services?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
title?: T;
|
||||||
|
description?: T;
|
||||||
|
icon?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
|
eligibility?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
title?: T;
|
||||||
|
subtitle?: T;
|
||||||
|
eligibleTitle?: T;
|
||||||
|
eligibleItems?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
text?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
limitationsTitle?: T;
|
||||||
|
limitationsText?: T;
|
||||||
|
excludedTitle?: T;
|
||||||
|
excludedItems?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
text?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
legalNotice?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
hero?:
|
hero?:
|
||||||
| T
|
| T
|
||||||
| {
|
| {
|
||||||
@@ -723,6 +812,12 @@ export interface Footer {
|
|||||||
logo?: (number | null) | Media;
|
logo?: (number | null) | Media;
|
||||||
link?: string | null;
|
link?: string | null;
|
||||||
};
|
};
|
||||||
|
copyright: string;
|
||||||
|
creator: {
|
||||||
|
label?: string | null;
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
updatedAt?: string | null;
|
updatedAt?: string | null;
|
||||||
createdAt?: string | null;
|
createdAt?: string | null;
|
||||||
}
|
}
|
||||||
@@ -942,6 +1037,14 @@ export interface FooterSelect<T extends boolean = true> {
|
|||||||
logo?: T;
|
logo?: T;
|
||||||
link?: T;
|
link?: T;
|
||||||
};
|
};
|
||||||
|
copyright?: T;
|
||||||
|
creator?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
label?: T;
|
||||||
|
name?: T;
|
||||||
|
url?: T;
|
||||||
|
};
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
globalType?: T;
|
globalType?: T;
|
||||||
|
|||||||
Reference in New Issue
Block a user