Add hourly detail charts for single-day ranges
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 46s

This commit is contained in:
Cauê Faleiros
2026-06-30 14:51:44 -03:00
parent 6a414a5bf4
commit ae7736019f
4 changed files with 178 additions and 20 deletions

View File

@@ -414,6 +414,8 @@ const getDateOnly = (value) => {
return match ? match[1] : null;
};
const isSingleDayRange = (start, end) => Boolean(start && end && start === end);
const WEEKDAY_LABELS = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'];
const getWeekdayIndex = (value) => {
@@ -751,6 +753,7 @@ const getProductDetailsAnalytics = async (productId, range = {}) => {
const normalizedStart = normalizeDateParam(range.start);
const normalizedEnd = normalizeDateParam(range.end);
const useHourlyChart = isSingleDayRange(normalizedStart, normalizedEnd);
const periodParams = [normalizedProductId];
const periodFilters = [
'produto_id = $1',
@@ -798,7 +801,16 @@ const getProductDetailsAnalytics = async (productId, range = {}) => {
LEFT JOIN order_info ON order_info.id = selected_product.id
WHERE stock_info.id IS NOT NULL OR order_info.id IS NOT NULL;
`, [normalizedProductId]),
pool.query(`
pool.query(useHourlyChart ? `
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
FROM orders
WHERE ${periodFilters.join(' AND ')}
GROUP BY hour
ORDER BY hour ASC;
` : `
SELECT
data_pedido_date,
MAX(data_pedido) as date_label,
@@ -814,10 +826,18 @@ const getProductDetailsAnalytics = async (productId, range = {}) => {
const summary = summaryResult.rows[0];
if (!summary) return null;
const chartData = periodResult.rows.map(row => ({
date: row.date_label || getDateOnly(row.data_pedido_date) || '',
value: toNumber(row.quantity_sold)
}));
const chartData = useHourlyChart
? Array.from({ length: 24 }, (_, hour) => {
const row = periodResult.rows.find(item => toNumber(item.hour) === hour);
return {
date: `${String(hour).padStart(2, '0')}h`,
value: row ? toNumber(row.quantity_sold) : 0
};
})
: periodResult.rows.map(row => ({
date: row.date_label || getDateOnly(row.data_pedido_date) || '',
value: toNumber(row.quantity_sold)
}));
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);
@@ -934,6 +954,7 @@ const getClientDetailsAnalytics = async (clientToken, range = {}) => {
const normalizedStart = normalizeDateParam(range.start);
const normalizedEnd = normalizeDateParam(range.end);
const useHourlyChart = isSingleDayRange(normalizedStart, normalizedEnd);
const periodParams = [resolvedCustomerKey];
const periodFilters = [
`${CUSTOMER_KEY_SQL} = $1`,
@@ -1006,6 +1027,10 @@ const getClientDetailsAnalytics = async (clientToken, range = {}) => {
const groupedOrdersByKey = new Map();
const spentByDate = new Map();
const spentByHour = Array.from({ length: 24 }, (_, hour) => ({
date: `${String(hour).padStart(2, '0')}h`,
value: 0
}));
const weekdayCounts = WEEKDAY_LABELS.map(label => ({ label, value: 0 }));
const hourCounts = Array.from({ length: 24 }, (_, hour) => ({
label: `${String(hour).padStart(2, '0')}h`,
@@ -1047,6 +1072,13 @@ const getClientDetailsAnalytics = async (clientToken, range = {}) => {
spentByDate.set(dateLabel, currentDateSpend);
}
if (useHourlyChart) {
const orderHour = getHourFromTimestamp(row.created_at) ?? getHourFromTimestamp(row.data_pedido);
if (orderHour !== null) {
spentByHour[orderHour].value += itemRevenue;
}
}
if (!groupedOrdersByKey.has(groupKey)) {
groupedOrdersByKey.set(groupKey, {
date: dateLabel,
@@ -1082,9 +1114,11 @@ const getClientDetailsAnalytics = async (clientToken, range = {}) => {
const groupedOrders = [...groupedOrdersByKey.values()]
.sort((a, b) => String(b.sortDate).localeCompare(String(a.sortDate)))
.map(({ sortDate, ...group }) => group);
const chartData = [...spentByDate.values()]
.sort((a, b) => String(a.sortDate).localeCompare(String(b.sortDate)))
.map(({ sortDate, ...entry }) => entry);
const chartData = useHourlyChart
? spentByHour
: [...spentByDate.values()]
.sort((a, b) => String(a.sortDate).localeCompare(String(b.sortDate)))
.map(({ sortDate, ...entry }) => entry);
const periodOrderCount = groupedOrders.length;
return {