feat(core): implement robust db:reset with content snapshotting

This commit is contained in:
MichalC
2026-09-04 00:05:00 +02:00
parent 2e782c431a
commit bbd55793e1
9 changed files with 1658 additions and 516 deletions
+2 -1
View File
@@ -15,7 +15,8 @@
"start": "cross-env NODE_OPTIONS=--no-deprecation next start", "start": "cross-env NODE_OPTIONS=--no-deprecation next start",
"test": "pnpm run test:int && pnpm run test:e2e", "test": "pnpm run test:int && pnpm run test:e2e",
"test:e2e": "cross-env NODE_OPTIONS=\"--no-deprecation --import=tsx/esm\" playwright test --config=playwright.config.ts", "test:e2e": "cross-env NODE_OPTIONS=\"--no-deprecation --import=tsx/esm\" playwright test --config=playwright.config.ts",
"test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts" "test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts",
"db:reset": "rm -f dev.db && cross-env NODE_OPTIONS=--no-deprecation payload run ./scripts/seed.ts"
}, },
"dependencies": { "dependencies": {
"@intecion/ipal-kit": "^1.1.2", "@intecion/ipal-kit": "^1.1.2",
File diff suppressed because it is too large Load Diff
+80
View File
@@ -0,0 +1,80 @@
import { getPayload } from 'payload'
import configPromise from '../src/payload.config'
import fs from 'fs'
import path from 'path'
async function run() {
console.log('Connecting to Payload Local API...')
const payload = await getPayload({ config: configPromise })
const locales = ['pl', 'de']
const snapshot: Record<string, any> = {
pages: [],
globals: {
header: {},
footer: {},
company: {},
'site-settings': {}
}
}
// 1. Fetch all pages in all locales
console.log('Exporting pages...')
const plPages = await payload.find({
collection: 'pages',
locale: 'pl',
limit: 100,
depth: 0,
})
const dePages = await payload.find({
collection: 'pages',
locale: 'de',
limit: 100,
depth: 0,
})
// We combine them by ID
for (const plPage of plPages.docs) {
const dePage = dePages.docs.find(p => p.id === plPage.id)
snapshot.pages.push({
id: plPage.id,
pl: plPage,
de: dePage
})
}
// 2. Fetch globals
console.log('Exporting globals...')
const globalsList = ['header', 'footer', 'company', 'site-settings']
for (const slug of globalsList) {
const plGlobal = await payload.findGlobal({
slug: slug as any,
locale: 'pl',
depth: 0
})
const deGlobal = await payload.findGlobal({
slug: slug as any,
locale: 'de',
depth: 0
})
snapshot.globals[slug] = {
pl: plGlobal,
de: deGlobal
}
}
const outputPath = path.join(process.cwd(), 'scripts', 'content-snapshot.json')
fs.writeFileSync(outputPath, JSON.stringify(snapshot, null, 2))
console.log(`Snapshot saved to ${outputPath}`)
process.exit(0)
}
try {
await run()
} catch (err) {
console.error('ERROR during export:', err)
process.exit(1)
}
+86 -507
View File
@@ -1,533 +1,112 @@
import { getPayload } from 'payload' import { getPayload } from 'payload'
import configPromise from '../src/payload.config' import configPromise from '../src/payload.config'
import { toSlug } from '@intecion/ipal-kit' import fs from 'fs'
import type { Page } from '../src/payload-types' import path from 'path'
const PAGES = [ // Helper to remove system fields
{ pl: 'Strona główna', de: 'Startseite', isHome: true }, const cleanData = (data: any) => {
{ pl: 'Usługi', de: 'Leistungen', isHome: false }, if (!data) return data;
{ pl: 'Jak się umówić', de: 'Terminvereinbarung', isHome: false }, const { id, createdAt, updatedAt, _status, ...rest } = data
{ pl: 'O mnie', de: 'Über mich', isHome: false }, return rest
{ pl: 'FAQ', de: 'FAQ', isHome: false }, }
{ pl: 'Kontakt', de: 'Kontakt', isHome: false },
{ pl: 'Stowarzyszenie', de: 'Verein', isHome: false },
{ pl: 'Polityka prywatności', de: 'Datenschutzerklärung', isHome: false },
{ pl: 'Polityka cookies', de: 'Cookie-Richtlinie', isHome: false },
{ pl: 'Impressum', de: 'Impressum', isHome: false },
];
const homeLayoutPl = [ // Deep replace page IDs in globals and nullify missing media relations
{ const mapRelations = (obj: any, idMap: Map<number, number>): any => {
blockType: 'hero', if (Array.isArray(obj)) {
title: 'Odbierz zwrot podatku z Niemiec — rozliczymy Cię po polsku', return obj.map(item => mapRelations(item, idMap))
subtitle: 'Ponad 20 lat doświadczenia w rozliczeniach dla Polaków pracujących w Niemczech. Szybko, rzetelnie i w pełni po polsku.',
primaryCta: 'Napisz na WhatsApp',
secondaryCta: { text: 'Zobacz jak to działa', url: '/jak-to-dziala' },
yearsOfExperience: 'Ponad 20 lat doświadczenia',
},
{
blockType: 'trustBar',
eyebrow: 'Zaufanie',
items: [
{ text: 'Działam w ramach b.b.h.', icon: 'shield' },
{ text: 'Doradztwo 100% po polsku', icon: 'message' },
{ text: 'Wsparcie z Finanzamt', icon: 'check' },
{ text: 'Wieloletnie doświadczenie', icon: 'star' },
],
},
{
blockType: 'uniqueValue',
eyebrow: 'Wartość',
title: 'Rozliczenia bez bariery językowej',
features: [{ text: 'Bez barier językowych' }, { text: 'Indywidualne podejście' }],
description: 'Niemieckie prawo podatkowe bywa skomplikowane. Przeprowadzę Cię przez cały proces w Twoim ojczystym języku, dbając o maksymalny zwrot i zero stresu.',
},
{
blockType: 'stats',
eyebrow: 'Statistiken',
items: [
{
stat: '20+',
label: 'Lat doświadczenia',
description: 'w prawie podatkowym i rozliczeniach dla Polaków w Niemczech',
},
{
stat: '100%',
label: 'Obsługa po polsku',
description: 'bezpośredni kontakt i wyjaśnienie każdej kwestii podatkowej',
},
{
stat: '§4 Nr. 11',
label: 'Stowarzyszenie b.b.h.',
description: 'oficjalna podstawa prawna doradztwa zgodnie ze StBerG',
},
],
},
{
blockType: 'servicesList',
eyebrow: 'Usługi',
title: 'Czym się zajmuję',
services: [
{ title: 'Deklaracje podatkowe', description: 'Kompleksowe sporządzenie i złożenie zeznania rocznego (Einkommensteuererklärung).', linkText: 'Zobacz więcej' },
{ title: 'Kontrola decyzji', description: 'Szczegółowa weryfikacja decyzji podatkowej (Steuerbescheid) z Finanzamt.', linkText: 'Zobacz więcej' },
{ title: 'Ulgi i dopłaty', description: 'Pomoc w uzyskaniu Kindergeld, Riester-Rente oraz odliczaniu kosztów dojazdów i podwójnego gospodarstwa.', linkText: 'Zobacz więcej' },
],
},
{
blockType: 'process',
eyebrow: 'Proces',
title: 'Jak to działa',
subtitle: 'Prosty i przejrzysty proces rozliczenia w 3 krokach — bez zbędnych formalności.',
steps: [
{ title: 'Napisz na WhatsApp', description: 'Wyślij krótką wiadomość lub zadzwoń, by rozpocząć proces.' },
{ title: 'Przekaż dokumenty', description: 'Prześlij nam wymagane dokumenty bezpiecznie online lub dostarcz osobiście.' },
{ title: 'Odbierz zwrot', description: 'Zajmiemy się resztą i poinformujemy Cię o spodziewanym zwrocie z Finanzamt.' },
],
ctaText: 'Zobacz cały proces',
},
{
blockType: 'affiliation',
eyebrow: 'Zaufanie',
title: 'Zaufanie i bezpieczeństwo',
description: 'Działam w ramach b.b.h. Lohnsteuerhilfeverein e.V. (Beratungsstelle). Oferujemy profesjonalną pomoc w sprawach podatku dochodowego w ramach uprawnień wynikających z §4 Nr. 11 StBerG.',
linkText: 'Poznaj stowarzyszenie',
},
{
blockType: 'testimonials',
title: 'Opinie klientów',
testimonials: [
{ quote: 'Pan Janusz pomógł mi odzyskać podatek za 3 ostatnie lata. Wszystko jasno wytłumaczone po polsku.', author: 'Piotr, Monachium' },
{ quote: 'Szybki kontakt przez WhatsApp i pełne wsparcie przy odwołaniu od decyzji Finanzamt. Polecam!', author: 'Anna, Berlin' },
{ quote: 'Ogromna wiedza o ulgach dla pracowników budowlanych. Zwrot był znacznie wyższy niż się spodziewałem.', author: 'Krzysztof, Dortmund' },
],
},
{
blockType: 'faqTeaser',
eyebrow: 'FAQ',
title: 'Częste pytania',
questions: [
{ question: 'Czy muszę przyjeżdżać osobiście?', answer: 'Nie, wszystkie formalności możemy załatwić zdalnie, przesyłając dokumenty online.' },
{ question: 'Do kiedy muszę złożyć deklarację?', answer: 'Standardowy termin to 31 sierpnia, ale korzystając z naszych usług, termin ten wydłuża się do końca lutego następnego roku.' },
],
linkText: 'Zobacz wszystkie pytania',
},
{
blockType: 'finalCta',
eyebrow: 'Kontakt',
title: 'Gotowy na wyższy zwrot podatku?',
subtitle: 'Napisz do mnie już teraz. Odpowiem najszybciej jak to możliwe.',
buttonText: 'Rozpocznij na WhatsApp',
} }
]; if (obj !== null && typeof obj === 'object') {
const newObj: any = {}
const homeLayoutDe = [ for (const key of Object.keys(obj)) {
{ if (['image', 'imageSecondary', 'photo', 'logo'].includes(key)) {
blockType: 'hero', newObj[key] = null
title: 'Sichern Sie sich Ihre Steuererstattung aus Deutschland — Beratung auf Polnisch', } else if (key === 'page' && typeof obj[key] === 'number') {
subtitle: 'Über 20 Jahre Erfahrung in der Steuererklärung für in Deutschland arbeitende Polen. Schnell, zuverlässig und komplett auf Polnisch.', newObj[key] = idMap.get(obj[key]) || obj[key]
primaryCta: 'Auf WhatsApp schreiben', } else if (key === 'homepage' && typeof obj[key] === 'number') {
secondaryCta: { text: 'So funktioniert es', url: '/jak-to-dziala' }, newObj[key] = idMap.get(obj[key]) || obj[key]
yearsOfExperience: 'Über 20 Jahre Erfahrung', } else {
}, newObj[key] = mapRelations(obj[key], idMap)
{ }
blockType: 'trustBar', }
eyebrow: 'Zaufanie', return newObj
items: [
{ text: 'Teil der b.b.h.', icon: 'shield' },
{ text: '100% Beratung auf Polnisch', icon: 'message' },
{ text: 'Finanzamt Unterstützung', icon: 'check' },
{ text: 'Langjährige Erfahrung', icon: 'star' },
],
},
{
blockType: 'uniqueValue',
eyebrow: 'Wert',
title: 'Steuererklärungen ohne Sprachbarriere',
features: [{ text: 'Ohne Sprachbarrieren' }, { text: 'Individueller Ansatz' }],
description: 'Das deutsche Steuerrecht kann kompliziert sein. Ich begleite Sie durch den gesamten Prozess in Ihrer Muttersprache, sorge für die maximale Erstattung und garantiere null Stress.',
},
{
blockType: 'stats',
eyebrow: 'Statistiken',
items: [
{
stat: '20+',
label: 'Jahre Erfahrung',
description: 'in der Steuerberatung für in Deutschland arbeitende Polen',
},
{
stat: '100%',
label: 'Auf Polnisch',
description: 'direkte Kommunikation in Ihrer vertrauten Muttersprache',
},
{
stat: '§4 Nr. 11',
label: 'b.b.h. e.V.',
description: 'gesetzliche Beratungsbefugnis nach dem Steuerberatungsgesetz',
},
],
},
{
blockType: 'servicesList',
eyebrow: 'Leistungen',
title: 'Was ich anbiete',
services: [
{ title: 'Steuererklärungen', description: 'Umfassende Erstellung und Einreichung der jährlichen Einkommensteuererklärung.', linkText: 'Mehr anzeigen' },
{ title: 'Steuerbescheidprüfung', description: 'Detaillierte Prüfung des Steuerbescheids vom Finanzamt.', linkText: 'Mehr anzeigen' },
{ title: 'Vergünstigungen und Zuschüsse', description: 'Hilfe bei der Beantragung von Kindergeld, Riester-Rente und der Absetzung von Fahrtkosten sowie doppelter Haushaltsführung.', linkText: 'Mehr anzeigen' },
],
},
{
blockType: 'process',
eyebrow: 'Prozess',
title: 'Wie es funktioniert',
subtitle: 'Ein einfacher und transparenter 3-Schritte-Abrechnungsprozess — ohne unnötige Formalitäten.',
steps: [
{ title: 'Auf WhatsApp schreiben', description: 'Senden Sie eine kurze Nachricht oder rufen Sie an, um den Prozess zu starten.' },
{ title: 'Dokumente einreichen', description: 'Senden Sie uns die erforderlichen Dokumente sicher online oder bringen Sie sie persönlich vorbei.' },
{ title: 'Erstattung erhalten', description: 'Wir kümmern uns um den Rest und informieren Sie über die erwartete Rückerstattung vom Finanzamt.' },
],
ctaText: 'Den gesamten Prozess ansehen',
},
{
blockType: 'affiliation',
eyebrow: 'Vertrauen',
title: 'Vertrauen und Sicherheit',
description: 'Ich bin im Rahmen des b.b.h. Lohnsteuerhilfevereins e.V. (Beratungsstelle) tätig. Wir bieten professionelle Hilfe in Lohnsteuersachen im Rahmen der Befugnisse nach §4 Nr. 11 StBerG.',
linkText: 'Den Verein kennenlernen',
},
{
blockType: 'testimonials',
title: 'Kundenrezensionen',
testimonials: [
{ quote: 'Herr Janusz half mir, meine Steuern für die letzten 3 Jahre zurückzubekommen. Alles wurde klar auf Polnisch erklärt.', author: 'Piotr, München' },
{ quote: 'Schneller Kontakt über WhatsApp und volle Unterstützung bei Einsprüchen beim Finanzamt. Sehr zu empfehlen!', author: 'Anna, Berlin' },
{ quote: 'Enormes Wissen über Vergünstigungen für Bauarbeiter. Die Erstattung war viel höher als erwartet.', author: 'Krzysztof, Dortmund' },
],
},
{
blockType: 'faqTeaser',
eyebrow: 'FAQ',
title: 'Häufige Fragen',
questions: [
{ question: 'Muss ich persönlich kommen?', answer: 'Nein, alle Formalitäten können online erledigt werden, indem Sie uns die Dokumente zusenden.' },
{ question: 'Bis wann muss ich die Steuererklärung einreichen?', answer: 'Die Standardfrist ist der 31. August, aber durch unsere Dienste verlängert sich diese bis Ende Februar des Folgejahres.' },
],
linkText: 'Alle Fragen ansehen',
},
{
blockType: 'finalCta',
eyebrow: 'Kontakt',
title: 'Bereit für eine höhere Steuererstattung?',
subtitle: 'Schreiben Sie mir jetzt. Ich werde so schnell wie möglich antworten.',
buttonText: 'Auf WhatsApp starten',
} }
]; return obj
}
const uslugiLayoutPl = [ console.log("Starting script"); async function run() {
{
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',
eyebrow: 'Usługi',
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',
eyebrow: 'Zakres uprawnień doradczych',
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',
eyebrow: 'Kontakt',
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',
eyebrow: 'Leistungen',
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',
eyebrow: 'Umfang der Beratungsbefugnis',
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',
eyebrow: 'Kontakt',
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() {
const payload = await getPayload({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const snapshotPath = path.join(process.cwd(), 'scripts', 'content-snapshot.json')
if (!fs.existsSync(snapshotPath)) {
console.error('No content-snapshot.json found!')
process.exit(1)
}
const snapshot = JSON.parse(fs.readFileSync(snapshotPath, 'utf-8'))
const idMap = new Map<number, number>()
// 1. Check if DB is already seeded (has pages)
const existingPages = await payload.find({ const existingPages = await payload.find({
collection: 'pages', collection: 'pages',
limit: 1, limit: 1,
}) })
if (existingPages.totalDocs === 0) { if (existingPages.totalDocs > 0) {
console.log('Seeding pages...') console.log('Pages already seeded. If you want to reset, use the db:reset script.')
process.exit(0)
}
console.log('Seeding pages from snapshot...')
for (const pageItem of snapshot.pages) {
const oldId = pageItem.id
const pageIds: number[] = []; // Create in PL first
const createdPage = await payload.create({
collection: 'pages',
locale: 'pl',
data: mapRelations(cleanData(pageItem.pl), idMap),
})
for (const page of PAGES) { idMap.set(oldId, createdPage.id as number)
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({
collection: 'pages',
data: {
title: page.pl,
slug: page.isHome ? 'home' : toSlug(page.pl),
layout: layoutPl as unknown as Page['layout'],
},
locale: 'pl',
});
pageIds.push(createdPage.id as unknown as number);
// Update with DE
if (pageItem.de) {
await payload.update({ await payload.update({
collection: 'pages', collection: 'pages',
id: createdPage.id, id: createdPage.id,
data: {
title: page.de,
slug: page.isHome ? 'home' : toSlug(page.de),
layout: layoutDe as unknown as Page['layout'],
},
locale: 'de', locale: 'de',
}); data: mapRelations(cleanData(pageItem.de), idMap),
})
console.log(`- Created ${page.pl} / ${page.de}`); }
console.log(`- Restored page: ${pageItem.pl?.title || 'Unknown'} (ID: ${oldId} -> ${createdPage.id})`)
}
console.log('Seeding globals from snapshot...')
for (const slug of Object.keys(snapshot.globals)) {
const globalData = snapshot.globals[slug]
if (globalData.pl) {
const plData = mapRelations(cleanData(globalData.pl), idMap)
await payload.updateGlobal({
slug: slug as any,
locale: 'pl',
data: plData,
})
} }
console.log('Seeding global header...') if (globalData.de) {
await payload.updateGlobal({ const deData = mapRelations(cleanData(globalData.de), idMap)
slug: 'header', await payload.updateGlobal({
data: { slug: slug as any,
navigation: pageIds.slice(0, 6).map(id => ({ page: id })), locale: 'de',
}, data: deData,
}); })
}
console.log('Seeding global company info...') console.log(`- Restored global: ${slug}`)
await payload.updateGlobal({
slug: 'company',
data: {
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',
whatsappNumber: '49123456789',
}
});
console.log('Seeding global footer...')
const footerNavIndices = [0, 1, 2, 3, 4, 5, 7, 8, 9];
const footerNavPages = footerNavIndices.map(index => ({ page: pageIds[index] }));
await payload.updateGlobal({
slug: 'footer',
data: {
navigation: footerNavPages,
association: {
title: 'Działam w ramach b.b.h. Lohnsteuerhilfeverein e.V.',
link: 'https://bbh-lohnsteuerhilfe.de',
},
copyright: '© {year} Finanz- & Lohnbuchhaltung Dipl.-Ing. Janusz Kędzierski. Wszelkie prawa zastrzeżone.',
creator: {
label: 'Realizacja:',
name: 'NetScale',
url: 'https://netscale.pl',
},
},
locale: 'pl'
});
await payload.updateGlobal({
slug: 'footer',
data: {
navigation: footerNavPages,
association: {
title: 'Ich bin im Rahmen des b.b.h. Lohnsteuerhilfevereins e.V. tätig.',
link: 'https://bbh-lohnsteuerhilfe.de',
},
copyright: '© {year} Finanz- & Lohnbuchhaltung Dipl.-Ing. Janusz Kędzierski. Alle Rechte vorbehalten.',
creator: {
label: 'Erstellt von',
name: 'NetScale',
url: 'https://netscale.pl',
},
},
locale: 'de'
});
console.log('Seeding site-settings (homepage)...')
await payload.updateGlobal({
slug: 'site-settings',
data: {
siteName: 'Finanz- & Lohnbuchhaltung Dipl.-Ing. Janusz Kędzierski',
titleOrder: 'page-first',
titleSeparator: '|',
homepage: pageIds[0],
}
});
console.log('Seed completed.')
} else {
console.log('Pages already seeded.')
} }
console.log('Seed completed successfully.')
} }
run().catch(console.error).finally(() => process.exit(0)) try { await run(); } catch (err) { console.error(err); process.exit(1); }
-1
View File
@@ -40,7 +40,6 @@ export const Bio: Block = {
name: 'image', name: 'image',
type: 'upload', type: 'upload',
relationTo: 'media', relationTo: 'media',
required: true,
}, },
], ],
} }
+1 -1
View File
@@ -23,7 +23,7 @@ export const Hero: Block = {
{ name: 'label', type: 'text', localized: true, defaultValue: 'Lat doświadczenia' }, { name: 'label', type: 'text', localized: true, defaultValue: 'Lat doświadczenia' },
], ],
}, },
{ name: 'image', type: 'upload', relationTo: 'media', required: true }, { name: 'image', type: 'upload', relationTo: 'media' },
{ name: 'imageSecondary', type: 'upload', relationTo: 'media' }, { name: 'imageSecondary', type: 'upload', relationTo: 'media' },
], ],
} }
+1 -1
View File
@@ -19,6 +19,6 @@ export const Process: Block = {
}, },
{ name: 'ctaText', type: 'text', localized: true }, { name: 'ctaText', type: 'text', localized: true },
{ name: 'ctaUrl', type: 'text', localized: true }, { name: 'ctaUrl', type: 'text', localized: true },
{ name: 'image', type: 'upload', relationTo: 'media', required: true }, { name: 'image', type: 'upload', relationTo: 'media' },
], ],
} }
+1 -1
View File
@@ -6,7 +6,7 @@ export const UniqueValue: Block = {
{ name: 'eyebrow', type: 'text', localized: true }, { name: 'eyebrow', type: 'text', localized: true },
{ name: 'title', type: 'text', required: true, localized: true }, { name: 'title', type: 'text', required: true, localized: true },
{ name: 'description', type: 'textarea', required: true, localized: true }, { name: 'description', type: 'textarea', required: true, localized: true },
{ name: 'image', type: 'upload', relationTo: 'media', required: true }, { name: 'image', type: 'upload', relationTo: 'media' },
{ {
name: 'features', name: 'features',
type: 'array', type: 'array',
+4 -4
View File
@@ -241,7 +241,7 @@ export interface Page {
id?: string | null; id?: string | null;
}[] }[]
| null; | null;
image: number | Media; image?: (number | null) | Media;
id?: string | null; id?: string | null;
blockName?: string | null; blockName?: string | null;
blockType: 'bio'; blockType: 'bio';
@@ -307,7 +307,7 @@ export interface Page {
value?: string | null; value?: string | null;
label?: string | null; label?: string | null;
}; };
image: number | Media; image?: (number | null) | Media;
imageSecondary?: (number | null) | Media; imageSecondary?: (number | null) | Media;
id?: string | null; id?: string | null;
blockName?: string | null; blockName?: string | null;
@@ -342,7 +342,7 @@ export interface Page {
eyebrow?: string | null; eyebrow?: string | null;
title: string; title: string;
description: string; description: string;
image: number | Media; image?: (number | null) | Media;
features?: features?:
| { | {
text: string; text: string;
@@ -381,7 +381,7 @@ export interface Page {
| null; | null;
ctaText?: string | null; ctaText?: string | null;
ctaUrl?: string | null; ctaUrl?: string | null;
image: number | Media; image?: (number | null) | Media;
id?: string | null; id?: string | null;
blockName?: string | null; blockName?: string | null;
blockType: 'process'; blockType: 'process';