forlogic-core 1.5.0 → 1.5.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.
- package/README.md +21 -1116
- package/dist/README.md +248 -0
- package/dist/docs/AI_RULES.md +213 -0
- package/dist/docs/README.md +102 -0
- package/dist/docs/TROUBLESHOOTING.md +473 -0
- package/dist/docs/architecture/TOKENS_ARCHITECTURE.md +712 -0
- package/dist/docs/templates/app-layout.tsx +192 -0
- package/dist/docs/templates/basic-crud-page.tsx +97 -0
- package/dist/docs/templates/basic-crud-working.tsx +182 -0
- package/dist/docs/templates/complete-crud-example.tsx +307 -0
- package/dist/docs/templates/custom-form.tsx +99 -0
- package/dist/docs/templates/custom-service.tsx +194 -0
- package/dist/docs/templates/permission-check-hook.tsx +275 -0
- package/dist/docs/templates/qualiex-config.ts +137 -0
- package/dist/docs/templates/qualiex-integration-example.tsx +430 -0
- package/dist/docs/templates/quick-start-example.tsx +96 -0
- package/dist/docs/templates/rls-policies.sql +80 -0
- package/dist/docs/templates/sidebar-config.tsx +145 -0
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -1
- package/dist/assets/index-C_ZLBeXY.css +0 -1
- package/dist/assets/index-wEAMQwsw.js +0 -7898
- package/dist/index.html +0 -19
package/README.md
CHANGED
|
@@ -1,1131 +1,36 @@
|
|
|
1
|
-
# 🧱
|
|
1
|
+
# 🧱 Documentação exclusiva para este projeto
|
|
2
2
|
|
|
3
|
-
> **
|
|
4
|
-
|
|
5
|
-
[](https://badge.fury.io/js/%40qualiex%2Fcore)
|
|
6
|
-
[](https://www.typescriptlang.org/)
|
|
7
|
-
[](https://reactjs.org/)
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## 📦 Instalação
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
npm install forlogic-core
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
### Dependências Peer (Obrigatórias)
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm install react react-dom react-router-dom
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
### Configuração Inicial
|
|
24
|
-
|
|
25
|
-
```tsx
|
|
26
|
-
// main.tsx ou App.tsx
|
|
27
|
-
import { setupQualiexCore } from 'forlogic-core';
|
|
28
|
-
import 'forlogic-core/index.css';
|
|
29
|
-
|
|
30
|
-
setupQualiexCore({
|
|
31
|
-
supabaseUrl: 'your-supabase-url',
|
|
32
|
-
supabaseKey: 'your-supabase-anon-key',
|
|
33
|
-
qualiexBaseUrl: 'your-qualiex-url'
|
|
34
|
-
});
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### Uso Básico
|
|
38
|
-
|
|
39
|
-
```tsx
|
|
40
|
-
import { createSimpleService, createCrudPage, generateCrudConfig } from 'forlogic-core';
|
|
41
|
-
|
|
42
|
-
// 1. Create service
|
|
43
|
-
const service = createSimpleService<MyEntity>({ tableName: 'my_entities' });
|
|
44
|
-
|
|
45
|
-
// 2. Generate config
|
|
46
|
-
const config = generateCrudConfig('My Entities', { name: '', email: '' });
|
|
47
|
-
|
|
48
|
-
// 3. Create page
|
|
49
|
-
export const MyPage = createCrudPage({ service, config });
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## 🚀 Stack Tecnológico
|
|
55
|
-
|
|
56
|
-
| Tecnologia | Versão | Propósito |
|
|
57
|
-
|------------|---------|-----------|
|
|
58
|
-
| **React** | 18+ | Framework UI |
|
|
59
|
-
| **TypeScript** | 5+ | Type Safety |
|
|
60
|
-
| **Tailwind CSS** | 3+ | Styling System |
|
|
61
|
-
| **Supabase** | 2+ | Backend BaaS |
|
|
62
|
-
| **React Query** | 5+ | Estado e Cache |
|
|
63
|
-
| **React Hook Form** | 7+ | Formulários |
|
|
64
|
-
| **Zod** | 3+ | Validação |
|
|
65
|
-
| **shadcn/ui** | Latest | Componentes Base |
|
|
66
|
-
| **Radix UI** | Latest | Primitivos Acessíveis |
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## 🏗️ Arquitetura do Projeto
|
|
71
|
-
|
|
72
|
-
### Sistema de Importação Direta (Zero Barrel Exports)
|
|
73
|
-
|
|
74
|
-
```
|
|
75
|
-
forlogic-core/
|
|
76
|
-
├── 🔐 auth/ # Sistema de Autenticação
|
|
77
|
-
│ ├── components/
|
|
78
|
-
│ │ ├── ProtectedRoute.tsx # Rota protegida por auth
|
|
79
|
-
│ │ └── UserInfo.tsx # Informações do usuário
|
|
80
|
-
│ ├── contexts/
|
|
81
|
-
│ │ └── AuthContext.tsx # Context de autenticação
|
|
82
|
-
│ ├── pages/
|
|
83
|
-
│ │ └── CallbackPage.tsx # Callback OAuth
|
|
84
|
-
│ └── services/
|
|
85
|
-
│ ├── AuthService.ts # Lógica de autenticação
|
|
86
|
-
│ └── TokenManager.ts # Gestão de tokens JWT
|
|
87
|
-
│
|
|
88
|
-
├── 🗂️ crud/ # Sistema CRUD Genérico
|
|
89
|
-
│ ├── components/
|
|
90
|
-
│ │ ├── ActionMenuItems.tsx # Itens do menu de ações
|
|
91
|
-
│ │ ├── BaseForm.tsx # Formulário base configurável
|
|
92
|
-
│ │ ├── CrudCards.tsx # Cards responsivos para mobile
|
|
93
|
-
│ │ ├── CrudForm.tsx # Wrapper CRUD simples
|
|
94
|
-
│ │ ├── CrudTable.tsx # Tabela CRUD responsiva
|
|
95
|
-
│ │ ├── FilterBar.tsx # Barra de filtros e busca
|
|
96
|
-
│ │ ├── TableFooter.tsx # Rodapé com paginação
|
|
97
|
-
│ │ └── TableRowActions.tsx # Ações de linha da tabela
|
|
98
|
-
│ └── hooks/
|
|
99
|
-
│ ├── useCrud.ts # Hook principal para CRUD
|
|
100
|
-
│ └── useBaseForm.ts # Hook para formulários
|
|
101
|
-
│
|
|
102
|
-
├── 🎨 components/ # Componentes Globais
|
|
103
|
-
│ ├── layout/
|
|
104
|
-
│ │ ├── AppHeader.tsx # Cabeçalho da aplicação
|
|
105
|
-
│ │ └── AppSidebar.tsx # Sidebar de navegação
|
|
106
|
-
│ └── ui/ # Componentes UI (shadcn/ui)
|
|
107
|
-
│ ├── button.tsx # Sistema de botões
|
|
108
|
-
│ ├── card.tsx # Sistema de cards
|
|
109
|
-
│ ├── form.tsx # Componentes de formulário
|
|
110
|
-
│ ├── input.tsx # Inputs customizados
|
|
111
|
-
│ ├── table.tsx # Tabelas responsivas
|
|
112
|
-
│ ├── toast.tsx # Notificações toast
|
|
113
|
-
│ └── [50+ componentes] # Biblioteca completa
|
|
114
|
-
│
|
|
115
|
-
├── 🔌 supabase/ # Configuração Supabase da Lib
|
|
116
|
-
│ ├── SupabaseSingleton.ts # Cliente Singleton customizado
|
|
117
|
-
│ └── types.ts # Tipos do banco de dados
|
|
118
|
-
│ └── qualiex/
|
|
119
|
-
│ ├── components/
|
|
120
|
-
│ │ ├── QualiexUserField.tsx # Campo de usuário Qualiex
|
|
121
|
-
│ │ └── QualiexResponsibleSelectField.tsx
|
|
122
|
-
│ ├── services/
|
|
123
|
-
│ │ └── qualiexApi.ts # API client Qualiex
|
|
124
|
-
│ ├── hooks/
|
|
125
|
-
│ │ └── useQualiexUsers.ts # Hook para usuários
|
|
126
|
-
│ └── types/
|
|
127
|
-
│ └── qualiexTypes.ts # Tipos da API Qualiex
|
|
128
|
-
│
|
|
129
|
-
├── 🔧 services/ # Serviços Base
|
|
130
|
-
│ ├── BaseService.ts # Service CRUD genérico
|
|
131
|
-
│ ├── ErrorService.ts # Tratamento de erros
|
|
132
|
-
│ └── QualiexEnrichmentService.ts # Enriquecimento de dados
|
|
133
|
-
│
|
|
134
|
-
├── 🧰 hooks/ # Hooks Globais
|
|
135
|
-
│ ├── useActiveModules.ts # Módulos ativos
|
|
136
|
-
│ ├── useDebounce.ts # Debounce de busca
|
|
137
|
-
│ ├── usePageTitle.ts # Título da página
|
|
138
|
-
│ └── use-toast.ts # Sistema de toast
|
|
139
|
-
│
|
|
140
|
-
├── 🛠️ utils/ # Utilitários
|
|
141
|
-
│ └── index.ts # Funções auxiliares
|
|
142
|
-
│
|
|
143
|
-
├── ⚙️ config/ # Configurações
|
|
144
|
-
│ └── index.ts # Config centralizada
|
|
145
|
-
│
|
|
146
|
-
└── 📄 types.ts # Tipos Globais
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
**✅ Princípios**: Imports diretos, componentes pequenos, zero over-engineering
|
|
150
|
-
|
|
151
|
-
### 🔧 Configuração Customizada do Supabase
|
|
152
|
-
|
|
153
|
-
Este projeto utiliza uma configuração customizada do Supabase através do `SupabaseSingleton` ao invés do cliente padrão do Lovable.
|
|
154
|
-
|
|
155
|
-
**📖 Documentação completa**: [Arquitetura Supabase da Lib](./docs/SUPABASE_LIB_ARCHITECTURE.md)
|
|
156
|
-
|
|
157
|
-
**Vantagens:**
|
|
158
|
-
- ✅ Gestão automática de tokens JWT
|
|
159
|
-
- ✅ Integração com sistema multi-tenant (alias)
|
|
160
|
-
- ✅ Performance otimizada com Singleton Pattern
|
|
161
|
-
- ✅ Headers dinâmicos para autenticação
|
|
162
|
-
- ✅ Compatibilidade com sistema de tokens triplos
|
|
163
|
-
|
|
164
|
-
```tsx
|
|
165
|
-
// ✅ Forma correta de usar o Supabase
|
|
166
|
-
import { getSupabaseClient } from '@/lib/supabase/SupabaseSingleton';
|
|
167
|
-
|
|
168
|
-
const supabase = getSupabaseClient();
|
|
169
|
-
```
|
|
3
|
+
> **Sistema empresarial com React + TypeScript + Supabase**
|
|
170
4
|
|
|
171
5
|
---
|
|
172
6
|
|
|
173
|
-
##
|
|
174
|
-
|
|
175
|
-
### Arquitetura de Tokens JWT
|
|
176
|
-
|
|
177
|
-
```mermaid
|
|
178
|
-
sequenceDiagram
|
|
179
|
-
participant U as Usuário
|
|
180
|
-
participant Q as Qualiex OAuth
|
|
181
|
-
participant E as Edge Function
|
|
182
|
-
participant S as Supabase
|
|
183
|
-
participant A as App
|
|
184
|
-
|
|
185
|
-
U->>Q: Login OAuth
|
|
186
|
-
Q->>U: access_token + id_token
|
|
187
|
-
U->>E: validate-token(access_token)
|
|
188
|
-
E->>Q: Valida token
|
|
189
|
-
E->>S: Gera supabase_token
|
|
190
|
-
E->>U: supabase_token
|
|
191
|
-
U->>A: Login com 3 tokens
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### Tokens Gerenciados
|
|
195
|
-
|
|
196
|
-
| Token | Função | Storage | Duração |
|
|
197
|
-
|-------|--------|---------|---------|
|
|
198
|
-
| `access_token` | API Qualiex | localStorage | 1h |
|
|
199
|
-
| `id_token` | Dados do usuário | localStorage | 1h |
|
|
200
|
-
| `supabase_token` | Acesso RLS | localStorage | 1h |
|
|
201
|
-
| `refresh_token` | Renovação | httpOnly cookie | 7d |
|
|
202
|
-
|
|
203
|
-
### Edge Functions
|
|
204
|
-
|
|
205
|
-
```
|
|
206
|
-
supabase/functions/
|
|
207
|
-
├── validate-token/ # Produção: Validação completa
|
|
208
|
-
│ └── index.ts # - Valida no Qualiex
|
|
209
|
-
│ # - Gera token Supabase
|
|
210
|
-
│ # - Retorna dados do usuário
|
|
211
|
-
└── dev-tokens/ # Desenvolvimento: Tokens mock
|
|
212
|
-
└── index.ts # - Bypass OAuth em dev
|
|
213
|
-
# - Gera tokens válidos
|
|
214
|
-
# - Acelera desenvolvimento
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### Fluxo de Unidades/Empresas
|
|
218
|
-
|
|
219
|
-
```tsx
|
|
220
|
-
// Contexto de autenticação com multi-tenancy
|
|
221
|
-
const { user, companies, selectedUnit, switchUnit } = useAuth();
|
|
222
|
-
|
|
223
|
-
// Trocar entre empresas do usuário
|
|
224
|
-
await switchUnit(companyAlias);
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
**🔒 Segurança**: RLS por alias + JWT claims + refresh automático
|
|
228
|
-
|
|
229
|
-
---
|
|
230
|
-
|
|
231
|
-
## 🗄️ Banco de Dados e RLS
|
|
232
|
-
|
|
233
|
-
### Estrutura Padrão de Tabela
|
|
234
|
-
|
|
235
|
-
```sql
|
|
236
|
-
-- Tabela modelo compatível com o sistema
|
|
237
|
-
CREATE TABLE schema_module.your_table (
|
|
238
|
-
-- 🔑 Campos obrigatórios do sistema
|
|
239
|
-
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
240
|
-
alias TEXT NOT NULL, -- Multi-tenancy RLS
|
|
241
|
-
|
|
242
|
-
-- 📝 Campos de negócio (personalizáveis)
|
|
243
|
-
title TEXT NOT NULL,
|
|
244
|
-
description TEXT,
|
|
245
|
-
url_field TEXT,
|
|
246
|
-
date_field TIMESTAMP WITH TIME ZONE,
|
|
247
|
-
color TEXT,
|
|
248
|
-
icon_name TEXT,
|
|
249
|
-
id_user TEXT, -- ID responsável (Qualiex)
|
|
250
|
-
|
|
251
|
-
-- 🎛️ Campos de controle (obrigatórios)
|
|
252
|
-
is_actived BOOLEAN DEFAULT true, -- Status ativo/inativo
|
|
253
|
-
is_removed BOOLEAN DEFAULT false, -- Soft delete
|
|
254
|
-
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
|
255
|
-
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
-- 🔒 Trigger para updated_at
|
|
259
|
-
CREATE TRIGGER update_your_table_updated_at
|
|
260
|
-
BEFORE UPDATE ON schema_module.your_table
|
|
261
|
-
FOR EACH ROW
|
|
262
|
-
EXECUTE FUNCTION schema_module.update_updated_at_column();
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
### Políticas RLS Multi-Tenant
|
|
266
|
-
|
|
267
|
-
```sql
|
|
268
|
-
-- 🔒 Habilitar Row Level Security
|
|
269
|
-
ALTER TABLE schema_module.your_table ENABLE ROW LEVEL SECURITY;
|
|
7
|
+
## ⚙️ Configuração deste Projeto
|
|
270
8
|
|
|
271
|
-
|
|
272
|
-
CREATE POLICY "Users can view company data" ON schema_module.your_table
|
|
273
|
-
FOR SELECT USING (
|
|
274
|
-
((SELECT auth.jwt()) ->> 'alias') = alias
|
|
275
|
-
);
|
|
276
|
-
|
|
277
|
-
-- ➕ INSERT: Inserir com alias da empresa
|
|
278
|
-
CREATE POLICY "Users can insert company data" ON schema_module.your_table
|
|
279
|
-
FOR INSERT WITH CHECK (
|
|
280
|
-
((SELECT auth.jwt()) ->> 'alias') = alias
|
|
281
|
-
);
|
|
282
|
-
|
|
283
|
-
-- ✏️ UPDATE: Atualizar dados da empresa
|
|
284
|
-
CREATE POLICY "Users can update company data" ON schema_module.your_table
|
|
285
|
-
FOR UPDATE USING (
|
|
286
|
-
((SELECT auth.jwt()) ->> 'alias') = alias
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
-- ⚠️ IMPORTANTE: NÃO criar política DELETE
|
|
290
|
-
-- Sistema usa soft delete (is_removed = true)
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
### Template SQL Completo
|
|
294
|
-
|
|
295
|
-
```sql
|
|
296
|
-
-- Copie este template para criar novas tabelas
|
|
297
|
-
\i 'docs/templates/rls-policies.sql'
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
**🛡️ Segurança**: Isolamento automático por empresa via RLS
|
|
301
|
-
|
|
302
|
-
---
|
|
303
|
-
|
|
304
|
-
## 🧠 Sistema CRUD Simplificado
|
|
305
|
-
|
|
306
|
-
### BaseService - CRUD Automático
|
|
307
|
-
|
|
308
|
-
```typescript
|
|
309
|
-
// Criar service em 3 linhas
|
|
310
|
-
import { createService } from 'forlogic-core';
|
|
311
|
-
import type { MyEntity } from '@/types';
|
|
312
|
-
|
|
313
|
-
export const MyService = createService<MyEntity>({
|
|
314
|
-
tableName: 'my_table',
|
|
315
|
-
searchFields: ['title', 'description'], // Campos de busca
|
|
316
|
-
schemaName: 'schema_module', // Schema do banco
|
|
317
|
-
entityName: 'MinhaEntidade' // Para enrichment Qualiex
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
// Pronto! Service com todas as operações CRUD:
|
|
321
|
-
// - MyService.getAll() - Listar com paginação e busca
|
|
322
|
-
// - MyService.create() - Criar com RLS automático
|
|
323
|
-
// - MyService.update() - Atualizar com validação
|
|
324
|
-
// - MyService.delete() - Soft delete (is_removed = true)
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
### Hook CRUD - Gerenciamento de Estado
|
|
328
|
-
|
|
329
|
-
```tsx
|
|
330
|
-
import { useCrud } from 'forlogic-core';
|
|
331
|
-
|
|
332
|
-
function MyComponent() {
|
|
333
|
-
const manager = useCrud({
|
|
334
|
-
queryKey: 'my-entities', // Cache key única
|
|
335
|
-
service: MyService, // Service criado acima
|
|
336
|
-
entityName: 'Item', // Nome singular
|
|
337
|
-
pageSize: 25 // Itens por página (opcional)
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
// Estado disponível:
|
|
341
|
-
const {
|
|
342
|
-
entities, // Dados da página atual
|
|
343
|
-
pagination, // Info de paginação
|
|
344
|
-
isLoading, // Estado de carregamento
|
|
345
|
-
searchTerm, // Termo de busca atual
|
|
346
|
-
|
|
347
|
-
// Operações:
|
|
348
|
-
createEntity, // Criar novo
|
|
349
|
-
updateEntity, // Atualizar existente
|
|
350
|
-
deleteEntity, // Soft delete
|
|
351
|
-
refetch, // Recarregar dados
|
|
352
|
-
handleSearch, // Buscar (debounced)
|
|
353
|
-
handlePageChange, // Mudar página
|
|
354
|
-
} = manager;
|
|
355
|
-
|
|
356
|
-
return (
|
|
357
|
-
<div>
|
|
358
|
-
<input
|
|
359
|
-
onChange={(e) => handleSearch(e.target.value)}
|
|
360
|
-
placeholder="Buscar..."
|
|
361
|
-
/>
|
|
362
|
-
|
|
363
|
-
{entities.map(item => (
|
|
364
|
-
<div key={item.id}>
|
|
365
|
-
<h3>{item.title}</h3>
|
|
366
|
-
<button onClick={() => deleteEntity(item.id)}>
|
|
367
|
-
Remover
|
|
368
|
-
</button>
|
|
369
|
-
</div>
|
|
370
|
-
))}
|
|
371
|
-
|
|
372
|
-
<button onClick={() => handlePageChange(pagination.currentPage + 1)}>
|
|
373
|
-
Próxima página
|
|
374
|
-
</button>
|
|
375
|
-
</div>
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
**⚡ Performance**: React Query + debounce + paginação server-side
|
|
381
|
-
|
|
382
|
-
---
|
|
383
|
-
|
|
384
|
-
## 🎨 Sistema de Formulários
|
|
385
|
-
|
|
386
|
-
### Configuração Declarativa
|
|
387
|
-
|
|
388
|
-
```tsx
|
|
389
|
-
// Definir formulário por configuração (zero JSX manual)
|
|
390
|
-
const formSections = [
|
|
391
|
-
{
|
|
392
|
-
id: 'general',
|
|
393
|
-
title: 'Informações Gerais',
|
|
394
|
-
description: 'Dados principais do item',
|
|
395
|
-
fields: [
|
|
396
|
-
{
|
|
397
|
-
name: 'title',
|
|
398
|
-
label: 'Título',
|
|
399
|
-
type: 'text',
|
|
400
|
-
required: true,
|
|
401
|
-
placeholder: 'Digite o título...'
|
|
402
|
-
},
|
|
403
|
-
{
|
|
404
|
-
name: 'description',
|
|
405
|
-
label: 'Descrição',
|
|
406
|
-
type: 'textarea',
|
|
407
|
-
rows: 4
|
|
408
|
-
},
|
|
409
|
-
{
|
|
410
|
-
name: 'date_field',
|
|
411
|
-
label: 'Data',
|
|
412
|
-
type: 'date',
|
|
413
|
-
required: true
|
|
414
|
-
}
|
|
415
|
-
]
|
|
416
|
-
},
|
|
417
|
-
{
|
|
418
|
-
id: 'advanced',
|
|
419
|
-
title: 'Configurações Avançadas',
|
|
420
|
-
condition: (formData) => formData.userType === 'admin', // Seção condicional
|
|
421
|
-
fields: [
|
|
422
|
-
{
|
|
423
|
-
name: 'color',
|
|
424
|
-
label: 'Cor do tema',
|
|
425
|
-
type: 'color-picker'
|
|
426
|
-
},
|
|
427
|
-
{
|
|
428
|
-
name: 'icon_name',
|
|
429
|
-
label: 'Ícone',
|
|
430
|
-
type: 'icon-picker'
|
|
431
|
-
},
|
|
432
|
-
{
|
|
433
|
-
name: 'id_user',
|
|
434
|
-
label: 'Responsável',
|
|
435
|
-
type: 'simple-qualiex-user-field' // Campo integrado Qualiex
|
|
436
|
-
}
|
|
437
|
-
]
|
|
438
|
-
}
|
|
439
|
-
];
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
### Tipos de Campo Disponíveis
|
|
443
|
-
|
|
444
|
-
#### 📝 Campos Básicos
|
|
445
|
-
| Tipo | Descrição | Props Extras |
|
|
446
|
-
|------|-----------|--------------|
|
|
447
|
-
| `text` | Entrada de texto | `placeholder`, `maxLength` |
|
|
448
|
-
| `textarea` | Área de texto | `rows`, `placeholder` |
|
|
449
|
-
| `email` | E-mail com validação | `placeholder` |
|
|
450
|
-
| `password` | Senha oculta | `placeholder` |
|
|
451
|
-
| `number` | Número | `step`, `min`, `max` |
|
|
452
|
-
| `date` | Data | `min`, `max` |
|
|
453
|
-
| `datetime-local` | Data e hora | - |
|
|
454
|
-
| `url` | URL com validação | `placeholder` |
|
|
455
|
-
|
|
456
|
-
#### 🎯 Campos de Seleção
|
|
457
|
-
| Tipo | Descrição | Props Extras |
|
|
458
|
-
|------|-----------|--------------|
|
|
459
|
-
| `select` | Seleção única | `options: [{label, value}]` |
|
|
460
|
-
| `multiselect` | Seleção múltipla | `options`, `maxSelections` |
|
|
461
|
-
| `checkbox` | Checkbox | `defaultValue: boolean` |
|
|
462
|
-
| `radio` | Grupo de rádio | `options: [{label, value}]` |
|
|
463
|
-
|
|
464
|
-
#### 🎨 Campos Visuais
|
|
465
|
-
| Tipo | Descrição | Props Extras |
|
|
466
|
-
|------|-----------|--------------|
|
|
467
|
-
| `color` | Cor (input nativo) | - |
|
|
468
|
-
| `color-picker` | Seletor de cores avançado | `presetColors` |
|
|
469
|
-
| `icon-picker` | Seletor de ícones Lucide | `categories` |
|
|
470
|
-
|
|
471
|
-
#### 🔌 Campos Integrados Qualiex
|
|
472
|
-
| Tipo | Descrição | Props Extras |
|
|
473
|
-
|------|-----------|--------------|
|
|
474
|
-
| `simple-qualiex-user-field` | Usuário simples | - |
|
|
475
|
-
| `user-select` | Seletor de usuário avançado | `multiple` |
|
|
476
|
-
| `single-responsible-select` | Responsável único | - |
|
|
477
|
-
|
|
478
|
-
#### 🔧 Campos Especiais
|
|
479
|
-
| Tipo | Descrição | Props Extras |
|
|
480
|
-
|------|-----------|--------------|
|
|
481
|
-
| `group` | Agrupamento de campos | `fields: FormField[]` |
|
|
482
|
-
| `divider` | Separador visual | `label` |
|
|
483
|
-
|
|
484
|
-
### Recursos Avançados
|
|
485
|
-
|
|
486
|
-
#### 💡 Campos Computados
|
|
487
|
-
```tsx
|
|
488
|
-
{
|
|
489
|
-
name: 'fullName',
|
|
490
|
-
label: 'Nome Completo',
|
|
491
|
-
type: 'text',
|
|
492
|
-
computedValue: (formData) => `${formData.firstName} ${formData.lastName}`,
|
|
493
|
-
disabled: true
|
|
494
|
-
}
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
#### 🔗 Dependências entre Campos
|
|
498
|
-
```tsx
|
|
499
|
-
{
|
|
500
|
-
name: 'endDate',
|
|
501
|
-
label: 'Data Final',
|
|
502
|
-
type: 'date',
|
|
503
|
-
dependsOn: 'startDate',
|
|
504
|
-
validation: (value, formData) => {
|
|
505
|
-
if (value < formData.startDate) {
|
|
506
|
-
return 'Data final deve ser após a inicial';
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
#### ✅ Validação Customizada
|
|
513
|
-
```tsx
|
|
514
|
-
{
|
|
515
|
-
name: 'cpf',
|
|
516
|
-
label: 'CPF',
|
|
517
|
-
type: 'text',
|
|
518
|
-
validation: {
|
|
519
|
-
pattern: /^\d{3}\.\d{3}\.\d{3}-\d{2}$/,
|
|
520
|
-
message: 'CPF deve estar no formato 000.000.000-00',
|
|
521
|
-
custom: (value) => {
|
|
522
|
-
if (!isValidCPF(value)) return 'CPF inválido';
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
#### 🔄 Callbacks de Mudança
|
|
529
|
-
```tsx
|
|
530
|
-
{
|
|
531
|
-
name: 'category',
|
|
532
|
-
label: 'Categoria',
|
|
533
|
-
type: 'select',
|
|
534
|
-
options: categories,
|
|
535
|
-
onValueChange: (value, formData, setValue) => {
|
|
536
|
-
// Recarregar subcategorias baseado na categoria
|
|
537
|
-
loadSubcategories(value).then(subs => {
|
|
538
|
-
setValue('subcategory', null); // Reset subcategoria
|
|
539
|
-
// Atualizar opções de subcategoria
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
---
|
|
546
|
-
|
|
547
|
-
## 📊 Sistema de Tabelas e Cards
|
|
548
|
-
|
|
549
|
-
### Página CRUD Simplificada
|
|
550
|
-
|
|
551
|
-
### Exemplo Completo
|
|
552
|
-
|
|
553
|
-
```tsx
|
|
554
|
-
import { createSimpleService, createCrudPage, generateCrudConfig } from 'forlogic-core';
|
|
555
|
-
import { Package } from 'lucide-react';
|
|
556
|
-
|
|
557
|
-
interface Product {
|
|
558
|
-
id: string;
|
|
559
|
-
name: string;
|
|
560
|
-
price: number;
|
|
561
|
-
category: string;
|
|
562
|
-
description?: string;
|
|
563
|
-
created_at?: Date;
|
|
564
|
-
updated_at?: Date;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
const productService = createSimpleService<Product>({
|
|
568
|
-
tableName: 'products'
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
const config = generateCrudConfig<Product>(
|
|
572
|
-
'Produtos',
|
|
573
|
-
{ name: '', price: 0, category: 'electronics' },
|
|
574
|
-
{
|
|
575
|
-
icon: Package,
|
|
576
|
-
columns: [
|
|
577
|
-
{ key: 'name', label: 'Nome', type: 'text' as const, required: true },
|
|
578
|
-
{ key: 'price', label: 'Preço', type: 'number' as const, required: true },
|
|
579
|
-
{ key: 'category', label: 'Categoria', type: 'select' as const, options: [
|
|
580
|
-
{ value: 'electronics', label: 'Eletrônicos' },
|
|
581
|
-
{ value: 'clothing', label: 'Roupas' },
|
|
582
|
-
{ value: 'books', label: 'Livros' }
|
|
583
|
-
]}
|
|
584
|
-
]
|
|
585
|
-
}
|
|
586
|
-
);
|
|
587
|
-
|
|
588
|
-
export const ProductsPage = createCrudPage({
|
|
589
|
-
service: productService,
|
|
590
|
-
config
|
|
591
|
-
});
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
### Responsividade Automática
|
|
595
|
-
|
|
596
|
-
- **Desktop (≥768px)**: Tabela completa com ordenação
|
|
597
|
-
- **Mobile (<768px)**: Cards otimizados para toque
|
|
598
|
-
- **Transição suave**: Mudança automática no breakpoint
|
|
599
|
-
|
|
600
|
-
---
|
|
9
|
+
### 🗄️ Schema do Banco
|
|
601
10
|
|
|
602
|
-
|
|
11
|
+
Schema padrão deste projeto: **`central`**
|
|
603
12
|
|
|
604
|
-
|
|
13
|
+
## 🤖 Instruções para IA (Lovable)
|
|
605
14
|
|
|
606
|
-
|
|
607
|
-
import { qualiexApi, useQualiexUsers } from 'forlogic-core';
|
|
15
|
+
⚠️ **CONSULTAR SEMPRE antes de qualquer modificação:**
|
|
608
16
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
return (
|
|
614
|
-
<select>
|
|
615
|
-
{users.map(user => (
|
|
616
|
-
<option key={user.userId} value={user.userId}>
|
|
617
|
-
{user.userName} ({user.userEmail})
|
|
618
|
-
</option>
|
|
619
|
-
))}
|
|
620
|
-
</select>
|
|
621
|
-
);
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// API direta
|
|
625
|
-
const users = await qualiexApi.fetchUsers(alias, companyId);
|
|
626
|
-
// Retorna: QualiexUser[] { userId, userName, userEmail }
|
|
627
|
-
```
|
|
628
|
-
|
|
629
|
-
### Enriquecimento Automático
|
|
630
|
-
|
|
631
|
-
```typescript
|
|
632
|
-
// Service com enrichment automático
|
|
633
|
-
export const TaskService = createService<Task>({
|
|
634
|
-
tableName: 'tasks',
|
|
635
|
-
searchFields: ['title'],
|
|
636
|
-
entityName: 'Task' // Nome da entidade no Qualiex
|
|
637
|
-
});
|
|
638
|
-
|
|
639
|
-
// Dados enriquecidos automaticamente:
|
|
640
|
-
// id_user "123" → responsible_name "João Silva"
|
|
641
|
-
// Busca automática na API Qualiex quando necessário
|
|
642
|
-
```
|
|
643
|
-
|
|
644
|
-
---
|
|
645
|
-
|
|
646
|
-
## 🎯 Exemplos Práticos
|
|
647
|
-
|
|
648
|
-
### 1. CRUD de Produtos
|
|
649
|
-
|
|
650
|
-
```tsx
|
|
651
|
-
// types.ts
|
|
652
|
-
export interface Product {
|
|
653
|
-
id: string;
|
|
654
|
-
alias: string;
|
|
655
|
-
name: string;
|
|
656
|
-
description?: string;
|
|
657
|
-
price: number;
|
|
658
|
-
category_id: string;
|
|
659
|
-
id_user: string; // Responsável
|
|
660
|
-
is_actived: boolean;
|
|
661
|
-
is_removed: boolean;
|
|
662
|
-
created_at: string;
|
|
663
|
-
updated_at: string;
|
|
664
|
-
|
|
665
|
-
// Campos enriquecidos
|
|
666
|
-
responsible_name?: string;
|
|
667
|
-
category_name?: string;
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
// ProductService.ts
|
|
671
|
-
import { createService } from 'forlogic-core';
|
|
672
|
-
|
|
673
|
-
export const ProductService = createService<Product>({
|
|
674
|
-
tableName: 'products',
|
|
675
|
-
searchFields: ['name', 'description'],
|
|
676
|
-
schemaName: 'schema_module',
|
|
677
|
-
entityName: 'Product'
|
|
678
|
-
});
|
|
679
|
-
|
|
680
|
-
```
|
|
681
|
-
|
|
682
|
-
---
|
|
683
|
-
|
|
684
|
-
### 2. Formulário de Contato Avançado
|
|
685
|
-
|
|
686
|
-
```tsx
|
|
687
|
-
import { BaseForm } from 'forlogic-core';
|
|
688
|
-
|
|
689
|
-
export function ContactForm({ open, onClose, onSubmit }) {
|
|
690
|
-
const sections = [
|
|
691
|
-
{
|
|
692
|
-
id: 'personal',
|
|
693
|
-
title: 'Dados Pessoais',
|
|
694
|
-
fields: [
|
|
695
|
-
{
|
|
696
|
-
name: 'name',
|
|
697
|
-
label: 'Nome Completo',
|
|
698
|
-
type: 'text',
|
|
699
|
-
required: true
|
|
700
|
-
},
|
|
701
|
-
{
|
|
702
|
-
name: 'email',
|
|
703
|
-
label: 'E-mail',
|
|
704
|
-
type: 'email',
|
|
705
|
-
required: true,
|
|
706
|
-
validation: {
|
|
707
|
-
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
708
|
-
message: 'E-mail inválido'
|
|
709
|
-
}
|
|
710
|
-
},
|
|
711
|
-
{
|
|
712
|
-
name: 'phone',
|
|
713
|
-
label: 'Telefone',
|
|
714
|
-
type: 'text',
|
|
715
|
-
validation: {
|
|
716
|
-
custom: (value) => {
|
|
717
|
-
if (value && !isValidPhone(value)) {
|
|
718
|
-
return 'Telefone inválido';
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
]
|
|
724
|
-
},
|
|
725
|
-
{
|
|
726
|
-
id: 'preferences',
|
|
727
|
-
title: 'Preferências',
|
|
728
|
-
fields: [
|
|
729
|
-
{
|
|
730
|
-
name: 'contactMethod',
|
|
731
|
-
label: 'Método de Contato Preferido',
|
|
732
|
-
type: 'select',
|
|
733
|
-
options: [
|
|
734
|
-
{ label: 'E-mail', value: 'email' },
|
|
735
|
-
{ label: 'Telefone', value: 'phone' },
|
|
736
|
-
{ label: 'WhatsApp', value: 'whatsapp' }
|
|
737
|
-
],
|
|
738
|
-
onValueChange: (value, formData, setValue) => {
|
|
739
|
-
if (value === 'whatsapp' && !formData.phone) {
|
|
740
|
-
// Foco automático no campo telefone
|
|
741
|
-
document.querySelector('[name="phone"]')?.focus();
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
},
|
|
745
|
-
{
|
|
746
|
-
name: 'newsletter',
|
|
747
|
-
label: 'Receber newsletter',
|
|
748
|
-
type: 'checkbox',
|
|
749
|
-
defaultValue: true
|
|
750
|
-
}
|
|
751
|
-
]
|
|
752
|
-
}
|
|
753
|
-
];
|
|
754
|
-
|
|
755
|
-
return (
|
|
756
|
-
<BaseForm
|
|
757
|
-
open={open}
|
|
758
|
-
title="Novo Contato"
|
|
759
|
-
sections={sections}
|
|
760
|
-
onSubmit={onSubmit}
|
|
761
|
-
onCancel={onClose}
|
|
762
|
-
submitText="Salvar Contato"
|
|
763
|
-
/>
|
|
764
|
-
);
|
|
765
|
-
}
|
|
766
|
-
```
|
|
767
|
-
|
|
768
|
-
---
|
|
769
|
-
|
|
770
|
-
## 🔒 Segurança e Performance
|
|
771
|
-
|
|
772
|
-
### Implementações de Segurança
|
|
773
|
-
|
|
774
|
-
| Recurso | Implementação | Benefício |
|
|
775
|
-
|---------|---------------|-----------|
|
|
776
|
-
| **RLS Multi-tenant** | Política por `alias` | Isolamento total entre empresas |
|
|
777
|
-
| **Soft Delete** | `is_removed = true` | Auditoria e recuperação |
|
|
778
|
-
| **JWT Refresh** | Renovação automática | Sessões seguras |
|
|
779
|
-
| **Input Validation** | Zod + client-side | Prevenção de dados inválidos |
|
|
780
|
-
| **XSS Protection** | Sanitização automática | Prevenção de ataques |
|
|
781
|
-
|
|
782
|
-
### Otimizações de Performance
|
|
783
|
-
|
|
784
|
-
| Recurso | Configuração | Impacto |
|
|
785
|
-
|---------|--------------|---------|
|
|
786
|
-
| **React Query Cache** | 10 min | -80% requests |
|
|
787
|
-
| **Search Debounce** | 500ms | -90% queries |
|
|
788
|
-
| **Paginação Server** | 25 itens | Escalabilidade |
|
|
789
|
-
| **Lazy Loading** | Componentes | Menor bundle |
|
|
790
|
-
| **Bundle Splitting** | Por rota | Carregamento rápido |
|
|
791
|
-
|
|
792
|
-
### Métricas Automáticas
|
|
793
|
-
|
|
794
|
-
```typescript
|
|
795
|
-
// Configuração de performance (opcional)
|
|
796
|
-
setupQualiexCore({
|
|
797
|
-
// ... outras configs
|
|
798
|
-
performance: {
|
|
799
|
-
cacheTime: 10 * 60 * 1000, // 10 minutos
|
|
800
|
-
staleTime: 5 * 60 * 1000, // 5 minutos
|
|
801
|
-
debounceDelay: 500, // 500ms
|
|
802
|
-
pageSize: 25, // 25 itens por página
|
|
803
|
-
enableMetrics: true // Métricas automáticas
|
|
804
|
-
}
|
|
805
|
-
});
|
|
806
|
-
```
|
|
807
|
-
|
|
808
|
-
---
|
|
809
|
-
|
|
810
|
-
## 📚 Documentação Técnica
|
|
811
|
-
|
|
812
|
-
### Estrutura de Dados
|
|
813
|
-
|
|
814
|
-
```typescript
|
|
815
|
-
// Tipos base do sistema
|
|
816
|
-
export interface BaseEntity {
|
|
817
|
-
id: string;
|
|
818
|
-
alias: string;
|
|
819
|
-
is_actived: boolean;
|
|
820
|
-
is_removed: boolean;
|
|
821
|
-
created_at: string;
|
|
822
|
-
updated_at: string;
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
export interface PaginationInfo {
|
|
826
|
-
currentPage: number;
|
|
827
|
-
totalPages: number;
|
|
828
|
-
totalItems: number;
|
|
829
|
-
pageSize: number;
|
|
830
|
-
hasNext: boolean;
|
|
831
|
-
hasPrev: boolean;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
export interface CrudManager<T> {
|
|
835
|
-
entities: T[];
|
|
836
|
-
pagination: PaginationInfo;
|
|
837
|
-
isLoading: boolean;
|
|
838
|
-
searchTerm: string;
|
|
839
|
-
createEntity: (data: Partial<T>) => Promise<void>;
|
|
840
|
-
updateEntity: (id: string, data: Partial<T>) => Promise<void>;
|
|
841
|
-
deleteEntity: (id: string) => Promise<void>;
|
|
842
|
-
refetch: () => void;
|
|
843
|
-
handleSearch: (term: string) => void;
|
|
844
|
-
handlePageChange: (page: number) => void;
|
|
845
|
-
}
|
|
846
|
-
```
|
|
847
|
-
|
|
848
|
-
### Políticas RLS Template
|
|
849
|
-
|
|
850
|
-
```sql
|
|
851
|
-
-- Template completo para novas tabelas
|
|
852
|
-
-- docs/templates/rls-policies.sql
|
|
853
|
-
|
|
854
|
-
-- Criar tabela
|
|
855
|
-
CREATE TABLE schema_module.{table_name} (
|
|
856
|
-
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
857
|
-
alias TEXT NOT NULL,
|
|
858
|
-
|
|
859
|
-
-- Campos customizados aqui
|
|
860
|
-
title TEXT NOT NULL,
|
|
861
|
-
description TEXT,
|
|
862
|
-
|
|
863
|
-
-- Campos de controle
|
|
864
|
-
is_actived BOOLEAN DEFAULT true,
|
|
865
|
-
is_removed BOOLEAN DEFAULT false,
|
|
866
|
-
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
|
867
|
-
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()
|
|
868
|
-
);
|
|
869
|
-
|
|
870
|
-
-- RLS
|
|
871
|
-
ALTER TABLE schema_module.{table_name} ENABLE ROW LEVEL SECURITY;
|
|
872
|
-
|
|
873
|
-
-- Políticas padrão
|
|
874
|
-
CREATE POLICY "view_company_data" ON schema_module.{table_name}
|
|
875
|
-
FOR SELECT USING (
|
|
876
|
-
((SELECT auth.jwt()) ->> 'alias') = alias
|
|
877
|
-
AND is_removed = false
|
|
878
|
-
);
|
|
879
|
-
|
|
880
|
-
CREATE POLICY "insert_company_data" ON schema_module.{table_name}
|
|
881
|
-
FOR INSERT WITH CHECK (
|
|
882
|
-
((SELECT auth.jwt()) ->> 'alias') = alias
|
|
883
|
-
);
|
|
884
|
-
|
|
885
|
-
CREATE POLICY "update_company_data" ON schema_module.{table_name}
|
|
886
|
-
FOR UPDATE USING (
|
|
887
|
-
((SELECT auth.jwt()) ->> 'alias') = alias
|
|
888
|
-
);
|
|
889
|
-
|
|
890
|
-
-- Trigger updated_at
|
|
891
|
-
CREATE TRIGGER update_{table_name}_updated_at
|
|
892
|
-
BEFORE UPDATE ON schema_module.{table_name}
|
|
893
|
-
FOR EACH ROW
|
|
894
|
-
EXECUTE FUNCTION schema_module.update_updated_at_column();
|
|
895
|
-
```
|
|
896
|
-
|
|
897
|
-
### Edge Functions
|
|
898
|
-
|
|
899
|
-
```typescript
|
|
900
|
-
// validate-token/index.ts
|
|
901
|
-
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
|
|
902
|
-
|
|
903
|
-
serve(async (req) => {
|
|
904
|
-
const { access_token } = await req.json();
|
|
905
|
-
|
|
906
|
-
// Validar token no Qualiex
|
|
907
|
-
const userInfo = await validateWithQualiex(access_token);
|
|
908
|
-
|
|
909
|
-
// Gerar token Supabase
|
|
910
|
-
const supabaseToken = await generateSupabaseToken(userInfo);
|
|
911
|
-
|
|
912
|
-
return new Response(
|
|
913
|
-
JSON.stringify({
|
|
914
|
-
supabase_token: supabaseToken,
|
|
915
|
-
user: userInfo
|
|
916
|
-
}),
|
|
917
|
-
{ headers: { 'Content-Type': 'application/json' } }
|
|
918
|
-
);
|
|
919
|
-
});
|
|
920
|
-
```
|
|
921
|
-
|
|
922
|
-
---
|
|
923
|
-
|
|
924
|
-
## 🚀 Guia de Migração
|
|
925
|
-
|
|
926
|
-
### De Sistemas Legados
|
|
927
|
-
|
|
928
|
-
```typescript
|
|
929
|
-
// ANTES: Código manual complexo
|
|
930
|
-
const [data, setData] = useState([]);
|
|
931
|
-
const [loading, setLoading] = useState(false);
|
|
932
|
-
const [search, setSearch] = useState('');
|
|
933
|
-
|
|
934
|
-
useEffect(() => {
|
|
935
|
-
loadData();
|
|
936
|
-
}, [search]);
|
|
937
|
-
|
|
938
|
-
const loadData = async () => {
|
|
939
|
-
setLoading(true);
|
|
940
|
-
try {
|
|
941
|
-
const result = await api.getData({ search });
|
|
942
|
-
setData(result);
|
|
943
|
-
} finally {
|
|
944
|
-
setLoading(false);
|
|
945
|
-
}
|
|
946
|
-
};
|
|
947
|
-
|
|
948
|
-
// DEPOIS: Hook simplificado
|
|
949
|
-
const manager = useCrud({
|
|
950
|
-
queryKey: 'my-data',
|
|
951
|
-
service: MyService,
|
|
952
|
-
entityName: 'Item'
|
|
953
|
-
});
|
|
954
|
-
|
|
955
|
-
// Pronto! Todas as funcionalidades automáticas
|
|
956
|
-
```
|
|
957
|
-
|
|
958
|
-
### Checklist de Migração
|
|
959
|
-
|
|
960
|
-
- [ ] ✅ Criar tabela com estrutura padrão
|
|
961
|
-
- [ ] ✅ Configurar políticas RLS
|
|
962
|
-
- [ ] ✅ Criar service com `createService()`
|
|
963
|
-
- [ ] ✅ Substituir código manual por `useCrud()`
|
|
964
|
-
- [ ] ✅ Configurar formulários declarativos
|
|
965
|
-
- [ ] ✅ Testar operações CRUD
|
|
966
|
-
- [ ] ✅ Verificar responsividade
|
|
967
|
-
- [ ] ✅ Configurar integração Qualiex (se necessário)
|
|
968
|
-
|
|
969
|
-
---
|
|
970
|
-
|
|
971
|
-
## 🛠️ Desenvolvimento
|
|
972
|
-
|
|
973
|
-
### Comandos de Build
|
|
974
|
-
|
|
975
|
-
```bash
|
|
976
|
-
# Desenvolvimento da lib
|
|
977
|
-
npm run dev
|
|
978
|
-
|
|
979
|
-
# Build da biblioteca
|
|
980
|
-
npm run build:lib
|
|
981
|
-
# ou
|
|
982
|
-
./build-and-publish.sh build
|
|
983
|
-
|
|
984
|
-
# Publicar versão
|
|
985
|
-
./build-and-publish.sh patch # 1.0.0 → 1.0.1
|
|
986
|
-
./build-and-publish.sh minor # 1.0.0 → 1.1.0
|
|
987
|
-
./build-and-publish.sh major # 1.0.0 → 2.0.0
|
|
988
|
-
|
|
989
|
-
# Teste de publicação
|
|
990
|
-
./build-and-publish.sh dry-run
|
|
991
|
-
```
|
|
992
|
-
|
|
993
|
-
### Estrutura de Build
|
|
994
|
-
|
|
995
|
-
```
|
|
996
|
-
dist/ # Saída da build
|
|
997
|
-
├── index.js # CommonJS
|
|
998
|
-
├── index.esm.js # ES Modules
|
|
999
|
-
├── index.d.ts # Types
|
|
1000
|
-
├── index.css # CSS incluído
|
|
1001
|
-
├── package.json # Package da lib
|
|
1002
|
-
└── README.md # Esta documentação
|
|
1003
|
-
```
|
|
1004
|
-
|
|
1005
|
-
### Scripts Personalizados
|
|
1006
|
-
|
|
1007
|
-
```javascript
|
|
1008
|
-
// scripts/build-lib.js - Build customizado
|
|
1009
|
-
// scripts/publish-lib.js - Publicação automatizada
|
|
1010
|
-
// build-and-publish.sh - Wrapper para comandos
|
|
1011
|
-
```
|
|
1012
|
-
|
|
1013
|
-
---
|
|
1014
|
-
|
|
1015
|
-
## 🎯 Princípios de Desenvolvimento
|
|
1016
|
-
|
|
1017
|
-
### ✅ SEMPRE Fazer
|
|
1018
|
-
|
|
1019
|
-
- **Imports diretos**: `import { Component } from 'forlogic-core'`
|
|
1020
|
-
- **Componentes pequenos**: Máximo 100 linhas
|
|
1021
|
-
- **RLS ativado**: Todas as tabelas com políticas
|
|
1022
|
-
- **Soft delete**: `is_removed = true`
|
|
1023
|
-
- **Props inline**: Evitar interfaces desnecessárias
|
|
1024
|
-
- **Configuração declarativa**: Formulários por config
|
|
1025
|
-
|
|
1026
|
-
### ❌ NUNCA Fazer
|
|
1027
|
-
|
|
1028
|
-
- Barrel exports (`index.ts` desnecessários)
|
|
1029
|
-
- Over-engineering (YAGNI)
|
|
1030
|
-
- DELETE físico no banco
|
|
1031
|
-
- Componentes monolíticos
|
|
1032
|
-
- Props drilling excessivo
|
|
1033
|
-
- Ignorar políticas RLS
|
|
1034
|
-
|
|
1035
|
-
### 🔥 Filosofia
|
|
1036
|
-
|
|
1037
|
-
> **"Máxima produtividade com mínima complexidade"**
|
|
1038
|
-
|
|
1039
|
-
- ⚡ **Performance**: React Query + otimizações automáticas
|
|
1040
|
-
- 🔒 **Segurança**: RLS + JWT + validação
|
|
1041
|
-
- 📱 **Responsividade**: Mobile-first automático
|
|
1042
|
-
- 🔧 **DX**: Zero config, máxima simplicidade
|
|
1043
|
-
- 🎨 **Design**: Sistema consistente e acessível
|
|
1044
|
-
|
|
1045
|
-
---
|
|
1046
|
-
|
|
1047
|
-
## 📋 Troubleshooting
|
|
1048
|
-
|
|
1049
|
-
### Problemas Comuns
|
|
1050
|
-
|
|
1051
|
-
**❌ Erro: "RLS policy violation"**
|
|
1052
|
-
```sql
|
|
1053
|
-
-- Verificar se as políticas estão corretas
|
|
1054
|
-
SELECT schemaname, tablename, policyname
|
|
1055
|
-
FROM pg_policies
|
|
1056
|
-
WHERE tablename = 'your_table';
|
|
1057
|
-
```
|
|
1058
|
-
|
|
1059
|
-
**❌ Erro: "Cannot read property of undefined"**
|
|
1060
|
-
```typescript
|
|
1061
|
-
// Verificar se o serviço foi configurado corretamente
|
|
1062
|
-
export const MyService = createService<MyEntity>({
|
|
1063
|
-
tableName: 'correct_table_name', // ← Verificar nome
|
|
1064
|
-
schemaName: 'schema_module', // ← Verificar schema
|
|
1065
|
-
// ...
|
|
1066
|
-
});
|
|
1067
|
-
```
|
|
1068
|
-
|
|
1069
|
-
**❌ Formulário não aparece**
|
|
1070
|
-
```tsx
|
|
1071
|
-
// Verificar se as seções estão configuradas
|
|
1072
|
-
const formSections = [
|
|
1073
|
-
{
|
|
1074
|
-
id: 'section1', // ← ID obrigatório
|
|
1075
|
-
title: 'Título', // ← Título obrigatório
|
|
1076
|
-
fields: [ // ← Array de campos obrigatório
|
|
1077
|
-
{
|
|
1078
|
-
name: 'field1', // ← Nome obrigatório
|
|
1079
|
-
label: 'Label', // ← Label obrigatório
|
|
1080
|
-
type: 'text' // ← Tipo obrigatório
|
|
1081
|
-
}
|
|
1082
|
-
]
|
|
1083
|
-
}
|
|
1084
|
-
];
|
|
1085
|
-
```
|
|
1086
|
-
|
|
1087
|
-
### Debug Mode
|
|
1088
|
-
|
|
1089
|
-
```typescript
|
|
1090
|
-
// Ativar logs detalhados
|
|
1091
|
-
setupQualiexCore({
|
|
1092
|
-
// ... outras configs
|
|
1093
|
-
debug: true // Logs no console
|
|
1094
|
-
});
|
|
1095
|
-
```
|
|
1096
|
-
|
|
1097
|
-
---
|
|
1098
|
-
|
|
1099
|
-
## 📞 Suporte
|
|
1100
|
-
|
|
1101
|
-
### Recursos
|
|
1102
|
-
|
|
1103
|
-
- 📖 **Documentação**: Este README
|
|
1104
|
-
- 🔧 **Exemplos**: `/src/examples/`
|
|
1105
|
-
- 🐛 **Issues**: GitHub Issues
|
|
1106
|
-
- 💬 **Suporte**: Equipe ForLogic
|
|
1107
|
-
|
|
1108
|
-
### Atualizações
|
|
1109
|
-
|
|
1110
|
-
```bash
|
|
1111
|
-
# Verificar versão atual
|
|
1112
|
-
npm list forlogic-core
|
|
1113
|
-
|
|
1114
|
-
# Atualizar para última versão
|
|
1115
|
-
npm update forlogic-core
|
|
1116
|
-
|
|
1117
|
-
# Migração automática
|
|
1118
|
-
npx forlogic-core migrate
|
|
1119
|
-
```
|
|
1120
|
-
|
|
1121
|
-
---
|
|
17
|
+
1. **README.md da lib**: Acesse via `node_modules/forlogic-core/dist/README.md` - Instalação e Quick Start
|
|
18
|
+
2. **AI_RULES.md**: Acesse via `node_modules/forlogic-core/dist/docs/AI_RULES.md` - **REGRAS CRÍTICAS**
|
|
19
|
+
3. **Templates**: Acesse via `node_modules/forlogic-core/dist/docs/templates/` - Códigos prontos
|
|
20
|
+
4. **Hub da Docs**: Acesse via `node_modules/forlogic-core/dist/docs/README.md` - Navegação completa
|
|
1122
21
|
|
|
1123
|
-
|
|
22
|
+
**NUNCA recriar:**
|
|
23
|
+
- ❌ Integração Qualiex (já existe na lib)
|
|
24
|
+
- ❌ Integração Supabase (já existe na lib)
|
|
25
|
+
- ❌ Sistema CRUD (usar `createCrudPage` da lib)
|
|
26
|
+
- ❌ Componentes base (importar de `forlogic-core`)
|
|
1124
27
|
|
|
1125
|
-
|
|
28
|
+
**SEMPRE verificar:**
|
|
29
|
+
- ✅ Schema padrão do projeto
|
|
30
|
+
- ✅ Templates disponíveis antes de criar do zero
|
|
1126
31
|
|
|
1127
32
|
---
|
|
1128
33
|
|
|
1129
|
-
|
|
34
|
+
## 📝 Licença
|
|
1130
35
|
|
|
1131
|
-
|
|
36
|
+
MIT License - ForLogic © 2025
|