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 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/ui';
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
- // CRUD
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
- // UI
5429
- import { Button, Input, Card, toast } from 'forlogic-core/ui';
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/ui';
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
- // CRUD
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
- // UI
5429
- import { Button, Input, Card, toast } from 'forlogic-core/ui';
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