forlogic-core 1.6.13 → 1.7.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 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[CrudForm<br/>Formulário]
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 ContentEntity + VisualEntity + ...
502
- □ types.ts - CreateExamplePayload com alias: string
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 - Importar { alias } de useAuth()
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 (useState + useMemo)
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
- // ⚠️ CRÍTICO: Preservar ID no update
724
- const handleUpdate = async (item: Process) => {
725
- await handleSave({
726
- ...item,
727
- id: item.id // ⚠️ OBRIGATÓRIO para update
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
- * ✅ Composição de Interfaces:
971
- * - ContentEntity: fornece title, description
972
- * - VisualEntity: fornece color, icon_name
973
- * - UserRelatedEntity: fornece id_user, responsible_name (enriquecido via Qualiex)
974
- * - ActivableEntity: fornece is_actived (campo de status on/off)
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
- ContentEntity,
987
- VisualEntity,
988
- UserRelatedEntity,
989
- ActivableEntity,
990
- FormEntity {}
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
- * - Sempre incluir `alias: string` (obrigatório para RLS)
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 interface CreateExamplePayload {
1001
- title: string; // ✅ Obrigatório
1002
- description?: string | null; // ❌ Opcional
1003
- alias: string; // ✅ OBRIGATÓRIO para RLS
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
- * - Estende Partial<CreateExamplePayload> (todos os campos opcionais)
1017
- * - MAS title é obrigatório (override)
1290
+ * - Todos os campos são opcionais (Partial)
1018
1291
  */
1019
- export interface UpdateExamplePayload extends Partial<CreateExamplePayload> {
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
- * - 'simple-qualiex-user-field' - Seletor de usuário Qualiex
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: 'simple-qualiex-user-field' as const, // 👤 Campo especial da lib
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 (Ativo/Inativo/Todos)**
1503
+ ### **Pattern 1: Filtro de Status (Backend - Recomendado)**
1235
1504
 
1236
1505
  ```typescript
1237
- // ✅ PADRÃO COMPLETO: Filtro + Estado Derivado + Manager Customizado
1506
+ // ✅ PADRÃO BACKEND: Filtro aplicado na query SQL (melhor performance)
1238
1507
 
1239
1508
  // 1) Estado do filtro
1240
- const [statusFilter, setStatusFilter] = useState<'active' | 'inactive' | 'all'>('active');
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
- // 3) Manager customizado
1251
- const filteredManager = useMemo(() => ({
1252
- ...manager,
1253
- entities: filteredEntities
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
- // 4) Componente do filtro
1516
+ // 3) Componente do filtro
1257
1517
  const StatusFilter = () => (
1258
1518
  <EntitySelect
1259
- value={statusFilter}
1260
- onChange={(value) => setStatusFilter(value as 'active' | 'inactive' | 'all')}
1519
+ value={String(statusFilter)}
1520
+ onChange={(v) => setStatusFilter(v === 'all' ? 'all' : v === 'true')}
1261
1521
  items={[
1262
- { id: 'active', name: 'Ativo' },
1263
- { id: 'inactive', name: 'Inativo' },
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
- // 5) Usar em config.filters
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
- // 6) Passar filteredManager para createCrudPage
1541
+ // 5) Passar manager original (filtro já aplicado)
1281
1542
  const CrudPage = createCrudPage({
1282
- manager: filteredManager, // ← Não manager original!
1543
+ manager, // ← Manager 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 `alias` no CREATE**
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: Payload sem alias
1474
- const handleSave = createSimpleSaveHandler(
1475
- manager,
1476
- (data) => ({
1477
- title: data.title,
1478
- email: data.email
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
- const handleSave = createSimpleSaveHandler(
1487
- manager,
1488
- (data) => ({
1489
- title: data.title,
1490
- email: data.email,
1491
- alias: currentAlias // ← Correto!
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: 'single-responsible-select' as const,
1925
+ type: 'user-select' as const,
1926
+ mode: 'single', // ou 'multiple'
1654
1927
  required: true
1655
1928
  }
1656
1929
  ```