Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5006163ddf | |||
| 35fb31249a | |||
| 120ba0488c | |||
| ffb637e05c | |||
| b97de267d9 |
@@ -149,7 +149,7 @@ export function TeamGrid({ members }: TeamGridProps) {
|
|||||||
className="group flex flex-col bg-white rounded-[2rem] border border-neutral-100 shadow-[0_8px_30px_rgb(0,0,0,0.04)] hover:shadow-[0_20px_40px_rgba(238,114,3,0.1)] transition-all duration-500 hover:-translate-y-2 overflow-hidden"
|
className="group flex flex-col bg-white rounded-[2rem] border border-neutral-100 shadow-[0_8px_30px_rgb(0,0,0,0.04)] hover:shadow-[0_20px_40px_rgba(238,114,3,0.1)] transition-all duration-500 hover:-translate-y-2 overflow-hidden"
|
||||||
>
|
>
|
||||||
{/* Card Banner with Signature E-TIB LogoArcs Design Element */}
|
{/* Card Banner with Signature E-TIB LogoArcs Design Element */}
|
||||||
<div className="h-36 bg-neutral-50 relative border-b border-neutral-100 overflow-hidden select-none">
|
<div className="h-44 sm:h-52 md:h-36 bg-neutral-50 relative border-b border-neutral-100 overflow-hidden select-none">
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-neutral-100/90 via-neutral-50 to-orange-50/20" />
|
<div className="absolute inset-0 bg-gradient-to-br from-neutral-100/90 via-neutral-50 to-orange-50/20" />
|
||||||
|
|
||||||
{/* Signature E-TIB Logo Arcs Design Element */}
|
{/* Signature E-TIB Logo Arcs Design Element */}
|
||||||
@@ -172,16 +172,18 @@ export function TeamGrid({ members }: TeamGridProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="px-6 sm:px-8 pb-8 relative flex-grow flex flex-col items-center md:items-start text-center md:text-left">
|
<div className="px-6 sm:px-8 pb-8 relative flex-grow flex flex-col items-center md:items-start text-center md:text-left">
|
||||||
{/* Overlapping Profile Picture with Shared Layout Animation (Centered & Larger on Mobile) */}
|
{/* Overlapping Profile Picture (Extra Large Centered on Mobile, Lightbox modal active only on Desktop) */}
|
||||||
<m.div
|
<m.div
|
||||||
layoutId={`team-avatar-${member.id}`}
|
layoutId={`team-avatar-${member.id}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPage([0, 0]);
|
if (typeof window !== 'undefined' && window.innerWidth >= 768) {
|
||||||
setSelectedMember(member);
|
setPage([0, 0]);
|
||||||
|
setSelectedMember(member);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
onMouseEnter={() => handlePreload(imageUrl)}
|
onMouseEnter={() => handlePreload(imageUrl)}
|
||||||
onTouchStart={() => handlePreload(imageUrl)}
|
onTouchStart={() => handlePreload(imageUrl)}
|
||||||
className="w-36 h-48 sm:w-40 sm:h-52 md:w-36 md:h-44 mx-auto md:mx-0 rounded-2xl overflow-hidden border-4 border-white shadow-xl bg-white relative -mt-20 md:-mt-24 mb-6 group-hover:-translate-y-2 transition-transform duration-500 cursor-pointer group/avatar"
|
className="w-52 h-64 sm:w-60 sm:h-72 md:w-36 md:h-44 mx-auto md:mx-0 rounded-2xl overflow-hidden border-4 border-white shadow-xl bg-white relative -mt-28 md:-mt-24 mb-6 transition-transform duration-500 cursor-default md:cursor-pointer group/avatar"
|
||||||
>
|
>
|
||||||
{imageUrl ? (
|
{imageUrl ? (
|
||||||
<>
|
<>
|
||||||
@@ -189,11 +191,11 @@ export function TeamGrid({ members }: TeamGridProps) {
|
|||||||
src={imageUrl}
|
src={imageUrl}
|
||||||
alt={member.name}
|
alt={member.name}
|
||||||
fill
|
fill
|
||||||
sizes="(max-width: 768px) 160px, 144px"
|
sizes="(max-width: 768px) 300px, 144px"
|
||||||
className="object-cover object-[center_15%] transition-transform duration-500 group-hover/avatar:scale-105"
|
className="object-cover object-[center_15%] transition-transform duration-500 group-hover/avatar:scale-105"
|
||||||
/>
|
/>
|
||||||
{/* Zoom Icon Overlay on Hover */}
|
{/* Zoom Icon Overlay on Hover (Desktop only) */}
|
||||||
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover/avatar:opacity-100 transition-opacity duration-300 flex items-center justify-center text-white cursor-pointer">
|
<div className="hidden md:flex absolute inset-0 bg-black/40 opacity-0 group-hover/avatar:opacity-100 transition-opacity duration-300 items-center justify-center text-white cursor-pointer">
|
||||||
<svg className="w-8 h-8 drop-shadow-md" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
<svg className="w-8 h-8 drop-shadow-md" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
<circle cx="11" cy="11" r="8"></circle>
|
<circle cx="11" cy="11" r="8"></circle>
|
||||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||||
@@ -204,8 +206,8 @@ export function TeamGrid({ members }: TeamGridProps) {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="absolute inset-0 flex items-center justify-center text-neutral-300 bg-neutral-50 group-hover/avatar:bg-neutral-100 transition-colors">
|
<div className="absolute inset-0 flex items-center justify-center text-neutral-300 bg-neutral-50 group-hover/avatar:bg-neutral-100 transition-colors">
|
||||||
<svg className="w-14 h-14" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
|
<svg className="w-16 h-16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
|
||||||
<div className="absolute inset-0 bg-black/30 opacity-0 group-hover/avatar:opacity-100 transition-opacity duration-300 flex items-center justify-center text-white cursor-pointer">
|
<div className="hidden md:flex absolute inset-0 bg-black/30 opacity-0 group-hover/avatar:opacity-100 transition-opacity duration-300 items-center justify-center text-white cursor-pointer">
|
||||||
<svg className="w-8 h-8 drop-shadow-md" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
<svg className="w-8 h-8 drop-shadow-md" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
<circle cx="11" cy="11" r="8"></circle>
|
<circle cx="11" cy="11" r="8"></circle>
|
||||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||||
@@ -370,10 +372,10 @@ export function TeamGrid({ members }: TeamGridProps) {
|
|||||||
{/* Mobile Drag Indicator Bar */}
|
{/* Mobile Drag Indicator Bar */}
|
||||||
<div className="absolute top-3 left-1/2 -translate-x-1/2 z-30 w-12 h-1.5 rounded-full bg-white/50 backdrop-blur-sm pointer-events-none" />
|
<div className="absolute top-3 left-1/2 -translate-x-1/2 z-30 w-12 h-1.5 rounded-full bg-white/50 backdrop-blur-sm pointer-events-none" />
|
||||||
|
|
||||||
{/* Portrait Container - Tall Portrait Aspect Framing for Mobile */}
|
{/* Portrait Container - Immersive Extra Large Portrait Aspect Framing for Mobile */}
|
||||||
<m.div
|
<m.div
|
||||||
layoutId={`team-avatar-${selectedMember.id}`}
|
layoutId={`team-avatar-${selectedMember.id}`}
|
||||||
className="relative w-full h-[52vh] min-h-[380px] max-h-[520px] md:h-[460px] bg-neutral-900 overflow-hidden shrink-0"
|
className="relative w-full h-[60vh] min-h-[440px] max-h-[620px] md:h-[460px] bg-neutral-900 overflow-hidden shrink-0"
|
||||||
>
|
>
|
||||||
{selectedMember.image ? (
|
{selectedMember.image ? (
|
||||||
<Image
|
<Image
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ layout: "fullBleed"
|
|||||||
icon: "hdd",
|
icon: "hdd",
|
||||||
lists: [
|
lists: [
|
||||||
[
|
[
|
||||||
"Horizontalspülbohrverfahren (bis 250m)",
|
"Horizontalspülbohrverfahren (bis 400m)",
|
||||||
"Horizontalspülbohrverfahren (bis 400er Rohr)",
|
"Horizontalspülbohrverfahren (bis 400er Rohr)",
|
||||||
"Erdrakete (bis 15m DÖ-Länge)",
|
"Erdrakete (bis 15m DÖ-Länge)",
|
||||||
"Erdrakete (bis 160er Rohr)"
|
"Erdrakete (bis 160er Rohr)"
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ layout: "fullBleed"
|
|||||||
icon: "hdd",
|
icon: "hdd",
|
||||||
lists: [
|
lists: [
|
||||||
[
|
[
|
||||||
"Horizontal directional drilling (up to 250m)",
|
"Horizontal directional drilling (up to 400m)",
|
||||||
"Horizontal directional drilling (up to 400mm pipe)",
|
"Horizontal directional drilling (up to 400mm pipe)",
|
||||||
"Earth rocket (up to 15m crossing length)",
|
"Earth rocket (up to 15m crossing length)",
|
||||||
"Earth rocket (up to 160mm pipe)"
|
"Earth rocket (up to 160mm pipe)"
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ services:
|
|||||||
- "traefik.http.routers.${PROJECT_NAME:-klz}.middlewares=${AUTH_MIDDLEWARE:-etib-ratelimit,etib-forward,etib-compress}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}.middlewares=${AUTH_MIDDLEWARE:-etib-ratelimit,etib-forward,etib-compress}"
|
||||||
|
|
||||||
# Public Router – paths that bypass Gatekeeper auth (health, SEO, static assets, OG images)
|
# Public Router – paths that bypass Gatekeeper auth (health, SEO, static assets, OG images)
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.rule=(${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-e-tib.com}`) || Host(`staging.${TRAEFIK_HOST:-e-tib.com}`) || Host(`testing.${TRAEFIK_HOST:-e-tib.com}`)}) && PathRegexp(`^/([a-z]{2}/)?(health|login|gatekeeper|uploads|media|assets|robots\\.txt|manifest\\.webmanifest|sitemap(-[0-9]+)?\\.xml|(.*/)?api/og(/.*)?|(.*/)?opengraph-image.*)`)"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.rule=(${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-e-tib.com}`) || Host(`staging.${TRAEFIK_HOST:-e-tib.com}`) || Host(`testing.${TRAEFIK_HOST:-e-tib.com}`)}) && PathRegexp(`^/([a-z]{2}/)?(health|login|gatekeeper|uploads|media|assets|_next|robots\\.txt|manifest\\.webmanifest|sitemap(-[0-9]+)?\\.xml|(.*/)?api/og(/.*)?|(.*/)?opengraph-image.*)`)"
|
||||||
|
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.tls=${TRAEFIK_TLS:-false}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.tls=${TRAEFIK_TLS:-false}"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "e-tib-nextjs",
|
"name": "e-tib-nextjs",
|
||||||
"version": "2.4.52",
|
"version": "2.4.57",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@10.18.3",
|
"packageManager": "pnpm@10.18.3",
|
||||||
|
|||||||
11
proxy.ts
11
proxy.ts
@@ -7,11 +7,12 @@ export default createMiddleware({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
// Match all pathnames except for
|
// Match all pathnames except for:
|
||||||
// - /api (API routes)
|
// - /api (API routes)
|
||||||
// - /stats (Analytics proxy)
|
// - /stats (Analytics proxy)
|
||||||
// - /_next (Next.js internals)
|
// - /_next (Next.js internals & image optimizer)
|
||||||
// - /_static (inside /public)
|
// - /assets, /_static (static files in /public)
|
||||||
// - all root files inside /public (e.g. /favicon.ico)
|
// - all files with an extension (e.g. /favicon.ico, .JPG, .png, .webp, .svg)
|
||||||
matcher: ['/((?!api|stats|_next|assets|_static|_vercel|[\\w-]+\\.\\w+).*)']
|
matcher: ['/((?!api|stats|_next|assets|_static|_vercel|.*\\..*).*)']
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
21
tests/proxy-config.test.ts
Normal file
21
tests/proxy-config.test.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { config } from '../proxy';
|
||||||
|
|
||||||
|
describe('Proxy Middleware Config Matcher', () => {
|
||||||
|
const matcherRegex = new RegExp(`^${config.matcher[0]}`);
|
||||||
|
|
||||||
|
|
||||||
|
it('matches localized page routes for next-intl processing', () => {
|
||||||
|
expect(matcherRegex.test('/de')).toBe(true);
|
||||||
|
expect(matcherRegex.test('/en/contact')).toBe(true);
|
||||||
|
expect(matcherRegex.test('/karriere')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('excludes static assets with any file extension including uppercase .JPG and subdirectories', () => {
|
||||||
|
// These should NOT match the middleware (expect test to be false so middleware is bypassed)
|
||||||
|
expect(matcherRegex.test('/assets/photos/DJI_0048.JPG')).toBe(false);
|
||||||
|
expect(matcherRegex.test('/assets/videos/web/hero-kabelpflug-poster.jpg')).toBe(false);
|
||||||
|
expect(matcherRegex.test('/assets/logo-white.png')).toBe(false);
|
||||||
|
expect(matcherRegex.test('/_next/image')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user