Compare commits
22 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 |
@@ -17,9 +17,6 @@ env:
|
||||
PUPPETEER_SKIP_DOWNLOAD: "true"
|
||||
COREPACK_NPM_REGISTRY: "https://registry.npmmirror.com"
|
||||
|
||||
concurrency:
|
||||
group: deploy-pipeline
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
@@ -146,6 +143,13 @@ jobs:
|
||||
echo "short_sha=$SHORT_SHA"
|
||||
} >> "$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)
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -2,145 +2,4 @@
|
||||
> e-tib-nextjs@2.2.12 lint /Volumes/Alpha SSD/Coding/e-tib.com
|
||||
> eslint .
|
||||
|
||||
|
||||
/Volumes/Alpha SSD/Coding/e-tib.com/app/[locale]/[slug]/page.tsx
|
||||
1:20 warning 'redirect' is defined but never used @typescript-eslint/no-unused-vars
|
||||
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
|
||||
118: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/app/[locale]/standorte/[slug]/page.tsx
|
||||
9:10 warning 'getButtonClasses' is defined but never used @typescript-eslint/no-unused-vars
|
||||
9:28 warning 'ButtonOverlay' is defined but never used @typescript-eslint/no-unused-vars
|
||||
58:9 warning 't' is assigned a value 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
|
||||
56:9 warning 'hq' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
57:9 warning 'branch' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
58:9 warning 'projects' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
59: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
|
||||
20: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/SubCompanyTiles.tsx
|
||||
92:19 warning 'CardWrapper' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
93:19 warning 'wrapperProps' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/Volumes/Alpha SSD/Coding/e-tib.com/components/blocks/TeamGridBlock.tsx
|
||||
20:39 warning 'title' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars
|
||||
20:46 warning 'department' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars
|
||||
20:58 warning 'showContact' is assigned a value but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars
|
||||
|
||||
/Volumes/Alpha SSD/Coding/e-tib.com/components/decorations/CorporateBackground.tsx
|
||||
36:13 warning 'distance' is assigned a value but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
/Volumes/Alpha SSD/Coding/e-tib.com/components/home/Hero.tsx
|
||||
9:39 warning 'useCallback' is defined but never used @typescript-eslint/no-unused-vars
|
||||
77:6 warning React Hook useEffect has 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
|
||||
|
||||
/Volumes/Alpha SSD/Coding/e-tib.com/lib/standorte-data.ts
|
||||
1:10 warning 'LucideIcon' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1:22 warning 'MapPin' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1:30 warning 'Building2' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1:41 warning 'HardHat' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1:50 warning 'Phone' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1:57 warning 'Mail' is defined but never used @typescript-eslint/no-unused-vars
|
||||
1:63 warning 'Navigation' is defined but never used @typescript-eslint/no-unused-vars
|
||||
|
||||
✖ 56 problems (0 errors, 56 warnings)
|
||||
0 errors and 2 warnings potentially fixable with the `--fix` option.
|
||||
|
||||
ELIFECYCLE Command failed.
|
||||
|
||||
@@ -5,33 +5,51 @@
|
||||
|
||||
[1m[46m RUN [49m[22m [36mv4.1.2 [39m[90m/Volumes/Alpha SSD/Coding/e-tib.com[39m
|
||||
|
||||
(node:74132) [DEP0205] DeprecationWarning: `module.register()` is deprecated. Use `module.registerHooks()` instead.
|
||||
(node:64961) [DEP0205] DeprecationWarning: `module.register()` is deprecated. Use `module.registerHooks()` instead.
|
||||
(Use `node --trace-deprecation ...` to show where the warning was created)
|
||||
[32m✓[39m tests/homepage-and-competence-content.test.ts [2m([22m[2m4 tests[22m[2m)[22m[32m 10[2mms[22m[39m
|
||||
[32m✓[39m tests/task-12-links.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 5[2mms[22m[39m
|
||||
[32m✓[39m tests/mdx-links.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 11[2mms[22m[39m
|
||||
[32m✓[39m tests/legal-pages.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 15[2mms[22m[39m
|
||||
[32m✓[39m tests/messen-content.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 30[2mms[22m[39m
|
||||
[32m✓[39m lib/mail/mailer.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 4[2mms[22m[39m
|
||||
[32m✓[39m components/search/AISearchResults.test.tsx [2m([22m[2m1 test[22m[2m)[22m[32m 25[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
|
||||
[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 26[2mms[22m[39m
|
||||
[32m✓[39m lib/env.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 3[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 52[2mms[22m[39m
|
||||
[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`.
|
||||
|
||||
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[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
|
||||
[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 7[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[32m11 passed[39m[22m[90m (11)[39m
|
||||
[2m Tests [22m [1m[32m27 passed[39m[22m[90m (27)[39m
|
||||
[2m Start at [22m 22:40:02
|
||||
[2m Duration [22m 2.69s[2m (transform 2.09s, setup 0ms, import 4.45s, tests 179ms, environment 8.10s)[22m
|
||||
[31m⎯⎯⎯⎯⎯⎯⎯[39m[1m[41m Failed Tests 1 [49m[22m[31m⎯⎯⎯⎯⎯⎯⎯[39m
|
||||
|
||||
[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
|
||||
> 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.
|
||||
@@ -186,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'}`}>
|
||||
{/* Generic Hero Section (only for standard pages) */}
|
||||
{!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 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>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { TransitionProvider } from '@/components/providers/TransitionProvider';
|
||||
import { PageTransitionShutter } from '@/components/providers/PageTransitionShutter';
|
||||
import { InitialLoader } from '@/components/providers/InitialLoader';
|
||||
import { CorporateBackground } from '@/components/decorations/CorporateBackground';
|
||||
import { EUFundingBadge } from '@/components/decorations/EUFundingBadge';
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
@@ -202,6 +203,7 @@ export default async function Layout(props: {
|
||||
<PageTransitionShutter />
|
||||
<SkipLink />
|
||||
<Header navLinks={navLinks} />
|
||||
<EUFundingBadge />
|
||||
<MobileBottomNav navLinks={navLinks} currentLocale={safeLocale} />
|
||||
|
||||
<main
|
||||
|
||||
@@ -65,7 +65,7 @@ export default async function StandortDetail(props: { params: Promise<{ locale:
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-white">
|
||||
{/* Generic Hero Section to match standard pages */}
|
||||
<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={`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="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>
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
}
|
||||
|
||||
.hide-scrollbar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none !important; /* IE and Edge */
|
||||
scrollbar-width: none !important; /* Firefox */
|
||||
}
|
||||
.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 {
|
||||
|
||||
@@ -68,31 +68,7 @@ const defaultCertificates: Certificate[] = [
|
||||
type: 'tax',
|
||||
date: '13.02.2025',
|
||||
},
|
||||
{
|
||||
title: 'Unbedenklichkeit IHK',
|
||||
description: 'Industrie- und Handelskammer',
|
||||
type: 'general',
|
||||
},
|
||||
{
|
||||
title: 'Unbedenklichkeit HWK',
|
||||
description: 'Handwerkskammer',
|
||||
type: 'general',
|
||||
},
|
||||
{
|
||||
title: 'Handelsregisterauszug',
|
||||
description: 'Amtsgericht Cottbus',
|
||||
type: 'general',
|
||||
},
|
||||
{
|
||||
title: 'Gewerbe-Anmeldung',
|
||||
description: 'Stadt Guben',
|
||||
type: 'general',
|
||||
},
|
||||
{
|
||||
title: 'Gewerbezentralregister',
|
||||
description: 'Auskunft aus dem Register',
|
||||
type: 'general',
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
export function CertificatesBlock({ badge, title, description, certificates = defaultCertificates, hideHeader = false }: CertificatesBlockProps) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import Image from 'next/image';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Badge, Container, Heading } from '@/components/ui';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
@@ -39,13 +40,15 @@ const itemVariants = {
|
||||
export const HeroSection: React.FC<HeroSectionProps> = (props) => {
|
||||
const { title, badge, subtitle, backgroundImage, alignment, ctaLabel, ctaHref } = props;
|
||||
const bgSrc = backgroundImage?.sizes?.card?.url || backgroundImage?.url;
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<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 && (
|
||||
<motion.div
|
||||
key={`hero-bg-${pathname}`}
|
||||
className="absolute inset-0 z-0"
|
||||
initial={{ scale: 1.05, opacity: 0 }}
|
||||
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' : ''}`}>
|
||||
<motion.div
|
||||
key={`hero-content-${pathname}`}
|
||||
className={`max-w-4xl ${alignment === 'center' ? 'mx-auto' : ''}`}
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Button } from '@/components/ui/Button';
|
||||
import Image from 'next/image';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
interface HeroVideoProps {
|
||||
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 explicitNoVideo = props.videoUrl === 'none' || props.videoUrl === '';
|
||||
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 posterAlt = props.backgroundImage?.alt || data?.posterImage?.alt || "E-TIB Gruppe Baustelle";
|
||||
@@ -56,7 +58,7 @@ export function HeroVideo(props: HeroVideoProps) {
|
||||
|
||||
{videoUrl ? (
|
||||
<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"
|
||||
src={videoUrl}
|
||||
autoPlay
|
||||
@@ -67,6 +69,7 @@ export function HeroVideo(props: HeroVideoProps) {
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
key={`img-${pathname}-${posterSrc}`}
|
||||
src={posterSrc}
|
||||
alt={posterAlt}
|
||||
fill
|
||||
@@ -88,6 +91,7 @@ export function HeroVideo(props: HeroVideoProps) {
|
||||
|
||||
<div className="container relative z-[4] text-center px-4">
|
||||
<motion.div
|
||||
key={`hero-content-${pathname}`}
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 1.2, ease: [0.16, 1, 0.3, 1] }}
|
||||
|
||||
@@ -4,7 +4,7 @@ import React, { useState } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { MapPin, Factory, Zap, CheckCircle2, ArrowUpRight } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useRouter, usePathname } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { AnimatedGlossyBorder } from '@/components/ui/AnimatedGlossyBorder';
|
||||
import { Location, defaultLocations, minorLocations, projectLocations } from '@/lib/map-data';
|
||||
@@ -37,6 +37,7 @@ export function InteractiveGermanyMap({
|
||||
}: InteractiveGermanyMapProps) {
|
||||
const [activeLocation, setActiveLocation] = useState<Location | null>(null);
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const locale = useLocale();
|
||||
const t = useTranslations('InteractiveGermanyMap');
|
||||
const tStandard = useTranslations('StandardPage');
|
||||
@@ -57,7 +58,7 @@ export function InteractiveGermanyMap({
|
||||
const minorNodes = locations.filter((l) => l.type === 'minor_node');
|
||||
|
||||
return (
|
||||
<div className={isHero ? "relative w-full" : "relative w-full max-w-7xl mx-auto py-16 md:py-24 px-4 sm:px-6 mt-16 md:mt-20"}>
|
||||
<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`}>
|
||||
{/* Animated Border Glow */}
|
||||
{!isHero && <AnimatedGlossyBorder opacity={0.7} className="z-30" />}
|
||||
|
||||
@@ -133,14 +133,16 @@ export function ReferencesSlider(props: ReferencesSliderProps) {
|
||||
<Link
|
||||
href={`/${locale}/referenzen/${ref.slug}`}
|
||||
data-testid="reference-tile"
|
||||
draggable={false}
|
||||
onClick={(e) => {
|
||||
if (dragDistanceRef.current > 5) {
|
||||
if (dragDistanceRef.current > 15) {
|
||||
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
|
||||
draggable={false}
|
||||
src={imgSrc}
|
||||
alt={ref.title}
|
||||
fill
|
||||
|
||||
@@ -79,7 +79,7 @@ export function SubCompanyTiles(props: SubCompanyTilesProps) {
|
||||
)}
|
||||
|
||||
<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}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
@@ -236,7 +236,7 @@ export function SubCompanyTiles(props: SubCompanyTilesProps) {
|
||||
<motion.div
|
||||
key={index}
|
||||
variants={itemVariants}
|
||||
className="h-full"
|
||||
className="h-full flex-shrink-0 snap-center w-[85%] sm:w-[70%] md:w-full"
|
||||
>
|
||||
{hasUrl ? (
|
||||
isExternal ? (
|
||||
|
||||
@@ -78,7 +78,7 @@ export function CorporateBackground() {
|
||||
const y5 = useTransform(scrollYProgress, [0, 1], [0, -500]);
|
||||
|
||||
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 */}
|
||||
<motion.div
|
||||
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 { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { AISearchResults } from '../search/AISearchResults';
|
||||
const HeroIllustration = dynamic(() => import('./HeroIllustration'), { 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 }) {
|
||||
const t = useTranslations('Home.hero');
|
||||
const locale = useLocale();
|
||||
const pathname = usePathname();
|
||||
const { trackEvent } = useAnalytics();
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
@@ -85,7 +87,7 @@ export default function Hero({ data }: { data?: any }) {
|
||||
|
||||
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">
|
||||
<div className="max-w-5xl mx-auto md:mx-0">
|
||||
<div>
|
||||
|
||||
@@ -18,7 +18,7 @@ export function Footer({ companyInfo }: FooterProps) {
|
||||
const locale = useLocale();
|
||||
|
||||
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 */}
|
||||
<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">
|
||||
{/* 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="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">
|
||||
|
||||
{/* 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" />
|
||||
<h4 className="font-heading font-bold uppercase tracking-widest text-sm mb-6 md:mb-8 text-white">
|
||||
{locale === 'de' ? 'Unternehmen' : 'Company'}
|
||||
@@ -124,7 +124,7 @@ export function Footer({ companyInfo }: FooterProps) {
|
||||
</div>
|
||||
|
||||
{/* 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" />
|
||||
<h4 className="font-heading font-bold uppercase tracking-widest text-sm mb-6 md:mb-8 text-white">
|
||||
{locale === 'de' ? 'Rechtliches' : 'Legal'}
|
||||
|
||||
@@ -72,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)] ${
|
||||
isStandard
|
||||
? '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
|
||||
@@ -80,7 +80,7 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
isStandard
|
||||
? 'py-4 px-6 md:px-8'
|
||||
: 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'
|
||||
}`}
|
||||
>
|
||||
@@ -165,6 +165,20 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
function NavItem({ link, currentLocale, pathname, isSolidMode }: { link: NavLink, currentLocale: string, pathname: string, isSolidMode: boolean }) {
|
||||
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)/)
|
||||
? `/${currentLocale}${link.url}`
|
||||
: link.url;
|
||||
@@ -237,6 +251,7 @@ function NavItem({ link, currentLocale, pathname, isSolidMode }: { link: NavLink
|
||||
<TransitionLink
|
||||
key={child.url}
|
||||
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 ${
|
||||
isSolidMode
|
||||
? isChildActive ? 'bg-primary/10 text-primary' : 'text-neutral-600 hover:text-primary hover:bg-neutral-50/80'
|
||||
|
||||
@@ -9,7 +9,7 @@ layout: "fullBleed"
|
||||
badge="Kernkompetenz"
|
||||
title="Bohrtechnik"
|
||||
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"
|
||||
ctaLabel="Projekt anfragen"
|
||||
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.",
|
||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",
|
||||
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",
|
||||
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",
|
||||
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",
|
||||
@@ -122,7 +122,7 @@ description: "Die E-TIB GmbH ist Ihr zuverlässiger Partner für komplexe Kabelt
|
||||
tag: "Energie",
|
||||
size: "large",
|
||||
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",
|
||||
@@ -130,7 +130,7 @@ description: "Die E-TIB GmbH ist Ihr zuverlässiger Partner für komplexe Kabelt
|
||||
tag: "Innovation",
|
||||
size: "medium",
|
||||
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",
|
||||
@@ -138,14 +138,15 @@ description: "Die E-TIB GmbH ist Ihr zuverlässiger Partner für komplexe Kabelt
|
||||
tag: "Präzision",
|
||||
size: "small",
|
||||
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",
|
||||
description: "Digitale Bestandsdokumentation",
|
||||
tag: "Digital",
|
||||
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" }
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -5,13 +5,14 @@ excerpt: "Werden Sie Teil eines dynamischen Teams und bauen Sie mit uns die Infr
|
||||
layout: "fullBleed"
|
||||
---
|
||||
|
||||
<HomeHero
|
||||
<HeroSection
|
||||
badge="Karriere"
|
||||
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."
|
||||
videoUrl="/assets/videos/web/hero-kabelpflug.mp4"
|
||||
linkText="Offene Stellen"
|
||||
linkHref="#stellen"
|
||||
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."
|
||||
backgroundImage={{ url: '/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-28.jpg' }}
|
||||
alignment="center"
|
||||
ctaLabel="Offene Stellen"
|
||||
ctaHref="#stellen"
|
||||
/>
|
||||
|
||||
<JobListingBlock showFairs={false} showJobs={true} />
|
||||
|
||||
@@ -5,13 +5,14 @@ excerpt: "Treffen Sie das Team der E-TIB Gruppe persönlich auf Fachmessen und B
|
||||
layout: "fullBleed"
|
||||
---
|
||||
|
||||
<HomeHero
|
||||
<HeroSection
|
||||
badge="Messen & Events"
|
||||
title="Treffen Sie uns persönlich"
|
||||
description="Lernen Sie die E-TIB Gruppe auf den führenden Branchenmessen kennen."
|
||||
videoUrl="/assets/videos/web/hero-bahnkreuzung.mp4"
|
||||
linkText="Kontakt aufnehmen"
|
||||
linkHref="/de/kontakt"
|
||||
subtitle="Lernen Sie die E-TIB Gruppe auf den führenden Branchenmessen kennen."
|
||||
backgroundImage={{ url: '/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-100.jpg' }}
|
||||
alignment="center"
|
||||
ctaLabel="Kontakt aufnehmen"
|
||||
ctaHref="/de/kontakt"
|
||||
/>
|
||||
|
||||
<div id="termine">
|
||||
|
||||
@@ -9,7 +9,7 @@ layout: "fullBleed"
|
||||
badge="Kernkompetenz"
|
||||
title="Planung & Beratung"
|
||||
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"
|
||||
ctaLabel="Projekt anfragen"
|
||||
ctaHref="/de/kontakt"
|
||||
|
||||
@@ -33,7 +33,7 @@ layout: "fullBleed"
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
<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-neutral-dark font-semibold">Mitarbeiter</div>
|
||||
<div className="text-neutral-dark font-semibold">Mitarbeitende</div>
|
||||
</div>
|
||||
<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>
|
||||
@@ -66,14 +66,14 @@ layout: "fullBleed"
|
||||
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",
|
||||
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",
|
||||
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",
|
||||
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",
|
||||
@@ -87,38 +87,12 @@ layout: "fullBleed"
|
||||
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",
|
||||
url: "/de/standorte/kirchheilingen",
|
||||
backgroundImage: "/assets/photos/DSC08653.JPG"
|
||||
backgroundImage: "/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-47.jpg"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</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 />
|
||||
|
||||
@@ -8,8 +8,8 @@ layout: "fullBleed"
|
||||
<HeroSection
|
||||
badge="Kernkompetenz"
|
||||
title="Vermessung & Dokumentation"
|
||||
subtitle="Lückenlose und rechtssichere Erfassung aller Bauphasen"
|
||||
backgroundImage={{ url: '/assets/photos/DSC08653.JPG' }}
|
||||
subtitle="Hochpräzise digitale Bestandsdokumentation, 3D-Scans und Drohnenvermessung für Ihre Leitungstrassen und Anlagen."
|
||||
backgroundImage={{ url: '/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(24_von_99)_groß2.jpg' }}
|
||||
alignment="center"
|
||||
ctaLabel="Projekt anfragen"
|
||||
ctaHref="/de/kontakt"
|
||||
|
||||
@@ -9,7 +9,7 @@ layout: "fullBleed"
|
||||
badge="Core Competency"
|
||||
title="Drilling Technology"
|
||||
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"
|
||||
ctaLabel="Request a project"
|
||||
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.",
|
||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",
|
||||
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",
|
||||
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",
|
||||
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",
|
||||
@@ -122,7 +122,7 @@ description: "E-TIB GmbH is your reliable partner for complex cable routes, hori
|
||||
tag: "Energy",
|
||||
size: "large",
|
||||
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",
|
||||
@@ -130,7 +130,7 @@ description: "E-TIB GmbH is your reliable partner for complex cable routes, hori
|
||||
tag: "Innovation",
|
||||
size: "medium",
|
||||
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",
|
||||
@@ -138,14 +138,15 @@ description: "E-TIB GmbH is your reliable partner for complex cable routes, hori
|
||||
tag: "Precision",
|
||||
size: "small",
|
||||
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",
|
||||
description: "Digital as-built documentation",
|
||||
tag: "Digital",
|
||||
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" }
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -5,13 +5,14 @@ excerpt: "Become part of a dynamic team and help us build the infrastructure of
|
||||
layout: "fullBleed"
|
||||
---
|
||||
|
||||
<HomeHero
|
||||
<HeroSection
|
||||
badge="Careers"
|
||||
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."
|
||||
videoUrl="/assets/videos/web/hero-kabelpflug.mp4"
|
||||
linkText="Open Positions"
|
||||
linkHref="#stellen"
|
||||
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."
|
||||
backgroundImage={{ url: '/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-28.jpg' }}
|
||||
alignment="center"
|
||||
ctaLabel="Open Positions"
|
||||
ctaHref="#stellen"
|
||||
/>
|
||||
|
||||
<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"
|
||||
---
|
||||
|
||||
<HomeHero
|
||||
<HeroSection
|
||||
badge="Fairs & Events"
|
||||
title="Meet us personally"
|
||||
description="Get to know the E-TIB Group at the leading industry trade fairs."
|
||||
videoUrl="/assets/videos/web/hero-bahnkreuzung.mp4"
|
||||
linkText="Get in Touch"
|
||||
linkHref="/en/contact"
|
||||
subtitle="Get to know the E-TIB Group at the leading industry trade fairs."
|
||||
backgroundImage={{ url: '/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-100.jpg' }}
|
||||
alignment="center"
|
||||
ctaLabel="Get in Touch"
|
||||
ctaHref="/en/contact"
|
||||
/>
|
||||
|
||||
<div id="termine">
|
||||
|
||||
@@ -9,7 +9,7 @@ layout: "fullBleed"
|
||||
badge="Core Competency"
|
||||
title="Planning & Consulting"
|
||||
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"
|
||||
ctaLabel="Request a project"
|
||||
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.",
|
||||
icon: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",
|
||||
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",
|
||||
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",
|
||||
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",
|
||||
@@ -87,38 +87,12 @@ layout: "fullBleed"
|
||||
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",
|
||||
url: "/en/standorte/kirchheilingen",
|
||||
backgroundImage: "/assets/photos/DSC08653.JPG"
|
||||
backgroundImage: "/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-47.jpg"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</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 />
|
||||
|
||||
@@ -8,8 +8,8 @@ layout: "fullBleed"
|
||||
<HeroSection
|
||||
badge="Core Competency"
|
||||
title="Surveying & Documentation"
|
||||
subtitle="Seamless and legally compliant recording of all construction phases"
|
||||
backgroundImage={{ url: '/assets/photos/DSC08653.JPG' }}
|
||||
subtitle="High-precision digital as-built documentation, 3D scans, and drone surveying for your line routes and facilities."
|
||||
backgroundImage={{ url: '/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(24_von_99)_groß2.jpg' }}
|
||||
alignment="center"
|
||||
ctaLabel="Request a project"
|
||||
ctaHref="/en/contact"
|
||||
|
||||
@@ -60,30 +60,6 @@ layout: "fullBleed"
|
||||
type: 'tax',
|
||||
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',
|
||||
}
|
||||
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -143,7 +143,7 @@
|
||||
"prepare": "husky",
|
||||
"preinstall": "npx only-allow pnpm"
|
||||
},
|
||||
"version": "2.2.12",
|
||||
"version": "2.2.13-rc.0",
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"@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 |
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."
|
||||
@@ -4,7 +4,7 @@ import path from 'path';
|
||||
|
||||
describe('Legal Pages Content Validation', () => {
|
||||
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', () => {
|
||||
const content = fs.readFileSync(deAgbPath, 'utf8');
|
||||
|
||||
Reference in New Issue
Block a user