Collapse client detail orders
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 50s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 50s
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams, Link, useOutletContext } from 'react-router-dom';
|
||||
import { ArrowLeft, User, Tag, Package, DollarSign, Clock, Phone, ChevronLeft, ChevronRight, ShoppingBag, ReceiptText } from 'lucide-react';
|
||||
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 DateRangePicker from '../components/DateRangePicker';
|
||||
import type { ClientDetailsAnalytics, DateRange, OrderData } from '../types';
|
||||
@@ -55,6 +55,7 @@ const ClientDetails = () => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [ordersPerPage, setOrdersPerPage] = useState(5);
|
||||
const [expandedOrderIds, setExpandedOrderIds] = useState<Set<string>>(() => new Set());
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
@@ -94,9 +95,22 @@ const ClientDetails = () => {
|
||||
|
||||
const handleDateRangeChange = (range: DateRange) => {
|
||||
setCurrentPage(1);
|
||||
setExpandedOrderIds(new Set());
|
||||
setDateRange(range);
|
||||
};
|
||||
|
||||
const toggleOrder = (orderId: string) => {
|
||||
setExpandedOrderIds(current => {
|
||||
const next = new Set(current);
|
||||
if (next.has(orderId)) {
|
||||
next.delete(orderId);
|
||||
} else {
|
||||
next.add(orderId);
|
||||
}
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="text-center py-12">
|
||||
@@ -261,72 +275,105 @@ const ClientDetails = () => {
|
||||
<p className="text-sm font-bold text-zinc-900 dark:text-dark-text">Nenhum pedido no período selecionado.</p>
|
||||
<p className="mt-1 text-sm text-zinc-500 dark:text-dark-muted">Altere o filtro de data para ver outros pedidos deste cliente.</p>
|
||||
</div>
|
||||
) : paginatedOrders.map((group, groupIndex) => (
|
||||
<div key={groupIndex} className="bg-white dark:bg-dark-card border border-zinc-200 dark:border-dark-border rounded-2xl overflow-hidden shadow-sm">
|
||||
<div className="p-4 border-b border-zinc-100 dark:border-dark-border bg-zinc-50/50 dark:bg-dark-header flex justify-between items-center">
|
||||
<h2 className="text-sm font-bold uppercase tracking-wider text-zinc-500 dark:text-dark-muted flex items-center gap-2">
|
||||
<Tag className="w-4 h-4 text-brand-primary" />
|
||||
Pedido ID: {group.orderId}
|
||||
</h2>
|
||||
<span className="text-sm font-bold text-brand-primary">
|
||||
Total do Pedido: {formatCurrency(group.orderTotal)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="divide-y divide-zinc-100 dark:divide-dark-border">
|
||||
{group.items.map((order, index) => {
|
||||
const metadata = getOrderMetadata(order);
|
||||
) : paginatedOrders.map((group) => {
|
||||
const isExpanded = expandedOrderIds.has(group.orderId);
|
||||
|
||||
return (
|
||||
<div key={`${order.ID_Produto}-${index}`} className="px-4 py-2 flex flex-col md:flex-row md:items-center justify-between gap-3 hover:bg-zinc-50/50 dark:hover:bg-dark-input/30 transition-colors">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-0.5">
|
||||
<div className="flex items-center gap-1">
|
||||
<Tag className="w-3 h-3 text-zinc-400 dark:text-dark-muted" />
|
||||
<span className="text-[10px] font-bold text-zinc-400 dark:text-dark-muted">ID: {order.ID_Produto}</span>
|
||||
</div>
|
||||
{order.Data_Pedido && (
|
||||
<div className="flex items-center gap-1 ml-2">
|
||||
<Clock className="w-3 h-3 text-zinc-400 dark:text-dark-muted" />
|
||||
<span className="text-[10px] font-bold text-zinc-400 dark:text-dark-muted">
|
||||
Comprado: {order.Data_Pedido}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
return (
|
||||
<div key={group.orderId} className="bg-white dark:bg-dark-card border border-zinc-200 dark:border-dark-border rounded-2xl overflow-hidden shadow-sm">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleOrder(group.orderId)}
|
||||
aria-expanded={isExpanded}
|
||||
className="flex w-full cursor-pointer items-center justify-between gap-4 bg-zinc-50/50 px-4 py-3 text-left transition-colors hover:bg-zinc-100/80 dark:bg-dark-header dark:hover:bg-dark-input/60"
|
||||
>
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border border-zinc-200 bg-white text-brand-primary dark:border-dark-border dark:bg-dark-card">
|
||||
<Tag className="h-4 w-4" />
|
||||
</span>
|
||||
<div className="min-w-0">
|
||||
<h2 className="truncate text-sm font-bold uppercase tracking-wider text-zinc-700 dark:text-dark-text">
|
||||
Pedido ID: {group.orderId}
|
||||
</h2>
|
||||
<div className="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs font-bold text-zinc-400 dark:text-dark-muted">
|
||||
<span>{group.items.length} {group.items.length === 1 ? 'item' : 'itens'}</span>
|
||||
{group.date && (
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<Clock className="h-3 w-3" />
|
||||
{group.date}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 items-center gap-3">
|
||||
<div className="text-right">
|
||||
<p className="text-[10px] font-bold uppercase tracking-widest text-zinc-400 dark:text-dark-muted">Total</p>
|
||||
<p className="text-sm font-bold text-brand-primary">{formatCurrency(group.orderTotal)}</p>
|
||||
</div>
|
||||
<span className="flex h-8 w-8 items-center justify-center rounded-lg border border-zinc-200 bg-white text-zinc-500 transition-colors dark:border-dark-border dark:bg-dark-card dark:text-dark-muted">
|
||||
<ChevronDown className={`h-4 w-4 transition-transform ${isExpanded ? 'rotate-180' : ''}`} />
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{isExpanded && (
|
||||
<div className="divide-y divide-zinc-100 dark:divide-dark-border">
|
||||
{group.items.map((order, index) => {
|
||||
const metadata = getOrderMetadata(order);
|
||||
|
||||
return (
|
||||
<div key={`${order.ID_Produto}-${index}`} className="px-4 py-2 flex flex-col md:flex-row md:items-center justify-between gap-3 hover:bg-zinc-50/50 dark:hover:bg-dark-input/30 transition-colors">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-0.5">
|
||||
<div className="flex items-center gap-1">
|
||||
<Tag className="w-3 h-3 text-zinc-400 dark:text-dark-muted" />
|
||||
<span className="text-[10px] font-bold text-zinc-400 dark:text-dark-muted">ID: {order.ID_Produto}</span>
|
||||
</div>
|
||||
<h3 className="text-sm font-bold text-zinc-900 dark:text-dark-text truncate mb-1">{order.Descricao_Produto}</h3>
|
||||
|
||||
<div className="flex gap-4">
|
||||
<div className="flex items-center gap-1.5 text-[11px]">
|
||||
<Package className="w-3.5 h-3.5 text-zinc-400 dark:text-dark-muted" />
|
||||
<span className="text-zinc-500 dark:text-dark-muted font-medium">Qtd: <span className="text-zinc-900 dark:text-dark-text font-bold">{order.Quantidade}</span></span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 text-[11px]">
|
||||
<DollarSign className="w-3.5 h-3.5 text-zinc-400 dark:text-dark-muted" />
|
||||
<span className="text-zinc-500 dark:text-dark-muted font-medium">Preço: <span className="text-zinc-900 dark:text-dark-text font-bold">{formatCurrency(order.Valor_Unitario)}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
{metadata.length > 0 && (
|
||||
<div className="mt-2 flex flex-wrap gap-1.5">
|
||||
{metadata.map(value => (
|
||||
<span key={value} className="max-w-full break-all rounded-md border border-zinc-200 dark:border-dark-border px-2 py-0.5 text-[10px] font-bold text-zinc-500 dark:text-dark-muted">
|
||||
{value}
|
||||
</span>
|
||||
))}
|
||||
{order.Data_Pedido && (
|
||||
<div className="flex items-center gap-1 ml-2">
|
||||
<Clock className="w-3 h-3 text-zinc-400 dark:text-dark-muted" />
|
||||
<span className="text-[10px] font-bold text-zinc-400 dark:text-dark-muted">
|
||||
Comprado: {order.Data_Pedido}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<h3 className="text-sm font-bold text-zinc-900 dark:text-dark-text truncate mb-1">{order.Descricao_Produto}</h3>
|
||||
|
||||
<div className="text-right shrink-0">
|
||||
<p className="text-[10px] font-bold text-zinc-400 dark:text-dark-muted uppercase tracking-widest mb-0.5">Subtotal</p>
|
||||
<p className="text-base font-bold text-zinc-900 dark:text-dark-text">{formatCurrency(order.Quantidade * order.Valor_Unitario)}</p>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex items-center gap-1.5 text-[11px]">
|
||||
<Package className="w-3.5 h-3.5 text-zinc-400 dark:text-dark-muted" />
|
||||
<span className="text-zinc-500 dark:text-dark-muted font-medium">Qtd: <span className="text-zinc-900 dark:text-dark-text font-bold">{order.Quantidade}</span></span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 text-[11px]">
|
||||
<DollarSign className="w-3.5 h-3.5 text-zinc-400 dark:text-dark-muted" />
|
||||
<span className="text-zinc-500 dark:text-dark-muted font-medium">Preço: <span className="text-zinc-900 dark:text-dark-text font-bold">{formatCurrency(order.Valor_Unitario)}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
{metadata.length > 0 && (
|
||||
<div className="mt-2 flex flex-wrap gap-1.5">
|
||||
{metadata.map(value => (
|
||||
<span key={value} className="max-w-full break-all rounded-md border border-zinc-200 dark:border-dark-border px-2 py-0.5 text-[10px] font-bold text-zinc-500 dark:text-dark-muted">
|
||||
{value}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="text-right shrink-0">
|
||||
<p className="text-[10px] font-bold text-zinc-400 dark:text-dark-muted uppercase tracking-widest mb-0.5">Subtotal</p>
|
||||
<p className="text-base font-bold text-zinc-900 dark:text-dark-text">{formatCurrency(order.Quantidade * order.Valor_Unitario)}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="px-6 py-4 border border-zinc-200 dark:border-dark-border rounded-2xl bg-white dark:bg-dark-card flex flex-col sm:flex-row items-center justify-between gap-4">
|
||||
|
||||
Reference in New Issue
Block a user