From b133d43ebe16409c733e85683caf5a86c3e631ab Mon Sep 17 00:00:00 2001 From: M_Kececi Date: Fri, 3 Jul 2026 17:43:13 +0300 Subject: [PATCH] product performance grouped kpi improvements --- .../pages/ProductPerformanceProfitability.vue | 101 +++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/ui/src/pages/ProductPerformanceProfitability.vue b/ui/src/pages/ProductPerformanceProfitability.vue index 6bea295..9a6ecef 100644 --- a/ui/src/pages/ProductPerformanceProfitability.vue +++ b/ui/src/pages/ProductPerformanceProfitability.vue @@ -207,6 +207,7 @@ :columns="orderProductCustomerColumns" :loading="loading || backendGroupedLoading" :pagination="{ rowsPerPage: 100, sortBy: 'order_usd', descending: true }" + :sort-method="sortProductGroupedTableRows" virtual-scroll :virtual-scroll-item-size="64" > @@ -396,6 +397,7 @@ :columns="orderMarketDetailColumns" :loading="loading || backendGroupedLoading" :pagination="{ rowsPerPage: 100, sortBy: 'order_date', descending: true }" + :sort-method="sortProductGroupedTableRows" virtual-scroll :virtual-scroll-item-size="64" > @@ -532,6 +534,7 @@ :columns="productColumns" :loading="loading || backendGroupedLoading" :pagination="pagination" + :sort-method="sortProductGroupedTableRows" virtual-scroll :virtual-scroll-item-size="64" > @@ -686,6 +689,7 @@ :columns="idleColumns" :loading="loading || backendGroupedLoading" :pagination="{ rowsPerPage: 100 }" + :sort-method="sortProductGroupedTableRows" virtual-scroll :virtual-scroll-item-size="64" > @@ -971,6 +975,7 @@ :columns="visibleSalesBreakdownColumns" :loading="loading || backendGroupedLoading" :pagination="{ rowsPerPage: 100, sortBy: 'performance_score_total', descending: true }" + :sort-method="sortProductGroupedTableRows" virtual-scroll :virtual-scroll-item-size="64" > @@ -2626,6 +2631,97 @@ function sortProductLeafRows (sourceRows) { }) } +function sortProductGroupedTableRows (tableRows, sortBy, descending) { + const source = Array.isArray(tableRows) ? tableRows : [] + if (!sortBy || !source.some(row => row?.__group)) { + return sortFlatTableRows(source, sortBy, descending) + } + + const roots = [] + const stack = [] + + for (const row of source) { + const node = { row, children: [] } + if (row?.__group) { + const level = Math.max(0, Number(row.level || 0)) + stack.length = Math.min(stack.length, level) + const parent = level > 0 ? stack[level - 1] : null + if (parent) parent.children.push(node) + else roots.push(node) + stack[level] = node + stack.length = level + 1 + } else { + const parent = stack[stack.length - 1] + if (parent) parent.children.push(node) + else roots.push(node) + } + } + + const sorted = [] + const appendNodes = (nodes) => { + const ordered = sortNodeSiblings(nodes, sortBy, descending) + for (const node of ordered) { + sorted.push(node.row) + if (node.children.length) appendNodes(node.children) + } + } + appendNodes(roots) + return sorted +} + +function sortNodeSiblings (nodes, sortBy, descending) { + const source = mergeDuplicateGroupSiblingNodes(nodes) + if (!sortBy) return source + return source.sort((a, b) => { + const cmp = compareColumnSortValues(sortValueForColumnName(a.row, sortBy), sortValueForColumnName(b.row, sortBy)) + return descending ? -cmp : cmp + }) +} + +function mergeDuplicateGroupSiblingNodes (nodes) { + const out = [] + const seen = new Map() + for (const node of nodes) { + if (!node?.row?.__group) { + out.push(node) + continue + } + const key = [ + Number(node.row.level || 0), + String(node.row.group_field || ''), + String(node.row.group_value || node.row.label || '') + ].join('|') + const existing = seen.get(key) + if (existing) { + existing.children.push(...node.children) + continue + } + seen.set(key, node) + out.push(node) + } + return out +} + +function sortFlatTableRows (tableRows, sortBy, descending) { + if (!sortBy) return [...tableRows] + return [...tableRows].sort((a, b) => { + const cmp = compareColumnSortValues(sortValueForColumnName(a, sortBy), sortValueForColumnName(b, sortBy)) + return descending ? -cmp : cmp + }) +} + +function sortValueForColumnName (row, columnName) { + const columns = [ + ...(productColumns.value || []), + ...idleColumns, + ...orderProductCustomerColumns, + ...orderMarketDetailColumns, + ...(visibleSalesBreakdownColumns.value || []) + ] + const col = columns.find(item => item.name === columnName || item.field === columnName) || { name: columnName, field: columnName } + return sortValueForColumn(row, col) +} + function makeGroupRow (key, level, groupDef, value, groupRows) { const imageSource = groupRows.find(row => row?.product_code) || {} const aggregate = aggregateGroupFields(groupRows, groupDef.key) @@ -3092,7 +3188,10 @@ function canShowProductImage (row) { } function groupShowsImage (row) { - return activeTab.value === 'products' && row?.__group && hasProductVariantImageKey(row) + return activeTab.value === 'products' && + row?.__group && + ['product_code', 'color_code', 'yaka_kodu'].includes(String(row.group_field || '')) && + hasProductVariantImageKey(row) } const groupDimensionFields = new Set(dimensionColumnNames)