Documentation Vite-PWA
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.
// 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(({ mode }) => {
const isDev = mode === 'development';
return {
appType: 'spa',
build: isDev ? {
minify: false,
sourcemap: true,
} : {
minify: 'terser',
terserOptions: {
compress: { drop_console: true, drop_debugger: true },
},
sourcemap: false,
},
optimizeDeps: {
include: ['prop-types', 'parse-numeric-range', 'boolbase', 'style-to-object', 'debug', 'extend', 'react-signature-canvas'],
},
resolve: {
alias: {
'src': path.resolve(__dirname, './src'),
'prop-types': 'prop-types/prop-types.js',
},
},
plugins: [
react(),
tailwindcss(),
VitePWA({
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg,json}'],
cleanupOutdatedCaches: true,
maximumFileSizeToCacheInBytes: 3000000,
skipWaiting: true,
clientsClaim: true,
runtimeCaching: [
{
urlPattern: /\/api\/(home|profile)/,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24 * 7,
},
cacheableResponse: { statuses: [0, 200] },
networkTimeoutSeconds: 10,
},
},
{
urlPattern: /\.(?:png|jpg|jpeg|svg|gif|webp)$/,
handler: 'CacheFirst',
options: {
cacheName: 'images-cache',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 * 30,
},
},
},
],
},
injectRegister: "auto",
includeAssets: ["favicon.ico", "assets/*", "favicon.png", "apple-touch-icon.png"],
// Dynamic manifest served by SmartAuth PwaController
manifest: false,
}),
]
};
});
/api/home et /api/profile sont cachées en NetworkFirst, les images en CacheFirst
SmartBoot utilise un manifest dynamique servi par SmartAuth via le PwaController. Le manifest est généré à partir des constantes Dolibarr de votre module.
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="manifest" href="api.php/manifest">
<link rel="icon" type="image/png" href="api.php/icon/64">
<link rel="apple-touch-icon" href="api.php/icon/192">
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
| Route | Description |
|---|---|
GET /manifest | Retourne le manifest.json dynamique |
GET /icon/{size} | Retourne l'icône PWA à la taille demandée (64, 192, 512) |
Configurez le manifest via les constantes de votre module dans l'administration Dolibarr :
| Constante | Description | Défaut |
|---|---|---|
{MODULE}PWANAME | Nom complet de l'application | Nom du module |
{MODULE}PWASHORTNAME | Couleur du thème | #5fbabf |
{MODULE}PWABACKGROUND_COLOR | Couleur de fond | #ffffff |
Le PwaController cherche les icônes dans cet ordre :
Les tailles disponibles sont 64×64, 192×192 et 512×512.
La configuration des stratégies de cache se fait dans la section runtimeCaching de la config Vite (voir ci-dessus).
| 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) |
SmartCommon fournit le hook usePWAUpdate et le composant UpdatePrompt pour gérer les mises à jour du Service Worker.
Hook pour détecter et appliquer les mises à jour PWA.
import { usePWAUpdate } from '@cap-rel/smartcommon';
const MyApp = () => {
const {
updateAvailable, // true quand une mise à jour est prête
updateActivated, // true quand la mise à jour est activée
checkForUpdates, // vérification manuelle
applyUpdate, // appliquer la mise à jour (skip waiting + reload)
reloadPage, // recharger la page
} = usePWAUpdate({
autoReload: false, // recharger automatiquement (défaut: false)
checkInterval: 0, // intervalle de vérification en ms (0 = désactivé)
onUpdateAvailable: () => {}, // callback quand une MAJ est disponible
onUpdateActivated: () => {}, // callback quand la MAJ est activée
});
return (
<div>
{updateAvailable && (
<button onClick={applyUpdate}>
Mettre à jour
</button>
)}
</div>
);
};
Composant prêt à l'emploi qui affiche une notification de mise à jour. Trois variantes disponibles :
| Variante | Description |
|---|---|
toast | Notification toast en bas de l'écran (défaut) |
banner | Bandeau fixe en haut ou en bas |
modal | Fenêtre modale centrée |
import { UpdatePrompt } from '@cap-rel/smartcommon';
const App = () => (
<Provider config={appConfig}>
<UpdatePrompt
variant="toast"
checkInterval={60000}
labels={{
title: "Mise à jour disponible",
message: "Une nouvelle version est disponible.",
reloadButton: "Rafraîchir",
dismissButton: "Plus tard",
}}
/>
<Router />
</Provider>
);
SmartCommon fournit le hook useOnlineStatus qui détecte l'état de la connexion avec support optionnel de health check serveur :
import { useOnlineStatus } from '@cap-rel/smartcommon';
const MyComponent = () => {
const {
isOnline, // true si le navigateur est en ligne
isServerReachable, // true/false/null selon le health check
lastOnline, // timestamp de la dernière connexion
checkNow, // vérification manuelle
} = useOnlineStatus({
healthCheckUrl: '/api/health', // URL de health check (optionnel)
healthCheckInterval: 30000, // intervalle en ms (défaut: 30000)
stabilityDelay: 2000, // délai de stabilisation (défaut: 2000)
timeout: 5000, // timeout du health check (défaut: 5000)
});
return (
<div>
{!isOnline && (
<div className="bg-yellow-500 text-white p-2 text-center">
Mode hors connexion
</div>
)}
</div>
);
};
SmartCommon fournit useSyncClient pour la synchronisation offline complète avec gestion de conflits :
import { useSyncClient } from '@cap-rel/smartcommon';
const sync = useSyncClient({
apiUrl: import.meta.env.VITE_API_URL,
getAccessToken: () => api.user?.accessToken,
scope: ['items'],
autoSync: true,
syncInterval: 60000,
});
// CRUD offline (les opérations sont mises en file d'attente)
await sync.create('items', { label: 'Nouveau' });
await sync.update('items', id, { label: 'Modifié' });
await sync.remove('items', id);
// Synchroniser manuellement
await sync.sync(); // push + pull
// Vérifier l'état
console.log(sync.pendingCount); // opérations en attente
console.log(sync.isSyncing); // synchronisation en cours
Voir Synchronisation offline pour plus de détails.
<note tip>Pour un stockage IndexedDB simple sans synchronisation serveur, vous pouvez aussi utiliser useDb (voir Stockage de données).</note>
# 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)
Après le build, vérifiez dans les DevTools :
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