forlogic-core 1.0.0

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