diff --git a/frontend/src/app/[locale]/(authenticated)/agents/[id]/page.tsx b/frontend/src/app/[locale]/(authenticated)/agents/[id]/page.tsx
index 6bcac09..31db268 100644
--- a/frontend/src/app/[locale]/(authenticated)/agents/[id]/page.tsx
+++ b/frontend/src/app/[locale]/(authenticated)/agents/[id]/page.tsx
@@ -8,7 +8,8 @@
'use client';
import { useCallback, useState } from 'react';
-import { useRouter, useParams } from 'next/navigation';
+import { useParams } from 'next/navigation';
+import { useRouter } from '@/lib/i18n/routing';
import { toast } from 'sonner';
import { AgentTypeDetail, AgentTypeForm } from '@/components/agents';
import {
diff --git a/frontend/src/app/[locale]/(authenticated)/agents/page.tsx b/frontend/src/app/[locale]/(authenticated)/agents/page.tsx
index f05efe2..72b2f7d 100644
--- a/frontend/src/app/[locale]/(authenticated)/agents/page.tsx
+++ b/frontend/src/app/[locale]/(authenticated)/agents/page.tsx
@@ -8,7 +8,7 @@
'use client';
import { useState, useCallback, useMemo } from 'react';
-import { useRouter } from 'next/navigation';
+import { useRouter } from '@/lib/i18n/routing';
import { toast } from 'sonner';
import { AgentTypeList } from '@/components/agents';
import { useAgentTypes } from '@/lib/api/hooks/useAgentTypes';
diff --git a/frontend/src/app/[locale]/(authenticated)/projects/[id]/issues/page.tsx b/frontend/src/app/[locale]/(authenticated)/projects/[id]/issues/page.tsx
index 0c83d98..afe230d 100644
--- a/frontend/src/app/[locale]/(authenticated)/projects/[id]/issues/page.tsx
+++ b/frontend/src/app/[locale]/(authenticated)/projects/[id]/issues/page.tsx
@@ -10,7 +10,7 @@
*/
import { useState, use } from 'react';
-import { useRouter } from 'next/navigation';
+import { useRouter } from '@/lib/i18n/routing';
import { Plus, Upload } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Skeleton } from '@/components/ui/skeleton';
@@ -25,7 +25,7 @@ interface ProjectIssuesPageProps {
}
export default function ProjectIssuesPage({ params }: ProjectIssuesPageProps) {
- const { locale, id: projectId } = use(params);
+ const { id: projectId } = use(params);
const router = useRouter();
// Filter state
@@ -49,7 +49,7 @@ export default function ProjectIssuesPage({ params }: ProjectIssuesPageProps) {
const { data, isLoading, error } = useIssues(projectId, filters, sort);
const handleIssueClick = (issueId: string) => {
- router.push(`/${locale}/projects/${projectId}/issues/${issueId}`);
+ router.push(`/projects/${projectId}/issues/${issueId}`);
};
const handleBulkChangeStatus = () => {
diff --git a/frontend/src/app/[locale]/(authenticated)/projects/new/page.tsx b/frontend/src/app/[locale]/(authenticated)/projects/new/page.tsx
index 95ae345..f7fe379 100644
--- a/frontend/src/app/[locale]/(authenticated)/projects/new/page.tsx
+++ b/frontend/src/app/[locale]/(authenticated)/projects/new/page.tsx
@@ -13,17 +13,11 @@ export const metadata: Metadata = {
description: 'Create a new Syndarix project with AI-powered agents',
};
-interface NewProjectPageProps {
- params: Promise<{ locale: string }>;
-}
-
-export default async function NewProjectPage({ params }: NewProjectPageProps) {
- const { locale } = await params;
-
+export default function NewProjectPage() {
return (
);
diff --git a/frontend/src/app/[locale]/(authenticated)/projects/page.tsx b/frontend/src/app/[locale]/(authenticated)/projects/page.tsx
index 4f259b7..a537af2 100644
--- a/frontend/src/app/[locale]/(authenticated)/projects/page.tsx
+++ b/frontend/src/app/[locale]/(authenticated)/projects/page.tsx
@@ -10,7 +10,7 @@
'use client';
import { useState, useCallback, useMemo } from 'react';
-import { useRouter } from 'next/navigation';
+import { useRouter } from '@/lib/i18n/routing';
import { toast } from 'sonner';
import { Plus } from 'lucide-react';
import { Button } from '@/components/ui/button';
diff --git a/frontend/src/components/projects/ProjectDashboard.tsx b/frontend/src/components/projects/ProjectDashboard.tsx
index 9f217a8..36f3ed5 100644
--- a/frontend/src/components/projects/ProjectDashboard.tsx
+++ b/frontend/src/components/projects/ProjectDashboard.tsx
@@ -10,7 +10,7 @@
'use client';
import { useCallback, useMemo, useState } from 'react';
-import { useRouter } from 'next/navigation';
+import { useRouter } from '@/lib/i18n/routing';
import { AlertCircle, RefreshCw } from 'lucide-react';
import { cn } from '@/lib/utils';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
diff --git a/frontend/src/components/projects/wizard/ProjectWizard.tsx b/frontend/src/components/projects/wizard/ProjectWizard.tsx
index 6bf6549..39570c9 100644
--- a/frontend/src/components/projects/wizard/ProjectWizard.tsx
+++ b/frontend/src/components/projects/wizard/ProjectWizard.tsx
@@ -8,7 +8,7 @@
*/
import { useState } from 'react';
-import { useRouter } from 'next/navigation';
+import { useRouter } from '@/lib/i18n/routing';
import { ArrowLeft, ArrowRight, Check, CheckCircle2, Loader2 } from 'lucide-react';
import { useMutation } from '@tanstack/react-query';
@@ -49,11 +49,10 @@ interface ProjectResponse {
}
interface ProjectWizardProps {
- locale: string;
className?: string;
}
-export function ProjectWizard({ locale, className }: ProjectWizardProps) {
+export function ProjectWizard({ className }: ProjectWizardProps) {
const router = useRouter();
const [isCreated, setIsCreated] = useState(false);
@@ -106,9 +105,9 @@ export function ProjectWizard({ locale, className }: ProjectWizardProps) {
const handleGoToProject = () => {
// Navigate to project dashboard - using slug from successful creation
if (createProjectMutation.data) {
- router.push(`/${locale}/projects/${createProjectMutation.data.slug}`);
+ router.push(`/projects/${createProjectMutation.data.slug}`);
} else {
- router.push(`/${locale}/projects`);
+ router.push(`/projects`);
}
};
diff --git a/frontend/tests/components/projects/ProjectDashboard.test.tsx b/frontend/tests/components/projects/ProjectDashboard.test.tsx
index 7ec2efc..6ed8464 100644
--- a/frontend/tests/components/projects/ProjectDashboard.test.tsx
+++ b/frontend/tests/components/projects/ProjectDashboard.test.tsx
@@ -92,18 +92,8 @@ jest.mock('@/lib/hooks/useProjectEvents', () => ({
useProjectEvents: jest.fn(() => mockUseProjectEventsResult),
}));
-// Mock next/navigation
-const mockPush = jest.fn();
-jest.mock('next/navigation', () => ({
- useRouter: () => ({
- push: mockPush,
- back: jest.fn(),
- forward: jest.fn(),
- refresh: jest.fn(),
- replace: jest.fn(),
- prefetch: jest.fn(),
- }),
-}));
+// Import mock from next-intl/navigation mock (used by @/lib/i18n/routing)
+import { mockPush } from 'next-intl/navigation';
describe('ProjectDashboard', () => {
const projectId = 'test-project-123';
diff --git a/frontend/tests/components/projects/wizard/ProjectWizard.test.tsx b/frontend/tests/components/projects/wizard/ProjectWizard.test.tsx
index 0b1ab9b..2c06ba5 100644
--- a/frontend/tests/components/projects/wizard/ProjectWizard.test.tsx
+++ b/frontend/tests/components/projects/wizard/ProjectWizard.test.tsx
@@ -77,13 +77,8 @@ jest.mock('@/components/projects/wizard/useWizardState', () => ({
})),
}));
-// Mock router
-const mockPush = jest.fn();
-jest.mock('next/navigation', () => ({
- useRouter: () => ({
- push: mockPush,
- }),
-}));
+// Import mock from next-intl/navigation mock (used by @/lib/i18n/routing)
+import { mockPush } from 'next-intl/navigation';
// Mock API client
const mockPost = jest.fn();
@@ -131,36 +126,36 @@ describe('ProjectWizard', () => {
describe('Rendering', () => {
it('renders the step indicator', () => {
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
expect(screen.getByTestId('step-indicator')).toBeInTheDocument();
});
it('renders BasicInfoStep on step 1', () => {
mockWizardState.step = 1;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
expect(screen.getByTestId('basic-info-step')).toBeInTheDocument();
});
it('renders ComplexityStep on step 2', () => {
mockWizardState.step = 2;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
expect(screen.getByTestId('complexity-step')).toBeInTheDocument();
});
it('renders AgentChatStep on step 5', () => {
mockWizardState.step = 5;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
expect(screen.getByTestId('agent-chat-step')).toBeInTheDocument();
});
it('renders ReviewStep on step 6', () => {
mockWizardState.step = 6;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
expect(screen.getByTestId('review-step')).toBeInTheDocument();
});
it('applies custom className', () => {
- const { container } = render(, {
+ const { container } = render(, {
wrapper: createWrapper(),
});
expect(container.firstChild).toHaveClass('custom-class');
@@ -171,7 +166,7 @@ describe('ProjectWizard', () => {
it('calls goNext when Next button is clicked', async () => {
const user = userEvent.setup();
mockWizardState.step = 1;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
await user.click(screen.getByRole('button', { name: /next/i }));
expect(mockGoNext).toHaveBeenCalled();
@@ -180,7 +175,7 @@ describe('ProjectWizard', () => {
it('calls goBack when Back button is clicked', async () => {
const user = userEvent.setup();
mockWizardState.step = 2;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
await user.click(screen.getByRole('button', { name: /back/i }));
expect(mockGoBack).toHaveBeenCalled();
@@ -188,21 +183,21 @@ describe('ProjectWizard', () => {
it('hides Back button on step 1', () => {
mockWizardState.step = 1;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
const backButton = screen.getByRole('button', { name: /back/i });
expect(backButton).toHaveClass('invisible');
});
it('shows Back button visible on step 2', () => {
mockWizardState.step = 2;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
const backButton = screen.getByRole('button', { name: /back/i });
expect(backButton).not.toHaveClass('invisible');
});
it('shows Create Project button on review step', () => {
mockWizardState.step = 6;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
expect(screen.getByRole('button', { name: /create project/i })).toBeInTheDocument();
});
});
@@ -211,7 +206,7 @@ describe('ProjectWizard', () => {
it('skips client mode step in script mode', () => {
mockWizardState.step = 3;
mockWizardState.complexity = 'script';
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
// ClientModeStep should not render for script mode
expect(screen.queryByTestId('client-mode-step')).not.toBeInTheDocument();
});
@@ -219,14 +214,14 @@ describe('ProjectWizard', () => {
it('skips autonomy step in script mode', () => {
mockWizardState.step = 4;
mockWizardState.complexity = 'script';
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
// AutonomyStep should not render for script mode
expect(screen.queryByTestId('autonomy-step')).not.toBeInTheDocument();
});
it('shows script mode indicator', () => {
mockWizardState.complexity = 'script';
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
expect(screen.getByText(/script mode/i)).toBeInTheDocument();
});
});
@@ -235,7 +230,7 @@ describe('ProjectWizard', () => {
it('shows success screen after creation', async () => {
const user = userEvent.setup();
mockWizardState.step = 6;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
await user.click(screen.getByRole('button', { name: /create project/i }));
@@ -247,7 +242,7 @@ describe('ProjectWizard', () => {
it('displays project name in success message', async () => {
const user = userEvent.setup();
mockWizardState.step = 6;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
await user.click(screen.getByRole('button', { name: /create project/i }));
@@ -259,7 +254,7 @@ describe('ProjectWizard', () => {
it('navigates to project dashboard on success', async () => {
const user = userEvent.setup();
mockWizardState.step = 6;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
await user.click(screen.getByRole('button', { name: /create project/i }));
@@ -270,13 +265,14 @@ describe('ProjectWizard', () => {
});
await user.click(screen.getByRole('button', { name: /go to project dashboard/i }));
- expect(mockPush).toHaveBeenCalledWith('/en/projects/test-project');
+ // Locale-aware router adds locale prefix automatically
+ expect(mockPush).toHaveBeenCalledWith('/projects/test-project');
});
it('allows creating another project', async () => {
const user = userEvent.setup();
mockWizardState.step = 6;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
await user.click(screen.getByRole('button', { name: /create project/i }));
@@ -294,7 +290,7 @@ describe('ProjectWizard', () => {
mockPost.mockRejectedValue(new Error('Network error'));
const user = userEvent.setup();
mockWizardState.step = 6;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
await user.click(screen.getByRole('button', { name: /create project/i }));
@@ -307,13 +303,13 @@ describe('ProjectWizard', () => {
describe('Button States', () => {
it('disables Next button when cannot proceed', () => {
mockWizardState.projectName = '';
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
expect(screen.getByRole('button', { name: /next/i })).toBeDisabled();
});
it('enables Next button when can proceed', () => {
mockWizardState.projectName = 'Valid Name';
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
expect(screen.getByRole('button', { name: /next/i })).not.toBeDisabled();
});
@@ -323,7 +319,7 @@ describe('ProjectWizard', () => {
);
const user = userEvent.setup();
mockWizardState.step = 6;
- render(, { wrapper: createWrapper() });
+ render(, { wrapper: createWrapper() });
await user.click(screen.getByRole('button', { name: /create project/i }));
expect(screen.getByText(/creating/i)).toBeInTheDocument();