product performance grouped kpi improvements
This commit is contained in:
@@ -176,6 +176,7 @@
|
||||
v-if="getCachedProductImageUrl(props.row)"
|
||||
:src="getCachedProductImageUrl(props.row)"
|
||||
class="product-thumb"
|
||||
@error="markProductImageFailed(props.row)"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
alt=""
|
||||
@@ -286,6 +287,7 @@
|
||||
v-if="getCachedProductImageUrl(props.row)"
|
||||
:src="getCachedProductImageUrl(props.row)"
|
||||
class="product-thumb"
|
||||
@error="markProductImageFailed(props.row)"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
alt=""
|
||||
@@ -327,6 +329,7 @@
|
||||
v-if="getCachedProductImageUrl(props.row)"
|
||||
:src="getCachedProductImageUrl(props.row)"
|
||||
class="product-thumb"
|
||||
@error="markProductImageFailed(props.row)"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
alt=""
|
||||
@@ -363,6 +366,7 @@
|
||||
v-if="getCachedProductImageUrl(props.row)"
|
||||
:src="getCachedProductImageUrl(props.row)"
|
||||
class="product-thumb"
|
||||
@error="markProductImageFailed(props.row)"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
alt=""
|
||||
@@ -616,6 +620,7 @@
|
||||
v-if="getCachedProductImageUrl(props.row)"
|
||||
:src="getCachedProductImageUrl(props.row)"
|
||||
class="product-thumb"
|
||||
@error="markProductImageFailed(props.row)"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
alt=""
|
||||
@@ -652,6 +657,7 @@
|
||||
v-if="getCachedProductImageUrl(props.row)"
|
||||
:src="getCachedProductImageUrl(props.row)"
|
||||
class="product-thumb"
|
||||
@error="markProductImageFailed(props.row)"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
alt=""
|
||||
@@ -761,6 +767,7 @@
|
||||
v-if="getCachedProductImageUrl(props.row)"
|
||||
:src="getCachedProductImageUrl(props.row)"
|
||||
class="product-thumb"
|
||||
@error="markProductImageFailed(props.row)"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
alt=""
|
||||
@@ -798,6 +805,7 @@
|
||||
v-if="getCachedProductImageUrl(props.row)"
|
||||
:src="getCachedProductImageUrl(props.row)"
|
||||
class="product-thumb"
|
||||
@error="markProductImageFailed(props.row)"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
alt=""
|
||||
@@ -834,6 +842,7 @@
|
||||
v-if="getCachedProductImageUrl(props.row)"
|
||||
:src="getCachedProductImageUrl(props.row)"
|
||||
class="product-thumb"
|
||||
@error="markProductImageFailed(props.row)"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
alt=""
|
||||
@@ -2477,6 +2486,14 @@ function aggregateGroupFields (sourceRows, groupField = '') {
|
||||
|
||||
for (const field of fields) {
|
||||
if (field === 'row_key' || field === 'key') continue
|
||||
if (field === 'stock_qty') {
|
||||
out[field] = distinctVariantStockQty(sourceRows)
|
||||
continue
|
||||
}
|
||||
if (field === 'idle_cost_usd') {
|
||||
out[field] = distinctVariantStockCost(sourceRows)
|
||||
continue
|
||||
}
|
||||
if (shouldSumField(field)) {
|
||||
out[field] = sumRows(sourceRows, field)
|
||||
} else if (shouldAverageField(field)) {
|
||||
@@ -2532,6 +2549,9 @@ function applyDerivedGroupMetrics (out, sourceRows, groupField = '') {
|
||||
|
||||
const orderQty = Number(out.order_qty || 0)
|
||||
if (orderQty > 0) out.avg_order_price_usd = Number(out.order_usd || 0) / orderQty
|
||||
if (out.net_stock_after_order !== undefined || orderQty > 0) {
|
||||
out.net_stock_after_order = Number(out.stock_qty || 0) - orderQty
|
||||
}
|
||||
|
||||
if (sourceRows.some(row => row.performance_bucket)) {
|
||||
out.performance_bucket = dominantValue(sourceRows, 'performance_bucket')
|
||||
@@ -2618,6 +2638,47 @@ function sumRows (sourceRows, field) {
|
||||
return sourceRows.reduce((sum, row) => sum + Number(row[field] || 0), 0)
|
||||
}
|
||||
|
||||
function productVariantStockKey (row) {
|
||||
const productCode = String(row?.product_code || row?.image_product_code || '').trim()
|
||||
const colorCode = String(row?.color_code || row?.image_color_code || '').trim()
|
||||
const yakaKodu = String(row?.yaka_kodu || row?.image_yaka_kodu || '').trim()
|
||||
if (!productCode && !colorCode && !yakaKodu) return ''
|
||||
return `${productCode}|${colorCode}|${yakaKodu}`
|
||||
}
|
||||
|
||||
function distinctVariantStockQty (sourceRows) {
|
||||
const seen = new Set()
|
||||
let total = 0
|
||||
let keyed = false
|
||||
for (const row of sourceRows) {
|
||||
const key = productVariantStockKey(row)
|
||||
const stockQty = Number(row?.stock_qty || 0)
|
||||
if (!key) continue
|
||||
keyed = true
|
||||
if (seen.has(key)) continue
|
||||
seen.add(key)
|
||||
total += stockQty
|
||||
}
|
||||
return keyed ? total : sumRows(sourceRows, 'stock_qty')
|
||||
}
|
||||
|
||||
function distinctVariantStockCost (sourceRows) {
|
||||
const seen = new Set()
|
||||
let total = 0
|
||||
let keyed = false
|
||||
for (const row of sourceRows) {
|
||||
const key = productVariantStockKey(row)
|
||||
if (!key) continue
|
||||
keyed = true
|
||||
if (seen.has(key)) continue
|
||||
seen.add(key)
|
||||
const stockQty = Number(row?.stock_qty || 0)
|
||||
const costPrice = Number(row?.cost_price_usd || 0)
|
||||
total += stockQty * costPrice
|
||||
}
|
||||
return keyed ? total : sumRows(sourceRows, 'idle_cost_usd')
|
||||
}
|
||||
|
||||
function distinctCount (sourceRows, field) {
|
||||
const values = new Set()
|
||||
for (const row of sourceRows) {
|
||||
@@ -3234,6 +3295,14 @@ function getCachedProductImageUrl (row) {
|
||||
return imageUrlByKey.value[key] || performanceStore.imageUrl(key) || ''
|
||||
}
|
||||
|
||||
function markProductImageFailed (row) {
|
||||
const key = productImageKey(row)
|
||||
if (!key) return
|
||||
performanceStore.setImageCache(key, [])
|
||||
imageListByKey.value = { ...imageListByKey.value, [key]: [] }
|
||||
imageUrlByKey.value = { ...imageUrlByKey.value, [key]: '' }
|
||||
}
|
||||
|
||||
async function primeProductImages (sourceRows) {
|
||||
imagePrimeActiveRequests += 1
|
||||
imagePrimeLoading.value = true
|
||||
@@ -4166,48 +4235,56 @@ onMounted(reload)
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.group-row td {
|
||||
background: color-mix(in srgb, var(--q-primary) 8%, #ffffff);
|
||||
.performance-table :deep(.q-table tbody tr.group-row td) {
|
||||
background: #edf3fb;
|
||||
font-weight: 600;
|
||||
color: color-mix(in srgb, var(--q-primary) 55%, #1f2937);
|
||||
border-color: color-mix(in srgb, var(--q-primary) 18%, #d5dce5);
|
||||
color: #1f2937;
|
||||
border-color: #c4d2e2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.group-row-level-0 td {
|
||||
background: color-mix(in srgb, var(--q-primary) 82%, #1f2937);
|
||||
.performance-table :deep(.q-table tbody tr.group-row.group-row-level-0 td) {
|
||||
background: #2f5f8f;
|
||||
color: #fff;
|
||||
border-color: color-mix(in srgb, var(--q-primary) 75%, #111827);
|
||||
border-color: #244b72;
|
||||
}
|
||||
|
||||
.group-row-level-1 td {
|
||||
background: color-mix(in srgb, var(--q-primary) 68%, #ffffff);
|
||||
.performance-table :deep(.q-table tbody tr.group-row.group-row-level-1 td) {
|
||||
background: #4f7fad;
|
||||
color: #fff;
|
||||
border-color: color-mix(in srgb, var(--q-primary) 58%, #d5dce5);
|
||||
border-color: #426d96;
|
||||
}
|
||||
|
||||
.group-row-level-2 td {
|
||||
background: color-mix(in srgb, var(--q-primary) 36%, #ffffff);
|
||||
color: color-mix(in srgb, var(--q-primary) 65%, #111827);
|
||||
border-color: color-mix(in srgb, var(--q-primary) 34%, #d5dce5);
|
||||
.performance-table :deep(.q-table tbody tr.group-row.group-row-level-2 td) {
|
||||
background: #d8e6f4;
|
||||
color: #1f2937;
|
||||
border-color: #b8cce0;
|
||||
}
|
||||
|
||||
.group-row-level-3 td {
|
||||
background: color-mix(in srgb, var(--q-primary) 22%, #ffffff);
|
||||
color: color-mix(in srgb, var(--q-primary) 58%, #1f2937);
|
||||
border-color: color-mix(in srgb, var(--q-primary) 24%, #d5dce5);
|
||||
.performance-table :deep(.q-table tbody tr.group-row.group-row-level-3 td) {
|
||||
background: #e6eef7;
|
||||
color: #1f2937;
|
||||
border-color: #c4d2e2;
|
||||
}
|
||||
|
||||
.group-row-level-4 td {
|
||||
background: color-mix(in srgb, var(--q-primary) 14%, #ffffff);
|
||||
color: color-mix(in srgb, var(--q-primary) 52%, #1f2937);
|
||||
border-color: color-mix(in srgb, var(--q-primary) 18%, #d5dce5);
|
||||
.performance-table :deep(.q-table tbody tr.group-row.group-row-level-4 td) {
|
||||
background: #eef4fa;
|
||||
color: #1f2937;
|
||||
border-color: #cbd8e6;
|
||||
}
|
||||
|
||||
.group-row-level-5 td {
|
||||
background: color-mix(in srgb, var(--q-primary) 8%, #ffffff);
|
||||
color: color-mix(in srgb, var(--q-primary) 48%, #1f2937);
|
||||
border-color: color-mix(in srgb, var(--q-primary) 14%, #d5dce5);
|
||||
.performance-table :deep(.q-table tbody tr.group-row.group-row-level-5 td) {
|
||||
background: #f4f8fc;
|
||||
color: #1f2937;
|
||||
border-color: #d5dce5;
|
||||
}
|
||||
|
||||
.performance-table :deep(.q-table tbody tr.group-row td span),
|
||||
.performance-table :deep(.q-table tbody tr.group-row .group-title),
|
||||
.performance-table :deep(.q-table tbody tr.group-row .q-btn),
|
||||
.performance-table :deep(.q-table tbody tr.group-row .q-icon) {
|
||||
color: inherit;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.group-cell-label {
|
||||
|
||||
Reference in New Issue
Block a user