# Composants et pages
Documentation [React](https://react.dev/)
Les composants sont les éléments de base d'un projet React. Chacun représente une partie de l'interface utilisateur, réutilisable et maintenable.
## Organisation des composants
### Structure recommandée
```
src/components/
├── app/ # Composants d'infrastructure
│ ├── Provider/
│ │ └── index.jsx
│ └── Router/
│ ├── index.jsx
│ └── Guards/
│ └── index.jsx
├── pages/ # Pages de l'application
│ ├── public/ # Pages sans authentification
│ │ ├── LoginPage/
│ │ └── WelcomePage/
│ ├── private/ # Pages authentifiées
│ │ ├── HomePage/
│ │ └── SettingsPage/
│ └── errors/ # Pages d'erreur
│ └── Error404Page/
├── forms/ # Composants de formulaire
│ ├── LoginForm/
│ └── ItemForm/
├── ui/ # Composants UI réutilisables
│ ├── Card/
│ ├── Modal/
│ └── Header/
└── layouts/ # Layouts de pages
├── MainLayout/
└── AuthLayout/
```
### Convention de nommage
* **Dossier par composant** : Chaque composant dans son propre dossier
* **index.jsx** : Fichier principal du composant
* **PascalCase** : Noms de composants en PascalCase
```
src/components/ui/Card/
├── index.jsx # Composant principal
├── Card.module.css # Styles (optionnel)
└── Card.test.jsx # Tests (optionnel)
```
## Créer un composant
### Composant simple
```
// src/components/ui/Card/index.jsx
export const Card = ({ children, className = '' }) => {
return (
{children}
);
};
```
### Composant avec props
```
// src/components/ui/Button/index.jsx
export const Button = ({
children,
variant = 'primary',
size = 'md',
loading = false,
disabled = false,
onClick,
type = 'button',
className = '',
}) => {
const variants = {
primary: 'bg-primary text-white hover:bg-primary-dark',
secondary: 'bg-secondary text-white hover:bg-secondary-dark',
outline: 'border-2 border-primary text-primary hover:bg-primary/10',
ghost: 'text-primary hover:bg-primary/10',
};
const sizes = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2',
lg: 'px-6 py-3 text-lg',
};
return (
{loading && (
⟳
)}
{children}
);
};
```
### Composant Input personnalisé
```
// src/components/form/Input/index.jsx
export const Input = (props) => {
const { label, id, error, ...inputProps } = props;
return (
{label && (
{label}
)}
{error && (
{error}
)}
);
};
```
## Structure d'une page
### Page simple
```
// src/components/pages/private/HomePage/index.jsx
import { useGlobalStates, useNavigation } from '@cap-rel/smartcommon';
export const HomePage = () => {
const navigate = useNavigation();
const [session] = useGlobalStates('session');
return (
{/* Header */}
{/* Content */}
Bienvenue {session?.user?.name}
{/* Navigation */}
navigate('/')}>Accueil
navigate('/settings')}>Paramètres
);
};
```
### Page avec chargement de données
```
// src/components/pages/private/ItemsPage/index.jsx
import { useApi, useGlobalStates, useNavigation } from '@cap-rel/smartcommon';
import { useEffect, useState } from 'react';
import { Card } from '../../ui/Card';
export const ItemsPage = () => {
const api = useApi();
const navigate = useNavigation();
const [items, setItems] = useGlobalStates('items');
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchItems = async () => {
setLoading(true);
setError(null);
const response = await api.private.get('items');
if (response.success) {
setItems(response.data);
} else {
setError(response.error);
}
setLoading(false);
};
fetchItems();
}, []);
if (loading) {
return (
⟳
);
}
if (error) {
return (
Erreur: {error}
);
}
return (
Mes items
{items.map((item) => (
navigate(`/items/${item.id}`)}
className="cursor-pointer hover:shadow-lg transition-shadow"
>
{item.label}
{item.description}
))}
);
};
```
## Utiliser les layouts
### Créer un layout
```
// src/components/layouts/MainLayout/index.jsx
import { useNavigation } from '@cap-rel/smartcommon';
export const MainLayout = ({ children, title, showBack = true }) => {
const navigate = useNavigation();
return (
{/* Header */}
{showBack && (
navigate(-1)} className="text-primary">
← Retour
)}
{title}
{/* Content */}
{children}
{/* Bottom navigation */}
navigate('/')}>🏠
navigate('/search')}>🔍
navigate('/profile')}>👤
);
};
```
### Utiliser un layout
```
// src/components/pages/private/SettingsPage/index.jsx
import { MainLayout } from '../../layouts/MainLayout';
import { useGlobalStates, useIntl } from '@cap-rel/smartcommon';
export const SettingsPage = () => {
const { t, lng, setLng } = useIntl();
const [theme, setTheme] = useGlobalStates('settings.theme');
return (
{/* Langue */}
{t('settings.language')}
setLng(e.target.value)}
className="w-full p-2 border rounded"
>
Français
English
{/* Thème */}
{t('settings.theme')}
setTheme('light')}
className={`p-4 rounded ${theme === 'light' ? 'ring-2 ring-primary' : ''}`}
>
☀️ Clair
setTheme('dark')}
className={`p-4 rounded ${theme === 'dark' ? 'ring-2 ring-primary' : ''}`}
>
🌙 Sombre
);
};
```
## Patterns courants
### Formulaire avec validation Zod
```
import { useForm, useApi, useNavigation } from '@cap-rel/smartcommon';
import { Form, Input, Button } from '@cap-rel/smartcommon';
import { z } from 'zod';
const schema = z.object({
name: z.string().min(2, "Minimum 2 caractères"),
email: z.string().email("Email invalide"),
});
const ContactForm = () => {
const api = useApi();
const navigate = useNavigation();
const form = useForm({ schema });
const handleSubmit = async (data) => {
const response = await api.private.post('contacts', { json: data });
if (response.success) {
navigate('/contacts');
}
};
return (
);
};
```
### Modal/Drawer avec animation
```
import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
const Modal = ({ isOpen, onClose, children }) => {
return (
{isOpen && (
<>
{/* Backdrop */}
{/* Content */}
{children}
>
)}
);
};
```
### Liste avec état vide
```
const ItemsList = ({ items }) => {
if (items.length === 0) {
return (
📭
Aucun élément pour le moment
);
}
return (
{items.map(item => (
))}
);
};
```
## Utiliser SmartCommon
Préférez les composants SmartCommon aux composants customs quand c'est possible :
```
import {
Form,
Input,
Select,
Checkbox,
Button,
Card,
Modal,
} from '@cap-rel/smartcommon';
```
Voir [[smartcommon|SmartCommon]] pour la liste complète.
## Voir aussi
* [[smartcommon|SmartCommon]] - Composants prêts à l'emploi
* [[hooks|Hooks]] - Hooks disponibles
* [[routage|Routage]] - Navigation entre pages
* [[animations|Animations]] - Transitions de pages