Fix RFV period buyers with historical scoring
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m34s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m34s
This commit is contained in:
@@ -13,6 +13,7 @@ export type ClientSortOption =
|
||||
| 'items_asc';
|
||||
|
||||
export interface ClientSummary {
|
||||
customerKey: string;
|
||||
name: string;
|
||||
phone: string;
|
||||
totalSpent: number;
|
||||
@@ -113,6 +114,7 @@ export const buildClientsSummary = (
|
||||
): ClientSummary[] => {
|
||||
const orders = filterOrdersByDateRange(ordersData, dateRange);
|
||||
const clientMap: Record<string, {
|
||||
name: string;
|
||||
totalSpent: number;
|
||||
totalItems: number;
|
||||
uniqueOrders: Set<string>;
|
||||
@@ -122,33 +124,35 @@ export const buildClientsSummary = (
|
||||
|
||||
orders.forEach(order => {
|
||||
const clientName = getClientDisplayName(order);
|
||||
const customerKey = order.Fone_Cliente || `name:${clientName}`;
|
||||
|
||||
if (!clientMap[clientName]) {
|
||||
clientMap[clientName] = { totalSpent: 0, totalItems: 0, uniqueOrders: new Set(), lastPurchase: 0, phone: '' };
|
||||
if (!clientMap[customerKey]) {
|
||||
clientMap[customerKey] = { name: clientName, totalSpent: 0, totalItems: 0, uniqueOrders: new Set(), lastPurchase: 0, phone: '' };
|
||||
}
|
||||
|
||||
if (order.Fone_Cliente) {
|
||||
clientMap[clientName].phone = order.Fone_Cliente;
|
||||
clientMap[customerKey].phone = order.Fone_Cliente;
|
||||
}
|
||||
|
||||
clientMap[clientName].totalSpent += getOrderItemRevenue(order);
|
||||
clientMap[clientName].totalItems += order.Quantidade;
|
||||
clientMap[clientName].uniqueOrders.add(`${order.Data_Pedido}_${order.Valor_Pedido}`);
|
||||
clientMap[customerKey].totalSpent += getOrderItemRevenue(order);
|
||||
clientMap[customerKey].totalItems += order.Quantidade;
|
||||
clientMap[customerKey].uniqueOrders.add(`${order.Data_Pedido}_${order.Valor_Pedido}`);
|
||||
|
||||
const orderTime = parseOrderDate(order.Data_Pedido).getTime();
|
||||
if (orderTime > clientMap[clientName].lastPurchase) {
|
||||
clientMap[clientName].lastPurchase = orderTime;
|
||||
if (orderTime > clientMap[customerKey].lastPurchase) {
|
||||
clientMap[customerKey].lastPurchase = orderTime;
|
||||
}
|
||||
});
|
||||
|
||||
const normalizedSearch = searchTerm.trim().toLowerCase();
|
||||
const clients = enrichClientsWithRfmType(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 clients = enrichClientsWithRfmType(Object.keys(clientMap).map(customerKey => ({
|
||||
customerKey,
|
||||
name: clientMap[customerKey].name,
|
||||
phone: clientMap[customerKey].phone,
|
||||
totalSpent: clientMap[customerKey].totalSpent,
|
||||
totalItems: clientMap[customerKey].totalItems,
|
||||
orderCount: clientMap[customerKey].uniqueOrders.size,
|
||||
lastPurchase: clientMap[customerKey].lastPurchase
|
||||
})), dateRange);
|
||||
|
||||
const filteredClients = normalizedSearch
|
||||
|
||||
@@ -102,10 +102,11 @@ const Clients = () => {
|
||||
|
||||
const allClientsData = useMemo(() => {
|
||||
const normalizedSearch = searchTerm.trim().toLowerCase();
|
||||
const rfmByPhone = new Map((rfmAnalytics?.clients || []).map(client => [client.phone, client]));
|
||||
const rfmByCustomerKey = new Map((rfmAnalytics?.clients || []).map(client => [client.customerKey, client]));
|
||||
const clients = clientAnalytics.map((client): ClientSummary => {
|
||||
const rfmClient = client.phone ? rfmByPhone.get(client.phone) : undefined;
|
||||
const rfmClient = rfmByCustomerKey.get(client.customerKey);
|
||||
return {
|
||||
customerKey: client.customerKey,
|
||||
name: client.name,
|
||||
phone: client.phone,
|
||||
totalSpent: client.totalSpent,
|
||||
@@ -281,7 +282,7 @@ const Clients = () => {
|
||||
</thead>
|
||||
<tbody className="divide-y divide-zinc-100 dark:divide-dark-border">
|
||||
{paginatedData.map((client, index) => (
|
||||
<tr key={client.name} className="hover:bg-zinc-50/80 dark:hover:bg-dark-input/50 transition-colors group">
|
||||
<tr key={client.customerKey} className="hover:bg-zinc-50/80 dark:hover:bg-dark-input/50 transition-colors group">
|
||||
<td className="px-6 py-2.5">
|
||||
<span className="inline-flex items-center justify-center w-7 h-7 rounded-full text-xs font-bold bg-zinc-100 dark:bg-dark-border text-zinc-500 dark:text-dark-muted">
|
||||
{startIndex + index + 1}
|
||||
|
||||
@@ -292,7 +292,7 @@ const Rfm = () => {
|
||||
<div className="bg-dark-card p-6 rounded-2xl border border-dark-border shadow-sm">
|
||||
<p className="text-dark-muted text-sm font-medium mb-1">Clientes no Período</p>
|
||||
<h3 className="text-3xl font-bold text-dark-text">{clients.length}</h3>
|
||||
<p className="mt-1 text-xs font-semibold text-dark-muted">Agrupados pela tag RFV anterior</p>
|
||||
<p className="mt-1 text-xs font-semibold text-dark-muted">Segmento RFV calculado até o fim do período</p>
|
||||
</div>
|
||||
<div className="bg-dark-card p-6 rounded-2xl border border-dark-border shadow-sm">
|
||||
<p className="text-dark-muted text-sm font-medium mb-1">Receita no Período</p>
|
||||
@@ -312,7 +312,7 @@ const Rfm = () => {
|
||||
<div className="mb-4 flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between">
|
||||
<div>
|
||||
<h2 className="text-lg font-bold text-dark-text">Matriz RFV</h2>
|
||||
<p className="text-sm font-medium text-dark-muted">Compradores do período agrupados pela tag RFV anterior ao período.</p>
|
||||
<p className="text-sm font-medium text-dark-muted">Compradores do período agrupados pelo RFV histórico até a data final.</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs font-semibold text-dark-muted">
|
||||
<span>Menor prioridade</span>
|
||||
@@ -531,7 +531,7 @@ const Rfm = () => {
|
||||
{paginatedClients.map((client: RfmClient) => {
|
||||
const style = segmentStyles[client.segmentKey] || segmentStyles.lost;
|
||||
return (
|
||||
<tr key={client.phone} className="hover:bg-dark-input/50 transition-colors">
|
||||
<tr key={client.customerKey} className="hover:bg-dark-input/50 transition-colors">
|
||||
<td className="px-6 py-3">
|
||||
<Link to={`/clients/${encodeURIComponent(client.name)}`} className="flex items-center gap-3 hover:text-brand-primary transition-colors">
|
||||
<span className="flex h-9 w-9 items-center justify-center rounded-xl bg-dark-input text-dark-muted">
|
||||
|
||||
@@ -64,6 +64,7 @@ export interface DashboardAnalytics {
|
||||
}
|
||||
|
||||
export interface ClientAnalyticsItem {
|
||||
customerKey: string;
|
||||
name: string;
|
||||
phone: string;
|
||||
quantityPurchased: number;
|
||||
@@ -73,6 +74,7 @@ export interface ClientAnalyticsItem {
|
||||
}
|
||||
|
||||
export interface RfmClient {
|
||||
customerKey: string;
|
||||
name: string;
|
||||
phone: string;
|
||||
monetary: number;
|
||||
|
||||
Reference in New Issue
Block a user