chore: add remaining db migration and translation scripts
This commit is contained in:
+2367
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
const pages = await payload.find({ collection: 'pages', locale: 'pl', limit: 100 })
|
||||
for (const p of pages.docs) {
|
||||
if (!p.layout) continue;
|
||||
const blocks = p.layout.map(b => b.blockType).join(', ')
|
||||
console.log(`Page: ${p.slug} | Blocks: ${blocks}`)
|
||||
}
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,13 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
const homePl = await payload.find({ collection: 'pages', where: { slug: { equals: 'home' } }, locale: 'pl' })
|
||||
const homeEn = await payload.find({ collection: 'pages', where: { slug: { equals: 'home' } }, locale: 'en' })
|
||||
|
||||
console.log("PL Layout length:", homePl.docs[0]?.layout?.length)
|
||||
console.log("EN Layout length:", homeEn.docs[0]?.layout?.length)
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,15 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
const home = await payload.find({ collection: 'pages', where: { slug: { equals: 'home' } }, locale: 'pl' })
|
||||
const blocks = home.docs[0].layout?.map(b => b.blockType)
|
||||
console.log("HOME PL BLOCKS:", blocks)
|
||||
|
||||
const homeEn = await payload.find({ collection: 'pages', where: { slug: { equals: 'home' } }, locale: 'en' })
|
||||
console.log("HOME EN BLOCKS:", homeEn.docs[0].layout?.map(b => b.blockType))
|
||||
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,10 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
const enPage = await payload.find({ collection: 'pages', locale: 'en', where: { slug: { equals: 'kontakt' } } })
|
||||
console.log("EN KONTAKT BLOCKS:", enPage.docs[0]?.layout?.map(b => b.blockType))
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
import fs from 'fs'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
|
||||
const header = await payload.findGlobal({ slug: 'header', locale: 'pl' })
|
||||
const footer = await payload.findGlobal({ slug: 'footer', locale: 'pl' })
|
||||
const pages = await payload.find({ collection: 'pages', locale: 'pl' })
|
||||
|
||||
fs.writeFileSync('content_dump.json', JSON.stringify({ header, footer, pages }, null, 2))
|
||||
console.log("Dumped to content_dump.json")
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,13 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
import fs from 'fs'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
const home = await payload.find({ collection: 'pages', where: { slug: { equals: 'home' } }, locale: 'pl' })
|
||||
if (home.docs[0]) {
|
||||
fs.writeFileSync('scripts/home_dump.json', JSON.stringify(home.docs[0].layout, null, 2))
|
||||
}
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,15 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
import fs from 'fs'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
const pages = await payload.find({ collection: 'pages', limit: 100, locale: 'pl' })
|
||||
const dump = pages.docs.filter(p => !['home'].includes(p.slug)).map(p => ({
|
||||
slug: p.slug,
|
||||
layout: p.layout
|
||||
}))
|
||||
fs.writeFileSync('scripts/remaining_dump.json', JSON.stringify(dump, null, 2))
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,18 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
const pages = await payload.find({ collection: 'pages', locale: 'all', limit: 100 })
|
||||
for (const p of pages.docs) {
|
||||
if (!p.layout) continue;
|
||||
for (const b of p.layout) {
|
||||
if (b.blockType === 'contactSection') {
|
||||
console.log(`Page ${p.slug} has contactSection in some locale.`)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,124 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
// Helper to remove ids deeply from layout blocks so Payload recreates them
|
||||
function removeIds(obj: any): any {
|
||||
if (Array.isArray(obj)) return obj.map(removeIds)
|
||||
if (obj !== null && typeof obj === 'object') {
|
||||
const newObj: any = {}
|
||||
for (const key of Object.keys(obj)) {
|
||||
if (key !== 'id') {
|
||||
newObj[key] = removeIds(obj[key])
|
||||
}
|
||||
}
|
||||
return newObj
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
|
||||
// 1. Ensure we have an uploaded image to use
|
||||
const mediaDir = path.join(process.cwd(), 'media')
|
||||
const files = fs.readdirSync(mediaDir).filter(f => f.endsWith('.webp'))
|
||||
|
||||
let heroImageId: number | null = null
|
||||
let aboutImageId: number | null = null
|
||||
|
||||
if (files.length > 0) {
|
||||
try {
|
||||
const created1 = await payload.create({
|
||||
collection: 'media',
|
||||
data: { alt: 'Hero background' },
|
||||
file: {
|
||||
data: fs.readFileSync(path.join(mediaDir, files[0])),
|
||||
mimetype: 'image/webp',
|
||||
name: 'hero-bg.webp',
|
||||
size: fs.statSync(path.join(mediaDir, files[0])).size
|
||||
}
|
||||
})
|
||||
heroImageId = created1.id as number
|
||||
console.log(`Uploaded hero image ID: ${heroImageId}`)
|
||||
|
||||
if (files.length > 1) {
|
||||
const created2 = await payload.create({
|
||||
collection: 'media',
|
||||
data: { alt: 'About section image' },
|
||||
file: {
|
||||
data: fs.readFileSync(path.join(mediaDir, files[1])),
|
||||
mimetype: 'image/webp',
|
||||
name: 'about-img.webp',
|
||||
size: fs.statSync(path.join(mediaDir, files[1])).size
|
||||
}
|
||||
})
|
||||
aboutImageId = created2.id as number
|
||||
console.log(`Uploaded about image ID: ${aboutImageId}`)
|
||||
}
|
||||
} catch(e) {
|
||||
console.error("Media upload error", e)
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Load original layouts from snapshot
|
||||
const snapshotPath = path.join(process.cwd(), 'scripts', 'content-snapshot.json')
|
||||
const snapshot = JSON.parse(fs.readFileSync(snapshotPath, 'utf-8'))
|
||||
const homeSnapshot = snapshot.pages.find((p: any) => p.pl.slug === 'home')
|
||||
|
||||
if (!homeSnapshot) {
|
||||
console.error("Home snapshot not found")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const homeDoc = await payload.find({ collection: 'pages', where: { slug: { equals: 'home' } }, locale: 'pl' })
|
||||
if (!homeDoc.docs[0]) {
|
||||
console.error("Home page not found in DB")
|
||||
process.exit(1)
|
||||
}
|
||||
const homeId = homeDoc.docs[0].id
|
||||
|
||||
const updateLayout = async (loc: 'pl' | 'de' | 'en', baseLoc: 'pl' | 'de') => {
|
||||
let layout = removeIds(homeSnapshot[baseLoc].layout)
|
||||
|
||||
// Inject images and EN translations
|
||||
layout = layout.map((block: any) => {
|
||||
if (block.blockType === 'hero') {
|
||||
block.image = heroImageId
|
||||
if (loc === 'en') {
|
||||
block.title = 'Your Trusted Accounting Office in Germany'
|
||||
block.titleAccent = 'We speak English and Polish'
|
||||
block.subtitle = 'Professional bookkeeping, tax settlements, and business consulting for companies in Germany.'
|
||||
block.primaryCta = 'Contact Us'
|
||||
block.secondaryCta = { text: 'Our Services', url: '/uslugi' }
|
||||
}
|
||||
} else if (block.blockType === 'about') {
|
||||
block.image = aboutImageId || heroImageId
|
||||
}
|
||||
return block
|
||||
})
|
||||
|
||||
try {
|
||||
await payload.update({
|
||||
collection: 'pages',
|
||||
id: homeId,
|
||||
locale: loc,
|
||||
data: {
|
||||
title: loc === 'en' ? 'Accounting Services in Germany' : homeSnapshot[baseLoc].title,
|
||||
layout
|
||||
}
|
||||
})
|
||||
console.log(`Successfully fixed home page layout for ${loc}`)
|
||||
} catch(e) {
|
||||
console.error(`Failed to fix ${loc} home page`, e)
|
||||
}
|
||||
}
|
||||
|
||||
await updateLayout('pl', 'pl')
|
||||
await updateLayout('de', 'de')
|
||||
await updateLayout('en', 'pl') // Clone EN from PL base
|
||||
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,43 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
|
||||
const home = await payload.find({ collection: 'pages', where: { slug: { equals: 'home' } }, locale: 'pl' })
|
||||
if (home.docs[0] && home.docs[0].layout) {
|
||||
const enLayout = home.docs[0].layout.map(block => {
|
||||
// Remove id so Payload generates a new one
|
||||
const { id, blockName, ...rest } = block as any
|
||||
if (rest.blockType === 'hero') {
|
||||
return {
|
||||
...rest,
|
||||
title: 'Your Trusted Accounting Office in Germany',
|
||||
titleAccent: 'We speak English and Polish',
|
||||
subtitle: 'Professional bookkeeping, tax settlements, and business consulting for companies in Germany.',
|
||||
primaryCta: 'Contact Us',
|
||||
secondaryCta: { text: 'Our Services', url: '/uslugi' }
|
||||
}
|
||||
}
|
||||
return rest
|
||||
})
|
||||
|
||||
try {
|
||||
await payload.update({
|
||||
collection: 'pages',
|
||||
id: home.docs[0].id,
|
||||
locale: 'en',
|
||||
data: {
|
||||
title: 'Accounting Services in Germany',
|
||||
layout: enLayout
|
||||
}
|
||||
})
|
||||
console.log("Successfully fixed EN home page")
|
||||
} catch (e) {
|
||||
console.error("Error fixing EN home page", e)
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,69 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
|
||||
const plPages = await payload.find({ collection: 'pages', locale: 'pl', where: { slug: { equals: 'kontakt' } } })
|
||||
const plPage = plPages.docs[0]
|
||||
if (!plPage) return;
|
||||
|
||||
const layout = plPage.layout || []
|
||||
|
||||
const enLayout = layout.map(block => {
|
||||
const b = JSON.parse(JSON.stringify(block))
|
||||
// strip populated
|
||||
for (const key of Object.keys(b)) {
|
||||
if (b[key] && typeof b[key] === 'object' && b[key].id !== undefined) {
|
||||
b[key] = b[key].id
|
||||
}
|
||||
}
|
||||
if (b.blockType === 'pageHeader') {
|
||||
b.title = "Contact"
|
||||
b.subtitle = "Do you have questions about tax settlement in Germany? Contact us — we will be happy to help."
|
||||
}
|
||||
if (b.blockType === 'contactSection') {
|
||||
b.address = "TO BE CONFIRMED BY CLIENT"
|
||||
b.hours = "Mon-Fri: 9:00 - 17:00\nBy appointment"
|
||||
}
|
||||
return b
|
||||
})
|
||||
|
||||
await payload.update({
|
||||
collection: 'pages',
|
||||
id: plPage.id,
|
||||
locale: 'en',
|
||||
data: { layout: enLayout }
|
||||
})
|
||||
console.log("Forced EN layout for kontakt")
|
||||
|
||||
const deLayout = layout.map(block => {
|
||||
const b = JSON.parse(JSON.stringify(block))
|
||||
// strip populated
|
||||
for (const key of Object.keys(b)) {
|
||||
if (b[key] && typeof b[key] === 'object' && b[key].id !== undefined) {
|
||||
b[key] = b[key].id
|
||||
}
|
||||
}
|
||||
if (b.blockType === 'pageHeader') {
|
||||
b.title = "Kontakt"
|
||||
b.subtitle = "Haben Sie Fragen zur Steuererklärung in Deutschland? Kontaktieren Sie uns — wir helfen Ihnen gerne weiter."
|
||||
}
|
||||
if (b.blockType === 'contactSection') {
|
||||
b.address = "TO BE CONFIRMED BY CLIENT"
|
||||
b.hours = "Mo-Fr: 9:00 - 17:00\nNach Vereinbarung"
|
||||
}
|
||||
return b
|
||||
})
|
||||
|
||||
await payload.update({
|
||||
collection: 'pages',
|
||||
id: plPage.id,
|
||||
locale: 'de',
|
||||
data: { layout: deLayout }
|
||||
})
|
||||
console.log("Forced DE layout for kontakt")
|
||||
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,309 @@
|
||||
[
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600ed",
|
||||
"title": "Odbierz zwrot podatku z Niemiec",
|
||||
"titleAccent": "Rozliczymy Cię po polsku",
|
||||
"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",
|
||||
"image": {
|
||||
"id": 16,
|
||||
"alt": "Obraz",
|
||||
"updatedAt": "2026-09-17T22:35:53.986Z",
|
||||
"createdAt": "2026-09-17T22:35:53.986Z",
|
||||
"url": "/api/media/file/d0c63f69-9a73-48ba-a03b-15dafd55b5cf-3.webp",
|
||||
"thumbnailURL": null,
|
||||
"filename": "d0c63f69-9a73-48ba-a03b-15dafd55b5cf-3.webp",
|
||||
"mimeType": "image/webp",
|
||||
"filesize": 69160,
|
||||
"width": 1536,
|
||||
"height": 1024,
|
||||
"focalX": 50,
|
||||
"focalY": 50
|
||||
},
|
||||
"imageSecondary": {
|
||||
"id": 1,
|
||||
"alt": "Obraz",
|
||||
"updatedAt": "2026-09-17T22:31:47.560Z",
|
||||
"createdAt": "2026-09-17T22:31:47.559Z",
|
||||
"url": "/api/media/file/38670a24-644b-42db-ac36-fd77f78352ba-2.webp",
|
||||
"thumbnailURL": null,
|
||||
"filename": "38670a24-644b-42db-ac36-fd77f78352ba-2.webp",
|
||||
"mimeType": "image/webp",
|
||||
"filesize": 74328,
|
||||
"width": 1536,
|
||||
"height": 1024,
|
||||
"focalX": 50,
|
||||
"focalY": 50
|
||||
},
|
||||
"blockName": null,
|
||||
"blockType": "hero",
|
||||
"secondaryCta": {
|
||||
"text": "Zobacz jak to działa",
|
||||
"url": "#jak-to-dziala"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600f2",
|
||||
"blockName": null,
|
||||
"items": [
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600ee",
|
||||
"text": "Działamy w ramach b.b.h.",
|
||||
"icon": "shield"
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600ef",
|
||||
"text": "Doradztwo 100% po polsku",
|
||||
"icon": "message"
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600f0",
|
||||
"text": "Wsparcie w Urzędzie Skarbowym",
|
||||
"icon": "check"
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600f1",
|
||||
"text": "Wieloletnie doświadczenie",
|
||||
"icon": "star"
|
||||
}
|
||||
],
|
||||
"blockType": "trustBar"
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600f5",
|
||||
"eyebrow": null,
|
||||
"title": "Rozliczenia bez bariery językowej",
|
||||
"description": "Niemieckie prawo podatkowe bywa skomplikowane. Przeprowadzimy Cię przez cały proces w Twoim ojczystym języku, dbając o maksymalny zwrot i zero stresu.",
|
||||
"image": {
|
||||
"id": 16,
|
||||
"alt": "Obraz",
|
||||
"updatedAt": "2026-09-17T22:35:53.986Z",
|
||||
"createdAt": "2026-09-17T22:35:53.986Z",
|
||||
"url": "/api/media/file/d0c63f69-9a73-48ba-a03b-15dafd55b5cf-3.webp",
|
||||
"thumbnailURL": null,
|
||||
"filename": "d0c63f69-9a73-48ba-a03b-15dafd55b5cf-3.webp",
|
||||
"mimeType": "image/webp",
|
||||
"filesize": 69160,
|
||||
"width": 1536,
|
||||
"height": 1024,
|
||||
"focalX": 50,
|
||||
"focalY": 50
|
||||
},
|
||||
"blockName": null,
|
||||
"features": [
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600f3",
|
||||
"text": "Bez barier językowych"
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600f4",
|
||||
"text": "Indywidualne podejście"
|
||||
}
|
||||
],
|
||||
"blockType": "uniqueValue"
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600f9",
|
||||
"title": null,
|
||||
"subtitle": null,
|
||||
"blockName": null,
|
||||
"items": [
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600f6",
|
||||
"stat": "20+",
|
||||
"label": "Lat doświadczenia",
|
||||
"description": "w prawie podatkowym i rozliczeniach dla Polaków w Niemczech"
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600f7",
|
||||
"stat": "100%",
|
||||
"label": "Obsługa po polsku",
|
||||
"description": "bezpośredni kontakt i wyjaśnienie każdej kwestii podatkowej"
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600f8",
|
||||
"stat": "§4 Nr. 11",
|
||||
"label": "Stowarzyszenie b.b.h.",
|
||||
"description": "oficjalna podstawa prawna doradztwa zgodnie ze StBerG"
|
||||
}
|
||||
],
|
||||
"blockType": "stats"
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600fd",
|
||||
"eyebrow": "Usługi",
|
||||
"title": "Czym się zajmujemy",
|
||||
"blockName": null,
|
||||
"services": [
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600fa",
|
||||
"title": "Deklaracje podatkowe",
|
||||
"description": "Kompleksowe sporządzenie i złożenie zeznania rocznego (Einkommensteuererklärung).",
|
||||
"linkText": "Zobacz więcej",
|
||||
"linkUrl": null
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600fb",
|
||||
"title": "Kontrola decyzji",
|
||||
"description": "Szczegółowa weryfikacja decyzji podatkowej (Steuerbescheid) z urzędu skarbowego.",
|
||||
"linkText": "Zobacz więcej",
|
||||
"linkUrl": null
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600fc",
|
||||
"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",
|
||||
"linkUrl": null
|
||||
}
|
||||
],
|
||||
"blockType": "servicesList"
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f660101",
|
||||
"eyebrow": null,
|
||||
"title": "Jak to działa",
|
||||
"subtitle": "Prosty i przejrzysty proces rozliczenia w 3 krokach — bez zbędnych formalności.",
|
||||
"ctaText": "Zobacz cały proces",
|
||||
"ctaUrl": "/pl/jak-sie-umowic",
|
||||
"image": {
|
||||
"id": 15,
|
||||
"alt": "Obraz",
|
||||
"updatedAt": "2026-09-17T22:35:53.848Z",
|
||||
"createdAt": "2026-09-17T22:35:53.848Z",
|
||||
"url": "/api/media/file/ac4e2098-0b88-4f6f-98ed-72461a36784c-3.webp",
|
||||
"thumbnailURL": null,
|
||||
"filename": "ac4e2098-0b88-4f6f-98ed-72461a36784c-3.webp",
|
||||
"mimeType": "image/webp",
|
||||
"filesize": 55450,
|
||||
"width": 1536,
|
||||
"height": 1024,
|
||||
"focalX": 50,
|
||||
"focalY": 50
|
||||
},
|
||||
"blockName": null,
|
||||
"steps": [
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600fe",
|
||||
"title": "Napisz na WhatsApp",
|
||||
"description": "Wyślij krótką wiadomość lub zadzwoń, by rozpocząć proces."
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f6600ff",
|
||||
"title": "Przekaż dokumenty",
|
||||
"description": "Prześlij nam wymagane dokumenty bezpiecznie online lub dostarcz osobiście."
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f660100",
|
||||
"title": "Odbierz zwrot",
|
||||
"description": "Zajmiemy się resztą i poinformujemy Cię o spodziewanym zwrocie z urzędu skarbowego."
|
||||
}
|
||||
],
|
||||
"blockType": "process"
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f660102",
|
||||
"eyebrow": "Zaufanie",
|
||||
"title": "Zaufanie i bezpieczeństwo",
|
||||
"description": "Działamy 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.",
|
||||
"logo": null,
|
||||
"linkText": "Poznaj stowarzyszenie",
|
||||
"linkUrl": "/pl/o-nas",
|
||||
"blockName": null,
|
||||
"trustPoints": [],
|
||||
"blockType": "affiliation"
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f660106",
|
||||
"title": "Opinie klientów",
|
||||
"blockName": null,
|
||||
"testimonials": [
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f660103",
|
||||
"quote": "Pan Janusz pomógł mi odzyskać podatek za 3 ostatnie lata. Wszystko jasno wytłumaczone po polsku.",
|
||||
"author": "Piotr, Monachium",
|
||||
"source": null
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f660104",
|
||||
"quote": "Szybki kontakt przez WhatsApp i pełne wsparcie przy odwołaniu od decyzji Finanzamt. Polecam!",
|
||||
"author": "Anna, Berlin",
|
||||
"source": null
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f660105",
|
||||
"quote": "Ogromna wiedza o ulgach dla pracowników budowlanych. Zwrot był znacznie wyższy niż się spodziewałem.",
|
||||
"author": "Krzysztof, Dortmund",
|
||||
"source": null
|
||||
}
|
||||
],
|
||||
"blockType": "testimonials"
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f66010f",
|
||||
"title": "Częste pytania",
|
||||
"ctaText": null,
|
||||
"ctaUrl": null,
|
||||
"blockName": null,
|
||||
"questions": [
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f660107",
|
||||
"category": null,
|
||||
"question": "Czy muszę przyjeżdżać osobiście do biura?",
|
||||
"answer": "Nie, wszystkie formalności możemy załatwić w 100% zdalnie. Dokumenty przesyłasz online przez bezpieczne kanały, a konsultacje prowadzimy telefonicznie lub przez WhatsApp."
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f660108",
|
||||
"category": null,
|
||||
"question": "Do kiedy muszę złożyć deklarację podatkową w Niemczech?",
|
||||
"answer": "Standardowy termin to 31 sierpnia kolejnego roku, jednak korzystając z usług doradcy podatkowego, zyskujesz dodatkowy czas – nawet do końca lutego lub maja następnego roku (w zależności od ustaleń na dany rok)."
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f660109",
|
||||
"category": null,
|
||||
"question": "Jak długo czeka się na zwrot podatku z urzędu skarbowego?",
|
||||
"answer": "Czas oczekiwania na decyzję i przelew zależy od urzędu skarbowego – najczęściej wynosi on od 3 do 8 tygodni po złożeniu kompletnej deklaracji."
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f66010a",
|
||||
"category": null,
|
||||
"question": "Czy załatwiacie również zasiłek Kindergeld?",
|
||||
"answer": "Tak, pomagamy w formalnościach związanych z pozyskaniem zasiłku rodzinnego Kindergeld dla pracowników zatrudnionych na terenie Niemiec."
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f66010b",
|
||||
"category": null,
|
||||
"question": "Jakie dokumenty są potrzebne do rozliczenia?",
|
||||
"answer": "Do podstawowego rozliczenia potrzebujesz Lohnsteuerbescheinigung (karty podatkowej od pracodawcy), zaświadczenia o dochodach w Polsce (EU/EWR) oraz dokumentów potwierdzających koszty (np. dojazdy, podwójne gospodarstwo)."
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f66010c",
|
||||
"category": null,
|
||||
"question": "Czy muszę przychodzić osobiście do biura?",
|
||||
"answer": "Nie, cały proces możemy przeprowadzić w 100% online. Dokumenty możesz przesłać nam bezpiecznie przez WhatsApp, e-mail lub pocztą tradycyjną."
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f66010d",
|
||||
"category": null,
|
||||
"question": "Ile kosztuje rozliczenie podatku z Niemiec?",
|
||||
"answer": "Koszt rozliczenia ustalamy indywidualnie, bazując na stopniu skomplikowania sprawy i rodzaju deklarowanych dochodów. Zawsze informujemy o cenie przed rozpoczęciem prac."
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f66010e",
|
||||
"category": null,
|
||||
"question": "Czy pomagacie przy wnioskach o Kindergeld?",
|
||||
"answer": "Tak, pomagamy w formalnościach związanych z pozyskaniem zasiłku rodzinnego Kindergeld dla pracowników zatrudnionych na terenie Niemiec."
|
||||
}
|
||||
],
|
||||
"blockType": "faqTeaser"
|
||||
},
|
||||
{
|
||||
"id": "6aac6aa8a53b4a5a4f660110",
|
||||
"eyebrow": "Kontakt",
|
||||
"title": "Gotowy na wyższy zwrot podatku?",
|
||||
"subtitle": "Napisz do nas już teraz. Odpowiemy najszybciej jak to możliwe.",
|
||||
"buttonText": "Rozpocznij na WhatsApp",
|
||||
"blockName": null,
|
||||
"blockType": "finalCta"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
const forms = await payload.find({ collection: 'forms' })
|
||||
console.log("FORMS:", forms.docs.map(f => ({ id: f.id, title: f.title })))
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,473 @@
|
||||
[
|
||||
{
|
||||
"slug": "uslugi",
|
||||
"layout": [
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd567a",
|
||||
"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.",
|
||||
"image": {
|
||||
"id": 10,
|
||||
"alt": "Obraz",
|
||||
"updatedAt": "2026-09-17T22:35:52.987Z",
|
||||
"createdAt": "2026-09-17T22:35:52.987Z",
|
||||
"url": "/api/media/file/38670a24-644b-42db-ac36-fd77f78352ba-3.webp",
|
||||
"thumbnailURL": null,
|
||||
"filename": "38670a24-644b-42db-ac36-fd77f78352ba-3.webp",
|
||||
"mimeType": "image/webp",
|
||||
"filesize": 74328,
|
||||
"width": 1536,
|
||||
"height": 1024,
|
||||
"focalX": 50,
|
||||
"focalY": 50
|
||||
},
|
||||
"blockName": null,
|
||||
"blockType": "pageHeader"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd5682",
|
||||
"title": "Czym się zajmujemy",
|
||||
"subtitle": "Zapewniamy rzetelne i kompleksowe wsparcie na każdym etapie rozliczenia podatku dochodowego w Niemczech.",
|
||||
"blockName": null,
|
||||
"services": [
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd567b",
|
||||
"title": "Sporządzenie deklaracji podatkowej (Einkommensteuererklärung)",
|
||||
"anchor": "sporzadzenie-deklaracji-podatkowej-einkommensteuererklarung",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd567c",
|
||||
"title": "Wstępne obliczenie przewidywanego zwrotu",
|
||||
"anchor": "wstepne-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 urzędu skarbowego.",
|
||||
"icon": "calculator"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd567d",
|
||||
"title": "Kontrola decyzji podatkowej (Steuerbescheid)",
|
||||
"anchor": "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"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd567e",
|
||||
"title": "Składanie odwołań (Einspruch)",
|
||||
"anchor": "skladanie-odwolan-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"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd567f",
|
||||
"title": "Całoroczne doradztwo podatkowe",
|
||||
"anchor": "caloroczne-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"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd5680",
|
||||
"title": "Doradztwo przy wyborze klasy podatkowej (Steuerklasse)",
|
||||
"anchor": "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"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd5681",
|
||||
"title": "Ulgi, dopłaty i odliczenia (Kindergeld, dojazdy, podwójne gospodarstwo)",
|
||||
"anchor": "ulgi-doplaty-i-odliczenia-kindergeld-dojazdy-podwojne-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": "servicesDetailed"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd568c",
|
||||
"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",
|
||||
"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)",
|
||||
"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).",
|
||||
"blockName": null,
|
||||
"eligibleItems": [
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd5683",
|
||||
"text": "Pracownicy zatrudnieni na umowę o pracę (Arbeitnehmer) oraz pracownicy tymczasowi"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd5684",
|
||||
"text": "Urzędnicy państwowi (Beamte)"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd5685",
|
||||
"text": "Emeryci i renciści (Rentner und Pensionäre)"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd5686",
|
||||
"text": "Osoby bezrobotne oraz pobierające zasiłki socjalne"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd5687",
|
||||
"text": "Odbiorcy świadczeń rodzinnych (Kindergeld, Elterngeld) oraz alimentów"
|
||||
}
|
||||
],
|
||||
"excludedItems": [
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd5688",
|
||||
"text": "Osoby prowadzące jednoosobową działalność gospodarczą (Gewerbe)"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd5689",
|
||||
"text": "Przedsiębiorcy, spółki oraz osoby samozatrudnione (Selbständige)"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd568a",
|
||||
"text": "Osoby uzyskujące przychody z działalności rolniczej i leśnej (Land- und Forstwirtschaft)"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd568b",
|
||||
"text": "Osoby z przychodami z najmu lub kapitału przekraczającymi limity 18 000 € / 36 000 €"
|
||||
}
|
||||
],
|
||||
"blockType": "eligibility"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd568d",
|
||||
"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",
|
||||
"image": {
|
||||
"id": 10,
|
||||
"alt": "Obraz",
|
||||
"updatedAt": "2026-09-17T22:35:52.987Z",
|
||||
"createdAt": "2026-09-17T22:35:52.987Z",
|
||||
"url": "/api/media/file/38670a24-644b-42db-ac36-fd77f78352ba-3.webp",
|
||||
"thumbnailURL": null,
|
||||
"filename": "38670a24-644b-42db-ac36-fd77f78352ba-3.webp",
|
||||
"mimeType": "image/webp",
|
||||
"filesize": 74328,
|
||||
"width": 1536,
|
||||
"height": 1024,
|
||||
"focalX": 50,
|
||||
"focalY": 50
|
||||
},
|
||||
"blockName": null,
|
||||
"blockType": "finalCta"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"slug": "jak-sie-umowic",
|
||||
"layout": [
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8d03",
|
||||
"title": "Jak się umówić",
|
||||
"subtitle": "Prosty, przejrzysty i w 100% zdalny proces rozliczenia podatkowego. Wszystko załatwisz bez wychodzenia z domu.",
|
||||
"image": {
|
||||
"id": 9,
|
||||
"alt": "About section image",
|
||||
"updatedAt": "2026-09-17T22:33:12.549Z",
|
||||
"createdAt": "2026-09-17T22:33:12.549Z",
|
||||
"url": "/api/media/file/about-img.webp",
|
||||
"thumbnailURL": null,
|
||||
"filename": "about-img.webp",
|
||||
"mimeType": "image/webp",
|
||||
"filesize": 72208,
|
||||
"width": 1536,
|
||||
"height": 1024,
|
||||
"focalX": 50,
|
||||
"focalY": 50
|
||||
},
|
||||
"blockName": null,
|
||||
"blockType": "pageHeader"
|
||||
},
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8d07",
|
||||
"eyebrow": null,
|
||||
"title": "Rozliczenie w 3 prostych krokach",
|
||||
"subtitle": "Zapewniamy pełne wsparcie i jasną komunikację na każdym etapie.",
|
||||
"ctaText": null,
|
||||
"ctaUrl": null,
|
||||
"image": {
|
||||
"id": 8,
|
||||
"alt": "Hero background",
|
||||
"updatedAt": "2026-09-17T22:33:12.415Z",
|
||||
"createdAt": "2026-09-17T22:33:12.414Z",
|
||||
"url": "/api/media/file/hero-bg.webp",
|
||||
"thumbnailURL": null,
|
||||
"filename": "hero-bg.webp",
|
||||
"mimeType": "image/webp",
|
||||
"filesize": 74328,
|
||||
"width": 1536,
|
||||
"height": 1024,
|
||||
"focalX": 50,
|
||||
"focalY": 50
|
||||
},
|
||||
"blockName": null,
|
||||
"steps": [
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8d04",
|
||||
"title": "Napisz na WhatsApp",
|
||||
"description": "Skontaktuj się z nami bezpośrednio przez WhatsApp lub formularz poniżej, aby rozpocząć i ustalić szczegóły Twojej sytuacji podatkowej."
|
||||
},
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8d05",
|
||||
"title": "Przekaż dokumenty",
|
||||
"description": "Prześlij zaświadczenia o dochodach (Lohnsteuerbescheinigung, EU/EWR), podatkowy numer identyfikacyjny (IdNr) lub PESEL oraz ewentualne potwierdzenia kosztów."
|
||||
},
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8d06",
|
||||
"title": "Odbierz gotowe rozliczenie do podpisu",
|
||||
"description": "Przygotowujemy kompletną deklarację, wyliczamy przewidywany zwrot z urzędu skarbowego i przesyłamy gotowy dokument do Twojej akceptacji i podpisu."
|
||||
}
|
||||
],
|
||||
"blockType": "process"
|
||||
},
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8d08",
|
||||
"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",
|
||||
"blockName": null,
|
||||
"blockType": "contactInfo"
|
||||
},
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8d09",
|
||||
"eyebrow": null,
|
||||
"title": "Chcesz szybko odzyskać podatek?",
|
||||
"subtitle": "Nie zwlekaj z rozliczeniem. Napisz teraz, a zajmiemy się formalnościami od początku do końca.",
|
||||
"buttonText": "Napisz na WhatsApp",
|
||||
"image": {
|
||||
"id": 9,
|
||||
"alt": "About section image",
|
||||
"updatedAt": "2026-09-17T22:33:12.549Z",
|
||||
"createdAt": "2026-09-17T22:33:12.549Z",
|
||||
"url": "/api/media/file/about-img.webp",
|
||||
"thumbnailURL": null,
|
||||
"filename": "about-img.webp",
|
||||
"mimeType": "image/webp",
|
||||
"filesize": 72208,
|
||||
"width": 1536,
|
||||
"height": 1024,
|
||||
"focalX": 50,
|
||||
"focalY": 50
|
||||
},
|
||||
"blockName": null,
|
||||
"blockType": "finalCta"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"slug": "o-nas",
|
||||
"layout": [
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8ce2",
|
||||
"title": "O nas",
|
||||
"subtitle": "Ponad 20 lat rzetelnego wsparcia podatkowego dla Polaków pracujących w Niemczech.",
|
||||
"image": {
|
||||
"id": 7,
|
||||
"alt": "Obraz",
|
||||
"updatedAt": "2026-09-17T22:31:48.622Z",
|
||||
"createdAt": "2026-09-17T22:31:48.622Z",
|
||||
"url": "/api/media/file/d0c63f69-9a73-48ba-a03b-15dafd55b5cf-2.webp",
|
||||
"thumbnailURL": null,
|
||||
"filename": "d0c63f69-9a73-48ba-a03b-15dafd55b5cf-2.webp",
|
||||
"mimeType": "image/webp",
|
||||
"filesize": 69160,
|
||||
"width": 1536,
|
||||
"height": 1024,
|
||||
"focalX": 50,
|
||||
"focalY": 50
|
||||
},
|
||||
"blockName": null,
|
||||
"blockType": "pageHeader"
|
||||
},
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8ce9",
|
||||
"title": "Janusz Kędzierski",
|
||||
"image": {
|
||||
"id": 6,
|
||||
"alt": "Obraz",
|
||||
"updatedAt": "2026-09-17T22:31:48.453Z",
|
||||
"createdAt": "2026-09-17T22:31:48.453Z",
|
||||
"url": "/api/media/file/ac4e2098-0b88-4f6f-98ed-72461a36784c-2.webp",
|
||||
"thumbnailURL": null,
|
||||
"filename": "ac4e2098-0b88-4f6f-98ed-72461a36784c-2.webp",
|
||||
"mimeType": "image/webp",
|
||||
"filesize": 55450,
|
||||
"width": 1536,
|
||||
"height": 1024,
|
||||
"focalX": 50,
|
||||
"focalY": 50
|
||||
},
|
||||
"blockName": null,
|
||||
"paragraphs": [
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8ce3",
|
||||
"text": "Od ponad dwóch dekad pomagam polskim pracownikom i specjalistom w Niemczech w bezpiecznym, bezstresowym i w pełni legalnym rozliczaniu podatków dochodowych przed urzędem skarbowym."
|
||||
},
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8ce4",
|
||||
"text": "Moim celem jest zdjęcie z Ciebie ciężaru skomplikowanych niemieckich przepisów skarbowych. Cały proces prowadzimy w języku polskim — od wstępnej analizy dokumentów, przez precyzyjne wyliczenia ulg i kosztów uzyskania przychodu, aż po weryfikację końcowej decyzji podatkowej."
|
||||
}
|
||||
],
|
||||
"credentials": [
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8ce5",
|
||||
"text": "Ponad 20 lat praktyki w niemieckim prawie podatkowym"
|
||||
},
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8ce6",
|
||||
"text": "Beratungsstellenleiter w b.b.h. Lohnsteuerhilfeverein e.V."
|
||||
},
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8ce7",
|
||||
"text": "Pełna obsługa w języku polskim i niemieckim"
|
||||
},
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8ce8",
|
||||
"text": "Maksymalizacja zwrotu podatku i zero stresu formalnego"
|
||||
}
|
||||
],
|
||||
"blockType": "bio"
|
||||
},
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8ceb",
|
||||
"title": "Nasz zespół",
|
||||
"subtitle": "Poznaj ekspertów, którzy z zaangażowaniem dbają o Twoje finanse. Każdy z nas wnosi wieloletnie doświadczenie, by zagwarantować Ci spokój i bezpieczeństwo.",
|
||||
"blockName": null,
|
||||
"members": [
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8cea",
|
||||
"name": "Janusz Kędzierski",
|
||||
"role": "Właściciel / Beratungsstellenleiter",
|
||||
"description": "Wieloletni praktyk prawa podatkowego ze specjalizacją w doradztwie dla polskich pracowników w Niemczech.",
|
||||
"photo": null,
|
||||
"bookingUrl": "/kontakt"
|
||||
}
|
||||
],
|
||||
"blockType": "team"
|
||||
},
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8cec",
|
||||
"eyebrow": null,
|
||||
"title": "Gotowy na rozliczenie podatku?",
|
||||
"subtitle": "Skontaktuj się z nami już dziś. Przeanalizujemy Twoją sytuację i pomożemy uzyskać maksymalny zwrot z niemieckiego urzędu skarbowego.",
|
||||
"buttonText": "Napisz na WhatsApp",
|
||||
"image": {
|
||||
"id": 8,
|
||||
"alt": "Hero background",
|
||||
"updatedAt": "2026-09-17T22:33:12.415Z",
|
||||
"createdAt": "2026-09-17T22:33:12.414Z",
|
||||
"url": "/api/media/file/hero-bg.webp",
|
||||
"thumbnailURL": null,
|
||||
"filename": "hero-bg.webp",
|
||||
"mimeType": "image/webp",
|
||||
"filesize": 74328,
|
||||
"width": 1536,
|
||||
"height": 1024,
|
||||
"focalX": 50,
|
||||
"focalY": 50
|
||||
},
|
||||
"blockName": null,
|
||||
"blockType": "finalCta"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"slug": "faq",
|
||||
"layout": [
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8cdf",
|
||||
"text": "Treść w przygotowaniu",
|
||||
"blockName": null,
|
||||
"blockType": "TextSection"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"slug": "kontakt",
|
||||
"layout": [
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd56a0",
|
||||
"title": "Kontakt",
|
||||
"subtitle": "Masz pytania dotyczące rozliczenia podatkowego w Niemczech? Skontaktuj się z nami — chętnie pomożemy.",
|
||||
"image": {
|
||||
"id": 5,
|
||||
"alt": "Obraz",
|
||||
"updatedAt": "2026-09-17T22:31:48.145Z",
|
||||
"createdAt": "2026-09-17T22:31:48.145Z",
|
||||
"url": "/api/media/file/9a2983cc-482c-4ff7-9f1d-31679e4dfc56-2.webp",
|
||||
"thumbnailURL": null,
|
||||
"filename": "9a2983cc-482c-4ff7-9f1d-31679e4dfc56-2.webp",
|
||||
"mimeType": "image/webp",
|
||||
"filesize": 70318,
|
||||
"width": 1672,
|
||||
"height": 941,
|
||||
"focalX": 50,
|
||||
"focalY": 50
|
||||
},
|
||||
"blockName": null,
|
||||
"blockType": "pageHeader"
|
||||
},
|
||||
{
|
||||
"id": "6aac6b4ada77e55afbdd56a1",
|
||||
"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",
|
||||
"blockName": null,
|
||||
"blockType": "contactInfo"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"slug": "stowarzyszenie",
|
||||
"layout": [
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8cd6",
|
||||
"text": "Treść w przygotowaniu",
|
||||
"blockName": null,
|
||||
"blockType": "TextSection"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"slug": "polityka-prywatnosci",
|
||||
"layout": [
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8cd3",
|
||||
"text": "Treść w przygotowaniu",
|
||||
"blockName": null,
|
||||
"blockType": "TextSection"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"slug": "polityka-cookies",
|
||||
"layout": [
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8cd0",
|
||||
"text": "Treść w przygotowaniu",
|
||||
"blockName": null,
|
||||
"blockType": "TextSection"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"slug": "impressum",
|
||||
"layout": [
|
||||
{
|
||||
"id": "6aac6afbef4df35aa15f8ccd",
|
||||
"text": "Treść w przygotowaniu",
|
||||
"blockName": null,
|
||||
"blockType": "TextSection"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,77 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
|
||||
const snapshotPath = path.join(process.cwd(), 'scripts', 'content-snapshot.json')
|
||||
const snapshot = JSON.parse(fs.readFileSync(snapshotPath, 'utf-8'))
|
||||
const kontaktSnap = snapshot.pages.find((p: any) => p.pl.slug === 'kontakt')
|
||||
|
||||
if (!kontaktSnap) {
|
||||
console.error("No kontakt in snapshot")
|
||||
return
|
||||
}
|
||||
|
||||
// Find actual page ID
|
||||
const dbPages = await payload.find({ collection: 'pages', where: { slug: { equals: 'kontakt' } } })
|
||||
const dbPage = dbPages.docs[0]
|
||||
|
||||
if (!dbPage) {
|
||||
console.error("No kontakt in DB")
|
||||
return
|
||||
}
|
||||
|
||||
for (const loc of ['pl', 'de', 'en']) {
|
||||
let layout = kontaktSnap[loc]?.layout
|
||||
if (!layout && loc === 'en') {
|
||||
// fallback to PL for layout structure
|
||||
layout = JSON.parse(JSON.stringify(kontaktSnap.pl.layout))
|
||||
}
|
||||
if (!layout) continue;
|
||||
|
||||
const newLayout = layout.map((block: any) => {
|
||||
const b = { ...block }
|
||||
delete b.id // VERY IMPORTANT to avoid SQLite unique constraint errors or table mismatches
|
||||
|
||||
// If we find contactInfo, UPGRADE it to contactSection and attach form 1!
|
||||
if (b.blockType === 'contactInfo' || b.blockType === 'contactSection') {
|
||||
b.blockType = 'contactSection'
|
||||
b.form = 1 // We created form 1 earlier
|
||||
}
|
||||
|
||||
// Fix EN translations if this is EN
|
||||
if (loc === 'en') {
|
||||
if (b.blockType === 'pageHeader') {
|
||||
b.title = "Contact"
|
||||
b.subtitle = "Do you have questions about tax settlement in Germany? Contact us — we will be happy to help."
|
||||
}
|
||||
if (b.blockType === 'contactSection') {
|
||||
b.address = "TO BE CONFIRMED BY CLIENT"
|
||||
b.hours = "Mon-Fri: 9:00 - 17:00\nBy appointment"
|
||||
}
|
||||
}
|
||||
|
||||
return b
|
||||
})
|
||||
|
||||
const title = loc === 'en' ? 'Contact' : (loc === 'de' ? 'Kontakt' : 'Kontakt')
|
||||
|
||||
await payload.update({
|
||||
collection: 'pages',
|
||||
id: dbPage.id,
|
||||
locale: loc as any,
|
||||
data: {
|
||||
title,
|
||||
layout: newLayout
|
||||
}
|
||||
})
|
||||
console.log(`Restored kontakt in ${loc}`)
|
||||
}
|
||||
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,16 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
const docs = await payload.find({
|
||||
collection: 'pages',
|
||||
where: { slug: { equals: 'uslugi' } }, // wait, slug in DE might be different! Let's query by ID 2
|
||||
locale: 'de'
|
||||
})
|
||||
const doc = docs.docs[0]
|
||||
const cta = doc.layout?.find(b => b.blockType === 'finalCta') as any
|
||||
console.log("USLUGI DE CTA IMAGE:", typeof cta?.image, cta?.image?.id || cta?.image)
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,75 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
|
||||
// 1. UPDATE GLOBALS
|
||||
await payload.updateGlobal({
|
||||
slug: 'footer',
|
||||
locale: 'en',
|
||||
data: {
|
||||
copyright: '© 2024 Accounting Kędzierski. All rights reserved.',
|
||||
association: {
|
||||
title: 'Member of b.b.h. Lohnsteuerhilfeverein e.V.'
|
||||
},
|
||||
creator: {
|
||||
label: 'Created by'
|
||||
}
|
||||
}
|
||||
})
|
||||
console.log("Footer updated")
|
||||
|
||||
// 2. UPDATE PAGES (Titles and basic fields)
|
||||
const translations: Record<string, any> = {
|
||||
'home': { title: 'Home' },
|
||||
'uslugi': { title: 'Services' },
|
||||
'jak-sie-umowic': { title: 'How to make an appointment' },
|
||||
'o-nas': { title: 'About Us' },
|
||||
'faq': { title: 'FAQ' },
|
||||
'kontakt': { title: 'Contact' },
|
||||
'stowarzyszenie': { title: 'Association' },
|
||||
'polityka-prywatnosci': { title: 'Privacy Policy' },
|
||||
'polityka-cookies': { title: 'Cookie Policy' },
|
||||
'impressum': { title: 'Imprint' }
|
||||
}
|
||||
|
||||
const pages = await payload.find({ collection: 'pages', limit: 100, locale: 'pl' })
|
||||
for (const page of pages.docs) {
|
||||
const slug = page.slug || ''
|
||||
const enData = translations[slug] || { title: page.title }
|
||||
|
||||
// For Home, let's keep the hero translations if it's there
|
||||
if (slug === 'home') {
|
||||
enData.layout = page.layout?.map(block => {
|
||||
if (block.blockType === 'hero') {
|
||||
return {
|
||||
...block,
|
||||
title: 'Your Trusted Accounting Office in Germany',
|
||||
titleAccent: 'We speak English and Polish',
|
||||
subtitle: 'Professional bookkeeping, tax settlements, and business consulting for companies in Germany.',
|
||||
primaryCta: 'Contact Us',
|
||||
secondaryCta: { text: 'Our Services', url: '/uslugi' }
|
||||
}
|
||||
}
|
||||
return block
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
await payload.update({
|
||||
collection: 'pages',
|
||||
id: page.id,
|
||||
locale: 'en',
|
||||
data: enData
|
||||
})
|
||||
console.log(`Page updated: ${slug}`)
|
||||
} catch (e) {
|
||||
console.error(`Error updating page ${slug}`, e)
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Done")
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,271 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
|
||||
const translations: Record<string, Record<string, any>> = {
|
||||
'uslugi': {
|
||||
en: {
|
||||
pageHeader: { title: "Comprehensive tax settlements in Germany", subtitle: "Professional tax return preparation, tax assessment verification, and ongoing support." },
|
||||
servicesDetailed: {
|
||||
title: "What we do",
|
||||
subtitle: "We provide reliable and comprehensive support at every stage of income tax settlement in Germany.",
|
||||
services: [
|
||||
{ title: "Tax return preparation (Einkommensteuererklärung)", description: "We prepare a complete tax return, taking into account all applicable allowances, deductible expenses, and deductions. We ensure maximum refund and full compliance with German law." },
|
||||
{ title: "Preliminary calculation of expected refund", description: "Before sending the documents to the tax office, we calculate the exact amount of the expected refund or potential surcharge. You know exactly what to expect." },
|
||||
{ title: "Tax assessment verification (Steuerbescheid)", description: "We thoroughly verify the received decision from the tax office for any errors by officials. We check if the office has included all reported costs and allowances." },
|
||||
{ title: "Filing appeals (Einspruch)", description: "In the event of an unfavorable or incorrect decision from the office, we prepare an official appeal and represent you in the clarification proceedings." },
|
||||
{ title: "Year-round tax consulting", description: "We remain at your disposal throughout the tax year. We answer questions, help in ongoing correspondence with the office and advise when life situations change." },
|
||||
{ title: "Advice on choosing a tax class (Steuerklasse)", description: "We advise on the optimal selection of tax classes for marriages and people working in Germany. We help to choose the variant (e.g. III/V or IV/IV) that will bring the greatest financial benefits." },
|
||||
{ title: "Allowances, subsidies and deductions", description: "We help in obtaining Kindergeld, Riester-Rente and correct deduction of the costs of maintaining a double household (doppelter Haushalt) and commuting to work (Pendlerpauschale)." }
|
||||
]
|
||||
},
|
||||
eligibility: {
|
||||
title: "For whom — scope of advisory powers under §4 No. 11 StBerG",
|
||||
subtitle: "As an advisory point of b.b.h. Lohnsteuerhilfeverein e.V. we operate strictly within statutory limits for tax assistance associations.",
|
||||
eligibleTitle: "Who can benefit from our help",
|
||||
limitationsTitle: "Permissible additional income (rental, capital, other)",
|
||||
limitationsText: "Persons earning additional income — advice is possible only if the total income from these sources does not exceed €18,000 (singles) or €36,000 (married couples).",
|
||||
excludedTitle: "Who is NOT covered by our services (statutory limit)",
|
||||
legalNotice: "Tax assistance is provided only to members of the b.b.h. Lohnsteuerhilfeverein e.V. according to § 4 No. 11 Steuerberatungsgesetz (StBerG).",
|
||||
eligibleItems: [
|
||||
{ text: "Employees (Arbeitnehmer) and temporary workers" },
|
||||
{ text: "Civil servants (Beamte)" },
|
||||
{ text: "Pensioners and retirees (Rentner und Pensionäre)" },
|
||||
{ text: "Unemployed persons and social benefit recipients" },
|
||||
{ text: "Recipients of family benefits (Kindergeld, Elterngeld) and alimony" }
|
||||
],
|
||||
excludedItems: [
|
||||
{ text: "Sole traders (Gewerbe)" },
|
||||
{ text: "Entrepreneurs, partnerships and self-employed (Selbständige)" },
|
||||
{ text: "Persons generating income from agriculture and forestry (Land- und Forstwirtschaft)" },
|
||||
{ text: "Persons with rental or capital income exceeding limits" }
|
||||
]
|
||||
}
|
||||
},
|
||||
de: {
|
||||
pageHeader: { title: "Umfassende Steuererklärungen in Deutschland", subtitle: "Professionelle Erstellung, Prüfung von Steuerbescheiden und laufende Unterstützung." },
|
||||
servicesDetailed: {
|
||||
title: "Was wir tun",
|
||||
subtitle: "Wir bieten zuverlässige und umfassende Unterstützung in jeder Phase der Einkommensteuererklärung in Deutschland.",
|
||||
services: [
|
||||
{ title: "Erstellung der Einkommensteuererklärung", description: "Wir erstellen eine vollständige Steuererklärung unter Berücksichtigung aller zustehenden Freibeträge, Werbungskosten und Abzüge. Wir sorgen für maximale Rückerstattung und volle Übereinstimmung mit deutschem Recht." },
|
||||
{ title: "Vorläufige Berechnung der erwarteten Rückerstattung", description: "Bevor wir die Dokumente an das Finanzamt senden, berechnen wir die genaue Höhe der erwarteten Rückerstattung oder einer möglichen Nachzahlung." },
|
||||
{ title: "Prüfung des Steuerbescheids", description: "Wir prüfen den erhaltenen Bescheid des Finanzamtes eingehend auf Fehler der Sachbearbeiter. Wir prüfen, ob das Amt alle gemeldeten Kosten und Freibeträge berücksichtigt hat." },
|
||||
{ title: "Einspruchsverfahren", description: "Im Falle eines unvorteilhaften oder falschen Bescheids des Amtes erstellen wir einen offiziellen Einspruch und vertreten Sie im Klärungsverfahren." },
|
||||
{ title: "Ganzjährige Steuerberatung", description: "Wir stehen Ihnen während des gesamten Steuerjahres zur Verfügung. Wir beantworten Fragen, helfen bei der laufenden Korrespondenz mit dem Amt und beraten bei Veränderungen der Lebenssituation." },
|
||||
{ title: "Beratung bei der Wahl der Steuerklasse", description: "Wir beraten bei der optimalen Wahl der Steuerklassen für Verheiratete und Arbeitnehmer in Deutschland. Wir helfen bei der Wahl der Variante (z. B. III/V oder IV/IV)." },
|
||||
{ title: "Freibeträge, Zuschüsse und Abzüge", description: "Wir helfen bei der Erlangung von Kindergeld, Riester-Rente und dem korrekten Abzug der Kosten für eine doppelte Haushaltsführung und Fahrtkosten zur Arbeit (Pendlerpauschale)." }
|
||||
]
|
||||
},
|
||||
eligibility: {
|
||||
title: "Für wen — Beratungsumfang nach §4 Nr. 11 StBerG",
|
||||
subtitle: "Als Beratungsstelle des b.b.h. Lohnsteuerhilfeverein e.V. arbeiten wir streng im Rahmen der gesetzlichen Befugnisse.",
|
||||
eligibleTitle: "Wer unsere Hilfe in Anspruch nehmen kann",
|
||||
limitationsTitle: "Zulässige Zusatzeinnahmen (Vermietung, Kapital, Sonstige)",
|
||||
limitationsText: "Personen mit Zusatzeinkünften — eine Beratung ist nur möglich, wenn die Gesamteinnahmen aus diesen Quellen 18.000 € (Alleinstehende) oder 36.000 € (Verheiratete) nicht überschreiten.",
|
||||
excludedTitle: "Wer NICHT unter unsere Leistungen fällt",
|
||||
legalNotice: "Die Hilfeleistung in Steuersachen erfolgt ausschließlich für Mitglieder des b.b.h. Lohnsteuerhilfeverein e.V. gem. § 4 Nr. 11 StBerG.",
|
||||
eligibleItems: [
|
||||
{ text: "Arbeitnehmer und Leiharbeiter" },
|
||||
{ text: "Beamte" },
|
||||
{ text: "Rentner und Pensionäre" },
|
||||
{ text: "Arbeitslose und Bezieher von Sozialleistungen" },
|
||||
{ text: "Bezieher von Familienleistungen (Kindergeld, Elterngeld) und Unterhalt" }
|
||||
],
|
||||
excludedItems: [
|
||||
{ text: "Gewerbetreibende" },
|
||||
{ text: "Unternehmer, Gesellschaften und Selbständige" },
|
||||
{ text: "Personen mit Einkünften aus Land- und Forstwirtschaft" },
|
||||
{ text: "Personen mit Miet- oder Kapitaleinkünften, die die Grenzen überschreiten" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
'jak-sie-umowic': {
|
||||
en: {
|
||||
pageHeader: { title: "How to make an appointment", subtitle: "Simple, transparent, and 100% remote tax settlement process. You can handle everything without leaving your home." },
|
||||
process: {
|
||||
title: "Settlement in 3 simple steps",
|
||||
subtitle: "We provide full support and clear communication at every stage.",
|
||||
steps: [
|
||||
{ title: "Message us on WhatsApp", description: "Contact us directly via WhatsApp or the form below to start and discuss the details of your tax situation." },
|
||||
{ title: "Hand over documents", description: "Send your income certificates (Lohnsteuerbescheinigung, EU/EWR), tax identification number (IdNr), and potential expense confirmations." },
|
||||
{ title: "Receive ready settlement for signature", description: "We prepare the complete declaration, calculate the expected refund, and send the ready document for your approval and signature." }
|
||||
]
|
||||
},
|
||||
contactInfo: { hours: "Mon-Fri: 9:00 - 17:00\nBy appointment" }
|
||||
},
|
||||
de: {
|
||||
pageHeader: { title: "Termin vereinbaren", subtitle: "Einfacher, transparenter und 100% digitaler Prozess der Steuererklärung. Alles von zu Hause aus." },
|
||||
process: {
|
||||
title: "Steuererklärung in 3 einfachen Schritten",
|
||||
subtitle: "Wir bieten volle Unterstützung und klare Kommunikation in jeder Phase.",
|
||||
steps: [
|
||||
{ title: "Auf WhatsApp schreiben", description: "Kontaktieren Sie uns direkt über WhatsApp oder das Formular unten, um zu beginnen und die Details zu besprechen." },
|
||||
{ title: "Dokumente übergeben", description: "Senden Sie uns Ihre Lohnsteuerbescheinigungen (ggf. EU/EWR), Steueridentifikationsnummer (IdNr) und mögliche Kostenbelege." },
|
||||
{ title: "Erklärung zur Unterschrift erhalten", description: "Wir erstellen die komplette Erklärung, berechnen die erwartete Erstattung und senden das fertige Dokument zur Freigabe und Unterschrift." }
|
||||
]
|
||||
},
|
||||
contactInfo: { hours: "Mo-Fr: 9:00 - 17:00\nNach Vereinbarung" }
|
||||
}
|
||||
},
|
||||
'o-nas': {
|
||||
en: {
|
||||
pageHeader: { title: "About Us", subtitle: "Over 20 years of reliable tax support for Poles working in Germany." },
|
||||
bio: {
|
||||
paragraphs: [
|
||||
{ text: "For over two decades, I have been helping Polish workers and specialists in Germany to safely, stress-free, and fully legally settle income taxes before the tax office." },
|
||||
{ text: "My goal is to take the burden of complicated German tax regulations off your shoulders. We conduct the entire process in Polish — from initial document analysis, through precise calculations of allowances, to verification of the final tax decision." }
|
||||
],
|
||||
credentials: [
|
||||
{ text: "Over 20 years of practice in German tax law" },
|
||||
{ text: "Beratungsstellenleiter at b.b.h. Lohnsteuerhilfeverein e.V." },
|
||||
{ text: "Full service in Polish and German" },
|
||||
{ text: "Maximizing tax refunds and zero formal stress" }
|
||||
]
|
||||
},
|
||||
team: {
|
||||
title: "Our Team",
|
||||
subtitle: "Meet the experts who are committed to your finances. Each of us brings years of experience to guarantee your peace of mind and safety.",
|
||||
members: [
|
||||
{ role: "Owner / Beratungsstellenleiter", description: "Long-time tax law practitioner specializing in consulting for Polish workers in Germany." }
|
||||
]
|
||||
}
|
||||
},
|
||||
de: {
|
||||
pageHeader: { title: "Über uns", subtitle: "Über 20 Jahre zuverlässige Steuerunterstützung für Polen, die in Deutschland arbeiten." },
|
||||
bio: {
|
||||
paragraphs: [
|
||||
{ text: "Seit über zwei Jahrzehnten helfe ich polnischen Arbeitnehmern und Fachkräften in Deutschland bei der sicheren, stressfreien und völlig legalen Erledigung der Einkommensteuer gegenüber dem Finanzamt." },
|
||||
{ text: "Mein Ziel ist es, Ihnen die Last der komplizierten deutschen Steuervorschriften von den Schultern zu nehmen. Wir führen den gesamten Prozess auf Polnisch durch — von der ersten Dokumentenanalyse über präzise Berechnungen von Freibeträgen bis hin zur Überprüfung des finalen Steuerbescheids." }
|
||||
],
|
||||
credentials: [
|
||||
{ text: "Über 20 Jahre Praxis im deutschen Steuerrecht" },
|
||||
{ text: "Beratungsstellenleiter im b.b.h. Lohnsteuerhilfeverein e.V." },
|
||||
{ text: "Vollständiger Service in Polnisch und Deutsch" },
|
||||
{ text: "Maximierung der Steuerrückerstattung und null formaler Stress" }
|
||||
]
|
||||
},
|
||||
team: {
|
||||
title: "Unser Team",
|
||||
subtitle: "Lernen Sie die Experten kennen, die sich für Ihre Finanzen einsetzen. Jeder von uns bringt jahrelange Erfahrung mit, um Ihre Ruhe und Sicherheit zu garantieren.",
|
||||
members: [
|
||||
{ role: "Inhaber / Beratungsstellenleiter", description: "Langjähriger Steuerrechtspraktiker mit Spezialisierung auf die Beratung polnischer Arbeitnehmer in Deutschland." }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
|
||||
const pages = await payload.find({ collection: 'pages', limit: 100, locale: 'pl' })
|
||||
|
||||
for (const page of pages.docs) {
|
||||
if (!translations[page.slug || '']) continue;
|
||||
|
||||
for (const loc of ['en', 'de'] as const) {
|
||||
const dbPage = await payload.findByID({ collection: 'pages', id: page.id, locale: loc })
|
||||
if (!dbPage || !dbPage.layout) continue;
|
||||
|
||||
const tPage = translations[page.slug!][loc];
|
||||
let modified = false;
|
||||
|
||||
const layout = dbPage.layout.map((block: any) => {
|
||||
// preserve IDs
|
||||
for (const key of Object.keys(block)) {
|
||||
if (block[key] && typeof block[key] === 'object' && block[key].id !== undefined) {
|
||||
block[key] = block[key].id
|
||||
}
|
||||
}
|
||||
|
||||
if (block.blockType === 'pageHeader' && tPage.pageHeader) {
|
||||
block.title = tPage.pageHeader.title;
|
||||
block.subtitle = tPage.pageHeader.subtitle;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (block.blockType === 'servicesDetailed' && tPage.servicesDetailed) {
|
||||
block.title = tPage.servicesDetailed.title;
|
||||
block.subtitle = tPage.servicesDetailed.subtitle;
|
||||
block.services = block.services.map((s: any, i: number) => ({
|
||||
...s,
|
||||
title: tPage.servicesDetailed.services[i]?.title || s.title,
|
||||
description: tPage.servicesDetailed.services[i]?.description || s.description
|
||||
}))
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (block.blockType === 'eligibility' && tPage.eligibility) {
|
||||
block.title = tPage.eligibility.title;
|
||||
block.subtitle = tPage.eligibility.subtitle;
|
||||
block.eligibleTitle = tPage.eligibility.eligibleTitle;
|
||||
block.limitationsTitle = tPage.eligibility.limitationsTitle;
|
||||
block.limitationsText = tPage.eligibility.limitationsText;
|
||||
block.excludedTitle = tPage.eligibility.excludedTitle;
|
||||
block.legalNotice = tPage.eligibility.legalNotice;
|
||||
|
||||
block.eligibleItems = block.eligibleItems.map((s: any, i: number) => ({
|
||||
...s, text: tPage.eligibility.eligibleItems[i]?.text || s.text
|
||||
}))
|
||||
block.excludedItems = block.excludedItems.map((s: any, i: number) => ({
|
||||
...s, text: tPage.eligibility.excludedItems[i]?.text || s.text
|
||||
}))
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (block.blockType === 'process' && tPage.process) {
|
||||
block.title = tPage.process.title;
|
||||
block.subtitle = tPage.process.subtitle;
|
||||
block.steps = block.steps.map((s: any, i: number) => ({
|
||||
...s,
|
||||
title: tPage.process.steps[i]?.title || s.title,
|
||||
description: tPage.process.steps[i]?.description || s.description
|
||||
}))
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (block.blockType === 'bio' && tPage.bio) {
|
||||
block.paragraphs = block.paragraphs.map((s: any, i: number) => ({
|
||||
...s, text: tPage.bio.paragraphs[i]?.text || s.text
|
||||
}))
|
||||
block.credentials = block.credentials.map((s: any, i: number) => ({
|
||||
...s, text: tPage.bio.credentials[i]?.text || s.text
|
||||
}))
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (block.blockType === 'team' && tPage.team) {
|
||||
block.title = tPage.team.title;
|
||||
block.subtitle = tPage.team.subtitle;
|
||||
block.members = block.members.map((s: any, i: number) => ({
|
||||
...s,
|
||||
role: tPage.team.members[i]?.role || s.role,
|
||||
description: tPage.team.members[i]?.description || s.description
|
||||
}))
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (block.blockType === 'contactInfo' && tPage.contactInfo) {
|
||||
block.hours = tPage.contactInfo.hours;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return block;
|
||||
});
|
||||
|
||||
if (modified) {
|
||||
await payload.update({
|
||||
collection: 'pages',
|
||||
id: page.id,
|
||||
locale: loc,
|
||||
data: { layout }
|
||||
})
|
||||
console.log(`Fully translated ${page.slug} to ${loc}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,97 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
|
||||
// Translation mapping for FinalCTA to EN
|
||||
const enCta = {
|
||||
title: "Have questions about your tax return?",
|
||||
subtitle: "Contact us directly via WhatsApp. We will analyze your situation and help you get the refund you deserve.",
|
||||
buttonText: "Message on WhatsApp",
|
||||
}
|
||||
// Translation mapping for FinalCTA to DE
|
||||
const deCta = {
|
||||
title: "Haben Sie Fragen zu Ihrer Steuererklärung?",
|
||||
subtitle: "Kontaktieren Sie uns direkt über WhatsApp. Wir analysieren Ihre Situation und helfen Ihnen, die Ihnen zustehende Rückerstattung zu erhalten.",
|
||||
buttonText: "Auf WhatsApp schreiben",
|
||||
}
|
||||
|
||||
const pages = await payload.find({ collection: 'pages', limit: 100, locale: 'pl' })
|
||||
|
||||
for (const page of pages.docs) {
|
||||
if (page.slug === 'home') continue; // already mostly translated
|
||||
|
||||
// EN Update
|
||||
const enPage = await payload.findByID({ collection: 'pages', id: page.id, locale: 'en' })
|
||||
if (enPage && enPage.layout) {
|
||||
let modified = false
|
||||
const layout = enPage.layout.map((block: any) => {
|
||||
// Strip populated ids
|
||||
for (const key of Object.keys(block)) {
|
||||
if (block[key] && typeof block[key] === 'object' && block[key].id !== undefined) {
|
||||
block[key] = block[key].id
|
||||
}
|
||||
}
|
||||
if (block.blockType === 'finalCta') {
|
||||
block.title = enCta.title
|
||||
block.subtitle = enCta.subtitle
|
||||
block.buttonText = enCta.buttonText
|
||||
modified = true
|
||||
}
|
||||
if (block.blockType === 'pageHeader') {
|
||||
if (page.slug === 'uslugi') {
|
||||
block.title = "Our Services"
|
||||
block.subtitle = "Professional tax and accounting support in Germany."
|
||||
} else if (page.slug === 'kontakt') {
|
||||
block.title = "Contact"
|
||||
block.subtitle = "Do you have questions about tax settlement in Germany? Contact us — we will be happy to help."
|
||||
}
|
||||
modified = true
|
||||
}
|
||||
return block
|
||||
})
|
||||
if (modified) {
|
||||
await payload.update({ collection: 'pages', id: page.id, locale: 'en', data: { layout } })
|
||||
console.log(`Updated EN translations for ${page.slug}`)
|
||||
}
|
||||
}
|
||||
|
||||
// DE Update
|
||||
const dePage = await payload.findByID({ collection: 'pages', id: page.id, locale: 'de' })
|
||||
if (dePage && dePage.layout) {
|
||||
let modified = false
|
||||
const layout = dePage.layout.map((block: any) => {
|
||||
for (const key of Object.keys(block)) {
|
||||
if (block[key] && typeof block[key] === 'object' && block[key].id !== undefined) {
|
||||
block[key] = block[key].id
|
||||
}
|
||||
}
|
||||
if (block.blockType === 'finalCta') {
|
||||
block.title = deCta.title
|
||||
block.subtitle = deCta.subtitle
|
||||
block.buttonText = deCta.buttonText
|
||||
modified = true
|
||||
}
|
||||
if (block.blockType === 'pageHeader') {
|
||||
if (page.slug === 'uslugi') {
|
||||
block.title = "Unsere Dienstleistungen"
|
||||
block.subtitle = "Professionelle steuerliche und buchhalterische Betreuung in Deutschland."
|
||||
} else if (page.slug === 'kontakt') {
|
||||
block.title = "Kontakt"
|
||||
block.subtitle = "Haben Sie Fragen zur Steuererklärung in Deutschland? Kontaktieren Sie uns — wir helfen Ihnen gerne weiter."
|
||||
}
|
||||
modified = true
|
||||
}
|
||||
return block
|
||||
})
|
||||
if (modified) {
|
||||
await payload.update({ collection: 'pages', id: page.id, locale: 'de', data: { layout } })
|
||||
console.log(`Updated DE translations for ${page.slug}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(0)
|
||||
}
|
||||
run().catch(console.error)
|
||||
@@ -0,0 +1,54 @@
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from '../src/payload.config'
|
||||
|
||||
async function run() {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
|
||||
const pages = await payload.find({ collection: 'pages', limit: 100, locale: 'pl' })
|
||||
|
||||
for (const page of pages.docs) {
|
||||
if (!['faq', 'kontakt', 'stowarzyszenie', 'polityka-prywatnosci', 'polityka-cookies', 'impressum'].includes(page.slug || '')) continue;
|
||||
|
||||
for (const loc of ['en', 'de'] as const) {
|
||||
const dbPage = await payload.findByID({ collection: 'pages', id: page.id, locale: loc })
|
||||
if (!dbPage || !dbPage.layout) continue;
|
||||
|
||||
let modified = false;
|
||||
|
||||
const layout = dbPage.layout.map((block: any) => {
|
||||
// preserve IDs
|
||||
for (const key of Object.keys(block)) {
|
||||
if (block[key] && typeof block[key] === 'object' && block[key].id !== undefined) {
|
||||
block[key] = block[key].id
|
||||
}
|
||||
}
|
||||
|
||||
if (block.blockType === 'contactInfo') {
|
||||
block.hours = loc === 'en' ? "Mon-Fri: 9:00 - 17:00\nBy appointment" : "Mo-Fr: 9:00 - 17:00\nNach Vereinbarung"
|
||||
block.address = "TO BE CONFIRMED BY CLIENT"
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (block.blockType === 'TextSection') {
|
||||
block.text = loc === 'en' ? "Content in preparation" : "Inhalt in Vorbereitung"
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return block;
|
||||
});
|
||||
|
||||
if (modified) {
|
||||
await payload.update({
|
||||
collection: 'pages',
|
||||
id: page.id,
|
||||
locale: loc,
|
||||
data: { layout }
|
||||
})
|
||||
console.log(`Fully translated ${page.slug} to ${loc}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
run().catch(console.error)
|
||||
Reference in New Issue
Block a user