@bluemarble/bi 0.4.0 → 0.6.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-7L93aV1-.js → DashboardDevtools-SCewfQGR.js} +1081 -240
- package/dist/DashboardDevtools-SCewfQGR.js.map +1 -0
- package/dist/{ModelDictionary-Y7W9oK1J.js → ModelDictionary-C2tzlOY8.js} +82 -5
- package/dist/ModelDictionary-C2tzlOY8.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +164 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/index.d.ts +32 -3
- package/dist/client/index.js +79 -100
- package/dist/client/index.js.map +1 -1
- package/dist/codegen-DBe4-XW_.js +354 -0
- package/dist/codegen-DBe4-XW_.js.map +1 -0
- package/dist/{types-HXAn-3I1.d.ts → layout-B7eYgNyR.d.ts} +236 -150
- package/dist/{layout-676sEvvb.js → layout-BS51SQcJ.js} +242 -345
- package/dist/layout-BS51SQcJ.js.map +1 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +2 -1
- package/dist/server/index.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +4 -1
- package/dist/DashboardDevtools-7L93aV1-.js.map +0 -1
- package/dist/ModelDictionary-Y7W9oK1J.js.map +0 -1
- package/dist/layout-676sEvvb.js.map +0 -1
package/dist/client/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { f as isBuiltinChart, o as chartCardStyle, u as customChartProps } from "../layout-
|
|
2
|
-
import { a as
|
|
1
|
+
import { T as rowRuleStyles, _ as tableColumns, f as isBuiltinChart, h as percentColumn, o as chartCardStyle, u as customChartProps } from "../layout-BS51SQcJ.js";
|
|
2
|
+
import { a as formatCurrency, c as measureFormat, d as useDashboard, i as fieldLabel, l as measureFormatter, m as layoutItemKey, n as INSPECT_ATTRIBUTE, o as formatMeasure, p as LAYOUT_ITEM_ATTRIBUTE, r as useModelQuery, s as formatNumber, t as ModelDictionary, u as DashboardProvider } from "../ModelDictionary-C2tzlOY8.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";
|
|
@@ -121,75 +121,6 @@ function TooltipBox({ title, caption, children }) {
|
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
123
|
//#endregion
|
|
124
|
-
//#region src/client/format.ts
|
|
125
|
-
const formatters = /* @__PURE__ */ new Map();
|
|
126
|
-
/** `compact`: notação curta do pt-BR (1,2 mil / 3,4 mi / 5,6 bi). */
|
|
127
|
-
function formatNumber(value, decimals = 2, compact = false) {
|
|
128
|
-
if (value === null || value === void 0 || value === "") return "";
|
|
129
|
-
const number = Number(value);
|
|
130
|
-
if (Number.isNaN(number)) return String(value);
|
|
131
|
-
const key = `${decimals}:${compact}`;
|
|
132
|
-
let formatter = formatters.get(key);
|
|
133
|
-
if (!formatter) {
|
|
134
|
-
formatter = new Intl.NumberFormat("pt-BR", {
|
|
135
|
-
minimumFractionDigits: decimals,
|
|
136
|
-
maximumFractionDigits: decimals,
|
|
137
|
-
...compact && { notation: "compact" }
|
|
138
|
-
});
|
|
139
|
-
formatters.set(key, formatter);
|
|
140
|
-
}
|
|
141
|
-
return formatter.format(number);
|
|
142
|
-
}
|
|
143
|
-
const formatCurrency = (value, decimals = 2, compact = false) => {
|
|
144
|
-
const text = formatNumber(value, decimals, compact);
|
|
145
|
-
return text && `R$ ${text}`;
|
|
146
|
-
};
|
|
147
|
-
/**
|
|
148
|
-
* Formata pelo dicionário do modelo (`format`/`decimals` da medida) ou pelo formato do gráfico.
|
|
149
|
-
* `fallbackDecimals` é o padrão do componente quando nenhum dos dois define `decimals`.
|
|
150
|
-
*/
|
|
151
|
-
function formatMeasure(value, meta, fallbackDecimals = 2) {
|
|
152
|
-
if (value === null || value === void 0 || value === "") return "";
|
|
153
|
-
const decimals = meta?.decimals ?? (meta?.format === "integer" ? 0 : fallbackDecimals);
|
|
154
|
-
const compact = meta?.compact ?? false;
|
|
155
|
-
let text;
|
|
156
|
-
switch (meta?.format) {
|
|
157
|
-
case "currency":
|
|
158
|
-
text = formatCurrency(value, decimals, compact);
|
|
159
|
-
break;
|
|
160
|
-
case "percent": {
|
|
161
|
-
const number = formatNumber(Number(value) * 100, decimals, compact);
|
|
162
|
-
text = number && `${number}%`;
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
165
|
-
default: text = formatNumber(value, decimals, compact);
|
|
166
|
-
}
|
|
167
|
-
return text && `${meta?.prefix ?? ""}${text}${meta?.suffix ?? ""}`;
|
|
168
|
-
}
|
|
169
|
-
/** Dicionário do modelo com o formato do gráfico por cima. */
|
|
170
|
-
function measureFormat(meta, measure, formats) {
|
|
171
|
-
const fromModel = meta?.measures[measure];
|
|
172
|
-
const fromChart = formats?.[measure];
|
|
173
|
-
if (!fromChart) return fromModel;
|
|
174
|
-
return {
|
|
175
|
-
...fromModel?.format !== void 0 && { format: fromModel.format },
|
|
176
|
-
...fromModel?.decimals !== void 0 && { decimals: fromModel.decimals },
|
|
177
|
-
...fromChart
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Formatter de uma medida: o `formats` do gráfico (se houver) por cima do dicionário do modelo; sem
|
|
182
|
-
* `format`, número com `fallbackDecimals`.
|
|
183
|
-
*/
|
|
184
|
-
function measureFormatter(meta, measure, fallbackDecimals = 2, formats) {
|
|
185
|
-
const format = measureFormat(meta, measure, formats);
|
|
186
|
-
return (value) => formatMeasure(value, format, fallbackDecimals);
|
|
187
|
-
}
|
|
188
|
-
/** Rótulo de uma dimensão/medida: o `labels` do gráfico, o dicionário do modelo ou o nome. */
|
|
189
|
-
function fieldLabel(meta, name, labels) {
|
|
190
|
-
return labels?.[name] || meta?.dimensions[name]?.label || meta?.measures[name]?.label || name;
|
|
191
|
-
}
|
|
192
|
-
//#endregion
|
|
193
124
|
//#region src/client/BarChart.tsx
|
|
194
125
|
const MAX_VALUE_LABELS = 30;
|
|
195
126
|
/** Altura de cada categoria no modo horizontal (barras finas, com respiro). */
|
|
@@ -774,8 +705,7 @@ function DashboardCanvas({ layout, children }) {
|
|
|
774
705
|
//#endregion
|
|
775
706
|
//#region src/client/DataTable.tsx
|
|
776
707
|
const EMPTY = [];
|
|
777
|
-
|
|
778
|
-
const PERCENT = "%:";
|
|
708
|
+
const NO_RULES = [];
|
|
779
709
|
function compare(a, b) {
|
|
780
710
|
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
781
711
|
return String(a).localeCompare(String(b), "pt-BR", { numeric: true });
|
|
@@ -784,17 +714,25 @@ function compare(a, b) {
|
|
|
784
714
|
* Tabela de medidas por dimensões, com total e ordenação pelo cabeçalho. Ex.:
|
|
785
715
|
* `<DataTable dimensions={['navio']} measures={['volTotal', 'custoTotal', 'custoTon']} />`.
|
|
786
716
|
*/
|
|
787
|
-
function DataTable({ dimensions = EMPTY, measures, percentOf = EMPTY, formatDimensions = {}, showTotal = true, sortBy, sortDirection = "desc", clickToFilter = false, height = 400, labels, formats: formatsByMeasure, percentDecimals = 1, queryLabel }) {
|
|
717
|
+
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 }) {
|
|
788
718
|
const { meta } = useDashboard();
|
|
789
719
|
const name = queryLabel ?? `DataTable · ${dimensions.join(", ") || "total"}`;
|
|
790
720
|
const firstDimension = dimensions[0];
|
|
791
721
|
const ignoreFilters = clickToFilter && firstDimension ? [firstDimension] : void 0;
|
|
792
722
|
const extraMeasures = percentOf.filter((measure) => !measures.includes(measure));
|
|
723
|
+
const outside = [...new Set([sortBy, ...formatRules.map((rule) => rule.field)].filter(Boolean))].filter((field) => !field.startsWith("%:") && !dimensions.includes(field) && !measures.includes(field) && !extraMeasures.includes(field));
|
|
724
|
+
const waitingMeta = outside.length > 0 && !meta;
|
|
725
|
+
const queryDimensions = [...dimensions, ...outside.filter((field) => meta?.dimensions[field])];
|
|
793
726
|
const { rows, isLoading, error, inspectProps } = useModelQuery({
|
|
794
|
-
dimensions,
|
|
795
|
-
measures: [
|
|
727
|
+
dimensions: queryDimensions,
|
|
728
|
+
measures: [
|
|
729
|
+
...measures,
|
|
730
|
+
...extraMeasures,
|
|
731
|
+
...outside.filter((field) => meta?.measures[field] && !meta.dimensions[field])
|
|
732
|
+
],
|
|
796
733
|
ignoreFilters,
|
|
797
|
-
label: name
|
|
734
|
+
label: name,
|
|
735
|
+
skip: waitingMeta
|
|
798
736
|
});
|
|
799
737
|
const totals = useModelQuery({
|
|
800
738
|
measures,
|
|
@@ -815,7 +753,7 @@ function DataTable({ dimensions = EMPTY, measures, percentOf = EMPTY, formatDime
|
|
|
815
753
|
direction: sortDirection
|
|
816
754
|
});
|
|
817
755
|
}
|
|
818
|
-
const sortKey = sort.by?.startsWith(
|
|
756
|
+
const sortKey = sort.by?.startsWith("%:") ? sort.by.slice(2) : sort.by;
|
|
819
757
|
const sorted = sortKey ? [...rows].sort((a, b) => {
|
|
820
758
|
const [x, y] = [a[sortKey], b[sortKey]];
|
|
821
759
|
if (x === null || x === void 0) return 1;
|
|
@@ -827,7 +765,7 @@ function DataTable({ dimensions = EMPTY, measures, percentOf = EMPTY, formatDime
|
|
|
827
765
|
const dimensionText = (dimension, value) => value === null || value === void 0 ? "(vazio)" : (formatDimensions[dimension] ?? String)(value);
|
|
828
766
|
const labelOf = (column) => fieldLabel(meta, column, labels);
|
|
829
767
|
const headerOf = (column) => {
|
|
830
|
-
if (!column.startsWith(
|
|
768
|
+
if (!column.startsWith("%:")) return labelOf(column);
|
|
831
769
|
const measure = column.slice(2);
|
|
832
770
|
return measures.includes(measure) ? "%" : `% ${labelOf(measure)}`;
|
|
833
771
|
};
|
|
@@ -837,8 +775,25 @@ function DataTable({ dimensions = EMPTY, measures, percentOf = EMPTY, formatDime
|
|
|
837
775
|
const total = sums[measure];
|
|
838
776
|
return value === null || value === void 0 || !total ? null : Number(value) / total;
|
|
839
777
|
};
|
|
840
|
-
const
|
|
841
|
-
const
|
|
778
|
+
const columns = tableColumns([...dimensions, ...measures], percentOf.filter((measure) => !dimensions.includes(measure)), columnOrder);
|
|
779
|
+
const ruleStyles = (row) => {
|
|
780
|
+
if (!formatRules.length) return void 0;
|
|
781
|
+
const evaluated = { ...row };
|
|
782
|
+
for (const measure of percentOf) evaluated[percentColumn(measure)] = percentOfRow(row, measure);
|
|
783
|
+
return rowRuleStyles(formatRules, evaluated, columns);
|
|
784
|
+
};
|
|
785
|
+
const ruleSx = (style) => style && {
|
|
786
|
+
...style.color && { color: style.color },
|
|
787
|
+
...style.background && { bgcolor: style.background },
|
|
788
|
+
...style.bold && { fontWeight: 700 },
|
|
789
|
+
...style.align && { textAlign: style.align },
|
|
790
|
+
...style.verticalAlign && { verticalAlign: style.verticalAlign }
|
|
791
|
+
};
|
|
792
|
+
const isDimension = (column) => dimensions.includes(column);
|
|
793
|
+
const alignOf = (column) => align?.[column] ?? (isDimension(column) ? "left" : "right");
|
|
794
|
+
const verticalOf = (column) => verticalAlign?.[column] ?? "middle";
|
|
795
|
+
const totalLabelColumn = columns.find(isDimension);
|
|
796
|
+
const cellText = (row, column) => column.startsWith("%:") ? formatPercent(percentOfRow(row, column.slice(2))) : formats[column]?.(row[column]);
|
|
842
797
|
const toggleSort = (column) => setSort((current) => current.by === column ? {
|
|
843
798
|
by: column,
|
|
844
799
|
direction: current.direction === "asc" ? "desc" : "asc"
|
|
@@ -847,19 +802,21 @@ function DataTable({ dimensions = EMPTY, measures, percentOf = EMPTY, formatDime
|
|
|
847
802
|
direction: dimensions.includes(column) ? "asc" : "desc"
|
|
848
803
|
});
|
|
849
804
|
const totalRow = dimensions.length ? totals.rows[0] : void 0;
|
|
805
|
+
const loading = isLoading || waitingMeta;
|
|
850
806
|
const placeholder = chartPlaceholder({
|
|
851
807
|
height: 160,
|
|
852
808
|
error,
|
|
853
|
-
isLoading,
|
|
809
|
+
isLoading: loading,
|
|
854
810
|
empty: !rows.length
|
|
855
811
|
});
|
|
856
812
|
const header = (column, numeric) => /* @__PURE__ */ jsx(TableCell, {
|
|
857
|
-
align:
|
|
813
|
+
align: alignOf(column),
|
|
858
814
|
sortDirection: sort.by === column ? sort.direction : false,
|
|
859
815
|
sx: {
|
|
860
816
|
fontWeight: 700,
|
|
861
817
|
bgcolor: "#f3f3f3",
|
|
862
|
-
whiteSpace: "nowrap"
|
|
818
|
+
whiteSpace: "nowrap",
|
|
819
|
+
verticalAlign: verticalOf(column)
|
|
863
820
|
},
|
|
864
821
|
children: /* @__PURE__ */ jsx(TableSortLabel, {
|
|
865
822
|
active: sort.by === column,
|
|
@@ -874,7 +831,7 @@ function DataTable({ dimensions = EMPTY, measures, percentOf = EMPTY, formatDime
|
|
|
874
831
|
minWidth: 0,
|
|
875
832
|
position: "relative"
|
|
876
833
|
},
|
|
877
|
-
children: [
|
|
834
|
+
children: [loading && /* @__PURE__ */ jsx(CircularProgress, {
|
|
878
835
|
size: 14,
|
|
879
836
|
sx: {
|
|
880
837
|
position: "absolute",
|
|
@@ -885,17 +842,21 @@ function DataTable({ dimensions = EMPTY, measures, percentOf = EMPTY, formatDime
|
|
|
885
842
|
sx: {
|
|
886
843
|
maxHeight: height,
|
|
887
844
|
overflow: "auto",
|
|
888
|
-
opacity:
|
|
845
|
+
opacity: loading ? .5 : 1,
|
|
889
846
|
transition: "opacity .2s"
|
|
890
847
|
},
|
|
891
848
|
children: /* @__PURE__ */ jsxs(Table, {
|
|
892
849
|
size: "small",
|
|
893
850
|
stickyHeader: true,
|
|
894
|
-
sx: { "& td, & th": {
|
|
895
|
-
|
|
851
|
+
sx: { "& td, & th": {
|
|
852
|
+
fontSize: 13,
|
|
853
|
+
...cellBorders && { border: `1px solid ${borderColor}` }
|
|
854
|
+
} },
|
|
855
|
+
children: [/* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsx(TableRow, { children: columns.map((column) => header(column, !isDimension(column))) }) }), /* @__PURE__ */ jsxs(TableBody, { children: [sorted.map((row) => {
|
|
896
856
|
const value = firstDimension ? row[firstDimension] : null;
|
|
897
857
|
const clickable = clickToFilter && value !== null && value !== void 0;
|
|
898
|
-
|
|
858
|
+
const styles = ruleStyles(row);
|
|
859
|
+
return /* @__PURE__ */ jsx(TableRow, {
|
|
899
860
|
hover: true,
|
|
900
861
|
onClick: clickable ? (event) => filter.toggle(value, event) : void 0,
|
|
901
862
|
selected: clickable && filter.selected.includes(value),
|
|
@@ -903,13 +864,24 @@ function DataTable({ dimensions = EMPTY, measures, percentOf = EMPTY, formatDime
|
|
|
903
864
|
cursor: clickable ? "pointer" : void 0,
|
|
904
865
|
opacity: clickable && filter.isDimmed(value) ? .5 : 1
|
|
905
866
|
},
|
|
906
|
-
children:
|
|
907
|
-
align:
|
|
908
|
-
sx: {
|
|
867
|
+
children: columns.map((column) => isDimension(column) ? /* @__PURE__ */ jsx(TableCell, {
|
|
868
|
+
align: alignOf(column),
|
|
869
|
+
sx: {
|
|
870
|
+
verticalAlign: verticalOf(column),
|
|
871
|
+
...ruleSx(styles?.[column])
|
|
872
|
+
},
|
|
873
|
+
children: dimensionText(column, row[column])
|
|
874
|
+
}, column) : /* @__PURE__ */ jsx(TableCell, {
|
|
875
|
+
align: alignOf(column),
|
|
876
|
+
sx: {
|
|
877
|
+
whiteSpace: "nowrap",
|
|
878
|
+
verticalAlign: verticalOf(column),
|
|
879
|
+
...ruleSx(styles?.[column])
|
|
880
|
+
},
|
|
909
881
|
children: cellText(row, column)
|
|
910
|
-
}, column))
|
|
911
|
-
},
|
|
912
|
-
}), totalRow && /* @__PURE__ */
|
|
882
|
+
}, column))
|
|
883
|
+
}, queryDimensions.map((dimension) => String(row[dimension])).join("\0"));
|
|
884
|
+
}), totalRow && /* @__PURE__ */ jsx(TableRow, {
|
|
913
885
|
sx: { "& td": {
|
|
914
886
|
fontWeight: 700,
|
|
915
887
|
position: "sticky",
|
|
@@ -917,11 +889,18 @@ function DataTable({ dimensions = EMPTY, measures, percentOf = EMPTY, formatDime
|
|
|
917
889
|
bgcolor: "#f3f3f3",
|
|
918
890
|
borderTop: "2px solid #ddd"
|
|
919
891
|
} },
|
|
920
|
-
children:
|
|
921
|
-
align:
|
|
922
|
-
sx: {
|
|
923
|
-
children: column
|
|
924
|
-
}, column)
|
|
892
|
+
children: columns.map((column) => isDimension(column) ? /* @__PURE__ */ jsx(TableCell, {
|
|
893
|
+
align: alignOf(column),
|
|
894
|
+
sx: { verticalAlign: verticalOf(column) },
|
|
895
|
+
children: column === totalLabelColumn ? "Total" : ""
|
|
896
|
+
}, column) : /* @__PURE__ */ jsx(TableCell, {
|
|
897
|
+
align: alignOf(column),
|
|
898
|
+
sx: {
|
|
899
|
+
whiteSpace: "nowrap",
|
|
900
|
+
verticalAlign: verticalOf(column)
|
|
901
|
+
},
|
|
902
|
+
children: column.startsWith("%:") ? formatPercent(sums[column.slice(2)] ? 1 : null) : formats[column]?.(totalRow[column])
|
|
903
|
+
}, column))
|
|
925
904
|
})] })]
|
|
926
905
|
})
|
|
927
906
|
})]
|