Vendaweb-portal/components/dashboard/ProductsSoldView.tsx

474 lines
14 KiB
TypeScript
Raw Permalink Normal View History

2026-01-08 12:09:16 +00:00
import React, { useState, useEffect } from "react";
import LoadingSpinner from "../LoadingSpinner";
import { env } from "../../src/config/env";
import { authService } from "../../src/services/auth.service";
import { formatCurrency } from "../../utils/formatters";
import NoData from "../NoData";
import { Input } from "../ui/input";
import { Label } from "../ui/label";
import { Button } from "../ui/button";
import { CustomAutocomplete } from "../ui/autocomplete";
import { DateInput } from "../ui/date-input";
import { DataGridPremium, GridColDef } from "@mui/x-data-grid-premium";
import "../../lib/mui-license";
import { Box } from "@mui/material";
interface ProductOrder {
date: string;
orderId: number;
invoice?: any;
customerId: number;
customer: string;
seller: string;
productId: number;
product: string;
package: string;
quantity: number;
color?: string;
local?: string;
deliveryType: string;
itemId: string;
}
interface Store {
id: string;
shortName: string;
name: string;
}
interface ProductFilterOption {
id: string;
option: string;
}
const ProductsSoldView: React.FC = () => {
const [products, setProducts] = useState<ProductOrder[]>([]);
const [stores, setStores] = useState<Store[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// Filtros
const [selectedStore, setSelectedStore] = useState<string>("");
const [startDate, setStartDate] = useState<string>("");
const [endDate, setEndDate] = useState<string>("");
const [orderId, setOrderId] = useState<string>("");
const [document, setDocument] = useState<string>("");
const [customerName, setCustomerName] = useState<string>("");
const [productFilterType, setProductFilterType] = useState<string>("");
const [productText, setProductText] = useState<string>("");
// Opções de filtro de produto
const productFilterOptions: ProductFilterOption[] = [
{ id: "ID", option: "Código" },
{ id: "EAN", option: "Ean" },
{ id: "TEXT", option: "Descrição" },
{ id: "PARTNER", option: "Código Fábrica" },
];
useEffect(() => {
fetchStores();
}, []);
const fetchStores = async () => {
try {
const token = authService.getToken();
const apiUrl = env.API_URL.replace(/\/$/, "");
const response = await fetch(`${apiUrl}/lists/store`, {
method: "GET",
headers: {
"Content-Type": "application/json",
...(token && { Authorization: `Basic ${token}` }),
},
});
if (response.ok) {
const data = await response.json();
setStores(data);
}
} catch (err) {
console.error("Erro ao buscar filiais:", err);
}
};
const handleSearch = async () => {
setLoading(true);
setError(null);
try {
const token = authService.getToken();
const apiUrl = env.API_URL.replace(/\/$/, "");
// Seguindo o padrão do Angular: sellerId sempre 0
let sellerId = 0;
if (authService.isManager()) {
sellerId = 0;
}
// Se não selecionou filial, usar '99' como padrão (seguindo o Angular)
const store = selectedStore || "99";
const params = new URLSearchParams({
"x-store": store,
initialDate: startDate || "",
finalDate: endDate || "",
document: document || "",
name: customerName.toUpperCase() || "",
sellerId: sellerId.toString(),
idOrder: orderId || "",
typeFilterProduct: productFilterType || "",
productText: productText || "",
});
const response = await fetch(
`${apiUrl}/order/products-order?${params.toString()}`,
{
method: "GET",
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json",
...(token && { Authorization: `Basic ${token}` }),
},
}
);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message ||
`Erro ao buscar produtos vendidos: ${response.statusText}`
);
}
const data = await response.json();
setProducts(data || []);
} catch (err) {
console.error("Erro ao buscar produtos vendidos:", err);
setError(
err instanceof Error
? err.message
: "Erro ao buscar produtos vendidos. Tente novamente."
);
} finally {
setLoading(false);
}
};
const handleClear = () => {
setSelectedStore("");
setStartDate("");
setEndDate("");
setOrderId("");
setDocument("");
setCustomerName("");
setProductFilterType("");
setProductText("");
setProducts([]);
setError(null);
};
const formatDate = (dateString: string): string => {
if (!dateString) return "";
const date = new Date(dateString);
return date.toLocaleDateString("pt-BR");
};
// Definir colunas do DataGrid
const columns: GridColDef[] = [
{
field: "date",
headerName: "Data",
width: 120,
valueFormatter: (value) => formatDate(value as string),
},
{
field: "orderId",
headerName: "N.Pedido",
width: 130,
headerAlign: "left",
},
{
field: "customerId",
headerName: "Cód.Cliente",
width: 120,
headerAlign: "left",
},
{
field: "customer",
headerName: "Cliente",
width: 250,
flex: 1,
},
{
field: "seller",
headerName: "Vendedor",
width: 150,
},
{
field: "productId",
headerName: "Cód.Produto",
width: 130,
headerAlign: "left",
},
{
field: "product",
headerName: "Produto",
width: 300,
flex: 1,
},
{
field: "package",
headerName: "Embalagem",
width: 120,
},
{
field: "quantity",
headerName: "Quantidade",
width: 120,
headerAlign: "right",
align: "right",
},
{
field: "deliveryType",
headerName: "Tipo Entrega",
width: 150,
},
];
return (
<div className="space-y-6">
{/* Header */}
<header>
<h2 className="text-2xl font-black text-[#002147] mb-2">
Consulta vendas por produto
</h2>
</header>
{/* Filtros */}
<div className="bg-white p-6 rounded-2xl border border-slate-100 shadow-sm">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Filial de venda */}
<div>
<Label htmlFor="store">Filial de venda</Label>
<CustomAutocomplete
id="store"
options={stores.map((store) => ({
value: store.id,
label: store.shortName,
}))}
value={selectedStore}
onValueChange={setSelectedStore}
placeholder="Selecione a filial de venda..."
/>
</div>
{/* Número do pedido */}
<div>
<Label htmlFor="orderId">Número do pedido</Label>
<Input
id="orderId"
type="text"
value={orderId}
onChange={(e) => setOrderId(e.target.value)}
placeholder="Informe o número do pedido"
/>
</div>
{/* Data do pedido - Início */}
<div>
<Label htmlFor="startDate">Data inicial</Label>
<DateInput
id="startDate"
value={startDate}
onChange={(e) => setStartDate(e.target.value)}
/>
</div>
{/* Data do pedido - Fim */}
<div>
<Label htmlFor="endDate">Data final</Label>
<DateInput
id="endDate"
value={endDate}
onChange={(e) => setEndDate(e.target.value)}
/>
</div>
{/* CPF/CNPJ */}
<div>
<Label htmlFor="document">CPF / CNPJ</Label>
<Input
id="document"
type="text"
value={document}
onChange={(e) => setDocument(e.target.value)}
placeholder="Informe o CPF ou CNPJ do cliente"
/>
</div>
{/* Nome do cliente */}
<div>
<Label htmlFor="customerName">Nome do cliente</Label>
<Input
id="customerName"
type="text"
value={customerName}
onChange={(e) => setCustomerName(e.target.value)}
placeholder="Informe o nome ou razão social do cliente"
/>
</div>
{/* Tipo de pesquisa do produto */}
<div>
<Label htmlFor="productFilterType">Tipo Pesquisa Produto</Label>
<CustomAutocomplete
id="productFilterType"
options={productFilterOptions.map((option) => ({
value: option.id,
label: option.option,
}))}
value={productFilterType}
onValueChange={setProductFilterType}
placeholder="Selecione o tipo de pesquisa..."
/>
</div>
{/* Produto */}
<div>
<Label htmlFor="productText">Produto</Label>
<Input
id="productText"
type="text"
value={productText}
onChange={(e) => setProductText(e.target.value)}
placeholder="Informe o texto para pesquisa do produto"
/>
</div>
</div>
{/* Botões de Ação */}
<div className="mt-6 flex gap-3">
<Button onClick={handleSearch} disabled={loading} className="flex-1">
{loading ? "Pesquisando..." : "Pesquisar"}
</Button>
<Button
onClick={handleClear}
disabled={loading}
variant="outline"
className="flex-1"
>
Limpar
</Button>
</div>
</div>
{/* Tabela de Produtos Vendidos */}
{error && (
<div className="bg-red-50 border border-red-200 rounded-2xl p-4">
<p className="text-red-600 text-sm font-medium">{error}</p>
</div>
)}
{loading && (
<div className="flex items-center justify-center py-20">
<LoadingSpinner />
</div>
)}
{!loading && products.length > 0 && (
<div className="bg-white rounded-2xl border border-slate-100 overflow-hidden">
<div className="p-5 border-b border-slate-50">
<h3 className="text-[9px] font-black text-slate-400 uppercase tracking-[0.2em]">
Produtos encontrados: {products.length}
</h3>
</div>
<Box sx={{ height: 600, width: "100%" }}>
<DataGridPremium
rows={products}
columns={columns}
getRowId={(row) => `${row.orderId}-${row.itemId}`}
disableRowSelectionOnClick
hideFooter
sx={{
border: "none",
"& .MuiDataGrid-columnHeaders": {
backgroundColor: "#f8fafc",
borderBottom: "1px solid #e2e8f0",
"& .MuiDataGrid-columnHeader": {
fontSize: "9px",
fontWeight: 900,
color: "#94a3b8",
textTransform: "uppercase",
letterSpacing: "0.2em",
padding: "12px 16px",
"& .MuiDataGrid-columnHeaderTitle": {
fontWeight: 900,
},
},
},
"& .MuiDataGrid-row": {
"&:hover": {
backgroundColor: "#f8fafc",
},
},
"& .MuiDataGrid-cell": {
fontSize: "12px",
color: "#475569",
padding: "16px",
borderBottom: "1px solid #f1f5f9",
"&:focus": {
outline: "none",
},
"&:focus-within": {
outline: "none",
},
},
"& .MuiDataGrid-cell[data-field='orderId']": {
fontWeight: 700,
color: "#0f172a",
},
"& .MuiDataGrid-cell[data-field='productId']": {
fontWeight: 700,
color: "#0f172a",
},
"& .MuiDataGrid-cell[data-field='quantity']": {
fontWeight: 700,
color: "#0f172a",
},
"& .MuiDataGrid-cell[data-field='date']": {
color: "#64748b",
},
"& .MuiDataGrid-cell[data-field='customerId']": {
color: "#64748b",
},
"& .MuiDataGrid-cell[data-field='customer']": {
color: "#64748b",
},
"& .MuiDataGrid-virtualScroller": {
overflowY: "auto",
},
"& .MuiDataGrid-virtualScrollerContent": {
height: "auto !important",
},
}}
/>
</Box>
</div>
)}
{!loading && products.length === 0 && !error && (
<div className="bg-white rounded-2xl border border-slate-100 overflow-hidden">
<NoData
title="Nenhum produto encontrado"
description="Não foram encontrados produtos vendidos com os filtros informados. Tente ajustar os parâmetros de pesquisa ou verifique se há produtos no período selecionado."
icon="search"
variant="outline"
/>
</div>
)}
</div>
);
};
export default ProductsSoldView;