refactor(frontend): replace legacy document typing for billing and categories

This commit is contained in:
Tiago Yamamoto 2026-03-07 07:49:58 -06:00
parent 6cde64f424
commit b633939d4e
10 changed files with 326 additions and 310 deletions

View file

@ -1,4 +1,4 @@
'use client';
'use client';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
@ -11,14 +11,15 @@ import FaturaList from '@/components/FaturaList';
import { useFaturas, FaturaFormData } from '@/hooks/useFaturas';
import { RoleGuard } from '@/components/auth/RoleGuard';
import { UserRole } from '@/types/auth';
import { FaturaDocument } from '@/types/legacyEntities';
const GestaoFaturas = () => {
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<FaturaDocument | null>(null);
const [activeTab, setActiveTab] = useState<'lista' | 'cadastro'>('lista');
const [searchTerm, setSearchTerm] = useState('');
const {
faturas,
loading,
@ -30,21 +31,19 @@ const GestaoFaturas = () => {
cadastrarFatura,
atualizarFatura,
deletarFatura,
setCurrentPage
} = useFaturas();
// 🔐 Verificar autenticação
useEffect(() => {
const initializeUser = async () => {
try {
const currentUser = await account.get();
setUser(currentUser);
if (activeTab === 'lista') {
await listarFaturas();
}
} catch (error) {
console.error('❌ Usuário não autenticado:', error);
console.error('Usuário não autenticado:', error);
router.push('/');
}
};
@ -60,16 +59,16 @@ const GestaoFaturas = () => {
setActiveTab('lista');
}
return success;
} else {
const success = await cadastrarFatura(formData);
if (success) {
setTimeout(() => setActiveTab('lista'), 2000);
}
return success;
}
const success = await cadastrarFatura(formData);
if (success) {
setTimeout(() => setActiveTab('lista'), 2000);
}
return success;
};
const handleEdit = (fatura: Models.Document) => {
const handleEdit = (fatura: FaturaDocument) => {
setEditing(fatura);
setActiveTab('cadastro');
};
@ -80,33 +79,40 @@ const GestaoFaturas = () => {
};
const handlePrevPage = async () => {
if (currentPage > 1) {
if (searchTerm.trim()) {
await buscarFaturas(searchTerm, currentPage - 1);
} else {
await listarFaturas(currentPage - 1);
}
if (currentPage <= 1) {
return;
}
if (searchTerm.trim()) {
await buscarFaturas(searchTerm, currentPage - 1);
return;
}
await listarFaturas(currentPage - 1);
};
const handleSearch = async (term: string) => {
setSearchTerm(term);
if (term.trim()) {
await buscarFaturas(term, 1);
} else {
await listarFaturas(1);
return;
}
await listarFaturas(1);
};
const handleNextPage = async () => {
const totalPages = Math.ceil(totalFaturas / 10);
if (currentPage < totalPages) {
if (searchTerm.trim()) {
await buscarFaturas(searchTerm, currentPage + 1);
} else {
await listarFaturas(currentPage + 1);
}
if (currentPage >= totalPages) {
return;
}
if (searchTerm.trim()) {
await buscarFaturas(searchTerm, currentPage + 1);
return;
}
await listarFaturas(currentPage + 1);
};
if (!user) {
@ -114,7 +120,7 @@ const GestaoFaturas = () => {
<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>
);
@ -123,75 +129,73 @@ const GestaoFaturas = () => {
return (
<RoleGuard allowedRoles={[UserRole.ADMIN, UserRole.ADMIN]}>
<div className="min-h-screen bg-gray-50">
<Header
user={user}
title="Gestão de Faturas"
<Header
user={user}
title="Gestão de Faturas"
subtitle="Gerencie as faturas da plataforma SaveInMed"
/>
<main className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{/* Navegação por Tabs */}
<div className="mb-6">
<nav className="flex space-x-8 bg-white p-4 rounded-lg shadow-sm">
<button
onClick={() => {
setActiveTab('lista');
setEditing(null);
}}
className={`py-2 px-4 border-b-2 font-medium text-sm transition-colors cursor-pointer rounded-t-md ${
activeTab === 'lista'
? 'border-blue-500 text-blue-600 bg-blue-50'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 hover:bg-gray-50'
}`}
>
<FileSpreadsheet className="w-4 h-4 inline-block mr-2" /> Listar Faturas
</button>
<button
onClick={() => {
setActiveTab('cadastro');
setEditing(null);
}}
className={`py-2 px-4 border-b-2 font-medium text-sm transition-colors cursor-pointer rounded-t-md ${
activeTab === 'cadastro'
? 'border-blue-500 text-blue-600 bg-blue-50'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 hover:bg-gray-50'
}`}
>
<Plus className="w-4 h-4 inline-block mr-2" /> Cadastrar Fatura
</button>
</nav>
</div>
{/* Conteúdo das Tabs */}
{activeTab === 'lista' && (
<FaturaList
faturas={faturas}
loading={loading}
error={error}
totalFaturas={totalFaturas}
currentPage={currentPage}
pageSize={10}
onEdit={handleEdit}
onDelete={deletarFatura}
onRefresh={() =>
searchTerm.trim()
? buscarFaturas(searchTerm, currentPage)
: listarFaturas(currentPage)
}
onPrevPage={handlePrevPage}
onNextPage={handleNextPage}
onSearch={handleSearch}
/>
)}
<main className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<div className="mb-6">
<nav className="flex space-x-8 bg-white p-4 rounded-lg shadow-sm">
<button
onClick={() => {
setActiveTab('lista');
setEditing(null);
}}
className={`py-2 px-4 border-b-2 font-medium text-sm transition-colors cursor-pointer rounded-t-md ${
activeTab === 'lista'
? 'border-blue-500 text-blue-600 bg-blue-50'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 hover:bg-gray-50'
}`}
>
<FileSpreadsheet className="w-4 h-4 inline-block mr-2" /> Listar Faturas
</button>
<button
onClick={() => {
setActiveTab('cadastro');
setEditing(null);
}}
className={`py-2 px-4 border-b-2 font-medium text-sm transition-colors cursor-pointer rounded-t-md ${
activeTab === 'cadastro'
? 'border-blue-500 text-blue-600 bg-blue-50'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 hover:bg-gray-50'
}`}
>
<Plus className="w-4 h-4 inline-block mr-2" /> Cadastrar Fatura
</button>
</nav>
</div>
{activeTab === 'cadastro' && (
<FaturaForm
onSubmit={handleFormSubmit}
onCancel={editing ? handleCancelEdit : undefined}
initialData={editing}
loading={loading}
/>
)}
{activeTab === 'lista' && (
<FaturaList
faturas={faturas}
loading={loading}
error={error}
totalFaturas={totalFaturas}
currentPage={currentPage}
pageSize={10}
onEdit={handleEdit}
onDelete={deletarFatura}
onRefresh={() =>
searchTerm.trim()
? buscarFaturas(searchTerm, currentPage)
: listarFaturas(currentPage)
}
onPrevPage={handlePrevPage}
onNextPage={handleNextPage}
onSearch={handleSearch}
/>
)}
{activeTab === 'cadastro' && (
<FaturaForm
onSubmit={handleFormSubmit}
onCancel={editing ? handleCancelEdit : undefined}
initialData={editing}
loading={loading}
/>
)}
</main>
</div>
</RoleGuard>
@ -199,4 +203,3 @@ const GestaoFaturas = () => {
};
export default GestaoFaturas;

View file

@ -1,67 +1,66 @@
import React, { useState, useEffect } from 'react';
import { Models } from '@/lib/appwrite';
import React, { useState, useEffect } from 'react';
import { CategoriaFormData } from '@/hooks/useCategorias';
import { Plus } from 'lucide-react';
import { CategoriaDocument } from '@/types/legacyEntities';
interface CategoriaFormProps {
onSubmit: (data: CategoriaFormData) => Promise<boolean>;
onCancel?: () => void;
initialData?: Models.Document | null;
initialData?: CategoriaDocument | null;
loading?: boolean;
}
type CategoriaDocument = Models.Document & {
nome?: string;
};
const CategoriaForm: React.FC<CategoriaFormProps> = ({
onSubmit,
onCancel,
initialData,
loading = false
loading = false,
}) => {
const [formData, setFormData] = useState<CategoriaFormData>({
nome: ''
nome: '',
});
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
useEffect(() => {
if (initialData) {
const document = initialData as CategoriaDocument;
setFormData({ nome: document.nome || '' });
} else {
setFormData({ nome: '' });
setFormData({ nome: initialData.nome || '' });
return;
}
setFormData({ nome: '' });
}, [initialData]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const success = await onSubmit(formData);
if (success) {
setMessage({
type: 'success',
text: initialData ? '🔄 Categoria atualizada com sucesso!' : '🎉 Categoria cadastrada com sucesso!'
setMessage({
type: 'success',
text: initialData ? 'Categoria atualizada com sucesso!' : 'Categoria cadastrada com sucesso!',
});
if (!initialData) {
setFormData({ nome: '' });
}
setTimeout(() => setMessage(null), 3000);
} else {
setMessage({
type: 'error',
text: initialData ? 'Erro ao atualizar categoria' : 'Erro ao cadastrar categoria'
});
return;
}
setMessage({
type: 'error',
text: initialData ? 'Erro ao atualizar categoria' : 'Erro ao cadastrar categoria',
});
};
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
if (message) setMessage(null);
if (message) {
setMessage(null);
}
};
return (
@ -109,7 +108,7 @@ const CategoriaForm: React.FC<CategoriaFormProps> = ({
>
{loading ? 'Processando...' : (initialData ? 'Atualizar' : <><Plus className="w-4 h-4 inline-block mr-1" /> Cadastrar</>)}
</button>
{onCancel && (
<button
type="button"
@ -127,4 +126,3 @@ const CategoriaForm: React.FC<CategoriaFormProps> = ({
};
export default CategoriaForm;

View file

@ -1,19 +1,19 @@
import React from 'react';
import { Models } from '@/lib/appwrite';
import React from 'react';
import SearchBar from './SearchBar';
import DataTable, { Column } from './DataTable';
import Pagination from './Pagination';
import TableActions from './TableActions';
import { CategoriaDocument } from '@/types/legacyEntities';
interface CategoriaListProps {
categorias: Models.Document[];
categorias: CategoriaDocument[];
loading: boolean;
isChangingPage?: boolean;
error: string | null;
totalCategorias: number;
currentPage: number;
pageSize: number;
onEdit: (lab: Models.Document) => void;
onEdit: (categoria: CategoriaDocument) => void;
onDelete: (id: string) => Promise<boolean>;
onPrevPage: () => void;
onNextPage: () => void;
@ -32,7 +32,7 @@ const CategoriaList: React.FC<CategoriaListProps> = ({
onDelete,
onPrevPage,
onNextPage,
onSearch
onSearch,
}) => {
const totalPages = Math.ceil(totalCategorias / pageSize);
const startItem = (currentPage - 1) * pageSize + 1;
@ -50,10 +50,14 @@ const CategoriaList: React.FC<CategoriaListProps> = ({
}
};
const columns: Column<Models.Document>[] = [
const columns: Column<CategoriaDocument>[] = [
{ key: 'nome', header: 'Nome' },
{ 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'),
},
];
return (
@ -77,7 +81,7 @@ const CategoriaList: React.FC<CategoriaListProps> = ({
<div className="bg-blue-50 border border-blue-200 rounded-md p-3 mb-4">
<div className="flex items-center">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-600 mr-2"></div>
<span className="text-blue-800 text-sm">Carregando página...</span>
<span className="text-blue-800 text-sm">Carregando página...</span>
</div>
</div>
)}
@ -91,10 +95,10 @@ const CategoriaList: React.FC<CategoriaListProps> = ({
<DataTable
columns={columns}
data={categorias}
actions={(lab) => (
actions={categoria => (
<TableActions
onEdit={() => onEdit(lab)}
onDelete={() => handleDelete(lab.$id)}
onEdit={() => onEdit(categoria)}
onDelete={() => handleDelete(categoria.$id)}
/>
)}
/>
@ -122,4 +126,4 @@ const CategoriaList: React.FC<CategoriaListProps> = ({
);
};
export default CategoriaList;
export default CategoriaList;

View file

@ -1,19 +1,15 @@
import React, { useState, useEffect } from 'react';
import { Models } from '@/lib/appwrite';
import React, { useState, useEffect } from 'react';
import { FaturaFormData } from '@/hooks/useFaturas';
import { Plus } from 'lucide-react';
import { FaturaDocument } from '@/types/legacyEntities';
interface FaturaFormProps {
onSubmit: (data: FaturaFormData) => Promise<boolean>;
onCancel?: () => void;
initialData?: Models.Document | null;
initialData?: FaturaDocument | null;
loading?: boolean;
}
type FaturaDocument = Models.Document & {
nome?: string;
};
const FaturaForm: React.FC<FaturaFormProps> = ({
onSubmit,
onCancel,
@ -25,41 +21,45 @@ const FaturaForm: React.FC<FaturaFormProps> = ({
useEffect(() => {
if (initialData) {
const document = initialData as FaturaDocument;
setFormData({ nome: document.nome || '' });
} else {
setFormData({ nome: '' });
setFormData({ nome: initialData.nome || '' });
return;
}
setFormData({ nome: '' });
}, [initialData]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const success = await onSubmit(formData);
if (success) {
setMessage({
type: 'success',
text: initialData ? '🔄 Fatura atualizada com sucesso!' : '🎉 Fatura cadastrada com sucesso!'
text: initialData ? 'Fatura atualizada com sucesso!' : 'Fatura cadastrada com sucesso!',
});
if (!initialData) {
setFormData({ nome: '' });
}
setTimeout(() => setMessage(null), 3000);
} else {
setMessage({ type: 'error', text: initialData ? 'Erro ao atualizar fatura' : 'Erro ao cadastrar fatura' });
return;
}
setMessage({ type: 'error', text: initialData ? 'Erro ao atualizar fatura' : 'Erro ao cadastrar fatura' });
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
if (message) setMessage(null);
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 Fatura' : <><Plus className="w-5 h-5 inline-block mr-2" /> Nova Fatura</>}
{initialData ? 'Editar Fatura' : <><Plus className="w-5 h-5 inline-block mr-2" /> Nova Fatura</>}
</h2>
<p className="text-gray-600">
{initialData ? 'Atualize os dados da fatura.' : 'Cadastre uma nova fatura na plataforma SaveInMed.'}
@ -95,7 +95,7 @@ const FaturaForm: React.FC<FaturaFormProps> = ({
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 && (
<button
@ -104,7 +104,7 @@ const FaturaForm: React.FC<FaturaFormProps> = ({
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>
@ -114,5 +114,3 @@ const FaturaForm: React.FC<FaturaFormProps> = ({
};
export default FaturaForm;

View file

@ -1,20 +1,20 @@
import React from 'react';
import { Models } from '@/lib/appwrite';
import React from 'react';
import SearchBar from './SearchBar';
import RefreshButton from './RefreshButton';
import ListHeader from './ListHeader';
import DataTable, { Column } from './DataTable';
import Pagination from './Pagination';
import TableActions from './TableActions';
import { FaturaDocument } from '@/types/legacyEntities';
interface FaturaListProps {
faturas: Models.Document[];
faturas: FaturaDocument[];
loading: boolean;
error: string | null;
totalFaturas: number;
currentPage: number;
pageSize: number;
onEdit: (fatura: Models.Document) => void;
onEdit: (fatura: FaturaDocument) => void;
onDelete: (id: string) => Promise<boolean>;
onRefresh: () => void;
onPrevPage: () => void;
@ -34,7 +34,7 @@ const FaturaList: React.FC<FaturaListProps> = ({
onRefresh,
onPrevPage,
onNextPage,
onSearch
onSearch,
}) => {
const totalPages = Math.ceil(totalFaturas / pageSize);
const startItem = (currentPage - 1) * pageSize + 1;
@ -52,10 +52,14 @@ const FaturaList: React.FC<FaturaListProps> = ({
}
};
const columns: Column<Models.Document>[] = [
const columns: Column<FaturaDocument>[] = [
{ key: 'nome', header: 'Nome' },
{ 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'),
},
];
return (
@ -85,7 +89,7 @@ const FaturaList: React.FC<FaturaListProps> = ({
<DataTable
columns={columns}
data={faturas}
actions={(fatura) => (
actions={fatura => (
<TableActions
onEdit={() => onEdit(fatura)}
onDelete={() => handleDelete(fatura.$id)}

View file

@ -1,13 +1,13 @@
import { useState, useCallback, useRef, useEffect } from 'react';
import { Models } from '@/lib/appwrite';
import { useState, useCallback, useRef, useEffect } from 'react';
import { categoriaService } from '@/services/categoriaService';
import { CategoriaDocument } from '@/types/legacyEntities';
export interface CategoriaFormData {
nome: string;
}
export interface UseCategoriasReturn {
categorias: Models.Document[];
categorias: CategoriaDocument[];
loading: boolean;
isChangingPage: boolean;
isCreating: boolean;
@ -27,7 +27,7 @@ export interface UseCategoriasReturn {
const PAGE_SIZE = 10;
export const useCategorias = (): UseCategoriasReturn => {
const [categorias, setCategorias] = useState<Models.Document[]>([]);
const [categorias, setCategorias] = useState<CategoriaDocument[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [totalCategorias, setTotalCategorias] = useState(0);
@ -37,9 +37,7 @@ export const useCategorias = (): UseCategoriasReturn => {
const [searchTerm, setSearchTerm] = useState('');
const isInitialMount = useRef(true);
// Listagem via API
const listarCategorias = useCallback(
async (page = currentPage, search = searchTerm) => {
const listarCategorias = useCallback(async (page = currentPage, search = searchTerm) => {
setIsChangingPage(true);
setLoading(true);
setError(null);
@ -50,53 +48,47 @@ export const useCategorias = (): UseCategoriasReturn => {
: await categoriaService.listar(page, PAGE_SIZE);
if (response.success) {
setCategorias((response.documents || []) as Models.Document[]);
setCategorias(response.documents || []);
setTotalCategorias(response.total || 0);
setCurrentPage(page);
setSearchTerm(search);
} else {
setError(response.error || 'Erro ao carregar categorias');
return;
}
} catch (err) {
setError('Erro de conexão ao carregar categorias');
setError(response.error || 'Erro ao carregar categorias');
} catch {
setError('Erro de conexão ao carregar categorias');
} finally {
setLoading(false);
setIsChangingPage(false);
}
}, [currentPage, searchTerm]);
const buscarCategorias = useCallback(
async (nome: string, page = 1) => {
setIsChangingPage(true);
setLoading(true);
setError(null);
const buscarCategorias = useCallback(async (nome: string, page = 1) => {
setIsChangingPage(true);
setLoading(true);
setError(null);
try {
const response = await categoriaService.buscarPorNome(
nome,
page,
PAGE_SIZE
);
try {
const response = await categoriaService.buscarPorNome(nome, page, PAGE_SIZE);
if (response.success) {
setCategorias((response.documents || []) as Models.Document[]);
setTotalCategorias(response.total || 0);
setCurrentPage(page);
setSearchTerm(nome);
} else {
setError(response.error || 'Erro ao buscar categorias');
}
} catch (err) {
setError('Erro de conexão ao buscar categorias');
} finally {
setLoading(false);
setIsChangingPage(false);
if (response.success) {
setCategorias(response.documents || []);
setTotalCategorias(response.total || 0);
setCurrentPage(page);
setSearchTerm(nome);
return;
}
},
[]
);
// Operações de criação, atualização e exclusão continuam usando API REST
setError(response.error || 'Erro ao buscar categorias');
} catch {
setError('Erro de conexão ao buscar categorias');
} finally {
setLoading(false);
setIsChangingPage(false);
}
}, []);
const cadastrarCategoria = useCallback(async (formData: CategoriaFormData): Promise<boolean> => {
setIsCreating(true);
setError(null);
@ -107,12 +99,12 @@ export const useCategorias = (): UseCategoriasReturn => {
if (response.success) {
await listarCategorias(1, searchTerm);
return true;
} else {
setError(response.error || 'Erro ao cadastrar categoria');
return false;
}
} catch (err) {
setError('Erro de conexão ao cadastrar categoria');
setError(response.error || 'Erro ao cadastrar categoria');
return false;
} catch {
setError('Erro de conexão ao cadastrar categoria');
return false;
} finally {
setIsCreating(false);
@ -129,17 +121,17 @@ export const useCategorias = (): UseCategoriasReturn => {
if (response.success) {
await listarCategorias(currentPage, searchTerm);
return true;
} else {
setError(response.error || 'Erro ao atualizar categoria');
return false;
}
} catch (err) {
setError('Erro de conexão ao atualizar categoria');
setError(response.error || 'Erro ao atualizar categoria');
return false;
} catch {
setError('Erro de conexão ao atualizar categoria');
return false;
} finally {
setLoading(false);
}
}, [currentPage, listarCategorias]);
}, [currentPage, listarCategorias, searchTerm]);
const deletarCategoria = useCallback(async (id: string): Promise<boolean> => {
setLoading(true);
@ -151,17 +143,17 @@ export const useCategorias = (): UseCategoriasReturn => {
if (response.success) {
await listarCategorias(currentPage, searchTerm);
return true;
} else {
setError(response.error || 'Erro ao deletar categoria');
return false;
}
} catch (err) {
setError('Erro de conexão ao deletar categoria');
setError(response.error || 'Erro ao deletar categoria');
return false;
} catch {
setError('Erro de conexão ao deletar categoria');
return false;
} finally {
setLoading(false);
}
}, [currentPage, listarCategorias]);
}, [currentPage, listarCategorias, searchTerm]);
useEffect(() => {
if (isInitialMount.current) {
@ -171,9 +163,10 @@ export const useCategorias = (): UseCategoriasReturn => {
if (searchTerm.trim()) {
buscarCategorias(searchTerm, currentPage);
} else {
listarCategorias(currentPage);
return;
}
listarCategorias(currentPage);
}, [currentPage, searchTerm, listarCategorias, buscarCategorias]);
return {
@ -191,7 +184,6 @@ export const useCategorias = (): UseCategoriasReturn => {
deletarCategoria,
setCurrentPage,
searchTerm,
setSearchTerm
setSearchTerm,
};
};

View file

@ -1,13 +1,13 @@
import { useState, useCallback } from 'react';
import { Models } from '@/lib/appwrite';
import { useState, useCallback } from 'react';
import { faturaService } from '@/services/faturaService';
import { FaturaDocument } from '@/types/legacyEntities';
export interface FaturaFormData {
nome: string;
}
export interface UseFaturasReturn {
faturas: Models.Document[];
faturas: FaturaDocument[];
loading: boolean;
error: string | null;
totalFaturas: number;
@ -23,13 +23,12 @@ export interface UseFaturasReturn {
const PAGE_SIZE = 10;
export const useFaturas = (): UseFaturasReturn => {
const [faturas, setFaturas] = useState<Models.Document[]>([]);
const [faturas, setFaturas] = useState<FaturaDocument[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [totalFaturas, setTotalFaturas] = useState(0);
const [currentPage, setCurrentPage] = useState(1);
// Listagem usando SDK do Appwrite
const listarFaturas = useCallback(async (page = currentPage) => {
setLoading(true);
setError(null);
@ -38,48 +37,42 @@ export const useFaturas = (): UseFaturasReturn => {
const response = await faturaService.listar(page, PAGE_SIZE);
if (response.success) {
setFaturas((response.documents || []) as Models.Document[]);
setFaturas(response.documents || []);
setTotalFaturas(response.total || 0);
setCurrentPage(page);
} else {
setError(response.error || 'Erro ao carregar faturas');
return;
}
} catch (err) {
setError('Erro de conexão ao carregar faturas');
setError(response.error || 'Erro ao carregar faturas');
} catch {
setError('Erro de conexão ao carregar faturas');
} finally {
setLoading(false);
}
}, [currentPage]);
const buscarFaturas = useCallback(
async (nome: string, page = 1) => {
setLoading(true);
setError(null);
const buscarFaturas = useCallback(async (nome: string, page = 1) => {
setLoading(true);
setError(null);
try {
const response = await faturaService.buscarPorNome(
nome,
page,
PAGE_SIZE
);
try {
const response = await faturaService.buscarPorNome(nome, page, PAGE_SIZE);
if (response.success) {
setFaturas((response.documents || []) as Models.Document[]);
setTotalFaturas(response.total || 0);
setCurrentPage(page);
} else {
setError(response.error || 'Erro ao buscar faturas');
}
} catch (err) {
setError('Erro de conexão ao buscar faturas');
} finally {
setLoading(false);
if (response.success) {
setFaturas(response.documents || []);
setTotalFaturas(response.total || 0);
setCurrentPage(page);
return;
}
},
[]
);
// Operações de criação, atualização e exclusão continuam usando API REST
setError(response.error || 'Erro ao buscar faturas');
} catch {
setError('Erro de conexão ao buscar faturas');
} finally {
setLoading(false);
}
}, []);
const cadastrarFatura = useCallback(async (formData: FaturaFormData): Promise<boolean> => {
setLoading(true);
setError(null);
@ -90,12 +83,12 @@ export const useFaturas = (): UseFaturasReturn => {
if (response.success) {
await listarFaturas(currentPage);
return true;
} else {
setError(response.error || 'Erro ao cadastrar fatura');
return false;
}
} catch (err) {
setError('Erro de conexão ao cadastrar fatura');
setError(response.error || 'Erro ao cadastrar fatura');
return false;
} catch {
setError('Erro de conexão ao cadastrar fatura');
return false;
} finally {
setLoading(false);
@ -112,12 +105,12 @@ export const useFaturas = (): UseFaturasReturn => {
if (response.success) {
await listarFaturas(currentPage);
return true;
} else {
setError(response.error || 'Erro ao atualizar fatura');
return false;
}
} catch (err) {
setError('Erro de conexão ao atualizar fatura');
setError(response.error || 'Erro ao atualizar fatura');
return false;
} catch {
setError('Erro de conexão ao atualizar fatura');
return false;
} finally {
setLoading(false);
@ -134,12 +127,12 @@ export const useFaturas = (): UseFaturasReturn => {
if (response.success) {
await listarFaturas(currentPage);
return true;
} else {
setError(response.error || 'Erro ao deletar fatura');
return false;
}
} catch (err) {
setError('Erro de conexão ao deletar fatura');
setError(response.error || 'Erro ao deletar fatura');
return false;
} catch {
setError('Erro de conexão ao deletar fatura');
return false;
} finally {
setLoading(false);
@ -160,5 +153,3 @@ export const useFaturas = (): UseFaturasReturn => {
setCurrentPage,
};
};

View file

@ -1,29 +1,29 @@
// @ts-nocheck
interface CategoriaData {
import { CategoriaDocument, ServiceResponse } from '@/types/legacyEntities';
export interface CategoriaData {
id?: string;
nome?: string;
[key: string]: any;
}
interface ServiceResponse<T = unknown> {
success: boolean;
data?: T;
documents?: T[];
total?: number;
error?: string;
message?: string;
}
const emptyList = (): ServiceResponse<CategoriaDocument> => ({
success: true,
data: null,
documents: [],
total: 0,
});
const emptyList = <T,>(): ServiceResponse<T> => ({ success: true, data: [], documents: [], total: 0 });
const removed = (message = 'Service removido'): ServiceResponse => ({ success: false, error: message, message });
const removed = (message = 'Service removido'): ServiceResponse<CategoriaDocument> => ({
success: false,
error: message,
message,
});
export const categoriaService = {
listarTodas: async () => emptyList<CategoriaData>(),
listar: async (_page = 1, _limit = 10) => emptyList<CategoriaData>(),
buscarPorNome: async (_nome: string, _page = 1, _limit = 10) => emptyList<CategoriaData>(),
buscarPorId: async (_id?: string) => ({ success: true, data: null as CategoriaData | null }),
listarTodas: async () => emptyList(),
listar: async (_page = 1, _limit = 10) => emptyList(),
buscarPorNome: async (_nome: string, _page = 1, _limit = 10) => emptyList(),
buscarPorId: async (_id?: string): Promise<ServiceResponse<CategoriaDocument>> => ({ success: true, data: null }),
criar: async (_data?: CategoriaData) => removed(),
atualizar: async (_id?: string, _data?: Partial<CategoriaData>) => removed(),
deletar: async (_id?: string) => removed(),
};

View file

@ -1,27 +1,30 @@
interface FaturaData {
import { FaturaDocument, ServiceResponse } from '@/types/legacyEntities';
export interface FaturaData {
id?: string;
[key: string]: any;
nome?: string;
}
interface ServiceResponse<T = unknown> {
success: boolean;
data?: T;
documents?: T[];
total?: number;
error?: string;
message?: string;
}
const emptyList = (): ServiceResponse<FaturaDocument> => ({
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<FaturaDocument> => ({
success: false,
error: message,
message,
});
export const faturaService = {
listar: async (_page = 1, _limit = 10) => emptyList<FaturaData>(),
buscarPorNome: async (_nome: string, _page = 1, _limit = 10) => emptyList<FaturaData>(),
listar: async (_page = 1, _limit = 10) => emptyList(),
buscarPorNome: async (_nome: string, _page = 1, _limit = 10) => emptyList(),
criar: async (_data?: FaturaData) => removed(),
criarFatura: async (_data?: FaturaData) => removed(),
buscarPorUserId: async () => emptyList<FaturaData>(),
buscarPorId: async (_id?: string) => ({ success: true, data: null as FaturaData | null }),
buscarPorUserId: async () => emptyList(),
buscarPorId: async (_id?: string): Promise<ServiceResponse<FaturaDocument>> => ({ success: true, data: null }),
atualizar: async (_id?: string, _data?: Partial<FaturaData>) => removed(),
deletar: async (_id?: string) => removed(),
atualizarFaturaParcial: async (_id?: string, _data?: Partial<FaturaData>) => removed(),

View file

@ -0,0 +1,23 @@
export interface LegacyDocument {
$id: string;
$createdAt: string;
$updatedAt?: string;
[key: string]: unknown;
}
export interface CategoriaDocument extends LegacyDocument {
nome?: string;
}
export interface FaturaDocument extends LegacyDocument {
nome?: string;
}
export interface ServiceResponse<TDocument> {
success: boolean;
data?: TDocument | null;
documents?: TDocument[];
total?: number;
error?: string;
message?: string;
}