feat(core): implement robust db:reset with content snapshotting
This commit is contained in:
+2
-1
@@ -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
@@ -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)
|
||||||
|
}
|
||||||
+85
-506
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
const pageIds: number[] = [];
|
console.log('Seeding pages from snapshot...')
|
||||||
|
for (const pageItem of snapshot.pages) {
|
||||||
|
const oldId = pageItem.id
|
||||||
|
|
||||||
for (const page of PAGES) {
|
// Create in PL first
|
||||||
const isHome = page.isHome;
|
const createdPage = await payload.create({
|
||||||
const isUslugi = page.pl === 'Usługi';
|
collection: 'pages',
|
||||||
|
locale: 'pl',
|
||||||
|
data: mapRelations(cleanData(pageItem.pl), idMap),
|
||||||
|
})
|
||||||
|
|
||||||
const layoutPl = isHome
|
idMap.set(oldId, createdPage.id as number)
|
||||||
? 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('Seeding global header...')
|
console.log(`- Restored page: ${pageItem.pl?.title || 'Unknown'} (ID: ${oldId} -> ${createdPage.id})`)
|
||||||
await payload.updateGlobal({
|
|
||||||
slug: 'header',
|
|
||||||
data: {
|
|
||||||
navigation: pageIds.slice(0, 6).map(id => ({ page: id })),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('Seeding global company info...')
|
|
||||||
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('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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (globalData.de) {
|
||||||
|
const deData = mapRelations(cleanData(globalData.de), idMap)
|
||||||
|
await payload.updateGlobal({
|
||||||
|
slug: slug as any,
|
||||||
|
locale: 'de',
|
||||||
|
data: deData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
console.log(`- Restored global: ${slug}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Seed completed successfully.')
|
||||||
}
|
}
|
||||||
|
|
||||||
run().catch(console.error).finally(() => process.exit(0))
|
try { await run(); } catch (err) { console.error(err); process.exit(1); }
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ export const Bio: Block = {
|
|||||||
name: 'image',
|
name: 'image',
|
||||||
type: 'upload',
|
type: 'upload',
|
||||||
relationTo: 'media',
|
relationTo: 'media',
|
||||||
required: true,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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' },
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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' },
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
Reference in New Issue
Block a user