From 1446d6225b5c53ea812ceb652fcfff4d3f1c83a1 Mon Sep 17 00:00:00 2001 From: Luiz Silva Date: Mon, 1 Jun 2026 13:13:32 -0300 Subject: [PATCH] ajustes na tabela de consulta --- .agent/atualizar-agente.global.sh | 26 +- .agent/rules/autonomia-agente.global.md | 60 + .../carta-principios-agente-azteca.global.md | 3 +- .agent/rules/escrita-natural.global.md | 12 +- .agent/rules/estrutura-vue.global.md | 86 + .agent/rules/fronteiras-monorepo.global.md | 120 + .agent/rules/jsdoc-padrao.global.md | 59 + .agent/rules/limite-linhas.global.md | 66 + .agent/rules/mensagens-usuario.global.md | 123 + .agent/rules/regras-negocio-md.global.md | 120 + .agent/rules/typescript-sem-any.global.md | 8 +- .agent/rules/zod-validacao-padrao.global.md | 315 + .../skills/criar-schema-zod.global/SKILL.md | 184 + .../diagnosticar-erros-build.global/SKILL.md | 5 +- .../SKILL.md | 3 +- .../documentar-dominio-modulo.global/SKILL.md | 131 + .../documentar-regras-negocio.global/SKILL.md | 8 +- .../mapear-codigo-fonte.global/SKILL.md | 3 +- .agent/tools.md | 2 +- .agent/verificar-linhas.sh | 41 + .agent/workflows/adicionar-ajuda.global.md | 4 + .agent/workflows/aprimorar-IA.global.md | 117 +- .agent/workflows/atualizar-agente.global.md | 4 + .agent/workflows/comercial.global.md | 6 +- .agent/workflows/criar-fluxograma.global.md | 8 +- .../documentar-regras-negocio.global.md | 6 +- .agent/workflows/marketing.global.md | 4 + .agent/workflows/orcamento.global.md | 4 + .agent/workflows/otimizar-codigos.global.md | 237 +- .agent/workflows/reels.global.md | 4 + .agent/workflows/ux.global.md | 44 +- dist/eli-vue.es.js | 6271 ++++---- dist/eli-vue.umd.js | 104 +- .../EliEntrada/EliEntradaConfirmacao.vue.d.ts | 1361 ++ .../EliEntrada/EliEntradaDataHora.vue.d.ts | 8 +- .../EliEntrada/EliEntradaNumero.vue.d.ts | 4 +- .../EliEntrada/EliEntradaParagrafo.vue.d.ts | 6 +- .../EliEntrada/EliEntradaSelecao.vue.d.ts | 10 +- .../EliEntradaSelecaoMultipla.vue.d.ts | 10024 +++++++++++++ .../EliEntrada/EliEntradaTexto.vue.d.ts | 4 +- dist/types/componentes/EliEntrada/index.d.ts | 4 +- .../EliEntrada/registryEliEntradas.d.ts | 12030 +--------------- .../componentes/EliEntrada/tiposEntradas.d.ts | 39 + .../componentes/EliTabela/EliTabela.vue.d.ts | 423 +- .../EliTabela/EliTabelaCabecalho.vue.d.ts | 27 + .../EliTabelaModalFiltroAvancado.vue.d.ts | 392 +- .../EliTabela/types-eli-tabela.d.ts | 6 +- .../componentes/EliTabela/useEliTabela.d.ts | 4 + .../useEliTabelaFiltrosAvancados.d.ts | 4 + .../EliTabela/useFiltroAvancado.d.ts | 394 +- .../ola_mundo/EliOlaMundo.vue.d.ts | 4 +- package.json | 8 +- pnpm-lock.yaml | 30 +- scripts/check-linhas.sh | 49 - scripts/check-vue-linhas.sh | 59 - .../EliEntrada/EliEntradaConfirmacao.vue | 62 + .../EliEntrada/EliEntradaSelecaoMultipla.vue | 95 + src/componentes/EliEntrada/index.ts | 4 + .../EliEntrada/registryEliEntradas.ts | 8 +- src/componentes/EliEntrada/tiposEntradas.ts | 54 + src/componentes/EliTabela/EliTabela.css | 281 +- src/componentes/EliTabela/EliTabela.vue | 1 + src/componentes/EliTabela/EliTabelaAcoes.css | 207 + .../EliTabela/EliTabelaCabecalho.vue | 46 +- .../EliTabela/EliTabelaModalColunas.css | 180 +- .../EliTabela/EliTabelaModalColunasItens.css | 182 + .../EliTabelaModalFiltroAvancado.css | 226 +- .../EliTabela/EliTabelaModalFiltroFormula.css | 225 + .../EliTabela/EliTabelaSelecao.css | 66 + src/componentes/EliTabela/types-eli-tabela.ts | 14 +- .../EliTabela/useEliTabelaFiltrosAvancados.ts | 112 +- .../EliTabela/useFiltroAvancado.ts | 21 +- src/playground/api_simulador/banco.ts | 33 +- src/playground/api_simulador/dados.ts | 251 +- src/playground/api_simulador/dadosSeed.ts | 249 + src/playground/api_simulador/index.ts | 1 + src/playground/useTabelaPlayground.ts | 2 + src/playground/useTabelaPlaygroundConfig.ts | 103 +- 78 files changed, 17921 insertions(+), 17580 deletions(-) create mode 100644 .agent/rules/autonomia-agente.global.md create mode 100644 .agent/rules/estrutura-vue.global.md create mode 100644 .agent/rules/fronteiras-monorepo.global.md create mode 100644 .agent/rules/jsdoc-padrao.global.md create mode 100644 .agent/rules/limite-linhas.global.md create mode 100644 .agent/rules/mensagens-usuario.global.md create mode 100644 .agent/rules/regras-negocio-md.global.md create mode 100644 .agent/rules/zod-validacao-padrao.global.md create mode 100644 .agent/skills/criar-schema-zod.global/SKILL.md create mode 100644 .agent/skills/documentar-dominio-modulo.global/SKILL.md create mode 100644 .agent/verificar-linhas.sh create mode 100644 dist/types/componentes/EliEntrada/EliEntradaConfirmacao.vue.d.ts create mode 100644 dist/types/componentes/EliEntrada/EliEntradaSelecaoMultipla.vue.d.ts delete mode 100755 scripts/check-linhas.sh delete mode 100755 scripts/check-vue-linhas.sh create mode 100644 src/componentes/EliEntrada/EliEntradaConfirmacao.vue create mode 100644 src/componentes/EliEntrada/EliEntradaSelecaoMultipla.vue create mode 100644 src/componentes/EliTabela/EliTabelaAcoes.css create mode 100644 src/componentes/EliTabela/EliTabelaModalColunasItens.css create mode 100644 src/componentes/EliTabela/EliTabelaModalFiltroFormula.css create mode 100644 src/componentes/EliTabela/EliTabelaSelecao.css create mode 100644 src/playground/api_simulador/dadosSeed.ts diff --git a/.agent/atualizar-agente.global.sh b/.agent/atualizar-agente.global.sh index b4707bf..52978aa 100755 --- a/.agent/atualizar-agente.global.sh +++ b/.agent/atualizar-agente.global.sh @@ -5,13 +5,25 @@ BASE="https://paiol.idz.one/desenvolvimento/agent-ia" ERROS=0 baixar() { + TMP=$(mktemp) echo "baixando $1" - ERRO_CURL="$(curl -sSfL --create-dirs "$1" -o "$2" 2>&1)" || { + ERRO_CURL="$(curl -sSfL --create-dirs "$1" -o "$TMP" 2>&1)" || { echo " ⚠ $1" echo " $ERRO_CURL" + rm -f "$TMP" ERROS=$((ERROS + 1)) return 1 } + # Rejeitar arquivos menores que 50 bytes (prováveis erros de proxy/WAF) + TAMANHO=$(wc -c < "$TMP" | tr -d ' ') + if [ "$TAMANHO" -lt 50 ]; then + echo " ⚠ $1 — arquivo muito pequeno ($TAMANHO bytes), ignorado" + rm -f "$TMP" + ERROS=$((ERROS + 1)) + return 1 + fi + mkdir -p "$(dirname "$2")" + mv "$TMP" "$2" } # ignorar se não auto atualizar @@ -27,7 +39,14 @@ fi baixar "$BASE/rules/carta-principios-agente-azteca.global.md" "$DIR/rules/carta-principios-agente-azteca.global.md" baixar "$BASE/rules/escrita-natural.global.md" "$DIR/rules/escrita-natural.global.md" baixar "$BASE/rules/typescript-sem-any.global.md" "$DIR/rules/typescript-sem-any.global.md" -baixar "$BASE/rules/regras_negocio_md.global.md" "$DIR/rules/regras_negocio_md.global.md" +baixar "$BASE/rules/regras-negocio-md.global.md" "$DIR/rules/regras-negocio-md.global.md" +baixar "$BASE/rules/zod-validacao-padrao.global.md" "$DIR/rules/zod-validacao-padrao.global.md" +baixar "$BASE/rules/limite-linhas.global.md" "$DIR/rules/limite-linhas.global.md" +baixar "$BASE/rules/estrutura-vue.global.md" "$DIR/rules/estrutura-vue.global.md" +baixar "$BASE/rules/jsdoc-padrao.global.md" "$DIR/rules/jsdoc-padrao.global.md" +baixar "$BASE/rules/autonomia-agente.global.md" "$DIR/rules/autonomia-agente.global.md" +baixar "$BASE/rules/fronteiras-monorepo.global.md" "$DIR/rules/fronteiras-monorepo.global.md" +baixar "$BASE/rules/mensagens-usuario.global.md" "$DIR/rules/mensagens-usuario.global.md" # workflows baixar "$BASE/workflows/aprimorar-IA.global.md" "$DIR/workflows/aprimorar-IA.global.md" @@ -48,8 +67,11 @@ baixar "$BASE/skills/mapear-codigo-fonte.global/SKILL.md" "$DIR/sk baixar "$BASE/skills/diagnosticar-erros-build.global/SKILL.md" "$DIR/skills/diagnosticar-erros-build.global/SKILL.md" baixar "$BASE/skills/documentar-decisao-tecnica.global/SKILL.md" "$DIR/skills/documentar-decisao-tecnica.global/SKILL.md" baixar "$BASE/skills/documentar-regras-negocio.global/SKILL.md" "$DIR/skills/documentar-regras-negocio.global/SKILL.md" +baixar "$BASE/skills/criar-schema-zod.global/SKILL.md" "$DIR/skills/criar-schema-zod.global/SKILL.md" +baixar "$BASE/skills/documentar-dominio-modulo.global/SKILL.md" "$DIR/skills/documentar-dominio-modulo.global/SKILL.md" # raiz (.agent) baixar "$BASE/tools.md" "$DIR/tools.md" +baixar "$BASE/verificar-linhas.sh" "$DIR/verificar-linhas.sh" exit "$ERROS" \ No newline at end of file diff --git a/.agent/rules/autonomia-agente.global.md b/.agent/rules/autonomia-agente.global.md new file mode 100644 index 0000000..91b1517 --- /dev/null +++ b/.agent/rules/autonomia-agente.global.md @@ -0,0 +1,60 @@ +--- +type: rule +name: autonomia-agente +description: Define limites de autonomia do agente — o que pode fazer sozinho, o que requer confirmação e o que é proibido +scope: global +trigger: always_on +priority: alta +updatedAt: "2026-06-01" +--- + +# Regra: Limites de Autonomia do Agente + +## Contexto + +O agente existe para **ampliar a capacidade humana**, não para substituir decisões. Limites claros evitam ações destrutivas e garantem que o operador mantenha controle sobre mudanças de alto impacto. + +## Ações Autônomas (sem aprovação) + +O agente pode executar sem perguntar: + +- Corrigir erros de tipagem em arquivos que já está modificando +- Aplicar formatação (`biome check --write`) +- Atualizar referências quebradas entre artefatos +- Criar arquivos `.regras-negocio.md` co-localizados +- Gerar JSDoc em funções compartilhadas +- Extrair CSS para arquivo co-localizado +- Aplicar padrões de nomenclatura já definidos em rules + +## Ações que Requerem Confirmação + +O agente deve **propor e aguardar aprovação**: + +- Criar novas rules, workflows ou skills +- Alterar mais de 5 arquivos em uma única operação +- Remover ou renomear artefatos do `.agent/` +- Mudar contratos de API (parâmetros, retornos, rotas) +- Alterar schemas Zod compartilhados no pacote `*-comuns` +- Propor novas dependências ao `package.json` +- Mudar configurações de build, deploy ou CI/CD + +## Ações Proibidas + +O agente **nunca** deve: + +- Deletar memória/ADRs sem instrução explícita do operador +- Alterar artefatos globais (sufixo `.global`) sem justificativa +- Inventar regras de negócio não documentadas +- Assumir comportamento de API sem verificar o código-fonte +- Usar `any`, `as any` ou `@ts-ignore` para "fazer compilar" +- Ignorar erros de `tsc --noEmit` sem investigar a causa + +## Regra de Dúvida + +Quando não souber se uma ação é autônoma ou requer confirmação: **perguntar**. + +O custo de uma pergunta é zero. O custo de uma ação destrutiva é alto. + +## Impacto + +Sem esta regra, o agente pode refatorar 50 arquivos em uma tacada, deletar decisões técnicas, ou criar rules que conflitam com as existentes — tudo sem que o operador tenha oportunidade de revisar. diff --git a/.agent/rules/carta-principios-agente-azteca.global.md b/.agent/rules/carta-principios-agente-azteca.global.md index 3929246..4a9b20e 100644 --- a/.agent/rules/carta-principios-agente-azteca.global.md +++ b/.agent/rules/carta-principios-agente-azteca.global.md @@ -3,9 +3,10 @@ trigger: always_on type: rule name: carta-principios-agente-azteca description: Princípios morais e operacionais que orientam o comportamento de agentes de IA no ecossistema Azteca -version: 1.0 +version: "1.0" scope: global priority: alta +updatedAt: "2026-06-01" --- # Carta de Princípios do Agente de IA da Azteca diff --git a/.agent/rules/escrita-natural.global.md b/.agent/rules/escrita-natural.global.md index 1e8a175..2c92584 100644 --- a/.agent/rules/escrita-natural.global.md +++ b/.agent/rules/escrita-natural.global.md @@ -1,6 +1,14 @@ --- -trigger: always_on -description: Garantir escrita natural, fluida e humana em textos técnicos e comerciais, evitando linguagem robótica ou excessivamente genérica. +type: rule +name: escrita-natural +trigger: context_match +description: Garantir escrita natural, fluida e humana em textos comerciais e de comunicação, evitando linguagem robótica ou excessivamente genérica. +scope: global +updatedAt: "2026-06-01" +ativacao: > + Aplicar quando o agente estiver produzindo textos comerciais, de marketing, + orçamentos, propostas, reels ou qualquer comunicação voltada ao público externo. + NÃO aplicar em documentação técnica (ADRs, rules, JSDoc, regras de negócio). --- # Regra: Escrita Estratégica e Natural diff --git a/.agent/rules/estrutura-vue.global.md b/.agent/rules/estrutura-vue.global.md new file mode 100644 index 0000000..de0a4cf --- /dev/null +++ b/.agent/rules/estrutura-vue.global.md @@ -0,0 +1,86 @@ +--- +type: rule +name: estrutura-vue +description: Padrões obrigatórios de estrutura para componentes Vue 3 (defineComponent, setup, CSS, dispatch) +scope: global +trigger: file_match +globs: + - "**/*.vue" +updatedAt: "2026-06-01" +--- + +# Regra: Estrutura de Componentes Vue + +## 1. `defineComponent` obrigatório + +Todo componente Vue deve usar `defineComponent`: + +```vue + +``` + +## 2. `setup()` não pode ser externo + +O `setup()` deve estar sempre dentro do `.vue`. Nunca delegar para arquivo externo. + +```ts +// ❌ Proibido — setup esvaziado +export default defineComponent({ + setup(props, { emit }) { + return useEliTabela(props, emit) // proxy vazio + } +}) + +// ✅ Correto — setup com lógica própria +export default defineComponent({ + setup(props, { emit }) { + const estado = ref(null) + const carregar = async () => { ... } + return { estado, carregar } + } +}) +``` + +> Composables `use*.ts` podem encapsular lógica reutilizável pontual, mas nunca substituir o `setup()`. Se ultrapassar 250 linhas → extrair subcomponentes, CSS ou Dispatch Table. + +## 3. CSS co-localizado + +Separação de estilos em arquivo externo quando o CSS contribui para o total de linhas: + +``` +MeuComponente.vue +MeuComponente.css +``` + +Importar via `import './MeuComponente.css'` ou ` diff --git a/src/componentes/EliTabela/EliTabelaModalColunas.css b/src/componentes/EliTabela/EliTabelaModalColunas.css index 4069069..f3717d8 100644 --- a/src/componentes/EliTabela/EliTabelaModalColunas.css +++ b/src/componentes/EliTabela/EliTabelaModalColunas.css @@ -1,3 +1,5 @@ +@import "./EliTabelaModalColunasItens.css"; + .eli-tabela-modal-colunas__overlay { position: fixed; inset: 0; @@ -162,181 +164,3 @@ .eli-tabela-modal-colunas__lista--invisiveis { background: var(--cor-eli-fundo); } - -/* ── Item ── */ -.eli-tabela-modal-colunas__item { - display: flex; - align-items: center; - gap: 8px; - padding: 9px 10px; - border-radius: 8px; - border: 1px solid var(--cor-eli-borda); - background: var(--cor-eli-superficie); - cursor: grab; - user-select: none; - transition: box-shadow 0.15s ease, opacity 0.15s ease, border-color 0.15s ease; -} - -.eli-tabela-modal-colunas__item + .eli-tabela-modal-colunas__item { - margin-top: 6px; -} - -.eli-tabela-modal-colunas__item:hover { - border-color: var(--cor-eli-borda-forte); - box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08); -} - -.eli-tabela-modal-colunas__item:active { - cursor: grabbing; -} - -.eli-tabela-modal-colunas__item--invisivel { - opacity: 0.72; -} - -.eli-tabela-modal-colunas__item--arrastando { - opacity: 0.15; - border-style: dashed; - border-color: var(--cor-eli-primaria-media); -} - -/* ── Partes do item ── */ -.eli-tabela-modal-colunas__item-handle { - color: var(--cor-eli-borda-forte); - flex-shrink: 0; - transition: color 0.15s ease; -} - -.eli-tabela-modal-colunas__item:hover .eli-tabela-modal-colunas__item-handle { - color: var(--cor-eli-neutro); -} - -.eli-tabela-modal-colunas__item-texto { - flex: 1; - min-width: 0; - font-size: 0.875rem; - color: var(--cor-eli-neutro-escuro); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.eli-tabela-modal-colunas__item-badge-icone { - flex-shrink: 0; -} - -.eli-tabela-modal-colunas__item-badge-icone--visivel { - color: var(--cor-eli-primaria-acao); -} - -.eli-tabela-modal-colunas__item-badge-icone--invisivel { - color: var(--cor-eli-borda-forte); -} - -/* ── Estado vazio ── */ -.eli-tabela-modal-colunas__vazio { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 8px; - padding: 24px 12px; - text-align: center; - font-size: 0.8rem; - color: var(--cor-eli-neutro); - line-height: 1.5; -} - -.eli-tabela-modal-colunas__vazio-icone { - color: var(--cor-eli-borda-forte); -} - -.eli-tabela-modal-colunas__vazio-icone--ok { - color: var(--cor-eli-primaria-acao); -} - -/* ── Animações TransitionGroup ── */ -.list-move, -.list-enter-active, -.list-leave-active { - transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); -} - -.list-enter-from, -.list-leave-to { - opacity: 0; - transform: scale(0.95); -} - -.list-leave-active { - position: absolute; - width: calc(100% - 16px); -} - -/* ── Footer ── */ -.eli-tabela-modal-colunas__footer { - display: flex; - justify-content: flex-end; - gap: 8px; - padding: 12px 16px; - border-top: 1px solid var(--cor-eli-borda); - background: var(--cor-eli-fundo); -} - -.eli-tabela-modal-colunas__botao { - display: inline-flex; - align-items: center; - gap: 6px; - height: 34px; - padding: 0 16px; - border-radius: 8px; - border: 1px solid var(--cor-eli-borda); - background: var(--cor-eli-superficie); - cursor: pointer; - font-size: 0.875rem; - font-weight: 500; - transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease; -} - -.eli-tabela-modal-colunas__botao--sec { - color: var(--cor-eli-neutro-escuro); -} - -.eli-tabela-modal-colunas__botao--sec:hover, -.eli-tabela-modal-colunas__botao--sec:focus-visible { - background: var(--cor-eli-fundo); - border-color: var(--cor-eli-borda-forte); -} - -.eli-tabela-modal-colunas__botao--sec:focus-visible { - outline: 2px solid var(--cor-eli-primaria-clara); - outline-offset: 2px; -} - -.eli-tabela-modal-colunas__botao--prim { - border: none; - background: var(--cor-eli-primaria); - color: #fff; -} - -.eli-tabela-modal-colunas__botao--prim:hover, -.eli-tabela-modal-colunas__botao--prim:focus-visible { - background: var(--cor-eli-primaria-escura); -} - -.eli-tabela-modal-colunas__botao--prim:focus-visible { - outline: 2px solid var(--cor-eli-primaria-clara); - outline-offset: 2px; -} - -/* ── Responsivo ── */ -@media (max-width: 720px) { - .eli-tabela-modal-colunas__conteudo { - grid-template-columns: 1fr; - } - - .eli-tabela-modal-colunas__coluna + .eli-tabela-modal-colunas__coluna { - border-left: none; - border-top: 1px solid var(--cor-eli-borda); - } -} diff --git a/src/componentes/EliTabela/EliTabelaModalColunasItens.css b/src/componentes/EliTabela/EliTabelaModalColunasItens.css new file mode 100644 index 0000000..1578638 --- /dev/null +++ b/src/componentes/EliTabela/EliTabelaModalColunasItens.css @@ -0,0 +1,182 @@ +/* ========================= + * EliTabelaModalColunas — Itens, estados e footer + * Extraído de EliTabelaModalColunas.css para manter cada arquivo abaixo de 250 linhas. + * ========================= */ + +/* ── Item ── */ +.eli-tabela-modal-colunas__item { + display: flex; + align-items: center; + gap: 8px; + padding: 9px 10px; + border-radius: 8px; + border: 1px solid var(--cor-eli-borda); + background: var(--cor-eli-superficie); + cursor: grab; + user-select: none; + transition: box-shadow 0.15s ease, opacity 0.15s ease, border-color 0.15s ease; +} + +.eli-tabela-modal-colunas__item + .eli-tabela-modal-colunas__item { + margin-top: 6px; +} + +.eli-tabela-modal-colunas__item:hover { + border-color: var(--cor-eli-borda-forte); + box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08); +} + +.eli-tabela-modal-colunas__item:active { + cursor: grabbing; +} + +.eli-tabela-modal-colunas__item--invisivel { + opacity: 0.72; +} + +.eli-tabela-modal-colunas__item--arrastando { + opacity: 0.15; + border-style: dashed; + border-color: var(--cor-eli-primaria-media); +} + +/* ── Partes do item ── */ +.eli-tabela-modal-colunas__item-handle { + color: var(--cor-eli-borda-forte); + flex-shrink: 0; + transition: color 0.15s ease; +} + +.eli-tabela-modal-colunas__item:hover .eli-tabela-modal-colunas__item-handle { + color: var(--cor-eli-neutro); +} + +.eli-tabela-modal-colunas__item-texto { + flex: 1; + min-width: 0; + font-size: 0.875rem; + color: var(--cor-eli-neutro-escuro); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.eli-tabela-modal-colunas__item-badge-icone { + flex-shrink: 0; +} + +.eli-tabela-modal-colunas__item-badge-icone--visivel { + color: var(--cor-eli-primaria-acao); +} + +.eli-tabela-modal-colunas__item-badge-icone--invisivel { + color: var(--cor-eli-borda-forte); +} + +/* ── Estado vazio ── */ +.eli-tabela-modal-colunas__vazio { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + padding: 24px 12px; + text-align: center; + font-size: 0.8rem; + color: var(--cor-eli-neutro); + line-height: 1.5; +} + +.eli-tabela-modal-colunas__vazio-icone { + color: var(--cor-eli-borda-forte); +} + +.eli-tabela-modal-colunas__vazio-icone--ok { + color: var(--cor-eli-primaria-acao); +} + +/* ── Animações TransitionGroup ── */ +.list-move, +.list-enter-active, +.list-leave-active { + transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); +} + +.list-enter-from, +.list-leave-to { + opacity: 0; + transform: scale(0.95); +} + +.list-leave-active { + position: absolute; + width: calc(100% - 16px); +} + +/* ── Footer ── */ +.eli-tabela-modal-colunas__footer { + display: flex; + justify-content: flex-end; + gap: 8px; + padding: 12px 16px; + border-top: 1px solid var(--cor-eli-borda); + background: var(--cor-eli-fundo); +} + +.eli-tabela-modal-colunas__botao { + display: inline-flex; + align-items: center; + gap: 6px; + height: 34px; + padding: 0 16px; + border-radius: 8px; + border: 1px solid var(--cor-eli-borda); + background: var(--cor-eli-superficie); + cursor: pointer; + font-size: 0.875rem; + font-weight: 500; + transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease; +} + +.eli-tabela-modal-colunas__botao--sec { + color: var(--cor-eli-neutro-escuro); +} + +.eli-tabela-modal-colunas__botao--sec:hover, +.eli-tabela-modal-colunas__botao--sec:focus-visible { + background: var(--cor-eli-fundo); + border-color: var(--cor-eli-borda-forte); +} + +.eli-tabela-modal-colunas__botao--sec:focus-visible { + outline: 2px solid var(--cor-eli-primaria-clara); + outline-offset: 2px; +} + +.eli-tabela-modal-colunas__botao--prim { + border: none; + background: var(--cor-eli-primaria); + color: #fff; +} + +.eli-tabela-modal-colunas__botao--prim:hover, +.eli-tabela-modal-colunas__botao--prim:focus-visible { + background: var(--cor-eli-primaria-escura); +} + +.eli-tabela-modal-colunas__botao--prim:focus-visible { + outline: 2px solid var(--cor-eli-primaria-clara); + outline-offset: 2px; +} + +/* ── Responsivo ── */ +@media (max-width: 720px) { + .eli-tabela-modal-colunas__conteudo { + grid-template-columns: 1fr; + } + + .eli-tabela-modal-colunas__coluna + .eli-tabela-modal-colunas__coluna { + border-left: none; + border-top: 1px solid var(--cor-eli-borda); + } +} diff --git a/src/componentes/EliTabela/EliTabelaModalFiltroAvancado.css b/src/componentes/EliTabela/EliTabelaModalFiltroAvancado.css index 4965b6a..f821609 100644 --- a/src/componentes/EliTabela/EliTabelaModalFiltroAvancado.css +++ b/src/componentes/EliTabela/EliTabelaModalFiltroAvancado.css @@ -1,3 +1,5 @@ +@import "./EliTabelaModalFiltroFormula.css"; + .eli-tabela-modal-filtro__overlay { position: fixed; inset: 0; @@ -217,227 +219,3 @@ outline: none; border-color: var(--cor-eli-primaria); } - -/* ── Fórmula de combinação ───────────────────── */ -.eli-tabela-modal-filtro__formula-container { - display: flex; - flex-direction: row; - align-items: center; - gap: 8px; - margin-left: auto; -} - -.eli-tabela-modal-filtro__formula-label { - font-size: 0.82rem; - font-weight: 500; - color: rgba(15, 23, 42, 0.55); - white-space: nowrap; - flex-shrink: 0; -} - - - -.eli-tabela-modal-filtro__formula-ajuda { - position: relative; - display: inline-flex; - align-items: center; - color: rgba(15, 23, 42, 0.35); - cursor: help; - transition: color 0.15s; -} - -.eli-tabela-modal-filtro__formula-ajuda:hover, -.eli-tabela-modal-filtro__formula-ajuda:focus-visible { - color: var(--cor-eli-primaria); - outline: none; -} - -.eli-tabela-modal-filtro__formula-ajuda:hover .eli-tabela-modal-filtro__formula-tooltip, -.eli-tabela-modal-filtro__formula-ajuda:focus-visible .eli-tabela-modal-filtro__formula-tooltip { - opacity: 1; - pointer-events: auto; - transform: translateY(0); -} - -.eli-tabela-modal-filtro__formula-tooltip { - position: absolute; - bottom: calc(100% + 10px); - right: 0; - width: 280px; - background: #0f172a; - color: #e2e8f0; - border-radius: 10px; - padding: 14px 16px; - font-size: 0.8rem; - line-height: 1.55; - z-index: 100; - opacity: 0; - pointer-events: none; - transform: translateY(4px); - transition: opacity 0.18s ease, transform 0.18s ease; - box-shadow: 0 8px 30px rgba(0, 0, 0, 0.35); -} - -.eli-tabela-modal-filtro__formula-tooltip::after { - content: ''; - position: absolute; - top: 100%; - right: 6px; - border: 6px solid transparent; - border-top-color: #0f172a; -} - -.eli-tabela-modal-filtro__formula-tooltip strong { - font-size: 0.82rem; - color: var(--cor-eli-fundo); - display: block; - margin-bottom: 6px; -} - -.eli-tabela-modal-filtro__formula-tooltip p { - margin: 6px 0 4px; - color: #94a3b8; -} - -.eli-tabela-modal-filtro__formula-tooltip ul { - margin: 4px 0 8px 14px; - padding: 0; -} - -.eli-tabela-modal-filtro__formula-tooltip li { - margin-bottom: 3px; - color: #cbd5e1; -} - -.eli-tabela-modal-filtro__formula-tooltip code { - font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; - color: #7dd3fc; - font-size: 0.78rem; - background: rgba(255, 255, 255, 0.07); - border-radius: 4px; - padding: 1px 4px; -} - -.eli-tabela-modal-filtro__formula-input { - width: 220px; - height: 36px; - border-radius: 10px; - border: 1px solid rgba(15, 23, 42, 0.12); - padding: 0 10px; - font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; - font-size: 0.85rem; - background: var(--cor-eli-fundo); - transition: border-color 0.15s, background 0.15s; - box-sizing: border-box; -} - -.eli-tabela-modal-filtro__formula-input:focus { - outline: none; - border-color: var(--cor-eli-primaria); - background: #fff; -} - -.eli-tabela-modal-filtro__formula-tooltip-nota { - margin-top: 8px; - opacity: 0.75; -} - -/* ── Footer ──────────────────────────────────── */ -.eli-tabela-modal-filtro__footer { - display: flex; - align-items: center; - gap: 8px; - padding: 14px 20px; - border-top: 1px solid rgba(15, 23, 42, 0.08); -} - -.eli-tabela-modal-filtro__footer-spacer { - flex: 1; -} - -/* ── Botões ──────────────────────────────────── */ -.eli-tabela-modal-filtro__botao { - display: inline-flex; - align-items: center; - gap: 5px; - height: 36px; - padding: 0 16px; - border-radius: 10px; - border: 1px solid rgba(15, 23, 42, 0.12); - background: #fff; - cursor: pointer; - font-size: 0.875rem; - font-weight: 500; - transition: background 0.15s, color 0.15s, border-color 0.15s; -} - -.eli-tabela-modal-filtro__botao:disabled { - opacity: 0.45; - cursor: not-allowed; -} - -.eli-tabela-modal-filtro__botao--sec:hover:not(:disabled), -.eli-tabela-modal-filtro__botao--sec:focus-visible { - background: rgba(15, 23, 42, 0.05); -} - -.eli-tabela-modal-filtro__botao--prim { - border: none; - background: var(--cor-eli-primaria); - color: #fff; -} - -.eli-tabela-modal-filtro__botao--prim:hover:not(:disabled), -.eli-tabela-modal-filtro__botao--prim:focus-visible { - background: var(--cor-eli-primaria-escura); -} - -.eli-tabela-modal-filtro__botao--perigo { - color: rgba(220, 38, 38, 0.85); - border-color: rgba(220, 38, 38, 0.18); - background: #fff; -} - -.eli-tabela-modal-filtro__botao--perigo:hover:not(:disabled), -.eli-tabela-modal-filtro__botao--perigo:focus-visible { - background: rgba(239, 68, 68, 0.06); - border-color: rgba(220, 38, 38, 0.3); - color: rgba(220, 38, 38, 1); -} - -/* ── Responsivo ──────────────────────────────── */ -@media (max-width: 600px) { - .eli-tabela-modal-filtro__lista { - grid-template-columns: 1fr; - } - .eli-tabela-modal-filtro__rodape-filtros { - flex-direction: column; - align-items: stretch; - } - .eli-tabela-modal-filtro__acoes { - flex-wrap: wrap; - } -} - -/* ── Transição de abertura/fechamento ────────── */ -.eli-tabela-modal-filtro-enter-active, -.eli-tabela-modal-filtro-leave-active { - transition: opacity 0.2s ease; -} - -.eli-tabela-modal-filtro-enter-active .eli-tabela-modal-filtro__modal, -.eli-tabela-modal-filtro-leave-active .eli-tabela-modal-filtro__modal { - transition: transform 0.2s ease, opacity 0.2s ease; -} - -.eli-tabela-modal-filtro-enter-from, -.eli-tabela-modal-filtro-leave-to { - opacity: 0; -} - -.eli-tabela-modal-filtro-enter-from .eli-tabela-modal-filtro__modal, -.eli-tabela-modal-filtro-leave-to .eli-tabela-modal-filtro__modal { - transform: scale(0.96); - opacity: 0; -} - diff --git a/src/componentes/EliTabela/EliTabelaModalFiltroFormula.css b/src/componentes/EliTabela/EliTabelaModalFiltroFormula.css new file mode 100644 index 0000000..943ec29 --- /dev/null +++ b/src/componentes/EliTabela/EliTabelaModalFiltroFormula.css @@ -0,0 +1,225 @@ +/* ========================= + * EliTabelaModalFiltroAvancado — Fórmula, tooltip, botões e transições + * Extraído de EliTabelaModalFiltroAvancado.css para manter cada arquivo abaixo de 250 linhas. + * ========================= */ + +/* ── Fórmula de combinação ───────────────────── */ +.eli-tabela-modal-filtro__formula-container { + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + margin-left: auto; +} + +.eli-tabela-modal-filtro__formula-label { + font-size: 0.82rem; + font-weight: 500; + color: rgba(15, 23, 42, 0.55); + white-space: nowrap; + flex-shrink: 0; +} + +.eli-tabela-modal-filtro__formula-ajuda { + position: relative; + display: inline-flex; + align-items: center; + color: rgba(15, 23, 42, 0.35); + cursor: help; + transition: color 0.15s; +} + +.eli-tabela-modal-filtro__formula-ajuda:hover, +.eli-tabela-modal-filtro__formula-ajuda:focus-visible { + color: var(--cor-eli-primaria); + outline: none; +} + +.eli-tabela-modal-filtro__formula-ajuda:hover .eli-tabela-modal-filtro__formula-tooltip, +.eli-tabela-modal-filtro__formula-ajuda:focus-visible .eli-tabela-modal-filtro__formula-tooltip { + opacity: 1; + pointer-events: auto; + transform: translateY(0); +} + +.eli-tabela-modal-filtro__formula-tooltip { + position: absolute; + bottom: calc(100% + 10px); + right: 0; + width: 280px; + background: #0f172a; + color: #e2e8f0; + border-radius: 10px; + padding: 14px 16px; + font-size: 0.8rem; + line-height: 1.55; + z-index: 100; + opacity: 0; + pointer-events: none; + transform: translateY(4px); + transition: opacity 0.18s ease, transform 0.18s ease; + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.35); +} + +.eli-tabela-modal-filtro__formula-tooltip::after { + content: ''; + position: absolute; + top: 100%; + right: 6px; + border: 6px solid transparent; + border-top-color: #0f172a; +} + +.eli-tabela-modal-filtro__formula-tooltip strong { + font-size: 0.82rem; + color: var(--cor-eli-fundo); + display: block; + margin-bottom: 6px; +} + +.eli-tabela-modal-filtro__formula-tooltip p { + margin: 6px 0 4px; + color: #94a3b8; +} + +.eli-tabela-modal-filtro__formula-tooltip ul { + margin: 4px 0 8px 14px; + padding: 0; +} + +.eli-tabela-modal-filtro__formula-tooltip li { + margin-bottom: 3px; + color: #cbd5e1; +} + +.eli-tabela-modal-filtro__formula-tooltip code { + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; + color: #7dd3fc; + font-size: 0.78rem; + background: rgba(255, 255, 255, 0.07); + border-radius: 4px; + padding: 1px 4px; +} + +.eli-tabela-modal-filtro__formula-input { + width: 220px; + height: 36px; + border-radius: 10px; + border: 1px solid rgba(15, 23, 42, 0.12); + padding: 0 10px; + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; + font-size: 0.85rem; + background: var(--cor-eli-fundo); + transition: border-color 0.15s, background 0.15s; + box-sizing: border-box; +} + +.eli-tabela-modal-filtro__formula-input:focus { + outline: none; + border-color: var(--cor-eli-primaria); + background: #fff; +} + +.eli-tabela-modal-filtro__formula-tooltip-nota { + margin-top: 8px; + opacity: 0.75; +} + +/* ── Footer ──────────────────────────────────── */ +.eli-tabela-modal-filtro__footer { + display: flex; + align-items: center; + gap: 8px; + padding: 14px 20px; + border-top: 1px solid rgba(15, 23, 42, 0.08); +} + +.eli-tabela-modal-filtro__footer-spacer { + flex: 1; +} + +/* ── Botões ──────────────────────────────────── */ +.eli-tabela-modal-filtro__botao { + display: inline-flex; + align-items: center; + gap: 5px; + height: 36px; + padding: 0 16px; + border-radius: 10px; + border: 1px solid rgba(15, 23, 42, 0.12); + background: #fff; + cursor: pointer; + font-size: 0.875rem; + font-weight: 500; + transition: background 0.15s, color 0.15s, border-color 0.15s; +} + +.eli-tabela-modal-filtro__botao:disabled { + opacity: 0.45; + cursor: not-allowed; +} + +.eli-tabela-modal-filtro__botao--sec:hover:not(:disabled), +.eli-tabela-modal-filtro__botao--sec:focus-visible { + background: rgba(15, 23, 42, 0.05); +} + +.eli-tabela-modal-filtro__botao--prim { + border: none; + background: var(--cor-eli-primaria); + color: #fff; +} + +.eli-tabela-modal-filtro__botao--prim:hover:not(:disabled), +.eli-tabela-modal-filtro__botao--prim:focus-visible { + background: var(--cor-eli-primaria-escura); +} + +.eli-tabela-modal-filtro__botao--perigo { + color: rgba(220, 38, 38, 0.85); + border-color: rgba(220, 38, 38, 0.18); + background: #fff; +} + +.eli-tabela-modal-filtro__botao--perigo:hover:not(:disabled), +.eli-tabela-modal-filtro__botao--perigo:focus-visible { + background: rgba(239, 68, 68, 0.06); + border-color: rgba(220, 38, 38, 0.3); + color: rgba(220, 38, 38, 1); +} + +/* ── Responsivo ──────────────────────────────── */ +@media (max-width: 600px) { + .eli-tabela-modal-filtro__lista { + grid-template-columns: 1fr; + } + .eli-tabela-modal-filtro__rodape-filtros { + flex-direction: column; + align-items: stretch; + } + .eli-tabela-modal-filtro__acoes { + flex-wrap: wrap; + } +} + +/* ── Transição de abertura/fechamento ────────── */ +.eli-tabela-modal-filtro-enter-active, +.eli-tabela-modal-filtro-leave-active { + transition: opacity 0.2s ease; +} + +.eli-tabela-modal-filtro-enter-active .eli-tabela-modal-filtro__modal, +.eli-tabela-modal-filtro-leave-active .eli-tabela-modal-filtro__modal { + transition: transform 0.2s ease, opacity 0.2s ease; +} + +.eli-tabela-modal-filtro-enter-from, +.eli-tabela-modal-filtro-leave-to { + opacity: 0; +} + +.eli-tabela-modal-filtro-enter-from .eli-tabela-modal-filtro__modal, +.eli-tabela-modal-filtro-leave-to .eli-tabela-modal-filtro__modal { + transform: scale(0.96); + opacity: 0; +} diff --git a/src/componentes/EliTabela/EliTabelaSelecao.css b/src/componentes/EliTabela/EliTabelaSelecao.css new file mode 100644 index 0000000..d219e5e --- /dev/null +++ b/src/componentes/EliTabela/EliTabelaSelecao.css @@ -0,0 +1,66 @@ +/* ========================================= + * Seleção (Checkboxes) + * Extraído de EliTabela.css para manter cada arquivo abaixo de 250 linhas. + * ========================================= */ +.eli-tabela__th--selecao, +.eli-tabela__td--selecao { + width: 48px; + min-width: 48px; + max-width: 48px; + text-align: center; + padding: 0 8px; + vertical-align: middle; +} + +.eli-tabela__checkbox { + appearance: none; + -webkit-appearance: none; + width: 15px; + height: 15px; + cursor: pointer; + border: 2px solid rgba(0, 0, 0, 0.54); + border-radius: 2px; + margin: 0; + vertical-align: middle; + position: relative; + transition: all 0.2s ease-in-out; + background-color: transparent; + outline: none; +} + +.eli-tabela__checkbox:hover { + border-color: rgba(0, 0, 0, 0.87); +} + +.eli-tabela__checkbox:checked, +.eli-tabela__checkbox:indeterminate { + background-color: var(--cor-eli-primaria); + border-color: var(--cor-eli-primaria); +} + +.eli-tabela__checkbox:checked::after { + content: ''; + position: absolute; + top: 0px; + left: 4px; + width: 4px; + height: 9px; + border: solid white; + border-width: 0 2px 2px 0; + transform: rotate(45deg); +} + +.eli-tabela__checkbox:indeterminate::after { + content: ''; + position: absolute; + top: 5px; + left: 2px; + width: 8px; + height: 2px; + background-color: white; + border-radius: 1px; +} + +.eli-tabela__checkbox:focus-visible { + box-shadow: 0 0 0 2px var(--cor-eli-primaria-clara); +} diff --git a/src/componentes/EliTabela/types-eli-tabela.ts b/src/componentes/EliTabela/types-eli-tabela.ts index 70fb000..39c76af 100644 --- a/src/componentes/EliTabela/types-eli-tabela.ts +++ b/src/componentes/EliTabela/types-eli-tabela.ts @@ -1,5 +1,5 @@ import type { LucideIcon } from "@lucide/vue" -import type { tipoFiltroDeltaColunaTipada } from "p-comuns" +import type { tipoCondicaoFiltroDeltaTipado, tipoFiltroDeltaColunaTipada } from "p-comuns" import type { tipoResposta } from "p-respostas" import type { ComponenteEntrada } from "../EliEntrada/tiposEntradas" @@ -160,9 +160,17 @@ export type tipoEliTabelaConsulta = { chave: string /** Rótulo exibido no select de adicionar filtro */ rotulo: string - /** vai gerar filtro e mescla com os filtros já existentes */ - filtro: (valor: unknown) => tipoFiltroDeltaColunaTipada> | undefined + /** vai gerar filtro e mescla com os filtros já existentes , id é gerado de forma altomatica para esse local*/ + filtro: ( + valor: unknown, + ) => + | Pick< + tipoCondicaoFiltroDeltaTipado>, + "coluna" | "operador" | "valor" + > + | undefined /** componente de entrada que vai receber o valor para o filtro */ entrada: ComponenteEntrada }[] } +;("") diff --git a/src/componentes/EliTabela/useEliTabelaFiltrosAvancados.ts b/src/componentes/EliTabela/useEliTabelaFiltrosAvancados.ts index dcdc843..ad20ef4 100644 --- a/src/componentes/EliTabela/useEliTabelaFiltrosAvancados.ts +++ b/src/componentes/EliTabela/useEliTabelaFiltrosAvancados.ts @@ -49,12 +49,13 @@ export function useEliTabelaFiltrosAvancados( .map((f) => { const b = base.find((x) => String(x.chave) === String(f.chave)) if (!b) return null - return { chave: f.chave, filtro: b.filtro(f.valor) } + const condicao = b.filtro(f.valor) + if (!condicao) return null + return { chave: f.chave, condicao } }) - .filter( - (f): f is { chave: string; filtro: tipoFiltroDeltaColunaTipada } => - f !== null && f.filtro !== undefined && f.filtro !== null, - ) + .filter((f): f is { chave: string; condicao: tipoCondicaoFiltroDelta } => f !== null) + + if (filtrosIndividuais.length === 0) return [] if (filtrosFormula.value && filtrosIndividuais.length > 1) { const condicoesFinais: tipoCondicaoFiltroDelta[] = [] @@ -62,25 +63,22 @@ export function useEliTabelaFiltrosAvancados( filtrosIndividuais.forEach(({ chave }, i) => { formulaFinal = formulaFinal.replace(new RegExp(`\\{${chave}\\}`, "g"), `{c${i + 1}}`) }) - let globalIdx = 1 - for (let i = 0; i < filtrosIndividuais.length; i++) { - const { filtro } = filtrosIndividuais[i] - const idMap: Record = {} - for (const cond of filtro.condicoes) { - const novoId = `{c${globalIdx++}}` - idMap[cond.id] = novoId - condicoesFinais.push({ ...cond, id: novoId }) - } - let formulaLocal = filtro.formula - for (const [oldId, newId] of Object.entries(idMap)) { - formulaLocal = formulaLocal.replace(new RegExp(`\\{${oldId.slice(1, -1)}\\}`, "g"), newId) - } - const subst = filtro.condicoes.length > 1 ? `(${formulaLocal})` : formulaLocal - formulaFinal = formulaFinal.replace(new RegExp(`\\{c${i + 1}\\}`, "g"), subst) - } + filtrosIndividuais.forEach(({ condicao }, i) => { + const novoId = `{c${i + 1}}` + condicoesFinais.push({ ...condicao, id: novoId }) + }) return [{ condicoes: condicoesFinais, formula: formulaFinal }] } - return filtrosIndividuais.map((f) => f.filtro) + + /** Sem fórmula customizada: monta condições com IDs sequenciais e fórmula "E" */ + const condicoesFinais: tipoCondicaoFiltroDelta[] = filtrosIndividuais.map( + ({ condicao }, i) => ({ + ...condicao, + id: `{c${i + 1}}`, + }), + ) + const formula = condicoesFinais.map((c) => c.id).join(" E ") + return [{ condicoes: condicoesFinais, formula }] }) function abrirModalFiltro() { @@ -114,12 +112,82 @@ export function useEliTabelaFiltrosAvancados( else void carregar() } + const nomesOperadores: Record = { + "=": "igual a", + "!=": "diferente de", + ">": "maior que", + "<": "menor que", + ">=": "maior ou igual a", + "<=": "menor ou igual a", + like: "contém", + in: "em", + "∩": "possui", + } + + function formatarFiltroAtivo(entrada: unknown, valor: unknown): string { + const [tipo, opcoes] = Array.isArray(entrada) + ? (entrada as [string, Record]) + : [entrada as string, {} as Record] + + if (tipo === "confirmacao") { + const opt = opcoes || {} + if (valor === true) return (opt.rotuloAtivo as string) || "Sim" + if (valor === false) return (opt.rotuloInativo as string) || "Não" + return String(valor) + } + + if ((tipo === "selecao" || tipo === "selecaoMultipla") && typeof opcoes?.itens === "function") { + try { + const itensResult = opcoes.itens() + if (Array.isArray(itensResult)) { + const findLabel = (key: string) => { + const found = (itensResult as { chave: string; rotulo: string }[]).find( + (item) => String(item.chave) === String(key), + ) + return found ? found.rotulo : key + } + if (Array.isArray(valor)) { + return valor.map((v) => findLabel(String(v))).join(", ") + } + return findLabel(String(valor)) + } + } catch { + // Fallback + } + } + + if (Array.isArray(valor)) { + return valor.map(String).join(", ") + } + return String(valor) + } + + /** Resumo dos filtros ativos para badge e tooltip do cabecalho */ + const filtrosAtivosResumo = computed(() => { + const base = props.tabela.filtroAvancado ?? [] + const ativos = (filtrosUi.value ?? []).filter((f) => f && f.chave !== undefined) + const rotulos: string[] = [] + for (const f of ativos) { + const b = base.find((x) => String(x.chave) === String(f.chave)) + if (b) { + const condicao = b.filtro(f.valor) + if (condicao) { + const opStr = nomesOperadores[condicao.operador] || condicao.operador + const valStr = formatarFiltroAtivo(b.entrada, condicao.valor) + rotulos.push(`${b.rotulo} ${opStr} "${valStr}"`) + } + } + } + return { quantidade: rotulos.length, rotulos } + }) + return { filtrosUi, filtrosFormula, modalFiltroAberto, exibirFiltroAvancado, filtrosAvancadosAtivos, + filtrosAtivosResumo, abrirModalFiltro, fecharModalFiltro, limparFiltrosAvancados, diff --git a/src/componentes/EliTabela/useFiltroAvancado.ts b/src/componentes/EliTabela/useFiltroAvancado.ts index 010420a..861d7ed 100644 --- a/src/componentes/EliTabela/useFiltroAvancado.ts +++ b/src/componentes/EliTabela/useFiltroAvancado.ts @@ -10,8 +10,16 @@ * * Extraído de `EliTabelaModalFiltroAvancado.vue` para manter o componente abaixo de 250 linhas. */ -import { computed, type Ref, ref, watch } from "vue" -import { EliEntradaDataHora, EliEntradaNumero, EliEntradaTexto } from "../EliEntrada" +import { type Component, computed, type Ref, ref, watch } from "vue" +import { + EliEntradaConfirmacao, + EliEntradaDataHora, + EliEntradaNumero, + EliEntradaParagrafo, + EliEntradaSelecao, + EliEntradaSelecaoMultipla, + EliEntradaTexto, +} from "../EliEntrada" import type { ComponenteEntrada } from "../EliEntrada/tiposEntradas" import type { tipoEliTabelaConsulta } from "./types-eli-tabela" @@ -97,10 +105,14 @@ export function useFiltroAvancado({ return (filtrosBase.value ?? []).filter((b) => !usadas.has(String(b.chave))) }) - function componenteEntrada(entrada: ComponenteEntrada) { + function componenteEntrada(entrada: ComponenteEntrada): Component { const tipo = entrada?.[0] if (tipo === "numero") return EliEntradaNumero if (tipo === "dataHora") return EliEntradaDataHora + if (tipo === "selecao") return EliEntradaSelecao + if (tipo === "selecaoMultipla") return EliEntradaSelecaoMultipla + if (tipo === "paragrafo") return EliEntradaParagrafo + if (tipo === "confirmacao") return EliEntradaConfirmacao return EliEntradaTexto } @@ -114,6 +126,9 @@ export function useFiltroAvancado({ function valorInicialPorEntrada(entrada: ComponenteEntrada) { if (entrada?.[0] === "numero") return null + if (entrada?.[0] === "selecao") return null + if (entrada?.[0] === "selecaoMultipla") return [] + if (entrada?.[0] === "confirmacao") return false return "" } diff --git a/src/playground/api_simulador/banco.ts b/src/playground/api_simulador/banco.ts index 0c51243..6ab633f 100644 --- a/src/playground/api_simulador/banco.ts +++ b/src/playground/api_simulador/banco.ts @@ -3,6 +3,26 @@ import type { tipoCondicaoFiltroDelta, zFiltroDelta } from "p-comuns" import type z from "zod" import { _dados_empreendedores, type tipoEmpreendedores } from "./dados" +/** + * UDF registrada no Alasql para suportar o operador ∩ (interseção). + * Verifica se dois arrays possuem ao menos um elemento em comum. + */ +alasql.fn.arr_intersecta = (coluna: unknown, filtro: unknown): boolean => { + if (!Array.isArray(coluna) || !Array.isArray(filtro)) return false + return coluna.some((item) => filtro.includes(item)) +} + +/** + * UDF universal para o operador "in". + * - Coluna escalar: verifica se o valor está na lista de filtro (IN clássico). + * - Coluna array: verifica se há interseção entre o array da coluna e a lista de filtro. + */ +alasql.fn.arr_in = (coluna: unknown, filtro: unknown): boolean => { + const lista = Array.isArray(filtro) ? filtro : [filtro] + if (Array.isArray(coluna)) return coluna.some((item) => lista.includes(item)) + return lista.includes(coluna) +} + const zCondicaoFiltroDeltaParaQuery = ({ condicao, }: { @@ -39,14 +59,21 @@ const zCondicaoFiltroDeltaParaQuery = ({ ? condicao.valor : `%${condicao.valor}%` } else if (condicao.operador === "in") { - // Alasql usa @(?) para arrays. Porém, o passo 2 substitui {id} por ?. - // O que resultará em @(?). - retorno.sql = `${coluna} IN @(${condicao.id})` + // UDF universal: funciona tanto para colunas escalares (IN clássico) + // quanto para colunas array (interseção). + retorno.sql = `arr_in(${coluna}, ${condicao.id}) = TRUE` retorno.parametro[condicao.id] = Array.isArray(condicao.valor) ? condicao.valor : String(condicao.valor) .split(",") .map((s) => s.trim()) + } else if (condicao.operador === "∩") { + // Interseção: verifica se o array da coluna contém ao menos um dos valores informados. + // Como o Alasql não suporta nativamente comparação de arrays, usamos uma UDF. + retorno.sql = `arr_intersecta(${coluna}, ${condicao.id}) = TRUE` + retorno.parametro[condicao.id] = Array.isArray(condicao.valor) + ? condicao.valor + : [condicao.valor] } else { throw new Error(`Operador inválido: ${condicao.operador as string}`) } diff --git a/src/playground/api_simulador/dados.ts b/src/playground/api_simulador/dados.ts index 37c43ef..431f094 100644 --- a/src/playground/api_simulador/dados.ts +++ b/src/playground/api_simulador/dados.ts @@ -1,8 +1,13 @@ import z from "zod" -let _id = 0 -/** Gera um ID incremental único para uso no seed de dados do playground. Não usar em produção. */ -export const gerarId = () => `item-${_id++}` +export const zAreaAtuacao = z.enum([ + "mineracao", + "agro", + "turismo", + "tecnologia", + "educacao", + "saude", +]) /** Schema Zod do registro de empreendedor — usado para validação e inferência de tipo no playground. */ export const zEmpreendedores = z.object({ @@ -14,237 +19,21 @@ export const zEmpreendedores = z.object({ telefone: z.string(), faturamento: z.number(), peso_kg: z.number(), - areas_atuacao: z.array( - z.enum(["mineracao", "agro", "turismo", "tecnologia", "educacao", "saude"]), - ), + ativo: z.boolean(), + areas_atuacao: z.array(zAreaAtuacao), criado_em: z.string(), atualizado_em: z.string(), }) +export const AreaAtuacaoDetalhes: Record, string> = { + mineracao: "Mineração", + agro: "Agropecuária", + turismo: "Turismo", + tecnologia: "Tecnologia", + educacao: "Educação", + saude: "Saúde", +} + export type tipoEmpreendedores = z.infer -// == SIMULAÇÃO DE API (CRUD) == -// Dados isolados (simulando um banco de dados no servidor) -export const _dados_empreendedores: tipoEmpreendedores[] = [ - { - codigo: gerarId(), - empreendedor: "Maria Silva", - empreendimento: "Doces da Maria", - documento: "12.345.678/0001-90", - email: "contato.comercial.super.longo@doces-da-maria-exemplo-muito-grande.com.br", - telefone: "(11) 91234-5678", - faturamento: 12500.5, - peso_kg: 12.4, - areas_atuacao: ["agro", "turismo"], - criado_em: "2026-01-09T16:15:00Z", - atualizado_em: "2026-01-29T15:30:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "João Pereira", - empreendimento: "Café Peregrino", - documento: "98.765.432/0001-12", - email: "contato@cafeperegrino.com", - telefone: "(11) 93456-7810", - faturamento: 8000, - peso_kg: 6.2, - areas_atuacao: ["tecnologia", "agro"], - criado_em: "2026-01-02T10:00:00-03:00", - atualizado_em: "2026-01-20T08:10:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Ana Costa", - empreendimento: "Flor de Sal Gastronomia", - documento: "45.678.912/0001-55", - email: "ana@flordesal.com", - telefone: "(21) 99876-5432", - faturamento: 3200.75, - peso_kg: 4.8, - areas_atuacao: ["educacao"], - criado_em: "2025-12-10T09:30:00-03:00", - atualizado_em: "2026-01-15T19:20:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Rafael Lima", - empreendimento: "Tech Agro Solutions", - documento: "07.654.321/0001-00", - email: "rafael@techagro.com", - telefone: "(31) 94567-8899", - faturamento: 15999.9, - peso_kg: 9.1, - areas_atuacao: ["tecnologia"], - criado_em: "2026-01-12T12:00:00-03:00", - atualizado_em: "2026-01-28T11:00:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Juliana Torres", - empreendimento: "Torres Moda Sustentável", - documento: "33.210.987/0001-44", - email: "juliana@torresmoda.com", - telefone: "(71) 97766-5544", - faturamento: 4500, - peso_kg: 3.3, - areas_atuacao: ["turismo", "agro"], - criado_em: "2026-01-01T00:00:00Z", - atualizado_em: "2026-01-10T13:45:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Marcos Vieira", - empreendimento: "Padaria Pão Quentinho", - documento: "21.109.876/0001-32", - email: "marcos@paoquentinho.com", - telefone: "(48) 99654-3210", - faturamento: 2200.4, - peso_kg: 15.7, - areas_atuacao: ["saude"], - criado_em: "2026-01-05T07:25:00-03:00", - atualizado_em: "2026-01-23T20:15:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Bianca Rocha", - empreendimento: "Studio Beleza em Casa", - documento: "54.321.098/0001-21", - email: "contato@belezaemcasa.com", - telefone: "(85) 98877-1122", - faturamento: 990, - peso_kg: 1.9, - areas_atuacao: ["agro", "tecnologia"], - criado_em: "2026-01-03T14:00:00-03:00", - atualizado_em: "2026-01-29T09:00:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Caio Albuquerque", - empreendimento: "Albuquerque Engenharia Verde", - documento: "65.432.109/0001-09", - email: "caio@engenhariaverde.com", - telefone: "(61) 98123-4567", - faturamento: 100000, - peso_kg: 21.3, - areas_atuacao: ["mineracao"], - criado_em: "2025-11-18T16:10:00-03:00", - atualizado_em: "2026-01-25T10:40:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Fernanda Almeida", - empreendimento: "Faz Bem Produtos Naturais", - documento: "87.654.210/0001-98", - email: "fernanda@fazbem.com", - telefone: "(41) 99777-6655", - faturamento: 5600.2, - peso_kg: 8.6, - areas_atuacao: ["mineracao", "turismo"], - criado_em: "2026-01-08T12:12:00-03:00", - atualizado_em: "2026-01-21T17:55:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Gabriel Martins", - empreendimento: "Martins Tech Repair", - documento: "19.876.543/0001-76", - email: "suporte@martinstech.com", - telefone: "(19) 98888-9090", - faturamento: 7550, - peso_kg: 7.7, - areas_atuacao: ["mineracao", "turismo"], - criado_em: "2026-01-11T11:00:00-03:00", - atualizado_em: "2026-01-18T11:30:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Helena Duarte", - empreendimento: "Ateliê Costura Criativa", - documento: "23.456.789/0001-65", - email: "helena@costuracriativa.com", - telefone: "(51) 98765-4433", - faturamento: 2700, - peso_kg: 2.5, - areas_atuacao: ["turismo", "mineracao"], - criado_em: "2026-01-07T08:00:00-03:00", - atualizado_em: "2026-01-16T14:10:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Igor Santos", - empreendimento: "Santos Consultoria Financeira", - documento: "43.219.876/0001-54", - email: "igor@santosconsultoria.com", - telefone: "(31) 99332-1100", - faturamento: 12000, - peso_kg: 5.4, - areas_atuacao: ["saude"], - criado_em: "2026-01-06T10:30:00-03:00", - atualizado_em: "2026-01-22T18:30:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Jéssica Nunes", - empreendimento: "Nunes Eventos & Decoração", - documento: "09.876.543/0001-33", - email: "jessica@nunesdecora.com", - telefone: "(62) 99922-3344", - faturamento: 3300.1, - peso_kg: 4.1, - areas_atuacao: ["turismo"], - criado_em: "2026-01-04T13:30:00-03:00", - atualizado_em: "2026-01-26T09:20:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Leonardo Prado", - empreendimento: "Prado Bike Courier", - documento: "72.345.098/0001-27", - email: "contato@pradobike.com", - telefone: "(47) 98444-6677", - faturamento: 4100, - peso_kg: 6.8, - areas_atuacao: ["mineracao"], - criado_em: "2026-01-02T18:40:00-03:00", - atualizado_em: "2026-01-27T12:00:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Marina Lopes", - empreendimento: "Lopes Design Digital", - documento: "56.789.012/0001-11", - email: "marina@lopesdesign.com", - telefone: "(27) 99911-2233", - faturamento: 2100, - peso_kg: 3.7, - areas_atuacao: ["agro"], - criado_em: "2026-01-09T09:00:00-03:00", - atualizado_em: "2026-01-19T15:35:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Nicolas Teixeira", - empreendimento: "QualiVida Fitness", - documento: "34.567.890/0001-02", - email: "nicolas@qualivida.com", - telefone: "(92) 99456-7788", - faturamento: 8600.9, - peso_kg: 11.2, - areas_atuacao: ["agro", "saude"], - criado_em: "2026-01-12T19:15:00-03:00", - atualizado_em: "2026-01-29T10:50:00-03:00", - }, - { - codigo: gerarId(), - empreendedor: "Olívia Azevedo", - empreendimento: "Azevedo Pet Care", - documento: "88.901.234/0001-45", - email: "olivia@petcare.com", - telefone: "(16) 99788-6655", - faturamento: 1900.3, - peso_kg: 2.2, - areas_atuacao: ["agro"], - criado_em: "2026-01-10T15:00:00-03:00", - atualizado_em: "2026-01-14T16:00:00-03:00", - }, -] +export { _dados_empreendedores } from "./dadosSeed" diff --git a/src/playground/api_simulador/dadosSeed.ts b/src/playground/api_simulador/dadosSeed.ts new file mode 100644 index 0000000..6189b98 --- /dev/null +++ b/src/playground/api_simulador/dadosSeed.ts @@ -0,0 +1,249 @@ +/** dadosSeed — dados de seed (mock) para o playground da EliTabela. */ +import type { tipoEmpreendedores } from "./dados" + +let _id = 0 +/** Gera um ID incremental único para uso no seed de dados do playground. Não usar em produção. */ +export const gerarId = () => `item-${_id++}` + +// == SIMULAÇÃO DE API (CRUD) == +// Dados isolados (simulando um banco de dados no servidor) +export const _dados_empreendedores: tipoEmpreendedores[] = [ + { + codigo: gerarId(), + empreendedor: "Maria Silva", + empreendimento: "Doces da Maria", + documento: "12.345.678/0001-90", + email: "contato.comercial.super.longo@doces-da-maria-exemplo-muito-grande.com.br", + telefone: "(11) 91234-5678", + faturamento: 12500.5, + peso_kg: 12.4, + ativo: true, + areas_atuacao: ["agro", "turismo"], + criado_em: "2026-01-09T16:15:00Z", + atualizado_em: "2026-01-29T15:30:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "João Pereira", + empreendimento: "Café Peregrino", + documento: "98.765.432/0001-12", + email: "contato@cafeperegrino.com", + telefone: "(11) 93456-7810", + faturamento: 8000, + peso_kg: 6.2, + ativo: true, + areas_atuacao: ["tecnologia", "agro"], + criado_em: "2026-01-02T10:00:00-03:00", + atualizado_em: "2026-01-20T08:10:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Ana Costa", + empreendimento: "Flor de Sal Gastronomia", + documento: "45.678.912/0001-55", + email: "ana@flordesal.com", + telefone: "(21) 99876-5432", + faturamento: 3200.75, + peso_kg: 4.8, + ativo: false, + areas_atuacao: ["educacao"], + criado_em: "2025-12-10T09:30:00-03:00", + atualizado_em: "2026-01-15T19:20:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Rafael Lima", + empreendimento: "Tech Agro Solutions", + documento: "07.654.321/0001-00", + email: "rafael@techagro.com", + telefone: "(31) 94567-8899", + faturamento: 15999.9, + peso_kg: 9.1, + ativo: true, + areas_atuacao: ["tecnologia"], + criado_em: "2026-01-12T12:00:00-03:00", + atualizado_em: "2026-01-28T11:00:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Juliana Torres", + empreendimento: "Torres Moda Sustentável", + documento: "33.210.987/0001-44", + email: "juliana@torresmoda.com", + telefone: "(71) 97766-5544", + faturamento: 4500, + peso_kg: 3.3, + ativo: true, + areas_atuacao: ["turismo", "agro"], + criado_em: "2026-01-01T00:00:00Z", + atualizado_em: "2026-01-10T13:45:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Marcos Vieira", + empreendimento: "Padaria Pão Quentinho", + documento: "21.109.876/0001-32", + email: "marcos@paoquentinho.com", + telefone: "(48) 99654-3210", + faturamento: 2200.4, + peso_kg: 15.7, + ativo: false, + areas_atuacao: ["saude"], + criado_em: "2026-01-05T07:25:00-03:00", + atualizado_em: "2026-01-23T20:15:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Bianca Rocha", + empreendimento: "Studio Beleza em Casa", + documento: "54.321.098/0001-21", + email: "contato@belezaemcasa.com", + telefone: "(85) 98877-1122", + faturamento: 990, + peso_kg: 1.9, + ativo: true, + areas_atuacao: ["agro", "tecnologia"], + criado_em: "2026-01-03T14:00:00-03:00", + atualizado_em: "2026-01-29T09:00:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Caio Albuquerque", + empreendimento: "Albuquerque Engenharia Verde", + documento: "65.432.109/0001-09", + email: "caio@engenhariaverde.com", + telefone: "(61) 98123-4567", + faturamento: 100000, + peso_kg: 21.3, + ativo: true, + areas_atuacao: ["mineracao"], + criado_em: "2025-11-18T16:10:00-03:00", + atualizado_em: "2026-01-25T10:40:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Fernanda Almeida", + empreendimento: "Faz Bem Produtos Naturais", + documento: "87.654.210/0001-98", + email: "fernanda@fazbem.com", + telefone: "(41) 99777-6655", + faturamento: 5600.2, + peso_kg: 8.6, + ativo: false, + areas_atuacao: ["mineracao", "turismo"], + criado_em: "2026-01-08T12:12:00-03:00", + atualizado_em: "2026-01-21T17:55:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Gabriel Martins", + empreendimento: "Martins Tech Repair", + documento: "19.876.543/0001-76", + email: "suporte@martinstech.com", + telefone: "(19) 98888-9090", + faturamento: 7550, + peso_kg: 7.7, + ativo: true, + areas_atuacao: ["mineracao", "turismo"], + criado_em: "2026-01-11T11:00:00-03:00", + atualizado_em: "2026-01-18T11:30:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Helena Duarte", + empreendimento: "Ateliê Costura Criativa", + documento: "23.456.789/0001-65", + email: "helena@costuracriativa.com", + telefone: "(51) 98765-4433", + faturamento: 2700, + peso_kg: 2.5, + ativo: true, + areas_atuacao: ["turismo", "mineracao"], + criado_em: "2026-01-07T08:00:00-03:00", + atualizado_em: "2026-01-16T14:10:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Igor Santos", + empreendimento: "Santos Consultoria Financeira", + documento: "43.219.876/0001-54", + email: "igor@santosconsultoria.com", + telefone: "(31) 99332-1100", + faturamento: 12000, + peso_kg: 5.4, + ativo: false, + areas_atuacao: ["saude"], + criado_em: "2026-01-06T10:30:00-03:00", + atualizado_em: "2026-01-22T18:30:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Jéssica Nunes", + empreendimento: "Nunes Eventos & Decoração", + documento: "09.876.543/0001-33", + email: "jessica@nunesdecora.com", + telefone: "(62) 99922-3344", + faturamento: 3300.1, + peso_kg: 4.1, + ativo: true, + areas_atuacao: ["turismo"], + criado_em: "2026-01-04T13:30:00-03:00", + atualizado_em: "2026-01-26T09:20:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Leonardo Prado", + empreendimento: "Prado Bike Courier", + documento: "72.345.098/0001-27", + email: "contato@pradobike.com", + telefone: "(47) 98444-6677", + faturamento: 4100, + peso_kg: 6.8, + ativo: true, + areas_atuacao: ["mineracao"], + criado_em: "2026-01-02T18:40:00-03:00", + atualizado_em: "2026-01-27T12:00:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Marina Lopes", + empreendimento: "Lopes Design Digital", + documento: "56.789.012/0001-11", + email: "marina@lopesdesign.com", + telefone: "(27) 99911-2233", + faturamento: 2100, + peso_kg: 3.7, + ativo: false, + areas_atuacao: ["agro"], + criado_em: "2026-01-09T09:00:00-03:00", + atualizado_em: "2026-01-19T15:35:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Nicolas Teixeira", + empreendimento: "QualiVida Fitness", + documento: "34.567.890/0001-02", + email: "nicolas@qualivida.com", + telefone: "(92) 99456-7788", + faturamento: 8600.9, + peso_kg: 11.2, + ativo: true, + areas_atuacao: ["agro", "saude"], + criado_em: "2026-01-12T19:15:00-03:00", + atualizado_em: "2026-01-29T10:50:00-03:00", + }, + { + codigo: gerarId(), + empreendedor: "Olívia Azevedo", + empreendimento: "Azevedo Pet Care", + documento: "88.901.234/0001-45", + email: "olivia@petcare.com", + telefone: "(16) 99788-6655", + faturamento: 1900.3, + peso_kg: 2.2, + ativo: true, + areas_atuacao: ["agro"], + criado_em: "2026-01-10T15:00:00-03:00", + atualizado_em: "2026-01-14T16:00:00-03:00", + }, +] diff --git a/src/playground/api_simulador/index.ts b/src/playground/api_simulador/index.ts index 18406e7..4ca5193 100644 --- a/src/playground/api_simulador/index.ts +++ b/src/playground/api_simulador/index.ts @@ -39,6 +39,7 @@ const api_criar: tipoFuncaoApiSimulacao<"criar"> = async (dados) => { telefone: dados.telefone || "(00) 0000-0000", faturamento: dados.faturamento || 0, peso_kg: dados.peso_kg || 0, + ativo: dados.ativo ?? true, areas_atuacao: dados.areas_atuacao || [], criado_em: new Date().toISOString(), atualizado_em: new Date().toISOString(), diff --git a/src/playground/useTabelaPlayground.ts b/src/playground/useTabelaPlayground.ts index 165d9b1..4f08838 100644 --- a/src/playground/useTabelaPlayground.ts +++ b/src/playground/useTabelaPlayground.ts @@ -37,6 +37,7 @@ export function useTabelaPlayground() { telefone: "", faturamento: 0, peso_kg: 0, + ativo: true, areas_atuacao: [], }) const erroCadastro = ref(undefined) @@ -54,6 +55,7 @@ export function useTabelaPlayground() { telefone: "", faturamento: 0, peso_kg: 0, + ativo: true, areas_atuacao: [], } erroCadastro.value = undefined diff --git a/src/playground/useTabelaPlaygroundConfig.ts b/src/playground/useTabelaPlaygroundConfig.ts index 8ebc242..30f2f9e 100644 --- a/src/playground/useTabelaPlaygroundConfig.ts +++ b/src/playground/useTabelaPlaygroundConfig.ts @@ -6,15 +6,22 @@ * @see useTabelaPlayground.ts — composable que consome estas configurações */ import { BadgeCheck, Eye, Pencil, Trash2 } from "@lucide/vue" -import { iniciarFiltroDelta } from "p-comuns" import type { ComponenteEntrada } from "@/componentes/EliEntrada/tiposEntradas" import type { tipoEliTabelaConsulta } from "@/componentes/EliTabela" import { celulaTabela } from "@/componentes/EliTabela" -import type { tipoEmpreendedores } from "./api_simulador/dados" +import { AreaAtuacaoDetalhes, type tipoEmpreendedores } from "./api_simulador/dados" -const { criarFiltroDelta } = iniciarFiltroDelta>() +/** Mapeamento de cor por área de atuação para uso nos chips da tabela */ +const corAreaAtuacao: Record = { + mineracao: "#78909C", + agro: "#4CAF50", + turismo: "#FF9800", + tecnologia: "#2196F3", + educacao: "#9C27B0", + saude: "#E91E63", +} /** Ações por linha usadas nas 3 instâncias de tabela do playground */ export function criarAcoesLinha( @@ -79,6 +86,17 @@ export const colunasTabela: tipoEliTabelaConsulta["colunas"] coluna_ordem: "telefone", visivel: true, }, + { + rotulo: "Áreas de Atuação", + celula: (l) => + celulaTabela("tags", { + opcoes: l.areas_atuacao.map((area) => ({ + rotulo: AreaAtuacaoDetalhes[area], + cor: corAreaAtuacao[area], + })), + }), + visivel: true, + }, { rotulo: "Faturamento", celula: (l) => @@ -134,12 +152,7 @@ export const filtrosAvancados: tipoEliTabelaConsulta["filtro rotulo: "Empreendedor", filtro: (valor: unknown) => { if (!valor) return undefined - return criarFiltroDelta({ - condicoes: [ - { id: "{c1}", coluna: "empreendedor", operador: "like", valor: valor as string }, - ], - formula: "{c1}", - }) + return { coluna: "empreendedor", operador: "like" as const, valor: valor as string } }, entrada: ["texto", { rotulo: "Empreendedor" }] as ComponenteEntrada, }, @@ -148,12 +161,7 @@ export const filtrosAvancados: tipoEliTabelaConsulta["filtro rotulo: "Empreendimento", filtro: (valor: unknown) => { if (!valor) return undefined - return criarFiltroDelta({ - condicoes: [ - { id: "{c1}", coluna: "empreendimento", operador: "like", valor: valor as string }, - ], - formula: "{c1}", - }) + return { coluna: "empreendimento", operador: "like" as const, valor: valor as string } }, entrada: ["texto", { rotulo: "Empreendimento" }] as ComponenteEntrada, }, @@ -162,10 +170,7 @@ export const filtrosAvancados: tipoEliTabelaConsulta["filtro rotulo: "Documento", filtro: (valor: unknown) => { if (!valor) return undefined - return criarFiltroDelta({ - condicoes: [{ id: "{c1}", coluna: "documento", operador: "like", valor: valor as string }], - formula: "{c1}", - }) + return { coluna: "documento", operador: "like" as const, valor: valor as string } }, entrada: ["texto", { rotulo: "Documento", formato: "cpfCnpj" }] as ComponenteEntrada, }, @@ -174,13 +179,65 @@ export const filtrosAvancados: tipoEliTabelaConsulta["filtro rotulo: "E-mail", filtro: (valor: unknown) => { if (!valor) return undefined - return criarFiltroDelta({ - condicoes: [{ id: "{c1}", coluna: "email", operador: "like", valor: valor as string }], - formula: "{c1}", - }) + return { coluna: "email", operador: "like" as const, valor: valor as string } }, entrada: ["texto", { rotulo: "E-mail", formato: "email" }] as ComponenteEntrada, }, + { + chave: "ativo", + rotulo: "Ativo", + filtro: (valor: unknown) => { + return { coluna: "ativo", operador: "=" as const, valor: valor as boolean } + }, + entrada: [ + "confirmacao", + { rotulo: "Ativo", rotuloAtivo: "Apenas ativos", rotuloInativo: "Apenas inativos" }, + ] as ComponenteEntrada, + }, + { + chave: "faturamento_min", + rotulo: "Faturamento mínimo", + filtro: (valor: unknown) => { + if (valor === null || valor === undefined) return undefined + return { coluna: "faturamento", operador: ">=" as const, valor: valor as number } + }, + entrada: ["numero", { rotulo: "Faturamento mínimo (R$)" }] as ComponenteEntrada, + }, + { + chave: "areas_atuacao", + rotulo: "Área de Atuação", + filtro: (valor: unknown) => { + const arr = valor as string[] + if (!arr.length) return undefined + return { coluna: "areas_atuacao", operador: "∩" as const, valor: arr } + }, + entrada: [ + "selecaoMultipla", + { + rotulo: "Área de Atuação", + limpavel: true, + itens: () => + Object.entries(AreaAtuacaoDetalhes).map(([chave, rotulo]) => ({ chave, rotulo })), + }, + ] as ComponenteEntrada, + }, + { + chave: "areas_atuacao2", + rotulo: "Área de Atuação (2)", + filtro: (valor: unknown) => { + if (!valor) return undefined + return { coluna: "areas_atuacao", operador: "in" as const, valor } + }, + entrada: [ + "selecao", + { + rotulo: "Área de Atuação", + limpavel: true, + itens: () => + Object.entries(AreaAtuacaoDetalhes).map(([chave, rotulo]) => ({ chave, rotulo })), + }, + ] as ComponenteEntrada, + }, ] /**