Fix product performance grouped JSON build
This commit is contained in:
@@ -1611,10 +1611,10 @@ const selectedExpandLevelKeysByTab = reactive({
|
||||
product_detail: ['urun_alt_grubu'],
|
||||
sales_color_yaka_market_customer: ['color_yaka'],
|
||||
idle: ['urun_ilk_grubu', 'askili_yan', 'kategori'],
|
||||
sales_product_country_segment_market_customer: ['market_key', 'customer_code', 'customer_name', 'urun_ilk_grubu', 'askili_yan'],
|
||||
sales_product_country_segment_market_customer: ['urun_ilk_grubu', 'askili_yan', 'kategori'],
|
||||
sales_market_customer_product: ['market_key', 'customer_code', 'customer_name', 'urun_ilk_grubu', 'askili_yan'],
|
||||
sales_country_segment_market_customer_product: ['market_key', 'customer_code', 'customer_name', 'urun_ilk_grubu', 'askili_yan'],
|
||||
order_product_customers: ['market_key', 'customer_code', 'customer_name', 'urun_ilk_grubu', 'askili_yan'],
|
||||
sales_country_segment_market_customer_product: ['country', 'customer_segment', 'market_key'],
|
||||
order_product_customers: ['urun_ilk_grubu', 'askili_yan', 'kategori'],
|
||||
order_market_details: ['market_key', 'customer_code', 'customer_name', 'urun_ilk_grubu', 'askili_yan']
|
||||
})
|
||||
const productKpiFetchLimit = 50000
|
||||
@@ -1623,6 +1623,7 @@ const maxBulkExpandKeys = 1000
|
||||
const maxBackendExpandedKeys = 1000
|
||||
const tableVirtualSliceSize = 160
|
||||
const productThumbVirtualItemSize = 236
|
||||
const maxRenderedGroupedRows = 2500
|
||||
const maxAutoExpandLevelByTab = {
|
||||
products: 8,
|
||||
product_detail: 8,
|
||||
@@ -2554,16 +2555,18 @@ const tabGroupLevels = {
|
||||
{ key: 'color_yaka', label: 'Renk/Yaka' }
|
||||
],
|
||||
sales_product_country_segment_market_customer: [
|
||||
{ key: 'market_key', label: 'Piyasa' },
|
||||
{ key: 'customer_code', label: 'Müşteri Kodu' },
|
||||
{ key: 'customer_name', label: 'Müşteri' },
|
||||
{ key: 'urun_ilk_grubu', label: 'Ürün İlk Grubu' },
|
||||
{ key: 'askili_yan', label: 'Askılı/Yan' },
|
||||
{ key: 'kategori', label: 'Kategori' },
|
||||
{ key: 'urun_ana_grubu', label: 'Ürün Ana Grubu' },
|
||||
{ key: 'urun_alt_grubu', label: 'Ürün Alt Grubu' },
|
||||
{ key: 'product_code', label: 'Ürün' },
|
||||
{ key: 'color_yaka', label: 'Renk/Yaka' }
|
||||
{ key: 'color_yaka', label: 'Renk/Yaka' },
|
||||
{ key: 'country', label: 'Ülke' },
|
||||
{ key: 'customer_segment', label: 'Segment' },
|
||||
{ key: 'market_key', label: 'Piyasa' },
|
||||
{ key: 'customer_code', label: 'Müşteri Kodu' },
|
||||
{ key: 'customer_name', label: 'Müşteri' }
|
||||
],
|
||||
sales_market_customer_product: [
|
||||
{ key: 'market_key', label: 'Piyasa' },
|
||||
@@ -2578,6 +2581,8 @@ const tabGroupLevels = {
|
||||
{ key: 'color_yaka', label: 'Renk/Yaka' }
|
||||
],
|
||||
sales_country_segment_market_customer_product: [
|
||||
{ key: 'country', label: 'Ülke' },
|
||||
{ key: 'customer_segment', label: 'Segment' },
|
||||
{ key: 'market_key', label: 'Piyasa' },
|
||||
{ key: 'customer_code', label: 'Müşteri Kodu' },
|
||||
{ key: 'customer_name', label: 'Müşteri' },
|
||||
@@ -2590,16 +2595,16 @@ const tabGroupLevels = {
|
||||
{ key: 'color_yaka', label: 'Renk/Yaka' }
|
||||
],
|
||||
order_product_customers: [
|
||||
{ key: 'market_key', label: 'Piyasa' },
|
||||
{ key: 'customer_code', label: 'Müşteri Kodu' },
|
||||
{ key: 'customer_name', label: 'Müşteri' },
|
||||
{ key: 'urun_ilk_grubu', label: 'Ürün İlk Grubu' },
|
||||
{ key: 'askili_yan', label: 'Askılı/Yan' },
|
||||
{ key: 'kategori', label: 'Kategori' },
|
||||
{ key: 'urun_ana_grubu', label: 'Ürün Ana Grubu' },
|
||||
{ key: 'urun_alt_grubu', label: 'Ürün Alt Grubu' },
|
||||
{ key: 'product_code', label: 'Ürün' },
|
||||
{ key: 'color_yaka', label: 'Renk/Yaka' }
|
||||
{ key: 'color_yaka', label: 'Renk/Yaka' },
|
||||
{ key: 'market_key', label: 'Piyasa' },
|
||||
{ key: 'customer_code', label: 'Müşteri Kodu' },
|
||||
{ key: 'customer_name', label: 'Müşteri' }
|
||||
],
|
||||
order_market_details: [
|
||||
{ key: 'market_key', label: 'Piyasa' },
|
||||
@@ -2660,22 +2665,6 @@ const fullExpandThroughLevel = computed(() => {
|
||||
return Math.min(lastGroupLevel, maxAutoExpandThroughLevelForTab())
|
||||
})
|
||||
|
||||
const tableFilterSourceMap = computed(() => ({
|
||||
products: { rows: filterSourceRowsForTab('products', rows.value), columns: productColumns.value },
|
||||
product_detail: { rows: filterSourceRowsForTab('product_detail', detailProductRows.value), columns: productDetailColumns.value },
|
||||
general: { rows: generalRows.value, columns: generalColumns },
|
||||
order_product_customers: { rows: filterSourceRowsForTab('order_product_customers', orderProductCustomerRows.value), columns: orderProductCustomerColumns },
|
||||
order_market_details: { rows: filterSourceRowsForTab('order_market_details', orderMarketDetailRows.value), columns: orderMarketDetailColumns },
|
||||
idle: { rows: filterSourceRowsForTab('idle', idleRows.value), columns: idleColumns },
|
||||
markets: { rows: marketRows.value, columns: marketColumns },
|
||||
countries: { rows: countryRows.value, columns: countryColumns },
|
||||
customers: { rows: customerRows.value, columns: customerColumns },
|
||||
sales_color_yaka_market_customer: { rows: filterSourceRowsForTab('sales_color_yaka_market_customer', salesBreakdownRows.sales_color_yaka_market_customer), columns: visibleSalesBreakdownColumns.value },
|
||||
sales_product_country_segment_market_customer: { rows: filterSourceRowsForTab('sales_product_country_segment_market_customer', salesBreakdownRows.sales_product_country_segment_market_customer), columns: visibleSalesBreakdownColumns.value },
|
||||
sales_market_customer_product: { rows: filterSourceRowsForTab('sales_market_customer_product', salesBreakdownRows.sales_market_customer_product), columns: visibleSalesBreakdownColumns.value },
|
||||
sales_country_segment_market_customer_product: { rows: filterSourceRowsForTab('sales_country_segment_market_customer_product', salesBreakdownRows.sales_country_segment_market_customer_product), columns: visibleSalesBreakdownColumns.value }
|
||||
}))
|
||||
|
||||
const filteredProductRows = computed(() => filterRowsForTable('products', rows.value, productColumns.value))
|
||||
const isProductKpiTab = computed(() => activeTab.value === 'products' || activeTab.value === 'product_detail')
|
||||
const activeProductTableKey = computed(() => activeTab.value === 'product_detail' ? 'product_detail' : 'products')
|
||||
@@ -2706,7 +2695,7 @@ const activeGroupSourceRows = computed(() => {
|
||||
const columnFilterOptionMap = computed(() => {
|
||||
const out = {}
|
||||
const tableKey = isProductKpiTab.value ? activeProductTableKey.value : activeTab.value
|
||||
const source = tableFilterSourceMap.value[tableKey]
|
||||
const source = tableFilterSource(tableKey)
|
||||
if (!source) return out
|
||||
out[tableKey] = {}
|
||||
for (const col of source.columns) {
|
||||
@@ -2730,7 +2719,7 @@ const columnFilterOptionMap = computed(() => {
|
||||
|
||||
const productTableRows = computed(() => {
|
||||
const out = []
|
||||
appendGroupRows(out, filteredProductRows.value, 0, ['tab:products'], groupLevels)
|
||||
appendGroupRows(out, filteredProductRows.value, 0, ['tab:products'], groupLevels, 'products')
|
||||
return out
|
||||
})
|
||||
const detailProductTableRows = computed(() => buildGroupedTableRows('product_detail', filteredDetailProductRows.value))
|
||||
@@ -2813,13 +2802,14 @@ const allProductGroupsExpanded = computed(() => {
|
||||
return selectedExpandThroughLevel.value >= fullExpandThroughLevel.value
|
||||
})
|
||||
|
||||
function appendGroupRows (out, sourceRows, level, parentKeys, levels = groupLevels) {
|
||||
appendGroupRowsAt(out, sourceRows, level, 0, parentKeys, levels)
|
||||
function appendGroupRows (out, sourceRows, level, parentKeys, levels = groupLevels, tableKey = activeTab.value) {
|
||||
appendGroupRowsAt(out, sourceRows, level, 0, parentKeys, levels, tableKey)
|
||||
}
|
||||
|
||||
function appendGroupRowsAt (out, sourceRows, level, visualLevel, parentKeys, levels = groupLevels) {
|
||||
function appendGroupRowsAt (out, sourceRows, level, visualLevel, parentKeys, levels = groupLevels, tableKey = activeTab.value) {
|
||||
if (out.length >= maxRenderedGroupedRows) return
|
||||
if (level >= levels.length) {
|
||||
out.push(...sortProductLeafRows(sourceRows))
|
||||
out.push(...sortLeafRowsForTable(tableKey, sourceRows).slice(0, Math.max(0, maxRenderedGroupedRows - out.length)))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2832,16 +2822,17 @@ function appendGroupRowsAt (out, sourceRows, level, visualLevel, parentKeys, lev
|
||||
}
|
||||
|
||||
Array.from(grouped.entries())
|
||||
.sort((a, b) => a[0].localeCompare(b[0], 'tr'))
|
||||
.sort((a, b) => compareGroupedBucketsForTable(tableKey, a, b, groupDef))
|
||||
.forEach(([value, groupRows]) => {
|
||||
if (out.length >= maxRenderedGroupedRows) return
|
||||
if (shouldSkipGroupValue(groupDef.key, value)) {
|
||||
appendGroupRowsAt(out, groupRows, level + 1, visualLevel, parentKeys, levels)
|
||||
appendGroupRowsAt(out, groupRows, level + 1, visualLevel, parentKeys, levels, tableKey)
|
||||
return
|
||||
}
|
||||
const key = [...parentKeys, `${groupDef.key}:${value}`].join('|')
|
||||
out.push(makeGroupRow(key, visualLevel, groupDef, value, groupRows))
|
||||
if (isGroupExpanded(key)) {
|
||||
appendGroupRowsAt(out, groupRows, level + 1, visualLevel + 1, [...parentKeys, `${groupDef.key}:${value}`], levels)
|
||||
appendGroupRowsAt(out, groupRows, level + 1, visualLevel + 1, [...parentKeys, `${groupDef.key}:${value}`], levels, tableKey)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -2849,7 +2840,7 @@ function appendGroupRowsAt (out, sourceRows, level, visualLevel, parentKeys, lev
|
||||
function buildGroupedTableRows (tableKey, sourceRows) {
|
||||
const levels = tabGroupLevels[tableKey] || groupLevels
|
||||
const out = []
|
||||
appendGroupRows(out, sourceRows, 0, [`tab:${tableKey}`], levels)
|
||||
appendGroupRows(out, sourceRows, 0, [`tab:${tableKey}`], levels, tableKey)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -2863,6 +2854,22 @@ function filterSourceRowsForTab (tabKey, fallbackRows) {
|
||||
return fallbackRows
|
||||
}
|
||||
|
||||
function tableFilterSource (tableKey) {
|
||||
if (tableKey === 'products') return { rows: filterSourceRowsForTab('products', rows.value), columns: productColumns.value }
|
||||
if (tableKey === 'product_detail') return { rows: filterSourceRowsForTab('product_detail', detailProductRows.value), columns: productDetailColumns.value }
|
||||
if (tableKey === 'general') return { rows: generalRows.value, columns: generalColumns }
|
||||
if (tableKey === 'order_product_customers') return { rows: filterSourceRowsForTab('order_product_customers', orderProductCustomerRows.value), columns: orderProductCustomerColumns }
|
||||
if (tableKey === 'order_market_details') return { rows: filterSourceRowsForTab('order_market_details', orderMarketDetailRows.value), columns: orderMarketDetailColumns }
|
||||
if (tableKey === 'idle') return { rows: filterSourceRowsForTab('idle', idleRows.value), columns: idleColumns }
|
||||
if (tableKey === 'markets') return { rows: marketRows.value, columns: marketColumns }
|
||||
if (tableKey === 'countries') return { rows: countryRows.value, columns: countryColumns }
|
||||
if (tableKey === 'customers') return { rows: customerRows.value, columns: customerColumns }
|
||||
if (salesBreakdownTabKeys.includes(tableKey)) {
|
||||
return { rows: filterSourceRowsForTab(tableKey, salesBreakdownRows[tableKey] || []), columns: visibleSalesBreakdownColumns.value }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function columnsForTableKey (tabKey) {
|
||||
if (tabKey === 'products') return productColumns.value
|
||||
if (tabKey === 'product_detail') return productDetailColumns.value
|
||||
@@ -2915,6 +2922,45 @@ function sortProductLeafRows (sourceRows) {
|
||||
})
|
||||
}
|
||||
|
||||
function sortLeafRowsForTable (tableKey, sourceRows) {
|
||||
const { sortBy, descending } = tableSortState(tableKey)
|
||||
if (!sortBy) return sortProductLeafRows(sourceRows)
|
||||
return sortFlatTableRows(sourceRows, sortBy, descending)
|
||||
}
|
||||
|
||||
function tableSortState (tableKey) {
|
||||
const pagination = tablePagination[tableKey] || {}
|
||||
return {
|
||||
sortBy: pagination.sortBy || '',
|
||||
descending: pagination.descending !== false
|
||||
}
|
||||
}
|
||||
|
||||
function compareGroupedBucketsForTable (tableKey, left, right, groupDef) {
|
||||
const [leftLabel, leftRows] = left
|
||||
const [rightLabel, rightRows] = right
|
||||
const { sortBy, descending } = tableSortState(tableKey)
|
||||
if (!sortBy || sortBy === groupDef.key) {
|
||||
return String(leftLabel).localeCompare(String(rightLabel), 'tr', { numeric: true, sensitivity: 'base' })
|
||||
}
|
||||
|
||||
const leftValue = groupedBucketSortValue(leftRows, sortBy)
|
||||
const rightValue = groupedBucketSortValue(rightRows, sortBy)
|
||||
const cmp = compareColumnSortValues(leftValue, rightValue)
|
||||
if (cmp !== 0) return descending ? -cmp : cmp
|
||||
return String(leftLabel).localeCompare(String(rightLabel), 'tr', { numeric: true, sensitivity: 'base' })
|
||||
}
|
||||
|
||||
function groupedBucketSortValue (sourceRows, sortBy) {
|
||||
if (!sourceRows?.length) return 0
|
||||
if (sortBy === 'stock_qty') return distinctVariantStockQty(sourceRows)
|
||||
if (sortBy === 'idle_cost_usd') return distinctVariantStockCost(sourceRows)
|
||||
if (sortBy === 'base_price_usd' || sortBy === 'cost_price_usd') return weightedAverageProductCost(sourceRows, sortBy)
|
||||
if (shouldSumField(sortBy)) return sumRows(sourceRows, sortBy)
|
||||
if (shouldAverageField(sortBy)) return weightedAverageOrAverage(sourceRows, sortBy, weightFieldForMetric(sortBy))
|
||||
return sortValueForColumnName(sourceRows[0], sortBy)
|
||||
}
|
||||
|
||||
function sortProductGroupedTableRows (tableRows, sortBy, descending) {
|
||||
const source = Array.isArray(tableRows) ? tableRows : []
|
||||
if (!sortBy || !source.some(row => row?.__group)) {
|
||||
@@ -3437,13 +3483,10 @@ function marginFromRows (sourceRows, salesField, qtyField, unitCostField) {
|
||||
}
|
||||
|
||||
function normalizeGroupValue (value) {
|
||||
return String(value || '').trim()
|
||||
return String(value || '').trim() || '(Boş)'
|
||||
}
|
||||
|
||||
function shouldSkipGroupValue (field, value) {
|
||||
const text = String(value || '').trim()
|
||||
if (field === 'urun_ilk_grubu') return !text || text === '-'
|
||||
if (field === 'askili_yan') return !text || text === '-'
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -3709,14 +3752,15 @@ function syncExpansionAfterFilterChange () {
|
||||
}
|
||||
|
||||
function filterRowsForTable (tableKey, sourceRows, sourceColumns) {
|
||||
return sourceRows.filter(row => {
|
||||
return sourceColumns.every(col => {
|
||||
if (!isColumnFilterable(col.name)) return true
|
||||
const selected = selectedColumnFilters(tableKey, col.name)
|
||||
if (!selected.length) return true
|
||||
return selected.includes(columnFilterValue(row, col))
|
||||
})
|
||||
})
|
||||
const activeFilters = sourceColumns
|
||||
.filter(col => isColumnFilterable(col.name))
|
||||
.map(col => ({
|
||||
col,
|
||||
selected: new Set(selectedColumnFilters(tableKey, col.name))
|
||||
}))
|
||||
.filter(item => item.selected.size > 0)
|
||||
if (!activeFilters.length) return sourceRows
|
||||
return sourceRows.filter(row => activeFilters.every(item => item.selected.has(columnFilterValue(row, item.col))))
|
||||
}
|
||||
|
||||
function columnFilterValue (row, col) {
|
||||
@@ -4465,7 +4509,6 @@ function setupTopScrollbarSync () {
|
||||
|
||||
function switchPerformanceTab (tabName) {
|
||||
if (!tabName || tabName === activeTab.value || loading.value) return
|
||||
backendGroupedLoading.value = true
|
||||
activeTab.value = tabName
|
||||
}
|
||||
|
||||
@@ -4887,7 +4930,7 @@ onBeforeUnmount(() => {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
background: rgba(246, 247, 249, 0.82);
|
||||
background: #f6f7f9;
|
||||
backdrop-filter: blur(2px);
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user