forlogic-core 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,258 +1,258 @@
1
- # Padrão: CoreProviders Setup
2
-
3
- ## Providers incluídos
4
-
5
- | Provider | Descrição |
6
- |----------|-----------|
7
- | ErrorBoundary | Captura erros de renderização React |
8
- | I18nextProvider | Sistema de internacionalização |
9
- | QueryClientProvider | Cache e queries (React Query) |
10
- | AuthProvider | Autenticação e sessão |
11
- | LocaleProvider | Idioma, timezone, formato de data |
12
- | ModuleProvider | Configuração de módulo ativo |
13
- | ModuleAccessGuard | Bloqueio de acesso por módulo |
14
-
15
- ## Uso básico
16
-
17
- ```tsx
18
- <CoreProviders moduleAlias="performance" appTranslations={{ 'pt-BR': ptBR }}>
19
- <BrowserRouter>
20
- <Routes />
21
- </BrowserRouter>
22
- </CoreProviders>
23
- ```
24
-
25
- ## Props
26
-
27
- | Prop | Tipo | Obrigatório | Descrição |
28
- |------|------|-------------|-----------|
29
- | children | ReactNode | Sim | Componentes filhos |
30
- | queryClient | QueryClient | Não | Instância customizada (default criado automaticamente) |
31
- | moduleAlias | string | Não | Alias do módulo para verificação de acesso |
32
- | moduleApiUrl | string | Não | (DEPRECATED — substituído por `associationsBaseUrl`) |
33
- | associationsBaseUrl | string | Não | Base URL completa para `/Users/{userId}/associations`. Ver seção abaixo. |
34
- | moduleAccessGuardProps | object | Não | Props para o ModuleAccessGuard |
35
- | appTranslations | Record<string, Record<string, string>> | Não | Traduções por idioma no namespace 'app' |
36
- | clarityProjectId | string | Não | Project ID do Microsoft Clarity. Sem ele, integração desativada. |
37
- | clarityMode | 'auto' \| 'disabled' | Não | Opt-out do tracking Clarity. Default `'auto'`. |
38
- | backend | 'supabase' \| 'dotnet' | Não | Backend ativo. Default `'supabase'`. Ver seção abaixo. |
39
- | signDocumentsSchema | string | Não | Schema onde a tabela de tracking do D4Sign vive neste projeto. Default `'common'`. Ver seção abaixo. |
40
- | signDocumentsTable | string | Não | Nome da tabela de tracking. Default `'sign_documents'`. |
41
-
42
- ## Tabela `sign_documents` por schema (D4Sign)
43
-
44
- Por padrão, o webhook D4Sign grava o resultado em `common.sign_documents`. Projetos que mantêm a tabela no próprio schema (ex.: Educação) podem redirecionar via:
45
-
46
- ```tsx
47
- <CoreProviders
48
- moduleAlias="educacao"
49
- signDocumentsSchema="educacao"
50
- // signDocumentsTable opcional — default 'sign_documents'
51
- >
52
- ...
53
- </CoreProviders>
54
- ```
55
-
56
- O que acontece:
57
-
58
- 1. `DocumentSigner` envia `target_schema` e `target_table` para a edge function `d4sign` no `create_and_send`.
59
- 2. A edge function `d4sign` insere a linha `pending` no schema/tabela alvo e registra o webhook do D4Sign com a URL `/functions/v1/d4sign-webhook?schema={schema}&table={table}`.
60
- 3. A edge function `d4sign-webhook` lê os query params, valida contra uma **allowlist** de schemas (`common`, `educacao`, `treinamentos`, `inovacao`, `public`) e faz o `UPDATE` no schema correto.
61
- 4. O front se inscreve no Realtime no mesmo schema/tabela.
62
-
63
- **Requisitos no projeto consumidor:**
64
-
65
- Cada projeto que redirecionar o destino precisa criar a tabela no seu próprio schema com **exatamente** o contrato abaixo — os nomes e tipos das colunas são consumidos pelas edge functions `d4sign` e `d4sign-webhook` e pelo `DocumentSigner`. Divergir quebra o fluxo.
66
-
67
- ### Contrato de campos
68
-
69
- | Campo | Tipo | Obrigatório | Default | Preenchido por |
70
- |---|---|---|---|---|
71
- | `document_id` | `text` PK | sim | — | front (`DocumentSigner`, `crypto.randomUUID()`) |
72
- | `alias` | `text` | sim | — | front |
73
- | `provider` | `text` | sim | `'d4sign'` | front |
74
- | `envelope_id` | `text` | sim | — | edge `d4sign` (UUID retornado pelo D4Sign) |
75
- | `signer_email` | `text` | sim | — | front |
76
- | `signer_name` | `text` | não | — | front |
77
- | `filename` | `text` | não | — | front |
78
- | `status` | `text` | sim | `'pending'` | webhook (`pending` → `signed` / `cancelled` / `failed`) |
79
- | `signed_at` | `timestamptz` | não | — | webhook |
80
- | `download_url` | `text` | não | — | webhook |
81
- | `download_url_expires_at` | `timestamptz` | não | — | webhook (~50 min) |
82
- | `raw_webhook_payload` | `jsonb` | não | — | webhook |
83
- | `created_at` | `timestamptz` | sim | `now()` | DB |
84
- | `updated_at` | `timestamptz` | sim | `now()` | trigger |
85
-
86
- > **PK em `document_id`** (não `id` serial) — é a chave que o front usa para correlacionar o envelope com a row.
87
-
88
- ### Script SQL (substituir `{schema}`)
89
-
90
- ```sql
91
- CREATE TABLE {schema}.sign_documents (
92
- document_id text PRIMARY KEY,
93
- alias text NOT NULL,
94
- provider text NOT NULL DEFAULT 'd4sign',
95
- envelope_id text NOT NULL,
96
- signer_email text NOT NULL,
97
- signer_name text,
98
- filename text,
99
- status text NOT NULL DEFAULT 'pending',
100
- download_url text,
101
- download_url_expires_at timestamptz,
102
- raw_webhook_payload jsonb,
103
- signed_at timestamptz,
104
- created_at timestamptz NOT NULL DEFAULT now(),
105
- updated_at timestamptz NOT NULL DEFAULT now()
106
- );
107
-
108
- CREATE INDEX sign_documents_alias_status_idx ON {schema}.sign_documents (alias, status);
109
- CREATE INDEX sign_documents_envelope_idx ON {schema}.sign_documents (envelope_id);
110
-
111
- GRANT SELECT ON {schema}.sign_documents TO authenticated;
112
- GRANT ALL ON {schema}.sign_documents TO service_role;
113
-
114
- ALTER TABLE {schema}.sign_documents ENABLE ROW LEVEL SECURITY;
115
-
116
- CREATE POLICY "sign_documents_select_own_tenant"
117
- ON {schema}.sign_documents
118
- FOR SELECT
119
- TO authenticated
120
- USING (alias = ((SELECT auth.jwt()) ->> 'alias'));
121
-
122
- CREATE OR REPLACE FUNCTION {schema}.sign_documents_set_updated_at()
123
- RETURNS TRIGGER LANGUAGE plpgsql SET search_path = {schema}, public AS $$
124
- BEGIN NEW.updated_at = now(); RETURN NEW; END;
125
- $$;
126
-
127
- CREATE TRIGGER sign_documents_updated_at
128
- BEFORE UPDATE ON {schema}.sign_documents
129
- FOR EACH ROW EXECUTE FUNCTION {schema}.sign_documents_set_updated_at();
130
-
131
- -- Realtime — obrigatório, senão o DocumentSigner nunca recebe o evento de assinatura
132
- ALTER TABLE {schema}.sign_documents REPLICA IDENTITY FULL;
133
- ALTER PUBLICATION supabase_realtime ADD TABLE {schema}.sign_documents;
134
- ```
135
-
136
- ### Notas importantes
137
-
138
- - **`sign_configs` continua em `common`** — o webhook lê de lá independentemente do schema alvo. Cada projeto que usa assinatura precisa ter `common.sign_configs` populado (via `/a/cs` no Admin).
139
- - **`REPLICA IDENTITY FULL` + entrada na publication `supabase_realtime`** são obrigatórios. Sem isso, o `DocumentSigner` fica em loading eterno após assinar.
140
- - **Schemas novos** precisam ser adicionados à constante `ALLOWED_SCHEMAS` em `supabase/functions/d4sign/index.ts` **e** `supabase/functions/d4sign-webhook/index.ts`, senão o webhook silenciosamente cai para `common` (proteção anti-SSRF).
141
-
142
- **Retrocompatibilidade:** sem essas props, tudo continua apontando para `common.sign_documents` — Admin segue funcionando inalterado.
143
-
144
-
145
- ## Associações em outra base URL
146
-
147
- Por padrão, `useModuleAccess` (e o `ModuleAccessGuard`) busca as associações em:
148
-
149
- ```
150
- {QUALIEX_API_URL}/api/common/v1/Users/{userId}/associations
151
- ```
152
-
153
- Quando o módulo expõe seu próprio endpoint de associações, informe `associationsBaseUrl` no `CoreProviders`. A lib passa a chamar:
154
-
155
- ```
156
- {associationsBaseUrl}/Users/{userId}/associations
157
- ```
158
-
159
- `associationsBaseUrl` deve incluir todo o prefixo (host + `/api/{modulo}/v1`):
160
-
161
- ```tsx
162
- <CoreProviders
163
- backend="dotnet"
164
- associationsBaseUrl={import.meta.env.VITE_ASSOCIATIONS_API_URL}
165
- // ex: "https://api.qualiex.com/api/inovacao/v1"
166
- >
167
- ...
168
- </CoreProviders>
169
- ```
170
-
171
- Características:
172
-
173
- - **Uma única variável** — não exige `moduleAlias`. Pode ser usada sem ativar o `ModuleAccessGuard` (útil enquanto o software do módulo ainda não foi cadastrado no `SOFTWARES_MAP` da lib).
174
- - A API alvo deve responder no mesmo contrato (`UserAssociation[]`) e aceitar os headers `Authorization: Bearer <token>` e `un-alias: <alias>`.
175
- - Demais endpoints (`/companiesusers`, `/softwares`) continuam consultando a common.
176
-
177
-
178
- ## Backend (Supabase opcional)
179
-
180
- A prop `backend` controla se a lib usa o stack Supabase ou apenas a API .NET do Qualiex.
181
-
182
- - `'supabase'` (default, retrocompat): inicializa o cliente Supabase, valida tokens via Edge Function `validate-token` e exige `SUPABASE_URL` + `SUPABASE_PUBLISHABLE_KEY` (ou as versões `VITE_SUPABASE_*` legadas) no `.env`. O `vite.config.ts` consolida ambos os formatos via `resolveSupabaseEnv()` — ver `docs/design-system/patterns/feature-flags.md`.
183
- - `'dotnet'`: a lib **não** chama Supabase em nenhum momento da inicialização. O `AuthProvider` autentica apenas com os tokens OAuth do Qualiex (`access_token` + `id_token`). Não exige envs do Supabase. **Importante:** defina `VITE_APP_ENV="DEV"` no `.env` quando o app rodar contra o ambiente dev do Qualiex — sem essa variável, o failsafe assume `PROD`.
184
-
185
- ### Exemplos de uso por backend
186
-
187
- ```tsx
188
- // ============================================================
189
- // Módulo COM Supabase (default — não precisa declarar backend)
190
- // ============================================================
191
- // .env obrigatório:
192
- // SUPABASE_URL="https://<project>.supabase.co"
193
- // SUPABASE_PUBLISHABLE_KEY="sb_publishable_..."
194
- // VITE_SUPABASE_PK_OVERRIDE="sb_publishable_..."
195
- //
196
- import { CoreProviders } from 'forlogic-core';
197
- import { BrowserRouter } from 'react-router';
198
- import pt from '@/locales/pt-BR.json';
199
-
200
- <CoreProviders moduleAlias="performance" appTranslations={{ 'pt-BR': pt }}>
201
- <BrowserRouter>
202
- <Routes />
203
- </BrowserRouter>
204
- </CoreProviders>
205
- ```
206
-
207
- ```tsx
208
- // ============================================================
209
- // Módulo SEM Supabase (ex.: Documentos, APIs próprias)
210
- // ============================================================
211
- // .env obrigatório:
212
- // VITE_APP_ENV="DEV" // ou "PROD"
213
- //
214
- // Nenhuma env do Supabase é exigida.
215
- //
216
- import { CoreProviders } from 'forlogic-core';
217
- import { BrowserRouter } from 'react-router';
218
- import pt from '@/locales/pt-BR.json';
219
-
220
- <CoreProviders
221
- backend="dotnet"
222
- moduleAlias="documents"
223
- appTranslations={{ 'pt-BR': pt }}
224
- >
225
- <BrowserRouter>
226
- <Routes />
227
- </BrowserRouter>
228
- </CoreProviders>
229
- ```
230
-
231
-
232
- ### O que funciona sem Supabase em `backend="dotnet"`
233
-
234
- A lib mantém os seguintes recursos funcionando 100% sem nenhuma env var Supabase:
235
-
236
- - **Logos e favicon** (`assets`, `logoSrc`, `smallLogoSrc`): servidos a partir de bucket público fixo embutido na lib.
237
- - **Login automático em dev/preview**: `shouldUseDevTokens()` agora exige modo Supabase, então em modo dotnet o `ProtectedRoute` sempre vai para `loginProd()` (OAuth real), mesmo em `localhost`/Lovable preview.
238
- - **Logoff manual**: `TokenManager.clearAll()` preserva o flag `manual_logout`, garantindo que `logout()` realmente deslogue (sem auto-login no próximo render).
239
- - **`LegacyKeyBanner`**: oculto automaticamente em modo dotnet.
240
-
241
- ### Features que continuam exigindo Supabase
242
-
243
- Mesmo com `backend="dotnet"`, se você importar qualquer um dos módulos abaixo eles vão falhar em runtime sem Supabase configurado:
244
-
245
- - `forlogic-core/sign`
246
- - `forlogic-core/audit-trail`
247
- - `forlogic-core/action-plans`
248
- - `forlogic-core/leadership`
249
- - `forlogic-core/places`
250
- - `EmailService` (do barrel principal)
251
-
252
- Esses módulos **foram removidos do barrel principal** (breaking change) — importe-os via subpath quando precisar. Use `isSupabaseConfigured()` para checar disponibilidade antes de chamar `getSupabaseClient()`.
253
-
254
- ## QueryClient default
255
-
256
- ```ts
257
- { defaultOptions: { queries: { staleTime: 5 * 60 * 1000, retry: 1 } } }
258
- ```
1
+ # Padrão: CoreProviders Setup
2
+
3
+ ## Providers incluídos
4
+
5
+ | Provider | Descrição |
6
+ |----------|-----------|
7
+ | ErrorBoundary | Captura erros de renderização React |
8
+ | I18nextProvider | Sistema de internacionalização |
9
+ | QueryClientProvider | Cache e queries (React Query) |
10
+ | AuthProvider | Autenticação e sessão |
11
+ | LocaleProvider | Idioma, timezone, formato de data |
12
+ | ModuleProvider | Configuração de módulo ativo |
13
+ | ModuleAccessGuard | Bloqueio de acesso por módulo |
14
+
15
+ ## Uso básico
16
+
17
+ ```tsx
18
+ <CoreProviders moduleAlias="performance" appTranslations={{ 'pt-BR': ptBR }}>
19
+ <BrowserRouter>
20
+ <Routes />
21
+ </BrowserRouter>
22
+ </CoreProviders>
23
+ ```
24
+
25
+ ## Props
26
+
27
+ | Prop | Tipo | Obrigatório | Descrição |
28
+ |------|------|-------------|-----------|
29
+ | children | ReactNode | Sim | Componentes filhos |
30
+ | queryClient | QueryClient | Não | Instância customizada (default criado automaticamente) |
31
+ | moduleAlias | string | Não | Alias do módulo para verificação de acesso |
32
+ | moduleApiUrl | string | Não | (DEPRECATED — substituído por `associationsBaseUrl`) |
33
+ | associationsBaseUrl | string | Não | Base URL completa para `/Users/{userId}/associations`. Ver seção abaixo. |
34
+ | moduleAccessGuardProps | object | Não | Props para o ModuleAccessGuard |
35
+ | appTranslations | Record<string, Record<string, string>> | Não | Traduções por idioma no namespace 'app' |
36
+ | clarityProjectId | string | Não | Project ID do Microsoft Clarity. Sem ele, integração desativada. |
37
+ | clarityMode | 'auto' \| 'disabled' | Não | Opt-out do tracking Clarity. Default `'auto'`. |
38
+ | backend | 'supabase' \| 'dotnet' | Não | Backend ativo. Default `'supabase'`. Ver seção abaixo. |
39
+ | signDocumentsSchema | string | Não | Schema onde a tabela de tracking do D4Sign vive neste projeto. Default `'common'`. Ver seção abaixo. |
40
+ | signDocumentsTable | string | Não | Nome da tabela de tracking. Default `'sign_documents'`. |
41
+
42
+ ## Tabela `sign_documents` por schema (D4Sign)
43
+
44
+ Por padrão, o webhook D4Sign grava o resultado em `common.sign_documents`. Projetos que mantêm a tabela no próprio schema (ex.: Educação) podem redirecionar via:
45
+
46
+ ```tsx
47
+ <CoreProviders
48
+ moduleAlias="educacao"
49
+ signDocumentsSchema="educacao"
50
+ // signDocumentsTable opcional — default 'sign_documents'
51
+ >
52
+ ...
53
+ </CoreProviders>
54
+ ```
55
+
56
+ O que acontece:
57
+
58
+ 1. `DocumentSigner` envia `target_schema` e `target_table` para a edge function `d4sign` no `create_and_send`.
59
+ 2. A edge function `d4sign` insere a linha `pending` no schema/tabela alvo e registra o webhook do D4Sign com a URL `/functions/v1/d4sign-webhook?schema={schema}&table={table}`.
60
+ 3. A edge function `d4sign-webhook` lê os query params, valida contra uma **allowlist** de schemas (`common`, `educacao`, `treinamentos`, `inovacao`, `public`) e faz o `UPDATE` no schema correto.
61
+ 4. O front se inscreve no Realtime no mesmo schema/tabela.
62
+
63
+ **Requisitos no projeto consumidor:**
64
+
65
+ Cada projeto que redirecionar o destino precisa criar a tabela no seu próprio schema com **exatamente** o contrato abaixo — os nomes e tipos das colunas são consumidos pelas edge functions `d4sign` e `d4sign-webhook` e pelo `DocumentSigner`. Divergir quebra o fluxo.
66
+
67
+ ### Contrato de campos
68
+
69
+ | Campo | Tipo | Obrigatório | Default | Preenchido por |
70
+ |---|---|---|---|---|
71
+ | `document_id` | `text` PK | sim | — | front (`DocumentSigner`, `crypto.randomUUID()`) |
72
+ | `alias` | `text` | sim | — | front |
73
+ | `provider` | `text` | sim | `'d4sign'` | front |
74
+ | `envelope_id` | `text` | sim | — | edge `d4sign` (UUID retornado pelo D4Sign) |
75
+ | `signer_email` | `text` | sim | — | front |
76
+ | `signer_name` | `text` | não | — | front |
77
+ | `filename` | `text` | não | — | front |
78
+ | `status` | `text` | sim | `'pending'` | webhook (`pending` → `signed` / `cancelled` / `failed`) |
79
+ | `signed_at` | `timestamptz` | não | — | webhook |
80
+ | `download_url` | `text` | não | — | webhook |
81
+ | `download_url_expires_at` | `timestamptz` | não | — | webhook (~50 min) |
82
+ | `raw_webhook_payload` | `jsonb` | não | — | webhook |
83
+ | `created_at` | `timestamptz` | sim | `now()` | DB |
84
+ | `updated_at` | `timestamptz` | sim | `now()` | trigger |
85
+
86
+ > **PK em `document_id`** (não `id` serial) — é a chave que o front usa para correlacionar o envelope com a row.
87
+
88
+ ### Script SQL (substituir `{schema}`)
89
+
90
+ ```sql
91
+ CREATE TABLE {schema}.sign_documents (
92
+ document_id text PRIMARY KEY,
93
+ alias text NOT NULL,
94
+ provider text NOT NULL DEFAULT 'd4sign',
95
+ envelope_id text NOT NULL,
96
+ signer_email text NOT NULL,
97
+ signer_name text,
98
+ filename text,
99
+ status text NOT NULL DEFAULT 'pending',
100
+ download_url text,
101
+ download_url_expires_at timestamptz,
102
+ raw_webhook_payload jsonb,
103
+ signed_at timestamptz,
104
+ created_at timestamptz NOT NULL DEFAULT now(),
105
+ updated_at timestamptz NOT NULL DEFAULT now()
106
+ );
107
+
108
+ CREATE INDEX sign_documents_alias_status_idx ON {schema}.sign_documents (alias, status);
109
+ CREATE INDEX sign_documents_envelope_idx ON {schema}.sign_documents (envelope_id);
110
+
111
+ GRANT SELECT ON {schema}.sign_documents TO authenticated;
112
+ GRANT ALL ON {schema}.sign_documents TO service_role;
113
+
114
+ ALTER TABLE {schema}.sign_documents ENABLE ROW LEVEL SECURITY;
115
+
116
+ CREATE POLICY "sign_documents_select_own_tenant"
117
+ ON {schema}.sign_documents
118
+ FOR SELECT
119
+ TO authenticated
120
+ USING (alias = ((SELECT auth.jwt()) ->> 'alias'));
121
+
122
+ CREATE OR REPLACE FUNCTION {schema}.sign_documents_set_updated_at()
123
+ RETURNS TRIGGER LANGUAGE plpgsql SET search_path = {schema}, public AS $$
124
+ BEGIN NEW.updated_at = now(); RETURN NEW; END;
125
+ $$;
126
+
127
+ CREATE TRIGGER sign_documents_updated_at
128
+ BEFORE UPDATE ON {schema}.sign_documents
129
+ FOR EACH ROW EXECUTE FUNCTION {schema}.sign_documents_set_updated_at();
130
+
131
+ -- Realtime — obrigatório, senão o DocumentSigner nunca recebe o evento de assinatura
132
+ ALTER TABLE {schema}.sign_documents REPLICA IDENTITY FULL;
133
+ ALTER PUBLICATION supabase_realtime ADD TABLE {schema}.sign_documents;
134
+ ```
135
+
136
+ ### Notas importantes
137
+
138
+ - **`sign_configs` continua em `common`** — o webhook lê de lá independentemente do schema alvo. Cada projeto que usa assinatura precisa ter `common.sign_configs` populado (via `/a/cs` no Admin).
139
+ - **`REPLICA IDENTITY FULL` + entrada na publication `supabase_realtime`** são obrigatórios. Sem isso, o `DocumentSigner` fica em loading eterno após assinar.
140
+ - **Schemas novos** precisam ser adicionados à constante `ALLOWED_SCHEMAS` em `supabase/functions/d4sign/index.ts` **e** `supabase/functions/d4sign-webhook/index.ts`, senão o webhook silenciosamente cai para `common` (proteção anti-SSRF).
141
+
142
+ **Retrocompatibilidade:** sem essas props, tudo continua apontando para `common.sign_documents` — Admin segue funcionando inalterado.
143
+
144
+
145
+ ## Associações em outra base URL
146
+
147
+ Por padrão, `useModuleAccess` (e o `ModuleAccessGuard`) busca as associações em:
148
+
149
+ ```
150
+ {QUALIEX_API_URL}/api/common/v1/Users/{userId}/associations
151
+ ```
152
+
153
+ Quando o módulo expõe seu próprio endpoint de associações, informe `associationsBaseUrl` no `CoreProviders`. A lib passa a chamar:
154
+
155
+ ```
156
+ {associationsBaseUrl}/Users/{userId}/associations
157
+ ```
158
+
159
+ `associationsBaseUrl` deve incluir todo o prefixo (host + `/api/{modulo}/v1`):
160
+
161
+ ```tsx
162
+ <CoreProviders
163
+ backend="dotnet"
164
+ associationsBaseUrl={import.meta.env.VITE_ASSOCIATIONS_API_URL}
165
+ // ex: "https://api.qualiex.com/api/inovacao/v1"
166
+ >
167
+ ...
168
+ </CoreProviders>
169
+ ```
170
+
171
+ Características:
172
+
173
+ - **Uma única variável** — não exige `moduleAlias`. Pode ser usada sem ativar o `ModuleAccessGuard` (útil enquanto o software do módulo ainda não foi cadastrado no `SOFTWARES_MAP` da lib).
174
+ - A API alvo deve responder no mesmo contrato (`UserAssociation[]`) e aceitar os headers `Authorization: Bearer <token>` e `un-alias: <alias>`.
175
+ - Demais endpoints (`/companiesusers`, `/softwares`) continuam consultando a common.
176
+
177
+
178
+ ## Backend (Supabase opcional)
179
+
180
+ A prop `backend` controla se a lib usa o stack Supabase ou apenas a API .NET do Qualiex.
181
+
182
+ - `'supabase'` (default, retrocompat): inicializa o cliente Supabase, valida tokens via Edge Function `validate-token` e exige `SUPABASE_URL` + `SUPABASE_PUBLISHABLE_KEY` (ou as versões `VITE_SUPABASE_*` legadas) no `.env`. O `vite.config.ts` consolida ambos os formatos via `resolveSupabaseEnv()` — ver `docs/design-system/patterns/feature-flags.md`.
183
+ - `'dotnet'`: a lib **não** chama Supabase em nenhum momento da inicialização. O `AuthProvider` autentica apenas com os tokens OAuth do Qualiex (`access_token` + `id_token`). Não exige envs do Supabase. **Importante:** defina `VITE_APP_ENV="DEV"` no `.env` quando o app rodar contra o ambiente dev do Qualiex — sem essa variável, o failsafe assume `PROD`.
184
+
185
+ ### Exemplos de uso por backend
186
+
187
+ ```tsx
188
+ // ============================================================
189
+ // Módulo COM Supabase (default — não precisa declarar backend)
190
+ // ============================================================
191
+ // .env obrigatório:
192
+ // SUPABASE_URL="https://<project>.supabase.co"
193
+ // SUPABASE_PUBLISHABLE_KEY="sb_publishable_..."
194
+ // VITE_SUPABASE_PK_OVERRIDE="sb_publishable_..."
195
+ //
196
+ import { CoreProviders } from 'forlogic-core';
197
+ import { BrowserRouter } from 'react-router';
198
+ import pt from '@/locales/pt-BR.json';
199
+
200
+ <CoreProviders moduleAlias="performance" appTranslations={{ 'pt-BR': pt }}>
201
+ <BrowserRouter>
202
+ <Routes />
203
+ </BrowserRouter>
204
+ </CoreProviders>
205
+ ```
206
+
207
+ ```tsx
208
+ // ============================================================
209
+ // Módulo SEM Supabase (ex.: Documentos, APIs próprias)
210
+ // ============================================================
211
+ // .env obrigatório:
212
+ // VITE_APP_ENV="DEV" // ou "PROD"
213
+ //
214
+ // Nenhuma env do Supabase é exigida.
215
+ //
216
+ import { CoreProviders } from 'forlogic-core';
217
+ import { BrowserRouter } from 'react-router';
218
+ import pt from '@/locales/pt-BR.json';
219
+
220
+ <CoreProviders
221
+ backend="dotnet"
222
+ moduleAlias="documents"
223
+ appTranslations={{ 'pt-BR': pt }}
224
+ >
225
+ <BrowserRouter>
226
+ <Routes />
227
+ </BrowserRouter>
228
+ </CoreProviders>
229
+ ```
230
+
231
+
232
+ ### O que funciona sem Supabase em `backend="dotnet"`
233
+
234
+ A lib mantém os seguintes recursos funcionando 100% sem nenhuma env var Supabase:
235
+
236
+ - **Logos e favicon** (`assets`, `logoSrc`, `smallLogoSrc`): servidos a partir de bucket público fixo embutido na lib.
237
+ - **Login automático em dev/preview**: `shouldUseDevTokens()` agora exige modo Supabase, então em modo dotnet o `ProtectedRoute` sempre vai para `loginProd()` (OAuth real), mesmo em `localhost`/Lovable preview.
238
+ - **Logoff manual**: `TokenManager.clearAll()` preserva o flag `manual_logout`, garantindo que `logout()` realmente deslogue (sem auto-login no próximo render).
239
+ - **`LegacyKeyBanner`**: oculto automaticamente em modo dotnet.
240
+
241
+ ### Features que continuam exigindo Supabase
242
+
243
+ Mesmo com `backend="dotnet"`, se você importar qualquer um dos módulos abaixo eles vão falhar em runtime sem Supabase configurado:
244
+
245
+ - `forlogic-core/sign`
246
+ - `forlogic-core/audit-trail`
247
+ - `forlogic-core/action-plans`
248
+ - `forlogic-core/leadership`
249
+ - `forlogic-core/places`
250
+ - `EmailService` (do barrel principal)
251
+
252
+ Esses módulos **foram removidos do barrel principal** (breaking change) — importe-os via subpath quando precisar. Use `isSupabaseConfigured()` para checar disponibilidade antes de chamar `getSupabaseClient()`.
253
+
254
+ ## QueryClient default
255
+
256
+ ```ts
257
+ { defaultOptions: { queries: { staleTime: 5 * 60 * 1000, retry: 1 } } }
258
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forlogic-core",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",