Compare commits
33 Commits
v2.2.13-rc
...
v2.2.13-rc
| Author | SHA1 | Date | |
|---|---|---|---|
| d09b576137 | |||
| 04599827f9 | |||
| 5368f1a64d | |||
| 241c97e346 | |||
| 3334e327bf | |||
| 145bff90b6 | |||
| d2bf48fc60 | |||
| 5e7ee84dc9 | |||
| b42e53495d | |||
| 8813ed3910 | |||
| 1d3c6a0f70 | |||
| 8fb3b96bd3 | |||
| 67dc56d3ee | |||
| e5933bad69 | |||
| 11b9e4630b | |||
| a47d378521 | |||
| 30047cb1c4 | |||
| 394ac5a19c | |||
| d63f196693 | |||
| 6092f1d827 | |||
| 766c385704 | |||
| 6c9d2b6832 | |||
| 7a9d4d7f18 | |||
| 0b9929c2ec | |||
| aa0dfdf331 | |||
| 4c9028f58b | |||
| 9de5f2938b | |||
| de41a0e29d | |||
| 6689dd3013 | |||
| 31fbcb32dd | |||
| 68eca73df2 | |||
| 3bb6eb3447 | |||
| a3c732dd13 |
@@ -17,9 +17,6 @@ env:
|
|||||||
PUPPETEER_SKIP_DOWNLOAD: "true"
|
PUPPETEER_SKIP_DOWNLOAD: "true"
|
||||||
COREPACK_NPM_REGISTRY: "https://registry.npmmirror.com"
|
COREPACK_NPM_REGISTRY: "https://registry.npmmirror.com"
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: deploy-pipeline
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
@@ -146,6 +143,13 @@ jobs:
|
|||||||
echo "short_sha=$SHORT_SHA"
|
echo "short_sha=$SHORT_SHA"
|
||||||
} >> "$GITHUB_OUTPUT"
|
} >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: 🚦 Priority Gate
|
||||||
|
env:
|
||||||
|
GITEA_PAT: ${{ secrets.GITEA_PAT }}
|
||||||
|
CURRENT_RUN_ID: ${{ github.run_id }}
|
||||||
|
CURRENT_TARGET: ${{ steps.determine.outputs.target }}
|
||||||
|
run: bash scripts/priority-gate.sh
|
||||||
|
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
# JOB 2: QA (Lint, Typecheck, Test)
|
# JOB 2: QA (Lint, Typecheck, Test)
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
1
.turbo/41698960411f6bf6-manifest.json
Normal file
1
.turbo/41698960411f6bf6-manifest.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"files":{".turbo/turbo-test.log":{"size":2776,"mtime_nanos":1781469605328811961,"mode":420,"is_dir":false}},"order":[".turbo/turbo-test.log"]}
|
||||||
1
.turbo/41698960411f6bf6-meta.json
Normal file
1
.turbo/41698960411f6bf6-meta.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"hash":"41698960411f6bf6","duration":4168,"sha":"a3c732dd133b1b8261b19cf64b3bf684e269e839","dirty_hash":"1ef3b75af417f41468b9558671572e75e0fc055f9dbe6e805cfa6f59b1cddf70"}
|
||||||
BIN
.turbo/41698960411f6bf6.tar.zst
Normal file
BIN
.turbo/41698960411f6bf6.tar.zst
Normal file
Binary file not shown.
1
.turbo/bb46018d4a7bc516-manifest.json
Normal file
1
.turbo/bb46018d4a7bc516-manifest.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"files":{".turbo/turbo-lint.log":{"size":12987,"mtime_nanos":1781469609469080321,"mode":420,"is_dir":false}},"order":[".turbo/turbo-lint.log"]}
|
||||||
1
.turbo/bb46018d4a7bc516-meta.json
Normal file
1
.turbo/bb46018d4a7bc516-meta.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"hash":"bb46018d4a7bc516","duration":8307,"sha":"a3c732dd133b1b8261b19cf64b3bf684e269e839","dirty_hash":"1ef3b75af417f41468b9558671572e75e0fc055f9dbe6e805cfa6f59b1cddf70"}
|
||||||
BIN
.turbo/bb46018d4a7bc516.tar.zst
Normal file
BIN
.turbo/bb46018d4a7bc516.tar.zst
Normal file
Binary file not shown.
1
.turbo/eb4eeeb717c1af0c-manifest.json
Normal file
1
.turbo/eb4eeeb717c1af0c-manifest.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"files":{".turbo/turbo-typecheck.log":{"size":85,"mtime_nanos":1781469608726900998,"mode":420,"is_dir":false}},"order":[".turbo/turbo-typecheck.log"]}
|
||||||
1
.turbo/eb4eeeb717c1af0c-meta.json
Normal file
1
.turbo/eb4eeeb717c1af0c-meta.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"hash":"eb4eeeb717c1af0c","duration":7565,"sha":"a3c732dd133b1b8261b19cf64b3bf684e269e839","dirty_hash":"1ef3b75af417f41468b9558671572e75e0fc055f9dbe6e805cfa6f59b1cddf70"}
|
||||||
BIN
.turbo/eb4eeeb717c1af0c.tar.zst
Normal file
BIN
.turbo/eb4eeeb717c1af0c.tar.zst
Normal file
Binary file not shown.
@@ -2,128 +2,4 @@
|
|||||||
> e-tib-nextjs@2.2.12 lint /Volumes/Alpha SSD/Coding/e-tib.com
|
> e-tib-nextjs@2.2.12 lint /Volumes/Alpha SSD/Coding/e-tib.com
|
||||||
> eslint .
|
> eslint .
|
||||||
|
|
||||||
|
ELIFECYCLE Command failed.
|
||||||
/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
|
|
||||||
75: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
|
|
||||||
92: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
|
|
||||||
102: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:102:5
|
|
||||||
100 |
|
|
||||||
101 | useEffect(() => {
|
|
||||||
> 102 | setIsMounted(true);
|
|
||||||
| ^^^^^^^^^^^^ Avoid calling setState() directly within an effect
|
|
||||||
103 | }, []);
|
|
||||||
104 |
|
|
||||||
105 | 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:18 warning 'Factory' is defined but never used @typescript-eslint/no-unused-vars
|
|
||||||
5:27 warning 'Zap' is defined but never used @typescript-eslint/no-unused-vars
|
|
||||||
53:9 warning 'hq' is assigned a value but never used @typescript-eslint/no-unused-vars
|
|
||||||
54:9 warning 'branch' is assigned a value but never used @typescript-eslint/no-unused-vars
|
|
||||||
55:9 warning 'projects' is assigned a value but never used @typescript-eslint/no-unused-vars
|
|
||||||
56:9 warning 'minorNodes' is assigned a value but never used @typescript-eslint/no-unused-vars
|
|
||||||
|
|
||||||
/Volumes/Alpha SSD/Coding/e-tib.com/components/blocks/ReferencesSlider.test.tsx
|
|
||||||
5:10 warning 'NextIntlClientProvider' is defined but never used @typescript-eslint/no-unused-vars
|
|
||||||
21:12 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/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 missing dependencies: 'HERO_PLACEHOLDERS' and 't'. Either include them 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
|
|
||||||
100: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/components/ui/AnimatedCounter.tsx
|
|
||||||
3:28 warning 'useState' is defined but never used @typescript-eslint/no-unused-vars
|
|
||||||
|
|
||||||
/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
|
|
||||||
|
|
||||||
✖ 45 problems (0 errors, 45 warnings)
|
|
||||||
0 errors and 2 warnings potentially fixable with the `--fix` option.
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,27 +5,51 @@
|
|||||||
|
|
||||||
[1m[46m RUN [49m[22m [36mv4.1.2 [39m[90m/Volumes/Alpha SSD/Coding/e-tib.com[39m
|
[1m[46m RUN [49m[22m [36mv4.1.2 [39m[90m/Volumes/Alpha SSD/Coding/e-tib.com[39m
|
||||||
|
|
||||||
[32m✓[39m tests/mdx-links.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 4[2mms[22m[39m
|
(node:64961) [DEP0205] DeprecationWarning: `module.register()` is deprecated. Use `module.registerHooks()` instead.
|
||||||
[32m✓[39m lib/mail/mailer.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 2[2mms[22m[39m
|
(Use `node --trace-deprecation ...` to show where the warning was created)
|
||||||
[32m✓[39m lib/env.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 6[2mms[22m[39m
|
[32m✓[39m tests/mdx-links.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 29[2mms[22m[39m
|
||||||
|
[32m✓[39m tests/homepage-and-competence-content.test.ts [2m([22m[2m4 tests[22m[2m)[22m[32m 14[2mms[22m[39m
|
||||||
|
[31m❯[39m tests/legal-pages.test.ts [2m([22m[2m2 tests[22m[2m | [22m[31m1 failed[39m[2m)[22m[32m 9[2mms[22m[39m
|
||||||
|
[32m✓[39m should contain highly professional B2B sections in German AGB[32m 5[2mms[22m[39m
|
||||||
|
[31m [31m×[31m should contain professional corresponding B2B sections in English Terms[39m[32m 2[2mms[22m[39m
|
||||||
|
[32m✓[39m tests/task-12-links.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 8[2mms[22m[39m
|
||||||
|
[32m✓[39m tests/messen-content.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 8[2mms[22m[39m
|
||||||
|
[32m✓[39m lib/mail/mailer.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 7[2mms[22m[39m
|
||||||
[90mstderr[2m | components/layout/LanguageSwitcher.test.tsx[2m > [22m[2mLanguageSwitcher TDD[2m > [22m[2mtranslates /en/trade-fairs to /de/messen when switching to German
|
[90mstderr[2m | components/layout/LanguageSwitcher.test.tsx[2m > [22m[2mLanguageSwitcher TDD[2m > [22m[2mtranslates /en/trade-fairs to /de/messen when switching to German
|
||||||
[22m[39mReact does not recognize the `transitionMessage` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `transitionmessage` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
|
[22m[39mReact does not recognize the `transitionMessage` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `transitionmessage` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
|
||||||
|
|
||||||
[32m✓[39m components/layout/LanguageSwitcher.test.tsx [2m([22m[2m5 tests[22m[2m)[22m[32m 21[2mms[22m[39m
|
[32m✓[39m components/layout/LanguageSwitcher.test.tsx [2m([22m[2m5 tests[22m[2m)[22m[32m 25[2mms[22m[39m
|
||||||
[32m✓[39m components/search/AISearchResults.test.tsx [2m([22m[2m1 test[22m[2m)[22m[32m 26[2mms[22m[39m
|
[32m✓[39m components/search/AISearchResults.test.tsx [2m([22m[2m1 test[22m[2m)[22m[32m 52[2mms[22m[39m
|
||||||
[90mstderr[2m | components/blocks/ReferencesSlider.test.tsx[2m > [22m[2mReferencesSlider TDD[2m > [22m[2mrenders reference tiles correctly
|
[90mstderr[2m | components/blocks/ReferencesSlider.test.tsx[2m > [22m[2mReferencesSlider TDD[2m > [22m[2mrenders reference tiles correctly
|
||||||
[22m[39mReceived `true` for a non-boolean attribute `fill`.
|
[22m[39mReceived `true` for a non-boolean attribute `fill`.
|
||||||
|
|
||||||
If you want to write it to the DOM, pass a string instead: fill="true" or fill={value.toString()}.
|
If you want to write it to the DOM, pass a string instead: fill="true" or fill={value.toString()}.
|
||||||
|
|
||||||
[32m✓[39m components/blocks/ReferencesSlider.test.tsx [2m([22m[2m4 tests[22m[2m)[22m[32m 40[2mms[22m[39m
|
[32m✓[39m components/blocks/ReferencesSlider.test.tsx [2m([22m[2m2 tests[22m[2m)[22m[32m 43[2mms[22m[39m
|
||||||
|
[32m✓[39m lib/env.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 4[2mms[22m[39m
|
||||||
[90mstdout[2m | app/[locale]/referenzen/[slug]/page.test.tsx
|
[90mstdout[2m | app/[locale]/referenzen/[slug]/page.test.tsx
|
||||||
[22m[39m[Config] Initializing Toggles: { feedbackEnabled: [33mfalse[39m }
|
[22m[39m[Config] Initializing Toggles: { feedbackEnabled: [33mfalse[39m }
|
||||||
|
|
||||||
[32m✓[39m app/[locale]/referenzen/[slug]/page.test.tsx [2m([22m[2m1 test[22m[2m)[22m[32m 6[2mms[22m[39m
|
[32m✓[39m app/[locale]/referenzen/[slug]/page.test.tsx [2m([22m[2m1 test[22m[2m)[22m[32m 26[2mms[22m[39m
|
||||||
|
|
||||||
[2m Test Files [22m [1m[32m7 passed[39m[22m[90m (7)[39m
|
[31m⎯⎯⎯⎯⎯⎯⎯[39m[1m[41m Failed Tests 1 [49m[22m[31m⎯⎯⎯⎯⎯⎯⎯[39m
|
||||||
[2m Tests [22m [1m[32m19 passed[39m[22m[90m (19)[39m
|
|
||||||
[2m Start at [22m 14:12:48
|
|
||||||
[2m Duration [22m 1.80s[2m (transform 863ms, setup 0ms, import 2.94s, tests 105ms, environment 3.18s)[22m
|
|
||||||
|
|
||||||
|
[41m[1m FAIL [22m[49m tests/legal-pages.test.ts[2m > [22mLegal Pages Content Validation[2m > [22mshould contain professional corresponding B2B sections in English Terms
|
||||||
|
[31m[1mError[22m: ENOENT: no such file or directory, open '/Volumes/Alpha SSD/Coding/e-tib.com/content/en/terms.mdx'[39m
|
||||||
|
[36m [2m❯[22m tests/legal-pages.test.ts:[2m25:24[22m[39m
|
||||||
|
[90m 23|[39m
|
||||||
|
[90m 24|[39m it('should contain professional corresponding B2B sections in Englis…
|
||||||
|
[90m 25|[39m [35mconst[39m content [33m=[39m fs[33m.[39m[34mreadFileSync[39m(enTermsPath[33m,[39m [32m'utf8'[39m)[33m;[39m
|
||||||
|
[90m |[39m [31m^[39m
|
||||||
|
[90m 26|[39m
|
||||||
|
[90m 27|[39m [34mexpect[39m(content)[33m.[39m[34mtoContain[39m([32m'title: "Terms and Conditions"'[39m)[33m;[39m
|
||||||
|
|
||||||
|
[31m[2m⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯[22m[39m
|
||||||
|
|
||||||
|
|
||||||
|
[2m Test Files [22m [1m[31m1 failed[39m[22m[2m | [22m[1m[32m10 passed[39m[22m[90m (11)[39m
|
||||||
|
[2m Tests [22m [1m[31m1 failed[39m[22m[2m | [22m[1m[32m26 passed[39m[22m[90m (27)[39m
|
||||||
|
[2m Start at [22m 14:54:33
|
||||||
|
[2m Duration [22m 5.94s[2m (transform 5.27s, setup 0ms, import 9.43s, tests 222ms, environment 13.80s)[22m
|
||||||
|
|
||||||
|
ELIFECYCLE Test failed. See above for more details.
|
||||||
|
|||||||
@@ -2,3 +2,4 @@
|
|||||||
> e-tib-nextjs@2.2.12 typecheck /Volumes/Alpha SSD/Coding/e-tib.com
|
> e-tib-nextjs@2.2.12 typecheck /Volumes/Alpha SSD/Coding/e-tib.com
|
||||||
> tsc --noEmit
|
> tsc --noEmit
|
||||||
|
|
||||||
|
ELIFECYCLE Command failed.
|
||||||
|
|||||||
25
INSTRUCTIONS.md
Normal file
25
INSTRUCTIONS.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Antigravity Instructions & Project Rules
|
||||||
|
|
||||||
|
This document contains critical project-specific rules for the e-tib.com codebase. Antigravity must read and follow these rules at all times.
|
||||||
|
|
||||||
|
## 1. Bilingual Site (German & English) - STRICT
|
||||||
|
- **Always update BOTH languages!** The website is bilingual.
|
||||||
|
- When changing content, adding pages, or updating text, you MUST always check and update both `content/de/` and `content/en/` simultaneously.
|
||||||
|
- When adding new components with text, ensure they are built to support both languages or update the respective translation files/mechanisms. Do NOT just hardcode German text and forget the English version.
|
||||||
|
|
||||||
|
## 2. No Dummy Data / No Hallucinations
|
||||||
|
- Do not invent or hallucinate placeholder text (e.g., "Nachweis liegt vor" for certificates that don't exist).
|
||||||
|
- Only implement what is explicitly requested or what matches provided assets/data.
|
||||||
|
- If data is missing, ask the user instead of inventing placeholders.
|
||||||
|
|
||||||
|
## 3. Git Workflow & RC Tagging
|
||||||
|
- When instructed to "commit and tag" (or similar), always follow this process:
|
||||||
|
1. Add and commit the changes with conventional commits (`feat:`, `fix:`, `chore:`, etc.).
|
||||||
|
2. Check the latest tag using `git describe --tags --abbrev=0`.
|
||||||
|
3. Increment the RC (Release Candidate) number of the current version (e.g., if latest is `v2.2.13-rc19`, the new tag is `v2.2.13-rc20`).
|
||||||
|
4. Create the tag: `git tag vX.Y.Z-rcN`.
|
||||||
|
5. Push the commit and the tag: `git push origin main --tags`.
|
||||||
|
|
||||||
|
## 4. Work Exclusively on the Correct Branch
|
||||||
|
- Follow the `USER_GLOBAL` directive: If on `main`, create a branch unless explicitly told to work on `main`.
|
||||||
|
- For e-tib.com, often small fixes are done on `main` if the user commands it ("auf main arbeiten"), but always verify context.
|
||||||
BIN
after-login.png
Normal file
BIN
after-login.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 274 KiB |
@@ -10,7 +10,6 @@ import TrackedLink from '@/components/analytics/TrackedLink';
|
|||||||
import { getButtonClasses, ButtonOverlay } from '@/components/ui/Button';
|
import { getButtonClasses, ButtonOverlay } from '@/components/ui/Button';
|
||||||
import { getAllReferences } from '@/lib/references';
|
import { getAllReferences } from '@/lib/references';
|
||||||
import { defaultLocations, minorLocations } from '@/lib/map-data';
|
import { defaultLocations, minorLocations } from '@/lib/map-data';
|
||||||
|
|
||||||
// Import components used in MDX
|
// Import components used in MDX
|
||||||
import { HeroVideo as HomeHero } from '@/components/blocks/HeroVideo';
|
import { HeroVideo as HomeHero } from '@/components/blocks/HeroVideo';
|
||||||
import { SubCompanyTiles as HomeSubCompanyTiles } from '@/components/blocks/SubCompanyTiles';
|
import { SubCompanyTiles as HomeSubCompanyTiles } from '@/components/blocks/SubCompanyTiles';
|
||||||
@@ -187,7 +186,7 @@ export default async function Page(props: { params: Promise<{ locale: string; sl
|
|||||||
<div className={`flex flex-col min-h-screen ${isFullBleed ? '' : 'bg-white'}`}>
|
<div className={`flex flex-col min-h-screen ${isFullBleed ? '' : 'bg-white'}`}>
|
||||||
{/* Generic Hero Section (only for standard pages) */}
|
{/* Generic Hero Section (only for standard pages) */}
|
||||||
{!isFullBleed && (
|
{!isFullBleed && (
|
||||||
<section className="bg-primary-dark text-white pt-40 pb-12 md:pt-56 md:pb-16 min-h-[40vh] md:min-h-[45vh] flex flex-col justify-end relative overflow-hidden">
|
<section key={`hero-section-${slug}`} className="bg-primary-dark text-white pt-40 pb-12 md:pt-56 md:pb-16 min-h-[40vh] md:min-h-[45vh] flex flex-col justify-end relative overflow-hidden">
|
||||||
<div className="absolute inset-0 opacity-20 pointer-events-none">
|
<div className="absolute inset-0 opacity-20 pointer-events-none">
|
||||||
<div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(circle_at_center,_var(--tw-gradient-stops))] from-accent/50 via-transparent to-transparent" />
|
<div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(circle_at_center,_var(--tw-gradient-stops))] from-accent/50 via-transparent to-transparent" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { TransitionProvider } from '@/components/providers/TransitionProvider';
|
|||||||
import { PageTransitionShutter } from '@/components/providers/PageTransitionShutter';
|
import { PageTransitionShutter } from '@/components/providers/PageTransitionShutter';
|
||||||
import { InitialLoader } from '@/components/providers/InitialLoader';
|
import { InitialLoader } from '@/components/providers/InitialLoader';
|
||||||
import { CorporateBackground } from '@/components/decorations/CorporateBackground';
|
import { CorporateBackground } from '@/components/decorations/CorporateBackground';
|
||||||
|
import { EUFundingBadge } from '@/components/decorations/EUFundingBadge';
|
||||||
|
|
||||||
const inter = Inter({
|
const inter = Inter({
|
||||||
subsets: ['latin'],
|
subsets: ['latin'],
|
||||||
@@ -92,15 +93,19 @@ export default async function Layout(props: {
|
|||||||
url: `/${safeLocale}/${await mapFileSlugToTranslated('ueber-uns', safeLocale)}`,
|
url: `/${safeLocale}/${await mapFileSlugToTranslated('ueber-uns', safeLocale)}`,
|
||||||
children: [
|
children: [
|
||||||
{ label: safeLocale === 'de' ? 'Firma' : 'Company', url: `/${safeLocale}/${await mapFileSlugToTranslated('ueber-uns', safeLocale)}` },
|
{ label: safeLocale === 'de' ? 'Firma' : 'Company', url: `/${safeLocale}/${await mapFileSlugToTranslated('ueber-uns', safeLocale)}` },
|
||||||
{ label: safeLocale === 'de' ? 'Standorte' : 'Locations', url: `/${safeLocale}/standorte` },
|
|
||||||
{ label: safeLocale === 'de' ? 'Unser Team' : 'Our Team', url: `/${safeLocale}/team` },
|
{ label: safeLocale === 'de' ? 'Unser Team' : 'Our Team', url: `/${safeLocale}/team` },
|
||||||
{ label: safeLocale === 'de' ? 'Zertifikate' : 'Certificates', url: `/${safeLocale}/zertifikate` }
|
{ label: safeLocale === 'de' ? 'Zertifikate' : 'Certificates', url: `/${safeLocale}/zertifikate` },
|
||||||
|
{ label: safeLocale === 'de' ? 'Standorte' : 'Locations', url: `/${safeLocale}/standorte` }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: safeLocale === 'de' ? 'Referenzen' : 'References',
|
label: safeLocale === 'de' ? 'Referenzen' : 'References',
|
||||||
url: `/${safeLocale}/referenzen`
|
url: `/${safeLocale}/referenzen`
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: safeLocale === 'de' ? 'Karriere' : 'Career',
|
||||||
|
url: `/${safeLocale}/${safeLocale === 'de' ? 'karriere' : 'career'}`
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: safeLocale === 'de' ? 'Messen' : 'Events',
|
label: safeLocale === 'de' ? 'Messen' : 'Events',
|
||||||
url: `/${safeLocale}/${await mapFileSlugToTranslated('messen', safeLocale)}`
|
url: `/${safeLocale}/${await mapFileSlugToTranslated('messen', safeLocale)}`
|
||||||
@@ -198,6 +203,7 @@ export default async function Layout(props: {
|
|||||||
<PageTransitionShutter />
|
<PageTransitionShutter />
|
||||||
<SkipLink />
|
<SkipLink />
|
||||||
<Header navLinks={navLinks} />
|
<Header navLinks={navLinks} />
|
||||||
|
<EUFundingBadge />
|
||||||
<MobileBottomNav navLinks={navLinks} currentLocale={safeLocale} />
|
<MobileBottomNav navLinks={navLinks} currentLocale={safeLocale} />
|
||||||
|
|
||||||
<main
|
<main
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { SITE_URL } from '@/lib/schema';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { InteractiveGermanyMap } from '@/components/blocks/InteractiveGermanyMap';
|
import { InteractiveGermanyMap } from '@/components/blocks/InteractiveGermanyMap';
|
||||||
import { defaultLocations, minorLocations } from '@/lib/map-data';
|
import { defaultLocations, minorLocations } from '@/lib/map-data';
|
||||||
import { standorteLocations } from '@/lib/standorte-data';
|
|
||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: Promise<{
|
params: Promise<{
|
||||||
@@ -53,7 +52,7 @@ export default async function ReferenzenOverview(props: { params: Promise<{ loca
|
|||||||
|
|
||||||
const references = await getAllReferences(locale);
|
const references = await getAllReferences(locale);
|
||||||
|
|
||||||
const allLocations = [...standorteLocations, ...defaultLocations, ...minorLocations];
|
const allLocations = [...defaultLocations, ...minorLocations];
|
||||||
const enrichedLocations = allLocations.map(loc => {
|
const enrichedLocations = allLocations.map(loc => {
|
||||||
if (loc.type === 'project') {
|
if (loc.type === 'project') {
|
||||||
const ref = references.find(r => r.slug === loc.id);
|
const ref = references.find(r => r.slug === loc.id);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Container } from '@/components/ui';
|
import { Container, Badge, Heading } from '@/components/ui';
|
||||||
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
@@ -63,137 +63,122 @@ export default async function StandortDetail(props: { params: Promise<{ locale:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col min-h-screen bg-neutral-50 pb-16 md:pb-24 pt-32">
|
<div className="flex flex-col min-h-screen bg-white">
|
||||||
<Container>
|
{/* Generic Hero Section to match standard pages */}
|
||||||
{/* Header Section */}
|
<section key={`standort-hero-${slug}`} className="bg-primary-dark text-white pt-40 pb-12 md:pt-56 md:pb-16 min-h-[40vh] md:min-h-[45vh] flex flex-col justify-end relative overflow-hidden">
|
||||||
<div className="mb-12 md:mb-16">
|
<div className="absolute inset-0 opacity-20 pointer-events-none">
|
||||||
<div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-primary/10 border border-primary/20 text-primary text-xs font-bold uppercase tracking-wider mb-6">
|
<div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(circle_at_center,_var(--tw-gradient-stops))] from-accent/50 via-transparent to-transparent" />
|
||||||
<MapPin className="w-4 h-4" />
|
</div>
|
||||||
<span>{standort.type === 'hq' ? (safeLocale === 'de' ? 'Hauptsitz' : 'Headquarters') : (safeLocale === 'de' ? 'Niederlassung' : 'Branch')}</span>
|
<Container className="relative z-10">
|
||||||
|
<div className="max-w-4xl">
|
||||||
|
<Badge variant="accent" className="mb-4 md:mb-6 shadow-sm flex inline-flex items-center gap-2">
|
||||||
|
<MapPin className="w-4 h-4" />
|
||||||
|
{standort.type === 'hq' ? (safeLocale === 'de' ? 'Hauptsitz' : 'Headquarters') : (safeLocale === 'de' ? 'Niederlassung' : 'Branch')}
|
||||||
|
</Badge>
|
||||||
|
<Heading level={1} variant="white" className="mb-0 drop-shadow-sm">
|
||||||
|
{standort.name}
|
||||||
|
</Heading>
|
||||||
</div>
|
</div>
|
||||||
<h1 className="font-heading text-4xl lg:text-5xl xl:text-6xl font-extrabold mb-6 leading-[1.1] text-neutral-dark text-balance">
|
</Container>
|
||||||
{standort.name}
|
</section>
|
||||||
</h1>
|
|
||||||
<p className="text-xl text-neutral-500 max-w-3xl leading-relaxed">
|
{/* Main Content Area */}
|
||||||
|
<Container className="py-16 md:py-24">
|
||||||
|
{/* Excerpt/Lead paragraph */}
|
||||||
|
<div className="mb-16 max-w-4xl mx-auto">
|
||||||
|
<p className="text-xl md:text-2xl text-text-primary leading-relaxed font-medium border-l-4 border-primary pl-8 py-2 italic">
|
||||||
{standort.description[safeLocale]}
|
{standort.description[safeLocale]}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:gap-12">
|
{/* Main content in prose style */}
|
||||||
{/* Main Content (Images & Description) */}
|
<div className="max-w-4xl mx-auto">
|
||||||
<div className="lg:col-span-8 space-y-8">
|
<div className="prose prose-lg md:prose-xl prose-neutral max-w-none prose-headings:font-heading prose-headings:font-bold prose-headings:tracking-tight prose-h1:text-4xl prose-h2:text-3xl prose-h2:mt-12 prose-h2:border-b prose-h2:border-neutral-200 prose-h2:pb-4 prose-h3:text-2xl prose-p:text-text-secondary prose-p:leading-relaxed prose-a:text-primary prose-a:font-medium prose-a:underline prose-a:underline-offset-4 prose-a:decoration-primary/30 hover:prose-a:decoration-primary prose-strong:text-text-primary prose-strong:font-semibold prose-li:text-text-secondary prose-ul:list-disc prose-ol:list-decimal">
|
||||||
{/* Featured Image */}
|
|
||||||
<div className="relative aspect-video w-full rounded-3xl overflow-hidden bg-neutral-900 shadow-xl border border-neutral-200">
|
|
||||||
<Image
|
|
||||||
src={standort.image}
|
|
||||||
alt={standort.name}
|
|
||||||
fill
|
|
||||||
className="object-cover"
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-black/40 to-transparent" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Key Features */}
|
<img className="rounded-2xl shadow-2xl my-12 max-w-full h-auto border border-neutral-100" src={standort.image} alt={standort.name} />
|
||||||
<div className="bg-white rounded-3xl p-8 md:p-10 shadow-sm border border-neutral-100">
|
|
||||||
<h2 className="text-2xl font-bold font-heading mb-6 text-neutral-dark">
|
<h2 className="mt-16 mb-6 border-b border-neutral-100 pb-4">{safeLocale === 'de' ? 'Unsere Leistungen vor Ort' : 'Our Local Services'}</h2>
|
||||||
{safeLocale === 'de' ? 'Unsere Leistungen vor Ort' : 'Our Local Services'}
|
<ul className="list-none mb-8 space-y-3 text-base md:text-lg text-text-secondary font-medium max-w-3xl">
|
||||||
</h2>
|
{standort.keyFeatures[safeLocale].map((feature, idx) => (
|
||||||
<ul className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<li key={idx} className="relative pl-6 before:content-[''] before:absolute before:left-0 before:top-[0.6em] before:w-2 before:h-2 before:bg-primary/50 before:rounded-sm">
|
||||||
{standort.keyFeatures[safeLocale].map((feature, idx) => (
|
{feature}
|
||||||
<li key={idx} className="flex items-start gap-3">
|
</li>
|
||||||
<CheckCircle2 className="w-5 h-5 text-primary shrink-0 mt-0.5" />
|
))}
|
||||||
<span className="text-neutral-700 font-medium">{feature}</span>
|
</ul>
|
||||||
</li>
|
|
||||||
))}
|
<h2 className="mt-16 mb-6 border-b border-neutral-100 pb-4">{safeLocale === 'de' ? 'Kontakt & Anfahrt' : 'Contact & Directions'}</h2>
|
||||||
</ul>
|
|
||||||
</div>
|
<p className="text-base md:text-lg text-text-secondary leading-[1.8] mb-6 font-medium max-w-3xl">
|
||||||
|
<strong>{safeLocale === 'de' ? 'Adresse:' : 'Address:'}</strong><br/>
|
||||||
|
{standort.address.street}<br/>
|
||||||
|
{standort.address.city}
|
||||||
|
</p>
|
||||||
|
<p className="text-base md:text-lg text-text-secondary leading-[1.8] mb-6 font-medium max-w-3xl">
|
||||||
|
<TrackedLink
|
||||||
|
href={`https://maps.google.com/?q=${encodeURIComponent(`${standort.address.street}, ${standort.address.city}`)}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-primary hover:text-primary-dark underline decoration-primary/30 underline-offset-4 hover:decoration-primary transition-all font-bold flex items-center gap-1 w-fit"
|
||||||
|
eventProperties={{ location: 'standort_maps_link', slug: standort.id }}
|
||||||
|
>
|
||||||
|
{safeLocale === 'de' ? 'In Google Maps öffnen' : 'Open in Google Maps'}
|
||||||
|
<ArrowUpRight className="w-4 h-4 inline-block" />
|
||||||
|
</TrackedLink>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className="text-base md:text-lg text-text-secondary leading-[1.8] mb-6 font-medium max-w-3xl">
|
||||||
|
<strong>{safeLocale === 'de' ? 'Telefon:' : 'Phone:'}</strong><br/>
|
||||||
|
<a href={`tel:${standort.contact.phone.replace(/[^0-9+]/g, '')}`} className="text-primary hover:text-primary-dark underline decoration-primary/30 underline-offset-4 hover:decoration-primary transition-all font-bold">
|
||||||
|
{standort.contact.phone}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className="text-base md:text-lg text-text-secondary leading-[1.8] mb-6 font-medium max-w-3xl">
|
||||||
|
<strong>Email:</strong><br/>
|
||||||
|
<a href={`mailto:${standort.contact.email}`} className="text-primary hover:text-primary-dark underline decoration-primary/30 underline-offset-4 hover:decoration-primary transition-all font-bold">
|
||||||
|
{standort.contact.email}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
{/* Gallery (if any) */}
|
|
||||||
{standort.gallery && standort.gallery.length > 0 && (
|
{standort.gallery && standort.gallery.length > 0 && (
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<>
|
||||||
{standort.gallery.map((img, idx) => (
|
<h2 className="mt-16 mb-6 border-b border-neutral-100 pb-4">Galerie</h2>
|
||||||
<div key={idx} className="relative aspect-[4/3] rounded-2xl overflow-hidden bg-neutral-100">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 not-prose my-12">
|
||||||
<Image src={img} alt={`Gallery ${idx + 1}`} fill className="object-cover" />
|
{standort.gallery.map((img, idx) => (
|
||||||
</div>
|
<div key={idx} className="relative aspect-[4/3] rounded-2xl overflow-hidden bg-neutral-100 border border-neutral-200 shadow-md hover:shadow-xl transition-shadow duration-300">
|
||||||
))}
|
<Image src={img} alt={`Gallery ${idx + 1}`} fill className="object-cover transition-transform duration-700 hover:scale-105" />
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
|
||||||
{/* Sidebar (Contact & Address) */}
|
{/* Support Section */}
|
||||||
<div className="lg:col-span-4">
|
<Container>
|
||||||
<div className="sticky top-32 space-y-6">
|
<div className="mt-12 mb-24 p-8 md:p-12 bg-primary-dark rounded-3xl text-white shadow-2xl relative overflow-hidden group animate-slight-fade-in-from-bottom">
|
||||||
|
<div className="absolute top-0 right-0 w-64 h-full bg-accent/5 -skew-x-12 translate-x-1/2 transition-transform group-hover:translate-x-1/3" />
|
||||||
{/* Contact Card */}
|
<div className="relative z-10 max-w-2xl">
|
||||||
<div className="bg-[#050B14] text-white rounded-3xl p-8 shadow-2xl relative overflow-hidden group">
|
<h3 className="text-2xl md:text-3xl font-bold mb-4">{t('needHelp')}</h3>
|
||||||
<AnimatedGlossyBorder opacity={0.5} className="z-10" />
|
<p className="text-lg text-white/70 mb-8">{t('supportTeamAvailable')}</p>
|
||||||
<div className="absolute top-0 right-0 w-64 h-full bg-primary/5 blur-3xl -translate-y-1/2 translate-x-1/3" />
|
<TrackedLink
|
||||||
|
href={`/${locale}/${locale === 'de' ? 'kontakt' : 'contact'}`}
|
||||||
<div className="relative z-20">
|
className={getButtonClasses('accent', 'lg')}
|
||||||
<h3 className="text-xl font-bold font-heading mb-6 flex items-center gap-2">
|
eventProperties={{
|
||||||
<Navigation className="w-5 h-5 text-primary" />
|
location: 'standort_page_support_cta',
|
||||||
{safeLocale === 'de' ? 'Kontakt & Anfahrt' : 'Contact & Directions'}
|
page_slug: slug,
|
||||||
</h3>
|
}}
|
||||||
|
>
|
||||||
<div className="space-y-6">
|
<span className="relative z-10 flex items-center justify-center gap-2 transition-colors duration-500 group-hover/btn:text-primary-dark">
|
||||||
<div className="flex items-start gap-4">
|
{t('contactUs')}
|
||||||
<div className="w-10 h-10 rounded-full bg-white/5 flex items-center justify-center shrink-0">
|
<span className="ml-2 transition-transform group-hover/btn:translate-x-1">
|
||||||
<MapPin className="w-5 h-5 text-primary" />
|
→
|
||||||
</div>
|
</span>
|
||||||
<div>
|
</span>
|
||||||
<div className="text-xs text-white/50 font-bold uppercase tracking-wider mb-1">
|
<ButtonOverlay variant="accent" />
|
||||||
{safeLocale === 'de' ? 'Adresse' : 'Address'}
|
</TrackedLink>
|
||||||
</div>
|
|
||||||
<div className="text-white font-medium">
|
|
||||||
{standort.address.street}<br />
|
|
||||||
{standort.address.city}
|
|
||||||
</div>
|
|
||||||
<TrackedLink
|
|
||||||
href={`https://maps.google.com/?q=${encodeURIComponent(`${standort.address.street}, ${standort.address.city}`)}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="inline-flex items-center gap-1 mt-2 text-primary hover:text-white transition-colors text-sm font-bold"
|
|
||||||
eventProperties={{ location: 'standort_maps_link', slug: standort.id }}
|
|
||||||
>
|
|
||||||
{safeLocale === 'de' ? 'In Google Maps öffnen' : 'Open in Google Maps'}
|
|
||||||
<ArrowUpRight className="w-3.5 h-3.5" />
|
|
||||||
</TrackedLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-start gap-4">
|
|
||||||
<div className="w-10 h-10 rounded-full bg-white/5 flex items-center justify-center shrink-0">
|
|
||||||
<Phone className="w-5 h-5 text-primary" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-xs text-white/50 font-bold uppercase tracking-wider mb-1">
|
|
||||||
{safeLocale === 'de' ? 'Telefon' : 'Phone'}
|
|
||||||
</div>
|
|
||||||
<a href={`tel:${standort.contact.phone.replace(/[^0-9+]/g, '')}`} className="text-white font-medium hover:text-primary transition-colors">
|
|
||||||
{standort.contact.phone}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-start gap-4">
|
|
||||||
<div className="w-10 h-10 rounded-full bg-white/5 flex items-center justify-center shrink-0">
|
|
||||||
<Mail className="w-5 h-5 text-primary" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-xs text-white/50 font-bold uppercase tracking-wider mb-1">
|
|
||||||
Email
|
|
||||||
</div>
|
|
||||||
<a href={`mailto:${standort.contact.email}`} className="text-white font-medium hover:text-primary transition-colors">
|
|
||||||
{standort.contact.email}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { MapPin, Navigation, ArrowUpRight } from 'lucide-react';
|
|||||||
import { getButtonClasses, ButtonOverlay } from '@/components/ui/Button';
|
import { getButtonClasses, ButtonOverlay } from '@/components/ui/Button';
|
||||||
import { SITE_URL } from '@/lib/schema';
|
import { SITE_URL } from '@/lib/schema';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { standorteData } from '@/lib/standorte-data';
|
import { standorteData, standorteLocations } from '@/lib/standorte-data';
|
||||||
import { InteractiveGermanyMap } from '@/components/blocks/InteractiveGermanyMap';
|
import { InteractiveGermanyMap } from '@/components/blocks/InteractiveGermanyMap';
|
||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
@@ -15,17 +15,15 @@ interface PageProps {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
|
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise<Metadata> {
|
||||||
const { locale } = await params;
|
const { locale } = await params;
|
||||||
if (locale !== 'de' && locale !== 'en') return {};
|
const safeLocale = locale === 'de' ? 'de' : 'en';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: locale === 'de' ? 'Standorte | E-TIB Gruppe' : 'Locations | E-TIB Group',
|
title: safeLocale === 'en' ? 'Our Locations' : 'Unsere Standorte',
|
||||||
description: locale === 'de'
|
description: safeLocale === 'en' ? 'Our operational locations across Germany.' : 'Unsere operativen Standorte deutschlandweit.',
|
||||||
? 'Die operativen Standorte der E-TIB Gruppe: Guben, Kirchheilingen und Bülstedt.'
|
|
||||||
: 'The operational locations of the E-TIB Group: Guben, Kirchheilingen and Bülstedt.',
|
|
||||||
alternates: {
|
alternates: {
|
||||||
canonical: `${SITE_URL}/${locale}/standorte`,
|
canonical: `${SITE_URL}/${safeLocale}/standorte`,
|
||||||
languages: {
|
languages: {
|
||||||
de: `${SITE_URL}/de/standorte`,
|
de: `${SITE_URL}/de/standorte`,
|
||||||
en: `${SITE_URL}/en/standorte`,
|
en: `${SITE_URL}/en/standorte`,
|
||||||
@@ -46,10 +44,11 @@ export default async function StandorteOverview(props: { params: Promise<{ local
|
|||||||
{/* Map Hero Section */}
|
{/* Map Hero Section */}
|
||||||
<InteractiveGermanyMap
|
<InteractiveGermanyMap
|
||||||
isHero={true}
|
isHero={true}
|
||||||
|
locations={standorteLocations}
|
||||||
badge={safeLocale === 'en' ? 'Our Locations' : 'Unsere Standorte'}
|
badge={safeLocale === 'en' ? 'Our Locations' : 'Unsere Standorte'}
|
||||||
title={safeLocale === 'en' ? 'Nationwide operational for you.' : 'Deutschlandweit für Sie im Einsatz.'}
|
title={safeLocale === 'en' ? 'Nationwide operational for you.' : 'Deutschlandweit für Sie im Einsatz.'}
|
||||||
description={safeLocale === 'en'
|
description={safeLocale === 'en'
|
||||||
? 'From our strategic locations in Guben, Kirchheilingen, and Bülstedt, we control and implement complex infrastructure projects nationwide.'
|
? 'From our strategic locations in Guben, Kirchheilingen and Bülstedt, we control and implement complex infrastructure projects nationwide.'
|
||||||
: 'Von unseren strategischen Standorten in Guben, Kirchheilingen und Bülstedt steuern und realisieren wir komplexe Infrastrukturprojekte im gesamten Bundesgebiet.'}
|
: 'Von unseren strategischen Standorten in Guben, Kirchheilingen und Bülstedt steuern und realisieren wir komplexe Infrastrukturprojekte im gesamten Bundesgebiet.'}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hide-scrollbar {
|
.hide-scrollbar {
|
||||||
-ms-overflow-style: none; /* IE and Edge */
|
-ms-overflow-style: none !important; /* IE and Edge */
|
||||||
scrollbar-width: none; /* Firefox */
|
scrollbar-width: none !important; /* Firefox */
|
||||||
}
|
}
|
||||||
.hide-scrollbar::-webkit-scrollbar {
|
.hide-scrollbar::-webkit-scrollbar {
|
||||||
display: none; /* Chrome, Safari and Opera */
|
display: none !important; /* Chrome, Safari and Opera */
|
||||||
|
width: 0 !important;
|
||||||
|
height: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes bg-pulse-15 {
|
@keyframes bg-pulse-15 {
|
||||||
|
|||||||
@@ -68,31 +68,7 @@ const defaultCertificates: Certificate[] = [
|
|||||||
type: 'tax',
|
type: 'tax',
|
||||||
date: '13.02.2025',
|
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) {
|
export function CertificatesBlock({ badge, title, description, certificates = defaultCertificates, hideHeader = false }: CertificatesBlockProps) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
|
import { usePathname } from 'next/navigation';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Badge, Container, Heading } from '@/components/ui';
|
import { Badge, Container, Heading } from '@/components/ui';
|
||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
@@ -39,13 +40,15 @@ const itemVariants = {
|
|||||||
export const HeroSection: React.FC<HeroSectionProps> = (props) => {
|
export const HeroSection: React.FC<HeroSectionProps> = (props) => {
|
||||||
const { title, badge, subtitle, backgroundImage, alignment, ctaLabel, ctaHref } = props;
|
const { title, badge, subtitle, backgroundImage, alignment, ctaLabel, ctaHref } = props;
|
||||||
const bgSrc = backgroundImage?.sizes?.card?.url || backgroundImage?.url;
|
const bgSrc = backgroundImage?.sizes?.card?.url || backgroundImage?.url;
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={`relative min-h-[45vh] md:min-h-[55vh] flex items-center pt-32 pb-20 md:pt-36 md:pb-24 overflow-hidden bg-neutral-dark ${alignment === 'center' ? 'justify-center text-center' : ''}`}
|
className={`relative min-h-[45vh] md:min-h-[55vh] flex items-center pt-24 pb-16 md:pt-36 md:pb-24 overflow-hidden bg-neutral-dark ${alignment === 'center' ? 'justify-center text-center' : ''}`}
|
||||||
>
|
>
|
||||||
{bgSrc && (
|
{bgSrc && (
|
||||||
<motion.div
|
<motion.div
|
||||||
|
key={`hero-bg-${pathname}`}
|
||||||
className="absolute inset-0 z-0"
|
className="absolute inset-0 z-0"
|
||||||
initial={{ scale: 1.05, opacity: 0 }}
|
initial={{ scale: 1.05, opacity: 0 }}
|
||||||
animate={{ scale: 1, opacity: 1 }}
|
animate={{ scale: 1, opacity: 1 }}
|
||||||
@@ -75,6 +78,7 @@ export const HeroSection: React.FC<HeroSectionProps> = (props) => {
|
|||||||
)}
|
)}
|
||||||
<Container className={`relative z-10 ${alignment === 'center' ? 'max-w-5xl' : ''}`}>
|
<Container className={`relative z-10 ${alignment === 'center' ? 'max-w-5xl' : ''}`}>
|
||||||
<motion.div
|
<motion.div
|
||||||
|
key={`hero-content-${pathname}`}
|
||||||
className={`max-w-4xl ${alignment === 'center' ? 'mx-auto' : ''}`}
|
className={`max-w-4xl ${alignment === 'center' ? 'mx-auto' : ''}`}
|
||||||
variants={containerVariants}
|
variants={containerVariants}
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Button } from '@/components/ui/Button';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
import { usePathname } from 'next/navigation';
|
||||||
|
|
||||||
interface HeroVideoProps {
|
interface HeroVideoProps {
|
||||||
data?: any;
|
data?: any;
|
||||||
@@ -28,6 +29,7 @@ export function HeroVideo(props: HeroVideoProps) {
|
|||||||
const subtitle = props.subtitle || props.description || data?.subtitle || 'Wir verbinden Infrastruktur mit Präzision. Von Horizontalbohrungen bis zu komplexen Leitungsnetzen.';
|
const subtitle = props.subtitle || props.description || data?.subtitle || 'Wir verbinden Infrastruktur mit Präzision. Von Horizontalbohrungen bis zu komplexen Leitungsnetzen.';
|
||||||
const explicitNoVideo = props.videoUrl === 'none' || props.videoUrl === '';
|
const explicitNoVideo = props.videoUrl === 'none' || props.videoUrl === '';
|
||||||
const videoUrl = explicitNoVideo ? null : (props.videoUrl || data?.videoUrl || '/assets/dummy-hero.mp4');
|
const videoUrl = explicitNoVideo ? null : (props.videoUrl || data?.videoUrl || '/assets/dummy-hero.mp4');
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
const posterSrc = props.backgroundImage?.url || props.posterImage?.url || data?.posterImage?.url || "/assets/photos/DJI_0048.JPG";
|
const posterSrc = props.backgroundImage?.url || props.posterImage?.url || data?.posterImage?.url || "/assets/photos/DJI_0048.JPG";
|
||||||
const posterAlt = props.backgroundImage?.alt || data?.posterImage?.alt || "E-TIB Gruppe Baustelle";
|
const posterAlt = props.backgroundImage?.alt || data?.posterImage?.alt || "E-TIB Gruppe Baustelle";
|
||||||
@@ -56,7 +58,7 @@ export function HeroVideo(props: HeroVideoProps) {
|
|||||||
|
|
||||||
{videoUrl ? (
|
{videoUrl ? (
|
||||||
<video
|
<video
|
||||||
key={videoUrl}
|
key={`${pathname}-${videoUrl}`}
|
||||||
className="absolute inset-0 w-full h-full object-cover z-1 pointer-events-none filter contrast-125 saturate-110 brightness-90"
|
className="absolute inset-0 w-full h-full object-cover z-1 pointer-events-none filter contrast-125 saturate-110 brightness-90"
|
||||||
src={videoUrl}
|
src={videoUrl}
|
||||||
autoPlay
|
autoPlay
|
||||||
@@ -67,6 +69,7 @@ export function HeroVideo(props: HeroVideoProps) {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Image
|
<Image
|
||||||
|
key={`img-${pathname}-${posterSrc}`}
|
||||||
src={posterSrc}
|
src={posterSrc}
|
||||||
alt={posterAlt}
|
alt={posterAlt}
|
||||||
fill
|
fill
|
||||||
@@ -88,6 +91,7 @@ export function HeroVideo(props: HeroVideoProps) {
|
|||||||
|
|
||||||
<div className="container relative z-[4] text-center px-4">
|
<div className="container relative z-[4] text-center px-4">
|
||||||
<motion.div
|
<motion.div
|
||||||
|
key={`hero-content-${pathname}`}
|
||||||
initial={{ opacity: 0, y: 40 }}
|
initial={{ opacity: 0, y: 40 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ duration: 1.2, ease: [0.16, 1, 0.3, 1] }}
|
transition={{ duration: 1.2, ease: [0.16, 1, 0.3, 1] }}
|
||||||
|
|||||||
@@ -4,15 +4,13 @@ import React, { useState } from 'react';
|
|||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { MapPin, Factory, Zap, CheckCircle2, ArrowUpRight } from 'lucide-react';
|
import { MapPin, Factory, Zap, CheckCircle2, ArrowUpRight } from 'lucide-react';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter, usePathname } from 'next/navigation';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { AnimatedGlossyBorder } from '@/components/ui/AnimatedGlossyBorder';
|
import { AnimatedGlossyBorder } from '@/components/ui/AnimatedGlossyBorder';
|
||||||
import { Location, defaultLocations, minorLocations } from '@/lib/map-data';
|
import { Location, defaultLocations, minorLocations, projectLocations } from '@/lib/map-data';
|
||||||
import { standorteLocations } from '@/lib/standorte-data';
|
import { standorteLocations } from '@/lib/standorte-data';
|
||||||
import { useLocale, useTranslations } from 'next-intl';
|
import { useLocale, useTranslations } from 'next-intl';
|
||||||
|
|
||||||
const allLocations = [...standorteLocations, ...defaultLocations, ...minorLocations];
|
|
||||||
|
|
||||||
interface Stat {
|
interface Stat {
|
||||||
value: string;
|
value: string;
|
||||||
suffix?: string;
|
suffix?: string;
|
||||||
@@ -34,11 +32,12 @@ export function InteractiveGermanyMap({
|
|||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
stats,
|
stats,
|
||||||
locations = allLocations,
|
locations = projectLocations,
|
||||||
isHero = false
|
isHero = false
|
||||||
}: InteractiveGermanyMapProps) {
|
}: InteractiveGermanyMapProps) {
|
||||||
const [activeLocation, setActiveLocation] = useState<Location | null>(null);
|
const [activeLocation, setActiveLocation] = useState<Location | null>(null);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const pathname = usePathname();
|
||||||
const locale = useLocale();
|
const locale = useLocale();
|
||||||
const t = useTranslations('InteractiveGermanyMap');
|
const t = useTranslations('InteractiveGermanyMap');
|
||||||
const tStandard = useTranslations('StandardPage');
|
const tStandard = useTranslations('StandardPage');
|
||||||
@@ -51,7 +50,7 @@ export function InteractiveGermanyMap({
|
|||||||
const finalBadge = badge || tStandard('badge');
|
const finalBadge = badge || tStandard('badge');
|
||||||
// the map is mostly used with specific props, so we leave title & description to fallbacks if not provided
|
// 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.</>);
|
const finalTitle = title || (locale === 'en' ? <>Nationwide<br/>in operation for you.</> : <>Deutschlandweit<br/>für Sie im Einsatz.</>);
|
||||||
const finalDescription = description || (locale === 'en' ? 'From our strategic locations in Guben and Bülstedt, we control and implement complex infrastructure projects nationwide.' : 'Von unseren strategischen Standorten in Guben und Bülstedt steuern und realisieren wir komplexe Infrastrukturprojekte im gesamten Bundesgebiet.');
|
const finalDescription = description || (locale === 'en' ? 'From our strategic locations in Guben, Kirchheilingen and Bülstedt, we control and implement complex infrastructure projects nationwide.' : 'Von unseren strategischen Standorten in Guben, Kirchheilingen und Bülstedt steuern und realisieren wir komplexe Infrastrukturprojekte im gesamten Bundesgebiet.');
|
||||||
|
|
||||||
const hq = locations.find((l) => l.type === 'hq');
|
const hq = locations.find((l) => l.type === 'hq');
|
||||||
const branch = locations.find((l) => l.type === 'branch');
|
const branch = locations.find((l) => l.type === 'branch');
|
||||||
@@ -59,7 +58,7 @@ export function InteractiveGermanyMap({
|
|||||||
const minorNodes = locations.filter((l) => l.type === 'minor_node');
|
const minorNodes = locations.filter((l) => l.type === 'minor_node');
|
||||||
|
|
||||||
return (
|
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"}>
|
<div key={isHero ? `hero-map-${pathname}` : undefined} 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"}>
|
||||||
<div className={`bg-[#050B14] ${isHero ? 'pt-32 pb-16 md:pt-40 md:pb-24' : 'rounded-[2.5rem] md:rounded-[3.5rem] shadow-2xl border border-white/5'} overflow-visible relative group/map`}>
|
<div className={`bg-[#050B14] ${isHero ? 'pt-32 pb-16 md:pt-40 md:pb-24' : 'rounded-[2.5rem] md:rounded-[3.5rem] shadow-2xl border border-white/5'} overflow-visible relative group/map`}>
|
||||||
{/* Animated Border Glow */}
|
{/* Animated Border Glow */}
|
||||||
{!isHero && <AnimatedGlossyBorder opacity={0.7} className="z-30" />}
|
{!isHero && <AnimatedGlossyBorder opacity={0.7} className="z-30" />}
|
||||||
|
|||||||
@@ -133,14 +133,16 @@ export function ReferencesSlider(props: ReferencesSliderProps) {
|
|||||||
<Link
|
<Link
|
||||||
href={`/${locale}/referenzen/${ref.slug}`}
|
href={`/${locale}/referenzen/${ref.slug}`}
|
||||||
data-testid="reference-tile"
|
data-testid="reference-tile"
|
||||||
|
draggable={false}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
if (dragDistanceRef.current > 5) {
|
if (dragDistanceRef.current > 15) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="block relative aspect-[16/10] bg-neutral-800 rounded-2xl overflow-hidden mb-5 border border-white/5 shadow-2xl"
|
className="block relative select-none aspect-[16/10] bg-neutral-800 rounded-2xl overflow-hidden mb-5 border border-white/5 shadow-2xl"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
|
draggable={false}
|
||||||
src={imgSrc}
|
src={imgSrc}
|
||||||
alt={ref.title}
|
alt={ref.title}
|
||||||
fill
|
fill
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export function SubCompanyTiles(props: SubCompanyTilesProps) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"
|
className="flex overflow-x-auto md:overflow-visible snap-x snap-mandatory pb-6 md:pb-0 md:grid md:grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6 hide-scrollbar -mx-4 px-4 md:mx-0 md:px-0"
|
||||||
variants={containerVariants}
|
variants={containerVariants}
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
whileInView="visible"
|
whileInView="visible"
|
||||||
@@ -236,7 +236,7 @@ export function SubCompanyTiles(props: SubCompanyTilesProps) {
|
|||||||
<motion.div
|
<motion.div
|
||||||
key={index}
|
key={index}
|
||||||
variants={itemVariants}
|
variants={itemVariants}
|
||||||
className="h-full"
|
className="h-full flex-shrink-0 snap-center w-[85%] sm:w-[70%] md:w-full"
|
||||||
>
|
>
|
||||||
{hasUrl ? (
|
{hasUrl ? (
|
||||||
isExternal ? (
|
isExternal ? (
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export function CorporateBackground() {
|
|||||||
const y5 = useTransform(scrollYProgress, [0, 1], [0, -500]);
|
const y5 = useTransform(scrollYProgress, [0, 1], [0, -500]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="absolute inset-0 z-[50] overflow-hidden pointer-events-none" aria-hidden="true">
|
<div className="absolute inset-0 z-[50] overflow-hidden pointer-events-none hidden md:block" aria-hidden="true">
|
||||||
{/* 1. Top Right - Medium, rotating moderately */}
|
{/* 1. Top Right - Medium, rotating moderately */}
|
||||||
<motion.div
|
<motion.div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
41
components/decorations/EUFundingBadge.tsx
Normal file
41
components/decorations/EUFundingBadge.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
|
export function EUFundingBadge() {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ duration: 1.5, ease: [0.16, 1, 0.3, 1] }}
|
||||||
|
className="absolute top-0 right-0 w-[260px] md:w-[380px] h-[260px] md:h-[380px] z-[40] pointer-events-none select-none overflow-hidden"
|
||||||
|
style={{
|
||||||
|
// Perfekter weicher Fade, der sicherstellt, dass harte Kanten komplett unsichtbar werden
|
||||||
|
maskImage: 'radial-gradient(100% 100% at top right, black 10%, rgba(0,0,0,0.8) 30%, transparent 70%)',
|
||||||
|
WebkitMaskImage: 'radial-gradient(100% 100% at top right, black 10%, rgba(0,0,0,0.8) 30%, transparent 70%)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Background Flag */}
|
||||||
|
<div className="absolute inset-0 w-full h-full opacity-95">
|
||||||
|
<Image
|
||||||
|
src="/assets/eu-flag.webp"
|
||||||
|
alt="European Union Background"
|
||||||
|
fill
|
||||||
|
className="object-cover object-top"
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Text overlaid on the flag */}
|
||||||
|
<div className="absolute top-0 right-0 p-5 md:p-8 flex flex-col items-end text-right z-10 drop-shadow-[0_2px_8px_rgba(0,0,0,0.9)]">
|
||||||
|
<span className="block text-[9px] md:text-[11px] font-bold text-white/90 tracking-wide leading-snug drop-shadow-md">
|
||||||
|
Kofinanziert von der
|
||||||
|
</span>
|
||||||
|
<span className="block text-[11px] md:text-[13px] font-extrabold text-white tracking-wider leading-snug uppercase drop-shadow-md">
|
||||||
|
Europäischen Union
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import { useAnalytics } from '../analytics/useAnalytics';
|
|||||||
import { AnalyticsEvents } from '../analytics/analytics-events';
|
import { AnalyticsEvents } from '../analytics/analytics-events';
|
||||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import { ChevronRight } from 'lucide-react';
|
import { ChevronRight } from 'lucide-react';
|
||||||
|
import { usePathname } from 'next/navigation';
|
||||||
import { AISearchResults } from '../search/AISearchResults';
|
import { AISearchResults } from '../search/AISearchResults';
|
||||||
const HeroIllustration = dynamic(() => import('./HeroIllustration'), { ssr: false });
|
const HeroIllustration = dynamic(() => import('./HeroIllustration'), { ssr: false });
|
||||||
const AIOrb = dynamic(() => import('../search/AIOrb'), { ssr: false });
|
const AIOrb = dynamic(() => import('../search/AIOrb'), { ssr: false });
|
||||||
@@ -15,6 +16,7 @@ const AIOrb = dynamic(() => import('../search/AIOrb'), { ssr: false });
|
|||||||
export default function Hero({ data }: { data?: any }) {
|
export default function Hero({ data }: { data?: any }) {
|
||||||
const t = useTranslations('Home.hero');
|
const t = useTranslations('Home.hero');
|
||||||
const locale = useLocale();
|
const locale = useLocale();
|
||||||
|
const pathname = usePathname();
|
||||||
const { trackEvent } = useAnalytics();
|
const { trackEvent } = useAnalytics();
|
||||||
|
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
@@ -85,7 +87,7 @@ export default function Hero({ data }: { data?: any }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Section className="relative min-h-[85vh] md:h-[90vh] flex flex-col items-center justify-center overflow-hidden bg-primary py-12 md:py-0 lg:py-0">
|
<Section key={`home-hero-${pathname}`} className="relative min-h-[85vh] md:h-[90vh] flex flex-col items-center justify-center overflow-hidden bg-primary py-12 md:py-0 lg:py-0">
|
||||||
<Container className="relative z-10 text-center md:text-left text-white w-full order-2 md:order-none">
|
<Container className="relative z-10 text-center md:text-left text-white w-full order-2 md:order-none">
|
||||||
<div className="max-w-5xl mx-auto md:mx-0">
|
<div className="max-w-5xl mx-auto md:mx-0">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export function Footer({ companyInfo }: FooterProps) {
|
|||||||
const locale = useLocale();
|
const locale = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className="bg-[#050B14] text-neutral-light py-10 md:py-16 relative overflow-hidden border-t border-white/5">
|
<footer className="bg-[#050B14] text-neutral-light pt-10 pb-28 md:py-16 relative overflow-hidden border-t border-white/5">
|
||||||
{/* Subtle background tech grid */}
|
{/* Subtle background tech grid */}
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(to_right,#8080800a_1px,transparent_1px),linear-gradient(to_bottom,#8080800a_1px,transparent_1px)] bg-[size:32px_32px] [mask-image:radial-gradient(ellipse_80%_50%_at_50%_0%,#000_70%,transparent_100%)] pointer-events-none" />
|
<div className="absolute inset-0 bg-[linear-gradient(to_right,#8080800a_1px,transparent_1px),linear-gradient(to_bottom,#8080800a_1px,transparent_1px)] bg-[size:32px_32px] [mask-image:radial-gradient(ellipse_80%_50%_at_50%_0%,#000_70%,transparent_100%)] pointer-events-none" />
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ export function Footer({ companyInfo }: FooterProps) {
|
|||||||
|
|
||||||
<div className="container relative z-10 grid grid-cols-1 md:grid-cols-4 lg:grid-cols-12 gap-4 md:gap-6 lg:gap-8 mb-8 md:mb-12">
|
<div className="container relative z-10 grid grid-cols-1 md:grid-cols-4 lg:grid-cols-12 gap-4 md:gap-6 lg:gap-8 mb-8 md:mb-12">
|
||||||
{/* Brand Column Bento */}
|
{/* Brand Column Bento */}
|
||||||
<div className="col-span-1 md:col-span-4 lg:col-span-5 flex flex-col justify-between bg-white/[0.02] border border-white/5 rounded-3xl p-6 md:p-8 lg:p-10 backdrop-blur-md relative group overflow-hidden">
|
<div className="col-span-1 md:col-span-4 lg:col-span-5 flex flex-col justify-between bg-white/[0.02] border border-white/5 rounded-3xl p-5 md:p-8 lg:p-10 backdrop-blur-md relative group overflow-hidden">
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-white/[0.04] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700" />
|
<div className="absolute inset-0 bg-gradient-to-br from-white/[0.04] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700" />
|
||||||
|
|
||||||
<div className="space-y-4 md:space-y-6 relative z-10">
|
<div className="space-y-4 md:space-y-6 relative z-10">
|
||||||
@@ -96,7 +96,7 @@ export function Footer({ companyInfo }: FooterProps) {
|
|||||||
<div className="col-span-1 md:col-span-4 lg:col-span-7 grid grid-cols-1 sm:grid-cols-2 gap-4 md:gap-6 lg:gap-8">
|
<div className="col-span-1 md:col-span-4 lg:col-span-7 grid grid-cols-1 sm:grid-cols-2 gap-4 md:gap-6 lg:gap-8">
|
||||||
|
|
||||||
{/* Company Column */}
|
{/* Company Column */}
|
||||||
<div className="bg-white/[0.02] border border-white/5 rounded-3xl p-6 md:p-8 lg:p-10 backdrop-blur-md relative group overflow-hidden flex flex-col">
|
<div className="bg-white/[0.02] border border-white/5 rounded-3xl p-5 md:p-8 lg:p-10 backdrop-blur-md relative group overflow-hidden flex flex-col">
|
||||||
<div className="absolute inset-0 bg-gradient-to-bl from-white/[0.04] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700 pointer-events-none" />
|
<div className="absolute inset-0 bg-gradient-to-bl from-white/[0.04] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700 pointer-events-none" />
|
||||||
<h4 className="font-heading font-bold uppercase tracking-widest text-sm mb-6 md:mb-8 text-white">
|
<h4 className="font-heading font-bold uppercase tracking-widest text-sm mb-6 md:mb-8 text-white">
|
||||||
{locale === 'de' ? 'Unternehmen' : 'Company'}
|
{locale === 'de' ? 'Unternehmen' : 'Company'}
|
||||||
@@ -108,6 +108,7 @@ export function Footer({ companyInfo }: FooterProps) {
|
|||||||
{ label: locale === 'de' ? 'Über uns' : 'About us', href: `/${locale}/${locale === 'de' ? 'ueber-uns' : 'about-us'}` },
|
{ label: locale === 'de' ? 'Über uns' : 'About us', href: `/${locale}/${locale === 'de' ? 'ueber-uns' : 'about-us'}` },
|
||||||
{ label: locale === 'de' ? 'Unser Team' : 'Our Team', href: `/${locale}/team` },
|
{ label: locale === 'de' ? 'Unser Team' : 'Our Team', href: `/${locale}/team` },
|
||||||
{ label: locale === 'de' ? 'Zertifikate' : 'Certificates', href: `/${locale}/zertifikate` },
|
{ label: locale === 'de' ? 'Zertifikate' : 'Certificates', href: `/${locale}/zertifikate` },
|
||||||
|
{ label: locale === 'de' ? 'Referenzen' : 'References', href: `/${locale}/referenzen` },
|
||||||
{ label: locale === 'de' ? 'Karriere' : 'Career', href: `/${locale}/${locale === 'de' ? 'karriere' : 'career'}` },
|
{ label: locale === 'de' ? 'Karriere' : 'Career', href: `/${locale}/${locale === 'de' ? 'karriere' : 'career'}` },
|
||||||
{ label: locale === 'de' ? 'Messen & Events' : 'Fairs & Events', href: `/${locale}/${locale === 'de' ? 'messen' : 'trade-fairs'}` },
|
{ label: locale === 'de' ? 'Messen & Events' : 'Fairs & Events', href: `/${locale}/${locale === 'de' ? 'messen' : 'trade-fairs'}` },
|
||||||
{ label: locale === 'de' ? 'Kontakt' : 'Contact', href: `/${locale}/${locale === 'de' ? 'kontakt' : 'contact'}` },
|
{ label: locale === 'de' ? 'Kontakt' : 'Contact', href: `/${locale}/${locale === 'de' ? 'kontakt' : 'contact'}` },
|
||||||
@@ -123,7 +124,7 @@ export function Footer({ companyInfo }: FooterProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Legal Column */}
|
{/* Legal Column */}
|
||||||
<div className="bg-white/[0.02] border border-white/5 rounded-3xl p-6 md:p-8 lg:p-10 backdrop-blur-md relative group overflow-hidden flex flex-col">
|
<div className="bg-white/[0.02] border border-white/5 rounded-3xl p-5 md:p-8 lg:p-10 backdrop-blur-md relative group overflow-hidden flex flex-col">
|
||||||
<div className="absolute inset-0 bg-gradient-to-tr from-white/[0.04] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700 pointer-events-none" />
|
<div className="absolute inset-0 bg-gradient-to-tr from-white/[0.04] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700 pointer-events-none" />
|
||||||
<h4 className="font-heading font-bold uppercase tracking-widest text-sm mb-6 md:mb-8 text-white">
|
<h4 className="font-heading font-bold uppercase tracking-widest text-sm mb-6 md:mb-8 text-white">
|
||||||
{locale === 'de' ? 'Rechtliches' : 'Legal'}
|
{locale === 'de' ? 'Rechtliches' : 'Legal'}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export interface NavLink {
|
|||||||
label: string;
|
label: string;
|
||||||
url: string;
|
url: string;
|
||||||
children?: NavLink[];
|
children?: NavLink[];
|
||||||
|
isGroupLabel?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
@@ -71,7 +72,7 @@ export function Header({ navLinks }: HeaderProps) {
|
|||||||
className={`fixed top-0 left-0 right-0 z-50 flex justify-center transition-all duration-700 ease-[cubic-bezier(0.16,1,0.3,1)] ${
|
className={`fixed top-0 left-0 right-0 z-50 flex justify-center transition-all duration-700 ease-[cubic-bezier(0.16,1,0.3,1)] ${
|
||||||
isStandard
|
isStandard
|
||||||
? 'bg-[#050B14]/95 backdrop-blur-xl border-b border-white/10'
|
? 'bg-[#050B14]/95 backdrop-blur-xl border-b border-white/10'
|
||||||
: (isSolidMode ? 'pt-4 px-4' : 'pt-0 px-0')
|
: (isSolidMode ? 'pt-4 px-4 sm:px-6' : 'pt-0 px-0')
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -79,7 +80,7 @@ export function Header({ navLinks }: HeaderProps) {
|
|||||||
isStandard
|
isStandard
|
||||||
? 'py-4 px-6 md:px-8'
|
? 'py-4 px-6 md:px-8'
|
||||||
: isSolidMode
|
: isSolidMode
|
||||||
? 'rounded-full bg-white/50 backdrop-blur-xl shadow-[0_8px_32px_rgba(0,0,0,0.08)] border border-white/40 py-2.5 px-6 md:px-8'
|
? 'rounded-full bg-white/50 backdrop-blur-xl shadow-[0_8px_32px_rgba(0,0,0,0.08)] border border-white/40 py-2.5 px-8 md:px-10'
|
||||||
: 'rounded-none bg-transparent py-6 px-4 md:px-8 border border-transparent'
|
: 'rounded-none bg-transparent py-6 px-4 md:px-8 border border-transparent'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -164,6 +165,20 @@ export function Header({ navLinks }: HeaderProps) {
|
|||||||
function NavItem({ link, currentLocale, pathname, isSolidMode }: { link: NavLink, currentLocale: string, pathname: string, isSolidMode: boolean }) {
|
function NavItem({ link, currentLocale, pathname, isSolidMode }: { link: NavLink, currentLocale: string, pathname: string, isSolidMode: boolean }) {
|
||||||
const [isHovered, setIsHovered] = React.useState(false);
|
const [isHovered, setIsHovered] = React.useState(false);
|
||||||
|
|
||||||
|
// Close dropdown on navigation
|
||||||
|
React.useEffect(() => {
|
||||||
|
setIsHovered(false);
|
||||||
|
}, [pathname]);
|
||||||
|
|
||||||
|
// Close dropdown on scroll
|
||||||
|
React.useEffect(() => {
|
||||||
|
const handleScroll = () => {
|
||||||
|
if (isHovered) setIsHovered(false);
|
||||||
|
};
|
||||||
|
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||||
|
return () => window.removeEventListener('scroll', handleScroll);
|
||||||
|
}, [isHovered]);
|
||||||
|
|
||||||
const mappedUrl = link.url.startsWith('/') && !link.url.match(/^\/(en|de)/)
|
const mappedUrl = link.url.startsWith('/') && !link.url.match(/^\/(en|de)/)
|
||||||
? `/${currentLocale}${link.url}`
|
? `/${currentLocale}${link.url}`
|
||||||
: link.url;
|
: link.url;
|
||||||
@@ -218,15 +233,25 @@ function NavItem({ link, currentLocale, pathname, isSolidMode }: { link: NavLink
|
|||||||
{/* Top glowing accent line */}
|
{/* Top glowing accent line */}
|
||||||
<div className="absolute top-0 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-primary to-transparent opacity-70" />
|
<div className="absolute top-0 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-primary to-transparent opacity-70" />
|
||||||
|
|
||||||
{link.children.map((child) => {
|
{link.children.map((child, idx) => {
|
||||||
const childUrl = child.url.startsWith('/') && !child.url.match(/^\/(en|de)/)
|
const childUrl = child.url.startsWith('/') && !child.url.match(/^\/(en|de)/)
|
||||||
? `/${currentLocale}${child.url}`
|
? `/${currentLocale}${child.url}`
|
||||||
: child.url;
|
: child.url;
|
||||||
const isChildActive = pathname === childUrl;
|
const isChildActive = pathname === childUrl;
|
||||||
|
|
||||||
|
if (child.isGroupLabel) {
|
||||||
|
return (
|
||||||
|
<div key={`group-${idx}`} className={`mt-2 mb-1 px-4 text-[11px] font-bold uppercase tracking-wider ${isSolidMode ? 'text-neutral-400' : 'text-neutral-500'}`}>
|
||||||
|
{child.label}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TransitionLink
|
<TransitionLink
|
||||||
key={child.url}
|
key={child.url}
|
||||||
href={childUrl}
|
href={childUrl}
|
||||||
|
onClick={() => setIsHovered(false)}
|
||||||
className={`group/dropitem relative block px-4 py-3.5 rounded-2xl text-sm font-medium transition-all duration-300 overflow-hidden ${
|
className={`group/dropitem relative block px-4 py-3.5 rounded-2xl text-sm font-medium transition-all duration-300 overflow-hidden ${
|
||||||
isSolidMode
|
isSolidMode
|
||||||
? isChildActive ? 'bg-primary/10 text-primary' : 'text-neutral-600 hover:text-primary hover:bg-neutral-50/80'
|
? isChildActive ? 'bg-primary/10 text-primary' : 'text-neutral-600 hover:text-primary hover:bg-neutral-50/80'
|
||||||
|
|||||||
1
contact-page-auth-debug.html
Normal file
1
contact-page-auth-debug.html
Normal file
File diff suppressed because one or more lines are too long
1
contact-page-debug.html
Normal file
1
contact-page-debug.html
Normal file
File diff suppressed because one or more lines are too long
BIN
contact.png
Normal file
BIN
contact.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 288 KiB |
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: "Grabenlose Leitungsverlegung"
|
title: "Bohrtechnik"
|
||||||
date: "2024-03-20"
|
date: "2024-03-20"
|
||||||
excerpt: "Präzise Horizontalspülbohrungen und Erdraketen in allen Bodenklassen für eine absolut oberflächenschonende und effiziente Leitungsverlegung."
|
excerpt: "Präzise Horizontalspülbohrungen und Erdraketen in allen Bodenklassen für eine absolut oberflächenschonende und effiziente Leitungsverlegung."
|
||||||
layout: "fullBleed"
|
layout: "fullBleed"
|
||||||
@@ -7,9 +7,9 @@ layout: "fullBleed"
|
|||||||
|
|
||||||
<HeroSection
|
<HeroSection
|
||||||
badge="Kernkompetenz"
|
badge="Kernkompetenz"
|
||||||
title="Grabenlose Verlegung"
|
title="Bohrtechnik"
|
||||||
subtitle="Oberflächenschonende Horizontalspülbohrungen und Erdraketen"
|
subtitle="Grabenlose Leitungsverlegung, Horizontalspülbohrungen und Erdraketen"
|
||||||
backgroundImage={{ url: '/assets/photos/DSC08653.JPG' }}
|
backgroundImage={{ url: '/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-47.jpg' }}
|
||||||
alignment="center"
|
alignment="center"
|
||||||
ctaLabel="Projekt anfragen"
|
ctaLabel="Projekt anfragen"
|
||||||
ctaHref="/de/kontakt"
|
ctaHref="/de/kontakt"
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ description: "Die E-TIB GmbH ist Ihr zuverlässiger Partner für komplexe Kabelt
|
|||||||
description: "Das komplette Leistungsspektrum zur Errichtung von Kabeltrassen aus einer Hand.",
|
description: "Das komplette Leistungsspektrum zur Errichtung von Kabeltrassen aus einer Hand.",
|
||||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",
|
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",
|
||||||
url: "/de/standorte/guben",
|
url: "/de/standorte/guben",
|
||||||
backgroundImage: "/assets/photos/DJI_0243.JPG"
|
backgroundImage: "/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-54.jpg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "E-TIB Ingenieurgesellschaft mbH",
|
title: "E-TIB Ingenieurgesellschaft mbH",
|
||||||
description: "Unsere Ingenieure planen für Sie die Energie- und Telekommunikationsnetze der Zukunft - Vermessung und Dokumentation inklusive.",
|
description: "Unsere Ingenieure planen für Sie die Energie- und Telekommunikationsnetze der Zukunft - Vermessung und Dokumentation inklusive.",
|
||||||
icon: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4",
|
icon: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4",
|
||||||
url: "https://www.etib-ing.com/",
|
url: "https://www.etib-ing.com/",
|
||||||
backgroundImage: "/assets/photos/DSC01137.JPG"
|
backgroundImage: "/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-298.jpg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "E-TIB Verwaltung GmbH",
|
title: "E-TIB Verwaltung GmbH",
|
||||||
@@ -41,7 +41,7 @@ description: "Die E-TIB GmbH ist Ihr zuverlässiger Partner für komplexe Kabelt
|
|||||||
title: "E-TIB Bohrtechnik GmbH",
|
title: "E-TIB Bohrtechnik GmbH",
|
||||||
description: "Präzise Horizontalspülbohrungen in allen Bodenklassen für eine oberflächenschonende und grabenlose Leitungsverlegung.",
|
description: "Präzise Horizontalspülbohrungen in allen Bodenklassen für eine oberflächenschonende und grabenlose Leitungsverlegung.",
|
||||||
icon: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z",
|
icon: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z",
|
||||||
url: "/de/standorte/kirchheilingen",
|
url: "/de/bohrtechnik",
|
||||||
backgroundImage: "/assets/photos/DSC08653.JPG"
|
backgroundImage: "/assets/photos/DSC08653.JPG"
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
@@ -122,7 +122,7 @@ description: "Die E-TIB GmbH ist Ihr zuverlässiger Partner für komplexe Kabelt
|
|||||||
tag: "Energie",
|
tag: "Energie",
|
||||||
size: "large",
|
size: "large",
|
||||||
href: "/de/kabeltiefbau",
|
href: "/de/kabeltiefbau",
|
||||||
image: { url: "/assets/photos/DSC01123.JPG", alt: "Kabelleitungstiefbau" }
|
image: { url: "/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-54.jpg", alt: "Kabelleitungstiefbau" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Bohrtechnik",
|
title: "Bohrtechnik",
|
||||||
@@ -130,7 +130,7 @@ description: "Die E-TIB GmbH ist Ihr zuverlässiger Partner für komplexe Kabelt
|
|||||||
tag: "Innovation",
|
tag: "Innovation",
|
||||||
size: "medium",
|
size: "medium",
|
||||||
href: "/de/bohrtechnik",
|
href: "/de/bohrtechnik",
|
||||||
image: { url: "/assets/photos/DSC08653.JPG", alt: "Bohrtechnik" }
|
image: { url: "/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-47.jpg", alt: "Bohrtechnik" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Planung",
|
title: "Planung",
|
||||||
@@ -138,14 +138,15 @@ description: "Die E-TIB GmbH ist Ihr zuverlässiger Partner für komplexe Kabelt
|
|||||||
tag: "Präzision",
|
tag: "Präzision",
|
||||||
size: "small",
|
size: "small",
|
||||||
href: "/de/planung",
|
href: "/de/planung",
|
||||||
image: { url: "/assets/photos/DJI_0048.JPG", alt: "Planung" }
|
image: { url: "/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-298.jpg", alt: "Planung" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Vermessung",
|
title: "Vermessung",
|
||||||
description: "Digitale Bestandsdokumentation",
|
description: "Digitale Bestandsdokumentation",
|
||||||
tag: "Digital",
|
tag: "Digital",
|
||||||
size: "accent",
|
size: "accent",
|
||||||
href: "/de/vermessung"
|
href: "/de/vermessung",
|
||||||
|
image: { url: "/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(26_von_99).jpg", alt: "Vermessung" }
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ layout: "fullBleed"
|
|||||||
|
|
||||||
## Alles aus einer Hand
|
## Alles aus einer Hand
|
||||||
|
|
||||||
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 Kabelnetzbau spezialisiert. Durch unsere langjährige Erfahrung erkennen wir Problemstellungen frühzeitig und erarbeiten für unsere Auftraggeber im Vorfeld sichere und zufriedenstellende Lösungen.
|
||||||
|
|
||||||
Wir bieten Ihnen das komplette Leistungsspektrum zur Errichtung von Kabeltrassen aus einer Hand – mit eigenem, hochmodernem Maschinenpark und festangestelltem Fachpersonal. 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.
|
Wir bieten Ihnen das komplette Leistungsspektrum zur Errichtung von Kabeltrassen aus einer Hand – mit eigenem, hochmodernem Maschinenpark und festangestelltem Fachpersonal. 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.
|
||||||
|
|
||||||
@@ -36,7 +36,9 @@ layout: "fullBleed"
|
|||||||
<ServiceDetailGrid
|
<ServiceDetailGrid
|
||||||
badge="Leistungsspektrum"
|
badge="Leistungsspektrum"
|
||||||
title="Alles für Ihr Netz"
|
title="Alles für Ihr Netz"
|
||||||
descriptionParagraphs={[]}
|
descriptionParagraphs={[
|
||||||
|
"Wir bieten höchste Präzision, modernste Maschinen und zertifizierte Experten für Ihr Projekt."
|
||||||
|
]}
|
||||||
panels={[
|
panels={[
|
||||||
{
|
{
|
||||||
id: "grabenbau",
|
id: "grabenbau",
|
||||||
|
|||||||
@@ -5,21 +5,22 @@ excerpt: "Werden Sie Teil eines dynamischen Teams und bauen Sie mit uns die Infr
|
|||||||
layout: "fullBleed"
|
layout: "fullBleed"
|
||||||
---
|
---
|
||||||
|
|
||||||
<HomeHero
|
<HeroSection
|
||||||
badge="Karriere"
|
badge="Karriere"
|
||||||
title="Zukunft bauen"
|
title="Zukunft bauen"
|
||||||
description="Suchen Sie eine neue Herausforderung in einem zukunftssicheren Markt? Bei E-TIB arbeiten wir täglich daran, Deutschland zu vernetzen und die Energiewende voranzutreiben."
|
subtitle="Suchen Sie eine neue Herausforderung in einem zukunftssicheren Markt? Bei E-TIB arbeiten wir täglich daran, Deutschland zu vernetzen und die Energiewende voranzutreiben."
|
||||||
videoUrl="/assets/videos/web/hero-kabelpflug.mp4"
|
backgroundImage={{ url: '/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-28.jpg' }}
|
||||||
linkText="Offene Stellen"
|
alignment="center"
|
||||||
linkHref="#stellen"
|
ctaLabel="Offene Stellen"
|
||||||
|
ctaHref="#stellen"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<JobListingBlock showFairs={false} showJobs={true} />
|
<JobListingBlock showFairs={false} showJobs={true} />
|
||||||
|
|
||||||
<BenefitGrid
|
<BenefitGrid
|
||||||
badge="E-TIB Standard"
|
badge="E-TIB Standard"
|
||||||
title="Wir bieten unseren Mitarbeitern"
|
title="Wir bieten unseren Mitarbeitenden"
|
||||||
description="Die E-TIB GmbH wurde aus der Motivation gegründet, hochwertige Leistungen sowie faire Entlohnung zu ermöglichen. Neben der Entlohnung steht die Wertschätzung jedes einzelnen Mitarbeiters bei uns an oberster Stelle."
|
description="Die E-TIB GmbH wurde aus der Motivation gegründet, hochwertige Leistungen sowie faire Entlohnung zu ermöglichen. Neben der Entlohnung steht die Wertschätzung jedes einzelnen Mitarbeitenden bei uns an oberster Stelle."
|
||||||
benefits={[
|
benefits={[
|
||||||
{
|
{
|
||||||
id: "region",
|
id: "region",
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ excerpt: "Treffen Sie das Team der E-TIB Gruppe persönlich auf Fachmessen und B
|
|||||||
layout: "fullBleed"
|
layout: "fullBleed"
|
||||||
---
|
---
|
||||||
|
|
||||||
<HomeHero
|
<HeroSection
|
||||||
badge="Messen & Events"
|
badge="Messen & Events"
|
||||||
title="Treffen Sie uns persönlich"
|
title="Treffen Sie uns persönlich"
|
||||||
description="Lernen Sie die E-TIB Gruppe auf den führenden Branchenmessen kennen."
|
subtitle="Lernen Sie die E-TIB Gruppe auf den führenden Branchenmessen kennen."
|
||||||
videoUrl="/assets/videos/web/hero-bahnkreuzung.mp4"
|
backgroundImage={{ url: '/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-100.jpg' }}
|
||||||
linkText="Kontakt aufnehmen"
|
alignment="center"
|
||||||
linkHref="/de/kontakt"
|
ctaLabel="Kontakt aufnehmen"
|
||||||
|
ctaHref="/de/kontakt"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div id="termine">
|
<div id="termine">
|
||||||
@@ -21,7 +22,7 @@ layout: "fullBleed"
|
|||||||
fairs={[
|
fairs={[
|
||||||
{ name: 'Intersolar München', date: '23. - 25. Juni 2026', type: 'Messe', location: 'München', url: 'https://www.intersolar.de/' },
|
{ name: 'Intersolar München', date: '23. - 25. Juni 2026', type: 'Messe', location: 'München', url: 'https://www.intersolar.de/' },
|
||||||
{ name: 'Windenergietage Linstow', date: '10. - 12. November 2026', type: 'Fachkongress', location: 'Linstow', url: 'https://www.windenergietage.de/' },
|
{ name: 'Windenergietage Linstow', date: '10. - 12. November 2026', type: 'Fachkongress', location: 'Linstow', url: 'https://www.windenergietage.de/' },
|
||||||
{ name: 'Werkstatt Kabel 2026', date: '24. - 25. November 2026', type: 'Fachtagung', location: 'Wiesbaden' }
|
{ name: 'Werkstatt Kabel 2026', date: '24. - 25. November 2026', type: 'Fachtagung', location: 'Wiesbaden', url: 'https://werkstatt-kabel.essociation.de/' }
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ layout: "fullBleed"
|
|||||||
badge="Kernkompetenz"
|
badge="Kernkompetenz"
|
||||||
title="Planung & Beratung"
|
title="Planung & Beratung"
|
||||||
subtitle="Kompetente Begleitung für Energie- und Telekommunikationsnetze"
|
subtitle="Kompetente Begleitung für Energie- und Telekommunikationsnetze"
|
||||||
backgroundImage={{ url: '/assets/photos/DJI_0048.JPG' }}
|
backgroundImage={{ url: '/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-298.jpg' }}
|
||||||
alignment="center"
|
alignment="center"
|
||||||
ctaLabel="Projekt anfragen"
|
ctaLabel="Projekt anfragen"
|
||||||
ctaHref="/de/kontakt"
|
ctaHref="/de/kontakt"
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ layout: "fullBleed"
|
|||||||
<div className="grid grid-cols-2 gap-6">
|
<div className="grid grid-cols-2 gap-6">
|
||||||
<div className="bg-white p-8 rounded-2xl shadow-sm border border-neutral-100">
|
<div className="bg-white p-8 rounded-2xl shadow-sm border border-neutral-100">
|
||||||
<div className="text-4xl font-extrabold text-primary mb-2">100+</div>
|
<div className="text-4xl font-extrabold text-primary mb-2">100+</div>
|
||||||
<div className="text-neutral-dark font-semibold">Mitarbeiter</div>
|
<div className="text-neutral-dark font-semibold">Mitarbeitende</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white p-8 rounded-2xl shadow-sm border border-neutral-100">
|
<div className="bg-white p-8 rounded-2xl shadow-sm border border-neutral-100">
|
||||||
<div className="text-4xl font-extrabold text-primary mb-2">2015</div>
|
<div className="text-4xl font-extrabold text-primary mb-2">2015</div>
|
||||||
@@ -66,14 +66,14 @@ layout: "fullBleed"
|
|||||||
description: "Das komplette Leistungsspektrum zur Errichtung von Kabeltrassen aus einer Hand.",
|
description: "Das komplette Leistungsspektrum zur Errichtung von Kabeltrassen aus einer Hand.",
|
||||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",
|
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",
|
||||||
url: "/de/standorte/guben",
|
url: "/de/standorte/guben",
|
||||||
backgroundImage: "/assets/photos/DJI_0243.JPG"
|
backgroundImage: "/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-54.jpg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "E-TIB Ingenieurgesellschaft mbH",
|
title: "E-TIB Ingenieurgesellschaft mbH",
|
||||||
description: "Unsere Ingenieure planen für Sie die Energie- und Telekommunikationsnetze der Zukunft - Vermessung und Dokumentation inklusive.",
|
description: "Unsere Ingenieure planen für Sie die Energie- und Telekommunikationsnetze der Zukunft - Vermessung und Dokumentation inklusive.",
|
||||||
icon: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4",
|
icon: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4",
|
||||||
url: "https://www.etib-ing.com/",
|
url: "https://www.etib-ing.com/",
|
||||||
backgroundImage: "/assets/photos/DSC01137.JPG"
|
backgroundImage: "/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-298.jpg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "E-TIB Verwaltung GmbH",
|
title: "E-TIB Verwaltung GmbH",
|
||||||
@@ -87,38 +87,12 @@ layout: "fullBleed"
|
|||||||
description: "Präzise Horizontalspülbohrungen in allen Bodenklassen für eine oberflächenschonende und grabenlose Leitungsverlegung.",
|
description: "Präzise Horizontalspülbohrungen in allen Bodenklassen für eine oberflächenschonende und grabenlose Leitungsverlegung.",
|
||||||
icon: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z",
|
icon: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z",
|
||||||
url: "/de/standorte/kirchheilingen",
|
url: "/de/standorte/kirchheilingen",
|
||||||
backgroundImage: "/assets/photos/DSC08653.JPG"
|
backgroundImage: "/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-47.jpg"
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-white py-24 border-b border-neutral-100">
|
|
||||||
<div className="container max-w-6xl mx-auto px-4">
|
|
||||||
<div className="text-center mb-16">
|
|
||||||
<div className="text-primary font-bold tracking-wider uppercase text-sm mb-4">Unser Team</div>
|
|
||||||
<h2 className="font-heading text-4xl md:text-5xl font-extrabold text-neutral-dark mb-6">Über 100 Experten für Ihr Projekt</h2>
|
|
||||||
<div className="w-20 h-2 bg-primary mx-auto"></div>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-5xl font-extrabold text-primary mb-2">72</div>
|
|
||||||
<div className="text-neutral-dark font-bold text-lg">E-TIB GmbH</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-5xl font-extrabold text-primary mb-2">10</div>
|
|
||||||
<div className="text-neutral-dark font-bold text-lg">E-TIB Bohrtechnik</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-5xl font-extrabold text-primary mb-2">8</div>
|
|
||||||
<div className="text-neutral-dark font-bold text-lg">E-TIB ING</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-5xl font-extrabold text-primary mb-2">10</div>
|
|
||||||
<div className="text-neutral-dark font-bold text-lg">Niedersachsen</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<CallToAction />
|
<CallToAction />
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ layout: "fullBleed"
|
|||||||
<HeroSection
|
<HeroSection
|
||||||
badge="Kernkompetenz"
|
badge="Kernkompetenz"
|
||||||
title="Vermessung & Dokumentation"
|
title="Vermessung & Dokumentation"
|
||||||
subtitle="Lückenlose und rechtssichere Erfassung aller Bauphasen"
|
subtitle="Hochpräzise digitale Bestandsdokumentation, 3D-Scans und Drohnenvermessung für Ihre Leitungstrassen und Anlagen."
|
||||||
backgroundImage={{ url: '/assets/photos/DSC08653.JPG' }}
|
backgroundImage={{ url: '/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(24_von_99)_groß2.jpg' }}
|
||||||
alignment="center"
|
alignment="center"
|
||||||
ctaLabel="Projekt anfragen"
|
ctaLabel="Projekt anfragen"
|
||||||
ctaHref="/de/kontakt"
|
ctaHref="/de/kontakt"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: "Trenchless Line Laying"
|
title: "Drilling Technology"
|
||||||
date: "2024-03-20"
|
date: "2024-03-20"
|
||||||
excerpt: "Precise horizontal directional drilling and earth rockets in all soil classes for surface-friendly and efficient line laying."
|
excerpt: "Precise horizontal directional drilling and earth rockets in all soil classes for surface-friendly and efficient line laying."
|
||||||
layout: "fullBleed"
|
layout: "fullBleed"
|
||||||
@@ -7,9 +7,9 @@ layout: "fullBleed"
|
|||||||
|
|
||||||
<HeroSection
|
<HeroSection
|
||||||
badge="Core Competency"
|
badge="Core Competency"
|
||||||
title="Trenchless Laying"
|
title="Drilling Technology"
|
||||||
subtitle="Surface-friendly horizontal directional drilling and earth rockets"
|
subtitle="Trenchless Laying, horizontal directional drilling and earth rockets"
|
||||||
backgroundImage={{ url: '/assets/photos/DSC08653.JPG' }}
|
backgroundImage={{ url: '/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-47.jpg' }}
|
||||||
alignment="center"
|
alignment="center"
|
||||||
ctaLabel="Request a project"
|
ctaLabel="Request a project"
|
||||||
ctaHref="/en/contact"
|
ctaHref="/en/contact"
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ description: "E-TIB GmbH is your reliable partner for complex cable routes, hori
|
|||||||
description: "The complete range of services for the construction of cable routes from a single source.",
|
description: "The complete range of services for the construction of cable routes from a single source.",
|
||||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",
|
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",
|
||||||
url: "/en/standorte/guben",
|
url: "/en/standorte/guben",
|
||||||
backgroundImage: "/assets/photos/DJI_0243.JPG"
|
backgroundImage: "/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-54.jpg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "E-TIB Ingenieurgesellschaft mbH",
|
title: "E-TIB Ingenieurgesellschaft mbH",
|
||||||
description: "Our engineers plan the energy and telecommunications networks of the future for you - surveying and documentation included.",
|
description: "Our engineers plan the energy and telecommunications networks of the future for you - surveying and documentation included.",
|
||||||
icon: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4",
|
icon: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4",
|
||||||
url: "https://www.etib-ing.com/",
|
url: "https://www.etib-ing.com/",
|
||||||
backgroundImage: "/assets/photos/DSC01137.JPG"
|
backgroundImage: "/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-298.jpg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "E-TIB Verwaltung GmbH",
|
title: "E-TIB Verwaltung GmbH",
|
||||||
@@ -41,7 +41,7 @@ description: "E-TIB GmbH is your reliable partner for complex cable routes, hori
|
|||||||
title: "E-TIB Bohrtechnik GmbH",
|
title: "E-TIB Bohrtechnik GmbH",
|
||||||
description: "Precise horizontal directional drilling in all soil classes for surface-friendly and trenchless pipe laying.",
|
description: "Precise horizontal directional drilling in all soil classes for surface-friendly and trenchless pipe laying.",
|
||||||
icon: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z",
|
icon: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z",
|
||||||
url: "/en/standorte/kirchheilingen",
|
url: "/en/drilling-technology",
|
||||||
backgroundImage: "/assets/photos/DSC08653.JPG"
|
backgroundImage: "/assets/photos/DSC08653.JPG"
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
@@ -122,7 +122,7 @@ description: "E-TIB GmbH is your reliable partner for complex cable routes, hori
|
|||||||
tag: "Energy",
|
tag: "Energy",
|
||||||
size: "large",
|
size: "large",
|
||||||
href: "/en/cable-civil-engineering",
|
href: "/en/cable-civil-engineering",
|
||||||
image: { url: "/assets/photos/DSC01123.JPG", alt: "Cable Civil Engineering" }
|
image: { url: "/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-54.jpg", alt: "Cable Civil Engineering" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Drilling Technology",
|
title: "Drilling Technology",
|
||||||
@@ -130,7 +130,7 @@ description: "E-TIB GmbH is your reliable partner for complex cable routes, hori
|
|||||||
tag: "Innovation",
|
tag: "Innovation",
|
||||||
size: "medium",
|
size: "medium",
|
||||||
href: "/en/drilling-technology",
|
href: "/en/drilling-technology",
|
||||||
image: { url: "/assets/photos/DSC08653.JPG", alt: "Drilling Technology" }
|
image: { url: "/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-47.jpg", alt: "Drilling Technology" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Planning",
|
title: "Planning",
|
||||||
@@ -138,14 +138,15 @@ description: "E-TIB GmbH is your reliable partner for complex cable routes, hori
|
|||||||
tag: "Precision",
|
tag: "Precision",
|
||||||
size: "small",
|
size: "small",
|
||||||
href: "/en/planning",
|
href: "/en/planning",
|
||||||
image: { url: "/assets/photos/DJI_0048.JPG", alt: "Planning" }
|
image: { url: "/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-298.jpg", alt: "Planning" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Surveying",
|
title: "Surveying",
|
||||||
description: "Digital as-built documentation",
|
description: "Digital as-built documentation",
|
||||||
tag: "Digital",
|
tag: "Digital",
|
||||||
size: "accent",
|
size: "accent",
|
||||||
href: "/en/surveying"
|
href: "/en/surveying",
|
||||||
|
image: { url: "/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(26_von_99).jpg", alt: "Surveying" }
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -24,19 +24,21 @@ layout: "fullBleed"
|
|||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
## Integrated Excellence from a Single Source
|
## Everything from a single source
|
||||||
|
|
||||||
At E-TIB, we leverage decades of collective expertise to anticipate structural and geological bottlenecks long before the first shovel hits the ground.
|
E-TIB GmbH is fully specialized in cable network construction. Thanks to our many years of experience, we identify problems early on and develop safe and satisfactory solutions for our clients in advance.
|
||||||
|
|
||||||
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 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.
|
We offer you the complete range of services for the construction of cable routes from a single source - with our own ultra-modern machine park and permanently employed specialist staff. In direct cooperation with our partner office, E-TIB Ingenieurgesellschaft mbH, we can also rule out planning errors early on and master even the most complex documentation requirements confidently.
|
||||||
|
|
||||||
<DataGridPulse />
|
<DataGridPulse />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ServiceDetailGrid
|
<ServiceDetailGrid
|
||||||
badge="Service Portfolio"
|
badge="Service Spectrum"
|
||||||
title="Everything for Your Network"
|
title="Everything for your network"
|
||||||
descriptionParagraphs={[]}
|
descriptionParagraphs={[
|
||||||
|
"We offer the highest precision, state-of-the-art machinery, and certified experts for your project."
|
||||||
|
]}
|
||||||
panels={[
|
panels={[
|
||||||
{
|
{
|
||||||
id: "grabenbau",
|
id: "grabenbau",
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ excerpt: "Become part of a dynamic team and help us build the infrastructure of
|
|||||||
layout: "fullBleed"
|
layout: "fullBleed"
|
||||||
---
|
---
|
||||||
|
|
||||||
<HomeHero
|
<HeroSection
|
||||||
badge="Careers"
|
badge="Careers"
|
||||||
title="Build the Future"
|
title="Build the Future"
|
||||||
description="Are you looking for a new challenge in a future-proof market? At E-TIB, we work daily to network Germany and drive the energy transition forward."
|
subtitle="Are you looking for a new challenge in a future-proof market? At E-TIB, we work daily to network Germany and drive the energy transition forward."
|
||||||
videoUrl="/assets/videos/web/hero-kabelpflug.mp4"
|
backgroundImage={{ url: '/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-28.jpg' }}
|
||||||
linkText="Open Positions"
|
alignment="center"
|
||||||
linkHref="#stellen"
|
ctaLabel="Open Positions"
|
||||||
|
ctaHref="#stellen"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<JobListingBlock showFairs={false} showJobs={true} />
|
<JobListingBlock showFairs={false} showJobs={true} />
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ excerpt: "Meet the team of the E-TIB Group personally at trade fairs and industr
|
|||||||
layout: "fullBleed"
|
layout: "fullBleed"
|
||||||
---
|
---
|
||||||
|
|
||||||
<HomeHero
|
<HeroSection
|
||||||
badge="Fairs & Events"
|
badge="Fairs & Events"
|
||||||
title="Meet us personally"
|
title="Meet us personally"
|
||||||
description="Get to know the E-TIB Group at the leading industry trade fairs."
|
subtitle="Get to know the E-TIB Group at the leading industry trade fairs."
|
||||||
videoUrl="/assets/videos/web/hero-bahnkreuzung.mp4"
|
backgroundImage={{ url: '/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-100.jpg' }}
|
||||||
linkText="Get in Touch"
|
alignment="center"
|
||||||
linkHref="/en/contact"
|
ctaLabel="Get in Touch"
|
||||||
|
ctaHref="/en/contact"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div id="termine">
|
<div id="termine">
|
||||||
@@ -21,7 +22,7 @@ layout: "fullBleed"
|
|||||||
fairs={[
|
fairs={[
|
||||||
{ name: 'Intersolar Munich', date: 'June 23–25, 2026', type: 'Fair', location: 'Munich', url: 'https://www.intersolar.de/' },
|
{ name: 'Intersolar Munich', date: 'June 23–25, 2026', type: 'Fair', location: 'Munich', url: 'https://www.intersolar.de/' },
|
||||||
{ name: 'Wind Energy Days Linstow', date: 'November 10–12, 2026', type: 'Specialist Congress', location: 'Linstow', url: 'https://www.windenergietage.de/' },
|
{ name: 'Wind Energy Days Linstow', date: 'November 10–12, 2026', type: 'Specialist Congress', location: 'Linstow', url: 'https://www.windenergietage.de/' },
|
||||||
{ name: 'Werkstatt Kabel 2026', date: 'November 24–25, 2026', type: 'Symposium', location: 'Wiesbaden' }
|
{ name: 'Werkstatt Kabel 2026', date: 'November 24–25, 2026', type: 'Symposium', location: 'Wiesbaden', url: 'https://werkstatt-kabel.essociation.de/' }
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ layout: "fullBleed"
|
|||||||
badge="Core Competency"
|
badge="Core Competency"
|
||||||
title="Planning & Consulting"
|
title="Planning & Consulting"
|
||||||
subtitle="Competent support for energy and telecommunications networks"
|
subtitle="Competent support for energy and telecommunications networks"
|
||||||
backgroundImage={{ url: '/assets/photos/DJI_0048.JPG' }}
|
backgroundImage={{ url: '/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-298.jpg' }}
|
||||||
alignment="center"
|
alignment="center"
|
||||||
ctaLabel="Request a project"
|
ctaLabel="Request a project"
|
||||||
ctaHref="/en/contact"
|
ctaHref="/en/contact"
|
||||||
|
|||||||
@@ -66,14 +66,14 @@ layout: "fullBleed"
|
|||||||
description: "The complete range of services for the construction of cable routes from a single source.",
|
description: "The complete range of services for the construction of cable routes from a single source.",
|
||||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",
|
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",
|
||||||
url: "/en/standorte/guben",
|
url: "/en/standorte/guben",
|
||||||
backgroundImage: "/assets/photos/DJI_0243.JPG"
|
backgroundImage: "/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-54.jpg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "E-TIB Ingenieurgesellschaft mbH",
|
title: "E-TIB Ingenieurgesellschaft mbH",
|
||||||
description: "Our engineers plan the energy and telecommunications networks of the future for you - surveying and documentation included.",
|
description: "Our engineers plan the energy and telecommunications networks of the future for you - surveying and documentation included.",
|
||||||
icon: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4",
|
icon: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4",
|
||||||
url: "https://www.etib-ing.com/",
|
url: "https://www.etib-ing.com/",
|
||||||
backgroundImage: "/assets/photos/DSC01137.JPG"
|
backgroundImage: "/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-298.jpg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "E-TIB Verwaltung GmbH",
|
title: "E-TIB Verwaltung GmbH",
|
||||||
@@ -87,38 +87,12 @@ layout: "fullBleed"
|
|||||||
description: "Precise horizontal directional drilling in all soil classes for surface-friendly and trenchless pipe laying.",
|
description: "Precise horizontal directional drilling in all soil classes for surface-friendly and trenchless pipe laying.",
|
||||||
icon: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z",
|
icon: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z",
|
||||||
url: "/en/standorte/kirchheilingen",
|
url: "/en/standorte/kirchheilingen",
|
||||||
backgroundImage: "/assets/photos/DSC08653.JPG"
|
backgroundImage: "/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-47.jpg"
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-white py-24 border-b border-neutral-100">
|
|
||||||
<div className="container max-w-6xl mx-auto px-4">
|
|
||||||
<div className="text-center mb-16">
|
|
||||||
<div className="text-primary font-bold tracking-wider uppercase text-sm mb-4">Our Team</div>
|
|
||||||
<h2 className="font-heading text-4xl md:text-5xl font-extrabold text-neutral-dark mb-6">Over 100 Experts for Your Project</h2>
|
|
||||||
<div className="w-20 h-2 bg-primary mx-auto"></div>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-5xl font-extrabold text-primary mb-2">72</div>
|
|
||||||
<div className="text-neutral-dark font-bold text-lg">E-TIB GmbH</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-5xl font-extrabold text-primary mb-2">10</div>
|
|
||||||
<div className="text-neutral-dark font-bold text-lg">E-TIB Bohrtechnik</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-5xl font-extrabold text-primary mb-2">8</div>
|
|
||||||
<div className="text-neutral-dark font-bold text-lg">E-TIB ING</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-5xl font-extrabold text-primary mb-2">10</div>
|
|
||||||
<div className="text-neutral-dark font-bold text-lg">Lower Saxony</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<CallToAction />
|
<CallToAction />
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ layout: "fullBleed"
|
|||||||
<HeroSection
|
<HeroSection
|
||||||
badge="Core Competency"
|
badge="Core Competency"
|
||||||
title="Surveying & Documentation"
|
title="Surveying & Documentation"
|
||||||
subtitle="Seamless and legally compliant recording of all construction phases"
|
subtitle="High-precision digital as-built documentation, 3D scans, and drone surveying for your line routes and facilities."
|
||||||
backgroundImage={{ url: '/assets/photos/DSC08653.JPG' }}
|
backgroundImage={{ url: '/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(24_von_99)_groß2.jpg' }}
|
||||||
alignment="center"
|
alignment="center"
|
||||||
ctaLabel="Request a project"
|
ctaLabel="Request a project"
|
||||||
ctaHref="/en/contact"
|
ctaHref="/en/contact"
|
||||||
|
|||||||
@@ -60,30 +60,6 @@ layout: "fullBleed"
|
|||||||
type: 'tax',
|
type: 'tax',
|
||||||
date: '13.02.2025',
|
date: '13.02.2025',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: 'IHK Clearance Certificate',
|
|
||||||
description: 'Chamber of Industry and Commerce',
|
|
||||||
type: 'general',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'HWK Clearance Certificate',
|
|
||||||
description: 'Chamber of Crafts',
|
|
||||||
type: 'general',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Commercial Register Extract',
|
|
||||||
description: 'District Court Cottbus',
|
|
||||||
type: 'general',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Trade Registration',
|
|
||||||
description: 'City of Guben',
|
|
||||||
type: 'general',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Central Trade Register',
|
|
||||||
description: 'Information from the Register',
|
|
||||||
type: 'general',
|
|
||||||
}
|
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -896,3 +896,4 @@ export const minorLocations: Location[] = [
|
|||||||
description: 'pv',
|
description: 'pv',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
export const projectLocations = [...defaultLocations, ...minorLocations];
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ export function getStandortById(id: string): StandortData | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert StandortData to the Location format required by the map
|
// Convert StandortData to the Location format required by the map
|
||||||
export const standorteLocations = standorteData.map((standort) => ({
|
export const standorteLocations = standorteData.map(standort => ({
|
||||||
id: standort.id,
|
id: standort.id,
|
||||||
name: standort.shortName,
|
name: standort.shortName,
|
||||||
type: standort.type,
|
type: standort.type,
|
||||||
|
|||||||
1
login-result.html
Normal file
1
login-result.html
Normal file
File diff suppressed because one or more lines are too long
@@ -143,7 +143,7 @@
|
|||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"preinstall": "npx only-allow pnpm"
|
"preinstall": "npx only-allow pnpm"
|
||||||
},
|
},
|
||||||
"version": "2.2.12",
|
"version": "2.2.13-rc.0",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
"@parcel/watcher",
|
"@parcel/watcher",
|
||||||
|
|||||||
BIN
public/assets/eu-flag-perspective.png
Normal file
BIN
public/assets/eu-flag-perspective.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 572 KiB |
BIN
public/assets/eu-flag.webp
Normal file
BIN
public/assets/eu-flag.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
1
runs.json
Normal file
1
runs.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"message":"token is required","url":"https://git.infra.mintel.me/api/swagger"}
|
||||||
84
scripts/priority-gate.sh
Executable file
84
scripts/priority-gate.sh
Executable file
@@ -0,0 +1,84 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ -z "$GITEA_PAT" ]; then
|
||||||
|
echo "No GITEA_PAT provided, skipping priority gate."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$CURRENT_RUN_ID" ] || [ -z "$CURRENT_TARGET" ] || [ -z "$GITHUB_REPOSITORY" ]; then
|
||||||
|
echo "Missing required environment variables."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
declare -A PRIO=( ["production"]=40 ["staging"]=30 ["testing"]=20 ["branch"]=10 )
|
||||||
|
CURRENT_PRIO=${PRIO[$CURRENT_TARGET]:-0}
|
||||||
|
|
||||||
|
echo "Current Run: $CURRENT_RUN_ID, Target: $CURRENT_TARGET, Priority: $CURRENT_PRIO"
|
||||||
|
|
||||||
|
API_BASE="https://git.infra.mintel.me/api/v1/repos/$GITHUB_REPOSITORY/actions/runs"
|
||||||
|
|
||||||
|
# Fetch running and waiting workflows
|
||||||
|
LIMIT=50
|
||||||
|
RESPONSE=$(curl -s -H "Authorization: token $GITEA_PAT" "$API_BASE?limit=$LIMIT")
|
||||||
|
|
||||||
|
# Verify response is valid JSON
|
||||||
|
if ! echo "$RESPONSE" | jq empty 2>/dev/null; then
|
||||||
|
echo "Failed to parse API response from Gitea."
|
||||||
|
echo "$RESPONSE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use jq to extract running or waiting jobs that are NOT the current one
|
||||||
|
RUNS=$(echo "$RESPONSE" | jq -c '.[] | select(.id != '"$CURRENT_RUN_ID"') | select(.status == "running" or .status == "waiting")')
|
||||||
|
|
||||||
|
if [ -z "$RUNS" ]; then
|
||||||
|
echo "No other running/waiting workflows found."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r run; do
|
||||||
|
if [ -z "$run" ]; then continue; fi
|
||||||
|
|
||||||
|
RUN_ID=$(echo "$run" | jq -r '.id')
|
||||||
|
REF=$(echo "$run" | jq -r '.ref')
|
||||||
|
REF_NAME=${REF#refs/tags/}
|
||||||
|
REF_NAME=${REF_NAME#refs/heads/}
|
||||||
|
|
||||||
|
# Determine target of the running workflow
|
||||||
|
TARGET="branch"
|
||||||
|
if [ "$REF" == "refs/heads/main" ] || [ "$REF_NAME" == "main" ]; then
|
||||||
|
TARGET="testing"
|
||||||
|
elif [[ "$REF" == refs/tags/* ]] || [[ "$REF" == refs/heads/v* ]]; then
|
||||||
|
if [[ "$REF_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
TARGET="production"
|
||||||
|
else
|
||||||
|
TARGET="staging"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
RUN_PRIO=${PRIO[$TARGET]:-0}
|
||||||
|
|
||||||
|
echo "Found running workflow ID: $RUN_ID (Target: $TARGET, Priority: $RUN_PRIO)"
|
||||||
|
|
||||||
|
if [ "$CURRENT_PRIO" -gt "$RUN_PRIO" ]; then
|
||||||
|
echo "Current workflow has HIGHER priority ($CURRENT_PRIO > $RUN_PRIO). Canceling run $RUN_ID..."
|
||||||
|
curl -s -X POST -H "Authorization: token $GITEA_PAT" "$API_BASE/$RUN_ID/cancel"
|
||||||
|
elif [ "$CURRENT_PRIO" -lt "$RUN_PRIO" ]; then
|
||||||
|
echo "Current workflow has LOWER priority ($CURRENT_PRIO < $RUN_PRIO). Aborting self."
|
||||||
|
curl -s -X POST -H "Authorization: token $GITEA_PAT" "$API_BASE/$CURRENT_RUN_ID/cancel"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
# Equal priority: Newer wins
|
||||||
|
if [ "$CURRENT_RUN_ID" -gt "$RUN_ID" ]; then
|
||||||
|
echo "Equal priority, but current is newer. Canceling run $RUN_ID..."
|
||||||
|
curl -s -X POST -H "Authorization: token $GITEA_PAT" "$API_BASE/$RUN_ID/cancel"
|
||||||
|
else
|
||||||
|
echo "Equal priority, but current is older. Aborting self."
|
||||||
|
curl -s -X POST -H "Authorization: token $GITEA_PAT" "$API_BASE/$CURRENT_RUN_ID/cancel"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done <<< "$RUNS"
|
||||||
|
|
||||||
|
echo "Priority gate cleared. Continuing deployment."
|
||||||
@@ -29,7 +29,12 @@ async function run() {
|
|||||||
console.log(` Waiting for potential Gatekeeper redirect...`);
|
console.log(` Waiting for potential Gatekeeper redirect...`);
|
||||||
await new Promise((resolve) => setTimeout(resolve, 3000));
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||||
|
|
||||||
const isGatekeeper = await page.$('input[name="password"]');
|
let isGatekeeper = null;
|
||||||
|
try {
|
||||||
|
isGatekeeper = await page.$('input[name="password"]');
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Navigation happened while checking gatekeeper:', e.message);
|
||||||
|
}
|
||||||
if (isGatekeeper) {
|
if (isGatekeeper) {
|
||||||
await page.type('input[name="password"]', gatekeeperPassword);
|
await page.type('input[name="password"]', gatekeeperPassword);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@@ -65,7 +70,7 @@ async function run() {
|
|||||||
urls.map(async (url) => {
|
urls.map(async (url) => {
|
||||||
if (url.includes('openstreetmap.org') || url.includes('unpkg.com')) return;
|
if (url.includes('openstreetmap.org') || url.includes('unpkg.com')) return;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(url, { method: 'HEAD' });
|
const res = await fetch(url, { method: 'GET' });
|
||||||
if (res.status >= 400) {
|
if (res.status >= 400) {
|
||||||
// Diagnostic: check if the raw source is available
|
// Diagnostic: check if the raw source is available
|
||||||
const rawUrlMatch = url.match(/[?&]url=([^&]+)/);
|
const rawUrlMatch = url.match(/[?&]url=([^&]+)/);
|
||||||
@@ -73,7 +78,7 @@ async function run() {
|
|||||||
const rawPath = decodeURIComponent(rawUrlMatch[1]);
|
const rawPath = decodeURIComponent(rawUrlMatch[1]);
|
||||||
const baseUrl = new URL(url).origin;
|
const baseUrl = new URL(url).origin;
|
||||||
const rawUrl = `${baseUrl}${rawPath}`;
|
const rawUrl = `${baseUrl}${rawPath}`;
|
||||||
const rawRes = await fetch(rawUrl, { method: 'HEAD' });
|
const rawRes = await fetch(rawUrl, { method: 'GET' });
|
||||||
broken.push(`${url} (Status: ${res.status}, Raw Status: ${rawRes.status})`);
|
broken.push(`${url} (Status: ${res.status}, Raw Status: ${rawRes.status})`);
|
||||||
} else {
|
} else {
|
||||||
broken.push(`${url} (Status: ${res.status})`);
|
broken.push(`${url} (Status: ${res.status})`);
|
||||||
|
|||||||
32
test-contact-auth.cjs
Normal file
32
test-contact-auth.cjs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
const puppeteer = require('puppeteer');
|
||||||
|
(async () => {
|
||||||
|
const targetUrl = 'https://test.e-tib.com';
|
||||||
|
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026';
|
||||||
|
|
||||||
|
const browser = await puppeteer.launch({ headless: true });
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||||
|
|
||||||
|
const isGatekeeper = await page.$('input[name="password"]');
|
||||||
|
if (isGatekeeper) {
|
||||||
|
await page.type('input[name="password"]', gatekeeperPassword);
|
||||||
|
await Promise.all([
|
||||||
|
page.click('button[type="submit"]'),
|
||||||
|
]);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.screenshot({ path: 'after-login.png' });
|
||||||
|
|
||||||
|
await page.goto(`${targetUrl}/de/kontakt`, { waitUntil: 'domcontentloaded' });
|
||||||
|
await new Promise(r => setTimeout(r, 2000));
|
||||||
|
|
||||||
|
const html = await page.content();
|
||||||
|
const fs = require('fs');
|
||||||
|
fs.writeFileSync('contact-page-auth-debug.html', html);
|
||||||
|
await page.screenshot({ path: 'contact.png' });
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
})();
|
||||||
12
test-contact.cjs
Normal file
12
test-contact.cjs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const puppeteer = require('puppeteer');
|
||||||
|
(async () => {
|
||||||
|
const browser = await puppeteer.launch({ headless: true });
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
await page.goto('https://test.e-tib.com/de/kontakt', { waitUntil: 'networkidle0' });
|
||||||
|
const html = await page.content();
|
||||||
|
const fs = require('fs');
|
||||||
|
fs.writeFileSync('contact-page-debug.html', html);
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
})();
|
||||||
12
test-contact.js
Normal file
12
test-contact.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const puppeteer = require('puppeteer');
|
||||||
|
(async () => {
|
||||||
|
const browser = await puppeteer.launch({ headless: true });
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
await page.goto('https://test.e-tib.com/de/kontakt', { waitUntil: 'networkidle0' });
|
||||||
|
const html = await page.content();
|
||||||
|
const fs = require('fs');
|
||||||
|
fs.writeFileSync('contact-page-debug.html', html);
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
})();
|
||||||
24
test-login-error.cjs
Normal file
24
test-login-error.cjs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
const puppeteer = require('puppeteer');
|
||||||
|
(async () => {
|
||||||
|
const targetUrl = 'https://test.e-tib.com';
|
||||||
|
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026';
|
||||||
|
|
||||||
|
const browser = await puppeteer.launch({ headless: true });
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
await page.goto(targetUrl, { waitUntil: 'networkidle0' });
|
||||||
|
const isGatekeeper = await page.$('input[name="password"]');
|
||||||
|
if (isGatekeeper) {
|
||||||
|
await page.type('input[name="password"]', gatekeeperPassword);
|
||||||
|
await Promise.all([
|
||||||
|
page.click('button[type="submit"]'),
|
||||||
|
page.waitForNavigation({ waitUntil: 'networkidle0' }).catch(() => {})
|
||||||
|
]);
|
||||||
|
const html = await page.content();
|
||||||
|
const fs = require('fs');
|
||||||
|
fs.writeFileSync('login-result.html', html);
|
||||||
|
} else {
|
||||||
|
console.log("No gatekeeper found!");
|
||||||
|
}
|
||||||
|
await browser.close();
|
||||||
|
})();
|
||||||
@@ -4,7 +4,7 @@ import path from 'path';
|
|||||||
|
|
||||||
describe('Legal Pages Content Validation', () => {
|
describe('Legal Pages Content Validation', () => {
|
||||||
const deAgbPath = path.join(process.cwd(), 'content', 'de', 'agb.mdx');
|
const deAgbPath = path.join(process.cwd(), 'content', 'de', 'agb.mdx');
|
||||||
const enTermsPath = path.join(process.cwd(), 'content', 'en', 'terms.mdx');
|
const enTermsPath = path.join(process.cwd(), 'content', 'en', 'agb.mdx');
|
||||||
|
|
||||||
it('should contain highly professional B2B sections in German AGB', () => {
|
it('should contain highly professional B2B sections in German AGB', () => {
|
||||||
const content = fs.readFileSync(deAgbPath, 'utf8');
|
const content = fs.readFileSync(deAgbPath, 'utf8');
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ describe('Task 12 TDD - Bohrtechnik and Reference Links', () => {
|
|||||||
const urlDeIndex = bohrtechnikDeBlock.indexOf('url:');
|
const urlDeIndex = bohrtechnikDeBlock.indexOf('url:');
|
||||||
const nextItemDeIndex = bohrtechnikDeBlock.indexOf('title:', 10);
|
const nextItemDeIndex = bohrtechnikDeBlock.indexOf('title:', 10);
|
||||||
|
|
||||||
// Ensure "url:" exists within the E-TIB Bohrtechnik block and points to "/de/standorte/kirchheilingen"
|
// Ensure "url:" exists within the E-TIB Bohrtechnik block and points to "/de/bohrtechnik"
|
||||||
expect(deContent).toMatch(/title:\s*"E-TIB Bohrtechnik GmbH"[\s\S]*?url:\s*["']\/de\/standorte\/kirchheilingen["']/);
|
expect(deContent).toMatch(/title:\s*"E-TIB Bohrtechnik GmbH"[\s\S]*?url:\s*["']\/de\/bohrtechnik["']/);
|
||||||
|
|
||||||
// Assert that E-TIB Bohrtechnik GmbH has url: "/en/standorte/kirchheilingen" in English content
|
// Assert that E-TIB Bohrtechnik GmbH has url: "/en/drilling-technology" in English content
|
||||||
expect(enContent).toContain('title: "E-TIB Bohrtechnik GmbH"');
|
expect(enContent).toContain('title: "E-TIB Bohrtechnik GmbH"');
|
||||||
expect(enContent).toMatch(/title:\s*"E-TIB Bohrtechnik GmbH"[\s\S]*?url:\s*["']\/en\/standorte\/kirchheilingen["']/);
|
expect(enContent).toMatch(/title:\s*"E-TIB Bohrtechnik GmbH"[\s\S]*?url:\s*["']\/en\/drilling-technology["']/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should verify that all reference projects in map-data have no href links', () => {
|
it('should verify that all reference projects in map-data have no href links', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user