refactor(frontend): type legacy payments and catalog flows
This commit is contained in:
parent
b633939d4e
commit
48a54a616e
8 changed files with 279 additions and 285 deletions
|
|
@ -1,4 +1,3 @@
|
|||
// @ts-nocheck
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
|
@ -11,11 +10,12 @@ import PagamentoForm from '@/components/PagamentoForm';
|
|||
import PagamentoList from '@/components/PagamentoList';
|
||||
import { usePagamentos } from '@/hooks/usePagamentos';
|
||||
import { PagamentoData } from '@/services/pagamentoService';
|
||||
import { PagamentoDocument } from '@/types/legacyEntities';
|
||||
|
||||
const GestaoPagamentos = () => {
|
||||
const router = useRouter();
|
||||
const [user, setUser] = useState<Models.User<Models.Preferences> | null>(null);
|
||||
const [editing, setEditing] = useState<Models.Document | null>(null);
|
||||
const [editing, setEditing] = useState<PagamentoDocument | null>(null);
|
||||
const [activeTab, setActiveTab] = useState<'lista' | 'cadastro'>('lista');
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ const GestaoPagamentos = () => {
|
|||
await listarPagamentos(1, searchTerm);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Usuário não autenticado:', error);
|
||||
console.error('Usuário não autenticado:', error);
|
||||
router.push('/');
|
||||
}
|
||||
};
|
||||
|
|
@ -57,17 +57,17 @@ const GestaoPagamentos = () => {
|
|||
setActiveTab('lista');
|
||||
}
|
||||
return success;
|
||||
} else {
|
||||
const success = await cadastrarPagamento(formData);
|
||||
if (success) {
|
||||
setTimeout(() => setActiveTab('lista'), 2000);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
const success = await cadastrarPagamento(formData);
|
||||
if (success) {
|
||||
setTimeout(() => setActiveTab('lista'), 2000);
|
||||
}
|
||||
return success;
|
||||
};
|
||||
|
||||
const handleEdit = (pag: Models.Document) => {
|
||||
setEditing(pag);
|
||||
const handleEdit = (pagamento: PagamentoDocument) => {
|
||||
setEditing(pagamento);
|
||||
setActiveTab('cadastro');
|
||||
};
|
||||
|
||||
|
|
@ -99,7 +99,7 @@ const GestaoPagamentos = () => {
|
|||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
|
||||
<p className="text-gray-600">Verificando autenticação...</p>
|
||||
<p className="text-gray-600">Verificando autenticação...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -109,7 +109,7 @@ const GestaoPagamentos = () => {
|
|||
<div className="min-h-screen bg-gray-50">
|
||||
<Header
|
||||
user={user}
|
||||
title="Gestão de Pagamentos"
|
||||
title="Gestão de Pagamentos"
|
||||
subtitle="Gerencie os pagamentos da plataforma"
|
||||
/>
|
||||
|
||||
|
|
@ -178,4 +178,3 @@ const GestaoPagamentos = () => {
|
|||
};
|
||||
|
||||
export default GestaoPagamentos;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
import React from "react";
|
||||
import { Models } from "@/lib/appwrite";
|
||||
import TableActions from "./TableActions";
|
||||
import React from 'react';
|
||||
import TableActions from './TableActions';
|
||||
import { CatalogoProdutoDocument } from '@/types/legacyEntities';
|
||||
|
||||
interface CatalogoProdutosListProps {
|
||||
produtos: Models.Document[];
|
||||
produtos: CatalogoProdutoDocument[];
|
||||
loading: boolean;
|
||||
isChangingPage?: boolean;
|
||||
totalProdutos: number;
|
||||
currentPage: number;
|
||||
pageSize: number;
|
||||
onEdit: (produto: Models.Document) => void;
|
||||
onEdit: (produto: CatalogoProdutoDocument) => void;
|
||||
onDelete: (id: string) => void;
|
||||
onView: (produto: Models.Document) => void;
|
||||
onView: (produto: CatalogoProdutoDocument) => void;
|
||||
onPrevPage: () => void;
|
||||
onNextPage: () => void;
|
||||
onRowClick?: (produto: Models.Document) => void;
|
||||
onRowClick?: (produto: CatalogoProdutoDocument) => void;
|
||||
}
|
||||
|
||||
const CatalogoProdutosList: React.FC<CatalogoProdutosListProps> = ({
|
||||
|
|
@ -42,7 +42,7 @@ const CatalogoProdutosList: React.FC<CatalogoProdutosListProps> = ({
|
|||
<div className="flex items-center justify-center p-8">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
||||
<span className="ml-2 text-gray-600">
|
||||
{isChangingPage ? "Carregando página..." : "Carregando produtos..."}
|
||||
{isChangingPage ? 'Carregando página...' : 'Carregando produtos...'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -67,36 +67,17 @@ const CatalogoProdutosList: React.FC<CatalogoProdutosListProps> = ({
|
|||
<table className="laboratory-table w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50/50">
|
||||
Nome do Produto
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50/50">
|
||||
Código Interno
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50/50">
|
||||
Código EAN
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50/50">
|
||||
Descrição
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50/50">
|
||||
Preço Atual
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50/50">
|
||||
Ações
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50/50">Nome do Produto</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50/50">Código Interno</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50/50">Código EAN</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50/50">Descrição</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50/50">Preço Atual</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50/50">Ações</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{produtos.map((produto) => {
|
||||
const produtoData = produto as any;
|
||||
const catalogoProduto = produtoData["catalogo-produtos"];
|
||||
const nomeProduto =
|
||||
produtoData.nome ||
|
||||
catalogoProduto?.nome ||
|
||||
produtoData.descricao ||
|
||||
catalogoProduto?.descricao ||
|
||||
"N/A";
|
||||
{produtos.map(produto => {
|
||||
const nomeProduto = produto.nome || produto.descricao || 'N/A';
|
||||
|
||||
return (
|
||||
<tr
|
||||
|
|
@ -105,19 +86,11 @@ const CatalogoProdutosList: React.FC<CatalogoProdutosListProps> = ({
|
|||
onClick={() => onRowClick?.(produto)}
|
||||
>
|
||||
<td className="px-6 py-4 max-w-xs font-medium text-gray-900 line-clamp-2">{nomeProduto}</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-700">{produto['codigo-interno'] || 'N/A'}</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-700">{produto['codigo-ean'] || 'N/A'}</td>
|
||||
<td className="px-6 py-4 max-w-xs text-sm text-gray-700 line-clamp-2">{produto.descricao || 'N/A'}</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-700">
|
||||
{produtoData["codigo-interno"] || catalogoProduto?.["codigo-interno"] || "N/A"}
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-700">
|
||||
{produtoData["codigo-ean"] || catalogoProduto?.["codigo-ean"] || "N/A"}
|
||||
</td>
|
||||
<td className="px-6 py-4 max-w-xs text-sm text-gray-700 line-clamp-2">
|
||||
{produtoData.descricao || catalogoProduto?.descricao || "N/A"}
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-700">
|
||||
{typeof produtoData["preco-atual"] === "number"
|
||||
? `R$ ${produtoData["preco-atual"].toFixed(2)}`
|
||||
: "N/A"}
|
||||
{typeof produto['preco-atual'] === 'number' ? `R$ ${produto['preco-atual'].toFixed(2)}` : 'N/A'}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<TableActions
|
||||
|
|
@ -143,25 +116,23 @@ const CatalogoProdutosList: React.FC<CatalogoProdutosListProps> = ({
|
|||
disabled={currentPage === 1}
|
||||
className={`px-3 py-1 rounded-md text-sm font-medium ${
|
||||
currentPage === 1
|
||||
? "bg-gray-100 text-gray-400 cursor-not-allowed"
|
||||
: "bg-white text-gray-700 hover:bg-gray-50 border border-gray-300"
|
||||
? 'bg-gray-100 text-gray-400 cursor-not-allowed'
|
||||
: 'bg-white text-gray-700 hover:bg-gray-50 border border-gray-300'
|
||||
}`}
|
||||
>
|
||||
Anterior
|
||||
</button>
|
||||
<span className="px-3 py-1 text-sm text-gray-700">
|
||||
Página {currentPage} de {totalPages}
|
||||
</span>
|
||||
<span className="px-3 py-1 text-sm text-gray-700">Página {currentPage} de {totalPages}</span>
|
||||
<button
|
||||
onClick={onNextPage}
|
||||
disabled={currentPage === totalPages}
|
||||
className={`px-3 py-1 rounded-md text-sm font-medium ${
|
||||
currentPage === totalPages
|
||||
? "bg-gray-100 text-gray-400 cursor-not-allowed"
|
||||
: "bg-white text-gray-700 hover:bg-gray-50 border border-gray-300"
|
||||
? 'bg-gray-100 text-gray-400 cursor-not-allowed'
|
||||
: 'bg-white text-gray-700 hover:bg-gray-50 border border-gray-300'
|
||||
}`}
|
||||
>
|
||||
Próxima
|
||||
Próxima
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -171,4 +142,3 @@ const CatalogoProdutosList: React.FC<CatalogoProdutosListProps> = ({
|
|||
};
|
||||
|
||||
export default CatalogoProdutosList;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,86 +1,112 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Models } from '@/lib/appwrite';
|
||||
|
||||
// Interface para dados do pagamento
|
||||
interface PagamentoData {
|
||||
pedidos: string;
|
||||
status: 'pendente' | 'pago' | 'cancelado';
|
||||
metodo: 'pix' | 'cartao';
|
||||
valor: number;
|
||||
}
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import type { PagamentoData } from '@/services/pagamentoService';
|
||||
import { PagamentoDocument } from '@/types/legacyEntities';
|
||||
|
||||
interface PagamentoFormProps {
|
||||
onSubmit: (data: PagamentoData) => Promise<boolean>;
|
||||
onCancel?: () => void;
|
||||
initialData?: PagamentoData | null;
|
||||
initialData?: PagamentoDocument | null;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
const statusOptions = ['pendente', 'pago', 'cancelado'];
|
||||
const metodoOptions = ['pix', 'cartao'];
|
||||
const statusOptions: PagamentoData['status'][] = ['pendente', 'pago', 'cancelado'];
|
||||
const metodoOptions: PagamentoData['metodo'][] = ['pix', 'cartao'];
|
||||
|
||||
const normalizePedidos = (pedidos: PagamentoDocument['pedidos']): string[] => {
|
||||
if (Array.isArray(pedidos)) {
|
||||
return pedidos.map(item => String(item));
|
||||
}
|
||||
if (pedidos && typeof pedidos === 'object') {
|
||||
return pedidos.$id ? [pedidos.$id] : [];
|
||||
}
|
||||
if (typeof pedidos === 'string' && pedidos.trim()) {
|
||||
return [pedidos.trim()];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const PagamentoForm: React.FC<PagamentoFormProps> = ({
|
||||
onSubmit,
|
||||
onCancel,
|
||||
initialData,
|
||||
loading = false
|
||||
loading = false,
|
||||
}) => {
|
||||
const [formData, setFormData] = useState<PagamentoData>({
|
||||
pedidos: '',
|
||||
pedidos: [],
|
||||
status: 'pendente',
|
||||
metodo: 'pix',
|
||||
valor: 0
|
||||
valor: 0,
|
||||
});
|
||||
const [pedidoInput, setPedidoInput] = useState('');
|
||||
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (initialData) {
|
||||
const pedidos = normalizePedidos(initialData.pedidos);
|
||||
setFormData({
|
||||
pedidos: initialData.pedidos || '',
|
||||
pedidos,
|
||||
status: initialData.status || 'pendente',
|
||||
metodo: initialData.metodo || 'pix',
|
||||
valor: Number(initialData.valor) || 0
|
||||
valor: Number(initialData.valor) || 0,
|
||||
});
|
||||
} else {
|
||||
setFormData({ pedidos: '', status: 'pendente', metodo: 'pix', valor: 0 });
|
||||
setPedidoInput(pedidos.join(', '));
|
||||
return;
|
||||
}
|
||||
|
||||
setFormData({ pedidos: [], status: 'pendente', metodo: 'pix', valor: 0 });
|
||||
setPedidoInput('');
|
||||
}, [initialData]);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const pedidos = pedidoInput
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const success = await onSubmit(formData);
|
||||
const success = await onSubmit({ ...formData, pedidos });
|
||||
|
||||
if (success) {
|
||||
setMessage({
|
||||
type: 'success',
|
||||
text: initialData ? '🔄 Pagamento atualizado com sucesso!' : '🎉 Pagamento cadastrado com sucesso!'
|
||||
text: initialData ? 'Pagamento atualizado com sucesso!' : 'Pagamento cadastrado com sucesso!',
|
||||
});
|
||||
|
||||
if (!initialData) {
|
||||
setFormData({ pedidos: '', status: 'pendente', metodo: 'pix', valor: 0 });
|
||||
setFormData({ pedidos: [], status: 'pendente', metodo: 'pix', valor: 0 });
|
||||
setPedidoInput('');
|
||||
}
|
||||
|
||||
setTimeout(() => setMessage(null), 3000);
|
||||
} else {
|
||||
setMessage({
|
||||
type: 'error',
|
||||
text: initialData ? 'Erro ao atualizar pagamento' : 'Erro ao cadastrar pagamento'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setMessage({
|
||||
type: 'error',
|
||||
text: initialData ? 'Erro ao atualizar pagamento' : 'Erro ao cadastrar pagamento',
|
||||
});
|
||||
};
|
||||
|
||||
const handleSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({ ...prev, [name]: value }));
|
||||
if (message) {
|
||||
setMessage(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData((prev: PagamentoData) => ({ ...prev, [name]: name === 'valor' ? Number(value) : value }));
|
||||
if (message) setMessage(null);
|
||||
const handleValueChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setFormData(prev => ({ ...prev, valor: Number(e.target.value) }));
|
||||
if (message) {
|
||||
setMessage(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow-md p-6">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-2">
|
||||
{initialData ? 'âœï¸ Editar Pagamento' : 'Novo Pagamento'}
|
||||
{initialData ? 'Editar Pagamento' : 'Novo Pagamento'}
|
||||
</h2>
|
||||
<p className="text-gray-600">
|
||||
{initialData ? 'Atualize os dados do pagamento.' : 'Cadastre um novo pagamento.'}
|
||||
|
|
@ -105,12 +131,12 @@ const PagamentoForm: React.FC<PagamentoFormProps> = ({
|
|||
type="text"
|
||||
id="pedidos"
|
||||
name="pedidos"
|
||||
value={formData.pedidos}
|
||||
onChange={handleInputChange}
|
||||
value={pedidoInput}
|
||||
onChange={e => setPedidoInput(e.target.value)}
|
||||
required
|
||||
disabled={loading}
|
||||
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed"
|
||||
placeholder="ID do pedido"
|
||||
placeholder="ID do pedido ou lista separada por vírgula"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -122,7 +148,7 @@ const PagamentoForm: React.FC<PagamentoFormProps> = ({
|
|||
id="status"
|
||||
name="status"
|
||||
value={formData.status}
|
||||
onChange={handleInputChange}
|
||||
onChange={handleSelectChange}
|
||||
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed"
|
||||
disabled={loading}
|
||||
>
|
||||
|
|
@ -134,13 +160,13 @@ const PagamentoForm: React.FC<PagamentoFormProps> = ({
|
|||
|
||||
<div>
|
||||
<label htmlFor="metodo" className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Método *
|
||||
Método *
|
||||
</label>
|
||||
<select
|
||||
id="metodo"
|
||||
name="metodo"
|
||||
value={formData.metodo}
|
||||
onChange={handleInputChange}
|
||||
onChange={handleSelectChange}
|
||||
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed"
|
||||
disabled={loading}
|
||||
>
|
||||
|
|
@ -159,8 +185,8 @@ const PagamentoForm: React.FC<PagamentoFormProps> = ({
|
|||
step="0.01"
|
||||
id="valor"
|
||||
name="valor"
|
||||
value={formData.valor}
|
||||
onChange={handleInputChange}
|
||||
value={formData.valor || 0}
|
||||
onChange={handleValueChange}
|
||||
required
|
||||
disabled={loading}
|
||||
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed"
|
||||
|
|
@ -175,7 +201,7 @@ const PagamentoForm: React.FC<PagamentoFormProps> = ({
|
|||
disabled={loading}
|
||||
className="flex-1 bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors cursor-pointer"
|
||||
>
|
||||
{loading ? 'ⳠProcessando...' : (initialData ? '🔄 Atualizar' : '➕ Cadastrar')}
|
||||
{loading ? 'Processando...' : (initialData ? 'Atualizar' : 'Cadastrar')}
|
||||
</button>
|
||||
|
||||
{onCancel && (
|
||||
|
|
@ -185,7 +211,7 @@ const PagamentoForm: React.FC<PagamentoFormProps> = ({
|
|||
disabled={loading}
|
||||
className="flex-1 bg-gray-600 text-white py-2 px-4 rounded-md hover:bg-gray-700 focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors cursor-pointer"
|
||||
>
|
||||
⌠Cancelar
|
||||
Cancelar
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
import React from 'react';
|
||||
import { Models } from '@/lib/appwrite';
|
||||
import React from 'react';
|
||||
import RefreshButton from './RefreshButton';
|
||||
import ListHeader from './ListHeader';
|
||||
import DataTable, { Column } from './DataTable';
|
||||
import Pagination from './Pagination';
|
||||
import TableActions from './TableActions';
|
||||
import SearchBar from './SearchBar';
|
||||
import { PagamentoDocument } from '@/types/legacyEntities';
|
||||
|
||||
interface PagamentoListProps {
|
||||
pagamentos: Models.Document[];
|
||||
pagamentos: PagamentoDocument[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
totalPagamentos: number;
|
||||
currentPage: number;
|
||||
pageSize: number;
|
||||
onEdit: (p: Models.Document) => void;
|
||||
onEdit: (pagamento: PagamentoDocument) => void;
|
||||
onDelete: (id: string) => Promise<boolean>;
|
||||
onRefresh: () => void;
|
||||
onPrevPage: () => void;
|
||||
|
|
@ -22,11 +22,6 @@ interface PagamentoListProps {
|
|||
onSearch: (term: string) => void;
|
||||
}
|
||||
|
||||
type PagamentoDocument = Models.Document & {
|
||||
pedidos?: string[] | { $id?: string } | string | null;
|
||||
valor?: number | string;
|
||||
};
|
||||
|
||||
const PagamentoList: React.FC<PagamentoListProps> = ({
|
||||
pagamentos,
|
||||
loading,
|
||||
|
|
@ -52,18 +47,28 @@ const PagamentoList: React.FC<PagamentoListProps> = ({
|
|||
header: 'Pedido',
|
||||
render: row => {
|
||||
const pedidos = row.pedidos;
|
||||
if (Array.isArray(pedidos)) return pedidos.join(', ');
|
||||
if (pedidos && typeof pedidos === 'object') return pedidos.$id || 'N/A';
|
||||
if (Array.isArray(pedidos)) {
|
||||
return pedidos.join(', ');
|
||||
}
|
||||
if (pedidos && typeof pedidos === 'object') {
|
||||
return pedidos.$id || 'N/A';
|
||||
}
|
||||
return pedidos ?? 'N/A';
|
||||
},
|
||||
},
|
||||
{ key: 'status', header: 'Status' },
|
||||
{ key: 'metodo', header: 'Método' },
|
||||
{ key: 'valor', header: 'Valor', render: row =>
|
||||
Number(row.valor).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' }) },
|
||||
{ key: 'metodo', header: 'Método' },
|
||||
{
|
||||
key: 'valor',
|
||||
header: 'Valor',
|
||||
render: row => Number(row.valor || 0).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' }),
|
||||
},
|
||||
{ key: '$id', header: 'ID' },
|
||||
{ key: '$createdAt', header: 'Data de Criação', render: row =>
|
||||
new Date(row.$createdAt).toLocaleDateString('pt-BR') },
|
||||
{
|
||||
key: '$createdAt',
|
||||
header: 'Data de Criação',
|
||||
render: row => new Date(row.$createdAt).toLocaleDateString('pt-BR'),
|
||||
},
|
||||
];
|
||||
|
||||
const handleDelete = async (id: string) => {
|
||||
|
|
@ -99,10 +104,10 @@ const PagamentoList: React.FC<PagamentoListProps> = ({
|
|||
<DataTable
|
||||
columns={columns}
|
||||
data={pagamentos}
|
||||
actions={(pag) => (
|
||||
actions={pagamento => (
|
||||
<TableActions
|
||||
onEdit={() => onEdit(pag)}
|
||||
onDelete={() => handleDelete(pag.$id)}
|
||||
onEdit={() => onEdit(pagamento)}
|
||||
onDelete={() => handleDelete(pagamento.$id)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
@ -130,5 +135,3 @@ const PagamentoList: React.FC<PagamentoListProps> = ({
|
|||
};
|
||||
|
||||
export default PagamentoList;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useCallback, useState } from "react";
|
||||
import { Models } from "@/lib/appwrite";
|
||||
import { catalogoProdutoService } from "../services/catalogoProdutoService";
|
||||
import { useCallback, useState } from 'react';
|
||||
import { catalogoProdutoService } from '../services/catalogoProdutoService';
|
||||
import { CatalogoProdutoDocument } from '@/types/legacyEntities';
|
||||
|
||||
export interface CatalogoProdutoFormData {
|
||||
descricao: string;
|
||||
|
|
@ -24,7 +24,7 @@ export interface CatalogoProdutoStats {
|
|||
}
|
||||
|
||||
export interface UseCatalogoProdutosReturn {
|
||||
catalogoProdutos: Models.Document[];
|
||||
catalogoProdutos: CatalogoProdutoDocument[];
|
||||
loading: boolean;
|
||||
isChangingPage: boolean;
|
||||
isCreating: boolean;
|
||||
|
|
@ -44,20 +44,20 @@ const PAGE_SIZE = 10;
|
|||
|
||||
const toApiPayload = (data: CatalogoProdutoFormData) => ({
|
||||
descricao: data.descricao,
|
||||
"codigo-interno": data.codigo_interno,
|
||||
"codigo-ean": data.codigo_ean,
|
||||
"preco-fabrica": data.preco_fabrica,
|
||||
'codigo-interno': data.codigo_interno,
|
||||
'codigo-ean': data.codigo_ean,
|
||||
'preco-fabrica': data.preco_fabrica,
|
||||
pmc: data.pmc,
|
||||
"desconto-comercial": data.desconto_comercial,
|
||||
"valor-substituicao-tributaria": data.valor_substituicao_tributaria,
|
||||
"preco-nf": data.preco_nf,
|
||||
'desconto-comercial': data.desconto_comercial,
|
||||
'valor-substituicao-tributaria': data.valor_substituicao_tributaria,
|
||||
'preco-nf': data.preco_nf,
|
||||
laboratorio: data.laboratorio,
|
||||
categoria: data.categoria,
|
||||
subcategoria: data.subcategoria,
|
||||
});
|
||||
|
||||
export const useCatalogoProdutos = (): UseCatalogoProdutosReturn => {
|
||||
const [catalogoProdutos, setCatalogoProdutos] = useState<Models.Document[]>([]);
|
||||
const [catalogoProdutos, setCatalogoProdutos] = useState<CatalogoProdutoDocument[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [totalCatalogoProdutos, setTotalCatalogoProdutos] = useState(0);
|
||||
|
|
@ -65,18 +65,18 @@ export const useCatalogoProdutos = (): UseCatalogoProdutosReturn => {
|
|||
const [isChangingPage, setIsChangingPage] = useState(false);
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
|
||||
const listarCatalogo = useCallback(async (page = 1, termoBusca = "") => {
|
||||
const listarCatalogo = useCallback(async (page = 1, termoBusca = '') => {
|
||||
setLoading(true);
|
||||
setIsChangingPage(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await catalogoProdutoService.listar(page, PAGE_SIZE, termoBusca);
|
||||
setCatalogoProdutos(response.documents as Models.Document[]);
|
||||
setCatalogoProdutos(response.documents);
|
||||
setTotalCatalogoProdutos(response.total);
|
||||
setCurrentPage(page);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "Erro ao listar catálogo de produtos";
|
||||
const message = err instanceof Error ? err.message : 'Erro ao listar catálogo de produtos';
|
||||
setError(message);
|
||||
setCatalogoProdutos([]);
|
||||
setTotalCatalogoProdutos(0);
|
||||
|
|
@ -86,99 +86,83 @@ export const useCatalogoProdutos = (): UseCatalogoProdutosReturn => {
|
|||
}
|
||||
}, []);
|
||||
|
||||
const buscarCatalogo = useCallback(
|
||||
async (params: { termo: string; filtros: unknown }, page = 1) => {
|
||||
await listarCatalogo(page, params.termo);
|
||||
},
|
||||
[listarCatalogo]
|
||||
);
|
||||
const buscarCatalogo = useCallback(async (params: { termo: string; filtros: unknown }, page = 1) => {
|
||||
await listarCatalogo(page, params.termo);
|
||||
}, [listarCatalogo]);
|
||||
|
||||
const cadastrarCatalogo = useCallback(
|
||||
async (data: CatalogoProdutoFormData): Promise<boolean> => {
|
||||
setIsCreating(true);
|
||||
setError(null);
|
||||
const cadastrarCatalogo = useCallback(async (data: CatalogoProdutoFormData): Promise<boolean> => {
|
||||
setIsCreating(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
await catalogoProdutoService.criar(toApiPayload(data));
|
||||
await listarCatalogo(currentPage);
|
||||
return true;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "Erro ao cadastrar produto";
|
||||
setError(message);
|
||||
return false;
|
||||
} finally {
|
||||
setIsCreating(false);
|
||||
}
|
||||
},
|
||||
[currentPage, listarCatalogo]
|
||||
);
|
||||
try {
|
||||
await catalogoProdutoService.criar(toApiPayload(data));
|
||||
await listarCatalogo(currentPage);
|
||||
return true;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Erro ao cadastrar produto';
|
||||
setError(message);
|
||||
return false;
|
||||
} finally {
|
||||
setIsCreating(false);
|
||||
}
|
||||
}, [currentPage, listarCatalogo]);
|
||||
|
||||
const atualizarCatalogo = useCallback(
|
||||
async (id: string, data: CatalogoProdutoFormData): Promise<boolean> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const atualizarCatalogo = useCallback(async (id: string, data: CatalogoProdutoFormData): Promise<boolean> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
await catalogoProdutoService.atualizar(id, toApiPayload(data));
|
||||
await listarCatalogo(currentPage);
|
||||
return true;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "Erro ao atualizar produto";
|
||||
setError(message);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[currentPage, listarCatalogo]
|
||||
);
|
||||
try {
|
||||
await catalogoProdutoService.atualizar(id, toApiPayload(data));
|
||||
await listarCatalogo(currentPage);
|
||||
return true;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Erro ao atualizar produto';
|
||||
setError(message);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [currentPage, listarCatalogo]);
|
||||
|
||||
const deletarCatalogo = useCallback(
|
||||
async (id: string): Promise<boolean> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const deletarCatalogo = useCallback(async (id: string): Promise<boolean> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
await catalogoProdutoService.deletar(id);
|
||||
await listarCatalogo(currentPage);
|
||||
return true;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "Erro ao deletar produto";
|
||||
setError(message);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[currentPage, listarCatalogo]
|
||||
);
|
||||
try {
|
||||
await catalogoProdutoService.deletar(id);
|
||||
await listarCatalogo(currentPage);
|
||||
return true;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Erro ao deletar produto';
|
||||
setError(message);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [currentPage, listarCatalogo]);
|
||||
|
||||
const obterEstatisticas = useCallback((): CatalogoProdutoStats => {
|
||||
return catalogoProdutos.reduce<CatalogoProdutoStats>(
|
||||
(stats, produto) => {
|
||||
const produtoData = produto as Models.Document & Record<string, unknown>;
|
||||
const categoria = typeof produtoData.categoria === "string" ? produtoData.categoria : "Sem categoria";
|
||||
return catalogoProdutos.reduce<CatalogoProdutoStats>((stats, produto) => {
|
||||
const categoria = typeof produto.categoria === 'string' ? produto.categoria : 'Sem categoria';
|
||||
|
||||
stats.total += 1;
|
||||
stats.total += 1;
|
||||
|
||||
if (produtoData.laboratorio) {
|
||||
stats.comLaboratorio += 1;
|
||||
}
|
||||
|
||||
if (typeof produtoData["preco-nf"] === "number" && produtoData["preco-nf"] > 0) {
|
||||
stats.comPreco += 1;
|
||||
}
|
||||
|
||||
stats.porCategoria[categoria] = (stats.porCategoria[categoria] || 0) + 1;
|
||||
return stats;
|
||||
},
|
||||
{
|
||||
total: 0,
|
||||
comLaboratorio: 0,
|
||||
comPreco: 0,
|
||||
porCategoria: {},
|
||||
if (produto.laboratorio) {
|
||||
stats.comLaboratorio += 1;
|
||||
}
|
||||
);
|
||||
|
||||
if (typeof produto['preco-nf'] === 'number' && produto['preco-nf'] > 0) {
|
||||
stats.comPreco += 1;
|
||||
}
|
||||
|
||||
stats.porCategoria[categoria] = (stats.porCategoria[categoria] || 0) + 1;
|
||||
return stats;
|
||||
}, {
|
||||
total: 0,
|
||||
comLaboratorio: 0,
|
||||
comPreco: 0,
|
||||
porCategoria: {},
|
||||
});
|
||||
}, [catalogoProdutos]);
|
||||
|
||||
return {
|
||||
|
|
@ -198,4 +182,3 @@ export const useCatalogoProdutos = (): UseCatalogoProdutosReturn => {
|
|||
setCurrentPage,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
// @ts-nocheck
|
||||
import { useState, useCallback } from 'react';
|
||||
import { Models } from '@/lib/appwrite';
|
||||
import { pagamentoService, PagamentoData } from '@/services/pagamentoService';
|
||||
import { PagamentoDocument } from '@/types/legacyEntities';
|
||||
export type { PagamentoData };
|
||||
|
||||
export interface UsePagamentosReturn {
|
||||
pagamentos: Models.Document[];
|
||||
pagamentos: PagamentoDocument[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
totalPagamentos: number;
|
||||
|
|
@ -21,53 +20,43 @@ export interface UsePagamentosReturn {
|
|||
const PAGE_SIZE = 10;
|
||||
|
||||
export const usePagamentos = (): UsePagamentosReturn => {
|
||||
const [pagamentos, setPagamentos] = useState<Models.Document[]>([]);
|
||||
const [pagamentos, setPagamentos] = useState<PagamentoDocument[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [totalPagamentos, setTotalPagamentos] = useState(0);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
const listarPagamentos = useCallback(
|
||||
async (page = currentPage, search = '') => {
|
||||
const listarPagamentos = useCallback(async (page = currentPage, search = '') => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await pagamentoService.listar(page, PAGE_SIZE, search);
|
||||
|
||||
setPagamentos(response.documents || []);
|
||||
setTotalPagamentos(response.total || 0);
|
||||
setCurrentPage(page);
|
||||
} catch (err) {
|
||||
setError('Erro de conexão ao carregar pagamentos');
|
||||
} catch {
|
||||
setError('Erro de conexão ao carregar pagamentos');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [currentPage]);
|
||||
|
||||
const buscarPagamentos = useCallback(
|
||||
async (termo: string, page = 1) => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const buscarPagamentos = useCallback(async (termo: string, page = 1) => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await pagamentoService.buscarPorNome(
|
||||
termo,
|
||||
page,
|
||||
PAGE_SIZE
|
||||
);
|
||||
|
||||
setPagamentos(response.documents || []);
|
||||
setTotalPagamentos(response.total || 0);
|
||||
setCurrentPage(page);
|
||||
} catch (err) {
|
||||
setError('Erro de conexão ao buscar pagamentos');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
try {
|
||||
const response = await pagamentoService.buscarPorNome(termo, page, PAGE_SIZE);
|
||||
setPagamentos(response.documents || []);
|
||||
setTotalPagamentos(response.total || 0);
|
||||
setCurrentPage(page);
|
||||
} catch {
|
||||
setError('Erro de conexão ao buscar pagamentos');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const cadastrarPagamento = useCallback(async (formData: PagamentoData): Promise<boolean> => {
|
||||
setLoading(true);
|
||||
|
|
@ -77,8 +66,8 @@ export const usePagamentos = (): UsePagamentosReturn => {
|
|||
await pagamentoService.criar(formData);
|
||||
await listarPagamentos(currentPage);
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError('Erro de conexão ao cadastrar pagamento');
|
||||
} catch {
|
||||
setError('Erro de conexão ao cadastrar pagamento');
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
|
@ -93,8 +82,8 @@ export const usePagamentos = (): UsePagamentosReturn => {
|
|||
await pagamentoService.atualizar(id, formData);
|
||||
await listarPagamentos(currentPage);
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError('Erro de conexão ao atualizar pagamento');
|
||||
} catch {
|
||||
setError('Erro de conexão ao atualizar pagamento');
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
|
@ -109,8 +98,8 @@ export const usePagamentos = (): UsePagamentosReturn => {
|
|||
await pagamentoService.deletar(id);
|
||||
await listarPagamentos(currentPage);
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError('Erro de conexão ao deletar pagamento');
|
||||
} catch {
|
||||
setError('Erro de conexão ao deletar pagamento');
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
|
@ -131,4 +120,3 @@ export const usePagamentos = (): UsePagamentosReturn => {
|
|||
setCurrentPage,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,31 +1,33 @@
|
|||
export interface PagamentoData {
|
||||
import { PagamentoDocument, ServiceResponse } from '@/types/legacyEntities';
|
||||
|
||||
export interface PagamentoData {
|
||||
id?: string;
|
||||
pedidos?: string[];
|
||||
status?: string;
|
||||
metodo?: string;
|
||||
valor?: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface ServiceResponse<T = unknown> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
documents?: T[];
|
||||
total?: number;
|
||||
error?: string;
|
||||
message?: string;
|
||||
}
|
||||
const emptyList = (): ServiceResponse<PagamentoDocument> => ({
|
||||
success: true,
|
||||
data: null,
|
||||
documents: [],
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const emptyList = <T,>(): ServiceResponse<T> => ({ success: true, documents: [], total: 0 });
|
||||
const removed = (message = 'Service removido'): ServiceResponse => ({ success: false, error: message, message });
|
||||
const removed = (message = 'Service removido'): ServiceResponse<PagamentoDocument> => ({
|
||||
success: false,
|
||||
error: message,
|
||||
message,
|
||||
});
|
||||
|
||||
export const pagamentoService = {
|
||||
listar: async (_page = 1, _limit = 10, _search = '') => emptyList<PagamentoData>(),
|
||||
buscarPorNome: async (_nome: string, _page = 1, _limit = 10) => emptyList<PagamentoData>(),
|
||||
listar: async (_page = 1, _limit = 10, _search = '') => emptyList(),
|
||||
buscarPorNome: async (_nome: string, _page = 1, _limit = 10) => emptyList(),
|
||||
criar: async (_data?: PagamentoData) => removed(),
|
||||
criarPagamento: async (_data?: PagamentoData) => removed(),
|
||||
buscarPorUserId: async () => emptyList<PagamentoData>(),
|
||||
buscarPorId: async (_id?: string) => ({ success: true, data: null as PagamentoData | null }),
|
||||
buscarPorUserId: async () => emptyList(),
|
||||
buscarPorId: async (_id?: string): Promise<ServiceResponse<PagamentoDocument>> => ({ success: true, data: null }),
|
||||
atualizar: async (_id?: string, _data?: Partial<PagamentoData>) => removed(),
|
||||
deletar: async (_id?: string) => removed(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,6 +13,29 @@ export interface FaturaDocument extends LegacyDocument {
|
|||
nome?: string;
|
||||
}
|
||||
|
||||
export interface PagamentoDocument extends LegacyDocument {
|
||||
pedidos?: string[] | { $id?: string } | string | null;
|
||||
status?: string;
|
||||
metodo?: string;
|
||||
valor?: number | string;
|
||||
}
|
||||
|
||||
export interface CatalogoProdutoDocument extends LegacyDocument {
|
||||
descricao: string;
|
||||
nome?: string;
|
||||
'codigo-interno'?: string;
|
||||
'codigo-ean'?: string;
|
||||
'preco-original'?: number;
|
||||
'preco-atual'?: number;
|
||||
'preco-fabrica'?: number;
|
||||
'preco-nf'?: number;
|
||||
pmc?: number;
|
||||
quantidade?: number;
|
||||
laboratorio?: string;
|
||||
categoria?: string;
|
||||
subcategoria?: string;
|
||||
}
|
||||
|
||||
export interface ServiceResponse<TDocument> {
|
||||
success: boolean;
|
||||
data?: TDocument | null;
|
||||
|
|
|
|||
Loading…
Reference in a new issue