forlogic-core 1.6.13 → 1.7.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 +385 -112
- package/dist/README.md +385 -112
- package/dist/index.esm.js +2 -2
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -220,6 +220,7 @@ Table, TableBody, TableCell, TableHead, TableHeader, TableRow
|
|
|
220
220
|
// Utils
|
|
221
221
|
cn // Merge classes Tailwind
|
|
222
222
|
formatDate, formatDatetime // Formatação de datas
|
|
223
|
+
handleExternalLink // Helper para links externos
|
|
223
224
|
|
|
224
225
|
// Auth
|
|
225
226
|
useAuth // Hook de autenticação
|
|
@@ -237,7 +238,6 @@ TokenManager // Gerenciamento de tokens
|
|
|
237
238
|
createSimpleService // Criar service CRUD
|
|
238
239
|
createCrudPage // Criar página CRUD
|
|
239
240
|
generateCrudConfig // Gerar config CRUD
|
|
240
|
-
createSimpleSaveHandler // Handler de save
|
|
241
241
|
|
|
242
242
|
// Errors
|
|
243
243
|
errorService // Service de erros
|
|
@@ -466,7 +466,7 @@ graph TD
|
|
|
466
466
|
C --> D[Page.tsx<br/>Componente Principal]
|
|
467
467
|
D --> E[createCrudPage<br/>Gerador de CRUD]
|
|
468
468
|
E --> F[CrudTable<br/>Tabela]
|
|
469
|
-
E --> G[
|
|
469
|
+
E --> G[BaseForm<br/>Formulário]
|
|
470
470
|
E --> H[BulkActionBar<br/>Ações em Massa]
|
|
471
471
|
|
|
472
472
|
I[(Supabase DB)] -.->|RLS + Soft Delete| B
|
|
@@ -498,18 +498,16 @@ src/
|
|
|
498
498
|
|
|
499
499
|
```
|
|
500
500
|
✅ OBRIGATÓRIOS:
|
|
501
|
-
□ types.ts - Definir Example extends
|
|
502
|
-
□ types.ts -
|
|
503
|
-
□ types.ts - UpdateExamplePayload extends Partial<CreateExamplePayload>
|
|
501
|
+
□ types.ts - Definir Example extends BaseEntity com campos explícitos
|
|
502
|
+
□ types.ts - Usar Omit<> e Partial<> para CreatePayload e UpdatePayload
|
|
504
503
|
□ Service.ts - Usar createSimpleService<Example, CreatePayload, UpdatePayload>
|
|
505
504
|
□ Service.ts - Configurar tableName, entityName, searchFields
|
|
506
505
|
□ Page.tsx - Chamar useExamplesCrud() DENTRO do componente
|
|
507
506
|
□ Page.tsx - Usar useMemo() para columns, formSections, config
|
|
508
|
-
□ Page.tsx -
|
|
509
|
-
□ Page.tsx - Passar alias no CreatePayload
|
|
507
|
+
□ Page.tsx - Usar manager.save() ao invés de createSimpleSaveHandler
|
|
510
508
|
|
|
511
509
|
🔧 OPCIONAIS:
|
|
512
|
-
□ Filtros customizados (
|
|
510
|
+
□ Filtros customizados (backend via additionalFilters recomendado)
|
|
513
511
|
□ onToggleStatus para ativar/desativar
|
|
514
512
|
□ Renderização customizada nas colunas
|
|
515
513
|
□ Campos de formulário customizados
|
|
@@ -709,24 +707,27 @@ export const { service: processService, useCrudHook: useProcesses } =
|
|
|
709
707
|
});
|
|
710
708
|
```
|
|
711
709
|
|
|
712
|
-
### **3️⃣ Save Handler**
|
|
710
|
+
### **3️⃣ Save Handler (Integrado ao useCrud)**
|
|
713
711
|
```typescript
|
|
714
712
|
// src/processes/ProcessesPage.tsx
|
|
715
|
-
import { createSimpleSaveHandler } from 'forlogic-core';
|
|
716
|
-
import { processService } from './processService';
|
|
717
|
-
|
|
718
|
-
const handleSave = createSimpleSaveHandler({
|
|
719
|
-
service: processService,
|
|
720
|
-
entityName: 'processo'
|
|
721
|
-
});
|
|
722
713
|
|
|
723
|
-
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
714
|
+
export default function ProcessesPage() {
|
|
715
|
+
const manager = useProcesses();
|
|
716
|
+
|
|
717
|
+
const handleSave = (data: any) => {
|
|
718
|
+
manager.save(data, (d) => ({
|
|
719
|
+
title: d.title,
|
|
720
|
+
description: d.description || null,
|
|
721
|
+
status: d.status || 'draft'
|
|
722
|
+
}));
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
// O método save() automaticamente:
|
|
726
|
+
// ✅ Detecta se é CREATE (sem id) ou UPDATE (com id)
|
|
727
|
+
// ✅ Injeta o alias no CREATE
|
|
728
|
+
// ✅ Chama createEntity ou updateEntity
|
|
729
|
+
// ✅ Fecha o modal automaticamente após sucesso
|
|
730
|
+
}
|
|
730
731
|
```
|
|
731
732
|
|
|
732
733
|
### **4️⃣ Config (com useMemo)**
|
|
@@ -789,6 +790,282 @@ function ProcessLayout() {
|
|
|
789
790
|
|
|
790
791
|
---
|
|
791
792
|
|
|
793
|
+
## 🔄 GUIA DE MIGRAÇÃO - v2.0
|
|
794
|
+
|
|
795
|
+
Se você tem projetos usando versões antigas do `forlogic-core`, siga este guia para atualizar.
|
|
796
|
+
|
|
797
|
+
### **📦 Atualizar Versão**
|
|
798
|
+
|
|
799
|
+
```bash
|
|
800
|
+
npm install forlogic-core@latest
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
---
|
|
804
|
+
|
|
805
|
+
### **1️⃣ Migração de Tipos (Breaking Change)**
|
|
806
|
+
|
|
807
|
+
#### **❌ ANTES (Versão Antiga):**
|
|
808
|
+
```typescript
|
|
809
|
+
import {
|
|
810
|
+
ContentEntity,
|
|
811
|
+
VisualEntity,
|
|
812
|
+
UserRelatedEntity,
|
|
813
|
+
ActivableEntity,
|
|
814
|
+
FormEntity
|
|
815
|
+
} from 'forlogic-core';
|
|
816
|
+
|
|
817
|
+
export interface Example extends
|
|
818
|
+
ContentEntity,
|
|
819
|
+
VisualEntity,
|
|
820
|
+
UserRelatedEntity,
|
|
821
|
+
ActivableEntity,
|
|
822
|
+
FormEntity {}
|
|
823
|
+
|
|
824
|
+
export interface CreateExamplePayload {
|
|
825
|
+
title: string;
|
|
826
|
+
description?: string | null;
|
|
827
|
+
alias: string; // ⚠️ Obrigatório manualmente
|
|
828
|
+
color?: string;
|
|
829
|
+
icon_name?: string;
|
|
830
|
+
id_user?: string | null;
|
|
831
|
+
is_actived?: boolean;
|
|
832
|
+
url_field?: string | null;
|
|
833
|
+
date_field?: string | null;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
export interface UpdateExamplePayload extends Partial<CreateExamplePayload> {
|
|
837
|
+
title: string; // Override
|
|
838
|
+
}
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
#### **✅ DEPOIS (Nova API):**
|
|
842
|
+
```typescript
|
|
843
|
+
import { BaseEntity } from 'forlogic-core';
|
|
844
|
+
|
|
845
|
+
export interface Example extends BaseEntity {
|
|
846
|
+
title: string;
|
|
847
|
+
description?: string | null;
|
|
848
|
+
color?: string;
|
|
849
|
+
icon_name?: string;
|
|
850
|
+
id_user?: string | null;
|
|
851
|
+
responsible_name?: string;
|
|
852
|
+
url_field?: string | null;
|
|
853
|
+
date_field?: string | null;
|
|
854
|
+
// is_actived agora vem de BaseEntity!
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
export type CreateExamplePayload = Omit<
|
|
858
|
+
Example,
|
|
859
|
+
keyof BaseEntity | 'responsible_name'
|
|
860
|
+
>;
|
|
861
|
+
|
|
862
|
+
export type UpdateExamplePayload = Partial<CreateExamplePayload>;
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
**📝 Mudanças:**
|
|
866
|
+
- ❌ **REMOVIDO**: Helper interfaces (`ContentEntity`, `VisualEntity`, etc)
|
|
867
|
+
- ✅ **NOVO**: Apenas `BaseEntity` + campos explícitos
|
|
868
|
+
- ✅ **NOVO**: `is_actived` agora é campo padrão de `BaseEntity`
|
|
869
|
+
- ✅ **NOVO**: Use `Omit<>` e `Partial<>` para Payloads
|
|
870
|
+
|
|
871
|
+
---
|
|
872
|
+
|
|
873
|
+
### **2️⃣ Migração de Save Handler (Breaking Change)**
|
|
874
|
+
|
|
875
|
+
#### **❌ ANTES (createSimpleSaveHandler):**
|
|
876
|
+
```typescript
|
|
877
|
+
import { createSimpleSaveHandler, useAuth } from 'forlogic-core';
|
|
878
|
+
|
|
879
|
+
const { alias: currentAlias } = useAuth();
|
|
880
|
+
|
|
881
|
+
const handleSave = createSimpleSaveHandler(
|
|
882
|
+
manager,
|
|
883
|
+
// createTransform
|
|
884
|
+
(data) => ({
|
|
885
|
+
title: data.title,
|
|
886
|
+
description: data.description || null,
|
|
887
|
+
alias: currentAlias // ⚠️ Injetar manualmente
|
|
888
|
+
}),
|
|
889
|
+
// updateTransform
|
|
890
|
+
(data) => ({
|
|
891
|
+
title: data.title,
|
|
892
|
+
description: data.description || null
|
|
893
|
+
})
|
|
894
|
+
);
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
#### **✅ DEPOIS (manager.save):**
|
|
898
|
+
```typescript
|
|
899
|
+
const handleSave = (data: any) => {
|
|
900
|
+
manager.save(data, (d) => ({
|
|
901
|
+
title: d.title,
|
|
902
|
+
description: d.description || null
|
|
903
|
+
}));
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
// O método save() faz automaticamente:
|
|
907
|
+
// ✅ Detecta CREATE vs UPDATE (baseado em data.id)
|
|
908
|
+
// ✅ Injeta alias no CREATE
|
|
909
|
+
// ✅ Fecha modal após sucesso
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
**📝 Mudanças:**
|
|
913
|
+
- ❌ **REMOVIDO**: `createSimpleSaveHandler`
|
|
914
|
+
- ❌ **REMOVIDO**: Import de `useAuth` para pegar `alias`
|
|
915
|
+
- ✅ **NOVO**: `manager.save(data, transform)`
|
|
916
|
+
- ✅ **NOVO**: Alias injetado automaticamente
|
|
917
|
+
|
|
918
|
+
---
|
|
919
|
+
|
|
920
|
+
### **3️⃣ Migração de Campos de Formulário**
|
|
921
|
+
|
|
922
|
+
#### **❌ ANTES (Tipos Múltiplos):**
|
|
923
|
+
```typescript
|
|
924
|
+
{
|
|
925
|
+
name: 'id_user',
|
|
926
|
+
label: 'Responsável',
|
|
927
|
+
type: 'simple-qualiex-user-field', // OU 'single-responsible-select'
|
|
928
|
+
required: true
|
|
929
|
+
}
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
#### **✅ DEPOIS (Tipo Unificado):**
|
|
933
|
+
```typescript
|
|
934
|
+
{
|
|
935
|
+
name: 'id_user',
|
|
936
|
+
label: 'Responsável',
|
|
937
|
+
type: 'user-select',
|
|
938
|
+
mode: 'single', // OU 'multiple'
|
|
939
|
+
required: true
|
|
940
|
+
}
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
**📝 Mudanças:**
|
|
944
|
+
- ❌ **REMOVIDO**: `'simple-qualiex-user-field'`, `'single-responsible-select'`
|
|
945
|
+
- ✅ **NOVO**: Apenas `'user-select'` com parâmetro `mode`
|
|
946
|
+
|
|
947
|
+
---
|
|
948
|
+
|
|
949
|
+
### **4️⃣ Migração de Imports**
|
|
950
|
+
|
|
951
|
+
#### **❌ ANTES:**
|
|
952
|
+
```typescript
|
|
953
|
+
import { createSimpleSaveHandler } from 'forlogic-core';
|
|
954
|
+
import { ContentEntity, VisualEntity, ... } from 'forlogic-core';
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
#### **✅ DEPOIS:**
|
|
958
|
+
```typescript
|
|
959
|
+
// createSimpleSaveHandler removido (usar manager.save)
|
|
960
|
+
import { BaseEntity } from 'forlogic-core';
|
|
961
|
+
import { handleExternalLink } from 'forlogic-core'; // NOVO helper
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
**📝 Mudanças:**
|
|
965
|
+
- ❌ **REMOVIDO**: `createSimpleSaveHandler`
|
|
966
|
+
- ❌ **REMOVIDO**: Helper interfaces de tipos
|
|
967
|
+
- ✅ **NOVO**: `handleExternalLink` (helper de links externos)
|
|
968
|
+
|
|
969
|
+
---
|
|
970
|
+
|
|
971
|
+
### **5️⃣ Migração de Filtros Customizados**
|
|
972
|
+
|
|
973
|
+
#### **❌ ANTES (Filtro Frontend):**
|
|
974
|
+
```typescript
|
|
975
|
+
const [statusFilter, setStatusFilter] = useState('active');
|
|
976
|
+
|
|
977
|
+
const filteredEntities = useMemo(() => {
|
|
978
|
+
return manager.entities.filter(e =>
|
|
979
|
+
statusFilter === 'all' ? true : e.is_actived
|
|
980
|
+
);
|
|
981
|
+
}, [manager.entities, statusFilter]);
|
|
982
|
+
|
|
983
|
+
const filteredManager = useMemo(() => ({
|
|
984
|
+
...manager,
|
|
985
|
+
entities: filteredEntities
|
|
986
|
+
}), [manager, filteredEntities]);
|
|
987
|
+
|
|
988
|
+
// Passar filteredManager para createCrudPage
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
#### **✅ DEPOIS (Filtro Backend - Recomendado):**
|
|
992
|
+
```typescript
|
|
993
|
+
const [statusFilter, setStatusFilter] = useState<boolean | 'all'>(true);
|
|
994
|
+
|
|
995
|
+
// Passar filtro direto para o hook
|
|
996
|
+
const manager = useExamplesCrud(
|
|
997
|
+
statusFilter === 'all' ? {} : { is_actived: statusFilter }
|
|
998
|
+
);
|
|
999
|
+
|
|
1000
|
+
// Passar manager original (já vem filtrado)
|
|
1001
|
+
const CrudPage = createCrudPage({ manager, config, onSave });
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
**📝 Mudanças:**
|
|
1005
|
+
- ✅ **RECOMENDADO**: Filtro aplicado no backend (melhor performance)
|
|
1006
|
+
- ✅ **NOVO**: Hook aceita `additionalFilters` como parâmetro
|
|
1007
|
+
- ❌ **EVITAR**: Filtro frontend (só para casos complexos)
|
|
1008
|
+
|
|
1009
|
+
---
|
|
1010
|
+
|
|
1011
|
+
### **6️⃣ Checklist de Migração**
|
|
1012
|
+
|
|
1013
|
+
Use este checklist para validar que seu projeto foi migrado corretamente:
|
|
1014
|
+
|
|
1015
|
+
#### **Types (`example.ts`):**
|
|
1016
|
+
- [ ] Removido imports de helper interfaces (`ContentEntity`, `VisualEntity`, etc)
|
|
1017
|
+
- [ ] Interface principal agora estende apenas `BaseEntity`
|
|
1018
|
+
- [ ] Campos explícitos declarados na interface
|
|
1019
|
+
- [ ] `CreatePayload` usa `Omit<Example, keyof BaseEntity | 'responsible_name'>`
|
|
1020
|
+
- [ ] `UpdatePayload` usa `Partial<CreatePayload>`
|
|
1021
|
+
- [ ] Removido `alias: string` do `CreatePayload`
|
|
1022
|
+
- [ ] Removido interfaces/types não usados (`ExampleFilters`, `ExampleSortField`, `ExampleInsert`, `ExampleUpdate`)
|
|
1023
|
+
|
|
1024
|
+
#### **Service (`ExampleService.ts`):**
|
|
1025
|
+
- [ ] Nenhuma mudança necessária (API permanece igual)
|
|
1026
|
+
|
|
1027
|
+
#### **Page (`ExamplesPage.tsx`):**
|
|
1028
|
+
- [ ] Removido import de `createSimpleSaveHandler`
|
|
1029
|
+
- [ ] Removido import de `useAuth` (se usado apenas para alias)
|
|
1030
|
+
- [ ] Substituído `createSimpleSaveHandler` por `manager.save()`
|
|
1031
|
+
- [ ] Campos de formulário tipo `'user-select'` ao invés de tipos antigos
|
|
1032
|
+
- [ ] Filtros usando backend (`useExamplesCrud(filters)`) quando possível
|
|
1033
|
+
- [ ] Substituído lógica de links externos por `handleExternalLink` helper
|
|
1034
|
+
|
|
1035
|
+
#### **Testes:**
|
|
1036
|
+
- [ ] Build sem erros TypeScript (`npm run build`)
|
|
1037
|
+
- [ ] Página carrega sem erros
|
|
1038
|
+
- [ ] Criar novo item funciona (alias injetado corretamente)
|
|
1039
|
+
- [ ] Editar item funciona
|
|
1040
|
+
- [ ] Deletar item funciona
|
|
1041
|
+
- [ ] Filtros funcionam corretamente
|
|
1042
|
+
- [ ] Paginação funciona
|
|
1043
|
+
- [ ] Busca funciona
|
|
1044
|
+
|
|
1045
|
+
---
|
|
1046
|
+
|
|
1047
|
+
### **7️⃣ Exemplo Completo de Migração**
|
|
1048
|
+
|
|
1049
|
+
**Ver arquivo `src/examples/ExamplesPage.tsx` do projeto para exemplo 100% atualizado.**
|
|
1050
|
+
|
|
1051
|
+
---
|
|
1052
|
+
|
|
1053
|
+
### **❓ Problemas na Migração?**
|
|
1054
|
+
|
|
1055
|
+
#### **Erro: "Property 'save' does not exist on type..."**
|
|
1056
|
+
- ✅ **Solução**: Atualize `forlogic-core` para versão mais recente
|
|
1057
|
+
- ✅ **Comando**: `npm install forlogic-core@latest`
|
|
1058
|
+
|
|
1059
|
+
#### **Erro: "Cannot find module 'ContentEntity'"**
|
|
1060
|
+
- ✅ **Solução**: Remova imports de helper interfaces e use `BaseEntity`
|
|
1061
|
+
- ✅ **Ver**: Seção "1️⃣ Migração de Tipos" acima
|
|
1062
|
+
|
|
1063
|
+
#### **Erro: "alias is required but not provided"**
|
|
1064
|
+
- ✅ **Solução**: Use `manager.save()` ao invés de `createEntity` direto
|
|
1065
|
+
- ✅ **Ver**: Seção "2️⃣ Migração de Save Handler" acima
|
|
1066
|
+
|
|
1067
|
+
---
|
|
1068
|
+
|
|
792
1069
|
## 🎯 AÇÕES EM LOTE (Bulk Actions)
|
|
793
1070
|
|
|
794
1071
|
O sistema CRUD suporta seleção múltipla e ações em lote usando checkboxes.
|
|
@@ -967,61 +1244,52 @@ import {
|
|
|
967
1244
|
/**
|
|
968
1245
|
* Example - Entidade completa de exemplo
|
|
969
1246
|
*
|
|
970
|
-
* ✅
|
|
971
|
-
* -
|
|
972
|
-
* -
|
|
973
|
-
* -
|
|
974
|
-
* -
|
|
975
|
-
* - FormEntity: fornece url_field, date_field
|
|
1247
|
+
* ✅ Campos Customizados:
|
|
1248
|
+
* - title, description (conteúdo)
|
|
1249
|
+
* - color, icon_name (visual)
|
|
1250
|
+
* - id_user, responsible_name (usuário - enriquecido via Qualiex)
|
|
1251
|
+
* - url_field, date_field (formulário)
|
|
976
1252
|
*
|
|
977
1253
|
* 🔒 Campos Herdados de BaseEntity (automáticos):
|
|
978
1254
|
* - id: string
|
|
979
1255
|
* - alias: string
|
|
980
1256
|
* - company_id: string
|
|
1257
|
+
* - is_actived: boolean
|
|
981
1258
|
* - is_removed: boolean
|
|
982
1259
|
* - created_at: string
|
|
983
1260
|
* - updated_at: string
|
|
984
1261
|
*/
|
|
985
|
-
export interface Example extends
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1262
|
+
export interface Example extends BaseEntity {
|
|
1263
|
+
title: string;
|
|
1264
|
+
description?: string | null;
|
|
1265
|
+
color?: string;
|
|
1266
|
+
icon_name?: string;
|
|
1267
|
+
id_user?: string | null;
|
|
1268
|
+
responsible_name?: string; // Enriquecido via Qualiex
|
|
1269
|
+
url_field?: string | null;
|
|
1270
|
+
date_field?: string | null;
|
|
1271
|
+
}
|
|
991
1272
|
|
|
992
1273
|
/**
|
|
993
1274
|
* CreateExamplePayload - Dados para CRIAR novo registro
|
|
994
1275
|
*
|
|
995
1276
|
* ⚠️ IMPORTANTE:
|
|
996
|
-
* -
|
|
1277
|
+
* - Campo `alias` é injetado AUTOMATICAMENTE pelo manager.save()
|
|
997
1278
|
* - Campos opcionais devem ter `| null`
|
|
998
1279
|
* - NÃO incluir id, created_at, updated_at (gerados automaticamente)
|
|
999
1280
|
*/
|
|
1000
|
-
export
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
url_field?: string | null;
|
|
1005
|
-
date_field?: string | null;
|
|
1006
|
-
color?: string;
|
|
1007
|
-
icon_name?: string;
|
|
1008
|
-
id_user?: string | null;
|
|
1009
|
-
is_actived?: boolean;
|
|
1010
|
-
}
|
|
1281
|
+
export type CreateExamplePayload = Omit<
|
|
1282
|
+
Example,
|
|
1283
|
+
keyof BaseEntity | 'responsible_name'
|
|
1284
|
+
>;
|
|
1011
1285
|
|
|
1012
1286
|
/**
|
|
1013
1287
|
* UpdateExamplePayload - Dados para ATUALIZAR registro existente
|
|
1014
1288
|
*
|
|
1015
1289
|
* 📝 Pattern:
|
|
1016
|
-
* -
|
|
1017
|
-
* - MAS title é obrigatório (override)
|
|
1290
|
+
* - Todos os campos são opcionais (Partial)
|
|
1018
1291
|
*/
|
|
1019
|
-
export
|
|
1020
|
-
title: string; // ✅ Override: title obrigatório mesmo no update
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
export type ExampleInsert = CreateExamplePayload;
|
|
1024
|
-
export type ExampleUpdate = UpdateExamplePayload;
|
|
1292
|
+
export type UpdateExamplePayload = Partial<CreateExamplePayload>;
|
|
1025
1293
|
```
|
|
1026
1294
|
|
|
1027
1295
|
**📖 Explicação Detalhada:**
|
|
@@ -1138,7 +1406,7 @@ import { useState, useMemo } from 'react';
|
|
|
1138
1406
|
* - 'select' - Dropdown com options
|
|
1139
1407
|
* - 'color-picker' - Seletor de cor
|
|
1140
1408
|
* - 'icon-picker' - Seletor de ícone Lucide
|
|
1141
|
-
* - '
|
|
1409
|
+
* - 'user-select' - Seletor de usuário (com mode: 'single' | 'multiple')
|
|
1142
1410
|
* - 'custom' - Campo completamente customizado
|
|
1143
1411
|
* - 'group' - Agrupa campos horizontalmente
|
|
1144
1412
|
*/
|
|
@@ -1164,7 +1432,8 @@ const formSections = [{
|
|
|
1164
1432
|
{
|
|
1165
1433
|
name: 'id_user',
|
|
1166
1434
|
label: 'Responsável',
|
|
1167
|
-
type: '
|
|
1435
|
+
type: 'user-select' as const, // 👤 Campo unificado
|
|
1436
|
+
mode: 'single',
|
|
1168
1437
|
required: true,
|
|
1169
1438
|
placeholder: 'Selecionar responsável',
|
|
1170
1439
|
defaultValue: '',
|
|
@@ -1231,45 +1500,37 @@ Ver código completo no arquivo `src/examples/ExamplesPage.tsx` do projeto.
|
|
|
1231
1500
|
|
|
1232
1501
|
## 🎯 PADRÕES DE FILTROS CUSTOMIZADOS
|
|
1233
1502
|
|
|
1234
|
-
### **Pattern 1: Filtro de Status (
|
|
1503
|
+
### **Pattern 1: Filtro de Status (Backend - Recomendado)**
|
|
1235
1504
|
|
|
1236
1505
|
```typescript
|
|
1237
|
-
// ✅ PADRÃO
|
|
1506
|
+
// ✅ PADRÃO BACKEND: Filtro aplicado na query SQL (melhor performance)
|
|
1238
1507
|
|
|
1239
1508
|
// 1) Estado do filtro
|
|
1240
|
-
const [statusFilter, setStatusFilter] = useState<
|
|
1241
|
-
|
|
1242
|
-
// 2) Estado derivado (filtra entities)
|
|
1243
|
-
const filteredEntities = useMemo(() => {
|
|
1244
|
-
if (statusFilter === 'all') return manager.entities;
|
|
1245
|
-
return manager.entities.filter(e =>
|
|
1246
|
-
statusFilter === 'active' ? e.is_actived : !e.is_actived
|
|
1247
|
-
);
|
|
1248
|
-
}, [manager.entities, statusFilter]);
|
|
1509
|
+
const [statusFilter, setStatusFilter] = useState<boolean | 'all'>(true);
|
|
1249
1510
|
|
|
1250
|
-
//
|
|
1251
|
-
const
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
}), [manager, filteredEntities]);
|
|
1511
|
+
// 2) Passar filtro para o hook (aplica no backend)
|
|
1512
|
+
const manager = useExamplesCrud(
|
|
1513
|
+
statusFilter === 'all' ? {} : { is_actived: statusFilter }
|
|
1514
|
+
);
|
|
1255
1515
|
|
|
1256
|
-
//
|
|
1516
|
+
// 3) Componente do filtro
|
|
1257
1517
|
const StatusFilter = () => (
|
|
1258
1518
|
<EntitySelect
|
|
1259
|
-
value={statusFilter}
|
|
1260
|
-
onChange={(
|
|
1519
|
+
value={String(statusFilter)}
|
|
1520
|
+
onChange={(v) => setStatusFilter(v === 'all' ? 'all' : v === 'true')}
|
|
1261
1521
|
items={[
|
|
1262
|
-
{ id: '
|
|
1263
|
-
{ id: '
|
|
1522
|
+
{ id: 'true', name: 'Ativo' },
|
|
1523
|
+
{ id: 'false', name: 'Inativo' },
|
|
1264
1524
|
{ id: 'all', name: '[Todos]' }
|
|
1265
1525
|
]}
|
|
1266
1526
|
getItemValue={(item) => item.id}
|
|
1267
1527
|
getItemLabel={(item) => item.name}
|
|
1268
1528
|
placeholder="Status"
|
|
1529
|
+
className="w-full sm:w-[180px]"
|
|
1269
1530
|
/>
|
|
1270
1531
|
);
|
|
1271
1532
|
|
|
1272
|
-
//
|
|
1533
|
+
// 4) Usar em config.filters
|
|
1273
1534
|
config: {
|
|
1274
1535
|
filters: [
|
|
1275
1536
|
{ type: 'search' },
|
|
@@ -1277,14 +1538,39 @@ config: {
|
|
|
1277
1538
|
]
|
|
1278
1539
|
}
|
|
1279
1540
|
|
|
1280
|
-
//
|
|
1541
|
+
// 5) Passar manager original (filtro já aplicado)
|
|
1281
1542
|
const CrudPage = createCrudPage({
|
|
1282
|
-
manager
|
|
1543
|
+
manager, // ← Manager já vem filtrado do backend
|
|
1283
1544
|
config,
|
|
1284
1545
|
onSave
|
|
1285
1546
|
});
|
|
1286
1547
|
```
|
|
1287
1548
|
|
|
1549
|
+
### **Pattern 1B: Filtro de Status (Frontend - Casos Específicos)**
|
|
1550
|
+
|
|
1551
|
+
```typescript
|
|
1552
|
+
// Use filtro frontend APENAS se:
|
|
1553
|
+
// ✅ Precisa combinar múltiplas propriedades (ex: status + tipo)
|
|
1554
|
+
// ✅ Lógica de filtro muito complexa para SQL
|
|
1555
|
+
// ❌ NÃO use para filtros simples (pior performance)
|
|
1556
|
+
|
|
1557
|
+
const [statusFilter, setStatusFilter] = useState<boolean | 'all'>(true);
|
|
1558
|
+
|
|
1559
|
+
const filteredEntities = useMemo(() => {
|
|
1560
|
+
if (statusFilter === 'all') return manager.entities;
|
|
1561
|
+
return manager.entities.filter(e => e.is_actived === statusFilter);
|
|
1562
|
+
}, [manager.entities, statusFilter]);
|
|
1563
|
+
|
|
1564
|
+
const filteredManager = useMemo(() => ({
|
|
1565
|
+
...manager,
|
|
1566
|
+
entities: filteredEntities,
|
|
1567
|
+
pagination: {
|
|
1568
|
+
...manager.pagination,
|
|
1569
|
+
totalItems: filteredEntities.length // ⚠️ Atualizar contador
|
|
1570
|
+
}
|
|
1571
|
+
}), [manager, filteredEntities]);
|
|
1572
|
+
```
|
|
1573
|
+
|
|
1288
1574
|
**📖 Explicação:**
|
|
1289
1575
|
- **`useState`**: Armazena valor selecionado no filtro
|
|
1290
1576
|
- **`useMemo` (filteredEntities)**: Evita re-filtrar a cada render
|
|
@@ -1466,31 +1752,25 @@ const filteredEntities = useMemo(() =>
|
|
|
1466
1752
|
|
|
1467
1753
|
---
|
|
1468
1754
|
|
|
1469
|
-
### **Erro 5: Esquecer
|
|
1755
|
+
### **Erro 5: Esquecer de usar `manager.save()`**
|
|
1470
1756
|
|
|
1471
1757
|
```typescript
|
|
1472
1758
|
// ❌ SINTOMA: Erro de RLS no Supabase, registro não é criado
|
|
1473
|
-
// ❌ CAUSA:
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
// ← Falta alias!
|
|
1480
|
-
})
|
|
1481
|
-
);
|
|
1482
|
-
|
|
1483
|
-
// ✅ SOLUÇÃO: Incluir alias do token
|
|
1484
|
-
const { alias: currentAlias } = useAuth();
|
|
1759
|
+
// ❌ CAUSA: Usar createEntity/updateEntity direto sem alias
|
|
1760
|
+
manager.createEntity({
|
|
1761
|
+
title: data.title,
|
|
1762
|
+
email: data.email
|
|
1763
|
+
// ← Falta alias!
|
|
1764
|
+
});
|
|
1485
1765
|
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
(data) => ({
|
|
1489
|
-
title:
|
|
1490
|
-
email:
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1766
|
+
// ✅ SOLUÇÃO: Usar manager.save() que injeta alias automaticamente
|
|
1767
|
+
const handleSave = (data: any) => {
|
|
1768
|
+
manager.save(data, (d) => ({
|
|
1769
|
+
title: d.title,
|
|
1770
|
+
email: d.email
|
|
1771
|
+
}));
|
|
1772
|
+
// O save() detecta CREATE vs UPDATE e injeta alias automaticamente
|
|
1773
|
+
};
|
|
1494
1774
|
```
|
|
1495
1775
|
|
|
1496
1776
|
---
|
|
@@ -1638,19 +1918,12 @@ import { QualiexUserField, QualiexResponsibleSelectField } from 'forlogic-core';
|
|
|
1638
1918
|
|
|
1639
1919
|
**Componentes em formulários CRUD:**
|
|
1640
1920
|
```typescript
|
|
1641
|
-
// Para seleção de usuário
|
|
1642
|
-
{
|
|
1643
|
-
name: 'responsible_id',
|
|
1644
|
-
label: 'Responsável',
|
|
1645
|
-
type: 'simple-qualiex-user-field' as const,
|
|
1646
|
-
required: true
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
// Para seleção de responsável
|
|
1921
|
+
// Para seleção de usuário (modo unificado)
|
|
1650
1922
|
{
|
|
1651
1923
|
name: 'responsible_id',
|
|
1652
1924
|
label: 'Responsável',
|
|
1653
|
-
type: '
|
|
1925
|
+
type: 'user-select' as const,
|
|
1926
|
+
mode: 'single', // ou 'multiple'
|
|
1654
1927
|
required: true
|
|
1655
1928
|
}
|
|
1656
1929
|
```
|