# PWA (Progressive Web App)
Documentation [Vite-PWA](https://vite-pwa-org.netlify.app/)
Une **Progressive Web App (PWA)** est une application qui combine le meilleur du web et du mobile. Elle s'installe sur l'écran d'accueil, fonctionne hors connexion et offre une expérience fluide proche d'une app native.
## Configuration Vite
### Configuration complète
```
// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import { VitePWA } from "vite-plugin-pwa";
import path from "path";
export default defineConfig({
// Réduire les logs en dev
logLevel: 'warn',
// Optimiser les dépendances
optimizeDeps: {
include: [
'prop-types',
'react-signature-canvas',
// Autres dépendances si nécessaire
],
},
// Alias pour les imports
resolve: {
alias: {
'src': path.resolve(__dirname, './src'),
'lib': path.resolve(__dirname, './src/lib'),
},
},
// Plugins
plugins: [
react(),
tailwindcss(),
VitePWA({
// Mise à jour automatique du Service Worker
registerType: 'autoUpdate',
// Configuration Workbox (cache)
workbox: {
// Fichiers à mettre en cache
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff,woff2}'],
// Nettoyer les anciens caches
cleanupOutdatedCaches: true,
// Activer immédiatement le nouveau SW
skipWaiting: true,
},
// Injection automatique du SW
injectRegister: "auto",
// Assets à inclure
includeAssets: [
"favicon.ico",
"favicon.png",
"apple-touch-icon.png",
"assets/*"
],
// Manifest de l'application
manifest: {
name: "Mon Application SmartMaker",
short_name: "MonApp",
description: "Description de mon application",
start_url: "/",
display: "standalone",
background_color: "#ffffff",
theme_color: "#5fbabf",
icons: [
{
src: 'images/pwa-64x64.png',
sizes: '64x64',
type: 'image/png'
},
{
src: 'images/pwa-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: 'images/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any'
},
{
src: 'images/maskable-icon-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'maskable'
}
],
},
}),
]
});
```
### Structure des fichiers
```
public/
favicon.ico
favicon.png
apple-touch-icon.png
images/
pwa-64x64.png
pwa-192x192.png
pwa-512x512.png
maskable-icon-512x512.png
```
Les images doivent être dans le dossier ''public/'' et les chemins dans le manifest sont relatifs à ce dossier.
## Icônes PWA
### Tailles requises
^ Taille ^ Usage ^
| 64x64 | Favicon |
| 192x192 | Icône Android/Chrome |
| 512x512 | Splash screen, installation |
| 180x180 | Apple Touch Icon (iOS) |
### Icône maskable
L'icône maskable est adaptée aux différentes formes d'icônes (ronde, carrée, etc.) sur Android. La zone de sécurité est un cercle de 80% centré.
Outils pour générer les icônes :
* [PWA Asset Generator](https://github.com/nicholasrobinson/pwa-asset-generator)
* [Maskable.app](https://maskable.app/)
## Manifest
### Options principales
^ Option ^ Description ^
| ''name'' | Nom complet de l'application |
| ''short_name'' | Nom court (affiché sous l'icône) |
| ''description'' | Description de l'application |
| ''start_url'' | URL de démarrage (généralement ''/'') |
| ''display'' | Mode d'affichage (voir ci-dessous) |
| ''background_color'' | Couleur de fond au lancement |
| ''theme_color'' | Couleur de la barre d'état |
| ''icons'' | Tableau des icônes |
### Modes d'affichage
^ Mode ^ Description ^
| ''standalone'' | Comme une app native (recommandé) |
| ''fullscreen'' | Plein écran (jeux) |
| ''minimal-ui'' | Avec contrôles de navigation minimaux |
| ''browser'' | Dans le navigateur normal |
## Service Worker et Cache
### Stratégies de cache Workbox
```
workbox: {
// Cache-first pour les assets statiques
runtimeCaching: [
{
urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
handler: 'CacheFirst',
options: {
cacheName: 'google-fonts-cache',
expiration: {
maxEntries: 10,
maxAgeSeconds: 60 * 60 * 24 * 365 // 1 an
},
cacheableResponse: {
statuses: [0, 200]
}
}
},
{
// Network-first pour l'API
urlPattern: /^https:\/\/api\.example\.com\/.*/i,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 60 // 1 heure
},
networkTimeoutSeconds: 10
}
}
]
}
```
### Stratégies disponibles
^ Stratégie ^ Usage ^
| ''CacheFirst'' | Assets statiques (fonts, images) |
| ''NetworkFirst'' | API, données dynamiques |
| ''StaleWhileRevalidate'' | Contenu qui peut être légèrement obsolète |
| ''NetworkOnly'' | Toujours réseau (authentification) |
| ''CacheOnly'' | Uniquement cache (assets embarqués) |
## Mode Offline
### Détecter la connexion
```
import { useEffect, useState } from 'react';
export const useOnlineStatus = () => {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
};
```
### Afficher un indicateur offline
```
import { useOnlineStatus } from './hooks/useOnlineStatus';
const App = () => {
const isOnline = useOnlineStatus();
return (
{!isOnline && (
Mode hors connexion
)}
{/* ... */}
);
};
```
### Synchronisation avec useDb
Utilisez ''useDb'' pour stocker les données localement et synchroniser quand la connexion revient :
```
import { useDb } from '@cap-rel/smartcommon';
const db = useDb({
name: 'myApp',
stores: {
pendingSync: 'id++, action, data, createdAt'
}
});
// Stocker une action en attente
const saveOffline = async (action, data) => {
await db.pendingSync.add({
action,
data,
createdAt: Date.now()
});
};
// Synchroniser quand en ligne
const syncPending = async (api) => {
const pending = await db.pendingSync.toArray();
for (const item of pending) {
try {
await api.private.post(item.action, { json: item.data });
await db.pendingSync.delete(item.id);
} catch (error) {
console.error('Sync failed:', error);
}
}
};
```
## Build et déploiement
### Commandes
```
# Build de production
npm run build
# Prévisualiser le build
npm run preview
# Le build génère:
# - dist/index.html
# - dist/assets/*.js
# - dist/assets/*.css
# - dist/sw.js (Service Worker)
# - dist/manifest.webmanifest
```
### Vérification
Après le build, vérifiez dans les DevTools :
* **Application > Manifest** : Le manifest est chargé
* **Application > Service Workers** : Le SW est actif
* **Application > Storage > Cache Storage** : Les assets sont cachés
### Déployer sur Dolibarr
Copiez le contenu de ''dist/'' dans le dossier ''pwa/'' de votre module Dolibarr :
```
# Depuis le dossier mobile/
npm run build
cp -r dist/* ../pwa/
```
Ou utilisez le Makefile fourni par SmartBoot :
```
make pwa
```
## Tester la PWA
### Lighthouse
1. Ouvrez les DevTools (F12)
2. Allez dans l'onglet "Lighthouse"
3. Sélectionnez "Progressive Web App"
4. Lancez l'audit
### Critères de validation
* HTTPS (ou localhost en dev)
* Manifest valide avec icônes
* Service Worker enregistré
* Fonctionne hors connexion
* Responsive design
## Voir aussi
* [[configuration|Configuration]] - Configuration Vite complète
* [[stockage_de_donnees|Stockage de données]] - IndexedDB pour le offline
* [Vite-PWA](https://vite-pwa-org.netlify.app/) - Documentation officielle