Compare commits

...

11 Commits

Author SHA1 Message Date
d2668e36eb chore(ci): run aggressive docker volume and system prune during maintenance to resolve ENOSPC errors
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 39s
Build & Deploy / 🧪 QA (push) Failing after 1m39s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 13s
2026-05-26 11:53:31 +02:00
5678ddcfd9 fix: interactive map visuals and docker dev setup
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 23s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🧪 QA (push) Successful in 1m6s
Build & Deploy / 🏗️ Build (push) Failing after 2m52s
Build & Deploy / 🔔 Notify (push) Successful in 1s
- Removed confusing SVG lines from map

- Restored distinct clickable markers for major references

- Fixed corepack failing to install pnpm v11 in Docker by explicitly forcing v10

- Added direct port mapping 3001:3001 to bypass proxy issues
2026-05-26 11:31:31 +02:00
06d5178614 feat(ui): add nice informative tooltips to footer quality badges
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 23s
Build & Deploy / 🧪 QA (push) Successful in 1m11s
Build & Deploy / 🏗️ Build (push) Successful in 2m48s
Build & Deploy / 🚀 Deploy (push) Successful in 27s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 45s
Build & Deploy / 🔔 Notify (push) Successful in 4s
2026-05-18 22:18:48 +02:00
a983696153 chore(ci): also purge hidden files in /opt/hostedtoolcache to reclaim disk space
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 18s
Build & Deploy / 🧪 QA (push) Successful in 1m4s
Build & Deploy / 🏗️ Build (push) Successful in 2m54s
Build & Deploy / 🚀 Deploy (push) Successful in 25s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 46s
Build & Deploy / 🔔 Notify (push) Successful in 4s
2026-05-18 14:46:27 +02:00
6908964df5 chore(ci): purge /opt/hostedtoolcache in maintenance step to free disk space
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🧪 QA (push) Has been cancelled
2026-05-18 14:45:46 +02:00
159587f545 chore(ci): make docker cleanup robust against hangs and log system disk usage
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 18s
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🧪 QA (push) Has been cancelled
2026-05-18 14:45:05 +02:00
c02b49644e chore(ci): log disk usage and add aggressive docker system volume prune
Some checks failed
Build & Deploy / 🧪 QA (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🔍 Prepare (push) Has been cancelled
2026-05-18 14:42:42 +02:00
313fa14c56 chore(ci): use aggressive docker system prune to resolve runner disk space issues
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 17s
Build & Deploy / 🧪 QA (push) Successful in 59s
Build & Deploy / 🏗️ Build (push) Failing after 1m13s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-05-18 14:39:36 +02:00
052be7d1c9 fix(referenzen): add cleanLocation helper to resolve compilation and linting errors
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 18s
Build & Deploy / 🧪 QA (push) Successful in 59s
Build & Deploy / 🏗️ Build (push) Failing after 1m15s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-05-18 14:36:30 +02:00
aef10f706c fix(pipeline): pass dynamic NPM_DOMAIN to docker build to prevent 404s, fix reference badge location cleaning, and enable glowing border on map
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 18s
Build & Deploy / 🧪 QA (push) Failing after 59s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-05-18 11:58:35 +02:00
35d9650626 fix(types): fix typescript errors breaking the pipeline
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 18s
Build & Deploy / 🧪 QA (push) Successful in 59s
Build & Deploy / 🏗️ Build (push) Failing after 1m19s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-05-17 15:41:49 +02:00
43 changed files with 1599 additions and 187 deletions

View File

@@ -43,9 +43,20 @@ jobs:
- name: 🧹 Maintenance (High Density Cleanup)
shell: bash
run: |
echo "Purging old build layers and dangling images..."
docker image prune -f
docker builder prune -f --filter "until=24h"
echo "=== System Disk Usage ==="
df -h || true
echo "=== Tool Cache Usage ==="
du -sh /opt/hostedtoolcache/* || true
du -sh /opt/hostedtoolcache/.[!.]* || true
echo "Purging old tool caches..."
rm -rf /opt/hostedtoolcache/* || true
rm -rf /opt/hostedtoolcache/.[!.]* || true
echo "Purging old build layers, unused volumes and images..."
docker builder prune -a -f || true
docker volume prune -f || true
docker system prune -a -f || true
echo "=== System Disk Usage After Prune ==="
df -h || true
- name: Checkout repository
uses: actions/checkout@v4
@@ -207,6 +218,7 @@ jobs:
UMAMI_WEBSITE_ID=${{ secrets.UMAMI_WEBSITE_ID || vars.UMAMI_WEBSITE_ID }}
UMAMI_API_ENDPOINT=${{ secrets.UMAMI_API_ENDPOINT || vars.UMAMI_API_ENDPOINT || 'https://analytics.infra.mintel.me' }}
NPM_TOKEN=${{ steps.auth.outputs.working_token }}
NPM_DOMAIN=${{ steps.auth.outputs.working_domain }}
tags: registry.infra.mintel.me/mintel/e-tib.com:${{ needs.prepare.outputs.image_tag }}
secrets: |
"NPM_TOKEN=${{ steps.auth.outputs.working_token }}"

View File

@@ -0,0 +1 @@
{"files":{".turbo/turbo-lint.log":{"size":10746,"mtime_nanos":1779098006288513580,"mode":420,"is_dir":false}},"order":[".turbo/turbo-lint.log"]}

View File

@@ -0,0 +1 @@
{"hash":"85340b254e0f10f1","duration":6595,"sha":"35d9650626b56a727e9c1981308145cb7b2f28aa","dirty_hash":"9cc3e16c40a10f42f7a963333e800e1384f047b1f387b18b5f37769c34f36609"}

Binary file not shown.

View File

@@ -0,0 +1 @@
{"files":{".turbo/turbo-typecheck.log":{"size":92,"mtime_nanos":1779097674383861303,"mode":420,"is_dir":false}},"order":[".turbo/turbo-typecheck.log"]}

View File

@@ -0,0 +1 @@
{"hash":"32905076da5f4894","duration":4492,"sha":"35d9650626b56a727e9c1981308145cb7b2f28aa","dirty_hash":"58f8ebc92a0452cbb0a09c5ebe800385fcc42ff82fee098bb7d6c8828bc526f0"}

BIN
.turbo/cache/32905076da5f4894.tar.zst vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
{"files":{".turbo/turbo-test.log":{"size":818,"mtime_nanos":1779097671747078359,"mode":420,"is_dir":false}},"order":[".turbo/turbo-test.log"]}

View File

@@ -0,0 +1 @@
{"hash":"7e2629942c881f80","duration":1855,"sha":"35d9650626b56a727e9c1981308145cb7b2f28aa","dirty_hash":"58f8ebc92a0452cbb0a09c5ebe800385fcc42ff82fee098bb7d6c8828bc526f0"}

BIN
.turbo/cache/7e2629942c881f80.tar.zst vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
{"files":{".turbo/turbo-lint.log":{"size":10746,"mtime_nanos":1779097675143794164,"mode":420,"is_dir":false}},"order":[".turbo/turbo-lint.log"]}

View File

@@ -0,0 +1 @@
{"hash":"85340b254e0f10f1","duration":5252,"sha":"35d9650626b56a727e9c1981308145cb7b2f28aa","dirty_hash":"58f8ebc92a0452cbb0a09c5ebe800385fcc42ff82fee098bb7d6c8828bc526f0"}

BIN
.turbo/cache/85340b254e0f10f1.tar.zst vendored Normal file

Binary file not shown.

119
.turbo/turbo-lint.log Normal file
View File

@@ -0,0 +1,119 @@

> e-tib-nextjs@2.2.12 lint /Volumes/Alpha SSD/Coding/e-tib.com
> eslint .
/Volumes/Alpha SSD/Coding/e-tib.com/app/[locale]/[slug]/page.tsx
1:20 warning 'redirect' is defined but never used @typescript-eslint/no-unused-vars
67:24 warning Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element
/Volumes/Alpha SSD/Coding/e-tib.com/app/[locale]/blog/[slug]/page.tsx
1:20 warning 'redirect' is defined but never used @typescript-eslint/no-unused-vars
39:24 warning Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element
/Volumes/Alpha SSD/Coding/e-tib.com/app/[locale]/error.tsx
7:30 warning 'AlertTriangle' is defined but never used @typescript-eslint/no-unused-vars
/Volumes/Alpha SSD/Coding/e-tib.com/app/[locale]/not-found.tsx
6:10 warning 'Terminal' is defined but never used @typescript-eslint/no-unused-vars
/Volumes/Alpha SSD/Coding/e-tib.com/app/[locale]/referenzen/[slug]/page.tsx
91:26 warning Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element
/Volumes/Alpha SSD/Coding/e-tib.com/app/[locale]/referenzen/page.tsx
1:21 warning 'Heading' is defined but never used @typescript-eslint/no-unused-vars
1:30 warning 'Badge' is defined but never used @typescript-eslint/no-unused-vars
/Volumes/Alpha SSD/Coding/e-tib.com/components/JsonLd.tsx
1:30 warning 'Graph' is defined but never used @typescript-eslint/no-unused-vars
43:31 warning '_' is assigned a value but never used @typescript-eslint/no-unused-vars
48:29 warning '_' is assigned a value but never used @typescript-eslint/no-unused-vars
/Volumes/Alpha SSD/Coding/e-tib.com/components/blocks/CertificatesBlock.tsx
6:10 warning 'Button' is defined but never used @typescript-eslint/no-unused-vars
9:10 warning 'FileText' is defined but never used @typescript-eslint/no-unused-vars
77:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders
Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.
Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).
/Volumes/Alpha SSD/Coding/e-tib.com/components/blocks/CertificatesBlock.tsx:77:5
75 |
76 | useEffect(() => {
> 77 | setIsMounted(true);
| ^^^^^^^^^^^^ Avoid calling setState() directly within an effect
78 | }, []);
79 |
80 | const badgeText = badge || 'Zertifizierungen & Nachweise'; react-hooks/set-state-in-effect
/Volumes/Alpha SSD/Coding/e-tib.com/components/blocks/CompetenceBentoGrid.tsx
53:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders
Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.
Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).
/Volumes/Alpha SSD/Coding/e-tib.com/components/blocks/CompetenceBentoGrid.tsx:53:5
51 |
52 | useEffect(() => {
> 53 | setIsMounted(true);
| ^^^^^^^^^^^^ Avoid calling setState() directly within an effect
54 | }, []);
55 |
56 | const badge = props.badge || data?.badge || 'Leistungsspektrum'; react-hooks/set-state-in-effect
/Volumes/Alpha SSD/Coding/e-tib.com/components/blocks/HeroVideo.tsx
5:18 warning 'AnimatePresence' is defined but never used @typescript-eslint/no-unused-vars
6:10 warning 'useState' is defined but never used @typescript-eslint/no-unused-vars
6:20 warning 'useEffect' is defined but never used @typescript-eslint/no-unused-vars
45:12 warning 'e' is defined but never used @typescript-eslint/no-unused-vars
/Volumes/Alpha SSD/Coding/e-tib.com/components/blocks/InteractiveGermanyMap.tsx
5:46 warning 'ArrowUpRight' is defined but never used @typescript-eslint/no-unused-vars
147:23 warning 'isNearRight' is assigned a value but never used @typescript-eslint/no-unused-vars
/Volumes/Alpha SSD/Coding/e-tib.com/components/blocks/TeamGridBlock.tsx
20:39 warning 'title' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars
20:46 warning 'department' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars
20:58 warning 'showContact' is assigned a value but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars
/Volumes/Alpha SSD/Coding/e-tib.com/components/decorations/CorporateBackground.tsx
36:13 warning 'distance' is assigned a value but never used @typescript-eslint/no-unused-vars
/Volumes/Alpha SSD/Coding/e-tib.com/components/home/Hero.tsx
9:39 warning 'useCallback' is defined but never used @typescript-eslint/no-unused-vars
77:6 warning React Hook useEffect has a missing dependency: 'HERO_PLACEHOLDERS'. Either include it or remove the dependency array react-hooks/exhaustive-deps
/Volumes/Alpha SSD/Coding/e-tib.com/components/home/MeetTheTeam.tsx
8:9 warning 'teamT' is assigned a value but never used @typescript-eslint/no-unused-vars
/Volumes/Alpha SSD/Coding/e-tib.com/components/layout/Footer.tsx
4:35 warning 'Accessibility' is defined but never used @typescript-eslint/no-unused-vars
/Volumes/Alpha SSD/Coding/e-tib.com/components/providers/InitialLoader.tsx
5:8 warning 'Image' is defined but never used @typescript-eslint/no-unused-vars
/Volumes/Alpha SSD/Coding/e-tib.com/components/providers/PageTransitionShutter.tsx
6:8 warning 'Image' is defined but never used @typescript-eslint/no-unused-vars
/Volumes/Alpha SSD/Coding/e-tib.com/components/search/AIOrb.tsx
153:13 warning 'breathe' is assigned a value but never used @typescript-eslint/no-unused-vars
212:15 warning 'targetExcite' is assigned a value but never used @typescript-eslint/no-unused-vars
/Volumes/Alpha SSD/Coding/e-tib.com/components/search/AISearchResults.tsx
99:6 warning React Hook useEffect has missing dependencies: 'handleSearch' and 'initialQuery'. Either include them or remove the dependency array react-hooks/exhaustive-deps
/Volumes/Alpha SSD/Coding/e-tib.com/lib/blog.ts
6:7 warning 'BLOG_DIR' is assigned a value but never used @typescript-eslint/no-unused-vars
94:7 warning 'next' is never reassigned. Use 'const' instead prefer-const
95:7 warning 'prev' is never reassigned. Use 'const' instead prefer-const
✖ 38 problems (0 errors, 38 warnings)
0 errors and 2 warnings potentially fixable with the `--fix` option.

31
.turbo/turbo-test.log Normal file
View File

@@ -0,0 +1,31 @@

> e-tib-nextjs@2.2.12 test /Volumes/Alpha SSD/Coding/e-tib.com
> vitest run --passWithNoTests
[?25l
RUN v4.1.2 /Volumes/Alpha SSD/Coding/e-tib.com
[?2026h
lib/mail/mailer.test.ts [queued]
Test Files 0 passed (2)
Tests 0 passed (0)
Start at 11:47:51
Duration 504ms
[?2026l[?2026h ✓ lib/mail/mailer.test.ts (1 test) 2ms
lib/env.test.ts 0/6
Test Files 1 passed (2)
Tests 1 passed (7)
Start at 11:47:51
Duration 605ms
[?2026l ✓ lib/env.test.ts (6 tests) 3ms
Test Files 2 passed (2)
Tests 7 passed (7)
Start at 11:47:51
Duration 672ms (transform 113ms, setup 0ms, import 222ms, tests 5ms, environment 813ms)
[?25h

View File

@@ -0,0 +1,5 @@

> e-tib-nextjs@2.2.12 typecheck /Volumes/Alpha SSD/Coding/e-tib.com
> tsc --noEmit

View File

@@ -20,13 +20,15 @@ ENV CI=true
COPY pnpm-lock.yaml package.json .npmrc* ./
COPY patches* ./patches/
ARG NPM_DOMAIN=git.infra.mintel.me
# Configure private registry and install dependencies
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
--mount=type=secret,id=NPM_TOKEN \
if [ -f /run/secrets/NPM_TOKEN ]; then \
export NPM_TOKEN=$(cat /run/secrets/NPM_TOKEN); \
echo "@mintel:registry=https://git.infra.mintel.me/api/packages/mmintel/npm" > .npmrc; \
echo "//git.infra.mintel.me/api/packages/mmintel/npm/:_authToken=${NPM_TOKEN}" >> .npmrc; \
echo "@mintel:registry=https://${NPM_DOMAIN}/api/packages/mmintel/npm" > .npmrc; \
echo "//${NPM_DOMAIN}/api/packages/mmintel/npm/:_authToken=${NPM_TOKEN}" >> .npmrc; \
fi && \
pnpm install --no-frozen-lockfile && \
rm -f .npmrc

View File

@@ -5,8 +5,8 @@ RUN apk add --no-cache libc6-compat python3 make g++ curl
WORKDIR /app
# Enable corepack for pnpm
RUN corepack enable
# Install pnpm v10 explicitly (corepack defaults to v11 which requires Node 22)
RUN npm install -g pnpm@10.18.3
# Pre-set the pnpm store directory
ENV PNPM_HOME="/pnpm"

View File

@@ -26,8 +26,12 @@ import { FaqBlock } from '@/components/blocks/FaqBlock';
import { CertificatesBlock } from '@/components/blocks/CertificatesBlock';
import { HeroSection } from '@/components/blocks/HeroSection';
import { TeamGrid } from '@/components/blocks/TeamGrid';
import { DeepDrillAnimation } from '@/components/blocks/DeepDrillAnimation';
import { DataGridPulse } from '@/components/blocks/DataGridPulse';
import { GrowthChart } from '@/components/blocks/GrowthChart';
import JsonLd from '@/components/JsonLd';
import { Button } from '@/components/ui/Button';
import { AnimatedCounter } from '@/components/ui';
const mdxComponents = {
@@ -47,6 +51,10 @@ const mdxComponents = {
TeamGrid,
JsonLd,
Button,
AnimatedCounter,
GrowthChart,
DeepDrillAnimation,
DataGridPulse,
// Standard HTML element mapping for consistent E-TIB typography
h1: (props: any) => <Heading level={1} size="2" className="hidden" {...props} />, // Hidden because Hero handles H1
h2: (props: any) => <Heading level={2} size="3" className="mt-16 mb-6 border-b border-neutral-100 pb-4" {...props} />,
@@ -169,7 +177,7 @@ export default async function Page(props: { params: Promise<{ locale: string; sl
id: r.slug,
slug: r.slug,
title: r.frontmatter.title,
category: r.frontmatter.category || 'Projekt',
category: Array.isArray(r.frontmatter.category) ? r.frontmatter.category[0] : (r.frontmatter.category || 'Projekt'),
image: r.frontmatter.featuredImage
}))} />,
};

View File

@@ -18,6 +18,9 @@ import JsonLd from '@/components/JsonLd';
import { Button } from '@/components/ui/Button';
import { Heading } from '@/components/ui/Heading';
import { AnimatedCounter } from '@/components/ui';
import { InteractiveGermanyMap } from '@/components/blocks/InteractiveGermanyMap';
import { ScaleOfImpact } from '@/components/blocks/ScaleOfImpact';
import { getAllReferences } from '@/lib/references';
const mdxComponents = (references: any[]) => ({
@@ -31,6 +34,9 @@ const mdxComponents = (references: any[]) => ({
JsonLd,
Button,
Heading,
AnimatedCounter,
InteractiveGermanyMap,
ScaleOfImpact,
});
export async function generateMetadata(props: any): Promise<Metadata> {

View File

@@ -8,7 +8,7 @@ import { getButtonClasses, ButtonOverlay } from '@/components/ui/Button';
import { SITE_URL } from '@/lib/schema';
import Image from 'next/image';
import { InteractiveGermanyMap } from '@/components/blocks/InteractiveGermanyMap';
import { defaultLocations } from '@/lib/map-data';
import { defaultLocations, minorLocations } from '@/lib/map-data';
interface PageProps {
params: Promise<{
@@ -34,6 +34,17 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
};
}
function cleanLocation(location?: string): string {
if (!location) return '';
// Remove 5 digit postal codes
let cleaned = location.replace(/\b\d{5}\b/g, '');
// Clean up multiple spaces, slashes and commas
cleaned = cleaned.replace(/\s*,\s*/g, ', ').replace(/\s*\/\s*/g, ' / ').trim();
// Remove leading/trailing slash/comma residues
cleaned = cleaned.replace(/^[,/\s]+|[,/\s]+$/g, '').trim();
return cleaned || location;
}
export default async function ReferenzenOverview(props: { params: Promise<{ locale: string }> }) {
const { locale } = await props.params;
setRequestLocale(locale);
@@ -41,8 +52,8 @@ export default async function ReferenzenOverview(props: { params: Promise<{ loca
const references = await getAllReferences(locale);
// Enrich the static map locations with dynamic project data (images, details)
const enrichedLocations = defaultLocations.map(loc => {
const allLocations = [...defaultLocations, ...minorLocations];
const enrichedLocations = allLocations.map(loc => {
if (loc.type === 'project') {
const ref = references.find(r => r.slug === loc.id);
if (ref) {
@@ -104,7 +115,7 @@ export default async function ReferenzenOverview(props: { params: Promise<{ loca
<div className="absolute top-4 left-4 z-10">
<span className="bg-white/95 backdrop-blur-md text-neutral-dark px-3 py-1.5 rounded-sm text-[10px] font-bold uppercase tracking-widest flex items-center gap-1.5 shadow-sm border border-neutral-200">
<MapPin className="w-3 h-3 text-primary" />
{ref.frontmatter.location}
{cleanLocation(ref.frontmatter.location)}
</span>
</div>
</div>

View File

@@ -93,7 +93,7 @@ export function CertificatesBlock({ badge, title, description, certificates = de
const itemVariants = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0, transition: { duration: 0.5, ease: 'easeOut' } },
show: { opacity: 1, y: 0, transition: { duration: 0.5, ease: 'easeOut' as const } },
};
if (!isMounted) {

View File

@@ -0,0 +1,52 @@
'use client';
import React from 'react';
import { motion } from 'framer-motion';
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">
{/* Blueprint grid background */}
<div
className="absolute inset-0 opacity-20 pointer-events-none"
style={{
backgroundImage: 'linear-gradient(#82ED20 1px, transparent 1px), linear-gradient(90deg, #82ED20 1px, transparent 1px)',
backgroundSize: '40px 40px'
}}
/>
{/* 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]"
/>
<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]"
/>
<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">
{locale === 'en' ? 'Network Expansion in Numbers' : 'Netzausbau in Zahlen'}
</h3>
<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>
<p className="text-white/70 text-lg md:text-xl font-medium max-w-lg mx-auto">
{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.'}
</p>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,64 @@
'use client';
import React, { useRef } from 'react';
import { motion, useScroll, useTransform } from 'framer-motion';
import { useLocale } from 'next-intl';
export function DeepDrillAnimation() {
const containerRef = useRef<HTMLDivElement>(null);
const locale = useLocale();
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ["start center", "end center"]
});
const drillDepth = useTransform(scrollYProgress, [0, 1], ["0%", "100%"]);
return (
<div ref={containerRef} className="relative w-full py-32 flex flex-col items-center">
<div className="absolute inset-0 bg-[#050B14] -mx-[50vw] px-[50vw]" />
<div className="relative z-10 w-full max-w-3xl mx-auto flex flex-col items-center text-center text-white">
<h3 className="text-3xl font-heading font-extrabold mb-8 tracking-tight">
{locale === 'en' ? 'Precision in Depth' : 'Präzision in der Tiefe'}
</h3>
<p className="text-white/60 text-lg mb-16 max-w-xl">
{locale === 'en'
? 'Horizontal Directional Drilling (HDD) allows us to cross obstacles and rivers with extreme care.'
: 'Horizontalspülbohrverfahren (HDD) erlaubt uns, Hindernisse und Flüsse extrem schonend zu unterqueren.'}
</p>
{/* The Drill Path */}
<div className="relative w-2 h-96 bg-white/10 rounded-full mb-16">
<motion.div
className="absolute top-0 left-0 w-full bg-primary rounded-full"
style={{ height: drillDepth, boxShadow: '0 0 20px rgba(130,237,32,0.6)' }}
>
{/* The Drill Head */}
<div className="absolute -bottom-2 -left-2 w-6 h-6 bg-white rounded-full border-4 border-primary shadow-[0_0_15px_rgba(255,255,255,1)]" />
</motion.div>
</div>
{/* The Reveal Data */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.8 }}
className="flex flex-col items-center"
>
<div className="font-mono text-6xl md:text-8xl font-black text-white tracking-tighter mb-4" style={{ fontVariantNumeric: 'tabular-nums' }}>
102.771<span className="text-primary text-3xl md:text-5xl ml-2">m</span>
</div>
<div className="text-sm font-bold uppercase tracking-widest text-primary">
{locale === 'en' ? 'Drilled distance since 2023' : 'Gebohrte Strecke seit 2023'}
</div>
</motion.div>
</div>
</div>
);
}

View File

@@ -0,0 +1,80 @@
'use client';
import React from 'react';
import { motion } from 'framer-motion';
export function GrowthChart() {
const data = [
{ year: '2023', hdd: 29436, kabel: 76727 },
{ year: '2024', hdd: 28680, kabel: 131964 },
{ year: '2025', hdd: 44655, kabel: 163470 },
];
const maxVal = 163470; // Highest value for scaling
return (
<div className="w-full bg-white rounded-3xl p-8 md:p-12 shadow-sm border border-neutral-100 my-16">
<div className="mb-10">
<h3 className="text-3xl font-heading font-extrabold text-neutral-dark mb-2">Bewiesene Leistungsfähigkeit</h3>
<p className="text-neutral-500 font-medium">Unsere verlässliche Baukapazität der letzten drei Jahre (in Metern)</p>
</div>
<div className="space-y-8">
{data.map((item, index) => {
const hddWidth = (item.hdd / maxVal) * 100;
const kabelWidth = (item.kabel / maxVal) * 100;
return (
<div key={item.year} className="relative">
<div className="text-sm font-extrabold text-neutral-dark mb-2 w-16">{item.year}</div>
<div className="space-y-2">
{/* Kabelverlegung Bar */}
<div className="flex items-center gap-4">
<div className="flex-1 bg-neutral-100 rounded-full h-6 overflow-hidden relative">
<motion.div
initial={{ width: 0 }}
whileInView={{ width: `${kabelWidth}%` }}
viewport={{ once: true, margin: "-50px" }}
transition={{ duration: 1.5, delay: 0.1 * index, ease: "easeOut" }}
className="absolute top-0 left-0 h-full bg-primary rounded-full"
/>
</div>
<div className="w-24 text-right text-xs font-bold text-neutral-500 uppercase">
Kabel: {new Intl.NumberFormat('de-DE').format(item.kabel)}m
</div>
</div>
{/* HDD Bar */}
<div className="flex items-center gap-4">
<div className="flex-1 bg-neutral-100 rounded-full h-4 overflow-hidden relative">
<motion.div
initial={{ width: 0 }}
whileInView={{ width: `${hddWidth}%` }}
viewport={{ once: true, margin: "-50px" }}
transition={{ duration: 1.5, delay: 0.2 + 0.1 * index, ease: "easeOut" }}
className="absolute top-0 left-0 h-full bg-neutral-dark rounded-full"
/>
</div>
<div className="w-24 text-right text-xs font-bold text-neutral-500 uppercase">
HDD: {new Intl.NumberFormat('de-DE').format(item.hdd)}m
</div>
</div>
</div>
</div>
);
})}
</div>
<div className="mt-8 flex gap-6 border-t border-neutral-100 pt-6">
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-full bg-primary" />
<span className="text-xs font-bold text-neutral-500 uppercase">Kabelverlegung</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-full bg-neutral-dark" />
<span className="text-xs font-bold text-neutral-500 uppercase">HDD Spülbohrung</span>
</div>
</div>
</div>
);
}

View File

@@ -6,7 +6,10 @@ import { MapPin, Factory, Zap, CheckCircle2, ArrowUpRight } from 'lucide-react';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { AnimatedGlossyBorder } from '@/components/ui/AnimatedGlossyBorder';
import { Location, defaultLocations } from '@/lib/map-data';
import { Location, defaultLocations, minorLocations } from '@/lib/map-data';
import { useLocale } from 'next-intl';
const allLocations = [...defaultLocations, ...minorLocations];
interface Stat {
value: string;
@@ -31,15 +34,17 @@ export function InteractiveGermanyMap({
{ value: '100', suffix: '%', label: 'Überregionale Reichweite' },
{ value: '2', suffix: '+', label: 'Operative Standorte' },
],
locations = defaultLocations,
locations = allLocations,
isHero = false
}: InteractiveGermanyMapProps) {
const [activeLocation, setActiveLocation] = useState<Location | null>(null);
const router = useRouter();
const locale = useLocale();
const hq = locations.find((l) => l.type === 'hq');
const branch = locations.find((l) => l.type === 'branch');
const projects = locations.filter((l) => l.type === 'project');
const minorNodes = locations.filter((l) => l.type === 'minor_node');
return (
<div className={isHero ? "relative w-full" : "relative w-full max-w-7xl mx-auto py-16 md:py-24 px-4 sm:px-6 mt-16 md:mt-20"}>
@@ -91,80 +96,49 @@ export function InteractiveGermanyMap({
</div>
{/* Map Right */}
<div className="xl:col-span-7 relative flex items-center justify-center lg:justify-end mt-8 lg:mt-0 z-10">
<div className="xl:col-span-7 relative flex items-center justify-center min-h-[500px] lg:min-h-[700px] z-10 pointer-events-none">
{/* Map Container - Enforce strict aspect ratio matching the SVG (1024x1024) */}
<div className="relative w-full max-w-[600px] aspect-square z-10">
<div className="relative w-full max-w-[600px] aspect-square group pointer-events-auto">
{/* Map SVG */}
<div className="absolute inset-0 opacity-[0.25] mix-blend-screen drop-shadow-2xl brightness-200">
<div className="absolute inset-0 opacity-[0.25] mix-blend-screen drop-shadow-2xl brightness-200 pointer-events-none">
<Image
src="/germany-map.svg"
alt="Deutschlandkarte"
alt={locale === 'en' ? "Map of Germany" : "Deutschlandkarte"}
fill
className="object-cover"
priority
/>
</div>
{/* Connections (Lines) */}
<svg className="absolute inset-0 w-full h-full pointer-events-none z-10">
{hq && branch && (
<motion.line
x1={`${hq.x}%`}
y1={`${hq.y}%`}
x2={`${branch.x}%`}
y2={`${branch.y}%`}
stroke="rgba(130,237,32,0.6)"
strokeWidth="2"
strokeDasharray="6 6"
initial={{ pathLength: 0, opacity: 0 }}
animate={{ pathLength: 1, opacity: 1 }}
transition={{ duration: 2, ease: "easeOut" }}
/>
)}
{hq && projects.map((proj, idx) => (
<motion.line
key={proj.id}
x1={`${hq.x}%`}
y1={`${hq.y}%`}
x2={`${proj.x}%`}
y2={`${proj.y}%`}
stroke="rgba(130,237,32,0.3)"
strokeWidth="1.5"
initial={{ pathLength: 0, opacity: 0 }}
animate={{ pathLength: 1, opacity: 1 }}
transition={{ duration: 1.5, delay: 0.5 + idx * 0.2 }}
/>
))}
</svg>
{/* Minor Location Markers (The 100+ generic projects) */}
{locations.filter(l => l.type === 'minor_node').map((loc, idx) => (
<div
key={`minor-${idx}`}
className="absolute z-10 group/minor cursor-pointer w-10 h-10 flex items-center justify-center"
style={{
left: `${loc.x}%`,
top: `${loc.y}%`,
transform: 'translate(-50%, -50%)',
}}
onMouseEnter={() => setActiveLocation(loc)}
onMouseLeave={() => setActiveLocation(null)}
>
<div className="w-1.5 h-1.5 bg-primary/80 rounded-full group-hover/minor:bg-white group-hover/minor:scale-150 transition-all duration-300" />
<div className="absolute inset-0 m-auto w-2 h-2 bg-primary rounded-full opacity-0 group-hover/minor:animate-ping" />
</div>
))}
{/* Location Pins */}
{locations.map((loc, idx) => {
{/* Major & Reference Location Markers (HQ, Branch, Project) */}
{locations.filter(l => l.type !== 'minor_node').map((loc, idx) => {
const isHQ = loc.type === 'hq';
const isBranch = loc.type === 'branch';
const isActive = activeLocation?.id === loc.id;
const isNearLeft = loc.x < 50;
const isNearRight = loc.x >= 50;
const isNearTop = loc.y < 55; // 55% threshold to strongly avoid the top header
const tooltipClasses = [
'absolute w-[280px] sm:w-64 bg-white text-neutral-dark p-5 rounded-2xl shadow-2xl z-[9999] border border-neutral-100',
isNearLeft ? 'left-1/2 sm:left-0 -translate-x-1/2 sm:translate-x-0' : 'left-1/2 sm:left-auto sm:right-0 -translate-x-1/2 sm:translate-x-0',
isNearTop
? 'top-full mt-4 before:absolute before:-top-6 before:left-0 before:w-full before:h-6'
: 'bottom-full mb-4 before:absolute before:-bottom-6 before:left-0 before:w-full before:h-6'
].join(' ');
const arrowClasses = [
'absolute border-8 border-transparent drop-shadow-sm hidden sm:block',
isNearLeft ? 'left-6' : 'right-6',
isNearTop ? '-top-4 border-b-white border-t-0' : '-bottom-4 border-t-white border-b-0'
].join(' ');
return (
<div
key={loc.id}
className={`absolute transform -translate-x-1/2 -translate-y-1/2 group cursor-pointer w-16 h-16 flex items-center justify-center ${isActive ? 'z-[60]' : 'z-20'}`}
className={`absolute transform -translate-x-1/2 -translate-y-1/2 group/pin cursor-pointer w-16 h-16 flex items-center justify-center ${isActive ? 'z-[60]' : 'z-20'}`}
style={{ left: `${loc.x}%`, top: `${loc.y}%` }}
onMouseEnter={() => setActiveLocation(loc)}
onMouseLeave={() => setActiveLocation(null)}
@@ -177,121 +151,125 @@ export function InteractiveGermanyMap({
<div className="absolute inset-0 m-auto w-6 h-6 rounded-full animate-ping bg-primary/50 scale-150" />
)}
{/* Marker Dot */}
{/* Elegant Marker Dot */}
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: 'spring', delay: idx * 0.1 }}
className={`relative flex items-center justify-center rounded-full shadow-[0_0_20px_rgba(130,237,32,0.4)] transition-transform duration-300 ${
isActive ? 'scale-125 z-30 ring-4 ring-primary/30' : 'scale-100'
className={`relative flex items-center justify-center rounded-full shadow-[0_0_20px_rgba(130,237,32,0.6)] transition-transform duration-300 ${
isActive ? 'scale-125 z-30 ring-4 ring-primary/40' : 'scale-100 group-hover/pin:scale-110'
} ${
isHQ || isBranch
? 'w-6 h-6 bg-primary text-[#050B14]'
? 'w-7 h-7 bg-primary text-[#050B14]'
: 'w-4 h-4 bg-white ring-[3px] ring-primary'
}`}
>
{(isHQ || isBranch) && <MapPin className="w-3.5 h-3.5" />}
{(isHQ || isBranch) && <MapPin className="w-4 h-4" />}
</motion.div>
{/* Tooltip */}
<AnimatePresence>
{isActive && (
<motion.div
variants={{
hidden: { opacity: 0, y: isNearTop ? -15 : 15, scale: 0.9 },
visible: {
opacity: 1, y: 0, scale: 1,
transition: {
type: "spring", stiffness: 350, damping: 25,
staggerChildren: 0.05, delayChildren: 0.05
}
},
exit: {
opacity: 0, y: isNearTop ? -10 : 10, scale: 0.95,
transition: { duration: 0.15, ease: "easeIn" }
}
}}
initial="hidden"
animate="visible"
exit="exit"
className={`${tooltipClasses} overflow-hidden p-0`}
>
{loc.featuredImage && (
<motion.div
variants={{ hidden: { opacity: 0 }, visible: { opacity: 1 } }}
className="relative w-full h-32 bg-neutral-100 overflow-hidden"
>
<Image
src={loc.featuredImage}
alt={loc.name}
fill
className="object-cover transform transition-transform duration-1000 ease-[cubic-bezier(0.16,1,0.3,1)] group-hover:scale-110"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent opacity-80" />
<div className="absolute bottom-3 left-4 flex items-center gap-2">
{isHQ ? <Factory className="w-4 h-4 text-primary drop-shadow" /> : <Zap className="w-4 h-4 text-primary drop-shadow" />}
<span className="font-extrabold text-sm leading-tight text-white drop-shadow-md">{loc.name}</span>
</div>
</motion.div>
)}
<div className={loc.featuredImage ? "p-5 pt-4" : "p-5"}>
{!loc.featuredImage && (
<motion.div
variants={{ hidden: { opacity: 0, x: -5 }, visible: { opacity: 1, x: 0 } }}
className="flex items-center gap-2 mb-2"
>
{isHQ ? <Factory className="w-4 h-4 text-primary" /> : <Zap className="w-4 h-4 text-primary" />}
<span className="font-extrabold text-sm leading-tight text-neutral-dark">{loc.name}</span>
</motion.div>
)}
<motion.p
variants={{ hidden: { opacity: 0, y: 5 }, visible: { opacity: 1, y: 0 } }}
className="text-xs text-neutral-500 font-medium"
>
{loc.description}
</motion.p>
{loc.details && (
<ul className="mt-4 space-y-2">
{loc.details.map((detail, i) => (
<motion.li
key={i}
variants={{ hidden: { opacity: 0, x: -5 }, visible: { opacity: 1, x: 0 } }}
className="text-[11px] text-neutral-dark flex items-center gap-2 font-semibold"
>
<div className="w-4 h-4 rounded-full bg-primary/10 flex items-center justify-center shrink-0">
<CheckCircle2 className="w-3 h-3 text-primary" />
</div>
{detail}
</motion.li>
))}
</ul>
)}
{loc.href && (
<motion.div
variants={{ hidden: { opacity: 0, y: 5 }, visible: { opacity: 1, y: 0 } }}
className="mt-4 pt-3 border-t border-neutral-100 text-xs font-bold text-primary flex items-center gap-1 group/link"
>
Zum Projekt
<span className="text-base leading-none translate-y-[1px] transform transition-transform duration-300 group-hover:translate-x-1.5">&rarr;</span>
</motion.div>
)}
</div>
{/* Arrow Down/Up */}
<div className={arrowClasses} />
</motion.div>
)}
</AnimatePresence>
</div>
);
})}
{/* Global Tooltip */}
<AnimatePresence>
{activeLocation && (
<motion.div
key="tooltip"
initial={{ opacity: 0, y: 10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 10, scale: 0.95 }}
transition={{ duration: 0.2 }}
className="absolute z-[9999] pointer-events-none"
style={{
left: `${activeLocation.x}%`,
top: `${activeLocation.y}%`,
transform: activeLocation.x < 50 ? 'translate(20px, -50%)' : 'translate(calc(-100% - 20px), -50%)',
}}
>
<div className="bg-[#050B14]/95 backdrop-blur-xl border border-white/10 rounded-2xl shadow-2xl min-w-[200px] w-max max-w-[300px] text-white overflow-hidden">
{activeLocation.featuredImage && (
<div className="relative w-full h-32 bg-neutral-900">
<Image
src={activeLocation.featuredImage}
alt={activeLocation.name}
fill
className="object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-t from-[#050B14] to-transparent opacity-90" />
</div>
)}
<div className="p-4">
<div className="flex items-center gap-2 mb-1">
<MapPin className="w-3.5 h-3.5 text-primary" />
<span className="text-xs font-bold text-primary uppercase tracking-wider">
{activeLocation.type === 'hq' ? 'Hauptsitz' :
activeLocation.type === 'branch' ? 'Niederlassung' :
activeLocation.type === 'minor_node' ? 'Projekt' : 'Referenz'}
</span>
</div>
<div className="font-heading font-bold text-lg leading-tight mb-2">
{activeLocation.name}
</div>
{activeLocation.type !== 'minor_node' ? (
<div className="text-white/60 text-sm line-clamp-3 mb-3">
{activeLocation.description}
</div>
) : (
<div className="text-white/60 text-xs mb-3">
{activeLocation.description === 'wind' ? 'Errichtung Leitungstrassen & Netzanbindung für Windpark-Infrastruktur.' :
activeLocation.description === 'pv' ? 'Bau von Mittelspannungstrassen zur Einspeisung der PV-Anlage.' :
activeLocation.description === 'fiber' ? 'Komplexer FTTx Breitbandausbau inkl. LWL-Montage & Tiefbau.' :
activeLocation.description === 'power' ? '110kV Hochspannungstrasse Erdkabelverlegung.' :
activeLocation.description === 'battery' ? 'Infrastrukturausbau für BESS (Batteriespeicher).' :
'Komplexes Infrastrukturprojekt (Tiefbau & HDD).'}
</div>
)}
{(activeLocation.details || activeLocation.type === 'minor_node') && (
<ul className="mb-3 space-y-1.5 border-t border-white/10 pt-3">
{activeLocation.type === 'minor_node' ? (
<>
<li className="text-[11px] text-white/80 flex items-start gap-2 font-medium">
<CheckCircle2 className="w-3 h-3 text-primary shrink-0 mt-0.5" />
<span>{
activeLocation.description === 'wind' ? 'Netzanbindung Windpark' :
activeLocation.description === 'pv' ? 'Einspeisung PV-Anlage' :
activeLocation.description === 'fiber' ? 'FTTx Infrastruktur' :
activeLocation.description === 'power' ? '110kV Trasse' :
activeLocation.description === 'battery' ? 'BESS Anschluss' : 'Infrastrukturprojekt'
}</span>
</li>
<li className="text-[11px] text-white/80 flex items-start gap-2 font-medium">
<CheckCircle2 className="w-3 h-3 text-primary shrink-0 mt-0.5" />
<span>Tiefbau & Spülbohrung (HDD)</span>
</li>
</>
) : (
activeLocation.details?.map((detail, i) => (
<li key={i} className="text-[11px] text-white/80 flex items-start gap-2 font-medium">
<CheckCircle2 className="w-3 h-3 text-primary shrink-0 mt-0.5" />
<span>{detail}</span>
</li>
))
)}
</ul>
)}
{activeLocation.href && (
<div className="inline-flex items-center text-xs font-bold text-primary">
{locale === 'en' ? 'Learn more' : 'Zum Projekt'}
<ArrowUpRight className="w-3.5 h-3.5 ml-1" />
</div>
)}
</div>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,102 @@
'use client';
import React, { useRef } from 'react';
import { motion, useScroll, useTransform } from 'framer-motion';
import { useLocale } from 'next-intl';
export function ScaleOfImpact() {
const containerRef = useRef<HTMLDivElement>(null);
const locale = useLocale();
// Track scroll progress through this container
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ["start end", "end start"]
});
// Map scroll progress to SVG path length
const pathLength = useTransform(scrollYProgress, [0.2, 0.8], [0, 1]);
// Opacity for the massive background numbers
const bgOpacity = useTransform(scrollYProgress, [0.1, 0.4, 0.8, 1], [0, 0.1, 0.1, 0]);
return (
<div
ref={containerRef}
className="relative w-full min-h-[70vh] bg-[#050B14] flex flex-col items-center justify-center overflow-hidden border-y border-neutral-800"
>
{/* Massive Watermark Numbers */}
<motion.div
style={{ opacity: bgOpacity }}
className="absolute inset-0 flex flex-col items-center justify-center pointer-events-none select-none overflow-hidden mix-blend-overlay"
>
<div className="font-heading font-black text-[20vw] leading-none text-white opacity-20 whitespace-nowrap">
<span className="font-mono tracking-tighter">474.932</span>
</div>
</motion.div>
{/* Foreground Content */}
<div className="relative z-10 container max-w-7xl mx-auto px-4 flex flex-col items-center text-center">
<h2 className="text-3xl md:text-5xl font-heading font-extrabold text-white mb-6 tracking-tight">
{locale === 'en' ? (
<>An infrastructure <br className="md:hidden"/> that connects the country.</>
) : (
<>Eine Infrastruktur, <br className="md:hidden"/> die das Land verbindet.</>
)}
</h2>
<p className="text-white/60 text-lg md:text-xl max-w-2xl font-medium mb-16">
{locale === 'en'
? "We don't just talk about the energy transition. We build it. In the last 36 months alone, we have completed a route that stretches from our headquarters in Guben all the way to Munich."
: "Wir sprechen nicht nur über die Energiewende. Wir bauen sie. Allein in den letzten 36 Monaten haben wir eine Strecke realisiert, die von unserem Hauptsitz in Guben bis nach München reicht."
}
</p>
{/* Abstract SVG Line Animation */}
<div className="w-full max-w-4xl h-32 relative">
<svg
width="100%"
height="100%"
viewBox="0 0 1000 100"
preserveAspectRatio="none"
className="absolute inset-0"
>
{/* Background dim line */}
<path
d="M0,50 Q250,0 500,50 T1000,50"
fill="none"
stroke="rgba(255,255,255,0.05)"
strokeWidth="2"
/>
{/* Glowing animated line */}
<motion.path
d="M0,50 Q250,0 500,50 T1000,50"
fill="none"
stroke="#82ED20" // primary color
strokeWidth="4"
style={{ pathLength }}
className="drop-shadow-[0_0_15px_rgba(130,237,32,0.8)]"
/>
</svg>
</div>
{/* Static Data Grid (No jumping text) */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 md:gap-16 w-full max-w-4xl mt-12">
<div className="flex flex-col items-center">
<div className="font-mono text-5xl md:text-6xl font-black text-white tracking-tighter mb-2">100<span className="text-primary">+</span></div>
<div className="text-xs font-bold uppercase tracking-widest text-white/50">{locale === 'en' ? 'Major Projects' : 'Großprojekte'}</div>
</div>
<div className="flex flex-col items-center">
<div className="font-mono text-5xl md:text-6xl font-black text-white tracking-tighter mb-2">372<span className="text-primary text-3xl">km</span></div>
<div className="text-xs font-bold uppercase tracking-widest text-white/50">{locale === 'en' ? 'Cable Laying' : 'Kabelverlegung'}</div>
</div>
<div className="flex flex-col items-center">
<div className="font-mono text-5xl md:text-6xl font-black text-white tracking-tighter mb-2">102<span className="text-primary text-3xl">km</span></div>
<div className="text-xs font-bold uppercase tracking-widest text-white/50">{locale === 'en' ? 'HDD Drilling' : 'Spülbohrung'}</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -3,7 +3,7 @@ import Image from 'next/image';
import { useLocale } from 'next-intl';
import { ShieldCheck, Leaf, Lock, Accessibility, Zap, Mail, Phone } from 'lucide-react';
import { LanguageSwitcher } from './LanguageSwitcher';
import { Tooltip } from '@/components/ui/Tooltip';
interface CompanyInfo {
contactEmail: string;
contactPhone: string;
@@ -165,10 +165,18 @@ export function Footer({ companyInfo }: FooterProps) {
{/* Quality Badges */}
<div className="mt-4 md:mt-6 flex flex-wrap justify-center gap-x-4 gap-y-3 md:gap-x-8 md:gap-y-4 text-neutral-400 text-[10px] md:text-xs font-medium uppercase tracking-wider px-2">
<div className="flex items-center gap-2"><ShieldCheck className="w-4 h-4 text-neutral-400" /><span>SSL Secured</span></div>
<div className="flex items-center gap-2"><Leaf className="w-4 h-4 text-primary" /><span>Green Hosting</span></div>
<div className="flex items-center gap-2"><Lock className="w-4 h-4 text-neutral-400" /><span>DSGVO Compliant</span></div>
<div className="flex items-center gap-2"><Zap className="w-4 h-4 text-neutral-400" /><span>High Performance</span></div>
<Tooltip content={locale === 'de' ? 'Ihre Daten werden sicher und verschlüsselt übertragen.' : 'Your data is transmitted securely and encrypted.'}>
<div className="flex items-center gap-2 cursor-help"><ShieldCheck className="w-4 h-4 text-neutral-400" /><span>SSL Secured</span></div>
</Tooltip>
<Tooltip content={locale === 'de' ? 'Wir nutzen 100% Ökostrom für den Betrieb unserer Website.' : 'We use 100% green energy to run our website.'}>
<div className="flex items-center gap-2 cursor-help"><Leaf className="w-4 h-4 text-primary" /><span>Green Hosting</span></div>
</Tooltip>
<Tooltip content={locale === 'de' ? 'Ihre Privatsphäre ist uns wichtig. Wir halten uns streng an den Datenschutz.' : 'Your privacy is important to us. We strictly adhere to data protection laws.'}>
<div className="flex items-center gap-2 cursor-help"><Lock className="w-4 h-4 text-neutral-400" /><span>DSGVO Compliant</span></div>
</Tooltip>
<Tooltip content={locale === 'de' ? 'Blitzschnelle Ladezeiten für ein optimales Nutzererlebnis.' : 'Lightning-fast loading times for an optimal user experience.'}>
<div className="flex items-center gap-2 cursor-help"><Zap className="w-4 h-4 text-neutral-400" /><span>High Performance</span></div>
</Tooltip>
</div>
{/* Mintel Design & Version */}

View File

@@ -0,0 +1,48 @@
'use client';
import React, { useEffect, useState } from 'react';
import { motion, useMotionValue, useTransform, animate, useInView } from 'framer-motion';
interface AnimatedCounterProps {
value: number;
duration?: number;
delay?: number;
suffix?: string;
prefix?: string;
className?: string;
}
export function AnimatedCounter({
value,
duration = 2,
delay = 0,
suffix = '',
prefix = '',
className = '',
}: AnimatedCounterProps) {
const count = useMotionValue(0);
const rounded = useTransform(count, (latest) => {
// Format with thousands separator
return new Intl.NumberFormat('de-DE').format(Math.round(latest));
});
const ref = React.useRef(null);
const inView = useInView(ref, { once: true, margin: "-100px" });
useEffect(() => {
if (inView) {
const controls = animate(count, value, {
duration: duration,
delay: delay,
ease: 'easeOut',
});
return controls.stop;
}
}, [inView, value, duration, delay, count]);
return (
<motion.span ref={ref} className={className}>
{prefix}<motion.span>{rounded}</motion.span>{suffix}
</motion.span>
);
}

19
components/ui/Tooltip.tsx Normal file
View File

@@ -0,0 +1,19 @@
import React, { ReactNode } from 'react';
interface TooltipProps {
children: ReactNode;
content: string;
}
export function Tooltip({ children, content }: TooltipProps) {
return (
<div className="group relative flex items-center justify-center">
{children}
<div className="absolute bottom-full mb-3 invisible opacity-0 translate-y-2 group-hover:visible group-hover:opacity-100 group-hover:translate-y-0 w-max max-w-[250px] px-4 py-2.5 text-[11px] md:text-xs text-white bg-neutral-900/95 backdrop-blur-md rounded-xl shadow-2xl border border-white/10 transition-all duration-300 pointer-events-none z-50 text-center leading-relaxed font-normal normal-case tracking-normal">
{content}
{/* Triangle Arrow */}
<div className="absolute left-1/2 top-full -translate-x-1/2 border-4 border-transparent border-t-neutral-900/95" />
</div>
</div>
);
}

View File

@@ -7,5 +7,6 @@ export * from './Card';
export * from './Badge';
export * from './Input';
export * from './Textarea';
export * from './AnimatedCounter';
export * from './Label';
export * from './Callout';

View File

@@ -29,6 +29,8 @@ layout: "fullBleed"
Mit unserem spezialisierten Gerätepark beherrschen wir die unterschiedlichsten Verfahren. Beim **Horizontalspülbohrverfahren (HDD)** steuern wir den Bohrkopf punktgenau unter der Erde hindurch. Wir realisieren dabei Bohrlängen von bis zu 250 Metern am Stück und können Schutzrohre mit einem Durchmesser von bis zu 400 mm einziehen.
Für kürzere Distanzen, wie beispielsweise schnelle und schonende Hausanschlüsse, setzen wir unsere **Erdraketen** (Bodenverdrängungsverfahren) ein. Hierbei erreichen wir Durchörterungslängen von bis zu 15 Metern für Rohre bis 160 mm Durchmesser ideal um Vorgärten und Einfahrten komplett unversehrt zu lassen.
<DeepDrillAnimation />
</div>
<ServiceDetailGrid

View File

@@ -143,5 +143,7 @@ description: "Die E-TIB GmbH ist Ihr zuverlässiger Partner für komplexe Kabelt
]}
/>
<ScaleOfImpact />
<HomeReferencesSlider />

View File

@@ -31,6 +31,8 @@ layout: "fullBleed"
Durch unsere langjährige Erfahrung erkennen wir Problemstellungen frühzeitig und erarbeiten für unsere Auftraggeber im Vorfeld sichere und zufriedenstellende Lösungen. Die E-TIB GmbH hat sich voll und ganz auf den Kabelleitungstiefbau spezialisiert.
Wir bieten Ihnen das komplette Leistungsspektrum zur Errichtung von Kabeltrassen aus einer Hand mit eigenem, hochmodernem Maschinenpark und festangestelltem Fachpersonal aus der Region. In direkter Zusammenarbeit mit unserem Partnerbüro, der E-TIB Ingenieurgesellschaft mbH, können wir zudem frühzeitig Planungsfehler ausschließen und selbst die komplexesten Dokumentationsanforderungen souverän erfüllen.
<DataGridPulse />
</div>
<ServiceDetailGrid

View File

@@ -52,9 +52,9 @@ layout: "fullBleed"
</div>
</div>
<CompanyTimeline />
<InteractiveGermanyMap isHero={false} badge="Bundesweite Präsenz" title="Infrastruktur für ein ganzes Land" description="Seit 2023 haben wir in über 100 Projekten quer durch Deutschland bewiesen, dass wir Infrastrukturprojekte jeder Größenordnung verlässlich umsetzen." />
<InteractiveGermanyMap />
<CompanyTimeline />
<div id="struktur">
<HomeSubCompanyTiles

View File

@@ -52,6 +52,8 @@ layout: "fullBleed"
]}
/>
<DeepDrillAnimation />
<CallToAction
title="Looking for a trenchless solution?"
text="We would be happy to advise you on the possibilities of horizontal directional drilling for your project."

View File

@@ -139,4 +139,6 @@ description: "E-TIB GmbH is your reliable partner for complex cable routes, hori
]}
/>
<ScaleOfImpact />
<HomeReferencesSlider />

View File

@@ -53,6 +53,8 @@ layout: "fullBleed"
]}
/>
<DataGridPulse />
<CallToAction
title="Do you have a specific project?"
text="We would be happy to advise you on all questions regarding cable civil engineering and line laying."

View File

@@ -52,6 +52,8 @@ layout: "fullBleed"
</div>
</div>
<InteractiveGermanyMap isHero={false} badge="Nationwide Presence" title="Infrastructure for an entire country" description="Since 2023, we have proven in over 100 projects across Germany that we reliably implement infrastructure projects of any scale." />
<CompanyTimeline
badge="Our History"
title="Milestones of Development"

View File

@@ -40,6 +40,8 @@ services:
NPM_TOKEN: ${NPM_TOKEN:-}
CI: "true"
WATCHPACK_POLLING: "true"
ports:
- "3001:3001"
volumes:
- .:/app
- etib_node_modules:/app/node_modules

View File

@@ -1,7 +1,7 @@
export interface Location {
id: string;
name: string;
type: 'hq' | 'branch' | 'project';
type: 'hq' | 'branch' | 'project' | 'minor_node';
x: number;
y: number;
description: string;
@@ -16,7 +16,7 @@ export const defaultLocations: Location[] = [
name: 'Guben (Hauptsitz)',
type: 'hq',
x: 85,
y: 48,
y: 41.1,
description: 'E-TIB GmbH Holding & Bohrtechnik GmbH',
details: ['Zentrale Steuerung', 'Kabelleitungstiefbau', 'Maschinenpark'],
},
@@ -25,7 +25,7 @@ export const defaultLocations: Location[] = [
name: 'Bülstedt (Ingenieurgesellschaft)',
type: 'branch',
x: 37,
y: 25,
y: 25.33,
description: 'E-TIB Ingenieurgesellschaft mbH',
details: ['Planung & Projektierung', 'Vermessung', 'Dokumentation'],
},
@@ -34,7 +34,7 @@ export const defaultLocations: Location[] = [
name: 'Spreewald (FTTC)',
type: 'project',
x: 81,
y: 42,
y: 36.99,
description: 'Breitbandausbau FTTC',
href: '/referenzen/spreewald',
},
@@ -43,7 +43,7 @@ export const defaultLocations: Location[] = [
name: 'Bomlitz (FTTH)',
type: 'project',
x: 43,
y: 30,
y: 28.76,
description: '88km Leitungstrasse (Tiefbau/HDD)',
href: '/referenzen/bomlitz',
},
@@ -52,7 +52,7 @@ export const defaultLocations: Location[] = [
name: 'Forst (Kabeltrasse)',
type: 'project',
x: 87,
y: 52,
y: 43.85,
description: 'Neubau MS-Kabeltrasse',
href: '/referenzen/forst',
},
@@ -61,7 +61,7 @@ export const defaultLocations: Location[] = [
name: 'Eisenhüttenstadt (Kabel)',
type: 'project',
x: 86,
y: 44,
y: 38.36,
description: 'Kabelverbindung U30 - T10',
href: '/referenzen/eisenhuettenstadt',
},
@@ -70,7 +70,7 @@ export const defaultLocations: Location[] = [
name: 'Görne (PV-Anlage)',
type: 'project',
x: 65,
y: 35,
y: 32.19,
description: 'Einspeisung PV-Anlage (MS-Kabel)',
href: '/referenzen/goerne',
},
@@ -79,8 +79,844 @@ export const defaultLocations: Location[] = [
name: 'Börde (FTTH-Ausbau)',
type: 'project',
x: 55,
y: 38,
y: 34.24,
description: '109km Leitungstrasse für Breitband',
href: '/referenzen/boerde',
}
];
export const minorLocations: Location[] = [
{
id: 'breddin-16845',
name: 'PV-Infrastrukturprojekt, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 63.88,
y: 29.36,
description: 'pv',
},
{
id: 'bad-liebenwerda-04924',
name: 'Breitbandausbau / FTTx, Region Berlin / Brandenburg',
type: 'minor_node',
x: 73.76,
y: 46.48,
description: 'fiber',
},
{
id: 'hünfelden-65597',
name: 'Netzanbindung Windenergie, Rhein-Ruhr-Gebiet',
type: 'minor_node',
x: 29.35,
y: 61.68,
description: 'wind',
},
{
id: 'zachow-19376',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 68.62,
y: 34.44,
description: 'pv',
},
{
id: 'schnarup-thumby-24891',
name: 'Netzanbindung Windenergie, Metropolregion Hamburg',
type: 'minor_node',
x: 41.96,
y: 7.31,
description: 'wind',
},
{
id: 'bartow-17089',
name: 'PV-Infrastrukturprojekt, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 73.39,
y: 17.66,
description: 'pv',
},
{
id: 'regesbostel-21649',
name: 'Netzanbindung Windenergie, Metropolregion Hamburg',
type: 'minor_node',
x: 42.09,
y: 23.17,
description: 'wind',
},
{
id: 'bomlitz-29699',
name: 'Breitbandausbau / FTTx, Metropolregion Hamburg',
type: 'minor_node',
x: 42.22,
y: 29.09,
description: 'fiber',
},
{
id: 'berzhahn-56459',
name: 'Netzanbindung Windenergie, Rhein-Ruhr-Gebiet',
type: 'minor_node',
x: 28.1,
y: 58.9,
description: 'wind',
},
{
id: 'oschersleben-39387',
name: 'Breitbandausbau / FTTx, Region Hannover / Braunschweig',
type: 'minor_node',
x: 55.74,
y: 40.14,
description: 'fiber',
},
{
id: 'jessen-06917',
name: 'Netzanbindung Windenergie, Region Berlin / Brandenburg',
type: 'minor_node',
x: 70.21,
y: 42.8,
description: 'wind',
},
{
id: 'dettmannsdorf-18334',
name: 'PV-Infrastrukturprojekt, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 66.38,
y: 14.04,
description: 'pv',
},
{
id: 'werneck-97440',
name: 'Netzanbindung Windenergie, Rhein-Main-Gebiet',
type: 'minor_node',
x: 45.73,
y: 65.8,
description: 'wind',
},
{
id: 'badendorf-23619',
name: 'PV-Infrastrukturprojekt, Metropolregion Hamburg',
type: 'minor_node',
x: 49.91,
y: 17.02,
description: 'pv',
},
{
id: 'hordorf-39387',
name: 'Breitbandausbau / FTTx, Region Hannover / Braunschweig',
type: 'minor_node',
x: 55.17,
y: 40.57,
description: 'fiber',
},
{
id: 'neißemünde-15898',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 84.95,
y: 39.9,
description: 'pv',
},
{
id: 'wiesenhagen-14959',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 72.47,
y: 38.31,
description: 'pv',
},
{
id: 'gersdorf-09355',
name: 'Netzanbindung Windenergie, Region Leipzig / Dresden',
type: 'minor_node',
x: 67.92,
y: 56.13,
description: 'wind',
},
{
id: 'nauen-14641',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 68.95,
y: 32.81,
description: 'pv',
},
{
id: 'eggersdorf-15374',
name: 'PV-Infrastrukturprojekt, Region Hannover / Braunschweig',
type: 'minor_node',
x: 59.59,
y: 40.82,
description: 'pv',
},
{
id: 'guben-03172',
name: 'Breitbandausbau / FTTx, Region Berlin / Brandenburg',
type: 'minor_node',
x: 84.55,
y: 41.12,
description: 'fiber',
},
{
id: 'beratzhausen-93176',
name: 'PV-Infrastrukturprojekt, Großraum Stuttgart / Franken',
type: 'minor_node',
x: 60.46,
y: 76.77,
description: 'pv',
},
{
id: 'felm-24161',
name: 'Netzanbindung Windenergie, Metropolregion Hamburg',
type: 'minor_node',
x: 45.58,
y: 10.19,
description: 'wind',
},
{
id: 'neukieritzsch-04575',
name: 'Breitbandausbau / FTTx, Region Leipzig / Dresden',
type: 'minor_node',
x: 65.5,
y: 51.2,
description: 'fiber',
},
{
id: 'oberbarnim-15377',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 79.52,
y: 33.09,
description: 'pv',
},
{
id: 'berne-27804',
name: 'Netzanbindung Windenergie, Region Emsland / Ostfriesland',
type: 'minor_node',
x: 32.1,
y: 25.85,
description: 'wind',
},
{
id: 'dossow-16909',
name: 'PV-Infrastrukturprojekt, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 66.44,
y: 26.41,
description: 'pv',
},
{
id: 'krempe-25361',
name: 'PV-Infrastrukturprojekt, Metropolregion Hamburg',
type: 'minor_node',
x: 40.7,
y: 17.42,
description: 'pv',
},
{
id: 'lübars-39291',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 73.45,
y: 32.78,
description: 'pv',
},
{
id: 'taarstedt-24893',
name: 'Netzanbindung Windenergie, Metropolregion Hamburg',
type: 'minor_node',
x: 42.4,
y: 8.17,
description: 'wind',
},
{
id: 'rom-19372',
name: 'PV-Infrastrukturprojekt, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 61.55,
y: 22.35,
description: 'pv',
},
{
id: 'bosbüll-25899',
name: 'PV-Infrastrukturprojekt, Region Emsland / Ostfriesland',
type: 'minor_node',
x: 35.36,
y: 5,
description: 'pv',
},
{
id: 'schwarzhofen-92447',
name: 'PV-Infrastrukturprojekt, Großraum Stuttgart / Franken',
type: 'minor_node',
x: 64.76,
y: 73.46,
description: 'pv',
},
{
id: 'göhlsdorf-14797',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 68.78,
y: 35.98,
description: 'pv',
},
{
id: 'zehdenick-16792',
name: 'Trassenbau Hochspannung, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 73.35,
y: 28.02,
description: 'power',
},
{
id: 'osterburg-39606',
name: 'Netzanbindung Windenergie, Region Hannover / Braunschweig',
type: 'minor_node',
x: 59.63,
y: 30.48,
description: 'wind',
},
{
id: 'holzheim-89438',
name: 'PV-Infrastrukturprojekt, Großraum Stuttgart / Franken',
type: 'minor_node',
x: 49.58,
y: 84.44,
description: 'pv',
},
{
id: 'adelschlag-85111',
name: 'PV-Infrastrukturprojekt, Großraum Stuttgart / Franken',
type: 'minor_node',
x: 55.23,
y: 80.23,
description: 'pv',
},
{
id: 'hoyerswerda-02977',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 80.8,
y: 47.74,
description: 'pv',
},
{
id: 'niederhummel-85416',
name: 'PV-Infrastrukturprojekt, Großraum Stuttgart / Franken',
type: 'minor_node',
x: 60.98,
y: 85.4,
description: 'pv',
},
{
id: 'löffingen-79843',
name: 'PV-Infrastrukturprojekt, Region Schwarzwald / Bodensee',
type: 'minor_node',
x: 31.06,
y: 92.31,
description: 'pv',
},
{
id: 'holste-27729',
name: 'Netzanbindung Windenergie, Region Emsland / Ostfriesland',
type: 'minor_node',
x: 35.35,
y: 23.24,
description: 'wind',
},
{
id: 'lasbek-23847',
name: 'Netzanbindung Windenergie, Metropolregion Hamburg',
type: 'minor_node',
x: 48.1,
y: 18.71,
description: 'wind',
},
{
id: 'bernau-bei-berlin-16321',
name: 'Netzanbindung Windenergie, Region Berlin / Brandenburg',
type: 'minor_node',
x: 75.39,
y: 31.83,
description: 'wind',
},
{
id: 'gorgast-15328',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 83.44,
y: 33.39,
description: 'pv',
},
{
id: 'neustadt-dosse-16845',
name: 'Netzanbindung Windenergie, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 65.82,
y: 29.83,
description: 'wind',
},
{
id: 'halle-06132',
name: 'Breitbandausbau / FTTx, Region Berlin / Brandenburg',
type: 'minor_node',
x: 61.88,
y: 47.76,
description: 'fiber',
},
{
id: 'letschin-15324',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 82.02,
y: 32.11,
description: 'pv',
},
{
id: 'sallgast-03238',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 77.65,
y: 45.69,
description: 'pv',
},
{
id: 'rodleben-dessau-roßlau-06861',
name: 'Breitbandausbau / FTTx, Region Berlin / Brandenburg',
type: 'minor_node',
x: 63.69,
y: 41.69,
description: 'fiber',
},
{
id: 'gerbstedt-06347',
name: 'Netzanbindung Windenergie, Region Hannover / Braunschweig',
type: 'minor_node',
x: 58.88,
y: 45.36,
description: 'wind',
},
{
id: 'elsterwerda-04910',
name: 'Breitbandausbau / FTTx, Region Berlin / Brandenburg',
type: 'minor_node',
x: 74.88,
y: 47.26,
description: 'fiber',
},
{
id: 'magdeburg-39106',
name: 'PV-Infrastrukturprojekt, Region Hannover / Braunschweig',
type: 'minor_node',
x: 59.01,
y: 38.7,
description: 'pv',
},
{
id: 'staßfurt-39443',
name: 'BESS-Anbindung, Region Hannover / Braunschweig',
type: 'minor_node',
x: 58.97,
y: 41.85,
description: 'battery',
},
{
id: 'alsleben-06425',
name: 'Netzanbindung Windenergie, Region Hannover / Braunschweig',
type: 'minor_node',
x: 59.15,
y: 44.01,
description: 'wind',
},
{
id: 'nienbüttel-25596',
name: 'PV-Infrastrukturprojekt, Metropolregion Hamburg',
type: 'minor_node',
x: 40.06,
y: 14.97,
description: 'pv',
},
{
id: 'werneuchen-16356',
name: 'Netzanbindung Windenergie, Region Berlin / Brandenburg',
type: 'minor_node',
x: 76.31,
y: 32.58,
description: 'wind',
},
{
id: 'stüdenitz-schönermark-16845',
name: 'PV-Infrastrukturprojekt, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 64.43,
y: 29.18,
description: 'pv',
},
{
id: 'gumtow-16866',
name: 'Netzanbindung Windenergie, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 64.12,
y: 28.2,
description: 'wind',
},
{
id: 'nöbdenitz-04626',
name: 'Breitbandausbau / FTTx, Region Leipzig / Dresden',
type: 'minor_node',
x: 64.59,
y: 54.41,
description: 'fiber',
},
{
id: 'kröpelin-18236',
name: 'Netzanbindung Windenergie, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 60.24,
y: 14.56,
description: 'wind',
},
{
id: 'laer-48366',
name: 'Netzanbindung Windenergie, Region Münsterland',
type: 'minor_node',
x: 22.68,
y: 39.78,
description: 'wind',
},
{
id: 'ostbevern-48346',
name: 'Netzanbindung Windenergie, Region Münsterland',
type: 'minor_node',
x: 26.64,
y: 39.84,
description: 'wind',
},
{
id: 'schwarmstedt-29690',
name: 'Breitbandausbau / FTTx, Region Hannover / Braunschweig',
type: 'minor_node',
x: 41.89,
y: 31.93,
description: 'fiber',
},
{
id: 'prötzel-15345',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 78.84,
y: 32.5,
description: 'pv',
},
{
id: 'blindheim-89434',
name: 'PV-Infrastrukturprojekt, Großraum Stuttgart / Franken',
type: 'minor_node',
x: 50.23,
y: 82.72,
description: 'pv',
},
{
id: 'großräschen-01983',
name: 'Netzanbindung Windenergie, Region Berlin / Brandenburg',
type: 'minor_node',
x: 78.82,
y: 45.64,
description: 'wind',
},
{
id: 'wolkenstein-09429',
name: 'Netzanbindung Windenergie, Region Leipzig / Dresden',
type: 'minor_node',
x: 71.1,
y: 57.44,
description: 'wind',
},
{
id: 'fronhausen-35112',
name: 'Netzanbindung Windenergie, Rhein-Ruhr-Gebiet',
type: 'minor_node',
x: 34.1,
y: 56.83,
description: 'wind',
},
{
id: 'dammfleth-25554',
name: 'Netzanbindung Windenergie, Region Emsland / Ostfriesland',
type: 'minor_node',
x: 39.62,
y: 16.66,
description: 'wind',
},
{
id: 'langwedel-24631',
name: 'PV-Infrastrukturprojekt, Metropolregion Hamburg',
type: 'minor_node',
x: 44.46,
y: 12.6,
description: 'pv',
},
{
id: 'jerchel-14715',
name: 'PV-Infrastrukturprojekt, Region Hannover / Braunschweig',
type: 'minor_node',
x: 56.22,
y: 34.63,
description: 'pv',
},
{
id: 'beeskow-15848',
name: 'Netzanbindung Windenergie, Region Berlin / Brandenburg',
type: 'minor_node',
x: 80.9,
y: 38.43,
description: 'wind',
},
{
id: 'perleberg-19348',
name: 'Netzanbindung Windenergie, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 60.84,
y: 26.25,
description: 'wind',
},
{
id: 'gilching-82205',
name: 'PV-Infrastrukturprojekt, Großraum Stuttgart / Franken',
type: 'minor_node',
x: 55.84,
y: 89.45,
description: 'pv',
},
{
id: 'bodenwöhr-92439',
name: 'PV-Infrastrukturprojekt, Großraum Stuttgart / Franken',
type: 'minor_node',
x: 64.41,
y: 74.55,
description: 'pv',
},
{
id: 'görne-14728',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 66.34,
y: 31.66,
description: 'pv',
},
{
id: 'agethorst-25560',
name: 'PV-Infrastrukturprojekt, Metropolregion Hamburg',
type: 'minor_node',
x: 40.21,
y: 15.07,
description: 'pv',
},
{
id: 'ansbach-91522',
name: 'Netzanbindung Windenergie, Großraum Stuttgart / Franken',
type: 'minor_node',
x: 49.8,
y: 74.59,
description: 'wind',
},
{
id: 'schinne-39579',
name: 'Netzanbindung Windenergie, Region Hannover / Braunschweig',
type: 'minor_node',
x: 59.69,
y: 32.23,
description: 'wind',
},
{
id: 'neuengörs-23818',
name: 'Netzanbindung Windenergie, Metropolregion Hamburg',
type: 'minor_node',
x: 48.33,
y: 16.63,
description: 'wind',
},
{
id: 'denkendorf-85095',
name: 'PV-Infrastrukturprojekt, Großraum Stuttgart / Franken',
type: 'minor_node',
x: 57.48,
y: 79.18,
description: 'pv',
},
{
id: 'lindhorst-31698',
name: 'Netzanbindung Windenergie, Region Hannover / Braunschweig',
type: 'minor_node',
x: 38.98,
y: 35.98,
description: 'wind',
},
{
id: 'rohrenfels-86701',
name: 'PV-Infrastrukturprojekt, Großraum Stuttgart / Franken',
type: 'minor_node',
x: 54.89,
y: 82.18,
description: 'pv',
},
{
id: 'ingoldingen-88456',
name: 'PV-Infrastrukturprojekt, Region Schwarzwald / Bodensee',
type: 'minor_node',
x: 42.75,
y: 90.62,
description: 'pv',
},
{
id: 'lichtenau-33165',
name: 'Netzanbindung Windenergie, Region Hannover / Braunschweig',
type: 'minor_node',
x: 35.64,
y: 45.44,
description: 'wind',
},
{
id: 'nochten-02943',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 84.03,
y: 47.68,
description: 'pv',
},
{
id: 'dalkendorf-17166',
name: 'Netzanbindung Windenergie, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 66.23,
y: 17.67,
description: 'wind',
},
{
id: 'schopfheim-79650',
name: 'Netzanbindung Windenergie, Region Schwarzwald / Bodensee',
type: 'minor_node',
x: 26.98,
y: 94.96,
description: 'wind',
},
{
id: 'lunow-stolzenhagen-16248',
name: 'Trassenbau Hochspannung, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 79.88,
y: 28.84,
description: 'power',
},
{
id: 'petershagen-15326',
name: 'PV-Infrastrukturprojekt, Region Hannover / Braunschweig',
type: 'minor_node',
x: 36.26,
y: 35.79,
description: 'pv',
},
{
id: 'krevese-39606',
name: 'Netzanbindung Windenergie, Region Hannover / Braunschweig',
type: 'minor_node',
x: 59.37,
y: 30.29,
description: 'wind',
},
{
id: 'seckeritz-17440',
name: 'PV-Infrastrukturprojekt, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 76.91,
y: 15.43,
description: 'pv',
},
{
id: 'tempelfelde-16230',
name: 'Netzanbindung Windenergie, Region Berlin / Brandenburg',
type: 'minor_node',
x: 76.55,
y: 31.59,
description: 'wind',
},
{
id: 'altenmedingen-29575',
name: 'Netzanbindung Windenergie, Metropolregion Hamburg',
type: 'minor_node',
x: 50.28,
y: 26.09,
description: 'wind',
},
{
id: 'neiße-malxetal-03159',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 84.5,
y: 45.15,
description: 'pv',
},
{
id: 'eisenhüttenstadt-15890',
name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
type: 'minor_node',
x: 84.27,
y: 38.7,
description: 'pv',
},
{
id: 'groß-pankow-16928',
name: 'Netzanbindung Windenergie, Region Mecklenburg-Vorpommern',
type: 'minor_node',
x: 62.41,
y: 26.45,
description: 'wind',
},
{
id: 'dahlhausen-16909',
name: 'PV-Infrastrukturprojekt, Rhein-Ruhr-Gebiet',
type: 'minor_node',
x: 22.21,
y: 50.39,
description: 'pv',
},
{
id: 'gnarrenburg-27442',
name: 'Netzanbindung Windenergie, Region Emsland / Ostfriesland',
type: 'minor_node',
x: 36.74,
y: 23.17,
description: 'wind',
},
{
id: 'greding-91171',
name: 'PV-Infrastrukturprojekt, Großraum Stuttgart / Franken',
type: 'minor_node',
x: 56.38,
y: 77.43,
description: 'pv',
},
{
id: 'scheeßel-27383',
name: 'Netzanbindung Windenergie, Metropolregion Hamburg',
type: 'minor_node',
x: 40.7,
y: 25.88,
description: 'wind',
},
{
id: 'egling-an-der-paar-86492',
name: 'PV-Infrastrukturprojekt, Großraum Stuttgart / Franken',
type: 'minor_node',
x: 53.41,
y: 88.51,
description: 'pv',
},
{
id: 'ellingen-91792',
name: 'PV-Infrastrukturprojekt, Großraum Stuttgart / Franken',
type: 'minor_node',
x: 52.92,
y: 77.37,
description: 'pv',
},
];

View File

@@ -9,6 +9,7 @@ export interface ReferenceFrontmatter {
dateString?: string;
featuredImage?: string | null;
location: string;
category?: string | string[];
}
export interface ReferenceData {

5
test-build-env.js Normal file
View File

@@ -0,0 +1,5 @@
const val = process.env.NEXT_PUBLIC_APP_VERSION;
console.log("val is:", val);
if (val) {
console.log("replace:", val.replace(/^v/, ''));
}