Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 68 additions & 62 deletions frontend/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { ThemeProvider } from '@/components/theme/ThemeProvider';
import { LogoutButton } from '@/components/auth/logoutButton';
import { getCurrentUser } from '@/lib/auth';
import LanguageSwitcher from '@/components/shared/LanguageSwitcher';
import { headers } from 'next/headers';
import { Header as ShopHeader } from '@/components/shop/shop-header';

export const dynamic = 'force-dynamic';

Expand All @@ -27,6 +29,8 @@ export default async function LocaleLayout({

const messages = await getMessages();
const user = await getCurrentUser();
const scope = (await headers()).get('x-app-scope') ?? 'site';
const showAdminNavLink = process.env.NEXT_PUBLIC_ENABLE_ADMIN === 'true';

return (
<NextIntlClientProvider messages={messages}>
Expand All @@ -36,75 +40,77 @@ export default async function LocaleLayout({
enableSystem
disableTransitionOnChange
>
<header className="bg-white dark:bg-neutral-900 border-b border-gray-200 dark:border-neutral-800 shadow-sm sticky top-0 z-50 transition-colors">
<div className="max-w-5xl mx-auto flex items-center justify-between px-6 py-4">
<Link href="/" className="flex items-center gap-2">
<span className="text-xl font-bold text-blue-600 dark:text-blue-500">
DevLovers
</span>
</Link>

<nav className="flex items-center gap-6 text-gray-700 dark:text-gray-300 font-medium">
<Link
href="/q&a"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
Q&A
</Link>
<Link
href="/quiz/react-fundamentals"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
Quiz
</Link>
<Link
href="/leaderboard"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
Leaderboard
</Link>
<Link
href="/blog"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
Blog
</Link>
<Link
href="/about"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
About
</Link>
<Link
href="/contacts"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
Contacts
</Link>
<Link
href="/shop"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
Shop
{scope === 'shop' ? (
<ShopHeader showAdminLink={showAdminNavLink} />
) : (
<header className="bg-white dark:bg-neutral-900 border-b border-gray-200 dark:border-neutral-800 shadow-sm sticky top-0 z-50 transition-colors">
<div className="max-w-5xl mx-auto flex items-center justify-between px-6 py-4">
<Link href="/" className="flex items-center gap-2">
<span className="text-xl font-bold text-blue-600 dark:text-blue-500">
DevLovers
</span>
</Link>

{/* Auth actions */}
{!user ? (
<nav className="flex items-center gap-6 text-gray-700 dark:text-gray-300 font-medium">
<Link
href="/q&a"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
Q&A
</Link>
<Link
href="/quiz/react-fundamentals"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
Quiz
</Link>
<Link
href="/leaderboard"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
Leaderboard
</Link>
<Link
href="/blog"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
Blog
</Link>
<Link
href="/about"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
About
</Link>
<Link
href="/contacts"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
Contacts
</Link>
<Link
href="/login"
href="/shop"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
Log in
Shop
</Link>
) : (
<LogoutButton />
)}

{/* Language Switcher */}
<LanguageSwitcher />
</nav>
</div>
</header>
{!user ? (
<Link
href="/login"
className="hover:text-blue-600 dark:hover:text-blue-400 transition"
>
Log in
</Link>
) : (
<LogoutButton />
)}

<LanguageSwitcher />
</nav>
</div>
</header>
)}

<main className="mx-auto px-6 min-h-[80vh]">{children}</main>

Expand Down
9 changes: 2 additions & 7 deletions frontend/app/[locale]/shop/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
// app/shop/layout.tsx
import type React from "react"

import { ThemeProvider } from "@/components/shop/theme-provider"
import { CartProvider } from "@/components/shop/cart-provider"
import { Header } from "@/components/shop/shop-header"
import { Footer } from "@/components/shop/shop-footer"

import "./shop-theme.css"

const showAdminNavLink = process.env.NEXT_PUBLIC_ENABLE_ADMIN === "true"

export default function ShopLayout({ children }: { children: React.ReactNode }) {
return (
<div className="shop-scope min-h-screen">
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<CartProvider>
<div className="flex min-h-screen flex-col">
<Header showAdminLink={showAdminNavLink} />

<main className="flex-1">{children}</main>
<Footer />

</div>
</CartProvider>
</ThemeProvider>
Expand Down
28 changes: 22 additions & 6 deletions frontend/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,36 @@ function authMiddleware(req: NextRequest) {

return null;
}
function getScopeFromPathname(pathname: string): "shop" | "site" {
const pathnameWithoutLocale = pathname.replace(/^\/(uk|en|pl)(?=\/|$)/, "") || "/"
return pathnameWithoutLocale.startsWith("/shop") ? "shop" : "site"
}


export function middleware(req: NextRequest) {

const intlResponse = intlMiddleware(req);
const authResponse = authMiddleware(req)
if (authResponse) return authResponse

const intlResponse = intlMiddleware(req)
const scope = getScopeFromPathname(req.nextUrl.pathname)

const authResponse = authMiddleware(req);
const requestHeaders = new Headers(req.headers)
requestHeaders.set("x-app-scope", scope)

if (authResponse) {
return authResponse;

const isRewriteOrRedirect = intlResponse.headers.has("location") || intlResponse.headers.has("x-middleware-rewrite")
if (isRewriteOrRedirect) {
intlResponse.headers.set("x-app-scope", scope)
return intlResponse
}

return intlResponse;
return NextResponse.next({
Comment on lines +65 to +69

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve intl middleware response for locale headers

When the request isn’t rewritten you’re now returning NextResponse.next instead of the intlResponse from createIntlMiddleware, so locale headers (e.g., x-next-intl-locale) generated by next-intl never reach the app. Because i18n/request.ts falls back to the default language when requestLocale is missing, localized URLs like /en/... or /pl/... will render default-language messages unless the request happens to hit the rewrite/redirect branch.

Useful? React with 👍 / 👎.

request: { headers: requestHeaders },
})
}



export const config = {
matcher: ['/', '/(uk|en|pl)/:path*', '/((?!api|_next|.*\\..*).*)',],
};