@bluemarble/bi 0.6.6 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DashboardDevtools-I00Iz0Z8.js → DashboardDevtools-wk4vMfhy.js} +1447 -499
- package/dist/DashboardDevtools-wk4vMfhy.js.map +1 -0
- package/dist/{ModelDictionary-DUGFHBo-.js → ModelDictionary-DZiYK1mV.js} +57 -4
- package/dist/ModelDictionary-DZiYK1mV.js.map +1 -0
- package/dist/client/index.d.ts +22 -5
- package/dist/client/index.js +30 -7
- package/dist/client/index.js.map +1 -1
- package/dist/{formatRules-DTKbov47.d.ts → formatRules-DXMHY9tU.d.ts} +41 -2
- package/dist/{layout-CrZ00tB0.js → layout-BS3nO6OT.js} +102 -2
- package/dist/layout-BS3nO6OT.js.map +1 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +17 -1
- package/dist/server/index.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/package.json +1 -1
- package/dist/DashboardDevtools-I00Iz0Z8.js.map +0 -1
- package/dist/ModelDictionary-DUGFHBo-.js.map +0 -1
- package/dist/layout-CrZ00tB0.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { s as AGGREGATION_LABEL, t as canonicalJson, u as isAggregate } from "./codegen-LoQzFAle.js";
|
|
2
|
-
import { _ as resolveChartTypes } from "./layout-
|
|
2
|
+
import { _ as resolveChartTypes } from "./layout-BS3nO6OT.js";
|
|
3
3
|
import { Box, CircularProgress, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material";
|
|
4
4
|
import { Suspense, createContext, lazy, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -283,7 +283,7 @@ function createDevtoolsStore() {
|
|
|
283
283
|
//#endregion
|
|
284
284
|
//#region src/client/DashboardProvider.tsx
|
|
285
285
|
const DashboardContext = createContext(null);
|
|
286
|
-
const DashboardDevtools = lazy(() => import("./DashboardDevtools-
|
|
286
|
+
const DashboardDevtools = lazy(() => import("./DashboardDevtools-wk4vMfhy.js"));
|
|
287
287
|
const DEVTOOLS_DEFAULT = process.env.NODE_ENV === "development";
|
|
288
288
|
const draftStorageKey = (model) => `bm-dashboard-devtools:draft:${model}`;
|
|
289
289
|
function loadDraft(model) {
|
|
@@ -596,6 +596,59 @@ function measureFormatter(meta, measure, fallbackDecimals = 2, formats) {
|
|
|
596
596
|
function fieldLabel(meta, name, labels) {
|
|
597
597
|
return labels?.[name] || meta?.dimensions[name]?.label || meta?.measures[name]?.label || name;
|
|
598
598
|
}
|
|
599
|
+
const monthNames = new Intl.DateTimeFormat("pt-BR", {
|
|
600
|
+
month: "long",
|
|
601
|
+
timeZone: "UTC"
|
|
602
|
+
});
|
|
603
|
+
const dateFormat = new Intl.DateTimeFormat("pt-BR", {
|
|
604
|
+
dateStyle: "short",
|
|
605
|
+
timeZone: "UTC"
|
|
606
|
+
});
|
|
607
|
+
const localDate = new Intl.DateTimeFormat("pt-BR", { dateStyle: "short" });
|
|
608
|
+
const localDateTime = new Intl.DateTimeFormat("pt-BR", {
|
|
609
|
+
dateStyle: "short",
|
|
610
|
+
timeStyle: "short"
|
|
611
|
+
});
|
|
612
|
+
const looseNumber = new Intl.NumberFormat("pt-BR", { maximumFractionDigits: 2 });
|
|
613
|
+
const capitalize = (text) => text.charAt(0).toUpperCase() + text.slice(1);
|
|
614
|
+
const ISO_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
615
|
+
/** `3` → "Março"; aceita também uma data ISO (usa o mês dela). Fora de 1–12, o próprio valor. */
|
|
616
|
+
function toMonthName(value) {
|
|
617
|
+
const month = typeof value === "string" && /^\d{4}-\d{2}/.test(value) ? Number(value.slice(5, 7)) : Number(value);
|
|
618
|
+
if (!Number.isInteger(month) || month < 1 || month > 12) return String(value);
|
|
619
|
+
return capitalize(monthNames.format(new Date(Date.UTC(2e3, month - 1, 1))));
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Data ISO → `dd/mm/aaaa`. Só a data (`2026-10-01`) não passa por fuso; com hora (o `Date` do
|
|
623
|
+
* banco chega como ISO em UTC), vira o horário local e a hora só aparece quando não é meia-noite.
|
|
624
|
+
*/
|
|
625
|
+
function toDate(value) {
|
|
626
|
+
if (typeof value !== "string") return String(value);
|
|
627
|
+
const day = ISO_DATE.exec(value);
|
|
628
|
+
if (day) return dateFormat.format(new Date(Date.UTC(+day[1], +day[2] - 1, +day[3])));
|
|
629
|
+
const date = new Date(value);
|
|
630
|
+
if (!/^\d{4}-\d{2}-\d{2}T/.test(value) || Number.isNaN(date.getTime())) return value;
|
|
631
|
+
if (date.getHours() || date.getMinutes()) return localDateTime.format(date).replace(",", "");
|
|
632
|
+
return localDate.format(date);
|
|
633
|
+
}
|
|
634
|
+
/** Não numérico: o próprio valor. */
|
|
635
|
+
const numeric = (format) => (value) => value === "" || Number.isNaN(Number(value)) ? String(value) : format(Number(value));
|
|
636
|
+
/**
|
|
637
|
+
* Formatters que todo `<DashboardCharts>`/`<DashboardFilters>` já tem, pelo nome (os do app com o
|
|
638
|
+
* mesmo nome vão por cima).
|
|
639
|
+
*/
|
|
640
|
+
const BUILTIN_FORMATTERS = {
|
|
641
|
+
to_month_name: toMonthName,
|
|
642
|
+
to_date: toDate,
|
|
643
|
+
to_currency: numeric((value) => formatCurrency(value)),
|
|
644
|
+
to_number: numeric((value) => looseNumber.format(value)),
|
|
645
|
+
to_string: (value) => String(value ?? "")
|
|
646
|
+
};
|
|
647
|
+
/** Os built-in mais os do app. */
|
|
648
|
+
const withBuiltinFormatters = (formatters) => ({
|
|
649
|
+
...BUILTIN_FORMATTERS,
|
|
650
|
+
...formatters
|
|
651
|
+
});
|
|
599
652
|
//#endregion
|
|
600
653
|
//#region src/client/useModelQuery.ts
|
|
601
654
|
const INSPECT_ATTRIBUTE = "data-bm-dashboard-query";
|
|
@@ -741,6 +794,6 @@ function ModelDictionary() {
|
|
|
741
794
|
});
|
|
742
795
|
}
|
|
743
796
|
//#endregion
|
|
744
|
-
export {
|
|
797
|
+
export { useDashboard as C, parseLayoutItemKey as D, layoutItemKey as E, DashboardProvider as S, LAYOUT_ITEM_ATTRIBUTE as T, TooltipBox as _, fieldLabel as a, seriesColor as b, formatNumber as c, toDate as d, toMonthName as f, SERIES_COLORS as g, OTHER_COLOR as h, BUILTIN_FORMATTERS as i, measureFormat as l, Legend as m, INSPECT_ATTRIBUTE as n, formatCurrency as o, withBuiltinFormatters as p, useModelQuery as r, formatMeasure as s, ModelDictionary as t, measureFormatter as u, chartPlaceholder as v, errorMessage as w, useClickFilter as x, numberOf as y };
|
|
745
798
|
|
|
746
|
-
//# sourceMappingURL=ModelDictionary-
|
|
799
|
+
//# sourceMappingURL=ModelDictionary-DZiYK1mV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModelDictionary-DZiYK1mV.js","names":["loadDraft"],"sources":["../src/devtools/layoutStore.ts","../src/client/batchQueries.ts","../src/devtools/store.ts","../src/client/DashboardProvider.tsx","../src/client/chartKit.tsx","../src/client/format.ts","../src/client/useModelQuery.ts","../src/client/ModelDictionary.tsx"],"sourcesContent":["import { canonicalJson } from '../codegen'\nimport type { ChartsLayout } from '../layout'\n\n/**\n * Layouts montados na página (`<DashboardCharts>` / `<DashboardFilters>`) e o rascunho de cada um,\n * editado na aba Layout do devtools. Store externo como o de consultas: editar um gráfico\n * re-renderiza só os componentes daquele layout e o painel.\n */\n\n/** Parte do layout que um componente renderiza. */\nexport type LayoutPart = 'charts' | 'filters' | 'canvas'\n\nexport type LayoutEntry = {\n /** O layout que veio do arquivo (o que a página passou ao `<DashboardCharts>`). */\n registered: ChartsLayout\n /** Rascunho editado no devtools; `null` = página usando o registrado. */\n draft: ChartsLayout | null\n /**\n * Partes montadas na página e os formatters que cada uma recebeu (`formatDimension`,\n * `formatLabel`). Parte ausente = a página não renderiza (ex.: sem `<DashboardFilters>`).\n */\n parts: Partial<Record<LayoutPart, string[]>>\n}\n\n/**\n * Marca o elemento de cada item do layout com `<layoutId>/chart:<id>` ou\n * `<layoutId>/filter:<dimensão>` (destaque e inspetor do devtools).\n */\nexport const LAYOUT_ITEM_ATTRIBUTE = 'data-bm-dashboard-layout-item'\n\nexport const layoutItemKey = (layoutId: string, item: LayoutItem) =>\n `${layoutId}/${item.kind}:${item.key}`\n\nexport type LayoutItem = { kind: 'chart' | 'filter'; key: string }\n\nexport function parseLayoutItemKey(value: string): { layoutId: string; item: LayoutItem } | null {\n const match = /^([^/]+)\\/(chart|filter):(.+)$/.exec(value)\n return match\n ? {\n layoutId: match[1] as string,\n item: { kind: match[2] as LayoutItem['kind'], key: match[3] as string }\n }\n : null\n}\n\nconst draftKey = (id: string) => `bm-dashboard-devtools:layout:${id}`\n\nconst sameLayout = (a: ChartsLayout | null, b: ChartsLayout | null) =>\n canonicalJson(a) === canonicalJson(b)\n\nfunction loadDraft(id: string): ChartsLayout | null {\n try {\n const saved = sessionStorage.getItem(draftKey(id))\n return saved ? (JSON.parse(saved) as ChartsLayout) : null\n } catch {\n return null\n }\n}\n\nfunction storeDraft(id: string, draft: ChartsLayout | null) {\n try {\n if (draft) sessionStorage.setItem(draftKey(id), JSON.stringify(draft))\n else sessionStorage.removeItem(draftKey(id))\n } catch {}\n}\n\nexport function createLayoutStore() {\n const entries = new Map<string, LayoutEntry>()\n // Montagens por layout e parte (`<id>\\u0000<parte>`)\n const mounts = new Map<string, number>()\n let snapshot: LayoutEntry[] = []\n const listeners = new Set<() => void>()\n // Edições da aba Layout ainda não aplicadas: sobrevivem a trocar de aba (o painel desmonta)\n const pending = new Map<string, ChartsLayout>()\n\n const emit = () => {\n snapshot = [...entries.values()]\n for (const listener of listeners) listener()\n }\n\n const setDraft = (id: string, next: ChartsLayout | null) => {\n const entry = entries.get(id)\n if (!entry) return\n // Rascunho igual ao arquivo não é rascunho (ex.: desfez tudo à mão)\n const draft = next && !sameLayout(next, entry.registered) ? next : null\n storeDraft(id, draft)\n entries.set(id, { ...entry, draft })\n emit()\n }\n\n return {\n subscribe(listener: () => void) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n getSnapshot: () => snapshot,\n get: (id: string) => entries.get(id) ?? null,\n /** Rascunho do layout, mesmo antes de registrar (evita renderizar o registrado e trocar). */\n draftOf(layout: ChartsLayout) {\n const entry = entries.get(layout.id)\n if (entry) return entry.draft\n const draft = loadDraft(layout.id)\n return draft && !sameLayout(draft, layout) ? draft : null\n },\n\n /** Chamado pelo `<DashboardCharts>`/`<DashboardFilters>` ao montar e quando o arquivo muda (HMR). */\n register(layout: ChartsLayout, part: LayoutPart, formatters: string[]) {\n const current = entries.get(layout.id)\n let draft = current ? current.draft : loadDraft(layout.id)\n // Depois de \"Salvar no arquivo\" o Next recarrega o JSON: o rascunho virou o registrado\n if (draft && sameLayout(draft, layout)) {\n draft = null\n storeDraft(layout.id, null)\n }\n const parts = { ...current?.parts, [part]: formatters }\n entries.set(layout.id, { registered: layout, draft, parts })\n const mountKey = `${layout.id}\\u0000${part}`\n mounts.set(mountKey, (mounts.get(mountKey) ?? 0) + 1)\n emit()\n return () => {\n const count = (mounts.get(mountKey) ?? 1) - 1\n mounts.set(mountKey, count)\n const entry = entries.get(layout.id)\n if (count > 0 || !entry) return\n const { [part]: _, ...rest } = entry.parts\n // O rascunho continua no sessionStorage e volta na próxima montagem\n if (Object.keys(rest).length) entries.set(layout.id, { ...entry, parts: rest })\n else entries.delete(layout.id)\n emit()\n }\n },\n setDraft,\n /** Edições ainda não aplicadas do layout (só em memória). */\n pendingOf: (id: string) => pending.get(id) ?? null,\n setPending(id: string, layout: ChartsLayout | null) {\n if (layout) pending.set(id, layout)\n else pending.delete(id)\n }\n }\n}\n\nexport type LayoutStore = ReturnType<typeof createLayoutStore>\n","import type { DashboardQuery, DashboardResult } from '../types'\n\ntype Fetcher = (query: DashboardQuery) => Promise<DashboardResult>\n\ntype Pending = {\n query: DashboardQuery\n resolve: (result: DashboardResult) => void\n reject: (error: unknown) => void\n}\n\n/**\n * Manda num request só (`{ model, queries }`) as consultas de um modelo feitas no mesmo ciclo — as\n * de um render da página. O servidor junta numa SQL só as que dão para juntar (mesmas dimensões,\n * filtros e fatos; ver `runBatch`): os KPIs viram uma consulta com todas as medidas, e \"Vol. Total\"\n * (fórmula de \"Vol. Convencional\" + \"Vol. Esteira\") reaproveita as agregações dos outros dois.\n *\n * Consultas do devtools (`query.devtools`) e de metadados passam direto. Se o lote falhar (ou o\n * servidor for de uma versão sem lote), cada consulta é refeita sozinha.\n */\nexport function batchQueries(fetcher: Fetcher): Fetcher {\n const batches = new Map<string, Pending[]>()\n\n const alone = ({ query, resolve, reject }: Pending) => fetcher(query).then(resolve, reject)\n\n const flush = (model: string) => {\n const members = batches.get(model) ?? []\n batches.delete(model)\n if (members.length === 1) return alone(members[0])\n const queries = members.map(({ query: { dimensions, measures, filters } }) => ({\n dimensions,\n measures,\n filters\n }))\n fetcher({ model, queries }).then(\n ({ results }) => {\n if (results?.length !== members.length) return members.forEach(alone)\n members.forEach(({ resolve, reject }, index) => {\n const { error, ...result } = results[index]\n if (error) reject(new Error(error))\n else resolve(result)\n })\n },\n () => members.forEach(alone)\n )\n }\n\n return (query) => {\n if (query.meta || query.devtools || query.queries) return fetcher(query)\n return new Promise((resolve, reject) => {\n const members = batches.get(query.model)\n if (members) return void members.push({ query, resolve, reject })\n batches.set(query.model, [{ query, resolve, reject }])\n // As consultas de uma renderização disparam todas nos efeitos do mesmo commit\n setTimeout(() => flush(query.model), 0)\n })\n }\n}\n","import type { DashboardQuery, DashboardQueryDebug, DashboardRow } from '../types'\n\n/**\n * Registro das consultas feitas pelos componentes (`useModelQuery`), lido pelo painel do devtools.\n * É um store externo (`useSyncExternalStore`) para que registrar uma consulta não re-renderize a\n * página inteira — só o painel.\n */\n\nexport type RunStatus = 'loading' | 'success' | 'error' | 'cancelled'\n\nexport type QueryRun = {\n runId: number\n instanceId: string\n label: string\n query: DashboardQuery\n status: RunStatus\n startedAt: number\n /** Ida e volta medida no browser. */\n durationMs?: number\n rows?: DashboardRow[]\n error?: string\n debug?: DashboardQueryDebug\n /** Foi num request junto com outras consultas (quantas, contando ela); o `debug` é do conjunto. */\n batched?: number\n}\n\nexport type TrackedInstance = {\n instanceId: string\n label: string\n runs: number\n last: QueryRun\n /** Refeita muitas vezes em pouco tempo: sinal de re-render em loop (ex.: array novo a cada render). */\n storm: boolean\n}\n\nexport type DevtoolsSnapshot = {\n instances: TrackedInstance[]\n /** Execuções mais recentes primeiro. */\n history: QueryRun[]\n}\n\nconst MAX_HISTORY = 100\n/** `STORM_RUNS` execuções da mesma instância dentro de `STORM_WINDOW_MS` = consulta em loop. */\nconst STORM_RUNS = 6\nconst STORM_WINDOW_MS = 5000\n\nexport function errorMessage(error: unknown): string {\n const err = error as {\n response?: { data?: { error?: string; message?: string } }\n message?: string\n }\n return err?.response?.data?.error ?? err?.response?.data?.message ?? err?.message ?? String(error)\n}\n\nexport function createDevtoolsStore() {\n const instances = new Map<string, TrackedInstance>()\n let history: QueryRun[] = []\n // Execuções em andamento (o histórico é limitado e pode já ter descartado uma consulta lenta)\n const pending = new Map<number, QueryRun>()\n const recentStarts = new Map<string, number[]>()\n // Re-execuções pedidas pelo próprio devtools (refresh, aplicar rascunho) não contam como loop\n let expectedUntil = 0\n let snapshot: DevtoolsSnapshot = { instances: [], history: [] }\n let nextRunId = 1\n const listeners = new Set<() => void>()\n\n const emit = () => {\n snapshot = { instances: [...instances.values()], history }\n for (const listener of listeners) listener()\n }\n\n const update = (runId: number, patch: Partial<QueryRun>) => {\n const current = pending.get(runId)\n if (!current) return\n pending.delete(runId)\n const run = { ...current, ...patch, durationMs: Date.now() - current.startedAt }\n history = history.map((item) => (item.runId === runId ? run : item))\n const instance = instances.get(run.instanceId)\n if (instance?.last.runId === runId) instances.set(run.instanceId, { ...instance, last: run })\n emit()\n }\n\n return {\n subscribe(listener: () => void) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n getSnapshot: () => snapshot,\n\n start(instanceId: string, label: string, query: DashboardQuery) {\n const run: QueryRun = {\n runId: nextRunId++,\n instanceId,\n label,\n query,\n status: 'loading',\n startedAt: Date.now()\n }\n pending.set(run.runId, run)\n history = [run, ...history].slice(0, MAX_HISTORY)\n const previous = instances.get(instanceId)\n const expected = run.startedAt <= expectedUntil\n const starts = [\n ...(recentStarts.get(instanceId) ?? []),\n ...(expected ? [] : [run.startedAt])\n ].filter((time) => run.startedAt - time <= STORM_WINDOW_MS)\n recentStarts.set(instanceId, starts)\n const storm = previous?.storm || starts.length >= STORM_RUNS\n if (storm && !previous?.storm)\n console.warn(\n `[@bluemarble/bi] \"${label}\" executou ${starts.length}x em ${STORM_WINDOW_MS / 1000}s. ` +\n 'Confira se o componente não recria dimensions/measures/filtros a cada render.'\n )\n instances.set(instanceId, {\n instanceId,\n label,\n runs: (previous?.runs ?? 0) + 1,\n last: run,\n storm\n })\n emit()\n return run.runId\n },\n succeed: (runId: number, rows: DashboardRow[], debug?: DashboardQueryDebug, batched?: number) =>\n update(runId, { status: 'success', rows, debug, batched }),\n fail: (runId: number, error: unknown) =>\n update(runId, { status: 'error', error: errorMessage(error) }),\n /** Consulta descartada antes de responder (filtro mudou, componente desmontou). */\n cancel: (runId: number) => update(runId, { status: 'cancelled' }),\n\n remove(instanceId: string) {\n recentStarts.delete(instanceId)\n if (instances.delete(instanceId)) emit()\n },\n /** Avisa que todas as consultas vão refazer agora, de propósito. */\n expectRefetch() {\n expectedUntil = Date.now() + 500\n },\n /** Zera os alertas de loop (depois de corrigir o componente, sem recarregar a página). */\n resetStorms() {\n recentStarts.clear()\n for (const [id, instance] of instances) instances.set(id, { ...instance, storm: false })\n emit()\n },\n clearHistory() {\n history = []\n emit()\n }\n }\n}\n\nexport type DevtoolsStore = ReturnType<typeof createDevtoolsStore>\n","import {\n type ComponentType,\n createContext,\n lazy,\n type ReactNode,\n Suspense,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState\n} from 'react'\nimport { createLayoutStore, type LayoutStore } from '../devtools/layoutStore'\nimport { batchQueries } from './batchQueries'\nimport { createDevtoolsStore, type DevtoolsStore } from '../devtools/store'\nimport { type ChartTypeSpec, type ChartTypes, resolveChartTypes } from '../layout'\nimport type { ModelDef, ModelMeta } from '../model'\nimport type { DashboardFilters, DashboardQuery, DashboardResult, FilterValue } from '../types'\n\nexport type DashboardFetcher = (query: DashboardQuery) => Promise<DashboardResult>\n\n/**\n * Tipo de gráfico do app: os campos do JSON (`ChartTypeSpec`) e o componente que os renderiza. O\n * componente recebe os campos como props (com os `default` aplicados e formatters já resolvidos) e\n * `queryLabel` — ver `ChartComponentProps`.\n */\n// biome-ignore lint/suspicious/noExplicitAny: cada tipo tem as próprias props, vindas do JSON\nexport type ChartComponentSpec = ChartTypeSpec & { component: ComponentType<any> }\n\n/** Props de um componente registrado em `charts`: os campos do tipo + `queryLabel`. */\nexport type ChartComponentProps<Fields extends object = Record<string, unknown>> = Fields & {\n /** Nome da consulta no devtools (`<título> · <tipo>`): passe ao `useModelQuery({ label })`. */\n queryLabel: string\n}\n\n/** Só existe com o devtools ligado. */\nexport type DashboardDevtoolsContext = {\n store: DevtoolsStore\n /** Layouts da página (`<DashboardCharts>`/`<DashboardFilters>`) e os rascunhos da aba Layout. */\n layouts: LayoutStore\n /** Consulta o modelo registrado, ignorando o rascunho (comparações, snapshots). */\n rawFetcher: DashboardFetcher\n /** Rascunho do modelo aplicado: todas as consultas da página passam a usá-lo. */\n draft: ModelDef | null\n setDraft: (draft: ModelDef | null) => void\n /** Refaz todas as consultas da página. */\n refresh: () => void\n defaultFilters: DashboardFilters\n setFilters: (filters: DashboardFilters) => void\n}\n\ntype DashboardContextValue = {\n model: string\n filters: DashboardFilters\n setFilter: (dimension: string, values: FilterValue[]) => void\n fetcher: DashboardFetcher\n /** Dicionário do modelo (rótulos, descrições, formatos); `null` até carregar. */\n meta: ModelMeta | null\n /** Muda quando o devtools pede para refazer as consultas. */\n refreshKey: number\n /** Tipos de gráfico do layout: os da lib e os do app (`charts`). */\n chartTypes: ChartTypes\n /** Componentes dos tipos do app, por nome. */\n chartComponents: Record<string, ChartComponentSpec['component']>\n devtools: DashboardDevtoolsContext | null\n}\n\nconst DashboardContext = createContext<DashboardContextValue | null>(null)\n\n// Lazy: o painel não entra no bundle da página, só é baixado quando o devtools está ligado\nconst DashboardDevtools = lazy(() => import('../devtools/DashboardDevtools'))\n\nconst DEVTOOLS_DEFAULT = process.env.NODE_ENV === 'development'\n\nconst draftStorageKey = (model: string) => `bm-dashboard-devtools:draft:${model}`\n\nfunction loadDraft(model: string): ModelDef | null {\n try {\n const saved = sessionStorage.getItem(draftStorageKey(model))\n return saved ? (JSON.parse(saved) as ModelDef) : null\n } catch {\n return null\n }\n}\n\ntype DashboardProviderProps = {\n /** Nome do modelo no servidor (chave do registro de modelos do app). */\n model: string\n /** Como chamar o endpoint do app (ex.: `(q) => api.post('/dashboard/query', q).then((r) => r.data)`). */\n fetcher: DashboardFetcher\n defaultFilters?: DashboardFilters\n /**\n * Painel de desenvolvimento (debug das consultas, edição do modelo). Padrão: ligado só com\n * `NODE_ENV=development`. O endpoint também precisa de `runQuery(..., { devtools: true })`.\n */\n devtools?: boolean\n /**\n * Tipos de gráfico do app, usáveis no layout JSON (`\"type\": \"<nome>\"`) e no editor do devtools.\n * Declare fora do componente (referência estável). O servidor precisa dos mesmos campos em\n * `devtools.chartTypes` para salvar o layout.\n */\n charts?: Record<string, ChartComponentSpec>\n /**\n * Manda as consultas de um render num request só; o servidor junta numa SQL as que têm as mesmas\n * dimensões, filtros e fatos (ex.: os KPIs). Padrão: ligado.\n */\n batch?: boolean\n children: ReactNode\n}\n\n/**\n * Guarda os filtros do dashboard. Todo componente dentro dele (filtros, gráficos, tabelas) consulta o\n * mesmo modelo com os mesmos filtros — é o que faz um slicer filtrar a página inteira, como no BI.\n */\nexport function DashboardProvider({\n model,\n fetcher,\n defaultFilters = {},\n devtools = DEVTOOLS_DEFAULT,\n charts,\n batch = true,\n children\n}: DashboardProviderProps) {\n const chartTypes = useMemo(() => resolveChartTypes(charts), [charts])\n const chartComponents = useMemo(\n () =>\n Object.fromEntries(\n Object.entries(charts ?? {}).map(([name, spec]) => [name, spec.component])\n ),\n [charts]\n )\n const [filters, setFilters] = useState<DashboardFilters>(defaultFilters)\n const defaultsRef = useRef(defaultFilters)\n\n const setFilter = useCallback(\n (dimension: string, values: FilterValue[]) =>\n setFilters((current) => ({ ...current, [dimension]: values })),\n []\n )\n\n // --- devtools ---\n const store = useMemo(() => (devtools ? createDevtoolsStore() : null), [devtools])\n const layouts = useMemo(() => (devtools ? createLayoutStore() : null), [devtools])\n // O rascunho aplicado sobrevive ao reload da aba (útil enquanto se edita o modelo)\n const [draft, setDraftState] = useState<ModelDef | null>(() =>\n devtools && typeof window !== 'undefined' ? loadDraft(model) : null\n )\n const [refreshKey, setRefreshKey] = useState(0)\n const [mounted, setMounted] = useState(false)\n useEffect(() => setMounted(true), [])\n\n const setDraft = useCallback(\n (next: ModelDef | null) => {\n store?.expectRefetch()\n setDraftState(next)\n try {\n if (next) sessionStorage.setItem(draftStorageKey(model), JSON.stringify(next))\n else sessionStorage.removeItem(draftStorageKey(model))\n } catch {}\n },\n [model, store]\n )\n\n const refresh = useCallback(() => {\n store?.expectRefetch()\n setRefreshKey((key) => key + 1)\n }, [store])\n\n // Com o devtools ligado, toda consulta pede debug e, se houver, roda contra o rascunho\n const effectiveFetcher = useMemo<DashboardFetcher>(() => {\n if (!devtools) return fetcher\n return (query) =>\n fetcher({\n ...query,\n devtools: { debug: true, ...(draft && { model: draft }), ...query.devtools }\n })\n }, [fetcher, devtools, draft])\n const queryFetcher = useMemo(\n () => (batch ? batchQueries(effectiveFetcher) : effectiveFetcher),\n [batch, effectiveFetcher]\n )\n\n const rawFetcher = useMemo<DashboardFetcher>(\n () => (query) => fetcher({ ...query, devtools: { debug: true, ...query.devtools } }),\n [fetcher]\n )\n\n // Dicionário: segue o rascunho, para os rótulos editados aparecerem na hora\n const [meta, setMeta] = useState<ModelMeta | null>(null)\n useEffect(() => {\n let cancelado = false\n effectiveFetcher({ model, meta: true })\n .then((result) => !cancelado && setMeta(result.meta ?? null))\n .catch(() => !cancelado && setMeta(null))\n return () => {\n cancelado = true\n }\n }, [effectiveFetcher, model])\n\n const devtoolsValue = useMemo(\n () =>\n store &&\n layouts && {\n store,\n layouts,\n rawFetcher,\n draft,\n setDraft,\n refresh,\n defaultFilters: defaultsRef.current,\n setFilters\n },\n [store, layouts, rawFetcher, draft, setDraft, refresh]\n )\n\n const value = useMemo(\n () => ({\n model,\n filters,\n setFilter,\n fetcher: queryFetcher,\n meta,\n refreshKey,\n chartTypes,\n chartComponents,\n devtools: devtoolsValue\n }),\n [\n model,\n filters,\n setFilter,\n queryFetcher,\n meta,\n refreshKey,\n chartTypes,\n chartComponents,\n devtoolsValue\n ]\n )\n\n return (\n <DashboardContext.Provider value={value}>\n {children}\n {/* Só depois de montar: o painel lê localStorage e não deve participar da hidratação */}\n {devtoolsValue && mounted && (\n <Suspense fallback={null}>\n <DashboardDevtools />\n </Suspense>\n )}\n </DashboardContext.Provider>\n )\n}\n\nexport function useDashboard() {\n const context = useContext(DashboardContext)\n if (!context) throw new Error('useDashboard precisa estar dentro de <DashboardProvider>')\n return context\n}\n","import { Box, CircularProgress } from '@mui/material'\nimport type { MouseEvent, ReactNode } from 'react'\nimport type { DashboardRow, FilterValue } from '../types'\nimport { useDashboard } from './DashboardProvider'\n\n/**\n * Peças comuns dos gráficos ligados ao modelo: paleta, filtro por clique, legenda e os estados de\n * carregando/erro/vazio.\n */\n\n/**\n * Paleta categórica, sempre nesta ordem (a série N tem sempre a mesma cor). Laranja Rocha primeiro;\n * a ordem passa no validador de daltonismo (CVD ΔE ≥ 9 entre vizinhas) e de visão normal (≥ 19).\n */\nexport const SERIES_COLORS = [\n '#F58220',\n '#2a78d6',\n '#1baf7a',\n '#eda100',\n '#e87ba4',\n '#008300',\n '#6250d6',\n '#e34948'\n] as const\n\n/** \"Outros\" (fatias agrupadas) fica neutro, fora da paleta. */\nexport const OTHER_COLOR = '#9e9e9e'\n\nexport const seriesColor = (index: number) => SERIES_COLORS[index % SERIES_COLORS.length] as string\n\n/** Valor numérico de uma coluna da linha (`null` quando vazio). */\nexport const numberOf = (row: DashboardRow, key?: string) =>\n key && row[key] !== null && row[key] !== undefined ? Number(row[key]) : null\n\n/**\n * Filtro cruzado a partir do gráfico: clicar num valor filtra o dashboard por ele (Ctrl/Cmd+clique\n * acumula; clicar de novo no único selecionado limpa). O gráfico deve ignorar o próprio filtro na\n * consulta (`ignoreFilters`) e só destacar a seleção.\n */\nexport function useClickFilter(dimension: string | undefined, enabled: boolean) {\n const { filters, setFilter } = useDashboard()\n const selected = enabled && dimension ? (filters[dimension] ?? []) : []\n\n const toggle = (value: FilterValue, event?: Pick<MouseEvent, 'ctrlKey' | 'metaKey'>) => {\n if (!enabled || !dimension) return\n if (event?.ctrlKey || event?.metaKey)\n setFilter(\n dimension,\n selected.includes(value) ? selected.filter((item) => item !== value) : [...selected, value]\n )\n else setFilter(dimension, selected.length === 1 && selected[0] === value ? [] : [value])\n }\n\n return {\n selected,\n /** Há seleção e este valor não está nela. */\n isDimmed: (value: FilterValue) => selected.length > 0 && !selected.includes(value),\n toggle,\n clear: () => {\n if (enabled && dimension && selected.length) setFilter(dimension, [])\n }\n }\n}\n\nexport function Legend({\n items,\n loading\n}: {\n items: { label: string; color: string }[]\n loading?: boolean\n}) {\n return (\n <Box\n sx={{\n display: 'flex',\n flexWrap: 'wrap',\n columnGap: 1.5,\n rowGap: 0.5,\n mb: 1,\n fontSize: 14,\n color: '#555',\n minHeight: 21\n }}\n >\n {items.map((item) => (\n <Box key={item.label} sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>\n <Box sx={{ width: 9, height: 9, borderRadius: '50%', bgcolor: item.color }} />\n {item.label}\n </Box>\n ))}\n {loading && <CircularProgress size={14} sx={{ ml: 'auto' }} />}\n </Box>\n )\n}\n\n/** Erro / carregando / sem dados no lugar do gráfico; `null` quando há o que desenhar (função, não componente). */\nexport function chartPlaceholder({\n height,\n error,\n isLoading,\n empty\n}: {\n height: number\n error: unknown\n isLoading: boolean\n empty: boolean\n}): ReactNode {\n if (!error && !empty) return null\n return (\n <Box\n sx={{\n height,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: error ? 'error.main' : '#999'\n }}\n >\n {error\n ? 'Erro ao carregar o gráfico'\n : isLoading\n ? 'Carregando…'\n : 'Sem dados para os filtros selecionados'}\n </Box>\n )\n}\n\n/** `caption`: o que é o título (ex.: o rótulo da dimensão), em cinza acima dele. */\nexport function TooltipBox({\n title,\n caption,\n children\n}: {\n title: string\n caption?: string\n children: ReactNode\n}) {\n return (\n <Box sx={{ bgcolor: '#fff', border: '1px solid #ccc', p: 1, fontSize: 13 }}>\n {caption && <Box sx={{ fontSize: 11, color: '#777' }}>{caption}</Box>}\n <Box sx={{ fontWeight: 700 }}>{title}</Box>\n {children}\n </Box>\n )\n}\n","import type { ChartNumberFormat } from '../layout'\nimport type { ModelMeta } from '../model'\nimport type { FilterValue } from '../types'\n\nconst formatters = new Map<string, Intl.NumberFormat>()\n\n/** `compact`: notação curta do pt-BR (1,2 mil / 3,4 mi / 5,6 bi). */\nexport function formatNumber(\n value: number | string | null | undefined,\n decimals = 2,\n compact = false\n) {\n if (value === null || value === undefined || value === '') return ''\n const number = Number(value)\n if (Number.isNaN(number)) return String(value)\n const key = `${decimals}:${compact}`\n let formatter = formatters.get(key)\n if (!formatter) {\n formatter = new Intl.NumberFormat('pt-BR', {\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n ...(compact && { notation: 'compact' })\n })\n formatters.set(key, formatter)\n }\n return formatter.format(number)\n}\n\nexport const formatCurrency = (\n value: number | string | null | undefined,\n decimals = 2,\n compact = false\n) => {\n const text = formatNumber(value, decimals, compact)\n return text && `R$ ${text}`\n}\n\n/**\n * Formata pelo dicionário do modelo (`format`/`decimals` da medida) ou pelo formato do gráfico.\n * `fallbackDecimals` é o padrão do componente quando nenhum dos dois define `decimals`.\n */\nexport function formatMeasure(\n value: number | string | null | undefined,\n meta: ChartNumberFormat | undefined,\n fallbackDecimals = 2\n) {\n if (value === null || value === undefined || value === '') return ''\n const decimals = meta?.decimals ?? (meta?.format === 'integer' ? 0 : fallbackDecimals)\n const compact = meta?.compact ?? false\n let text: string\n switch (meta?.format) {\n case 'currency':\n text = formatCurrency(value, decimals, compact)\n break\n case 'percent': {\n const number = formatNumber(Number(value) * 100, decimals, compact)\n text = number && `${number}%`\n break\n }\n default:\n text = formatNumber(value, decimals, compact)\n }\n return text && `${meta?.prefix ?? ''}${text}${meta?.suffix ?? ''}`\n}\n\n/** Dicionário do modelo com o formato do gráfico por cima. */\nexport function measureFormat(\n meta: ModelMeta | null,\n measure: string,\n formats?: Record<string, ChartNumberFormat>\n): ChartNumberFormat | undefined {\n const fromModel = meta?.measures[measure]\n const fromChart = formats?.[measure]\n if (!fromChart) return fromModel\n return {\n ...(fromModel?.format !== undefined && { format: fromModel.format }),\n ...(fromModel?.decimals !== undefined && { decimals: fromModel.decimals }),\n ...fromChart\n }\n}\n\n/**\n * Formatter de uma medida: o `formats` do gráfico (se houver) por cima do dicionário do modelo; sem\n * `format`, número com `fallbackDecimals`.\n */\nexport function measureFormatter(\n meta: ModelMeta | null,\n measure: string,\n fallbackDecimals = 2,\n formats?: Record<string, ChartNumberFormat>\n): (value: number | string | null | undefined) => string {\n const format = measureFormat(meta, measure, formats)\n return (value) => formatMeasure(value, format, fallbackDecimals)\n}\n\n/** Rótulo de uma dimensão/medida: o `labels` do gráfico, o dicionário do modelo ou o nome. */\nexport function fieldLabel(\n meta: ModelMeta | null,\n name: string,\n labels?: Record<string, string>\n): string {\n return labels?.[name] || meta?.dimensions[name]?.label || meta?.measures[name]?.label || name\n}\n\nconst monthNames = new Intl.DateTimeFormat('pt-BR', { month: 'long', timeZone: 'UTC' })\nconst dateFormat = new Intl.DateTimeFormat('pt-BR', { dateStyle: 'short', timeZone: 'UTC' })\nconst localDate = new Intl.DateTimeFormat('pt-BR', { dateStyle: 'short' })\nconst localDateTime = new Intl.DateTimeFormat('pt-BR', { dateStyle: 'short', timeStyle: 'short' })\nconst looseNumber = new Intl.NumberFormat('pt-BR', { maximumFractionDigits: 2 })\n\nconst capitalize = (text: string) => text.charAt(0).toUpperCase() + text.slice(1)\nconst ISO_DATE = /^(\\d{4})-(\\d{2})-(\\d{2})$/\n\n/** `3` → \"Março\"; aceita também uma data ISO (usa o mês dela). Fora de 1–12, o próprio valor. */\nexport function toMonthName(value: FilterValue): string {\n const iso = typeof value === 'string' && /^\\d{4}-\\d{2}/.test(value)\n const month = iso ? Number(value.slice(5, 7)) : Number(value)\n if (!Number.isInteger(month) || month < 1 || month > 12) return String(value)\n return capitalize(monthNames.format(new Date(Date.UTC(2000, month - 1, 1))))\n}\n\n/**\n * Data ISO → `dd/mm/aaaa`. Só a data (`2026-10-01`) não passa por fuso; com hora (o `Date` do\n * banco chega como ISO em UTC), vira o horário local e a hora só aparece quando não é meia-noite.\n */\nexport function toDate(value: FilterValue): string {\n if (typeof value !== 'string') return String(value)\n const day = ISO_DATE.exec(value)\n if (day) return dateFormat.format(new Date(Date.UTC(+day[1], +day[2] - 1, +day[3])))\n const date = new Date(value)\n if (!/^\\d{4}-\\d{2}-\\d{2}T/.test(value) || Number.isNaN(date.getTime())) return value\n if (date.getHours() || date.getMinutes()) return localDateTime.format(date).replace(',', '')\n return localDate.format(date)\n}\n\n/** Não numérico: o próprio valor. */\nconst numeric = (format: (value: number) => string) => (value: FilterValue) =>\n value === '' || Number.isNaN(Number(value)) ? String(value) : format(Number(value))\n\n/**\n * Formatters que todo `<DashboardCharts>`/`<DashboardFilters>` já tem, pelo nome (os do app com o\n * mesmo nome vão por cima).\n */\nexport const BUILTIN_FORMATTERS: Record<string, (value: FilterValue) => string> = {\n to_month_name: toMonthName,\n to_date: toDate,\n to_currency: numeric((value) => formatCurrency(value)),\n to_number: numeric((value) => looseNumber.format(value)),\n to_string: (value) => String(value ?? '')\n}\n\n/** Os built-in mais os do app. */\nexport const withBuiltinFormatters = (\n formatters: Record<string, (value: FilterValue) => string>\n) => ({ ...BUILTIN_FORMATTERS, ...formatters })\n","import { useEffect, useId, useMemo, useState } from 'react'\nimport type { DashboardFilters, DashboardRow } from '../types'\nimport { useDashboard } from './DashboardProvider'\n\nexport const INSPECT_ATTRIBUTE = 'data-bm-dashboard-query'\n\ntype UseModelQueryParams = {\n dimensions?: string[]\n measures?: string[]\n /**\n * Filtros do dashboard que esta consulta NÃO deve respeitar. Um filtro ignora a si mesmo para\n * listar as opções (senão, ao escolher um navio, a lista mostraria só ele).\n */\n ignoreFilters?: string[]\n /** Nome da consulta no devtools (padrão: dimensões × medidas). */\n label?: string\n /** Não consulta (devolve `rows: []`); ex.: a linha de total de uma tabela que não mostra total. */\n skip?: boolean\n}\n\nexport function useModelQuery({\n dimensions,\n measures,\n ignoreFilters,\n label,\n skip = false\n}: UseModelQueryParams) {\n const { model, filters, fetcher, refreshKey, devtools } = useDashboard()\n const instanceId = useId()\n\n const applied: DashboardFilters = {}\n for (const [dimension, values] of Object.entries(filters)) {\n if (values.length && !ignoreFilters?.includes(dimension)) applied[dimension] = values\n }\n\n // Chave por valor: só refaz a consulta quando o que vai pro servidor muda (não a cada render,\n // nem quando muda um filtro que esta consulta ignora)\n const key = JSON.stringify([model, dimensions, measures, applied])\n const query = useMemo(() => ({ model, dimensions, measures, filters: applied }), [key])\n\n const [rows, setRows] = useState<DashboardRow[]>([])\n const [isLoading, setIsLoading] = useState(true)\n const [error, setError] = useState<unknown>()\n\n const store = devtools?.store\n const debugLabel =\n label ?? [dimensions?.join(', '), measures?.join(', ')].filter(Boolean).join(' × ')\n\n useEffect(() => {\n if (skip) {\n setRows([])\n setIsLoading(false)\n return\n }\n let cancelado = false\n setIsLoading(true)\n const runId = store?.start(instanceId, debugLabel, query)\n fetcher(query)\n .then((result) => {\n if (cancelado) return\n setRows(result.rows)\n setError(undefined)\n if (runId) store?.succeed(runId, result.rows, result.devtools?.debug, result.batched)\n })\n .catch((err) => {\n if (cancelado) return\n setError(err)\n if (runId) store?.fail(runId, err)\n })\n .finally(() => !cancelado && setIsLoading(false))\n return () => {\n cancelado = true\n if (runId) store?.cancel(runId)\n }\n }, [fetcher, query, refreshKey, skip])\n\n useEffect(() => (store ? () => store.remove(instanceId) : undefined), [store, instanceId, skip])\n\n // Espalhar no elemento raiz do componente: é como o inspetor do devtools acha o componente na tela\n const inspectProps = store && !skip ? { [INSPECT_ATTRIBUTE]: instanceId } : {}\n\n return { rows, isLoading, error, inspectProps }\n}\n","import {\n Box,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Typography\n} from '@mui/material'\nimport { AGGREGATION_LABEL, isAggregate, type ModelMeta, type ValueFormat } from '../model'\nimport { useDashboard } from './DashboardProvider'\n\nconst FORMAT_LABEL: Record<ValueFormat, string> = {\n number: 'Número',\n integer: 'Inteiro',\n currency: 'Moeda (R$)',\n percent: 'Percentual'\n}\n\n/** Como a medida é calculada, em termos de negócio (o SQL não sai do servidor). */\nfunction formula(meta: ModelMeta, name: string) {\n const measure = meta.measures[name]\n const label = (ref: string) => meta.measures[ref]?.label ?? ref\n if (measure.type === 'formula' && measure.refs?.length)\n return `Combina ${measure.refs.map(label).join(', ')}`\n if (isAggregate(measure)) return AGGREGATION_LABEL[measure.type]\n if (measure.type !== 'ratio' || !measure.numerator || !measure.denominator) return '—'\n return `${label(measure.numerator)} ÷ ${label(measure.denominator)}`\n}\n\n/**\n * Dicionário do modelo do `DashboardProvider`: o que cada filtro/eixo e cada número significa. É\n * montado a partir de `label`/`description` das dimensões e medidas — documentação que não\n * desatualiza, porque é a mesma definição que calcula os números.\n */\nexport function ModelDictionary() {\n const { meta } = useDashboard()\n if (!meta) return null\n\n const header = (labels: string[]) => (\n <TableHead>\n <TableRow>\n {labels.map((label) => (\n <TableCell key={label} sx={{ fontWeight: 700 }}>\n {label}\n </TableCell>\n ))}\n </TableRow>\n </TableHead>\n )\n\n return (\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <Box>\n <Typography sx={{ fontWeight: 700, mb: 1 }}>Indicadores</Typography>\n <TableContainer>\n <Table size=\"small\">\n {header(['Indicador', 'Descrição', 'Cálculo', 'Formato'])}\n <TableBody>\n {Object.entries(meta.measures).map(([name, measure]) => (\n <TableRow key={name}>\n <TableCell sx={{ whiteSpace: 'nowrap' }}>{measure.label ?? name}</TableCell>\n <TableCell>{measure.description ?? '—'}</TableCell>\n <TableCell sx={{ whiteSpace: 'nowrap' }}>{formula(meta, name)}</TableCell>\n <TableCell sx={{ whiteSpace: 'nowrap' }}>\n {measure.format ? FORMAT_LABEL[measure.format] : '—'}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </Box>\n <Box>\n <Typography sx={{ fontWeight: 700, mb: 1 }}>Filtros e agrupamentos</Typography>\n <TableContainer>\n <Table size=\"small\">\n {header(['Campo', 'Descrição'])}\n <TableBody>\n {Object.entries(meta.dimensions).map(([name, dimension]) => (\n <TableRow key={name}>\n <TableCell sx={{ whiteSpace: 'nowrap' }}>{dimension.label ?? name}</TableCell>\n <TableCell>{dimension.description ?? '—'}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </Box>\n </Box>\n )\n}\n"],"mappings":"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"}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as ChartNumberFormat, D as CardStyle, M as ChartTypeSpec, N as ChartTypes, P as ChartsLayout, R as DonutLegendPosition, a as ValueRule, d as DashboardQueryDebug, et as ModelDef, f as DashboardResult, g as FilterValue, i as FormatRule, k as ChartDef, l as DashboardFilters$1, n as ColumnAlign, p as DashboardRow, r as ColumnVerticalAlign, t as ColorScale, tt as ModelMeta, u as DashboardQuery, w as BarValuePosition, z as DonutValueContent } from "../formatRules-DXMHY9tU.js";
|
|
2
2
|
import { ComponentType, MouseEvent, ReactNode } from "react";
|
|
3
3
|
//#region src/client/BarChart.d.ts
|
|
4
4
|
type BarChartProps = {
|
|
@@ -202,7 +202,7 @@ type ChartPanelProps = {
|
|
|
202
202
|
type DashboardChartsProps = {
|
|
203
203
|
/** Layout importado do JSON (`src/dashboard/layouts/<id>.json`). */
|
|
204
204
|
layout: ChartsLayout;
|
|
205
|
-
/** Formatters por nome, referenciados no JSON por `formatDimension
|
|
205
|
+
/** Formatters por nome, referenciados no JSON por `formatDimension` (além dos built-in `to_*`). */
|
|
206
206
|
formatters?: Record<string, (value: FilterValue) => string>;
|
|
207
207
|
/** Moldura de cada gráfico (título + conteúdo). */
|
|
208
208
|
panel?: ComponentType<ChartPanelProps>;
|
|
@@ -246,7 +246,7 @@ declare function DashboardCharts({ layout, formatters, panel: Panel, gap }: Dash
|
|
|
246
246
|
type DashboardFiltersProps = {
|
|
247
247
|
/** O mesmo layout do `<DashboardCharts>` (`filters` do JSON). */
|
|
248
248
|
layout: ChartsLayout;
|
|
249
|
-
/** Formatters por nome, referenciados no JSON por `formatLabel
|
|
249
|
+
/** Formatters por nome, referenciados no JSON por `formatLabel` (além dos built-in `to_*`). */
|
|
250
250
|
formatters?: Record<string, (value: FilterValue) => string>;
|
|
251
251
|
gap?: number;
|
|
252
252
|
};
|
|
@@ -465,6 +465,11 @@ type DataTableProps = {
|
|
|
465
465
|
* uma dimensão/medida (da tabela, de fora dela ou `%:medida`).
|
|
466
466
|
*/
|
|
467
467
|
formatRules?: FormatRule[];
|
|
468
|
+
/**
|
|
469
|
+
* Escalas de cor (gradiente): o fundo ou o texto de uma coluna numérica vai de `minColor` a
|
|
470
|
+
* `color` conforme o valor (ex.: 0 a 100). As regras de `formatRules` ficam por cima.
|
|
471
|
+
*/
|
|
472
|
+
colorScales?: ColorScale[];
|
|
468
473
|
/** Bordas entre as células (grade). Padrão: só a linha embaixo de cada linha. */
|
|
469
474
|
cellBorders?: boolean;
|
|
470
475
|
/** Cor das bordas da grade. Padrão: `#e0e0e0`. */
|
|
@@ -483,7 +488,7 @@ type DataTableProps = {
|
|
|
483
488
|
* Tabela de medidas por dimensões, com total e ordenação pelo cabeçalho. Ex.:
|
|
484
489
|
* `<DataTable dimensions={['navio']} measures={['volTotal', 'custoTotal', 'custoTon']} />`.
|
|
485
490
|
*/
|
|
486
|
-
declare function DataTable({ dimensions, measures, percentOf, formatDimensions, showTotal, sortBy, sortDirection, clickToFilter, height, labels, formats: formatsByMeasure, percentDecimals, columnOrder, formatRules, cellBorders, borderColor, align, verticalAlign, queryLabel }: DataTableProps): import("react").JSX.Element;
|
|
491
|
+
declare function DataTable({ dimensions, measures, percentOf, formatDimensions, showTotal, sortBy, sortDirection, clickToFilter, height, labels, formats: formatsByMeasure, percentDecimals, columnOrder, formatRules, colorScales, cellBorders, borderColor, align, verticalAlign, queryLabel }: DataTableProps): import("react").JSX.Element;
|
|
487
492
|
//#endregion
|
|
488
493
|
//#region src/client/DonutChart.d.ts
|
|
489
494
|
type DonutChartProps = {
|
|
@@ -553,6 +558,18 @@ declare function measureFormat(meta: ModelMeta | null, measure: string, formats?
|
|
|
553
558
|
declare function measureFormatter(meta: ModelMeta | null, measure: string, fallbackDecimals?: number, formats?: Record<string, ChartNumberFormat>): (value: number | string | null | undefined) => string;
|
|
554
559
|
/** Rótulo de uma dimensão/medida: o `labels` do gráfico, o dicionário do modelo ou o nome. */
|
|
555
560
|
declare function fieldLabel(meta: ModelMeta | null, name: string, labels?: Record<string, string>): string;
|
|
561
|
+
/** `3` → "Março"; aceita também uma data ISO (usa o mês dela). Fora de 1–12, o próprio valor. */
|
|
562
|
+
declare function toMonthName(value: FilterValue): string;
|
|
563
|
+
/**
|
|
564
|
+
* Data ISO → `dd/mm/aaaa`. Só a data (`2026-10-01`) não passa por fuso; com hora (o `Date` do
|
|
565
|
+
* banco chega como ISO em UTC), vira o horário local e a hora só aparece quando não é meia-noite.
|
|
566
|
+
*/
|
|
567
|
+
declare function toDate(value: FilterValue): string;
|
|
568
|
+
/**
|
|
569
|
+
* Formatters que todo `<DashboardCharts>`/`<DashboardFilters>` já tem, pelo nome (os do app com o
|
|
570
|
+
* mesmo nome vão por cima).
|
|
571
|
+
*/
|
|
572
|
+
declare const BUILTIN_FORMATTERS: Record<string, (value: FilterValue) => string>;
|
|
556
573
|
//#endregion
|
|
557
574
|
//#region src/client/KpiCard.d.ts
|
|
558
575
|
type KpiCardProps = {
|
|
@@ -612,5 +629,5 @@ declare function useModelQuery({ dimensions, measures, ignoreFilters, label, ski
|
|
|
612
629
|
};
|
|
613
630
|
};
|
|
614
631
|
//#endregion
|
|
615
|
-
export { BarChart, type ChartComponentProps, type ChartComponentSpec, type ChartPanelProps, ComboChart, DashboardCanvas, DashboardCharts, type DashboardFetcher, DashboardFilters, DashboardProvider, DataTable, DonutChart, FilterSelect, INSPECT_ATTRIBUTE, KpiCard, Legend, ModelDictionary, OTHER_COLOR, SERIES_COLORS, TooltipBox, chartCardSx, chartPlaceholder, chartTitleSx, fieldLabel, formatCurrency, formatMeasure, formatNumber, measureFormat, measureFormatter, numberOf, seriesColor, useClickFilter, useDashboard, useModelQuery };
|
|
632
|
+
export { BUILTIN_FORMATTERS, BarChart, type ChartComponentProps, type ChartComponentSpec, type ChartPanelProps, ComboChart, DashboardCanvas, DashboardCharts, type DashboardFetcher, DashboardFilters, DashboardProvider, DataTable, DonutChart, FilterSelect, INSPECT_ATTRIBUTE, KpiCard, Legend, ModelDictionary, OTHER_COLOR, SERIES_COLORS, TooltipBox, chartCardSx, chartPlaceholder, chartTitleSx, fieldLabel, formatCurrency, formatMeasure, formatNumber, measureFormat, measureFormatter, numberOf, seriesColor, toDate, toMonthName, useClickFilter, useDashboard, useModelQuery };
|
|
616
633
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/client/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { A as contrastText, M as rowRuleStyles, d as customChartProps, g as percentColumn, j as matchesValueRule, k as colorScaleStyles, o as cardsGap, p as isBuiltinChart, s as chartCardStyle, v as tableColumns } from "../layout-BS3nO6OT.js";
|
|
2
|
+
import { C as useDashboard, E as layoutItemKey, S as DashboardProvider, T as LAYOUT_ITEM_ATTRIBUTE, _ as TooltipBox, a as fieldLabel, b as seriesColor, c as formatNumber, d as toDate, f as toMonthName, g as SERIES_COLORS, h as OTHER_COLOR, i as BUILTIN_FORMATTERS, l as measureFormat, m as Legend, n as INSPECT_ATTRIBUTE, o as formatCurrency, p as withBuiltinFormatters, r as useModelQuery, s as formatMeasure, t as ModelDictionary, u as measureFormatter, v as chartPlaceholder, x as useClickFilter, y as numberOf } from "../ModelDictionary-DZiYK1mV.js";
|
|
3
3
|
import { Box, Checkbox, CircularProgress, FormControl, InputLabel, ListItemText, MenuItem, OutlinedInput, Paper, Select, Table, TableBody, TableCell, TableHead, TableRow, TableSortLabel, Typography } from "@mui/material";
|
|
4
4
|
import { Bar, BarChart as BarChart$1, CartesianGrid, Cell, ComposedChart, LabelList, Line, Pie, PieChart, ResponsiveContainer, Tooltip as Tooltip$1, XAxis, YAxis } from "recharts";
|
|
5
5
|
import { createContext, useContext, useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
@@ -819,6 +819,7 @@ function DashboardCanvas({ layout, children }) {
|
|
|
819
819
|
//#region src/client/DataTable.tsx
|
|
820
820
|
const EMPTY = [];
|
|
821
821
|
const NO_RULES = [];
|
|
822
|
+
const NO_SCALES = [];
|
|
822
823
|
function compare(a, b) {
|
|
823
824
|
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
824
825
|
return String(a).localeCompare(String(b), "pt-BR", { numeric: true });
|
|
@@ -827,7 +828,7 @@ function compare(a, b) {
|
|
|
827
828
|
* Tabela de medidas por dimensões, com total e ordenação pelo cabeçalho. Ex.:
|
|
828
829
|
* `<DataTable dimensions={['navio']} measures={['volTotal', 'custoTotal', 'custoTon']} />`.
|
|
829
830
|
*/
|
|
830
|
-
function DataTable({ dimensions = EMPTY, measures, percentOf = EMPTY, formatDimensions = {}, showTotal = true, sortBy, sortDirection = "desc", clickToFilter = false, height = 400, labels, formats: formatsByMeasure, percentDecimals = 1, columnOrder, formatRules = NO_RULES, cellBorders = false, borderColor = "#e0e0e0", align, verticalAlign, queryLabel }) {
|
|
831
|
+
function DataTable({ dimensions = EMPTY, measures, percentOf = EMPTY, formatDimensions = {}, showTotal = true, sortBy, sortDirection = "desc", clickToFilter = false, height = 400, labels, formats: formatsByMeasure, percentDecimals = 1, columnOrder, formatRules = NO_RULES, colorScales = NO_SCALES, cellBorders = false, borderColor = "#e0e0e0", align, verticalAlign, queryLabel }) {
|
|
831
832
|
const { meta } = useDashboard();
|
|
832
833
|
const name = queryLabel ?? `DataTable · ${dimensions.join(", ") || "total"}`;
|
|
833
834
|
const firstDimension = dimensions[0];
|
|
@@ -889,11 +890,31 @@ function DataTable({ dimensions = EMPTY, measures, percentOf = EMPTY, formatDime
|
|
|
889
890
|
return value === null || value === void 0 || !total ? null : Number(value) / total;
|
|
890
891
|
};
|
|
891
892
|
const columns = tableColumns([...dimensions, ...measures], percentOf.filter((measure) => !dimensions.includes(measure)), columnOrder);
|
|
892
|
-
const
|
|
893
|
-
if (!
|
|
893
|
+
const evaluate = (row) => {
|
|
894
|
+
if (!percentOf.length) return row;
|
|
894
895
|
const evaluated = { ...row };
|
|
895
896
|
for (const measure of percentOf) evaluated[percentColumn(measure)] = percentOfRow(row, measure);
|
|
896
|
-
return
|
|
897
|
+
return evaluated;
|
|
898
|
+
};
|
|
899
|
+
const scaleStyles = colorScales.length ? colorScaleStyles(colorScales, rows.map(evaluate)) : null;
|
|
900
|
+
const ruleStyles = (row) => {
|
|
901
|
+
if (!formatRules.length && !scaleStyles) return void 0;
|
|
902
|
+
const evaluated = evaluate(row);
|
|
903
|
+
const rules = formatRules.length ? rowRuleStyles(formatRules, evaluated, columns) : {};
|
|
904
|
+
if (!scaleStyles) return rules;
|
|
905
|
+
const scaled = scaleStyles(evaluated);
|
|
906
|
+
return Object.fromEntries(columns.map((column) => {
|
|
907
|
+
const [scale, rule] = [scaled[column], rules[column]];
|
|
908
|
+
const style = {
|
|
909
|
+
...scale,
|
|
910
|
+
...rule
|
|
911
|
+
};
|
|
912
|
+
if (scale?.background && !rule?.background && !style.color) {
|
|
913
|
+
const text = contrastText(scale.background);
|
|
914
|
+
if (text) style.color = text;
|
|
915
|
+
}
|
|
916
|
+
return [column, style];
|
|
917
|
+
}));
|
|
897
918
|
};
|
|
898
919
|
const ruleSx = (style) => style && {
|
|
899
920
|
...style.color && { color: style.color },
|
|
@@ -1433,6 +1454,7 @@ function DefaultPanel({ title, card, children }) {
|
|
|
1433
1454
|
* ```
|
|
1434
1455
|
*/
|
|
1435
1456
|
function DashboardCharts({ layout, formatters = {}, panel: Panel = DefaultPanel, gap = 1.5 }) {
|
|
1457
|
+
formatters = withBuiltinFormatters(formatters);
|
|
1436
1458
|
const current = useLayout(layout, "charts", formatters);
|
|
1437
1459
|
const { charts } = current;
|
|
1438
1460
|
const fixed = useFixedCanvas();
|
|
@@ -1602,6 +1624,7 @@ function FilterSelect({ dimension, label, formatLabel = String, width = 200 }) {
|
|
|
1602
1624
|
* devtools adiciona, remove, reordena e edita os filtros ao vivo.
|
|
1603
1625
|
*/
|
|
1604
1626
|
function DashboardFilters({ layout, formatters = {}, gap = 2 }) {
|
|
1627
|
+
formatters = withBuiltinFormatters(formatters);
|
|
1605
1628
|
const { filters = [] } = useLayout(layout, "filters", formatters);
|
|
1606
1629
|
return /* @__PURE__ */ jsx(Box, {
|
|
1607
1630
|
sx: {
|
|
@@ -1624,6 +1647,6 @@ function DashboardFilters({ layout, formatters = {}, gap = 2 }) {
|
|
|
1624
1647
|
});
|
|
1625
1648
|
}
|
|
1626
1649
|
//#endregion
|
|
1627
|
-
export { BarChart, ComboChart, DashboardCanvas, DashboardCharts, DashboardFilters, DashboardProvider, DataTable, DonutChart, FilterSelect, INSPECT_ATTRIBUTE, KpiCard, Legend, ModelDictionary, OTHER_COLOR, SERIES_COLORS, TooltipBox, chartCardSx, chartPlaceholder, chartTitleSx, fieldLabel, formatCurrency, formatMeasure, formatNumber, measureFormat, measureFormatter, numberOf, seriesColor, useClickFilter, useDashboard, useModelQuery };
|
|
1650
|
+
export { BUILTIN_FORMATTERS, BarChart, ComboChart, DashboardCanvas, DashboardCharts, DashboardFilters, DashboardProvider, DataTable, DonutChart, FilterSelect, INSPECT_ATTRIBUTE, KpiCard, Legend, ModelDictionary, OTHER_COLOR, SERIES_COLORS, TooltipBox, chartCardSx, chartPlaceholder, chartTitleSx, fieldLabel, formatCurrency, formatMeasure, formatNumber, measureFormat, measureFormatter, numberOf, seriesColor, toDate, toMonthName, useClickFilter, useDashboard, useModelQuery };
|
|
1628
1651
|
|
|
1629
1652
|
//# sourceMappingURL=index.js.map
|