Fix product performance grouped JSON build
This commit is contained in:
@@ -273,6 +273,13 @@ func GetProductPerformanceGroupedHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
groupLevels := splitCSVQuery(r.URL.Query().Get("group_levels"))
|
groupLevels := splitCSVQuery(r.URL.Query().Get("group_levels"))
|
||||||
limit := intQuery(r, "limit", 50000)
|
limit := intQuery(r, "limit", 50000)
|
||||||
expanded := map[string]bool{}
|
expanded := map[string]bool{}
|
||||||
|
const maxExpandedGroupKeys = 200
|
||||||
|
addExpandedKey := func(key string) {
|
||||||
|
key = strings.TrimSpace(key)
|
||||||
|
if key != "" && len(expanded) < maxExpandedGroupKeys {
|
||||||
|
expanded[key] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
if r.Method == http.MethodPost {
|
if r.Method == http.MethodPost {
|
||||||
var body struct {
|
var body struct {
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
@@ -298,17 +305,11 @@ func GetProductPerformanceGroupedHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
limit = body.Limit
|
limit = body.Limit
|
||||||
}
|
}
|
||||||
for _, key := range body.ExpandedKeys {
|
for _, key := range body.ExpandedKeys {
|
||||||
key = strings.TrimSpace(key)
|
addExpandedKey(key)
|
||||||
if key != "" {
|
|
||||||
expanded[key] = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, key := range strings.Split(r.URL.Query().Get("expanded_keys"), ",") {
|
for _, key := range strings.Split(r.URL.Query().Get("expanded_keys"), ",") {
|
||||||
key = strings.TrimSpace(key)
|
addExpandedKey(key)
|
||||||
if key != "" {
|
|
||||||
expanded[key] = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,7 @@
|
|||||||
:pagination="{ rowsPerPage: 0, sortBy: 'performance_score_total', descending: true }"
|
:pagination="{ rowsPerPage: 0, sortBy: 'performance_score_total', descending: true }"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="64"
|
:virtual-scroll-item-size="64"
|
||||||
|
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
||||||
>
|
>
|
||||||
<template #header-cell="props">
|
<template #header-cell="props">
|
||||||
<q-th :props="props" class="filterable-header-cell">
|
<q-th :props="props" class="filterable-header-cell">
|
||||||
@@ -224,6 +225,7 @@
|
|||||||
:sort-method="sortProductGroupedTableRows"
|
:sort-method="sortProductGroupedTableRows"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="64"
|
:virtual-scroll-item-size="64"
|
||||||
|
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
||||||
>
|
>
|
||||||
<template #header-cell="props">
|
<template #header-cell="props">
|
||||||
<q-th :props="props" class="filterable-header-cell">
|
<q-th :props="props" class="filterable-header-cell">
|
||||||
@@ -414,6 +416,7 @@
|
|||||||
:sort-method="sortProductGroupedTableRows"
|
:sort-method="sortProductGroupedTableRows"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="64"
|
:virtual-scroll-item-size="64"
|
||||||
|
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
||||||
>
|
>
|
||||||
<template #header-cell="props">
|
<template #header-cell="props">
|
||||||
<q-th :props="props" class="filterable-header-cell">
|
<q-th :props="props" class="filterable-header-cell">
|
||||||
@@ -556,6 +559,7 @@
|
|||||||
:sort-method="sortProductGroupedTableRows"
|
:sort-method="sortProductGroupedTableRows"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="64"
|
:virtual-scroll-item-size="64"
|
||||||
|
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
||||||
>
|
>
|
||||||
<template #header-cell="props">
|
<template #header-cell="props">
|
||||||
<q-th :props="props" class="filterable-header-cell">
|
<q-th :props="props" class="filterable-header-cell">
|
||||||
@@ -711,6 +715,7 @@
|
|||||||
:sort-method="sortProductGroupedTableRows"
|
:sort-method="sortProductGroupedTableRows"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="64"
|
:virtual-scroll-item-size="64"
|
||||||
|
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
||||||
>
|
>
|
||||||
<template #header-cell="props">
|
<template #header-cell="props">
|
||||||
<q-th :props="props" class="filterable-header-cell">
|
<q-th :props="props" class="filterable-header-cell">
|
||||||
@@ -896,6 +901,7 @@
|
|||||||
:pagination="{ rowsPerPage: 0 }"
|
:pagination="{ rowsPerPage: 0 }"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="64"
|
:virtual-scroll-item-size="64"
|
||||||
|
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
||||||
>
|
>
|
||||||
<template #header-cell="props">
|
<template #header-cell="props">
|
||||||
<q-th :props="props" class="filterable-header-cell">
|
<q-th :props="props" class="filterable-header-cell">
|
||||||
@@ -1003,6 +1009,7 @@
|
|||||||
:sort-method="sortProductGroupedTableRows"
|
:sort-method="sortProductGroupedTableRows"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="64"
|
:virtual-scroll-item-size="64"
|
||||||
|
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
||||||
>
|
>
|
||||||
<template #header-cell="props">
|
<template #header-cell="props">
|
||||||
<q-th :props="props" class="filterable-header-cell">
|
<q-th :props="props" class="filterable-header-cell">
|
||||||
@@ -1160,6 +1167,7 @@
|
|||||||
:pagination="{ rowsPerPage: 0 }"
|
:pagination="{ rowsPerPage: 0 }"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="64"
|
:virtual-scroll-item-size="64"
|
||||||
|
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
||||||
>
|
>
|
||||||
<template #header-cell="props">
|
<template #header-cell="props">
|
||||||
<q-th :props="props" class="filterable-header-cell">
|
<q-th :props="props" class="filterable-header-cell">
|
||||||
@@ -1261,6 +1269,7 @@
|
|||||||
:pagination="{ rowsPerPage: 0 }"
|
:pagination="{ rowsPerPage: 0 }"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="64"
|
:virtual-scroll-item-size="64"
|
||||||
|
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
||||||
>
|
>
|
||||||
<template #header-cell="props">
|
<template #header-cell="props">
|
||||||
<q-th :props="props" class="filterable-header-cell">
|
<q-th :props="props" class="filterable-header-cell">
|
||||||
@@ -1532,6 +1541,9 @@ const orderMarketDetailRows = ref([])
|
|||||||
const orderAnalysisLoaded = ref(false)
|
const orderAnalysisLoaded = ref(false)
|
||||||
const orderProductCustomersLoaded = ref(false)
|
const orderProductCustomersLoaded = ref(false)
|
||||||
const orderMarketDetailsLoaded = ref(false)
|
const orderMarketDetailsLoaded = ref(false)
|
||||||
|
const marketRowsLoaded = ref(false)
|
||||||
|
const countryRowsLoaded = ref(false)
|
||||||
|
const customerRowsLoaded = ref(false)
|
||||||
const orderAnalysisMode = ref('product')
|
const orderAnalysisMode = ref('product')
|
||||||
const marketRows = ref([])
|
const marketRows = ref([])
|
||||||
const countryRows = ref([])
|
const countryRows = ref([])
|
||||||
@@ -1574,10 +1586,10 @@ const columnFilterSearch = reactive({})
|
|||||||
const expandedGroups = ref({})
|
const expandedGroups = ref({})
|
||||||
const lastManualExpandedGroupKey = ref('')
|
const lastManualExpandedGroupKey = ref('')
|
||||||
const selectedExpandLevelKeysByTab = reactive({
|
const selectedExpandLevelKeysByTab = reactive({
|
||||||
products: ['urun_ilk_grubu', 'askili_yan', 'kategori', 'urun_ana_grubu', 'urun_alt_grubu'],
|
products: ['urun_ilk_grubu', 'askili_yan', 'kategori'],
|
||||||
product_detail: ['urun_ana_grubu', 'urun_alt_grubu', 'product_code', 'color_yaka'],
|
product_detail: ['urun_ana_grubu', 'urun_alt_grubu'],
|
||||||
sales_color_yaka_market_customer: ['color_yaka', 'urun_ilk_grubu', 'askili_yan'],
|
sales_color_yaka_market_customer: ['color_yaka'],
|
||||||
idle: ['urun_ilk_grubu', 'askili_yan', 'kategori', 'urun_ana_grubu', 'urun_alt_grubu'],
|
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: ['market_key', 'customer_code', 'customer_name', 'urun_ilk_grubu', 'askili_yan'],
|
||||||
sales_market_customer_product: ['market_key', 'customer_code', 'customer_name', 'urun_ilk_grubu', 'askili_yan'],
|
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'],
|
sales_country_segment_market_customer_product: ['market_key', 'customer_code', 'customer_name', 'urun_ilk_grubu', 'askili_yan'],
|
||||||
@@ -1586,7 +1598,15 @@ const selectedExpandLevelKeysByTab = reactive({
|
|||||||
})
|
})
|
||||||
const productKpiFetchLimit = 50000
|
const productKpiFetchLimit = 50000
|
||||||
const reportFetchLimit = 50000
|
const reportFetchLimit = 50000
|
||||||
const maxBulkExpandKeys = 1200
|
const maxBulkExpandKeys = 500
|
||||||
|
const maxBackendExpandedKeys = 120
|
||||||
|
const tableVirtualSliceSize = 160
|
||||||
|
const maxAutoExpandLevelByTab = {
|
||||||
|
products: 2,
|
||||||
|
product_detail: 1,
|
||||||
|
idle: 2,
|
||||||
|
sales_color_yaka_market_customer: 0
|
||||||
|
}
|
||||||
const performanceTabs = [
|
const performanceTabs = [
|
||||||
{ name: 'products', icon: 'dashboard', label: 'Genel Özet KPI' },
|
{ name: 'products', icon: 'dashboard', label: 'Genel Özet KPI' },
|
||||||
{ name: 'product_detail', icon: 'category', label: 'Detay KPI' },
|
{ name: 'product_detail', icon: 'category', label: 'Detay KPI' },
|
||||||
@@ -1597,7 +1617,7 @@ const performanceTabs = [
|
|||||||
{ name: 'order_product_customers', icon: 'assignment_ind', label: 'Ürün > Piyasa > Müşteri Sipariş' },
|
{ name: 'order_product_customers', icon: 'assignment_ind', label: 'Ürün > Piyasa > Müşteri Sipariş' },
|
||||||
{ name: 'order_market_details', icon: 'receipt_long', label: 'Piyasa > Müşteri > Ürün Sipariş' }
|
{ name: 'order_market_details', icon: 'receipt_long', label: 'Piyasa > Müşteri > Ürün Sipariş' }
|
||||||
]
|
]
|
||||||
const pageBusy = computed(() => loading.value || backendGroupedLoading.value || imagePrimeLoading.value || filterBusy.value)
|
const pageBusy = computed(() => loading.value || filterBusy.value)
|
||||||
|
|
||||||
const pagination = ref({
|
const pagination = ref({
|
||||||
page: 1,
|
page: 1,
|
||||||
@@ -2558,26 +2578,38 @@ const selectedExpandThroughLevel = computed(() => {
|
|||||||
return indexes.length ? Math.max(...indexes) : -1
|
return indexes.length ? Math.max(...indexes) : -1
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function maxAutoExpandThroughLevelForTab (tabKey = activeTab.value) {
|
||||||
|
return Number.isFinite(maxAutoExpandLevelByTab[tabKey]) ? maxAutoExpandLevelByTab[tabKey] : 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function maxBackendExpandedKeysForTab (tabKey = activeTab.value) {
|
||||||
|
if (tabKey === 'product_detail') return 160
|
||||||
|
if (tabKey === 'products' || tabKey === 'idle') return maxBackendExpandedKeys
|
||||||
|
if (salesBreakdownTabKeys.includes(tabKey)) return 80
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
|
||||||
const autoExpandThroughLevel = computed(() => {
|
const autoExpandThroughLevel = computed(() => {
|
||||||
const selectedLevel = selectedExpandThroughLevel.value
|
const selectedLevel = selectedExpandThroughLevel.value
|
||||||
const lastGroupLevel = Math.max(0, activeGroupLevels.value.length - 2)
|
const lastGroupLevel = Math.max(0, activeGroupLevels.value.length - 2)
|
||||||
return selectedLevel >= 0 ? Math.min(selectedLevel, lastGroupLevel) : Math.min(2, lastGroupLevel)
|
const requestedLevel = selectedLevel >= 0 ? selectedLevel : 2
|
||||||
|
return Math.min(requestedLevel, lastGroupLevel, maxAutoExpandThroughLevelForTab())
|
||||||
})
|
})
|
||||||
|
|
||||||
const tableFilterSourceMap = computed(() => ({
|
const tableFilterSourceMap = computed(() => ({
|
||||||
products: { rows: rows.value, columns: productColumns.value },
|
products: { rows: filterSourceRowsForTab('products', rows.value), columns: productColumns.value },
|
||||||
product_detail: { rows: detailProductRows.value, columns: productDetailColumns.value },
|
product_detail: { rows: filterSourceRowsForTab('product_detail', detailProductRows.value), columns: productDetailColumns.value },
|
||||||
general: { rows: generalRows.value, columns: generalColumns },
|
general: { rows: generalRows.value, columns: generalColumns },
|
||||||
order_product_customers: { rows: orderProductCustomerRows.value, columns: orderProductCustomerColumns },
|
order_product_customers: { rows: filterSourceRowsForTab('order_product_customers', orderProductCustomerRows.value), columns: orderProductCustomerColumns },
|
||||||
order_market_details: { rows: orderMarketDetailRows.value, columns: orderMarketDetailColumns },
|
order_market_details: { rows: filterSourceRowsForTab('order_market_details', orderMarketDetailRows.value), columns: orderMarketDetailColumns },
|
||||||
idle: { rows: idleRows.value, columns: idleColumns },
|
idle: { rows: filterSourceRowsForTab('idle', idleRows.value), columns: idleColumns },
|
||||||
markets: { rows: marketRows.value, columns: marketColumns },
|
markets: { rows: marketRows.value, columns: marketColumns },
|
||||||
countries: { rows: countryRows.value, columns: countryColumns },
|
countries: { rows: countryRows.value, columns: countryColumns },
|
||||||
customers: { rows: customerRows.value, columns: customerColumns },
|
customers: { rows: customerRows.value, columns: customerColumns },
|
||||||
sales_color_yaka_market_customer: { rows: salesBreakdownRows.sales_color_yaka_market_customer, columns: visibleSalesBreakdownColumns.value },
|
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: salesBreakdownRows.sales_product_country_segment_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: salesBreakdownRows.sales_market_customer_product, 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: salesBreakdownRows.sales_country_segment_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 filteredProductRows = computed(() => filterRowsForTable('products', rows.value, productColumns.value))
|
||||||
@@ -2722,11 +2754,34 @@ function buildGroupedTableRows (tableKey, sourceRows) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function backendRowsForTab (tabKey, fallbackRows) {
|
function backendRowsForTab (tabKey, fallbackRows) {
|
||||||
if (hasActiveTableFilters(tabKey)) return fallbackRows
|
const rows = backendGroupedRows.value[tabKey]
|
||||||
|
const backendRows = Array.isArray(rows) && rows.length ? rows : []
|
||||||
|
const sourceRows = backendRows.length ? backendRows : fallbackRows
|
||||||
|
if (hasActiveTableFilters(tabKey)) {
|
||||||
|
return filterRowsForTable(tabKey, sourceRows, columnsForTableKey(tabKey))
|
||||||
|
}
|
||||||
|
return sourceRows
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterSourceRowsForTab (tabKey, fallbackRows) {
|
||||||
const rows = backendGroupedRows.value[tabKey]
|
const rows = backendGroupedRows.value[tabKey]
|
||||||
return Array.isArray(rows) && rows.length ? rows : fallbackRows
|
return Array.isArray(rows) && rows.length ? rows : fallbackRows
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function columnsForTableKey (tabKey) {
|
||||||
|
if (tabKey === 'products') return productColumns.value
|
||||||
|
if (tabKey === 'product_detail') return productDetailColumns.value
|
||||||
|
if (tabKey === 'general') return generalColumns
|
||||||
|
if (tabKey === 'order_product_customers') return orderProductCustomerColumns
|
||||||
|
if (tabKey === 'order_market_details') return orderMarketDetailColumns
|
||||||
|
if (tabKey === 'idle') return idleColumns
|
||||||
|
if (tabKey === 'markets') return marketColumns
|
||||||
|
if (tabKey === 'countries') return countryColumns
|
||||||
|
if (tabKey === 'customers') return customerColumns
|
||||||
|
if (salesBreakdownTabKeys.includes(tabKey)) return visibleSalesBreakdownColumns.value
|
||||||
|
return productColumns.value
|
||||||
|
}
|
||||||
|
|
||||||
function collectGroupKeys (out, sourceRows, level, parentKeys, levels = groupLevels, maxLevel = levels.length - 1) {
|
function collectGroupKeys (out, sourceRows, level, parentKeys, levels = groupLevels, maxLevel = levels.length - 1) {
|
||||||
if (out.length >= maxBulkExpandKeys) return
|
if (out.length >= maxBulkExpandKeys) return
|
||||||
if (level >= levels.length) return
|
if (level >= levels.length) return
|
||||||
@@ -3327,7 +3382,8 @@ function collapseAllProductGroups () {
|
|||||||
|
|
||||||
function expandSelectedProductGroups () {
|
function expandSelectedProductGroups () {
|
||||||
const keys = productAutoExpandKeys.value
|
const keys = productAutoExpandKeys.value
|
||||||
const expandableKeys = keys.slice(0, maxBulkExpandKeys)
|
const maxKeys = Math.min(maxBulkExpandKeys, maxBackendExpandedKeysForTab(activeTab.value))
|
||||||
|
const expandableKeys = keys.slice(0, maxKeys)
|
||||||
const tabPrefix = `tab:${activeTab.value}|`
|
const tabPrefix = `tab:${activeTab.value}|`
|
||||||
const next = Object.fromEntries(
|
const next = Object.fromEntries(
|
||||||
Object.entries(expandedGroups.value).filter(([key]) => !key.startsWith(tabPrefix))
|
Object.entries(expandedGroups.value).filter(([key]) => !key.startsWith(tabPrefix))
|
||||||
@@ -4132,16 +4188,22 @@ function backendGroupedSupportedTab (tabKey = activeTab.value) {
|
|||||||
function activeExpandedGroupKeys () {
|
function activeExpandedGroupKeys () {
|
||||||
const tabPrefix = `tab:${activeTab.value}|`
|
const tabPrefix = `tab:${activeTab.value}|`
|
||||||
const validFields = new Set(activeGroupLevels.value.map(level => level.key))
|
const validFields = new Set(activeGroupLevels.value.map(level => level.key))
|
||||||
|
const maxKeys = maxBackendExpandedKeysForTab(activeTab.value)
|
||||||
const keys = Object.entries(expandedGroups.value)
|
const keys = Object.entries(expandedGroups.value)
|
||||||
.filter(([, value]) => value === true)
|
.filter(([, value]) => value === true)
|
||||||
.map(([key]) => key)
|
.map(([key]) => key)
|
||||||
.filter(key => key.startsWith(tabPrefix))
|
.filter(key => key.startsWith(tabPrefix))
|
||||||
.filter(key => key.split('|').slice(1).every(part => validFields.has(String(part).split(':')[0])))
|
.filter(key => key.split('|').slice(1).every(part => validFields.has(String(part).split(':')[0])))
|
||||||
|
.sort((a, b) => {
|
||||||
|
const depthDiff = a.split('|').length - b.split('|').length
|
||||||
|
if (depthDiff !== 0) return depthDiff
|
||||||
|
return a.localeCompare(b, 'tr', { numeric: true })
|
||||||
|
})
|
||||||
const manualKey = lastManualExpandedGroupKey.value
|
const manualKey = lastManualExpandedGroupKey.value
|
||||||
const out = []
|
const out = []
|
||||||
if (manualKey && keys.includes(manualKey)) out.push(manualKey)
|
if (manualKey && keys.includes(manualKey)) out.push(manualKey)
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
if (out.length >= maxBulkExpandKeys) break
|
if (out.length >= maxKeys) break
|
||||||
if (key !== manualKey) out.push(key)
|
if (key !== manualKey) out.push(key)
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
@@ -4168,12 +4230,12 @@ function scheduleLoadBackendGroupedRows () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function switchPerformanceTab (tabName) {
|
function switchPerformanceTab (tabName) {
|
||||||
if (!tabName || tabName === activeTab.value || pageBusy.value) return
|
if (!tabName || tabName === activeTab.value || loading.value) return
|
||||||
backendGroupedLoading.value = true
|
backendGroupedLoading.value = true
|
||||||
activeTab.value = tabName
|
activeTab.value = tabName
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadBackendGroupedRows () {
|
async function loadBackendGroupedRows (options = {}) {
|
||||||
const tabKey = activeTab.value
|
const tabKey = activeTab.value
|
||||||
if (!backendGroupedSupportedTab(tabKey)) return
|
if (!backendGroupedSupportedTab(tabKey)) return
|
||||||
pruneActiveExpandedGroups()
|
pruneActiveExpandedGroups()
|
||||||
@@ -4189,7 +4251,7 @@ async function loadBackendGroupedRows () {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const startedAt = performance.now()
|
const startedAt = performance.now()
|
||||||
const rows = await performanceStore.fetchGroupedRows(params)
|
const rows = await performanceStore.fetchGroupedRows(params, { force: options.force === true })
|
||||||
const normalizedRows = Array.isArray(rows) ? rows.map(row => normalizeGroupedDisplayRow(tabKey, row)) : []
|
const normalizedRows = Array.isArray(rows) ? rows.map(row => normalizeGroupedDisplayRow(tabKey, row)) : []
|
||||||
backendGroupedRows.value = {
|
backendGroupedRows.value = {
|
||||||
...backendGroupedRows.value,
|
...backendGroupedRows.value,
|
||||||
@@ -4269,13 +4331,10 @@ async function timedProductPerformanceGet (label, url, config = {}) {
|
|||||||
async function reload () {
|
async function reload () {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const salesBreakdownRequests = salesBreakdownTabKeys.map(tabKey => {
|
marketRowsLoaded.value = false
|
||||||
return timedProductPerformanceGet(`sales-breakdown:${tabKey}`, '/pricing/product-performance/sales-breakdown', {
|
countryRowsLoaded.value = false
|
||||||
params: { limit: reportFetchLimit, mode: salesBreakdownModes[tabKey] },
|
customerRowsLoaded.value = false
|
||||||
timeout: 60000
|
const [summaryResp, listResp, generalResp] = await Promise.all([
|
||||||
})
|
|
||||||
})
|
|
||||||
const [summaryResp, listResp, generalResp, marketResp, countryResp, customerResp, ...salesBreakdownResp] = await Promise.all([
|
|
||||||
timedProductPerformanceGet('summary', '/pricing/product-performance/summary', { timeout: 60000 }),
|
timedProductPerformanceGet('summary', '/pricing/product-performance/summary', { timeout: 60000 }),
|
||||||
timedProductPerformanceGet('product-kpi', '/pricing/product-performance', {
|
timedProductPerformanceGet('product-kpi', '/pricing/product-performance', {
|
||||||
params: {
|
params: {
|
||||||
@@ -4284,14 +4343,7 @@ async function reload () {
|
|||||||
},
|
},
|
||||||
timeout: 60000
|
timeout: 60000
|
||||||
}),
|
}),
|
||||||
timedProductPerformanceGet('general', '/pricing/product-performance/general', { params: { limit: reportFetchLimit }, timeout: 60000 }),
|
timedProductPerformanceGet('general', '/pricing/product-performance/general', { params: { limit: reportFetchLimit }, timeout: 60000 })
|
||||||
timedProductPerformanceGet('markets', '/pricing/product-performance/markets', { params: { limit: reportFetchLimit }, timeout: 60000 }),
|
|
||||||
timedProductPerformanceGet('countries', '/pricing/product-performance/countries', { params: { limit: reportFetchLimit }, timeout: 60000 }),
|
|
||||||
timedProductPerformanceGet('customers', '/pricing/product-performance/customers', {
|
|
||||||
params: { limit: reportFetchLimit, breakdown: customerBreakdown.value },
|
|
||||||
timeout: 60000
|
|
||||||
}),
|
|
||||||
...salesBreakdownRequests
|
|
||||||
])
|
])
|
||||||
summary.value = summaryResp?.data || {}
|
summary.value = summaryResp?.data || {}
|
||||||
const normalizedGeneralRows = (Array.isArray(generalResp?.data) ? generalResp.data : []).map(normalizeGeneralRow)
|
const normalizedGeneralRows = (Array.isArray(generalResp?.data) ? generalResp.data : []).map(normalizeGeneralRow)
|
||||||
@@ -4301,25 +4353,8 @@ async function reload () {
|
|||||||
return normalizeRow({ ...row, ...generalMetrics })
|
return normalizeRow({ ...row, ...generalMetrics })
|
||||||
})
|
})
|
||||||
generalRows.value = normalizedGeneralRows
|
generalRows.value = normalizedGeneralRows
|
||||||
marketRows.value = Array.isArray(marketResp?.data) ? marketResp.data : []
|
await ensureActiveTabData(false)
|
||||||
countryRows.value = (Array.isArray(countryResp?.data) ? countryResp.data : []).map(row => ({
|
await loadBackendGroupedRows({ force: true })
|
||||||
...row,
|
|
||||||
country_key: `${row.country || '-'}|${row.customer_segment || '-'}|${row.market_key || '-'}`
|
|
||||||
}))
|
|
||||||
customerRows.value = (Array.isArray(customerResp?.data) ? customerResp.data : []).map(row => ({
|
|
||||||
...row,
|
|
||||||
customer_key: `${row.breakdown || '-'}|${row.market_key || '-'}|${row.country || '-'}|${row.customer_segment || '-'}|${row.customer_code || '-'}`
|
|
||||||
}))
|
|
||||||
salesBreakdownTabKeys.forEach((tabKey, index) => {
|
|
||||||
salesBreakdownRows[tabKey] = (Array.isArray(salesBreakdownResp[index]?.data) ? salesBreakdownResp[index].data : []).map(normalizeSalesBreakdownRow)
|
|
||||||
})
|
|
||||||
if (activeTab.value === 'order_product_customers') {
|
|
||||||
await loadOrderProductCustomers(false)
|
|
||||||
}
|
|
||||||
if (activeTab.value === 'order_market_details') {
|
|
||||||
await loadOrderMarketDetails(false)
|
|
||||||
}
|
|
||||||
await loadBackendGroupedRows()
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Ürün performans verisi alınamadı' })
|
Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Ürün performans verisi alınamadı' })
|
||||||
} finally {
|
} finally {
|
||||||
@@ -4353,6 +4388,64 @@ async function loadOrderMarketDetails (showLoading = true) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadMarketRows (showLoading = true) {
|
||||||
|
if (marketRowsLoaded.value) return
|
||||||
|
if (showLoading) loading.value = true
|
||||||
|
try {
|
||||||
|
const resp = await timedProductPerformanceGet('markets', '/pricing/product-performance/markets', { params: { limit: reportFetchLimit }, timeout: 60000 })
|
||||||
|
marketRows.value = Array.isArray(resp?.data) ? resp.data : []
|
||||||
|
marketRowsLoaded.value = true
|
||||||
|
} catch (err) {
|
||||||
|
Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Piyasa performans verisi alınamadı' })
|
||||||
|
} finally {
|
||||||
|
if (showLoading) loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadCountryRows (showLoading = true) {
|
||||||
|
if (countryRowsLoaded.value) return
|
||||||
|
if (showLoading) loading.value = true
|
||||||
|
try {
|
||||||
|
const resp = await timedProductPerformanceGet('countries', '/pricing/product-performance/countries', { params: { limit: reportFetchLimit }, timeout: 60000 })
|
||||||
|
countryRows.value = (Array.isArray(resp?.data) ? resp.data : []).map(row => ({
|
||||||
|
...row,
|
||||||
|
country_key: `${row.country || '-'}|${row.customer_segment || '-'}|${row.market_key || '-'}`
|
||||||
|
}))
|
||||||
|
countryRowsLoaded.value = true
|
||||||
|
} catch (err) {
|
||||||
|
Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Ülke performans verisi alınamadı' })
|
||||||
|
} finally {
|
||||||
|
if (showLoading) loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadCustomerRows (showLoading = true) {
|
||||||
|
if (customerRowsLoaded.value) return
|
||||||
|
if (showLoading) loading.value = true
|
||||||
|
try {
|
||||||
|
const resp = await timedProductPerformanceGet('customers', '/pricing/product-performance/customers', {
|
||||||
|
params: { limit: reportFetchLimit, breakdown: customerBreakdown.value },
|
||||||
|
timeout: 60000
|
||||||
|
})
|
||||||
|
customerRows.value = (Array.isArray(resp?.data) ? resp.data : []).map(row => ({
|
||||||
|
...row,
|
||||||
|
customer_key: `${row.breakdown || '-'}|${row.market_key || '-'}|${row.country || '-'}|${row.customer_segment || '-'}|${row.customer_code || '-'}`
|
||||||
|
}))
|
||||||
|
customerRowsLoaded.value = true
|
||||||
|
} catch (err) {
|
||||||
|
Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Müşteri performans verisi alınamadı' })
|
||||||
|
} finally {
|
||||||
|
if (showLoading) loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureActiveTabData (showLoading = true) {
|
||||||
|
const tab = activeTab.value
|
||||||
|
if (tab === 'markets') return loadMarketRows(showLoading)
|
||||||
|
if (tab === 'countries') return loadCountryRows(showLoading)
|
||||||
|
if (tab === 'customers') return loadCustomerRows(showLoading)
|
||||||
|
}
|
||||||
|
|
||||||
async function loadOrderAnalysis (showLoading = true) {
|
async function loadOrderAnalysis (showLoading = true) {
|
||||||
if (showLoading) loading.value = true
|
if (showLoading) loading.value = true
|
||||||
try {
|
try {
|
||||||
@@ -4388,13 +4481,8 @@ function bucketColor (value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(activeTab, tab => {
|
watch(activeTab, () => {
|
||||||
if (tab === 'order_product_customers' && !orderProductCustomersLoaded.value) {
|
void ensureActiveTabData(false)
|
||||||
void loadOrderProductCustomers(true)
|
|
||||||
}
|
|
||||||
if (tab === 'order_market_details' && !orderMarketDetailsLoaded.value) {
|
|
||||||
void loadOrderMarketDetails(true)
|
|
||||||
}
|
|
||||||
scheduleLoadBackendGroupedRows()
|
scheduleLoadBackendGroupedRows()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user