Improve product analytics UI
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m48s

This commit is contained in:
Cauê Faleiros
2026-07-01 10:57:33 -03:00
parent a26bc8813e
commit f5e2de9a35
6 changed files with 417 additions and 76 deletions

View File

@@ -759,18 +759,24 @@ const getProductDetailsAnalytics = async (productId, range = {}) => {
'produto_id = $1',
'data_pedido_date IS NOT NULL'
];
const variantParams = [normalizedProductId];
const variantFilters = ['data_pedido_date IS NOT NULL'];
if (normalizedStart) {
periodParams.push(normalizedStart);
periodFilters.push(`data_pedido_date >= $${periodParams.length}::date`);
variantParams.push(normalizedStart);
variantFilters.push(`data_pedido_date >= $${variantParams.length}::date`);
}
if (normalizedEnd) {
periodParams.push(normalizedEnd);
periodFilters.push(`data_pedido_date <= $${periodParams.length}::date`);
variantParams.push(normalizedEnd);
variantFilters.push(`data_pedido_date <= $${variantParams.length}::date`);
}
const [summaryResult, periodResult] = await Promise.all([
const [summaryResult, periodResult, variantResult] = await Promise.all([
pool.query(`
WITH selected_product AS (
SELECT $1::text as id
@@ -778,7 +784,8 @@ const getProductDetailsAnalytics = async (productId, range = {}) => {
stock_info AS (
SELECT
produto_id as id,
MAX(NULLIF(nome, '')) as name
MAX(NULLIF(nome, '')) as name,
COALESCE(MAX(saldo), 0) as stock
FROM stock
WHERE produto_id = $1
GROUP BY produto_id
@@ -795,7 +802,8 @@ const getProductDetailsAnalytics = async (productId, range = {}) => {
SELECT
selected_product.id,
COALESCE(stock_info.name, order_info.name, 'Unknown') as name,
COALESCE(order_info.price, 0) as price
COALESCE(order_info.price, 0) as price,
COALESCE(stock_info.stock, 0) as stock
FROM selected_product
LEFT JOIN stock_info ON stock_info.id = selected_product.id
LEFT JOIN order_info ON order_info.id = selected_product.id
@@ -805,7 +813,8 @@ const getProductDetailsAnalytics = async (productId, range = {}) => {
SELECT
EXTRACT(HOUR FROM created_at AT TIME ZONE 'America/Sao_Paulo')::int as hour,
COALESCE(SUM(quantidade), 0) as quantity_sold,
COALESCE(SUM(quantidade * valor_unitario), 0) as revenue
COALESCE(SUM(quantidade * valor_unitario), 0) as revenue,
COUNT(DISTINCT COALESCE(NULLIF(pedido_id, ''), data_pedido || '_' || valor_pedido::text))::int as order_count
FROM orders
WHERE ${periodFilters.join(' AND ')}
GROUP BY hour
@@ -815,12 +824,35 @@ const getProductDetailsAnalytics = async (productId, range = {}) => {
data_pedido_date,
MAX(data_pedido) as date_label,
COALESCE(SUM(quantidade), 0) as quantity_sold,
COALESCE(SUM(quantidade * valor_unitario), 0) as revenue
COALESCE(SUM(quantidade * valor_unitario), 0) as revenue,
COUNT(DISTINCT COALESCE(NULLIF(pedido_id, ''), data_pedido || '_' || valor_pedido::text))::int as order_count
FROM orders
WHERE ${periodFilters.join(' AND ')}
GROUP BY data_pedido_date
ORDER BY data_pedido_date ASC;
`, periodParams)
`, periodParams),
pool.query(`
WITH selected_base AS (
SELECT COALESCE(${PRODUCT_NAME_SQL}, 'Unknown') as base_name
FROM orders
WHERE produto_id = $1
AND data_pedido_date IS NOT NULL
ORDER BY data_pedido_date DESC NULLS LAST, data_pedido DESC NULLS LAST
LIMIT 1
)
SELECT
produto_id as id,
MAX(COALESCE(NULLIF(produto_descricao, ''), 'Unknown')) as name,
COALESCE(SUM(quantidade), 0) as quantity_sold,
COALESCE(SUM(quantidade * valor_unitario), 0) as revenue,
COUNT(DISTINCT COALESCE(NULLIF(pedido_id, ''), data_pedido || '_' || valor_pedido::text))::int as order_count
FROM orders
WHERE ${variantFilters.join(' AND ')}
AND COALESCE(${PRODUCT_NAME_SQL}, 'Unknown') = (SELECT base_name FROM selected_base)
GROUP BY produto_id
ORDER BY quantity_sold DESC, revenue DESC, name ASC
LIMIT 8;
`, variantParams)
]);
const summary = summaryResult.rows[0];
@@ -829,17 +861,43 @@ const getProductDetailsAnalytics = async (productId, range = {}) => {
const chartData = useHourlyChart
? Array.from({ length: 24 }, (_, hour) => {
const row = periodResult.rows.find(item => toNumber(item.hour) === hour);
const quantitySold = row ? toNumber(row.quantity_sold) : 0;
const revenue = row ? toNumber(row.revenue) : 0;
const orderCount = row ? toNumber(row.order_count) : 0;
return {
date: `${String(hour).padStart(2, '0')}h`,
value: row ? toNumber(row.quantity_sold) : 0
value: quantitySold,
quantitySold,
revenue,
orderCount,
averageTicket: orderCount ? revenue / orderCount : 0
};
})
: periodResult.rows.map(row => ({
date: row.date_label || getDateOnly(row.data_pedido_date) || '',
value: toNumber(row.quantity_sold)
}));
: periodResult.rows.map(row => {
const quantitySold = toNumber(row.quantity_sold);
const revenue = toNumber(row.revenue);
const orderCount = toNumber(row.order_count);
return {
date: row.date_label || getDateOnly(row.data_pedido_date) || '',
value: quantitySold,
quantitySold,
revenue,
orderCount,
averageTicket: orderCount ? revenue / orderCount : 0
};
});
const totalSold = periodResult.rows.reduce((sum, row) => sum + toNumber(row.quantity_sold), 0);
const totalRevenue = periodResult.rows.reduce((sum, row) => sum + toNumber(row.revenue), 0);
const totalOrders = periodResult.rows.reduce((sum, row) => sum + toNumber(row.order_count), 0);
const variantBreakdown = variantResult.rows.map(row => ({
id: row.id,
name: row.name,
quantitySold: toNumber(row.quantity_sold),
revenue: toNumber(row.revenue),
orderCount: toNumber(row.order_count)
}));
return {
range: {
@@ -849,11 +907,15 @@ const getProductDetailsAnalytics = async (productId, range = {}) => {
productInfo: {
id: summary.id,
name: summary.name,
price: toNumber(summary.price)
price: toNumber(summary.price),
stock: toNumber(summary.stock)
},
chartData,
totalSold,
totalRevenue
totalRevenue,
totalOrders,
averageTicket: totalOrders ? totalRevenue / totalOrders : 0,
variantBreakdown
};
};