forlogic-core 1.6.4 → 1.6.5
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 +260 -1
- package/dist/README.md +260 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -906,6 +906,265 @@ fetch(url, { headers: { 'un-alias': 'true' } }); // ✅
|
|
|
906
906
|
|
|
907
907
|
---
|
|
908
908
|
|
|
909
|
+
## 🔍 FILTROS NO BACKEND (Backend Filtering)
|
|
910
|
+
|
|
911
|
+
### **Por que Filtros no Backend?**
|
|
912
|
+
|
|
913
|
+
Filtrar dados no **backend** é a abordagem recomendada porque:
|
|
914
|
+
- ✅ **Paginação correta**: Total de itens reflete os dados filtrados
|
|
915
|
+
- ✅ **Performance**: Menos dados trafegados pela rede
|
|
916
|
+
- ✅ **Escalabilidade**: Funciona com milhares de registros
|
|
917
|
+
- ✅ **Ordenação**: Ordena apenas os dados filtrados
|
|
918
|
+
|
|
919
|
+
### **Como Implementar Filtros no Backend**
|
|
920
|
+
|
|
921
|
+
#### **1️⃣ Filtros Estáticos (via additionalFilters)**
|
|
922
|
+
|
|
923
|
+
Para filtros simples e estáticos, use `additionalFilters` no service:
|
|
924
|
+
|
|
925
|
+
```typescript
|
|
926
|
+
// src/subprocesses/SubprocessService.ts
|
|
927
|
+
import { createSimpleService } from 'forlogic-core';
|
|
928
|
+
import { Subprocess, SubprocessInsert, SubprocessUpdate } from './types';
|
|
929
|
+
|
|
930
|
+
export const {
|
|
931
|
+
service: subprocessService,
|
|
932
|
+
useCrudHook: baseUseCrudHook
|
|
933
|
+
} = createSimpleService<Subprocess, SubprocessInsert, SubprocessUpdate>({
|
|
934
|
+
tableName: 'subprocesses',
|
|
935
|
+
entityName: 'subprocesso',
|
|
936
|
+
schemaName: 'processes',
|
|
937
|
+
searchFields: ['title', 'description'],
|
|
938
|
+
enableQualiexEnrichment: true,
|
|
939
|
+
// Filtros estáticos aplicados sempre
|
|
940
|
+
additionalFilters: [
|
|
941
|
+
{ field: 'is_removed', operator: 'eq', value: false }
|
|
942
|
+
]
|
|
943
|
+
});
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
#### **2️⃣ Filtros Dinâmicos (via Hook Customizado)**
|
|
947
|
+
|
|
948
|
+
Para filtros dinâmicos controlados por URL ou estado, crie um hook wrapper:
|
|
949
|
+
|
|
950
|
+
```typescript
|
|
951
|
+
// src/subprocesses/SubprocessService.ts
|
|
952
|
+
import { useMemo } from 'react';
|
|
953
|
+
import { createSimpleService } from 'forlogic-core';
|
|
954
|
+
|
|
955
|
+
// Service base
|
|
956
|
+
export const {
|
|
957
|
+
service: subprocessService,
|
|
958
|
+
useCrudHook: baseUseCrudHook
|
|
959
|
+
} = createSimpleService<Subprocess, SubprocessInsert, SubprocessUpdate>({
|
|
960
|
+
tableName: 'subprocesses',
|
|
961
|
+
entityName: 'subprocesso',
|
|
962
|
+
schemaName: 'processes',
|
|
963
|
+
searchFields: ['title', 'description']
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
// Hook customizado que aceita filtros dinâmicos
|
|
967
|
+
export function useSubprocessesCrud(filters?: {
|
|
968
|
+
is_actived?: boolean;
|
|
969
|
+
id_process?: string | 'none';
|
|
970
|
+
}) {
|
|
971
|
+
// Transforma filtros em additionalFilters
|
|
972
|
+
const additionalFilters = useMemo(() => {
|
|
973
|
+
const filterList: any[] = [];
|
|
974
|
+
|
|
975
|
+
// Filtro de status (ativo/inativo)
|
|
976
|
+
if (filters?.is_actived !== undefined) {
|
|
977
|
+
filterList.push({
|
|
978
|
+
field: 'is_actived',
|
|
979
|
+
operator: 'eq',
|
|
980
|
+
value: filters.is_actived
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// Filtro de processo (incluindo "sem processo")
|
|
985
|
+
if (filters?.id_process) {
|
|
986
|
+
if (filters.id_process === 'none') {
|
|
987
|
+
// Operador especial para NULL
|
|
988
|
+
filterList.push({
|
|
989
|
+
field: 'id_process',
|
|
990
|
+
operator: 'is_null'
|
|
991
|
+
});
|
|
992
|
+
} else {
|
|
993
|
+
filterList.push({
|
|
994
|
+
field: 'id_process',
|
|
995
|
+
operator: 'eq',
|
|
996
|
+
value: filters.id_process
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
return filterList;
|
|
1002
|
+
}, [filters?.is_actived, filters?.id_process]);
|
|
1003
|
+
|
|
1004
|
+
// Chama hook base com filtros dinâmicos
|
|
1005
|
+
return baseUseCrudHook({ additionalFilters });
|
|
1006
|
+
}
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
#### **3️⃣ Usar na Página com URL Search Params**
|
|
1010
|
+
|
|
1011
|
+
```typescript
|
|
1012
|
+
// src/subprocesses/SubprocessesPage.tsx
|
|
1013
|
+
import { useSearchParams } from 'react-router-dom';
|
|
1014
|
+
import { useSubprocessesCrud } from './SubprocessService';
|
|
1015
|
+
|
|
1016
|
+
export default function SubprocessesPage() {
|
|
1017
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
1018
|
+
|
|
1019
|
+
// Ler filtros da URL
|
|
1020
|
+
const filters = useMemo(() => ({
|
|
1021
|
+
is_actived: searchParams.get('is_actived') === 'true',
|
|
1022
|
+
id_process: searchParams.get('id_process') || undefined
|
|
1023
|
+
}), [searchParams]);
|
|
1024
|
+
|
|
1025
|
+
// Manager com filtros aplicados no backend
|
|
1026
|
+
const manager = useSubprocessesCrud(filters);
|
|
1027
|
+
|
|
1028
|
+
// Config com filtros de UI
|
|
1029
|
+
const config = useMemo(() => generateCrudConfig<Subprocess>({
|
|
1030
|
+
entityName: 'Subprocesso',
|
|
1031
|
+
entityNamePlural: 'Subprocessos',
|
|
1032
|
+
filters: [
|
|
1033
|
+
{
|
|
1034
|
+
type: 'custom',
|
|
1035
|
+
component: StatusSelect,
|
|
1036
|
+
props: {
|
|
1037
|
+
value: searchParams.get('is_actived') || 'all',
|
|
1038
|
+
onChange: (value: string) => {
|
|
1039
|
+
if (value === 'all') {
|
|
1040
|
+
searchParams.delete('is_actived');
|
|
1041
|
+
} else {
|
|
1042
|
+
searchParams.set('is_actived', value === 'active' ? 'true' : 'false');
|
|
1043
|
+
}
|
|
1044
|
+
setSearchParams(searchParams);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
type: 'custom',
|
|
1050
|
+
component: ProcessSelect,
|
|
1051
|
+
props: {
|
|
1052
|
+
value: searchParams.get('id_process') || 'all',
|
|
1053
|
+
onChange: (value: string) => {
|
|
1054
|
+
if (value === 'all') {
|
|
1055
|
+
searchParams.delete('id_process');
|
|
1056
|
+
} else {
|
|
1057
|
+
searchParams.set('id_process', value);
|
|
1058
|
+
}
|
|
1059
|
+
setSearchParams(searchParams);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
],
|
|
1064
|
+
columns: [...]
|
|
1065
|
+
}), [searchParams]);
|
|
1066
|
+
|
|
1067
|
+
return <CrudPage />;
|
|
1068
|
+
}
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
### **Operadores de Filtro Disponíveis**
|
|
1072
|
+
|
|
1073
|
+
O `BaseService` suporta os seguintes operadores em `additionalFilters`:
|
|
1074
|
+
|
|
1075
|
+
| Operador | Descrição | Exemplo |
|
|
1076
|
+
|----------|-----------|---------|
|
|
1077
|
+
| `eq` | Igual a | `{ field: 'status', operator: 'eq', value: 'active' }` |
|
|
1078
|
+
| `neq` | Diferente de | `{ field: 'status', operator: 'neq', value: 'archived' }` |
|
|
1079
|
+
| `gt` | Maior que | `{ field: 'price', operator: 'gt', value: 100 }` |
|
|
1080
|
+
| `gte` | Maior ou igual | `{ field: 'price', operator: 'gte', value: 100 }` |
|
|
1081
|
+
| `lt` | Menor que | `{ field: 'stock', operator: 'lt', value: 10 }` |
|
|
1082
|
+
| `lte` | Menor ou igual | `{ field: 'stock', operator: 'lte', value: 10 }` |
|
|
1083
|
+
| `in` | Em lista | `{ field: 'category', operator: 'in', value: ['A', 'B'] }` |
|
|
1084
|
+
| `contains` | Contém texto | `{ field: 'name', operator: 'contains', value: 'test' }` |
|
|
1085
|
+
| `is_null` | É nulo | `{ field: 'deleted_at', operator: 'is_null' }` |
|
|
1086
|
+
|
|
1087
|
+
### **Exemplo Completo: Filtro de Status e Processo**
|
|
1088
|
+
|
|
1089
|
+
```typescript
|
|
1090
|
+
// Hook com filtros dinâmicos
|
|
1091
|
+
export function useSubprocessesCrud(filters?: {
|
|
1092
|
+
is_actived?: boolean;
|
|
1093
|
+
id_process?: string | 'none';
|
|
1094
|
+
}) {
|
|
1095
|
+
const additionalFilters = useMemo(() => {
|
|
1096
|
+
const filterList: any[] = [];
|
|
1097
|
+
|
|
1098
|
+
if (filters?.is_actived !== undefined) {
|
|
1099
|
+
filterList.push({
|
|
1100
|
+
field: 'is_actived',
|
|
1101
|
+
operator: 'eq',
|
|
1102
|
+
value: filters.is_actived
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
if (filters?.id_process) {
|
|
1107
|
+
if (filters.id_process === 'none') {
|
|
1108
|
+
filterList.push({
|
|
1109
|
+
field: 'id_process',
|
|
1110
|
+
operator: 'is_null'
|
|
1111
|
+
});
|
|
1112
|
+
} else {
|
|
1113
|
+
filterList.push({
|
|
1114
|
+
field: 'id_process',
|
|
1115
|
+
operator: 'eq',
|
|
1116
|
+
value: filters.id_process
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
return filterList;
|
|
1122
|
+
}, [filters?.is_actived, filters?.id_process]);
|
|
1123
|
+
|
|
1124
|
+
return baseUseCrudHook({ additionalFilters });
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
// Na página
|
|
1128
|
+
const filters = useMemo(() => ({
|
|
1129
|
+
is_actived: searchParams.get('is_actived') === 'true',
|
|
1130
|
+
id_process: searchParams.get('id_process') || undefined
|
|
1131
|
+
}), [searchParams]);
|
|
1132
|
+
|
|
1133
|
+
const manager = useSubprocessesCrud(filters);
|
|
1134
|
+
|
|
1135
|
+
// Resultado: Paginação mostra "1-10 de 43 itens" (correto!)
|
|
1136
|
+
// Backend retorna apenas os 43 subprocessos ativos
|
|
1137
|
+
```
|
|
1138
|
+
|
|
1139
|
+
### **⚠️ Filtros Frontend vs Backend**
|
|
1140
|
+
|
|
1141
|
+
**❌ Filtros no Frontend (EVITAR):**
|
|
1142
|
+
```typescript
|
|
1143
|
+
// Problema: Paginação incorreta
|
|
1144
|
+
const filteredEntities = useMemo(() =>
|
|
1145
|
+
manager.entities.filter(e => e.is_actived),
|
|
1146
|
+
[manager.entities]
|
|
1147
|
+
);
|
|
1148
|
+
|
|
1149
|
+
// manager.totalCount = 100 (total do backend)
|
|
1150
|
+
// filteredEntities.length = 43 (após filtro)
|
|
1151
|
+
// Paginação mostra "1-10 de 100 itens" ❌ ERRADO
|
|
1152
|
+
```
|
|
1153
|
+
|
|
1154
|
+
**✅ Filtros no Backend (CORRETO):**
|
|
1155
|
+
```typescript
|
|
1156
|
+
// Backend retorna apenas dados filtrados
|
|
1157
|
+
const manager = useSubprocessesCrud({
|
|
1158
|
+
is_actived: true
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
// manager.totalCount = 43 (correto!)
|
|
1162
|
+
// manager.entities.length = 10 (página 1)
|
|
1163
|
+
// Paginação mostra "1-10 de 43 itens" ✅ CORRETO
|
|
1164
|
+
```
|
|
1165
|
+
|
|
1166
|
+
---
|
|
1167
|
+
|
|
909
1168
|
## 📐 CONTROLE DE LARGURA DAS COLUNAS
|
|
910
1169
|
|
|
911
1170
|
O `forlogic-core` oferece três formas de definir larguras de colunas nas tabelas CRUD:
|
|
@@ -1027,4 +1286,4 @@ src/
|
|
|
1027
1286
|
|
|
1028
1287
|
MIT License - ForLogic © 2025
|
|
1029
1288
|
|
|
1030
|
-
**Última atualização:** 2025-10-
|
|
1289
|
+
**Última atualização:** 2025-10-06
|
package/dist/README.md
CHANGED
|
@@ -906,6 +906,265 @@ fetch(url, { headers: { 'un-alias': 'true' } }); // ✅
|
|
|
906
906
|
|
|
907
907
|
---
|
|
908
908
|
|
|
909
|
+
## 🔍 FILTROS NO BACKEND (Backend Filtering)
|
|
910
|
+
|
|
911
|
+
### **Por que Filtros no Backend?**
|
|
912
|
+
|
|
913
|
+
Filtrar dados no **backend** é a abordagem recomendada porque:
|
|
914
|
+
- ✅ **Paginação correta**: Total de itens reflete os dados filtrados
|
|
915
|
+
- ✅ **Performance**: Menos dados trafegados pela rede
|
|
916
|
+
- ✅ **Escalabilidade**: Funciona com milhares de registros
|
|
917
|
+
- ✅ **Ordenação**: Ordena apenas os dados filtrados
|
|
918
|
+
|
|
919
|
+
### **Como Implementar Filtros no Backend**
|
|
920
|
+
|
|
921
|
+
#### **1️⃣ Filtros Estáticos (via additionalFilters)**
|
|
922
|
+
|
|
923
|
+
Para filtros simples e estáticos, use `additionalFilters` no service:
|
|
924
|
+
|
|
925
|
+
```typescript
|
|
926
|
+
// src/subprocesses/SubprocessService.ts
|
|
927
|
+
import { createSimpleService } from 'forlogic-core';
|
|
928
|
+
import { Subprocess, SubprocessInsert, SubprocessUpdate } from './types';
|
|
929
|
+
|
|
930
|
+
export const {
|
|
931
|
+
service: subprocessService,
|
|
932
|
+
useCrudHook: baseUseCrudHook
|
|
933
|
+
} = createSimpleService<Subprocess, SubprocessInsert, SubprocessUpdate>({
|
|
934
|
+
tableName: 'subprocesses',
|
|
935
|
+
entityName: 'subprocesso',
|
|
936
|
+
schemaName: 'processes',
|
|
937
|
+
searchFields: ['title', 'description'],
|
|
938
|
+
enableQualiexEnrichment: true,
|
|
939
|
+
// Filtros estáticos aplicados sempre
|
|
940
|
+
additionalFilters: [
|
|
941
|
+
{ field: 'is_removed', operator: 'eq', value: false }
|
|
942
|
+
]
|
|
943
|
+
});
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
#### **2️⃣ Filtros Dinâmicos (via Hook Customizado)**
|
|
947
|
+
|
|
948
|
+
Para filtros dinâmicos controlados por URL ou estado, crie um hook wrapper:
|
|
949
|
+
|
|
950
|
+
```typescript
|
|
951
|
+
// src/subprocesses/SubprocessService.ts
|
|
952
|
+
import { useMemo } from 'react';
|
|
953
|
+
import { createSimpleService } from 'forlogic-core';
|
|
954
|
+
|
|
955
|
+
// Service base
|
|
956
|
+
export const {
|
|
957
|
+
service: subprocessService,
|
|
958
|
+
useCrudHook: baseUseCrudHook
|
|
959
|
+
} = createSimpleService<Subprocess, SubprocessInsert, SubprocessUpdate>({
|
|
960
|
+
tableName: 'subprocesses',
|
|
961
|
+
entityName: 'subprocesso',
|
|
962
|
+
schemaName: 'processes',
|
|
963
|
+
searchFields: ['title', 'description']
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
// Hook customizado que aceita filtros dinâmicos
|
|
967
|
+
export function useSubprocessesCrud(filters?: {
|
|
968
|
+
is_actived?: boolean;
|
|
969
|
+
id_process?: string | 'none';
|
|
970
|
+
}) {
|
|
971
|
+
// Transforma filtros em additionalFilters
|
|
972
|
+
const additionalFilters = useMemo(() => {
|
|
973
|
+
const filterList: any[] = [];
|
|
974
|
+
|
|
975
|
+
// Filtro de status (ativo/inativo)
|
|
976
|
+
if (filters?.is_actived !== undefined) {
|
|
977
|
+
filterList.push({
|
|
978
|
+
field: 'is_actived',
|
|
979
|
+
operator: 'eq',
|
|
980
|
+
value: filters.is_actived
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// Filtro de processo (incluindo "sem processo")
|
|
985
|
+
if (filters?.id_process) {
|
|
986
|
+
if (filters.id_process === 'none') {
|
|
987
|
+
// Operador especial para NULL
|
|
988
|
+
filterList.push({
|
|
989
|
+
field: 'id_process',
|
|
990
|
+
operator: 'is_null'
|
|
991
|
+
});
|
|
992
|
+
} else {
|
|
993
|
+
filterList.push({
|
|
994
|
+
field: 'id_process',
|
|
995
|
+
operator: 'eq',
|
|
996
|
+
value: filters.id_process
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
return filterList;
|
|
1002
|
+
}, [filters?.is_actived, filters?.id_process]);
|
|
1003
|
+
|
|
1004
|
+
// Chama hook base com filtros dinâmicos
|
|
1005
|
+
return baseUseCrudHook({ additionalFilters });
|
|
1006
|
+
}
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
#### **3️⃣ Usar na Página com URL Search Params**
|
|
1010
|
+
|
|
1011
|
+
```typescript
|
|
1012
|
+
// src/subprocesses/SubprocessesPage.tsx
|
|
1013
|
+
import { useSearchParams } from 'react-router-dom';
|
|
1014
|
+
import { useSubprocessesCrud } from './SubprocessService';
|
|
1015
|
+
|
|
1016
|
+
export default function SubprocessesPage() {
|
|
1017
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
1018
|
+
|
|
1019
|
+
// Ler filtros da URL
|
|
1020
|
+
const filters = useMemo(() => ({
|
|
1021
|
+
is_actived: searchParams.get('is_actived') === 'true',
|
|
1022
|
+
id_process: searchParams.get('id_process') || undefined
|
|
1023
|
+
}), [searchParams]);
|
|
1024
|
+
|
|
1025
|
+
// Manager com filtros aplicados no backend
|
|
1026
|
+
const manager = useSubprocessesCrud(filters);
|
|
1027
|
+
|
|
1028
|
+
// Config com filtros de UI
|
|
1029
|
+
const config = useMemo(() => generateCrudConfig<Subprocess>({
|
|
1030
|
+
entityName: 'Subprocesso',
|
|
1031
|
+
entityNamePlural: 'Subprocessos',
|
|
1032
|
+
filters: [
|
|
1033
|
+
{
|
|
1034
|
+
type: 'custom',
|
|
1035
|
+
component: StatusSelect,
|
|
1036
|
+
props: {
|
|
1037
|
+
value: searchParams.get('is_actived') || 'all',
|
|
1038
|
+
onChange: (value: string) => {
|
|
1039
|
+
if (value === 'all') {
|
|
1040
|
+
searchParams.delete('is_actived');
|
|
1041
|
+
} else {
|
|
1042
|
+
searchParams.set('is_actived', value === 'active' ? 'true' : 'false');
|
|
1043
|
+
}
|
|
1044
|
+
setSearchParams(searchParams);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
type: 'custom',
|
|
1050
|
+
component: ProcessSelect,
|
|
1051
|
+
props: {
|
|
1052
|
+
value: searchParams.get('id_process') || 'all',
|
|
1053
|
+
onChange: (value: string) => {
|
|
1054
|
+
if (value === 'all') {
|
|
1055
|
+
searchParams.delete('id_process');
|
|
1056
|
+
} else {
|
|
1057
|
+
searchParams.set('id_process', value);
|
|
1058
|
+
}
|
|
1059
|
+
setSearchParams(searchParams);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
],
|
|
1064
|
+
columns: [...]
|
|
1065
|
+
}), [searchParams]);
|
|
1066
|
+
|
|
1067
|
+
return <CrudPage />;
|
|
1068
|
+
}
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
### **Operadores de Filtro Disponíveis**
|
|
1072
|
+
|
|
1073
|
+
O `BaseService` suporta os seguintes operadores em `additionalFilters`:
|
|
1074
|
+
|
|
1075
|
+
| Operador | Descrição | Exemplo |
|
|
1076
|
+
|----------|-----------|---------|
|
|
1077
|
+
| `eq` | Igual a | `{ field: 'status', operator: 'eq', value: 'active' }` |
|
|
1078
|
+
| `neq` | Diferente de | `{ field: 'status', operator: 'neq', value: 'archived' }` |
|
|
1079
|
+
| `gt` | Maior que | `{ field: 'price', operator: 'gt', value: 100 }` |
|
|
1080
|
+
| `gte` | Maior ou igual | `{ field: 'price', operator: 'gte', value: 100 }` |
|
|
1081
|
+
| `lt` | Menor que | `{ field: 'stock', operator: 'lt', value: 10 }` |
|
|
1082
|
+
| `lte` | Menor ou igual | `{ field: 'stock', operator: 'lte', value: 10 }` |
|
|
1083
|
+
| `in` | Em lista | `{ field: 'category', operator: 'in', value: ['A', 'B'] }` |
|
|
1084
|
+
| `contains` | Contém texto | `{ field: 'name', operator: 'contains', value: 'test' }` |
|
|
1085
|
+
| `is_null` | É nulo | `{ field: 'deleted_at', operator: 'is_null' }` |
|
|
1086
|
+
|
|
1087
|
+
### **Exemplo Completo: Filtro de Status e Processo**
|
|
1088
|
+
|
|
1089
|
+
```typescript
|
|
1090
|
+
// Hook com filtros dinâmicos
|
|
1091
|
+
export function useSubprocessesCrud(filters?: {
|
|
1092
|
+
is_actived?: boolean;
|
|
1093
|
+
id_process?: string | 'none';
|
|
1094
|
+
}) {
|
|
1095
|
+
const additionalFilters = useMemo(() => {
|
|
1096
|
+
const filterList: any[] = [];
|
|
1097
|
+
|
|
1098
|
+
if (filters?.is_actived !== undefined) {
|
|
1099
|
+
filterList.push({
|
|
1100
|
+
field: 'is_actived',
|
|
1101
|
+
operator: 'eq',
|
|
1102
|
+
value: filters.is_actived
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
if (filters?.id_process) {
|
|
1107
|
+
if (filters.id_process === 'none') {
|
|
1108
|
+
filterList.push({
|
|
1109
|
+
field: 'id_process',
|
|
1110
|
+
operator: 'is_null'
|
|
1111
|
+
});
|
|
1112
|
+
} else {
|
|
1113
|
+
filterList.push({
|
|
1114
|
+
field: 'id_process',
|
|
1115
|
+
operator: 'eq',
|
|
1116
|
+
value: filters.id_process
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
return filterList;
|
|
1122
|
+
}, [filters?.is_actived, filters?.id_process]);
|
|
1123
|
+
|
|
1124
|
+
return baseUseCrudHook({ additionalFilters });
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
// Na página
|
|
1128
|
+
const filters = useMemo(() => ({
|
|
1129
|
+
is_actived: searchParams.get('is_actived') === 'true',
|
|
1130
|
+
id_process: searchParams.get('id_process') || undefined
|
|
1131
|
+
}), [searchParams]);
|
|
1132
|
+
|
|
1133
|
+
const manager = useSubprocessesCrud(filters);
|
|
1134
|
+
|
|
1135
|
+
// Resultado: Paginação mostra "1-10 de 43 itens" (correto!)
|
|
1136
|
+
// Backend retorna apenas os 43 subprocessos ativos
|
|
1137
|
+
```
|
|
1138
|
+
|
|
1139
|
+
### **⚠️ Filtros Frontend vs Backend**
|
|
1140
|
+
|
|
1141
|
+
**❌ Filtros no Frontend (EVITAR):**
|
|
1142
|
+
```typescript
|
|
1143
|
+
// Problema: Paginação incorreta
|
|
1144
|
+
const filteredEntities = useMemo(() =>
|
|
1145
|
+
manager.entities.filter(e => e.is_actived),
|
|
1146
|
+
[manager.entities]
|
|
1147
|
+
);
|
|
1148
|
+
|
|
1149
|
+
// manager.totalCount = 100 (total do backend)
|
|
1150
|
+
// filteredEntities.length = 43 (após filtro)
|
|
1151
|
+
// Paginação mostra "1-10 de 100 itens" ❌ ERRADO
|
|
1152
|
+
```
|
|
1153
|
+
|
|
1154
|
+
**✅ Filtros no Backend (CORRETO):**
|
|
1155
|
+
```typescript
|
|
1156
|
+
// Backend retorna apenas dados filtrados
|
|
1157
|
+
const manager = useSubprocessesCrud({
|
|
1158
|
+
is_actived: true
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
// manager.totalCount = 43 (correto!)
|
|
1162
|
+
// manager.entities.length = 10 (página 1)
|
|
1163
|
+
// Paginação mostra "1-10 de 43 itens" ✅ CORRETO
|
|
1164
|
+
```
|
|
1165
|
+
|
|
1166
|
+
---
|
|
1167
|
+
|
|
909
1168
|
## 📐 CONTROLE DE LARGURA DAS COLUNAS
|
|
910
1169
|
|
|
911
1170
|
O `forlogic-core` oferece três formas de definir larguras de colunas nas tabelas CRUD:
|
|
@@ -1027,4 +1286,4 @@ src/
|
|
|
1027
1286
|
|
|
1028
1287
|
MIT License - ForLogic © 2025
|
|
1029
1288
|
|
|
1030
|
-
**Última atualização:** 2025-10-
|
|
1289
|
+
**Última atualização:** 2025-10-06
|