feat: complete MDX migration, stabilize environment & verify via E2E tests

Former-commit-id: ec3e64156a2e182535cbdcf0d975cd54603a517d
This commit is contained in:
2026-05-03 18:46:41 +02:00
parent e57661a586
commit b13f628b55
131 changed files with 1388 additions and 25288 deletions

View File

@@ -1,28 +0,0 @@
import { getPayload } from 'payload';
import configPromise from '../payload.config';
const clear = async () => {
const payload = await getPayload({ config: configPromise });
payload.logger.info('Clearing database...');
try {
const pages = await payload.find({ collection: 'pages', limit: 100 });
for (const page of pages.docs) await payload.delete({ collection: 'pages', id: page.id });
const jobs = await payload.find({ collection: 'jobs', limit: 100 });
for (const job of jobs.docs) await payload.delete({ collection: 'jobs', id: job.id });
const refs = await payload.find({ collection: 'references', limit: 100 });
for (const ref of refs.docs) await payload.delete({ collection: 'references', id: ref.id });
payload.logger.info('✅ Database clear completed successfully.');
process.exit(0);
} catch (err) {
payload.logger.error('❌ Database clear failed:');
payload.logger.error(err);
process.exit(1);
}
};
clear();

View File

@@ -1,257 +0,0 @@
import { getPayload } from 'payload';
import configPromise from '../payload.config';
const fixContent = async () => {
const payload = await getPayload({ config: configPromise });
payload.logger.info('Running complete content alignment script...');
const services = [
{ title: 'Kabelbau', enTitle: 'Cable Construction', description: 'Wir verlegen Kabel für sämtliche Spannungsebenen und übernehmen den kompletten Trassen- und Rohrleitungsbau.', tag: 'Tiefbau', size: 'large' },
{ title: 'Kabelpflugarbeiten', enTitle: 'Cable Plowing', description: 'Umweltschonendes und extrem effizientes Einpflügen von Kabeln und Rohren auf langen Strecken.', tag: 'Spezialmaschinen', size: 'medium' },
{ title: 'Horizontalspülbohrungen', enTitle: 'Directional Drilling', description: 'Grabenlose Leitungsverlegung unter Straßen, Flüssen und Bauwerken hindurch.', tag: 'Bohrtechnik', size: 'small' },
{ title: 'Elektromontagen bis 110 kV', enTitle: 'Electrical Installations', description: 'Installation, Montage und Wartung von Mittel- und Hochspannungsanlagen.', tag: 'Montage', size: 'medium' },
{ title: 'Glasfaser-Kabelmontagen', enTitle: 'Fiber Optic Montage', description: 'Spleißen, Messen und Einblasen von LWL-Kabeln für den modernen Breitbandausbau.', tag: 'Telekommunikation', size: 'small' },
{ title: 'Wartung & Störungsdienst', enTitle: 'Maintenance & Emergency', description: '24/7 Bereitschaft für schnelle Fehlerortung und Reparatur an Kabelanlagen.', tag: 'Service', size: 'accent' },
{ title: 'Genehmigungs- und Ausführungsplanung', enTitle: 'Planning & Permits', description: 'Einholen von Wegerechten und Erstellung baureifer Planungsunterlagen.', tag: 'Planung', size: 'small' },
{ title: 'Komplexe Querung', enTitle: 'Complex Crossings', description: 'Spezialtiefbau für die Unterquerung kritischer Infrastruktur (Bahn, Autobahn, Gewässer).', tag: 'Spezialtiefbau', size: 'large' },
{ title: 'Elektro- und Netzanschlussplanung', enTitle: 'Grid Connection Planning', description: 'Berechnung, Projektierung und Auslegung von Netzanschlüssen.', tag: 'Planung', size: 'medium' },
{ title: 'Vermessung & Dokumentation', enTitle: 'Surveying & Documentation', description: 'Digitale Trasseneinmessung (GPS/Tachymeter) und revisionssichere Dokumentation (GIS).', tag: 'Dokumentation', size: 'small' },
];
try {
for (const loc of ['de', 'en']) {
// 1. Home Page Alignment
const { docs: homeDocs } = await payload.find({
collection: 'pages',
where: { slug: { equals: 'home' } },
locale: loc as any,
limit: 1,
});
if (homeDocs.length > 0) {
payload.logger.info(`Aligning Home page content [${loc}]...`);
let content = homeDocs[0].content as any;
const children = content?.root?.children || [];
// Ensure Hero is correct
const heroIndex = children.findIndex((c: any) => c.fields?.blockType === 'heroVideo');
if (heroIndex >= 0) {
children[heroIndex].fields.title = loc === 'de' ? 'DIE EXPERTEN FÜR KABELTIEFBAU' : 'THE EXPERTS FOR CABLE CONSTRUCTION';
children[heroIndex].fields.subtitle = loc === 'de' ? 'Wir helfen beim Ausbau der Energiekabelnetze für eine grüne Zukunft.' : 'We help expanding the energy cable networks for a green future.';
children[heroIndex].fields.ctaLabel = loc === 'de' ? 'Jetzt anfragen' : 'Request now';
}
// Add/Update Competence Bento on Home
const bentoGridIndex = children.findIndex((c: any) => c.fields?.blockType === 'homeCompetenceBentoGrid');
const bentoBlock = {
type: 'block',
format: '',
version: 2,
fields: {
blockType: 'homeCompetenceBentoGrid',
badge: loc === 'de' ? 'Leistungsspektrum' : 'Service Spectrum',
title: loc === 'de' ? 'Umfassende Lösungen für komplexe Netzwerke' : 'Comprehensive solutions for complex networks',
ctaLabel: loc === 'de' ? 'Alle Kompetenzen ansehen' : 'View all competencies',
ctaHref: loc === 'de' ? '/kompetenzen' : '/en/competencies',
items: services.map(s => ({
title: loc === 'de' ? s.title : s.enTitle,
description: s.description,
tag: s.tag,
size: s.size
})),
},
};
if (bentoGridIndex >= 0) {
children[bentoGridIndex] = bentoBlock;
} else {
children.splice(2, 0, bentoBlock);
}
await payload.update({
collection: 'pages',
id: homeDocs[0].id,
locale: loc as any,
data: {
title: 'Home',
content: { root: { ...content.root, children } }
},
});
}
// 2. Kompetenzen Page
const { docs: compDocs } = await payload.find({
collection: 'pages',
where: { slug: { equals: 'kompetenzen' } },
locale: loc as any,
limit: 1,
});
const compData = {
title: loc === 'de' ? 'Kompetenzen' : 'Competencies',
slug: 'kompetenzen',
_status: 'published',
content: {
root: {
type: 'root',
format: '',
indent: 0,
version: 1,
children: [
{
type: 'block',
format: '',
version: 2,
fields: {
blockType: 'homeCompetenceBentoGrid',
badge: loc === 'de' ? 'Leistungsspektrum' : 'Service Spectrum',
title: loc === 'de' ? 'Unsere Kompetenzen im Überblick' : 'Our Competencies at a glance',
items: services.map(s => ({
title: loc === 'de' ? s.title : s.enTitle,
description: s.description,
tag: s.tag,
size: s.size
})),
},
}
]
}
}
};
if (compDocs.length > 0) {
payload.logger.info(`Updating Kompetenzen page [${loc}]...`);
await payload.update({
collection: 'pages',
id: compDocs[0].id,
locale: loc as any,
data: compData,
});
} else {
payload.logger.info(`Creating Kompetenzen page [${loc}]...`);
await payload.create({
collection: 'pages',
locale: loc as any,
data: compData
});
}
// 3. Über uns (Timeline & Group Tiles)
const { docs: aboutDocs } = await payload.find({
collection: 'pages',
where: { slug: { equals: 'ueber-uns' } },
locale: loc as any,
limit: 1,
});
const aboutData = {
title: loc === 'de' ? 'Über uns' : 'About us',
slug: 'ueber-uns',
_status: 'published',
content: {
root: {
type: 'root',
format: '',
indent: 0,
version: 1,
children: [
{
type: 'block',
format: '',
version: 2,
fields: {
blockType: 'homeSubCompanyTiles',
title: loc === 'de' ? 'Die E-TIB Unternehmensgruppe' : 'The E-TIB Corporate Group',
companies: [
{ title: 'E-TIB GmbH', description: loc === 'de' ? 'Ausführung elektrischer Infrastrukturprojekte.' : 'Execution of electrical infrastructure projects.' },
{ title: 'E-TIB Bohrtechnik GmbH', description: loc === 'de' ? 'Präzise Horizontalbohrungen in allen Bodenklassen.' : 'Precise horizontal directional drilling.' },
{ title: 'E-TIB Ingenieurgesellschaft mbH', description: loc === 'de' ? 'Planung, Projektierung und Dokumentation.' : 'Planning, engineering and documentation.' },
{ title: 'E-TIB Verwaltung GmbH', description: loc === 'de' ? 'Zentrale Dienste, Einkauf und Finanzen.' : 'Central services, purchasing and finance.' },
]
}
},
{
type: 'block',
format: '',
version: 2,
fields: {
blockType: 'companyTimeline',
title: loc === 'de' ? 'Unsere Geschichte' : 'Our History',
items: [
{ year: '2015', title: 'Gründung E-TIB GmbH', description: 'Start der Ausführung von Kabelbauprojekten in Guben.' },
{ year: '2019', title: 'Gründung Ingenieurgesellschaft', description: 'Erweiterung um Planung und Projektierung.' },
{ year: '2025', title: 'Erweiterung Bohrtechnik', description: 'Eigenständige Bohrtechnik-Sparte für Horizontalbohrungen.' },
]
}
}
]
}
}
};
if (aboutDocs.length > 0) {
payload.logger.info(`Updating Über uns page [${loc}]...`);
await payload.update({
collection: 'pages',
id: aboutDocs[0].id,
locale: loc as any,
data: aboutData,
});
} else {
payload.logger.info(`Creating Über uns page [${loc}]...`);
await payload.create({
collection: 'pages',
locale: loc as any,
data: aboutData
});
}
// 4. Impressum
const { docs: impDocs } = await payload.find({
collection: 'pages',
where: { slug: { equals: 'impressum' } },
locale: loc as any,
limit: 1,
});
const impData = {
title: loc === 'de' ? 'Impressum' : 'Imprint',
slug: 'impressum',
_status: 'published',
content: {
root: {
type: 'root',
format: '',
indent: 0,
version: 1,
children: [
{
type: 'paragraph',
children: [{ type: 'text', text: 'E-TIB GmbH\nGewerbestraße 22\n03172 Guben\n\nGeschäftsführung: Danny Joseph\nAmtsgericht Cottbus, HRB 12403 CB\nUSt-ID: DE304799919', version: 1 }]
}
]
}
}
};
if (impDocs.length > 0) {
await payload.update({ collection: 'pages', id: impDocs[0].id, locale: loc as any, data: impData });
} else {
await payload.create({ collection: 'pages', locale: loc as any, data: impData });
}
}
payload.logger.info('✅ Content alignment completed successfully!');
process.exit(0);
} catch (err: any) {
payload.logger.error('❌ Content alignment failed!');
if (err.data) {
payload.logger.error(JSON.stringify(err.data, null, 2));
} else {
payload.logger.error(err.stack || err.message || err);
}
process.exit(1);
}
};
fixContent();

View File

@@ -1,104 +0,0 @@
import { getPayload } from 'payload';
import configPromise from '../payload.config';
const fixHome = async () => {
const payload = await getPayload({ config: configPromise });
payload.logger.info('Running missing Home page script...');
try {
for (const loc of ['de', 'en']) {
const existingHome = await payload.find({
collection: 'pages',
where: { slug: { equals: 'home' } },
locale: loc as any,
limit: 1,
});
const homeData = {
title: loc === 'de' ? 'Startseite' : 'Home',
slug: 'home',
_status: 'published',
content: {
root: {
type: 'root',
format: '',
indent: 0,
version: 1,
children: [
{
type: 'block',
format: '',
version: 2,
fields: {
blockType: 'homeHero',
title: loc === 'de' ? 'DIE EXPERTEN FÜR \nKABELTIEFBAU' : 'THE EXPERTS FOR \nCABLE ENGINEERING',
subtitle: loc === 'de' ? 'Wir verbinden Infrastruktur mit Präzision. Von Horizontalbohrungen bis zu komplexen Leitungsnetzen.' : 'We connect infrastructure with precision. From horizontal drilling to complex pipe networks.',
videoUrl: '/assets/dummy-hero.mp4',
ctaLabel: loc === 'de' ? 'Unternehmen entdecken' : 'Discover Company',
ctaHref: '#unternehmen',
secondaryCtaLabel: loc === 'de' ? 'Projekt anfragen' : 'Request Project',
secondaryCtaHref: `/${loc}/kontakt`
}
},
{
type: 'block',
format: '',
version: 2,
fields: {
blockType: 'homeSubCompanyTiles',
badge: loc === 'de' ? 'Die Gruppe' : 'The Group',
title: loc === 'de' ? 'Geballte Kompetenz unter einem Dach' : 'Concentrated competence under one roof'
}
},
{
type: 'block',
format: '',
version: 2,
fields: {
blockType: 'homeCompetenceBentoGrid',
badge: loc === 'de' ? 'Leistungsspektrum' : 'Range of Services',
title: loc === 'de' ? 'Umfassende Lösungen für komplexe Netzwerke' : 'Comprehensive solutions for complex networks',
ctaLabel: loc === 'de' ? 'Alle Kompetenzen ansehen' : 'View all competences',
ctaHref: `/${loc}/kompetenzen`
}
},
{
type: 'block',
format: '',
version: 2,
fields: {
blockType: 'homeMeetTheTeam',
title: loc === 'de' ? 'Das sind Ihre Ansprechpartner' : 'Meet our team',
subtitle: loc === 'de' ? 'Unser Team aus Experten freut sich auf Ihr Projekt.' : 'Our team of experts is looking forward to your project.',
description: loc === 'de' ? 'Hier finden Sie alle Zertifikate und Ansprechpartner an einem Ort.' : 'Here you can find all certificates and contacts in one place.',
ctaLabel: loc === 'de' ? 'Zum Kontaktformular' : 'To Contact Form',
networkLabel: loc === 'de' ? 'Das E-TIB Netzwerk' : 'The E-TIB Network'
}
}
]
}
}
};
if (existingHome.totalDocs === 0) {
payload.logger.info(`Creating Home page [${loc}]...`);
await payload.create({ collection: 'pages', locale: loc as any, data: homeData });
} else {
await payload.update({ collection: 'pages', id: existingHome.docs[0].id, locale: loc as any, data: homeData });
}
}
payload.logger.info('✅ Home page created successfully!');
process.exit(0);
} catch (err: any) {
payload.logger.error('❌ Missing Home page script failed!');
if (err.data) {
payload.logger.error(JSON.stringify(err.data, null, 2));
} else {
payload.logger.error(err.stack || err.message || err);
}
process.exit(1);
}
};
fixHome();

View File

@@ -1,158 +0,0 @@
import { getPayload } from 'payload';
import configPromise from '../payload.config';
const fixPages = async () => {
const payload = await getPayload({ config: configPromise });
payload.logger.info('Running missing pages alignment script...');
const teamMembers = [
{ name: 'Danny Joseph', position: 'Geschäftsführung', email: 'd.joseph@e-tib.com', phone: '015207230518', branch: 'e-tib' },
{ name: 'Dirk Schmidt', position: 'Projektleitung', email: 'd.schmidt@e-tib.com', phone: '015165069040', branch: 'e-tib' },
{ name: 'Oliver Fiedler', position: 'Bauleitung', email: 'o.fiedler@e-tib.com', phone: '015152264087', branch: 'e-tib' },
{ name: 'Martin Polan', position: 'Polier', email: 'm.polan@e-tib.com', phone: '01713868167', branch: 'e-tib' },
{ name: 'Kerstin Joseph', position: 'Sekretariat / Buchhaltung', email: 'k.joseph@e-tib.com', phone: '03561 6851692', branch: 'verw' },
{ name: 'Kathrin Lemanski', position: 'Sachbearbeitung', email: 'k.lemanski@e-tib.com', phone: '03561 6857733', branch: 'verw' },
{ name: 'Sven Kirbs', position: 'Bauleitung', email: 's.kirbs@e-tib.com', phone: '', branch: 'e-tib' },
{ name: 'Maik Weichert', position: 'Bauleitung', email: 'm.weichert@e-tib.com', phone: '', branch: 'e-tib' },
];
try {
// Seed Team Members
for (const member of teamMembers) {
const existing = await payload.find({
collection: 'team',
where: { name: { equals: member.name } },
limit: 1,
});
if (existing.totalDocs === 0) {
payload.logger.info(`Creating team member: ${member.name}`);
await payload.create({
collection: 'team',
data: {
name: member.name,
position: member.position,
email: member.email,
phone: member.phone,
branch: member.branch as any,
},
});
}
}
// Create Kontakt and Karriere pages
for (const loc of ['de', 'en']) {
// Kontakt
const existingKontakt = await payload.find({
collection: 'pages',
where: { slug: { equals: 'kontakt' } },
locale: loc as any,
limit: 1,
});
const kontaktData = {
title: loc === 'de' ? 'Kontakt' : 'Contact',
slug: 'kontakt',
_status: 'published',
content: {
root: {
type: 'root',
format: '',
indent: 0,
version: 1,
children: [
{
type: 'block',
format: '',
version: 2,
fields: {
blockType: 'heroSection',
title: loc === 'de' ? 'Kontaktieren Sie uns' : 'Contact Us',
description: loc === 'de' ? 'Ihre Ansprechpartner der E-TIB Gruppe' : 'Your contacts at the E-TIB Group'
}
},
{
type: 'block',
format: '',
version: 2,
fields: {
blockType: 'homeMeetTheTeam',
title: loc === 'de' ? 'Das sind Ihre Ansprechpartner' : 'Meet our team',
subtitle: loc === 'de' ? 'Unser Team aus Experten freut sich auf Ihr Projekt.' : 'Our team of experts is looking forward to your project.',
description: loc === 'de' ? 'Hier finden Sie alle Zertifikate und Ansprechpartner an einem Ort.' : 'Here you can find all certificates and contacts in one place.',
ctaLabel: loc === 'de' ? 'Zum Kontaktformular' : 'To Contact Form',
networkLabel: loc === 'de' ? 'Das E-TIB Netzwerk' : 'The E-TIB Network'
}
}
]
}
}
};
if (existingKontakt.totalDocs === 0) {
payload.logger.info(`Creating Kontakt page [${loc}]...`);
await payload.create({ collection: 'pages', locale: loc as any, data: kontaktData });
} else {
await payload.update({ collection: 'pages', id: existingKontakt.docs[0].id, locale: loc as any, data: kontaktData });
}
// Karriere
const existingKarriere = await payload.find({
collection: 'pages',
where: { slug: { equals: 'karriere' } },
locale: loc as any,
limit: 1,
});
const karriereData = {
title: loc === 'de' ? 'Karriere & Messen' : 'Career & Exhibitions',
slug: 'karriere',
_status: 'published',
content: {
root: {
type: 'root',
format: '',
indent: 0,
version: 1,
children: [
{
type: 'block',
format: '',
version: 2,
fields: {
blockType: 'heroSection',
title: loc === 'de' ? 'Karriere bei E-TIB' : 'Career at E-TIB',
description: loc === 'de' ? 'Wir wachsen und werfen gemeinsam mit Dir die Energie-Wende an.' : 'We are growing - and fueling the energy transition together.'
}
},
{
type: 'paragraph',
children: [{ type: 'text', text: loc === 'de' ? 'Trefft uns auf diesen kommenden Messen:\n- Intersolar München\n- Windenergietage Linstow\n- Kabelwerkstatt Wiesbaden' : 'Meet us at these upcoming exhibitions:\n- Intersolar Munich\n- Wind Energy Days Linstow\n- Cable Workshop Wiesbaden', version: 1 }]
}
]
}
}
};
if (existingKarriere.totalDocs === 0) {
payload.logger.info(`Creating Karriere page [${loc}]...`);
await payload.create({ collection: 'pages', locale: loc as any, data: karriereData });
} else {
await payload.update({ collection: 'pages', id: existingKarriere.docs[0].id, locale: loc as any, data: karriereData });
}
}
payload.logger.info('✅ Missing pages created successfully!');
process.exit(0);
} catch (err: any) {
payload.logger.error('❌ Missing pages script failed!');
if (err.data) {
payload.logger.error(JSON.stringify(err.data, null, 2));
} else {
payload.logger.error(err.stack || err.message || err);
}
process.exit(1);
}
};
fixPages();

View File

@@ -1,81 +0,0 @@
import { getPayload } from 'payload';
import configPromise from '../payload.config';
const createReferenzen = async () => {
const payload = await getPayload({ config: configPromise });
payload.logger.info('Creating Referenzen...');
try {
for (const loc of ['de', 'en']) {
const existing = await payload.find({
collection: 'pages',
where: { slug: { equals: 'referenzen' } },
locale: loc as any,
limit: 1,
});
const referenzenData = {
title: loc === 'de' ? 'Referenzen' : 'References',
slug: 'referenzen',
_status: 'published',
content: {
root: {
type: 'root',
format: '',
indent: 0,
version: 1,
children: [
{
type: 'block',
format: '',
version: 2,
fields: {
blockType: 'heroSection',
title: loc === 'de' ? 'Unsere Referenzen' : 'Our References',
description: loc === 'de' ? 'Projekte die uns stolz machen.' : 'Projects that make us proud.'
}
}
]
}
}
};
if (existing.totalDocs === 0) {
await payload.create({ collection: 'pages', locale: loc as any, data: referenzenData });
} else {
await payload.update({ collection: 'pages', id: existing.docs[0].id, locale: loc as any, data: referenzenData });
}
// update setings again
const navde = [
{ label: 'Unternehmen', url: '/' },
{ label: 'Kompetenzen', url: '/kompetenzen' },
{ label: 'Referenzen', url: '/referenzen' },
{ label: 'Karriere', url: '/karriere' }
];
const naven = [
{ label: 'Company', url: '/' },
{ label: 'Competences', url: '/kompetenzen' },
{ label: 'References', url: '/referenzen' },
{ label: 'Career', url: '/karriere' }
];
await payload.updateGlobal({
slug: 'settings',
locale: loc as any,
data: {
mainNav: loc === 'de' ? navde : naven
}
});
}
payload.logger.info('✅ Referenzen created!');
process.exit(0);
} catch (err: any) {
payload.logger.error('❌ Failed!');
process.exit(1);
}
};
createReferenzen();

View File

@@ -1,49 +0,0 @@
import { getPayload } from 'payload';
import configPromise from '../payload.config';
const updateSettings = async () => {
const payload = await getPayload({ config: configPromise });
payload.logger.info('Updating Global Settings...');
try {
for (const loc of ['de', 'en']) {
const navde = [
{ label: 'Unternehmen', url: '/' },
{ label: 'Kompetenzen', url: '/kompetenzen' },
{ label: 'Referenzen', url: '/startseite' },
{ label: 'Karriere', url: '/karriere' }
];
const naven = [
{ label: 'Company', url: '/' },
{ label: 'Competences', url: '/kompetenzen' },
{ label: 'References', url: '/startseite' },
{ label: 'Career', url: '/karriere' }
];
await payload.updateGlobal({
slug: 'settings',
locale: loc as any,
data: {
contactEmail: 'd.joseph@e-tib.com',
contactPhone: '+49 15207230518',
address: 'Gewerbestraße 22\n03172 Guben',
mainNav: loc === 'de' ? navde : naven
}
});
}
payload.logger.info('✅ Settings updated successfully!');
process.exit(0);
} catch (err: any) {
payload.logger.error('❌ Settings update failed!');
if (err.data) {
payload.logger.error(JSON.stringify(err.data, null, 2));
} else {
payload.logger.error(err.stack || err.message || err);
}
process.exit(1);
}
};
updateSettings();

View File

@@ -1,21 +0,0 @@
import { getPayload } from 'payload';
import configPromise from '../payload.config';
import { seedDatabase } from '../src/payload/seed';
const seed = async () => {
const payload = await getPayload({ config: configPromise });
payload.logger.info('Starting manual database seed...');
try {
await seedDatabase(payload);
payload.logger.info('✅ Database seed completed successfully.');
process.exit(0);
} catch (err) {
payload.logger.error('❌ Database seed failed:');
payload.logger.error(err);
process.exit(1);
}
};
seed();