Nous allons créer un mini projet : une **ToDo List** (liste de tâches) avec navigation. C'est un site web simple avec des pages : Accueil (Home), Connexion (Login), Déconnexion (Logout), et une page pour la liste de tâches avec CRUD (Create : ajouter une tâche, Read : lister, Update : modifier, Delete : supprimer).
**Pourquoi ce projet ?**
- Il couvre les bases : Composants, état (useState), effets (useEffect), props, gestion des formulaires, événements.
- Navigation avec React Router (basique pour les sites multi-pages).
- Authentification simple (fausse, avec état local, pas de backend réel).
- CRUD sur les tâches (stockées en mémoire, disparaissent au rechargement).
- Style avec CSS natif (pas de librairies, pour apprendre les bases).
**Structure du projet (expliquée en détail)** :
- **Dossier src/** : Contient tout le code.
- `index.tsx` : Point d'entrée, rend l'app.
- `App.tsx` : Composant principal, gère les routes (navigation).
- `components/` : Dossier pour les composants réutilisables.
- `Nav.tsx` : Barre de navigation (liens vers Home, Todo List, Login/Logout).
- `Home.tsx` : Page d'accueil simple.
- `Login.tsx` : Formulaire de connexion (fausse, avec nom d'utilisateur et mot de passe).
- `TodoList.tsx` : Page de la liste de tâches avec CRUD.
- `TodoForm.tsx` : Formulaire pour ajouter/modifier une tâche (réutilisable).
- `TodoItem.tsx` : Composant pour afficher une tâche individuelle.
- `index.css` : Fichier CSS global pour les styles (simple, responsive).
**Fonctionnement global** :
- Navigation : Utilise React Router pour changer de page sans recharger.
- Auth : Un état "isLoggedIn" (true/false). Login met à true, Logout à false. Si pas connecté, la Todo List redirige vers Login.
- ToDo List : Tâches avec id, texte, completed (fait/pas fait). CRUD avec boutons.
- CSS : Natif, classes simples pour layout, boutons, formulaires. Couleurs basiques (bleu pour boutons, rouge pour erreurs). Responsive avec media queries.
Je vais avancer étape par étape. Pour chaque étape, j'explique le rôle, donne le code à copier, et pose des questions pour vérifier si vous suivez. Répondez-moi après chaque étape si possible, mais comme vous avez demandé tout le code, je continue. Prenez votre temps pour copier et tester.
**Prérequis** : Assurez-vous d'avoir Node.js installé. Ouvrez VS Code, créez un nouveau dossier pour le projet.
---
### Étape 1 : Créer le projet React avec TypeScript
**Rôle** : Créer la base. TypeScript aide à éviter les erreurs de types (ex. s'assurer que id est un nombre). On installe React Router pour la navigation.
1. Ouvrez un terminal dans VS Code.
2. Créez le dossier et allez dedans :
```
mkdir todo-list-app
cd todo-list-app
```
3. Créez le projet :
```
npx create-react-app . --template typescript
```
4. Installez React Router (pour navigation) :
```
npm install react-router-dom @types/react-router-dom
```
5. Testez :
```
npm start
```
Allez sur http://localhost:3000. Vous voyez la page React par défaut.
**Questions pour vous** : As-tu réussi à créer le projet et à le lancer ? Y a-t-il une erreur dans le terminal ? Prêt pour nettoyer et structurer ?
---
### Étape 2 : Nettoyer et structurer les fichiers
**Rôle** : Supprimer le inutile, créer les dossiers/fichiers, setup basique.
1. Supprimez ces fichiers dans src/ : App.css, logo.svg, setupTests.ts (inutiles).
2. Créez le dossier src/components/ (clic droit sur src > New Folder).
3. Créez ces fichiers vides dans src/components/ : Nav.tsx, Home.tsx, Login.tsx, TodoList.tsx, TodoForm.tsx, TodoItem.tsx.
4. Mettez à jour src/App.tsx avec un squelette (on le remplira plus tard) :
```
import React from 'react';
import './index.css';
function App() {
return (
<div className="container">
<h1>Bienvenue dans la ToDo List App</h1>
</div>
);
}
export default App;
```
5. Mettez à jour src/index.tsx (ajoutez BrowserRouter pour Router) :
```
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
reportWebVitals();
```
6. Créez src/index.css avec styles basiques (on l'étendra) :
```
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f9f9f9;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h1, h2 {
text-align: center;
}
button {
cursor: pointer;
}
```
**Vérification** : `npm start`. Voyez "Bienvenue dans la ToDo List App".
**Questions pour vous** : La structure est-elle claire ? As-tu créé tous les fichiers ? Prêt pour implémenter la navigation ?
---
### Étape 3 : Implémenter la navigation avec Nav.tsx
**Rôle** : La barre de navigation montre les liens (Home, Todo List, Login/Logout). Elle change en fonction de si l'utilisateur est connecté.
1. Dans src/components/Nav.tsx, copiez :
```
import React from 'react';
import { Link } from 'react-router-dom';
interface NavProps {
isLoggedIn: boolean;
logout: () => void;
}
const Nav: React.FC<NavProps> = ({ isLoggedIn, logout }) => {
return (
<nav style={{ backgroundColor: '#007bff', padding: '10px', textAlign: 'center' }}>
<Link to="/" style={{ color: 'white', margin: '0 10px', textDecoration: 'none' }}>Accueil</Link>
{isLoggedIn && <Link to="/todo" style={{ color: 'white', margin: '0 10px', textDecoration: 'none' }}>ToDo List</Link>}
{isLoggedIn ? (
<button onClick={logout} style={{ color: 'white', background: 'none', border: 'none', cursor: 'pointer' }}>Déconnexion</button>
) : (
<Link to="/login" style={{ color: 'white', margin: '0 10px', textDecoration: 'none' }}>Connexion</Link>
)}
</nav>
);
};
export default Nav;
```
**Explication** :
- Props : isLoggedIn (booléen), logout (fonction).
- Liens avec <Link> de React Router (pas <a>, pour navigation sans rechargement).
- Si connecté, montre ToDo List et Déconnexion ; sinon, Connexion.
- Style inline pour simplicité (on peut le mover en CSS plus tard).
**Questions pour vous** : Comprends-tu comment <Link> fonctionne pour la navigation ? As-tu testé en ajoutant Nav dans App.tsx (temporairement) ? Prêt pour les pages Home et Login ?
---
### Étape 4 : Créer la page Home (Home.tsx)
**Rôle** : Page d'accueil simple, visible par tous.
1. Dans src/components/Home.tsx, copiez :
```
import React from 'react';
const Home: React.FC = () => {
return (
<div>
<h2>Bienvenue sur l'application ToDo List</h2>
<p>Connectez-vous pour gérer vos tâches.</p>
</div>
);
};
export default Home;
```
**Explication** : Composant simple avec du texte. Pas d'état ou props.
**Questions pour vous** : Simple, non ? Prêt pour le Login ?
---
### Étape 5 : Créer la page Login (Login.tsx)
**Rôle** : Formulaire pour "se connecter" (fausse auth, juste vérifier si champs remplis).
1. Dans src/components/Login.tsx, copiez :
```
import React, { useState } from 'react';
interface LoginProps {
login: (username: string) => void;
}
const Login: React.FC<LoginProps> = ({ login }) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (username && password) {
login(username);
} else {
setError('Veuillez remplir tous les champs.');
}
};
return (
<div style={{ textAlign: 'center' }}>
<h2>Connexion</h2>
<form onSubmit={handleSubmit}>
<div style={{ marginBottom: '10px' }}>
<label>Nom d'utilisateur</label>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
style={{ display: 'block', margin: '0 auto', padding: '5px' }}
/>
</div>
<div style={{ marginBottom: '10px' }}>
<label>Mot de passe</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
style={{ display: 'block', margin: '0 auto', padding: '5px' }}
/>
</div>
{error && <p style={{ color: 'red' }}>{error}</p>}
<button type="submit" style={{ backgroundColor: '#007bff', color: 'white', padding: '10px' }}>Se connecter</button>
</form>
</div>
);
};
export default Login;
```
**Explication** :
- État pour username, password, error.
- handleSubmit : Vérifie champs, appelle login (fonction prop).
- Formulaire avec inputs, erreur en rouge.
**Questions pour vous** : Comprends-tu la validation simple ? Prêt pour la ToDo List ?
---
### Étape 6 : Créer TodoForm.tsx (formulaire pour tâches)
**Rôle** : Formulaire réutilisable pour ajouter/modifier une tâche.
1. Dans src/components/TodoForm.tsx, copiez :
```
import React, { useState, useEffect } from 'react';
interface TodoFormProps {
onSubmit: (text: string) => void;
initialText?: string;
}
const TodoForm: React.FC<TodoFormProps> = ({ onSubmit, initialText }) => {
const [text, setText] = useState(initialText || '');
useEffect(() => {
setText(initialText || '');
}, [initialText]);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (text) {
onSubmit(text);
setText('');
}
};
return (
<form onSubmit={handleSubmit} style={{ textAlign: 'center' }}>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Entrez une tâche"
style={{ padding: '5px', width: '300px' }}
/>
<button type="submit" style={{ backgroundColor: '#28a745', color: 'white', padding: '5px 10px', marginLeft: '10px' }}>
{initialText ? 'Modifier' : 'Ajouter'}
</button>
</form>
);
};
export default TodoForm;
```
**Explication** :
- État pour text.
- useEffect pour pré-remplir lors d'édition.
- onSubmit appelle la fonction parent avec la tâche.
**Questions pour vous** : Le formulaire est réutilisable, compris ? Prêt pour TodoItem ?
---
### Étape 7 : Créer TodoItem.tsx (une tâche individuelle)
**Rôle** : Affiche une tâche avec boutons modifier/supprimer/compléter.
1. Dans src/components/TodoItem.tsx, copiez :
```
import React from 'react';
interface TodoItemProps {
id: number;
text: string;
completed: boolean;
onToggle: (id: number) => void;
onEdit: (id: number) => void;
onDelete: (id: number) => void;
}
const TodoItem: React.FC<TodoItemProps> = ({ id, text, completed, onToggle, onEdit, onDelete }) => {
return (
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '10px' }}>
<input type="checkbox" checked={completed} onChange={() => onToggle(id)} />
<span style={{ textDecoration: completed ? 'line-through' : 'none', marginLeft: '10px', flexGrow: 1 }}>
{text}
</span>
<button onClick={() => onEdit(id)} style={{ backgroundColor: '#ffc107', color: 'white', marginLeft: '10px', padding: '5px' }}>Modifier</button>
<button onClick={() => onDelete(id)} style={{ backgroundColor: '#dc3545', color: 'white', marginLeft: '10px', padding: '5px' }}>Supprimer</button>
</div>
);
};
export default TodoItem;
```
**Explication** :
- Checkbox pour compléter (line-through si completed).
- Boutons pour edit/delete.
**Questions pour vous** : Les actions CRUD sont gérées par props, OK ? Prêt pour TodoList ?
---
### Étape 8 : Créer TodoList.tsx (page de la liste)
**Rôle** : Gère la liste de tâches, état local pour todos, CRUD.
1. Dans src/components/TodoList.tsx, copiez :
```
import React, { useState } from 'react';
import TodoForm from './TodoForm';
import TodoItem from './TodoItem';
interface Todo {
id: number;
text: string;
completed: boolean;
}
const TodoList: React.FC = () => {
const [todos, setTodos] = useState<Todo[]>([]);
const [editingId, setEditingId] = useState<number | null>(null);
const [editingText, setEditingText] = useState('');
const addTodo = (text: string) => {
setTodos([...todos, { id: Date.now(), text, completed: false }]);
};
const toggleTodo = (id: number) => {
setTodos(todos.map(todo => todo.id === id ? { ...todo, completed: !todo.completed } : todo));
};
const editTodo = (id: number) => {
const todo = todos.find(t => t.id === id);
if (todo) {
setEditingId(id);
setEditingText(todo.text);
}
};
const updateTodo = (text: string) => {
setTodos(todos.map(todo => todo.id === editingId ? { ...todo, text } : todo));
setEditingId(null);
setEditingText('');
};
const deleteTodo = (id: number) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div>
<h2>Ma ToDo List</h2>
<TodoForm onSubmit={editingId ? updateTodo : addTodo} initialText={editingText} />
<div>
{todos.map(todo => (
<TodoItem
key={todo.id}
id={todo.id}
text={todo.text}
completed={todo.completed}
onToggle={toggleTodo}
onEdit={editTodo}
onDelete={deleteTodo}
/>
))}
</div>
</div>
);
};
export default TodoList;
```
**Explication** :
- État pour todos (liste), editingId (pour édition), editingText.
- Fonctions pour add, toggle, edit, update, delete.
- Affiche TodoForm et liste de TodoItem.
**Questions pour vous** : Le CRUD est complet, compris comment l'état est mis à jour ? Prêt pour intégrer tout dans App.tsx ?
---
### Étape 9 : Intégrer tout dans App.tsx avec routes
**Rôle** : Gère les routes (pages), état global pour auth.
1. Dans src/App.tsx, copiez :
```
import React, { useState } from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';
import Nav from './components/Nav';
import Home from './components/Home';
import Login from './components/Login';
import TodoList from './components/TodoList';
import './index.css';
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [username, setUsername] = useState('');
const login = (user: string) => {
setUsername(user);
setIsLoggedIn(true);
};
const logout = () => {
setIsLoggedIn(false);
setUsername('');
};
return (
<div>
<Nav isLoggedIn={isLoggedIn} logout={logout} />
<div className="container">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={isLoggedIn ? <Navigate to="/todo" /> : <Login login={login} />} />
<Route path="/todo" element={isLoggedIn ? <TodoList /> : <Navigate to="/login" />} />
</Routes>
</div>
</div>
);
}
export default App;
```
**Explication** :
- État pour isLoggedIn et username.
- <Routes> définit les pages : / pour Home, /login, /todo (protégée).
- <Navigate> redirige si pas connecté.
**Vérification** : `npm start`. Naviguez, connectez-vous (n'importe quoi comme username/password), ajoutez tâches, déconnectez.
**Questions pour vous** : L'app complète marche-t-elle ? As-tu testé la navigation et le CRUD ? Qu'est-ce que tu n'as pas compris ?
---
### Étape 10 : Améliorer le CSS dans index.css
**Rôle** : Rendre plus joli avec CSS.
Ajoutez ceci à la fin de src/index.css :
```
nav {
background-color: #007bff;
padding: 10px;
text-align: center;
}
nav a, nav button {
color: white;
margin: 0 10px;
text-decoration: none;
border: none;
background: none;
}
nav button:hover, nav a:hover {
text-decoration: underline;
}
.form-group {
margin-bottom: 10px;
}
input, textarea {
padding: 5px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
border-radius: 4px;
border: none;
padding: 5px 10px;
}
.error {
color: red;
}
```
**Vérification** : Relance, voyez les styles appliqués.
**Questions pour vous** : Le style est-il OK ? Veux-tu ajouter plus (ex. couleurs) ?
---
### Fin du projet