# 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