---
applyTo: 'src/app/**'
---

# App Router & Routing Guidelines

## Route Structure

Project Zero uses App Router with dynamic segments:

```
src/app/[commerce]/[locale]/[currency]/
```

**Dynamic Parameters:**

- `[commerce]` - Commerce instance (configured in settings.js)
- `[locale]` - Language code (e.g., 'tr-TR', 'en-US')
- `[currency]` - Currency code (e.g., 'TRY', 'USD', 'EUR')

## File Structure

### Required Files

- `page.tsx` - Page component (required for each route)
- `layout.tsx` - Layout wrapper (optional, inherits from parent)
- `loading.tsx` - Loading UI (optional)
- `error.tsx` - Error boundary (optional)
- `not-found.tsx` - 404 page (optional, custom implementation in `pz-not-found/`)

### Route Organization

```
app/[commerce]/[locale]/[currency]/
├── layout.tsx                    # Root layout with withSegmentDefaults
├── template.tsx                  # Root template with client-side logic
├── client-root.tsx               # Client root component
├── page.tsx                      # Homepage with widget system
├── error.tsx                     # Error boundary with Sentry integration
├── [...prettyurl]/               # Pretty URL handler for legacy routes
│   └── page.tsx                  # Dynamic route resolver
├── category/                     # Category routes
│   ├── [pk]/                     # Category by ID
│   │   ├── page.tsx              # Category page
│   │   └── loading.tsx           # Category loading state
│   └── [...slug]/                # Category by slug (empty directory)
├── product/                      # Product routes
│   ├── [pk]/                     # Product by ID
│   │   └── page.tsx              # Product page with metadata
│   └── [slug]/                   # Product by slug (empty directory)
├── group-product/                # Group product routes
│   └── [pk]/
│       ├── page.tsx              # Group product page
│       └── loading.tsx           # Group product loading state
├── special-page/                 # Special page routes
│   └── [pk]/
│       ├── page.tsx              # Special page with widgets
│       └── loading.tsx           # Special page loading state
├── flat-page/                    # Flat/CMS page routes
│   └── [pk]/
│       ├── page.tsx              # Flat page with HTML content
│       └── loading.tsx           # Flat page loading state
├── landing-page/                 # Landing page routes
│   └── [pk]/
│       ├── page.tsx              # Landing page
│       └── loading.tsx           # Landing page loading state
├── account/                      # Account section
│   ├── layout.tsx                # Account layout with authentication
│   ├── page.tsx                  # Account dashboard
│   ├── orders/                   # Order management
│   │   └── [id]/                 # Order detail
│   │       ├── layout.tsx        # Order layout
│   │       ├── page.tsx          # Order details
│   │       └── cancellation/     # Order cancellation
│   ├── profile/page.tsx          # User profile
│   ├── address/page.tsx          # Address management
│   ├── change-email/page.tsx     # Email change
│   ├── change-password/page.tsx  # Password change
│   ├── contact/page.tsx          # Contact form
│   ├── coupons/page.tsx          # Coupon management
│   ├── email-verification/page.tsx # Email verification
│   ├── faq/page.tsx              # FAQ page
│   ├── favourite-products/page.tsx # Wishlist
│   └── my-quotations/page.tsx    # Quotations
├── basket/                       # Shopping basket
│   └── page.tsx                  # Basket page
├── basket-b2b/                   # B2B basket
│   └── page.tsx                  # B2B basket page
├── orders/                       # Order flow
│   ├── checkout/page.tsx         # Checkout process
│   └── completed/[token]/        # Order completion
│       ├── layout.tsx            # Completion layout
│       └── page.tsx              # Completion page
├── auth/                         # Authentication
│   ├── page.tsx                  # Login page
│   └── oauth-login/page.tsx      # OAuth login
├── users/                        # User management
│   ├── password/reset/page.tsx   # Password reset
│   ├── email-set-primary/[[...id]]/page.tsx # Primary email
│   ├── registration/account-confirm-email/[[...id]]/page.tsx # Email confirmation
│   └── reset/[[...id]]/page.tsx  # Account reset
├── list/page.tsx                 # Product listing/search
├── contact-us/page.tsx           # Contact page
├── anonymous-tracking/page.tsx   # Anonymous order tracking
├── address/stores/page.tsx       # Store locator
├── forms/[pk]/generate/page.tsx  # Form generation
├── pz-not-found/page.tsx         # Custom 404 page
└── xml-sitemap/                  # Sitemap generation
    ├── route.ts                  # Main sitemap
    └── [node]/route.ts           # Node-specific sitemap
```

## Route Parameters

### Server Components

```tsx
import { PageProps } from '@akinon/next/types';

// For pages with additional parameters (e.g., pk for product/category)
export default function Page({
  params,
  searchParams
}: PageProps<{ pk: number }>) {
  const { commerce, locale, currency, pk } = params;
  // Component logic
}

// For pages with slug parameters
export default function Page({
  params,
  searchParams
}: PageProps<{ slug: string }>) {
  const { commerce, locale, currency, slug } = params;
  // Component logic
}

// For pages with prettyurl parameters
export default function Page({ params }: PageProps) {
  const { commerce, locale, currency, prettyurl } = params;
  // Component logic
}
```

**Note:** The `PageProps` interface from `@akinon/next/types` automatically includes:

- `params: { locale: string; currency: string } & T` (where T is your custom params)
- `searchParams: URLSearchParams`

### Client Components

```tsx
'use client';
import { useParams, useSearchParams } from 'next/navigation';

export default function ClientComponent() {
  const params = useParams();
  const searchParams = useSearchParams();

  const { commerce, locale, currency } = params;
  // Component logic
}
```

## Route Constants

Route patterns are defined in `src/routes/index.ts`. Use these constants instead of hardcoding URLs:

```tsx
import { ROUTES } from '@theme/routes';

// General routes
ROUTES.HOME; // '/'
ROUTES.BASKET; // '/baskets/basket'
ROUTES.LIST; // '/list'

// Authentication routes
ROUTES.AUTH; // '/users/auth'
ROUTES.FORGOT_PASSWORD; // '/users/password/reset'
ROUTES.EMAIL_SET_PRIMARY; // '/users/email-set-primary/.+'
ROUTES.CONFIRM_EMAIL; // '/users/registration/account-confirm-email/.+'

// Account routes
ROUTES.ACCOUNT; // '/account'
ROUTES.ACCOUNT_ADDRESS; // '/account/address'
ROUTES.ACCOUNT_CHANGE_EMAIL; // '/account/change-email'
ROUTES.ACCOUNT_CHANGE_PASSWORD; // '/account/change-password'
ROUTES.ACCOUNT_CONTACT; // '/account/contact'
ROUTES.ACCOUNT_COUPONS; // '/account/coupons'
ROUTES.ACCOUNT_FAQ; // '/account/faq'
ROUTES.ACCOUNT_ORDERS; // '/users/orders'
ROUTES.ACCOUNT_PROFILE; // '/account/profile'
ROUTES.ACCOUNT_WISHLIST; // '/account/favourite-products/'
ROUTES.ANONYMOUS_TRACKING; // '/anonymous-tracking'

// Order routes
ROUTES.CHECKOUT; // '/orders/checkout'
ROUTES.CHECKOUT_COMPLETED; // '/orders/completed'

// Flat page routes
ROUTES.CONTACT_US; // '/contact-us'
```

## Middleware Integration

The `middleware.ts` file with `withPzDefault` wrapper handles:

- Parameter validation
- Commerce/locale/currency resolution
- Authentication checks
- Redirects and rewrites

**Critical:** Never remove `withPzDefault` - it's essential for routing functionality.

```tsx
// middleware.ts
import { withPzDefault } from '@akinon/next/middlewares';
import { NextMiddleware, NextResponse } from 'next/server';

export const config = {
  matcher: [
    '/((?!api|_next|[\\w-\\/*]+\\.\\w+).*)',
    '/(.*sitemap\\.xml)',
    '/(.+\\.)(html|htm|aspx|asp|php)',
    '/(.*orders\\/checkout-with-token.*)'
  ]
};

const middleware: NextMiddleware = () => {
  return NextResponse.next();
};

export default withPzDefault(middleware);
```

## Pretty URL System

The `[...prettyurl]` route handles legacy URL patterns and dynamic routing:

```tsx
// [...prettyurl]/page.tsx
export default async function Page({ params }: PageProps) {
  const { prettyurl } = params;
  const pageSlug = prettyurl
    .filter((x) => !x.startsWith('searchparams'))
    .join('/');

  // Resolves legacy URLs to current route structure
  // Handles: /urun/product-name -> /product/[pk]
  // Handles: /kategori/category-name -> /category/[pk]
  // etc.
}
```

## Dynamic Routes

### Product Pages by ID

```tsx
// product/[pk]/page.tsx
export async function generateMetadata({
  params,
  searchParams
}: PageProps<{ pk: number }>) {
  // Generate dynamic metadata for SEO
}

export default function ProductPage({
  params,
  searchParams
}: PageProps<{ pk: number }>) {
  const { pk } = params;
  // Product logic
}
```

### Category Pages by ID

```tsx
// category/[pk]/page.tsx
export default function CategoryPage({
  params,
  searchParams
}: PageProps<{ pk: number }>) {
  const { pk } = params;
  // Category logic
}
```

### Special Pages

```tsx
// special-page/[pk]/page.tsx
export default function SpecialPage({
  params,
  searchParams
}: PageProps<{ pk: number }>) {
  const { pk } = params;
  // Special page with widgets
}
```

### Flat/CMS Pages

```tsx
// flat-page/[pk]/page.tsx
export default function FlatPage({ params }: PageProps<{ pk: number }>) {
  const { pk } = params;
  // CMS content rendering
}
```

## Navigation

### Link Components

```tsx
import Link from 'next/link';

export default function Navigation() {
  return <Link href="/account">Account</Link>;
}
```

### Programmatic Navigation

```tsx
'use client';
import { useRouter } from 'next/navigation';

export default function Component() {
  const router = useRouter();

  const handleNavigate = () => {
    router.push('/account');
  };

  return <button onClick={handleNavigate}>Go to Account</button>;
}
```

## Error Handling

### Error Boundaries

```tsx
// error.tsx
'use client';

import { useSentryUncaughtErrors } from '@akinon/next/hooks';
import PzErrorPage from '@akinon/next/views/error-page';

export default function ErrorPage({
  error,
  reset
}: {
  error: Error & { digest?: string; isServerError?: boolean };
  reset: () => void;
}) {
  // DO NOT REMOVE THIS LINE TO REPORT UNCAUGHT ERRORS TO SENTRY
  useSentryUncaughtErrors(error);

  return <PzErrorPage error={error} reset={reset} />;
}
```

### Not Found Pages

```tsx
// pz-not-found/page.tsx
'use client';

import React from 'react';
import { Button, Link } from '@theme/components';
import { useLocalization } from '@akinon/next/hooks';

const NotFound = () => {
  const { t } = useLocalization();

  return (
    <div className="py-6 flex flex-col items-center justify-center">
      <div className="text-8xl font-bold">404</div>
      <h1 className="text-4xl font-bold mb-4">{t('not_found.title')}</h1>
      <p className="text-lg mb-6">{t('not_found.sub_title')}</p>
      <Link href={'/'}>
        <Button className="h-auto mt-4 text-base py-3 px-6">
          {t('not_found.button')}
        </Button>
      </Link>
    </div>
  );
};

export default NotFound;
```

## Loading States

```tsx
// loading.tsx
import { Skeleton, SkeletonWrapper } from 'components';

export default function Loading() {
  return (
    <div className="container p-4 mx-auto lg:px-0 lg:my-4">
      <SkeletonWrapper className="md:mb-7">
        <Skeleton className="w-[17.25rem] h-4 lg:w-64" />
      </SkeletonWrapper>

      <div className="w-full flex gap-8">
        <div className="hidden lg:block">
          <SkeletonWrapper className="w-[17.25rem] h-[650px] shrink-0">
            <Skeleton className="w-full h-full" />
          </SkeletonWrapper>
        </div>

        <div className="flex-1">
          <div className="grid gap-x-4 gap-y-7 grid-cols-2 md:grid-cols-3 lg:grid-cols-3">
            {Array(6)
              .fill(null)
              .map((_, index) => (
                <Skeleton
                  key={index}
                  className="w-full h-80 md:h-[26.813rem] lg:h-[35.875rem]"
                />
              ))}
          </div>
        </div>
      </div>
    </div>
  );
}
```

## Metadata Generation

### Static Metadata

```tsx
import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'Page Title',
  description: 'Page description'
};
```

### Dynamic Metadata

```tsx
import { PageProps, Metadata } from '@akinon/next/types';

export async function generateMetadata({
  params,
  searchParams
}: PageProps<{ pk: number }>): Promise<Metadata> {
  const { pk } = params;

  try {
    const data = await getProductData({ pk, searchParams });

    return {
      title: data.product.name,
      description: String(data.product.attributes.description),
      twitter: {
        title: data.product.name,
        description: String(data.product.attributes.description)
      },
      openGraph: {
        title: data.product.name,
        description: String(data.product.attributes.description),
        images: data.product.productimage_set?.map((item) => ({
          url: item.image
        }))
      }
    };
  } catch (error) {
    return {};
  }
}
```

## Component Patterns

### withSegmentDefaults Wrapper

All page components should use the `withSegmentDefaults` HOC:

```tsx
import { withSegmentDefaults } from '@akinon/next/hocs/server';

async function Page({ params, searchParams }: PageProps<{ pk: number }>) {
  // Page logic
}

export default withSegmentDefaults(Page, { segmentType: 'page' });
```

### Layout Components

```tsx
import { withSegmentDefaults } from '@akinon/next/hocs/server';
import { RootLayoutProps } from '@akinon/next/types';

async function RootLayout({
  params,
  locale,
  translations,
  children
}: RootLayoutProps) {
  return (
    <html lang={locale.isoCode}>
      <head />
      <body>{children}</body>
    </html>
  );
}

export default withSegmentDefaults(RootLayout, {
  segmentType: 'root-layout'
});
```

## API Routes

The application includes several API routes in the `app/api/` directory:

```tsx
// API route structure
app/api/
├── cache/route.ts              # Cache management
├── client/[...slug]/route.ts   # Client API proxy
├── form/[...id]/route.ts       # Form handling
├── logout/route.ts             # Logout endpoint
├── sentry/route.ts             # Sentry integration
├── web-vitals/route.ts         # Web vitals tracking
└── widgets/[slug]/             # Widget API
```

### API Route Example

```tsx
// app/api/logout/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  // Logout logic
  return NextResponse.json({ success: true });
}
```

## Route Testing

### Test Route Parameters

```tsx
import { render } from '@testing-library/react';
import ProductPage from '@/app/[commerce]/[locale]/[currency]/product/[pk]/page';

describe('Product Page', () => {
  it('handles route parameters correctly', () => {
    const mockParams = {
      commerce: 'tr',
      locale: 'tr-TR',
      currency: 'TRY',
      pk: 123
    };

    const mockSearchParams = new URLSearchParams();

    render(<ProductPage params={mockParams} searchParams={mockSearchParams} />);
    // Test assertions
  });
});
```

### Test Middleware

```tsx
import { withPzDefault } from '@akinon/next/middlewares';

describe('Middleware', () => {
  it('should wrap middleware with withPzDefault', () => {
    const middleware = () => NextResponse.next();
    const wrappedMiddleware = withPzDefault(middleware);

    expect(wrappedMiddleware).toBeDefined();
  });
});
```

## Best Practices

1. **Always use `withSegmentDefaults`** for page and layout components
2. **Use `PageProps<T>` interface** from `@akinon/next/types` for type safety
3. **Implement proper error boundaries** with Sentry integration
4. **Use route constants** from `@theme/routes` instead of hardcoded URLs
5. **Generate dynamic metadata** for SEO optimization
6. **Implement loading states** with skeleton components
7. **Use the pretty URL system** for legacy URL support
8. **Follow the middleware pattern** with `withPzDefault` wrapper

This routing structure supports the multi-commerce, internationalized architecture of Project Zero applications with comprehensive error handling, SEO optimization, and legacy URL support.
