Files
btpxpress-frontend/DEVELOPMENT.md
2025-10-01 01:39:07 +00:00

11 KiB

🛠️ GUIDE DE DÉVELOPPEMENT - BTPXPRESS FRONTEND

📋 Table des matières


🔧 Prérequis

Logiciels requis

Logiciel Version Vérification
Node.js 20.x LTS node --version
npm 10.x npm --version
VS Code Latest Recommandé

Extensions VS Code recommandées

  • ES7+ React/Redux/React-Native snippets
  • Prettier - Code formatter
  • ESLint
  • TypeScript Vue Plugin (Volar)
  • Tailwind CSS IntelliSense
  • Auto Rename Tag
  • Path Intellisense

📦 Installation

1. Cloner et installer

cd btpxpress/btpxpress-client
npm install

2. Configurer les variables d'environnement

Créer .env.local :

NEXT_PUBLIC_API_URL=http://localhost:8080/api/v1
NEXT_PUBLIC_KEYCLOAK_URL=http://localhost:8180
NEXT_PUBLIC_KEYCLOAK_REALM=btpxpress
NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=btpxpress-frontend

3. Lancer le serveur de développement

npm run dev

Ouvrir http://localhost:3000


⚙️ Configuration

next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  images: {
    domains: ['localhost'],
  },
  env: {
    API_URL: process.env.NEXT_PUBLIC_API_URL,
  },
}

module.exports = nextConfig

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "paths": {
      "@/*": ["./*"],
      "@/components/*": ["./components/*"],
      "@/services/*": ["./services/*"],
      "@/types/*": ["./types/*"],
      "@/hooks/*": ["./hooks/*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}

📁 Structure du projet

btpxpress-client/
├── app/                          # App Router (Next.js 15)
│   ├── (auth)/                   # Groupe de routes authentifiées
│   │   ├── layout.tsx            # Layout avec sidebar
│   │   ├── dashboard/
│   │   │   └── page.tsx          # /dashboard
│   │   ├── chantiers/
│   │   │   ├── page.tsx          # /chantiers (liste)
│   │   │   ├── [id]/
│   │   │   │   └── page.tsx      # /chantiers/[id] (détails)
│   │   │   └── nouveau/
│   │   │       └── page.tsx      # /chantiers/nouveau
│   │   └── ...
│   ├── login/
│   │   └── page.tsx              # /login
│   ├── layout.tsx                # Layout racine
│   ├── page.tsx                  # / (accueil)
│   └── globals.css               # Styles globaux
├── components/
│   ├── layout/
│   │   ├── Header.tsx
│   │   ├── Sidebar.tsx
│   │   └── Footer.tsx
│   ├── forms/
│   │   ├── ChantierForm.tsx
│   │   └── ClientForm.tsx
│   ├── tables/
│   │   └── DataTableWrapper.tsx
│   └── common/
│       ├── LoadingSpinner.tsx
│       └── ErrorMessage.tsx
├── services/
│   ├── api/
│   │   ├── chantierService.ts
│   │   ├── clientService.ts
│   │   └── apiClient.ts
│   ├── auth/
│   │   └── keycloakService.ts
│   └── utils/
│       └── formatters.ts
├── types/
│   ├── models/
│   │   ├── Chantier.ts
│   │   └── Client.ts
│   └── api/
│       └── responses.ts
├── hooks/
│   ├── useAuth.ts
│   ├── useChantiers.ts
│   └── useToast.ts
├── contexts/
│   ├── AuthContext.tsx
│   └── ThemeContext.tsx
└── public/
    ├── images/
    └── icons/

📝 Conventions de code

Nommage

Élément Convention Exemple
Composants PascalCase ChantierForm.tsx
Fichiers PascalCase (composants), camelCase (autres) Header.tsx, apiClient.ts
Variables camelCase const userName = ...
Constantes UPPER_SNAKE_CASE const API_URL = ...
Types/Interfaces PascalCase interface ChantierDTO
Hooks camelCase avec use useAuth()

Structure d'un composant

'use client'; // Si nécessaire (composant client)

import React from 'react';
import { Button } from 'primereact/button';
import type { ChantierDTO } from '@/types/models/Chantier';

interface ChantierFormProps {
  chantier?: ChantierDTO;
  onSubmit: (data: ChantierDTO) => void;
  onCancel: () => void;
}

export const ChantierForm: React.FC<ChantierFormProps> = ({
  chantier,
  onSubmit,
  onCancel,
}) => {
  // Hooks
  const [loading, setLoading] = React.useState(false);

  // Handlers
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);
    // ...
  };

  // Render
  return (
    <form onSubmit={handleSubmit}>
      {/* JSX */}
    </form>
  );
};

🔄 Workflow de développement

1. Créer une branche

git checkout -b feature/nom-de-la-fonctionnalite

2. Développer

  1. Créer le type TypeScript (types/models/)
  2. Créer le service API (services/api/)
  3. Créer le composant (components/)
  4. Créer la page (app/)
  5. Tester

3. Tester localement

npm run dev
npm run lint
npm test

4. Commit et Push

git add .
git commit -m "feat: ajout de la fonctionnalité X"
git push origin feature/nom-de-la-fonctionnalite

🧩 Composants

Composant fonctionnel avec TypeScript

import React from 'react';

interface Props {
  title: string;
  count: number;
  onIncrement: () => void;
}

export const Counter: React.FC<Props> = ({ title, count, onIncrement }) => {
  return (
    <div>
      <h2>{title}</h2>
      <p>Count: {count}</p>
      <button onClick={onIncrement}>Increment</button>
    </div>
  );
};

Composant avec PrimeReact

'use client';

import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { Button } from 'primereact/button';

export const ChantierTable = ({ chantiers }) => {
  const actionBodyTemplate = (rowData) => {
    return (
      <Button
        icon="pi pi-pencil"
        className="p-button-rounded p-button-text"
        onClick={() => handleEdit(rowData)}
      />
    );
  };

  return (
    <DataTable value={chantiers} paginator rows={10}>
      <Column field="nom" header="Nom" sortable />
      <Column field="code" header="Code" sortable />
      <Column field="statut" header="Statut" />
      <Column body={actionBodyTemplate} header="Actions" />
    </DataTable>
  );
};

🔌 Services API

Client API de base

// services/api/apiClient.ts
import axios from 'axios';

const apiClient = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
  headers: {
    'Content-Type': 'application/json',
  },
});

// Intercepteur pour ajouter le token
apiClient.interceptors.request.use((config) => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

export default apiClient;

Service spécifique

// services/api/chantierService.ts
import apiClient from './apiClient';
import type { ChantierDTO } from '@/types/models/Chantier';

export const chantierService = {
  getAll: async (): Promise<ChantierDTO[]> => {
    const response = await apiClient.get('/chantiers');
    return response.data;
  },

  getById: async (id: string): Promise<ChantierDTO> => {
    const response = await apiClient.get(`/chantiers/${id}`);
    return response.data;
  },

  create: async (data: ChantierDTO): Promise<ChantierDTO> => {
    const response = await apiClient.post('/chantiers', data);
    return response.data;
  },

  update: async (id: string, data: ChantierDTO): Promise<ChantierDTO> => {
    const response = await apiClient.put(`/chantiers/${id}`, data);
    return response.data;
  },

  delete: async (id: string): Promise<void> => {
    await apiClient.delete(`/chantiers/${id}`);
  },
};

🎨 Styling

PrimeReact Theme

// app/layout.tsx
import 'primereact/resources/themes/lara-light-blue/theme.css';
import 'primereact/resources/primereact.min.css';
import 'primeicons/primeicons.css';

CSS Modules

// components/Header.module.css
.header {
  background-color: #fff;
  padding: 1rem;
}

// components/Header.tsx
import styles from './Header.module.css';

export const Header = () => {
  return <header className={styles.header}>...</header>;
};

🐛 Debugging

React DevTools

Installer l'extension Chrome/Firefox : React Developer Tools

Console logs

console.log('Debug:', data);
console.error('Error:', error);
console.table(array);

Debugger

const handleClick = () => {
  debugger; // Point d'arrêt
  // ...
};

Bonnes pratiques

1. Typage strict

// ❌ Mauvais
const data: any = ...

// ✅ Bon
const data: ChantierDTO = ...

2. Gestion des erreurs

try {
  const data = await chantierService.getAll();
  setChantiers(data);
} catch (error) {
  console.error('Erreur:', error);
  toast.current?.show({
    severity: 'error',
    summary: 'Erreur',
    detail: 'Impossible de charger les chantiers',
  });
}

3. Loading states

const [loading, setLoading] = useState(false);

const loadData = async () => {
  setLoading(true);
  try {
    const data = await service.getData();
    setData(data);
  } finally {
    setLoading(false);
  }
};

Dernière mise à jour : 2025-09-30
Version : 1.0
Auteur : Équipe BTPXpress