forlogic-core 1.8.2 → 1.8.4
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 +233 -13
- package/dist/README.md +233 -13
- 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/assets/index-2WBeKMgq.css +0 -1
- package/dist/assets/index-Dg0JuUel.js +0 -696
- package/dist/index.html +0 -19
package/README.md
CHANGED
|
@@ -223,7 +223,7 @@ import { Button } from 'forlogic-core'
|
|
|
223
223
|
// Formulários
|
|
224
224
|
Button, Input, Textarea, Label, Select, SelectContent,
|
|
225
225
|
SelectItem, SelectTrigger, SelectValue, Checkbox, RadioGroup,
|
|
226
|
-
RadioGroupItem, Switch, CreatableCombobox
|
|
226
|
+
RadioGroupItem, Switch, CreatableCombobox, EntitySelect, MultiSelect
|
|
227
227
|
|
|
228
228
|
// Layout
|
|
229
229
|
Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle
|
|
@@ -477,6 +477,183 @@ export function DepartmentSelect(props: DepartmentSelectProps) {
|
|
|
477
477
|
|
|
478
478
|
---
|
|
479
479
|
|
|
480
|
+
#### MultiSelect - Seleção Múltipla Genérica
|
|
481
|
+
|
|
482
|
+
**Quando usar?**
|
|
483
|
+
|
|
484
|
+
Use o `MultiSelect` para permitir seleção múltipla de itens com busca inteligente e exibição em badges:
|
|
485
|
+
|
|
486
|
+
- ✅ Seleção de múltiplos processos, colaboradores, treinamentos, etc
|
|
487
|
+
- ✅ Busca com normalização (remove acentos, case-insensitive)
|
|
488
|
+
- ✅ Badges interativos para remover seleções
|
|
489
|
+
- ✅ Estados de loading e erro inclusos
|
|
490
|
+
- ✅ Limite de badges exibidos com contador (+N)
|
|
491
|
+
|
|
492
|
+
**Uso Básico:**
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
import { MultiSelect } from 'forlogic-core';
|
|
496
|
+
import { useTrainings } from './trainingService';
|
|
497
|
+
|
|
498
|
+
function MyForm() {
|
|
499
|
+
const { data: trainings = [], isLoading, error } = useTrainings();
|
|
500
|
+
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
|
501
|
+
|
|
502
|
+
return (
|
|
503
|
+
<MultiSelect
|
|
504
|
+
value={selectedIds}
|
|
505
|
+
onChange={setSelectedIds}
|
|
506
|
+
options={trainings}
|
|
507
|
+
isLoading={isLoading}
|
|
508
|
+
error={error}
|
|
509
|
+
getOptionValue={(training) => training.id}
|
|
510
|
+
getOptionLabel={(training) => training.title}
|
|
511
|
+
placeholder="Selecione treinamentos..."
|
|
512
|
+
label="Treinamentos"
|
|
513
|
+
icon={GraduationCap}
|
|
514
|
+
/>
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**Props do MultiSelect:**
|
|
520
|
+
|
|
521
|
+
| Prop | Tipo | Obrigatório | Descrição |
|
|
522
|
+
| ---- | ---- | ----------- | --------- |
|
|
523
|
+
| `options` | `T[]` | ✅ | Array de itens disponíveis |
|
|
524
|
+
| `value` | `string[]` | ❌ | Array de IDs selecionados |
|
|
525
|
+
| `onChange` | `(value: string[]) => void` | ❌ | Callback quando seleção muda |
|
|
526
|
+
| `getOptionValue` | `(option: T) => string` | ❌ | Extrai valor único (default: `opt.value`) |
|
|
527
|
+
| `getOptionLabel` | `(option: T) => string` | ❌ | Extrai label exibido (default: `opt.label`) |
|
|
528
|
+
| `placeholder` | `string` | ❌ | Placeholder quando nada selecionado |
|
|
529
|
+
| `label` | `string` | ❌ | Label do campo |
|
|
530
|
+
| `icon` | `LucideIcon` | ❌ | Ícone exibido no trigger |
|
|
531
|
+
| `emptyMessage` | `string` | ❌ | Mensagem quando não há itens |
|
|
532
|
+
| `searchPlaceholder` | `string` | ❌ | Placeholder da busca |
|
|
533
|
+
| `disabled` | `boolean` | ❌ | Desabilita o componente |
|
|
534
|
+
| `required` | `boolean` | ❌ | Adiciona asterisco vermelho ao label |
|
|
535
|
+
| `isLoading` | `boolean` | ❌ | Exibe skeleton durante carregamento |
|
|
536
|
+
| `error` | `string \| boolean` | ❌ | Mensagem de erro |
|
|
537
|
+
| `className` | `string` | ❌ | Classes CSS adicionais |
|
|
538
|
+
| `sortOptions` | `boolean` | ❌ | Ordena alfabeticamente (default: true) |
|
|
539
|
+
| `maxDisplayedBadges` | `number` | ❌ | Limite de badges visíveis |
|
|
540
|
+
| `onOpen` | `() => void` | ❌ | Callback ao abrir popover |
|
|
541
|
+
| `onClose` | `() => void` | ❌ | Callback ao fechar popover |
|
|
542
|
+
|
|
543
|
+
**Exemplo com Objetos Customizados:**
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
interface User {
|
|
547
|
+
id: string;
|
|
548
|
+
name: string;
|
|
549
|
+
email: string;
|
|
550
|
+
department: string;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
<MultiSelect<User>
|
|
554
|
+
options={users}
|
|
555
|
+
value={selectedUserIds}
|
|
556
|
+
onChange={setSelectedUserIds}
|
|
557
|
+
getOptionValue={(user) => user.id}
|
|
558
|
+
getOptionLabel={(user) => `${user.name} - ${user.department}`}
|
|
559
|
+
placeholder="Selecione colaboradores..."
|
|
560
|
+
label="Colaboradores"
|
|
561
|
+
icon={Users}
|
|
562
|
+
required
|
|
563
|
+
/>
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**Limite de Badges Exibidos:**
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
<MultiSelect
|
|
570
|
+
options={items}
|
|
571
|
+
value={selected}
|
|
572
|
+
onChange={setSelected}
|
|
573
|
+
maxDisplayedBadges={3} // Exibe: [Badge 1] [Badge 2] [Badge 3] [+5]
|
|
574
|
+
placeholder="Selecione itens..."
|
|
575
|
+
/>
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
**Integração com React Hook Form:**
|
|
579
|
+
|
|
580
|
+
```typescript
|
|
581
|
+
import { useForm } from 'react-hook-form';
|
|
582
|
+
import { MultiSelect } from 'forlogic-core';
|
|
583
|
+
|
|
584
|
+
function MyForm() {
|
|
585
|
+
const { control } = useForm();
|
|
586
|
+
const { data: categories = [] } = useCategories();
|
|
587
|
+
|
|
588
|
+
return (
|
|
589
|
+
<FormField
|
|
590
|
+
control={control}
|
|
591
|
+
name="category_ids"
|
|
592
|
+
render={({ field }) => (
|
|
593
|
+
<FormItem>
|
|
594
|
+
<FormControl>
|
|
595
|
+
<MultiSelect
|
|
596
|
+
value={field.value}
|
|
597
|
+
onChange={field.onChange}
|
|
598
|
+
options={categories}
|
|
599
|
+
getOptionValue={(c) => c.id}
|
|
600
|
+
getOptionLabel={(c) => c.name}
|
|
601
|
+
placeholder="Selecione categorias..."
|
|
602
|
+
label="Categorias"
|
|
603
|
+
/>
|
|
604
|
+
</FormControl>
|
|
605
|
+
<FormMessage />
|
|
606
|
+
</FormItem>
|
|
607
|
+
)}
|
|
608
|
+
/>
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
**Usando em Formulários CRUD:**
|
|
614
|
+
|
|
615
|
+
Para usar o `MultiSelect` em formulários CRUD gerados automaticamente, crie um wrapper e use `type: 'custom'`:
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
// 1. Criar wrapper do MultiSelect
|
|
619
|
+
// src/components/TrainingMultiSelect.tsx
|
|
620
|
+
import { MultiSelect } from 'forlogic-core';
|
|
621
|
+
import { useTrainings } from '@/trainings/trainingService';
|
|
622
|
+
import { GraduationCap } from 'lucide-react';
|
|
623
|
+
|
|
624
|
+
export function TrainingMultiSelect({ value, onChange, disabled, error }: any) {
|
|
625
|
+
const { data: trainings = [], isLoading } = useTrainings();
|
|
626
|
+
|
|
627
|
+
return (
|
|
628
|
+
<MultiSelect
|
|
629
|
+
value={value || []}
|
|
630
|
+
onChange={onChange}
|
|
631
|
+
options={trainings}
|
|
632
|
+
isLoading={isLoading}
|
|
633
|
+
error={error}
|
|
634
|
+
getOptionValue={(t) => t.id}
|
|
635
|
+
getOptionLabel={(t) => t.title}
|
|
636
|
+
disabled={disabled}
|
|
637
|
+
placeholder="Selecione treinamentos..."
|
|
638
|
+
icon={GraduationCap}
|
|
639
|
+
/>
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// 2. Usar na configuração do formulário com type: 'custom'
|
|
644
|
+
import { TrainingMultiSelect } from './components/TrainingMultiSelect';
|
|
645
|
+
|
|
646
|
+
{
|
|
647
|
+
name: 'training_ids',
|
|
648
|
+
label: 'Treinamentos',
|
|
649
|
+
type: 'custom' as const,
|
|
650
|
+
component: TrainingMultiSelect,
|
|
651
|
+
required: true
|
|
652
|
+
}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
---
|
|
656
|
+
|
|
480
657
|
### 🎨 Campos Customizados no BaseForm
|
|
481
658
|
|
|
482
659
|
O `BaseForm` suporta campos totalmente customizados através do tipo `'custom'`, permitindo usar **qualquer componente React** como campo de formulário.
|
|
@@ -1010,7 +1187,7 @@ function TagsPage() {
|
|
|
1010
1187
|
#### Uso Standalone (Fora do BaseForm)
|
|
1011
1188
|
|
|
1012
1189
|
```typescript
|
|
1013
|
-
import { CreatableCombobox } from 'forlogic-core
|
|
1190
|
+
import { CreatableCombobox } from 'forlogic-core';
|
|
1014
1191
|
import { useState } from 'react';
|
|
1015
1192
|
|
|
1016
1193
|
function MyComponent() {
|
|
@@ -3825,11 +4002,42 @@ const config = useMemo((): CrudPageConfig<Process> => ({
|
|
|
3825
4002
|
|
|
3826
4003
|
Quando `enableBulkActions: true`, as seguintes ações ficam disponíveis automaticamente:
|
|
3827
4004
|
|
|
3828
|
-
- ✅ **Deletar em lote**: Deleta múltiplos itens selecionados (usa a mesma função do delete individual)
|
|
3829
4005
|
- ✅ **Checkbox "Selecionar Todos"**: No header da tabela para selecionar/deselecionar todos os itens da página
|
|
3830
4006
|
- ✅ **Indicador Visual**: Linhas/cards selecionados têm background destacado
|
|
3831
4007
|
- ✅ **Barra de Ações**: Aparece no topo quando há itens selecionados
|
|
3832
4008
|
|
|
4009
|
+
**⚠️ IMPORTANTE - Botão "Remover" Padrão:**
|
|
4010
|
+
|
|
4011
|
+
- ❌ **Se você definir `bulkActions` customizadas**, o botão "Remover" padrão **NÃO** será adicionado automaticamente
|
|
4012
|
+
- ✅ **Se você NÃO definir `bulkActions`**, um botão "Remover" padrão será adicionado automaticamente
|
|
4013
|
+
|
|
4014
|
+
```typescript
|
|
4015
|
+
// ❌ Duplicação: Botão padrão + custom "Remover"
|
|
4016
|
+
bulkActions: [
|
|
4017
|
+
{
|
|
4018
|
+
label: 'Remover', // ← Vai aparecer 2x (padrão + este)
|
|
4019
|
+
icon: Trash2,
|
|
4020
|
+
variant: 'destructive',
|
|
4021
|
+
action: async (items) => { /* ... */ }
|
|
4022
|
+
}
|
|
4023
|
+
]
|
|
4024
|
+
|
|
4025
|
+
// ✅ Correto: Apenas o padrão
|
|
4026
|
+
enableBulkActions: true,
|
|
4027
|
+
// Não definir bulkActions → Usa botão "Remover" padrão
|
|
4028
|
+
|
|
4029
|
+
// ✅ Correto: Apenas ações customizadas (sem padrão)
|
|
4030
|
+
bulkActions: [
|
|
4031
|
+
{
|
|
4032
|
+
label: 'Remover',
|
|
4033
|
+
icon: Trash2,
|
|
4034
|
+
variant: 'destructive',
|
|
4035
|
+
action: async (items) => { /* ... */ }
|
|
4036
|
+
}
|
|
4037
|
+
]
|
|
4038
|
+
```
|
|
4039
|
+
|
|
4040
|
+
|
|
3833
4041
|
### **Ações Customizadas**
|
|
3834
4042
|
|
|
3835
4043
|
Você pode adicionar ações customizadas além do delete padrão:
|
|
@@ -5416,23 +5624,35 @@ await notifyLeadershipChange(
|
|
|
5416
5624
|
|
|
5417
5625
|
### Imports Essenciais
|
|
5418
5626
|
|
|
5627
|
+
> **⚠️ IMPORTANTE**: A partir da versão 1.8.4+, todos os componentes devem ser importados diretamente de `forlogic-core`. Os exports modulares (`/ui`, `/modular`, `/crud`) foram removidos para simplificar o uso da biblioteca.
|
|
5628
|
+
|
|
5419
5629
|
```typescript
|
|
5420
|
-
//
|
|
5630
|
+
// ✅ CORRETO (v1.8.4+)
|
|
5421
5631
|
import {
|
|
5632
|
+
// CRUD
|
|
5422
5633
|
createSimpleService,
|
|
5423
5634
|
createCrudPage,
|
|
5424
5635
|
generateCrudConfig,
|
|
5425
|
-
createSimpleSaveHandler
|
|
5636
|
+
createSimpleSaveHandler,
|
|
5637
|
+
|
|
5638
|
+
// UI
|
|
5639
|
+
Button,
|
|
5640
|
+
Input,
|
|
5641
|
+
Card,
|
|
5642
|
+
toast,
|
|
5643
|
+
|
|
5644
|
+
// Auth
|
|
5645
|
+
useAuth,
|
|
5646
|
+
ProtectedRoute,
|
|
5647
|
+
|
|
5648
|
+
// Qualiex
|
|
5649
|
+
QualiexUserField,
|
|
5650
|
+
useQualiexUsers
|
|
5426
5651
|
} from 'forlogic-core';
|
|
5427
5652
|
|
|
5428
|
-
//
|
|
5429
|
-
import { Button, Input
|
|
5430
|
-
|
|
5431
|
-
// Auth
|
|
5432
|
-
import { useAuth, ProtectedRoute } from 'forlogic-core';
|
|
5433
|
-
|
|
5434
|
-
// Qualiex
|
|
5435
|
-
import { QualiexUserField, useQualiexUsers } from 'forlogic-core';
|
|
5653
|
+
// ❌ DEPRECATED (removido em v1.8.4+)
|
|
5654
|
+
// import { Button, Input } from 'forlogic-core/ui';
|
|
5655
|
+
// import { createCrudPage } from 'forlogic-core/crud';
|
|
5436
5656
|
```
|
|
5437
5657
|
|
|
5438
5658
|
### Estrutura de Arquivos
|
package/dist/README.md
CHANGED
|
@@ -223,7 +223,7 @@ import { Button } from 'forlogic-core'
|
|
|
223
223
|
// Formulários
|
|
224
224
|
Button, Input, Textarea, Label, Select, SelectContent,
|
|
225
225
|
SelectItem, SelectTrigger, SelectValue, Checkbox, RadioGroup,
|
|
226
|
-
RadioGroupItem, Switch, CreatableCombobox
|
|
226
|
+
RadioGroupItem, Switch, CreatableCombobox, EntitySelect, MultiSelect
|
|
227
227
|
|
|
228
228
|
// Layout
|
|
229
229
|
Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle
|
|
@@ -477,6 +477,183 @@ export function DepartmentSelect(props: DepartmentSelectProps) {
|
|
|
477
477
|
|
|
478
478
|
---
|
|
479
479
|
|
|
480
|
+
#### MultiSelect - Seleção Múltipla Genérica
|
|
481
|
+
|
|
482
|
+
**Quando usar?**
|
|
483
|
+
|
|
484
|
+
Use o `MultiSelect` para permitir seleção múltipla de itens com busca inteligente e exibição em badges:
|
|
485
|
+
|
|
486
|
+
- ✅ Seleção de múltiplos processos, colaboradores, treinamentos, etc
|
|
487
|
+
- ✅ Busca com normalização (remove acentos, case-insensitive)
|
|
488
|
+
- ✅ Badges interativos para remover seleções
|
|
489
|
+
- ✅ Estados de loading e erro inclusos
|
|
490
|
+
- ✅ Limite de badges exibidos com contador (+N)
|
|
491
|
+
|
|
492
|
+
**Uso Básico:**
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
import { MultiSelect } from 'forlogic-core';
|
|
496
|
+
import { useTrainings } from './trainingService';
|
|
497
|
+
|
|
498
|
+
function MyForm() {
|
|
499
|
+
const { data: trainings = [], isLoading, error } = useTrainings();
|
|
500
|
+
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
|
501
|
+
|
|
502
|
+
return (
|
|
503
|
+
<MultiSelect
|
|
504
|
+
value={selectedIds}
|
|
505
|
+
onChange={setSelectedIds}
|
|
506
|
+
options={trainings}
|
|
507
|
+
isLoading={isLoading}
|
|
508
|
+
error={error}
|
|
509
|
+
getOptionValue={(training) => training.id}
|
|
510
|
+
getOptionLabel={(training) => training.title}
|
|
511
|
+
placeholder="Selecione treinamentos..."
|
|
512
|
+
label="Treinamentos"
|
|
513
|
+
icon={GraduationCap}
|
|
514
|
+
/>
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**Props do MultiSelect:**
|
|
520
|
+
|
|
521
|
+
| Prop | Tipo | Obrigatório | Descrição |
|
|
522
|
+
| ---- | ---- | ----------- | --------- |
|
|
523
|
+
| `options` | `T[]` | ✅ | Array de itens disponíveis |
|
|
524
|
+
| `value` | `string[]` | ❌ | Array de IDs selecionados |
|
|
525
|
+
| `onChange` | `(value: string[]) => void` | ❌ | Callback quando seleção muda |
|
|
526
|
+
| `getOptionValue` | `(option: T) => string` | ❌ | Extrai valor único (default: `opt.value`) |
|
|
527
|
+
| `getOptionLabel` | `(option: T) => string` | ❌ | Extrai label exibido (default: `opt.label`) |
|
|
528
|
+
| `placeholder` | `string` | ❌ | Placeholder quando nada selecionado |
|
|
529
|
+
| `label` | `string` | ❌ | Label do campo |
|
|
530
|
+
| `icon` | `LucideIcon` | ❌ | Ícone exibido no trigger |
|
|
531
|
+
| `emptyMessage` | `string` | ❌ | Mensagem quando não há itens |
|
|
532
|
+
| `searchPlaceholder` | `string` | ❌ | Placeholder da busca |
|
|
533
|
+
| `disabled` | `boolean` | ❌ | Desabilita o componente |
|
|
534
|
+
| `required` | `boolean` | ❌ | Adiciona asterisco vermelho ao label |
|
|
535
|
+
| `isLoading` | `boolean` | ❌ | Exibe skeleton durante carregamento |
|
|
536
|
+
| `error` | `string \| boolean` | ❌ | Mensagem de erro |
|
|
537
|
+
| `className` | `string` | ❌ | Classes CSS adicionais |
|
|
538
|
+
| `sortOptions` | `boolean` | ❌ | Ordena alfabeticamente (default: true) |
|
|
539
|
+
| `maxDisplayedBadges` | `number` | ❌ | Limite de badges visíveis |
|
|
540
|
+
| `onOpen` | `() => void` | ❌ | Callback ao abrir popover |
|
|
541
|
+
| `onClose` | `() => void` | ❌ | Callback ao fechar popover |
|
|
542
|
+
|
|
543
|
+
**Exemplo com Objetos Customizados:**
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
interface User {
|
|
547
|
+
id: string;
|
|
548
|
+
name: string;
|
|
549
|
+
email: string;
|
|
550
|
+
department: string;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
<MultiSelect<User>
|
|
554
|
+
options={users}
|
|
555
|
+
value={selectedUserIds}
|
|
556
|
+
onChange={setSelectedUserIds}
|
|
557
|
+
getOptionValue={(user) => user.id}
|
|
558
|
+
getOptionLabel={(user) => `${user.name} - ${user.department}`}
|
|
559
|
+
placeholder="Selecione colaboradores..."
|
|
560
|
+
label="Colaboradores"
|
|
561
|
+
icon={Users}
|
|
562
|
+
required
|
|
563
|
+
/>
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**Limite de Badges Exibidos:**
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
<MultiSelect
|
|
570
|
+
options={items}
|
|
571
|
+
value={selected}
|
|
572
|
+
onChange={setSelected}
|
|
573
|
+
maxDisplayedBadges={3} // Exibe: [Badge 1] [Badge 2] [Badge 3] [+5]
|
|
574
|
+
placeholder="Selecione itens..."
|
|
575
|
+
/>
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
**Integração com React Hook Form:**
|
|
579
|
+
|
|
580
|
+
```typescript
|
|
581
|
+
import { useForm } from 'react-hook-form';
|
|
582
|
+
import { MultiSelect } from 'forlogic-core';
|
|
583
|
+
|
|
584
|
+
function MyForm() {
|
|
585
|
+
const { control } = useForm();
|
|
586
|
+
const { data: categories = [] } = useCategories();
|
|
587
|
+
|
|
588
|
+
return (
|
|
589
|
+
<FormField
|
|
590
|
+
control={control}
|
|
591
|
+
name="category_ids"
|
|
592
|
+
render={({ field }) => (
|
|
593
|
+
<FormItem>
|
|
594
|
+
<FormControl>
|
|
595
|
+
<MultiSelect
|
|
596
|
+
value={field.value}
|
|
597
|
+
onChange={field.onChange}
|
|
598
|
+
options={categories}
|
|
599
|
+
getOptionValue={(c) => c.id}
|
|
600
|
+
getOptionLabel={(c) => c.name}
|
|
601
|
+
placeholder="Selecione categorias..."
|
|
602
|
+
label="Categorias"
|
|
603
|
+
/>
|
|
604
|
+
</FormControl>
|
|
605
|
+
<FormMessage />
|
|
606
|
+
</FormItem>
|
|
607
|
+
)}
|
|
608
|
+
/>
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
**Usando em Formulários CRUD:**
|
|
614
|
+
|
|
615
|
+
Para usar o `MultiSelect` em formulários CRUD gerados automaticamente, crie um wrapper e use `type: 'custom'`:
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
// 1. Criar wrapper do MultiSelect
|
|
619
|
+
// src/components/TrainingMultiSelect.tsx
|
|
620
|
+
import { MultiSelect } from 'forlogic-core';
|
|
621
|
+
import { useTrainings } from '@/trainings/trainingService';
|
|
622
|
+
import { GraduationCap } from 'lucide-react';
|
|
623
|
+
|
|
624
|
+
export function TrainingMultiSelect({ value, onChange, disabled, error }: any) {
|
|
625
|
+
const { data: trainings = [], isLoading } = useTrainings();
|
|
626
|
+
|
|
627
|
+
return (
|
|
628
|
+
<MultiSelect
|
|
629
|
+
value={value || []}
|
|
630
|
+
onChange={onChange}
|
|
631
|
+
options={trainings}
|
|
632
|
+
isLoading={isLoading}
|
|
633
|
+
error={error}
|
|
634
|
+
getOptionValue={(t) => t.id}
|
|
635
|
+
getOptionLabel={(t) => t.title}
|
|
636
|
+
disabled={disabled}
|
|
637
|
+
placeholder="Selecione treinamentos..."
|
|
638
|
+
icon={GraduationCap}
|
|
639
|
+
/>
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// 2. Usar na configuração do formulário com type: 'custom'
|
|
644
|
+
import { TrainingMultiSelect } from './components/TrainingMultiSelect';
|
|
645
|
+
|
|
646
|
+
{
|
|
647
|
+
name: 'training_ids',
|
|
648
|
+
label: 'Treinamentos',
|
|
649
|
+
type: 'custom' as const,
|
|
650
|
+
component: TrainingMultiSelect,
|
|
651
|
+
required: true
|
|
652
|
+
}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
---
|
|
656
|
+
|
|
480
657
|
### 🎨 Campos Customizados no BaseForm
|
|
481
658
|
|
|
482
659
|
O `BaseForm` suporta campos totalmente customizados através do tipo `'custom'`, permitindo usar **qualquer componente React** como campo de formulário.
|
|
@@ -1010,7 +1187,7 @@ function TagsPage() {
|
|
|
1010
1187
|
#### Uso Standalone (Fora do BaseForm)
|
|
1011
1188
|
|
|
1012
1189
|
```typescript
|
|
1013
|
-
import { CreatableCombobox } from 'forlogic-core
|
|
1190
|
+
import { CreatableCombobox } from 'forlogic-core';
|
|
1014
1191
|
import { useState } from 'react';
|
|
1015
1192
|
|
|
1016
1193
|
function MyComponent() {
|
|
@@ -3825,11 +4002,42 @@ const config = useMemo((): CrudPageConfig<Process> => ({
|
|
|
3825
4002
|
|
|
3826
4003
|
Quando `enableBulkActions: true`, as seguintes ações ficam disponíveis automaticamente:
|
|
3827
4004
|
|
|
3828
|
-
- ✅ **Deletar em lote**: Deleta múltiplos itens selecionados (usa a mesma função do delete individual)
|
|
3829
4005
|
- ✅ **Checkbox "Selecionar Todos"**: No header da tabela para selecionar/deselecionar todos os itens da página
|
|
3830
4006
|
- ✅ **Indicador Visual**: Linhas/cards selecionados têm background destacado
|
|
3831
4007
|
- ✅ **Barra de Ações**: Aparece no topo quando há itens selecionados
|
|
3832
4008
|
|
|
4009
|
+
**⚠️ IMPORTANTE - Botão "Remover" Padrão:**
|
|
4010
|
+
|
|
4011
|
+
- ❌ **Se você definir `bulkActions` customizadas**, o botão "Remover" padrão **NÃO** será adicionado automaticamente
|
|
4012
|
+
- ✅ **Se você NÃO definir `bulkActions`**, um botão "Remover" padrão será adicionado automaticamente
|
|
4013
|
+
|
|
4014
|
+
```typescript
|
|
4015
|
+
// ❌ Duplicação: Botão padrão + custom "Remover"
|
|
4016
|
+
bulkActions: [
|
|
4017
|
+
{
|
|
4018
|
+
label: 'Remover', // ← Vai aparecer 2x (padrão + este)
|
|
4019
|
+
icon: Trash2,
|
|
4020
|
+
variant: 'destructive',
|
|
4021
|
+
action: async (items) => { /* ... */ }
|
|
4022
|
+
}
|
|
4023
|
+
]
|
|
4024
|
+
|
|
4025
|
+
// ✅ Correto: Apenas o padrão
|
|
4026
|
+
enableBulkActions: true,
|
|
4027
|
+
// Não definir bulkActions → Usa botão "Remover" padrão
|
|
4028
|
+
|
|
4029
|
+
// ✅ Correto: Apenas ações customizadas (sem padrão)
|
|
4030
|
+
bulkActions: [
|
|
4031
|
+
{
|
|
4032
|
+
label: 'Remover',
|
|
4033
|
+
icon: Trash2,
|
|
4034
|
+
variant: 'destructive',
|
|
4035
|
+
action: async (items) => { /* ... */ }
|
|
4036
|
+
}
|
|
4037
|
+
]
|
|
4038
|
+
```
|
|
4039
|
+
|
|
4040
|
+
|
|
3833
4041
|
### **Ações Customizadas**
|
|
3834
4042
|
|
|
3835
4043
|
Você pode adicionar ações customizadas além do delete padrão:
|
|
@@ -5416,23 +5624,35 @@ await notifyLeadershipChange(
|
|
|
5416
5624
|
|
|
5417
5625
|
### Imports Essenciais
|
|
5418
5626
|
|
|
5627
|
+
> **⚠️ IMPORTANTE**: A partir da versão 1.8.4+, todos os componentes devem ser importados diretamente de `forlogic-core`. Os exports modulares (`/ui`, `/modular`, `/crud`) foram removidos para simplificar o uso da biblioteca.
|
|
5628
|
+
|
|
5419
5629
|
```typescript
|
|
5420
|
-
//
|
|
5630
|
+
// ✅ CORRETO (v1.8.4+)
|
|
5421
5631
|
import {
|
|
5632
|
+
// CRUD
|
|
5422
5633
|
createSimpleService,
|
|
5423
5634
|
createCrudPage,
|
|
5424
5635
|
generateCrudConfig,
|
|
5425
|
-
createSimpleSaveHandler
|
|
5636
|
+
createSimpleSaveHandler,
|
|
5637
|
+
|
|
5638
|
+
// UI
|
|
5639
|
+
Button,
|
|
5640
|
+
Input,
|
|
5641
|
+
Card,
|
|
5642
|
+
toast,
|
|
5643
|
+
|
|
5644
|
+
// Auth
|
|
5645
|
+
useAuth,
|
|
5646
|
+
ProtectedRoute,
|
|
5647
|
+
|
|
5648
|
+
// Qualiex
|
|
5649
|
+
QualiexUserField,
|
|
5650
|
+
useQualiexUsers
|
|
5426
5651
|
} from 'forlogic-core';
|
|
5427
5652
|
|
|
5428
|
-
//
|
|
5429
|
-
import { Button, Input
|
|
5430
|
-
|
|
5431
|
-
// Auth
|
|
5432
|
-
import { useAuth, ProtectedRoute } from 'forlogic-core';
|
|
5433
|
-
|
|
5434
|
-
// Qualiex
|
|
5435
|
-
import { QualiexUserField, useQualiexUsers } from 'forlogic-core';
|
|
5653
|
+
// ❌ DEPRECATED (removido em v1.8.4+)
|
|
5654
|
+
// import { Button, Input } from 'forlogic-core/ui';
|
|
5655
|
+
// import { createCrudPage } from 'forlogic-core/crud';
|
|
5436
5656
|
```
|
|
5437
5657
|
|
|
5438
5658
|
### Estrutura de Arquivos
|