{"id":"repo/implementation-drag-drop","source":"repo","sourceLabel":"repository root","sourcePath":"IMPLEMENTATION_DRAG_DROP.md","routePath":"/fastapp/public-docs/page/repo/implementation-drag-drop","rawPath":"/fastapp/public-docs/raw/repo/implementation-drag-drop","slug":["repo","implementation-drag-drop"],"slugPath":"repo/implementation-drag-drop","title":"Funcionalidade de Reordenação Nativa Documentação de Implementação","description":"A funcionalidade de Drag & Drop foi implementada nativamente no componente DynamicList.vue e em um novo componente especializado PropertyListEditor.vue, permitindo que coleções de metadados (como properties em um objeto) sejam reorganiza...","headings":["Funcionalidade de Reordenação Nativa Documentação de Implementação","Visão Geral","Componentes Implementados","1. DynamicList.vue (Melhorado)","2. PropertyListEditor.vue (Novo)","Integração no Schema Manager","Mudanças Implementadas","Caso de Uso: Schema Manager","Fluxo de Uso","Exemplo Visual","Sincronização com Store","Estado \"Dirty\" Flow","Estilos e Animações","Classes CSS Personalizadas","Configuração Vuedraggable","Considerações de Performance","Testes Recomendados","Backward Compatibility","Próximas Melhorias (Sugestões)","Referências"],"wordCount":477,"markdown":"# Funcionalidade de Reordenação Nativa - Documentação de Implementação\r\n\r\n## Visão Geral\r\n\r\nA funcionalidade de Drag & Drop foi implementada nativamente no componente `DynamicList.vue` e em um novo componente especializado `PropertyListEditor.vue`, permitindo que coleções de metadados (como properties em um objeto) sejam reorganizadas visualmente com suporte completo ao estado \"dirty\" do aplicativo.\r\n\r\n## Componentes Implementados\r\n\r\n### 1. `DynamicList.vue` (Melhorado)\r\n\r\n**Localização**: `app/components/core/DynamicList.vue`\r\n\r\n**Funcionalidades principais**:\r\n- Integração com `vuedraggable` v4.0.1 (já instalado no projeto)\r\n- Reordenação condicional via prop `sortable` (default: `false`)\r\n- Ícone de \"drag handle\" com classe `.drag-handle` (seis pontos)\r\n- Preservação de UI compacta com badges de taxonomia (`category`, `nature`)\r\n- Emissão de evento `reorder` ao finalizar arraste\r\n- Animações suaves com transições CSS personalizadas\r\n\r\n**Props**:\r\n```typescript\r\ninterface Props {\r\n  schema: ObjectSchema;\r\n  data: any[];\r\n  sortable?: boolean; // Ativa/desativa reordenação\r\n}\r\n```\r\n\r\n**Eventos**:\r\n```typescript\r\n// Novo evento emitido quando usuário finaliza arraste\r\nemit('reorder', localData.value);\r\n\r\n// Eventos existentes mantidos\r\nemit('delete', item);\r\nemit('edit', item);\r\nemit('update', item);\r\nemit('refresh');\r\n```\r\n\r\n**Exemplo de Uso**:\r\n```vue\r\n<DynamicList\r\n  :schema=\"propertySchema\"\r\n  :data=\"properties\"\r\n  :sortable=\"true\"\r\n  @reorder=\"handlePropertiesReordered\"\r\n/>\r\n```\r\n\r\n### 2. `PropertyListEditor.vue` (Novo)\r\n\r\n**Localização**: `app/components/core/PropertyListEditor.vue`\r\n\r\n**Funcionalidades principais**:\r\n- Componente especializado para editar e reordenar propriedades de um objeto\r\n- Drag & Drop nativo com validação de UX\r\n- Edição inline de todos os atributos de propriedade\r\n- Checkboxes para flags (`required`, `show_in_list`, `show_in_form`)\r\n- Indicador visual de ordem (número serial)\r\n- Adição/remoção de propriedades com confirmação\r\n- Sincronização bidirecional com estado do objeto\r\n\r\n**Props**:\r\n```typescript\r\ninterface Props {\r\n  properties: PropSchema[];\r\n}\r\n```\r\n\r\n**Eventos**:\r\n```typescript\r\n// Emitido ao modificar uma propriedade individual\r\nemit('update', { \r\n  index: number;\r\n  property?: PropSchema;\r\n  action?: string;\r\n  deletedIndex?: number;\r\n});\r\n\r\n// Emitido ao finalizar reordenação\r\nemit('reorder', reorderedProperties: PropSchema[]);\r\n```\r\n\r\n**Exemplo de Uso**:\r\n```vue\r\n<PropertyListEditor\r\n  :properties=\"editingObject.properties\"\r\n  @update=\"onPropertyUpdate\"\r\n  @reorder=\"onPropertyReorder\"\r\n/>\r\n```\r\n\r\n## Integração no Schema Manager\r\n\r\nA funcionalidade foi integrada na página de Schema Manager (`app/pages/fastapp/schema.vue`):\r\n\r\n### Mudanças Implementadas\r\n\r\n1. **Importação do Componente**:\r\n```typescript\r\nimport PropertyListEditor from '~/components/core/PropertyListEditor.vue';\r\n```\r\n\r\n2. **Substituição da Interface de Edição**:\r\n```vue\r\n<!-- Antes: Lista inline com inputs -->\r\n<div v-for=\"(p, idx) in editingObject.properties\">\r\n  <InputText v-model=\"p.name\" ... />\r\n  <!-- ... mais inputs ... -->\r\n</div>\r\n\r\n<!-- Depois: Componente especializado com Drag & Drop -->\r\n<PropertyListEditor\r\n  v-if=\"editingObject\"\r\n  :properties=\"editingObject.properties\"\r\n  @update=\"onPropertyUpdate\"\r\n  @reorder=\"onPropertyReorder\"\r\n/>\r\n```\r\n\r\n3. **Handlers de Eventos**:\r\n```typescript\r\n/**\r\n * Handle property updates (add/edit/delete)\r\n */\r\nfunction onPropertyUpdate(event: { index: number; property?: any; action?: string; deletedIndex?: number }) {\r\n    if (!editingObject.value) return;\r\n    isDirty.value = true;\r\n}\r\n\r\n/**\r\n * Handle property reordering\r\n * Sincroniza a nova ordem e marca como dirty\r\n */\r\nfunction onPropertyReorder(reorderedProperties: any[]) {\r\n    if (!editingObject.value) return;\r\n    \r\n    editingObject.value.properties = reorderedProperties;\r\n    isDirty.value = true;\r\n    \r\n    toast.add({ \r\n        severity: 'info', \r\n        summary: 'Propriedades Reordenadas', \r\n        detail: 'Salve a aplicação para persistir as alterações.',\r\n        life: 2000\r\n    });\r\n}\r\n```\r\n\r\n## Caso de Uso: Schema Manager\r\n\r\n### Fluxo de Uso\r\n\r\n1. **Abrir Schema Manager** → Selecionar um App\r\n2. **Clicar em \"Editar Objeto\"** na seção \"Objetos do Sistema\"\r\n3. **Dialog Se Abre** com a interface visual de edição\r\n4. **PropertyListEditor é Renderizado** com lista de propriedades\r\n5. **Usuário Arrasta Propriedades** para reordenar\r\n6. **Evento `reorder` é Emitido** com a nova ordem\r\n7. **Estado `isDirty` é Marcado como `true`** (visível no indicador amarelo)\r\n8. **Botão \"Salvar\"** na header fica habilitado\r\n9. **Usuário Clica \"Aplicar ao App\"** para persistir a ordem no objeto\r\n10. **Usuário Clica \"Salvar\"** para persistir no banco de dados\r\n\r\n### Exemplo Visual\r\n\r\n```\r\n📋 Schema Manager\r\n\r\n┌─────────────────────────────────────────┐\r\n│ Definição de Objeto: lead               │\r\n├─────────────────────────────────────────┤\r\n│ ID Interno: lead                        │\r\n│ Label: Lead                             │\r\n│                                         │\r\n│ PROPRIEDADES (5)        [+ Adicionar]   │\r\n│ ┌─────────────────────────────────────┐ │\r\n│ │ ⋮⋮  name    Full Name   string      │ │\r\n│ │ ⋮⋮  email   E-mail      string  🔳 🔲│ │\r\n│ │ ⋮⋮  phone   Telefone    string  ✓ ✓ │ │ ← Reordenável via arraste\r\n│ │ ⋮⋮  status  Status      enum    ✓ ✓ │ │\r\n│ │ ⋮⋮  notes   Notas       json    🔲 ✓ │ │\r\n│ └─────────────────────────────────────┘ │\r\n│                                         │\r\n│ [Fechar]  [Aplicar ao App]              │\r\n└─────────────────────────────────────────┘\r\n```\r\n\r\n## Sincronização com Store\r\n\r\n### Estado \"Dirty\" Flow\r\n\r\n```typescript\r\n// 1. Usuário arrasta propriedade\r\nonPropertyReorder(reorderedProperties) {\r\n    // 2. Propriedades do objeto são atualizadas\r\n    editingObject.value.properties = reorderedProperties;\r\n    \r\n    // 3. isDirty é marcado como true\r\n    isDirty.value = true;\r\n    \r\n    // 4. Toast notifica o usuário\r\n    toast.add({ ... });\r\n    \r\n    // 5. Botão \"Salvar\" na header fica habilitado\r\n    // (disabled={!isDirty})\r\n}\r\n\r\n// 6. Usuário clica \"Aplicar ao App\"\r\napplyObjectChanges() {\r\n    // 7. Objeto com propriedades reordenadas é atualizado no selectedApp\r\n    selectedApp.value.objects[idx] = editingObject.value;\r\n    \r\n    // 8. isDirty permanece true até salvar\r\n}\r\n\r\n// 9. Usuário clica \"Salvar\"\r\nsaveChanges() {\r\n    // 10. Payload é enviado para API\r\n    await $fetch(`/api/app/${payload.id}`, {\r\n        method: 'PATCH',\r\n        body: payload\r\n    });\r\n    \r\n    // 11. isDirty é resetado\r\n    isDirty.value = false;\r\n    \r\n    // 12. Apps são refreshed e estado sincronizado\r\n}\r\n```\r\n\r\n## Estilos e Animações\r\n\r\n### Classes CSS Personalizadas\r\n\r\n```css\r\n/* Drag container */\r\n.drag-list-move {\r\n  transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\r\n}\r\n\r\n/* Ghost state (elemento sendo arrastado) */\r\n.ghost {\r\n  opacity: 0.5;\r\n  background: rgba(99, 102, 241, 0.1);\r\n  border-color: rgba(99, 102, 241, 0.3);\r\n}\r\n\r\n/* Drag state (hover) */\r\n.drag {\r\n  opacity: 1;\r\n  transform: rotate(2deg) scale(1.02);\r\n  box-shadow: 0 5px 25px rgba(99, 102, 241, 0.3);\r\n}\r\n\r\n/* Handle cursor */\r\n.drag-handle {\r\n  cursor: grab;\r\n}\r\n.drag-handle:active {\r\n  cursor: grabbing;\r\n}\r\n```\r\n\r\n## Configuração Vuedraggable\r\n\r\n```javascript\r\nconst dragOptions = computed(() => ({\r\n  animation: 200,                    // Duração da animação (ms)\r\n  group: \"properties\",               // Grupo para restricionar arraste\r\n  ghostClass: \"ghost\",               // Classe do elemento sendo arrastado\r\n  dragClass: \"drag\",                 // Classe durante arraste\r\n  handle: \".drag-handle\",            // Seletor do handle\r\n  easing: \"cubic-bezier(1, 0, 0, 1)\" // Easing animation\r\n}));\r\n```\r\n\r\n## Considerações de Performance\r\n\r\n1. **Computed Properties**: Utilizadas para otimizar re-renders\r\n2. **v-model Reativo**: Mantém sincronização entre componente pai e filho\r\n3. **Watch com Deep**: Monitora mudanças profundas em propriedades\r\n4. **Transition Groups**: Aplica animações apenas quando necessário\r\n\r\n## Testes Recomendados\r\n\r\n```\r\n✓ Arrastar propriedade de cima para baixo\r\n✓ Arrastar propriedade de baixo para cima\r\n✓ Adicionar nova propriedade (deve aparecer no final)\r\n✓ Remover propriedade (com confirmação)\r\n✓ Editar nome/label durante reordenação\r\n✓ isDirty deve ser true após qualquer mudança\r\n✓ isDirty deve ser false após salvar\r\n✓ Botão \"Salvar\" deve estar desabilitado quando isDirty=false\r\n✓ Toast deve aparecer após reordenação\r\n✓ Ordem deve persistir no banco após salvar\r\n```\r\n\r\n## Backward Compatibility\r\n\r\nOs componentes mantêm retrocompatibilidade:\r\n- `DynamicList.vue`: Funciona normalmente sem prop `sortable` (padrão: false)\r\n- Propriedades antigas do componente continuam funcionando\r\n- Eventos legados são mantidos\r\n- A reordenação é uma opção, não obrigatória\r\n\r\n## Próximas Melhorias (Sugestões)\r\n\r\n1. **Persistência Automática**: Auto-save após 2 segundos de inatividade\r\n2. **Undo/Redo**: Stack de mudanças para desfazer/refazer\r\n3. **Vinculação a CRM**: Sincronizar ordem com renderização de formulários em tempo real\r\n4. **Histórico de Mudanças**: Log de quem alterou e quando\r\n5. **Validação Avançada**: Verificar dependências entre campos antes de reordenar\r\n6. **Temas Customizáveis**: Cores e ícones configuráveis via props\r\n\r\n## Referências\r\n\r\n- **Vuedraggable**: https://github.com/SortableJS/vue.draggable.next\r\n- **Sortable.js**: https://sortablejs.github.io/Sortable/\r\n- **Vue 3 Composables**: https://vuejs.org/guide/extras/composition-api-faq.html\r\n"}