feat: upgrade UI with premium industrial aesthetics, purely visual language switch, and resilient error pages
Former-commit-id: e4d801c24e64c41816f86de9767b47894abb6806
This commit is contained in:
@@ -34,8 +34,17 @@ export function HeroVideo(props: HeroVideoProps) {
|
||||
const ctaLabel = props.ctaLabel || props.linkText || data?.ctaLabel || 'Unternehmen entdecken';
|
||||
const ctaHref = props.ctaHref || props.linkHref || data?.ctaHref || '#unternehmen';
|
||||
|
||||
const secondaryCtaLabel = props.secondaryCtaLabel || data?.secondaryCtaLabel || 'Projekt anfragen';
|
||||
const secondaryCtaHref = props.secondaryCtaHref || data?.secondaryCtaHref || '/kontakt';
|
||||
let currentLocale = 'de';
|
||||
try {
|
||||
// We can't unconditionally call hooks if they might fail outside context, but HeroVideo is usually in context.
|
||||
// However, to be safe and avoid adding new imports, we can use window.location or default to 'de' if not available.
|
||||
if (typeof window !== 'undefined') {
|
||||
currentLocale = window.location.pathname.startsWith('/en') ? 'en' : 'de';
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
const secondaryCtaLabel = props.secondaryCtaLabel || data?.secondaryCtaLabel || (currentLocale === 'de' ? 'Projekt anfragen' : 'Inquire Project');
|
||||
const secondaryCtaHref = props.secondaryCtaHref || data?.secondaryCtaHref || `/${currentLocale}/${currentLocale === 'de' ? 'contact' : 'contact'}`;
|
||||
|
||||
return (
|
||||
<div className="relative w-full h-[100svh] flex items-center justify-center overflow-hidden bg-neutral-dark">
|
||||
@@ -72,7 +81,7 @@ export function HeroVideo(props: HeroVideoProps) {
|
||||
>
|
||||
<h1
|
||||
className="font-heading font-bold text-4xl md:text-6xl lg:text-8xl text-white mb-6 uppercase tracking-tight drop-shadow-lg"
|
||||
dangerouslySetInnerHTML={{ __html: title.replace('KABELTIEFBAU', '<span class="text-primary-light">KABELTIEFBAU</span>').replace(/\n/g, '<br />') }}
|
||||
dangerouslySetInnerHTML={{ __html: title.replace('KABELTIEFBAU', '<span class="text-primary-light">KABELTIEFBAU</span>').replace(/\\n|\n/g, '<br />') }}
|
||||
/>
|
||||
|
||||
<p className="text-lg md:text-xl lg:text-2xl text-white/90 max-w-3xl mx-auto mb-12 drop-shadow">
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { HoverShineOverlay } from '@/components/ui/HoverShineOverlay';
|
||||
|
||||
export interface JobListingBlockProps {
|
||||
title?: string;
|
||||
showJobs?: boolean;
|
||||
showFairs?: boolean;
|
||||
fairsTitle?: string;
|
||||
fairs?: Array<{ name: string; date: string; type: string }>;
|
||||
fairs?: Array<{
|
||||
name: string;
|
||||
date: string;
|
||||
type: string;
|
||||
location?: string;
|
||||
booth?: string;
|
||||
url?: string;
|
||||
}>;
|
||||
emptyStateMessage?: string;
|
||||
emptyStateLinkText?: string;
|
||||
emptyStateLinkHref?: string;
|
||||
@@ -20,9 +28,9 @@ export const JobListingBlock = async (props: JobListingBlockProps) => {
|
||||
showFairs,
|
||||
fairsTitle = 'Nächste Messetermine 2026',
|
||||
fairs = [
|
||||
{ name: 'Intersolar München', date: '19. - 21. Juni 2026', type: 'Energie-Messe' },
|
||||
{ name: 'Windenergietage Linstow', date: '04. - 06. November 2026', type: 'Fachkongress' },
|
||||
{ name: 'Kabelwerkstatt Wiesbaden', date: '02. - 03. Dezember 2026', type: 'Fachmesse' }
|
||||
{ name: 'Intersolar München', date: '19. - 21. Juni 2026', type: 'Energie-Messe', location: 'Messe München', booth: 'Halle A3, Stand 110', url: 'https://www.intersolar.de' },
|
||||
{ name: 'Windenergietage Linstow', date: '04. - 06. November 2026', type: 'Fachkongress', location: 'Van der Valk Resort Linstow', booth: 'Stand 42', url: 'https://windenergietage.de' },
|
||||
{ name: 'Kabelwerkstatt Wiesbaden', date: '02. - 03. Dezember 2026', type: 'Fachmesse', location: 'RheinMain CongressCenter', booth: 'Halle 1, Stand B20' }
|
||||
],
|
||||
emptyStateMessage = 'Aktuell sind alle Positionen besetzt. Senden Sie uns gerne eine Initiativbewerbung!',
|
||||
emptyStateLinkText = 'Jetzt Kontakt aufnehmen',
|
||||
@@ -34,27 +42,97 @@ export const JobListingBlock = async (props: JobListingBlockProps) => {
|
||||
|
||||
return (
|
||||
<div className="py-12 md:py-24">
|
||||
<div className="container max-w-4xl mx-auto">
|
||||
<div className="container mx-auto">
|
||||
{showFairs && (
|
||||
<div className="bg-neutral-dark rounded-2xl p-8 md:p-12 mb-16 text-white overflow-hidden relative group">
|
||||
<div className="absolute top-0 right-0 w-64 h-64 bg-primary/20 blur-3xl -translate-y-1/2 translate-x-1/2" />
|
||||
<h3 className="font-heading font-bold text-3xl mb-8 relative z-10 flex items-center gap-3">
|
||||
<span className="w-8 h-1 bg-primary" />
|
||||
<div className="mb-24 relative">
|
||||
<h3 className="font-heading font-extrabold text-4xl text-neutral-dark mb-12 flex items-center gap-4">
|
||||
<span className="w-12 h-1.5 bg-primary rounded-full" />
|
||||
{fairsTitle}
|
||||
</h3>
|
||||
<ul className="space-y-6 relative z-10">
|
||||
{fairs.map((messe) => (
|
||||
<li key={messe.name} className="flex flex-col md:flex-row md:items-center justify-between border-b border-white/10 pb-6 group/item hover:bg-white/5 transition-colors p-4 -m-4 rounded-2xl">
|
||||
<div>
|
||||
<span className="text-primary-light font-bold text-sm uppercase tracking-widest block mb-1">{messe.type}</span>
|
||||
<span className="font-heading font-bold text-2xl">{messe.name}</span>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{fairs.map((messe, idx) => {
|
||||
const isLink = !!messe.url;
|
||||
const innerContent = (
|
||||
<>
|
||||
{/* Glass Background Elements */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-white/[0.03] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700" />
|
||||
<div className="absolute -top-32 -right-32 w-64 h-64 bg-primary/20 blur-[100px] rounded-full group-hover:bg-primary/30 transition-colors duration-700" />
|
||||
|
||||
<HoverShineOverlay shineColor="via-primary/10" />
|
||||
|
||||
<div className="relative z-10 flex-grow flex flex-col">
|
||||
<div className="flex justify-between items-start mb-6">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-white/[0.05] border border-white/10 text-xs font-bold uppercase tracking-wider text-primary-light backdrop-blur-sm">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-primary animate-pulse shadow-[0_0_8px_rgba(var(--color-primary),1)]" />
|
||||
{messe.type}
|
||||
</div>
|
||||
{isLink && (
|
||||
<div className="w-8 h-8 rounded-full bg-white/[0.05] border border-white/10 flex items-center justify-center text-white/40 group-hover:text-primary transition-colors group-hover:bg-primary/10">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M7 17l9.2-9.2M17 17V7H7"/></svg>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h4 className="font-heading font-bold text-3xl text-white mb-8 group-hover:text-primary-light transition-colors leading-tight">
|
||||
{messe.name}
|
||||
</h4>
|
||||
|
||||
<div className="mt-auto space-y-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-8 h-8 rounded-full bg-white/[0.05] flex items-center justify-center text-primary shrink-0 mt-0.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-white/40 uppercase tracking-widest font-heading mb-1">Datum</p>
|
||||
<p className="text-white/90 font-medium">{messe.date}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{messe.location && (
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-8 h-8 rounded-full bg-white/[0.05] flex items-center justify-center text-primary shrink-0 mt-0.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-white/40 uppercase tracking-widest font-heading mb-1">Location</p>
|
||||
<p className="text-white/90 font-medium">{messe.location}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{messe.booth && (
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-8 h-8 rounded-full bg-white/[0.05] flex items-center justify-center text-primary shrink-0 mt-0.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-white/40 uppercase tracking-widest font-heading mb-1">Stand</p>
|
||||
<p className="text-primary-light font-bold text-sm">{messe.booth}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
const baseClasses = "group relative bg-[#050B14] rounded-[2rem] p-8 overflow-hidden border border-white/5 shadow-2xl transition-all duration-500 hover:scale-[1.02] hover:shadow-primary/10 flex flex-col h-full";
|
||||
|
||||
if (isLink) {
|
||||
return (
|
||||
<a key={`fair-${idx}`} href={messe.url} target="_blank" rel="noopener noreferrer" className={`${baseClasses} cursor-pointer`}>
|
||||
{innerContent}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={`fair-${idx}`} className={baseClasses}>
|
||||
{innerContent}
|
||||
</div>
|
||||
<div className="mt-2 md:mt-0 text-right">
|
||||
<span className="text-white/60 font-medium block">{messe.date}</span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user