Add client purchase pattern charts
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m9s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m9s
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams, Link, useOutletContext } from 'react-router-dom';
|
||||
import { ArrowLeft, User, Tag, Package, DollarSign, Clock, Phone, ChevronDown, ChevronLeft, ChevronRight, ShoppingBag, ReceiptText } from 'lucide-react';
|
||||
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
||||
import { AreaChart, Area, BarChart, Bar, Cell, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
||||
import DateRangePicker from '../components/DateRangePicker';
|
||||
import type { ClientDetailsAnalytics, DateRange, OrderData } from '../types';
|
||||
import { fetchClientDetailsAnalytics } from '../dataService';
|
||||
@@ -11,6 +11,8 @@ const CHART_GRID_COLOR = 'var(--chart-grid)';
|
||||
const CHART_AXIS_COLOR = 'var(--chart-axis)';
|
||||
const CHART_CURSOR_COLOR = 'var(--chart-cursor)';
|
||||
const CHART_DETAIL_BAR_COLOR = 'var(--chart-detail-bar)';
|
||||
const WEEKDAY_BAR_COLOR = '#25C2FF';
|
||||
const HOUR_BAR_COLOR = '#52DFA0';
|
||||
|
||||
type CustomTooltipProps = {
|
||||
active?: boolean;
|
||||
@@ -32,6 +34,21 @@ const CustomTooltip = ({ active, payload, label }: CustomTooltipProps) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const PatternTooltip = ({ active, payload, label }: CustomTooltipProps) => {
|
||||
if (active && payload && payload.length) {
|
||||
const value = payload[0].value;
|
||||
return (
|
||||
<div className="rounded-xl bg-dark-card p-3 shadow-lg">
|
||||
<p className="mb-1 font-bold text-brand-primary">{label}</p>
|
||||
<p className="m-0 text-dark-text">
|
||||
{value} {value === 1 ? 'pedido' : 'pedidos'}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const getOrderMetadata = (order: OrderData) => {
|
||||
const sellerName = formatDisplayName(removeTrailingSellerId(order.nome_vendedor || ''));
|
||||
|
||||
@@ -131,6 +148,8 @@ const ClientDetails = () => {
|
||||
const {
|
||||
chartData,
|
||||
groupedOrders,
|
||||
purchaseHours = [],
|
||||
purchaseWeekdays = [],
|
||||
allTimeOrderCount,
|
||||
clientName,
|
||||
clientPhone,
|
||||
@@ -144,6 +163,8 @@ const ClientDetails = () => {
|
||||
const safeCurrentPage = Math.min(currentPage, totalPages || 1);
|
||||
const startIndex = (safeCurrentPage - 1) * ordersPerPage;
|
||||
const paginatedOrders = groupedOrders.slice(startIndex, startIndex + ordersPerPage);
|
||||
const hasWeekdayPattern = purchaseWeekdays.some(day => day.value > 0);
|
||||
const hasHourPattern = purchaseHours.some(hour => hour.value > 0);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -268,6 +289,73 @@ const ClientDetails = () => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<section className="space-y-4">
|
||||
<div>
|
||||
<h3 className="text-lg font-bold text-zinc-900 dark:text-dark-text">Padrão de Compra</h3>
|
||||
<p className="mt-1 text-sm font-medium text-zinc-500 dark:text-dark-muted">Quando este cliente costuma comprar.</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
<div className="bg-white dark:bg-dark-card border border-zinc-200 dark:border-dark-border rounded-2xl p-6 shadow-sm">
|
||||
<h4 className="text-sm font-bold uppercase tracking-widest text-zinc-500 dark:text-dark-muted">Compras por Dia</h4>
|
||||
{hasWeekdayPattern ? (
|
||||
<div className="mt-5 h-56">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart data={purchaseWeekdays} margin={{ top: 8, right: 10, left: -18, bottom: 0 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke={CHART_GRID_COLOR} vertical={false} />
|
||||
<XAxis dataKey="label" stroke={CHART_AXIS_COLOR} fontSize={11} tickLine={false} axisLine={false} />
|
||||
<YAxis allowDecimals={false} stroke={CHART_AXIS_COLOR} fontSize={11} tickLine={false} axisLine={false} />
|
||||
<Tooltip content={<PatternTooltip />} cursor={{ fill: CHART_CURSOR_COLOR }} />
|
||||
<Bar dataKey="value" radius={[4, 4, 0, 0]}>
|
||||
{purchaseWeekdays.map(day => (
|
||||
<Cell key={`weekday-${day.label}`} fill={WEEKDAY_BAR_COLOR} fillOpacity={0.62} stroke={WEEKDAY_BAR_COLOR} strokeOpacity={0.9} strokeWidth={1.25} />
|
||||
))}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-56 items-center justify-center text-sm font-semibold text-zinc-500 dark:text-dark-muted">
|
||||
Sem compras no período.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="bg-white dark:bg-dark-card border border-zinc-200 dark:border-dark-border rounded-2xl p-6 shadow-sm">
|
||||
<h4 className="text-sm font-bold uppercase tracking-widest text-zinc-500 dark:text-dark-muted">Compras por Horário</h4>
|
||||
{hasHourPattern ? (
|
||||
<div className="mt-5 h-56">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart data={purchaseHours} margin={{ top: 8, right: 10, left: -18, bottom: 0 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke={CHART_GRID_COLOR} vertical={false} />
|
||||
<XAxis
|
||||
dataKey="label"
|
||||
stroke={CHART_AXIS_COLOR}
|
||||
fontSize={10}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
interval={0}
|
||||
tickFormatter={(value) => Number(String(value).replace('h', '')) % 3 === 0 ? String(value) : ''}
|
||||
/>
|
||||
<YAxis allowDecimals={false} stroke={CHART_AXIS_COLOR} fontSize={11} tickLine={false} axisLine={false} />
|
||||
<Tooltip content={<PatternTooltip />} cursor={{ fill: CHART_CURSOR_COLOR }} />
|
||||
<Bar dataKey="value" radius={[4, 4, 0, 0]}>
|
||||
{purchaseHours.map(hour => (
|
||||
<Cell key={`hour-${hour.label}`} fill={HOUR_BAR_COLOR} fillOpacity={0.56} stroke={HOUR_BAR_COLOR} strokeOpacity={0.86} strokeWidth={1.1} />
|
||||
))}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-56 items-center justify-center px-6 text-center text-sm font-semibold text-zinc-500 dark:text-dark-muted">
|
||||
Sem horário de compra disponível para este período.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* Orders List */}
|
||||
<div className="flex flex-col gap-6">
|
||||
{paginatedOrders.length === 0 ? (
|
||||
|
||||
Reference in New Issue
Block a user