forlogic-core 1.5.2 → 1.5.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
@@ -1,36 +1,628 @@
1
- # 🧱 Documentação exclusiva para este projeto
1
+ # 🧱 Projeto atual
2
2
 
3
- > **Sistema empresarial com React + TypeScript + Supabase**
3
+ **Schema padrão:** `central`
4
+
5
+ > **Nota sobre schemas:** O schema padrão é `central`, mas módulos podem usar schemas diferentes quando necessário para organização ou isolamento de dados. Por exemplo, o módulo de treinamentos usa o schema `trainings`. Você pode especificar o schema no service usando `schemaName: 'nome_do_schema'`.
6
+
7
+ ---
8
+
9
+ ## 🤖 REGRAS CRÍTICAS
10
+
11
+ ### ⚠️ TOP 3 ERROS
12
+
13
+ 1. **ESQUECER SCHEMA**
14
+ ```typescript
15
+ // ❌ ERRADO
16
+ .from('table')
17
+
18
+ // ✅ CORRETO
19
+ .schema('schema').from('table')
20
+ ```
21
+
22
+ 2. **RLS COM `id` (sem alias)**
23
+ ```sql
24
+ -- ❌ ERRADO
25
+ CREATE POLICY "Users view own" ON schema.table
26
+ FOR SELECT USING (id_user = auth.uid());
27
+
28
+ -- ✅ CORRETO
29
+ CREATE POLICY "Users view own" ON schema.table
30
+ FOR SELECT USING (alias = auth.uid());
31
+ ```
32
+
33
+ 3. **NÃO CRIAR ÍNDICES AUTOMATICAMENTE**
34
+ ```sql
35
+ -- ❌ PROIBIDO criar índices sem aprovação
36
+ CREATE INDEX idx_table_user ON schema.table(id_user);
37
+
38
+ -- ✅ Apenas quando solicitado explicitamente
39
+ ```
40
+
41
+ ---
42
+
43
+ ### ✅ CHECKLIST (antes de implementar)
44
+
45
+ - [ ] Schema `schema` especificado em queries e service?
46
+ - [ ] RLS usando `alias` (nunca `id_user`)?
47
+ - [ ] Preservar `item.id` no update?
48
+ - [ ] Config gerado com `useMemo()`?
49
+ - [ ] `<Outlet />` no componente pai?
50
+ - [ ] Qualiex header `un-alias` configurado?
4
51
 
5
52
  ---
6
53
 
7
- ## ⚙️ Configuração deste Projeto
54
+ ## 🚀 QUICK START - Criar CRUD Completo
55
+
56
+ ### **1️⃣ Type**
57
+ ```typescript
58
+ // src/processes/process.ts
59
+ export interface Process {
60
+ id: string;
61
+ id_user: string | null;
62
+ title: string;
63
+ description?: string;
64
+ status: 'draft' | 'active' | 'completed';
65
+ created_at?: string;
66
+ updated_at?: string;
67
+ deleted?: boolean;
68
+ }
69
+
70
+ export type ProcessInsert = Omit<Process, 'id' | 'created_at' | 'updated_at'>;
71
+ export type ProcessUpdate = Partial<ProcessInsert>;
72
+ ```
73
+
74
+ ### **2️⃣ Service**
75
+ ```typescript
76
+ // src/processes/processService.ts
77
+ import { createSimpleService } from 'forlogic-core';
78
+ import { Process, ProcessInsert, ProcessUpdate } from './process';
79
+
80
+ export const { service: processService, useCrudHook: useProcesses } =
81
+ createSimpleService<Process, ProcessInsert, ProcessUpdate>({
82
+ tableName: 'processes',
83
+ entityName: 'processo',
84
+ schemaName: 'central', // ⚠️ OBRIGATÓRIO
85
+ searchFields: ['title', 'description'],
86
+ enableQualiexEnrichment: true
87
+ });
88
+ ```
89
+
90
+ ### **3️⃣ Save Handler**
91
+ ```typescript
92
+ // src/processes/ProcessesPage.tsx
93
+ import { createSimpleSaveHandler } from 'forlogic-core';
94
+ import { processService } from './processService';
95
+
96
+ const handleSave = createSimpleSaveHandler({
97
+ service: processService,
98
+ entityName: 'processo'
99
+ });
8
100
 
9
- ### 🗄️ Schema do Banco
101
+ // ⚠️ CRÍTICO: Preservar ID no update
102
+ const handleUpdate = async (item: Process) => {
103
+ await handleSave({
104
+ ...item,
105
+ id: item.id // ⚠️ OBRIGATÓRIO para update
106
+ });
107
+ };
108
+ ```
10
109
 
11
- Schema padrão deste projeto: **`central`**
110
+ ### **4️⃣ Config (com useMemo)**
111
+ ```typescript
112
+ // src/processes/ProcessesPage.tsx
113
+ import { useMemo } from 'react';
114
+ import { generateCrudConfig } from 'forlogic-core';
12
115
 
13
- ## 🤖 Instruções para IA (Lovable)
116
+ export default function ProcessesPage() {
117
+ const crud = useProcesses();
14
118
 
15
- ⚠️ **CONSULTAR SEMPRE antes de qualquer modificação:**
119
+ // ⚠️ OBRIGATÓRIO useMemo para evitar re-renders
120
+ const config = useMemo(() =>
121
+ generateCrudConfig<Process>({
122
+ entity: 'processo',
123
+ columns: [
124
+ { key: 'title', label: 'Título', type: 'text', required: true },
125
+ { key: 'status', label: 'Status', type: 'select',
126
+ options: [
127
+ { value: 'draft', label: 'Rascunho' },
128
+ { value: 'active', label: 'Ativo' },
129
+ { value: 'completed', label: 'Concluído' }
130
+ ]
131
+ },
132
+ { key: 'description', label: 'Descrição', type: 'textarea' }
133
+ ]
134
+ }),
135
+ []);
16
136
 
17
- 1. **README.md da lib**: Acesse via `node_modules/forlogic-core/dist/README.md` - Instalação e Quick Start
18
- 2. **AI_RULES.md**: Acesse via `node_modules/forlogic-core/dist/docs/AI_RULES.md` - **REGRAS CRÍTICAS**
19
- 3. **Templates**: Acesse via `node_modules/forlogic-core/dist/docs/templates/` - Códigos prontos
20
- 4. **Hub da Docs**: Acesse via `node_modules/forlogic-core/dist/docs/README.md` - Navegação completa
137
+ return createCrudPage({
138
+ config,
139
+ crud,
140
+ saveHandler: handleSave
141
+ });
142
+ }
143
+ ```
21
144
 
22
- **NUNCA recriar:**
23
- - ❌ Integração Qualiex (já existe na lib)
24
- - ❌ Integração Supabase (já existe na lib)
25
- - Sistema CRUD (usar `createCrudPage` da lib)
26
- - ❌ Componentes base (importar de `forlogic-core`)
145
+ ### **5️⃣ Page + Outlet (preservar estado)**
146
+ ```typescript
147
+ // src/App.tsx
148
+ import { Outlet } from 'react-router-dom';
27
149
 
28
- **SEMPRE verificar:**
29
- - ✅ Schema padrão do projeto
30
- - ✅ Templates disponíveis antes de criar do zero
150
+ function App() {
151
+ return (
152
+ <Routes>
153
+ <Route path="/processes" element={<ProcessLayout />}>
154
+ <Route index element={<ProcessesPage />} />
155
+ <Route path="new" element={<ProcessesPage />} />
156
+ <Route path=":id/edit" element={<ProcessesPage />} />
157
+ </Route>
158
+ </Routes>
159
+ );
160
+ }
161
+
162
+ // ⚠️ OBRIGATÓRIO: Outlet preserva estado
163
+ function ProcessLayout() {
164
+ return <Outlet />; // Evita reload do componente
165
+ }
166
+ ```
167
+
168
+ ---
169
+
170
+ ## 🎯 AÇÕES EM LOTE (Bulk Actions)
171
+
172
+ O sistema CRUD suporta seleção múltipla e ações em lote usando checkboxes.
173
+
174
+ ### **Habilitar Bulk Actions**
175
+
176
+ Adicione `enableBulkActions: true` na configuração:
177
+
178
+ ```typescript
179
+ const config = useMemo((): CrudPageConfig<Process> => ({
180
+ entityName: "Processo",
181
+ entityNamePlural: "Processos",
182
+ enableBulkActions: true, // 🆕 Habilita checkboxes e ações em lote
183
+ columns: [...],
184
+ formSections: [...]
185
+ }), []);
186
+ ```
187
+
188
+ ### **Ações Padrão Disponíveis**
189
+
190
+ Quando `enableBulkActions: true`, as seguintes ações ficam disponíveis automaticamente:
191
+
192
+ - ✅ **Deletar em lote**: Deleta múltiplos itens selecionados (usa a mesma função do delete individual)
193
+ - ✅ **Checkbox "Selecionar Todos"**: No header da tabela para selecionar/deselecionar todos os itens da página
194
+ - ✅ **Indicador Visual**: Linhas/cards selecionados têm background destacado
195
+ - ✅ **Barra de Ações**: Aparece no topo quando há itens selecionados
196
+
197
+ ### **Ações Customizadas**
198
+
199
+ Você pode adicionar ações customizadas além do delete padrão:
200
+
201
+ ```typescript
202
+ import { Download, Archive } from 'lucide-react';
203
+ import type { BulkAction } from 'forlogic-core';
204
+
205
+ const config: CrudPageConfig<Process> = {
206
+ entityName: "Processo",
207
+ entityNamePlural: "Processos",
208
+ enableBulkActions: true,
209
+ bulkActions: [
210
+ {
211
+ label: "Exportar Selecionados",
212
+ icon: Download,
213
+ variant: "outline", // 'default' | 'destructive' | 'outline'
214
+ action: async (items) => {
215
+ // Sua lógica de exportação
216
+ const selectedItems = manager.entities.filter(
217
+ e => manager.selectedIds.includes(e.id)
218
+ );
219
+ await exportToExcel(selectedItems);
220
+ }
221
+ },
222
+ {
223
+ label: "Arquivar",
224
+ icon: Archive,
225
+ confirmMessage: "Tem certeza que deseja arquivar os itens selecionados?",
226
+ action: async (items) => {
227
+ const selectedItems = manager.entities.filter(
228
+ e => manager.selectedIds.includes(e.id)
229
+ );
230
+ await bulkArchive(selectedItems.map(i => i.id));
231
+ }
232
+ }
233
+ ],
234
+ columns: [...]
235
+ };
236
+ ```
237
+
238
+ ### **Comportamento**
239
+
240
+ **Desktop (Tabela):**
241
+ - Checkbox na primeira coluna (50px de largura)
242
+ - Checkbox no header para "Selecionar Todos"
243
+ - Click na linha NÃO abre o form quando bulk actions está ativo (evita edição acidental)
244
+
245
+ **Mobile (Cards):**
246
+ - Checkbox no canto superior esquerdo de cada card
247
+ - Mesmo comportamento de seleção que desktop
248
+
249
+ **Barra de Ações:**
250
+ - Aparece automaticamente quando há itens selecionados
251
+ - Mostra quantidade de itens selecionados
252
+ - Botão "Limpar" para deselecionar todos
253
+ - Botões de ações (Delete + customizadas)
254
+
255
+ ### **Propriedades do Manager**
256
+
257
+ Quando bulk actions está habilitado, o `manager` retornado pelo `useCrud` inclui:
258
+
259
+ ```typescript
260
+ manager.selectedIds // string[] - IDs dos itens selecionados
261
+ manager.selectItem(id) // Seleciona/desseleciona um item
262
+ manager.selectAll() // Seleciona/desseleciona todos
263
+ manager.clearSelection() // Limpa seleção
264
+ manager.isAllSelected // boolean - Todos selecionados?
265
+ manager.bulkDelete(ids) // Deleta múltiplos IDs
266
+ manager.isBulkDeleting // boolean - Loading do bulk delete
267
+ ```
268
+
269
+ ### **Exemplo Completo**
270
+
271
+ ```typescript
272
+ // src/processes/ProcessesPage.tsx
273
+ import { useMemo } from 'react';
274
+ import { createCrudPage, createSimpleSaveHandler, type BulkAction } from 'forlogic-core';
275
+ import { Download, Archive } from 'lucide-react';
276
+ import { Process } from './process';
277
+ import { useProcessCrud } from './processService';
278
+ import { exportToExcel } from '@/utils/export';
279
+
280
+ export function ProcessesPage() {
281
+ const manager = useProcessCrud();
282
+
283
+ const bulkActions: BulkAction<Process>[] = useMemo(() => [
284
+ {
285
+ label: "Exportar",
286
+ icon: Download,
287
+ variant: "outline",
288
+ action: async () => {
289
+ const selected = manager.entities.filter(
290
+ e => manager.selectedIds.includes(e.id)
291
+ );
292
+ await exportToExcel(selected);
293
+ }
294
+ }
295
+ ], [manager.entities, manager.selectedIds]);
296
+
297
+ const config = useMemo(() => ({
298
+ entityName: "Processo",
299
+ entityNamePlural: "Processos",
300
+ enableBulkActions: true,
301
+ bulkActions,
302
+ columns: [
303
+ { key: 'title', header: 'Título', sortable: true },
304
+ { key: 'status', header: 'Status', className: 'w-24 text-center' },
305
+ ],
306
+ formSections: [...]
307
+ }), [bulkActions]);
308
+
309
+ const handleSave = createSimpleSaveHandler(
310
+ manager,
311
+ (data) => ({ ...data }),
312
+ (data) => ({ ...data })
313
+ );
314
+
315
+ const CrudPage = createCrudPage({
316
+ manager,
317
+ config,
318
+ onSave: handleSave
319
+ });
320
+
321
+ return <CrudPage />;
322
+ }
323
+ ```
324
+
325
+ ---
326
+
327
+ ### 🔗 Integração Qualiex (opcional)
328
+
329
+ **Auto-enrichment** (já configurado no BaseService):
330
+ ```typescript
331
+ // ✅ Automático - dados enriquecidos com nome do usuário
332
+ const processes = await processService.getAll();
333
+ // processes[0].usuario_nome = "João Silva" (se enableQualiexEnrichment: true)
334
+ ```
335
+
336
+ **Componentes prontos:**
337
+ ```typescript
338
+ import { QualiexUserField, QualiexResponsibleSelectField } from 'forlogic-core';
339
+
340
+ // Select de usuários Qualiex
341
+ <QualiexResponsibleSelectField
342
+ value={form.watch('id_user')}
343
+ onChange={(userId) => form.setValue('id_user', userId)}
344
+ />
345
+ ```
346
+
347
+ **Componentes em formulários CRUD:**
348
+ ```typescript
349
+ // Para seleção de usuário
350
+ {
351
+ name: 'responsible_id',
352
+ label: 'Responsável',
353
+ type: 'simple-qualiex-user-field' as const,
354
+ required: true
355
+ }
356
+
357
+ // Para seleção de responsável
358
+ {
359
+ name: 'responsible_id',
360
+ label: 'Responsável',
361
+ type: 'single-responsible-select' as const,
362
+ required: true
363
+ }
364
+ ```
365
+
366
+ **Componentes customizados:**
367
+
368
+ Você pode criar e usar componentes customizados nos formulários para necessidades específicas:
369
+
370
+ ```typescript
371
+ // Exemplo de campo customizado
372
+ {
373
+ name: 'custom_field',
374
+ label: 'Campo Customizado',
375
+ type: 'my-custom-component' as const,
376
+ required: true
377
+ }
378
+ ```
379
+
380
+ > **Nota:** Componentes customizados devem ser registrados no `BaseForm.tsx` para funcionarem corretamente nos formulários CRUD.
381
+
382
+ **⚠️ CRÍTICO:** Requests Qualiex exigem header `un-alias`:
383
+ ```typescript
384
+ // ✅ Já configurado no BaseService automaticamente
385
+ headers: { 'un-alias': 'true' }
386
+ ```
387
+
388
+ ---
389
+
390
+ ## 🗃️ MIGRATIONS + RLS
391
+
392
+ ### Template SQL Completo
393
+ ```sql
394
+ -- 1️⃣ Criar tabela
395
+ CREATE TABLE central.processes (
396
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
397
+ id_user UUID, -- ⚠️ NUNCA foreign key para auth.users
398
+ title TEXT NOT NULL,
399
+ description TEXT,
400
+ status TEXT DEFAULT 'draft',
401
+ created_at TIMESTAMPTZ DEFAULT now(),
402
+ updated_at TIMESTAMPTZ DEFAULT now(),
403
+ deleted BOOLEAN DEFAULT false
404
+ );
405
+
406
+ -- 2️⃣ Habilitar RLS
407
+ ALTER TABLE central.processes ENABLE ROW LEVEL SECURITY;
408
+
409
+ -- 3️⃣ Políticas RLS (usar 'alias', nunca 'id_user')
410
+ CREATE POLICY "Users view own processes"
411
+ ON central.processes FOR SELECT
412
+ USING (alias = auth.uid()); -- ⚠️ 'alias', não 'id_user'
413
+
414
+ CREATE POLICY "Users insert own processes"
415
+ ON central.processes FOR INSERT
416
+ WITH CHECK (alias = auth.uid());
417
+
418
+ CREATE POLICY "Users update own processes"
419
+ ON central.processes FOR UPDATE
420
+ USING (alias = auth.uid());
421
+
422
+ -- 4️⃣ Trigger updated_at
423
+ CREATE TRIGGER set_updated_at
424
+ BEFORE UPDATE ON central.processes
425
+ FOR EACH ROW
426
+ EXECUTE FUNCTION public.set_updated_at();
427
+
428
+ -- 5️⃣ Índices (apenas se necessário)
429
+ -- ⚠️ NÃO criar automaticamente - solicitar aprovação
430
+ -- CREATE INDEX idx_processes_user ON central.processes(id_user);
431
+ ```
432
+
433
+ ### ❌ Sintaxes Proibidas RLS
434
+ ```sql
435
+ -- NUNCA usar 'id' ou 'id_user' direto
436
+ id_user = auth.uid() -- ❌ ERRADO
437
+ id = auth.uid() -- ❌ ERRADO
438
+
439
+ -- SEMPRE usar 'alias'
440
+ alias = auth.uid() -- ✅ CORRETO
441
+ ```
442
+
443
+ ---
444
+
445
+ ## 🐛 TROUBLESHOOTING
446
+
447
+ ### 1️⃣ "relation does not exist"
448
+ ```typescript
449
+ // Causa: Schema ausente
450
+ .from('processes') // ❌
451
+
452
+ // Solução:
453
+ .from('processes', { schema: 'central' }) // ✅
454
+ ```
455
+
456
+ ### 2️⃣ RLS retorna vazio
457
+ ```sql
458
+ -- Causa: Sintaxe incorreta
459
+ USING (id_user = auth.uid()) -- ❌
460
+
461
+ -- Solução:
462
+ USING (alias = auth.uid()) -- ✅
463
+ ```
464
+
465
+ ### 3️⃣ Duplicação de registros
466
+ ```typescript
467
+ // Causa: ID ausente no update
468
+ await service.save({ title: 'Novo' }); // ❌ Cria duplicado
469
+
470
+ // Solução: Preservar ID
471
+ await service.save({ id: item.id, title: 'Novo' }); // ✅
472
+ ```
473
+
474
+ ### 4️⃣ Página recarrega ao editar
475
+ ```typescript
476
+ // Causa: Config sem useMemo
477
+ const config = generateCrudConfig(...); // ❌ Re-render infinito
478
+
479
+ // Solução:
480
+ const config = useMemo(() => generateCrudConfig(...), []); // ✅
481
+ ```
482
+
483
+ ### 5️⃣ Estado reseta ao navegar
484
+ ```typescript
485
+ // Causa: Outlet ausente
486
+ <Route path="/processes" element={<ProcessesPage />} /> // ❌
487
+
488
+ // Solução: Layout com Outlet
489
+ <Route path="/processes" element={<ProcessLayout />}>
490
+ <Route index element={<ProcessesPage />} />
491
+ </Route>
492
+ // ProcessLayout: return <Outlet />; // ✅
493
+ ```
494
+
495
+ ### 6️⃣ Qualiex retorna 401
496
+ ```typescript
497
+ // Causa: Header ausente
498
+ fetch(url); // ❌
499
+
500
+ // Solução: Adicionar header
501
+ fetch(url, { headers: { 'un-alias': 'true' } }); // ✅
502
+ // (BaseService já faz automaticamente)
503
+ ```
504
+
505
+ ---
506
+
507
+ ## 📐 CONTROLE DE LARGURA DAS COLUNAS
508
+
509
+ O `forlogic-core` oferece três formas de definir larguras de colunas nas tabelas CRUD:
510
+
511
+ ### **1️⃣ Via `className` (Recomendado)**
512
+ Use classes do Tailwind para larguras fixas ou responsivas:
513
+ ```typescript
514
+ const columns = [
515
+ {
516
+ key: 'status',
517
+ header: 'Status',
518
+ className: 'w-24 text-center', // Largura fixa: 96px
519
+ },
520
+ {
521
+ key: 'updated_at',
522
+ header: 'Atualizado em',
523
+ className: 'w-40 text-center whitespace-nowrap', // 160px, sem quebra
524
+ },
525
+ ];
526
+ ```
527
+
528
+ ### **2️⃣ Via `width` (Fixo em pixels)**
529
+ Especifique largura fixa diretamente:
530
+ ```typescript
531
+ {
532
+ key: 'order',
533
+ header: 'Ordem',
534
+ width: 60, // Largura fixa: 60px
535
+ className: 'text-center',
536
+ }
537
+ ```
538
+
539
+ ### **3️⃣ Via `minWidth` + `weight` (Flexível)**
540
+ Para colunas que crescem proporcionalmente:
541
+ ```typescript
542
+ {
543
+ key: 'description',
544
+ header: 'Descrição',
545
+ minWidth: 200, // Mínimo: 200px
546
+ weight: 2, // 2x mais espaço que colunas padrão (weight: 1)
547
+ }
548
+ ```
549
+
550
+ ### **⚠️ Importante**
551
+ - A tabela usa `table-auto` para respeitar essas configurações
552
+ - Para truncar textos longos, use: `className: "max-w-[200px] truncate"`
553
+ - Combine `whitespace-nowrap` com largura fixa para evitar quebras
554
+
555
+ ### **📋 Exemplo Completo**
556
+ ```typescript
557
+ const columns: CrudColumn<MyEntity>[] = [
558
+ {
559
+ key: 'order',
560
+ header: 'Ordem',
561
+ width: 60, // Fixo: 60px
562
+ className: 'text-center',
563
+ },
564
+ {
565
+ key: 'title',
566
+ header: 'Título',
567
+ minWidth: 200, // Mínimo: 200px, cresce conforme espaço disponível
568
+ weight: 3, // 3x mais espaço que colunas padrão
569
+ },
570
+ {
571
+ key: 'status',
572
+ header: 'Status',
573
+ className: 'w-24 text-center', // Tailwind: 96px
574
+ },
575
+ {
576
+ key: 'progress',
577
+ header: 'Nível & Progresso',
578
+ className: 'w-40 text-center', // Tailwind: 160px
579
+ },
580
+ {
581
+ key: 'updated_at',
582
+ header: 'Atualizado em',
583
+ className: 'w-40 text-center whitespace-nowrap', // 160px, sem quebra
584
+ render: (item) => formatDate(item.updated_at)
585
+ },
586
+ ];
587
+ ```
588
+
589
+ ---
590
+
591
+ ## 📚 REFERÊNCIA RÁPIDA
592
+
593
+ ### Imports Essenciais
594
+ ```typescript
595
+ // CRUD
596
+ import {
597
+ createSimpleService,
598
+ createCrudPage,
599
+ generateCrudConfig,
600
+ createSimpleSaveHandler
601
+ } from 'forlogic-core';
602
+
603
+ // UI
604
+ import { Button, Input, Card, toast } from 'forlogic-core/ui';
605
+
606
+ // Auth
607
+ import { useAuth, ProtectedRoute } from 'forlogic-core';
608
+
609
+ // Qualiex
610
+ import { QualiexUserField, useQualiexUsers } from 'forlogic-core';
611
+ ```
612
+
613
+ ### Estrutura de Arquivos
614
+ ```
615
+ src/
616
+ ├── processes/
617
+ │ ├── process.ts # Types
618
+ │ ├── processService.ts # Service + Hook
619
+ │ └── ProcessesPage.tsx # Page + Config
620
+ ```
31
621
 
32
622
  ---
33
623
 
34
624
  ## 📝 Licença
35
625
 
36
- MIT License - ForLogic © 2025
626
+ MIT License - ForLogic © 2025
627
+
628
+ **Última atualização:** 2025-10-05