fix(i18n): render consent banner inside NextIntl provider
Move ConsentBanner rendering into the locale layout so next-intl context is always available (prevents missing provider errors). fix(activity-feed): use dark styling for disabled state Avoid white disabled cards so the feed never appears as a white/transparent block after reload. test(e2e): assert nav text changes on locale switch Strengthen i18n test to verify translated labels. Co-authored-by: dennis <dennis@konkol.net>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { NextIntlClientProvider } from "next-intl";
|
||||
import { getMessages, setRequestLocale } from "next-intl/server";
|
||||
import React from "react";
|
||||
import ConsentBanner from "../components/ConsentBanner";
|
||||
|
||||
export default async function LocaleLayout({
|
||||
children,
|
||||
@@ -17,6 +18,7 @@ export default async function LocaleLayout({
|
||||
return (
|
||||
<NextIntlClientProvider locale={locale} messages={messages}>
|
||||
{children}
|
||||
<ConsentBanner />
|
||||
</NextIntlClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1490,20 +1490,20 @@ export default function ActivityFeed() {
|
||||
<motion.div
|
||||
initial={false}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
className="pointer-events-auto bg-white/90 backdrop-blur-2xl border border-white/60 rounded-2xl shadow-xl overflow-hidden w-full"
|
||||
className="pointer-events-auto bg-black/95 backdrop-blur-2xl border border-white/10 rounded-2xl shadow-2xl overflow-hidden w-full"
|
||||
>
|
||||
<div className="px-4 py-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<Activity size={18} className="text-stone-900" />
|
||||
<Activity size={18} className="text-white" />
|
||||
<div className="text-left">
|
||||
<h3 className="text-sm font-bold text-stone-900">Live Activity</h3>
|
||||
<p className="text-[10px] text-stone-500">Tracking disabled</p>
|
||||
<h3 className="text-sm font-bold text-white">Live Activity</h3>
|
||||
<p className="text-[10px] text-white/50">Tracking disabled</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleTracking}
|
||||
className="text-xs font-semibold px-3 py-1.5 rounded-full bg-stone-900 text-white hover:bg-stone-800 transition-colors"
|
||||
className="text-xs font-semibold px-3 py-1.5 rounded-full bg-white/10 text-white hover:bg-white/15 transition-colors border border-white/10"
|
||||
title="Enable activity tracking"
|
||||
>
|
||||
Enable
|
||||
@@ -1521,20 +1521,20 @@ export default function ActivityFeed() {
|
||||
<motion.div
|
||||
initial={false}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
className="pointer-events-auto bg-white/90 backdrop-blur-2xl border border-white/60 rounded-2xl shadow-xl overflow-hidden w-full"
|
||||
className="pointer-events-auto bg-black/95 backdrop-blur-2xl border border-white/10 rounded-2xl shadow-2xl overflow-hidden w-full"
|
||||
>
|
||||
<div className="px-4 py-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<Activity size={18} className="text-stone-900" />
|
||||
<Activity size={18} className="text-white" />
|
||||
<div className="text-left">
|
||||
<h3 className="text-sm font-bold text-stone-900">Live Activity</h3>
|
||||
<p className="text-[10px] text-stone-500">Tracking disabled</p>
|
||||
<h3 className="text-sm font-bold text-white">Live Activity</h3>
|
||||
<p className="text-[10px] text-white/50">Tracking disabled</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleTracking}
|
||||
className="text-xs font-semibold px-3 py-1.5 rounded-full bg-stone-900 text-white hover:bg-stone-800 transition-colors"
|
||||
className="text-xs font-semibold px-3 py-1.5 rounded-full bg-white/10 text-white hover:bg-white/15 transition-colors border border-white/10"
|
||||
title="Enable activity tracking"
|
||||
>
|
||||
Enable
|
||||
|
||||
@@ -7,7 +7,6 @@ import { ToastProvider } from "@/components/Toast";
|
||||
import ErrorBoundary from "@/components/ErrorBoundary";
|
||||
import { AnalyticsProvider } from "@/components/AnalyticsProvider";
|
||||
import { ConsentProvider, useConsent } from "./ConsentProvider";
|
||||
import ConsentBanner from "./ConsentBanner";
|
||||
|
||||
// Dynamic import with SSR disabled to avoid framer-motion issues
|
||||
const BackgroundBlobs = dynamic(() => import("@/components/BackgroundBlobs").catch(() => ({ default: () => null })), {
|
||||
@@ -105,7 +104,6 @@ function GatedProviders({
|
||||
<ToastProvider>
|
||||
{mounted && <BackgroundBlobs />}
|
||||
<div className="relative z-10">{children}</div>
|
||||
{mounted && !isAdminRoute && <ConsentBanner />}
|
||||
{mounted && !is404Page && !isAdminRoute && chatEnabled && <ChatWidget />}
|
||||
</ToastProvider>
|
||||
</ErrorBoundary>
|
||||
|
||||
@@ -7,8 +7,14 @@ test.describe("i18n routing", () => {
|
||||
// Buttons are "EN"/"DE" in the header
|
||||
const deButton = page.getByRole("button", { name: "DE" });
|
||||
if (await deButton.count()) {
|
||||
// Verify an EN label is present before switching (nav.home)
|
||||
await expect(page.getByRole("link", { name: "Home" })).toBeVisible();
|
||||
|
||||
await deButton.click();
|
||||
await expect(page).toHaveURL(/\/de(\/|$)/);
|
||||
|
||||
// Verify the nav label updates after switching
|
||||
await expect(page.getByRole("link", { name: "Start" })).toBeVisible();
|
||||
} else {
|
||||
test.skip();
|
||||
}
|
||||
|
||||
@@ -44,7 +44,8 @@ export default defineConfig({
|
||||
],
|
||||
|
||||
webServer: {
|
||||
command: 'npm run dev',
|
||||
// Use plain Next.js dev server for E2E (no Docker dependency)
|
||||
command: 'npm run dev:next',
|
||||
url: 'http://localhost:3000',
|
||||
reuseExistingServer: true, // Always reuse if server is running
|
||||
timeout: 120 * 1000,
|
||||
|
||||
Reference in New Issue
Block a user