---
source_hash: "c131fb4e"
title: "SmartBoot: A ready-to-use skeleton"
weight: 280
---

# SmartBoot: A ready-to-use skeleton

SmartBoot generates the complete structure of a Dolibarr module augmented with SmartMaker (React front + PHP back).

Sources: https://inligit.fr/cap-rel/dolibarr/smartmaker/smartboot.git

## Installation

### Linux

```
git clone https://inligit.fr/cap-rel/dolibarr/smartmaker/smartboot.git && ./smartboot/setup.sh
```

### Windows

> [!NOTE]
> The PowerShell script is being finalised.

```
git clone https://inligit.fr/cap-rel/dolibarr/smartmaker/smartboot.git && powershell ./smartboot/setup.ps1
```

Then follow the wizard steps:

```
Is your project name Coucou ?
[y/n] y
ok on continue

please wait during npm install depends ... it could take time :)
```

## Generated structure

After installation, SmartBoot adds to your module:

```
monmodule/
├── mobile/                          # React application
│   ├── index.html                   # Entry point (dynamic PWA manifest)
│   ├── vite.config.js               # Vite + PWA + dev proxy configuration
│   ├── .env.example                 # .env template (created by setup.sh)
│   ├── src/
│   │   ├── main.jsx                 # React entry point
│   │   ├── App.jsx                  # Root component
│   │   ├── appConfig.js             # Global configuration
│   │   ├── sw.js                    # Service Worker (injectManifest mode)
│   │   ├── api/
│   │   │   └── mapping/             # Backend <-> front mapping, one per feature
│   │   ├── db/
│   │   │   ├── index.js             # Db instantiation
│   │   │   └── stores/users/        # indexes.js + useDbUsers.jsx
│   │   ├── components/
│   │   │   ├── app/                 # Router, Provider, Head, Toaster
│   │   │   ├── layouts/
│   │   │   │   ├── AnimationLayout/ # Page transitions
│   │   │   │   └── PagesLayout/     # Global layout (theme, scale)
│   │   │   └── pages/
│   │   │       ├── public/          # Pages without auth (Login, Welcome)
│   │   │       ├── private/         # Pages with auth (Home, DeviceIdentification)
│   │   │       └── errors/          # Error404Page
│   │   ├── global-state/slices/     # Interface state only
│   │   ├── hooks/
│   │   │   └── useSmartcommonLabels/  # smartcommon labels in the active language
│   │   ├── i18n/
│   │   └── utils/
│   │       ├── constants/
│   │       ├── functions/
│   │       └── maps/                # form.jsx and list.jsx
│   └── public/
│       ├── images/                  # PWA icons
│       └── locales/<lang>/<ns>.json # One file per language AND per feature
├── pwa/                             # Production build
│   ├── api.php                      # API router
│   └── .htaccess                    # Apache redirection
├── smartmaker-api/
│   ├── Controllers/                 # Your PHP controllers
│   ├── dmGenericObject.php          # Dolibarr mapping example
│   └── HomeController.php           # Example controller
└── smartmaker-api-prepend.php       # SmartAuth initialization
```

This tree is not decorative: it applies the organisation rules described in [Architecture](/front/architecture), in particular splitting the data layer into `indexes.js` and a `useDb<Feature>` hook, and splitting the translations per namespace.

### Translations per namespace

The skeleton ships five namespaces, in French and in English: `common`, `welcomePage`, `loginPage`, `deviceIdentificationPage` and `homePage`.

```
const { t } = useTranslation('loginPage');
```

> [!WARNING]
> A Makefile deriving `VITE_LOCALES` must list the **subdirectories** of `public/locales/`, not the `*.json` files. Older recipes looked for `locales/*.json`: since the move to multiple namespaces they no longer find anything, the language list is empty and the application stays stuck on the fallback language.

### Development proxy

If your Dolibarr is not served by the Vite development server, set the target in `mobile/.env`:

```
VITE_DEV_PROXY_TARGET=https://dolibarr.local/custom/monmodule/pwa
```

This is the directory that **contains** `api.php`, with no trailing slash. When the variable is missing or empty, no proxy is registered. The reasoning is explained in [PWA](/front/pwa), section "Front origin and API origin".

### Two-pass linting

`npm run lint` runs `oxlint` then `eslint .`. The first pass is native and fast, the second covers what the first one does not handle. `eslint-plugin-oxlint` disables on the ESLint side the rules already checked by oxlint.

## Dynamic PWA manifest

SmartBoot automatically configures the PWA manifest dynamically through SmartAuth. The `index.html` file points at `api.php/manifest.webmanifest` instead of a static file:

```
<link rel="manifest" href="api.php/manifest.webmanifest">
<link rel="icon" type="image/png" href="api.php/icon/64">
<link rel="apple-touch-icon" href="api.php/icon/192">
```

And in `vite.config.js`, the static manifest is disabled:

```
VitePWA({
  // ...
  manifest: false, // Served dynamically by SmartAuth
})
```

> [!IMPORTANT]
> This link is relative: it assumes the front and the API are served from the same origin, which is the case once the PWA is deployed in `pwa/`. In development, with `vite dev` on port 5173 and Dolibarr on another host, `api.php` has to be proxied. See [PWA](/front/pwa), section "Front origin and API origin".

See [PWA](/front/pwa) for the Dolibarr constants, the two Service Worker modes and offline startup.

## Layouts and route guards

SmartBoot only provides two visual layouts:

| Layout | Role |
| --- | --- |
| `PagesLayout` | Global layout: applies the theme, the dark mode and the scale |
| `AnimationLayout` | Handles the animated transitions between pages (Framer Motion) |

Authentication and device identification are handled by the `<RouteGuard>` component from smartcommon (4 modes: `requireGuest`, `requireAuth`, `requireDeviceIdentification`, `requireDeviceIdentified`). See [Advanced components -> RouteGuard](/front/composants-avances#routeguard) for the detailed documentation.

### Router example (SmartBoot skeleton)

```
import { Routes, Route, RouteGuard } from '@cap-rel/smartcommon';

import {
  LoginPage, HomePage, Error404Page, PagesLayout,
  WelcomePage, DeviceIdentificationPage, AnimationLayout,
} from 'src/components';

export const Router = () => (
  <Routes>
    <Route element={<PagesLayout />}>
      {/* Public pages: an authenticated user is sent back to / */}
      <Route element={<RouteGuard requireGuest />}>
        <Route path="/welcome" element={<WelcomePage />} />
        <Route path="/login" element={<LoginPage />} />
      </Route>

      {/* Authenticated, device identification still to be done */}
      <Route element={<RouteGuard requireDeviceIdentification />}>
        <Route path="/device-identification" element={<DeviceIdentificationPage />} />
      </Route>

      {/* Authenticated + device identified: every private page */}
      <Route element={<RouteGuard requireDeviceIdentified />}>
        {/* AnimationLayout is mounted ONCE here, never page by page */}
        <Route element={<AnimationLayout />}>
          <Route path="/" element={<HomePage />} />
          {/* Add your private routes here */}
        </Route>
      </Route>

      <Route path="*" element={<Error404Page />} />
    </Route>
  </Routes>
);
```

> [!WARNING]
> **No `<BrowserRouter>` here.** The SmartCommon `<Provider>` already mounts one. Adding a second one produces the error `You cannot render a <Router> inside another <Router>`. A PWA served under a sub-path, or using hash-based deep links, passes `config.router: "hash"` and `config.basename` to the `Provider` rather than mounting its own router.

> [!NOTE]
> `Routes` and `Route` are re-exported by SmartCommon: a page must never import `react-router-dom` directly. To navigate, use `useNavigation()`.

> [!TIP]
> Before the migration to smartcommon, SmartBoot provided four custom layouts (`PrivatePagesLayout`, `PublicPagesLayout`, `PreDeviceIdentificationLayout`, `PostDeviceIdentificationLayout`). They were replaced by `<RouteGuard>`, which centralises the logic in smartcommon and makes it possible to evolve without touching the skeleton.

## Generated pages (LoginPage, DeviceIdentificationPage)

The public login and identification pages use the high-level components `<LoginComponent>` and `<DeviceIdentificationComponent>` from smartcommon. The skeleton only dresses them up (visual wrapper, background waves, register/forgot-password links) and wires `onSuccess`/`onError` to the local store.
- `<LoginComponent>` includes the **smartAuth QR pair flow for free** (scan -> claim -> poll). See [details](/front/composants-avances#logincomponent).
- `<DeviceIdentificationComponent>` reads `useApi().user.deviceOptions` to display either a plain input (first device) or a radio + input (pairing). See [details](/front/composants-avances#deviceidentificationcomponent).

## AboutModal

SmartBoot integrates the smartcommon `AboutModal` (automatic PWA update check through [usePWAUpdate](/front/hooks#usepwaupdate)):

```
import { AboutModal } from '@cap-rel/smartcommon';
import { APP_VERSION } from 'src/utils';

<AboutModal
  open={showAbout}
  onClose={() => setShowAbout(false)}
  appName="Mon Application"
  version={APP_VERSION}
/>
```

This component displays:
- The application name and the version (`APP_VERSION` coming from `VITE_APP_VERSION`)
- Optional free fields through the `fields` prop
- A "Check for updates" button that restarts the Service Worker

See [Advanced components -> AboutModal](/front/composants-avances#aboutmodal) for the labels and slots.

## Example controller

SmartBoot generates an example `HomeController.php` with a `dmGenericObject.php` mapping:

```
// smartmaker-api/HomeController.php
class HomeController
{
    public function index($arr = null)
    {
        global $db, $langs;

        $ret = [
            'statusCode' => 200,
            'generic_message' => "",
            'lastupdate'  => "",
            'home'  => "",
        ];

        return ([$ret, 200]);
    }
}
```

## Components mounted by default

The skeleton already mounts, in `App.jsx`, three cross-cutting SmartCommon components. You have nothing to add:

| Component | Role |
| --- | --- |
| `<UpdatePrompt>` | offers to reload when a new version is ready |
| `<InstallPrompt>` | offers to install the PWA on the device |
| `<ViewportProvider>` | exposes the display breakpoint (mobile, tablet, desktop) |

> [!NOTE]
> `InstallPrompt` has no translation bundle: its default labels are in English. The skeleton passes it its own `labels`, to be translated in your namespaces. See [PWA](/front/pwa), section "Offering the installation".

## Next steps

You can now move on to development:

- [PHP development (back)](/howto/devback) - Routes and controllers
- [React development (front)](/howto/devfront) - User interface
- [Architecture](/front/architecture) - file organisation, to read before adding your first feature
