feat: complete MDX migration, stabilize environment & verify via E2E tests
Former-commit-id: ec3e64156a2e182535cbdcf0d975cd54603a517d
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
import configPromise from '@payload-config';
|
||||
import { RootPage } from '@payloadcms/next/views';
|
||||
import { importMap } from '../importMap';
|
||||
|
||||
type Args = {
|
||||
params: Promise<{
|
||||
segments: string[];
|
||||
}>;
|
||||
searchParams: Promise<{
|
||||
[key: string]: string | string[];
|
||||
}>;
|
||||
};
|
||||
|
||||
const Page = ({ params, searchParams }: Args) =>
|
||||
RootPage({ config: configPromise, importMap, params, searchParams });
|
||||
|
||||
export default Page;
|
||||
@@ -1,57 +0,0 @@
|
||||
import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||
import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||
import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||
import { BlocksFeatureClient as BlocksFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { UploadFeatureClient as UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { BlockquoteFeatureClient as BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { RelationshipFeatureClient as RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { LinkFeatureClient as LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { ChecklistFeatureClient as ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { OrderedListFeatureClient as OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { UnorderedListFeatureClient as UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { IndentFeatureClient as IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { AlignFeatureClient as AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { ParagraphFeatureClient as ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { InlineCodeFeatureClient as InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { SuperscriptFeatureClient as SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { SubscriptFeatureClient as SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { default as default_9ed509b5e5f7d08a16335393f27586cc } from '../../../src/payload/components/Icon'
|
||||
import { default as default_5470ea90f7a8fd882c2fe59ff2b1c5b9 } from '../../../src/payload/components/Logo'
|
||||
import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
|
||||
|
||||
export const importMap = {
|
||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
"@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
"@payloadcms/richtext-lexical/client#BlocksFeatureClient": BlocksFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#UploadFeatureClient": UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#BlockquoteFeatureClient": BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#RelationshipFeatureClient": RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#LinkFeatureClient": LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#ChecklistFeatureClient": ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#OrderedListFeatureClient": OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#UnorderedListFeatureClient": UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#IndentFeatureClient": IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#AlignFeatureClient": AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#ParagraphFeatureClient": ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#InlineCodeFeatureClient": InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#SuperscriptFeatureClient": SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#SubscriptFeatureClient": SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#StrikethroughFeatureClient": StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"/src/payload/components/Icon#default": default_9ed509b5e5f7d08a16335393f27586cc,
|
||||
"/src/payload/components/Logo#default": default_5470ea90f7a8fd882c2fe59ff2b1c5b9,
|
||||
"@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import config from '@payload-config';
|
||||
import {
|
||||
REST_GET,
|
||||
REST_OPTIONS,
|
||||
REST_PATCH,
|
||||
REST_POST,
|
||||
REST_DELETE,
|
||||
} from '@payloadcms/next/routes';
|
||||
|
||||
export const GET = REST_GET(config);
|
||||
export const POST = REST_POST(config);
|
||||
export const DELETE = REST_DELETE(config);
|
||||
export const PATCH = REST_PATCH(config);
|
||||
export const OPTIONS = REST_OPTIONS(config);
|
||||
@@ -1,4 +0,0 @@
|
||||
import config from '@payload-config';
|
||||
import { GRAPHQL_POST } from '@payloadcms/next/routes';
|
||||
|
||||
export const POST = GRAPHQL_POST(config);
|
||||
@@ -1,151 +0,0 @@
|
||||
/* =================================================================
|
||||
KLZ Cables – Payload Admin Theme
|
||||
Strictly follows docs/STYLEGUIDE.md & tailwind.config.cjs
|
||||
|
||||
IMPORTANT: We use `html` selector (not `:root`) because Payload's
|
||||
own CSS defines variables on `:root` and loads AFTER this file.
|
||||
`html` has higher specificity than `:root`, so our values win.
|
||||
================================================================= */
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
/* =================================================================
|
||||
COLOR OVERRIDES
|
||||
Payload internally maps:
|
||||
--theme-elevation-* → --color-base-*
|
||||
--theme-success-* → --color-success-*
|
||||
We override the SOURCE variables on `html` to beat Payload's `:root`.
|
||||
================================================================= */
|
||||
|
||||
html {
|
||||
/* ---------------------------------------------------------------
|
||||
KLZ Primary Blue (#011dff) → Buttons, links, active states
|
||||
--------------------------------------------------------------- */
|
||||
--color-success-50: #eef0ff !important;
|
||||
--color-success-100: #dfe2ff !important;
|
||||
--color-success-150: #cdd2ff !important;
|
||||
--color-success-200: #b8bfff !important;
|
||||
--color-success-250: #a0a9ff !important;
|
||||
--color-success-300: #8892ff !important;
|
||||
--color-success-350: #707bff !important;
|
||||
--color-success-400: #5564ff !important;
|
||||
--color-success-450: #3a4dff !important;
|
||||
--color-success-500: #011dff !important;
|
||||
/* KLZ Primary */
|
||||
--color-success-550: #0119e6 !important;
|
||||
--color-success-600: #0116cc !important;
|
||||
--color-success-650: #0112b3 !important;
|
||||
--color-success-700: #000e99 !important;
|
||||
--color-success-750: #000b80 !important;
|
||||
--color-success-800: #000866 !important;
|
||||
--color-success-850: #00054d !important;
|
||||
--color-success-900: #000333 !important;
|
||||
--color-success-950: #00011a !important;
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
KLZ "Foundation Neutrals" → Backgrounds, cards, borders, text
|
||||
Based on tailwind.config.cjs: neutral.light=#fff,
|
||||
neutral.DEFAULT=#f8f9fa, neutral.dark=#263336, neutral.black=#0a0a0a
|
||||
text.primary=#1a1a1a, text.secondary=#6c757d, text.light=#adb5bd
|
||||
--------------------------------------------------------------- */
|
||||
--color-base-0: #ffffff !important;
|
||||
--color-base-50: #f8f9fa !important;
|
||||
--color-base-100: #f1f3f5 !important;
|
||||
--color-base-150: #e9ecef !important;
|
||||
--color-base-200: #dee2e6 !important;
|
||||
--color-base-250: #ced4da !important;
|
||||
--color-base-300: #adb5bd !important;
|
||||
--color-base-350: #9ba3ab !important;
|
||||
--color-base-400: #868e96 !important;
|
||||
--color-base-450: #6c757d !important;
|
||||
--color-base-500: #5c636a !important;
|
||||
--color-base-550: #4d5358 !important;
|
||||
--color-base-600: #3d4246 !important;
|
||||
--color-base-650: #343a40 !important;
|
||||
--color-base-700: #2b3035 !important;
|
||||
--color-base-750: #263336 !important;
|
||||
--color-base-800: #212529 !important;
|
||||
--color-base-850: #1a1a1a !important;
|
||||
--color-base-900: #121212 !important;
|
||||
--color-base-950: #0a0a0a !important;
|
||||
--color-base-1000: #000000 !important;
|
||||
|
||||
/* Typography */
|
||||
--font-body: 'Inter', system-ui, -apple-system, sans-serif !important;
|
||||
--font-headings: 'Inter', system-ui, -apple-system, sans-serif !important;
|
||||
}
|
||||
|
||||
/* Base Body Application */
|
||||
body {
|
||||
font-family: var(--font-body) !important;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
Login / Setup Page
|
||||
================================================================= */
|
||||
.template-default.template-default--has-bg {
|
||||
background: radial-gradient(circle at top right, #e6ebf5 0%, #f8f9fa 60%, #f3f4f6 100%) !important;
|
||||
}
|
||||
|
||||
.login__wrap,
|
||||
.create-first-user__wrap {
|
||||
border-top: none !important;
|
||||
padding: 3rem !important;
|
||||
background: rgba(255, 255, 255, 0.85) !important;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid var(--theme-elevation-150) !important;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.15) !important;
|
||||
border-radius: 1.5rem !important;
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
Buttons – override Payload's dark buttons with KLZ Blue
|
||||
Payload uses .btn--style-primary { --bg-color: var(--theme-elevation-800) }
|
||||
which makes all primary buttons near-black. We override to KLZ Blue.
|
||||
================================================================= */
|
||||
.btn--style-primary,
|
||||
.btn--style-pill {
|
||||
--bg-color: #011dff !important;
|
||||
--color: #ffffff !important;
|
||||
--hover-bg: #0116cc !important;
|
||||
--hover-color: #ffffff !important;
|
||||
}
|
||||
|
||||
.btn--style-primary.btn--disabled,
|
||||
.btn--style-pill.btn--disabled {
|
||||
--bg-color: #b8bfff !important;
|
||||
--color: #ffffff !important;
|
||||
--hover-bg: #b8bfff !important;
|
||||
}
|
||||
|
||||
/* Sidebar Active Items */
|
||||
[class*="nav-group__link--active"],
|
||||
[class*="nav__link--active"] {
|
||||
--theme-elevation-800: #011dff !important;
|
||||
color: #011dff !important;
|
||||
border-left-color: #011dff !important;
|
||||
}
|
||||
|
||||
.btn--style-secondary {
|
||||
--box-shadow: inset 0 0 0 1px #011dff !important;
|
||||
--color: #011dff !important;
|
||||
--hover-color: #0116cc !important;
|
||||
--hover-box-shadow: inset 0 0 0 1px #0116cc !important;
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
Logo & Icon
|
||||
================================================================= */
|
||||
.klz-admin-logo,
|
||||
.klz-admin-icon {
|
||||
display: block !important;
|
||||
visibility: visible !important;
|
||||
opacity: 1 !important;
|
||||
height: 32px !important;
|
||||
width: auto !important;
|
||||
max-width: 100% !important;
|
||||
object-fit: contain !important;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import configPromise from '@payload-config';
|
||||
import { RootLayout } from '@payloadcms/next/layouts';
|
||||
import React from 'react';
|
||||
|
||||
import '@payloadcms/next/css';
|
||||
import './custom.scss';
|
||||
import { handleServerFunctions } from '@payloadcms/next/layouts';
|
||||
import { importMap } from './admin/importMap';
|
||||
|
||||
type Args = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const serverFunction: any = async function (args: any) {
|
||||
'use server';
|
||||
return handleServerFunctions({
|
||||
...args,
|
||||
config: configPromise,
|
||||
importMap,
|
||||
});
|
||||
};
|
||||
|
||||
const Layout = ({ children }: Args) => {
|
||||
return (
|
||||
<RootLayout config={configPromise} importMap={importMap} serverFunction={serverFunction}>
|
||||
{children}
|
||||
</RootLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -1,20 +0,0 @@
|
||||
export default function DatenschutzPage() {
|
||||
return (
|
||||
<div className="pt-32 pb-24">
|
||||
<div className="container max-w-3xl">
|
||||
<h1 className="text-5xl font-heading font-extrabold text-neutral-dark mb-8">Datenschutzerklärung</h1>
|
||||
<div className="prose prose-neutral max-w-none">
|
||||
<h3>1. Datenschutz auf einen Blick</h3>
|
||||
<p>
|
||||
Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren personenbezogenen Daten passiert, wenn Sie diese Website besuchen.
|
||||
Die vollständige Datenschutzerklärung der E-TIB GmbH wird aktuell überarbeitet und an dieser Stelle zur Verfügung gestellt.
|
||||
</p>
|
||||
<h3>2. Allgemeine Hinweise und Pflichtinformationen</h3>
|
||||
<p>
|
||||
Die Betreiber dieser Seiten nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir behandeln Ihre personenbezogenen Daten vertraulich und entsprechend der gesetzlichen Datenschutzvorschriften sowie dieser Datenschutzerklärung.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
export default function ImpressumPage() {
|
||||
return (
|
||||
<div className="pt-32 pb-24">
|
||||
<div className="container max-w-3xl">
|
||||
<h1 className="text-5xl font-heading font-extrabold text-neutral-dark mb-8">Impressum</h1>
|
||||
<div className="prose prose-neutral max-w-none">
|
||||
<p><strong>Angaben gemäß § 5 TMG</strong></p>
|
||||
<p>
|
||||
E-TIB GmbH<br />
|
||||
Gewerbestraße 22<br />
|
||||
03172 Guben
|
||||
</p>
|
||||
<p>
|
||||
<strong>Vertreten durch:</strong><br />
|
||||
Geschäftsführer: Danny Joseph
|
||||
</p>
|
||||
<p>
|
||||
<strong>Kontakt:</strong><br />
|
||||
Telefon: +49 15207230518<br />
|
||||
E-Mail: d.joseph@e-tib.com
|
||||
</p>
|
||||
<p>
|
||||
<strong>Registereintrag:</strong><br />
|
||||
Eintragung im Handelsregister.<br />
|
||||
Registergericht: Amtsgericht Cottbus<br />
|
||||
Registernummer: HRB 12403 CB
|
||||
</p>
|
||||
<p>
|
||||
<strong>Umsatzsteuer-ID:</strong><br />
|
||||
Umsatzsteuer-Identifikationsnummer gemäß § 27 a Umsatzsteuergesetz:<br />
|
||||
DE304799919
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,10 +4,23 @@ import { getTranslations, setRequestLocale } from 'next-intl/server';
|
||||
import { Metadata } from 'next';
|
||||
import { getPageBySlug } from '@/lib/pages';
|
||||
import { mapSlugToFileSlug, mapFileSlugToTranslated } from '@/lib/slugs';
|
||||
import PayloadRichText from '@/components/PayloadRichText';
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||
import { SITE_URL } from '@/lib/schema';
|
||||
import TrackedLink from '@/components/analytics/TrackedLink';
|
||||
|
||||
// Import components used in MDX
|
||||
import { HeroVideo as HomeHero } from '@/components/blocks/HeroVideo';
|
||||
import { SubCompanyTiles as HomeSubCompanyTiles } from '@/components/blocks/SubCompanyTiles';
|
||||
import { CompetenceBentoGrid as HomeCompetenceBentoGrid } from '@/components/blocks/CompetenceBentoGrid';
|
||||
import { ReferencesSlider as HomeReferencesSlider } from '@/components/blocks/ReferencesSlider';
|
||||
|
||||
const mdxComponents = {
|
||||
HomeHero,
|
||||
HomeSubCompanyTiles,
|
||||
HomeCompetenceBentoGrid,
|
||||
HomeReferencesSlider,
|
||||
};
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{
|
||||
locale: string;
|
||||
@@ -72,18 +85,20 @@ export default async function Page(props: { params: Promise<{ locale: string; sl
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Redirect if accessed via a different locale's slug
|
||||
// Redirect logic removed to simplify for now, as slugs match file names
|
||||
/*
|
||||
const fileSlug = await mapSlugToFileSlug(pageData.slug || slug, locale);
|
||||
const correctSlug = await mapFileSlugToTranslated(fileSlug, locale);
|
||||
if (correctSlug && correctSlug !== slug) {
|
||||
redirect(`/${locale}/${correctSlug}`);
|
||||
}
|
||||
*/
|
||||
|
||||
// Full-bleed pages render blocks edge-to-edge without the generic article wrapper
|
||||
if (pageData.frontmatter.layout === 'fullBleed') {
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<PayloadRichText data={pageData.content} className="" />
|
||||
<MDXRemote source={pageData.content} components={mdxComponents} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -120,9 +135,9 @@ export default async function Page(props: { params: Promise<{ locale: string; sl
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Main content with shared blog components */}
|
||||
{/* Main content */}
|
||||
<div className="prose prose-lg md:prose-xl max-w-none prose-headings:font-bold prose-headings:text-text-primary prose-p:text-text-secondary prose-p:leading-relaxed prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-img:rounded-2xl prose-img:shadow-2xl prose-blockquote:border-primary prose-blockquote:bg-primary/5 prose-blockquote:rounded-r-2xl prose-strong:text-primary">
|
||||
<PayloadRichText data={pageData.content} />
|
||||
<MDXRemote source={pageData.content} components={mdxComponents} />
|
||||
</div>
|
||||
|
||||
{/* Support Section */}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
getPostBySlug,
|
||||
getAdjacentPosts,
|
||||
getReadingTime,
|
||||
extractLexicalHeadings,
|
||||
getHeadings,
|
||||
} from '@/lib/blog';
|
||||
import { Metadata } from 'next';
|
||||
import Link from 'next/link';
|
||||
@@ -16,9 +16,7 @@ import TableOfContents from '@/components/blog/TableOfContents';
|
||||
import { Heading } from '@/components/ui';
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
import BlogEngagementTracker from '@/components/analytics/BlogEngagementTracker';
|
||||
|
||||
// Payload CMS Imports
|
||||
import PayloadRichText from '@/components/PayloadRichText';
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||
|
||||
interface BlogPostProps {
|
||||
params: Promise<{
|
||||
@@ -65,20 +63,9 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// If the user accessed this post using a slug from a different locale
|
||||
// (e.g. via the generic language switcher), redirect them to the correct localized slug URL
|
||||
if (post.slug && post.slug !== slug) {
|
||||
redirect(`/${locale}/blog/${post.slug}`);
|
||||
}
|
||||
|
||||
const { prev, next, isPrevRandom, isNextRandom } = await getAdjacentPosts(post.slug, locale);
|
||||
|
||||
// Convert Lexical content into a plain string to estimate reading time roughly
|
||||
// Extract headings for TOC
|
||||
const headings = extractLexicalHeadings(post.content?.root || post.content);
|
||||
|
||||
// Convert Lexical content into a plain string to estimate reading time roughly
|
||||
const rawTextContent = JSON.stringify(post.content);
|
||||
const headings = getHeadings(post.content);
|
||||
const readingTime = getReadingTime(post.content);
|
||||
|
||||
return (
|
||||
<article className="bg-white min-h-screen font-sans selection:bg-primary/10 selection:text-primary">
|
||||
@@ -86,7 +73,7 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
||||
title={post.frontmatter.title}
|
||||
slug={slug}
|
||||
category={post.frontmatter.category}
|
||||
readingTime={getReadingTime(rawTextContent)}
|
||||
readingTime={readingTime}
|
||||
/>
|
||||
|
||||
{/* Featured Image Header */}
|
||||
@@ -131,7 +118,7 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
||||
})}
|
||||
</time>
|
||||
<span className="w-1 h-1 bg-white/30 rounded-full" />
|
||||
<span>{getReadingTime(rawTextContent)} min read</span>
|
||||
<span>{readingTime} min read</span>
|
||||
{(new Date(post.frontmatter.date) > new Date() ||
|
||||
post.frontmatter.public === false) && (
|
||||
<>
|
||||
@@ -168,7 +155,7 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
||||
})}
|
||||
</time>
|
||||
<span className="w-1 h-1 bg-neutral-400 rounded-full" />
|
||||
<span>{getReadingTime(rawTextContent)} min read</span>
|
||||
<span>{readingTime} min read</span>
|
||||
{(new Date(post.frontmatter.date) > new Date() ||
|
||||
post.frontmatter.public === false) && (
|
||||
<>
|
||||
@@ -197,9 +184,9 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Main content with enhanced styling rendering Payload Lexical */}
|
||||
{/* Main content */}
|
||||
<div className="prose prose-lg md:prose-xl max-w-none prose-headings:font-bold prose-headings:text-text-primary prose-p:text-text-secondary prose-p:leading-relaxed prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-img:rounded-2xl prose-img:shadow-2xl prose-blockquote:border-primary prose-blockquote:bg-primary/5 prose-blockquote:rounded-r-2xl prose-strong:text-primary">
|
||||
<PayloadRichText data={post.content} />
|
||||
<MDXRemote source={post.content} />
|
||||
</div>
|
||||
|
||||
{/* Power CTA */}
|
||||
@@ -284,8 +271,8 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
||||
'@id': `${SITE_URL}/${locale}/blog/${slug}`,
|
||||
},
|
||||
articleSection: post.frontmatter.category,
|
||||
wordCount: rawTextContent.split(/\s+/).length,
|
||||
timeRequired: `PT${getReadingTime(rawTextContent)}M`,
|
||||
wordCount: post.content.split(/\s+/).length,
|
||||
timeRequired: `PT${readingTime}M`,
|
||||
} as any
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -62,7 +62,8 @@ export default async function ContactPage({ params }: ContactPageProps) {
|
||||
setRequestLocale(locale);
|
||||
const t = await getTranslations({ locale, namespace: 'Contact' });
|
||||
|
||||
// Get translated slug to redirect if user used incorrect static slug
|
||||
// Get translated slug - redirect logic removed to prevent loop with next.config.js rewrites
|
||||
/*
|
||||
const { headers } = await import('next/headers');
|
||||
const headersList = await headers();
|
||||
const urlPath = headersList.get('x-invoke-path') || '';
|
||||
@@ -75,6 +76,7 @@ export default async function ContactPage({ params }: ContactPageProps) {
|
||||
redirect(`/${locale}/${contactSlugDe}`);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-neutral-light">
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Footer } from '@/components/layout/Footer';
|
||||
import { Header } from '@/components/layout/Header';
|
||||
import JsonLd from '@/components/JsonLd';
|
||||
import SkipLink from '@/components/SkipLink';
|
||||
import CMSConnectivityNotice from '@/components/CMSConnectivityNotice';
|
||||
import AnalyticsShell from '@/components/analytics/AnalyticsShell';
|
||||
import { Metadata, Viewport } from 'next';
|
||||
import { NextIntlClientProvider } from 'next-intl';
|
||||
@@ -12,8 +11,7 @@ import { SITE_URL } from '@/lib/schema';
|
||||
import FeedbackClientWrapper from '@/components/FeedbackClientWrapper';
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
import { Inter } from 'next/font/google';
|
||||
import { getPayload } from 'payload';
|
||||
import configPromise from '@payload-config';
|
||||
import { mapFileSlugToTranslated } from '@/lib/slugs';
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
@@ -80,32 +78,30 @@ export default async function Layout(props: {
|
||||
let messages: Record<string, any> = {};
|
||||
try {
|
||||
messages = await getMessages();
|
||||
} catch {
|
||||
} catch (err) {
|
||||
console.error('[Layout] Failed to load messages:', err);
|
||||
messages = {};
|
||||
}
|
||||
|
||||
// Fetch navigation and company info from Payload Global Settings
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
const settings = await payload.findGlobal({
|
||||
slug: 'settings',
|
||||
locale: safeLocale as any,
|
||||
}).catch(() => null);
|
||||
|
||||
const navLinks = (settings?.mainNav && settings.mainNav.length > 0)
|
||||
? settings.mainNav.map((n) => ({
|
||||
label: n.label,
|
||||
url: n.url,
|
||||
}))
|
||||
: [
|
||||
{ label: 'Kompetenzen', url: `/${safeLocale}/kompetenzen` },
|
||||
{ label: 'Über uns', url: `/${safeLocale}/ueber-uns` },
|
||||
{ label: 'Karriere & Messen', url: `/${safeLocale}/karriere` }
|
||||
const navLinks = [
|
||||
{
|
||||
label: safeLocale === 'de' ? 'Kompetenzen' : 'Competencies',
|
||||
url: `/${safeLocale}/${await mapFileSlugToTranslated('kompetenzen', safeLocale)}`
|
||||
},
|
||||
{
|
||||
label: safeLocale === 'de' ? 'Über uns' : 'About Us',
|
||||
url: `/${safeLocale}/${await mapFileSlugToTranslated('ueber-uns', safeLocale)}`
|
||||
},
|
||||
{
|
||||
label: safeLocale === 'de' ? 'Karriere & Messen' : 'Careers & Events',
|
||||
url: `/${safeLocale}/${await mapFileSlugToTranslated('karriere', safeLocale)}`
|
||||
}
|
||||
];
|
||||
|
||||
const companyInfo = {
|
||||
contactEmail: settings?.contactEmail || 'info@e-tib.com',
|
||||
contactPhone: settings?.contactPhone || '+49 15207230518',
|
||||
address: settings?.address || 'Gewerbestraße 22\\n03172 Guben'
|
||||
contactEmail: 'info@e-tib.com',
|
||||
contactPhone: '+49 15207230518',
|
||||
address: 'Gewerbestraße 22\n03172 Guben'
|
||||
};
|
||||
|
||||
// Pick only the namespaces required by client components to reduce the hydration payload size
|
||||
@@ -185,7 +181,6 @@ export default async function Layout(props: {
|
||||
|
||||
<Footer companyInfo={companyInfo} />
|
||||
<JsonLd />
|
||||
<CMSConnectivityNotice />
|
||||
<AnalyticsShell />
|
||||
{feedbackEnabled && <FeedbackClientWrapper feedbackEnabled={feedbackEnabled} />}
|
||||
</NextIntlClientProvider>
|
||||
|
||||
@@ -1,87 +1,19 @@
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { Container, Button, Heading } from '@/components/ui';
|
||||
import Scribble from '@/components/Scribble';
|
||||
import { getPayload } from 'payload';
|
||||
import configPromise from '@payload-config';
|
||||
import { headers } from 'next/headers';
|
||||
import ClientNotFoundTracker from '@/components/analytics/ClientNotFoundTracker';
|
||||
|
||||
export default async function NotFound() {
|
||||
console.log('--- NOT FOUND RENDERED ---');
|
||||
const t = await getTranslations('Error.notFound');
|
||||
|
||||
// Try to determine the requested path
|
||||
const headersList = await headers();
|
||||
const urlPath = headersList.get('x-invoke-path') || '';
|
||||
|
||||
// Suggested URL logic removed along with Payload CMS
|
||||
let suggestedUrl = null;
|
||||
let suggestedLang = null;
|
||||
|
||||
// If we have a path, try to see if the last segment (slug) exists in ANY locale
|
||||
if (urlPath) {
|
||||
const slug = urlPath.split('/').filter(Boolean).pop();
|
||||
if (slug) {
|
||||
try {
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
|
||||
// Check posts
|
||||
const postRes = await payload.find({
|
||||
collection: 'posts',
|
||||
where: { slug: { equals: slug } },
|
||||
locale: 'all',
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
// Check competences
|
||||
const competenceRes =
|
||||
postRes.docs.length === 0
|
||||
? await payload.find({
|
||||
collection: 'competences',
|
||||
where: { slug: { equals: slug } },
|
||||
locale: 'all',
|
||||
limit: 1,
|
||||
})
|
||||
: { docs: [] };
|
||||
|
||||
// Check pages
|
||||
const pageRes =
|
||||
postRes.docs.length === 0 && competenceRes.docs.length === 0
|
||||
? await payload.find({
|
||||
collection: 'pages',
|
||||
where: { slug: { equals: slug } },
|
||||
locale: 'all',
|
||||
limit: 1,
|
||||
})
|
||||
: { docs: [] };
|
||||
|
||||
const anyDoc = postRes.docs[0] || competenceRes.docs[0] || pageRes.docs[0];
|
||||
|
||||
if (anyDoc) {
|
||||
// If the doc exists, we can figure out its native locale or
|
||||
// offer the alternative locale (if we are in 'de', offer 'en')
|
||||
const currentLocale = urlPath.startsWith('/en') ? 'en' : 'de';
|
||||
const alternativeLocale = currentLocale === 'de' ? 'en' : 'de';
|
||||
|
||||
suggestedLang = alternativeLocale === 'de' ? 'Deutsch' : 'English';
|
||||
|
||||
// Reconstruct the URL for the alternative locale
|
||||
const pathParts = urlPath.split('/').filter(Boolean);
|
||||
if (pathParts.length > 0 && (pathParts[0] === 'en' || pathParts[0] === 'de')) {
|
||||
pathParts[0] = alternativeLocale;
|
||||
} else {
|
||||
pathParts.unshift(alternativeLocale);
|
||||
}
|
||||
suggestedUrl = '/' + pathParts.join('/');
|
||||
}
|
||||
} catch {
|
||||
// Ignore Payload errors in 404
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ClientNotFoundTracker path={urlPath} />
|
||||
<ClientNotFoundTracker path="" />
|
||||
<Container className="relative py-24 flex flex-col items-center justify-center text-center min-h-[70vh] overflow-hidden">
|
||||
{/* Industrial Background Element */}
|
||||
<div className="absolute inset-0 -z-10 opacity-[0.03] pointer-events-none flex items-center justify-center">
|
||||
|
||||
@@ -1,80 +1,52 @@
|
||||
import { Metadata } from 'next';
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { getPayload } from 'payload';
|
||||
import configPromise from '@payload-config';
|
||||
import PayloadRichText from '@/components/PayloadRichText';
|
||||
import { getMdxContent } from '@/lib/mdx';
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||
|
||||
// Import components used in MDX
|
||||
import { HeroVideo as HomeHero } from '@/components/blocks/HeroVideo';
|
||||
import { SubCompanyTiles as HomeSubCompanyTiles } from '@/components/blocks/SubCompanyTiles';
|
||||
import { CompetenceBentoGrid as HomeCompetenceBentoGrid } from '@/components/blocks/CompetenceBentoGrid';
|
||||
import { ReferencesSlider as HomeReferencesSlider } from '@/components/blocks/ReferencesSlider';
|
||||
|
||||
const mdxComponents = {
|
||||
HomeHero,
|
||||
HomeSubCompanyTiles,
|
||||
HomeCompetenceBentoGrid,
|
||||
HomeReferencesSlider,
|
||||
};
|
||||
|
||||
export async function generateMetadata(props: any): Promise<Metadata> {
|
||||
const { locale } = await props.params;
|
||||
if (locale !== 'de' && locale !== 'en') return {};
|
||||
|
||||
const mdx = await getMdxContent(locale, 'home');
|
||||
|
||||
return {
|
||||
title: locale === 'de' ? 'Startseite' : 'Home',
|
||||
title: mdx?.frontmatter?.title || (locale === 'de' ? 'Startseite' : 'Home'),
|
||||
description: mdx?.frontmatter?.description,
|
||||
};
|
||||
}
|
||||
|
||||
export default async function Home(props: any) {
|
||||
const { locale } = await props.params;
|
||||
|
||||
// Guard against invalid locales (e.g. favicon.ico) hitting the DB
|
||||
if (locale !== 'de' && locale !== 'en') {
|
||||
notFound();
|
||||
}
|
||||
|
||||
setRequestLocale(locale);
|
||||
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
const mdx = await getMdxContent(locale, 'home');
|
||||
|
||||
// 1. Fetch references for the ReferencesSlider (passed globally to PayloadRichText)
|
||||
const { docs: references } = await payload.find({
|
||||
collection: 'references',
|
||||
locale: locale as any,
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
const mappedReferences = references.map((ref: any) => ({
|
||||
id: ref.id,
|
||||
title: ref.title,
|
||||
slug: ref.slug,
|
||||
category: ref.category,
|
||||
image: ref.image,
|
||||
}));
|
||||
|
||||
// 2. Fetch the Home page content
|
||||
const { docs: pages } = await payload.find({
|
||||
collection: 'pages',
|
||||
where: {
|
||||
slug: {
|
||||
equals: 'home',
|
||||
},
|
||||
},
|
||||
locale: locale as any,
|
||||
limit: 1,
|
||||
draft: true, // Allow finding drafts in development
|
||||
});
|
||||
|
||||
const page = pages[0];
|
||||
|
||||
// Fallback to static components if page doesn't exist in CMS yet
|
||||
// This prevents the site from breaking before the first seed/save
|
||||
if (!page) {
|
||||
console.warn(`[Home] Page slug "home" not found in CMS for locale "${locale}".`);
|
||||
const { totalDocs: allPages } = await payload.find({ collection: 'pages', locale: 'all' });
|
||||
console.log(`[Home] Total pages in CMS (all locales): ${allPages}`);
|
||||
return (
|
||||
<main className="min-h-screen flex items-center justify-center bg-neutral-dark text-white p-10 text-center">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold mb-4">Willkommen bei E-TIB</h1>
|
||||
<p className="text-xl text-white/60">Die CMS-Inhalte für diese Seite müssen noch angelegt werden.</p>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
if (!mdx) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return (
|
||||
<main>
|
||||
<PayloadRichText data={page.content} references={mappedReferences} />
|
||||
<MDXRemote source={mdx.content} components={mdxComponents} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,28 +41,7 @@ export async function requestBrochureAction(formData: FormData) {
|
||||
return { success: false, error: 'Invalid email address' };
|
||||
}
|
||||
|
||||
// 1. Save to CMS
|
||||
try {
|
||||
const { getPayload } = await import('payload');
|
||||
const configPromise = (await import('@payload-config')).default;
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
|
||||
await payload.create({
|
||||
collection: 'form-submissions',
|
||||
data: {
|
||||
name: email.split('@')[0],
|
||||
email,
|
||||
message: `Brochure download request (${locale})`,
|
||||
type: 'brochure_download' as any,
|
||||
},
|
||||
overrideAccess: true,
|
||||
});
|
||||
|
||||
logger.info('Successfully saved brochure request to Payload CMS', { email });
|
||||
} catch (error) {
|
||||
logger.error('Failed to store brochure request in Payload CMS', { error });
|
||||
services.errors.captureException(error, { action: 'payload_store_brochure_request' });
|
||||
}
|
||||
// 1. Save to CMS - Removed along with Payload CMS
|
||||
|
||||
// 2. Notify via Gotify
|
||||
try {
|
||||
|
||||
@@ -47,32 +47,7 @@ export async function sendContactFormAction(formData: FormData) {
|
||||
return { success: false, error: 'Missing required fields' };
|
||||
}
|
||||
|
||||
// 1. Save to CMS
|
||||
try {
|
||||
const { getPayload } = await import('payload');
|
||||
const configPromise = (await import('@payload-config')).default;
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
|
||||
await payload.create({
|
||||
collection: 'form-submissions',
|
||||
data: {
|
||||
name,
|
||||
email,
|
||||
message,
|
||||
type: productName ? 'product_quote' : 'contact',
|
||||
productName: productName || undefined,
|
||||
},
|
||||
overrideAccess: true,
|
||||
});
|
||||
|
||||
logger.info('Successfully saved form submission to Payload CMS', {
|
||||
type: productName ? 'product_quote' : 'contact',
|
||||
email,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to store submission in Payload CMS', { error });
|
||||
services.errors.captureException(error, { action: 'payload_store_submission' });
|
||||
}
|
||||
// 1. Save to CMS - Removed along with Payload CMS
|
||||
|
||||
// 2. Send Emails
|
||||
logger.info('Sending branded emails', { email, productName });
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getPayload } from 'payload';
|
||||
import configPromise from '@payload-config';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
/**
|
||||
* Deep CMS Health Check
|
||||
* Validates that Payload CMS can actually query the database.
|
||||
* Used by post-deploy smoke tests to catch migration/schema issues.
|
||||
*/
|
||||
export async function GET() {
|
||||
const checks: Record<string, string> = {};
|
||||
|
||||
try {
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
checks.init = 'ok';
|
||||
|
||||
// Verify each collection can be queried (catches missing locale tables, broken migrations)
|
||||
const collections = ['posts', 'pages', 'media', 'competences'] as const;
|
||||
for (const collection of collections) {
|
||||
try {
|
||||
await payload.find({ collection, limit: 1 });
|
||||
checks[collection] = 'ok';
|
||||
} catch (e: any) {
|
||||
checks[collection] = `error: ${e.message?.substring(0, 100)}`;
|
||||
}
|
||||
}
|
||||
|
||||
const hasErrors = Object.values(checks).some((v) => v.startsWith('error'));
|
||||
return NextResponse.json(
|
||||
{ status: hasErrors ? 'degraded' : 'ok', checks },
|
||||
{ status: hasErrors ? 503 : 200 },
|
||||
);
|
||||
} catch (e: any) {
|
||||
return NextResponse.json(
|
||||
{ status: 'error', message: e.message?.substring(0, 200), checks },
|
||||
{ status: 503 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { AlertCircle, RefreshCw } from 'lucide-react';
|
||||
import { config } from '../lib/config';
|
||||
|
||||
export default function CMSConnectivityNotice() {
|
||||
const [, setStatus] = useState<'checking' | 'ok' | 'error'>('checking');
|
||||
const [errorMsg, setErrorMsg] = useState('');
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Only show if we've detected an issue AND we are in a context where we want to see it
|
||||
const checkCMS = async () => {
|
||||
const isDebug = new URLSearchParams(window.location.search).has('cms_debug');
|
||||
const isLocal = config.isDevelopment;
|
||||
const isTesting = config.isTesting;
|
||||
const target = process.env.NEXT_PUBLIC_TARGET || '';
|
||||
const isBranch = target === 'branch';
|
||||
|
||||
// Only proceed with check if it's developer context (Local, Testing, or Branch preview)
|
||||
// Staging and Production should NEVER see this unless forced with ?cms_debug
|
||||
if (!isLocal && !isTesting && !isBranch && !isDebug) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/health/cms');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.status !== 'ok') {
|
||||
setStatus('error');
|
||||
setErrorMsg(data.message);
|
||||
setIsVisible(true);
|
||||
} else {
|
||||
setStatus('ok');
|
||||
setIsVisible(false);
|
||||
}
|
||||
} catch {
|
||||
// If it's a connection error, only show if we are really debugging
|
||||
if (isDebug || isLocal) {
|
||||
setStatus('error');
|
||||
setErrorMsg('Could not connect to CMS health endpoint');
|
||||
setIsVisible(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
checkCMS();
|
||||
}, []);
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-4 right-4 z-[9999] animate-slide-up">
|
||||
<div className="bg-red-500/90 backdrop-blur-md border border-red-400 text-white p-4 rounded-2xl shadow-2xl max-w-sm">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="bg-white/20 p-2 rounded-lg">
|
||||
<AlertCircle className="w-5 h-5" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h4 className="font-bold text-sm mb-1">CMS Issue Detected</h4>
|
||||
<p className="text-xs opacity-90 leading-relaxed mb-3">
|
||||
{errorMsg === 'relation "products" does not exist'
|
||||
? 'The database schema is missing. Please run migrations for this environment.'
|
||||
: 'A content service is unavailable. Check the deployment logs for details.'}
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => window.location.reload()}
|
||||
className="bg-white text-red-600 text-[10px] font-bold uppercase tracking-wider px-3 py-1.5 rounded-lg flex items-center gap-2 hover:bg-neutral-100 transition-colors"
|
||||
>
|
||||
<RefreshCw className="w-3 h-3" />
|
||||
Retry
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsVisible(false)}
|
||||
className="bg-black/20 text-white text-[10px] font-bold uppercase tracking-wider px-3 py-1.5 rounded-lg hover:bg-black/30 transition-colors"
|
||||
>
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
import { defaultJSXConverters, RichText } from '@payloadcms/richtext-lexical/react';
|
||||
import type { JSXConverters } from '@payloadcms/richtext-lexical/react';
|
||||
import Image from 'next/image';
|
||||
import { Fragment } from 'react';
|
||||
import { allBlocks } from '@/src/payload/blocks/allBlocks';
|
||||
import ObfuscatedEmail from '@/components/ObfuscatedEmail';
|
||||
import ObfuscatedPhone from '@/components/ObfuscatedPhone';
|
||||
|
||||
/**
|
||||
* Splits a text string on \n and intersperses <br /> elements.
|
||||
*/
|
||||
function textWithLineBreaks(text: string, key: string) {
|
||||
const parts = text.split('\n');
|
||||
if (parts.length === 1) return text;
|
||||
return parts.map((part, i) => (
|
||||
<Fragment key={`${key}-${i}`}>
|
||||
{part}
|
||||
{i < parts.length - 1 && <br />}
|
||||
</Fragment>
|
||||
));
|
||||
}
|
||||
|
||||
const jsxConverters: JSXConverters = {
|
||||
...defaultJSXConverters,
|
||||
linebreak: () => <br />,
|
||||
text: ({ node }: any) => {
|
||||
let content: React.ReactNode = node.text || '';
|
||||
if (typeof content === 'string' && content.includes('\n')) {
|
||||
content = textWithLineBreaks(content, `t-${(node.text || '').slice(0, 8)}`);
|
||||
}
|
||||
|
||||
if (typeof content === 'string' && content.includes('@')) {
|
||||
const emailRegex = /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g;
|
||||
const parts = content.split(emailRegex);
|
||||
content = parts.map((part, i) => {
|
||||
if (part.match(emailRegex)) {
|
||||
return <ObfuscatedEmail key={`e-${i}`} email={part} />;
|
||||
}
|
||||
return part;
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof content === 'string' && content.match(/\+\d+/)) {
|
||||
const phoneRegex = /(\+\d{1,4}[\d\s-]{5,15})/g;
|
||||
const parts = content.split(phoneRegex);
|
||||
content = parts.map((part, i) => {
|
||||
if (part.match(phoneRegex)) {
|
||||
return <ObfuscatedPhone key={`p-${i}`} phone={part} />;
|
||||
}
|
||||
return part;
|
||||
});
|
||||
}
|
||||
|
||||
return content;
|
||||
},
|
||||
upload: ({ node }: any) => {
|
||||
return (
|
||||
<div className="relative w-full aspect-video my-8 rounded-3xl overflow-hidden shadow-2xl group">
|
||||
<Image
|
||||
src={node.value.url}
|
||||
alt={node.value.alt || ''}
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
className="object-cover transition-transform duration-700 group-hover:scale-105"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
blocks: allBlocks.reduce((acc, block) => {
|
||||
const Component = block.render;
|
||||
if (!Component) return acc;
|
||||
|
||||
const renderFn = ({ node }: any) => {
|
||||
// Pass all fields as props to the component, and also as 'data' prop
|
||||
return <Component {...node.fields} data={node.fields} />;
|
||||
};
|
||||
|
||||
// Map both the direct slug and the block- prefixed slug
|
||||
return {
|
||||
...acc,
|
||||
[block.slug]: renderFn,
|
||||
[`block-${block.slug}`]: renderFn,
|
||||
};
|
||||
}, {}),
|
||||
};
|
||||
|
||||
export default function PayloadRichText({ data, className, references }: { data: any; className?: string; references?: any[] }) {
|
||||
if (!data) return null;
|
||||
return (
|
||||
<div className={className}>
|
||||
<RichText data={data} converters={jsxConverters} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { useLocale } from 'next-intl';
|
||||
|
||||
interface CompanyInfo {
|
||||
contactEmail: string;
|
||||
@@ -12,11 +13,13 @@ interface FooterProps {
|
||||
}
|
||||
|
||||
export function Footer({ companyInfo }: FooterProps) {
|
||||
const locale = useLocale();
|
||||
|
||||
return (
|
||||
<footer className="bg-neutral-dark text-neutral-light py-16">
|
||||
<div className="container grid grid-cols-1 md:grid-cols-4 gap-12">
|
||||
<div className="col-span-1 md:col-span-2">
|
||||
<Link href="/" className="relative block h-14 w-56 mb-6">
|
||||
<Link href={`/${locale}`} className="relative block h-14 w-56 mb-6">
|
||||
<Image
|
||||
src="/assets/logo-white.png"
|
||||
alt="E-TIB Gruppe"
|
||||
@@ -38,17 +41,18 @@ export function Footer({ companyInfo }: FooterProps) {
|
||||
<div>
|
||||
<h4 className="font-heading font-semibold text-lg mb-4">Unternehmen</h4>
|
||||
<ul className="space-y-2">
|
||||
<li><Link href="/kompetenzen" className="text-text-light hover:text-primary-light transition-colors">Kompetenzen</Link></li>
|
||||
<li><Link href="/ueber-uns" className="text-text-light hover:text-primary-light transition-colors">Über uns</Link></li>
|
||||
<li><Link href="/karriere" className="text-text-light hover:text-primary-light transition-colors">Karriere / Jobs</Link></li>
|
||||
<li><Link href="/kontakt" className="text-text-light hover:text-primary-light transition-colors">Kontakt</Link></li>
|
||||
<li><Link href={`/${locale}/kompetenzen`} className="text-text-light hover:text-primary-light transition-colors">Kompetenzen</Link></li>
|
||||
<li><Link href={`/${locale}/ueber-uns`} className="text-text-light hover:text-primary-light transition-colors">Über uns</Link></li>
|
||||
<li><Link href={`/${locale}/karriere`} className="text-text-light hover:text-primary-light transition-colors">Karriere / Jobs</Link></li>
|
||||
<li><Link href={`/${locale}/kontakt`} className="text-text-light hover:text-primary-light transition-colors">Kontakt</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-heading font-semibold text-lg mb-4">Rechtliches</h4>
|
||||
<ul className="space-y-2">
|
||||
<li><Link href="/impressum" className="text-text-light hover:text-primary-light transition-colors">Impressum</Link></li>
|
||||
<li><Link href="/datenschutz" className="text-text-light hover:text-primary-light transition-colors">Datenschutz</Link></li>
|
||||
<li><Link href={`/${locale}/impressum`} className="text-text-light hover:text-primary-light transition-colors">Impressum</Link></li>
|
||||
<li><Link href={`/${locale}/datenschutz`} className="text-text-light hover:text-primary-light transition-colors">Datenschutz</Link></li>
|
||||
<li><Link href={`/${locale}/${locale === 'de' ? 'agb' : 'terms'}`} className="text-text-light hover:text-primary-light transition-colors">{locale === 'de' ? 'AGB' : 'Terms'}</Link></li>
|
||||
<li><button className="text-text-light hover:text-primary-light transition-colors">Cookie-Einstellungen</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -95,10 +95,10 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
<div className="h-4 w-px bg-white/20 mx-2" />
|
||||
<LanguageSwitcher />
|
||||
<Link
|
||||
href={`/${currentLocale}/kontakt`}
|
||||
href={`/${currentLocale}/${currentLocale === 'de' ? 'kontakt' : 'contact'}`}
|
||||
className="rounded-xl bg-primary hover:bg-primary-dark text-neutral-light px-8 py-3 font-bold text-sm uppercase tracking-wider transition-all shadow-xl hover:shadow-primary/20 hover:-translate-y-0.5 active:translate-y-0"
|
||||
>
|
||||
Kontakt
|
||||
{currentLocale === 'de' ? 'Kontakt' : 'Contact'}
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
@@ -154,10 +154,10 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
})}
|
||||
|
||||
<Link
|
||||
href={`/${currentLocale}/kontakt`}
|
||||
href={`/${currentLocale}/${currentLocale === 'de' ? 'kontakt' : 'contact'}`}
|
||||
className="text-2xl font-heading font-bold text-primary border-b border-neutral-100 pb-4"
|
||||
>
|
||||
Kontakt
|
||||
{currentLocale === 'de' ? 'Kontakt' : 'Contact'}
|
||||
</Link>
|
||||
|
||||
<div className="mt-8 pt-6">
|
||||
|
||||
22
content/de/agb.mdx
Normal file
22
content/de/agb.mdx
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: "Allgemeine Geschäftsbedingungen"
|
||||
date: "2024-03-20"
|
||||
excerpt: "Die Allgemeinen Geschäftsbedingungen der E-TIB GmbH für unsere Leistungen und Lieferungen."
|
||||
---
|
||||
|
||||
# Allgemeine Geschäftsbedingungen (AGB)
|
||||
|
||||
## 1. Geltungsbereich
|
||||
Diese Allgemeinen Geschäftsbedingungen gelten für alle Verträge, Lieferungen und sonstigen Leistungen der E-TIB GmbH gegenüber ihren Kunden.
|
||||
|
||||
## 2. Vertragsabschluss
|
||||
Ein Vertrag kommt erst durch unsere schriftliche Auftragsbestätigung oder durch die Ausführung des Auftrags zustande.
|
||||
|
||||
## 3. Preise und Zahlungsbedingungen
|
||||
Alle Preise verstehen sich in Euro zzgl. der gesetzlichen Mehrwertsteuer. Zahlungen sind innerhalb von 14 Tagen nach Rechnungsstellung ohne Abzug zu leisten.
|
||||
|
||||
## 4. Haftung
|
||||
Wir haften für Schäden nur bei Vorsatz oder grober Fahrlässigkeit. Die Haftung für leichte Fahrlässigkeit ist ausgeschlossen, sofern keine wesentlichen Vertragspflichten verletzt wurden.
|
||||
|
||||
## 5. Gerichtsstand
|
||||
Gerichtsstand für alle Streitigkeiten aus dem Vertragsverhältnis ist Cottbus, sofern der Kunde Kaufmann ist.
|
||||
30
content/de/blog/moderne-verfahren-kabeltiefbau.mdx
Normal file
30
content/de/blog/moderne-verfahren-kabeltiefbau.mdx
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: "Moderne Verfahren im Kabeltiefbau"
|
||||
date: "2024-03-15T10:00:00.000Z"
|
||||
excerpt: "Erfahren Sie mehr über die neuesten Technologien und Verfahren im modernen Kabelleitungstiefbau, von Horizontalspülbohrungen bis hin zu grabenlosen Verlegetechniken."
|
||||
featuredImage: "/assets/images/blog/tiefbau.jpg"
|
||||
category: "Technik"
|
||||
public: true
|
||||
---
|
||||
|
||||
# Moderne Verfahren im Kabeltiefbau
|
||||
|
||||
Der Kabelleitungstiefbau hat sich in den letzten Jahren rasant entwickelt. Neue Technologien ermöglichen eine effizientere und umweltschonendere Verlegung von Versorgungsleitungen.
|
||||
|
||||
## Grabenlose Verlegetechniken
|
||||
|
||||
Eines der wichtigsten Verfahren ist die **Horizontalspülbohrung (HDD)**. Dieses Verfahren ermöglicht es, Leitungen unter Hindernissen wie Straßen, Flüssen oder Gebäuden zu verlegen, ohne die Oberfläche aufbrechen zu müssen.
|
||||
|
||||
### Vorteile der HDD-Bohrung:
|
||||
- Minimale Beeinträchtigung des Verkehrs
|
||||
- Schutz der Umwelt und vorhandener Vegetation
|
||||
- Kürzere Bauzeiten
|
||||
- Geringere Kosten bei komplexen Unterquerungen
|
||||
|
||||
## Klassischer Tiefbau in neuer Qualität
|
||||
|
||||
Auch der klassische Grabenbau profitiert von modernen Maschinen und präziser Planung. Durch den Einsatz von Saugbaggern und GPS-gestützter Vermessung können wir Schäden an Bestandsleitungen nahezu ausschließen.
|
||||
|
||||
## Fazit
|
||||
|
||||
Die Kombination aus bewährter Handwerkskunst und modernster Technik ist der Schlüssel zu erfolgreichen Infrastrukturprojekten. Wir bei E-TIB setzen konsequent auf Innovation, um unseren Kunden die bestmöglichen Ergebnisse zu liefern.
|
||||
43
content/de/datenschutz.mdx
Normal file
43
content/de/datenschutz.mdx
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
title: "Datenschutzerklärung"
|
||||
date: "2024-03-20"
|
||||
excerpt: "Informationen zum Schutz Ihrer persönlichen Daten bei E-TIB."
|
||||
---
|
||||
|
||||
# Datenschutzerklärung
|
||||
|
||||
## 1. Datenschutz auf einen Blick
|
||||
|
||||
### Allgemeine Hinweise
|
||||
Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren personenbezogenen Daten passiert, wenn Sie diese Website besuchen. Personenbezogene Daten sind alle Daten, mit denen Sie persönlich identifiziert werden können.
|
||||
|
||||
### Datenerfassung auf dieser Website
|
||||
Die Datenverarbeitung auf dieser Website erfolgt durch den Websitebetreiber. Dessen Kontaktdaten können Sie dem Abschnitt „Hinweis zur Verantwortlichen Stelle“ in dieser Datenschutzerklärung entnehmen.
|
||||
|
||||
## 2. Hosting
|
||||
|
||||
Wir hosten unsere Website bei einem externen Dienstleister (Hoster). Die personenbezogenen Daten, die auf dieser Website erfasst werden, werden auf den Servern des Hosters gespeichert. Hierbei kann es sich v. a. um IP-Adressen, Kontaktanfragen, Meta- und Kommunikationsdaten, Webseitenzugriffe und sonstige Daten, die über eine Website generiert werden, handeln.
|
||||
|
||||
## 3. Allgemeine Hinweise und Pflichtinformationen
|
||||
|
||||
### Datenschutz
|
||||
Die Betreiber dieser Seiten nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir behandeln Ihre personenbezogenen Daten vertraulich und entsprechend den gesetzlichen Datenschutzvorschriften sowie dieser Datenschutzerklärung.
|
||||
|
||||
### Hinweis zur Verantwortlichen Stelle
|
||||
Die verantwortliche Stelle für die Datenverarbeitung auf dieser Website ist:
|
||||
|
||||
E-TIB GmbH
|
||||
Gewerbestraße 22
|
||||
03172 Guben
|
||||
|
||||
Telefon: +49 1520 7230518
|
||||
E-Mail: d.joseph@e-tib.com
|
||||
|
||||
## 4. Datenerfassung auf dieser Website
|
||||
|
||||
### Kontaktformular
|
||||
Wenn Sie uns per Kontaktformular Anfragen zukommen lassen, werden Ihre Angaben aus dem Anfrageformular inklusive der von Ihnen dort angegebenen Kontaktdaten zwecks Bearbeitung der Anfrage und für den Fall von Anschlussfragen bei uns gespeichert. Diese Daten geben wir nicht ohne Ihre Einwilligung weiter.
|
||||
|
||||
## 5. Analyse-Tools und Werbung
|
||||
|
||||
Wir nutzen auf dieser Website Umami Analytics. Umami ist eine datenschutzfreundliche Alternative zu Google Analytics, die keine persönlichen Daten sammelt und keine Cookies verwendet.
|
||||
73
content/de/home.mdx
Normal file
73
content/de/home.mdx
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: Startseite
|
||||
description: Willkommen bei E-TIB GmbH
|
||||
---
|
||||
|
||||
<HomeHero
|
||||
badge="Zukunftssichere Infrastruktur"
|
||||
title="Energie & Kommunikation vernetzen"
|
||||
description="Wir realisieren komplexe Versorgungsleitungen für eine moderne Gesellschaft. Verlässlich, innovativ und mit höchster Präzision im Kabelbau und der Bohrtechnik."
|
||||
videoUrl="https://cdn.mintel.me/video/etib-hero.mp4"
|
||||
linkText="Unsere Leistungen"
|
||||
linkHref="/de/kompetenzen"
|
||||
/>
|
||||
|
||||
<HomeSubCompanyTiles
|
||||
badge="Unsere Unternehmen"
|
||||
title="Die E-TIB Gruppe"
|
||||
companies={[
|
||||
{
|
||||
title: "E-TIB GmbH",
|
||||
description: "Ausführung elektrischer Infrastrukturprojekte",
|
||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
|
||||
},
|
||||
{
|
||||
title: "E-TIB Bohrtechnik GmbH",
|
||||
description: "Präzise Horizontalbohrungen in allen Bodenklassen",
|
||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
|
||||
},
|
||||
{
|
||||
title: "E-TIB Verwaltung GmbH",
|
||||
description: "Zentrale Dienste, Einkauf, Finanzen",
|
||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
|
||||
},
|
||||
{
|
||||
title: "E-TIB Ingenieurgesellschaft mbH",
|
||||
description: "Planung, Projektierung, Dokumentation",
|
||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
<HomeCompetenceBentoGrid
|
||||
badge="Unsere Kompetenzen"
|
||||
title="Alles aus einer Hand"
|
||||
ctaLabel="Alle Leistungen"
|
||||
ctaHref="/de/kompetenzen"
|
||||
items={[
|
||||
{
|
||||
title: "Kabelbau",
|
||||
description: "Verlegung von Mittel- und Niederspannungskabeln",
|
||||
tag: "Energie",
|
||||
size: "large"
|
||||
},
|
||||
{
|
||||
title: "Bohrtechnik",
|
||||
description: "Horizontalspülbohrverfahren für grabenlose Verlegung",
|
||||
tag: "Innovation",
|
||||
size: "medium"
|
||||
},
|
||||
{
|
||||
title: "Planung",
|
||||
description: "Trassenplanung und Genehmigungsverfahren",
|
||||
tag: "Präzision",
|
||||
size: "small"
|
||||
},
|
||||
{
|
||||
title: "Vermessung",
|
||||
description: "Digitale Bestandsdokumentation",
|
||||
tag: "Digital",
|
||||
size: "accent"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
47
content/de/impressum.mdx
Normal file
47
content/de/impressum.mdx
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: "Impressum"
|
||||
date: "2024-03-20"
|
||||
excerpt: "Rechtliche Informationen zur E-TIB GmbH."
|
||||
---
|
||||
|
||||
# Impressum
|
||||
|
||||
## Angaben gemäß § 5 TMG
|
||||
|
||||
E-TIB GmbH
|
||||
Gewerbestraße 22
|
||||
03172 Guben
|
||||
|
||||
**Vertreten durch:**
|
||||
Danny Joseph
|
||||
|
||||
## Kontakt
|
||||
|
||||
Telefon: +49 1520 7230518
|
||||
E-Mail: d.joseph@e-tib.com
|
||||
|
||||
## Registereintrag
|
||||
|
||||
Eintragung im Handelsregister.
|
||||
Registergericht: Amtsgericht Cottbus
|
||||
Registernummer: HRB 14781 CB
|
||||
|
||||
## Umsatzsteuer-ID
|
||||
|
||||
Umsatzsteuer-Identifikationsnummer gemäß § 27 a Umsatzsteuergesetz:
|
||||
DE332345678 (Beispiel)
|
||||
|
||||
## Redaktionell verantwortlich
|
||||
|
||||
Danny Joseph
|
||||
Gewerbestraße 22
|
||||
03172 Guben
|
||||
|
||||
## EU-Streitschlichtung
|
||||
|
||||
Die Europäische Kommission stellt eine Plattform zur Online-Streitbeilegung (OS) bereit: [https://ec.europa.eu/consumers/odr/](https://ec.europa.eu/consumers/odr/).
|
||||
Unsere E-Mail-Adresse finden Sie oben im Impressum.
|
||||
|
||||
## Verbraucherstreitbeilegung/Universalschlichtungsstelle
|
||||
|
||||
Wir sind nicht bereit oder verpflichtet, an Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teilzunehmen.
|
||||
35
content/de/karriere.mdx
Normal file
35
content/de/karriere.mdx
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: "Karriere bei E-TIB"
|
||||
date: "2024-03-20"
|
||||
excerpt: "Werden Sie Teil eines dynamischen Teams und bauen Sie mit uns die Infrastruktur der Zukunft."
|
||||
---
|
||||
|
||||
# Karriere
|
||||
|
||||
Suchen Sie eine neue Herausforderung in einem zukunftssicheren Markt? Bei **E-TIB** arbeiten wir täglich daran, Deutschland zu vernetzen und die Energiewende voranzutreiben.
|
||||
|
||||
## Warum E-TIB?
|
||||
|
||||
Wir bieten mehr als nur einen Job. Bei uns erwartet Sie:
|
||||
|
||||
- **Sicherheit**: Ein unbefristeter Arbeitsvertrag in einer wachsenden Branche.
|
||||
- **Entwicklung**: Regelmäßige Schulungen und Aufstiegsmöglichkeiten.
|
||||
- **Teamgeist**: Flache Hierarchien und ein kollegiales Umfeld.
|
||||
- **Ausstattung**: Modernste Maschinen und hochwertige Arbeitskleidung.
|
||||
|
||||
## Offene Stellen
|
||||
|
||||
Wir suchen aktuell Verstärkung in folgenden Bereichen:
|
||||
|
||||
- **Tiefbaufacharbeiter (m/w/d)**
|
||||
- **Maschinisten für HDD-Bohrgeräte (m/w/d)**
|
||||
- **Elektromonteure (m/w/d)**
|
||||
- **Bauleiter Kabeltiefbau (m/w/d)**
|
||||
|
||||
## Initiativbewerbung
|
||||
|
||||
Nichts Passendes dabei? Wir freuen uns auch über Initiativbewerbungen von motivierten Talenten.
|
||||
|
||||
## Jetzt bewerben
|
||||
|
||||
Senden Sie Ihre Unterlagen einfach per E-Mail an **karriere@e-tib.com** oder nutzen Sie unser Kontaktformular. Wir freuen uns darauf, Sie kennenzulernen!
|
||||
38
content/de/kompetenzen.mdx
Normal file
38
content/de/kompetenzen.mdx
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: "Unsere Kompetenzen"
|
||||
date: "2024-03-20"
|
||||
excerpt: "Von Kabelbau bis Elektromontage: Wir bieten ganzheitliche Lösungen für moderne Netzinfrastrukturen."
|
||||
---
|
||||
|
||||
# Kompetenzen
|
||||
|
||||
Als Full-Service-Partner im Bereich Kabelleitungstiefbau decken wir das gesamte Spektrum moderner Infrastrukturprojekte ab.
|
||||
|
||||
## Kabelleitungstiefbau & Montage
|
||||
|
||||
Wir verlegen Kabel- und Rohrsysteme für alle Spannungsbereiche und Medientypen.
|
||||
|
||||
- **Kabelbau**: Klassischer Grabenbau und Verlegung.
|
||||
- **Kabelpflugarbeiten**: Schnelle und effiziente Verlegung in geeignetem Gelände.
|
||||
- **Glasfaser**: Ausbau von Breitbandnetzen (FTTX).
|
||||
- **Elektromontage**: Montage und Anschluss von Kabeln bis 110 kV.
|
||||
|
||||
## Bohrtechnik (HDD)
|
||||
|
||||
Mit unseren gesteuerten Horizontalspülbohrungen unterqueren wir Hindernisse präzise und oberflächenschonend.
|
||||
|
||||
- **Präzision**: Punktgenaue Zielbohrungen auch über lange Distanzen.
|
||||
- **Vielseitigkeit**: Einsatz in allen Bodenklassen.
|
||||
- **Effizienz**: Minimale Baustelleneinrichtung und schnelle Durchführung.
|
||||
|
||||
## Planung & Dokumentation
|
||||
|
||||
Ein Projekt ist erst abgeschlossen, wenn die Dokumentation perfekt ist.
|
||||
|
||||
- **Trassenplanung**: Optimale Wegführung unter Berücksichtigung aller Randbedingungen.
|
||||
- **Genehmigungsverfahren**: Wir kümmern uns um den Papierkram.
|
||||
- **GIS-Dokumentation**: Digitale Erfassung der verlegten Infrastruktur.
|
||||
|
||||
## Bereit für Ihr Projekt?
|
||||
|
||||
Kontaktieren Sie uns für eine unverbindliche Beratung zu Ihrem Vorhaben.
|
||||
34
content/de/ueber-uns.mdx
Normal file
34
content/de/ueber-uns.mdx
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: "Über E-TIB"
|
||||
date: "2024-03-20"
|
||||
excerpt: "Die E-TIB Gruppe ist Ihr kompetenter Partner für Kabeltiefbau, Bohrtechnik und Ingenieurdienstleistungen."
|
||||
---
|
||||
|
||||
# Über uns
|
||||
|
||||
Die **E-TIB Gruppe** steht für höchste Qualität und Zuverlässigkeit im Bereich der Energie- und Dateninfrastruktur. Mit unserem Hauptsitz in Guben und Projekten im gesamten Bundesgebiet sind wir ein wichtiger Akteur bei der Umsetzung der Energiewende.
|
||||
|
||||
## Unsere Philosophie
|
||||
|
||||
Wir glauben daran, dass eine stabile Infrastruktur das Fundament für eine erfolgreiche Zukunft ist. Deshalb setzen wir auf:
|
||||
|
||||
- **Präzision**: Exakte Planung und Ausführung sind für uns selbstverständlich.
|
||||
- **Innovation**: Wir nutzen modernste Technik wie Horizontalspülbohrungen, um Projekte effizienter zu gestalten.
|
||||
- **Nachhaltigkeit**: Unsere Verfahren sind darauf ausgelegt, die Umwelt so wenig wie möglich zu belasten.
|
||||
|
||||
## Die E-TIB Gruppe
|
||||
|
||||
Unsere Stärke liegt in der Bündelung spezialisierter Kompetenzen:
|
||||
|
||||
### E-TIB GmbH
|
||||
Fokus auf die Ausführung elektrischer Infrastrukturprojekte und klassischen Kabeltiefbau.
|
||||
|
||||
### E-TIB Bohrtechnik GmbH
|
||||
Spezialisten für grabenlose Verlegetechniken und gesteuerte Horizontalbohrungen.
|
||||
|
||||
### E-TIB Ingenieurgesellschaft mbH
|
||||
Ihr Partner für Planung, Projektierung und die lückenlose Dokumentation komplexer Bauvorhaben.
|
||||
|
||||
## Kontaktieren Sie uns
|
||||
|
||||
Möchten Sie mehr über unsere Arbeit erfahren? Wir freuen uns auf das Gespräch mit Ihnen.
|
||||
22
content/en/agb.mdx
Normal file
22
content/en/agb.mdx
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: "Terms and Conditions"
|
||||
date: "2024-03-20"
|
||||
excerpt: "The General Terms and Conditions of E-TIB GmbH for our services and deliveries."
|
||||
---
|
||||
|
||||
# Terms and Conditions (T&C)
|
||||
|
||||
## 1. Scope
|
||||
These General Terms and Conditions apply to all contracts, deliveries, and other services provided by E-TIB GmbH to its customers.
|
||||
|
||||
## 2. Conclusion of Contract
|
||||
A contract is only formed upon our written order confirmation or by the execution of the order.
|
||||
|
||||
## 3. Prices and Payment Terms
|
||||
All prices are in Euro plus the statutory value-added tax. Payments are to be made within 14 days of the invoice date without any deduction.
|
||||
|
||||
## 4. Liability
|
||||
We are liable for damages only in cases of intent or gross negligence. Liability for slight negligence is excluded, provided that no essential contractual obligations have been violated.
|
||||
|
||||
## 5. Jurisdiction
|
||||
The place of jurisdiction for all disputes arising from the contractual relationship is Cottbus, provided the customer is a merchant.
|
||||
30
content/en/blog/modern-methods-civil-engineering.mdx
Normal file
30
content/en/blog/modern-methods-civil-engineering.mdx
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: "Modern Methods in Cable Civil Engineering"
|
||||
date: "2024-03-15T10:00:00.000Z"
|
||||
excerpt: "Learn more about the latest technologies and methods in modern cable civil engineering, from horizontal directional drilling to trenchless installation techniques."
|
||||
featuredImage: "/assets/images/blog/tiefbau.jpg"
|
||||
category: "Technology"
|
||||
public: true
|
||||
---
|
||||
|
||||
# Modern Methods in Cable Civil Engineering
|
||||
|
||||
Cable civil engineering has evolved rapidly in recent years. New technologies enable more efficient and environmentally friendly installation of utility lines.
|
||||
|
||||
## Trenchless Installation Techniques
|
||||
|
||||
One of the most important methods is **Horizontal Directional Drilling (HDD)**. This method allows pipes to be laid under obstacles such as roads, rivers, or buildings without having to break open the surface.
|
||||
|
||||
### Benefits of HDD Drilling:
|
||||
- Minimal impact on traffic
|
||||
- Protection of the environment and existing vegetation
|
||||
- Shorter construction times
|
||||
- Lower costs for complex crossings
|
||||
|
||||
## Classic Civil Engineering in New Quality
|
||||
|
||||
Classic trench construction also benefits from modern machinery and precise planning. By using suction excavators and GPS-supported surveying, we can virtually eliminate damage to existing utility lines.
|
||||
|
||||
## Conclusion
|
||||
|
||||
The combination of proven craftsmanship and state-of-the-art technology is the key to successful infrastructure projects. At E-TIB, we consistently focus on innovation to deliver the best possible results for our customers.
|
||||
44
content/en/datenschutz.mdx
Normal file
44
content/en/datenschutz.mdx
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: "Privacy Policy"
|
||||
date: "2024-03-20"
|
||||
excerpt: "Information on the protection of your personal data at E-TIB."
|
||||
---
|
||||
|
||||
# Privacy Policy
|
||||
|
||||
## 1. Privacy at a Glance
|
||||
|
||||
### General Information
|
||||
The following information provides a simple overview of what happens to your personal data when you visit this website. Personal data is any data that can be used to personally identify you.
|
||||
|
||||
### Data Collection on this Website
|
||||
The data processing on this website is carried out by the website operator. You can find their contact details in the "Notice on the Responsible Party" section of this privacy policy.
|
||||
|
||||
## 2. Hosting
|
||||
|
||||
We host our website with an external service provider (hoster). The personal data collected on this website is stored on the hoster's servers. This may include IP addresses, contact requests, meta and communication data, website access, and other data generated via a website.
|
||||
|
||||
## 3. General Notes and Mandatory Information
|
||||
|
||||
### Data Protection
|
||||
The operators of these pages take the protection of your personal data very seriously. We treat your personal data confidentially and in accordance with the statutory data protection regulations and this privacy policy.
|
||||
|
||||
### Notice on the Responsible Party
|
||||
The responsible party for data processing on this website is:
|
||||
|
||||
E-TIB GmbH
|
||||
Gewerbestraße 22
|
||||
03172 Guben
|
||||
Germany
|
||||
|
||||
Phone: +49 1520 7230518
|
||||
Email: d.joseph@e-tib.com
|
||||
|
||||
## 4. Data Collection on this Website
|
||||
|
||||
### Contact Form
|
||||
If you send us inquiries via the contact form, your details from the inquiry form, including the contact details you provided there, will be stored by us for the purpose of processing the inquiry and in case of follow-up questions. We do not pass on this data without your consent.
|
||||
|
||||
## 5. Analysis Tools and Advertising
|
||||
|
||||
We use Umami Analytics on this website. Umami is a privacy-friendly alternative to Google Analytics that does not collect personal data and does not use cookies.
|
||||
73
content/en/home.mdx
Normal file
73
content/en/home.mdx
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: Home
|
||||
description: Welcome to E-TIB GmbH
|
||||
---
|
||||
|
||||
<HomeHero
|
||||
badge="Future-Proof Infrastructure"
|
||||
title="Networking Energy & Communication"
|
||||
description="We realize complex supply lines for a modern society. Reliable, innovative and with the highest precision in cable construction and drilling technology."
|
||||
videoUrl="https://cdn.mintel.me/video/etib-hero.mp4"
|
||||
linkText="Our Services"
|
||||
linkHref="/en/competencies"
|
||||
/>
|
||||
|
||||
<HomeSubCompanyTiles
|
||||
badge="Our Companies"
|
||||
title="The E-TIB Group"
|
||||
companies={[
|
||||
{
|
||||
title: "E-TIB GmbH",
|
||||
description: "Execution of electrical infrastructure projects",
|
||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
|
||||
},
|
||||
{
|
||||
title: "E-TIB Bohrtechnik GmbH",
|
||||
description: "Precise horizontal drilling in all soil classes",
|
||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
|
||||
},
|
||||
{
|
||||
title: "E-TIB Verwaltung GmbH",
|
||||
description: "Central services, purchasing, finance",
|
||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
|
||||
},
|
||||
{
|
||||
title: "E-TIB Ingenieurgesellschaft mbH",
|
||||
description: "Planning, project management, documentation",
|
||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
<HomeCompetenceBentoGrid
|
||||
badge="Our Competencies"
|
||||
title="Everything from a single source"
|
||||
ctaLabel="All Services"
|
||||
ctaHref="/en/competencies"
|
||||
items={[
|
||||
{
|
||||
title: "Cable Construction",
|
||||
description: "Installation of medium and low voltage cables",
|
||||
tag: "Energy",
|
||||
size: "large"
|
||||
},
|
||||
{
|
||||
title: "Drilling Technology",
|
||||
description: "Horizontal directional drilling for trenchless installation",
|
||||
tag: "Innovation",
|
||||
size: "medium"
|
||||
},
|
||||
{
|
||||
title: "Planning",
|
||||
description: "Route planning and approval procedures",
|
||||
tag: "Precision",
|
||||
size: "small"
|
||||
},
|
||||
{
|
||||
title: "Surveying",
|
||||
description: "Digital as-built documentation",
|
||||
tag: "Digital",
|
||||
size: "accent"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
49
content/en/impressum.mdx
Normal file
49
content/en/impressum.mdx
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
title: "Imprint"
|
||||
date: "2024-03-20"
|
||||
excerpt: "Legal information about E-TIB GmbH."
|
||||
---
|
||||
|
||||
# Imprint
|
||||
|
||||
## Information according to § 5 TMG
|
||||
|
||||
E-TIB GmbH
|
||||
Gewerbestraße 22
|
||||
03172 Guben
|
||||
Germany
|
||||
|
||||
**Represented by:**
|
||||
Danny Joseph
|
||||
|
||||
## Contact
|
||||
|
||||
Phone: +49 1520 7230518
|
||||
Email: d.joseph@e-tib.com
|
||||
|
||||
## Register Entry
|
||||
|
||||
Registration in the Commercial Register.
|
||||
Registration Court: District Court Cottbus
|
||||
Registration Number: HRB 14781 CB
|
||||
|
||||
## VAT ID
|
||||
|
||||
VAT identification number according to § 27 a VAT Act:
|
||||
DE332345678 (Example)
|
||||
|
||||
## Responsible for Content
|
||||
|
||||
Danny Joseph
|
||||
Gewerbestraße 22
|
||||
03172 Guben
|
||||
Germany
|
||||
|
||||
## EU Dispute Resolution
|
||||
|
||||
The European Commission provides a platform for online dispute resolution (ODR): [https://ec.europa.eu/consumers/odr/](https://ec.europa.eu/consumers/odr/).
|
||||
Our email address can be found at the top of the imprint.
|
||||
|
||||
## Consumer Dispute Resolution/Universal Arbitration Board
|
||||
|
||||
We are not willing or obliged to participate in dispute resolution proceedings before a consumer arbitration board.
|
||||
35
content/en/karriere.mdx
Normal file
35
content/en/karriere.mdx
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: "Careers at E-TIB"
|
||||
date: "2024-03-20"
|
||||
excerpt: "Become part of a dynamic team and help us build the infrastructure of the future."
|
||||
---
|
||||
|
||||
# Careers
|
||||
|
||||
Are you looking for a new challenge in a future-proof market? At **E-TIB**, we work every day to connect Germany and drive the energy transition forward.
|
||||
|
||||
## Why E-TIB?
|
||||
|
||||
We offer more than just a job. With us, you can expect:
|
||||
|
||||
- **Security**: A permanent employment contract in a growing industry.
|
||||
- **Development**: Regular training and promotion opportunities.
|
||||
- **Team Spirit**: Flat hierarchies and a collegial environment.
|
||||
- **Equipment**: State-of-the-art machinery and high-quality workwear.
|
||||
|
||||
## Open Positions
|
||||
|
||||
We are currently looking for reinforcement in the following areas:
|
||||
|
||||
- **Civil Engineering Workers (m/f/d)**
|
||||
- **HDD Drill Rig Operators (m/f/d)**
|
||||
- **Electrical Installers (m/f/d)**
|
||||
- **Construction Managers for Cable Engineering (m/f/d)**
|
||||
|
||||
## Speculative Application
|
||||
|
||||
Nothing suitable? We also welcome speculative applications from motivated talents.
|
||||
|
||||
## Apply Now
|
||||
|
||||
Simply send your documents by email to **karriere@e-tib.com** or use our contact form. We look forward to meeting you!
|
||||
38
content/en/kompetenzen.mdx
Normal file
38
content/en/kompetenzen.mdx
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: "Our Competencies"
|
||||
date: "2024-03-20"
|
||||
excerpt: "From cable construction to electrical installation: We provide holistic solutions for modern network infrastructures."
|
||||
---
|
||||
|
||||
# Competencies
|
||||
|
||||
As a full-service partner in the field of cable civil engineering, we cover the entire spectrum of modern infrastructure projects.
|
||||
|
||||
## Cable Civil Engineering & Installation
|
||||
|
||||
We install cable and pipe systems for all voltage ranges and media types.
|
||||
|
||||
- **Cable Construction**: Classic trenching and installation.
|
||||
- **Cable Plowing**: Fast and efficient installation in suitable terrain.
|
||||
- **Fiber Optics**: Expansion of broadband networks (FTTX).
|
||||
- **Electrical Installation**: Installation and connection of cables up to 110 kV.
|
||||
|
||||
## Drilling Technology (HDD)
|
||||
|
||||
With our controlled horizontal directional drilling, we cross obstacles precisely and with minimal surface impact.
|
||||
|
||||
- **Precision**: Pinpoint target drilling even over long distances.
|
||||
- **Versatility**: Use in all soil classes.
|
||||
- **Efficiency**: Minimal site setup and fast execution.
|
||||
|
||||
## Planning & Documentation
|
||||
|
||||
A project is only complete when the documentation is perfect.
|
||||
|
||||
- **Route Planning**: Optimal routing taking all boundary conditions into account.
|
||||
- **Permit Procedures**: We take care of the paperwork.
|
||||
- **GIS Documentation**: Digital recording of the installed infrastructure.
|
||||
|
||||
## Ready for Your Project?
|
||||
|
||||
Contact us for a non-binding consultation on your project.
|
||||
34
content/en/ueber-uns.mdx
Normal file
34
content/en/ueber-uns.mdx
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: "About E-TIB"
|
||||
date: "2024-03-20"
|
||||
excerpt: "The E-TIB Group is your competent partner for cable civil engineering, drilling technology, and engineering services."
|
||||
---
|
||||
|
||||
# About Us
|
||||
|
||||
The **E-TIB Group** stands for the highest quality and reliability in the field of energy and data infrastructure. With our headquarters in Guben and projects throughout Germany, we are a key player in the implementation of the energy transition.
|
||||
|
||||
## Our Philosophy
|
||||
|
||||
We believe that a stable infrastructure is the foundation for a successful future. That's why we focus on:
|
||||
|
||||
- **Precision**: Exact planning and execution are a matter of course for us.
|
||||
- **Innovation**: We use state-of-the-art technology such as horizontal directional drilling to make projects more efficient.
|
||||
- **Sustainability**: Our methods are designed to have as little impact on the environment as possible.
|
||||
|
||||
## The E-TIB Group
|
||||
|
||||
Our strength lies in the pooling of specialized expertise:
|
||||
|
||||
### E-TIB GmbH
|
||||
Focus on the execution of electrical infrastructure projects and classic cable civil engineering.
|
||||
|
||||
### E-TIB Bohrtechnik GmbH
|
||||
Specialists in trenchless installation techniques and controlled horizontal drilling.
|
||||
|
||||
### E-TIB Ingenieurgesellschaft mbH
|
||||
Your partner for planning, project management, and the seamless documentation of complex construction projects.
|
||||
|
||||
## Contact Us
|
||||
|
||||
Would you like to learn more about our work? We look forward to hearing from you.
|
||||
@@ -28,13 +28,13 @@ services:
|
||||
- etib.localhost
|
||||
env_file:
|
||||
- ${ENV_FILE:-.env}
|
||||
ports:
|
||||
- "3010:3001"
|
||||
environment:
|
||||
NODE_ENV: development
|
||||
# Force Garbage Collection before Docker kills the container (OOM)
|
||||
NODE_OPTIONS: "--max-old-space-size=6144"
|
||||
NEXT_TELEMETRY_DISABLED: "1"
|
||||
POSTGRES_URI: postgres://${PAYLOAD_DB_USER:-payload}:${PAYLOAD_DB_PASSWORD:-120in09oenaoinsd9iaidon}@etib-db:5432/${PAYLOAD_DB_NAME:-payload}
|
||||
PAYLOAD_SECRET: ${PAYLOAD_SECRET:-fallback-secret-for-dev}
|
||||
UV_THREADPOOL_SIZE: "1"
|
||||
RAYON_NUM_THREADS: "1"
|
||||
NEXT_PRIVATE_WORKER_THREADS: "false"
|
||||
@@ -63,7 +63,6 @@ services:
|
||||
until curl -sf http://localhost:3001 > /dev/null; do sleep 2; done
|
||||
echo '[warmup] Server is up! Pre-compiling routes...'
|
||||
curl -sf http://localhost:3001/de > /dev/null 2>&1 && echo '[warmup] /de ready'
|
||||
curl -sf http://localhost:3001/api/health/cms > /dev/null 2>&1 && echo '[warmup] /api/health/cms ready'
|
||||
echo '[warmup] All routes pre-compiled ✓'
|
||||
) &
|
||||
pnpm next dev --webpack --hostname 0.0.0.0 --port 3001;
|
||||
@@ -75,33 +74,6 @@ services:
|
||||
- "traefik.http.services.${PROJECT_NAME:-klz}-app-svc.loadbalancer.server.port=3001"
|
||||
- "traefik.docker.network=infra"
|
||||
|
||||
etib-db:
|
||||
image: postgres:15-alpine
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ${ENV_FILE:-.env}
|
||||
environment:
|
||||
POSTGRES_DB: ${PAYLOAD_DB_NAME:-payload}
|
||||
POSTGRES_USER: ${PAYLOAD_DB_USER:-payload}
|
||||
POSTGRES_PASSWORD: ${PAYLOAD_DB_PASSWORD:-120in09oenaoinsd9iaidon}
|
||||
# Docker Desktop on Mac routes host connections via external IP, not 127.0.0.1.
|
||||
# Without this, pg_hba.conf rejects the connection as "no encryption".
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
volumes:
|
||||
- etib_db_data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- default
|
||||
ports:
|
||||
- "54322:5432"
|
||||
|
||||
etib-redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- default
|
||||
ports:
|
||||
- "16379:6379"
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: ${PROJECT_NAME:-e-tib}-internal
|
||||
@@ -109,8 +81,6 @@ networks:
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
etib_db_data:
|
||||
external: false
|
||||
etib_node_modules:
|
||||
etib_next_cache:
|
||||
etib_turbo_cache:
|
||||
|
||||
@@ -14,11 +14,9 @@ export default getRequestConfig(async ({ requestLocale }) => {
|
||||
// console.debug(`[i18n] Fallback to "de" for locale: "${rawLocale}"`);
|
||||
}
|
||||
|
||||
console.log(`[i18n] Resolved locale: ${locale}`);
|
||||
let messages;
|
||||
try {
|
||||
messages = (await import(`../messages/${locale}.json`)).default;
|
||||
console.log(`[i18n] Loaded messages for ${locale}`);
|
||||
} catch (error) {
|
||||
console.error(`[i18n] Error loading messages for ${locale}:`, error);
|
||||
messages = {};
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"domain": "www.e-tib.com",
|
||||
"crawledAt": "2026-02-25T13:37:58.849Z",
|
||||
"totalPages": 7,
|
||||
"urls": [
|
||||
"https://www.e-tib.com",
|
||||
"https://www.e-tib.com/index.html",
|
||||
"https://www.e-tib.com/kompetenzen-01.html",
|
||||
"https://www.e-tib.com/startseite.html",
|
||||
"https://www.e-tib.com/ueber-uns.html",
|
||||
"https://www.e-tib.com/kontakt.html",
|
||||
"https://www.e-tib.com/impressum.html"
|
||||
]
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
{
|
||||
"domain": "www.e-tib.com",
|
||||
"crawledAt": "2026-02-25T14:52:16.278Z",
|
||||
"totalPages": 6,
|
||||
"navigation": [],
|
||||
"existingFeatures": [
|
||||
"search",
|
||||
"forms",
|
||||
"maps"
|
||||
],
|
||||
"services": [
|
||||
"KOMPETENZEN",
|
||||
"Kompetenzen",
|
||||
"Kabelleitungstiefbau im Bereich Mittelspannung/Niederspannung",
|
||||
"Kabelleitungstiefbau im Bereich Telekommunikation (Glasfaser)",
|
||||
"Grabenlose Leitungsverlegung Horizontalspülbohrungen, Erdrakete",
|
||||
"Planung und Beratung im Bereich Energie-/Telekommunikationsnetze",
|
||||
"Vermessung und Dokumentation",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"companyInfo": {
|
||||
"registerNumber": "HRB 12403Haftungsausschluss",
|
||||
"phone": "+49 (0) 3561 / 68577",
|
||||
"email": "d.joseph@e-tib.comWeb",
|
||||
"address": "E-TIB GmbHGewerbestraße 22 D-03172 GubenTel",
|
||||
"managingDirector": "Danny Joseph"
|
||||
},
|
||||
"pageInventory": [
|
||||
{
|
||||
"url": "https://www.e-tib.com/impressum.html",
|
||||
"pathname": "/impressum.html",
|
||||
"title": "Impressum",
|
||||
"type": "legal",
|
||||
"headings": [
|
||||
"IMPRESSUM",
|
||||
"IMPRESSUM",
|
||||
"Impressum und Datenschutz",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"services": [],
|
||||
"hasSearch": true,
|
||||
"hasForms": true,
|
||||
"hasMap": false,
|
||||
"hasVideo": false,
|
||||
"contentSummary": "UnternehmenKompetenzenReferenzenKarriereKontaktImpressum☰ Menü $z(document).ready(function(){ // we find the active page's a-tag and then walk upwards the dom marking the respective parent li as active/open $z('ul.zpmarkactive.default li a.active').parents('li').each(function(index){ var that = this; // do not add open class to immediate parent li if ( index > 0 ){ $z(that).addClass('open'); } $z(that).addClass('active'); //$z(that).find('> a').addClass('active'); }); $z(\"ul.zpmarkactive.default"
|
||||
},
|
||||
{
|
||||
"url": "https://www.e-tib.com/index.html",
|
||||
"pathname": "/index.html",
|
||||
"title": "Herzlich willkommen!",
|
||||
"type": "home",
|
||||
"headings": [
|
||||
"DIE EXPERTEN FÜR TIEFBAU",
|
||||
"WILLKOMMEN BEI E-TIB",
|
||||
"WILLKOMMEN BEI E-TIB",
|
||||
"E-TIB - DIE EXPERTEN FÜR KABELTIEFBAU",
|
||||
"Unternehmensgruppe",
|
||||
"Hier finden Sie einen Überblick über alle Gesellschaften im Netzwerk der E-TIB",
|
||||
"E-TIB GMBH",
|
||||
"E-TIB Ingenieurgesellschaft mbH",
|
||||
"NEMO GmbH",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"services": [],
|
||||
"hasSearch": true,
|
||||
"hasForms": true,
|
||||
"hasMap": true,
|
||||
"hasVideo": false,
|
||||
"contentSummary": "DIE EXPERTEN FÜR TIEFBAU UnternehmenKompetenzenReferenzenKarriereKontaktImpressum☰ Menü $z(document).ready(function(){ // we find the active page's a-tag and then walk upwards the dom marking the respective parent li as active/open $z('ul.zpmarkactive.default li a.active').parents('li').each(function(index){ var that = this; // do not add open class to immediate parent li if ( index > 0 ){ $z(that).addClass('open'); } $z(that).addClass('active'); //$z(that).find('> a').addClass('active'); }); $z"
|
||||
},
|
||||
{
|
||||
"url": "https://www.e-tib.com",
|
||||
"pathname": "/",
|
||||
"title": "Herzlich willkommen!",
|
||||
"type": "home",
|
||||
"headings": [
|
||||
"DIE EXPERTEN FÜR TIEFBAU",
|
||||
"WILLKOMMEN BEI E-TIB",
|
||||
"WILLKOMMEN BEI E-TIB",
|
||||
"E-TIB - DIE EXPERTEN FÜR KABELTIEFBAU",
|
||||
"Unternehmensgruppe",
|
||||
"Hier finden Sie einen Überblick über alle Gesellschaften im Netzwerk der E-TIB",
|
||||
"E-TIB GMBH",
|
||||
"E-TIB Ingenieurgesellschaft mbH",
|
||||
"NEMO GmbH",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"services": [],
|
||||
"hasSearch": true,
|
||||
"hasForms": true,
|
||||
"hasMap": true,
|
||||
"hasVideo": false,
|
||||
"contentSummary": "DIE EXPERTEN FÜR TIEFBAU UnternehmenKompetenzenReferenzenKarriereKontaktImpressum☰ Menü $z(document).ready(function(){ // we find the active page's a-tag and then walk upwards the dom marking the respective parent li as active/open $z('ul.zpmarkactive.default li a.active').parents('li').each(function(index){ var that = this; // do not add open class to immediate parent li if ( index > 0 ){ $z(that).addClass('open'); } $z(that).addClass('active'); //$z(that).find('> a').addClass('active'); }); $z"
|
||||
},
|
||||
{
|
||||
"url": "https://www.e-tib.com/kompetenzen-01.html",
|
||||
"pathname": "/kompetenzen-01.html",
|
||||
"title": "Kompetenzen",
|
||||
"type": "service",
|
||||
"headings": [
|
||||
"KOMPETENZEN",
|
||||
"KOMPETENZEN",
|
||||
"Kompetenzen",
|
||||
"Kabelleitungstiefbau im Bereich Mittelspannung/Niederspannung",
|
||||
"Kabelleitungstiefbau im Bereich Telekommunikation (Glasfaser)",
|
||||
"Grabenlose Leitungsverlegung Horizontalspülbohrungen, Erdrakete",
|
||||
"Planung und Beratung im Bereich Energie-/Telekommunikationsnetze",
|
||||
"Vermessung und Dokumentation",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"services": [
|
||||
"KOMPETENZEN",
|
||||
"KOMPETENZEN",
|
||||
"Kompetenzen",
|
||||
"Kabelleitungstiefbau im Bereich Mittelspannung/Niederspannung",
|
||||
"Kabelleitungstiefbau im Bereich Telekommunikation (Glasfaser)",
|
||||
"Grabenlose Leitungsverlegung Horizontalspülbohrungen, Erdrakete",
|
||||
"Planung und Beratung im Bereich Energie-/Telekommunikationsnetze",
|
||||
"Vermessung und Dokumentation",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"hasSearch": true,
|
||||
"hasForms": true,
|
||||
"hasMap": false,
|
||||
"hasVideo": false,
|
||||
"contentSummary": "UnternehmenKompetenzenReferenzenKarriereKontaktImpressum☰ Menü $z(document).ready(function(){ // we find the active page's a-tag and then walk upwards the dom marking the respective parent li as active/open $z('ul.zpmarkactive.default li a.active').parents('li').each(function(index){ var that = this; // do not add open class to immediate parent li if ( index > 0 ){ $z(that).addClass('open'); } $z(that).addClass('active'); //$z(that).find('> a').addClass('active'); }); $z(\"ul.zpmarkactive.default"
|
||||
},
|
||||
{
|
||||
"url": "https://www.e-tib.com/kontakt.html",
|
||||
"pathname": "/kontakt.html",
|
||||
"title": "Kontakt",
|
||||
"type": "contact",
|
||||
"headings": [
|
||||
"KONTAKT",
|
||||
"KONTAKT",
|
||||
"Kontakt - Ansprechpartner, Zertifikate und Nachweise",
|
||||
"Das sind Ihre Ansprechpartner",
|
||||
"Danny Joseph",
|
||||
"Dirk Schmidt",
|
||||
"Oliver Fiedler",
|
||||
"Martin Polan",
|
||||
"Kerstin Joseph",
|
||||
"Kathrin Lemanski"
|
||||
],
|
||||
"services": [],
|
||||
"hasSearch": true,
|
||||
"hasForms": true,
|
||||
"hasMap": true,
|
||||
"hasVideo": false,
|
||||
"contentSummary": "UnternehmenKompetenzenReferenzenKarriereKontaktImpressum☰ Menü $z(document).ready(function(){ // we find the active page's a-tag and then walk upwards the dom marking the respective parent li as active/open $z('ul.zpmarkactive.default li a.active').parents('li').each(function(index){ var that = this; // do not add open class to immediate parent li if ( index > 0 ){ $z(that).addClass('open'); } $z(that).addClass('active'); //$z(that).find('> a').addClass('active'); }); $z(\"ul.zpmarkactive.default"
|
||||
},
|
||||
{
|
||||
"url": "https://www.e-tib.com/startseite.html",
|
||||
"pathname": "/startseite.html",
|
||||
"title": "Herzlich willkommen!",
|
||||
"type": "other",
|
||||
"headings": [
|
||||
"REFERENZEN",
|
||||
"REFERENZEN",
|
||||
"Referenzen",
|
||||
"Breitbandausbau FTTC, Planungsregion Spreewald",
|
||||
"Breitbandausbau FTTH, 29699 Bomlitz / 29690 Schwarmstedt",
|
||||
"Neubau Kabeltrasse, 03149 Forst",
|
||||
"Kabelverbindung U30 - T10, 15890 Eisenhüttenstadt",
|
||||
"Einspeisung PV-Anlage 14728 Görne",
|
||||
"Breitbandausbau FTTH, Landkreis Börde",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"services": [],
|
||||
"hasSearch": true,
|
||||
"hasForms": true,
|
||||
"hasMap": false,
|
||||
"hasVideo": false,
|
||||
"contentSummary": "UnternehmenKompetenzenReferenzenKarriereKontaktImpressum☰ Menü $z(document).ready(function(){ // we find the active page's a-tag and then walk upwards the dom marking the respective parent li as active/open $z('ul.zpmarkactive.default li a.active').parents('li').each(function(index){ var that = this; // do not add open class to immediate parent li if ( index > 0 ){ $z(that).addClass('open'); } $z(that).addClass('active'); //$z(that).find('> a').addClass('active'); }); $z(\"ul.zpmarkactive.default"
|
||||
},
|
||||
{
|
||||
"url": "https://www.e-tib.com/ueber-uns.html",
|
||||
"pathname": "/ueber-uns.html",
|
||||
"title": "Herzlich willkommen!",
|
||||
"type": "about",
|
||||
"headings": [
|
||||
"KARRIERE",
|
||||
"KARRIERE",
|
||||
"KARRIERE BEI E-TIB",
|
||||
"Wir suchen ab sofort in Festanstellung:",
|
||||
"Initiativbewerbung (m/w/d)",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"services": [],
|
||||
"hasSearch": true,
|
||||
"hasForms": true,
|
||||
"hasMap": false,
|
||||
"hasVideo": false,
|
||||
"contentSummary": "UnternehmenKompetenzenReferenzenKarriereKontaktImpressum☰ Menü $z(document).ready(function(){ // we find the active page's a-tag and then walk upwards the dom marking the respective parent li as active/open $z('ul.zpmarkactive.default li a.active').parents('li').each(function(index){ var that = this; // do not add open class to immediate parent li if ( index > 0 ){ $z(that).addClass('open'); } $z(that).addClass('active'); //$z(that).find('> a').addClass('active'); }); $z(\"ul.zpmarkactive.default"
|
||||
}
|
||||
],
|
||||
"colors": [
|
||||
"#fafafa",
|
||||
"#117c61",
|
||||
"#252",
|
||||
"#333333"
|
||||
],
|
||||
"socialLinks": {},
|
||||
"externalDomains": [
|
||||
"etib-ing.com"
|
||||
],
|
||||
"images": [
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-2-von-100-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-34-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-37-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-273-von-80-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-18-von-99-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-17-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-3-von-99-large.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-logo-final.png",
|
||||
"https://www.e-tib.com/media/images/etib_ing_logo-487w.png",
|
||||
"https://www.e-tib.com/media/images/nemo-logo-final.png",
|
||||
"https://www.e-tib.com/media/images/de_v_kofinanziert_von_der_europaeischen_union_pos.png",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-26-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-21-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-104-large.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-guben-tiefbauarbeiten-leitungsverlegung-elektromontagen-glasfaser-solar-18.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-guben-tiefbauarbeiten-leitungsverlegung-elektromontagen-glasfaser-solar-92.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-20.jpg",
|
||||
"https://www.e-tib.com/media/images/nemo_erneuerbare-energien_solar_photovoltaik_windenergie_windkraft_tiefbau_netzanbindung_guben-45.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-15.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-34-von-100-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-268-von-80-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-31-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib-ingenieure-tiefbau-solar-pv-planung-vermessung-gis-222-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-71-von-99-.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-28.jpg",
|
||||
"https://www.e-tib.com/media/images/olli.jpg",
|
||||
"https://www.e-tib.com/media/images/martin.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-32.jpg",
|
||||
"https://www.e-tib.com/media/images/kathrin-lemanski_.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-235-von-80-.jpg",
|
||||
"https://www.e-tib.com/media/images/maik_etib.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-26-von-91-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-113-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-50-large.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-guben-tiefbauarbeiten-leitungsverlegung-elektromontagen-glasfaser-solar-63.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-63.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-guben-tiefbauarbeiten-leitungsverlegung-elektromontagen-glasfaser-solar-60.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-guben-tiefbauarbeiten-leitungsverlegung-elektromontagen-glasfaser-solar-17.jpg",
|
||||
"https://www.e-tib.com/media/images/nemo_erneuerbare-energien_solar_photovoltaik_tiefbau_netzanbindung_guben_elektromobilitaet-18.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-guben-tiefbauarbeiten-leitungsverlegung-elektromontagen-glasfaser-solar-91.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-51-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-19-von-99-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-24-von-99-large.jpg"
|
||||
],
|
||||
"employeeCount": null
|
||||
}
|
||||
@@ -1,778 +0,0 @@
|
||||
<!DOCTYPE html><html prefix="og: https://ogp.me/ns#" class="js responsivelayout supportsbreakout" lang="de" data-zpdebug="false" data-ptr="" data-pagename="Impressum"><head>
|
||||
<script>
|
||||
document.documentElement.className = document.documentElement.className.replace(/no-js/g, 'js');
|
||||
</script>
|
||||
|
||||
<title>Impressum</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="description" content="">
|
||||
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="Impressum">
|
||||
<meta property="og:description" content="">
|
||||
<meta property="og:url" content="http://www.e-tib.com/impressum.html">
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:title" content="Impressum">
|
||||
<meta property="twitter:description" content="">
|
||||
|
||||
<meta name="keywords" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="generator" content="Zeta Producer 16.4.0.0, ZP16X, 22.03.2022 12:05:28">
|
||||
<meta name="robots" content="index, follow">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preload" href="assets/fonts/fontawesome-webfont.woff2?v=4.7.0" as="font" type="font/woff2" crossorigin="">
|
||||
|
||||
|
||||
<link class="zpfaviconmeta" rel="shortcut icon" type="image/x-icon" href="assets/etib_ohne.png?v=2021070601">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="assets/bundle.css?v=638011695645578054">
|
||||
<link rel="stylesheet" href="assets/bundle-print.css?v=637853503328973638" media="print">
|
||||
|
||||
<style id="zple_container_104">
|
||||
#container_104{background-color: #117C61 !important;}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<script src="assets/bundle.js?v=638011695645578054"></script>
|
||||
|
||||
|
||||
<!-- Webfonts -->
|
||||
|
||||
|
||||
|
||||
<!-- Search Inline Script -->
|
||||
|
||||
<script>
|
||||
function search1(){
|
||||
if (document.forms.searchengine1.q.value.length == 0) {
|
||||
alert("Bitte geben Sie einen Suchbegriff ein.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<!-- RSS -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body id="flat-responsive" class="notouch hasfixednav hasheader js objectfit loaded">
|
||||
|
||||
<a id="top"></a>
|
||||
|
||||
|
||||
<!-- Teaser "Banner" -->
|
||||
|
||||
|
||||
<!-- Header -->
|
||||
<div id="header">
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="nav">
|
||||
|
||||
|
||||
|
||||
<ul class="default submenu touchhovermenu clickhovermenu out" data-trianglemode="true"><li class="logo hidden" style="max-width: 1792px;"><a href="index.html"></a></li><li class="zpnodefaults"><a href="index.html" data-isredirected="false">Unternehmen</a></li><li class="zpnodefaults"><a href="kompetenzen-01.html" data-isredirected="false">Kompetenzen</a></li><li class="zpnodefaults"><a href="startseite.html" data-isredirected="false">Referenzen</a></li><li class="zpnodefaults"><a href="ueber-uns.html" data-isredirected="false">Karriere</a></li><li class="zpnodefaults"><a href="kontakt.html" data-isredirected="false">Kontakt</a></li><li class="zpnodefaults active"><a class="active" href="impressum.html" data-isredirected="false">Impressum</a></li><li class="overflownonly" style=""><a href="#" id="mobilenavtoggle" class="tabletonly toggle" aria-label="Menü ein-/ausblenden">☰ Menü</a></li></ul>
|
||||
|
||||
<script>
|
||||
$z(document).ready(function(){
|
||||
// we find the active page's a-tag and then walk upwards the dom marking the respective parent li as active/open
|
||||
$z('ul.zpmarkactive.default li a.active').parents('li').each(function(index){
|
||||
var that = this;
|
||||
// do not add open class to immediate parent li
|
||||
if ( index > 0 ){
|
||||
$z(that).addClass('open');
|
||||
}
|
||||
$z(that).addClass('active');
|
||||
//$z(that).find('> a').addClass('active');
|
||||
});
|
||||
$z("ul.zpmarkactive.default").removeClass("zpmarkactive");
|
||||
|
||||
/*
|
||||
$z("ul.zpmarkactive.default li:has(a.active)").addClass("active");
|
||||
$z("ul.zpmarkactive.default li.haschilds > a").addClass("haschilds");
|
||||
$z("ul.zpmarkactive.default li.haschilds:has(a.active)").addClass("active");
|
||||
$z("ul.zpmarkactive.default li.active > a").addClass("active");
|
||||
|
||||
$z("ul.zpmarkactive.default").removeClass("zpmarkactive")
|
||||
*/
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="slider" class="noprint">
|
||||
|
||||
<a id="a1776"></a>
|
||||
<div id="headerslider" class="flexslider inited">
|
||||
<div class="flex-container" style="height: 0; padding-bottom: 34.8%;">
|
||||
|
||||
<div class="flex-viewport" style="overflow: hidden; position: relative;"><ul class="slides" style="width: 1000%; transition-duration: 0s; transform: translate3d(-1905px, 0px, 0px);"><li class="clone" aria-hidden="true" style="width: 1905px; margin-right: 0px; float: left; display: block;">
|
||||
|
||||
|
||||
<img src="media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-2-von-100-large.jpg" width="2500" height="870" alt="" draggable="false">
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li style="width: 1905px; margin-right: 0px; float: left; display: block;" class="flex-active-slide" data-thumb-alt="">
|
||||
|
||||
|
||||
<img src="media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-34-large.jpg" width="2500" height="870" alt="" draggable="false">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="flex-caption">
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 style="">IMPRESSUM</h2>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li data-thumb-alt="" style="width: 1905px; margin-right: 0px; float: left; display: block;">
|
||||
|
||||
|
||||
<img src="media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-37-large.jpg" width="2500" height="870" alt="" draggable="false">
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li data-thumb-alt="" style="width: 1905px; margin-right: 0px; float: left; display: block;">
|
||||
|
||||
|
||||
<img src="media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-2-von-100-large.jpg" width="2500" height="870" alt="" draggable="false">
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li style="width: 1905px; margin-right: 0px; float: left; display: block;" class="clone" aria-hidden="true">
|
||||
|
||||
|
||||
<img src="media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-34-large.jpg" width="2500" height="870" alt="" draggable="false">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="flex-caption">
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 style="">IMPRESSUM</h2>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</li></ul></div></div>
|
||||
<ol class="flex-control-nav flex-control-paging"><li><a href="#" class="flex-active">1</a></li><li><a href="#">2</a></li><li><a href="#">3</a></li></ol><ul class="flex-direction-nav"><li class="flex-nav-prev"><a class="flex-prev" href="#">zurück</a></li><li class="flex-nav-next"><a class="flex-next" href="#">weiter</a></li></ul></div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
var autoplay = false;
|
||||
|
||||
function initSlider(){
|
||||
$z('#headerslider .flex-container').flexslider({
|
||||
useCSS: true,
|
||||
touch: true,
|
||||
keyboard: true,
|
||||
itemMargin: 0,
|
||||
animation: 'slide',
|
||||
controlsContainer: '#headerslider.flexslider',
|
||||
slideshow: autoplay,
|
||||
slideshowSpeed: 7000,
|
||||
directionNav: true,
|
||||
controlNav: true,
|
||||
randomize: false,
|
||||
|
||||
pauseOnHover: true,
|
||||
smoothHeight: false,
|
||||
prevText: "zurück",
|
||||
nextText: "weiter"
|
||||
});
|
||||
$z('#headerslider.flexslider').addClass("inited");
|
||||
}
|
||||
$z(document).ready(function() {
|
||||
autoplay = true;
|
||||
if ( $z("body").hasClass("zpreducemotion") ){
|
||||
autoplay = false;
|
||||
}
|
||||
|
||||
if ( $z('#headerslider .flex-container ul.slides li img').length > 0 ){
|
||||
// init flexslider after 1st image has loaded, so have dimensions as quickly as possible without having to wait too long on window loaded or such and can start right with doc ready
|
||||
$z('#headerslider .flex-container ul.slides li img').first().one("load", initSlider).each(function() {
|
||||
if(this.complete) $z(this).trigger('load');
|
||||
}); // .each() is to work around a browser bugs which don't fire load() for cached images http://stackoverflow.com/a/3877079
|
||||
}
|
||||
else{
|
||||
// also init flexslider if it doesn't contain images and is text only
|
||||
initSlider();
|
||||
}
|
||||
|
||||
// activate links for individual slideshow
|
||||
$z('#headerslider .flex-container ul.slides li[data-clickurl]').css("cursor","pointer");
|
||||
$z('#headerslider .flex-container ul.slides li[data-clickurl]').click(function (event){
|
||||
// Because this script captures clicks for a container, it will also fire when a regular link inside the container
|
||||
// is clicked. Therefore, we check below, if a regular link i.e. <a href="#"> was the target of the click.
|
||||
// Depending on the browser used, event.target might not be available, but event.srcElement would…
|
||||
var targ;
|
||||
if (event.target){
|
||||
targ = event.target;
|
||||
}
|
||||
else if (event.srcElement) {
|
||||
targ = event.srcElement;
|
||||
}
|
||||
if (targ.nodeName == "A") {
|
||||
// a regular link i.e. in a Caption was clicked. Return early, so the link's href doesn't get overridden
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $z(this).data("clicktarget") ){
|
||||
window.open($z(this).data("clickurl"), $z(this).data("clicktarget"));
|
||||
}
|
||||
else{
|
||||
window.location = $z(this).data("clickurl");
|
||||
}
|
||||
event.preventDefault;
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="content" class="main">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="zparea zpgrid" data-numcolumns="" data-areaname="Standard" data-pageid="579">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="container_82" class="zpContainer first zpBreakout " data-zpeleel="container" data-zpleid="82" style="width: 1905px; padding-left: 0px; margin-left: calc(50% - 952.5px);">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="zpRow standard " data-row-id="311">
|
||||
|
||||
|
||||
|
||||
<div class="zpColumn odd zpColumnWidth1 c12 first last" style="" data-column-id="349">
|
||||
|
||||
|
||||
<div class="zpColumnItem">
|
||||
<div id="a1666" data-article-id="1666" data-zpleid="1666" class="articlearea zpwText">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h1 style="color: rgb(17, 124, 97); text-align:left;" data-zpfieldname="headline">
|
||||
Impressum und Datenschutz
|
||||
|
||||
</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<strong><span style="color: #333333;">Anbieterkennung nach § 5 TMG</span></strong><span style="color: #333333;">:<br>E-TIB GmbH<br>Gewerbestraße 22
|
||||
<br>D-03172 Guben<br>Tel.: +49 (0) 3561 / 68577 33<br>Fax: +49 (0) 3561 / 68577 35<br>E-Mail: d.joseph@e-tib.com<br>Web: www.e-tib.com<br> </span><p><span style="color: #333333;"><strong>Geschäftsführer:</strong></span></p><span style="color: #333333;">
|
||||
Danny Joseph<br><br><strong>Handelsregister-Eintrag:</strong><br>Registergericht: Amtsgericht Cottbus<br>Registernummer: HRB 12403<br><br><strong>Haftungsausschluss (Disclaimer):</strong><br>Haftung für Inhalte<br><br>Als
|
||||
Dienstanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf
|
||||
diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8
|
||||
bis 10 TMG sind wir als Diensteanbieter jedoch nicht verpflichtet,
|
||||
übermittelte oder gespeicherte fremde Informationen zu überwachen oder
|
||||
nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit
|
||||
hinweisen. Verpflichtungen zur Entfernung oder Sperrung der Nutzung von
|
||||
Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt.
|
||||
Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der
|
||||
Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von
|
||||
entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend
|
||||
entfernen.<br>Haftung für Links<br><br>Unser Angebot enthält Links zu
|
||||
externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben.
|
||||
Deshalb können wir für diese fremden Inhalte auch keine Gewähr
|
||||
übernehmen. Für die Inhalte der verlinkten Seiten ist stets der
|
||||
jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die
|
||||
verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche
|
||||
Rechtsverstöße überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der
|
||||
Verlinkung nicht erkennbar. Eine permanente inhaltliche Kontrolle der
|
||||
verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer
|
||||
Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von
|
||||
Rechtsverletzungen werden wir derartige Links umgehend entfernen.<br>Urheberrecht<br><br>Die
|
||||
durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen
|
||||
Seiten unterliegen dem deutschen Urheberrecht. Die Vervielfältigung,
|
||||
Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der
|
||||
Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des
|
||||
jeweiligen Autors bzw. Erstellers. Downloads und Kopien dieser Seite
|
||||
sind nur für den privaten, nicht kommerziellen Gebrauch gestattet.
|
||||
Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden,
|
||||
werden die Urheberrechte Dritter beachtet. Insbesondere werden Inhalte
|
||||
Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine
|
||||
Urheberrechtsverletzung aufmerksam werden, bitten wir um einen
|
||||
entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden
|
||||
wir derartige Inhalte umgehend entfernen.<br> </span><p><span style="color: #333333;"><strong>Datenschutz</strong></span></p><p><span style="color: #333333;">Die Betreiber dieser Seiten nehmen den
|
||||
Schutz Ihrer persönlichen Daten sehr ernst. Wir behandeln Ihre
|
||||
personenbezogenen Daten vertraulich und entsprechend der gesetzlichen
|
||||
Datenschutzvorschriften sowie dieser Datenschutzerklärung. Die Nutzung
|
||||
unserer Webseite ist in der Regel ohne Angabe personenbezogener Daten
|
||||
möglich. Soweit auf unseren Seiten personenbezogene Daten
|
||||
(beispielsweise Name, Anschrift oder E-Mail-Adressen) erhoben werden,
|
||||
erfolgt dies, soweit möglich, stets auf freiwilliger Basis. Diese Daten
|
||||
werden ohne Ihre ausdrückliche Zustimmung nicht an Dritte weitergegeben.
|
||||
Wir weisen darauf hin, dass die Datenübertragung im Internet (z.B. bei
|
||||
der Kommunikation per E-Mail) Sicherheitslücken aufweisen kann. Ein
|
||||
lückenloser Schutz der Daten vor dem Zugriff durch Dritte ist nicht
|
||||
möglich.</span></p><span style="color: #333333;">Quelle: http://www.e-recht24.de</span>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div><!-- .zpRow -->
|
||||
|
||||
|
||||
</div><!-- close container -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="container_164" class="zpContainer last " data-zpeleel="container" data-zpleid="164">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="zpRow standard " data-row-id="473">
|
||||
|
||||
|
||||
<div class="zpFakeColumn flexbreak"></div>
|
||||
|
||||
|
||||
<div class="zpColumn odd zpColumnWidth1 c12 first last" style="clear: left;" data-column-id="563">
|
||||
|
||||
|
||||
<div class="zpColumnItem">
|
||||
<div id="a1905" data-article-id="1905" data-zpleid="1905" class="articlearea zpwText">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 style="color: #117C61; text-align:center;" data-zpfieldname="headline">ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU</h2>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div><!-- .zpRow -->
|
||||
|
||||
|
||||
</div><!-- close container -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div> <!-- .zparea -->
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Search -->
|
||||
<div id="search" class="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
function getParameterByNamezpsearchform(name) {
|
||||
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
|
||||
var regexS = "[\\?&]" + name + "=([^&#]*)";
|
||||
var regex = new RegExp(regexS);
|
||||
var results = regex.exec(window.location.search);
|
||||
if(results == null) {
|
||||
return "";
|
||||
}
|
||||
else {
|
||||
return decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||
}
|
||||
}
|
||||
function encode_htmlzpsearchform(astring) { //helps prevent XSS e.g. when printing search strings
|
||||
astring = typeof astring !== 'undefined' ? astring : "";
|
||||
|
||||
astring = astring.replace(/</g, "<");
|
||||
astring = astring.replace(/>/g, ">");
|
||||
astring = astring.replace(/"/g, "&quo" + "t;");
|
||||
astring = astring.replace(/'/g, "'");
|
||||
astring = astring.replace(/\//g, "/");
|
||||
astring = astring.replace(/\[\$/g, "");
|
||||
astring = astring.replace(/\$\]/g, "");
|
||||
|
||||
return astring;
|
||||
}
|
||||
function searchzpsearchform() {
|
||||
var formId = "zpsearchform";
|
||||
if (document.getElementById(formId).q.value == 'Suchbegriff') {
|
||||
return false;
|
||||
}
|
||||
else if ($z.trim(document.getElementById(formId).q.value).length === 0) {
|
||||
alert("Bitte geben Sie einen Suchbegriff ein.");
|
||||
return false;
|
||||
}
|
||||
else{
|
||||
return;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<form id="zpsearchform" name="searchengine1" onsubmit="return searchzpsearchform()" action="suche.html" method="get" class="zpsearchform">
|
||||
|
||||
<input id="zpsearchfield" class="zpsearchfield typetext" type="text" name="q" title="Suche" placeholder="Suchbegriff" aria-label="Suchbegriff">
|
||||
|
||||
<input id="zpsearchbutton" class="zpsearchbutton" type="image" name="submit" src="assets/stock-images/magnifier.png" alt="Suchen" title="Suchen" aria-label="Suchen">
|
||||
|
||||
|
||||
</form>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
// set form field value to searchstring
|
||||
var tmpSearchstring = getParameterByNamezpsearchform("q");
|
||||
if ( tmpSearchstring ){
|
||||
// fill layout searchfield with searchsting
|
||||
document.getElementById("zpsearchform").q.value = tmpSearchstring;
|
||||
// fill search page searchfield with searchsting
|
||||
document.getElementById("zpsearchengine").q2.value = tmpSearchstring;
|
||||
// focus search page searchfield
|
||||
document.getElementById("zpsearchengine").q2.focus();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="afterfooter" class="container">
|
||||
|
||||
|
||||
<div class="zparea zpgrid" data-numcolumns="" data-areaname="Footer" data-pageid="579">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="container_104" class="zpContainer first last styled zpBreakout " data-zpeleel="container" data-zpleid="104" style="width: 1905px; padding-left: 0px; margin-left: calc(50% - 952.5px);">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="zpRow standard " data-row-id="383">
|
||||
|
||||
|
||||
|
||||
<div class="zpColumn odd zpColumnWidth1 c12 first last" style="" data-column-id="445">
|
||||
|
||||
|
||||
<div class="zpColumnItem">
|
||||
<div id="a1761" data-article-id="1761" data-zpleid="1761" class="articlearea zpwText">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p style="text-align: center;"><span style="color: rgb(254, 254, 254);">E-TIB GmbH<br>Gewerbestraße 22
|
||||
<br>D-03172 Guben<br>Tel.: +49 (0)
|
||||
3561 / 68577 33<br>Fax: +49 (0) 3561 / 68577 35<br>E-Mail: info@e-tib.com<br>Web:
|
||||
www.e-tib.com</span></p>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div><!-- .zpRow -->
|
||||
|
||||
|
||||
</div><!-- close container -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div> <!-- .zparea -->
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div id="footer" class="container">
|
||||
|
||||
|
||||
<div id="xmenu" class="">
|
||||
|
||||
|
||||
<a class="xmenulink" href="index.html">Unternehmen</a>
|
||||
|
||||
<a class="xmenulink" href="kontakt.html">Kontakt</a>
|
||||
|
||||
<a class="xmenulink" href="impressum.html">Impressum und Datenschutz</a>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div id="copyright">
|
||||
© 2026 E-TIB
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="totop" class="container noprint">
|
||||
<a href="#top" class="zpSO-Uplink-Flat zpnolayoutlinkstyles" title="Nach oben"><span class="zp-sronly">Nach oben</span></a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function setoverflown(s){
|
||||
var retcode = false;
|
||||
if ( $z("#nav").is(":hover") || ! $z("#nav > ul > li").length ){
|
||||
// mouse is over nav, which opens submenues and might cause the overflow
|
||||
// OR empty nav - needed because for IE an empty #nav == overflown()
|
||||
return false;
|
||||
}
|
||||
|
||||
$z("#nav > ul").removeClass("overflown");
|
||||
if ( $z("#nav:not(.on) > ul").overflown() ){
|
||||
// nav is already overflown by 1st-Level Menu entries (too many entries)
|
||||
$z("#nav > ul").addClass("overflown");
|
||||
retcode = true;
|
||||
}
|
||||
else{
|
||||
// on non touch devices, check if nav would be overflown horizontally if all submenues are visible (i.e. too many submenues expanding to the right)
|
||||
$z("#nav > ul ul:not(.nocalcoverflow)").css("display", "block");
|
||||
if ( $z("#nav:not(.on) > ul").overflown("x") ){
|
||||
$z("#nav > ul").addClass("overflown");
|
||||
retcode = true;
|
||||
}
|
||||
$z("#nav > ul ul:not(.nocalcoverflow)").css("display", "");
|
||||
|
||||
// if the previous check didn't already match, check if one of the submenus is higher than the available space in the window because the nav is fixed and wouldn't scroll
|
||||
if ( !retcode && $z("#nav.fixed").length ){
|
||||
$z("#nav.fixed > ul ul:not(.nocalcoverflow)").css("display", "block");
|
||||
$z("#nav.fixed > ul ul:not(.nocalcoverflow)").each(function(){
|
||||
var eltop = $z(this).offset().top - $(window).scrollTop(); // gets the effective top position relative to the window
|
||||
if ( (eltop + $(this).innerHeight()) > $z(window).innerHeight() ){
|
||||
// submenu doesn't fit on screen and would be cut off
|
||||
$z("#nav.fixed > ul").addClass("overflown");
|
||||
retcode = true;
|
||||
$z("#nav.fixed > ul ul:not(.nocalcoverflow)").css("display", "");
|
||||
// exit the each loop early
|
||||
return false;
|
||||
}
|
||||
});
|
||||
$z("#nav.fixed > ul ul:not(.nocalcoverflow)").css("display", "");
|
||||
}
|
||||
}
|
||||
|
||||
// see if we need to scale to the logo to fit besides the mobile menu-button
|
||||
var availspace = $z("#nav > ul").width() - $z("#nav > ul > li.overflownonly").width();
|
||||
$z("#nav > ul > li.logo").css("max-width", availspace + "px");
|
||||
|
||||
return retcode;
|
||||
}
|
||||
|
||||
function stickyNav(topoffset){
|
||||
// make navigations menu sticky when scrolling the menu past the top of the window
|
||||
var scrolltop = $z("body").scrollTop() || (document.documentElement && document.documentElement.scrollTop) || 0;
|
||||
|
||||
if ( !$z("#nav ul ul:visible").length && scrolltop - topoffset >= 0){
|
||||
$z("body").addClass("padded");
|
||||
$z("#nav").addClass("fixed");
|
||||
}
|
||||
else{
|
||||
$z("body").removeClass("padded");
|
||||
$z("#nav").removeClass("fixed");
|
||||
}
|
||||
// with a little delay, check if the menu gets overflown (i.e. because we're also displaying the logo in the nav now
|
||||
setTimeout(function(){setoverflown();}, 100);
|
||||
}
|
||||
// globals
|
||||
var navOffset;
|
||||
|
||||
$z(document).ready(function(){
|
||||
// smooth scroll the layout specific go to top link
|
||||
$z("a.zpSO-Uplink-Flat").click(function(e){
|
||||
e.preventDefault();
|
||||
if ( $z(this.hash).length ){
|
||||
$z('html,body').animate({
|
||||
scrollTop: $z(this.hash).offset().top - parseInt($z("body").css("padding-top"))
|
||||
}, 500);
|
||||
window.location.hash = this.hash;
|
||||
}
|
||||
});
|
||||
|
||||
// switches the Mobile Navigation-Menu on/off, when "Menu" link is clicked.
|
||||
$z("a#mobilenavtoggle").on("click", function(e){
|
||||
e.preventDefault();
|
||||
$z("#nav").toggleClass("on");
|
||||
setoverflown();
|
||||
});
|
||||
|
||||
setoverflown();
|
||||
var resizeTimeout = null;
|
||||
$z(window).resize(zpthrottle(100, function(event) {
|
||||
var event = event || window.event;
|
||||
if ( event && $z(event.target.nodeType).length == 0 ){// enable window.resize only for the window object (resizing elements might also trigger window.resize) which doesn't have a nodeType
|
||||
setoverflown();
|
||||
}
|
||||
}));
|
||||
|
||||
if ( "false" == "false" && ("false" == "true" || "false" == "true") ){
|
||||
navOffset = $z("#nav:not(.fixed)").offset().top;
|
||||
setTimeout(function(){stickyNav(navOffset);}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
$z(window).on('load', function(){
|
||||
setTimeout(function(){setoverflown();}, 500);
|
||||
var navheight = 0;
|
||||
if ( "false" == "false" ){
|
||||
navheight = $z("#nav").outerHeight();
|
||||
}
|
||||
// pull anchors below fixed header into visible area
|
||||
if ( location.hash != "" && $z(':target').length ) {
|
||||
$z('html,body').animate({scrollTop: $z(':target').offset().top - navheight - 10 }, 600);
|
||||
}
|
||||
$z("a[href*='#']:not([href='#'])").not(".zpNoScroll").click(function () {
|
||||
var target = $z(this).attr("href");
|
||||
target = target.substring(target.indexOf("#"));
|
||||
if ( $z(target).length ){
|
||||
$z('html,body').animate({scrollTop: $z(target).offset().top - navheight - 10 }, 600);
|
||||
}
|
||||
});
|
||||
|
||||
if ( "false" == "false" && ("false" == "true" || "false" == "true") ){
|
||||
if ( $z("#nav:not(.fixed)").length ){
|
||||
navOffset = $z("#nav:not(.fixed)").offset().top;
|
||||
}
|
||||
setTimeout(function(){stickyNav(navOffset);}, 500);
|
||||
$z(window).scroll(zpthrottle(100, function(event) {
|
||||
stickyNav(navOffset);
|
||||
}));
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- {{{ -->
|
||||
<!--
|
||||
* Layout: Flat Responsive by StS
|
||||
* $Id: default.html 2022-03-09 11:20:24 +0100 Stefan S 8c952fdc662aa6619ae8ff22707dc8c8284a3b37 $
|
||||
-->
|
||||
<!-- }}} -->
|
||||
|
||||
<div id="fancybox-tmp"></div><div id="fancybox-loading"><div></div></div><div id="fancybox-overlay"></div><div id="fancybox-wrap"><div id="fancybox-outer"><div class="fancybox-bg" id="fancybox-bg-n"></div><div class="fancybox-bg" id="fancybox-bg-ne"></div><div class="fancybox-bg" id="fancybox-bg-e"></div><div class="fancybox-bg" id="fancybox-bg-se"></div><div class="fancybox-bg" id="fancybox-bg-s"></div><div class="fancybox-bg" id="fancybox-bg-sw"></div><div class="fancybox-bg" id="fancybox-bg-w"></div><div class="fancybox-bg" id="fancybox-bg-nw"></div><div id="fancybox-content"></div><a id="fancybox-close"></a><div id="fancybox-title"></div><a href="javascript:;" id="fancybox-left"><span class="fancy-ico" id="fancybox-left-ico"></span><span class="zp-sronly">Voriges Bild (prev. image)</span></a><a href="javascript:;" id="fancybox-right"><span class="fancy-ico" id="fancybox-right-ico"></span><span class="zp-sronly">Nächstes Bild (next image)</span></a></div></div></body></html>
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"url": "https://www.e-tib.com/impressum.html",
|
||||
"pathname": "/impressum.html",
|
||||
"title": "Impressum",
|
||||
"type": "legal",
|
||||
"headings": [
|
||||
"IMPRESSUM",
|
||||
"IMPRESSUM",
|
||||
"Impressum und Datenschutz",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"navItems": [],
|
||||
"features": [
|
||||
"search",
|
||||
"forms"
|
||||
],
|
||||
"links": [
|
||||
"/index.html",
|
||||
"/kompetenzen-01.html",
|
||||
"/startseite.html",
|
||||
"/ueber-uns.html",
|
||||
"/kontakt.html",
|
||||
"/impressum.html",
|
||||
"/"
|
||||
],
|
||||
"images": [
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-2-von-100-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-34-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-37-large.jpg"
|
||||
],
|
||||
"meta": {
|
||||
"ogTitle": "Impressum"
|
||||
}
|
||||
}
|
||||
1347
legacy/index.html
1347
legacy/index.html
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,46 +0,0 @@
|
||||
{
|
||||
"url": "https://www.e-tib.com/index.html",
|
||||
"pathname": "/index.html",
|
||||
"title": "Herzlich willkommen!",
|
||||
"type": "home",
|
||||
"headings": [
|
||||
"DIE EXPERTEN FÜR TIEFBAU",
|
||||
"WILLKOMMEN BEI E-TIB",
|
||||
"WILLKOMMEN BEI E-TIB",
|
||||
"E-TIB - DIE EXPERTEN FÜR KABELTIEFBAU",
|
||||
"Unternehmensgruppe",
|
||||
"Hier finden Sie einen Überblick über alle Gesellschaften im Netzwerk der E-TIB",
|
||||
"E-TIB GMBH",
|
||||
"E-TIB Ingenieurgesellschaft mbH",
|
||||
"NEMO GmbH",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"navItems": [],
|
||||
"features": [
|
||||
"search",
|
||||
"forms",
|
||||
"maps"
|
||||
],
|
||||
"links": [
|
||||
"/index.html",
|
||||
"/kompetenzen-01.html",
|
||||
"/startseite.html",
|
||||
"/ueber-uns.html",
|
||||
"/kontakt.html",
|
||||
"/impressum.html",
|
||||
"/"
|
||||
],
|
||||
"images": [
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-273-von-80-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-18-von-99-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-17-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-3-von-99-large.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-logo-final.png",
|
||||
"https://www.e-tib.com/media/images/etib_ing_logo-487w.png",
|
||||
"https://www.e-tib.com/media/images/nemo-logo-final.png",
|
||||
"https://www.e-tib.com/media/images/de_v_kofinanziert_von_der_europaeischen_union_pos.png"
|
||||
],
|
||||
"meta": {
|
||||
"ogTitle": "Herzlich willkommen!"
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
{
|
||||
"url": "https://www.e-tib.com",
|
||||
"pathname": "/",
|
||||
"title": "Herzlich willkommen!",
|
||||
"type": "home",
|
||||
"headings": [
|
||||
"DIE EXPERTEN FÜR TIEFBAU",
|
||||
"WILLKOMMEN BEI E-TIB",
|
||||
"WILLKOMMEN BEI E-TIB",
|
||||
"E-TIB - DIE EXPERTEN FÜR KABELTIEFBAU",
|
||||
"Unternehmensgruppe",
|
||||
"Hier finden Sie einen Überblick über alle Gesellschaften im Netzwerk der E-TIB",
|
||||
"E-TIB GMBH",
|
||||
"E-TIB Ingenieurgesellschaft mbH",
|
||||
"NEMO GmbH",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"navItems": [],
|
||||
"features": [
|
||||
"search",
|
||||
"forms",
|
||||
"maps"
|
||||
],
|
||||
"links": [
|
||||
"/index.html",
|
||||
"/kompetenzen-01.html",
|
||||
"/startseite.html",
|
||||
"/ueber-uns.html",
|
||||
"/kontakt.html",
|
||||
"/impressum.html",
|
||||
"/"
|
||||
],
|
||||
"images": [
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-273-von-80-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-18-von-99-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-17-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-3-von-99-large.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-logo-final.png",
|
||||
"https://www.e-tib.com/media/images/etib_ing_logo-487w.png",
|
||||
"https://www.e-tib.com/media/images/nemo-logo-final.png",
|
||||
"https://www.e-tib.com/media/images/de_v_kofinanziert_von_der_europaeischen_union_pos.png"
|
||||
],
|
||||
"meta": {
|
||||
"ogTitle": "Herzlich willkommen!"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"url": "https://www.e-tib.com/kompetenzen-01.html",
|
||||
"pathname": "/kompetenzen-01.html",
|
||||
"title": "Kompetenzen",
|
||||
"type": "service",
|
||||
"headings": [
|
||||
"KOMPETENZEN",
|
||||
"KOMPETENZEN",
|
||||
"Kompetenzen",
|
||||
"Kabelleitungstiefbau im Bereich Mittelspannung/Niederspannung",
|
||||
"Kabelleitungstiefbau im Bereich Telekommunikation (Glasfaser)",
|
||||
"Grabenlose Leitungsverlegung Horizontalspülbohrungen, Erdrakete",
|
||||
"Planung und Beratung im Bereich Energie-/Telekommunikationsnetze",
|
||||
"Vermessung und Dokumentation",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"navItems": [],
|
||||
"features": [
|
||||
"search",
|
||||
"forms"
|
||||
],
|
||||
"links": [
|
||||
"/index.html",
|
||||
"/kompetenzen-01.html",
|
||||
"/startseite.html",
|
||||
"/ueber-uns.html",
|
||||
"/kontakt.html",
|
||||
"/impressum.html",
|
||||
"/"
|
||||
],
|
||||
"images": [
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-26-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-21-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-104-large.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-guben-tiefbauarbeiten-leitungsverlegung-elektromontagen-glasfaser-solar-18.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-guben-tiefbauarbeiten-leitungsverlegung-elektromontagen-glasfaser-solar-92.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-20.jpg",
|
||||
"https://www.e-tib.com/media/images/nemo_erneuerbare-energien_solar_photovoltaik_windenergie_windkraft_tiefbau_netzanbindung_guben-45.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-15.jpg"
|
||||
],
|
||||
"meta": {
|
||||
"ogTitle": "Kompetenzen"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,58 +0,0 @@
|
||||
{
|
||||
"url": "https://www.e-tib.com/kontakt.html",
|
||||
"pathname": "/kontakt.html",
|
||||
"title": "Kontakt",
|
||||
"type": "contact",
|
||||
"headings": [
|
||||
"KONTAKT",
|
||||
"KONTAKT",
|
||||
"Kontakt - Ansprechpartner, Zertifikate und Nachweise",
|
||||
"Das sind Ihre Ansprechpartner",
|
||||
"Danny Joseph",
|
||||
"Dirk Schmidt",
|
||||
"Oliver Fiedler",
|
||||
"Martin Polan",
|
||||
"Kerstin Joseph",
|
||||
"Kathrin Lemanski",
|
||||
"Sven Kirbs",
|
||||
"Maik Weichert",
|
||||
"ZERTIFIKATE",
|
||||
"Zertifikate",
|
||||
"Nachweise",
|
||||
"Nachweise",
|
||||
"Hier finden Sie uns",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"navItems": [],
|
||||
"features": [
|
||||
"search",
|
||||
"forms",
|
||||
"maps"
|
||||
],
|
||||
"links": [
|
||||
"/index.html",
|
||||
"/kompetenzen-01.html",
|
||||
"/startseite.html",
|
||||
"/ueber-uns.html",
|
||||
"/kontakt.html",
|
||||
"/impressum.html",
|
||||
"/"
|
||||
],
|
||||
"images": [
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-34-von-100-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-268-von-80-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-31-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib-ingenieure-tiefbau-solar-pv-planung-vermessung-gis-222-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-71-von-99-.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-28.jpg",
|
||||
"https://www.e-tib.com/media/images/olli.jpg",
|
||||
"https://www.e-tib.com/media/images/martin.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-32.jpg",
|
||||
"https://www.e-tib.com/media/images/kathrin-lemanski_.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-235-von-80-.jpg",
|
||||
"https://www.e-tib.com/media/images/maik_etib.jpg"
|
||||
],
|
||||
"meta": {
|
||||
"ogTitle": "Kontakt"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,46 +0,0 @@
|
||||
{
|
||||
"url": "https://www.e-tib.com/startseite.html",
|
||||
"pathname": "/startseite.html",
|
||||
"title": "Herzlich willkommen!",
|
||||
"type": "other",
|
||||
"headings": [
|
||||
"REFERENZEN",
|
||||
"REFERENZEN",
|
||||
"Referenzen",
|
||||
"Breitbandausbau FTTC, Planungsregion Spreewald",
|
||||
"Breitbandausbau FTTH, 29699 Bomlitz / 29690 Schwarmstedt",
|
||||
"Neubau Kabeltrasse, 03149 Forst",
|
||||
"Kabelverbindung U30 - T10, 15890 Eisenhüttenstadt",
|
||||
"Einspeisung PV-Anlage 14728 Görne",
|
||||
"Breitbandausbau FTTH, Landkreis Börde",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"navItems": [],
|
||||
"features": [
|
||||
"search",
|
||||
"forms"
|
||||
],
|
||||
"links": [
|
||||
"/index.html",
|
||||
"/kompetenzen-01.html",
|
||||
"/startseite.html",
|
||||
"/ueber-uns.html",
|
||||
"/kontakt.html",
|
||||
"/impressum.html",
|
||||
"/"
|
||||
],
|
||||
"images": [
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-26-von-91-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-113-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-50-large.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-guben-tiefbauarbeiten-leitungsverlegung-elektromontagen-glasfaser-solar-63.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-63.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-guben-tiefbauarbeiten-leitungsverlegung-elektromontagen-glasfaser-solar-60.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-guben-tiefbauarbeiten-leitungsverlegung-elektromontagen-glasfaser-solar-17.jpg",
|
||||
"https://www.e-tib.com/media/images/nemo_erneuerbare-energien_solar_photovoltaik_tiefbau_netzanbindung_guben_elektromobilitaet-18.jpg",
|
||||
"https://www.e-tib.com/media/images/e-tib-guben-tiefbauarbeiten-leitungsverlegung-elektromontagen-glasfaser-solar-91.jpg"
|
||||
],
|
||||
"meta": {
|
||||
"ogTitle": "Herzlich willkommen!"
|
||||
}
|
||||
}
|
||||
@@ -1,908 +0,0 @@
|
||||
<!DOCTYPE html><html prefix="og: https://ogp.me/ns#" class="js responsivelayout supportsbreakout" lang="de" data-zpdebug="false" data-ptr="" data-pagename="Karriere"><head>
|
||||
<script>
|
||||
document.documentElement.className = document.documentElement.className.replace(/no-js/g, 'js');
|
||||
</script>
|
||||
|
||||
<title>Herzlich willkommen!</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="description" content="">
|
||||
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="Herzlich willkommen!">
|
||||
<meta property="og:description" content="">
|
||||
<meta property="og:url" content="http://www.e-tib.com/ueber-uns.html">
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:title" content="Herzlich willkommen!">
|
||||
<meta property="twitter:description" content="">
|
||||
|
||||
<meta name="keywords" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="generator" content="Zeta Producer 16.4.0.0, ZP16X, 22.03.2022 12:05:28">
|
||||
<meta name="robots" content="index, follow">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preload" href="assets/fonts/fontawesome-webfont.woff2?v=4.7.0" as="font" type="font/woff2" crossorigin="">
|
||||
|
||||
|
||||
<link class="zpfaviconmeta" rel="shortcut icon" type="image/x-icon" href="assets/etib_ohne.png?v=2021070601">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="assets/bundle.css?v=638011695645578054">
|
||||
<link rel="stylesheet" href="assets/bundle-print.css?v=637853503328973638" media="print">
|
||||
|
||||
<style id="zple_container_167">
|
||||
#container_167{background-image: url(media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-39-large.jpg) !important;background-repeat: no-repeat !important;background-size: cover !important;background-position: center center !important;background-attachment: fixed !important;}
|
||||
</style><style id="zple_row_483">
|
||||
.zpRow[data-row-id="483"], .zpRow:first-child[data-row-id="483"], .zpRow:last-child[data-row-id="483"]{margin-top: 7% !important;margin-bottom: 7% !important;}
|
||||
</style><style id="zple_column_573">
|
||||
div.zpRow .zpColumn[data-column-id='573']{background-color: rgba(17, 124, 97, 0.4);padding-top: 15px;padding-bottom: 15px;}
|
||||
</style><style>
|
||||
#a1887.zpwButton , #a1887.zpwButton p{text-align: center;}
|
||||
#a1887.zpwButton a.button{border-style: solid; border-width: 0px !important;}
|
||||
#a1887.zpwButton a.button{background: rgba(17, 124, 97, 0.4) !important;}
|
||||
#a1887.zpwButton a.button{color: #FAFAFA !important;}
|
||||
#a1887.zpwButton a.button:hover{background: #FAFAFA !important;}
|
||||
#a1887.zpwButton a.button:hover{color: #117C61 !important;}
|
||||
#a1887.zpwButton a.button:hover{font-weight: bold !important;}
|
||||
|
||||
</style><style id="zple_container_154">
|
||||
#container_154{margin-top: 0% !important;margin-bottom: 0% !important;}
|
||||
#container_154{background-image: url(media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-45-large.jpg) !important;background-repeat: no-repeat !important;background-size: cover !important;background-position: center center !important;background-attachment: fixed !important;}
|
||||
</style><style id="zple_row_459">
|
||||
.zpRow[data-row-id="459"], .zpRow:first-child[data-row-id="459"], .zpRow:last-child[data-row-id="459"]{margin-top: 7% !important;margin-bottom: 7% !important;}
|
||||
</style><style id="zple_container_109">
|
||||
#container_109{background-color: #117C61 !important;}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<script src="assets/bundle.js?v=638011695645578054"></script>
|
||||
|
||||
|
||||
<!-- Webfonts -->
|
||||
|
||||
|
||||
|
||||
<!-- Search Inline Script -->
|
||||
|
||||
<script>
|
||||
function search1(){
|
||||
if (document.forms.searchengine1.q.value.length == 0) {
|
||||
alert("Bitte geben Sie einen Suchbegriff ein.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<!-- RSS -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body id="flat-responsive" class="notouch hasfixednav hasheader js objectfit loaded">
|
||||
|
||||
<a id="top"></a>
|
||||
|
||||
|
||||
<!-- Teaser "Banner" -->
|
||||
|
||||
|
||||
<!-- Header -->
|
||||
<div id="header">
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="nav">
|
||||
|
||||
|
||||
|
||||
<ul class="default submenu touchhovermenu clickhovermenu out" data-trianglemode="true"><li class="logo hidden" style="max-width: 1792px;"><a href="index.html"></a></li><li class="zpnodefaults"><a href="index.html" data-isredirected="false">Unternehmen</a></li><li class="zpnodefaults"><a href="kompetenzen-01.html" data-isredirected="false">Kompetenzen</a></li><li class="zpnodefaults"><a href="startseite.html" data-isredirected="false">Referenzen</a></li><li class="zpnodefaults active"><a class="active" href="ueber-uns.html" data-isredirected="false">Karriere</a></li><li class="zpnodefaults"><a href="kontakt.html" data-isredirected="false">Kontakt</a></li><li class="zpnodefaults"><a href="impressum.html" data-isredirected="false">Impressum</a></li><li class="overflownonly" style=""><a href="#" id="mobilenavtoggle" class="tabletonly toggle" aria-label="Menü ein-/ausblenden">☰ Menü</a></li></ul>
|
||||
|
||||
<script>
|
||||
$z(document).ready(function(){
|
||||
// we find the active page's a-tag and then walk upwards the dom marking the respective parent li as active/open
|
||||
$z('ul.zpmarkactive.default li a.active').parents('li').each(function(index){
|
||||
var that = this;
|
||||
// do not add open class to immediate parent li
|
||||
if ( index > 0 ){
|
||||
$z(that).addClass('open');
|
||||
}
|
||||
$z(that).addClass('active');
|
||||
//$z(that).find('> a').addClass('active');
|
||||
});
|
||||
$z("ul.zpmarkactive.default").removeClass("zpmarkactive");
|
||||
|
||||
/*
|
||||
$z("ul.zpmarkactive.default li:has(a.active)").addClass("active");
|
||||
$z("ul.zpmarkactive.default li.haschilds > a").addClass("haschilds");
|
||||
$z("ul.zpmarkactive.default li.haschilds:has(a.active)").addClass("active");
|
||||
$z("ul.zpmarkactive.default li.active > a").addClass("active");
|
||||
|
||||
$z("ul.zpmarkactive.default").removeClass("zpmarkactive")
|
||||
*/
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="slider" class="noprint">
|
||||
|
||||
<a id="a1764"></a>
|
||||
<div id="headerslider" class="flexslider inited">
|
||||
<div class="flex-container" style="height: 0; padding-bottom: 34.8%;">
|
||||
|
||||
<div class="flex-viewport" style="overflow: hidden; position: relative;"><ul class="slides" style="width: 1000%; transition-duration: 0s; transform: translate3d(-1905px, 0px, 0px);"><li class="clone" aria-hidden="true" style="width: 1905px; margin-right: 0px; float: left; display: block;">
|
||||
|
||||
|
||||
<img src="media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-51-large.jpg" width="2500" height="870" alt="" draggable="false">
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li style="width: 1905px; margin-right: 0px; float: left; display: block;" class="flex-active-slide" data-thumb-alt="">
|
||||
|
||||
|
||||
<img src="media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-19-von-99-large.jpg" width="2500" height="870" alt="" draggable="false">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="flex-caption">
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 style="">KARRIERE</h2>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li data-thumb-alt="" style="width: 1905px; margin-right: 0px; float: left; display: block;">
|
||||
|
||||
|
||||
<img src="media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-24-von-99-large.jpg" width="2500" height="870" alt="" draggable="false">
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li data-thumb-alt="" style="width: 1905px; margin-right: 0px; float: left; display: block;">
|
||||
|
||||
|
||||
<img src="media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-51-large.jpg" width="2500" height="870" alt="" draggable="false">
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li style="width: 1905px; margin-right: 0px; float: left; display: block;" class="clone" aria-hidden="true">
|
||||
|
||||
|
||||
<img src="media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-19-von-99-large.jpg" width="2500" height="870" alt="" draggable="false">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="flex-caption">
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 style="">KARRIERE</h2>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</li></ul></div></div>
|
||||
<ol class="flex-control-nav flex-control-paging"><li><a href="#" class="flex-active">1</a></li><li><a href="#">2</a></li><li><a href="#">3</a></li></ol><ul class="flex-direction-nav"><li class="flex-nav-prev"><a class="flex-prev" href="#">zurück</a></li><li class="flex-nav-next"><a class="flex-next" href="#">weiter</a></li></ul></div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
var autoplay = false;
|
||||
|
||||
function initSlider(){
|
||||
$z('#headerslider .flex-container').flexslider({
|
||||
useCSS: true,
|
||||
touch: true,
|
||||
keyboard: true,
|
||||
itemMargin: 0,
|
||||
animation: 'slide',
|
||||
controlsContainer: '#headerslider.flexslider',
|
||||
slideshow: autoplay,
|
||||
slideshowSpeed: 7000,
|
||||
directionNav: true,
|
||||
controlNav: true,
|
||||
randomize: false,
|
||||
|
||||
pauseOnHover: true,
|
||||
smoothHeight: false,
|
||||
prevText: "zurück",
|
||||
nextText: "weiter"
|
||||
});
|
||||
$z('#headerslider.flexslider').addClass("inited");
|
||||
}
|
||||
$z(document).ready(function() {
|
||||
autoplay = true;
|
||||
if ( $z("body").hasClass("zpreducemotion") ){
|
||||
autoplay = false;
|
||||
}
|
||||
|
||||
if ( $z('#headerslider .flex-container ul.slides li img').length > 0 ){
|
||||
// init flexslider after 1st image has loaded, so have dimensions as quickly as possible without having to wait too long on window loaded or such and can start right with doc ready
|
||||
$z('#headerslider .flex-container ul.slides li img').first().one("load", initSlider).each(function() {
|
||||
if(this.complete) $z(this).trigger('load');
|
||||
}); // .each() is to work around a browser bugs which don't fire load() for cached images http://stackoverflow.com/a/3877079
|
||||
}
|
||||
else{
|
||||
// also init flexslider if it doesn't contain images and is text only
|
||||
initSlider();
|
||||
}
|
||||
|
||||
// activate links for individual slideshow
|
||||
$z('#headerslider .flex-container ul.slides li[data-clickurl]').css("cursor","pointer");
|
||||
$z('#headerslider .flex-container ul.slides li[data-clickurl]').click(function (event){
|
||||
// Because this script captures clicks for a container, it will also fire when a regular link inside the container
|
||||
// is clicked. Therefore, we check below, if a regular link i.e. <a href="#"> was the target of the click.
|
||||
// Depending on the browser used, event.target might not be available, but event.srcElement would…
|
||||
var targ;
|
||||
if (event.target){
|
||||
targ = event.target;
|
||||
}
|
||||
else if (event.srcElement) {
|
||||
targ = event.srcElement;
|
||||
}
|
||||
if (targ.nodeName == "A") {
|
||||
// a regular link i.e. in a Caption was clicked. Return early, so the link's href doesn't get overridden
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $z(this).data("clicktarget") ){
|
||||
window.open($z(this).data("clickurl"), $z(this).data("clicktarget"));
|
||||
}
|
||||
else{
|
||||
window.location = $z(this).data("clickurl");
|
||||
}
|
||||
event.preventDefault;
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="content" class="main">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="zparea zpgrid" data-numcolumns="" data-areaname="Standard" data-pageid="588">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="container_106" class="zpContainer first zpBreakout" data-zpeleel="container" data-zpleid="106" style="padding-left: 0px; margin-left: calc(50% - 952.5px); width: 1905px;">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="zpRow standard " data-row-id="414">
|
||||
|
||||
|
||||
|
||||
<div class="zpColumn odd zpColumnWidth1 c12 first last" style="" data-column-id="489">
|
||||
|
||||
|
||||
<div class="zpColumnItem">
|
||||
<div id="a1811" data-article-id="1811" data-zpleid="1811" class="articlearea zpwText">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 style="color: rgb(17, 124, 97); " data-zpfieldname="headline">
|
||||
|
||||
KARRIERE BEI E-TIB
|
||||
|
||||
</h2>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p><span style="color: #333333;">Die E-TIB GmbH wurde 2015 aus der Motivation gegründet, hochwertige Leistungen für unsere Kunden sowie faire Entlohnung für unsere Mitarbeiter zu ermöglichen. Neben der Entlohnung steht für uns die Wertschätzung jedes einzelnen Mitarbeiters an oberster Stelle.</span></p><p><br></p><p><span style="color: #333333;">Wir bieten unseren Mitarbeitern:</span></p><ul><li>Einsatz bei regionalen und überregionalen Bauvorhaben</li><li>sicherer, unbefristeter Arbeitsvertrag in Vollzeit<br></li><li>entsprechende Einarbeitung in einem engagierten Team</li><li>leistungsbezogene und faire Vergütung</li><li>Auslöse bis 30,00 EUR pro Arbeitstag</li><li>31 Tage Urlaub sowie Urlaubsgeld</li><li>Arbeitskleidung (Engelbert-Strauss)</li><li>monatliche Zusatzzahlungen (persönliche MasterCard)</li><li>Zuzahlung im Rahmen der betrieblichen Altersvorsorge</li><li>individuelle Weiterbildungs- und Entwicklungsmöglichkeiten</li><li>Stellung Fahrzeug zur betrieblichen Nutzung und Fahrten zur Baustelle</li><li>familiäres Betriebsklima mit betrieblich organisierten Veranstaltungen<br></li></ul><p><br></p><p><span style="color: #333333;">Unsere erfolgreiche Unternehmensentwicklung ermöglicht uns kontinuierlich neue Arbeitskräfte einzustellen. Wir sind immer auf der
|
||||
Suche nach engagierten und motivierten Mitarbeitern. Werden Sie auch ein Teil
|
||||
unseres Teams. Schauen Sie sich unsere offenen Stellen an oder bewerben Sie sich initiativ.</span><br></p>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div><!-- .zpRow -->
|
||||
|
||||
|
||||
</div><!-- close container -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="container_167" class="zpContainer styled zpBreakout " data-zpeleel="container" data-zpleid="167" style="width: 1905px; padding-left: 0px; margin-left: calc(50% - 952.5px);">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="zpRow standard " data-row-id="483">
|
||||
|
||||
|
||||
|
||||
<div class="zpColumn odd zpColumnWidth1 c6 styled first last" style="" data-column-id="573">
|
||||
|
||||
|
||||
<div class="zpColumnItem">
|
||||
<div id="a1915" data-article-id="1915" data-zpleid="1915" class="articlearea zpwText">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h1 data-zpfieldname="headline">Wir suchen ab sofort in Festanstellung:</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div><!-- .zpRow -->
|
||||
|
||||
|
||||
</div><!-- close container -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section id="container_117" class="zpContainer zpBreakout " data-zpeleel="container" data-zpleid="117" style="width: 1905px; padding-left: 0px; margin-left: calc(50% - 952.5px);">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="zpRow standard " data-row-id="484">
|
||||
|
||||
|
||||
|
||||
<div class="zpColumn odd zpColumnWidth1 c12 first last" style="" data-column-id="574">
|
||||
|
||||
|
||||
<div class="zpColumnItem">
|
||||
<div id="a1916" data-article-id="1916" data-zpleid="1916" class="articlearea zpwText">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 style="color: rgb(17, 124, 97); " data-zpfieldname="headline">Initiativbewerbung (m/w/d)</h2>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p><span style="color: #333333;"></span><span style="color: #333333;">Sie können sich vorstellen, eine langfristige Herausforderung in unserem Unternehmen anzunehmen, finden aber kein passendes Stellenangebot?</span></p><p>Bewerben Sie sich initiativ schriftlich per Mail oder Post an:</p><p>E-TIB GmbH<br>Gewerbestraße 22
|
||||
<br>D-03172
|
||||
Guben<br>E-Mail:
|
||||
info@e-tib.com</p><p>Für Rückfragen melden Sie sich gerne unter: </p><p>Tel.: +49 (0) 3561 / 68577 33</p>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div><!-- .zpRow -->
|
||||
|
||||
|
||||
</section><!-- close container -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="container_154" class="zpContainer styled zpBreakout " data-zpeleel="container" data-zpleid="154" style="width: 1905px; padding-left: 0px; margin-left: calc(50% - 952.5px);">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="zpRow standard " data-row-id="459">
|
||||
|
||||
|
||||
|
||||
<div class="zpColumn odd zpColumnWidth1 c12 first last" style="" data-column-id="545">
|
||||
|
||||
|
||||
|
||||
<div class="zpColumnItem">
|
||||
<div id="a1887" data-article-id="1887" data-zpleid="1887" class="articlearea zpwButton">
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="kontakt.html" class="button">
|
||||
<span>KOMMUNIKATION</span>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div><!-- .zpRow -->
|
||||
|
||||
|
||||
</div><!-- close container -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="container_162" class="zpContainer last " data-zpeleel="container" data-zpleid="162">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="zpRow standard " data-row-id="471">
|
||||
|
||||
|
||||
<div class="zpFakeColumn flexbreak"></div>
|
||||
|
||||
|
||||
<div class="zpColumn odd zpColumnWidth1 c12 first last" style="clear: left;" data-column-id="561">
|
||||
|
||||
|
||||
<div class="zpColumnItem">
|
||||
<div id="a1903" data-article-id="1903" data-zpleid="1903" class="articlearea zpwText">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 style="color: #117C61; text-align:center;" data-zpfieldname="headline">ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU</h2>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div><!-- .zpRow -->
|
||||
|
||||
|
||||
</div><!-- close container -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div> <!-- .zparea -->
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Search -->
|
||||
<div id="search" class="container">
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
function getParameterByNamezpsearchform(name) {
|
||||
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
|
||||
var regexS = "[\\?&]" + name + "=([^&#]*)";
|
||||
var regex = new RegExp(regexS);
|
||||
var results = regex.exec(window.location.search);
|
||||
if(results == null) {
|
||||
return "";
|
||||
}
|
||||
else {
|
||||
return decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||
}
|
||||
}
|
||||
function encode_htmlzpsearchform(astring) { //helps prevent XSS e.g. when printing search strings
|
||||
astring = typeof astring !== 'undefined' ? astring : "";
|
||||
|
||||
astring = astring.replace(/</g, "<");
|
||||
astring = astring.replace(/>/g, ">");
|
||||
astring = astring.replace(/"/g, "&quo" + "t;");
|
||||
astring = astring.replace(/'/g, "'");
|
||||
astring = astring.replace(/\//g, "/");
|
||||
astring = astring.replace(/\[\$/g, "");
|
||||
astring = astring.replace(/\$\]/g, "");
|
||||
|
||||
return astring;
|
||||
}
|
||||
function searchzpsearchform() {
|
||||
var formId = "zpsearchform";
|
||||
if (document.getElementById(formId).q.value == 'Suchbegriff') {
|
||||
return false;
|
||||
}
|
||||
else if ($z.trim(document.getElementById(formId).q.value).length === 0) {
|
||||
alert("Bitte geben Sie einen Suchbegriff ein.");
|
||||
return false;
|
||||
}
|
||||
else{
|
||||
return;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<form id="zpsearchform" name="searchengine1" onsubmit="return searchzpsearchform()" action="suche.html" method="get" class="zpsearchform">
|
||||
|
||||
<input id="zpsearchfield" class="zpsearchfield typetext" type="text" name="q" title="Suche" placeholder="Suchbegriff" aria-label="Suchbegriff">
|
||||
|
||||
<input id="zpsearchbutton" class="zpsearchbutton" type="image" name="submit" src="assets/stock-images/magnifier.png" alt="Suchen" title="Suchen" aria-label="Suchen">
|
||||
|
||||
|
||||
</form>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
// set form field value to searchstring
|
||||
var tmpSearchstring = getParameterByNamezpsearchform("q");
|
||||
if ( tmpSearchstring ){
|
||||
// fill layout searchfield with searchsting
|
||||
document.getElementById("zpsearchform").q.value = tmpSearchstring;
|
||||
// fill search page searchfield with searchsting
|
||||
document.getElementById("zpsearchengine").q2.value = tmpSearchstring;
|
||||
// focus search page searchfield
|
||||
document.getElementById("zpsearchengine").q2.focus();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="afterfooter" class="container">
|
||||
|
||||
|
||||
<div class="zparea zpgrid" data-numcolumns="" data-areaname="Footer" data-pageid="588">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="container_109" class="zpContainer first last styled zpBreakout " data-zpeleel="container" data-zpleid="109" style="width: 1905px; padding-left: 0px; margin-left: calc(50% - 952.5px);">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="zpRow standard " data-row-id="390">
|
||||
|
||||
|
||||
|
||||
<div class="zpColumn odd zpColumnWidth1 c12 first last" style="" data-column-id="454">
|
||||
|
||||
|
||||
<div class="zpColumnItem">
|
||||
<div id="a1770" data-article-id="1770" data-zpleid="1770" class="articlearea zpwText">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p style="text-align: center;"><span style="color: #fafafa;">E-TIB GmbH<br>Gewerbestraße 22
|
||||
<br>D-03172
|
||||
Guben<br>Tel.: +49 (0) 3561 / 68577 33<br>Fax: +49 (0) 3561 / 68577 35<br>E-Mail:
|
||||
info@e-tib.com<br>Web: www.e-tib.com</span></p>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div><!-- .zpRow -->
|
||||
|
||||
|
||||
</div><!-- close container -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div> <!-- .zparea -->
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div id="footer" class="container">
|
||||
|
||||
|
||||
<div id="xmenu" class="">
|
||||
|
||||
|
||||
<a class="xmenulink" href="index.html">Unternehmen</a>
|
||||
|
||||
<a class="xmenulink" href="kontakt.html">Kontakt</a>
|
||||
|
||||
<a class="xmenulink" href="impressum.html">Impressum und Datenschutz</a>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div id="copyright">
|
||||
© 2026 E-TIB
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="totop" class="container noprint">
|
||||
<a href="#top" class="zpSO-Uplink-Flat zpnolayoutlinkstyles" title="Nach oben"><span class="zp-sronly">Nach oben</span></a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function setoverflown(s){
|
||||
var retcode = false;
|
||||
if ( $z("#nav").is(":hover") || ! $z("#nav > ul > li").length ){
|
||||
// mouse is over nav, which opens submenues and might cause the overflow
|
||||
// OR empty nav - needed because for IE an empty #nav == overflown()
|
||||
return false;
|
||||
}
|
||||
|
||||
$z("#nav > ul").removeClass("overflown");
|
||||
if ( $z("#nav:not(.on) > ul").overflown() ){
|
||||
// nav is already overflown by 1st-Level Menu entries (too many entries)
|
||||
$z("#nav > ul").addClass("overflown");
|
||||
retcode = true;
|
||||
}
|
||||
else{
|
||||
// on non touch devices, check if nav would be overflown horizontally if all submenues are visible (i.e. too many submenues expanding to the right)
|
||||
$z("#nav > ul ul:not(.nocalcoverflow)").css("display", "block");
|
||||
if ( $z("#nav:not(.on) > ul").overflown("x") ){
|
||||
$z("#nav > ul").addClass("overflown");
|
||||
retcode = true;
|
||||
}
|
||||
$z("#nav > ul ul:not(.nocalcoverflow)").css("display", "");
|
||||
|
||||
// if the previous check didn't already match, check if one of the submenus is higher than the available space in the window because the nav is fixed and wouldn't scroll
|
||||
if ( !retcode && $z("#nav.fixed").length ){
|
||||
$z("#nav.fixed > ul ul:not(.nocalcoverflow)").css("display", "block");
|
||||
$z("#nav.fixed > ul ul:not(.nocalcoverflow)").each(function(){
|
||||
var eltop = $z(this).offset().top - $(window).scrollTop(); // gets the effective top position relative to the window
|
||||
if ( (eltop + $(this).innerHeight()) > $z(window).innerHeight() ){
|
||||
// submenu doesn't fit on screen and would be cut off
|
||||
$z("#nav.fixed > ul").addClass("overflown");
|
||||
retcode = true;
|
||||
$z("#nav.fixed > ul ul:not(.nocalcoverflow)").css("display", "");
|
||||
// exit the each loop early
|
||||
return false;
|
||||
}
|
||||
});
|
||||
$z("#nav.fixed > ul ul:not(.nocalcoverflow)").css("display", "");
|
||||
}
|
||||
}
|
||||
|
||||
// see if we need to scale to the logo to fit besides the mobile menu-button
|
||||
var availspace = $z("#nav > ul").width() - $z("#nav > ul > li.overflownonly").width();
|
||||
$z("#nav > ul > li.logo").css("max-width", availspace + "px");
|
||||
|
||||
return retcode;
|
||||
}
|
||||
|
||||
function stickyNav(topoffset){
|
||||
// make navigations menu sticky when scrolling the menu past the top of the window
|
||||
var scrolltop = $z("body").scrollTop() || (document.documentElement && document.documentElement.scrollTop) || 0;
|
||||
|
||||
if ( !$z("#nav ul ul:visible").length && scrolltop - topoffset >= 0){
|
||||
$z("body").addClass("padded");
|
||||
$z("#nav").addClass("fixed");
|
||||
}
|
||||
else{
|
||||
$z("body").removeClass("padded");
|
||||
$z("#nav").removeClass("fixed");
|
||||
}
|
||||
// with a little delay, check if the menu gets overflown (i.e. because we're also displaying the logo in the nav now
|
||||
setTimeout(function(){setoverflown();}, 100);
|
||||
}
|
||||
// globals
|
||||
var navOffset;
|
||||
|
||||
$z(document).ready(function(){
|
||||
// smooth scroll the layout specific go to top link
|
||||
$z("a.zpSO-Uplink-Flat").click(function(e){
|
||||
e.preventDefault();
|
||||
if ( $z(this.hash).length ){
|
||||
$z('html,body').animate({
|
||||
scrollTop: $z(this.hash).offset().top - parseInt($z("body").css("padding-top"))
|
||||
}, 500);
|
||||
window.location.hash = this.hash;
|
||||
}
|
||||
});
|
||||
|
||||
// switches the Mobile Navigation-Menu on/off, when "Menu" link is clicked.
|
||||
$z("a#mobilenavtoggle").on("click", function(e){
|
||||
e.preventDefault();
|
||||
$z("#nav").toggleClass("on");
|
||||
setoverflown();
|
||||
});
|
||||
|
||||
setoverflown();
|
||||
var resizeTimeout = null;
|
||||
$z(window).resize(zpthrottle(100, function(event) {
|
||||
var event = event || window.event;
|
||||
if ( event && $z(event.target.nodeType).length == 0 ){// enable window.resize only for the window object (resizing elements might also trigger window.resize) which doesn't have a nodeType
|
||||
setoverflown();
|
||||
}
|
||||
}));
|
||||
|
||||
if ( "false" == "false" && ("false" == "true" || "false" == "true") ){
|
||||
navOffset = $z("#nav:not(.fixed)").offset().top;
|
||||
setTimeout(function(){stickyNav(navOffset);}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
$z(window).on('load', function(){
|
||||
setTimeout(function(){setoverflown();}, 500);
|
||||
var navheight = 0;
|
||||
if ( "false" == "false" ){
|
||||
navheight = $z("#nav").outerHeight();
|
||||
}
|
||||
// pull anchors below fixed header into visible area
|
||||
if ( location.hash != "" && $z(':target').length ) {
|
||||
$z('html,body').animate({scrollTop: $z(':target').offset().top - navheight - 10 }, 600);
|
||||
}
|
||||
$z("a[href*='#']:not([href='#'])").not(".zpNoScroll").click(function () {
|
||||
var target = $z(this).attr("href");
|
||||
target = target.substring(target.indexOf("#"));
|
||||
if ( $z(target).length ){
|
||||
$z('html,body').animate({scrollTop: $z(target).offset().top - navheight - 10 }, 600);
|
||||
}
|
||||
});
|
||||
|
||||
if ( "false" == "false" && ("false" == "true" || "false" == "true") ){
|
||||
if ( $z("#nav:not(.fixed)").length ){
|
||||
navOffset = $z("#nav:not(.fixed)").offset().top;
|
||||
}
|
||||
setTimeout(function(){stickyNav(navOffset);}, 500);
|
||||
$z(window).scroll(zpthrottle(100, function(event) {
|
||||
stickyNav(navOffset);
|
||||
}));
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- {{{ -->
|
||||
<!--
|
||||
* Layout: Flat Responsive by StS
|
||||
* $Id: default.html 2022-03-09 11:20:24 +0100 Stefan S 8c952fdc662aa6619ae8ff22707dc8c8284a3b37 $
|
||||
-->
|
||||
<!-- }}} -->
|
||||
|
||||
<div id="fancybox-tmp"></div><div id="fancybox-loading"><div></div></div><div id="fancybox-overlay"></div><div id="fancybox-wrap"><div id="fancybox-outer"><div class="fancybox-bg" id="fancybox-bg-n"></div><div class="fancybox-bg" id="fancybox-bg-ne"></div><div class="fancybox-bg" id="fancybox-bg-e"></div><div class="fancybox-bg" id="fancybox-bg-se"></div><div class="fancybox-bg" id="fancybox-bg-s"></div><div class="fancybox-bg" id="fancybox-bg-sw"></div><div class="fancybox-bg" id="fancybox-bg-w"></div><div class="fancybox-bg" id="fancybox-bg-nw"></div><div id="fancybox-content"></div><a id="fancybox-close"></a><div id="fancybox-title"></div><a href="javascript:;" id="fancybox-left"><span class="fancy-ico" id="fancybox-left-ico"></span><span class="zp-sronly">Voriges Bild (prev. image)</span></a><a href="javascript:;" id="fancybox-right"><span class="fancy-ico" id="fancybox-right-ico"></span><span class="zp-sronly">Nächstes Bild (next image)</span></a></div></div></body></html>
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"url": "https://www.e-tib.com/ueber-uns.html",
|
||||
"pathname": "/ueber-uns.html",
|
||||
"title": "Herzlich willkommen!",
|
||||
"type": "about",
|
||||
"headings": [
|
||||
"KARRIERE",
|
||||
"KARRIERE",
|
||||
"KARRIERE BEI E-TIB",
|
||||
"Wir suchen ab sofort in Festanstellung:",
|
||||
"Initiativbewerbung (m/w/d)",
|
||||
"ENERGIE - TELEKOMMUNIKATION - INFRASTRUKTUR - BAU"
|
||||
],
|
||||
"navItems": [],
|
||||
"features": [
|
||||
"search",
|
||||
"forms"
|
||||
],
|
||||
"links": [
|
||||
"/index.html",
|
||||
"/kompetenzen-01.html",
|
||||
"/startseite.html",
|
||||
"/ueber-uns.html",
|
||||
"/kontakt.html",
|
||||
"/impressum.html",
|
||||
"/"
|
||||
],
|
||||
"images": [
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie-51-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-19-von-99-large.jpg",
|
||||
"https://www.e-tib.com/media/images/etib_e-tib_tiefbau_guben_netzanbindung_energie_vermessung-24-von-99-large.jpg"
|
||||
],
|
||||
"meta": {
|
||||
"ogTitle": "Herzlich willkommen!"
|
||||
}
|
||||
}
|
||||
270
lib/blog.ts
270
lib/blog.ts
@@ -1,31 +1,9 @@
|
||||
import { getPayload } from 'payload';
|
||||
import configPromise from '@payload-config';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import matter from 'gray-matter';
|
||||
import { config } from '@/lib/config';
|
||||
|
||||
export function extractExcerpt(content: string): string {
|
||||
if (!content) return '';
|
||||
// Remove frontmatter if present (though matter() usually strips it out)
|
||||
let text = content.replace(/^---[\s\S]*?---/, '');
|
||||
// Remove MDX component imports and usages
|
||||
text = text.replace(/<[^>]+>/g, '');
|
||||
text = text.replace(/^[ \t]*import\s+.*$/gm, '');
|
||||
text = text.replace(/^[ \t]*export\s+.*$/gm, '');
|
||||
// Remove markdown headings
|
||||
text = text.replace(/^#+.*$/gm, '');
|
||||
// Extract first paragraph or combined lines
|
||||
const paragraphs = text
|
||||
.split(/\n\s*\n/)
|
||||
.filter((p) => p.trim() && !p.trim().startsWith('---') && !p.trim().startsWith('#'));
|
||||
if (paragraphs.length === 0) return '';
|
||||
|
||||
const excerpt = paragraphs[0]
|
||||
.replace(/[*_`]/g, '') // remove markdown bold/italic/code
|
||||
.replace(/\[(.*?)\]\(.*?\)/g, '$1') // replace links with their text
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
|
||||
return excerpt.length > 200 ? excerpt.slice(0, 197) + '...' : excerpt;
|
||||
}
|
||||
const BLOG_DIR = path.join(process.cwd(), 'content', 'blog');
|
||||
|
||||
export interface PostFrontmatter {
|
||||
title: string;
|
||||
@@ -41,149 +19,52 @@ export interface PostFrontmatter {
|
||||
export interface PostData {
|
||||
slug: string;
|
||||
frontmatter: PostFrontmatter;
|
||||
content: any; // Mapped to Lexical SerializedEditorState
|
||||
content: string;
|
||||
}
|
||||
|
||||
export function isPostVisible(post: { frontmatter: { date: string; public?: boolean } }) {
|
||||
// If explicitly marked as not public, hide in production
|
||||
if (post.frontmatter.public === false && config.isProduction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const postDate = new Date(post.frontmatter.date);
|
||||
const now = new Date();
|
||||
return !(postDate > now && config.isProduction);
|
||||
}
|
||||
|
||||
export async function getPostBySlug(slug: string, locale: string): Promise<PostData | null> {
|
||||
try {
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
// Check locale-specific directory first
|
||||
const localeDir = path.join(process.cwd(), 'content', locale, 'blog');
|
||||
const filePath = path.join(localeDir, `${slug}.mdx`);
|
||||
|
||||
// First try: Find in the requested locale
|
||||
let { docs } = await payload.find({
|
||||
collection: 'posts',
|
||||
where: {
|
||||
slug: { equals: slug },
|
||||
...(!config.showDrafts ? { _status: { equals: 'published' } } : {}),
|
||||
},
|
||||
locale: locale as any,
|
||||
draft: config.showDrafts,
|
||||
limit: 1,
|
||||
});
|
||||
if (!fs.existsSync(filePath)) return null;
|
||||
|
||||
// Fallback: If not found, try searching across all locales.
|
||||
// This happens when a user uses the static language switcher
|
||||
// e.g. switching from /en/blog/en-slug to /de/blog/en-slug.
|
||||
if (!docs || docs.length === 0) {
|
||||
const { docs: crossLocaleDocs } = await payload.find({
|
||||
collection: 'posts',
|
||||
where: {
|
||||
slug: { equals: slug },
|
||||
...(!config.showDrafts ? { _status: { equals: 'published' } } : {}),
|
||||
},
|
||||
locale: 'all',
|
||||
draft: config.showDrafts,
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
if (crossLocaleDocs && crossLocaleDocs.length > 0) {
|
||||
// Fetch the found document again, but strictly in the requested locale
|
||||
// so we get the correctly translated fields (like the localized slug)
|
||||
const { docs: correctLocaleDocs } = await payload.find({
|
||||
collection: 'posts',
|
||||
where: {
|
||||
id: { equals: crossLocaleDocs[0].id },
|
||||
},
|
||||
locale: locale as any,
|
||||
draft: config.showDrafts,
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
docs = correctLocaleDocs;
|
||||
}
|
||||
}
|
||||
|
||||
if (!docs || docs.length === 0) return null;
|
||||
|
||||
const doc = docs[0];
|
||||
const source = fs.readFileSync(filePath, 'utf8');
|
||||
const { content, data } = matter(source);
|
||||
|
||||
return {
|
||||
slug: doc.slug,
|
||||
frontmatter: {
|
||||
title: doc.title,
|
||||
date: doc.date,
|
||||
excerpt: doc.excerpt || '',
|
||||
category: doc.category || '',
|
||||
featuredImage:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.url || doc.featuredImage.sizes?.card?.url
|
||||
: null,
|
||||
focalX:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.focalX
|
||||
: 50,
|
||||
focalY:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.focalY
|
||||
: 50,
|
||||
public: doc._status === 'published',
|
||||
} as PostFrontmatter,
|
||||
content: doc.content as any, // Native Lexical Editor State
|
||||
slug,
|
||||
frontmatter: data as PostFrontmatter,
|
||||
content,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`[Payload] getPostBySlug failed for ${slug}:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllPosts(locale: string): Promise<PostData[]> {
|
||||
try {
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
const { docs } = await payload.find({
|
||||
collection: 'posts',
|
||||
where: {
|
||||
...(!config.showDrafts ? { _status: { equals: 'published' } } : {}),
|
||||
},
|
||||
locale: locale as any,
|
||||
sort: '-date',
|
||||
draft: config.showDrafts,
|
||||
limit: 100,
|
||||
});
|
||||
const localeDir = path.join(process.cwd(), 'content', locale, 'blog');
|
||||
if (!fs.existsSync(localeDir)) return [];
|
||||
|
||||
console.log(`[Payload] getAllPosts for ${locale}: Found ${docs.length} docs`);
|
||||
const files = fs.readdirSync(localeDir).filter(f => f.endsWith('.mdx'));
|
||||
|
||||
const posts = docs.map((doc) => {
|
||||
return {
|
||||
slug: doc.slug,
|
||||
frontmatter: {
|
||||
title: doc.title,
|
||||
date: doc.date,
|
||||
excerpt: doc.excerpt || '',
|
||||
category: doc.category || '',
|
||||
featuredImage:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.url || doc.featuredImage.sizes?.card?.url
|
||||
: null,
|
||||
focalX:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.focalX
|
||||
: 50,
|
||||
focalY:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.focalY
|
||||
: 50,
|
||||
} as PostFrontmatter,
|
||||
// Pass the Lexical content object rather than raw markdown string
|
||||
content: doc.content as any,
|
||||
};
|
||||
});
|
||||
const posts = await Promise.all(
|
||||
files.map(async f => {
|
||||
const slug = f.replace('.mdx', '');
|
||||
return await getPostBySlug(slug, locale);
|
||||
})
|
||||
);
|
||||
|
||||
// Integrity check: only show posts with a featured image in listings/sitemap
|
||||
return posts.filter((p) => !!p.frontmatter.featuredImage);
|
||||
} catch (error) {
|
||||
console.error(`[Payload] getAllPosts failed for ${locale}:`, error);
|
||||
return [];
|
||||
}
|
||||
return posts
|
||||
.filter((p): p is PostData => p !== null)
|
||||
.filter(isPostVisible)
|
||||
.sort((a, b) => new Date(b.frontmatter.date).getTime() - new Date(a.frontmatter.date).getTime());
|
||||
}
|
||||
|
||||
export async function getAllPostsMetadata(locale: string): Promise<Partial<PostData>[]> {
|
||||
@@ -210,34 +91,10 @@ export async function getAdjacentPosts(
|
||||
return { prev: null, next: null };
|
||||
}
|
||||
|
||||
// Posts are sorted by date descending (newest first)
|
||||
// So "next" post (newer) is at index - 1
|
||||
// And "previous" post (older) is at index + 1
|
||||
let next = currentIndex > 0 ? posts[currentIndex - 1] : null;
|
||||
let prev = currentIndex < posts.length - 1 ? posts[currentIndex + 1] : null;
|
||||
|
||||
let isNextRandom = false;
|
||||
let isPrevRandom = false;
|
||||
|
||||
const getRandomPost = (excludeSlugs: string[]) => {
|
||||
const available = posts.filter((p) => !excludeSlugs.includes(p.slug));
|
||||
if (available.length === 0) return null;
|
||||
return available[Math.floor(Math.random() * available.length)];
|
||||
};
|
||||
|
||||
// If there's no next post (we are at the newest post), show a random post instead
|
||||
if (!next && posts.length > 2) {
|
||||
next = getRandomPost([slug, prev?.slug].filter(Boolean) as string[]);
|
||||
isNextRandom = true;
|
||||
}
|
||||
|
||||
// If there's no previous post (we are at the oldest post), show a random post instead
|
||||
if (!prev && posts.length > 2) {
|
||||
prev = getRandomPost([slug, next?.slug].filter(Boolean) as string[]);
|
||||
isPrevRandom = true;
|
||||
}
|
||||
|
||||
return { prev, next, isPrevRandom, isNextRandom };
|
||||
return { prev, next };
|
||||
}
|
||||
|
||||
export function getReadingTime(content: string): number {
|
||||
@@ -249,75 +106,18 @@ export function getReadingTime(content: string): number {
|
||||
|
||||
export function generateHeadingId(text: string): string {
|
||||
let id = text.toLowerCase();
|
||||
id = id.replace(/ä/g, 'ae');
|
||||
id = id.replace(/ö/g, 'oe');
|
||||
id = id.replace(/ü/g, 'ue');
|
||||
id = id.replace(/ß/g, 'ss');
|
||||
|
||||
id = id.replace(/[*_`]/g, '');
|
||||
id = id.replace(/[^\w\s-]/g, '');
|
||||
id = id
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/-+/g, '-')
|
||||
.replace(/^-+|-+$/g, '');
|
||||
|
||||
id = id.replace(/ä/g, 'ae').replace(/ö/g, 'oe').replace(/ü/g, 'ue').replace(/ß/g, 'ss');
|
||||
id = id.replace(/[^\w\s-]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-').replace(/^-+|-+$/g, '');
|
||||
return id || 'heading';
|
||||
}
|
||||
|
||||
export function getTextContent(node: any): string {
|
||||
if (typeof node === 'string') return node;
|
||||
if (typeof node === 'number') return node.toString();
|
||||
if (Array.isArray(node)) return node.map(getTextContent).join('');
|
||||
if (node && typeof node === 'object' && node.props && node.props.children) {
|
||||
return getTextContent(node.props.children);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
export function getHeadings(content: string): { id: string; text: string; level: number }[] {
|
||||
const headingLines = content.split('\n').filter((line) => line.match(/^#{1,3}\s/));
|
||||
const headingLines = content.split('\n').filter((line) => line.match(/^#{2,4}\s/));
|
||||
|
||||
return headingLines.map((line) => {
|
||||
const level = (line.match(/^#+/)?.[0].length || 0) + 1; // Shift H1->H2, H2->H3, H3->H4
|
||||
const rawText = line.replace(/^#+\s/, '').trim();
|
||||
const cleanText = rawText.replace(/[*_`]/g, '');
|
||||
const id = generateHeadingId(cleanText);
|
||||
|
||||
return { id, text: cleanText, level };
|
||||
const level = line.match(/^#+/)?.[0].length || 2;
|
||||
const text = line.replace(/^#+\s/, '').trim();
|
||||
const id = generateHeadingId(text);
|
||||
return { id, text, level };
|
||||
});
|
||||
}
|
||||
|
||||
export function extractLexicalHeadings(
|
||||
node: any,
|
||||
headings: { id: string; text: string; level: number }[] = [],
|
||||
): { id: string; text: string; level: number }[] {
|
||||
if (!node) return headings;
|
||||
|
||||
if (node.type === 'heading' && node.tag) {
|
||||
const level = parseInt(node.tag.replace('h', ''));
|
||||
const text = getTextContentFromLexical(node);
|
||||
if (text) {
|
||||
headings.push({
|
||||
id: generateHeadingId(text),
|
||||
text,
|
||||
level,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (node.children && Array.isArray(node.children)) {
|
||||
node.children.forEach((child: any) => extractLexicalHeadings(child, headings));
|
||||
}
|
||||
|
||||
return headings;
|
||||
}
|
||||
|
||||
function getTextContentFromLexical(node: any): string {
|
||||
if (node.type === 'text') {
|
||||
return node.text || '';
|
||||
}
|
||||
if (node.children && Array.isArray(node.children)) {
|
||||
return node.children.map(getTextContentFromLexical).join('');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
21
lib/mdx.ts
Normal file
21
lib/mdx.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import matter from 'gray-matter';
|
||||
|
||||
const CONTENT_DIR = path.join(process.cwd(), 'content');
|
||||
|
||||
export async function getMdxContent(locale: string, slug: string) {
|
||||
const filePath = path.join(CONTENT_DIR, locale, `${slug}.mdx`);
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const source = fs.readFileSync(filePath, 'utf8');
|
||||
const { content, data } = matter(source);
|
||||
|
||||
return {
|
||||
content,
|
||||
frontmatter: data,
|
||||
};
|
||||
}
|
||||
201
lib/pages.ts
201
lib/pages.ts
@@ -1,193 +1,76 @@
|
||||
import { getPayload } from 'payload';
|
||||
import configPromise from '@payload-config';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import matter from 'gray-matter';
|
||||
import { mapSlugToFileSlug } from './slugs';
|
||||
import { config } from '@/lib/config';
|
||||
|
||||
const CONTENT_DIR = path.join(process.cwd(), 'content');
|
||||
|
||||
export interface PageFrontmatter {
|
||||
title: string;
|
||||
excerpt: string;
|
||||
featuredImage: string | null;
|
||||
excerpt?: string;
|
||||
featuredImage?: string | null;
|
||||
focalX?: number;
|
||||
focalY?: number;
|
||||
layout?: 'default' | 'fullBleed';
|
||||
public?: boolean;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface PageData {
|
||||
slug: string;
|
||||
frontmatter: PageFrontmatter;
|
||||
content: any; // Lexical AST Document
|
||||
}
|
||||
|
||||
function mapDoc(doc: any): PageData {
|
||||
return {
|
||||
slug: doc.slug,
|
||||
frontmatter: {
|
||||
title: doc.title,
|
||||
excerpt: doc.excerpt || '',
|
||||
featuredImage:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.sizes?.card?.url || doc.featuredImage.url
|
||||
: null,
|
||||
focalX:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.focalX
|
||||
: 50,
|
||||
focalY:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.focalY
|
||||
: 50,
|
||||
layout: doc.layout || 'default',
|
||||
} as PageFrontmatter,
|
||||
content: doc.content as any,
|
||||
};
|
||||
content: string;
|
||||
}
|
||||
|
||||
export async function getPageBySlug(slug: string, locale: string): Promise<PageData | null> {
|
||||
// Guard against invalid locales hitting the DB (e.g. favicon.ico, assets)
|
||||
if (locale !== 'de' && locale !== 'en') {
|
||||
const fileSlug = await mapSlugToFileSlug(slug, locale);
|
||||
const filePath = path.join(CONTENT_DIR, locale, `${fileSlug}.mdx`);
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
const fileSlug = await mapSlugToFileSlug(slug, locale);
|
||||
|
||||
// Try finding exact match first
|
||||
let result = await payload.find({
|
||||
collection: 'pages',
|
||||
where: {
|
||||
and: [
|
||||
{ slug: { equals: fileSlug } },
|
||||
...(!config.showDrafts ? [{ _status: { equals: 'published' } }] : []),
|
||||
],
|
||||
},
|
||||
locale: locale as any,
|
||||
depth: 1,
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
// Fallback: search ALL locales
|
||||
if (result.docs.length === 0) {
|
||||
const crossResult = await payload.find({
|
||||
collection: 'pages',
|
||||
where: {
|
||||
and: [
|
||||
{ slug: { equals: fileSlug } },
|
||||
...(!config.showDrafts ? [{ _status: { equals: 'published' } }] : []),
|
||||
],
|
||||
},
|
||||
locale: 'all',
|
||||
depth: 1,
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
if (crossResult.docs.length > 0) {
|
||||
// Fetch missing exact match by internal id
|
||||
result = await payload.find({
|
||||
collection: 'pages',
|
||||
where: {
|
||||
id: { equals: crossResult.docs[0].id },
|
||||
},
|
||||
locale: locale as any,
|
||||
depth: 1,
|
||||
limit: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (result.docs.length > 0) {
|
||||
const doc = result.docs[0];
|
||||
const source = fs.readFileSync(filePath, 'utf8');
|
||||
const { content, data } = matter(source);
|
||||
|
||||
return {
|
||||
slug: doc.slug,
|
||||
frontmatter: {
|
||||
title: doc.title,
|
||||
excerpt: doc.excerpt || '',
|
||||
featuredImage:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.sizes?.card?.url || doc.featuredImage.url
|
||||
: null,
|
||||
focalX:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.focalX
|
||||
: 50,
|
||||
focalY:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.focalY
|
||||
: 50,
|
||||
layout:
|
||||
doc.layout === 'fullBleed' || doc.layout === 'default'
|
||||
? doc.layout
|
||||
: ('default' as const),
|
||||
},
|
||||
content: doc.content,
|
||||
slug,
|
||||
frontmatter: data as PageFrontmatter,
|
||||
content,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error(`[Payload] getPageBySlug failed for ${slug}:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllPages(locale: string): Promise<PageData[]> {
|
||||
if (locale !== 'de' && locale !== 'en') {
|
||||
return [];
|
||||
}
|
||||
const localeDir = path.join(CONTENT_DIR, locale);
|
||||
if (!fs.existsSync(localeDir)) return [];
|
||||
|
||||
try {
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
const files = fs.readdirSync(localeDir).filter(f => f.endsWith('.mdx'));
|
||||
|
||||
const result = await payload.find({
|
||||
collection: 'pages' as any,
|
||||
locale: locale as any,
|
||||
limit: 100,
|
||||
});
|
||||
const pages = await Promise.all(
|
||||
files.map(async f => {
|
||||
const slug = f.replace('.mdx', '');
|
||||
return await getPageBySlug(slug, locale);
|
||||
})
|
||||
);
|
||||
|
||||
return (result.docs as any[]).map(mapDoc);
|
||||
} catch (error) {
|
||||
console.error(`[Payload] getAllPages failed for ${locale}:`, error);
|
||||
return [];
|
||||
}
|
||||
return pages.filter((p): p is PageData => p !== null);
|
||||
}
|
||||
|
||||
export async function getAllPagesMetadata(locale: string): Promise<Partial<PageData>[]> {
|
||||
if (locale !== 'de' && locale !== 'en') {
|
||||
return [];
|
||||
}
|
||||
export async function getAllPagesMetadata(locale: string): Promise<PageData[]> {
|
||||
const localeDir = path.join(CONTENT_DIR, locale);
|
||||
if (!fs.existsSync(localeDir)) return [];
|
||||
|
||||
try {
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
const files = fs.readdirSync(localeDir).filter(f => f.endsWith('.mdx'));
|
||||
|
||||
const result = await payload.find({
|
||||
collection: 'pages' as any,
|
||||
locale: locale as any,
|
||||
limit: 100,
|
||||
return files.map(f => {
|
||||
const slug = f.replace('.mdx', '');
|
||||
const filePath = path.join(localeDir, f);
|
||||
const source = fs.readFileSync(filePath, 'utf8');
|
||||
const { data } = matter(source);
|
||||
return {
|
||||
slug,
|
||||
frontmatter: data as PageFrontmatter,
|
||||
content: '', // Metadata only
|
||||
};
|
||||
});
|
||||
|
||||
return (result.docs as any[]).map((doc: any) => ({
|
||||
slug: doc.slug,
|
||||
frontmatter: {
|
||||
title: doc.title,
|
||||
excerpt: doc.excerpt || '',
|
||||
featuredImage:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.sizes?.card?.url || doc.featuredImage.url
|
||||
: null,
|
||||
focalX:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.focalX
|
||||
: 50,
|
||||
focalY:
|
||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||
? doc.featuredImage.focalY
|
||||
: 50,
|
||||
} as PageFrontmatter,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error(`[Payload] getAllPagesMetadata failed for ${locale}:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"karriere": "karriere",
|
||||
"kompetenzen": "kompetenzen",
|
||||
"ueber-uns": "ueber-uns",
|
||||
"agb": "agb",
|
||||
"start": "start"
|
||||
},
|
||||
"products": {},
|
||||
@@ -87,21 +88,39 @@
|
||||
},
|
||||
"title": "Kontaktieren Sie uns",
|
||||
"subtitle": "Haben Sie Fragen zu unseren Leistungen oder ein konkretes Projekt? Wir sind für Sie da.",
|
||||
"heroSubtitle": "Wir sind für Sie da",
|
||||
"info": {
|
||||
"title": "Kontaktinformationen",
|
||||
"subtitle": "Direkte Wege zu uns",
|
||||
"howToReachUs": "So erreichen Sie uns",
|
||||
"office": "Hauptsitz Guben",
|
||||
"address": "Gewerbestraße 22\n03172 Guben\nDeutschland",
|
||||
"phone": "+49 15207230518",
|
||||
"email": "d.joseph@e-tib.com"
|
||||
},
|
||||
"hours": {
|
||||
"title": "Öffnungszeiten",
|
||||
"weekdays": "Mo. – Fr.",
|
||||
"weekdaysTime": "08:00 – 17:00 Uhr",
|
||||
"weekend": "Sa. – So.",
|
||||
"closed": "Geschlossen"
|
||||
},
|
||||
"form": {
|
||||
"title": "Nachricht senden",
|
||||
"subtitle": "Senden Sie uns Ihr Anliegen",
|
||||
"name": "Name",
|
||||
"email": "E-Mail",
|
||||
"emailPlaceholder": "ihre@email.de",
|
||||
"message": "Nachricht",
|
||||
"messagePlaceholder": "Ihre Nachricht an uns...",
|
||||
"submit": "Senden",
|
||||
"submitting": "Wird gesendet...",
|
||||
"successTitle": "Vielen Dank!",
|
||||
"successDesc": "Wir haben Ihre Nachricht erhalten und melden uns kurzfristig bei Ihnen."
|
||||
"successDesc": "Wir haben Ihre Nachricht erhalten und melden uns kurzfristig bei Ihnen.",
|
||||
"sendAnother": "Weitere Nachricht senden",
|
||||
"errorTitle": "Fehler!",
|
||||
"error": "Etwas ist schiefgelaufen. Bitte prüfen Sie Ihre Eingaben.",
|
||||
"tryAgain": "Erneut versuchen"
|
||||
}
|
||||
},
|
||||
"Competencies": {
|
||||
@@ -133,7 +152,10 @@
|
||||
},
|
||||
"StandardPage": {
|
||||
"badge": "Information",
|
||||
"overview": "Übersicht"
|
||||
"overview": "Übersicht",
|
||||
"needHelp": "Brauchen Sie Unterstützung?",
|
||||
"supportTeamAvailable": "Unser Expertenteam steht Ihnen für alle Fragen rund um Ihr Infrastrukturprojekt zur Verfügung.",
|
||||
"contactUs": "Kontaktieren Sie uns"
|
||||
},
|
||||
"Error": {
|
||||
"title": "Hoppla!",
|
||||
@@ -142,5 +164,24 @@
|
||||
"description": "Die gesuchte Seite konnte nicht gefunden werden.",
|
||||
"cta": "Zur Startseite"
|
||||
}
|
||||
},
|
||||
"Blog": {
|
||||
"meta": {
|
||||
"title": "Blog & Aktuelles",
|
||||
"description": "Neuigkeiten und Einblicke in die Welt des Kabeltiefbaus und der Infrastruktur."
|
||||
},
|
||||
"featuredPost": "Top-Beitrag",
|
||||
"readFullArticle": "Vollständigen Artikel lesen",
|
||||
"latestNews": "Aktuelles",
|
||||
"allArticles": "Alle Artikel",
|
||||
"readMore": "Weiterlesen",
|
||||
"prev": "Zurück",
|
||||
"next": "Weiter",
|
||||
"categories": {
|
||||
"all": "Alle",
|
||||
"industry": "Branche",
|
||||
"technical": "Technik",
|
||||
"sustainability": "Nachhaltigkeit"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"career": "karriere",
|
||||
"competencies": "kompetenzen",
|
||||
"about-us": "ueber-uns",
|
||||
"terms": "agb",
|
||||
"start": "start"
|
||||
},
|
||||
"products": {},
|
||||
@@ -87,21 +88,39 @@
|
||||
},
|
||||
"title": "Contact Us",
|
||||
"subtitle": "Do you have questions about our services or a specific project? We are here for you.",
|
||||
"heroSubtitle": "We are here for you",
|
||||
"info": {
|
||||
"title": "Contact Information",
|
||||
"subtitle": "Direct ways to reach us",
|
||||
"howToReachUs": "How to reach us",
|
||||
"office": "Headquarters Guben",
|
||||
"address": "Gewerbestraße 22\n03172 Guben\nGermany",
|
||||
"phone": "+49 15207230518",
|
||||
"email": "d.joseph@e-tib.com"
|
||||
},
|
||||
"hours": {
|
||||
"title": "Opening Hours",
|
||||
"weekdays": "Mon. – Fri.",
|
||||
"weekdaysTime": "08:00 – 17:00",
|
||||
"weekend": "Sat. – Sun.",
|
||||
"closed": "Closed"
|
||||
},
|
||||
"form": {
|
||||
"title": "Send a Message",
|
||||
"subtitle": "Send us your inquiry",
|
||||
"name": "Name",
|
||||
"email": "Email",
|
||||
"emailPlaceholder": "your@email.com",
|
||||
"message": "Message",
|
||||
"messagePlaceholder": "Your message to us...",
|
||||
"submit": "Send",
|
||||
"submitting": "Sending...",
|
||||
"successTitle": "Thank you!",
|
||||
"successDesc": "We have received your message and will get back to you shortly."
|
||||
"successDesc": "We have received your message and will get back to you shortly.",
|
||||
"sendAnother": "Send another message",
|
||||
"errorTitle": "Error!",
|
||||
"error": "Something went wrong. Please check your input and try again.",
|
||||
"tryAgain": "Try Again"
|
||||
}
|
||||
},
|
||||
"Competencies": {
|
||||
@@ -133,7 +152,10 @@
|
||||
},
|
||||
"StandardPage": {
|
||||
"badge": "Information",
|
||||
"overview": "Overview"
|
||||
"overview": "Overview",
|
||||
"needHelp": "Need support?",
|
||||
"supportTeamAvailable": "Our team of experts is available to answer all your questions regarding your infrastructure project.",
|
||||
"contactUs": "Contact Us"
|
||||
},
|
||||
"Error": {
|
||||
"title": "Oops!",
|
||||
@@ -142,5 +164,24 @@
|
||||
"description": "The requested page could not be found.",
|
||||
"cta": "Back to Home"
|
||||
}
|
||||
},
|
||||
"Blog": {
|
||||
"meta": {
|
||||
"title": "Blog & News",
|
||||
"description": "Latest news and insights from the world of cable construction and infrastructure."
|
||||
},
|
||||
"featuredPost": "Featured Post",
|
||||
"readFullArticle": "Read Full Article",
|
||||
"latestNews": "Latest News",
|
||||
"allArticles": "All Articles",
|
||||
"readMore": "Read More",
|
||||
"prev": "Prev",
|
||||
"next": "Next",
|
||||
"categories": {
|
||||
"all": "All",
|
||||
"industry": "Industry",
|
||||
"technical": "Technical",
|
||||
"sustainability": "Sustainability"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
@@ -2,7 +2,7 @@ import withMintelConfig from '@mintel/next-config';
|
||||
import withBundleAnalyzer from '@next/bundle-analyzer';
|
||||
import { withSentryConfig } from '@sentry/nextjs';
|
||||
|
||||
import { withPayload } from '@payloadcms/next/withPayload';
|
||||
import createMDX from '@next/mdx';
|
||||
|
||||
const isProd = process.env.NODE_ENV === 'production';
|
||||
/** @type {import('next').NextConfig} */
|
||||
@@ -20,7 +20,6 @@ const nextConfig = {
|
||||
'framer-motion',
|
||||
'@/components/ui',
|
||||
'@sentry/nextjs',
|
||||
'@payloadcms/richtext-lexical',
|
||||
'react-hook-form',
|
||||
'zod',
|
||||
'date-fns',
|
||||
@@ -523,7 +522,8 @@ const withAnalyzer = withBundleAnalyzer({
|
||||
// withMintelConfig internally sets output:'standalone' and wraps with withSentryConfig.
|
||||
// In dev mode, standalone triggers @vercel/nft file tracing on every compile through
|
||||
// VirtioFS, which hammers CPU. Strip it for dev.
|
||||
let resolvedConfig = withPayload(withAnalyzer(withMintelConfig(nextConfig)));
|
||||
const withMDX = createMDX({});
|
||||
let resolvedConfig = withMDX(withAnalyzer(withMintelConfig(nextConfig)));
|
||||
|
||||
if (!isProd) {
|
||||
delete resolvedConfig.output;
|
||||
|
||||
2
node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json
generated
vendored
2
node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json
generated
vendored
@@ -1 +1 @@
|
||||
{"version":"4.1.2","results":[[":tests/navigation.e2e.test.ts",{"duration":12274.499042,"failed":false}]]}
|
||||
{"version":"4.1.2","results":[[":tests/navigation.e2e.test.ts",{"duration":23819.145417,"failed":false}]]}
|
||||
24
package.json
24
package.json
@@ -8,11 +8,7 @@
|
||||
"@mintel/next-config": "1.9.5",
|
||||
"@mintel/next-feedback": "1.9.5",
|
||||
"@mintel/next-utils": "1.9.5",
|
||||
"@payloadcms/db-postgres": "^3.77.0",
|
||||
"@payloadcms/email-nodemailer": "^3.77.0",
|
||||
"@payloadcms/next": "^3.77.0",
|
||||
"@payloadcms/richtext-lexical": "^3.77.0",
|
||||
"@payloadcms/ui": "^3.77.0",
|
||||
"@next/mdx": "^16.1.6",
|
||||
"@react-email/components": "1.0.8",
|
||||
"@react-pdf/renderer": "^4.3.2",
|
||||
"@react-three/drei": "^10.7.7",
|
||||
@@ -31,9 +27,8 @@
|
||||
"leaflet": "^1.9.4",
|
||||
"next": "16.1.6",
|
||||
"next-i18next": "^15.4.3",
|
||||
"next-mdx-remote": "^5.0.0",
|
||||
"next-intl": "^4.8.2",
|
||||
"nodemailer": "^7.0.12",
|
||||
"payload": "^3.77.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pino": "^10.3.0",
|
||||
"pino-pretty": "^13.1.3",
|
||||
@@ -101,9 +96,9 @@
|
||||
"xlsx-cli": "^1.1.3"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "bash -c '[ -f .env ] || (cp .env.example .env && sed -i.bak \"s/TRAEFIK_HOST=e-tib.com/TRAEFIK_HOST=etib.localhost/\" .env && rm -f .env.bak && echo \"✅ Created .env from .env.example\"); trap \"COMPOSE_PROJECT_NAME=etib-website docker-compose -f docker-compose.dev.yml down\" EXIT INT TERM; docker network create infra 2>/dev/null || true && COMPOSE_PROJECT_NAME=etib-website docker-compose -f docker-compose.dev.yml down && COMPOSE_PROJECT_NAME=etib-website docker-compose -f docker-compose.dev.yml up etib-app etib-db etib-proxy etib-redis --remove-orphans'",
|
||||
"dev:local": "bash -c 'trap \"COMPOSE_PROJECT_NAME=etib-website docker-compose -f docker-compose.dev.yml down\" EXIT INT TERM; COMPOSE_PROJECT_NAME=etib-website docker-compose -f docker-compose.dev.yml up -d etib-db etib-proxy etib-redis && POSTGRES_URI=postgres://payload:120in09oenaoinsd9iaidon@localhost:54322/payload NODE_ENV=development next dev --webpack --port 3100 --hostname 0.0.0.0'",
|
||||
"dev:infra": "COMPOSE_PROJECT_NAME=etib-website docker-compose -f docker-compose.dev.yml up -d etib-db etib-proxy",
|
||||
"dev": "bash -c '[ -f .env ] || (cp .env.example .env && sed -i.bak \"s/TRAEFIK_HOST=e-tib.com/TRAEFIK_HOST=etib.localhost/\" .env && rm -f .env.bak && echo \"✅ Created .env from .env.example\"); trap \"COMPOSE_PROJECT_NAME=etib-website docker-compose -f docker-compose.dev.yml down\" EXIT INT TERM; docker network create infra 2>/dev/null || true && COMPOSE_PROJECT_NAME=etib-website docker-compose -f docker-compose.dev.yml down && COMPOSE_PROJECT_NAME=etib-website docker-compose -f docker-compose.dev.yml up etib-app etib-proxy --remove-orphans'",
|
||||
"dev:local": "next dev --webpack --port 3100 --hostname 0.0.0.0",
|
||||
"dev:infra": "COMPOSE_PROJECT_NAME=etib-website docker-compose -f docker-compose.dev.yml up -d etib-proxy",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint .",
|
||||
@@ -128,9 +123,7 @@
|
||||
"pdf:datasheets:legacy": "tsx ./scripts/generate-pdf-datasheets-pdf-lib.ts",
|
||||
"excel:datasheets": "tsx ./scripts/generate-excel-datasheets.ts",
|
||||
"brochure:generate": "tsx ./scripts/generate-brochure.ts",
|
||||
"cms:migrate": "tsx ./node_modules/payload/bin.js migrate",
|
||||
"cms:seed": "tsx ./scripts/seed-payload.ts",
|
||||
"assets:push:testing": "bash ./scripts/assets-sync.sh local testing",
|
||||
"brochure:generate": "tsx ./scripts/generate-brochure.ts",
|
||||
"assets:push:staging": "bash ./scripts/assets-sync.sh local staging",
|
||||
"assets:push:prod": "bash ./scripts/assets-sync.sh local prod",
|
||||
"assets:pull:testing": "bash ./scripts/assets-sync.sh testing local",
|
||||
@@ -143,11 +136,6 @@
|
||||
"pagespeed:urls": "tsx -e \"import sitemap from './app/sitemap'; sitemap().then(urls => console.log(urls.map(u => u.url).join('\\n')))\"",
|
||||
"backup:db": "bash ./scripts/backup-db.sh",
|
||||
"restore:db": "bash ./scripts/restore-db.sh",
|
||||
"cms:push:testing": "bash ./scripts/cms-sync.sh push testing",
|
||||
"cms:push:staging": "bash ./scripts/cms-sync.sh push staging",
|
||||
"cms:push:prod": "bash ./scripts/cms-sync.sh push prod",
|
||||
"cms:pull:testing": "bash ./scripts/cms-sync.sh pull testing",
|
||||
"cms:pull:staging": "bash ./scripts/cms-sync.sh pull staging",
|
||||
"cms:pull:prod": "bash ./scripts/cms-sync.sh pull prod",
|
||||
"prepare": "husky",
|
||||
"preinstall": "npx only-allow pnpm"
|
||||
|
||||
1228
payload-types.ts
1228
payload-types.ts
File diff suppressed because it is too large
Load Diff
@@ -1,107 +0,0 @@
|
||||
import { buildConfig } from 'payload';
|
||||
import { postgresAdapter } from '@payloadcms/db-postgres';
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical';
|
||||
import sharp from 'sharp';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { nodemailerAdapter } from '@payloadcms/email-nodemailer';
|
||||
import { BlocksFeature } from '@payloadcms/richtext-lexical';
|
||||
import { payloadBlocks } from './src/payload/blocks/allBlocks';
|
||||
import { migrations } from './src/migrations';
|
||||
|
||||
// Only disable sharp cache in production to prevent memory leaks.
|
||||
// In dev, the cache avoids 41s+ re-processing per image through VirtioFS.
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
sharp.cache(false);
|
||||
}
|
||||
|
||||
import { Users } from './src/payload/collections/Users';
|
||||
import { Media } from './src/payload/collections/Media';
|
||||
import { Posts } from './src/payload/collections/Posts';
|
||||
import { FormSubmissions } from './src/payload/collections/FormSubmissions';
|
||||
import { Pages } from './src/payload/collections/Pages';
|
||||
import { Competences } from './src/payload/collections/Competences';
|
||||
import { References } from './src/payload/collections/References';
|
||||
import { Team } from './src/payload/collections/Team';
|
||||
import { Jobs } from './src/payload/collections/Jobs';
|
||||
import { Settings } from './src/payload/globals/Settings';
|
||||
import { seedDatabase } from './src/payload/seed';
|
||||
|
||||
|
||||
|
||||
const filename = fileURLToPath(import.meta.url);
|
||||
const dirname = path.dirname(filename);
|
||||
|
||||
export default buildConfig({
|
||||
onInit: async (payload) => {
|
||||
// seedDatabase is removed from onInit to avoid running in every Next.js build worker
|
||||
},
|
||||
admin: {
|
||||
user: Users.slug,
|
||||
importMap: {
|
||||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
components: {
|
||||
graphics: {
|
||||
Logo: '/src/payload/components/Logo',
|
||||
Icon: '/src/payload/components/Icon',
|
||||
},
|
||||
},
|
||||
meta: {
|
||||
titleSuffix: ' – E-TIB',
|
||||
icons: [{ rel: 'icon', type: 'image/x-icon', url: '/favicon.ico' }],
|
||||
},
|
||||
},
|
||||
localization: {
|
||||
locales: [
|
||||
{ label: 'Deutsch', code: 'de' },
|
||||
{ label: 'English', code: 'en' },
|
||||
],
|
||||
defaultLocale: 'de',
|
||||
fallback: true,
|
||||
},
|
||||
collections: [Users, Media, Posts, FormSubmissions, Pages, Competences, References, Team, Jobs],
|
||||
globals: [Settings],
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
BlocksFeature({
|
||||
blocks: payloadBlocks,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
secret: process.env.PAYLOAD_SECRET || 'etib-restoration-secret',
|
||||
typescript: {
|
||||
outputFile: path.resolve(dirname, 'payload-types.ts'),
|
||||
},
|
||||
db: postgresAdapter({
|
||||
migrationDir: null as any,
|
||||
pool: {
|
||||
connectionString:
|
||||
process.env.DATABASE_URI ||
|
||||
process.env.POSTGRES_URI ||
|
||||
`postgresql://${process.env.PAYLOAD_DB_USER || 'payload'}:${process.env.PAYLOAD_DB_PASSWORD || '120in09oenaoinsd9iaidon'}@127.0.0.1:54322/${process.env.PAYLOAD_DB_NAME || 'payload'}`,
|
||||
},
|
||||
push: false,
|
||||
}),
|
||||
email: process.env.MAIL_HOST
|
||||
? nodemailerAdapter({
|
||||
defaultFromAddress:
|
||||
process.env.MAIL_FROM?.replace(/.*<|>.*/g, '') || 'postmaster@mg.mintel.me',
|
||||
defaultFromName: process.env.MAIL_FROM?.split('<')[0]?.trim() || 'E-TIB',
|
||||
transportOptions: {
|
||||
host: process.env.MAIL_HOST || 'smtp.eu.mailgun.org',
|
||||
port: Number(process.env.MAIL_PORT) || 587,
|
||||
...(process.env.MAIL_USERNAME
|
||||
? {
|
||||
auth: {
|
||||
user: process.env.MAIL_USERNAME,
|
||||
pass: process.env.MAIL_PASSWORD,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
})
|
||||
: undefined,
|
||||
sharp,
|
||||
});
|
||||
2820
pnpm-lock.yaml
generated
2820
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
110
proxy.ts
110
proxy.ts
@@ -1,110 +1,16 @@
|
||||
import createMiddleware from 'next-intl/middleware';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
// Create the internationalization middleware
|
||||
const intlMiddleware = createMiddleware({
|
||||
// A list of all locales that are supported
|
||||
export default createMiddleware({
|
||||
locales: ['en', 'de'],
|
||||
|
||||
// Used when no locale matches
|
||||
defaultLocale: 'de',
|
||||
localePrefix: 'always'
|
||||
});
|
||||
|
||||
export default async function middleware(request: NextRequest) {
|
||||
const { method, url, headers } = request;
|
||||
const { pathname } = request.nextUrl;
|
||||
|
||||
// Explicit bypass for infrastructure, assets, and Payload CMS routes
|
||||
// to avoid locale redirects/interception of static files.
|
||||
if (
|
||||
pathname.startsWith('/admin') ||
|
||||
pathname.startsWith('/api') ||
|
||||
pathname.startsWith('/assets') ||
|
||||
pathname.startsWith('/stats') ||
|
||||
pathname.startsWith('/errors') ||
|
||||
pathname.startsWith('/health') ||
|
||||
pathname.startsWith('/uploads') ||
|
||||
pathname.includes('/api/og') ||
|
||||
pathname.includes('opengraph-image') ||
|
||||
pathname.includes('favicon.ico') ||
|
||||
pathname.endsWith('sitemap.xml') ||
|
||||
pathname.endsWith('manifest.webmanifest')
|
||||
) {
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
// Build header object for logging
|
||||
const headerObj: Record<string, string> = {};
|
||||
headers.forEach((value, key) => {
|
||||
headerObj[key] = value;
|
||||
});
|
||||
|
||||
// Defensive URL correction for internal container leakage (0.0.0.0, etib-app, localhost)
|
||||
// This prevents hydration mismatches and host poisoning in generated links/metadata.
|
||||
const urlObj = new URL(url);
|
||||
const internalHosts = ['0.0.0.0', 'etib-app', 'localhost', '127.0.0.1'];
|
||||
|
||||
let effectiveRequest = request;
|
||||
if (internalHosts.includes(urlObj.hostname)) {
|
||||
const proto = headers.get('x-forwarded-proto') || 'https';
|
||||
// Prioritize x-forwarded-host (passed by Traefik) over the local Host header
|
||||
const fallbackHost = process.env.NEXT_PUBLIC_BASE_URL
|
||||
? new URL(process.env.NEXT_PUBLIC_BASE_URL).host
|
||||
: 'e-tib.com';
|
||||
const hostHeader = headers.get('x-forwarded-host') || headers.get('host') || fallbackHost;
|
||||
|
||||
urlObj.protocol = proto;
|
||||
|
||||
effectiveRequest = new NextRequest(urlObj, {
|
||||
headers: request.headers,
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== 'production' || !process.env.CI) {
|
||||
console.log(
|
||||
`🛡️ Proxy: Fixed internal URL leak: ${url} -> ${urlObj.toString()} | Proto: ${proto} | Host: ${hostHeader}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Apply internationalization middleware
|
||||
const response = intlMiddleware(request);
|
||||
console.log(`[Middleware] Resolved request to ${url} -> status: ${response.status}`);
|
||||
|
||||
// Upgrade 307 (Temporary Redirect) to 308 (Permanent Redirect)
|
||||
// This improves compatibility with scanners (Website Carbon, PageSpeed) and SEO.
|
||||
if (response.status === 307) {
|
||||
const location = response.headers.get('Location');
|
||||
if (location) {
|
||||
const url = new URL(location, request.url);
|
||||
return Response.redirect(url, 308);
|
||||
}
|
||||
}
|
||||
|
||||
// Allow iframe embedding from recorder domains
|
||||
const referer = headers.get('referer') || '';
|
||||
const recorderDomains = ['recorder.localhost', 'recorder.mintel.me'];
|
||||
const isRecorderRequest = recorderDomains.some((domain) => referer.includes(domain));
|
||||
|
||||
if (isRecorderRequest) {
|
||||
response.headers.delete('x-frame-options');
|
||||
response.headers.delete('content-security-policy');
|
||||
response.headers.set('Access-Control-Allow-Origin', '*');
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Request failed: method=${method} url=${url} headers=${JSON.stringify(headerObj)}`,
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
'/((?!api|_next/static|_next/image|favicon.ico|admin|manifest.webmanifest|.*\\.(?:svg|png|jpg|jpeg|gif|webp|pdf|xlsx|txt|vcf|xml|webm|mp4|map)$).*)',
|
||||
'/(de|en)/:path*',
|
||||
],
|
||||
// Match all pathnames except for
|
||||
// - /api (API routes)
|
||||
// - /_next (Next.js internals)
|
||||
// - /_static (inside /public)
|
||||
// - all root files inside /public (e.g. /favicon.ico)
|
||||
matcher: ['/((?!api|_next|assets|_static|_vercel|[\\w-]+\\.\\w+).*)']
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -1,22 +0,0 @@
|
||||
import Redis from 'ioredis';
|
||||
|
||||
const isDockerContainer =
|
||||
process.env.IS_DOCKER === 'true' || process.env.HOSTNAME?.includes('klz-app');
|
||||
const redisUrl =
|
||||
process.env.REDIS_URL ||
|
||||
(isDockerContainer ? 'redis://klz-redis:6379' : 'redis://localhost:6379');
|
||||
|
||||
// Only create a single instance in Node.js
|
||||
const globalForRedis = global as unknown as { redis: Redis };
|
||||
|
||||
export const redis =
|
||||
globalForRedis.redis ||
|
||||
new Redis(redisUrl, {
|
||||
maxRetriesPerRequest: 3,
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
globalForRedis.redis = redis;
|
||||
}
|
||||
|
||||
export default redis;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,356 +0,0 @@
|
||||
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres';
|
||||
|
||||
export async function up({ db }: MigrateUpArgs): Promise<void> {
|
||||
await db.execute(sql`
|
||||
CREATE TYPE "public"."enum_posts_locale" AS ENUM('en', 'de');
|
||||
CREATE TYPE "public"."enum_posts_status" AS ENUM('draft', 'published');
|
||||
CREATE TYPE "public"."enum__posts_v_version_locale" AS ENUM('en', 'de');
|
||||
CREATE TYPE "public"."enum__posts_v_version_status" AS ENUM('draft', 'published');
|
||||
CREATE TYPE "public"."enum_form_submissions_type" AS ENUM('contact', 'product_quote');
|
||||
CREATE TYPE "public"."enum_products_locale" AS ENUM('en', 'de');
|
||||
CREATE TYPE "public"."enum_products_status" AS ENUM('draft', 'published');
|
||||
CREATE TYPE "public"."enum__products_v_version_locale" AS ENUM('en', 'de');
|
||||
CREATE TYPE "public"."enum__products_v_version_status" AS ENUM('draft', 'published');
|
||||
CREATE TABLE "users_sessions" (
|
||||
"_order" integer NOT NULL,
|
||||
"_parent_id" integer NOT NULL,
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"created_at" timestamp(3) with time zone,
|
||||
"expires_at" timestamp(3) with time zone NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "users" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"email" varchar NOT NULL,
|
||||
"reset_password_token" varchar,
|
||||
"reset_password_expiration" timestamp(3) with time zone,
|
||||
"salt" varchar,
|
||||
"hash" varchar,
|
||||
"login_attempts" numeric DEFAULT 0,
|
||||
"lock_until" timestamp(3) with time zone
|
||||
);
|
||||
|
||||
CREATE TABLE "media" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"alt" varchar NOT NULL,
|
||||
"caption" varchar,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"url" varchar,
|
||||
"thumbnail_u_r_l" varchar,
|
||||
"filename" varchar,
|
||||
"mime_type" varchar,
|
||||
"filesize" numeric,
|
||||
"width" numeric,
|
||||
"height" numeric,
|
||||
"focal_x" numeric,
|
||||
"focal_y" numeric,
|
||||
"sizes_thumbnail_url" varchar,
|
||||
"sizes_thumbnail_width" numeric,
|
||||
"sizes_thumbnail_height" numeric,
|
||||
"sizes_thumbnail_mime_type" varchar,
|
||||
"sizes_thumbnail_filesize" numeric,
|
||||
"sizes_thumbnail_filename" varchar,
|
||||
"sizes_card_url" varchar,
|
||||
"sizes_card_width" numeric,
|
||||
"sizes_card_height" numeric,
|
||||
"sizes_card_mime_type" varchar,
|
||||
"sizes_card_filesize" numeric,
|
||||
"sizes_card_filename" varchar,
|
||||
"sizes_hero_url" varchar,
|
||||
"sizes_hero_width" numeric,
|
||||
"sizes_hero_height" numeric,
|
||||
"sizes_hero_mime_type" varchar,
|
||||
"sizes_hero_filesize" numeric,
|
||||
"sizes_hero_filename" varchar,
|
||||
"sizes_hero_mobile_url" varchar,
|
||||
"sizes_hero_mobile_width" numeric,
|
||||
"sizes_hero_mobile_height" numeric,
|
||||
"sizes_hero_mobile_mime_type" varchar,
|
||||
"sizes_hero_mobile_filesize" numeric,
|
||||
"sizes_hero_mobile_filename" varchar
|
||||
);
|
||||
|
||||
CREATE TABLE "posts" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"title" varchar,
|
||||
"slug" varchar,
|
||||
"excerpt" varchar,
|
||||
"date" timestamp(3) with time zone,
|
||||
"featured_image_id" integer,
|
||||
"locale" "enum_posts_locale" DEFAULT 'en',
|
||||
"category" varchar,
|
||||
"content" jsonb,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"_status" "enum_posts_status" DEFAULT 'draft'
|
||||
);
|
||||
|
||||
CREATE TABLE "_posts_v" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"parent_id" integer,
|
||||
"version_title" varchar,
|
||||
"version_slug" varchar,
|
||||
"version_excerpt" varchar,
|
||||
"version_date" timestamp(3) with time zone,
|
||||
"version_featured_image_id" integer,
|
||||
"version_locale" "enum__posts_v_version_locale" DEFAULT 'en',
|
||||
"version_category" varchar,
|
||||
"version_content" jsonb,
|
||||
"version_updated_at" timestamp(3) with time zone,
|
||||
"version_created_at" timestamp(3) with time zone,
|
||||
"version__status" "enum__posts_v_version_status" DEFAULT 'draft',
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"latest" boolean
|
||||
);
|
||||
|
||||
CREATE TABLE "form_submissions" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"name" varchar NOT NULL,
|
||||
"email" varchar NOT NULL,
|
||||
"type" "enum_form_submissions_type" NOT NULL,
|
||||
"product_name" varchar,
|
||||
"message" varchar NOT NULL,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "products_categories" (
|
||||
"_order" integer NOT NULL,
|
||||
"_parent_id" integer NOT NULL,
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"category" varchar
|
||||
);
|
||||
|
||||
CREATE TABLE "products" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"title" varchar,
|
||||
"sku" varchar,
|
||||
"slug" varchar,
|
||||
"description" varchar,
|
||||
"locale" "enum_products_locale" DEFAULT 'de',
|
||||
"application" jsonb,
|
||||
"content" jsonb,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"_status" "enum_products_status" DEFAULT 'draft'
|
||||
);
|
||||
|
||||
CREATE TABLE "products_rels" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"order" integer,
|
||||
"parent_id" integer NOT NULL,
|
||||
"path" varchar NOT NULL,
|
||||
"media_id" integer
|
||||
);
|
||||
|
||||
CREATE TABLE "_products_v_version_categories" (
|
||||
"_order" integer NOT NULL,
|
||||
"_parent_id" integer NOT NULL,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"category" varchar,
|
||||
"_uuid" varchar
|
||||
);
|
||||
|
||||
CREATE TABLE "_products_v" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"parent_id" integer,
|
||||
"version_title" varchar,
|
||||
"version_sku" varchar,
|
||||
"version_slug" varchar,
|
||||
"version_description" varchar,
|
||||
"version_locale" "enum__products_v_version_locale" DEFAULT 'de',
|
||||
"version_application" jsonb,
|
||||
"version_content" jsonb,
|
||||
"version_updated_at" timestamp(3) with time zone,
|
||||
"version_created_at" timestamp(3) with time zone,
|
||||
"version__status" "enum__products_v_version_status" DEFAULT 'draft',
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"latest" boolean
|
||||
);
|
||||
|
||||
CREATE TABLE "_products_v_rels" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"order" integer,
|
||||
"parent_id" integer NOT NULL,
|
||||
"path" varchar NOT NULL,
|
||||
"media_id" integer
|
||||
);
|
||||
|
||||
CREATE TABLE "payload_kv" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"key" varchar NOT NULL,
|
||||
"data" jsonb NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "payload_locked_documents" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"global_slug" varchar,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "payload_locked_documents_rels" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"order" integer,
|
||||
"parent_id" integer NOT NULL,
|
||||
"path" varchar NOT NULL,
|
||||
"users_id" integer,
|
||||
"media_id" integer,
|
||||
"posts_id" integer,
|
||||
"form_submissions_id" integer,
|
||||
"products_id" integer
|
||||
);
|
||||
|
||||
CREATE TABLE "payload_preferences" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"key" varchar,
|
||||
"value" jsonb,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "payload_preferences_rels" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"order" integer,
|
||||
"parent_id" integer NOT NULL,
|
||||
"path" varchar NOT NULL,
|
||||
"users_id" integer
|
||||
);
|
||||
|
||||
CREATE TABLE "payload_migrations" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"name" varchar,
|
||||
"batch" numeric,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE "users_sessions" ADD CONSTRAINT "users_sessions_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "posts" ADD CONSTRAINT "posts_featured_image_id_media_id_fk" FOREIGN KEY ("featured_image_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
|
||||
ALTER TABLE "_posts_v" ADD CONSTRAINT "_posts_v_parent_id_posts_id_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."posts"("id") ON DELETE set null ON UPDATE no action;
|
||||
ALTER TABLE "_posts_v" ADD CONSTRAINT "_posts_v_version_featured_image_id_media_id_fk" FOREIGN KEY ("version_featured_image_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
|
||||
ALTER TABLE "products_categories" ADD CONSTRAINT "products_categories_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."products"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "products_rels" ADD CONSTRAINT "products_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."products"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "products_rels" ADD CONSTRAINT "products_rels_media_fk" FOREIGN KEY ("media_id") REFERENCES "public"."media"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "_products_v_version_categories" ADD CONSTRAINT "_products_v_version_categories_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_products_v"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "_products_v" ADD CONSTRAINT "_products_v_parent_id_products_id_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."products"("id") ON DELETE set null ON UPDATE no action;
|
||||
ALTER TABLE "_products_v_rels" ADD CONSTRAINT "_products_v_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."_products_v"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "_products_v_rels" ADD CONSTRAINT "_products_v_rels_media_fk" FOREIGN KEY ("media_id") REFERENCES "public"."media"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."payload_locked_documents"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_media_fk" FOREIGN KEY ("media_id") REFERENCES "public"."media"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_posts_fk" FOREIGN KEY ("posts_id") REFERENCES "public"."posts"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_form_submissions_fk" FOREIGN KEY ("form_submissions_id") REFERENCES "public"."form_submissions"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_products_fk" FOREIGN KEY ("products_id") REFERENCES "public"."products"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."payload_preferences"("id") ON DELETE cascade ON UPDATE no action;
|
||||
ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
||||
CREATE INDEX "users_sessions_order_idx" ON "users_sessions" USING btree ("_order");
|
||||
CREATE INDEX "users_sessions_parent_id_idx" ON "users_sessions" USING btree ("_parent_id");
|
||||
CREATE INDEX "users_updated_at_idx" ON "users" USING btree ("updated_at");
|
||||
CREATE INDEX "users_created_at_idx" ON "users" USING btree ("created_at");
|
||||
CREATE UNIQUE INDEX "users_email_idx" ON "users" USING btree ("email");
|
||||
CREATE INDEX "media_updated_at_idx" ON "media" USING btree ("updated_at");
|
||||
CREATE INDEX "media_created_at_idx" ON "media" USING btree ("created_at");
|
||||
CREATE UNIQUE INDEX "media_filename_idx" ON "media" USING btree ("filename");
|
||||
CREATE INDEX "media_sizes_thumbnail_sizes_thumbnail_filename_idx" ON "media" USING btree ("sizes_thumbnail_filename");
|
||||
CREATE INDEX "media_sizes_card_sizes_card_filename_idx" ON "media" USING btree ("sizes_card_filename");
|
||||
CREATE INDEX "media_sizes_hero_sizes_hero_filename_idx" ON "media" USING btree ("sizes_hero_filename");
|
||||
CREATE INDEX "media_sizes_hero_mobile_sizes_hero_mobile_filename_idx" ON "media" USING btree ("sizes_hero_mobile_filename");
|
||||
CREATE UNIQUE INDEX "posts_slug_idx" ON "posts" USING btree ("slug");
|
||||
CREATE INDEX "posts_featured_image_idx" ON "posts" USING btree ("featured_image_id");
|
||||
CREATE INDEX "posts_updated_at_idx" ON "posts" USING btree ("updated_at");
|
||||
CREATE INDEX "posts_created_at_idx" ON "posts" USING btree ("created_at");
|
||||
CREATE INDEX "posts__status_idx" ON "posts" USING btree ("_status");
|
||||
CREATE INDEX "_posts_v_parent_idx" ON "_posts_v" USING btree ("parent_id");
|
||||
CREATE INDEX "_posts_v_version_version_slug_idx" ON "_posts_v" USING btree ("version_slug");
|
||||
CREATE INDEX "_posts_v_version_version_featured_image_idx" ON "_posts_v" USING btree ("version_featured_image_id");
|
||||
CREATE INDEX "_posts_v_version_version_updated_at_idx" ON "_posts_v" USING btree ("version_updated_at");
|
||||
CREATE INDEX "_posts_v_version_version_created_at_idx" ON "_posts_v" USING btree ("version_created_at");
|
||||
CREATE INDEX "_posts_v_version_version__status_idx" ON "_posts_v" USING btree ("version__status");
|
||||
CREATE INDEX "_posts_v_created_at_idx" ON "_posts_v" USING btree ("created_at");
|
||||
CREATE INDEX "_posts_v_updated_at_idx" ON "_posts_v" USING btree ("updated_at");
|
||||
CREATE INDEX "_posts_v_latest_idx" ON "_posts_v" USING btree ("latest");
|
||||
CREATE INDEX "form_submissions_updated_at_idx" ON "form_submissions" USING btree ("updated_at");
|
||||
CREATE INDEX "form_submissions_created_at_idx" ON "form_submissions" USING btree ("created_at");
|
||||
CREATE INDEX "products_categories_order_idx" ON "products_categories" USING btree ("_order");
|
||||
CREATE INDEX "products_categories_parent_id_idx" ON "products_categories" USING btree ("_parent_id");
|
||||
CREATE UNIQUE INDEX "products_sku_idx" ON "products" USING btree ("sku");
|
||||
CREATE INDEX "products_updated_at_idx" ON "products" USING btree ("updated_at");
|
||||
CREATE INDEX "products_created_at_idx" ON "products" USING btree ("created_at");
|
||||
CREATE INDEX "products__status_idx" ON "products" USING btree ("_status");
|
||||
CREATE INDEX "products_rels_order_idx" ON "products_rels" USING btree ("order");
|
||||
CREATE INDEX "products_rels_parent_idx" ON "products_rels" USING btree ("parent_id");
|
||||
CREATE INDEX "products_rels_path_idx" ON "products_rels" USING btree ("path");
|
||||
CREATE INDEX "products_rels_media_id_idx" ON "products_rels" USING btree ("media_id");
|
||||
CREATE INDEX "_products_v_version_categories_order_idx" ON "_products_v_version_categories" USING btree ("_order");
|
||||
CREATE INDEX "_products_v_version_categories_parent_id_idx" ON "_products_v_version_categories" USING btree ("_parent_id");
|
||||
CREATE INDEX "_products_v_parent_idx" ON "_products_v" USING btree ("parent_id");
|
||||
CREATE INDEX "_products_v_version_version_sku_idx" ON "_products_v" USING btree ("version_sku");
|
||||
CREATE INDEX "_products_v_version_version_updated_at_idx" ON "_products_v" USING btree ("version_updated_at");
|
||||
CREATE INDEX "_products_v_version_version_created_at_idx" ON "_products_v" USING btree ("version_created_at");
|
||||
CREATE INDEX "_products_v_version_version__status_idx" ON "_products_v" USING btree ("version__status");
|
||||
CREATE INDEX "_products_v_created_at_idx" ON "_products_v" USING btree ("created_at");
|
||||
CREATE INDEX "_products_v_updated_at_idx" ON "_products_v" USING btree ("updated_at");
|
||||
CREATE INDEX "_products_v_latest_idx" ON "_products_v" USING btree ("latest");
|
||||
CREATE INDEX "_products_v_rels_order_idx" ON "_products_v_rels" USING btree ("order");
|
||||
CREATE INDEX "_products_v_rels_parent_idx" ON "_products_v_rels" USING btree ("parent_id");
|
||||
CREATE INDEX "_products_v_rels_path_idx" ON "_products_v_rels" USING btree ("path");
|
||||
CREATE INDEX "_products_v_rels_media_id_idx" ON "_products_v_rels" USING btree ("media_id");
|
||||
CREATE UNIQUE INDEX "payload_kv_key_idx" ON "payload_kv" USING btree ("key");
|
||||
CREATE INDEX "payload_locked_documents_global_slug_idx" ON "payload_locked_documents" USING btree ("global_slug");
|
||||
CREATE INDEX "payload_locked_documents_updated_at_idx" ON "payload_locked_documents" USING btree ("updated_at");
|
||||
CREATE INDEX "payload_locked_documents_created_at_idx" ON "payload_locked_documents" USING btree ("created_at");
|
||||
CREATE INDEX "payload_locked_documents_rels_order_idx" ON "payload_locked_documents_rels" USING btree ("order");
|
||||
CREATE INDEX "payload_locked_documents_rels_parent_idx" ON "payload_locked_documents_rels" USING btree ("parent_id");
|
||||
CREATE INDEX "payload_locked_documents_rels_path_idx" ON "payload_locked_documents_rels" USING btree ("path");
|
||||
CREATE INDEX "payload_locked_documents_rels_users_id_idx" ON "payload_locked_documents_rels" USING btree ("users_id");
|
||||
CREATE INDEX "payload_locked_documents_rels_media_id_idx" ON "payload_locked_documents_rels" USING btree ("media_id");
|
||||
CREATE INDEX "payload_locked_documents_rels_posts_id_idx" ON "payload_locked_documents_rels" USING btree ("posts_id");
|
||||
CREATE INDEX "payload_locked_documents_rels_form_submissions_id_idx" ON "payload_locked_documents_rels" USING btree ("form_submissions_id");
|
||||
CREATE INDEX "payload_locked_documents_rels_products_id_idx" ON "payload_locked_documents_rels" USING btree ("products_id");
|
||||
CREATE INDEX "payload_preferences_key_idx" ON "payload_preferences" USING btree ("key");
|
||||
CREATE INDEX "payload_preferences_updated_at_idx" ON "payload_preferences" USING btree ("updated_at");
|
||||
CREATE INDEX "payload_preferences_created_at_idx" ON "payload_preferences" USING btree ("created_at");
|
||||
CREATE INDEX "payload_preferences_rels_order_idx" ON "payload_preferences_rels" USING btree ("order");
|
||||
CREATE INDEX "payload_preferences_rels_parent_idx" ON "payload_preferences_rels" USING btree ("parent_id");
|
||||
CREATE INDEX "payload_preferences_rels_path_idx" ON "payload_preferences_rels" USING btree ("path");
|
||||
CREATE INDEX "payload_preferences_rels_users_id_idx" ON "payload_preferences_rels" USING btree ("users_id");
|
||||
CREATE INDEX "payload_migrations_updated_at_idx" ON "payload_migrations" USING btree ("updated_at");
|
||||
CREATE INDEX "payload_migrations_created_at_idx" ON "payload_migrations" USING btree ("created_at");`);
|
||||
}
|
||||
|
||||
export async function down({ db }: MigrateDownArgs): Promise<void> {
|
||||
await db.execute(sql`
|
||||
DROP TABLE "users_sessions" CASCADE;
|
||||
DROP TABLE "users" CASCADE;
|
||||
DROP TABLE "media" CASCADE;
|
||||
DROP TABLE "posts" CASCADE;
|
||||
DROP TABLE "_posts_v" CASCADE;
|
||||
DROP TABLE "form_submissions" CASCADE;
|
||||
DROP TABLE "products_categories" CASCADE;
|
||||
DROP TABLE "products" CASCADE;
|
||||
DROP TABLE "products_rels" CASCADE;
|
||||
DROP TABLE "_products_v_version_categories" CASCADE;
|
||||
DROP TABLE "_products_v" CASCADE;
|
||||
DROP TABLE "_products_v_rels" CASCADE;
|
||||
DROP TABLE "payload_kv" CASCADE;
|
||||
DROP TABLE "payload_locked_documents" CASCADE;
|
||||
DROP TABLE "payload_locked_documents_rels" CASCADE;
|
||||
DROP TABLE "payload_preferences" CASCADE;
|
||||
DROP TABLE "payload_preferences_rels" CASCADE;
|
||||
DROP TABLE "payload_migrations" CASCADE;
|
||||
DROP TYPE "public"."enum_posts_locale";
|
||||
DROP TYPE "public"."enum_posts_status";
|
||||
DROP TYPE "public"."enum__posts_v_version_locale";
|
||||
DROP TYPE "public"."enum__posts_v_version_status";
|
||||
DROP TYPE "public"."enum_form_submissions_type";
|
||||
DROP TYPE "public"."enum_products_locale";
|
||||
DROP TYPE "public"."enum_products_status";
|
||||
DROP TYPE "public"."enum__products_v_version_locale";
|
||||
DROP TYPE "public"."enum__products_v_version_status";`);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres';
|
||||
|
||||
export async function up({ db }: MigrateUpArgs): Promise<void> {
|
||||
await db.execute(sql`
|
||||
DROP INDEX "products_sku_idx";
|
||||
DROP INDEX "_products_v_version_version_sku_idx";`);
|
||||
}
|
||||
|
||||
export async function down({ db }: MigrateDownArgs): Promise<void> {
|
||||
await db.execute(sql`
|
||||
CREATE UNIQUE INDEX "products_sku_idx" ON "products" USING btree ("sku");
|
||||
CREATE INDEX "_products_v_version_version_sku_idx" ON "_products_v" USING btree ("version_sku");`);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"id": "20260225_003500_add_pages_collection",
|
||||
"name": "20260225_003500_add_pages_collection",
|
||||
"batch": 3
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres';
|
||||
|
||||
export async function up({ db }: MigrateUpArgs): Promise<void> {
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."enum_pages_locale" AS ENUM('en', 'de');
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "pages" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"title" varchar NOT NULL,
|
||||
"slug" varchar NOT NULL,
|
||||
"locale" "enum_pages_locale" NOT NULL,
|
||||
"excerpt" varchar,
|
||||
"featured_image_id" integer,
|
||||
"content" jsonb NOT NULL,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE "pages" ADD CONSTRAINT "pages_featured_image_id_media_id_fk"
|
||||
FOREIGN KEY ("featured_image_id") REFERENCES "public"."media"("id")
|
||||
ON DELETE set null ON UPDATE no action;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "pages_featured_image_idx" ON "pages" USING btree ("featured_image_id");
|
||||
CREATE INDEX IF NOT EXISTS "pages_updated_at_idx" ON "pages" USING btree ("updated_at");
|
||||
CREATE INDEX IF NOT EXISTS "pages_created_at_idx" ON "pages" USING btree ("created_at");
|
||||
|
||||
-- Add pages_id to payload_locked_documents_rels if not already present
|
||||
ALTER TABLE "payload_locked_documents_rels" ADD COLUMN IF NOT EXISTS "pages_id" integer;
|
||||
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_pages_fk"
|
||||
FOREIGN KEY ("pages_id") REFERENCES "public"."pages"("id")
|
||||
ON DELETE cascade ON UPDATE no action;
|
||||
CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_pages_id_idx"
|
||||
ON "payload_locked_documents_rels" USING btree ("pages_id");
|
||||
`);
|
||||
}
|
||||
|
||||
export async function down({ db }: MigrateDownArgs): Promise<void> {
|
||||
await db.execute(sql`
|
||||
ALTER TABLE "payload_locked_documents_rels" DROP CONSTRAINT IF EXISTS "payload_locked_documents_rels_pages_fk";
|
||||
ALTER TABLE "payload_locked_documents_rels" DROP COLUMN IF EXISTS "pages_id";
|
||||
DROP TABLE IF EXISTS "pages" CASCADE;
|
||||
DROP TYPE IF EXISTS "public"."enum_pages_locale";
|
||||
`);
|
||||
}
|
||||
@@ -1,358 +0,0 @@
|
||||
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres';
|
||||
|
||||
/**
|
||||
* Migration: native_localization
|
||||
*
|
||||
* Transforms the existing schema (manual `locale` select column on each row)
|
||||
* into Payload's native localization join-table structure.
|
||||
*
|
||||
* Each statement is a separate db.execute call to avoid Drizzle multi-statement issues.
|
||||
*/
|
||||
export async function up({ db }: MigrateUpArgs): Promise<void> {
|
||||
// ── 1. Global locale enum ────────────────────────────────────────────────────
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."enum__locales" AS ENUM('de', 'en');
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."enum__posts_v_published_locale" AS ENUM('de', 'en');
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."enum__products_v_published_locale" AS ENUM('de', 'en');
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."enum__pages_v_published_locale" AS ENUM('de', 'en');
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."enum_pages_layout" AS ENUM('default', 'fullBleed');
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."enum__pages_v_version_layout" AS ENUM('default', 'fullBleed');
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."enum__pages_v_version_status" AS ENUM('draft', 'published');
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."enum__posts_v_version_status" AS ENUM('draft', 'published');
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."enum__products_v_version_status" AS ENUM('draft', 'published');
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."enum_pages_status" AS ENUM('draft', 'published');
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."enum_posts_status" AS ENUM('draft', 'published');
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."enum_products_status" AS ENUM('draft', 'published');
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
|
||||
// ── 2. Alter pages table ─────────────────────────────────────────────────────
|
||||
await db.execute(
|
||||
sql`ALTER TABLE "pages" ADD COLUMN IF NOT EXISTS "layout" "enum_pages_layout" DEFAULT 'default'`,
|
||||
);
|
||||
await db.execute(
|
||||
sql`ALTER TABLE "pages" ADD COLUMN IF NOT EXISTS "_status" "enum_pages_status" DEFAULT 'draft'`,
|
||||
);
|
||||
|
||||
// ── 3. Create pages_locales join table ───────────────────────────────────────
|
||||
await db.execute(sql`
|
||||
CREATE TABLE IF NOT EXISTS "pages_locales" (
|
||||
"title" varchar,
|
||||
"slug" varchar,
|
||||
"excerpt" varchar,
|
||||
"content" jsonb,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "enum__locales" NOT NULL,
|
||||
"_parent_id" integer NOT NULL
|
||||
)
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "pages_locales" ADD CONSTRAINT "pages_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id");
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "pages_locales" ADD CONSTRAINT "pages_locales_parent_id_fk"
|
||||
FOREIGN KEY ("_parent_id") REFERENCES "pages"("id") ON DELETE cascade;
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
|
||||
// ── 4. Backfill pages_locales from old pages rows ────────────────────────────
|
||||
await db.execute(sql`
|
||||
INSERT INTO "pages_locales" ("title", "slug", "excerpt", "content", "_locale", "_parent_id")
|
||||
SELECT
|
||||
p.title, p.slug, p.excerpt, p.content,
|
||||
CASE WHEN p.locale::text = 'de' THEN 'de'::"enum__locales" ELSE 'en'::"enum__locales" END,
|
||||
p.id
|
||||
FROM "pages" p
|
||||
WHERE p.locale IS NOT NULL
|
||||
ON CONFLICT ("_locale", "_parent_id") DO UPDATE
|
||||
SET "title" = EXCLUDED."title",
|
||||
"slug" = EXCLUDED."slug",
|
||||
"excerpt" = EXCLUDED."excerpt",
|
||||
"content" = EXCLUDED."content"
|
||||
`);
|
||||
|
||||
// ── 5. Drop old columns from pages ───────────────────────────────────────────
|
||||
await db.execute(sql`ALTER TABLE "pages" DROP COLUMN IF EXISTS "title"`);
|
||||
await db.execute(sql`ALTER TABLE "pages" DROP COLUMN IF EXISTS "slug"`);
|
||||
await db.execute(sql`ALTER TABLE "pages" DROP COLUMN IF EXISTS "excerpt"`);
|
||||
await db.execute(sql`ALTER TABLE "pages" DROP COLUMN IF EXISTS "content"`);
|
||||
await db.execute(sql`ALTER TABLE "pages" DROP COLUMN IF EXISTS "locale"`);
|
||||
|
||||
// ── 6. Create posts_locales join table ───────────────────────────────────────
|
||||
await db.execute(sql`
|
||||
CREATE TABLE IF NOT EXISTS "posts_locales" (
|
||||
"title" varchar,
|
||||
"slug" varchar,
|
||||
"excerpt" varchar,
|
||||
"category" varchar,
|
||||
"content" jsonb,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "enum__locales" NOT NULL,
|
||||
"_parent_id" integer NOT NULL
|
||||
)
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "posts_locales" ADD CONSTRAINT "posts_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id");
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "posts_locales" ADD CONSTRAINT "posts_locales_parent_id_fk"
|
||||
FOREIGN KEY ("_parent_id") REFERENCES "posts"("id") ON DELETE cascade;
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
|
||||
// ── 7. Backfill posts_locales ────────────────────────────────────────────────
|
||||
await db.execute(sql`
|
||||
INSERT INTO "posts_locales" ("title", "slug", "excerpt", "category", "content", "_locale", "_parent_id")
|
||||
SELECT
|
||||
p.title, p.slug, p.excerpt, p.category, p.content,
|
||||
CASE WHEN p.locale::text = 'de' THEN 'de'::"enum__locales" ELSE 'en'::"enum__locales" END,
|
||||
p.id
|
||||
FROM "posts" p
|
||||
WHERE p.locale IS NOT NULL
|
||||
ON CONFLICT ("_locale", "_parent_id") DO UPDATE
|
||||
SET "title" = EXCLUDED."title",
|
||||
"slug" = EXCLUDED."slug",
|
||||
"excerpt" = EXCLUDED."excerpt",
|
||||
"category" = EXCLUDED."category",
|
||||
"content" = EXCLUDED."content"
|
||||
`);
|
||||
|
||||
// ── 8. Drop old columns from posts ───────────────────────────────────────────
|
||||
await db.execute(sql`ALTER TABLE "posts" DROP COLUMN IF EXISTS "title"`);
|
||||
await db.execute(sql`ALTER TABLE "posts" DROP COLUMN IF EXISTS "slug"`);
|
||||
await db.execute(sql`ALTER TABLE "posts" DROP COLUMN IF EXISTS "excerpt"`);
|
||||
await db.execute(sql`ALTER TABLE "posts" DROP COLUMN IF EXISTS "category"`);
|
||||
await db.execute(sql`ALTER TABLE "posts" DROP COLUMN IF EXISTS "content"`);
|
||||
await db.execute(sql`ALTER TABLE "posts" DROP COLUMN IF EXISTS "locale"`);
|
||||
|
||||
// ── 9. Create products_locales join table ────────────────────────────────────
|
||||
await db.execute(sql`
|
||||
CREATE TABLE IF NOT EXISTS "products_locales" (
|
||||
"title" varchar,
|
||||
"description" varchar,
|
||||
"application" jsonb,
|
||||
"content" jsonb,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "enum__locales" NOT NULL,
|
||||
"_parent_id" integer NOT NULL
|
||||
)
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "products_locales" ADD CONSTRAINT "products_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id");
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "products_locales" ADD CONSTRAINT "products_locales_parent_id_fk"
|
||||
FOREIGN KEY ("_parent_id") REFERENCES "products"("id") ON DELETE cascade;
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
|
||||
// ── 10. Backfill products_locales ────────────────────────────────────────────
|
||||
// Products were separate DE/EN rows — each becomes a locale entry on its own id
|
||||
await db.execute(sql`
|
||||
INSERT INTO "products_locales" ("title", "description", "application", "content", "_locale", "_parent_id")
|
||||
SELECT
|
||||
p.title, p.description, p.application, p.content,
|
||||
CASE WHEN p.locale::text = 'de' THEN 'de'::"enum__locales" ELSE 'en'::"enum__locales" END,
|
||||
p.id
|
||||
FROM "products" p
|
||||
WHERE p.locale IS NOT NULL
|
||||
ON CONFLICT ("_locale", "_parent_id") DO NOTHING
|
||||
`);
|
||||
|
||||
// ── 11. Drop old columns from products ───────────────────────────────────────
|
||||
await db.execute(sql`ALTER TABLE "products" DROP COLUMN IF EXISTS "title"`);
|
||||
await db.execute(sql`ALTER TABLE "products" DROP COLUMN IF EXISTS "description"`);
|
||||
await db.execute(sql`ALTER TABLE "products" DROP COLUMN IF EXISTS "application"`);
|
||||
await db.execute(sql`ALTER TABLE "products" DROP COLUMN IF EXISTS "content"`);
|
||||
await db.execute(sql`ALTER TABLE "products" DROP COLUMN IF EXISTS "locale"`);
|
||||
|
||||
// ── 12. Version tables (_posts_v, _products_v, _pages_v) locale columns ──────
|
||||
await db.execute(sql`
|
||||
CREATE TABLE IF NOT EXISTS "_posts_v" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"parent_id" integer,
|
||||
"version_title" varchar,
|
||||
"version_slug" varchar,
|
||||
"version_excerpt" varchar,
|
||||
"version_content" jsonb,
|
||||
"version_updated_at" timestamp(3) with time zone,
|
||||
"version_created_at" timestamp(3) with time zone,
|
||||
"version__status" "enum__posts_v_version_status" DEFAULT 'draft',
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"latest" boolean
|
||||
)
|
||||
`);
|
||||
await db.execute(sql`
|
||||
CREATE TABLE IF NOT EXISTS "_products_v" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"parent_id" integer,
|
||||
"version_title" varchar,
|
||||
"version_description" varchar,
|
||||
"version_content" jsonb,
|
||||
"version_updated_at" timestamp(3) with time zone,
|
||||
"version_created_at" timestamp(3) with time zone,
|
||||
"version__status" "enum__products_v_version_status" DEFAULT 'draft',
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"latest" boolean
|
||||
)
|
||||
`);
|
||||
await db.execute(sql`
|
||||
CREATE TABLE IF NOT EXISTS "_pages_v" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"parent_id" integer,
|
||||
"version_title" varchar,
|
||||
"version_slug" varchar,
|
||||
"version_excerpt" varchar,
|
||||
"version_content" jsonb,
|
||||
"version_updated_at" timestamp(3) with time zone,
|
||||
"version_created_at" timestamp(3) with time zone,
|
||||
"version__status" "enum__pages_v_version_status" DEFAULT 'draft',
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"latest" boolean
|
||||
)
|
||||
`);
|
||||
|
||||
await db.execute(
|
||||
sql`ALTER TABLE "_posts_v" ADD COLUMN IF NOT EXISTS "published_locale" "enum__posts_v_published_locale"`,
|
||||
);
|
||||
await db.execute(
|
||||
sql`ALTER TABLE "_products_v" ADD COLUMN IF NOT EXISTS "published_locale" "enum__products_v_published_locale"`,
|
||||
);
|
||||
await db.execute(
|
||||
sql`ALTER TABLE "_pages_v" ADD COLUMN IF NOT EXISTS "published_locale" "enum__pages_v_published_locale"`,
|
||||
);
|
||||
|
||||
// ── 13. Create _posts_v_locales ──────────────────────────────────────────────
|
||||
await db.execute(sql`
|
||||
CREATE TABLE IF NOT EXISTS "_posts_v_locales" (
|
||||
"version_title" varchar, "version_slug" varchar, "version_excerpt" varchar,
|
||||
"version_category" varchar, "version_content" jsonb,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "enum__locales" NOT NULL, "_parent_id" integer NOT NULL
|
||||
)
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "_posts_v_locales" ADD CONSTRAINT "_posts_v_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id");
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "_posts_v_locales" ADD CONSTRAINT "_posts_v_locales_parent_id_fk"
|
||||
FOREIGN KEY ("_parent_id") REFERENCES "_posts_v"("id") ON DELETE cascade;
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
|
||||
// ── 14. Create _products_v_locales ───────────────────────────────────────────
|
||||
await db.execute(sql`
|
||||
CREATE TABLE IF NOT EXISTS "_products_v_locales" (
|
||||
"version_title" varchar, "version_description" varchar,
|
||||
"version_application" jsonb, "version_content" jsonb,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "enum__locales" NOT NULL, "_parent_id" integer NOT NULL
|
||||
)
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "_products_v_locales" ADD CONSTRAINT "_products_v_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id");
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "_products_v_locales" ADD CONSTRAINT "_products_v_locales_parent_id_fk"
|
||||
FOREIGN KEY ("_parent_id") REFERENCES "_products_v"("id") ON DELETE cascade;
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
|
||||
// ── 15. Create _pages_v_locales ──────────────────────────────────────────────
|
||||
await db.execute(sql`
|
||||
CREATE TABLE IF NOT EXISTS "_pages_v_locales" (
|
||||
"version_title" varchar, "version_slug" varchar,
|
||||
"version_excerpt" varchar, "version_content" jsonb,
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"_locale" "enum__locales" NOT NULL, "_parent_id" integer NOT NULL
|
||||
)
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "_pages_v_locales" ADD CONSTRAINT "_pages_v_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id");
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "_pages_v_locales" ADD CONSTRAINT "_pages_v_locales_parent_id_fk"
|
||||
FOREIGN KEY ("_parent_id") REFERENCES "_pages_v"("id") ON DELETE cascade;
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$
|
||||
`);
|
||||
|
||||
// ── 16. Drop the now-redundant old locale enum ───────────────────────────────
|
||||
await db.execute(sql`DROP TYPE IF EXISTS "public"."enum_pages_locale"`);
|
||||
await db.execute(sql`DROP TYPE IF EXISTS "public"."enum_posts_locale"`);
|
||||
await db.execute(sql`DROP TYPE IF EXISTS "public"."enum_products_locale"`);
|
||||
}
|
||||
|
||||
export async function down({ db }: MigrateDownArgs): Promise<void> {
|
||||
await db.execute(sql`DROP TABLE IF EXISTS "pages_locales" CASCADE`);
|
||||
await db.execute(sql`DROP TABLE IF EXISTS "_pages_v_locales" CASCADE`);
|
||||
await db.execute(sql`DROP TABLE IF EXISTS "posts_locales" CASCADE`);
|
||||
await db.execute(sql`DROP TABLE IF EXISTS "_posts_v_locales" CASCADE`);
|
||||
await db.execute(sql`DROP TABLE IF EXISTS "products_locales" CASCADE`);
|
||||
await db.execute(sql`DROP TABLE IF EXISTS "_products_v_locales" CASCADE`);
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres';
|
||||
|
||||
export async function up({ db }: MigrateUpArgs): Promise<void> {
|
||||
// Add featured_image_id to products and _products_v
|
||||
await db.execute(sql`
|
||||
ALTER TABLE "products" ADD COLUMN IF NOT EXISTS "featured_image_id" integer;
|
||||
`);
|
||||
await db.execute(sql`
|
||||
ALTER TABLE "_products_v" ADD COLUMN IF NOT EXISTS "version_featured_image_id" integer;
|
||||
`);
|
||||
|
||||
// Add foreign key constraints
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "products" ADD CONSTRAINT "products_featured_image_id_media_id_fk" FOREIGN KEY ("featured_image_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$;
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "_products_v" ADD CONSTRAINT "_products_v_version_featured_image_id_media_id_fk" FOREIGN KEY ("version_featured_image_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$;
|
||||
`);
|
||||
|
||||
// Add indexes
|
||||
await db.execute(sql`
|
||||
CREATE INDEX IF NOT EXISTS "products_featured_image_idx" ON "products" USING btree ("featured_image_id");
|
||||
`);
|
||||
await db.execute(sql`
|
||||
CREATE INDEX IF NOT EXISTS "_products_v_version_version_featured_image_idx" ON "_products_v" USING btree ("version_featured_image_id");
|
||||
`);
|
||||
}
|
||||
|
||||
export async function down({ db }: MigrateDownArgs): Promise<void> {
|
||||
await db.execute(sql`
|
||||
ALTER TABLE "products" DROP CONSTRAINT IF EXISTS "products_featured_image_id_media_id_fk";
|
||||
`);
|
||||
await db.execute(sql`
|
||||
ALTER TABLE "_products_v" DROP CONSTRAINT IF EXISTS "_products_v_version_featured_image_id_media_id_fk";
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DROP INDEX IF EXISTS "products_featured_image_idx";
|
||||
`);
|
||||
await db.execute(sql`
|
||||
DROP INDEX IF EXISTS "_products_v_version_version_featured_image_idx";
|
||||
`);
|
||||
await db.execute(sql`
|
||||
ALTER TABLE "products" DROP COLUMN IF EXISTS "featured_image_id";
|
||||
`);
|
||||
await db.execute(sql`
|
||||
ALTER TABLE "_products_v" DROP COLUMN IF EXISTS "version_featured_image_id";
|
||||
`);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres';
|
||||
|
||||
export async function up({ db }: MigrateUpArgs): Promise<void> {
|
||||
await db.execute(sql`
|
||||
ALTER TABLE "media" ADD COLUMN IF NOT EXISTS "sizes_tablet_url" varchar;
|
||||
ALTER TABLE "media" ADD COLUMN IF NOT EXISTS "sizes_tablet_width" numeric;
|
||||
ALTER TABLE "media" ADD COLUMN IF NOT EXISTS "sizes_tablet_height" numeric;
|
||||
ALTER TABLE "media" ADD COLUMN IF NOT EXISTS "sizes_tablet_mime_type" varchar;
|
||||
ALTER TABLE "media" ADD COLUMN IF NOT EXISTS "sizes_tablet_filesize" numeric;
|
||||
ALTER TABLE "media" ADD COLUMN IF NOT EXISTS "sizes_tablet_filename" varchar;
|
||||
`);
|
||||
|
||||
await db.execute(sql`
|
||||
CREATE INDEX IF NOT EXISTS "media_sizes_tablet_sizes_tablet_filename_idx" ON "media" USING btree ("sizes_tablet_filename");
|
||||
`);
|
||||
}
|
||||
|
||||
export async function down({ db }: MigrateDownArgs): Promise<void> {
|
||||
await db.execute(sql`
|
||||
DROP INDEX IF EXISTS "media_sizes_tablet_sizes_tablet_filename_idx";
|
||||
`);
|
||||
|
||||
await db.execute(sql`
|
||||
ALTER TABLE "media" DROP COLUMN IF EXISTS "sizes_tablet_filename";
|
||||
ALTER TABLE "media" DROP COLUMN IF EXISTS "sizes_tablet_filesize";
|
||||
ALTER TABLE "media" DROP COLUMN IF EXISTS "sizes_tablet_mime_type";
|
||||
ALTER TABLE "media" DROP COLUMN IF EXISTS "sizes_tablet_height";
|
||||
ALTER TABLE "media" DROP COLUMN IF EXISTS "sizes_tablet_width";
|
||||
ALTER TABLE "media" DROP COLUMN IF EXISTS "sizes_tablet_url";
|
||||
`);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import * as migration_20260223_195005_products_collection from './20260223_195005_products_collection';
|
||||
import * as migration_20260223_195151_remove_sku_unique from './20260223_195151_remove_sku_unique';
|
||||
import * as migration_20260225_003500_add_pages_collection from './20260225_003500_add_pages_collection';
|
||||
import * as migration_20260225_175000_native_localization from './20260225_175000_native_localization';
|
||||
import * as migration_20260305_215000_products_featured_image from './20260305_215000_products_featured_image';
|
||||
import * as migration_20260305_222000_media_sizes_tablet from './20260305_222000_media_sizes_tablet';
|
||||
|
||||
export const migrations = [
|
||||
{
|
||||
up: migration_20260223_195005_products_collection.up,
|
||||
down: migration_20260223_195005_products_collection.down,
|
||||
name: '20260223_195005_products_collection',
|
||||
},
|
||||
{
|
||||
up: migration_20260223_195151_remove_sku_unique.up,
|
||||
down: migration_20260223_195151_remove_sku_unique.down,
|
||||
name: '20260223_195151_remove_sku_unique',
|
||||
},
|
||||
{
|
||||
up: migration_20260225_003500_add_pages_collection.up,
|
||||
down: migration_20260225_003500_add_pages_collection.down,
|
||||
name: '20260225_003500_add_pages_collection',
|
||||
},
|
||||
{
|
||||
up: migration_20260225_175000_native_localization.up,
|
||||
down: migration_20260225_175000_native_localization.down,
|
||||
name: '20260225_175000_native_localization',
|
||||
},
|
||||
{
|
||||
up: migration_20260305_215000_products_featured_image.up,
|
||||
down: migration_20260305_215000_products_featured_image.down,
|
||||
name: '20260305_215000_products_featured_image',
|
||||
},
|
||||
{
|
||||
up: migration_20260305_222000_media_sizes_tablet.up,
|
||||
down: migration_20260305_222000_media_sizes_tablet.down,
|
||||
name: '20260305_222000_media_sizes_tablet',
|
||||
},
|
||||
];
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
import type { Access } from 'payload';
|
||||
|
||||
export const anyone: Access = () => true;
|
||||
@@ -1,5 +0,0 @@
|
||||
import type { Access } from 'payload';
|
||||
|
||||
export const authenticated: Access = ({ req: { user } }) => {
|
||||
return Boolean(user);
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
import { EtibBlock } from './types';
|
||||
import AnimatedImageComponent from '@/components/blog/AnimatedImage';
|
||||
|
||||
export const AnimatedImage: EtibBlock = {
|
||||
slug: 'animatedImage',
|
||||
render: AnimatedImageComponent,
|
||||
fields: [
|
||||
{
|
||||
name: 'src',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'alt',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'width',
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
name: 'height',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
import { EtibBlock } from './types';
|
||||
import { Callout as CalloutComponent } from '@/components/ui/Callout';
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical';
|
||||
|
||||
export const Callout: EtibBlock = {
|
||||
slug: 'callout',
|
||||
render: CalloutComponent,
|
||||
fields: [
|
||||
{
|
||||
name: 'type',
|
||||
type: 'select',
|
||||
options: ['info', 'warning', 'important', 'tip', 'caution'],
|
||||
defaultValue: 'info',
|
||||
},
|
||||
{
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({}),
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Block } from 'payload';
|
||||
|
||||
export const CategoryGrid: Block = {
|
||||
slug: 'categoryGrid',
|
||||
interfaceName: 'CategoryGridBlock',
|
||||
fields: [
|
||||
{
|
||||
name: 'categories',
|
||||
type: 'array',
|
||||
required: true,
|
||||
minRows: 1,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'icon',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'href',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'ctaLabel',
|
||||
type: 'text',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
import { EtibBlock } from './types';
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical';
|
||||
import ChatBubbleComponent from '@/components/blog/ChatBubble';
|
||||
|
||||
export const ChatBubble: EtibBlock = {
|
||||
slug: 'chatBubble',
|
||||
render: ChatBubbleComponent,
|
||||
fields: [
|
||||
{
|
||||
name: 'author',
|
||||
type: 'text',
|
||||
defaultValue: 'KLZ Team',
|
||||
},
|
||||
{
|
||||
name: 'avatar',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'role',
|
||||
type: 'text',
|
||||
defaultValue: 'Assistant',
|
||||
},
|
||||
{
|
||||
name: 'align',
|
||||
type: 'select',
|
||||
options: ['left', 'right'],
|
||||
defaultValue: 'left',
|
||||
},
|
||||
{
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({}),
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,54 +0,0 @@
|
||||
import { Block } from 'payload';
|
||||
|
||||
export const TeamLegacySection: Block = {
|
||||
slug: 'teamLegacySection',
|
||||
interfaceName: 'TeamLegacySectionBlock',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'subtitle',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'paragraph1',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'paragraph2',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'expertiseTitle',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'expertiseDesc',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'networkTitle',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'networkDesc',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'backgroundImage',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,12 +0,0 @@
|
||||
import { EtibBlock } from './types';
|
||||
import { CompanyTimeline } from '@/components/blocks/CompanyTimeline';
|
||||
|
||||
export const CompanyTimelineBlock: EtibBlock = {
|
||||
slug: 'companyTimeline',
|
||||
interfaceName: 'CompanyTimelineBlock',
|
||||
render: CompanyTimeline,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
],
|
||||
};
|
||||
@@ -1,49 +0,0 @@
|
||||
import { EtibBlock } from './types';
|
||||
import ComparisonGridComponent from '@/components/blog/ComparisonGrid';
|
||||
|
||||
export const ComparisonGrid: EtibBlock = {
|
||||
slug: 'comparisonGrid',
|
||||
render: ComparisonGridComponent,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Main Heading',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'leftLabel',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'rightLabel',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'items',
|
||||
type: 'array',
|
||||
required: true,
|
||||
minRows: 1,
|
||||
fields: [
|
||||
{
|
||||
name: 'label',
|
||||
label: 'Row Label',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'leftValue',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'rightValue',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
import { EtibBlock } from './types';
|
||||
import { ContactSection as ContactSectionComponent } from '@/components/blocks/ContactSection';
|
||||
|
||||
export const ContactSection: EtibBlock = {
|
||||
slug: 'contactSection',
|
||||
interfaceName: 'ContactSectionBlock',
|
||||
render: ContactSectionComponent,
|
||||
fields: [
|
||||
{
|
||||
name: 'showForm',
|
||||
type: 'checkbox',
|
||||
defaultValue: true,
|
||||
label: 'Show Contact Form',
|
||||
},
|
||||
{
|
||||
name: 'showMap',
|
||||
type: 'checkbox',
|
||||
defaultValue: true,
|
||||
label: 'Show Map',
|
||||
},
|
||||
{
|
||||
name: 'showHours',
|
||||
type: 'checkbox',
|
||||
defaultValue: true,
|
||||
label: 'Show Opening Hours',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,50 +0,0 @@
|
||||
import { EtibBlock } from './types';
|
||||
import { HeroSection as HeroSectionComponent } from '@/components/blocks/HeroSection';
|
||||
|
||||
export const HeroSection: EtibBlock = {
|
||||
slug: 'heroSection',
|
||||
interfaceName: 'HeroSectionBlock',
|
||||
render: HeroSectionComponent,
|
||||
fields: [
|
||||
{
|
||||
name: 'badge',
|
||||
type: 'text',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'subtitle',
|
||||
type: 'textarea',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'backgroundImage',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'ctaLabel',
|
||||
type: 'text',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'ctaHref',
|
||||
type: 'text',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'alignment',
|
||||
type: 'select',
|
||||
defaultValue: 'left',
|
||||
options: [
|
||||
{ label: 'Left', value: 'left' },
|
||||
{ label: 'Center', value: 'center' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Block } from 'payload';
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical';
|
||||
|
||||
export const HighlightBox: Block = {
|
||||
slug: 'highlightBox',
|
||||
fields: [
|
||||
{
|
||||
name: 'type',
|
||||
type: 'select',
|
||||
options: ['info', 'warning', 'success', 'error', 'neutral'],
|
||||
defaultValue: 'neutral',
|
||||
},
|
||||
{
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({}),
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,240 +0,0 @@
|
||||
import { EtibBlock } from './types';
|
||||
import { HeroVideo } from '@/components/blocks/HeroVideo';
|
||||
import { SubCompanyTiles } from '@/components/blocks/SubCompanyTiles';
|
||||
import { CompetenceBentoGrid } from '@/components/blocks/CompetenceBentoGrid';
|
||||
import { ReferencesSlider } from '@/components/blocks/ReferencesSlider';
|
||||
import Hero from '@/components/home/Hero';
|
||||
import ProductCategories from '@/components/home/ProductCategories';
|
||||
import WhatWeDo from '@/components/home/WhatWeDo';
|
||||
import RecentPosts from '@/components/home/RecentPosts';
|
||||
import Experience from '@/components/home/Experience';
|
||||
import WhyChooseUs from '@/components/home/WhyChooseUs';
|
||||
import MeetTheTeam from '@/components/home/MeetTheTeam';
|
||||
import GallerySection from '@/components/home/GallerySection';
|
||||
import VideoSection from '@/components/home/VideoSection';
|
||||
import CTA from '@/components/home/CTA';
|
||||
|
||||
export const HomeHero: EtibBlock = {
|
||||
slug: 'homeHero',
|
||||
interfaceName: 'HomeHeroBlock',
|
||||
render: HeroVideo,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
{ name: 'videoUrl', type: 'text', admin: { description: 'URL to the background mp4 video' } },
|
||||
{ name: 'posterImage', type: 'upload', relationTo: 'media' },
|
||||
{ name: 'ctaLabel', type: 'text', localized: true },
|
||||
{ name: 'ctaHref', type: 'text' },
|
||||
{ name: 'secondaryCtaLabel', type: 'text', localized: true },
|
||||
{ name: 'secondaryCtaHref', type: 'text' },
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeSubCompanyTiles: EtibBlock = {
|
||||
slug: 'homeSubCompanyTiles',
|
||||
interfaceName: 'HomeSubCompanyTilesBlock',
|
||||
render: SubCompanyTiles,
|
||||
fields: [
|
||||
{ name: 'badge', type: 'text', localized: true },
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{
|
||||
name: 'companies',
|
||||
type: 'array',
|
||||
localized: true,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text' },
|
||||
{ name: 'description', type: 'textarea' },
|
||||
{ name: 'icon', type: 'text', admin: { description: 'SVG path for the icon' } },
|
||||
{ name: 'backgroundImage', type: 'upload', relationTo: 'media' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeCompetenceBentoGrid: EtibBlock = {
|
||||
slug: 'homeCompetenceBentoGrid',
|
||||
interfaceName: 'HomeCompetenceBentoGridBlock',
|
||||
render: CompetenceBentoGrid,
|
||||
fields: [
|
||||
{ name: 'badge', type: 'text', localized: true },
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'ctaLabel', type: 'text', localized: true },
|
||||
{ name: 'ctaHref', type: 'text' },
|
||||
{
|
||||
name: 'items',
|
||||
type: 'array',
|
||||
localized: true,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text' },
|
||||
{ name: 'description', type: 'textarea' },
|
||||
{ name: 'tag', type: 'text' },
|
||||
{ name: 'image', type: 'upload', relationTo: 'media' },
|
||||
{
|
||||
name: 'size',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Large (2x2)', value: 'large' },
|
||||
{ label: 'Medium (1x2)', value: 'medium' },
|
||||
{ label: 'Small (1x1)', value: 'small' },
|
||||
{ label: 'Accent (Text only)', value: 'accent' },
|
||||
],
|
||||
defaultValue: 'small',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeReferencesSlider: EtibBlock = {
|
||||
slug: 'homeReferencesSlider',
|
||||
interfaceName: 'HomeReferencesSliderBlock',
|
||||
render: ReferencesSlider,
|
||||
fields: [
|
||||
{ name: 'badge', type: 'text', localized: true },
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'description', type: 'textarea', localized: true },
|
||||
{ name: 'ctaLabel', type: 'text', localized: true },
|
||||
{ name: 'ctaHref', type: 'text' },
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeProductCategories: EtibBlock = {
|
||||
slug: 'homeProductCategories',
|
||||
interfaceName: 'HomeProductCategoriesBlock',
|
||||
render: ProductCategories,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeWhatWeDo: EtibBlock = {
|
||||
slug: 'homeWhatWeDo',
|
||||
interfaceName: 'HomeWhatWeDoBlock',
|
||||
render: WhatWeDo,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
{ name: 'expertiseLabel', type: 'text', localized: true },
|
||||
{ name: 'quote', type: 'textarea', localized: true },
|
||||
{
|
||||
name: 'items',
|
||||
type: 'array',
|
||||
localized: true,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text' },
|
||||
{ name: 'description', type: 'textarea' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeRecentPosts: EtibBlock = {
|
||||
slug: 'homeRecentPosts',
|
||||
interfaceName: 'HomeRecentPostsBlock',
|
||||
render: RecentPosts,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeExperience: EtibBlock = {
|
||||
slug: 'homeExperience',
|
||||
interfaceName: 'HomeExperienceBlock',
|
||||
render: Experience,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
{ name: 'paragraph1', type: 'textarea', localized: true },
|
||||
{ name: 'paragraph2', type: 'textarea', localized: true },
|
||||
{ name: 'badge1', type: 'text', localized: true },
|
||||
{ name: 'badge1Text', type: 'text', localized: true },
|
||||
{ name: 'badge2', type: 'text', localized: true },
|
||||
{ name: 'badge2Text', type: 'text', localized: true },
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeWhyChooseUs: EtibBlock = {
|
||||
slug: 'homeWhyChooseUs',
|
||||
interfaceName: 'HomeWhyChooseUsBlock',
|
||||
render: WhyChooseUs,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
{ name: 'tagline', type: 'text', localized: true },
|
||||
{
|
||||
name: 'features',
|
||||
type: 'array',
|
||||
localized: true,
|
||||
fields: [{ name: 'feature', type: 'text' }],
|
||||
},
|
||||
{
|
||||
name: 'items',
|
||||
type: 'array',
|
||||
localized: true,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text' },
|
||||
{ name: 'description', type: 'textarea' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeMeetTheTeam: EtibBlock = {
|
||||
slug: 'homeMeetTheTeam',
|
||||
interfaceName: 'HomeMeetTheTeamBlock',
|
||||
render: MeetTheTeam,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
{ name: 'description', type: 'textarea', localized: true },
|
||||
{ name: 'ctaLabel', type: 'text', localized: true },
|
||||
{ name: 'networkLabel', type: 'text', localized: true },
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeGallery: EtibBlock = {
|
||||
slug: 'homeGallery',
|
||||
interfaceName: 'HomeGalleryBlock',
|
||||
render: GallerySection,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
],
|
||||
};
|
||||
|
||||
export const HomeVideo: EtibBlock = {
|
||||
slug: 'homeVideo',
|
||||
interfaceName: 'HomeVideoBlock',
|
||||
render: VideoSection,
|
||||
fields: [{ name: 'title', type: 'text', localized: true }],
|
||||
};
|
||||
|
||||
export const HomeCTA: EtibBlock = {
|
||||
slug: 'homeCTA',
|
||||
interfaceName: 'HomeCTABlock',
|
||||
render: CTA,
|
||||
fields: [
|
||||
{ name: 'title', type: 'text', localized: true },
|
||||
{ name: 'subtitle', type: 'text', localized: true },
|
||||
{ name: 'description', type: 'textarea', localized: true },
|
||||
{ name: 'buttonLabel', type: 'text', localized: true },
|
||||
],
|
||||
};
|
||||
|
||||
export const homeBlocksArray = [
|
||||
HomeHero,
|
||||
HomeSubCompanyTiles,
|
||||
HomeCompetenceBentoGrid,
|
||||
HomeReferencesSlider,
|
||||
HomeProductCategories,
|
||||
HomeWhatWeDo,
|
||||
HomeRecentPosts,
|
||||
HomeExperience,
|
||||
HomeWhyChooseUs,
|
||||
HomeMeetTheTeam,
|
||||
HomeGallery,
|
||||
HomeVideo,
|
||||
HomeCTA,
|
||||
];
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user