npm package: @logcomex/log-platform-frontend-lib
Description: Frontend library for Logcomex platform (ProductSelector, platform auth, shell)
Latest version: 0.2.6

# `@logcomex/log-platform-frontend-lib` — Guia passo a passo

Biblioteca React para integração com a **Plataforma Logcomex / Supply**: SSO (`validate-auth`), `ProductSelector`, modal de escolha de conta para system admin.

**Backend:** as rotas HTTP vivem na **log-platform-backend-lib** (`validate-auth`, `user-products`, `accounts-to-join`, `join-account`, …). Configura o Laravel desse pacote antes de testar o front.

**Alterações entre versões:** [CHANGELOG.md](./CHANGELOG.md).

---

## Parte 1 — Instalar o pacote e dependências

**Passo 1.** Na raiz do teu projeto front, instala a lib:

```bash
npm install @logcomex/log-platform-frontend-lib
```

**Passo 2.** Garante que o `package.json` do app tem as **peer dependencies** (versões compatíveis com as da lib). Se faltar alguma, instala por exemplo:

```bash
npm install react react-dom react-router-dom @tanstack/react-query react-i18next i18next @radix-ui/react-tooltip @radix-ui/react-dropdown-menu @radix-ui/react-dialog lucide-react clsx tailwind-merge
```

**Passo 3.** Usa **sempre** o import da raiz do pacote (não existem subpaths como `/platform-auth`):

```ts
import {
  LogPlatformAppShell,
  LogPlatformAuthProvider,
  useLogPlatformAuthRoutes,
  ProductSelector,
  registerLogPlatformI18n,
  SelectAccountModal,
  useLogPlatformAuth,
} from "@logcomex/log-platform-frontend-lib";
```

(Importa só o que precisares em cada ficheiro.)

---

## Parte 2 — Configurar o Tailwind CSS

Sem isto, os componentes da lib aparecem sem estilos.

**Passo 1.** Abre o `tailwind.config.js` ou `tailwind.config.ts` **do teu app** (não o da lib).

**Passo 2.** Adiciona os ficheiros compilados da lib ao array `content`.

*Se o config for CommonJS:*

```js
const logPlatformContent = require("@logcomex/log-platform-frontend-lib/tailwind-content");

module.exports = {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}", ...logPlatformContent],
  // theme, plugins…
};
```

*Se for ESM (`tailwind.config.ts`), o export da lib é CJS — usa `createRequire`:*

```ts
import { createRequire } from "node:module";

const require = createRequire(import.meta.url);
const logPlatformContent = require("@logcomex/log-platform-frontend-lib/tailwind-content");

export default {
  content: ["./index.html", "./src/**/*.{tsx,ts,jsx,js}", ...logPlatformContent],
};
```

**Passo 3.** Reinicia o servidor de desenvolvimento (ex.: Vite) para o Tailwind voltar a analisar o `content`.

---

## Parte 3 — Configurar traduções (i18n)

As strings da lib ficam sob **`translation.logPlatform.*`** para não colidir com as chaves `sidebar` / `common` do teu produto.

**Passo 1.** Mantém o teu fluxo atual de `i18next` + `initReactI18next` (ficheiro tipo `src/i18n.ts` / `i18n.js`).

**Passo 2.** **Depois** de `i18n.init({ ... })`, regista as traduções da lib:

```ts
import { registerLogPlatformI18n } from "@logcomex/log-platform-frontend-lib";

registerLogPlatformI18n(i18n);
```

Opcional: `registerLogPlatformI18n(i18n, { lngs: ["pt", "en"], ns: "translation" })`.

**Passo 3.** No entry da app (`main.tsx` / `main.jsx`), importa o i18n **antes** do `App`:

```ts
import "./i18n";
import App from "./App";
```

---

## Parte 4 — Configurar as rotas de autenticação (SSO / callback)

A Plataforma redireciona para algo como `/auth/platform-callback?token=...`. A lib regista essa rota e trata o `validate-auth`.

Tens duas formas: **com shell** (recomendado) ou **manual**.

### Opção A — Com `LogPlatformAppShell`

**Passo 1.** Abre o ficheiro onde definiste o componente **raiz** que contém o `<Routes>` do React Router — em projetos Vite + React é habitualmente **`src/App.tsx`** ou **`src/App.jsx`**. É **aí** que deves colocar o `LogPlatformAppShell`: envolve o retorno desse componente (ou substitui a pilha antiga de `BrowserRouter` + providers por este shell). O `main.tsx` / `main.jsx` costuma ficar só com `createRoot(...).render(<App />)`; **não** é obrigatório mudar o `main` se o router já vive no `App`.

**Passo 2.** Envolve a árvore onde precisas de router + auth com `LogPlatformAppShell`, passando `auth` com pelo menos:

- `apiBaseUrl` — URL base da **API** onde está o prefixo da lib (ex.: `https://api.teudominio.com`, igual ao que usas para outras chamadas).
- `getToken` / `setToken` — ler e gravar o **token do produto** após o login (ex.: `localStorage`).
- `paths` — no mínimo `home` (ex.: `"/"`) e, se quiseres outro path, `platformCallback` (default: `"/auth/platform-callback"`).

**Passo 3.** Usa **filho em forma de função** para receber `platformRoutes` e coloca-o **dentro** de `<Routes>` como irmã das tuas rotas (no mesmo ficheiro ou num componente filho importado pelo `App`, desde que continue a ser filho direto do shell):

```tsx
import { LogPlatformAppShell } from "@logcomex/log-platform-frontend-lib";
import { Routes, Route } from "react-router-dom";

export function App() {
  return (
    <LogPlatformAppShell
      auth={{
        apiBaseUrl: import.meta.env.VITE_API_URL ?? "",
        getToken: () => localStorage.getItem("supply_product_token"),
        setToken: (t) => localStorage.setItem("supply_product_token", t),
        paths: { home: "/" },
      }}
    >
      {({ platformRoutes }) => (
        <Routes>
          {platformRoutes}
          <Route path="/" element={<Home />} />
          {/* …outras rotas */}
        </Routes>
      )}
    </LogPlatformAppShell>
  );
}
```

**Passo 4.** Confirma no Laravel que o prefixo (default `platform-supply-lib`) coincide com `supplyLibBasePath` se usares um path custom.

**Passo 5.** Se o teu app **já** tem `QueryClientProvider`, `TooltipProvider` ou `BrowserRouter` por cima, passa `wrapQueryClient={false}`, `wrapTooltipProvider={false}` e/ou `wrapBrowserRouter={false}` e mantém `LogPlatformAppShell` **dentro** dessa ordem (Router por fora do provider de auth da lib se desativares o router do shell).

### Opção B — Integração manual (sem shell)

**Passo 1.** No mesmo sítio onde montas a app com router — de novo, em geral **`src/App.tsx`** / **`src/App.jsx`** (ou um ficheiro dedicado tipo `src/routes/AppRoutes.tsx` que o `App` importa) — aplica a pilha abaixo.

**Passo 2.** Por fora para dentro: `QueryClientProvider` → `TooltipProvider` (Radix) → `BrowserRouter`.

**Passo 3.** Dentro do router, envolve com `LogPlatformAuthProvider` com as mesmas props `auth` descritas na Opção A.

**Passo 4.** Cria um componente filho que chama `useLogPlatformAuthRoutes()` **só dentro** do provider e, no JSX, dentro de `<Routes>`, renderiza `{platformRoutes}` junto com as tuas `<Route />`.

**Passo 5.** O React Router só aceita `<Route>` ou fragmento como filhos diretos de `<Routes>` — não substituas isto por um wrapper que não devolva rotas válidas.

---

## Parte 5 — Configurar o modal de troca de conta (system admin)

Serve para o administrador da plataforma escolher **em que conta cliente** quer atuar depois do login.

**Passo 1.** No `LogPlatformAuthProvider` (ou no objeto `auth` do `LogPlatformAppShell`), define `selectAccountPresentation`:

- `"none"` — após o login, vai direto à `home`; podes abrir o modal só quando quiseres (modo controlado).
- `"modal"` — após o login como system admin, a lib marca um flag em `sessionStorage` para o modal poder abrir na primeira página autenticada.

**Passo 2.** Coloca **`<SelectAccountModal />`** num layout que já tenha token válido e que seja renderizado quando o utilizador está autenticado (ex.: layout da área logada, não na página de login).

**Passo 3.** Se usaste `selectAccountPresentation="modal"`, garante que esse layout é montado depois do redirect do callback (a `home` ou rota inicial autenticada).

**Passo 4.** **Modo controlado (opcional):** para um botão “Trocar empresa”, usa estado local e passa `open` + `onOpenChange` ao `SelectAccountModal`; podes combinar com `useLogPlatformAuth()` para `isSystemAdmin`. Por defeito, após `join-account` bem-sucedido o modal dispara `window.location.reload()` (`reloadAfterJoin`, default `true`) para evitar dados da conta anterior em cache; usa `reloadAfterJoin={false}` se precisares do comportamento só-SPA.

**Passo 5.** Para obter o fluxo de contas (`join-account`, etc.), o backend já expõe as rotas da **log-platform-backend-lib**; o modal usa o cliente HTTP interno do contexto.

**Banner de impersonação:** coloca **`<SystemAdminImpersonationBanner />`** no topo do layout autenticado e passa `currentAccountId` (ex.: `user.account_id`) e, opcionalmente, `currentAccountLabel`. O backend **só** envia `system_admin_account` em `accounts-to-join` quando o token tem **`viewing_as`** (já noutra conta); na conta própria vem `null`. Por isso o provider aceita **`getFallbackSystemAdminHomeAccountId`** (ex.: devolver `user.account_id` do `/me` enquanto o admin está na conta agregadora) e persiste o ID em `sessionStorage` para o banner comparar antes/depois do join. Usa **`authRevision`** que mude com token **e** `account_id` do utilizador (ex.: após `getMe`) para voltar a sincronizar. **`isSystemAdminFromProduct`** continua necessário para login por email sem SSO.

---

## Parte 6 — Configurar o `ProductSelector`

Menu (geralmente na sidebar) para mudar de produto e, para system admin, ir ao hub da Plataforma.

**Passo 1.** O selector usa **React Query** — tem de estar dentro de `QueryClientProvider` (o `LogPlatformAppShell` já inclui um por defeito).

**Passo 2.** Coloca o `ProductSelector` **dentro** da árvore onde já existe `LogPlatformAuthProvider` (ou dentro do shell), para poderes **omitir** `platformBaseUrl` e `getAuthToken` (a lib usa `apiBaseUrl` e o token do contexto).

**Passo 3.** Passa no mínimo:

- `collapsed` — se a sidebar está colapsada (afeta tooltip vs texto completo).
- Se **não** estiveres dentro do provider: `platformBaseUrl` e `getAuthToken` obrigatórios.

**Passo 4.** Configura variáveis de ambiente conforme o teu cenário:

- URL pública do **hub** da Plataforma Supply (ex.: `VITE_SUPPLY_PLATFORM_APP_URL`) — prop `supplyPlatformAppBaseUrl` para o item “Plataforma Logcomex”.
- `hostAppBaseUrl` — URL deste SPA (ex.: `import.meta.env.VITE_APP_URL` ou `window.location.origin`) para alinhar seleção com o produto atual.

**Passo 5.** O pedido de lista de produtos é um `GET` para `{apiBaseUrl}/{prefixo}/user-products` (prefixo default `platform-supply-lib`), com `Authorization: Bearer` do token do produto. Se a tua API usar outro path, usa `userProductsListPath` ou `supplyLibBasePath`.

**Passo 6.** Implementa `onSelectInvalidUrl` para feedback quando um produto não tem URL ou falta token da plataforma para saltar de produto.

**Passo 7.** Garante que o fluxo SSO (Parte 4) grava o token da plataforma em `localStorage` após o callback — o selector usa isso para montar `.../auth/platform-callback?token=...` ao mudar de produto.

Exemplo mínimo com provider:

```tsx
<ProductSelector
  collapsed={sidebarCollapsed}
  hostAppBaseUrl={import.meta.env.VITE_APP_URL ?? window.location.origin}
  supplyPlatformAppBaseUrl={import.meta.env.VITE_SUPPLY_PLATFORM_APP_URL}
  onSelectInvalidUrl={() => toast.error("Não foi possível abrir o produto")}
/>
```

---

## Parte 7 — Logout (limpar sessão da lib)

**Passo 1.** Remove o token do produto (o mesmo que `getToken` lê / `setToken` grava).

**Passo 2.** Chama `clearPlatformToken()` de `useLogPlatformAuth()` (ou os helpers exportados que limpam o token da plataforma no `localStorage`).

**Passo 3.** Chama `setIsSystemAdmin(false)` e, se usaste o modal automático, remove a chave de `sessionStorage` associada ao “pending select account” (constante exportada `PENDING_SELECT_ACCOUNT_MODAL_KEY`).

---

## Apêndice A — CLI de dicas

```bash
npx log-platform-frontend-init
```

Imprime lembretes (Tailwind, i18n, shell); **não** modifica ficheiros.

---

## Apêndice B — Desenvolvimento local da lib

```bash
cd log-platform-frontend-lib
npm run build
npm link
# no app consumidor:
npm link @logcomex/log-platform-frontend-lib
```

Instalar a partir do Git: `npm install github:ORG/log-platform-frontend-lib#main`.

---

## Apêndice C — Prefixos das chaves i18n

| Área | Prefixo das chaves |
|------|---------------------|
| Selector | `logPlatform.productSelector.*` |
| Página de callback SSO | `logPlatform.platformAuth.callback.*` |
| Modal / painel de contas | `logPlatform.platformAuth.selectAccount.*` |