Inhoud

PWA (Progressive Web App)

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.

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(({ 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,
      }),
    ]
  };
});

Points clés de la configuration

Manifest dynamique (SmartAuth)

SmartBoot utilise un manifest dynamique servi par SmartAuth via le PwaController. Le manifest est généré à partir des constantes Dolibarr de votre module.

Configuration dans index.html

<!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>

Routes SmartAuth

Route Description
GET /manifest Retourne le manifest.json dynamique
GET /icon/{size} Retourne l'icône PWA à la taille demandée (64, 192, 512)

Constantes Dolibarr

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 | Nom court (sous l'icône) | Nom du module | | {MODULE}PWADESCRIPTION | Description | - | | {MODULE}PWATHEMECOLOR Couleur du thème #5fbabf
{MODULE}PWABACKGROUND_COLOR Couleur de fond #ffffff

Icônes

Le PwaController cherche les icônes dans cet ordre :

Les tailles disponibles sont 64×64, 192×192 et 512×512.

Service Worker et Cache

Stratégies de cache Workbox

La configuration des stratégies de cache se fait dans la section runtimeCaching de la config Vite (voir ci-dessus).

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)

Mise à jour de l'application

SmartCommon fournit le hook usePWAUpdate et le composant UpdatePrompt pour gérer les mises à jour du Service Worker.

usePWAUpdate

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>
  );
};

UpdatePrompt

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>
);

Mode Offline

Détecter la connexion

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>
  );
};

Synchronisation offline

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 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)

Vérification

Après le build, vérifiez dans les DevTools :

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

Voir aussi