ajustes na tabela de consulta
This commit is contained in:
parent
aea3da6585
commit
1446d6225b
78 changed files with 17921 additions and 17580 deletions
|
|
@ -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"
|
||||
60
.agent/rules/autonomia-agente.global.md
Normal file
60
.agent/rules/autonomia-agente.global.md
Normal file
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
86
.agent/rules/estrutura-vue.global.md
Normal file
86
.agent/rules/estrutura-vue.global.md
Normal file
|
|
@ -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
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MeuComponente',
|
||||
setup() {
|
||||
// lógica aqui
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## 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 `<style src="./MeuComponente.css" />`.
|
||||
|
||||
## 4. Dispatch Table
|
||||
|
||||
Uso de mapa de funções para eliminar `if/else` ou `switch` longos:
|
||||
|
||||
```ts
|
||||
// handlers.ts
|
||||
export const acaoHandlers: Record<string, () => void> = {
|
||||
salvar: () => { ... },
|
||||
cancelar: () => { ... },
|
||||
excluir: () => { ... },
|
||||
}
|
||||
```
|
||||
|
||||
## Impacto
|
||||
|
||||
Sem estes padrões, componentes perdem inferência de tipos, lógica fica inauditável em composables externos, e CSS cresce inline sem controle.
|
||||
|
||||
## Referências
|
||||
|
||||
- Rule: `.agent/rules/limite-linhas.global.md`
|
||||
- Rule: `.agent/rules/typescript-sem-any.global.md`
|
||||
120
.agent/rules/fronteiras-monorepo.global.md
Normal file
120
.agent/rules/fronteiras-monorepo.global.md
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
---
|
||||
type: rule
|
||||
name: fronteiras-monorepo
|
||||
description: Define a convenção de pacotes e regras de importação entre módulos do monorepo
|
||||
scope: global
|
||||
trigger: always_on
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Regra: Fronteiras de Pacote no Monorepo
|
||||
|
||||
## Contexto
|
||||
|
||||
Cada módulo SaaS do ecossistema é organizado em até 4 pacotes dentro de um monorepo. O agente **deve** respeitar as fronteiras de importação entre pacotes para evitar acoplamento indevido.
|
||||
|
||||
## Convenção de Pacotes
|
||||
|
||||
Cada módulo segue a estrutura:
|
||||
|
||||
```
|
||||
<modulo>-back ← Servidor: rotas, serviços, filas, CRON
|
||||
<modulo>-front ← Interface: páginas Vue, componentes, composables
|
||||
<modulo>-comuns ← Compartilhado: tipos, schemas Zod, enums, utils puros
|
||||
<modulo>-migracao ← Banco de dados: migrations, seeds, scripts de carga
|
||||
```
|
||||
|
||||
Nem todo módulo terá os 4 pacotes. O mínimo funcional é `back` + `front` + `comuns`.
|
||||
|
||||
## Regras de Importação
|
||||
|
||||
### Permitido ✅
|
||||
|
||||
```
|
||||
<modulo>-front → pode importar de → <modulo>-comuns
|
||||
<modulo>-back → pode importar de → <modulo>-comuns
|
||||
<modulo>-migracao → pode importar de → <modulo>-comuns
|
||||
```
|
||||
|
||||
### Proibido ❌
|
||||
|
||||
```
|
||||
<modulo>-front ✗ nunca importar de → <modulo>-back
|
||||
<modulo>-back ✗ nunca importar de → <modulo>-front
|
||||
<modulo>-migracao ✗ nunca importar de → <modulo>-front
|
||||
<modulo>-front ✗ nunca importar de → <outro-modulo>-back
|
||||
<outro-modulo> ✗ nunca importar de → <modulo>-back (direto)
|
||||
```
|
||||
|
||||
### Comunicação entre módulos
|
||||
|
||||
Módulos diferentes **nunca** importam código um do outro diretamente. Comunicação entre módulos deve ocorrer via:
|
||||
|
||||
- **API HTTP** (chamada de serviço entre backs)
|
||||
- **Fila** (BullMQ, Redis) para processamento assíncrono
|
||||
- **Banco compartilhado** com schemas/tabelas separados por módulo
|
||||
- **Pacote `*-comuns` compartilhado** apenas para tipos e schemas de contrato
|
||||
|
||||
## O que vive em cada pacote
|
||||
|
||||
### `<modulo>-comuns`
|
||||
|
||||
O pacote comuns é a **única fonte de verdade** para tipos e schemas compartilhados:
|
||||
|
||||
```
|
||||
<modulo>-comuns/src/
|
||||
├── tabelas/ ← schemas Zod por entidade (zEntidadeCriar, etc.)
|
||||
├── enums/ ← enums de domínio do módulo
|
||||
├── tipos/ ← tipos derivados dos schemas
|
||||
└── utils/ ← funções puras sem side-effect
|
||||
```
|
||||
|
||||
**Proibido** no comuns:
|
||||
- Componentes Vue ou React
|
||||
- Código com `import` de Node.js (`fs`, `path`, `http`)
|
||||
- Código com `import` de bibliotecas de frontend (`vue`, `vuetify`)
|
||||
- Lógica de negócio que depende de banco ou serviço externo
|
||||
|
||||
### `<modulo>-back`
|
||||
|
||||
```
|
||||
<modulo>-back/src/
|
||||
├── rotas/ ← endpoints Express/Fastify
|
||||
├── servicos/ ← lógica de negócio
|
||||
├── banco/ ← queries (Knex, Prisma, etc.)
|
||||
├── filas/ ← processadores de fila
|
||||
├── cron/ ← tarefas agendadas
|
||||
└── utils/ ← helpers exclusivos do back
|
||||
```
|
||||
|
||||
### `<modulo>-front`
|
||||
|
||||
```
|
||||
<modulo>-front/src/
|
||||
├── paginas/ ← páginas/views por rota
|
||||
├── componentes/ ← componentes reutilizáveis do módulo
|
||||
├── composables/ ← lógica reutilizável (use*.ts)
|
||||
└── utils/ ← helpers exclusivos do front
|
||||
```
|
||||
|
||||
### `<modulo>-migracao`
|
||||
|
||||
```
|
||||
<modulo>-migracao/src/
|
||||
├── migrations/ ← criação/alteração de tabelas
|
||||
├── seeds/ ← dados iniciais
|
||||
└── scripts/ ← scripts de carga/transformação
|
||||
```
|
||||
|
||||
## Impacto
|
||||
|
||||
Sem esta regra:
|
||||
- Front importa serviço de back (quebra em build de produção)
|
||||
- Módulo A acessa tabelas do Módulo B diretamente (acoplamento oculto)
|
||||
- Schemas ficam duplicados entre back e front
|
||||
- Mudança em um módulo quebra outro sem aviso
|
||||
|
||||
## Referências
|
||||
|
||||
- Rule: `.agent/rules/zod-validacao-padrao.global.md` (schemas no `*-comuns`)
|
||||
- Skill: `.agent/skills/mapear-codigo-fonte.global/SKILL.md`
|
||||
59
.agent/rules/jsdoc-padrao.global.md
Normal file
59
.agent/rules/jsdoc-padrao.global.md
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
type: rule
|
||||
name: jsdoc-padrao
|
||||
description: Padrões de documentação em código (JSDoc, casts temporários e TODOs estruturados)
|
||||
scope: global
|
||||
trigger: file_match
|
||||
globs:
|
||||
- "**/*.ts"
|
||||
- "**/*.tsx"
|
||||
- "**/*.vue"
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Regra: Documentação em Código
|
||||
|
||||
## 1. JSDoc — Princípios
|
||||
|
||||
- **Por que, não o Quê**: documente o raciocínio, não o mecanismo que o TypeScript já expressa.
|
||||
- **Nomenclatura limpa**: confie em nomes expressivos antes de comentários.
|
||||
|
||||
## 2. JSDoc obrigatório em funções compartilhadas
|
||||
|
||||
Toda função central ou de uso compartilhado deve ter cabeçalho JSDoc:
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Busca detalhes da entidade e calcula privilégios aplicáveis.
|
||||
* Não utilizar em processamento em lote para evitar sobrecarga.
|
||||
*
|
||||
* @param tenantId - Identificador do tenant no sistema
|
||||
* @param entityId - ID do registro na base de dados
|
||||
* @returns Objeto consolidado ou `null` se não encontrado
|
||||
*/
|
||||
export const obterDetalhes = async (tenantId: string, entityId: string) => { ... }
|
||||
```
|
||||
|
||||
## 3. Casts temporários — justificativa obrigatória
|
||||
|
||||
Coerções agressivas requerem comentário explicando o porquê:
|
||||
|
||||
```typescript
|
||||
// Necessário: biblioteca externa não provê tipagem genérica na versão atual
|
||||
const dados = payload.raw as unknown as RespostaFormatada
|
||||
```
|
||||
|
||||
## 4. TODOs estruturados
|
||||
|
||||
Dívida técnica ou dúvida de negócio deve seguir formato padronizado:
|
||||
|
||||
- **Tipagem**: `// TODO de tipagem: Criar interface para o retorno da API`
|
||||
- **Negócio**: `// TODO de regra: Validar com Produto se taxa se aplica a clientes antigos`
|
||||
|
||||
## Impacto
|
||||
|
||||
Sem este padrão, funções compartilhadas ficam sem documentação, casts aparecem sem justificativa, e TODOs são inconsistentes e difíceis de buscar.
|
||||
|
||||
## Referências
|
||||
|
||||
- Rule: `.agent/rules/typescript-sem-any.global.md`
|
||||
66
.agent/rules/limite-linhas.global.md
Normal file
66
.agent/rules/limite-linhas.global.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
---
|
||||
type: rule
|
||||
name: limite-linhas
|
||||
description: Nenhum arquivo de código pode ultrapassar 250 linhas
|
||||
scope: global
|
||||
trigger: file_match
|
||||
globs:
|
||||
- "**/*.ts"
|
||||
- "**/*.tsx"
|
||||
- "**/*.vue"
|
||||
- "**/*.css"
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Regra: Limite de 250 Linhas por Arquivo
|
||||
|
||||
## Regra
|
||||
|
||||
Nenhum arquivo `*.vue`, `*.ts`, `*.tsx` ou `*.css` pode ultrapassar **250 linhas**.
|
||||
|
||||
> Validação automática: `.agent/verificar-linhas.sh`
|
||||
|
||||
## Estratégias de Decomposição
|
||||
|
||||
| Situação | Ação |
|
||||
|---|---|
|
||||
| Template longo | Extrair seções como subcomponentes |
|
||||
| CSS extenso | Mover para `<nome>.css` co-localizado |
|
||||
| `setup()` extenso | Extrair subcomponentes, CSS ou Dispatch Table |
|
||||
| Modal/Dialog embutido | Sempre em componente separado (`*Modal.vue`) |
|
||||
| Handlers e dispatch | Extrair para `handlers.ts` ou `dispatch.ts` |
|
||||
|
||||
## Divisão em Pasta
|
||||
|
||||
Quando um componente precisa de múltiplos sub-arquivos, converter em **pasta homônima** com `index.vue`:
|
||||
|
||||
```
|
||||
# Antes
|
||||
ComponenteGrande.vue ← monolítico (+250 linhas)
|
||||
|
||||
# Depois
|
||||
ComponenteGrande/
|
||||
├── index.vue ← orquestração (≤250 linhas)
|
||||
├── SecaoPrincipal.vue ← bloco de template extraído
|
||||
├── SecaoSecundaria.vue ← outro bloco extraído
|
||||
├── componenteGrande.mapeadores.ts ← lógica de eventos
|
||||
└── ComponenteGrande.css ← estilos co-localizados
|
||||
```
|
||||
|
||||
| Arquivo | Responsabilidade |
|
||||
|---|---|
|
||||
| `index.vue` | Orquestração: importa e compõe. Sem lógica densa. |
|
||||
| `Secao*.vue` | Template isolado. Props + eventos. |
|
||||
| `*.mapeadores.ts` | Funções de ação desacopladas. |
|
||||
| `*.css` | Estilos co-localizados. |
|
||||
|
||||
> Importações externas não mudam: `ComponenteGrande/` resolve para `index.vue`.
|
||||
|
||||
## Impacto
|
||||
|
||||
Sem esta regra, arquivos crescem silenciosamente até se tornarem intocáveis — ninguém quer revisar ou refatorar um componente de 800 linhas.
|
||||
|
||||
## Referências
|
||||
|
||||
- Script: `.agent/verificar-linhas.sh`
|
||||
- Workflow: `.agent/workflows/otimizar-codigos.global.md`
|
||||
123
.agent/rules/mensagens-usuario.global.md
Normal file
123
.agent/rules/mensagens-usuario.global.md
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
---
|
||||
type: rule
|
||||
name: mensagens-usuario
|
||||
description: Toda mensagem visível ao usuário final deve ser escrita em linguagem do domínio, sem termos técnicos de TI
|
||||
scope: global
|
||||
trigger: file_match
|
||||
globs:
|
||||
- "**/*.vue"
|
||||
- "**/*.ts"
|
||||
- "**/*.tsx"
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Regra: Mensagens Orientadas ao Domínio
|
||||
|
||||
## Contexto
|
||||
|
||||
O usuário final dos módulos SaaS é profissional de **engenharia, ciência ou gestão ambiental**. Ele entende de legislação, processos técnicos e ciência aplicada — mas **não tem obrigação de entender tecnologia**.
|
||||
|
||||
Toda mensagem visível na interface — erro, validação, confirmação, tooltip, empty state, ajuda — deve ser escrita na linguagem do domínio do módulo.
|
||||
|
||||
## Regra
|
||||
|
||||
### 1. Erros de validação devem indicar O QUE preencher, não O QUE falhou
|
||||
|
||||
```typescript
|
||||
// ❌ ERRADO — linguagem técnica
|
||||
"Campo obrigatório"
|
||||
"Erro de validação"
|
||||
"Formato inválido"
|
||||
"Payload inválido"
|
||||
|
||||
// ✅ CORRETO — linguagem do domínio
|
||||
"Informe a Área do Empreendimento (em hectares)"
|
||||
"Selecione o Tipo de Licença: LP, LI ou LO"
|
||||
"A Data da Vistoria deve ser anterior à data atual"
|
||||
"Informe o CNPJ no formato 00.000.000/0000-00"
|
||||
```
|
||||
|
||||
### 2. Erros de sistema devem ser traduzidos para o usuário
|
||||
|
||||
```typescript
|
||||
// ❌ ERRADO — erro técnico exposto
|
||||
"Erro 500: Internal Server Error"
|
||||
"Timeout na requisição"
|
||||
"Network Error"
|
||||
"ECONNREFUSED"
|
||||
|
||||
// ✅ CORRETO — mensagem humanizada
|
||||
"Não foi possível salvar. Tente novamente em instantes."
|
||||
"A operação demorou mais que o esperado. Tente novamente."
|
||||
"Sem conexão com o servidor. Verifique sua internet."
|
||||
```
|
||||
|
||||
### 3. Estados vazios devem explicar o contexto
|
||||
|
||||
Quando uma lista ou tabela está vazia, a mensagem deve orientar o usuário:
|
||||
|
||||
```typescript
|
||||
// ❌ ERRADO — genérico
|
||||
"Nenhum registro encontrado"
|
||||
"Lista vazia"
|
||||
|
||||
// ✅ CORRETO — explica o domínio e orienta
|
||||
"Nenhuma condicionante cadastrada para esta licença."
|
||||
"Nenhum relatório de monitoramento registrado neste período."
|
||||
"Nenhum empreendimento encontrado com os filtros aplicados. Tente ampliar a busca."
|
||||
```
|
||||
|
||||
### 4. Confirmações devem descrever a consequência
|
||||
|
||||
```typescript
|
||||
// ❌ ERRADO — genérico
|
||||
"Tem certeza que deseja excluir?"
|
||||
"Confirmar operação?"
|
||||
|
||||
// ✅ CORRETO — descreve o impacto
|
||||
"Excluir esta condicionante? O prazo de cumprimento e os documentos vinculados serão removidos."
|
||||
"Arquivar esta licença? Ela não aparecerá mais nas listagens ativas."
|
||||
```
|
||||
|
||||
### 5. Labels e placeholders devem usar vocabulário do domínio
|
||||
|
||||
```typescript
|
||||
// ❌ ERRADO — genérico
|
||||
label: "Nome"
|
||||
placeholder: "Digite aqui"
|
||||
|
||||
// ✅ CORRETO — específico do domínio
|
||||
label: "Razão Social do Empreendedor"
|
||||
placeholder: "Ex: Mineração Vale Verde Ltda."
|
||||
|
||||
label: "Área Total do Empreendimento"
|
||||
placeholder: "Em hectares (ex: 150,5)"
|
||||
```
|
||||
|
||||
### 6. Schemas Zod devem ter mensagens de domínio
|
||||
|
||||
```typescript
|
||||
// ❌ ERRADO — mensagens padrão do Zod
|
||||
const zEmpreendimento = z.object({
|
||||
nome: z.string().min(1), // "String must contain at least 1 character(s)"
|
||||
area: z.number().positive(), // "Number must be greater than 0"
|
||||
})
|
||||
|
||||
// ✅ CORRETO — mensagens customizadas
|
||||
const zEmpreendimento = z.object({
|
||||
nome: z.string().min(1, "Informe o nome do empreendimento"),
|
||||
area: z.number({ invalid_type_error: "A área deve ser um número" })
|
||||
.positive("A área deve ser maior que zero"),
|
||||
cnpj: z.string().regex(/^\d{2}\.\d{3}\.\d{3}\/\d{4}-\d{2}$/,
|
||||
"CNPJ inválido. Use o formato 00.000.000/0000-00"),
|
||||
})
|
||||
```
|
||||
|
||||
## Impacto
|
||||
|
||||
Sem esta regra, o agente gera mensagens técnicas que confundem gestores ambientais, consultores e técnicos de campo — forçando a equipe de suporte a traduzir erros de sistema para o vocabulário do cliente.
|
||||
|
||||
## Referências
|
||||
|
||||
- Rule: `.agent/rules/zod-validacao-padrao.global.md` (schemas com mensagens customizadas)
|
||||
- Workflow: `.agent/workflows/ux.global.md` (design de interface)
|
||||
120
.agent/rules/regras-negocio-md.global.md
Normal file
120
.agent/rules/regras-negocio-md.global.md
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
---
|
||||
type: rule
|
||||
name: regras-negocio-md
|
||||
description: Arquivo {modulo}.regras-negocio.md obrigatório para módulos com lógica complexa
|
||||
scope: global
|
||||
trigger: always_on
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Regra: Arquivo `{modulo}.regras-negocio.md` Obrigatório para Módulos com Lógica Complexa
|
||||
|
||||
> **Escopo global** — aplica-se a todos os projetos do ecossistema Azteca.
|
||||
|
||||
## Contexto
|
||||
|
||||
Qualquer arquivo TypeScript que contenha lógica de negócio não-trivial — múltiplas regras de decisão, critérios de bloqueio, estratégias de processamento — deve ter suas regras documentadas em linguagem natural em um arquivo Markdown co-localizado.
|
||||
|
||||
Isso garante que produto, suporte e desenvolvimento possam entender as regras sem precisar ler TypeScript, e que o agente de IA encontre rapidamente os critérios corretos antes de alterar qualquer código.
|
||||
|
||||
## Regra
|
||||
|
||||
**Todo módulo com lógica de negócio não-trivial DEVE ter um arquivo `{modulo}.regras-negocio.md` co-localizado.**
|
||||
|
||||
Considera-se lógica não-trivial qualquer módulo que:
|
||||
- possui 3 ou mais critérios de decisão distintos
|
||||
- realiza eleição/priorização entre registros
|
||||
- aplica diferentes estratégias por tipo de dado
|
||||
- pode resultar em revisão manual (caminho alternativo ao fluxo automático)
|
||||
- é consumido por CRON, worker ou importador automático
|
||||
|
||||
O módulo TypeScript deve conter, na primeira linha do arquivo, a referência:
|
||||
|
||||
```typescript
|
||||
// Regras de negócio: ver {modulo}.regras-negocio.md
|
||||
```
|
||||
|
||||
O arquivo `.md` é a **fonte de verdade**. Os JSDoc do código devem referenciar o `.md`, não duplicá-lo.
|
||||
|
||||
## Estrutura Obrigatória do Arquivo
|
||||
|
||||
```markdown
|
||||
# Regras de Negócio — [Nome do Módulo]
|
||||
|
||||
Módulo: `_arquivo.ts`
|
||||
Localização: `caminho/relativo/ao/arquivo`
|
||||
|
||||
---
|
||||
|
||||
## Contexto
|
||||
Por que este módulo existe, qual problema resolve.
|
||||
|
||||
## Critérios de Entrada / Identificação
|
||||
O que aciona o módulo, quais dados recebe, filtros aplicados.
|
||||
|
||||
## Regras de Processamento
|
||||
As regras numeradas em ordem de avaliação.
|
||||
|
||||
## Casos de Revisão Manual (quando aplicável)
|
||||
Quando o processamento automático não pode prosseguir.
|
||||
|
||||
## Estratégia de Fusão / Resultado (quando aplicável)
|
||||
O que o módulo produz, como campos são combinados.
|
||||
|
||||
## Dependências Externas
|
||||
Tabelas, filas, serviços externos tocados pelo módulo.
|
||||
```
|
||||
|
||||
## Exemplos
|
||||
|
||||
✅ **Correto** — referência no topo do módulo, regras no `.md`:
|
||||
```typescript
|
||||
// Regras de negócio: ver juntar_execucoes.regras-negocio.md
|
||||
|
||||
import { ... } from "..."
|
||||
|
||||
export const compilarJuntarExecucoes = async (...) => {
|
||||
// implementação conforme regras-negocio.md
|
||||
}
|
||||
```
|
||||
|
||||
❌ **Errado** — regras embutidas no código como comentário de bloco:
|
||||
```typescript
|
||||
/**
|
||||
* Regras de negócio:
|
||||
* [x] Pode ter apenas uma execução por data de vencimento.
|
||||
* [x] Se tiver apenas uma execução consolidada → ela é a principal.
|
||||
* ...
|
||||
*/
|
||||
```
|
||||
|
||||
❌ **Errado** — JSDoc duplicando as regras em vez de referenciar o `.md`:
|
||||
```typescript
|
||||
/**
|
||||
* Retorna false quando:
|
||||
* - vencimentos divergentes
|
||||
* - executada com datas distintas
|
||||
* - vencimento_prorrogado distintos
|
||||
* ...
|
||||
*/
|
||||
```
|
||||
|
||||
## Impacto
|
||||
|
||||
Sem este padrão:
|
||||
- Regras de negócio ficam implícitas no código ou em JSDoc que envelhece sem controle
|
||||
- O agente de IA precisa inferir regras do código ao invés de lê-las diretamente
|
||||
- Produto e suporte não conseguem validar o comportamento sem ajuda técnica
|
||||
- Mudanças de regra geram JSDoc desatualizado espalhado pelo código
|
||||
|
||||
## Aplicação
|
||||
|
||||
- Módulos de CRON com lógica de auditoria/fusão
|
||||
- Importadores automáticos (ex: importação de licenças via IA)
|
||||
- Workers de processamento em lote
|
||||
- Qualquer utilitário com lógica de decisão multi-critério
|
||||
|
||||
## Referências
|
||||
|
||||
- Skill: `.agent/skills/documentar-regras-negocio.global/SKILL.md`
|
||||
- Workflow: `.agent/workflows/documentar-regras-negocio.global.md`
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
---
|
||||
type: rule
|
||||
name: typescript-sem-any
|
||||
trigger: always_on
|
||||
description: Proíbe uso de any, casts inseguros e padroniza uso de enums em código TypeScript
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
globs:
|
||||
- "**/*.ts"
|
||||
- "**/*.tsx"
|
||||
|
|
@ -68,6 +72,6 @@ Se encontrar uma string que represente um valor de domínio do sistema, o agente
|
|||
|
||||
## Referências
|
||||
|
||||
- Skill: `.agent/skills/diagnosticar-erros-build/SKILL.md` (quando erros de tipagem causam falhas de build em cascata)
|
||||
- Workflow: `.agent/workflows/documentar-codigo.global.md` (padrão de documentação para casts temporários)
|
||||
- Skill: `.agent/skills/diagnosticar-erros-build.global/SKILL.md` (quando erros de tipagem causam falhas de build em cascata)
|
||||
- Workflow: `.agent/workflows/otimizar-codigos.global.md` (padrão de documentação para casts temporários)
|
||||
|
||||
|
|
|
|||
315
.agent/rules/zod-validacao-padrao.global.md
Normal file
315
.agent/rules/zod-validacao-padrao.global.md
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
---
|
||||
type: rule
|
||||
name: zod-validacao-padrao
|
||||
description: Estabelece Zod como biblioteca obrigatória de validação em todo o ecossistema Azteca
|
||||
version: "1.0"
|
||||
scope: global
|
||||
priority: alta
|
||||
trigger: always_on
|
||||
globs:
|
||||
- "**/*.ts"
|
||||
- "**/*.tsx"
|
||||
- "**/*.vue"
|
||||
alwaysApply: true
|
||||
updatedAt: "2026-06-01"
|
||||
references:
|
||||
- .agent/rules/typescript-sem-any.global.md
|
||||
- .agent/skills/criar-schema-zod.global/SKILL.md
|
||||
---
|
||||
|
||||
# Regra: Zod como Validador Padrão
|
||||
|
||||
## Contexto
|
||||
|
||||
Todo projeto do ecossistema Azteca que receba dados externos — API, formulário, webhook, importação, fila, variáveis de ambiente — **deve** validar esses dados com [Zod](https://zod.dev).
|
||||
|
||||
Zod é a única biblioteca de validação autorizada porque:
|
||||
|
||||
- Inferência nativa de tipos TypeScript (`z.infer<>`) — elimina duplicação entre schema e tipo
|
||||
- Bundle pequeno (~13KB gzipped) — viável no frontend
|
||||
- Compartilhável entre back e front via pacotes `*-comuns`
|
||||
- Ecossistema maduro: `zod-to-json-schema`, `@anatine/zod-nestjs`, integração com `tRPC`
|
||||
|
||||
---
|
||||
|
||||
## Regra
|
||||
|
||||
### 1. Toda entrada externa deve ser validada com Zod
|
||||
|
||||
Entradas externas incluem:
|
||||
|
||||
- **Corpo de requisição** (API REST, webhook, GraphQL)
|
||||
- **Query params e path params**
|
||||
- **Formulários de frontend**
|
||||
- **Dados de fila** (BullMQ, Redis, etc.)
|
||||
- **Arquivos importados** (CSV, JSON, Excel)
|
||||
- **Variáveis de ambiente**
|
||||
- **Respostas de APIs externas** (quando a resposta não é confiável)
|
||||
|
||||
### 2. Schemas devem residir no pacote `*-comuns`
|
||||
|
||||
Para que front e back compartilhem os mesmos schemas, eles devem residir no pacote `<modulo>-comuns`:
|
||||
|
||||
```
|
||||
<modulo>-comuns/src/
|
||||
├── tabelas/
|
||||
│ ├── usuarios/
|
||||
│ │ ├── zUsuarioCriar.ts
|
||||
│ │ ├── zUsuarioEditar.ts
|
||||
│ │ └── index.ts
|
||||
│ ├── empresas/
|
||||
│ │ ├── zEmpresaCriar.ts
|
||||
│ │ └── index.ts
|
||||
│ └── index.ts
|
||||
```
|
||||
|
||||
### 3. Tipos devem ser derivados dos schemas (nunca o inverso)
|
||||
|
||||
```typescript
|
||||
// ✅ CORRETO — tipo derivado do schema
|
||||
export const zUsuarioCriar = z.object({
|
||||
nome: z.string().min(1),
|
||||
email: z.string().email(),
|
||||
ativo: z.boolean().default(true),
|
||||
})
|
||||
export type tipoUsuarioCriar = z.infer<typeof zUsuarioCriar>
|
||||
|
||||
// ❌ ERRADO — tipo manual separado do schema
|
||||
interface UsuarioCriar {
|
||||
nome: string
|
||||
email: string
|
||||
ativo?: boolean
|
||||
}
|
||||
const zUsuarioCriar = z.object({ ... }) // duplicação!
|
||||
```
|
||||
|
||||
### 4. Nomenclatura obrigatória de schemas
|
||||
|
||||
| Padrão | Uso | Exemplo |
|
||||
|--------|-----|---------|
|
||||
| `z{Entidade}Criar` | Criação de registro | `zUsuarioCriar` |
|
||||
| `z{Entidade}Editar` | Edição de registro | `zUsuarioEditar` |
|
||||
| `z{Entidade}Filtro` | Filtros de listagem | `zUsuarioFiltro` |
|
||||
| `z{Entidade}Resposta` | Resposta de API | `zUsuarioResposta` |
|
||||
| `z{Entidade}Params` | Parâmetros de rota | `zUsuarioParams` |
|
||||
| `z{Modulo}Config` | Configuração do módulo | `zGeeConfig` |
|
||||
| `z{Modulo}Ambiente` | Variáveis de ambiente | `zBackAmbiente` |
|
||||
|
||||
Prefixo `z` minúsculo + nome em `PascalCase`.
|
||||
|
||||
### 5. Usar `safeParse` em vez de `parse` para erros controlados
|
||||
|
||||
```typescript
|
||||
// ✅ CORRETO — erro controlado
|
||||
const resultado = zUsuarioCriar.safeParse(dados)
|
||||
if (!resultado.success) {
|
||||
return respostaComuns.erro(resultado.error.issues[0].message)
|
||||
}
|
||||
const usuario = resultado.data // tipado automaticamente
|
||||
|
||||
// ❌ ERRADO — throw não controlado
|
||||
const usuario = zUsuarioCriar.parse(dados) // lança ZodError
|
||||
```
|
||||
|
||||
A exceção é em contextos onde o throw é desejável (middleware global, início de pipeline).
|
||||
|
||||
### 6. Nunca usar validação manual quando Zod resolve
|
||||
|
||||
```typescript
|
||||
// ❌ ERRADO — validação manual
|
||||
if (!dados.nome || typeof dados.nome !== "string") {
|
||||
return erro("Nome é obrigatório")
|
||||
}
|
||||
if (!dados.email || !dados.email.includes("@")) {
|
||||
return erro("Email inválido")
|
||||
}
|
||||
|
||||
// ✅ CORRETO — Zod faz tudo
|
||||
const resultado = zUsuarioCriar.safeParse(dados)
|
||||
if (!resultado.success) {
|
||||
return erro(resultado.error.issues[0].message)
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Transformações e defaults pertencem ao schema
|
||||
|
||||
```typescript
|
||||
// ✅ CORRETO — transformação no schema
|
||||
export const zProdutoCriar = z.object({
|
||||
nome: z.string().trim().min(1),
|
||||
preco: z.coerce.number().positive(),
|
||||
ativo: z.boolean().default(true),
|
||||
codigo: z.string().toUpperCase().optional(),
|
||||
tags: z.array(z.string()).default([]),
|
||||
})
|
||||
|
||||
// ❌ ERRADO — transformação manual após validação
|
||||
const produto = zProdutoCriar.parse(dados)
|
||||
produto.nome = produto.nome.trim()
|
||||
produto.codigo = produto.codigo?.toUpperCase()
|
||||
```
|
||||
|
||||
### 8. Composição de schemas em vez de duplicação
|
||||
|
||||
```typescript
|
||||
// ✅ CORRETO — composição
|
||||
const zEndereco = z.object({
|
||||
rua: z.string(),
|
||||
cidade: z.string(),
|
||||
uf: z.string().length(2),
|
||||
})
|
||||
|
||||
const zEmpresaCriar = z.object({
|
||||
nome: z.string(),
|
||||
endereco: zEndereco,
|
||||
})
|
||||
|
||||
const zEmpresaEditar = zEmpresaCriar.partial() // todos opcionais
|
||||
|
||||
const zEmpresaResposta = zEmpresaCriar.extend({
|
||||
codigo: z.string().uuid(),
|
||||
criadoEm: z.string().datetime(),
|
||||
})
|
||||
|
||||
// ❌ ERRADO — copiar campos entre schemas
|
||||
const zEmpresaEditar = z.object({
|
||||
nome: z.string().optional(), // duplicou!
|
||||
endereco: z.object({ // duplicou!
|
||||
rua: z.string().optional(),
|
||||
cidade: z.string().optional(),
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
### 9. Enums Zod para valores de domínio
|
||||
|
||||
```typescript
|
||||
// ✅ CORRETO — enum Zod quando os valores vêm de dados externos
|
||||
export const zStatusLicenca = z.enum(["ativa", "vencida", "suspensa", "cancelada"])
|
||||
export type tipoStatusLicenca = z.infer<typeof zStatusLicenca>
|
||||
|
||||
// Usar em outros schemas
|
||||
const zLicencaCriar = z.object({
|
||||
status: zStatusLicenca,
|
||||
// ...
|
||||
})
|
||||
|
||||
// ✅ TAMBÉM CORRETO — TypeScript enum quando já existe no projeto
|
||||
// e o schema referencia o enum
|
||||
import { StatusLicenca } from "./enums"
|
||||
const zLicencaCriar = z.object({
|
||||
status: z.nativeEnum(StatusLicenca),
|
||||
})
|
||||
```
|
||||
|
||||
### 10. Bibliotecas proibidas para validação
|
||||
|
||||
| ❌ Proibido | Motivo |
|
||||
|-------------|--------|
|
||||
| `joi` | Não infere tipos TS, bundle pesado |
|
||||
| `yup` | Inferência fraca, API menos ergonômica |
|
||||
| `class-validator` | Decorators, não compartilhável com frontend |
|
||||
| `io-ts` | Verboso, curva de aprendizado alta |
|
||||
| `ajv` (direto) | JSON Schema — usar `zod-to-json-schema` se precisar de JSON Schema |
|
||||
| Validação manual com `if/typeof` | Quando Zod resolve o mesmo caso |
|
||||
|
||||
---
|
||||
|
||||
## Exemplos
|
||||
|
||||
### API backend (Express)
|
||||
|
||||
```typescript
|
||||
// ✅ Correto
|
||||
import { zUsuarioCriar } from "projeto-comuns/tabelas/usuarios"
|
||||
|
||||
app.post("/usuarios", async (req, res) => {
|
||||
const resultado = zUsuarioCriar.safeParse(req.body)
|
||||
if (!resultado.success) {
|
||||
return res.status(400).json({ erro: resultado.error.issues })
|
||||
}
|
||||
const usuario = await criarUsuario(resultado.data)
|
||||
return res.json(usuario)
|
||||
})
|
||||
```
|
||||
|
||||
### Webhook com fila
|
||||
|
||||
```typescript
|
||||
// ✅ Correto — valida antes de enfileirar
|
||||
const zPayloadWebhook = z.object({
|
||||
tabela: z.enum(["empreendedores", "empreendimentos", "usuarios"]),
|
||||
dados: z.array(z.unknown()).min(1),
|
||||
})
|
||||
|
||||
app.post("/webhook/:tabela", async (req, res) => {
|
||||
const resultado = zPayloadWebhook.safeParse({
|
||||
tabela: req.params.tabela,
|
||||
dados: req.body,
|
||||
})
|
||||
if (!resultado.success) {
|
||||
return res.status(400).json({ erro: "Payload inválido" })
|
||||
}
|
||||
await fila.add("sync", resultado.data)
|
||||
return res.json({ ok: true })
|
||||
})
|
||||
```
|
||||
|
||||
### Variáveis de ambiente
|
||||
|
||||
```typescript
|
||||
// ✅ Correto — validar no boot da aplicação
|
||||
const zAmbiente = z.object({
|
||||
DATABASE_URL: z.string().url(),
|
||||
REDIS_HOST: z.string().default("localhost"),
|
||||
REDIS_PORT: z.coerce.number().default(6379),
|
||||
BASE_URL: z.string().url(),
|
||||
SESSAO_BACK: z.string().uuid(),
|
||||
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
|
||||
})
|
||||
|
||||
export const ambiente = () => zAmbiente.parse(process.env)
|
||||
```
|
||||
|
||||
### Frontend (formulário Vue)
|
||||
|
||||
```typescript
|
||||
// ✅ Correto — schema compartilhado validando antes de enviar
|
||||
import { zUsuarioCriar } from "projeto-comuns/tabelas/usuarios"
|
||||
|
||||
const salvar = async () => {
|
||||
const resultado = zUsuarioCriar.safeParse(formulario.value)
|
||||
if (!resultado.success) {
|
||||
erros.value = resultado.error.flatten().fieldErrors
|
||||
return
|
||||
}
|
||||
await api.post("/usuarios", resultado.data)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Impacto
|
||||
|
||||
Sem esta regra:
|
||||
|
||||
- Cada desenvolvedor escolhe uma estratégia diferente de validação (if/typeof, joi, yup, etc.)
|
||||
- Tipos e schemas ficam dessincronizados — mudança no schema não reflete no tipo e vice-versa
|
||||
- Validações manuais são incompletas e inconsistentes entre endpoints
|
||||
- Código de validação é duplicado entre front e back
|
||||
- Dados inválidos passam despercebidos e corrompem a base
|
||||
|
||||
Com esta regra:
|
||||
|
||||
- Uma única fonte de verdade: o schema Zod
|
||||
- Tipos TypeScript sempre sincronizados via `z.infer<>`
|
||||
- Validação consistente em todos os pontos de entrada
|
||||
- Reutilização entre front, back e workers via pacote `*-comuns`
|
||||
- Erros de validação estruturados e internacionalizáveis
|
||||
|
||||
---
|
||||
|
||||
## Referências
|
||||
|
||||
- Rule: `.agent/rules/typescript-sem-any.global.md` (narrowing com Zod mencionado como alternativa)
|
||||
- Skill: `.agent/skills/criar-schema-zod.global/SKILL.md` (guia prático para criar schemas)
|
||||
184
.agent/skills/criar-schema-zod.global/SKILL.md
Normal file
184
.agent/skills/criar-schema-zod.global/SKILL.md
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
---
|
||||
name: criar-schema-zod
|
||||
scope: global
|
||||
description: >
|
||||
Guia operacional para criar, organizar e manter schemas de validação Zod em projetos TypeScript.
|
||||
---
|
||||
|
||||
# Skill: Criar Schema Zod
|
||||
|
||||
Use esta skill ao adicionar validação de dados em qualquer ponto de entrada do sistema — API, formulário, webhook, importação, fila ou variáveis de ambiente.
|
||||
|
||||
---
|
||||
|
||||
## PASSO 1 — Identificar a Entrada Externa
|
||||
|
||||
Determinar qual dado externo precisa de validação:
|
||||
|
||||
| Tipo de entrada | Onde validar |
|
||||
|---|---|
|
||||
| Corpo de requisição (POST/PUT) | Handler da rota, antes de qualquer lógica |
|
||||
| Query/path params | Handler da rota |
|
||||
| Formulário frontend | Antes de chamar a API |
|
||||
| Dados de fila (BullMQ) | No processador da fila |
|
||||
| Arquivo importado (CSV/JSON) | No parser, linha a linha |
|
||||
| Variáveis de ambiente | No boot da aplicação |
|
||||
| Resposta de API externa | Após receber a resposta |
|
||||
|
||||
---
|
||||
|
||||
## PASSO 2 — Localizar ou Criar o Schema no Pacote Comuns
|
||||
|
||||
Schemas compartilhados vivem no pacote `*-comuns`:
|
||||
|
||||
```
|
||||
projeto-comuns/src/tabelas/
|
||||
├── usuarios/
|
||||
│ ├── zUsuarioCriar.ts
|
||||
│ ├── zUsuarioEditar.ts
|
||||
│ └── index.ts
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
### 2.1. Verificar se já existe
|
||||
|
||||
```bash
|
||||
rg "z{NomeDaEntidade}" --include='*.ts' -l
|
||||
```
|
||||
|
||||
Se já existir → reutilizar ou compor a partir dele (`.extend()`, `.pick()`, `.partial()`).
|
||||
|
||||
### 2.2. Criar o schema
|
||||
|
||||
```typescript
|
||||
import { z } from "zod"
|
||||
|
||||
export const zUsuarioCriar = z.object({
|
||||
nome: z.string().trim().min(1, "Nome é obrigatório"),
|
||||
email: z.string().email("E-mail inválido"),
|
||||
ativo: z.boolean().default(true),
|
||||
})
|
||||
```
|
||||
|
||||
### 2.3. Nomenclatura obrigatória
|
||||
|
||||
| Padrão | Uso |
|
||||
|---|---|
|
||||
| `z{Entidade}Criar` | Criação |
|
||||
| `z{Entidade}Editar` | Edição (geralmente `.partial()`) |
|
||||
| `z{Entidade}Filtro` | Filtros de listagem |
|
||||
| `z{Entidade}Resposta` | Resposta de API |
|
||||
| `z{Entidade}Params` | Parâmetros de rota |
|
||||
| `z{Modulo}Config` | Configuração |
|
||||
| `z{Modulo}Ambiente` | Variáveis de ambiente |
|
||||
|
||||
---
|
||||
|
||||
## PASSO 3 — Derivar o Tipo com `z.infer<>`
|
||||
|
||||
```typescript
|
||||
export type tipoUsuarioCriar = z.infer<typeof zUsuarioCriar>
|
||||
```
|
||||
|
||||
**Nunca** criar um `interface` ou `type` manual separado do schema. O tipo **sempre** deriva do schema.
|
||||
|
||||
---
|
||||
|
||||
## PASSO 4 — Implementar `safeParse` no Ponto de Entrada
|
||||
|
||||
### Backend (Express)
|
||||
|
||||
```typescript
|
||||
import { zUsuarioCriar } from "projeto-comuns/tabelas/usuarios"
|
||||
|
||||
app.post("/usuarios", async (req, res) => {
|
||||
const resultado = zUsuarioCriar.safeParse(req.body)
|
||||
if (!resultado.success) {
|
||||
return res.status(400).json({ erro: resultado.error.issues[0].message })
|
||||
}
|
||||
const usuario = await criarUsuario(resultado.data)
|
||||
return res.json(usuario)
|
||||
})
|
||||
```
|
||||
|
||||
### Frontend (Vue)
|
||||
|
||||
```typescript
|
||||
import { zUsuarioCriar } from "projeto-comuns/tabelas/usuarios"
|
||||
|
||||
const salvar = async () => {
|
||||
const resultado = zUsuarioCriar.safeParse(formulario.value)
|
||||
if (!resultado.success) {
|
||||
erros.value = resultado.error.flatten().fieldErrors
|
||||
return
|
||||
}
|
||||
await api.post("/usuarios", resultado.data)
|
||||
}
|
||||
```
|
||||
|
||||
### Variáveis de ambiente
|
||||
|
||||
```typescript
|
||||
const zAmbiente = z.object({
|
||||
DATABASE_URL: z.string().url(),
|
||||
REDIS_HOST: z.string().default("localhost"),
|
||||
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
|
||||
})
|
||||
|
||||
// Aqui o throw é desejável — a app não deve subir com env inválido
|
||||
export const ambiente = () => zAmbiente.parse(process.env)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PASSO 5 — Compor Schemas Derivados
|
||||
|
||||
Quando precisar de variações do mesmo schema, **compor** em vez de duplicar:
|
||||
|
||||
```typescript
|
||||
// Schema base
|
||||
const zProduto = z.object({
|
||||
nome: z.string().trim().min(1),
|
||||
preco: z.coerce.number().positive(),
|
||||
categoria: z.string(),
|
||||
})
|
||||
|
||||
// Edição: todos os campos opcionais
|
||||
const zProdutoEditar = zProduto.partial()
|
||||
|
||||
// Resposta: base + campos gerados
|
||||
const zProdutoResposta = zProduto.extend({
|
||||
codigo: z.string().uuid(),
|
||||
criadoEm: z.string().datetime(),
|
||||
})
|
||||
|
||||
// Filtro: subset de campos opcionais
|
||||
const zProdutoFiltro = zProduto.pick({ categoria: true }).partial()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PASSO 6 — Validar Tipagem
|
||||
|
||||
```bash
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
Zero erros = concluído.
|
||||
|
||||
---
|
||||
|
||||
## Checklist Final
|
||||
|
||||
- [ ] O schema está no pacote `*-comuns` (quando compartilhado entre front/back)?
|
||||
- [ ] O tipo é derivado do schema com `z.infer<>`?
|
||||
- [ ] A validação usa `safeParse` (exceto em boot/middleware)?
|
||||
- [ ] A nomenclatura segue o padrão `z{Entidade}{Acao}`?
|
||||
- [ ] Transformações (trim, coerce, default) estão no schema, não no handler?
|
||||
- [ ] Schemas derivados usam composição (.partial, .extend, .pick), não duplicação?
|
||||
- [ ] `tsc --noEmit` passa sem erros?
|
||||
|
||||
## Referências
|
||||
|
||||
- Rule: `.agent/rules/zod-validacao-padrao.global.md`
|
||||
- Rule: `.agent/rules/typescript-sem-any.global.md`
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
name: diagnosticar-erros-build
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
description: >
|
||||
Guia operacional para diagnosticar e resolver erros de build em cascata em monorepos TypeScript.
|
||||
---
|
||||
|
|
@ -181,5 +182,5 @@ pnpm install && pnpm build
|
|||
## Referências
|
||||
|
||||
- Rules: `.agent/rules/typescript-sem-any.global.md`
|
||||
- Workflow: `.agent/workflows/documentar-codigo.global.md`
|
||||
- Skill: `.agent/skills/mapear-codigo-fonte/SKILL.md`
|
||||
- Workflow: `.agent/workflows/otimizar-codigos.global.md`
|
||||
- Skill: `.agent/skills/mapear-codigo-fonte.global/SKILL.md`
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
name: documentar-decisao-tecnica
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
description: >
|
||||
Guia operacional para registrar decisões técnicas e arquiteturais como ADRs (Architecture Decision Records).
|
||||
---
|
||||
|
|
@ -157,4 +158,4 @@ Antes de considerar completo:
|
|||
## Referências
|
||||
|
||||
- Workflow: `.agent/workflows/aprimorar-IA.global.md` (seção de classificação de artefatos)
|
||||
- Workflow: `.agent/workflows/documentar-codigo.global.md`
|
||||
- Workflow: `.agent/workflows/otimizar-codigos.global.md`
|
||||
|
|
|
|||
131
.agent/skills/documentar-dominio-modulo.global/SKILL.md
Normal file
131
.agent/skills/documentar-dominio-modulo.global/SKILL.md
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
---
|
||||
name: documentar-dominio-modulo
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
description: >
|
||||
Guia para documentar o domínio científico/técnico de cada módulo SaaS,
|
||||
garantindo que o agente compreenda a terminologia e as regras do domínio
|
||||
ao gerar código, texto e interface.
|
||||
---
|
||||
|
||||
# Skill: Documentar Domínio de um Módulo
|
||||
|
||||
Use esta skill ao iniciar um novo módulo SaaS ou ao perceber que o agente não conhece o vocabulário e as regras do domínio que o módulo aborda.
|
||||
|
||||
Cada módulo opera em uma disciplina diferente da ciência/gestão ambiental (licenciamento, gases de efeito estufa, resíduos, projetos, etc.). O agente precisa de uma **base factual do domínio** para gerar labels, mensagens, regras de negócio e textos comerciais corretos.
|
||||
|
||||
---
|
||||
|
||||
## PASSO 1 — Identificar o Domínio do Módulo
|
||||
|
||||
Determinar qual área da ciência/gestão o módulo aborda:
|
||||
|
||||
| Exemplo de módulo | Domínio |
|
||||
|---|---|
|
||||
| Licenciamento | Licenciamento ambiental (LP, LI, LO, condicionantes) |
|
||||
| GEE | Gases de Efeito Estufa (inventário, emissões, fatores) |
|
||||
| Resíduos | Gestão de resíduos sólidos (PGRS, MTR, classificação) |
|
||||
| Outorga | Outorga de uso de recursos hídricos |
|
||||
| Flora/Fauna | Monitoramento de biodiversidade |
|
||||
| Projetos | Gestão de projetos ambientais e consultorias |
|
||||
|
||||
---
|
||||
|
||||
## PASSO 2 — Criar o Arquivo de Domínio
|
||||
|
||||
Criar em `.agent/memoria/dominio-<modulo>.md`:
|
||||
|
||||
```markdown
|
||||
# Domínio: <Nome do Módulo>
|
||||
|
||||
## Área de Conhecimento
|
||||
Breve descrição da disciplina (1-3 linhas).
|
||||
|
||||
## Glossário do Módulo
|
||||
|
||||
| Termo | Definição | Sigla |
|
||||
|---|---|---|
|
||||
| Licença Prévia | Autorização para planejamento do empreendimento | LP |
|
||||
| Licença de Instalação | Autorização para início das obras | LI |
|
||||
| ... | ... | ... |
|
||||
|
||||
## Entidades Principais
|
||||
|
||||
Lista das entidades de negócio do módulo com descrição e relações:
|
||||
|
||||
- **Empreendimento**: unidade física onde ocorre a atividade potencialmente poluidora
|
||||
- **Licença**: autorização ambiental vinculada a um empreendimento
|
||||
- **Condicionante**: obrigação imposta pelo órgão ambiental na licença
|
||||
- ...
|
||||
|
||||
## Legislação Aplicável
|
||||
|
||||
| Norma | Descrição | Relevância |
|
||||
|---|---|---|
|
||||
| Lei 6.938/1981 | Política Nacional do Meio Ambiente | Base legal do licenciamento |
|
||||
| Resolução CONAMA 237/1997 | Regulamenta o licenciamento ambiental | Tipos de licença, prazos |
|
||||
| ... | ... | ... |
|
||||
|
||||
## Regras de citação legal
|
||||
- Formato: "Resolução CONAMA nº 237/1997, art. 8º, inciso I"
|
||||
- Toda sigla na primeira ocorrência com nome por extenso
|
||||
- Legislação federal > estadual > municipal (hierarquia)
|
||||
|
||||
## Fluxos do Domínio
|
||||
|
||||
Descrever os processos-chave que o módulo implementa:
|
||||
|
||||
1. Solicitação de licença → Análise técnica → Emissão → Monitoramento
|
||||
2. Cadastro de condicionante → Acompanhamento de prazo → Cumprimento
|
||||
|
||||
## Vocabulário de Interface
|
||||
|
||||
Termos que devem ser usados em labels, botões e mensagens:
|
||||
|
||||
| Contexto | ❌ Genérico | ✅ Correto para o módulo |
|
||||
|---|---|---|
|
||||
| Campo de nome | "Nome" | "Razão Social do Empreendedor" |
|
||||
| Status | "Ativo/Inativo" | "Vigente/Vencida/Suspensa" |
|
||||
| Ação principal | "Criar novo" | "Solicitar nova licença" |
|
||||
| Lista vazia | "Nenhum registro" | "Nenhuma licença cadastrada" |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PASSO 3 — Validar com o Operador
|
||||
|
||||
Antes de consolidar:
|
||||
|
||||
- [ ] Os termos do glossário estão corretos para o contexto do módulo?
|
||||
- [ ] As entidades cobrem os conceitos centrais?
|
||||
- [ ] A legislação listada é a principal (não exaustiva)?
|
||||
- [ ] O vocabulário de interface reflete como o usuário fala no dia a dia?
|
||||
|
||||
---
|
||||
|
||||
## PASSO 4 — Conectar com as Rules
|
||||
|
||||
Após documentar o domínio:
|
||||
|
||||
1. Verificar se labels e mensagens no código usam o vocabulário definido (rule `mensagens-usuario.global.md`)
|
||||
2. Verificar se schemas Zod têm mensagens de erro no idioma do domínio (rule `zod-validacao-padrao.global.md`)
|
||||
3. Verificar se os arquivos `.ajuda.md` usam termos do glossário (workflow `adicionar-ajuda.global.md`)
|
||||
4. Verificar se textos comerciais citam a legislação corretamente (workflow `comercial.global.md`)
|
||||
|
||||
---
|
||||
|
||||
## Checklist Final
|
||||
|
||||
- [ ] Arquivo `dominio-<modulo>.md` criado em `.agent/memoria/`?
|
||||
- [ ] Glossário com pelo menos os 10 termos centrais do módulo?
|
||||
- [ ] Entidades principais listadas com descrição?
|
||||
- [ ] Legislação base documentada com formato de citação?
|
||||
- [ ] Vocabulário de interface definido (labels, status, ações, empty states)?
|
||||
- [ ] Operador validou a terminologia?
|
||||
|
||||
## Referências
|
||||
|
||||
- Rule: `.agent/rules/mensagens-usuario.global.md`
|
||||
- Rule: `.agent/rules/regras-negocio-md.global.md`
|
||||
- Workflow: `.agent/workflows/comercial.global.md`
|
||||
- Workflow: `.agent/workflows/adicionar-ajuda.global.md`
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
---
|
||||
name: documentar-regras-negocio.global
|
||||
name: documentar-regras-negocio
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
description: >
|
||||
Guia operacional para criar, preencher e manter arquivos {modulo}.regras-negocio.md
|
||||
co-localizados com módulos TypeScript de lógica de negócio complexa.
|
||||
|
|
@ -206,7 +208,5 @@ Percorrer cada regra listada no `.md` e verificar que há código correspondente
|
|||
|
||||
## Referências
|
||||
|
||||
- Rule: `.agent/rules/regras_negocio_md.global.md`
|
||||
- Rule: `.agent/rules/regras-negocio-md.global.md`
|
||||
- Workflow: `.agent/workflows/documentar-regras-negocio.global.md`
|
||||
- Skill: `.agent/skills/consultar-banco-knex/SKILL.md` (orderBy em whereIn)
|
||||
- ADR: `.agent/memoria/adr-003-crons-complexos-compilacao-execucao.md`
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
name: mapear-codigo-fonte
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
description: >
|
||||
Guia operacional para mapear, navegar e compreender a estrutura de um monorepo TypeScript.
|
||||
---
|
||||
|
|
@ -156,4 +157,4 @@ Descrever como os dados fluem entre pacotes.
|
|||
## Referências
|
||||
|
||||
- Rules: `.agent/rules/typescript-sem-any.global.md`
|
||||
- Workflow: `.agent/workflows/documentar-codigo.global.md`
|
||||
- Workflow: `.agent/workflows/otimizar-codigos.global.md`
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ Para instalar as ferramentas base de forma ágil e em lote, copie os blocos de c
|
|||
```bash
|
||||
# 1. Atualizar e instalar ferramentas base do sistema
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ripgrep fd-find jq shellcheck bat tree httpie gh
|
||||
sudo apt-get install -y ripgrep fd-find jq shellcheck bat tree eza httpie gh
|
||||
|
||||
# 2. Instalar ferramentas em Node
|
||||
npm install -g @ast-grep/cli @biomejs/biome
|
||||
|
|
|
|||
41
.agent/verificar-linhas.sh
Normal file
41
.agent/verificar-linhas.sh
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
# comando sh ../.agent/verificar-linhas.sh || exit 1
|
||||
|
||||
LIMITE=250
|
||||
|
||||
RAIZ=$(cd "$(dirname "$0")/.." && pwd)
|
||||
|
||||
RESULTADO=$(
|
||||
find "$RAIZ" \
|
||||
-type d \( \
|
||||
-name node_modules -o \
|
||||
-name .git -o \
|
||||
-name dist -o \
|
||||
-name build \
|
||||
\) -prune \
|
||||
-o \
|
||||
-type f \( \
|
||||
-name "*.ts" -o \
|
||||
-name "*.tsx" -o \
|
||||
-name "*.vue" -o \
|
||||
-name "*.css" \
|
||||
\) \
|
||||
-print | while IFS= read -r arquivo; do
|
||||
linhas=$(wc -l < "$arquivo" | tr -d ' ')
|
||||
if [ "$linhas" -gt "$LIMITE" ]; then
|
||||
relativo="${arquivo#$RAIZ/}"
|
||||
printf "%5s linhas -> %s\n" "$linhas" "$relativo"
|
||||
fi
|
||||
done
|
||||
)
|
||||
|
||||
if [ -n "$RESULTADO" ]; then
|
||||
echo "$RESULTADO"
|
||||
VIOLACOES=$(printf '%s\n' "$RESULTADO" | wc -l | tr -d ' ')
|
||||
printf "\n❌ %s arquivo(s) violam o limite de %s linhas.\n" "$VIOLACOES" "$LIMITE"
|
||||
printf "\n Resolva usando o arquivo: .agent/workflows/otimizar-codigos.global.md\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf "✅ Todos os arquivos dentro do limite de %s linhas.\n" "$LIMITE"
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
---
|
||||
type: workflow
|
||||
name: adicionar-ajuda
|
||||
description: Adicionar ou atualizar botão de ajuda e documentação em telas (Frontend)
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Workflow: Adicionar Ajuda em Telas
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
---
|
||||
type: workflow
|
||||
name: aprimorar-IA
|
||||
description: Consolidar aprendizados da conversa atual e evoluir continuamente as regras, skills e fluxos da aplicação
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Workflow: Aprimorar Conhecimento da Aplicação
|
||||
|
|
@ -74,7 +78,7 @@ Para cada item identificado, classificar obrigatoriamente:
|
|||
### Estrutura obrigatória:
|
||||
|
||||
- Um arquivo = um único assunto.
|
||||
- Nome claro em `snake_case`.
|
||||
- Nome claro em `kebab-case`.
|
||||
- Limite máximo: ~10.000 caracteres.
|
||||
|
||||
### É obrigatório:
|
||||
|
|
@ -196,105 +200,64 @@ Exemplos de skills de projeto:
|
|||
Sempre que decisões críticas sobre a arquitetura da aplicação ou o contexto do domínio precisarem ser arquivadas temporária ou permanentemente de maneira descritiva (e não diretamente injetável em código ou regra de lint):
|
||||
|
||||
* Registre o novo documento no formato adequado mantendo a pasta limpa em `.agent/memoria/`.
|
||||
* Exemplo: Manter diário de pendências (`diario_pendencias_modulo_x.md`) ou Arquivo de Registro de Decisão Arquitetural (`adr-001-migracao-nuxt-4.md`).
|
||||
* Atenção! Tudo que puder ser abstraído para um formato técnico estrito/guiado, deve virar **Skill** ou **Rule**. Use a Memória apenas como extensão de base factual.
|
||||
* Exemplo: Manter diário de pendências (`diario-pendencias-modulo-x.md`) ou ADR (`adr-001-migracao-nuxt-4.md`).
|
||||
* Atenção! Tudo que puder ser abstraído para formato técnico estrito deve virar **Skill** ou **Rule**. Use a Memória apenas como extensão de base factual.
|
||||
|
||||
---
|
||||
|
||||
## 6️⃣ Revisão Global de TODO o Sistema de Conhecimento (OBRIGATÓRIA E INEGOCIÁVEL)
|
||||
## 6️⃣ Revisão de Impacto (Dirigida por Grafo)
|
||||
|
||||
Esta é a etapa mais crítica.
|
||||
Quando um artefato é alterado, a revisão deve ser **dirigida**, não exaustiva.
|
||||
|
||||
Sempre que qualquer alteração ocorrer, é **OBRIGATÓRIO revisar TODO o sistema de conhecimento**, incluindo:
|
||||
### Processo:
|
||||
|
||||
* **TODOS os arquivos em `.agent/rules/`**
|
||||
* **TODOS os arquivos em `.agent/workflows/`**
|
||||
* **TODOS os arquivos em `.agent/skills/`**
|
||||
* **TODOS os arquivos em `.agent/memoria/`**
|
||||
1. Identificar o artefato alterado
|
||||
2. Consultar a seção `Referências` do artefato para encontrar dependentes diretos
|
||||
3. Verificar consistência APENAS entre o artefato alterado e seus dependentes
|
||||
4. Se uma inconsistência for encontrada, propagar para o próximo nível
|
||||
|
||||
### Regras de escopo:
|
||||
|
||||
| Se alterou... | Revisar... |
|
||||
|---|---|
|
||||
| Uma **Rule** | Workflows e skills que a **referenciam** |
|
||||
| Um **Workflow** | Skills que o **referenciam** |
|
||||
| Uma **Skill** | Rules e workflows na seção Referências |
|
||||
| **Memória/ADR** | Nada — memória é registro, não dependência |
|
||||
|
||||
### É PROIBIDO:
|
||||
|
||||
* Atualizar apenas um arquivo isolado.
|
||||
* Assumir que o impacto é local.
|
||||
* Ignorar arquivos existentes.
|
||||
* Criar artefatos sem validar o restante do sistema.
|
||||
* Revisar artefatos **sem relação** com a mudança
|
||||
* Afirmar que "tudo foi revisado" sem listar quais artefatos foram verificados
|
||||
* Criar artefatos sem verificar se já existe um com o mesmo escopo
|
||||
|
||||
### A revisão DEVE verificar:
|
||||
|
||||
* Não existem regras conflitantes entre o artefato alterado e seus dependentes
|
||||
* Referências cruzadas continuam válidas (arquivos existem, nomes corretos)
|
||||
* Skills refletem fielmente as rules que referenciam
|
||||
* Não há duplicação de conteúdo entre artefatos
|
||||
|
||||
---
|
||||
|
||||
### A revisão DEVE obrigatoriamente verificar:
|
||||
|
||||
#### Consistência
|
||||
|
||||
* Não existem regras conflitantes.
|
||||
* Não existem interpretações ambíguas.
|
||||
* Skills refletem fielmente as rules e workflows atuais.
|
||||
|
||||
#### Atualização
|
||||
|
||||
* Nenhuma rule está desatualizada.
|
||||
* Nenhum workflow usa regra antiga.
|
||||
* Nenhuma skill contém padrões obsoletos.
|
||||
|
||||
#### Não duplicação
|
||||
|
||||
* Não existem regras repetidas em arquivos diferentes.
|
||||
* Conteúdos redundantes foram consolidados.
|
||||
* Skills referenciam rules (não copiam o conteúdo).
|
||||
|
||||
#### Propagação de impacto
|
||||
|
||||
* Toda mudança foi refletida em:
|
||||
|
||||
* Rules relacionadas
|
||||
* Workflows relacionados
|
||||
* Skills relacionadas
|
||||
* Memória afetada pela mudança (ADRs, Diários)
|
||||
|
||||
#### Coerência estrutural
|
||||
|
||||
* Nomes de arquivos continuam válidos.
|
||||
* Assuntos estão bem separados.
|
||||
* Arquivos grandes foram divididos.
|
||||
* Skills estão organizadas em diretórios individuais.
|
||||
|
||||
---
|
||||
|
||||
### Regras obrigatórias desta etapa:
|
||||
|
||||
* **Toda alteração é considerada global até prova contrária.**
|
||||
* **Se uma rule muda, todos os workflows e skills relacionados DEVEM ser revisados.**
|
||||
* **Se um workflow muda, todas as rules e skills relacionadas DEVEM ser revisadas.**
|
||||
* **Se uma skill muda, as rules e workflows que ela referencia DEVEM ser verificados.**
|
||||
* **Atualizações de contexto devem garantir que toda a memória conflitante seja suprimida ou tagueada.**
|
||||
* **Nenhuma inconsistência pode permanecer.**
|
||||
* **Nenhuma duplicação é aceitável.**
|
||||
|
||||
---
|
||||
|
||||
## 7️⃣ Verificação Final (Checklist obrigatório)
|
||||
## 7⃣ Verificação Final (Checklist obrigatório)
|
||||
|
||||
Só é permitido encerrar se TODOS forem verdadeiros:
|
||||
|
||||
* [ ] Todas as rules foram revisadas
|
||||
* [ ] Todos os workflows foram revisados
|
||||
* [ ] Todas as skills foram revisadas
|
||||
* [ ] Não existe conflito entre artefatos
|
||||
* [ ] Artefatos dependentes do alterado foram revisados
|
||||
* [ ] Não existe conflito entre artefatos revisados
|
||||
* [ ] Não existe duplicação de conteúdo
|
||||
* [ ] Nenhuma regra ficou implícita
|
||||
* [ ] Todas as mudanças foram propagadas (rules ↔ workflows ↔ skills)
|
||||
* [ ] Referências cruzadas estão válidas
|
||||
* [ ] Estrutura está organizada por assunto
|
||||
* [ ] Nenhum arquivo excede ~10k caracteres
|
||||
* [ ] Workflows refletem fielmente as rules atuais
|
||||
* [ ] Skills referenciam (não duplicam) rules
|
||||
* [ ] O sistema está consistente como um todo
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Regra Suprema
|
||||
|
||||
> Se existir qualquer dúvida, conflito, duplicação ou inconsistência,
|
||||
> Se existir qualquer dúvida, conflito ou inconsistência
|
||||
> entre o artefato alterado e seus dependentes,
|
||||
> o trabalho é considerado **INCOMPLETO**.
|
||||
|
||||
Não existe atualização parcial.
|
||||
Não existe impacto local.
|
||||
Não existe exceção.
|
||||
A revisão é dirigida, não opcional.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
---
|
||||
type: workflow
|
||||
name: atualizar-agente
|
||||
description: Aplica atualizações globais para o agente de IA
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Workflow: Atualizar Agente Global
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
---
|
||||
type: workflow
|
||||
name: comercial
|
||||
description: Criar uma descrição comercial das funcionalidades e do sistema
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Workflow: Criar Descrição Comercial
|
||||
|
|
@ -137,4 +141,4 @@ O arquivo `./COMERCIAL/<nome-do-modulo>.md` deve seguir esta estrutura:
|
|||
- Regra global: `.agent/rules/escrita-natural.global.md` (padrão obrigatório de redação)
|
||||
- Workflow: `.agent/workflows/orcamento.global.md` (texto para documentos de orçamento e proposta — apresentação + lista de funcionalidades)
|
||||
- Workflow: `.agent/workflows/marketing.global.md` (peças de marketing digital — LinkedIn, Instagram, WhatsApp, E-mail)
|
||||
- Workflow: `.agent/workflows/documentar-codigo.global.md` (para documentação técnica, não comercial)
|
||||
- Workflow: `.agent/workflows/otimizar-codigos.global.md` (para documentação técnica, não comercial)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
---
|
||||
type: workflow
|
||||
name: criar-fluxograma
|
||||
description: Criar ou atualizar uma página de fluxograma interativo em qualquer sistema
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Workflow: Criar Fluxograma de Decisões
|
||||
|
|
@ -181,5 +185,5 @@ Caso contrário, seguir os passos acima para criar a primeira implementação.
|
|||
|
||||
## Referências
|
||||
|
||||
- Workflow: `.agent/workflows/documentar-codigo.global.md`
|
||||
- Skill: `.agent/skills/documentar-decisao-tecnica/SKILL.md` (para decisões que geram fluxogramas)
|
||||
- Workflow: `.agent/workflows/otimizar-codigos.global.md`
|
||||
- Skill: `.agent/skills/documentar-decisao-tecnica.global/SKILL.md` (para decisões que geram fluxogramas)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
---
|
||||
type: workflow
|
||||
name: documentar-regras-negocio
|
||||
description: Criar ou atualizar o arquivo {modulo}.regras-negocio.md co-localizado com um módulo TypeScript e garantir consistência entre código e documentação
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Workflow: Documentar Regras de Negócio de um Módulo
|
||||
|
|
@ -86,7 +90,7 @@ Para cada regra listada no `.md`:
|
|||
|
||||
// turbo
|
||||
```bash
|
||||
cd e-licencie-back && node_modules/.bin/tsc --noEmit
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
Zero erros = concluído.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
---
|
||||
type: workflow
|
||||
name: marketing
|
||||
description: Criar peças de marketing digital para funcionalidades e módulos do sistema
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Workflow: Criar Peças de Marketing
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
---
|
||||
type: workflow
|
||||
name: orcamento
|
||||
description: Criar texto de orçamento pronto para copiar e colar em peças de orçamento/proposta
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Workflow: Criar Texto de Orçamento
|
||||
|
|
|
|||
|
|
@ -1,221 +1,76 @@
|
|||
---
|
||||
description: Otimizar código e documentar padrões de qualidade (estrutura, limites, JSDoc e tipagem)
|
||||
type: workflow
|
||||
name: otimizar-codigos
|
||||
description: Processo de refatoração e validação de qualidade de código
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
|
||||
|
||||
# Workflow: Qualidade e Documentação de Código
|
||||
# Workflow: Qualidade e Otimização de Código
|
||||
|
||||
## 🎯 Objetivo
|
||||
|
||||
Unificar as regras de **estrutura de arquivos**, **otimização** e **documentação** em um único fluxo de referência para o projeto `eli-vue`.
|
||||
Aplicar as rules de qualidade do ecossistema Azteca em código existente ou novo, garantindo que todos os padrões sejam cumpridos antes de finalizar uma tarefa.
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ Limite de Linhas por Arquivo
|
||||
## Passos
|
||||
|
||||
Nenhum arquivo `*.vue`, `*.ts` ou `*.tsx` pode ultrapassar **250 linhas**.
|
||||
### 1. Verificar limite de linhas
|
||||
|
||||
Essa regra existe para facilitar a leitura humana, a revisão de código e a manutenção a longo prazo.
|
||||
|
||||
> O script `scripts/check-vue-linhas.sh` valida automaticamente os arquivos `.vue` no `prebuild`.
|
||||
|
||||
**Estratégias quando o limite se aproxima:**
|
||||
|
||||
| Situação | Ação |
|
||||
|---|---|
|
||||
| Template longo | Extrair seções como subcomponentes |
|
||||
| CSS extenso | Mover para `<nome>.css` co-localizado |
|
||||
| `setup()` extenso | Extrair subcomponentes, CSS ou Dispatch Table |
|
||||
| Modal/Dialog embutido | Sempre em componente separado (`*Modal.vue`) |
|
||||
| Handlers e dispatch | Extrair para `handlers.ts` ou `dispatch.ts` |
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ Estrutura de Componentes Vue
|
||||
|
||||
### `defineComponent` obrigatório
|
||||
|
||||
Todo componente Vue deve usar `defineComponent` para garantir inferência de tipos correta e consistência.
|
||||
|
||||
```vue
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MeuComponente',
|
||||
setup() {
|
||||
// lógica aqui
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```bash
|
||||
sh .agent/verificar-linhas.sh
|
||||
```
|
||||
|
||||
**Por que é importante:**
|
||||
- Inferência de tipos TypeScript correta dentro do componente
|
||||
- Reconhecimento por ferramentas de análise estática e IDEs
|
||||
- Compatível com Options API e Composition API com `setup()`
|
||||
- Padronização entre todos os componentes do projeto
|
||||
Se houver violações, aplicar estratégias da rule `limite-linhas.global.md`:
|
||||
- Extrair subcomponentes, mover CSS, usar Dispatch Table
|
||||
- Se necessário, converter em pasta com `index.vue`
|
||||
|
||||
---
|
||||
### 2. Validar estrutura Vue
|
||||
|
||||
### `setup()` não pode ser externo
|
||||
Para cada componente `.vue` modificado, verificar conforme `estrutura-vue.global.md`:
|
||||
- `defineComponent` presente?
|
||||
- `setup()` contém lógica própria (não delegada)?
|
||||
- CSS extenso foi movido para arquivo co-localizado?
|
||||
|
||||
O `setup()` deve estar sempre dentro do componente `.vue`. Não é permitido extrair ou delegar o `setup` para nenhum arquivo externo.
|
||||
### 3. Documentar código
|
||||
|
||||
```ts
|
||||
// ❌ Proibido — setup delegado inteiramente para arquivo externo
|
||||
// useEliTabela.ts
|
||||
export function useEliTabela(props, emit) { ... }
|
||||
Para cada função compartilhada criada/modificada, aplicar `jsdoc-padrao.global.md`:
|
||||
- JSDoc com `@param` e `@returns`?
|
||||
- Casts temporários têm comentário justificando?
|
||||
- TODOs no formato padronizado?
|
||||
|
||||
// EliTabela.vue
|
||||
export default defineComponent({
|
||||
setup(props, { emit }) {
|
||||
return useEliTabela(props, emit) // setup é apenas um proxy vazio
|
||||
}
|
||||
})
|
||||
### 4. Validar tipagem
|
||||
|
||||
```bash
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
```ts
|
||||
// ✅ Correto — setup com lógica própria dentro do componente
|
||||
// EliTabela.vue
|
||||
export default defineComponent({
|
||||
setup(props, { emit }) {
|
||||
const estado = ref(null)
|
||||
const carregar = async () => { ... }
|
||||
return { estado, carregar }
|
||||
}
|
||||
})
|
||||
```
|
||||
Zero erros = prosseguir. Se houver erros, aplicar `typescript-sem-any.global.md` — nunca resolver com `any`.
|
||||
|
||||
> Composables `use*.ts` podem encapsular lógica reutilizável pontual (ex: `useFormatador`, `usePermissao`), mas nunca substituir ou esvaziar o `setup()` do componente. Se o componente ultrapassar 250 linhas, a solução é extrair subcomponentes, mover CSS ou usar Dispatch Table — não esvaziar o `setup`.
|
||||
### 5. Formatar
|
||||
|
||||
---
|
||||
|
||||
### Desassociação de CSS
|
||||
|
||||
A separação de estilos em arquivo externo é permitida e recomendada quando o CSS contribui significativamente para o total de linhas.
|
||||
|
||||
**Padrão de nomenclatura:**
|
||||
|
||||
```
|
||||
MeuComponente.vue
|
||||
MeuComponente.css
|
||||
```
|
||||
|
||||
O `.vue` importa o arquivo CSS no script:
|
||||
|
||||
```ts
|
||||
import './MeuComponente.css'
|
||||
```
|
||||
|
||||
Ou via tag `<style>`:
|
||||
|
||||
```vue
|
||||
<style src="./MeuComponente.css" />
|
||||
```bash
|
||||
npx biome check --write .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Dispatch Table em Arquivo Separado
|
||||
## Checklist Final
|
||||
|
||||
O uso de Dispatch Table (mapa de funções) em arquivos separados é bem-vindo para eliminar blocos longos de `if/else` ou `switch`.
|
||||
- [ ] `verificar-linhas.sh` passa sem violações?
|
||||
- [ ] Componentes Vue usam `defineComponent`?
|
||||
- [ ] Funções compartilhadas têm JSDoc?
|
||||
- [ ] Casts temporários estão justificados?
|
||||
- [ ] `tsc --noEmit` zero erros?
|
||||
- [ ] `biome check` sem problemas?
|
||||
|
||||
```ts
|
||||
// handlers.ts
|
||||
export const acaoHandlers: Record<string, () => void> = {
|
||||
salvar: () => { ... },
|
||||
cancelar: () => { ... },
|
||||
excluir: () => { ... },
|
||||
}
|
||||
```
|
||||
## Rules Aplicadas
|
||||
|
||||
```ts
|
||||
// MeuComponente.vue
|
||||
import { acaoHandlers } from './handlers'
|
||||
Este workflow orquestra a verificação das seguintes rules:
|
||||
|
||||
function executar(acao: string) {
|
||||
acaoHandlers[acao]?.()
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ Documentação em Código (JSDoc)
|
||||
|
||||
### Princípios básicos
|
||||
|
||||
- **Por que, não o Quê**: Documente o raciocínio por trás da decisão arquitetural, não o mecanismo óbvio que o TypeScript já expressa.
|
||||
- **Nomenclatura limpa**: Confie na auto-documentação de nomes expressivos antes de escrever comentários de texto livre.
|
||||
|
||||
### Funções compartilhadas — JSDoc obrigatório
|
||||
|
||||
Toda função central ou de uso compartilhado (frontend, backend, mobile ou bibliotecas comuns) deve ter cabeçalho em `JSDoc`.
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Busca os detalhes de uma entidade baseando-se no ID fornecido e calcula privilégios aplicáveis.
|
||||
* Não utilizar em fluxos de processamento em lote para evitar sobrecarga de requisições.
|
||||
*
|
||||
* @param tenantId - Identificador único do cliente/tenant no sistema
|
||||
* @param entityId - ID exclusivo identificando o registro na base de dados
|
||||
* @returns Retorna o objeto consolidado com dados ou `null` caso não seja encontrado
|
||||
*/
|
||||
export const obterDetalhesEntidade = async (tenantId: string, entityId: string) => { ... }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4️⃣ Casts Temporários e Exceções de Tipagem
|
||||
|
||||
Devido à política **TypeScript sem any**, coerções agressivas requerem justificativa obrigatória em comentário.
|
||||
|
||||
```typescript
|
||||
// Necessário cast duplo pois a biblioteca externa não provê tipagem genérica adequada na versão atual
|
||||
const dados = payload.raw as unknown as RespostaFormatada
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5️⃣ TODOs Estruturados
|
||||
|
||||
Toda dívida técnica ou dúvida de negócio deixada em código deve seguir o formato `// TODO`:
|
||||
|
||||
- **Tipagem**: `// TODO de tipagem: Criar interface estrita para o retorno da API de listagem`
|
||||
- **Negócio**: `// TODO de regra: Validar com Produto se a taxa adicional se aplica a clientes antigos`
|
||||
|
||||
---
|
||||
|
||||
## 6️⃣ Resumo das Regras
|
||||
|
||||
| Regra | Permitido | Proibido |
|
||||
|---|---|---|
|
||||
| Tamanho de arquivo | Até 250 linhas | Acima de 250 linhas |
|
||||
| `defineComponent` | Obrigatório em todo `.vue` | Componentes sem `defineComponent` |
|
||||
| `setup()` externo | — | Proibido em qualquer forma |
|
||||
| CSS externo | `<nome>.css` vinculado ao `.vue` | CSS inline quando ultrapassa o limite |
|
||||
| Dispatch Table | Em arquivo separado | — |
|
||||
| JSDoc | Obrigatório em funções compartilhadas | Omitir em funções de uso amplo |
|
||||
| Casts `as any` | Com `biome-ignore` justificado | Sem comentário explicativo |
|
||||
|
||||
---
|
||||
|
||||
## 7️⃣ Checklist de Validação
|
||||
|
||||
Antes de concluir qualquer refatoração ou adição de código:
|
||||
|
||||
- [ ] O arquivo resultante tem menos de 250 linhas?
|
||||
- [ ] Funções compartilhadas têm `JSDoc` com `@param` e `@returns`?
|
||||
- [ ] Casts temporários têm comentário justificando?
|
||||
- [ ] TODOs estão no formato padronizado?
|
||||
- [ ] A documentação explica o **porquê**, não apenas o **o quê**?
|
||||
- [ ] `defineComponent` está presente em todo componente `.vue`?
|
||||
|
||||
---
|
||||
|
||||
## Referências
|
||||
|
||||
- Script: `scripts/check-vue-linhas.sh` (validação automática de tamanho)
|
||||
- Rule: `.agent/rules/vue-limite-linhas.global.md` (política de limite de linhas)
|
||||
- Rule: `.agent/rules/typescript-sem-any.global.md` (política de tipagem)
|
||||
- Skill: `.agent/skills/documentar-decisao-tecnica/SKILL.md` (ADRs para decisões arquiteturais)
|
||||
- `.agent/rules/limite-linhas.global.md` — máximo 250 linhas
|
||||
- `.agent/rules/estrutura-vue.global.md` — defineComponent, setup, CSS, dispatch
|
||||
- `.agent/rules/jsdoc-padrao.global.md` — JSDoc, casts, TODOs
|
||||
- `.agent/rules/typescript-sem-any.global.md` — proibição de any
|
||||
- `.agent/rules/zod-validacao-padrao.global.md` — validação de entradas externas
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
---
|
||||
type: workflow
|
||||
name: reels
|
||||
description: Criar cadeia de roteiros de Reels para divulgação de funcionalidades e módulos
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Workflow: Criar Roteiros de Reels
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
---
|
||||
type: workflow
|
||||
name: ux
|
||||
description: Revisar e otimizar UX de componentes e telas Vue (Frontend)
|
||||
scope: global
|
||||
updatedAt: "2026-06-01"
|
||||
---
|
||||
|
||||
# Workflow: Revisão de UX
|
||||
|
|
@ -191,44 +195,8 @@ import { Filter, Plus, X, Trash2, Eye } from "@lucide/vue"
|
|||
|
||||
**Catálogo de referência**: https://lucide.dev/icons — buscar pelo nome do ícone em inglês.
|
||||
|
||||
#### CSS do projeto
|
||||
|
||||
```css
|
||||
/* Escala primária — Verde floresta */
|
||||
--cor-eli-primaria-bg: #EEF3D0;
|
||||
--cor-eli-primaria-zebra: #F6F9E8;
|
||||
--cor-eli-primaria-suave: #BFC99C;
|
||||
--cor-eli-primaria-clara: #D4E290;
|
||||
--cor-eli-primaria-media: #ADCC44;
|
||||
--cor-eli-primaria-acao: #8AAF00;
|
||||
--cor-eli-primaria: #688C00;
|
||||
--cor-eli-primaria-escura: #4A6500;
|
||||
--cor-eli-primaria-texto: #2E3F00;
|
||||
|
||||
/* Escala de apoio — Âmbar terroso */
|
||||
--cor-eli-alerta-bg: #FAF3DF;
|
||||
--cor-eli-alerta-clara: #F5DC8A;
|
||||
--cor-eli-alerta: #DDB847;
|
||||
--cor-eli-alerta-forte: #8C6A00;
|
||||
|
||||
/* Escala neutra — Cinza musgo */
|
||||
--cor-eli-fundo: #F2F3EE;
|
||||
--cor-eli-borda: #D5D8CC;
|
||||
--cor-eli-borda-forte: #B5BAA8;
|
||||
--cor-eli-neutro: #5E6553;
|
||||
--cor-eli-neutro-escuro: #3E4436;
|
||||
--cor-eli-superficie: #FFFFFF;
|
||||
|
||||
/* Semânticas */
|
||||
--cor-eli-sucesso: #2E7D32;
|
||||
--cor-eli-sucesso-bg: #E8F5E9;
|
||||
--cor-eli-erro: #C62828;
|
||||
--cor-eli-erro-bg: #FFEBEE;
|
||||
--cor-eli-info: #1565C0;
|
||||
--cor-eli-info-bg: #E3F2FD;
|
||||
```
|
||||
|
||||
> **⚠️ Regra crítica para componentes distribuídos como biblioteca**: todo token CSS novo (`--cor-eli-primaria-zebra`, `--cor-eli-primaria-suave`, etc.) deve ter **fallback hardcoded** no CSS do componente: `var(--token, #valorhex)`. Projetos destino podem não ter a versão mais recente do `design-tokens.css` e a variável resolverá como `undefined`, tornando a propriedade invisível.
|
||||
> Os tokens CSS estão documentados nas tabelas da seção **Paleta de Cores Institucional** acima.
|
||||
> No código, usar `var(--token, #fallback)` — sempre com fallback hardcoded em componentes de biblioteca.
|
||||
|
||||
- Classes devem seguir BEM: `eli-componente__elemento--modificador`
|
||||
- Evitar `!important` — resolver especificidade via seletor mais específico
|
||||
|
|
|
|||
6271
dist/eli-vue.es.js
vendored
6271
dist/eli-vue.es.js
vendored
File diff suppressed because one or more lines are too long
104
dist/eli-vue.umd.js
vendored
104
dist/eli-vue.umd.js
vendored
File diff suppressed because one or more lines are too long
1361
dist/types/componentes/EliEntrada/EliEntradaConfirmacao.vue.d.ts
vendored
Normal file
1361
dist/types/componentes/EliEntrada/EliEntradaConfirmacao.vue.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -187,14 +187,14 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
};
|
||||
}>> & Readonly<{
|
||||
onInput?: ((_v: string | null) => any) | undefined;
|
||||
"onUpdate:modelValue"?: ((_v: string | null) => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
"onUpdate:value"?: ((_v: string | null) => any) | undefined;
|
||||
onChange?: ((_v: string | null) => any) | undefined;
|
||||
"onUpdate:modelValue"?: ((_v: string | null) => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onAlterar?: ((_v: string | null) => any) | undefined;
|
||||
onFoco?: (() => any) | undefined;
|
||||
onDesfoco?: (() => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
}>, {
|
||||
modo: "data" | "dataHora" | undefined;
|
||||
limpavel: boolean;
|
||||
|
|
@ -206,6 +206,7 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
max: string | undefined;
|
||||
densidade: CampoDensidade;
|
||||
variante: CampoVariante;
|
||||
desabilitado: boolean;
|
||||
opcoes: {
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
|
|
@ -225,5 +226,4 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
modelValue: string | null;
|
||||
rotulo: string;
|
||||
placeholder: string;
|
||||
desabilitado: boolean;
|
||||
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
};
|
||||
}>, {
|
||||
attrs: import("vue").Attrs;
|
||||
emit: ((event: "input", _v: number | null | undefined) => void) & ((event: "update:value", _v: number | null | undefined) => void) & ((event: "change", _v: number | null | undefined) => void) & ((event: "focus") => void) & ((event: "blur") => void);
|
||||
emit: ((event: "input", _v: number | null | undefined) => void) & ((event: "blur") => void) & ((event: "update:value", _v: number | null | undefined) => void) & ((event: "change", _v: number | null | undefined) => void) & ((event: "focus") => void);
|
||||
displayValue: import("vue").Ref<string, string>;
|
||||
isInteiro: import("vue").ComputedRef<boolean>;
|
||||
onUpdateModelValue: (texto: string) => void;
|
||||
|
|
@ -39,10 +39,10 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
};
|
||||
}>> & Readonly<{
|
||||
onInput?: ((_v: number | null | undefined) => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
"onUpdate:value"?: ((_v: number | null | undefined) => any) | undefined;
|
||||
onChange?: ((_v: number | null | undefined) => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
}>, {
|
||||
value: number | null | undefined;
|
||||
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
};
|
||||
}>, {
|
||||
attrs: import("vue").Attrs;
|
||||
emit: ((event: "input", _v: string | null | undefined) => void) & ((event: "update:value", _v: string | null | undefined) => void) & ((event: "change", _v: string | null | undefined) => void) & ((event: "focus") => void) & ((event: "blur") => void);
|
||||
emit: ((event: "input", _v: string | null | undefined) => void) & ((event: "blur") => void) & ((event: "update:value", _v: string | null | undefined) => void) & ((event: "change", _v: string | null | undefined) => void) & ((event: "focus") => void);
|
||||
localValue: import("vue").WritableComputedRef<string | null | undefined, string | null | undefined>;
|
||||
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||
"update:value": (_v: EntradaParagrafo["value"]) => true;
|
||||
|
|
@ -33,10 +33,10 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
};
|
||||
}>> & Readonly<{
|
||||
onInput?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
"onUpdate:value"?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onChange?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
}>, {
|
||||
value: string | null | undefined;
|
||||
}, {}, {
|
||||
|
|
@ -1290,7 +1290,7 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
} & import("vue").ComponentCustomProperties & import("vuetify/lib/util/defineComponent.mjs").GenericProps<{
|
||||
modelValue?: unknown;
|
||||
"onUpdate:modelValue"?: ((value: unknown) => void) | undefined;
|
||||
}, import("vuetify/lib/components/VInput/VInput.mjs").VInputSlots>, "name" | "color" | "disabled" | "error" | "label" | "style" | "class" | keyof import("vue").VNodeProps | "modelValue" | "onUpdate:modelValue" | "centerAffix" | "density" | "direction" | "errorMessages" | "focused" | "glow" | "hideSpinButtons" | "maxErrors" | "messages" | "persistentHint" | "readonly" | "rules" | "theme" | "onUpdate:focused" | "validateOn" | "validationValue" | "maxWidth" | "minWidth" | "width" | "id" | "appendIcon" | "baseColor" | "iconColor" | "prependIcon" | "hideDetails" | "hint" | "onClick:prepend" | "onClick:append" | "$children" | "v-slot:append" | "v-slot:default" | "v-slot:details" | "v-slot:message" | "v-slot:prepend" | "v-slots">, `$${any}`> & {
|
||||
}, import("vuetify/lib/components/VInput/VInput.mjs").VInputSlots>, "name" | "color" | "disabled" | "error" | "label" | "style" | "class" | keyof import("vue").VNodeProps | "centerAffix" | "density" | "direction" | "errorMessages" | "focused" | "glow" | "hideSpinButtons" | "maxErrors" | "messages" | "persistentHint" | "readonly" | "rules" | "theme" | "onUpdate:focused" | "validateOn" | "validationValue" | "maxWidth" | "minWidth" | "width" | "id" | "appendIcon" | "baseColor" | "iconColor" | "prependIcon" | "hideDetails" | "hint" | "onClick:prepend" | "onClick:append" | "$children" | "modelValue" | "onUpdate:modelValue" | "v-slot:append" | "v-slot:default" | "v-slot:details" | "v-slot:message" | "v-slot:prepend" | "v-slots">, `$${any}`> & {
|
||||
_allExposed: {
|
||||
reset: () => Promise<void>;
|
||||
resetValidation: () => Promise<void>;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
10024
dist/types/componentes/EliEntrada/EliEntradaSelecaoMultipla.vue.d.ts
vendored
Normal file
10024
dist/types/componentes/EliEntrada/EliEntradaSelecaoMultipla.vue.d.ts
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -15,7 +15,7 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
};
|
||||
}>, {
|
||||
attrs: import("vue").Attrs;
|
||||
emit: ((event: "input", _v: string | null | undefined) => void) & ((event: "update:value", _v: string | null | undefined) => void) & ((event: "change", _v: string | null | undefined) => void) & ((event: "focus") => void) & ((event: "blur") => void);
|
||||
emit: ((event: "input", _v: string | null | undefined) => void) & ((event: "blur") => void) & ((event: "update:value", _v: string | null | undefined) => void) & ((event: "change", _v: string | null | undefined) => void) & ((event: "focus") => void);
|
||||
localValue: import("vue").WritableComputedRef<string | null | undefined, string | null | undefined>;
|
||||
inputHtmlType: import("vue").ComputedRef<"text" | "email" | "url">;
|
||||
inputMode: import("vue").ComputedRef<string | undefined>;
|
||||
|
|
@ -39,10 +39,10 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
};
|
||||
}>> & Readonly<{
|
||||
onInput?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
"onUpdate:value"?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onChange?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
}>, {
|
||||
value: string | null | undefined;
|
||||
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||
|
|
|
|||
4
dist/types/componentes/EliEntrada/index.d.ts
vendored
4
dist/types/componentes/EliEntrada/index.d.ts
vendored
|
|
@ -1,7 +1,9 @@
|
|||
import EliEntradaConfirmacao from "./EliEntradaConfirmacao.vue";
|
||||
import EliEntradaDataHora from "./EliEntradaDataHora.vue";
|
||||
import EliEntradaNumero from "./EliEntradaNumero.vue";
|
||||
import EliEntradaParagrafo from "./EliEntradaParagrafo.vue";
|
||||
import EliEntradaSelecao from "./EliEntradaSelecao.vue";
|
||||
import EliEntradaSelecaoMultipla from "./EliEntradaSelecaoMultipla.vue";
|
||||
import EliEntradaTexto from "./EliEntradaTexto.vue";
|
||||
export type { PadroesEntradas, TipoEntrada } from "./tiposEntradas";
|
||||
export { EliEntradaDataHora, EliEntradaNumero, EliEntradaParagrafo, EliEntradaSelecao, EliEntradaTexto, };
|
||||
export { EliEntradaConfirmacao, EliEntradaDataHora, EliEntradaNumero, EliEntradaParagrafo, EliEntradaSelecao, EliEntradaSelecaoMultipla, EliEntradaTexto, };
|
||||
|
|
|
|||
12030
dist/types/componentes/EliEntrada/registryEliEntradas.d.ts
vendored
12030
dist/types/componentes/EliEntrada/registryEliEntradas.d.ts
vendored
File diff suppressed because one or more lines are too long
|
|
@ -131,6 +131,45 @@ export type PadroesEntradas = {
|
|||
/** Variante do v-text-field (Vuetify). */
|
||||
variante?: import("../../tipos").CampoVariante;
|
||||
}>;
|
||||
selecaoMultipla: tipoPadraoEntrada<string[] | null | undefined, {
|
||||
/**
|
||||
* Carrega os itens da seleção (sincrono ou async).
|
||||
* - Cada item precisa ter uma chave estável (value) e um rótulo (title).
|
||||
*/
|
||||
itens: () => {
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
}[] | Promise<{
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
}[]>;
|
||||
/** Se true, mostra ícone para limpar o valor (Vuetify clearable). */
|
||||
limpavel?: boolean;
|
||||
/** Estado de erro (visual). */
|
||||
erro?: boolean;
|
||||
/** Mensagens de erro. */
|
||||
mensagensErro?: string | string[];
|
||||
/** Texto de apoio. */
|
||||
dica?: string;
|
||||
/** Mantém a dica sempre visível. */
|
||||
dicaPersistente?: boolean;
|
||||
/** Densidade do campo (Vuetify). */
|
||||
densidade?: import("../../tipos").CampoDensidade;
|
||||
/** Variante do v-text-field (Vuetify). */
|
||||
variante?: import("../../tipos").CampoVariante;
|
||||
}>;
|
||||
confirmacao: tipoPadraoEntrada<boolean, {
|
||||
/** Cor do switch quando ativo (Vuetify color). */
|
||||
cor?: string;
|
||||
/** Se true, desabilita a interação. */
|
||||
desabilitado?: boolean;
|
||||
/** Texto exibido ao lado do switch quando ativo. */
|
||||
rotuloAtivo?: string;
|
||||
/** Texto exibido ao lado do switch quando inativo. */
|
||||
rotuloInativo?: string;
|
||||
/** Densidade do campo (Vuetify). */
|
||||
densidade?: import("../../tipos").CampoDensidade;
|
||||
}>;
|
||||
};
|
||||
/**
|
||||
* União dos tipos de entrada suportados.
|
||||
|
|
|
|||
423
dist/types/componentes/EliTabela/EliTabela.vue.d.ts
vendored
423
dist/types/componentes/EliTabela/EliTabela.vue.d.ts
vendored
|
|
@ -96,6 +96,10 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
modalFiltroAberto: import("vue").Ref<boolean, boolean>;
|
||||
exibirFiltroAvancado: import("vue").ComputedRef<boolean>;
|
||||
filtrosAvancadosAtivos: import("vue").ComputedRef<import("p-comuns").tipoFiltroDeltaColunaTipada<any>[]>;
|
||||
filtrosAtivosResumo: import("vue").ComputedRef<{
|
||||
quantidade: number;
|
||||
rotulos: string[];
|
||||
}>;
|
||||
abrirModalFiltro: () => void;
|
||||
fecharModalFiltro: () => void;
|
||||
limparFiltrosAvancados: () => void;
|
||||
|
|
@ -191,6 +195,17 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
required: false;
|
||||
default: boolean;
|
||||
};
|
||||
filtrosAtivosResumo: {
|
||||
type: PropType<{
|
||||
quantidade: number;
|
||||
rotulos: string[];
|
||||
}>;
|
||||
required: false;
|
||||
default: () => {
|
||||
quantidade: number;
|
||||
rotulos: never[];
|
||||
};
|
||||
};
|
||||
valorBusca: {
|
||||
type: StringConstructor;
|
||||
required: true;
|
||||
|
|
@ -210,6 +225,7 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
};
|
||||
}>, {
|
||||
temAcoesCabecalho: import("vue").ComputedRef<boolean>;
|
||||
tooltipFiltro: import("vue").ComputedRef<string>;
|
||||
emitBuscar: (texto: string) => void;
|
||||
emitColunas: () => void;
|
||||
emitFiltroAvancado: () => void;
|
||||
|
|
@ -232,6 +248,17 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
required: false;
|
||||
default: boolean;
|
||||
};
|
||||
filtrosAtivosResumo: {
|
||||
type: PropType<{
|
||||
quantidade: number;
|
||||
rotulos: string[];
|
||||
}>;
|
||||
required: false;
|
||||
default: () => {
|
||||
quantidade: number;
|
||||
rotulos: never[];
|
||||
};
|
||||
};
|
||||
valorBusca: {
|
||||
type: StringConstructor;
|
||||
required: true;
|
||||
|
|
@ -256,6 +283,10 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
}>, {
|
||||
exibirBotaoColunas: boolean;
|
||||
exibirBotaoFiltroAvancado: boolean;
|
||||
filtrosAtivosResumo: {
|
||||
quantidade: number;
|
||||
rotulos: string[];
|
||||
};
|
||||
}, {}, {
|
||||
Columns3: import("@lucide/vue").LucideIcon;
|
||||
EliTabelaCaixaDeBusca: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||
|
|
@ -995,6 +1026,31 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
dicaPersistente?: boolean | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
variante?: import("../..").CampoVariante | undefined;
|
||||
}] | readonly ["selecaoMultipla", {
|
||||
rotulo: string;
|
||||
placeholder?: string | undefined;
|
||||
itens: () => {
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
}[] | Promise<{
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
}[]>;
|
||||
limpavel?: boolean | undefined;
|
||||
erro?: boolean | undefined;
|
||||
mensagensErro?: string | string[] | undefined;
|
||||
dica?: string | undefined;
|
||||
dicaPersistente?: boolean | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
variante?: import("../..").CampoVariante | undefined;
|
||||
}] | readonly ["confirmacao", {
|
||||
rotulo: string;
|
||||
placeholder?: string | undefined;
|
||||
cor?: string | undefined;
|
||||
desabilitado?: boolean | undefined;
|
||||
rotuloAtivo?: string | undefined;
|
||||
rotuloInativo?: string | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
}];
|
||||
valor: unknown;
|
||||
}[], import("./useFiltroAvancado").LinhaFiltro[] | {
|
||||
|
|
@ -1052,357 +1108,44 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
dicaPersistente?: boolean | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
variante?: import("../..").CampoVariante | undefined;
|
||||
}] | readonly ["selecaoMultipla", {
|
||||
rotulo: string;
|
||||
placeholder?: string | undefined;
|
||||
itens: () => {
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
}[] | Promise<{
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
}[]>;
|
||||
limpavel?: boolean | undefined;
|
||||
erro?: boolean | undefined;
|
||||
mensagensErro?: string | string[] | undefined;
|
||||
dica?: string | undefined;
|
||||
dicaPersistente?: boolean | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
variante?: import("../..").CampoVariante | undefined;
|
||||
}] | readonly ["confirmacao", {
|
||||
rotulo: string;
|
||||
placeholder?: string | undefined;
|
||||
cor?: string | undefined;
|
||||
desabilitado?: boolean | undefined;
|
||||
rotuloAtivo?: string | undefined;
|
||||
rotuloInativo?: string | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
}];
|
||||
valor: unknown;
|
||||
}[]>;
|
||||
opcoesParaAdicionar: import("vue").ComputedRef<{
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
filtro: (valor: unknown) => import("p-comuns").tipoFiltroDeltaColunaTipada<"small" | "length" | "concat" | "slice" | "indexOf" | "lastIndexOf" | "includes" | "toString" | "charAt" | "charCodeAt" | "localeCompare" | "match" | "replace" | "search" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "codePointAt" | "endsWith" | "normalize" | "repeat" | "startsWith" | "anchor" | "big" | "blink" | "bold" | "fixed" | "fontcolor" | "fontsize" | "italics" | "link" | "strike" | "sub" | "sup" | "padStart" | "padEnd" | "trimEnd" | "trimStart" | "trimLeft" | "trimRight" | "matchAll" | "valueOf"> | undefined;
|
||||
filtro: (valor: unknown) => Pick<import("p-comuns").tipoCondicaoFiltroDeltaTipado<"small" | "length" | "concat" | "slice" | "indexOf" | "lastIndexOf" | "includes" | "toString" | "charAt" | "charCodeAt" | "localeCompare" | "match" | "replace" | "search" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "codePointAt" | "endsWith" | "normalize" | "repeat" | "startsWith" | "anchor" | "big" | "blink" | "bold" | "fixed" | "fontcolor" | "fontsize" | "italics" | "link" | "strike" | "sub" | "sup" | "padStart" | "padEnd" | "trimEnd" | "trimStart" | "trimLeft" | "trimRight" | "matchAll" | "valueOf">, "valor" | "coluna" | "operador"> | undefined;
|
||||
entrada: import("../EliEntrada/tiposEntradas").ComponenteEntrada;
|
||||
}[]>;
|
||||
chaveParaAdicionar: import("vue").Ref<string, string>;
|
||||
formulaCustomizada: import("vue").Ref<string, string>;
|
||||
formulaGerada: import("vue").ComputedRef<string>;
|
||||
componenteEntrada: (entrada: import("../EliEntrada/tiposEntradas").ComponenteEntrada) => import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: PropType<string | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
modo?: "data" | "dataHora";
|
||||
limpavel?: boolean;
|
||||
erro?: boolean;
|
||||
mensagensErro?: string | string[];
|
||||
dica?: string;
|
||||
dicaPersistente?: boolean;
|
||||
min?: string;
|
||||
max?: string;
|
||||
densidade?: import("../..").CampoDensidade;
|
||||
variante?: import("../..").CampoVariante;
|
||||
}>;
|
||||
required: false;
|
||||
default: undefined;
|
||||
};
|
||||
modelValue: {
|
||||
type: PropType<string | null>;
|
||||
default: null;
|
||||
};
|
||||
modo: {
|
||||
type: PropType<"data" | "dataHora" | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
rotulo: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
placeholder: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
desabilitado: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
limpavel: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
erro: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
mensagensErro: {
|
||||
type: PropType<string | string[]>;
|
||||
default: undefined;
|
||||
};
|
||||
dica: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
dicaPersistente: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
densidade: {
|
||||
type: PropType<import("../..").CampoDensidade>;
|
||||
default: undefined;
|
||||
};
|
||||
variante: {
|
||||
type: PropType<import("../..").CampoVariante>;
|
||||
default: undefined;
|
||||
};
|
||||
min: {
|
||||
type: PropType<string | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
max: {
|
||||
type: PropType<string | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
}>, {
|
||||
attrs: import("vue").Attrs;
|
||||
valor: import("vue").WritableComputedRef<string, string>;
|
||||
tipoInput: import("vue").ComputedRef<"date" | "datetime-local">;
|
||||
minLocal: import("vue").ComputedRef<string | undefined>;
|
||||
maxLocal: import("vue").ComputedRef<string | undefined>;
|
||||
opcoesEfetivas: import("vue").ComputedRef<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
modo?: "data" | "dataHora";
|
||||
limpavel?: boolean;
|
||||
erro?: boolean;
|
||||
mensagensErro?: string | string[];
|
||||
dica?: string;
|
||||
dicaPersistente?: boolean;
|
||||
min?: string;
|
||||
max?: string;
|
||||
densidade?: import("../..").CampoDensidade;
|
||||
variante?: import("../..").CampoVariante;
|
||||
}>;
|
||||
desabilitadoEfetivo: import("vue").ComputedRef<boolean>;
|
||||
emitCompatFocus: () => void;
|
||||
emitCompatBlur: () => void;
|
||||
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||
"update:value": (_v: string | null) => true;
|
||||
input: (_v: string | null) => true;
|
||||
change: (_v: string | null) => true;
|
||||
"update:modelValue": (_v: string | null) => true;
|
||||
alterar: (_v: string | null) => true;
|
||||
foco: () => true;
|
||||
desfoco: () => true;
|
||||
focus: () => true;
|
||||
blur: () => true;
|
||||
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: PropType<string | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
modo?: "data" | "dataHora";
|
||||
limpavel?: boolean;
|
||||
erro?: boolean;
|
||||
mensagensErro?: string | string[];
|
||||
dica?: string;
|
||||
dicaPersistente?: boolean;
|
||||
min?: string;
|
||||
max?: string;
|
||||
densidade?: import("../..").CampoDensidade;
|
||||
variante?: import("../..").CampoVariante;
|
||||
}>;
|
||||
required: false;
|
||||
default: undefined;
|
||||
};
|
||||
modelValue: {
|
||||
type: PropType<string | null>;
|
||||
default: null;
|
||||
};
|
||||
modo: {
|
||||
type: PropType<"data" | "dataHora" | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
rotulo: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
placeholder: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
desabilitado: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
limpavel: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
erro: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
mensagensErro: {
|
||||
type: PropType<string | string[]>;
|
||||
default: undefined;
|
||||
};
|
||||
dica: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
dicaPersistente: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
densidade: {
|
||||
type: PropType<import("../..").CampoDensidade>;
|
||||
default: undefined;
|
||||
};
|
||||
variante: {
|
||||
type: PropType<import("../..").CampoVariante>;
|
||||
default: undefined;
|
||||
};
|
||||
min: {
|
||||
type: PropType<string | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
max: {
|
||||
type: PropType<string | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
}>> & Readonly<{
|
||||
onInput?: ((_v: string | null) => any) | undefined;
|
||||
"onUpdate:value"?: ((_v: string | null) => any) | undefined;
|
||||
onChange?: ((_v: string | null) => any) | undefined;
|
||||
"onUpdate:modelValue"?: ((_v: string | null) => any) | undefined;
|
||||
onAlterar?: ((_v: string | null) => any) | undefined;
|
||||
onFoco?: (() => any) | undefined;
|
||||
onDesfoco?: (() => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
}>, {
|
||||
modo: "data" | "dataHora" | undefined;
|
||||
limpavel: boolean;
|
||||
erro: boolean;
|
||||
mensagensErro: string | string[];
|
||||
dica: string;
|
||||
dicaPersistente: boolean;
|
||||
min: string | undefined;
|
||||
max: string | undefined;
|
||||
densidade: import("../..").CampoDensidade;
|
||||
variante: import("../..").CampoVariante;
|
||||
opcoes: {
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
modo?: "data" | "dataHora";
|
||||
limpavel?: boolean;
|
||||
erro?: boolean;
|
||||
mensagensErro?: string | string[];
|
||||
dica?: string;
|
||||
dicaPersistente?: boolean;
|
||||
min?: string;
|
||||
max?: string;
|
||||
densidade?: import("../..").CampoDensidade;
|
||||
variante?: import("../..").CampoVariante;
|
||||
};
|
||||
value: string | null | undefined;
|
||||
modelValue: string | null;
|
||||
rotulo: string;
|
||||
placeholder: string;
|
||||
desabilitado: boolean;
|
||||
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any> | import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: PropType<number | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
sufixo?: string;
|
||||
prefixo?: string;
|
||||
precisao?: number;
|
||||
}>;
|
||||
required: true;
|
||||
};
|
||||
}>, {
|
||||
attrs: import("vue").Attrs;
|
||||
emit: ((event: "input", _v: number | null | undefined) => void) & ((event: "update:value", _v: number | null | undefined) => void) & ((event: "change", _v: number | null | undefined) => void) & ((event: "focus") => void) & ((event: "blur") => void);
|
||||
displayValue: import("vue").Ref<string, string>;
|
||||
isInteiro: import("vue").ComputedRef<boolean>;
|
||||
onUpdateModelValue: (texto: string) => void;
|
||||
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||
"update:value": (_v: number | null | undefined) => true;
|
||||
input: (_v: number | null | undefined) => true;
|
||||
change: (_v: number | null | undefined) => true;
|
||||
focus: () => true;
|
||||
blur: () => true;
|
||||
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: PropType<number | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
sufixo?: string;
|
||||
prefixo?: string;
|
||||
precisao?: number;
|
||||
}>;
|
||||
required: true;
|
||||
};
|
||||
}>> & Readonly<{
|
||||
onInput?: ((_v: number | null | undefined) => any) | undefined;
|
||||
"onUpdate:value"?: ((_v: number | null | undefined) => any) | undefined;
|
||||
onChange?: ((_v: number | null | undefined) => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
}>, {
|
||||
value: number | null | undefined;
|
||||
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any> | import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: PropType<string | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
limiteCaracteres?: number;
|
||||
formato?: "texto" | "email" | "url" | "telefone" | "cpfCnpj" | "cep";
|
||||
}>;
|
||||
required: true;
|
||||
};
|
||||
}>, {
|
||||
attrs: import("vue").Attrs;
|
||||
emit: ((event: "input", _v: string | null | undefined) => void) & ((event: "update:value", _v: string | null | undefined) => void) & ((event: "change", _v: string | null | undefined) => void) & ((event: "focus") => void) & ((event: "blur") => void);
|
||||
localValue: import("vue").WritableComputedRef<string | null | undefined, string | null | undefined>;
|
||||
inputHtmlType: import("vue").ComputedRef<"text" | "email" | "url">;
|
||||
inputMode: import("vue").ComputedRef<string | undefined>;
|
||||
onInput: (e: Event) => void;
|
||||
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||
"update:value": (_v: string | null | undefined) => true;
|
||||
input: (_v: string | null | undefined) => true;
|
||||
change: (_v: string | null | undefined) => true;
|
||||
focus: () => true;
|
||||
blur: () => true;
|
||||
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: PropType<string | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
limiteCaracteres?: number;
|
||||
formato?: "texto" | "email" | "url" | "telefone" | "cpfCnpj" | "cep";
|
||||
}>;
|
||||
required: true;
|
||||
};
|
||||
}>> & Readonly<{
|
||||
onInput?: ((_v: string | null | undefined) => any) | undefined;
|
||||
"onUpdate:value"?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onChange?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
}>, {
|
||||
value: string | null | undefined;
|
||||
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||
componenteEntrada: (entrada: import("../EliEntrada/tiposEntradas").ComponenteEntrada) => import("vue").Component;
|
||||
opcoesEntrada: (entrada: import("../EliEntrada/tiposEntradas").ComponenteEntrada) => {
|
||||
rotulo: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,6 +16,17 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
required: false;
|
||||
default: boolean;
|
||||
};
|
||||
filtrosAtivosResumo: {
|
||||
type: PropType<{
|
||||
quantidade: number;
|
||||
rotulos: string[];
|
||||
}>;
|
||||
required: false;
|
||||
default: () => {
|
||||
quantidade: number;
|
||||
rotulos: never[];
|
||||
};
|
||||
};
|
||||
valorBusca: {
|
||||
type: StringConstructor;
|
||||
required: true;
|
||||
|
|
@ -35,6 +46,7 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
};
|
||||
}>, {
|
||||
temAcoesCabecalho: import("vue").ComputedRef<boolean>;
|
||||
tooltipFiltro: import("vue").ComputedRef<string>;
|
||||
emitBuscar: (texto: string) => void;
|
||||
emitColunas: () => void;
|
||||
emitFiltroAvancado: () => void;
|
||||
|
|
@ -57,6 +69,17 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
required: false;
|
||||
default: boolean;
|
||||
};
|
||||
filtrosAtivosResumo: {
|
||||
type: PropType<{
|
||||
quantidade: number;
|
||||
rotulos: string[];
|
||||
}>;
|
||||
required: false;
|
||||
default: () => {
|
||||
quantidade: number;
|
||||
rotulos: never[];
|
||||
};
|
||||
};
|
||||
valorBusca: {
|
||||
type: StringConstructor;
|
||||
required: true;
|
||||
|
|
@ -81,6 +104,10 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
}>, {
|
||||
exibirBotaoColunas: boolean;
|
||||
exibirBotaoFiltroAvancado: boolean;
|
||||
filtrosAtivosResumo: {
|
||||
quantidade: number;
|
||||
rotulos: string[];
|
||||
};
|
||||
}, {}, {
|
||||
Columns3: import("@lucide/vue").LucideIcon;
|
||||
EliTabelaCaixaDeBusca: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||
|
|
|
|||
|
|
@ -81,6 +81,31 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
dicaPersistente?: boolean | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
variante?: import("../..").CampoVariante | undefined;
|
||||
}] | readonly ["selecaoMultipla", {
|
||||
rotulo: string;
|
||||
placeholder?: string | undefined;
|
||||
itens: () => {
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
}[] | Promise<{
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
}[]>;
|
||||
limpavel?: boolean | undefined;
|
||||
erro?: boolean | undefined;
|
||||
mensagensErro?: string | string[] | undefined;
|
||||
dica?: string | undefined;
|
||||
dicaPersistente?: boolean | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
variante?: import("../..").CampoVariante | undefined;
|
||||
}] | readonly ["confirmacao", {
|
||||
rotulo: string;
|
||||
placeholder?: string | undefined;
|
||||
cor?: string | undefined;
|
||||
desabilitado?: boolean | undefined;
|
||||
rotuloAtivo?: string | undefined;
|
||||
rotuloInativo?: string | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
}];
|
||||
valor: unknown;
|
||||
}[], import("./useFiltroAvancado").LinhaFiltro[] | {
|
||||
|
|
@ -138,357 +163,44 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
dicaPersistente?: boolean | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
variante?: import("../..").CampoVariante | undefined;
|
||||
}] | readonly ["selecaoMultipla", {
|
||||
rotulo: string;
|
||||
placeholder?: string | undefined;
|
||||
itens: () => {
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
}[] | Promise<{
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
}[]>;
|
||||
limpavel?: boolean | undefined;
|
||||
erro?: boolean | undefined;
|
||||
mensagensErro?: string | string[] | undefined;
|
||||
dica?: string | undefined;
|
||||
dicaPersistente?: boolean | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
variante?: import("../..").CampoVariante | undefined;
|
||||
}] | readonly ["confirmacao", {
|
||||
rotulo: string;
|
||||
placeholder?: string | undefined;
|
||||
cor?: string | undefined;
|
||||
desabilitado?: boolean | undefined;
|
||||
rotuloAtivo?: string | undefined;
|
||||
rotuloInativo?: string | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
}];
|
||||
valor: unknown;
|
||||
}[]>;
|
||||
opcoesParaAdicionar: import("vue").ComputedRef<{
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
filtro: (valor: unknown) => import("p-comuns").tipoFiltroDeltaColunaTipada<"small" | "length" | "concat" | "slice" | "indexOf" | "lastIndexOf" | "includes" | "toString" | "charAt" | "charCodeAt" | "localeCompare" | "match" | "replace" | "search" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "codePointAt" | "endsWith" | "normalize" | "repeat" | "startsWith" | "anchor" | "big" | "blink" | "bold" | "fixed" | "fontcolor" | "fontsize" | "italics" | "link" | "strike" | "sub" | "sup" | "padStart" | "padEnd" | "trimEnd" | "trimStart" | "trimLeft" | "trimRight" | "matchAll" | "valueOf"> | undefined;
|
||||
filtro: (valor: unknown) => Pick<import("p-comuns").tipoCondicaoFiltroDeltaTipado<"small" | "length" | "concat" | "slice" | "indexOf" | "lastIndexOf" | "includes" | "toString" | "charAt" | "charCodeAt" | "localeCompare" | "match" | "replace" | "search" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "codePointAt" | "endsWith" | "normalize" | "repeat" | "startsWith" | "anchor" | "big" | "blink" | "bold" | "fixed" | "fontcolor" | "fontsize" | "italics" | "link" | "strike" | "sub" | "sup" | "padStart" | "padEnd" | "trimEnd" | "trimStart" | "trimLeft" | "trimRight" | "matchAll" | "valueOf">, "valor" | "coluna" | "operador"> | undefined;
|
||||
entrada: import("../EliEntrada/tiposEntradas").ComponenteEntrada;
|
||||
}[]>;
|
||||
chaveParaAdicionar: import("vue").Ref<string, string>;
|
||||
formulaCustomizada: import("vue").Ref<string, string>;
|
||||
formulaGerada: import("vue").ComputedRef<string>;
|
||||
componenteEntrada: (entrada: import("../EliEntrada/tiposEntradas").ComponenteEntrada) => import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: PropType<string | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
modo?: "data" | "dataHora";
|
||||
limpavel?: boolean;
|
||||
erro?: boolean;
|
||||
mensagensErro?: string | string[];
|
||||
dica?: string;
|
||||
dicaPersistente?: boolean;
|
||||
min?: string;
|
||||
max?: string;
|
||||
densidade?: import("../..").CampoDensidade;
|
||||
variante?: import("../..").CampoVariante;
|
||||
}>;
|
||||
required: false;
|
||||
default: undefined;
|
||||
};
|
||||
modelValue: {
|
||||
type: PropType<string | null>;
|
||||
default: null;
|
||||
};
|
||||
modo: {
|
||||
type: PropType<"data" | "dataHora" | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
rotulo: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
placeholder: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
desabilitado: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
limpavel: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
erro: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
mensagensErro: {
|
||||
type: PropType<string | string[]>;
|
||||
default: undefined;
|
||||
};
|
||||
dica: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
dicaPersistente: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
densidade: {
|
||||
type: PropType<import("../..").CampoDensidade>;
|
||||
default: undefined;
|
||||
};
|
||||
variante: {
|
||||
type: PropType<import("../..").CampoVariante>;
|
||||
default: undefined;
|
||||
};
|
||||
min: {
|
||||
type: PropType<string | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
max: {
|
||||
type: PropType<string | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
}>, {
|
||||
attrs: import("vue").Attrs;
|
||||
valor: import("vue").WritableComputedRef<string, string>;
|
||||
tipoInput: import("vue").ComputedRef<"date" | "datetime-local">;
|
||||
minLocal: import("vue").ComputedRef<string | undefined>;
|
||||
maxLocal: import("vue").ComputedRef<string | undefined>;
|
||||
opcoesEfetivas: import("vue").ComputedRef<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
modo?: "data" | "dataHora";
|
||||
limpavel?: boolean;
|
||||
erro?: boolean;
|
||||
mensagensErro?: string | string[];
|
||||
dica?: string;
|
||||
dicaPersistente?: boolean;
|
||||
min?: string;
|
||||
max?: string;
|
||||
densidade?: import("../..").CampoDensidade;
|
||||
variante?: import("../..").CampoVariante;
|
||||
}>;
|
||||
desabilitadoEfetivo: import("vue").ComputedRef<boolean>;
|
||||
emitCompatFocus: () => void;
|
||||
emitCompatBlur: () => void;
|
||||
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||
"update:value": (_v: string | null) => true;
|
||||
input: (_v: string | null) => true;
|
||||
change: (_v: string | null) => true;
|
||||
"update:modelValue": (_v: string | null) => true;
|
||||
alterar: (_v: string | null) => true;
|
||||
foco: () => true;
|
||||
desfoco: () => true;
|
||||
focus: () => true;
|
||||
blur: () => true;
|
||||
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: PropType<string | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
modo?: "data" | "dataHora";
|
||||
limpavel?: boolean;
|
||||
erro?: boolean;
|
||||
mensagensErro?: string | string[];
|
||||
dica?: string;
|
||||
dicaPersistente?: boolean;
|
||||
min?: string;
|
||||
max?: string;
|
||||
densidade?: import("../..").CampoDensidade;
|
||||
variante?: import("../..").CampoVariante;
|
||||
}>;
|
||||
required: false;
|
||||
default: undefined;
|
||||
};
|
||||
modelValue: {
|
||||
type: PropType<string | null>;
|
||||
default: null;
|
||||
};
|
||||
modo: {
|
||||
type: PropType<"data" | "dataHora" | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
rotulo: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
placeholder: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
desabilitado: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
limpavel: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
erro: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
mensagensErro: {
|
||||
type: PropType<string | string[]>;
|
||||
default: undefined;
|
||||
};
|
||||
dica: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
dicaPersistente: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
densidade: {
|
||||
type: PropType<import("../..").CampoDensidade>;
|
||||
default: undefined;
|
||||
};
|
||||
variante: {
|
||||
type: PropType<import("../..").CampoVariante>;
|
||||
default: undefined;
|
||||
};
|
||||
min: {
|
||||
type: PropType<string | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
max: {
|
||||
type: PropType<string | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
}>> & Readonly<{
|
||||
onInput?: ((_v: string | null) => any) | undefined;
|
||||
"onUpdate:value"?: ((_v: string | null) => any) | undefined;
|
||||
onChange?: ((_v: string | null) => any) | undefined;
|
||||
"onUpdate:modelValue"?: ((_v: string | null) => any) | undefined;
|
||||
onAlterar?: ((_v: string | null) => any) | undefined;
|
||||
onFoco?: (() => any) | undefined;
|
||||
onDesfoco?: (() => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
}>, {
|
||||
modo: "data" | "dataHora" | undefined;
|
||||
limpavel: boolean;
|
||||
erro: boolean;
|
||||
mensagensErro: string | string[];
|
||||
dica: string;
|
||||
dicaPersistente: boolean;
|
||||
min: string | undefined;
|
||||
max: string | undefined;
|
||||
densidade: import("../..").CampoDensidade;
|
||||
variante: import("../..").CampoVariante;
|
||||
opcoes: {
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
modo?: "data" | "dataHora";
|
||||
limpavel?: boolean;
|
||||
erro?: boolean;
|
||||
mensagensErro?: string | string[];
|
||||
dica?: string;
|
||||
dicaPersistente?: boolean;
|
||||
min?: string;
|
||||
max?: string;
|
||||
densidade?: import("../..").CampoDensidade;
|
||||
variante?: import("../..").CampoVariante;
|
||||
};
|
||||
value: string | null | undefined;
|
||||
modelValue: string | null;
|
||||
rotulo: string;
|
||||
placeholder: string;
|
||||
desabilitado: boolean;
|
||||
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any> | import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: PropType<number | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
sufixo?: string;
|
||||
prefixo?: string;
|
||||
precisao?: number;
|
||||
}>;
|
||||
required: true;
|
||||
};
|
||||
}>, {
|
||||
attrs: import("vue").Attrs;
|
||||
emit: ((event: "input", _v: number | null | undefined) => void) & ((event: "update:value", _v: number | null | undefined) => void) & ((event: "change", _v: number | null | undefined) => void) & ((event: "focus") => void) & ((event: "blur") => void);
|
||||
displayValue: import("vue").Ref<string, string>;
|
||||
isInteiro: import("vue").ComputedRef<boolean>;
|
||||
onUpdateModelValue: (texto: string) => void;
|
||||
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||
"update:value": (_v: number | null | undefined) => true;
|
||||
input: (_v: number | null | undefined) => true;
|
||||
change: (_v: number | null | undefined) => true;
|
||||
focus: () => true;
|
||||
blur: () => true;
|
||||
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: PropType<number | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
sufixo?: string;
|
||||
prefixo?: string;
|
||||
precisao?: number;
|
||||
}>;
|
||||
required: true;
|
||||
};
|
||||
}>> & Readonly<{
|
||||
onInput?: ((_v: number | null | undefined) => any) | undefined;
|
||||
"onUpdate:value"?: ((_v: number | null | undefined) => any) | undefined;
|
||||
onChange?: ((_v: number | null | undefined) => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
}>, {
|
||||
value: number | null | undefined;
|
||||
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any> | import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: PropType<string | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
limiteCaracteres?: number;
|
||||
formato?: "texto" | "email" | "url" | "telefone" | "cpfCnpj" | "cep";
|
||||
}>;
|
||||
required: true;
|
||||
};
|
||||
}>, {
|
||||
attrs: import("vue").Attrs;
|
||||
emit: ((event: "input", _v: string | null | undefined) => void) & ((event: "update:value", _v: string | null | undefined) => void) & ((event: "change", _v: string | null | undefined) => void) & ((event: "focus") => void) & ((event: "blur") => void);
|
||||
localValue: import("vue").WritableComputedRef<string | null | undefined, string | null | undefined>;
|
||||
inputHtmlType: import("vue").ComputedRef<"text" | "email" | "url">;
|
||||
inputMode: import("vue").ComputedRef<string | undefined>;
|
||||
onInput: (e: Event) => void;
|
||||
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||
"update:value": (_v: string | null | undefined) => true;
|
||||
input: (_v: string | null | undefined) => true;
|
||||
change: (_v: string | null | undefined) => true;
|
||||
focus: () => true;
|
||||
blur: () => true;
|
||||
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: PropType<string | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
limiteCaracteres?: number;
|
||||
formato?: "texto" | "email" | "url" | "telefone" | "cpfCnpj" | "cep";
|
||||
}>;
|
||||
required: true;
|
||||
};
|
||||
}>> & Readonly<{
|
||||
onInput?: ((_v: string | null | undefined) => any) | undefined;
|
||||
"onUpdate:value"?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onChange?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
}>, {
|
||||
value: string | null | undefined;
|
||||
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||
componenteEntrada: (entrada: import("../EliEntrada/tiposEntradas").ComponenteEntrada) => import("vue").Component;
|
||||
opcoesEntrada: (entrada: import("../EliEntrada/tiposEntradas").ComponenteEntrada) => {
|
||||
rotulo: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
import type { tiposTabelaCelulas, tipoTabelaCelula } from "./celulas/tiposTabelaCelulas";
|
||||
|
|
@ -136,8 +136,8 @@ export type tipoEliTabelaConsulta<T> = {
|
|||
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<Extract<keyof T, string>> | undefined;
|
||||
/** vai gerar filtro e mescla com os filtros já existentes , id é gerado de forma altomatica para esse local*/
|
||||
filtro: (valor: unknown) => Pick<tipoCondicaoFiltroDeltaTipado<Extract<keyof T, string>>, "coluna" | "operador" | "valor"> | undefined;
|
||||
/** componente de entrada que vai receber o valor para o filtro */
|
||||
entrada: ComponenteEntrada;
|
||||
}[];
|
||||
|
|
|
|||
|
|
@ -70,6 +70,10 @@ export declare function useEliTabela(props: {
|
|||
modalFiltroAberto: import("vue").Ref<boolean, boolean>;
|
||||
exibirFiltroAvancado: import("vue").ComputedRef<boolean>;
|
||||
filtrosAvancadosAtivos: import("vue").ComputedRef<import("p-comuns").tipoFiltroDeltaColunaTipada<any>[]>;
|
||||
filtrosAtivosResumo: import("vue").ComputedRef<{
|
||||
quantidade: number;
|
||||
rotulos: string[];
|
||||
}>;
|
||||
abrirModalFiltro: () => void;
|
||||
fecharModalFiltro: () => void;
|
||||
limparFiltrosAvancados: () => void;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ export declare function useEliTabelaFiltrosAvancados(props: PropsFiltros, pagina
|
|||
modalFiltroAberto: Ref<boolean, boolean>;
|
||||
exibirFiltroAvancado: import("vue").ComputedRef<boolean>;
|
||||
filtrosAvancadosAtivos: import("vue").ComputedRef<tipoFiltroDeltaColunaTipada<any>[]>;
|
||||
filtrosAtivosResumo: import("vue").ComputedRef<{
|
||||
quantidade: number;
|
||||
rotulos: string[];
|
||||
}>;
|
||||
abrirModalFiltro: () => void;
|
||||
fecharModalFiltro: () => void;
|
||||
limparFiltrosAvancados: () => void;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*
|
||||
* Extraído de `EliTabelaModalFiltroAvancado.vue` para manter o componente abaixo de 250 linhas.
|
||||
*/
|
||||
import { type Ref } from "vue";
|
||||
import { type Component, type Ref } from "vue";
|
||||
import type { ComponenteEntrada } from "../EliEntrada/tiposEntradas";
|
||||
import type { tipoEliTabelaConsulta } from "./types-eli-tabela";
|
||||
export type FiltroBase<T> = NonNullable<tipoEliTabelaConsulta<T>["filtroAvancado"]>[number];
|
||||
|
|
@ -112,6 +112,31 @@ export declare function useFiltroAvancado({ aberto, filtrosBase, modelo, modeloF
|
|||
dicaPersistente?: boolean | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
variante?: import("../..").CampoVariante | undefined;
|
||||
}] | readonly ["selecaoMultipla", {
|
||||
rotulo: string;
|
||||
placeholder?: string | undefined;
|
||||
itens: () => {
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
}[] | Promise<{
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
}[]>;
|
||||
limpavel?: boolean | undefined;
|
||||
erro?: boolean | undefined;
|
||||
mensagensErro?: string | string[] | undefined;
|
||||
dica?: string | undefined;
|
||||
dicaPersistente?: boolean | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
variante?: import("../..").CampoVariante | undefined;
|
||||
}] | readonly ["confirmacao", {
|
||||
rotulo: string;
|
||||
placeholder?: string | undefined;
|
||||
cor?: string | undefined;
|
||||
desabilitado?: boolean | undefined;
|
||||
rotuloAtivo?: string | undefined;
|
||||
rotuloInativo?: string | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
}];
|
||||
valor: unknown;
|
||||
}[], LinhaFiltro[] | {
|
||||
|
|
@ -169,357 +194,44 @@ export declare function useFiltroAvancado({ aberto, filtrosBase, modelo, modeloF
|
|||
dicaPersistente?: boolean | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
variante?: import("../..").CampoVariante | undefined;
|
||||
}] | readonly ["selecaoMultipla", {
|
||||
rotulo: string;
|
||||
placeholder?: string | undefined;
|
||||
itens: () => {
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
}[] | Promise<{
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
}[]>;
|
||||
limpavel?: boolean | undefined;
|
||||
erro?: boolean | undefined;
|
||||
mensagensErro?: string | string[] | undefined;
|
||||
dica?: string | undefined;
|
||||
dicaPersistente?: boolean | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
variante?: import("../..").CampoVariante | undefined;
|
||||
}] | readonly ["confirmacao", {
|
||||
rotulo: string;
|
||||
placeholder?: string | undefined;
|
||||
cor?: string | undefined;
|
||||
desabilitado?: boolean | undefined;
|
||||
rotuloAtivo?: string | undefined;
|
||||
rotuloInativo?: string | undefined;
|
||||
densidade?: import("../..").CampoDensidade | undefined;
|
||||
}];
|
||||
valor: unknown;
|
||||
}[]>;
|
||||
opcoesParaAdicionar: import("vue").ComputedRef<{
|
||||
chave: string;
|
||||
rotulo: string;
|
||||
filtro: (valor: unknown) => import("p-comuns").tipoFiltroDeltaColunaTipada<"small" | "length" | "concat" | "slice" | "indexOf" | "lastIndexOf" | "includes" | "toString" | "charAt" | "charCodeAt" | "localeCompare" | "match" | "replace" | "search" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "codePointAt" | "endsWith" | "normalize" | "repeat" | "startsWith" | "anchor" | "big" | "blink" | "bold" | "fixed" | "fontcolor" | "fontsize" | "italics" | "link" | "strike" | "sub" | "sup" | "padStart" | "padEnd" | "trimEnd" | "trimStart" | "trimLeft" | "trimRight" | "matchAll" | "valueOf"> | undefined;
|
||||
filtro: (valor: unknown) => Pick<import("p-comuns").tipoCondicaoFiltroDeltaTipado<"small" | "length" | "concat" | "slice" | "indexOf" | "lastIndexOf" | "includes" | "toString" | "charAt" | "charCodeAt" | "localeCompare" | "match" | "replace" | "search" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "codePointAt" | "endsWith" | "normalize" | "repeat" | "startsWith" | "anchor" | "big" | "blink" | "bold" | "fixed" | "fontcolor" | "fontsize" | "italics" | "link" | "strike" | "sub" | "sup" | "padStart" | "padEnd" | "trimEnd" | "trimStart" | "trimLeft" | "trimRight" | "matchAll" | "valueOf">, "valor" | "coluna" | "operador"> | undefined;
|
||||
entrada: ComponenteEntrada;
|
||||
}[]>;
|
||||
chaveParaAdicionar: Ref<string, string>;
|
||||
formulaCustomizada: Ref<string, string>;
|
||||
formulaGerada: import("vue").ComputedRef<string>;
|
||||
componenteEntrada: (entrada: ComponenteEntrada) => import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: import("vue").PropType<string | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: import("vue").PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
modo?: "data" | "dataHora";
|
||||
limpavel?: boolean;
|
||||
erro?: boolean;
|
||||
mensagensErro?: string | string[];
|
||||
dica?: string;
|
||||
dicaPersistente?: boolean;
|
||||
min?: string;
|
||||
max?: string;
|
||||
densidade?: import("../..").CampoDensidade;
|
||||
variante?: import("../..").CampoVariante;
|
||||
}>;
|
||||
required: false;
|
||||
default: undefined;
|
||||
};
|
||||
modelValue: {
|
||||
type: import("vue").PropType<string | null>;
|
||||
default: null;
|
||||
};
|
||||
modo: {
|
||||
type: import("vue").PropType<"data" | "dataHora" | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
rotulo: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
placeholder: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
desabilitado: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
limpavel: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
erro: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
mensagensErro: {
|
||||
type: import("vue").PropType<string | string[]>;
|
||||
default: undefined;
|
||||
};
|
||||
dica: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
dicaPersistente: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
densidade: {
|
||||
type: import("vue").PropType<import("../..").CampoDensidade>;
|
||||
default: undefined;
|
||||
};
|
||||
variante: {
|
||||
type: import("vue").PropType<import("../..").CampoVariante>;
|
||||
default: undefined;
|
||||
};
|
||||
min: {
|
||||
type: import("vue").PropType<string | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
max: {
|
||||
type: import("vue").PropType<string | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
}>, {
|
||||
attrs: import("vue").Attrs;
|
||||
valor: import("vue").WritableComputedRef<string, string>;
|
||||
tipoInput: import("vue").ComputedRef<"date" | "datetime-local">;
|
||||
minLocal: import("vue").ComputedRef<string | undefined>;
|
||||
maxLocal: import("vue").ComputedRef<string | undefined>;
|
||||
opcoesEfetivas: import("vue").ComputedRef<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
modo?: "data" | "dataHora";
|
||||
limpavel?: boolean;
|
||||
erro?: boolean;
|
||||
mensagensErro?: string | string[];
|
||||
dica?: string;
|
||||
dicaPersistente?: boolean;
|
||||
min?: string;
|
||||
max?: string;
|
||||
densidade?: import("../..").CampoDensidade;
|
||||
variante?: import("../..").CampoVariante;
|
||||
}>;
|
||||
desabilitadoEfetivo: import("vue").ComputedRef<boolean>;
|
||||
emitCompatFocus: () => void;
|
||||
emitCompatBlur: () => void;
|
||||
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||
"update:value": (_v: string | null) => true;
|
||||
input: (_v: string | null) => true;
|
||||
change: (_v: string | null) => true;
|
||||
"update:modelValue": (_v: string | null) => true;
|
||||
alterar: (_v: string | null) => true;
|
||||
foco: () => true;
|
||||
desfoco: () => true;
|
||||
focus: () => true;
|
||||
blur: () => true;
|
||||
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: import("vue").PropType<string | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: import("vue").PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
modo?: "data" | "dataHora";
|
||||
limpavel?: boolean;
|
||||
erro?: boolean;
|
||||
mensagensErro?: string | string[];
|
||||
dica?: string;
|
||||
dicaPersistente?: boolean;
|
||||
min?: string;
|
||||
max?: string;
|
||||
densidade?: import("../..").CampoDensidade;
|
||||
variante?: import("../..").CampoVariante;
|
||||
}>;
|
||||
required: false;
|
||||
default: undefined;
|
||||
};
|
||||
modelValue: {
|
||||
type: import("vue").PropType<string | null>;
|
||||
default: null;
|
||||
};
|
||||
modo: {
|
||||
type: import("vue").PropType<"data" | "dataHora" | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
rotulo: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
placeholder: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
desabilitado: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
limpavel: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
erro: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
mensagensErro: {
|
||||
type: import("vue").PropType<string | string[]>;
|
||||
default: undefined;
|
||||
};
|
||||
dica: {
|
||||
type: StringConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
dicaPersistente: {
|
||||
type: BooleanConstructor;
|
||||
default: undefined;
|
||||
};
|
||||
densidade: {
|
||||
type: import("vue").PropType<import("../..").CampoDensidade>;
|
||||
default: undefined;
|
||||
};
|
||||
variante: {
|
||||
type: import("vue").PropType<import("../..").CampoVariante>;
|
||||
default: undefined;
|
||||
};
|
||||
min: {
|
||||
type: import("vue").PropType<string | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
max: {
|
||||
type: import("vue").PropType<string | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
}>> & Readonly<{
|
||||
onInput?: ((_v: string | null) => any) | undefined;
|
||||
"onUpdate:value"?: ((_v: string | null) => any) | undefined;
|
||||
onChange?: ((_v: string | null) => any) | undefined;
|
||||
"onUpdate:modelValue"?: ((_v: string | null) => any) | undefined;
|
||||
onAlterar?: ((_v: string | null) => any) | undefined;
|
||||
onFoco?: (() => any) | undefined;
|
||||
onDesfoco?: (() => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
}>, {
|
||||
modo: "data" | "dataHora" | undefined;
|
||||
limpavel: boolean;
|
||||
erro: boolean;
|
||||
mensagensErro: string | string[];
|
||||
dica: string;
|
||||
dicaPersistente: boolean;
|
||||
min: string | undefined;
|
||||
max: string | undefined;
|
||||
densidade: import("../..").CampoDensidade;
|
||||
variante: import("../..").CampoVariante;
|
||||
opcoes: {
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
modo?: "data" | "dataHora";
|
||||
limpavel?: boolean;
|
||||
erro?: boolean;
|
||||
mensagensErro?: string | string[];
|
||||
dica?: string;
|
||||
dicaPersistente?: boolean;
|
||||
min?: string;
|
||||
max?: string;
|
||||
densidade?: import("../..").CampoDensidade;
|
||||
variante?: import("../..").CampoVariante;
|
||||
};
|
||||
value: string | null | undefined;
|
||||
modelValue: string | null;
|
||||
rotulo: string;
|
||||
placeholder: string;
|
||||
desabilitado: boolean;
|
||||
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any> | import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: import("vue").PropType<number | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: import("vue").PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
sufixo?: string;
|
||||
prefixo?: string;
|
||||
precisao?: number;
|
||||
}>;
|
||||
required: true;
|
||||
};
|
||||
}>, {
|
||||
attrs: import("vue").Attrs;
|
||||
emit: ((event: "input", _v: number | null | undefined) => void) & ((event: "update:value", _v: number | null | undefined) => void) & ((event: "change", _v: number | null | undefined) => void) & ((event: "focus") => void) & ((event: "blur") => void);
|
||||
displayValue: Ref<string, string>;
|
||||
isInteiro: import("vue").ComputedRef<boolean>;
|
||||
onUpdateModelValue: (texto: string) => void;
|
||||
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||
"update:value": (_v: number | null | undefined) => true;
|
||||
input: (_v: number | null | undefined) => true;
|
||||
change: (_v: number | null | undefined) => true;
|
||||
focus: () => true;
|
||||
blur: () => true;
|
||||
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: import("vue").PropType<number | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: import("vue").PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
sufixo?: string;
|
||||
prefixo?: string;
|
||||
precisao?: number;
|
||||
}>;
|
||||
required: true;
|
||||
};
|
||||
}>> & Readonly<{
|
||||
onInput?: ((_v: number | null | undefined) => any) | undefined;
|
||||
"onUpdate:value"?: ((_v: number | null | undefined) => any) | undefined;
|
||||
onChange?: ((_v: number | null | undefined) => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
}>, {
|
||||
value: number | null | undefined;
|
||||
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any> | import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: import("vue").PropType<string | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: import("vue").PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
limiteCaracteres?: number;
|
||||
formato?: "texto" | "email" | "url" | "telefone" | "cpfCnpj" | "cep";
|
||||
}>;
|
||||
required: true;
|
||||
};
|
||||
}>, {
|
||||
attrs: import("vue").Attrs;
|
||||
emit: ((event: "input", _v: string | null | undefined) => void) & ((event: "update:value", _v: string | null | undefined) => void) & ((event: "change", _v: string | null | undefined) => void) & ((event: "focus") => void) & ((event: "blur") => void);
|
||||
localValue: import("vue").WritableComputedRef<string | null | undefined, string | null | undefined>;
|
||||
inputHtmlType: import("vue").ComputedRef<"text" | "email" | "url">;
|
||||
inputMode: import("vue").ComputedRef<string | undefined>;
|
||||
onInput: (e: Event) => void;
|
||||
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||
"update:value": (_v: string | null | undefined) => true;
|
||||
input: (_v: string | null | undefined) => true;
|
||||
change: (_v: string | null | undefined) => true;
|
||||
focus: () => true;
|
||||
blur: () => true;
|
||||
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
value: {
|
||||
type: import("vue").PropType<string | null | undefined>;
|
||||
default: undefined;
|
||||
};
|
||||
opcoes: {
|
||||
type: import("vue").PropType<{
|
||||
rotulo: string;
|
||||
placeholder?: string;
|
||||
} & {
|
||||
limiteCaracteres?: number;
|
||||
formato?: "texto" | "email" | "url" | "telefone" | "cpfCnpj" | "cep";
|
||||
}>;
|
||||
required: true;
|
||||
};
|
||||
}>> & Readonly<{
|
||||
onInput?: ((_v: string | null | undefined) => any) | undefined;
|
||||
"onUpdate:value"?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onChange?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
}>, {
|
||||
value: string | null | undefined;
|
||||
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||
componenteEntrada: (entrada: ComponenteEntrada) => Component;
|
||||
opcoesEntrada: (entrada: ComponenteEntrada) => {
|
||||
rotulo: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ declare const __VLS_export: import("vue").DefineComponent<{}, {
|
|||
};
|
||||
}>, {
|
||||
attrs: import("vue").Attrs;
|
||||
emit: ((event: "input", _v: string | null | undefined) => void) & ((event: "update:value", _v: string | null | undefined) => void) & ((event: "change", _v: string | null | undefined) => void) & ((event: "focus") => void) & ((event: "blur") => void);
|
||||
emit: ((event: "input", _v: string | null | undefined) => void) & ((event: "blur") => void) & ((event: "update:value", _v: string | null | undefined) => void) & ((event: "change", _v: string | null | undefined) => void) & ((event: "focus") => void);
|
||||
localValue: import("vue").WritableComputedRef<string | null | undefined, string | null | undefined>;
|
||||
inputHtmlType: import("vue").ComputedRef<"text" | "email" | "url">;
|
||||
inputMode: import("vue").ComputedRef<string | undefined>;
|
||||
|
|
@ -182,10 +182,10 @@ declare const __VLS_export: import("vue").DefineComponent<{}, {
|
|||
};
|
||||
}>> & Readonly<{
|
||||
onInput?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
"onUpdate:value"?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onChange?: ((_v: string | null | undefined) => any) | undefined;
|
||||
onFocus?: (() => any) | undefined;
|
||||
onBlur?: (() => any) | undefined;
|
||||
}>, {
|
||||
value: string | null | undefined;
|
||||
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "eli-vue",
|
||||
"version": "0.1.184",
|
||||
"version": "0.1.187",
|
||||
"private": false,
|
||||
"main": "./dist/eli-vue.umd.js",
|
||||
"module": "./dist/eli-vue.es.js",
|
||||
|
|
@ -15,10 +15,10 @@
|
|||
"scripts": {
|
||||
"biome": "pnpm exec biome check --write",
|
||||
"check": "pnpm run biome && npx tsc --noEmit",
|
||||
"prebuild": "npm version patch --no-git-tag-version && bash scripts/check-linhas.sh",
|
||||
"prebuild": "npm version patch --no-git-tag-version",
|
||||
"build:types": "vue-tsc -p tsconfig.build.json --declaration --emitDeclarationOnly",
|
||||
"build": "pnpm run biome && vite build && npm run build:types",
|
||||
"dev": "pnpm run biome && pnpm run fechar && vite",
|
||||
"build": "sh ./.agent/verificar-linhas.sh || exit 1 && pnpm run biome && vite build && npm run build:types",
|
||||
"dev": "sh ./.agent/verificar-linhas.sh || exit 1 && pnpm run biome && pnpm run fechar && vite",
|
||||
"preview": "vite preview",
|
||||
"fechar": "fuser -k 5555/tcp || true"
|
||||
},
|
||||
|
|
|
|||
30
pnpm-lock.yaml
generated
30
pnpm-lock.yaml
generated
|
|
@ -22,10 +22,10 @@ importers:
|
|||
version: 1.11.21
|
||||
p-comuns:
|
||||
specifier: git+https://git2.idz.one/publico/_comuns.git
|
||||
version: git+https://git2.idz.one/publico/_comuns.git#4aa775266f009b085dd9067e47a3206792812863(dayjs@1.11.21)(uuid@13.0.2)(zod@4.4.3)
|
||||
version: git+https://git2.idz.one/publico/_comuns.git#48125b53699708bc3747015dfda380473c1298d8(dayjs@1.11.21)(uuid@13.0.2)(zod@4.4.3)
|
||||
p-respostas:
|
||||
specifier: git+https://git2.idz.one/publico/_respostas.git
|
||||
version: git+https://git2.idz.one/publico/_respostas.git#9aa36bcba98de8efd9babec6ab84099da5d46234(p-comuns@git+https://git2.idz.one/publico/_comuns.git#4aa775266f009b085dd9067e47a3206792812863(dayjs@1.11.21)(uuid@13.0.2)(zod@4.4.3))(zod@4.4.3)
|
||||
version: git+https://git2.idz.one/publico/_respostas.git#9aa36bcba98de8efd9babec6ab84099da5d46234(p-comuns@git+https://git2.idz.one/publico/_comuns.git#48125b53699708bc3747015dfda380473c1298d8(dayjs@1.11.21)(uuid@13.0.2)(zod@4.4.3))(zod@4.4.3)
|
||||
uuid:
|
||||
specifier: ^13.0.0
|
||||
version: 13.0.2
|
||||
|
|
@ -1272,9 +1272,9 @@ packages:
|
|||
resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
p-comuns@git+https://git2.idz.one/publico/_comuns.git#4aa775266f009b085dd9067e47a3206792812863:
|
||||
resolution: {commit: 4aa775266f009b085dd9067e47a3206792812863, repo: https://git2.idz.one/publico/_comuns.git, type: git}
|
||||
version: 0.342.0
|
||||
p-comuns@git+https://git2.idz.one/publico/_comuns.git#48125b53699708bc3747015dfda380473c1298d8:
|
||||
resolution: {commit: 48125b53699708bc3747015dfda380473c1298d8, repo: https://git2.idz.one/publico/_comuns.git, type: git}
|
||||
version: 0.346.0
|
||||
peerDependencies:
|
||||
dayjs: ^1.11.18
|
||||
uuid: ^11.1.0
|
||||
|
|
@ -1483,6 +1483,10 @@ packages:
|
|||
resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tinyglobby@0.2.17:
|
||||
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tmpl@1.0.5:
|
||||
resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
|
||||
|
||||
|
|
@ -2040,7 +2044,7 @@ snapshots:
|
|||
hermes-parser: 0.33.3
|
||||
invariant: 2.2.4
|
||||
nullthrows: 1.1.1
|
||||
tinyglobby: 0.2.16
|
||||
tinyglobby: 0.2.17
|
||||
yargs: 17.7.2
|
||||
optional: true
|
||||
|
||||
|
|
@ -3034,15 +3038,15 @@ snapshots:
|
|||
is-wsl: 2.2.0
|
||||
optional: true
|
||||
|
||||
p-comuns@git+https://git2.idz.one/publico/_comuns.git#4aa775266f009b085dd9067e47a3206792812863(dayjs@1.11.21)(uuid@13.0.2)(zod@4.4.3):
|
||||
p-comuns@git+https://git2.idz.one/publico/_comuns.git#48125b53699708bc3747015dfda380473c1298d8(dayjs@1.11.21)(uuid@13.0.2)(zod@4.4.3):
|
||||
dependencies:
|
||||
dayjs: 1.11.21
|
||||
uuid: 13.0.2
|
||||
zod: 4.4.3
|
||||
|
||||
p-respostas@git+https://git2.idz.one/publico/_respostas.git#9aa36bcba98de8efd9babec6ab84099da5d46234(p-comuns@git+https://git2.idz.one/publico/_comuns.git#4aa775266f009b085dd9067e47a3206792812863(dayjs@1.11.21)(uuid@13.0.2)(zod@4.4.3))(zod@4.4.3):
|
||||
p-respostas@git+https://git2.idz.one/publico/_respostas.git#9aa36bcba98de8efd9babec6ab84099da5d46234(p-comuns@git+https://git2.idz.one/publico/_comuns.git#48125b53699708bc3747015dfda380473c1298d8(dayjs@1.11.21)(uuid@13.0.2)(zod@4.4.3))(zod@4.4.3):
|
||||
dependencies:
|
||||
p-comuns: git+https://git2.idz.one/publico/_comuns.git#4aa775266f009b085dd9067e47a3206792812863(dayjs@1.11.21)(uuid@13.0.2)(zod@4.4.3)
|
||||
p-comuns: git+https://git2.idz.one/publico/_comuns.git#48125b53699708bc3747015dfda380473c1298d8(dayjs@1.11.21)(uuid@13.0.2)(zod@4.4.3)
|
||||
zod: 4.4.3
|
||||
|
||||
parseurl@1.3.3:
|
||||
|
|
@ -3136,7 +3140,7 @@ snapshots:
|
|||
scheduler: 0.27.0
|
||||
semver: 7.8.1
|
||||
stacktrace-parser: 0.1.11
|
||||
tinyglobby: 0.2.16
|
||||
tinyglobby: 0.2.17
|
||||
whatwg-fetch: 3.6.20
|
||||
ws: 7.5.11
|
||||
yargs: 17.7.2
|
||||
|
|
@ -3320,6 +3324,12 @@ snapshots:
|
|||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
picomatch: 4.0.4
|
||||
|
||||
tinyglobby@0.2.17:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
picomatch: 4.0.4
|
||||
optional: true
|
||||
|
||||
tmpl@1.0.5:
|
||||
optional: true
|
||||
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# check-linhas.sh
|
||||
# Garante que nenhum arquivo *.vue, *.ts ou *.tsx ultrapasse o limite de linhas definido.
|
||||
# Executado automaticamente no prebuild via package.json.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
LIMITE=250
|
||||
|
||||
# Arquivos isentos permanentes (gerados, dados de teste, etc.)
|
||||
# Formato: caminho relativo a partir da raiz do projeto
|
||||
ISENTOS=(
|
||||
|
||||
)
|
||||
|
||||
RAIZ="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
ERROS=0
|
||||
|
||||
while IFS= read -r arquivo; do
|
||||
relativo="${arquivo#$RAIZ/}"
|
||||
|
||||
# Verifica se está na lista de isentos
|
||||
isento=false
|
||||
for i in "${ISENTOS[@]}"; do
|
||||
if [ "$relativo" = "$i" ]; then
|
||||
isento=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if $isento; then
|
||||
continue
|
||||
fi
|
||||
|
||||
linhas=$(wc -l < "$arquivo")
|
||||
if [ "$linhas" -gt "$LIMITE" ]; then
|
||||
echo "❌ $relativo ($linhas linhas) — limite: $LIMITE"
|
||||
ERROS=$((ERROS + 1))
|
||||
fi
|
||||
done < <(find "$RAIZ/src" \( -name "*.vue" -o -name "*.ts" -o -name "*.tsx" \) -not -path "*/node_modules/*")
|
||||
|
||||
if [ "$ERROS" -gt 0 ]; then
|
||||
echo ""
|
||||
echo "⚠️ $ERROS arquivo(s) ultrapassam o limite de $LIMITE linhas."
|
||||
echo " Estratégias: subcomponentes, composables use*.ts, ou separar CSS."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Todos os arquivos (*.vue, *.ts, *.tsx) estão dentro do limite de $LIMITE linhas."
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# check-vue-linhas.sh
|
||||
# Garante que nenhum arquivo *.vue ultrapasse o limite de linhas definido.
|
||||
#
|
||||
# Arquivos listados em ISENTOS são legados em processo de refatoração —
|
||||
# cada um deve ser quebrado em componentes menores antes de ser removido daqui.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
LIMITE=250
|
||||
|
||||
# Todos os arquivos *.vue devem respeitar o limite.
|
||||
# Não existem isentos.
|
||||
ISENTOS=()
|
||||
|
||||
RAIZ="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
ERROS=0
|
||||
|
||||
# Constrói padrão grep para os isentos
|
||||
isentos_grep=""
|
||||
for isento in "${ISENTOS[@]}"; do
|
||||
if [ -n "$isentos_grep" ]; then
|
||||
isentos_grep="${isentos_grep}\|${isento}"
|
||||
else
|
||||
isentos_grep="${isento}"
|
||||
fi
|
||||
done
|
||||
|
||||
while IFS= read -r arquivo; do
|
||||
relativo="${arquivo#$RAIZ/}"
|
||||
|
||||
# Verifica se está na lista de isentos
|
||||
isento=false
|
||||
for i in "${ISENTOS[@]}"; do
|
||||
if [ "$relativo" = "$i" ]; then
|
||||
isento=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if $isento; then
|
||||
continue
|
||||
fi
|
||||
|
||||
linhas=$(wc -l < "$arquivo")
|
||||
if [ "$linhas" -gt "$LIMITE" ]; then
|
||||
echo "❌ $relativo ($linhas linhas) — limite: $LIMITE"
|
||||
ERROS=$((ERROS + 1))
|
||||
fi
|
||||
done < <(find "$RAIZ/src" -name "*.vue" -not -path "*/node_modules/*")
|
||||
|
||||
if [ "$ERROS" -gt 0 ]; then
|
||||
echo ""
|
||||
echo "⚠️ $ERROS arquivo(s) Vue ultrapassam o limite de $LIMITE linhas."
|
||||
echo " Quebre em componentes menores antes de fazer build."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Todos os arquivos Vue estão dentro do limite de $LIMITE linhas."
|
||||
62
src/componentes/EliEntrada/EliEntradaConfirmacao.vue
Normal file
62
src/componentes/EliEntrada/EliEntradaConfirmacao.vue
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<template>
|
||||
<v-switch
|
||||
v-model="localValue"
|
||||
:label="rotuloExibido"
|
||||
:color="opcoes?.cor ?? 'primary'"
|
||||
:disabled="Boolean(opcoes?.desabilitado)"
|
||||
:density="opcoes?.densidade ?? 'comfortable'"
|
||||
hide-details
|
||||
v-bind="attrs"
|
||||
@focus="() => emit('focus')"
|
||||
@blur="() => emit('blur')"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, type PropType } from "vue"
|
||||
import { VSwitch } from "vuetify/components"
|
||||
import type { PadroesEntradas } from "./tiposEntradas"
|
||||
|
||||
type EntradaConfirmacao = PadroesEntradas["confirmacao"]
|
||||
|
||||
export default defineComponent({
|
||||
name: "EliEntradaConfirmacao",
|
||||
components: { VSwitch },
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean as PropType<EntradaConfirmacao["value"]>,
|
||||
default: false,
|
||||
},
|
||||
opcoes: {
|
||||
type: Object as PropType<EntradaConfirmacao["opcoes"]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: {
|
||||
"update:value": (_v: EntradaConfirmacao["value"]) => true,
|
||||
input: (_v: EntradaConfirmacao["value"]) => true,
|
||||
change: (_v: EntradaConfirmacao["value"]) => true,
|
||||
focus: () => true,
|
||||
blur: () => true,
|
||||
},
|
||||
setup(props, { attrs, emit }) {
|
||||
const localValue = computed<EntradaConfirmacao["value"]>({
|
||||
get: () => props.value,
|
||||
set: (v) => {
|
||||
emit("update:value", v)
|
||||
emit("input", v)
|
||||
emit("change", v)
|
||||
},
|
||||
})
|
||||
|
||||
const rotuloExibido = computed(() => {
|
||||
if (localValue.value && props.opcoes?.rotuloAtivo) return props.opcoes.rotuloAtivo
|
||||
if (!localValue.value && props.opcoes?.rotuloInativo) return props.opcoes.rotuloInativo
|
||||
return props.opcoes?.rotulo ?? ""
|
||||
})
|
||||
|
||||
return { attrs, emit, localValue, rotuloExibido }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
95
src/componentes/EliEntrada/EliEntradaSelecaoMultipla.vue
Normal file
95
src/componentes/EliEntrada/EliEntradaSelecaoMultipla.vue
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<template>
|
||||
<v-select
|
||||
v-model="localValue"
|
||||
:label="opcoes?.rotulo"
|
||||
:placeholder="opcoes?.placeholder"
|
||||
:items="itens"
|
||||
item-title="rotulo"
|
||||
item-value="chave"
|
||||
:loading="carregando"
|
||||
:disabled="carregando"
|
||||
:menu-props="{ maxHeight: 320, zIndex: 99999 }"
|
||||
:clearable="Boolean(opcoes?.limpavel)"
|
||||
:error="Boolean(opcoes?.erro)"
|
||||
:error-messages="opcoes?.mensagensErro"
|
||||
:hint="opcoes?.dica"
|
||||
:persistent-hint="Boolean(opcoes?.dicaPersistente)"
|
||||
:density="opcoes?.densidade ?? 'comfortable'"
|
||||
:variant="opcoes?.variante ?? 'outlined'"
|
||||
multiple
|
||||
chips
|
||||
closable-chips
|
||||
v-bind="attrs"
|
||||
@focus="() => emit('focus')"
|
||||
@blur="() => emit('blur')"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onMounted, type PropType, ref, watch } from "vue"
|
||||
import { VSelect } from "vuetify/components"
|
||||
import type { PadroesEntradas } from "./tiposEntradas"
|
||||
|
||||
type EntradaSelecaoMultipla = PadroesEntradas["selecaoMultipla"]
|
||||
type ItemSelecao = { chave: string; rotulo: string }
|
||||
|
||||
export default defineComponent({
|
||||
name: "EliEntradaSelecaoMultipla",
|
||||
components: { VSelect },
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
value: {
|
||||
type: [Array, null] as unknown as PropType<EntradaSelecaoMultipla["value"]>,
|
||||
default: () => [],
|
||||
},
|
||||
opcoes: {
|
||||
type: Object as PropType<EntradaSelecaoMultipla["opcoes"]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: {
|
||||
"update:value": (_v: EntradaSelecaoMultipla["value"]) => true,
|
||||
input: (_v: EntradaSelecaoMultipla["value"]) => true,
|
||||
change: (_v: EntradaSelecaoMultipla["value"]) => true,
|
||||
focus: () => true,
|
||||
blur: () => true,
|
||||
},
|
||||
setup(props, { attrs, emit }) {
|
||||
const itens = ref<ItemSelecao[]>([])
|
||||
const carregando = ref(false)
|
||||
|
||||
const localValue = computed<EntradaSelecaoMultipla["value"]>({
|
||||
get: () => props.value ?? [],
|
||||
set: (v) => {
|
||||
const valor = Array.isArray(v) ? v : []
|
||||
emit("update:value", valor)
|
||||
emit("input", valor)
|
||||
emit("change", valor)
|
||||
},
|
||||
})
|
||||
|
||||
async function carregarItens() {
|
||||
carregando.value = true
|
||||
try {
|
||||
const resultado = await props.opcoes.itens()
|
||||
itens.value = [...(Array.isArray(resultado) ? resultado : [])]
|
||||
} finally {
|
||||
carregando.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.opcoes.itens,
|
||||
() => {
|
||||
void carregarItens()
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
void carregarItens()
|
||||
})
|
||||
|
||||
return { attrs, emit, localValue, itens, carregando }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
@ -1,14 +1,18 @@
|
|||
import EliEntradaConfirmacao from "./EliEntradaConfirmacao.vue"
|
||||
import EliEntradaDataHora from "./EliEntradaDataHora.vue"
|
||||
import EliEntradaNumero from "./EliEntradaNumero.vue"
|
||||
import EliEntradaParagrafo from "./EliEntradaParagrafo.vue"
|
||||
import EliEntradaSelecao from "./EliEntradaSelecao.vue"
|
||||
import EliEntradaSelecaoMultipla from "./EliEntradaSelecaoMultipla.vue"
|
||||
import EliEntradaTexto from "./EliEntradaTexto.vue"
|
||||
|
||||
export type { PadroesEntradas, TipoEntrada } from "./tiposEntradas"
|
||||
export {
|
||||
EliEntradaConfirmacao,
|
||||
EliEntradaDataHora,
|
||||
EliEntradaNumero,
|
||||
EliEntradaParagrafo,
|
||||
EliEntradaSelecao,
|
||||
EliEntradaSelecaoMultipla,
|
||||
EliEntradaTexto,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import type { Component } from "vue"
|
||||
import EliEntradaConfirmacao from "./EliEntradaConfirmacao.vue"
|
||||
import EliEntradaDataHora from "./EliEntradaDataHora.vue"
|
||||
import EliEntradaNumero from "./EliEntradaNumero.vue"
|
||||
import EliEntradaParagrafo from "./EliEntradaParagrafo.vue"
|
||||
import EliEntradaSelecao from "./EliEntradaSelecao.vue"
|
||||
import EliEntradaSelecaoMultipla from "./EliEntradaSelecaoMultipla.vue"
|
||||
import EliEntradaTexto from "./EliEntradaTexto.vue"
|
||||
|
||||
import type { TipoEntrada } from "./tiposEntradas"
|
||||
|
|
@ -17,10 +19,12 @@ import type { TipoEntrada } from "./tiposEntradas"
|
|||
* 2. Adicionar a chave em `TipoEntrada` (tiposEntradas.ts)
|
||||
* 3. Registrar aqui no mapa
|
||||
*/
|
||||
export const registryTabelaCelulas = {
|
||||
export const registryTabelaCelulas: Record<TipoEntrada, Component> = {
|
||||
texto: EliEntradaTexto,
|
||||
numero: EliEntradaNumero,
|
||||
dataHora: EliEntradaDataHora,
|
||||
paragrafo: EliEntradaParagrafo,
|
||||
selecao: EliEntradaSelecao,
|
||||
} as const satisfies Record<TipoEntrada, Component>
|
||||
selecaoMultipla: EliEntradaSelecaoMultipla,
|
||||
confirmacao: EliEntradaConfirmacao,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,6 +177,60 @@ export type PadroesEntradas = {
|
|||
variante?: import("../../tipos").CampoVariante
|
||||
}
|
||||
>
|
||||
|
||||
selecaoMultipla: tipoPadraoEntrada<
|
||||
string[] | null | undefined,
|
||||
{
|
||||
/**
|
||||
* Carrega os itens da seleção (sincrono ou async).
|
||||
* - Cada item precisa ter uma chave estável (value) e um rótulo (title).
|
||||
*/
|
||||
itens: () =>
|
||||
| { chave: string; rotulo: string }[]
|
||||
| Promise<{ chave: string; rotulo: string }[]>
|
||||
|
||||
/** Se true, mostra ícone para limpar o valor (Vuetify clearable). */
|
||||
limpavel?: boolean
|
||||
|
||||
/** Estado de erro (visual). */
|
||||
erro?: boolean
|
||||
|
||||
/** Mensagens de erro. */
|
||||
mensagensErro?: string | string[]
|
||||
|
||||
/** Texto de apoio. */
|
||||
dica?: string
|
||||
|
||||
/** Mantém a dica sempre visível. */
|
||||
dicaPersistente?: boolean
|
||||
|
||||
/** Densidade do campo (Vuetify). */
|
||||
densidade?: import("../../tipos").CampoDensidade
|
||||
|
||||
/** Variante do v-text-field (Vuetify). */
|
||||
variante?: import("../../tipos").CampoVariante
|
||||
}
|
||||
>
|
||||
|
||||
confirmacao: tipoPadraoEntrada<
|
||||
boolean,
|
||||
{
|
||||
/** Cor do switch quando ativo (Vuetify color). */
|
||||
cor?: string
|
||||
|
||||
/** Se true, desabilita a interação. */
|
||||
desabilitado?: boolean
|
||||
|
||||
/** Texto exibido ao lado do switch quando ativo. */
|
||||
rotuloAtivo?: string
|
||||
|
||||
/** Texto exibido ao lado do switch quando inativo. */
|
||||
rotuloInativo?: string
|
||||
|
||||
/** Densidade do campo (Vuetify). */
|
||||
densidade?: import("../../tipos").CampoDensidade
|
||||
}
|
||||
>
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
@import "./EliTabelaAcoes.css";
|
||||
@import "./EliTabelaSelecao.css";
|
||||
.eli-tabela {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
@ -132,7 +134,6 @@
|
|||
background: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
|
||||
.eli-tabela--erro {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
|
@ -244,284 +245,6 @@
|
|||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
/* Altura padrão para controles do cabeçalho (busca e botões)
|
||||
* - Mantém alinhamento visual entre input de busca e ações
|
||||
* - Pode ser sobrescrita via CSS no consumidor, se necessário
|
||||
*/
|
||||
--eli-tabela-cabecalho-controle-altura: 34px;
|
||||
|
||||
/* Borda dos controles do cabeçalho: menos "pílula", mais quadrado.
|
||||
* Ajuste fino via CSS no consumidor, se necessário.
|
||||
*/
|
||||
--eli-tabela-cabecalho-controle-radius: 8px;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-cabecalho {
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-cabecalho-botao {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
height: var(--eli-tabela-cabecalho-controle-altura);
|
||||
padding: 0 14px;
|
||||
border-radius: var(--eli-tabela-cabecalho-controle-radius);
|
||||
border: none;
|
||||
background: var(--cor-eli-primaria-bg);
|
||||
color: var(--cor-eli-primaria-escura);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-cabecalho-botao:hover,
|
||||
.eli-tabela__acoes-cabecalho-botao:focus-visible {
|
||||
background: var(--cor-eli-primaria-clara);
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-cabecalho-botao:focus-visible {
|
||||
outline: 2px solid var(--cor-eli-primaria-clara);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-cabecalho-icone {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-cabecalho-rotulo {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 9999px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: rgba(15, 23, 42, 0.72);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-toggle-icone {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-toggle:hover,
|
||||
.eli-tabela__acoes-toggle:focus-visible {
|
||||
background-color: rgba(15, 23, 42, 0.08);
|
||||
color: rgba(15, 23, 42, 0.95);
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-toggle:focus-visible {
|
||||
outline: 2px solid var(--cor-eli-primaria-clara);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-toggle:disabled {
|
||||
cursor: default;
|
||||
color: rgba(148, 163, 184, 0.8);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-menu {
|
||||
min-width: 180px;
|
||||
padding: 6px 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
background: var(--cor-eli-superficie);
|
||||
border: 1px solid rgba(15, 23, 42, 0.08);
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.18);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-item {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-item-botao {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-item-botao:hover,
|
||||
.eli-tabela__acoes-item-botao:focus-visible {
|
||||
background-color: rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-item-botao:focus-visible {
|
||||
outline: 2px solid currentColor;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-item-icone {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-item-texto {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Expander (colunas invisíveis)
|
||||
* ========================= */
|
||||
|
||||
.eli-tabela__th--expander,
|
||||
.eli-tabela__td--expander {
|
||||
width: 42px;
|
||||
padding: 6px 8px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.eli-tabela__expander-botao {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 9999px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: rgba(15, 23, 42, 0.72);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.eli-tabela__expander-botao:hover,
|
||||
.eli-tabela__expander-botao:focus-visible {
|
||||
background-color: rgba(15, 23, 42, 0.08);
|
||||
color: rgba(15, 23, 42, 0.95);
|
||||
}
|
||||
|
||||
.eli-tabela__expander-botao:focus-visible {
|
||||
outline: 2px solid var(--cor-eli-primaria-clara);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.eli-tabela__expander-botao--ativo {
|
||||
background-color: rgba(15, 23, 42, 0.06);
|
||||
color: rgba(15, 23, 42, 0.95);
|
||||
}
|
||||
|
||||
.eli-tabela__td--detalhes {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.eli-tabela__tr--detalhes .eli-tabela__td {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.eli-tabela__detalhes {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.eli-tabela__detalhe {
|
||||
display: grid;
|
||||
grid-template-columns: 180px 1fr;
|
||||
gap: 10px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.eli-tabela__detalhe-rotulo {
|
||||
font-weight: 600;
|
||||
color: rgba(15, 23, 42, 0.85);
|
||||
}
|
||||
|
||||
.eli-tabela__detalhe-valor {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.eli-tabela__detalhe {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================
|
||||
* Seleção (Checkboxes)
|
||||
* ========================================= */
|
||||
.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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
v-if="exibirBusca || temAcoesSuperiores"
|
||||
:exibirBusca="exibirBusca"
|
||||
:exibirBotaoFiltroAvancado="exibirFiltroAvancado"
|
||||
:filtrosAtivosResumo="filtrosAtivosResumo"
|
||||
:valorBusca="valorBusca"
|
||||
:acoesCabecalho="acoesSuperiores"
|
||||
:parametrosConsulta="parametrosConsultaAtuais"
|
||||
|
|
|
|||
207
src/componentes/EliTabela/EliTabelaAcoes.css
Normal file
207
src/componentes/EliTabela/EliTabelaAcoes.css
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
/* =========================
|
||||
* EliTabela — Ações de linha, Menu e Seleção (Checkboxes)
|
||||
* Extraído de EliTabela.css para manter cada arquivo CSS abaixo de 250 linhas.
|
||||
* ========================= */
|
||||
|
||||
.eli-tabela__acoes-cabecalho {
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-cabecalho-botao {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
height: var(--eli-tabela-cabecalho-controle-altura);
|
||||
padding: 0 14px;
|
||||
border-radius: var(--eli-tabela-cabecalho-controle-radius);
|
||||
border: none;
|
||||
background: var(--cor-eli-primaria-bg);
|
||||
color: var(--cor-eli-primaria-escura);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-cabecalho-botao:hover,
|
||||
.eli-tabela__acoes-cabecalho-botao:focus-visible {
|
||||
background: var(--cor-eli-primaria-clara);
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-cabecalho-botao:focus-visible {
|
||||
outline: 2px solid var(--cor-eli-primaria-clara);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-cabecalho-icone {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-cabecalho-rotulo {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 9999px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: rgba(15, 23, 42, 0.72);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-toggle-icone {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-toggle:hover,
|
||||
.eli-tabela__acoes-toggle:focus-visible {
|
||||
background-color: rgba(15, 23, 42, 0.08);
|
||||
color: rgba(15, 23, 42, 0.95);
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-toggle:focus-visible {
|
||||
outline: 2px solid var(--cor-eli-primaria-clara);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-toggle:disabled {
|
||||
cursor: default;
|
||||
color: rgba(148, 163, 184, 0.8);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-menu {
|
||||
min-width: 180px;
|
||||
padding: 6px 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
background: var(--cor-eli-superficie);
|
||||
border: 1px solid rgba(15, 23, 42, 0.08);
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.18);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-item {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-item-botao {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-item-botao:hover,
|
||||
.eli-tabela__acoes-item-botao:focus-visible {
|
||||
background-color: rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-item-botao:focus-visible {
|
||||
outline: 2px solid currentColor;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-item-icone {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.eli-tabela__acoes-item-texto {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Expander (colunas invisíveis)
|
||||
* ========================= */
|
||||
|
||||
.eli-tabela__th--expander,
|
||||
.eli-tabela__td--expander {
|
||||
width: 42px;
|
||||
padding: 6px 8px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.eli-tabela__expander-botao {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 9999px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: rgba(15, 23, 42, 0.72);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.eli-tabela__expander-botao:hover,
|
||||
.eli-tabela__expander-botao:focus-visible {
|
||||
background-color: rgba(15, 23, 42, 0.08);
|
||||
color: rgba(15, 23, 42, 0.95);
|
||||
}
|
||||
|
||||
.eli-tabela__expander-botao:focus-visible {
|
||||
outline: 2px solid var(--cor-eli-primaria-clara);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.eli-tabela__expander-botao--ativo {
|
||||
background-color: rgba(15, 23, 42, 0.06);
|
||||
color: rgba(15, 23, 42, 0.95);
|
||||
}
|
||||
|
||||
.eli-tabela__td--detalhes {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.eli-tabela__tr--detalhes .eli-tabela__td {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.eli-tabela__detalhes {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.eli-tabela__detalhe {
|
||||
display: grid;
|
||||
grid-template-columns: 180px 1fr;
|
||||
gap: 10px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.eli-tabela__detalhe-rotulo {
|
||||
font-weight: 600;
|
||||
color: rgba(15, 23, 42, 0.85);
|
||||
}
|
||||
|
||||
.eli-tabela__detalhe-valor {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.eli-tabela__detalhe {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -16,9 +16,16 @@
|
|||
v-if="exibirBotaoFiltroAvancado"
|
||||
type="button"
|
||||
class="eli-tabela__acoes-cabecalho-botao eli-tabela__acoes-cabecalho-botao--filtro"
|
||||
:title="tooltipFiltro"
|
||||
@click="emitFiltroAvancado"
|
||||
>
|
||||
<Filter :size="15" :stroke-width="2" />
|
||||
<span class="eli-tabela__filtro-icone-container">
|
||||
<Filter :size="15" :stroke-width="2" />
|
||||
<span
|
||||
v-if="filtrosAtivosResumo.quantidade > 0"
|
||||
class="eli-tabela__filtro-badge"
|
||||
>{{ filtrosAtivosResumo.quantidade }}</span>
|
||||
</span>
|
||||
Filtro
|
||||
</button>
|
||||
<EliTabelaCaixaDeBusca :modelo="valorBusca" @buscar="emitBuscar" />
|
||||
|
|
@ -70,6 +77,11 @@ export default defineComponent({
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
filtrosAtivosResumo: {
|
||||
type: Object as PropType<{ quantidade: number; rotulos: string[] }>,
|
||||
required: false,
|
||||
default: () => ({ quantidade: 0, rotulos: [] }),
|
||||
},
|
||||
valorBusca: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
|
@ -107,6 +119,12 @@ export default defineComponent({
|
|||
setup(props, { emit }) {
|
||||
const temAcoesCabecalho = computed(() => props.acoesCabecalho.length > 0)
|
||||
|
||||
const tooltipFiltro = computed(() => {
|
||||
const { quantidade, rotulos } = props.filtrosAtivosResumo
|
||||
if (quantidade === 0) return "Filtro avançado"
|
||||
return `Filtros ativos (${quantidade}):\n${rotulos.map((r) => `• ${r}`).join("\n")}`
|
||||
})
|
||||
|
||||
function emitBuscar(texto: string) {
|
||||
emit("buscar", texto)
|
||||
}
|
||||
|
|
@ -119,7 +137,7 @@ export default defineComponent({
|
|||
emit("filtroAvancado")
|
||||
}
|
||||
|
||||
return { temAcoesCabecalho, emitBuscar, emitColunas, emitFiltroAvancado }
|
||||
return { temAcoesCabecalho, tooltipFiltro, emitBuscar, emitColunas, emitFiltroAvancado }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
@ -131,4 +149,28 @@ export default defineComponent({
|
|||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.eli-tabela__filtro-icone-container {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.eli-tabela__filtro-badge {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
right: -9px;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
padding: 0 4px;
|
||||
border-radius: 9999px;
|
||||
background: var(--cor-eli-erro, #dc3545);
|
||||
color: #fff;
|
||||
font-size: 0.65rem;
|
||||
font-weight: 700;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
182
src/componentes/EliTabela/EliTabelaModalColunasItens.css
Normal file
182
src/componentes/EliTabela/EliTabelaModalColunasItens.css
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
225
src/componentes/EliTabela/EliTabelaModalFiltroFormula.css
Normal file
225
src/componentes/EliTabela/EliTabelaModalFiltroFormula.css
Normal file
|
|
@ -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;
|
||||
}
|
||||
66
src/componentes/EliTabela/EliTabelaSelecao.css
Normal file
66
src/componentes/EliTabela/EliTabelaSelecao.css
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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<T> = {
|
|||
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<Extract<keyof T, string>> | undefined
|
||||
/** vai gerar filtro e mescla com os filtros já existentes , id é gerado de forma altomatica para esse local*/
|
||||
filtro: (
|
||||
valor: unknown,
|
||||
) =>
|
||||
| Pick<
|
||||
tipoCondicaoFiltroDeltaTipado<Extract<keyof T, string>>,
|
||||
"coluna" | "operador" | "valor"
|
||||
>
|
||||
| undefined
|
||||
/** componente de entrada que vai receber o valor para o filtro */
|
||||
entrada: ComponenteEntrada
|
||||
}[]
|
||||
}
|
||||
;("")
|
||||
|
|
|
|||
|
|
@ -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<string> } =>
|
||||
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<string, string> = {}
|
||||
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<string, string> = {
|
||||
"=": "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<string, unknown>])
|
||||
: [entrada as string, {} as Record<string, unknown>]
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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}`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<z.infer<typeof zAreaAtuacao>, string> = {
|
||||
mineracao: "Mineração",
|
||||
agro: "Agropecuária",
|
||||
turismo: "Turismo",
|
||||
tecnologia: "Tecnologia",
|
||||
educacao: "Educação",
|
||||
saude: "Saúde",
|
||||
}
|
||||
|
||||
export type tipoEmpreendedores = z.infer<typeof zEmpreendedores>
|
||||
|
||||
// == 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"
|
||||
|
|
|
|||
249
src/playground/api_simulador/dadosSeed.ts
Normal file
249
src/playground/api_simulador/dadosSeed.ts
Normal file
|
|
@ -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",
|
||||
},
|
||||
]
|
||||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export function useTabelaPlayground() {
|
|||
telefone: "",
|
||||
faturamento: 0,
|
||||
peso_kg: 0,
|
||||
ativo: true,
|
||||
areas_atuacao: [],
|
||||
})
|
||||
const erroCadastro = ref<string | undefined>(undefined)
|
||||
|
|
@ -54,6 +55,7 @@ export function useTabelaPlayground() {
|
|||
telefone: "",
|
||||
faturamento: 0,
|
||||
peso_kg: 0,
|
||||
ativo: true,
|
||||
areas_atuacao: [],
|
||||
}
|
||||
erroCadastro.value = undefined
|
||||
|
|
|
|||
|
|
@ -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<Extract<keyof tipoEmpreendedores, string>>()
|
||||
/** Mapeamento de cor por área de atuação para uso nos chips da tabela */
|
||||
const corAreaAtuacao: Record<tipoEmpreendedores["areas_atuacao"][number], string> = {
|
||||
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<tipoEmpreendedores>["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<tipoEmpreendedores>["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<tipoEmpreendedores>["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<tipoEmpreendedores>["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<tipoEmpreendedores>["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,
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue