Buscar K
Aparência
Aparência
O Widget Svelte é a ferramenta definitiva para flexibilidade no Horus DataViz. Ele permite que desenvolvedores criem qualquer tipo de visualização ou interatividade utilizando código padrão Svelte, JavaScript e CSS (Tailwind).
WARNING
Versão do Svelte: O ambiente suporta Svelte 4. Funcionalidades do Svelte 5 (como Runes) não são suportadas. Utilize a sintaxe clássica de reatividade (let, $:, etc.).
Antes de mergulhar no código, o Widget possui configurações visuais que controlam seu contêiner e comportamento no Dashboard.
Ao clicar em "Editar Código", há acesso a um editor completo.
O ambiente já vem com bibliotecas essenciais pré-instaladas. Não é necessário (nem possível) fazer npm install.
{#if}, {#each}, on:click).@highcharts/svelte._): Utilitários para manipulação de arrays/objetos.moment): Manipulação de datas e horas.chroma): Manipulação de cores.xlsx): Geração de planilhas Excel.app A variável app (export let app;) é sua ponte com os dados e o motor do Horus BI.
fetchMatrix) Use app.fetchMatrix para executar queries no motor de dados (Hyper-c).
/* Exemplo de uso dentro de uma função async ou onMount */
const data = await app.fetchMatrix({
columns: [
'[Vendas]."VENDEDOR":AGP', // Dimensão (Agrupador)
'[Vendas]."VALOR":SUM' // Métrica (Soma)
],
filters: [], // Opcional: filtros adicionais
orderBy: ['[Vendas]."VALOR":SUM:DESC'], // Ordenação
limit: 100
});
// A resposta "data" contém:
// data.Columns: Array com metadados (rótulos, tipos)
// data.Result: Matriz de dados [[Vendedor A, 1000], [Vendedor B, 500]]IMPORTANT
Expressões são Obrigatórias: Ao definir colunas, é necessário sempre incluir a expressão de agregação (:SUM, :AGP, :DATE, etc.). Sem isso, o motor não sabe como processar a query.
É possível fazer o Widget interagir com o resto do Dashboard aplicando filtros globais.
// Criar referência à coluna (sempre com expressão!)
const colunaVendedor = app.columnFromString('[Vendas]."VENDEDOR":AGP');
// Aplicar filtro (Ex: Filtrar onde Vendedor é 'João')
app.addFilter(colunaVendedor, "=", "João");
// Remover filtro
app.addFilter(colunaVendedor, "=", false);Use app.formatValue para garantir que números e datas sigam o padrão do sistema (moeda, casas decimais, locale).
// row[1] é o valor numérico cru (ex: 1540.5)
// colunaValor contém os metadados de formatação
const textoFormatado = app.formatValue(row[1], colunaValor);
// Resultado: "R$ 1.540,50" (dependendo do locale)Abaixo, um exemplo de Widget que lista vendedores e filtra o Dashboard ao clicar.
<script>
import { onMount } from "svelte";
export let app; // Injeção automática do Horus
let loading = true;
let vendedores = [];
let colunas = [];
async function carregarDados() {
loading = true;
const res = await app.fetchMatrix({
columns: [
'[Vendas]."VENDEDOR":AGP',
'[Vendas]."TOTAL":SUM'
],
limit: 10
});
// Mapear resultado para objeto mais fácil de usar
colunas = res.Columns;
vendedores = res.Result.map(row => ({
nome: row[0],
valor: row[1],
valorFormatado: app.formatValue(row[1], res.Columns[1])
}));
loading = false;
}
function filtrar(nome) {
const colVendedor = app.columnFromString('[Vendas]."VENDEDOR":AGP');
app.addFilter(colVendedor, "=", nome);
}
// Reagir a mudanças globais (filtros externos) recarregando os dados
$: {
if (app) carregarDados();
}
</script>
<div class="w-full h-full p-4 overflow-auto">
{#if loading}
<div class="flex items-center justify-center h-full">Carregando...</div>
{:else}
<h3 class="text-lg font-bold mb-4">Top Vendedores</h3>
<ul class="space-y-2">
{#each vendedores as v}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<li
class="flex justify-between p-2 bg-slate-100 hover:bg-blue-100 cursor-pointer rounded transition"
on:click={() => filtrar(v.nome)}
>
<span>{v.nome}</span>
<span class="font-mono">{v.valorFormatado}</span>
</li>
{/each}
</ul>
{/if}
</div>O método app.generateJWTToken() permite criar tokens de autenticação para chamar APIs externas (ERP, CRM, etc.) de forma segura, sem expor credenciais no frontend.
Armazenar tokens de API ou senhas no código JavaScript é uma falha de segurança — qualquer usuário pode inspecionar o código do browser. O JWT resolve isso criando um mecanismo de confiança entre o Horus e seu sistema:
IMPORTANT
Para usar generateJWTToken(), é necessário primeiro habilitar e configurar a Chave JWT na aba Avançado do Tenant ou Cliente. Veja a documentação.
const result = await app.generateJWTToken();
// result = {
// token: "eyJhbGciOiJIUzI1NiIs...", // Token JWT assinado
// payload: {
// userId: 123,
// tenantId: 456,
// data: "2024-01-15T10:30:00.000Z" // ISO timestamp UTC
// }
// }Um Widget que lista pedidos pendentes e permite aprová-los diretamente no Dashboard:
<script>
import { onMount } from "svelte";
export let app;
let pedidos = [];
let loading = true;
let aprovando = null; // ID do pedido sendo aprovado
async function carregarPedidos() {
loading = true;
const res = await app.fetchMatrix({
columns: [
'[Pedidos]."ID":AGP',
'[Pedidos]."CLIENTE":AGP',
'[Pedidos]."VALOR":SUM',
'[Pedidos]."STATUS":AGP'
],
filters: [
{ column: app.columnFromString('[Pedidos]."STATUS":AGP'), operator: '=', value: 'PENDENTE' }
],
limit: 50
});
pedidos = res.Result.map(row => ({
id: row[0],
cliente: row[1],
valor: app.formatValue(row[2], res.Columns[2]),
status: row[3]
}));
loading = false;
}
async function aprovarPedido(pedidoId) {
aprovando = pedidoId;
try {
// 1. Gerar token JWT assinado pelo backend Horus
const jwt = await app.generateJWTToken();
if (!jwt?.token) {
alert('Erro: JWT não configurado neste tenant');
return;
}
// 2. Chamar sua API externa com o token
const response = await fetch('https://api.seuerpcloud.com/pedidos/aprovar', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${jwt.token}` // Token JWT no header
},
body: JSON.stringify({ pedidoId })
});
if (response.ok) {
alert('Pedido aprovado com sucesso!');
await carregarPedidos(); // Recarrega a lista
} else {
alert('Erro ao aprovar pedido');
}
} catch (error) {
alert('Erro de conexão: ' + error.message);
} finally {
aprovando = null;
}
}
$: if (app) carregarPedidos();
</script>
<div class="w-full h-full p-4 overflow-auto">
<h3 class="text-lg font-bold mb-4">Pedidos Pendentes de Aprovação</h3>
{#if loading}
<div class="text-center py-8">Carregando...</div>
{:else if pedidos.length === 0}
<div class="text-center py-8 text-gray-500">Nenhum pedido pendente</div>
{:else}
<table class="w-full">
<thead>
<tr class="border-b">
<th class="text-left py-2">ID</th>
<th class="text-left py-2">Cliente</th>
<th class="text-right py-2">Valor</th>
<th class="text-center py-2">Ação</th>
</tr>
</thead>
<tbody>
{#each pedidos as pedido}
<tr class="border-b hover:bg-slate-50">
<td class="py-2">{pedido.id}</td>
<td class="py-2">{pedido.cliente}</td>
<td class="py-2 text-right font-mono">{pedido.valor}</td>
<td class="py-2 text-center">
<button
class="px-3 py-1 bg-green-500 text-white rounded hover:bg-green-600 disabled:opacity-50"
on:click={() => aprovarPedido(pedido.id)}
disabled={aprovando === pedido.id}
>
{aprovando === pedido.id ? 'Aprovando...' : 'Aprovar'}
</button>
</td>
</tr>
{/each}
</tbody>
</table>
{/if}
</div>No lado do servidor (ERP, API, etc.), é necessário validar o token JWT usando a mesma chave configurada no Horus.
Exemplo em Node.js:
const jwt = require('jsonwebtoken');
const CHAVE_JWT = 'sua-chave-configurada-no-horus'; // Mesma chave do Tenant
app.post('/pedidos/aprovar', (req, res) => {
const token = req.headers.authorization?.replace('Bearer ', '');
try {
const payload = jwt.verify(token, CHAVE_JWT);
// payload = { userId: 123, tenantId: 456, data: "2024-01-15T10:30:00.000Z" }
console.log(`Usuário ${payload.userId} do tenant ${payload.tenantId} está aprovando pedido`);
// Seu código de aprovação aqui...
res.json({ success: true });
} catch (error) {
res.status(401).json({ error: 'Token inválido' });
}
});Exemplo em Python:
import jwt
CHAVE_JWT = 'sua-chave-configurada-no-horus'
@app.route('/pedidos/aprovar', methods=['POST'])
def aprovar_pedido():
token = request.headers.get('Authorization', '').replace('Bearer ', '')
try:
payload = jwt.decode(token, CHAVE_JWT, algorithms=['HS256'])
# payload = { 'userId': 123, 'tenantId': 456, 'data': '2024-01-15T10:30:00.000Z' }
print(f"Usuário {payload['userId']} aprovando pedido")
# Seu código aqui...
return jsonify({'success': True})
except jwt.InvalidTokenError:
return jsonify({'error': 'Token inválido'}), 401CAUTION
Nunca exponha a Chave JWT no código frontend. Ela deve existir apenas: