forlogic-core 1.5.5 → 1.6.1
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 +404 -2
- package/dist/README.md +404 -2
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.esm.js +2 -2
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# 🧱 Projeto atual
|
|
2
2
|
|
|
3
|
-
**Schema padrão:** `
|
|
3
|
+
**Schema padrão:** `processes`
|
|
4
4
|
|
|
5
|
-
> **Nota sobre schemas:**
|
|
5
|
+
> **Nota sobre schemas:** Temos o schema padrão, mas módulos podem usar schemas diferentes quando necessário para organização ou isolamento de dados. Por exemplo, o módulo de treinamentos usa o schema `trainings`. Você pode especificar o schema no service usando `schemaName: 'nome_do_schema'`.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -40,10 +40,412 @@ CREATE INDEX idx_table_user ON schema.table(id_user);
|
|
|
40
40
|
|
|
41
41
|
---
|
|
42
42
|
|
|
43
|
+
## 📝 CONVENÇÕES DE NOMENCLATURA
|
|
44
|
+
|
|
45
|
+
### ⚠️ PADRÕES OBRIGATÓRIOS
|
|
46
|
+
|
|
47
|
+
**Foreign Keys (Chaves Estrangeiras):**
|
|
48
|
+
```typescript
|
|
49
|
+
// ❌ ERRADO
|
|
50
|
+
process_id: string
|
|
51
|
+
user_id: string
|
|
52
|
+
|
|
53
|
+
// ✅ CORRETO - Sempre prefixo id_
|
|
54
|
+
id_process: string
|
|
55
|
+
id_user: string
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Booleans:**
|
|
59
|
+
```typescript
|
|
60
|
+
// ❌ ERRADO
|
|
61
|
+
active: boolean
|
|
62
|
+
removed: boolean
|
|
63
|
+
|
|
64
|
+
// ✅ CORRETO - Sempre prefixo is_
|
|
65
|
+
is_active: boolean
|
|
66
|
+
is_removed: boolean
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Timestamps:**
|
|
70
|
+
```typescript
|
|
71
|
+
// ❌ ERRADO
|
|
72
|
+
creation_date: string
|
|
73
|
+
update_time: string
|
|
74
|
+
|
|
75
|
+
// ✅ CORRETO - Sempre sufixo _at
|
|
76
|
+
created_at: string
|
|
77
|
+
updated_at: string
|
|
78
|
+
deleted_at: string
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 🚫 ÍNDICES: ABSOLUTAMENTE PROIBIDO CRIAR AUTOMATICAMENTE
|
|
84
|
+
|
|
85
|
+
### ⚠️ REGRA DE OURO
|
|
86
|
+
**NUNCA, EM HIPÓTESE ALGUMA, criar índices automaticamente em migrations!**
|
|
87
|
+
|
|
88
|
+
### 🤔 Por Quê?
|
|
89
|
+
1. **Custo**: Índices ocupam espaço em disco e custam dinheiro
|
|
90
|
+
2. **Performance de Escrita**: Cada índice adiciona overhead em INSERTs/UPDATEs
|
|
91
|
+
3. **Otimização Prematura**: 99% dos índices criados "por precaução" nunca são usados
|
|
92
|
+
4. **Manutenção**: Índices desnecessários dificultam manutenção e análise de queries
|
|
93
|
+
|
|
94
|
+
### ❌ Casos PROIBIDOS (mesmo que pareçam "boas práticas")
|
|
95
|
+
```sql
|
|
96
|
+
-- ❌ PROIBIDO: Índice em FK "porque é boa prática"
|
|
97
|
+
CREATE INDEX idx_subprocess_process ON subprocesses(id_process);
|
|
98
|
+
|
|
99
|
+
-- ❌ PROIBIDO: Índice em campo de busca "porque pode ser útil"
|
|
100
|
+
CREATE INDEX idx_process_title ON processes(title);
|
|
101
|
+
|
|
102
|
+
-- ❌ PROIBIDO: Índice composto "por precaução"
|
|
103
|
+
CREATE INDEX idx_deliverable_status ON deliverables(id_subprocess, is_completed);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### ✅ Quando Criar Índices?
|
|
107
|
+
|
|
108
|
+
**APENAS** quando:
|
|
109
|
+
1. ✅ **Solicitado explicitamente** pelo usuário
|
|
110
|
+
2. ✅ **Análise de performance** comprovou necessidade (EXPLAIN ANALYZE)
|
|
111
|
+
3. ✅ **Aprovação prévia** do usuário para incluir na migration
|
|
112
|
+
|
|
113
|
+
### 📋 Checklist OBRIGATÓRIO Antes de Qualquer Migration
|
|
114
|
+
```markdown
|
|
115
|
+
- [ ] A migration NÃO contém NENHUM `CREATE INDEX`?
|
|
116
|
+
- [ ] Se contém, o usuário solicitou EXPLICITAMENTE?
|
|
117
|
+
- [ ] Se contém, foi feita análise de performance (EXPLAIN ANALYZE)?
|
|
118
|
+
- [ ] Se contém, o usuário APROVOU adicionar à migration?
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 🔧 Processo Correto Para Criar Índices
|
|
122
|
+
1. **Usuário solicita** OU problemas de performance são detectados
|
|
123
|
+
2. **Rodar EXPLAIN ANALYZE** para confirmar necessidade
|
|
124
|
+
3. **Perguntar ao usuário**: "Posso criar o índice X na coluna Y? Isso vai melhorar a query Z mas adiciona overhead."
|
|
125
|
+
4. **Aguardar aprovação** do usuário
|
|
126
|
+
5. **Criar migration separada** apenas com os índices aprovados
|
|
127
|
+
|
|
128
|
+
### 📝 Template de Migration de Índices (quando aprovado)
|
|
129
|
+
```sql
|
|
130
|
+
-- Migration: [TIMESTAMP]_add_performance_indexes.sql
|
|
131
|
+
-- Aprovado em: [DATA]
|
|
132
|
+
-- Justificativa: [RAZÃO ESPECÍFICA]
|
|
133
|
+
|
|
134
|
+
CREATE INDEX idx_processes_title ON processes.processes(title);
|
|
135
|
+
-- Melhora busca por título em 80% (EXPLAIN ANALYZE anexo)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 🚫 USAR forlogic-core - NÃO DUPLICAR CÓDIGO
|
|
141
|
+
|
|
142
|
+
### ⚠️ ARQUIVOS PROIBIDOS DE CRIAR LOCALMENTE
|
|
143
|
+
|
|
144
|
+
**NUNCA crie estes arquivos localmente:**
|
|
145
|
+
```typescript
|
|
146
|
+
// ❌ PROIBIDO - Usar da lib
|
|
147
|
+
src/lib/utils.ts // cn() já existe no forlogic-core
|
|
148
|
+
src/components/ui/dialog.tsx
|
|
149
|
+
src/components/ui/button.tsx
|
|
150
|
+
src/components/ui/card.tsx
|
|
151
|
+
src/components/ui/input.tsx
|
|
152
|
+
// ... e TODOS os outros componentes UI
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### ✅ Imports Corretos
|
|
156
|
+
|
|
157
|
+
**Utils (cn):**
|
|
158
|
+
```typescript
|
|
159
|
+
// ❌ ERRADO
|
|
160
|
+
import { cn } from '@/lib/utils'
|
|
161
|
+
|
|
162
|
+
// ✅ CORRETO
|
|
163
|
+
import { cn } from 'forlogic-core'
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Componentes UI:**
|
|
167
|
+
```typescript
|
|
168
|
+
// ❌ ERRADO
|
|
169
|
+
import { Dialog } from '@/components/ui/dialog'
|
|
170
|
+
import { Button } from '@/components/ui/button'
|
|
171
|
+
|
|
172
|
+
// ✅ CORRETO
|
|
173
|
+
import { Dialog, DialogContent, DialogTitle, DialogHeader } from 'forlogic-core'
|
|
174
|
+
import { Button } from 'forlogic-core'
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 📋 Checklist Antes de Criar Novo Arquivo
|
|
178
|
+
```markdown
|
|
179
|
+
- [ ] Este componente/util JÁ existe no forlogic-core?
|
|
180
|
+
- [ ] Verifiquei a lista de exports disponíveis abaixo?
|
|
181
|
+
- [ ] Se existe, estou usando o import correto da lib?
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 📦 Componentes e Utils Disponíveis no forlogic-core
|
|
185
|
+
|
|
186
|
+
**🎨 Componentes UI:**
|
|
187
|
+
```typescript
|
|
188
|
+
// Formulários
|
|
189
|
+
Button, Input, Textarea, Label, Select, SelectContent,
|
|
190
|
+
SelectItem, SelectTrigger, SelectValue, Checkbox, RadioGroup,
|
|
191
|
+
RadioGroupItem, Switch
|
|
192
|
+
|
|
193
|
+
// Layout
|
|
194
|
+
Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle
|
|
195
|
+
Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader,
|
|
196
|
+
DialogTitle, DialogTrigger
|
|
197
|
+
Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle
|
|
198
|
+
Separator, ScrollArea
|
|
199
|
+
|
|
200
|
+
// Navegação
|
|
201
|
+
Tabs, TabsContent, TabsList, TabsTrigger
|
|
202
|
+
Accordion, AccordionContent, AccordionItem, AccordionTrigger
|
|
203
|
+
DropdownMenu, DropdownMenuContent, DropdownMenuItem
|
|
204
|
+
|
|
205
|
+
// Feedback
|
|
206
|
+
Alert, AlertDescription, AlertTitle
|
|
207
|
+
Toast, useToast
|
|
208
|
+
Progress
|
|
209
|
+
Badge
|
|
210
|
+
Tooltip, TooltipContent, TooltipProvider, TooltipTrigger
|
|
211
|
+
|
|
212
|
+
// Tabelas
|
|
213
|
+
Table, TableBody, TableCell, TableHead, TableHeader, TableRow
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**🛠️ Utils e Hooks:**
|
|
217
|
+
```typescript
|
|
218
|
+
// Utils
|
|
219
|
+
cn // Merge classes Tailwind
|
|
220
|
+
formatDate, formatDatetime // Formatação de datas
|
|
221
|
+
|
|
222
|
+
// Auth
|
|
223
|
+
useAuth // Hook de autenticação
|
|
224
|
+
ProtectedRoute // Componente de rota protegida
|
|
225
|
+
|
|
226
|
+
// Qualiex
|
|
227
|
+
useQualiexUsers // Hook de usuários
|
|
228
|
+
QualiexUserField // Campo de seleção de usuário
|
|
229
|
+
|
|
230
|
+
// Supabase
|
|
231
|
+
getSupabaseClient // Cliente Supabase configurado
|
|
232
|
+
TokenManager // Gerenciamento de tokens
|
|
233
|
+
|
|
234
|
+
// Services
|
|
235
|
+
createSimpleService // Criar service CRUD
|
|
236
|
+
createCrudPage // Criar página CRUD
|
|
237
|
+
generateCrudConfig // Gerar config CRUD
|
|
238
|
+
createSimpleSaveHandler // Handler de save
|
|
239
|
+
|
|
240
|
+
// Errors
|
|
241
|
+
errorService // Service de erros
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### 🔍 Como Verificar Exports Disponíveis
|
|
245
|
+
|
|
246
|
+
Se não tiver certeza se algo existe no forlogic-core:
|
|
247
|
+
1. Abra `node_modules/forlogic-core/package.json`
|
|
248
|
+
2. Verifique os exports disponíveis
|
|
249
|
+
3. Em caso de dúvida, pergunte ao usuário antes de criar localmente
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
### 🔽 COMPONENTES AVANÇADOS
|
|
254
|
+
|
|
255
|
+
#### EntitySelect - Dropdown Genérico com Busca
|
|
256
|
+
|
|
257
|
+
**Quando usar?**
|
|
258
|
+
|
|
259
|
+
Use o `EntitySelect` para criar dropdowns com busca e ordenação para qualquer entidade do sistema:
|
|
260
|
+
- ✅ Seleção de processos, departamentos, categorias, etc
|
|
261
|
+
- ✅ Busca em tempo real
|
|
262
|
+
- ✅ Ordenação alfabética automática
|
|
263
|
+
- ✅ Estados de loading e erro inclusos
|
|
264
|
+
|
|
265
|
+
**Uso Básico:**
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
import { EntitySelect } from 'forlogic-core';
|
|
269
|
+
import { useProcesses } from './processService';
|
|
270
|
+
|
|
271
|
+
function MyForm() {
|
|
272
|
+
const { data: processes = [], isLoading, error } = useProcesses();
|
|
273
|
+
const [selectedId, setSelectedId] = useState('');
|
|
274
|
+
|
|
275
|
+
return (
|
|
276
|
+
<EntitySelect
|
|
277
|
+
value={selectedId}
|
|
278
|
+
onChange={setSelectedId}
|
|
279
|
+
items={processes}
|
|
280
|
+
isLoading={isLoading}
|
|
281
|
+
error={error}
|
|
282
|
+
getItemValue={(process) => process.id}
|
|
283
|
+
getItemLabel={(process) => process.title}
|
|
284
|
+
placeholder="Selecionar processo"
|
|
285
|
+
searchPlaceholder="Buscar processo..."
|
|
286
|
+
/>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**Props do EntitySelect:**
|
|
292
|
+
|
|
293
|
+
| Prop | Tipo | Obrigatório | Descrição |
|
|
294
|
+
|------|------|-------------|-----------|
|
|
295
|
+
| `items` | `T[]` | ✅ | Array de itens para seleção |
|
|
296
|
+
| `getItemValue` | `(item: T) => string` | ✅ | Função que retorna o valor único do item (ex: `id`) |
|
|
297
|
+
| `getItemLabel` | `(item: T) => string` | ✅ | Função que retorna o texto exibido |
|
|
298
|
+
| `value` | `string` | ❌ | Valor selecionado |
|
|
299
|
+
| `onChange` | `(value: string) => void` | ❌ | Callback quando valor muda |
|
|
300
|
+
| `isLoading` | `boolean` | ❌ | Mostra skeleton durante carregamento |
|
|
301
|
+
| `error` | `Error \| null` | ❌ | Mostra mensagem de erro |
|
|
302
|
+
| `placeholder` | `string` | ❌ | Placeholder do select (padrão: "Selecionar...") |
|
|
303
|
+
| `searchPlaceholder` | `string` | ❌ | Placeholder da busca (padrão: "Buscar...") |
|
|
304
|
+
| `emptyMessage` | `string` | ❌ | Mensagem quando não há itens |
|
|
305
|
+
| `noResultsMessage` | `string` | ❌ | Mensagem quando busca não encontra resultados |
|
|
306
|
+
| `sortItems` | `(a: T, b: T) => number` | ❌ | Função customizada de ordenação (padrão: alfabético) |
|
|
307
|
+
| `disabled` | `boolean` | ❌ | Desabilita o select |
|
|
308
|
+
| `className` | `string` | ❌ | Classes CSS adicionais |
|
|
309
|
+
|
|
310
|
+
**Ordenação Customizada:**
|
|
311
|
+
|
|
312
|
+
Por padrão, itens são ordenados alfabeticamente pelo `label`. Para customizar:
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
<EntitySelect
|
|
316
|
+
items={employees}
|
|
317
|
+
getItemValue={(e) => e.id}
|
|
318
|
+
getItemLabel={(e) => `${e.name} - ${e.department}`}
|
|
319
|
+
sortItems={(a, b) => {
|
|
320
|
+
// Ordenar por departamento, depois por nome
|
|
321
|
+
if (a.department !== b.department) {
|
|
322
|
+
return a.department.localeCompare(b.department);
|
|
323
|
+
}
|
|
324
|
+
return a.name.localeCompare(b.name);
|
|
325
|
+
}}
|
|
326
|
+
/>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Integração com React Hook Form:**
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
import { useForm } from 'react-hook-form';
|
|
333
|
+
import { EntitySelect } from 'forlogic-core';
|
|
334
|
+
|
|
335
|
+
function MyForm() {
|
|
336
|
+
const { control } = useForm();
|
|
337
|
+
const { data: categories = [] } = useCategories();
|
|
338
|
+
|
|
339
|
+
return (
|
|
340
|
+
<FormField
|
|
341
|
+
control={control}
|
|
342
|
+
name="category_id"
|
|
343
|
+
render={({ field }) => (
|
|
344
|
+
<FormItem>
|
|
345
|
+
<FormLabel>Categoria</FormLabel>
|
|
346
|
+
<FormControl>
|
|
347
|
+
<EntitySelect
|
|
348
|
+
value={field.value}
|
|
349
|
+
onChange={field.onChange}
|
|
350
|
+
items={categories}
|
|
351
|
+
getItemValue={(c) => c.id}
|
|
352
|
+
getItemLabel={(c) => c.name}
|
|
353
|
+
placeholder="Selecionar categoria"
|
|
354
|
+
/>
|
|
355
|
+
</FormControl>
|
|
356
|
+
<FormMessage />
|
|
357
|
+
</FormItem>
|
|
358
|
+
)}
|
|
359
|
+
/>
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**Usando em Formulários CRUD:**
|
|
365
|
+
|
|
366
|
+
Para usar o `EntitySelect` em formulários CRUD gerados automaticamente, você precisa criar um componente wrapper e registrá-lo no `BaseForm.tsx`:
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
// 1. Criar wrapper do EntitySelect
|
|
370
|
+
// src/components/ProcessSelect.tsx
|
|
371
|
+
import { EntitySelect } from 'forlogic-core';
|
|
372
|
+
import { useProcesses } from '@/processes/processService';
|
|
373
|
+
|
|
374
|
+
export function ProcessSelect({ value, onChange, disabled }: any) {
|
|
375
|
+
const { data: processes = [], isLoading, error } = useProcesses();
|
|
376
|
+
|
|
377
|
+
return (
|
|
378
|
+
<EntitySelect
|
|
379
|
+
value={value}
|
|
380
|
+
onChange={onChange}
|
|
381
|
+
items={processes}
|
|
382
|
+
isLoading={isLoading}
|
|
383
|
+
error={error}
|
|
384
|
+
getItemValue={(p) => p.id}
|
|
385
|
+
getItemLabel={(p) => p.title}
|
|
386
|
+
disabled={disabled}
|
|
387
|
+
placeholder="Selecionar processo"
|
|
388
|
+
/>
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// 2. Registrar no BaseForm.tsx (lib/crud/components/BaseForm.tsx)
|
|
393
|
+
// Adicionar no switch case do renderField:
|
|
394
|
+
case 'process-select':
|
|
395
|
+
return <ProcessSelect {...commonFieldProps} />;
|
|
396
|
+
|
|
397
|
+
// 3. Usar na configuração do formulário
|
|
398
|
+
{
|
|
399
|
+
name: 'id_process',
|
|
400
|
+
label: 'Processo',
|
|
401
|
+
type: 'process-select' as const,
|
|
402
|
+
required: true
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
**Exemplo Completo:**
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
// src/departments/DepartmentSelect.tsx
|
|
410
|
+
import { EntitySelect } from 'forlogic-core';
|
|
411
|
+
import { useDepartments } from './departmentService';
|
|
412
|
+
|
|
413
|
+
interface DepartmentSelectProps {
|
|
414
|
+
value?: string;
|
|
415
|
+
onChange?: (value: string) => void;
|
|
416
|
+
disabled?: boolean;
|
|
417
|
+
className?: string;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export function DepartmentSelect(props: DepartmentSelectProps) {
|
|
421
|
+
const { data: departments = [], isLoading, error } = useDepartments();
|
|
422
|
+
|
|
423
|
+
return (
|
|
424
|
+
<EntitySelect
|
|
425
|
+
{...props}
|
|
426
|
+
items={departments}
|
|
427
|
+
isLoading={isLoading}
|
|
428
|
+
error={error}
|
|
429
|
+
getItemValue={(dept) => dept.id}
|
|
430
|
+
getItemLabel={(dept) => dept.name}
|
|
431
|
+
placeholder="Selecionar departamento"
|
|
432
|
+
searchPlaceholder="Buscar departamento..."
|
|
433
|
+
emptyMessage="Nenhum departamento cadastrado"
|
|
434
|
+
noResultsMessage="Nenhum departamento encontrado com esse nome"
|
|
435
|
+
/>
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
43
442
|
### ✅ CHECKLIST (antes de implementar)
|
|
44
443
|
|
|
45
444
|
- [ ] Schema `schema` especificado em queries e service?
|
|
46
445
|
- [ ] RLS usando `alias` (nunca `id_user`)?
|
|
446
|
+
- [ ] **Nomenclatura correta**: `id_<entity>`, `is_<bool>`, `<action>_at`?
|
|
447
|
+
- [ ] **Migration SEM índices** (ou aprovados pelo usuário)?
|
|
448
|
+
- [ ] **Imports do forlogic-core** (não criar utils.ts ou ui/* localmente)?
|
|
47
449
|
- [ ] Preservar `item.id` no update?
|
|
48
450
|
- [ ] Config gerado com `useMemo()`?
|
|
49
451
|
- [ ] `<Outlet />` no componente pai?
|