extract frontend analytics helpers
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m12s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m12s
This commit is contained in:
122
src/analytics/clients.ts
Normal file
122
src/analytics/clients.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import type { DateRange, OrderData } from '../types';
|
||||
import { parseOrderDate } from '../dataService';
|
||||
import { filterOrdersByDateRange, getClientDisplayName, getOrderItemRevenue } from './orders';
|
||||
|
||||
export type ClientSortOption = 'recent' | 'spent_desc' | 'spent_asc' | 'items_desc' | 'items_asc';
|
||||
|
||||
export interface ClientSummary {
|
||||
name: string;
|
||||
phone: string;
|
||||
totalSpent: number;
|
||||
totalItems: number;
|
||||
orderCount: number;
|
||||
lastPurchase: number;
|
||||
}
|
||||
|
||||
export interface GroupedClientOrder {
|
||||
date: string;
|
||||
orderId: string;
|
||||
orderTotal: number;
|
||||
items: OrderData[];
|
||||
}
|
||||
|
||||
export interface ClientDetailsMetrics {
|
||||
groupedOrders: GroupedClientOrder[];
|
||||
totalSpent: number;
|
||||
totalItems: number;
|
||||
clientPhone: string;
|
||||
}
|
||||
|
||||
export const buildClientsSummary = (
|
||||
ordersData: OrderData[],
|
||||
dateRange: DateRange,
|
||||
searchTerm: string,
|
||||
sortBy: ClientSortOption
|
||||
): ClientSummary[] => {
|
||||
const orders = filterOrdersByDateRange(ordersData, dateRange);
|
||||
const clientMap: Record<string, {
|
||||
totalSpent: number;
|
||||
totalItems: number;
|
||||
uniqueOrders: Set<string>;
|
||||
lastPurchase: number;
|
||||
phone: string;
|
||||
}> = {};
|
||||
|
||||
orders.forEach(order => {
|
||||
const clientName = getClientDisplayName(order);
|
||||
|
||||
if (!clientMap[clientName]) {
|
||||
clientMap[clientName] = { totalSpent: 0, totalItems: 0, uniqueOrders: new Set(), lastPurchase: 0, phone: '' };
|
||||
}
|
||||
|
||||
if (order.Fone_Cliente) {
|
||||
clientMap[clientName].phone = order.Fone_Cliente;
|
||||
}
|
||||
|
||||
clientMap[clientName].totalSpent += getOrderItemRevenue(order);
|
||||
clientMap[clientName].totalItems += order.Quantidade;
|
||||
clientMap[clientName].uniqueOrders.add(`${order.Data_Pedido}_${order.Valor_Pedido}`);
|
||||
|
||||
const orderTime = parseOrderDate(order.Data_Pedido).getTime();
|
||||
if (orderTime > clientMap[clientName].lastPurchase) {
|
||||
clientMap[clientName].lastPurchase = orderTime;
|
||||
}
|
||||
});
|
||||
|
||||
const normalizedSearch = searchTerm.trim().toLowerCase();
|
||||
const clients = Object.keys(clientMap).map(name => ({
|
||||
name,
|
||||
phone: clientMap[name].phone,
|
||||
totalSpent: clientMap[name].totalSpent,
|
||||
totalItems: clientMap[name].totalItems,
|
||||
orderCount: clientMap[name].uniqueOrders.size,
|
||||
lastPurchase: clientMap[name].lastPurchase
|
||||
}));
|
||||
|
||||
const filteredClients = normalizedSearch
|
||||
? clients.filter(client => client.name.toLowerCase().includes(normalizedSearch))
|
||||
: clients;
|
||||
|
||||
return filteredClients.sort((a, b) => {
|
||||
switch (sortBy) {
|
||||
case 'recent': return b.lastPurchase - a.lastPurchase;
|
||||
case 'spent_desc': return b.totalSpent - a.totalSpent;
|
||||
case 'spent_asc': return a.totalSpent - b.totalSpent;
|
||||
case 'items_desc': return b.totalItems - a.totalItems;
|
||||
case 'items_asc': return a.totalItems - b.totalItems;
|
||||
default: return 0;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const buildClientDetailsMetrics = (ordersData: OrderData[], clientName: string): ClientDetailsMetrics => {
|
||||
const clientOrders = ordersData.filter(order => getClientDisplayName(order) === clientName);
|
||||
const groupedOrdersMap: Record<string, GroupedClientOrder> = {};
|
||||
let clientPhone = '';
|
||||
let totalSpent = 0;
|
||||
let totalItems = 0;
|
||||
|
||||
clientOrders.forEach(order => {
|
||||
if (order.Fone_Cliente && !clientPhone) clientPhone = order.Fone_Cliente;
|
||||
|
||||
totalSpent += getOrderItemRevenue(order);
|
||||
totalItems += order.Quantidade;
|
||||
|
||||
const key = order.ID_Pedido || `${order.Data_Pedido}_${order.Valor_Pedido}`;
|
||||
if (!groupedOrdersMap[key]) {
|
||||
groupedOrdersMap[key] = {
|
||||
date: order.Data_Pedido,
|
||||
orderId: order.ID_Pedido || key,
|
||||
orderTotal: order.Valor_Pedido,
|
||||
items: []
|
||||
};
|
||||
}
|
||||
groupedOrdersMap[key].items.push(order);
|
||||
});
|
||||
|
||||
const groupedOrders = Object.values(groupedOrdersMap).sort((a, b) => {
|
||||
return parseOrderDate(b.date).getTime() - parseOrderDate(a.date).getTime();
|
||||
});
|
||||
|
||||
return { groupedOrders, totalSpent, totalItems, clientPhone };
|
||||
};
|
||||
Reference in New Issue
Block a user