Chapter 2: Configuration

The appConfig.js File

The appConfig.js file centralizes all application configuration. It is passed to the SmartCommon Provider.

// src/appConfig.js
export const config = {
    // Debug mode
    debug: import.meta.env.DEV,

    // API configuration
    api: {
        prefixUrl: import.meta.env.VITE_API_URL,
        timeout: 30000,
        debug: import.meta.env.DEV,
        paths: {
            login: "login",
            logout: "logout",
            refresh: "refresh"
        }
    },

    // localStorage persistence
    storage: {
        local: ["session", "settings"]
    },

    // Initial global state
    globalState: {
        reducers: {
            session: null,
            settings: { lng: "fr" },
            items: []
        }
    },

    // Page transition animations
    pages: {
        "/": { "/settings": "slideLeft", "*": "fade" },
        "*": "fade"
    }
};

Configuration Options Details

debug

debug: import.meta.env.DEV

Enables debug logs in the console. Uses the Vite environment variable DEV which is true in development.

api

Configuration of the HTTP client (ky) with JWT authentication.

api: {
    // Base URL for all requests
    prefixUrl: import.meta.env.VITE_API_URL,

    // Timeout in milliseconds
    timeout: 30000,

    // Log requests in console
    debug: import.meta.env.DEV,

    // SmartAuth endpoints
    paths: {
        login: "login",      // POST for authentication
        logout: "logout",    // POST for logout
        refresh: "refresh"   // GET to renew token
    }
}

storage

Defines which global state keys are persisted in localStorage.

storage: {
    local: ["session", "settings"]
}

With this configuration:

  • session will be saved in localStorage.session
  • settings will be saved in localStorage.settings
  • On page reload, these values will be restored

Use cases:

  • session: JWT tokens and user information
  • settings: preferences (language, theme)

globalState

Initializes the global Redux state via useGlobalStates.

globalState: {
    reducers: {
        // Logged in user (null = not logged in)
        session: null,

        // User preferences
        settings: { lng: "fr" },

        // Business data
        items: [],
        currentItem: null
    }
}

Each key becomes accessible via useGlobalStates:

const gst = useGlobalStates();

const session = gst.get('session');
const settings = gst.get('settings');
const items = gst.get('items');

// Write
gst.set('items', [...items, newItem]);

// Write with localStorage persistence
gst.local.set('session', userData);
gst.local.set('settings', { lng: 'en' });

pages

Configuration of page transition animations (Framer Motion).

pages: {
    // From page "/"
    "/": {
        "/settings": "slideLeft",  // To settings: slide left
        "*": "fade"                 // To others: fade
    },
    // From any other page
    "*": "fade"
}

Available animations:

  • fade: fade transition
  • slideLeft: slide to the left
  • slideRight: slide to the right
  • slideUp: slide up
  • slideDown: slide down

The SmartCommon Provider

The Provider initializes all necessary contexts:

// src/App.jsx
import { Provider } from '@cap-rel/smartcommon';
import { Router } from './components/app/Router';
import { config } from './appConfig';

export const App = () => (
    <Provider config={config}>
        <Router />
    </Provider>
);

Provider Props

Prop Type Description
config object Application configuration (appConfig)
onError function Error callback
errorFallback ReactNode Fallback content on error
ErrorFallbackComponent Component Fallback component on error
pwaUpdate object Props passed to UpdatePrompt (see State Management chapter)

Example with PWA Update

export const App = () => (
    <Provider
        config={config}
        pwaUpdate={{ variant: 'toast', checkInterval: 300000 }}
    >
        <Router />
    </Provider>
);

Internally, the Provider wraps:

// Equivalent internal (simplified)
<ErrorBoundary>
    <LibConfigProvider config={config}>
        <ReduxProvider>
            <GlobalStatesProvider>
                <ApiProvider>
                    <ConfirmProvider>
                        <Router>
                            <NavigationProvider>
                                {children}
                            </NavigationProvider>
                        </Router>
                        <Toaster />
                        {pwaUpdate && <UpdatePrompt />}
                    </ConfirmProvider>
                </ApiProvider>
            </GlobalStatesProvider>
        </ReduxProvider>
    </LibConfigProvider>
</ErrorBoundary>

Access Configuration

In any component:

import { useLibConfig } from '@cap-rel/smartcommon';

function MyComponent() {
    const config = useLibConfig();

    console.log(config.api.prefixUrl);
    console.log(config.debug);

    return <div>...</div>;
}

Advanced Configuration

Internationalization (i18n)

export const config = {
    // ...
    i18n: {
        defaultLanguage: 'fr',
        supportedLanguages: ['fr', 'en'],
        debug: import.meta.env.DEV
    }
};

Local Database (Dexie)

export const config = {
    // ...
    db: {
        name: 'monapp',
        version: 1,
        stores: {
            items: 'id++, name, category',
            logs: 'id++, action, timestamp'
        }
    }
};

Best Practices

1. Use Environment Variables

// .env.development
VITE_API_URL=http://localhost/dolibarr/modules/monmodule/pwa/api.php

// .env.production
VITE_API_URL=https://production.com/modules/monmodule/pwa/api.php

2. Separate Environments

const isDev = import.meta.env.DEV;

export const config = {
    debug: isDev,
    api: {
        prefixUrl: import.meta.env.VITE_API_URL,
        timeout: isDev ? 60000 : 30000,  // Longer in dev
        debug: isDev
    }
};

3. Do Not Store Sensitive Data

storage: {
    // OK: non-sensitive data
    local: ["session", "settings", "cart"],

    // NOT in code: passwords, server-side API keys
}

Complete Example

// src/appConfig.js
const isDev = import.meta.env.DEV;

export const config = {
    debug: isDev,

    api: {
        prefixUrl: import.meta.env.VITE_API_URL,
        timeout: isDev ? 60000 : 30000,
        debug: isDev,
        paths: {
            login: "login",
            logout: "logout",
            refresh: "refresh"
        }
    },

    storage: {
        local: ["session", "settings"]
    },

    globalState: {
        reducers: {
            // Auth
            session: null,

            // Preferences
            settings: {
                lng: "fr",
                theme: "light",
                notifications: true
            },

            // Business data
            products: [],
            cart: { items: [], total: 0 },
            currentProduct: null
        }
    },

    pages: {
        "/": {
            "/cart": "slideLeft",
            "/product/*": "slideLeft",
            "*": "fade"
        },
        "/cart": {
            "/": "slideRight",
            "*": "fade"
        },
        "*": "fade"
    },

    i18n: {
        defaultLanguage: 'fr',
        supportedLanguages: ['fr', 'en']
    }
};

Key Points to Remember

  1. appConfig.js centralizes all configuration
  2. api configures the HTTP client with JWT
  3. storage.local defines what is persisted
  4. globalState.reducers initializes the global state
  5. pages configures transition animations
  6. Use import.meta.env for environment variables

Previous Chapter | Back to Module | Next Chapter: Data Flow ->