# Développement React (front)
Vous venez de déployer SmartBoot dans votre module et vous vous demandez par où commencer ? Vous êtes au bon endroit !
## Installation des dépendances
```
cd mobile
npm i
```
## Configuration initiale
### Fichier .env
Copiez ou renommez ''mobile/.env.example'' en ''mobile/.env'' :
```
VITE_API_URL=https://votre-dolibarr.com/custom/monmodule/pwa/api.php
VITE_APP_VERSION=dev
VITE_LOCALES=en,fr
```
### Configuration de l'application
Le fichier ''appConfig.js'' centralise toute la configuration :
```
// src/appConfig.js
export const config = {
// Mode debug (logs colorés dans la console)
debug: import.meta.env.DEV,
// Configuration API
api: {
prefixUrl: import.meta.env.VITE_API_URL,
timeout: 30000,
paths: {
login: "login",
logout: "logout",
refresh: "refresh",
},
},
// Stockage
storage: {
local: ["session", "settings"], // Persisté en localStorage
},
// État global initial
globalState: {
reducers: {
session: null,
settings: { lng: "fr" },
items: [],
},
},
// Animations de pages
pages: {
"/": { "/settings": "slideLeft", "*": "fade" },
"*": "fade",
},
};
```
## Lancement
```
cd mobile
npm run dev
```
Ouvrez http://localhost:5173/ (mode mobile recommandé dans les DevTools).
Attention [[SmartAuth]] est nécessaire !
## Structure des pages
L'arborescence préinstallée par SmartBoot :
```
src/components/pages/
├── errors/
│ └── Error404Page/
│ └── index.jsx
├── private/
│ └── HomePage/
│ └── index.jsx
└── public/
├── LoginPage/
│ └── index.jsx
└── WelcomePage/
└── index.jsx
```
^ Dossier ^ Description ^
| ''public/'' | Pages accessibles sans authentification |
| ''private/'' | Pages nécessitant une authentification |
| ''errors/'' | Pages d'erreur (404, etc.) |
## Créer une page de login
Avec SmartCommon, la page de login devient très simple :
```
// src/components/pages/public/LoginPage/index.jsx
import { useApi, useGlobalStates, useForm, useNavigation } from '@cap-rel/smartcommon';
import { Form, Input, Button } from '@cap-rel/smartcommon';
import { z } from 'zod';
const schema = z.object({
login: z.string().min(1, "Login requis"),
password: z.string().min(1, "Mot de passe requis"),
});
export const LoginPage = () => {
const api = useApi();
const navigate = useNavigation();
const [, setSession] = useGlobalStates('session');
const form = useForm({ schema });
const handleSubmit = async (data) => {
const response = await api.public.post('login', { json: data });
if (response.success) {
setSession(response.data);
navigate('/');
}
};
return (
);
};
```
## Créer une page privée
### Page d'accueil avec liste
```
// src/components/pages/private/HomePage/index.jsx
import { useApi, useGlobalStates, useNavigation } from '@cap-rel/smartcommon';
import { useEffect } from 'react';
export const HomePage = () => {
const api = useApi();
const navigate = useNavigation();
const [items, setItems] = useGlobalStates('items');
const [session, setSession] = useGlobalStates('session');
// Charger les items au montage
useEffect(() => {
const fetchItems = async () => {
const response = await api.private.get('items');
if (response.success) {
setItems(response.data);
}
};
fetchItems();
}, []);
// Déconnexion
const handleLogout = async () => {
await api.private.post('logout');
setSession(null);
navigate('/login');
};
return (
{items.map((item) => (
navigate(`/items/${item.id}`)}
className="bg-white p-4 rounded-lg shadow"
>
{item.label}
{item.description}
))}
);
};
```
### Page de détail
```
// src/components/pages/private/ItemPage/index.jsx
import { useApi, useGlobalStates, useNavigation } from '@cap-rel/smartcommon';
import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
export const ItemPage = () => {
const { id } = useParams();
const api = useApi();
const navigate = useNavigation();
const [item, setItem] = useState(null);
useEffect(() => {
const fetchItem = async () => {
const response = await api.private.get(`items/${id}`);
if (response.success) {
setItem(response.data);
}
};
fetchItem();
}, [id]);
if (!item) {
return Chargement...
;
}
return (
{item.label}
{item.description}
);
};
```
## Configurer le routeur
```
// src/components/app/Router/index.jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { PublicRoutes, PrivateRoutes } from './Guards';
import { LoginPage } from '../../pages/public/LoginPage';
import { WelcomePage } from '../../pages/public/WelcomePage';
import { HomePage } from '../../pages/private/HomePage';
import { ItemPage } from '../../pages/private/ItemPage';
import { Error404Page } from '../../pages/errors/Error404Page';
export const Router = () => {
return (
{/* Routes publiques */}
}>
} />
} />
{/* Routes privées */}
}>
} />
} />
{/* Erreur 404 */}
} />
);
};
```
### Guards avec SmartCommon
```
// src/components/app/Router/Guards/index.jsx
import { Outlet, Navigate } from 'react-router-dom';
import { useGlobalStates } from '@cap-rel/smartcommon';
export const PublicRoutes = () => {
const [session] = useGlobalStates('session');
return session ? : ;
};
export const PrivateRoutes = () => {
const [session] = useGlobalStates('session');
return session ? : ;
};
```
## Utiliser les états globaux
### Stocker des données
```
import { useGlobalStates } from '@cap-rel/smartcommon';
// Lecture + écriture
const [items, setItems] = useGlobalStates('items');
// Lecture seule
const [items] = useGlobalStates('items');
// Accès à une propriété imbriquée
const [settings, setSettings] = useGlobalStates('settings');
const [lng, setLng] = useGlobalStates('settings.lng');
```
### Persistance automatique
Les clés listées dans ''config.storage.local'' sont automatiquement persistées en localStorage :
```
// appConfig.js
storage: {
local: ["session", "settings"], // Persisté automatiquement
}
```
## Utiliser les formulaires
### Formulaire complet avec validation
```
import { useForm, Form, Input, Select, Button } from '@cap-rel/smartcommon';
import { z } from 'zod';
const schema = z.object({
label: z.string().min(3, "Minimum 3 caractères"),
type: z.enum(["A", "B", "C"]),
description: z.string().optional(),
});
const CreateItemForm = ({ onSuccess }) => {
const api = useApi();
const form = useForm({ schema });
const handleSubmit = async (data) => {
const response = await api.private.post('items', { json: data });
if (response.success) {
onSuccess(response.data);
form.reset();
}
};
return (
);
};
```
## Build et déploiement
```
# Build de production
npm run build
# Le build génère le dossier dist/
# Copiez-le dans le dossier pwa/ de votre module
cp -r dist/* ../pwa/
```
Ou utilisez le Makefile :
```
make pwa
```
## Voir aussi
* [[../front/hooks|Hooks]] - Documentation complète des hooks
* [[../front/smartcommon|SmartCommon]] - Tous les composants disponibles
* [[../front/configuration|Configuration]] - Options du Provider
* [[../front/requetes_api|Requêtes API]] - Utilisation de useApi
* [[devback|Développement PHP]] - API backend