Fix product performance grouped JSON build
This commit is contained in:
@@ -16,7 +16,7 @@ func GetProductPerformanceHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
traceID := utils.TraceIDFromRequest(r)
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 60*time.Second)
|
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 180*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
f := queries.ProductPerformanceFilters{
|
f := queries.ProductPerformanceFilters{
|
||||||
@@ -90,7 +90,7 @@ func GetProductPerformanceSummaryHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
traceID := utils.TraceIDFromRequest(r)
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 30*time.Second)
|
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 180*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
summary, err := queries.GetProductPerformanceSummary(ctx, pg)
|
summary, err := queries.GetProductPerformanceSummary(ctx, pg)
|
||||||
@@ -106,7 +106,7 @@ func GetProductPerformanceGeneralHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
traceID := utils.TraceIDFromRequest(r)
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 60*time.Second)
|
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 180*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
rows, err := queries.ListProductPerformanceGeneral(ctx, pg, intQuery(r, "limit", 50000))
|
rows, err := queries.ListProductPerformanceGeneral(ctx, pg, intQuery(r, "limit", 50000))
|
||||||
@@ -122,7 +122,7 @@ func GetProductPerformanceOrdersHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
traceID := utils.TraceIDFromRequest(r)
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 90*time.Second)
|
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 180*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
rows, err := queries.ListProductPerformanceOrderAnalysis(ctx, pg, intQuery(r, "limit", 50000))
|
rows, err := queries.ListProductPerformanceOrderAnalysis(ctx, pg, intQuery(r, "limit", 50000))
|
||||||
@@ -138,7 +138,7 @@ func GetProductPerformanceOrderGroupsHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
traceID := utils.TraceIDFromRequest(r)
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 90*time.Second)
|
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 180*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
rows, err := queries.ListProductPerformanceOrderGroups(
|
rows, err := queries.ListProductPerformanceOrderGroups(
|
||||||
@@ -159,7 +159,7 @@ func GetProductPerformanceOrderProductCustomersHandler(pg *sql.DB) http.HandlerF
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
traceID := utils.TraceIDFromRequest(r)
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 90*time.Second)
|
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 180*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
rows, err := queries.ListProductPerformanceOrderProductCustomers(ctx, pg, intQuery(r, "limit", 50000))
|
rows, err := queries.ListProductPerformanceOrderProductCustomers(ctx, pg, intQuery(r, "limit", 50000))
|
||||||
@@ -175,7 +175,7 @@ func GetProductPerformanceOrderMarketDetailsHandler(pg *sql.DB) http.HandlerFunc
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
traceID := utils.TraceIDFromRequest(r)
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 90*time.Second)
|
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 180*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
rows, err := queries.ListProductPerformanceOrderMarketDetails(ctx, pg, intQuery(r, "limit", 50000))
|
rows, err := queries.ListProductPerformanceOrderMarketDetails(ctx, pg, intQuery(r, "limit", 50000))
|
||||||
@@ -191,7 +191,7 @@ func GetProductPerformanceMarketsHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
traceID := utils.TraceIDFromRequest(r)
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 30*time.Second)
|
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 180*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
rows, err := queries.ListProductPerformanceMarkets(ctx, pg, intQuery(r, "limit", 50000))
|
rows, err := queries.ListProductPerformanceMarkets(ctx, pg, intQuery(r, "limit", 50000))
|
||||||
@@ -207,7 +207,7 @@ func GetProductPerformanceCountriesHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
traceID := utils.TraceIDFromRequest(r)
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 30*time.Second)
|
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 180*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
rows, err := queries.ListProductPerformanceCountries(ctx, pg, intQuery(r, "limit", 50000))
|
rows, err := queries.ListProductPerformanceCountries(ctx, pg, intQuery(r, "limit", 50000))
|
||||||
@@ -223,7 +223,7 @@ func GetProductPerformanceCustomersHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
traceID := utils.TraceIDFromRequest(r)
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 30*time.Second)
|
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 180*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
rows, err := queries.ListProductPerformanceCustomers(
|
rows, err := queries.ListProductPerformanceCustomers(
|
||||||
@@ -244,7 +244,7 @@ func GetProductPerformanceSalesBreakdownHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
traceID := utils.TraceIDFromRequest(r)
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 45*time.Second)
|
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 180*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
rows, err := queries.ListProductPerformanceSalesBreakdown(
|
rows, err := queries.ListProductPerformanceSalesBreakdown(
|
||||||
@@ -265,7 +265,7 @@ func GetProductPerformanceGroupedHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
traceID := utils.TraceIDFromRequest(r)
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 90*time.Second)
|
ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 180*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
mode := strings.TrimSpace(r.URL.Query().Get("mode"))
|
mode := strings.TrimSpace(r.URL.Query().Get("mode"))
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
@wheel.stop
|
@wheel.stop
|
||||||
>
|
>
|
||||||
<q-spinner-gears size="56px" color="primary" />
|
<q-spinner-gears size="56px" color="primary" />
|
||||||
<div class="page-busy-label">Yükleniyor...</div>
|
<div class="page-busy-label">{{ loadingMessage }}</div>
|
||||||
|
<div v-if="loadingElapsedLabel" class="page-busy-time">{{ loadingElapsedLabel }}</div>
|
||||||
</div>
|
</div>
|
||||||
</teleport>
|
</teleport>
|
||||||
<div class="performance-top-bar row items-center justify-between q-col-gutter-xs q-mb-xs">
|
<div class="performance-top-bar row items-center justify-between q-col-gutter-xs q-mb-xs">
|
||||||
@@ -113,7 +114,7 @@
|
|||||||
:rows="filteredGeneralRows"
|
:rows="filteredGeneralRows"
|
||||||
:columns="generalColumns"
|
:columns="generalColumns"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:pagination="{ rowsPerPage: 0, sortBy: 'performance_score_total', descending: true }"
|
v-model:pagination="tablePagination.general"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="productThumbVirtualItemSize"
|
:virtual-scroll-item-size="productThumbVirtualItemSize"
|
||||||
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
||||||
@@ -224,8 +225,8 @@
|
|||||||
class="performance-table sticky-dim-table sticky-dim-8 bg-white"
|
class="performance-table sticky-dim-table sticky-dim-8 bg-white"
|
||||||
:rows="displayOrderProductCustomerTableRows"
|
:rows="displayOrderProductCustomerTableRows"
|
||||||
:columns="orderProductCustomerColumns"
|
:columns="orderProductCustomerColumns"
|
||||||
:loading="loading || backendGroupedLoading"
|
:loading="loading"
|
||||||
:pagination="{ rowsPerPage: 0, sortBy: 'order_usd', descending: true }"
|
v-model:pagination="tablePagination.order_product_customers"
|
||||||
:sort-method="sortProductGroupedTableRows"
|
:sort-method="sortProductGroupedTableRows"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="productThumbVirtualItemSize"
|
:virtual-scroll-item-size="productThumbVirtualItemSize"
|
||||||
@@ -415,8 +416,8 @@
|
|||||||
class="performance-table sticky-dim-table sticky-dim-10 bg-white"
|
class="performance-table sticky-dim-table sticky-dim-10 bg-white"
|
||||||
:rows="displayOrderMarketDetailTableRows"
|
:rows="displayOrderMarketDetailTableRows"
|
||||||
:columns="orderMarketDetailColumns"
|
:columns="orderMarketDetailColumns"
|
||||||
:loading="loading || backendGroupedLoading"
|
:loading="loading"
|
||||||
:pagination="{ rowsPerPage: 0, sortBy: 'order_date', descending: true }"
|
v-model:pagination="tablePagination.order_market_details"
|
||||||
:sort-method="sortProductGroupedTableRows"
|
:sort-method="sortProductGroupedTableRows"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="64"
|
:virtual-scroll-item-size="64"
|
||||||
@@ -558,8 +559,8 @@
|
|||||||
]"
|
]"
|
||||||
:rows="displayProductKpiTableRows"
|
:rows="displayProductKpiTableRows"
|
||||||
:columns="activeProductColumns"
|
:columns="activeProductColumns"
|
||||||
:loading="loading || backendGroupedLoading"
|
:loading="loading"
|
||||||
:pagination="pagination"
|
v-model:pagination="activeProductPagination"
|
||||||
:sort-method="sortProductGroupedTableRows"
|
:sort-method="sortProductGroupedTableRows"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="productThumbVirtualItemSize"
|
:virtual-scroll-item-size="productThumbVirtualItemSize"
|
||||||
@@ -714,8 +715,8 @@
|
|||||||
class="performance-table sticky-dim-table sticky-dim-11 bg-white"
|
class="performance-table sticky-dim-table sticky-dim-11 bg-white"
|
||||||
:rows="displayIdleTableRows"
|
:rows="displayIdleTableRows"
|
||||||
:columns="idleColumns"
|
:columns="idleColumns"
|
||||||
:loading="loading || backendGroupedLoading"
|
:loading="loading"
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
v-model:pagination="tablePagination.idle"
|
||||||
:sort-method="sortProductGroupedTableRows"
|
:sort-method="sortProductGroupedTableRows"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="productThumbVirtualItemSize"
|
:virtual-scroll-item-size="productThumbVirtualItemSize"
|
||||||
@@ -902,7 +903,7 @@
|
|||||||
:rows="filteredMarketRows"
|
:rows="filteredMarketRows"
|
||||||
:columns="marketColumns"
|
:columns="marketColumns"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
v-model:pagination="tablePagination.markets"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="64"
|
:virtual-scroll-item-size="64"
|
||||||
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
||||||
@@ -1008,8 +1009,8 @@
|
|||||||
]"
|
]"
|
||||||
:rows="displayActiveSalesBreakdownTableRows"
|
:rows="displayActiveSalesBreakdownTableRows"
|
||||||
:columns="visibleSalesBreakdownColumns"
|
:columns="visibleSalesBreakdownColumns"
|
||||||
:loading="loading || backendGroupedLoading"
|
:loading="loading"
|
||||||
:pagination="{ rowsPerPage: 0, sortBy: 'performance_score_total', descending: true }"
|
v-model:pagination="activeSalesBreakdownPagination"
|
||||||
:sort-method="sortProductGroupedTableRows"
|
:sort-method="sortProductGroupedTableRows"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="productThumbVirtualItemSize"
|
:virtual-scroll-item-size="productThumbVirtualItemSize"
|
||||||
@@ -1168,7 +1169,7 @@
|
|||||||
:rows="filteredCustomerRows"
|
:rows="filteredCustomerRows"
|
||||||
:columns="customerColumns"
|
:columns="customerColumns"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
v-model:pagination="tablePagination.customers"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="64"
|
:virtual-scroll-item-size="64"
|
||||||
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
||||||
@@ -1270,7 +1271,7 @@
|
|||||||
:rows="filteredCountryRows"
|
:rows="filteredCountryRows"
|
||||||
:columns="countryColumns"
|
:columns="countryColumns"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
v-model:pagination="tablePagination.countries"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:virtual-scroll-item-size="64"
|
:virtual-scroll-item-size="64"
|
||||||
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
:virtual-scroll-slice-size="tableVirtualSliceSize"
|
||||||
@@ -1566,6 +1567,7 @@ const selectedDetailMainGroup = ref('')
|
|||||||
const imageUrlByKey = ref({})
|
const imageUrlByKey = ref({})
|
||||||
const imageListByKey = ref({})
|
const imageListByKey = ref({})
|
||||||
const imageFailedKeys = ref(new Set())
|
const imageFailedKeys = ref(new Set())
|
||||||
|
const imageLoadingKeys = ref(new Set())
|
||||||
const imageDialog = ref(false)
|
const imageDialog = ref(false)
|
||||||
const imageDialogUrls = ref([])
|
const imageDialogUrls = ref([])
|
||||||
const imageDialogTitle = ref('')
|
const imageDialogTitle = ref('')
|
||||||
@@ -1584,6 +1586,9 @@ const backendGroupedLoading = ref(false)
|
|||||||
const imagePrimeLoading = ref(false)
|
const imagePrimeLoading = ref(false)
|
||||||
const filterBusy = ref(false)
|
const filterBusy = ref(false)
|
||||||
const bulkExpandLoading = ref(false)
|
const bulkExpandLoading = ref(false)
|
||||||
|
const loadingStartedAt = ref(0)
|
||||||
|
const loadingNow = ref(0)
|
||||||
|
const loadingStage = ref('')
|
||||||
const generalRowsLoaded = ref(false)
|
const generalRowsLoaded = ref(false)
|
||||||
const detailLevelMenuOpen = ref(false)
|
const detailLevelMenuOpen = ref(false)
|
||||||
const topScrollbarRef = ref(null)
|
const topScrollbarRef = ref(null)
|
||||||
@@ -1593,6 +1598,7 @@ let imagePrimeActiveRequests = 0
|
|||||||
let activeTableMiddleEl = null
|
let activeTableMiddleEl = null
|
||||||
let topScrollbarResizeObserver = null
|
let topScrollbarResizeObserver = null
|
||||||
let topScrollbarSyncing = false
|
let topScrollbarSyncing = false
|
||||||
|
let loadingTimer = null
|
||||||
|
|
||||||
const columnFilters = reactive({})
|
const columnFilters = reactive({})
|
||||||
const columnFilterSearch = reactive({})
|
const columnFilterSearch = reactive({})
|
||||||
@@ -1631,14 +1637,54 @@ 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 || filterBusy.value || bulkExpandLoading.value)
|
const pageBusy = computed(() => loading.value)
|
||||||
|
const loadingElapsedLabel = computed(() => {
|
||||||
const pagination = ref({
|
if (!loadingStartedAt.value || !loadingNow.value) return ''
|
||||||
page: 1,
|
const seconds = Math.max(0, Math.floor((loadingNow.value - loadingStartedAt.value) / 1000))
|
||||||
rowsPerPage: 0,
|
const minutes = Math.floor(seconds / 60)
|
||||||
sortBy: 'performance_score_total',
|
const rest = seconds % 60
|
||||||
descending: true
|
return minutes > 0 ? `${minutes}:${String(rest).padStart(2, '0')}` : `${seconds} sn`
|
||||||
})
|
})
|
||||||
|
const loadingMessage = computed(() => loadingStage.value || 'Rapor verileri yükleniyor...')
|
||||||
|
|
||||||
|
const tablePagination = reactive({
|
||||||
|
general: { page: 1, rowsPerPage: 0, sortBy: 'performance_score_total', descending: true },
|
||||||
|
products: { page: 1, rowsPerPage: 0, sortBy: 'performance_score_total', descending: true },
|
||||||
|
product_detail: { page: 1, rowsPerPage: 0, sortBy: 'performance_score_total', descending: true },
|
||||||
|
order_product_customers: { page: 1, rowsPerPage: 0, sortBy: 'order_usd', descending: true },
|
||||||
|
order_market_details: { page: 1, rowsPerPage: 0, sortBy: 'order_date', descending: true },
|
||||||
|
idle: { page: 1, rowsPerPage: 0, sortBy: 'idle_cost_usd', descending: true },
|
||||||
|
markets: { page: 1, rowsPerPage: 0, sortBy: 'sales_usd_90d', descending: true },
|
||||||
|
countries: { page: 1, rowsPerPage: 0, sortBy: 'sales_usd_90d', descending: true },
|
||||||
|
customers: { page: 1, rowsPerPage: 0, sortBy: 'sales_usd_365d', descending: true },
|
||||||
|
sales_color_yaka_market_customer: { page: 1, rowsPerPage: 0, sortBy: 'performance_score_total', descending: true },
|
||||||
|
sales_product_country_segment_market_customer: { page: 1, rowsPerPage: 0, sortBy: 'performance_score_total', descending: true },
|
||||||
|
sales_market_customer_product: { page: 1, rowsPerPage: 0, sortBy: 'performance_score_total', descending: true },
|
||||||
|
sales_country_segment_market_customer_product: { page: 1, rowsPerPage: 0, sortBy: 'performance_score_total', descending: true }
|
||||||
|
})
|
||||||
|
|
||||||
|
const activeProductPagination = computed({
|
||||||
|
get: () => tablePagination[activeTab.value === 'product_detail' ? 'product_detail' : 'products'],
|
||||||
|
set: value => {
|
||||||
|
tablePagination[activeTab.value === 'product_detail' ? 'product_detail' : 'products'] = normalizeTablePagination(value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const activeSalesBreakdownPagination = computed({
|
||||||
|
get: () => tablePagination[activeTab.value] || tablePagination.sales_color_yaka_market_customer,
|
||||||
|
set: value => {
|
||||||
|
tablePagination[activeTab.value] = normalizeTablePagination(value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function normalizeTablePagination (value) {
|
||||||
|
return {
|
||||||
|
page: Number(value?.page || 1),
|
||||||
|
rowsPerPage: 0,
|
||||||
|
sortBy: value?.sortBy || '',
|
||||||
|
descending: value?.descending === true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const bucketOptions = [
|
const bucketOptions = [
|
||||||
{ label: 'Yıldız Ürün', value: 'YILDIZ_URUN' },
|
{ label: 'Yıldız Ürün', value: 'YILDIZ_URUN' },
|
||||||
@@ -2739,8 +2785,7 @@ const productAutoExpandKeys = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const allProductGroupsExpanded = computed(() => {
|
const allProductGroupsExpanded = computed(() => {
|
||||||
const keys = productAutoExpandKeys.value
|
return selectedExpandThroughLevel.value >= autoExpandThroughLevel.value
|
||||||
return keys.length > 0 && keys.every(key => expandedGroups.value[key] === true)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function appendGroupRows (out, sourceRows, level, parentKeys, levels = groupLevels) {
|
function appendGroupRows (out, sourceRows, level, parentKeys, levels = groupLevels) {
|
||||||
@@ -2784,22 +2829,13 @@ function buildGroupedTableRows (tableKey, sourceRows) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function backendRowsForTab (tabKey, fallbackRows) {
|
function backendRowsForTab (tabKey, fallbackRows) {
|
||||||
const rows = backendGroupedRows.value[tabKey]
|
|
||||||
const backendRows = Array.isArray(rows) && rows.length ? rows : []
|
|
||||||
if (hasActiveTableFilters(tabKey)) {
|
|
||||||
return tabKey === 'product_detail'
|
return tabKey === 'product_detail'
|
||||||
? filterRowsBySelectedDetailMainGroup(fallbackRows)
|
? filterRowsBySelectedDetailMainGroup(fallbackRows)
|
||||||
: fallbackRows
|
: fallbackRows
|
||||||
}
|
|
||||||
const sourceRows = tabKey === 'product_detail'
|
|
||||||
? filterRowsBySelectedDetailMainGroup(backendRows.length ? backendRows : fallbackRows)
|
|
||||||
: (backendRows.length ? backendRows : fallbackRows)
|
|
||||||
return sourceRows
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterSourceRowsForTab (tabKey, fallbackRows) {
|
function filterSourceRowsForTab (tabKey, fallbackRows) {
|
||||||
const rows = backendGroupedRows.value[tabKey]
|
return fallbackRows
|
||||||
return Array.isArray(rows) && rows.length ? rows : fallbackRows
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function columnsForTableKey (tabKey) {
|
function columnsForTableKey (tabKey) {
|
||||||
@@ -3387,7 +3423,10 @@ function shouldSkipGroupValue (field, value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isGroupExpanded (key) {
|
function isGroupExpanded (key) {
|
||||||
return expandedGroups.value[key] === true
|
const tabKey = tabKeyFromGroupKey(key)
|
||||||
|
const depth = groupKeyDepth(key)
|
||||||
|
const level = selectedExpandThroughLevelForTab(tabKey)
|
||||||
|
return depth >= 0 && level >= depth
|
||||||
}
|
}
|
||||||
|
|
||||||
function groupKeyDepth (key) {
|
function groupKeyDepth (key) {
|
||||||
@@ -3395,6 +3434,20 @@ function groupKeyDepth (key) {
|
|||||||
return Math.max(-1, parts.length - 2)
|
return Math.max(-1, parts.length - 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tabKeyFromGroupKey (key) {
|
||||||
|
const first = String(key || '').split('|')[0] || ''
|
||||||
|
return first.startsWith('tab:') ? first.slice(4) : activeTab.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedExpandThroughLevelForTab (tabKey) {
|
||||||
|
const levels = tabGroupLevels[tabKey] || groupLevels
|
||||||
|
const selected = new Set(selectedExpandLevelKeysByTab[tabKey] || [])
|
||||||
|
const indexes = levels
|
||||||
|
.map((level, index) => selected.has(level.key) ? index : -1)
|
||||||
|
.filter(index => index >= 0)
|
||||||
|
return indexes.length ? Math.max(...indexes) : -1
|
||||||
|
}
|
||||||
|
|
||||||
function activeTabPrefix () {
|
function activeTabPrefix () {
|
||||||
return `tab:${activeTab.value}|`
|
return `tab:${activeTab.value}|`
|
||||||
}
|
}
|
||||||
@@ -3423,24 +3476,22 @@ function setSelectedExpandLevelsThrough (level) {
|
|||||||
|
|
||||||
function applyActiveExpansionThroughLevel (level) {
|
function applyActiveExpansionThroughLevel (level) {
|
||||||
setSelectedExpandLevelsThrough(level)
|
setSelectedExpandLevelsThrough(level)
|
||||||
replaceActiveTabExpandedGroups(expandedLevelKeysThrough(level))
|
replaceActiveTabExpandedGroups([])
|
||||||
lastManualExpandedGroupKey.value = ''
|
lastManualExpandedGroupKey.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleGroup (key) {
|
function toggleGroup (key) {
|
||||||
const level = groupKeyDepth(key)
|
const level = groupKeyDepth(key)
|
||||||
applyActiveExpansionThroughLevel(isGroupExpanded(key) ? level - 1 : level)
|
applyActiveExpansionThroughLevel(isGroupExpanded(key) ? level - 1 : level)
|
||||||
scheduleLoadBackendGroupedRows()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function collapseAllProductGroups () {
|
async function collapseAllProductGroups () {
|
||||||
if (bulkExpandLoading.value) return
|
if (bulkExpandLoading.value) return
|
||||||
detailLevelMenuOpen.value = false
|
detailLevelMenuOpen.value = false
|
||||||
applyActiveExpansionThroughLevel(-1)
|
applyActiveExpansionThroughLevel(-1)
|
||||||
if (backendGroupedTimer) window.clearTimeout(backendGroupedTimer)
|
|
||||||
bulkExpandLoading.value = true
|
bulkExpandLoading.value = true
|
||||||
try {
|
try {
|
||||||
await loadBackendGroupedRows({ force: true })
|
await nextTick()
|
||||||
} finally {
|
} finally {
|
||||||
bulkExpandLoading.value = false
|
bulkExpandLoading.value = false
|
||||||
}
|
}
|
||||||
@@ -3449,21 +3500,10 @@ async function collapseAllProductGroups () {
|
|||||||
async function expandSelectedProductGroups () {
|
async function expandSelectedProductGroups () {
|
||||||
if (bulkExpandLoading.value) return
|
if (bulkExpandLoading.value) return
|
||||||
detailLevelMenuOpen.value = false
|
detailLevelMenuOpen.value = false
|
||||||
const keys = productAutoExpandKeys.value
|
applyActiveExpansionThroughLevel(autoExpandThroughLevel.value)
|
||||||
const maxKeys = Math.min(maxBulkExpandKeys, maxBackendExpandedKeysForTab(activeTab.value))
|
|
||||||
const expandableKeys = keys.slice(0, maxKeys)
|
|
||||||
replaceActiveTabExpandedGroups(expandableKeys)
|
|
||||||
lastManualExpandedGroupKey.value = ''
|
|
||||||
if (keys.length > expandableKeys.length) {
|
|
||||||
Notify.create({
|
|
||||||
type: 'warning',
|
|
||||||
message: `${formatNumber(expandableKeys.length, 0)} grup açıldı. Daha derin detaylar için satırları kademeli açın.`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (backendGroupedTimer) window.clearTimeout(backendGroupedTimer)
|
|
||||||
bulkExpandLoading.value = true
|
bulkExpandLoading.value = true
|
||||||
try {
|
try {
|
||||||
await loadBackendGroupedRows({ force: true })
|
await nextTick()
|
||||||
} finally {
|
} finally {
|
||||||
bulkExpandLoading.value = false
|
bulkExpandLoading.value = false
|
||||||
}
|
}
|
||||||
@@ -3649,12 +3689,7 @@ function runWithFilterBusy (apply) {
|
|||||||
function syncExpansionAfterFilterChange () {
|
function syncExpansionAfterFilterChange () {
|
||||||
if (!backendGroupedSupportedTab(activeTab.value)) return
|
if (!backendGroupedSupportedTab(activeTab.value)) return
|
||||||
const level = selectedExpandThroughLevel.value
|
const level = selectedExpandThroughLevel.value
|
||||||
if (level < 0) {
|
if (level >= 0) applyActiveExpansionThroughLevel(autoExpandThroughLevel.value)
|
||||||
scheduleLoadBackendGroupedRows()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
applyActiveExpansionThroughLevel(autoExpandThroughLevel.value)
|
|
||||||
scheduleLoadBackendGroupedRows()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterRowsForTable (tableKey, sourceRows, sourceColumns) {
|
function filterRowsForTable (tableKey, sourceRows, sourceColumns) {
|
||||||
@@ -4166,7 +4201,21 @@ function getCachedProductImageUrl (row) {
|
|||||||
if (!canShowProductImage(row)) return ''
|
if (!canShowProductImage(row)) return ''
|
||||||
const key = productImageKey(row)
|
const key = productImageKey(row)
|
||||||
if (imageFailedKeys.value.has(key)) return ''
|
if (imageFailedKeys.value.has(key)) return ''
|
||||||
return imageUrlByKey.value[key] || performanceStore.imageUrl(key) || ''
|
const url = imageUrlByKey.value[key] || performanceStore.imageUrl(key) || ''
|
||||||
|
if (!url) queueVisibleProductImage(row)
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueVisibleProductImage (row) {
|
||||||
|
const key = productImageKey(row)
|
||||||
|
if (!key || imageLoadingKeys.value.has(key) || imageFailedKeys.value.has(key)) return
|
||||||
|
if (Object.prototype.hasOwnProperty.call(imageListByKey.value, key) || performanceStore.hasImageKey(key)) return
|
||||||
|
imageLoadingKeys.value = new Set([...imageLoadingKeys.value, key])
|
||||||
|
void fetchProductImagesForRow(row).finally(() => {
|
||||||
|
const next = new Set(imageLoadingKeys.value)
|
||||||
|
next.delete(key)
|
||||||
|
imageLoadingKeys.value = next
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function markProductImageFailed (row) {
|
function markProductImageFailed (row) {
|
||||||
@@ -4510,30 +4559,112 @@ async function timedProductPerformanceGet (label, url, config = {}) {
|
|||||||
|
|
||||||
async function reload () {
|
async function reload () {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
startLoadingTimer('Rapor verileri hazırlanıyor...')
|
||||||
try {
|
try {
|
||||||
generalRowsLoaded.value = false
|
generalRowsLoaded.value = false
|
||||||
generalRows.value = []
|
generalRows.value = []
|
||||||
|
orderProductCustomersLoaded.value = false
|
||||||
|
orderProductCustomerRows.value = []
|
||||||
|
orderMarketDetailsLoaded.value = false
|
||||||
|
orderMarketDetailRows.value = []
|
||||||
|
orderAnalysisLoaded.value = false
|
||||||
|
orderAnalysisRows.value = []
|
||||||
|
orderMarketRows.value = []
|
||||||
|
orderCustomerRows.value = []
|
||||||
marketRowsLoaded.value = false
|
marketRowsLoaded.value = false
|
||||||
|
marketRows.value = []
|
||||||
countryRowsLoaded.value = false
|
countryRowsLoaded.value = false
|
||||||
|
countryRows.value = []
|
||||||
customerRowsLoaded.value = false
|
customerRowsLoaded.value = false
|
||||||
|
customerRows.value = []
|
||||||
|
for (const key of salesBreakdownTabKeys) salesBreakdownRows[key] = []
|
||||||
|
|
||||||
|
loadingStage.value = 'Ana KPI ve özet yükleniyor...'
|
||||||
const [summaryResp, listResp] = await Promise.all([
|
const [summaryResp, listResp] = await Promise.all([
|
||||||
timedProductPerformanceGet('summary', '/pricing/product-performance/summary', { timeout: 60000 }),
|
timedProductPerformanceGet('summary', '/pricing/product-performance/summary', { timeout: 180000 }),
|
||||||
timedProductPerformanceGet('product-kpi', '/pricing/product-performance', {
|
timedProductPerformanceGet('product-kpi', '/pricing/product-performance', {
|
||||||
params: {
|
params: {
|
||||||
page: 1,
|
page: 1,
|
||||||
limit: productKpiFetchLimit
|
limit: productKpiFetchLimit
|
||||||
},
|
},
|
||||||
timeout: 60000
|
timeout: 180000
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
summary.value = summaryResp?.data || {}
|
summary.value = summaryResp?.data || {}
|
||||||
rows.value = (Array.isArray(listResp?.data?.rows) ? listResp.data.rows : []).map(normalizeRow)
|
rows.value = (Array.isArray(listResp?.data?.rows) ? listResp.data.rows : []).map(normalizeRow)
|
||||||
await ensureActiveTabData(false)
|
loadingStage.value = 'Tüm rapor sekmeleri cacheleniyor...'
|
||||||
await loadBackendGroupedRows({ force: true })
|
await preloadAllReportTabs()
|
||||||
|
loadingStage.value = 'Rapor ekranı hazırlanıyor...'
|
||||||
|
await nextTick()
|
||||||
} 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 {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
stopLoadingTimer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startLoadingTimer (stage = '') {
|
||||||
|
loadingStage.value = stage
|
||||||
|
loadingStartedAt.value = Date.now()
|
||||||
|
loadingNow.value = Date.now()
|
||||||
|
if (loadingTimer) window.clearInterval(loadingTimer)
|
||||||
|
loadingTimer = window.setInterval(() => {
|
||||||
|
loadingNow.value = Date.now()
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopLoadingTimer () {
|
||||||
|
if (loadingTimer) {
|
||||||
|
window.clearInterval(loadingTimer)
|
||||||
|
loadingTimer = null
|
||||||
|
}
|
||||||
|
loadingStartedAt.value = 0
|
||||||
|
loadingNow.value = 0
|
||||||
|
loadingStage.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
async function preloadAllReportTabs () {
|
||||||
|
const tasks = [
|
||||||
|
loadGeneralRows(false),
|
||||||
|
loadOrderProductCustomers(false),
|
||||||
|
loadOrderMarketDetails(false),
|
||||||
|
loadMarketRows(false),
|
||||||
|
loadCountryRows(false),
|
||||||
|
loadCustomerRows(false),
|
||||||
|
loadAllSalesBreakdownRows(false)
|
||||||
|
]
|
||||||
|
const results = await Promise.allSettled(tasks)
|
||||||
|
const failed = results.filter(item => item.status === 'rejected')
|
||||||
|
if (failed.length) {
|
||||||
|
console.warn('[ProductPerformance][preload] partial failure', failed.map(item => item.reason?.message || item.reason))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadAllSalesBreakdownRows (showLoading = true) {
|
||||||
|
if (showLoading) loading.value = true
|
||||||
|
try {
|
||||||
|
await Promise.all(salesBreakdownTabKeys.map(key => loadSalesBreakdownRows(key, false)))
|
||||||
|
} finally {
|
||||||
|
if (showLoading) loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadSalesBreakdownRows (tabKey, showLoading = true) {
|
||||||
|
if (!salesBreakdownTabKeys.includes(tabKey)) return
|
||||||
|
if (Array.isArray(salesBreakdownRows[tabKey]) && salesBreakdownRows[tabKey].length) return
|
||||||
|
if (showLoading) loading.value = true
|
||||||
|
try {
|
||||||
|
const mode = salesBreakdownModes[tabKey] || tabKey
|
||||||
|
const resp = await timedProductPerformanceGet(`sales:${tabKey}`, '/pricing/product-performance/sales-breakdown', {
|
||||||
|
params: { mode, limit: reportFetchLimit },
|
||||||
|
timeout: 180000
|
||||||
|
})
|
||||||
|
salesBreakdownRows[tabKey] = (Array.isArray(resp?.data) ? resp.data : []).map(normalizeSalesBreakdownRow)
|
||||||
|
} catch (err) {
|
||||||
|
Notify.create({ type: 'negative', message: err?.response?.data || err?.message || 'Satış kırılım verisi alınamadı' })
|
||||||
|
} finally {
|
||||||
|
if (showLoading) loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4541,7 +4672,7 @@ async function loadGeneralRows (showLoading = true) {
|
|||||||
if (generalRowsLoaded.value) return
|
if (generalRowsLoaded.value) return
|
||||||
if (showLoading) loading.value = true
|
if (showLoading) loading.value = true
|
||||||
try {
|
try {
|
||||||
const resp = await timedProductPerformanceGet('general', '/pricing/product-performance/general', { params: { limit: reportFetchLimit }, timeout: 90000 })
|
const resp = await timedProductPerformanceGet('general', '/pricing/product-performance/general', { params: { limit: reportFetchLimit }, timeout: 180000 })
|
||||||
const normalizedGeneralRows = (Array.isArray(resp?.data) ? resp.data : []).map(normalizeGeneralRow)
|
const normalizedGeneralRows = (Array.isArray(resp?.data) ? resp.data : []).map(normalizeGeneralRow)
|
||||||
generalRows.value = normalizedGeneralRows
|
generalRows.value = normalizedGeneralRows
|
||||||
generalRowsLoaded.value = true
|
generalRowsLoaded.value = true
|
||||||
@@ -4559,9 +4690,10 @@ async function loadGeneralRows (showLoading = true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadOrderProductCustomers (showLoading = true) {
|
async function loadOrderProductCustomers (showLoading = true) {
|
||||||
|
if (orderProductCustomersLoaded.value) return
|
||||||
if (showLoading) loading.value = true
|
if (showLoading) loading.value = true
|
||||||
try {
|
try {
|
||||||
const resp = await timedProductPerformanceGet('orders:product-customers', '/pricing/product-performance/orders/product-customers', { params: { limit: reportFetchLimit }, timeout: 90000 })
|
const resp = await timedProductPerformanceGet('orders:product-customers', '/pricing/product-performance/orders/product-customers', { params: { limit: reportFetchLimit }, timeout: 180000 })
|
||||||
orderProductCustomerRows.value = (Array.isArray(resp?.data) ? resp.data : []).map(normalizeOrderProductCustomerRow)
|
orderProductCustomerRows.value = (Array.isArray(resp?.data) ? resp.data : []).map(normalizeOrderProductCustomerRow)
|
||||||
orderProductCustomersLoaded.value = true
|
orderProductCustomersLoaded.value = true
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -4572,9 +4704,10 @@ async function loadOrderProductCustomers (showLoading = true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadOrderMarketDetails (showLoading = true) {
|
async function loadOrderMarketDetails (showLoading = true) {
|
||||||
|
if (orderMarketDetailsLoaded.value) return
|
||||||
if (showLoading) loading.value = true
|
if (showLoading) loading.value = true
|
||||||
try {
|
try {
|
||||||
const resp = await timedProductPerformanceGet('orders:market-details', '/pricing/product-performance/orders/market-details', { params: { limit: reportFetchLimit }, timeout: 90000 })
|
const resp = await timedProductPerformanceGet('orders:market-details', '/pricing/product-performance/orders/market-details', { params: { limit: reportFetchLimit }, timeout: 180000 })
|
||||||
orderMarketDetailRows.value = (Array.isArray(resp?.data) ? resp.data : []).map(normalizeOrderMarketDetailRow)
|
orderMarketDetailRows.value = (Array.isArray(resp?.data) ? resp.data : []).map(normalizeOrderMarketDetailRow)
|
||||||
orderMarketDetailsLoaded.value = true
|
orderMarketDetailsLoaded.value = true
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -4588,7 +4721,7 @@ async function loadMarketRows (showLoading = true) {
|
|||||||
if (marketRowsLoaded.value) return
|
if (marketRowsLoaded.value) return
|
||||||
if (showLoading) loading.value = true
|
if (showLoading) loading.value = true
|
||||||
try {
|
try {
|
||||||
const resp = await timedProductPerformanceGet('markets', '/pricing/product-performance/markets', { params: { limit: reportFetchLimit }, timeout: 60000 })
|
const resp = await timedProductPerformanceGet('markets', '/pricing/product-performance/markets', { params: { limit: reportFetchLimit }, timeout: 180000 })
|
||||||
marketRows.value = Array.isArray(resp?.data) ? resp.data : []
|
marketRows.value = Array.isArray(resp?.data) ? resp.data : []
|
||||||
marketRowsLoaded.value = true
|
marketRowsLoaded.value = true
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -4602,7 +4735,7 @@ async function loadCountryRows (showLoading = true) {
|
|||||||
if (countryRowsLoaded.value) return
|
if (countryRowsLoaded.value) return
|
||||||
if (showLoading) loading.value = true
|
if (showLoading) loading.value = true
|
||||||
try {
|
try {
|
||||||
const resp = await timedProductPerformanceGet('countries', '/pricing/product-performance/countries', { params: { limit: reportFetchLimit }, timeout: 60000 })
|
const resp = await timedProductPerformanceGet('countries', '/pricing/product-performance/countries', { params: { limit: reportFetchLimit }, timeout: 180000 })
|
||||||
countryRows.value = (Array.isArray(resp?.data) ? resp.data : []).map(row => ({
|
countryRows.value = (Array.isArray(resp?.data) ? resp.data : []).map(row => ({
|
||||||
...row,
|
...row,
|
||||||
country_key: `${row.country || '-'}|${row.customer_segment || '-'}|${row.market_key || '-'}`
|
country_key: `${row.country || '-'}|${row.customer_segment || '-'}|${row.market_key || '-'}`
|
||||||
@@ -4621,7 +4754,7 @@ async function loadCustomerRows (showLoading = true) {
|
|||||||
try {
|
try {
|
||||||
const resp = await timedProductPerformanceGet('customers', '/pricing/product-performance/customers', {
|
const resp = await timedProductPerformanceGet('customers', '/pricing/product-performance/customers', {
|
||||||
params: { limit: reportFetchLimit, breakdown: customerBreakdown.value },
|
params: { limit: reportFetchLimit, breakdown: customerBreakdown.value },
|
||||||
timeout: 60000
|
timeout: 180000
|
||||||
})
|
})
|
||||||
customerRows.value = (Array.isArray(resp?.data) ? resp.data : []).map(row => ({
|
customerRows.value = (Array.isArray(resp?.data) ? resp.data : []).map(row => ({
|
||||||
...row,
|
...row,
|
||||||
@@ -4641,6 +4774,9 @@ async function ensureActiveTabData (showLoading = true) {
|
|||||||
if (tab === 'markets') return loadMarketRows(showLoading)
|
if (tab === 'markets') return loadMarketRows(showLoading)
|
||||||
if (tab === 'countries') return loadCountryRows(showLoading)
|
if (tab === 'countries') return loadCountryRows(showLoading)
|
||||||
if (tab === 'customers') return loadCustomerRows(showLoading)
|
if (tab === 'customers') return loadCustomerRows(showLoading)
|
||||||
|
if (tab === 'order_product_customers') return loadOrderProductCustomers(showLoading)
|
||||||
|
if (tab === 'order_market_details') return loadOrderMarketDetails(showLoading)
|
||||||
|
if (salesBreakdownTabKeys.includes(tab)) return loadSalesBreakdownRows(tab, showLoading)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadOrderAnalysis (showLoading = true) {
|
async function loadOrderAnalysis (showLoading = true) {
|
||||||
@@ -4680,7 +4816,6 @@ function bucketColor (value) {
|
|||||||
|
|
||||||
watch(activeTab, () => {
|
watch(activeTab, () => {
|
||||||
void ensureActiveTabData(false)
|
void ensureActiveTabData(false)
|
||||||
scheduleLoadBackendGroupedRows()
|
|
||||||
setupTopScrollbarSync()
|
setupTopScrollbarSync()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -4704,14 +4839,9 @@ watch(detailMainGroupOptions, () => {
|
|||||||
|
|
||||||
watch(selectedDetailMainGroup, () => {
|
watch(selectedDetailMainGroup, () => {
|
||||||
if (activeTab.value === 'product_detail') {
|
if (activeTab.value === 'product_detail') {
|
||||||
backendGroupedRows.value = {
|
|
||||||
...backendGroupedRows.value,
|
|
||||||
product_detail: []
|
|
||||||
}
|
|
||||||
void nextTick(() => {
|
void nextTick(() => {
|
||||||
const level = selectedExpandThroughLevel.value
|
const level = selectedExpandThroughLevel.value
|
||||||
applyActiveExpansionThroughLevel(level >= 0 ? autoExpandThroughLevel.value : -1)
|
applyActiveExpansionThroughLevel(level >= 0 ? autoExpandThroughLevel.value : -1)
|
||||||
scheduleLoadBackendGroupedRows()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -4733,6 +4863,7 @@ onMounted(() => {
|
|||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
cleanupTopScrollbarSync()
|
cleanupTopScrollbarSync()
|
||||||
if (backendGroupedTimer) window.clearTimeout(backendGroupedTimer)
|
if (backendGroupedTimer) window.clearTimeout(backendGroupedTimer)
|
||||||
|
stopLoadingTimer()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -4762,6 +4893,12 @@ onBeforeUnmount(() => {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-busy-time {
|
||||||
|
color: #5f6b7a;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
.performance-top-bar {
|
.performance-top-bar {
|
||||||
min-height: 34px;
|
min-height: 34px;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user