Compare commits
10 Commits
3b20d4087c
...
content/su
| Author | SHA1 | Date | |
|---|---|---|---|
| 68c4a056b7 | |||
| 57a67ffdf3 | |||
| 5977ebf23d | |||
| ee2bcea42b | |||
| 2f2fcfdf13 | |||
| 070f97dd6f | |||
| 2a46015d0d | |||
| 5329d96e3b | |||
| 0c01aa799d | |||
| 75c15fce43 |
@@ -123,6 +123,13 @@ export default async function Layout(props: {
|
||||
'Error',
|
||||
'StandardPage',
|
||||
'Brochure',
|
||||
'JobListingBlock',
|
||||
'CallToAction',
|
||||
'InteractiveGermanyMap',
|
||||
'ReferencesSlider',
|
||||
'CompanyTimeline',
|
||||
'TeamGrid',
|
||||
'AISearch',
|
||||
];
|
||||
const clientMessages: Record<string, any> = {};
|
||||
for (const key of clientKeys) {
|
||||
|
||||
38
app/[locale]/referenzen/[slug]/page.test.tsx
Normal file
38
app/[locale]/referenzen/[slug]/page.test.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import ReferenceDetail from './page';
|
||||
|
||||
// Mock next/navigation
|
||||
vi.mock('next/navigation', () => ({
|
||||
notFound: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock next-intl/server
|
||||
vi.mock('next-intl/server', () => ({
|
||||
setRequestLocale: vi.fn(),
|
||||
getTranslations: vi.fn().mockResolvedValue(() => 'translated text'),
|
||||
}));
|
||||
|
||||
// Mock @/lib/references
|
||||
vi.mock('@/lib/references', () => ({
|
||||
getReferenceBySlug: vi.fn().mockResolvedValue({
|
||||
slug: 'test-slug',
|
||||
frontmatter: {
|
||||
title: 'Test Title',
|
||||
location: 'Test Location',
|
||||
category: 'Test Category',
|
||||
},
|
||||
content: 'Test content',
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('ReferenceDetail Page TDD', () => {
|
||||
it('renders reference detail correctly', async () => {
|
||||
const props = {
|
||||
params: Promise.resolve({ locale: 'de', slug: 'test-slug' })
|
||||
};
|
||||
|
||||
// We expect the execution to fail if getTranslations is not defined/imported inside page.tsx
|
||||
const component = await ReferenceDetail(props);
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { notFound } from 'next/navigation';
|
||||
import { Container, Badge, Heading } from '@/components/ui';
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
import { setRequestLocale, getTranslations } from 'next-intl/server';
|
||||
import { Metadata } from 'next';
|
||||
import { getReferenceBySlug, getAllReferences } from '@/lib/references';
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||
@@ -57,6 +57,7 @@ export default async function ReferenceDetail(props: { params: Promise<{ locale:
|
||||
|
||||
setRequestLocale(locale);
|
||||
const reference = await getReferenceBySlug(slug, locale);
|
||||
const t = await getTranslations('ReferenceDetail');
|
||||
|
||||
if (!reference) {
|
||||
notFound();
|
||||
@@ -119,12 +120,12 @@ export default async function ReferenceDetail(props: { params: Promise<{ locale:
|
||||
eventProperties={{ location: 'reference_back_btn' }}
|
||||
>
|
||||
<ChevronLeft className="w-5 h-5" />
|
||||
Zurück zur Übersicht
|
||||
{t('backToOverview')}
|
||||
</TrackedLink>
|
||||
</div>
|
||||
<div className="max-w-4xl">
|
||||
<Badge variant="accent" className="mb-4 md:mb-6">
|
||||
Projektreferenz
|
||||
{t('projectReference')}
|
||||
</Badge>
|
||||
<Heading level={1} variant="white" className="mb-8 leading-tight">
|
||||
{reference.frontmatter.title}
|
||||
@@ -134,21 +135,21 @@ export default async function ReferenceDetail(props: { params: Promise<{ locale:
|
||||
<div className="flex items-start gap-4 p-6 bg-white/5 rounded-2xl border border-white/10 backdrop-blur-sm">
|
||||
<MapPin className="w-8 h-8 text-primary shrink-0" />
|
||||
<div>
|
||||
<p className="text-sm text-white/50 mb-1 uppercase tracking-wider font-bold">Ort</p>
|
||||
<p className="text-sm text-white/50 mb-1 uppercase tracking-wider font-bold">{t('location')}</p>
|
||||
<p className="font-semibold text-lg">{reference.frontmatter.location}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-4 p-6 bg-white/5 rounded-2xl border border-white/10 backdrop-blur-sm">
|
||||
<Briefcase className="w-8 h-8 text-primary shrink-0" />
|
||||
<div>
|
||||
<p className="text-sm text-white/50 mb-1 uppercase tracking-wider font-bold">Auftraggeber</p>
|
||||
<p className="text-sm text-white/50 mb-1 uppercase tracking-wider font-bold">{t('client')}</p>
|
||||
<p className="font-semibold text-lg">{reference.frontmatter.client}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-4 p-6 bg-white/5 rounded-2xl border border-white/10 backdrop-blur-sm">
|
||||
<Calendar className="w-8 h-8 text-primary shrink-0" />
|
||||
<div>
|
||||
<p className="text-sm text-white/50 mb-1 uppercase tracking-wider font-bold">Zeitraum</p>
|
||||
<p className="text-sm text-white/50 mb-1 uppercase tracking-wider font-bold">{t('period')}</p>
|
||||
<p className="font-semibold text-lg">{reference.frontmatter.dateString || new Date(reference.frontmatter.date).getFullYear()}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -160,7 +161,7 @@ export default async function ReferenceDetail(props: { params: Promise<{ locale:
|
||||
{/* Main Content Area */}
|
||||
<Container className="py-16 md:py-24">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h2 className="text-3xl font-bold text-neutral-dark mb-8">Leistungsumfang & Projektbeschreibung</h2>
|
||||
<h2 className="text-3xl font-bold text-neutral-dark mb-8">{t('scopeTitle')}</h2>
|
||||
|
||||
<div className="w-full">
|
||||
<MDXRemote source={reference.content} components={mdxComponents} />
|
||||
@@ -173,7 +174,7 @@ export default async function ReferenceDetail(props: { params: Promise<{ locale:
|
||||
eventProperties={{ location: 'reference_bottom_back_btn' }}
|
||||
>
|
||||
<span className="relative z-10 flex items-center justify-center gap-2">
|
||||
Alle Referenzen ansehen
|
||||
{t('viewAll')}
|
||||
</span>
|
||||
<ButtonOverlay variant="primary" />
|
||||
</TrackedLink>
|
||||
|
||||
@@ -76,7 +76,7 @@ export default async function ReferenzenOverview(props: { params: Promise<{ loca
|
||||
<InteractiveGermanyMap
|
||||
isHero={true}
|
||||
badge={locale === 'en' ? 'Our References' : 'Unsere Referenzen'}
|
||||
title={locale === 'en' ? <>Successfully realized<br/>projects.</> : <>Erfolgreich umgesetzte<br/>Projekte.</>}
|
||||
title={locale === 'en' ? 'Successfully realized projects.' : 'Erfolgreich umgesetzte Projekte.'}
|
||||
description={locale === 'en' ? 'From broadband expansion to complex 110kV lines: A selection of our nationwide projects where we have created infrastructure for the future. Discover our locations.' : 'Vom Breitbandausbau bis zur komplexen 110kV-Trasse: Ein Auszug unserer bundesweiten Projekte, bei denen wir Infrastruktur für die Zukunft geschaffen haben. Entdecken Sie unsere Standorte.'}
|
||||
locations={enrichedLocations}
|
||||
/>
|
||||
|
||||
@@ -36,7 +36,7 @@ export default function LeafletMap({ locations }: LeafletMapProps) {
|
||||
const firstLoc = locations[0];
|
||||
const map = L.map(mapRef.current, {
|
||||
center: [firstLoc.lat, firstLoc.lng],
|
||||
zoom: 6, // Zoom out to see both or more
|
||||
zoom: locations.length === 1 ? 16 : 6,
|
||||
scrollWheelZoom: false,
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { LogoArcs } from '@/components/ui/LogoArcs';
|
||||
interface Certificate {
|
||||
title: string;
|
||||
description: string;
|
||||
pdfUrl: string;
|
||||
pdfUrl?: string;
|
||||
type: 'iso' | 'tax' | 'general';
|
||||
date?: string;
|
||||
}
|
||||
@@ -29,45 +29,70 @@ const defaultCertificates: Certificate[] = [
|
||||
{
|
||||
title: 'ISO 14001:2015',
|
||||
description: 'Umweltmanagementsystem',
|
||||
pdfUrl: '/assets/certificates/231214_Zertifikat ISO 14001 Umweltmanagement.pdf',
|
||||
pdfUrl: '/assets/certificates/iso14001.pdf',
|
||||
type: 'iso',
|
||||
date: '14.12.2023',
|
||||
},
|
||||
{
|
||||
title: 'ISO 9001:2015',
|
||||
description: 'Qualitätsmanagementsystem',
|
||||
pdfUrl: '/assets/certificates/231214_Zertifikat ISO 9001 Qualitätsmanagement.pdf',
|
||||
pdfUrl: '/assets/certificates/iso9001.pdf',
|
||||
type: 'iso',
|
||||
date: '14.12.2023',
|
||||
},
|
||||
{
|
||||
title: 'DIN EN ISO 45001:2018',
|
||||
description: 'Arbeits- und Gesundheitsschutz',
|
||||
pdfUrl: '/assets/certificates/Zertifizierung DIN EN ISO 45001 bis 05122028.pdf',
|
||||
pdfUrl: '/assets/certificates/iso45001.pdf',
|
||||
type: 'iso',
|
||||
date: '05.12.2025', // Assuming valid till
|
||||
date: '05.12.2025',
|
||||
},
|
||||
{
|
||||
title: 'Freistellungsbescheinigung',
|
||||
description: 'Nach § 48 b EStG',
|
||||
pdfUrl: '/assets/certificates/240209_Freistellungsbescheinigung § 48 b.pdf',
|
||||
pdfUrl: '/assets/certificates/freistellung.pdf',
|
||||
type: 'tax',
|
||||
date: '09.02.2024',
|
||||
},
|
||||
{
|
||||
title: 'Nachweis § 13b UStG',
|
||||
description: 'Steuerschuldnerschaft des Leistungsempfängers',
|
||||
pdfUrl: '/assets/certificates/240209_Nachweis § 13 b.pdf',
|
||||
pdfUrl: '/assets/certificates/nachweis13b.pdf',
|
||||
type: 'tax',
|
||||
date: '09.02.2024',
|
||||
},
|
||||
{
|
||||
title: 'Bescheinigung in Steuersachen',
|
||||
description: 'Zertifikat des Finanzamtes',
|
||||
pdfUrl: '/assets/certificates/250213_Bescheinigung in Steuersachen.pdf',
|
||||
pdfUrl: '/assets/certificates/bescheinigung.pdf',
|
||||
type: 'tax',
|
||||
date: '13.02.2025',
|
||||
},
|
||||
{
|
||||
title: 'Unbedenklichkeit IHK',
|
||||
description: 'Industrie- und Handelskammer',
|
||||
type: 'general',
|
||||
},
|
||||
{
|
||||
title: 'Unbedenklichkeit HWK',
|
||||
description: 'Handwerkskammer',
|
||||
type: 'general',
|
||||
},
|
||||
{
|
||||
title: 'Handelsregisterauszug',
|
||||
description: 'Amtsgericht Cottbus',
|
||||
type: 'general',
|
||||
},
|
||||
{
|
||||
title: 'Gewerbe-Anmeldung',
|
||||
description: 'Stadt Guben',
|
||||
type: 'general',
|
||||
},
|
||||
{
|
||||
title: 'Gewerbezentralregister',
|
||||
description: 'Auskunft aus dem Register',
|
||||
type: 'general',
|
||||
},
|
||||
];
|
||||
|
||||
export function CertificatesBlock({ badge, title, description, certificates = defaultCertificates, hideHeader = false }: CertificatesBlockProps) {
|
||||
@@ -143,13 +168,17 @@ export function CertificatesBlock({ badge, title, description, certificates = de
|
||||
>
|
||||
{certificates.map((cert, index) => {
|
||||
const isIso = cert.type === 'iso';
|
||||
const Wrapper = cert.pdfUrl ? motion.a : motion.div;
|
||||
const wrapperProps = cert.pdfUrl ? {
|
||||
href: encodeURI(cert.pdfUrl),
|
||||
target: "_blank",
|
||||
rel: "noopener noreferrer"
|
||||
} : {};
|
||||
|
||||
return (
|
||||
<motion.a
|
||||
<Wrapper
|
||||
key={index}
|
||||
href={encodeURI(cert.pdfUrl)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
{...wrapperProps as any}
|
||||
variants={itemVariants}
|
||||
className={`group relative rounded-2xl p-8 flex flex-col justify-between overflow-hidden transition-all duration-500 hover:shadow-xl hover:-translate-y-1 border ${
|
||||
isIso
|
||||
@@ -196,14 +225,22 @@ export function CertificatesBlock({ badge, title, description, certificates = de
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={`mt-auto inline-flex items-center text-sm font-bold uppercase tracking-wider group-hover:gap-3 transition-all ${
|
||||
<div className={`mt-auto inline-flex items-center text-sm font-bold uppercase tracking-wider transition-all ${
|
||||
cert.pdfUrl ? 'group-hover:gap-3' : ''
|
||||
} ${
|
||||
isIso ? 'text-primary-light' : 'text-primary'
|
||||
}`}>
|
||||
<span>Download PDF</span>
|
||||
<Download size={16} className="ml-2 group-hover:translate-y-0.5 transition-transform" />
|
||||
{cert.pdfUrl ? (
|
||||
<>
|
||||
<span>Download PDF</span>
|
||||
<Download size={16} className="ml-2 group-hover:translate-y-0.5 transition-transform" />
|
||||
</>
|
||||
) : (
|
||||
<span className="text-neutral-400">Nachweis liegt vor</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</motion.a>
|
||||
</Wrapper>
|
||||
);
|
||||
})}
|
||||
</motion.div>
|
||||
|
||||
@@ -7,13 +7,13 @@ import { useLocale } from 'next-intl';
|
||||
export function DataGridPulse() {
|
||||
const locale = useLocale();
|
||||
return (
|
||||
<div className="relative w-full py-32 bg-[#050B14] overflow-hidden flex flex-col items-center border-y border-primary/20 -mx-[50vw] px-[50vw] mb-16">
|
||||
<div className="relative w-full py-16 md:py-24 bg-neutral-50 border border-neutral-100/80 overflow-hidden flex flex-col items-center rounded-3xl mb-16 shadow-sm">
|
||||
|
||||
{/* Blueprint grid background */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-20 pointer-events-none"
|
||||
className="absolute inset-0 opacity-[0.03] pointer-events-none"
|
||||
style={{
|
||||
backgroundImage: 'linear-gradient(#82ED20 1px, transparent 1px), linear-gradient(90deg, #82ED20 1px, transparent 1px)',
|
||||
backgroundImage: 'linear-gradient(#117C61 1px, transparent 1px), linear-gradient(90deg, #117C61 1px, transparent 1px)',
|
||||
backgroundSize: '40px 40px'
|
||||
}}
|
||||
/>
|
||||
@@ -21,25 +21,25 @@ export function DataGridPulse() {
|
||||
{/* Pulsing horizontal lines (Data Flow) */}
|
||||
<motion.div
|
||||
animate={{ x: ["-100%", "100%"] }}
|
||||
transition={{ duration: 3, repeat: Infinity, ease: "linear" }}
|
||||
className="absolute top-1/3 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-primary to-transparent opacity-50 blur-[2px]"
|
||||
transition={{ duration: 5, repeat: Infinity, ease: "linear" }}
|
||||
className="absolute top-1/3 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-primary/30 to-transparent blur-[1px]"
|
||||
/>
|
||||
<motion.div
|
||||
animate={{ x: ["100%", "-100%"] }}
|
||||
transition={{ duration: 4, repeat: Infinity, ease: "linear" }}
|
||||
className="absolute top-2/3 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-white to-transparent opacity-30 blur-[2px]"
|
||||
transition={{ duration: 7, repeat: Infinity, ease: "linear" }}
|
||||
className="absolute top-2/3 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-primary/25 to-transparent blur-[1px]"
|
||||
/>
|
||||
|
||||
<div className="relative z-10 w-full max-w-4xl mx-auto flex flex-col items-center text-center">
|
||||
<h3 className="text-sm font-bold uppercase tracking-widest text-primary mb-6">
|
||||
<div className="relative z-10 w-full max-w-2xl px-6 mx-auto flex flex-col items-center text-center">
|
||||
<span className="text-xs md:text-sm font-bold uppercase tracking-widest text-primary mb-4 bg-primary/10 px-3 py-1 rounded-full">
|
||||
{locale === 'en' ? 'Network Expansion in Numbers' : 'Netzausbau in Zahlen'}
|
||||
</h3>
|
||||
</span>
|
||||
|
||||
<div className="bg-black/40 backdrop-blur-md p-12 md:p-16 rounded-3xl border border-white/10 shadow-2xl">
|
||||
<div className="font-mono text-6xl md:text-8xl font-black text-white tracking-tighter mb-4 leading-none" style={{ fontVariantNumeric: 'tabular-nums' }}>
|
||||
372.161<span className="text-primary text-3xl md:text-5xl ml-2">m</span>
|
||||
<div className="bg-white/80 backdrop-blur-md p-8 md:p-12 rounded-2xl border border-neutral-200/50 shadow-lg w-full">
|
||||
<div className="font-mono text-5xl md:text-7xl font-black text-neutral-900 tracking-tighter mb-4 leading-none animate-pulse-slow" style={{ fontVariantNumeric: 'tabular-nums' }}>
|
||||
372.161<span className="text-primary text-2xl md:text-4xl ml-1 font-sans">m</span>
|
||||
</div>
|
||||
<p className="text-white/70 text-lg md:text-xl font-medium max-w-lg mx-auto">
|
||||
<p className="text-text-secondary text-base md:text-lg font-medium max-w-md mx-auto leading-relaxed">
|
||||
{locale === 'en'
|
||||
? 'Cable routes laid since 2023. A massive infrastructure achievement, driven by our own machinery.'
|
||||
: 'Verlegte Kabeltrassen seit 2023. Eine massive Infrastrukturleistung, getragen von unserem eigenen Maschinenpark.'}
|
||||
|
||||
@@ -30,10 +30,7 @@ export function InteractiveGermanyMap({
|
||||
badge,
|
||||
title,
|
||||
description,
|
||||
stats = [
|
||||
{ value: '100', suffix: '%', label: 'Überregionale Reichweite' },
|
||||
{ value: '2', suffix: '+', label: 'Operative Standorte' },
|
||||
],
|
||||
stats,
|
||||
locations = allLocations,
|
||||
isHero = false
|
||||
}: InteractiveGermanyMapProps) {
|
||||
@@ -43,6 +40,11 @@ export function InteractiveGermanyMap({
|
||||
const t = useTranslations('InteractiveGermanyMap');
|
||||
const tStandard = useTranslations('StandardPage');
|
||||
|
||||
const finalStats = stats || [
|
||||
{ value: '100', suffix: '%', label: locale === 'en' ? 'Nationwide Reach' : 'Überregionale Reichweite' },
|
||||
{ value: '2', suffix: '+', label: locale === 'en' ? 'Operational Locations' : 'Operative Standorte' },
|
||||
];
|
||||
|
||||
const finalBadge = badge || tStandard('badge');
|
||||
// the map is mostly used with specific props, so we leave title & description to fallbacks if not provided
|
||||
const finalTitle = title || (locale === 'en' ? <>Nationwide<br/>in operation for you.</> : <>Deutschlandweit<br/>für Sie im Einsatz.</>);
|
||||
@@ -73,11 +75,11 @@ export function InteractiveGermanyMap({
|
||||
</div>
|
||||
|
||||
{isHero ? (
|
||||
<h1 className="font-heading text-4xl lg:text-5xl xl:text-6xl font-extrabold mb-6 leading-[1.1] text-transparent bg-clip-text bg-gradient-to-r from-white to-white/70 break-words [hyphens:auto]">
|
||||
<h1 className="font-heading text-4xl lg:text-5xl xl:text-6xl font-extrabold mb-6 leading-[1.1] text-transparent bg-clip-text bg-gradient-to-r from-white to-white/70 text-balance">
|
||||
{finalTitle}
|
||||
</h1>
|
||||
) : (
|
||||
<h3 className="font-heading text-4xl lg:text-5xl xl:text-6xl font-extrabold mb-6 leading-[1.1] text-transparent bg-clip-text bg-gradient-to-r from-white to-white/70 break-words [hyphens:auto]">
|
||||
<h3 className="font-heading text-4xl lg:text-5xl xl:text-6xl font-extrabold mb-6 leading-[1.1] text-transparent bg-clip-text bg-gradient-to-r from-white to-white/70 text-balance">
|
||||
{finalTitle}
|
||||
</h3>
|
||||
)}
|
||||
@@ -88,7 +90,7 @@ export function InteractiveGermanyMap({
|
||||
|
||||
{/* Industrial Stats Grid */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{stats.map((stat, i) => (
|
||||
{finalStats.map((stat, i) => (
|
||||
<div key={i} className="bg-white/5 border border-white/10 rounded-2xl p-6 backdrop-blur-md relative overflow-hidden group">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-primary/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
|
||||
<div className="relative z-10">
|
||||
@@ -150,7 +152,10 @@ export function InteractiveGermanyMap({
|
||||
onMouseEnter={() => setActiveLocation(loc)}
|
||||
onMouseLeave={() => setActiveLocation(null)}
|
||||
onClick={() => {
|
||||
if (loc.href) router.push(loc.href);
|
||||
if (loc.href) {
|
||||
const href = loc.href.startsWith('/') ? `/${locale}${loc.href}` : loc.href;
|
||||
router.push(href);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* Ping Animation for HQ / Branch */}
|
||||
|
||||
@@ -59,7 +59,7 @@ export const JobListingBlock = (props: JobListingBlockProps) => {
|
||||
],
|
||||
emptyStateMessage = t('emptyStateMessage'),
|
||||
emptyStateLinkText = t('emptyStateLinkText'),
|
||||
emptyStateLinkHref = '/kontakt'
|
||||
emptyStateLinkHref = t('emptyStateLinkHref')
|
||||
} = props;
|
||||
|
||||
const title = props.title || t('title');
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import * as React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import Image from 'next/image';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
export interface TeamMember {
|
||||
id: string;
|
||||
@@ -22,16 +23,18 @@ interface TeamGridProps {
|
||||
}
|
||||
|
||||
export function TeamGrid({ members }: TeamGridProps) {
|
||||
const t = useTranslations('TeamGrid');
|
||||
|
||||
if (!members || members.length === 0) return null;
|
||||
|
||||
return (
|
||||
<section className="py-24 bg-white relative overflow-hidden">
|
||||
<div className="container relative z-10">
|
||||
<div className="mb-12">
|
||||
<h2 className="text-primary font-bold tracking-wider uppercase text-sm mb-3">Persönliche Beratung</h2>
|
||||
<h3 className="font-heading text-4xl font-extrabold text-neutral-dark mb-4">Ihre Ansprechpartner</h3>
|
||||
<h2 className="text-primary font-bold tracking-wider uppercase text-sm mb-3">{t('badge')}</h2>
|
||||
<h3 className="font-heading text-4xl font-extrabold text-neutral-dark mb-4">{t('title')}</h3>
|
||||
<p className="text-text-secondary max-w-2xl text-lg">
|
||||
Sprechen Sie direkt mit unseren Experten für Ihr regionales Projekt.
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -56,7 +59,7 @@ export function TeamGrid({ members }: TeamGridProps) {
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-primary"></span>
|
||||
</span>
|
||||
Geschäftsführung
|
||||
{t('management')}
|
||||
</div>
|
||||
<h3 className="font-heading font-black text-5xl md:text-6xl lg:text-7xl mb-4 leading-[1.1] tracking-tight">
|
||||
{members[0].name}
|
||||
@@ -145,7 +148,7 @@ export function TeamGrid({ members }: TeamGridProps) {
|
||||
{member.branch && (
|
||||
<div className="mb-6">
|
||||
<span className="inline-block px-3 py-1 bg-neutral-100 text-neutral-600 rounded-md text-xs uppercase tracking-widest font-semibold border border-neutral-200/60">
|
||||
{member.branch === 'e-tib' ? 'E-TIB GmbH' : member.branch === 'ing' ? 'Ingenieurgesellschaft' : member.branch === 'bohrtechnik' ? 'Bohrtechnik' : member.branch}
|
||||
{member.branch === 'e-tib' ? t('branchETIB') : member.branch === 'ing' ? t('branchIng') : member.branch === 'bohrtechnik' ? t('branchBohr') : member.branch}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import * as React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { sendContactFormAction } from '@/app/actions/contact';
|
||||
|
||||
export function ContactForm() {
|
||||
const [status, setStatus] = React.useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
||||
@@ -11,10 +12,18 @@ export function ContactForm() {
|
||||
e.preventDefault();
|
||||
setStatus('loading');
|
||||
|
||||
// Simulate server action using @mintel/mail or API route
|
||||
setTimeout(() => {
|
||||
setStatus('success');
|
||||
}, 1500);
|
||||
const formData = new FormData(e.currentTarget);
|
||||
try {
|
||||
const result = await sendContactFormAction(formData);
|
||||
if (result.success) {
|
||||
setStatus('success');
|
||||
} else {
|
||||
setStatus('error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
setStatus('error');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -41,13 +50,31 @@ export function ContactForm() {
|
||||
<p>Vielen Dank für Ihr Interesse an der E-TIB Gruppe. Ein Ansprechpartner wird sich zeitnah mit Ihnen in Verbindung setzen.</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
) : (
|
||||
) : status === 'error' ? (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
className="bg-red-500/10 border border-red-500 text-red-600 p-6 rounded-xl flex items-start gap-4 mb-6"
|
||||
>
|
||||
<div className="bg-red-500 text-white rounded-full p-1 mt-0.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-bold text-lg mb-1">Fehler beim Senden</h4>
|
||||
<p>Leider ist ein Fehler aufgetreten. Bitte versuchen Sie es später noch einmal oder kontaktieren Sie uns direkt per E-Mail oder Telefon.</p>
|
||||
<Button variant="outline" size="sm" className="mt-4" onClick={() => setStatus('idle')}>Erneut versuchen</Button>
|
||||
</div>
|
||||
</motion.div>
|
||||
) : null}
|
||||
|
||||
{status !== 'success' && status !== 'error' && (
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="firstName" className="block text-sm font-semibold text-neutral-dark">Vorname</label>
|
||||
<label htmlFor="name" className="block text-sm font-semibold text-neutral-dark">Vorname</label>
|
||||
<input
|
||||
id="firstName"
|
||||
id="name"
|
||||
name="name"
|
||||
required
|
||||
disabled={status === 'loading'}
|
||||
className="w-full bg-neutral-50 px-4 py-3 rounded-lg border border-neutral-200 focus:border-primary focus:ring-2 focus:ring-primary/20 outline-none transition-all"
|
||||
@@ -57,7 +84,8 @@ export function ContactForm() {
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="lastName" className="block text-sm font-semibold text-neutral-dark">Nachname</label>
|
||||
<input
|
||||
id="lastName"
|
||||
id="lastName"
|
||||
name="lastName"
|
||||
required
|
||||
disabled={status === 'loading'}
|
||||
className="w-full bg-neutral-50 px-4 py-3 rounded-lg border border-neutral-200 focus:border-primary focus:ring-2 focus:ring-primary/20 outline-none transition-all"
|
||||
@@ -70,6 +98,7 @@ export function ContactForm() {
|
||||
<label htmlFor="email" className="block text-sm font-semibold text-neutral-dark">E-Mail Adresse</label>
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
required
|
||||
disabled={status === 'loading'}
|
||||
@@ -82,6 +111,7 @@ export function ContactForm() {
|
||||
<label htmlFor="company" className="block text-sm font-semibold text-neutral-dark">Unternehmen (Optional)</label>
|
||||
<input
|
||||
id="company"
|
||||
name="company"
|
||||
disabled={status === 'loading'}
|
||||
className="w-full bg-neutral-50 px-4 py-3 rounded-lg border border-neutral-200 focus:border-primary focus:ring-2 focus:ring-primary/20 outline-none transition-all"
|
||||
placeholder="Firma GmbH"
|
||||
@@ -92,6 +122,7 @@ export function ContactForm() {
|
||||
<label htmlFor="message" className="block text-sm font-semibold text-neutral-dark">Ihre Nachricht</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
required
|
||||
disabled={status === 'loading'}
|
||||
rows={5}
|
||||
@@ -100,6 +131,8 @@ export function ContactForm() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<input type="text" name="company_website" style={{ display: 'none' }} tabIndex={-1} autoComplete="off" />
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={status === 'loading'}
|
||||
|
||||
@@ -20,23 +20,23 @@ export default function Hero({ data }: { data?: any }) {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [isSearchOpen, setIsSearchOpen] = useState(false);
|
||||
const [heroPlaceholder, setHeroPlaceholder] = useState(
|
||||
'Projekt beschreiben oder Kabel suchen...',
|
||||
t('searchPlaceholder')
|
||||
);
|
||||
const typingRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
const HERO_PLACEHOLDERS = [
|
||||
'Querschnittsberechnung für 110kV Trasse', // Hochspannung
|
||||
'Wie schwer ist NAYY 4x150?',
|
||||
'Ich plane einen Solarpark, was brauche ich?', // Projekt Solar
|
||||
'Unterschied zwischen N2XSY und NAY2XSY?', // Fach
|
||||
'Mittelspannungskabel für Windkraftanlage', // Windpark
|
||||
'Welches Aluminiumkabel für 20kV?', // Mittelspannung
|
||||
t('placeholder1'),
|
||||
t('placeholder2'),
|
||||
t('placeholder3'),
|
||||
t('placeholder4'),
|
||||
t('placeholder5'),
|
||||
t('placeholder6'),
|
||||
];
|
||||
|
||||
// Typing animation for the hero search placeholder
|
||||
useEffect(() => {
|
||||
if (searchQuery) {
|
||||
setHeroPlaceholder('Projekt beschreiben oder Kabel suchen...');
|
||||
setHeroPlaceholder(t('searchPlaceholder'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ export default function Hero({ data }: { data?: any }) {
|
||||
size="lg"
|
||||
className="rounded-xl px-6 py-4 shrink-0 flex items-center shadow-md font-bold cursor-pointer hover:bg-accent hover:brightness-110"
|
||||
>
|
||||
Fragen
|
||||
{t('ask')}
|
||||
<ChevronRight className="w-5 h-5 ml-2 -mr-1" />
|
||||
</Button>
|
||||
</form>
|
||||
@@ -160,7 +160,7 @@ export default function Hero({ data }: { data?: any }) {
|
||||
<div className="flex flex-col sm:flex-row justify-center md:justify-start gap-4 md:gap-6">
|
||||
<div>
|
||||
<Button
|
||||
href="/contact"
|
||||
href={`/${locale}/${locale === 'de' ? 'kontakt' : 'contact'}`}
|
||||
variant="white"
|
||||
size="lg"
|
||||
className="group w-full sm:w-auto h-14 md:h-16 px-8 md:px-10 text-base md:text-lg hover:scale-105 transition-all outline-none"
|
||||
|
||||
61
components/search/AISearchResults.test.tsx
Normal file
61
components/search/AISearchResults.test.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import * as React from 'react';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { render } from '@testing-library/react';
|
||||
import { AISearchResults } from './AISearchResults';
|
||||
|
||||
// Mock lucide-react
|
||||
vi.mock('lucide-react', () => ({
|
||||
ArrowUp: () => <div data-testid="arrow-up" />,
|
||||
X: () => <div data-testid="close-icon" />,
|
||||
Sparkles: () => <div data-testid="sparkles" />,
|
||||
ChevronRight: () => <div data-testid="chevron-right" />,
|
||||
RotateCcw: () => <div data-testid="rotate-ccw" />,
|
||||
Copy: () => <div data-testid="copy" />,
|
||||
Check: () => <div data-testid="check" />,
|
||||
}));
|
||||
|
||||
// Mock next/link
|
||||
vi.mock('next/link', () => ({
|
||||
default: ({ children, href }: any) => <a href={href}>{children}</a>,
|
||||
}));
|
||||
|
||||
// Mock useAnalytics
|
||||
vi.mock('../analytics/useAnalytics', () => ({
|
||||
useAnalytics: () => ({
|
||||
trackEvent: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock react-markdown
|
||||
vi.mock('react-markdown', () => ({
|
||||
default: ({ children }: any) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
// Mock remark-gfm
|
||||
vi.mock('remark-gfm', () => ({
|
||||
default: {},
|
||||
}));
|
||||
|
||||
// Mock next-intl
|
||||
vi.mock('next-intl', () => ({
|
||||
useTranslations: () => {
|
||||
const translate = (key: string) => key;
|
||||
translate.raw = (key: string) => {
|
||||
if (key === 'loadingTexts') return ['Lade...', 'Denke nach...'];
|
||||
if (key === 'prompts') return ['Prompt 1', 'Prompt 2'];
|
||||
return [];
|
||||
};
|
||||
return translate;
|
||||
},
|
||||
useLocale: () => 'en',
|
||||
}));
|
||||
|
||||
describe('AISearchResults Component Test (TDD)', () => {
|
||||
it('renders correctly when open', () => {
|
||||
const handleClose = vi.fn();
|
||||
const { container } = render(
|
||||
<AISearchResults isOpen={true} onClose={handleClose} />
|
||||
);
|
||||
expect(container).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -8,14 +8,9 @@ import { AnalyticsEvents } from '../analytics/analytics-events';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useTranslations } from 'next-intl';
|
||||
const AIOrb = dynamic(() => import('./AIOrb'), { ssr: false });
|
||||
|
||||
const LOADING_TEXTS = [
|
||||
'Durchsuche das Kabelhandbuch... 📖',
|
||||
'Frage den Senior-Ingenieur... 👴🔧',
|
||||
'Frage ChatGPTs Cousin 2. Grades... 🤖',
|
||||
];
|
||||
|
||||
interface ProductMatch {
|
||||
id: string;
|
||||
title: string;
|
||||
@@ -51,7 +46,13 @@ export function AISearchResults({
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [copiedIndex, setCopiedIndex] = useState<number | null>(null);
|
||||
const [copiedAll, setCopiedAll] = useState(false);
|
||||
const [loadingText, setLoadingText] = useState(LOADING_TEXTS[0]);
|
||||
const t = useTranslations('AISearch');
|
||||
|
||||
// We load texts dynamically from translations
|
||||
const loadingTexts = t.raw('loadingTexts') as string[];
|
||||
const prompts = t.raw('prompts') as string[];
|
||||
|
||||
const [loadingText, setLoadingText] = useState(loadingTexts[0]);
|
||||
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
@@ -141,11 +142,11 @@ export function AISearchResults({
|
||||
setTimeout(() => {
|
||||
setIsLoading(true);
|
||||
// Start rotating loading texts
|
||||
let textIdx = Math.floor(Math.random() * LOADING_TEXTS.length);
|
||||
setLoadingText(LOADING_TEXTS[textIdx]);
|
||||
let textIdx = Math.floor(Math.random() * loadingTexts.length);
|
||||
setLoadingText(loadingTexts[textIdx]);
|
||||
loadingIntervalRef.current = setInterval(() => {
|
||||
textIdx = (textIdx + 1) % LOADING_TEXTS.length;
|
||||
setLoadingText(LOADING_TEXTS[textIdx]);
|
||||
textIdx = (textIdx + 1) % loadingTexts.length;
|
||||
setLoadingText(loadingTexts[textIdx]);
|
||||
}, 2500);
|
||||
}, 400);
|
||||
|
||||
@@ -191,8 +192,8 @@ export function AISearchResults({
|
||||
console.error(err);
|
||||
const msg =
|
||||
err.name === 'AbortError'
|
||||
? 'Anfrage hat zu lange gedauert. Bitte versuche es erneut.'
|
||||
: err.message || 'Ein Fehler ist aufgetreten.';
|
||||
? t('timeoutError')
|
||||
: err.message || t('genericError');
|
||||
|
||||
// Show error as a system message in the chat instead of a separate error banner
|
||||
setMessages((prev) => [
|
||||
@@ -248,7 +249,7 @@ export function AISearchResults({
|
||||
|
||||
const handleCopyChat = () => {
|
||||
const fullChat = messages
|
||||
.map((m) => `${m.role === 'user' ? 'Du' : 'Ohm'}:\n${m.content}`)
|
||||
.map((m) => `${m.role === 'user' ? t('you') : 'Ohm'}:\n${m.content}`)
|
||||
.join('\n\n');
|
||||
handleCopy(fullChat);
|
||||
};
|
||||
@@ -294,7 +295,7 @@ export function AISearchResults({
|
||||
<div>
|
||||
<h2 className="text-white font-bold text-sm tracking-wide">Ohm</h2>
|
||||
<p className="text-[10px] text-white/30 font-medium tracking-wider uppercase">
|
||||
{isLoading ? 'Denkt nach...' : error ? 'Fehler aufgetreten' : 'Online'}
|
||||
{isLoading ? t('thinking') : error ? t('errorStatus') : t('online')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -303,14 +304,14 @@ export function AISearchResults({
|
||||
<button
|
||||
onClick={handleCopyChat}
|
||||
className="flex items-center gap-1.5 text-[10px] font-bold text-white/40 hover:text-white/80 transition-all duration-200 hover:bg-white/5 rounded-full px-3 py-1.5 cursor-pointer uppercase tracking-wider"
|
||||
title="gesamten Chat kopieren"
|
||||
title={t('copyChatTitle')}
|
||||
>
|
||||
{copiedAll ? (
|
||||
<Check className="w-3.5 h-3.5 text-accent" />
|
||||
) : (
|
||||
<Copy className="w-3.5 h-3.5" />
|
||||
)}
|
||||
<span>{copiedAll ? 'Kopiert' : 'Chat kopieren'}</span>
|
||||
<span>{copiedAll ? t('copied') : t('copyChat')}</span>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
@@ -336,16 +337,15 @@ export function AISearchResults({
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xl md:text-2xl font-bold text-white/80">
|
||||
Wie kann ich helfen?
|
||||
{t('howCanIHelp')}
|
||||
</p>
|
||||
<p className="text-sm text-white/30 mt-2 max-w-md">
|
||||
Beschreibe dein Projekt, frag nach bestimmten Kabeln, oder nenne mir deine
|
||||
Anforderungen.
|
||||
{t('helpDescription')}
|
||||
</p>
|
||||
</div>
|
||||
{/* Quick prompts */}
|
||||
<div className="flex flex-wrap justify-center gap-2 mt-4">
|
||||
{['Windpark 33kV Verkabelung', 'NYCWY 4x185', 'Erdkabel für Solarpark'].map(
|
||||
{prompts.map(
|
||||
(prompt) => (
|
||||
<button
|
||||
key={prompt}
|
||||
@@ -384,7 +384,7 @@ export function AISearchResults({
|
||||
? 'top-2 right-2 bg-primary/10 hover:bg-primary/20 text-primary/60 hover:text-primary'
|
||||
: 'top-2 right-2 bg-white/5 hover:bg-white/10 text-white/40 hover:text-white'
|
||||
}`}
|
||||
title="Nachricht kopieren"
|
||||
title={t('copyMessage')}
|
||||
>
|
||||
{copiedIndex === index ? (
|
||||
<Check className="w-3.5 h-3.5" />
|
||||
@@ -431,7 +431,7 @@ export function AISearchResults({
|
||||
{msg.role === 'assistant' && msg.products && msg.products.length > 0 && (
|
||||
<div className="mt-4 space-y-2 border-t border-white/[0.06] pt-4">
|
||||
<h4 className="text-[10px] font-bold tracking-widest uppercase text-white/30 mb-2">
|
||||
Empfohlene Produkte
|
||||
{t('recommendedProducts')}
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||
{msg.products.map((product, idx) => (
|
||||
@@ -509,7 +509,7 @@ export function AISearchResults({
|
||||
<AIOrb isThinking={false} hasError={true} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-sm font-bold text-red-300">Da ist was schiefgelaufen 😬</h3>
|
||||
<h3 className="text-sm font-bold text-red-300">{t('errorTitle')}</h3>
|
||||
<p className="text-xs text-red-300/60 mt-1">{error}</p>
|
||||
<button
|
||||
onClick={() => {
|
||||
@@ -519,7 +519,7 @@ export function AISearchResults({
|
||||
className="flex items-center gap-1.5 text-[10px] font-bold text-red-300/50 hover:text-red-300 mt-2 transition-colors cursor-pointer"
|
||||
>
|
||||
<RotateCcw className="w-3 h-3" />
|
||||
Nochmal versuchen
|
||||
{t('tryAgain')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -544,7 +544,7 @@ export function AISearchResults({
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
onKeyDown={onKeyDown}
|
||||
placeholder="Nachricht eingeben..."
|
||||
placeholder={t('placeholder')}
|
||||
className="flex-1 bg-transparent border-none text-white text-sm md:text-base px-5 py-4 focus:outline-none placeholder:text-white/20"
|
||||
disabled={isLoading}
|
||||
tabIndex={1}
|
||||
@@ -567,20 +567,20 @@ export function AISearchResults({
|
||||
? 'bg-accent text-primary shadow-lg shadow-accent/20 hover:shadow-accent/40 hover:scale-105 active:scale-95'
|
||||
: 'bg-white/5 text-white/20'
|
||||
}`}
|
||||
aria-label="Nachricht senden"
|
||||
aria-label={t('send')}
|
||||
>
|
||||
<ArrowUp className="w-4 h-4" strokeWidth={2.5} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-3 mt-2.5">
|
||||
<span className="text-[9px] uppercase tracking-[0.15em] font-medium text-white/15">
|
||||
Enter zum Senden · Esc zum Schließen
|
||||
{t('footerShortcuts')}
|
||||
</span>
|
||||
<span className="text-[9px] uppercase tracking-[0.15em] font-medium text-white/15">
|
||||
·
|
||||
</span>
|
||||
<span className="text-[9px] uppercase tracking-[0.15em] font-medium text-accent/40 flex items-center gap-1">
|
||||
🛡️ DSGVO-konform · EU-Server
|
||||
{t('footerPrivacy')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,9 +19,9 @@ layout: "fullBleed"
|
||||
showFairs={true}
|
||||
showJobs={false}
|
||||
fairs={[
|
||||
{ name: 'Intersolar München', date: '2024 / 2025', type: 'Messe', location: 'München', url: 'https://www.intersolar.de/' },
|
||||
{ name: 'Windenergietage Linstow', date: '2024 / 2025', type: 'Fachkongress', location: 'Linstow', url: 'https://www.windenergietage.de/' },
|
||||
{ name: 'Kabel-Workshop Wiesbaden', date: '2024 / 2025', type: 'Fachmesse', location: 'Wiesbaden' }
|
||||
{ name: 'Intersolar München', date: '2026', type: 'Messe', location: 'München', url: 'https://www.intersolar.de/' },
|
||||
{ name: 'Windenergietage Linstow', date: '2026', type: 'Fachkongress', location: 'Linstow', url: 'https://www.windenergietage.de/' },
|
||||
{ name: 'Kabel-Workshop Wiesbaden', date: '2026', type: 'Fachmesse', location: 'Wiesbaden' }
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -17,18 +17,18 @@ layout: "fullBleed"
|
||||
|
||||
<div className="container px-4 max-w-7xl mx-auto py-16">
|
||||
<blockquote>
|
||||
If classic trench construction is out of the question for space, environmental or cost reasons, we rely on the most modern trenchless techniques. We protect nature and minimize restoration costs.
|
||||
When traditional open-cut excavation is restricted by space, ecological standards, or cost constraints, trenchless engineering becomes the superior choice. We safeguard the environment while driving down surface restoration costs.
|
||||
</blockquote>
|
||||
|
||||
## Maximum Efficiency, Minimal Intervention
|
||||
## Maximum Efficiency, Minimal Impact
|
||||
|
||||
Trenchless line laying is the smartest solution for complex construction projects. This process shows its absolute strengths particularly when crossing highly frequented roads, railway lines, highways or sensitive bodies of water. Traffic continues to flow, nature remains untouched and complex surfaces such as expensive paving or asphalt do not have to be destroyed.
|
||||
Trenchless utility laying represents the pinnacle of smart infrastructure deployment. This methodology demonstrates its absolute superiority when navigating busy transportation corridors, active rail tracks, highways, or sensitive ecological waterways. By keeping traffic flowing and preserving the natural topography, we completely eliminate the need to break open expensive paved surfaces or asphalt.
|
||||
|
||||
## HDD Boreholes and Earth Rockets
|
||||
## HDD Directional Drilling & Pneumatic Earth Rockets
|
||||
|
||||
With our specialized machinery, we master a wide variety of processes. With the **Horizontal Directional Drilling (HDD) process**, we steer the drill head precisely underground. We achieve drilling lengths of up to 250 meters in one piece and can pull in protective pipes with a diameter of up to 400 mm.
|
||||
Armed with a state-of-the-art, specialized fleet of drilling rigs, we master complex subterranean conditions. Utilizing the **Horizontal Directional Drilling (HDD)** method, we guide the drill head with centimeter-level accuracy beneath obstacles. This enables us to achieve continuous drilling lengths of up to 250 meters in a single run, pulling in high-durability protective conduits with diameters up to 400 mm.
|
||||
|
||||
For shorter distances, such as fast and gentle house connections, we use our **earth rockets** (soil displacement method). Here we achieve drilling lengths of up to 15 meters for pipes up to 160 mm in diameter - ideal for leaving front gardens and driveways completely intact.
|
||||
For localized, rapid deployments—such as residential service connections—we deploy high-speed **pneumatic earth rockets** (soil displacement). This method allows us to complete under-crossings of up to 15 meters for conduits up to 160 mm in diameter, ensuring pristine driveways, manicured gardens, and pedestrian walks remain completely undisturbed.
|
||||
|
||||
<DeepDrillAnimation />
|
||||
</div>
|
||||
|
||||
@@ -17,18 +17,18 @@ layout: "fullBleed"
|
||||
|
||||
<div className="container px-4 max-w-7xl mx-auto py-16">
|
||||
<blockquote>
|
||||
A life without electricity or high-speed internet is unimaginable today - that is why we lay cables. We build the backbone of the digital society.
|
||||
Modern life depends entirely on seamless energy and ultra-fast connectivity. By building robust, high-performance physical networks, we lay the groundwork for a truly connected future.
|
||||
</blockquote>
|
||||
|
||||
## The Backbone of Digitalization
|
||||
## The Digital Backbone: Precision-Driven Fiber Expansion
|
||||
|
||||
Fast internet and state-of-the-art telecommunications networks require an absolutely smooth and highly professional network expansion. With highly specialized assembly teams and our own modern fleet of machinery, E-TIB ensures the highest execution quality in nationwide fiber optic expansion.
|
||||
High-speed fiber optics and next-generation telecommunications networks demand a zero-tolerance approach to execution quality. At E-TIB, we mobilize highly trained technical teams and an advanced, company-owned fleet of machinery to deliver flawless broadband expansion across the country.
|
||||
|
||||
## FTTX Expansion from a Single Source
|
||||
## FTTX Deployment: A Seamless, End-to-End Solution
|
||||
|
||||
As a powerful partner for telecommunications providers, we cover every step of physical network creation. We take over the construction of complete empty conduit routes as well as cable and pipe trenches. In addition, we position and build multi-function enclosures at strategic network nodes.
|
||||
As a trusted, tier-one partner for leading telecommunications and grid providers, we cover the entire physical lifecycle of network creation. From excavating precise cable and utility trenches to laying complete, high-capacity conduit paths, we manage every phase of civil engineering. We also handle the installation and positioning of strategic multi-function enclosures (MFG) at critical network hub points.
|
||||
|
||||
Our service does not end with pure civil engineering: We take care of the professional pulling and blowing in of highly sensitive fiber optic and telecommunication cables over long distances and carry out all required cable assemblies. In this way, we guarantee that the digital infrastructure can go into operation safely and performantly.
|
||||
Our expertise goes far beyond standard civil works. We perform the specialized pulling and pneumatic blowing of highly sensitive fiber optic and telecommunication cables over expansive, complex distances. Combining this with state-of-the-art splicing, testing, and cable assembly, we ensure your digital infrastructure operates with maximum throughput, security, and long-term reliability.
|
||||
</div>
|
||||
|
||||
<ServiceDetailGrid
|
||||
|
||||
@@ -17,20 +17,20 @@ layout: "fullBleed"
|
||||
|
||||
<div className="container px-4 max-w-7xl mx-auto py-16">
|
||||
<blockquote>
|
||||
Everywhere digging and drilling is happening, but unfortunately a lot also goes wrong. We focus on quality, the highest safety standards and legally compliant documentation - so that your construction project does not turn into a cost trap.
|
||||
While underground grid construction is expanding rapidly, execution quality varies widely. We counter this with elite craftsmanship, strict safety protocols, and ironclad documentation—ensuring your infrastructure project remains an asset, not a liability.
|
||||
</blockquote>
|
||||
|
||||
## Quality instead of a Cost Trap
|
||||
## Precision over Compromise: Preventing Infrastructure Failures
|
||||
|
||||
On many construction sites, cheap providers from abroad are increasingly being used, which often leads to **safety standards and documentation requirements being ignored**. The consequences are severe: accidents, damaged external lines (such as electricity, water and gas) as well as massive construction delays and expensive supplements. A supposed bargain quickly turns out to be a financial burden.
|
||||
Modern civil engineering is plagued by low-cost contractors who routinely **compromise on safety margins and technical documentation**. The repercussions are severe: catastrophic utility strikes (power, water, gas, telecom), costly project delays, and massive budget overruns. What begins as a low-bid shortcut quickly turns into a financial and legal nightmare.
|
||||
|
||||
Particularly for funded infrastructure projects, retrieving the funding is extremely problematic without absolutely seamless, standard-compliant documentation.
|
||||
Furthermore, public or subsidized infrastructure projects require meticulously structured, standard-compliant documentation. Without flawless records, the release of critical funding and subsidies is regularly delayed or denied.
|
||||
|
||||
## Everything from a Single Source
|
||||
## Integrated Excellence from a Single Source
|
||||
|
||||
Thanks to our many years of experience, we identify problems early on and work out safe and satisfactory solutions for our clients in advance. E-TIB GmbH has fully specialized in cable civil engineering.
|
||||
At E-TIB, we leverage decades of collective expertise to anticipate structural and geological bottlenecks long before the first shovel hits the ground. Our specialization in cable civil engineering guarantees that every deployment runs with absolute precision.
|
||||
|
||||
We offer you the complete range of services for the construction of cable routes from a single source - with our own ultra-modern machine fleet and permanent specialist staff from the region. In direct cooperation with our partner office, E-TIB Ingenieurgesellschaft mbH, we can also rule out planning errors at an early stage and sovereignly meet even the most complex documentation requirements.
|
||||
We deliver turnkey cable routes entirely in-house. Our operations are powered by a cutting-edge fleet of heavy machinery and staffed exclusively by highly trained, permanent regional professionals. Working in close synergy with our sister firm, **E-TIB Ingenieurgesellschaft mbH**, we eliminate planning oversights at the inception phase and effortlessly handle the most complex GIS and administrative documentation requirements in the industry.
|
||||
|
||||
<DataGridPulse />
|
||||
</div>
|
||||
|
||||
@@ -27,7 +27,7 @@ layout: "fullBleed"
|
||||
description: "Classic trench construction and professional laying of medium and low voltage cables.",
|
||||
tag: "Energy",
|
||||
size: "large",
|
||||
href: "/en/kabeltiefbau",
|
||||
href: "/en/cable-civil-engineering",
|
||||
image: { url: "/assets/photos/DSC01123.JPG", alt: "Cable Construction" }
|
||||
},
|
||||
{
|
||||
@@ -35,7 +35,7 @@ layout: "fullBleed"
|
||||
description: "Horizontal directional drilling (HDD) for trenchless, surface-friendly installation.",
|
||||
tag: "Innovation",
|
||||
size: "medium",
|
||||
href: "/en/bohrtechnik",
|
||||
href: "/en/drilling-technology",
|
||||
image: { url: "/assets/photos/DSC08653.JPG", alt: "Drilling Technology" }
|
||||
},
|
||||
{
|
||||
@@ -43,7 +43,7 @@ layout: "fullBleed"
|
||||
description: "Expansion of future-proof broadband networks (FTTX).",
|
||||
tag: "Communication",
|
||||
size: "small",
|
||||
href: "/en/glasfaser",
|
||||
href: "/en/fiber-optics",
|
||||
image: { url: "/assets/photos/DSC01129.JPG", alt: "Fiber Optics" }
|
||||
},
|
||||
{
|
||||
@@ -51,7 +51,7 @@ layout: "fullBleed"
|
||||
description: "Route planning, approval procedures, and precise GIS documentation.",
|
||||
tag: "Digital",
|
||||
size: "accent",
|
||||
href: "/en/planung"
|
||||
href: "/en/planning"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -19,9 +19,9 @@ layout: "fullBleed"
|
||||
showFairs={true}
|
||||
showJobs={false}
|
||||
fairs={[
|
||||
{ name: 'Intersolar Munich', date: '2024 / 2025', type: 'Fair', location: 'Munich', url: 'https://www.intersolar.de/' },
|
||||
{ name: 'Wind Energy Days Linstow', date: '2024 / 2025', type: 'Specialist Congress', location: 'Linstow', url: 'https://www.windenergietage.de/' },
|
||||
{ name: 'Cable Workshop Wiesbaden', date: '2024 / 2025', type: 'Trade Fair', location: 'Wiesbaden' }
|
||||
{ name: 'Intersolar Munich', date: '2026', type: 'Fair', location: 'Munich', url: 'https://www.intersolar.de/' },
|
||||
{ name: 'Wind Energy Days Linstow', date: '2026', type: 'Specialist Congress', location: 'Linstow', url: 'https://www.windenergietage.de/' },
|
||||
{ name: 'Cable Workshop Wiesbaden', date: '2026', type: 'Trade Fair', location: 'Wiesbaden' }
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -17,18 +17,18 @@ layout: "fullBleed"
|
||||
|
||||
<div className="container px-4 max-w-7xl mx-auto py-16">
|
||||
<blockquote>
|
||||
We offer our clients everything from a single source - from the rough planning of your project idea and cable civil engineering to the construction of your renewable energy project.
|
||||
We offer our clients unified, end-to-end project orchestration—from raw feasibility mapping to technical execution and grid connection for utility-scale renewable energy assets.
|
||||
</blockquote>
|
||||
|
||||
## Foundation for Project Success
|
||||
## The Foundation of Project Viability
|
||||
|
||||
Solid planning is the crucial foundation for every successful infrastructure project. Projects often fail or are massively delayed because the complexity of civil engineering planning and approval procedures was underestimated. Through our combined competence, you avoid expensive planning errors and frictional losses at the interfaces.
|
||||
A rigorous engineering layout is the cornerstone of every successful infrastructure initiative. Many large-scale utility projects encounter critical delays or structural failures simply because the nuances of subterranean logistics and regulatory approvals were underestimated. By bridging the gap between design and physical field operations, we proactively mitigate risk, eliminating friction at critical operational interfaces.
|
||||
|
||||
Both for the highly topical broadband expansion and for the demanding grid connection of solar or wind projects, absolutely precise and high-quality cable route planning is required. To do this, we inspect the route on site, analyze all potential construction conflicts at an early stage and ensure that all necessary applications for the laying are submitted completely and on time to the authorities.
|
||||
Whether executing fast-paced broadband fiber rollouts or designing complex grid connections for gigawatt-scale wind and solar farms, precision is mandatory. Our engineering teams perform comprehensive on-site route audits, map potential utility conflicts early, and guarantee all localized regulatory, environmental, and municipal permits are prepared and filed seamlessly.
|
||||
|
||||
## Strong Engineering Competence
|
||||
## Elite Engineering & Technical Advisory
|
||||
|
||||
In close cooperation with our specialized partner engineering firm, **E-TIB Ingenieurgesellschaft mbH**, we accompany you through all service phases. Whether structural planning for route optimization, complex approval planning or detailed execution planning - we steer your project from the first feasibility study to successful construction supervision and acceptance.
|
||||
In strategic partnership with **E-TIB Ingenieurgesellschaft mbH**, we guide your development through every single phase of the project lifecycle. From initial corridor scouting and route optimization to obtaining formal municipal approvals and producing final, field-ready execution plans—we provide the strategic oversight and specialized engineering supervision needed to ensure a flawless handover.
|
||||
</div>
|
||||
|
||||
<ServiceDetailGrid
|
||||
|
||||
@@ -18,7 +18,7 @@ layout: "fullBleed"
|
||||
{
|
||||
id: "danny-joseph",
|
||||
name: "Danny Joseph",
|
||||
position: "Management",
|
||||
position: "Managing Director",
|
||||
email: "d.joseph@e-tib.com",
|
||||
phone: "+49 1520 7230518",
|
||||
image: "/assets/photos/team/danny.jpg"
|
||||
|
||||
@@ -17,20 +17,20 @@ layout: "fullBleed"
|
||||
|
||||
<div className="container px-4 max-w-7xl mx-auto py-16">
|
||||
<blockquote>
|
||||
We work with highly precise GPS equipment for the seamless documentation of our clients' projects. For this purpose, we independently develop app integrations to automate surveying.
|
||||
Leveraging ultra-precise, state-of-the-art GNSS/GPS technology and proprietary app integrations, we automate the surveying process to deliver seamless, real-time documentation.
|
||||
</blockquote>
|
||||
|
||||
## Highest Precision Protects Against Surprises
|
||||
## Centimeter Precision Prevents Costly Operations
|
||||
|
||||
In today's times, legal documentation requirements and strict safety standards are growing rapidly. Many conventional civil engineering companies are hardly able to cleanly meet these highly complex requirements - especially for funded broadband expansion. Missing or incorrect documentation inevitably leads to massive problems during construction acceptance and project financing.
|
||||
Regulatory documentation mandates and safety benchmarks are expanding rapidly. Traditional civil engineering contractors frequently struggle to meet these highly complex data requirements—especially under strict public funding frameworks. Neglecting or incorrectly mapping underground routes inevitably triggers major roadblocks during project handovers, audit reviews, and long-term asset management.
|
||||
|
||||
We identified this bottleneck early on and proactively specialized in the highly precise surveying and digital documentation of cable routes.
|
||||
Recognizing this critical bottleneck early, we pro-actively integrated advanced geodetic surveying and digital mapping directly into our core operations.
|
||||
|
||||
## Digital Recording at the State of the Art
|
||||
## Cutting-Edge Digital Twin & GIS Records
|
||||
|
||||
With state-of-the-art GPS technology and self-developed digital app integrations, we ensure highly automated, seamless and absolutely legally compliant recording of all laid lines and systems.
|
||||
Using advanced GNSS/GPS devices and in-house developed digital workflows, we capture the precise physical reality of every conduit and utility in real-time. This automated process generates a highly accurate digital twin of your underground infrastructure.
|
||||
|
||||
In addition to classic GPS staking and surveying, we create complete geodatabases for our clients for transparent project accounting and offer seamless 360° photo and video recording of the construction field. In this way, you keep full track of your underground infrastructure at all times.
|
||||
Beyond classic surveying, staking, and as-built mapping, we construct fully integrated geodatabases for transparent project accounting. We also provide complete, immersive 360° photo and video documentation of the construction area before and after ground-breaking. This ensures you maintain full transparency, legal compliance, and total control over your physical assets.
|
||||
</div>
|
||||
|
||||
<ServiceDetailGrid
|
||||
|
||||
@@ -21,42 +21,42 @@ layout: "fullBleed"
|
||||
{
|
||||
title: 'ISO 14001:2015',
|
||||
description: 'Environmental Management System',
|
||||
pdfUrl: '/assets/certificates/231214_Zertifikat ISO 14001 Umweltmanagement.pdf',
|
||||
pdfUrl: '/assets/certificates/iso14001.pdf',
|
||||
type: 'iso',
|
||||
date: '14.12.2023',
|
||||
},
|
||||
{
|
||||
title: 'ISO 9001:2015',
|
||||
description: 'Quality Management System',
|
||||
pdfUrl: '/assets/certificates/231214_Zertifikat ISO 9001 Qualitätsmanagement.pdf',
|
||||
pdfUrl: '/assets/certificates/iso9001.pdf',
|
||||
type: 'iso',
|
||||
date: '14.12.2023',
|
||||
},
|
||||
{
|
||||
title: 'DIN EN ISO 45001:2018',
|
||||
description: 'Occupational Health and Safety',
|
||||
pdfUrl: '/assets/certificates/Zertifizierung DIN EN ISO 45001 bis 05122028.pdf',
|
||||
pdfUrl: '/assets/certificates/iso45001.pdf',
|
||||
type: 'iso',
|
||||
date: '05.12.2025',
|
||||
},
|
||||
{
|
||||
title: 'Exemption Certificate',
|
||||
description: 'According to § 48 b EStG',
|
||||
pdfUrl: '/assets/certificates/240209_Freistellungsbescheinigung § 48 b.pdf',
|
||||
pdfUrl: '/assets/certificates/freistellung.pdf',
|
||||
type: 'tax',
|
||||
date: '09.02.2024',
|
||||
},
|
||||
{
|
||||
title: 'Proof § 13b UStG',
|
||||
description: 'Tax liability of the service recipient',
|
||||
pdfUrl: '/assets/certificates/240209_Nachweis § 13 b.pdf',
|
||||
pdfUrl: '/assets/certificates/nachweis13b.pdf',
|
||||
type: 'tax',
|
||||
date: '09.02.2024',
|
||||
},
|
||||
{
|
||||
title: 'Certificate in Tax Matters',
|
||||
description: 'Tax Office Certificate',
|
||||
pdfUrl: '/assets/certificates/250213_Bescheinigung in Steuersachen.pdf',
|
||||
pdfUrl: '/assets/certificates/bescheinigung.pdf',
|
||||
type: 'tax',
|
||||
date: '13.02.2025',
|
||||
}
|
||||
|
||||
BIN
data/Mengen Homepage.docx
Normal file
BIN
data/Mengen Homepage.docx
Normal file
Binary file not shown.
BIN
data/Orte 2016 - 2022.docx
Normal file
BIN
data/Orte 2016 - 2022.docx
Normal file
Binary file not shown.
BIN
data/Orte 2023.docx
Normal file
BIN
data/Orte 2023.docx
Normal file
Binary file not shown.
BIN
data/Orte 2024.docx
Normal file
BIN
data/Orte 2024.docx
Normal file
Binary file not shown.
14
data/Orte 2024.txt
Normal file
14
data/Orte 2024.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
Windpark, 48366 Laer
|
||||
PV-Anlage, 89434 Blindheim OT Wolpertstetten
|
||||
Windpark, 24893 Taarstedt
|
||||
Windpark, 24891 Schnarup-Thumby
|
||||
Windpark, 23818 Neuengörs
|
||||
PV-Anlage, 15326 Petershagen
|
||||
Windpark, 16866 Gumtow OT Schrepkow
|
||||
PV-Anlage, 14641 Nauen
|
||||
PV-Anlage, 23619 Badendorf
|
||||
PV-Anlage, 14959 Wiesenhagen
|
||||
PV-Anlage, 17089 Bartow
|
||||
PV-Anlage, 15345 Prötzel OT Sternebeck
|
||||
Windpark, 15848 Beeskow
|
||||
PV-Anlage, 15328 Gorgast
|
||||
BIN
data/Orte 2025.docx
Normal file
BIN
data/Orte 2025.docx
Normal file
Binary file not shown.
9
data/Orte 2025.txt
Normal file
9
data/Orte 2025.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
PV-Anlage, 18334 Dettmannsdorf
|
||||
PV-Anlage, 59320 Ennigerloh-Oelde
|
||||
PV-Anlage, 93176 Beratzhausen
|
||||
Windpark, 16845 Neustadt Dosse
|
||||
PV-Anlage, 16845 Stüdenitz-Schönermark
|
||||
Windpark, 48346 Ostbevern
|
||||
Windpark, 27804 Berne
|
||||
Windpark, 17166 Dalkendorf
|
||||
Windpark, 29575 Altenmedingen
|
||||
BIN
data/Orte 2026.docx
Normal file
BIN
data/Orte 2026.docx
Normal file
Binary file not shown.
@@ -9,7 +9,12 @@
|
||||
"ueber-uns": "ueber-uns",
|
||||
"agb": "agb",
|
||||
"start": "start",
|
||||
"messen": "messen"
|
||||
"messen": "messen",
|
||||
"kabeltiefbau": "kabeltiefbau",
|
||||
"glasfaser": "glasfaser",
|
||||
"bohrtechnik": "bohrtechnik",
|
||||
"planung": "planung",
|
||||
"vermessung": "vermessung"
|
||||
},
|
||||
"products": {},
|
||||
"categories": {}
|
||||
@@ -97,7 +102,7 @@
|
||||
"office": "Hauptsitz Guben",
|
||||
"address": "Gewerbestraße 22\n03172 Guben\nDeutschland",
|
||||
"phone": "+49 (0) 3561 / 68577 33",
|
||||
"email": "d.joseph@e-tib.com"
|
||||
"email": "info@e-tib.com"
|
||||
},
|
||||
"hours": {
|
||||
"title": "Öffnungszeiten",
|
||||
@@ -146,7 +151,15 @@
|
||||
"hero": {
|
||||
"title": "DIE EXPERTEN FÜR <green>KABELTIEFBAU</green>",
|
||||
"subtitle": "Wir helfen beim Ausbau der Energiekabelnetze für eine grüne Zukunft.",
|
||||
"cta": "Jetzt anfragen"
|
||||
"cta": "Jetzt anfragen",
|
||||
"searchPlaceholder": "Projekt beschreiben oder Kabel suchen...",
|
||||
"ask": "Fragen",
|
||||
"placeholder1": "Querschnittsberechnung für 110kV Trasse",
|
||||
"placeholder2": "Wie schwer ist NAYY 4x150?",
|
||||
"placeholder3": "Ich plane einen Solarpark, was brauche ich?",
|
||||
"placeholder4": "Unterschied zwischen N2XSY und NAY2XSY?",
|
||||
"placeholder5": "Mittelspannungskabel für Windkraftanlage",
|
||||
"placeholder6": "Welches Aluminiumkabel für 20kV?"
|
||||
},
|
||||
"video": {
|
||||
"title": "Vom ersten Spatenstich bis zum Netzanschluss – wir bauen die Infrastruktur von morgen."
|
||||
@@ -166,12 +179,14 @@
|
||||
"CallToAction": {
|
||||
"title": "Bereit für Ihr Projekt?",
|
||||
"description": "Wir suchen stets nach neuen Herausforderungen und starken Partnern. Kontaktieren Sie uns für eine unverbindliche Beratung zu Ihrem Vorhaben.",
|
||||
"ctaLabel": "Jetzt Kontakt aufnehmen"
|
||||
"ctaLabel": "Jetzt Kontakt aufnehmen",
|
||||
"ctaHref": "/de/kontakt"
|
||||
},
|
||||
"JobListingBlock": {
|
||||
"fairsTitle": "Nächste Messetermine",
|
||||
"emptyStateMessage": "Aktuell sind alle Positionen besetzt. Senden Sie uns gerne eine Initiativbewerbung!",
|
||||
"emptyStateLinkText": "Jetzt Kontakt aufnehmen",
|
||||
"emptyStateLinkHref": "/de/kontakt",
|
||||
"date": "Datum",
|
||||
"location": "Location",
|
||||
"booth": "Stand",
|
||||
@@ -222,6 +237,10 @@
|
||||
"title": "Gründung E-TIB GmbH",
|
||||
"desc": "Ausführung elektrischer Infrastrukturprojekte, Kabeltiefbau und Horizontalspülbohrungen."
|
||||
},
|
||||
"2025": {
|
||||
"title": "Gründung E-TIB Bohrtechnik GmbH",
|
||||
"desc": "Spezialisierung auf präzise Horizontalspülbohrungen in allen Bodenklassen."
|
||||
},
|
||||
"2019_ing": {
|
||||
"title": "Gründung E-TIB Ingenieurgesellschaft",
|
||||
"desc": "Genehmigungs- und Ausführungsplanung, komplexe Querungen sowie Netzanschlussplanung."
|
||||
@@ -229,10 +248,6 @@
|
||||
"2019_verw": {
|
||||
"title": "Gründung E-TIB Verwaltung GmbH",
|
||||
"desc": "Zentrale Dienste, Erwerb, Vermietung, Verpachtung und Verwaltung von Immobilien und Maschinen."
|
||||
},
|
||||
"2025": {
|
||||
"title": "Gründung E-TIB Bohrtechnik GmbH",
|
||||
"desc": "Spezialisierung auf präzise Horizontalspülbohrungen in allen Bodenklassen."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -261,5 +276,55 @@
|
||||
"method": "Tiefbau & Spülbohrung (HDD)"
|
||||
},
|
||||
"learnMore": "Zum Projekt"
|
||||
},
|
||||
"TeamGrid": {
|
||||
"badge": "Persönliche Beratung",
|
||||
"title": "Ihre Ansprechpartner",
|
||||
"subtitle": "Sprechen Sie direkt mit unseren Experten für Ihr regionales Projekt.",
|
||||
"management": "Geschäftsführung",
|
||||
"branchETIB": "E-TIB GmbH",
|
||||
"branchIng": "Ingenieurgesellschaft",
|
||||
"branchBohr": "Bohrtechnik"
|
||||
},
|
||||
"AISearch": {
|
||||
"loadingTexts": [
|
||||
"Durchsuche das Kabelhandbuch... 📖",
|
||||
"Frage den Senior-Ingenieur... 👴🔧",
|
||||
"Frage ChatGPTs Cousin 2. Grades... 🤖"
|
||||
],
|
||||
"thinking": "Denkt nach...",
|
||||
"errorStatus": "Fehler aufgetreten",
|
||||
"online": "Online",
|
||||
"copyChat": "Chat kopieren",
|
||||
"copyChatTitle": "gesamten Chat kopieren",
|
||||
"copied": "Kopiert",
|
||||
"close": "Schließen",
|
||||
"howCanIHelp": "Wie kann ich helfen?",
|
||||
"helpDescription": "Beschreibe dein Projekt, frag nach bestimmten Kabeln, oder nenne mir deine Anforderungen.",
|
||||
"prompts": [
|
||||
"Windpark 33kV Verkabelung",
|
||||
"NYCWY 4x185",
|
||||
"Erdkabel für Solarpark"
|
||||
],
|
||||
"you": "Du",
|
||||
"copyMessage": "Nachricht kopieren",
|
||||
"recommendedProducts": "Empfohlene Produkte",
|
||||
"errorTitle": "Da ist was schiefgelaufen 😬",
|
||||
"tryAgain": "Nochmal versuchen",
|
||||
"placeholder": "Nachricht eingeben...",
|
||||
"send": "Nachricht senden",
|
||||
"footerShortcuts": "Enter zum Senden · Esc zum Schließen",
|
||||
"footerPrivacy": "🛡️ DSGVO-konform · EU-Server",
|
||||
"timeoutError": "Anfrage hat zu lange gedauert. Bitte versuche es erneut.",
|
||||
"genericError": "Ein Fehler ist aufgetreten."
|
||||
},
|
||||
"ReferenceDetail": {
|
||||
"backToOverview": "Zurück zur Übersicht",
|
||||
"projectReference": "Projektreferenz",
|
||||
"location": "Ort",
|
||||
"client": "Auftraggeber",
|
||||
"period": "Zeitraum",
|
||||
"scopeTitle": "Leistungsumfang & Projektbeschreibung",
|
||||
"viewAll": "Alle Referenzen ansehen"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,12 @@
|
||||
"about-us": "ueber-uns",
|
||||
"terms": "agb",
|
||||
"start": "start",
|
||||
"trade-fairs": "messen"
|
||||
"trade-fairs": "messen",
|
||||
"cable-civil-engineering": "kabeltiefbau",
|
||||
"fiber-optics": "glasfaser",
|
||||
"drilling-technology": "bohrtechnik",
|
||||
"planning": "planung",
|
||||
"surveying": "vermessung"
|
||||
},
|
||||
"products": {},
|
||||
"categories": {}
|
||||
@@ -97,7 +102,7 @@
|
||||
"office": "Headquarters Guben",
|
||||
"address": "Gewerbestraße 22\n03172 Guben\nGermany",
|
||||
"phone": "+49 (0) 3561 / 68577 33",
|
||||
"email": "d.joseph@e-tib.com"
|
||||
"email": "info@e-tib.com"
|
||||
},
|
||||
"hours": {
|
||||
"title": "Opening Hours",
|
||||
@@ -146,7 +151,15 @@
|
||||
"hero": {
|
||||
"title": "THE EXPERTS FOR <green>UNDERGROUND CABLE ENGINEERING</green>",
|
||||
"subtitle": "We help expanding the energy cable networks for a green future.",
|
||||
"cta": "Request now"
|
||||
"cta": "Request now",
|
||||
"searchPlaceholder": "Describe project or search for cables...",
|
||||
"ask": "Ask",
|
||||
"placeholder1": "Cross-section calculation for 110kV route",
|
||||
"placeholder2": "How heavy is NAYY 4x150?",
|
||||
"placeholder3": "I'm planning a solar park, what do I need?",
|
||||
"placeholder4": "Difference between N2XSY and NAY2XSY?",
|
||||
"placeholder5": "Medium voltage cable for wind turbine",
|
||||
"placeholder6": "Which aluminum cable for 20kV?"
|
||||
},
|
||||
"video": {
|
||||
"title": "From the first spade cut to the grid connection – we build the infrastructure of tomorrow."
|
||||
@@ -166,12 +179,14 @@
|
||||
"CallToAction": {
|
||||
"title": "Ready for your project?",
|
||||
"description": "We are always looking for new challenges and strong partners. Contact us for a non-binding consultation about your project.",
|
||||
"ctaLabel": "Contact us now"
|
||||
"ctaLabel": "Contact us now",
|
||||
"ctaHref": "/en/contact"
|
||||
},
|
||||
"JobListingBlock": {
|
||||
"fairsTitle": "Upcoming Trade Fairs",
|
||||
"emptyStateMessage": "All positions are currently filled. Feel free to send us an unsolicited application!",
|
||||
"emptyStateLinkText": "Contact us now",
|
||||
"emptyStateLinkHref": "/en/contact",
|
||||
"date": "Date",
|
||||
"location": "Location",
|
||||
"booth": "Booth",
|
||||
@@ -222,6 +237,10 @@
|
||||
"title": "Foundation of E-TIB GmbH",
|
||||
"desc": "Execution of electrical infrastructure projects, cable civil engineering, and horizontal directional drilling."
|
||||
},
|
||||
"2025": {
|
||||
"title": "Foundation of E-TIB Bohrtechnik GmbH",
|
||||
"desc": "Specialization in precise horizontal directional drilling in all soil classes."
|
||||
},
|
||||
"2019_ing": {
|
||||
"title": "Foundation of E-TIB Ingenieurgesellschaft",
|
||||
"desc": "Permit and execution planning, complex crossings, and grid connection planning."
|
||||
@@ -229,10 +248,6 @@
|
||||
"2019_verw": {
|
||||
"title": "Foundation of E-TIB Verwaltung GmbH",
|
||||
"desc": "Central services, acquisition, leasing, and management of real estate and machinery."
|
||||
},
|
||||
"2025": {
|
||||
"title": "Foundation of E-TIB Bohrtechnik GmbH",
|
||||
"desc": "Specialization in precise horizontal directional drilling in all soil classes."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -261,5 +276,55 @@
|
||||
"method": "Civil engineering & HDD"
|
||||
},
|
||||
"learnMore": "Learn more"
|
||||
},
|
||||
"TeamGrid": {
|
||||
"badge": "Personal Consultation",
|
||||
"title": "Your Contacts",
|
||||
"subtitle": "Speak directly with our experts for your regional project.",
|
||||
"management": "Management",
|
||||
"branchETIB": "E-TIB GmbH",
|
||||
"branchIng": "Engineering Company",
|
||||
"branchBohr": "Drilling Technology"
|
||||
},
|
||||
"AISearch": {
|
||||
"loadingTexts": [
|
||||
"Searching the cable manual... 📖",
|
||||
"Asking the senior engineer... 👴🔧",
|
||||
"Asking ChatGPT's 2nd cousin... 🤖"
|
||||
],
|
||||
"thinking": "Thinking...",
|
||||
"errorStatus": "Error occurred",
|
||||
"online": "Online",
|
||||
"copyChat": "Copy chat",
|
||||
"copyChatTitle": "copy entire chat",
|
||||
"copied": "Copied",
|
||||
"close": "Close",
|
||||
"howCanIHelp": "How can I help?",
|
||||
"helpDescription": "Describe your project, ask for specific cables, or tell me your requirements.",
|
||||
"prompts": [
|
||||
"Wind park 33kV cabling",
|
||||
"NYCWY 4x185",
|
||||
"Underground cable for solar park"
|
||||
],
|
||||
"you": "You",
|
||||
"copyMessage": "Copy message",
|
||||
"recommendedProducts": "Recommended Products",
|
||||
"errorTitle": "Something went wrong 😬",
|
||||
"tryAgain": "Try again",
|
||||
"placeholder": "Enter message...",
|
||||
"send": "Send message",
|
||||
"footerShortcuts": "Enter to send · Esc to close",
|
||||
"footerPrivacy": "🛡️ GDPR compliant · EU servers",
|
||||
"timeoutError": "Request took too long. Please try again.",
|
||||
"genericError": "An error occurred."
|
||||
},
|
||||
"ReferenceDetail": {
|
||||
"backToOverview": "Back to Overview",
|
||||
"projectReference": "Project Reference",
|
||||
"location": "Location",
|
||||
"client": "Client",
|
||||
"period": "Period",
|
||||
"scopeTitle": "Scope & Project Description",
|
||||
"viewAll": "View all References"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user