IMPLEMENTATION_DRAG_DROP.md

Funcionalidade de Reordenação Nativa Documentação de Implementação

Public Markdown Document

Funcionalidade de Reordenação Nativa Documentação de Implementação

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...

477 palavras20 headingsrepository root

Funcionalidade de Reordenação Nativa - Documentação de Implementação

Visão Geral

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 reorganizadas visualmente com suporte completo ao estado "dirty" do aplicativo.

Componentes Implementados

1. DynamicList.vue (Melhorado)

Localização: app/components/core/DynamicList.vue

Funcionalidades principais:

  • Integração com vuedraggable v4.0.1 (já instalado no projeto)
  • Reordenação condicional via prop sortable (default: false)
  • Ícone de "drag handle" com classe .drag-handle (seis pontos)
  • Preservação de UI compacta com badges de taxonomia (category, nature)
  • Emissão de evento reorder ao finalizar arraste
  • Animações suaves com transições CSS personalizadas

Props:

interface Props {
  schema: ObjectSchema;
  data: any[];
  sortable?: boolean; // Ativa/desativa reordenação
}

Eventos:

// Novo evento emitido quando usuário finaliza arraste
emit('reorder', localData.value);

// Eventos existentes mantidos
emit('delete', item);
emit('edit', item);
emit('update', item);
emit('refresh');

Exemplo de Uso:

<DynamicList
  :schema="propertySchema"
  :data="properties"
  :sortable="true"
  @reorder="handlePropertiesReordered"
/>

2. PropertyListEditor.vue (Novo)

Localização: app/components/core/PropertyListEditor.vue

Funcionalidades principais:

  • Componente especializado para editar e reordenar propriedades de um objeto
  • Drag & Drop nativo com validação de UX
  • Edição inline de todos os atributos de propriedade
  • Checkboxes para flags (required, show_in_list, show_in_form)
  • Indicador visual de ordem (número serial)
  • Adição/remoção de propriedades com confirmação
  • Sincronização bidirecional com estado do objeto

Props:

interface Props {
  properties: PropSchema[];
}

Eventos:

// Emitido ao modificar uma propriedade individual
emit('update', { 
  index: number;
  property?: PropSchema;
  action?: string;
  deletedIndex?: number;
});

// Emitido ao finalizar reordenação
emit('reorder', reorderedProperties: PropSchema[]);

Exemplo de Uso:

<PropertyListEditor
  :properties="editingObject.properties"
  @update="onPropertyUpdate"
  @reorder="onPropertyReorder"
/>

Integração no Schema Manager

A funcionalidade foi integrada na página de Schema Manager (app/pages/fastapp/schema.vue):

Mudanças Implementadas

  1. Importação do Componente:
import PropertyListEditor from '~/components/core/PropertyListEditor.vue';
  1. Substituição da Interface de Edição:
<!-- Antes: Lista inline com inputs -->
<div v-for="(p, idx) in editingObject.properties">
  <InputText v-model="p.name" ... />
  <!-- ... mais inputs ... -->
</div>

<!-- Depois: Componente especializado com Drag & Drop -->
<PropertyListEditor
  v-if="editingObject"
  :properties="editingObject.properties"
  @update="onPropertyUpdate"
  @reorder="onPropertyReorder"
/>
  1. Handlers de Eventos:
/**
 * Handle property updates (add/edit/delete)
 */
function onPropertyUpdate(event: { index: number; property?: any; action?: string; deletedIndex?: number }) {
    if (!editingObject.value) return;
    isDirty.value = true;
}

/**
 * Handle property reordering
 * Sincroniza a nova ordem e marca como dirty
 */
function onPropertyReorder(reorderedProperties: any[]) {
    if (!editingObject.value) return;
    
    editingObject.value.properties = reorderedProperties;
    isDirty.value = true;
    
    toast.add({ 
        severity: 'info', 
        summary: 'Propriedades Reordenadas', 
        detail: 'Salve a aplicação para persistir as alterações.',
        life: 2000
    });
}

Caso de Uso: Schema Manager

Fluxo de Uso

  1. Abrir Schema Manager → Selecionar um App
  2. Clicar em "Editar Objeto" na seção "Objetos do Sistema"
  3. Dialog Se Abre com a interface visual de edição
  4. PropertyListEditor é Renderizado com lista de propriedades
  5. Usuário Arrasta Propriedades para reordenar
  6. Evento reorder é Emitido com a nova ordem
  7. Estado isDirty é Marcado como true (visível no indicador amarelo)
  8. Botão "Salvar" na header fica habilitado
  9. Usuário Clica "Aplicar ao App" para persistir a ordem no objeto
  10. Usuário Clica "Salvar" para persistir no banco de dados

Exemplo Visual

📋 Schema Manager

┌─────────────────────────────────────────┐
│ Definição de Objeto: lead               │
├─────────────────────────────────────────┤
│ ID Interno: lead                        │
│ Label: Lead                             │
│                                         │
│ PROPRIEDADES (5)        [+ Adicionar]   │
│ ┌─────────────────────────────────────┐ │
│ │ ⋮⋮  name    Full Name   string      │ │
│ │ ⋮⋮  email   E-mail      string  🔳 🔲│ │
│ │ ⋮⋮  phone   Telefone    string  ✓ ✓ │ │ ← Reordenável via arraste
│ │ ⋮⋮  status  Status      enum    ✓ ✓ │ │
│ │ ⋮⋮  notes   Notas       json    🔲 ✓ │ │
│ └─────────────────────────────────────┘ │
│                                         │
│ [Fechar]  [Aplicar ao App]              │
└─────────────────────────────────────────┘

Sincronização com Store

Estado "Dirty" Flow

// 1. Usuário arrasta propriedade
onPropertyReorder(reorderedProperties) {
    // 2. Propriedades do objeto são atualizadas
    editingObject.value.properties = reorderedProperties;
    
    // 3. isDirty é marcado como true
    isDirty.value = true;
    
    // 4. Toast notifica o usuário
    toast.add({ ... });
    
    // 5. Botão "Salvar" na header fica habilitado
    // (disabled={!isDirty})
}

// 6. Usuário clica "Aplicar ao App"
applyObjectChanges() {
    // 7. Objeto com propriedades reordenadas é atualizado no selectedApp
    selectedApp.value.objects[idx] = editingObject.value;
    
    // 8. isDirty permanece true até salvar
}

// 9. Usuário clica "Salvar"
saveChanges() {
    // 10. Payload é enviado para API
    await $fetch(`/api/app/${payload.id}`, {
        method: 'PATCH',
        body: payload
    });
    
    // 11. isDirty é resetado
    isDirty.value = false;
    
    // 12. Apps são refreshed e estado sincronizado
}

Estilos e Animações

Classes CSS Personalizadas

/* Drag container */
.drag-list-move {
  transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Ghost state (elemento sendo arrastado) */
.ghost {
  opacity: 0.5;
  background: rgba(99, 102, 241, 0.1);
  border-color: rgba(99, 102, 241, 0.3);
}

/* Drag state (hover) */
.drag {
  opacity: 1;
  transform: rotate(2deg) scale(1.02);
  box-shadow: 0 5px 25px rgba(99, 102, 241, 0.3);
}

/* Handle cursor */
.drag-handle {
  cursor: grab;
}
.drag-handle:active {
  cursor: grabbing;
}

Configuração Vuedraggable

const dragOptions = computed(() => ({
  animation: 200,                    // Duração da animação (ms)
  group: "properties",               // Grupo para restricionar arraste
  ghostClass: "ghost",               // Classe do elemento sendo arrastado
  dragClass: "drag",                 // Classe durante arraste
  handle: ".drag-handle",            // Seletor do handle
  easing: "cubic-bezier(1, 0, 0, 1)" // Easing animation
}));

Considerações de Performance

  1. Computed Properties: Utilizadas para otimizar re-renders
  2. v-model Reativo: Mantém sincronização entre componente pai e filho
  3. Watch com Deep: Monitora mudanças profundas em propriedades
  4. Transition Groups: Aplica animações apenas quando necessário

Testes Recomendados

✓ Arrastar propriedade de cima para baixo
✓ Arrastar propriedade de baixo para cima
✓ Adicionar nova propriedade (deve aparecer no final)
✓ Remover propriedade (com confirmação)
✓ Editar nome/label durante reordenação
✓ isDirty deve ser true após qualquer mudança
✓ isDirty deve ser false após salvar
✓ Botão "Salvar" deve estar desabilitado quando isDirty=false
✓ Toast deve aparecer após reordenação
✓ Ordem deve persistir no banco após salvar

Backward Compatibility

Os componentes mantêm retrocompatibilidade:

  • DynamicList.vue: Funciona normalmente sem prop sortable (padrão: false)
  • Propriedades antigas do componente continuam funcionando
  • Eventos legados são mantidos
  • A reordenação é uma opção, não obrigatória

Próximas Melhorias (Sugestões)

  1. Persistência Automática: Auto-save após 2 segundos de inatividade
  2. Undo/Redo: Stack de mudanças para desfazer/refazer
  3. Vinculação a CRM: Sincronizar ordem com renderização de formulários em tempo real
  4. Histórico de Mudanças: Log de quem alterou e quando
  5. Validação Avançada: Verificar dependências entre campos antes de reordenar
  6. Temas Customizáveis: Cores e ícones configuráveis via props

Referências