@bygd/nc-report-ui 0.1.41 → 0.1.42
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/app/esm/index.js +693 -381
- package/dist/default/cjs/index.cjs +691 -381
- package/dist/default/esm/index.js +691 -382
- package/package.json +1 -1
package/dist/app/esm/index.js
CHANGED
|
@@ -77168,6 +77168,140 @@ var ArrowDownwardIcon = createSvgIcon$1(/*#__PURE__*/u("path", {
|
|
|
77168
77168
|
d: "m20 12-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8z"
|
|
77169
77169
|
}));
|
|
77170
77170
|
|
|
77171
|
+
// Shared drag-and-drop primitives for the index-identified, arrow-reorderable
|
|
77172
|
+
// lists used across the report builder (Dimensions, Metrics, Column Order).
|
|
77173
|
+
// Previously duplicated per-tab; consolidated here so all three behave and
|
|
77174
|
+
// look identical and only need to be fixed in one place.
|
|
77175
|
+
|
|
77176
|
+
const useSortableListSensors = () => useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, {
|
|
77177
|
+
coordinateGetter: sortableKeyboardCoordinates
|
|
77178
|
+
}));
|
|
77179
|
+
|
|
77180
|
+
// Builds drag/move handlers for a list where dnd-kit item ids are the array
|
|
77181
|
+
// index. `onReorder` receives the whole reordered list.
|
|
77182
|
+
const makeListReorderHandlers = (list, onReorder) => {
|
|
77183
|
+
const handleDragEnd = ({
|
|
77184
|
+
active,
|
|
77185
|
+
over
|
|
77186
|
+
}) => {
|
|
77187
|
+
if (over && active.id !== over.id) {
|
|
77188
|
+
const oldIndex = list.findIndex((_, i) => i === active.id);
|
|
77189
|
+
const newIndex = list.findIndex((_, i) => i === over.id);
|
|
77190
|
+
onReorder(arrayMove(list, oldIndex, newIndex));
|
|
77191
|
+
}
|
|
77192
|
+
};
|
|
77193
|
+
const handleMoveUp = index => {
|
|
77194
|
+
if (index > 0) {
|
|
77195
|
+
onReorder(arrayMove(list, index, index - 1));
|
|
77196
|
+
}
|
|
77197
|
+
};
|
|
77198
|
+
const handleMoveDown = index => {
|
|
77199
|
+
if (index < list.length - 1) {
|
|
77200
|
+
onReorder(arrayMove(list, index, index + 1));
|
|
77201
|
+
}
|
|
77202
|
+
};
|
|
77203
|
+
return {
|
|
77204
|
+
handleDragEnd,
|
|
77205
|
+
handleMoveUp,
|
|
77206
|
+
handleMoveDown
|
|
77207
|
+
};
|
|
77208
|
+
};
|
|
77209
|
+
|
|
77210
|
+
// Wraps DndContext + SortableContext + the vertical list container.
|
|
77211
|
+
// `itemIds` must line up with the ids passed to each item's useSortable().
|
|
77212
|
+
const SortableListContext = ({
|
|
77213
|
+
sensors,
|
|
77214
|
+
itemIds,
|
|
77215
|
+
onDragEnd,
|
|
77216
|
+
children
|
|
77217
|
+
}) => /*#__PURE__*/gn.createElement(DndContext, {
|
|
77218
|
+
sensors: sensors,
|
|
77219
|
+
collisionDetection: closestCenter,
|
|
77220
|
+
onDragEnd: onDragEnd
|
|
77221
|
+
}, /*#__PURE__*/gn.createElement(SortableContext, {
|
|
77222
|
+
items: itemIds,
|
|
77223
|
+
strategy: verticalListSortingStrategy
|
|
77224
|
+
}, /*#__PURE__*/gn.createElement(Box, {
|
|
77225
|
+
sx: {
|
|
77226
|
+
display: "flex",
|
|
77227
|
+
flexDirection: "column",
|
|
77228
|
+
gap: 1
|
|
77229
|
+
}
|
|
77230
|
+
}, children)));
|
|
77231
|
+
|
|
77232
|
+
// Per-row useSortable() wiring shared by every sortable chip/row component.
|
|
77233
|
+
const useSortableRow = id => {
|
|
77234
|
+
const {
|
|
77235
|
+
attributes,
|
|
77236
|
+
listeners,
|
|
77237
|
+
setNodeRef,
|
|
77238
|
+
transform,
|
|
77239
|
+
transition,
|
|
77240
|
+
isDragging
|
|
77241
|
+
} = useSortable({
|
|
77242
|
+
id
|
|
77243
|
+
});
|
|
77244
|
+
const style = {
|
|
77245
|
+
transform: CSS$1.Transform.toString(transform),
|
|
77246
|
+
transition,
|
|
77247
|
+
opacity: isDragging ? 0.5 : 1,
|
|
77248
|
+
display: "flex",
|
|
77249
|
+
alignItems: "center",
|
|
77250
|
+
width: "100%"
|
|
77251
|
+
};
|
|
77252
|
+
return {
|
|
77253
|
+
attributes,
|
|
77254
|
+
listeners,
|
|
77255
|
+
setNodeRef,
|
|
77256
|
+
style
|
|
77257
|
+
};
|
|
77258
|
+
};
|
|
77259
|
+
const DragHandle = ({
|
|
77260
|
+
listeners
|
|
77261
|
+
}) => /*#__PURE__*/gn.createElement(Box, _extends({}, listeners, {
|
|
77262
|
+
sx: {
|
|
77263
|
+
display: "flex",
|
|
77264
|
+
alignItems: "center",
|
|
77265
|
+
cursor: "grab",
|
|
77266
|
+
"&:active": {
|
|
77267
|
+
cursor: "grabbing"
|
|
77268
|
+
}
|
|
77269
|
+
}
|
|
77270
|
+
}), /*#__PURE__*/gn.createElement(DragIndicatorIcon, {
|
|
77271
|
+
sx: {
|
|
77272
|
+
cursor: "grab",
|
|
77273
|
+
color: "rgba(110, 110, 110, 0.62)"
|
|
77274
|
+
}
|
|
77275
|
+
}));
|
|
77276
|
+
const MoveButtons = ({
|
|
77277
|
+
onMoveUp,
|
|
77278
|
+
onMoveDown,
|
|
77279
|
+
isFirst,
|
|
77280
|
+
isLast
|
|
77281
|
+
}) => /*#__PURE__*/gn.createElement(Box, {
|
|
77282
|
+
className: "hover-icons",
|
|
77283
|
+
sx: {
|
|
77284
|
+
display: "flex",
|
|
77285
|
+
gap: 0.5,
|
|
77286
|
+
opacity: 0,
|
|
77287
|
+
transition: "opacity 0.2s"
|
|
77288
|
+
}
|
|
77289
|
+
}, /*#__PURE__*/gn.createElement(IconButton, {
|
|
77290
|
+
size: "small",
|
|
77291
|
+
onClick: onMoveUp,
|
|
77292
|
+
disabled: isFirst,
|
|
77293
|
+
"aria-label": "move up"
|
|
77294
|
+
}, /*#__PURE__*/gn.createElement(ArrowUpwardIcon, {
|
|
77295
|
+
fontSize: "small"
|
|
77296
|
+
})), /*#__PURE__*/gn.createElement(IconButton, {
|
|
77297
|
+
size: "small",
|
|
77298
|
+
onClick: onMoveDown,
|
|
77299
|
+
disabled: isLast,
|
|
77300
|
+
"aria-label": "move down"
|
|
77301
|
+
}, /*#__PURE__*/gn.createElement(ArrowDownwardIcon, {
|
|
77302
|
+
fontSize: "small"
|
|
77303
|
+
})));
|
|
77304
|
+
|
|
77171
77305
|
var SortIcon = createSvgIcon$1(/*#__PURE__*/u("path", {
|
|
77172
77306
|
d: "M3 18h6v-2H3zM3 6v2h18V6zm0 7h12v-2H3z"
|
|
77173
77307
|
}));
|
|
@@ -77203,6 +77337,50 @@ const interpolateTitle = (template, params) => {
|
|
|
77203
77337
|
});
|
|
77204
77338
|
};
|
|
77205
77339
|
|
|
77340
|
+
// Resolves the final column display order for the results grid: dimensions,
|
|
77341
|
+
// computed dimensions, and metrics combined into a single list.
|
|
77342
|
+
//
|
|
77343
|
+
// `columnOrder` is the optional, explicitly-arranged order (raw entries
|
|
77344
|
+
// `{ type: 'dimension' | 'computed' | 'metric', key }`) set on the Column
|
|
77345
|
+
// Order tab. When present, its entries come first, in that order. Anything
|
|
77346
|
+
// not covered by it (new fields added since it was last touched, or when
|
|
77347
|
+
// it's empty/undefined because no custom order has ever been defined) is
|
|
77348
|
+
// appended in the pre-existing default order: dimensions/computed (in their
|
|
77349
|
+
// own `dimensionOrder`) first, then metrics (in their own array order) —
|
|
77350
|
+
// this keeps behavior unchanged for reports that never define a column order.
|
|
77351
|
+
const resolveColumnOrder = (columnOrder, orderedDimensionItems, metrics) => {
|
|
77352
|
+
// Normalise every candidate item to a uniform { kind, key, data } shape
|
|
77353
|
+
// up front, so callers (e.g. the Column Order tab) can always read
|
|
77354
|
+
// `.key` regardless of where the item came from.
|
|
77355
|
+
const normalizedDimensionItems = orderedDimensionItems.map(item => ({
|
|
77356
|
+
kind: item.kind,
|
|
77357
|
+
key: item.kind === 'computed' ? item.data.name : item.data.fullPath,
|
|
77358
|
+
data: item.data
|
|
77359
|
+
}));
|
|
77360
|
+
const normalizedMetricItems = metrics.map(m => ({
|
|
77361
|
+
kind: 'metric',
|
|
77362
|
+
key: m.fullPath,
|
|
77363
|
+
data: m
|
|
77364
|
+
}));
|
|
77365
|
+
const byKey = Object.fromEntries([...normalizedDimensionItems, ...normalizedMetricItems].map(item => [`${item.kind}:${item.key}`, item]));
|
|
77366
|
+
const resolved = [];
|
|
77367
|
+
const seen = new Set();
|
|
77368
|
+
(columnOrder || []).forEach(entry => {
|
|
77369
|
+
const seenKey = `${entry.type}:${entry.key}`;
|
|
77370
|
+
if (seen.has(seenKey) || !byKey[seenKey]) return;
|
|
77371
|
+
resolved.push(byKey[seenKey]);
|
|
77372
|
+
seen.add(seenKey);
|
|
77373
|
+
});
|
|
77374
|
+
[...normalizedDimensionItems, ...normalizedMetricItems].forEach(item => {
|
|
77375
|
+
const seenKey = `${item.kind}:${item.key}`;
|
|
77376
|
+
if (!seen.has(seenKey)) {
|
|
77377
|
+
resolved.push(item);
|
|
77378
|
+
seen.add(seenKey);
|
|
77379
|
+
}
|
|
77380
|
+
});
|
|
77381
|
+
return resolved;
|
|
77382
|
+
};
|
|
77383
|
+
|
|
77206
77384
|
// Sortable Chip Component
|
|
77207
77385
|
const SortableChip$1 = ({
|
|
77208
77386
|
id,
|
|
@@ -77231,20 +77409,8 @@ const SortableChip$1 = ({
|
|
|
77231
77409
|
attributes,
|
|
77232
77410
|
listeners,
|
|
77233
77411
|
setNodeRef,
|
|
77234
|
-
|
|
77235
|
-
|
|
77236
|
-
isDragging
|
|
77237
|
-
} = useSortable({
|
|
77238
|
-
id
|
|
77239
|
-
});
|
|
77240
|
-
const style = {
|
|
77241
|
-
transform: CSS$1.Transform.toString(transform),
|
|
77242
|
-
transition,
|
|
77243
|
-
opacity: isDragging ? 0.5 : 1,
|
|
77244
|
-
display: "flex",
|
|
77245
|
-
alignItems: "center",
|
|
77246
|
-
width: "100%"
|
|
77247
|
-
};
|
|
77412
|
+
style
|
|
77413
|
+
} = useSortableRow(id);
|
|
77248
77414
|
|
|
77249
77415
|
// Focus input when entering edit mode
|
|
77250
77416
|
h(() => {
|
|
@@ -77353,21 +77519,9 @@ const SortableChip$1 = ({
|
|
|
77353
77519
|
opacity: 1 // show icons on hover
|
|
77354
77520
|
}
|
|
77355
77521
|
}
|
|
77356
|
-
}, /*#__PURE__*/gn.createElement(
|
|
77357
|
-
|
|
77358
|
-
|
|
77359
|
-
alignItems: "center",
|
|
77360
|
-
cursor: "grab",
|
|
77361
|
-
"&:active": {
|
|
77362
|
-
cursor: "grabbing"
|
|
77363
|
-
}
|
|
77364
|
-
}
|
|
77365
|
-
}), /*#__PURE__*/gn.createElement(DragIndicatorIcon, {
|
|
77366
|
-
sx: {
|
|
77367
|
-
cursor: "grab",
|
|
77368
|
-
color: "rgba(110, 110, 110, 0.62)"
|
|
77369
|
-
}
|
|
77370
|
-
})), !isEditing ? /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(Box, {
|
|
77522
|
+
}, /*#__PURE__*/gn.createElement(DragHandle, {
|
|
77523
|
+
listeners: listeners
|
|
77524
|
+
}), !isEditing ? /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(Box, {
|
|
77371
77525
|
sx: {
|
|
77372
77526
|
minWidth: 0
|
|
77373
77527
|
}
|
|
@@ -77430,29 +77584,12 @@ const SortableChip$1 = ({
|
|
|
77430
77584
|
sx: {
|
|
77431
77585
|
flex: 1
|
|
77432
77586
|
}
|
|
77433
|
-
}), /*#__PURE__*/gn.createElement(
|
|
77434
|
-
|
|
77435
|
-
|
|
77436
|
-
|
|
77437
|
-
|
|
77438
|
-
|
|
77439
|
-
transition: "opacity 0.2s"
|
|
77440
|
-
}
|
|
77441
|
-
}, /*#__PURE__*/gn.createElement(IconButton, {
|
|
77442
|
-
size: "small",
|
|
77443
|
-
onClick: onMoveUp,
|
|
77444
|
-
disabled: isFirst,
|
|
77445
|
-
"aria-label": "move up"
|
|
77446
|
-
}, /*#__PURE__*/gn.createElement(ArrowUpwardIcon, {
|
|
77447
|
-
fontSize: "small"
|
|
77448
|
-
})), /*#__PURE__*/gn.createElement(IconButton, {
|
|
77449
|
-
size: "small",
|
|
77450
|
-
onClick: onMoveDown,
|
|
77451
|
-
disabled: isLast,
|
|
77452
|
-
"aria-label": "move down"
|
|
77453
|
-
}, /*#__PURE__*/gn.createElement(ArrowDownwardIcon, {
|
|
77454
|
-
fontSize: "small"
|
|
77455
|
-
})))) : /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(TextField, {
|
|
77587
|
+
}), /*#__PURE__*/gn.createElement(MoveButtons, {
|
|
77588
|
+
onMoveUp: onMoveUp,
|
|
77589
|
+
onMoveDown: onMoveDown,
|
|
77590
|
+
isFirst: isFirst,
|
|
77591
|
+
isLast: isLast
|
|
77592
|
+
})) : /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(TextField, {
|
|
77456
77593
|
inputRef: inputRef,
|
|
77457
77594
|
value: editValue,
|
|
77458
77595
|
onChange: e => setEditValue(e.target.value),
|
|
@@ -77528,24 +77665,12 @@ const SortableComputedChip = ({
|
|
|
77528
77665
|
attributes,
|
|
77529
77666
|
listeners,
|
|
77530
77667
|
setNodeRef,
|
|
77531
|
-
|
|
77532
|
-
|
|
77533
|
-
isDragging
|
|
77534
|
-
} = useSortable({
|
|
77535
|
-
id
|
|
77536
|
-
});
|
|
77668
|
+
style
|
|
77669
|
+
} = useSortableRow(id);
|
|
77537
77670
|
const [isEditing, setIsEditing] = d(false);
|
|
77538
77671
|
const [editName, setEditName] = d("");
|
|
77539
77672
|
const [editValue, setEditValue] = d("");
|
|
77540
77673
|
const containerRef = A$1(null);
|
|
77541
|
-
const style = {
|
|
77542
|
-
transform: CSS$1.Transform.toString(transform),
|
|
77543
|
-
transition,
|
|
77544
|
-
opacity: isDragging ? 0.5 : 1,
|
|
77545
|
-
display: "flex",
|
|
77546
|
-
alignItems: "center",
|
|
77547
|
-
width: "100%"
|
|
77548
|
-
};
|
|
77549
77674
|
|
|
77550
77675
|
// Handle click outside to cancel
|
|
77551
77676
|
h(() => {
|
|
@@ -77608,21 +77733,9 @@ const SortableComputedChip = ({
|
|
|
77608
77733
|
opacity: 1
|
|
77609
77734
|
}
|
|
77610
77735
|
}
|
|
77611
|
-
}, /*#__PURE__*/gn.createElement(
|
|
77612
|
-
|
|
77613
|
-
|
|
77614
|
-
alignItems: "center",
|
|
77615
|
-
cursor: "grab",
|
|
77616
|
-
"&:active": {
|
|
77617
|
-
cursor: "grabbing"
|
|
77618
|
-
}
|
|
77619
|
-
}
|
|
77620
|
-
}), /*#__PURE__*/gn.createElement(DragIndicatorIcon, {
|
|
77621
|
-
sx: {
|
|
77622
|
-
cursor: "grab",
|
|
77623
|
-
color: "rgba(110, 110, 110, 0.62)"
|
|
77624
|
-
}
|
|
77625
|
-
})), !isEditing ? /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(Chip, {
|
|
77736
|
+
}, /*#__PURE__*/gn.createElement(DragHandle, {
|
|
77737
|
+
listeners: listeners
|
|
77738
|
+
}), !isEditing ? /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(Chip, {
|
|
77626
77739
|
icon: /*#__PURE__*/gn.createElement(FunctionsIcon, {
|
|
77627
77740
|
sx: {
|
|
77628
77741
|
fontSize: "15px !important"
|
|
@@ -77686,29 +77799,12 @@ const SortableComputedChip = ({
|
|
|
77686
77799
|
sx: {
|
|
77687
77800
|
flex: 1
|
|
77688
77801
|
}
|
|
77689
|
-
}), /*#__PURE__*/gn.createElement(
|
|
77690
|
-
|
|
77691
|
-
|
|
77692
|
-
|
|
77693
|
-
|
|
77694
|
-
|
|
77695
|
-
transition: "opacity 0.2s"
|
|
77696
|
-
}
|
|
77697
|
-
}, /*#__PURE__*/gn.createElement(IconButton, {
|
|
77698
|
-
size: "small",
|
|
77699
|
-
onClick: onMoveUp,
|
|
77700
|
-
disabled: isFirst,
|
|
77701
|
-
"aria-label": "move up"
|
|
77702
|
-
}, /*#__PURE__*/gn.createElement(ArrowUpwardIcon, {
|
|
77703
|
-
fontSize: "small"
|
|
77704
|
-
})), /*#__PURE__*/gn.createElement(IconButton, {
|
|
77705
|
-
size: "small",
|
|
77706
|
-
onClick: onMoveDown,
|
|
77707
|
-
disabled: isLast,
|
|
77708
|
-
"aria-label": "move down"
|
|
77709
|
-
}, /*#__PURE__*/gn.createElement(ArrowDownwardIcon, {
|
|
77710
|
-
fontSize: "small"
|
|
77711
|
-
})))) : /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(TextField, {
|
|
77802
|
+
}), /*#__PURE__*/gn.createElement(MoveButtons, {
|
|
77803
|
+
onMoveUp: onMoveUp,
|
|
77804
|
+
onMoveDown: onMoveDown,
|
|
77805
|
+
isFirst: isFirst,
|
|
77806
|
+
isLast: isLast
|
|
77807
|
+
})) : /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(TextField, {
|
|
77712
77808
|
value: editName,
|
|
77713
77809
|
onChange: e => setEditName(e.target.value),
|
|
77714
77810
|
onKeyDown: handleKeyDown,
|
|
@@ -77793,9 +77889,7 @@ const Dimensions = ({
|
|
|
77793
77889
|
const [computedValue, setComputedValue] = d("");
|
|
77794
77890
|
|
|
77795
77891
|
// Setup drag and drop sensors
|
|
77796
|
-
const sensors =
|
|
77797
|
-
coordinateGetter: sortableKeyboardCoordinates
|
|
77798
|
-
}));
|
|
77892
|
+
const sensors = useSortableListSensors();
|
|
77799
77893
|
|
|
77800
77894
|
// Resolve the unified dimensionOrder into the actual dimension / computed
|
|
77801
77895
|
// dimension objects, so dimensions and computed dimensions can be shown
|
|
@@ -77812,32 +77906,12 @@ const Dimensions = ({
|
|
|
77812
77906
|
data: computedDimensionByName[entry.key]
|
|
77813
77907
|
}).filter(item => item.data);
|
|
77814
77908
|
|
|
77815
|
-
// Handle drag
|
|
77816
|
-
const
|
|
77817
|
-
|
|
77818
|
-
|
|
77819
|
-
|
|
77820
|
-
|
|
77821
|
-
if (over && active.id !== over.id) {
|
|
77822
|
-
const oldIndex = dimensionOrder.findIndex((_, i) => i === active.id);
|
|
77823
|
-
const newIndex = dimensionOrder.findIndex((_, i) => i === over.id);
|
|
77824
|
-
onReorderDimensionItems(arrayMove(dimensionOrder, oldIndex, newIndex));
|
|
77825
|
-
}
|
|
77826
|
-
};
|
|
77827
|
-
|
|
77828
|
-
// Handle move up
|
|
77829
|
-
const handleMoveUp = index => {
|
|
77830
|
-
if (index > 0) {
|
|
77831
|
-
onReorderDimensionItems(arrayMove(dimensionOrder, index, index - 1));
|
|
77832
|
-
}
|
|
77833
|
-
};
|
|
77834
|
-
|
|
77835
|
-
// Handle move down
|
|
77836
|
-
const handleMoveDown = index => {
|
|
77837
|
-
if (index < dimensionOrder.length - 1) {
|
|
77838
|
-
onReorderDimensionItems(arrayMove(dimensionOrder, index, index + 1));
|
|
77839
|
-
}
|
|
77840
|
-
};
|
|
77909
|
+
// Handle drag/move for the unified list
|
|
77910
|
+
const {
|
|
77911
|
+
handleDragEnd,
|
|
77912
|
+
handleMoveUp,
|
|
77913
|
+
handleMoveDown
|
|
77914
|
+
} = makeListReorderHandlers(dimensionOrder, onReorderDimensionItems);
|
|
77841
77915
|
|
|
77842
77916
|
// Handle sort order change (dimensions only - computed dimensions have no order_by)
|
|
77843
77917
|
const handleSortOrderChange = (fullPath, sortOrder) => {
|
|
@@ -78330,19 +78404,10 @@ const Dimensions = ({
|
|
|
78330
78404
|
color: "#666",
|
|
78331
78405
|
marginBottom: 2
|
|
78332
78406
|
}
|
|
78333
|
-
}, "Drag to reorder or use arrows"), /*#__PURE__*/gn.createElement(
|
|
78407
|
+
}, "Drag to reorder or use arrows"), /*#__PURE__*/gn.createElement(SortableListContext, {
|
|
78334
78408
|
sensors: sensors,
|
|
78335
|
-
|
|
78409
|
+
itemIds: orderedItems.map((_, index) => index),
|
|
78336
78410
|
onDragEnd: handleDragEnd
|
|
78337
|
-
}, /*#__PURE__*/gn.createElement(SortableContext, {
|
|
78338
|
-
items: orderedItems.map((_, index) => index),
|
|
78339
|
-
strategy: verticalListSortingStrategy
|
|
78340
|
-
}, /*#__PURE__*/gn.createElement(Box, {
|
|
78341
|
-
sx: {
|
|
78342
|
-
display: "flex",
|
|
78343
|
-
flexDirection: "column",
|
|
78344
|
-
gap: 1
|
|
78345
|
-
}
|
|
78346
78411
|
}, orderedItems.map((item, index) => item.kind === "dimension" ? /*#__PURE__*/gn.createElement(SortableChip$1, {
|
|
78347
78412
|
key: `dim-${item.key}`,
|
|
78348
78413
|
id: index,
|
|
@@ -78372,7 +78437,7 @@ const Dimensions = ({
|
|
|
78372
78437
|
onMoveDown: () => handleMoveDown(index),
|
|
78373
78438
|
isFirst: index === 0,
|
|
78374
78439
|
isLast: index === orderedItems.length - 1
|
|
78375
|
-
})))))
|
|
78440
|
+
})))));
|
|
78376
78441
|
};
|
|
78377
78442
|
|
|
78378
78443
|
var StorageIcon = createSvgIcon$1(/*#__PURE__*/u("path", {
|
|
@@ -78459,20 +78524,8 @@ const SortableChip = ({
|
|
|
78459
78524
|
attributes,
|
|
78460
78525
|
listeners,
|
|
78461
78526
|
setNodeRef,
|
|
78462
|
-
|
|
78463
|
-
|
|
78464
|
-
isDragging
|
|
78465
|
-
} = useSortable({
|
|
78466
|
-
id
|
|
78467
|
-
});
|
|
78468
|
-
const style = {
|
|
78469
|
-
transform: CSS$1.Transform.toString(transform),
|
|
78470
|
-
transition,
|
|
78471
|
-
opacity: isDragging ? 0.5 : 1,
|
|
78472
|
-
display: 'flex',
|
|
78473
|
-
alignItems: 'center',
|
|
78474
|
-
width: '100%'
|
|
78475
|
-
};
|
|
78527
|
+
style
|
|
78528
|
+
} = useSortableRow(id);
|
|
78476
78529
|
|
|
78477
78530
|
// Focus input when entering edit mode
|
|
78478
78531
|
h(() => {
|
|
@@ -78550,21 +78603,9 @@ const SortableChip = ({
|
|
|
78550
78603
|
opacity: 1 // show icons on hover
|
|
78551
78604
|
}
|
|
78552
78605
|
}
|
|
78553
|
-
}, /*#__PURE__*/gn.createElement(
|
|
78554
|
-
|
|
78555
|
-
|
|
78556
|
-
alignItems: "center",
|
|
78557
|
-
cursor: "grab",
|
|
78558
|
-
"&:active": {
|
|
78559
|
-
cursor: "grabbing"
|
|
78560
|
-
}
|
|
78561
|
-
}
|
|
78562
|
-
}), /*#__PURE__*/gn.createElement(DragIndicatorIcon, {
|
|
78563
|
-
sx: {
|
|
78564
|
-
cursor: "grab",
|
|
78565
|
-
color: "rgba(110, 110, 110, 0.62)"
|
|
78566
|
-
}
|
|
78567
|
-
})), !isEditing ? /*#__PURE__*/gn.createElement(gn.Fragment, null, source && /*#__PURE__*/gn.createElement(Box, {
|
|
78606
|
+
}, /*#__PURE__*/gn.createElement(DragHandle, {
|
|
78607
|
+
listeners: listeners
|
|
78608
|
+
}), !isEditing ? /*#__PURE__*/gn.createElement(gn.Fragment, null, source && /*#__PURE__*/gn.createElement(Box, {
|
|
78568
78609
|
sx: {
|
|
78569
78610
|
display: 'flex',
|
|
78570
78611
|
alignItems: 'center',
|
|
@@ -78626,29 +78667,12 @@ const SortableChip = ({
|
|
|
78626
78667
|
sx: {
|
|
78627
78668
|
flex: 1
|
|
78628
78669
|
}
|
|
78629
|
-
}), /*#__PURE__*/gn.createElement(
|
|
78630
|
-
|
|
78631
|
-
|
|
78632
|
-
|
|
78633
|
-
|
|
78634
|
-
|
|
78635
|
-
transition: "opacity 0.2s"
|
|
78636
|
-
}
|
|
78637
|
-
}, /*#__PURE__*/gn.createElement(IconButton, {
|
|
78638
|
-
size: "small",
|
|
78639
|
-
onClick: onMoveUp,
|
|
78640
|
-
disabled: isFirst,
|
|
78641
|
-
"aria-label": "move up"
|
|
78642
|
-
}, /*#__PURE__*/gn.createElement(ArrowUpwardIcon, {
|
|
78643
|
-
fontSize: "small"
|
|
78644
|
-
})), /*#__PURE__*/gn.createElement(IconButton, {
|
|
78645
|
-
size: "small",
|
|
78646
|
-
onClick: onMoveDown,
|
|
78647
|
-
disabled: isLast,
|
|
78648
|
-
"aria-label": "move down"
|
|
78649
|
-
}, /*#__PURE__*/gn.createElement(ArrowDownwardIcon, {
|
|
78650
|
-
fontSize: "small"
|
|
78651
|
-
})))) : /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(TextField, {
|
|
78670
|
+
}), /*#__PURE__*/gn.createElement(MoveButtons, {
|
|
78671
|
+
onMoveUp: onMoveUp,
|
|
78672
|
+
onMoveDown: onMoveDown,
|
|
78673
|
+
isFirst: isFirst,
|
|
78674
|
+
isLast: isLast
|
|
78675
|
+
})) : /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(TextField, {
|
|
78652
78676
|
inputRef: inputRef,
|
|
78653
78677
|
value: editValue,
|
|
78654
78678
|
onChange: e => setEditValue(e.target.value),
|
|
@@ -78723,39 +78747,14 @@ const Metrics = ({
|
|
|
78723
78747
|
const [selectedMetric, setSelectedMetric] = d('');
|
|
78724
78748
|
|
|
78725
78749
|
// Setup drag and drop sensors
|
|
78726
|
-
const sensors =
|
|
78727
|
-
coordinateGetter: sortableKeyboardCoordinates
|
|
78728
|
-
}));
|
|
78729
|
-
|
|
78730
|
-
// Handle drag end
|
|
78731
|
-
const handleDragEnd = event => {
|
|
78732
|
-
const {
|
|
78733
|
-
active,
|
|
78734
|
-
over
|
|
78735
|
-
} = event;
|
|
78736
|
-
if (over && active.id !== over.id) {
|
|
78737
|
-
const oldIndex = savedMetrics.findIndex((_, i) => i === active.id);
|
|
78738
|
-
const newIndex = savedMetrics.findIndex((_, i) => i === over.id);
|
|
78739
|
-
const newOrder = arrayMove(savedMetrics, oldIndex, newIndex);
|
|
78740
|
-
onReorderMetrics(newOrder);
|
|
78741
|
-
}
|
|
78742
|
-
};
|
|
78743
|
-
|
|
78744
|
-
// Handle move up
|
|
78745
|
-
const handleMoveUp = index => {
|
|
78746
|
-
if (index > 0) {
|
|
78747
|
-
const newOrder = arrayMove(savedMetrics, index, index - 1);
|
|
78748
|
-
onReorderMetrics(newOrder);
|
|
78749
|
-
}
|
|
78750
|
-
};
|
|
78750
|
+
const sensors = useSortableListSensors();
|
|
78751
78751
|
|
|
78752
|
-
// Handle move
|
|
78753
|
-
const
|
|
78754
|
-
|
|
78755
|
-
|
|
78756
|
-
|
|
78757
|
-
|
|
78758
|
-
};
|
|
78752
|
+
// Handle drag/move
|
|
78753
|
+
const {
|
|
78754
|
+
handleDragEnd,
|
|
78755
|
+
handleMoveUp,
|
|
78756
|
+
handleMoveDown
|
|
78757
|
+
} = makeListReorderHandlers(savedMetrics, onReorderMetrics);
|
|
78759
78758
|
|
|
78760
78759
|
// Get the current provider based on selection chain
|
|
78761
78760
|
const getCurrentProvider = () => {
|
|
@@ -79094,19 +79093,10 @@ const Metrics = ({
|
|
|
79094
79093
|
marginBottom: 1,
|
|
79095
79094
|
color: "rgb(37, 37, 37)"
|
|
79096
79095
|
}
|
|
79097
|
-
}, "Saved Metrics"), /*#__PURE__*/gn.createElement(
|
|
79096
|
+
}, "Saved Metrics"), /*#__PURE__*/gn.createElement(SortableListContext, {
|
|
79098
79097
|
sensors: sensors,
|
|
79099
|
-
|
|
79098
|
+
itemIds: savedMetrics.map((_, index) => index),
|
|
79100
79099
|
onDragEnd: handleDragEnd
|
|
79101
|
-
}, /*#__PURE__*/gn.createElement(SortableContext, {
|
|
79102
|
-
items: savedMetrics.map((_, index) => index),
|
|
79103
|
-
strategy: verticalListSortingStrategy
|
|
79104
|
-
}, /*#__PURE__*/gn.createElement(Box, {
|
|
79105
|
-
sx: {
|
|
79106
|
-
display: 'flex',
|
|
79107
|
-
flexDirection: 'column',
|
|
79108
|
-
gap: 1
|
|
79109
|
-
}
|
|
79110
79100
|
}, savedMetrics.map((metric, index) => /*#__PURE__*/gn.createElement(SortableChip, {
|
|
79111
79101
|
key: index,
|
|
79112
79102
|
id: index,
|
|
@@ -79123,7 +79113,7 @@ const Metrics = ({
|
|
|
79123
79113
|
onUpdateTitle: onUpdateTitle,
|
|
79124
79114
|
onResetTitle: onResetTitle,
|
|
79125
79115
|
source: metric.metric?.source
|
|
79126
|
-
})))))
|
|
79116
|
+
})))));
|
|
79127
79117
|
};
|
|
79128
79118
|
|
|
79129
79119
|
var FilterAltIcon = createSvgIcon$1(/*#__PURE__*/u("path", {
|
|
@@ -91686,6 +91676,209 @@ const Filters = ({
|
|
|
91686
91676
|
}))));
|
|
91687
91677
|
};
|
|
91688
91678
|
|
|
91679
|
+
var CalculateIcon = createSvgIcon$1(/*#__PURE__*/u("path", {
|
|
91680
|
+
d: "M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2m-5.97 4.06L14.09 6l1.41 1.41L16.91 6l1.06 1.06-1.41 1.41 1.41 1.41-1.06 1.06-1.41-1.4-1.41 1.41-1.06-1.06 1.41-1.41zm-6.78.66h5v1.5h-5zM11.5 16h-2v2H8v-2H6v-1.5h2v-2h1.5v2h2zm6.5 1.25h-5v-1.5h5zm0-2.5h-5v-1.5h5z"
|
|
91681
|
+
}));
|
|
91682
|
+
|
|
91683
|
+
const KIND_LABEL = {
|
|
91684
|
+
dimension: "Dimension",
|
|
91685
|
+
computed: "Computed",
|
|
91686
|
+
metric: "Metric"
|
|
91687
|
+
};
|
|
91688
|
+
const KindChip = ({
|
|
91689
|
+
kind,
|
|
91690
|
+
source
|
|
91691
|
+
}) => {
|
|
91692
|
+
if (kind === "metric") {
|
|
91693
|
+
return /*#__PURE__*/gn.createElement(Box, {
|
|
91694
|
+
sx: {
|
|
91695
|
+
display: "flex",
|
|
91696
|
+
alignItems: "center",
|
|
91697
|
+
flexShrink: 0
|
|
91698
|
+
}
|
|
91699
|
+
}, /*#__PURE__*/gn.createElement(MetricSourceIcon, {
|
|
91700
|
+
source: source
|
|
91701
|
+
}));
|
|
91702
|
+
}
|
|
91703
|
+
return /*#__PURE__*/gn.createElement(Chip, {
|
|
91704
|
+
icon: kind === "computed" ? /*#__PURE__*/gn.createElement(CalculateIcon, {
|
|
91705
|
+
sx: {
|
|
91706
|
+
fontSize: "15px !important"
|
|
91707
|
+
}
|
|
91708
|
+
}) : undefined,
|
|
91709
|
+
label: KIND_LABEL[kind],
|
|
91710
|
+
size: "small",
|
|
91711
|
+
sx: {
|
|
91712
|
+
fontWeight: 500,
|
|
91713
|
+
backgroundColor: "rgba(70, 134, 128, 0.1)",
|
|
91714
|
+
color: "rgb(70, 134, 128)",
|
|
91715
|
+
flexShrink: 0
|
|
91716
|
+
}
|
|
91717
|
+
});
|
|
91718
|
+
};
|
|
91719
|
+
|
|
91720
|
+
// Read-only sortable row: this tab only reorders columns. Adding, removing,
|
|
91721
|
+
// and renaming stay on the Dimensions/Metrics tabs so there's one place each
|
|
91722
|
+
// field is managed.
|
|
91723
|
+
const SortableColumnRow = ({
|
|
91724
|
+
id,
|
|
91725
|
+
label,
|
|
91726
|
+
fullPath,
|
|
91727
|
+
kind,
|
|
91728
|
+
source,
|
|
91729
|
+
onMoveUp,
|
|
91730
|
+
onMoveDown,
|
|
91731
|
+
isFirst,
|
|
91732
|
+
isLast
|
|
91733
|
+
}) => {
|
|
91734
|
+
const reportingContext = useReportingContextOptional();
|
|
91735
|
+
const parameters = reportingContext?.parameters;
|
|
91736
|
+
const {
|
|
91737
|
+
attributes,
|
|
91738
|
+
listeners,
|
|
91739
|
+
setNodeRef,
|
|
91740
|
+
style
|
|
91741
|
+
} = useSortableRow(id);
|
|
91742
|
+
const displayLabel = interpolateTitle(label, parameters);
|
|
91743
|
+
return /*#__PURE__*/gn.createElement("div", _extends({
|
|
91744
|
+
ref: setNodeRef,
|
|
91745
|
+
style: style
|
|
91746
|
+
}, attributes), /*#__PURE__*/gn.createElement(Box, {
|
|
91747
|
+
sx: {
|
|
91748
|
+
display: "flex",
|
|
91749
|
+
alignItems: "center",
|
|
91750
|
+
width: "100%",
|
|
91751
|
+
gap: 1,
|
|
91752
|
+
backgroundColor: "white",
|
|
91753
|
+
border: "1px solid #e0e0e0",
|
|
91754
|
+
borderRadius: 2,
|
|
91755
|
+
padding: 1,
|
|
91756
|
+
transition: "transform 0.2s ease, box-shadow 0.2s ease",
|
|
91757
|
+
"&:hover .hover-icons": {
|
|
91758
|
+
opacity: 1
|
|
91759
|
+
}
|
|
91760
|
+
}
|
|
91761
|
+
}, /*#__PURE__*/gn.createElement(DragHandle, {
|
|
91762
|
+
listeners: listeners
|
|
91763
|
+
}), /*#__PURE__*/gn.createElement(KindChip, {
|
|
91764
|
+
kind: kind,
|
|
91765
|
+
source: source
|
|
91766
|
+
}), /*#__PURE__*/gn.createElement(Tooltip, {
|
|
91767
|
+
title: fullPath,
|
|
91768
|
+
arrow: true,
|
|
91769
|
+
placement: "top"
|
|
91770
|
+
}, /*#__PURE__*/gn.createElement(Typography, {
|
|
91771
|
+
variant: "h6",
|
|
91772
|
+
sx: {
|
|
91773
|
+
fontWeight: 500,
|
|
91774
|
+
color: "#1a1a1a",
|
|
91775
|
+
fontSize: "14px",
|
|
91776
|
+
whiteSpace: "nowrap",
|
|
91777
|
+
overflow: "hidden",
|
|
91778
|
+
textOverflow: "ellipsis"
|
|
91779
|
+
}
|
|
91780
|
+
}, displayLabel)), /*#__PURE__*/gn.createElement(Box, {
|
|
91781
|
+
sx: {
|
|
91782
|
+
flex: 1
|
|
91783
|
+
}
|
|
91784
|
+
}), /*#__PURE__*/gn.createElement(MoveButtons, {
|
|
91785
|
+
onMoveUp: onMoveUp,
|
|
91786
|
+
onMoveDown: onMoveDown,
|
|
91787
|
+
isFirst: isFirst,
|
|
91788
|
+
isLast: isLast
|
|
91789
|
+
})));
|
|
91790
|
+
};
|
|
91791
|
+
const ColumnOrder = ({
|
|
91792
|
+
orderedDimensionItems = [],
|
|
91793
|
+
metrics = [],
|
|
91794
|
+
columnOrder = [],
|
|
91795
|
+
onReorderColumnItems,
|
|
91796
|
+
titleOverrides = {
|
|
91797
|
+
dimensions: {},
|
|
91798
|
+
metrics: {}
|
|
91799
|
+
}
|
|
91800
|
+
}) => {
|
|
91801
|
+
const isCustomized = columnOrder.length > 0;
|
|
91802
|
+
const resolvedItems = resolveColumnOrder(columnOrder, orderedDimensionItems, metrics);
|
|
91803
|
+
const sensors = useSortableListSensors();
|
|
91804
|
+
const reorderResolved = newResolvedItems => {
|
|
91805
|
+
onReorderColumnItems(newResolvedItems.map(item => ({
|
|
91806
|
+
type: item.kind,
|
|
91807
|
+
key: item.key
|
|
91808
|
+
})));
|
|
91809
|
+
};
|
|
91810
|
+
const {
|
|
91811
|
+
handleDragEnd,
|
|
91812
|
+
handleMoveUp,
|
|
91813
|
+
handleMoveDown
|
|
91814
|
+
} = makeListReorderHandlers(resolvedItems, reorderResolved);
|
|
91815
|
+
const labelFor = item => {
|
|
91816
|
+
if (item.kind === "computed") return item.data.name;
|
|
91817
|
+
if (item.kind === "metric") {
|
|
91818
|
+
return titleOverrides.metrics[item.key] || item.data.metricTitle || item.key;
|
|
91819
|
+
}
|
|
91820
|
+
return titleOverrides.dimensions[item.key] || item.data.dimensionTitle || item.key;
|
|
91821
|
+
};
|
|
91822
|
+
return /*#__PURE__*/gn.createElement("div", null, /*#__PURE__*/gn.createElement(Box, {
|
|
91823
|
+
sx: {
|
|
91824
|
+
display: "flex",
|
|
91825
|
+
justifyContent: "space-between",
|
|
91826
|
+
alignItems: "flex-start",
|
|
91827
|
+
mb: 2,
|
|
91828
|
+
gap: 2
|
|
91829
|
+
}
|
|
91830
|
+
}, /*#__PURE__*/gn.createElement(Typography, {
|
|
91831
|
+
sx: {
|
|
91832
|
+
fontSize: "13px",
|
|
91833
|
+
color: "#666"
|
|
91834
|
+
}
|
|
91835
|
+
}, "Optional: define a single order for all columns (dimensions, computed dimensions and metrics together) in the results grid. Drag to reorder or use the arrows. Newly added dimensions/metrics are appended to the end automatically. Without a custom order, columns follow the Dimensions tab order, then the Metrics tab order."), isCustomized && /*#__PURE__*/gn.createElement(Tooltip, {
|
|
91836
|
+
title: "Discard the custom order and go back to the default (Dimensions, then Metrics)",
|
|
91837
|
+
arrow: true
|
|
91838
|
+
}, /*#__PURE__*/gn.createElement(Button, {
|
|
91839
|
+
variant: "outlined",
|
|
91840
|
+
size: "small",
|
|
91841
|
+
startIcon: /*#__PURE__*/gn.createElement(RestartAltIcon, null),
|
|
91842
|
+
onClick: () => onReorderColumnItems([]),
|
|
91843
|
+
sx: {
|
|
91844
|
+
height: "36px",
|
|
91845
|
+
flexShrink: 0,
|
|
91846
|
+
fontFamily: "system-ui",
|
|
91847
|
+
fontSize: "13px",
|
|
91848
|
+
fontWeight: 500,
|
|
91849
|
+
borderRadius: "8px",
|
|
91850
|
+
boxShadow: "none",
|
|
91851
|
+
textTransform: "none",
|
|
91852
|
+
borderColor: "#e0e0e0",
|
|
91853
|
+
color: "rgb(37, 37, 37)",
|
|
91854
|
+
"&:hover": {
|
|
91855
|
+
backgroundColor: "#f5f5f5",
|
|
91856
|
+
borderColor: "#d0d0d0"
|
|
91857
|
+
}
|
|
91858
|
+
}
|
|
91859
|
+
}, "Reset to Default"))), resolvedItems.length === 0 ? /*#__PURE__*/gn.createElement(Typography, {
|
|
91860
|
+
sx: {
|
|
91861
|
+
fontSize: "13px",
|
|
91862
|
+
color: "#999"
|
|
91863
|
+
}
|
|
91864
|
+
}, "Add dimensions or metrics in the other tabs first.") : /*#__PURE__*/gn.createElement(SortableListContext, {
|
|
91865
|
+
sensors: sensors,
|
|
91866
|
+
itemIds: resolvedItems.map((_, index) => index),
|
|
91867
|
+
onDragEnd: handleDragEnd
|
|
91868
|
+
}, resolvedItems.map((item, index) => /*#__PURE__*/gn.createElement(SortableColumnRow, {
|
|
91869
|
+
key: `${item.kind}-${item.key}`,
|
|
91870
|
+
id: index,
|
|
91871
|
+
label: labelFor(item),
|
|
91872
|
+
fullPath: item.kind === "computed" ? item.data.value : item.key,
|
|
91873
|
+
kind: item.kind,
|
|
91874
|
+
source: item.kind === "metric" ? item.data.metric?.source : undefined,
|
|
91875
|
+
onMoveUp: () => handleMoveUp(index),
|
|
91876
|
+
onMoveDown: () => handleMoveDown(index),
|
|
91877
|
+
isFirst: index === 0,
|
|
91878
|
+
isLast: index === resolvedItems.length - 1
|
|
91879
|
+
}))));
|
|
91880
|
+
};
|
|
91881
|
+
|
|
91689
91882
|
// Default numeral.js formats applied when a dimension/metric definition has a
|
|
91690
91883
|
// known type but no explicit `format` set on the provider.
|
|
91691
91884
|
const DEFAULT_FORMATS_BY_TYPE = {
|
|
@@ -91700,10 +91893,107 @@ const formatValue = (value, def) => {
|
|
|
91700
91893
|
return def?.prefix ? `${def.prefix} ${formatted}` : formatted;
|
|
91701
91894
|
};
|
|
91702
91895
|
const isNumericType = type => type === 'integer' || type === 'currency';
|
|
91896
|
+
|
|
91897
|
+
// Builds a single DataGrid column definition for one resolved column item
|
|
91898
|
+
// (a dimension, computed dimension, or metric). Field naming logic:
|
|
91899
|
+
// - Dimensions from base provider: baseAlias.field -> baseAlias_field
|
|
91900
|
+
// - Dimensions from nested provider: baseAlias_rel1_rel2.field -> rel1_rel2_field (drops base alias)
|
|
91901
|
+
// - Computed dimensions have no provider/relations chain, so the API returns
|
|
91902
|
+
// each row keyed by the computed dimension's own `name`.
|
|
91903
|
+
// - Metrics: API returns metric keys in different forms depending on path depth
|
|
91904
|
+
// (see comment inline below), normalised to match the row-key normalisation
|
|
91905
|
+
// done when building `rows`.
|
|
91906
|
+
const buildColumn = (item, titleOverrides, parameters) => {
|
|
91907
|
+
if (item.kind === 'computed') {
|
|
91908
|
+
const cd = item.data;
|
|
91909
|
+
return {
|
|
91910
|
+
field: cd.name,
|
|
91911
|
+
headerName: cd.name,
|
|
91912
|
+
flex: 1,
|
|
91913
|
+
minWidth: 150,
|
|
91914
|
+
type: 'string'
|
|
91915
|
+
};
|
|
91916
|
+
}
|
|
91917
|
+
if (item.kind === 'metric') {
|
|
91918
|
+
const metric = item.data;
|
|
91919
|
+
const metricDef = metric.metric;
|
|
91920
|
+
let fieldName;
|
|
91921
|
+
const dotCount = (metric.fullPath.match(/\./g) || []).length;
|
|
91922
|
+
if (dotCount >= 2) {
|
|
91923
|
+
// 3+-part namespaced path: API returns the full dotted path as the key.
|
|
91924
|
+
// Normalise dots to underscores to match the row key normalisation below.
|
|
91925
|
+
// Example: "mc.dkpi.activeAgreementsCount" -> "mc_dkpi_activeAgreementsCount"
|
|
91926
|
+
fieldName = metric.fullPath.replace(/\./g, '_');
|
|
91927
|
+
} else if (metric.relations && metric.relations.length > 0) {
|
|
91928
|
+
// 2-part path from a nested provider: API drops the base provider alias.
|
|
91929
|
+
// Example: "mc_fa.total_amount" -> "fa_total_amount"
|
|
91930
|
+
const parts = metric.fullPath.split('.');
|
|
91931
|
+
const pathWithoutField = parts[0]; // e.g., "mc_fa"
|
|
91932
|
+
const field = parts[1]; // e.g., "total_amount"
|
|
91933
|
+
|
|
91934
|
+
const pathParts = pathWithoutField.split('_');
|
|
91935
|
+
pathParts.shift(); // remove base provider alias
|
|
91936
|
+
const pathWithoutBase = pathParts.join('_');
|
|
91937
|
+
fieldName = pathWithoutBase ? `${pathWithoutBase}_${field}` : field;
|
|
91938
|
+
} else {
|
|
91939
|
+
// 2-part path from the base provider: API returns just the metric name.
|
|
91940
|
+
// Example: "mc.record_count" -> "record_count"
|
|
91941
|
+
fieldName = metric.metricName;
|
|
91942
|
+
}
|
|
91943
|
+
const headerName = interpolateTitle(titleOverrides.metrics[metric.fullPath] || metric.metricTitle || fieldName, parameters);
|
|
91944
|
+
return {
|
|
91945
|
+
field: fieldName,
|
|
91946
|
+
headerName: headerName,
|
|
91947
|
+
flex: 1,
|
|
91948
|
+
minWidth: 150,
|
|
91949
|
+
type: isNumericType(metricDef?.type) ? 'number' : 'string',
|
|
91950
|
+
renderHeader: () => /*#__PURE__*/gn.createElement(Box, {
|
|
91951
|
+
sx: {
|
|
91952
|
+
display: 'flex',
|
|
91953
|
+
alignItems: 'center',
|
|
91954
|
+
gap: 0.5
|
|
91955
|
+
}
|
|
91956
|
+
}, /*#__PURE__*/gn.createElement(MetricSourceIcon, {
|
|
91957
|
+
source: metricDef?.source
|
|
91958
|
+
}), /*#__PURE__*/gn.createElement("span", null, headerName)),
|
|
91959
|
+
valueFormatter: value => formatValue(value, metricDef)
|
|
91960
|
+
};
|
|
91961
|
+
}
|
|
91962
|
+
|
|
91963
|
+
// Dimension
|
|
91964
|
+
const dim = item.data;
|
|
91965
|
+
let fieldName;
|
|
91966
|
+
if (dim.relations && dim.relations.length > 0) {
|
|
91967
|
+
// From nested provider: drop the base provider alias
|
|
91968
|
+
// Example: ft_fa_db.currency -> fa_db_currency
|
|
91969
|
+
const parts = dim.fullPath.split('.');
|
|
91970
|
+
const pathWithoutField = parts[0]; // e.g., "ft_fa_db"
|
|
91971
|
+
const field = parts[1]; // e.g., "currency"
|
|
91972
|
+
|
|
91973
|
+
const pathParts = pathWithoutField.split('_');
|
|
91974
|
+
pathParts.shift(); // Remove base provider alias
|
|
91975
|
+
const pathWithoutBase = pathParts.join('_'); // e.g., "fa_db"
|
|
91976
|
+
|
|
91977
|
+
fieldName = pathWithoutBase ? `${pathWithoutBase}_${field}` : field;
|
|
91978
|
+
} else {
|
|
91979
|
+
// From base provider: keep the full path with underscore
|
|
91980
|
+
// Example: ba.created_at -> ba_created_at
|
|
91981
|
+
fieldName = dim.fullPath.replace('.', '_');
|
|
91982
|
+
}
|
|
91983
|
+
const headerName = interpolateTitle(titleOverrides.dimensions[dim.fullPath] || dim.dimensionTitle || fieldName, parameters);
|
|
91984
|
+
const dimDef = dim.dimension;
|
|
91985
|
+
return {
|
|
91986
|
+
field: fieldName,
|
|
91987
|
+
headerName: headerName,
|
|
91988
|
+
flex: 1,
|
|
91989
|
+
minWidth: 150,
|
|
91990
|
+
type: isNumericType(dimDef?.type) ? 'number' : 'string',
|
|
91991
|
+
valueFormatter: value => formatValue(value, dimDef)
|
|
91992
|
+
};
|
|
91993
|
+
};
|
|
91703
91994
|
const ReportDataGrid = ({
|
|
91704
91995
|
reportData,
|
|
91705
|
-
|
|
91706
|
-
metrics,
|
|
91996
|
+
columnItems = [],
|
|
91707
91997
|
loading,
|
|
91708
91998
|
onPageChange,
|
|
91709
91999
|
totalRows = 0,
|
|
@@ -91719,127 +92009,10 @@ const ReportDataGrid = ({
|
|
|
91719
92009
|
pageSize: 50
|
|
91720
92010
|
});
|
|
91721
92011
|
|
|
91722
|
-
// Generate columns dynamically
|
|
91723
|
-
|
|
91724
|
-
|
|
91725
|
-
|
|
91726
|
-
// Add dimension + computed-dimension columns, in the single unified
|
|
91727
|
-
// order the user arranged them in on the Dimensions tab.
|
|
91728
|
-
// Field naming logic (dimensions):
|
|
91729
|
-
// - If from base provider: baseAlias.field -> baseAlias_field
|
|
91730
|
-
// - If from nested provider: baseAlias_rel1_rel2.field -> rel1_rel2_field (drops base alias)
|
|
91731
|
-
// Computed dimensions have no provider/relations chain, so the API
|
|
91732
|
-
// returns each row keyed by the computed dimension's own `name`.
|
|
91733
|
-
orderedDimensionItems.forEach(item => {
|
|
91734
|
-
if (item.kind === 'computed') {
|
|
91735
|
-
const cd = item.data;
|
|
91736
|
-
cols.push({
|
|
91737
|
-
field: cd.name,
|
|
91738
|
-
headerName: cd.name,
|
|
91739
|
-
flex: 1,
|
|
91740
|
-
minWidth: 150,
|
|
91741
|
-
type: 'string'
|
|
91742
|
-
});
|
|
91743
|
-
return;
|
|
91744
|
-
}
|
|
91745
|
-
const dim = item.data;
|
|
91746
|
-
let fieldName;
|
|
91747
|
-
let headerName;
|
|
91748
|
-
|
|
91749
|
-
// Check if there are relations (nested providers)
|
|
91750
|
-
if (dim.relations && dim.relations.length > 0) {
|
|
91751
|
-
// From nested provider: drop the base provider alias
|
|
91752
|
-
// Example: ft_fa_db.currency -> fa_db_currency
|
|
91753
|
-
const parts = dim.fullPath.split('.');
|
|
91754
|
-
const pathWithoutField = parts[0]; // e.g., "ft_fa_db"
|
|
91755
|
-
const field = parts[1]; // e.g., "currency"
|
|
91756
|
-
|
|
91757
|
-
// Remove the base provider alias (first part before first underscore)
|
|
91758
|
-
const pathParts = pathWithoutField.split('_');
|
|
91759
|
-
pathParts.shift(); // Remove base provider alias
|
|
91760
|
-
const pathWithoutBase = pathParts.join('_'); // e.g., "fa_db"
|
|
91761
|
-
|
|
91762
|
-
fieldName = pathWithoutBase ? `${pathWithoutBase}_${field}` : field;
|
|
91763
|
-
} else {
|
|
91764
|
-
// From base provider: keep the full path with underscore
|
|
91765
|
-
// Example: ba.created_at -> ba_created_at
|
|
91766
|
-
fieldName = dim.fullPath.replace('.', '_');
|
|
91767
|
-
}
|
|
91768
|
-
|
|
91769
|
-
// Check for title override, otherwise use the friendly dimension title from provider
|
|
91770
|
-
headerName = titleOverrides.dimensions[dim.fullPath] || dim.dimensionTitle || fieldName;
|
|
91771
|
-
headerName = interpolateTitle(headerName, parameters);
|
|
91772
|
-
const dimDef = dim.dimension;
|
|
91773
|
-
cols.push({
|
|
91774
|
-
field: fieldName,
|
|
91775
|
-
headerName: headerName,
|
|
91776
|
-
flex: 1,
|
|
91777
|
-
minWidth: 150,
|
|
91778
|
-
type: isNumericType(dimDef?.type) ? 'number' : 'string',
|
|
91779
|
-
valueFormatter: value => formatValue(value, dimDef)
|
|
91780
|
-
});
|
|
91781
|
-
});
|
|
91782
|
-
|
|
91783
|
-
// Add metric columns
|
|
91784
|
-
// The API returns metric keys in two different forms depending on the path depth:
|
|
91785
|
-
//
|
|
91786
|
-
// • 2-part path "mc.record_count" → API key: "record_count"
|
|
91787
|
-
// • 2-part path "mc_fa.total_amount" (nested) → API key: "fa_total_amount"
|
|
91788
|
-
// • 3+-part path "mc.dkpi.activeAgreementsCount"→ API key: "mc.dkpi.activeAgreementsCount" (dots)
|
|
91789
|
-
//
|
|
91790
|
-
// For 3+-part paths the API key contains dots. MUI DataGrid interprets dots
|
|
91791
|
-
// in field names as nested-object accessors, so the rows useMemo normalises
|
|
91792
|
-
// those keys (dots → underscores). The column field must match that normalised key.
|
|
91793
|
-
metrics.forEach(metric => {
|
|
91794
|
-
const metricDef = metric.metric;
|
|
91795
|
-
let fieldName;
|
|
91796
|
-
let headerName;
|
|
91797
|
-
const dotCount = (metric.fullPath.match(/\./g) || []).length;
|
|
91798
|
-
if (dotCount >= 2) {
|
|
91799
|
-
// 3+-part namespaced path: API returns the full dotted path as the key.
|
|
91800
|
-
// Normalise dots to underscores to match the row key normalisation below.
|
|
91801
|
-
// Example: "mc.dkpi.activeAgreementsCount" -> "mc_dkpi_activeAgreementsCount"
|
|
91802
|
-
fieldName = metric.fullPath.replace(/\./g, '_');
|
|
91803
|
-
} else if (metric.relations && metric.relations.length > 0) {
|
|
91804
|
-
// 2-part path from a nested provider: API drops the base provider alias.
|
|
91805
|
-
// Example: "mc_fa.total_amount" -> "fa_total_amount"
|
|
91806
|
-
const parts = metric.fullPath.split('.');
|
|
91807
|
-
const pathWithoutField = parts[0]; // e.g., "mc_fa"
|
|
91808
|
-
const field = parts[1]; // e.g., "total_amount"
|
|
91809
|
-
|
|
91810
|
-
const pathParts = pathWithoutField.split('_');
|
|
91811
|
-
pathParts.shift(); // remove base provider alias
|
|
91812
|
-
const pathWithoutBase = pathParts.join('_');
|
|
91813
|
-
fieldName = pathWithoutBase ? `${pathWithoutBase}_${field}` : field;
|
|
91814
|
-
} else {
|
|
91815
|
-
// 2-part path from the base provider: API returns just the metric name.
|
|
91816
|
-
// Example: "mc.record_count" -> "record_count"
|
|
91817
|
-
fieldName = metric.metricName;
|
|
91818
|
-
}
|
|
91819
|
-
|
|
91820
|
-
// Check for title override, otherwise use the friendly metric title from provider
|
|
91821
|
-
headerName = titleOverrides.metrics[metric.fullPath] || metric.metricTitle || fieldName;
|
|
91822
|
-
headerName = interpolateTitle(headerName, parameters);
|
|
91823
|
-
cols.push({
|
|
91824
|
-
field: fieldName,
|
|
91825
|
-
headerName: headerName,
|
|
91826
|
-
flex: 1,
|
|
91827
|
-
minWidth: 150,
|
|
91828
|
-
type: isNumericType(metricDef?.type) ? 'number' : 'string',
|
|
91829
|
-
renderHeader: () => /*#__PURE__*/gn.createElement(Box, {
|
|
91830
|
-
sx: {
|
|
91831
|
-
display: 'flex',
|
|
91832
|
-
alignItems: 'center',
|
|
91833
|
-
gap: 0.5
|
|
91834
|
-
}
|
|
91835
|
-
}, /*#__PURE__*/gn.createElement(MetricSourceIcon, {
|
|
91836
|
-
source: metricDef?.source
|
|
91837
|
-
}), /*#__PURE__*/gn.createElement("span", null, headerName)),
|
|
91838
|
-
valueFormatter: value => formatValue(value, metricDef)
|
|
91839
|
-
});
|
|
91840
|
-
});
|
|
91841
|
-
return cols;
|
|
91842
|
-
}, [orderedDimensionItems, metrics, titleOverrides, parameters]);
|
|
92012
|
+
// Generate columns dynamically, in the final resolved column order
|
|
92013
|
+
// (custom Column Order tab arrangement when defined, otherwise
|
|
92014
|
+
// dimensions/computed then metrics).
|
|
92015
|
+
const columns = gn.useMemo(() => columnItems.map(item => buildColumn(item, titleOverrides, parameters)), [columnItems, titleOverrides, parameters]);
|
|
91843
92016
|
|
|
91844
92017
|
// Transform report data to rows with unique IDs.
|
|
91845
92018
|
// Keys are normalised by replacing dots with underscores so that MUI DataGrid
|
|
@@ -91949,7 +92122,12 @@ const ReportBuilder = ({
|
|
|
91949
92122
|
computedDimensions: [],
|
|
91950
92123
|
// Unified display/execution order across dimensions + computedDimensions.
|
|
91951
92124
|
// Entries: { type: 'dimension', key: fullPath } | { type: 'computed', key: name }
|
|
91952
|
-
dimensionOrder: []
|
|
92125
|
+
dimensionOrder: [],
|
|
92126
|
+
// Optional unified display order across dimensions + computedDimensions +
|
|
92127
|
+
// metrics, set on the Column Order tab. Empty means "not defined" — the
|
|
92128
|
+
// results grid falls back to dimensionOrder followed by metrics order.
|
|
92129
|
+
// Entries: { type: 'dimension' | 'computed' | 'metric', key }
|
|
92130
|
+
columnOrder: []
|
|
91953
92131
|
});
|
|
91954
92132
|
const [titleOverrides, setTitleOverrides] = d({
|
|
91955
92133
|
dimensions: {},
|
|
@@ -92180,6 +92358,46 @@ const ReportBuilder = ({
|
|
|
92180
92358
|
});
|
|
92181
92359
|
return dimensionOrder;
|
|
92182
92360
|
};
|
|
92361
|
+
|
|
92362
|
+
// Build the optional unified column order (dimensions + computed
|
|
92363
|
+
// dimensions + metrics) from the persisted `ordered_columns` field.
|
|
92364
|
+
// Unlike buildDimensionOrder, entries that no longer resolve are simply
|
|
92365
|
+
// dropped rather than backfilled — an empty/partial result just means "no
|
|
92366
|
+
// custom order defined (yet)", which resolveColumnOrder treats as
|
|
92367
|
+
// "fall back to the default order".
|
|
92368
|
+
const buildColumnOrder = (rawOrderedColumns, dimensions, computedDimensions, metrics) => {
|
|
92369
|
+
if (!Array.isArray(rawOrderedColumns)) return [];
|
|
92370
|
+
const dimensionPaths = new Set(dimensions.map(d => d.fullPath));
|
|
92371
|
+
const computedNames = new Set(computedDimensions.map(cd => cd.name));
|
|
92372
|
+
const metricPaths = new Set(metrics.map(m => m.fullPath));
|
|
92373
|
+
const columnOrder = [];
|
|
92374
|
+
rawOrderedColumns.forEach(entry => {
|
|
92375
|
+
if (entry.type === "dimension" && dimensionPaths.has(entry.ref)) {
|
|
92376
|
+
columnOrder.push({
|
|
92377
|
+
type: "dimension",
|
|
92378
|
+
key: entry.ref
|
|
92379
|
+
});
|
|
92380
|
+
} else if (entry.type === "computed" && computedNames.has(entry.ref)) {
|
|
92381
|
+
columnOrder.push({
|
|
92382
|
+
type: "computed",
|
|
92383
|
+
key: entry.ref
|
|
92384
|
+
});
|
|
92385
|
+
} else if (entry.type === "metric" && metricPaths.has(entry.ref)) {
|
|
92386
|
+
columnOrder.push({
|
|
92387
|
+
type: "metric",
|
|
92388
|
+
key: entry.ref
|
|
92389
|
+
});
|
|
92390
|
+
}
|
|
92391
|
+
});
|
|
92392
|
+
return columnOrder;
|
|
92393
|
+
};
|
|
92394
|
+
|
|
92395
|
+
// Auto-append a newly added dimension/computed dimension/metric to the end
|
|
92396
|
+
// of columnOrder — but only once a custom order actually exists. While
|
|
92397
|
+
// columnOrder is empty (no custom order defined) it's left untouched, so
|
|
92398
|
+
// the results grid keeps falling back to the default dimensions-then-
|
|
92399
|
+
// metrics order until the user visits the Column Order tab.
|
|
92400
|
+
const appendToColumnOrder = (columnOrder, entry) => columnOrder.length > 0 ? [...columnOrder, entry] : columnOrder;
|
|
92183
92401
|
const loadReportDefinition = async id => {
|
|
92184
92402
|
try {
|
|
92185
92403
|
console.log("Loading report definition:", id);
|
|
@@ -92276,6 +92494,7 @@ const ReportBuilder = ({
|
|
|
92276
92494
|
// provider/relation chain, so they're loaded as-is (no reconstruction needed)
|
|
92277
92495
|
const computedDimensions = reportDef.definition?.doc?.query?.computed_dimensions || [];
|
|
92278
92496
|
const dimensionOrder = buildDimensionOrder(reportDef.definition?.doc?.query?.ordered_dimensions, reconstructedDimensions, computedDimensions);
|
|
92497
|
+
const columnOrder = buildColumnOrder(reportDef.definition?.doc?.query?.ordered_columns, reconstructedDimensions, computedDimensions, reconstructedMetrics);
|
|
92279
92498
|
|
|
92280
92499
|
// Set the report state
|
|
92281
92500
|
setReport({
|
|
@@ -92283,7 +92502,8 @@ const ReportBuilder = ({
|
|
|
92283
92502
|
metrics: reconstructedMetrics,
|
|
92284
92503
|
filters: loadedFilters,
|
|
92285
92504
|
computedDimensions,
|
|
92286
|
-
dimensionOrder
|
|
92505
|
+
dimensionOrder,
|
|
92506
|
+
columnOrder
|
|
92287
92507
|
});
|
|
92288
92508
|
|
|
92289
92509
|
// Set title overrides
|
|
@@ -92393,6 +92613,7 @@ const ReportBuilder = ({
|
|
|
92393
92613
|
// provider/relation chain, so they're loaded as-is (no reconstruction needed)
|
|
92394
92614
|
const computedDimensions = reportDef.definition?.doc?.query?.computed_dimensions || [];
|
|
92395
92615
|
const dimensionOrder = buildDimensionOrder(reportDef.definition?.doc?.query?.ordered_dimensions, reconstructedDimensions, computedDimensions);
|
|
92616
|
+
const columnOrder = buildColumnOrder(reportDef.definition?.doc?.query?.ordered_columns, reconstructedDimensions, computedDimensions, reconstructedMetrics);
|
|
92396
92617
|
|
|
92397
92618
|
// Set the report state
|
|
92398
92619
|
setReport({
|
|
@@ -92400,7 +92621,8 @@ const ReportBuilder = ({
|
|
|
92400
92621
|
metrics: reconstructedMetrics,
|
|
92401
92622
|
filters: loadedFilters,
|
|
92402
92623
|
computedDimensions,
|
|
92403
|
-
dimensionOrder
|
|
92624
|
+
dimensionOrder,
|
|
92625
|
+
columnOrder
|
|
92404
92626
|
});
|
|
92405
92627
|
|
|
92406
92628
|
// Set title overrides
|
|
@@ -92434,7 +92656,8 @@ const ReportBuilder = ({
|
|
|
92434
92656
|
metrics: [],
|
|
92435
92657
|
filters: {},
|
|
92436
92658
|
computedDimensions: [],
|
|
92437
|
-
dimensionOrder: []
|
|
92659
|
+
dimensionOrder: [],
|
|
92660
|
+
columnOrder: []
|
|
92438
92661
|
});
|
|
92439
92662
|
// Reset title overrides
|
|
92440
92663
|
setTitleOverrides({
|
|
@@ -92515,7 +92738,11 @@ const ReportBuilder = ({
|
|
|
92515
92738
|
dimensionOrder: [...prev.dimensionOrder, {
|
|
92516
92739
|
type: "dimension",
|
|
92517
92740
|
key: dimensionData.fullPath
|
|
92518
|
-
}]
|
|
92741
|
+
}],
|
|
92742
|
+
columnOrder: appendToColumnOrder(prev.columnOrder, {
|
|
92743
|
+
type: "dimension",
|
|
92744
|
+
key: dimensionData.fullPath
|
|
92745
|
+
})
|
|
92519
92746
|
};
|
|
92520
92747
|
console.log("Dimension saved:", dimensionData);
|
|
92521
92748
|
console.log("Complete report:", newReport);
|
|
@@ -92526,7 +92753,8 @@ const ReportBuilder = ({
|
|
|
92526
92753
|
setReport(prev => ({
|
|
92527
92754
|
...prev,
|
|
92528
92755
|
dimensions: prev.dimensions.filter(d => d.fullPath !== fullPath),
|
|
92529
|
-
dimensionOrder: prev.dimensionOrder.filter(entry => !(entry.type === "dimension" && entry.key === fullPath))
|
|
92756
|
+
dimensionOrder: prev.dimensionOrder.filter(entry => !(entry.type === "dimension" && entry.key === fullPath)),
|
|
92757
|
+
columnOrder: prev.columnOrder.filter(entry => !(entry.type === "dimension" && entry.key === fullPath))
|
|
92530
92758
|
}));
|
|
92531
92759
|
};
|
|
92532
92760
|
const handleUpdateDimensionSortOrder = (fullPath, sortOrder) => {
|
|
@@ -92555,7 +92783,11 @@ const ReportBuilder = ({
|
|
|
92555
92783
|
dimensionOrder: [...prev.dimensionOrder, {
|
|
92556
92784
|
type: "computed",
|
|
92557
92785
|
key: computedDimensionData.name
|
|
92558
|
-
}]
|
|
92786
|
+
}],
|
|
92787
|
+
columnOrder: appendToColumnOrder(prev.columnOrder, {
|
|
92788
|
+
type: "computed",
|
|
92789
|
+
key: computedDimensionData.name
|
|
92790
|
+
})
|
|
92559
92791
|
}));
|
|
92560
92792
|
};
|
|
92561
92793
|
const handleUpdateComputedDimension = (name, computedDimensionData) => {
|
|
@@ -92565,6 +92797,10 @@ const ReportBuilder = ({
|
|
|
92565
92797
|
dimensionOrder: prev.dimensionOrder.map(entry => entry.type === "computed" && entry.key === name ? {
|
|
92566
92798
|
...entry,
|
|
92567
92799
|
key: computedDimensionData.name
|
|
92800
|
+
} : entry),
|
|
92801
|
+
columnOrder: prev.columnOrder.map(entry => entry.type === "computed" && entry.key === name ? {
|
|
92802
|
+
...entry,
|
|
92803
|
+
key: computedDimensionData.name
|
|
92568
92804
|
} : entry)
|
|
92569
92805
|
}));
|
|
92570
92806
|
};
|
|
@@ -92572,14 +92808,19 @@ const ReportBuilder = ({
|
|
|
92572
92808
|
setReport(prev => ({
|
|
92573
92809
|
...prev,
|
|
92574
92810
|
computedDimensions: prev.computedDimensions.filter(cd => cd.name !== name),
|
|
92575
|
-
dimensionOrder: prev.dimensionOrder.filter(entry => !(entry.type === "computed" && entry.key === name))
|
|
92811
|
+
dimensionOrder: prev.dimensionOrder.filter(entry => !(entry.type === "computed" && entry.key === name)),
|
|
92812
|
+
columnOrder: prev.columnOrder.filter(entry => !(entry.type === "computed" && entry.key === name))
|
|
92576
92813
|
}));
|
|
92577
92814
|
};
|
|
92578
92815
|
const handleSaveMetric = metricData => {
|
|
92579
92816
|
setReport(prev => {
|
|
92580
92817
|
const newReport = {
|
|
92581
92818
|
...prev,
|
|
92582
|
-
metrics: [...prev.metrics, metricData]
|
|
92819
|
+
metrics: [...prev.metrics, metricData],
|
|
92820
|
+
columnOrder: appendToColumnOrder(prev.columnOrder, {
|
|
92821
|
+
type: "metric",
|
|
92822
|
+
key: metricData.fullPath
|
|
92823
|
+
})
|
|
92583
92824
|
};
|
|
92584
92825
|
console.log("Metric saved:", metricData);
|
|
92585
92826
|
console.log("Complete report:", newReport);
|
|
@@ -92587,15 +92828,29 @@ const ReportBuilder = ({
|
|
|
92587
92828
|
});
|
|
92588
92829
|
};
|
|
92589
92830
|
const handleRemoveMetric = index => {
|
|
92831
|
+
setReport(prev => {
|
|
92832
|
+
const fullPath = prev.metrics[index]?.fullPath;
|
|
92833
|
+
return {
|
|
92834
|
+
...prev,
|
|
92835
|
+
metrics: prev.metrics.filter((_, i) => i !== index),
|
|
92836
|
+
columnOrder: prev.columnOrder.filter(entry => !(entry.type === "metric" && entry.key === fullPath))
|
|
92837
|
+
};
|
|
92838
|
+
});
|
|
92839
|
+
};
|
|
92840
|
+
const handleReorderMetrics = newOrder => {
|
|
92590
92841
|
setReport(prev => ({
|
|
92591
92842
|
...prev,
|
|
92592
|
-
metrics:
|
|
92843
|
+
metrics: newOrder
|
|
92593
92844
|
}));
|
|
92594
92845
|
};
|
|
92595
|
-
|
|
92846
|
+
|
|
92847
|
+
// Sets the unified column order shown on the Column Order tab. An empty
|
|
92848
|
+
// array (e.g. via "Reset to Default") clears the customization, and the
|
|
92849
|
+
// results grid falls back to dimensionOrder followed by metrics order.
|
|
92850
|
+
const handleReorderColumnItems = newColumnOrder => {
|
|
92596
92851
|
setReport(prev => ({
|
|
92597
92852
|
...prev,
|
|
92598
|
-
|
|
92853
|
+
columnOrder: newColumnOrder
|
|
92599
92854
|
}));
|
|
92600
92855
|
};
|
|
92601
92856
|
const handleSaveFilter = (fullPath, filterData) => {
|
|
@@ -92720,6 +92975,17 @@ const ReportBuilder = ({
|
|
|
92720
92975
|
}));
|
|
92721
92976
|
}
|
|
92722
92977
|
|
|
92978
|
+
// Persist the optional unified column order (Column Order tab) so the
|
|
92979
|
+
// results grid can reproduce it on reload. Absent when no custom order
|
|
92980
|
+
// has been defined, so old/untouched reports keep their current
|
|
92981
|
+
// dimensions-then-metrics column order unchanged.
|
|
92982
|
+
if (report.columnOrder.length > 0) {
|
|
92983
|
+
queryObj.ordered_columns = report.columnOrder.map(entry => ({
|
|
92984
|
+
type: entry.type,
|
|
92985
|
+
ref: entry.key
|
|
92986
|
+
}));
|
|
92987
|
+
}
|
|
92988
|
+
|
|
92723
92989
|
// Only add titles if there are any overrides
|
|
92724
92990
|
if (Object.keys(titles).length > 0) {
|
|
92725
92991
|
queryObj.titles = titles;
|
|
@@ -92779,8 +93045,8 @@ const ReportBuilder = ({
|
|
|
92779
93045
|
const handleRunReport = async () => {
|
|
92780
93046
|
setCurrentPage(0);
|
|
92781
93047
|
await runReportWithPagination(0, pageSize);
|
|
92782
|
-
// Switch to Results tab after running report (
|
|
92783
|
-
setActiveTab(
|
|
93048
|
+
// Switch to Results tab after running report (index 4: Dimensions, Metrics, Filters, Column Order, Results)
|
|
93049
|
+
setActiveTab(4);
|
|
92784
93050
|
};
|
|
92785
93051
|
const handleDownloadReport = async () => {
|
|
92786
93052
|
try {
|
|
@@ -92887,6 +93153,11 @@ const ReportBuilder = ({
|
|
|
92887
93153
|
kind: "computed",
|
|
92888
93154
|
data: computedDimensionByName[entry.key]
|
|
92889
93155
|
}).filter(item => item.data);
|
|
93156
|
+
|
|
93157
|
+
// Final column list for the results grid: the explicit Column Order tab
|
|
93158
|
+
// arrangement when defined, otherwise dimensions/computed (in their own
|
|
93159
|
+
// order) followed by metrics (in their own order) — today's behavior.
|
|
93160
|
+
const columnItems = resolveColumnOrder(report.columnOrder, orderedDimensionItems, report.metrics);
|
|
92890
93161
|
return /*#__PURE__*/gn.createElement(Box, {
|
|
92891
93162
|
sx: {
|
|
92892
93163
|
p: 3,
|
|
@@ -93141,9 +93412,36 @@ const ReportBuilder = ({
|
|
|
93141
93412
|
}
|
|
93142
93413
|
}
|
|
93143
93414
|
}), /*#__PURE__*/gn.createElement(Tab, {
|
|
93144
|
-
label:
|
|
93415
|
+
label: /*#__PURE__*/gn.createElement(Badge, {
|
|
93416
|
+
variant: "dot",
|
|
93417
|
+
invisible: report.columnOrder.length === 0,
|
|
93418
|
+
sx: {
|
|
93419
|
+
"& .MuiBadge-dot": {
|
|
93420
|
+
backgroundColor: "rgb(70, 134, 128)"
|
|
93421
|
+
}
|
|
93422
|
+
}
|
|
93423
|
+
}, /*#__PURE__*/gn.createElement("span", {
|
|
93424
|
+
style: {
|
|
93425
|
+
marginRight: report.columnOrder.length > 0 ? "8px" : "0"
|
|
93426
|
+
}
|
|
93427
|
+
}, "Column Order")),
|
|
93145
93428
|
id: "report-tab-3",
|
|
93146
93429
|
"aria-controls": "report-tabpanel-3",
|
|
93430
|
+
sx: {
|
|
93431
|
+
height: "41px",
|
|
93432
|
+
fontFamily: "system-ui",
|
|
93433
|
+
borderRadius: "0.5rem",
|
|
93434
|
+
boxShadow: "none",
|
|
93435
|
+
textTransform: "none",
|
|
93436
|
+
color: "rgb(37, 37, 37)",
|
|
93437
|
+
"&.Mui-selected": {
|
|
93438
|
+
color: "rgb(70, 134, 128)"
|
|
93439
|
+
}
|
|
93440
|
+
}
|
|
93441
|
+
}), /*#__PURE__*/gn.createElement(Tab, {
|
|
93442
|
+
label: reportData ? "Results" : "Results (Run report first)",
|
|
93443
|
+
id: "report-tab-4",
|
|
93444
|
+
"aria-controls": "report-tabpanel-4",
|
|
93147
93445
|
disabled: !reportData,
|
|
93148
93446
|
sx: {
|
|
93149
93447
|
height: "41px",
|
|
@@ -93210,10 +93508,18 @@ const ReportBuilder = ({
|
|
|
93210
93508
|
})), /*#__PURE__*/gn.createElement(TabPanel, {
|
|
93211
93509
|
value: activeTab,
|
|
93212
93510
|
index: 3
|
|
93213
|
-
},
|
|
93214
|
-
reportData: reportData,
|
|
93511
|
+
}, /*#__PURE__*/gn.createElement(ColumnOrder, {
|
|
93215
93512
|
orderedDimensionItems: orderedDimensionItems,
|
|
93216
93513
|
metrics: report.metrics,
|
|
93514
|
+
columnOrder: report.columnOrder,
|
|
93515
|
+
onReorderColumnItems: handleReorderColumnItems,
|
|
93516
|
+
titleOverrides: titleOverrides
|
|
93517
|
+
})), /*#__PURE__*/gn.createElement(TabPanel, {
|
|
93518
|
+
value: activeTab,
|
|
93519
|
+
index: 4
|
|
93520
|
+
}, reportData && /*#__PURE__*/gn.createElement(ReportDataGrid, {
|
|
93521
|
+
reportData: reportData,
|
|
93522
|
+
columnItems: columnItems,
|
|
93217
93523
|
loading: loading,
|
|
93218
93524
|
onPageChange: handlePageChange,
|
|
93219
93525
|
totalRows: totalRows,
|
|
@@ -93301,6 +93607,12 @@ const CurrencySelector = () => {
|
|
|
93301
93607
|
parameters,
|
|
93302
93608
|
setParameters
|
|
93303
93609
|
} = useReportingContext();
|
|
93610
|
+
|
|
93611
|
+
// Hidden by default; only shown when a `cur` query parameter is present in the top-level URL.
|
|
93612
|
+
const showCurrencySelector = new URLSearchParams(window.location.search).has("cur");
|
|
93613
|
+
if (!showCurrencySelector) {
|
|
93614
|
+
return null;
|
|
93615
|
+
}
|
|
93304
93616
|
return /*#__PURE__*/gn.createElement(SingleSelect, {
|
|
93305
93617
|
items: CURRENCY_OPTIONS,
|
|
93306
93618
|
value: parameters.base_currency,
|