# 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 ( ); }; ``` ### Composant Input personnalisé ``` // src/components/form/Input/index.jsx export const Input = (props) => { const { label, id, error, ...inputProps } = props; return (
{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 */}

Accueil

{/* Content */}

Bienvenue {session?.user?.name}

{/* Navigation */}
); }; ``` ### 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 && ( )}

{title}

{/* Content */}
{children}
{/* Bottom navigation */}
); }; ``` ### 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')}

{/* Thème */}

{t('settings.theme')}

); }; ``` ## 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