diff --git a/frontend/README.md b/frontend/README.md
index e215bc4..c3a98de 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -1,36 +1,95 @@
-This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+# Frontend Implementation
-## Getting Started
+This document provides an overview of the frontend implementation for the "Application de Création de Groupes" project.
-First, run the development server:
+## Architecture
-```bash
-npm run dev
-# or
-yarn dev
-# or
-pnpm dev
-# or
-bun dev
-```
+The frontend is built with Next.js 15 using the App Router architecture. It follows a component-based approach with a clear separation of concerns:
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+- **app/**: Contains all the pages and layouts organized by route
+- **components/**: Reusable UI components
+- **lib/**: Utility functions, hooks, and services
-You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+## Authentication Flow
-This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+The application uses GitHub OAuth for authentication:
-## Learn More
+1. User clicks "Login with GitHub" on the login page
+2. User is redirected to GitHub for authorization
+3. GitHub redirects back to our callback page with an authorization code
+4. The callback page exchanges the code for an access token
+5. User information is stored in the AuthContext and localStorage
+6. User is redirected to the dashboard or the original page they were trying to access
-To learn more about Next.js, take a look at the following resources:
+### Authentication Components
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+- **AuthProvider**: Context provider that manages authentication state
+- **AuthLoading**: Component that displays a loading screen during authentication checks
+- **useAuth**: Hook to access authentication state and methods
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+## API Communication
-## Deploy on Vercel
+All API communication is centralized in the `lib/api.ts` file, which provides:
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+- A base `fetchAPI` function with error handling and authentication
+- Specific API modules for different resources (auth, projects, persons, tags, groups)
+- Type-safe methods for all API operations
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
+## Protected Routes
+
+All authenticated routes are protected by:
+
+1. **Middleware**: Redirects unauthenticated users to the login page
+2. **AuthLoading**: Shows a loading screen during authentication checks
+3. **AuthContext**: Provides user information and authentication methods
+
+## Layout Structure
+
+The application uses a nested layout structure:
+
+- **RootLayout**: Provides global styles and the AuthProvider
+- **DashboardLayout**: Provides the sidebar navigation and user interface for authenticated pages
+- **AdminLayout**: Provides the admin interface for admin-only pages
+
+## Components
+
+### UI Components
+
+The application uses ShadcnUI for UI components, which provides:
+
+- A consistent design system
+- Accessible components
+- Dark mode support
+
+### Custom Components
+
+- **dashboard-layout.tsx**: Main layout for authenticated pages
+- **auth-loading.tsx**: Loading component for authentication checks
+- **admin-layout.tsx**: Layout for admin pages
+
+## Future Development
+
+### Adding New Pages
+
+1. Create a new directory in the `app/` folder
+2. Create a `page.tsx` file with your page content
+3. Create a `layout.tsx` file that uses the appropriate layout and AuthLoading component
+
+### Adding New API Endpoints
+
+1. Add new methods to the appropriate API module in `lib/api.ts`
+2. Use the methods in your components with the `useEffect` hook or event handlers
+
+### Adding New Features
+
+1. Create new components in the `components/` folder
+2. Use the components in your pages
+3. Add new API methods if needed
+
+## Best Practices
+
+- Use the AuthContext for authentication-related operations
+- Use the API service for all API communication
+- Wrap authenticated pages with the AuthLoading component
+- Use TypeScript for type safety
+- Follow the component-based architecture
\ No newline at end of file
diff --git a/frontend/docs/RESPONSIVE_DESIGN.md b/frontend/docs/RESPONSIVE_DESIGN.md
new file mode 100644
index 0000000..6cea565
--- /dev/null
+++ b/frontend/docs/RESPONSIVE_DESIGN.md
@@ -0,0 +1,91 @@
+# Responsive Design Patterns
+
+This document outlines the responsive design patterns used in the application to ensure a consistent user experience across different devices and screen sizes.
+
+## Breakpoints
+
+The application uses the following breakpoints, based on Tailwind CSS defaults:
+
+- **sm**: 640px and up (small devices like large phones and small tablets)
+- **md**: 768px and up (medium devices like tablets)
+- **lg**: 1024px and up (large devices like desktops)
+- **xl**: 1280px and up (extra large devices)
+- **2xl**: 1536px and up (very large screens)
+
+## Viewport Configuration
+
+The application uses the following viewport configuration in the root layout:
+
+```tsx
+export const viewport: Viewport = {
+ width: "device-width",
+ initialScale: 1,
+ maximumScale: 1,
+ userScalable: true,
+};
+```
+
+This ensures proper scaling on mobile devices while allowing users to zoom if needed.
+
+## Layout Patterns
+
+### Responsive Container
+
+- Use `container` class with responsive padding: `px-4 md:px-6`
+- Example: `
`
+
+### Responsive Flexbox
+
+- Stack elements vertically on small screens, horizontally on larger screens
+- Example: `
`
+
+### Responsive Grid
+
+- Single column on small screens, multiple columns on larger screens
+- Example: `
`
+
+### Responsive Spacing
+
+- Less padding on small screens, more on larger screens
+- Example: `
`
+
+## Component Patterns
+
+### Responsive Typography
+
+- Smaller font sizes on mobile, larger on desktop
+- Example: ``
+
+### Responsive Buttons
+
+- Full-width on small screens, auto-width on larger screens
+- Example: ``
+
+### Responsive Tables
+
+- Card layout on small screens, table layout on larger screens
+- Hide less important columns on small screens
+- Add horizontal scrolling for tables that don't fit
+- Example: See the projects page implementation
+
+### Responsive Forms
+
+- Stack form actions on small screens, side-by-side on larger screens
+- Adjust button order for mobile-first experience
+- Example: ``
+
+### Responsive Navigation
+
+- Use a sidebar that collapses to an icon or off-canvas menu on small screens
+- Use a dropdown or hamburger menu for mobile navigation
+- Example: See the `Sidebar` component implementation
+
+## Best Practices
+
+1. **Mobile-First Approach**: Start with the mobile layout and progressively enhance for larger screens
+2. **Consistent Patterns**: Use the same responsive patterns throughout the application
+3. **Avoid Fixed Widths**: Use relative units (%, rem) and flexible layouts
+4. **Test on Real Devices**: Verify the responsive design on actual devices, not just browser emulation
+5. **Consider Touch Targets**: Make interactive elements large enough for touch (at least 44x44px)
+6. **Optimize Images**: Use responsive images with appropriate sizes for different devices
+7. **Performance**: Ensure the application performs well on mobile devices with potentially slower connections
\ No newline at end of file
diff --git a/frontend/lib/api.ts b/frontend/lib/api.ts
new file mode 100644
index 0000000..071e8cb
--- /dev/null
+++ b/frontend/lib/api.ts
@@ -0,0 +1,301 @@
+/**
+ * API Service
+ *
+ * This service centralizes all API communication with the backend.
+ * It provides methods for authentication, projects, persons, and tags.
+ */
+
+const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
+
+/**
+ * Base fetch function with error handling and authentication
+ */
+async function fetchAPI(endpoint: string, options: RequestInit = {}) {
+ // Set default headers
+ const headers: Record = {
+ 'Content-Type': 'application/json',
+ ...(options.headers as Record || {}),
+ };
+
+ // Get token from localStorage if available (client-side only)
+ if (typeof window !== 'undefined') {
+ const token = localStorage.getItem('auth_token');
+ if (token) {
+ headers['Authorization'] = `Bearer ${token}`;
+ }
+ }
+
+ // Prepare fetch options
+ const fetchOptions: RequestInit = {
+ ...options,
+ headers,
+ credentials: 'include', // Include cookies for session management
+ };
+
+ try {
+ const response = await fetch(`${API_URL}${endpoint}`, fetchOptions);
+
+ // Handle HTTP errors
+ if (!response.ok) {
+ const errorData = await response.json().catch(() => ({}));
+ throw new Error(errorData.message || `API error: ${response.status}`);
+ }
+
+ // Parse JSON response if content exists
+ const contentType = response.headers.get('content-type');
+ if (contentType && contentType.includes('application/json')) {
+ return await response.json();
+ }
+
+ return response;
+ } catch (error) {
+ console.error('API request failed:', error);
+ throw error;
+ }
+}
+
+/**
+ * Authentication API
+ */
+export const authAPI = {
+ /**
+ * Get GitHub OAuth URL
+ */
+ getGitHubOAuthUrl: async () => {
+ return fetchAPI('/auth/github', { method: 'GET' });
+ },
+
+ /**
+ * Exchange code for access token
+ */
+ githubCallback: async (code: string) => {
+ return fetchAPI('/auth/github/callback', {
+ method: 'POST',
+ body: JSON.stringify({ code }),
+ });
+ },
+
+ /**
+ * Logout user
+ */
+ logout: async () => {
+ return fetchAPI('/auth/logout', { method: 'POST' });
+ },
+
+ /**
+ * Get current user
+ */
+ getCurrentUser: async () => {
+ return fetchAPI('/auth/me', { method: 'GET' });
+ },
+};
+
+/**
+ * Projects API
+ */
+export const projectsAPI = {
+ /**
+ * Get all projects
+ */
+ getProjects: async () => {
+ return fetchAPI('/projects', { method: 'GET' });
+ },
+
+ /**
+ * Get project by ID
+ */
+ getProject: async (id: string) => {
+ return fetchAPI(`/projects/${id}`, { method: 'GET' });
+ },
+
+ /**
+ * Create new project
+ */
+ createProject: async (data: any) => {
+ return fetchAPI('/projects', {
+ method: 'POST',
+ body: JSON.stringify(data),
+ });
+ },
+
+ /**
+ * Update project
+ */
+ updateProject: async (id: string, data: any) => {
+ return fetchAPI(`/projects/${id}`, {
+ method: 'PATCH',
+ body: JSON.stringify(data),
+ });
+ },
+
+ /**
+ * Delete project
+ */
+ deleteProject: async (id: string) => {
+ return fetchAPI(`/projects/${id}`, { method: 'DELETE' });
+ },
+};
+
+/**
+ * Persons API
+ */
+export const personsAPI = {
+ /**
+ * Get all persons for a project
+ */
+ getPersons: async (projectId: string) => {
+ return fetchAPI(`/projects/${projectId}/persons`, { method: 'GET' });
+ },
+
+ /**
+ * Get person by ID
+ */
+ getPerson: async (id: string) => {
+ return fetchAPI(`/persons/${id}`, { method: 'GET' });
+ },
+
+ /**
+ * Create new person
+ */
+ createPerson: async (projectId: string, data: any) => {
+ return fetchAPI(`/projects/${projectId}/persons`, {
+ method: 'POST',
+ body: JSON.stringify(data),
+ });
+ },
+
+ /**
+ * Update person
+ */
+ updatePerson: async (id: string, data: any) => {
+ return fetchAPI(`/persons/${id}`, {
+ method: 'PATCH',
+ body: JSON.stringify(data),
+ });
+ },
+
+ /**
+ * Delete person
+ */
+ deletePerson: async (id: string) => {
+ return fetchAPI(`/persons/${id}`, { method: 'DELETE' });
+ },
+};
+
+/**
+ * Tags API
+ */
+export const tagsAPI = {
+ /**
+ * Get all tags
+ */
+ getTags: async () => {
+ return fetchAPI('/tags', { method: 'GET' });
+ },
+
+ /**
+ * Get tag by ID
+ */
+ getTag: async (id: string) => {
+ return fetchAPI(`/tags/${id}`, { method: 'GET' });
+ },
+
+ /**
+ * Create new tag
+ */
+ createTag: async (data: any) => {
+ return fetchAPI('/tags', {
+ method: 'POST',
+ body: JSON.stringify(data),
+ });
+ },
+
+ /**
+ * Update tag
+ */
+ updateTag: async (id: string, data: any) => {
+ return fetchAPI(`/tags/${id}`, {
+ method: 'PATCH',
+ body: JSON.stringify(data),
+ });
+ },
+
+ /**
+ * Delete tag
+ */
+ deleteTag: async (id: string) => {
+ return fetchAPI(`/tags/${id}`, { method: 'DELETE' });
+ },
+};
+
+/**
+ * Groups API
+ */
+export const groupsAPI = {
+ /**
+ * Get all groups for a project
+ */
+ getGroups: async (projectId: string) => {
+ return fetchAPI(`/projects/${projectId}/groups`, { method: 'GET' });
+ },
+
+ /**
+ * Get group by ID
+ */
+ getGroup: async (id: string) => {
+ return fetchAPI(`/groups/${id}`, { method: 'GET' });
+ },
+
+ /**
+ * Create new group
+ */
+ createGroup: async (projectId: string, data: any) => {
+ return fetchAPI(`/projects/${projectId}/groups`, {
+ method: 'POST',
+ body: JSON.stringify(data),
+ });
+ },
+
+ /**
+ * Update group
+ */
+ updateGroup: async (id: string, data: any) => {
+ return fetchAPI(`/groups/${id}`, {
+ method: 'PATCH',
+ body: JSON.stringify(data),
+ });
+ },
+
+ /**
+ * Delete group
+ */
+ deleteGroup: async (id: string) => {
+ return fetchAPI(`/groups/${id}`, { method: 'DELETE' });
+ },
+
+ /**
+ * Add person to group
+ */
+ addPersonToGroup: async (groupId: string, personId: string) => {
+ return fetchAPI(`/groups/${groupId}/persons/${personId}`, {
+ method: 'POST',
+ });
+ },
+
+ /**
+ * Remove person from group
+ */
+ removePersonFromGroup: async (groupId: string, personId: string) => {
+ return fetchAPI(`/groups/${groupId}/persons/${personId}`, {
+ method: 'DELETE',
+ });
+ },
+};
+
+export default {
+ auth: authAPI,
+ projects: projectsAPI,
+ persons: personsAPI,
+ tags: tagsAPI,
+ groups: groupsAPI,
+};
diff --git a/frontend/lib/auth-context.tsx b/frontend/lib/auth-context.tsx
new file mode 100644
index 0000000..d87228a
--- /dev/null
+++ b/frontend/lib/auth-context.tsx
@@ -0,0 +1,145 @@
+"use client";
+
+import { createContext, useContext, useEffect, useState, ReactNode } from "react";
+import { useRouter } from "next/navigation";
+import api from "./api";
+
+// Define the User type
+interface User {
+ id: string;
+ name: string;
+ avatar?: string;
+ role: string;
+}
+
+// Define the AuthContext type
+interface AuthContextType {
+ user: User | null;
+ isLoading: boolean;
+ isAuthenticated: boolean;
+ login: (code: string) => Promise;
+ logout: () => Promise;
+ checkAuth: () => Promise;
+}
+
+// Create the AuthContext
+const AuthContext = createContext(undefined);
+
+// Create a provider component
+export function AuthProvider({ children }: { children: ReactNode }) {
+ const [user, setUser] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const router = useRouter();
+
+ // Check if the user is authenticated on mount
+ useEffect(() => {
+ const initAuth = async () => {
+ setIsLoading(true);
+ try {
+ await checkAuth();
+ } catch (error) {
+ console.error("Auth initialization error:", error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ initAuth();
+ }, []);
+
+ // Check if the user is authenticated
+ const checkAuth = async (): Promise => {
+ try {
+ // Try to get the current user from the API
+ const userData = await api.auth.getCurrentUser();
+
+ if (userData) {
+ setUser(userData);
+
+ // Update localStorage with user data
+ localStorage.setItem('user_role', userData.role);
+ localStorage.setItem('user_name', userData.name);
+ if (userData.avatar) {
+ localStorage.setItem('user_avatar', userData.avatar);
+ }
+
+ return true;
+ }
+
+ return false;
+ } catch (error) {
+ console.error("Auth check error:", error);
+ setUser(null);
+ return false;
+ }
+ };
+
+ // Login function
+ const login = async (code: string): Promise => {
+ setIsLoading(true);
+ try {
+ const data = await api.auth.githubCallback(code);
+
+ if (data.user) {
+ setUser(data.user);
+
+ // Store user info in localStorage
+ localStorage.setItem('auth_token', data.accessToken);
+ localStorage.setItem('user_role', data.user.role);
+ localStorage.setItem('user_name', data.user.name);
+ if (data.user.avatar) {
+ localStorage.setItem('user_avatar', data.user.avatar);
+ }
+ }
+ } catch (error) {
+ console.error("Login error:", error);
+ throw error;
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ // Logout function
+ const logout = async (): Promise => {
+ setIsLoading(true);
+ try {
+ await api.auth.logout();
+
+ // Clear user state and localStorage
+ setUser(null);
+ localStorage.removeItem('auth_token');
+ localStorage.removeItem('user_role');
+ localStorage.removeItem('user_name');
+ localStorage.removeItem('user_avatar');
+
+ // Redirect to login page
+ router.push('/auth/login');
+ } catch (error) {
+ console.error("Logout error:", error);
+ throw error;
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ // Create the context value
+ const value = {
+ user,
+ isLoading,
+ isAuthenticated: !!user,
+ login,
+ logout,
+ checkAuth,
+ };
+
+ return {children} ;
+}
+
+// Create a hook to use the AuthContext
+export function useAuth() {
+ const context = useContext(AuthContext);
+ if (context === undefined) {
+ throw new Error("useAuth must be used within an AuthProvider");
+ }
+ return context;
+}
\ No newline at end of file
diff --git a/frontend/middleware.ts b/frontend/middleware.ts
new file mode 100644
index 0000000..f5e7390
--- /dev/null
+++ b/frontend/middleware.ts
@@ -0,0 +1,60 @@
+import { NextResponse } from 'next/server';
+import type { NextRequest } from 'next/server';
+
+// Define public routes that don't require authentication
+const publicRoutes = [
+ '/',
+ '/auth/login',
+ '/auth/callback',
+];
+
+// Define routes that require admin role
+const adminRoutes = [
+ '/admin',
+];
+
+export function middleware(request: NextRequest) {
+ const { pathname } = request.nextUrl;
+
+ // Allow access to public routes without authentication
+ if (publicRoutes.some(route => pathname === route || pathname.startsWith(`${route}/`))) {
+ return NextResponse.next();
+ }
+
+ // Get the auth token from cookies
+ const token = request.cookies.get('auth_token')?.value;
+ const userRole = request.cookies.get('user_role')?.value;
+
+ // If no token, redirect to login
+ if (!token) {
+ // Store the original URL to redirect back after login
+ const url = new URL('/auth/login', request.url);
+ url.searchParams.set('callbackUrl', pathname);
+ return NextResponse.redirect(url);
+ }
+
+ // Check if the route requires admin role
+ if (adminRoutes.some(route => pathname === route || pathname.startsWith(`${route}/`))) {
+ // If not admin role, redirect to dashboard
+ if (userRole !== 'ADMIN') {
+ return NextResponse.redirect(new URL('/dashboard', request.url));
+ }
+ }
+
+ return NextResponse.next();
+}
+
+// Configure the middleware to run on all routes except static files and api routes
+export const config = {
+ matcher: [
+ /*
+ * Match all request paths except for:
+ * 1. /api routes
+ * 2. /_next (Next.js internals)
+ * 3. /_static (static files)
+ * 4. /_vercel (Vercel internals)
+ * 5. /favicon.ico, /robots.txt, /sitemap.xml (common static files)
+ */
+ '/((?!api|_next|_static|_vercel|favicon.ico|robots.txt|sitemap.xml).*)',
+ ],
+};
diff --git a/frontend/package.json b/frontend/package.json
index e1dfa58..8443875 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -52,6 +52,7 @@
"react-resizable-panels": "^3.0.2",
"recharts": "^2.15.3",
"sonner": "^2.0.3",
+ "swr": "^2.3.3",
"tailwind-merge": "^3.3.0",
"vaul": "^1.1.2",
"zod": "^3.24.4"