@bluemarble/bi 0.2.1 → 0.3.1
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-5wh3brAT.js → DashboardDevtools-C4E9afPT.js} +792 -303
- package/dist/DashboardDevtools-C4E9afPT.js.map +1 -0
- package/dist/{ModelDictionary-DN77UDVM.js → ModelDictionary-kUKcdbc8.js} +3 -3
- package/dist/{ModelDictionary-DN77UDVM.js.map → ModelDictionary-kUKcdbc8.js.map} +1 -1
- package/dist/client/index.d.ts +10 -2
- package/dist/client/index.js +55 -28
- package/dist/client/index.js.map +1 -1
- package/dist/{layout-DvHS8XFz.js → layout-DTR4vI-I.js} +120 -5
- package/dist/layout-DTR4vI-I.js.map +1 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +18 -2
- package/dist/server/index.js.map +1 -1
- package/dist/{types-BO_AQNxv.d.ts → types-DUJS2Zmx.d.ts} +39 -3
- package/dist/types.d.ts +2 -2
- package/package.json +2 -1
- package/dist/DashboardDevtools-5wh3brAT.js.map +0 -1
- package/dist/layout-DvHS8XFz.js.map +0 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as modelEdges, C as VALUE_FORMATS, D as validateModel, N as referencedTables, S as AGGREGATION_LABEL, T as isAggregate, _ as canonicalJson, a as FILTER_FIELDS, b as toModelCode, c as chartOfType, d as filterReferences, g as validateLayout, i as CARD_FIELDS, j as parseColumnRef, k as measureHome, l as chartReferences, m as layoutModelWarnings, n as CANVAS_PRESETS, p as layoutFileContent, r as CARDS_FIELDS, s as chartColumns, t as BUILTIN_CHART_TYPES, x as AGGREGATIONS } from "./layout-DTR4vI-I.js";
|
|
2
2
|
import { n as diffRows } from "./compare-DloZhg1T.js";
|
|
3
|
-
import { a as useDashboard, c as layoutItemKey, l as parseLayoutItemKey, n as INSPECT_ATTRIBUTE, o as errorMessage, s as LAYOUT_ITEM_ATTRIBUTE, t as ModelDictionary } from "./ModelDictionary-
|
|
4
|
-
import { Alert, Autocomplete, Box, Button, Checkbox, Chip, CircularProgress, Collapse, Dialog, DialogActions, DialogContent, DialogTitle, Divider, FormControlLabel, IconButton, List, ListItemButton, Menu, MenuItem, MenuList, Paper, Popper, Portal, Tab, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Tabs, TextField, ThemeProvider, ToggleButton, ToggleButtonGroup, Tooltip, Typography, createTheme } from "@mui/material";
|
|
3
|
+
import { a as useDashboard, c as layoutItemKey, l as parseLayoutItemKey, n as INSPECT_ATTRIBUTE, o as errorMessage, s as LAYOUT_ITEM_ATTRIBUTE, t as ModelDictionary } from "./ModelDictionary-kUKcdbc8.js";
|
|
4
|
+
import { Alert, Autocomplete, Box, Button, Checkbox, Chip, CircularProgress, Collapse, Dialog, DialogActions, DialogContent, DialogTitle, Divider, FormControlLabel, IconButton, List, ListItemButton, Menu, MenuItem, MenuList, Paper, Popper, Portal, Tab, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TableSortLabel, Tabs, TextField, ThemeProvider, ToggleButton, ToggleButtonGroup, Tooltip, Typography, createTheme } from "@mui/material";
|
|
5
5
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { createPortal } from "react-dom";
|
|
7
8
|
//#region src/devtools/sqlHighlight.tsx
|
|
8
9
|
const KEYWORDS$1 = new Set(`SELECT FROM WHERE AND OR NOT IN IS NULL JOIN LEFT RIGHT INNER OUTER CROSS ON AS GROUP BY ORDER
|
|
9
10
|
HAVING LIMIT OFFSET UNION ALL DISTINCT CASE WHEN THEN ELSE END WITH ASC DESC BETWEEN LIKE EXISTS
|
|
@@ -91,9 +92,13 @@ function StatusDot({ status }) {
|
|
|
91
92
|
}
|
|
92
93
|
});
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Copia para a área de transferência. Com o devtools em janela separada, o clipboard tem que ser o
|
|
97
|
+
* dela (o da página não está em foco e recusa): passe o elemento clicado.
|
|
98
|
+
*/
|
|
99
|
+
async function copyText(text, from) {
|
|
95
100
|
try {
|
|
96
|
-
await navigator.clipboard.writeText(text);
|
|
101
|
+
await (from?.ownerDocument.defaultView ?? window).navigator.clipboard.writeText(text);
|
|
97
102
|
return true;
|
|
98
103
|
} catch {
|
|
99
104
|
return false;
|
|
@@ -103,8 +108,8 @@ function CopyButton({ text, label = "Copiar" }) {
|
|
|
103
108
|
const [copied, setCopied] = useState(false);
|
|
104
109
|
return /* @__PURE__ */ jsx(Button, {
|
|
105
110
|
size: "small",
|
|
106
|
-
onClick: async () => {
|
|
107
|
-
if (await copyText(text)) {
|
|
111
|
+
onClick: async (event) => {
|
|
112
|
+
if (await copyText(text, event.currentTarget)) {
|
|
108
113
|
setCopied(true);
|
|
109
114
|
setTimeout(() => setCopied(false), 1200);
|
|
110
115
|
}
|
|
@@ -112,9 +117,33 @@ function CopyButton({ text, label = "Copiar" }) {
|
|
|
112
117
|
children: copied ? "Copiado!" : label
|
|
113
118
|
});
|
|
114
119
|
}
|
|
115
|
-
|
|
120
|
+
const COLLAPSED_KEY = "bm-dashboard-devtools:collapsed";
|
|
121
|
+
function loadCollapsed() {
|
|
122
|
+
try {
|
|
123
|
+
const saved = JSON.parse(localStorage.getItem(COLLAPSED_KEY) ?? "[]");
|
|
124
|
+
return Array.isArray(saved) ? saved : [];
|
|
125
|
+
} catch {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/** Seção recolhida (lembrada entre recargas, pela chave). */
|
|
130
|
+
function useCollapsed(key) {
|
|
131
|
+
const [collapsed, setCollapsed] = useState(() => !!key && loadCollapsed().includes(key));
|
|
132
|
+
const toggle = () => {
|
|
133
|
+
if (!key) return;
|
|
134
|
+
const next = !collapsed;
|
|
135
|
+
setCollapsed(next);
|
|
136
|
+
try {
|
|
137
|
+
const others = loadCollapsed().filter((item) => item !== key);
|
|
138
|
+
localStorage.setItem(COLLAPSED_KEY, JSON.stringify(next ? [...others, key] : others));
|
|
139
|
+
} catch {}
|
|
140
|
+
};
|
|
141
|
+
return [collapsed, toggle];
|
|
142
|
+
}
|
|
143
|
+
function Section({ title, actions, collapseKey, children }) {
|
|
144
|
+
const [collapsed, toggle] = useCollapsed(collapseKey);
|
|
116
145
|
return /* @__PURE__ */ jsxs(Box, {
|
|
117
|
-
sx: { mb: 2 },
|
|
146
|
+
sx: { mb: collapsed ? .5 : 2 },
|
|
118
147
|
children: [/* @__PURE__ */ jsxs(Box, {
|
|
119
148
|
sx: {
|
|
120
149
|
display: "flex",
|
|
@@ -123,15 +152,43 @@ function Section({ title, actions, children }) {
|
|
|
123
152
|
mb: .5,
|
|
124
153
|
minHeight: 30
|
|
125
154
|
},
|
|
126
|
-
children: [/* @__PURE__ */
|
|
155
|
+
children: [/* @__PURE__ */ jsxs(Typography, {
|
|
127
156
|
variant: "overline",
|
|
157
|
+
component: collapseKey ? "button" : "span",
|
|
158
|
+
...collapseKey && {
|
|
159
|
+
type: "button",
|
|
160
|
+
onClick: toggle,
|
|
161
|
+
"aria-expanded": !collapsed,
|
|
162
|
+
title: collapsed ? "Expandir" : "Recolher"
|
|
163
|
+
},
|
|
128
164
|
sx: {
|
|
129
165
|
color: "text.secondary",
|
|
130
166
|
lineHeight: 1.5,
|
|
131
|
-
fontWeight: 700
|
|
167
|
+
fontWeight: 700,
|
|
168
|
+
...collapseKey && {
|
|
169
|
+
display: "flex",
|
|
170
|
+
alignItems: "center",
|
|
171
|
+
gap: .5,
|
|
172
|
+
p: 0,
|
|
173
|
+
border: 0,
|
|
174
|
+
bgcolor: "transparent",
|
|
175
|
+
cursor: "pointer",
|
|
176
|
+
textAlign: "left",
|
|
177
|
+
"&:hover": { color: "text.primary" }
|
|
178
|
+
}
|
|
132
179
|
},
|
|
133
|
-
children:
|
|
134
|
-
|
|
180
|
+
children: [collapseKey && /* @__PURE__ */ jsx(Box, {
|
|
181
|
+
component: "span",
|
|
182
|
+
"aria-hidden": true,
|
|
183
|
+
sx: {
|
|
184
|
+
display: "inline-block",
|
|
185
|
+
fontSize: 9,
|
|
186
|
+
transition: "transform .15s",
|
|
187
|
+
transform: collapsed ? "rotate(-90deg)" : "none"
|
|
188
|
+
},
|
|
189
|
+
children: "▼"
|
|
190
|
+
}), title]
|
|
191
|
+
}), !collapsed && /* @__PURE__ */ jsx(Box, {
|
|
135
192
|
sx: {
|
|
136
193
|
ml: "auto",
|
|
137
194
|
display: "flex",
|
|
@@ -139,7 +196,7 @@ function Section({ title, actions, children }) {
|
|
|
139
196
|
},
|
|
140
197
|
children: actions
|
|
141
198
|
})]
|
|
142
|
-
}), children]
|
|
199
|
+
}), !collapsed && children]
|
|
143
200
|
});
|
|
144
201
|
}
|
|
145
202
|
function CodeBlock({ code, maxHeight = 260, sql = false }) {
|
|
@@ -181,7 +238,16 @@ function inlineParams(sql, params) {
|
|
|
181
238
|
});
|
|
182
239
|
}
|
|
183
240
|
const MAX_ROWS = 500;
|
|
241
|
+
/** Ordena linhas de resultado por uma coluna; `null` sempre no fim. */
|
|
242
|
+
function compareCells(a, b, { column, direction }) {
|
|
243
|
+
const [x, y] = [a[column], b[column]];
|
|
244
|
+
if (x === null || x === void 0) return y === null || y === void 0 ? 0 : 1;
|
|
245
|
+
if (y === null || y === void 0) return -1;
|
|
246
|
+
const order = typeof x === "number" && typeof y === "number" ? x - y : String(x).localeCompare(String(y), "pt-BR", { numeric: true });
|
|
247
|
+
return direction === "asc" ? order : -order;
|
|
248
|
+
}
|
|
184
249
|
function ResultTable({ rows, maxHeight = 320 }) {
|
|
250
|
+
const [sort, setSort] = useState(null);
|
|
185
251
|
if (!rows.length) return /* @__PURE__ */ jsx(Typography, {
|
|
186
252
|
sx: {
|
|
187
253
|
color: "text.secondary",
|
|
@@ -190,6 +256,14 @@ function ResultTable({ rows, maxHeight = 320 }) {
|
|
|
190
256
|
children: "Nenhuma linha."
|
|
191
257
|
});
|
|
192
258
|
const columns = Object.keys(rows[0]);
|
|
259
|
+
const sorted = sort ? [...rows].sort((a, b) => compareCells(a, b, sort)) : rows;
|
|
260
|
+
const toggleSort = (column) => setSort((current) => current?.column !== column ? {
|
|
261
|
+
column,
|
|
262
|
+
direction: "asc"
|
|
263
|
+
} : current.direction === "asc" ? {
|
|
264
|
+
column,
|
|
265
|
+
direction: "desc"
|
|
266
|
+
} : null);
|
|
193
267
|
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(TableContainer, {
|
|
194
268
|
sx: {
|
|
195
269
|
maxHeight,
|
|
@@ -201,12 +275,19 @@ function ResultTable({ rows, maxHeight = 320 }) {
|
|
|
201
275
|
size: "small",
|
|
202
276
|
stickyHeader: true,
|
|
203
277
|
children: [/* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsx(TableRow, { children: columns.map((column) => /* @__PURE__ */ jsx(TableCell, {
|
|
278
|
+
sortDirection: sort?.column === column ? sort.direction : false,
|
|
204
279
|
sx: {
|
|
205
280
|
fontFamily: MONO,
|
|
206
|
-
fontWeight: 700
|
|
281
|
+
fontWeight: 700,
|
|
282
|
+
whiteSpace: "nowrap"
|
|
207
283
|
},
|
|
208
|
-
children:
|
|
209
|
-
|
|
284
|
+
children: /* @__PURE__ */ jsx(TableSortLabel, {
|
|
285
|
+
active: sort?.column === column,
|
|
286
|
+
direction: sort?.column === column ? sort.direction : "asc",
|
|
287
|
+
onClick: () => toggleSort(column),
|
|
288
|
+
children: column
|
|
289
|
+
})
|
|
290
|
+
}, column)) }) }), /* @__PURE__ */ jsx(TableBody, { children: sorted.slice(0, MAX_ROWS).map((row, index) => /* @__PURE__ */ jsx(TableRow, {
|
|
210
291
|
hover: true,
|
|
211
292
|
children: columns.map((column) => /* @__PURE__ */ jsx(TableCell, {
|
|
212
293
|
align: typeof row[column] === "number" ? "right" : "left",
|
|
@@ -508,6 +589,126 @@ function FiltersPanel({ devtools }) {
|
|
|
508
589
|
});
|
|
509
590
|
}
|
|
510
591
|
//#endregion
|
|
592
|
+
//#region src/devtools/DevtoolsWindow.tsx
|
|
593
|
+
/**
|
|
594
|
+
* Devtools numa janela separada do navegador (popup), para usar com a página em tela cheia ou em
|
|
595
|
+
* outro monitor. O painel continua sendo renderizado pela árvore React da página (mesmo contexto,
|
|
596
|
+
* mesmo store), só que via portal no `document` do popup:
|
|
597
|
+
*
|
|
598
|
+
* - os estilos do emotion/MUI vão para o `<head>` da página; aqui eles são espelhados no popup;
|
|
599
|
+
* - menus, tooltips e diálogos do MUI montam no `body` da página por padrão: o tema da janela
|
|
600
|
+
* aponta o `container` deles para o popup.
|
|
601
|
+
*
|
|
602
|
+
* Recarregar a página derruba o vínculo (o popup é de outra árvore): o popup avisa e o botão do
|
|
603
|
+
* devtools na página reconecta (abrir popup exige um clique, o navegador bloqueia sem gesto).
|
|
604
|
+
*/
|
|
605
|
+
const WINDOW_NAME = "bm-bi-devtools";
|
|
606
|
+
/** Abre (ou reaproveita, se ainda estiver aberta) a janela do devtools. `null` = bloqueada. */
|
|
607
|
+
function openDevtoolsWindow() {
|
|
608
|
+
const width = Math.min(1100, window.screen.availWidth);
|
|
609
|
+
const height = Math.min(720, window.screen.availHeight);
|
|
610
|
+
return window.open("", WINDOW_NAME, `popup,width=${width},height=${height}`);
|
|
611
|
+
}
|
|
612
|
+
const isStyleNode = (node) => node instanceof HTMLStyleElement || node instanceof HTMLLinkElement && node.rel === "stylesheet";
|
|
613
|
+
/** Cópia de um `<style>`/`<link>`; estilo inserido por `insertRule` (sem texto) vai pelas regras. */
|
|
614
|
+
function cloneStyle(node, target) {
|
|
615
|
+
const clone = target.importNode(node, true);
|
|
616
|
+
if (node instanceof HTMLStyleElement && !node.textContent && node.sheet?.cssRules.length) clone.textContent = [...node.sheet.cssRules].map((rule) => rule.cssText).join("\n");
|
|
617
|
+
return clone;
|
|
618
|
+
}
|
|
619
|
+
/** Copia os estilos da página para o popup e acompanha os que entrarem/saírem depois. */
|
|
620
|
+
function mirrorStyles(source, target) {
|
|
621
|
+
const clones = /* @__PURE__ */ new Map();
|
|
622
|
+
const add = (node) => {
|
|
623
|
+
if (!isStyleNode(node) || clones.has(node)) return;
|
|
624
|
+
const clone = cloneStyle(node, target);
|
|
625
|
+
clones.set(node, clone);
|
|
626
|
+
target.head.appendChild(clone);
|
|
627
|
+
};
|
|
628
|
+
for (const node of source.head.querySelectorAll("style, link[rel=\"stylesheet\"]")) add(node);
|
|
629
|
+
const observer = new MutationObserver((mutations) => {
|
|
630
|
+
for (const mutation of mutations) {
|
|
631
|
+
if (mutation.type === "characterData" || mutation.target !== source.head) {
|
|
632
|
+
const style = (mutation.target instanceof Element ? mutation.target : mutation.target.parentElement)?.closest("style");
|
|
633
|
+
const clone = style && clones.get(style);
|
|
634
|
+
if (style && clone) {
|
|
635
|
+
const next = cloneStyle(style, target);
|
|
636
|
+
target.head.replaceChild(next, clone);
|
|
637
|
+
clones.set(style, next);
|
|
638
|
+
}
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
for (const node of mutation.addedNodes) add(node);
|
|
642
|
+
for (const node of mutation.removedNodes) {
|
|
643
|
+
clones.get(node)?.parentNode?.removeChild(clones.get(node));
|
|
644
|
+
clones.delete(node);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
observer.observe(source.head, {
|
|
649
|
+
childList: true,
|
|
650
|
+
subtree: true,
|
|
651
|
+
characterData: true
|
|
652
|
+
});
|
|
653
|
+
return () => observer.disconnect();
|
|
654
|
+
}
|
|
655
|
+
/** Tema com os portais do MUI (menus, tooltips, diálogos) dentro do popup. */
|
|
656
|
+
function windowTheme(base, container) {
|
|
657
|
+
return createTheme(base, { components: {
|
|
658
|
+
MuiModal: { defaultProps: { container } },
|
|
659
|
+
MuiPopover: { defaultProps: { container } },
|
|
660
|
+
MuiPopper: { defaultProps: { container } },
|
|
661
|
+
MuiDialog: { defaultProps: { container } },
|
|
662
|
+
MuiMenu: { defaultProps: { container } }
|
|
663
|
+
} });
|
|
664
|
+
}
|
|
665
|
+
function DevtoolsWindow({ popup, title, theme, onClosed, children }) {
|
|
666
|
+
const [root, setRoot] = useState(null);
|
|
667
|
+
const onClosedRef = useRef(onClosed);
|
|
668
|
+
onClosedRef.current = onClosed;
|
|
669
|
+
useEffect(() => {
|
|
670
|
+
const doc = popup.document;
|
|
671
|
+
doc.title = title;
|
|
672
|
+
doc.head.replaceChildren();
|
|
673
|
+
doc.body.replaceChildren();
|
|
674
|
+
const meta = doc.createElement("meta");
|
|
675
|
+
meta.setAttribute("charset", "utf-8");
|
|
676
|
+
doc.head.appendChild(meta);
|
|
677
|
+
const base = doc.createElement("style");
|
|
678
|
+
base.textContent = `html, body { height: 100%; margin: 0; background: ${theme.palette.background.paper}; color-scheme: dark; } body { font-family: ${theme.typography.fontFamily}; } #${WINDOW_NAME} { height: 100%; display: flex; flex-direction: column; }`;
|
|
679
|
+
doc.head.appendChild(base);
|
|
680
|
+
const container = doc.createElement("div");
|
|
681
|
+
container.id = WINDOW_NAME;
|
|
682
|
+
doc.body.appendChild(container);
|
|
683
|
+
const stopMirror = mirrorStyles(document, doc);
|
|
684
|
+
setRoot(container);
|
|
685
|
+
const poll = setInterval(() => {
|
|
686
|
+
if (popup.closed) onClosedRef.current();
|
|
687
|
+
}, 400);
|
|
688
|
+
const onPageHide = () => {
|
|
689
|
+
stopMirror();
|
|
690
|
+
doc.body.innerHTML = `<p style="font: 14px sans-serif; color: #ccc; padding: 24px">A página do dashboard recarregou. Clique no botão <b style="color: #F58220">◆ BI</b> da página para reconectar o devtools a esta janela.</p>`;
|
|
691
|
+
};
|
|
692
|
+
window.addEventListener("pagehide", onPageHide);
|
|
693
|
+
return () => {
|
|
694
|
+
clearInterval(poll);
|
|
695
|
+
window.removeEventListener("pagehide", onPageHide);
|
|
696
|
+
stopMirror();
|
|
697
|
+
setRoot(null);
|
|
698
|
+
};
|
|
699
|
+
}, [
|
|
700
|
+
popup,
|
|
701
|
+
title,
|
|
702
|
+
theme
|
|
703
|
+
]);
|
|
704
|
+
const localTheme = useMemo(() => root && windowTheme(theme, root), [theme, root]);
|
|
705
|
+
if (!root || !localTheme) return null;
|
|
706
|
+
return createPortal(/* @__PURE__ */ jsx(ThemeProvider, {
|
|
707
|
+
theme: localTheme,
|
|
708
|
+
children
|
|
709
|
+
}), root);
|
|
710
|
+
}
|
|
711
|
+
//#endregion
|
|
511
712
|
//#region src/devtools/Inspector.tsx
|
|
512
713
|
/**
|
|
513
714
|
* Liga componente da página <-> consulta do devtools. Os componentes marcam o elemento raiz com
|
|
@@ -676,7 +877,7 @@ function useWidth() {
|
|
|
676
877
|
const [width, setWidth] = useState(Number.POSITIVE_INFINITY);
|
|
677
878
|
useEffect(() => {
|
|
678
879
|
if (!element) return;
|
|
679
|
-
const observer = new ResizeObserver(([entry]) => entry && setWidth(entry.contentRect.width));
|
|
880
|
+
const observer = new ((element.ownerDocument.defaultView?.ResizeObserver) ?? ResizeObserver)(([entry]) => entry && setWidth(entry.contentRect.width));
|
|
680
881
|
observer.observe(element);
|
|
681
882
|
return () => observer.disconnect();
|
|
682
883
|
}, [element]);
|
|
@@ -696,6 +897,18 @@ const uniqueId = (charts, base) => {
|
|
|
696
897
|
while (ids.has(`${base}-${n}`)) n++;
|
|
697
898
|
return `${base}-${n}`;
|
|
698
899
|
};
|
|
900
|
+
/** Objeto sem nenhum campo sai do JSON. */
|
|
901
|
+
const nonEmpty = (object) => Object.keys(object).length ? object : void 0;
|
|
902
|
+
/** Campos do card de um gráfico, com o padrão do layout (`cards`) no lugar do padrão da lib. */
|
|
903
|
+
const chartCardFields = (cards) => Object.fromEntries(Object.entries(CARD_FIELDS).map(([key, spec]) => [key, {
|
|
904
|
+
...spec,
|
|
905
|
+
default: cards?.[key] ?? spec.default
|
|
906
|
+
}]));
|
|
907
|
+
const cardsSummary = (cards) => {
|
|
908
|
+
const { gap, ...style } = cards ?? {};
|
|
909
|
+
const custom = Object.keys(style).length;
|
|
910
|
+
return `espaço ${String(gap ?? CARDS_FIELDS.gap?.default)}px · ${custom ? `${custom} ajuste(s) de estilo` : "estilo padrão"}`;
|
|
911
|
+
};
|
|
699
912
|
/** Campo opcional vazio sai do objeto, para o JSON ficar só com o que foi definido. */
|
|
700
913
|
function withField(object, key, value) {
|
|
701
914
|
const next = { ...object };
|
|
@@ -809,7 +1022,7 @@ function LayoutPanel({ devtools, model, capabilities, picked, onHover }) {
|
|
|
809
1022
|
const charts = editing.charts;
|
|
810
1023
|
const filters = editing.filters ?? [];
|
|
811
1024
|
const selectedFilter = selection?.kind === "filter" ? filters.find((filter) => filter.dimension === selection.key) ?? null : null;
|
|
812
|
-
const selected = selection?.kind === "filter" || selection?.kind === "canvas" ? null : charts.find((chart) => chart.id === selection?.key) ?? charts[0] ?? null;
|
|
1025
|
+
const selected = selection?.kind === "filter" || selection?.kind === "canvas" || selection?.kind === "cards" ? null : charts.find((chart) => chart.id === selection?.key) ?? charts[0] ?? null;
|
|
813
1026
|
const layoutKey = entry.registered.id;
|
|
814
1027
|
const differsFromFile = canonicalJson(editing) !== canonicalJson(entry.registered);
|
|
815
1028
|
const update = (next) => {
|
|
@@ -826,6 +1039,7 @@ function LayoutPanel({ devtools, model, capabilities, picked, onHover }) {
|
|
|
826
1039
|
});
|
|
827
1040
|
const patchChart = (id, change) => setCharts(charts.map((chart) => chart.id === id ? change(chart) : chart));
|
|
828
1041
|
const setCanvas = (next) => update(withField(editing, "canvas", next));
|
|
1042
|
+
const setCards = (key, value) => update(withField(editing, "cards", nonEmpty(withField(editing.cards ?? {}, key, value))));
|
|
829
1043
|
const setFilters = (next) => update({
|
|
830
1044
|
...editing,
|
|
831
1045
|
filters: next
|
|
@@ -884,7 +1098,7 @@ function LayoutPanel({ devtools, model, capabilities, picked, onHover }) {
|
|
|
884
1098
|
};
|
|
885
1099
|
const stacked = rootWidth < STACK_WIDTH;
|
|
886
1100
|
const formColumns = editorWidth < 420 ? 1 : editorWidth < 720 ? 2 : 4;
|
|
887
|
-
const selectedTitle = selection?.kind === "canvas" ? "Tela" : selectedFilter ? selectedFilter.label || model.dimensions[selectedFilter.dimension]?.label || selectedFilter.dimension : selected ? selected.title || selected.id : "";
|
|
1101
|
+
const selectedTitle = selection?.kind === "canvas" ? "Tela" : selection?.kind === "cards" ? "Cards" : selectedFilter ? selectedFilter.label || model.dimensions[selectedFilter.dimension]?.label || selectedFilter.dimension : selected ? selected.title || selected.id : "";
|
|
888
1102
|
const hover = (item) => onHover(item && itemQueryId(layoutKey, item));
|
|
889
1103
|
const add = (type, base) => {
|
|
890
1104
|
setAddMenu(null);
|
|
@@ -992,9 +1206,10 @@ function LayoutPanel({ devtools, model, capabilities, picked, onHover }) {
|
|
|
992
1206
|
children: [item.registered.id, item.draft ? " · rascunho" : ""]
|
|
993
1207
|
}, item.registered.id))
|
|
994
1208
|
}),
|
|
995
|
-
/* @__PURE__ */
|
|
1209
|
+
/* @__PURE__ */ jsxs(Section, {
|
|
996
1210
|
title: "Tela",
|
|
997
|
-
|
|
1211
|
+
collapseKey: "layout:lista:tela",
|
|
1212
|
+
children: [/* @__PURE__ */ jsx(ItemRow, {
|
|
998
1213
|
title: editing.canvas ? "Tamanho fixo" : "Responsiva",
|
|
999
1214
|
subtitle: editing.canvas ? presetLabel(editing.canvas) : "reorganiza conforme a largura",
|
|
1000
1215
|
selected: selection?.kind === "canvas",
|
|
@@ -1002,10 +1217,19 @@ function LayoutPanel({ devtools, model, capabilities, picked, onHover }) {
|
|
|
1002
1217
|
setSelection({ kind: "canvas" });
|
|
1003
1218
|
if (stacked) setSidebar(false);
|
|
1004
1219
|
}
|
|
1005
|
-
})
|
|
1220
|
+
}), /* @__PURE__ */ jsx(ItemRow, {
|
|
1221
|
+
title: "Cards",
|
|
1222
|
+
subtitle: cardsSummary(editing.cards),
|
|
1223
|
+
selected: selection?.kind === "cards",
|
|
1224
|
+
onSelect: () => {
|
|
1225
|
+
setSelection({ kind: "cards" });
|
|
1226
|
+
if (stacked) setSidebar(false);
|
|
1227
|
+
}
|
|
1228
|
+
})]
|
|
1006
1229
|
}),
|
|
1007
1230
|
/* @__PURE__ */ jsxs(Section, {
|
|
1008
1231
|
title: `Filtros (${filters.length})`,
|
|
1232
|
+
collapseKey: "layout:lista:filtros",
|
|
1009
1233
|
actions: /* @__PURE__ */ jsx(Button, {
|
|
1010
1234
|
size: "small",
|
|
1011
1235
|
onClick: addFilter,
|
|
@@ -1037,6 +1261,7 @@ function LayoutPanel({ devtools, model, capabilities, picked, onHover }) {
|
|
|
1037
1261
|
}),
|
|
1038
1262
|
/* @__PURE__ */ jsx(Section, {
|
|
1039
1263
|
title: `Gráficos (${charts.length})`,
|
|
1264
|
+
collapseKey: "layout:lista:graficos",
|
|
1040
1265
|
actions: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Button, {
|
|
1041
1266
|
size: "small",
|
|
1042
1267
|
onClick: (e) => setAddMenu(e.currentTarget),
|
|
@@ -1113,7 +1338,23 @@ function LayoutPanel({ devtools, model, capabilities, picked, onHover }) {
|
|
|
1113
1338
|
canvas: editing.canvas,
|
|
1114
1339
|
columns: formColumns,
|
|
1115
1340
|
onChange: setCanvas
|
|
1116
|
-
}) :
|
|
1341
|
+
}) : selection?.kind === "cards" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Typography, {
|
|
1342
|
+
sx: {
|
|
1343
|
+
fontSize: 12,
|
|
1344
|
+
color: "text.secondary",
|
|
1345
|
+
mb: 1
|
|
1346
|
+
},
|
|
1347
|
+
children: "Estilo padrão dos cards de todos os gráficos; cada gráfico pode sobrepor na seção Card dele. Vazio = padrão do componente."
|
|
1348
|
+
}), /* @__PURE__ */ jsx(FieldsForm, {
|
|
1349
|
+
fields: CARDS_FIELDS,
|
|
1350
|
+
values: editing.cards ?? {},
|
|
1351
|
+
onSet: setCards,
|
|
1352
|
+
model,
|
|
1353
|
+
formatters: [],
|
|
1354
|
+
columns: [],
|
|
1355
|
+
formColumns,
|
|
1356
|
+
chosenDimensions: []
|
|
1357
|
+
})] }) : selectedFilter ? /* @__PURE__ */ jsx(FilterForm, {
|
|
1117
1358
|
filter: selectedFilter,
|
|
1118
1359
|
model,
|
|
1119
1360
|
formatters: entry.parts.filters ?? [],
|
|
@@ -1127,6 +1368,7 @@ function LayoutPanel({ devtools, model, capabilities, picked, onHover }) {
|
|
|
1127
1368
|
}
|
|
1128
1369
|
}, `filter:${selectedFilter.dimension}`) : selected ? /* @__PURE__ */ jsx(ChartForm, {
|
|
1129
1370
|
chart: selected,
|
|
1371
|
+
cards: editing.cards,
|
|
1130
1372
|
types: chartTypes,
|
|
1131
1373
|
model,
|
|
1132
1374
|
formatters: entry.parts.charts ?? [],
|
|
@@ -1234,6 +1476,7 @@ function CanvasForm({ canvas, columns, onChange }) {
|
|
|
1234
1476
|
const number = (value) => value === "" ? void 0 : Number(value);
|
|
1235
1477
|
return /* @__PURE__ */ jsx(Section, {
|
|
1236
1478
|
title: "Tela",
|
|
1479
|
+
collapseKey: "layout:form:Tela",
|
|
1237
1480
|
children: /* @__PURE__ */ jsxs(Box, {
|
|
1238
1481
|
sx: formGrid(columns),
|
|
1239
1482
|
children: [/* @__PURE__ */ jsx(Box, {
|
|
@@ -1401,9 +1644,10 @@ function SmallButton({ title, disabled, onClick, children }) {
|
|
|
1401
1644
|
}) })
|
|
1402
1645
|
});
|
|
1403
1646
|
}
|
|
1404
|
-
function ChartForm({ chart, types, model, formatters, columns, onChange }) {
|
|
1647
|
+
function ChartForm({ chart, cards, types, model, formatters, columns, onChange }) {
|
|
1405
1648
|
const set = (key, value) => onChange((current) => withField(current, key, value));
|
|
1406
|
-
|
|
1649
|
+
const setCard = (key, value) => onChange((current) => withField(current, "card", nonEmpty(withField(current.card ?? {}, key, value))));
|
|
1650
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(FieldsForm, {
|
|
1407
1651
|
fields: types[chart.type]?.fields ?? {},
|
|
1408
1652
|
values: chart,
|
|
1409
1653
|
onSet: set,
|
|
@@ -1435,7 +1679,16 @@ function ChartForm({ chart, types, model, formatters, columns, onChange }) {
|
|
|
1435
1679
|
onChange: (e) => set("id", e.target.value),
|
|
1436
1680
|
sx: inputSx$1
|
|
1437
1681
|
}), "Identificador no JSON (letras, números, \"-\" e \"_\")")] })
|
|
1438
|
-
})
|
|
1682
|
+
}), /* @__PURE__ */ jsx(FieldsForm, {
|
|
1683
|
+
fields: chartCardFields(cards),
|
|
1684
|
+
values: chart.card ?? {},
|
|
1685
|
+
onSet: setCard,
|
|
1686
|
+
model,
|
|
1687
|
+
formatters: [],
|
|
1688
|
+
columns: [],
|
|
1689
|
+
formColumns: columns,
|
|
1690
|
+
chosenDimensions: []
|
|
1691
|
+
})] });
|
|
1439
1692
|
}
|
|
1440
1693
|
function FilterForm({ filter, model, formatters, columns, onChange }) {
|
|
1441
1694
|
return /* @__PURE__ */ jsx(FieldsForm, {
|
|
@@ -1625,6 +1878,7 @@ function FieldsForm({ fields, values, onSet: set, model, formatters, columns, fo
|
|
|
1625
1878
|
const inSection = entries.filter(([, spec]) => spec.section === section);
|
|
1626
1879
|
return /* @__PURE__ */ jsx(Section, {
|
|
1627
1880
|
title: section,
|
|
1881
|
+
collapseKey: `layout:form:${section}`,
|
|
1628
1882
|
children: /* @__PURE__ */ jsxs(Box, {
|
|
1629
1883
|
sx: formGrid(formColumns),
|
|
1630
1884
|
children: [section === sections[0] && header, inSection.map(([key, spec]) => /* @__PURE__ */ jsx(Box, {
|
|
@@ -2705,6 +2959,7 @@ function track(event, onMove, onUp) {
|
|
|
2705
2959
|
x: event.clientX,
|
|
2706
2960
|
y: event.clientY
|
|
2707
2961
|
};
|
|
2962
|
+
const win = event.currentTarget.ownerDocument.defaultView ?? window;
|
|
2708
2963
|
let moved = false;
|
|
2709
2964
|
const move = (moveEvent) => {
|
|
2710
2965
|
const dx = moveEvent.clientX - start.x;
|
|
@@ -2713,12 +2968,12 @@ function track(event, onMove, onUp) {
|
|
|
2713
2968
|
if (moved) onMove(dx, dy, moveEvent);
|
|
2714
2969
|
};
|
|
2715
2970
|
const up = (upEvent) => {
|
|
2716
|
-
|
|
2717
|
-
|
|
2971
|
+
win.removeEventListener("mousemove", move);
|
|
2972
|
+
win.removeEventListener("mouseup", up);
|
|
2718
2973
|
onUp?.(upEvent, moved);
|
|
2719
2974
|
};
|
|
2720
|
-
|
|
2721
|
-
|
|
2975
|
+
win.addEventListener("mousemove", move);
|
|
2976
|
+
win.addEventListener("mouseup", up);
|
|
2722
2977
|
}
|
|
2723
2978
|
/** Colunas citadas como `tabela.coluna` no SQL das dimensões/medidas. */
|
|
2724
2979
|
function citedColumns(sql, table) {
|
|
@@ -3060,7 +3315,7 @@ function ModelDiagram({ editor, setEditor, columns, errors, warnings, loadingCol
|
|
|
3060
3315
|
selectNode(node);
|
|
3061
3316
|
return;
|
|
3062
3317
|
}
|
|
3063
|
-
const target = document.elementFromPoint(upEvent.clientX, upEvent.clientY)?.closest("[data-diagram-column]");
|
|
3318
|
+
const target = (canvas.current?.ownerDocument ?? document).elementFromPoint(upEvent.clientX, upEvent.clientY)?.closest("[data-diagram-column]");
|
|
3064
3319
|
const targetNode = nodes.find((item) => item.id === target?.dataset.diagramNode);
|
|
3065
3320
|
const targetColumn = target?.dataset.diagramColumn;
|
|
3066
3321
|
if (targetNode && targetColumn) connect(node, column, targetNode, targetColumn);
|
|
@@ -4587,7 +4842,7 @@ function ModelPanel({ devtools, original, capabilities, tables, onTry, onSaved }
|
|
|
4587
4842
|
}),
|
|
4588
4843
|
/* @__PURE__ */ jsx(Button, {
|
|
4589
4844
|
size: "small",
|
|
4590
|
-
onClick: () => copyText(toModelCode(edited)),
|
|
4845
|
+
onClick: (e) => copyText(toModelCode(edited), e.currentTarget),
|
|
4591
4846
|
children: "Copiar TS"
|
|
4592
4847
|
}),
|
|
4593
4848
|
/* @__PURE__ */ jsx(Button, {
|
|
@@ -4850,12 +5105,55 @@ function PlaygroundPanel({ model, selection, onSelectionChange }) {
|
|
|
4850
5105
|
//#endregion
|
|
4851
5106
|
//#region src/devtools/QueriesPanel.tsx
|
|
4852
5107
|
const queryKey = (run) => JSON.stringify(run.query);
|
|
5108
|
+
const SORT_LABEL = {
|
|
5109
|
+
default: "ordem da página",
|
|
5110
|
+
label: "nome",
|
|
5111
|
+
duration: "mais lentas",
|
|
5112
|
+
rows: "mais linhas",
|
|
5113
|
+
status: "erros primeiro"
|
|
5114
|
+
};
|
|
5115
|
+
const SORT_KEY = "bm-dashboard-devtools:queries-sort";
|
|
5116
|
+
/** Problemas primeiro: erro, em andamento, cancelada, ok. */
|
|
5117
|
+
const STATUS_ORDER = {
|
|
5118
|
+
error: 0,
|
|
5119
|
+
loading: 1,
|
|
5120
|
+
cancelled: 2,
|
|
5121
|
+
success: 3
|
|
5122
|
+
};
|
|
5123
|
+
function loadSort() {
|
|
5124
|
+
try {
|
|
5125
|
+
const saved = localStorage.getItem(SORT_KEY);
|
|
5126
|
+
return saved && saved in SORT_LABEL ? saved : "default";
|
|
5127
|
+
} catch {
|
|
5128
|
+
return "default";
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
5131
|
+
/** Ordena a lista; empate mantém a ordem original (página ou mais recente primeiro). */
|
|
5132
|
+
function sortRuns(runs, key) {
|
|
5133
|
+
if (key === "default") return runs;
|
|
5134
|
+
const compare = (a, b) => {
|
|
5135
|
+
switch (key) {
|
|
5136
|
+
case "label": return a.label.localeCompare(b.label, "pt-BR", { numeric: true });
|
|
5137
|
+
case "duration": return (b.durationMs ?? Date.now() - b.startedAt) - (a.durationMs ?? Date.now() - a.startedAt);
|
|
5138
|
+
case "rows": return (b.rows?.length ?? -1) - (a.rows?.length ?? -1);
|
|
5139
|
+
case "status": return STATUS_ORDER[a.status] - STATUS_ORDER[b.status];
|
|
5140
|
+
}
|
|
5141
|
+
};
|
|
5142
|
+
return [...runs].sort(compare);
|
|
5143
|
+
}
|
|
4853
5144
|
/** Consultas ativas na página (uma por componente) e o histórico de execuções. */
|
|
4854
5145
|
function QueriesPanel({ devtools, capabilities, selected, onSelect, onHover, onClearServerCache }) {
|
|
4855
5146
|
const { store, refresh } = devtools;
|
|
4856
5147
|
const snapshot = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
4857
5148
|
const [view, setView] = useState("active");
|
|
4858
|
-
const
|
|
5149
|
+
const [sort, setSort] = useState(loadSort);
|
|
5150
|
+
const changeSort = (next) => {
|
|
5151
|
+
setSort(next);
|
|
5152
|
+
try {
|
|
5153
|
+
localStorage.setItem(SORT_KEY, next);
|
|
5154
|
+
} catch {}
|
|
5155
|
+
};
|
|
5156
|
+
const runs = sortRuns(view === "active" ? snapshot.instances.map((instance) => instance.last) : snapshot.history, sort);
|
|
4859
5157
|
const instanceById = new Map(snapshot.instances.map((item) => [item.instanceId, item]));
|
|
4860
5158
|
const sameQuery = /* @__PURE__ */ new Map();
|
|
4861
5159
|
for (const { last } of snapshot.instances) sameQuery.set(queryKey(last), (sameQuery.get(queryKey(last)) ?? 0) + 1);
|
|
@@ -4886,31 +5184,54 @@ function QueriesPanel({ devtools, capabilities, selected, onSelect, onHover, onC
|
|
|
4886
5184
|
borderBottom: 1,
|
|
4887
5185
|
borderColor: "divider"
|
|
4888
5186
|
},
|
|
4889
|
-
children: [
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
5187
|
+
children: [
|
|
5188
|
+
/* @__PURE__ */ jsxs(ToggleButtonGroup, {
|
|
5189
|
+
size: "small",
|
|
5190
|
+
exclusive: true,
|
|
5191
|
+
value: view,
|
|
5192
|
+
onChange: (_, value) => value && setView(value),
|
|
5193
|
+
children: [/* @__PURE__ */ jsxs(ToggleButton, {
|
|
5194
|
+
value: "active",
|
|
5195
|
+
children: [
|
|
5196
|
+
"Ativas (",
|
|
5197
|
+
snapshot.instances.length,
|
|
5198
|
+
")"
|
|
5199
|
+
]
|
|
5200
|
+
}), /* @__PURE__ */ jsxs(ToggleButton, {
|
|
5201
|
+
value: "history",
|
|
5202
|
+
children: [
|
|
5203
|
+
"Histórico (",
|
|
5204
|
+
snapshot.history.length,
|
|
5205
|
+
")"
|
|
5206
|
+
]
|
|
5207
|
+
})]
|
|
5208
|
+
}),
|
|
5209
|
+
view === "history" && /* @__PURE__ */ jsx(Button, {
|
|
5210
|
+
size: "small",
|
|
5211
|
+
onClick: store.clearHistory,
|
|
5212
|
+
children: "Limpar"
|
|
5213
|
+
}),
|
|
5214
|
+
/* @__PURE__ */ jsx(TextField, {
|
|
5215
|
+
select: true,
|
|
5216
|
+
size: "small",
|
|
5217
|
+
value: sort,
|
|
5218
|
+
onChange: (e) => changeSort(e.target.value),
|
|
5219
|
+
title: "Ordenar a lista",
|
|
5220
|
+
sx: {
|
|
5221
|
+
ml: "auto",
|
|
5222
|
+
minWidth: 0,
|
|
5223
|
+
"& .MuiSelect-select": {
|
|
5224
|
+
fontSize: 12,
|
|
5225
|
+
py: .5
|
|
5226
|
+
}
|
|
5227
|
+
},
|
|
5228
|
+
children: Object.keys(SORT_LABEL).map((key) => /* @__PURE__ */ jsx(MenuItem, {
|
|
5229
|
+
value: key,
|
|
5230
|
+
dense: true,
|
|
5231
|
+
children: key === "default" && view === "history" ? "mais recentes" : SORT_LABEL[key]
|
|
5232
|
+
}, key))
|
|
5233
|
+
})
|
|
5234
|
+
]
|
|
4914
5235
|
}),
|
|
4915
5236
|
/* @__PURE__ */ jsxs(Box, {
|
|
4916
5237
|
sx: {
|
|
@@ -5102,6 +5423,7 @@ function RunDetail({ run, devtools, capabilities, mounted }) {
|
|
|
5102
5423
|
},
|
|
5103
5424
|
children: run.error
|
|
5104
5425
|
}),
|
|
5426
|
+
run.status === "error" && !debug && /* @__PURE__ */ jsx(FailedSql, { run }),
|
|
5105
5427
|
/* @__PURE__ */ jsx(Section, {
|
|
5106
5428
|
title: "Consulta",
|
|
5107
5429
|
children: /* @__PURE__ */ jsx(CodeBlock, {
|
|
@@ -5149,6 +5471,67 @@ function RunDetail({ run, devtools, capabilities, mounted }) {
|
|
|
5149
5471
|
})
|
|
5150
5472
|
] });
|
|
5151
5473
|
}
|
|
5474
|
+
/**
|
|
5475
|
+
* SQL de uma consulta que deu erro: o servidor não devolve debug junto com o erro, então pede só a
|
|
5476
|
+
* compilação (`devtools.compile`), sem executar. Se nem compilar, mostra por quê.
|
|
5477
|
+
*/
|
|
5478
|
+
function FailedSql({ run }) {
|
|
5479
|
+
const { fetcher } = useDashboard();
|
|
5480
|
+
const [state, setState] = useState(null);
|
|
5481
|
+
useEffect(() => {
|
|
5482
|
+
let active = true;
|
|
5483
|
+
setState({ loading: true });
|
|
5484
|
+
fetcher({
|
|
5485
|
+
...run.query,
|
|
5486
|
+
devtools: { compile: true }
|
|
5487
|
+
}).then((result) => {
|
|
5488
|
+
const debug = result.devtools?.debug;
|
|
5489
|
+
if (active) setState(debug ? {
|
|
5490
|
+
loading: false,
|
|
5491
|
+
data: debug
|
|
5492
|
+
} : {
|
|
5493
|
+
loading: false,
|
|
5494
|
+
error: "O endpoint não devolveu o SQL (precisa de runQuery(..., { devtools: true }) numa versão que aceite devtools.compile)."
|
|
5495
|
+
});
|
|
5496
|
+
}).catch((error) => active && setState({
|
|
5497
|
+
loading: false,
|
|
5498
|
+
error: errorMessage(error)
|
|
5499
|
+
}));
|
|
5500
|
+
return () => {
|
|
5501
|
+
active = false;
|
|
5502
|
+
};
|
|
5503
|
+
}, [fetcher, run.query]);
|
|
5504
|
+
return /* @__PURE__ */ jsx(Section, {
|
|
5505
|
+
title: "SQL gerado (não executou)",
|
|
5506
|
+
children: /* @__PURE__ */ jsx(ToolResult, {
|
|
5507
|
+
state,
|
|
5508
|
+
children: (debug) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(CodeBlock, {
|
|
5509
|
+
sql: true,
|
|
5510
|
+
code: inlineParams(debug.sql, debug.params)
|
|
5511
|
+
}), /* @__PURE__ */ jsxs(Typography, {
|
|
5512
|
+
sx: {
|
|
5513
|
+
fontSize: 11,
|
|
5514
|
+
color: "text.secondary",
|
|
5515
|
+
mt: .5
|
|
5516
|
+
},
|
|
5517
|
+
children: [
|
|
5518
|
+
"Compilado de novo agora",
|
|
5519
|
+
debug.draft ? ", com o rascunho atual" : "",
|
|
5520
|
+
", para depurar o erro acima.",
|
|
5521
|
+
!!debug.params.length && /* @__PURE__ */ jsxs(Box, {
|
|
5522
|
+
component: "span",
|
|
5523
|
+
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" },
|
|
5524
|
+
children: [
|
|
5525
|
+
" ",
|
|
5526
|
+
"params: ",
|
|
5527
|
+
JSON.stringify(debug.params)
|
|
5528
|
+
]
|
|
5529
|
+
})
|
|
5530
|
+
]
|
|
5531
|
+
})] })
|
|
5532
|
+
})
|
|
5533
|
+
});
|
|
5534
|
+
}
|
|
5152
5535
|
function useTool() {
|
|
5153
5536
|
const [state, setState] = useState(null);
|
|
5154
5537
|
const run = async (load) => {
|
|
@@ -5632,9 +6015,10 @@ function CheckList({ checks, expectedLabel, actualLabel }) {
|
|
|
5632
6015
|
* painel com as consultas da página, o modelo editável, um playground, regressão, dicionário e
|
|
5633
6016
|
* filtros. Montado pelo próprio `DashboardProvider` em desenvolvimento.
|
|
5634
6017
|
*
|
|
5635
|
-
*
|
|
5636
|
-
* redimensiona pelo canto)
|
|
5637
|
-
* página encolhe para não ficar escondida atrás dele; só o modo flutuante
|
|
6018
|
+
* Modos: fixado (embaixo, à esquerda ou à direita), flutuante (janela que se arrasta pelo cabeçalho
|
|
6019
|
+
* e redimensiona pelo canto) ou numa janela separada do navegador (`DevtoolsWindow`). Fixado, o
|
|
6020
|
+
* painel ocupa espaço: a página encolhe para não ficar escondida atrás dele; só o modo flutuante
|
|
6021
|
+
* sobrepõe a página.
|
|
5638
6022
|
* Atalhos: Alt+Shift+B abre/fecha o painel, Alt+Shift+F alterna entre fixado e flutuante,
|
|
5639
6023
|
* Alt+Shift+I liga o inspetor, Esc sai do inspetor.
|
|
5640
6024
|
*/
|
|
@@ -5723,13 +6107,15 @@ function loadUi() {
|
|
|
5723
6107
|
"bottom",
|
|
5724
6108
|
"left",
|
|
5725
6109
|
"right",
|
|
5726
|
-
"floating"
|
|
6110
|
+
"floating",
|
|
6111
|
+
"window"
|
|
5727
6112
|
].includes(saved.mode)) saved.mode = "bottom";
|
|
5728
6113
|
if (![
|
|
5729
6114
|
"bottom",
|
|
5730
6115
|
"left",
|
|
5731
6116
|
"right"
|
|
5732
6117
|
].includes(saved.dock)) saved.dock = "bottom";
|
|
6118
|
+
if (saved.mode === "window") saved.open = false;
|
|
5733
6119
|
return {
|
|
5734
6120
|
...saved,
|
|
5735
6121
|
float: clampFloat({
|
|
@@ -5828,6 +6214,7 @@ function DashboardDevtools() {
|
|
|
5828
6214
|
const [pickedItem, setPickedItem] = useState(null);
|
|
5829
6215
|
const [hovered, setHovered] = useState(null);
|
|
5830
6216
|
const [inspecting, setInspecting] = useState(false);
|
|
6217
|
+
const [popup, setPopup] = useState(null);
|
|
5831
6218
|
const store = devtools?.store;
|
|
5832
6219
|
const instances = useSyncExternalStore(store?.subscribe ?? (() => () => {}), () => store?.getSnapshot(), () => store?.getSnapshot())?.instances ?? [];
|
|
5833
6220
|
useEffect(() => {
|
|
@@ -5886,18 +6273,85 @@ function DashboardDevtools() {
|
|
|
5886
6273
|
}));
|
|
5887
6274
|
}, [layoutTab])
|
|
5888
6275
|
});
|
|
6276
|
+
const closePopup = useCallback(() => {
|
|
6277
|
+
setPopup((current) => {
|
|
6278
|
+
if (current && !current.closed) current.close();
|
|
6279
|
+
return null;
|
|
6280
|
+
});
|
|
6281
|
+
}, []);
|
|
6282
|
+
const openPanel = useCallback(() => {
|
|
6283
|
+
if (ui.mode !== "window") {
|
|
6284
|
+
setUi((current) => ({
|
|
6285
|
+
...current,
|
|
6286
|
+
open: true
|
|
6287
|
+
}));
|
|
6288
|
+
return;
|
|
6289
|
+
}
|
|
6290
|
+
const opened = popup && !popup.closed ? popup : openDevtoolsWindow();
|
|
6291
|
+
if (opened) {
|
|
6292
|
+
opened.focus();
|
|
6293
|
+
setPopup(opened);
|
|
6294
|
+
setUi((current) => ({
|
|
6295
|
+
...current,
|
|
6296
|
+
open: true
|
|
6297
|
+
}));
|
|
6298
|
+
} else setUi((current) => ({
|
|
6299
|
+
...current,
|
|
6300
|
+
open: true,
|
|
6301
|
+
mode: current.dock
|
|
6302
|
+
}));
|
|
6303
|
+
}, [ui.mode, popup]);
|
|
6304
|
+
const closePanel = useCallback(() => {
|
|
6305
|
+
closePopup();
|
|
6306
|
+
setUi((current) => ({
|
|
6307
|
+
...current,
|
|
6308
|
+
open: false
|
|
6309
|
+
}));
|
|
6310
|
+
}, [closePopup]);
|
|
6311
|
+
const toWindow = () => {
|
|
6312
|
+
const opened = openDevtoolsWindow();
|
|
6313
|
+
if (!opened) return;
|
|
6314
|
+
opened.focus();
|
|
6315
|
+
setPopup(opened);
|
|
6316
|
+
setUi((current) => ({
|
|
6317
|
+
...current,
|
|
6318
|
+
open: true,
|
|
6319
|
+
mode: "window"
|
|
6320
|
+
}));
|
|
6321
|
+
};
|
|
6322
|
+
const onPopupClosed = useCallback(() => {
|
|
6323
|
+
setPopup(null);
|
|
6324
|
+
setUi((current) => ({
|
|
6325
|
+
...current,
|
|
6326
|
+
open: false
|
|
6327
|
+
}));
|
|
6328
|
+
}, []);
|
|
6329
|
+
const keyActions = useRef({
|
|
6330
|
+
openPanel,
|
|
6331
|
+
closePanel,
|
|
6332
|
+
closePopup,
|
|
6333
|
+
open: ui.open,
|
|
6334
|
+
inspecting
|
|
6335
|
+
});
|
|
6336
|
+
keyActions.current = {
|
|
6337
|
+
openPanel,
|
|
6338
|
+
closePanel,
|
|
6339
|
+
closePopup,
|
|
6340
|
+
open: ui.open,
|
|
6341
|
+
inspecting
|
|
6342
|
+
};
|
|
5889
6343
|
useEffect(() => {
|
|
5890
6344
|
const onKey = (event) => {
|
|
5891
|
-
|
|
6345
|
+
const actions = keyActions.current;
|
|
6346
|
+
if (event.key === "Escape" && actions.inspecting) setInspecting(false);
|
|
5892
6347
|
if (!event.altKey || !event.shiftKey) return;
|
|
5893
6348
|
if (event.code === "KeyB") {
|
|
5894
6349
|
event.preventDefault();
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
open: !current.open
|
|
5898
|
-
}));
|
|
6350
|
+
if (actions.open) actions.closePanel();
|
|
6351
|
+
else actions.openPanel();
|
|
5899
6352
|
} else if (event.code === "KeyF") {
|
|
5900
6353
|
event.preventDefault();
|
|
6354
|
+
actions.closePopup();
|
|
5901
6355
|
setUi((current) => ({
|
|
5902
6356
|
...current,
|
|
5903
6357
|
open: true,
|
|
@@ -5908,10 +6362,13 @@ function DashboardDevtools() {
|
|
|
5908
6362
|
setInspecting((value) => !value);
|
|
5909
6363
|
}
|
|
5910
6364
|
};
|
|
5911
|
-
window
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
6365
|
+
const targets = popup && !popup.closed ? [window, popup] : [window];
|
|
6366
|
+
for (const target of targets) target.addEventListener("keydown", onKey);
|
|
6367
|
+
return () => {
|
|
6368
|
+
for (const target of targets) target.removeEventListener("keydown", onKey);
|
|
6369
|
+
};
|
|
6370
|
+
}, [popup]);
|
|
6371
|
+
const reserved = ui.open && ui.mode !== "floating" && ui.mode !== "window" ? ui.mode : null;
|
|
5915
6372
|
const reservedSize = reserved === "bottom" ? ui.height : ui.width;
|
|
5916
6373
|
const reservation = useRef(null);
|
|
5917
6374
|
const reservedSizeRef = useRef(reservedSize);
|
|
@@ -5935,6 +6392,7 @@ function DashboardDevtools() {
|
|
|
5935
6392
|
return () => window.removeEventListener("resize", onResize);
|
|
5936
6393
|
}, []);
|
|
5937
6394
|
if (!devtools || !store) return null;
|
|
6395
|
+
const inWindow = ui.mode === "window" && !!popup;
|
|
5938
6396
|
const floating = ui.mode === "floating";
|
|
5939
6397
|
const side = ui.mode === "left" || ui.mode === "right" ? ui.mode : null;
|
|
5940
6398
|
const startDockResize = (event) => {
|
|
@@ -5971,15 +6429,21 @@ function DashboardDevtools() {
|
|
|
5971
6429
|
})
|
|
5972
6430
|
})));
|
|
5973
6431
|
};
|
|
5974
|
-
const toggleMode = () =>
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
6432
|
+
const toggleMode = () => {
|
|
6433
|
+
closePopup();
|
|
6434
|
+
setUi((current) => ({
|
|
6435
|
+
...current,
|
|
6436
|
+
mode: current.mode === "floating" ? current.dock : "floating"
|
|
6437
|
+
}));
|
|
6438
|
+
};
|
|
6439
|
+
const dockTo = (dock) => {
|
|
6440
|
+
closePopup();
|
|
6441
|
+
setUi((current) => ({
|
|
6442
|
+
...current,
|
|
6443
|
+
mode: dock,
|
|
6444
|
+
dock
|
|
6445
|
+
}));
|
|
6446
|
+
};
|
|
5983
6447
|
const clearServerCache = () => loadModel(true).then(devtools.refresh);
|
|
5984
6448
|
const loading = instances.filter((item) => item.last.status === "loading").length;
|
|
5985
6449
|
const errors = instances.filter((item) => item.last.status === "error").length;
|
|
@@ -6008,6 +6472,229 @@ function DashboardDevtools() {
|
|
|
6008
6472
|
},
|
|
6009
6473
|
children: /* @__PURE__ */ jsx(CircularProgress, { size: 20 })
|
|
6010
6474
|
}) : content(original);
|
|
6475
|
+
const header = /* @__PURE__ */ jsxs(Box, {
|
|
6476
|
+
onMouseDown: startDrag,
|
|
6477
|
+
onDoubleClick: (event) => {
|
|
6478
|
+
if (floating && !event.target.closest("button, [role=\"tab\"]")) toggleMode();
|
|
6479
|
+
},
|
|
6480
|
+
sx: {
|
|
6481
|
+
display: "flex",
|
|
6482
|
+
alignItems: "center",
|
|
6483
|
+
gap: 1.5,
|
|
6484
|
+
px: 1.5,
|
|
6485
|
+
borderBottom: 1,
|
|
6486
|
+
borderColor: "divider",
|
|
6487
|
+
cursor: floating ? "move" : void 0,
|
|
6488
|
+
...side && {
|
|
6489
|
+
flexWrap: "wrap",
|
|
6490
|
+
columnGap: 1,
|
|
6491
|
+
pt: .5
|
|
6492
|
+
}
|
|
6493
|
+
},
|
|
6494
|
+
children: [
|
|
6495
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
6496
|
+
sx: {
|
|
6497
|
+
fontWeight: 800,
|
|
6498
|
+
color: "primary.main",
|
|
6499
|
+
whiteSpace: "nowrap"
|
|
6500
|
+
},
|
|
6501
|
+
children: "◆ BI Devtools"
|
|
6502
|
+
}),
|
|
6503
|
+
/* @__PURE__ */ jsx(Chip, {
|
|
6504
|
+
size: "small",
|
|
6505
|
+
label: model,
|
|
6506
|
+
sx: { fontFamily: MONO }
|
|
6507
|
+
}),
|
|
6508
|
+
/* @__PURE__ */ jsxs(Tabs, {
|
|
6509
|
+
value: ui.tab,
|
|
6510
|
+
onChange: (_, tab) => setTab(tab),
|
|
6511
|
+
variant: "scrollable",
|
|
6512
|
+
scrollButtons: "auto",
|
|
6513
|
+
sx: {
|
|
6514
|
+
minHeight: 40,
|
|
6515
|
+
...side && {
|
|
6516
|
+
order: 1,
|
|
6517
|
+
width: "100%"
|
|
6518
|
+
}
|
|
6519
|
+
},
|
|
6520
|
+
children: [
|
|
6521
|
+
/* @__PURE__ */ jsx(Tab, {
|
|
6522
|
+
value: "queries",
|
|
6523
|
+
label: /* @__PURE__ */ jsxs(Box, {
|
|
6524
|
+
sx: {
|
|
6525
|
+
display: "flex",
|
|
6526
|
+
alignItems: "center",
|
|
6527
|
+
gap: .75
|
|
6528
|
+
},
|
|
6529
|
+
children: [
|
|
6530
|
+
"Consultas (",
|
|
6531
|
+
instances.length,
|
|
6532
|
+
")",
|
|
6533
|
+
!!loading && /* @__PURE__ */ jsx(CircularProgress, { size: 10 }),
|
|
6534
|
+
!!(errors || storms) && /* @__PURE__ */ jsx(Chip, {
|
|
6535
|
+
size: "small",
|
|
6536
|
+
color: "error",
|
|
6537
|
+
label: errors + storms,
|
|
6538
|
+
sx: { height: 16 }
|
|
6539
|
+
})
|
|
6540
|
+
]
|
|
6541
|
+
})
|
|
6542
|
+
}),
|
|
6543
|
+
/* @__PURE__ */ jsx(Tab, {
|
|
6544
|
+
value: "model",
|
|
6545
|
+
label: "Modelo"
|
|
6546
|
+
}),
|
|
6547
|
+
/* @__PURE__ */ jsx(Tab, {
|
|
6548
|
+
value: "layout",
|
|
6549
|
+
label: "Layout"
|
|
6550
|
+
}),
|
|
6551
|
+
/* @__PURE__ */ jsx(Tab, {
|
|
6552
|
+
value: "playground",
|
|
6553
|
+
label: "Playground"
|
|
6554
|
+
}),
|
|
6555
|
+
/* @__PURE__ */ jsx(Tab, {
|
|
6556
|
+
value: "regression",
|
|
6557
|
+
label: "Regressão"
|
|
6558
|
+
}),
|
|
6559
|
+
/* @__PURE__ */ jsx(Tab, {
|
|
6560
|
+
value: "dictionary",
|
|
6561
|
+
label: "Dicionário"
|
|
6562
|
+
}),
|
|
6563
|
+
/* @__PURE__ */ jsx(Tab, {
|
|
6564
|
+
value: "filters",
|
|
6565
|
+
label: "Filtros"
|
|
6566
|
+
})
|
|
6567
|
+
]
|
|
6568
|
+
}),
|
|
6569
|
+
devtools.draft && /* @__PURE__ */ jsx(Chip, {
|
|
6570
|
+
size: "small",
|
|
6571
|
+
color: "warning",
|
|
6572
|
+
label: "rascunho aplicado"
|
|
6573
|
+
}),
|
|
6574
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
6575
|
+
sx: {
|
|
6576
|
+
ml: "auto",
|
|
6577
|
+
display: "flex",
|
|
6578
|
+
alignItems: "center",
|
|
6579
|
+
gap: .5
|
|
6580
|
+
},
|
|
6581
|
+
children: [
|
|
6582
|
+
/* @__PURE__ */ jsx(Tooltip, {
|
|
6583
|
+
title: "Inspecionar componente (Alt+Shift+I)",
|
|
6584
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
6585
|
+
size: "small",
|
|
6586
|
+
color: inspecting ? "primary" : "default",
|
|
6587
|
+
onClick: () => setInspecting((value) => !value),
|
|
6588
|
+
"aria-label": "Inspecionar componente",
|
|
6589
|
+
children: "⌖"
|
|
6590
|
+
})
|
|
6591
|
+
}),
|
|
6592
|
+
DOCK_SIDES.map(({ side: dock, icon, label }) => /* @__PURE__ */ jsx(Tooltip, {
|
|
6593
|
+
title: `${label} (ocupa espaço na página)`,
|
|
6594
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
6595
|
+
size: "small",
|
|
6596
|
+
color: ui.mode === dock ? "primary" : "default",
|
|
6597
|
+
onClick: () => dockTo(dock),
|
|
6598
|
+
"aria-label": label,
|
|
6599
|
+
children: icon
|
|
6600
|
+
})
|
|
6601
|
+
}, dock)),
|
|
6602
|
+
/* @__PURE__ */ jsx(Tooltip, {
|
|
6603
|
+
title: "Janela flutuante, por cima da página (Alt+Shift+F) · arraste pelo cabeçalho",
|
|
6604
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
6605
|
+
size: "small",
|
|
6606
|
+
color: floating ? "primary" : "default",
|
|
6607
|
+
onClick: toggleMode,
|
|
6608
|
+
"aria-label": "Devtools flutuante",
|
|
6609
|
+
children: "⧉"
|
|
6610
|
+
})
|
|
6611
|
+
}),
|
|
6612
|
+
/* @__PURE__ */ jsx(Tooltip, {
|
|
6613
|
+
title: "Abrir numa janela separada do navegador (ex.: em outro monitor)",
|
|
6614
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
6615
|
+
size: "small",
|
|
6616
|
+
color: inWindow ? "primary" : "default",
|
|
6617
|
+
onClick: inWindow ? () => popup?.focus() : toWindow,
|
|
6618
|
+
"aria-label": "Devtools em janela separada",
|
|
6619
|
+
children: "↗"
|
|
6620
|
+
})
|
|
6621
|
+
}),
|
|
6622
|
+
/* @__PURE__ */ jsx(Tooltip, {
|
|
6623
|
+
title: "Fechar (Alt+Shift+B)",
|
|
6624
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
6625
|
+
size: "small",
|
|
6626
|
+
onClick: closePanel,
|
|
6627
|
+
"aria-label": "Fechar devtools",
|
|
6628
|
+
children: "✕"
|
|
6629
|
+
})
|
|
6630
|
+
})
|
|
6631
|
+
]
|
|
6632
|
+
})
|
|
6633
|
+
]
|
|
6634
|
+
});
|
|
6635
|
+
const body = /* @__PURE__ */ jsxs(Box, {
|
|
6636
|
+
sx: {
|
|
6637
|
+
flex: 1,
|
|
6638
|
+
minHeight: 0
|
|
6639
|
+
},
|
|
6640
|
+
children: [
|
|
6641
|
+
ui.tab === "queries" && /* @__PURE__ */ jsx(QueriesPanel, {
|
|
6642
|
+
devtools,
|
|
6643
|
+
capabilities,
|
|
6644
|
+
selected,
|
|
6645
|
+
onSelect: setSelected,
|
|
6646
|
+
onHover: setHovered,
|
|
6647
|
+
onClearServerCache: clearServerCache
|
|
6648
|
+
}),
|
|
6649
|
+
ui.tab === "model" && needsModel((registered) => /* @__PURE__ */ jsx(ModelPanel, {
|
|
6650
|
+
devtools,
|
|
6651
|
+
original: registered,
|
|
6652
|
+
capabilities,
|
|
6653
|
+
tables: tables ?? [],
|
|
6654
|
+
onTry: (selection) => {
|
|
6655
|
+
setPlayground(selection);
|
|
6656
|
+
setTab("playground");
|
|
6657
|
+
},
|
|
6658
|
+
onSaved: () => setTimeout(() => loadModel(), 1e3)
|
|
6659
|
+
})),
|
|
6660
|
+
ui.tab === "layout" && needsModel((registered) => /* @__PURE__ */ jsx(LayoutPanel, {
|
|
6661
|
+
devtools,
|
|
6662
|
+
model: devtools.draft ?? registered,
|
|
6663
|
+
capabilities,
|
|
6664
|
+
picked: pickedItem,
|
|
6665
|
+
onHover: setHovered
|
|
6666
|
+
})),
|
|
6667
|
+
ui.tab === "playground" && needsModel((registered) => /* @__PURE__ */ jsx(PlaygroundPanel, {
|
|
6668
|
+
model: devtools.draft ?? registered,
|
|
6669
|
+
selection: playground,
|
|
6670
|
+
onSelectionChange: setPlayground
|
|
6671
|
+
})),
|
|
6672
|
+
ui.tab === "regression" && /* @__PURE__ */ jsx(RegressionPanel, {
|
|
6673
|
+
devtools,
|
|
6674
|
+
capabilities
|
|
6675
|
+
}),
|
|
6676
|
+
ui.tab === "dictionary" && /* @__PURE__ */ jsxs(Box, {
|
|
6677
|
+
sx: {
|
|
6678
|
+
p: 1.5,
|
|
6679
|
+
overflow: "auto",
|
|
6680
|
+
height: "100%"
|
|
6681
|
+
},
|
|
6682
|
+
children: [/* @__PURE__ */ jsxs(Typography, {
|
|
6683
|
+
sx: {
|
|
6684
|
+
fontSize: 12,
|
|
6685
|
+
color: "text.secondary",
|
|
6686
|
+
mb: 1.5
|
|
6687
|
+
},
|
|
6688
|
+
children: [
|
|
6689
|
+
"O mesmo ",
|
|
6690
|
+
/* @__PURE__ */ jsx("code", { children: "<ModelDictionary />" }),
|
|
6691
|
+
" pode ir numa página do dashboard como documentação para quem usa. Edite rótulos e descrições na aba Modelo."
|
|
6692
|
+
]
|
|
6693
|
+
}), /* @__PURE__ */ jsx(ModelDictionary, {})]
|
|
6694
|
+
}),
|
|
6695
|
+
ui.tab === "filters" && /* @__PURE__ */ jsx(FiltersPanel, { devtools })
|
|
6696
|
+
]
|
|
6697
|
+
});
|
|
6011
6698
|
return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs(ThemeProvider, {
|
|
6012
6699
|
theme,
|
|
6013
6700
|
children: [
|
|
@@ -6016,7 +6703,26 @@ function DashboardDevtools() {
|
|
|
6016
6703
|
label: hoveredLabel,
|
|
6017
6704
|
zIndex: 1251
|
|
6018
6705
|
}),
|
|
6019
|
-
ui.open
|
|
6706
|
+
ui.open && inWindow && popup && /* @__PURE__ */ jsx(DevtoolsWindow, {
|
|
6707
|
+
popup,
|
|
6708
|
+
title: `BI Devtools · ${model}`,
|
|
6709
|
+
theme,
|
|
6710
|
+
onClosed: onPopupClosed,
|
|
6711
|
+
children: /* @__PURE__ */ jsxs(Paper, {
|
|
6712
|
+
...devtoolsMarker,
|
|
6713
|
+
square: true,
|
|
6714
|
+
elevation: 0,
|
|
6715
|
+
sx: {
|
|
6716
|
+
height: "100%",
|
|
6717
|
+
display: "flex",
|
|
6718
|
+
flexDirection: "column",
|
|
6719
|
+
color: "text.primary",
|
|
6720
|
+
overflow: "hidden"
|
|
6721
|
+
},
|
|
6722
|
+
children: [header, body]
|
|
6723
|
+
})
|
|
6724
|
+
}),
|
|
6725
|
+
ui.open && !inWindow ? /* @__PURE__ */ jsxs(Paper, {
|
|
6020
6726
|
...devtoolsMarker,
|
|
6021
6727
|
square: !floating,
|
|
6022
6728
|
elevation: floating ? 16 : 12,
|
|
@@ -6081,232 +6787,15 @@ function DashboardDevtools() {
|
|
|
6081
6787
|
cursor: "ns-resize"
|
|
6082
6788
|
}
|
|
6083
6789
|
}),
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
onDoubleClick: (event) => {
|
|
6087
|
-
if (floating && !event.target.closest("button, [role=\"tab\"]")) toggleMode();
|
|
6088
|
-
},
|
|
6089
|
-
sx: {
|
|
6090
|
-
display: "flex",
|
|
6091
|
-
alignItems: "center",
|
|
6092
|
-
gap: 1.5,
|
|
6093
|
-
px: 1.5,
|
|
6094
|
-
borderBottom: 1,
|
|
6095
|
-
borderColor: "divider",
|
|
6096
|
-
cursor: floating ? "move" : void 0,
|
|
6097
|
-
...side && {
|
|
6098
|
-
flexWrap: "wrap",
|
|
6099
|
-
columnGap: 1,
|
|
6100
|
-
pt: .5
|
|
6101
|
-
}
|
|
6102
|
-
},
|
|
6103
|
-
children: [
|
|
6104
|
-
/* @__PURE__ */ jsx(Typography, {
|
|
6105
|
-
sx: {
|
|
6106
|
-
fontWeight: 800,
|
|
6107
|
-
color: "primary.main",
|
|
6108
|
-
whiteSpace: "nowrap"
|
|
6109
|
-
},
|
|
6110
|
-
children: "◆ BI Devtools"
|
|
6111
|
-
}),
|
|
6112
|
-
/* @__PURE__ */ jsx(Chip, {
|
|
6113
|
-
size: "small",
|
|
6114
|
-
label: model,
|
|
6115
|
-
sx: { fontFamily: MONO }
|
|
6116
|
-
}),
|
|
6117
|
-
/* @__PURE__ */ jsxs(Tabs, {
|
|
6118
|
-
value: ui.tab,
|
|
6119
|
-
onChange: (_, tab) => setTab(tab),
|
|
6120
|
-
variant: "scrollable",
|
|
6121
|
-
scrollButtons: "auto",
|
|
6122
|
-
sx: {
|
|
6123
|
-
minHeight: 40,
|
|
6124
|
-
...side && {
|
|
6125
|
-
order: 1,
|
|
6126
|
-
width: "100%"
|
|
6127
|
-
}
|
|
6128
|
-
},
|
|
6129
|
-
children: [
|
|
6130
|
-
/* @__PURE__ */ jsx(Tab, {
|
|
6131
|
-
value: "queries",
|
|
6132
|
-
label: /* @__PURE__ */ jsxs(Box, {
|
|
6133
|
-
sx: {
|
|
6134
|
-
display: "flex",
|
|
6135
|
-
alignItems: "center",
|
|
6136
|
-
gap: .75
|
|
6137
|
-
},
|
|
6138
|
-
children: [
|
|
6139
|
-
"Consultas (",
|
|
6140
|
-
instances.length,
|
|
6141
|
-
")",
|
|
6142
|
-
!!loading && /* @__PURE__ */ jsx(CircularProgress, { size: 10 }),
|
|
6143
|
-
!!(errors || storms) && /* @__PURE__ */ jsx(Chip, {
|
|
6144
|
-
size: "small",
|
|
6145
|
-
color: "error",
|
|
6146
|
-
label: errors + storms,
|
|
6147
|
-
sx: { height: 16 }
|
|
6148
|
-
})
|
|
6149
|
-
]
|
|
6150
|
-
})
|
|
6151
|
-
}),
|
|
6152
|
-
/* @__PURE__ */ jsx(Tab, {
|
|
6153
|
-
value: "model",
|
|
6154
|
-
label: "Modelo"
|
|
6155
|
-
}),
|
|
6156
|
-
/* @__PURE__ */ jsx(Tab, {
|
|
6157
|
-
value: "layout",
|
|
6158
|
-
label: "Layout"
|
|
6159
|
-
}),
|
|
6160
|
-
/* @__PURE__ */ jsx(Tab, {
|
|
6161
|
-
value: "playground",
|
|
6162
|
-
label: "Playground"
|
|
6163
|
-
}),
|
|
6164
|
-
/* @__PURE__ */ jsx(Tab, {
|
|
6165
|
-
value: "regression",
|
|
6166
|
-
label: "Regressão"
|
|
6167
|
-
}),
|
|
6168
|
-
/* @__PURE__ */ jsx(Tab, {
|
|
6169
|
-
value: "dictionary",
|
|
6170
|
-
label: "Dicionário"
|
|
6171
|
-
}),
|
|
6172
|
-
/* @__PURE__ */ jsx(Tab, {
|
|
6173
|
-
value: "filters",
|
|
6174
|
-
label: "Filtros"
|
|
6175
|
-
})
|
|
6176
|
-
]
|
|
6177
|
-
}),
|
|
6178
|
-
devtools.draft && /* @__PURE__ */ jsx(Chip, {
|
|
6179
|
-
size: "small",
|
|
6180
|
-
color: "warning",
|
|
6181
|
-
label: "rascunho aplicado"
|
|
6182
|
-
}),
|
|
6183
|
-
/* @__PURE__ */ jsxs(Box, {
|
|
6184
|
-
sx: {
|
|
6185
|
-
ml: "auto",
|
|
6186
|
-
display: "flex",
|
|
6187
|
-
alignItems: "center",
|
|
6188
|
-
gap: .5
|
|
6189
|
-
},
|
|
6190
|
-
children: [
|
|
6191
|
-
/* @__PURE__ */ jsx(Tooltip, {
|
|
6192
|
-
title: "Inspecionar componente (Alt+Shift+I)",
|
|
6193
|
-
children: /* @__PURE__ */ jsx(IconButton, {
|
|
6194
|
-
size: "small",
|
|
6195
|
-
color: inspecting ? "primary" : "default",
|
|
6196
|
-
onClick: () => setInspecting((value) => !value),
|
|
6197
|
-
"aria-label": "Inspecionar componente",
|
|
6198
|
-
children: "⌖"
|
|
6199
|
-
})
|
|
6200
|
-
}),
|
|
6201
|
-
DOCK_SIDES.map(({ side: dock, icon, label }) => /* @__PURE__ */ jsx(Tooltip, {
|
|
6202
|
-
title: `${label} (ocupa espaço na página)`,
|
|
6203
|
-
children: /* @__PURE__ */ jsx(IconButton, {
|
|
6204
|
-
size: "small",
|
|
6205
|
-
color: ui.mode === dock ? "primary" : "default",
|
|
6206
|
-
onClick: () => dockTo(dock),
|
|
6207
|
-
"aria-label": label,
|
|
6208
|
-
children: icon
|
|
6209
|
-
})
|
|
6210
|
-
}, dock)),
|
|
6211
|
-
/* @__PURE__ */ jsx(Tooltip, {
|
|
6212
|
-
title: "Janela flutuante, por cima da página (Alt+Shift+F) · arraste pelo cabeçalho",
|
|
6213
|
-
children: /* @__PURE__ */ jsx(IconButton, {
|
|
6214
|
-
size: "small",
|
|
6215
|
-
color: floating ? "primary" : "default",
|
|
6216
|
-
onClick: toggleMode,
|
|
6217
|
-
"aria-label": "Devtools flutuante",
|
|
6218
|
-
children: "⧉"
|
|
6219
|
-
})
|
|
6220
|
-
}),
|
|
6221
|
-
/* @__PURE__ */ jsx(Tooltip, {
|
|
6222
|
-
title: "Fechar (Alt+Shift+B)",
|
|
6223
|
-
children: /* @__PURE__ */ jsx(IconButton, {
|
|
6224
|
-
size: "small",
|
|
6225
|
-
onClick: () => setUi((current) => ({
|
|
6226
|
-
...current,
|
|
6227
|
-
open: false
|
|
6228
|
-
})),
|
|
6229
|
-
"aria-label": "Fechar devtools",
|
|
6230
|
-
children: "✕"
|
|
6231
|
-
})
|
|
6232
|
-
})
|
|
6233
|
-
]
|
|
6234
|
-
})
|
|
6235
|
-
]
|
|
6236
|
-
}),
|
|
6237
|
-
/* @__PURE__ */ jsxs(Box, {
|
|
6238
|
-
sx: {
|
|
6239
|
-
flex: 1,
|
|
6240
|
-
minHeight: 0
|
|
6241
|
-
},
|
|
6242
|
-
children: [
|
|
6243
|
-
ui.tab === "queries" && /* @__PURE__ */ jsx(QueriesPanel, {
|
|
6244
|
-
devtools,
|
|
6245
|
-
capabilities,
|
|
6246
|
-
selected,
|
|
6247
|
-
onSelect: setSelected,
|
|
6248
|
-
onHover: setHovered,
|
|
6249
|
-
onClearServerCache: clearServerCache
|
|
6250
|
-
}),
|
|
6251
|
-
ui.tab === "model" && needsModel((registered) => /* @__PURE__ */ jsx(ModelPanel, {
|
|
6252
|
-
devtools,
|
|
6253
|
-
original: registered,
|
|
6254
|
-
capabilities,
|
|
6255
|
-
tables: tables ?? [],
|
|
6256
|
-
onTry: (selection) => {
|
|
6257
|
-
setPlayground(selection);
|
|
6258
|
-
setTab("playground");
|
|
6259
|
-
},
|
|
6260
|
-
onSaved: () => setTimeout(() => loadModel(), 1e3)
|
|
6261
|
-
})),
|
|
6262
|
-
ui.tab === "layout" && needsModel((registered) => /* @__PURE__ */ jsx(LayoutPanel, {
|
|
6263
|
-
devtools,
|
|
6264
|
-
model: devtools.draft ?? registered,
|
|
6265
|
-
capabilities,
|
|
6266
|
-
picked: pickedItem,
|
|
6267
|
-
onHover: setHovered
|
|
6268
|
-
})),
|
|
6269
|
-
ui.tab === "playground" && needsModel((registered) => /* @__PURE__ */ jsx(PlaygroundPanel, {
|
|
6270
|
-
model: devtools.draft ?? registered,
|
|
6271
|
-
selection: playground,
|
|
6272
|
-
onSelectionChange: setPlayground
|
|
6273
|
-
})),
|
|
6274
|
-
ui.tab === "regression" && /* @__PURE__ */ jsx(RegressionPanel, {
|
|
6275
|
-
devtools,
|
|
6276
|
-
capabilities
|
|
6277
|
-
}),
|
|
6278
|
-
ui.tab === "dictionary" && /* @__PURE__ */ jsxs(Box, {
|
|
6279
|
-
sx: {
|
|
6280
|
-
p: 1.5,
|
|
6281
|
-
overflow: "auto",
|
|
6282
|
-
height: "100%"
|
|
6283
|
-
},
|
|
6284
|
-
children: [/* @__PURE__ */ jsxs(Typography, {
|
|
6285
|
-
sx: {
|
|
6286
|
-
fontSize: 12,
|
|
6287
|
-
color: "text.secondary",
|
|
6288
|
-
mb: 1.5
|
|
6289
|
-
},
|
|
6290
|
-
children: [
|
|
6291
|
-
"O mesmo ",
|
|
6292
|
-
/* @__PURE__ */ jsx("code", { children: "<ModelDictionary />" }),
|
|
6293
|
-
" pode ir numa página do dashboard como documentação para quem usa. Edite rótulos e descrições na aba Modelo."
|
|
6294
|
-
]
|
|
6295
|
-
}), /* @__PURE__ */ jsx(ModelDictionary, {})]
|
|
6296
|
-
}),
|
|
6297
|
-
ui.tab === "filters" && /* @__PURE__ */ jsx(FiltersPanel, { devtools })
|
|
6298
|
-
]
|
|
6299
|
-
})
|
|
6790
|
+
header,
|
|
6791
|
+
body
|
|
6300
6792
|
]
|
|
6301
6793
|
}) : /* @__PURE__ */ jsx(ToggleButton$1, {
|
|
6302
6794
|
draft: !!devtools.draft,
|
|
6303
6795
|
loading,
|
|
6304
6796
|
problems: errors + storms,
|
|
6305
6797
|
inspecting,
|
|
6306
|
-
onOpen: () =>
|
|
6307
|
-
...current,
|
|
6308
|
-
open: true
|
|
6309
|
-
}))
|
|
6798
|
+
onOpen: inWindow ? () => popup?.focus() : openPanel
|
|
6310
6799
|
}),
|
|
6311
6800
|
inspecting && /* @__PURE__ */ jsx(Box, {
|
|
6312
6801
|
...devtoolsMarker,
|
|
@@ -6394,4 +6883,4 @@ function ToggleButton$1({ draft, loading, problems, inspecting, onOpen }) {
|
|
|
6394
6883
|
//#endregion
|
|
6395
6884
|
export { DashboardDevtools as default };
|
|
6396
6885
|
|
|
6397
|
-
//# sourceMappingURL=DashboardDevtools-
|
|
6886
|
+
//# sourceMappingURL=DashboardDevtools-C4E9afPT.js.map
|