refactor(frontend): type legacy payments and catalog flows

This commit is contained in:
Tiago Yamamoto 2026-03-07 07:56:20 -06:00
parent b633939d4e
commit 48a54a616e
8 changed files with 279 additions and 285 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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>

View file

@ -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;

View file

@ -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,
};
};

View file

@ -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,
};
};

View file

@ -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(),
};

View file

@ -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;