chore: sincroniza projeto para gitea
This commit is contained in:
155
client/src/pages/PedidosCatalogo.js
Normal file
155
client/src/pages/PedidosCatalogo.js
Normal file
@@ -0,0 +1,155 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
FiRefreshCw,
|
||||
FiClipboard,
|
||||
FiClock,
|
||||
FiPhone,
|
||||
FiMapPin,
|
||||
FiPackage
|
||||
} from 'react-icons/fi';
|
||||
import toast from 'react-hot-toast';
|
||||
import '../styles/pedidos-catalogo.css';
|
||||
|
||||
const formatarMoeda = (valor) => {
|
||||
return (Number(valor) || 0).toLocaleString('pt-BR', {
|
||||
style: 'currency',
|
||||
currency: 'BRL'
|
||||
});
|
||||
};
|
||||
|
||||
const formatarData = (dataISO) => {
|
||||
if (!dataISO) return '-';
|
||||
try {
|
||||
return new Date(dataISO).toLocaleString('pt-BR', {
|
||||
dateStyle: 'short',
|
||||
timeStyle: 'short'
|
||||
});
|
||||
} catch (error) {
|
||||
return dataISO;
|
||||
}
|
||||
};
|
||||
|
||||
const PedidosCatalogo = () => {
|
||||
const [pedidos, setPedidos] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
const carregarPedidos = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const response = await fetch('/api/catalogo/pedidos');
|
||||
if (!response.ok) {
|
||||
throw new Error('Erro ao carregar pedidos');
|
||||
}
|
||||
const data = await response.json();
|
||||
setPedidos(Array.isArray(data) ? data : []);
|
||||
} catch (err) {
|
||||
console.error('Erro ao carregar pedidos:', err);
|
||||
setError('Não foi possível carregar os pedidos.');
|
||||
toast.error('Erro ao carregar pedidos do catálogo.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
carregarPedidos();
|
||||
}, []);
|
||||
|
||||
const pedidosOrdenados = useMemo(() => {
|
||||
return [...pedidos].sort((a, b) => new Date(b.createdAt || 0) - new Date(a.createdAt || 0));
|
||||
}, [pedidos]);
|
||||
|
||||
return (
|
||||
<div className="pedidos-catalogo-page fade-in">
|
||||
<div className="page-header">
|
||||
<div>
|
||||
<h1>Pedidos do Catálogo</h1>
|
||||
<p>Pedidos registrados automaticamente através do catálogo online</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
onClick={carregarPedidos}
|
||||
disabled={loading}
|
||||
>
|
||||
<FiRefreshCw />
|
||||
{loading ? 'Atualizando...' : 'Atualizar'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="alert alert-error">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{pedidosOrdenados.length === 0 && !loading ? (
|
||||
<div className="empty-state">
|
||||
<FiClipboard size={48} />
|
||||
<p>Nenhum pedido registrado ainda</p>
|
||||
<span>Assim que um cliente finalizar uma compra no catálogo, o pedido aparecerá aqui.</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="pedidos-lista">
|
||||
{pedidosOrdenados.map((pedido) => (
|
||||
<div key={pedido.id} className="pedido-card">
|
||||
<header className="pedido-card-header">
|
||||
<div className="pedido-id">
|
||||
<FiClipboard />
|
||||
<span>{pedido.codigo || pedido.id || 'Pedido sem ID'}</span>
|
||||
</div>
|
||||
<div className="pedido-data">
|
||||
<FiClock />
|
||||
<span>{formatarData(pedido.createdAt)}</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section className="pedido-cliente">
|
||||
<div className="cliente-nome">{pedido.cliente?.nome || 'Cliente não informado'}</div>
|
||||
<div className="cliente-info">
|
||||
<span><FiPhone /> {pedido.cliente?.whatsapp || 'Sem contato'}</span>
|
||||
{pedido.cliente?.endereco && (
|
||||
<span><FiMapPin /> {pedido.cliente.endereco}</span>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="pedido-itens">
|
||||
<h4>Itens</h4>
|
||||
<div className="pedido-itens-lista">
|
||||
{(pedido.itens || []).map((item, index) => (
|
||||
<div key={`${pedido.id}-item-${index}`} className="pedido-item">
|
||||
<div className="pedido-item-info">
|
||||
<strong>{item.nome}</strong>
|
||||
<div className="pedido-item-meta">
|
||||
<span><FiPackage /> {item.codigo || 'Sem ID'}</span>
|
||||
<span>Tam: {item.tamanho || '-'}</span>
|
||||
<span>Cor: {item.cor || '-'}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pedido-item-valores">
|
||||
<span>{item.quantidade || 0} x {formatarMoeda(item.preco)}</span>
|
||||
<strong>{formatarMoeda(item.subtotal)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer className="pedido-footer">
|
||||
<div className="pedido-total">
|
||||
<span>Total do pedido</span>
|
||||
<strong>{formatarMoeda(pedido.total)}</strong>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PedidosCatalogo;
|
||||
Reference in New Issue
Block a user