@getsoren/shared-module

v2.44.0

Published

> This package contains shared components, hooks and utilities. > This package use dependencies only with injection.

Readme

@getsoren/shared-module

This package contains shared components, hooks and utilities. This package use dependencies only with injection.

Installation

yarn add @getsoren/shared-module

Usage

import { InitializeAxiosConfig } from "@getsoren/shared-module";

Inject dependencies with provider

import { InjectDependenciesProvider, InitializeAxiosConfig } from "@getsoren/shared-module";

const App = () => (
  <InjectDependenciesProvider apiURL={import.meta.env.VITE_API_URL} libraries={{
    axios,
  }}>
    <InitializeAxiosConfig/>
    ...
  </InjectDependenciesProvider>
);

export default App;

Providers

| Module | Description | Dependencies | |-------------------------------|-------------------------------------------------------|--------------| | InjectDependenciesProvider | Inject dependencies for other shared component | - | | QueryClientProviderWithConfig | React Query provider with default config | React Query | | ChatProvider | Shared WebSocket chat connection provider via context | - |

Components

| Module | Type | Description | Dependencies | |------------------------|-----------------|-----------------------------------------------------|------------------------------------------------------------| | RequireAuth | React Component | Component for protected routing | Axios & react-router-dom | | GTMSendPageView | React Component | Send page view event to Google Tag Manager | @getsoren/react-google-tag-manager & react-router-dom | | InitializeAxiosConfig | React Component | Initialize Axios with custom default config options | Axios | | InitializeI18nConfig | React Component | Initialize i18n with custom config options | i18next & react-i18next & i18next-browser-languagedetector | | InitializeSentryConfig | React Component | Initialize Sentry | @sentry/react & react-router-dom | | InitializeMapBoxConfig | React Component | Initialize MapBox | mapbox-gl | | InitializeDaysJSConfig | React Component | Initialize DayJS | dayjs | | InitializeHubSpot | React Component | Initialize HubSpot chat widget | - | | PreloadErrorHandler | React Component | his component is used to handle preload error. | dayjs |

Hooks

| Module | Description | Dependencies | |---------------------|----------------------------------------------------------------------------|-------------------| | useAdapter | Hook with several adapter | - | | useAuth | Hook for authentification management | Axios | | useHubSpot | Hook to interact with the HubSpot chat widget (toggle, availability) | InitializeHubSpot | | useResponseError | This hook is used to print error messages from the API | i18next | | useInfiniteDataGrid | This hook is used to handle the infinite scroll of the DataGrid component. | - | | useCurrentLanguage | This get the current language of app | - | | useFilters | Hook to handle filter | - | | useChat | Hook to consume the ChatProvider context (threads, messaging, presence) | ChatProvider |

WebSocket

Chat

The chat module provides a WebSocket client for thread subscription, messaging, read receipts, and presence events.

With the React provider (recommended):

First, wrap your app with ChatProvider (inside InjectDependenciesProvider):

import { ChatProvider, InjectDependenciesProvider } from "@getsoren/shared-module";

const App = () => (
  <InjectDependenciesProvider apiURL={import.meta.env.VITE_API_URL} libraries={libraries} localStorageKeys={localStorageKeys}>
    <ChatProvider>
      <MyApp />
    </ChatProvider>
  </InjectDependenciesProvider>
);

Then use useChat() in any component:

import { useChat } from "@getsoren/shared-module";

const Chat = ({ threadId }: { threadId: string }) => {
  const { isReady, sendMessage, joinThread } = useChat();

  return (
    <div>
      <p>{isReady ? "Connected" : "Connecting..."}</p>
      <button onClick={() => joinThread(threadId)}>Join</button>
      <button onClick={() => sendMessage(threadId, "Hello!")}>Send</button>
    </div>
  );
};

The provider reads apiURL and the user token from InjectDependenciesProvider automatically. You can override both via props:

<ChatProvider url="wss://api.example.com/v2/threads/ws" token="my-jwt">

Without React (class only):

import { ChatClient } from "@getsoren/shared-module";

const client = new ChatClient({
  url: "wss://api.example.com/v2/threads/ws",
  getToken: () => "my-jwt",
  onEvent: (event) => console.log(event),
  onConnectionChange: (connected) => console.log("connected:", connected),
});

client.connect();
client.joinThread("thread-1");
client.sendMessage("thread-1", "Hello!");

| Module | Description | Dependencies | |--------------|-----------------------------------------------------------|--------------| | ChatClient | Framework-agnostic WebSocket client for the chat protocol | - | | ChatProvider | Provider for shared WebSocket connection via context | - | | useChat | React hook to consume the ChatProvider context | ChatProvider |

Orval

The @getsoren/shared-module/orval entry point provides everything needed to write an orval.config.ts.

⚠️ This entry relies on Node built-ins (fs, child_process) and must only be imported from orval.config.ts files, never from browser code.

// orval.config.ts
import { getOrvalConfig } from "@getsoren/shared-module/orval";
import { defineConfig } from "orval";

const config = getOrvalConfig([
  { customAxiosInstancePath: "./src/api/customSaasInstance.ts", input: { host: "saas" } },
  { customAxiosInstancePath: "./src/api/customAuthInstance.ts", input: { host: "auth" } },
  { customAxiosInstancePath: "./src/api/customSupplierInstance.ts", input: { host: "supplier" } },
]);

export default defineConfig(config);

Input resolution

When input is an object, the OpenAPI schema target is resolved with the following priorities:

  1. Local file (./openapi.<name>.json) — pulled schema snapshot
  2. Local service (https://<subdomain>.api.tracktor.test/openapi.json) — docker compose back environment, probed via cURL
  3. Upstream deployed service (https://<subdomain>.api.dev.tracktor.fr/openapi.json) — dev cluster, fallback

Host

host is the API name ("saas" | "auth" | "supplier") and derives all defaults:

| Derived value | Default | |-------------------|----------------------------------------------------------| | overrideApiName | <host> → generated folders src/api/<host>/ | | localFile | ./openapi.<host>.json | | localUrl | https://<subdomain>.api.tracktor.test/openapi.json | | upstreamUrl | https://<subdomain>.api.dev.tracktor.fr/openapi.json |

The subdomain matches the host, except saas which is served on the app subdomain. Every derived value can be overridden individually:

// Target a specific environment
input: { host: "saas", upstreamUrl: "https://app.api.rc.tracktor.fr/openapi.json" }

// Fully custom, without host
input: {
  localFile: "./openapi.custom.json",
  localUrl: "https://custom.test/openapi.json",
  upstreamUrl: "https://custom.fr/openapi.json",
}

// Plain path or URL, no resolution
input: "./openapi.json"

customAxiosInstancePath is required: each API needs a dedicated axios instance file (its own baseURL env variable) which orval uses as mutator.

| Module | Description | |-----------------------|----------------------------------------------------------------------| | getOrvalConfig | Build the orval config (input resolution included) | | resolveOrvalInput | Standalone schema target resolution (local file → local → upstream) |

Utils function

| Module | Description | |---------------------|----------------------| | dateAdapter | Adapt given date | | distanceAdapter | Adapt given distance | | worksiteNameAdapter | Adapt worksite name |