Atualização floresta
This commit is contained in:
parent
0886fd5c3e
commit
2d4d8f8929
103 changed files with 19266 additions and 14160 deletions
|
|
@ -1,33 +1,78 @@
|
|||
<template>
|
||||
<v-container class="py-6">
|
||||
<h1 class="text-h5 mb-4">Playground — eli-vue</h1>
|
||||
<div style="display: flex; flex-direction: row; min-height: 100vh;">
|
||||
<!-- Seção de Componentes -->
|
||||
<div style="flex: 1; min-width: 0; overflow-y: auto;">
|
||||
<v-container class="py-6">
|
||||
<h1 class="text-h5 mb-4">Playground — eli-vue</h1>
|
||||
|
||||
<v-tabs v-model="aba" color="primary" density="comfortable">
|
||||
<v-tab value="botao">Botão</v-tab>
|
||||
<v-tab value="indicador">Indicador</v-tab>
|
||||
<v-tab value="cartao">Cartão</v-tab>
|
||||
<v-tab value="entradas">Entradas</v-tab>
|
||||
<v-tab value="data_hora">Data e hora</v-tab>
|
||||
<v-tab value="tabela">Tabela</v-tab>
|
||||
<v-tab value="ola_mundo">Demo</v-tab>
|
||||
</v-tabs>
|
||||
|
||||
<v-divider class="my-4" />
|
||||
|
||||
<v-tabs v-model="aba" color="primary" density="comfortable">
|
||||
<v-tab value="botao">Botão</v-tab>
|
||||
<v-tab value="indicador">Indicador</v-tab>
|
||||
<v-tab value="cartao">Cartão</v-tab>
|
||||
<v-tab value="entradas">Entradas</v-tab>
|
||||
<v-tab value="data_hora">Data e hora</v-tab>
|
||||
<v-tab value="tabela">Tabela</v-tab>
|
||||
<v-tab value="ola_mundo">Demo</v-tab>
|
||||
</v-tabs>
|
||||
<BotaoPlayground v-if="aba === 'botao'" />
|
||||
<IndicadorPlayground v-else-if="aba === 'indicador'" />
|
||||
<CartaoPlayground v-else-if="aba === 'cartao'" />
|
||||
<EntradasPlayground v-else-if="aba === 'entradas'" />
|
||||
<DataHoraPlayground v-else-if="aba === 'data_hora'" />
|
||||
<TabelaPlayground v-else-if="aba === 'tabela'" />
|
||||
<OlaMundoPlayground v-else />
|
||||
</v-container>
|
||||
</div>
|
||||
|
||||
<v-divider class="my-4" />
|
||||
<!-- Console Global do Playground -->
|
||||
<div
|
||||
class="playground-console"
|
||||
style="width: 400px; flex-shrink: 0; background: #121212; color: #a3e635; padding: 24px 20px; border-left: 1px solid #333; display: flex; flex-direction: column; font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; font-size: 12px;"
|
||||
>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #333; padding-bottom: 12px; margin-bottom: 16px;">
|
||||
<span style="font-weight: 700; color: #fff; text-transform: uppercase; letter-spacing: 1.5px; font-size: 11px; display: flex; align-items: center; gap: 8px;">
|
||||
Console
|
||||
<div style="width: 6px; height: 6px; border-radius: 50%; background-color: #22c55e; box-shadow: 0 0 8px #22c55e;"></div>
|
||||
</span>
|
||||
<button
|
||||
@click="logsConsole = []"
|
||||
style="font-size: 10px; cursor: pointer; color: #ef4444; background: rgba(239, 68, 68, 0.1); border: 1px solid rgba(239, 68, 68, 0.2); border-radius: 4px; padding: 2px 8px; font-family: inherit;"
|
||||
>LIMPAR</button>
|
||||
</div>
|
||||
|
||||
<BotaoPlayground v-if="aba === 'botao'" />
|
||||
<IndicadorPlayground v-else-if="aba === 'indicador'" />
|
||||
<CartaoPlayground v-else-if="aba === 'cartao'" />
|
||||
<EntradasPlayground v-else-if="aba === 'entradas'" />
|
||||
<DataHoraPlayground v-else-if="aba === 'data_hora'" />
|
||||
<TabelaPlayground v-else-if="aba === 'tabela'" />
|
||||
<OlaMundoPlayground v-else />
|
||||
</v-container>
|
||||
<div style="flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 8px;" class="playground-console-scroll">
|
||||
<div v-if="logsConsole.length === 0" style="color: #52525b; font-size: 11px;">Nenhum log registrado ainda...</div>
|
||||
<pre
|
||||
v-for="(log, i) in logsConsole"
|
||||
:key="i"
|
||||
style="font-size: 11px; white-space: pre-wrap; word-break: break-all; color: #a1a1aa; border-left: 2px solid #52525b; padding-left: 8px; margin: 0;"
|
||||
>{{ log }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.playground-console-scroll::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
.playground-console-scroll::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
.playground-console-scroll::-webkit-scrollbar-thumb {
|
||||
background: #3f3f46;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.playground-console-scroll::-webkit-scrollbar-thumb:hover {
|
||||
background: #52525b;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue"
|
||||
import { defineComponent, provide, ref } from "vue"
|
||||
import BotaoPlayground from "./botao.playground.vue"
|
||||
import CartaoPlayground from "./cartao.playground.vue"
|
||||
import DataHoraPlayground from "./data_hora.playground.vue"
|
||||
|
|
@ -65,6 +110,15 @@ const mapaAbaParaHash: Record<AbaPlayground, string> = {
|
|||
ola_mundo: "demo",
|
||||
}
|
||||
|
||||
/** Chave de injeção para o console do playground */
|
||||
export const PLAYGROUND_CONSOLE_KEY = "playgroundConsole"
|
||||
|
||||
/** Tipagem do console injetado */
|
||||
export interface PlaygroundConsole {
|
||||
adicionarLog: (acao: string, req: unknown, res: unknown) => void
|
||||
logMensagem: (mensagem: string) => void
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: "App",
|
||||
components: {
|
||||
|
|
@ -76,43 +130,34 @@ export default defineComponent({
|
|||
TabelaPlayground,
|
||||
OlaMundoPlayground,
|
||||
},
|
||||
data() {
|
||||
setup() {
|
||||
const logsConsole = ref<string[]>([])
|
||||
|
||||
/** Adiciona um log estruturado ao console global do playground */
|
||||
function adicionarLog(acao: string, req: unknown, res: unknown) {
|
||||
const time = new Date().toLocaleTimeString()
|
||||
logsConsole.value.unshift(
|
||||
`[${time}] 🌐 ${acao}\nReq: ${JSON.stringify(req, null, 2)}\nRes: ${JSON.stringify(res, null, 2)}`,
|
||||
)
|
||||
}
|
||||
|
||||
/** Adiciona uma mensagem simples ao console */
|
||||
function logMensagem(mensagem: string) {
|
||||
const time = new Date().toLocaleTimeString()
|
||||
logsConsole.value.unshift(`[${time}] ${mensagem}`)
|
||||
}
|
||||
|
||||
const consoleApi: PlaygroundConsole = { adicionarLog, logMensagem }
|
||||
provide(PLAYGROUND_CONSOLE_KEY, consoleApi)
|
||||
|
||||
return {
|
||||
aba: "botao" as AbaPlayground,
|
||||
fontVars: {
|
||||
eli: "",
|
||||
v: "",
|
||||
body: "",
|
||||
},
|
||||
googleSansCheck: "(checando...)" as string,
|
||||
logsConsole,
|
||||
aba: ref<AbaPlayground>("botao"),
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.sincronizarAbaComHash()
|
||||
window.addEventListener("hashchange", this.sincronizarAbaComHash)
|
||||
|
||||
// Coleta variáveis de fonte e confirma carregamento via FontFaceSet.
|
||||
this.fontVars.eli = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--eli-font-family")
|
||||
.trim()
|
||||
this.fontVars.v = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--v-font-family")
|
||||
.trim()
|
||||
this.fontVars.body = getComputedStyle(document.body).fontFamily
|
||||
|
||||
// Espera fonts carregarem e então checa se o navegador considera Google Sans disponível
|
||||
// (isso não garante 100% que TODOS pesos/estilos foram baixados, mas confirma que a face existe/foi carregada)
|
||||
document.fonts.ready
|
||||
.then(() => {
|
||||
this.googleSansCheck = String(
|
||||
document.fonts.check('12px "Google Sans"'),
|
||||
)
|
||||
// atualiza body depois do load, pois pode mudar após o font-face carregar
|
||||
this.fontVars.body = getComputedStyle(document.body).fontFamily
|
||||
})
|
||||
.catch(() => {
|
||||
this.googleSansCheck = "erro"
|
||||
})
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener("hashchange", this.sincronizarAbaComHash)
|
||||
|
|
@ -129,19 +174,14 @@ export default defineComponent({
|
|||
},
|
||||
sincronizarAbaComHash() {
|
||||
const hash = this.normalizarHash()
|
||||
|
||||
const aba = mapaHashParaAba[hash] ?? "botao"
|
||||
|
||||
if (this.aba !== aba) {
|
||||
this.aba = aba
|
||||
}
|
||||
|
||||
// Se a URL estiver vazia/fora do padrão, normalizamos.
|
||||
this.sincronizarHashComAba()
|
||||
},
|
||||
sincronizarHashComAba() {
|
||||
const destino = `#/${mapaAbaParaHash[this.aba]}`
|
||||
|
||||
if (window.location.hash !== destino) {
|
||||
window.location.hash = destino
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,445 +0,0 @@
|
|||
import { respostaComuns, type tipoResposta } from "p-respostas"
|
||||
import type { tipoFiltro } from "@/componentes/EliTabela/types-eli-tabela"
|
||||
|
||||
export type Linha = {
|
||||
codigo: string
|
||||
empreendedor: string
|
||||
empreendimento: string
|
||||
documento: string
|
||||
email: string
|
||||
telefone: string
|
||||
faturamento: number
|
||||
peso_kg: number
|
||||
criado_em: string
|
||||
atualizado_em: string
|
||||
}
|
||||
|
||||
// == SIMULAÇÃO DE API (CRUD) ==
|
||||
// Dados isolados (simulando um banco de dados no servidor)
|
||||
const _bancoDeDados: Linha[] = [
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-09T16:15:00Z",
|
||||
atualizado_em: "2026-01-29T15:30:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-02T10:00:00-03:00",
|
||||
atualizado_em: "2026-01-20T08:10:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2025-12-10T09:30:00-03:00",
|
||||
atualizado_em: "2026-01-15T19:20:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-12T12:00:00-03:00",
|
||||
atualizado_em: "2026-01-28T11:00:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-01T00:00:00Z",
|
||||
atualizado_em: "2026-01-10T13:45:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-05T07:25:00-03:00",
|
||||
atualizado_em: "2026-01-23T20:15:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-03T14:00:00-03:00",
|
||||
atualizado_em: "2026-01-29T09:00:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2025-11-18T16:10:00-03:00",
|
||||
atualizado_em: "2026-01-25T10:40:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-08T12:12:00-03:00",
|
||||
atualizado_em: "2026-01-21T17:55:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-11T11:00:00-03:00",
|
||||
atualizado_em: "2026-01-18T11:30:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-07T08:00:00-03:00",
|
||||
atualizado_em: "2026-01-16T14:10:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-06T10:30:00-03:00",
|
||||
atualizado_em: "2026-01-22T18:30:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-04T13:30:00-03:00",
|
||||
atualizado_em: "2026-01-26T09:20:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-02T18:40:00-03:00",
|
||||
atualizado_em: "2026-01-27T12:00:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-09T09:00:00-03:00",
|
||||
atualizado_em: "2026-01-19T15:35:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-12T19:15:00-03:00",
|
||||
atualizado_em: "2026-01-29T10:50:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
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,
|
||||
criado_em: "2026-01-10T15:00:00-03:00",
|
||||
atualizado_em: "2026-01-14T16:00:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
empreendedor: "Paulo Henrique",
|
||||
empreendimento: "Henrique Soluções Elétricas",
|
||||
documento: "44.556.778/0001-90",
|
||||
email: "paulo@solucoeseletricas.com",
|
||||
telefone: "(13) 98810-2020",
|
||||
faturamento: 50000,
|
||||
peso_kg: 17.9,
|
||||
criado_em: "2026-01-13T10:10:00-03:00",
|
||||
atualizado_em: "2026-01-24T21:00:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
empreendedor: "Renata Souza",
|
||||
empreendimento: "Souza Traduções Especializadas",
|
||||
documento: "11.223.344/0001-08",
|
||||
email: "renata@souzatraducoes.com",
|
||||
telefone: "(24) 99661-7788",
|
||||
faturamento: 6400,
|
||||
peso_kg: 9.9,
|
||||
criado_em: "2026-01-08T07:00:00-03:00",
|
||||
atualizado_em: "2026-01-28T09:05:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
empreendedor: "Tiago Moura",
|
||||
empreendimento: "Moura Agro Sustentável",
|
||||
documento: "78.912.345/0001-67",
|
||||
email: "tiago@mouraagro.com",
|
||||
telefone: "(68) 99999-4545",
|
||||
faturamento: 7200,
|
||||
peso_kg: 13.5,
|
||||
criado_em: "2026-01-03T12:00:00-03:00",
|
||||
atualizado_em: "2026-01-23T12:45:00-03:00",
|
||||
},
|
||||
{
|
||||
codigo: String(Math.random()),
|
||||
empreendedor: "Viviane Castro",
|
||||
empreendimento: "Castro Arte & Cerâmica",
|
||||
documento: "32.165.498/0001-87",
|
||||
email: "viviane@castroarte.com",
|
||||
telefone: "(81) 98787-1212",
|
||||
faturamento: 2800,
|
||||
peso_kg: 4.6,
|
||||
criado_em: "2026-01-01T09:00:00-03:00",
|
||||
atualizado_em: "2026-01-17T09:30:00-03:00",
|
||||
},
|
||||
]
|
||||
|
||||
// Helpers internos da API (não expostos)
|
||||
const _filtrarPorBusca = (lista: Linha[], texto?: string) => {
|
||||
const termo = texto?.trim().toLowerCase()
|
||||
if (!termo) return [...lista]
|
||||
return lista.filter((linha) =>
|
||||
[linha.empreendedor, linha.empreendimento].some((v) =>
|
||||
v.toLowerCase().includes(termo),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
const _comparar = (
|
||||
op: string,
|
||||
valLine: unknown,
|
||||
valFiltro: unknown,
|
||||
): boolean => {
|
||||
switch (op) {
|
||||
case "=":
|
||||
return valLine == valFiltro
|
||||
case "!=":
|
||||
return valLine != valFiltro
|
||||
case ">":
|
||||
return Number(valLine) > Number(valFiltro)
|
||||
case ">=":
|
||||
return Number(valLine) >= Number(valFiltro)
|
||||
case "<":
|
||||
return Number(valLine) < Number(valFiltro)
|
||||
case "<=":
|
||||
return Number(valLine) <= Number(valFiltro)
|
||||
case "like":
|
||||
return String(valLine ?? "")
|
||||
.toLowerCase()
|
||||
.includes(String(valFiltro ?? "").toLowerCase())
|
||||
case "in": {
|
||||
const arr = Array.isArray(valFiltro)
|
||||
? valFiltro
|
||||
: String(valFiltro ?? "")
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
return arr.includes(String(valLine))
|
||||
}
|
||||
case "isNull":
|
||||
return valLine === null || valLine === undefined || valLine === ""
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const _filtrarAvancado = (lista: Linha[], filtros?: tipoFiltro<Linha>[]) => {
|
||||
if (!filtros?.length) return [...lista]
|
||||
|
||||
return lista.filter((linha) =>
|
||||
filtros.every((f) => {
|
||||
// f é um objeto { [coluna]: { [op]: valor } }
|
||||
// Percorremos as colunas (chaves)
|
||||
return Object.entries(f).every(([coluna, condicoes]) => {
|
||||
// Ignorar chaves de lógica complexa por enquanto no playground
|
||||
if (coluna === "AND" || coluna === "OR") return true
|
||||
|
||||
const valLinha = linha[coluna as keyof Linha]
|
||||
|
||||
// Se condicoes for objeto { op: val }
|
||||
if (typeof condicoes === "object" && condicoes !== null) {
|
||||
return Object.entries(condicoes).every(([op, val]) =>
|
||||
_comparar(op, valLinha, val),
|
||||
)
|
||||
}
|
||||
// Se for valor direto (igualdade implícita? ou erro?)
|
||||
// Vamos assumir igualdade se não for objeto
|
||||
return _comparar("=", valLinha, condicoes)
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
const _ordenar = (
|
||||
lista: Linha[],
|
||||
p?: { coluna_ordem?: keyof Linha; direcao_ordem?: "asc" | "desc" },
|
||||
) => {
|
||||
if (!p?.coluna_ordem) return [...lista]
|
||||
const dir = p.direcao_ordem === "desc" ? -1 : 1
|
||||
const key = p.coluna_ordem
|
||||
return [...lista].sort((a, b) => {
|
||||
const va = String(a[key] ?? "")
|
||||
const vb = String(b[key] ?? "")
|
||||
return dir * va.localeCompare(vb, "pt-BR", { sensitivity: "base" })
|
||||
})
|
||||
}
|
||||
|
||||
const delay = (ms: number) =>
|
||||
new Promise((r) => setTimeout(r, ms * Math.random() + 2000))
|
||||
|
||||
// -- API PÚBLICA SIMULADA --
|
||||
|
||||
export async function api_ler(params: {
|
||||
texto_busca?: string
|
||||
filtros?: tipoFiltro<Linha>[]
|
||||
coluna_ordem?: keyof Linha
|
||||
direcao_ordem?: "asc" | "desc"
|
||||
limit?: number
|
||||
offSet?: number
|
||||
}) {
|
||||
await delay(600) // simula latência de rede
|
||||
|
||||
// 1. Filtro
|
||||
let resultado = params.texto_busca
|
||||
? _filtrarPorBusca(_bancoDeDados, params.texto_busca)
|
||||
: _filtrarAvancado(_bancoDeDados, params.filtros)
|
||||
|
||||
// 2. Ordenação
|
||||
resultado = _ordenar(resultado, params)
|
||||
|
||||
// 3. Paginação
|
||||
const limit = Math.max(1, Number(params.limit ?? 10))
|
||||
const offset = Math.max(0, Number(params.offSet ?? 0))
|
||||
const paginado = resultado.slice(offset, offset + limit)
|
||||
|
||||
return {
|
||||
total: resultado.length,
|
||||
dados: paginado,
|
||||
}
|
||||
}
|
||||
|
||||
export async function api_criar(
|
||||
dados: Partial<Linha>,
|
||||
): Promise<tipoResposta<Linha>> {
|
||||
await delay(1500) // simula tempo de salvar
|
||||
|
||||
if (!dados.empreendedor) {
|
||||
return respostaComuns.erro("Empreendedor é obrigatório")
|
||||
}
|
||||
|
||||
const novo: Linha = {
|
||||
codigo: Math.random().toString(),
|
||||
empreendedor: dados.empreendedor || "Novo",
|
||||
empreendimento: dados.empreendimento || "Novo Emp",
|
||||
documento: dados.documento || "00.000.000/0000-00",
|
||||
email: dados.email || "novo@email.com",
|
||||
telefone: dados.telefone || "(00) 0000-0000",
|
||||
faturamento: dados.faturamento || 0,
|
||||
peso_kg: dados.peso_kg || 0,
|
||||
criado_em: new Date().toISOString(),
|
||||
atualizado_em: new Date().toISOString(),
|
||||
}
|
||||
_bancoDeDados.unshift(novo)
|
||||
return respostaComuns.valor(novo)
|
||||
}
|
||||
|
||||
export async function api_editar(
|
||||
codigo: string,
|
||||
novosDados: Partial<Linha>,
|
||||
): Promise<tipoResposta<Linha>> {
|
||||
await delay(1000)
|
||||
const idx = _bancoDeDados.find((l) => l.codigo === codigo)
|
||||
if (!idx) return respostaComuns.erro("Registro não encontrado")
|
||||
Object.assign(idx, novosDados)
|
||||
return respostaComuns.valor(idx)
|
||||
}
|
||||
|
||||
export async function api_deletar(
|
||||
codigo: string,
|
||||
): Promise<tipoResposta<string>> {
|
||||
await delay(800)
|
||||
const idx = _bancoDeDados.findIndex((l) => l.codigo === codigo)
|
||||
if (idx !== -1) {
|
||||
_bancoDeDados.splice(idx, 1)
|
||||
return respostaComuns.valor("Registro deletado com sucesso")
|
||||
}
|
||||
return respostaComuns.erro("Registro não encontrado")
|
||||
}
|
||||
195
src/playground/api_simulador/banco.ts
Normal file
195
src/playground/api_simulador/banco.ts
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
import alasql from "alasql"
|
||||
import type { tipoCondicaoFiltroDelta, zFiltroDelta } from "p-comuns"
|
||||
import type z from "zod"
|
||||
import { _dados_empreendedores, type tipoEmpreendedores } from "./dados"
|
||||
|
||||
const zCondicaoFiltroDeltaParaQuery = ({
|
||||
condicao,
|
||||
}: {
|
||||
condicao: tipoCondicaoFiltroDelta
|
||||
}): { sql: string; parametro: Record<string, unknown> } => {
|
||||
const coluna = condicao.coluna
|
||||
|
||||
const retorno: { sql: string; parametro: Record<string, unknown> } = {
|
||||
sql: "",
|
||||
parametro: {},
|
||||
}
|
||||
|
||||
if (condicao.operador === "!=") {
|
||||
retorno.sql = `${coluna} != ${condicao.id}`
|
||||
retorno.parametro[condicao.id] = condicao.valor
|
||||
} else if (condicao.operador === "=") {
|
||||
retorno.sql = `${coluna} = ${condicao.id}`
|
||||
retorno.parametro[condicao.id] = condicao.valor
|
||||
} else if (condicao.operador === ">") {
|
||||
retorno.sql = `${coluna} > ${condicao.id}`
|
||||
retorno.parametro[condicao.id] = condicao.valor
|
||||
} else if (condicao.operador === "<") {
|
||||
retorno.sql = `${coluna} < ${condicao.id}`
|
||||
retorno.parametro[condicao.id] = condicao.valor
|
||||
} else if (condicao.operador === ">=") {
|
||||
retorno.sql = `${coluna} >= ${condicao.id}`
|
||||
retorno.parametro[condicao.id] = condicao.valor
|
||||
} else if (condicao.operador === "<=") {
|
||||
retorno.sql = `${coluna} <= ${condicao.id}`
|
||||
retorno.parametro[condicao.id] = condicao.valor
|
||||
} else if (condicao.operador === "like") {
|
||||
retorno.sql = `LOWER(${coluna}) LIKE LOWER(${condicao.id})`
|
||||
retorno.parametro[condicao.id] = condicao.valor.includes("%")
|
||||
? 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})`
|
||||
retorno.parametro[condicao.id] = Array.isArray(condicao.valor)
|
||||
? condicao.valor
|
||||
: String(condicao.valor)
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
} else {
|
||||
throw new Error(`Operador inválido: ${condicao.operador as string}`)
|
||||
}
|
||||
|
||||
return retorno
|
||||
}
|
||||
|
||||
/**
|
||||
* Converte um `FiltroDelta` em um fragmento SQL com parâmetros posicionais para o Alasql.
|
||||
* Realiza dois passes na fórmula:
|
||||
* 1. Substitui cada `{id}` pelo sub-SQL da condição correspondente
|
||||
* 2. Extrai os parâmetros em ordem e troca `{id}` por `?`
|
||||
* Por fim, traduz os operadores lógicos pt-BR (E, OU, NAO) para SQL (AND, OR, NOT).
|
||||
*
|
||||
* @param filtros - Um FiltroDelta (fórmula + condições) ou `undefined` para sem filtro
|
||||
* @returns Fragmento SQL e array de parâmetros posicionais
|
||||
*/
|
||||
export const zFiltroDeltaParaQuery = ({
|
||||
filtros,
|
||||
}: {
|
||||
filtros: z.infer<typeof zFiltroDelta> | undefined
|
||||
}): { sql: string; parametros: unknown[] } => {
|
||||
if (!filtros) return { sql: "", parametros: [] }
|
||||
|
||||
const parametros: unknown[] = []
|
||||
const sqlParametros: Record<string, unknown> = {}
|
||||
|
||||
// 1. Substituir `{id}` pelo sub-sql de cada condição e extrair parâmetros
|
||||
let sql = filtros.formula.replace(/\{[\w:]+\}/g, (match) => {
|
||||
const id = match.slice(1, -1)
|
||||
const condicao = filtros.condicoes.find((c) => c.id === match)
|
||||
|
||||
if (!condicao) return match
|
||||
|
||||
const { sql: sqlCondicao, parametro } = zCondicaoFiltroDeltaParaQuery({ condicao })
|
||||
sqlParametros[id] = parametro[match]
|
||||
|
||||
return ` (${sqlCondicao}) `
|
||||
})
|
||||
|
||||
// 2. Extrair parâmetros na ordem correta, trocando os {id} restantes por ?
|
||||
sql = sql.replace(/\{[\w:]+\}/g, (match) => {
|
||||
const id = match.slice(1, -1)
|
||||
parametros.push(sqlParametros[id])
|
||||
return "?"
|
||||
})
|
||||
|
||||
// 3. Traduzir operadores lógicos do filtroDelta
|
||||
sql = sql
|
||||
.replace(/\bNAO\b/g, " NOT ")
|
||||
.replace(/\bE\b/g, " AND ")
|
||||
.replace(/\bOU\b/g, " OR ")
|
||||
|
||||
return { sql, parametros }
|
||||
}
|
||||
|
||||
/**
|
||||
* Combina um array de FiltroDelta em uma cláusula WHERE única para o Alasql.
|
||||
* Cada filtro é convertido individualmente e combinado com AND.
|
||||
* Retorna `"1=1"` (sem filtro) quando o array é vazio.
|
||||
*
|
||||
* @param filtrosArray - Lista de FiltroDelta a combinar; `undefined` ou vazio = sem filtro
|
||||
* @returns Fragmento SQL e array de parâmetros posicionais combinados
|
||||
*/
|
||||
export const zFiltrosArrayParaAlasql = (
|
||||
filtrosArray?: z.infer<typeof zFiltroDelta>[],
|
||||
): { sql: string; parametros: unknown[] } => {
|
||||
if (!filtrosArray?.length) return { sql: "1=1", parametros: [] }
|
||||
|
||||
const queryStrings: string[] = []
|
||||
const todosParametros: unknown[] = []
|
||||
|
||||
for (const filtro of filtrosArray) {
|
||||
const { sql, parametros } = zFiltroDeltaParaQuery({ filtros: filtro })
|
||||
if (sql) {
|
||||
queryStrings.push(`(${sql})`)
|
||||
todosParametros.push(...parametros)
|
||||
}
|
||||
}
|
||||
|
||||
if (queryStrings.length === 0) return { sql: "1=1", parametros: [] }
|
||||
return { sql: queryStrings.join(" AND "), parametros: todosParametros }
|
||||
}
|
||||
|
||||
/**
|
||||
* Executa uma consulta no banco em memória Alasql com suporte a filtros, ordenação e paginação.
|
||||
* Simula o comportamento de uma API REST real para fins de demonstração no playground.
|
||||
* Os filtros são compilados pela `zFiltroDeltaParaQuery`; a contagem total é obtida antes da paginação.
|
||||
*
|
||||
* @param params.filtros - FiltroDelta opcional (filtros avançados do usuário)
|
||||
* @param params.coluna_ordem - Nome da coluna para ordenar
|
||||
* @param params.direcao_ordem - Direção da ordenação (`"asc"` | `"desc"`)
|
||||
* @param params.limit - Número de registros por página
|
||||
* @param params.offSet - Deslocamento (página × limit)
|
||||
* @returns Objeto `{ total, dados }` com o total geral e a página de registros
|
||||
*/
|
||||
export const executarConsultaAlasql = (params: {
|
||||
filtros?: z.infer<typeof zFiltroDelta>
|
||||
coluna_ordem?: string
|
||||
direcao_ordem?: "asc" | "desc"
|
||||
limit?: number
|
||||
offSet?: number
|
||||
}): { total: number; dados: tipoEmpreendedores[] } => {
|
||||
let whereSql = "1=1"
|
||||
const parametros: unknown[] = []
|
||||
|
||||
// 2. Filtros Avançados
|
||||
if (params.filtros) {
|
||||
const parsed = zFiltroDeltaParaQuery({ filtros: params.filtros })
|
||||
if (parsed.sql && parsed.sql !== "1=1") {
|
||||
whereSql += ` AND (${parsed.sql})`
|
||||
parametros.push(...parsed.parametros)
|
||||
}
|
||||
}
|
||||
|
||||
// Montagem da Query base
|
||||
let sql = `SELECT * FROM ? WHERE ${whereSql}`
|
||||
|
||||
// 3. Ordenação
|
||||
if (params.coluna_ordem) {
|
||||
const dir = params.direcao_ordem === "desc" ? "DESC" : "ASC"
|
||||
sql += ` ORDER BY ${params.coluna_ordem} ${dir}`
|
||||
}
|
||||
|
||||
// Obter Total antes da paginação
|
||||
const sqlCount = `SELECT COUNT(*) as t FROM ? WHERE ${whereSql}`
|
||||
const countResult = alasql(sqlCount, [_dados_empreendedores, ...parametros]) as {
|
||||
t: number
|
||||
}[]
|
||||
const total = countResult[0].t
|
||||
|
||||
// 4. Paginação
|
||||
const limit = Math.max(1, Number(params.limit ?? 10))
|
||||
const offset = Math.max(0, Number(params.offSet ?? 0))
|
||||
sql += ` LIMIT ${limit} OFFSET ${offset}`
|
||||
|
||||
console.groupCollapsed("🔍 [Alasql] Consulta executada")
|
||||
console.log("SQL:", sql)
|
||||
console.log("Parâmetros:", parametros)
|
||||
console.log("Total Encontrado:", total)
|
||||
console.groupEnd()
|
||||
|
||||
const dados = alasql(sql, [_dados_empreendedores, ...parametros]) as tipoEmpreendedores[]
|
||||
|
||||
return { total, dados }
|
||||
}
|
||||
250
src/playground/api_simulador/dados.ts
Normal file
250
src/playground/api_simulador/dados.ts
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
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++}`
|
||||
|
||||
/** Schema Zod do registro de empreendedor — usado para validação e inferência de tipo no playground. */
|
||||
export const zEmpreendedores = z.object({
|
||||
codigo: z.string(),
|
||||
empreendedor: z.string(),
|
||||
empreendimento: z.string(),
|
||||
documento: z.string(),
|
||||
email: z.string(),
|
||||
telefone: z.string(),
|
||||
faturamento: z.number(),
|
||||
peso_kg: z.number(),
|
||||
areas_atuacao: z.array(
|
||||
z.enum(["mineracao", "agro", "turismo", "tecnologia", "educacao", "saude"]),
|
||||
),
|
||||
criado_em: z.string(),
|
||||
atualizado_em: z.string(),
|
||||
})
|
||||
|
||||
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",
|
||||
},
|
||||
]
|
||||
114
src/playground/api_simulador/index.ts
Normal file
114
src/playground/api_simulador/index.ts
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import { respostaComuns, type tipoResposta } from "p-respostas"
|
||||
import type z from "zod"
|
||||
import { executarConsultaAlasql } from "./banco"
|
||||
import { _dados_empreendedores, type tipoEmpreendedores } from "./dados"
|
||||
import { type tipoFuncaoApiSimulacao, zApiSimulacao } from "./tipagem"
|
||||
|
||||
const delay = (ms: number) => new Promise((r) => setTimeout(r, ms * Math.random() + 2000))
|
||||
|
||||
// -- API PÚBLICA SIMULADA --
|
||||
|
||||
const api_ler: tipoFuncaoApiSimulacao<"listar"> = async (params) => {
|
||||
// simula latência de rede
|
||||
|
||||
const { total, dados } = executarConsultaAlasql({
|
||||
filtros: params.filtros,
|
||||
coluna_ordem: params.coluna_ordem as string | undefined,
|
||||
direcao_ordem: params.direcao_ordem,
|
||||
limit: params.limit,
|
||||
offSet: params.offSet,
|
||||
})
|
||||
|
||||
return respostaComuns.valor({
|
||||
total,
|
||||
dados,
|
||||
})
|
||||
}
|
||||
|
||||
const api_criar: tipoFuncaoApiSimulacao<"criar"> = async (dados) => {
|
||||
if (!dados.empreendedor) {
|
||||
return respostaComuns.erro("Empreendedor é obrigatório")
|
||||
}
|
||||
|
||||
const novo: tipoEmpreendedores = {
|
||||
codigo: Math.random().toString(),
|
||||
empreendedor: dados.empreendedor || "Novo",
|
||||
empreendimento: dados.empreendimento || "Novo Emp",
|
||||
documento: dados.documento || "00.000.000/0000-00",
|
||||
email: dados.email || "novo@email.com",
|
||||
telefone: dados.telefone || "(00) 0000-0000",
|
||||
faturamento: dados.faturamento || 0,
|
||||
peso_kg: dados.peso_kg || 0,
|
||||
areas_atuacao: dados.areas_atuacao || [],
|
||||
criado_em: new Date().toISOString(),
|
||||
atualizado_em: new Date().toISOString(),
|
||||
}
|
||||
_dados_empreendedores.unshift(novo)
|
||||
return respostaComuns.valor(novo)
|
||||
}
|
||||
|
||||
const api_editar: tipoFuncaoApiSimulacao<"editar"> = async (dados) => {
|
||||
const idx = _dados_empreendedores.find((l) => l.codigo === dados.codigo)
|
||||
if (!idx) return respostaComuns.erro("Registro não encontrado")
|
||||
Object.assign(idx, dados.novosDados)
|
||||
return respostaComuns.valor(idx)
|
||||
}
|
||||
|
||||
const api_deletar: tipoFuncaoApiSimulacao<"deletar"> = async (dados) => {
|
||||
const idx = _dados_empreendedores.findIndex((l) => l.codigo === dados.codigo)
|
||||
if (idx !== -1) {
|
||||
_dados_empreendedores.splice(idx, 1)
|
||||
return respostaComuns.valor("Registro deletado com sucesso")
|
||||
}
|
||||
return respostaComuns.erro("Registro não encontrado")
|
||||
}
|
||||
|
||||
const ApiSimulacao = async <tTipo extends keyof z.infer<typeof zApiSimulacao>>(
|
||||
tIPO: tTipo,
|
||||
req: z.infer<typeof zApiSimulacao>[tTipo]["req"],
|
||||
Callback?: (
|
||||
req: z.infer<typeof zApiSimulacao>[tTipo]["req"],
|
||||
res: tipoResposta<z.infer<typeof zApiSimulacao>[tTipo]["res"]>,
|
||||
) => void,
|
||||
): Promise<tipoResposta<z.infer<typeof zApiSimulacao>[tTipo]["res"]>> => {
|
||||
await delay((2000 * Math.random() + 200) | 0)
|
||||
|
||||
let retorno: tipoResposta<z.infer<typeof zApiSimulacao>[tTipo]["res"]> =
|
||||
respostaComuns.erro("Não entrou!")
|
||||
const zSchema = zApiSimulacao.shape[tIPO]
|
||||
|
||||
try {
|
||||
await zSchema.shape.req.parseAsync(req)
|
||||
|
||||
switch (tIPO) {
|
||||
case "listar":
|
||||
retorno = await api_ler(req as z.infer<typeof zApiSimulacao>["listar"]["req"])
|
||||
break
|
||||
case "criar":
|
||||
retorno = await api_criar(req as z.infer<typeof zApiSimulacao>["criar"]["req"])
|
||||
break
|
||||
case "editar":
|
||||
retorno = await api_editar(req as z.infer<typeof zApiSimulacao>["editar"]["req"])
|
||||
break
|
||||
case "deletar":
|
||||
retorno = await api_deletar(req as z.infer<typeof zApiSimulacao>["deletar"]["req"])
|
||||
break
|
||||
default:
|
||||
retorno = respostaComuns.erro("Tipo de API não encontrado!")
|
||||
}
|
||||
|
||||
if (retorno.eCerto) {
|
||||
await zSchema.shape.res.parseAsync(retorno.valor)
|
||||
}
|
||||
} catch (error) {
|
||||
retorno = respostaComuns.erro((error as Error).message)
|
||||
}
|
||||
|
||||
if (Callback) {
|
||||
Callback(req, retorno)
|
||||
}
|
||||
|
||||
return retorno
|
||||
}
|
||||
|
||||
export { ApiSimulacao }
|
||||
52
src/playground/api_simulador/tipagem.ts
Normal file
52
src/playground/api_simulador/tipagem.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { zFiltroDelta } from "p-comuns"
|
||||
import type { tipoResposta } from "p-respostas"
|
||||
import z from "zod"
|
||||
import { type tipoEmpreendedores, zEmpreendedores } from "./dados"
|
||||
|
||||
const baseApi = <tTipo extends string, tReq extends z.ZodTypeAny, tRes extends z.ZodTypeAny>(
|
||||
tipo: tTipo,
|
||||
req: tReq,
|
||||
res: tRes,
|
||||
) =>
|
||||
z.object({
|
||||
tipo: z.literal(tipo),
|
||||
req: req,
|
||||
res: res,
|
||||
})
|
||||
|
||||
export const zApiSimulacao = z.object({
|
||||
listar: baseApi(
|
||||
"listar",
|
||||
z.object({
|
||||
filtros: zFiltroDelta.optional(),
|
||||
coluna_ordem: z.custom<keyof tipoEmpreendedores>().optional(),
|
||||
direcao_ordem: z.enum(["asc", "desc"]).optional(),
|
||||
limit: z.number().optional(),
|
||||
offSet: z.number().optional(),
|
||||
}),
|
||||
z.object({
|
||||
total: z.number(),
|
||||
dados: z.array(zEmpreendedores),
|
||||
}),
|
||||
),
|
||||
criar: baseApi("criar", zEmpreendedores, zEmpreendedores),
|
||||
editar: baseApi(
|
||||
"editar",
|
||||
z.object({
|
||||
codigo: z.string(),
|
||||
novosDados: zEmpreendedores.partial(),
|
||||
}),
|
||||
zEmpreendedores,
|
||||
),
|
||||
deletar: baseApi(
|
||||
"deletar",
|
||||
z.object({
|
||||
codigo: z.string(),
|
||||
}),
|
||||
z.string(),
|
||||
),
|
||||
})
|
||||
|
||||
export type tipoFuncaoApiSimulacao<tTipo extends keyof z.infer<typeof zApiSimulacao>> = (
|
||||
dados: z.infer<typeof zApiSimulacao>[tTipo]["req"],
|
||||
) => Promise<tipoResposta<z.infer<typeof zApiSimulacao>[tTipo]["res"]>>
|
||||
|
|
@ -121,9 +121,7 @@ export default defineComponent({
|
|||
function itensFiltrados(itens: Card[]) {
|
||||
const q = filtro.value.trim().toLowerCase()
|
||||
if (!q) return itens
|
||||
return itens.filter((i) =>
|
||||
`${i.titulo} ${i.cliente} ${i.valor}`.toLowerCase().includes(q),
|
||||
)
|
||||
return itens.filter((i) => `${i.titulo} ${i.cliente} ${i.valor}`.toLowerCase().includes(q))
|
||||
}
|
||||
|
||||
function criar() {
|
||||
|
|
|
|||
|
|
@ -125,12 +125,7 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue"
|
||||
import {
|
||||
EliEntradaNumero,
|
||||
EliEntradaParagrafo,
|
||||
EliEntradaSelecao,
|
||||
EliEntradaTexto,
|
||||
} from "../index"
|
||||
import { EliEntradaNumero, EliEntradaParagrafo, EliEntradaSelecao, EliEntradaTexto } from "../index"
|
||||
|
||||
export default defineComponent({
|
||||
name: "EntradasPlayground",
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import * as directives from "vuetify/directives"
|
|||
// Estilos globais da lib (font-face + variáveis de fonte)
|
||||
// Importamos DEPOIS do Vuetify para garantir que `--v-font-family` seja sobrescrito.
|
||||
import "../styles/eli-vue-fonts.css"
|
||||
import "../design-tokens.css"
|
||||
|
||||
const themeDefault = {
|
||||
dark: false,
|
||||
|
|
@ -173,22 +174,15 @@ if (import.meta.env.DEV) {
|
|||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
"[eli-vue] --v-font-family:",
|
||||
getComputedStyle(document.documentElement).getPropertyValue(
|
||||
"--v-font-family",
|
||||
),
|
||||
getComputedStyle(document.documentElement).getPropertyValue("--v-font-family"),
|
||||
)
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
"[eli-vue] --eli-font-family:",
|
||||
getComputedStyle(document.documentElement).getPropertyValue(
|
||||
"--eli-font-family",
|
||||
),
|
||||
getComputedStyle(document.documentElement).getPropertyValue("--eli-font-family"),
|
||||
)
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
"[eli-vue] body font-family:",
|
||||
getComputedStyle(document.body).fontFamily,
|
||||
)
|
||||
console.log("[eli-vue] body font-family:", getComputedStyle(document.body).fontFamily)
|
||||
}
|
||||
|
||||
app.use(vuetify).mount("#app")
|
||||
|
|
|
|||
|
|
@ -2,15 +2,14 @@
|
|||
<section class="stack">
|
||||
<h2>EliTabela</h2>
|
||||
|
||||
<!-- Exemplo: botão de ação "Novo" no cabeçalho que adiciona uma linha -->
|
||||
<div style="margin-bottom: 12px">
|
||||
<strong>Selecionados:</strong> {{ selecionados.map(s => s.empreendedor).join(', ') || 'Nenhum' }}
|
||||
</div>
|
||||
<EliTabela :tabela="tabelaOk" :key="versaoTabelaOk" v-model:selecionados="selecionados" />
|
||||
<EliTabela :tabela="tabelaOk" :key="versaoTabelaOk" v-model:selecionados="selecionados" esconderDebug />
|
||||
|
||||
<EliTabela :tabela="tabelaVazia" />
|
||||
<EliTabela :tabela="tabelaVazia" esconderDebug />
|
||||
|
||||
<EliTabela :tabela="tabelaErro" />
|
||||
<EliTabela :tabela="tabelaErro" esconderDebug />
|
||||
|
||||
<!-- Modal de simulação de cadastro -->
|
||||
<v-dialog v-model="mostrarModalCadastro" max-width="500px">
|
||||
|
|
@ -65,359 +64,15 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { BadgeCheck, Eye, Pencil, Plus, Trash2 } from "@lucide/vue"
|
||||
import { criarFiltro26, type tipoFiltro26 } from "p-comuns"
|
||||
import { codigosResposta } from "p-respostas"
|
||||
import { defineComponent, ref } from "vue"
|
||||
import type { ComponenteEntrada } from "@/componentes/EliEntrada/tiposEntradas"
|
||||
import type { tipoEliTabelaConsulta } from "@/componentes/EliTabela"
|
||||
import { celulaTabela, EliTabela } from "@/componentes/EliTabela"
|
||||
|
||||
import { api_criar, api_deletar, api_ler, type Linha } from "./api_simulador"
|
||||
import { defineComponent } from "vue"
|
||||
import { EliTabela } from "@/componentes/EliTabela"
|
||||
import { useTabelaPlayground } from "./useTabelaPlayground"
|
||||
|
||||
export default defineComponent({
|
||||
name: "TabelaPlayground",
|
||||
components: { EliTabela },
|
||||
setup() {
|
||||
const acoesLinha: tipoEliTabelaConsulta<Linha>["acoesLinha"] = [
|
||||
{
|
||||
icone: Eye,
|
||||
cor: "#2563eb",
|
||||
rotulo: "Detalhes",
|
||||
acao: (linha) => {
|
||||
console.log("Visualizar detalhes de", linha.empreendedor)
|
||||
},
|
||||
},
|
||||
{
|
||||
icone: Trash2,
|
||||
cor: "#dc2626",
|
||||
rotulo: "Remover",
|
||||
acao: (linha) => {
|
||||
removerCadastro(linha)
|
||||
},
|
||||
exibir: (linha) => !linha.empreendimento.toLowerCase().includes("café"),
|
||||
},
|
||||
]
|
||||
|
||||
const versaoTabelaOk = ref(0)
|
||||
const selecionados = ref<Linha[]>([])
|
||||
const mostrarModalCadastro = ref(false)
|
||||
const salvando = ref(false)
|
||||
const novoCadastro = ref({
|
||||
empreendedor: "",
|
||||
empreendimento: "",
|
||||
documento: "",
|
||||
email: "",
|
||||
telefone: "",
|
||||
faturamento: 0,
|
||||
peso_kg: 0,
|
||||
})
|
||||
const erroCadastro = ref<string | undefined>(undefined)
|
||||
|
||||
// Armazena a função de atualização da tabela atual (vinda da ação do cabeçalho)
|
||||
let atualizarTabelaCallback: (() => Promise<void>) | undefined
|
||||
|
||||
// A ação do cabeçalho recebe { atualizarConsulta, editarLista, ... }
|
||||
// biome-ignore lint/suspicious/noExplicitAny: playground
|
||||
function abrirModalCadastro(params?: any) {
|
||||
if (params?.atualizarConsulta) {
|
||||
atualizarTabelaCallback = params.atualizarConsulta
|
||||
}
|
||||
|
||||
novoCadastro.value = {
|
||||
empreendedor: "",
|
||||
empreendimento: "",
|
||||
documento: "",
|
||||
email: "",
|
||||
telefone: "",
|
||||
faturamento: 0,
|
||||
peso_kg: 0,
|
||||
}
|
||||
erroCadastro.value = undefined
|
||||
mostrarModalCadastro.value = true
|
||||
}
|
||||
|
||||
async function salvarNovoCadastro() {
|
||||
salvando.value = true
|
||||
erroCadastro.value = undefined
|
||||
try {
|
||||
const resposta = await api_criar(novoCadastro.value)
|
||||
if (resposta.eErro) {
|
||||
erroCadastro.value = resposta.mensagem
|
||||
return
|
||||
}
|
||||
|
||||
// PADRÃO RECOMENDADO:
|
||||
// Se temos o callback de atualização, usamos ele para recarregar os dados na tabela
|
||||
// sem precisar remontar o componente (sem alterar key).
|
||||
if (atualizarTabelaCallback) {
|
||||
await atualizarTabelaCallback()
|
||||
} else {
|
||||
// Fallback (caso não tenha vindo pela ação da tabela): força re-render
|
||||
versaoTabelaOk.value++
|
||||
}
|
||||
|
||||
mostrarModalCadastro.value = false
|
||||
} catch (e) {
|
||||
erroCadastro.value = String(e)
|
||||
} finally {
|
||||
salvando.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function removerCadastro(linha: Linha) {
|
||||
// Exemplo de uso do api_deletar
|
||||
// Não temos um ID único confiável além das propriedades, vamos usar empreendedor como chave neste exemplo simples
|
||||
if (confirm(`Tem certeza que deseja remover ${linha.empreendedor}?`)) {
|
||||
api_deletar("empreendedor", linha.empreendedor).then(() => {
|
||||
versaoTabelaOk.value++
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const tabelaOk: tipoEliTabelaConsulta<Linha> = {
|
||||
nome: "ExemploPlaygroundData",
|
||||
registros_por_consulta: 10,
|
||||
mostrarCaixaDeBusca: true,
|
||||
mostrar_selecao: true,
|
||||
chave_selecao: "empreendedor",
|
||||
acoesTabela: [
|
||||
{
|
||||
posicao: "superior",
|
||||
icone: Plus,
|
||||
cor: "#16a34a",
|
||||
rotulo: "Novo (Superior)",
|
||||
acao: abrirModalCadastro,
|
||||
},
|
||||
{
|
||||
posicao: "inferior",
|
||||
icone: Plus,
|
||||
cor: "#2563eb",
|
||||
rotulo: "Novo (Inferior)",
|
||||
acao: abrirModalCadastro,
|
||||
},
|
||||
],
|
||||
|
||||
colunas: [
|
||||
{
|
||||
rotulo: "Empreendedor",
|
||||
celula: (l) =>
|
||||
celulaTabela("textoSimples", { texto: l.empreendedor }),
|
||||
coluna_ordem: "empreendedor",
|
||||
visivel: true,
|
||||
},
|
||||
{
|
||||
rotulo: "Empreendimento",
|
||||
celula: (l) =>
|
||||
celulaTabela("textoSimples", { texto: l.empreendimento }),
|
||||
coluna_ordem: "empreendimento",
|
||||
visivel: true,
|
||||
},
|
||||
{
|
||||
rotulo: "Documento",
|
||||
celula: (l) => celulaTabela("textoSimples", { texto: l.documento }),
|
||||
coluna_ordem: "documento",
|
||||
visivel: false,
|
||||
},
|
||||
{
|
||||
rotulo: "E-mail",
|
||||
celula: (l) =>
|
||||
celulaTabela("textoTruncado", {
|
||||
texto: l.email,
|
||||
acao: () => {
|
||||
// Exemplo de ação: poderia abrir detalhes
|
||||
alert(`Clicou em ${l.email}`)
|
||||
},
|
||||
}),
|
||||
coluna_ordem: "email",
|
||||
visivel: true,
|
||||
},
|
||||
{
|
||||
rotulo: "Telefone",
|
||||
celula: (l) => celulaTabela("textoSimples", { texto: l.telefone }),
|
||||
coluna_ordem: "telefone",
|
||||
visivel: true,
|
||||
},
|
||||
{
|
||||
rotulo: "Faturamento",
|
||||
celula: (l) =>
|
||||
celulaTabela("numero", {
|
||||
numero: l.faturamento,
|
||||
prefixo: "R$",
|
||||
// Exemplo de ação (clicável)
|
||||
acao: () =>
|
||||
alert(`Faturamento de ${l.empreendedor}: R$ ${l.faturamento}`),
|
||||
}),
|
||||
visivel: true,
|
||||
},
|
||||
{
|
||||
rotulo: "Peso",
|
||||
celula: (l) =>
|
||||
celulaTabela("numero", {
|
||||
numero: l.peso_kg,
|
||||
sufixo: "kg",
|
||||
}),
|
||||
visivel: false,
|
||||
},
|
||||
{
|
||||
rotulo: "Status",
|
||||
celula: (l) =>
|
||||
celulaTabela("tags", {
|
||||
opcoes: [
|
||||
{
|
||||
rotulo: "Ativo",
|
||||
cor: "success",
|
||||
icone: BadgeCheck,
|
||||
},
|
||||
{
|
||||
rotulo: "Editar",
|
||||
cor: "primary",
|
||||
icone: Pencil,
|
||||
acao: () => {
|
||||
alert(`Editar ${l.empreendedor}`)
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
visivel: true,
|
||||
},
|
||||
{
|
||||
rotulo: "Criado em",
|
||||
celula: (l) =>
|
||||
celulaTabela("data", {
|
||||
valor: l.criado_em,
|
||||
formato: "data",
|
||||
}),
|
||||
visivel: false,
|
||||
},
|
||||
{
|
||||
rotulo: "Atualizado",
|
||||
celula: (l) =>
|
||||
celulaTabela("data", {
|
||||
valor: l.atualizado_em,
|
||||
formato: "relativo",
|
||||
acao: () => alert(`Atualizado em: ${l.atualizado_em}`),
|
||||
}),
|
||||
visivel: true,
|
||||
},
|
||||
],
|
||||
acoesLinha: acoesLinha,
|
||||
filtroAvancado: [
|
||||
{
|
||||
chave: "empreendedor",
|
||||
rotulo: "Empreendedor",
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
filtro: (valor: unknown) =>
|
||||
criarFiltro26({
|
||||
// biome-ignore lint/suspicious/noExplicitAny: playground
|
||||
empreendedor: { like: valor as string } as any,
|
||||
}) as tipoFiltro26<Linha>,
|
||||
entrada: ["texto", { rotulo: "Empreendedor" }] as ComponenteEntrada,
|
||||
},
|
||||
{
|
||||
chave: "documento",
|
||||
rotulo: "Documento",
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
filtro: (valor: string) =>
|
||||
criarFiltro26({
|
||||
// biome-ignore lint/suspicious/noExplicitAny: playground
|
||||
documento: { like: valor as string } as any,
|
||||
}) as tipoFiltro26<Linha>,
|
||||
entrada: [
|
||||
"texto",
|
||||
{ rotulo: "Documento", formato: "cpfCnpj" },
|
||||
] as ComponenteEntrada,
|
||||
},
|
||||
{
|
||||
chave: "email",
|
||||
rotulo: "E-mail",
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
filtro: (valor: string) =>
|
||||
criarFiltro26({
|
||||
// biome-ignore lint/suspicious/noExplicitAny: playground
|
||||
email: { like: valor as string } as any,
|
||||
}) as tipoFiltro26<Linha>,
|
||||
entrada: [
|
||||
"texto",
|
||||
{ rotulo: "E-mail", formato: "email" },
|
||||
] as ComponenteEntrada,
|
||||
},
|
||||
],
|
||||
consulta: async (parametrosConsulta) => {
|
||||
try {
|
||||
const resposta = await api_ler(parametrosConsulta)
|
||||
return {
|
||||
cod: codigosResposta.sucesso,
|
||||
eCerto: true,
|
||||
eErro: false,
|
||||
mensagem: undefined,
|
||||
valor: {
|
||||
quantidade: resposta.total,
|
||||
valores: resposta.dados,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
cod: codigosResposta.erroConhecido,
|
||||
eCerto: false,
|
||||
eErro: true,
|
||||
mensagem: String(error),
|
||||
valor: undefined,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const tabelaVazia: tipoEliTabelaConsulta<Linha> = {
|
||||
nome: "Exemplo Vazio",
|
||||
registros_por_consulta: 10,
|
||||
mostrarCaixaDeBusca: true,
|
||||
colunas: tabelaOk.colunas,
|
||||
consulta: async (_parametrosConsulta) => {
|
||||
return {
|
||||
cod: codigosResposta.sucesso,
|
||||
eCerto: true,
|
||||
eErro: false,
|
||||
mensagem: undefined,
|
||||
valor: {
|
||||
quantidade: 0,
|
||||
valores: [],
|
||||
},
|
||||
}
|
||||
},
|
||||
mensagemVazio: "Nada para mostrar aqui.",
|
||||
acoesLinha: acoesLinha,
|
||||
}
|
||||
|
||||
const tabelaErro: tipoEliTabelaConsulta<Linha> = {
|
||||
nome: "Exemplo",
|
||||
registros_por_consulta: tabelaOk.registros_por_consulta,
|
||||
mostrarCaixaDeBusca: tabelaOk.mostrarCaixaDeBusca,
|
||||
colunas: tabelaOk.colunas,
|
||||
acoesLinha: acoesLinha,
|
||||
consulta: async (_parametrosConsulta) => {
|
||||
return {
|
||||
cod: codigosResposta.erroConhecido,
|
||||
eCerto: false,
|
||||
eErro: true,
|
||||
mensagem: "Falha ao buscar dados",
|
||||
valor: undefined,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
tabelaOk,
|
||||
tabelaVazia,
|
||||
tabelaErro,
|
||||
versaoTabelaOk,
|
||||
mostrarModalCadastro,
|
||||
salvando,
|
||||
novoCadastro,
|
||||
salvarNovoCadastro,
|
||||
erroCadastro,
|
||||
selecionados,
|
||||
}
|
||||
return useTabelaPlayground()
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
@ -429,6 +84,15 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
.stack :deep(.eli-tabela) {
|
||||
max-width: 900px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.debug {
|
||||
padding: 12px;
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
border-radius: 8px;
|
||||
overflow: auto;
|
||||
font-size: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
171
src/playground/useTabelaPlayground.ts
Normal file
171
src/playground/useTabelaPlayground.ts
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/**
|
||||
* useTabelaPlayground
|
||||
* Composable principal do playground da EliTabela.
|
||||
* Gerencia estado de CRUD (modal, formulário, erros) e compõe as 3 instâncias de tabela de demonstração.
|
||||
* A configuração de colunas, filtros e consulta está em `useTabelaPlaygroundConfig.ts`.
|
||||
*
|
||||
* @returns Estado reativo, configurações de tabela e ações de CRUD para o template
|
||||
* @see useTabelaPlaygroundConfig.ts — definições de colunas, filtros e consulta
|
||||
* @see tabela.playground.vue — componente que consome este composable
|
||||
*/
|
||||
import { Plus } from "@lucide/vue"
|
||||
import { codigosResposta } from "p-respostas"
|
||||
import { inject, ref } from "vue"
|
||||
import type { tipoEliTabelaConsulta } from "@/componentes/EliTabela"
|
||||
import { PLAYGROUND_CONSOLE_KEY, type PlaygroundConsole } from "./App.vue"
|
||||
import { ApiSimulacao } from "./api_simulador"
|
||||
import type { tipoEmpreendedores } from "./api_simulador/dados"
|
||||
import { colunasTabela, criarAcoesLinha, filtrosAvancados } from "./useTabelaPlaygroundConfig"
|
||||
import { consultar } from "./useTabelaPlaygroundConsultar"
|
||||
|
||||
export function useTabelaPlayground() {
|
||||
const playgroundConsole = inject<PlaygroundConsole>(PLAYGROUND_CONSOLE_KEY)
|
||||
|
||||
function adicionarLog(acao: string, req: unknown, res: unknown) {
|
||||
if (playgroundConsole) playgroundConsole.adicionarLog(`ApiSimulacao: ${acao}`, req, res)
|
||||
}
|
||||
|
||||
const versaoTabelaOk = ref(0)
|
||||
const selecionados = ref<tipoEmpreendedores[]>([])
|
||||
const mostrarModalCadastro = ref(false)
|
||||
const salvando = ref(false)
|
||||
const novoCadastro = ref<Omit<tipoEmpreendedores, "codigo" | "criado_em" | "atualizado_em">>({
|
||||
empreendedor: "",
|
||||
empreendimento: "",
|
||||
documento: "",
|
||||
email: "",
|
||||
telefone: "",
|
||||
faturamento: 0,
|
||||
peso_kg: 0,
|
||||
areas_atuacao: [],
|
||||
})
|
||||
const erroCadastro = ref<string | undefined>(undefined)
|
||||
|
||||
let atualizarTabelaCallback: (() => Promise<void>) | undefined
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: playground — parâmetros dinâmicos da ação de tabela
|
||||
function abrirModalCadastro(params?: any) {
|
||||
if (params?.atualizarConsulta) atualizarTabelaCallback = params.atualizarConsulta
|
||||
novoCadastro.value = {
|
||||
empreendedor: "",
|
||||
empreendimento: "",
|
||||
documento: "",
|
||||
email: "",
|
||||
telefone: "",
|
||||
faturamento: 0,
|
||||
peso_kg: 0,
|
||||
areas_atuacao: [],
|
||||
}
|
||||
erroCadastro.value = undefined
|
||||
mostrarModalCadastro.value = true
|
||||
}
|
||||
|
||||
async function salvarNovoCadastro() {
|
||||
salvando.value = true
|
||||
erroCadastro.value = undefined
|
||||
try {
|
||||
const payload: tipoEmpreendedores = {
|
||||
...novoCadastro.value,
|
||||
codigo: "novo",
|
||||
criado_em: new Date().toISOString(),
|
||||
atualizado_em: new Date().toISOString(),
|
||||
}
|
||||
const resposta = await ApiSimulacao("criar", payload, (req, res) => {
|
||||
console.groupCollapsed("🌐 [ApiSimulacao] criar")
|
||||
console.info("Req:", req)
|
||||
console.info("Res:", res)
|
||||
console.groupEnd()
|
||||
adicionarLog("criar", req, res)
|
||||
})
|
||||
if (resposta.eErro) {
|
||||
erroCadastro.value = resposta.mensagem
|
||||
return
|
||||
}
|
||||
if (atualizarTabelaCallback) await atualizarTabelaCallback()
|
||||
else versaoTabelaOk.value++
|
||||
mostrarModalCadastro.value = false
|
||||
} catch (e) {
|
||||
erroCadastro.value = String(e)
|
||||
} finally {
|
||||
salvando.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function removerCadastro(linha: tipoEmpreendedores) {
|
||||
if (confirm(`Tem certeza que deseja remover ${linha.empreendedor}?`)) {
|
||||
ApiSimulacao("deletar", { codigo: linha.codigo }, (req, res) => {
|
||||
console.groupCollapsed("🌐 [ApiSimulacao] deletar")
|
||||
console.info("Req:", req)
|
||||
console.info("Res:", res)
|
||||
console.groupEnd()
|
||||
adicionarLog("deletar", req, res)
|
||||
}).then(() => {
|
||||
versaoTabelaOk.value++
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const acoesLinha = criarAcoesLinha(removerCadastro)
|
||||
const consultarFn: tipoEliTabelaConsulta<tipoEmpreendedores>["consulta"] = (params) =>
|
||||
consultar(params, adicionarLog)
|
||||
|
||||
const tabelaOk: tipoEliTabelaConsulta<tipoEmpreendedores> = {
|
||||
nome: "ExemploPlaygroundData",
|
||||
registros_por_consulta: 10,
|
||||
mostrarCaixaDeBusca: true,
|
||||
mostrar_selecao: true,
|
||||
chave_selecao: "empreendedor",
|
||||
acoesTabela: [
|
||||
{ posicao: "superior", icone: Plus, rotulo: "Novo (Superior)", acao: abrirModalCadastro },
|
||||
{ posicao: "inferior", icone: Plus, rotulo: "Novo (Inferior)", acao: abrirModalCadastro },
|
||||
],
|
||||
colunas: colunasTabela,
|
||||
acoesLinha,
|
||||
filtroAvancado: filtrosAvancados,
|
||||
consulta: consultarFn,
|
||||
}
|
||||
|
||||
const tabelaVazia: tipoEliTabelaConsulta<tipoEmpreendedores> = {
|
||||
nome: "Exemplo Vazio",
|
||||
registros_por_consulta: 10,
|
||||
mostrarCaixaDeBusca: true,
|
||||
colunas: colunasTabela,
|
||||
mensagemVazio: "Nada para mostrar aqui.",
|
||||
acoesLinha,
|
||||
consulta: async () => ({
|
||||
cod: codigosResposta.sucesso,
|
||||
eCerto: true,
|
||||
eErro: false,
|
||||
mensagem: undefined,
|
||||
valor: { quantidade: 0, valores: [] },
|
||||
}),
|
||||
}
|
||||
|
||||
const tabelaErro: tipoEliTabelaConsulta<tipoEmpreendedores> = {
|
||||
nome: "Exemplo",
|
||||
registros_por_consulta: tabelaOk.registros_por_consulta,
|
||||
mostrarCaixaDeBusca: tabelaOk.mostrarCaixaDeBusca,
|
||||
colunas: colunasTabela,
|
||||
acoesLinha,
|
||||
consulta: async () => ({
|
||||
cod: codigosResposta.erroConhecido,
|
||||
eCerto: false,
|
||||
eErro: true,
|
||||
mensagem: "Falha ao buscar dados",
|
||||
valor: undefined,
|
||||
}),
|
||||
}
|
||||
|
||||
return {
|
||||
tabelaOk,
|
||||
tabelaVazia,
|
||||
tabelaErro,
|
||||
versaoTabelaOk,
|
||||
mostrarModalCadastro,
|
||||
salvando,
|
||||
novoCadastro,
|
||||
salvarNovoCadastro,
|
||||
erroCadastro,
|
||||
selecionados,
|
||||
}
|
||||
}
|
||||
190
src/playground/useTabelaPlaygroundConfig.ts
Normal file
190
src/playground/useTabelaPlaygroundConfig.ts
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
/**
|
||||
* useTabelaPlaygroundConfig
|
||||
* Definições de colunas, filtros avançados, ações por linha e função consultar do playground.
|
||||
* Extraído de useTabelaPlayground.ts para manter cada arquivo abaixo de 250 linhas.
|
||||
*
|
||||
* @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"
|
||||
|
||||
const { criarFiltroDelta } = iniciarFiltroDelta<Extract<keyof tipoEmpreendedores, string>>()
|
||||
|
||||
/** Ações por linha usadas nas 3 instâncias de tabela do playground */
|
||||
export function criarAcoesLinha(
|
||||
removerCadastro: (linha: tipoEmpreendedores) => void,
|
||||
): tipoEliTabelaConsulta<tipoEmpreendedores>["acoesLinha"] {
|
||||
return [
|
||||
{
|
||||
icone: Eye,
|
||||
cor: "#2563eb",
|
||||
rotulo: "Detalhes",
|
||||
acao: (linha) => {
|
||||
console.log("Visualizar detalhes de", linha.empreendedor)
|
||||
},
|
||||
},
|
||||
{
|
||||
icone: Trash2,
|
||||
cor: "#dc2626",
|
||||
rotulo: "Remover",
|
||||
acao: (linha) => {
|
||||
removerCadastro(linha)
|
||||
},
|
||||
exibir: (linha) => !linha.empreendimento.toLowerCase().includes("café"),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
/** Colunas da tabela de empreendedores */
|
||||
export const colunasTabela: tipoEliTabelaConsulta<tipoEmpreendedores>["colunas"] = [
|
||||
{
|
||||
rotulo: "Empreendedor",
|
||||
celula: (l) => celulaTabela("textoSimples", { texto: l.empreendedor }),
|
||||
coluna_ordem: "empreendedor",
|
||||
visivel: true,
|
||||
},
|
||||
{
|
||||
rotulo: "Empreendimento",
|
||||
celula: (l) => celulaTabela("textoSimples", { texto: l.empreendimento }),
|
||||
coluna_ordem: "empreendimento",
|
||||
visivel: true,
|
||||
},
|
||||
{
|
||||
rotulo: "Documento",
|
||||
celula: (l) => celulaTabela("textoSimples", { texto: l.documento }),
|
||||
coluna_ordem: "documento",
|
||||
visivel: false,
|
||||
},
|
||||
{
|
||||
rotulo: "E-mail",
|
||||
celula: (l) =>
|
||||
celulaTabela("textoTruncado", {
|
||||
texto: l.email,
|
||||
acao: () => {
|
||||
alert(`Clicou em ${l.email}`)
|
||||
},
|
||||
}),
|
||||
coluna_ordem: "email",
|
||||
visivel: true,
|
||||
},
|
||||
{
|
||||
rotulo: "Telefone",
|
||||
celula: (l) => celulaTabela("textoSimples", { texto: l.telefone }),
|
||||
coluna_ordem: "telefone",
|
||||
visivel: true,
|
||||
},
|
||||
{
|
||||
rotulo: "Faturamento",
|
||||
celula: (l) =>
|
||||
celulaTabela("numero", {
|
||||
numero: l.faturamento,
|
||||
prefixo: "R$",
|
||||
acao: () => alert(`Faturamento de ${l.empreendedor}: R$ ${l.faturamento}`),
|
||||
}),
|
||||
visivel: true,
|
||||
},
|
||||
{
|
||||
rotulo: "Peso",
|
||||
celula: (l) => celulaTabela("numero", { numero: l.peso_kg, sufixo: "kg" }),
|
||||
visivel: false,
|
||||
},
|
||||
{
|
||||
rotulo: "Status",
|
||||
celula: (l) =>
|
||||
celulaTabela("tags", {
|
||||
opcoes: [
|
||||
{ rotulo: "Ativo", cor: "success", icone: BadgeCheck },
|
||||
{
|
||||
rotulo: "Editar",
|
||||
cor: "primary",
|
||||
icone: Pencil,
|
||||
acao: () => alert(`Editar ${l.empreendedor}`),
|
||||
},
|
||||
],
|
||||
}),
|
||||
visivel: true,
|
||||
},
|
||||
{
|
||||
rotulo: "Criado em",
|
||||
celula: (l) => celulaTabela("data", { valor: l.criado_em, formato: "data" }),
|
||||
visivel: false,
|
||||
},
|
||||
{
|
||||
rotulo: "Atualizado",
|
||||
celula: (l) =>
|
||||
celulaTabela("data", {
|
||||
valor: l.atualizado_em,
|
||||
formato: "relativo",
|
||||
acao: () => alert(`Atualizado em: ${l.atualizado_em}`),
|
||||
}),
|
||||
visivel: true,
|
||||
},
|
||||
]
|
||||
|
||||
/** Filtros avançados da tabela de empreendedores */
|
||||
export const filtrosAvancados: tipoEliTabelaConsulta<tipoEmpreendedores>["filtroAvancado"] = [
|
||||
{
|
||||
chave: "empreendedor",
|
||||
rotulo: "Empreendedor",
|
||||
filtro: (valor: unknown) => {
|
||||
if (!valor) return undefined
|
||||
return criarFiltroDelta({
|
||||
condicoes: [
|
||||
{ id: "{c1}", coluna: "empreendedor", operador: "like", valor: valor as string },
|
||||
],
|
||||
formula: "{c1}",
|
||||
})
|
||||
},
|
||||
entrada: ["texto", { rotulo: "Empreendedor" }] as ComponenteEntrada,
|
||||
},
|
||||
{
|
||||
chave: "empreendimento",
|
||||
rotulo: "Empreendimento",
|
||||
filtro: (valor: unknown) => {
|
||||
if (!valor) return undefined
|
||||
return criarFiltroDelta({
|
||||
condicoes: [
|
||||
{ id: "{c1}", coluna: "empreendimento", operador: "like", valor: valor as string },
|
||||
],
|
||||
formula: "{c1}",
|
||||
})
|
||||
},
|
||||
entrada: ["texto", { rotulo: "Empreendimento" }] as ComponenteEntrada,
|
||||
},
|
||||
{
|
||||
chave: "documento",
|
||||
rotulo: "Documento",
|
||||
filtro: (valor: unknown) => {
|
||||
if (!valor) return undefined
|
||||
return criarFiltroDelta({
|
||||
condicoes: [{ id: "{c1}", coluna: "documento", operador: "like", valor: valor as string }],
|
||||
formula: "{c1}",
|
||||
})
|
||||
},
|
||||
entrada: ["texto", { rotulo: "Documento", formato: "cpfCnpj" }] as ComponenteEntrada,
|
||||
},
|
||||
{
|
||||
chave: "email",
|
||||
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}",
|
||||
})
|
||||
},
|
||||
entrada: ["texto", { rotulo: "E-mail", formato: "email" }] as ComponenteEntrada,
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* Executa a consulta na ApiSimulacao, traduzindo busca textual e filtros avançados para FiltroDelta.
|
||||
* @param parametrosConsulta - Parâmetros vindos do EliTabela (offSet, limit, filtros, texto_busca)
|
||||
* @param adicionarLog - Callback para registrar no painel de debug do playground
|
||||
*/
|
||||
102
src/playground/useTabelaPlaygroundConsultar.ts
Normal file
102
src/playground/useTabelaPlaygroundConsultar.ts
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/**
|
||||
* consultarPlayground
|
||||
* Executa a consulta na ApiSimulacao traduzindo busca textual e filtros avançados para FiltroDelta.
|
||||
* Extraído de useTabelaPlaygroundConfig.ts para manter cada arquivo abaixo de 250 linhas.
|
||||
*
|
||||
* @see useTabelaPlaygroundConfig.ts — definições de colunas e filtros
|
||||
* @see useTabelaPlayground.ts — composable que usa este consultar
|
||||
*/
|
||||
import { iniciarFiltroDelta } from "p-comuns"
|
||||
import { codigosResposta } from "p-respostas"
|
||||
import type { tipoEliTabelaConsulta } from "@/componentes/EliTabela"
|
||||
import { ApiSimulacao } from "./api_simulador"
|
||||
import type { tipoEmpreendedores } from "./api_simulador/dados"
|
||||
|
||||
const { criarFiltroDelta } = iniciarFiltroDelta<Extract<keyof tipoEmpreendedores, string>>()
|
||||
|
||||
export async function consultar(
|
||||
parametrosConsulta: Parameters<tipoEliTabelaConsulta<tipoEmpreendedores>["consulta"]>[0],
|
||||
adicionarLog: (acao: string, req: unknown, res: unknown) => void,
|
||||
): ReturnType<tipoEliTabelaConsulta<tipoEmpreendedores>["consulta"]> {
|
||||
try {
|
||||
// biome-ignore lint/suspicious/noExplicitAny: playground — mock de API com estrutura dinâmica
|
||||
const req: any = { ...(parametrosConsulta || {}) }
|
||||
if (req.texto_busca) {
|
||||
const termo = `${req.texto_busca}`
|
||||
const fBusca = criarFiltroDelta({
|
||||
condicoes: [
|
||||
{ id: "{c1}", coluna: "empreendedor", operador: "like", valor: termo },
|
||||
{ id: "{c2}", coluna: "empreendimento", operador: "like", valor: termo },
|
||||
],
|
||||
formula: "{c1} OU {c2}",
|
||||
})
|
||||
if (req.filtros) {
|
||||
const fExistente = req.filtros
|
||||
const condicoesBusca = fBusca.condicoes.map(
|
||||
(c: { id: string; coluna: string; operador: string; valor: unknown }) => ({
|
||||
...c,
|
||||
id: c.id.replace(/[{}]/g, (m: string) => (m === "{" ? "{busca_" : "}")),
|
||||
}),
|
||||
)
|
||||
const formulaBusca = fBusca.formula.replace(/\{(\w+)\}/g, "{busca_$1}")
|
||||
req.filtros = criarFiltroDelta({
|
||||
condicoes: [...fExistente.condicoes, ...condicoesBusca],
|
||||
formula: `(${fExistente.formula}) E (${formulaBusca})`,
|
||||
})
|
||||
} else {
|
||||
req.filtros = fBusca
|
||||
}
|
||||
delete req.texto_busca
|
||||
} else if (Array.isArray(req.filtros)) {
|
||||
const todos = req.filtros as ReturnType<typeof criarFiltroDelta>[]
|
||||
if (todos.length === 1) {
|
||||
req.filtros = todos[0]
|
||||
} else if (todos.length > 1) {
|
||||
const condicoesFinais: unknown[] = []
|
||||
const partes: string[] = []
|
||||
for (let i = 0; i < todos.length; i++) {
|
||||
const f = todos[i]
|
||||
const prefixo = `f${i}_`
|
||||
const conds = f.condicoes.map(
|
||||
(c: { id: string; coluna: string; operador: string; valor: unknown }) => ({
|
||||
...c,
|
||||
id: c.id.replace(/\{(\w+)\}/, `{${prefixo}$1}`),
|
||||
}),
|
||||
)
|
||||
const formula = f.formula.replace(/\{(\w+)\}/g, `{${prefixo}$1}`)
|
||||
condicoesFinais.push(...conds)
|
||||
partes.push(`(${formula})`)
|
||||
}
|
||||
req.filtros = criarFiltroDelta({
|
||||
condicoes: condicoesFinais as ReturnType<typeof criarFiltroDelta>["condicoes"],
|
||||
formula: partes.join(" E "),
|
||||
})
|
||||
} else {
|
||||
delete req.filtros
|
||||
}
|
||||
}
|
||||
const resposta = await ApiSimulacao("listar", req, (r, res) => {
|
||||
console.groupCollapsed("🌐 [ApiSimulacao] listar")
|
||||
console.info("Req:", r)
|
||||
console.info("Res:", res)
|
||||
console.groupEnd()
|
||||
adicionarLog("listar", r, res)
|
||||
})
|
||||
if (resposta.eErro) throw new Error(resposta.mensagem)
|
||||
return {
|
||||
cod: codigosResposta.sucesso,
|
||||
eCerto: true,
|
||||
eErro: false,
|
||||
mensagem: undefined,
|
||||
valor: { quantidade: resposta.valor.total, valores: resposta.valor.dados },
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
cod: codigosResposta.erroConhecido,
|
||||
eCerto: false,
|
||||
eErro: true,
|
||||
mensagem: String(error),
|
||||
valor: undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue