forlogic-core 1.8.13 → 1.8.15
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 +341 -35
- package/dist/README.md +341 -35
- 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/dist/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# 🧱 forlogic-core - Guia de Desenvolvimento
|
|
2
2
|
|
|
3
|
-
> **IMPORTANTE**: Este README é genérico para todos os projetos. Para configurações específicas do projeto (como schema padrão), consulte o
|
|
3
|
+
> **IMPORTANTE**: Este README é genérico para todos os projetos. Para configurações específicas do projeto (como schema padrão), consulte o **Custom Knowledge** do projeto nas configurações do Lovable.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## 🤖 REGRAS CRÍTICAS
|
|
8
8
|
|
|
9
|
-
> **📋 Schema Padrão**: Veja no
|
|
9
|
+
> **📋 Schema Padrão**: Veja no **Custom Knowledge** do projeto (Settings → Manage Knowledge) qual é o schema padrão deste projeto específico.
|
|
10
10
|
|
|
11
11
|
### ⚠️ TOP 4 ERROS MAIS COMUNS
|
|
12
12
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// ❌ ERRADO
|
|
17
17
|
.from('table')
|
|
18
18
|
|
|
19
|
-
// ✅ CORRETO (verifique o schema padrão no
|
|
19
|
+
// ✅ CORRETO (verifique o schema padrão no Custom Knowledge)
|
|
20
20
|
.schema('SEU_SCHEMA').from('table')
|
|
21
21
|
```
|
|
22
22
|
|
|
@@ -1923,30 +1923,9 @@ KR1 50% dos indicadores de empreendimento e áreas de negócio atingindo a meta
|
|
|
1923
1923
|
|
|
1924
1924
|
O `forlogic-core` suporta enriquecimento automático de **múltiplos campos de ID de usuário** com dados do Qualiex (nome, email, username).
|
|
1925
1925
|
|
|
1926
|
-
#### **
|
|
1926
|
+
#### **Como Funciona**
|
|
1927
1927
|
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
```typescript
|
|
1931
|
-
import { setQualiexConfig } from 'forlogic-core';
|
|
1932
|
-
|
|
1933
|
-
// Configuração básica (usa localStorage como fallback)
|
|
1934
|
-
setQualiexConfig({
|
|
1935
|
-
enableUserEnrichment: true,
|
|
1936
|
-
enableUsersApi: true,
|
|
1937
|
-
});
|
|
1938
|
-
|
|
1939
|
-
// Configuração avançada (recomendado)
|
|
1940
|
-
setQualiexConfig({
|
|
1941
|
-
enableUserEnrichment: true,
|
|
1942
|
-
enableUsersApi: true,
|
|
1943
|
-
getAccessToken: () => localStorage.getItem('qualiex_access_token'),
|
|
1944
|
-
getCompanyId: () => localStorage.getItem('selectedUnitId'),
|
|
1945
|
-
userNameFieldSuffix: '_name', // default
|
|
1946
|
-
userEmailFieldSuffix: '_email', // default
|
|
1947
|
-
userUsernameFieldSuffix: '_username', // default
|
|
1948
|
-
});
|
|
1949
|
-
```
|
|
1928
|
+
O enriquecimento é **explícito e controlado por serviço**, não requer configuração global. Você decide quando enriquecer ativando `enableQualiexEnrichment` nos serviços.
|
|
1950
1929
|
|
|
1951
1930
|
#### **Uso Básico: Lista de Campos de ID**
|
|
1952
1931
|
|
|
@@ -2059,21 +2038,61 @@ const { service } = createSimpleService({
|
|
|
2059
2038
|
✅ **Type-safe**: TypeScript valida configurações
|
|
2060
2039
|
✅ **Retrocompatível**: Projetos antigos continuam funcionando
|
|
2061
2040
|
|
|
2041
|
+
#### **Hook useQualiexUsers - Controle Explícito**
|
|
2042
|
+
|
|
2043
|
+
Para buscar usuários do Qualiex em componentes, use `enabled: true` explicitamente:
|
|
2044
|
+
|
|
2045
|
+
```typescript
|
|
2046
|
+
import { useQualiexUsers } from 'forlogic-core';
|
|
2047
|
+
|
|
2048
|
+
// ✅ CORRETO: Busca ativa quando enabled=true
|
|
2049
|
+
const { data: users = [] } = useQualiexUsers({ enabled: true });
|
|
2050
|
+
|
|
2051
|
+
// ✅ CORRETO: Busca sob demanda com refetch
|
|
2052
|
+
const { data: users = [], refetch } = useQualiexUsers({ enabled: false });
|
|
2053
|
+
// Depois, quando necessário:
|
|
2054
|
+
await refetch();
|
|
2055
|
+
```
|
|
2056
|
+
|
|
2057
|
+
**Exemplo: Importação de Qualiex**
|
|
2058
|
+
|
|
2059
|
+
```typescript
|
|
2060
|
+
function ImportQualiexButton() {
|
|
2061
|
+
// Não busca automaticamente
|
|
2062
|
+
const { data: qualiexUsers = [], refetch, isLoading } = useQualiexUsers({
|
|
2063
|
+
enabled: false
|
|
2064
|
+
});
|
|
2065
|
+
|
|
2066
|
+
const handleImport = async () => {
|
|
2067
|
+
// Busca apenas quando usuário clica
|
|
2068
|
+
const { data } = await refetch();
|
|
2069
|
+
// Processar data...
|
|
2070
|
+
};
|
|
2071
|
+
|
|
2072
|
+
return <Button onClick={handleImport}>Importar do Qualiex</Button>;
|
|
2073
|
+
}
|
|
2074
|
+
```
|
|
2075
|
+
|
|
2062
2076
|
#### **Boas Práticas**
|
|
2063
2077
|
|
|
2064
|
-
✅ **BOM**:
|
|
2078
|
+
✅ **BOM**: Ativar enrichment apenas onde necessário
|
|
2065
2079
|
|
|
2066
2080
|
```typescript
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
},
|
|
2073
|
-
getCompanyId: () => myAppStore.getSelectedCompanyId(),
|
|
2081
|
+
const { service } = createSimpleService({
|
|
2082
|
+
tableName: 'processes',
|
|
2083
|
+
entityName: 'Processo',
|
|
2084
|
+
enableQualiexEnrichment: true, // Enriquece dados de usuário
|
|
2085
|
+
userIdFields: ['created_by_id', 'owner_id'],
|
|
2074
2086
|
});
|
|
2075
2087
|
```
|
|
2076
2088
|
|
|
2089
|
+
✅ **BOM**: Usar `enabled: true` apenas quando precisar dos dados
|
|
2090
|
+
|
|
2091
|
+
```typescript
|
|
2092
|
+
// Em um componente que mostra lista de usuários
|
|
2093
|
+
const { data: users } = useQualiexUsers({ enabled: true });
|
|
2094
|
+
```
|
|
2095
|
+
|
|
2077
2096
|
❌ **EVITAR**: Múltiplos campos apontando para o mesmo campo de saída
|
|
2078
2097
|
|
|
2079
2098
|
```typescript
|
|
@@ -5459,6 +5478,293 @@ fetch(url, { headers: { 'un-alias': 'true' } }); // ✅
|
|
|
5459
5478
|
|
|
5460
5479
|
---
|
|
5461
5480
|
|
|
5481
|
+
## 🔍 COMPONENTE DE SELEÇÃO: SelectSearch
|
|
5482
|
+
|
|
5483
|
+
### **SelectSearch** - Componente Unificado de Seleção
|
|
5484
|
+
|
|
5485
|
+
Componente versátil que suporta **seleção única** ou **múltipla** com busca inteligente, normalização de acentos e funciona perfeitamente dentro de Dialogs.
|
|
5486
|
+
|
|
5487
|
+
⚠️ **IMPORTANTE**: `MultiSelect` e `EntitySelect` estão obsoletos. Use `SelectSearch` para todos os casos de seleção.
|
|
5488
|
+
|
|
5489
|
+
---
|
|
5490
|
+
|
|
5491
|
+
### **Props Principais**
|
|
5492
|
+
|
|
5493
|
+
```typescript
|
|
5494
|
+
interface SelectSearchProps<T> {
|
|
5495
|
+
options: T[]; // Lista de opções
|
|
5496
|
+
value?: string | string[]; // Valor(es) selecionado(s)
|
|
5497
|
+
onChange: (value: string | string[] | undefined) => void; // Callback de mudança
|
|
5498
|
+
|
|
5499
|
+
// Labels e placeholder
|
|
5500
|
+
placeholder?: string; // Ex: "Selecione..."
|
|
5501
|
+
emptyMessage?: string; // Ex: "Nenhum item encontrado"
|
|
5502
|
+
|
|
5503
|
+
// Configuração de modo
|
|
5504
|
+
multiple?: boolean; // false (padrão) = seleção única, true = múltipla
|
|
5505
|
+
|
|
5506
|
+
// Funções de mapeamento (default: { value: 'id', label: 'name' })
|
|
5507
|
+
getOptionValue?: (option: T) => string;
|
|
5508
|
+
getOptionLabel?: (option: T) => string;
|
|
5509
|
+
|
|
5510
|
+
// Ordenação
|
|
5511
|
+
sortOptions?: boolean; // true (padrão) = ordena por label
|
|
5512
|
+
|
|
5513
|
+
// Uso em Dialog (CRÍTICO!)
|
|
5514
|
+
popoverContainer?: HTMLElement | null; // Necessário para funcionar dentro de Dialog
|
|
5515
|
+
|
|
5516
|
+
// Estados
|
|
5517
|
+
isLoading?: boolean; // Mostra skeleton
|
|
5518
|
+
error?: string; // Mostra mensagem de erro
|
|
5519
|
+
disabled?: boolean; // Desabilita o componente
|
|
5520
|
+
}
|
|
5521
|
+
```
|
|
5522
|
+
|
|
5523
|
+
---
|
|
5524
|
+
|
|
5525
|
+
### **Exemplo 1: Seleção Única**
|
|
5526
|
+
|
|
5527
|
+
```typescript
|
|
5528
|
+
import { SelectSearch } from 'forlogic-core';
|
|
5529
|
+
|
|
5530
|
+
function MyForm() {
|
|
5531
|
+
const [language, setLanguage] = useState<string>();
|
|
5532
|
+
|
|
5533
|
+
return (
|
|
5534
|
+
<SelectSearch
|
|
5535
|
+
options={[
|
|
5536
|
+
{ id: 'pt', name: 'Português' },
|
|
5537
|
+
{ id: 'en', name: 'English' },
|
|
5538
|
+
{ id: 'es', name: 'Español' }
|
|
5539
|
+
]}
|
|
5540
|
+
value={language}
|
|
5541
|
+
onChange={setLanguage}
|
|
5542
|
+
placeholder="Selecione um idioma"
|
|
5543
|
+
getOptionValue={(opt) => opt.id}
|
|
5544
|
+
getOptionLabel={(opt) => opt.name}
|
|
5545
|
+
/>
|
|
5546
|
+
);
|
|
5547
|
+
}
|
|
5548
|
+
```
|
|
5549
|
+
|
|
5550
|
+
---
|
|
5551
|
+
|
|
5552
|
+
### **Exemplo 2: Seleção Múltipla**
|
|
5553
|
+
|
|
5554
|
+
```typescript
|
|
5555
|
+
import { SelectSearch } from 'forlogic-core';
|
|
5556
|
+
|
|
5557
|
+
function MyForm() {
|
|
5558
|
+
const [tags, setTags] = useState<string[]>([]);
|
|
5559
|
+
|
|
5560
|
+
return (
|
|
5561
|
+
<SelectSearch
|
|
5562
|
+
multiple
|
|
5563
|
+
options={[
|
|
5564
|
+
{ id: '1', name: 'React' },
|
|
5565
|
+
{ id: '2', name: 'TypeScript' },
|
|
5566
|
+
{ id: '3', name: 'Tailwind' }
|
|
5567
|
+
]}
|
|
5568
|
+
value={tags}
|
|
5569
|
+
onChange={(value) => setTags(value as string[])}
|
|
5570
|
+
placeholder="Selecione tecnologias"
|
|
5571
|
+
getOptionValue={(opt) => opt.id}
|
|
5572
|
+
getOptionLabel={(opt) => opt.name}
|
|
5573
|
+
/>
|
|
5574
|
+
);
|
|
5575
|
+
}
|
|
5576
|
+
```
|
|
5577
|
+
|
|
5578
|
+
---
|
|
5579
|
+
|
|
5580
|
+
### **Exemplo 3: Dentro de Dialog** ⚠️ **CRÍTICO**
|
|
5581
|
+
|
|
5582
|
+
Para funcionar corretamente dentro de um Dialog, você **DEVE** passar o `popoverContainer`:
|
|
5583
|
+
|
|
5584
|
+
```typescript
|
|
5585
|
+
import { SelectSearch, Dialog, DialogContent } from 'forlogic-core';
|
|
5586
|
+
|
|
5587
|
+
function MyDialog() {
|
|
5588
|
+
const [open, setOpen] = useState(false);
|
|
5589
|
+
const [category, setCategory] = useState<string>();
|
|
5590
|
+
const dialogContentRef = useRef<HTMLDivElement>(null);
|
|
5591
|
+
|
|
5592
|
+
return (
|
|
5593
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
5594
|
+
<DialogContent
|
|
5595
|
+
ref={dialogContentRef}
|
|
5596
|
+
className="overflow-visible" // ⚠️ IMPORTANTE: overflow-visible no DialogContent
|
|
5597
|
+
>
|
|
5598
|
+
<div className="space-y-4 overflow-auto max-h-[60vh]">
|
|
5599
|
+
{/* ⚠️ CRÍTICO: Passar popoverContainer */}
|
|
5600
|
+
<SelectSearch
|
|
5601
|
+
options={categories}
|
|
5602
|
+
value={category}
|
|
5603
|
+
onChange={setCategory}
|
|
5604
|
+
placeholder="Selecione categoria"
|
|
5605
|
+
popoverContainer={dialogContentRef.current}
|
|
5606
|
+
/>
|
|
5607
|
+
</div>
|
|
5608
|
+
</DialogContent>
|
|
5609
|
+
</Dialog>
|
|
5610
|
+
);
|
|
5611
|
+
}
|
|
5612
|
+
```
|
|
5613
|
+
|
|
5614
|
+
---
|
|
5615
|
+
|
|
5616
|
+
### **⚠️ IMPORTANTE: Uso em Dialog**
|
|
5617
|
+
|
|
5618
|
+
**Configuração Correta:**
|
|
5619
|
+
```tsx
|
|
5620
|
+
// 1. Ref no DialogContent
|
|
5621
|
+
const dialogContentRef = useRef<HTMLDivElement>(null);
|
|
5622
|
+
|
|
5623
|
+
// 2. overflow-visible no DialogContent
|
|
5624
|
+
<DialogContent ref={dialogContentRef} className="overflow-visible">
|
|
5625
|
+
|
|
5626
|
+
// 3. Div interna com overflow-auto
|
|
5627
|
+
<div className="overflow-auto max-h-[60vh]">
|
|
5628
|
+
|
|
5629
|
+
// 4. Passar popoverContainer
|
|
5630
|
+
<SelectSearch
|
|
5631
|
+
{...props}
|
|
5632
|
+
popoverContainer={dialogContentRef.current}
|
|
5633
|
+
/>
|
|
5634
|
+
</div>
|
|
5635
|
+
</DialogContent>
|
|
5636
|
+
```
|
|
5637
|
+
|
|
5638
|
+
**❌ Configurações Incorretas Comuns:**
|
|
5639
|
+
|
|
5640
|
+
```tsx
|
|
5641
|
+
// ❌ ERRADO: Sem popoverContainer
|
|
5642
|
+
<SelectSearch options={data} value={value} onChange={onChange} />
|
|
5643
|
+
|
|
5644
|
+
// ❌ ERRADO: overflow-hidden no DialogContent
|
|
5645
|
+
<DialogContent className="overflow-hidden">
|
|
5646
|
+
|
|
5647
|
+
// ❌ ERRADO: Sem div interna com scroll
|
|
5648
|
+
<DialogContent className="overflow-visible">
|
|
5649
|
+
<SelectSearch {...props} />
|
|
5650
|
+
</DialogContent>
|
|
5651
|
+
```
|
|
5652
|
+
|
|
5653
|
+
---
|
|
5654
|
+
|
|
5655
|
+
### **Características Avançadas**
|
|
5656
|
+
|
|
5657
|
+
#### **Busca Inteligente**
|
|
5658
|
+
- **Case-insensitive**: `react` encontra `React`
|
|
5659
|
+
- **Accent-insensitive**: `frances` encontra `Français`
|
|
5660
|
+
- **Busca em qualquer parte**: `script` encontra `TypeScript`
|
|
5661
|
+
|
|
5662
|
+
#### **Badges Interativos (modo múltiplo)**
|
|
5663
|
+
- Exibe badges com `×` para remover itens
|
|
5664
|
+
- Limite de 3 badges exibidos, com `+N` para excedentes
|
|
5665
|
+
- Click em `+N` expande para mostrar todos
|
|
5666
|
+
|
|
5667
|
+
#### **Estados**
|
|
5668
|
+
```typescript
|
|
5669
|
+
// Loading
|
|
5670
|
+
<SelectSearch isLoading options={[]} />
|
|
5671
|
+
|
|
5672
|
+
// Erro
|
|
5673
|
+
<SelectSearch error="Falha ao carregar opções" options={[]} />
|
|
5674
|
+
|
|
5675
|
+
// Desabilitado
|
|
5676
|
+
<SelectSearch disabled options={data} />
|
|
5677
|
+
```
|
|
5678
|
+
|
|
5679
|
+
---
|
|
5680
|
+
|
|
5681
|
+
### **Uso em BaseForm**
|
|
5682
|
+
|
|
5683
|
+
```typescript
|
|
5684
|
+
import { BaseForm, SelectSearch } from 'forlogic-core';
|
|
5685
|
+
|
|
5686
|
+
const fields = [
|
|
5687
|
+
{
|
|
5688
|
+
name: 'language',
|
|
5689
|
+
label: 'Idioma',
|
|
5690
|
+
type: 'custom' as const,
|
|
5691
|
+
component: ({ field }: any) => (
|
|
5692
|
+
<SelectSearch
|
|
5693
|
+
options={languages}
|
|
5694
|
+
value={field.value}
|
|
5695
|
+
onChange={field.onChange}
|
|
5696
|
+
placeholder="Selecione um idioma"
|
|
5697
|
+
/>
|
|
5698
|
+
),
|
|
5699
|
+
validation: z.string().min(1, 'Idioma obrigatório')
|
|
5700
|
+
},
|
|
5701
|
+
{
|
|
5702
|
+
name: 'tags',
|
|
5703
|
+
label: 'Tags',
|
|
5704
|
+
type: 'custom' as const,
|
|
5705
|
+
component: ({ field }: any) => (
|
|
5706
|
+
<SelectSearch
|
|
5707
|
+
multiple
|
|
5708
|
+
options={allTags}
|
|
5709
|
+
value={field.value}
|
|
5710
|
+
onChange={field.onChange}
|
|
5711
|
+
placeholder="Selecione tags"
|
|
5712
|
+
/>
|
|
5713
|
+
),
|
|
5714
|
+
validation: z.array(z.string()).min(1, 'Selecione ao menos uma tag')
|
|
5715
|
+
}
|
|
5716
|
+
];
|
|
5717
|
+
```
|
|
5718
|
+
|
|
5719
|
+
---
|
|
5720
|
+
|
|
5721
|
+
### **Migração de MultiSelect e EntitySelect (DEPRECATED)**
|
|
5722
|
+
|
|
5723
|
+
⚠️ **Estes componentes estão obsoletos. Use `SelectSearch` para todos os casos de seleção.**
|
|
5724
|
+
|
|
5725
|
+
**Migração de MultiSelect:**
|
|
5726
|
+
|
|
5727
|
+
```typescript
|
|
5728
|
+
// ❌ ANTIGO: MultiSelect
|
|
5729
|
+
<MultiSelect
|
|
5730
|
+
options={data}
|
|
5731
|
+
value={selected}
|
|
5732
|
+
onChange={setSelected}
|
|
5733
|
+
/>
|
|
5734
|
+
|
|
5735
|
+
// ✅ NOVO: SelectSearch
|
|
5736
|
+
<SelectSearch
|
|
5737
|
+
multiple
|
|
5738
|
+
options={data}
|
|
5739
|
+
value={selected}
|
|
5740
|
+
onChange={setSelected}
|
|
5741
|
+
/>
|
|
5742
|
+
```
|
|
5743
|
+
|
|
5744
|
+
**Migração de EntitySelect:**
|
|
5745
|
+
|
|
5746
|
+
```typescript
|
|
5747
|
+
// ❌ ANTIGO: EntitySelect
|
|
5748
|
+
<EntitySelect
|
|
5749
|
+
items={data}
|
|
5750
|
+
value={selected}
|
|
5751
|
+
onChange={setSelected}
|
|
5752
|
+
getItemValue={(item) => item.id}
|
|
5753
|
+
getItemLabel={(item) => item.name}
|
|
5754
|
+
/>
|
|
5755
|
+
|
|
5756
|
+
// ✅ NOVO: SelectSearch
|
|
5757
|
+
<SelectSearch
|
|
5758
|
+
options={data}
|
|
5759
|
+
value={selected}
|
|
5760
|
+
onChange={setSelected}
|
|
5761
|
+
getOptionValue={(item) => item.id}
|
|
5762
|
+
getOptionLabel={(item) => item.name}
|
|
5763
|
+
/>
|
|
5764
|
+
```
|
|
5765
|
+
|
|
5766
|
+
---
|
|
5767
|
+
|
|
5462
5768
|
## 🔍 FILTROS NO BACKEND (Backend Filtering)
|
|
5463
5769
|
|
|
5464
5770
|
### **Por que Filtros no Backend?**
|
|
@@ -6051,4 +6357,4 @@ src/
|
|
|
6051
6357
|
|
|
6052
6358
|
## 📝 Licença
|
|
6053
6359
|
|
|
6054
|
-
MIT License - ForLogic © 2025
|
|
6360
|
+
MIT License - ForLogic © 2025
|