578 lines
14 KiB
Markdown
578 lines
14 KiB
Markdown
# IA.md — Guia para IAs (consumidoras) do pacote `eli-vue`
|
|
|
|
Este arquivo existe para **IAs e automações** que precisam **importar e usar** o pacote `eli-vue` em projetos Vue 3.
|
|
|
|
> Fonte da verdade das regras internas do repositório: **`.agent`**.
|
|
> Este `IA.md` é focado no **uso do pacote como dependência** (projeto consumidor).
|
|
|
|
---
|
|
|
|
## O que é o `eli-vue`
|
|
|
|
Biblioteca (Design System) de componentes para **Vue 3** com **TypeScript** e integração com **Vuetify 3**.
|
|
|
|
### Premissas importantes
|
|
|
|
- O `eli-vue` **não cria** nem configura Vuetify no seu projeto.
|
|
- `vue` e `vuetify` são **peerDependencies**: o projeto consumidor precisa ter ambos instalados.
|
|
- O pacote utiliza internamente **lucide-vue-next** para ícones.
|
|
|
|
---
|
|
|
|
## Instalação (projeto consumidor)
|
|
|
|
> Preferência do repo: `pnpm`.
|
|
|
|
```bash
|
|
pnpm add eli-vue
|
|
```
|
|
|
|
Se o projeto ainda não tiver as peer dependencies, instale também:
|
|
|
|
```bash
|
|
pnpm add vue vuetify lucide-vue-next
|
|
```
|
|
|
|
> Nota: `lucide-vue-next` é recomendado para passar ícones compatíveis aos componentes (como `EliTabela`).
|
|
|
|
---
|
|
|
|
## Uso recomendado
|
|
|
|
### 1) Registro global via plugin (recomendado)
|
|
|
|
```ts
|
|
import { createApp } from "vue";
|
|
import App from "./App.vue";
|
|
|
|
// Vuetify
|
|
import "vuetify/styles";
|
|
import { createVuetify } from "vuetify";
|
|
|
|
// eli-vue
|
|
import EliVue from "eli-vue";
|
|
import "eli-vue/dist/eli-vue.css";
|
|
|
|
const vuetify = createVuetify({});
|
|
|
|
createApp(App)
|
|
.use(vuetify)
|
|
.use(EliVue)
|
|
.mount("#app");
|
|
```
|
|
|
|
> **IMPORTANTE**: O import do CSS (`import "eli-vue/dist/eli-vue.css";`) é **obrigatório**.
|
|
> O pacote utiliza **namespacing estático (BEM)** e não estilos scoped, portanto o arquivo CSS contém toda a estilização necessária.
|
|
|
|
### 2) Importação direta (quando não quiser plugin)
|
|
|
|
```ts
|
|
import {
|
|
EliBotao,
|
|
EliBadge,
|
|
EliCartao,
|
|
EliTabela,
|
|
EliEntradaTexto,
|
|
EliEntradaNumero,
|
|
EliEntradaDataHora,
|
|
EliEntradaParagrafo,
|
|
EliEntradaSelecao,
|
|
} from "eli-vue";
|
|
|
|
// OBRIGATÓRIO: Importar o CSS global
|
|
import "eli-vue/dist/eli-vue.css";
|
|
```
|
|
|
|
---
|
|
|
|
## Tipagem e Helpers
|
|
|
|
O pacote exporta tipos e funções utilitárias essenciais para o desenvolvimento com TypeScript.
|
|
|
|
```ts
|
|
import {
|
|
// Componentes
|
|
EliTabela,
|
|
|
|
// Helpers
|
|
celulaTabela,
|
|
|
|
// Tipos
|
|
tipoEliTabelaConsulta,
|
|
tipoEliColuna,
|
|
tipoEliTabelaAcao,
|
|
CartaoStatus // Tipos compartilhados
|
|
} from "eli-vue";
|
|
```
|
|
|
|
---
|
|
|
|
## Exemplos mínimos
|
|
|
|
### Botão
|
|
|
|
```vue
|
|
<template>
|
|
<EliBotao @click="salvar">Salvar</EliBotao>
|
|
</template>
|
|
```
|
|
|
|
### Entradas (EliEntrada*) com v-model
|
|
|
|
O `eli-vue` usa uma família de componentes `EliEntrada*` (em vez do antigo `EliInput`).
|
|
|
|
#### Texto
|
|
|
|
```vue
|
|
<template>
|
|
<EliEntradaTexto
|
|
v-model:value="nome"
|
|
:opcoes="{ rotulo: 'Nome', placeholder: 'Digite seu nome' }"
|
|
/>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent, ref } from "vue";
|
|
|
|
export default defineComponent({
|
|
setup() {
|
|
const nome = ref<string | null>("");
|
|
return { nome };
|
|
},
|
|
});
|
|
</script>
|
|
```
|
|
|
|
#### Parágrafo (textarea)
|
|
|
|
```vue
|
|
<template>
|
|
<EliEntradaParagrafo
|
|
v-model:value="descricao"
|
|
:opcoes="{ rotulo: 'Descrição', placeholder: 'Digite...', linhas: 5 }"
|
|
/>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent, ref } from "vue";
|
|
|
|
export default defineComponent({
|
|
setup() {
|
|
const descricao = ref<string | null>("");
|
|
return { descricao };
|
|
},
|
|
});
|
|
</script>
|
|
```
|
|
|
|
#### Seleção (select)
|
|
|
|
```vue
|
|
<template>
|
|
<EliEntradaSelecao
|
|
v-model:value="categoria"
|
|
:opcoes="{
|
|
rotulo: 'Categoria',
|
|
placeholder: 'Selecione...',
|
|
itens: async () => [
|
|
{ chave: 'a', rotulo: 'Categoria A' },
|
|
{ chave: 'b', rotulo: 'Categoria B' },
|
|
],
|
|
}"
|
|
/>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent, ref } from "vue";
|
|
|
|
export default defineComponent({
|
|
setup() {
|
|
const categoria = ref<string | null>(null);
|
|
return { categoria };
|
|
},
|
|
});
|
|
</script>
|
|
```
|
|
|
|
#### Texto com formato/máscara
|
|
|
|
> Regra importante: o `value` emitido é **sempre o texto formatado** (igual ao que aparece no input).
|
|
|
|
```vue
|
|
<template>
|
|
<EliEntradaTexto
|
|
v-model:value="documento"
|
|
:opcoes="{ rotulo: 'CPF/CNPJ', formato: 'cpfCnpj' }"
|
|
/>
|
|
|
|
<EliEntradaTexto
|
|
v-model:value="telefone"
|
|
:opcoes="{ rotulo: 'Telefone', formato: 'telefone' }"
|
|
/>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent, ref } from "vue";
|
|
export default defineComponent({
|
|
setup() {
|
|
const documento = ref<string | null>("");
|
|
const telefone = ref<string | null>("");
|
|
return { documento, telefone };
|
|
},
|
|
});
|
|
</script>
|
|
```
|
|
|
|
### Data e hora (entrada) com suporte a UTC/Z
|
|
|
|
```vue
|
|
<template>
|
|
<!-- Valor chega do backend em ISO 8601 (UTC/offset), e o componente exibe em horário local -->
|
|
<EliEntradaDataHora v-model:value="dataHora" :opcoes="{ rotulo: 'Agendamento' }" />
|
|
|
|
<!-- Somente data -->
|
|
<EliEntradaDataHora v-model:value="data" :opcoes="{ rotulo: 'Nascimento', modo: 'data' }" />
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent, ref } from "vue";
|
|
|
|
export default defineComponent({
|
|
setup() {
|
|
// Exemplo de valor ISO vindo do backend/banco
|
|
const dataHora = ref<string | null>("2026-01-09T16:15:00Z");
|
|
const data = ref<string | null>("2026-01-09T00:00:00-03:00");
|
|
return { dataHora, data };
|
|
},
|
|
});
|
|
</script>
|
|
```
|
|
|
|
---
|
|
|
|
## Outros Componentes
|
|
|
|
### EliBadge (Indicador)
|
|
|
|
Badge aprimorado que suporta raios predefinidos ("suave" | "pill").
|
|
|
|
```vue
|
|
<template>
|
|
<EliBadge :badge="3" color="error" radius="pill">
|
|
<v-icon>mdi-bell</v-icon>
|
|
</EliBadge>
|
|
</template>
|
|
```
|
|
|
|
### EliCartao
|
|
|
|
Cartão padronizado para representar itens de domínio (ex: propostas) com status coloridos automaticamente.
|
|
|
|
> **Importante**: O status deve ser um dos valores do tipo `CartaoStatus` ("novo" | "rascunho" | "vendido" | "cancelado").
|
|
|
|
```vue
|
|
<template>
|
|
<EliCartao
|
|
titulo="Proposta #123"
|
|
status="novo"
|
|
variant="outlined"
|
|
>
|
|
<!-- Conteúdo principal (default slot) -->
|
|
<div class="text-body-2">Cliente: ACME Corp</div>
|
|
<div class="text-caption text-medium-emphasis">Criado há 2 dias</div>
|
|
|
|
<!-- Ações (slot 'acoes') -->
|
|
<template #acoes>
|
|
<EliBotao variant="text" size="small">Ver detalhes</EliBotao>
|
|
</template>
|
|
</EliCartao>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent } from "vue";
|
|
// O tipo CartaoStatus é exportado para ajudar na tipagem
|
|
import type { CartaoStatus } from "eli-vue";
|
|
|
|
export default defineComponent({
|
|
setup() {
|
|
const statusAtual: CartaoStatus = "novo";
|
|
return { statusAtual };
|
|
}
|
|
});
|
|
</script>
|
|
```
|
|
|
|
---
|
|
|
|
## EliTabela (Tabela Avançada)
|
|
|
|
O componente `EliTabela` suporta ordenação, paginação, busca e **filtro avançado**.
|
|
Para type-safety, recomenda-se definir a estrutura da consulta usando `tipoEliTabelaConsulta<T>`.
|
|
|
|
### Barra de Busca e Filtros
|
|
|
|
O `EliTabela` oferece duas formas principais de filtrar dados:
|
|
1. **Busca Textual Simples**: Uma caixa de busca global.
|
|
2. **Filtro Avançado**: Um modal para composição de filtros específicos por coluna.
|
|
|
|
#### 1. Busca Textual Simples
|
|
|
|
Para habilitar, defina `mostrarCaixaDeBusca: true`.
|
|
O termo digitado será passado para a função `consulta` no parâmetro `texto_busca`.
|
|
|
|
```ts
|
|
mostrarCaixaDeBusca: true,
|
|
consulta: async ({ texto_busca }) => {
|
|
// Use 'texto_busca' para filtrar no backend (ex: ILIKE %texto%)
|
|
}
|
|
```
|
|
|
|
#### 2. Filtro Avançado
|
|
|
|
Permite que o usuário crie filtros complexos (ex: "Data > X" E "Status = Y").
|
|
Você define os campos disponíveis em `filtroAvancado`.
|
|
|
|
Cada definição de filtro precisa de:
|
|
- `rotulo`: Nome exibido ao usuário.
|
|
- `coluna`: Chave do dado (`keyof T`) a ser filtrada.
|
|
- `operador`: Operação padrão (ex: `like`, `=`, `>`, `<`, `>=`, `<=`, `!=`, `in`, `between`).
|
|
- `entrada`: Definição do componente de entrada (`EliEntrada*`) usado para capturar o valor.
|
|
|
|
> **Dica de Tipagem**: O campo `entrada` espera uma tupla `[Tipo, Opcoes]`. Devido à complexidade do TypeScript, pode ser necessário usar `as any` ou castar para `ComponenteEntrada`.
|
|
|
|
```ts
|
|
filtroAvancado: [
|
|
{
|
|
rotulo: "Nome do Cliente",
|
|
coluna: "nome", // chave em T
|
|
operador: "like",
|
|
// Configuração do input (igual ao usar EliEntradaTexto)
|
|
entrada: ["texto", { rotulo: "Digite o nome", placeholder: "Ex: João" }] as any,
|
|
},
|
|
{
|
|
rotulo: "Data de Criação",
|
|
coluna: "criado_em",
|
|
operador: ">=",
|
|
// Configuração do input (EliEntradaDataHora)
|
|
entrada: ["dataHora", { rotulo: "A partir de", modo: "data" }] as any,
|
|
},
|
|
{
|
|
rotulo: "Status",
|
|
coluna: "status",
|
|
operador: "=",
|
|
// Configuração do input com seleção
|
|
entrada: ["selecao", {
|
|
rotulo: "Selecione",
|
|
itens: () => [
|
|
{ chave: "ativo", rotulo: "Ativo" },
|
|
{ chave: "inativo", rotulo: "Inativo" }
|
|
]
|
|
}] as any
|
|
}
|
|
]
|
|
```
|
|
|
|
### Processando a Consulta
|
|
|
|
A função `consulta` recebe um objeto com todos os parâmetros necessários para buscar os dados corretamente.
|
|
|
|
```ts
|
|
consulta: async (params) => {
|
|
const {
|
|
offSet, // Paginação: pular N registros
|
|
limit, // Paginação: limite por página
|
|
texto_busca, // Busca simples (string | undefined)
|
|
coluna_ordem, // Ordenação: qual coluna
|
|
direcao_ordem, // Ordenação: "asc" | "desc"
|
|
filtros // Array de filtros aplicados pelo usuário
|
|
} = params;
|
|
|
|
// Exemplo de estrutura de 'filtros':
|
|
// [
|
|
// { coluna: "nome", operador: "like", valor: "Ana" },
|
|
// { coluna: "criado_em", operador: ">=", valor: "2024-01-01" }
|
|
// ]
|
|
|
|
console.log("Buscando dados...", params);
|
|
|
|
// ... chamada à API ...
|
|
|
|
return {
|
|
cod: 0,
|
|
eCerto: true,
|
|
eErro: false,
|
|
mensagem: undefined,
|
|
valor: {
|
|
// Retorne SEMPRE o total real de registros para a paginação funcionar
|
|
quantidade: 100,
|
|
valores: [ /* ... array de T ... */ ]
|
|
},
|
|
};
|
|
},
|
|
```
|
|
|
|
### Exemplo Completo Tipado
|
|
|
|
```ts
|
|
import { defineComponent } from "vue";
|
|
import { EliTabela, celulaTabela } from "eli-vue";
|
|
import type { tipoEliTabelaConsulta } from "eli-vue";
|
|
// Tipos auxiliares (opcionais, mas úteis)
|
|
import { BadgeCheck, Pencil } from "lucide-vue-next";
|
|
|
|
// 1. Defina o tipo da linha
|
|
type Usuario = {
|
|
id: number;
|
|
nome: string;
|
|
documento: string;
|
|
email: string;
|
|
ativo: boolean;
|
|
criado_em: string;
|
|
};
|
|
|
|
// 2. Defina a configuração da tabela
|
|
const tabelaUsuarios: tipoEliTabelaConsulta<Usuario> = {
|
|
nome: "Usuarios",
|
|
mostrarCaixaDeBusca: true,
|
|
registros_por_consulta: 10,
|
|
|
|
// Colunas Visuais
|
|
colunas: [
|
|
{
|
|
rotulo: "Nome",
|
|
celula: (row) => celulaTabela("textoSimples", { texto: row.nome }),
|
|
visivel: true,
|
|
coluna_ordem: "nome" // Habilita ordenação por esta coluna
|
|
},
|
|
{
|
|
rotulo: "Status",
|
|
visivel: true,
|
|
celula: (row) => celulaTabela("tags", {
|
|
opcoes: [
|
|
row.ativo
|
|
? { rotulo: "Ativo", cor: "success", icone: BadgeCheck }
|
|
: { rotulo: "Inativo", cor: "error" }
|
|
]
|
|
})
|
|
},
|
|
{
|
|
rotulo: "Criado em",
|
|
visivel: true,
|
|
celula: (row) => celulaTabela("data", {
|
|
valor: row.criado_em,
|
|
formato: "data_hora"
|
|
})
|
|
}
|
|
],
|
|
|
|
// Ações de linha (botões à direita)
|
|
acoesLinha: [
|
|
{
|
|
rotulo: "Editar",
|
|
icone: Pencil,
|
|
cor: "primary",
|
|
acao: (row) => console.log("Editar", row.id)
|
|
}
|
|
],
|
|
|
|
// Definição dos Filtros Avançados disponíveis
|
|
filtroAvancado: [
|
|
{
|
|
rotulo: "Documento",
|
|
coluna: "documento",
|
|
operador: "like",
|
|
entrada: ["texto", { rotulo: "Documento", formato: "cpfCnpj" }] as any,
|
|
},
|
|
{
|
|
rotulo: "Criado em (Início)",
|
|
coluna: "criado_em",
|
|
operador: ">=",
|
|
entrada: ["dataHora", { rotulo: "A partir de", modo: "data" }] as any
|
|
}
|
|
],
|
|
|
|
// Função de consulta
|
|
consulta: async (params) => {
|
|
// Aqui você conectaria com sua API, passando params.filtros, params.texto_busca, etc.
|
|
console.log("Filtros aplicados:", params.filtros);
|
|
|
|
return {
|
|
cod: 0,
|
|
eCerto: true,
|
|
eErro: false,
|
|
mensagem: undefined,
|
|
valor: {
|
|
quantidade: 1,
|
|
valores: [
|
|
{ id: 1, nome: "Luiz", documento: "123", email: "a@a.com", ativo: true, criado_em: "2024-01-01" }
|
|
]
|
|
},
|
|
};
|
|
},
|
|
};
|
|
```
|
|
|
|
### Tipos de Células (`celulaTabela`)
|
|
|
|
Helper: `celulaTabela(tipo, dados)`
|
|
|
|
- `textoSimples`: `{ texto: string; acao?: () => void }`
|
|
- `textoTruncado`: `{ texto: string; acao?: () => void }`
|
|
- `numero`: `{ numero: number; prefixo?: string; sufixo?: string; acao?: () => void }`
|
|
- `tags`: `{ opcoes: { rotulo: string; cor?: string; icone?: LucideIcon; acao?: () => void }[] }`
|
|
- `data`: `{ valor: string; formato: "data" | "data_hora" | "relativo"; acao?: () => void }`
|
|
|
|
---
|
|
|
|
## Troubleshooting (para IAs)
|
|
|
|
### 1) “Failed to resolve component”
|
|
|
|
Provável causa: componente não foi registrado.
|
|
- Se estiver usando plugin: confirme `app.use(EliVue)`
|
|
- Se estiver importando direto: registre localmente no componente
|
|
|
|
### 2) Estilos não aplicam ou componentes "quebrados"
|
|
|
|
**Causa provável**: O CSS do pacote não foi importado.
|
|
|
|
O `eli-vue` removeu o uso de `<style scoped>` para evitar problemas de hidratação e compatibilidade entre builds.
|
|
Todo o estilo reside em arquivos CSS estáticos que precisam ser carregados.
|
|
|
|
**Solução**:
|
|
Adicione no seu `main.ts` ou componente raiz:
|
|
|
|
```ts
|
|
import "eli-vue/dist/eli-vue.css";
|
|
```
|
|
|
|
### 3) Erro de tipo em `celulaTabela`
|
|
|
|
Verifique se você importou `celulaTabela` de `eli-vue`.
|
|
Certifique-se de que o segundo argumento corresponde ao tipo da célula (ex: `textoSimples` pede `{ texto: string }`).
|
|
|
|
### 4) Ícones não aparecem
|
|
|
|
O `eli-vue` espera ícones do pacote `lucide-vue-next` (ou compatíveis com Component type do Vue).
|
|
Certifique-se de passar o componente do ícone (ex: `Pencil`), e não string.
|
|
|
|
---
|
|
|
|
## Como a IA deve agir ao modificar um projeto consumidor
|
|
|
|
Quando for integrar `eli-vue` num projeto existente:
|
|
|
|
1) Verifique se **Vue 3**, **Vuetify 3** e **lucide-vue-next** estão instalados.
|
|
2) Prefira usar o **plugin** do `eli-vue` (simplifica registro global).
|
|
3) Garanta o import do CSS do pacote (`eli-vue/dist/eli-vue.css`).
|
|
4) Use `celulaTabela` para construir colunas de tabelas de forma tipada.
|
|
5) Ao definir tabelas, use `tipoEliTabelaConsulta<T>` para garantir que colunas e filtros batam com o tipo de dados.
|
|
|
|
---
|
|
|
|
## Quando este arquivo deve ser atualizado
|
|
|
|
Atualize este `IA.md` quando houver mudanças em:
|
|
- Estrutura de exports (novos componentes ou helpers).
|
|
- Tipagem das células ou entradas.
|
|
- Dependências obrigatórias.
|